From 954046ab836bdc42e7eb6044c2136588cacd1af6 Mon Sep 17 00:00:00 2001 From: Wei Tang Date: Wed, 15 Nov 2023 11:32:13 +0100 Subject: [PATCH 1/2] Refactor invoker with routines --- src/standard/{invoker.rs => invoker/mod.rs} | 204 ++++++-------------- src/standard/invoker/routines.rs | 143 ++++++++++++++ 2 files changed, 205 insertions(+), 142 deletions(-) rename src/standard/{invoker.rs => invoker/mod.rs} (81%) create mode 100644 src/standard/invoker/routines.rs diff --git a/src/standard/invoker.rs b/src/standard/invoker/mod.rs similarity index 81% rename from src/standard/invoker.rs rename to src/standard/invoker/mod.rs index 582011b03..ea0de4c0c 100644 --- a/src/standard/invoker.rs +++ b/src/standard/invoker/mod.rs @@ -1,3 +1,6 @@ +mod routines; + +use self::routines::try_or_oog; use super::{gasometer::TransactionCost, Config, Etable, GasedMachine, Gasometer, Machine}; use crate::call_create::{CallCreateTrapData, CallTrapData, CreateScheme, CreateTrapData}; use crate::{ @@ -57,94 +60,72 @@ impl<'config> Invoker<'config> { where H: RuntimeEnvironment + RuntimeBackend + TransactionalBackend, { - let gas_fee = gas_limit.saturating_mul(gas_price); - handler.withdrawal(caller, gas_fee)?; - - handler.push_substate(); - - let work = || -> ExitResult { - let gas_limit = if gas_limit > U256::from(u64::MAX) { - return Err(ExitException::OutOfGas.into()); - } else { - gas_limit.as_u64() - }; + routines::transact_and_work( + self, + caller, + gas_limit, + gas_price, + handler, + |handler: &mut H| -> (ExitResult, U256) { + let gas_limit = if gas_limit > U256::from(u64::MAX) { + return (Err(ExitException::OutOfGas.into()), U256::zero()); + } else { + gas_limit.as_u64() + }; - let context = Context { - caller, - address, - apparent_value: value, - }; - let code = handler.code(address); + let context = Context { + caller, + address, + apparent_value: value, + }; + let transfer = Transfer { + source: caller, + target: address, + value, + }; + let transaction_context = TransactionContext { + origin: caller, + gas_price, + }; - let transaction_cost = TransactionCost::call(&data, &access_list).cost(self.config); + let transaction_cost = TransactionCost::call(&data, &access_list).cost(self.config); + if gas_limit < transaction_cost { + return (Err(ExitException::OutOfGas.into()), U256::zero()); + } - let machine = Machine::new( - Rc::new(code), - Rc::new(data), - self.config.stack_limit, - self.config.memory_limit, - RuntimeState { + let mut machine = try_or_oog!(routines::make_enter_call_machine( + self, + true, // is_transaction + address, + data, + gas_limit, + false, // is_static + Some(transfer), context, - transaction_context: TransactionContext { - origin: caller, - gas_price, - } - .into(), - retbuf: Vec::new(), - gas: U256::zero(), - }, - ); - let mut gasometer = Gasometer::new(gas_limit, &machine, self.config); + Rc::new(transaction_context), + handler + )); - gasometer.record_cost(transaction_cost)?; + try_or_oog!(machine.gasometer.record_cost(transaction_cost)); - if self.config.increase_state_access_gas { - if self.config.warm_coinbase_address { - let coinbase = handler.block_coinbase(); - handler.mark_hot(coinbase, None)?; + if self.config.increase_state_access_gas { + if self.config.warm_coinbase_address { + let coinbase = handler.block_coinbase(); + try_or_oog!(handler.mark_hot(coinbase, None)); + } + try_or_oog!(handler.mark_hot(caller, None)); + try_or_oog!(handler.mark_hot(address, None)); } - handler.mark_hot(caller, None)?; - handler.mark_hot(address, None)?; - } - handler.inc_nonce(caller)?; + try_or_oog!(handler.inc_nonce(caller)); - let transfer = Transfer { - source: caller, - target: address, - value, - }; - handler.transfer(transfer)?; + let (machine, result) = + crate::execute(machine, handler, 0, DEFAULT_HEAP_DEPTH, self, etable); - let machine = GasedMachine { - machine, - gasometer, - is_static: false, - }; - - let (machine, result) = - crate::execute(machine, handler, 0, DEFAULT_HEAP_DEPTH, self, etable); - - let refunded_gas = U256::from(machine.gasometer.gas()); - let refunded_fee = refunded_gas * gas_price; - let coinbase_reward = gas_fee - refunded_fee; - - handler.deposit(caller, refunded_fee); - handler.deposit(handler.block_coinbase(), coinbase_reward); - - result - }; - - match work() { - Ok(exit) => { - handler.pop_substate(TransactionalMergeStrategy::Commit); - Ok(exit) - } - Err(err) => { - handler.pop_substate(TransactionalMergeStrategy::Discard); - Err(err) - } - } + let refunded_gas = U256::from(machine.gasometer.gas()); + (result, refunded_gas) + }, + ) } pub fn transact_create( @@ -493,13 +474,13 @@ where is_static, transaction_context, trap, - } => enter_call_trap_stack( + } => routines::enter_call_trap_stack( + self, + trap, gas_limit, is_static, transaction_context, - trap, handler, - self.config, ), } } @@ -707,64 +688,3 @@ where Ok(address) } - -fn enter_call_trap_stack<'config, H>( - mut gas_limit: u64, - is_static: bool, - transaction_context: Rc, - trap_data: CallTrapData, - handler: &mut H, - config: &'config Config, -) -> Result<(CallCreateTrapEnterData, GasedMachine<'config>), ExitError> -where - H: RuntimeEnvironment + RuntimeBackend + TransactionalBackend, -{ - handler.push_substate(); - - let work = || -> Result<(CallCreateTrapEnterData, GasedMachine<'config>), ExitError> { - handler.mark_hot(trap_data.context.address, None)?; - let code = handler.code(trap_data.target); - - if let Some(transfer) = trap_data.transfer.clone() { - if transfer.value != U256::zero() { - gas_limit = gas_limit.saturating_add(config.call_stipend); - } - - handler.transfer(transfer)?; - } - - // TODO: precompile contracts. - - let machine = Machine::new( - Rc::new(code), - Rc::new(trap_data.input.clone()), - config.stack_limit, - config.memory_limit, - RuntimeState { - context: trap_data.context.clone(), - transaction_context, - retbuf: Vec::new(), - gas: U256::zero(), - }, - ); - - let gasometer = Gasometer::new(gas_limit, &machine, config); - - Ok(( - CallCreateTrapEnterData::Call { trap: trap_data }, - GasedMachine { - machine, - gasometer, - is_static, - }, - )) - }; - - match work() { - Ok(machine) => Ok(machine), - Err(err) => { - handler.pop_substate(TransactionalMergeStrategy::Discard); - Err(err) - } - } -} diff --git a/src/standard/invoker/routines.rs b/src/standard/invoker/routines.rs new file mode 100644 index 000000000..58eec04ad --- /dev/null +++ b/src/standard/invoker/routines.rs @@ -0,0 +1,143 @@ +use super::{CallCreateTrapEnterData, CallTrapData, Invoker}; +use crate::standard::{GasedMachine, Gasometer, Machine}; +use crate::{ + Context, ExitError, RuntimeBackend, RuntimeEnvironment, RuntimeState, TransactionContext, + TransactionalBackend, TransactionalMergeStrategy, Transfer, +}; +use alloc::rc::Rc; +use primitive_types::{H160, U256}; + +macro_rules! try_or_oog { + ($e:expr) => { + match $e { + Ok(v) => v, + Err(e) => return (Err(e), ::primitive_types::U256::zero()), + } + } +} +pub(crate) use try_or_oog; + +pub fn transact_and_work<'config, H, R, F>( + _invoker: &Invoker<'config>, + caller: H160, + gas_limit: U256, + gas_price: U256, + handler: &mut H, + f: F, +) -> Result +where + H: RuntimeEnvironment + RuntimeBackend + TransactionalBackend, + F: FnOnce(&mut H) -> (Result, U256), +{ + let gas_fee = gas_limit.saturating_mul(gas_price); + handler.withdrawal(caller, gas_fee)?; + + handler.push_substate(); + + let (result, refunded_gas) = f(handler); + let refunded_fee = refunded_gas.saturating_mul(gas_price); + let coinbase_reward = gas_fee.saturating_sub(refunded_fee); + + handler.deposit(caller, refunded_fee); + handler.deposit(handler.block_coinbase(), coinbase_reward); + + match result { + Ok(exit) => { + handler.pop_substate(TransactionalMergeStrategy::Commit); + Ok(exit) + } + Err(err) => { + handler.pop_substate(TransactionalMergeStrategy::Discard); + Err(err) + } + } +} + +pub fn make_enter_call_machine<'config, H>( + invoker: &Invoker<'config>, + is_transaction: bool, + target: H160, + input: Vec, + mut gas_limit: u64, + is_static: bool, + transfer: Option, + context: Context, + transaction_context: Rc, + handler: &mut H, +) -> Result, ExitError> +where + H: RuntimeEnvironment + RuntimeBackend + TransactionalBackend, +{ + handler.mark_hot(context.address, None)?; + let code = handler.code(target); + + if let Some(transfer) = transfer.clone() { + if !is_transaction && transfer.value != U256::zero() { + gas_limit = gas_limit.saturating_add(invoker.config.call_stipend); + } + + handler.transfer(transfer)?; + } + + // TODO: precompile contracts. + + let machine = Machine::new( + Rc::new(code), + Rc::new(input.clone()), + invoker.config.stack_limit, + invoker.config.memory_limit, + RuntimeState { + context: context.clone(), + transaction_context, + retbuf: Vec::new(), + gas: U256::zero(), + }, + ); + + let gasometer = Gasometer::new(gas_limit, &machine, invoker.config); + + Ok(GasedMachine { + machine, + gasometer, + is_static, + }) +} + +pub fn enter_call_trap_stack<'config, H>( + invoker: &Invoker<'config>, + trap_data: CallTrapData, + gas_limit: u64, + is_static: bool, + transaction_context: Rc, + handler: &mut H, +) -> Result<(CallCreateTrapEnterData, GasedMachine<'config>), ExitError> +where + H: RuntimeEnvironment + RuntimeBackend + TransactionalBackend, +{ + handler.push_substate(); + + let work = || -> Result<(CallCreateTrapEnterData, GasedMachine<'config>), ExitError> { + let machine = make_enter_call_machine( + invoker, + false, // is_transaction + trap_data.target, + trap_data.input.clone(), + gas_limit, + is_static, + trap_data.transfer.clone(), + trap_data.context.clone(), + transaction_context, + handler, + )?; + + Ok((CallCreateTrapEnterData::Call { trap: trap_data }, machine)) + }; + + match work() { + Ok(machine) => Ok(machine), + Err(err) => { + handler.pop_substate(TransactionalMergeStrategy::Discard); + Err(err) + } + } +} From bcb33e2bb5aa9b88e2e659ce3bb9c225e9471f90 Mon Sep 17 00:00:00 2001 From: Wei Tang Date: Wed, 15 Nov 2023 12:01:27 +0100 Subject: [PATCH 2/2] Refactor create into subroutines --- interpreter/tests/usability.rs | 1 - src/standard/invoker/mod.rs | 458 +++++++++---------------------- src/standard/invoker/routines.rs | 166 ++++++++++- 3 files changed, 288 insertions(+), 337 deletions(-) diff --git a/interpreter/tests/usability.rs b/interpreter/tests/usability.rs index 1494b9dac..4a28da8da 100644 --- a/interpreter/tests/usability.rs +++ b/interpreter/tests/usability.rs @@ -1,7 +1,6 @@ use evm_interpreter::{ Capture, Context, Control, Etable, ExitError, ExitSucceed, Log, Machine, Opcode, RuntimeBackend, RuntimeBaseBackend, RuntimeEnvironment, RuntimeState, TransactionContext, - Transfer, }; use primitive_types::{H160, H256, U256}; use std::rc::Rc; diff --git a/src/standard/invoker/mod.rs b/src/standard/invoker/mod.rs index ea0de4c0c..92cf0a011 100644 --- a/src/standard/invoker/mod.rs +++ b/src/standard/invoker/mod.rs @@ -1,7 +1,7 @@ mod routines; use self::routines::try_or_oog; -use super::{gasometer::TransactionCost, Config, Etable, GasedMachine, Gasometer, Machine}; +use super::{gasometer::TransactionCost, Config, Etable, GasedMachine, Gasometer}; use crate::call_create::{CallCreateTrapData, CallTrapData, CreateScheme, CreateTrapData}; use crate::{ Capture, Context, ExitError, ExitException, ExitResult, Gasometer as GasometerT, @@ -142,111 +142,71 @@ impl<'config> Invoker<'config> { where H: RuntimeEnvironment + RuntimeBackend + TransactionalBackend, { - let gas_fee = gas_limit.saturating_mul(gas_price); - handler.withdrawal(caller, gas_fee)?; + routines::transact_and_work( + self, + caller, + gas_limit, + gas_price, + handler, + |handler| -> (Result, U256) { + let gas_limit = if gas_limit > U256::from(u64::MAX) { + return (Err(ExitException::OutOfGas.into()), U256::zero()); + } else { + gas_limit.as_u64() + }; - handler.push_substate(); + let scheme = CreateScheme::Legacy { caller }; + let address = scheme.address(handler); - let work = || -> Result { - if let Some(limit) = self.config.max_initcode_size { - if init_code.len() > limit { - return Err(ExitException::CreateContractLimit.into()); + let context = Context { + caller, + address, + apparent_value: value, + }; + let transaction_context = TransactionContext { + origin: caller, + gas_price, + }; + let transfer = Transfer { + source: caller, + target: address, + value, + }; + let transaction_cost = + TransactionCost::create(&init_code, &access_list).cost(self.config); + if gas_limit < transaction_cost { + return (Err(ExitException::OutOfGas.into()), U256::zero()); } - } - let gas_limit = if gas_limit > U256::from(u64::MAX) { - return Err(ExitException::OutOfGas.into()); - } else { - gas_limit.as_u64() - }; - - let scheme = CreateScheme::Legacy { caller }; - let address = scheme.address(handler); - - if self.config.create_increase_nonce { - handler.inc_nonce(address)?; - } - - let context = Context { - caller, - address, - apparent_value: value, - }; - - let transaction_cost = - TransactionCost::create(&init_code, &access_list).cost(self.config); - - let machine = Machine::new( - Rc::new(init_code), - Rc::new(Vec::new()), - self.config.stack_limit, - self.config.memory_limit, - RuntimeState { + let mut machine = try_or_oog!(routines::make_enter_create_machine( + self, + caller, + address, + init_code, + gas_limit, + false, + transfer, context, - transaction_context: TransactionContext { - origin: caller, - gas_price, - } - .into(), - retbuf: Vec::new(), - gas: U256::zero(), - }, - ); - let mut gasometer = Gasometer::new(gas_limit, &machine, self.config); - - gasometer.record_cost(transaction_cost)?; - - let transfer = Transfer { - source: caller, - target: address, - value, - }; - handler.transfer(transfer)?; - - let machine = GasedMachine { - machine, - gasometer, - is_static: false, - }; - - let (mut machine, result) = - crate::execute(machine, handler, 0, DEFAULT_HEAP_DEPTH, self, etable); - result?; - - let retbuf = machine.machine.into_retbuf(); - check_first_byte(self.config, &retbuf[..])?; - - if let Some(limit) = self.config.create_contract_limit { - if retbuf.len() > limit { - return Err(ExitException::CreateContractLimit.into()); - } - } - - GasometerT::::record_codedeposit( - &mut machine.gasometer, - retbuf.len(), - )?; - - let refunded_gas = U256::from(machine.gasometer.gas()); - let refunded_fee = refunded_gas * gas_price; - let coinbase_reward = gas_fee - refunded_fee; - - handler.deposit(caller, refunded_fee); - handler.deposit(handler.block_coinbase(), coinbase_reward); + Rc::new(transaction_context), + handler, + )); + try_or_oog!(machine.gasometer.record_cost(transaction_cost)); - Ok(address) - }; + let (mut machine, result) = + crate::execute(machine, handler, 0, DEFAULT_HEAP_DEPTH, self, etable); + let retbuf = machine.machine.into_retbuf(); + let address = try_or_oog!(routines::deploy_create_code( + self, + result.map(|_| address), + &retbuf, + &mut machine.gasometer, + handler + )); - match work() { - Ok(exit) => { - handler.pop_substate(TransactionalMergeStrategy::Commit); - Ok(exit) - } - Err(err) => { - handler.pop_substate(TransactionalMergeStrategy::Discard); - Err(err) - } - } + let refunded_gas = U256::from(machine.gasometer.gas()); + (Ok(address), refunded_gas) + }, + ) } pub fn transact_create2( @@ -264,115 +224,75 @@ impl<'config> Invoker<'config> { where H: RuntimeEnvironment + RuntimeBackend + TransactionalBackend, { - let gas_fee = gas_limit.saturating_mul(gas_price); - handler.withdrawal(caller, gas_fee)?; + routines::transact_and_work( + self, + caller, + gas_limit, + gas_price, + handler, + |handler| -> (Result, U256) { + let gas_limit = if gas_limit > U256::from(u64::MAX) { + return (Err(ExitException::OutOfGas.into()), U256::zero()); + } else { + gas_limit.as_u64() + }; - handler.push_substate(); + let scheme = CreateScheme::Create2 { + caller, + code_hash: H256::from_slice(Keccak256::digest(&init_code).as_slice()), + salt, + }; + let address = scheme.address(handler); - let work = || -> Result { - if let Some(limit) = self.config.max_initcode_size { - if init_code.len() > limit { - return Err(ExitException::CreateContractLimit.into()); + let context = Context { + caller, + address, + apparent_value: value, + }; + let transaction_context = TransactionContext { + origin: caller, + gas_price, + }; + let transfer = Transfer { + source: caller, + target: address, + value, + }; + let transaction_cost = + TransactionCost::create(&init_code, &access_list).cost(self.config); + if gas_limit < transaction_cost { + return (Err(ExitException::OutOfGas.into()), U256::zero()); } - } - - let gas_limit = if gas_limit > U256::from(u64::MAX) { - return Err(ExitException::OutOfGas.into()); - } else { - gas_limit.as_u64() - }; - - let scheme = CreateScheme::Create2 { - caller, - code_hash: H256::from_slice(Keccak256::digest(&init_code).as_slice()), - salt, - }; - let address = scheme.address(handler); - - if self.config.create_increase_nonce { - handler.inc_nonce(address)?; - } - let context = Context { - caller, - address, - apparent_value: value, - }; - - let transaction_cost = - TransactionCost::create(&init_code, &access_list).cost(self.config); - - let machine = Machine::new( - Rc::new(init_code), - Rc::new(Vec::new()), - self.config.stack_limit, - self.config.memory_limit, - RuntimeState { + let mut machine = try_or_oog!(routines::make_enter_create_machine( + self, + caller, + address, + init_code, + gas_limit, + false, + transfer, context, - transaction_context: TransactionContext { - origin: caller, - gas_price, - } - .into(), - retbuf: Vec::new(), - gas: U256::zero(), - }, - ); - let mut gasometer = Gasometer::new(gas_limit, &machine, self.config); - - gasometer.record_cost(transaction_cost)?; - - let transfer = Transfer { - source: caller, - target: address, - value, - }; - handler.transfer(transfer)?; - - let machine = GasedMachine { - machine, - gasometer, - is_static: false, - }; - - let (mut machine, result) = - crate::execute(machine, handler, 0, DEFAULT_HEAP_DEPTH, self, etable); - result?; - - let retbuf = machine.machine.into_retbuf(); - check_first_byte(self.config, &retbuf[..])?; - - if let Some(limit) = self.config.create_contract_limit { - if retbuf.len() > limit { - return Err(ExitException::CreateContractLimit.into()); - } - } - - GasometerT::::record_codedeposit( - &mut machine.gasometer, - retbuf.len(), - )?; - - let refunded_gas = U256::from(machine.gasometer.gas()); - let refunded_fee = refunded_gas * gas_price; - let coinbase_reward = gas_fee - refunded_fee; - - handler.deposit(caller, refunded_fee); - handler.deposit(handler.block_coinbase(), coinbase_reward); + Rc::new(transaction_context), + handler, + )); + try_or_oog!(machine.gasometer.record_cost(transaction_cost)); - Ok(address) - }; + let (mut machine, result) = + crate::execute(machine, handler, 0, DEFAULT_HEAP_DEPTH, self, etable); + let retbuf = machine.machine.into_retbuf(); + let address = try_or_oog!(routines::deploy_create_code( + self, + result.map(|_| address), + &retbuf, + &mut machine.gasometer, + handler + )); - match work() { - Ok(exit) => { - handler.pop_substate(TransactionalMergeStrategy::Commit); - Ok(exit) - } - Err(err) => { - handler.pop_substate(TransactionalMergeStrategy::Discard); - Err(err) - } - } + let refunded_gas = U256::from(machine.gasometer.gas()); + (Ok(address), refunded_gas) + }, + ) } } @@ -461,13 +381,13 @@ where is_static, transaction_context, trap, - } => enter_create_trap_stack( + } => routines::enter_create_trap_stack( + self, + trap, gas_limit, is_static, transaction_context, - trap, handler, - self.config, ), CallCreateTrapPrepareData::Call { gas_limit, @@ -496,12 +416,12 @@ where match trap_data { CallCreateTrapEnterData::Create { address, trap } => { let retbuf = child.machine.into_retbuf(); - let result = exit_create_trap_stack_no_exit_substate( + let result = routines::deploy_create_code( + self, result.map(|_| address), &retbuf, &mut child.gasometer, handler, - self.config, ); match &result { @@ -562,129 +482,3 @@ where } } } - -fn enter_create_trap_stack<'config, H>( - gas_limit: u64, - is_static: bool, - transaction_context: Rc, - trap_data: CreateTrapData, - handler: &mut H, - config: &'config Config, -) -> Result<(CallCreateTrapEnterData, GasedMachine<'config>), ExitError> -where - H: RuntimeEnvironment + RuntimeBackend + TransactionalBackend, -{ - handler.push_substate(); - - let work = || -> Result<(CallCreateTrapEnterData, GasedMachine<'config>), ExitError> { - let CreateTrapData { - scheme, - value, - code, - } = trap_data.clone(); - - let caller = scheme.caller(); - let address = scheme.address(handler); - - handler.mark_hot(caller, None)?; - handler.mark_hot(address, None)?; - - if handler.balance(caller) < value { - return Err(ExitException::OutOfFund.into()); - } - - handler.inc_nonce(caller)?; - - if handler.code_size(address) != U256::zero() || handler.nonce(address) > U256::zero() { - return Err(ExitException::CreateCollision.into()); - } - - handler.reset_storage(address); - - let context = Context { - address, - caller, - apparent_value: value, - }; - - let transfer = Transfer { - source: caller, - target: address, - value, - }; - - handler.transfer(transfer)?; - - if config.create_increase_nonce { - handler.inc_nonce(address)?; - } - - let machine = Machine::new( - Rc::new(code), - Rc::new(Vec::new()), - config.stack_limit, - config.memory_limit, - RuntimeState { - context, - transaction_context, - retbuf: Vec::new(), - gas: U256::zero(), - }, - ); - - let gasometer = Gasometer::new(gas_limit, &machine, config); - - Ok(( - CallCreateTrapEnterData::Create { - address, - trap: trap_data, - }, - GasedMachine { - machine, - gasometer, - is_static, - }, - )) - }; - - match work() { - Ok(machine) => Ok(machine), - Err(err) => { - handler.pop_substate(TransactionalMergeStrategy::Discard); - Err(err) - } - } -} - -fn check_first_byte(config: &Config, code: &[u8]) -> Result<(), ExitError> { - if config.disallow_executable_format && Some(&Opcode::EOFMAGIC.as_u8()) == code.first() { - return Err(ExitException::InvalidOpcode(Opcode::EOFMAGIC).into()); - } - Ok(()) -} - -fn exit_create_trap_stack_no_exit_substate<'config, H>( - result: Result, - retbuf: &Vec, - gasometer: &mut Gasometer<'config>, - handler: &mut H, - config: &'config Config, -) -> Result -where - H: RuntimeEnvironment + RuntimeBackend + TransactionalBackend, -{ - let address = result?; - check_first_byte(config, &retbuf[..])?; - - if let Some(limit) = config.create_contract_limit { - if retbuf.len() > limit { - return Err(ExitException::CreateContractLimit.into()); - } - } - - GasometerT::::record_codedeposit(gasometer, retbuf.len())?; - - handler.set_code(address, retbuf.clone()); - - Ok(address) -} diff --git a/src/standard/invoker/routines.rs b/src/standard/invoker/routines.rs index 58eec04ad..1d7ae9233 100644 --- a/src/standard/invoker/routines.rs +++ b/src/standard/invoker/routines.rs @@ -1,8 +1,9 @@ -use super::{CallCreateTrapEnterData, CallTrapData, Invoker}; -use crate::standard::{GasedMachine, Gasometer, Machine}; +use super::{CallCreateTrapEnterData, CallTrapData, CreateTrapData, Invoker}; +use crate::standard::{Config, GasedMachine, Gasometer, Machine}; use crate::{ - Context, ExitError, RuntimeBackend, RuntimeEnvironment, RuntimeState, TransactionContext, - TransactionalBackend, TransactionalMergeStrategy, Transfer, + Context, ExitError, ExitException, Gasometer as GasometerT, Opcode, RuntimeBackend, + RuntimeEnvironment, RuntimeState, TransactionContext, TransactionalBackend, + TransactionalMergeStrategy, Transfer, }; use alloc::rc::Rc; use primitive_types::{H160, U256}; @@ -103,6 +104,64 @@ where }) } +pub fn make_enter_create_machine<'config, H>( + invoker: &Invoker<'config>, + caller: H160, + target: H160, + init_code: Vec, + gas_limit: u64, + is_static: bool, + transfer: Transfer, + context: Context, + transaction_context: Rc, + handler: &mut H, +) -> Result, ExitError> +where + H: RuntimeEnvironment + RuntimeBackend + TransactionalBackend, +{ + if let Some(limit) = invoker.config.max_initcode_size { + if init_code.len() > limit { + return Err(ExitException::CreateContractLimit.into()); + } + } + + handler.mark_hot(caller, None)?; + handler.mark_hot(target, None)?; + + handler.transfer(transfer)?; + + if handler.code_size(target) != U256::zero() || handler.nonce(target) > U256::zero() { + return Err(ExitException::CreateCollision.into()); + } + handler.inc_nonce(caller)?; + if invoker.config.create_increase_nonce { + handler.inc_nonce(target)?; + } + + handler.reset_storage(target); + + let machine = Machine::new( + Rc::new(init_code), + Rc::new(Vec::new()), + invoker.config.stack_limit, + invoker.config.memory_limit, + RuntimeState { + context: context.clone(), + transaction_context, + retbuf: Vec::new(), + gas: U256::zero(), + }, + ); + + let gasometer = Gasometer::new(gas_limit, &machine, invoker.config); + + Ok(GasedMachine { + machine, + gasometer, + is_static, + }) +} + pub fn enter_call_trap_stack<'config, H>( invoker: &Invoker<'config>, trap_data: CallTrapData, @@ -141,3 +200,102 @@ where } } } + +pub fn enter_create_trap_stack<'config, H>( + invoker: &Invoker<'config>, + trap_data: CreateTrapData, + gas_limit: u64, + is_static: bool, + transaction_context: Rc, + handler: &mut H, +) -> Result<(CallCreateTrapEnterData, GasedMachine<'config>), ExitError> +where + H: RuntimeEnvironment + RuntimeBackend + TransactionalBackend, +{ + handler.push_substate(); + + let work = || -> Result<(CallCreateTrapEnterData, GasedMachine<'config>), ExitError> { + let CreateTrapData { + scheme, + value, + code, + } = trap_data.clone(); + + let caller = scheme.caller(); + let address = scheme.address(handler); + + let context = Context { + address, + caller, + apparent_value: value, + }; + + let transfer = Transfer { + source: caller, + target: address, + value, + }; + + let machine = make_enter_create_machine( + invoker, + caller, + address, + code, + gas_limit, + is_static, + transfer, + context, + transaction_context, + handler, + )?; + + Ok(( + CallCreateTrapEnterData::Create { + address, + trap: trap_data, + }, + machine, + )) + }; + + match work() { + Ok(machine) => Ok(machine), + Err(err) => { + handler.pop_substate(TransactionalMergeStrategy::Discard); + Err(err) + } + } +} + +fn check_first_byte(config: &Config, code: &[u8]) -> Result<(), ExitError> { + if config.disallow_executable_format && Some(&Opcode::EOFMAGIC.as_u8()) == code.first() { + return Err(ExitException::InvalidOpcode(Opcode::EOFMAGIC).into()); + } + Ok(()) +} + +pub fn deploy_create_code<'config, H>( + invoker: &Invoker<'config>, + result: Result, + retbuf: &Vec, + gasometer: &mut Gasometer<'config>, + handler: &mut H, +) -> Result +where + H: RuntimeEnvironment + RuntimeBackend + TransactionalBackend, +{ + let address = result?; + check_first_byte(invoker.config, &retbuf[..])?; + + if let Some(limit) = invoker.config.create_contract_limit { + if retbuf.len() > limit { + return Err(ExitException::CreateContractLimit.into()); + } + } + + GasometerT::::record_codedeposit(gasometer, retbuf.len())?; + + handler.set_code(address, retbuf.clone()); + + Ok(address) +}