precompile_utils/
precompile_set.rs

1// This file is part of Frontier.
2
3// Copyright (c) Moonsong Labs.
4// Copyright (C) Parity Technologies (UK) Ltd.
5// SPDX-License-Identifier: Apache-2.0
6
7// Licensed under the Apache License, Version 2.0 (the "License");
8// you may not use this file except in compliance with the License.
9// You may obtain a copy of the License at
10//
11// 	http://www.apache.org/licenses/LICENSE-2.0
12//
13// Unless required by applicable law or agreed to in writing, software
14// distributed under the License is distributed on an "AS IS" BASIS,
15// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16// See the License for the specific language governing permissions and
17// limitations under the License.
18
19//! Provide utils to assemble precompiles and precompilesets into a
20//! final precompile set with security checks. All security checks are enabled by
21//! default and must be disabled explicely through type annotations.
22
23use crate::{
24	evm::handle::PrecompileHandleExt,
25	solidity::{codec::String, revert::revert},
26	EvmResult,
27};
28use alloc::{collections::btree_map::BTreeMap, vec, vec::Vec};
29use core::{cell::RefCell, marker::PhantomData, ops::RangeInclusive};
30use fp_evm::{
31	ExitError, IsPrecompileResult, Precompile, PrecompileFailure, PrecompileHandle,
32	PrecompileResult, PrecompileSet, ACCOUNT_CODES_METADATA_PROOF_SIZE,
33};
34use frame_support::pallet_prelude::Get;
35use impl_trait_for_tuples::impl_for_tuples;
36use pallet_evm::AddressMapping;
37use sp_core::{H160, H256};
38
39/// Trait representing checks that can be made on a precompile call.
40/// Types implementing this trait are made to be chained in a tuple.
41///
42/// For that reason every method returns an Option, None meaning that
43/// the implementer have no constraint and the decision is left to
44/// latter elements in the chain. If None is returned by all elements of
45/// the chain then sensible defaults are used.
46///
47/// Both `PrecompileAt` and `PrecompileSetStartingWith` have a type parameter that must
48/// implement this trait to configure the checks of the precompile(set) it represents.
49pub trait PrecompileChecks {
50	#[inline(always)]
51	/// Is there a limit to the amount of recursions this precompile
52	/// can make using subcalls? 0 means this specific precompile will not
53	/// be callable as a subcall of itself, 1 will allow one level of recursion,
54	/// etc...
55	///
56	/// If all checks return None, defaults to `Some(0)` (no recursion allowed).
57	fn recursion_limit() -> Option<Option<u16>> {
58		None
59	}
60
61	#[inline(always)]
62	/// Does this precompile supports being called with DELEGATECALL or CALLCODE?
63	///
64	/// If all checks return None, defaults to `false`.
65	fn accept_delegate_call() -> Option<bool> {
66		None
67	}
68
69	#[inline(always)]
70	/// Is this precompile callable by a smart contract?
71	///
72	/// If all checks return None, defaults to `false`.
73	fn callable_by_smart_contract(_caller: H160, _called_selector: Option<u32>) -> Option<bool> {
74		None
75	}
76
77	#[inline(always)]
78	/// Is this precompile callable by a precompile?
79	///
80	/// If all checks return None, defaults to `false`.
81	fn callable_by_precompile(_caller: H160, _called_selector: Option<u32>) -> Option<bool> {
82		None
83	}
84
85	#[inline(always)]
86	/// Is this precompile able to do subcalls?
87	///
88	/// If all checks return None, defaults to `false`.
89	fn allow_subcalls() -> Option<bool> {
90		None
91	}
92
93	/// Summarize the checks when being called by a smart contract.
94	fn callable_by_smart_contract_summary() -> Option<String> {
95		None
96	}
97
98	/// Summarize the checks when being called by a precompile.
99	fn callable_by_precompile_summary() -> Option<String> {
100		None
101	}
102}
103
104#[derive(Debug, Clone)]
105pub enum DiscriminantResult<T> {
106	Some(T, u64),
107	None(u64),
108	OutOfGas,
109}
110
111impl<T> From<DiscriminantResult<T>> for IsPrecompileResult {
112	fn from(val: DiscriminantResult<T>) -> Self {
113		match val {
114			DiscriminantResult::<T>::Some(_, extra_cost) => IsPrecompileResult::Answer {
115				is_precompile: true,
116				extra_cost,
117			},
118			DiscriminantResult::<T>::None(extra_cost) => IsPrecompileResult::Answer {
119				is_precompile: false,
120				extra_cost,
121			},
122			DiscriminantResult::<T>::OutOfGas => IsPrecompileResult::OutOfGas,
123		}
124	}
125}
126
127#[derive(Debug, Clone)]
128#[cfg_attr(feature = "testing", derive(serde::Serialize, serde::Deserialize))]
129pub enum PrecompileKind {
130	Single(H160),
131	Prefixed(Vec<u8>),
132	Multiple(Vec<H160>),
133}
134
135#[derive(Debug, Clone)]
136#[cfg_attr(feature = "testing", derive(serde::Serialize, serde::Deserialize))]
137pub struct PrecompileCheckSummary {
138	pub name: Option<String>,
139	pub precompile_kind: PrecompileKind,
140	pub recursion_limit: Option<u16>,
141	pub accept_delegate_call: bool,
142	pub callable_by_smart_contract: String,
143	pub callable_by_precompile: String,
144}
145
146#[impl_for_tuples(0, 20)]
147impl PrecompileChecks for Tuple {
148	#[inline(always)]
149	fn recursion_limit() -> Option<Option<u16>> {
150		for_tuples!(#(
151			if let Some(check) = Tuple::recursion_limit() {
152				return Some(check);
153			}
154		)*);
155
156		None
157	}
158
159	#[inline(always)]
160	fn accept_delegate_call() -> Option<bool> {
161		for_tuples!(#(
162			if let Some(check) = Tuple::accept_delegate_call() {
163				return Some(check);
164			}
165		)*);
166
167		None
168	}
169
170	#[inline(always)]
171	fn callable_by_smart_contract(caller: H160, called_selector: Option<u32>) -> Option<bool> {
172		for_tuples!(#(
173			if let Some(check) = Tuple::callable_by_smart_contract(caller, called_selector) {
174				return Some(check);
175			}
176		)*);
177
178		None
179	}
180
181	#[inline(always)]
182	fn callable_by_precompile(caller: H160, called_selector: Option<u32>) -> Option<bool> {
183		for_tuples!(#(
184			if let Some(check) = Tuple::callable_by_precompile(caller, called_selector) {
185				return Some(check);
186			}
187		)*);
188
189		None
190	}
191
192	#[inline(always)]
193	fn allow_subcalls() -> Option<bool> {
194		for_tuples!(#(
195			if let Some(check) = Tuple::allow_subcalls() {
196				return Some(check);
197			}
198		)*);
199
200		None
201	}
202
203	fn callable_by_smart_contract_summary() -> Option<String> {
204		for_tuples!(#(
205			if let Some(check) = Tuple::callable_by_smart_contract_summary() {
206				return Some(check);
207			}
208		)*);
209
210		None
211	}
212
213	fn callable_by_precompile_summary() -> Option<String> {
214		for_tuples!(#(
215			if let Some(check) = Tuple::callable_by_precompile_summary() {
216				return Some(check);
217			}
218		)*);
219
220		None
221	}
222}
223
224/// Precompile can be called using DELEGATECALL/CALLCODE.
225pub struct AcceptDelegateCall;
226
227impl PrecompileChecks for AcceptDelegateCall {
228	#[inline(always)]
229	fn accept_delegate_call() -> Option<bool> {
230		Some(true)
231	}
232}
233
234/// Precompile is able to do subcalls with provided nesting limit.
235pub struct SubcallWithMaxNesting<const R: u16>;
236
237impl<const R: u16> PrecompileChecks for SubcallWithMaxNesting<R> {
238	#[inline(always)]
239	fn recursion_limit() -> Option<Option<u16>> {
240		Some(Some(R))
241	}
242
243	#[inline(always)]
244	fn allow_subcalls() -> Option<bool> {
245		Some(true)
246	}
247}
248
249pub trait SelectorFilter {
250	fn is_allowed(_caller: H160, _selector: Option<u32>) -> bool;
251
252	fn description() -> String;
253}
254pub struct ForAllSelectors;
255impl SelectorFilter for ForAllSelectors {
256	fn is_allowed(_caller: H160, _selector: Option<u32>) -> bool {
257		true
258	}
259
260	fn description() -> String {
261		"Allowed for all selectors and callers".into()
262	}
263}
264
265pub struct OnlyFrom<T>(PhantomData<T>);
266impl<T: Get<H160>> SelectorFilter for OnlyFrom<T> {
267	fn is_allowed(caller: H160, _selector: Option<u32>) -> bool {
268		caller == T::get()
269	}
270
271	fn description() -> String {
272		alloc::format!("Allowed for all selectors only if called from {}", T::get())
273	}
274}
275
276pub struct CallableByContract<T = ForAllSelectors>(PhantomData<T>);
277
278impl<T: SelectorFilter> PrecompileChecks for CallableByContract<T> {
279	#[inline(always)]
280	fn callable_by_smart_contract(caller: H160, called_selector: Option<u32>) -> Option<bool> {
281		Some(T::is_allowed(caller, called_selector))
282	}
283
284	fn callable_by_smart_contract_summary() -> Option<String> {
285		Some(T::description())
286	}
287}
288
289/// Precompiles are allowed to call this precompile.
290pub struct CallableByPrecompile<T = ForAllSelectors>(PhantomData<T>);
291
292impl<T: SelectorFilter> PrecompileChecks for CallableByPrecompile<T> {
293	#[inline(always)]
294	fn callable_by_precompile(caller: H160, called_selector: Option<u32>) -> Option<bool> {
295		Some(T::is_allowed(caller, called_selector))
296	}
297
298	fn callable_by_precompile_summary() -> Option<String> {
299		Some(T::description())
300	}
301}
302
303/// The type of EVM address.
304#[derive(PartialEq)]
305#[cfg_attr(feature = "std", derive(Debug))]
306pub enum AddressType {
307	/// No code is stored at the address, therefore is EOA.
308	EOA,
309	/// The 5-byte magic constant for a precompile is stored at the address.
310	Precompile,
311	/// Every address that is not a EOA or a Precompile is potentially a Smart Contract.
312	Contract,
313}
314
315/// Retrieves the type of address demarcated by `AddressType`.
316pub fn get_address_type<R: pallet_evm::Config>(
317	handle: &mut impl PrecompileHandle,
318	address: H160,
319) -> 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
330	// AccountCodesMetadata:
331	// Blake2128(16) + H160(20) + CodeMetadata(40)
332	handle.record_db_read::<R>(ACCOUNT_CODES_METADATA_PROOF_SIZE as usize)?;
333	let code_len = pallet_evm::Pallet::<R>::account_code_metadata(address).size;
334
335	// Having no code at this point means that the address is an EOA
336	if code_len == 0 {
337		return Ok(AddressType::EOA);
338	}
339
340	Ok(AddressType::Contract)
341}
342
343/// Common checks for precompile and precompile sets.
344/// Don't contain recursion check as precompile sets have recursion check for each member.
345fn common_checks<R: pallet_evm::Config, C: PrecompileChecks>(
346	handle: &mut impl PrecompileHandle,
347) -> EvmResult<()> {
348	let code_address = handle.code_address();
349	let caller = handle.context().caller;
350
351	// Check DELEGATECALL config.
352	let accept_delegate_call = C::accept_delegate_call().unwrap_or(false);
353	if !accept_delegate_call && code_address != handle.context().address {
354		return Err(revert("Cannot be called with DELEGATECALL or CALLCODE"));
355	}
356
357	// Extract which selector is called.
358	let selector = handle.input().get(0..4).map(|bytes| {
359		let mut buffer = [0u8; 4];
360		buffer.copy_from_slice(bytes);
361		u32::from_be_bytes(buffer)
362	});
363
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		}
385	}
386
387	Ok(())
388}
389
390pub fn is_precompile_or_fail<R: pallet_evm::Config>(address: H160, gas: u64) -> EvmResult<bool> {
391	match <R as pallet_evm::Config>::PrecompilesValue::get().is_precompile(address, gas) {
392		IsPrecompileResult::Answer { is_precompile, .. } => Ok(is_precompile),
393		IsPrecompileResult::OutOfGas => Err(PrecompileFailure::Error {
394			exit_status: ExitError::OutOfGas,
395		}),
396	}
397}
398
399pub struct AddressU64<const N: u64>;
400impl<const N: u64> Get<H160> for AddressU64<N> {
401	#[inline(always)]
402	fn get() -> H160 {
403		H160::from_low_u64_be(N)
404	}
405}
406
407pub struct RestrictiveHandle<'a, H> {
408	handle: &'a mut H,
409	allow_subcalls: bool,
410}
411
412impl<H: PrecompileHandle> PrecompileHandle for RestrictiveHandle<'_, H> {
413	fn call(
414		&mut self,
415		address: H160,
416		transfer: Option<evm::Transfer>,
417		input: Vec<u8>,
418		target_gas: Option<u64>,
419		is_static: bool,
420		context: &evm::Context,
421	) -> (evm::ExitReason, Vec<u8>) {
422		if !self.allow_subcalls {
423			return (
424				evm::ExitReason::Revert(evm::ExitRevert::Reverted),
425				crate::solidity::revert::revert_as_bytes("subcalls disabled for this precompile"),
426			);
427		}
428
429		self.handle
430			.call(address, transfer, input, target_gas, is_static, context)
431	}
432
433	fn record_cost(&mut self, cost: u64) -> Result<(), evm::ExitError> {
434		self.handle.record_cost(cost)
435	}
436
437	fn remaining_gas(&self) -> u64 {
438		self.handle.remaining_gas()
439	}
440
441	fn log(
442		&mut self,
443		address: H160,
444		topics: Vec<H256>,
445		data: Vec<u8>,
446	) -> Result<(), evm::ExitError> {
447		self.handle.log(address, topics, data)
448	}
449
450	fn code_address(&self) -> H160 {
451		self.handle.code_address()
452	}
453
454	fn input(&self) -> &[u8] {
455		self.handle.input()
456	}
457
458	fn context(&self) -> &evm::Context {
459		self.handle.context()
460	}
461
462	fn origin(&self) -> H160 {
463		self.handle.origin()
464	}
465
466	fn is_static(&self) -> bool {
467		self.handle.is_static()
468	}
469
470	fn gas_limit(&self) -> Option<u64> {
471		self.handle.gas_limit()
472	}
473
474	fn record_external_cost(
475		&mut self,
476		ref_time: Option<u64>,
477		proof_size: Option<u64>,
478		storage_growth: Option<u64>,
479	) -> Result<(), ExitError> {
480		self.handle
481			.record_external_cost(ref_time, proof_size, storage_growth)
482	}
483
484	fn refund_external_cost(&mut self, ref_time: Option<u64>, proof_size: Option<u64>) {
485		self.handle.refund_external_cost(ref_time, proof_size)
486	}
487
488	fn is_contract_being_constructed(&self, address: H160) -> bool {
489		self.handle.is_contract_being_constructed(address)
490	}
491}
492
493/// Allows to know if a precompile is active or not.
494/// This allows to detect deactivated precompile, that are still considered precompiles by
495/// the EVM but that will always revert when called.
496pub trait IsActivePrecompile {
497	/// Is the provided address an active precompile, a precompile that has
498	/// not be deactivated. Note that a deactivated precompile is still considered a precompile
499	/// for the EVM, but it will always revert when called.
500	fn is_active_precompile(&self, address: H160, gas: u64) -> IsPrecompileResult;
501}
502
503// INDIVIDUAL PRECOMPILE(SET)
504
505/// A fragment of a PrecompileSet. Should be implemented as is it
506/// was a PrecompileSet containing only the precompile(set) it wraps.
507/// They can be combined into a real PrecompileSet using `PrecompileSetBuilder`.
508pub trait PrecompileSetFragment {
509	/// Instantiate the fragment.
510	fn new() -> Self;
511
512	/// Execute the fragment.
513	fn execute<R: pallet_evm::Config>(
514		&self,
515		handle: &mut impl PrecompileHandle,
516	) -> Option<PrecompileResult>;
517
518	/// Is the provided address a precompile in this fragment?
519	fn is_precompile(&self, address: H160, gas: u64) -> IsPrecompileResult;
520
521	/// Return the list of addresses covered by this fragment.
522	fn used_addresses(&self) -> Vec<H160>;
523
524	/// Summarize
525	fn summarize_checks(&self) -> Vec<PrecompileCheckSummary>;
526}
527
528/// Wraps a stateless precompile: a type implementing the `Precompile` trait.
529/// Type parameters allow to define:
530/// - A: The address of the precompile
531/// - R: The recursion limit (defaults to 1)
532/// - D: If DELEGATECALL is supported (default to no)
533pub struct PrecompileAt<A, P, C = ()> {
534	current_recursion_level: RefCell<u16>,
535	_phantom: PhantomData<(A, P, C)>,
536}
537
538impl<A, P, C> PrecompileSetFragment for PrecompileAt<A, P, C>
539where
540	A: Get<H160>,
541	P: Precompile,
542	C: PrecompileChecks,
543{
544	#[inline(always)]
545	fn new() -> Self {
546		Self {
547			current_recursion_level: RefCell::new(0),
548			_phantom: PhantomData,
549		}
550	}
551
552	#[inline(always)]
553	fn execute<R: pallet_evm::Config>(
554		&self,
555		handle: &mut impl PrecompileHandle,
556	) -> Option<PrecompileResult> {
557		let code_address = handle.code_address();
558
559		// Check if this is the address of the precompile.
560		if A::get() != code_address {
561			return None;
562		}
563
564		// Perform common checks.
565		if let Err(err) = common_checks::<R, C>(handle) {
566			return Some(Err(err));
567		}
568
569		// Check and increase recursion level if needed.
570		let recursion_limit = C::recursion_limit().unwrap_or(Some(0));
571		if let Some(max_recursion_level) = recursion_limit {
572			match self.current_recursion_level.try_borrow_mut() {
573				Ok(mut recursion_level) => {
574					if *recursion_level > max_recursion_level {
575						return Some(Err(revert("Precompile is called with too high nesting")));
576					}
577
578					*recursion_level += 1;
579				}
580				// We don't hold the borrow and are in single-threaded code, thus we should
581				// not be able to fail borrowing in nested calls.
582				Err(_) => return Some(Err(revert("Couldn't check precompile nesting"))),
583			}
584		}
585
586		// Subcall protection.
587		let allow_subcalls = C::allow_subcalls().unwrap_or(false);
588		let mut handle = RestrictiveHandle {
589			handle,
590			allow_subcalls,
591		};
592
593		let res = P::execute(&mut handle);
594
595		// Decrease recursion level if needed.
596		if recursion_limit.is_some() {
597			match self.current_recursion_level.try_borrow_mut() {
598				Ok(mut recursion_level) => {
599					*recursion_level -= 1;
600				}
601				// We don't hold the borrow and are in single-threaded code, thus we should
602				// not be able to fail borrowing in nested calls.
603				Err(_) => return Some(Err(revert("Couldn't check precompile nesting"))),
604			}
605		}
606
607		Some(res)
608	}
609
610	#[inline(always)]
611	fn is_precompile(&self, address: H160, _gas: u64) -> IsPrecompileResult {
612		IsPrecompileResult::Answer {
613			is_precompile: address == A::get(),
614			extra_cost: 0,
615		}
616	}
617
618	#[inline(always)]
619	fn used_addresses(&self) -> Vec<H160> {
620		vec![A::get()]
621	}
622
623	fn summarize_checks(&self) -> Vec<PrecompileCheckSummary> {
624		vec![PrecompileCheckSummary {
625			name: None,
626			precompile_kind: PrecompileKind::Single(A::get()),
627			recursion_limit: C::recursion_limit().unwrap_or(Some(0)),
628			accept_delegate_call: C::accept_delegate_call().unwrap_or(false),
629			callable_by_smart_contract: C::callable_by_smart_contract_summary()
630				.unwrap_or_else(|| "Not callable".into()),
631			callable_by_precompile: C::callable_by_precompile_summary()
632				.unwrap_or_else(|| "Not callable".into()),
633		}]
634	}
635}
636
637impl<A, P, C> IsActivePrecompile for PrecompileAt<A, P, C>
638where
639	A: Get<H160>,
640{
641	#[inline(always)]
642	fn is_active_precompile(&self, address: H160, _gas: u64) -> IsPrecompileResult {
643		IsPrecompileResult::Answer {
644			is_precompile: address == A::get(),
645			extra_cost: 0,
646		}
647	}
648}
649
650/// Wraps an inner PrecompileSet with all its addresses starting with
651/// a common prefix.
652/// Type parameters allow to define:
653/// - A: The common prefix
654/// - D: If DELEGATECALL is supported (default to no)
655pub struct PrecompileSetStartingWith<A, P, C = ()> {
656	precompile_set: P,
657	current_recursion_level: RefCell<BTreeMap<H160, u16>>,
658	_phantom: PhantomData<(A, C)>,
659}
660
661impl<A, P, C> PrecompileSetFragment for PrecompileSetStartingWith<A, P, C>
662where
663	A: Get<&'static [u8]>,
664	P: PrecompileSet + Default,
665	C: PrecompileChecks,
666{
667	#[inline(always)]
668	fn new() -> Self {
669		Self {
670			precompile_set: P::default(),
671			current_recursion_level: RefCell::new(BTreeMap::new()),
672			_phantom: PhantomData,
673		}
674	}
675
676	#[inline(always)]
677	fn execute<R: pallet_evm::Config>(
678		&self,
679		handle: &mut impl PrecompileHandle,
680	) -> Option<PrecompileResult> {
681		let code_address = handle.code_address();
682		if !is_precompile_or_fail::<R>(code_address, handle.remaining_gas()).ok()? {
683			return None;
684		}
685		// Perform common checks.
686		if let Err(err) = common_checks::<R, C>(handle) {
687			return Some(Err(err));
688		}
689
690		// Check and increase recursion level if needed.
691		let recursion_limit = C::recursion_limit().unwrap_or(Some(0));
692		if let Some(max_recursion_level) = recursion_limit {
693			match self.current_recursion_level.try_borrow_mut() {
694				Ok(mut recursion_level_map) => {
695					let recursion_level = recursion_level_map.entry(code_address).or_insert(0);
696
697					if *recursion_level > max_recursion_level {
698						return Some(Err(revert("Precompile is called with too high nesting")));
699					}
700
701					*recursion_level += 1;
702				}
703				// We don't hold the borrow and are in single-threaded code, thus we should
704				// not be able to fail borrowing in nested calls.
705				Err(_) => return Some(Err(revert("Couldn't check precompile nesting"))),
706			}
707		}
708
709		// Subcall protection.
710		let allow_subcalls = C::allow_subcalls().unwrap_or(false);
711		let mut handle = RestrictiveHandle {
712			handle,
713			allow_subcalls,
714		};
715
716		let res = self.precompile_set.execute(&mut handle);
717
718		// Decrease recursion level if needed.
719		if recursion_limit.is_some() {
720			match self.current_recursion_level.try_borrow_mut() {
721				Ok(mut recursion_level_map) => {
722					let recursion_level = match recursion_level_map.get_mut(&code_address) {
723						Some(recursion_level) => recursion_level,
724						None => return Some(Err(revert("Couldn't retrieve precompile nesting"))),
725					};
726
727					*recursion_level -= 1;
728				}
729				// We don't hold the borrow and are in single-threaded code, thus we should
730				// not be able to fail borrowing in nested calls.
731				Err(_) => return Some(Err(revert("Couldn't check precompile nesting"))),
732			}
733		}
734
735		res
736	}
737
738	#[inline(always)]
739	fn is_precompile(&self, address: H160, gas: u64) -> IsPrecompileResult {
740		if address.as_bytes().starts_with(A::get()) {
741			return self.precompile_set.is_precompile(address, gas);
742		}
743		IsPrecompileResult::Answer {
744			is_precompile: false,
745			extra_cost: 0,
746		}
747	}
748
749	#[inline(always)]
750	fn used_addresses(&self) -> Vec<H160> {
751		// TODO: We currently can't get the list of used addresses.
752		vec![]
753	}
754
755	fn summarize_checks(&self) -> Vec<PrecompileCheckSummary> {
756		let prefix = A::get();
757
758		vec![PrecompileCheckSummary {
759			name: None,
760			precompile_kind: PrecompileKind::Prefixed(prefix.to_vec()),
761			recursion_limit: C::recursion_limit().unwrap_or(Some(0)),
762			accept_delegate_call: C::accept_delegate_call().unwrap_or(false),
763			callable_by_smart_contract: C::callable_by_smart_contract_summary()
764				.unwrap_or_else(|| "Not callable".into()),
765			callable_by_precompile: C::callable_by_precompile_summary()
766				.unwrap_or_else(|| "Not callable".into()),
767		}]
768	}
769}
770
771impl<A, P, C> IsActivePrecompile for PrecompileSetStartingWith<A, P, C>
772where
773	Self: PrecompileSetFragment,
774{
775	#[inline(always)]
776	fn is_active_precompile(&self, address: H160, gas: u64) -> IsPrecompileResult {
777		self.is_precompile(address, gas)
778	}
779}
780
781/// Make a precompile that always revert.
782/// Can be useful when writing tests.
783pub struct RevertPrecompile<A>(PhantomData<A>);
784
785impl<A> PrecompileSetFragment for RevertPrecompile<A>
786where
787	A: Get<H160>,
788{
789	#[inline(always)]
790	fn new() -> Self {
791		Self(PhantomData)
792	}
793
794	#[inline(always)]
795	fn execute<R: pallet_evm::Config>(
796		&self,
797		handle: &mut impl PrecompileHandle,
798	) -> Option<PrecompileResult> {
799		if A::get() == handle.code_address() {
800			Some(Err(revert("revert")))
801		} else {
802			None
803		}
804	}
805
806	#[inline(always)]
807	fn is_precompile(&self, address: H160, _gas: u64) -> IsPrecompileResult {
808		IsPrecompileResult::Answer {
809			is_precompile: address == A::get(),
810			extra_cost: 0,
811		}
812	}
813
814	#[inline(always)]
815	fn used_addresses(&self) -> Vec<H160> {
816		vec![A::get()]
817	}
818
819	fn summarize_checks(&self) -> Vec<PrecompileCheckSummary> {
820		vec![PrecompileCheckSummary {
821			name: None,
822			precompile_kind: PrecompileKind::Single(A::get()),
823			recursion_limit: Some(0),
824			accept_delegate_call: true,
825			callable_by_smart_contract: "Reverts in all cases".into(),
826			callable_by_precompile: "Reverts in all cases".into(),
827		}]
828	}
829}
830
831impl<A> IsActivePrecompile for RevertPrecompile<A> {
832	#[inline(always)]
833	fn is_active_precompile(&self, _address: H160, _gas: u64) -> IsPrecompileResult {
834		IsPrecompileResult::Answer {
835			is_precompile: true,
836			extra_cost: 0,
837		}
838	}
839}
840
841/// Precompiles that were removed from a precompile set.
842/// Still considered precompiles but are inactive and always revert.
843pub struct RemovedPrecompilesAt<A>(PhantomData<A>);
844impl<A> PrecompileSetFragment for RemovedPrecompilesAt<A>
845where
846	A: Get<Vec<H160>>,
847{
848	#[inline(always)]
849	fn new() -> Self {
850		Self(PhantomData)
851	}
852
853	#[inline(always)]
854	fn execute<R: pallet_evm::Config>(
855		&self,
856		handle: &mut impl PrecompileHandle,
857	) -> Option<PrecompileResult> {
858		if A::get().contains(&handle.code_address()) {
859			Some(Err(revert("Removed precompile")))
860		} else {
861			None
862		}
863	}
864
865	#[inline(always)]
866	fn is_precompile(&self, address: H160, _gas: u64) -> IsPrecompileResult {
867		IsPrecompileResult::Answer {
868			is_precompile: A::get().contains(&address),
869			extra_cost: 0,
870		}
871	}
872
873	#[inline(always)]
874	fn used_addresses(&self) -> Vec<H160> {
875		A::get()
876	}
877
878	fn summarize_checks(&self) -> Vec<PrecompileCheckSummary> {
879		vec![PrecompileCheckSummary {
880			name: None,
881			precompile_kind: PrecompileKind::Multiple(A::get()),
882			recursion_limit: Some(0),
883			accept_delegate_call: true,
884			callable_by_smart_contract: "Reverts in all cases".into(),
885			callable_by_precompile: "Reverts in all cases".into(),
886		}]
887	}
888}
889
890impl<A> IsActivePrecompile for RemovedPrecompilesAt<A>
891where
892	Self: PrecompileSetFragment,
893{
894	#[inline(always)]
895	fn is_active_precompile(&self, _address: H160, _gas: u64) -> IsPrecompileResult {
896		IsPrecompileResult::Answer {
897			is_precompile: false,
898			extra_cost: 0,
899		}
900	}
901}
902
903/// A precompile that was removed from a precompile set.
904/// Still considered a precompile but is inactive and always revert.
905pub struct RemovedPrecompileAt<A>(PhantomData<A>);
906impl<A> PrecompileSetFragment for RemovedPrecompileAt<A>
907where
908	A: Get<H160>,
909{
910	#[inline(always)]
911	fn new() -> Self {
912		Self(PhantomData)
913	}
914
915	#[inline(always)]
916	fn execute<R: pallet_evm::Config>(
917		&self,
918		handle: &mut impl PrecompileHandle,
919	) -> Option<PrecompileResult> {
920		if A::get() == handle.code_address() {
921			Some(Err(revert("Removed precompile")))
922		} else {
923			None
924		}
925	}
926
927	#[inline(always)]
928	fn is_precompile(&self, address: H160, _gas: u64) -> IsPrecompileResult {
929		IsPrecompileResult::Answer {
930			is_precompile: address == A::get(),
931			extra_cost: 0,
932		}
933	}
934
935	#[inline(always)]
936	fn used_addresses(&self) -> Vec<H160> {
937		vec![A::get()]
938	}
939
940	fn summarize_checks(&self) -> Vec<PrecompileCheckSummary> {
941		vec![PrecompileCheckSummary {
942			name: None,
943			precompile_kind: PrecompileKind::Single(A::get()),
944			recursion_limit: Some(0),
945			accept_delegate_call: true,
946			callable_by_smart_contract: "Reverts in all cases".into(),
947			callable_by_precompile: "Reverts in all cases".into(),
948		}]
949	}
950}
951
952impl<A> IsActivePrecompile for RemovedPrecompileAt<A> {
953	#[inline(always)]
954	fn is_active_precompile(&self, _address: H160, _gas: u64) -> IsPrecompileResult {
955		IsPrecompileResult::Answer {
956			is_precompile: false,
957			extra_cost: 0,
958		}
959	}
960}
961
962// COMPOSITION OF PARTS
963#[impl_for_tuples(1, 100)]
964impl PrecompileSetFragment for Tuple {
965	#[inline(always)]
966	fn new() -> Self {
967		(for_tuples!(#(
968			Tuple::new()
969		),*))
970	}
971
972	#[inline(always)]
973	fn execute<R: pallet_evm::Config>(
974		&self,
975		handle: &mut impl PrecompileHandle,
976	) -> Option<PrecompileResult> {
977		for_tuples!(#(
978			if let Some(res) = self.Tuple.execute::<R>(handle) {
979				return Some(res);
980			}
981		)*);
982
983		None
984	}
985
986	#[inline(always)]
987	fn is_precompile(&self, address: H160, gas: u64) -> IsPrecompileResult {
988		for_tuples!(#(
989			if let IsPrecompileResult::Answer {
990			is_precompile: true,
991				..
992			} = self.Tuple.is_precompile(address, gas) { return IsPrecompileResult::Answer {
993				is_precompile: true,
994				extra_cost: 0,
995				}
996			};
997		)*);
998		IsPrecompileResult::Answer {
999			is_precompile: false,
1000			extra_cost: 0,
1001		}
1002	}
1003
1004	#[inline(always)]
1005	fn used_addresses(&self) -> Vec<H160> {
1006		let mut used_addresses = vec![];
1007
1008		for_tuples!(#(
1009			let mut inner = self.Tuple.used_addresses();
1010			used_addresses.append(&mut inner);
1011		)*);
1012
1013		used_addresses
1014	}
1015
1016	fn summarize_checks(&self) -> Vec<PrecompileCheckSummary> {
1017		let mut checks = Vec::new();
1018
1019		for_tuples!(#(
1020			let mut inner = self.Tuple.summarize_checks();
1021			checks.append(&mut inner);
1022		)*);
1023
1024		checks
1025	}
1026}
1027
1028#[impl_for_tuples(1, 100)]
1029impl IsActivePrecompile for Tuple {
1030	#[inline(always)]
1031	fn is_active_precompile(&self, address: H160, gas: u64) -> IsPrecompileResult {
1032		for_tuples!(#(
1033			if let IsPrecompileResult::Answer {
1034				is_precompile: true,
1035				..
1036			} = self.Tuple.is_active_precompile(address, gas) { return IsPrecompileResult::Answer {
1037				is_precompile: true,
1038				extra_cost: 0,
1039			} };
1040		)*);
1041		IsPrecompileResult::Answer {
1042			is_precompile: false,
1043			extra_cost: 0,
1044		}
1045	}
1046}
1047
1048/// Wraps a precompileset fragment into a range, and will skip processing it if the address
1049/// is out of the range.
1050pub struct PrecompilesInRangeInclusive<R, P> {
1051	inner: P,
1052	range: RangeInclusive<H160>,
1053	_phantom: PhantomData<R>,
1054}
1055
1056impl<S, E, P> PrecompileSetFragment for PrecompilesInRangeInclusive<(S, E), P>
1057where
1058	S: Get<H160>,
1059	E: Get<H160>,
1060	P: PrecompileSetFragment,
1061{
1062	fn new() -> Self {
1063		Self {
1064			inner: P::new(),
1065			range: RangeInclusive::new(S::get(), E::get()),
1066			_phantom: PhantomData,
1067		}
1068	}
1069
1070	fn execute<R: pallet_evm::Config>(
1071		&self,
1072		handle: &mut impl PrecompileHandle,
1073	) -> Option<PrecompileResult> {
1074		if self.range.contains(&handle.code_address()) {
1075			self.inner.execute::<R>(handle)
1076		} else {
1077			None
1078		}
1079	}
1080
1081	fn is_precompile(&self, address: H160, gas: u64) -> IsPrecompileResult {
1082		if self.range.contains(&address) {
1083			self.inner.is_precompile(address, gas)
1084		} else {
1085			IsPrecompileResult::Answer {
1086				is_precompile: false,
1087				extra_cost: 0,
1088			}
1089		}
1090	}
1091
1092	fn used_addresses(&self) -> Vec<H160> {
1093		self.inner.used_addresses()
1094	}
1095
1096	fn summarize_checks(&self) -> Vec<PrecompileCheckSummary> {
1097		self.inner.summarize_checks()
1098	}
1099}
1100
1101impl<S, E, P> IsActivePrecompile for PrecompilesInRangeInclusive<(S, E), P>
1102where
1103	P: IsActivePrecompile,
1104{
1105	fn is_active_precompile(&self, address: H160, gas: u64) -> IsPrecompileResult {
1106		if self.range.contains(&address) {
1107			self.inner.is_active_precompile(address, gas)
1108		} else {
1109			IsPrecompileResult::Answer {
1110				is_precompile: false,
1111				extra_cost: 0,
1112			}
1113		}
1114	}
1115}
1116
1117/// Wraps a tuple of `PrecompileSetFragment` to make a real `PrecompileSet`.
1118pub struct PrecompileSetBuilder<R, P> {
1119	inner: P,
1120	_phantom: PhantomData<R>,
1121}
1122
1123impl<R: pallet_evm::Config, P: PrecompileSetFragment> PrecompileSet for PrecompileSetBuilder<R, P> {
1124	fn execute(&self, handle: &mut impl PrecompileHandle) -> Option<PrecompileResult> {
1125		self.inner.execute::<R>(handle)
1126	}
1127
1128	fn is_precompile(&self, address: H160, gas: u64) -> IsPrecompileResult {
1129		self.inner.is_precompile(address, gas)
1130	}
1131}
1132
1133impl<R, P: IsActivePrecompile> IsActivePrecompile for PrecompileSetBuilder<R, P> {
1134	fn is_active_precompile(&self, address: H160, gas: u64) -> IsPrecompileResult {
1135		self.inner.is_active_precompile(address, gas)
1136	}
1137}
1138
1139impl<R: pallet_evm::Config, P: PrecompileSetFragment> Default for PrecompileSetBuilder<R, P> {
1140	fn default() -> Self {
1141		Self::new()
1142	}
1143}
1144
1145impl<R: pallet_evm::Config, P: PrecompileSetFragment> PrecompileSetBuilder<R, P> {
1146	/// Create a new instance of the PrecompileSet.
1147	pub fn new() -> Self {
1148		Self {
1149			inner: P::new(),
1150			_phantom: PhantomData,
1151		}
1152	}
1153
1154	/// Return the list of mapped addresses contained in this PrecompileSet.
1155	pub fn used_addresses() -> impl Iterator<Item = pallet_evm::AccountIdOf<R>> {
1156		Self::used_addresses_h160().map(R::AddressMapping::into_account_id)
1157	}
1158
1159	/// Return the list of H160 addresses contained in this PrecompileSet.
1160	pub fn used_addresses_h160() -> impl Iterator<Item = H160> {
1161		Self::new().inner.used_addresses().into_iter()
1162	}
1163
1164	pub fn summarize_checks(&self) -> Vec<PrecompileCheckSummary> {
1165		self.inner.summarize_checks()
1166	}
1167}