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

Add get_processed_inner_instruction syscall #22515

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions program-runtime/src/invoke_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ pub struct InvokeContext<'a> {
pub timings: ExecuteDetailsTimings,
pub blockhash: Hash,
pub lamports_per_signature: u64,
pub processed_inner_instructions: Vec<(usize, Instruction)>,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should go in TransactionContext, because it is not runtime internal and should be exposed to ABIv2 programs as well. I am preparing a PR that moves the InstructionRecorder there, on which you could rebase this PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm going to merge this today, feel free to move it after

}

impl<'a> InvokeContext<'a> {
Expand Down Expand Up @@ -229,6 +230,7 @@ impl<'a> InvokeContext<'a> {
timings: ExecuteDetailsTimings::default(),
blockhash,
lamports_per_signature,
processed_inner_instructions: Vec::new(),
}
}

Expand Down Expand Up @@ -805,6 +807,7 @@ impl<'a> InvokeContext<'a> {
.transaction_context
.get_instruction_context_stack_height()
== 0;

if !is_lowest_invocation_level {
// Verify the calling program hasn't misbehaved
let mut verify_caller_time = Measure::start("verify_caller_time");
Expand Down Expand Up @@ -833,6 +836,8 @@ impl<'a> InvokeContext<'a> {
};
self.transaction_context
.record_compiled_instruction(compiled_instruction);
} else {
self.processed_inner_instructions.clear();
}

let result = self
Expand Down Expand Up @@ -998,6 +1003,24 @@ impl<'a> InvokeContext<'a> {
pub fn get_sysvar_cache(&self) -> &SysvarCache {
&self.sysvar_cache
}

// Push a processed inner instruction
pub fn add_processed_inner_instruction(&mut self, instruction: Instruction) {
self.processed_inner_instructions
.push((self.invoke_depth(), instruction));
}

/// Get a processed inner instruction, reverse ordered list, where last added is index 0
pub fn get_processed_inner_instruction(&self, index: usize) -> Option<(usize, &Instruction)> {
let index = self
.processed_inner_instructions
.len()
.checked_sub(1)?
.checked_sub(index)?;
self.processed_inner_instructions
.get(index)
.map(|(usize, instruction)| (*usize, instruction))
}
}

pub struct MockInvokeContextPreparation {
Expand Down
7 changes: 7 additions & 0 deletions programs/bpf/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions programs/bpf/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ members = [
"rust/log_data",
"rust/external_spend",
"rust/finalize",
"rust/inner_instruction",
"rust/instruction_introspection",
"rust/invoke",
"rust/invoke_and_error",
Expand Down
1 change: 1 addition & 0 deletions programs/bpf/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ fn main() {
"log_data",
"external_spend",
"finalize",
"inner_instruction",
"instruction_introspection",
"invoke",
"invoke_and_error",
Expand Down
23 changes: 23 additions & 0 deletions programs/bpf/rust/inner_instruction/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
[package]
name = "solana-bpf-rust-inner-instructions"
version = "1.10.0"
description = "Solana BPF test program written in Rust"
authors = ["Solana Maintainers <[email protected]>"]
repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0"
homepage = "https://solana.com/"
documentation = "https://docs.rs/solana-bpf-rust-log-data"
edition = "2021"

[dependencies]
solana-program = { path = "../../../../sdk/program", version = "=1.10.0" }

[features]
default = ["program"]
program = []

[lib]
crate-type = ["lib", "cdylib"]

[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]
62 changes: 62 additions & 0 deletions programs/bpf/rust/inner_instruction/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
//! Example Rust-based BPF program that uses sol_get_processed_inner_instruction syscall

#![cfg(feature = "program")]

use solana_program::{
jackcmay marked this conversation as resolved.
Show resolved Hide resolved
account_info::AccountInfo,
entrypoint,
entrypoint::ProgramResult,
instruction::{get_invoke_depth, get_processed_inner_instruction, AccountMeta, Instruction},
msg,
program::invoke,
pubkey::Pubkey,
};

entrypoint!(process_instruction);
fn process_instruction(
_program_id: &Pubkey,
accounts: &[AccountInfo],
instruction_data: &[u8],
) -> ProgramResult {
msg!("inner");

// account 0 is mint
// account 1 is noop
// account 2 is invoke_and_return

let instruction3 = Instruction::new_with_bytes(*accounts[1].key, instruction_data, vec![]);
let instruction2 = Instruction::new_with_bytes(
*accounts[2].key,
instruction_data,
vec![AccountMeta::new_readonly(*accounts[1].key, false)],
);
let instruction1 = Instruction::new_with_bytes(
*accounts[1].key,
instruction_data,
vec![
AccountMeta::new_readonly(*accounts[0].key, true),
AccountMeta::new_readonly(*accounts[1].key, false),
],
);
let instruction0 = Instruction::new_with_bytes(
*accounts[1].key,
instruction_data,
vec![
AccountMeta::new_readonly(*accounts[1].key, false),
AccountMeta::new_readonly(*accounts[0].key, true),
],
);

invoke(&instruction2, accounts)?;
invoke(&instruction1, accounts)?;
invoke(&instruction0, accounts)?;

assert_eq!(1, get_invoke_depth());
assert_eq!(Some((1, instruction0)), get_processed_inner_instruction(0));
assert_eq!(Some((1, instruction1)), get_processed_inner_instruction(1));
assert_eq!(Some((1, instruction2)), get_processed_inner_instruction(2));
assert_eq!(Some((2, instruction3)), get_processed_inner_instruction(3));
assert!(get_processed_inner_instruction(4).is_none());

Ok(())
}
50 changes: 50 additions & 0 deletions programs/bpf/tests/programs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3331,3 +3331,53 @@ fn test_program_bpf_realloc_invoke() {
TransactionError::InstructionError(0, InstructionError::InvalidRealloc)
);
}

#[test]
#[cfg(any(feature = "bpf_rust"))]
fn test_program_bpf_processed_inner_instruction() {
solana_logger::setup();

let GenesisConfigInfo {
genesis_config,
mint_keypair,
..
} = create_genesis_config(50);
let mut bank = Bank::new_for_tests(&genesis_config);
let (name, id, entrypoint) = solana_bpf_loader_program!();
bank.add_builtin(&name, &id, entrypoint);
let bank = Arc::new(bank);
let bank_client = BankClient::new_shared(&bank);

let program_id = load_bpf_program(
&bank_client,
&bpf_loader::id(),
&mint_keypair,
"solana_bpf_rust_inner_instructions",
);
let noop_program_id = load_bpf_program(
&bank_client,
&bpf_loader::id(),
&mint_keypair,
"solana_bpf_rust_noop",
);
let invoke_and_return_program_id = load_bpf_program(
&bank_client,
&bpf_loader::id(),
&mint_keypair,
"solana_bpf_rust_invoke_and_return",
);

let instruction = Instruction::new_with_bytes(
program_id,
&[1, 2, 3, 0, 4, 5, 6],
vec![
AccountMeta::new(mint_keypair.pubkey(), true),
AccountMeta::new_readonly(noop_program_id, false),
AccountMeta::new_readonly(invoke_and_return_program_id, false),
],
);
let message = Message::new(&[instruction], Some(&mint_keypair.pubkey()));
assert!(bank_client
.send_and_confirm_message(&[&mint_keypair], message)
.is_ok());
}
Loading