banks-client: Add `simulate_transaction` implementation (#25830)

This commit is contained in:
Jon Cinque 2022-06-08 13:56:54 +02:00 committed by GitHub
parent 591986eb01
commit 3be11061ed
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 93 additions and 33 deletions

View File

@ -138,6 +138,18 @@ impl BanksClient {
.map_err(Into::into) .map_err(Into::into)
} }
pub fn simulate_transaction_with_commitment_and_context(
&mut self,
ctx: Context,
transaction: Transaction,
commitment: CommitmentLevel,
) -> impl Future<Output = Result<BanksTransactionResultWithSimulation, BanksClientError>> + '_
{
self.inner
.simulate_transaction_with_commitment_and_context(ctx, transaction, commitment)
.map_err(Into::into)
}
pub fn get_account_with_commitment_and_context( pub fn get_account_with_commitment_and_context(
&mut self, &mut self,
ctx: Context, ctx: Context,
@ -300,6 +312,29 @@ impl BanksClient {
self.process_transactions_with_commitment(transactions, CommitmentLevel::default()) self.process_transactions_with_commitment(transactions, CommitmentLevel::default())
} }
/// Simulate a transaction at the given commitment level
pub fn simulate_transaction_with_commitment(
&mut self,
transaction: Transaction,
commitment: CommitmentLevel,
) -> impl Future<Output = Result<BanksTransactionResultWithSimulation, BanksClientError>> + '_
{
self.simulate_transaction_with_commitment_and_context(
context::current(),
transaction,
commitment,
)
}
/// Simulate a transaction at the default commitment level
pub fn simulate_transaction(
&mut self,
transaction: Transaction,
) -> impl Future<Output = Result<BanksTransactionResultWithSimulation, BanksClientError>> + '_
{
self.simulate_transaction_with_commitment(transaction, CommitmentLevel::default())
}
/// Return the most recent rooted slot. All transactions at or below this slot /// Return the most recent rooted slot. All transactions at or below this slot
/// are said to be finalized. The cluster will not fork to a higher slot. /// are said to be finalized. The cluster will not fork to a higher slot.
pub fn get_root_slot(&mut self) -> impl Future<Output = Result<Slot, BanksClientError>> + '_ { pub fn get_root_slot(&mut self) -> impl Future<Output = Result<Slot, BanksClientError>> + '_ {
@ -516,6 +551,11 @@ mod tests {
let recent_blockhash = banks_client.get_latest_blockhash().await?; let recent_blockhash = banks_client.get_latest_blockhash().await?;
let transaction = Transaction::new(&[&genesis.mint_keypair], message, recent_blockhash); let transaction = Transaction::new(&[&genesis.mint_keypair], message, recent_blockhash);
let simulation_result = banks_client
.simulate_transaction(transaction.clone())
.await
.unwrap();
assert!(simulation_result.result.unwrap().is_ok());
banks_client.process_transaction(transaction).await.unwrap(); banks_client.process_transaction(transaction).await.unwrap();
assert_eq!(banks_client.get_balance(bob_pubkey).await?, 1); assert_eq!(banks_client.get_balance(bob_pubkey).await?, 1);
Ok(()) Ok(())

View File

@ -67,6 +67,10 @@ pub trait Banks {
transaction: Transaction, transaction: Transaction,
commitment: CommitmentLevel, commitment: CommitmentLevel,
) -> Option<transaction::Result<()>>; ) -> Option<transaction::Result<()>>;
async fn simulate_transaction_with_commitment_and_context(
transaction: Transaction,
commitment: CommitmentLevel,
) -> BanksTransactionResultWithSimulation;
async fn get_account_with_commitment_and_context( async fn get_account_with_commitment_and_context(
address: Pubkey, address: Pubkey,
commitment: CommitmentLevel, commitment: CommitmentLevel,

View File

@ -161,6 +161,38 @@ fn verify_transaction(
} }
} }
fn simulate_transaction(
bank: &Bank,
transaction: Transaction,
) -> BanksTransactionResultWithSimulation {
let sanitized_transaction = match SanitizedTransaction::try_from_legacy_transaction(transaction)
{
Err(err) => {
return BanksTransactionResultWithSimulation {
result: Some(Err(err)),
simulation_details: None,
};
}
Ok(tx) => tx,
};
let TransactionSimulationResult {
result,
logs,
post_simulation_accounts: _,
units_consumed,
return_data,
} = bank.simulate_transaction_unchecked(sanitized_transaction);
let simulation_details = TransactionSimulationDetails {
logs,
units_consumed,
return_data,
};
BanksTransactionResultWithSimulation {
result: Some(result),
simulation_details: Some(simulation_details),
}
}
#[tarpc::server] #[tarpc::server]
impl Banks for BanksServer { impl Banks for BanksServer {
async fn send_transaction_with_context(self, _: Context, transaction: Transaction) { async fn send_transaction_with_context(self, _: Context, transaction: Transaction) {
@ -252,41 +284,25 @@ impl Banks for BanksServer {
transaction: Transaction, transaction: Transaction,
commitment: CommitmentLevel, commitment: CommitmentLevel,
) -> BanksTransactionResultWithSimulation { ) -> BanksTransactionResultWithSimulation {
let sanitized_transaction = let mut simulation_result =
match SanitizedTransaction::try_from_legacy_transaction(transaction.clone()) { simulate_transaction(&self.bank(commitment), transaction.clone());
Err(err) => { // Simulation was ok, so process the real transaction and replace the
return BanksTransactionResultWithSimulation { // simulation's result with the real transaction result
result: Some(Err(err)), if let Some(Ok(_)) = simulation_result.result {
simulation_details: None, simulation_result.result = self
};
}
Ok(tx) => tx,
};
if let TransactionSimulationResult {
result: Err(err),
logs,
post_simulation_accounts: _,
units_consumed,
return_data,
} = self
.bank(commitment)
.simulate_transaction_unchecked(sanitized_transaction)
{
return BanksTransactionResultWithSimulation {
result: Some(Err(err)),
simulation_details: Some(TransactionSimulationDetails {
logs,
units_consumed,
return_data,
}),
};
}
BanksTransactionResultWithSimulation {
result: self
.process_transaction_with_commitment_and_context(ctx, transaction, commitment) .process_transaction_with_commitment_and_context(ctx, transaction, commitment)
.await, .await;
simulation_details: None,
} }
simulation_result
}
async fn simulate_transaction_with_commitment_and_context(
self,
_: Context,
transaction: Transaction,
commitment: CommitmentLevel,
) -> BanksTransactionResultWithSimulation {
simulate_transaction(&self.bank(commitment), transaction)
} }
async fn process_transaction_with_commitment_and_context( async fn process_transaction_with_commitment_and_context(