precompile_utils_tests_external/
lib.rs1#![cfg(test)]
20
21extern crate alloc;
23
24use std::{cell::RefCell, rc::Rc};
25
26use frame_support::{
28 construct_runtime, derive_impl, parameter_types, traits::Everything, weights::Weight,
29};
30use sp_core::{H160, H256, U256};
31use sp_runtime::{
32 traits::{BlakeTwo256, IdentityLookup},
33 BuildStorage, Perbill,
34};
35use fp_evm::{ExitReason, ExitRevert, PrecompileFailure, PrecompileHandle};
37use pallet_evm::{CodeMetadata, EnsureAddressNever, EnsureAddressRoot};
38use precompile_utils::{
39 precompile_set::*,
40 solidity::{codec::Writer, revert::revert},
41 testing::*,
42 EvmResult,
43};
44
45pub type AccountId = MockAccount;
46pub type Balance = u128;
47
48construct_runtime!(
49 pub enum Runtime {
50 System: frame_system::{Pallet, Call, Config<T>, Storage, Event<T>},
51 Balances: pallet_balances::{Pallet, Call, Storage, Event<T>},
52 Evm: pallet_evm::{Pallet, Call, Storage, Event<T>},
53 Timestamp: pallet_timestamp::{Pallet, Call, Storage, Inherent},
54 }
55);
56
57parameter_types! {
58 pub const BlockHashCount: u32 = 250;
59 pub const MaximumBlockWeight: Weight = Weight::from_parts(1024, 1);
60 pub const MaximumBlockLength: u32 = 2 * 1024;
61 pub const AvailableBlockRatio: Perbill = Perbill::one();
62 pub const SS58Prefix: u8 = 42;
63}
64
65#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)]
66impl frame_system::Config for Runtime {
67 type RuntimeEvent = RuntimeEvent;
68 type BaseCallFilter = Everything;
69 type BlockWeights = ();
70 type BlockLength = ();
71 type RuntimeOrigin = RuntimeOrigin;
72 type RuntimeCall = RuntimeCall;
73 type RuntimeTask = RuntimeTask;
74 type Nonce = u64;
75 type Hash = H256;
76 type Hashing = BlakeTwo256;
77 type AccountId = AccountId;
78 type Lookup = IdentityLookup<Self::AccountId>;
79 type Block = frame_system::mocking::MockBlock<Self>;
80 type BlockHashCount = BlockHashCount;
81 type DbWeight = ();
82 type Version = ();
83 type PalletInfo = PalletInfo;
84 type AccountData = pallet_balances::AccountData<Balance>;
85 type OnNewAccount = ();
86 type OnKilledAccount = ();
87 type SystemWeightInfo = ();
88 type SS58Prefix = SS58Prefix;
89 type OnSetCode = ();
90 type MaxConsumers = frame_support::traits::ConstU32<16>;
91}
92
93parameter_types! {
94 pub const ExistentialDeposit: u128 = 0;
95}
96impl pallet_balances::Config for Runtime {
97 type RuntimeEvent = RuntimeEvent;
98 type RuntimeHoldReason = RuntimeHoldReason;
99 type RuntimeFreezeReason = RuntimeFreezeReason;
100 type WeightInfo = ();
101 type Balance = Balance;
102 type DustRemoval = ();
103 type ExistentialDeposit = ExistentialDeposit;
104 type AccountStore = System;
105 type ReserveIdentifier = [u8; 8];
106 type FreezeIdentifier = RuntimeFreezeReason;
107 type MaxLocks = ();
108 type MaxReserves = ();
109 type MaxFreezes = ();
110 type DoneSlashHandler = ();
111}
112
113#[derive(Debug, Clone)]
114pub struct MockPrecompile;
115
116#[precompile_utils::precompile]
117impl MockPrecompile {
118 #[precompile::public("subcall()")]
120 fn subcall(handle: &mut impl PrecompileHandle) -> EvmResult {
121 match handle.call(
122 handle.code_address(),
123 None,
124 Writer::new_with_selector(0x0b93381bu32).build(),
126 None,
127 false,
128 &evm::Context {
129 caller: handle.code_address(),
130 address: handle.code_address(),
131 apparent_value: 0.into(),
132 },
133 ) {
134 (ExitReason::Succeed(_), _) => Ok(()),
135 (ExitReason::Revert(_), v) => Err(PrecompileFailure::Revert {
136 exit_status: ExitRevert::Reverted,
137 output: v,
138 }),
139 _ => Err(revert("unexpected error")),
140 }
141 }
142
143 #[precompile::public("success()")]
145 fn success(_: &mut impl PrecompileHandle) -> EvmResult {
146 Ok(())
147 }
148}
149
150#[derive(Default)]
151struct MockPrecompileHandle {
152 contracts_being_constructed: Vec<H160>,
153}
154impl 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}
160impl PrecompileHandle for MockPrecompileHandle {
161 fn call(
162 &mut self,
163 _: H160,
164 _: Option<evm::Transfer>,
165 _: Vec<u8>,
166 _: Option<u64>,
167 _: bool,
168 _: &evm::Context,
169 ) -> (ExitReason, Vec<u8>) {
170 unimplemented!()
171 }
172
173 fn record_cost(&mut self, _: u64) -> Result<(), evm::ExitError> {
174 Ok(())
175 }
176
177 fn record_external_cost(
178 &mut self,
179 _ref_time: Option<u64>,
180 _proof_size: Option<u64>,
181 _storage_growth: Option<u64>,
182 ) -> Result<(), fp_evm::ExitError> {
183 Ok(())
184 }
185
186 fn refund_external_cost(&mut self, _ref_time: Option<u64>, _proof_size: Option<u64>) {}
187
188 fn remaining_gas(&self) -> u64 {
189 0
190 }
191
192 fn log(&mut self, _: H160, _: Vec<H256>, _: Vec<u8>) -> Result<(), evm::ExitError> {
193 unimplemented!()
194 }
195
196 fn code_address(&self) -> H160 {
197 unimplemented!()
198 }
199
200 fn input(&self) -> &[u8] {
201 unimplemented!()
202 }
203
204 fn context(&self) -> &evm::Context {
205 unimplemented!()
206 }
207
208 fn origin(&self) -> H160 {
209 Alice.into()
210 }
211
212 fn is_static(&self) -> bool {
213 true
214 }
215
216 fn gas_limit(&self) -> Option<u64> {
217 unimplemented!()
218 }
219
220 fn is_contract_being_constructed(&self, address: H160) -> bool {
221 self.contracts_being_constructed.contains(&address)
222 }
223}
224
225pub type Precompiles<R> = PrecompileSetBuilder<
226 R,
227 (
228 PrecompileAt<AddressU64<1>, MockPrecompile>,
229 PrecompileAt<AddressU64<2>, MockPrecompile, CallableByContract>,
230 PrecompileAt<AddressU64<3>, MockPrecompile, CallableByPrecompile>,
231 PrecompileAt<AddressU64<4>, MockPrecompile, SubcallWithMaxNesting<1>>,
232 ),
233>;
234
235pub type PCall = MockPrecompileCall;
236
237const MAX_POV_SIZE: u64 = 5 * 1024 * 1024;
238
239parameter_types! {
240 pub BlockGasLimit: U256 = U256::from(u64::MAX);
241 pub PrecompilesValue: Precompiles<Runtime> = Precompiles::new();
242 pub const WeightPerGas: Weight = Weight::from_parts(1, 0);
243 pub GasLimitPovSizeRatio: u64 = {
244 let block_gas_limit = BlockGasLimit::get().min(u64::MAX.into()).low_u64();
245 block_gas_limit.saturating_div(MAX_POV_SIZE)
246 };
247}
248
249impl pallet_evm::Config for Runtime {
250 type AccountProvider = pallet_evm::FrameSystemAccountProvider<Self>;
251 type FeeCalculator = ();
252 type GasWeightMapping = pallet_evm::FixedGasWeightMapping<Self>;
253 type WeightPerGas = WeightPerGas;
254 type BlockHashMapping = pallet_evm::SubstrateBlockHashMapping<Self>;
255 type CallOrigin = EnsureAddressRoot<AccountId>;
256 type WithdrawOrigin = EnsureAddressNever<AccountId>;
257 type AddressMapping = AccountId;
258 type Currency = Balances;
259 type PrecompilesType = Precompiles<Runtime>;
260 type PrecompilesValue = PrecompilesValue;
261 type ChainId = ();
262 type BlockGasLimit = BlockGasLimit;
263 type Runner = pallet_evm::runner::stack::Runner<Self>;
264 type OnChargeTransaction = ();
265 type OnCreate = ();
266 type FindAuthor = ();
267 type GasLimitPovSizeRatio = GasLimitPovSizeRatio;
268 type GasLimitStorageGrowthRatio = ();
269 type Timestamp = Timestamp;
270 type CreateInnerOriginFilter = ();
271 type CreateOriginFilter = ();
272 type WeightInfo = pallet_evm::weights::SubstrateWeight<Runtime>;
273}
274
275parameter_types! {
276 pub const MinimumPeriod: u64 = 5;
277}
278impl pallet_timestamp::Config for Runtime {
279 type Moment = u64;
280 type OnTimestampSet = ();
281 type MinimumPeriod = MinimumPeriod;
282 type WeightInfo = ();
283}
284
285#[derive(Default)]
286struct ExtBuilder {}
287
288impl ExtBuilder {
289 fn build(self) -> sp_io::TestExternalities {
290 let t = frame_system::GenesisConfig::<Runtime>::default()
291 .build_storage()
292 .expect("Frame system builds valid default genesis config");
293
294 let mut ext = sp_io::TestExternalities::new(t);
295 ext.execute_with(|| {
296 System::set_block_number(1);
297 });
298 ext
299 }
300}
301
302fn precompiles() -> Precompiles<Runtime> {
303 PrecompilesValue::get()
304}
305
306#[test]
307fn default_checks_succeed_when_called_by_eoa() {
308 ExtBuilder::default().build().execute_with(|| {
309 precompiles()
310 .prepare_test(Alice, H160::from_low_u64_be(1), PCall::success {})
311 .with_subcall_handle(|Subcall { .. }| panic!("there should be no subcall"))
312 .execute_returns(())
313 })
314}
315
316#[test]
317fn default_checks_revert_when_called_by_precompile() {
318 ExtBuilder::default().build().execute_with(|| {
319 precompiles()
320 .prepare_test(
321 H160::from_low_u64_be(1),
322 H160::from_low_u64_be(1),
323 PCall::success {},
324 )
325 .with_subcall_handle(|Subcall { .. }| panic!("there should be no subcall"))
326 .execute_reverts(|r| r == b"Function not callable by precompiles")
327 })
328}
329
330#[test]
331fn default_checks_revert_when_called_by_contract() {
332 ExtBuilder::default().build().execute_with(|| {
333 let _ = pallet_evm::Pallet::<Runtime>::create_account(
334 Alice.into(),
335 hex_literal::hex!("1460006000fd").to_vec(),
336 None,
337 );
338
339 precompiles()
340 .prepare_test(Alice, H160::from_low_u64_be(1), PCall::success {})
341 .with_subcall_handle(|Subcall { .. }| panic!("there should be no subcall"))
342 .execute_reverts(|r| r == b"Function not callable by smart contracts")
343 })
344}
345
346#[test]
347fn default_checks_revert_when_doing_subcall() {
348 ExtBuilder::default().build().execute_with(|| {
349 precompiles()
350 .prepare_test(Alice, H160::from_low_u64_be(1), PCall::subcall {})
351 .with_subcall_handle(|Subcall { .. }| panic!("there should be no subcall"))
352 .execute_reverts(|r| r == b"subcalls disabled for this precompile")
353 })
354}
355
356#[test]
357fn callable_by_contract_works() {
358 ExtBuilder::default().build().execute_with(|| {
359 let _ = pallet_evm::Pallet::<Runtime>::create_account(
360 Alice.into(),
361 hex_literal::hex!("1460006000fd").to_vec(),
362 None,
363 );
364
365 precompiles()
366 .prepare_test(Alice, H160::from_low_u64_be(2), PCall::success {})
367 .with_subcall_handle(|Subcall { .. }| panic!("there should be no subcall"))
368 .execute_returns(())
369 })
370}
371
372#[test]
373fn callable_by_precompile_works() {
374 ExtBuilder::default().build().execute_with(|| {
375 precompiles()
376 .prepare_test(
377 H160::from_low_u64_be(3),
378 H160::from_low_u64_be(3),
379 PCall::success {},
380 )
381 .with_subcall_handle(|Subcall { .. }| panic!("there should be no subcall"))
382 .execute_returns(())
383 })
384}
385
386#[test]
387fn subcalls_works_when_allowed() {
388 ExtBuilder::default().build().execute_with(|| {
389 let subcall_occured = Rc::new(RefCell::new(false));
390 {
391 let subcall_occured = Rc::clone(&subcall_occured);
392 precompiles()
393 .prepare_test(Alice, H160::from_low_u64_be(4), PCall::subcall {})
394 .with_subcall_handle(move |Subcall { .. }| {
395 *subcall_occured.borrow_mut() = true;
396 SubcallOutput::succeed()
397 })
398 .execute_returns(());
399 }
400 assert!(*subcall_occured.borrow());
401 })
402}
403
404#[test]
405fn get_address_type_works_for_eoa() {
406 ExtBuilder::default().build().execute_with(|| {
407 let externally_owned_account: H160 = Alice.into();
408 let mut handle = MockPrecompileHandle::default();
409
410 assert_eq!(
411 AddressType::EOA,
412 get_address_type::<Runtime>(&mut handle, externally_owned_account).expect("OOG")
413 );
414 })
415}
416
417#[test]
418fn get_address_type_works_for_precompile() {
419 ExtBuilder::default().build().execute_with(|| {
420 let precompiles: Vec<H160> = Precompiles::<Runtime>::used_addresses_h160().collect();
421 assert_eq!(precompiles.len(), 4);
423
424 let mut handle = MockPrecompileHandle::default();
425 precompiles.iter().cloned().for_each(|precompile| {
426 assert_eq!(
427 AddressType::Precompile,
428 get_address_type::<Runtime>(&mut handle, precompile).expect("OOG")
429 );
430 });
431 })
432}
433
434#[test]
435fn get_address_type_works_for_smart_contract() {
436 ExtBuilder::default().build().execute_with(|| {
437 let address = H160::repeat_byte(0x1d);
438 pallet_evm::AccountCodesMetadata::<Runtime>::insert(
439 address,
440 CodeMetadata {
441 hash: Default::default(),
442 size: 1,
443 },
444 );
445
446 let mut handle = MockPrecompileHandle::default();
447 assert_eq!(
448 AddressType::Contract,
449 get_address_type::<Runtime>(&mut handle, address).expect("OOG")
450 );
451 })
452}
453
454#[test]
455fn get_address_type_works_for_smart_contract_being_constructed() {
456 ExtBuilder::default().build().execute_with(|| {
457 let contract_being_constucted = H160::repeat_byte(0x1d);
458 let mut handle = MockPrecompileHandle::default()
459 .with_contracts_being_constructed(vec![contract_being_constucted]);
460
461 assert_eq!(
462 AddressType::Contract,
463 get_address_type::<Runtime>(&mut handle, contract_being_constucted).expect("OOG")
464 );
465 })
466}