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