From f19096189038be3358c2892de723ca28128ad62f Mon Sep 17 00:00:00 2001
From: nanocryk <6422796+nanocryk@users.noreply.github.com>
Date: Tue, 28 Feb 2023 11:41:40 +0100
Subject: [PATCH 1/8] add IsActivePrecompile trait in utils
---
precompiles/utils/src/precompile_set.rs | 76 +++++++++++++++++++++++--
1 file changed, 72 insertions(+), 4 deletions(-)
diff --git a/precompiles/utils/src/precompile_set.rs b/precompiles/utils/src/precompile_set.rs
index 94743c99d1..d2ab44da8a 100644
--- a/precompiles/utils/src/precompile_set.rs
+++ b/precompiles/utils/src/precompile_set.rs
@@ -29,10 +29,6 @@ use sp_std::{
vec::Vec,
};
-mod sealed {
- pub trait Sealed {}
-}
-
/// Trait representing checks that can be made on a precompile call.
/// Types implementing this trait are made to be chained in a tuple.
///
@@ -405,6 +401,16 @@ impl<'a, H: PrecompileHandle> PrecompileHandle for RestrictiveHandle<'a, H> {
}
}
+/// Allows to know if a precompile is active or not.
+/// This allows to detect deactivated precompile, that are still considered precompiles by
+/// the EVM but that will always revert when called.
+pub trait IsActivePrecompile {
+ /// Is the provided address an active precompile, a precompile that has
+ /// not be deactivated. Note that a deactivated precompile is still considered a precompile
+ /// for the EVM, but it will always revert when called.
+ fn is_active_precompile(&self, address: H160) -> bool;
+}
+
// INDIVIDUAL PRECOMPILE(SET)
/// A fragment of a PrecompileSet. Should be implemented as is it
@@ -538,6 +544,16 @@ where
}
}
+impl IsActivePrecompile for PrecompileAt
+where
+ A: Get,
+{
+ #[inline(always)]
+ fn is_active_precompile(&self, address: H160) -> bool {
+ address == A::get()
+ }
+}
+
/// Wraps an inner PrecompileSet with all its addresses starting with
/// a common prefix.
/// Type parameters allow to define:
@@ -655,9 +671,20 @@ where
}
}
+impl IsActivePrecompile for PrecompileSetStartingWith
+where
+ Self: PrecompileSetFragment,
+{
+ #[inline(always)]
+ fn is_active_precompile(&self, address: H160) -> bool {
+ self.is_precompile(address)
+ }
+}
+
/// Make a precompile that always revert.
/// Can be useful when writing tests.
pub struct RevertPrecompile(PhantomData);
+pub type RemovedPrecompileAt = RevertPrecompile;
impl PrecompileSetFragment for RevertPrecompile
where
@@ -702,6 +729,13 @@ where
}
}
+impl IsActivePrecompile for RevertPrecompile {
+ #[inline(always)]
+ fn is_active_precompile(&self, _address: H160) -> bool {
+ false
+ }
+}
+
// COMPOSITION OF PARTS
#[impl_for_tuples(1, 100)]
impl PrecompileSetFragment for Tuple {
@@ -761,6 +795,20 @@ impl PrecompileSetFragment for Tuple {
}
}
+#[impl_for_tuples(1, 100)]
+impl IsActivePrecompile for Tuple {
+ #[inline(always)]
+ fn is_active_precompile(&self, address: H160) -> bool {
+ for_tuples!(#(
+ if self.Tuple.is_active_precompile(address) {
+ return true;
+ }
+ )*);
+
+ false
+ }
+}
+
/// Wraps a precompileset fragment into a range, and will skip processing it if the address
/// is out of the range.
pub struct PrecompilesInRangeInclusive {
@@ -811,6 +859,20 @@ where
}
}
+impl IsActivePrecompile for PrecompilesInRangeInclusive<(S, E), P>
+where
+ Self: PrecompileSetFragment,
+ P: IsActivePrecompile,
+{
+ fn is_active_precompile(&self, address: H160) -> bool {
+ if self.range.contains(&address) {
+ self.inner.is_active_precompile(address)
+ } else {
+ false
+ }
+ }
+}
+
/// Wraps a tuple of `PrecompileSetFragment` to make a real `PrecompileSet`.
pub struct PrecompileSetBuilder {
inner: P,
@@ -827,6 +889,12 @@ impl PrecompileSet for Precompi
}
}
+impl IsActivePrecompile for PrecompileSetBuilder {
+ fn is_active_precompile(&self, address: H160) -> bool {
+ self.inner.is_active_precompile(address)
+ }
+}
+
impl PrecompileSetBuilder {
/// Create a new instance of the PrecompileSet.
pub fn new() -> Self {
From 41edc246d9d4b2de6f9903f03c96350bb9354c9f Mon Sep 17 00:00:00 2001
From: nanocryk <6422796+nanocryk@users.noreply.github.com>
Date: Tue, 28 Feb 2023 11:41:51 +0100
Subject: [PATCH 2/8] PrecompileRegistry precompile
---
Cargo.lock | 24 ++
Cargo.toml | 1 +
precompiles/precompile-registry/Cargo.toml | 53 +++++
.../PrecompileRegistry.sol | 32 +++
precompiles/precompile-registry/src/lib.rs | 71 ++++++
precompiles/precompile-registry/src/mock.rs | 186 +++++++++++++++
precompiles/precompile-registry/src/tests.rs | 219 ++++++++++++++++++
7 files changed, 586 insertions(+)
create mode 100644 precompiles/precompile-registry/Cargo.toml
create mode 100644 precompiles/precompile-registry/PrecompileRegistry.sol
create mode 100644 precompiles/precompile-registry/src/lib.rs
create mode 100644 precompiles/precompile-registry/src/mock.rs
create mode 100644 precompiles/precompile-registry/src/tests.rs
diff --git a/Cargo.lock b/Cargo.lock
index 9b627281f9..e4671cbed8 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -7650,6 +7650,30 @@ dependencies = [
"sp-std",
]
+[[package]]
+name = "pallet-evm-precompile-registry"
+version = "0.1.0"
+dependencies = [
+ "derive_more",
+ "fp-evm",
+ "frame-support",
+ "frame-system",
+ "hex-literal",
+ "log",
+ "pallet-balances",
+ "pallet-evm",
+ "pallet-scheduler",
+ "pallet-timestamp",
+ "parity-scale-codec",
+ "precompile-utils",
+ "scale-info",
+ "serde",
+ "sp-core",
+ "sp-io",
+ "sp-runtime",
+ "sp-std",
+]
+
[[package]]
name = "pallet-evm-precompile-relay-encoder"
version = "0.1.0"
diff --git a/Cargo.toml b/Cargo.toml
index c019e1217a..a1dbd9f05d 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -22,6 +22,7 @@ members = [
"precompiles/crowdloan-rewards",
"precompiles/pallet-democracy",
"precompiles/parachain-staking",
+ "precompiles/precompile-registry",
"precompiles/preimage",
"precompiles/proxy",
"precompiles/randomness",
diff --git a/precompiles/precompile-registry/Cargo.toml b/precompiles/precompile-registry/Cargo.toml
new file mode 100644
index 0000000000..6e99fd8383
--- /dev/null
+++ b/precompiles/precompile-registry/Cargo.toml
@@ -0,0 +1,53 @@
+[package]
+name = "pallet-evm-precompile-registry"
+authors = { workspace = true }
+description = "Registry of active precompiles"
+edition = "2021"
+version = "0.1.0"
+
+[dependencies]
+log = { workspace = true }
+
+# Moonbeam
+precompile-utils = { workspace = true }
+
+# Substrate
+frame-support = { workspace = true }
+frame-system = { workspace = true }
+parity-scale-codec = { workspace = true }
+sp-core = { workspace = true }
+sp-io = { workspace = true }
+sp-std = { workspace = true }
+
+# Frontier
+fp-evm = { workspace = true }
+pallet-evm = { workspace = true, features = [ "forbid-evm-reentrancy" ] }
+
+[dev-dependencies]
+derive_more = { workspace = true }
+hex-literal = { workspace = true }
+serde = { workspace = true }
+
+# Moonbeam
+precompile-utils = { workspace = true, features = [ "std", "testing" ] }
+
+# Substrate
+pallet-balances = { workspace = true, features = ["std"] }
+pallet-scheduler = { workspace = true }
+pallet-timestamp = { workspace = true }
+scale-info = { workspace = true, features = [ "derive" ] }
+sp-runtime = { workspace = true }
+
+[features]
+default = [ "std" ]
+std = [
+ "fp-evm/std",
+ "frame-support/std",
+ "frame-system/std",
+ "pallet-evm/std",
+ "parity-scale-codec/std",
+ "precompile-utils/std",
+ "sp-core/std",
+ "sp-io/std",
+ "sp-std/std",
+]
diff --git a/precompiles/precompile-registry/PrecompileRegistry.sol b/precompiles/precompile-registry/PrecompileRegistry.sol
new file mode 100644
index 0000000000..a21b85b56a
--- /dev/null
+++ b/precompiles/precompile-registry/PrecompileRegistry.sol
@@ -0,0 +1,32 @@
+// SPDX-License-Identifier: GPL-3.0-only
+pragma solidity >=0.8.3;
+
+/// @author The Moonbeam Team
+/// @title Precompile Registry
+/// @dev Interface to the set of available precompiles.
+interface PrecompileRegistry {
+ /// @dev Query if the given address is a precompile. Note that deactivated precompiles
+ /// are still considered precompiles and will return `true`.
+ /// @param a: Address to query
+ /// @return output Is this address a precompile?
+ /// @custom:selector 446b450e
+ function isPrecompile(address a) external returns (bool);
+
+ /// @dev Query if the given address is an active precompile. Will return false if the
+ /// address is not a precompile or if this precompile is deactivated.
+ /// @param a: Address to query
+ /// @return output Is this address an active precompile?
+ /// @custom:selector 6f5e23cf
+ function isActivePrecompile(address a) external returns (bool);
+
+ /// @dev Update the account code of a precompile address.
+ /// As precompiles are implemented inside the Runtime, they don't have a bytecode, and
+ /// their account code is empty by default. However in Solidity calling a function of a
+ /// contract often automatically adds a check that the contract bytecode is non-empty.
+ /// For that reason a dummy code (0x60006000fd) can be inserted at the precompile address
+ /// to pass that check. This function allows any user to insert that code to precompile address
+ /// if they need it.
+ /// @param a: Address of the precompile.
+ /// @custom:selector 48ceb1b4
+ function updateAccountCode(address a) external;
+}
diff --git a/precompiles/precompile-registry/src/lib.rs b/precompiles/precompile-registry/src/lib.rs
new file mode 100644
index 0000000000..6a23caf329
--- /dev/null
+++ b/precompiles/precompile-registry/src/lib.rs
@@ -0,0 +1,71 @@
+// Copyright 2019-2022 PureStake Inc.
+// This file is part of Moonbeam.
+
+// Moonbeam is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Moonbeam is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Moonbeam. If not, see .
+
+#![cfg_attr(not(feature = "std"), no_std)]
+
+#[cfg(test)]
+mod mock;
+#[cfg(test)]
+mod tests;
+
+use core::marker::PhantomData;
+use fp_evm::PrecompileSet;
+use precompile_utils::{precompile_set::IsActivePrecompile, prelude::*};
+use sp_core::Get;
+
+const DUMMY_CODE: [u8; 5] = [0x60, 0x00, 0x60, 0x00, 0xfd];
+
+pub struct PrecompileRegistry(PhantomData);
+
+#[precompile_utils::precompile]
+impl PrecompileRegistry
+where
+ Runtime: pallet_evm::Config,
+ Runtime::PrecompilesType: IsActivePrecompile,
+{
+ #[precompile::public("isPrecompile(address)")]
+ #[precompile::view]
+ fn is_precompile(_handle: &mut impl PrecompileHandle, address: Address) -> EvmResult {
+ // TODO: what cost?
+ let active = ::get().is_precompile(address.0);
+ Ok(active)
+ }
+
+ #[precompile::public("isActivePrecompile(address)")]
+ #[precompile::view]
+ fn is_active_precompile(
+ _handle: &mut impl PrecompileHandle,
+ address: Address,
+ ) -> EvmResult {
+ // TODO: what cost?
+ let active = ::get().is_active_precompile(address.0);
+ Ok(active)
+ }
+
+ #[precompile::public("updateAccountCode(address)")]
+ fn update_account_code(handle: &mut impl PrecompileHandle, address: Address) -> EvmResult<()> {
+ handle.record_cost(RuntimeHelper::::db_write_gas_cost())?;
+
+ // Prevent touching addresses that are not precompiles.
+ if !::get().is_precompile(address.0) {
+ return Err(revert("provided address is not a precompile"));
+ }
+
+ pallet_evm::Pallet::::create_account(address.0, DUMMY_CODE.to_vec());
+
+ Ok(())
+ }
+}
diff --git a/precompiles/precompile-registry/src/mock.rs b/precompiles/precompile-registry/src/mock.rs
new file mode 100644
index 0000000000..f050b97033
--- /dev/null
+++ b/precompiles/precompile-registry/src/mock.rs
@@ -0,0 +1,186 @@
+// Copyright 2019-2022 PureStake Inc.
+// This file is part of Moonbeam.
+
+// Moonbeam is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Moonbeam is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Moonbeam. If not, see .
+
+//! Test utilities
+use super::*;
+
+use frame_support::traits::Everything;
+use frame_support::{construct_runtime, pallet_prelude::*, parameter_types};
+use pallet_evm::{EnsureAddressNever, EnsureAddressRoot};
+use precompile_utils::{mock_account, precompile_set::*, testing::MockAccount};
+use sp_core::H160;
+use sp_core::H256;
+use sp_runtime::{
+ traits::{BlakeTwo256, IdentityLookup},
+ Perbill,
+};
+
+pub type AccountId = MockAccount;
+pub type Balance = u128;
+pub type BlockNumber = u32;
+
+type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic;
+type Block = frame_system::mocking::MockBlock;
+
+construct_runtime!(
+ pub enum Runtime where
+ Block = Block,
+ NodeBlock = Block,
+ UncheckedExtrinsic = UncheckedExtrinsic,
+ {
+ System: frame_system::{Pallet, Call, Config, Storage, Event},
+ Balances: pallet_balances::{Pallet, Call, Storage, Config, Event},
+ Evm: pallet_evm::{Pallet, Call, Storage, Event},
+ Timestamp: pallet_timestamp::{Pallet, Call, Storage, Inherent},
+ }
+);
+
+parameter_types! {
+ pub const BlockHashCount: u32 = 250;
+ pub const MaximumBlockWeight: Weight = Weight::from_ref_time(1024);
+ pub const MaximumBlockLength: u32 = 2 * 1024;
+ pub const AvailableBlockRatio: Perbill = Perbill::one();
+ pub const SS58Prefix: u8 = 42;
+}
+
+impl frame_system::Config for Runtime {
+ type BaseCallFilter = Everything;
+ type DbWeight = ();
+ type RuntimeOrigin = RuntimeOrigin;
+ type Index = u64;
+ type BlockNumber = BlockNumber;
+ type RuntimeCall = RuntimeCall;
+ type Hash = H256;
+ type Hashing = BlakeTwo256;
+ type AccountId = AccountId;
+ type Lookup = IdentityLookup;
+ type Header = sp_runtime::generic::Header;
+ type RuntimeEvent = RuntimeEvent;
+ type BlockHashCount = BlockHashCount;
+ type Version = ();
+ type PalletInfo = PalletInfo;
+ type AccountData = pallet_balances::AccountData;
+ type OnNewAccount = ();
+ type OnKilledAccount = ();
+ type SystemWeightInfo = ();
+ type BlockWeights = ();
+ type BlockLength = ();
+ type SS58Prefix = SS58Prefix;
+ type OnSetCode = ();
+ type MaxConsumers = frame_support::traits::ConstU32<16>;
+}
+parameter_types! {
+ pub const ExistentialDeposit: u128 = 1;
+}
+impl pallet_balances::Config for Runtime {
+ type MaxReserves = ();
+ type ReserveIdentifier = [u8; 4];
+ type MaxLocks = ();
+ type Balance = Balance;
+ type RuntimeEvent = RuntimeEvent;
+ type DustRemoval = ();
+ type ExistentialDeposit = ExistentialDeposit;
+ type AccountStore = System;
+ type WeightInfo = ();
+}
+
+mock_account!(Registry, |_| MockAccount::from_u64(1));
+mock_account!(Removed, |_| MockAccount::from_u64(2));
+mock_account!(SmartContract, |_| MockAccount::from_u64(3));
+
+pub type Precompiles = PrecompileSetBuilder<
+ R,
+ (
+ PrecompileAt, PrecompileRegistry>,
+ RemovedPrecompileAt>,
+ ),
+>;
+
+pub type PCall = PrecompileRegistryCall;
+
+parameter_types! {
+ pub PrecompilesValue: Precompiles = Precompiles::new();
+ pub const WeightPerGas: Weight = Weight::from_ref_time(1);
+}
+
+impl pallet_evm::Config for Runtime {
+ type FeeCalculator = ();
+ type GasWeightMapping = pallet_evm::FixedGasWeightMapping;
+ type WeightPerGas = WeightPerGas;
+ type CallOrigin = EnsureAddressRoot;
+ type WithdrawOrigin = EnsureAddressNever;
+ type AddressMapping = AccountId;
+ type Currency = Balances;
+ type RuntimeEvent = RuntimeEvent;
+ type Runner = pallet_evm::runner::stack::Runner;
+ type PrecompilesType = Precompiles;
+ type PrecompilesValue = PrecompilesValue;
+ type ChainId = ();
+ type OnChargeTransaction = ();
+ type BlockGasLimit = ();
+ type BlockHashMapping = pallet_evm::SubstrateBlockHashMapping;
+ type FindAuthor = ();
+}
+
+parameter_types! {
+ pub const MinimumPeriod: u64 = 5;
+}
+impl pallet_timestamp::Config for Runtime {
+ type Moment = u64;
+ type OnTimestampSet = ();
+ type MinimumPeriod = MinimumPeriod;
+ type WeightInfo = ();
+}
+
+pub(crate) struct ExtBuilder {
+ // endowed accounts with balances
+ balances: Vec<(AccountId, Balance)>,
+}
+
+impl Default for ExtBuilder {
+ fn default() -> ExtBuilder {
+ ExtBuilder { balances: vec![] }
+ }
+}
+
+impl ExtBuilder {
+ pub(crate) fn with_balances(mut self, balances: Vec<(AccountId, Balance)>) -> Self {
+ self.balances = balances;
+ self
+ }
+
+ pub(crate) fn build(self) -> sp_io::TestExternalities {
+ let mut t = frame_system::GenesisConfig::default()
+ .build_storage::()
+ .expect("Frame system builds valid default genesis config");
+
+ pallet_balances::GenesisConfig:: {
+ balances: self.balances,
+ }
+ .assimilate_storage(&mut t)
+ .expect("Pallet balances storage can be assimilated");
+
+ let mut ext = sp_io::TestExternalities::new(t);
+ ext.execute_with(|| {
+ System::set_block_number(1);
+ pallet_evm::Pallet::::create_account(
+ SmartContract.into(),
+ b"SmartContract".to_vec(),
+ );
+ });
+ ext
+ }
+}
diff --git a/precompiles/precompile-registry/src/tests.rs b/precompiles/precompile-registry/src/tests.rs
new file mode 100644
index 0000000000..c42f9586bd
--- /dev/null
+++ b/precompiles/precompile-registry/src/tests.rs
@@ -0,0 +1,219 @@
+// Copyright 2019-2022 PureStake Inc.
+// This file is part of Moonbeam.
+
+// Moonbeam is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Moonbeam is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Moonbeam. If not, see .
+
+use crate::mock::{
+ ExtBuilder, PCall, Precompiles, PrecompilesValue, Registry, Removed, Runtime, SmartContract,
+};
+use precompile_utils::{prelude::*, testing::*};
+use sp_core::H160;
+
+fn precompiles() -> Precompiles {
+ PrecompilesValue::get()
+}
+
+mod selectors {
+ use super::*;
+
+ #[test]
+ fn selectors() {
+ assert!(PCall::is_precompile_selectors().contains(&0x446b450e));
+ assert!(PCall::is_active_precompile_selectors().contains(&0x6f5e23cf));
+ assert!(PCall::update_account_code_selectors().contains(&0x48ceb1b4));
+ }
+
+ #[test]
+ fn modifiers() {
+ ExtBuilder::default()
+ .with_balances(vec![(CryptoAlith.into(), 1000)])
+ .build()
+ .execute_with(|| {
+ let mut tester =
+ PrecompilesModifierTester::new(precompiles(), CryptoAlith, Registry);
+
+ tester.test_view_modifier(PCall::is_precompile_selectors());
+ tester.test_view_modifier(PCall::is_active_precompile_selectors());
+ tester.test_default_modifier(PCall::update_account_code_selectors());
+ });
+ }
+}
+
+mod is_precompile {
+
+ use super::*;
+
+ fn call(target_address: impl Into, output: bool) {
+ ExtBuilder::default()
+ .with_balances(vec![(CryptoAlith.into(), 1000)])
+ .build()
+ .execute_with(|| {
+ precompiles()
+ .prepare_test(
+ Alice, // can be anyone,*$
+ Registry,
+ PCall::is_precompile {
+ address: Address(target_address.into()),
+ },
+ )
+ .expect_no_logs()
+ .execute_returns_encoded(output);
+ });
+ }
+
+ #[test]
+ fn works_on_precompile() {
+ call(Registry, true);
+ }
+
+ #[test]
+ fn works_on_removed_precompile() {
+ call(Removed, true);
+ }
+
+ #[test]
+ fn works_on_eoa() {
+ call(CryptoAlith, false);
+ }
+
+ #[test]
+ fn works_on_smart_contract() {
+ call(SmartContract, false);
+ }
+}
+
+mod is_active_precompile {
+
+ use super::*;
+
+ fn call(target_address: impl Into, output: bool) {
+ ExtBuilder::default()
+ .with_balances(vec![(CryptoAlith.into(), 1000)])
+ .build()
+ .execute_with(|| {
+ precompiles()
+ .prepare_test(
+ Alice, // can be anyone,*$
+ Registry,
+ PCall::is_active_precompile {
+ address: Address(target_address.into()),
+ },
+ )
+ .expect_no_logs()
+ .execute_returns_encoded(output);
+ });
+ }
+
+ #[test]
+ fn works_on_precompile() {
+ call(Registry, true);
+ }
+
+ #[test]
+ fn works_on_removed_precompile() {
+ call(Removed, false);
+ }
+
+ #[test]
+ fn works_on_eoa() {
+ call(CryptoAlith, false);
+ }
+
+ #[test]
+ fn works_on_smart_contract() {
+ call(SmartContract, false);
+ }
+}
+
+mod update_account_code {
+ use super::*;
+
+ fn call(target_address: impl Into, expect_changes: bool) {
+ ExtBuilder::default()
+ .with_balances(vec![(CryptoAlith.into(), 1000)])
+ .build()
+ .execute_with(|| {
+ let target_address = target_address.into();
+
+ let precompiles = precompiles();
+ let tester = precompiles.prepare_test(
+ Alice, // can be anyone,*$
+ Registry,
+ PCall::update_account_code {
+ address: Address(target_address),
+ },
+ );
+
+ if expect_changes {
+ tester.execute_returns_encoded(());
+ let new_code = pallet_evm::AccountCodes::::get(target_address);
+ assert_eq!(&new_code, &[0x60, 0x00, 0x60, 0x00, 0xfd]);
+ } else {
+ let current_code = pallet_evm::AccountCodes::::get(target_address);
+
+ tester.execute_reverts(|revert| {
+ revert == b"provided address is not a precompile"
+ });
+
+ let new_code = pallet_evm::AccountCodes::::get(target_address);
+ assert_eq!(current_code, new_code);
+ }
+ });
+ }
+
+ #[test]
+ fn works_on_precompile() {
+ call(Registry, true);
+ }
+
+ #[test]
+ fn works_on_removed_precompile() {
+ call(Removed, true);
+ }
+
+ #[test]
+ fn works_on_eoa() {
+ call(CryptoAlith, false);
+ }
+
+ #[test]
+ fn works_on_smart_contract() {
+ call(SmartContract, false);
+ }
+}
+
+#[test]
+fn test_solidity_interface() {
+ for file in ["PrecompileRegistry.sol"] {
+ for solidity_fn in solidity::get_selectors(file) {
+ assert_eq!(
+ solidity_fn.compute_selector_hex(),
+ solidity_fn.docs_selector,
+ "documented selector for '{}' did not match for file '{}'",
+ solidity_fn.signature(),
+ file,
+ );
+
+ let selector = solidity_fn.compute_selector();
+ if !PCall::supports_selector(selector) {
+ panic!(
+ "unsupported selector 0x{:x} => '{}' for file '{}'",
+ selector,
+ solidity_fn.signature(),
+ file,
+ )
+ }
+ }
+ }
+}
From 507e4cd7927142a45247d8c057e0c9ca69f34a03 Mon Sep 17 00:00:00 2001
From: nanocryk <6422796+nanocryk@users.noreply.github.com>
Date: Tue, 28 Feb 2023 11:42:07 +0100
Subject: [PATCH 3/8] toml-sort
---
precompiles/precompile-registry/Cargo.toml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/precompiles/precompile-registry/Cargo.toml b/precompiles/precompile-registry/Cargo.toml
index 6e99fd8383..1a6df27791 100644
--- a/precompiles/precompile-registry/Cargo.toml
+++ b/precompiles/precompile-registry/Cargo.toml
@@ -32,7 +32,7 @@ serde = { workspace = true }
precompile-utils = { workspace = true, features = [ "std", "testing" ] }
# Substrate
-pallet-balances = { workspace = true, features = ["std"] }
+pallet-balances = { workspace = true, features = [ "std" ] }
pallet-scheduler = { workspace = true }
pallet-timestamp = { workspace = true }
scale-info = { workspace = true, features = [ "derive" ] }
From c8dc5dfbf12a724601280afb7bd92a8b3cf84af5 Mon Sep 17 00:00:00 2001
From: nanocryk <6422796+nanocryk@users.noreply.github.com>
Date: Tue, 21 Mar 2023 10:22:03 +0100
Subject: [PATCH 4/8] record 1 db read cost
---
precompiles/precompile-registry/src/lib.rs | 16 ++++++++++++----
1 file changed, 12 insertions(+), 4 deletions(-)
diff --git a/precompiles/precompile-registry/src/lib.rs b/precompiles/precompile-registry/src/lib.rs
index 6a23caf329..7c3d965c2e 100644
--- a/precompiles/precompile-registry/src/lib.rs
+++ b/precompiles/precompile-registry/src/lib.rs
@@ -38,8 +38,10 @@ where
{
#[precompile::public("isPrecompile(address)")]
#[precompile::view]
- fn is_precompile(_handle: &mut impl PrecompileHandle, address: Address) -> EvmResult {
- // TODO: what cost?
+ fn is_precompile(handle: &mut impl PrecompileHandle, address: Address) -> EvmResult {
+ // We consider the precompile set is optimized to do at most one storage read.
+ handle.record_cost(RuntimeHelper::::db_read_gas_cost())?;
+
let active = ::get().is_precompile(address.0);
Ok(active)
}
@@ -47,16 +49,22 @@ where
#[precompile::public("isActivePrecompile(address)")]
#[precompile::view]
fn is_active_precompile(
- _handle: &mut impl PrecompileHandle,
+ handle: &mut impl PrecompileHandle,
address: Address,
) -> EvmResult {
- // TODO: what cost?
+ // We consider the precompile set is optimized to do at most one storage read.
+ handle.record_cost(RuntimeHelper::::db_read_gas_cost())?;
+
let active = ::get().is_active_precompile(address.0);
Ok(active)
}
#[precompile::public("updateAccountCode(address)")]
fn update_account_code(handle: &mut impl PrecompileHandle, address: Address) -> EvmResult<()> {
+ // We consider the precompile set is optimized to do at most one storage read.
+ handle.record_cost(RuntimeHelper::::db_read_gas_cost())?;
+
+ // We will write into the account code.
handle.record_cost(RuntimeHelper::::db_write_gas_cost())?;
// Prevent touching addresses that are not precompiles.
From 0c8de7a94ecb8c8ffeb09e2ccd5913dd22a18944 Mon Sep 17 00:00:00 2001
From: nanocryk <6422796+nanocryk@users.noreply.github.com>
Date: Fri, 24 Mar 2023 14:03:17 +0100
Subject: [PATCH 5/8] add registry to moonbase
---
Cargo.lock | 1 +
Cargo.toml | 1 +
precompiles/utils/src/precompile_set.rs | 1 -
runtime/moonbase/Cargo.toml | 2 ++
runtime/moonbase/src/precompiles.rs | 8 +++++++-
5 files changed, 11 insertions(+), 2 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
index e4671cbed8..87c68e07c6 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -5262,6 +5262,7 @@ dependencies = [
"pallet-evm-precompile-proxy",
"pallet-evm-precompile-randomness",
"pallet-evm-precompile-referenda",
+ "pallet-evm-precompile-registry",
"pallet-evm-precompile-relay-encoder",
"pallet-evm-precompile-sha3fips",
"pallet-evm-precompile-simple",
diff --git a/Cargo.toml b/Cargo.toml
index a1dbd9f05d..732e37759a 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -82,6 +82,7 @@ pallet-evm-precompile-preimage = { path = "precompiles/preimage", default-featur
pallet-evm-precompile-proxy = { path = "precompiles/proxy", default-features = false }
pallet-evm-precompile-randomness = { path = "precompiles/randomness", default-features = false }
pallet-evm-precompile-referenda = { path = "precompiles/referenda", default-features = false }
+pallet-evm-precompile-registry = { path = "precompiles/precompile-registry", default-features = false }
pallet-evm-precompile-relay-encoder = { path = "precompiles/relay-encoder", default-features = false }
pallet-evm-precompile-xcm-transactor = { path = "precompiles/xcm-transactor", default-features = false }
pallet-evm-precompile-xcm-utils = { path = "precompiles/xcm-utils", default-features = false }
diff --git a/precompiles/utils/src/precompile_set.rs b/precompiles/utils/src/precompile_set.rs
index d2ab44da8a..afb64ac1c0 100644
--- a/precompiles/utils/src/precompile_set.rs
+++ b/precompiles/utils/src/precompile_set.rs
@@ -861,7 +861,6 @@ where
impl IsActivePrecompile for PrecompilesInRangeInclusive<(S, E), P>
where
- Self: PrecompileSetFragment,
P: IsActivePrecompile,
{
fn is_active_precompile(&self, address: H160) -> bool {
diff --git a/runtime/moonbase/Cargo.toml b/runtime/moonbase/Cargo.toml
index b811395132..2f3641357d 100644
--- a/runtime/moonbase/Cargo.toml
+++ b/runtime/moonbase/Cargo.toml
@@ -58,6 +58,7 @@ pallet-evm-precompile-preimage = { workspace = true }
pallet-evm-precompile-proxy = { workspace = true }
pallet-evm-precompile-randomness = { workspace = true }
pallet-evm-precompile-referenda = { workspace = true }
+pallet-evm-precompile-registry = { workspace = true }
pallet-evm-precompile-relay-encoder = { workspace = true }
pallet-evm-precompile-xcm-transactor = { workspace = true }
pallet-evm-precompile-xcm-utils = { workspace = true }
@@ -222,6 +223,7 @@ std = [
"pallet-evm-precompile-preimage/std",
"pallet-evm-precompile-randomness/std",
"pallet-evm-precompile-referenda/std",
+ "pallet-evm-precompile-registry/std",
"pallet-evm-precompile-xcm-transactor/std",
"pallet-evm-precompile-xcm-utils/std",
"pallet-evm-precompile-xtokens/std",
diff --git a/runtime/moonbase/src/precompiles.rs b/runtime/moonbase/src/precompiles.rs
index e2255016a2..2f29dc380c 100644
--- a/runtime/moonbase/src/precompiles.rs
+++ b/runtime/moonbase/src/precompiles.rs
@@ -37,6 +37,7 @@ use pallet_evm_precompile_preimage::PreimagePrecompile;
use pallet_evm_precompile_proxy::{OnlyIsProxy, ProxyPrecompile};
use pallet_evm_precompile_randomness::RandomnessPrecompile;
use pallet_evm_precompile_referenda::ReferendaPrecompile;
+use pallet_evm_precompile_registry::PrecompileRegistry;
use pallet_evm_precompile_relay_encoder::RelayEncoderPrecompile;
use pallet_evm_precompile_sha3fips::Sha3FIPS256;
use pallet_evm_precompile_simple::{ECRecover, ECRecoverPublicKey, Identity, Ripemd160, Sha256};
@@ -103,7 +104,7 @@ type MoonbasePrecompilesAt = (
PrecompileAt, Blake2F, EthereumPrecompilesChecks>,
// Non-Moonbeam specific nor Ethereum precompiles :
PrecompileAt, Sha3FIPS256, (CallableByContract, CallableByPrecompile)>,
- // PrecompileAt, Dispatch>,
+ RemovedPrecompileAt>, // Dispatch
PrecompileAt, ECRecoverPublicKey, (CallableByContract, CallableByPrecompile)>,
// Moonbeam specific precompiles:
PrecompileAt<
@@ -224,6 +225,11 @@ type MoonbasePrecompilesAt = (
CollectivePrecompile,
(CallableByContract, CallableByPrecompile),
>,
+ PrecompileAt<
+ AddressU64<2069>,
+ PrecompileRegistry,
+ (CallableByContract, CallableByPrecompile),
+ >,
);
/// The PrecompileSet installed in the Moonbase runtime.
From 66f84768e26de23347b66a042b85752e132b4391 Mon Sep 17 00:00:00 2001
From: nanocryk <6422796+nanocryk@users.noreply.github.com>
Date: Fri, 24 Mar 2023 14:14:27 +0100
Subject: [PATCH 6/8] typo
---
precompiles/precompile-registry/src/tests.rs | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/precompiles/precompile-registry/src/tests.rs b/precompiles/precompile-registry/src/tests.rs
index c42f9586bd..4d14427d7d 100644
--- a/precompiles/precompile-registry/src/tests.rs
+++ b/precompiles/precompile-registry/src/tests.rs
@@ -61,7 +61,7 @@ mod is_precompile {
.execute_with(|| {
precompiles()
.prepare_test(
- Alice, // can be anyone,*$
+ Alice, // can be anyone
Registry,
PCall::is_precompile {
address: Address(target_address.into()),
@@ -104,7 +104,7 @@ mod is_active_precompile {
.execute_with(|| {
precompiles()
.prepare_test(
- Alice, // can be anyone,*$
+ Alice, // can be anyone
Registry,
PCall::is_active_precompile {
address: Address(target_address.into()),
@@ -148,7 +148,7 @@ mod update_account_code {
let precompiles = precompiles();
let tester = precompiles.prepare_test(
- Alice, // can be anyone,*$
+ Alice, // can be anyone
Registry,
PCall::update_account_code {
address: Address(target_address),
From 9da0c094fbfa1e570bca705b9b772e3cfe2caf92 Mon Sep 17 00:00:00 2001
From: nanocryk <6422796+nanocryk@users.noreply.github.com>
Date: Mon, 27 Mar 2023 11:17:36 +0200
Subject: [PATCH 7/8] update precompiles tests in basebase
---
precompiles/utils/src/precompile_set.rs | 2 +-
runtime/moonbase/tests/integration_test.rs | 40 ++++++++++++++++++++--
2 files changed, 38 insertions(+), 4 deletions(-)
diff --git a/precompiles/utils/src/precompile_set.rs b/precompiles/utils/src/precompile_set.rs
index afb64ac1c0..b6bebdb2eb 100644
--- a/precompiles/utils/src/precompile_set.rs
+++ b/precompiles/utils/src/precompile_set.rs
@@ -701,7 +701,7 @@ where
handle: &mut impl PrecompileHandle,
) -> Option {
if A::get() == handle.code_address() {
- Some(Err(revert("revert")))
+ Some(Err(revert("Removed precompile")))
} else {
None
}
diff --git a/runtime/moonbase/tests/integration_test.rs b/runtime/moonbase/tests/integration_test.rs
index e191cb2c0b..24fe259269 100644
--- a/runtime/moonbase/tests/integration_test.rs
+++ b/runtime/moonbase/tests/integration_test.rs
@@ -19,7 +19,7 @@
mod common;
use common::*;
-use precompile_utils::{prelude::*, testing::*};
+use precompile_utils::{precompile_set::IsActivePrecompile, prelude::*, testing::*};
use fp_evm::Context;
use frame_support::{
@@ -2948,8 +2948,9 @@ fn precompile_existence() {
ExtBuilder::default().build().execute_with(|| {
let precompiles = Precompiles::new();
let precompile_addresses: std::collections::BTreeSet<_> = vec![
- 1, 2, 3, 4, 5, 6, 7, 8, 9, 1024, 1026, 2048, 2049, 2050, 2051, 2052, 2053, 2054, 2055,
- 2056, 2057, 2058, 2059, 2060, 2061, 2062, 2063, 2064, 2065, 2066, 2067, 2068,
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 1024, 1025, 1026, 2048, 2049, 2050, 2051, 2052, 2053, 2054,
+ 2055, 2056, 2057, 2058, 2059, 2060, 2061, 2062, 2063, 2064, 2065, 2066, 2067, 2068,
+ 2069,
]
.into_iter()
.map(H160::from_low_u64_be)
@@ -3005,6 +3006,39 @@ fn precompile_existence() {
});
}
+#[test]
+fn removed_precompiles() {
+ ExtBuilder::default().build().execute_with(|| {
+ let precompiles = Precompiles::new();
+ let removed_precompiles = [1025];
+
+ for i in 1..3000 {
+ let address = H160::from_low_u64_be(i);
+
+ if !precompiles.is_precompile(address) {
+ continue;
+ }
+
+ if !removed_precompiles.contains(&i) {
+ assert!(
+ precompiles.is_active_precompile(address),
+ "{i} should be an active precompile"
+ );
+ continue;
+ }
+
+ assert!(
+ !precompiles.is_active_precompile(address),
+ "{i} shouldn't be an active precompile"
+ );
+
+ precompiles
+ .prepare_test(Alice, address, [])
+ .execute_reverts(|out| out == b"Removed precompile");
+ }
+ })
+}
+
#[test]
fn substrate_based_fees_zero_txn_costs_only_base_extrinsic() {
use frame_support::dispatch::{DispatchInfo, Pays};
From fb3ff7d3e117ba7f95703455eb4f042f168d7e94 Mon Sep 17 00:00:00 2001
From: nanocryk <6422796+nanocryk@users.noreply.github.com>
Date: Tue, 28 Mar 2023 11:52:01 +0200
Subject: [PATCH 8/8] separate RevertPrecompile and RemovedPrecompileAt
---
precompiles/utils/src/precompile_set.rs | 56 ++++++++++++++++++++++++-
1 file changed, 54 insertions(+), 2 deletions(-)
diff --git a/precompiles/utils/src/precompile_set.rs b/precompiles/utils/src/precompile_set.rs
index b6bebdb2eb..c8d0015ce4 100644
--- a/precompiles/utils/src/precompile_set.rs
+++ b/precompiles/utils/src/precompile_set.rs
@@ -684,7 +684,6 @@ where
/// Make a precompile that always revert.
/// Can be useful when writing tests.
pub struct RevertPrecompile(PhantomData);
-pub type RemovedPrecompileAt = RevertPrecompile;
impl PrecompileSetFragment for RevertPrecompile
where
@@ -701,7 +700,7 @@ where
handle: &mut impl PrecompileHandle,
) -> Option {
if A::get() == handle.code_address() {
- Some(Err(revert("Removed precompile")))
+ Some(Err(revert("revert")))
} else {
None
}
@@ -730,6 +729,59 @@ where
}
impl IsActivePrecompile for RevertPrecompile {
+ #[inline(always)]
+ fn is_active_precompile(&self, _address: H160) -> bool {
+ true
+ }
+}
+
+/// A precompile that was removed from a precompile set.
+/// Still considered a precompile but is inactive and always revert.
+pub struct RemovedPrecompileAt(PhantomData);
+impl PrecompileSetFragment for RemovedPrecompileAt
+where
+ A: Get,
+{
+ #[inline(always)]
+ fn new() -> Self {
+ Self(PhantomData)
+ }
+
+ #[inline(always)]
+ fn execute(
+ &self,
+ handle: &mut impl PrecompileHandle,
+ ) -> Option {
+ if A::get() == handle.code_address() {
+ Some(Err(revert("Removed precompile")))
+ } else {
+ None
+ }
+ }
+
+ #[inline(always)]
+ fn is_precompile(&self, address: H160) -> bool {
+ address == A::get()
+ }
+
+ #[inline(always)]
+ fn used_addresses(&self) -> Vec {
+ vec![A::get()]
+ }
+
+ fn summarize_checks(&self) -> Vec {
+ vec![PrecompileCheckSummary {
+ name: None,
+ precompile_kind: PrecompileKind::Single(A::get()),
+ recursion_limit: Some(0),
+ accept_delegate_call: true,
+ callable_by_smart_contract: "Reverts in all cases".into(),
+ callable_by_precompile: "Reverts in all cases".into(),
+ }]
+ }
+}
+
+impl IsActivePrecompile for RemovedPrecompileAt {
#[inline(always)]
fn is_active_precompile(&self, _address: H160) -> bool {
false