Bump solana_rbpf to 0.2.36 (#28794)

* Bumps solana_rbpf to v0.2.36

* Removes ThisInstructionMeter.

* Removes one "unsafe" expression.

* Removes redundant call to solana_rbpf:🧝:register_bpf_function().

* Adjusts SyscallFunction and SyscallRegistry.

* Inlines ProgramEnvironment into EbpfVm.

* Refactors trait SyscallConsume into fn consume_compute_meter().

* Inlines ComputeMeter into InvokeContext.

* Removes solana-metrics dependency from bpf_loader.

* Replaces RBPF tracer functionality by the debugger.

* Take compute_units_consumed from execute_program().

* Merges execute_program_interpreted() and execute_program_jit().
This commit is contained in:
Alexander Meißner 2022-11-15 15:21:11 +01:00 committed by GitHub
parent 84cfdf23fc
commit ff1ff587d1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 354 additions and 528 deletions

33
Cargo.lock generated
View File

@ -1715,6 +1715,20 @@ dependencies = [
"tempfile",
]
[[package]]
name = "gdbstub"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32c95766e0414f8bfc1d07055574c621b67739466d6ba516c4fef8e99d30d2e6"
dependencies = [
"bitflags",
"cfg-if 1.0.0",
"log",
"managed",
"num-traits",
"paste",
]
[[package]]
name = "gen-headers"
version = "1.15.0"
@ -2576,6 +2590,12 @@ dependencies = [
"libc",
]
[[package]]
name = "managed"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ca88d725a0a943b096803bd34e73a4437208b6077654cc4ecb2947a5f91618d"
[[package]]
name = "maplit"
version = "1.0.2"
@ -3116,6 +3136,12 @@ dependencies = [
"windows-sys 0.32.0",
]
[[package]]
name = "paste"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1de2e551fb905ac83f73f7aedf2f0cb4a0da7e35efa24a202a936269f1f18e1"
[[package]]
name = "pbkdf2"
version = "0.4.0"
@ -4747,7 +4773,6 @@ dependencies = [
"log",
"rand 0.7.3",
"solana-measure",
"solana-metrics",
"solana-program-runtime",
"solana-sdk 1.15.0",
"solana-zk-token-sdk 1.15.0",
@ -5877,6 +5902,7 @@ dependencies = [
"solana-measure",
"solana-metrics",
"solana-sdk 1.15.0",
"solana_rbpf",
"thiserror",
]
@ -6822,12 +6848,13 @@ dependencies = [
[[package]]
name = "solana_rbpf"
version = "0.2.35"
version = "0.2.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9bcad04ab8dff83d39e5b039e419d03e83dafc6643401700b61acf0cc1589ff8"
checksum = "94fe0b05d69875dc1da1e646aec8284c680b9b3f5f521c8d8f958b2cbc108d0b"
dependencies = [
"byteorder",
"combine",
"gdbstub",
"goblin",
"hash32",
"libc",

View File

@ -54,7 +54,7 @@ solana-tpu-client = { path = "../tpu-client", version = "=1.15.0" }
solana-transaction-status = { path = "../transaction-status", version = "=1.15.0" }
solana-version = { path = "../version", version = "=1.15.0" }
solana-vote-program = { path = "../programs/vote", version = "=1.15.0" }
solana_rbpf = "=0.2.35"
solana_rbpf = "=0.2.36"
spl-memo = { version = "=3.0.1", features = ["no-entrypoint"] }
thiserror = "1.0.31"
tiny-bip39 = "0.8.2"

View File

@ -10,7 +10,7 @@ use {
clap::{App, AppSettings, Arg, ArgMatches, SubCommand},
log::*,
solana_account_decoder::{UiAccountEncoding, UiDataSliceConfig},
solana_bpf_loader_program::{syscalls::register_syscalls, ThisInstructionMeter},
solana_bpf_loader_program::syscalls::register_syscalls,
solana_clap_utils::{self, input_parsers::*, input_validators::*, keypair::*},
solana_cli_output::{
CliProgram, CliProgramAccountType, CliProgramAuthority, CliProgramBuffer, CliProgramId,
@ -2004,7 +2004,7 @@ fn read_and_verify_elf(program_location: &str) -> Result<Vec<u8>, Box<dyn std::e
let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
// Verify the program
let executable = Executable::<ThisInstructionMeter>::from_elf(
let executable = Executable::<InvokeContext>::from_elf(
&program_data,
Config {
reject_broken_elfs: true,
@ -2014,8 +2014,7 @@ fn read_and_verify_elf(program_location: &str) -> Result<Vec<u8>, Box<dyn std::e
)
.map_err(|err| format!("ELF error: {}", err))?;
let _ =
VerifiedExecutable::<RequisiteVerifier, ThisInstructionMeter>::from_executable(executable)
let _ = VerifiedExecutable::<RequisiteVerifier, InvokeContext>::from_executable(executable)
.map_err(|err| format!("ELF error: {}", err))?;
Ok(program_data)

View File

@ -26,6 +26,7 @@ solana-frozen-abi = { path = "../frozen-abi", version = "=1.15.0" }
solana-frozen-abi-macro = { path = "../frozen-abi/macro", version = "=1.15.0" }
solana-measure = { path = "../measure", version = "=1.15.0" }
solana-metrics = { path = "../metrics", version = "=1.15.0" }
solana_rbpf = "=0.2.36"
solana-sdk = { path = "../sdk", version = "=1.15.0" }
thiserror = "1.0"

View File

@ -11,6 +11,7 @@ use {
timings::{ExecuteDetailsTimings, ExecuteTimings},
},
solana_measure::measure::Measure,
solana_rbpf::vm::ContextObject,
solana_sdk::{
account::{AccountSharedData, ReadableAccount},
bpf_loader_upgradeable::{self, UpgradeableLoaderState},
@ -58,33 +59,20 @@ impl std::fmt::Debug for BuiltinProgram {
}
}
/// Compute meter
pub struct ComputeMeter {
remaining: u64,
impl<'a> ContextObject for InvokeContext<'a> {
fn trace(&mut self, state: [u64; 12]) {
self.trace_log.push(state);
}
impl ComputeMeter {
/// Consume compute units
pub fn consume(&mut self, amount: u64) -> Result<(), InstructionError> {
let exceeded = self.remaining < amount;
self.remaining = self.remaining.saturating_sub(amount);
if exceeded {
return Err(InstructionError::ComputationalBudgetExceeded);
fn consume(&mut self, amount: u64) {
// 1 to 1 instruction to compute unit mapping
// ignore overflow, Ebpf will bail if exceeded
let mut compute_meter = self.compute_meter.borrow_mut();
*compute_meter = compute_meter.saturating_sub(amount);
}
Ok(())
}
/// Get the number of remaining compute units
pub fn get_remaining(&self) -> u64 {
self.remaining
}
/// Set compute units
///
/// Only use for tests and benchmarks
pub fn mock_set_remaining(&mut self, remaining: u64) {
self.remaining = remaining;
}
/// Construct a new one with the given remaining units
pub fn new_ref(remaining: u64) -> Rc<RefCell<Self>> {
Rc::new(RefCell::new(Self { remaining }))
fn get_remaining(&self) -> u64 {
*self.compute_meter.borrow()
}
}
@ -116,10 +104,11 @@ pub struct InvokeContext<'a> {
pre_accounts: Vec<PreAccount>,
builtin_programs: &'a [BuiltinProgram],
pub sysvar_cache: Cow<'a, SysvarCache>,
pub trace_log: Vec<[u64; 12]>,
log_collector: Option<Rc<RefCell<LogCollector>>>,
compute_budget: ComputeBudget,
current_compute_budget: ComputeBudget,
compute_meter: Rc<RefCell<ComputeMeter>>,
compute_meter: RefCell<u64>,
accounts_data_meter: AccountsDataMeter,
pub tx_executor_cache: Rc<RefCell<TransactionExecutorCache>>,
pub feature_set: Arc<FeatureSet>,
@ -150,10 +139,11 @@ impl<'a> InvokeContext<'a> {
pre_accounts: Vec::new(),
builtin_programs,
sysvar_cache,
trace_log: Vec::new(),
log_collector,
current_compute_budget: compute_budget,
compute_budget,
compute_meter: ComputeMeter::new_ref(compute_budget.compute_unit_limit),
compute_meter: RefCell::new(compute_budget.compute_unit_limit),
accounts_data_meter: AccountsDataMeter::new(prev_accounts_data_len),
tx_executor_cache,
feature_set,
@ -746,7 +736,7 @@ impl<'a> InvokeContext<'a> {
self.transaction_context
.set_return_data(program_id, Vec::new())?;
let pre_remaining_units = self.compute_meter.borrow().get_remaining();
let pre_remaining_units = self.get_remaining();
let result = if builtin_id == program_id {
let logger = self.get_log_collector();
stable_log::program_invoke(&logger, &program_id, self.get_stack_height());
@ -761,7 +751,7 @@ impl<'a> InvokeContext<'a> {
} else {
(entry.process_instruction)(first_instruction_account, self)
};
let post_remaining_units = self.compute_meter.borrow().get_remaining();
let post_remaining_units = self.get_remaining();
*compute_units_consumed = pre_remaining_units.saturating_sub(post_remaining_units);
process_executable_chain_time.stop();
@ -784,9 +774,22 @@ impl<'a> InvokeContext<'a> {
self.log_collector.clone()
}
/// Get this invocation's ComputeMeter
pub fn get_compute_meter(&self) -> Rc<RefCell<ComputeMeter>> {
self.compute_meter.clone()
/// Consume compute units
pub fn consume_checked(&self, amount: u64) -> Result<(), InstructionError> {
let mut compute_meter = self.compute_meter.borrow_mut();
let exceeded = *compute_meter < amount;
*compute_meter = compute_meter.saturating_sub(amount);
if exceeded {
return Err(InstructionError::ComputationalBudgetExceeded);
}
Ok(())
}
/// Set compute units
///
/// Only use for tests and benchmarks
pub fn mock_set_remaining(&self, remaining: u64) {
*self.compute_meter.borrow_mut() = remaining;
}
/// Get this invocation's AccountsDataMeter
@ -1138,10 +1141,7 @@ mod tests {
compute_units_to_consume,
desired_result,
} => {
invoke_context
.get_compute_meter()
.borrow_mut()
.consume(compute_units_to_consume)?;
invoke_context.consume_checked(compute_units_to_consume)?;
return desired_result;
}
MockInstruction::Resize { new_len } => instruction_context

View File

@ -35,7 +35,7 @@ use {
instruction::{Instruction, InstructionError},
native_token::sol_to_lamports,
poh_config::PohConfig,
program_error::{ProgramError, ACCOUNT_BORROW_FAILED, UNSUPPORTED_SYSVAR},
program_error::{ProgramError, UNSUPPORTED_SYSVAR},
pubkey::Pubkey,
rent::Rent,
signature::{Keypair, Signer},
@ -197,11 +197,7 @@ fn get_sysvar<T: Default + Sysvar + Sized + serde::de::DeserializeOwned + Clone>
) -> u64 {
let invoke_context = get_invoke_context();
if invoke_context
.get_compute_meter()
.try_borrow_mut()
.map_err(|_| ACCOUNT_BORROW_FAILED)
.unwrap()
.consume(invoke_context.get_compute_budget().sysvar_base_cost + T::size_of() as u64)
.consume_checked(invoke_context.get_compute_budget().sysvar_base_cost + T::size_of() as u64)
.is_err()
{
panic!("Exceeded compute budget");

View File

@ -15,11 +15,10 @@ byteorder = "1.4.3"
libsecp256k1 = "0.6.0"
log = "0.4.17"
solana-measure = { path = "../../measure", version = "=1.15.0" }
solana-metrics = { path = "../../metrics", version = "=1.15.0" }
solana-program-runtime = { path = "../../program-runtime", version = "=1.15.0" }
solana-sdk = { path = "../../sdk", version = "=1.15.0" }
solana-zk-token-sdk = { path = "../../zk-token-sdk", version = "=1.15.0" }
solana_rbpf = "=0.2.35"
solana_rbpf = "=0.2.36"
thiserror = "1.0"
[dev-dependencies]

View File

@ -25,6 +25,10 @@ impl BpfAllocator {
pos: 0,
}
}
pub fn get_heap(&mut self) -> &mut [u8] {
self.heap.as_slice_mut()
}
}
impl Alloc for BpfAllocator {

View File

@ -15,14 +15,13 @@ use {
serialization::{deserialize_parameters, serialize_parameters},
syscalls::SyscallError,
},
log::{log_enabled, trace, Level::Trace},
solana_measure::measure::Measure,
solana_program_runtime::{
compute_budget::ComputeBudget,
executor::{CreateMetrics, Executor},
executor_cache::TransactionExecutorCache,
ic_logger_msg, ic_msg,
invoke_context::{ComputeMeter, InvokeContext},
invoke_context::InvokeContext,
log_collector::LogCollector,
stable_log,
sysvar_cache::get_sysvar_with_account_check,
@ -33,9 +32,8 @@ use {
elf::Executable,
error::{EbpfError, UserDefinedError},
memory_region::MemoryRegion,
static_analysis::Analysis,
verifier::{RequisiteVerifier, VerifierError},
vm::{Config, EbpfVm, InstructionMeter, ProgramResult, VerifiedExecutable},
vm::{Config, ContextObject, EbpfVm, ProgramResult, VerifiedExecutable},
},
solana_sdk::{
bpf_loader, bpf_loader_deprecated,
@ -148,7 +146,7 @@ fn create_executor_from_bytes(
enable_stack_frame_gaps: true,
instruction_meter_checkpoint_distance: 10000,
enable_instruction_meter: true,
enable_instruction_tracing: log_enabled!(Trace),
enable_instruction_tracing: false,
enable_symbol_and_section_labels: false,
reject_broken_elfs: reject_deployment_of_broken_elfs,
noop_instruction_rate: 256,
@ -168,8 +166,7 @@ fn create_executor_from_bytes(
// Warning, do not use `Config::default()` so that configuration here is explicit.
};
let mut load_elf_time = Measure::start("load_elf_time");
let executable =
Executable::<ThisInstructionMeter>::from_elf(programdata, config, syscall_registry)
let executable = Executable::<InvokeContext>::from_elf(programdata, config, syscall_registry)
.map_err(|err| {
ic_logger_msg!(log_collector, "{}", err);
InstructionError::InvalidAccountData
@ -179,7 +176,7 @@ fn create_executor_from_bytes(
let executable = executable?;
let mut verify_code_time = Measure::start("verify_code_time");
let mut verified_executable =
VerifiedExecutable::<RequisiteVerifier, ThisInstructionMeter>::from_executable(executable)
VerifiedExecutable::<RequisiteVerifier, InvokeContext>::from_executable(executable)
.map_err(|err| {
ic_logger_msg!(log_collector, "{}", err);
InstructionError::InvalidAccountData
@ -316,22 +313,20 @@ fn check_loader_id(id: &Pubkey) -> bool {
/// Create the SBF virtual machine
pub fn create_vm<'a, 'b>(
program: &'a VerifiedExecutable<RequisiteVerifier, ThisInstructionMeter>,
program: &'a VerifiedExecutable<RequisiteVerifier, InvokeContext<'b>>,
regions: Vec<MemoryRegion>,
orig_account_lengths: Vec<usize>,
invoke_context: &'a mut InvokeContext<'b>,
) -> Result<EbpfVm<'a, RequisiteVerifier, ThisInstructionMeter>, EbpfError> {
) -> Result<EbpfVm<'a, RequisiteVerifier, InvokeContext<'b>>, EbpfError> {
let compute_budget = invoke_context.get_compute_budget();
let heap_size = compute_budget.heap_size.unwrap_or(HEAP_LENGTH);
let _ = invoke_context.get_compute_meter().borrow_mut().consume(
let _ = invoke_context.consume_checked(
((heap_size as u64).saturating_div(32_u64.saturating_mul(1024)))
.saturating_sub(1)
.saturating_mul(compute_budget.heap_cost),
);
let mut heap =
let heap =
AlignedMemory::<HOST_ALIGN>::zero_filled(compute_budget.heap_size.unwrap_or(HEAP_LENGTH));
let vm = EbpfVm::new(program, invoke_context, heap.as_slice_mut(), regions)?;
let check_aligned = bpf_loader_deprecated::id()
!= invoke_context
.transaction_context
@ -345,15 +340,22 @@ pub fn create_vm<'a, 'b>(
let check_size = invoke_context
.feature_set
.is_active(&check_slice_translation_size::id());
let allocator = Rc::new(RefCell::new(BpfAllocator::new(heap, MM_HEAP_START)));
invoke_context
.set_syscall_context(
check_aligned,
check_size,
orig_account_lengths,
Rc::new(RefCell::new(BpfAllocator::new(heap, MM_HEAP_START))),
allocator.clone(),
)
.map_err(SyscallError::InstructionError)?;
Ok(vm)
let result = EbpfVm::new(
program,
invoke_context,
allocator.borrow_mut().get_heap(),
regions,
);
result
}
pub fn process_instruction(
@ -1366,29 +1368,9 @@ fn process_loader_instruction(
Ok(())
}
/// Passed to the VM to enforce the compute budget
pub struct ThisInstructionMeter {
pub compute_meter: Rc<RefCell<ComputeMeter>>,
}
impl ThisInstructionMeter {
fn new(compute_meter: Rc<RefCell<ComputeMeter>>) -> Self {
Self { compute_meter }
}
}
impl InstructionMeter for ThisInstructionMeter {
fn consume(&mut self, amount: u64) {
// 1 to 1 instruction to compute unit mapping
// ignore error, Ebpf will bail if exceeded
let _ = self.compute_meter.borrow_mut().consume(amount);
}
fn get_remaining(&self) -> u64 {
self.compute_meter.borrow().get_remaining()
}
}
/// BPF Loader's Executor implementation
pub struct BpfExecutor {
verified_executable: VerifiedExecutable<RequisiteVerifier, ThisInstructionMeter>,
verified_executable: VerifiedExecutable<RequisiteVerifier, InvokeContext<'static>>,
use_jit: bool,
}
@ -1402,7 +1384,6 @@ impl Debug for BpfExecutor {
impl Executor for BpfExecutor {
fn execute(&self, invoke_context: &mut InvokeContext) -> Result<(), InstructionError> {
let log_collector = invoke_context.get_log_collector();
let compute_meter = invoke_context.get_compute_meter();
let stack_height = invoke_context.get_stack_height();
let transaction_context = &invoke_context.transaction_context;
let instruction_context = transaction_context.get_current_instruction_context()?;
@ -1421,8 +1402,11 @@ impl Executor for BpfExecutor {
let mut create_vm_time = Measure::start("create_vm");
let mut execute_time;
let execution_result = {
let compute_meter_prev = invoke_context.get_remaining();
let mut vm = match create_vm(
&self.verified_executable,
// We dropped the lifetime tracking in the Executor by setting it to 'static,
// thus we need to reintroduce the correct lifetime of InvokeContext here again.
unsafe { std::mem::transmute(&self.verified_executable) },
regions,
account_lengths,
invoke_context,
@ -1437,33 +1421,15 @@ impl Executor for BpfExecutor {
execute_time = Measure::start("execute");
stable_log::program_invoke(&log_collector, &program_id, stack_height);
let mut instruction_meter = ThisInstructionMeter::new(compute_meter.clone());
let before = compute_meter.borrow().get_remaining();
let result = if self.use_jit {
vm.execute_program_jit(&mut instruction_meter)
} else {
vm.execute_program_interpreted(&mut instruction_meter)
};
let after = compute_meter.borrow().get_remaining();
let (compute_units_consumed, result) = vm.execute_program(!self.use_jit);
drop(vm);
ic_logger_msg!(
log_collector,
"Program {} consumed {} of {} compute units",
&program_id,
before.saturating_sub(after),
before
compute_units_consumed,
compute_meter_prev
);
if log_enabled!(Trace) {
let mut trace_buffer = Vec::<u8>::new();
let analysis =
Analysis::from_executable(self.verified_executable.get_executable()).unwrap();
vm.get_program_environment()
.tracer
.write(&mut trace_buffer, &analysis)
.unwrap();
let trace_string = String::from_utf8(trace_buffer).unwrap();
trace!("SBF Program Instruction Trace:\n{}", trace_string);
}
drop(vm);
let (_returned_from_program_id, return_data) =
invoke_context.transaction_context.get_return_data();
if !return_data.is_empty() {
@ -1558,7 +1524,11 @@ mod tests {
super::*,
rand::Rng,
solana_program_runtime::invoke_context::mock_process_instruction,
solana_rbpf::{ebpf::MM_INPUT_START, verifier::Verifier, vm::SyscallRegistry},
solana_rbpf::{
ebpf::MM_INPUT_START,
verifier::Verifier,
vm::{ContextObject, FunctionRegistry, SyscallRegistry},
},
solana_sdk::{
account::{
create_account_shared_data_for_test as create_account_for_test, AccountSharedData,
@ -1574,10 +1544,11 @@ mod tests {
std::{fs::File, io::Read, ops::Range},
};
struct TestInstructionMeter {
struct TestContextObject {
remaining: u64,
}
impl InstructionMeter for TestInstructionMeter {
impl ContextObject for TestContextObject {
fn trace(&mut self, _state: [u64; 12]) {}
fn consume(&mut self, amount: u64) {
self.remaining = self.remaining.saturating_sub(amount);
}
@ -1621,7 +1592,11 @@ mod tests {
struct TautologyVerifier {}
impl Verifier for TautologyVerifier {
fn verify(_prog: &[u8], _config: &Config) -> std::result::Result<(), VerifierError> {
fn verify(
_prog: &[u8],
_config: &Config,
_function_registry: &FunctionRegistry,
) -> std::result::Result<(), VerifierError> {
Ok(())
}
}
@ -1638,16 +1613,8 @@ mod tests {
let mut input_mem = [0x00];
let config = Config::default();
let syscall_registry = SyscallRegistry::default();
let mut bpf_functions = std::collections::BTreeMap::<u32, (usize, String)>::new();
solana_rbpf::elf::register_bpf_function(
&config,
&mut bpf_functions,
&syscall_registry,
0,
"entrypoint",
)
.unwrap();
let executable = Executable::<TestInstructionMeter>::from_text_bytes(
let bpf_functions = std::collections::BTreeMap::<u32, (usize, String)>::new();
let executable = Executable::<TestContextObject>::from_text_bytes(
program,
config,
syscall_registry,
@ -1655,16 +1622,18 @@ mod tests {
)
.unwrap();
let verified_executable =
VerifiedExecutable::<TautologyVerifier, TestInstructionMeter>::from_executable(
executable,
)
VerifiedExecutable::<TautologyVerifier, TestContextObject>::from_executable(executable)
.unwrap();
let input_region = MemoryRegion::new_writable(&mut input_mem, MM_INPUT_START);
let mut vm =
EbpfVm::new(&verified_executable, &mut (), &mut [], vec![input_region]).unwrap();
let mut instruction_meter = TestInstructionMeter { remaining: 10 };
vm.execute_program_interpreted(&mut instruction_meter)
let mut context_object = TestContextObject { remaining: 10 };
let mut vm = EbpfVm::new(
&verified_executable,
&mut context_object,
&mut [],
vec![input_region],
)
.unwrap();
vm.execute_program(true).1.unwrap();
}
#[test]
@ -1673,7 +1642,7 @@ mod tests {
let prog = &[
0x18, 0x00, 0x00, 0x00, 0x88, 0x77, 0x66, 0x55, // first half of lddw
];
RequisiteVerifier::verify(prog, &Config::default()).unwrap();
RequisiteVerifier::verify(prog, &Config::default(), &FunctionRegistry::default()).unwrap();
}
#[test]
@ -1878,10 +1847,7 @@ mod tests {
None,
Err(InstructionError::ProgramFailedToComplete),
|first_instruction_account: IndexOfAccount, invoke_context: &mut InvokeContext| {
invoke_context
.get_compute_meter()
.borrow_mut()
.mock_set_remaining(0);
invoke_context.mock_set_remaining(0);
super::process_instruction(first_instruction_account, invoke_context)
},
);

View File

@ -101,7 +101,8 @@ impl SyscallInvokeSigned for SyscallInvokeSignedRust {
.feature_set
.is_active(&feature_set::loosen_cpi_size_restriction::id())
{
invoke_context.get_compute_meter().consume(
consume_compute_meter(
invoke_context,
(ix_data_len)
.saturating_div(invoke_context.get_compute_budget().cpi_bytes_per_unit),
)?;
@ -175,7 +176,8 @@ impl SyscallInvokeSigned for SyscallInvokeSignedRust {
invoke_context.get_check_aligned(),
)?;
invoke_context.get_compute_meter().consume(
consume_compute_meter(
invoke_context,
(data.len() as u64)
.saturating_div(invoke_context.get_compute_budget().cpi_bytes_per_unit),
)?;
@ -392,7 +394,8 @@ impl SyscallInvokeSigned for SyscallInvokeSignedC {
.feature_set
.is_active(&feature_set::loosen_cpi_size_restriction::id())
{
invoke_context.get_compute_meter().consume(
consume_compute_meter(
invoke_context,
(ix_data_len)
.saturating_div(invoke_context.get_compute_budget().cpi_bytes_per_unit),
)?;
@ -471,7 +474,8 @@ impl SyscallInvokeSigned for SyscallInvokeSignedC {
)?;
let vm_data_addr = account_info.data_addr;
invoke_context.get_compute_meter().consume(
consume_compute_meter(
invoke_context,
account_info
.data_len
.saturating_div(invoke_context.get_compute_budget().cpi_bytes_per_unit),
@ -630,7 +634,8 @@ where
.map_err(SyscallError::InstructionError)?;
if callee_account.is_executable() {
// Use the known account
invoke_context.get_compute_meter().consume(
consume_compute_meter(
invoke_context,
(callee_account.get_data().len() as u64)
.saturating_div(invoke_context.get_compute_budget().cpi_bytes_per_unit),
)?;
@ -862,9 +867,10 @@ fn cpi_common<S: SyscallInvokeSigned>(
signers_seeds_len: u64,
memory_mapping: &mut MemoryMapping,
) -> Result<u64, EbpfError> {
invoke_context
.get_compute_meter()
.consume(invoke_context.get_compute_budget().invoke_units)?;
consume_compute_meter(
invoke_context,
invoke_context.get_compute_budget().invoke_units,
)?;
// Translate and verify caller's data
let instruction = S::translate_instruction(instruction_addr, memory_mapping, invoke_context)?;

View File

@ -1,4 +1,4 @@
use {super::*, crate::declare_syscall};
use {super::*, crate::declare_syscall, solana_rbpf::vm::ContextObject};
declare_syscall!(
/// Log a user's info message
@ -16,7 +16,7 @@ declare_syscall!(
.get_compute_budget()
.syscall_base_cost
.max(len);
invoke_context.get_compute_meter().consume(cost)?;
consume_compute_meter(invoke_context, cost)?;
translate_string_and_do(
memory_mapping,
@ -46,7 +46,7 @@ declare_syscall!(
_memory_mapping: &mut MemoryMapping,
) -> Result<u64, EbpfError> {
let cost = invoke_context.get_compute_budget().log_64_units;
invoke_context.get_compute_meter().consume(cost)?;
consume_compute_meter(invoke_context, cost)?;
stable_log::program_log(
&invoke_context.get_log_collector(),
@ -72,12 +72,12 @@ declare_syscall!(
_memory_mapping: &mut MemoryMapping,
) -> Result<u64, EbpfError> {
let cost = invoke_context.get_compute_budget().syscall_base_cost;
invoke_context.get_compute_meter().consume(cost)?;
consume_compute_meter(invoke_context, cost)?;
ic_logger_msg!(
invoke_context.get_log_collector(),
"Program consumption: {} units remaining",
invoke_context.get_compute_meter().borrow().get_remaining()
invoke_context.get_remaining(),
);
Ok(0)
}
@ -96,7 +96,7 @@ declare_syscall!(
memory_mapping: &mut MemoryMapping,
) -> Result<u64, EbpfError> {
let cost = invoke_context.get_compute_budget().log_pubkey_units;
invoke_context.get_compute_meter().consume(cost)?;
consume_compute_meter(invoke_context, cost)?;
let pubkey = translate_type::<Pubkey>(
memory_mapping,
@ -122,9 +122,7 @@ declare_syscall!(
) -> Result<u64, EbpfError> {
let budget = invoke_context.get_compute_budget();
invoke_context
.get_compute_meter()
.consume(budget.syscall_base_cost)?;
consume_compute_meter(invoke_context, budget.syscall_base_cost)?;
let untranslated_fields = translate_slice::<&[u8]>(
memory_mapping,
@ -134,12 +132,14 @@ declare_syscall!(
invoke_context.get_check_size(),
)?;
invoke_context.get_compute_meter().consume(
consume_compute_meter(
invoke_context,
budget
.syscall_base_cost
.saturating_mul(untranslated_fields.len() as u64),
)?;
invoke_context.get_compute_meter().consume(
consume_compute_meter(
invoke_context,
untranslated_fields
.iter()
.fold(0, |total, e| total.saturating_add(e.len() as u64)),

View File

@ -5,7 +5,7 @@ fn mem_op_consume(invoke_context: &mut InvokeContext, n: u64) -> Result<(), Ebpf
let cost = compute_budget
.mem_op_base_cost
.max(n.saturating_div(compute_budget.cpi_bytes_per_unit));
invoke_context.get_compute_meter().consume(cost)
consume_compute_meter(invoke_context, cost)
}
declare_syscall!(

View File

@ -13,10 +13,7 @@ pub use self::{
use {
crate::BpfError,
solana_program_runtime::{
ic_logger_msg, ic_msg,
invoke_context::{ComputeMeter, InvokeContext},
stable_log,
timings::ExecuteTimings,
ic_logger_msg, ic_msg, invoke_context::InvokeContext, stable_log, timings::ExecuteTimings,
},
solana_rbpf::{
error::EbpfError,
@ -55,9 +52,7 @@ use {
},
std::{
alloc::Layout,
cell::RefCell,
mem::{align_of, size_of},
rc::Rc,
slice::from_raw_parts_mut,
str::{from_utf8, Utf8Error},
sync::Arc,
@ -129,18 +124,12 @@ impl From<SyscallError> for EbpfError {
}
}
trait SyscallConsume {
fn consume(&mut self, amount: u64) -> Result<(), EbpfError>;
}
impl SyscallConsume for Rc<RefCell<ComputeMeter>> {
fn consume(&mut self, amount: u64) -> Result<(), EbpfError> {
self.try_borrow_mut()
.map_err(|_| SyscallError::InvokeContextBorrowFailed)?
.consume(amount)
fn consume_compute_meter(invoke_context: &InvokeContext, amount: u64) -> Result<(), EbpfError> {
invoke_context
.consume_checked(amount)
.map_err(SyscallError::InstructionError)?;
Ok(())
}
}
macro_rules! register_feature_gated_syscall {
($syscall_registry:expr, $is_feature_active:expr, $name:expr, $call:expr $(,)?) => {
@ -152,10 +141,10 @@ macro_rules! register_feature_gated_syscall {
};
}
pub fn register_syscalls(
pub fn register_syscalls<'a>(
feature_set: &FeatureSet,
disable_deploy_of_alloc_free_syscall: bool,
) -> Result<SyscallRegistry, EbpfError> {
) -> Result<SyscallRegistry<InvokeContext<'a>>, EbpfError> {
let blake3_syscall_enabled = feature_set.is_active(&blake3_syscall_enabled::id());
let curve25519_syscall_enabled = feature_set.is_active(&curve25519_syscall_enabled::id());
let disable_fees_sysvar = feature_set.is_active(&disable_fees_sysvar::id());
@ -460,7 +449,7 @@ declare_syscall!(
_arg5: u64,
memory_mapping: &mut MemoryMapping,
) -> Result<u64, EbpfError> {
invoke_context.get_compute_meter().consume(len)?;
consume_compute_meter(invoke_context, len)?;
translate_string_and_do(
memory_mapping,
@ -572,7 +561,7 @@ declare_syscall!(
let cost = invoke_context
.get_compute_budget()
.create_program_address_units;
invoke_context.get_compute_meter().consume(cost)?;
consume_compute_meter(invoke_context, cost)?;
let (seeds, program_id) = translate_and_check_program_address_inputs(
seeds_addr,
@ -616,7 +605,7 @@ declare_syscall!(
let cost = invoke_context
.get_compute_budget()
.create_program_address_units;
invoke_context.get_compute_meter().consume(cost)?;
consume_compute_meter(invoke_context, cost)?;
let (seeds, program_id) = translate_and_check_program_address_inputs(
seeds_addr,
@ -665,7 +654,7 @@ declare_syscall!(
}
}
bump_seed[0] = bump_seed[0].saturating_sub(1);
invoke_context.get_compute_meter().consume(cost)?;
consume_compute_meter(invoke_context, cost)?;
}
Ok(1)
}
@ -694,9 +683,7 @@ declare_syscall!(
return Err(SyscallError::TooManySlices.into());
}
invoke_context
.get_compute_meter()
.consume(compute_budget.sha256_base_cost)?;
consume_compute_meter(invoke_context, compute_budget.sha256_base_cost)?;
let hash_result = translate_slice_mut::<u8>(
memory_mapping,
@ -727,7 +714,7 @@ declare_syscall!(
.sha256_byte_cost
.saturating_mul((val.len() as u64).saturating_div(2)),
);
invoke_context.get_compute_meter().consume(cost)?;
consume_compute_meter(invoke_context, cost)?;
hasher.hash(bytes);
}
}
@ -759,9 +746,7 @@ declare_syscall!(
return Err(SyscallError::TooManySlices.into());
}
invoke_context
.get_compute_meter()
.consume(compute_budget.sha256_base_cost)?;
consume_compute_meter(invoke_context, compute_budget.sha256_base_cost)?;
let hash_result = translate_slice_mut::<u8>(
memory_mapping,
@ -792,7 +777,7 @@ declare_syscall!(
.sha256_byte_cost
.saturating_mul((val.len() as u64).saturating_div(2)),
);
invoke_context.get_compute_meter().consume(cost)?;
consume_compute_meter(invoke_context, cost)?;
hasher.hash(bytes);
}
}
@ -814,7 +799,7 @@ declare_syscall!(
memory_mapping: &mut MemoryMapping,
) -> Result<u64, EbpfError> {
let cost = invoke_context.get_compute_budget().secp256k1_recover_cost;
invoke_context.get_compute_meter().consume(cost)?;
consume_compute_meter(invoke_context, cost)?;
let hash = translate_slice::<u8>(
memory_mapping,
@ -911,7 +896,7 @@ declare_syscall!(
let cost = invoke_context
.get_compute_budget()
.curve25519_edwards_validate_point_cost;
invoke_context.get_compute_meter().consume(cost)?;
consume_compute_meter(invoke_context, cost)?;
let point = translate_type::<edwards::PodEdwardsPoint>(
memory_mapping,
@ -929,7 +914,7 @@ declare_syscall!(
let cost = invoke_context
.get_compute_budget()
.curve25519_ristretto_validate_point_cost;
invoke_context.get_compute_meter().consume(cost)?;
consume_compute_meter(invoke_context, cost)?;
let point = translate_type::<ristretto::PodRistrettoPoint>(
memory_mapping,
@ -971,7 +956,7 @@ declare_syscall!(
let cost = invoke_context
.get_compute_budget()
.curve25519_edwards_add_cost;
invoke_context.get_compute_meter().consume(cost)?;
consume_compute_meter(invoke_context, cost)?;
let left_point = translate_type::<edwards::PodEdwardsPoint>(
memory_mapping,
@ -999,7 +984,7 @@ declare_syscall!(
let cost = invoke_context
.get_compute_budget()
.curve25519_edwards_subtract_cost;
invoke_context.get_compute_meter().consume(cost)?;
consume_compute_meter(invoke_context, cost)?;
let left_point = translate_type::<edwards::PodEdwardsPoint>(
memory_mapping,
@ -1027,7 +1012,7 @@ declare_syscall!(
let cost = invoke_context
.get_compute_budget()
.curve25519_edwards_multiply_cost;
invoke_context.get_compute_meter().consume(cost)?;
consume_compute_meter(invoke_context, cost)?;
let scalar = translate_type::<scalar::PodScalar>(
memory_mapping,
@ -1059,7 +1044,7 @@ declare_syscall!(
let cost = invoke_context
.get_compute_budget()
.curve25519_ristretto_add_cost;
invoke_context.get_compute_meter().consume(cost)?;
consume_compute_meter(invoke_context, cost)?;
let left_point = translate_type::<ristretto::PodRistrettoPoint>(
memory_mapping,
@ -1087,7 +1072,7 @@ declare_syscall!(
let cost = invoke_context
.get_compute_budget()
.curve25519_ristretto_subtract_cost;
invoke_context.get_compute_meter().consume(cost)?;
consume_compute_meter(invoke_context, cost)?;
let left_point = translate_type::<ristretto::PodRistrettoPoint>(
memory_mapping,
@ -1117,7 +1102,7 @@ declare_syscall!(
let cost = invoke_context
.get_compute_budget()
.curve25519_ristretto_multiply_cost;
invoke_context.get_compute_meter().consume(cost)?;
consume_compute_meter(invoke_context, cost)?;
let scalar = translate_type::<scalar::PodScalar>(
memory_mapping,
@ -1177,7 +1162,7 @@ declare_syscall!(
.curve25519_edwards_msm_incremental_cost
.saturating_mul(points_len.saturating_sub(1)),
);
invoke_context.get_compute_meter().consume(cost)?;
consume_compute_meter(invoke_context, cost)?;
let scalars = translate_slice::<scalar::PodScalar>(
memory_mapping,
@ -1217,7 +1202,7 @@ declare_syscall!(
.curve25519_ristretto_msm_incremental_cost
.saturating_mul(points_len.saturating_sub(1)),
);
invoke_context.get_compute_meter().consume(cost)?;
consume_compute_meter(invoke_context, cost)?;
let scalars = translate_slice::<scalar::PodScalar>(
memory_mapping,
@ -1277,9 +1262,7 @@ declare_syscall!(
return Err(SyscallError::TooManySlices.into());
}
invoke_context
.get_compute_meter()
.consume(compute_budget.sha256_base_cost)?;
consume_compute_meter(invoke_context, compute_budget.sha256_base_cost)?;
let hash_result = translate_slice_mut::<u8>(
memory_mapping,
@ -1310,7 +1293,7 @@ declare_syscall!(
.sha256_byte_cost
.saturating_mul((val.len() as u64).saturating_div(2)),
);
invoke_context.get_compute_meter().consume(cost)?;
consume_compute_meter(invoke_context, cost)?;
hasher.hash(bytes);
}
}
@ -1345,7 +1328,7 @@ declare_syscall!(
len / budget.cpi_bytes_per_unit + budget.syscall_base_cost
}
};
invoke_context.get_compute_meter().consume(cost)?;
consume_compute_meter(invoke_context, cost)?;
if len > MAX_RETURN_DATA as u64 {
return Err(SyscallError::ReturnDataTooLarge(len, MAX_RETURN_DATA as u64).into());
@ -1393,9 +1376,7 @@ declare_syscall!(
) -> Result<u64, EbpfError> {
let budget = invoke_context.get_compute_budget();
invoke_context
.get_compute_meter()
.consume(budget.syscall_base_cost)?;
consume_compute_meter(invoke_context, budget.syscall_base_cost)?;
let (program_id, return_data) = invoke_context.transaction_context.get_return_data();
length = length.min(return_data.len() as u64);
@ -1413,7 +1394,7 @@ declare_syscall!(
(length + size_of::<Pubkey>() as u64) / budget.cpi_bytes_per_unit
}
};
invoke_context.get_compute_meter().consume(cost)?;
consume_compute_meter(invoke_context, cost)?;
let return_data_result = translate_slice_mut::<u8>(
memory_mapping,
@ -1472,9 +1453,7 @@ declare_syscall!(
) -> Result<u64, EbpfError> {
let budget = invoke_context.get_compute_budget();
invoke_context
.get_compute_meter()
.consume(budget.syscall_base_cost)?;
consume_compute_meter(invoke_context, budget.syscall_base_cost)?;
let stop_sibling_instruction_search_at_parent = invoke_context
.feature_set
.is_active(&stop_sibling_instruction_search_at_parent::id());
@ -1626,9 +1605,7 @@ declare_syscall!(
) -> Result<u64, EbpfError> {
let budget = invoke_context.get_compute_budget();
invoke_context
.get_compute_meter()
.consume(budget.syscall_base_cost)?;
consume_compute_meter(invoke_context, budget.syscall_base_cost)?;
Ok(invoke_context.get_stack_height() as u64)
}
@ -1657,7 +1634,7 @@ mod tests {
sysvar::{clock::Clock, epoch_schedule::EpochSchedule, rent::Rent},
transaction_context::TransactionContext,
},
std::{borrow::Cow, str::FromStr},
std::{borrow::Cow, cell::RefCell, rc::Rc, str::FromStr},
};
macro_rules! assert_access_violation {
@ -1979,10 +1956,7 @@ mod tests {
)
.unwrap();
invoke_context
.get_compute_meter()
.borrow_mut()
.mock_set_remaining(string.len() as u64 - 1);
invoke_context.mock_set_remaining(string.len() as u64 - 1);
let mut result = ProgramResult::Ok(0);
SyscallPanic::call(
&mut invoke_context,
@ -2001,10 +1975,7 @@ mod tests {
),
));
invoke_context
.get_compute_meter()
.borrow_mut()
.mock_set_remaining(string.len() as u64);
invoke_context.mock_set_remaining(string.len() as u64);
let mut result = ProgramResult::Ok(0);
SyscallPanic::call(
&mut invoke_context,
@ -2043,10 +2014,7 @@ mod tests {
)
.unwrap();
invoke_context
.get_compute_meter()
.borrow_mut()
.mock_set_remaining(400 - 1);
invoke_context.mock_set_remaining(400 - 1);
let mut result = ProgramResult::Ok(0);
SyscallLog::call(
&mut invoke_context,
@ -2122,10 +2090,7 @@ mod tests {
);
let cost = invoke_context.get_compute_budget().log_64_units;
invoke_context
.get_compute_meter()
.borrow_mut()
.mock_set_remaining(cost);
invoke_context.mock_set_remaining(cost);
let config = Config::default();
let mut memory_mapping = MemoryMapping::new(vec![], &config).unwrap();
let mut result = ProgramResult::Ok(0);
@ -2189,10 +2154,7 @@ mod tests {
);
assert_access_violation!(result, 0x100000001, 32);
invoke_context
.get_compute_meter()
.borrow_mut()
.mock_set_remaining(1);
invoke_context.mock_set_remaining(1);
let mut result = ProgramResult::Ok(0);
SyscallLogPubkey::call(
&mut invoke_context,
@ -2211,10 +2173,7 @@ mod tests {
),
));
invoke_context
.get_compute_meter()
.borrow_mut()
.mock_set_remaining(cost);
invoke_context.mock_set_remaining(cost);
let mut result = ProgramResult::Ok(0);
SyscallLogPubkey::call(
&mut invoke_context,
@ -2532,10 +2491,7 @@ mod tests {
)
.unwrap();
invoke_context
.get_compute_meter()
.borrow_mut()
.mock_set_remaining(
invoke_context.mock_set_remaining(
(invoke_context.get_compute_budget().sha256_base_cost
+ invoke_context.get_compute_budget().mem_op_base_cost.max(
invoke_context
@ -2661,10 +2617,7 @@ mod tests {
)
.unwrap();
invoke_context
.get_compute_meter()
.borrow_mut()
.mock_set_remaining(
invoke_context.mock_set_remaining(
(invoke_context
.get_compute_budget()
.curve25519_edwards_validate_point_cost)
@ -2761,10 +2714,7 @@ mod tests {
)
.unwrap();
invoke_context
.get_compute_meter()
.borrow_mut()
.mock_set_remaining(
invoke_context.mock_set_remaining(
(invoke_context
.get_compute_budget()
.curve25519_ristretto_validate_point_cost)
@ -2895,10 +2845,7 @@ mod tests {
)
.unwrap();
invoke_context
.get_compute_meter()
.borrow_mut()
.mock_set_remaining(
invoke_context.mock_set_remaining(
(invoke_context
.get_compute_budget()
.curve25519_edwards_add_cost
@ -3105,10 +3052,7 @@ mod tests {
)
.unwrap();
invoke_context
.get_compute_meter()
.borrow_mut()
.mock_set_remaining(
invoke_context.mock_set_remaining(
(invoke_context
.get_compute_budget()
.curve25519_ristretto_add_cost
@ -3324,10 +3268,7 @@ mod tests {
)
.unwrap();
invoke_context
.get_compute_meter()
.borrow_mut()
.mock_set_remaining(
invoke_context.mock_set_remaining(
invoke_context
.get_compute_budget()
.curve25519_edwards_msm_base_cost
@ -3606,7 +3547,7 @@ mod tests {
seeds: &[&[u8]],
program_id: &Pubkey,
overlap_outputs: bool,
syscall: SyscallFunction<&'a mut InvokeContext<'b>>,
syscall: SyscallFunction<InvokeContext<'b>>,
) -> Result<(Pubkey, u8), EbpfError> {
const SEEDS_VA: u64 = 0x100000000;
const PROGRAM_ID_VA: u64 = 0x200000000;
@ -3874,10 +3815,7 @@ mod tests {
)
.unwrap();
invoke_context
.get_compute_meter()
.borrow_mut()
.mock_set_remaining(syscall_base_cost);
invoke_context.mock_set_remaining(syscall_base_cost);
let mut result = ProgramResult::Ok(0);
SyscallGetProcessedSiblingInstruction::call(
&mut invoke_context,
@ -3909,10 +3847,7 @@ mod tests {
);
}
invoke_context
.get_compute_meter()
.borrow_mut()
.mock_set_remaining(syscall_base_cost);
invoke_context.mock_set_remaining(syscall_base_cost);
let mut result = ProgramResult::Ok(0);
SyscallGetProcessedSiblingInstruction::call(
&mut invoke_context,
@ -3926,10 +3861,7 @@ mod tests {
);
assert_eq!(result.unwrap(), 0);
invoke_context
.get_compute_meter()
.borrow_mut()
.mock_set_remaining(syscall_base_cost);
invoke_context.mock_set_remaining(syscall_base_cost);
let mut result = ProgramResult::Ok(0);
SyscallGetProcessedSiblingInstruction::call(
&mut invoke_context,
@ -4056,10 +3988,7 @@ mod tests {
.unwrap(),
create_program_address(&mut invoke_context, &[b"Talking"], &address).unwrap(),
);
invoke_context
.get_compute_meter()
.borrow_mut()
.mock_set_remaining(0);
invoke_context.mock_set_remaining(0);
assert!(matches!(
create_program_address(&mut invoke_context, &[b"", &[1]], &address),
Err(EbpfError::UserError(error)) if error.downcast_ref::<BpfError>().unwrap() == &BpfError::SyscallError(
@ -4084,10 +4013,7 @@ mod tests {
for _ in 0..1_000 {
let address = Pubkey::new_unique();
invoke_context
.get_compute_meter()
.borrow_mut()
.mock_set_remaining(cost * max_tries);
invoke_context.mock_set_remaining(cost * max_tries);
let (found_address, bump_seed) =
try_find_program_address(&mut invoke_context, &[b"Lil'", b"Bits"], &address)
.unwrap();
@ -4103,21 +4029,12 @@ mod tests {
}
let seeds: &[&[u8]] = &[b""];
invoke_context
.get_compute_meter()
.borrow_mut()
.mock_set_remaining(cost * max_tries);
invoke_context.mock_set_remaining(cost * max_tries);
let (_, bump_seed) =
try_find_program_address(&mut invoke_context, seeds, &address).unwrap();
invoke_context
.get_compute_meter()
.borrow_mut()
.mock_set_remaining(cost * (max_tries - bump_seed as u64));
invoke_context.mock_set_remaining(cost * (max_tries - bump_seed as u64));
try_find_program_address(&mut invoke_context, seeds, &address).unwrap();
invoke_context
.get_compute_meter()
.borrow_mut()
.mock_set_remaining(cost * (max_tries - bump_seed as u64 - 1));
invoke_context.mock_set_remaining(cost * (max_tries - bump_seed as u64 - 1));
assert!(matches!(
try_find_program_address(&mut invoke_context, seeds, &address),
Err(EbpfError::UserError(error)) if error.downcast_ref::<BpfError>().unwrap() == &BpfError::SyscallError(
@ -4126,10 +4043,7 @@ mod tests {
));
let exceeded_seed = &[127; MAX_SEED_LEN + 1];
invoke_context
.get_compute_meter()
.borrow_mut()
.mock_set_remaining(cost * (max_tries - 1));
invoke_context.mock_set_remaining(cost * (max_tries - 1));
assert!(matches!(
try_find_program_address(&mut invoke_context, &[exceeded_seed], &address),
Err(EbpfError::UserError(error)) if error.downcast_ref::<BpfError>().unwrap() == &BpfError::SyscallError(
@ -4155,10 +4069,7 @@ mod tests {
&[16],
&[17],
];
invoke_context
.get_compute_meter()
.borrow_mut()
.mock_set_remaining(cost * (max_tries - 1));
invoke_context.mock_set_remaining(cost * (max_tries - 1));
assert!(matches!(
try_find_program_address(&mut invoke_context, exceeded_seeds, &address),
Err(EbpfError::UserError(error)) if error.downcast_ref::<BpfError>().unwrap() == &BpfError::SyscallError(

View File

@ -7,7 +7,8 @@ fn get_sysvar<T: std::fmt::Debug + Sysvar + SysvarId + Clone>(
memory_mapping: &mut MemoryMapping,
invoke_context: &mut InvokeContext,
) -> Result<u64, EbpfError> {
invoke_context.get_compute_meter().consume(
consume_compute_meter(
invoke_context,
invoke_context
.get_compute_budget()
.sysvar_base_cost

View File

@ -4178,7 +4178,6 @@ dependencies = [
"libsecp256k1 0.6.0",
"log",
"solana-measure",
"solana-metrics",
"solana-program-runtime",
"solana-sdk 1.15.0",
"solana-zk-token-sdk 1.15.0",
@ -4862,6 +4861,7 @@ dependencies = [
"solana-measure",
"solana-metrics",
"solana-sdk 1.15.0",
"solana_rbpf",
"thiserror",
]
@ -5992,9 +5992,9 @@ dependencies = [
[[package]]
name = "solana_rbpf"
version = "0.2.35"
version = "0.2.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9bcad04ab8dff83d39e5b039e419d03e83dafc6643401700b61acf0cc1589ff8"
checksum = "94fe0b05d69875dc1da1e646aec8284c680b9b3f5f521c8d8f958b2cbc108d0b"
dependencies = [
"byteorder 1.4.3",
"combine",

View File

@ -38,7 +38,7 @@ solana-sbf-rust-realloc = { path = "rust/realloc", version = "=1.15.0" }
solana-sbf-rust-realloc-invoke = { path = "rust/realloc_invoke", version = "=1.15.0" }
solana-sdk = { path = "../../sdk", version = "=1.15.0" }
solana-transaction-status = { path = "../../transaction-status", version = "=1.15.0" }
solana_rbpf = "=0.2.35"
solana_rbpf = "=0.2.36"
[dev-dependencies]
solana-ledger = { path = "../../ledger", version = "=1.15.0" }

View File

@ -9,16 +9,15 @@ use {
byteorder::{ByteOrder, LittleEndian, WriteBytesExt},
solana_bpf_loader_program::{
create_vm, serialization::serialize_parameters, syscalls::register_syscalls,
ThisInstructionMeter,
},
solana_measure::measure::Measure,
solana_program_runtime::invoke_context::with_mock_invoke_context,
solana_program_runtime::invoke_context::{with_mock_invoke_context, InvokeContext},
solana_rbpf::{
ebpf::MM_INPUT_START,
elf::Executable,
memory_region::MemoryRegion,
verifier::RequisiteVerifier,
vm::{Config, InstructionMeter, SyscallRegistry, VerifiedExecutable},
vm::{Config, ContextObject, SyscallRegistry, VerifiedExecutable},
},
solana_runtime::{
bank::Bank,
@ -82,7 +81,7 @@ fn bench_program_create_executable(bencher: &mut Bencher) {
let elf = load_elf("bench_alu").unwrap();
bencher.iter(|| {
let _ = Executable::<ThisInstructionMeter>::from_elf(
let _ = Executable::<InvokeContext>::from_elf(
&elf,
Config::default(),
SyscallRegistry::default(),
@ -103,11 +102,7 @@ fn bench_program_alu(bencher: &mut Bencher) {
let elf = load_elf("bench_alu").unwrap();
let loader_id = bpf_loader::id();
with_mock_invoke_context(loader_id, 10000001, false, |invoke_context| {
invoke_context
.get_compute_meter()
.borrow_mut()
.mock_set_remaining(std::i64::MAX as u64);
let executable = Executable::<ThisInstructionMeter>::from_elf(
let executable = Executable::<InvokeContext>::from_elf(
&elf,
Config::default(),
register_syscalls(&invoke_context.feature_set, true).unwrap(),
@ -115,14 +110,10 @@ fn bench_program_alu(bencher: &mut Bencher) {
.unwrap();
let mut verified_executable =
VerifiedExecutable::<RequisiteVerifier, ThisInstructionMeter>::from_executable(
executable,
)
VerifiedExecutable::<RequisiteVerifier, InvokeContext>::from_executable(executable)
.unwrap();
verified_executable.jit_compile().unwrap();
let compute_meter = invoke_context.get_compute_meter();
let mut instruction_meter = ThisInstructionMeter { compute_meter };
let mut vm = create_vm(
&verified_executable,
vec![MemoryRegion::new_writable(&mut inner_iter, MM_INPUT_START)],
@ -132,11 +123,9 @@ fn bench_program_alu(bencher: &mut Bencher) {
.unwrap();
println!("Interpreted:");
assert_eq!(
SUCCESS,
vm.execute_program_interpreted(&mut instruction_meter)
.unwrap()
);
vm.context_object.mock_set_remaining(std::i64::MAX as u64);
let (instructions, result) = vm.execute_program(true);
assert_eq!(SUCCESS, result.unwrap());
assert_eq!(ARMSTRONG_LIMIT, LittleEndian::read_u64(&inner_iter));
assert_eq!(
ARMSTRONG_EXPECTED,
@ -144,10 +133,9 @@ fn bench_program_alu(bencher: &mut Bencher) {
);
bencher.iter(|| {
vm.execute_program_interpreted(&mut instruction_meter)
.unwrap();
vm.context_object.mock_set_remaining(std::i64::MAX as u64);
vm.execute_program(true).1.unwrap();
});
let instructions = vm.get_total_instruction_count();
let summary = bencher.bench(|_bencher| Ok(())).unwrap().unwrap();
println!(" {:?} instructions", instructions);
println!(" {:?} ns/iter median", summary.median as u64);
@ -157,17 +145,17 @@ fn bench_program_alu(bencher: &mut Bencher) {
println!("{{ \"type\": \"bench\", \"name\": \"bench_program_alu_interpreted_mips\", \"median\": {:?}, \"deviation\": 0 }}", mips);
println!("JIT to native:");
assert_eq!(
SUCCESS,
vm.execute_program_jit(&mut instruction_meter).unwrap()
);
assert_eq!(SUCCESS, vm.execute_program(false).1.unwrap());
assert_eq!(ARMSTRONG_LIMIT, LittleEndian::read_u64(&inner_iter));
assert_eq!(
ARMSTRONG_EXPECTED,
LittleEndian::read_u64(&inner_iter[mem::size_of::<u64>()..])
);
bencher.iter(|| vm.execute_program_jit(&mut instruction_meter).unwrap());
bencher.iter(|| {
vm.context_object.mock_set_remaining(std::i64::MAX as u64);
vm.execute_program(false).1.unwrap();
});
let summary = bencher.bench(|_bencher| Ok(())).unwrap().unwrap();
println!(" {:?} instructions", instructions);
println!(" {:?} ns/iter median", summary.median as u64);
@ -219,10 +207,7 @@ fn bench_create_vm(bencher: &mut Bencher) {
let loader_id = bpf_loader::id();
with_mock_invoke_context(loader_id, 10000001, false, |invoke_context| {
const BUDGET: u64 = 200_000;
invoke_context
.get_compute_meter()
.borrow_mut()
.mock_set_remaining(BUDGET);
invoke_context.mock_set_remaining(BUDGET);
// Serialize account data
let (_serialized, regions, account_lengths) = serialize_parameters(
@ -235,7 +220,7 @@ fn bench_create_vm(bencher: &mut Bencher) {
)
.unwrap();
let executable = Executable::<ThisInstructionMeter>::from_elf(
let executable = Executable::<InvokeContext>::from_elf(
&elf,
Config::default(),
register_syscalls(&invoke_context.feature_set, true).unwrap(),
@ -243,9 +228,7 @@ fn bench_create_vm(bencher: &mut Bencher) {
.unwrap();
let verified_executable =
VerifiedExecutable::<RequisiteVerifier, ThisInstructionMeter>::from_executable(
executable,
)
VerifiedExecutable::<RequisiteVerifier, InvokeContext>::from_executable(executable)
.unwrap();
bencher.iter(|| {
@ -266,10 +249,7 @@ fn bench_instruction_count_tuner(_bencher: &mut Bencher) {
let loader_id = bpf_loader::id();
with_mock_invoke_context(loader_id, 10000001, true, |invoke_context| {
const BUDGET: u64 = 200_000;
invoke_context
.get_compute_meter()
.borrow_mut()
.mock_set_remaining(BUDGET);
invoke_context.mock_set_remaining(BUDGET);
// Serialize account data
let (_serialized, regions, account_lengths) = serialize_parameters(
@ -282,7 +262,7 @@ fn bench_instruction_count_tuner(_bencher: &mut Bencher) {
)
.unwrap();
let executable = Executable::<ThisInstructionMeter>::from_elf(
let executable = Executable::<InvokeContext>::from_elf(
&elf,
Config::default(),
register_syscalls(&invoke_context.feature_set, true).unwrap(),
@ -290,13 +270,9 @@ fn bench_instruction_count_tuner(_bencher: &mut Bencher) {
.unwrap();
let verified_executable =
VerifiedExecutable::<RequisiteVerifier, ThisInstructionMeter>::from_executable(
executable,
)
VerifiedExecutable::<RequisiteVerifier, InvokeContext>::from_executable(executable)
.unwrap();
let compute_meter = invoke_context.get_compute_meter();
let mut instruction_meter = ThisInstructionMeter { compute_meter };
let mut vm = create_vm(
&verified_executable,
regions,
@ -306,19 +282,19 @@ fn bench_instruction_count_tuner(_bencher: &mut Bencher) {
.unwrap();
let mut measure = Measure::start("tune");
let _ = vm.execute_program_interpreted(&mut instruction_meter);
let (instructions, _result) = vm.execute_program(true);
measure.stop();
assert_eq!(
0,
instruction_meter.get_remaining(),
vm.context_object.get_remaining(),
"Tuner must consume the whole budget"
);
println!(
"{:?} compute units took {:?} us ({:?} instructions)",
BUDGET - instruction_meter.get_remaining(),
BUDGET - vm.context_object.get_remaining(),
measure.as_us(),
vm.get_total_instruction_count(),
instructions,
);
});
}

View File

@ -12,6 +12,7 @@ use {
solana_ledger::token_balances::collect_token_balances,
solana_program_runtime::{
compute_budget::{self, ComputeBudget},
invoke_context::InvokeContext,
timings::ExecuteTimings,
},
solana_runtime::{
@ -52,19 +53,16 @@ use {
std::{collections::HashMap, str::FromStr},
};
use {
log::{log_enabled, trace, Level::Trace},
solana_bpf_loader_program::{
create_vm,
serialization::{deserialize_parameters, serialize_parameters},
syscalls::register_syscalls,
ThisInstructionMeter,
},
solana_program_runtime::invoke_context::with_mock_invoke_context,
solana_rbpf::{
elf::Executable,
static_analysis::Analysis,
verifier::RequisiteVerifier,
vm::{Config, Tracer, VerifiedExecutable},
vm::{Config, VerifiedExecutable},
},
solana_runtime::{
bank::Bank,
@ -226,14 +224,12 @@ fn run_program(name: &str) -> u64 {
file.read_to_end(&mut data).unwrap();
let loader_id = bpf_loader::id();
with_mock_invoke_context(loader_id, 0, false, |invoke_context| {
let compute_meter = invoke_context.get_compute_meter();
let mut instruction_meter = ThisInstructionMeter { compute_meter };
let config = Config {
enable_instruction_tracing: true,
reject_broken_elfs: true,
..Config::default()
};
let executable = Executable::<ThisInstructionMeter>::from_elf(
let executable = Executable::<InvokeContext>::from_elf(
&data,
config,
register_syscalls(
@ -246,9 +242,7 @@ fn run_program(name: &str) -> u64 {
#[allow(unused_mut)]
let mut verified_executable =
VerifiedExecutable::<RequisiteVerifier, ThisInstructionMeter>::from_executable(
executable,
)
VerifiedExecutable::<RequisiteVerifier, InvokeContext>::from_executable(executable)
.unwrap();
let run_program_iterations = {
@ -262,7 +256,7 @@ fn run_program(name: &str) -> u64 {
};
let mut instruction_count = 0;
let mut tracer = None;
let mut trace_log = None;
for i in 0..run_program_iterations {
let transaction_context = &mut invoke_context.transaction_context;
let instruction_context = transaction_context
@ -293,53 +287,24 @@ fn run_program(name: &str) -> u64 {
invoke_context,
)
.unwrap();
let result = if i == 0 {
vm.execute_program_interpreted(&mut instruction_meter)
} else {
vm.execute_program_jit(&mut instruction_meter)
};
let (compute_units_consumed, result) = vm.execute_program(i == 0);
assert_eq!(SUCCESS, result.unwrap());
if i == 1 {
assert_eq!(instruction_count, vm.get_total_instruction_count());
assert_eq!(instruction_count, compute_units_consumed);
}
instruction_count = vm.get_total_instruction_count();
instruction_count = compute_units_consumed;
if config.enable_instruction_tracing {
if i == 1 {
if !Tracer::compare(
tracer.as_ref().unwrap(),
&vm.get_program_environment().tracer,
) {
let analysis =
Analysis::from_executable(verified_executable.get_executable())
.unwrap();
let stdout = std::io::stdout();
println!("TRACE (interpreted):");
tracer
.as_ref()
.unwrap()
.write(&mut stdout.lock(), &analysis)
.unwrap();
println!("TRACE (jit):");
vm.get_program_environment()
.tracer
.write(&mut stdout.lock(), &analysis)
.unwrap();
assert!(false);
} else if log_enabled!(Trace) {
let analysis =
Analysis::from_executable(verified_executable.get_executable())
.unwrap();
let mut trace_buffer = Vec::<u8>::new();
tracer
.as_ref()
.unwrap()
.write(&mut trace_buffer, &analysis)
.unwrap();
let trace_string = String::from_utf8(trace_buffer).unwrap();
trace!("SBF Program Instruction Trace:\n{}", trace_string);
if i == 0 {
trace_log = Some(vm.context_object.trace_log.clone());
} else {
let interpreter = trace_log.as_ref().unwrap().as_slice();
let mut jit = vm.context_object.trace_log.as_slice();
if jit.len() > interpreter.len() {
jit = &jit[0..interpreter.len()];
}
assert_eq!(interpreter, jit);
trace_log = None;
}
tracer = Some(vm.get_program_environment().tracer.clone());
}
}
assert!(match deserialize_parameters(

View File

@ -39,9 +39,8 @@ pub fn process_instruction(
// Consume compute units since proof verification is an expensive operation
{
let compute_meter = invoke_context.get_compute_meter();
// TODO: Tune the number of units consumed. The current value is just a rough estimate
compute_meter.borrow_mut().consume(100_000)?;
invoke_context.consume_checked(100_000)?;
}
let transaction_context = &invoke_context.transaction_context;

View File

@ -17,4 +17,4 @@ solana-bpf-loader-program = { path = "../programs/bpf_loader", version = "=1.15.
solana-logger = { path = "../logger", version = "=1.15.0" }
solana-program-runtime = { path = "../program-runtime", version = "=1.15.0" }
solana-sdk = { path = "../sdk", version = "=1.15.0" }
solana_rbpf = "=0.2.35"
solana_rbpf = { version = "=0.2.36", features = ["debugger"] }

View File

@ -4,15 +4,16 @@ use {
serde_json::Result,
solana_bpf_loader_program::{
create_vm, serialization::serialize_parameters, syscalls::register_syscalls,
ThisInstructionMeter,
},
solana_program_runtime::invoke_context::{prepare_mock_invoke_context, InvokeContext},
solana_rbpf::{
assembler::assemble,
debugger,
elf::Executable,
interpreter::Interpreter,
static_analysis::Analysis,
verifier::RequisiteVerifier,
vm::{Config, DynamicAnalysis, VerifiedExecutable},
vm::{Config, VerifiedExecutable},
},
solana_sdk::{
account::AccountSharedData, bpf_loader, instruction::AccountMeta, pubkey::Pubkey,
@ -121,14 +122,15 @@ with input data, or BYTES is the number of 0-valued bytes to allocate for progra
.help(
"Method of execution to use, where 'cfg' generates Control Flow Graph \
of the program, 'disassembler' dumps disassembled code of the program, 'interpreter' runs \
the program in the virtual machine's interpreter, and 'jit' precompiles the program to \
native machine code before execting it in the virtual machine.",
the program in the virtual machine's interpreter, 'debugger' is the same as 'interpreter' \
but hosts a GDB interface, and 'jit' precompiles the program to native machine code \
before execting it in the virtual machine.",
)
.short('u')
.long("use")
.takes_value(true)
.value_name("VALUE")
.possible_values(["cfg", "disassembler", "interpreter", "jit"])
.possible_values(["cfg", "disassembler", "interpreter", "debugger", "jit"])
.default_value("jit"),
)
.arg(
@ -141,16 +143,12 @@ native machine code before execting it in the virtual machine.",
.default_value(&std::i64::MAX.to_string()),
)
.arg(
Arg::new("trace")
.help("Output trace to 'trace.out' file using tracing instrumentation")
.short('t')
.long("trace"),
)
.arg(
Arg::new("profile")
.help("Output profile to 'profile.dot' file using tracing instrumentation")
.short('p')
.long("profile"),
Arg::new("port")
.help("Port to use for the connection with a remote debugger")
.long("port")
.takes_value(true)
.value_name("PORT")
.default_value("9001"),
)
.arg(
Arg::new("output_format")
@ -164,7 +162,6 @@ native machine code before execting it in the virtual machine.",
.get_matches();
let config = Config {
enable_instruction_tracing: matches.is_present("trace") || matches.is_present("profile"),
enable_symbol_and_section_labels: true,
..Config::default()
};
@ -242,8 +239,6 @@ native machine code before execting it in the virtual machine.",
true, // should_cap_ix_accounts
)
.unwrap();
let compute_meter = invoke_context.get_compute_meter();
let mut instruction_meter = ThisInstructionMeter { compute_meter };
let program = matches.value_of("PROGRAM").unwrap();
let mut file = File::open(Path::new(program)).unwrap();
@ -254,10 +249,10 @@ native machine code before execting it in the virtual machine.",
file.read_to_end(&mut contents).unwrap();
let syscall_registry = register_syscalls(&invoke_context.feature_set, true).unwrap();
let executable = if magic == [0x7f, 0x45, 0x4c, 0x46] {
Executable::<ThisInstructionMeter>::from_elf(&contents, config, syscall_registry)
Executable::<InvokeContext>::from_elf(&contents, config, syscall_registry)
.map_err(|err| format!("Executable constructor failed: {:?}", err))
} else {
assemble::<ThisInstructionMeter>(
assemble::<InvokeContext>(
std::str::from_utf8(contents.as_slice()).unwrap(),
config,
syscall_registry,
@ -266,7 +261,7 @@ native machine code before execting it in the virtual machine.",
.unwrap();
let mut verified_executable =
VerifiedExecutable::<RequisiteVerifier, ThisInstructionMeter>::from_executable(executable)
VerifiedExecutable::<RequisiteVerifier, InvokeContext>::from_executable(executable)
.map_err(|err| format!("Executable verifier failed: {:?}", err))
.unwrap();
@ -298,33 +293,14 @@ native machine code before execting it in the virtual machine.",
)
.unwrap();
let start_time = Instant::now();
let result = if matches.value_of("use").unwrap() == "interpreter" {
vm.execute_program_interpreted(&mut instruction_meter)
let (instruction_count, result) = if matches.value_of("use").unwrap() == "debugger" {
let mut interpreter = Interpreter::new(&mut vm).unwrap();
let port = matches.value_of("port").unwrap().parse::<u16>().unwrap();
debugger::execute(&mut interpreter, port)
} else {
vm.execute_program_jit(&mut instruction_meter)
vm.execute_program(matches.value_of("use").unwrap() == "interpreter")
};
let duration = Instant::now() - start_time;
if matches.is_present("trace") {
eprintln!("Trace is saved in trace.out");
let mut file = File::create("trace.out").unwrap();
vm.get_program_environment()
.tracer
.write(&mut file, analysis.analyze())
.unwrap();
}
if matches.is_present("profile") {
eprintln!("Profile is saved in profile.dot");
let tracer = &vm.get_program_environment().tracer;
let analysis = analysis.analyze();
let dynamic_analysis = DynamicAnalysis::new(tracer, analysis);
let mut file = File::create("profile.dot").unwrap();
analysis
.visualize_graphically(&mut file, Some(&dynamic_analysis))
.unwrap();
}
let instruction_count = vm.get_total_instruction_count();
drop(vm);
let output = Output {
@ -374,20 +350,20 @@ impl Debug for Output {
// Replace with std::lazy::Lazy when stabilized.
// https://github.com/rust-lang/rust/issues/74465
struct LazyAnalysis<'a> {
analysis: Option<Analysis<'a, ThisInstructionMeter>>,
executable: &'a Executable<ThisInstructionMeter>,
struct LazyAnalysis<'a, 'b> {
analysis: Option<Analysis<'a, InvokeContext<'b>>>,
executable: &'a Executable<InvokeContext<'b>>,
}
impl<'a> LazyAnalysis<'a> {
fn new(executable: &'a Executable<ThisInstructionMeter>) -> Self {
impl<'a, 'b> LazyAnalysis<'a, 'b> {
fn new(executable: &'a Executable<InvokeContext<'b>>) -> Self {
Self {
analysis: None,
executable,
}
}
fn analyze(&mut self) -> &Analysis<ThisInstructionMeter> {
fn analyze(&mut self) -> &Analysis<InvokeContext<'b>> {
if let Some(ref analysis) = self.analysis {
return analysis;
}