From 168de4ab67f71003e6997ee44fa1e5aa3c9b24b5 Mon Sep 17 00:00:00 2001 From: Lohann Paterno Coutinho Ferreira Date: Sun, 19 Nov 2023 23:29:55 -0300 Subject: [PATCH 1/8] Fix issue-222 --- gasometer/src/lib.rs | 8 ++++++++ src/executor/stack/executor.rs | 22 +++++++++++++++------- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/gasometer/src/lib.rs b/gasometer/src/lib.rs index 8e4e6a143..cc76e8f23 100644 --- a/gasometer/src/lib.rs +++ b/gasometer/src/lib.rs @@ -172,6 +172,14 @@ impl<'config> Gasometer<'config> { Ok(()) } + #[inline] + /// Check gas limit before `CREATE` code deposit. (See EIP-2) + pub fn can_deposit(&self, len: usize) -> bool { + let cost = len as u64 * consts::G_CODEDEPOSIT; + let all_gas_cost = self.total_used_gas() + cost; + self.gas_limit >= all_gas_cost + } + #[inline] /// Record `CREATE` code deposit. pub fn record_deposit(&mut self, len: usize) -> Result<(), ExitError> { diff --git a/src/executor/stack/executor.rs b/src/executor/stack/executor.rs index e35bae13b..9e8375854 100644 --- a/src/executor/stack/executor.rs +++ b/src/executor/stack/executor.rs @@ -985,7 +985,7 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> match reason { ExitReason::Succeed(s) => { - let out = return_data; + let mut out = return_data; let address = created_address; // As of EIP-3541 code starting with 0xef cannot be deployed if let Err(e) = check_first_byte(self.config, &out) { @@ -1002,12 +1002,20 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> } } - match self - .state - .metadata_mut() - .gasometer - .record_deposit(out.len()) - { + // Before EIP-2 is possible to create empty contract + let result = if self.config.empty_considered_exists { + if self.state.metadata().gasometer.can_deposit(out.len()) { + out.clear(); + } + Ok(()) + } else { + self.state + .metadata_mut() + .gasometer + .record_deposit(out.len()) + }; + + match result { Ok(()) => { let exit_result = self.exit_substate(StackExitKind::Succeeded); if let Err(e) = self.record_external_operation( From f020894485fdabc65a4d8c00e60072af6f3d5165 Mon Sep 17 00:00:00 2001 From: Lohann Paterno Coutinho Ferreira Date: Mon, 20 Nov 2023 02:46:15 -0300 Subject: [PATCH 2/8] cargo fmt --- src/executor/stack/executor.rs | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/src/executor/stack/executor.rs b/src/executor/stack/executor.rs index 9e8375854..8fedb94e3 100644 --- a/src/executor/stack/executor.rs +++ b/src/executor/stack/executor.rs @@ -985,7 +985,7 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> match reason { ExitReason::Succeed(s) => { - let mut out = return_data; + let out = return_data; let address = created_address; // As of EIP-3541 code starting with 0xef cannot be deployed if let Err(e) = check_first_byte(self.config, &out) { @@ -1003,27 +1003,34 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> } // Before EIP-2 is possible to create empty contract - let result = if self.config.empty_considered_exists { - if self.state.metadata().gasometer.can_deposit(out.len()) { - out.clear(); - } - Ok(()) + let bytecode = if self.config.empty_considered_exists + && !self.state.metadata().gasometer.can_deposit(out.len()) + { + Ok(None) } else { self.state .metadata_mut() .gasometer .record_deposit(out.len()) + .map(|_| Some(out)) }; - match result { - Ok(()) => { + match bytecode { + Ok(Some(bytecode)) => { let exit_result = self.exit_substate(StackExitKind::Succeeded); if let Err(e) = self.record_external_operation( - crate::ExternalOperation::Write(U256::from(out.len())), + crate::ExternalOperation::Write(U256::from(bytecode.len())), ) { return (e.into(), None, Vec::new()); } - self.state.set_code(address, out); + self.state.set_code(address, bytecode); + if let Err(e) = exit_result { + return (e.into(), None, Vec::new()); + } + (ExitReason::Succeed(s), Some(address), Vec::new()) + } + Ok(None) => { + let exit_result = self.exit_substate(StackExitKind::Succeeded); if let Err(e) = exit_result { return (e.into(), None, Vec::new()); } From 5232998479cd841c2d3969cdcd286879c4d3312f Mon Sep 17 00:00:00 2001 From: Lohann Paterno Coutinho Ferreira Date: Fri, 1 Dec 2023 10:14:17 -0300 Subject: [PATCH 3/8] Add config for homestead and tangerine whistle --- runtime/src/lib.rs | 37 ++++++++++++++++++++++++++++++++++ src/executor/stack/executor.rs | 3 ++- 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 8809e58f3..a6b667a36 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -252,6 +252,8 @@ pub struct Config { pub call_l64_after_gas: bool, /// Whether empty account is considered exists. pub empty_considered_exists: bool, + /// Allow create empty contract if the user don't have gas for paying code deposit. + pub can_create_empty_contract: bool, /// Whether create transactions and create opcode increases nonce by one. pub create_increase_nonce: bool, /// Stack limit. @@ -323,6 +325,7 @@ impl Config { warm_coinbase_address: false, err_on_call_with_more_gas: true, empty_considered_exists: true, + can_create_empty_contract: true, create_increase_nonce: false, call_l64_after_gas: false, stack_limit: 1024, @@ -345,6 +348,38 @@ impl Config { } } + /// Homestead hard fork configuration. + pub const fn homestead() -> Config { + let mut config = Self::frontier(); + // makes edits to contract creation process + // see [EIP-2](https://eips.ethereum.org/EIPS/eip-2) + config.gas_transaction_create = 53_000; + config.can_create_empty_contract = false; + config.empty_considered_exists = true; + + // Enable delegate call opcode + // see [EIP-7](https://eips.ethereum.org/EIPS/eip-7) + config.has_delegate_call = true; + config + } + + /// Tangerine whistle hard fork configuration. + pub const fn tangerine_whistle() -> Config { + let mut config = Self::homestead(); + // Gas cost changes for IO-heavy operations + // see [EIP-150](https://eips.ethereum.org/EIPS/eip-150) + config.gas_ext_code = 700; + config.gas_ext_code_hash = 700; + config.gas_balance = 400; + config.gas_sload = 200; + config.gas_call = 700; + config.gas_ext_code = 700; + config.gas_suicide = 5_000; + config.gas_suicide_new_account = 30_000; + config.err_on_call_with_more_gas = false; + config + } + /// Istanbul hard fork configuration. pub const fn istanbul() -> Config { Config { @@ -377,6 +412,7 @@ impl Config { warm_coinbase_address: false, err_on_call_with_more_gas: false, empty_considered_exists: false, + can_create_empty_contract: false, create_increase_nonce: true, call_l64_after_gas: true, stack_limit: 1024, @@ -474,6 +510,7 @@ impl Config { warm_coinbase_address, err_on_call_with_more_gas: false, empty_considered_exists: false, + can_create_empty_contract: false, create_increase_nonce: true, call_l64_after_gas: true, stack_limit: 1024, diff --git a/src/executor/stack/executor.rs b/src/executor/stack/executor.rs index 8fedb94e3..ae8b1083b 100644 --- a/src/executor/stack/executor.rs +++ b/src/executor/stack/executor.rs @@ -1003,7 +1003,7 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> } // Before EIP-2 is possible to create empty contract - let bytecode = if self.config.empty_considered_exists + let bytecode = if self.config.can_create_empty_contract && !self.state.metadata().gasometer.can_deposit(out.len()) { Ok(None) @@ -1335,6 +1335,7 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> Handler stack: &Stack, ) -> Result<(), ExitError> { // log::trace!(target: "evm", "Running opcode: {:?}, Pre gas-left: {:?}", opcode, gasometer.gas()); + // log::trace!(target: "evm", "Running opcode: {:?}, Pre gas-left: {:?}", opcode, self.state.metadata().gasometer.gas()); if let Some(cost) = gasometer::static_opcode_cost(opcode) { self.state.metadata_mut().gasometer.record_cost(cost)?; From f56d078e24d717b9e36f76c8244c3766a75137ec Mon Sep 17 00:00:00 2001 From: Lohann Paterno Coutinho Ferreira Date: Fri, 1 Dec 2023 10:22:37 -0300 Subject: [PATCH 4/8] Update config documentation --- runtime/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index a6b667a36..a1e722f56 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -252,7 +252,7 @@ pub struct Config { pub call_l64_after_gas: bool, /// Whether empty account is considered exists. pub empty_considered_exists: bool, - /// Allow create empty contract if the user don't have gas for paying code deposit. + /// Create empty contract if there's no gas for paying code deposit. pub can_create_empty_contract: bool, /// Whether create transactions and create opcode increases nonce by one. pub create_increase_nonce: bool, From 3c8476e27220144a8c6ecbf44ca100cbc9c33155 Mon Sep 17 00:00:00 2001 From: Lohann Paterno Coutinho Ferreira Date: Fri, 1 Dec 2023 19:24:09 -0300 Subject: [PATCH 5/8] Enable call_l64_after_gas for tangerine whistle --- runtime/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index a1e722f56..93d15761c 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -377,6 +377,7 @@ impl Config { config.gas_suicide = 5_000; config.gas_suicide_new_account = 30_000; config.err_on_call_with_more_gas = false; + config.call_l64_after_gas = true; config } From 4676082485a99828c11c51ac8ab0570a99e44afd Mon Sep 17 00:00:00 2001 From: Lohann Paterno Coutinho Ferreira Date: Fri, 1 Dec 2023 19:40:55 -0300 Subject: [PATCH 6/8] Fix suicide new account gas cost --- runtime/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 93d15761c..bca89f523 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -375,7 +375,7 @@ impl Config { config.gas_call = 700; config.gas_ext_code = 700; config.gas_suicide = 5_000; - config.gas_suicide_new_account = 30_000; + config.gas_suicide_new_account = 25_000; config.err_on_call_with_more_gas = false; config.call_l64_after_gas = true; config From 2fe14a3cbfe7df0e0dc7cca430f0cf131133fa2e Mon Sep 17 00:00:00 2001 From: Lohann Paterno Coutinho Ferreira Date: Sat, 2 Dec 2023 10:57:14 -0300 Subject: [PATCH 7/8] Add byzantium hardfork config --- runtime/src/lib.rs | 48 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index bca89f523..2aa55b3d1 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -349,6 +349,7 @@ impl Config { } /// Homestead hard fork configuration. + /// mainnet block number: 1,150,000 pub const fn homestead() -> Config { let mut config = Self::frontier(); // makes edits to contract creation process @@ -381,6 +382,53 @@ impl Config { config } + /// Spurious Dragon hard fork configuration. + pub const fn spurious_dragon() -> Config { + let mut config = Self::tangerine_whistle(); + // Simple replay attack protection + // see [EIP-155](https://eips.ethereum.org/EIPS/eip-155) + config.has_chain_id = true; + + // EXP cost increase + // see [EIP-160](https://eips.ethereum.org/EIPS/eip-160) + config.gas_expbyte = 50; + + // State trie clearing + // - An account is considered empty when it has no code and zero nonce and zero balance. It should be noted + // that very few state changes can ultimately result in accounts that are empty following the execution of the transaction: + // - an empty account has zero value transferred to it through CALL; + // - an empty account has zero value transferred to it through SUICIDE; + // - an empty account has zero value transferred to it through a message-call transaction; + // - an empty account has zero value transferred to it through a zero-gas-price fees transfer. + // - empty account deletions are reverted when the state is reverted. + // - An account is considered dead when either it is non-existent or it is empty. + // see [EIP-161](https://eips.ethereum.org/EIPS/eip-161) + config.create_increase_nonce = true; + config.empty_considered_exists = false; + + // Contract code size limit + // see [EIP-170](https://eips.ethereum.org/EIPS/eip-170) + config.create_contract_limit = Some(0x6000); + config + } + + /// Byzantium hard fork configuration. + pub const fn byzantium() -> Config { + let mut config = Self::spurious_dragon(); + + // adds REVERT opcode + // see [EIP-140](https://eips.ethereum.org/EIPS/eip-140) + config.has_revert = true; + + // adds support for variable length return values + // see [EIP-211](https://eips.ethereum.org/EIPS/eip-211) + config.has_return_data = true; + + // TODO: enable STATICCALL opcode + // Ref: https://github.com/rust-ethereum/evm/issues/248 + config + } + /// Istanbul hard fork configuration. pub const fn istanbul() -> Config { Config { From 55ba5d51ac38e7bc85b3a0ae0ed1c2c9efab2430 Mon Sep 17 00:00:00 2001 From: Lohann Paterno Coutinho Ferreira Date: Sat, 2 Dec 2023 14:42:29 -0300 Subject: [PATCH 8/8] Support optional staticcall opcode --- runtime/src/lib.rs | 35 +++++++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 2aa55b3d1..6bb18e836 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -280,6 +280,8 @@ pub struct Config { pub has_bitwise_shifting: bool, /// Has chain ID. pub has_chain_id: bool, + /// Has static_call. See [EIP-214](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-214.md) + pub has_static_call: bool, /// Has self balance. pub has_self_balance: bool, /// Has ext code hash. @@ -340,6 +342,7 @@ impl Config { has_return_data: false, has_bitwise_shifting: false, has_chain_id: false, + has_static_call: false, has_self_balance: false, has_ext_code_hash: false, has_base_fee: false, @@ -370,11 +373,9 @@ impl Config { // Gas cost changes for IO-heavy operations // see [EIP-150](https://eips.ethereum.org/EIPS/eip-150) config.gas_ext_code = 700; - config.gas_ext_code_hash = 700; config.gas_balance = 400; config.gas_sload = 200; config.gas_call = 700; - config.gas_ext_code = 700; config.gas_suicide = 5_000; config.gas_suicide_new_account = 25_000; config.err_on_call_with_more_gas = false; @@ -385,10 +386,6 @@ impl Config { /// Spurious Dragon hard fork configuration. pub const fn spurious_dragon() -> Config { let mut config = Self::tangerine_whistle(); - // Simple replay attack protection - // see [EIP-155](https://eips.ethereum.org/EIPS/eip-155) - config.has_chain_id = true; - // EXP cost increase // see [EIP-160](https://eips.ethereum.org/EIPS/eip-160) config.gas_expbyte = 50; @@ -424,8 +421,28 @@ impl Config { // see [EIP-211](https://eips.ethereum.org/EIPS/eip-211) config.has_return_data = true; - // TODO: enable STATICCALL opcode - // Ref: https://github.com/rust-ethereum/evm/issues/248 + // adds STATICCALL opcode, allowing non-state-changing calls to other contracts. + // see [EIP-214](https://eips.ethereum.org/EIPS/eip-214) + config.has_static_call = true; + config + } + + /// Constantinople hard fork configuration. + pub const fn constantinople() -> Config { + let mut config = Self::byzantium(); + + // adds Bitwise shifting instructions + // see [EIP-145](https://eips.ethereum.org/EIPS/eip-145) + config.has_bitwise_shifting = true; + + // allows you to interact with addresses that have yet to be created. + // see [EIP-1014](https://eips.ethereum.org/EIPS/eip-1014) + config.has_create2 = true; + + // adds EXTCODEHASH opcode + // see [EIP-1052](https://eips.ethereum.org/EIPS/eip-1052) + config.has_ext_code_hash = true; + config.gas_ext_code_hash = 400; config } @@ -476,6 +493,7 @@ impl Config { has_return_data: true, has_bitwise_shifting: true, has_chain_id: true, + has_static_call: true, has_self_balance: true, has_ext_code_hash: true, has_base_fee: false, @@ -574,6 +592,7 @@ impl Config { has_return_data: true, has_bitwise_shifting: true, has_chain_id: true, + has_static_call: true, has_self_balance: true, has_ext_code_hash: true, has_base_fee,