Pipe FeatureSet though InvokeContext (#12536)

* Pipe FeatureSet though InvokeContext

* gate program size cap

* nit
This commit is contained in:
Jack May 2020-09-29 14:36:30 -07:00 committed by GitHub
parent ce98088457
commit 74fcb184b2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 124 additions and 180 deletions

View File

@ -218,7 +218,7 @@ fn bench_instruction_count_tuner(_bencher: &mut Bencher) {
"Tuner must consume the whole budget"
);
println!(
"{:?} Consumed compute budget took {:?} us ({:?} instructions)",
"{:?} compute units took {:?} us ({:?} instructions)",
BUDGET - instruction_meter.get_remaining(),
measure.as_us(),
vm.get_total_instruction_count(),
@ -229,6 +229,7 @@ fn bench_instruction_count_tuner(_bencher: &mut Bencher) {
pub struct MockInvokeContext {
key: Pubkey,
logger: MockLogger,
compute_budget: ComputeBudget,
compute_meter: Rc<RefCell<MockComputeMeter>>,
}
impl InvokeContext for MockInvokeContext {
@ -253,11 +254,8 @@ impl InvokeContext for MockInvokeContext {
fn get_logger(&self) -> Rc<RefCell<dyn Logger>> {
Rc::new(RefCell::new(self.logger.clone()))
}
fn is_cross_program_supported(&self) -> bool {
true
}
fn get_compute_budget(&self) -> ComputeBudget {
ComputeBudget::default()
fn get_compute_budget(&self) -> &ComputeBudget {
&self.compute_budget
}
fn get_compute_meter(&self) -> Rc<RefCell<dyn ComputeMeter>> {
self.compute_meter.clone()
@ -267,6 +265,9 @@ impl InvokeContext for MockInvokeContext {
None
}
fn record_instruction(&self, _instruction: &Instruction) {}
fn is_feature_active(&self, _feature_id: &Pubkey) -> bool {
true
}
}
#[derive(Debug, Default, Clone)]
pub struct MockLogger {

View File

@ -23,7 +23,6 @@ use solana_sdk::{
client::SyncClient,
clock::{DEFAULT_SLOTS_PER_EPOCH, MAX_PROCESSING_AGE},
entrypoint::{MAX_PERMITTED_DATA_INCREASE, SUCCESS},
instruction::{AccountMeta, CompiledInstruction, Instruction, InstructionError},
message::Message,
pubkey::Pubkey,
@ -657,6 +656,7 @@ fn assert_instruction_count() {
struct MockInvokeContext {
pub key: Pubkey,
pub logger: MockLogger,
pub compute_budget: ComputeBudget,
pub compute_meter: MockComputeMeter,
}
impl InvokeContext for MockInvokeContext {
@ -681,11 +681,8 @@ impl InvokeContext for MockInvokeContext {
fn get_logger(&self) -> Rc<RefCell<dyn Logger>> {
Rc::new(RefCell::new(self.logger.clone()))
}
fn is_cross_program_supported(&self) -> bool {
true
}
fn get_compute_budget(&self) -> ComputeBudget {
ComputeBudget::default()
fn get_compute_budget(&self) -> &ComputeBudget {
&self.compute_budget
}
fn get_compute_meter(&self) -> Rc<RefCell<dyn ComputeMeter>> {
Rc::new(RefCell::new(self.compute_meter.clone()))
@ -695,6 +692,9 @@ impl InvokeContext for MockInvokeContext {
None
}
fn record_instruction(&self, _instruction: &Instruction) {}
fn is_feature_active(&self, _feature_id: &Pubkey) -> bool {
true
}
}
#[derive(Debug, Default, Clone)]

View File

@ -54,11 +54,11 @@ pub enum VerifierError {
InvalidRegister(usize),
}
fn check_prog_len(prog: &[u8]) -> Result<(), BPFError> {
fn check_prog_len(prog: &[u8], is_program_size_cap: bool) -> Result<(), BPFError> {
if prog.len() % ebpf::INSN_SIZE != 0 {
return Err(VerifierError::ProgramLengthNotMultiple.into());
}
if prog.len() > ebpf::PROG_MAX_SIZE {
if is_program_size_cap && prog.len() > ebpf::PROG_MAX_SIZE {
return Err(VerifierError::ProgramTooLarge(prog.len() / ebpf::INSN_SIZE).into());
}
@ -139,8 +139,8 @@ fn check_imm_register(insn: &ebpf::Insn, insn_ptr: usize) -> Result<(), Verifier
}
#[rustfmt::skip]
pub fn check(prog: &[u8]) -> Result<(), BPFError> {
check_prog_len(prog)?;
pub fn check(prog: &[u8], is_program_size_cap: bool) -> Result<(), BPFError> {
check_prog_len(prog, is_program_size_cap)?;
let mut insn_ptr: usize = 0;
while insn_ptr * ebpf::INSN_SIZE < prog.len() {

View File

@ -16,7 +16,10 @@ use solana_rbpf::{
memory_region::MemoryRegion,
vm::{EbpfVm, Executable, InstructionMeter},
};
use solana_runtime::process_instruction::{ComputeMeter, Executor, InvokeContext};
use solana_runtime::{
feature_set::compute_budget_config2,
process_instruction::{ComputeMeter, Executor, InvokeContext},
};
use solana_sdk::{
account::{is_executable, next_keyed_account, KeyedAccount},
bpf_loader, bpf_loader_deprecated,
@ -78,19 +81,29 @@ macro_rules! log{
};
}
fn map_ebpf_error(
invoke_context: &mut dyn InvokeContext,
e: EbpfError<BPFError>,
) -> InstructionError {
let logger = invoke_context.get_logger();
log!(logger, "{}", e);
InstructionError::InvalidAccountData
}
pub fn create_and_cache_executor(
program: &KeyedAccount,
invoke_context: &mut dyn InvokeContext,
) -> Result<Arc<BPFExecutor>, InstructionError> {
let executable = EbpfVm::create_executable_from_elf(
&program.try_account_ref()?.data,
Some(bpf_verifier::check),
let executable = EbpfVm::create_executable_from_elf(&program.try_account_ref()?.data, None)
.map_err(|e| map_ebpf_error(invoke_context, e))?;
let (_, elf_bytes) = executable
.get_text_bytes()
.map_err(|e| map_ebpf_error(invoke_context, e))?;
bpf_verifier::check(
elf_bytes,
!invoke_context.is_feature_active(&compute_budget_config2::id()),
)
.map_err(|e| {
let logger = invoke_context.get_logger();
log!(logger, "{}", e);
InstructionError::InvalidAccountData
})?;
.map_err(|e| map_ebpf_error(invoke_context, EbpfError::UserError(e)))?;
let executor = Arc::new(BPFExecutor { executable });
invoke_context.add_executor(program.unsigned_key(), executor.clone());
Ok(executor)
@ -271,6 +284,7 @@ mod tests {
use super::*;
use rand::Rng;
use solana_runtime::{
feature_set::FeatureSet,
message_processor::{Executors, ThisInvokeContext},
process_instruction::{ComputeBudget, Logger, ProcessInstruction},
};
@ -313,6 +327,7 @@ mod tests {
pub struct MockInvokeContext {
pub key: Pubkey,
pub logger: MockLogger,
pub compute_budget: ComputeBudget,
pub compute_meter: MockComputeMeter,
}
impl Default for MockInvokeContext {
@ -320,6 +335,7 @@ mod tests {
MockInvokeContext {
key: Pubkey::default(),
logger: MockLogger::default(),
compute_budget: ComputeBudget::default(),
compute_meter: MockComputeMeter {
remaining: std::u64::MAX,
},
@ -348,11 +364,8 @@ mod tests {
fn get_logger(&self) -> Rc<RefCell<dyn Logger>> {
Rc::new(RefCell::new(self.logger.clone()))
}
fn is_cross_program_supported(&self) -> bool {
true
}
fn get_compute_budget(&self) -> ComputeBudget {
ComputeBudget::default()
fn get_compute_budget(&self) -> &ComputeBudget {
&self.compute_budget
}
fn get_compute_meter(&self) -> Rc<RefCell<dyn ComputeMeter>> {
Rc::new(RefCell::new(self.compute_meter.clone()))
@ -362,6 +375,9 @@ mod tests {
None
}
fn record_instruction(&self, _instruction: &Instruction) {}
fn is_feature_active(&self, _feature_id: &Pubkey) -> bool {
true
}
}
struct TestInstructionMeter {
@ -387,8 +403,7 @@ mod tests {
];
let input = &mut [0x00];
let executable =
EbpfVm::create_executable_from_text_bytes(program, Some(bpf_verifier::check)).unwrap();
let executable = EbpfVm::create_executable_from_text_bytes(program, None).unwrap();
let mut vm = EbpfVm::<BPFError>::new(executable.as_ref()).unwrap();
let instruction_meter = TestInstructionMeter { remaining: 10 };
vm.execute_program_metered(input, &[], &[], instruction_meter)
@ -579,7 +594,6 @@ mod tests {
vec![],
vec![],
None,
true,
ComputeBudget {
max_units: 1,
log_units: 100,
@ -590,6 +604,7 @@ mod tests {
},
Rc::new(RefCell::new(Executors::default())),
None,
Arc::new(FeatureSet::default()),
);
assert_eq!(
Err(InstructionError::Custom(194969602)),

View File

@ -94,6 +94,7 @@ pub fn register_syscalls<'a>(
invoke_context: &'a mut dyn InvokeContext,
) -> Result<MemoryRegion, EbpfError<BPFError>> {
let compute_budget = invoke_context.get_compute_budget();
// Syscall functions common across languages
vm.register_syscall_ex("abort", syscall_abort)?;
@ -115,36 +116,35 @@ pub fn register_syscalls<'a>(
logger: invoke_context.get_logger(),
}),
)?;
if invoke_context.is_cross_program_supported() {
vm.register_syscall_with_context_ex(
"sol_create_program_address",
Box::new(SyscallCreateProgramAddress {
cost: compute_budget.create_program_address_units,
compute_meter: invoke_context.get_compute_meter(),
loader_id,
}),
)?;
// Cross-program invocation syscalls
vm.register_syscall_with_context_ex(
"sol_create_program_address",
Box::new(SyscallCreateProgramAddress {
cost: compute_budget.create_program_address_units,
compute_meter: invoke_context.get_compute_meter(),
loader_id,
}),
)?;
let invoke_context = Rc::new(RefCell::new(invoke_context));
vm.register_syscall_with_context_ex(
"sol_invoke_signed_c",
Box::new(SyscallInvokeSignedC {
callers_keyed_accounts,
invoke_context: invoke_context.clone(),
loader_id,
}),
)?;
vm.register_syscall_with_context_ex(
"sol_invoke_signed_rust",
Box::new(SyscallInvokeSignedRust {
callers_keyed_accounts,
invoke_context: invoke_context.clone(),
loader_id,
}),
)?;
}
// Cross-program invocation syscalls
let invoke_context = Rc::new(RefCell::new(invoke_context));
vm.register_syscall_with_context_ex(
"sol_invoke_signed_c",
Box::new(SyscallInvokeSignedC {
callers_keyed_accounts,
invoke_context: invoke_context.clone(),
loader_id,
}),
)?;
vm.register_syscall_with_context_ex(
"sol_invoke_signed_rust",
Box::new(SyscallInvokeSignedRust {
callers_keyed_accounts,
invoke_context: invoke_context.clone(),
loader_id,
}),
)?;
// Memory allocator
let heap = vec![0_u8; DEFAULT_HEAP_SIZE];
@ -1044,7 +1044,6 @@ fn call<'a>(
}
message_processor.add_loader(bpf_loader::id(), crate::process_instruction);
message_processor.add_loader(bpf_loader_deprecated::id(), crate::process_instruction);
message_processor.set_cross_program_support(invoke_context.is_cross_program_supported());
#[allow(clippy::deref_addrof)]
match message_processor.process_cross_program_instruction(

View File

@ -18,9 +18,7 @@ use crate::{
log_collector::LogCollector,
message_processor::{Executors, MessageProcessor},
nonce_utils,
process_instruction::{
ComputeBudget, Executor, ProcessInstruction, ProcessInstructionWithContext,
},
process_instruction::{Executor, ProcessInstruction, ProcessInstructionWithContext},
rent_collector::RentCollector,
stakes::Stakes,
status_cache::{SlotDelta, StatusCache},
@ -1476,15 +1474,6 @@ impl Bank {
debug!("Added native program {} under {:?}", name, program_id);
}
pub fn set_cross_program_support(&mut self, is_supported: bool) {
self.message_processor
.set_cross_program_support(is_supported);
}
pub fn set_compute_budget(&mut self, budget: ComputeBudget) {
self.message_processor.set_compute_budget(budget);
}
pub fn set_rent_burn_percentage(&mut self, burn_percent: u8) {
self.rent_collector.rent.burn_percent = burn_percent;
}
@ -2155,7 +2144,7 @@ impl Bank {
log_collector.clone(),
executors.clone(),
instruction_recorders.as_deref(),
&self.feature_set,
self.feature_set.clone(),
);
Self::compile_recorded_instructions(
@ -3610,8 +3599,6 @@ impl Bank {
}
self.ensure_feature_builtins(init_finish_or_warp, &new_feature_activations);
self.recheck_cross_program_support();
self.recheck_compute_budget();
self.reconfigure_token2_native_mint();
self.ensure_no_storage_rewards_pool();
}
@ -3675,35 +3662,6 @@ impl Bank {
}
}
fn recheck_cross_program_support(&mut self) {
if ClusterType::MainnetBeta == self.cluster_type() {
self.set_cross_program_support(self.epoch() >= 63);
} else {
self.set_cross_program_support(true);
}
}
fn recheck_compute_budget(&mut self) {
let compute_budget = if ClusterType::MainnetBeta == self.cluster_type() {
if self.epoch() >= u64::MAX - 1 {
ComputeBudget::default()
} else {
// Original
ComputeBudget {
max_units: 100_000,
log_units: 0,
log_64_units: 0,
create_program_address_units: 0,
invoke_units: 0,
max_invoke_depth: 2,
}
}
} else {
ComputeBudget::default()
};
self.set_compute_budget(compute_budget);
}
fn apply_spl_token_v2_multisig_fix(&mut self) {
if let Some(mut account) = self.get_account(&inline_spl_token_v2_0::id()) {
self.capitalization.fetch_sub(account.lamports, Relaxed);
@ -8701,19 +8659,6 @@ mod tests {
assert_eq!(bank.get_balance(&mint_keypair.pubkey()), 496); // no transaction fee charged
}
#[test]
fn test_finish_init() {
let (genesis_config, _mint_keypair) = create_genesis_config(100_000);
let mut bank = Bank::new(&genesis_config);
bank.message_processor = MessageProcessor::default();
bank.message_processor.set_cross_program_support(false);
// simulate bank is just after deserialized from snapshot
bank.finish_init(&genesis_config, None);
assert_eq!(bank.message_processor.get_cross_program_support(), true);
}
#[test]
fn test_add_builtin_program_no_overwrite() {
let (genesis_config, _mint_keypair) = create_genesis_config(100_000);

View File

@ -29,6 +29,10 @@ pub mod bpf_loader2_program {
solana_sdk::declare_id!("DFBnrgThdzH4W6wZ12uGPoWcMnvfZj11EHnxHcVxLPhD");
}
pub mod compute_budget_config2 {
solana_sdk::declare_id!("HxvjqDSiF5sYdSYuCXsUnS8UeAoWsMT9iGoFP8pgV1mB");
}
lazy_static! {
/// Map of feature identifiers to user-visible description
pub static ref FEATURE_NAMES: HashMap<Pubkey, &'static str> = [
@ -38,6 +42,7 @@ lazy_static! {
(pico_inflation::id(), "pico-inflation"),
(spl_token_v2_multisig_fix::id(), "spl-token multisig fix"),
(bpf_loader2_program::id(), "bpf_loader2 program"),
(compute_budget_config2::id(), "1ms compute budget"),
/*************** ADD NEW FEATURES HERE ***************/
]
.iter()
@ -78,12 +83,3 @@ impl Default for FeatureSet {
}
}
}
impl FeatureSet {
pub fn enabled() -> Self {
Self {
active: FEATURE_NAMES.keys().cloned().collect(),
inactive: HashSet::new(),
}
}
}

View File

@ -1,5 +1,5 @@
use crate::{
feature_set::{self, FeatureSet},
feature_set::{compute_budget_config2, instructions_sysvar_enabled, FeatureSet},
instruction_recorder::InstructionRecorder,
log_collector::LogCollector,
native_loader::NativeLoader,
@ -206,11 +206,11 @@ pub struct ThisInvokeContext {
pre_accounts: Vec<PreAccount>,
programs: Vec<(Pubkey, ProcessInstruction)>,
logger: Rc<RefCell<dyn Logger>>,
is_cross_program_supported: bool,
compute_budget: ComputeBudget,
compute_meter: Rc<RefCell<dyn ComputeMeter>>,
executors: Rc<RefCell<Executors>>,
instruction_recorder: Option<InstructionRecorder>,
feature_set: Arc<FeatureSet>,
}
impl ThisInvokeContext {
pub fn new(
@ -219,10 +219,10 @@ impl ThisInvokeContext {
pre_accounts: Vec<PreAccount>,
programs: Vec<(Pubkey, ProcessInstruction)>,
log_collector: Option<Rc<LogCollector>>,
is_cross_program_supported: bool,
compute_budget: ComputeBudget,
executors: Rc<RefCell<Executors>>,
instruction_recorder: Option<InstructionRecorder>,
feature_set: Arc<FeatureSet>,
) -> Self {
let mut program_ids = Vec::with_capacity(compute_budget.max_invoke_depth);
program_ids.push(*program_id);
@ -232,13 +232,13 @@ impl ThisInvokeContext {
pre_accounts,
programs,
logger: Rc::new(RefCell::new(ThisLogger { log_collector })),
is_cross_program_supported,
compute_budget,
compute_meter: Rc::new(RefCell::new(ThisComputeMeter {
remaining: compute_budget.max_units,
})),
executors,
instruction_recorder,
feature_set,
}
}
}
@ -286,11 +286,8 @@ impl InvokeContext for ThisInvokeContext {
fn get_logger(&self) -> Rc<RefCell<dyn Logger>> {
self.logger.clone()
}
fn is_cross_program_supported(&self) -> bool {
self.is_cross_program_supported
}
fn get_compute_budget(&self) -> ComputeBudget {
self.compute_budget
fn get_compute_budget(&self) -> &ComputeBudget {
&self.compute_budget
}
fn get_compute_meter(&self) -> Rc<RefCell<dyn ComputeMeter>> {
self.compute_meter.clone()
@ -306,6 +303,9 @@ impl InvokeContext for ThisInvokeContext {
recorder.record_instruction(instruction.clone());
}
}
fn is_feature_active(&self, feature_id: &Pubkey) -> bool {
self.feature_set.is_active(feature_id)
}
}
pub struct ThisLogger {
log_collector: Option<Rc<LogCollector>>,
@ -330,10 +330,6 @@ pub struct MessageProcessor {
loaders: Vec<(Pubkey, ProcessInstructionWithContext)>,
#[serde(skip)]
native_loader: NativeLoader,
#[serde(skip)]
is_cross_program_supported: bool,
#[serde(skip)]
compute_budget: ComputeBudget,
}
impl std::fmt::Debug for MessageProcessor {
@ -343,8 +339,6 @@ impl std::fmt::Debug for MessageProcessor {
programs: Vec<String>,
loaders: Vec<String>,
native_loader: &'a NativeLoader,
is_cross_program_supported: bool,
compute_budget: ComputeBudget,
}
// rustc doesn't compile due to bug without this work around
// https://github.com/rust-lang/rust/issues/50280
@ -367,8 +361,6 @@ impl std::fmt::Debug for MessageProcessor {
})
.collect::<Vec<_>>(),
native_loader: &self.native_loader,
is_cross_program_supported: self.is_cross_program_supported,
compute_budget: self.compute_budget,
};
write!(f, "{:?}", processor)
@ -381,8 +373,6 @@ impl Default for MessageProcessor {
programs: vec![],
loaders: vec![],
native_loader: NativeLoader::default(),
is_cross_program_supported: true,
compute_budget: ComputeBudget::default(),
}
}
}
@ -392,7 +382,6 @@ impl Clone for MessageProcessor {
programs: self.programs.clone(),
loaders: self.loaders.clone(),
native_loader: NativeLoader::default(),
..*self
}
}
}
@ -426,17 +415,20 @@ impl MessageProcessor {
}
}
pub fn set_cross_program_support(&mut self, is_supported: bool) {
self.is_cross_program_supported = is_supported;
}
pub fn set_compute_budget(&mut self, compute_budget: ComputeBudget) {
self.compute_budget = compute_budget;
}
#[cfg(test)]
pub fn get_cross_program_support(&mut self) -> bool {
self.is_cross_program_supported
fn get_compute_budget(feature_set: &FeatureSet) -> ComputeBudget {
if feature_set.is_active(&compute_budget_config2::id()) {
ComputeBudget::default()
} else {
// Original
ComputeBudget {
max_units: 100_000,
log_units: 0,
log_64_units: 0,
create_program_address_units: 0,
invoke_units: 0,
max_invoke_depth: 2,
}
}
}
/// Create the KeyedAccounts that will be passed to the program
@ -527,10 +519,6 @@ impl MessageProcessor {
accounts: &[Rc<RefCell<Account>>],
invoke_context: &mut dyn InvokeContext,
) -> Result<(), InstructionError> {
if !self.is_cross_program_supported {
return Err(InstructionError::ReentrancyNotAllowed);
}
let instruction = &message.instructions[0];
// Verify the calling program hasn't misbehaved
@ -681,11 +669,11 @@ impl MessageProcessor {
executors: Rc<RefCell<Executors>>,
instruction_recorder: Option<InstructionRecorder>,
instruction_index: usize,
feature_set: &FeatureSet,
feature_set: Arc<FeatureSet>,
) -> Result<(), InstructionError> {
// Fixup the special instructions key if present
// before the account pre-values are taken care of
if feature_set.is_active(&feature_set::instructions_sysvar_enabled::id()) {
if feature_set.is_active(&instructions_sysvar_enabled::id()) {
for (i, key) in message.account_keys.iter().enumerate() {
if solana_sdk::sysvar::instructions::check_id(key) {
let mut mut_account_ref = accounts[i].borrow_mut();
@ -705,10 +693,10 @@ impl MessageProcessor {
pre_accounts,
self.programs.clone(), // get rid of clone
log_collector,
self.is_cross_program_supported,
self.compute_budget,
Self::get_compute_budget(&feature_set),
executors,
instruction_recorder,
feature_set,
);
let keyed_accounts =
Self::create_keyed_accounts(message, instruction, executable_accounts, accounts)?;
@ -737,7 +725,7 @@ impl MessageProcessor {
log_collector: Option<Rc<LogCollector>>,
executors: Rc<RefCell<Executors>>,
instruction_recorders: Option<&[InstructionRecorder]>,
feature_set: &FeatureSet,
feature_set: Arc<FeatureSet>,
) -> Result<(), TransactionError> {
for (instruction_index, instruction) in message.instructions.iter().enumerate() {
let instruction_recorder = instruction_recorders
@ -753,7 +741,7 @@ impl MessageProcessor {
executors.clone(),
instruction_recorder,
instruction_index,
feature_set,
feature_set.clone(),
)
.map_err(|err| TransactionError::InstructionError(instruction_index as u8, err))?;
}
@ -798,10 +786,10 @@ mod tests {
pre_accounts,
vec![],
None,
true,
ComputeBudget::default(),
Rc::new(RefCell::new(Executors::default())),
None,
Arc::new(FeatureSet::default()),
);
// Check call depth increases and has a limit
@ -1338,7 +1326,7 @@ mod tests {
None,
executors.clone(),
None,
&FeatureSet::default(),
Arc::new(FeatureSet::default()),
);
assert_eq!(result, Ok(()));
assert_eq!(accounts[0].borrow().lamports, 100);
@ -1361,7 +1349,7 @@ mod tests {
None,
executors.clone(),
None,
&FeatureSet::default(),
Arc::new(FeatureSet::default()),
);
assert_eq!(
result,
@ -1388,7 +1376,7 @@ mod tests {
None,
executors,
None,
&FeatureSet::default(),
Arc::new(FeatureSet::default()),
);
assert_eq!(
result,
@ -1498,7 +1486,7 @@ mod tests {
None,
executors.clone(),
None,
&FeatureSet::default(),
Arc::new(FeatureSet::default()),
);
assert_eq!(
result,
@ -1525,7 +1513,7 @@ mod tests {
None,
executors.clone(),
None,
&FeatureSet::default(),
Arc::new(FeatureSet::default()),
);
assert_eq!(result, Ok(()));
@ -1549,7 +1537,7 @@ mod tests {
None,
executors,
None,
&FeatureSet::default(),
Arc::new(FeatureSet::default()),
);
assert_eq!(result, Ok(()));
assert_eq!(accounts[0].borrow().lamports, 80);
@ -1623,10 +1611,10 @@ mod tests {
vec![owned_preaccount, not_owned_preaccount],
vec![],
None,
true,
ComputeBudget::default(),
Rc::new(RefCell::new(Executors::default())),
None,
Arc::new(FeatureSet::default()),
);
let metas = vec![
AccountMeta::new(owned_key, false),

View File

@ -56,10 +56,8 @@ pub trait InvokeContext {
fn get_programs(&self) -> &[(Pubkey, ProcessInstruction)];
/// Get this invocation's logger
fn get_logger(&self) -> Rc<RefCell<dyn Logger>>;
/// Are cross program invocations supported
fn is_cross_program_supported(&self) -> bool;
/// Get this invocation's compute budget
fn get_compute_budget(&self) -> ComputeBudget;
fn get_compute_budget(&self) -> &ComputeBudget;
/// Get this invocation's compute meter
fn get_compute_meter(&self) -> Rc<RefCell<dyn ComputeMeter>>;
/// Loaders may need to do work in order to execute a program. Cache
@ -69,6 +67,8 @@ pub trait InvokeContext {
fn get_executor(&mut self, pubkey: &Pubkey) -> Option<Arc<dyn Executor>>;
/// Record invoked instruction
fn record_instruction(&self, instruction: &Instruction);
/// Get the bank's active feature set
fn is_feature_active(&self, feature_id: &Pubkey) -> bool;
}
#[derive(Clone, Copy, Debug)]