Skip to content

Commit a100bc4

Browse files
committed
Fix EOA check for precompiles (#249)
1 parent 82e7129 commit a100bc4

File tree

7 files changed

+129
-70
lines changed

7 files changed

+129
-70
lines changed

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
}

precompiles/tests-external/lib.rs

Lines changed: 51 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ use sp_runtime::{
3434
};
3535
// Frontier
3636
use fp_evm::{ExitReason, ExitRevert, PrecompileFailure, PrecompileHandle};
37-
use pallet_evm::{EnsureAddressNever, EnsureAddressRoot};
37+
use pallet_evm::{CodeMetadata, EnsureAddressNever, EnsureAddressRoot};
3838
use precompile_utils::{
3939
precompile_set::*,
4040
solidity::{codec::Writer, revert::revert},
@@ -147,7 +147,16 @@ impl MockPrecompile {
147147
}
148148
}
149149

150-
struct MockPrecompileHandle;
150+
#[derive(Default)]
151+
struct MockPrecompileHandle {
152+
contracts_being_constructed: Vec<H160>,
153+
}
154+
impl MockPrecompileHandle {
155+
fn with_contracts_being_constructed(mut self, contracts_being_constructed: Vec<H160>) -> Self {
156+
self.contracts_being_constructed = contracts_being_constructed;
157+
self
158+
}
159+
}
151160
impl PrecompileHandle for MockPrecompileHandle {
152161
fn call(
153162
&mut self,
@@ -177,7 +186,7 @@ impl PrecompileHandle for MockPrecompileHandle {
177186
fn refund_external_cost(&mut self, _ref_time: Option<u64>, _proof_size: Option<u64>) {}
178187

179188
fn remaining_gas(&self) -> u64 {
180-
unimplemented!()
189+
0
181190
}
182191

183192
fn log(&mut self, _: H160, _: Vec<H256>, _: Vec<u8>) -> Result<(), evm::ExitError> {
@@ -196,13 +205,21 @@ impl PrecompileHandle for MockPrecompileHandle {
196205
unimplemented!()
197206
}
198207

208+
fn origin(&self) -> H160 {
209+
Alice.into()
210+
}
211+
199212
fn is_static(&self) -> bool {
200213
true
201214
}
202215

203216
fn gas_limit(&self) -> Option<u64> {
204217
unimplemented!()
205218
}
219+
220+
fn is_contract_being_constructed(&self, address: H160) -> bool {
221+
self.contracts_being_constructed.contains(&address)
222+
}
206223
}
207224

208225
pub type Precompiles<R> = PrecompileSetBuilder<
@@ -384,58 +401,63 @@ fn subcalls_works_when_allowed() {
384401
#[test]
385402
fn get_address_type_works_for_eoa() {
386403
ExtBuilder::default().build().execute_with(|| {
387-
let addr = H160::repeat_byte(0x1d);
404+
let externally_owned_account: H160 = Alice.into();
405+
let mut handle = MockPrecompileHandle::default();
406+
388407
assert_eq!(
389408
AddressType::EOA,
390-
get_address_type::<Runtime>(&mut MockPrecompileHandle, addr).expect("OOG")
409+
get_address_type::<Runtime>(&mut handle, externally_owned_account).expect("OOG")
391410
);
392411
})
393412
}
394413

395414
#[test]
396415
fn get_address_type_works_for_precompile() {
397416
ExtBuilder::default().build().execute_with(|| {
398-
let addr = H160::repeat_byte(0x1d);
399-
pallet_evm::AccountCodes::<Runtime>::insert(addr, vec![0x60, 0x00, 0x60, 0x00, 0xfd]);
400-
assert_eq!(
401-
AddressType::Precompile,
402-
get_address_type::<Runtime>(&mut MockPrecompileHandle, addr).expect("OOG")
403-
);
417+
let precompiles: Vec<H160> = Precompiles::<Runtime>::used_addresses_h160().collect();
418+
// We expect 4 precompiles
419+
assert_eq!(precompiles.len(), 4);
420+
421+
let mut handle = MockPrecompileHandle::default();
422+
precompiles.iter().cloned().for_each(|precompile| {
423+
assert_eq!(
424+
AddressType::Precompile,
425+
get_address_type::<Runtime>(&mut handle, precompile).expect("OOG")
426+
);
427+
});
404428
})
405429
}
406430

407431
#[test]
408432
fn get_address_type_works_for_smart_contract() {
409433
ExtBuilder::default().build().execute_with(|| {
410-
let addr = H160::repeat_byte(0x1d);
411-
412-
// length > 5
413-
pallet_evm::AccountCodes::<Runtime>::insert(
414-
addr,
415-
vec![0x60, 0x00, 0x60, 0x00, 0xfd, 0xff, 0xff],
416-
);
417-
assert_eq!(
418-
AddressType::Contract,
419-
get_address_type::<Runtime>(&mut MockPrecompileHandle, addr).expect("OOG")
434+
let address = H160::repeat_byte(0x1d);
435+
pallet_evm::AccountCodesMetadata::<Runtime>::insert(
436+
address,
437+
CodeMetadata {
438+
hash: Default::default(),
439+
size: 1,
440+
},
420441
);
421442

422-
// length < 5
423-
pallet_evm::AccountCodes::<Runtime>::insert(addr, vec![0x60, 0x00, 0x60]);
443+
let mut handle = MockPrecompileHandle::default();
424444
assert_eq!(
425445
AddressType::Contract,
426-
get_address_type::<Runtime>(&mut MockPrecompileHandle, addr).expect("OOG")
446+
get_address_type::<Runtime>(&mut handle, address).expect("OOG")
427447
);
428448
})
429449
}
430450

431451
#[test]
432-
fn get_address_type_works_for_unknown() {
452+
fn get_address_type_works_for_smart_contract_being_constructed() {
433453
ExtBuilder::default().build().execute_with(|| {
434-
let addr = H160::repeat_byte(0x1d);
435-
pallet_evm::AccountCodes::<Runtime>::insert(addr, vec![0x11, 0x00, 0x60, 0x00, 0xfd]);
454+
let contract_being_constucted = H160::repeat_byte(0x1d);
455+
let mut handle = MockPrecompileHandle::default()
456+
.with_contracts_being_constructed(vec![contract_being_constucted]);
457+
436458
assert_eq!(
437-
AddressType::Unknown,
438-
get_address_type::<Runtime>(&mut MockPrecompileHandle, addr).expect("OOG")
459+
AddressType::Contract,
460+
get_address_type::<Runtime>(&mut handle, contract_being_constucted).expect("OOG")
439461
);
440462
})
441463
}

0 commit comments

Comments
 (0)