2021-08-17 15:17:56 -07:00
|
|
|
//! Defines a transaction which supports multiple versions of messages.
|
|
|
|
|
|
|
|
#![cfg(feature = "full")]
|
|
|
|
|
|
|
|
use {
|
|
|
|
crate::{
|
|
|
|
hash::Hash,
|
|
|
|
message::VersionedMessage,
|
|
|
|
sanitize::{Sanitize, SanitizeError},
|
|
|
|
short_vec,
|
|
|
|
signature::Signature,
|
2022-01-13 23:24:41 -08:00
|
|
|
signer::SignerError,
|
|
|
|
signers::Signers,
|
2021-08-17 15:17:56 -07:00
|
|
|
transaction::{Result, Transaction, TransactionError},
|
|
|
|
},
|
|
|
|
serde::Serialize,
|
2021-12-14 06:23:05 -08:00
|
|
|
std::cmp::Ordering,
|
2021-08-17 15:17:56 -07:00
|
|
|
};
|
|
|
|
|
2022-03-07 23:20:34 -08:00
|
|
|
/// Type that serializes to the string "legacy"
|
|
|
|
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
|
|
|
#[serde(rename_all = "camelCase")]
|
|
|
|
pub enum Legacy {
|
|
|
|
Legacy,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
|
|
|
#[serde(rename_all = "camelCase", untagged)]
|
|
|
|
pub enum TransactionVersion {
|
|
|
|
Legacy(Legacy),
|
|
|
|
Number(u8),
|
|
|
|
}
|
|
|
|
|
|
|
|
impl TransactionVersion {
|
|
|
|
pub const LEGACY: Self = Self::Legacy(Legacy::Legacy);
|
|
|
|
}
|
|
|
|
|
2021-08-17 15:17:56 -07:00
|
|
|
// NOTE: Serialization-related changes must be paired with the direct read at sigverify.
|
|
|
|
/// An atomic transaction
|
|
|
|
#[derive(Debug, PartialEq, Default, Eq, Clone, Serialize, Deserialize, AbiExample)]
|
|
|
|
pub struct VersionedTransaction {
|
|
|
|
/// List of signatures
|
|
|
|
#[serde(with = "short_vec")]
|
|
|
|
pub signatures: Vec<Signature>,
|
|
|
|
/// Message to sign.
|
|
|
|
pub message: VersionedMessage,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Sanitize for VersionedTransaction {
|
|
|
|
fn sanitize(&self) -> std::result::Result<(), SanitizeError> {
|
|
|
|
self.message.sanitize()?;
|
|
|
|
|
2021-12-14 06:23:05 -08:00
|
|
|
let num_required_signatures = usize::from(self.message.header().num_required_signatures);
|
|
|
|
match num_required_signatures.cmp(&self.signatures.len()) {
|
|
|
|
Ordering::Greater => Err(SanitizeError::IndexOutOfBounds),
|
|
|
|
Ordering::Less => Err(SanitizeError::InvalidValue),
|
|
|
|
Ordering::Equal => Ok(()),
|
|
|
|
}?;
|
2021-08-17 15:17:56 -07:00
|
|
|
|
2022-01-06 19:59:09 -08:00
|
|
|
// Signatures are verified before message keys are loaded so all signers
|
|
|
|
// must correspond to static account keys.
|
2022-02-05 04:00:31 -08:00
|
|
|
if self.signatures.len() > self.message.static_account_keys().len() {
|
2021-08-17 15:17:56 -07:00
|
|
|
return Err(SanitizeError::IndexOutOfBounds);
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<Transaction> for VersionedTransaction {
|
|
|
|
fn from(transaction: Transaction) -> Self {
|
|
|
|
Self {
|
|
|
|
signatures: transaction.signatures,
|
|
|
|
message: VersionedMessage::Legacy(transaction.message),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl VersionedTransaction {
|
2022-01-13 23:24:41 -08:00
|
|
|
/// Signs a versioned message and if successful, returns a signed
|
|
|
|
/// transaction.
|
|
|
|
pub fn try_new<T: Signers>(
|
|
|
|
message: VersionedMessage,
|
|
|
|
keypairs: &T,
|
|
|
|
) -> std::result::Result<Self, SignerError> {
|
|
|
|
let static_account_keys = message.static_account_keys();
|
|
|
|
if static_account_keys.len() < message.header().num_required_signatures as usize {
|
|
|
|
return Err(SignerError::InvalidInput("invalid message".to_string()));
|
|
|
|
}
|
|
|
|
|
|
|
|
let signer_keys = keypairs.pubkeys();
|
|
|
|
let expected_signer_keys =
|
|
|
|
&static_account_keys[0..message.header().num_required_signatures as usize];
|
|
|
|
|
|
|
|
match signer_keys.len().cmp(&expected_signer_keys.len()) {
|
|
|
|
Ordering::Greater => Err(SignerError::TooManySigners),
|
|
|
|
Ordering::Less => Err(SignerError::NotEnoughSigners),
|
|
|
|
Ordering::Equal => Ok(()),
|
|
|
|
}?;
|
|
|
|
|
|
|
|
if signer_keys != expected_signer_keys {
|
|
|
|
return Err(SignerError::KeypairPubkeyMismatch);
|
|
|
|
}
|
|
|
|
|
|
|
|
let message_data = message.serialize();
|
|
|
|
let signatures = keypairs.try_sign_message(&message_data)?;
|
|
|
|
|
|
|
|
Ok(Self {
|
|
|
|
signatures,
|
|
|
|
message,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2022-03-07 23:20:34 -08:00
|
|
|
/// Returns the version of the transaction
|
|
|
|
pub fn version(&self) -> TransactionVersion {
|
|
|
|
match self.message {
|
|
|
|
VersionedMessage::Legacy(_) => TransactionVersion::LEGACY,
|
|
|
|
VersionedMessage::V0(_) => TransactionVersion::Number(0),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-08-17 15:17:56 -07:00
|
|
|
/// Returns a legacy transaction if the transaction message is legacy.
|
|
|
|
pub fn into_legacy_transaction(self) -> Option<Transaction> {
|
|
|
|
match self.message {
|
|
|
|
VersionedMessage::Legacy(message) => Some(Transaction {
|
|
|
|
signatures: self.signatures,
|
|
|
|
message,
|
|
|
|
}),
|
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Verify the transaction and hash its message
|
|
|
|
pub fn verify_and_hash_message(&self) -> Result<Hash> {
|
|
|
|
let message_bytes = self.message.serialize();
|
2022-01-06 19:59:09 -08:00
|
|
|
if !self
|
|
|
|
._verify_with_results(&message_bytes)
|
2021-08-17 15:17:56 -07:00
|
|
|
.iter()
|
2022-01-06 19:59:09 -08:00
|
|
|
.all(|verify_result| *verify_result)
|
2021-08-17 15:17:56 -07:00
|
|
|
{
|
|
|
|
Err(TransactionError::SignatureFailure)
|
|
|
|
} else {
|
|
|
|
Ok(VersionedMessage::hash_raw_message(&message_bytes))
|
|
|
|
}
|
|
|
|
}
|
2022-01-06 19:59:09 -08:00
|
|
|
|
|
|
|
/// Verify the transaction and return a list of verification results
|
|
|
|
pub fn verify_with_results(&self) -> Vec<bool> {
|
|
|
|
let message_bytes = self.message.serialize();
|
|
|
|
self._verify_with_results(&message_bytes)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn _verify_with_results(&self, message_bytes: &[u8]) -> Vec<bool> {
|
|
|
|
self.signatures
|
|
|
|
.iter()
|
2022-02-05 04:00:31 -08:00
|
|
|
.zip(self.message.static_account_keys().iter())
|
2022-01-06 19:59:09 -08:00
|
|
|
.map(|(signature, pubkey)| signature.verify(pubkey.as_ref(), message_bytes))
|
|
|
|
.collect()
|
|
|
|
}
|
2021-08-17 15:17:56 -07:00
|
|
|
}
|