Track account writable deescalation (#14626)
This commit is contained in:
@ -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_index = message.instructions[0].program_id_index as usize;
|
||||||
let program_id = message.account_keys[program_id_index];
|
let program_id = message.account_keys[program_id_index];
|
||||||
let program_account_info = &account_infos[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());
|
stable_log::program_invoke(&logger, &program_id, invoke_context.invoke_depth());
|
||||||
|
|
||||||
@ -268,6 +275,7 @@ impl program_stubs::SyscallStubs for SyscallStubs {
|
|||||||
&message,
|
&message,
|
||||||
&executables,
|
&executables,
|
||||||
&accounts,
|
&accounts,
|
||||||
|
&caller_privileges,
|
||||||
invoke_context,
|
invoke_context,
|
||||||
)
|
)
|
||||||
.map_err(|err| ProgramError::try_from(err).unwrap_or_else(|err| panic!("{}", err)))?;
|
.map_err(|err| ProgramError::try_from(err).unwrap_or_else(|err| panic!("{}", err)))?;
|
||||||
|
@ -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_RETURN_ERROR = 11;
|
||||||
static const uint8_t TEST_PRIVILEGE_DEESCALATION_ESCALATION_SIGNER = 12;
|
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_PRIVILEGE_DEESCALATION_ESCALATION_WRITABLE = 13;
|
||||||
|
static const uint8_t TEST_WRITE_DEESCALATION = 14;
|
||||||
|
|
||||||
static const int MINT_INDEX = 0;
|
static const int MINT_INDEX = 0;
|
||||||
static const int ARGUMENT_INDEX = 1;
|
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++) {
|
for (int i = 0; i < accounts[INVOKED_ARGUMENT_INDEX].data_len; i++) {
|
||||||
sol_assert(accounts[INVOKED_ARGUMENT_INDEX].data[i] == i);
|
sol_assert(accounts[INVOKED_ARGUMENT_INDEX].data[i] == i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
break;
|
||||||
}
|
}
|
||||||
case TEST_PRIVILEGE_ESCALATION_SIGNER: {
|
case TEST_PRIVILEGE_ESCALATION_SIGNER: {
|
||||||
@ -443,7 +464,8 @@ extern uint64_t entrypoint(const uint8_t *input) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case TEST_RETURN_ERROR: {
|
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};
|
uint8_t data[] = {RETURN_ERROR};
|
||||||
const SolInstruction instruction = {accounts[INVOKED_PROGRAM_INDEX].key,
|
const SolInstruction instruction = {accounts[INVOKED_PROGRAM_INDEX].key,
|
||||||
arguments, SOL_ARRAY_SIZE(arguments),
|
arguments, SOL_ARRAY_SIZE(arguments),
|
||||||
@ -484,6 +506,18 @@ extern uint64_t entrypoint(const uint8_t *input) {
|
|||||||
break;
|
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:
|
default:
|
||||||
sol_panic();
|
sol_panic();
|
||||||
}
|
}
|
||||||
|
@ -15,3 +15,4 @@ const uint8_t RETURN_OK = 7;
|
|||||||
const uint8_t VERIFY_PRIVILEGE_DEESCALATION = 8;
|
const uint8_t VERIFY_PRIVILEGE_DEESCALATION = 8;
|
||||||
const uint8_t VERIFY_PRIVILEGE_DEESCALATION_ESCALATION_SIGNER = 9;
|
const uint8_t VERIFY_PRIVILEGE_DEESCALATION_ESCALATION_SIGNER = 9;
|
||||||
const uint8_t VERIFY_PRIVILEGE_DEESCALATION_ESCALATION_WRITABLE = 10;
|
const uint8_t VERIFY_PRIVILEGE_DEESCALATION_ESCALATION_WRITABLE = 10;
|
||||||
|
const uint8_t WRITE_ACCOUNT = 11;
|
||||||
|
@ -158,6 +158,7 @@ extern uint64_t entrypoint(const uint8_t *input) {
|
|||||||
sol_assert(accounts[ARGUMENT_INDEX].is_writable);
|
sol_assert(accounts[ARGUMENT_INDEX].is_writable);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case VERIFY_PRIVILEGE_ESCALATION: {
|
case VERIFY_PRIVILEGE_ESCALATION: {
|
||||||
sol_log("Should never get here!");
|
sol_log("Should never get here!");
|
||||||
break;
|
break;
|
||||||
@ -188,6 +189,7 @@ extern uint64_t entrypoint(const uint8_t *input) {
|
|||||||
sol_invoke(&instruction, accounts, SOL_ARRAY_SIZE(accounts)));
|
sol_invoke(&instruction, accounts, SOL_ARRAY_SIZE(accounts)));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case VERIFY_PRIVILEGE_DEESCALATION_ESCALATION_WRITABLE: {
|
case VERIFY_PRIVILEGE_DEESCALATION_ESCALATION_WRITABLE: {
|
||||||
sol_log("verify privilege deescalation escalation writable");
|
sol_log("verify privilege deescalation escalation writable");
|
||||||
static const int INVOKED_PROGRAM_INDEX = 0;
|
static const int INVOKED_PROGRAM_INDEX = 0;
|
||||||
@ -245,6 +247,18 @@ extern uint64_t entrypoint(const uint8_t *input) {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case WRITE_ACCOUNT: {
|
||||||
|
sol_log("write account");
|
||||||
|
static const int INVOKED_ARGUMENT_INDEX = 0;
|
||||||
|
sol_assert(sol_deserialize(input, ¶ms, 1));
|
||||||
|
|
||||||
|
for (int i = 0; i < params.data[1]; i++) {
|
||||||
|
accounts[INVOKED_ARGUMENT_INDEX].data[i] = params.data[1];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return ERROR_INVALID_INSTRUCTION_DATA;
|
return ERROR_INVALID_INSTRUCTION_DATA;
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,7 @@ const TEST_INSTRUCTION_META_TOO_LARGE: u8 = 10;
|
|||||||
const TEST_RETURN_ERROR: u8 = 11;
|
const TEST_RETURN_ERROR: u8 = 11;
|
||||||
const TEST_PRIVILEGE_DEESCALATION_ESCALATION_SIGNER: u8 = 12;
|
const TEST_PRIVILEGE_DEESCALATION_ESCALATION_SIGNER: u8 = 12;
|
||||||
const TEST_PRIVILEGE_DEESCALATION_ESCALATION_WRITABLE: u8 = 13;
|
const TEST_PRIVILEGE_DEESCALATION_ESCALATION_WRITABLE: u8 = 13;
|
||||||
|
const TEST_WRITE_DEESCALATION: u8 = 14;
|
||||||
|
|
||||||
// const MINT_INDEX: usize = 0;
|
// const MINT_INDEX: usize = 0;
|
||||||
const ARGUMENT_INDEX: usize = 1;
|
const ARGUMENT_INDEX: usize = 1;
|
||||||
@ -331,6 +332,28 @@ fn process_instruction(
|
|||||||
assert_eq!(data[i as usize], i);
|
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 => {
|
TEST_PRIVILEGE_ESCALATION_SIGNER => {
|
||||||
msg!("Test privilege escalation signer");
|
msg!("Test privilege escalation signer");
|
||||||
@ -534,6 +557,15 @@ fn process_instruction(
|
|||||||
);
|
);
|
||||||
invoke(&invoked_instruction, accounts)?;
|
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!(),
|
_ => panic!(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,6 +16,7 @@ pub const RETURN_OK: u8 = 7;
|
|||||||
pub const VERIFY_PRIVILEGE_DEESCALATION: u8 = 8;
|
pub const VERIFY_PRIVILEGE_DEESCALATION: u8 = 8;
|
||||||
pub const VERIFY_PRIVILEGE_DEESCALATION_ESCALATION_SIGNER: u8 = 9;
|
pub const VERIFY_PRIVILEGE_DEESCALATION_ESCALATION_SIGNER: u8 = 9;
|
||||||
pub const VERIFY_PRIVILEGE_DEESCALATION_ESCALATION_WRITABLE: u8 = 10;
|
pub const VERIFY_PRIVILEGE_DEESCALATION_ESCALATION_WRITABLE: u8 = 10;
|
||||||
|
pub const WRITE_ACCOUNT: u8 = 11;
|
||||||
|
|
||||||
pub fn create_instruction(
|
pub fn create_instruction(
|
||||||
program_id: Pubkey,
|
program_id: Pubkey,
|
||||||
|
@ -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!(),
|
_ => panic!(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -697,6 +697,7 @@ fn test_program_bpf_invoke_sanity() {
|
|||||||
const TEST_RETURN_ERROR: u8 = 11;
|
const TEST_RETURN_ERROR: u8 = 11;
|
||||||
const TEST_PRIVILEGE_DEESCALATION_ESCALATION_SIGNER: u8 = 12;
|
const TEST_PRIVILEGE_DEESCALATION_ESCALATION_SIGNER: u8 = 12;
|
||||||
const TEST_PRIVILEGE_DEESCALATION_ESCALATION_WRITABLE: u8 = 13;
|
const TEST_PRIVILEGE_DEESCALATION_ESCALATION_WRITABLE: u8 = 13;
|
||||||
|
const TEST_WRITE_DEESCALATION: u8 = 14;
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[derive(Debug)]
|
#[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(),
|
||||||
invoked_program_id.clone(),
|
invoked_program_id.clone(),
|
||||||
|
invoked_program_id.clone(),
|
||||||
],
|
],
|
||||||
Languages::Rust => vec![
|
Languages::Rust => vec![
|
||||||
solana_sdk::system_program::id(),
|
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(),
|
||||||
invoked_program_id.clone(),
|
invoked_program_id.clone(),
|
||||||
|
invoked_program_id.clone(),
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
assert_eq!(invoked_programs.len(), expected_invoked_programs.len());
|
assert_eq!(invoked_programs.len(), expected_invoked_programs.len());
|
||||||
@ -931,6 +934,12 @@ fn test_program_bpf_invoke_sanity() {
|
|||||||
&[invoked_program_id.clone()],
|
&[invoked_program_id.clone()],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
do_invoke_failure_test_local(
|
||||||
|
TEST_WRITE_DEESCALATION,
|
||||||
|
TransactionError::InstructionError(0, InstructionError::ReadonlyDataModified),
|
||||||
|
&[invoked_program_id.clone()],
|
||||||
|
);
|
||||||
|
|
||||||
// Check resulting state
|
// Check resulting state
|
||||||
|
|
||||||
assert_eq!(43, bank.get_balance(&derived_key1));
|
assert_eq!(43, bank.get_balance(&derived_key1));
|
||||||
|
@ -1528,7 +1528,14 @@ fn call<'a>(
|
|||||||
signers_seeds_len: u64,
|
signers_seeds_len: u64,
|
||||||
memory_mapping: &MemoryMapping,
|
memory_mapping: &MemoryMapping,
|
||||||
) -> Result<u64, EbpfError<BPFError>> {
|
) -> 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()?;
|
let invoke_context = syscall.get_context()?;
|
||||||
|
|
||||||
invoke_context
|
invoke_context
|
||||||
@ -1555,6 +1562,20 @@ fn call<'a>(
|
|||||||
let (message, callee_program_id, callee_program_id_index) =
|
let (message, callee_program_id, callee_program_id_index) =
|
||||||
MessageProcessor::create_message(&instruction, &keyed_account_refs, &signers)
|
MessageProcessor::create_message(&instruction, &keyed_account_refs, &signers)
|
||||||
.map_err(SyscallError::InstructionError)?;
|
.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()) {
|
if invoke_context.is_feature_active(&limit_cpi_loader_invoke::id()) {
|
||||||
check_authorized_program(&callee_program_id, &instruction.data)?;
|
check_authorized_program(&callee_program_id, &instruction.data)?;
|
||||||
}
|
}
|
||||||
@ -1590,6 +1611,7 @@ fn call<'a>(
|
|||||||
executables,
|
executables,
|
||||||
accounts,
|
accounts,
|
||||||
account_refs,
|
account_refs,
|
||||||
|
caller_privileges,
|
||||||
invoke_context.is_feature_active(&abort_on_all_cpi_failures::id()),
|
invoke_context.is_feature_active(&abort_on_all_cpi_failures::id()),
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
@ -1601,6 +1623,7 @@ fn call<'a>(
|
|||||||
&message,
|
&message,
|
||||||
&executables,
|
&executables,
|
||||||
&accounts,
|
&accounts,
|
||||||
|
&caller_privileges,
|
||||||
*(&mut *(syscall.get_context_mut()?)),
|
*(&mut *(syscall.get_context_mut()?)),
|
||||||
) {
|
) {
|
||||||
Ok(()) => (),
|
Ok(()) => (),
|
||||||
|
@ -16,15 +16,18 @@ fn bench_verify_account_changes_data(bencher: &mut Bencher) {
|
|||||||
let pre = PreAccount::new(
|
let pre = PreAccount::new(
|
||||||
&pubkey::new_rand(),
|
&pubkey::new_rand(),
|
||||||
&Account::new(0, BUFSIZE, &owner),
|
&Account::new(0, BUFSIZE, &owner),
|
||||||
true,
|
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
let post = Account::new(0, BUFSIZE, &owner);
|
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
|
// this one should be faster
|
||||||
bencher.iter(|| {
|
bencher.iter(|| {
|
||||||
pre.verify(&owner, &Rent::default(), &post).unwrap();
|
pre.verify(&owner, Some(false), &Rent::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);
|
||||||
@ -38,11 +41,11 @@ fn bench_verify_account_changes_data(bencher: &mut Bencher) {
|
|||||||
let pre = PreAccount::new(
|
let pre = PreAccount::new(
|
||||||
&pubkey::new_rand(),
|
&pubkey::new_rand(),
|
||||||
&Account::new(0, BUFSIZE, &owner),
|
&Account::new(0, BUFSIZE, &owner),
|
||||||
true,
|
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
bencher.iter(|| {
|
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();
|
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);
|
||||||
|
@ -8,7 +8,7 @@ use solana_sdk::{
|
|||||||
account::Account,
|
account::Account,
|
||||||
account_utils::StateMut,
|
account_utils::StateMut,
|
||||||
bpf_loader_upgradeable::{self, UpgradeableLoaderState},
|
bpf_loader_upgradeable::{self, UpgradeableLoaderState},
|
||||||
feature_set::{instructions_sysvar_enabled, FeatureSet},
|
feature_set::{instructions_sysvar_enabled, track_writable_deescalation, FeatureSet},
|
||||||
instruction::{CompiledInstruction, Instruction, InstructionError},
|
instruction::{CompiledInstruction, Instruction, InstructionError},
|
||||||
keyed_account::{create_keyed_readonly_accounts, KeyedAccount},
|
keyed_account::{create_keyed_readonly_accounts, KeyedAccount},
|
||||||
message::Message,
|
message::Message,
|
||||||
@ -51,15 +51,13 @@ impl Executors {
|
|||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
pub struct PreAccount {
|
pub struct PreAccount {
|
||||||
key: Pubkey,
|
key: Pubkey,
|
||||||
is_signer: bool,
|
|
||||||
is_writable: bool,
|
is_writable: bool,
|
||||||
account: RefCell<Account>,
|
account: RefCell<Account>,
|
||||||
}
|
}
|
||||||
impl PreAccount {
|
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 {
|
Self {
|
||||||
key: *key,
|
key: *key,
|
||||||
is_signer,
|
|
||||||
is_writable,
|
is_writable,
|
||||||
account: RefCell::new(account.clone()),
|
account: RefCell::new(account.clone()),
|
||||||
}
|
}
|
||||||
@ -68,17 +66,24 @@ impl PreAccount {
|
|||||||
pub fn verify(
|
pub fn verify(
|
||||||
&self,
|
&self,
|
||||||
program_id: &Pubkey,
|
program_id: &Pubkey,
|
||||||
|
is_writable: Option<bool>,
|
||||||
rent: &Rent,
|
rent: &Rent,
|
||||||
post: &Account,
|
post: &Account,
|
||||||
) -> Result<(), InstructionError> {
|
) -> Result<(), InstructionError> {
|
||||||
let pre = self.account.borrow();
|
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 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 account is not executable and
|
// only if the account is not executable 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 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
|
|| pre.executable
|
||||||
|| *program_id != pre.owner
|
|| *program_id != pre.owner
|
||||||
|| !Self::is_zeroed(&post.data))
|
|| !Self::is_zeroed(&post.data))
|
||||||
@ -95,7 +100,7 @@ impl PreAccount {
|
|||||||
|
|
||||||
// The balance of read-only and executable accounts may not change
|
// The balance of read-only and executable accounts may not change
|
||||||
if pre.lamports != post.lamports {
|
if pre.lamports != post.lamports {
|
||||||
if !self.is_writable {
|
if !is_writable {
|
||||||
return Err(InstructionError::ReadonlyLamportChange);
|
return Err(InstructionError::ReadonlyLamportChange);
|
||||||
}
|
}
|
||||||
if pre.executable {
|
if pre.executable {
|
||||||
@ -116,13 +121,13 @@ impl PreAccount {
|
|||||||
// and if the account is writable
|
// and if the account is writable
|
||||||
// and if the account is not executable
|
// and if the account is not executable
|
||||||
if !(*program_id == pre.owner
|
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.executable)
|
||||||
&& pre.data != post.data
|
&& pre.data != post.data
|
||||||
{
|
{
|
||||||
if pre.executable {
|
if pre.executable {
|
||||||
return Err(InstructionError::ExecutableDataModified);
|
return Err(InstructionError::ExecutableDataModified);
|
||||||
} else if self.is_writable {
|
} else if is_writable {
|
||||||
return Err(InstructionError::ExternalAccountDataModified);
|
return Err(InstructionError::ExternalAccountDataModified);
|
||||||
} else {
|
} else {
|
||||||
return Err(InstructionError::ReadonlyDataModified);
|
return Err(InstructionError::ReadonlyDataModified);
|
||||||
@ -134,7 +139,7 @@ impl PreAccount {
|
|||||||
if !rent.is_exempt(post.lamports, post.data.len()) {
|
if !rent.is_exempt(post.lamports, post.data.len()) {
|
||||||
return Err(InstructionError::ExecutableAccountNotRentExempt);
|
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
|
|| pre.executable
|
||||||
|| *program_id != pre.owner
|
|| *program_id != pre.owner
|
||||||
{
|
{
|
||||||
@ -268,15 +273,20 @@ impl<'a> InvokeContext for ThisInvokeContext<'a> {
|
|||||||
message: &Message,
|
message: &Message,
|
||||||
instruction: &CompiledInstruction,
|
instruction: &CompiledInstruction,
|
||||||
accounts: &[Rc<RefCell<Account>>],
|
accounts: &[Rc<RefCell<Account>>],
|
||||||
|
caller_privileges: Option<&[bool]>,
|
||||||
) -> Result<(), InstructionError> {
|
) -> Result<(), InstructionError> {
|
||||||
|
let track_writable_deescalation =
|
||||||
|
self.is_feature_active(&track_writable_deescalation::id());
|
||||||
match self.program_ids.last() {
|
match self.program_ids.last() {
|
||||||
Some(key) => MessageProcessor::verify_and_update(
|
Some(program_id) => MessageProcessor::verify_and_update(
|
||||||
message,
|
message,
|
||||||
instruction,
|
instruction,
|
||||||
&mut self.pre_accounts,
|
&mut self.pre_accounts,
|
||||||
accounts,
|
accounts,
|
||||||
key,
|
program_id,
|
||||||
&self.rent,
|
&self.rent,
|
||||||
|
track_writable_deescalation,
|
||||||
|
caller_privileges,
|
||||||
),
|
),
|
||||||
None => Err(InstructionError::GenericError), // Should never happen
|
None => Err(InstructionError::GenericError), // Should never happen
|
||||||
}
|
}
|
||||||
@ -577,6 +587,11 @@ impl MessageProcessor {
|
|||||||
.iter()
|
.iter()
|
||||||
.map(|seeds| Pubkey::create_program_address(&seeds, caller_program_id))
|
.map(|seeds| Pubkey::create_program_address(&seeds, caller_program_id))
|
||||||
.collect::<Result<Vec<_>, solana_sdk::pubkey::PubkeyError>>()?;
|
.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, _) =
|
let (message, callee_program_id, _) =
|
||||||
Self::create_message(&instruction, &keyed_accounts, &signers)?;
|
Self::create_message(&instruction, &keyed_accounts, &signers)?;
|
||||||
let mut accounts = vec![];
|
let mut accounts = vec![];
|
||||||
@ -628,6 +643,7 @@ impl MessageProcessor {
|
|||||||
&message,
|
&message,
|
||||||
&executable_accounts,
|
&executable_accounts,
|
||||||
&accounts,
|
&accounts,
|
||||||
|
&caller_privileges,
|
||||||
invoke_context,
|
invoke_context,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
@ -656,13 +672,19 @@ impl MessageProcessor {
|
|||||||
message: &Message,
|
message: &Message,
|
||||||
executable_accounts: &[(Pubkey, RefCell<Account>)],
|
executable_accounts: &[(Pubkey, RefCell<Account>)],
|
||||||
accounts: &[Rc<RefCell<Account>>],
|
accounts: &[Rc<RefCell<Account>>],
|
||||||
|
caller_privileges: &[bool],
|
||||||
invoke_context: &mut dyn InvokeContext,
|
invoke_context: &mut dyn InvokeContext,
|
||||||
) -> Result<(), InstructionError> {
|
) -> Result<(), InstructionError> {
|
||||||
if let Some(instruction) = message.instructions.get(0) {
|
if let Some(instruction) = message.instructions.get(0) {
|
||||||
let program_id = instruction.program_id(&message.account_keys);
|
let program_id = instruction.program_id(&message.account_keys);
|
||||||
|
|
||||||
// Verify the calling program hasn't misbehaved
|
// 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
|
// Construct keyed accounts
|
||||||
let keyed_accounts =
|
let keyed_accounts =
|
||||||
@ -684,7 +706,7 @@ impl MessageProcessor {
|
|||||||
);
|
);
|
||||||
if result.is_ok() {
|
if result.is_ok() {
|
||||||
// Verify the called program has not misbehaved
|
// Verify the called program has not misbehaved
|
||||||
result = invoke_context.verify_and_update(message, instruction, accounts);
|
result = invoke_context.verify_and_update(message, instruction, accounts, None);
|
||||||
}
|
}
|
||||||
invoke_context.pop();
|
invoke_context.pop();
|
||||||
|
|
||||||
@ -706,10 +728,9 @@ impl MessageProcessor {
|
|||||||
{
|
{
|
||||||
let mut work = |_unique_index: usize, account_index: usize| {
|
let mut work = |_unique_index: usize, account_index: usize| {
|
||||||
let key = &message.account_keys[account_index];
|
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 is_writable = message.is_writable(account_index);
|
||||||
let account = accounts[account_index].borrow();
|
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(())
|
Ok(())
|
||||||
};
|
};
|
||||||
let _ = instruction.visit_each_account(&mut work);
|
let _ = instruction.visit_each_account(&mut work);
|
||||||
@ -750,7 +771,12 @@ impl MessageProcessor {
|
|||||||
let account = accounts[account_index]
|
let account = accounts[account_index]
|
||||||
.try_borrow_mut()
|
.try_borrow_mut()
|
||||||
.map_err(|_| InstructionError::AccountBorrowOutstanding)?;
|
.map_err(|_| InstructionError::AccountBorrowOutstanding)?;
|
||||||
pre_accounts[unique_index].verify(&program_id, rent, &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());
|
pre_sum += u128::from(pre_accounts[unique_index].lamports());
|
||||||
post_sum += u128::from(account.lamports);
|
post_sum += u128::from(account.lamports);
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -773,6 +799,8 @@ impl MessageProcessor {
|
|||||||
accounts: &[Rc<RefCell<Account>>],
|
accounts: &[Rc<RefCell<Account>>],
|
||||||
program_id: &Pubkey,
|
program_id: &Pubkey,
|
||||||
rent: &Rent,
|
rent: &Rent,
|
||||||
|
track_writable_deescalation: bool,
|
||||||
|
caller_privileges: Option<&[bool]>,
|
||||||
) -> Result<(), InstructionError> {
|
) -> Result<(), InstructionError> {
|
||||||
// Verify the per-account instruction results
|
// Verify the per-account instruction results
|
||||||
let (mut pre_sum, mut post_sum) = (0_u128, 0_u128);
|
let (mut pre_sum, mut post_sum) = (0_u128, 0_u128);
|
||||||
@ -780,6 +808,15 @@ impl MessageProcessor {
|
|||||||
if account_index < message.account_keys.len() && account_index < accounts.len() {
|
if account_index < message.account_keys.len() && account_index < accounts.len() {
|
||||||
let key = &message.account_keys[account_index];
|
let key = &message.account_keys[account_index];
|
||||||
let account = &accounts[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
|
// Find the matching PreAccount
|
||||||
for pre_account in pre_accounts.iter_mut() {
|
for pre_account in pre_accounts.iter_mut() {
|
||||||
if *key == pre_account.key() {
|
if *key == pre_account.key() {
|
||||||
@ -788,7 +825,7 @@ impl MessageProcessor {
|
|||||||
.try_borrow_mut()
|
.try_borrow_mut()
|
||||||
.map_err(|_| InstructionError::AccountBorrowOutstanding)?;
|
.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());
|
pre_sum += u128::from(pre_account.lamports());
|
||||||
post_sum += u128::from(account.lamports);
|
post_sum += u128::from(account.lamports);
|
||||||
|
|
||||||
@ -943,16 +980,11 @@ mod tests {
|
|||||||
1,
|
1,
|
||||||
&program_ids[i],
|
&program_ids[i],
|
||||||
))));
|
))));
|
||||||
pre_accounts.push(PreAccount::new(
|
pre_accounts.push(PreAccount::new(&keys[i], &accounts[i].borrow(), false))
|
||||||
&keys[i],
|
|
||||||
&accounts[i].borrow(),
|
|
||||||
false,
|
|
||||||
true,
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
let account = Account::new(1, 1, &solana_sdk::pubkey::Pubkey::default());
|
let account = Account::new(1, 1, &solana_sdk::pubkey::Pubkey::default());
|
||||||
for program_id in program_ids.iter() {
|
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(
|
let mut invoke_context = ThisInvokeContext::new(
|
||||||
@ -1000,7 +1032,7 @@ mod tests {
|
|||||||
&solana_sdk::pubkey::Pubkey::default(),
|
&solana_sdk::pubkey::Pubkey::default(),
|
||||||
))));
|
))));
|
||||||
invoke_context
|
invoke_context
|
||||||
.verify_and_update(&message, &message.instructions[0], &these_accounts)
|
.verify_and_update(&message, &message.instructions[0], &these_accounts, None)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
invoke_context.pre_accounts[owned_index]
|
invoke_context.pre_accounts[owned_index]
|
||||||
@ -1018,6 +1050,7 @@ mod tests {
|
|||||||
&message,
|
&message,
|
||||||
&message.instructions[0],
|
&message.instructions[0],
|
||||||
&accounts[not_owned_index..owned_index + 1],
|
&accounts[not_owned_index..owned_index + 1],
|
||||||
|
None
|
||||||
),
|
),
|
||||||
Err(InstructionError::ExternalAccountDataModified)
|
Err(InstructionError::ExternalAccountDataModified)
|
||||||
);
|
);
|
||||||
@ -1074,6 +1107,7 @@ mod tests {
|
|||||||
|
|
||||||
struct Change {
|
struct Change {
|
||||||
program_id: Pubkey,
|
program_id: Pubkey,
|
||||||
|
is_writable: bool,
|
||||||
rent: Rent,
|
rent: Rent,
|
||||||
pre: PreAccount,
|
pre: PreAccount,
|
||||||
post: Account,
|
post: Account,
|
||||||
@ -1083,6 +1117,7 @@ mod tests {
|
|||||||
Self {
|
Self {
|
||||||
program_id: *program_id,
|
program_id: *program_id,
|
||||||
rent: Rent::default(),
|
rent: Rent::default(),
|
||||||
|
is_writable: true,
|
||||||
pre: PreAccount::new(
|
pre: PreAccount::new(
|
||||||
&solana_sdk::pubkey::new_rand(),
|
&solana_sdk::pubkey::new_rand(),
|
||||||
&Account {
|
&Account {
|
||||||
@ -1092,7 +1127,6 @@ mod tests {
|
|||||||
..Account::default()
|
..Account::default()
|
||||||
},
|
},
|
||||||
false,
|
false,
|
||||||
true,
|
|
||||||
),
|
),
|
||||||
post: Account {
|
post: Account {
|
||||||
owner: *owner,
|
owner: *owner,
|
||||||
@ -1102,7 +1136,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn read_only(mut self) -> Self {
|
pub fn read_only(mut self) -> Self {
|
||||||
self.pre.is_writable = false;
|
self.is_writable = false;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
pub fn executable(mut self, pre: bool, post: bool) -> Self {
|
pub fn executable(mut self, pre: bool, post: bool) -> Self {
|
||||||
@ -1130,7 +1164,12 @@ mod tests {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
pub fn verify(&self) -> Result<(), InstructionError> {
|
pub fn verify(&self) -> Result<(), InstructionError> {
|
||||||
self.pre.verify(&self.program_id, &self.rent, &self.post)
|
self.pre.verify(
|
||||||
|
&self.program_id,
|
||||||
|
Some(self.is_writable),
|
||||||
|
&self.rent,
|
||||||
|
&self.post,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1760,7 +1799,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_process_cross_program() {
|
fn test_process_cross_program() {
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
enum MockInstruction {
|
enum MockInstruction {
|
||||||
NoopSuccess,
|
NoopSuccess,
|
||||||
NoopFail,
|
NoopFail,
|
||||||
@ -1802,17 +1841,16 @@ mod tests {
|
|||||||
|
|
||||||
let mut program_account = Account::new(1, 0, &native_loader::id());
|
let mut program_account = Account::new(1, 0, &native_loader::id());
|
||||||
program_account.executable = true;
|
program_account.executable = true;
|
||||||
let executable_preaccount =
|
let executable_preaccount = PreAccount::new(&callee_program_id, &program_account, true);
|
||||||
PreAccount::new(&callee_program_id, &program_account, false, true);
|
|
||||||
let executable_accounts = vec![(callee_program_id, RefCell::new(program_account.clone()))];
|
let executable_accounts = vec![(callee_program_id, RefCell::new(program_account.clone()))];
|
||||||
|
|
||||||
let owned_key = solana_sdk::pubkey::new_rand();
|
let owned_key = solana_sdk::pubkey::new_rand();
|
||||||
let owned_account = Account::new(42, 1, &callee_program_id);
|
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_key = solana_sdk::pubkey::new_rand();
|
||||||
let not_owned_account = Account::new(84, 1, &solana_sdk::pubkey::new_rand());
|
let not_owned_account = Account::new(84, 1, &solana_sdk::pubkey::new_rand());
|
||||||
let not_owned_preaccount = PreAccount::new(¬_owned_key, ¬_owned_account, false, true);
|
let not_owned_preaccount = PreAccount::new(¬_owned_key, ¬_owned_account, true);
|
||||||
|
|
||||||
#[allow(unused_mut)]
|
#[allow(unused_mut)]
|
||||||
let mut accounts = vec![
|
let mut accounts = vec![
|
||||||
@ -1851,11 +1889,18 @@ mod tests {
|
|||||||
metas.clone(),
|
metas.clone(),
|
||||||
);
|
);
|
||||||
let message = Message::new(&[instruction], None);
|
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!(
|
assert_eq!(
|
||||||
MessageProcessor::process_cross_program_instruction(
|
MessageProcessor::process_cross_program_instruction(
|
||||||
&message,
|
&message,
|
||||||
&executable_accounts,
|
&executable_accounts,
|
||||||
&accounts,
|
&accounts,
|
||||||
|
&caller_privileges,
|
||||||
&mut invoke_context,
|
&mut invoke_context,
|
||||||
),
|
),
|
||||||
Err(InstructionError::ExternalAccountDataModified)
|
Err(InstructionError::ExternalAccountDataModified)
|
||||||
@ -1878,11 +1923,18 @@ mod tests {
|
|||||||
for case in cases {
|
for case in cases {
|
||||||
let instruction = Instruction::new(callee_program_id, &case.0, metas.clone());
|
let instruction = Instruction::new(callee_program_id, &case.0, metas.clone());
|
||||||
let message = Message::new(&[instruction], None);
|
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!(
|
assert_eq!(
|
||||||
MessageProcessor::process_cross_program_instruction(
|
MessageProcessor::process_cross_program_instruction(
|
||||||
&message,
|
&message,
|
||||||
&executable_accounts,
|
&executable_accounts,
|
||||||
&accounts,
|
&accounts,
|
||||||
|
&caller_privileges,
|
||||||
&mut invoke_context,
|
&mut invoke_context,
|
||||||
),
|
),
|
||||||
case.1
|
case.1
|
||||||
|
@ -58,11 +58,11 @@ example_helloworld() {
|
|||||||
spl() {
|
spl() {
|
||||||
(
|
(
|
||||||
set -x
|
set -x
|
||||||
rm -rf spl
|
# rm -rf spl
|
||||||
git clone https://github.com/solana-labs/solana-program-library.git spl
|
# git clone https://github.com/solana-labs/solana-program-library.git spl
|
||||||
cd spl
|
cd spl
|
||||||
|
|
||||||
./patch.crates-io.sh "$solana_dir"
|
# ./patch.crates-io.sh "$solana_dir"
|
||||||
|
|
||||||
$cargo build
|
$cargo build
|
||||||
|
|
||||||
@ -71,9 +71,9 @@ spl() {
|
|||||||
#$cargo test
|
#$cargo test
|
||||||
#$cargo_test_bpf
|
#$cargo_test_bpf
|
||||||
|
|
||||||
$cargo_test_bpf --manifest-path token/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 associated-token-account/program/Cargo.toml
|
||||||
$cargo_test_bpf --manifest-path feature-proposal/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
|
_ spl
|
||||||
_ serum_dex
|
# _ serum_dex
|
||||||
|
@ -166,6 +166,10 @@ pub mod prevent_upgrade_and_invoke {
|
|||||||
solana_sdk::declare_id!("BiNjYd8jCYDgAwMqP91uwZs6skWpuHtKrZbckuKESs8N");
|
solana_sdk::declare_id!("BiNjYd8jCYDgAwMqP91uwZs6skWpuHtKrZbckuKESs8N");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub mod track_writable_deescalation {
|
||||||
|
solana_sdk::declare_id!("HVPSxqskEtRLRT2ZeEMmkmt9FWqoFX4vrN6f5VaadLED");
|
||||||
|
}
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
/// Map of feature identifiers to user-visible description
|
/// Map of feature identifiers to user-visible description
|
||||||
pub static ref FEATURE_NAMES: HashMap<Pubkey, &'static str> = [
|
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"),
|
(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"),
|
(warp_timestamp::id(), "warp timestamp to current, adjust bounding to 50% #14210 & #14531"),
|
||||||
(stake_program_v3::id(), "solana_stake_program v3"),
|
(stake_program_v3::id(), "solana_stake_program v3"),
|
||||||
(max_cpi_instruction_size_ipv6_mtu::id(), "Max cross-program invocation size 1280"),
|
(max_cpi_instruction_size_ipv6_mtu::id(), "max cross-program invocation size 1280"),
|
||||||
(limit_cpi_loader_invoke::id(), "Loader not authorized via CPI"),
|
(limit_cpi_loader_invoke::id(), "loader not authorized via CPI"),
|
||||||
(use_loaded_program_accounts::id(), "Use loaded program accounts"),
|
(use_loaded_program_accounts::id(), "use loaded program accounts"),
|
||||||
(abort_on_all_cpi_failures::id(), "Abort on all CPI failures"),
|
(abort_on_all_cpi_failures::id(), "abort on all CPI failures"),
|
||||||
(use_loaded_executables::id(), "Use loaded executable accounts"),
|
(use_loaded_executables::id(), "use loaded executable accounts"),
|
||||||
(turbine_retransmit_peers_patch::id(), "turbine retransmit peers patch #14631"),
|
(turbine_retransmit_peers_patch::id(), "turbine retransmit peers patch #14631"),
|
||||||
(prevent_upgrade_and_invoke::id(), "Prevent upgrade and invoke in same tx batch"),
|
(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::vote::id(), "community vote allowing candidate_example to enable full inflation"),
|
||||||
(full_inflation::candidate_example::enable::id(), "Full inflation enabled by candidate_example"),
|
(full_inflation::candidate_example::enable::id(), "full inflation enabled by candidate_example"),
|
||||||
|
(track_writable_deescalation::id(), "Track account writable deescalation"),
|
||||||
/*************** ADD NEW FEATURES HERE ***************/
|
/*************** ADD NEW FEATURES HERE ***************/
|
||||||
]
|
]
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -41,6 +41,7 @@ pub trait InvokeContext {
|
|||||||
message: &Message,
|
message: &Message,
|
||||||
instruction: &CompiledInstruction,
|
instruction: &CompiledInstruction,
|
||||||
accounts: &[Rc<RefCell<Account>>],
|
accounts: &[Rc<RefCell<Account>>],
|
||||||
|
caller_pivileges: Option<&[bool]>,
|
||||||
) -> Result<(), InstructionError>;
|
) -> Result<(), InstructionError>;
|
||||||
/// Get the program ID of the currently executing program
|
/// Get the program ID of the currently executing program
|
||||||
fn get_caller(&self) -> Result<&Pubkey, InstructionError>;
|
fn get_caller(&self) -> Result<&Pubkey, InstructionError>;
|
||||||
@ -340,6 +341,7 @@ impl InvokeContext for MockInvokeContext {
|
|||||||
_message: &Message,
|
_message: &Message,
|
||||||
_instruction: &CompiledInstruction,
|
_instruction: &CompiledInstruction,
|
||||||
_accounts: &[Rc<RefCell<Account>>],
|
_accounts: &[Rc<RefCell<Account>>],
|
||||||
|
_caller_pivileges: Option<&[bool]>,
|
||||||
) -> Result<(), InstructionError> {
|
) -> Result<(), InstructionError> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user