From ea6f8fb4f2af07ade4453959abc5fab9185deaa4 Mon Sep 17 00:00:00 2001 From: Wei Tang Date: Tue, 21 Nov 2023 00:51:33 +0100 Subject: [PATCH] Generalize machine run/step in call stack --- jsontests/src/run.rs | 47 ++++---- src/call_stack.rs | 178 ++++++++++--------------------- src/invoker.rs | 25 +++-- src/standard/invoker/mod.rs | 54 +++++++--- src/standard/invoker/routines.rs | 32 +++--- 5 files changed, 147 insertions(+), 189 deletions(-) diff --git a/jsontests/src/run.rs b/jsontests/src/run.rs index eb4bf7331..a1e99b017 100644 --- a/jsontests/src/run.rs +++ b/jsontests/src/run.rs @@ -5,7 +5,7 @@ use evm::backend::in_memory::{ }; use evm::standard::{Config, Etable, Gasometer, Invoker, TransactArgs}; use evm::utils::u256_to_h256; -use evm::{Capture, RuntimeState}; +use evm::{Capture, GasedMachine}; use primitive_types::U256; use std::collections::{BTreeMap, BTreeSet}; @@ -59,7 +59,7 @@ pub fn run_test(_filename: &str, _test_name: &str, test: Test, debug: bool) -> R .collect::>(); let etable = Etable::runtime(); - let invoker = Invoker::new(&config); + let invoker = Invoker::<_, Gasometer, _, _, _>::new(&config, &etable); let args = TransactArgs::Call { caller: test.transaction.sender, address: test.transaction.to, @@ -93,36 +93,27 @@ pub fn run_test(_filename: &str, _test_name: &str, test: Test, debug: bool) -> R let mut step_backend = run_backend.clone(); // Run - let run_result = evm::transact::( - args.clone(), - Some(4), - &mut run_backend, - &invoker, - &etable, - ); + let run_result = evm::transact(args.clone(), Some(4), &mut run_backend, &invoker); run_backend.layers[0].clear_pending(); // Step if debug { - let _step_result = evm::HeapTransact::::new( - args, - &invoker, - &mut step_backend, - ) - .and_then(|mut stepper| loop { - { - let machine = stepper.last_machine()?; - println!( - "pc: {}, opcode: {:?}, gas: 0x{:x}", - machine.machine.position(), - machine.machine.peek_opcode(), - machine.gasometer.gas(), - ); - } - if let Err(Capture::Exit(result)) = stepper.step(&etable) { - break result; - } - }); + let _step_result = evm::HeapTransact::new(args, &invoker, &mut step_backend).and_then( + |mut stepper| loop { + { + let machine: &GasedMachine<_, Gasometer> = stepper.last_machine()?; + println!( + "pc: {}, opcode: {:?}, gas: 0x{:x}", + machine.machine.position(), + machine.machine.peek_opcode(), + machine.gasometer.gas(), + ); + } + if let Err(Capture::Exit(result)) = stepper.step() { + break result; + } + }, + ); step_backend.layers[0].clear_pending(); } diff --git a/src/call_stack.rs b/src/call_stack.rs index df0c2a1ee..bf1bcef40 100644 --- a/src/call_stack.rs +++ b/src/call_stack.rs @@ -1,16 +1,13 @@ -use crate::{ - Capture, Control, Etable, ExitError, ExitFatal, ExitResult, GasedMachine, Gasometer, Invoker, - Machine, Opcode, RuntimeState, -}; +use crate::{Capture, ExitError, ExitFatal, ExitResult, Invoker}; use core::convert::Infallible; -struct Substack { +struct Substack { invoke: TrD, - machine: GasedMachine, + machine: M, } -struct LastSubstack { - machine: GasedMachine, +struct LastSubstack { + machine: M, status: LastSubstackStatus, } @@ -22,22 +19,20 @@ enum LastSubstackStatus { // Note: this should not be exposed to public because it does not implement // Drop. -struct CallStack<'backend, 'invoker, S, G, H, Tr, I: Invoker> { - stack: Vec>, - last: Option>, +struct CallStack<'backend, 'invoker, H, Tr, I: Invoker> { + stack: Vec>, + last: Option>, initial_depth: usize, backend: &'backend mut H, invoker: &'invoker I, } -impl<'backend, 'invoker, S, G, H, Tr, I> CallStack<'backend, 'invoker, S, G, H, Tr, I> +impl<'backend, 'invoker, H, Tr, I> CallStack<'backend, 'invoker, H, Tr, I> where - S: AsMut, - G: Gasometer, - I: Invoker, + I: Invoker, { pub fn new( - machine: GasedMachine, + machine: I::Machine, initial_depth: usize, backend: &'backend mut H, invoker: &'invoker I, @@ -56,15 +51,9 @@ where call_stack } - pub fn run( - &mut self, - etable: &Etable, - ) -> Capture), ExitFatal>, I::Interrupt> - where - F: Fn(&mut Machine, &mut H, Opcode, usize) -> Control, - { + pub fn run(&mut self) -> Capture, I::Interrupt> { loop { - let step_ret = self.step_run(etable); + let step_ret = self.step_run(); if let Err(step_ret) = step_ret { return step_ret; @@ -72,15 +61,11 @@ where } } - pub fn step( + pub fn step( &mut self, - etable: &Etable, - ) -> Result<(), Capture), ExitFatal>, I::Interrupt>> - where - F: Fn(&mut Machine, &mut H, Opcode, usize) -> Control, - { - self.step_with(etable, |machine, handler, etable| { - let result = machine.step(handler, etable); + ) -> Result<(), Capture, I::Interrupt>> { + self.step_with(|invoker, machine, handler| { + let result = invoker.step_machine(machine, handler); match result { Ok(()) => LastSubstackStatus::Running, Err(result) => LastSubstackStatus::Exited(result), @@ -88,27 +73,21 @@ where }) } - pub fn step_run( + pub fn step_run( &mut self, - etable: &Etable, - ) -> Result<(), Capture), ExitFatal>, I::Interrupt>> - where - F: Fn(&mut Machine, &mut H, Opcode, usize) -> Control, - { - self.step_with(etable, |machine, handler, etable| { - let result = machine.run(handler, etable); + ) -> Result<(), Capture, I::Interrupt>> { + self.step_with(|invoker, machine, handler| { + let result = invoker.run_machine(machine, handler); LastSubstackStatus::Exited(result) }) } - fn step_with( + fn step_with( &mut self, - etable: &Etable, fs: FS, - ) -> Result<(), Capture), ExitFatal>, I::Interrupt>> + ) -> Result<(), Capture, I::Interrupt>> where - F: Fn(&mut Machine, &mut H, Opcode, usize) -> Control, - FS: Fn(&mut GasedMachine, &mut H, &Etable) -> LastSubstackStatus, + FS: Fn(&I, &mut I::Machine, &mut H) -> LastSubstackStatus, { let mut step_ret = None; @@ -128,7 +107,7 @@ where status: LastSubstackStatus::Running, mut machine, }) => { - let status = fs(&mut machine, self.backend, etable); + let status = fs(self.invoker, &mut machine, self.backend); Some(LastSubstack { status, machine }) } Some(LastSubstack { @@ -208,21 +187,17 @@ where } } -fn execute( - mut machine: GasedMachine, +fn execute( + mut machine: I::Machine, initial_depth: usize, heap_depth: Option, backend: &mut H, invoker: &I, - etable: &Etable, -) -> Result<(ExitResult, GasedMachine), ExitFatal> +) -> Result<(ExitResult, I::Machine), ExitFatal> where - S: AsMut, - G: Gasometer, - I: Invoker, - F: Fn(&mut Machine, &mut H, Opcode, usize) -> Control, + I: Invoker, { - let mut result = machine.run(backend, etable); + let mut result = invoker.run_machine(&mut machine, backend); loop { match result { @@ -235,20 +210,13 @@ where .unwrap_or(false) { match CallStack::new(sub_machine, initial_depth + 1, backend, invoker) - .run(etable) + .run() { Capture::Exit(v) => v?, Capture::Trap(infallible) => match infallible {}, } } else { - execute( - sub_machine, - initial_depth + 1, - heap_depth, - backend, - invoker, - etable, - )? + execute(sub_machine, initial_depth + 1, heap_depth, backend, invoker)? }; match invoker.exit_substack( @@ -259,7 +227,7 @@ where backend, ) { Ok(()) => { - result = machine.run(backend, etable); + result = invoker.run_machine(&mut machine, backend); } Err(err) => return Ok((Err(err), machine)), } @@ -272,24 +240,14 @@ where } } -pub struct HeapTransact< - 'backend, - 'invoker, - S: AsMut, - G: Gasometer, - H, - Tr, - I: Invoker, -> { - call_stack: CallStack<'backend, 'invoker, S, G, H, Tr, I>, +pub struct HeapTransact<'backend, 'invoker, H, Tr, I: Invoker> { + call_stack: CallStack<'backend, 'invoker, H, Tr, I>, transact_invoke: I::TransactInvoke, } -impl<'backend, 'invoker, S, G, H, Tr, I> HeapTransact<'backend, 'invoker, S, G, H, Tr, I> +impl<'backend, 'invoker, H, Tr, I> HeapTransact<'backend, 'invoker, H, Tr, I> where - S: AsMut, - G: Gasometer, - I: Invoker, + I: Invoker, { pub fn new( args: I::TransactArgs, @@ -305,22 +263,16 @@ where }) } - fn step_with( + fn step_with( &mut self, - etable: &Etable, fs: FS, ) -> Result<(), Capture, I::Interrupt>> where - F: Fn(&mut Machine, &mut H, Opcode, usize) -> Control, FS: Fn( - &mut CallStack, - &Etable, - ) -> Result< - (), - Capture), ExitFatal>, I::Interrupt>, - >, + &mut CallStack, + ) -> Result<(), Capture, I::Interrupt>>, { - match fs(&mut self.call_stack, etable) { + match fs(&mut self.call_stack) { Ok(()) => Ok(()), Err(Capture::Trap(interrupt)) => Err(Capture::Trap(interrupt)), Err(Capture::Exit(Err(fatal))) => Err(Capture::Exit(Err(fatal.into()))), @@ -335,35 +287,21 @@ where } } - pub fn step_run( + pub fn step_run( &mut self, - etable: &Etable, - ) -> Result<(), Capture, I::Interrupt>> - where - F: Fn(&mut Machine, &mut H, Opcode, usize) -> Control, - { - self.step_with(etable, |call_stack, etable| call_stack.step_run(etable)) + ) -> Result<(), Capture, I::Interrupt>> { + self.step_with(|call_stack| call_stack.step_run()) } - pub fn step( + pub fn step( &mut self, - etable: &Etable, - ) -> Result<(), Capture, I::Interrupt>> - where - F: Fn(&mut Machine, &mut H, Opcode, usize) -> Control, - { - self.step_with(etable, |call_stack, etable| call_stack.step(etable)) + ) -> Result<(), Capture, I::Interrupt>> { + self.step_with(|call_stack| call_stack.step()) } - pub fn run( - &mut self, - etable: &Etable, - ) -> Capture, I::Interrupt> - where - F: Fn(&mut Machine, &mut H, Opcode, usize) -> Control, - { + pub fn run(&mut self) -> Capture, I::Interrupt> { loop { - let step_ret = self.step_run(etable); + let step_ret = self.step_run(); if let Err(step_ret) = step_ret { return step_ret; @@ -371,7 +309,7 @@ where } } - pub fn last_machine(&self) -> Result<&GasedMachine, ExitError> { + pub fn last_machine(&self) -> Result<&I::Machine, ExitError> { match &self.call_stack.last { Some(last) => Ok(&last.machine), None => Err(ExitFatal::AlreadyExited.into()), @@ -379,11 +317,9 @@ where } } -impl<'backend, 'invoker, S, G, H, Tr, I> Drop for HeapTransact<'backend, 'invoker, S, G, H, Tr, I> +impl<'backend, 'invoker, H, Tr, I> Drop for HeapTransact<'backend, 'invoker, H, Tr, I> where - S: AsMut, - G: Gasometer, - I: Invoker, + I: Invoker, { fn drop(&mut self) { if let Some(mut last) = self.call_stack.last.take() { @@ -418,20 +354,16 @@ where } } -pub fn transact( +pub fn transact( args: I::TransactArgs, heap_depth: Option, backend: &mut H, invoker: &I, - etable: &Etable, ) -> Result where - S: AsMut, - G: Gasometer, - I: Invoker, - F: Fn(&mut Machine, &mut H, Opcode, usize) -> Control, + I: Invoker, { let (transact_invoke, machine) = invoker.new_transact(args, backend)?; - let (ret, machine) = execute(machine, 0, heap_depth, backend, invoker, etable)?; + let (ret, machine) = execute(machine, 0, heap_depth, backend, invoker)?; invoker.finalize_transact(&transact_invoke, ret, machine, backend) } diff --git a/src/invoker.rs b/src/invoker.rs index a5188e948..c12dcb119 100644 --- a/src/invoker.rs +++ b/src/invoker.rs @@ -1,6 +1,7 @@ -use crate::{Capture, ExitError, ExitResult, GasedMachine}; +use crate::{Capture, ExitError, ExitResult}; -pub trait Invoker { +pub trait Invoker { + type Machine; type Interrupt; type TransactArgs; @@ -8,33 +9,41 @@ pub trait Invoker { type TransactValue; type SubstackInvoke; + fn run_machine(&self, machine: &mut Self::Machine, handler: &mut H) -> Capture; + fn step_machine( + &self, + machine: &mut Self::Machine, + handler: &mut H, + ) -> Result<(), Capture>; + fn new_transact( &self, args: Self::TransactArgs, handler: &mut H, - ) -> Result<(Self::TransactInvoke, GasedMachine), ExitError>; + ) -> Result<(Self::TransactInvoke, Self::Machine), ExitError>; + fn finalize_transact( &self, invoke: &Self::TransactInvoke, exit: ExitResult, - machine: GasedMachine, + machine: Self::Machine, handler: &mut H, ) -> Result; fn exit_substack( &self, result: ExitResult, - child: GasedMachine, + child: Self::Machine, trap_data: Self::SubstackInvoke, - parent: &mut GasedMachine, + parent: &mut Self::Machine, handler: &mut H, ) -> Result<(), ExitError>; fn enter_substack( &self, trap: Tr, - machine: &mut GasedMachine, + machine: &mut Self::Machine, handler: &mut H, depth: usize, - ) -> Capture), ExitError>, Self::Interrupt>; + ) -> Capture, Self::Interrupt>; } diff --git a/src/standard/invoker/mod.rs b/src/standard/invoker/mod.rs index fa631f8a0..ba559719f 100644 --- a/src/standard/invoker/mod.rs +++ b/src/standard/invoker/mod.rs @@ -3,13 +3,15 @@ mod routines; use super::{Config, MergeableRuntimeState, TransactGasometer}; use crate::call_create::{CallCreateTrapData, CallTrapData, CreateScheme, CreateTrapData}; use crate::{ - Capture, Context, ExitError, ExitException, ExitResult, ExitSucceed, GasedMachine, - Gasometer as GasometerT, Invoker as InvokerT, MergeStrategy, Opcode, RuntimeBackend, - RuntimeEnvironment, RuntimeState, TransactionContext, TransactionalBackend, Transfer, + Capture, Context, Control, Etable, ExitError, ExitException, ExitResult, ExitSucceed, + GasedMachine, Gasometer as GasometerT, Invoker as InvokerT, Machine, MergeStrategy, Opcode, + RuntimeBackend, RuntimeEnvironment, RuntimeState, TransactionContext, TransactionalBackend, + Transfer, }; use alloc::rc::Rc; use core::cmp::min; use core::convert::Infallible; +use core::marker::PhantomData; use primitive_types::{H160, H256, U256}; use sha3::{Digest, Keccak256}; @@ -98,29 +100,53 @@ impl TransactArgs { } } -pub struct Invoker<'config> { +pub struct Invoker<'config, 'etable, S, G, H, Tr, F> { config: &'config Config, + etable: &'etable Etable, + _marker: PhantomData, } -impl<'config> Invoker<'config> { - pub fn new(config: &'config Config) -> Self { - Self { config } +impl<'config, 'etable, S, G, H, Tr, F> Invoker<'config, 'etable, S, G, H, Tr, F> { + pub fn new(config: &'config Config, etable: &'etable Etable) -> Self { + Self { + config, + etable, + _marker: PhantomData, + } } } -impl<'config, S, G, H, Tr> InvokerT for Invoker<'config> +impl<'config, 'etable, S, G, H, Tr, F> InvokerT for Invoker<'config, 'etable, S, G, H, Tr, F> where S: MergeableRuntimeState, G: GasometerT + TransactGasometer<'config, S>, H: RuntimeEnvironment + RuntimeBackend + TransactionalBackend, Tr: IntoCallCreateTrap, + F: Fn(&mut Machine, &mut H, Opcode, usize) -> Control, { + type Machine = GasedMachine; type Interrupt = Tr::Interrupt; type TransactArgs = TransactArgs; type TransactInvoke = TransactInvoke; type TransactValue = (ExitSucceed, Option); type SubstackInvoke = SubstackInvoke; + fn run_machine( + &self, + machine: &mut GasedMachine, + handler: &mut H, + ) -> Capture { + machine.run(handler, self.etable) + } + + fn step_machine( + &self, + machine: &mut GasedMachine, + handler: &mut H, + ) -> Result<(), Capture> { + machine.step(handler, self.etable) + } + fn new_transact( &self, args: TransactArgs, @@ -208,7 +234,7 @@ where G::new_transact_call(gas_limit, &code, &data, &access_list, self.config)?; let machine = routines::make_enter_call_machine( - self, + self.config, code, data, false, // is_static @@ -245,7 +271,7 @@ where G::new_transact_create(gas_limit, &init_code, &access_list, self.config)?; let machine = routines::make_enter_create_machine( - self, + self.config, caller, init_code, false, // is_static @@ -289,7 +315,7 @@ where let retbuf = machine.machine.retval; routines::deploy_create_code( - self, + self.config, address, &retbuf, &mut machine.gasometer, @@ -390,7 +416,7 @@ where }); Capture::Exit(routines::enter_call_substack( - self, + self.config, code, call_trap_data, is_static, @@ -414,7 +440,7 @@ where }); Capture::Exit(routines::enter_create_substack( - self, + self.config, code, create_trap_data, is_static, @@ -448,7 +474,7 @@ where let mut child_gasometer = child.gasometer; let result = result.and_then(|_| { routines::deploy_create_code( - self, + self.config, address, &retbuf, &mut child_gasometer, diff --git a/src/standard/invoker/routines.rs b/src/standard/invoker/routines.rs index 362dac86e..7f87f5057 100644 --- a/src/standard/invoker/routines.rs +++ b/src/standard/invoker/routines.rs @@ -1,4 +1,4 @@ -use super::{CallTrapData, CreateTrapData, Invoker, SubstackInvoke}; +use super::{CallTrapData, CreateTrapData, SubstackInvoke}; use crate::standard::{Config, MergeableRuntimeState}; use crate::{ ExitError, ExitException, GasedMachine, Gasometer as GasometerT, Machine, MergeStrategy, @@ -8,7 +8,7 @@ use alloc::rc::Rc; use primitive_types::{H160, U256}; pub fn make_enter_call_machine<'config, S, G, H>( - invoker: &Invoker<'config>, + config: &'config Config, code: Vec, input: Vec, is_static: bool, @@ -33,8 +33,8 @@ where let machine = Machine::::new( Rc::new(code), Rc::new(input.clone()), - invoker.config.stack_limit, - invoker.config.memory_limit, + config.stack_limit, + config.memory_limit, state, ); @@ -46,7 +46,7 @@ where } pub fn make_enter_create_machine<'config, S, G, H>( - invoker: &Invoker<'config>, + config: &'config Config, caller: H160, init_code: Vec, is_static: bool, @@ -60,7 +60,7 @@ where G: GasometerT, H: RuntimeEnvironment + RuntimeBackend + TransactionalBackend, { - if let Some(limit) = invoker.config.max_initcode_size { + if let Some(limit) = config.max_initcode_size { if init_code.len() > limit { return Err(ExitException::CreateContractLimit.into()); } @@ -77,7 +77,7 @@ where return Err(ExitException::CreateCollision.into()); } handler.inc_nonce(caller)?; - if invoker.config.create_increase_nonce { + if config.create_increase_nonce { handler.inc_nonce(state.as_ref().context.address)?; } @@ -86,8 +86,8 @@ where let machine = Machine::new( Rc::new(init_code), Rc::new(Vec::new()), - invoker.config.stack_limit, - invoker.config.memory_limit, + config.stack_limit, + config.memory_limit, state, ); @@ -99,7 +99,7 @@ where } pub fn enter_call_substack<'config, S, G, H>( - invoker: &Invoker<'config>, + config: &'config Config, code: Vec, trap_data: CallTrapData, is_static: bool, @@ -116,7 +116,7 @@ where let work = || -> Result<(SubstackInvoke, GasedMachine), ExitError> { let machine = make_enter_call_machine( - invoker, + config, code, trap_data.input.clone(), is_static, @@ -139,7 +139,7 @@ where } pub fn enter_create_substack<'config, S, G, H>( - invoker: &Invoker<'config>, + config: &'config Config, code: Vec, trap_data: CreateTrapData, is_static: bool, @@ -171,7 +171,7 @@ where }; let machine = make_enter_create_machine( - invoker, caller, code, is_static, transfer, state, gasometer, handler, + config, caller, code, is_static, transfer, state, gasometer, handler, )?; Ok(( @@ -200,7 +200,7 @@ fn check_first_byte(config: &Config, code: &[u8]) -> Result<(), ExitError> { } pub fn deploy_create_code<'config, S, G, H>( - invoker: &Invoker<'config>, + config: &'config Config, address: H160, retbuf: &Vec, gasometer: &mut G, @@ -211,9 +211,9 @@ where G: GasometerT, H: RuntimeEnvironment + RuntimeBackend + TransactionalBackend, { - check_first_byte(invoker.config, &retbuf[..])?; + check_first_byte(config, &retbuf[..])?; - if let Some(limit) = invoker.config.create_contract_limit { + if let Some(limit) = config.create_contract_limit { if retbuf.len() > limit { return Err(ExitException::CreateContractLimit.into()); }