Reorganize message processor in prep for cross-program-invocation (#8338)

This commit is contained in:
Jack May
2020-02-21 11:30:00 -08:00
committed by GitHub
parent 3f04226864
commit 0e6aca5a7e
4 changed files with 213 additions and 265 deletions

View File

@ -1359,7 +1359,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "memmap" name = "memmap"
version = "0.6.2" version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
@ -2599,10 +2599,12 @@ dependencies = [
"byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", "num-derive 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
"solana-logger 0.24.0", "solana-logger 0.24.0",
"solana-sdk 0.24.0", "solana-sdk 0.24.0",
"solana_rbpf 0.1.20", "solana_rbpf 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)",
"thiserror 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
@ -2691,10 +2693,10 @@ name = "solana-move-loader-program"
version = "0.24.0" version = "0.24.0"
dependencies = [ dependencies = [
"bincode 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "bincode 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"indexmap 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "indexmap 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
"num-derive 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_bytes 0.11.2 (registry+https://github.com/rust-lang/crates.io-index)", "serde_bytes 0.11.2 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
@ -2712,6 +2714,7 @@ dependencies = [
"solana_libra_vm_cache_map 0.0.1-sol4 (registry+https://github.com/rust-lang/crates.io-index)", "solana_libra_vm_cache_map 0.0.1-sol4 (registry+https://github.com/rust-lang/crates.io-index)",
"solana_libra_vm_runtime 0.0.1-sol4 (registry+https://github.com/rust-lang/crates.io-index)", "solana_libra_vm_runtime 0.0.1-sol4 (registry+https://github.com/rust-lang/crates.io-index)",
"solana_libra_vm_runtime_types 0.0.1-sol4 (registry+https://github.com/rust-lang/crates.io-index)", "solana_libra_vm_runtime_types 0.0.1-sol4 (registry+https://github.com/rust-lang/crates.io-index)",
"thiserror 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
@ -2735,12 +2738,13 @@ dependencies = [
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
"libloading 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", "libloading 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
"memmap 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", "memmap 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"num-derive 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
"rayon 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "rayon 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.46 (registry+https://github.com/rust-lang/crates.io-index)",
"solana-bpf-loader-program 0.24.0", "solana-bpf-loader-program 0.24.0",
"solana-logger 0.24.0", "solana-logger 0.24.0",
"solana-measure 0.24.0", "solana-measure 0.24.0",
@ -2752,6 +2756,7 @@ dependencies = [
"solana-vote-program 0.24.0", "solana-vote-program 0.24.0",
"sys-info 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)", "sys-info 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)",
"tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"thiserror 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
@ -2769,7 +2774,7 @@ dependencies = [
"hmac 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "hmac 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
"itertools 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", "itertools 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
"memmap 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", "memmap 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"num-derive 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "num-derive 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
"pbkdf2 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "pbkdf2 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -2804,7 +2809,6 @@ dependencies = [
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
"num-derive 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "num-derive 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
"solana-config-program 0.24.0", "solana-config-program 0.24.0",
@ -3168,7 +3172,8 @@ dependencies = [
[[package]] [[package]]
name = "solana_rbpf" name = "solana_rbpf"
version = "0.1.20" version = "0.1.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"combine 2.5.2 (registry+https://github.com/rust-lang/crates.io-index)", "combine 2.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
@ -4212,7 +4217,7 @@ dependencies = [
"checksum mach_o_sys 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3e854583a83f20cf329bb9283366335387f7db59d640d1412167e05fedb98826" "checksum mach_o_sys 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3e854583a83f20cf329bb9283366335387f7db59d640d1412167e05fedb98826"
"checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" "checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08"
"checksum memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "88579771288728879b57485cc7d6b07d648c9f0141eb955f8ab7f9d45394468e" "checksum memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "88579771288728879b57485cc7d6b07d648c9f0141eb955f8ab7f9d45394468e"
"checksum memmap 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e2ffa2c986de11a9df78620c01eeaaf27d94d3ff02bf81bfcca953102dd0c6ff" "checksum memmap 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b"
"checksum memoffset 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ce6075db033bbbb7ee5a0bbd3a3186bbae616f57fb001c485c7ff77955f8177f" "checksum memoffset 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ce6075db033bbbb7ee5a0bbd3a3186bbae616f57fb001c485c7ff77955f8177f"
"checksum memsec 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ccabb92f665f997bcb4f3ade019a8e07315148d8bcef3e65fbc5dbd65a22eb04" "checksum memsec 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ccabb92f665f997bcb4f3ade019a8e07315148d8bcef3e65fbc5dbd65a22eb04"
"checksum mime 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)" = "dd1d63acd1b78403cc0c325605908475dd9b9a3acbf65ed8bcab97e27014afcf" "checksum mime 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)" = "dd1d63acd1b78403cc0c325605908475dd9b9a3acbf65ed8bcab97e27014afcf"
@ -4365,6 +4370,7 @@ dependencies = [
"checksum solana_libra_vm_cache_map 0.0.1-sol4 (registry+https://github.com/rust-lang/crates.io-index)" = "37fa2e1f00a87514cd2169149a5f81a89279703b2523979688d6ef84081a4690" "checksum solana_libra_vm_cache_map 0.0.1-sol4 (registry+https://github.com/rust-lang/crates.io-index)" = "37fa2e1f00a87514cd2169149a5f81a89279703b2523979688d6ef84081a4690"
"checksum solana_libra_vm_runtime 0.0.1-sol4 (registry+https://github.com/rust-lang/crates.io-index)" = "ff9f8a7b8212dc4ece5d93f2839896e633c34d7463856e4a555cbcb5c67e9c26" "checksum solana_libra_vm_runtime 0.0.1-sol4 (registry+https://github.com/rust-lang/crates.io-index)" = "ff9f8a7b8212dc4ece5d93f2839896e633c34d7463856e4a555cbcb5c67e9c26"
"checksum solana_libra_vm_runtime_types 0.0.1-sol4 (registry+https://github.com/rust-lang/crates.io-index)" = "254c23c8f30e7c82ae4dc6694e743400d674c66d371b700eec03378ba994f00b" "checksum solana_libra_vm_runtime_types 0.0.1-sol4 (registry+https://github.com/rust-lang/crates.io-index)" = "254c23c8f30e7c82ae4dc6694e743400d674c66d371b700eec03378ba994f00b"
"checksum solana_rbpf 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)" = "053e2a0f1f6c6298bf832493aeacdd6df98efb756a90feabd33caca9c708f2be"
"checksum sourcefile 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4bf77cb82ba8453b42b6ae1d692e4cdc92f9a47beaf89a847c8be83f4e328ad3" "checksum sourcefile 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4bf77cb82ba8453b42b6ae1d692e4cdc92f9a47beaf89a847c8be83f4e328ad3"
"checksum spin 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" "checksum spin 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
"checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" "checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8"

View File

@ -72,7 +72,5 @@ fn test_accounts_hash_bank_hash(bencher: &mut Bencher) {
let mut pubkeys: Vec<Pubkey> = vec![]; let mut pubkeys: Vec<Pubkey> = vec![];
create_test_accounts(&accounts, &mut pubkeys, 60000, 0); create_test_accounts(&accounts, &mut pubkeys, 60000, 0);
let ancestors = vec![(0, 0)].into_iter().collect(); let ancestors = vec![(0, 0)].into_iter().collect();
bencher.iter(|| { bencher.iter(|| assert!(accounts.verify_bank_hash(0, &ancestors)));
accounts.verify_bank_hash(0, &ancestors);
});
} }

View File

@ -3,7 +3,7 @@
extern crate test; extern crate test;
use log::*; use log::*;
use solana_runtime::message_processor::*; use solana_runtime::message_processor::PreAccount;
use solana_sdk::{account::Account, pubkey::Pubkey}; use solana_sdk::{account::Account, pubkey::Pubkey};
use test::Bencher; use test::Bencher;
@ -13,26 +13,18 @@ fn bench_verify_account_changes_data(bencher: &mut Bencher) {
let owner = Pubkey::new_rand(); let owner = Pubkey::new_rand();
let non_owner = Pubkey::new_rand(); let non_owner = Pubkey::new_rand();
let pre = PreInstructionAccount::new( let pre = PreAccount::new(&Account::new(0, BUFSIZE, &owner), true, &owner);
&Account::new(0, BUFSIZE, &owner),
true,
need_account_data_checked(&owner, &owner, true),
);
let post = Account::new(0, BUFSIZE, &owner); let post = Account::new(0, BUFSIZE, &owner);
assert_eq!(verify_account_changes(&owner, &pre, &post), Ok(())); assert_eq!(pre.verify(&owner, &post), Ok(()));
// this one should be faster // this one should be faster
bencher.iter(|| { bencher.iter(|| {
verify_account_changes(&owner, &pre, &post).unwrap(); pre.verify(&owner, &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);
let pre = PreInstructionAccount::new( let pre = PreAccount::new(&Account::new(0, BUFSIZE, &owner), true, &non_owner);
&Account::new(0, BUFSIZE, &owner),
true,
need_account_data_checked(&owner, &non_owner, true),
);
match pre.data { match pre.data {
Some(ref data) => bencher.iter(|| *data == post.data), Some(ref data) => bencher.iter(|| *data == post.data),
None => panic!("No data!"), None => panic!("No data!"),
@ -40,7 +32,7 @@ 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(|| {
verify_account_changes(&non_owner, &pre, &post).unwrap(); pre.verify(&non_owner, &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);
@ -53,14 +45,14 @@ static BUF1: [u8; BUFSIZE] = [1; BUFSIZE];
#[bench] #[bench]
fn bench_is_zeroed(bencher: &mut Bencher) { fn bench_is_zeroed(bencher: &mut Bencher) {
bencher.iter(|| { bencher.iter(|| {
is_zeroed(&BUF0); PreAccount::is_zeroed(&BUF0);
}); });
} }
#[bench] #[bench]
fn bench_is_zeroed_not(bencher: &mut Bencher) { fn bench_is_zeroed_not(bencher: &mut Bencher) {
bencher.iter(|| { bencher.iter(|| {
is_zeroed(&BUF1); PreAccount::is_zeroed(&BUF1);
}); });
} }

View File

@ -1,5 +1,4 @@
use crate::native_loader; use crate::{native_loader, system_instruction_processor};
use crate::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},
@ -20,7 +19,8 @@ use libloading::os::windows::*;
// The relevant state of an account before an Instruction executes, used // The relevant state of an account before an Instruction executes, used
// to verify account integrity after the Instruction completes // to verify account integrity after the Instruction completes
pub struct PreInstructionAccount { #[derive(Clone, Debug, PartialEq)]
pub struct PreAccount {
pub is_writable: bool, pub is_writable: bool,
pub lamports: u64, pub lamports: u64,
pub data_len: usize, pub data_len: usize,
@ -29,13 +29,13 @@ pub struct PreInstructionAccount {
pub executable: bool, pub executable: bool,
pub rent_epoch: Epoch, pub rent_epoch: Epoch,
} }
impl PreInstructionAccount { impl PreAccount {
pub fn new(account: &Account, is_writable: bool, copy_data: bool) -> Self { pub fn new(account: &Account, is_writable: bool, program_id: &Pubkey) -> Self {
Self { Self {
is_writable, is_writable,
lamports: account.lamports, lamports: account.lamports,
data_len: account.data.len(), data_len: account.data.len(),
data: if copy_data { data: if Self::should_verify_data(&account.owner, program_id, is_writable) {
Some(account.data.clone()) Some(account.data.clone())
} else { } else {
None None
@ -45,59 +45,56 @@ impl PreInstructionAccount {
rent_epoch: account.rent_epoch, rent_epoch: account.rent_epoch,
} }
} }
}
pub fn need_account_data_checked(program_id: &Pubkey, owner: &Pubkey, is_writable: bool) -> bool { fn should_verify_data(owner: &Pubkey, program_id: &Pubkey, is_writable: bool) -> bool {
// For accounts not assigned to the program, the data may not change. // For accounts not assigned to the program, the data may not change.
program_id != owner program_id != owner
// Read-only account data may not change. // Read-only account data may not change.
|| !is_writable || !is_writable
} }
pub fn verify_account_changes(
program_id: &Pubkey, pub fn verify(&self, program_id: &Pubkey, post: &Account) -> Result<(), InstructionError> {
pre: &PreInstructionAccount,
post: &Account,
) -> Result<(), InstructionError> {
// Verify the transaction // Verify the transaction
// 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
if pre.owner != post.owner if self.owner != post.owner
&& (!pre.is_writable // line coverage used to get branch coverage && (!self.is_writable // line coverage used to get branch coverage
|| *program_id != pre.owner // line coverage used to get branch coverage || *program_id != self.owner // line coverage used to get branch coverage
|| !is_zeroed(&post.data)) || !Self::is_zeroed(&post.data))
{ {
return Err(InstructionError::ModifiedProgramId); return Err(InstructionError::ModifiedProgramId);
} }
// An account not assigned to the program cannot have its balance decrease. // An account not assigned to the program cannot have its balance decrease.
if *program_id != pre.owner // line coverage used to get branch coverage if *program_id != self.owner // line coverage used to get branch coverage
&& pre.lamports > post.lamports && self.lamports > post.lamports
{ {
return Err(InstructionError::ExternalAccountLamportSpend); return Err(InstructionError::ExternalAccountLamportSpend);
} }
// The balance of read-only accounts may not change. // The balance of read-only accounts may not change.
if !pre.is_writable // line coverage used to get branch coverage if !self.is_writable // line coverage used to get branch coverage
&& pre.lamports != post.lamports && self.lamports != post.lamports
{ {
return Err(InstructionError::ReadonlyLamportChange); return Err(InstructionError::ReadonlyLamportChange);
} }
// Only the system program can change the size of the data // Only the system program can change the size of the data
// and only if the system program owns the account // and only if the system program owns the account
if pre.data_len != post.data.len() if self.data_len != post.data.len()
&& (!system_program::check_id(program_id) // line coverage used to get branch coverage && (!system_program::check_id(program_id) // line coverage used to get branch coverage
|| !system_program::check_id(&pre.owner)) || !system_program::check_id(&self.owner))
{ {
return Err(InstructionError::AccountDataSizeChanged); return Err(InstructionError::AccountDataSizeChanged);
} }
if need_account_data_checked(&pre.owner, program_id, pre.is_writable) { if Self::should_verify_data(&self.owner, program_id, self.is_writable) {
match &pre.data { match &self.data {
Some(data) if *data == post.data => (), Some(data) if *data == post.data => (),
_ => { _ => {
if !pre.is_writable { if !self.is_writable {
return Err(InstructionError::ReadonlyDataModified); return Err(InstructionError::ReadonlyDataModified);
} else { } else {
return Err(InstructionError::ExternalAccountDataModified); return Err(InstructionError::ExternalAccountDataModified);
@ -107,24 +104,33 @@ pub fn verify_account_changes(
} }
// 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 pre.executable != post.executable if self.executable != post.executable
&& (!pre.is_writable // line coverage used to get branch coverage && (!self.is_writable // line coverage used to get branch coverage
|| pre.executable // line coverage used to get branch coverage || self.executable // line coverage used to get branch coverage
|| *program_id != pre.owner) || *program_id != self.owner)
{ {
return Err(InstructionError::ExecutableModified); return Err(InstructionError::ExecutableModified);
} }
// No one modifies rent_epoch (yet). // No one modifies r ent_epoch (yet).
if pre.rent_epoch != post.rent_epoch { if self.rent_epoch != post.rent_epoch {
return Err(InstructionError::RentEpochModified); return Err(InstructionError::RentEpochModified);
} }
Ok(()) Ok(())
}
pub fn is_zeroed(buf: &[u8]) -> bool {
const ZEROS_LEN: usize = 1024;
static ZEROS: [u8; ZEROS_LEN] = [0; ZEROS_LEN];
let mut chunks = buf.chunks_exact(ZEROS_LEN);
chunks.all(|chunk| chunk == &ZEROS[..])
&& chunks.remainder() == &ZEROS[..chunks.remainder().len()]
}
} }
pub type ProcessInstruction = fn(&Pubkey, &[KeyedAccount], &[u8]) -> Result<(), InstructionError>; pub type ProcessInstruction = fn(&Pubkey, &[KeyedAccount], &[u8]) -> Result<(), InstructionError>;
pub type SymbolCache = RwLock<HashMap<Vec<u8>, Symbol<entrypoint_native::Entrypoint>>>; pub type SymbolCache = RwLock<HashMap<Vec<u8>, Symbol<entrypoint_native::Entrypoint>>>;
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
@ -232,21 +238,40 @@ impl MessageProcessor {
Ok(()) Ok(())
} }
pub fn sum_account_lamports(accounts: &[Rc<RefCell<Account>>]) -> u128 { pub fn verify(
program_id: &Pubkey,
pre_accounts: &[PreAccount],
executable_accounts: &[(Pubkey, RefCell<Account>)],
program_accounts: &[Rc<RefCell<Account>>],
) -> Result<(), InstructionError> {
// Verify all accounts have zero outstanding refs
Self::verify_account_references(executable_accounts, program_accounts)?;
// Verify the per-account instruction results
let (mut pre_sum, mut post_sum) = (0_u128, 0_u128);
'root: for (i, (pre_account, account)) in
pre_accounts.iter().zip(program_accounts).enumerate()
{
// Note: This is an O(n^2) algorithm, // Note: This is an O(n^2) algorithm,
// but performed on a very small slice and requires no heap allocations // but performed on a very small slice and requires no heap allocations
accounts for account_after in program_accounts.iter().skip(i + 1) {
.iter() if Rc::ptr_eq(account, account_after) {
.enumerate() continue 'root; // don't verify duplicates
.map(|(i, a)| {
for account in accounts.iter().skip(i + 1) {
if Rc::ptr_eq(a, account) {
return 0; // don't double count duplicates
} }
} }
u128::from(a.borrow().lamports) let account = account
}) .try_borrow()
.sum() .map_err(|_| InstructionError::AccountBorrowFailed)?;
pre_account.verify(&program_id, &account)?;
pre_sum += u128::from(pre_account.lamports);
post_sum += u128::from(account.lamports);
}
// Verify that the total sum of all the lamports did not change
if pre_sum != post_sum {
return Err(InstructionError::UnbalancedInstruction);
}
Ok(())
} }
/// Execute an instruction /// Execute an instruction
@ -269,32 +294,20 @@ impl MessageProcessor {
.map(|(i, account)| { .map(|(i, account)| {
let is_writable = message.is_writable(instruction.accounts[i] as usize); let is_writable = message.is_writable(instruction.accounts[i] as usize);
let account = account.borrow(); let account = account.borrow();
PreInstructionAccount::new( PreAccount::new(&account, is_writable, program_id)
&account,
is_writable,
need_account_data_checked(&account.owner, program_id, is_writable),
)
}) })
.collect(); .collect();
// Sum total lamports before instruction processing
let pre_total = Self::sum_account_lamports(program_accounts);
self.process_instruction(message, instruction, executable_accounts, program_accounts)?; self.process_instruction(message, instruction, executable_accounts, program_accounts)?;
// Verify all accounts have zero outstanding refs // Verify the instruction results
Self::verify_account_references(executable_accounts, program_accounts)?; Self::verify(
// Verify the instruction &program_id,
for (pre_account, post_account) in pre_accounts.iter().zip(program_accounts.iter()) { &pre_accounts,
let post_account = post_account executable_accounts,
.try_borrow() program_accounts,
.map_err(|_| InstructionError::AccountBorrowFailed)?; )?;
verify_account_changes(&program_id, pre_account, &post_account)?;
}
// Verify total sum of all the lamports did not change
let post_total = Self::sum_account_lamports(program_accounts);
if pre_total != post_total {
return Err(InstructionError::UnbalancedInstruction);
}
Ok(()) Ok(())
} }
@ -329,15 +342,6 @@ impl MessageProcessor {
} }
} }
pub const ZEROS_LEN: usize = 1024;
static ZEROS: [u8; ZEROS_LEN] = [0; ZEROS_LEN];
pub fn is_zeroed(buf: &[u8]) -> bool {
let mut chunks = buf.chunks_exact(ZEROS_LEN);
chunks.all(|chunk| chunk == &ZEROS[..])
&& chunks.remainder() == &ZEROS[..chunks.remainder().len()]
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
@ -349,23 +353,24 @@ mod tests {
#[test] #[test]
fn test_is_zeroed() { fn test_is_zeroed() {
const ZEROS_LEN: usize = 1024;
let mut buf = [0; ZEROS_LEN]; let mut buf = [0; ZEROS_LEN];
assert_eq!(is_zeroed(&buf), true); assert_eq!(PreAccount::is_zeroed(&buf), true);
buf[0] = 1; buf[0] = 1;
assert_eq!(is_zeroed(&buf), false); assert_eq!(PreAccount::is_zeroed(&buf), false);
let mut buf = [0; ZEROS_LEN - 1]; let mut buf = [0; ZEROS_LEN - 1];
assert_eq!(is_zeroed(&buf), true); assert_eq!(PreAccount::is_zeroed(&buf), true);
buf[0] = 1; buf[0] = 1;
assert_eq!(is_zeroed(&buf), false); assert_eq!(PreAccount::is_zeroed(&buf), false);
let mut buf = [0; ZEROS_LEN + 1]; let mut buf = [0; ZEROS_LEN + 1];
assert_eq!(is_zeroed(&buf), true); assert_eq!(PreAccount::is_zeroed(&buf), true);
buf[0] = 1; buf[0] = 1;
assert_eq!(is_zeroed(&buf), false); assert_eq!(PreAccount::is_zeroed(&buf), false);
let buf = vec![]; let buf = vec![];
assert_eq!(is_zeroed(&buf), true); assert_eq!(PreAccount::is_zeroed(&buf), true);
} }
#[test] #[test]
@ -394,51 +399,6 @@ mod tests {
); );
} }
#[test]
fn test_sum_account_lamports() {
let owner_pubkey = Pubkey::new_rand();
let account1 = Rc::new(RefCell::new(Account::new(1, 1, &owner_pubkey)));
let account2 = Rc::new(RefCell::new(Account::new(2, 1, &owner_pubkey)));
let account3 = Rc::new(RefCell::new(Account::new(3, 1, &owner_pubkey)));
assert_eq!(0, MessageProcessor::sum_account_lamports(&vec![]));
assert_eq!(
6,
MessageProcessor::sum_account_lamports(&vec![
account1.clone(),
account2.clone(),
account3.clone()
])
);
assert_eq!(
3,
MessageProcessor::sum_account_lamports(&vec![
account1.clone(),
account2.clone(),
account1.clone()
])
);
assert_eq!(
1,
MessageProcessor::sum_account_lamports(&vec![
account1.clone(),
account1.clone(),
account1.clone()
])
);
assert_eq!(
6,
MessageProcessor::sum_account_lamports(&vec![
account1.clone(),
account2.clone(),
account3.clone(),
account1.clone(),
account2.clone(),
account3.clone(),
])
);
}
#[test] #[test]
fn test_verify_account_changes_owner() { fn test_verify_account_changes_owner() {
fn change_owner( fn change_owner(
@ -447,15 +407,8 @@ mod tests {
post: &Pubkey, post: &Pubkey,
is_writable: bool, is_writable: bool,
) -> Result<(), InstructionError> { ) -> Result<(), InstructionError> {
verify_account_changes( PreAccount::new(&Account::new(0, 0, pre), is_writable, ix)
&ix, .verify(ix, &Account::new(0, 0, post))
&PreInstructionAccount::new(
&Account::new(0, 0, pre),
is_writable,
need_account_data_checked(pre, ix, is_writable),
),
&Account::new(0, 0, post),
)
} }
let system_program_id = system_program::id(); let system_program_id = system_program::id();
@ -505,27 +458,27 @@ mod tests {
); );
assert_eq!( assert_eq!(
verify_account_changes( PreAccount::new(
&mallory_program_id,
&PreInstructionAccount::new(
&Account::new_data(0, &[42], &mallory_program_id).unwrap(), &Account::new_data(0, &[42], &mallory_program_id).unwrap(),
true, true,
need_account_data_checked(&mallory_program_id, &mallory_program_id, true), &mallory_program_id,
), )
&Account::new_data(0, &[0], &alice_program_id,).unwrap(), .verify(
&mallory_program_id,
&Account::new_data(0, &[0], &alice_program_id).unwrap(),
), ),
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!(
verify_account_changes( PreAccount::new(
&mallory_program_id,
&PreInstructionAccount::new(
&Account::new_data(0, &[42], &mallory_program_id).unwrap(), &Account::new_data(0, &[42], &mallory_program_id).unwrap(),
true, true,
need_account_data_checked(&mallory_program_id, &mallory_program_id, true), &mallory_program_id,
), )
&Account::new_data(0, &[42], &alice_program_id,).unwrap(), .verify(
&mallory_program_id,
&Account::new_data(0, &[42], &alice_program_id).unwrap(),
), ),
Err(InstructionError::ModifiedProgramId), Err(InstructionError::ModifiedProgramId),
"mallory should not be able to inject data into the alice program" "mallory should not be able to inject data into the alice program"
@ -540,14 +493,14 @@ mod tests {
pre_executable: bool, pre_executable: bool,
post_executable: bool| post_executable: bool|
-> Result<(), InstructionError> { -> Result<(), InstructionError> {
let pre = PreInstructionAccount::new( let pre = PreAccount::new(
&Account { &Account {
owner, owner,
executable: pre_executable, executable: pre_executable,
..Account::default() ..Account::default()
}, },
is_writable, is_writable,
need_account_data_checked(&owner, &program_id, is_writable), &program_id,
); );
let post = Account { let post = Account {
@ -555,7 +508,7 @@ mod tests {
executable: post_executable, executable: post_executable,
..Account::default() ..Account::default()
}; };
verify_account_changes(&program_id, &pre, &post) pre.verify(&program_id, &post)
}; };
let mallory_program_id = Pubkey::new_rand(); let mallory_program_id = Pubkey::new_rand();
@ -591,14 +544,14 @@ mod tests {
#[test] #[test]
fn test_verify_account_changes_data_len() { fn test_verify_account_changes_data_len() {
assert_eq!( assert_eq!(
verify_account_changes( PreAccount::new(
&system_program::id(),
&PreInstructionAccount::new(
&Account::new_data(0, &[0], &system_program::id()).unwrap(), &Account::new_data(0, &[0], &system_program::id()).unwrap(),
true, true,
need_account_data_checked(&system_program::id(), &system_program::id(), true), &system_program::id()
), )
&Account::new_data(0, &[0, 0], &system_program::id()).unwrap(), .verify(
&system_program::id(),
&Account::new_data(0, &[0, 0], &system_program::id()).unwrap()
), ),
Ok(()), Ok(()),
"system program should be able to change the data len" "system program should be able to change the data len"
@ -606,13 +559,12 @@ mod tests {
let alice_program_id = Pubkey::new_rand(); let alice_program_id = Pubkey::new_rand();
assert_eq!( assert_eq!(
verify_account_changes( PreAccount::new(
&system_program::id(),
&PreInstructionAccount::new(
&Account::new_data(0, &[0], &alice_program_id).unwrap(), &Account::new_data(0, &[0], &alice_program_id).unwrap(),
true, true,
need_account_data_checked(&alice_program_id, &system_program::id(), true), &system_program::id(),
), ).verify(
&system_program::id(),
&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),
@ -626,13 +578,13 @@ mod tests {
let change_data = let change_data =
|program_id: &Pubkey, is_writable: bool| -> Result<(), InstructionError> { |program_id: &Pubkey, is_writable: bool| -> Result<(), InstructionError> {
let pre = PreInstructionAccount::new( let pre = PreAccount::new(
&Account::new_data(0, &[0], &alice_program_id).unwrap(), &Account::new_data(0, &[0], &alice_program_id).unwrap(),
is_writable, is_writable,
need_account_data_checked(&alice_program_id, &program_id, is_writable), &program_id,
); );
let post = Account::new_data(0, &[42], &alice_program_id).unwrap(); let post = Account::new_data(0, &[42], &alice_program_id).unwrap();
verify_account_changes(&program_id, &pre, &post) pre.verify(&program_id, &post)
}; };
let mallory_program_id = Pubkey::new_rand(); let mallory_program_id = Pubkey::new_rand();
@ -658,22 +610,22 @@ 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 pre = PreInstructionAccount::new( let pre = PreAccount::new(
&Account::new(0, 0, &alice_program_id), &Account::new(0, 0, &alice_program_id),
false, false,
need_account_data_checked(&alice_program_id, &system_program::id(), false), &system_program::id(),
); );
let mut post = Account::new(0, 0, &alice_program_id); let mut post = Account::new(0, 0, &alice_program_id);
assert_eq!( assert_eq!(
verify_account_changes(&system_program::id(), &pre, &post), pre.verify(&system_program::id(), &post),
Ok(()), Ok(()),
"nothing changed!" "nothing changed!"
); );
post.rent_epoch += 1; post.rent_epoch += 1;
assert_eq!( assert_eq!(
verify_account_changes(&system_program::id(), &pre, &post), pre.verify(&system_program::id(), &post),
Err(InstructionError::RentEpochModified), Err(InstructionError::RentEpochModified),
"no one touches rent_epoch" "no one touches rent_epoch"
); );
@ -683,16 +635,16 @@ mod tests {
fn test_verify_account_changes_deduct_lamports_and_reassign_account() { fn test_verify_account_changes_deduct_lamports_and_reassign_account() {
let alice_program_id = Pubkey::new_rand(); let alice_program_id = Pubkey::new_rand();
let bob_program_id = Pubkey::new_rand(); let bob_program_id = Pubkey::new_rand();
let pre = PreInstructionAccount::new( let pre = PreAccount::new(
&Account::new_data(42, &[42], &alice_program_id).unwrap(), &Account::new_data(42, &[42], &alice_program_id).unwrap(),
true, true,
need_account_data_checked(&alice_program_id, &alice_program_id, true), &alice_program_id,
); );
let post = Account::new_data(1, &[0], &bob_program_id).unwrap(); let post = Account::new_data(1, &[0], &bob_program_id).unwrap();
// positive test of this capability // positive test of this capability
assert_eq!( assert_eq!(
verify_account_changes(&alice_program_id, &pre, &post), pre.verify(&alice_program_id, &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",
); );
@ -701,51 +653,51 @@ 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 pre = PreInstructionAccount::new( let pre = PreAccount::new(
&Account::new(42, 0, &alice_program_id), &Account::new(42, 0, &alice_program_id),
false, false,
need_account_data_checked(&alice_program_id, &system_program::id(), false), &system_program::id(),
); );
let post = Account::new(0, 0, &alice_program_id); let post = Account::new(0, 0, &alice_program_id);
assert_eq!( assert_eq!(
verify_account_changes(&system_program::id(), &pre, &post), pre.verify(&system_program::id(), &post),
Err(InstructionError::ExternalAccountLamportSpend), Err(InstructionError::ExternalAccountLamportSpend),
"debit should fail, even if system program" "debit should fail, even if system program"
); );
let pre = PreInstructionAccount::new( let pre = PreAccount::new(
&Account::new(42, 0, &alice_program_id), &Account::new(42, 0, &alice_program_id),
false, false,
need_account_data_checked(&alice_program_id, &alice_program_id, false), &alice_program_id,
); );
assert_eq!( assert_eq!(
verify_account_changes(&alice_program_id, &pre, &post,), pre.verify(&alice_program_id, &post,),
Err(InstructionError::ReadonlyLamportChange), Err(InstructionError::ReadonlyLamportChange),
"debit should fail, even if owning program" "debit should fail, even if owning program"
); );
let pre = PreInstructionAccount::new( let pre = PreAccount::new(
&Account::new(42, 0, &alice_program_id), &Account::new(42, 0, &alice_program_id),
true, true,
need_account_data_checked(&alice_program_id, &system_program::id(), true), &system_program::id(),
); );
let post = Account::new(0, 0, &system_program::id()); let post = Account::new(0, 0, &system_program::id());
assert_eq!( assert_eq!(
verify_account_changes(&system_program::id(), &pre, &post), pre.verify(&system_program::id(), &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"
); );
let pre = PreInstructionAccount::new( let pre = PreAccount::new(
&Account::new(42, 0, &system_program::id()), &Account::new(42, 0, &system_program::id()),
true, true,
need_account_data_checked(&system_program::id(), &system_program::id(), true), &system_program::id(),
); );
let post = Account::new(0, 0, &alice_program_id); let post = Account::new(0, 0, &alice_program_id);
assert_eq!( assert_eq!(
verify_account_changes(&system_program::id(), &pre, &post), pre.verify(&system_program::id(), &post),
Ok(()), Ok(()),
"system can spend (and change owner)" "system can spend (and change owner)"
); );
@ -754,34 +706,34 @@ mod tests {
#[test] #[test]
fn test_verify_account_changes_data_size_changed() { fn test_verify_account_changes_data_size_changed() {
let alice_program_id = Pubkey::new_rand(); let alice_program_id = Pubkey::new_rand();
let pre = PreInstructionAccount::new( let pre = PreAccount::new(
&Account::new_data(42, &[0], &alice_program_id).unwrap(), &Account::new_data(42, &[0], &alice_program_id).unwrap(),
true, true,
need_account_data_checked(&alice_program_id, &system_program::id(), true), &system_program::id(),
); );
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!(
verify_account_changes(&system_program::id(), &pre, &post), pre.verify(&system_program::id(), &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"
); );
let pre = PreInstructionAccount::new( let pre = PreAccount::new(
&Account::new_data(42, &[0], &alice_program_id).unwrap(), &Account::new_data(42, &[0], &alice_program_id).unwrap(),
true, true,
need_account_data_checked(&alice_program_id, &alice_program_id, true), &alice_program_id,
); );
assert_eq!( assert_eq!(
verify_account_changes(&alice_program_id, &pre, &post), pre.verify(&alice_program_id, &post),
Err(InstructionError::AccountDataSizeChanged), Err(InstructionError::AccountDataSizeChanged),
"non-system programs cannot change their data size" "non-system programs cannot change their data size"
); );
let pre = PreInstructionAccount::new( let pre = PreAccount::new(
&Account::new_data(42, &[0], &system_program::id()).unwrap(), &Account::new_data(42, &[0], &system_program::id()).unwrap(),
true, true,
need_account_data_checked(&system_program::id(), &system_program::id(), true), &system_program::id(),
); );
assert_eq!( assert_eq!(
verify_account_changes(&system_program::id(), &pre, &post), pre.verify(&system_program::id(), &post),
Ok(()), Ok(()),
"system program should be able to change acount data size" "system program should be able to change acount data size"
); );