This commit is contained in:
@ -43,21 +43,28 @@ impl VersionedMessage {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unmapped_keys(self) -> Vec<Pubkey> {
|
||||
pub fn static_account_keys(&self) -> &[Pubkey] {
|
||||
match self {
|
||||
Self::Legacy(message) => &message.account_keys,
|
||||
Self::V0(message) => &message.account_keys,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_static_account_keys(self) -> Vec<Pubkey> {
|
||||
match self {
|
||||
Self::Legacy(message) => message.account_keys,
|
||||
Self::V0(message) => message.account_keys,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unmapped_keys_iter(&self) -> impl Iterator<Item = &Pubkey> {
|
||||
pub fn static_account_keys_iter(&self) -> impl Iterator<Item = &Pubkey> {
|
||||
match self {
|
||||
Self::Legacy(message) => message.account_keys.iter(),
|
||||
Self::V0(message) => message.account_keys.iter(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unmapped_keys_len(&self) -> usize {
|
||||
pub fn static_account_keys_len(&self) -> usize {
|
||||
match self {
|
||||
Self::Legacy(message) => message.account_keys.len(),
|
||||
Self::V0(message) => message.account_keys.len(),
|
||||
|
@ -5,7 +5,7 @@ use {
|
||||
pubkey::Pubkey,
|
||||
sysvar,
|
||||
},
|
||||
std::{collections::HashSet, ops::Deref, convert::TryFrom},
|
||||
std::{collections::HashSet, ops::Deref},
|
||||
};
|
||||
|
||||
/// Combination of a version #0 message and its loaded addresses
|
||||
@ -34,6 +34,19 @@ pub struct LoadedAddresses {
|
||||
pub readonly: Vec<Pubkey>,
|
||||
}
|
||||
|
||||
impl FromIterator<LoadedAddresses> for LoadedAddresses {
|
||||
fn from_iter<T: IntoIterator<Item = LoadedAddresses>>(iter: T) -> Self {
|
||||
let (writable, readonly): (Vec<Vec<Pubkey>>, Vec<Vec<Pubkey>>) = iter
|
||||
.into_iter()
|
||||
.map(|addresses| (addresses.writable, addresses.readonly))
|
||||
.unzip();
|
||||
LoadedAddresses {
|
||||
writable: writable.into_iter().flatten().collect(),
|
||||
readonly: readonly.into_iter().flatten().collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl LoadedMessage {
|
||||
/// Returns an iterator of account key segments. The ordering of segments
|
||||
/// affects how account indexes from compiled instructions are resolved and
|
||||
@ -68,8 +81,9 @@ impl LoadedMessage {
|
||||
}
|
||||
|
||||
/// Returns the address of the account at the specified index of the list of
|
||||
/// message account keys constructed from unmapped keys, followed by mapped
|
||||
/// writable addresses, and lastly the list of mapped readonly addresses.
|
||||
/// message account keys constructed from static keys, followed by dynamically
|
||||
/// loaded writable addresses, and lastly the list of dynamically loaded
|
||||
/// readonly addresses.
|
||||
pub fn get_account_key(&self, mut index: usize) -> Option<&Pubkey> {
|
||||
for key_segment in self.account_keys_segment_iter() {
|
||||
if index < key_segment.len() {
|
||||
@ -88,8 +102,8 @@ impl LoadedMessage {
|
||||
let num_account_keys = self.message.account_keys.len();
|
||||
let num_signed_accounts = usize::from(header.num_required_signatures);
|
||||
if key_index >= num_account_keys {
|
||||
let mapped_addresses_index = key_index.saturating_sub(num_account_keys);
|
||||
mapped_addresses_index < self.loaded_addresses.writable.len()
|
||||
let loaded_addresses_index = key_index.saturating_sub(num_account_keys);
|
||||
loaded_addresses_index < self.loaded_addresses.writable.len()
|
||||
} else if key_index >= num_signed_accounts {
|
||||
let num_unsigned_accounts = num_account_keys.saturating_sub(num_signed_accounts);
|
||||
let num_writable_unsigned_accounts = num_unsigned_accounts
|
||||
|
@ -109,6 +109,22 @@ pub enum TransactionError {
|
||||
/// Transaction locked too many accounts
|
||||
#[error("Transaction locked too many accounts")]
|
||||
TooManyAccountLocks,
|
||||
|
||||
/// Address lookup table not found
|
||||
#[error("Transaction loads an address table account that doesn't exist")]
|
||||
AddressLookupTableNotFound,
|
||||
|
||||
/// Attempted to lookup addresses from an account owned by the wrong program
|
||||
#[error("Transaction loads an address table account with an invalid owner")]
|
||||
InvalidAddressLookupTableOwner,
|
||||
|
||||
/// Attempted to lookup addresses from an invalid account
|
||||
#[error("Transaction loads an address table account with invalid data")]
|
||||
InvalidAddressLookupTableData,
|
||||
|
||||
/// Address table lookup uses an invalid index
|
||||
#[error("Transaction address table lookup uses an invalid index")]
|
||||
InvalidAddressLookupTableIndex,
|
||||
}
|
||||
|
||||
impl From<SanitizeError> for TransactionError {
|
||||
@ -122,3 +138,33 @@ impl From<SanitizeMessageError> for TransactionError {
|
||||
Self::SanitizeFailure
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Error, PartialEq, Eq, Clone)]
|
||||
pub enum AddressLookupError {
|
||||
/// Attempted to lookup addresses from a table that does not exist
|
||||
#[error("Attempted to lookup addresses from a table that does not exist")]
|
||||
LookupTableAccountNotFound,
|
||||
|
||||
/// Attempted to lookup addresses from an account owned by the wrong program
|
||||
#[error("Attempted to lookup addresses from an account owned by the wrong program")]
|
||||
InvalidAccountOwner,
|
||||
|
||||
/// Attempted to lookup addresses from an invalid account
|
||||
#[error("Attempted to lookup addresses from an invalid account")]
|
||||
InvalidAccountData,
|
||||
|
||||
/// Address lookup contains an invalid index
|
||||
#[error("Address lookup contains an invalid index")]
|
||||
InvalidLookupIndex,
|
||||
}
|
||||
|
||||
impl From<AddressLookupError> for TransactionError {
|
||||
fn from(err: AddressLookupError) -> Self {
|
||||
match err {
|
||||
AddressLookupError::LookupTableAccountNotFound => Self::AddressLookupTableNotFound,
|
||||
AddressLookupError::InvalidAccountOwner => Self::InvalidAddressLookupTableOwner,
|
||||
AddressLookupError::InvalidAccountData => Self::InvalidAddressLookupTableData,
|
||||
AddressLookupError::InvalidLookupIndex => Self::InvalidAddressLookupTableIndex,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,9 @@
|
||||
#![cfg(feature = "full")]
|
||||
|
||||
use {
|
||||
crate::{
|
||||
hash::Hash,
|
||||
message::{
|
||||
v0::{self, LoadedAddresses},
|
||||
v0::{self, LoadedAddresses, MessageAddressTableLookup},
|
||||
SanitizedMessage, VersionedMessage,
|
||||
},
|
||||
nonce::NONCED_TX_MARKER_IX_INDEX,
|
||||
@ -51,7 +50,7 @@ impl SanitizedTransaction {
|
||||
tx: VersionedTransaction,
|
||||
message_hash: Hash,
|
||||
is_simple_vote_tx: Option<bool>,
|
||||
address_loader: impl Fn(&v0::Message) -> Result<LoadedAddresses>,
|
||||
address_loader: impl Fn(&[MessageAddressTableLookup]) -> Result<LoadedAddresses>,
|
||||
) -> Result<Self> {
|
||||
tx.sanitize()?;
|
||||
|
||||
@ -59,7 +58,7 @@ impl SanitizedTransaction {
|
||||
let message = match tx.message {
|
||||
VersionedMessage::Legacy(message) => SanitizedMessage::Legacy(message),
|
||||
VersionedMessage::V0(message) => SanitizedMessage::V0(v0::LoadedMessage {
|
||||
loaded_addresses: address_loader(&message)?,
|
||||
loaded_addresses: address_loader(&message.address_table_lookups)?,
|
||||
message,
|
||||
}),
|
||||
};
|
||||
|
@ -35,9 +35,9 @@ impl Sanitize for VersionedTransaction {
|
||||
return Err(SanitizeError::IndexOutOfBounds);
|
||||
}
|
||||
|
||||
// Signatures are verified before message keys are mapped so all signers
|
||||
// must correspond to unmapped keys.
|
||||
if self.signatures.len() > self.message.unmapped_keys_len() {
|
||||
// Signatures are verified before message keys are loaded so all signers
|
||||
// must correspond to static account keys.
|
||||
if self.signatures.len() > self.message.static_account_keys_len() {
|
||||
return Err(SanitizeError::IndexOutOfBounds);
|
||||
}
|
||||
|
||||
@ -69,16 +69,28 @@ impl VersionedTransaction {
|
||||
/// Verify the transaction and hash its message
|
||||
pub fn verify_and_hash_message(&self) -> Result<Hash> {
|
||||
let message_bytes = self.message.serialize();
|
||||
if self
|
||||
.signatures
|
||||
if !self
|
||||
._verify_with_results(&message_bytes)
|
||||
.iter()
|
||||
.zip(self.message.unmapped_keys_iter())
|
||||
.map(|(signature, pubkey)| signature.verify(pubkey.as_ref(), &message_bytes))
|
||||
.any(|verified| !verified)
|
||||
.all(|verify_result| *verify_result)
|
||||
{
|
||||
Err(TransactionError::SignatureFailure)
|
||||
} else {
|
||||
Ok(VersionedMessage::hash_raw_message(&message_bytes))
|
||||
}
|
||||
}
|
||||
|
||||
/// 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()
|
||||
.zip(self.message.static_account_keys_iter())
|
||||
.map(|(signature, pubkey)| signature.verify(pubkey.as_ref(), message_bytes))
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user