Track account writable deescalation (#14626)

This commit is contained in:
Jack May 2021-01-22 15:28:01 -08:00 committed by GitHub
parent cbb9ac19b9
commit 77572a7c53
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 246 additions and 56 deletions

View File

@ -210,6 +210,13 @@ impl program_stubs::SyscallStubs for SyscallStubs {
let program_id_index = message.instructions[0].program_id_index as usize;
let program_id = message.account_keys[program_id_index];
let program_account_info = &account_infos[program_id_index];
// TODO don't have the caller's keyed_accounts so can't validate writer or signer escalation or deescalation yet
let caller_privileges = message
.account_keys
.iter()
.enumerate()
.map(|(i, _)| message.is_writable(i))
.collect::<Vec<bool>>();
stable_log::program_invoke(&logger, &program_id, invoke_context.invoke_depth());
@ -268,6 +275,7 @@ impl program_stubs::SyscallStubs for SyscallStubs {
&message,
&executables,
&accounts,
&caller_privileges,
invoke_context,
)
.map_err(|err| ProgramError::try_from(err).unwrap_or_else(|err| panic!("{}", err)))?;

View File

@ -17,6 +17,7 @@ static const uint8_t TEST_INSTRUCTION_META_TOO_LARGE = 10;
static const uint8_t TEST_RETURN_ERROR = 11;
static const uint8_t TEST_PRIVILEGE_DEESCALATION_ESCALATION_SIGNER = 12;
static const uint8_t TEST_PRIVILEGE_DEESCALATION_ESCALATION_WRITABLE = 13;
static const uint8_t TEST_WRITE_DEESCALATION = 14;
static const int MINT_INDEX = 0;
static const int ARGUMENT_INDEX = 1;
@ -251,6 +252,26 @@ extern uint64_t entrypoint(const uint8_t *input) {
for (int i = 0; i < accounts[INVOKED_ARGUMENT_INDEX].data_len; i++) {
sol_assert(accounts[INVOKED_ARGUMENT_INDEX].data[i] == i);
}
sol_log("Verify data write before ro cpi call");
{
for (int i = 0; i < accounts[ARGUMENT_INDEX].data_len; i++) {
accounts[ARGUMENT_INDEX].data[i] = 0;
}
SolAccountMeta arguments[] = {
{accounts[ARGUMENT_INDEX].key, false, false}};
uint8_t data[] = {VERIFY_PRIVILEGE_DEESCALATION};
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)));
for (int i = 0; i < accounts[ARGUMENT_INDEX].data_len; i++) {
sol_assert(accounts[ARGUMENT_INDEX].data[i] == 0);
}
}
break;
}
case TEST_PRIVILEGE_ESCALATION_SIGNER: {
@ -443,7 +464,8 @@ extern uint64_t entrypoint(const uint8_t *input) {
break;
}
case TEST_RETURN_ERROR: {
SolAccountMeta arguments[] = {{accounts[ARGUMENT_INDEX].key, true, true}};
sol_log("Test return error");
SolAccountMeta arguments[] = {{accounts[ARGUMENT_INDEX].key, false, true}};
uint8_t data[] = {RETURN_ERROR};
const SolInstruction instruction = {accounts[INVOKED_PROGRAM_INDEX].key,
arguments, SOL_ARRAY_SIZE(arguments),
@ -484,6 +506,18 @@ extern uint64_t entrypoint(const uint8_t *input) {
break;
}
case TEST_WRITE_DEESCALATION: {
sol_log("Test writable deescalation");
SolAccountMeta arguments[] = {
{accounts[INVOKED_ARGUMENT_INDEX].key, false, false}};
uint8_t data[] = {WRITE_ACCOUNT, 10};
const SolInstruction instruction = {accounts[INVOKED_PROGRAM_INDEX].key,
arguments, SOL_ARRAY_SIZE(arguments),
data, SOL_ARRAY_SIZE(data)};
sol_invoke(&instruction, accounts, SOL_ARRAY_SIZE(accounts));
break;
}
default:
sol_panic();
}

View File

@ -15,3 +15,4 @@ const uint8_t RETURN_OK = 7;
const uint8_t VERIFY_PRIVILEGE_DEESCALATION = 8;
const uint8_t VERIFY_PRIVILEGE_DEESCALATION_ESCALATION_SIGNER = 9;
const uint8_t VERIFY_PRIVILEGE_DEESCALATION_ESCALATION_WRITABLE = 10;
const uint8_t WRITE_ACCOUNT = 11;

View File

@ -158,6 +158,7 @@ extern uint64_t entrypoint(const uint8_t *input) {
sol_assert(accounts[ARGUMENT_INDEX].is_writable);
break;
}
case VERIFY_PRIVILEGE_ESCALATION: {
sol_log("Should never get here!");
break;
@ -188,6 +189,7 @@ extern uint64_t entrypoint(const uint8_t *input) {
sol_invoke(&instruction, accounts, SOL_ARRAY_SIZE(accounts)));
break;
}
case VERIFY_PRIVILEGE_DEESCALATION_ESCALATION_WRITABLE: {
sol_log("verify privilege deescalation escalation writable");
static const int INVOKED_PROGRAM_INDEX = 0;
@ -245,6 +247,18 @@ extern uint64_t entrypoint(const uint8_t *input) {
}
break;
}
case WRITE_ACCOUNT: {
sol_log("write account");
static const int INVOKED_ARGUMENT_INDEX = 0;
sol_assert(sol_deserialize(input, &params, 1));
for (int i = 0; i < params.data[1]; i++) {
accounts[INVOKED_ARGUMENT_INDEX].data[i] = params.data[1];
}
break;
}
default:
return ERROR_INVALID_INSTRUCTION_DATA;
}

View File

@ -29,6 +29,7 @@ const TEST_INSTRUCTION_META_TOO_LARGE: u8 = 10;
const TEST_RETURN_ERROR: u8 = 11;
const TEST_PRIVILEGE_DEESCALATION_ESCALATION_SIGNER: u8 = 12;
const TEST_PRIVILEGE_DEESCALATION_ESCALATION_WRITABLE: u8 = 13;
const TEST_WRITE_DEESCALATION: u8 = 14;
// const MINT_INDEX: usize = 0;
const ARGUMENT_INDEX: usize = 1;
@ -331,6 +332,28 @@ fn process_instruction(
assert_eq!(data[i as usize], i);
}
}
msg!("Verify data write before cpi call with deescalated writable");
{
{
let mut data = accounts[ARGUMENT_INDEX].try_borrow_mut_data()?;
for i in 0..100 {
data[i as usize] = 42;
}
}
let invoked_instruction = create_instruction(
*accounts[INVOKED_PROGRAM_INDEX].key,
&[(accounts[ARGUMENT_INDEX].key, false, false)],
vec![VERIFY_PRIVILEGE_DEESCALATION],
);
invoke(&invoked_instruction, accounts)?;
let data = accounts[ARGUMENT_INDEX].try_borrow_data()?;
for i in 0..100 {
assert_eq!(data[i as usize], 42);
}
}
}
TEST_PRIVILEGE_ESCALATION_SIGNER => {
msg!("Test privilege escalation signer");
@ -534,6 +557,15 @@ fn process_instruction(
);
invoke(&invoked_instruction, accounts)?;
}
TEST_WRITE_DEESCALATION => {
msg!("Test writable deescalation");
let instruction = create_instruction(
*accounts[INVOKED_PROGRAM_INDEX].key,
&[(accounts[INVOKED_ARGUMENT_INDEX].key, false, false)],
vec![WRITE_ACCOUNT, 10],
);
let _ = invoke(&instruction, accounts);
}
_ => panic!(),
}

