Programs can only sign their accounts

This commit is contained in:
Jack May 2020-05-08 12:24:36 -07:00
parent b4b4d6b00d
commit 97e17f9b32
10 changed files with 187 additions and 100 deletions

View File

@ -1,22 +1,23 @@
/** /**
* @brief Example C-based BPF program that prints out the parameters * @brief Example C-based BPF program that tests cross-program invocations
* passed to it
*/ */
#include "../invoked/instruction.h"
#include <solana_sdk.h> #include <solana_sdk.h>
#define MINT_INDEX 0 static const int MINT_INDEX = 0;
#define ARGUMENT_INDEX 1 static const int ARGUMENT_INDEX = 1;
#define INVOKED_PROGRAM_INDEX 2 static const int INVOKED_PROGRAM_INDEX = 2;
#define INVOKED_ARGUMENT_INDEX 3 static const int INVOKED_ARGUMENT_INDEX = 3;
#define INVOKED_PROGRAM_DUP_INDEX 4 static const int INVOKED_PROGRAM_DUP_INDEX = 4;
#define ARGUMENT_DUP_INDEX 5 static const int ARGUMENT_DUP_INDEX = 5;
#define DERIVED_KEY_INDEX 6 static const int DERIVED_KEY1_INDEX = 6;
#define DERIVED_KEY2_INDEX 7 static const int DERIVED_KEY2_INDEX = 7;
static const int DERIVED_KEY3_INDEX = 8;
extern uint64_t entrypoint(const uint8_t *input) { extern uint64_t entrypoint(const uint8_t *input) {
sol_log("Invoke C program"); sol_log("Invoke C program");
SolAccountInfo accounts[8]; SolAccountInfo accounts[9];
SolParameters params = (SolParameters){.ka = accounts}; SolParameters params = (SolParameters){.ka = accounts};
if (!sol_deserialize(input, &params, SOL_ARRAY_SIZE(accounts))) { if (!sol_deserialize(input, &params, SOL_ARRAY_SIZE(accounts))) {
@ -34,40 +35,45 @@ extern uint64_t entrypoint(const uint8_t *input) {
{accounts[INVOKED_ARGUMENT_INDEX].key, true, true}, {accounts[INVOKED_ARGUMENT_INDEX].key, true, true},
{accounts[INVOKED_PROGRAM_INDEX].key, false, false}, {accounts[INVOKED_PROGRAM_INDEX].key, false, false},
{accounts[INVOKED_PROGRAM_DUP_INDEX].key, false, false}}; {accounts[INVOKED_PROGRAM_DUP_INDEX].key, false, false}};
uint8_t data[] = {0, 1, 2, 3, 4, 5}; uint8_t data[] = {TEST_VERIFY_TRANSLATIONS, 1, 2, 3, 4, 5};
const SolInstruction instruction = {accounts[INVOKED_PROGRAM_INDEX].key, const SolInstruction instruction = {accounts[INVOKED_PROGRAM_INDEX].key,
arguments, 4, data, 6}; arguments, 4, data, 6};
sol_assert(SUCCESS == sol_invoke(&instruction, accounts, sol_assert(SUCCESS ==
SOL_ARRAY_SIZE(accounts))); sol_invoke(&instruction, accounts, SOL_ARRAY_SIZE(accounts)));
} }
sol_log("Test return error"); sol_log("Test return error");
{ {
SolAccountMeta arguments[] = {{accounts[ARGUMENT_INDEX].key, true, true}}; SolAccountMeta arguments[] = {{accounts[ARGUMENT_INDEX].key, true, true}};
uint8_t data[] = {1}; uint8_t data[] = {TEST_RETURN_ERROR};
const SolInstruction instruction = {accounts[INVOKED_PROGRAM_INDEX].key, const SolInstruction instruction = {accounts[INVOKED_PROGRAM_INDEX].key,
arguments, SOL_ARRAY_SIZE(arguments), arguments, SOL_ARRAY_SIZE(arguments),
data, SOL_ARRAY_SIZE(data)}; data, SOL_ARRAY_SIZE(data)};
sol_assert(42 == sol_invoke(&instruction, accounts, sol_assert(42 ==
SOL_ARRAY_SIZE(accounts))); sol_invoke(&instruction, accounts, SOL_ARRAY_SIZE(accounts)));
} }
sol_log("Test derived signers"); sol_log("Test derived signers");
{ {
sol_assert(!accounts[DERIVED_KEY1_INDEX].is_signer);
sol_assert(!accounts[DERIVED_KEY2_INDEX].is_signer);
sol_assert(!accounts[DERIVED_KEY3_INDEX].is_signer);
SolAccountMeta arguments[] = { SolAccountMeta arguments[] = {
{accounts[DERIVED_KEY_INDEX].key, true, true}, {accounts[INVOKED_PROGRAM_INDEX].key, false, false},
{accounts[DERIVED_KEY2_INDEX].key, false, true}}; {accounts[DERIVED_KEY1_INDEX].key, true, true},
uint8_t data[] = {2}; {accounts[DERIVED_KEY2_INDEX].key, true, false},
{accounts[DERIVED_KEY3_INDEX].key, false, false}};
uint8_t data[] = {TEST_DERIVED_SIGNERS};
const SolInstruction instruction = {accounts[INVOKED_PROGRAM_INDEX].key, const SolInstruction instruction = {accounts[INVOKED_PROGRAM_INDEX].key,
arguments, SOL_ARRAY_SIZE(arguments), arguments, SOL_ARRAY_SIZE(arguments),
data, SOL_ARRAY_SIZE(data)}; data, SOL_ARRAY_SIZE(data)};
const SolSignerSeed seeds1[] = {{"Lil'", 4}, {"Bits", 4}}; const SolSignerSeed seeds1[] = {{"You pass butter", 15}};
const SolSignerSeed seeds2[] = {{"Gar Ma Nar Nar", 14}}; const SolSignerSeed seeds2[] = {{"Lil'", 4}, {"Bits", 4}};
const SolSignerSeeds signers_seeds[] = {{seeds1, SOL_ARRAY_SIZE(seeds1)}, const SolSignerSeeds signers_seeds[] = {{seeds1, SOL_ARRAY_SIZE(seeds1)},
{seeds2, SOL_ARRAY_SIZE(seeds2)}}; {seeds2, SOL_ARRAY_SIZE(seeds2)}};
sol_assert(SUCCESS == sol_invoke_signed( sol_assert(SUCCESS == sol_invoke_signed(
&instruction, accounts, SOL_ARRAY_SIZE(accounts), &instruction, accounts, SOL_ARRAY_SIZE(accounts),
signers_seeds, SOL_ARRAY_SIZE(signers_seeds))); signers_seeds, SOL_ARRAY_SIZE(signers_seeds)));
@ -77,43 +83,36 @@ extern uint64_t entrypoint(const uint8_t *input) {
{ {
SolAccountMeta arguments[] = { SolAccountMeta arguments[] = {
{accounts[INVOKED_ARGUMENT_INDEX].key, true, false}}; {accounts[INVOKED_ARGUMENT_INDEX].key, true, false}};
uint8_t data[] = {3}; uint8_t data[] = {TEST_VERIFY_WRITER};
const SolInstruction instruction = {accounts[INVOKED_PROGRAM_INDEX].key, const SolInstruction instruction = {accounts[INVOKED_PROGRAM_INDEX].key,
arguments, SOL_ARRAY_SIZE(arguments), arguments, SOL_ARRAY_SIZE(arguments),
data, SOL_ARRAY_SIZE(data)}; data, SOL_ARRAY_SIZE(data)};
sol_assert(SUCCESS == sol_invoke(&instruction, accounts, sol_assert(SUCCESS ==
SOL_ARRAY_SIZE(accounts))); sol_invoke(&instruction, accounts, SOL_ARRAY_SIZE(accounts)));
} }
sol_log("Test invoke"); sol_log("Test invoke");
{ {
sol_assert(accounts[ARGUMENT_INDEX].is_signer); sol_assert(accounts[ARGUMENT_INDEX].is_signer);
sol_assert(!accounts[DERIVED_KEY_INDEX].is_signer);
sol_assert(!accounts[DERIVED_KEY2_INDEX].is_signer);
*accounts[ARGUMENT_INDEX].lamports -= 5; *accounts[ARGUMENT_INDEX].lamports -= 5;
*accounts[INVOKED_ARGUMENT_INDEX].lamports += 5; *accounts[INVOKED_ARGUMENT_INDEX].lamports += 5;
SolAccountMeta arguments[] = { SolAccountMeta arguments[] = {
{accounts[INVOKED_ARGUMENT_INDEX].key, true, true}, {accounts[INVOKED_ARGUMENT_INDEX].key, true, true},
{accounts[ARGUMENT_INDEX].key, true, true}, {accounts[ARGUMENT_INDEX].key, true, true}};
{accounts[DERIVED_KEY_INDEX].key, true, true}}; uint8_t data[] = {TEST_NESTED_INVOKE};
uint8_t data[] = {4};
const SolInstruction instruction = {accounts[INVOKED_PROGRAM_INDEX].key, const SolInstruction instruction = {accounts[INVOKED_PROGRAM_INDEX].key,
arguments, SOL_ARRAY_SIZE(arguments), arguments, SOL_ARRAY_SIZE(arguments),
data, SOL_ARRAY_SIZE(data)}; data, SOL_ARRAY_SIZE(data)};
const SolSignerSeed seeds[] = {{"Lil'", 4}, {"Bits", 4}};
const SolSignerSeeds signers_seeds[] = {{seeds, SOL_ARRAY_SIZE(seeds)}};
sol_log("Fist invoke"); sol_log("Fist invoke");
sol_assert(SUCCESS == sol_invoke_signed( sol_assert(SUCCESS ==
&instruction, accounts, SOL_ARRAY_SIZE(accounts), sol_invoke(&instruction, accounts, SOL_ARRAY_SIZE(accounts)));
signers_seeds, SOL_ARRAY_SIZE(signers_seeds)));
sol_log("2nd invoke from first program"); sol_log("2nd invoke from first program");
sol_assert(SUCCESS == sol_invoke_signed( sol_assert(SUCCESS ==
&instruction, accounts, SOL_ARRAY_SIZE(accounts), sol_invoke(&instruction, accounts, SOL_ARRAY_SIZE(accounts)));
signers_seeds, SOL_ARRAY_SIZE(signers_seeds)));
sol_assert(*accounts[ARGUMENT_INDEX].lamports == 42 - 5 + 1 + 1); sol_assert(*accounts[ARGUMENT_INDEX].lamports == 42 - 5 + 1 + 1);
sol_assert(*accounts[INVOKED_ARGUMENT_INDEX].lamports == 10 + 5 - 1 - 1); sol_assert(*accounts[INVOKED_ARGUMENT_INDEX].lamports == 10 + 5 - 1 - 1);

View File

@ -1,7 +1,7 @@
/** /**
* @brief Example C-based BPF program that prints out the parameters * @brief Example C-based BPF program that tests cross-program invocations
* passed to it
*/ */
#include "instruction.h"
#include <solana_sdk.h> #include <solana_sdk.h>
extern uint64_t entrypoint(const uint8_t *input) { extern uint64_t entrypoint(const uint8_t *input) {
@ -13,7 +13,7 @@ extern uint64_t entrypoint(const uint8_t *input) {
} }
switch (params.data[0]) { switch (params.data[0]) {
case (0): { case TEST_VERIFY_TRANSLATIONS: {
sol_log("verify data translations"); sol_log("verify data translations");
static const int ARGUMENT_INDEX = 0; static const int ARGUMENT_INDEX = 0;
@ -54,7 +54,7 @@ extern uint64_t entrypoint(const uint8_t *input) {
sol_assert( sol_assert(
SolPubkey_same(accounts[INVOKED_PROGRAM_INDEX].key, params.program_id)) SolPubkey_same(accounts[INVOKED_PROGRAM_INDEX].key, params.program_id))
sol_assert(SolPubkey_same(accounts[INVOKED_PROGRAM_INDEX].owner, sol_assert(SolPubkey_same(accounts[INVOKED_PROGRAM_INDEX].owner,
&bpf_loader_id)); &bpf_loader_id));
sol_assert(!accounts[INVOKED_PROGRAM_INDEX].is_signer); sol_assert(!accounts[INVOKED_PROGRAM_INDEX].is_signer);
sol_assert(!accounts[INVOKED_PROGRAM_INDEX].is_writable); sol_assert(!accounts[INVOKED_PROGRAM_INDEX].is_writable);
sol_assert(accounts[INVOKED_PROGRAM_INDEX].rent_epoch == 1); sol_assert(accounts[INVOKED_PROGRAM_INDEX].rent_epoch == 1);
@ -76,21 +76,55 @@ extern uint64_t entrypoint(const uint8_t *input) {
accounts[INVOKED_PROGRAM_DUP_INDEX].executable); accounts[INVOKED_PROGRAM_DUP_INDEX].executable);
break; break;
} }
case (1): { case TEST_RETURN_ERROR: {
sol_log("reutrn error"); sol_log("reutrn error");
return 42; return 42;
} }
case (2): { case TEST_DERIVED_SIGNERS: {
sol_log("verify derived signers"); sol_log("verify derived signers");
static const int DERIVED_KEY_INDEX = 0; static const int INVOKED_PROGRAM_INDEX = 0;
static const int DERIVED_KEY2_INDEX = 1; static const int DERIVED_KEY1_INDEX = 1;
sol_assert(sol_deserialize(input, &params, 2)); static const int DERIVED_KEY2_INDEX = 2;
static const int DERIVED_KEY3_INDEX = 3;
sol_assert(sol_deserialize(input, &params, 4));
sol_assert(accounts[DERIVED_KEY_INDEX].is_signer); sol_assert(accounts[DERIVED_KEY1_INDEX].is_signer);
sol_assert(!accounts[DERIVED_KEY2_INDEX].is_signer);
sol_assert(!accounts[DERIVED_KEY2_INDEX].is_signer);
SolAccountMeta arguments[] = {
{accounts[DERIVED_KEY1_INDEX].key, true, false},
{accounts[DERIVED_KEY2_INDEX].key, true, true},
{accounts[DERIVED_KEY3_INDEX].key, false, true}};
uint8_t data[] = {TEST_VERIFY_NESTED_SIGNERS};
const SolInstruction instruction = {accounts[INVOKED_PROGRAM_INDEX].key,
arguments, SOL_ARRAY_SIZE(arguments),
data, SOL_ARRAY_SIZE(data)};
const SolSignerSeed seeds1[] = {{"Lil'", 4}, {"Bits", 4}};
const SolSignerSeed seeds2[] = {{"Gar Ma Nar Nar", 14}};
const SolSignerSeeds signers_seeds[] = {{seeds1, SOL_ARRAY_SIZE(seeds1)},
{seeds2, SOL_ARRAY_SIZE(seeds2)}};
sol_assert(SUCCESS == sol_invoke_signed(
&instruction, accounts, SOL_ARRAY_SIZE(accounts),
signers_seeds, SOL_ARRAY_SIZE(signers_seeds)));
break;
}
case TEST_VERIFY_NESTED_SIGNERS: {
sol_log("verify derived nested signers");
static const int DERIVED_KEY1_INDEX = 0;
static const int DERIVED_KEY2_INDEX = 1;
static const int DERIVED_KEY3_INDEX = 2;
sol_assert(sol_deserialize(input, &params, 3));
sol_assert(!accounts[DERIVED_KEY1_INDEX].is_signer);
sol_assert(accounts[DERIVED_KEY2_INDEX].is_signer);
sol_assert(accounts[DERIVED_KEY2_INDEX].is_signer); sol_assert(accounts[DERIVED_KEY2_INDEX].is_signer);
break; break;
} }
case (3): {
case TEST_VERIFY_WRITER: {
sol_log("verify writable"); sol_log("verify writable");
static const int ARGUMENT_INDEX = 0; static const int ARGUMENT_INDEX = 0;
sol_assert(sol_deserialize(input, &params, 1)); sol_assert(sol_deserialize(input, &params, 1));
@ -98,17 +132,15 @@ extern uint64_t entrypoint(const uint8_t *input) {
sol_assert(accounts[ARGUMENT_INDEX].is_writable); sol_assert(accounts[ARGUMENT_INDEX].is_writable);
break; break;
} }
case (4): { case TEST_NESTED_INVOKE: {
sol_log("invoke"); sol_log("invoke");
static const int INVOKED_ARGUMENT_INDEX = 0; static const int INVOKED_ARGUMENT_INDEX = 0;
static const int ARGUMENT_INDEX = 1; static const int ARGUMENT_INDEX = 1;
static const int DERIVED_KEY_INDEX = 2; sol_assert(sol_deserialize(input, &params, 2));
sol_assert(sol_deserialize(input, &params, 3));
sol_assert(accounts[INVOKED_ARGUMENT_INDEX].is_signer); sol_assert(accounts[INVOKED_ARGUMENT_INDEX].is_signer);
sol_assert(accounts[ARGUMENT_INDEX].is_signer); sol_assert(accounts[ARGUMENT_INDEX].is_signer);
sol_assert(accounts[DERIVED_KEY_INDEX].is_signer);
*accounts[INVOKED_ARGUMENT_INDEX].lamports -= 1; *accounts[INVOKED_ARGUMENT_INDEX].lamports -= 1;
*accounts[ARGUMENT_INDEX].lamports += 1; *accounts[ARGUMENT_INDEX].lamports += 1;

View File

@ -4,7 +4,7 @@
extern crate solana_sdk; extern crate solana_sdk;
use solana_bpf_rust_invoked::instruction::create_instruction; use solana_bpf_rust_invoked::instruction::*;
use solana_sdk::{ use solana_sdk::{
account_info::AccountInfo, account_info::AccountInfo,
entrypoint, entrypoint,
@ -21,8 +21,9 @@ const INVOKED_PROGRAM_INDEX: usize = 2;
const INVOKED_ARGUMENT_INDEX: usize = 3; const INVOKED_ARGUMENT_INDEX: usize = 3;
const INVOKED_PROGRAM_DUP_INDEX: usize = 4; const INVOKED_PROGRAM_DUP_INDEX: usize = 4;
// const ARGUMENT_DUP_INDEX: usize = 5; // const ARGUMENT_DUP_INDEX: usize = 5;
const DERIVED_KEY_INDEX: usize = 6; const DERIVED_KEY1_INDEX: usize = 6;
const DERIVED_KEY2_INDEX: usize = 7; const DERIVED_KEY2_INDEX: usize = 7;
const DERIVED_KEY3_INDEX: usize = 8;
entrypoint!(process_instruction); entrypoint!(process_instruction);
fn process_instruction( fn process_instruction(
@ -49,7 +50,7 @@ fn process_instruction(
(accounts[INVOKED_PROGRAM_INDEX].key, false, false), (accounts[INVOKED_PROGRAM_INDEX].key, false, false),
(accounts[INVOKED_PROGRAM_DUP_INDEX].key, false, false), (accounts[INVOKED_PROGRAM_DUP_INDEX].key, false, false),
], ],
vec![0, 1, 2, 3, 4, 5], vec![TEST_VERIFY_TRANSLATIONS, 1, 2, 3, 4, 5],
); );
invoke(&instruction, accounts)?; invoke(&instruction, accounts)?;
} }
@ -59,7 +60,7 @@ fn process_instruction(
let instruction = create_instruction( let instruction = create_instruction(
*accounts[INVOKED_PROGRAM_INDEX].key, *accounts[INVOKED_PROGRAM_INDEX].key,
&[(accounts[ARGUMENT_INDEX].key, true, true)], &[(accounts[ARGUMENT_INDEX].key, true, true)],
vec![1], vec![TEST_RETURN_ERROR],
); );
assert_eq!( assert_eq!(
invoke(&instruction, accounts), invoke(&instruction, accounts),
@ -69,21 +70,24 @@ fn process_instruction(
info!("Test derived signers"); info!("Test derived signers");
{ {
assert!(!accounts[DERIVED_KEY_INDEX].is_signer); assert!(!accounts[DERIVED_KEY1_INDEX].is_signer);
assert!(!accounts[DERIVED_KEY2_INDEX].is_signer); assert!(!accounts[DERIVED_KEY2_INDEX].is_signer);
assert!(!accounts[DERIVED_KEY3_INDEX].is_signer);
let invoked_instruction = create_instruction( let invoked_instruction = create_instruction(
*accounts[INVOKED_PROGRAM_INDEX].key, *accounts[INVOKED_PROGRAM_INDEX].key,
&[ &[
(accounts[DERIVED_KEY_INDEX].key, true, true), (accounts[INVOKED_PROGRAM_INDEX].key, false, false),
(accounts[DERIVED_KEY2_INDEX].key, false, true), (accounts[DERIVED_KEY1_INDEX].key, true, true),
(accounts[DERIVED_KEY2_INDEX].key, true, false),
(accounts[DERIVED_KEY3_INDEX].key, false, false),
], ],
vec![2], vec![TEST_DERIVED_SIGNERS],
); );
invoke_signed( invoke_signed(
&invoked_instruction, &invoked_instruction,
accounts, accounts,
&[&["Lil'", "Bits"], &["Gar Ma Nar Nar"]], &[&["You pass butter"], &["Lil'", "Bits"]],
)?; )?;
} }
@ -92,7 +96,7 @@ fn process_instruction(
let invoked_instruction = create_instruction( let invoked_instruction = create_instruction(
*accounts[INVOKED_PROGRAM_INDEX].key, *accounts[INVOKED_PROGRAM_INDEX].key,
&[(accounts[ARGUMENT_INDEX].key, false, true)], &[(accounts[ARGUMENT_INDEX].key, false, true)],
vec![3], vec![TEST_VERIFY_WRITER],
); );
invoke(&invoked_instruction, accounts)?; invoke(&invoked_instruction, accounts)?;
} }
@ -100,8 +104,6 @@ fn process_instruction(
info!("Test nested invoke"); info!("Test nested invoke");
{ {
assert!(accounts[ARGUMENT_INDEX].is_signer); assert!(accounts[ARGUMENT_INDEX].is_signer);
assert!(!accounts[DERIVED_KEY_INDEX].is_signer);
assert!(!accounts[DERIVED_KEY2_INDEX].is_signer);
**accounts[ARGUMENT_INDEX].lamports.borrow_mut() -= 5; **accounts[ARGUMENT_INDEX].lamports.borrow_mut() -= 5;
**accounts[INVOKED_ARGUMENT_INDEX].lamports.borrow_mut() += 5; **accounts[INVOKED_ARGUMENT_INDEX].lamports.borrow_mut() += 5;
@ -112,11 +114,10 @@ fn process_instruction(
&[ &[
(accounts[ARGUMENT_INDEX].key, true, true), (accounts[ARGUMENT_INDEX].key, true, true),
(accounts[INVOKED_ARGUMENT_INDEX].key, true, true), (accounts[INVOKED_ARGUMENT_INDEX].key, true, true),
(accounts[DERIVED_KEY_INDEX].key, true, false),
(accounts[INVOKED_PROGRAM_DUP_INDEX].key, false, false), (accounts[INVOKED_PROGRAM_DUP_INDEX].key, false, false),
(accounts[INVOKED_PROGRAM_DUP_INDEX].key, false, false), (accounts[INVOKED_PROGRAM_DUP_INDEX].key, false, false),
], ],
vec![4], vec![TEST_NESTED_INVOKE],
); );
invoke(&instruction, accounts)?; invoke(&instruction, accounts)?;
info!("2nd invoke from first program"); info!("2nd invoke from first program");

View File

@ -5,6 +5,13 @@ use solana_sdk::{
pubkey::Pubkey, pubkey::Pubkey,
}; };
pub const TEST_VERIFY_TRANSLATIONS: u8 = 0;
pub const TEST_RETURN_ERROR: u8 = 1;
pub const TEST_DERIVED_SIGNERS: u8 = 2;
pub const TEST_VERIFY_NESTED_SIGNERS: u8 = 3;
pub const TEST_VERIFY_WRITER: u8 = 4;
pub const TEST_NESTED_INVOKE: u8 = 5;
pub fn create_instruction( pub fn create_instruction(
program_id: Pubkey, program_id: Pubkey,
arguments: &[(&Pubkey, bool, bool)], arguments: &[(&Pubkey, bool, bool)],

View File

@ -6,10 +6,15 @@ pub mod instruction;
extern crate solana_sdk; extern crate solana_sdk;
use crate::instruction::create_instruction; use crate::instruction::*;
use solana_sdk::{ use solana_sdk::{
account_info::AccountInfo, bpf_loader, entrypoint, entrypoint::ProgramResult, info, account_info::AccountInfo,
program::invoke_signed, program_error::ProgramError, pubkey::Pubkey, bpf_loader, entrypoint,
entrypoint::ProgramResult,
info,
program::{invoke, invoke_signed},
program_error::ProgramError,
pubkey::Pubkey,
}; };
entrypoint!(process_instruction); entrypoint!(process_instruction);
@ -22,7 +27,7 @@ fn process_instruction(
info!("Invoked program"); info!("Invoked program");
match instruction_data[0] { match instruction_data[0] {
0 => { TEST_VERIFY_TRANSLATIONS => {
info!("verify data translations"); info!("verify data translations");
const ARGUMENT_INDEX: usize = 0; const ARGUMENT_INDEX: usize = 0;
@ -30,7 +35,7 @@ fn process_instruction(
const INVOKED_PROGRAM_INDEX: usize = 2; const INVOKED_PROGRAM_INDEX: usize = 2;
const INVOKED_PROGRAM_DUP_INDEX: usize = 3; const INVOKED_PROGRAM_DUP_INDEX: usize = 3;
assert_eq!(instruction_data, &[0, 1, 2, 3, 4, 5]); assert_eq!(&instruction_data[1..], &[1, 2, 3, 4, 5]);
assert_eq!(accounts.len(), 4); assert_eq!(accounts.len(), 4);
assert_eq!(accounts[ARGUMENT_INDEX].lamports(), 42); assert_eq!(accounts[ARGUMENT_INDEX].lamports(), 42);
@ -100,51 +105,76 @@ fn process_instruction(
info!(data[0], 0, 0, 0, 0); info!(data[0], 0, 0, 0, 0);
} }
} }
1 => { TEST_RETURN_ERROR => {
info!("return error"); info!("return error");
return Err(ProgramError::Custom(42)); return Err(ProgramError::Custom(42));
} }
2 => { TEST_DERIVED_SIGNERS => {
info!("verify derived signers"); info!("verify derived signers");
const DERIVED_KEY_INDEX: usize = 0; const INVOKED_PROGRAM_INDEX: usize = 0;
const DERIVED_KEY2_INDEX: usize = 1; const DERIVED_KEY1_INDEX: usize = 1;
const DERIVED_KEY2_INDEX: usize = 2;
const DERIVED_KEY3_INDEX: usize = 3;
assert!(accounts[DERIVED_KEY_INDEX].is_signer); assert!(accounts[DERIVED_KEY1_INDEX].is_signer);
assert!(accounts[DERIVED_KEY2_INDEX].is_signer); assert!(!accounts[DERIVED_KEY2_INDEX].is_signer);
assert!(!accounts[DERIVED_KEY3_INDEX].is_signer);
let invoked_instruction = create_instruction(
*accounts[INVOKED_PROGRAM_INDEX].key,
&[
(accounts[DERIVED_KEY1_INDEX].key, true, false),
(accounts[DERIVED_KEY2_INDEX].key, true, true),
(accounts[DERIVED_KEY3_INDEX].key, false, true),
],
vec![TEST_VERIFY_NESTED_SIGNERS],
);
invoke_signed(
&invoked_instruction,
accounts,
&[&["Lil'", "Bits"], &["Gar Ma Nar Nar"]],
)?;
} }
3 => { TEST_VERIFY_NESTED_SIGNERS => {
info!("verify nested derived signers");
const DERIVED_KEY1_INDEX: usize = 0;
const DERIVED_KEY2_INDEX: usize = 1;
const DERIVED_KEY3_INDEX: usize = 2;
assert!(!accounts[DERIVED_KEY1_INDEX].is_signer);
assert!(accounts[DERIVED_KEY2_INDEX].is_signer);
assert!(accounts[DERIVED_KEY3_INDEX].is_signer);
}
TEST_VERIFY_WRITER => {
info!("verify writable"); info!("verify writable");
const ARGUMENT_INDEX: usize = 0; const ARGUMENT_INDEX: usize = 0;
assert!(!accounts[ARGUMENT_INDEX].is_writable); assert!(!accounts[ARGUMENT_INDEX].is_writable);
} }
4 => { TEST_NESTED_INVOKE => {
info!("nested invoke"); info!("nested invoke");
const ARGUMENT_INDEX: usize = 0; const ARGUMENT_INDEX: usize = 0;
const INVOKED_ARGUMENT_INDEX: usize = 1; const INVOKED_ARGUMENT_INDEX: usize = 1;
const DERIVED_KEY_INDEX: usize = 2;
const INVOKED_PROGRAM_INDEX: usize = 3; const INVOKED_PROGRAM_INDEX: usize = 3;
assert!(accounts[INVOKED_ARGUMENT_INDEX].is_signer); assert!(accounts[INVOKED_ARGUMENT_INDEX].is_signer);
**accounts[INVOKED_ARGUMENT_INDEX].lamports.borrow_mut() -= 1; **accounts[INVOKED_ARGUMENT_INDEX].lamports.borrow_mut() -= 1;
**accounts[ARGUMENT_INDEX].lamports.borrow_mut() += 1; **accounts[ARGUMENT_INDEX].lamports.borrow_mut() += 1;
if accounts.len() > 3 { if accounts.len() > 2 {
info!("Invoke again"); info!("Invoke again");
let invoked_instruction = create_instruction( let invoked_instruction = create_instruction(
*accounts[INVOKED_PROGRAM_INDEX].key, *accounts[INVOKED_PROGRAM_INDEX].key,
&[ &[
(accounts[ARGUMENT_INDEX].key, true, true), (accounts[ARGUMENT_INDEX].key, true, true),
(accounts[INVOKED_ARGUMENT_INDEX].key, true, true), (accounts[INVOKED_ARGUMENT_INDEX].key, true, true),
(accounts[DERIVED_KEY_INDEX].key, true, true),
], ],
vec![4], vec![TEST_NESTED_INVOKE],
); );
invoke_signed(&invoked_instruction, accounts, &[&["Lil'", "Bits"]])?; invoke(&invoked_instruction, accounts)?;
} else { } else {
info!("Last invoked"); info!("Last invoked");
assert!(accounts[DERIVED_KEY_INDEX].is_signer);
{ {
let mut data = accounts[INVOKED_ARGUMENT_INDEX].try_borrow_mut_data()?; let mut data = accounts[INVOKED_ARGUMENT_INDEX].try_borrow_mut_data()?;
for i in 0..10 { for i in 0..10 {

View File

@ -333,10 +333,10 @@ mod bpf {
let bank = Arc::new(Bank::new(&genesis_config)); let bank = Arc::new(Bank::new(&genesis_config));
let bank_client = BankClient::new_shared(&bank); let bank_client = BankClient::new_shared(&bank);
let program_id = load_bpf_program(&bank_client, &mint_keypair, program.0); let invoke_program_id = load_bpf_program(&bank_client, &mint_keypair, program.0);
let invoked_program_id = load_bpf_program(&bank_client, &mint_keypair, program.1); let invoked_program_id = load_bpf_program(&bank_client, &mint_keypair, program.1);
let account = Account::new(42, 100, &program_id); let account = Account::new(42, 100, &invoke_program_id);
let argument_keypair = Keypair::new(); let argument_keypair = Keypair::new();
bank.store_account(&argument_keypair.pubkey(), &account); bank.store_account(&argument_keypair.pubkey(), &account);
@ -344,9 +344,11 @@ mod bpf {
let invoked_argument_keypair = Keypair::new(); let invoked_argument_keypair = Keypair::new();
bank.store_account(&invoked_argument_keypair.pubkey(), &account); bank.store_account(&invoked_argument_keypair.pubkey(), &account);
let derived_key = let derived_key1 =
Pubkey::create_program_address(&["Lil'", "Bits"], &invoked_program_id).unwrap(); Pubkey::create_program_address(&["You pass butter"], &invoke_program_id).unwrap();
let derived_key2 = let derived_key2 =
Pubkey::create_program_address(&["Lil'", "Bits"], &invoked_program_id).unwrap();
let derived_key3 =
Pubkey::create_program_address(&["Gar Ma Nar Nar"], &invoked_program_id).unwrap(); Pubkey::create_program_address(&["Gar Ma Nar Nar"], &invoked_program_id).unwrap();
let account_metas = vec![ let account_metas = vec![
@ -356,11 +358,12 @@ mod bpf {
AccountMeta::new(invoked_argument_keypair.pubkey(), true), AccountMeta::new(invoked_argument_keypair.pubkey(), true),
AccountMeta::new_readonly(invoked_program_id, false), AccountMeta::new_readonly(invoked_program_id, false),
AccountMeta::new(argument_keypair.pubkey(), true), AccountMeta::new(argument_keypair.pubkey(), true),
AccountMeta::new(derived_key, false), AccountMeta::new(derived_key1, false),
AccountMeta::new_readonly(derived_key2, false), AccountMeta::new(derived_key2, false),
AccountMeta::new_readonly(derived_key3, false),
]; ];
let instruction = Instruction::new(program_id, &1u8, account_metas); let instruction = Instruction::new(invoke_program_id, &1u8, account_metas);
let message = Message::new(&[instruction]); let message = Message::new(&[instruction]);
assert!(bank_client assert!(bank_client

View File

@ -262,7 +262,9 @@ mod tests {
use std::{cell::RefCell, fs::File, io::Read, ops::Range, rc::Rc}; use std::{cell::RefCell, fs::File, io::Read, ops::Range, rc::Rc};
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct MockInvokeContext {} pub struct MockInvokeContext {
key: Pubkey,
}
impl InvokeContext for MockInvokeContext { impl InvokeContext for MockInvokeContext {
fn push(&mut self, _key: &Pubkey) -> Result<(), InstructionError> { fn push(&mut self, _key: &Pubkey) -> Result<(), InstructionError> {
Ok(()) Ok(())
@ -277,6 +279,9 @@ mod tests {
) -> Result<(), InstructionError> { ) -> Result<(), InstructionError> {
Ok(()) Ok(())
} }
fn get_caller(&self) -> Result<&Pubkey, InstructionError> {
Ok(&self.key)
}
} }
#[test] #[test]

View File

@ -693,8 +693,12 @@ fn call<'a>(
let instruction = syscall.translate_instruction(instruction_addr, ro_regions)?; let instruction = syscall.translate_instruction(instruction_addr, ro_regions)?;
let message = Message::new(&[instruction]); let message = Message::new(&[instruction]);
let program_id_index = message.instructions[0].program_id_index as usize; let callee_program_id_index = message.instructions[0].program_id_index as usize;
let program_id = message.account_keys[program_id_index]; let callee_program_id = message.account_keys[callee_program_id_index];
let caller_program_id = invoke_context
.get_caller()
.map_err(|err| SyscallError::InstructionError(err))?
.into();
let (accounts, refs) = syscall.translate_accounts( let (accounts, refs) = syscall.translate_accounts(
&message, &message,
account_infos_addr, account_infos_addr,
@ -703,7 +707,7 @@ fn call<'a>(
rw_regions, rw_regions,
)?; )?;
let signers = syscall.translate_signers( let signers = syscall.translate_signers(
&program_id, caller_program_id,
signers_seeds_addr, signers_seeds_addr,
signers_seeds_len as usize, signers_seeds_len as usize,
ro_regions, ro_regions,
@ -711,12 +715,12 @@ fn call<'a>(
// Process instruction // Process instruction
let program_account = (*accounts[program_id_index]).clone(); let program_account = (*accounts[callee_program_id_index]).clone();
if program_account.borrow().owner != bpf_loader::id() { if program_account.borrow().owner != bpf_loader::id() {
// Only BPF programs supported for now // Only BPF programs supported for now
return Err(SyscallError::ProgramNotSupported.into()); return Err(SyscallError::ProgramNotSupported.into());
} }
let executable_accounts = vec![(program_id, program_account)]; let executable_accounts = vec![(callee_program_id, program_account)];
#[allow(clippy::deref_addrof)] #[allow(clippy::deref_addrof)]
match MessageProcessor::process_cross_program_instruction( match MessageProcessor::process_cross_program_instruction(

View File

@ -229,6 +229,11 @@ impl InvokeContext for ThisInvokeContext {
None => Err(InstructionError::GenericError), // Should never happen None => Err(InstructionError::GenericError), // Should never happen
} }
} }
fn get_caller(&self) -> Result<&Pubkey, InstructionError> {
self.program_ids
.last()
.ok_or(InstructionError::GenericError)
}
} }
pub type ProcessInstruction = fn(&Pubkey, &[KeyedAccount], &[u8]) -> Result<(), InstructionError>; pub type ProcessInstruction = fn(&Pubkey, &[KeyedAccount], &[u8]) -> Result<(), InstructionError>;

View File

@ -157,4 +157,5 @@ pub trait InvokeContext {
signers: &[Pubkey], signers: &[Pubkey],
accounts: &[Rc<RefCell<Account>>], accounts: &[Rc<RefCell<Account>>],
) -> Result<(), InstructionError>; ) -> Result<(), InstructionError>;
fn get_caller(&self) -> Result<&Pubkey, InstructionError>;
} }