Skip to content

Commit

Permalink
Add way to look at tx instructions
Browse files Browse the repository at this point in the history
  • Loading branch information
sakridge committed Aug 28, 2020
1 parent 0221822 commit f8e977a
Show file tree
Hide file tree
Showing 12 changed files with 190 additions and 1 deletion.
8 changes: 8 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 @@ -40,6 +40,7 @@ members = [
"rust/dup_accounts",
"rust/error_handling",
"rust/external_spend",
"rust/instruction_introspection",
"rust/invoke",
"rust/invoked",
"rust/iter",
Expand Down
1 change: 1 addition & 0 deletions programs/bpf/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ fn main() {
"dup_accounts",
"error_handling",
"external_spend",
"instruction_introspection",
"invoke",
"invoked",
"iter",
Expand Down
27 changes: 27 additions & 0 deletions programs/bpf/rust/instruction_introspection/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@

# Note: This crate must be built using do.sh

[package]
name = "solana-bpf-rust-instruction-introspection"
version = "1.4.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/"
edition = "2018"

[dependencies]
solana-sdk = { path = "../../../../sdk/", version = "1.4.0", default-features = false }
bincode = "1.3.1"

[features]
program = ["solana-sdk/program"]
default = ["program"]

[lib]
name = "solana_bpf_rust_instruction_introspection"
crate-type = ["cdylib"]

[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]
2 changes: 2 additions & 0 deletions programs/bpf/rust/instruction_introspection/Xargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[target.bpfel-unknown-unknown.dependencies.std]
features = []
44 changes: 44 additions & 0 deletions programs/bpf/rust/instruction_introspection/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
//! @brief Example Rust-based BPF program that exercises instruction introspection
extern crate solana_sdk;
use solana_sdk::{
account_info::next_account_info, account_info::AccountInfo, entrypoint,
entrypoint::ProgramResult, info, instruction::Instruction, program_error::ProgramError,
pubkey::Pubkey,
};

entrypoint!(process_instruction);
fn process_instruction(
_program_id: &Pubkey,
accounts: &[AccountInfo],
instruction_data: &[u8],
) -> ProgramResult {
if instruction_data.is_empty() {
return Err(ProgramError::InvalidAccountData);
}

let secp_instruction_index = instruction_data[0];
let account_info_iter = &mut accounts.iter();
let instruction_accounts = next_account_info(account_info_iter)?;
assert_eq!(
*instruction_accounts.key,
solana_sdk::sysvar::instructions::id()
);
let instructions: Vec<Instruction> = unsafe {
bincode::deserialize(*(*instruction_accounts.data).as_ptr())
.map_err(|_| ProgramError::InvalidAccountData)?
};
if secp_instruction_index as usize > instructions.len() {
return Err(ProgramError::InvalidAccountData);
}
info!(&format!(
"{}",
instructions[secp_instruction_index as usize].program_id
));

info!(&format!(
"{}",
instructions[secp_instruction_index as usize].data[0]
));
Ok(())
}
35 changes: 35 additions & 0 deletions programs/bpf/tests/programs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -457,3 +457,38 @@ fn test_program_bpf_invoke() {
}
}
}

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

let GenesisConfigInfo {
genesis_config,
mint_keypair,
..
} = create_genesis_config(50_000);
let mut bank = Bank::new(&genesis_config);

let (name, id, entrypoint) = solana_bpf_loader_program!();
bank.add_builtin_loader(&name, id, entrypoint);
let bank = Arc::new(bank);
let bank_client = BankClient::new_shared(&bank);

let program_id = load_bpf_program(
&bank_client,
&mint_keypair,
"solana_bpf_rust_instruction_introspection",
);
let account_metas = vec![AccountMeta::new(
solana_sdk::sysvar::instructions::id(),
false,
)];
let instruction = Instruction::new(program_id, &0u8, account_metas);
let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction);
assert!(result.is_ok());

