diff --git a/Cargo.lock b/Cargo.lock index bd51a3c319..1f0f283deb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2161,7 +2161,7 @@ dependencies = [ [[package]] name = "evm" version = "0.41.2" -source = "git+https://github.com/moonbeam-foundation/evm?branch=moonbeam-polkadot-stable2409#aeff7f361687b4c6a7fcbe1cf6e4fe5f2aea32b5" +source = "git+https://github.com/moonbeam-foundation/evm?branch=moonbeam-polkadot-stable2409#092e1f5a138a2f52cd5d0f009ff1c5ebea1cdc21" dependencies = [ "auto_impl", "environmental", @@ -2181,7 +2181,7 @@ dependencies = [ [[package]] name = "evm-core" version = "0.41.0" -source = "git+https://github.com/moonbeam-foundation/evm?branch=moonbeam-polkadot-stable2409#aeff7f361687b4c6a7fcbe1cf6e4fe5f2aea32b5" +source = "git+https://github.com/moonbeam-foundation/evm?branch=moonbeam-polkadot-stable2409#092e1f5a138a2f52cd5d0f009ff1c5ebea1cdc21" dependencies = [ "parity-scale-codec", "primitive-types", @@ -2192,7 +2192,7 @@ dependencies = [ [[package]] name = "evm-gasometer" version = "0.41.0" -source = "git+https://github.com/moonbeam-foundation/evm?branch=moonbeam-polkadot-stable2409#aeff7f361687b4c6a7fcbe1cf6e4fe5f2aea32b5" +source = "git+https://github.com/moonbeam-foundation/evm?branch=moonbeam-polkadot-stable2409#092e1f5a138a2f52cd5d0f009ff1c5ebea1cdc21" dependencies = [ "environmental", "evm-core", @@ -2203,7 +2203,7 @@ dependencies = [ [[package]] name = "evm-runtime" version = "0.41.0" -source = "git+https://github.com/moonbeam-foundation/evm?branch=moonbeam-polkadot-stable2409#aeff7f361687b4c6a7fcbe1cf6e4fe5f2aea32b5" +source = "git+https://github.com/moonbeam-foundation/evm?branch=moonbeam-polkadot-stable2409#092e1f5a138a2f52cd5d0f009ff1c5ebea1cdc21" dependencies = [ "auto_impl", "environmental", diff --git a/frame/evm/precompile/dispatch/src/mock.rs b/frame/evm/precompile/dispatch/src/mock.rs index 98d21c8978..8234b07c07 100644 --- a/frame/evm/precompile/dispatch/src/mock.rs +++ b/frame/evm/precompile/dispatch/src/mock.rs @@ -217,6 +217,10 @@ impl PrecompileHandle for MockHandle { &self.context } + fn origin(&self) -> H160 { + unimplemented!() + } + fn is_static(&self) -> bool { unimplemented!() } @@ -224,4 +228,8 @@ impl PrecompileHandle for MockHandle { fn gas_limit(&self) -> Option { None } + + fn is_contract_being_constructed(&self, _address: H160) -> bool { + unimplemented!() + } } diff --git a/frame/evm/src/lib.rs b/frame/evm/src/lib.rs index c1ec9a3a7b..77563709fd 100644 --- a/frame/evm/src/lib.rs +++ b/frame/evm/src/lib.rs @@ -701,7 +701,6 @@ pub mod pallet { .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(account_basic_weight) .saturating_add(min_gas_weight); - } total_weight diff --git a/frame/evm/test-vector-support/src/lib.rs b/frame/evm/test-vector-support/src/lib.rs index cd4252c9db..aa4048044c 100644 --- a/frame/evm/test-vector-support/src/lib.rs +++ b/frame/evm/test-vector-support/src/lib.rs @@ -111,6 +111,10 @@ impl PrecompileHandle for MockHandle { &self.context } + fn origin(&self) -> H160 { + unimplemented!() + } + fn is_static(&self) -> bool { self.is_static } @@ -118,6 +122,10 @@ impl PrecompileHandle for MockHandle { fn gas_limit(&self) -> Option { self.gas_limit } + + fn is_contract_being_constructed(&self, _address: H160) -> bool { + unimplemented!() + } } /// Tests a precompile against the ethereum consensus tests defined in the given file at filepath. diff --git a/precompiles/src/evm/handle.rs b/precompiles/src/evm/handle.rs index 7cc73bc65c..2d37d37dff 100644 --- a/precompiles/src/evm/handle.rs +++ b/precompiles/src/evm/handle.rs @@ -177,6 +177,10 @@ mod tests { unimplemented!() } + fn origin(&self) -> sp_core::H160 { + unimplemented!() + } + fn is_static(&self) -> bool { true } @@ -195,6 +199,10 @@ mod tests { } fn refund_external_cost(&mut self, _ref_time: Option, _proof_size: Option) {} + + fn is_contract_being_constructed(&self, _address: sp_core::H160) -> bool { + unimplemented!() + } } #[test] diff --git a/precompiles/src/precompile_set.rs b/precompiles/src/precompile_set.rs index 2c20d78cd8..0f4889f295 100644 --- a/precompiles/src/precompile_set.rs +++ b/precompiles/src/precompile_set.rs @@ -29,7 +29,7 @@ use alloc::{collections::btree_map::BTreeMap, vec, vec::Vec}; use core::{cell::RefCell, marker::PhantomData, ops::RangeInclusive}; use fp_evm::{ ExitError, IsPrecompileResult, Precompile, PrecompileFailure, PrecompileHandle, - PrecompileResult, PrecompileSet, + PrecompileResult, PrecompileSet, ACCOUNT_CODES_METADATA_PROOF_SIZE, }; use frame_support::pallet_prelude::Get; use impl_trait_for_tuples::impl_for_tuples; @@ -304,13 +304,11 @@ impl PrecompileChecks for CallableByPrecompile { #[derive(PartialEq)] #[cfg_attr(feature = "std", derive(Debug))] pub enum AddressType { - /// The code stored at the address is less than 5 bytes, but not well known. - Unknown, /// No code is stored at the address, therefore is EOA. EOA, /// The 5-byte magic constant for a precompile is stored at the address. Precompile, - /// The code is greater than 5-bytes, potentially a Smart Contract. + /// Every address that is not a EOA or a Precompile is potentially a Smart Contract. Contract, } @@ -319,39 +317,27 @@ pub fn get_address_type( handle: &mut impl PrecompileHandle, address: H160, ) -> Result { + // Check if address is a precompile + if let Ok(true) = is_precompile_or_fail::(address, handle.remaining_gas()) { + return Ok(AddressType::Precompile); + } + + // Contracts under-construction don't have code yet + if handle.is_contract_being_constructed(address) { + return Ok(AddressType::Contract); + } + // AccountCodesMetadata: // Blake2128(16) + H160(20) + CodeMetadata(40) - handle.record_db_read::(76)?; + handle.record_db_read::(ACCOUNT_CODES_METADATA_PROOF_SIZE as usize)?; let code_len = pallet_evm::Pallet::::account_code_metadata(address).size; - // 0 => either EOA or precompile without dummy code + // Having no code at this point means that the address is an EOA if code_len == 0 { return Ok(AddressType::EOA); } - // dummy code is 5 bytes long, so any other len means it is a contract. - if code_len != 5 { - return Ok(AddressType::Contract); - } - - // check code matches dummy code - handle.record_db_read::(code_len as usize)?; - let code = pallet_evm::AccountCodes::::get(address); - if code == [0x60, 0x00, 0x60, 0x00, 0xfd] { - return Ok(AddressType::Precompile); - } - - Ok(AddressType::Unknown) -} - -fn is_address_eoa_or_precompile( - handle: &mut impl PrecompileHandle, - address: H160, -) -> Result { - match get_address_type::(handle, address)? { - AddressType::EOA | AddressType::Precompile => Ok(true), - _ => Ok(false), - } + Ok(AddressType::Contract) } /// Common checks for precompile and precompile sets. @@ -375,17 +361,27 @@ fn common_checks( u32::from_be_bytes(buffer) }); - // Is this selector callable from a smart contract? - let callable_by_smart_contract = - C::callable_by_smart_contract(caller, selector).unwrap_or(false); - if !callable_by_smart_contract && !is_address_eoa_or_precompile::(handle, caller)? { - return Err(revert("Function not callable by smart contracts")); - } - - // Is this selector callable from a precompile? - let callable_by_precompile = C::callable_by_precompile(caller, selector).unwrap_or(false); - if !callable_by_precompile && is_precompile_or_fail::(caller, handle.remaining_gas())? { - return Err(revert("Function not callable by precompiles")); + let caller_address_type = get_address_type::(handle, caller)?; + match caller_address_type { + AddressType::Precompile => { + // Is this selector callable from a precompile? + let callable_by_precompile = + C::callable_by_precompile(caller, selector).unwrap_or(false); + if !callable_by_precompile { + return Err(revert("Function not callable by precompiles")); + } + } + AddressType::Contract => { + // Is this selector callable from a smart contract? + let callable_by_smart_contract = + C::callable_by_smart_contract(caller, selector).unwrap_or(false); + if !callable_by_smart_contract { + return Err(revert("Function not callable by smart contracts")); + } + } + AddressType::EOA => { + // No check required for EOA + } } Ok(()) @@ -463,6 +459,10 @@ impl<'a, H: PrecompileHandle> PrecompileHandle for RestrictiveHandle<'a, H> { self.handle.context() } + fn origin(&self) -> H160 { + self.handle.origin() + } + fn is_static(&self) -> bool { self.handle.is_static() } @@ -484,6 +484,10 @@ impl<'a, H: PrecompileHandle> PrecompileHandle for RestrictiveHandle<'a, H> { fn refund_external_cost(&mut self, ref_time: Option, proof_size: Option) { self.handle.refund_external_cost(ref_time, proof_size) } + + fn is_contract_being_constructed(&self, address: H160) -> bool { + self.handle.is_contract_being_constructed(address) + } } /// Allows to know if a precompile is active or not. diff --git a/precompiles/src/testing/handle.rs b/precompiles/src/testing/handle.rs index 0228bcca43..91150e924b 100644 --- a/precompiles/src/testing/handle.rs +++ b/precompiles/src/testing/handle.rs @@ -22,6 +22,8 @@ use evm::{ExitRevert, ExitSucceed}; use fp_evm::{Context, ExitError, ExitReason, Log, PrecompileHandle, Transfer}; use sp_core::{H160, H256}; +use super::Alice; + #[derive(Debug, Clone)] pub struct Subcall { pub address: H160, @@ -213,4 +215,12 @@ impl PrecompileHandle for MockHandle { } fn refund_external_cost(&mut self, _ref_time: Option, _proof_size: Option) {} + + fn origin(&self) -> H160 { + Alice.into() + } + + fn is_contract_being_constructed(&self, _address: H160) -> bool { + false + } } diff --git a/precompiles/tests-external/lib.rs b/precompiles/tests-external/lib.rs index fd5a18e4a5..5e661a3b83 100644 --- a/precompiles/tests-external/lib.rs +++ b/precompiles/tests-external/lib.rs @@ -34,7 +34,7 @@ use sp_runtime::{ }; // Frontier use fp_evm::{ExitReason, ExitRevert, PrecompileFailure, PrecompileHandle}; -use pallet_evm::{EnsureAddressNever, EnsureAddressRoot}; +use pallet_evm::{CodeMetadata, EnsureAddressNever, EnsureAddressRoot}; use precompile_utils::{ precompile_set::*, solidity::{codec::Writer, revert::revert}, @@ -146,7 +146,16 @@ impl MockPrecompile { } } -struct MockPrecompileHandle; +#[derive(Default)] +struct MockPrecompileHandle { + contracts_being_constructed: Vec, +} +impl MockPrecompileHandle { + fn with_contracts_being_constructed(mut self, contracts_being_constructed: Vec) -> Self { + self.contracts_being_constructed = contracts_being_constructed; + self + } +} impl PrecompileHandle for MockPrecompileHandle { fn call( &mut self, @@ -176,7 +185,7 @@ impl PrecompileHandle for MockPrecompileHandle { fn refund_external_cost(&mut self, _ref_time: Option, _proof_size: Option) {} fn remaining_gas(&self) -> u64 { - unimplemented!() + 0 } fn log(&mut self, _: H160, _: Vec, _: Vec) -> Result<(), evm::ExitError> { @@ -195,6 +204,10 @@ impl PrecompileHandle for MockPrecompileHandle { unimplemented!() } + fn origin(&self) -> H160 { + Alice.into() + } + fn is_static(&self) -> bool { true } @@ -202,6 +215,10 @@ impl PrecompileHandle for MockPrecompileHandle { fn gas_limit(&self) -> Option { unimplemented!() } + + fn is_contract_being_constructed(&self, address: H160) -> bool { + self.contracts_being_constructed.contains(&address) + } } pub type Precompiles = PrecompileSetBuilder< @@ -385,10 +402,12 @@ fn subcalls_works_when_allowed() { #[test] fn get_address_type_works_for_eoa() { ExtBuilder::default().build().execute_with(|| { - let addr = H160::repeat_byte(0x1d); + let externally_owned_account: H160 = Alice.into(); + let mut handle = MockPrecompileHandle::default(); + assert_eq!( AddressType::EOA, - get_address_type::(&mut MockPrecompileHandle, addr).expect("OOG") + get_address_type::(&mut handle, externally_owned_account).expect("OOG") ); }) } @@ -396,47 +415,50 @@ fn get_address_type_works_for_eoa() { #[test] fn get_address_type_works_for_precompile() { ExtBuilder::default().build().execute_with(|| { - let addr = H160::repeat_byte(0x1d); - pallet_evm::AccountCodes::::insert(addr, vec![0x60, 0x00, 0x60, 0x00, 0xfd]); - assert_eq!( - AddressType::Precompile, - get_address_type::(&mut MockPrecompileHandle, addr).expect("OOG") - ); + let precompiles: Vec = Precompiles::::used_addresses_h160().collect(); + // We expect 4 precompiles + assert_eq!(precompiles.len(), 4); + + let mut handle = MockPrecompileHandle::default(); + precompiles.iter().cloned().for_each(|precompile| { + assert_eq!( + AddressType::Precompile, + get_address_type::(&mut handle, precompile).expect("OOG") + ); + }); }) } #[test] fn get_address_type_works_for_smart_contract() { ExtBuilder::default().build().execute_with(|| { - let addr = H160::repeat_byte(0x1d); - - // length > 5 - pallet_evm::AccountCodes::::insert( - addr, - vec![0x60, 0x00, 0x60, 0x00, 0xfd, 0xff, 0xff], - ); - assert_eq!( - AddressType::Contract, - get_address_type::(&mut MockPrecompileHandle, addr).expect("OOG") + let address = H160::repeat_byte(0x1d); + pallet_evm::AccountCodesMetadata::::insert( + address, + CodeMetadata { + hash: Default::default(), + size: 1, + }, ); - // length < 5 - pallet_evm::AccountCodes::::insert(addr, vec![0x60, 0x00, 0x60]); + let mut handle = MockPrecompileHandle::default(); assert_eq!( AddressType::Contract, - get_address_type::(&mut MockPrecompileHandle, addr).expect("OOG") + get_address_type::(&mut handle, address).expect("OOG") ); }) } #[test] -fn get_address_type_works_for_unknown() { +fn get_address_type_works_for_smart_contract_being_constructed() { ExtBuilder::default().build().execute_with(|| { - let addr = H160::repeat_byte(0x1d); - pallet_evm::AccountCodes::::insert(addr, vec![0x11, 0x00, 0x60, 0x00, 0xfd]); + let contract_being_constucted = H160::repeat_byte(0x1d); + let mut handle = MockPrecompileHandle::default() + .with_contracts_being_constructed(vec![contract_being_constucted]); + assert_eq!( - AddressType::Unknown, - get_address_type::(&mut MockPrecompileHandle, addr).expect("OOG") + AddressType::Contract, + get_address_type::(&mut handle, contract_being_constucted).expect("OOG") ); }) }