Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement standard invoker #207

Merged
merged 1 commit into from
Nov 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading