feat: Add Moonbeam EVM Precompile Registry (#137)

## Summary

This PR implements a comprehensive EVM precompile registry system for
DataHaven, following Moonbeam's exact architecture and patterns. The
implementation includes:

- **Registry Precompile**: A new precompile at address `0x0815` (2069)
that manages and queries available precompiles
- **Core Ethereum Precompiles**: Standard Ethereum precompiles
(ECRecover, SHA256, RIPEMD160, Identity, ModExp, BN128Add, BN128Mul,
BN128Pairing, Blake2F, SHA3FIPS)
- **Modular Architecture**: Clean separation following Moonbeam's
structure with dedicated precompile modules per runtime

## Key Features

### Registry Precompile Functions
- `isPrecompile(address)`: Check if an address corresponds to any
precompile (active or inactive)
- `isActivePrecompile(address)`: Check if a precompile is currently
active in the runtime
- `updateAccountCode(address)`: Insert dummy EVM bytecode for Solidity
compatibility

### Runtime Integration
- Integrated across all three runtimes (testnet, stagenet, mainnet)
- Uses Moonbeam's `PrecompileSetBuilder` pattern for composable
precompile management
- Proper gas accounting with database read/write operations
- Access control through `CallableByContract` and `CallableByPrecompile`
traits

---------

Co-authored-by: undercover-cactus <lola@moonsonglabs.com>
This commit is contained in:
Steve Degosserie 2025-09-04 10:25:59 +02:00 committed by GitHub
parent ee7969b540
commit 6886bcbdde
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
19 changed files with 1093 additions and 63 deletions

232
operator/Cargo.lock generated
View file

@ -1309,6 +1309,17 @@ dependencies = [
"tinyvec",
]
[[package]]
name = "bstr"
version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "234113d19d0d7d613b40e86fb654acf958910802bcceab913a4f9e7cda03b1a4"
dependencies = [
"memchr",
"regex-automata 0.4.9",
"serde",
]
[[package]]
name = "build-helper"
version = "0.1.1"
@ -1409,6 +1420,12 @@ dependencies = [
"thiserror 1.0.69",
]
[[package]]
name = "case"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd6c0e7b807d60291f42f33f58480c0bfafe28ed08286446f45e463728cf9c1c"
[[package]]
name = "cc"
version = "1.2.22"
@ -2355,6 +2372,7 @@ dependencies = [
"hex-literal 0.3.4",
"log",
"num-bigint",
"num_enum",
"pallet-authorship",
"pallet-babe",
"pallet-balances",
@ -2368,6 +2386,12 @@ dependencies = [
"pallet-ethereum",
"pallet-evm",
"pallet-evm-chain-id",
"pallet-evm-precompile-blake2",
"pallet-evm-precompile-bn128",
"pallet-evm-precompile-modexp",
"pallet-evm-precompile-registry",
"pallet-evm-precompile-sha3fips",
"pallet-evm-precompile-simple",
"pallet-external-validators",
"pallet-external-validators-rewards",
"pallet-external-validators-rewards-runtime-api",
@ -2405,6 +2429,7 @@ dependencies = [
"parity-scale-codec",
"polkadot-primitives",
"polkadot-runtime-common",
"precompile-utils",
"scale-info",
"serde_json",
"shp-constants",
@ -2579,6 +2604,7 @@ dependencies = [
"hex-literal 0.3.4",
"log",
"num-bigint",
"num_enum",
"pallet-authorship",
"pallet-babe",
"pallet-balances",
@ -2592,6 +2618,12 @@ dependencies = [
"pallet-ethereum",
"pallet-evm",
"pallet-evm-chain-id",
"pallet-evm-precompile-blake2",
"pallet-evm-precompile-bn128",
"pallet-evm-precompile-modexp",
"pallet-evm-precompile-registry",
"pallet-evm-precompile-sha3fips",
"pallet-evm-precompile-simple",
"pallet-external-validators",
"pallet-external-validators-rewards",
"pallet-external-validators-rewards-runtime-api",
@ -2629,6 +2661,7 @@ dependencies = [
"parity-scale-codec",
"polkadot-primitives",
"polkadot-runtime-common",
"precompile-utils",
"scale-info",
"serde_json",
"shp-constants",
@ -2704,6 +2737,7 @@ dependencies = [
"hex-literal 0.3.4",
"log",
"num-bigint",
"num_enum",
"pallet-authorship",
"pallet-babe",
"pallet-balances",
@ -2717,6 +2751,12 @@ dependencies = [
"pallet-ethereum",
"pallet-evm",
"pallet-evm-chain-id",
"pallet-evm-precompile-blake2",
"pallet-evm-precompile-bn128",
"pallet-evm-precompile-modexp",
"pallet-evm-precompile-registry",
"pallet-evm-precompile-sha3fips",
"pallet-evm-precompile-simple",
"pallet-external-validators",
"pallet-external-validators-rewards",
"pallet-external-validators-rewards-runtime-api",
@ -2754,6 +2794,7 @@ dependencies = [
"parity-scale-codec",
"polkadot-primitives",
"polkadot-runtime-common",
"precompile-utils",
"scale-info",
"serde_json",
"shp-constants",
@ -2925,6 +2966,7 @@ dependencies = [
"proc-macro2",
"quote",
"syn 2.0.101",
"unicode-xid",
]
[[package]]
@ -3841,12 +3883,6 @@ version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
[[package]]
name = "fixedbitset"
version = "0.5.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99"
[[package]]
name = "float-cmp"
version = "0.9.0"
@ -5989,6 +6025,9 @@ name = "lazy_static"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
dependencies = [
"spin 0.9.8",
]
[[package]]
name = "lazycell"
@ -7347,6 +7386,20 @@ dependencies = [
"winapi",
]
[[package]]
name = "num"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23"
dependencies = [
"num-bigint",
"num-complex",
"num-integer",
"num-iter",
"num-rational",
"num-traits",
]
[[package]]
name = "num-bigint"
version = "0.4.6"
@ -7391,6 +7444,17 @@ dependencies = [
"num-traits",
]
[[package]]
name = "num-iter"
version = "0.1.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf"
dependencies = [
"autocfg",
"num-integer",
"num-traits",
]
[[package]]
name = "num-rational"
version = "0.4.2"
@ -7599,7 +7663,7 @@ dependencies = [
"expander",
"indexmap 2.9.0",
"itertools 0.11.0",
"petgraph 0.6.5",
"petgraph",
"proc-macro-crate 3.3.0",
"proc-macro2",
"quote",
@ -7961,6 +8025,70 @@ dependencies = [
"scale-info",
]
[[package]]
name = "pallet-evm-precompile-blake2"
version = "2.0.0-dev"
source = "git+https://github.com/polkadot-evm/frontier?rev=75329a2df49e2cc7981485392c31160929d1bd48#75329a2df49e2cc7981485392c31160929d1bd48"
dependencies = [
"fp-evm",
]
[[package]]
name = "pallet-evm-precompile-bn128"
version = "2.0.0-dev"
source = "git+https://github.com/polkadot-evm/frontier?rev=75329a2df49e2cc7981485392c31160929d1bd48#75329a2df49e2cc7981485392c31160929d1bd48"
dependencies = [
"fp-evm",
"sp-core",
"substrate-bn",
]
[[package]]
name = "pallet-evm-precompile-modexp"
version = "2.0.0-dev"
source = "git+https://github.com/polkadot-evm/frontier?rev=75329a2df49e2cc7981485392c31160929d1bd48#75329a2df49e2cc7981485392c31160929d1bd48"
dependencies = [
"fp-evm",
"num",
]
[[package]]
name = "pallet-evm-precompile-registry"
version = "0.1.0"
dependencies = [
"fp-evm",
"frame-support",
"frame-system",
"pallet-balances",
"pallet-evm",
"pallet-timestamp",
"parity-scale-codec",
"precompile-utils",
"scale-info",
"sp-core",
"sp-io",
"sp-runtime",
]
[[package]]
name = "pallet-evm-precompile-sha3fips"
version = "2.0.0-dev"
source = "git+https://github.com/polkadot-evm/frontier?rev=75329a2df49e2cc7981485392c31160929d1bd48#75329a2df49e2cc7981485392c31160929d1bd48"
dependencies = [
"fp-evm",
"tiny-keccak",
]
[[package]]
name = "pallet-evm-precompile-simple"
version = "2.0.0-dev"
source = "git+https://github.com/polkadot-evm/frontier?rev=75329a2df49e2cc7981485392c31160929d1bd48#75329a2df49e2cc7981485392c31160929d1bd48"
dependencies = [
"fp-evm",
"ripemd",
"sp-io",
]
[[package]]
name = "pallet-external-validators"
version = "0.1.0"
@ -8871,17 +8999,7 @@ version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db"
dependencies = [
"fixedbitset 0.4.2",
"indexmap 2.9.0",
]
[[package]]
name = "petgraph"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3672b37090dbd86368a4145bc067582552b29c27377cad4e0a306c97f9bd7772"
dependencies = [
"fixedbitset 0.5.7",
"fixedbitset",
"indexmap 2.9.0",
]
@ -9438,6 +9556,49 @@ dependencies = [
"zerocopy",
]
[[package]]
name = "precompile-utils"
version = "0.1.0"
source = "git+https://github.com/polkadot-evm/frontier?rev=75329a2df49e2cc7981485392c31160929d1bd48#75329a2df49e2cc7981485392c31160929d1bd48"
dependencies = [
"derive_more 1.0.0",
"environmental",
"evm",
"fp-evm",
"frame-support",
"frame-system",
"hex",
"hex-literal 0.4.1",
"impl-trait-for-tuples",
"log",
"num_enum",
"pallet-evm",
"parity-scale-codec",
"precompile-utils-macro",
"scale-info",
"serde",
"similar-asserts",
"sp-core",
"sp-io",
"sp-runtime",
"sp-weights",
"staging-xcm",
]
[[package]]
name = "precompile-utils-macro"
version = "0.1.0"
source = "git+https://github.com/polkadot-evm/frontier?rev=75329a2df49e2cc7981485392c31160929d1bd48#75329a2df49e2cc7981485392c31160929d1bd48"
dependencies = [
"case",
"num_enum",
"prettyplease",
"proc-macro2",
"quote",
"sp-crypto-hashing 0.1.0 (git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-stable2412-6)",
"syn 2.0.101",
]
[[package]]
name = "predicates"
version = "2.1.5"
@ -9726,7 +9887,7 @@ dependencies = [
"log",
"multimap",
"once_cell",
"petgraph 0.7.1",
"petgraph",
"prettyplease",
"prost 0.13.5",
"prost-types",
@ -12448,6 +12609,26 @@ dependencies = [
"wide",
]
[[package]]
name = "similar"
version = "2.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa"
dependencies = [
"bstr",
"unicode-segmentation",
]
[[package]]
name = "similar-asserts"
version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5b441962c817e33508847a22bd82f03a30cff43642dc2fae8b050566121eb9a"
dependencies = [
"console",
"similar",
]
[[package]]
name = "simple-dns"
version = "0.9.3"
@ -14280,6 +14461,19 @@ dependencies = [
"zeroize",
]
[[package]]
name = "substrate-bn"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b5bbfa79abbae15dd642ea8176a21a635ff3c00059961d1ea27ad04e5b441c"
dependencies = [
"byteorder",
"crunchy",
"lazy_static",
"rand 0.8.5",
"rustc-hex",
]
[[package]]
name = "substrate-build-script-utils"
version = "11.0.0"

View file

@ -10,6 +10,7 @@ members = [
"node",
"pallets/outbound-commitment-store",
"pallets/*",
"precompiles/*",
"primitives/bridge",
"runtime/*",
]
@ -29,6 +30,7 @@ pallet-external-validators-rewards = { path = "./pallets/external-validators-rew
pallet-external-validators-rewards-runtime-api = { path = "./pallets/external-validators-rewards/runtime-api", default-features = false }
pallet-outbound-commitment-store = { path = "./pallets/outbound-commitment-store", default-features = false }
pallet-datahaven-native-transfer = { path = "./pallets/datahaven-native-transfer", default-features = false }
pallet-evm-precompile-registry = { path = "./precompiles/precompile-registry", default-features = false }
# Crates.io (wasm)
alloy-core = { version = "0.8.15", default-features = false }
@ -54,6 +56,7 @@ libsecp256k1 = { version = "0.7", default-features = false }
log = { version = "0.4.25" }
milagro-bls = { version = "1.5.4", default-features = false, package = "snowbridge-milagro-bls" }
num-bigint = { version = "0.4.3", default-features = false }
num_enum = { version = "0.7.3", default-features = false }
openssl-sys = { version = "0.9", features = [
"vendored",
] } # This is just to set the "vendored" feature required for the crossbuild, so that OpenSSL builds from source
@ -211,10 +214,14 @@ pallet-dynamic-fee = { git = "https://github.com/polkadot-evm/frontier", rev="75
pallet-ethereum = { git = "https://github.com/polkadot-evm/frontier/", rev="75329a2df49e2cc7981485392c31160929d1bd48", default-features = false }
pallet-evm = { git = "https://github.com/polkadot-evm/frontier/", rev="75329a2df49e2cc7981485392c31160929d1bd48", default-features = false }
pallet-evm-chain-id = { git = "https://github.com/polkadot-evm/frontier/", rev="75329a2df49e2cc7981485392c31160929d1bd48", default-features = false }
pallet-evm-precompile-blake2 = { git = "https://github.com/polkadot-evm/frontier", rev="75329a2df49e2cc7981485392c31160929d1bd48", default-features = false }
pallet-evm-precompile-bn128 = { git = "https://github.com/polkadot-evm/frontier", rev="75329a2df49e2cc7981485392c31160929d1bd48", default-features = false }
pallet-evm-precompile-modexp = { git = "https://github.com/polkadot-evm/frontier", rev="75329a2df49e2cc7981485392c31160929d1bd48", default-features = false }
pallet-evm-precompile-sha3fips = { git = "https://github.com/polkadot-evm/frontier", rev="75329a2df49e2cc7981485392c31160929d1bd48", default-features = false }
pallet-evm-precompile-simple = { git = "https://github.com/polkadot-evm/frontier", rev="75329a2df49e2cc7981485392c31160929d1bd48", default-features = false }
pallet-hotfix-sufficients = { git = "https://github.com/polkadot-evm/frontier", rev="75329a2df49e2cc7981485392c31160929d1bd48", default-features = false }
precompile-utils = { git = "https://github.com/polkadot-evm/frontier/", rev="75329a2df49e2cc7981485392c31160929d1bd48", default-features = false }
precompile-utils-macro = { git = "https://github.com/polkadot-evm/frontier", rev="75329a2df49e2cc7981485392c31160929d1bd48", default-features = false }
# Frontier (client)
fc-api = { git = "https://github.com/polkadot-evm/frontier", rev="75329a2df49e2cc7981485392c31160929d1bd48", default-features = false }

View file

@ -0,0 +1,42 @@
[package]
name = "pallet-evm-precompile-registry"
authors = ["The DataHaven Team"]
description = "Registry of active precompiles"
edition = "2021"
version = "0.1.0"
[dependencies]
# Substrate
frame-support = { workspace = true }
frame-system = { workspace = true }
sp-core = { workspace = true }
sp-io = { workspace = true }
# Frontier
fp-evm = { workspace = true }
pallet-evm = { workspace = true, features = ["forbid-evm-reentrancy"] }
precompile-utils = { workspace = true }
[dev-dependencies]
# Precompile utils for testing
precompile-utils = { workspace = true, features = ["std", "testing"] }
# Substrate
pallet-balances = { workspace = true, features = ["insecure_zero_ed", "std"] }
pallet-timestamp = { workspace = true, features = ["std"] }
parity-scale-codec = { workspace = true, features = ["max-encoded-len", "std"] }
scale-info = { workspace = true, features = ["derive", "std"] }
sp-runtime = { workspace = true, features = ["std"] }
[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",
]

View file

@ -0,0 +1,41 @@
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity >=0.8.3;
/// @dev The PrecompileRegistry contract's address.
address constant PRECOMPILE_REGISTRY_ADDRESS = 0x0000000000000000000000000000000000000815;
/// @dev The PrecompileRegistry contract's instance.
PrecompileRegistry constant PRECOMPILE_REGISTRY_CONTRACT = PrecompileRegistry(
PRECOMPILE_REGISTRY_ADDRESS
);
/// @author The Moonbeam Team
/// @title Precompile Registry
/// @dev Interface to the set of available precompiles.
/// @custom:address 0x0000000000000000000000000000000000000815
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 view 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 view 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;
}

View file

@ -0,0 +1,99 @@
// Copyright 2019-2025 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 <http://www.gnu.org/licenses/>.
#![cfg_attr(not(feature = "std"), no_std)]
#[cfg(test)]
mod mock;
#[cfg(test)]
mod tests;
use core::marker::PhantomData;
use fp_evm::{ExitError, IsPrecompileResult, PrecompileFailure};
use precompile_utils::{
precompile_set::{is_precompile_or_fail, IsActivePrecompile},
prelude::*,
};
use sp_core::Get;
const DUMMY_CODE: [u8; 5] = [0x60, 0x00, 0x60, 0x00, 0xfd];
pub struct PrecompileRegistry<Runtime>(PhantomData<Runtime>);
#[precompile_utils::precompile]
impl<Runtime> PrecompileRegistry<Runtime>
where
Runtime: pallet_evm::Config,
Runtime::PrecompilesType: IsActivePrecompile,
{
#[precompile::public("isPrecompile(address)")]
#[precompile::view]
fn is_precompile(handle: &mut impl PrecompileHandle, address: Address) -> EvmResult<bool> {
// We consider the precompile set is optimized to do at most one storage read.
// In the case of moonbeam, the storage item that can be read is pallet_asset::Asset
// (TODO make it more generic, maybe add a const generic on PrecompileRegistry type)
// Storage item: Asset:
// Blake2_128(16) + AssetId(16) + AssetDetails((4 * AccountId(20)) + (3 * Balance(16)) + 15)
handle.record_db_read::<Runtime>(175)?;
is_precompile_or_fail::<Runtime>(address.0, handle.remaining_gas())
}
#[precompile::public("isActivePrecompile(address)")]
#[precompile::view]
fn is_active_precompile(
handle: &mut impl PrecompileHandle,
address: Address,
) -> EvmResult<bool> {
// We consider the precompile set is optimized to do at most one storage read.
// In the case of moonbeam, the storage item that can be read is pallet_asset::Asset
// (TODO make it more generic, maybe add a const generic on PrecompileRegistry type)
// Storage item: Asset:
// Blake2_128(16) + AssetId(16) + AssetDetails((4 * AccountId(20)) + (3 * Balance(16)) + 15)
handle.record_db_read::<Runtime>(175)?;
match <Runtime::PrecompilesValue>::get()
.is_active_precompile(address.0, handle.remaining_gas())
{
IsPrecompileResult::Answer { is_precompile, .. } => Ok(is_precompile),
IsPrecompileResult::OutOfGas => Err(PrecompileFailure::Error {
exit_status: ExitError::OutOfGas,
}),
}
}
#[precompile::public("updateAccountCode(address)")]
fn update_account_code(handle: &mut impl PrecompileHandle, address: Address) -> EvmResult<()> {
// Prevent touching addresses that are not precompiles.
//
// We consider the precompile set is optimized to do at most one storage read.
// In the case of moonbeam, the storage item that can be read is pallet_asset::Asset
// (TODO make it more generic, maybe add a const generic on PrecompileRegistry type)
// Storage item: Asset:
// Blake2_128(16) + AssetId(16) + AssetDetails((4 * AccountId(20)) + (3 * Balance(16)) + 15)
handle.record_db_read::<Runtime>(175)?;
if !is_precompile_or_fail::<Runtime>(address.0, handle.remaining_gas())? {
return Err(revert("provided address is not a precompile"));
}
// pallet_evm::create_account read storage item pallet_evm::AccountCodes
//
// AccountCodes: Blake2128(16) + H160(20) + Vec(5)
// We asume an existing precompile can hold at most 5 bytes worth of dummy code.
handle.record_db_read::<Runtime>(41)?;
pallet_evm::Pallet::<Runtime>::create_account(address.0, DUMMY_CODE.to_vec());
Ok(())
}
}

View file

@ -0,0 +1,198 @@
// Copyright 2019-2025 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 <http://www.gnu.org/licenses/>.
use super::*;
use frame_support::traits::Everything;
use frame_support::{construct_runtime, pallet_prelude::*, parameter_types};
use pallet_evm::{EnsureAddressNever, EnsureAddressRoot, FrameSystemAccountProvider};
use precompile_utils::{mock_account, precompile_set::*, testing::MockAccount};
use sp_core::H256;
use sp_runtime::BuildStorage;
use sp_runtime::{
traits::{BlakeTwo256, IdentityLookup},
Perbill,
};
pub type AccountId = MockAccount;
pub type Balance = u128;
type Block = frame_system::mocking::MockBlockU32<Runtime>;
construct_runtime!(
pub enum Runtime {
System: frame_system,
Balances: pallet_balances,
Evm: pallet_evm,
Timestamp: pallet_timestamp,
}
);
parameter_types! {
pub const BlockHashCount: u32 = 250;
pub const MaximumBlockWeight: Weight = Weight::from_parts(1024, 1);
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 RuntimeTask = RuntimeTask;
type Nonce = u64;
type Block = Block;
type RuntimeCall = RuntimeCall;
type Hash = H256;
type Hashing = BlakeTwo256;
type AccountId = AccountId;
type Lookup = IdentityLookup<Self::AccountId>;
type RuntimeEvent = RuntimeEvent;
type BlockHashCount = BlockHashCount;
type Version = ();
type PalletInfo = PalletInfo;
type AccountData = pallet_balances::AccountData<Balance>;
type OnNewAccount = ();
type OnKilledAccount = ();
type SystemWeightInfo = ();
type BlockWeights = ();
type BlockLength = ();
type SS58Prefix = SS58Prefix;
type OnSetCode = ();
type MaxConsumers = frame_support::traits::ConstU32<16>;
type SingleBlockMigrations = ();
type MultiBlockMigrator = ();
type PreInherents = ();
type PostInherents = ();
type PostTransactions = ();
type ExtensionsWeightInfo = ();
}
parameter_types! {
pub const ExistentialDeposit: u128 = 0;
}
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 = ();
type RuntimeHoldReason = ();
type FreezeIdentifier = ();
type MaxFreezes = ();
type RuntimeFreezeReason = ();
type DoneSlashHandler = ();
}
mock_account!(Registry, |_| MockAccount::from_u64(1));
mock_account!(Removed, |_| MockAccount::from_u64(2));
mock_account!(SmartContract, |_| MockAccount::from_u64(3));
pub type Precompiles<R> = PrecompileSetBuilder<
R,
(
PrecompileAt<AddressU64<1>, PrecompileRegistry<R>>,
RemovedPrecompileAt<AddressU64<2>>,
),
>;
pub type PCall = PrecompileRegistryCall<Runtime>;
parameter_types! {
pub PrecompilesValue: Precompiles<Runtime> = Precompiles::new();
pub const WeightPerGas: Weight = Weight::from_parts(1, 0);
}
impl pallet_evm::Config for Runtime {
type FeeCalculator = ();
type GasWeightMapping = pallet_evm::FixedGasWeightMapping<Self>;
type WeightPerGas = WeightPerGas;
type CallOrigin = EnsureAddressRoot<AccountId>;
type WithdrawOrigin = EnsureAddressNever<AccountId>;
type AddressMapping = AccountId;
type Currency = Balances;
type RuntimeEvent = RuntimeEvent;
type Runner = pallet_evm::runner::stack::Runner<Self>;
type PrecompilesType = Precompiles<Runtime>;
type PrecompilesValue = PrecompilesValue;
type ChainId = ();
type OnChargeTransaction = ();
type BlockGasLimit = ();
type BlockHashMapping = pallet_evm::SubstrateBlockHashMapping<Self>;
type FindAuthor = ();
type OnCreate = ();
type GasLimitPovSizeRatio = ();
type GasLimitStorageGrowthRatio = ();
type Timestamp = Timestamp;
type WeightInfo = pallet_evm::weights::SubstrateWeight<Runtime>;
type AccountProvider = FrameSystemAccountProvider<Runtime>;
}
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::<Runtime>::default()
.build_storage()
.expect("Frame system builds valid default genesis config");
pallet_balances::GenesisConfig::<Runtime> {
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::<Runtime>::create_account(
SmartContract.into(),
b"SmartContract".to_vec(),
);
});
ext
}
}

View file

@ -0,0 +1,202 @@
// Copyright 2019-2025 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 <http://www.gnu.org/licenses/>.
use crate::mock::{
ExtBuilder, PCall, Precompiles, PrecompilesValue, Registry, Removed, Runtime, SmartContract,
};
use precompile_utils::{prelude::*, testing::*};
use sp_core::H160;
fn precompiles() -> Precompiles<Runtime> {
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<H160>, 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(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<H160>, 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(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<H160>, 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(());
let new_code = pallet_evm::AccountCodes::<Runtime>::get(target_address);
assert_eq!(&new_code, &[0x60, 0x00, 0x60, 0x00, 0xfd]);
} else {
let current_code = pallet_evm::AccountCodes::<Runtime>::get(target_address);
tester.execute_reverts(|revert| {
revert == b"provided address is not a precompile"
});
let new_code = pallet_evm::AccountCodes::<Runtime>::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() {
check_precompile_implements_solidity_interfaces(
&["PrecompileRegistry.sol"],
PCall::supports_selector,
)
}

View file

@ -33,6 +33,7 @@ hex = { workspace = true }
hex-literal = { workspace = true }
log = { workspace = true }
num-bigint = { workspace = true }
num_enum = { workspace = true }
pallet-authorship = { workspace = true }
pallet-babe = { workspace = true }
pallet-balances = { workspace = true, features = ["insecure_zero_ed"] }
@ -40,10 +41,14 @@ pallet-beefy = { workspace = true }
pallet-beefy-mmr = { workspace = true }
pallet-collective = { workspace = true }
pallet-conviction-voting = { workspace = true }
pallet-ethereum = { workspace = true }
pallet-referenda = { workspace = true }
pallet-evm = { workspace = true }
pallet-ethereum = { workspace = true, features = ["forbid-evm-reentrancy"] }
pallet-evm = { workspace = true, features = ["forbid-evm-reentrancy"] }
pallet-evm-chain-id = { workspace = true }
pallet-evm-precompile-blake2 = { workspace = true }
pallet-evm-precompile-bn128 = { workspace = true }
pallet-evm-precompile-modexp = { workspace = true }
pallet-evm-precompile-sha3fips = { workspace = true }
pallet-evm-precompile-simple = { workspace = true }
pallet-external-validators = { workspace = true }
pallet-external-validators-rewards = { workspace = true }
pallet-external-validators-rewards-runtime-api = { workspace = true }
@ -59,17 +64,19 @@ pallet-datahaven-native-transfer = { workspace = true }
pallet-parameters = { workspace = true }
pallet-preimage = { workspace = true }
pallet-proxy = { workspace = true }
pallet-referenda = { workspace = true }
pallet-scheduler = { workspace = true }
pallet-session = { workspace = true }
pallet-sudo = { workspace = true }
pallet-timestamp = { workspace = true }
pallet-transaction-payment = { workspace = true }
pallet-transaction-payment = { workspace = true }
pallet-transaction-payment-rpc-runtime-api = { workspace = true }
pallet-treasury = { workspace = true }
pallet-utility = { workspace = true }
pallet-whitelist = { workspace = true }
polkadot-primitives = { workspace = true }
polkadot-runtime-common = { workspace = true }
precompile-utils = { workspace = true }
scale-info = { workspace = true, features = ["derive", "serde"] }
serde_json = { workspace = true, default-features = false, features = [
"alloc",
@ -112,6 +119,8 @@ xcm-executor = { workspace = true }
strum = { workspace = true }
strum_macros = { workspace = true }
# DataHaven precompiles
pallet-evm-precompile-registry = { workspace = true }
# StorageHub
pallet-bucket-nfts = { workspace = true }
@ -135,7 +144,6 @@ shp-file-key-verifier = { workspace = true }
shp-data-price-updater = { workspace = true }
sp-trie = { workspace = true }
[build-dependencies]
substrate-wasm-builder = { workspace = true, optional = true, default-features = true }
@ -145,6 +153,8 @@ frame-support-test = { workspace = true }
sp-io = { workspace = true }
sp-tracing = { workspace = true }
precompile-utils = { workspace = true, features = ["std", "testing"] }
# Snowbridge testing
snowbridge-core = { workspace = true }
snowbridge-pallet-system = { workspace = true }
@ -157,6 +167,7 @@ std = [
"codec/std",
"datahaven-runtime-common/std",
"fp-account/std",
"fp-evm/std",
"frame-benchmarking?/std",
"frame-executive/std",
"frame-metadata-hash-extension/std",
@ -175,6 +186,7 @@ std = [
"pallet-ethereum/std",
"pallet-evm-chain-id/std",
"pallet-evm/std",
"pallet-evm-precompile-registry/std",
"pallet-external-validators/std",
"pallet-external-validators-rewards/std",
"pallet-external-validators-rewards-runtime-api/std",
@ -200,6 +212,7 @@ std = [
"pallet-whitelist/std",
"polkadot-primitives/std",
"polkadot-runtime-common/std",
"precompile-utils/std",
"scale-info/std",
"serde_json/std",
"snowbridge-beacon-primitives/std",

View file

@ -28,14 +28,14 @@ pub mod runtime_params;
mod storagehub;
use super::{
currency::*, AccountId, Babe, Balance, Balances, BeefyMmrLeaf, Block, BlockNumber,
EthereumBeaconClient, EthereumOutboundQueueV2, EvmChainId, ExistentialDeposit,
ExternalValidators, ExternalValidatorsRewards, Hash, Historical, ImOnline, MessageQueue, Nonce,
Offences, OriginCaller, OutboundCommitmentStore, PalletInfo, Preimage, Referenda, Runtime,
RuntimeCall, RuntimeEvent, RuntimeFreezeReason, RuntimeHoldReason, RuntimeOrigin, RuntimeTask,
Scheduler, Session, SessionKeys, Signature, System, Timestamp, Treasury, BLOCK_HASH_COUNT,
EXTRINSIC_BASE_WEIGHT, MAXIMUM_BLOCK_WEIGHT, NORMAL_BLOCK_WEIGHT, NORMAL_DISPATCH_RATIO,
SLOT_DURATION, VERSION,
currency::*, precompiles::DataHavenPrecompiles, AccountId, Babe, Balance, Balances,
BeefyMmrLeaf, Block, BlockNumber, EthereumBeaconClient, EthereumOutboundQueueV2, EvmChainId,
ExistentialDeposit, ExternalValidators, ExternalValidatorsRewards, Hash, Historical, ImOnline,
MessageQueue, Nonce, Offences, OriginCaller, OutboundCommitmentStore, PalletInfo, Preimage,
Referenda, Runtime, RuntimeCall, RuntimeEvent, RuntimeFreezeReason, RuntimeHoldReason,
RuntimeOrigin, RuntimeTask, Scheduler, Session, SessionKeys, Signature, System, Timestamp,
Treasury, BLOCK_HASH_COUNT, EXTRINSIC_BASE_WEIGHT, MAXIMUM_BLOCK_WEIGHT, NORMAL_BLOCK_WEIGHT,
NORMAL_DISPATCH_RATIO, SLOT_DURATION, VERSION,
};
use codec::{Decode, Encode};
use datahaven_runtime_common::{
@ -774,7 +774,7 @@ datahaven_runtime_common::impl_on_charge_evm_transaction!();
parameter_types! {
pub BlockGasLimit: U256
= U256::from(NORMAL_DISPATCH_RATIO * MAXIMUM_BLOCK_WEIGHT.ref_time() / WEIGHT_PER_GAS);
// pub PrecompilesValue: TemplatePrecompiles<Runtime> = TemplatePrecompiles::<_>::new();
pub PrecompilesValue: DataHavenPrecompiles<Runtime> = DataHavenPrecompiles::<_>::new();
pub WeightPerGas: Weight = Weight::from_parts(WEIGHT_PER_GAS, 0);
pub SuicideQuickClearLimit: u32 = 0;
/// The amount of gas per pov. A ratio of 16 if we convert ref_time to gas and we compare
@ -800,8 +800,8 @@ impl pallet_evm::Config for Runtime {
type AddressMapping = IdentityAddressMapping;
type Currency = Balances;
type RuntimeEvent = RuntimeEvent;
type PrecompilesType = ();
type PrecompilesValue = ();
type PrecompilesType = DataHavenPrecompiles<Self>;
type PrecompilesValue = PrecompilesValue;
type ChainId = EvmChainId;
type BlockGasLimit = BlockGasLimit;
type Runner = pallet_evm::runner::stack::Runner<Self>;

View file

@ -9,6 +9,7 @@ include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs"));
#[cfg(feature = "runtime-benchmarks")]
mod benchmarks;
pub mod configs;
pub mod precompiles;
pub mod weights;
// Re-export governance for tests

View file

@ -0,0 +1,67 @@
// Copyright 2019-2025 The DataHaven Team
// This file is part of DataHaven.
// DataHaven 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.
// DataHaven 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 DataHaven. If not, see <http://www.gnu.org/licenses/>.
use pallet_evm_precompile_blake2::Blake2F;
use pallet_evm_precompile_bn128::{Bn128Add, Bn128Mul, Bn128Pairing};
use pallet_evm_precompile_modexp::Modexp;
use pallet_evm_precompile_registry::PrecompileRegistry;
use pallet_evm_precompile_sha3fips::Sha3FIPS256;
use pallet_evm_precompile_simple::{ECRecover, ECRecoverPublicKey, Identity, Ripemd160, Sha256};
use precompile_utils::precompile_set::*;
type EthereumPrecompilesChecks = (AcceptDelegateCall, CallableByContract, CallableByPrecompile);
/// EVM precompiles available in the DataHaven Mainnet runtime.
#[precompile_utils::precompile_name_from_address]
type DataHavenPrecompilesAt<R> = (
// Ethereum precompiles:
// We allow DELEGATECALL to stay compliant with Ethereum behavior.
PrecompileAt<AddressU64<1>, ECRecover, EthereumPrecompilesChecks>,
PrecompileAt<AddressU64<2>, Sha256, EthereumPrecompilesChecks>,
PrecompileAt<AddressU64<3>, Ripemd160, EthereumPrecompilesChecks>,
PrecompileAt<AddressU64<4>, Identity, EthereumPrecompilesChecks>,
PrecompileAt<AddressU64<5>, Modexp, EthereumPrecompilesChecks>,
PrecompileAt<AddressU64<6>, Bn128Add, EthereumPrecompilesChecks>,
PrecompileAt<AddressU64<7>, Bn128Mul, EthereumPrecompilesChecks>,
PrecompileAt<AddressU64<8>, Bn128Pairing, EthereumPrecompilesChecks>,
PrecompileAt<AddressU64<9>, Blake2F, EthereumPrecompilesChecks>,
// Non-DataHaven specific nor Ethereum precompiles :
PrecompileAt<AddressU64<1024>, Sha3FIPS256, (CallableByContract, CallableByPrecompile)>,
RemovedPrecompileAt<AddressU64<1025>>,
PrecompileAt<AddressU64<1026>, ECRecoverPublicKey, (CallableByContract, CallableByPrecompile)>,
RemovedPrecompileAt<AddressU64<1027>>,
// DataHaven specific precompiles:
PrecompileAt<
AddressU64<2069>,
PrecompileRegistry<R>,
(CallableByContract, CallableByPrecompile),
>,
);
/// The PrecompileSet installed in the DataHaven runtime.
/// We include the nine Istanbul precompiles
/// (https://github.com/ethereum/go-ethereum/blob/3c46f557/core/vm/contracts.go#L69)
/// The following distribution has been decided for the precompiles
/// 0-1023: Ethereum Mainnet Precompiles
/// 1024-2047 Precompiles that are not in Ethereum Mainnet but are neither DataHaven specific
/// 2048-4095 DataHaven specific precompiles
pub type DataHavenPrecompiles<R> = PrecompileSetBuilder<
R,
(
// Skip precompiles if out of range.
PrecompilesInRangeInclusive<(AddressU64<1>, AddressU64<4095>), DataHavenPrecompilesAt<R>>,
),
>;

View file

@ -33,6 +33,7 @@ hex = { workspace = true }
hex-literal = { workspace = true }
log = { workspace = true }
num-bigint = { workspace = true }
num_enum = { workspace = true }
pallet-authorship = { workspace = true }
pallet-babe = { workspace = true }
pallet-balances = { workspace = true, features = ["insecure_zero_ed"]}
@ -40,10 +41,14 @@ pallet-beefy = { workspace = true }
pallet-beefy-mmr = { workspace = true }
pallet-collective = { workspace = true }
pallet-conviction-voting = { workspace = true }
pallet-ethereum = { workspace = true }
pallet-referenda = { workspace = true }
pallet-evm = { workspace = true }
pallet-ethereum = { workspace = true, features = ["forbid-evm-reentrancy"] }
pallet-evm = { workspace = true, features = ["forbid-evm-reentrancy"] }
pallet-evm-chain-id = { workspace = true }
pallet-evm-precompile-blake2 = { workspace = true }
pallet-evm-precompile-bn128 = { workspace = true }
pallet-evm-precompile-modexp = { workspace = true }
pallet-evm-precompile-sha3fips = { workspace = true }
pallet-evm-precompile-simple = { workspace = true }
pallet-external-validators = { workspace = true }
pallet-external-validators-rewards = { workspace = true }
pallet-external-validators-rewards-runtime-api = { workspace = true }
@ -59,6 +64,7 @@ pallet-datahaven-native-transfer = { workspace = true }
pallet-parameters = { workspace = true }
pallet-preimage = { workspace = true }
pallet-proxy = { workspace = true }
pallet-referenda = { workspace = true }
pallet-scheduler = { workspace = true }
pallet-session = { workspace = true }
pallet-sudo = { workspace = true }
@ -70,6 +76,7 @@ pallet-utility = { workspace = true }
pallet-whitelist = { workspace = true }
polkadot-primitives = { workspace = true }
polkadot-runtime-common = { workspace = true }
precompile-utils = { workspace = true }
scale-info = { workspace = true, features = ["derive", "serde"] }
serde_json = { workspace = true, default-features = false, features = [
"alloc",
@ -112,6 +119,9 @@ xcm = { workspace = true }
xcm-builder = { workspace = true }
xcm-executor = { workspace = true }
# DataHaven precompiles
pallet-evm-precompile-registry = { workspace = true }
# StorageHub
pallet-bucket-nfts = { workspace = true }
pallet-nfts = { workspace = true }
@ -143,6 +153,8 @@ frame-support-test = { workspace = true }
sp-io = { workspace = true }
sp-tracing = { workspace = true }
precompile-utils = { workspace = true, features = ["std", "testing"] }
# Snowbridge testing
snowbridge-core = { workspace = true }
snowbridge-pallet-system = { workspace = true }
@ -155,6 +167,7 @@ std = [
"codec/std",
"datahaven-runtime-common/std",
"fp-account/std",
"fp-evm/std",
"frame-benchmarking?/std",
"frame-executive/std",
"frame-metadata-hash-extension/std",
@ -173,6 +186,7 @@ std = [
"pallet-ethereum/std",
"pallet-evm-chain-id/std",
"pallet-evm/std",
"pallet-evm-precompile-registry/std",
"pallet-external-validators/std",
"pallet-external-validators-rewards/std",
"pallet-external-validators-rewards-runtime-api/std",
@ -198,6 +212,7 @@ std = [
"pallet-whitelist/std",
"polkadot-primitives/std",
"polkadot-runtime-common/std",
"precompile-utils/std",
"scale-info/std",
"serde_json/std",
"snowbridge-beacon-primitives/std",

View file

@ -28,14 +28,14 @@ pub mod runtime_params;
mod storagehub;
use super::{
currency::*, AccountId, Babe, Balance, Balances, BeefyMmrLeaf, Block, BlockNumber,
EthereumBeaconClient, EthereumOutboundQueueV2, EvmChainId, ExistentialDeposit,
ExternalValidators, ExternalValidatorsRewards, Hash, Historical, ImOnline, MessageQueue, Nonce,
Offences, OriginCaller, OutboundCommitmentStore, PalletInfo, Preimage, Referenda, Runtime,
RuntimeCall, RuntimeEvent, RuntimeFreezeReason, RuntimeHoldReason, RuntimeOrigin, RuntimeTask,
Scheduler, Session, SessionKeys, Signature, System, Timestamp, Treasury, BLOCK_HASH_COUNT,
EXTRINSIC_BASE_WEIGHT, MAXIMUM_BLOCK_WEIGHT, NORMAL_BLOCK_WEIGHT, NORMAL_DISPATCH_RATIO,
SLOT_DURATION, VERSION,
currency::*, precompiles::DataHavenPrecompiles, AccountId, Babe, Balance, Balances,
BeefyMmrLeaf, Block, BlockNumber, EthereumBeaconClient, EthereumOutboundQueueV2, EvmChainId,
ExistentialDeposit, ExternalValidators, ExternalValidatorsRewards, Hash, Historical, ImOnline,
MessageQueue, Nonce, Offences, OriginCaller, OutboundCommitmentStore, PalletInfo, Preimage,
Referenda, Runtime, RuntimeCall, RuntimeEvent, RuntimeFreezeReason, RuntimeHoldReason,
RuntimeOrigin, RuntimeTask, Scheduler, Session, SessionKeys, Signature, System, Timestamp,
Treasury, BLOCK_HASH_COUNT, EXTRINSIC_BASE_WEIGHT, MAXIMUM_BLOCK_WEIGHT, NORMAL_BLOCK_WEIGHT,
NORMAL_DISPATCH_RATIO, SLOT_DURATION, VERSION,
};
use codec::{Decode, Encode};
use datahaven_runtime_common::{
@ -773,7 +773,7 @@ datahaven_runtime_common::impl_on_charge_evm_transaction!();
parameter_types! {
pub BlockGasLimit: U256
= U256::from(NORMAL_DISPATCH_RATIO * MAXIMUM_BLOCK_WEIGHT.ref_time() / WEIGHT_PER_GAS);
// pub PrecompilesValue: TemplatePrecompiles<Runtime> = TemplatePrecompiles::<_>::new();
pub PrecompilesValue: DataHavenPrecompiles<Runtime> = DataHavenPrecompiles::<_>::new();
pub WeightPerGas: Weight = Weight::from_parts(WEIGHT_PER_GAS, 0);
pub SuicideQuickClearLimit: u32 = 0;
/// The amount of gas per pov. A ratio of 16 if we convert ref_time to gas and we compare
@ -799,8 +799,8 @@ impl pallet_evm::Config for Runtime {
type AddressMapping = IdentityAddressMapping;
type Currency = Balances;
type RuntimeEvent = RuntimeEvent;
type PrecompilesType = ();
type PrecompilesValue = ();
type PrecompilesType = DataHavenPrecompiles<Self>;
type PrecompilesValue = PrecompilesValue;
type ChainId = EvmChainId;
type BlockGasLimit = BlockGasLimit;
type Runner = pallet_evm::runner::stack::Runner<Self>;

View file

@ -9,6 +9,7 @@ include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs"));
#[cfg(feature = "runtime-benchmarks")]
mod benchmarks;
pub mod configs;
pub mod precompiles;
pub mod weights;
// Re-export governance for tests

View file

@ -0,0 +1,67 @@
// Copyright 2019-2025 The DataHaven Team
// This file is part of DataHaven.
// DataHaven 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.
// DataHaven 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 DataHaven. If not, see <http://www.gnu.org/licenses/>.
use pallet_evm_precompile_blake2::Blake2F;
use pallet_evm_precompile_bn128::{Bn128Add, Bn128Mul, Bn128Pairing};
use pallet_evm_precompile_modexp::Modexp;
use pallet_evm_precompile_registry::PrecompileRegistry;
use pallet_evm_precompile_sha3fips::Sha3FIPS256;
use pallet_evm_precompile_simple::{ECRecover, ECRecoverPublicKey, Identity, Ripemd160, Sha256};
use precompile_utils::precompile_set::*;
type EthereumPrecompilesChecks = (AcceptDelegateCall, CallableByContract, CallableByPrecompile);
/// EVM precompiles available in the DataHaven Stagenet runtime.
#[precompile_utils::precompile_name_from_address]
type DataHavenPrecompilesAt<R> = (
// Ethereum precompiles:
// We allow DELEGATECALL to stay compliant with Ethereum behavior.
PrecompileAt<AddressU64<1>, ECRecover, EthereumPrecompilesChecks>,
PrecompileAt<AddressU64<2>, Sha256, EthereumPrecompilesChecks>,
PrecompileAt<AddressU64<3>, Ripemd160, EthereumPrecompilesChecks>,
PrecompileAt<AddressU64<4>, Identity, EthereumPrecompilesChecks>,
PrecompileAt<AddressU64<5>, Modexp, EthereumPrecompilesChecks>,
PrecompileAt<AddressU64<6>, Bn128Add, EthereumPrecompilesChecks>,
PrecompileAt<AddressU64<7>, Bn128Mul, EthereumPrecompilesChecks>,
PrecompileAt<AddressU64<8>, Bn128Pairing, EthereumPrecompilesChecks>,
PrecompileAt<AddressU64<9>, Blake2F, EthereumPrecompilesChecks>,
// Non-DataHaven specific nor Ethereum precompiles :
PrecompileAt<AddressU64<1024>, Sha3FIPS256, (CallableByContract, CallableByPrecompile)>,
RemovedPrecompileAt<AddressU64<1025>>,
PrecompileAt<AddressU64<1026>, ECRecoverPublicKey, (CallableByContract, CallableByPrecompile)>,
RemovedPrecompileAt<AddressU64<1027>>,
// DataHaven specific precompiles:
PrecompileAt<
AddressU64<2069>,
PrecompileRegistry<R>,
(CallableByContract, CallableByPrecompile),
>,
);
/// The PrecompileSet installed in the DataHaven runtime.
/// We include the nine Istanbul precompiles
/// (https://github.com/ethereum/go-ethereum/blob/3c46f557/core/vm/contracts.go#L69)
/// The following distribution has been decided for the precompiles
/// 0-1023: Ethereum Mainnet Precompiles
/// 1024-2047 Precompiles that are not in Ethereum Mainnet but are neither DataHaven specific
/// 2048-4095 DataHaven specific precompiles
pub type DataHavenPrecompiles<R> = PrecompileSetBuilder<
R,
(
// Skip precompiles if out of range.
PrecompilesInRangeInclusive<(AddressU64<1>, AddressU64<4095>), DataHavenPrecompilesAt<R>>,
),
>;

View file

@ -33,6 +33,7 @@ hex = { workspace = true }
hex-literal = { workspace = true }
log = { workspace = true }
num-bigint = { workspace = true }
num_enum = { workspace = true }
pallet-authorship = { workspace = true }
pallet-babe = { workspace = true }
pallet-balances = { workspace = true, features = ["insecure_zero_ed"] }
@ -41,10 +42,14 @@ pallet-beefy-mmr = { workspace = true }
pallet-collective = { workspace = true }
pallet-conviction-voting = { workspace = true }
pallet-datahaven-native-transfer = { workspace = true }
pallet-referenda = { workspace = true }
pallet-ethereum = { workspace = true }
pallet-evm = { workspace = true }
pallet-ethereum = { workspace = true, features = ["forbid-evm-reentrancy"] }
pallet-evm = { workspace = true, features = ["forbid-evm-reentrancy"] }
pallet-evm-chain-id = { workspace = true }
pallet-evm-precompile-blake2 = { workspace = true }
pallet-evm-precompile-bn128 = { workspace = true }
pallet-evm-precompile-modexp = { workspace = true }
pallet-evm-precompile-sha3fips = { workspace = true }
pallet-evm-precompile-simple = { workspace = true }
pallet-external-validators = { workspace = true }
pallet-external-validators-rewards = { workspace = true }
pallet-external-validators-rewards-runtime-api = { workspace = true }
@ -59,6 +64,7 @@ pallet-outbound-commitment-store = { workspace = true }
pallet-parameters = { workspace = true }
pallet-preimage = { workspace = true }
pallet-proxy = { workspace = true }
pallet-referenda = { workspace = true }
pallet-scheduler = { workspace = true }
pallet-session = { workspace = true }
pallet-sudo = { workspace = true }
@ -70,6 +76,7 @@ pallet-utility = { workspace = true }
pallet-whitelist = { workspace = true }
polkadot-primitives = { workspace = true }
polkadot-runtime-common = { workspace = true }
precompile-utils = { workspace = true }
scale-info = { workspace = true, features = ["derive", "serde"] }
serde_json = { workspace = true, default-features = false, features = [
"alloc",
@ -112,6 +119,9 @@ xcm-executor = { workspace = true }
strum = { workspace = true }
strum_macros = { workspace = true }
# DataHaven precompiles
pallet-evm-precompile-registry = { workspace = true }
# StorageHub
pallet-bucket-nfts = { workspace = true }
pallet-nfts = { workspace = true }
@ -143,6 +153,8 @@ frame-support-test = { workspace = true }
sp-io = { workspace = true }
sp-tracing = { workspace = true }
precompile-utils = { workspace = true, features = ["std", "testing"] }
# Snowbridge testing
snowbridge-core = { workspace = true }
snowbridge-outbound-queue-primitives = { workspace = true }
@ -155,6 +167,7 @@ std = [
"codec/std",
"datahaven-runtime-common/std",
"fp-account/std",
"fp-evm/std",
"frame-benchmarking?/std",
"frame-executive/std",
"frame-metadata-hash-extension/std",
@ -173,6 +186,7 @@ std = [
"pallet-ethereum/std",
"pallet-evm-chain-id/std",
"pallet-evm/std",
"pallet-evm-precompile-registry/std",
"pallet-grandpa/std",
"pallet-identity/std",
"pallet-im-online/std",
@ -195,6 +209,7 @@ std = [
"pallet-whitelist/std",
"polkadot-primitives/std",
"polkadot-runtime-common/std",
"precompile-utils/std",
"scale-info/std",
"serde_json/std",
"snowbridge-beacon-primitives/std",

View file

@ -28,14 +28,14 @@ pub mod runtime_params;
mod storagehub;
use super::{
currency::*, AccountId, Babe, Balance, Balances, BeefyMmrLeaf, Block, BlockNumber,
EthereumBeaconClient, EthereumOutboundQueueV2, EvmChainId, ExistentialDeposit,
ExternalValidators, ExternalValidatorsRewards, Hash, Historical, ImOnline, MessageQueue, Nonce,
Offences, OriginCaller, OutboundCommitmentStore, PalletInfo, Preimage, Referenda, Runtime,
RuntimeCall, RuntimeEvent, RuntimeFreezeReason, RuntimeHoldReason, RuntimeOrigin, RuntimeTask,
Scheduler, Session, SessionKeys, Signature, System, Timestamp, Treasury, BLOCK_HASH_COUNT,
EXTRINSIC_BASE_WEIGHT, MAXIMUM_BLOCK_WEIGHT, NORMAL_BLOCK_WEIGHT, NORMAL_DISPATCH_RATIO,
SLOT_DURATION, VERSION,
currency::*, precompiles::DataHavenPrecompiles, AccountId, Babe, Balance, Balances,
BeefyMmrLeaf, Block, BlockNumber, EthereumBeaconClient, EthereumOutboundQueueV2, EvmChainId,
ExistentialDeposit, ExternalValidators, ExternalValidatorsRewards, Hash, Historical, ImOnline,
MessageQueue, Nonce, Offences, OriginCaller, OutboundCommitmentStore, PalletInfo, Preimage,
Referenda, Runtime, RuntimeCall, RuntimeEvent, RuntimeFreezeReason, RuntimeHoldReason,
RuntimeOrigin, RuntimeTask, Scheduler, Session, SessionKeys, Signature, System, Timestamp,
Treasury, BLOCK_HASH_COUNT, EXTRINSIC_BASE_WEIGHT, MAXIMUM_BLOCK_WEIGHT, NORMAL_BLOCK_WEIGHT,
NORMAL_DISPATCH_RATIO, SLOT_DURATION, VERSION,
};
use codec::{Decode, Encode};
use datahaven_runtime_common::{
@ -773,7 +773,7 @@ datahaven_runtime_common::impl_on_charge_evm_transaction!();
parameter_types! {
pub BlockGasLimit: U256
= U256::from(NORMAL_DISPATCH_RATIO * MAXIMUM_BLOCK_WEIGHT.ref_time() / WEIGHT_PER_GAS);
// pub PrecompilesValue: TemplatePrecompiles<Runtime> = TemplatePrecompiles::<_>::new();
pub PrecompilesValue: DataHavenPrecompiles<Runtime> = DataHavenPrecompiles::<_>::new();
pub WeightPerGas: Weight = Weight::from_parts(WEIGHT_PER_GAS, 0);
pub SuicideQuickClearLimit: u32 = 0;
/// The amount of gas per pov. A ratio of 16 if we convert ref_time to gas and we compare
@ -799,8 +799,8 @@ impl pallet_evm::Config for Runtime {
type AddressMapping = IdentityAddressMapping;
type Currency = Balances;
type RuntimeEvent = RuntimeEvent;
type PrecompilesType = ();
type PrecompilesValue = ();
type PrecompilesType = DataHavenPrecompiles<Self>;
type PrecompilesValue = PrecompilesValue;
type ChainId = EvmChainId;
type BlockGasLimit = BlockGasLimit;
type Runner = pallet_evm::runner::stack::Runner<Self>;

View file

@ -9,6 +9,7 @@ include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs"));
#[cfg(feature = "runtime-benchmarks")]
mod benchmarks;
pub mod configs;
pub mod precompiles;
pub mod weights;
// Re-export governance for tests
pub use configs::governance;

View file

@ -0,0 +1,67 @@
// Copyright 2019-2025 The DataHaven Team
// This file is part of DataHaven.
// DataHaven 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.
// DataHaven 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 DataHaven. If not, see <http://www.gnu.org/licenses/>.
use pallet_evm_precompile_blake2::Blake2F;
use pallet_evm_precompile_bn128::{Bn128Add, Bn128Mul, Bn128Pairing};
use pallet_evm_precompile_modexp::Modexp;
use pallet_evm_precompile_registry::PrecompileRegistry;
use pallet_evm_precompile_sha3fips::Sha3FIPS256;
use pallet_evm_precompile_simple::{ECRecover, ECRecoverPublicKey, Identity, Ripemd160, Sha256};
use precompile_utils::precompile_set::*;
type EthereumPrecompilesChecks = (AcceptDelegateCall, CallableByContract, CallableByPrecompile);
/// EVM precompiles available in the DataHaven Testnet runtime.
#[precompile_utils::precompile_name_from_address]
type DataHavenPrecompilesAt<R> = (
// Ethereum precompiles:
// We allow DELEGATECALL to stay compliant with Ethereum behavior.
PrecompileAt<AddressU64<1>, ECRecover, EthereumPrecompilesChecks>,
PrecompileAt<AddressU64<2>, Sha256, EthereumPrecompilesChecks>,
PrecompileAt<AddressU64<3>, Ripemd160, EthereumPrecompilesChecks>,
PrecompileAt<AddressU64<4>, Identity, EthereumPrecompilesChecks>,
PrecompileAt<AddressU64<5>, Modexp, EthereumPrecompilesChecks>,
PrecompileAt<AddressU64<6>, Bn128Add, EthereumPrecompilesChecks>,
PrecompileAt<AddressU64<7>, Bn128Mul, EthereumPrecompilesChecks>,
PrecompileAt<AddressU64<8>, Bn128Pairing, EthereumPrecompilesChecks>,
PrecompileAt<AddressU64<9>, Blake2F, EthereumPrecompilesChecks>,
// Non-DataHaven specific nor Ethereum precompiles :
PrecompileAt<AddressU64<1024>, Sha3FIPS256, (CallableByContract, CallableByPrecompile)>,
RemovedPrecompileAt<AddressU64<1025>>,
PrecompileAt<AddressU64<1026>, ECRecoverPublicKey, (CallableByContract, CallableByPrecompile)>,
RemovedPrecompileAt<AddressU64<1027>>,
// DataHaven specific precompiles:
PrecompileAt<
AddressU64<2069>,
PrecompileRegistry<R>,
(CallableByContract, CallableByPrecompile),
>,
);
/// The PrecompileSet installed in the DataHaven runtime.
/// We include the nine Istanbul precompiles
/// (https://github.com/ethereum/go-ethereum/blob/3c46f557/core/vm/contracts.go#L69)
/// The following distribution has been decided for the precompiles
/// 0-1023: Ethereum Mainnet Precompiles
/// 1024-2047 Precompiles that are not in Ethereum Mainnet but are neither DataHaven specific
/// 2048-4095 DataHaven specific precompiles
pub type DataHavenPrecompiles<R> = PrecompileSetBuilder<
R,
(
// Skip precompiles if out of range.
PrecompilesInRangeInclusive<(AddressU64<1>, AddressU64<4095>), DataHavenPrecompilesAt<R>>,
),
>;