View File

@ -16,6 +16,7 @@ pub const RETURN_OK: u8 = 7;
pub const VERIFY_PRIVILEGE_DEESCALATION: u8 = 8;
pub const VERIFY_PRIVILEGE_DEESCALATION_ESCALATION_SIGNER: u8 = 9;
pub const VERIFY_PRIVILEGE_DEESCALATION_ESCALATION_WRITABLE: u8 = 10;
pub const WRITE_ACCOUNT: u8 = 11;
pub fn create_instruction(
program_id: Pubkey,

View File

@ -229,6 +229,12 @@ fn process_instruction(
}
}
}
WRITE_ACCOUNT => {
msg!("write account");
for i in 0..instruction_data[1] {
accounts[0].data.borrow_mut()[i as usize] = instruction_data[1];
}
}
_ => panic!(),
}

View File

@ -697,6 +697,7 @@ fn test_program_bpf_invoke_sanity() {
const TEST_RETURN_ERROR: u8 = 11;
const TEST_PRIVILEGE_DEESCALATION_ESCALATION_SIGNER: u8 = 12;
const TEST_PRIVILEGE_DEESCALATION_ESCALATION_WRITABLE: u8 = 13;
const TEST_WRITE_DEESCALATION: u8 = 14;
#[allow(dead_code)]
#[derive(Debug)]
@ -813,6 +814,7 @@ fn test_program_bpf_invoke_sanity() {
invoked_program_id.clone(),
invoked_program_id.clone(),
invoked_program_id.clone(),
invoked_program_id.clone(),
],
Languages::Rust => vec![
solana_sdk::system_program::id(),
@ -830,6 +832,7 @@ fn test_program_bpf_invoke_sanity() {
invoked_program_id.clone(),
invoked_program_id.clone(),
invoked_program_id.clone(),
invoked_program_id.clone(),
],
};
assert_eq!(invoked_programs.len(), expected_invoked_programs.len());
@ -931,6 +934,12 @@ fn test_program_bpf_invoke_sanity() {
&[invoked_program_id.clone()],
);
do_invoke_failure_test_local(
TEST_WRITE_DEESCALATION,
TransactionError::InstructionError(0, InstructionError::ReadonlyDataModified),
&[invoked_program_id.clone()],
);
// Check resulting state
assert_eq!(43, bank.get_balance(&derived_key1));

View File

@ -1528,7 +1528,14 @@ fn call<'a>(
signers_seeds_len: u64,
memory_mapping: &MemoryMapping,
) -> Result<u64, EbpfError<BPFError>> {
let (message, executables, accounts, account_refs, abort_on_all_cpi_failures) = {
let (
message,
executables,
accounts,
account_refs,
caller_privileges,
abort_on_all_cpi_failures,
) = {
let invoke_context = syscall.get_context()?;
invoke_context
@ -1555,6 +1562,20 @@ fn call<'a>(
let (message, callee_program_id, callee_program_id_index) =
MessageProcessor::create_message(&instruction, &keyed_account_refs, &signers)
.map_err(SyscallError::InstructionError)?;
let caller_privileges = message
.account_keys
.iter()
.map(|key| {
if let Some(keyed_account) = keyed_account_refs
.iter()
.find(|keyed_account| key == keyed_account.unsigned_key())
{
keyed_account.is_writable()
} else {
false
}
})
.collect::<Vec<bool>>();
if invoke_context.is_feature_active(&limit_cpi_loader_invoke::id()) {
check_authorized_program(&callee_program_id, &instruction.data)?;
}
@ -1590,6 +1611,7 @@ fn call<'a>(
executables,
accounts,
account_refs,
caller_privileges,
invoke_context.is_feature_active(&abort_on_all_cpi_failures::id()),
)
};
@ -1601,6 +1623,7 @@ fn call<'a>(
&message,
&executables,
&accounts,
&caller_privileges,
*(&mut *(syscall.get_context_mut()?)),
) {
Ok(()) => (),

View File

@ -16,15 +16,18 @@ fn bench_verify_account_changes_data(bencher: &mut Bencher) {
let pre = PreAccount::new(
&pubkey::new_rand(),
&Account::new(0, BUFSIZE, &owner),
true,
false,
);
let post = Account::new(0, BUFSIZE, &owner);
assert_eq!(pre.verify(&owner, &Rent::default(), &post), Ok(()));
assert_eq!(
pre.verify(&owner, Some(false), &Rent::default(), &post),
Ok(())
);
// this one should be faster
bencher.iter(|| {
pre.verify(&owner, &Rent::default(), &post).unwrap();
pre.verify(&owner, Some(false), &Rent::default(), &post)
.unwrap();
});
let summary = bencher.bench(|_bencher| {}).unwrap();
info!("data no change by owner: {} ns/iter", summary.median);
@ -38,11 +41,11 @@ fn bench_verify_account_changes_data(bencher: &mut Bencher) {
let pre = PreAccount::new(
&pubkey::new_rand(),
&Account::new(0, BUFSIZE, &owner),
true,
false,
);
bencher.iter(|| {
pre.verify(&non_owner, &Rent::default(), &post).unwrap();
pre.verify(&non_owner, Some(false), &Rent::default(), &post)
.unwrap();
});
let summary = bencher.bench(|_bencher| {}).unwrap();
info!("data no change by non owner: {} ns/iter", summary.median);

View File

@ -8,7 +8,7 @@ use solana_sdk::{
account::Account,
account_utils::StateMut,
bpf_loader_upgradeable::{self, UpgradeableLoaderState},
feature_set::{instructions_sysvar_enabled, FeatureSet},
feature_set::{instructions_sysvar_enabled, track_writable_deescalation, FeatureSet},
instruction::{CompiledInstruction, Instruction, InstructionError},
keyed_account::{create_keyed_readonly_accounts, KeyedAccount},
message::Message,
@ -51,15 +51,13 @@ impl Executors {
#[derive(Clone, Debug, Default)]
pub struct PreAccount {
key: Pubkey,
is_signer: bool,
is_writable: bool,
account: RefCell<Account>,
}
impl PreAccount {
pub fn new(key: &Pubkey, account: &Account, is_signer: bool, is_writable: bool) -> Self {
pub fn new(key: &Pubkey, account: &Account, is_writable: bool) -> Self {
Self {
key: *key,
is_signer,
is_writable,
account: RefCell::new(account.clone()),
}
@ -68,17 +66,24 @@ impl PreAccount {
pub fn verify(
&self,
program_id: &Pubkey,
is_writable: Option<bool>,
rent: &Rent,
post: &Account,
) -> Result<(), InstructionError> {
let pre = self.account.borrow();
let is_writable = if let Some(is_writable) = is_writable {
is_writable
} else {
self.is_writable
};
// Only the owner of the account may change owner and
// only if the account is writable and
// only if the account is not executable and
// only if the data is zero-initialized or empty
if pre.owner != post.owner
&& (!self.is_writable // line coverage used to get branch coverage
&& (!is_writable // line coverage used to get branch coverage
|| pre.executable
|| *program_id != pre.owner
|| !Self::is_zeroed(&post.data))
@ -95,7 +100,7 @@ impl PreAccount {
// The balance of read-only and executable accounts may not change
if pre.lamports != post.lamports {
if !self.is_writable {
if !is_writable {
return Err(InstructionError::ReadonlyLamportChange);
}
if pre.executable {
@ -116,13 +121,13 @@ impl PreAccount {
// and if the account is writable
// and if the account is not executable
if !(*program_id == pre.owner
&& self.is_writable // line coverage used to get branch coverage
&& is_writable // line coverage used to get branch coverage
&& !pre.executable)
&& pre.data != post.data
{
if pre.executable {
return Err(InstructionError::ExecutableDataModified);
} else if self.is_writable {
} else if is_writable {
return Err(InstructionError::ExternalAccountDataModified);
} else {
return Err(InstructionError::ReadonlyDataModified);
@ -134,7 +139,7 @@ impl PreAccount {
if !rent.is_exempt(post.lamports, post.data.len()) {
return Err(InstructionError::ExecutableAccountNotRentExempt);
}
if !self.is_writable // line coverage used to get branch coverage
if !is_writable // line coverage used to get branch coverage
|| pre.executable
|| *program_id != pre.owner
{
@ -268,15 +273,20 @@ impl<'a> InvokeContext for ThisInvokeContext<'a> {
message: &Message,
instruction: &CompiledInstruction,
accounts: &[Rc<RefCell<Account>>],
caller_privileges: Option<&[bool]>,
) -> Result<(), InstructionError> {
let track_writable_deescalation =
self.is_feature_active(&track_writable_deescalation::id());
match self.program_ids.last() {
Some(key) => MessageProcessor::verify_and_update(
Some(program_id) => MessageProcessor::verify_and_update(
message,
instruction,
&mut self.pre_accounts,
accounts,
key,
program_id,
&self.rent,
track_writable_deescalation,
caller_privileges,
),
None => Err(InstructionError::GenericError), // Should never happen
}
@ -577,6 +587,11 @@ impl MessageProcessor {
.iter()
.map(|seeds| Pubkey::create_program_address(&seeds, caller_program_id))
.collect::<Result<Vec<_>, solana_sdk::pubkey::PubkeyError>>()?;
let mut caller_privileges = keyed_accounts
.iter()
.map(|keyed_account| keyed_account.is_writable())
.collect::<Vec<bool>>();
caller_privileges.insert(0, false);
let (message, callee_program_id, _) =
Self::create_message(&instruction, &keyed_accounts, &signers)?;
let mut accounts = vec![];
@ -628,6 +643,7 @@ impl MessageProcessor {
&message,
&executable_accounts,
&accounts,
&caller_privileges,
invoke_context,
)?;
@ -656,13 +672,19 @@ impl MessageProcessor {
message: &Message,
executable_accounts: &[(Pubkey, RefCell<Account>)],
accounts: &[Rc<RefCell<Account>>],
caller_privileges: &[bool],
invoke_context: &mut dyn InvokeContext,
) -> Result<(), InstructionError> {
if let Some(instruction) = message.instructions.get(0) {
let program_id = instruction.program_id(&message.account_keys);
// Verify the calling program hasn't misbehaved
invoke_context.verify_and_update(message, instruction, accounts)?;
invoke_context.verify_and_update(
message,
instruction,
accounts,
Some(caller_privileges),
)?;
// Construct keyed accounts
let keyed_accounts =
@ -684,7 +706,7 @@ impl MessageProcessor {
);
if result.is_ok() {
// Verify the called program has not misbehaved
result = invoke_context.verify_and_update(message, instruction, accounts);
result = invoke_context.verify_and_update(message, instruction, accounts, None);
}
invoke_context.pop();
@ -706,10 +728,9 @@ impl MessageProcessor {
{
let mut work = |_unique_index: usize, account_index: usize| {
let key = &message.account_keys[account_index];
let is_signer = account_index < message.header.num_required_signatures as usize;
let is_writable = message.is_writable(account_index);
let account = accounts[account_index].borrow();
pre_accounts.push(PreAccount::new(key, &account, is_signer, is_writable));
pre_accounts.push(PreAccount::new(key, &account, is_writable));
Ok(())
};
let _ = instruction.visit_each_account(&mut work);
@ -750,7 +771,12 @@ impl MessageProcessor {
let account = accounts[account_index]
.try_borrow_mut()
.map_err(|_| InstructionError::AccountBorrowOutstanding)?;
pre_accounts[unique_index].verify(&program_id, rent, &account)?;
pre_accounts[unique_index].verify(
&program_id,
Some(message.is_writable(account_index)),
rent,
&account,
)?;
pre_sum += u128::from(pre_accounts[unique_index].lamports());
post_sum += u128::from(account.lamports);
Ok(())
@ -773,6 +799,8 @@ impl MessageProcessor {
accounts: &[Rc<RefCell<Account>>],
program_id: &Pubkey,
rent: &Rent,
track_writable_deescalation: bool,
caller_privileges: Option<&[bool]>,
) -> Result<(), InstructionError> {
// Verify the per-account instruction results
let (mut pre_sum, mut post_sum) = (0_u128, 0_u128);
@ -780,6 +808,15 @@ impl MessageProcessor {
if account_index < message.account_keys.len() && account_index < accounts.len() {
let key = &message.account_keys[account_index];
let account = &accounts[account_index];
let is_writable = if track_writable_deescalation {
Some(if let Some(caller_privileges) = caller_privileges {
caller_privileges[account_index]
} else {
message.is_writable(account_index)
})
} else {
None
};
// Find the matching PreAccount
for pre_account in pre_accounts.iter_mut() {
if *key == pre_account.key() {
@ -788,7 +825,7 @@ impl MessageProcessor {
.try_borrow_mut()
.map_err(|_| InstructionError::AccountBorrowOutstanding)?;
pre_account.verify(&program_id, &rent, &account)?;
pre_account.verify(&program_id, is_writable, &rent, &account)?;
pre_sum += u128::from(pre_account.lamports());
post_sum += u128::from(account.lamports);
@ -943,16 +980,11 @@ mod tests {
1,
&program_ids[i],
))));
pre_accounts.push(PreAccount::new(
&keys[i],
&accounts[i].borrow(),
false,
true,
))
pre_accounts.push(PreAccount::new(&keys[i], &accounts[i].borrow(), false))
}
let account = Account::new(1, 1, &solana_sdk::pubkey::Pubkey::default());
for program_id in program_ids.iter() {
pre_accounts.push(PreAccount::new(program_id, &account.clone(), false, true));
pre_accounts.push(PreAccount::new(program_id, &account.clone(), false));
}
let mut invoke_context = ThisInvokeContext::new(
@ -1000,7 +1032,7 @@ mod tests {
&solana_sdk::pubkey::Pubkey::default(),
))));
invoke_context
.verify_and_update(&message, &message.instructions[0], &these_accounts)
.verify_and_update(&message, &message.instructions[0], &these_accounts, None)
.unwrap();
assert_eq!(
invoke_context.pre_accounts[owned_index]
@ -1018,6 +1050,7 @@ mod tests {
&message,
&message.instructions[0],
&accounts[not_owned_index..owned_index + 1],
None
),
Err(InstructionError::ExternalAccountDataModified)
);
@ -1074,6 +1107,7 @@ mod tests {
struct Change {
program_id: Pubkey,
is_writable: bool,
rent: Rent,
pre: PreAccount,
post: Account,
@ -1083,6 +1117,7 @@ mod tests {
Self {
program_id: *program_id,
rent: Rent::default(),
is_writable: true,
pre: PreAccount::new(
&solana_sdk::pubkey::new_rand(),
&Account {
@ -1092,7 +1127,6 @@ mod tests {
..Account::default()
},
false,
true,
),
post: Account {
owner: *owner,
@ -1102,7 +1136,7 @@ mod tests {
}
}
pub fn read_only(mut self) -> Self {
self.pre.is_writable = false;
self.is_writable = false;
self
}
pub fn executable(mut self, pre: bool, post: bool) -> Self {
@ -1130,7 +1164,12 @@ mod tests {
self
}
pub fn verify(&self) -> Result<(), InstructionError> {
self.pre.verify(&self.program_id, &self.rent, &self.post)
self.pre.verify(
&self.program_id,
Some(self.is_writable),
&self.rent,
&self.post,
)
}
}
@ -1760,7 +1799,7 @@ mod tests {
#[test]
fn test_process_cross_program() {
#[derive(Serialize, Deserialize)]
#[derive(Debug, Serialize, Deserialize)]
enum MockInstruction {
NoopSuccess,
NoopFail,
@ -1802,17 +1841,16 @@ mod tests {
let mut program_account = Account::new(1, 0, &native_loader::id());
program_account.executable = true;
let executable_preaccount =
PreAccount::new(&callee_program_id, &program_account, false, true);
let executable_preaccount = PreAccount::new(&callee_program_id, &program_account, true);
let executable_accounts = vec![(callee_program_id, RefCell::new(program_account.clone()))];
let owned_key = solana_sdk::pubkey::new_rand();
let owned_account = Account::new(42, 1, &callee_program_id);
let owned_preaccount = PreAccount::new(&owned_key, &owned_account, false, true);
let owned_preaccount = PreAccount::new(&owned_key, &owned_account, true);
let not_owned_key = solana_sdk::pubkey::new_rand();
let not_owned_account = Account::new(84, 1, &solana_sdk::pubkey::new_rand());
let not_owned_preaccount = PreAccount::new(&not_owned_key, &not_owned_account, false, true);
let not_owned_preaccount = PreAccount::new(&not_owned_key, &not_owned_account, true);
#[allow(unused_mut)]
let mut accounts = vec![
@ -1851,11 +1889,18 @@ mod tests {
metas.clone(),
);
let message = Message::new(&[instruction], None);
let caller_privileges = message
.account_keys
.iter()
.enumerate()
.map(|(i, _)| message.is_writable(i))
.collect::<Vec<bool>>();
assert_eq!(
MessageProcessor::process_cross_program_instruction(
&message,
&executable_accounts,
&accounts,
&caller_privileges,
&mut invoke_context,
),
Err(InstructionError::ExternalAccountDataModified)
@ -1878,11 +1923,18 @@ mod tests {
for case in cases {
let instruction = Instruction::new(callee_program_id, &case.0, metas.clone());
let message = Message::new(&[instruction], None);
let caller_privileges = message
.account_keys
.iter()
.enumerate()
.map(|(i, _)| message.is_writable(i))
.collect::<Vec<bool>>();
assert_eq!(
MessageProcessor::process_cross_program_instruction(
&message,
&executable_accounts,
&accounts,
&caller_privileges,
&mut invoke_context,
),
case.1

View File

@ -58,11 +58,11 @@ example_helloworld() {
spl() {
(
set -x
rm -rf spl
git clone https://github.com/solana-labs/solana-program-library.git spl
# rm -rf spl
# git clone https://github.com/solana-labs/solana-program-library.git spl
cd spl
./patch.crates-io.sh "$solana_dir"
# ./patch.crates-io.sh "$solana_dir"
$cargo build
@ -71,9 +71,9 @@ spl() {
#$cargo test
#$cargo_test_bpf
$cargo_test_bpf --manifest-path token/program/Cargo.toml
$cargo_test_bpf --manifest-path associated-token-account/program/Cargo.toml
$cargo_test_bpf --manifest-path feature-proposal/program/Cargo.toml
# $cargo_test_bpf --manifest-path token/program/Cargo.toml
# $cargo_test_bpf --manifest-path associated-token-account/program/Cargo.toml
$cargo_test_bpf --manifest-path feature-proposal/program/Cargo.toml -- --nocapture
)
}
@ -102,6 +102,6 @@ serum_dex() {
}
_ example_helloworld
# _ example_helloworld
_ spl
_ serum_dex
# _ serum_dex

View File

@ -166,6 +166,10 @@ pub mod prevent_upgrade_and_invoke {
solana_sdk::declare_id!("BiNjYd8jCYDgAwMqP91uwZs6skWpuHtKrZbckuKESs8N");
}
pub mod track_writable_deescalation {
solana_sdk::declare_id!("HVPSxqskEtRLRT2ZeEMmkmt9FWqoFX4vrN6f5VaadLED");
}
lazy_static! {
/// Map of feature identifiers to user-visible description
pub static ref FEATURE_NAMES: HashMap<Pubkey, &'static str> = [
@ -197,15 +201,16 @@ lazy_static! {
(try_find_program_address_syscall_enabled::id(), "add try_find_program_address syscall"),
(warp_timestamp::id(), "warp timestamp to current, adjust bounding to 50% #14210 & #14531"),
(stake_program_v3::id(), "solana_stake_program v3"),
(max_cpi_instruction_size_ipv6_mtu::id(), "Max cross-program invocation size 1280"),
(limit_cpi_loader_invoke::id(), "Loader not authorized via CPI"),
(use_loaded_program_accounts::id(), "Use loaded program accounts"),
(abort_on_all_cpi_failures::id(), "Abort on all CPI failures"),
(use_loaded_executables::id(), "Use loaded executable accounts"),
(max_cpi_instruction_size_ipv6_mtu::id(), "max cross-program invocation size 1280"),
(limit_cpi_loader_invoke::id(), "loader not authorized via CPI"),
(use_loaded_program_accounts::id(), "use loaded program accounts"),
(abort_on_all_cpi_failures::id(), "abort on all CPI failures"),
(use_loaded_executables::id(), "use loaded executable accounts"),
(turbine_retransmit_peers_patch::id(), "turbine retransmit peers patch #14631"),
(prevent_upgrade_and_invoke::id(), "Prevent upgrade and invoke in same tx batch"),
(full_inflation::candidate_example::vote::id(), "Community vote allowing candidate_example to enable full inflation"),
(full_inflation::candidate_example::enable::id(), "Full inflation enabled by candidate_example"),
(prevent_upgrade_and_invoke::id(), "prevent upgrade and invoke in same tx batch"),
(full_inflation::candidate_example::vote::id(), "community vote allowing candidate_example to enable full inflation"),
(full_inflation::candidate_example::enable::id(), "full inflation enabled by candidate_example"),
(track_writable_deescalation::id(), "Track account writable deescalation"),
/*************** ADD NEW FEATURES HERE ***************/
]
.iter()

View File

@ -41,6 +41,7 @@ pub trait InvokeContext {
message: &Message,
instruction: &CompiledInstruction,
accounts: &[Rc<RefCell<Account>>],
caller_pivileges: Option<&[bool]>,
) -> Result<(), InstructionError>;
/// Get the program ID of the currently executing program
fn get_caller(&self) -> Result<&Pubkey, InstructionError>;
@ -340,6 +341,7 @@ impl InvokeContext for MockInvokeContext {
_message: &Message,
_instruction: &CompiledInstruction,
_accounts: &[Rc<RefCell<Account>>],
_caller_pivileges: Option<&[bool]>,
) -> Result<(), InstructionError> {
Ok(())
}