Record and store invoked instructions in transaction meta (#12311)
* Record invoked instructions and store in transaction meta * Enable cpi recording if transaction sender is some * Rename invoked to innerInstructions
This commit is contained in:
@@ -227,6 +227,7 @@ impl InvokeContext for MockInvokeContext {
|
||||
fn get_executor(&mut self, _pubkey: &Pubkey) -> Option<Arc<dyn Executor>> {
|
||||
None
|
||||
}
|
||||
fn record_instruction(&self, _instruction: &Instruction) {}
|
||||
}
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct MockLogger {
|
||||
|
@@ -251,9 +251,9 @@ extern uint64_t entrypoint(const uint8_t *input) {
|
||||
sol_assert(SUCCESS ==
|
||||
sol_invoke(&instruction, accounts, SOL_ARRAY_SIZE(accounts)));
|
||||
|
||||
// Signer privilege escalation will always fail the whole transaction
|
||||
instruction.accounts[0].is_signer = true;
|
||||
sol_assert(SUCCESS !=
|
||||
sol_invoke(&instruction, accounts, SOL_ARRAY_SIZE(accounts)));
|
||||
sol_invoke(&instruction, accounts, SOL_ARRAY_SIZE(accounts));
|
||||
break;
|
||||
}
|
||||
case TEST_PRIVILEGE_ESCALATION_WRITABLE: {
|
||||
@@ -267,9 +267,9 @@ extern uint64_t entrypoint(const uint8_t *input) {
|
||||
sol_assert(SUCCESS ==
|
||||
sol_invoke(&instruction, accounts, SOL_ARRAY_SIZE(accounts)));
|
||||
|
||||
// Writable privilege escalation will always fail the whole transaction
|
||||
instruction.accounts[0].is_writable = true;
|
||||
sol_assert(SUCCESS !=
|
||||
sol_invoke(&instruction, accounts, SOL_ARRAY_SIZE(accounts)));
|
||||
sol_invoke(&instruction, accounts, SOL_ARRAY_SIZE(accounts));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
@@ -239,6 +239,7 @@ fn process_instruction(
|
||||
);
|
||||
invoke(&invoked_instruction, accounts)?;
|
||||
|
||||
// Signer privilege escalation will always fail the whole transaction
|
||||
invoked_instruction.accounts[0].is_signer = true;
|
||||
assert_eq!(
|
||||
invoke(&invoked_instruction, accounts),
|
||||
@@ -254,6 +255,7 @@ fn process_instruction(
|
||||
);
|
||||
invoke(&invoked_instruction, accounts)?;
|
||||
|
||||
// Writable privilege escalation will always fail the whole transaction
|
||||
invoked_instruction.accounts[0].is_writable = true;
|
||||
assert_eq!(
|
||||
invoke(&invoked_instruction, accounts),
|
||||
|
@@ -18,7 +18,7 @@ use solana_sdk::{
|
||||
account::{Account, KeyedAccount},
|
||||
bpf_loader, bpf_loader_deprecated,
|
||||
client::SyncClient,
|
||||
clock::DEFAULT_SLOTS_PER_EPOCH,
|
||||
clock::{DEFAULT_SLOTS_PER_EPOCH, MAX_PROCESSING_AGE},
|
||||
entrypoint::{MAX_PERMITTED_DATA_INCREASE, SUCCESS},
|
||||
entrypoint_native::{
|
||||
ComputeBudget, ComputeMeter, Executor, InvokeContext, Logger, ProcessInstruction,
|
||||
@@ -28,7 +28,7 @@ use solana_sdk::{
|
||||
pubkey::Pubkey,
|
||||
signature::{Keypair, Signer},
|
||||
sysvar::{clock, fees, rent, rewards, slot_hashes, stake_history},
|
||||
transaction::TransactionError,
|
||||
transaction::{Transaction, TransactionError},
|
||||
};
|
||||
use std::{cell::RefCell, env, fs::File, io::Read, path::PathBuf, rc::Rc, sync::Arc};
|
||||
|
||||
@@ -98,6 +98,26 @@ fn run_program(
|
||||
Ok(vm.get_total_instruction_count())
|
||||
}
|
||||
|
||||
fn process_transaction_and_record_inner(
|
||||
bank: &Bank,
|
||||
tx: Transaction,
|
||||
) -> (Result<(), TransactionError>, Vec<Vec<CompiledInstruction>>) {
|
||||
let signature = tx.signatures.get(0).unwrap().clone();
|
||||
let txs = vec![tx];
|
||||
let tx_batch = bank.prepare_batch(&txs, None);
|
||||
let (mut results, _, mut inner) =
|
||||
bank.load_execute_and_commit_transactions(&tx_batch, MAX_PROCESSING_AGE, false, true);
|
||||
let inner_instructions = inner.swap_remove(0);
|
||||
let result = results
|
||||
.fee_collection_results
|
||||
.swap_remove(0)
|
||||
.and_then(|_| bank.get_signature_status(&signature).unwrap());
|
||||
(
|
||||
result,
|
||||
inner_instructions.expect("cpi recording should be enabled"),
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "bpf_c", feature = "bpf_rust"))]
|
||||
fn test_program_bpf_sanity() {
|
||||
@@ -482,61 +502,91 @@ fn test_program_bpf_invoke() {
|
||||
account_metas.clone(),
|
||||
);
|
||||
let message = Message::new(&[instruction], Some(&mint_pubkey));
|
||||
assert!(bank_client
|
||||
.send_and_confirm_message(
|
||||
&[
|
||||
&mint_keypair,
|
||||
&argument_keypair,
|
||||
&invoked_argument_keypair,
|
||||
&from_keypair
|
||||
],
|
||||
message,
|
||||
)
|
||||
.is_ok());
|
||||
let tx = Transaction::new(
|
||||
&[
|
||||
&mint_keypair,
|
||||
&argument_keypair,
|
||||
&invoked_argument_keypair,
|
||||
&from_keypair,
|
||||
],
|
||||
message.clone(),
|
||||
bank.last_blockhash(),
|
||||
);
|
||||
let (result, inner_instructions) = process_transaction_and_record_inner(&bank, tx);
|
||||
assert!(result.is_ok());
|
||||
let invoked_programs: Vec<Pubkey> = inner_instructions[0]
|
||||
.iter()
|
||||
.map(|ix| message.account_keys[ix.program_id_index as usize].clone())
|
||||
.collect();
|
||||
assert_eq!(
|
||||
invoked_programs,
|
||||
vec![
|
||||
solana_sdk::system_program::id(),
|
||||
solana_sdk::system_program::id(),
|
||||
invoked_program_id.clone(),
|
||||
invoked_program_id.clone(),
|
||||
invoked_program_id.clone(),
|
||||
invoked_program_id.clone(),
|
||||
invoked_program_id.clone(),
|
||||
invoked_program_id.clone(),
|
||||
invoked_program_id.clone(),
|
||||
]
|
||||
);
|
||||
|
||||
// failure cases
|
||||
|
||||
let instruction = Instruction::new(
|
||||
invoke_program_id,
|
||||
&TEST_PRIVILEGE_ESCALATION_SIGNER,
|
||||
&[TEST_PRIVILEGE_ESCALATION_SIGNER, nonce1, nonce2, nonce3],
|
||||
account_metas.clone(),
|
||||
);
|
||||
let message = Message::new(&[instruction], Some(&mint_pubkey));
|
||||
let tx = Transaction::new(
|
||||
&[
|
||||
&mint_keypair,
|
||||
&argument_keypair,
|
||||
&invoked_argument_keypair,
|
||||
&from_keypair,
|
||||
],
|
||||
message.clone(),
|
||||
bank.last_blockhash(),
|
||||
);
|
||||
|
||||
let (result, inner_instructions) = process_transaction_and_record_inner(&bank, tx);
|
||||
let invoked_programs: Vec<Pubkey> = inner_instructions[0]
|
||||
.iter()
|
||||
.map(|ix| message.account_keys[ix.program_id_index as usize].clone())
|
||||
.collect();
|
||||
assert_eq!(invoked_programs, vec![invoked_program_id.clone()]);
|
||||
assert_eq!(
|
||||
bank_client
|
||||
.send_and_confirm_message(
|
||||
&[
|
||||
&mint_keypair,
|
||||
&argument_keypair,
|
||||
&invoked_argument_keypair,
|
||||
&from_keypair
|
||||
],
|
||||
message,
|
||||
)
|
||||
.unwrap_err()
|
||||
.unwrap(),
|
||||
result.unwrap_err(),
|
||||
TransactionError::InstructionError(0, InstructionError::Custom(194969602))
|
||||
);
|
||||
|
||||
let instruction = Instruction::new(
|
||||
invoke_program_id,
|
||||
&TEST_PRIVILEGE_ESCALATION_WRITABLE,
|
||||
&[TEST_PRIVILEGE_ESCALATION_WRITABLE, nonce1, nonce2, nonce3],
|
||||
account_metas.clone(),
|
||||
);
|
||||
let message = Message::new(&[instruction], Some(&mint_pubkey));
|
||||
let tx = Transaction::new(
|
||||
&[
|
||||
&mint_keypair,
|
||||
&argument_keypair,
|
||||
&invoked_argument_keypair,
|
||||
&from_keypair,
|
||||
],
|
||||
message.clone(),
|
||||
bank.last_blockhash(),
|
||||
);
|
||||
let (result, inner_instructions) = process_transaction_and_record_inner(&bank, tx);
|
||||
let invoked_programs: Vec<Pubkey> = inner_instructions[0]
|
||||
.iter()
|
||||
.map(|ix| message.account_keys[ix.program_id_index as usize].clone())
|
||||
.collect();
|
||||
assert_eq!(invoked_programs, vec![invoked_program_id.clone()]);
|
||||
assert_eq!(
|
||||
bank_client
|
||||
.send_and_confirm_message(
|
||||
&[
|
||||
&mint_keypair,
|
||||
&argument_keypair,
|
||||
&invoked_argument_keypair,
|
||||
&from_keypair
|
||||
],
|
||||
message,
|
||||
)
|
||||
.unwrap_err()
|
||||
.unwrap(),
|
||||
result.unwrap_err(),
|
||||
TransactionError::InstructionError(0, InstructionError::Custom(194969602))
|
||||
);
|
||||
|
||||
@@ -639,6 +689,7 @@ impl InvokeContext for MockInvokeContext {
|
||||
fn get_executor(&mut self, _pubkey: &Pubkey) -> Option<Arc<dyn Executor>> {
|
||||
None
|
||||
}
|
||||
fn record_instruction(&self, _instruction: &Instruction) {}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
|
@@ -275,6 +275,7 @@ mod tests {
|
||||
account::Account,
|
||||
entrypoint_native::{ComputeBudget, Logger, ProcessInstruction},
|
||||
instruction::CompiledInstruction,
|
||||
instruction::Instruction,
|
||||
message::Message,
|
||||
rent::Rent,
|
||||
};
|
||||
@@ -360,6 +361,7 @@ mod tests {
|
||||
fn get_executor(&mut self, _pubkey: &Pubkey) -> Option<Arc<dyn Executor>> {
|
||||
None
|
||||
}
|
||||
fn record_instruction(&self, _instruction: &Instruction) {}
|
||||
}
|
||||
|
||||
struct TestInstructionMeter {
|
||||
@@ -587,6 +589,7 @@ mod tests {
|
||||
max_invoke_depth: 2,
|
||||
},
|
||||
Rc::new(RefCell::new(Executors::default())),
|
||||
None,
|
||||
);
|
||||
assert_eq!(
|
||||
Err(InstructionError::Custom(194969602)),
|
||||
|
@@ -1020,6 +1020,7 @@ fn call<'a>(
|
||||
ro_regions,
|
||||
)?;
|
||||
verify_instruction(syscall, &instruction, &signers)?;
|
||||
invoke_context.record_instruction(&instruction);
|
||||
let message = Message::new(&[instruction], None);
|
||||
let callee_program_id_index = message.instructions[0].program_id_index as usize;
|
||||
let callee_program_id = message.account_keys[callee_program_id_index];
|
||||
|
Reference in New Issue
Block a user