Pipe FeatureSet though InvokeContext (#12536)
* Pipe FeatureSet though InvokeContext * gate program size cap * nit
This commit is contained in:
@@ -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 {
|
||||
|
@@ -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)]
|
||||
|
@@ -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() {
|
||||
|
@@ -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)),
|
||||
|
@@ -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(
|
||||
|
Reference in New Issue
Block a user