Skip to content

Commit fb5c139

Browse files
committed
Fix EOA check for precompiles (#249)
1 parent 52cbb21 commit fb5c139

File tree

8 files changed

+133
-74
lines changed

8 files changed

+133
-74
lines changed

Cargo.lock

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

frame/evm/precompile/dispatch/src/mock.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,11 +216,19 @@ impl PrecompileHandle for MockHandle {
216216
&self.context
217217
}
218218

219+
fn origin(&self) -> H160 {
220+
unimplemented!()
221+
}
222+
219223
fn is_static(&self) -> bool {
220224
unimplemented!()
221225
}
222226

223227
fn gas_limit(&self) -> Option<u64> {
224228
None
225229
}
230+
231+
fn is_contract_being_constructed(&self, _address: H160) -> bool {
232+
unimplemented!()
233+
}
226234
}

frame/evm/src/lib.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -696,7 +696,6 @@ pub mod pallet {
696696
.saturating_add(T::DbWeight::get().reads(1))
697697
.saturating_add(account_basic_weight)
698698
.saturating_add(min_gas_weight);
699-
700699
}
701700

702701
total_weight

frame/evm/test-vector-support/src/lib.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,13 +111,21 @@ impl PrecompileHandle for MockHandle {
111111
&self.context
112112
}
113113

114+
fn origin(&self) -> H160 {
115+
unimplemented!()
116+
}
117+
114118
fn is_static(&self) -> bool {
115119
self.is_static
116120
}
117121

118122
fn gas_limit(&self) -> Option<u64> {
119123
self.gas_limit
120124
}
125+
126+
fn is_contract_being_constructed(&self, _address: H160) -> bool {
127+
unimplemented!()
128+
}
121129
}
122130

123131
/// Tests a precompile against the ethereum consensus tests defined in the given file at filepath.

precompiles/src/evm/handle.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,10 @@ mod tests {
177177
unimplemented!()
178178
}
179179

180+
fn origin(&self) -> sp_core::H160 {
181+
unimplemented!()
182+
}
183+
180184
fn is_static(&self) -> bool {
181185
true
182186
}
@@ -195,6 +199,10 @@ mod tests {
195199
}
196200

197201
fn refund_external_cost(&mut self, _ref_time: Option<u64>, _proof_size: Option<u64>) {}
202+
203+
fn is_contract_being_constructed(&self, _address: sp_core::H160) -> bool {
204+
unimplemented!()
205+
}
198206
}
199207

200208
#[test]

precompiles/src/precompile_set.rs

Lines changed: 44 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ use alloc::{collections::btree_map::BTreeMap, vec, vec::Vec};
2929
use core::{cell::RefCell, marker::PhantomData, ops::RangeInclusive};
3030
use fp_evm::{
3131
ExitError, IsPrecompileResult, Precompile, PrecompileFailure, PrecompileHandle,
32-
PrecompileResult, PrecompileSet,
32+
PrecompileResult, PrecompileSet, ACCOUNT_CODES_METADATA_PROOF_SIZE,
3333
};
3434
use frame_support::pallet_prelude::Get;
3535
use impl_trait_for_tuples::impl_for_tuples;
@@ -304,13 +304,11 @@ impl<T: SelectorFilter> PrecompileChecks for CallableByPrecompile<T> {
304304
#[derive(PartialEq)]
305305
#[cfg_attr(feature = "std", derive(Debug))]
306306
pub enum AddressType {
307-
/// The code stored at the address is less than 5 bytes, but not well known.
308-
Unknown,
309307
/// No code is stored at the address, therefore is EOA.
310308
EOA,
311309
/// The 5-byte magic constant for a precompile is stored at the address.
312310
Precompile,
313-
/// The code is greater than 5-bytes, potentially a Smart Contract.
311+
/// Every address that is not a EOA or a Precompile is potentially a Smart Contract.
314312
Contract,
315313
}
316314

@@ -319,39 +317,27 @@ pub fn get_address_type<R: pallet_evm::Config>(
319317
handle: &mut impl PrecompileHandle,
320318
address: H160,
321319
) -> Result<AddressType, ExitError> {
320+
// Check if address is a precompile
321+
if let Ok(true) = is_precompile_or_fail::<R>(address, handle.remaining_gas()) {
322+
return Ok(AddressType::Precompile);
323+
}
324+
325+
// Contracts under-construction don't have code yet
326+
if handle.is_contract_being_constructed(address) {
327+
return Ok(AddressType::Contract);
328+
}
329+
322330
// AccountCodesMetadata:
323331
// Blake2128(16) + H160(20) + CodeMetadata(40)
324-
handle.record_db_read::<R>(76)?;
332+
handle.record_db_read::<R>(ACCOUNT_CODES_METADATA_PROOF_SIZE as usize)?;
325333
let code_len = pallet_evm::Pallet::<R>::account_code_metadata(address).size;
326334

327-
// 0 => either EOA or precompile without dummy code
335+
// Having no code at this point means that the address is an EOA
328336
if code_len == 0 {
329337
return Ok(AddressType::EOA);
330338
}
331339

332-
// dummy code is 5 bytes long, so any other len means it is a contract.
333-
if code_len != 5 {
334-
return Ok(AddressType::Contract);
335-
}
336-
337-
// check code matches dummy code
338-
handle.record_db_read::<R>(code_len as usize)?;
339-
let code = pallet_evm::AccountCodes::<R>::get(address);
340-
if code == [0x60, 0x00, 0x60, 0x00, 0xfd] {
341-
return Ok(AddressType::Precompile);
342-
}
343-
344-
Ok(AddressType::Unknown)
345-
}
346-
347-
fn is_address_eoa_or_precompile<R: pallet_evm::Config>(
348-
handle: &mut impl PrecompileHandle,
349-
address: H160,
350-
) -> Result<bool, ExitError> {
351-
match get_address_type::<R>(handle, address)? {
352-
AddressType::EOA | AddressType::Precompile => Ok(true),
353-
_ => Ok(false),
354-
}
340+
Ok(AddressType::Contract)
355341
}
356342

357343
/// Common checks for precompile and precompile sets.
@@ -375,17 +361,27 @@ fn common_checks<R: pallet_evm::Config, C: PrecompileChecks>(
375361
u32::from_be_bytes(buffer)
376362
});
377363

