diff --git a/Cargo.lock b/Cargo.lock index b3a6acc11..8cee4506f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5919,6 +5919,7 @@ dependencies = [ "log", "serde", "solana-banks-client", + "solana-banks-interface", "solana-banks-server", "solana-bpf-loader-program", "solana-logger 1.15.0", diff --git a/banks-client/src/lib.rs b/banks-client/src/lib.rs index cbe9943fa..c701badf8 100644 --- a/banks-client/src/lib.rs +++ b/banks-client/src/lib.rs @@ -12,7 +12,10 @@ pub use { use { borsh::BorshDeserialize, futures::{future::join_all, Future, FutureExt, TryFutureExt}, - solana_banks_interface::{BanksRequest, BanksResponse, BanksTransactionResultWithSimulation}, + solana_banks_interface::{ + BanksRequest, BanksResponse, BanksTransactionResultWithMetadata, + BanksTransactionResultWithSimulation, + }, solana_program::{ clock::Slot, fee_calculator::FeeCalculator, hash::Hash, program_pack::Pack, pubkey::Pubkey, rent::Rent, sysvar::Sysvar, @@ -22,7 +25,7 @@ use { commitment_config::CommitmentLevel, message::Message, signature::Signature, - transaction::{self, Transaction}, + transaction::{self, Transaction, VersionedTransaction}, }, tarpc::{ client::{self, NewClient, RequestDispatch}, @@ -59,10 +62,10 @@ impl BanksClient { pub fn send_transaction_with_context( &mut self, ctx: Context, - transaction: Transaction, + transaction: impl Into, ) -> impl Future> + '_ { self.inner - .send_transaction_with_context(ctx, transaction) + .send_transaction_with_context(ctx, transaction.into()) .map_err(Into::into) } @@ -114,39 +117,50 @@ impl BanksClient { pub fn process_transaction_with_commitment_and_context( &mut self, ctx: Context, - transaction: Transaction, + transaction: impl Into, commitment: CommitmentLevel, ) -> impl Future>, BanksClientError>> + '_ { self.inner - .process_transaction_with_commitment_and_context(ctx, transaction, commitment) + .process_transaction_with_commitment_and_context(ctx, transaction.into(), commitment) .map_err(Into::into) } pub fn process_transaction_with_preflight_and_commitment_and_context( &mut self, ctx: Context, - transaction: Transaction, + transaction: impl Into, commitment: CommitmentLevel, ) -> impl Future> + '_ { self.inner .process_transaction_with_preflight_and_commitment_and_context( ctx, - transaction, + transaction.into(), commitment, ) .map_err(Into::into) } + pub fn process_transaction_with_metadata_and_context( + &mut self, + ctx: Context, + transaction: impl Into, + ) -> impl Future> + '_ + { + self.inner + .process_transaction_with_metadata_and_context(ctx, transaction.into()) + .map_err(Into::into) + } + pub fn simulate_transaction_with_commitment_and_context( &mut self, ctx: Context, - transaction: Transaction, + transaction: impl Into, commitment: CommitmentLevel, ) -> impl Future> + '_ { self.inner - .simulate_transaction_with_commitment_and_context(ctx, transaction, commitment) + .simulate_transaction_with_commitment_and_context(ctx, transaction.into(), commitment) .map_err(Into::into) } @@ -166,9 +180,9 @@ impl BanksClient { /// blockhash expires. pub fn send_transaction( &mut self, - transaction: Transaction, + transaction: impl Into, ) -> impl Future> + '_ { - self.send_transaction_with_context(context::current(), transaction) + self.send_transaction_with_context(context::current(), transaction.into()) } /// Return the fee parameters associated with a recent, rooted blockhash. The cluster @@ -231,6 +245,17 @@ impl BanksClient { }) } + /// Process a transaction and return the result with metadata. + pub fn process_transaction_with_metadata( + &mut self, + transaction: impl Into, + ) -> impl Future> + '_ + { + let mut ctx = context::current(); + ctx.deadline += Duration::from_secs(50); + self.process_transaction_with_metadata_and_context(ctx, transaction.into()) + } + /// Send a transaction and return any preflight (sanitization or simulation) errors, or return /// after the transaction has been rejected or reached the given level of commitment. pub fn process_transaction_with_preflight_and_commitment( diff --git a/banks-interface/src/lib.rs b/banks-interface/src/lib.rs index cf38502a2..0a86a4a5a 100644 --- a/banks-interface/src/lib.rs +++ b/banks-interface/src/lib.rs @@ -11,7 +11,7 @@ use { message::Message, pubkey::Pubkey, signature::Signature, - transaction::{self, Transaction, TransactionError}, + transaction::{self, TransactionError, VersionedTransaction}, transaction_context::TransactionReturnData, }, }; @@ -39,15 +39,29 @@ pub struct TransactionSimulationDetails { pub return_data: Option, } +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct TransactionMetadata { + pub log_messages: Vec, + pub compute_units_consumed: u64, + pub return_data: Option, +} + #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct BanksTransactionResultWithSimulation { pub result: Option>, pub simulation_details: Option, } +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct BanksTransactionResultWithMetadata { + pub result: transaction::Result<()>, + pub metadata: Option, +} + #[tarpc::service] pub trait Banks { - async fn send_transaction_with_context(transaction: Transaction); + async fn send_transaction_with_context(transaction: VersionedTransaction); #[deprecated( since = "1.9.0", note = "Please use `get_fee_for_message_with_commitment_and_context` instead" @@ -60,15 +74,18 @@ pub trait Banks { async fn get_slot_with_context(commitment: CommitmentLevel) -> Slot; async fn get_block_height_with_context(commitment: CommitmentLevel) -> u64; async fn process_transaction_with_preflight_and_commitment_and_context( - transaction: Transaction, + transaction: VersionedTransaction, commitment: CommitmentLevel, ) -> BanksTransactionResultWithSimulation; async fn process_transaction_with_commitment_and_context( - transaction: Transaction, + transaction: VersionedTransaction, commitment: CommitmentLevel, ) -> Option>; + async fn process_transaction_with_metadata_and_context( + transaction: VersionedTransaction, + ) -> BanksTransactionResultWithMetadata; async fn simulate_transaction_with_commitment_and_context( - transaction: Transaction, + transaction: VersionedTransaction, commitment: CommitmentLevel, ) -> BanksTransactionResultWithSimulation; async fn get_account_with_commitment_and_context( diff --git a/banks-server/src/banks_server.rs b/banks-server/src/banks_server.rs index d33eebdbd..ee0fc2641 100644 --- a/banks-server/src/banks_server.rs +++ b/banks-server/src/banks_server.rs @@ -3,11 +3,12 @@ use { crossbeam_channel::{unbounded, Receiver, Sender}, futures::{future, prelude::stream::StreamExt}, solana_banks_interface::{ - Banks, BanksRequest, BanksResponse, BanksTransactionResultWithSimulation, - TransactionConfirmationStatus, TransactionSimulationDetails, TransactionStatus, + Banks, BanksRequest, BanksResponse, BanksTransactionResultWithMetadata, + BanksTransactionResultWithSimulation, TransactionConfirmationStatus, TransactionMetadata, + TransactionSimulationDetails, TransactionStatus, }, solana_runtime::{ - bank::{Bank, TransactionSimulationResult}, + bank::{Bank, TransactionExecutionResult, TransactionSimulationResult}, bank_forks::BankForks, commitment::BlockCommitmentCache, }, @@ -21,7 +22,7 @@ use { message::{Message, SanitizedMessage}, pubkey::Pubkey, signature::Signature, - transaction::{self, SanitizedTransaction, Transaction}, + transaction::{self, MessageHash, SanitizedTransaction, VersionedTransaction}, }, solana_send_transaction_service::{ send_transaction_service::{SendTransactionService, TransactionInfo}, @@ -150,7 +151,7 @@ impl BanksServer { } fn verify_transaction( - transaction: &Transaction, + transaction: &SanitizedTransaction, feature_set: &Arc, ) -> transaction::Result<()> { transaction.verify()?; @@ -160,10 +161,15 @@ fn verify_transaction( fn simulate_transaction( bank: &Bank, - transaction: Transaction, + transaction: VersionedTransaction, ) -> BanksTransactionResultWithSimulation { - let sanitized_transaction = match SanitizedTransaction::try_from_legacy_transaction(transaction) - { + let sanitized_transaction = match SanitizedTransaction::try_create( + transaction, + MessageHash::Compute, + Some(false), // is_simple_vote_tx + bank, + true, // require_static_program_ids + ) { Err(err) => { return BanksTransactionResultWithSimulation { result: Some(Err(err)), @@ -192,8 +198,8 @@ fn simulate_transaction( #[tarpc::server] impl Banks for BanksServer { - async fn send_transaction_with_context(self, _: Context, transaction: Transaction) { - let blockhash = &transaction.message.recent_blockhash; + async fn send_transaction_with_context(self, _: Context, transaction: VersionedTransaction) { + let blockhash = transaction.message.recent_blockhash(); let last_valid_block_height = self .bank_forks .read() @@ -278,7 +284,7 @@ impl Banks for BanksServer { async fn process_transaction_with_preflight_and_commitment_and_context( self, ctx: Context, - transaction: Transaction, + transaction: VersionedTransaction, commitment: CommitmentLevel, ) -> BanksTransactionResultWithSimulation { let mut simulation_result = @@ -296,7 +302,7 @@ impl Banks for BanksServer { async fn simulate_transaction_with_commitment_and_context( self, _: Context, - transaction: Transaction, + transaction: VersionedTransaction, commitment: CommitmentLevel, ) -> BanksTransactionResultWithSimulation { simulate_transaction(&self.bank(commitment), transaction) @@ -305,21 +311,33 @@ impl Banks for BanksServer { async fn process_transaction_with_commitment_and_context( self, _: Context, - transaction: Transaction, + transaction: VersionedTransaction, commitment: CommitmentLevel, ) -> Option> { - if let Err(err) = verify_transaction(&transaction, &self.bank(commitment).feature_set) { + let bank = self.bank(commitment); + let sanitized_transaction = match SanitizedTransaction::try_create( + transaction.clone(), + MessageHash::Compute, + Some(false), // is_simple_vote_tx + bank.as_ref(), + true, // require_static_program_ids + ) { + Ok(tx) => tx, + Err(err) => return Some(Err(err)), + }; + + if let Err(err) = verify_transaction(&sanitized_transaction, &bank.feature_set) { return Some(Err(err)); } - let blockhash = &transaction.message.recent_blockhash; + let blockhash = transaction.message.recent_blockhash(); let last_valid_block_height = self .bank(commitment) .get_blockhash_last_valid_block_height(blockhash) .unwrap(); - let signature = transaction.signatures.get(0).cloned().unwrap_or_default(); + let signature = sanitized_transaction.signature(); let info = TransactionInfo::new( - signature, + *signature, serialize(&transaction).unwrap(), last_valid_block_height, None, @@ -327,10 +345,34 @@ impl Banks for BanksServer { None, ); self.transaction_sender.send(info).unwrap(); - self.poll_signature_status(&signature, blockhash, last_valid_block_height, commitment) + self.poll_signature_status(signature, blockhash, last_valid_block_height, commitment) .await } + async fn process_transaction_with_metadata_and_context( + self, + _: Context, + transaction: VersionedTransaction, + ) -> BanksTransactionResultWithMetadata { + let bank = self.bank_forks.read().unwrap().working_bank(); + match bank.process_transaction_with_metadata(transaction) { + TransactionExecutionResult::NotExecuted(error) => BanksTransactionResultWithMetadata { + result: Err(error), + metadata: None, + }, + TransactionExecutionResult::Executed { details, .. } => { + BanksTransactionResultWithMetadata { + result: details.status, + metadata: Some(TransactionMetadata { + compute_units_consumed: details.executed_units, + log_messages: details.log_messages.unwrap_or_default(), + return_data: details.return_data, + }), + } + } + } + } + async fn get_account_with_commitment_and_context( self, _: Context, diff --git a/program-test/Cargo.toml b/program-test/Cargo.toml index 51e9c8c16..49b091541 100644 --- a/program-test/Cargo.toml +++ b/program-test/Cargo.toml @@ -17,6 +17,7 @@ crossbeam-channel = "0.5" log = "0.4.17" serde = "1.0.144" solana-banks-client = { path = "../banks-client", version = "=1.15.0" } +solana-banks-interface = { path = "../banks-interface", version = "=1.15.0" } solana-banks-server = { path = "../banks-server", version = "=1.15.0" } solana-bpf-loader-program = { path = "../programs/bpf_loader", version = "=1.15.0" } solana-logger = { path = "../logger", version = "=1.15.0" } diff --git a/program-test/src/lib.rs b/program-test/src/lib.rs index d5b7e604e..4615ff1de 100644 --- a/program-test/src/lib.rs +++ b/program-test/src/lib.rs @@ -63,6 +63,7 @@ use { // Export types so test clients can limit their solana crate dependencies pub use { solana_banks_client::{BanksClient, BanksClientError}, + solana_banks_interface::BanksTransactionResultWithMetadata, solana_program_runtime::invoke_context::InvokeContext, solana_sdk::transaction_context::IndexOfAccount, }; diff --git a/programs/sbf/Cargo.lock b/programs/sbf/Cargo.lock index 6aabc9da1..e9befbea8 100644 --- a/programs/sbf/Cargo.lock +++ b/programs/sbf/Cargo.lock @@ -4878,6 +4878,7 @@ dependencies = [ "log", "serde", "solana-banks-client", + "solana-banks-interface", "solana-banks-server", "solana-bpf-loader-program", "solana-logger 1.15.0", diff --git a/rpc/src/rpc_subscriptions.rs b/rpc/src/rpc_subscriptions.rs index 3aae2b5d5..e43f3c00b 100644 --- a/rpc/src/rpc_subscriptions.rs +++ b/rpc/src/rpc_subscriptions.rs @@ -2951,13 +2951,13 @@ pub(crate) mod tests { &system_program::id(), ); - bank_forks + assert!(bank_forks .read() .unwrap() .get(0) .unwrap() - .process_transaction_with_logs(&tx) - .unwrap(); + .process_transaction_with_metadata(tx.clone()) + .was_executed()); subscriptions.notify_subscribers(CommitmentSlots::new_from_slot(0)); diff --git a/runtime/src/bank.rs b/runtime/src/bank.rs index 0d953c3e9..f28f5d4a8 100644 --- a/runtime/src/bank.rs +++ b/runtime/src/bank.rs @@ -6170,24 +6170,37 @@ impl Bank { .map_or(Ok(()), |sig| self.get_signature_status(sig).unwrap()) } - /// Process a Transaction and store program log data. This is used for unit tests, and simply - /// replicates the vector Bank::process_transactions method with `enable_cpi_recording: true` - pub fn process_transaction_with_logs(&self, tx: &Transaction) -> Result<()> { - let txs = vec![VersionedTransaction::from(tx.clone())]; - let batch = self.prepare_entry_batch(txs)?; - let _results = self.load_execute_and_commit_transactions( + /// Process a Transaction and store metadata. This is used for tests and the banks services. It + /// replicates the vector Bank::process_transaction method with metadata recording enabled. + #[must_use] + pub fn process_transaction_with_metadata( + &self, + tx: impl Into, + ) -> TransactionExecutionResult { + let txs = vec![tx.into()]; + let batch = match self.prepare_entry_batch(txs) { + Ok(batch) => batch, + Err(err) => return TransactionExecutionResult::NotExecuted(err), + }; + + let ( + TransactionResults { + mut execution_results, + .. + }, + .., + ) = self.load_execute_and_commit_transactions( &batch, MAX_PROCESSING_AGE, - false, - false, - true, - false, + false, // collect_balances + false, // enable_cpi_recording + true, // enable_log_recording + true, // enable_return_data_recording &mut ExecuteTimings::default(), - None, + Some(1000 * 1000), ); - tx.signatures - .get(0) - .map_or(Ok(()), |sig| self.get_signature_status(sig).unwrap()) + + execution_results.remove(0) } /// Process multiple transaction in a single batch. This is used for benches and unit tests.