@ -7,7 +7,7 @@ use solana_rbpf::EbpfVm;
|
|||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
account::Account,
|
account::Account,
|
||||||
bpf_loader,
|
bpf_loader,
|
||||||
entrypoint_native::{ComputeMeter, InvokeContext, Logger, ProcessInstruction},
|
entrypoint_native::{ComputeBudget, ComputeMeter, InvokeContext, Logger, ProcessInstruction},
|
||||||
instruction::{CompiledInstruction, InstructionError},
|
instruction::{CompiledInstruction, InstructionError},
|
||||||
message::Message,
|
message::Message,
|
||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
@ -166,6 +166,9 @@ impl InvokeContext for MockInvokeContext {
|
|||||||
fn is_cross_program_supported(&self) -> bool {
|
fn is_cross_program_supported(&self) -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
fn get_compute_budget(&self) -> ComputeBudget {
|
||||||
|
ComputeBudget::default()
|
||||||
|
}
|
||||||
fn get_compute_meter(&self) -> Rc<RefCell<dyn ComputeMeter>> {
|
fn get_compute_meter(&self) -> Rc<RefCell<dyn ComputeMeter>> {
|
||||||
Rc::new(RefCell::new(self.mock_compute_meter.clone()))
|
Rc::new(RefCell::new(self.mock_compute_meter.clone()))
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@ use solana_rbpf::ebpf;
|
|||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
/// Error definitions
|
/// Error definitions
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error, PartialEq)]
|
||||||
pub enum VerifierError {
|
pub enum VerifierError {
|
||||||
/// ProgramLengthNotMultiple
|
/// ProgramLengthNotMultiple
|
||||||
#[error("program length must be a multiple of {} octets", ebpf::INSN_SIZE)]
|
#[error("program length must be a multiple of {} octets", ebpf::INSN_SIZE)]
|
||||||
|
@ -50,7 +50,7 @@ impl<E> DecodeError<E> for BPFLoaderError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Errors returned by functions the BPF Loader registers with the vM
|
/// Errors returned by functions the BPF Loader registers with the vM
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error, PartialEq)]
|
||||||
pub enum BPFError {
|
pub enum BPFError {
|
||||||
#[error("{0}")]
|
#[error("{0}")]
|
||||||
VerifierError(#[from] VerifierError),
|
VerifierError(#[from] VerifierError),
|
||||||
@ -253,9 +253,10 @@ pub fn process_instruction(
|
|||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
|
use solana_runtime::message_processor::ThisInvokeContext;
|
||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
account::Account,
|
account::Account,
|
||||||
entrypoint_native::{ComputeMeter, Logger, ProcessInstruction},
|
entrypoint_native::{ComputeBudget, Logger, ProcessInstruction},
|
||||||
instruction::CompiledInstruction,
|
instruction::CompiledInstruction,
|
||||||
message::Message,
|
message::Message,
|
||||||
rent::Rent,
|
rent::Rent,
|
||||||
@ -332,6 +333,9 @@ mod tests {
|
|||||||
fn is_cross_program_supported(&self) -> bool {
|
fn is_cross_program_supported(&self) -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
fn get_compute_budget(&self) -> ComputeBudget {
|
||||||
|
ComputeBudget::default()
|
||||||
|
}
|
||||||
fn get_compute_meter(&self) -> Rc<RefCell<dyn ComputeMeter>> {
|
fn get_compute_meter(&self) -> Rc<RefCell<dyn ComputeMeter>> {
|
||||||
Rc::new(RefCell::new(self.compute_meter.clone()))
|
Rc::new(RefCell::new(self.compute_meter.clone()))
|
||||||
}
|
}
|
||||||
@ -562,6 +566,29 @@ mod tests {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Case: limited budget
|
||||||
|
let program_id = Pubkey::default();
|
||||||
|
let mut invoke_context = ThisInvokeContext::new(
|
||||||
|
&program_id,
|
||||||
|
Rent::default(),
|
||||||
|
vec![],
|
||||||
|
vec![],
|
||||||
|
None,
|
||||||
|
true,
|
||||||
|
ComputeBudget {
|
||||||
|
max_units: 1,
|
||||||
|
log_units: 100,
|
||||||
|
log_64_units: 100,
|
||||||
|
create_program_address_units: 1500,
|
||||||
|
invoke_units: 1000,
|
||||||
|
max_invoke_depth: 2,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
Err(InstructionError::Custom(194969602)),
|
||||||
|
process_instruction(&bpf_loader::id(), &keyed_accounts, &[], &mut invoke_context)
|
||||||
|
);
|
||||||
|
|
||||||
// Case: With duplicate accounts
|
// Case: With duplicate accounts
|
||||||
let duplicate_key = Pubkey::new_rand();
|
let duplicate_key = Pubkey::new_rand();
|
||||||
let parameter_account = Account::new_ref(1, 0, &program_id);
|
let parameter_account = Account::new_ref(1, 0, &program_id);
|
||||||
|
@ -30,7 +30,7 @@ use std::{
|
|||||||
use thiserror::Error as ThisError;
|
use thiserror::Error as ThisError;
|
||||||
|
|
||||||
/// Error definitions
|
/// Error definitions
|
||||||
#[derive(Debug, ThisError)]
|
#[derive(Debug, ThisError, PartialEq)]
|
||||||
pub enum SyscallError {
|
pub enum SyscallError {
|
||||||
#[error("{0}: {1:?}")]
|
#[error("{0}: {1:?}")]
|
||||||
InvalidString(Utf8Error, Vec<u8>),
|
InvalidString(Utf8Error, Vec<u8>),
|
||||||
@ -59,13 +59,6 @@ impl From<SyscallError> for EbpfError<BPFError> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sysval compute costs
|
|
||||||
/// Note: `abort`, `sol_panic_`, and `sol_alloc_free_` do not currently incur a cost
|
|
||||||
const COMPUTE_COST_LOG: u64 = 100;
|
|
||||||
const COMPUTE_COST_LOG_64: u64 = 100;
|
|
||||||
const COMPUTE_COST_CREATE_PROGRAM_ADDRESS: u64 = 1000;
|
|
||||||
const COMPUTE_COST_INVOKE: u64 = 1000;
|
|
||||||
|
|
||||||
trait SyscallConsume {
|
trait SyscallConsume {
|
||||||
fn consume(&mut self, amount: u64) -> Result<(), EbpfError<BPFError>>;
|
fn consume(&mut self, amount: u64) -> Result<(), EbpfError<BPFError>>;
|
||||||
}
|
}
|
||||||
@ -97,6 +90,7 @@ pub fn register_syscalls<'a>(
|
|||||||
callers_keyed_accounts: &'a [KeyedAccount<'a>],
|
callers_keyed_accounts: &'a [KeyedAccount<'a>],
|
||||||
invoke_context: &'a mut dyn InvokeContext,
|
invoke_context: &'a mut dyn InvokeContext,
|
||||||
) -> Result<MemoryRegion, EbpfError<BPFError>> {
|
) -> Result<MemoryRegion, EbpfError<BPFError>> {
|
||||||
|
let compute_budget = invoke_context.get_compute_budget();
|
||||||
// Syscall functions common across languages
|
// Syscall functions common across languages
|
||||||
|
|
||||||
vm.register_syscall_ex("abort", syscall_abort)?;
|
vm.register_syscall_ex("abort", syscall_abort)?;
|
||||||
@ -104,6 +98,7 @@ pub fn register_syscalls<'a>(
|
|||||||
vm.register_syscall_with_context_ex(
|
vm.register_syscall_with_context_ex(
|
||||||
"sol_log_",
|
"sol_log_",
|
||||||
Box::new(SyscallLog {
|
Box::new(SyscallLog {
|
||||||
|
cost: compute_budget.log_units,
|
||||||
compute_meter: invoke_context.get_compute_meter(),
|
compute_meter: invoke_context.get_compute_meter(),
|
||||||
logger: invoke_context.get_logger(),
|
logger: invoke_context.get_logger(),
|
||||||
}),
|
}),
|
||||||
@ -111,6 +106,7 @@ pub fn register_syscalls<'a>(
|
|||||||
vm.register_syscall_with_context_ex(
|
vm.register_syscall_with_context_ex(
|
||||||
"sol_log_64_",
|
"sol_log_64_",
|
||||||
Box::new(SyscallLogU64 {
|
Box::new(SyscallLogU64 {
|
||||||
|
cost: compute_budget.log_64_units,
|
||||||
compute_meter: invoke_context.get_compute_meter(),
|
compute_meter: invoke_context.get_compute_meter(),
|
||||||
logger: invoke_context.get_logger(),
|
logger: invoke_context.get_logger(),
|
||||||
}),
|
}),
|
||||||
@ -119,6 +115,7 @@ pub fn register_syscalls<'a>(
|
|||||||
vm.register_syscall_with_context_ex(
|
vm.register_syscall_with_context_ex(
|
||||||
"sol_create_program_address",
|
"sol_create_program_address",
|
||||||
Box::new(SyscallCreateProgramAddress {
|
Box::new(SyscallCreateProgramAddress {
|
||||||
|
cost: compute_budget.create_program_address_units,
|
||||||
compute_meter: invoke_context.get_compute_meter(),
|
compute_meter: invoke_context.get_compute_meter(),
|
||||||
}),
|
}),
|
||||||
)?;
|
)?;
|
||||||
@ -282,6 +279,7 @@ pub fn syscall_sol_panic(
|
|||||||
|
|
||||||
/// Log a user's info message
|
/// Log a user's info message
|
||||||
pub struct SyscallLog {
|
pub struct SyscallLog {
|
||||||
|
cost: u64,
|
||||||
compute_meter: Rc<RefCell<dyn ComputeMeter>>,
|
compute_meter: Rc<RefCell<dyn ComputeMeter>>,
|
||||||
logger: Rc<RefCell<dyn Logger>>,
|
logger: Rc<RefCell<dyn Logger>>,
|
||||||
}
|
}
|
||||||
@ -296,7 +294,7 @@ impl SyscallObject<BPFError> for SyscallLog {
|
|||||||
ro_regions: &[MemoryRegion],
|
ro_regions: &[MemoryRegion],
|
||||||
_rw_regions: &[MemoryRegion],
|
_rw_regions: &[MemoryRegion],
|
||||||
) -> Result<u64, EbpfError<BPFError>> {
|
) -> Result<u64, EbpfError<BPFError>> {
|
||||||
self.compute_meter.consume(COMPUTE_COST_LOG)?;
|
self.compute_meter.consume(self.cost)?;
|
||||||
let mut logger = self
|
let mut logger = self
|
||||||
.logger
|
.logger
|
||||||
.try_borrow_mut()
|
.try_borrow_mut()
|
||||||
@ -313,6 +311,7 @@ impl SyscallObject<BPFError> for SyscallLog {
|
|||||||
|
|
||||||
/// Log 5 64-bit values
|
/// Log 5 64-bit values
|
||||||
pub struct SyscallLogU64 {
|
pub struct SyscallLogU64 {
|
||||||
|
cost: u64,
|
||||||
compute_meter: Rc<RefCell<dyn ComputeMeter>>,
|
compute_meter: Rc<RefCell<dyn ComputeMeter>>,
|
||||||
logger: Rc<RefCell<dyn Logger>>,
|
logger: Rc<RefCell<dyn Logger>>,
|
||||||
}
|
}
|
||||||
@ -327,7 +326,7 @@ impl SyscallObject<BPFError> for SyscallLogU64 {
|
|||||||
_ro_regions: &[MemoryRegion],
|
_ro_regions: &[MemoryRegion],
|
||||||
_rw_regions: &[MemoryRegion],
|
_rw_regions: &[MemoryRegion],
|
||||||
) -> Result<u64, EbpfError<BPFError>> {
|
) -> Result<u64, EbpfError<BPFError>> {
|
||||||
self.compute_meter.consume(COMPUTE_COST_LOG_64)?;
|
self.compute_meter.consume(self.cost)?;
|
||||||
let mut logger = self
|
let mut logger = self
|
||||||
.logger
|
.logger
|
||||||
.try_borrow_mut()
|
.try_borrow_mut()
|
||||||
@ -386,6 +385,7 @@ impl SyscallObject<BPFError> for SyscallSolAllocFree {
|
|||||||
|
|
||||||
/// Create a program address
|
/// Create a program address
|
||||||
pub struct SyscallCreateProgramAddress {
|
pub struct SyscallCreateProgramAddress {
|
||||||
|
cost: u64,
|
||||||
compute_meter: Rc<RefCell<dyn ComputeMeter>>,
|
compute_meter: Rc<RefCell<dyn ComputeMeter>>,
|
||||||
}
|
}
|
||||||
impl SyscallObject<BPFError> for SyscallCreateProgramAddress {
|
impl SyscallObject<BPFError> for SyscallCreateProgramAddress {
|
||||||
@ -399,8 +399,7 @@ impl SyscallObject<BPFError> for SyscallCreateProgramAddress {
|
|||||||
ro_regions: &[MemoryRegion],
|
ro_regions: &[MemoryRegion],
|
||||||
rw_regions: &[MemoryRegion],
|
rw_regions: &[MemoryRegion],
|
||||||
) -> Result<u64, EbpfError<BPFError>> {
|
) -> Result<u64, EbpfError<BPFError>> {
|
||||||
self.compute_meter
|
self.compute_meter.consume(self.cost)?;
|
||||||
.consume(COMPUTE_COST_CREATE_PROGRAM_ADDRESS)?;
|
|
||||||
|
|
||||||
let untranslated_seeds = translate_slice!(&[&u8], seeds_addr, seeds_len, ro_regions)?;
|
let untranslated_seeds = translate_slice!(&[&u8], seeds_addr, seeds_len, ro_regions)?;
|
||||||
let seeds = untranslated_seeds
|
let seeds = untranslated_seeds
|
||||||
@ -894,7 +893,7 @@ fn call<'a>(
|
|||||||
let mut invoke_context = syscall.get_context_mut()?;
|
let mut invoke_context = syscall.get_context_mut()?;
|
||||||
invoke_context
|
invoke_context
|
||||||
.get_compute_meter()
|
.get_compute_meter()
|
||||||
.consume(COMPUTE_COST_INVOKE)?;
|
.consume(invoke_context.get_compute_budget().invoke_units)?;
|
||||||
|
|
||||||
// Translate data passed from the VM
|
// Translate data passed from the VM
|
||||||
|
|
||||||
@ -1134,13 +1133,12 @@ mod tests {
|
|||||||
let addr = string.as_ptr() as *const _ as u64;
|
let addr = string.as_ptr() as *const _ as u64;
|
||||||
|
|
||||||
let compute_meter: Rc<RefCell<dyn ComputeMeter>> =
|
let compute_meter: Rc<RefCell<dyn ComputeMeter>> =
|
||||||
Rc::new(RefCell::new(MockComputeMeter {
|
Rc::new(RefCell::new(MockComputeMeter { remaining: 3 }));
|
||||||
remaining: std::u64::MAX, // TODO also test error
|
|
||||||
}));
|
|
||||||
let log = Rc::new(RefCell::new(vec![]));
|
let log = Rc::new(RefCell::new(vec![]));
|
||||||
let logger: Rc<RefCell<dyn Logger>> =
|
let logger: Rc<RefCell<dyn Logger>> =
|
||||||
Rc::new(RefCell::new(MockLogger { log: log.clone() }));
|
Rc::new(RefCell::new(MockLogger { log: log.clone() }));
|
||||||
let mut syscall_sol_log = SyscallLog {
|
let mut syscall_sol_log = SyscallLog {
|
||||||
|
cost: 1,
|
||||||
compute_meter,
|
compute_meter,
|
||||||
logger,
|
logger,
|
||||||
};
|
};
|
||||||
@ -1155,8 +1153,15 @@ mod tests {
|
|||||||
.call(100, string.len() as u64, 0, 0, 0, ro_regions, rw_regions)
|
.call(100, string.len() as u64, 0, 0, 0, ro_regions, rw_regions)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
syscall_sol_log
|
assert_eq!(
|
||||||
.call(
|
Err(EbpfError::AccessViolation(
|
||||||
|
"programs/bpf_loader/src/syscalls.rs".to_string(),
|
||||||
|
238,
|
||||||
|
100,
|
||||||
|
32,
|
||||||
|
" regions: \n0x64-0x73".to_string()
|
||||||
|
)),
|
||||||
|
syscall_sol_log.call(
|
||||||
100,
|
100,
|
||||||
string.len() as u64 * 2, // AccessViolation
|
string.len() as u64 * 2, // AccessViolation
|
||||||
0,
|
0,
|
||||||
@ -1165,7 +1170,22 @@ mod tests {
|
|||||||
ro_regions,
|
ro_regions,
|
||||||
rw_regions,
|
rw_regions,
|
||||||
)
|
)
|
||||||
.unwrap_err();
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
Err(EbpfError::UserError(BPFError::SyscallError(
|
||||||
|
SyscallError::InstructionError(InstructionError::ComputationalBudgetExceeded)
|
||||||
|
))),
|
||||||
|
syscall_sol_log.call(
|
||||||
|
100,
|
||||||
|
string.len() as u64 * 2, // AccessViolation
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
ro_regions,
|
||||||
|
rw_regions,
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
assert_eq!(log.borrow().len(), 1);
|
assert_eq!(log.borrow().len(), 1);
|
||||||
assert_eq!(log.borrow()[0], "Program log: Gaggablaghblagh!");
|
assert_eq!(log.borrow()[0], "Program log: Gaggablaghblagh!");
|
||||||
@ -1175,12 +1195,13 @@ mod tests {
|
|||||||
fn test_syscall_sol_log_u64() {
|
fn test_syscall_sol_log_u64() {
|
||||||
let compute_meter: Rc<RefCell<dyn ComputeMeter>> =
|
let compute_meter: Rc<RefCell<dyn ComputeMeter>> =
|
||||||
Rc::new(RefCell::new(MockComputeMeter {
|
Rc::new(RefCell::new(MockComputeMeter {
|
||||||
remaining: std::u64::MAX, // TODO also test error
|
remaining: std::u64::MAX,
|
||||||
}));
|
}));
|
||||||
let log = Rc::new(RefCell::new(vec![]));
|
let log = Rc::new(RefCell::new(vec![]));
|
||||||
let logger: Rc<RefCell<dyn Logger>> =
|
let logger: Rc<RefCell<dyn Logger>> =
|
||||||
Rc::new(RefCell::new(MockLogger { log: log.clone() }));
|
Rc::new(RefCell::new(MockLogger { log: log.clone() }));
|
||||||
let mut syscall_sol_log_u64 = SyscallLogU64 {
|
let mut syscall_sol_log_u64 = SyscallLogU64 {
|
||||||
|
cost: 0,
|
||||||
compute_meter,
|
compute_meter,
|
||||||
logger,
|
logger,
|
||||||
};
|
};
|
||||||
|
@ -13,7 +13,7 @@ use crate::{
|
|||||||
builtins::get_builtins,
|
builtins::get_builtins,
|
||||||
epoch_stakes::{EpochStakes, NodeVoteAccounts},
|
epoch_stakes::{EpochStakes, NodeVoteAccounts},
|
||||||
log_collector::LogCollector,
|
log_collector::LogCollector,
|
||||||
message_processor::{MessageProcessor, DEFAULT_COMPUTE_BUDGET, DEFAULT_MAX_INVOKE_DEPTH},
|
message_processor::MessageProcessor,
|
||||||
nonce_utils,
|
nonce_utils,
|
||||||
rent_collector::RentCollector,
|
rent_collector::RentCollector,
|
||||||
stakes::Stakes,
|
stakes::Stakes,
|
||||||
@ -35,7 +35,7 @@ use solana_sdk::{
|
|||||||
Epoch, Slot, SlotCount, SlotIndex, UnixTimestamp, DEFAULT_TICKS_PER_SECOND,
|
Epoch, Slot, SlotCount, SlotIndex, UnixTimestamp, DEFAULT_TICKS_PER_SECOND,
|
||||||
MAX_PROCESSING_AGE, MAX_RECENT_BLOCKHASHES, SECONDS_PER_DAY,
|
MAX_PROCESSING_AGE, MAX_RECENT_BLOCKHASHES, SECONDS_PER_DAY,
|
||||||
},
|
},
|
||||||
entrypoint_native::{ProcessInstruction, ProcessInstructionWithContext},
|
entrypoint_native::{ComputeBudget, ProcessInstruction, ProcessInstructionWithContext},
|
||||||
epoch_info::EpochInfo,
|
epoch_info::EpochInfo,
|
||||||
epoch_schedule::EpochSchedule,
|
epoch_schedule::EpochSchedule,
|
||||||
fee_calculator::{FeeCalculator, FeeRateGovernor},
|
fee_calculator::{FeeCalculator, FeeRateGovernor},
|
||||||
@ -1245,13 +1245,8 @@ impl Bank {
|
|||||||
.set_cross_program_support(is_supported);
|
.set_cross_program_support(is_supported);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_max_invoke_depth(&mut self, max_invoke_depth: usize) {
|
pub fn set_compute_budget(&mut self, budget: ComputeBudget) {
|
||||||
self.message_processor
|
self.message_processor.set_compute_budget(budget);
|
||||||
.set_max_invoke_depth(max_invoke_depth);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_compute_budget(&mut self, compute_units: u64) {
|
|
||||||
self.message_processor.set_compute_budget(compute_units);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the last block hash registered.
|
/// Return the last block hash registered.
|
||||||
@ -3210,8 +3205,7 @@ impl Bank {
|
|||||||
self.ensure_builtins(init_finish_or_warp);
|
self.ensure_builtins(init_finish_or_warp);
|
||||||
self.reinvoke_entered_epoch_callback(initiate_callback);
|
self.reinvoke_entered_epoch_callback(initiate_callback);
|
||||||
self.recheck_cross_program_support();
|
self.recheck_cross_program_support();
|
||||||
self.set_max_invoke_depth(DEFAULT_MAX_INVOKE_DEPTH);
|
self.recheck_compute_budget();
|
||||||
self.set_compute_budget(DEFAULT_COMPUTE_BUDGET);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ensure_builtins(&mut self, init_or_warp: bool) {
|
fn ensure_builtins(&mut self, init_or_warp: bool) {
|
||||||
@ -3240,6 +3234,27 @@ impl Bank {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn recheck_compute_budget(self: &mut Bank) {
|
||||||
|
let compute_budget = if OperatingMode::Stable == self.operating_mode() {
|
||||||
|
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 fix_recent_blockhashes_sysvar_delay(&self) -> bool {
|
fn fix_recent_blockhashes_sysvar_delay(&self) -> bool {
|
||||||
let activation_slot = match self.operating_mode() {
|
let activation_slot = match self.operating_mode() {
|
||||||
OperatingMode::Development => 0,
|
OperatingMode::Development => 0,
|
||||||
|
@ -7,8 +7,8 @@ use solana_sdk::{
|
|||||||
account::{create_keyed_readonly_accounts, Account, KeyedAccount},
|
account::{create_keyed_readonly_accounts, Account, KeyedAccount},
|
||||||
clock::Epoch,
|
clock::Epoch,
|
||||||
entrypoint_native::{
|
entrypoint_native::{
|
||||||
ComputeMeter, ErasedProcessInstruction, ErasedProcessInstructionWithContext, InvokeContext,
|
ComputeBudget, ComputeMeter, ErasedProcessInstruction, ErasedProcessInstructionWithContext,
|
||||||
Logger, ProcessInstruction, ProcessInstructionWithContext,
|
InvokeContext, Logger, ProcessInstruction, ProcessInstructionWithContext,
|
||||||
},
|
},
|
||||||
instruction::{CompiledInstruction, InstructionError},
|
instruction::{CompiledInstruction, InstructionError},
|
||||||
message::Message,
|
message::Message,
|
||||||
@ -20,9 +20,6 @@ use solana_sdk::{
|
|||||||
};
|
};
|
||||||
use std::{cell::RefCell, rc::Rc};
|
use std::{cell::RefCell, rc::Rc};
|
||||||
|
|
||||||
pub const DEFAULT_MAX_INVOKE_DEPTH: usize = 2;
|
|
||||||
pub const DEFAULT_COMPUTE_BUDGET: u64 = 100_000;
|
|
||||||
|
|
||||||
// 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
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
@ -183,7 +180,7 @@ pub struct ThisInvokeContext {
|
|||||||
programs: Vec<(Pubkey, ProcessInstruction)>,
|
programs: Vec<(Pubkey, ProcessInstruction)>,
|
||||||
logger: Rc<RefCell<dyn Logger>>,
|
logger: Rc<RefCell<dyn Logger>>,
|
||||||
is_cross_program_supported: bool,
|
is_cross_program_supported: bool,
|
||||||
max_invoke_depth: usize,
|
compute_budget: ComputeBudget,
|
||||||
compute_meter: Rc<RefCell<dyn ComputeMeter>>,
|
compute_meter: Rc<RefCell<dyn ComputeMeter>>,
|
||||||
}
|
}
|
||||||
impl ThisInvokeContext {
|
impl ThisInvokeContext {
|
||||||
@ -194,10 +191,9 @@ impl ThisInvokeContext {
|
|||||||
programs: Vec<(Pubkey, ProcessInstruction)>,
|
programs: Vec<(Pubkey, ProcessInstruction)>,
|
||||||
log_collector: Option<Rc<LogCollector>>,
|
log_collector: Option<Rc<LogCollector>>,
|
||||||
is_cross_program_supported: bool,
|
is_cross_program_supported: bool,
|
||||||
max_invoke_depth: usize,
|
compute_budget: ComputeBudget,
|
||||||
compute_budget: u64,
|
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let mut program_ids = Vec::with_capacity(max_invoke_depth);
|
let mut program_ids = Vec::with_capacity(compute_budget.max_invoke_depth);
|
||||||
program_ids.push(*program_id);
|
program_ids.push(*program_id);
|
||||||
Self {
|
Self {
|
||||||
program_ids,
|
program_ids,
|
||||||
@ -206,16 +202,16 @@ impl ThisInvokeContext {
|
|||||||
programs,
|
programs,
|
||||||
logger: Rc::new(RefCell::new(ThisLogger { log_collector })),
|
logger: Rc::new(RefCell::new(ThisLogger { log_collector })),
|
||||||
is_cross_program_supported,
|
is_cross_program_supported,
|
||||||
max_invoke_depth,
|
compute_budget,
|
||||||
compute_meter: Rc::new(RefCell::new(ThisComputeMeter {
|
compute_meter: Rc::new(RefCell::new(ThisComputeMeter {
|
||||||
remaining: compute_budget,
|
remaining: compute_budget.max_units,
|
||||||
})),
|
})),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl InvokeContext for ThisInvokeContext {
|
impl InvokeContext for ThisInvokeContext {
|
||||||
fn push(&mut self, key: &Pubkey) -> Result<(), InstructionError> {
|
fn push(&mut self, key: &Pubkey) -> Result<(), InstructionError> {
|
||||||
if self.program_ids.len() >= self.max_invoke_depth {
|
if self.program_ids.len() >= self.compute_budget.max_invoke_depth {
|
||||||
return Err(InstructionError::CallDepth);
|
return Err(InstructionError::CallDepth);
|
||||||
}
|
}
|
||||||
if self.program_ids.contains(key) && self.program_ids.last() != Some(key) {
|
if self.program_ids.contains(key) && self.program_ids.last() != Some(key) {
|
||||||
@ -260,6 +256,9 @@ impl InvokeContext for ThisInvokeContext {
|
|||||||
fn is_cross_program_supported(&self) -> bool {
|
fn is_cross_program_supported(&self) -> bool {
|
||||||
self.is_cross_program_supported
|
self.is_cross_program_supported
|
||||||
}
|
}
|
||||||
|
fn get_compute_budget(&self) -> ComputeBudget {
|
||||||
|
self.compute_budget
|
||||||
|
}
|
||||||
fn get_compute_meter(&self) -> Rc<RefCell<dyn ComputeMeter>> {
|
fn get_compute_meter(&self) -> Rc<RefCell<dyn ComputeMeter>> {
|
||||||
self.compute_meter.clone()
|
self.compute_meter.clone()
|
||||||
}
|
}
|
||||||
@ -290,9 +289,7 @@ pub struct MessageProcessor {
|
|||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
is_cross_program_supported: bool,
|
is_cross_program_supported: bool,
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
max_invoke_depth: usize,
|
compute_budget: ComputeBudget,
|
||||||
#[serde(skip)]
|
|
||||||
compute_budget: u64,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Debug for MessageProcessor {
|
impl std::fmt::Debug for MessageProcessor {
|
||||||
@ -339,11 +336,7 @@ impl Default for MessageProcessor {
|
|||||||
loaders: vec![],
|
loaders: vec![],
|
||||||
native_loader: NativeLoader::default(),
|
native_loader: NativeLoader::default(),
|
||||||
is_cross_program_supported: true,
|
is_cross_program_supported: true,
|
||||||
// Maximum cross-program invocation depth allowed including the orignal caller
|
compute_budget: ComputeBudget::default(),
|
||||||
max_invoke_depth: DEFAULT_MAX_INVOKE_DEPTH,
|
|
||||||
// Number of compute units that an instruction is allowed. Compute units
|
|
||||||
// are consumed by program execution, resources they use, etc...
|
|
||||||
compute_budget: DEFAULT_COMPUTE_BUDGET,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -391,11 +384,7 @@ impl MessageProcessor {
|
|||||||
self.is_cross_program_supported = is_supported;
|
self.is_cross_program_supported = is_supported;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_max_invoke_depth(&mut self, max_invoke_depth: usize) {
|
pub fn set_compute_budget(&mut self, compute_budget: ComputeBudget) {
|
||||||
self.max_invoke_depth = max_invoke_depth;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_compute_budget(&mut self, compute_budget: u64) {
|
|
||||||
self.compute_budget = compute_budget;
|
self.compute_budget = compute_budget;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -651,7 +640,6 @@ impl MessageProcessor {
|
|||||||
self.programs.clone(), // get rid of clone
|
self.programs.clone(), // get rid of clone
|
||||||
log_collector,
|
log_collector,
|
||||||
self.is_cross_program_supported,
|
self.is_cross_program_supported,
|
||||||
self.max_invoke_depth,
|
|
||||||
self.compute_budget,
|
self.compute_budget,
|
||||||
);
|
);
|
||||||
let keyed_accounts =
|
let keyed_accounts =
|
||||||
@ -742,8 +730,7 @@ mod tests {
|
|||||||
vec![],
|
vec![],
|
||||||
None,
|
None,
|
||||||
true,
|
true,
|
||||||
DEFAULT_MAX_INVOKE_DEPTH,
|
ComputeBudget::default(),
|
||||||
DEFAULT_COMPUTE_BUDGET,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// Check call depth increases and has a limit
|
// Check call depth increases and has a limit
|
||||||
@ -1514,8 +1501,7 @@ mod tests {
|
|||||||
vec![],
|
vec![],
|
||||||
None,
|
None,
|
||||||
true,
|
true,
|
||||||
DEFAULT_MAX_INVOKE_DEPTH,
|
ComputeBudget::default(),
|
||||||
DEFAULT_COMPUTE_BUDGET,
|
|
||||||
);
|
);
|
||||||
let metas = vec![
|
let metas = vec![
|
||||||
AccountMeta::new(owned_key, false),
|
AccountMeta::new(owned_key, false),
|
||||||
|
@ -210,10 +210,43 @@ pub trait InvokeContext {
|
|||||||
fn get_logger(&self) -> Rc<RefCell<dyn Logger>>;
|
fn get_logger(&self) -> Rc<RefCell<dyn Logger>>;
|
||||||
/// Are cross program invocations supported
|
/// Are cross program invocations supported
|
||||||
fn is_cross_program_supported(&self) -> bool;
|
fn is_cross_program_supported(&self) -> bool;
|
||||||
|
/// Get this invocation's compute budget
|
||||||
|
fn get_compute_budget(&self) -> ComputeBudget;
|
||||||
/// Get this invocation's compute meter
|
/// Get this invocation's compute meter
|
||||||
fn get_compute_meter(&self) -> Rc<RefCell<dyn ComputeMeter>>;
|
fn get_compute_meter(&self) -> Rc<RefCell<dyn ComputeMeter>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub struct ComputeBudget {
|
||||||
|
/// Number of compute units that an instruction is allowed. Compute units
|
||||||
|
/// are consumed by program execution, resources they use, etc...
|
||||||
|
pub max_units: u64,
|
||||||
|
/// Number of compute units consumed by a log call
|
||||||
|
pub log_units: u64,
|
||||||
|
/// Number of compute units consumed by a log_u64 call
|
||||||
|
pub log_64_units: u64,
|
||||||
|
/// Number of compute units consumed by a create_program_address call
|
||||||
|
pub create_program_address_units: u64,
|
||||||
|
/// Number of compute units consumed by an invoke call (not including the cost incured by
|
||||||
|
/// the called program)
|
||||||
|
pub invoke_units: u64,
|
||||||
|
/// Maximum cross-program invocation depth allowed including the orignal caller
|
||||||
|
pub max_invoke_depth: usize,
|
||||||
|
}
|
||||||
|
impl Default for ComputeBudget {
|
||||||
|
fn default() -> Self {
|
||||||
|
// Tuned for ~1ms
|
||||||
|
ComputeBudget {
|
||||||
|
max_units: 200_000,
|
||||||
|
log_units: 100,
|
||||||
|
log_64_units: 100,
|
||||||
|
create_program_address_units: 1500,
|
||||||
|
invoke_units: 1000,
|
||||||
|
max_invoke_depth: 2,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Compute meter
|
/// Compute meter
|
||||||
pub trait ComputeMeter {
|
pub trait ComputeMeter {
|
||||||
/// Consume compute units
|
/// Consume compute units
|
||||||
|
Reference in New Issue
Block a user