Support versioned transactions in program test framework (#28739)
* Support versioned transactions in program test framework * use working bank * Update to process_transaction_with_metadata * Migrate client apis from Transaction to Into<VersionedTransaction> * feedback
This commit is contained in:
parent
f1e7ffba0c
commit
7371608722
|
@ -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",
|
||||
|
|
|
@ -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<VersionedTransaction>,
|
||||
) -> impl Future<Output = Result<(), BanksClientError>> + '_ {
|
||||
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<VersionedTransaction>,
|
||||
commitment: CommitmentLevel,
|
||||
) -> impl Future<Output = Result<Option<transaction::Result<()>>, 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<VersionedTransaction>,
|
||||
commitment: CommitmentLevel,
|
||||
) -> impl Future<Output = Result<BanksTransactionResultWithSimulation, BanksClientError>> + '_
|
||||
{
|
||||
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<VersionedTransaction>,
|
||||
) -> impl Future<Output = Result<BanksTransactionResultWithMetadata, BanksClientError>> + '_
|
||||
{
|
||||
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<VersionedTransaction>,
|
||||
commitment: CommitmentLevel,
|
||||
) -> impl Future<Output = Result<BanksTransactionResultWithSimulation, BanksClientError>> + '_
|
||||
{
|
||||
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<VersionedTransaction>,
|
||||
) -> impl Future<Output = Result<(), BanksClientError>> + '_ {
|
||||
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<VersionedTransaction>,
|
||||
) -> impl Future<Output = Result<BanksTransactionResultWithMetadata, BanksClientError>> + '_
|
||||
{
|
||||
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(
|
||||
|
|
|
@ -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<TransactionReturnData>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct TransactionMetadata {
|
||||
pub log_messages: Vec<String>,
|
||||
pub compute_units_consumed: u64,
|
||||
pub return_data: Option<TransactionReturnData>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct BanksTransactionResultWithSimulation {
|
||||
pub result: Option<transaction::Result<()>>,
|
||||
pub simulation_details: Option<TransactionSimulationDetails>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct BanksTransactionResultWithMetadata {
|
||||
pub result: transaction::Result<()>,
|
||||
pub metadata: Option<TransactionMetadata>,
|
||||
}
|
||||
|
||||
#[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<transaction::Result<()>>;
|
||||
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(
|
||||
|
|
|
@ -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<FeatureSet>,
|
||||
) -> 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<transaction::Result<()>> {
|
||||
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,
|
||||
|
|
|
@ -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" }
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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));
|
||||
|
||||
|
|
|
@ -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<VersionedTransaction>,
|
||||
) -> 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.
|
||||
|
|
Loading…
Reference in New Issue