From db2209f0651b7a9806e9d9aaa02eaa8b9309e73a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20Enrique=20Mu=C3=B1oz=20Mart=C3=ADn?= Date: Thu, 15 Feb 2024 11:10:52 +0100 Subject: [PATCH] Precompile for balances ERC-20 (#1731) * Use last features from precompile_utils & add balances erc20 * taplo fmt * fix integration tests * add forbid-evm-reentrancy * extra docs * add AcceptDelegateCall to balance-erc20 precompile * remove delegate call * apply review CR * add h160 utility and remove to_evm_address * add migration * fix node * fix features * correct taplo installation --- Cargo.lock | 56 +++- Cargo.toml | 10 +- ci/run-check.sh | 7 +- node/src/chain_spec.rs | 109 ++----- runtime/altair/src/evm.rs | 9 +- runtime/centrifuge/src/evm.rs | 9 +- runtime/centrifuge/src/migrations.rs | 2 + runtime/common/Cargo.toml | 10 +- runtime/common/src/evm/precompile.rs | 205 ++++++------ .../migrations/precompile_account_codes.rs | 306 +----------------- runtime/development/src/evm.rs | 9 +- .../integration-tests/src/evm/precompile.rs | 3 +- 12 files changed, 212 insertions(+), 523 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f30a82616a..5ed3e6fda6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3130,6 +3130,12 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" +[[package]] +name = "faster-hex" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51e2ce894d53b295cf97b05685aa077950ff3e8541af83217fc720a6437169f8" + [[package]] name = "fastrand" version = "2.0.1" @@ -4471,7 +4477,7 @@ dependencies = [ "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "windows-core 0.52.0", + "windows-core", ] [[package]] @@ -7090,6 +7096,28 @@ dependencies = [ "scale-info", ] +[[package]] +name = "pallet-evm-precompile-balances-erc20" +version = "0.1.0" +source = "git+https://github.com/moonbeam-foundation/moonbeam?rev=96ac7576f93bb6828415bf3edeef9e8c4b5b4adf#96ac7576f93bb6828415bf3edeef9e8c4b5b4adf" +dependencies = [ + "fp-evm", + "frame-support", + "frame-system", + "log", + "num_enum 0.5.11", + "pallet-balances", + "pallet-evm", + "pallet-timestamp", + "parity-scale-codec 3.6.9", + "paste", + "precompile-utils", + "slices", + "sp-core", + "sp-io", + "sp-std", +] + [[package]] name = "pallet-evm-precompile-blake2" version = "2.0.0-dev" @@ -10775,6 +10803,7 @@ dependencies = [ "frame-system", "hex-literal 0.3.4", "log", + "num_enum 0.5.11", "orml-asset-registry", "orml-tokens", "orml-traits", @@ -10798,6 +10827,7 @@ dependencies = [ "pallet-ethereum-transaction", "pallet-evm", "pallet-evm-chain-id", + "pallet-evm-precompile-balances-erc20", "pallet-evm-precompile-blake2", "pallet-evm-precompile-bn128", "pallet-evm-precompile-dispatch", @@ -10844,6 +10874,7 @@ dependencies = [ "parachain-info", "parity-scale-codec 3.6.9", "polkadot-parachain", + "precompile-utils", "scale-info", "serde", "smallvec", @@ -12641,6 +12672,18 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "826167069c09b99d56f31e9ae5c99049e932a98c9dc2dac47645b08dbbf76ba7" +[[package]] +name = "slices" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2086e458a369cdca838e9f6ed04b4cc2e3ce636d99abb80c9e2eada107749cf" +dependencies = [ + "faster-hex", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "slot-range-helper" version = "0.9.43" @@ -15119,7 +15162,7 @@ version = "0.51.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca229916c5ee38c2f2bc1e9d8f04df975b4bd93f9955dc69fabb5d91270045c9" dependencies = [ - "windows-core 0.51.1", + "windows-core", "windows-targets 0.48.5", ] @@ -15132,15 +15175,6 @@ dependencies = [ "windows-targets 0.48.5", ] -[[package]] -name = "windows-core" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" -dependencies = [ - "windows-targets 0.52.0", -] - [[package]] name = "windows-sys" version = "0.45.0" diff --git a/Cargo.toml b/Cargo.toml index 5e61acd654..c38c61f26b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -81,6 +81,7 @@ rand = { version = "0.8.5", default-features = false } rev_slice = { version = "0.1.5", default-features = false } impl-trait-for-tuples = "0.2.1" num-traits = { version = "0.2", default-features = false } +num_enum = { version = "0.5.3", default-features = false } # Cumulus cumulus-pallet-aura-ext = { git = "https://github.com/paritytech/cumulus", default-features = false, branch = "polkadot-v0.9.43" } @@ -218,8 +219,12 @@ orml-xtokens = { git = "https://github.com/open-web3-stack/open-runtime-module-l fp-rpc = { git = "https://github.com/moonbeam-foundation/frontier", default-features = false, branch = "moonbeam-polkadot-v0.9.43" } fp-self-contained = { git = "https://github.com/moonbeam-foundation/frontier", default-features = false, branch = "moonbeam-polkadot-v0.9.43" } pallet-base-fee = { git = "https://github.com/moonbeam-foundation/frontier", default-features = false, branch = "moonbeam-polkadot-v0.9.43" } -pallet-ethereum = { git = "https://github.com/moonbeam-foundation/frontier", default-features = false, branch = "moonbeam-polkadot-v0.9.43" } -pallet-evm = { git = "https://github.com/moonbeam-foundation/frontier", default-features = false, branch = "moonbeam-polkadot-v0.9.43" } +pallet-ethereum = { git = "https://github.com/moonbeam-foundation/frontier", default-features = false, branch = "moonbeam-polkadot-v0.9.43", features = [ + "forbid-evm-reentrancy", +] } +pallet-evm = { git = "https://github.com/moonbeam-foundation/frontier", default-features = false, branch = "moonbeam-polkadot-v0.9.43", features = [ + "forbid-evm-reentrancy", +] } pallet-evm-chain-id = { git = "https://github.com/moonbeam-foundation/frontier", default-features = false, branch = "moonbeam-polkadot-v0.9.43" } pallet-evm-precompile-blake2 = { git = "https://github.com/moonbeam-foundation/frontier", default-features = false, branch = "moonbeam-polkadot-v0.9.43" } pallet-evm-precompile-bn128 = { git = "https://github.com/moonbeam-foundation/frontier", default-features = false, branch = "moonbeam-polkadot-v0.9.43" } @@ -241,6 +246,7 @@ moonbeam-relay-encoder = { git = "https://github.com/moonbeam-foundation/moonbea xcm-primitives = { git = "https://github.com/moonbeam-foundation/moonbeam", default-features = false, rev = "96ac7576f93bb6828415bf3edeef9e8c4b5b4adf" } pallet-xcm-transactor = { git = "https://github.com/moonbeam-foundation/moonbeam", default-features = false, rev = "96ac7576f93bb6828415bf3edeef9e8c4b5b4adf" } precompile-utils = { git = "https://github.com/moonbeam-foundation/moonbeam", default-features = false, rev = "96ac7576f93bb6828415bf3edeef9e8c4b5b4adf" } +pallet-evm-precompile-balances-erc20 = { git = "https://github.com/moonbeam-foundation/moonbeam", default-features = false, rev = "96ac7576f93bb6828415bf3edeef9e8c4b5b4adf" } # Centrifuge organization fudge = { git = "https://github.com/centrifuge/fudge", branch = "polkadot-v0.9.43" } diff --git a/ci/run-check.sh b/ci/run-check.sh index 84f58158b1..c75124c403 100755 --- a/ci/run-check.sh +++ b/ci/run-check.sh @@ -26,12 +26,7 @@ case $TARGET in ;; lint-taplo) - # The recomended installation fails because an issue in taplo, issue: - # https://github.com/tamasfe/taplo/issues/507 - # Should be fixed in the next taplo release. - # Recomended command: - # cargo install taplo-cli --locked - cargo install --git=https://github.com/tamasfe/taplo.git taplo-cli + cargo install taplo-cli --locked taplo fmt --check ;; diff --git a/node/src/chain_spec.rs b/node/src/chain_spec.rs index baa6c0b4e8..0dfdf26181 100644 --- a/node/src/chain_spec.rs +++ b/node/src/chain_spec.rs @@ -41,11 +41,11 @@ use cfg_types::{ use cfg_utils::vec_to_fixed_array; use cumulus_primitives_core::ParaId; use hex_literal::hex; -use runtime_common::account_conversion::AccountConverter; +use runtime_common::{account_conversion::AccountConverter, evm::precompile::H160Addresses}; use sc_chain_spec::{ChainSpecExtension, ChainSpecGroup}; use sc_service::{ChainType, Properties}; use serde::{Deserialize, Serialize}; -use sp_core::{crypto::UncheckedInto, sr25519, Encode, Pair, Public, H160, U256}; +use sp_core::{crypto::UncheckedInto, sr25519, Encode, Pair, Public, H160}; use sp_runtime::traits::{IdentifyAccount, Verify}; use xcm::{ latest::MultiLocation, @@ -59,6 +59,10 @@ pub type CentrifugeChainSpec = pub type DevelopmentChainSpec = sc_service::GenericChainSpec; +use altair_runtime::evm::AltairPrecompiles; +use centrifuge_runtime::evm::CentrifugePrecompiles; +use development_runtime::evm::DevelopmentPrecompiles; + /// Helper function to generate a crypto pair from seed pub fn get_from_seed(seed: &str) -> ::Public { TPublic::Pair::from_string(&format!("//{seed}"), None) @@ -666,7 +670,7 @@ fn centrifuge_genesis( }, ethereum: Default::default(), evm: centrifuge_runtime::EVMConfig { - accounts: precompile_account_genesis(), + accounts: precompile_account_genesis::(), }, liquidity_rewards_base: Default::default(), polkadot_xcm: centrifuge_runtime::PolkadotXcmConfig { @@ -780,7 +784,7 @@ fn altair_genesis( }, ethereum: Default::default(), evm: centrifuge_runtime::EVMConfig { - accounts: precompile_account_genesis(), + accounts: precompile_account_genesis::(), }, liquidity_rewards_base: Default::default(), polkadot_xcm: altair_runtime::PolkadotXcmConfig { @@ -943,7 +947,7 @@ fn development_genesis( }, ethereum: Default::default(), evm: centrifuge_runtime::EVMConfig { - accounts: precompile_account_genesis(), + accounts: precompile_account_genesis::(), }, block_rewards_base: Default::default(), liquidity_rewards_base: Default::default(), @@ -1020,84 +1024,19 @@ fn asset_registry_assets() -> Vec<(CurrencyId, Vec)> { ] } -fn precompile_account_genesis() -> BTreeMap { - use fp_evm::GenesisAccount; - use runtime_common::evm::precompile::*; - - let mut map = BTreeMap::new(); - /* - pub struct GenesisAccount { - /// Account nonce. - pub nonce: U256, - /// Account balance. - pub balance: U256, - /// Full account storage. - pub storage: std::collections::BTreeMap, - /// Account code. - pub code: Vec, - } - */ - let to_genesis_account = |code: [u8; 5]| -> GenesisAccount { - GenesisAccount { - nonce: U256::zero(), - balance: U256::zero(), - storage: BTreeMap::new(), - code: code.to_vec(), - } - }; - - map.insert( - H160::from(ECRECOVER_ADDR), - to_genesis_account(PRECOMPILE_CODE_STORAGE), - ); - map.insert( - H160::from(SHA256_ADDR), - to_genesis_account(PRECOMPILE_CODE_STORAGE), - ); - map.insert( - H160::from(RIPEMD160_ADDR), - to_genesis_account(PRECOMPILE_CODE_STORAGE), - ); - map.insert( - H160::from(IDENTITY_ADDR), - to_genesis_account(PRECOMPILE_CODE_STORAGE), - ); - map.insert( - H160::from(MODEXP_ADDR), - to_genesis_account(PRECOMPILE_CODE_STORAGE), - ); - map.insert( - H160::from(BN128ADD_ADDR), - to_genesis_account(PRECOMPILE_CODE_STORAGE), - ); - map.insert( - H160::from(BN128MUL_ADDR), - to_genesis_account(PRECOMPILE_CODE_STORAGE), - ); - map.insert( - H160::from(BN128PAIRING_ADDR), - to_genesis_account(PRECOMPILE_CODE_STORAGE), - ); - map.insert( - H160::from(BLAKE2F_ADDR), - to_genesis_account(PRECOMPILE_CODE_STORAGE), - ); - map.insert( - H160::from(SHA3FIPS256_ADDR), - to_genesis_account(PRECOMPILE_CODE_STORAGE), - ); - map.insert( - H160::from(DISPATCH_ADDR), - to_genesis_account(PRECOMPILE_CODE_STORAGE), - ); - map.insert( - H160::from(ECRECOVERPUBLICKEY_ADDR), - to_genesis_account(PRECOMPILE_CODE_STORAGE), - ); - map.insert( - H160::from(LP_AXELAR_GATEWAY), - to_genesis_account(PRECOMPILE_CODE_STORAGE), - ); - - map +fn precompile_account_genesis( +) -> BTreeMap { + PrecompileSet::h160_addresses() + .map(|addr| { + ( + addr, + fp_evm::GenesisAccount { + nonce: Default::default(), + balance: Default::default(), + storage: Default::default(), + code: runtime_common::evm::precompile::utils::REVERT_BYTECODE.to_vec(), + }, + ) + }) + .collect() } diff --git a/runtime/altair/src/evm.rs b/runtime/altair/src/evm.rs index dff7edcbcb..5ab831dcff 100644 --- a/runtime/altair/src/evm.rs +++ b/runtime/altair/src/evm.rs @@ -16,7 +16,7 @@ use pallet_ethereum::PostLogContent; use pallet_evm::{EnsureAddressRoot, EnsureAddressTruncated}; use runtime_common::{ account_conversion::AccountConverter, - evm::{precompile::Altair, BaseFeeThreshold, WEIGHT_PER_GAS}, + evm::{precompile::Precompiles, BaseFeeThreshold, WEIGHT_PER_GAS}, }; use sp_core::{crypto::ByteArray, H160, U256}; use sp_runtime::Permill; @@ -41,9 +41,11 @@ impl> FindAuthor for FindAuthorTruncated { } } +pub type AltairPrecompiles = Precompiles; + parameter_types! { pub BlockGasLimit: U256 = U256::from(NORMAL_DISPATCH_RATIO * MAXIMUM_BLOCK_WEIGHT.ref_time() / WEIGHT_PER_GAS); - pub PrecompilesValue: Altair = Altair::<_>::new(); + pub PrecompilesValue: AltairPrecompiles = Precompiles::<_, _>::new(); pub WeightPerGas: Weight = Weight::from_parts(WEIGHT_PER_GAS, 0); // // pub GasLimitPovSizeRatio: u64 = { @@ -65,6 +67,7 @@ parameter_types! { // in their staging environment. As we can not constantly assert this value we hardcode // it for now. pub const GasLimitStorageGrowthRatio: u64 = 366; + pub const TokenSymbol: &'static str = "AIR"; } impl pallet_evm::Config for Runtime { @@ -81,7 +84,7 @@ impl pallet_evm::Config for Runtime { type GasWeightMapping = pallet_evm::FixedGasWeightMapping; type OnChargeTransaction = (); type OnCreate = (); - type PrecompilesType = Altair; + type PrecompilesType = AltairPrecompiles; type PrecompilesValue = PrecompilesValue; type Runner = pallet_evm::runner::stack::Runner; type RuntimeEvent = RuntimeEvent; diff --git a/runtime/centrifuge/src/evm.rs b/runtime/centrifuge/src/evm.rs index b4627ad7dc..0856ef3956 100644 --- a/runtime/centrifuge/src/evm.rs +++ b/runtime/centrifuge/src/evm.rs @@ -16,7 +16,7 @@ use pallet_ethereum::PostLogContent; use pallet_evm::{EnsureAddressRoot, EnsureAddressTruncated}; use runtime_common::{ account_conversion::AccountConverter, - evm::{precompile::CentrifugePrecompiles, BaseFeeThreshold, WEIGHT_PER_GAS}, + evm::{precompile::Precompiles, BaseFeeThreshold, WEIGHT_PER_GAS}, origin::EnsureAccountOrRootOr, }; use sp_core::{crypto::ByteArray, H160, U256}; @@ -42,9 +42,11 @@ impl> FindAuthor for FindAuthorTruncated { } } +pub type CentrifugePrecompiles = Precompiles; + parameter_types! { pub BlockGasLimit: U256 = U256::from(NORMAL_DISPATCH_RATIO * MAXIMUM_BLOCK_WEIGHT.ref_time() / WEIGHT_PER_GAS); - pub PrecompilesValue: CentrifugePrecompiles = CentrifugePrecompiles::<_>::new(); + pub PrecompilesValue: CentrifugePrecompiles = Precompiles::<_, _>::new(); pub WeightPerGas: Weight = Weight::from_parts(WEIGHT_PER_GAS, 0); // // pub GasLimitPovSizeRatio: u64 = { @@ -66,6 +68,7 @@ parameter_types! { // in their staging environment. As we can not constantly assert this value we hardcode // it for now. pub const GasLimitStorageGrowthRatio: u64 = 366; + pub const TokenSymbol: &'static str = "CFG"; } impl pallet_evm::Config for crate::Runtime { @@ -82,7 +85,7 @@ impl pallet_evm::Config for crate::Runtime { type GasWeightMapping = pallet_evm::FixedGasWeightMapping; type OnChargeTransaction = (); type OnCreate = (); - type PrecompilesType = CentrifugePrecompiles; + type PrecompilesType = CentrifugePrecompiles; type PrecompilesValue = PrecompilesValue; type Runner = pallet_evm::runner::stack::Runner; type RuntimeEvent = crate::RuntimeEvent; diff --git a/runtime/centrifuge/src/migrations.rs b/runtime/centrifuge/src/migrations.rs index ffb72da625..089d27089c 100644 --- a/runtime/centrifuge/src/migrations.rs +++ b/runtime/centrifuge/src/migrations.rs @@ -27,6 +27,8 @@ pub type UpgradeCentrifuge1025 = ( runtime_common::migrations::nuke::KillPallet, // Removes unused migration pallet runtime_common::migrations::nuke::KillPallet, + // Sets account codes for all precompiles + runtime_common::migrations::precompile_account_codes::Migration, ); // Copyright 2021 Centrifuge Foundation (centrifuge.io). diff --git a/runtime/common/Cargo.toml b/runtime/common/Cargo.toml index fa7480ffcb..552185897f 100644 --- a/runtime/common/Cargo.toml +++ b/runtime/common/Cargo.toml @@ -11,6 +11,7 @@ documentation.workspace = true [dependencies] hex-literal = { workspace = true } log = { workspace = true } +num_enum = { workspace = true } parity-scale-codec = { workspace = true } scale-info = { workspace = true } serde = { workspace = true } @@ -32,12 +33,14 @@ xcm-executor = { workspace = true } orml-traits = { workspace = true } +pallet-evm-precompile-balances-erc20 = { workspace = true } pallet-evm-precompile-blake2 = { workspace = true } pallet-evm-precompile-bn128 = { workspace = true } pallet-evm-precompile-dispatch = { workspace = true } pallet-evm-precompile-modexp = { workspace = true } pallet-evm-precompile-sha3fips = { workspace = true } pallet-evm-precompile-simple = { workspace = true } +precompile-utils = { workspace = true } xcm-primitives = { workspace = true } @@ -140,6 +143,7 @@ std = [ "pallet-base-fee/std", "pallet-ethereum/std", "pallet-evm-chain-id/std", + "pallet-evm-precompile-balances-erc20/std", "pallet-evm-precompile-blake2/std", "pallet-evm-precompile-bn128/std", "pallet-evm-precompile-dispatch/std", @@ -168,12 +172,6 @@ std = [ "xcm-executor/std", "xcm-primitives/std", "xcm/std", - "pallet-evm-precompile-blake2/std", - "pallet-evm-precompile-bn128/std", - "pallet-evm-precompile-dispatch/std", - "pallet-evm-precompile-modexp/std", - "pallet-evm-precompile-sha3fips/std", - "pallet-evm-precompile-simple/std", # Locals "cfg-primitives/std", diff --git a/runtime/common/src/evm/precompile.rs b/runtime/common/src/evm/precompile.rs index 27a443a851..632380f237 100644 --- a/runtime/common/src/evm/precompile.rs +++ b/runtime/common/src/evm/precompile.rs @@ -12,129 +12,114 @@ use core::marker::PhantomData; -use frame_support::dispatch::{Dispatchable, GetDispatchInfo, PostDispatchInfo}; -use hex_literal::hex; -use pallet_evm::{ - IsPrecompileResult, Precompile, PrecompileHandle, PrecompileResult, PrecompileSet, -}; +use frame_support::traits::Get; +use pallet_evm_precompile_balances_erc20::{Erc20BalancesPrecompile, Erc20Metadata}; use pallet_evm_precompile_blake2::Blake2F; use pallet_evm_precompile_bn128::{Bn128Add, Bn128Mul, Bn128Pairing}; use pallet_evm_precompile_dispatch::Dispatch; use pallet_evm_precompile_modexp::Modexp; use pallet_evm_precompile_sha3fips::Sha3FIPS256; use pallet_evm_precompile_simple::{ECRecover, ECRecoverPublicKey, Identity, Ripemd160, Sha256}; -use parity_scale_codec::Decode; +use precompile_utils::precompile_set::*; use sp_core::H160; -/// `pallet_evm::AccountCodes` must be populated for precompiles as -/// otherwise `OPCODE::EXTCODESIZE` will make the EVM error upon calling an -/// precompile. -/// -/// The following bytes represent: `PUSH1 00`, `PUSH1 00`, `REVERT`. -pub const PRECOMPILE_CODE_STORAGE: [u8; 5] = hex!("60006000fd"); - -// 0000->1023: Standard Ethereum precompiles -pub const ECRECOVER_ADDR: Addr = addr(1); -pub const SHA256_ADDR: Addr = addr(2); -pub const RIPEMD160_ADDR: Addr = addr(3); -pub const IDENTITY_ADDR: Addr = addr(4); -pub const MODEXP_ADDR: Addr = addr(5); -pub const BN128ADD_ADDR: Addr = addr(6); -pub const BN128MUL_ADDR: Addr = addr(7); -pub const BN128PAIRING_ADDR: Addr = addr(8); -pub const BLAKE2F_ADDR: Addr = addr(9); -// 1024->2047: Nonstandard precompiles shared with other chains (such -// as Moonbeam). See -// https://docs.moonbeam.network/builders/pallets-precompiles/precompiles/overview/#precompiled-contract-addresses -pub const SHA3FIPS256_ADDR: Addr = addr(1024); -pub const DISPATCH_ADDR: Addr = addr(1025); -pub const ECRECOVERPUBLICKEY_ADDR: Addr = addr(1026); -// 2048-XXXX: Nonstandard precompiles that are specific to our chain. - -/// The address of our local Axelar gateway. This is the address that -/// Liquidity-Pool contracts on other domains must use in order to hit the -/// Liquidity-Pool logic on centrifuge. -/// -/// The precompile implements -pub const LP_AXELAR_GATEWAY: Addr = addr(2048); - -pub struct CentrifugePrecompiles(PhantomData); - -impl CentrifugePrecompiles { - #[allow(clippy::new_without_default)] // We'll never use Default and can't derive it. - pub fn new() -> Self { - Self(Default::default()) +pub struct NativeErc20Metadata(PhantomData); +impl> Erc20Metadata for NativeErc20Metadata { + fn name() -> &'static str { + Symbol::get() } -} -impl PrecompileSet for CentrifugePrecompiles -where - R: pallet_evm::Config + axelar_gateway_precompile::Config + frame_system::Config, - R::RuntimeCall: Dispatchable + GetDispatchInfo + Decode, - ::RuntimeOrigin: From>, - axelar_gateway_precompile::Pallet: Precompile, -{ - fn execute(&self, handle: &mut impl PrecompileHandle) -> Option { - match handle.code_address().0 { - ECRECOVER_ADDR => Some(ECRecover::execute(handle)), - SHA256_ADDR => Some(Sha256::execute(handle)), - RIPEMD160_ADDR => Some(Ripemd160::execute(handle)), - IDENTITY_ADDR => Some(Identity::execute(handle)), - MODEXP_ADDR => Some(Modexp::execute(handle)), - BN128ADD_ADDR => Some(Bn128Add::execute(handle)), - BN128MUL_ADDR => Some(Bn128Mul::execute(handle)), - BN128PAIRING_ADDR => Some(Bn128Pairing::execute(handle)), - BLAKE2F_ADDR => Some(Blake2F::execute(handle)), - SHA3FIPS256_ADDR => Some(Sha3FIPS256::execute(handle)), - DISPATCH_ADDR => Some(Dispatch::::execute(handle)), - ECRECOVERPUBLICKEY_ADDR => Some(ECRecoverPublicKey::execute(handle)), - LP_AXELAR_GATEWAY => { - Some( as Precompile>::execute(handle)) - } - _ => None, - } + fn symbol() -> &'static str { + Symbol::get() } - fn is_precompile(&self, address: H160, _remaining_gas: u64) -> IsPrecompileResult { - IsPrecompileResult::Answer { - is_precompile: matches!( - address.0, - ECRECOVER_ADDR - | SHA256_ADDR | RIPEMD160_ADDR - | IDENTITY_ADDR | MODEXP_ADDR - | BN128ADD_ADDR | BN128MUL_ADDR - | BN128PAIRING_ADDR | BLAKE2F_ADDR - | SHA3FIPS256_ADDR | DISPATCH_ADDR - | ECRECOVERPUBLICKEY_ADDR - | LP_AXELAR_GATEWAY - ), - extra_cost: 0, - } + fn decimals() -> u8 { + 18 + } + + fn is_native_currency() -> bool { + true } } -/// Altair's precompiles -/// For now, Altair uses the exact same set of precompiles used in Development. -pub type Altair = CentrifugePrecompiles; - -/// A set of precompiles. This set might contain -/// not yet mainnet ready precompiles in order to test -/// those in development or staging environment without touching -/// the mainnet set. -pub type Development = CentrifugePrecompiles; - -// H160 cannot be used in a match statement due to its hand-rolled -// PartialEq implementation. This just gives a nice name to the -// internal array of bytes that an H160 wraps. -type Addr = [u8; 20]; - -// This is a reimplementation of the upstream u64->H160 conversion -// function, made `const` to make our precompile address `const`s a -// bit cleaner. It can be removed when upstream has a const conversion -// function. -const fn addr(a: u64) -> Addr { - let b = a.to_be_bytes(); - [ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7], - ] +type EthereumPrecompilesChecks = (AcceptDelegateCall, CallableByContract, CallableByPrecompile); + +// Address numbers linked with: +// - https://docs.moonbeam.network/builders/pallets-precompiles/precompiles/overview/#precompiled-contract-addresses +// - https://github.com/centrifuge/liquidity-pools/blob/release-v1.0/src/gateway/routers/axelar/Forwarder.sol#L29 + +pub const LP_AXELAR_GATEWAY: u64 = 0x800; + +#[precompile_utils::precompile_name_from_address] +pub type RuntimePrecompilesAt = ( + // Ethereum precompiles: + // We allow DELEGATECALL to stay compliant with Ethereum behavior. + PrecompileAt, ECRecover, EthereumPrecompilesChecks>, + PrecompileAt, Sha256, EthereumPrecompilesChecks>, + PrecompileAt, Ripemd160, EthereumPrecompilesChecks>, + PrecompileAt, Identity, EthereumPrecompilesChecks>, + PrecompileAt, Modexp, EthereumPrecompilesChecks>, + PrecompileAt, Bn128Add, EthereumPrecompilesChecks>, + PrecompileAt, Bn128Mul, EthereumPrecompilesChecks>, + PrecompileAt, Bn128Pairing, EthereumPrecompilesChecks>, + PrecompileAt, Blake2F, EthereumPrecompilesChecks>, + // Non-Moonbeam specific nor Ethereum precompiles: + PrecompileAt, Sha3FIPS256, (CallableByContract, CallableByPrecompile)>, + PrecompileAt, Dispatch>, + PrecompileAt, ECRecoverPublicKey, (CallableByContract, CallableByPrecompile)>, + // Moonbeam specific precompiles: + PrecompileAt< + AddressU64<0x802>, + Erc20BalancesPrecompile>, + (CallableByContract, CallableByPrecompile), + >, + // Centrifuge specific precompiles: + PrecompileAt< + AddressU64, + axelar_gateway_precompile::Pallet, + CallableByContract, + >, +); + +pub type Precompiles = PrecompileSetBuilder>; + +pub trait H160Addresses { + fn h160_addresses() -> impl Iterator; +} + +impl H160Addresses for PrecompileSetBuilder { + fn h160_addresses() -> impl Iterator { + P::new().used_addresses().into_iter() + } +} + +pub mod utils { + use super::H160Addresses; + + // From Moonbeam: + // This is the simplest bytecode to revert without returning any data. + // We will pre-deploy it under all of our precompiles to ensure they can be + // called from within contracts. + // + // (PUSH1 0x00 PUSH1 0x00 REVERT) + pub const REVERT_BYTECODE: [u8; 5] = [0x60, 0x00, 0x60, 0x00, 0xFD]; + + /// Initialize all required accounts for precompiles. + /// Used for migrations + #[allow(dead_code)] + pub fn initialize_accounts() -> (u64, u64) + where + T: pallet_evm::Config, + ::PrecompilesType: H160Addresses, + { + let (mut reads, mut writes) = (0, 0); + for addr in T::PrecompilesType::h160_addresses() { + reads += 1; + if !pallet_evm::AccountCodes::::contains_key(addr) { + writes += 1; + pallet_evm::AccountCodes::::insert(addr, REVERT_BYTECODE.to_vec()); + } + } + (reads, writes) + } } diff --git a/runtime/common/src/migrations/precompile_account_codes.rs b/runtime/common/src/migrations/precompile_account_codes.rs index 6aa0bd3187..0721cf4f93 100644 --- a/runtime/common/src/migrations/precompile_account_codes.rs +++ b/runtime/common/src/migrations/precompile_account_codes.rs @@ -15,318 +15,38 @@ use frame_support::{ traits::{Get, OnRuntimeUpgrade}, weights::Weight, }; -use sp_core::H160; #[cfg(feature = "try-runtime")] use sp_runtime::DispatchError; -use crate::evm::precompile::PRECOMPILE_CODE_STORAGE; +use crate::evm::precompile::{utils::initialize_accounts, H160Addresses}; pub struct Migration(sp_std::marker::PhantomData); -impl OnRuntimeUpgrade for Migration { +impl OnRuntimeUpgrade for Migration +where + T: pallet_evm::Config, + ::PrecompilesType: H160Addresses, +{ fn on_runtime_upgrade() -> Weight { - log::info!("precompile::AccountCodes: Inserting precompile account codes: on_runtime_upgrade: started"); + log::info!("precompile::AccountCodes: Inserting precompile account codes"); - if pallet_evm::AccountCodes::::get(H160::from(crate::evm::precompile::ECRECOVER_ADDR)) - .is_empty() - { - log::info!("precompile::AccountCodes: Inserting code for ECRECOVER."); - pallet_evm::AccountCodes::::insert( - H160::from(crate::evm::precompile::ECRECOVER_ADDR), - PRECOMPILE_CODE_STORAGE.to_vec(), - ); - } else { - log::warn!("precompile::AccountCodes: ECRECOVER storage already populated. Skipping.") - } + let (reads, writes) = initialize_accounts::(); - if pallet_evm::AccountCodes::::get(H160::from(crate::evm::precompile::SHA256_ADDR)) - .is_empty() - { - log::info!("precompile::AccountCodes: Inserting code for SHA256."); - pallet_evm::AccountCodes::::insert( - H160::from(crate::evm::precompile::SHA256_ADDR), - PRECOMPILE_CODE_STORAGE.to_vec(), - ); - } else { - log::warn!("precompile::AccountCodes: SHA256 storage already populated. Skipping.") - } - - if pallet_evm::AccountCodes::::get(H160::from(crate::evm::precompile::RIPEMD160_ADDR)) - .is_empty() - { - log::info!("precompile::AccountCodes: Inserting code for RIPEMD160."); - pallet_evm::AccountCodes::::insert( - H160::from(crate::evm::precompile::RIPEMD160_ADDR), - PRECOMPILE_CODE_STORAGE.to_vec(), - ); - } else { - log::warn!("precompile::AccountCodes: RIPEMD160 storage already populated. Skipping.") - } - - if pallet_evm::AccountCodes::::get(H160::from(crate::evm::precompile::IDENTITY_ADDR)) - .is_empty() - { - log::info!("precompile::AccountCodes: Inserting code for IDENTITY."); - pallet_evm::AccountCodes::::insert( - H160::from(crate::evm::precompile::IDENTITY_ADDR), - PRECOMPILE_CODE_STORAGE.to_vec(), - ); - } else { - log::warn!("precompile::AccountCodes: IDENTITY storage already populated. Skipping.") - } - - if pallet_evm::AccountCodes::::get(H160::from(crate::evm::precompile::MODEXP_ADDR)) - .is_empty() - { - log::info!("precompile::AccountCodes: Inserting code for MODEXP."); - pallet_evm::AccountCodes::::insert( - H160::from(crate::evm::precompile::MODEXP_ADDR), - PRECOMPILE_CODE_STORAGE.to_vec(), - ); - } else { - log::warn!("precompile::AccountCodes: MODEXP storage already populated. Skipping.") - } - - if pallet_evm::AccountCodes::::get(H160::from(crate::evm::precompile::BN128ADD_ADDR)) - .is_empty() - { - log::info!("precompile::AccountCodes: Inserting code for BN128ADD."); - pallet_evm::AccountCodes::::insert( - H160::from(crate::evm::precompile::BN128ADD_ADDR), - PRECOMPILE_CODE_STORAGE.to_vec(), - ); - } else { - log::warn!("precompile::AccountCodes: BN128ADD storage already populated. Skipping.") - } - - if pallet_evm::AccountCodes::::get(H160::from(crate::evm::precompile::BN128MUL_ADDR)) - .is_empty() - { - log::info!("precompile::AccountCodes: Inserting code for BN128MUL."); - pallet_evm::AccountCodes::::insert( - H160::from(crate::evm::precompile::BN128MUL_ADDR), - PRECOMPILE_CODE_STORAGE.to_vec(), - ); - } else { - log::warn!("precompile::AccountCodes: BN128MUL storage already populated. Skipping.") - } - - if pallet_evm::AccountCodes::::get(H160::from(crate::evm::precompile::BN128PAIRING_ADDR)) - .is_empty() - { - log::info!("precompile::AccountCodes: Inserting code for BN128PAIRING."); - pallet_evm::AccountCodes::::insert( - H160::from(crate::evm::precompile::BN128PAIRING_ADDR), - PRECOMPILE_CODE_STORAGE.to_vec(), - ); - } else { - log::warn!( - "precompile::AccountCodes: BN128PAIRING storage already populated. Skipping." - ) - } - - if pallet_evm::AccountCodes::::get(H160::from(crate::evm::precompile::BLAKE2F_ADDR)) - .is_empty() - { - log::info!("precompile::AccountCodes: Inserting code for BLAKE2F."); - pallet_evm::AccountCodes::::insert( - H160::from(crate::evm::precompile::BLAKE2F_ADDR), - PRECOMPILE_CODE_STORAGE.to_vec(), - ); - } else { - log::warn!("precompile::AccountCodes: BLAKE2F storage already populated. Skipping.") - } - - if pallet_evm::AccountCodes::::get(H160::from(crate::evm::precompile::SHA3FIPS256_ADDR)) - .is_empty() - { - log::info!("precompile::AccountCodes: Inserting code for SHA3FIPS256."); - pallet_evm::AccountCodes::::insert( - H160::from(crate::evm::precompile::SHA3FIPS256_ADDR), - PRECOMPILE_CODE_STORAGE.to_vec(), - ); - } else { - log::warn!("precompile::AccountCodes: SHA3FIPS256 storage already populated. Skipping.") - } - - if pallet_evm::AccountCodes::::get(H160::from(crate::evm::precompile::DISPATCH_ADDR)) - .is_empty() - { - log::info!("precompile::AccountCodes: Inserting code for DISPATCH."); - pallet_evm::AccountCodes::::insert( - H160::from(crate::evm::precompile::DISPATCH_ADDR), - PRECOMPILE_CODE_STORAGE.to_vec(), - ); - } else { - log::warn!("precompile::AccountCodes: DISPATCH storage already populated. Skipping.") - } - - if pallet_evm::AccountCodes::::get(H160::from( - crate::evm::precompile::ECRECOVERPUBLICKEY_ADDR, - )) - .is_empty() - { - log::info!("precompile::AccountCodes: Inserting code for ECRECOVERPUBLICKEY."); - pallet_evm::AccountCodes::::insert( - H160::from(crate::evm::precompile::ECRECOVERPUBLICKEY_ADDR), - PRECOMPILE_CODE_STORAGE.to_vec(), - ); - } else { - log::warn!( - "precompile::AccountCodes: ECRECOVERPUBLICKEY storage already populated. Skipping." - ) - } - - if pallet_evm::AccountCodes::::get(H160::from(crate::evm::precompile::LP_AXELAR_GATEWAY)) - .is_empty() - { - log::info!("precompile::AccountCodes: Inserting code for LP_AXELAR_GATEWAY."); - pallet_evm::AccountCodes::::insert( - H160::from(crate::evm::precompile::LP_AXELAR_GATEWAY), - PRECOMPILE_CODE_STORAGE.to_vec(), - ); - } else { - log::warn!( - "precompile::AccountCodes: LP_AXELAR_GATEWAY storage already populated. Skipping." - ) - } - - log::info!("precompile::AccountCodes: Inserting precompile account codes, on_runtime_upgrade: completed!"); + log::info!( + "precompile::AccountCodes: Added new {} account codes", + writes + ); - // NOTE: This is a worst case weight and we do not care to adjust it correctly - // depending on skipped read/writes. - T::DbWeight::get().reads_writes(13, 13) + T::DbWeight::get().reads_writes(writes, reads) } #[cfg(feature = "try-runtime")] fn pre_upgrade() -> Result, DispatchError> { - assert_eq!( - pallet_evm::AccountCodes::::get(H160::from(crate::evm::precompile::ECRECOVER_ADDR)), - sp_std::vec::Vec::::new() - ); - assert_eq!( - pallet_evm::AccountCodes::::get(H160::from(crate::evm::precompile::SHA256_ADDR)), - sp_std::vec::Vec::::new() - ); - assert_eq!( - pallet_evm::AccountCodes::::get(H160::from(crate::evm::precompile::RIPEMD160_ADDR)), - sp_std::vec::Vec::::new() - ); - assert_eq!( - pallet_evm::AccountCodes::::get(H160::from(crate::evm::precompile::IDENTITY_ADDR)), - sp_std::vec::Vec::::new() - ); - assert_eq!( - pallet_evm::AccountCodes::::get(H160::from(crate::evm::precompile::MODEXP_ADDR)), - sp_std::vec::Vec::::new() - ); - assert_eq!( - pallet_evm::AccountCodes::::get(H160::from(crate::evm::precompile::BN128ADD_ADDR)), - sp_std::vec::Vec::::new() - ); - assert_eq!( - pallet_evm::AccountCodes::::get(H160::from(crate::evm::precompile::BN128MUL_ADDR)), - sp_std::vec::Vec::::new() - ); - assert_eq!( - pallet_evm::AccountCodes::::get(H160::from( - crate::evm::precompile::BN128PAIRING_ADDR - )), - sp_std::vec::Vec::::new() - ); - assert_eq!( - pallet_evm::AccountCodes::::get(H160::from(crate::evm::precompile::BLAKE2F_ADDR)), - sp_std::vec::Vec::::new() - ); - assert_eq!( - pallet_evm::AccountCodes::::get(H160::from( - crate::evm::precompile::SHA3FIPS256_ADDR - )), - sp_std::vec::Vec::::new() - ); - assert_eq!( - pallet_evm::AccountCodes::::get(H160::from(crate::evm::precompile::DISPATCH_ADDR)), - sp_std::vec::Vec::::new() - ); - assert_eq!( - pallet_evm::AccountCodes::::get(H160::from( - crate::evm::precompile::ECRECOVERPUBLICKEY_ADDR - )), - sp_std::vec::Vec::::new() - ); - assert_eq!( - pallet_evm::AccountCodes::::get(H160::from( - crate::evm::precompile::LP_AXELAR_GATEWAY - )), - sp_std::vec::Vec::::new() - ); - Ok(sp_std::vec::Vec::::new()) } #[cfg(feature = "try-runtime")] fn post_upgrade(_state: sp_std::vec::Vec) -> Result<(), DispatchError> { - assert_eq!( - pallet_evm::AccountCodes::::get(H160::from(crate::evm::precompile::ECRECOVER_ADDR)), - PRECOMPILE_CODE_STORAGE.to_vec() - ); - assert_eq!( - pallet_evm::AccountCodes::::get(H160::from(crate::evm::precompile::SHA256_ADDR)), - PRECOMPILE_CODE_STORAGE.to_vec() - ); - assert_eq!( - pallet_evm::AccountCodes::::get(H160::from(crate::evm::precompile::RIPEMD160_ADDR)), - PRECOMPILE_CODE_STORAGE.to_vec() - ); - assert_eq!( - pallet_evm::AccountCodes::::get(H160::from(crate::evm::precompile::IDENTITY_ADDR)), - PRECOMPILE_CODE_STORAGE.to_vec() - ); - assert_eq!( - pallet_evm::AccountCodes::::get(H160::from(crate::evm::precompile::MODEXP_ADDR)), - PRECOMPILE_CODE_STORAGE.to_vec() - ); - assert_eq!( - pallet_evm::AccountCodes::::get(H160::from(crate::evm::precompile::BN128ADD_ADDR)), - PRECOMPILE_CODE_STORAGE.to_vec() - ); - assert_eq!( - pallet_evm::AccountCodes::::get(H160::from(crate::evm::precompile::BN128MUL_ADDR)), - PRECOMPILE_CODE_STORAGE.to_vec() - ); - assert_eq!( - pallet_evm::AccountCodes::::get(H160::from( - crate::evm::precompile::BN128PAIRING_ADDR - )), - PRECOMPILE_CODE_STORAGE.to_vec() - ); - assert_eq!( - pallet_evm::AccountCodes::::get(H160::from(crate::evm::precompile::BLAKE2F_ADDR)), - PRECOMPILE_CODE_STORAGE.to_vec() - ); - assert_eq!( - pallet_evm::AccountCodes::::get(H160::from( - crate::evm::precompile::SHA3FIPS256_ADDR - )), - PRECOMPILE_CODE_STORAGE.to_vec() - ); - assert_eq!( - pallet_evm::AccountCodes::::get(H160::from(crate::evm::precompile::DISPATCH_ADDR)), - PRECOMPILE_CODE_STORAGE.to_vec() - ); - assert_eq!( - pallet_evm::AccountCodes::::get(H160::from( - crate::evm::precompile::ECRECOVERPUBLICKEY_ADDR - )), - PRECOMPILE_CODE_STORAGE.to_vec() - ); - assert_eq!( - pallet_evm::AccountCodes::::get(H160::from( - crate::evm::precompile::LP_AXELAR_GATEWAY - )), - PRECOMPILE_CODE_STORAGE.to_vec() - ); - Ok(()) } } diff --git a/runtime/development/src/evm.rs b/runtime/development/src/evm.rs index dc1a0ead1f..1a42523b31 100644 --- a/runtime/development/src/evm.rs +++ b/runtime/development/src/evm.rs @@ -17,7 +17,7 @@ use pallet_ethereum::PostLogContent; use pallet_evm::EnsureAddressTruncated; use runtime_common::{ account_conversion::AccountConverter, - evm::{precompile::Development, BaseFeeThreshold, WEIGHT_PER_GAS}, + evm::{precompile::Precompiles, BaseFeeThreshold, WEIGHT_PER_GAS}, }; use sp_core::{crypto::ByteArray, H160, U256}; use sp_runtime::Permill; @@ -42,9 +42,11 @@ impl> FindAuthor for FindAuthorTruncated { } } +pub type DevelopmentPrecompiles = Precompiles; + parameter_types! { pub BlockGasLimit: U256 = U256::from(NORMAL_DISPATCH_RATIO * MAXIMUM_BLOCK_WEIGHT.ref_time() / WEIGHT_PER_GAS); - pub PrecompilesValue: Development = Development::<_>::new(); + pub PrecompilesValue: DevelopmentPrecompiles = Precompiles::<_, _>::new(); pub WeightPerGas: Weight = Weight::from_parts(WEIGHT_PER_GAS, 0); // // pub GasLimitPovSizeRatio: u64 = { @@ -66,6 +68,7 @@ parameter_types! { // in their staging environment. As we can not constantly assert this value we hardcode // it for now. pub const GasLimitStorageGrowthRatio: u64 = 366; + pub const TokenSymbol: &'static str = "DCFG"; } impl pallet_evm::Config for Runtime { @@ -82,7 +85,7 @@ impl pallet_evm::Config for Runtime { type GasWeightMapping = pallet_evm::FixedGasWeightMapping; type OnChargeTransaction = (); type OnCreate = (); - type PrecompilesType = Development; + type PrecompilesType = DevelopmentPrecompiles; type PrecompilesValue = PrecompilesValue; type Runner = pallet_evm::runner::stack::Runner; type RuntimeEvent = RuntimeEvent; diff --git a/runtime/integration-tests/src/evm/precompile.rs b/runtime/integration-tests/src/evm/precompile.rs index 97f54c82a3..8612f8bb98 100644 --- a/runtime/integration-tests/src/evm/precompile.rs +++ b/runtime/integration-tests/src/evm/precompile.rs @@ -58,6 +58,7 @@ async fn axelar_precompile_execute() { let currency_id = CurrencyId::ForeignAsset(123456); + let lp_axelar_gateway = H160::from_low_u64_be(LP_AXELAR_GATEWAY); let sender_address = H160::from_low_u64_be(1_000_002); mint_balance_into_derived_account(&mut env, sender_address, 1_000_000 * CFG); @@ -216,7 +217,7 @@ async fn axelar_precompile_execute() { assert_ok!(pallet_evm::Pallet::::call( RawOrigin::Signed(derived_sender_account.clone()).into(), sender_address, - LP_AXELAR_GATEWAY.into(), + lp_axelar_gateway, test_input.to_vec(), U256::from(0), 0x100000,