From 0ca1826205953a0d2182d8de963ecf07229e440f Mon Sep 17 00:00:00 2001 From: Tao Zhu Date: Fri, 29 Sep 2023 22:23:08 +0000 Subject: [PATCH] create separate structs for runtime in different state, add coresponding traits for transacion_meta --- .../src/runtime_transaction.rs | 143 ++++++++++++------ runtime-transaction/src/transaction_meta.rs | 32 +++- 2 files changed, 127 insertions(+), 48 deletions(-) diff --git a/runtime-transaction/src/runtime_transaction.rs b/runtime-transaction/src/runtime_transaction.rs index 7dcfa2a4ef974b..62e1bfdcd480da 100644 --- a/runtime-transaction/src/runtime_transaction.rs +++ b/runtime-transaction/src/runtime_transaction.rs @@ -1,72 +1,125 @@ use { crate::{ - simple_vote_transaction_checker::is_simple_vote_transaction, transaction_meta::TransactionMeta, + simple_vote_transaction_checker::is_simple_vote_transaction, + transaction_meta::{DynamicallyLoadedMeta, StaticallyLoadedMeta, TransactionMeta}, }, solana_sdk::{ hash::Hash, message::{ - v0::{self}, - AddressLoader, LegacyMessage, SanitizedMessage, SanitizedVersionedMessage, - VersionedMessage, + AddressLoader, SanitizedMessage, SanitizedVersionedMessage, }, signature::Signature, - transaction::{MessageHash, Result, SanitizedVersionedTransaction}, + transaction::{Result, SanitizedVersionedTransaction}, }, }; -/// RuntimeTransaction is runtime face representation of transaction, as -/// SanitizedTransaction is client facing rep. +/// RuntimeTransaction is `runtime` facing representation of transaction, while +/// solana_sdk::SanitizedTransaction is client facing representation. +/// +/// It has two states: +/// 1. Statically Loaded: after receiving `packet` from sigverify and deserializing +/// it into `solana_sdk::VersioedTransaction`, then sanitizing into +/// `solana_sdk::SanitizedVersionedTransaction`, `RuntimeTransactionStaticallyLoaded` +/// can be created from it with static transaction metadata extracted. +/// 2. Dynamically Loaded: after successfully loaded account addresses from onchain +/// ALT, RuntimeTransaction transits into DynamicallyLoaded state, with +/// its dynamic metadata loaded. + #[derive(Debug, Clone, Eq, PartialEq)] -pub struct RuntimeTransaction { - message: SanitizedMessage, - message_hash: Hash, +pub struct RuntimeTransactionStaticallyLoaded { + // sanitized signatures signatures: Vec, - transaction_meta: TransactionMeta, + + // sanitized message + message: SanitizedVersionedMessage, + + // transaction meta is a collection of fields, it is updated + // during message state transition + meta: TransactionMeta, } -impl RuntimeTransaction { - /// Create a runtime transaction from a sanitized versioend transaction, - /// then load account addresses from address-lookup-table if apply, and - /// populate transaction metadata. - pub fn try_new( - // Note: transaction sanitization is a function of `sdk` that is shared - // by both runtime and client. +impl StaticallyLoadedMeta for RuntimeTransactionStaticallyLoaded { + fn message_hash(&self) -> &Hash { &self.meta.message_hash } + fn is_simple_vote_tx(&self) -> bool { self.meta.is_simple_vote_tx } +} + +impl RuntimeTransactionStaticallyLoaded{ + pub fn try_from( sanitized_versioned_tx: SanitizedVersionedTransaction, - message_hash: impl Into, + message_hash: Option, is_simple_vote_tx: Option, - address_loader: impl AddressLoader, ) -> Result { - // metadata can be lazily updated alone transaction's execution path - let mut transaction_meta = TransactionMeta::default(); - transaction_meta.set_is_simple_vote_tx(is_simple_vote_tx.unwrap_or_else(|| { - is_simple_vote_transaction(&sanitized_versioned_tx) + let meta = Self::load_static_metadata(&sanitized_versioned_tx, message_hash, is_simple_vote_tx)?; + + Ok(Self { + signatures: sanitized_versioned_tx.signatures, + message: sanitized_versioned_tx.message, + meta, + }) + } + + // private helpers + fn load_static_metadata( + sanitized_versioned_tx: &SanitizedVersionedTransaction, + message_hash: Option, + is_simple_vote_tx: Option, + ) ->Result { + let mut meta = TransactionMeta::default(); + meta.set_is_simple_vote_tx(is_simple_vote_tx.unwrap_or_else(|| { + is_simple_vote_transaction(sanitized_versioned_tx) + })); + meta.set_message_hash(message_hash.unwrap_or_else(|| { + sanitized_versioned_tx.message.message.hash() })); - let signatures = sanitized_versioned_tx.signatures; + Ok(meta) + } - let SanitizedVersionedMessage { message } = sanitized_versioned_tx.message; +} - let message_hash = match message_hash.into() { - MessageHash::Compute => message.hash(), - MessageHash::Precomputed(hash) => hash, - }; - let message = match message { - VersionedMessage::Legacy(message) => { - SanitizedMessage::Legacy(LegacyMessage::new(message)) - } - VersionedMessage::V0(message) => { - let loaded_addresses = - address_loader.load_addresses(&message.address_table_lookups)?; - SanitizedMessage::V0(v0::LoadedMessage::new(message, loaded_addresses)) - } +/// Statically Loaded transaction can transit to Dynamically Loaded with supplied +/// address_loader, to load accounts from on-chain ALT, then resolve dynamic metadata +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct RuntimeTransactionDynamicallyLoaded { + // sanitized signatures + signatures: Vec, + + // sanitized message + message: SanitizedMessage, + + // transaction meta is a collection of fields, it is updated + // during message state transition + meta: TransactionMeta, +} + +impl DynamicallyLoadedMeta for RuntimeTransactionDynamicallyLoaded { +} + +impl StaticallyLoadedMeta for RuntimeTransactionDynamicallyLoaded { + fn message_hash(&self) -> &Hash { &self.meta.message_hash } + fn is_simple_vote_tx(&self) -> bool { self.meta.is_simple_vote_tx } +} + +impl RuntimeTransactionDynamicallyLoaded{ + pub fn try_from( + statically_loaded_runtime_tx: RuntimeTransactionStaticallyLoaded, + address_loader: impl AddressLoader, + ) -> Result { + let mut tx = Self { + signatures: statically_loaded_runtime_tx.signatures, + message: SanitizedMessage::try_new(statically_loaded_runtime_tx.message, address_loader)?, + meta: statically_loaded_runtime_tx.meta, }; + tx.load_dynamic_metadata()?; - Ok(Self { - message, - message_hash, - signatures, - transaction_meta, - }) + Ok(tx) + } + + // private helpers + fn load_dynamic_metadata( + &mut self, + ) -> Result<()> { + Ok(()) } } diff --git a/runtime-transaction/src/transaction_meta.rs b/runtime-transaction/src/transaction_meta.rs index 1be6826fbf5871..1b14ac808821ca 100644 --- a/runtime-transaction/src/transaction_meta.rs +++ b/runtime-transaction/src/transaction_meta.rs @@ -1,16 +1,42 @@ //! Transaction Meta contains data that follows a transaction through the //! execution pipeline in runtime. Metadata can include limits specified by -//! compute-budget instructions, simple-vote flag, transactino costs, -//! durable nonce account etc; Metadata can be lazily populated as -//! transaction goes through execution path. +//! compute-budget instructions, simple-vote flag, transaction costs, +//! durable nonce account etc; //! +//! The premiss is if anything qualifies as metadata, then it must be valid +//! and available as long as the transaction itself is valid and available. +//! Hence they are not Option type. Their visibility are defined in traits. +//! +use { + solana_sdk::hash::Hash, +}; + +/// metadata can be extracted statically from sanitized transaction, +/// for example: message hash, simple-vote-tx flag, compute budget limits, +pub trait StaticallyLoadedMeta { + fn message_hash(&self) -> &Hash; + fn is_simple_vote_tx(&self) -> bool; +} + +/// Statically loaded meta is a supertrait of Dynamically loaded meta, when +/// transaction transited successfully into dynamically loaded, it should +/// have both meta data populated and available. +/// Dynamic metadata available after accounts addresses are loaded from +/// on-chain ALT, examples are: transaction usage costs, nonce account. +pub trait DynamicallyLoadedMeta: StaticallyLoadedMeta { +} #[derive(Clone, Debug, Default, PartialEq, Eq)] pub struct TransactionMeta { + pub message_hash: Hash, pub is_simple_vote_tx: bool, } impl TransactionMeta { + pub fn set_message_hash(&mut self, message_hash: Hash) { + self.message_hash = message_hash; + } + pub fn set_is_simple_vote_tx(&mut self, is_simple_vote_tx: bool) { self.is_simple_vote_tx = is_simple_vote_tx; }