1mod builtin;
29
30mod tests;
31
32pub use crate::{
33 exec::{ExecError, PrecompileExt as Ext, PrecompileWithInfoExt as ExtWithInfo},
34 gas::{GasMeter, Token},
35 storage::meter::Diff,
36 vm::RuntimeCosts,
37 AddressMapper,
38};
39pub use alloy_core as alloy;
40pub use sp_core::{H160, H256, U256};
41
42use crate::{
43 exec::ExecResult, precompiles::builtin::Builtin, primitives::ExecReturnValue, Config,
44 Error as CrateError, LOG_TARGET,
45};
46use alloc::vec::Vec;
47use alloy::sol_types::{Panic, PanicKind, Revert, SolError, SolInterface};
48use core::num::NonZero;
49use pallet_revive_uapi::ReturnFlags;
50use sp_runtime::DispatchError;
51
52#[cfg(feature = "runtime-benchmarks")]
53pub(crate) use builtin::{
54 IBenchmarking, NoInfo as BenchmarkNoInfo, System as BenchmarkSystem,
55 WithInfo as BenchmarkWithInfo,
56};
57
58const UNIMPLEMENTED: &str = "A precompile must either implement `call` or `call_with_info`";
59
60pub(crate) const EVM_REVERT: [u8; 5] = sp_core::hex2array!("60006000fd");
62
63pub(crate) type All<T> = (Builtin<T>, <T as Config>::Precompiles);
67
68pub enum AddressMatcher {
78 Fixed(NonZero<u16>),
87 Prefix(NonZero<u16>),
100}
101
102pub(crate) enum BuiltinAddressMatcher {
108 Fixed(NonZero<u32>),
109 Prefix(NonZero<u32>),
110}
111
112#[derive(derive_more::From, Debug)]
114pub enum Error {
115 Revert(Revert),
120 Panic(PanicKind),
124 Error(ExecError),
128}
129
130impl From<DispatchError> for Error {
131 fn from(error: DispatchError) -> Self {
132 Self::Error(error.into())
133 }
134}
135
136impl<T: Config> From<CrateError<T>> for Error {
137 fn from(error: CrateError<T>) -> Self {
138 Self::Error(DispatchError::from(error).into())
139 }
140}
141
142pub trait Precompile {
151 type T: Config;
153 type Interface: SolInterface;
162 const MATCHER: AddressMatcher;
164 const HAS_CONTRACT_INFO: bool;
203
204 #[allow(unused_variables)]
206 fn call(
207 address: &[u8; 20],
208 input: &Self::Interface,
209 env: &mut impl Ext<T = Self::T>,
210 ) -> Result<Vec<u8>, Error> {
211 unimplemented!("{UNIMPLEMENTED}")
212 }
213
214 #[allow(unused_variables)]
216 fn call_with_info(
217 address: &[u8; 20],
218 input: &Self::Interface,
219 env: &mut impl ExtWithInfo<T = Self::T>,
220 ) -> Result<Vec<u8>, Error> {
221 unimplemented!("{UNIMPLEMENTED}")
222 }
223}
224
225pub(crate) trait BuiltinPrecompile {
232 type T: Config;
233 type Interface: SolInterface;
234 const MATCHER: BuiltinAddressMatcher;
235 const HAS_CONTRACT_INFO: bool;
236 const CODE: &[u8] = &EVM_REVERT;
237
238 fn call(
239 _address: &[u8; 20],
240 _input: &Self::Interface,
241 _env: &mut impl Ext<T = Self::T>,
242 ) -> Result<Vec<u8>, Error> {
243 unimplemented!("{UNIMPLEMENTED}")
244 }
245
246 fn call_with_info(
247 _address: &[u8; 20],
248 _input: &Self::Interface,
249 _env: &mut impl ExtWithInfo<T = Self::T>,
250 ) -> Result<Vec<u8>, Error> {
251 unimplemented!("{UNIMPLEMENTED}")
252 }
253}
254
255pub(crate) trait PrimitivePrecompile {
263 type T: Config;
264 const MATCHER: BuiltinAddressMatcher;
265 const HAS_CONTRACT_INFO: bool;
266 const CODE: &[u8] = &[];
267
268 fn call(
269 _address: &[u8; 20],
270 _input: Vec<u8>,
271 _env: &mut impl Ext<T = Self::T>,
272 ) -> Result<Vec<u8>, Error> {
273 unimplemented!("{UNIMPLEMENTED}")
274 }
275
276 fn call_with_info(
277 _address: &[u8; 20],
278 _input: Vec<u8>,
279 _env: &mut impl ExtWithInfo<T = Self::T>,
280 ) -> Result<Vec<u8>, Error> {
281 unimplemented!("{UNIMPLEMENTED}")
282 }
283}
284
285pub(crate) struct Instance<E> {
287 has_contract_info: bool,
288 address: [u8; 20],
289 function: fn(&[u8; 20], Vec<u8>, &mut E) -> Result<Vec<u8>, Error>,
291}
292
293impl<E> Instance<E> {
294 pub fn has_contract_info(&self) -> bool {
295 self.has_contract_info
296 }
297
298 pub fn call(&self, input: Vec<u8>, env: &mut E) -> ExecResult {
299 let result = (self.function)(&self.address, input, env);
300 match result {
301 Ok(data) => Ok(ExecReturnValue { flags: ReturnFlags::empty(), data }),
302 Err(Error::Revert(msg)) =>
303 Ok(ExecReturnValue { flags: ReturnFlags::REVERT, data: msg.abi_encode() }),
304 Err(Error::Panic(kind)) => Ok(ExecReturnValue {
305 flags: ReturnFlags::REVERT,
306 data: Panic::from(kind).abi_encode(),
307 }),
308 Err(Error::Error(err)) => Err(err.into()),
309 }
310 }
311}
312
313pub(crate) trait Precompiles<T: Config> {
318 const CHECK_COLLISION: ();
320 const USES_EXTERNAL_RANGE: bool;
325
326 fn code(address: &[u8; 20]) -> Option<&'static [u8]>;
332
333 fn get<E: ExtWithInfo<T = T>>(address: &[u8; 20]) -> Option<Instance<E>>;
337}
338
339impl<P: Precompile> BuiltinPrecompile for P {
340 type T = <Self as Precompile>::T;
341 type Interface = <Self as Precompile>::Interface;
342 const MATCHER: BuiltinAddressMatcher = P::MATCHER.into_builtin();
343 const HAS_CONTRACT_INFO: bool = P::HAS_CONTRACT_INFO;
344
345 fn call(
346 address: &[u8; 20],
347 input: &Self::Interface,
348 env: &mut impl Ext<T = Self::T>,
349 ) -> Result<Vec<u8>, Error> {
350 Self::call(address, input, env)
351 }
352
353 fn call_with_info(
354 address: &[u8; 20],
355 input: &Self::Interface,
356 env: &mut impl ExtWithInfo<T = Self::T>,
357 ) -> Result<Vec<u8>, Error> {
358 Self::call_with_info(address, input, env)
359 }
360}
361
362impl<P: BuiltinPrecompile> PrimitivePrecompile for P {
363 type T = <Self as BuiltinPrecompile>::T;
364 const MATCHER: BuiltinAddressMatcher = P::MATCHER;
365 const HAS_CONTRACT_INFO: bool = P::HAS_CONTRACT_INFO;
366 const CODE: &[u8] = P::CODE;
367
368 fn call(
369 address: &[u8; 20],
370 input: Vec<u8>,
371 env: &mut impl Ext<T = Self::T>,
372 ) -> Result<Vec<u8>, Error> {
373 let call =
374 <Self as BuiltinPrecompile>::Interface::abi_decode_validate(&input).map_err(|err| {
375 log::debug!(target: LOG_TARGET, "`abi_decode_validate` for pre-compile failed: {err:?}");
376 Error::Panic(PanicKind::ResourceError)
377 })?;
378 <Self as BuiltinPrecompile>::call(address, &call, env)
379 }
380
381 fn call_with_info(
382 address: &[u8; 20],
383 input: Vec<u8>,
384 env: &mut impl ExtWithInfo<T = Self::T>,
385 ) -> Result<Vec<u8>, Error> {
386 let call = <Self as BuiltinPrecompile>::Interface::abi_decode_validate(&input)
387 .map_err(|err| {
388 log::debug!(target: LOG_TARGET, "`abi_decode_validate` for pre-compile (with info) failed: {err:?}");
389 Error::Panic(PanicKind::ResourceError)
390 })?;
391 <Self as BuiltinPrecompile>::call_with_info(address, &call, env)
392 }
393}
394
395#[impl_trait_for_tuples::impl_for_tuples(20)]
396#[tuple_types_custom_trait_bound(PrimitivePrecompile<T=T>)]
397impl<T: Config> Precompiles<T> for Tuple {
398 const CHECK_COLLISION: () = {
399 let matchers = [for_tuples!( #( Tuple::MATCHER ),* )];
400 if BuiltinAddressMatcher::has_duplicates(&matchers) {
401 panic!("Precompiles with duplicate matcher detected")
402 }
403 for_tuples!(
404 #(
405 let is_fixed = Tuple::MATCHER.is_fixed();
406 let has_info = Tuple::HAS_CONTRACT_INFO;
407 assert!(is_fixed || !has_info, "Only fixed precompiles can have a contract info.");
408 )*
409 );
410 };
411 const USES_EXTERNAL_RANGE: bool = {
412 let mut uses_external = false;
413 for_tuples!(
414 #(
415 if Tuple::MATCHER.suffix() > u16::MAX as u32 {
416 uses_external = true;
417 }
418 )*
419 );
420 uses_external
421 };
422
423 fn code(address: &[u8; 20]) -> Option<&'static [u8]> {
424 for_tuples!(
425 #(
426 if Tuple::MATCHER.matches(address) {
427 return Some(Tuple::CODE)
428 }
429 )*
430 );
431 None
432 }
433
434 fn get<E: ExtWithInfo<T = T>>(address: &[u8; 20]) -> Option<Instance<E>> {
435 let _ = <Self as Precompiles<T>>::CHECK_COLLISION;
436 let mut instance: Option<Instance<E>> = None;
437 for_tuples!(
438 #(
439 if Tuple::MATCHER.matches(address) {
440 if Tuple::HAS_CONTRACT_INFO {
441 instance = Some(Instance {
442 address: *address,
443 has_contract_info: true,
444 function: Tuple::call_with_info,
445 })
446 } else {
447 instance = Some(Instance {
448 address: *address,
449 has_contract_info: false,
450 function: Tuple::call,
451 })
452 }
453 }
454 )*
455 );
456 instance
457 }
458}
459
460impl<T: Config> Precompiles<T> for (Builtin<T>, <T as Config>::Precompiles) {
461 const CHECK_COLLISION: () = {
462 assert!(
463 !<Builtin<T>>::USES_EXTERNAL_RANGE,
464 "Builtin precompiles must not use addresses reserved for external precompiles"
465 );
466 };
467 const USES_EXTERNAL_RANGE: bool = { <T as Config>::Precompiles::USES_EXTERNAL_RANGE };
468
469 fn code(address: &[u8; 20]) -> Option<&'static [u8]> {
470 <Builtin<T>>::code(address).or_else(|| <T as Config>::Precompiles::code(address))
471 }
472
473 fn get<E: ExtWithInfo<T = T>>(address: &[u8; 20]) -> Option<Instance<E>> {
474 let _ = <Self as Precompiles<T>>::CHECK_COLLISION;
475 <Builtin<T>>::get(address).or_else(|| <T as Config>::Precompiles::get(address))
476 }
477}
478
479impl AddressMatcher {
480 pub const fn base_address(&self) -> [u8; 20] {
481 self.into_builtin().base_address()
482 }
483
484 pub const fn highest_address(&self) -> [u8; 20] {
485 self.into_builtin().highest_address()
486 }
487
488 pub const fn matches(&self, address: &[u8; 20]) -> bool {
489 self.into_builtin().matches(address)
490 }
491
492 const fn into_builtin(&self) -> BuiltinAddressMatcher {
493 const fn left_shift(val: NonZero<u16>) -> NonZero<u32> {
494 let shifted = (val.get() as u32) << 16;
495 NonZero::new(shifted).expect(
496 "Value was non zero before.
497 The shift is small enough to not truncate any existing bits.
498 Hence the value is still non zero; qed",
499 )
500 }
501
502 match self {
503 Self::Fixed(i) => BuiltinAddressMatcher::Fixed(left_shift(*i)),
504 Self::Prefix(i) => BuiltinAddressMatcher::Prefix(left_shift(*i)),
505 }
506 }
507}
508
509impl BuiltinAddressMatcher {
510 pub const fn base_address(&self) -> [u8; 20] {
511 let suffix = self.suffix().to_be_bytes();
512 let mut address = [0u8; 20];
513 let mut i = 16;
514 while i < address.len() {
515 address[i] = suffix[i - 16];
516 i = i + 1;
517 }
518 address
519 }
520
521 pub const fn highest_address(&self) -> [u8; 20] {
522 let mut address = self.base_address();
523 match self {
524 Self::Fixed(_) => (),
525 Self::Prefix(_) => {
526 address[0] = 0xFF;
527 address[1] = 0xFF;
528 address[2] = 0xFF;
529 address[3] = 0xFF;
530 },
531 }
532 address
533 }
534
535 pub const fn matches(&self, address: &[u8; 20]) -> bool {
536 let base_address = self.base_address();
537 let mut i = match self {
538 Self::Fixed(_) => 0,
539 Self::Prefix(_) => 4,
540 };
541 while i < base_address.len() {
542 if address[i] != base_address[i] {
543 return false
544 }
545 i = i + 1;
546 }
547 true
548 }
549
550 const fn suffix(&self) -> u32 {
551 match self {
552 Self::Fixed(i) => i.get(),
553 Self::Prefix(i) => i.get(),
554 }
555 }
556
557 const fn has_duplicates(nums: &[Self]) -> bool {
558 let len = nums.len();
559 let mut i = 0;
560 while i < len {
561 let mut j = i + 1;
562 while j < len {
563 if nums[i].suffix() == nums[j].suffix() {
564 return true;
565 }
566 j += 1;
567 }
568 i += 1;
569 }
570 false
571 }
572
573 const fn is_fixed(&self) -> bool {
574 matches!(self, Self::Fixed(_))
575 }
576}
577
578#[cfg(any(test, feature = "runtime-benchmarks"))]
584pub mod run {
585 pub use crate::{
586 call_builder::{CallSetup, Contract, VmBinaryModule},
587 BalanceOf, MomentOf,
588 };
589 pub use sp_core::{H256, U256};
590
591 use super::*;
592
593 pub fn precompile<P, E>(
598 ext: &mut E,
599 address: &[u8; 20],
600 input: &P::Interface,
601 ) -> Result<Vec<u8>, Error>
602 where
603 P: Precompile<T = E::T>,
604 E: ExtWithInfo,
605 BalanceOf<E::T>: Into<U256> + TryFrom<U256>,
606 MomentOf<E::T>: Into<U256>,
607 <<E as Ext>::T as frame_system::Config>::Hash: frame_support::traits::IsType<H256>,
608 {
609 assert!(P::MATCHER.into_builtin().matches(address));
610 if P::HAS_CONTRACT_INFO {
611 P::call_with_info(address, input, ext)
612 } else {
613 P::call(address, input, ext)
614 }
615 }
616
617 #[cfg(feature = "runtime-benchmarks")]
619 pub(crate) fn builtin<E>(ext: &mut E, address: &[u8; 20], input: Vec<u8>) -> ExecResult
620 where
621 E: ExtWithInfo,
622 BalanceOf<E::T>: Into<U256> + TryFrom<U256>,
623 MomentOf<E::T>: Into<U256>,
624 <<E as Ext>::T as frame_system::Config>::Hash: frame_support::traits::IsType<H256>,
625 {
626 let precompile = <Builtin<E::T>>::get(address)
627 .ok_or(DispatchError::from("No pre-compile at address"))?;
628 precompile.call(input, ext)
629 }
630}