378-
// Is this selector callable from a smart contract?
379-
let callable_by_smart_contract =
380-
C::callable_by_smart_contract(caller, selector).unwrap_or(false);
381-
if !callable_by_smart_contract && !is_address_eoa_or_precompile::<R>(handle, caller)? {
382-
return Err(revert("Function not callable by smart contracts"));
383-
}
384-
385-
// Is this selector callable from a precompile?
386-
let callable_by_precompile = C::callable_by_precompile(caller, selector).unwrap_or(false);
387-
if !callable_by_precompile && is_precompile_or_fail::<R>(caller, handle.remaining_gas())? {
388-
return Err(revert("Function not callable by precompiles"));
364+
let caller_address_type = get_address_type::<R>(handle, caller)?;
365+
match caller_address_type {
366+
AddressType::Precompile => {
367+
// Is this selector callable from a precompile?
368+
let callable_by_precompile =
369+
C::callable_by_precompile(caller, selector).unwrap_or(false);
370+
if !callable_by_precompile {
371+
return Err(revert("Function not callable by precompiles"));
372+
}
373+
}
374+
AddressType::Contract => {
375+
// Is this selector callable from a smart contract?
376+
let callable_by_smart_contract =
377+
C::callable_by_smart_contract(caller, selector).unwrap_or(false);
378+
if !callable_by_smart_contract {
379+
return Err(revert("Function not callable by smart contracts"));
380+
}
381+
}
382+
AddressType::EOA => {
383+
// No check required for EOA
384+
}
389385
}
390386

391387
Ok(())
@@ -463,6 +459,10 @@ impl<'a, H: PrecompileHandle> PrecompileHandle for RestrictiveHandle<'a, H> {
463459
self.handle.context()
464460
}
465461

462+
fn origin(&self) -> H160 {
463+
self.handle.origin()
464+
}
465+
466466
fn is_static(&self) -> bool {
467467
self.handle.is_static()
468468
}
@@ -484,6 +484,10 @@ impl<'a, H: PrecompileHandle> PrecompileHandle for RestrictiveHandle<'a, H> {
484484
fn refund_external_cost(&mut self, ref_time: Option<u64>, proof_size: Option<u64>) {
485485
self.handle.refund_external_cost(ref_time, proof_size)
486486
}
487+
488+
fn is_contract_being_constructed(&self, address: H160) -> bool {
489+
self.handle.is_contract_being_constructed(address)
490+
}
487491
}
488492

489493
/// Allows to know if a precompile is active or not.

precompiles/src/testing/handle.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ use evm::{ExitRevert, ExitSucceed};
2222
use fp_evm::{Context, ExitError, ExitReason, Log, PrecompileHandle, Transfer};
2323
use sp_core::{H160, H256};
2424

25+
use super::Alice;
26+
2527
#[derive(Debug, Clone)]
2628
pub struct Subcall {
2729
pub address: H160,
@@ -213,4 +215,12 @@ impl PrecompileHandle for MockHandle {
213215
}
214216

215217
fn refund_external_cost(&mut self, _ref_time: Option<u64>, _proof_size: Option<u64>) {}
218+
219+
fn origin(&self) -> H160 {
220+
Alice.into()
221+
}
222+
223+
fn is_contract_being_constructed(&self, _address: H160) -> bool {
224+
false
225+
}
216226
}

0 commit comments

Comments
 (0)