Skip to content
Merged
Show file tree
Hide file tree
Changes from 25 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
29 changes: 29 additions & 0 deletions crates/anvil-polkadot/src/api_server/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,35 @@ 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();
let account_id = self
.client
.runtime_api()
.account_id(latest_block, H160::from_slice(address.as_slice()))
.map_err(Error::RuntimeApi);
account_id
.map(|inner| self.backend.inject_aura_authority(latest_block, inner))
.to_rpc_result()
}
EthRequest::EthCoinbase(()) => {
node_info!("eth_coinbase");
let latest_block = self.latest_block();
let authority =
self.backend.read_aura_authority(latest_block).map_err(Error::Backend);
authority
.and_then(|inner| {
self.client
.runtime_api()
.address(latest_block, inner)
.map_err(Error::RuntimeApi)
})
.map(|inner| Address::from(inner.to_fixed_bytes()))
.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"
);
}
}
33 changes: 33 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 aura authorities in the state")]
MissingAuraAuthorities,
#[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 aura authorities: {0}")]
DecodeAuraAuthorities(codec::Error),
}

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

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

let value =
self.read_top_state(hash, key.to_vec())?.ok_or(BackendError::MissingAuraAuthorities)?;
let authorities = <Vec<[u8; 32]>>::decode(&mut &value[..])
.map_err(BackendError::DecodeAuraAuthorities)?;
// Read the first authority, since that's what we modify via RPC, or at genesis,
// and instruct the runtime to pick via the consensus data provider, whenever it
// needs the block author.
let authority = *authorities.first().ok_or(BackendError::MissingAuraAuthorities)?;
Ok(authority.into())
}

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

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

pub fn inject_aura_authority(&self, at: Hash, aura_authority: AccountId) {
let mut overrides = self.overrides.lock();
overrides.set_coinbase(at, aura_authority);
}

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 +241,16 @@ impl StorageOverrides {
self.add(latest_block, changeset);
}

fn set_coinbase(&mut self, latest_block: Hash, aura_authority: AccountId) {
let mut changeset = BlockOverrides::default();
changeset.top.insert(
well_known_keys::AURA_AUTHORITIES.to_vec(),
Some(vec![aura_authority].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
57 changes: 57 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,57 @@
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. This will always use slot 0 (used to determine the
/// index of the AURA authority from the authorities set by AURA runtimes) for the aura
/// digest since anvil-polkadot node will be the sole block author and AURA will pick
/// only its configured address, residing at index 0 in the AURA authorities set. When
/// forking from an assethub chain, we expect an assethub runtime based on AURA,
/// which will pick the author based on the slot given through the digest, which will
/// also result in picking the AURA authority from index 0.
pub struct SameSlotConsensusDataProvider<B, P> {
_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
Loading
Loading