Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
38315a0
anvil-polkadot: implement anvil_setCoinbase
iulianbarbu Oct 21, 2025
2b27704
anvil-polkadot: added AURA based FindAuthor impl
iulianbarbu Oct 22, 2025
8001624
anvil-polkadot: add eth_coinbase and tests
iulianbarbu Oct 23, 2025
5656467
Merge branch 'master' into ib-add-genesis-coinbase-support
iulianbarbu Oct 23, 2025
016801f
anvil-polkadot: polish code
iulianbarbu Oct 23, 2025
93aed0f
anvil-polkadot: apply fmt
iulianbarbu Oct 23, 2025
b1552c4
anvil-polkadot(tests): test based on Multicall contract
iulianbarbu Oct 23, 2025
628af56
anvil-polkadot(tests): adjust thresholds for timestamp tests on revert
iulianbarbu Oct 23, 2025
85a1ac1
Merge branch 'master' into ib-add-genesis-coinbase-support
iulianbarbu Oct 24, 2025
b75dd8c
Update crates/anvil-polkadot/src/substrate_node/service/consensus.rs
iulianbarbu Oct 24, 2025
4aa4559
anvil-polkadot: address feedback part 1
iulianbarbu Oct 25, 2025
67c2819
anvil-polkadot: address feedback part 2
iulianbarbu Oct 25, 2025
ff3d218
anvil-polkadot: address feedback part 3
iulianbarbu Oct 25, 2025
c4060d3
anvil-polkadot: address feedback part 4
iulianbarbu Oct 26, 2025
8da38fa
anvil-polkadot(tests): removed unnecessary sol call conversions
iulianbarbu Oct 26, 2025
dbab1e7
anvil-polkadot(tests): remove other redundant SollCall conversions
iulianbarbu Oct 27, 2025
8a8d56d
anvil-polkadot: use Error::RuntimeApi
iulianbarbu Oct 27, 2025
30d1503
Update crates/anvil-polkadot/src/substrate_node/service/consensus.rs
iulianbarbu Oct 27, 2025
af2eda8
update to polkadot-sdk master (#352)
alindima Oct 24, 2025
8129cfe
Tracing support (#350)
pkhry Oct 26, 2025
d25535a
Revert "Tracing support (#350)"
iulianbarbu Oct 27, 2025
e63d18d
Revert "update to polkadot-sdk master (#352)"
iulianbarbu Oct 27, 2025
c8d6c14
Merge branch 'master' of github.com:paritytech/foundry-polkadot into …
iulianbarbu Oct 27, 2025
1d672ab
anvil-polkadot(tests): fix test
iulianbarbu Oct 27, 2025
fbf1abc
anvil-polkadot: fix rustfmt
iulianbarbu Oct 27, 2025
9d35a5b
anvil-polkadot: use pallet-aura FindAuthor todo comment
iulianbarbu Oct 28, 2025
c3451f6
Merge branch 'master' of github.com:paritytech/foundry-polkadot into …
iulianbarbu Oct 28, 2025
3c79ad9
anvil-polkadot: address G feedback
iulianbarbu Oct 28, 2025
e8da020
Merge branch 'master' into ib-add-genesis-coinbase-support
iulianbarbu Oct 28, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions crates/anvil-polkadot/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ polkadot-sdk = { git = "https://github.com/paritytech/polkadot-sdk.git", branch
"sc-client-api",
"sc-client-db",
"sc-consensus",
"sc-consensus-aura",
"sc-consensus-manual-seal",
"sc-executor",
"sc-executor-common",
Expand All @@ -55,6 +56,7 @@ polkadot-sdk = { git = "https://github.com/paritytech/polkadot-sdk.git", branch
"sc-utils",
"sp-blockchain",
"sp-consensus",
"sp-consensus-slots",
"sp-core-hashing",
"sp-core-hashing-proc-macro",
"sp-database",
Expand Down
13 changes: 13 additions & 0 deletions crates/anvil-polkadot/src/api_server/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,19 @@ impl ApiServer {
EthRequest::EvmMine(mine) => self.evm_mine(mine).await.to_rpc_result(),
EthRequest::EvmMineDetailed(mine) => self.evm_mine_detailed(mine).await.to_rpc_result(),

// ---- Coinbase ----
EthRequest::SetCoinbase(address) => {
node_info!("anvil_setCoinbase");
let latest_block = self.latest_block();
self.backend.inject_coinbase(latest_block, address);
Ok(()).to_rpc_result()
}
EthRequest::EthCoinbase(()) => {
node_info!("eth_coinbase");
let latest_block = self.latest_block();
self.backend.read_coinbase(latest_block).map_err(Error::Backend).to_rpc_result()
}

//------- TimeMachine---------
EthRequest::EvmSetBlockTimeStampInterval(time) => {
self.set_block_timestamp_interval(time).to_rpc_result()
Expand Down
26 changes: 24 additions & 2 deletions crates/anvil-polkadot/src/substrate_node/genesis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ pub struct GenesisConfig {
pub base_fee_per_gas: u64,
/// The genesis header gas limit.
pub gas_limit: Option<u128>,
/// Coinbase address
pub coinbase: Option<Address>,
}

impl<'a> From<&'a AnvilNodeConfig> for GenesisConfig {
Expand All @@ -50,16 +52,22 @@ impl<'a> From<&'a AnvilNodeConfig> for GenesisConfig {
.expect("Genesis block number overflow"),
base_fee_per_gas: anvil_config.get_base_fee(),
gas_limit: anvil_config.gas_limit,
coinbase: anvil_config.genesis.as_ref().map(|g| g.coinbase),
}
}
}

impl GenesisConfig {
pub fn as_storage_key_value(&self) -> Vec<(Vec<u8>, Vec<u8>)> {
let mut aura_authority_id = [0xEE; 32];
aura_authority_id[..20].copy_from_slice(
self.coinbase.as_ref().map(|addr| addr.0.as_slice()).unwrap_or(&[0; 20]),
);
let storage = vec![
(well_known_keys::CHAIN_ID.to_vec(), self.chain_id.encode()),
(well_known_keys::TIMESTAMP.to_vec(), self.timestamp.encode()),
(well_known_keys::BLOCK_NUMBER_KEY.to_vec(), self.number.encode()),
(well_known_keys::AURA_AUTHORITIES.to_vec(), vec![aura_authority_id].encode()),
];
// TODO: add other fields
storage
Expand Down Expand Up @@ -171,8 +179,14 @@ mod tests {
let block_number: u32 = 5;
let timestamp: u64 = 10;
let chain_id: u64 = 42;
let genesis_config =
GenesisConfig { number: block_number, timestamp, chain_id, ..Default::default() };
let authority_id: [u8; 32] = [0xEE; 32];
let genesis_config = GenesisConfig {
number: block_number,
timestamp,
chain_id,
coinbase: Some(Address::from([0xEE; 20])),
..Default::default()
};
let genesis_storage = genesis_config.as_storage_key_value();
assert!(
genesis_storage
Expand All @@ -187,5 +201,13 @@ mod tests {
genesis_storage.contains(&(well_known_keys::CHAIN_ID.to_vec(), chain_id.encode())),
"Chain id not found in genesis key-value storage"
);

assert!(
genesis_storage.contains(&(
well_known_keys::AURA_AUTHORITIES.to_vec(),
vec![authority_id].encode()
)),
"Authorities not found in genesis key-value storage"
);
}
}
31 changes: 31 additions & 0 deletions crates/anvil-polkadot/src/substrate_node/service/backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ pub enum BackendError {
MissingTotalIssuance,
#[error("Could not find chain id in the state")]
MissingChainId,
#[error("Could not find coinbase in the state")]
MissingCoinbase,
#[error("Could not find timestamp in the state")]
MissingTimestamp,
#[error("Unable to decode total issuance {0}")]
Expand All @@ -42,6 +44,8 @@ pub enum BackendError {
DecodeCodeInfo(codec::Error),
#[error("Unable to decode timestamp: {0}")]
DecodeTimestamp(codec::Error),
#[error("Unable to decode coinbase: {0}")]
DecodeCoinbase(codec::Error),
}

type Result<T> = std::result::Result<T, BackendError>;
Expand Down Expand Up @@ -75,6 +79,17 @@ impl BackendWithOverlay {
u64::decode(&mut &value[..]).map_err(BackendError::DecodeChainId)
}

pub fn read_coinbase(&self, hash: Hash) -> Result<Address> {
let key = well_known_keys::AURA_AUTHORITIES;

let value =
self.read_top_state(hash, key.to_vec())?.ok_or(BackendError::MissingCoinbase)?;
let authorities =
<Vec<[u8; 32]>>::decode(&mut &value[..]).map_err(BackendError::DecodeCoinbase)?;
let authority = authorities.first().ok_or(BackendError::MissingCoinbase)?;
Ok(Address::from_slice(&authority[..20]))
}

pub fn read_total_issuance(&self, hash: Hash) -> Result<Balance> {
let key = well_known_keys::TOTAL_ISSUANCE;

Expand Down Expand Up @@ -136,6 +151,11 @@ impl BackendWithOverlay {
overrides.set_chain_id(at, chain_id);
}

pub fn inject_coinbase(&self, at: Hash, coinbase: Address) {
let mut overrides = self.overrides.lock();
overrides.set_coinbase(at, coinbase);
}

pub fn inject_total_issuance(&self, at: Hash, value: Balance) {
let mut overrides = self.overrides.lock();
overrides.set_total_issuance(at, value);
Expand Down Expand Up @@ -218,6 +238,17 @@ impl StorageOverrides {
self.add(latest_block, changeset);
}

fn set_coinbase(&mut self, latest_block: Hash, coinbase: Address) {
let mut changeset = BlockOverrides::default();
let mut account_id = [0xEE; 32];
account_id[..20].copy_from_slice(coinbase.0.as_slice());
changeset
.top
.insert(well_known_keys::AURA_AUTHORITIES.to_vec(), Some(vec![account_id].encode()));

self.add(latest_block, changeset);
}

#[allow(unused)]
fn set_timestamp(&mut self, latest_block: Hash, timestamp: u64) {
let mut changeset = BlockOverrides::default();
Expand Down
52 changes: 52 additions & 0 deletions crates/anvil-polkadot/src/substrate_node/service/consensus.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
use polkadot_sdk::{
sc_consensus::BlockImportParams,
sc_consensus_aura::CompatibleDigestItem,
sc_consensus_manual_seal::{ConsensusDataProvider, Error},
sp_consensus_aura::ed25519::AuthoritySignature,
sp_consensus_babe::Slot,
sp_inherents::InherentData,
sp_runtime::{Digest, DigestItem, traits::Block as BlockT},
};
use std::marker::PhantomData;

/// Consensus data provider for Aura.
pub struct SameSlotConsensusDataProvider<B, P> {
// slot duration
_phantom: PhantomData<(B, P)>,
}

impl<B, P> SameSlotConsensusDataProvider<B, P> {
pub fn new() -> Self {
Self { _phantom: PhantomData }
}
}

impl<B, P> ConsensusDataProvider<B> for SameSlotConsensusDataProvider<B, P>
where
B: BlockT,
P: Send + Sync,
{
type Proof = P;

fn create_digest(
&self,
_parent: &B::Header,
_inherents: &InherentData,
) -> Result<Digest, Error> {
let digest_item = <DigestItem as CompatibleDigestItem<AuthoritySignature>>::aura_pre_digest(
Slot::default(),
);

Ok(Digest { logs: vec![digest_item] })
}

fn append_block_import(
&self,
_parent: &B::Header,
_params: &mut BlockImportParams<B>,
_inherents: &InherentData,
_proof: Self::Proof,
) -> Result<(), Error> {
Ok(())
}
}
7 changes: 5 additions & 2 deletions crates/anvil-polkadot/src/substrate_node/service/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@ use crate::{
substrate_node::{
mining_engine::{MiningEngine, MiningMode, run_mining_engine},
rpc::spawn_rpc_server,
service::consensus::SameSlotConsensusDataProvider,
},
};
use anvil::eth::backend::time::TimeManager;
use parking_lot::Mutex;
use polkadot_sdk::{
parachains_common::opaque::Block,
sc_basic_authorship, sc_consensus, sc_consensus_manual_seal,
sc_basic_authorship, sc_consensus,
sc_consensus_manual_seal::{self},
sc_service::{
self, Configuration, RpcHandlers, SpawnTaskHandle, TaskManager,
error::Error as ServiceError,
Expand All @@ -24,6 +26,7 @@ pub use client::Client;

mod backend;
mod client;
mod consensus;
mod executor;
pub mod storage;

Expand Down Expand Up @@ -136,7 +139,7 @@ pub fn new(
pool: transaction_pool.clone(),
select_chain: SelectChain::new(backend.clone()),
commands_stream: Box::pin(commands_stream),
consensus_data_provider: None,
consensus_data_provider: Some(Box::new(SameSlotConsensusDataProvider::new())),
create_inherent_data_providers,
};
let authorship_future = sc_consensus_manual_seal::run_manual_seal(params);
Expand Down
6 changes: 6 additions & 0 deletions crates/anvil-polkadot/src/substrate_node/service/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,12 @@ pub mod well_known_keys {
154u8, 202u8, 73u8, 131u8, 172u8,
];

//twox_128(b"Aura" + b"Authorities")
pub const AURA_AUTHORITIES: [u8; 32] = [
87, 248, 220, 47, 90, 176, 148, 103, 137, 111, 71, 48, 15, 4, 36, 56, 94, 6, 33, 196, 134,
154, 166, 12, 2, 190, 154, 220, 201, 138, 13, 29,
];

pub fn system_account_info(account_id: AccountId) -> Vec<u8> {
let mut key = Vec::new();
key.extend_from_slice(&twox_128("System".as_bytes()));
Expand Down
2 changes: 2 additions & 0 deletions crates/anvil-polkadot/substrate-runtime/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ license.workspace = true
array-bytes = { version = "6.2.2", default-features = false }
codec = { version = "3.7.5", default-features = false, package = "parity-scale-codec" }
polkadot-sdk = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "master", default-features = false, features = [
"pallet-aura",
"pallet-balances",
"pallet-revive",
"pallet-sudo",
Expand All @@ -21,6 +22,7 @@ polkadot-sdk = { git = "https://github.com/paritytech/polkadot-sdk.git", branch
"pallet-transaction-payment-rpc-runtime-api",
"parachains-common",
"runtime",
"sp-consensus-aura",
"with-tracing",
] }
scale-info = { version = "2.11.6", default-features = false }
Expand Down
51 changes: 50 additions & 1 deletion crates/anvil-polkadot/substrate-runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs"));

extern crate alloc;

use crate::sp_runtime::ConsensusEngineId;
use alloc::{vec, vec::Vec};
use currency::*;
use frame_support::weights::{
Expand All @@ -17,11 +18,16 @@ use frame_system::limits::BlockWeights;
use pallet_revive::{AccountId32Mapper, evm::runtime::EthExtra};
use pallet_transaction_payment::{FeeDetails, RuntimeDispatchInfo};
use polkadot_sdk::{
parachains_common::{AccountId, BlockNumber, Hash as CommonHash, Header, Nonce, Signature},
parachains_common::{
AccountId, AssetHubPolkadotAuraId as AuraId, BlockNumber, Hash as CommonHash, Header,
Nonce, Signature,
},
polkadot_sdk_frame::{
deps::sp_genesis_builder,
runtime::{apis, prelude::*},
traits::FindAuthor,
},
sp_consensus_aura::{self, SlotDuration, runtime_decl_for_aura_api::AuraApiV1},
*,
};

Expand Down Expand Up @@ -222,6 +228,20 @@ mod runtime {
/// Provides the ability to execute Smart Contracts.
#[runtime::pallet_index(5)]
pub type Revive = pallet_revive::Pallet<Runtime>;

/// Provides the ability to determine AURA authorities for block building.
#[runtime::pallet_index(6)]
pub type Aura = pallet_aura::Pallet<Runtime>;
}

impl pallet_aura::Config for Runtime {
type AuthorityId = AuraId;
type DisabledValidators = ();
type MaxAuthorities = ConstU32<1>;
type AllowMultipleBlocksPerSlot = ConstBool<true>;
// Not relevant in general since the node digest
// will refer to slot 0 always.
type SlotDuration = ConstU64<6000>;
}

/// We assume that ~10% of the block weight is consumed by `on_initialize` handlers.
Expand Down Expand Up @@ -304,12 +324,25 @@ parameter_types! {
pub storage ChainId: u64 = 420_420_420;
}

pub struct BlockAuthor;
impl FindAuthor<AccountId> for BlockAuthor {
fn find_author<'a, I>(_digests: I) -> Option<AccountId>
where
I: 'a + IntoIterator<Item = (ConsensusEngineId, &'a [u8])>,
{
let authorities = Runtime::authorities();
let key = authorities[0].clone().into_inner();
Some(key.into())
}
}

#[derive_impl(pallet_revive::config_preludes::TestDefaultConfig)]
impl pallet_revive::Config for Runtime {
type AddressMapper = AccountId32Mapper<Self>;
type ChainId = ChainId;
type CodeHashLockupDepositPercent = CodeHashLockupDepositPercent;
type Currency = Balances;
type FindAuthor = BlockAuthor;
type NativeToEthRatio = ConstU32<1_000_000>;
type UploadOrigin = EnsureSigned<Self::AccountId>;
type InstantiateOrigin = EnsureSigned<Self::AccountId>;
Expand Down Expand Up @@ -435,4 +468,20 @@ pallet_revive::impl_runtime_apis_plus_revive!(
self::genesis_config_presets::preset_names()
}
}

impl sp_consensus_aura::AuraApi<Block, AuraId> for Runtime {
fn slot_duration() -> SlotDuration {
// This is not relevant when considering a manual-seal
// driven node. The slot duration is used by Aura to determine
// the authority, but anvil-polkadot will provide a single slot
// always, not taking into consideration computing based on this
// runtime API.
SlotDuration::from_millis(6000)
}

fn authorities() -> Vec<AuraId> {
pallet_aura::Authorities::<Runtime>::get().into_inner()
}
}

);
Loading
Loading