mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 10:59:55 +00:00
feat: add a --dev option (#3866)
This commit is contained in:
99
bin/reth/src/args/dev_args.rs
Normal file
99
bin/reth/src/args/dev_args.rs
Normal file
@ -0,0 +1,99 @@
|
||||
//! clap [Args](clap::Args) for Dev testnet configuration
|
||||
use std::time::Duration;
|
||||
|
||||
use clap::Args;
|
||||
use humantime::parse_duration;
|
||||
|
||||
/// Parameters for Dev testnet configuration
|
||||
#[derive(Debug, Args, PartialEq, Default, Clone, Copy)]
|
||||
#[command(next_help_heading = "Dev testnet")]
|
||||
pub struct DevArgs {
|
||||
/// Start the node in dev mode
|
||||
///
|
||||
/// This mode uses a local proof-of-authority consensus engine with either fixed block times
|
||||
/// or automatically mined blocks.
|
||||
/// Disables network discovery and enables local http server.
|
||||
/// Prefunds 20 accounts derived by mnemonic "test test test test test test test test test test
|
||||
/// test junk" with 10 000 ETH each.
|
||||
#[arg(long = "dev", alias = "auto-mine", help_heading = "Dev testnet", verbatim_doc_comment)]
|
||||
pub dev: bool,
|
||||
|
||||
/// How many transactions to mine per block.
|
||||
#[arg(
|
||||
long = "dev.block_max_transactions",
|
||||
help_heading = "Dev testnet",
|
||||
conflicts_with = "block_time"
|
||||
)]
|
||||
pub block_max_transactions: Option<usize>,
|
||||
|
||||
/// Interval between blocks.
|
||||
///
|
||||
/// Parses strings using [humantime::parse_duration]
|
||||
/// --dev.block_time 12s
|
||||
#[arg(
|
||||
long = "dev.block_time",
|
||||
help_heading = "Dev testnet",
|
||||
conflicts_with = "block_max_transactions",
|
||||
value_parser = parse_duration,
|
||||
verbatim_doc_comment
|
||||
)]
|
||||
pub block_time: Option<Duration>,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use clap::Parser;
|
||||
|
||||
/// A helper type to parse Args more easily
|
||||
#[derive(Parser)]
|
||||
struct CommandParser<T: Args> {
|
||||
#[clap(flatten)]
|
||||
args: T,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_dev_args() {
|
||||
let args = CommandParser::<DevArgs>::parse_from(["reth"]).args;
|
||||
assert_eq!(args, DevArgs { dev: false, block_max_transactions: None, block_time: None });
|
||||
|
||||
let args = CommandParser::<DevArgs>::parse_from(["reth", "--dev"]).args;
|
||||
assert_eq!(args, DevArgs { dev: true, block_max_transactions: None, block_time: None });
|
||||
|
||||
let args = CommandParser::<DevArgs>::parse_from(["reth", "--auto-mine"]).args;
|
||||
assert_eq!(args, DevArgs { dev: true, block_max_transactions: None, block_time: None });
|
||||
|
||||
let args = CommandParser::<DevArgs>::parse_from([
|
||||
"reth",
|
||||
"--dev",
|
||||
"--dev.block_max_transactions",
|
||||
"2",
|
||||
])
|
||||
.args;
|
||||
assert_eq!(args, DevArgs { dev: true, block_max_transactions: Some(2), block_time: None });
|
||||
|
||||
let args =
|
||||
CommandParser::<DevArgs>::parse_from(["reth", "--dev", "--dev.block_time", "1s"]).args;
|
||||
assert_eq!(
|
||||
args,
|
||||
DevArgs {
|
||||
dev: true,
|
||||
block_max_transactions: None,
|
||||
block_time: Some(std::time::Duration::from_secs(1))
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_dev_args_conflicts() {
|
||||
let args = CommandParser::<DevArgs>::try_parse_from([
|
||||
"reth",
|
||||
"--dev",
|
||||
"--dev.block_max_transactions",
|
||||
"2",
|
||||
"--dev.block_time",
|
||||
"1s",
|
||||
]);
|
||||
assert!(args.is_err());
|
||||
}
|
||||
}
|
||||
@ -35,4 +35,8 @@ pub use gas_price_oracle_args::GasPriceOracleArgs;
|
||||
mod txpool_args;
|
||||
pub use txpool_args::TxPoolArgs;
|
||||
|
||||
/// DevArgs for configuring the dev testnet
|
||||
mod dev_args;
|
||||
pub use dev_args::DevArgs;
|
||||
|
||||
pub mod utils;
|
||||
|
||||
@ -110,7 +110,7 @@ impl NetworkArgs {
|
||||
#[derive(Debug, Args)]
|
||||
pub struct DiscoveryArgs {
|
||||
/// Disable the discovery service.
|
||||
#[arg(short, long)]
|
||||
#[arg(short, long, default_value_if("dev", "true", "true"))]
|
||||
pub disable_discovery: bool,
|
||||
|
||||
/// Disable the DNS discovery.
|
||||
|
||||
@ -56,7 +56,7 @@ pub(crate) const RPC_DEFAULT_MAX_TRACING_REQUESTS: u32 = 25;
|
||||
#[command(next_help_heading = "RPC")]
|
||||
pub struct RpcServerArgs {
|
||||
/// Enable the HTTP-RPC server
|
||||
#[arg(long)]
|
||||
#[arg(long, default_value_if("dev", "true", "true"))]
|
||||
pub http: bool,
|
||||
|
||||
/// Http server address to listen on
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
//! Clap parser utilities
|
||||
|
||||
use reth_primitives::{
|
||||
fs, AllGenesisFormats, BlockHashOrNumber, ChainSpec, GOERLI, MAINNET, SEPOLIA,
|
||||
fs, AllGenesisFormats, BlockHashOrNumber, ChainSpec, DEV, GOERLI, MAINNET, SEPOLIA,
|
||||
};
|
||||
use reth_revm::primitives::B256 as H256;
|
||||
use std::{
|
||||
@ -25,6 +25,7 @@ pub fn chain_spec_value_parser(s: &str) -> eyre::Result<Arc<ChainSpec>, eyre::Er
|
||||
"mainnet" => MAINNET.clone(),
|
||||
"goerli" => GOERLI.clone(),
|
||||
"sepolia" => SEPOLIA.clone(),
|
||||
"dev" => DEV.clone(),
|
||||
_ => {
|
||||
let raw = fs::read_to_string(PathBuf::from(shellexpand::full(s)?.into_owned()))?;
|
||||
serde_json::from_str(&raw)?
|
||||
@ -39,6 +40,7 @@ pub fn genesis_value_parser(s: &str) -> eyre::Result<Arc<ChainSpec>, eyre::Error
|
||||
"mainnet" => MAINNET.clone(),
|
||||
"goerli" => GOERLI.clone(),
|
||||
"sepolia" => SEPOLIA.clone(),
|
||||
"dev" => DEV.clone(),
|
||||
_ => {
|
||||
let raw = fs::read_to_string(PathBuf::from(shellexpand::full(s)?.into_owned()))?;
|
||||
let genesis: AllGenesisFormats = serde_json::from_str(&raw)?;
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
//!
|
||||
//! Starts the client
|
||||
use crate::{
|
||||
args::{get_secret_key, DebugArgs, NetworkArgs, RpcServerArgs, TxPoolArgs},
|
||||
args::{get_secret_key, DebugArgs, DevArgs, NetworkArgs, RpcServerArgs, TxPoolArgs},
|
||||
dirs::DataDirPath,
|
||||
init::init_genesis,
|
||||
prometheus_exporter,
|
||||
@ -14,7 +14,7 @@ use clap::Parser;
|
||||
use eyre::Context;
|
||||
use fdlimit::raise_fd_limit;
|
||||
use futures::{future::Either, pin_mut, stream, stream_select, StreamExt};
|
||||
use reth_auto_seal_consensus::{AutoSealBuilder, AutoSealConsensus};
|
||||
use reth_auto_seal_consensus::{AutoSealBuilder, AutoSealConsensus, MiningMode};
|
||||
use reth_basic_payload_builder::{BasicPayloadJobGenerator, BasicPayloadJobGeneratorConfig};
|
||||
use reth_beacon_consensus::{BeaconConsensus, BeaconConsensusEngine, MIN_BLOCKS_FOR_PIPELINE_RUN};
|
||||
use reth_blockchain_tree::{
|
||||
@ -112,12 +112,15 @@ pub struct Command {
|
||||
/// - mainnet
|
||||
/// - goerli
|
||||
/// - sepolia
|
||||
/// - dev
|
||||
#[arg(
|
||||
long,
|
||||
value_name = "CHAIN_OR_PATH",
|
||||
verbatim_doc_comment,
|
||||
default_value = "mainnet",
|
||||
value_parser = genesis_value_parser
|
||||
default_value_if("dev", "true", "dev"),
|
||||
value_parser = genesis_value_parser,
|
||||
required = false,
|
||||
)]
|
||||
chain: Arc<ChainSpec>,
|
||||
|
||||
@ -145,9 +148,8 @@ pub struct Command {
|
||||
#[clap(flatten)]
|
||||
db: DatabaseArgs,
|
||||
|
||||
/// Automatically mine blocks for new transactions
|
||||
#[arg(long)]
|
||||
auto_mine: bool,
|
||||
#[clap(flatten)]
|
||||
dev: DevArgs,
|
||||
}
|
||||
|
||||
impl Command {
|
||||
@ -181,7 +183,7 @@ impl Command {
|
||||
|
||||
info!(target: "reth::cli", "{}", DisplayHardforks::from(self.chain.hardforks().clone()));
|
||||
|
||||
let consensus: Arc<dyn Consensus> = if self.auto_mine {
|
||||
let consensus: Arc<dyn Consensus> = if self.dev.dev {
|
||||
debug!(target: "reth::cli", "Using auto seal");
|
||||
Arc::new(AutoSealConsensus::new(Arc::clone(&self.chain)))
|
||||
} else {
|
||||
@ -304,13 +306,28 @@ impl Command {
|
||||
};
|
||||
|
||||
// Configure the pipeline
|
||||
let (mut pipeline, client) = if self.auto_mine {
|
||||
let (mut pipeline, client) = if self.dev.dev {
|
||||
info!(target: "reth::cli", "Starting Reth in dev mode");
|
||||
|
||||
let mining_mode = if let Some(interval) = self.dev.block_time {
|
||||
MiningMode::interval(interval)
|
||||
} else if let Some(max_transactions) = self.dev.block_max_transactions {
|
||||
MiningMode::instant(
|
||||
max_transactions,
|
||||
transaction_pool.pending_transactions_listener(),
|
||||
)
|
||||
} else {
|
||||
info!(target: "reth::cli", "No mining mode specified, defaulting to ReadyTransaction");
|
||||
MiningMode::instant(1, transaction_pool.pending_transactions_listener())
|
||||
};
|
||||
|
||||
let (_, client, mut task) = AutoSealBuilder::new(
|
||||
Arc::clone(&self.chain),
|
||||
blockchain_db.clone(),
|
||||
transaction_pool.clone(),
|
||||
consensus_engine_tx.clone(),
|
||||
canon_state_notification_sender,
|
||||
mining_mode,
|
||||
)
|
||||
.build();
|
||||
|
||||
@ -798,6 +815,8 @@ async fn run_network_until_shutdown<C>(
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use reth_primitives::DEV;
|
||||
|
||||
use super::*;
|
||||
use std::{net::IpAddr, path::Path};
|
||||
|
||||
@ -869,4 +888,22 @@ mod tests {
|
||||
let db_path = data_dir.db_path();
|
||||
assert_eq!(db_path, Path::new("my/custom/path/db"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_dev() {
|
||||
let cmd = Command::parse_from(["reth", "--dev"]);
|
||||
let chain = DEV.clone();
|
||||
assert_eq!(cmd.chain.chain, chain.chain);
|
||||
assert_eq!(cmd.chain.genesis_hash, chain.genesis_hash);
|
||||
assert_eq!(
|
||||
cmd.chain.paris_block_and_final_difficulty,
|
||||
chain.paris_block_and_final_difficulty
|
||||
);
|
||||
assert_eq!(cmd.chain.hardforks, chain.hardforks);
|
||||
|
||||
assert!(cmd.rpc.http);
|
||||
assert!(cmd.network.discovery.disable_discovery);
|
||||
|
||||
assert!(cmd.dev.dev);
|
||||
}
|
||||
}
|
||||
|
||||
@ -115,13 +115,13 @@ where
|
||||
pool: Pool,
|
||||
to_engine: UnboundedSender<BeaconEngineMessage>,
|
||||
canon_state_notification: CanonStateNotificationSender,
|
||||
mode: MiningMode,
|
||||
) -> Self {
|
||||
let latest_header = client
|
||||
.latest_header()
|
||||
.ok()
|
||||
.flatten()
|
||||
.unwrap_or_else(|| chain_spec.sealed_genesis_header());
|
||||
let mode = MiningMode::interval(std::time::Duration::from_secs(1));
|
||||
|
||||
Self {
|
||||
storage: Storage::new(latest_header),
|
||||
|
||||
75
crates/primitives/res/genesis/dev.json
Normal file
75
crates/primitives/res/genesis/dev.json
Normal file
@ -0,0 +1,75 @@
|
||||
{
|
||||
"nonce": "0x0",
|
||||
"timestamp": "0x6490fdd2",
|
||||
"extraData": "0x",
|
||||
"gasLimit": "0x1c9c380",
|
||||
"difficulty": "0x0",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"coinbase": "0x0000000000000000000000000000000000000000",
|
||||
"stateRoot": "0x5eb6e371a698b8d68f665192350ffcecbbbf322916f4b51bd79bb6887da3f494",
|
||||
"alloc": {
|
||||
"0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266": {
|
||||
"balance": "0xD3C21BCECCEDA1000000"
|
||||
},
|
||||
"0x70997970C51812dc3A010C7d01b50e0d17dc79C8": {
|
||||
"balance": "0xD3C21BCECCEDA1000000"
|
||||
},
|
||||
"0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC": {
|
||||
"balance": "0xD3C21BCECCEDA1000000"
|
||||
},
|
||||
"0x90F79bf6EB2c4f870365E785982E1f101E93b906": {
|
||||
"balance": "0xD3C21BCECCEDA1000000"
|
||||
},
|
||||
"0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65": {
|
||||
"balance": "0xD3C21BCECCEDA1000000"
|
||||
},
|
||||
"0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc": {
|
||||
"balance": "0xD3C21BCECCEDA1000000"
|
||||
},
|
||||
"0x976EA74026E726554dB657fA54763abd0C3a0aa9": {
|
||||
"balance": "0xD3C21BCECCEDA1000000"
|
||||
},
|
||||
"0x14dC79964da2C08b23698B3D3cc7Ca32193d9955": {
|
||||
"balance": "0xD3C21BCECCEDA1000000"
|
||||
},
|
||||
"0x23618e81E3f5cdF7f54C3d65f7FBc0aBf5B21E8f": {
|
||||
"balance": "0xD3C21BCECCEDA1000000"
|
||||
},
|
||||
"0xa0Ee7A142d267C1f36714E4a8F75612F20a79720": {
|
||||
"balance": "0xD3C21BCECCEDA1000000"
|
||||
},
|
||||
"0xBcd4042DE499D14e55001CcbB24a551F3b954096": {
|
||||
"balance": "0xD3C21BCECCEDA1000000"
|
||||
},
|
||||
"0x71bE63f3384f5fb98995898A86B02Fb2426c5788": {
|
||||
"balance": "0xD3C21BCECCEDA1000000"
|
||||
},
|
||||
"0xFABB0ac9d68B0B445fB7357272Ff202C5651694a": {
|
||||
"balance": "0xD3C21BCECCEDA1000000"
|
||||
},
|
||||
"0x1CBd3b2770909D4e10f157cABC84C7264073C9Ec": {
|
||||
"balance": "0xD3C21BCECCEDA1000000"
|
||||
},
|
||||
"0xdF3e18d64BC6A983f673Ab319CCaE4f1a57C7097": {
|
||||
"balance": "0xD3C21BCECCEDA1000000"
|
||||
},
|
||||
"0xcd3B766CCDd6AE721141F452C550Ca635964ce71": {
|
||||
"balance": "0xD3C21BCECCEDA1000000"
|
||||
},
|
||||
"0x2546BcD3c84621e976D8185a91A922aE77ECEc30": {
|
||||
"balance": "0xD3C21BCECCEDA1000000"
|
||||
},
|
||||
"0xbDA5747bFD65F08deb54cb465eB87D40e51B197E": {
|
||||
"balance": "0xD3C21BCECCEDA1000000"
|
||||
},
|
||||
"0xdD2FD4581271e230360230F9337D5c0430Bf44C0": {
|
||||
"balance": "0xD3C21BCECCEDA1000000"
|
||||
},
|
||||
"0x8626f6940E2eb28930eFb4CeF49B2d1F2C9C1199": {
|
||||
"balance": "0xD3C21BCECCEDA1000000"
|
||||
}
|
||||
},
|
||||
"number": "0x0",
|
||||
"gasUsed": "0x0",
|
||||
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000"
|
||||
}
|
||||
@ -11,7 +11,7 @@ use std::{fmt, str::FromStr};
|
||||
mod spec;
|
||||
pub use spec::{
|
||||
AllGenesisFormats, ChainSpec, ChainSpecBuilder, DisplayHardforks, ForkCondition,
|
||||
ForkTimestamps, GOERLI, MAINNET, SEPOLIA,
|
||||
ForkTimestamps, DEV, GOERLI, MAINNET, SEPOLIA,
|
||||
};
|
||||
|
||||
// The chain info module.
|
||||
@ -44,6 +44,11 @@ impl Chain {
|
||||
Chain::Named(ethers_core::types::Chain::Sepolia)
|
||||
}
|
||||
|
||||
/// Returns the dev chain.
|
||||
pub const fn dev() -> Self {
|
||||
Chain::Named(ethers_core::types::Chain::Dev)
|
||||
}
|
||||
|
||||
/// The id of the chain
|
||||
pub fn id(&self) -> u64 {
|
||||
match self {
|
||||
|
||||
@ -130,6 +130,43 @@ pub static SEPOLIA: Lazy<Arc<ChainSpec>> = Lazy::new(|| {
|
||||
.into()
|
||||
});
|
||||
|
||||
/// Dev testnet specification
|
||||
///
|
||||
/// Includes 20 prefunded accounts with 10_000 ETH each derived from mnemonic "test test test test
|
||||
/// test test test test test test test junk".
|
||||
pub static DEV: Lazy<Arc<ChainSpec>> = Lazy::new(|| {
|
||||
ChainSpec {
|
||||
chain: Chain::dev(),
|
||||
genesis: serde_json::from_str(include_str!("../../res/genesis/dev.json"))
|
||||
.expect("Can't deserialize Dev testnet genesis json"),
|
||||
genesis_hash: Some(H256(hex!(
|
||||
"2f980576711e3617a5e4d83dd539548ec0f7792007d505a3d2e9674833af2d7c"
|
||||
))),
|
||||
paris_block_and_final_difficulty: Some((0, U256::from(0))),
|
||||
fork_timestamps: ForkTimestamps::default().shanghai(0),
|
||||
hardforks: BTreeMap::from([
|
||||
(Hardfork::Frontier, ForkCondition::Block(0)),
|
||||
(Hardfork::Homestead, ForkCondition::Block(0)),
|
||||
(Hardfork::Dao, ForkCondition::Block(0)),
|
||||
(Hardfork::Tangerine, ForkCondition::Block(0)),
|
||||
(Hardfork::SpuriousDragon, ForkCondition::Block(0)),
|
||||
(Hardfork::Byzantium, ForkCondition::Block(0)),
|
||||
(Hardfork::Constantinople, ForkCondition::Block(0)),
|
||||
(Hardfork::Petersburg, ForkCondition::Block(0)),
|
||||
(Hardfork::Istanbul, ForkCondition::Block(0)),
|
||||
(Hardfork::MuirGlacier, ForkCondition::Block(0)),
|
||||
(Hardfork::Berlin, ForkCondition::Block(0)),
|
||||
(Hardfork::London, ForkCondition::Block(0)),
|
||||
(
|
||||
Hardfork::Paris,
|
||||
ForkCondition::TTD { fork_block: Some(0), total_difficulty: U256::from(0) },
|
||||
),
|
||||
(Hardfork::Shanghai, ForkCondition::Timestamp(0)),
|
||||
]),
|
||||
}
|
||||
.into()
|
||||
});
|
||||
|
||||
/// An Ethereum chain specification.
|
||||
///
|
||||
/// A chain specification describes:
|
||||
@ -887,8 +924,8 @@ where
|
||||
mod tests {
|
||||
use crate::{
|
||||
Address, AllGenesisFormats, Chain, ChainSpec, ChainSpecBuilder, DisplayHardforks,
|
||||
ForkCondition, ForkHash, ForkId, Genesis, Hardfork, Head, GOERLI, H256, MAINNET, SEPOLIA,
|
||||
U256,
|
||||
ForkCondition, ForkHash, ForkId, Genesis, Hardfork, Head, DEV, GOERLI, H256, MAINNET,
|
||||
SEPOLIA, U256,
|
||||
};
|
||||
use bytes::BytesMut;
|
||||
use ethers_core::types as EtherType;
|
||||
@ -1188,6 +1225,17 @@ Post-merge hard forks (timestamp based):
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dev_forkids() {
|
||||
test_fork_ids(
|
||||
&DEV,
|
||||
&[(
|
||||
Head { number: 0, ..Default::default() },
|
||||
ForkId { hash: ForkHash([0x45, 0xb8, 0x36, 0x12]), next: 0 },
|
||||
)],
|
||||
)
|
||||
}
|
||||
|
||||
/// Checks that time-based forks work
|
||||
///
|
||||
/// This is based off of the test vectors here: https://github.com/ethereum/go-ethereum/blob/5c8cc10d1e05c23ff1108022f4150749e73c0ca1/core/forkid/forkid_test.go#L155-L188
|
||||
|
||||
@ -77,6 +77,10 @@ pub const GOERLI_GENESIS: H256 =
|
||||
pub const SEPOLIA_GENESIS: H256 =
|
||||
H256(hex!("25a5cc106eea7138acab33231d7160d69cb777ee0c2c553fcddf5138993e6dd9"));
|
||||
|
||||
/// Testnet genesis hash.
|
||||
pub const DEV_GENESIS: H256 =
|
||||
H256(hex!("2f980576711e3617a5e4d83dd539548ec0f7792007d505a3d2e9674833af2d7c"));
|
||||
|
||||
/// Keccak256 over empty array.
|
||||
pub const KECCAK_EMPTY: H256 =
|
||||
H256(hex!("c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"));
|
||||
|
||||
@ -60,11 +60,11 @@ pub use block::{
|
||||
pub use bloom::Bloom;
|
||||
pub use chain::{
|
||||
AllGenesisFormats, Chain, ChainInfo, ChainSpec, ChainSpecBuilder, DisplayHardforks,
|
||||
ForkCondition, ForkTimestamps, GOERLI, MAINNET, SEPOLIA,
|
||||
ForkCondition, ForkTimestamps, DEV, GOERLI, MAINNET, SEPOLIA,
|
||||
};
|
||||
pub use compression::*;
|
||||
pub use constants::{
|
||||
EMPTY_OMMER_ROOT, GOERLI_GENESIS, KECCAK_EMPTY, MAINNET_GENESIS, SEPOLIA_GENESIS,
|
||||
DEV_GENESIS, EMPTY_OMMER_ROOT, GOERLI_GENESIS, KECCAK_EMPTY, MAINNET_GENESIS, SEPOLIA_GENESIS,
|
||||
};
|
||||
pub use forkid::{ForkFilter, ForkHash, ForkId, ForkTransition, ValidationError};
|
||||
pub use genesis::{Genesis, GenesisAccount};
|
||||
|
||||
Reference in New Issue
Block a user