1mod builtin;
29
30mod tests;
31
32pub use crate::{
33 AddressMapper, TransactionLimits,
34 access_list::StorageOp,
35 exec::{
36 ExecError, PrecompileExt as Ext, PrecompileWithInfoExt as ExtWithInfo, ReentrancyProtection,
37 },
38 metering::{Diff, Token},
39 vm::RuntimeCosts,
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 message = match () {
149 _ if e == delegate_denied => "illegal to call this pre-compile via delegate call",
150 _ if e == construct => "terminate pre-compile cannot be called from the constructor",
151 _ => return e.into(),
152 };
153 Self::Revert(message.into())
154 }
155}
156
157pub trait Precompile {
166 type T: Config;
168 type Interface: SolInterface;
177 const MATCHER: AddressMatcher;
179 const HAS_CONTRACT_INFO: bool;
218
219 #[allow(unused_variables)]
221 fn call(
222 address: &[u8; 20],
223 input: &Self::Interface,
224 env: &mut impl Ext<T = Self::T>,
225 ) -> Result<Vec<u8>, Error> {
226 unimplemented!("{UNIMPLEMENTED}")
227 }
228
229 #[allow(unused_variables)]
231 fn call_with_info(
232 address: &[u8; 20],
233 input: &Self::Interface,
234 env: &mut impl ExtWithInfo<T = Self::T>,
235 ) -> Result<Vec<u8>, Error> {
236 unimplemented!("{UNIMPLEMENTED}")
237 }
238}
239
240pub(crate) trait BuiltinPrecompile {
247 type T: Config;
248 type Interface: SolInterface;
249 const MATCHER: BuiltinAddressMatcher;
250 const HAS_CONTRACT_INFO: bool;
251 const CODE: &[u8] = &EVM_REVERT;
252
253 fn call(
254 _address: &[u8; 20],
255 _input: &Self::Interface,
256 _env: &mut impl Ext<T = Self::T>,
257 ) -> Result<Vec<u8>, Error> {
258 unimplemented!("{UNIMPLEMENTED}")
259 }
260
261 fn call_with_info(
262 _address: &[u8; 20],
263 _input: &Self::Interface,
264 _env: &mut impl ExtWithInfo<T = Self::T>,
265 ) -> Result<Vec<u8>, Error> {
266 unimplemented!("{UNIMPLEMENTED}")
267 }
268}
269
270pub(crate) trait PrimitivePrecompile {
278 type T: Config;
279 const MATCHER: BuiltinAddressMatcher;
280 const HAS_CONTRACT_INFO: bool;
281 const CODE: &[u8] = &[];
282
283 fn call(
284 _address: &[u8; 20],
285 _input: Vec<u8>,
286 _env: &mut impl Ext<T = Self::T>,
287 ) -> Result<Vec<u8>, Error> {
288 unimplemented!("{UNIMPLEMENTED}")
289 }
290
291 fn call_with_info(
292 _address: &[u8; 20],
293 _input: Vec<u8>,
294 _env: &mut impl ExtWithInfo<T = Self::T>,
295 ) -> Result<Vec<u8>, Error> {
296 unimplemented!("{UNIMPLEMENTED}")
297 }
298}
299
300pub(crate) struct Instance<E> {
302 has_contract_info: bool,
303 address: [u8; 20],
304 function: fn(&[u8; 20], Vec<u8>, &mut E) -> Result<Vec<u8>, Error>,
306}
307
308impl<E> Instance<E> {
309 pub fn has_contract_info(&self) -> bool {
310 self.has_contract_info
311 }
312
313 pub fn call(&self, input: Vec<u8>, env: &mut E) -> ExecResult {
314 let result = (self.function)(&self.address, input, env);
315 match result {
316 Ok(data) => Ok(ExecReturnValue { flags: ReturnFlags::empty(), data }),
317 Err(Error::Revert(msg)) => {
318 Ok(ExecReturnValue { flags: ReturnFlags::REVERT, data: msg.abi_encode() })
319 },
320 Err(Error::Panic(kind)) => Ok(ExecReturnValue {
321 flags: ReturnFlags::REVERT,
322 data: Panic::from(kind).abi_encode(),
323 }),
324 Err(Error::Error(err)) => Err(err.into()),
325 }
326 }
327}
328
329pub(crate) trait Precompiles<T: Config> {
334 const CHECK_COLLISION: ();
336 const USES_EXTERNAL_RANGE: bool;
341
342 fn code(address: &[u8; 20]) -> Option<&'static [u8]>;
348
349 fn get<E: ExtWithInfo<T = T>>(address: &[u8; 20]) -> Option<Instance<E>>;
353}
354
355impl<P: Precompile> BuiltinPrecompile for P {
356 type T = <Self as Precompile>::T;
357 type Interface = <Self as Precompile>::Interface;
358 const MATCHER: BuiltinAddressMatcher = P::MATCHER.into_builtin();
359 const HAS_CONTRACT_INFO: bool = P::HAS_CONTRACT_INFO;
360
361 fn call(
362 address: &[u8; 20],
363 input: &Self::Interface,
364 env: &mut impl Ext<T = Self::T>,
365 ) -> Result<Vec<u8>, Error> {
366 Self::call(address, input, env)
367 }
368
369 fn call_with_info(
370 address: &[u8; 20],
371 input: &Self::Interface,
372 env: &mut impl ExtWithInfo<T = Self::T>,
373 ) -> Result<Vec<u8>, Error> {
374 Self::call_with_info(address, input, env)
375 }
376}
377
378impl<P: BuiltinPrecompile> PrimitivePrecompile for P {
379 type T = <Self as BuiltinPrecompile>::T;
380 const MATCHER: BuiltinAddressMatcher = P::MATCHER;
381 const HAS_CONTRACT_INFO: bool = P::HAS_CONTRACT_INFO;
382 const CODE: &[u8] = P::CODE;
383
384 fn call(
385 address: &[u8; 20],
386 input: Vec<u8>,
387 env: &mut impl Ext<T = Self::T>,
388 ) -> Result<Vec<u8>, Error> {
389 log::trace!(target: crate::LOG_TARGET, "pre-compile call at {:?} with {:x?}", address, input);
390 let call = <Self as BuiltinPrecompile>::Interface::abi_decode_validate(&input)
391 .map_err(|_| Error::Panic(PanicKind::ResourceError))?;
392 let res = <Self as BuiltinPrecompile>::call(address, &call, env);
393 log::trace!(target: crate::LOG_TARGET, "pre-compile call at {:?} result: {:x?}", address, res);
394 res
395 }
396
397 fn call_with_info(
398 address: &[u8; 20],
399 input: Vec<u8>,
400 env: &mut impl ExtWithInfo<T = Self::T>,
401 ) -> Result<Vec<u8>, Error> {
402 log::trace!(target: crate::LOG_TARGET, "pre-compile call_with_info at {:?} with {:x?}", address, input);
403 let call = <Self as BuiltinPrecompile>::Interface::abi_decode_validate(&input)
404 .map_err(|_| Error::Panic(PanicKind::ResourceError))?;
405 let res = <Self as BuiltinPrecompile>::call_with_info(address, &call, env);
406 log::trace!(target: crate::LOG_TARGET, "pre-compile call_with_info at {:?} result: {:x?}", address, res);
407 res
408 }
409}
410
411#[impl_trait_for_tuples::impl_for_tuples(20)]
413#[tuple_types_custom_trait_bound(PrimitivePrecompile<T=T>)]
414impl<T: Config> Precompiles<T> for Tuple {
415 const CHECK_COLLISION: () = {
416 let matchers = [for_tuples!( #( Tuple::MATCHER ),* )];
417 if BuiltinAddressMatcher::has_duplicates(&matchers) {
418 panic!("Precompiles with duplicate matcher detected")
419 }
420 for_tuples!(
421 #(
422 let is_fixed = Tuple::MATCHER.is_fixed();
423 let has_info = Tuple::HAS_CONTRACT_INFO;
424 assert!(is_fixed || !has_info, "Only fixed precompiles can have a contract info.");
425 )*
426 );
427 };
428 const USES_EXTERNAL_RANGE: bool = {
429 let mut uses_external = false;
430 for_tuples!(
431 #(
432 if Tuple::MATCHER.suffix() > u16::MAX as u32 {
433 uses_external = true;
434 }
435 )*
436 );
437 uses_external
438 };
439
440 fn code(address: &[u8; 20]) -> Option<&'static [u8]> {
441 for_tuples!(
442 #(
443 if Tuple::MATCHER.matches(address) {
444 return Some(Tuple::CODE)
445 }
446 )*
447 );
448 None
449 }
450
451 fn get<E: ExtWithInfo<T = T>>(address: &[u8; 20]) -> Option<Instance<E>> {
452 let _ = <Self as Precompiles<T>>::CHECK_COLLISION;
453 let mut instance: Option<Instance<E>> = None;
454 for_tuples!(
455 #(
456 if Tuple::MATCHER.matches(address) {
457 if Tuple::HAS_CONTRACT_INFO {
458 instance = Some(Instance {
459 address: *address,
460 has_contract_info: true,
461 function: Tuple::call_with_info,
462 })
463 } else {
464 instance = Some(Instance {
465 address: *address,
466 has_contract_info: false,
467 function: Tuple::call,
468 })
469 }
470 }
471 )*
472 );
473 instance
474 }
475}
476
477#[cfg(feature = "trybuild")]
479#[allow(private_bounds)]
480pub const fn check_collision_for<T: Config, Tuple: Precompiles<T>>() {
481 let _ = <Tuple as Precompiles<T>>::CHECK_COLLISION;
482}
483
484impl<T: Config> Precompiles<T> for (Builtin<T>, <T as Config>::Precompiles) {
485 const CHECK_COLLISION: () = {
486 assert!(
487 !<Builtin<T>>::USES_EXTERNAL_RANGE,
488 "Builtin precompiles must not use addresses reserved for external precompiles"
489 );
490 };
491 const USES_EXTERNAL_RANGE: bool = { <T as Config>::Precompiles::USES_EXTERNAL_RANGE };
492
493 fn code(address: &[u8; 20]) -> Option<&'static [u8]> {
494 <Builtin<T>>::code(address).or_else(|| <T as Config>::Precompiles::code(address))
495 }
496
497 fn get<E: ExtWithInfo<T = T>>(address: &[u8; 20]) -> Option<Instance<E>> {
498 let _ = <Self as Precompiles<T>>::CHECK_COLLISION;
499 <Builtin<T>>::get(address).or_else(|| <T as Config>::Precompiles::get(address))
500 }
501}
502
503impl AddressMatcher {
504 pub const fn base_address(&self) -> [u8; 20] {
505 self.into_builtin().base_address()
506 }
507
508 pub const fn highest_address(&self) -> [u8; 20] {
509 self.into_builtin().highest_address()
510 }
511
512 pub const fn matches(&self, address: &[u8; 20]) -> bool {
513 self.into_builtin().matches(address)
514 }
515
516 const fn into_builtin(&self) -> BuiltinAddressMatcher {
517 const fn left_shift(val: NonZero<u16>) -> NonZero<u32> {
518 let shifted = (val.get() as u32) << 16;
519 NonZero::new(shifted).expect(
520 "Value was non zero before.
521 The shift is small enough to not truncate any existing bits.
522 Hence the value is still non zero; qed",
523 )
524 }
525
526 match self {
527 Self::Fixed(i) => BuiltinAddressMatcher::Fixed(left_shift(*i)),
528 Self::Prefix(i) => BuiltinAddressMatcher::Prefix(left_shift(*i)),
529 }
530 }
531}
532
533impl BuiltinAddressMatcher {
534 pub const fn base_address(&self) -> [u8; 20] {
535 let suffix = self.suffix().to_be_bytes();
536 let mut address = [0u8; 20];
537 let mut i = 16;
538 while i < address.len() {
539 address[i] = suffix[i - 16];
540 i = i + 1;
541 }
542 address
543 }
544
545 pub const fn highest_address(&self) -> [u8; 20] {
546 let mut address = self.base_address();
547 match self {
548 Self::Fixed(_) => (),
549 Self::Prefix(_) => {
550 address[0] = 0xFF;
551 address[1] = 0xFF;
552 address[2] = 0xFF;
553 address[3] = 0xFF;
554 },
555 }
556 address
557 }
558
559 pub const fn matches(&self, address: &[u8; 20]) -> bool {
560 let base_address = self.base_address();
561 let mut i = match self {
562 Self::Fixed(_) => 0,
563 Self::Prefix(_) => 4,
564 };
565 while i < base_address.len() {
566 if address[i] != base_address[i] {
567 return false;
568 }
569 i = i + 1;
570 }
571 true
572 }
573
574 const fn suffix(&self) -> u32 {
575 match self {
576 Self::Fixed(i) => i.get(),
577 Self::Prefix(i) => i.get(),
578 }
579 }
580
581 const fn has_duplicates(nums: &[Self]) -> bool {
582 let len = nums.len();
583 let mut i = 0;
584 while i < len {
585 let mut j = i + 1;
586 while j < len {
587 if nums[i].suffix() == nums[j].suffix() {
588 return true;
589 }
590 j += 1;
591 }
592 i += 1;
593 }
594 false
595 }
596
597 const fn is_fixed(&self) -> bool {
598 matches!(self, Self::Fixed(_))
599 }
600}
601
602#[cfg(any(test, feature = "runtime-benchmarks"))]
608pub mod run {
609 pub use crate::{
610 BalanceOf, MomentOf,
611 call_builder::{CallSetup, Contract, VmBinaryModule},
612 };
613 pub use sp_core::{H256, U256};
614
615 use super::*;
616
617 pub fn precompile<P, E>(
622 ext: &mut E,
623 address: &[u8; 20],
624 input: &P::Interface,
625 ) -> Result<Vec<u8>, Error>
626 where
627 P: Precompile<T = E::T>,
628 E: ExtWithInfo,
629 {
630 assert!(P::MATCHER.into_builtin().matches(address));
631 if P::HAS_CONTRACT_INFO {
632 P::call_with_info(address, input, ext)
633 } else {
634 P::call(address, input, ext)
635 }
636 }
637
638 #[cfg(feature = "runtime-benchmarks")]
640 pub(crate) fn builtin<E>(ext: &mut E, address: &[u8; 20], input: Vec<u8>) -> ExecResult
641 where
642 E: ExtWithInfo,
643 {
644 let precompile = <Builtin<E::T>>::get(address)
645 .ok_or(DispatchError::from("No pre-compile at address"))
646 .inspect_err(|_| {
647 log::debug!(target: crate::LOG_TARGET, "No pre-compile at address {address:?}");
648 })?;
649 precompile.call(input, ext)
650 }
651}