Skip to content

Commit

Permalink
Implement standard invoker (#207)
Browse files Browse the repository at this point in the history
  • Loading branch information
sorpaas committed Nov 8, 2023
1 parent 5ec9288 commit 7d133f2
Show file tree
Hide file tree
Showing 10 changed files with 517 additions and 38 deletions.
2 changes: 2 additions & 0 deletions interpreter/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ serde = { version = "1.0", default-features = false, features = ["derive"], opti
bytes = { version = "1.5", default-features = false }
auto_impl = "1.0"
sha3 = { version = "0.10", default-features = false }
rlp = { version = "0.5", default-features = false }

[dev-dependencies]
hex = "0.4"
Expand All @@ -29,6 +30,7 @@ std = [
"scale-codec/std",
"scale-info/std",
"bytes/std",
"rlp/std",
]
with-codec = [
"scale-codec",
Expand Down
88 changes: 74 additions & 14 deletions interpreter/src/call_create.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
use crate::utils::{h256_to_u256, u256_to_usize};
use crate::{Context, ExitError, ExitException, ExitResult, Machine, Memory, RuntimeState};
use crate::{
Context, ExitError, ExitException, ExitResult, Machine, Memory, Opcode, RuntimeFullBackend,
RuntimeState, Transfer,
};
use core::cmp::{max, min};
use primitive_types::{H160, H256, U256};
use sha3::{Digest, Keccak256};
Expand All @@ -21,8 +24,39 @@ pub enum CreateScheme {
/// Salt.
salt: H256,
},
/// Create at a fixed location.
Fixed(H160),
}

impl CreateScheme {
pub fn address<H: RuntimeFullBackend>(&self, handler: &H) -> H160 {
match self {
CreateScheme::Create2 {
caller,
code_hash,
salt,
} => {
let mut hasher = Keccak256::new();
hasher.update([0xff]);
hasher.update(&caller[..]);
hasher.update(&salt[..]);
hasher.update(&code_hash[..]);
H256::from_slice(hasher.finalize().as_slice()).into()
}
CreateScheme::Legacy { caller } => {
let nonce = handler.nonce(*caller);
let mut stream = rlp::RlpStream::new_list(2);
stream.append(caller);
stream.append(&nonce);
H256::from_slice(Keccak256::digest(&stream.out()).as_slice()).into()
}
}
}

pub fn caller(&self) -> H160 {
match self {
Self::Create2 { caller, .. } => *caller,
Self::Legacy { caller } => *caller,
}
}
}

/// Call scheme.
Expand All @@ -38,22 +72,47 @@ pub enum CallScheme {
StaticCall,
}

/// Transfer from source to target, with given value.
#[derive(Clone, Debug)]
pub struct Transfer {
/// Source address.
pub source: H160,
/// Target address.
pub target: H160,
/// Transfer value.
pub value: U256,
}

pub enum CallCreateTrapData {
Call(CallTrapData),
Create(CreateTrapData),
}

impl CallCreateTrapData {
pub fn target_gas(&self) -> Option<U256> {
match self {
Self::Call(CallTrapData { gas, .. }) => Some(*gas),
Self::Create(_) => None,
}
}

pub fn new_from<S: AsRef<RuntimeState> + AsMut<RuntimeState>>(
opcode: Opcode,
machine: &mut Machine<S>,
) -> Result<Self, ExitError> {
match opcode {
Opcode::CREATE => Ok(Self::Create(CreateTrapData::new_create_from(machine)?)),
Opcode::CREATE2 => Ok(Self::Create(CreateTrapData::new_create2_from(machine)?)),
Opcode::CALL => Ok(Self::Call(CallTrapData::new_from(
CallScheme::Call,
machine,
)?)),
Opcode::CALLCODE => Ok(Self::Call(CallTrapData::new_from(
CallScheme::CallCode,
machine,
)?)),
Opcode::DELEGATECALL => Ok(Self::Call(CallTrapData::new_from(
CallScheme::DelegateCall,
machine,
)?)),
Opcode::STATICCALL => Ok(Self::Call(CallTrapData::new_from(
CallScheme::StaticCall,
machine,
)?)),
_ => Err(ExitException::InvalidOpcode(opcode).into()),
}
}
}

pub struct CallTrapData {
pub target: H160,
pub transfer: Option<Transfer>,
Expand Down Expand Up @@ -254,6 +313,7 @@ impl CallTrapData {
}
}

#[derive(Clone, Debug)]
pub struct CreateTrapData {
pub scheme: CreateScheme,
pub value: U256,
Expand Down
4 changes: 3 additions & 1 deletion interpreter/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ pub use crate::error::{Capture, ExitError, ExitException, ExitFatal, ExitResult,
pub use crate::eval::{Control, Efn, Etable};
pub use crate::memory::Memory;
pub use crate::opcode::Opcode;
pub use crate::runtime::{CallCreateTrap, Context, RuntimeBackend, RuntimeState};
pub use crate::runtime::{
CallCreateTrap, Context, RuntimeBackend, RuntimeFullBackend, RuntimeState, Transfer,
};
pub use crate::stack::Stack;
pub use crate::valids::Valids;

Expand Down
26 changes: 26 additions & 0 deletions interpreter/src/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,17 @@ pub struct Context {
pub apparent_value: U256,
}

/// Transfer from source to target, with given value.
#[derive(Clone, Debug)]
pub struct Transfer {
/// Source address.
pub source: H160,
/// Target address.
pub target: H160,
/// Transfer value.
pub value: U256,
}

pub trait CallCreateTrap: Sized {
fn call_create_trap(opcode: Opcode) -> Self;
}
Expand Down Expand Up @@ -98,3 +109,18 @@ pub trait RuntimeBackend {
/// Mark an address to be deleted, with funds transferred to target.
fn mark_delete(&mut self, address: H160, target: H160) -> Result<(), ExitError>;
}

pub trait RuntimeFullBackend: RuntimeBackend {
/// Get the current nonce of an account.
fn nonce(&self, address: H160) -> U256;
/// Fully delete storages of an account.
fn reset_storage(&mut self, address: H160);
/// Set code of an account.
fn set_code(&mut self, address: H160, code: Vec<u8>);
/// Reset balance of an account.
fn reset_balance(&mut self, address: H160);
/// Initiate a transfer.
fn transfer(&mut self, transfer: Transfer) -> Result<(), ExitError>;
/// Increase the nonce value.
fn inc_nonce(&mut self, address: H160) -> Result<(), ExitError>;
}
12 changes: 6 additions & 6 deletions src/call_stack.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ enum LastCallStackData<S, G, Tr> {
}

pub struct CallStack<'backend, 'invoker, S, G, H, Tr, I: Invoker<S, G, H, Tr>> {
stack: Vec<TrappedCallStackData<S, G, I::CallCreateTrapData>>,
stack: Vec<TrappedCallStackData<S, G, I::CallCreateTrapEnterData>>,
last: LastCallStackData<S, G, Tr>,
initial_depth: usize,
backend: &'backend mut H,
Expand Down Expand Up @@ -154,8 +154,8 @@ where
self.initial_depth + self.stack.len() + 1,
) {
Capture::Exit(Ok(trap_data)) => {
match self.invoker.enter_trap_stack(&trap_data, self.backend) {
Ok(sub_machine) => {
match self.invoker.enter_trap_stack(trap_data, self.backend) {
Ok((trap_data, sub_machine)) => {
self.stack.push(TrappedCallStackData { trap_data, machine });

LastCallStackData::Running {
Expand Down Expand Up @@ -207,14 +207,14 @@ where
Capture::Exit(exit) => return (machine, exit),
Capture::Trap(trap) => {
let prepared_trap_data: Capture<
Result<I::CallCreateTrapData, ExitError>,
Result<I::CallCreateTrapPrepareData, ExitError>,
Infallible,
> = invoker.prepare_trap(trap, &mut machine, backend, initial_depth + 1);

match prepared_trap_data {
Capture::Exit(Ok(trap_data)) => {
match invoker.enter_trap_stack(&trap_data, backend) {
Ok(sub_machine) => {
match invoker.enter_trap_stack(trap_data, backend) {
Ok((trap_data, sub_machine)) => {
let (sub_machine, sub_result) = if heap_depth
.map(|hd| initial_depth + 1 >= hd)
.unwrap_or(false)
Expand Down
2 changes: 0 additions & 2 deletions src/gasometer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,7 @@ pub enum GasometerMergeStrategy {

pub trait Gasometer<S, H>: Sized {
type Gas: Gas;
type Config;

fn new(gas_limit: Self::Gas, machine: &Machine<S>, config: Self::Config) -> Self;
fn record_stepn(
&mut self,
machine: &Machine<S>,
Expand Down
14 changes: 9 additions & 5 deletions src/invoker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,32 @@ use crate::{Capture, ExitError, ExitResult, GasedMachine};

pub trait Invoker<S, G, H, Tr> {
type Interrupt;
type CallCreateTrapData;
type CallCreateTrapPrepareData;
type CallCreateTrapEnterData;

fn exit_trap_stack(
&self,
result: ExitResult,
child: GasedMachine<S, G>,
trap_data: Self::CallCreateTrapData,
trap_data: Self::CallCreateTrapEnterData,
parent: &mut GasedMachine<S, G>,
handler: &mut H,
) -> Result<(), ExitError>;

/// The separation of `prepare_trap` and `enter_trap_stack` is to give an opportunity for the
/// trait to return `Self::Interrupt`. When `Self::Interrupt` is `Infallible`, there's no
/// difference whether a code is in `prepare_trap` or `enter_trap_stack`.
fn prepare_trap(
&self,
trap: Tr,
machine: &mut GasedMachine<S, G>,
handler: &mut H,
depth: usize,
) -> Capture<Result<Self::CallCreateTrapData, ExitError>, Self::Interrupt>;
) -> Capture<Result<Self::CallCreateTrapPrepareData, ExitError>, Self::Interrupt>;

fn enter_trap_stack(
&self,
trap_data: &Self::CallCreateTrapData,
trap_data: Self::CallCreateTrapPrepareData,
handler: &mut H,
) -> Result<GasedMachine<S, G>, ExitError>;
) -> Result<(Self::CallCreateTrapEnterData, GasedMachine<S, G>), ExitError>;
}
19 changes: 9 additions & 10 deletions src/standard/gasometer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ impl<'config> Gasometer<'config> {
}

/// Record an explicit cost.
fn record_cost_nocleanup(&mut self, cost: u64) -> Result<(), ExitError> {
pub fn record_cost(&mut self, cost: u64) -> Result<(), ExitError> {
let all_gas_cost = self.total_used_gas() + cost;
if self.gas_limit < all_gas_cost {
Err(ExitException::OutOfGas.into())
Expand All @@ -57,13 +57,8 @@ impl<'config> Gasometer<'config> {
Ok(())
}
}
}

impl<'config, S: AsRef<RuntimeState>, H: RuntimeBackend> GasometerT<S, H> for Gasometer<'config> {
type Gas = u64;
type Config = &'config Config;

fn new(gas_limit: u64, _machine: &Machine<S>, config: &'config Config) -> Self {
pub fn new<S>(gas_limit: u64, _machine: &Machine<S>, config: &'config Config) -> Self {
Self {
gas_limit,
memory_gas: 0,
Expand All @@ -72,6 +67,10 @@ impl<'config, S: AsRef<RuntimeState>, H: RuntimeBackend> GasometerT<S, H> for Ga
config,
}
}
}

impl<'config, S: AsRef<RuntimeState>, H: RuntimeBackend> GasometerT<S, H> for Gasometer<'config> {
type Gas = u64;

fn record_stepn(
&mut self,
Expand All @@ -83,7 +82,7 @@ impl<'config, S: AsRef<RuntimeState>, H: RuntimeBackend> GasometerT<S, H> for Ga
let opcode = machine.peek_opcode().ok_or(ExitException::OutOfGas)?;

if let Some(cost) = consts::STATIC_COST_TABLE[opcode.as_usize()] {
gasometer.record_cost_nocleanup(cost)?;
gasometer.record_cost(cost)?;
} else {
let address = machine.state.as_ref().context.address;
let (gas, memory_gas) = dynamic_opcode_cost(
Expand All @@ -97,7 +96,7 @@ impl<'config, S: AsRef<RuntimeState>, H: RuntimeBackend> GasometerT<S, H> for Ga
let cost = gas.cost(gasometer.gas(), gasometer.config)?;
let refund = gas.refund(gasometer.config);

gasometer.record_cost_nocleanup(cost)?;
gasometer.record_cost(cost)?;
if refund >= 0 {
gasometer.refunded_gas += refund as u64;
} else {
Expand All @@ -121,7 +120,7 @@ impl<'config, S: AsRef<RuntimeState>, H: RuntimeBackend> GasometerT<S, H> for Ga
fn record_codedeposit(&mut self, len: usize) -> Result<(), ExitError> {
self.perform(|gasometer| {
let cost = len as u64 * consts::G_CODEDEPOSIT;
gasometer.record_cost_nocleanup(cost)?;
gasometer.record_cost(cost)?;
Ok(())
})
}
Expand Down
Loading

0 comments on commit 7d133f2

Please sign in to comment.