Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
8 changes: 4 additions & 4 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions frame/evm/precompile/dispatch/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -217,11 +217,19 @@ impl PrecompileHandle for MockHandle {
&self.context
}

fn origin(&self) -> H160 {
unimplemented!()
}

fn is_static(&self) -> bool {
unimplemented!()
}

fn gas_limit(&self) -> Option<u64> {
None
}

fn is_contract_being_constructed(&self, _address: H160) -> bool {
unimplemented!()
}
}
1 change: 0 additions & 1 deletion frame/evm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 8 additions & 0 deletions frame/evm/test-vector-support/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,13 +111,21 @@ impl PrecompileHandle for MockHandle {
&self.context
}

fn origin(&self) -> H160 {
unimplemented!()
}

fn is_static(&self) -> bool {
self.is_static
}

fn gas_limit(&self) -> Option<u64> {
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.
Expand Down
8 changes: 8 additions & 0 deletions precompiles/src/evm/handle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,10 @@ mod tests {
unimplemented!()
}

fn origin(&self) -> sp_core::H160 {
unimplemented!()
}

fn is_static(&self) -> bool {
true
}
Expand All @@ -195,6 +199,10 @@ mod tests {
}

fn refund_external_cost(&mut self, _ref_time: Option<u64>, _proof_size: Option<u64>) {}

fn is_contract_being_constructed(&self, _address: sp_core::H160) -> bool {
unimplemented!()
}
}

#[test]
Expand Down
70 changes: 34 additions & 36 deletions precompiles/src/precompile_set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -304,13 +304,11 @@ impl<T: SelectorFilter> PrecompileChecks for CallableByPrecompile<T> {
#[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,
}

Expand All @@ -319,39 +317,27 @@ pub fn get_address_type<R: pallet_evm::Config>(
handle: &mut impl PrecompileHandle,
address: H160,
) -> Result<AddressType, ExitError> {
// Check if address is a precompile
if let Ok(true) = is_precompile_or_fail::<R>(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::<R>(76)?;
handle.record_db_read::<R>(ACCOUNT_CODES_METADATA_PROOF_SIZE as usize)?;
let code_len = pallet_evm::Pallet::<R>::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::<R>(code_len as usize)?;
let code = pallet_evm::AccountCodes::<R>::get(address);
if code == [0x60, 0x00, 0x60, 0x00, 0xfd] {
return Ok(AddressType::Precompile);
}

Ok(AddressType::Unknown)
}

fn is_address_eoa_or_precompile<R: pallet_evm::Config>(
handle: &mut impl PrecompileHandle,
address: H160,
) -> Result<bool, ExitError> {
match get_address_type::<R>(handle, address)? {
AddressType::EOA | AddressType::Precompile => Ok(true),
_ => Ok(false),
}
Ok(AddressType::Contract)
}

/// Common checks for precompile and precompile sets.
Expand All @@ -375,19 +361,23 @@ fn common_checks<R: pallet_evm::Config, C: PrecompileChecks>(
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::<R>(handle, caller)? {
return Err(revert("Function not callable by smart contracts"));
}
let caller_address_type = get_address_type::<R>(handle, caller)?;

// 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::<R>(caller, handle.remaining_gas())? {
let is_precompile = caller_address_type == AddressType::Precompile;
if !callable_by_precompile && is_precompile {
return Err(revert("Function not callable by precompiles"));
}

// Is this selector callable from a smart contract?
let callable_by_smart_contract =
C::callable_by_smart_contract(caller, selector).unwrap_or(false);
let is_smart_contract = caller_address_type == AddressType::Contract;
if !callable_by_smart_contract && is_smart_contract {
return Err(revert("Function not callable by smart contracts"));
}

Ok(())
}

Expand Down Expand Up @@ -463,6 +453,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()
}
Expand All @@ -484,6 +478,10 @@ impl<'a, H: PrecompileHandle> PrecompileHandle for RestrictiveHandle<'a, H> {
fn refund_external_cost(&mut self, ref_time: Option<u64>, proof_size: Option<u64>) {
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.
Expand Down
10 changes: 10 additions & 0 deletions precompiles/src/testing/handle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -213,4 +215,12 @@ impl PrecompileHandle for MockHandle {
}

fn refund_external_cost(&mut self, _ref_time: Option<u64>, _proof_size: Option<u64>) {}

fn origin(&self) -> H160 {
Alice.into()
}

fn is_contract_being_constructed(&self, _address: H160) -> bool {
false
}
}
Loading
Loading