1mod builtin;
29
30mod tests;
31
32pub use crate::{
33 AddressMapper, TransactionLimits,
34 access_list::{StorageOp, Warmth},
35 exec::{
36 ExecError, PrecompileExt as Ext, PrecompileWithInfoExt as ExtWithInfo, ReentrancyProtection,
37 },
38 metering::{Diff, Token},
39 vm::{RuntimeCosts, StorageAccessKind},
40};
41pub use alloy_core as alloy;
42pub use sp_core::{H160, H256, U256};
43
44use crate::{
45 Config, Error as CrateError, exec::ExecResult, precompiles::builtin::Builtin,
46 primitives::ExecReturnValue,
47};
48use alloc::vec::Vec;
49use alloy::sol_types::{Panic, PanicKind, Revert, SolError, SolInterface};
50use core::num::NonZero;
51use pallet_revive_uapi::ReturnFlags;
52use sp_runtime::DispatchError;
53
54#[cfg(feature = "runtime-benchmarks")]
55pub(crate) use builtin::{
56 IBenchmarking, NoInfo as BenchmarkNoInfo, Storage as BenchmarkStorage,
57 System as BenchmarkSystem, WithInfo as BenchmarkWithInfo,
58};
59
60const UNIMPLEMENTED: &str = "A precompile must either implement `call` or `call_with_info`";
61
62pub(crate) const EVM_REVERT: [u8; 5] = sp_core::hex2array!("60006000fd");
64
65pub(crate) type All<T> = (Builtin<T>, <T as Config>::Precompiles);
69
70pub enum AddressMatcher {
80 Fixed(NonZero<u16>),
89 Prefix(NonZero<u16>),
102}
103
104pub(crate) enum BuiltinAddressMatcher {
110 Fixed(NonZero<u32>),
111 Prefix(NonZero<u32>),
112}
113
114#[derive(derive_more::From, Debug, Eq, PartialEq)]
116pub enum Error {
117 Revert(Revert),
122 Panic(PanicKind),
126 Error(ExecError),
130}
131
132impl From<DispatchError> for Error {
133 fn from(error: DispatchError) -> Self {
134 Self::Error(error.into())
135 }
136}
137
138impl<T: Config> From<CrateError<T>> for Error {
139 fn from(error: CrateError<T>) -> Self {
140 Self::Error(DispatchError::from(error).into())
141 }
142}
143
144impl Error {
145 pub fn try_to_revert<T: Config>(e: DispatchError) -> Self {
146 let delegate_denied = CrateError::<T>::PrecompileDelegateDenied.into();
147 let construct = CrateError::<T>::TerminatedInConstructor.into();
148 let cannot_terminate_delegated = CrateError::<T>::CannotTerminateDelegatedAccount.into();
149 let message = match () {
150 _ if e == delegate_denied => "illegal to call this pre-compile via delegate call",
151 _ if e == construct => "terminate pre-compile cannot be called from the constructor",
152 _ if e == cannot_terminate_delegated => {
153 "cannot terminate an EIP-7702 delegated account via the terminate pre-compile"
154 },
155 _ => return e.into(),
156 };
157 Self::Revert(message.into())
158 }
159}
160
161pub trait Precompile {
170 type T: Config;
172 type Interface: SolInterface;
181 const MATCHER: AddressMatcher;
183 const HAS_CONTRACT_INFO: bool;
222
223 #[allow(unused_variables)]
225 fn call(
226 address: &[u8; 20],
227 input: &Self::Interface,
228 env: &mut impl Ext<T = Self::T>,
229 ) -> Result<Vec<u8>, Error> {
230 unimplemented!("{UNIMPLEMENTED}")
231 }
232
233 #[allow(unused_variables)]
235 fn call_with_info(
236 address: &[u8; 20],
237 input: &Self::Interface,
238 env: &mut impl ExtWithInfo<T = Self::T>,
239 ) -> Result<Vec<u8>, Error> {
240 unimplemented!("{UNIMPLEMENTED}")
241 }
242}
243
244pub(crate) trait BuiltinPrecompile {
251 type T: Config;
252 type Interface: SolInterface;
253 const MATCHER: BuiltinAddressMatcher;
254 const HAS_CONTRACT_INFO: bool;
255 const CODE: &[u8] = &EVM_REVERT;
256
257 fn call(
258 _address: &[u8; 20],
259 _input: &Self::Interface,
260 _env: &mut impl Ext<T = Self::T>,
261 ) -> Result<Vec<u8>, Error> {
262 unimplemented!("{UNIMPLEMENTED}")
263 }
264
265 fn call_with_info(
266 _address: &[u8; 20],
267 _input: &Self::Interface,
268 _env: &mut impl ExtWithInfo<T = Self::T>,
269 ) -> Result<Vec<u8>, Error> {
270 unimplemented!("{UNIMPLEMENTED}")
271 }
272}
273
274pub(crate) trait PrimitivePrecompile {
282 type T: Config;
283 const MATCHER: BuiltinAddressMatcher;
284 const HAS_CONTRACT_INFO: bool;
285 const CODE: &[u8] = &[];
286
287 fn call(
288 _address: &[u8; 20],
289 _input: Vec<u8>,
290 _env: &mut impl Ext<T = Self::T>,
291 ) -> Result<Vec<u8>, Error> {
292 unimplemented!("{UNIMPLEMENTED}")
293 }
294
295 fn call_with_info(
296 _address: &[u8; 20],
297 _input: Vec<u8>,
298 _env: &mut impl ExtWithInfo<T = Self::T>,
299 ) -> Result<Vec<u8>, Error> {
300 unimplemented!("{UNIMPLEMENTED}")
301 }
302}
303
304pub(crate) struct Instance<E> {
306 has_contract_info: bool,
307 address: [u8; 20],
308 function: fn(&[u8; 20], Vec<u8>, &mut E) -> Result<Vec<u8>, Error>,
310}
311
312impl<E> Instance<E> {
313 pub fn has_contract_info(&self) -> bool {
314 self.has_contract_info
315 }
316
317 pub fn call(&self, input: Vec<u8>, env: &mut E) -> ExecResult {
318 let result = (self.function)(&self.address, input, env);
319 match result {
320 Ok(data) => Ok(ExecReturnValue { flags: ReturnFlags::empty(), data }),
321 Err(Error::Revert(msg)) => {
322 Ok(ExecReturnValue { flags: ReturnFlags::REVERT, data: msg.abi_encode() })
323 },
324 Err(Error::Panic(kind)) => Ok(ExecReturnValue {
325 flags: ReturnFlags::REVERT,
326 data: Panic::from(kind).abi_encode(),
327 }),
328 Err(Error::Error(err)) => Err(err.into()),
329 }
330 }
331}
332
333pub(crate) trait Precompiles<T: Config> {
338 const CHECK_COLLISION: ();
340 const USES_EXTERNAL_RANGE: bool;
345
346 fn code(address: &[u8; 20]) -> Option<&'static [u8]>;
352
353 fn get<E: ExtWithInfo<T = T>>(address: &[u8; 20]) -> Option<Instance<E>>;
357}
358
359impl<P: Precompile> BuiltinPrecompile for P {
360 type T = <Self as Precompile>::T;
361 type Interface = <Self as Precompile>::Interface;
362 const MATCHER: BuiltinAddressMatcher = P::MATCHER.into_builtin();
363 const HAS_CONTRACT_INFO: bool = P::HAS_CONTRACT_INFO;
364
365 fn call(
366 address: &[u8; 20],
367 input: &Self::Interface,
368 env: &mut impl Ext<T = Self::T>,
369 ) -> Result<Vec<u8>, Error> {
370 Self::call(address, input, env)
371 }
372
373 fn call_with_info(
374 address: &[u8; 20],
375 input: &Self::Interface,
376 env: &mut impl ExtWithInfo<T = Self::T>,
377 ) -> Result<Vec<u8>, Error> {
378 Self::call_with_info(address, input, env)
379 }
380}
381
382impl<P: BuiltinPrecompile> PrimitivePrecompile for P {
383 type T = <Self as BuiltinPrecompile>::T;
384 const MATCHER: BuiltinAddressMatcher = P::MATCHER;
385 const HAS_CONTRACT_INFO: bool = P::HAS_CONTRACT_INFO;
386 const CODE: &[u8] = P::CODE;
387
388 fn call(
389 address: &[u8; 20],
390 input: Vec<u8>,
391 env: &mut impl Ext<T = Self::T>,
392 ) -> Result<Vec<u8>, Error> {
393 log::trace!(target: crate::LOG_TARGET, "pre-compile call at {:?} with {:x?}", address, input);
394 let call = <Self as BuiltinPrecompile>::Interface::abi_decode_validate(&input)
395 .map_err(|_| Error::Panic(PanicKind::ResourceError))?;
396 let res = <Self as BuiltinPrecompile>::call(address, &call, env);
397 log::trace!(target: crate::LOG_TARGET, "pre-compile call at {:?} result: {:x?}", address, res);
398 res
399 }
400
401 fn call_with_info(
402 address: &[u8; 20],
403 input: Vec<u8>,
404 env: &mut impl ExtWithInfo<T = Self::T>,
405 ) -> Result<Vec<u8>, Error> {
406 log::trace!(target: crate::LOG_TARGET, "pre-compile call_with_info at {:?} with {:x?}", address, input);
407 let call = <Self as BuiltinPrecompile>::Interface::abi_decode_validate(&input)
408 .map_err(|_| Error::Panic(PanicKind::ResourceError))?;
409 let res = <Self as BuiltinPrecompile>::call_with_info(address, &call, env);
410 log::trace!(target: crate::LOG_TARGET, "pre-compile call_with_info at {:?} result: {:x?}", address, res);
411 res
412 }
413}
414
415#[impl_trait_for_tuples::impl_for_tuples(20)]
417#[tuple_types_custom_trait_bound(PrimitivePrecompile<T=T>)]
418impl<T: Config> Precompiles<T> for Tuple {
419 const CHECK_COLLISION: () = {
420 let matchers = [for_tuples!( #( Tuple::MATCHER ),* )];
421 if BuiltinAddressMatcher::has_duplicates(&matchers) {
422 panic!("Precompiles with duplicate matcher detected")
423 }
424 for_tuples!(
425 #(
426 let is_fixed = Tuple::MATCHER.is_fixed();
427 let has_info = Tuple::HAS_CONTRACT_INFO;
428 assert!(is_fixed || !has_info, "Only fixed precompiles can have a contract info.");
429 )*
430 );
431 };
432 const USES_EXTERNAL_RANGE: bool = {
433 let mut uses_external = false;
434 for_tuples!(
435 #(
436 if Tuple::MATCHER.suffix() > u16::MAX as u32 {
437 uses_external = true;
438 }
439 )*
440 );
441 uses_external
442 };
443
444 fn code(address: &[u8; 20]) -> Option<&'static [u8]> {
445 for_tuples!(
446 #(
447 if Tuple::MATCHER.matches(address) {
448 return Some(Tuple::CODE)
449 }
450 )*
451 );
452 None
453 }
454
455 fn get<E: ExtWithInfo<T = T>>(address: &[u8; 20]) -> Option<Instance<E>> {
456 let _ = <Self as Precompiles<T>>::CHECK_COLLISION;
457 let mut instance: Option<Instance<E>> = None;
458 for_tuples!(
459 #(
460 if Tuple::MATCHER.matches(address) {
461 if Tuple::HAS_CONTRACT_INFO {
462 instance = Some(Instance {
463 address: *address,
464 has_contract_info: true,
465 function: Tuple::call_with_info,
466 })
467 } else {
468 instance = Some(Instance {
469 address: *address,
470 has_contract_info: false,
471 function: Tuple::call,
472 })
473 }
474 }
475 )*
476 );
477 instance
478 }
479}
480
481#[cfg(feature = "trybuild")]
483#[allow(private_bounds)]
484pub const fn check_collision_for<T: Config, Tuple: Precompiles<T>>() {
485 let _ = <Tuple as Precompiles<T>>::CHECK_COLLISION;
486}
487
488impl<T: Config> Precompiles<T> for (Builtin<T>, <T as Config>::Precompiles) {
489 const CHECK_COLLISION: () = {
490 assert!(
491 !<Builtin<T>>::USES_EXTERNAL_RANGE,
492 "Builtin precompiles must not use addresses reserved for external precompiles"
493 );
494 };
495 const USES_EXTERNAL_RANGE: bool = { <T as Config>::Precompiles::USES_EXTERNAL_RANGE };
496
497 fn code(address: &[u8; 20]) -> Option<&'static [u8]> {
498 <Builtin<T>>::code(address).or_else(|| <T as Config>::Precompiles::code(address))
499 }
500
501 fn get<E: ExtWithInfo<T = T>>(address: &[u8; 20]) -> Option<Instance<E>> {
502 let _ = <Self as Precompiles<T>>::CHECK_COLLISION;
503 <Builtin<T>>::get(address).or_else(|| <T as Config>::Precompiles::get(address))
504 }
505}
506
507impl AddressMatcher {
508 pub const fn base_address(&self) -> [u8; 20] {
509 self.into_builtin().base_address()
510 }
511
512 pub const fn highest_address(&self) -> [u8; 20] {
513 self.into_builtin().highest_address()
514 }
515
516 pub const fn matches(&self, address: &[u8; 20]) -> bool {
517 self.into_builtin().matches(address)
518 }
519
520 const fn into_builtin(&self) -> BuiltinAddressMatcher {
521 const fn left_shift(val: NonZero<u16>) -> NonZero<u32> {
522 let shifted = (val.get() as u32) << 16;
523 NonZero::new(shifted).expect(
524 "Value was non zero before.
525 The shift is small enough to not truncate any existing bits.
526 Hence the value is still non zero; qed",
527 )
528 }
529
530 match self {
531 Self::Fixed(i) => BuiltinAddressMatcher::Fixed(left_shift(*i)),
532 Self::Prefix(i) => BuiltinAddressMatcher::Prefix(left_shift(*i)),
533 }
534 }
535}
536
537impl BuiltinAddressMatcher {
538 pub const fn base_address(&self) -> [u8; 20] {
539 let suffix = self.suffix().to_be_bytes();
540 let mut address = [0u8; 20];
541 let mut i = 16;
542 while i < address.len() {
543 address[i] = suffix[i - 16];
544 i = i + 1;
545 }
546 address
547 }
548
549 pub const fn highest_address(&self) -> [u8; 20] {
550 let mut address = self.base_address();
551 match self {
552 Self::Fixed(_) => (),
553 Self::Prefix(_) => {
554 address[0] = 0xFF;
555 address[1] = 0xFF;
556 address[2] = 0xFF;
557 address[3] = 0xFF;
558 },
559 }
560 address
561 }
562
563 pub const fn matches(&self, address: &[u8; 20]) -> bool {
564 let base_address = self.base_address();
565 let mut i = match self {
566 Self::Fixed(_) => 0,
567 Self::Prefix(_) => 4,
568 };
569 while i < base_address.len() {
570 if address[i] != base_address[i] {
571 return false;
572 }
573 i = i + 1;
574 }
575 true
576 }
577
578 const fn suffix(&self) -> u32 {
579 match self {
580 Self::Fixed(i) => i.get(),
581 Self::Prefix(i) => i.get(),
582 }
583 }
584
585 const fn has_duplicates(nums: &[Self]) -> bool {
586 let len = nums.len();
587 let mut i = 0;
588 while i < len {
589 let mut j = i + 1;
590 while j < len {
591 if nums[i].suffix() == nums[j].suffix() {
592 return true;
593 }
594 j += 1;
595 }
596 i += 1;
597 }
598 false
599 }
600
601 const fn is_fixed(&self) -> bool {
602 matches!(self, Self::Fixed(_))
603 }
604}
605
606#[cfg(any(test, feature = "runtime-benchmarks"))]
612pub mod run {
613 pub use crate::{
614 BalanceOf, MomentOf,
615 call_builder::{CallSetup, Contract, VmBinaryModule},
616 };
617 pub use sp_core::{H256, U256};
618
619 use super::*;
620
621 pub fn precompile<P, E>(
626 ext: &mut E,
627 address: &[u8; 20],
628 input: &P::Interface,
629 ) -> Result<Vec<u8>, Error>
630 where
631 P: Precompile<T = E::T>,
632 E: ExtWithInfo,
633 {
634 assert!(P::MATCHER.into_builtin().matches(address));
635 if P::HAS_CONTRACT_INFO {
636 P::call_with_info(address, input, ext)
637 } else {
638 P::call(address, input, ext)
639 }
640 }
641
642 #[cfg(feature = "runtime-benchmarks")]
644 pub(crate) fn builtin<E>(ext: &mut E, address: &[u8; 20], input: Vec<u8>) -> ExecResult
645 where
646 E: ExtWithInfo,
647 {
648 let precompile = <Builtin<E::T>>::get(address)
649 .ok_or(DispatchError::from("No pre-compile at address"))
650 .inspect_err(|_| {
651 log::debug!(target: crate::LOG_TARGET, "No pre-compile at address {address:?}");
652 })?;
653 precompile.call(input, ext)
654 }
655}