Skip to content
This repository was archived by the owner on Jan 22, 2025. It is now read-only.
Merged
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
59 changes: 36 additions & 23 deletions program-runtime/src/invoke_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,15 @@ use {
tx_wide_compute_cap, FeatureSet,
},
hash::Hash,
instruction::{AccountMeta, CompiledInstruction, Instruction, InstructionError},
instruction::{AccountMeta, Instruction, InstructionError},
keyed_account::{create_keyed_accounts_unified, KeyedAccount},
native_loader,
pubkey::Pubkey,
rent::Rent,
saturating_add_assign,
transaction_context::{InstructionAccount, TransactionAccount, TransactionContext},
transaction_context::{
InstructionAccount, InstructionContext, TransactionAccount, TransactionContext,
},
},
std::{borrow::Cow, cell::RefCell, collections::HashMap, fmt::Debug, rc::Rc, sync::Arc},
};
Expand Down Expand Up @@ -408,8 +410,9 @@ impl<'a> InvokeContext<'a> {
self.transaction_context.pop()
}

/// Current depth of the invocation stack
pub fn get_invoke_depth(&self) -> usize {
/// Current height of the invocation stack, top level instructions are height
/// `solana_sdk::instruction::TRANSACTION_LEVEL_STACK_HEIGHT`
pub fn get_stack_height(&self) -> usize {
self.transaction_context
.get_instruction_context_stack_height()
}
Expand Down Expand Up @@ -798,11 +801,13 @@ impl<'a> InvokeContext<'a> {
.map(|index| *self.transaction_context.get_key_of_account_at_index(*index))
.unwrap_or_else(native_loader::id);

let is_lowest_invocation_level = self
let stack_height = self
.transaction_context
.get_instruction_context_stack_height()
== 0;
if !is_lowest_invocation_level {
.get_instruction_context_stack_height();

let is_top_level_instruction = stack_height == 0;

if !is_top_level_instruction {
// Verify the calling program hasn't misbehaved
let mut verify_caller_time = Measure::start("verify_caller_time");
let verify_caller_result = self.verify_and_update(instruction_accounts, true);
Expand All @@ -816,20 +821,10 @@ impl<'a> InvokeContext<'a> {
);
verify_caller_result?;

// Record instruction
let compiled_instruction = CompiledInstruction {
program_id_index: self
.transaction_context
.find_index_of_account(&program_id)
.unwrap_or(0) as u8,
data: instruction_data.to_vec(),
accounts: instruction_accounts
.iter()
.map(|instruction_account| instruction_account.index_in_transaction as u8)
.collect(),
};
self.transaction_context
.record_compiled_instruction(compiled_instruction);
self.transaction_context.record_instruction(
stack_height.saturating_add(1),
InstructionContext::new(program_indices, instruction_accounts, instruction_data),
);
}

let result = self
Expand All @@ -848,7 +843,7 @@ impl<'a> InvokeContext<'a> {
// Verify the called program has not misbehaved
let mut verify_callee_time = Measure::start("verify_callee_time");
let result = execution_result.and_then(|_| {
if is_lowest_invocation_level {
if is_top_level_instruction {
self.verify(instruction_accounts, program_indices)
} else {
self.verify_and_update(instruction_accounts, false)
Expand Down Expand Up @@ -995,6 +990,24 @@ impl<'a> InvokeContext<'a> {
pub fn get_sysvar_cache(&self) -> &SysvarCache {
&self.sysvar_cache
}

/// Get instruction trace
pub fn get_instruction_trace(&self) -> &[Vec<(usize, InstructionContext)>] {
self.transaction_context.get_instruction_trace()
}

// Get pubkey of account at index
pub fn get_key_of_account_at_index(&self, index_in_transaction: usize) -> &Pubkey {
self.transaction_context
.get_key_of_account_at_index(index_in_transaction)
}

/// Get an instruction context
pub fn get_instruction_context_at(&self, level: usize) -> Option<&InstructionContext> {
self.transaction_context
.get_instruction_context_at(level)
.ok()
}
}

pub struct MockInvokeContextPreparation {
Expand Down
4 changes: 2 additions & 2 deletions program-test/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ pub fn builtin_process_instruction(
stable_log::program_invoke(
&log_collector,
program_id,
invoke_context.get_invoke_depth(),
invoke_context.get_stack_height(),
);

// Copy indices_in_instruction into a HashSet to ensure there are no duplicates
Expand Down Expand Up @@ -255,7 +255,7 @@ impl solana_sdk::program_stubs::SyscallStubs for SyscallStubs {
stable_log::program_invoke(
&log_collector,
&instruction.program_id,
invoke_context.get_invoke_depth(),
invoke_context.get_stack_height(),
);

let signers = signers_seeds
Expand Down
14 changes: 14 additions & 0 deletions programs/bpf/Cargo.lock

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

2 changes: 2 additions & 0 deletions programs/bpf/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ members = [
"rust/sanity",
"rust/secp256k1_recover",
"rust/sha",
"rust/sibling_inner_instruction",
"rust/sibling_instruction",
"rust/spoof1",
"rust/spoof1_system",
"rust/sysvar",
Expand Down
2 changes: 2 additions & 0 deletions programs/bpf/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@ fn main() {
"sanity",
"secp256k1_recover",
"sha",
"sibling_inner_instruction",
"sibling_instruction",
"spoof1",
"spoof1_system",
"upgradeable",
Expand Down
23 changes: 23 additions & 0 deletions programs/bpf/rust/sibling_inner_instruction/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
[package]
name = "solana-bpf-rust-sibling_inner-instructions"
version = "1.10.0"
description = "Solana BPF test program written in Rust"
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
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"]
69 changes: 69 additions & 0 deletions programs/bpf/rust/sibling_inner_instruction/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
//! Example Rust-based BPF program that queries sibling instructions

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

use solana_program::{
account_info::AccountInfo,
entrypoint,
entrypoint::ProgramResult,
instruction::{
get_processed_sibling_instruction, get_stack_height, AccountMeta, Instruction,
TRANSACTION_LEVEL_STACK_HEIGHT,
},
msg,
pubkey::Pubkey,
};

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

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

// Check sibling instructions

let sibling_instruction2 = Instruction::new_with_bytes(
*accounts[2].key,
&[3],
vec![AccountMeta::new_readonly(*accounts[1].key, false)],
);
let sibling_instruction1 = Instruction::new_with_bytes(
*accounts[1].key,
&[2],
vec![
AccountMeta::new_readonly(*accounts[0].key, true),
AccountMeta::new_readonly(*accounts[1].key, false),
],
);
let sibling_instruction0 = Instruction::new_with_bytes(
*accounts[1].key,
&[1],
vec![
AccountMeta::new_readonly(*accounts[1].key, false),
AccountMeta::new_readonly(*accounts[0].key, true),
],
);

assert_eq!(TRANSACTION_LEVEL_STACK_HEIGHT + 1, get_stack_height());
assert_eq!(
get_processed_sibling_instruction(0),
Some(sibling_instruction0)
);
assert_eq!(
get_processed_sibling_instruction(1),
Some(sibling_instruction1)
);
assert_eq!(
get_processed_sibling_instruction(2),
Some(sibling_instruction2)
);
assert_eq!(get_processed_sibling_instruction(3), None);

Ok(())
}
23 changes: 23 additions & 0 deletions programs/bpf/rust/sibling_instruction/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
[package]
name = "solana-bpf-rust-sibling-instructions"
version = "1.10.0"
description = "Solana BPF test program written in Rust"
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
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"]
100 changes: 100 additions & 0 deletions programs/bpf/rust/sibling_instruction/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
//! Example Rust-based BPF program that queries sibling instructions

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

use solana_program::{
account_info::AccountInfo,
entrypoint,
entrypoint::ProgramResult,
instruction::{
get_processed_sibling_instruction, get_stack_height, AccountMeta, Instruction,
TRANSACTION_LEVEL_STACK_HEIGHT,
},
msg,
program::invoke,
pubkey::Pubkey,
};

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

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

// Invoke child instructions

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

// Check sibling instructions

let sibling_instruction1 = Instruction::new_with_bytes(
*accounts[1].key,
&[43],
vec![
AccountMeta::new_readonly(*accounts[1].key, false),
AccountMeta::new(*accounts[0].key, true),
],
);
let sibling_instruction0 = Instruction::new_with_bytes(
*accounts[1].key,
&[42],
vec![
AccountMeta::new(*accounts[0].key, true),
AccountMeta::new_readonly(*accounts[1].key, false),
],
);

assert_eq!(TRANSACTION_LEVEL_STACK_HEIGHT, get_stack_height());
assert_eq!(
get_processed_sibling_instruction(0),
Some(sibling_instruction0)
);
assert_eq!(
get_processed_sibling_instruction(1),
Some(sibling_instruction1)
);
assert_eq!(get_processed_sibling_instruction(2), None);

Ok(())
}
Loading