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