RPC: Add inner instructions to simulate transaction response (#34313)
* rpc: add optional `innerInstructions: bool` arg to `simulateTransaction` * bank: enable cpi recording in simulate * sdk: move `InnerInstructions` into SDK from accounts DB * bank: return inner instructions from simulate tx * rpc: return inner instructions from simulate tx * rpc: simulate tx: add `jsonParsed` support for inner instructions * accounts db: add deprecated attribute to re-exported inner instructions * rpc: de-dupe inner instruction mapping * update deprecated comment Co-authored-by: Tyera <teulberg@gmail.com> --------- Co-authored-by: Tyera <teulberg@gmail.com>
This commit is contained in:
parent
1f2b72b6e3
commit
171c58c5c0
|
@ -1,3 +1,9 @@
|
||||||
|
// Re-exported since these have moved to `solana_sdk`.
|
||||||
|
#[deprecated(
|
||||||
|
since = "1.18.0",
|
||||||
|
note = "Please use `solana_sdk::inner_instruction` types instead"
|
||||||
|
)]
|
||||||
|
pub use solana_sdk::inner_instruction::{InnerInstruction, InnerInstructionsList};
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
nonce_info::{NonceFull, NonceInfo, NoncePartial},
|
nonce_info::{NonceFull, NonceInfo, NoncePartial},
|
||||||
|
@ -105,22 +111,6 @@ impl DurableNonceFee {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An ordered list of compiled instructions that were invoked during a
|
|
||||||
/// transaction instruction
|
|
||||||
pub type InnerInstructions = Vec<InnerInstruction>;
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub struct InnerInstruction {
|
|
||||||
pub instruction: CompiledInstruction,
|
|
||||||
/// Invocation stack height of this instruction. Instruction stack height
|
|
||||||
/// starts at 1 for transaction instructions.
|
|
||||||
pub stack_height: u8,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A list of compiled instructions that were invoked during each instruction of
|
|
||||||
/// a transaction
|
|
||||||
pub type InnerInstructionsList = Vec<InnerInstructions>;
|
|
||||||
|
|
||||||
/// Extract the InnerInstructionsList from a TransactionContext
|
/// Extract the InnerInstructionsList from a TransactionContext
|
||||||
pub fn inner_instructions_list_from_instruction_trace(
|
pub fn inner_instructions_list_from_instruction_trace(
|
||||||
transaction_context: &TransactionContext,
|
transaction_context: &TransactionContext,
|
||||||
|
|
|
@ -8,6 +8,7 @@ use {
|
||||||
commitment_config::CommitmentLevel,
|
commitment_config::CommitmentLevel,
|
||||||
fee_calculator::FeeCalculator,
|
fee_calculator::FeeCalculator,
|
||||||
hash::Hash,
|
hash::Hash,
|
||||||
|
inner_instruction::InnerInstructions,
|
||||||
message::Message,
|
message::Message,
|
||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
signature::Signature,
|
signature::Signature,
|
||||||
|
@ -37,6 +38,7 @@ pub struct TransactionSimulationDetails {
|
||||||
pub logs: Vec<String>,
|
pub logs: Vec<String>,
|
||||||
pub units_consumed: u64,
|
pub units_consumed: u64,
|
||||||
pub return_data: Option<TransactionReturnData>,
|
pub return_data: Option<TransactionReturnData>,
|
||||||
|
pub inner_instructions: Option<Vec<InnerInstructions>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
|
|
@ -194,11 +194,14 @@ fn simulate_transaction(
|
||||||
post_simulation_accounts: _,
|
post_simulation_accounts: _,
|
||||||
units_consumed,
|
units_consumed,
|
||||||
return_data,
|
return_data,
|
||||||
} = bank.simulate_transaction_unchecked(sanitized_transaction);
|
inner_instructions,
|
||||||
|
} = bank.simulate_transaction_unchecked(&sanitized_transaction, false);
|
||||||
|
|
||||||
let simulation_details = TransactionSimulationDetails {
|
let simulation_details = TransactionSimulationDetails {
|
||||||
logs,
|
logs,
|
||||||
units_consumed,
|
units_consumed,
|
||||||
return_data,
|
return_data,
|
||||||
|
inner_instructions,
|
||||||
};
|
};
|
||||||
BanksTransactionResultWithSimulation {
|
BanksTransactionResultWithSimulation {
|
||||||
result: Some(result),
|
result: Some(result),
|
||||||
|
|
|
@ -54,7 +54,7 @@ use {
|
||||||
transaction::VersionedTransaction,
|
transaction::VersionedTransaction,
|
||||||
},
|
},
|
||||||
solana_transaction_status::{
|
solana_transaction_status::{
|
||||||
ConfirmedTransactionWithStatusMeta, InnerInstructions, TransactionStatusMeta,
|
map_inner_instructions, ConfirmedTransactionWithStatusMeta, TransactionStatusMeta,
|
||||||
TransactionWithStatusMeta, VersionedTransactionWithStatusMeta,
|
TransactionWithStatusMeta, VersionedTransactionWithStatusMeta,
|
||||||
},
|
},
|
||||||
std::collections::HashMap,
|
std::collections::HashMap,
|
||||||
|
@ -212,21 +212,7 @@ fn execute_transactions(
|
||||||
);
|
);
|
||||||
|
|
||||||
let inner_instructions = inner_instructions.map(|inner_instructions| {
|
let inner_instructions = inner_instructions.map(|inner_instructions| {
|
||||||
inner_instructions
|
map_inner_instructions(inner_instructions).collect()
|
||||||
.into_iter()
|
|
||||||
.enumerate()
|
|
||||||
.map(|(index, instructions)| InnerInstructions {
|
|
||||||
index: index as u8,
|
|
||||||
instructions: instructions
|
|
||||||
.into_iter()
|
|
||||||
.map(|ix| solana_transaction_status::InnerInstruction {
|
|
||||||
instruction: ix.instruction,
|
|
||||||
stack_height: Some(u32::from(ix.stack_height)),
|
|
||||||
})
|
|
||||||
.collect(),
|
|
||||||
})
|
|
||||||
.filter(|i| !i.instructions.is_empty())
|
|
||||||
.collect()
|
|
||||||
});
|
});
|
||||||
|
|
||||||
let tx_status_meta = TransactionStatusMeta {
|
let tx_status_meta = TransactionStatusMeta {
|
||||||
|
@ -766,7 +752,7 @@ fn test_return_data_and_log_data_syscall() {
|
||||||
let transaction = Transaction::new(&[&mint_keypair], message, blockhash);
|
let transaction = Transaction::new(&[&mint_keypair], message, blockhash);
|
||||||
let sanitized_tx = SanitizedTransaction::from_transaction_for_tests(transaction);
|
let sanitized_tx = SanitizedTransaction::from_transaction_for_tests(transaction);
|
||||||
|
|
||||||
let result = bank.simulate_transaction(sanitized_tx);
|
let result = bank.simulate_transaction(&sanitized_tx, false);
|
||||||
|
|
||||||
assert!(result.result.is_ok());
|
assert!(result.result.is_ok());
|
||||||
|
|
||||||
|
|
|
@ -44,6 +44,8 @@ pub struct RpcSimulateTransactionConfig {
|
||||||
pub encoding: Option<UiTransactionEncoding>,
|
pub encoding: Option<UiTransactionEncoding>,
|
||||||
pub accounts: Option<RpcSimulateTransactionAccountsConfig>,
|
pub accounts: Option<RpcSimulateTransactionAccountsConfig>,
|
||||||
pub min_context_slot: Option<Slot>,
|
pub min_context_slot: Option<Slot>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub inner_instructions: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
|
|
@ -11,7 +11,7 @@ use {
|
||||||
},
|
},
|
||||||
solana_transaction_status::{
|
solana_transaction_status::{
|
||||||
ConfirmedTransactionStatusWithSignature, TransactionConfirmationStatus, UiConfirmedBlock,
|
ConfirmedTransactionStatusWithSignature, TransactionConfirmationStatus, UiConfirmedBlock,
|
||||||
UiTransactionReturnData,
|
UiInnerInstructions, UiTransactionReturnData,
|
||||||
},
|
},
|
||||||
std::{collections::HashMap, fmt, net::SocketAddr, str::FromStr},
|
std::{collections::HashMap, fmt, net::SocketAddr, str::FromStr},
|
||||||
thiserror::Error,
|
thiserror::Error,
|
||||||
|
@ -423,6 +423,7 @@ pub struct RpcSimulateTransactionResult {
|
||||||
pub accounts: Option<Vec<Option<UiAccount>>>,
|
pub accounts: Option<Vec<Option<UiAccount>>>,
|
||||||
pub units_consumed: Option<u64>,
|
pub units_consumed: Option<u64>,
|
||||||
pub return_data: Option<UiTransactionReturnData>,
|
pub return_data: Option<UiTransactionReturnData>,
|
||||||
|
pub inner_instructions: Option<Vec<UiInnerInstructions>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
|
||||||
|
|
|
@ -350,6 +350,7 @@ impl RpcSender for MockSender {
|
||||||
accounts: None,
|
accounts: None,
|
||||||
units_consumed: None,
|
units_consumed: None,
|
||||||
return_data: None,
|
return_data: None,
|
||||||
|
inner_instructions: None,
|
||||||
},
|
},
|
||||||
})?,
|
})?,
|
||||||
"getMinimumBalanceForRentExemption" => json![20],
|
"getMinimumBalanceForRentExemption" => json![20],
|
||||||
|
|
|
@ -87,10 +87,10 @@ use {
|
||||||
solana_storage_bigtable::Error as StorageError,
|
solana_storage_bigtable::Error as StorageError,
|
||||||
solana_streamer::socket::SocketAddrSpace,
|
solana_streamer::socket::SocketAddrSpace,
|
||||||
solana_transaction_status::{
|
solana_transaction_status::{
|
||||||
BlockEncodingOptions, ConfirmedBlock, ConfirmedTransactionStatusWithSignature,
|
map_inner_instructions, BlockEncodingOptions, ConfirmedBlock,
|
||||||
ConfirmedTransactionWithStatusMeta, EncodedConfirmedTransactionWithStatusMeta, Reward,
|
ConfirmedTransactionStatusWithSignature, ConfirmedTransactionWithStatusMeta,
|
||||||
RewardType, TransactionBinaryEncoding, TransactionConfirmationStatus, TransactionStatus,
|
EncodedConfirmedTransactionWithStatusMeta, Reward, RewardType, TransactionBinaryEncoding,
|
||||||
UiConfirmedBlock, UiTransactionEncoding,
|
TransactionConfirmationStatus, TransactionStatus, UiConfirmedBlock, UiTransactionEncoding,
|
||||||
},
|
},
|
||||||
solana_vote_program::vote_state::{VoteState, MAX_LOCKOUT_HISTORY},
|
solana_vote_program::vote_state::{VoteState, MAX_LOCKOUT_HISTORY},
|
||||||
spl_token_2022::{
|
spl_token_2022::{
|
||||||
|
@ -3266,6 +3266,7 @@ pub mod rpc_full {
|
||||||
use {
|
use {
|
||||||
super::*,
|
super::*,
|
||||||
solana_sdk::message::{SanitizedVersionedMessage, VersionedMessage},
|
solana_sdk::message::{SanitizedVersionedMessage, VersionedMessage},
|
||||||
|
solana_transaction_status::UiInnerInstructions,
|
||||||
};
|
};
|
||||||
#[rpc]
|
#[rpc]
|
||||||
pub trait Full {
|
pub trait Full {
|
||||||
|
@ -3676,7 +3677,8 @@ pub mod rpc_full {
|
||||||
post_simulation_accounts: _,
|
post_simulation_accounts: _,
|
||||||
units_consumed,
|
units_consumed,
|
||||||
return_data,
|
return_data,
|
||||||
} = preflight_bank.simulate_transaction(transaction)
|
inner_instructions: _, // Always `None` due to `enable_cpi_recording = false`
|
||||||
|
} = preflight_bank.simulate_transaction(&transaction, false)
|
||||||
{
|
{
|
||||||
match err {
|
match err {
|
||||||
TransactionError::BlockhashNotFound => {
|
TransactionError::BlockhashNotFound => {
|
||||||
|
@ -3694,6 +3696,7 @@ pub mod rpc_full {
|
||||||
accounts: None,
|
accounts: None,
|
||||||
units_consumed: Some(units_consumed),
|
units_consumed: Some(units_consumed),
|
||||||
return_data: return_data.map(|return_data| return_data.into()),
|
return_data: return_data.map(|return_data| return_data.into()),
|
||||||
|
inner_instructions: None,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
.into());
|
.into());
|
||||||
|
@ -3724,6 +3727,7 @@ pub mod rpc_full {
|
||||||
encoding,
|
encoding,
|
||||||
accounts: config_accounts,
|
accounts: config_accounts,
|
||||||
min_context_slot,
|
min_context_slot,
|
||||||
|
inner_instructions: enable_cpi_recording,
|
||||||
} = config.unwrap_or_default();
|
} = config.unwrap_or_default();
|
||||||
let tx_encoding = encoding.unwrap_or(UiTransactionEncoding::Base58);
|
let tx_encoding = encoding.unwrap_or(UiTransactionEncoding::Base58);
|
||||||
let binary_encoding = tx_encoding.into_binary_encoding().ok_or_else(|| {
|
let binary_encoding = tx_encoding.into_binary_encoding().ok_or_else(|| {
|
||||||
|
@ -3753,7 +3757,6 @@ pub mod rpc_full {
|
||||||
if sig_verify {
|
if sig_verify {
|
||||||
verify_transaction(&transaction, &bank.feature_set)?;
|
verify_transaction(&transaction, &bank.feature_set)?;
|
||||||
}
|
}
|
||||||
let number_of_accounts = transaction.message().account_keys().len();
|
|
||||||
|
|
||||||
let TransactionSimulationResult {
|
let TransactionSimulationResult {
|
||||||
result,
|
result,
|
||||||
|
@ -3761,7 +3764,11 @@ pub mod rpc_full {
|
||||||
post_simulation_accounts,
|
post_simulation_accounts,
|
||||||
units_consumed,
|
units_consumed,
|
||||||
return_data,
|
return_data,
|
||||||
} = bank.simulate_transaction(transaction);
|
inner_instructions,
|
||||||
|
} = bank.simulate_transaction(&transaction, enable_cpi_recording);
|
||||||
|
|
||||||
|
let account_keys = transaction.message().account_keys();
|
||||||
|
let number_of_accounts = account_keys.len();
|
||||||
|
|
||||||
let accounts = if let Some(config_accounts) = config_accounts {
|
let accounts = if let Some(config_accounts) = config_accounts {
|
||||||
let accounts_encoding = config_accounts
|
let accounts_encoding = config_accounts
|
||||||
|
@ -3804,6 +3811,12 @@ pub mod rpc_full {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let inner_instructions = inner_instructions.map(|info| {
|
||||||
|
map_inner_instructions(info)
|
||||||
|
.map(|converted| UiInnerInstructions::parse(converted, &account_keys))
|
||||||
|
.collect()
|
||||||
|
});
|
||||||
|
|
||||||
Ok(new_response(
|
Ok(new_response(
|
||||||
bank,
|
bank,
|
||||||
RpcSimulateTransactionResult {
|
RpcSimulateTransactionResult {
|
||||||
|
@ -3812,6 +3825,7 @@ pub mod rpc_full {
|
||||||
accounts,
|
accounts,
|
||||||
units_consumed: Some(units_consumed),
|
units_consumed: Some(units_consumed),
|
||||||
return_data: return_data.map(|return_data| return_data.into()),
|
return_data: return_data.map(|return_data| return_data.into()),
|
||||||
|
inner_instructions,
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
@ -5913,6 +5927,7 @@ pub mod tests {
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"err":null,
|
"err":null,
|
||||||
|
"innerInstructions": null,
|
||||||
"logs":[
|
"logs":[
|
||||||
"Program 11111111111111111111111111111111 invoke [1]",
|
"Program 11111111111111111111111111111111 invoke [1]",
|
||||||
"Program 11111111111111111111111111111111 success"
|
"Program 11111111111111111111111111111111 success"
|
||||||
|
@ -5997,6 +6012,7 @@ pub mod tests {
|
||||||
"value":{
|
"value":{
|
||||||
"accounts":null,
|
"accounts":null,
|
||||||
"err":null,
|
"err":null,
|
||||||
|
"innerInstructions":null,
|
||||||
"logs":[
|
"logs":[
|
||||||
"Program 11111111111111111111111111111111 invoke [1]",
|
"Program 11111111111111111111111111111111 invoke [1]",
|
||||||
"Program 11111111111111111111111111111111 success"
|
"Program 11111111111111111111111111111111 success"
|
||||||
|
@ -6025,6 +6041,7 @@ pub mod tests {
|
||||||
"value":{
|
"value":{
|
||||||
"accounts":null,
|
"accounts":null,
|
||||||
"err":null,
|
"err":null,
|
||||||
|
"innerInstructions":null,
|
||||||
"logs":[
|
"logs":[
|
||||||
"Program 11111111111111111111111111111111 invoke [1]",
|
"Program 11111111111111111111111111111111 invoke [1]",
|
||||||
"Program 11111111111111111111111111111111 success"
|
"Program 11111111111111111111111111111111 success"
|
||||||
|
@ -6077,6 +6094,7 @@ pub mod tests {
|
||||||
"value":{
|
"value":{
|
||||||
"err":"BlockhashNotFound",
|
"err":"BlockhashNotFound",
|
||||||
"accounts":null,
|
"accounts":null,
|
||||||
|
"innerInstructions":null,
|
||||||
"logs":[],
|
"logs":[],
|
||||||
"returnData":null,
|
"returnData":null,
|
||||||
"unitsConsumed":0,
|
"unitsConsumed":0,
|
||||||
|
@ -6103,6 +6121,7 @@ pub mod tests {
|
||||||
"value":{
|
"value":{
|
||||||
"accounts":null,
|
"accounts":null,
|
||||||
"err":null,
|
"err":null,
|
||||||
|
"innerInstructions":null,
|
||||||
"logs":[
|
"logs":[
|
||||||
"Program 11111111111111111111111111111111 invoke [1]",
|
"Program 11111111111111111111111111111111 invoke [1]",
|
||||||
"Program 11111111111111111111111111111111 success"
|
"Program 11111111111111111111111111111111 success"
|
||||||
|
@ -6483,7 +6502,7 @@ pub mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
res,
|
res,
|
||||||
Some(
|
Some(
|
||||||
r#"{"jsonrpc":"2.0","error":{"code":-32002,"message":"Transaction simulation failed: Blockhash not found","data":{"accounts":null,"err":"BlockhashNotFound","logs":[],"returnData":null,"unitsConsumed":0}},"id":1}"#.to_string(),
|
r#"{"jsonrpc":"2.0","error":{"code":-32002,"message":"Transaction simulation failed: Blockhash not found","data":{"accounts":null,"err":"BlockhashNotFound","innerInstructions":null,"logs":[],"returnData":null,"unitsConsumed":0}},"id":1}"#.to_string(),
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ use {
|
||||||
blockstore_processor::{TransactionStatusBatch, TransactionStatusMessage},
|
blockstore_processor::{TransactionStatusBatch, TransactionStatusMessage},
|
||||||
},
|
},
|
||||||
solana_transaction_status::{
|
solana_transaction_status::{
|
||||||
extract_and_fmt_memos, InnerInstruction, InnerInstructions, Reward, TransactionStatusMeta,
|
extract_and_fmt_memos, map_inner_instructions, Reward, TransactionStatusMeta,
|
||||||
},
|
},
|
||||||
std::{
|
std::{
|
||||||
sync::{
|
sync::{
|
||||||
|
@ -121,21 +121,7 @@ impl TransactionStatusService {
|
||||||
let tx_account_locks = transaction.get_account_locks_unchecked();
|
let tx_account_locks = transaction.get_account_locks_unchecked();
|
||||||
|
|
||||||
let inner_instructions = inner_instructions.map(|inner_instructions| {
|
let inner_instructions = inner_instructions.map(|inner_instructions| {
|
||||||
inner_instructions
|
map_inner_instructions(inner_instructions).collect()
|
||||||
.into_iter()
|
|
||||||
.enumerate()
|
|
||||||
.map(|(index, instructions)| InnerInstructions {
|
|
||||||
index: index as u8,
|
|
||||||
instructions: instructions
|
|
||||||
.into_iter()
|
|
||||||
.map(|info| InnerInstruction {
|
|
||||||
instruction: info.instruction,
|
|
||||||
stack_height: Some(u32::from(info.stack_height)),
|
|
||||||
})
|
|
||||||
.collect(),
|
|
||||||
})
|
|
||||||
.filter(|i| !i.instructions.is_empty())
|
|
||||||
.collect()
|
|
||||||
});
|
});
|
||||||
|
|
||||||
let pre_token_balances = Some(pre_token_balances);
|
let pre_token_balances = Some(pre_token_balances);
|
||||||
|
|
|
@ -150,6 +150,7 @@ use {
|
||||||
hash::{extend_and_hash, hashv, Hash},
|
hash::{extend_and_hash, hashv, Hash},
|
||||||
incinerator,
|
incinerator,
|
||||||
inflation::Inflation,
|
inflation::Inflation,
|
||||||
|
inner_instruction::InnerInstructions,
|
||||||
instruction::InstructionError,
|
instruction::InstructionError,
|
||||||
loader_v4::{self, LoaderV4State, LoaderV4Status},
|
loader_v4::{self, LoaderV4State, LoaderV4Status},
|
||||||
message::{AccountKeys, SanitizedMessage},
|
message::{AccountKeys, SanitizedMessage},
|
||||||
|
@ -338,6 +339,7 @@ pub struct TransactionSimulationResult {
|
||||||
pub post_simulation_accounts: Vec<TransactionAccount>,
|
pub post_simulation_accounts: Vec<TransactionAccount>,
|
||||||
pub units_consumed: u64,
|
pub units_consumed: u64,
|
||||||
pub return_data: Option<TransactionReturnData>,
|
pub return_data: Option<TransactionReturnData>,
|
||||||
|
pub inner_instructions: Option<Vec<InnerInstructions>>,
|
||||||
}
|
}
|
||||||
pub struct TransactionBalancesSet {
|
pub struct TransactionBalancesSet {
|
||||||
pub pre_balances: TransactionBalances,
|
pub pre_balances: TransactionBalances,
|
||||||
|
@ -4308,23 +4310,25 @@ impl Bank {
|
||||||
/// Run transactions against a frozen bank without committing the results
|
/// Run transactions against a frozen bank without committing the results
|
||||||
pub fn simulate_transaction(
|
pub fn simulate_transaction(
|
||||||
&self,
|
&self,
|
||||||
transaction: SanitizedTransaction,
|
transaction: &SanitizedTransaction,
|
||||||
|
enable_cpi_recording: bool,
|
||||||
) -> TransactionSimulationResult {
|
) -> TransactionSimulationResult {
|
||||||
assert!(self.is_frozen(), "simulation bank must be frozen");
|
assert!(self.is_frozen(), "simulation bank must be frozen");
|
||||||
|
|
||||||
self.simulate_transaction_unchecked(transaction)
|
self.simulate_transaction_unchecked(transaction, enable_cpi_recording)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Run transactions against a bank without committing the results; does not check if the bank
|
/// Run transactions against a bank without committing the results; does not check if the bank
|
||||||
/// is frozen, enabling use in single-Bank test frameworks
|
/// is frozen, enabling use in single-Bank test frameworks
|
||||||
pub fn simulate_transaction_unchecked(
|
pub fn simulate_transaction_unchecked(
|
||||||
&self,
|
&self,
|
||||||
transaction: SanitizedTransaction,
|
transaction: &SanitizedTransaction,
|
||||||
|
enable_cpi_recording: bool,
|
||||||
) -> TransactionSimulationResult {
|
) -> TransactionSimulationResult {
|
||||||
let account_keys = transaction.message().account_keys();
|
let account_keys = transaction.message().account_keys();
|
||||||
let number_of_accounts = account_keys.len();
|
let number_of_accounts = account_keys.len();
|
||||||
let account_overrides = self.get_account_overrides_for_simulation(&account_keys);
|
let account_overrides = self.get_account_overrides_for_simulation(&account_keys);
|
||||||
let batch = self.prepare_unlocked_batch_from_single_tx(&transaction);
|
let batch = self.prepare_unlocked_batch_from_single_tx(transaction);
|
||||||
let mut timings = ExecuteTimings::default();
|
let mut timings = ExecuteTimings::default();
|
||||||
|
|
||||||
let LoadAndExecuteTransactionsOutput {
|
let LoadAndExecuteTransactionsOutput {
|
||||||
|
@ -4337,7 +4341,7 @@ impl Bank {
|
||||||
// for processing. During forwarding, the transaction could expire if the
|
// for processing. During forwarding, the transaction could expire if the
|
||||||
// delay is not accounted for.
|
// delay is not accounted for.
|
||||||
MAX_PROCESSING_AGE - MAX_TRANSACTION_FORWARDING_DELAY,
|
MAX_PROCESSING_AGE - MAX_TRANSACTION_FORWARDING_DELAY,
|
||||||
false,
|
enable_cpi_recording,
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
&mut timings,
|
&mut timings,
|
||||||
|
@ -4374,11 +4378,13 @@ impl Bank {
|
||||||
|
|
||||||
let execution_result = execution_results.pop().unwrap();
|
let execution_result = execution_results.pop().unwrap();
|
||||||
let flattened_result = execution_result.flattened_result();
|
let flattened_result = execution_result.flattened_result();
|
||||||
let (logs, return_data) = match execution_result {
|
let (logs, return_data, inner_instructions) = match execution_result {
|
||||||
TransactionExecutionResult::Executed { details, .. } => {
|
TransactionExecutionResult::Executed { details, .. } => (
|
||||||
(details.log_messages, details.return_data)
|
details.log_messages,
|
||||||
}
|
details.return_data,
|
||||||
TransactionExecutionResult::NotExecuted(_) => (None, None),
|
details.inner_instructions,
|
||||||
|
),
|
||||||
|
TransactionExecutionResult::NotExecuted(_) => (None, None, None),
|
||||||
};
|
};
|
||||||
let logs = logs.unwrap_or_default();
|
let logs = logs.unwrap_or_default();
|
||||||
|
|
||||||
|
@ -4388,6 +4394,7 @@ impl Bank {
|
||||||
post_simulation_accounts,
|
post_simulation_accounts,
|
||||||
units_consumed,
|
units_consumed,
|
||||||
return_data,
|
return_data,
|
||||||
|
inner_instructions,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14136,6 +14136,6 @@ fn test_failed_simulation_compute_units() {
|
||||||
|
|
||||||
bank.freeze();
|
bank.freeze();
|
||||||
let sanitized = SanitizedTransaction::from_transaction_for_tests(transaction);
|
let sanitized = SanitizedTransaction::from_transaction_for_tests(transaction);
|
||||||
let simulation = bank.simulate_transaction(sanitized);
|
let simulation = bank.simulate_transaction(&sanitized, false);
|
||||||
assert_eq!(TEST_UNITS, simulation.units_consumed);
|
assert_eq!(TEST_UNITS, simulation.units_consumed);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
use {
|
||||||
|
crate::instruction::CompiledInstruction,
|
||||||
|
serde::{Deserialize, Serialize},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct InnerInstruction {
|
||||||
|
pub instruction: CompiledInstruction,
|
||||||
|
/// Invocation stack height of this instruction. Instruction stack height
|
||||||
|
/// starts at 1 for transaction instructions.
|
||||||
|
pub stack_height: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An ordered list of compiled instructions that were invoked during a
|
||||||
|
/// transaction instruction
|
||||||
|
pub type InnerInstructions = Vec<InnerInstruction>;
|
||||||
|
|
||||||
|
/// A list of compiled instructions that were invoked during each instruction of
|
||||||
|
/// a transaction
|
||||||
|
pub type InnerInstructionsList = Vec<InnerInstructions>;
|
|
@ -78,6 +78,7 @@ pub mod genesis_config;
|
||||||
pub mod hard_forks;
|
pub mod hard_forks;
|
||||||
pub mod hash;
|
pub mod hash;
|
||||||
pub mod inflation;
|
pub mod inflation;
|
||||||
|
pub mod inner_instruction;
|
||||||
pub mod log;
|
pub mod log;
|
||||||
pub mod native_loader;
|
pub mod native_loader;
|
||||||
pub mod net;
|
pub mod net;
|
||||||
|
|
|
@ -230,6 +230,27 @@ pub struct InnerInstruction {
|
||||||
pub stack_height: Option<u32>,
|
pub stack_height: Option<u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Maps a list of inner instructions from `solana_sdk` into a list of this
|
||||||
|
/// crate's representation of inner instructions (with instruction indices).
|
||||||
|
pub fn map_inner_instructions(
|
||||||
|
inner_instructions: solana_sdk::inner_instruction::InnerInstructionsList,
|
||||||
|
) -> impl Iterator<Item = InnerInstructions> {
|
||||||
|
inner_instructions
|
||||||
|
.into_iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(index, instructions)| InnerInstructions {
|
||||||
|
index: index as u8,
|
||||||
|
instructions: instructions
|
||||||
|
.into_iter()
|
||||||
|
.map(|info| InnerInstruction {
|
||||||
|
stack_height: Some(u32::from(info.stack_height)),
|
||||||
|
instruction: info.instruction,
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
})
|
||||||
|
.filter(|i| !i.instructions.is_empty())
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct UiInnerInstructions {
|
pub struct UiInnerInstructions {
|
||||||
|
@ -240,7 +261,7 @@ pub struct UiInnerInstructions {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UiInnerInstructions {
|
impl UiInnerInstructions {
|
||||||
fn parse(inner_instructions: InnerInstructions, account_keys: &AccountKeys) -> Self {
|
pub fn parse(inner_instructions: InnerInstructions, account_keys: &AccountKeys) -> Self {
|
||||||
Self {
|
Self {
|
||||||
index: inner_instructions.index,
|
index: inner_instructions.index,
|
||||||
instructions: inner_instructions
|
instructions: inner_instructions
|
||||||
|
|
Loading…
Reference in New Issue