Programs can only sign their accounts
This commit is contained in:
parent
b4b4d6b00d
commit
97e17f9b32
|
@ -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, ¶ms, SOL_ARRAY_SIZE(accounts))) {
|
if (!sol_deserialize(input, ¶ms, 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);
|
||||||
|
|
|
@ -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, ¶ms, 2));
|
static const int DERIVED_KEY2_INDEX = 2;
|
||||||
|
static const int DERIVED_KEY3_INDEX = 3;
|
||||||
|
sol_assert(sol_deserialize(input, ¶ms, 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, ¶ms, 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, ¶ms, 1));
|
sol_assert(sol_deserialize(input, ¶ms, 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, ¶ms, 2));
|
||||||
sol_assert(sol_deserialize(input, ¶ms, 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;
|
||||||
|
|
|
@ -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");
|
||||||
|
|
|
@ -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)],
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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]
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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>;
|
||||||
|
|
|
@ -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>;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue