Enforce an executable's rent exemption in the runtime (#9134)
This commit is contained in:
parent
974848310c
commit
130c0b484d
|
@ -21,7 +21,6 @@ use solana_sdk::{
|
||||||
program_utils::DecodeError,
|
program_utils::DecodeError,
|
||||||
program_utils::{is_executable, limited_deserialize, next_keyed_account},
|
program_utils::{is_executable, limited_deserialize, next_keyed_account},
|
||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
sysvar::rent,
|
|
||||||
};
|
};
|
||||||
use std::{io::prelude::*, mem};
|
use std::{io::prelude::*, mem};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
@ -229,7 +228,6 @@ pub fn process_instruction(
|
||||||
LoaderInstruction::Finalize => {
|
LoaderInstruction::Finalize => {
|
||||||
let mut keyed_accounts_iter = keyed_accounts.iter();
|
let mut keyed_accounts_iter = keyed_accounts.iter();
|
||||||
let program = next_keyed_account(&mut keyed_accounts_iter)?;
|
let program = next_keyed_account(&mut keyed_accounts_iter)?;
|
||||||
let rent = next_keyed_account(&mut keyed_accounts_iter)?;
|
|
||||||
|
|
||||||
if program.signer_key().is_none() {
|
if program.signer_key().is_none() {
|
||||||
warn!("key[0] did not sign the transaction");
|
warn!("key[0] did not sign the transaction");
|
||||||
|
@ -241,8 +239,6 @@ pub fn process_instruction(
|
||||||
return Err(InstructionError::InvalidAccountData);
|
return Err(InstructionError::InvalidAccountData);
|
||||||
}
|
}
|
||||||
|
|
||||||
rent::verify_rent_exemption(&program, &rent)?;
|
|
||||||
|
|
||||||
program.try_account_ref_mut()?.executable = true;
|
program.try_account_ref_mut()?.executable = true;
|
||||||
info!("Finalize: account {:?}", program.signer_key().unwrap());
|
info!("Finalize: account {:?}", program.signer_key().unwrap());
|
||||||
}
|
}
|
||||||
|
@ -254,8 +250,8 @@ pub fn process_instruction(
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use solana_sdk::account::Account;
|
use solana_sdk::{account::Account, rent::Rent};
|
||||||
use std::{cell::RefCell, fs::File, io::Read};
|
use std::{fs::File, io::Read};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[should_panic(expected = "ExceededMaxInstructions(10)")]
|
#[should_panic(expected = "ExceededMaxInstructions(10)")]
|
||||||
|
@ -324,14 +320,13 @@ mod tests {
|
||||||
fn test_bpf_loader_finalize() {
|
fn test_bpf_loader_finalize() {
|
||||||
let program_id = Pubkey::new_rand();
|
let program_id = Pubkey::new_rand();
|
||||||
let program_key = Pubkey::new_rand();
|
let program_key = Pubkey::new_rand();
|
||||||
let rent_key = rent::id();
|
|
||||||
let mut file = File::open("test_elfs/noop.so").expect("file open failed");
|
let mut file = File::open("test_elfs/noop.so").expect("file open failed");
|
||||||
let mut elf = Vec::new();
|
let mut elf = Vec::new();
|
||||||
let rent = rent::Rent::default();
|
let rent = Rent::default();
|
||||||
file.read_to_end(&mut elf).unwrap();
|
file.read_to_end(&mut elf).unwrap();
|
||||||
let program_account = Account::new_ref(rent.minimum_balance(elf.len()), 0, &program_id);
|
let program_account = Account::new_ref(rent.minimum_balance(elf.len()), 0, &program_id);
|
||||||
program_account.borrow_mut().data = elf;
|
program_account.borrow_mut().data = elf;
|
||||||
let mut keyed_accounts = vec![KeyedAccount::new(&program_key, false, &program_account)];
|
let keyed_accounts = vec![KeyedAccount::new(&program_key, false, &program_account)];
|
||||||
let instruction_data = bincode::serialize(&LoaderInstruction::Finalize).unwrap();
|
let instruction_data = bincode::serialize(&LoaderInstruction::Finalize).unwrap();
|
||||||
|
|
||||||
// Case: Empty keyed accounts
|
// Case: Empty keyed accounts
|
||||||
|
@ -340,9 +335,6 @@ mod tests {
|
||||||
process_instruction(&bpf_loader::id(), &vec![], &instruction_data)
|
process_instruction(&bpf_loader::id(), &vec![], &instruction_data)
|
||||||
);
|
);
|
||||||
|
|
||||||
let rent_account = RefCell::new(rent::create_account(1, &rent));
|
|
||||||
keyed_accounts.push(KeyedAccount::new(&rent_key, false, &rent_account));
|
|
||||||
|
|
||||||
// Case: Not signed
|
// Case: Not signed
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Err(InstructionError::MissingRequiredSignature),
|
Err(InstructionError::MissingRequiredSignature),
|
||||||
|
@ -350,10 +342,7 @@ mod tests {
|
||||||
);
|
);
|
||||||
|
|
||||||
// Case: Finalize
|
// Case: Finalize
|
||||||
let keyed_accounts = vec![
|
let keyed_accounts = vec![KeyedAccount::new(&program_key, true, &program_account)];
|
||||||
KeyedAccount::new(&program_key, true, &program_account),
|
|
||||||
KeyedAccount::new(&rent_key, false, &rent_account),
|
|
||||||
];
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Ok(()),
|
Ok(()),
|
||||||
process_instruction(&bpf_loader::id(), &keyed_accounts, &instruction_data)
|
process_instruction(&bpf_loader::id(), &keyed_accounts, &instruction_data)
|
||||||
|
@ -364,10 +353,7 @@ mod tests {
|
||||||
|
|
||||||
// Case: Finalize
|
// Case: Finalize
|
||||||
program_account.borrow_mut().data[0] = 0; // bad elf
|
program_account.borrow_mut().data[0] = 0; // bad elf
|
||||||
let keyed_accounts = vec![
|
let keyed_accounts = vec![KeyedAccount::new(&program_key, true, &program_account)];
|
||||||
KeyedAccount::new(&program_key, true, &program_account),
|
|
||||||
KeyedAccount::new(&rent_key, false, &rent_account),
|
|
||||||
];
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Err(InstructionError::InvalidAccountData),
|
Err(InstructionError::InvalidAccountData),
|
||||||
process_instruction(&bpf_loader::id(), &keyed_accounts, &instruction_data)
|
process_instruction(&bpf_loader::id(), &keyed_accounts, &instruction_data)
|
||||||
|
|
|
@ -13,7 +13,6 @@ use solana_sdk::{
|
||||||
program_utils::DecodeError,
|
program_utils::DecodeError,
|
||||||
program_utils::{is_executable, limited_deserialize, next_keyed_account},
|
program_utils::{is_executable, limited_deserialize, next_keyed_account},
|
||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
sysvar::rent,
|
|
||||||
};
|
};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use types::{
|
use types::{
|
||||||
|
@ -73,7 +72,6 @@ pub enum MoveLoaderInstruction {
|
||||||
/// bit of the Account
|
/// bit of the Account
|
||||||
///
|
///
|
||||||
/// * key[0] - the account to prepare for execution
|
/// * key[0] - the account to prepare for execution
|
||||||
/// * key[1] - rent sysvar account
|
|
||||||
///
|
///
|
||||||
/// The transaction must be signed by key[0]
|
/// The transaction must be signed by key[0]
|
||||||
Finalize,
|
Finalize,
|
||||||
|
@ -301,15 +299,12 @@ impl MoveProcessor {
|
||||||
pub fn do_finalize(keyed_accounts: &[KeyedAccount]) -> Result<(), InstructionError> {
|
pub fn do_finalize(keyed_accounts: &[KeyedAccount]) -> Result<(), InstructionError> {
|
||||||
let mut keyed_accounts_iter = keyed_accounts.iter();
|
let mut keyed_accounts_iter = keyed_accounts.iter();
|
||||||
let finalized = next_keyed_account(&mut keyed_accounts_iter)?;
|
let finalized = next_keyed_account(&mut keyed_accounts_iter)?;
|
||||||
let rent = next_keyed_account(&mut keyed_accounts_iter)?;
|
|
||||||
|
|
||||||
if finalized.signer_key().is_none() {
|
if finalized.signer_key().is_none() {
|
||||||
debug!("Error: account to finalize did not sign the transaction");
|
debug!("Error: account to finalize did not sign the transaction");
|
||||||
return Err(InstructionError::MissingRequiredSignature);
|
return Err(InstructionError::MissingRequiredSignature);
|
||||||
}
|
}
|
||||||
|
|
||||||
rent::verify_rent_exemption(&finalized, &rent)?;
|
|
||||||
|
|
||||||
match finalized.state()? {
|
match finalized.state()? {
|
||||||
LibraAccountState::CompiledScript(string) => {
|
LibraAccountState::CompiledScript(string) => {
|
||||||
let script: Script = serde_json::from_str(&string).map_err(map_json_error)?;
|
let script: Script = serde_json::from_str(&string).map_err(map_json_error)?;
|
||||||
|
@ -479,8 +474,6 @@ impl MoveProcessor {
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use solana_sdk::account::Account;
|
use solana_sdk::account::Account;
|
||||||
use solana_sdk::rent::Rent;
|
|
||||||
use solana_sdk::sysvar::rent;
|
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
|
|
||||||
const BIG_ENOUGH: usize = 10_000;
|
const BIG_ENOUGH: usize = 10_000;
|
||||||
|
@ -518,11 +511,8 @@ mod tests {
|
||||||
let code = "main() { return; }";
|
let code = "main() { return; }";
|
||||||
let sender_address = AccountAddress::default();
|
let sender_address = AccountAddress::default();
|
||||||
let script = LibraAccount::create_script(&sender_address, code, vec![]);
|
let script = LibraAccount::create_script(&sender_address, code, vec![]);
|
||||||
let rent_id = rent::id();
|
|
||||||
let rent_account = RefCell::new(rent::create_account(1, &Rent::free()));
|
|
||||||
let keyed_accounts = vec![
|
let keyed_accounts = vec![
|
||||||
KeyedAccount::new(&script.key, true, &script.account),
|
KeyedAccount::new(&script.key, true, &script.account),
|
||||||
KeyedAccount::new(&rent_id, false, &rent_account),
|
|
||||||
];
|
];
|
||||||
MoveProcessor::do_finalize(&keyed_accounts).unwrap();
|
MoveProcessor::do_finalize(&keyed_accounts).unwrap();
|
||||||
let _ = MoveProcessor::deserialize_verified_script(&script.account.borrow().data).unwrap();
|
let _ = MoveProcessor::deserialize_verified_script(&script.account.borrow().data).unwrap();
|
||||||
|
@ -561,11 +551,8 @@ mod tests {
|
||||||
let script = LibraAccount::create_script(&sender_address, code, vec![]);
|
let script = LibraAccount::create_script(&sender_address, code, vec![]);
|
||||||
let genesis = LibraAccount::create_genesis(1_000_000_000);
|
let genesis = LibraAccount::create_genesis(1_000_000_000);
|
||||||
|
|
||||||
let rent_id = rent::id();
|
|
||||||
let rent_account = RefCell::new(rent::create_account(1, &Rent::free()));
|
|
||||||
let keyed_accounts = vec![
|
let keyed_accounts = vec![
|
||||||
KeyedAccount::new(&script.key, true, &script.account),
|
KeyedAccount::new(&script.key, true, &script.account),
|
||||||
KeyedAccount::new(&rent_id, false, &rent_account),
|
|
||||||
];
|
];
|
||||||
|
|
||||||
MoveProcessor::do_finalize(&keyed_accounts).unwrap();
|
MoveProcessor::do_finalize(&keyed_accounts).unwrap();
|
||||||
|
@ -591,11 +578,8 @@ mod tests {
|
||||||
}
|
}
|
||||||
";
|
";
|
||||||
let module = LibraAccount::create_module(code, vec![]);
|
let module = LibraAccount::create_module(code, vec![]);
|
||||||
let rent_id = rent::id();
|
|
||||||
let rent_account = RefCell::new(rent::create_account(1, &Rent::free()));
|
|
||||||
let keyed_accounts = vec![
|
let keyed_accounts = vec![
|
||||||
KeyedAccount::new(&module.key, true, &module.account),
|
KeyedAccount::new(&module.key, true, &module.account),
|
||||||
KeyedAccount::new(&rent_id, false, &rent_account),
|
|
||||||
];
|
];
|
||||||
keyed_accounts[0]
|
keyed_accounts[0]
|
||||||
.account
|
.account
|
||||||
|
@ -620,11 +604,8 @@ mod tests {
|
||||||
let script = LibraAccount::create_script(&sender_address, code, vec![]);
|
let script = LibraAccount::create_script(&sender_address, code, vec![]);
|
||||||
let genesis = LibraAccount::create_genesis(1_000_000_000);
|
let genesis = LibraAccount::create_genesis(1_000_000_000);
|
||||||
|
|
||||||
let rent_id = rent::id();
|
|
||||||
let rent_account = RefCell::new(rent::create_account(1, &Rent::free()));
|
|
||||||
let keyed_accounts = vec![
|
let keyed_accounts = vec![
|
||||||
KeyedAccount::new(&script.key, true, &script.account),
|
KeyedAccount::new(&script.key, true, &script.account),
|
||||||
KeyedAccount::new(&rent_id, false, &rent_account),
|
|
||||||
];
|
];
|
||||||
|
|
||||||
MoveProcessor::do_finalize(&keyed_accounts).unwrap();
|
MoveProcessor::do_finalize(&keyed_accounts).unwrap();
|
||||||
|
@ -695,11 +676,8 @@ mod tests {
|
||||||
let script = LibraAccount::create_script(&genesis.address, code, vec![]);
|
let script = LibraAccount::create_script(&genesis.address, code, vec![]);
|
||||||
let payee = LibraAccount::create_unallocated(BIG_ENOUGH);
|
let payee = LibraAccount::create_unallocated(BIG_ENOUGH);
|
||||||
|
|
||||||
let rent_id = rent::id();
|
|
||||||
let rent_account = RefCell::new(rent::create_account(1, &Rent::free()));
|
|
||||||
let keyed_accounts = vec![
|
let keyed_accounts = vec![
|
||||||
KeyedAccount::new(&script.key, true, &script.account),
|
KeyedAccount::new(&script.key, true, &script.account),
|
||||||
KeyedAccount::new(&rent_id, false, &rent_account),
|
|
||||||
];
|
];
|
||||||
|
|
||||||
MoveProcessor::do_finalize(&keyed_accounts).unwrap();
|
MoveProcessor::do_finalize(&keyed_accounts).unwrap();
|
||||||
|
@ -753,11 +731,8 @@ mod tests {
|
||||||
);
|
);
|
||||||
let module = LibraAccount::create_module(&code, vec![]);
|
let module = LibraAccount::create_module(&code, vec![]);
|
||||||
|
|
||||||
let rent_id = rent::id();
|
|
||||||
let rent_account = RefCell::new(rent::create_account(1, &Rent::free()));
|
|
||||||
let keyed_accounts = vec![
|
let keyed_accounts = vec![
|
||||||
KeyedAccount::new(&module.key, true, &module.account),
|
KeyedAccount::new(&module.key, true, &module.account),
|
||||||
KeyedAccount::new(&rent_id, false, &rent_account),
|
|
||||||
];
|
];
|
||||||
keyed_accounts[0]
|
keyed_accounts[0]
|
||||||
.account
|
.account
|
||||||
|
@ -799,11 +774,8 @@ mod tests {
|
||||||
);
|
);
|
||||||
let payee = LibraAccount::create_unallocated(BIG_ENOUGH);
|
let payee = LibraAccount::create_unallocated(BIG_ENOUGH);
|
||||||
|
|
||||||
let rent_id = rent::id();
|
|
||||||
let rent_account = RefCell::new(rent::create_account(1, &Rent::free()));
|
|
||||||
let keyed_accounts = vec![
|
let keyed_accounts = vec![
|
||||||
KeyedAccount::new(&script.key, true, &script.account),
|
KeyedAccount::new(&script.key, true, &script.account),
|
||||||
KeyedAccount::new(&rent_id, false, &rent_account),
|
|
||||||
];
|
];
|
||||||
|
|
||||||
MoveProcessor::do_finalize(&keyed_accounts).unwrap();
|
MoveProcessor::do_finalize(&keyed_accounts).unwrap();
|
||||||
|
@ -849,11 +821,8 @@ mod tests {
|
||||||
let script = LibraAccount::create_script(&genesis.address.clone(), code, vec![]);
|
let script = LibraAccount::create_script(&genesis.address.clone(), code, vec![]);
|
||||||
let payee = LibraAccount::create_unallocated(BIG_ENOUGH);
|
let payee = LibraAccount::create_unallocated(BIG_ENOUGH);
|
||||||
|
|
||||||
let rent_id = rent::id();
|
|
||||||
let rent_account = RefCell::new(rent::create_account(1, &Rent::free()));
|
|
||||||
let keyed_accounts = vec![
|
let keyed_accounts = vec![
|
||||||
KeyedAccount::new(&script.key, true, &script.account),
|
KeyedAccount::new(&script.key, true, &script.account),
|
||||||
KeyedAccount::new(&rent_id, false, &rent_account),
|
|
||||||
];
|
];
|
||||||
|
|
||||||
MoveProcessor::do_finalize(&keyed_accounts).unwrap();
|
MoveProcessor::do_finalize(&keyed_accounts).unwrap();
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
extern crate test;
|
extern crate test;
|
||||||
|
|
||||||
use log::*;
|
use log::*;
|
||||||
use solana_runtime::message_processor::PreAccount;
|
use solana_runtime::{message_processor::PreAccount, rent_collector::RentCollector};
|
||||||
use solana_sdk::{account::Account, pubkey::Pubkey};
|
use solana_sdk::{account::Account, pubkey::Pubkey};
|
||||||
use test::Bencher;
|
use test::Bencher;
|
||||||
|
|
||||||
|
@ -15,11 +15,12 @@ fn bench_verify_account_changes_data(bencher: &mut Bencher) {
|
||||||
let non_owner = Pubkey::new_rand();
|
let non_owner = Pubkey::new_rand();
|
||||||
let pre = PreAccount::new(&Account::new(0, BUFSIZE, &owner), true, &owner);
|
let pre = PreAccount::new(&Account::new(0, BUFSIZE, &owner), true, &owner);
|
||||||
let post = Account::new(0, BUFSIZE, &owner);
|
let post = Account::new(0, BUFSIZE, &owner);
|
||||||
assert_eq!(pre.verify(&owner, &post), Ok(()));
|
assert_eq!(pre.verify(&owner, &RentCollector::default(), &post), Ok(()));
|
||||||
|
|
||||||
// this one should be faster
|
// this one should be faster
|
||||||
bencher.iter(|| {
|
bencher.iter(|| {
|
||||||
pre.verify(&owner, &post).unwrap();
|
pre.verify(&owner, &RentCollector::default(), &post)
|
||||||
|
.unwrap();
|
||||||
});
|
});
|
||||||
let summary = bencher.bench(|_bencher| {}).unwrap();
|
let summary = bencher.bench(|_bencher| {}).unwrap();
|
||||||
info!("data no change by owner: {} ns/iter", summary.median);
|
info!("data no change by owner: {} ns/iter", summary.median);
|
||||||
|
@ -32,7 +33,8 @@ fn bench_verify_account_changes_data(bencher: &mut Bencher) {
|
||||||
let summary = bencher.bench(|_bencher| {}).unwrap();
|
let summary = bencher.bench(|_bencher| {}).unwrap();
|
||||||
info!("data compare {} ns/iter", summary.median);
|
info!("data compare {} ns/iter", summary.median);
|
||||||
bencher.iter(|| {
|
bencher.iter(|| {
|
||||||
pre.verify(&non_owner, &post).unwrap();
|
pre.verify(&non_owner, &RentCollector::default(), &post)
|
||||||
|
.unwrap();
|
||||||
});
|
});
|
||||||
let summary = bencher.bench(|_bencher| {}).unwrap();
|
let summary = bencher.bench(|_bencher| {}).unwrap();
|
||||||
info!("data no change by non owner: {} ns/iter", summary.median);
|
info!("data no change by non owner: {} ns/iter", summary.median);
|
||||||
|
|
|
@ -1375,6 +1375,7 @@ impl Bank {
|
||||||
tx.message(),
|
tx.message(),
|
||||||
&loader_refcells,
|
&loader_refcells,
|
||||||
&account_refcells,
|
&account_refcells,
|
||||||
|
&self.rent_collector,
|
||||||
);
|
);
|
||||||
|
|
||||||
Self::from_refcells(accounts, loaders, account_refcells, loader_refcells);
|
Self::from_refcells(accounts, loaders, account_refcells, loader_refcells);
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::{native_loader, system_instruction_processor};
|
use crate::{native_loader, rent_collector::RentCollector, system_instruction_processor};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
account::{create_keyed_readonly_accounts, Account, KeyedAccount},
|
account::{create_keyed_readonly_accounts, Account, KeyedAccount},
|
||||||
|
@ -66,7 +66,12 @@ impl PreAccount {
|
||||||
|| is_executable
|
|| is_executable
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn verify(&self, program_id: &Pubkey, post: &Account) -> Result<(), InstructionError> {
|
pub fn verify(
|
||||||
|
&self,
|
||||||
|
program_id: &Pubkey,
|
||||||
|
rent_collector: &RentCollector,
|
||||||
|
post: &Account,
|
||||||
|
) -> Result<(), InstructionError> {
|
||||||
// Only the owner of the account may change owner and
|
// Only the owner of the account may change owner and
|
||||||
// only if the account is writable and
|
// only if the account is writable and
|
||||||
// only if the data is zero-initialized or empty
|
// only if the data is zero-initialized or empty
|
||||||
|
@ -120,13 +125,20 @@ impl PreAccount {
|
||||||
}
|
}
|
||||||
|
|
||||||
// executable is one-way (false->true) and only the account owner may set it.
|
// executable is one-way (false->true) and only the account owner may set it.
|
||||||
if self.executable != post.executable
|
if self.executable != post.executable {
|
||||||
&& (!self.is_writable // line coverage used to get branch coverage
|
if !rent_collector
|
||||||
|
.rent
|
||||||
|
.is_exempt(post.lamports, post.data.len())
|
||||||
|
{
|
||||||
|
return Err(InstructionError::ExecutableAccountNotRentExempt);
|
||||||
|
}
|
||||||
|
if !self.is_writable // line coverage used to get branch coverage
|
||||||
|| self.executable // line coverage used to get branch coverage
|
|| self.executable // line coverage used to get branch coverage
|
||||||
|| *program_id != self.owner)
|
|| *program_id != self.owner
|
||||||
{
|
{
|
||||||
return Err(InstructionError::ExecutableModified);
|
return Err(InstructionError::ExecutableModified);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// No one modifies rent_epoch (yet).
|
// No one modifies rent_epoch (yet).
|
||||||
if self.rent_epoch != post.rent_epoch {
|
if self.rent_epoch != post.rent_epoch {
|
||||||
|
@ -156,7 +168,6 @@ pub struct MessageProcessor {
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
symbol_cache: SymbolCache,
|
symbol_cache: SymbolCache,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for MessageProcessor {
|
impl Default for MessageProcessor {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
let instruction_processors: Vec<(Pubkey, ProcessInstruction)> = vec![(
|
let instruction_processors: Vec<(Pubkey, ProcessInstruction)> = vec![(
|
||||||
|
@ -170,7 +181,6 @@ impl Default for MessageProcessor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MessageProcessor {
|
impl MessageProcessor {
|
||||||
/// Add a static entrypoint to intercept instructions before the dynamic loader.
|
/// Add a static entrypoint to intercept instructions before the dynamic loader.
|
||||||
pub fn add_instruction_processor(
|
pub fn add_instruction_processor(
|
||||||
|
@ -275,6 +285,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,
|
||||||
) -> Result<(), InstructionError> {
|
) -> Result<(), InstructionError> {
|
||||||
// Verify all accounts have zero outstanding refs
|
// Verify all accounts have zero outstanding refs
|
||||||
Self::verify_account_references(executable_accounts, accounts)?;
|
Self::verify_account_references(executable_accounts, accounts)?;
|
||||||
|
@ -285,7 +296,7 @@ impl MessageProcessor {
|
||||||
let program_id = instruction.program_id(&message.account_keys);
|
let program_id = instruction.program_id(&message.account_keys);
|
||||||
let mut work = |unique_index: usize, account_index: usize| {
|
let mut work = |unique_index: usize, account_index: usize| {
|
||||||
let account = accounts[account_index].borrow();
|
let account = accounts[account_index].borrow();
|
||||||
pre_accounts[unique_index].verify(&program_id, &account)?;
|
pre_accounts[unique_index].verify(&program_id, rent_collector, &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(())
|
||||||
|
@ -310,6 +321,7 @@ impl MessageProcessor {
|
||||||
instruction: &CompiledInstruction,
|
instruction: &CompiledInstruction,
|
||||||
executable_accounts: &[(Pubkey, RefCell<Account>)],
|
executable_accounts: &[(Pubkey, RefCell<Account>)],
|
||||||
accounts: &[Rc<RefCell<Account>>],
|
accounts: &[Rc<RefCell<Account>>],
|
||||||
|
rent_collector: &RentCollector,
|
||||||
) -> Result<(), InstructionError> {
|
) -> Result<(), InstructionError> {
|
||||||
let pre_accounts = Self::create_pre_accounts(message, instruction, accounts);
|
let pre_accounts = Self::create_pre_accounts(message, instruction, accounts);
|
||||||
self.process_instruction(message, instruction, executable_accounts, accounts)?;
|
self.process_instruction(message, instruction, executable_accounts, accounts)?;
|
||||||
|
@ -319,6 +331,7 @@ impl MessageProcessor {
|
||||||
&pre_accounts,
|
&pre_accounts,
|
||||||
executable_accounts,
|
executable_accounts,
|
||||||
accounts,
|
accounts,
|
||||||
|
rent_collector,
|
||||||
)?;
|
)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -331,6 +344,7 @@ impl MessageProcessor {
|
||||||
message: &Message,
|
message: &Message,
|
||||||
loaders: &[Vec<(Pubkey, RefCell<Account>)>],
|
loaders: &[Vec<(Pubkey, RefCell<Account>)>],
|
||||||
accounts: &[Rc<RefCell<Account>>],
|
accounts: &[Rc<RefCell<Account>>],
|
||||||
|
rent_collector: &RentCollector,
|
||||||
) -> Result<(), TransactionError> {
|
) -> Result<(), TransactionError> {
|
||||||
for (instruction_index, instruction) in message.instructions.iter().enumerate() {
|
for (instruction_index, instruction) in message.instructions.iter().enumerate() {
|
||||||
let executable_index = message
|
let executable_index = message
|
||||||
|
@ -338,7 +352,13 @@ impl MessageProcessor {
|
||||||
.ok_or(TransactionError::InvalidAccountIndex)?;
|
.ok_or(TransactionError::InvalidAccountIndex)?;
|
||||||
let executable_accounts = &loaders[executable_index];
|
let executable_accounts = &loaders[executable_index];
|
||||||
|
|
||||||
self.execute_instruction(message, instruction, executable_accounts, accounts)
|
self.execute_instruction(
|
||||||
|
message,
|
||||||
|
instruction,
|
||||||
|
executable_accounts,
|
||||||
|
accounts,
|
||||||
|
rent_collector,
|
||||||
|
)
|
||||||
.map_err(|err| TransactionError::InstructionError(instruction_index as u8, err))?;
|
.map_err(|err| TransactionError::InstructionError(instruction_index as u8, err))?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -410,8 +430,11 @@ mod tests {
|
||||||
post: &Pubkey,
|
post: &Pubkey,
|
||||||
is_writable: bool,
|
is_writable: bool,
|
||||||
) -> Result<(), InstructionError> {
|
) -> Result<(), InstructionError> {
|
||||||
PreAccount::new(&Account::new(0, 0, pre), is_writable, ix)
|
PreAccount::new(&Account::new(0, 0, pre), is_writable, ix).verify(
|
||||||
.verify(ix, &Account::new(0, 0, post))
|
ix,
|
||||||
|
&RentCollector::default(),
|
||||||
|
&Account::new(0, 0, post),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
let system_program_id = system_program::id();
|
let system_program_id = system_program::id();
|
||||||
|
@ -448,7 +471,6 @@ mod tests {
|
||||||
Err(InstructionError::ModifiedProgramId),
|
Err(InstructionError::ModifiedProgramId),
|
||||||
"system program should not be able to change the account owner of a non-system account"
|
"system program should not be able to change the account owner of a non-system account"
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
change_owner(
|
change_owner(
|
||||||
&mallory_program_id,
|
&mallory_program_id,
|
||||||
|
@ -459,7 +481,6 @@ mod tests {
|
||||||
Ok(()),
|
Ok(()),
|
||||||
"mallory should be able to change the account owner, if she leaves clear data"
|
"mallory should be able to change the account owner, if she leaves clear data"
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
PreAccount::new(
|
PreAccount::new(
|
||||||
&Account::new_data(0, &[42], &mallory_program_id).unwrap(),
|
&Account::new_data(0, &[42], &mallory_program_id).unwrap(),
|
||||||
|
@ -468,6 +489,7 @@ mod tests {
|
||||||
)
|
)
|
||||||
.verify(
|
.verify(
|
||||||
&mallory_program_id,
|
&mallory_program_id,
|
||||||
|
&RentCollector::default(),
|
||||||
&Account::new_data(0, &[0], &alice_program_id).unwrap(),
|
&Account::new_data(0, &[0], &alice_program_id).unwrap(),
|
||||||
),
|
),
|
||||||
Ok(()),
|
Ok(()),
|
||||||
|
@ -481,6 +503,7 @@ mod tests {
|
||||||
)
|
)
|
||||||
.verify(
|
.verify(
|
||||||
&mallory_program_id,
|
&mallory_program_id,
|
||||||
|
&RentCollector::default(),
|
||||||
&Account::new_data(0, &[42], &alice_program_id).unwrap(),
|
&Account::new_data(0, &[42], &alice_program_id).unwrap(),
|
||||||
),
|
),
|
||||||
Err(InstructionError::ModifiedProgramId),
|
Err(InstructionError::ModifiedProgramId),
|
||||||
|
@ -505,6 +528,7 @@ mod tests {
|
||||||
pre: PreAccount::new(
|
pre: PreAccount::new(
|
||||||
&Account {
|
&Account {
|
||||||
owner: *owner,
|
owner: *owner,
|
||||||
|
lamports: std::u64::MAX,
|
||||||
data: vec![],
|
data: vec![],
|
||||||
..Account::default()
|
..Account::default()
|
||||||
},
|
},
|
||||||
|
@ -513,6 +537,7 @@ mod tests {
|
||||||
),
|
),
|
||||||
post: Account {
|
post: Account {
|
||||||
owner: *owner,
|
owner: *owner,
|
||||||
|
lamports: std::u64::MAX,
|
||||||
..Account::default()
|
..Account::default()
|
||||||
},
|
},
|
||||||
program_id: *program_id,
|
program_id: *program_id,
|
||||||
|
@ -539,7 +564,8 @@ mod tests {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
pub fn verify(self) -> Result<(), InstructionError> {
|
pub fn verify(self) -> Result<(), InstructionError> {
|
||||||
self.pre.verify(&self.program_id, &self.post)
|
self.pre
|
||||||
|
.verify(&self.program_id, &RentCollector::default(), &self.post)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -623,6 +649,25 @@ mod tests {
|
||||||
Err(InstructionError::ExecutableLamportChange),
|
Err(InstructionError::ExecutableLamportChange),
|
||||||
"owner should not be able to subtract lamports once marked executable"
|
"owner should not be able to subtract lamports once marked executable"
|
||||||
);
|
);
|
||||||
|
let data = vec![1; 100];
|
||||||
|
let min_lamports = RentCollector::default().rent.minimum_balance(data.len());
|
||||||
|
assert_eq!(
|
||||||
|
Change::new(&owner, &owner)
|
||||||
|
.executable(false, true)
|
||||||
|
.lamports(0, min_lamports)
|
||||||
|
.data(data.clone(), data.clone())
|
||||||
|
.verify(),
|
||||||
|
Ok(()),
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
Change::new(&owner, &owner)
|
||||||
|
.executable(false, true)
|
||||||
|
.lamports(0, min_lamports - 1)
|
||||||
|
.data(data.clone(), data.clone())
|
||||||
|
.verify(),
|
||||||
|
Err(InstructionError::ExecutableAccountNotRentExempt),
|
||||||
|
"owner should not be able to change an account's data once its marked executable"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -635,6 +680,7 @@ mod tests {
|
||||||
)
|
)
|
||||||
.verify(
|
.verify(
|
||||||
&system_program::id(),
|
&system_program::id(),
|
||||||
|
&RentCollector::default(),
|
||||||
&Account::new_data(0, &[0, 0], &system_program::id()).unwrap()
|
&Account::new_data(0, &[0, 0], &system_program::id()).unwrap()
|
||||||
),
|
),
|
||||||
Ok(()),
|
Ok(()),
|
||||||
|
@ -648,7 +694,7 @@ mod tests {
|
||||||
true,
|
true,
|
||||||
&system_program::id(),
|
&system_program::id(),
|
||||||
).verify(
|
).verify(
|
||||||
&system_program::id(),
|
&system_program::id(), &RentCollector::default(),
|
||||||
&Account::new_data(0, &[0, 0], &alice_program_id).unwrap(),
|
&Account::new_data(0, &[0, 0], &alice_program_id).unwrap(),
|
||||||
),
|
),
|
||||||
Err(InstructionError::AccountDataSizeChanged),
|
Err(InstructionError::AccountDataSizeChanged),
|
||||||
|
@ -659,7 +705,6 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_verify_account_changes_data() {
|
fn test_verify_account_changes_data() {
|
||||||
let alice_program_id = Pubkey::new_rand();
|
let alice_program_id = Pubkey::new_rand();
|
||||||
|
|
||||||
let change_data =
|
let change_data =
|
||||||
|program_id: &Pubkey, is_writable: bool| -> Result<(), InstructionError> {
|
|program_id: &Pubkey, is_writable: bool| -> Result<(), InstructionError> {
|
||||||
let pre = PreAccount::new(
|
let pre = PreAccount::new(
|
||||||
|
@ -668,7 +713,7 @@ mod tests {
|
||||||
&program_id,
|
&program_id,
|
||||||
);
|
);
|
||||||
let post = Account::new_data(0, &[42], &alice_program_id).unwrap();
|
let post = Account::new_data(0, &[42], &alice_program_id).unwrap();
|
||||||
pre.verify(&program_id, &post)
|
pre.verify(&program_id, &RentCollector::default(), &post)
|
||||||
};
|
};
|
||||||
|
|
||||||
let mallory_program_id = Pubkey::new_rand();
|
let mallory_program_id = Pubkey::new_rand();
|
||||||
|
@ -694,6 +739,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_verify_account_changes_rent_epoch() {
|
fn test_verify_account_changes_rent_epoch() {
|
||||||
let alice_program_id = Pubkey::new_rand();
|
let alice_program_id = Pubkey::new_rand();
|
||||||
|
let rent_collector = RentCollector::default();
|
||||||
let pre = PreAccount::new(
|
let pre = PreAccount::new(
|
||||||
&Account::new(0, 0, &alice_program_id),
|
&Account::new(0, 0, &alice_program_id),
|
||||||
false,
|
false,
|
||||||
|
@ -702,14 +748,14 @@ mod tests {
|
||||||
let mut post = Account::new(0, 0, &alice_program_id);
|
let mut post = Account::new(0, 0, &alice_program_id);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
pre.verify(&system_program::id(), &post),
|
pre.verify(&system_program::id(), &rent_collector, &post),
|
||||||
Ok(()),
|
Ok(()),
|
||||||
"nothing changed!"
|
"nothing changed!"
|
||||||
);
|
);
|
||||||
|
|
||||||
post.rent_epoch += 1;
|
post.rent_epoch += 1;
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
pre.verify(&system_program::id(), &post),
|
pre.verify(&system_program::id(), &rent_collector, &post),
|
||||||
Err(InstructionError::RentEpochModified),
|
Err(InstructionError::RentEpochModified),
|
||||||
"no one touches rent_epoch"
|
"no one touches rent_epoch"
|
||||||
);
|
);
|
||||||
|
@ -728,7 +774,7 @@ mod tests {
|
||||||
|
|
||||||
// positive test of this capability
|
// positive test of this capability
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
pre.verify(&alice_program_id, &post),
|
pre.verify(&alice_program_id, &RentCollector::default(), &post),
|
||||||
Ok(()),
|
Ok(()),
|
||||||
"alice should be able to deduct lamports and give the account to bob if the data is zeroed",
|
"alice should be able to deduct lamports and give the account to bob if the data is zeroed",
|
||||||
);
|
);
|
||||||
|
@ -737,6 +783,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_verify_account_changes_lamports() {
|
fn test_verify_account_changes_lamports() {
|
||||||
let alice_program_id = Pubkey::new_rand();
|
let alice_program_id = Pubkey::new_rand();
|
||||||
|
let rent_collector = RentCollector::default();
|
||||||
let pre = PreAccount::new(
|
let pre = PreAccount::new(
|
||||||
&Account::new(42, 0, &alice_program_id),
|
&Account::new(42, 0, &alice_program_id),
|
||||||
false,
|
false,
|
||||||
|
@ -745,7 +792,7 @@ mod tests {
|
||||||
let post = Account::new(0, 0, &alice_program_id);
|
let post = Account::new(0, 0, &alice_program_id);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
pre.verify(&system_program::id(), &post),
|
pre.verify(&system_program::id(), &rent_collector, &post),
|
||||||
Err(InstructionError::ExternalAccountLamportSpend),
|
Err(InstructionError::ExternalAccountLamportSpend),
|
||||||
"debit should fail, even if system program"
|
"debit should fail, even if system program"
|
||||||
);
|
);
|
||||||
|
@ -757,7 +804,7 @@ mod tests {
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
pre.verify(&alice_program_id, &post,),
|
pre.verify(&alice_program_id, &rent_collector, &post),
|
||||||
Err(InstructionError::ReadonlyLamportChange),
|
Err(InstructionError::ReadonlyLamportChange),
|
||||||
"debit should fail, even if owning program"
|
"debit should fail, even if owning program"
|
||||||
);
|
);
|
||||||
|
@ -769,7 +816,7 @@ mod tests {
|
||||||
);
|
);
|
||||||
let post = Account::new(0, 0, &system_program::id());
|
let post = Account::new(0, 0, &system_program::id());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
pre.verify(&system_program::id(), &post),
|
pre.verify(&system_program::id(), &rent_collector, &post),
|
||||||
Err(InstructionError::ModifiedProgramId),
|
Err(InstructionError::ModifiedProgramId),
|
||||||
"system program can't debit the account unless it was the pre.owner"
|
"system program can't debit the account unless it was the pre.owner"
|
||||||
);
|
);
|
||||||
|
@ -781,7 +828,7 @@ mod tests {
|
||||||
);
|
);
|
||||||
let post = Account::new(0, 0, &alice_program_id);
|
let post = Account::new(0, 0, &alice_program_id);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
pre.verify(&system_program::id(), &post),
|
pre.verify(&system_program::id(), &rent_collector, &post),
|
||||||
Ok(()),
|
Ok(()),
|
||||||
"system can spend (and change owner)"
|
"system can spend (and change owner)"
|
||||||
);
|
);
|
||||||
|
@ -789,6 +836,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_verify_account_changes_data_size_changed() {
|
fn test_verify_account_changes_data_size_changed() {
|
||||||
|
let rent_collector = RentCollector::default();
|
||||||
let alice_program_id = Pubkey::new_rand();
|
let alice_program_id = Pubkey::new_rand();
|
||||||
let pre = PreAccount::new(
|
let pre = PreAccount::new(
|
||||||
&Account::new_data(42, &[0], &alice_program_id).unwrap(),
|
&Account::new_data(42, &[0], &alice_program_id).unwrap(),
|
||||||
|
@ -797,7 +845,7 @@ mod tests {
|
||||||
);
|
);
|
||||||
let post = Account::new_data(42, &[0, 0], &alice_program_id).unwrap();
|
let post = Account::new_data(42, &[0, 0], &alice_program_id).unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
pre.verify(&system_program::id(), &post),
|
pre.verify(&system_program::id(), &rent_collector, &post),
|
||||||
Err(InstructionError::AccountDataSizeChanged),
|
Err(InstructionError::AccountDataSizeChanged),
|
||||||
"system program should not be able to change another program's account data size"
|
"system program should not be able to change another program's account data size"
|
||||||
);
|
);
|
||||||
|
@ -807,7 +855,7 @@ mod tests {
|
||||||
&alice_program_id,
|
&alice_program_id,
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
pre.verify(&alice_program_id, &post),
|
pre.verify(&alice_program_id, &rent_collector, &post),
|
||||||
Err(InstructionError::AccountDataSizeChanged),
|
Err(InstructionError::AccountDataSizeChanged),
|
||||||
"non-system programs cannot change their data size"
|
"non-system programs cannot change their data size"
|
||||||
);
|
);
|
||||||
|
@ -817,7 +865,7 @@ mod tests {
|
||||||
&system_program::id(),
|
&system_program::id(),
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
pre.verify(&system_program::id(), &post),
|
pre.verify(&system_program::id(), &rent_collector, &post),
|
||||||
Ok(()),
|
Ok(()),
|
||||||
"system program should be able to change acount data size"
|
"system program should be able to change acount data size"
|
||||||
);
|
);
|
||||||
|
@ -857,6 +905,7 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
let mock_system_program_id = Pubkey::new(&[2u8; 32]);
|
let mock_system_program_id = Pubkey::new(&[2u8; 32]);
|
||||||
|
let rent_collector = RentCollector::default();
|
||||||
let mut message_processor = MessageProcessor::default();
|
let mut message_processor = MessageProcessor::default();
|
||||||
message_processor
|
message_processor
|
||||||
.add_instruction_processor(mock_system_program_id, mock_system_process_instruction);
|
.add_instruction_processor(mock_system_program_id, mock_system_process_instruction);
|
||||||
|
@ -883,7 +932,8 @@ mod tests {
|
||||||
account_metas.clone(),
|
account_metas.clone(),
|
||||||
)]);
|
)]);
|
||||||
|
|
||||||
let result = message_processor.process_message(&message, &loaders, &accounts);
|
let result =
|
||||||
|
message_processor.process_message(&message, &loaders, &accounts, &rent_collector);
|
||||||
assert_eq!(result, Ok(()));
|
assert_eq!(result, Ok(()));
|
||||||
assert_eq!(accounts[0].borrow().lamports, 100);
|
assert_eq!(accounts[0].borrow().lamports, 100);
|
||||||
assert_eq!(accounts[1].borrow().lamports, 0);
|
assert_eq!(accounts[1].borrow().lamports, 0);
|
||||||
|
@ -894,7 +944,8 @@ mod tests {
|
||||||
account_metas.clone(),
|
account_metas.clone(),
|
||||||
)]);
|
)]);
|
||||||
|
|
||||||
let result = message_processor.process_message(&message, &loaders, &accounts);
|
let result =
|
||||||
|
message_processor.process_message(&message, &loaders, &accounts, &rent_collector);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
result,
|
result,
|
||||||
Err(TransactionError::InstructionError(
|
Err(TransactionError::InstructionError(
|
||||||
|
@ -909,7 +960,8 @@ mod tests {
|
||||||
account_metas,
|
account_metas,
|
||||||
)]);
|
)]);
|
||||||
|
|
||||||
let result = message_processor.process_message(&message, &loaders, &accounts);
|
let result =
|
||||||
|
message_processor.process_message(&message, &loaders, &accounts, &rent_collector);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
result,
|
result,
|
||||||
Err(TransactionError::InstructionError(
|
Err(TransactionError::InstructionError(
|
||||||
|
@ -976,6 +1028,7 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
let mock_program_id = Pubkey::new(&[2u8; 32]);
|
let mock_program_id = Pubkey::new(&[2u8; 32]);
|
||||||
|
let rent_collector = RentCollector::default();
|
||||||
let mut message_processor = MessageProcessor::default();
|
let mut message_processor = MessageProcessor::default();
|
||||||
message_processor
|
message_processor
|
||||||
.add_instruction_processor(mock_program_id, mock_system_process_instruction);
|
.add_instruction_processor(mock_program_id, mock_system_process_instruction);
|
||||||
|
@ -1005,7 +1058,8 @@ mod tests {
|
||||||
&MockSystemInstruction::BorrowFail,
|
&MockSystemInstruction::BorrowFail,
|
||||||
account_metas.clone(),
|
account_metas.clone(),
|
||||||
)]);
|
)]);
|
||||||
let result = message_processor.process_message(&message, &loaders, &accounts);
|
let result =
|
||||||
|
message_processor.process_message(&message, &loaders, &accounts, &rent_collector);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
result,
|
result,
|
||||||
Err(TransactionError::InstructionError(
|
Err(TransactionError::InstructionError(
|
||||||
|
@ -1020,7 +1074,8 @@ mod tests {
|
||||||
&MockSystemInstruction::MultiBorrowMut,
|
&MockSystemInstruction::MultiBorrowMut,
|
||||||
account_metas.clone(),
|
account_metas.clone(),
|
||||||
)]);
|
)]);
|
||||||
let result = message_processor.process_message(&message, &loaders, &accounts);
|
let result =
|
||||||
|
message_processor.process_message(&message, &loaders, &accounts, &rent_collector);
|
||||||
assert_eq!(result, Ok(()));
|
assert_eq!(result, Ok(()));
|
||||||
|
|
||||||
// Do work on the same account but at different location in keyed_accounts[]
|
// Do work on the same account but at different location in keyed_accounts[]
|
||||||
|
@ -1032,7 +1087,8 @@ mod tests {
|
||||||
},
|
},
|
||||||
account_metas,
|
account_metas,
|
||||||
)]);
|
)]);
|
||||||
let result = message_processor.process_message(&message, &loaders, &accounts);
|
let result =
|
||||||
|
message_processor.process_message(&message, &loaders, &accounts, &rent_collector);
|
||||||
assert_eq!(result, Ok(()));
|
assert_eq!(result, Ok(()));
|
||||||
assert_eq!(accounts[0].borrow().lamports, 80);
|
assert_eq!(accounts[0].borrow().lamports, 80);
|
||||||
assert_eq!(accounts[1].borrow().lamports, 20);
|
assert_eq!(accounts[1].borrow().lamports, 20);
|
||||||
|
|
|
@ -130,6 +130,10 @@ pub enum InstructionError {
|
||||||
/// Executable account's lamports modified
|
/// Executable account's lamports modified
|
||||||
#[error("instruction changed the balance of a executable account")]
|
#[error("instruction changed the balance of a executable account")]
|
||||||
ExecutableLamportChange,
|
ExecutableLamportChange,
|
||||||
|
|
||||||
|
/// Executable accounts must be rent exempt
|
||||||
|
#[error("executable accounts must be rent exempt")]
|
||||||
|
ExecutableAccountNotRentExempt,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InstructionError {
|
impl InstructionError {
|
||||||
|
|
Loading…
Reference in New Issue