let instruction = Instruction::new(program_id, &0u8, vec![]);
let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction);
assert!(result.is_err());
}
11 changes: 11 additions & 0 deletions runtime/src/accounts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,13 @@ impl Accounts {
false
}

fn construct_instructions_account(message: &Message) -> Account {
let mut account = Account::default();
let instructions = message.decompile_instructions();
account.data = bincode::serialize(&instructions).unwrap();
account
}

fn load_tx_accounts(
&self,
storage: &AccountStorage,
Expand Down Expand Up @@ -136,6 +143,10 @@ impl Accounts {
if payer_index.is_none() {
payer_index = Some(i);
}

if solana_sdk::sysvar::instructions::check_id(key) {
return Self::construct_instructions_account(message);
}
let (account, rent) =
AccountsDB::load(storage, ancestors, accounts_index, key)
.map(|(mut account, _)| {
Expand Down
2 changes: 1 addition & 1 deletion sdk/src/instruction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ pub enum InstructionError {
ComputationalBudgetExceeded,
}

#[derive(Debug, PartialEq, Clone)]
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
pub struct Instruction {
/// Pubkey of the instruction processor that executes this instruction
pub program_id: Pubkey,
Expand Down
47 changes: 47 additions & 0 deletions sdk/src/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,34 @@ impl Message {
}
(writable_keys, readonly_keys)
}

pub fn decompile_instructions(&self) -> Vec<Instruction> {
self.instructions
.iter()
.map(|instruction| {
let accounts: Vec<_> = instruction
.accounts
.iter()
.map(|account_index| {
let account_index = *account_index as usize;
let is_signer = self.is_signer(account_index);
let is_writable = self.is_writable(account_index);
AccountMeta {
pubkey: self.account_keys[account_index],
is_signer,
is_writable,
}
})
.collect();
let program_id = self.account_keys[instruction.program_id_index as usize];
Instruction {
program_id,
data: instruction.data.clone(),
accounts,
}
})
.collect()
}
}

#[cfg(test)]
Expand Down Expand Up @@ -673,4 +701,23 @@ mod tests {
(vec![&id1, &id0], vec![&id3, &id2, &program_id])
);
}

#[test]
fn test_decompile_instructions() {
let program_id0 = Pubkey::new_rand();
let program_id1 = Pubkey::new_rand();
let id0 = Pubkey::new_rand();
let id1 = Pubkey::new_rand();
let id2 = Pubkey::new_rand();
let id3 = Pubkey::new_rand();
let instructions = vec![
Instruction::new(program_id0, &0, vec![AccountMeta::new(id0, false)]),
Instruction::new(program_id0, &0, vec![AccountMeta::new(id1, true)]),
Instruction::new(program_id1, &0, vec![AccountMeta::new_readonly(id2, false)]),
Instruction::new(program_id1, &0, vec![AccountMeta::new_readonly(id3, true)]),
];

let message = Message::new(&instructions, Some(&id1));
assert_eq!(message.decompile_instructions(), instructions);
}
}
11 changes: 11 additions & 0 deletions sdk/src/sysvar/instructions.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
//! This account contains the serialized transaction instructions
//!
use crate::instruction::Instruction;
use crate::sysvar::Sysvar;

pub type Instructions = Vec<Instruction>;

crate::declare_sysvar_id!("instructions1111111111111111111111111111111", Instructions);

impl Sysvar for Instructions {}
2 changes: 2 additions & 0 deletions sdk/src/sysvar/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use crate::{
pub mod clock;
pub mod epoch_schedule;
pub mod fees;
pub mod instructions;
pub mod recent_blockhashes;
pub mod rent;
pub mod rewards;
Expand All @@ -28,6 +29,7 @@ pub fn is_sysvar_id(id: &Pubkey) -> bool {
|| slot_hashes::check_id(id)
|| slot_history::check_id(id)
|| stake_history::check_id(id)
|| instructions::check_id(id)
}

#[macro_export]
Expand Down

0 comments on commit f8e977a

Please sign in to comment.