Skip to content

Commit

Permalink
Add get_processed_sibling_instruction syscall (solana-labs#22859)
Browse files Browse the repository at this point in the history
  • Loading branch information
jackcmay authored and jeffwashington committed Feb 24, 2022
1 parent 9403e2f commit a30d4a4
Show file tree
Hide file tree
Showing 19 changed files with 897 additions and 60 deletions.
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 <[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"]
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 <[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"]
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

0 comments on commit a30d4a4

Please sign in to comment.