Parse bpf loader instructions (#12998)

* Add parsing for BpfLoader2 instructions

* Skip info if null

* Return account address in info map
This commit is contained in:
Tyera Eulberg 2020-10-19 23:13:02 -06:00 committed by GitHub
parent c7c6c28455
commit 942e4273ba
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 111 additions and 1 deletions

View File

@ -4,6 +4,7 @@ extern crate lazy_static;
extern crate serde_derive;
pub mod parse_accounts;
pub mod parse_bpf_loader;
pub mod parse_instruction;
pub mod parse_token;

View File

@ -0,0 +1,102 @@
use crate::parse_instruction::{ParsableProgram, ParseInstructionError, ParsedInstructionEnum};
use bincode::deserialize;
use serde_json::json;
use solana_sdk::{
instruction::CompiledInstruction, loader_instruction::LoaderInstruction, pubkey::Pubkey,
};
pub fn parse_bpf_loader(
instruction: &CompiledInstruction,
account_keys: &[Pubkey],
) -> Result<ParsedInstructionEnum, ParseInstructionError> {
let bpf_loader_instruction: LoaderInstruction =
deserialize(&instruction.data).map_err(|err| {
println!("{:?}", err);
ParseInstructionError::InstructionNotParsable(ParsableProgram::BpfLoader)
})?;
if instruction.accounts.is_empty() || instruction.accounts[0] as usize >= account_keys.len() {
return Err(ParseInstructionError::InstructionKeyMismatch(
ParsableProgram::BpfLoader,
));
}
match bpf_loader_instruction {
LoaderInstruction::Write { offset, bytes } => Ok(ParsedInstructionEnum {
instruction_type: "write".to_string(),
info: json!({
"offset": offset,
"bytes": base64::encode(bytes),
"account": account_keys[instruction.accounts[0] as usize].to_string(),
}),
}),
LoaderInstruction::Finalize => Ok(ParsedInstructionEnum {
instruction_type: "finalize".to_string(),
info: json!({
"account": account_keys[instruction.accounts[0] as usize].to_string(),
}),
}),
}
}
#[cfg(test)]
mod test {
use super::*;
use solana_sdk::{message::Message, pubkey::Pubkey};
#[test]
fn test_parse_bpf_loader_instructions() {
let account_pubkey = Pubkey::new_rand();
let program_id = Pubkey::new_rand();
let offset = 4242;
let bytes = vec![8; 99];
let fee_payer = Pubkey::new_rand();
let account_keys = vec![fee_payer, account_pubkey];
let missing_account_keys = vec![account_pubkey];
let instruction = solana_sdk::loader_instruction::write(
&account_pubkey,
&program_id,
offset,
bytes.clone(),
);
let message = Message::new(&[instruction], Some(&fee_payer));
assert_eq!(
parse_bpf_loader(&message.instructions[0], &account_keys).unwrap(),
ParsedInstructionEnum {
instruction_type: "write".to_string(),
info: json!({
"offset": offset,
"bytes": base64::encode(&bytes),
"account": account_pubkey.to_string(),
}),
}
);
assert!(parse_bpf_loader(&message.instructions[0], &missing_account_keys).is_err());
let instruction = solana_sdk::loader_instruction::finalize(&account_pubkey, &program_id);
let message = Message::new(&[instruction], Some(&fee_payer));
assert_eq!(
parse_bpf_loader(&message.instructions[0], &account_keys).unwrap(),
ParsedInstructionEnum {
instruction_type: "finalize".to_string(),
info: json!({
"account": account_pubkey.to_string(),
}),
}
);
assert!(parse_bpf_loader(&message.instructions[0], &missing_account_keys).is_err());
let bad_compiled_instruction = CompiledInstruction {
program_id_index: 3,
accounts: vec![1, 2],
data: vec![2, 0, 0, 0], // LoaderInstruction enum only has 2 variants
};
assert!(parse_bpf_loader(&bad_compiled_instruction, &account_keys).is_err());
let bad_compiled_instruction = CompiledInstruction {
program_id_index: 3,
accounts: vec![],
data: vec![1, 0, 0, 0],
};
assert!(parse_bpf_loader(&bad_compiled_instruction, &account_keys).is_err());
}
}

View File

@ -1,4 +1,4 @@
use crate::parse_token::parse_token;
use crate::{parse_bpf_loader::parse_bpf_loader, parse_token::parse_token};
use inflector::Inflector;
use serde_json::Value;
use solana_account_decoder::parse_token::spl_token_id_v2_0;
@ -10,6 +10,7 @@ use std::{
use thiserror::Error;
lazy_static! {
static ref BPF_LOADER_PROGRAM_ID: Pubkey = solana_sdk::bpf_loader::id();
static ref MEMO_PROGRAM_ID: Pubkey =
Pubkey::from_str(&spl_memo_v1_0::id().to_string()).unwrap();
static ref TOKEN_PROGRAM_ID: Pubkey = spl_token_id_v2_0();
@ -17,6 +18,7 @@ lazy_static! {
let mut m = HashMap::new();
m.insert(*MEMO_PROGRAM_ID, ParsableProgram::SplMemo);
m.insert(*TOKEN_PROGRAM_ID, ParsableProgram::SplToken);
m.insert(*BPF_LOADER_PROGRAM_ID, ParsableProgram::BpfLoader);
m
};
}
@ -49,6 +51,7 @@ pub struct ParsedInstruction {
pub struct ParsedInstructionEnum {
#[serde(rename = "type")]
pub instruction_type: String,
#[serde(default, skip_serializing_if = "Value::is_null")]
pub info: Value,
}
@ -57,6 +60,7 @@ pub struct ParsedInstructionEnum {
pub enum ParsableProgram {
SplMemo,
SplToken,
BpfLoader,
}
pub fn parse(
@ -70,6 +74,9 @@ pub fn parse(
let parsed_json = match program_name {
ParsableProgram::SplMemo => parse_memo(instruction),
ParsableProgram::SplToken => serde_json::to_value(parse_token(instruction, account_keys)?)?,
ParsableProgram::BpfLoader => {
serde_json::to_value(parse_bpf_loader(instruction, account_keys)?)?
}
};
Ok(ParsedInstruction {
program: format!("{:?}", program_name).to_kebab_case(),