Add runtime support for address table lookups (#22223)

* Add support for address table lookups in runtime

* feedback

* feedback
This commit is contained in:
Justin Starry
2022-01-07 11:59:09 +08:00
committed by GitHub
parent 08e64c88ed
commit 52d12cc802
16 changed files with 568 additions and 69 deletions

View File

@ -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,
}
}
}

View File

@ -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,
}),
};

View File

@ -37,9 +37,9 @@ impl Sanitize for VersionedTransaction {
Ordering::Equal => Ok(()),
}?;
// 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);
}
@ -71,16 +71,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()
}
}