Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RPC: Add inner instructions to simulate transaction response #34313

Merged
merged 10 commits into from
Dec 16, 2023
22 changes: 6 additions & 16 deletions accounts-db/src/transaction_results.rs
Original file line number Diff line number Diff line change
@@ -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};
buffalojoec marked this conversation as resolved.
Show resolved Hide resolved
use {
crate::{
nonce_info::{NonceFull, NonceInfo, NoncePartial},
Expand Down Expand Up @@ -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
pub fn inner_instructions_list_from_instruction_trace(
transaction_context: &TransactionContext,
Expand Down
2 changes: 2 additions & 0 deletions banks-interface/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use {
commitment_config::CommitmentLevel,
fee_calculator::FeeCalculator,
hash::Hash,
inner_instruction::InnerInstructions,
message::Message,
pubkey::Pubkey,
signature::Signature,
Expand Down Expand Up @@ -37,6 +38,7 @@ pub struct TransactionSimulationDetails {
pub logs: Vec<String>,
pub units_consumed: u64,
pub return_data: Option<TransactionReturnData>,
pub inner_instructions: Option<Vec<InnerInstructions>>,
}

#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
Expand Down
5 changes: 4 additions & 1 deletion banks-server/src/banks_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -194,11 +194,14 @@ fn simulate_transaction(
post_simulation_accounts: _,
units_consumed,
return_data,
} = bank.simulate_transaction_unchecked(sanitized_transaction);
inner_instructions,
} = bank.simulate_transaction_unchecked(&sanitized_transaction, false);

let simulation_details = TransactionSimulationDetails {
logs,
units_consumed,
return_data,
inner_instructions,
};
BanksTransactionResultWithSimulation {
result: Some(result),
Expand Down
20 changes: 3 additions & 17 deletions programs/sbf/tests/programs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ use {
transaction::VersionedTransaction,
},
solana_transaction_status::{
ConfirmedTransactionWithStatusMeta, InnerInstructions, TransactionStatusMeta,
map_inner_instructions, ConfirmedTransactionWithStatusMeta, TransactionStatusMeta,
TransactionWithStatusMeta, VersionedTransactionWithStatusMeta,
},
std::collections::HashMap,
Expand Down Expand Up @@ -212,21 +212,7 @@ fn execute_transactions(
);

let inner_instructions = inner_instructions.map(|inner_instructions| {
inner_instructions
.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()
map_inner_instructions(inner_instructions).collect()
});

let tx_status_meta = TransactionStatusMeta {
Expand Down Expand Up @@ -766,7 +752,7 @@ fn test_return_data_and_log_data_syscall() {
let transaction = Transaction::new(&[&mint_keypair], message, blockhash);
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());

Expand Down
2 changes: 2 additions & 0 deletions rpc-client-api/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ pub struct RpcSimulateTransactionConfig {
pub encoding: Option<UiTransactionEncoding>,
pub accounts: Option<RpcSimulateTransactionAccountsConfig>,
pub min_context_slot: Option<Slot>,
#[serde(default)]
pub inner_instructions: bool,
}

#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
Expand Down
3 changes: 2 additions & 1 deletion rpc-client-api/src/response.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use {
},
solana_transaction_status::{
ConfirmedTransactionStatusWithSignature, TransactionConfirmationStatus, UiConfirmedBlock,
UiTransactionReturnData,
UiInnerInstructions, UiTransactionReturnData,
},
std::{collections::HashMap, fmt, net::SocketAddr, str::FromStr},
thiserror::Error,
Expand Down Expand Up @@ -423,6 +423,7 @@ pub struct RpcSimulateTransactionResult {
pub accounts: Option<Vec<Option<UiAccount>>>,
pub units_consumed: Option<u64>,
pub return_data: Option<UiTransactionReturnData>,
pub inner_instructions: Option<Vec<UiInnerInstructions>>,
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
Expand Down
1 change: 1 addition & 0 deletions rpc-client/src/mock_sender.rs
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,7 @@ impl RpcSender for MockSender {
accounts: None,
units_consumed: None,
return_data: None,
inner_instructions: None,
},
})?,
"getMinimumBalanceForRentExemption" => json![20],
Expand Down
35 changes: 27 additions & 8 deletions rpc/src/rpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,10 +87,10 @@ use {
solana_storage_bigtable::Error as StorageError,
solana_streamer::socket::SocketAddrSpace,
solana_transaction_status::{
BlockEncodingOptions, ConfirmedBlock, ConfirmedTransactionStatusWithSignature,
ConfirmedTransactionWithStatusMeta, EncodedConfirmedTransactionWithStatusMeta, Reward,
RewardType, TransactionBinaryEncoding, TransactionConfirmationStatus, TransactionStatus,
UiConfirmedBlock, UiTransactionEncoding,
map_inner_instructions, BlockEncodingOptions, ConfirmedBlock,
ConfirmedTransactionStatusWithSignature, ConfirmedTransactionWithStatusMeta,
EncodedConfirmedTransactionWithStatusMeta, Reward, RewardType, TransactionBinaryEncoding,
TransactionConfirmationStatus, TransactionStatus, UiConfirmedBlock, UiTransactionEncoding,
},
solana_vote_program::vote_state::{VoteState, MAX_LOCKOUT_HISTORY},
spl_token_2022::{
Expand Down Expand Up @@ -3266,6 +3266,7 @@ pub mod rpc_full {
use {
super::*,
solana_sdk::message::{SanitizedVersionedMessage, VersionedMessage},
solana_transaction_status::UiInnerInstructions,
};
#[rpc]
pub trait Full {
Expand Down Expand Up @@ -3676,7 +3677,8 @@ pub mod rpc_full {
post_simulation_accounts: _,
units_consumed,
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 {
TransactionError::BlockhashNotFound => {
Expand All @@ -3694,6 +3696,7 @@ pub mod rpc_full {
accounts: None,
units_consumed: Some(units_consumed),
return_data: return_data.map(|return_data| return_data.into()),
inner_instructions: None,
},
}
.into());
Expand Down Expand Up @@ -3724,6 +3727,7 @@ pub mod rpc_full {
encoding,
accounts: config_accounts,
min_context_slot,
inner_instructions: enable_cpi_recording,
} = config.unwrap_or_default();
let tx_encoding = encoding.unwrap_or(UiTransactionEncoding::Base58);
let binary_encoding = tx_encoding.into_binary_encoding().ok_or_else(|| {
Expand Down Expand Up @@ -3753,15 +3757,18 @@ pub mod rpc_full {
if sig_verify {
verify_transaction(&transaction, &bank.feature_set)?;
}
let number_of_accounts = transaction.message().account_keys().len();

let TransactionSimulationResult {
result,
logs,
post_simulation_accounts,
units_consumed,
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_encoding = config_accounts
Expand Down Expand Up @@ -3804,6 +3811,12 @@ pub mod rpc_full {
None
};

let inner_instructions = inner_instructions.map(|info| {
map_inner_instructions(info)
.map(|converted| UiInnerInstructions::parse(converted, &account_keys))
.collect()
});

Ok(new_response(
bank,
RpcSimulateTransactionResult {
Expand All @@ -3812,6 +3825,7 @@ pub mod rpc_full {
accounts,
units_consumed: Some(units_consumed),
return_data: return_data.map(|return_data| return_data.into()),
inner_instructions,
},
))
}
Expand Down Expand Up @@ -5913,6 +5927,7 @@ pub mod tests {
}
],
"err":null,
"innerInstructions": null,
"logs":[
"Program 11111111111111111111111111111111 invoke [1]",
"Program 11111111111111111111111111111111 success"
Expand Down Expand Up @@ -5997,6 +6012,7 @@ pub mod tests {
"value":{
"accounts":null,
"err":null,
"innerInstructions":null,
"logs":[
"Program 11111111111111111111111111111111 invoke [1]",
"Program 11111111111111111111111111111111 success"
Expand Down Expand Up @@ -6025,6 +6041,7 @@ pub mod tests {
"value":{
"accounts":null,
"err":null,
"innerInstructions":null,
"logs":[
"Program 11111111111111111111111111111111 invoke [1]",
"Program 11111111111111111111111111111111 success"
Expand Down Expand Up @@ -6077,6 +6094,7 @@ pub mod tests {
"value":{
"err":"BlockhashNotFound",
"accounts":null,
"innerInstructions":null,
"logs":[],
"returnData":null,
"unitsConsumed":0,
Expand All @@ -6103,6 +6121,7 @@ pub mod tests {
"value":{
"accounts":null,
"err":null,
"innerInstructions":null,
"logs":[
"Program 11111111111111111111111111111111 invoke [1]",
"Program 11111111111111111111111111111111 success"
Expand Down Expand Up @@ -6483,7 +6502,7 @@ pub mod tests {
assert_eq!(
res,
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(),
)
);

Expand Down
18 changes: 2 additions & 16 deletions rpc/src/transaction_status_service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use {
blockstore_processor::{TransactionStatusBatch, TransactionStatusMessage},
},
solana_transaction_status::{
extract_and_fmt_memos, InnerInstruction, InnerInstructions, Reward, TransactionStatusMeta,
extract_and_fmt_memos, map_inner_instructions, Reward, TransactionStatusMeta,
},
std::{
sync::{
Expand Down Expand Up @@ -121,21 +121,7 @@ impl TransactionStatusService {
let tx_account_locks = transaction.get_account_locks_unchecked();

let inner_instructions = inner_instructions.map(|inner_instructions| {
inner_instructions
.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()
map_inner_instructions(inner_instructions).collect()
});

let pre_token_balances = Some(pre_token_balances);
Expand Down
27 changes: 17 additions & 10 deletions runtime/src/bank.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ use {
hash::{extend_and_hash, hashv, Hash},
incinerator,
inflation::Inflation,
inner_instruction::InnerInstructions,
instruction::InstructionError,
loader_v4::{self, LoaderV4State, LoaderV4Status},
message::{AccountKeys, SanitizedMessage},
Expand Down Expand Up @@ -338,6 +339,7 @@ pub struct TransactionSimulationResult {
pub post_simulation_accounts: Vec<TransactionAccount>,
pub units_consumed: u64,
pub return_data: Option<TransactionReturnData>,
pub inner_instructions: Option<Vec<InnerInstructions>>,
}
pub struct TransactionBalancesSet {
pub pre_balances: TransactionBalances,
Expand Down Expand Up @@ -4308,23 +4310,25 @@ impl Bank {
/// Run transactions against a frozen bank without committing the results
pub fn simulate_transaction(
&self,
transaction: SanitizedTransaction,
transaction: &SanitizedTransaction,
enable_cpi_recording: bool,
) -> TransactionSimulationResult {
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
/// is frozen, enabling use in single-Bank test frameworks
pub fn simulate_transaction_unchecked(
&self,
transaction: SanitizedTransaction,
transaction: &SanitizedTransaction,
enable_cpi_recording: bool,
) -> TransactionSimulationResult {
let account_keys = transaction.message().account_keys();
let number_of_accounts = account_keys.len();
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 LoadAndExecuteTransactionsOutput {
Expand All @@ -4337,7 +4341,7 @@ impl Bank {
// for processing. During forwarding, the transaction could expire if the
// delay is not accounted for.
MAX_PROCESSING_AGE - MAX_TRANSACTION_FORWARDING_DELAY,
false,
enable_cpi_recording,
true,
true,
&mut timings,
Expand Down Expand Up @@ -4374,11 +4378,13 @@ impl Bank {

let execution_result = execution_results.pop().unwrap();
let flattened_result = execution_result.flattened_result();
let (logs, return_data) = match execution_result {
TransactionExecutionResult::Executed { details, .. } => {
(details.log_messages, details.return_data)
}
TransactionExecutionResult::NotExecuted(_) => (None, None),
let (logs, return_data, inner_instructions) = match execution_result {
TransactionExecutionResult::Executed { details, .. } => (
details.log_messages,
details.return_data,
details.inner_instructions,
),
TransactionExecutionResult::NotExecuted(_) => (None, None, None),
};
let logs = logs.unwrap_or_default();

Expand All @@ -4388,6 +4394,7 @@ impl Bank {
post_simulation_accounts,
units_consumed,
return_data,
inner_instructions,
}
}

Expand Down
Loading