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)
}
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(
&mut self,
ctx: Context,
@ -300,6 +312,29 @@ impl BanksClient {
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
/// 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>> + '_ {
@ -516,6 +551,11 @@ mod tests {
let recent_blockhash = banks_client.get_latest_blockhash().await?;
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();
assert_eq!(banks_client.get_balance(bob_pubkey).await?, 1);
Ok(())

View File

@ -67,6 +67,10 @@ pub trait Banks {
transaction: Transaction,
commitment: CommitmentLevel,
) -> Option<transaction::Result<()>>;
async fn simulate_transaction_with_commitment_and_context(
transaction: Transaction,
commitment: CommitmentLevel,
) -> BanksTransactionResultWithSimulation;
async fn get_account_with_commitment_and_context(
address: Pubkey,
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]
impl Banks for BanksServer {
async fn send_transaction_with_context(self, _: Context, transaction: Transaction) {
@ -252,41 +284,25 @@ impl Banks for BanksServer {
transaction: Transaction,
commitment: CommitmentLevel,
) -> BanksTransactionResultWithSimulation {
let sanitized_transaction =
match SanitizedTransaction::try_from_legacy_transaction(transaction.clone()) {
Err(err) => {
return BanksTransactionResultWithSimulation {
result: Some(Err(err)),
simulation_details: None,
};
}
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
let mut simulation_result =
simulate_transaction(&self.bank(commitment), transaction.clone());
// Simulation was ok, so process the real transaction and replace the
// simulation's result with the real transaction result
if let Some(Ok(_)) = simulation_result.result {
simulation_result.result = self
.process_transaction_with_commitment_and_context(ctx, transaction, commitment)
.await,
simulation_details: None,
.await;
}
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(