1mod builtin;
29
30mod tests;
31
32pub use crate::{
33 exec::{ExecError, PrecompileExt as Ext, PrecompileWithInfoExt as ExtWithInfo},
34 metering::{Diff, Token},
35 vm::RuntimeCosts,
36 AddressMapper, TransactionLimits,
37};
38pub use alloy_core as alloy;
39pub use sp_core::{H160, H256, U256};
40
41use crate::{
42 exec::ExecResult, precompiles::builtin::Builtin, primitives::ExecReturnValue, Config,
43 Error as CrateError,
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 Err(Error::Panic(kind)) => Ok(ExecReturnValue {
317 flags: ReturnFlags::REVERT,
318 data: Panic::from(kind).abi_encode(),
319 }),
320 Err(Error::Error(err)) => Err(err.into()),
321 }
322 }
323}
324
325pub(crate) trait Precompiles<T: Config> {
330 const CHECK_COLLISION: ();
332 const USES_EXTERNAL_RANGE: bool;
337
338 fn code(address: &[u8; 20]) -> Option<&'static [u8]>;
344
345 fn get<E: ExtWithInfo<T = T>>(address: &[u8; 20]) -> Option<Instance<E>>;
349}
350
351impl<P: Precompile> BuiltinPrecompile for P {
352 type T = <Self as Precompile>::T;
353 type Interface = <Self as Precompile>::Interface;
354 const MATCHER: BuiltinAddressMatcher = P::MATCHER.into_builtin();
355 const HAS_CONTRACT_INFO: bool = P::HAS_CONTRACT_INFO;
356
357 fn call(
358 address: &[u8; 20],
359 input: &Self::Interface,
360 env: &mut impl Ext<T = Self::T>,
361 ) -> Result<Vec<u8>, Error> {
362 Self::call(address, input, env)
363 }
364
365 fn call_with_info(
366 address: &[u8; 20],
367 input: &Self::Interface,
368 env: &mut impl ExtWithInfo<T = Self::T>,
369 ) -> Result<Vec<u8>, Error> {
370 Self::call_with_info(address, input, env)
371 }
372}
373
374impl<P: BuiltinPrecompile> PrimitivePrecompile for P {
375 type T = <Self as BuiltinPrecompile>::T;
376 const MATCHER: BuiltinAddressMatcher = P::MATCHER;
377 const HAS_CONTRACT_INFO: bool = P::HAS_CONTRACT_INFO;
378 const CODE: &[u8] = P::CODE;
379
380 fn call(
381 address: &[u8; 20],
382 input: Vec<u8>,
383 env: &mut impl Ext<T = Self::T>,
384 ) -> Result<Vec<u8>, Error> {
385 log::trace!(target: crate::LOG_TARGET, "pre-compile call at {:?} with {:x?}", address, input);
386 let call = <Self as BuiltinPrecompile>::Interface::abi_decode_validate(&input)
387 .map_err(|_| Error::Panic(PanicKind::ResourceError))?;
388 let res = <Self as BuiltinPrecompile>::call(address, &call, env);
389 log::trace!(target: crate::LOG_TARGET, "pre-compile call at {:?} result: {:x?}", address, res);
390 res
391 }
392
393 fn call_with_info(
394 address: &[u8; 20],
395 input: Vec<u8>,
396 env: &mut impl ExtWithInfo<T = Self::T>,
397 ) -> Result<Vec<u8>, Error> {
398 log::trace!(target: crate::LOG_TARGET, "pre-compile call_with_info at {:?} with {:x?}", address, input);
399 let call = <Self as BuiltinPrecompile>::Interface::abi_decode_validate(&input)
400 .map_err(|_| Error::Panic(PanicKind::ResourceError))?;
401 let res = <Self as BuiltinPrecompile>::call_with_info(address, &call, env);
402 log::trace!(target: crate::LOG_TARGET, "pre-compile call_with_info at {:?} result: {:x?}", address, res);
403 res
404 }
405}
406
407#[impl_trait_for_tuples::impl_for_tuples(20)]
408#[tuple_types_custom_trait_bound(PrimitivePrecompile<T=T>)]
409impl<T: Config> Precompiles<T> for Tuple {
410 const CHECK_COLLISION: () = {
411 let matchers = [for_tuples!( #( Tuple::MATCHER ),* )];
412 if BuiltinAddressMatcher::has_duplicates(&matchers) {
413 panic!("Precompiles with duplicate matcher detected")
414 }
415 for_tuples!(
416 #(
417 let is_fixed = Tuple::MATCHER.is_fixed();
418 let has_info = Tuple::HAS_CONTRACT_INFO;
419 assert!(is_fixed || !has_info, "Only fixed precompiles can have a contract info.");
420 )*
421 );
422 };
423 const USES_EXTERNAL_RANGE: bool = {
424 let mut uses_external = false;
425 for_tuples!(
426 #(
427 if Tuple::MATCHER.suffix() > u16::MAX as u32 {
428 uses_external = true;
429 }
430 )*
431 );
432 uses_external
433 };
434
435 fn code(address: &[u8; 20]) -> Option<&'static [u8]> {
436 for_tuples!(
437 #(
438 if Tuple::MATCHER.matches(address) {
439 return Some(Tuple::CODE)
440 }
441 )*
442 );
443 None
444 }
445
446 fn get<E: ExtWithInfo<T = T>>(address: &[u8; 20]) -> Option<Instance<E>> {
447 let _ = <Self as Precompiles<T>>::CHECK_COLLISION;
448 let mut instance: Option<Instance<E>> = None;
449 for_tuples!(
450 #(
451 if Tuple::MATCHER.matches(address) {
452 if Tuple::HAS_CONTRACT_INFO {
453 instance = Some(Instance {
454 address: *address,
455 has_contract_info: true,
456 function: Tuple::call_with_info,
457 })
458 } else {
459 instance = Some(Instance {
460 address: *address,
461 has_contract_info: false,
462 function: Tuple::call,
463 })
464 }
465 }
466 )*
467 );
468 instance
469 }
470}
471
472impl<T: Config> Precompiles<T> for (Builtin<T>, <T as Config>::Precompiles) {
473 const CHECK_COLLISION: () = {
474 assert!(
475 !<Builtin<T>>::USES_EXTERNAL_RANGE,
476 "Builtin precompiles must not use addresses reserved for external precompiles"
477 );
478 };
479 const USES_EXTERNAL_RANGE: bool = { <T as Config>::Precompiles::USES_EXTERNAL_RANGE };
480
481 fn code(address: &[u8; 20]) -> Option<&'static [u8]> {
482 <Builtin<T>>::code(address).or_else(|| <T as Config>::Precompiles::code(address))
483 }
484
485 fn get<E: ExtWithInfo<T = T>>(address: &[u8; 20]) -> Option<Instance<E>> {
486 let _ = <Self as Precompiles<T>>::CHECK_COLLISION;
487 <Builtin<T>>::get(address).or_else(|| <T as Config>::Precompiles::get(address))
488 }
489}
490
491impl AddressMatcher {
492 pub const fn base_address(&self) -> [u8; 20] {
493 self.into_builtin().base_address()
494 }
495
496 pub const fn highest_address(&self) -> [u8; 20] {
497 self.into_builtin().highest_address()
498 }
499
500 pub const fn matches(&self, address: &[u8; 20]) -> bool {
501 self.into_builtin().matches(address)
502 }
503
504 const fn into_builtin(&self) -> BuiltinAddressMatcher {
505 const fn left_shift(val: NonZero<u16>) -> NonZero<u32> {
506 let shifted = (val.get() as u32) << 16;
507 NonZero::new(shifted).expect(
508 "Value was non zero before.
509 The shift is small enough to not truncate any existing bits.
510 Hence the value is still non zero; qed",
511 )
512 }
513
514 match self {
515 Self::Fixed(i) => BuiltinAddressMatcher::Fixed(left_shift(*i)),
516 Self::Prefix(i) => BuiltinAddressMatcher::Prefix(left_shift(*i)),
517 }
518 }
519}
520
521impl BuiltinAddressMatcher {
522 pub const fn base_address(&self) -> [u8; 20] {
523 let suffix = self.suffix().to_be_bytes();
524 let mut address = [0u8; 20];
525 let mut i = 16;
526 while i < address.len() {
527 address[i] = suffix[i - 16];
528 i = i + 1;
529 }
530 address
531 }
532
533 pub const fn highest_address(&self) -> [u8; 20] {
534 let mut address = self.base_address();
535 match self {
536 Self::Fixed(_) => (),
537 Self::Prefix(_) => {
538 address[0] = 0xFF;
539 address[1] = 0xFF;
540 address[2] = 0xFF;
541 address[3] = 0xFF;
542 },
543 }
544 address
545 }
546
547 pub const fn matches(&self, address: &[u8; 20]) -> bool {
548 let base_address = self.base_address();
549 let mut i = match self {
550 Self::Fixed(_) => 0,
551 Self::Prefix(_) => 4,
552 };
553 while i < base_address.len() {
554 if address[i] != base_address[i] {
555 return false
556 }
557 i = i + 1;
558 }
559 true
560 }
561
562 const fn suffix(&self) -> u32 {
563 match self {
564 Self::Fixed(i) => i.get(),
565 Self::Prefix(i) => i.get(),
566 }
567 }
568
569 const fn has_duplicates(nums: &[Self]) -> bool {
570 let len = nums.len();
571 let mut i = 0;
572 while i < len {
573 let mut j = i + 1;
574 while j < len {
575 if nums[i].suffix() == nums[j].suffix() {
576 return true;
577 }
578 j += 1;
579 }
580 i += 1;
581 }
582 false
583 }
584
585 const fn is_fixed(&self) -> bool {
586 matches!(self, Self::Fixed(_))
587 }
588}
589
590#[cfg(any(test, feature = "runtime-benchmarks"))]
596pub mod run {
597 pub use crate::{
598 call_builder::{CallSetup, Contract, VmBinaryModule},
599 BalanceOf, MomentOf,
600 };
601 pub use sp_core::{H256, U256};
602
603 use super::*;
604
605 pub fn precompile<P, E>(
610 ext: &mut E,
611 address: &[u8; 20],
612 input: &P::Interface,
613 ) -> Result<Vec<u8>, Error>
614 where
615 P: Precompile<T = E::T>,
616 E: ExtWithInfo,
617 {
618 assert!(P::MATCHER.into_builtin().matches(address));
619 if P::HAS_CONTRACT_INFO {
620 P::call_with_info(address, input, ext)
621 } else {
622 P::call(address, input, ext)
623 }
624 }
625
626 #[cfg(feature = "runtime-benchmarks")]
628 pub(crate) fn builtin<E>(ext: &mut E, address: &[u8; 20], input: Vec<u8>) -> ExecResult
629 where
630 E: ExtWithInfo,
631 {
632 let precompile = <Builtin<E::T>>::get(address)
633 .ok_or(DispatchError::from("No pre-compile at address"))
634 .inspect_err(|_| {
635 log::debug!(target: crate::LOG_TARGET, "No pre-compile at address {address:?}");
636 })?;
637 precompile.call(input, ext)
638 }
639}