pub mod analysis;
mod contract;
pub(crate) mod memory;
mod stack;
pub use analysis::BytecodeLocked;
pub use contract::Contract;
pub use memory::Memory;
pub use stack::Stack;
use crate::primitives::{Bytes, Spec};
use crate::{
instructions::{eval, InstructionResult},
Gas, Host,
};
use core::ops::Range;
pub const STACK_LIMIT: u64 = 1024;
pub const CALL_STACK_LIMIT: u64 = 1024;
pub const MAX_CODE_SIZE: usize = 0x6000;
pub const MAX_INITCODE_SIZE: usize = 2 * MAX_CODE_SIZE;
pub struct Interpreter {
pub instruction_pointer: *const u8,
pub instruction_result: InstructionResult,
pub gas: Gas,
pub memory: Memory,
pub stack: Stack,
pub return_data_buffer: Bytes,
pub return_range: Range<usize>,
pub is_static: bool,
pub contract: Contract,
#[cfg(feature = "memory_limit")]
pub memory_limit: u64,
}
impl Interpreter {
pub fn current_opcode(&self) -> u8 {
unsafe { *self.instruction_pointer }
}
pub fn new(contract: Contract, gas_limit: u64, is_static: bool) -> Self {
#[cfg(not(feature = "memory_limit"))]
{
Self {
instruction_pointer: contract.bytecode.as_ptr(),
return_range: Range::default(),
memory: Memory::new(),
stack: Stack::new(),
return_data_buffer: Bytes::new(),
contract,
instruction_result: InstructionResult::Continue,
is_static,
gas: Gas::new(gas_limit),
}
}
#[cfg(feature = "memory_limit")]
{
Self::new_with_memory_limit(contract, gas_limit, is_static, u64::MAX)
}
}
#[cfg(feature = "memory_limit")]
pub fn new_with_memory_limit(
contract: Contract,
gas_limit: u64,
is_static: bool,
memory_limit: u64,
) -> Self {
Self {
instruction_pointer: contract.bytecode.as_ptr(),
return_range: Range::default(),
memory: Memory::new(),
stack: Stack::new(),
return_data_buffer: Bytes::new(),
contract,
instruction_result: InstructionResult::Continue,
is_static,
gas: Gas::new(gas_limit),
memory_limit,
}
}
pub fn contract(&self) -> &Contract {
&self.contract
}
pub fn gas(&self) -> &Gas {
&self.gas
}
pub fn memory(&self) -> &Memory {
&self.memory
}
pub fn stack(&self) -> &Stack {
&self.stack
}
pub fn program_counter(&self) -> usize {
unsafe {
self.instruction_pointer
.offset_from(self.contract.bytecode.as_ptr()) as usize
}
}
#[inline(always)]
pub fn step<H: Host, SPEC: Spec>(&mut self, host: &mut H) {
let opcode = unsafe { *self.instruction_pointer };
self.instruction_pointer = unsafe { self.instruction_pointer.offset(1) };
eval::<H, SPEC>(opcode, self, host);
}
pub fn run<H: Host, SPEC: Spec>(&mut self, host: &mut H) -> InstructionResult {
while self.instruction_result == InstructionResult::Continue {
self.step::<H, SPEC>(host)
}
self.instruction_result
}
pub fn run_inspect<H: Host, SPEC: Spec>(&mut self, host: &mut H) -> InstructionResult {
while self.instruction_result == InstructionResult::Continue {
let ret = host.step(self, self.is_static);
if ret != InstructionResult::Continue {
return ret;
}
self.step::<H, SPEC>(host);
let ret = host.step_end(self, self.is_static, self.instruction_result);
if ret != InstructionResult::Continue {
return ret;
}
}
self.instruction_result
}
pub fn return_value(&self) -> Bytes {
if self.return_range.start == usize::MAX {
Bytes::new()
} else {
Bytes::copy_from_slice(self.memory.get_slice(
self.return_range.start,
self.return_range.end - self.return_range.start,
))
}
}
}