parent
5d96fcec63
commit
03abd3ddd7
|
@ -80,7 +80,7 @@ fn bench_program_alu(bencher: &mut Bencher) {
|
||||||
let mut invoke_context = MockInvokeContext::default();
|
let mut invoke_context = MockInvokeContext::default();
|
||||||
|
|
||||||
let elf = load_elf().unwrap();
|
let elf = load_elf().unwrap();
|
||||||
let (mut vm, _) = solana_bpf_loader_program::create_vm(&elf, &mut invoke_context).unwrap();
|
let (mut vm, _) = solana_bpf_loader_program::create_vm(&elf, &[], &mut invoke_context).unwrap();
|
||||||
|
|
||||||
println!("Interpreted:");
|
println!("Interpreted:");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -145,7 +145,6 @@ impl InvokeContext for MockInvokeContext {
|
||||||
&mut self,
|
&mut self,
|
||||||
_message: &Message,
|
_message: &Message,
|
||||||
_instruction: &CompiledInstruction,
|
_instruction: &CompiledInstruction,
|
||||||
_signers: &[Pubkey],
|
|
||||||
_accounts: &[Rc<RefCell<Account>>],
|
_accounts: &[Rc<RefCell<Account>>],
|
||||||
) -> Result<(), InstructionError> {
|
) -> Result<(), InstructionError> {
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -4,6 +4,10 @@
|
||||||
#include "../invoked/instruction.h"
|
#include "../invoked/instruction.h"
|
||||||
#include <solana_sdk.h>
|
#include <solana_sdk.h>
|
||||||
|
|
||||||
|
static const uint8_t TEST_SUCCESS = 1;
|
||||||
|
static const uint8_t TEST_PRIVILEGE_ESCALATION_SIGNER = 2;
|
||||||
|
static const uint8_t TEST_PRIVILEGE_ESCALATION_WRITABLE = 3;
|
||||||
|
|
||||||
static const int MINT_INDEX = 0;
|
static const int MINT_INDEX = 0;
|
||||||
static const int ARGUMENT_INDEX = 1;
|
static const int ARGUMENT_INDEX = 1;
|
||||||
static const int INVOKED_PROGRAM_INDEX = 2;
|
static const int INVOKED_PROGRAM_INDEX = 2;
|
||||||
|
@ -26,11 +30,14 @@ extern uint64_t entrypoint(const uint8_t *input) {
|
||||||
return ERROR_INVALID_ARGUMENT;
|
return ERROR_INVALID_ARGUMENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
switch (params.data[0]) {
|
||||||
|
case TEST_SUCCESS: {
|
||||||
sol_log("Call system program");
|
sol_log("Call system program");
|
||||||
{
|
{
|
||||||
sol_assert(*accounts[FROM_INDEX].lamports = 43);
|
sol_assert(*accounts[FROM_INDEX].lamports = 43);
|
||||||
sol_assert(*accounts[ARGUMENT_INDEX].lamports = 41);
|
sol_assert(*accounts[ARGUMENT_INDEX].lamports = 41);
|
||||||
SolAccountMeta arguments[] = {{accounts[FROM_INDEX].key, false, true},
|
SolAccountMeta arguments[] = {
|
||||||
|
{accounts[FROM_INDEX].key, false, true},
|
||||||
{accounts[ARGUMENT_INDEX].key, false, false}};
|
{accounts[ARGUMENT_INDEX].key, false, false}};
|
||||||
uint8_t data[] = {2, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0};
|
uint8_t data[] = {2, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0};
|
||||||
const SolInstruction instruction = {accounts[SYSTEM_PROGRAM_INDEX].key,
|
const SolInstruction instruction = {accounts[SYSTEM_PROGRAM_INDEX].key,
|
||||||
|
@ -97,9 +104,10 @@ extern uint64_t entrypoint(const uint8_t *input) {
|
||||||
{seed3, sol_strlen(seed3)}};
|
{seed3, sol_strlen(seed3)}};
|
||||||
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,
|
||||||
&instruction, accounts, SOL_ARRAY_SIZE(accounts),
|
SOL_ARRAY_SIZE(accounts),
|
||||||
signers_seeds, SOL_ARRAY_SIZE(signers_seeds)));
|
signers_seeds,
|
||||||
|
SOL_ARRAY_SIZE(signers_seeds)));
|
||||||
}
|
}
|
||||||
|
|
||||||
sol_log("Test readonly with writable account");
|
sol_log("Test readonly with writable account");
|
||||||
|
@ -130,7 +138,7 @@ extern uint64_t entrypoint(const uint8_t *input) {
|
||||||
arguments, SOL_ARRAY_SIZE(arguments),
|
arguments, SOL_ARRAY_SIZE(arguments),
|
||||||
data, SOL_ARRAY_SIZE(data)};
|
data, SOL_ARRAY_SIZE(data)};
|
||||||
|
|
||||||
sol_log("Fist invoke");
|
sol_log("First invoke");
|
||||||
sol_assert(SUCCESS ==
|
sol_assert(SUCCESS ==
|
||||||
sol_invoke(&instruction, accounts, SOL_ARRAY_SIZE(accounts)));
|
sol_invoke(&instruction, accounts, SOL_ARRAY_SIZE(accounts)));
|
||||||
sol_log("2nd invoke from first program");
|
sol_log("2nd invoke from first program");
|
||||||
|
@ -148,6 +156,43 @@ extern uint64_t entrypoint(const uint8_t *input) {
|
||||||
for (int i = 0; i < accounts[INVOKED_ARGUMENT_INDEX].data_len; i++) {
|
for (int i = 0; i < accounts[INVOKED_ARGUMENT_INDEX].data_len; i++) {
|
||||||
sol_assert(accounts[INVOKED_ARGUMENT_INDEX].data[i] == i);
|
sol_assert(accounts[INVOKED_ARGUMENT_INDEX].data[i] == i);
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TEST_PRIVILEGE_ESCALATION_SIGNER: {
|
||||||
|
sol_log("Test privilege escalation signer");
|
||||||
|
SolAccountMeta arguments[] = {
|
||||||
|
{accounts[DERIVED_KEY3_INDEX].key, false, false}};
|
||||||
|
uint8_t data[] = {TEST_VERIFY_PRIVILEGE_ESCALATION};
|
||||||
|
const SolInstruction instruction = {accounts[INVOKED_PROGRAM_INDEX].key,
|
||||||
|
arguments, SOL_ARRAY_SIZE(arguments),
|
||||||
|
data, SOL_ARRAY_SIZE(data)};
|
||||||
|
sol_assert(SUCCESS ==
|
||||||
|
sol_invoke(&instruction, accounts, SOL_ARRAY_SIZE(accounts)));
|
||||||
|
|
||||||
|
instruction.accounts[0].is_signer = true;
|
||||||
|
sol_assert(SUCCESS !=
|
||||||
|
sol_invoke(&instruction, accounts, SOL_ARRAY_SIZE(accounts)));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TEST_PRIVILEGE_ESCALATION_WRITABLE: {
|
||||||
|
sol_log("Test privilege escalation writable");
|
||||||
|
SolAccountMeta arguments[] = {
|
||||||
|
{accounts[DERIVED_KEY3_INDEX].key, false, false}};
|
||||||
|
uint8_t data[] = {TEST_VERIFY_PRIVILEGE_ESCALATION};
|
||||||
|
const SolInstruction instruction = {accounts[INVOKED_PROGRAM_INDEX].key,
|
||||||
|
arguments, SOL_ARRAY_SIZE(arguments),
|
||||||
|
data, SOL_ARRAY_SIZE(data)};
|
||||||
|
sol_assert(SUCCESS ==
|
||||||
|
sol_invoke(&instruction, accounts, SOL_ARRAY_SIZE(accounts)));
|
||||||
|
|
||||||
|
instruction.accounts[0].is_writable = true;
|
||||||
|
sol_assert(SUCCESS !=
|
||||||
|
sol_invoke(&instruction, accounts, SOL_ARRAY_SIZE(accounts)));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
sol_panic();
|
||||||
|
}
|
||||||
|
|
||||||
return SUCCESS;
|
return SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,9 +2,12 @@
|
||||||
* @brief Instruction definitions for the invoked program
|
* @brief Instruction definitions for the invoked program
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const int TEST_VERIFY_TRANSLATIONS = 0;
|
#include <solana_sdk.h>
|
||||||
const int TEST_RETURN_ERROR = 1;
|
|
||||||
const int TEST_DERIVED_SIGNERS = 2;
|
const uint8_t TEST_VERIFY_TRANSLATIONS = 0;
|
||||||
const int TEST_VERIFY_NESTED_SIGNERS = 3;
|
const uint8_t TEST_RETURN_ERROR = 1;
|
||||||
const int TEST_VERIFY_WRITER = 4;
|
const uint8_t TEST_DERIVED_SIGNERS = 2;
|
||||||
const int TEST_NESTED_INVOKE = 5;
|
const uint8_t TEST_VERIFY_NESTED_SIGNERS = 3;
|
||||||
|
const uint8_t TEST_VERIFY_WRITER = 4;
|
||||||
|
const uint8_t TEST_VERIFY_PRIVILEGE_ESCALATION = 5;
|
||||||
|
const uint8_t TEST_NESTED_INVOKE = 6;
|
||||||
|
|
|
@ -136,6 +136,9 @@ 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 TEST_VERIFY_PRIVILEGE_ESCALATION: {
|
||||||
|
sol_log("Success");
|
||||||
|
}
|
||||||
case TEST_NESTED_INVOKE: {
|
case TEST_NESTED_INVOKE: {
|
||||||
sol_log("invoke");
|
sol_log("invoke");
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,10 @@ use solana_sdk::{
|
||||||
system_instruction,
|
system_instruction,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const TEST_SUCCESS: u8 = 1;
|
||||||
|
const TEST_PRIVILEGE_ESCALATION_SIGNER: u8 = 2;
|
||||||
|
const TEST_PRIVILEGE_ESCALATION_WRITABLE: u8 = 3;
|
||||||
|
|
||||||
// const MINT_INDEX: usize = 0;
|
// const MINT_INDEX: usize = 0;
|
||||||
const ARGUMENT_INDEX: usize = 1;
|
const ARGUMENT_INDEX: usize = 1;
|
||||||
const INVOKED_PROGRAM_INDEX: usize = 2;
|
const INVOKED_PROGRAM_INDEX: usize = 2;
|
||||||
|
@ -32,16 +36,21 @@ entrypoint!(process_instruction);
|
||||||
fn process_instruction(
|
fn process_instruction(
|
||||||
_program_id: &Pubkey,
|
_program_id: &Pubkey,
|
||||||
accounts: &[AccountInfo],
|
accounts: &[AccountInfo],
|
||||||
_instruction_data: &[u8],
|
instruction_data: &[u8],
|
||||||
) -> ProgramResult {
|
) -> ProgramResult {
|
||||||
info!("invoke Rust program");
|
info!("invoke Rust program");
|
||||||
|
|
||||||
|
match instruction_data[0] {
|
||||||
|
TEST_SUCCESS => {
|
||||||
info!("Call system program");
|
info!("Call system program");
|
||||||
{
|
{
|
||||||
assert_eq!(accounts[FROM_INDEX].lamports(), 43);
|
assert_eq!(accounts[FROM_INDEX].lamports(), 43);
|
||||||
assert_eq!(accounts[ARGUMENT_INDEX].lamports(), 41);
|
assert_eq!(accounts[ARGUMENT_INDEX].lamports(), 41);
|
||||||
let instruction =
|
let instruction = system_instruction::transfer(
|
||||||
system_instruction::transfer(accounts[FROM_INDEX].key, accounts[ARGUMENT_INDEX].key, 1);
|
accounts[FROM_INDEX].key,
|
||||||
|
accounts[ARGUMENT_INDEX].key,
|
||||||
|
1,
|
||||||
|
);
|
||||||
invoke(&instruction, accounts)?;
|
invoke(&instruction, accounts)?;
|
||||||
assert_eq!(accounts[FROM_INDEX].lamports(), 42);
|
assert_eq!(accounts[FROM_INDEX].lamports(), 42);
|
||||||
assert_eq!(accounts[ARGUMENT_INDEX].lamports(), 42);
|
assert_eq!(accounts[ARGUMENT_INDEX].lamports(), 42);
|
||||||
|
@ -122,7 +131,7 @@ fn process_instruction(
|
||||||
**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;
|
||||||
|
|
||||||
info!("Fist invoke");
|
info!("First invoke");
|
||||||
let instruction = create_instruction(
|
let instruction = create_instruction(
|
||||||
*accounts[INVOKED_PROGRAM_INDEX].key,
|
*accounts[INVOKED_PROGRAM_INDEX].key,
|
||||||
&[
|
&[
|
||||||
|
@ -155,6 +164,39 @@ fn process_instruction(
|
||||||
assert_eq!(data[i as usize], i);
|
assert_eq!(data[i as usize], i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
TEST_PRIVILEGE_ESCALATION_SIGNER => {
|
||||||
|
info!("Test privilege escalation signer");
|
||||||
|
let mut invoked_instruction = create_instruction(
|
||||||
|
*accounts[INVOKED_PROGRAM_INDEX].key,
|
||||||
|
&[(accounts[DERIVED_KEY3_INDEX].key, false, false)],
|
||||||
|
vec![TEST_VERIFY_PRIVILEGE_ESCALATION],
|
||||||
|
);
|
||||||
|
invoke(&invoked_instruction, accounts)?;
|
||||||
|
|
||||||
|
invoked_instruction.accounts[0].is_signer = true;
|
||||||
|
assert_eq!(
|
||||||
|
invoke(&invoked_instruction, accounts),
|
||||||
|
Err(ProgramError::Custom(0x0b9f_0002))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
TEST_PRIVILEGE_ESCALATION_WRITABLE => {
|
||||||
|
info!("Test privilege escalation writable");
|
||||||
|
let mut invoked_instruction = create_instruction(
|
||||||
|
*accounts[INVOKED_PROGRAM_INDEX].key,
|
||||||
|
&[(accounts[DERIVED_KEY3_INDEX].key, false, false)],
|
||||||
|
vec![TEST_VERIFY_PRIVILEGE_ESCALATION],
|
||||||
|
);
|
||||||
|
invoke(&invoked_instruction, accounts)?;
|
||||||
|
|
||||||
|
invoked_instruction.accounts[0].is_writable = true;
|
||||||
|
assert_eq!(
|
||||||
|
invoke(&invoked_instruction, accounts),
|
||||||
|
Err(ProgramError::Custom(0x0b9f_0002))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_ => panic!(),
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,8 @@ pub const TEST_RETURN_ERROR: u8 = 1;
|
||||||
pub const TEST_DERIVED_SIGNERS: u8 = 2;
|
pub const TEST_DERIVED_SIGNERS: u8 = 2;
|
||||||
pub const TEST_VERIFY_NESTED_SIGNERS: u8 = 3;
|
pub const TEST_VERIFY_NESTED_SIGNERS: u8 = 3;
|
||||||
pub const TEST_VERIFY_WRITER: u8 = 4;
|
pub const TEST_VERIFY_WRITER: u8 = 4;
|
||||||
pub const TEST_NESTED_INVOKE: u8 = 5;
|
pub const TEST_VERIFY_PRIVILEGE_ESCALATION: u8 = 5;
|
||||||
|
pub const TEST_NESTED_INVOKE: u8 = 6;
|
||||||
|
|
||||||
pub fn create_instruction(
|
pub fn create_instruction(
|
||||||
program_id: Pubkey,
|
program_id: Pubkey,
|
||||||
|
|
|
@ -151,6 +151,9 @@ fn process_instruction(
|
||||||
|
|
||||||
assert!(!accounts[ARGUMENT_INDEX].is_writable);
|
assert!(!accounts[ARGUMENT_INDEX].is_writable);
|
||||||
}
|
}
|
||||||
|
TEST_VERIFY_PRIVILEGE_ESCALATION => {
|
||||||
|
info!("Success");
|
||||||
|
}
|
||||||
TEST_NESTED_INVOKE => {
|
TEST_NESTED_INVOKE => {
|
||||||
info!("nested invoke");
|
info!("nested invoke");
|
||||||
|
|
||||||
|
|
|
@ -309,6 +309,10 @@ mod bpf {
|
||||||
fn test_program_bpf_invoke() {
|
fn test_program_bpf_invoke() {
|
||||||
solana_logger::setup();
|
solana_logger::setup();
|
||||||
|
|
||||||
|
const TEST_SUCCESS: u8 = 1;
|
||||||
|
const TEST_PRIVILEGE_ESCALATION_SIGNER: u8 = 2;
|
||||||
|
const TEST_PRIVILEGE_ESCALATION_WRITABLE: u8 = 3;
|
||||||
|
|
||||||
let mut programs = Vec::new();
|
let mut programs = Vec::new();
|
||||||
#[cfg(feature = "bpf_c")]
|
#[cfg(feature = "bpf_c")]
|
||||||
{
|
{
|
||||||
|
@ -369,9 +373,11 @@ mod bpf {
|
||||||
AccountMeta::new(from_keypair.pubkey(), true),
|
AccountMeta::new(from_keypair.pubkey(), true),
|
||||||
];
|
];
|
||||||
|
|
||||||
let instruction = Instruction::new(invoke_program_id, &1u8, account_metas);
|
// success cases
|
||||||
let message = Message::new(&[instruction]);
|
|
||||||
|
|
||||||
|
let instruction =
|
||||||
|
Instruction::new(invoke_program_id, &TEST_SUCCESS, account_metas.clone());
|
||||||
|
let message = Message::new(&[instruction]);
|
||||||
assert!(bank_client
|
assert!(bank_client
|
||||||
.send_message(
|
.send_message(
|
||||||
&[
|
&[
|
||||||
|
@ -383,6 +389,52 @@ mod bpf {
|
||||||
message,
|
message,
|
||||||
)
|
)
|
||||||
.is_ok());
|
.is_ok());
|
||||||
|
|
||||||
|
// failure cases
|
||||||
|
|
||||||
|
let instruction = Instruction::new(
|
||||||
|
invoke_program_id,
|
||||||
|
&TEST_PRIVILEGE_ESCALATION_SIGNER,
|
||||||
|
account_metas.clone(),
|
||||||
|
);
|
||||||
|
let message = Message::new(&[instruction]);
|
||||||
|
assert_eq!(
|
||||||
|
bank_client
|
||||||
|
.send_message(
|
||||||
|
&[
|
||||||
|
&mint_keypair,
|
||||||
|
&argument_keypair,
|
||||||
|
&invoked_argument_keypair,
|
||||||
|
&from_keypair
|
||||||
|
],
|
||||||
|
message,
|
||||||
|
)
|
||||||
|
.unwrap_err()
|
||||||
|
.unwrap(),
|
||||||
|
TransactionError::InstructionError(0, InstructionError::Custom(194969602))
|
||||||
|
);
|
||||||
|
|
||||||
|
let instruction = Instruction::new(
|
||||||
|
invoke_program_id,
|
||||||
|
&TEST_PRIVILEGE_ESCALATION_WRITABLE,
|
||||||
|
account_metas.clone(),
|
||||||
|
);
|
||||||
|
let message = Message::new(&[instruction]);
|
||||||
|
assert_eq!(
|
||||||
|
bank_client
|
||||||
|
.send_message(
|
||||||
|
&[
|
||||||
|
&mint_keypair,
|
||||||
|
&argument_keypair,
|
||||||
|
&invoked_argument_keypair,
|
||||||
|
&from_keypair
|
||||||
|
],
|
||||||
|
message,
|
||||||
|
)
|
||||||
|
.unwrap_err()
|
||||||
|
.unwrap(),
|
||||||
|
TransactionError::InstructionError(0, InstructionError::Custom(194969602))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,6 +57,7 @@ impl UserDefinedError for BPFError {}
|
||||||
|
|
||||||
pub fn create_vm<'a>(
|
pub fn create_vm<'a>(
|
||||||
prog: &'a [u8],
|
prog: &'a [u8],
|
||||||
|
parameter_accounts: &'a [KeyedAccount<'a>],
|
||||||
invoke_context: &'a mut dyn InvokeContext,
|
invoke_context: &'a mut dyn InvokeContext,
|
||||||
) -> Result<(EbpfVm<'a, BPFError>, MemoryRegion), EbpfError<BPFError>> {
|
) -> Result<(EbpfVm<'a, BPFError>, MemoryRegion), EbpfError<BPFError>> {
|
||||||
let mut vm = EbpfVm::new(None)?;
|
let mut vm = EbpfVm::new(None)?;
|
||||||
|
@ -64,7 +65,7 @@ pub fn create_vm<'a>(
|
||||||
vm.set_max_instruction_count(100_000)?;
|
vm.set_max_instruction_count(100_000)?;
|
||||||
vm.set_elf(&prog)?;
|
vm.set_elf(&prog)?;
|
||||||
|
|
||||||
let heap_region = syscalls::register_syscalls(&mut vm, invoke_context)?;
|
let heap_region = syscalls::register_syscalls(&mut vm, parameter_accounts, invoke_context)?;
|
||||||
|
|
||||||
Ok((vm, heap_region))
|
Ok((vm, heap_region))
|
||||||
}
|
}
|
||||||
|
@ -182,7 +183,8 @@ pub fn process_instruction(
|
||||||
)?;
|
)?;
|
||||||
{
|
{
|
||||||
let program_account = program.try_account_ref_mut()?;
|
let program_account = program.try_account_ref_mut()?;
|
||||||
let (mut vm, heap_region) = match create_vm(&program_account.data, invoke_context) {
|
let (mut vm, heap_region) =
|
||||||
|
match create_vm(&program_account.data, ¶meter_accounts, invoke_context) {
|
||||||
Ok(info) => info,
|
Ok(info) => info,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
warn!("Failed to create BPF VM: {}", e);
|
warn!("Failed to create BPF VM: {}", e);
|
||||||
|
@ -274,7 +276,6 @@ mod tests {
|
||||||
&mut self,
|
&mut self,
|
||||||
_message: &Message,
|
_message: &Message,
|
||||||
_instruction: &CompiledInstruction,
|
_instruction: &CompiledInstruction,
|
||||||
_signers: &[Pubkey],
|
|
||||||
_accounts: &[Rc<RefCell<Account>>],
|
_accounts: &[Rc<RefCell<Account>>],
|
||||||
) -> Result<(), InstructionError> {
|
) -> Result<(), InstructionError> {
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -9,6 +9,7 @@ use solana_rbpf::{
|
||||||
use solana_runtime::{builtin_programs::get_builtin_programs, message_processor::MessageProcessor};
|
use solana_runtime::{builtin_programs::get_builtin_programs, message_processor::MessageProcessor};
|
||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
account::Account,
|
account::Account,
|
||||||
|
account::KeyedAccount,
|
||||||
account_info::AccountInfo,
|
account_info::AccountInfo,
|
||||||
bpf_loader,
|
bpf_loader,
|
||||||
entrypoint::SUCCESS,
|
entrypoint::SUCCESS,
|
||||||
|
@ -48,6 +49,8 @@ pub enum SyscallError {
|
||||||
ProgramNotSupported,
|
ProgramNotSupported,
|
||||||
#[error("{0}")]
|
#[error("{0}")]
|
||||||
InstructionError(InstructionError),
|
InstructionError(InstructionError),
|
||||||
|
#[error("Cross-program invocation with unauthorized signer or writable account")]
|
||||||
|
PrivilegeEscalation,
|
||||||
}
|
}
|
||||||
impl From<SyscallError> for EbpfError<BPFError> {
|
impl From<SyscallError> for EbpfError<BPFError> {
|
||||||
fn from(error: SyscallError) -> Self {
|
fn from(error: SyscallError) -> Self {
|
||||||
|
@ -69,6 +72,7 @@ const DEFAULT_HEAP_SIZE: usize = 32 * 1024;
|
||||||
|
|
||||||
pub fn register_syscalls<'a>(
|
pub fn register_syscalls<'a>(
|
||||||
vm: &mut EbpfVm<'a, BPFError>,
|
vm: &mut EbpfVm<'a, BPFError>,
|
||||||
|
callers_keyed_accounts: &'a [KeyedAccount<'a>],
|
||||||
invoke_context: &'a mut dyn InvokeContext,
|
invoke_context: &'a mut dyn InvokeContext,
|
||||||
) -> Result<MemoryRegion, EbpfError<BPFError>> {
|
) -> Result<MemoryRegion, EbpfError<BPFError>> {
|
||||||
// Syscall function common across languages
|
// Syscall function common across languages
|
||||||
|
@ -83,12 +87,14 @@ pub fn register_syscalls<'a>(
|
||||||
vm.register_syscall_with_context_ex(
|
vm.register_syscall_with_context_ex(
|
||||||
"sol_invoke_signed_c",
|
"sol_invoke_signed_c",
|
||||||
Box::new(SyscallProcessSolInstructionC {
|
Box::new(SyscallProcessSolInstructionC {
|
||||||
|
callers_keyed_accounts,
|
||||||
invoke_context: invoke_context.clone(),
|
invoke_context: invoke_context.clone(),
|
||||||
}),
|
}),
|
||||||
)?;
|
)?;
|
||||||
vm.register_syscall_with_context_ex(
|
vm.register_syscall_with_context_ex(
|
||||||
"sol_invoke_signed_rust",
|
"sol_invoke_signed_rust",
|
||||||
Box::new(SyscallProcessInstructionRust {
|
Box::new(SyscallProcessInstructionRust {
|
||||||
|
callers_keyed_accounts,
|
||||||
invoke_context: invoke_context.clone(),
|
invoke_context: invoke_context.clone(),
|
||||||
}),
|
}),
|
||||||
)?;
|
)?;
|
||||||
|
@ -301,6 +307,7 @@ pub type TranslatedAccounts<'a> = (Vec<Rc<RefCell<Account>>>, Vec<(&'a mut u64,
|
||||||
/// Implemented by language specific data structure translators
|
/// Implemented by language specific data structure translators
|
||||||
trait SyscallProcessInstruction<'a> {
|
trait SyscallProcessInstruction<'a> {
|
||||||
fn get_context_mut(&self) -> Result<RefMut<&'a mut dyn InvokeContext>, EbpfError<BPFError>>;
|
fn get_context_mut(&self) -> Result<RefMut<&'a mut dyn InvokeContext>, EbpfError<BPFError>>;
|
||||||
|
fn get_callers_keyed_accounts(&self) -> &'a [KeyedAccount<'a>];
|
||||||
fn translate_instruction(
|
fn translate_instruction(
|
||||||
&self,
|
&self,
|
||||||
addr: u64,
|
addr: u64,
|
||||||
|
@ -325,6 +332,7 @@ trait SyscallProcessInstruction<'a> {
|
||||||
|
|
||||||
/// Cross-program invocation called from Rust
|
/// Cross-program invocation called from Rust
|
||||||
pub struct SyscallProcessInstructionRust<'a> {
|
pub struct SyscallProcessInstructionRust<'a> {
|
||||||
|
callers_keyed_accounts: &'a [KeyedAccount<'a>],
|
||||||
invoke_context: Rc<RefCell<&'a mut dyn InvokeContext>>,
|
invoke_context: Rc<RefCell<&'a mut dyn InvokeContext>>,
|
||||||
}
|
}
|
||||||
impl<'a> SyscallProcessInstruction<'a> for SyscallProcessInstructionRust<'a> {
|
impl<'a> SyscallProcessInstruction<'a> for SyscallProcessInstructionRust<'a> {
|
||||||
|
@ -333,6 +341,9 @@ impl<'a> SyscallProcessInstruction<'a> for SyscallProcessInstructionRust<'a> {
|
||||||
.try_borrow_mut()
|
.try_borrow_mut()
|
||||||
.map_err(|_| SyscallError::InvokeContextBorrowFailed.into())
|
.map_err(|_| SyscallError::InvokeContextBorrowFailed.into())
|
||||||
}
|
}
|
||||||
|
fn get_callers_keyed_accounts(&self) -> &'a [KeyedAccount<'a>] {
|
||||||
|
self.callers_keyed_accounts
|
||||||
|
}
|
||||||
fn translate_instruction(
|
fn translate_instruction(
|
||||||
&self,
|
&self,
|
||||||
addr: u64,
|
addr: u64,
|
||||||
|
@ -519,6 +530,7 @@ struct SolSignerSeedsC {
|
||||||
|
|
||||||
/// Cross-program invocation called from C
|
/// Cross-program invocation called from C
|
||||||
pub struct SyscallProcessSolInstructionC<'a> {
|
pub struct SyscallProcessSolInstructionC<'a> {
|
||||||
|
callers_keyed_accounts: &'a [KeyedAccount<'a>],
|
||||||
invoke_context: Rc<RefCell<&'a mut dyn InvokeContext>>,
|
invoke_context: Rc<RefCell<&'a mut dyn InvokeContext>>,
|
||||||
}
|
}
|
||||||
impl<'a> SyscallProcessInstruction<'a> for SyscallProcessSolInstructionC<'a> {
|
impl<'a> SyscallProcessInstruction<'a> for SyscallProcessSolInstructionC<'a> {
|
||||||
|
@ -527,7 +539,9 @@ impl<'a> SyscallProcessInstruction<'a> for SyscallProcessSolInstructionC<'a> {
|
||||||
.try_borrow_mut()
|
.try_borrow_mut()
|
||||||
.map_err(|_| SyscallError::InvokeContextBorrowFailed.into())
|
.map_err(|_| SyscallError::InvokeContextBorrowFailed.into())
|
||||||
}
|
}
|
||||||
|
fn get_callers_keyed_accounts(&self) -> &'a [KeyedAccount<'a>] {
|
||||||
|
self.callers_keyed_accounts
|
||||||
|
}
|
||||||
fn translate_instruction(
|
fn translate_instruction(
|
||||||
&self,
|
&self,
|
||||||
addr: u64,
|
addr: u64,
|
||||||
|
@ -673,6 +687,44 @@ impl<'a> SyscallObject<BPFError> for SyscallProcessSolInstructionC<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn verify_instruction<'a>(
|
||||||
|
syscall: &dyn SyscallProcessInstruction<'a>,
|
||||||
|
instruction: &Instruction,
|
||||||
|
signers: &[Pubkey],
|
||||||
|
) -> Result<(), EbpfError<BPFError>> {
|
||||||
|
let callers_keyed_accounts = syscall.get_callers_keyed_accounts();
|
||||||
|
|
||||||
|
// Check for privilege escalation
|
||||||
|
for account in instruction.accounts.iter() {
|
||||||
|
let keyed_account = callers_keyed_accounts
|
||||||
|
.iter()
|
||||||
|
.find_map(|keyed_account| {
|
||||||
|
if &account.pubkey == keyed_account.unsigned_key() {
|
||||||
|
Some(keyed_account)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.ok_or(SyscallError::InstructionError(
|
||||||
|
InstructionError::MissingAccount,
|
||||||
|
))?;
|
||||||
|
// Readonly account cannot become writable
|
||||||
|
if account.is_writable && !keyed_account.is_writable() {
|
||||||
|
return Err(SyscallError::PrivilegeEscalation.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
if account.is_signer && // If message indicates account is signed
|
||||||
|
!( // one of the following needs to be true:
|
||||||
|
keyed_account.signer_key().is_some() // Signed in the parent instruction
|
||||||
|
|| signers.contains(&account.pubkey) // Signed by the program
|
||||||
|
) {
|
||||||
|
return Err(SyscallError::PrivilegeEscalation.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Call process instruction, common to both Rust and C
|
/// Call process instruction, common to both Rust and C
|
||||||
fn call<'a>(
|
fn call<'a>(
|
||||||
syscall: &mut dyn SyscallProcessInstruction<'a>,
|
syscall: &mut dyn SyscallProcessInstruction<'a>,
|
||||||
|
@ -689,12 +741,19 @@ fn call<'a>(
|
||||||
// Translate data passed from the VM
|
// Translate data passed from the VM
|
||||||
|
|
||||||
let instruction = syscall.translate_instruction(instruction_addr, ro_regions)?;
|
let instruction = syscall.translate_instruction(instruction_addr, ro_regions)?;
|
||||||
let message = Message::new_with_payer(&[instruction], None);
|
|
||||||
let callee_program_id_index = message.instructions[0].program_id_index as usize;
|
|
||||||
let callee_program_id = message.account_keys[callee_program_id_index];
|
|
||||||
let caller_program_id = invoke_context
|
let caller_program_id = invoke_context
|
||||||
.get_caller()
|
.get_caller()
|
||||||
.map_err(SyscallError::InstructionError)?;
|
.map_err(SyscallError::InstructionError)?;
|
||||||
|
let signers = syscall.translate_signers(
|
||||||
|
caller_program_id,
|
||||||
|
signers_seeds_addr,
|
||||||
|
signers_seeds_len as usize,
|
||||||
|
ro_regions,
|
||||||
|
)?;
|
||||||
|
verify_instruction(syscall, &instruction, &signers)?;
|
||||||
|
let message = Message::new_with_payer(&[instruction], None);
|
||||||
|
let callee_program_id_index = message.instructions[0].program_id_index as usize;
|
||||||
|
let callee_program_id = message.account_keys[callee_program_id_index];
|
||||||
let (accounts, refs) = syscall.translate_accounts(
|
let (accounts, refs) = syscall.translate_accounts(
|
||||||
&message,
|
&message,
|
||||||
account_infos_addr,
|
account_infos_addr,
|
||||||
|
@ -702,12 +761,6 @@ fn call<'a>(
|
||||||
ro_regions,
|
ro_regions,
|
||||||
rw_regions,
|
rw_regions,
|
||||||
)?;
|
)?;
|
||||||
let signers = syscall.translate_signers(
|
|
||||||
caller_program_id,
|
|
||||||
signers_seeds_addr,
|
|
||||||
signers_seeds_len as usize,
|
|
||||||
ro_regions,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
// Process instruction
|
// Process instruction
|
||||||
|
|
||||||
|
@ -725,7 +778,6 @@ fn call<'a>(
|
||||||
&message,
|
&message,
|
||||||
&executable_accounts,
|
&executable_accounts,
|
||||||
&accounts,
|
&accounts,
|
||||||
&signers,
|
|
||||||
*(&mut *invoke_context),
|
*(&mut *invoke_context),
|
||||||
) {
|
) {
|
||||||
Ok(()) => (),
|
Ok(()) => (),
|
||||||
|
|
|
@ -122,31 +122,6 @@ impl PreAccount {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn verify_cross_program(
|
|
||||||
&self,
|
|
||||||
is_writable: bool,
|
|
||||||
is_signer: bool,
|
|
||||||
signers: &[Pubkey],
|
|
||||||
program_id: &Pubkey,
|
|
||||||
rent: &Rent,
|
|
||||||
post: &Account,
|
|
||||||
) -> Result<(), InstructionError> {
|
|
||||||
// Readonly account cannot become writable
|
|
||||||
if is_writable && !self.is_writable {
|
|
||||||
return Err(InstructionError::WritableModified);
|
|
||||||
}
|
|
||||||
|
|
||||||
if is_signer && // If message indicates account is signed
|
|
||||||
!( // one of the following needs to be true:
|
|
||||||
self.is_signer // Signed in the original transaction
|
|
||||||
|| signers.contains(&self.key) // Signed by the program
|
|
||||||
) {
|
|
||||||
return Err(InstructionError::SignerModified);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.verify(program_id, rent, post)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn update(&mut self, account: &Account) {
|
pub fn update(&mut self, account: &Account) {
|
||||||
self.lamports = account.lamports;
|
self.lamports = account.lamports;
|
||||||
if self.data.len() != account.data.len() {
|
if self.data.len() != account.data.len() {
|
||||||
|
@ -213,7 +188,6 @@ impl InvokeContext for ThisInvokeContext {
|
||||||
&mut self,
|
&mut self,
|
||||||
message: &Message,
|
message: &Message,
|
||||||
instruction: &CompiledInstruction,
|
instruction: &CompiledInstruction,
|
||||||
signers: &[Pubkey],
|
|
||||||
accounts: &[Rc<RefCell<Account>>],
|
accounts: &[Rc<RefCell<Account>>],
|
||||||
) -> Result<(), InstructionError> {
|
) -> Result<(), InstructionError> {
|
||||||
match self.program_ids.last() {
|
match self.program_ids.last() {
|
||||||
|
@ -221,10 +195,9 @@ impl InvokeContext for ThisInvokeContext {
|
||||||
message,
|
message,
|
||||||
instruction,
|
instruction,
|
||||||
&mut self.pre_accounts,
|
&mut self.pre_accounts,
|
||||||
|
accounts,
|
||||||
key,
|
key,
|
||||||
&self.rent,
|
&self.rent,
|
||||||
signers,
|
|
||||||
accounts,
|
|
||||||
),
|
),
|
||||||
None => Err(InstructionError::GenericError), // Should never happen
|
None => Err(InstructionError::GenericError), // Should never happen
|
||||||
}
|
}
|
||||||
|
@ -353,13 +326,12 @@ impl MessageProcessor {
|
||||||
message: &Message,
|
message: &Message,
|
||||||
executable_accounts: &[(Pubkey, RefCell<Account>)],
|
executable_accounts: &[(Pubkey, RefCell<Account>)],
|
||||||
accounts: &[Rc<RefCell<Account>>],
|
accounts: &[Rc<RefCell<Account>>],
|
||||||
signers: &[Pubkey],
|
|
||||||
invoke_context: &mut dyn InvokeContext,
|
invoke_context: &mut dyn InvokeContext,
|
||||||
) -> Result<(), InstructionError> {
|
) -> Result<(), InstructionError> {
|
||||||
let instruction = &message.instructions[0];
|
let instruction = &message.instructions[0];
|
||||||
|
|
||||||
// Verify the calling program hasn't misbehaved
|
// Verify the calling program hasn't misbehaved
|
||||||
invoke_context.verify_and_update(message, instruction, signers, accounts)?;
|
invoke_context.verify_and_update(message, instruction, accounts)?;
|
||||||
|
|
||||||
// Construct keyed accounts
|
// Construct keyed accounts
|
||||||
let keyed_accounts =
|
let keyed_accounts =
|
||||||
|
@ -371,7 +343,7 @@ impl MessageProcessor {
|
||||||
self.process_instruction(&keyed_accounts, &instruction.data, invoke_context);
|
self.process_instruction(&keyed_accounts, &instruction.data, invoke_context);
|
||||||
if result.is_ok() {
|
if result.is_ok() {
|
||||||
// Verify the called program has not misbehaved
|
// Verify the called program has not misbehaved
|
||||||
result = invoke_context.verify_and_update(message, instruction, signers, accounts);
|
result = invoke_context.verify_and_update(message, instruction, accounts);
|
||||||
}
|
}
|
||||||
invoke_context.pop();
|
invoke_context.pop();
|
||||||
|
|
||||||
|
@ -419,7 +391,7 @@ impl MessageProcessor {
|
||||||
pre_accounts: &[PreAccount],
|
pre_accounts: &[PreAccount],
|
||||||
executable_accounts: &[(Pubkey, RefCell<Account>)],
|
executable_accounts: &[(Pubkey, RefCell<Account>)],
|
||||||
accounts: &[Rc<RefCell<Account>>],
|
accounts: &[Rc<RefCell<Account>>],
|
||||||
rent_collector: &RentCollector,
|
rent: &Rent,
|
||||||
) -> Result<(), InstructionError> {
|
) -> Result<(), InstructionError> {
|
||||||
// Verify all executable accounts have zero outstanding refs
|
// Verify all executable accounts have zero outstanding refs
|
||||||
Self::verify_account_references(executable_accounts)?;
|
Self::verify_account_references(executable_accounts)?;
|
||||||
|
@ -433,7 +405,7 @@ impl MessageProcessor {
|
||||||
let account = accounts[account_index]
|
let account = accounts[account_index]
|
||||||
.try_borrow_mut()
|
.try_borrow_mut()
|
||||||
.map_err(|_| InstructionError::AccountBorrowOutstanding)?;
|
.map_err(|_| InstructionError::AccountBorrowOutstanding)?;
|
||||||
pre_accounts[unique_index].verify(&program_id, &rent_collector.rent, &account)?;
|
pre_accounts[unique_index].verify(&program_id, rent, &account)?;
|
||||||
pre_sum += u128::from(pre_accounts[unique_index].lamports());
|
pre_sum += u128::from(pre_accounts[unique_index].lamports());
|
||||||
post_sum += u128::from(account.lamports);
|
post_sum += u128::from(account.lamports);
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -453,10 +425,9 @@ impl MessageProcessor {
|
||||||
message: &Message,
|
message: &Message,
|
||||||
instruction: &CompiledInstruction,
|
instruction: &CompiledInstruction,
|
||||||
pre_accounts: &mut [PreAccount],
|
pre_accounts: &mut [PreAccount],
|
||||||
|
accounts: &[Rc<RefCell<Account>>],
|
||||||
program_id: &Pubkey,
|
program_id: &Pubkey,
|
||||||
rent: &Rent,
|
rent: &Rent,
|
||||||
signers: &[Pubkey],
|
|
||||||
accounts: &[Rc<RefCell<Account>>],
|
|
||||||
) -> Result<(), InstructionError> {
|
) -> Result<(), InstructionError> {
|
||||||
// Verify the per-account instruction results
|
// Verify the per-account instruction results
|
||||||
let (mut pre_sum, mut post_sum) = (0_u128, 0_u128);
|
let (mut pre_sum, mut post_sum) = (0_u128, 0_u128);
|
||||||
|
@ -471,14 +442,7 @@ impl MessageProcessor {
|
||||||
.try_borrow_mut()
|
.try_borrow_mut()
|
||||||
.map_err(|_| InstructionError::AccountBorrowOutstanding)?;
|
.map_err(|_| InstructionError::AccountBorrowOutstanding)?;
|
||||||
|
|
||||||
pre_account.verify_cross_program(
|
pre_account.verify(&program_id, &rent, &account)?;
|
||||||
message.is_writable(account_index),
|
|
||||||
message.is_signer(account_index),
|
|
||||||
signers,
|
|
||||||
&program_id,
|
|
||||||
&rent,
|
|
||||||
&account,
|
|
||||||
)?;
|
|
||||||
pre_sum += u128::from(pre_account.lamports());
|
pre_sum += u128::from(pre_account.lamports());
|
||||||
post_sum += u128::from(account.lamports);
|
post_sum += u128::from(account.lamports);
|
||||||
|
|
||||||
|
@ -525,7 +489,7 @@ impl MessageProcessor {
|
||||||
&invoke_context.pre_accounts,
|
&invoke_context.pre_accounts,
|
||||||
executable_accounts,
|
executable_accounts,
|
||||||
accounts,
|
accounts,
|
||||||
rent_collector,
|
&rent_collector.rent,
|
||||||
)?;
|
)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -622,7 +586,6 @@ mod tests {
|
||||||
.verify_and_update(
|
.verify_and_update(
|
||||||
&message,
|
&message,
|
||||||
&message.instructions[0],
|
&message.instructions[0],
|
||||||
&[],
|
|
||||||
&accounts[not_owned_index..owned_index + 1],
|
&accounts[not_owned_index..owned_index + 1],
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -638,7 +601,6 @@ mod tests {
|
||||||
invoke_context.verify_and_update(
|
invoke_context.verify_and_update(
|
||||||
&message,
|
&message,
|
||||||
&message.instructions[0],
|
&message.instructions[0],
|
||||||
&[],
|
|
||||||
&accounts[not_owned_index..owned_index + 1],
|
&accounts[not_owned_index..owned_index + 1],
|
||||||
),
|
),
|
||||||
Err(InstructionError::ExternalAccountDataModified)
|
Err(InstructionError::ExternalAccountDataModified)
|
||||||
|
@ -685,23 +647,16 @@ mod tests {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Change<'a> {
|
struct Change {
|
||||||
program_id: Pubkey,
|
program_id: Pubkey,
|
||||||
message_is_writable: bool,
|
|
||||||
message_is_signer: bool,
|
|
||||||
signers: &'a [Pubkey],
|
|
||||||
rent: Rent,
|
rent: Rent,
|
||||||
pre: PreAccount,
|
pre: PreAccount,
|
||||||
post: Account,
|
post: Account,
|
||||||
}
|
}
|
||||||
impl<'a> Change<'a> {
|
impl Change {
|
||||||
pub fn new(owner: &Pubkey, program_id: &Pubkey) -> Self {
|
pub fn new(owner: &Pubkey, program_id: &Pubkey) -> Self {
|
||||||
Self {
|
Self {
|
||||||
// key: Pubkey::new_rand(),
|
|
||||||
program_id: *program_id,
|
program_id: *program_id,
|
||||||
message_is_writable: false,
|
|
||||||
message_is_signer: false,
|
|
||||||
signers: &[],
|
|
||||||
rent: Rent::default(),
|
rent: Rent::default(),
|
||||||
pre: PreAccount::new(
|
pre: PreAccount::new(
|
||||||
&Pubkey::new_rand(),
|
&Pubkey::new_rand(),
|
||||||
|
@ -721,26 +676,10 @@ mod tests {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn new_cross_program(owner: &Pubkey, program_id: &Pubkey, key: &Pubkey) -> Self {
|
|
||||||
let mut change = Change::new(owner, program_id);
|
|
||||||
change.pre.key = *key;
|
|
||||||
change
|
|
||||||
}
|
|
||||||
pub fn read_only(mut self) -> Self {
|
pub fn read_only(mut self) -> Self {
|
||||||
self.pre.is_writable = false;
|
self.pre.is_writable = false;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
pub fn writable(mut self, pre: bool, message_is_writable: bool) -> Self {
|
|
||||||
self.pre.is_writable = pre;
|
|
||||||
self.message_is_writable = message_is_writable;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
pub fn signer(mut self, pre: bool, message_is_signer: bool, signers: &'a [Pubkey]) -> Self {
|
|
||||||
self.pre.is_signer = pre;
|
|
||||||
self.message_is_signer = message_is_signer;
|
|
||||||
self.signers = signers;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
pub fn executable(mut self, pre: bool, post: bool) -> Self {
|
pub fn executable(mut self, pre: bool, post: bool) -> Self {
|
||||||
self.pre.is_executable = pre;
|
self.pre.is_executable = pre;
|
||||||
self.post.executable = post;
|
self.post.executable = post;
|
||||||
|
@ -768,16 +707,6 @@ mod tests {
|
||||||
pub fn verify(&self) -> Result<(), InstructionError> {
|
pub fn verify(&self) -> Result<(), InstructionError> {
|
||||||
self.pre.verify(&self.program_id, &self.rent, &self.post)
|
self.pre.verify(&self.program_id, &self.rent, &self.post)
|
||||||
}
|
}
|
||||||
pub fn verify_cross_program(&self) -> Result<(), InstructionError> {
|
|
||||||
self.pre.verify_cross_program(
|
|
||||||
self.message_is_writable,
|
|
||||||
self.message_is_signer,
|
|
||||||
self.signers,
|
|
||||||
&self.program_id,
|
|
||||||
&self.rent,
|
|
||||||
&self.post,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -940,59 +869,6 @@ mod tests {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_verify_account_changes_writable() {
|
|
||||||
let owner = Pubkey::new_rand();
|
|
||||||
let system_program_id = system_program::id();
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
Change::new(&owner, &system_program_id)
|
|
||||||
.writable(true, false)
|
|
||||||
.verify_cross_program(),
|
|
||||||
Ok(()),
|
|
||||||
"account can we changed to readonly"
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
Change::new(&owner, &system_program_id)
|
|
||||||
.writable(false, true)
|
|
||||||
.verify_cross_program(),
|
|
||||||
Err(InstructionError::WritableModified),
|
|
||||||
"account cannot be changed to writable"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_verify_account_changes_signer() {
|
|
||||||
let owner = Pubkey::new_rand();
|
|
||||||
let system_program_id = system_program::id();
|
|
||||||
let key = Pubkey::new_rand();
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
Change::new_cross_program(&owner, &system_program_id, &key)
|
|
||||||
.signer(false, true, &[key])
|
|
||||||
.verify_cross_program(),
|
|
||||||
Ok(()),
|
|
||||||
"account signed by a signer"
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
Change::new_cross_program(&owner, &system_program_id, &key)
|
|
||||||
.signer(false, true, &[])
|
|
||||||
.verify_cross_program(),
|
|
||||||
Err(InstructionError::SignerModified),
|
|
||||||
"account cannot be changed to signed if no signer"
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
Change::new_cross_program(&owner, &system_program_id, &key)
|
|
||||||
.signer(false, true, &[Pubkey::new_rand(), Pubkey::new_rand()])
|
|
||||||
.verify_cross_program(),
|
|
||||||
Err(InstructionError::SignerModified),
|
|
||||||
"account cannot be changed to signed if no signer exists"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_verify_account_changes_data_len() {
|
fn test_verify_account_changes_data_len() {
|
||||||
let alice_program_id = Pubkey::new_rand();
|
let alice_program_id = Pubkey::new_rand();
|
||||||
|
@ -1446,7 +1322,6 @@ mod tests {
|
||||||
&message,
|
&message,
|
||||||
&executable_accounts,
|
&executable_accounts,
|
||||||
&accounts,
|
&accounts,
|
||||||
&[],
|
|
||||||
&mut invoke_context,
|
&mut invoke_context,
|
||||||
),
|
),
|
||||||
Err(InstructionError::ExternalAccountDataModified)
|
Err(InstructionError::ExternalAccountDataModified)
|
||||||
|
@ -1474,7 +1349,6 @@ mod tests {
|
||||||
&message,
|
&message,
|
||||||
&executable_accounts,
|
&executable_accounts,
|
||||||
&accounts,
|
&accounts,
|
||||||
&[],
|
|
||||||
&mut invoke_context,
|
&mut invoke_context,
|
||||||
),
|
),
|
||||||
case.1
|
case.1
|
||||||
|
|
|
@ -154,7 +154,6 @@ pub trait InvokeContext {
|
||||||
&mut self,
|
&mut self,
|
||||||
message: &Message,
|
message: &Message,
|
||||||
instruction: &CompiledInstruction,
|
instruction: &CompiledInstruction,
|
||||||
signers: &[Pubkey],
|
|
||||||
accounts: &[Rc<RefCell<Account>>],
|
accounts: &[Rc<RefCell<Account>>],
|
||||||
) -> Result<(), InstructionError>;
|
) -> Result<(), InstructionError>;
|
||||||
fn get_caller(&self) -> Result<&Pubkey, InstructionError>;
|
fn get_caller(&self) -> Result<&Pubkey, InstructionError>;
|
||||||
|
|
|
@ -140,14 +140,6 @@ pub enum InstructionError {
|
||||||
#[error("Unsupported program id")]
|
#[error("Unsupported program id")]
|
||||||
UnsupportedProgramId,
|
UnsupportedProgramId,
|
||||||
|
|
||||||
/// Writable bit on account info changed, but shouldn't have
|
|
||||||
#[error("Writable bit on account info changed, but shouldn't have")]
|
|
||||||
WritableModified,
|
|
||||||
|
|
||||||
/// Signer bit on account info changed, but shouldn't have
|
|
||||||
#[error("Signer bit on account info changed, but shouldn't have")]
|
|
||||||
SignerModified,
|
|
||||||
|
|
||||||
/// Cross-program invocation call depth too deep
|
/// Cross-program invocation call depth too deep
|
||||||
#[error("Cross-program invocation call depth too deep")]
|
#[error("Cross-program invocation call depth too deep")]
|
||||||
CallDepth,
|
CallDepth,
|
||||||
|
|
Loading…
Reference in New Issue