add alloy_chains (#5952)

Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
This commit is contained in:
Darex
2024-01-12 20:13:55 +05:30
committed by GitHub
parent 3675c632fd
commit 3ce9fcf7e5
30 changed files with 117 additions and 447 deletions

View File

@@ -1,439 +1,71 @@
use crate::{
holesky_nodes,
net::{goerli_nodes, mainnet_nodes, sepolia_nodes},
NodeRecord, U256, U64,
};
use alloy_rlp::{Decodable, Encodable};
use num_enum::TryFromPrimitive;
use reth_codecs::add_arbitrary_tests;
use serde::{Deserialize, Serialize};
use std::{fmt, str::FromStr};
use strum::{AsRefStr, EnumCount, EnumIter, EnumString, EnumVariantNames};
// The chain spec module.
mod spec;
pub use alloy_chains::{Chain, NamedChain};
pub use info::ChainInfo;
pub use spec::{
AllGenesisFormats, BaseFeeParams, BaseFeeParamsKind, ChainSpec, ChainSpecBuilder,
DisplayHardforks, ForkBaseFeeParams, ForkCondition, ForkTimestamps, DEV, GOERLI, HOLESKY,
MAINNET, SEPOLIA,
};
#[cfg(feature = "optimism")]
pub use spec::{BASE_GOERLI, BASE_MAINNET, BASE_SEPOLIA, OP_GOERLI};
// The chain spec module.
mod spec;
// The chain info module.
mod info;
pub use info::ChainInfo;
/// An Ethereum EIP-155 chain.
#[derive(
Clone,
Copy,
Debug,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
AsRefStr, // AsRef<str>, fmt::Display
EnumVariantNames, // Chain::VARIANTS
EnumString, // FromStr, TryFrom<&str>
EnumIter, // Chain::iter
EnumCount, // Chain::COUNT
TryFromPrimitive, // TryFrom<u64>
Deserialize,
Serialize,
)]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
#[repr(u64)]
pub enum NamedChain {
/// Ethereum Mainnet.
Mainnet = 1,
/// Morden Testnet.
Morden = 2,
/// Ropsten Testnet.
Ropsten = 3,
/// Rinkeby Testnet.
Rinkeby = 4,
/// Goerli Testnet.
Goerli = 5,
/// Kovan Testnet.
Kovan = 42,
/// Holesky Testnet.
Holesky = 17000,
/// Sepolia Testnet.
Sepolia = 11155111,
/// Optimism Mainnet.
Optimism = 10,
/// Optimism Kovan Testnet.
OptimismKovan = 69,
/// Optimism Goerli Testnet.
OptimismGoerli = 420,
/// Base chain.
Base = 8453,
/// Base Goerli Testnet.
BaseGoerli = 84531,
/// Base Sepolia Testnet.
BaseSepolia = 84532,
/// Arbitrum Mainnet.
Arbitrum = 42161,
/// Arbitrum Testnet.
ArbitrumTestnet = 421611,
/// Arbitrum Goerli Testnet.
ArbitrumGoerli = 421613,
/// Arbitrum Nova.
ArbitrumNova = 42170,
/// Binance Smart Chain Mainnet.
#[serde(alias = "bsc")]
BinanceSmartChain = 56,
/// Binance Smart Chain Testnet.
#[serde(alias = "bsc_testnet")]
BinanceSmartChainTestnet = 97,
/// Development Testnet.
Dev = 1337,
}
impl From<NamedChain> for u64 {
fn from(value: NamedChain) -> Self {
value as u64
}
}
impl fmt::Display for NamedChain {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.as_ref().fmt(f)
}
}
/// Either a named or chain id or the actual id value
#[add_arbitrary_tests(rlp)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum Chain {
/// Contains a known chain
Named(NamedChain),
/// Contains the id of a chain
Id(u64),
}
impl Chain {
/// Returns the mainnet chain.
pub const fn mainnet() -> Self {
Chain::Named(NamedChain::Mainnet)
}
/// Returns the goerli chain.
pub const fn goerli() -> Self {
Chain::Named(NamedChain::Goerli)
}
/// Returns the sepolia chain.
pub const fn sepolia() -> Self {
Chain::Named(NamedChain::Sepolia)
}
/// Returns the holesky chain.
pub const fn holesky() -> Self {
Chain::Named(NamedChain::Holesky)
}
/// Returns the optimism goerli chain.
pub const fn optimism_goerli() -> Self {
Chain::Named(NamedChain::OptimismGoerli)
}
/// Returns the optimism mainnet chain.
pub const fn optimism_mainnet() -> Self {
Chain::Named(NamedChain::Optimism)
}
/// Returns the base goerli chain.
pub const fn base_goerli() -> Self {
Chain::Named(NamedChain::BaseGoerli)
}
/// Returns the base sepolia chain.
pub const fn base_sepolia() -> Self {
Chain::Named(NamedChain::BaseSepolia)
}
/// Returns the base mainnet chain.
pub const fn base_mainnet() -> Self {
Chain::Named(NamedChain::Base)
}
/// Returns the dev chain.
pub const fn dev() -> Self {
Chain::Named(NamedChain::Dev)
}
/// Returns true if the chain contains Optimism configuration.
pub fn is_optimism(self) -> bool {
self.named().map_or(false, |c| {
matches!(
c,
NamedChain::Optimism |
NamedChain::OptimismGoerli |
NamedChain::OptimismKovan |
NamedChain::Base |
NamedChain::BaseGoerli |
NamedChain::BaseSepolia
)
})
}
/// Attempts to convert the chain into a named chain.
pub fn named(&self) -> Option<NamedChain> {
match self {
Chain::Named(chain) => Some(*chain),
Chain::Id(id) => NamedChain::try_from(*id).ok(),
}
}
/// The id of the chain
pub fn id(&self) -> u64 {
match self {
Chain::Named(chain) => *chain as u64,
Chain::Id(id) => *id,
}
}
/// Returns the address of the public DNS node list for the given chain.
///
/// See also <https://github.com/ethereum/discv4-dns-lists>
pub fn public_dns_network_protocol(self) -> Option<String> {
use NamedChain as C;
const DNS_PREFIX: &str = "enrtree://AKA3AM6LPBYEUDMVNU3BSVQJ5AD45Y7YPOHJLEF6W26QOE4VTUDPE@";
let named: NamedChain = self.try_into().ok()?;
if matches!(named, C::Mainnet | C::Goerli | C::Sepolia | C::Holesky) {
return Some(format!("{DNS_PREFIX}all.{}.ethdisco.net", named.as_ref().to_lowercase()))
}
None
}
/// Returns bootnodes for the given chain.
pub fn bootnodes(self) -> Option<Vec<NodeRecord>> {
use NamedChain as C;
match self.try_into().ok()? {
C::Mainnet => Some(mainnet_nodes()),
C::Goerli => Some(goerli_nodes()),
C::Sepolia => Some(sepolia_nodes()),
C::Holesky => Some(holesky_nodes()),
_ => None,
}
}
}
impl fmt::Display for Chain {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Chain::Named(chain) => chain.fmt(f),
Chain::Id(id) => {
if let Ok(chain) = NamedChain::try_from(*id) {
chain.fmt(f)
} else {
id.fmt(f)
}
}
}
}
}
impl From<NamedChain> for Chain {
fn from(id: NamedChain) -> Self {
Chain::Named(id)
}
}
impl From<u64> for Chain {
fn from(id: u64) -> Self {
NamedChain::try_from(id).map(Chain::Named).unwrap_or_else(|_| Chain::Id(id))
}
}
impl From<U256> for Chain {
fn from(id: U256) -> Self {
id.to::<u64>().into()
}
}
impl From<Chain> for u64 {
fn from(c: Chain) -> Self {
match c {
Chain::Named(c) => c as u64,
Chain::Id(id) => id,
}
}
}
impl From<Chain> for U64 {
fn from(c: Chain) -> Self {
U64::from(u64::from(c))
}
}
impl From<Chain> for U256 {
fn from(c: Chain) -> Self {
U256::from(u64::from(c))
}
}
impl TryFrom<Chain> for NamedChain {
type Error = <NamedChain as TryFrom<u64>>::Error;
fn try_from(chain: Chain) -> Result<Self, Self::Error> {
match chain {
Chain::Named(chain) => Ok(chain),
Chain::Id(id) => id.try_into(),
}
}
}
impl FromStr for Chain {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if let Ok(chain) = NamedChain::from_str(s) {
Ok(Chain::Named(chain))
} else {
s.parse::<u64>()
.map(Chain::Id)
.map_err(|_| format!("Expected known chain or integer, found: {s}"))
}
}
}
impl Encodable for Chain {
fn encode(&self, out: &mut dyn alloy_rlp::BufMut) {
match self {
Self::Named(chain) => u64::from(*chain).encode(out),
Self::Id(id) => id.encode(out),
}
}
fn length(&self) -> usize {
match self {
Self::Named(chain) => u64::from(*chain).length(),
Self::Id(id) => id.length(),
}
}
}
impl Decodable for Chain {
fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> {
Ok(u64::decode(buf)?.into())
}
}
impl Default for Chain {
fn default() -> Self {
NamedChain::Mainnet.into()
}
}
#[cfg(any(test, feature = "arbitrary"))]
impl<'a> arbitrary::Arbitrary<'a> for Chain {
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
if u.ratio(1, 2)? {
let chain = u.int_in_range(0..=(NamedChain::COUNT - 1))?;
return Ok(Chain::Named(NamedChain::iter().nth(chain).expect("in range")))
}
Ok(Self::Id(u64::arbitrary(u)?))
}
}
#[cfg(any(test, feature = "arbitrary"))]
use strum::IntoEnumIterator;
#[cfg(any(test, feature = "arbitrary"))]
use proptest::{
arbitrary::ParamsFor,
prelude::{any, Strategy},
sample::Selector,
strategy::BoxedStrategy,
};
#[cfg(any(test, feature = "arbitrary"))]
impl proptest::arbitrary::Arbitrary for Chain {
type Parameters = ParamsFor<u32>;
fn arbitrary_with(_: Self::Parameters) -> Self::Strategy {
let named =
any::<Selector>().prop_map(move |sel| Chain::Named(sel.select(NamedChain::iter())));
let id = any::<u64>().prop_map(Chain::from);
proptest::strategy::Union::new_weighted(vec![(50, named.boxed()), (50, id.boxed())]).boxed()
}
type Strategy = BoxedStrategy<Chain>;
}
#[cfg(test)]
mod tests {
use super::*;
use crate::U256;
use alloy_rlp::Encodable;
use std::str::FromStr;
#[test]
fn test_id() {
let chain = Chain::Id(1234);
let chain = Chain::from(1234);
assert_eq!(chain.id(), 1234);
}
#[test]
fn test_named_id() {
let chain = Chain::Named(NamedChain::Goerli);
let chain = Chain::from_named(NamedChain::Goerli);
assert_eq!(chain.id(), 5);
}
#[test]
fn test_display_named_chain() {
let chain = Chain::Named(NamedChain::Mainnet);
let chain = Chain::from_named(NamedChain::Mainnet);
assert_eq!(format!("{chain}"), "mainnet");
}
#[test]
fn test_display_id_chain() {
let chain = Chain::Id(1234);
let chain = Chain::from(1234);
assert_eq!(format!("{chain}"), "1234");
}
#[test]
fn test_from_u256() {
let n = U256::from(1234);
let chain = Chain::from(n);
let expected = Chain::Id(1234);
let chain = Chain::from(n.to::<u64>());
let expected = Chain::from(1234);
assert_eq!(chain, expected);
}
#[test]
fn test_into_u256() {
let chain = Chain::Named(NamedChain::Goerli);
let n: U256 = chain.into();
let chain = Chain::from_named(NamedChain::Goerli);
let n: U256 = U256::from(chain.id());
let expected = U256::from(5);
assert_eq!(n, expected);
}
#[test]
#[allow(non_snake_case)]
fn test_into_U64() {
let chain = Chain::Named(NamedChain::Goerli);
let n: U64 = chain.into();
let expected = U64::from(5);
assert_eq!(n, expected);
}
#[test]
fn test_from_str_named_chain() {
let result = Chain::from_str("mainnet");
let expected = Chain::Named(NamedChain::Mainnet);
let expected = Chain::from_named(NamedChain::Mainnet);
assert!(result.is_ok());
assert_eq!(result.unwrap(), expected);
@@ -449,7 +81,7 @@ mod tests {
#[test]
fn test_from_str_id_chain() {
let result = Chain::from_str("1234");
let expected = Chain::Id(1234);
let expected = Chain::from(1234);
assert!(result.is_ok());
assert_eq!(result.unwrap(), expected);
@@ -458,14 +90,14 @@ mod tests {
#[test]
fn test_default() {
let default = Chain::default();
let expected = Chain::Named(NamedChain::Mainnet);
let expected = Chain::from_named(NamedChain::Mainnet);
assert_eq!(default, expected);
}
#[test]
fn test_id_chain_encodable_length() {
let chain = Chain::Id(1234);
let chain = Chain::from(1234);
assert_eq!(chain.length(), 3);
}

View File

@@ -3,11 +3,14 @@ use crate::{
EIP1559_DEFAULT_BASE_FEE_MAX_CHANGE_DENOMINATOR, EIP1559_DEFAULT_ELASTICITY_MULTIPLIER,
EIP1559_INITIAL_BASE_FEE, EMPTY_RECEIPTS, EMPTY_TRANSACTIONS, EMPTY_WITHDRAWALS,
},
holesky_nodes,
net::{goerli_nodes, mainnet_nodes, sepolia_nodes},
proofs::state_root_ref_unhashed,
revm_primitives::{address, b256},
Address, BlockNumber, Chain, ForkFilter, ForkFilterKey, ForkHash, ForkId, Genesis, Hardfork,
Head, Header, SealedHeader, B256, EMPTY_OMMER_ROOT_HASH, U256,
Address, BlockNumber, ForkFilter, ForkFilterKey, ForkHash, ForkId, Genesis, Hardfork, Head,
Header, NodeRecord, SealedHeader, B256, EMPTY_OMMER_ROOT_HASH, U256,
};
use alloy_chains::{Chain, NamedChain};
use once_cell::sync::Lazy;
use serde::{Deserialize, Serialize};
use std::{
@@ -960,6 +963,19 @@ impl ChainSpec {
pub fn builder() -> ChainSpecBuilder {
ChainSpecBuilder::default()
}
/// Returns the known bootnode records for the given chain.
pub fn bootnodes(&self) -> Option<Vec<NodeRecord>> {
use NamedChain as C;
let chain = self.chain;
match chain.try_into().ok()? {
C::Mainnet => Some(mainnet_nodes()),
C::Goerli => Some(goerli_nodes()),
C::Sepolia => Some(sepolia_nodes()),
C::Holesky => Some(holesky_nodes()),
_ => None,
}
}
}
impl From<Genesis> for ChainSpec {
@@ -1603,17 +1619,17 @@ impl DepositContract {
#[cfg(test)]
mod tests {
use super::*;
#[cfg(feature = "optimism")]
use crate::OP_GOERLI;
use crate::{
b256, hex, trie::TrieAccount, ChainConfig, GenesisAccount, NamedChain, B256, DEV, GOERLI,
HOLESKY, MAINNET, SEPOLIA, U256,
b256, hex, trie::TrieAccount, ChainConfig, GenesisAccount, B256, DEV, GOERLI, HOLESKY,
MAINNET, SEPOLIA, U256,
};
use alloy_chains::NamedChain;
use alloy_rlp::Encodable;
use bytes::BytesMut;
use std::{collections::HashMap, str::FromStr};
#[cfg(feature = "optimism")]
use crate::OP_GOERLI;
fn test_fork_ids(spec: &ChainSpec, cases: &[(Head, ForkId)]) {
for (block, expected_id) in cases {
let computed_id = spec.fork_id(block);
@@ -2494,7 +2510,7 @@ Post-merge hard forks (timestamp based):
fn test_timestamp_fork_in_genesis() {
let timestamp = 1690475657u64;
let default_spec_builder = ChainSpecBuilder::default()
.chain(Chain::Id(1337))
.chain(Chain::from_id(1337))
.genesis(Genesis::default().with_timestamp(timestamp))
.paris_activated();
@@ -2755,7 +2771,7 @@ Post-merge hard forks (timestamp based):
let genesis = serde_json::from_str::<AllGenesisFormats>(hive_json).unwrap();
let chainspec: ChainSpec = genesis.into();
assert_eq!(chainspec.genesis_hash, None);
assert_eq!(chainspec.chain, Chain::Named(NamedChain::Optimism));
assert_eq!(chainspec.chain, Chain::from_named(NamedChain::Optimism));
let expected_state_root: B256 =
hex!("9a6049ac535e3dc7436c189eaa81c73f35abd7f282ab67c32944ff0301d63360").into();
assert_eq!(chainspec.genesis_header().state_root, expected_state_root);
@@ -2827,7 +2843,7 @@ Post-merge hard forks (timestamp based):
// set the gas limit from the hive test genesis according to the hash
let genesis = Genesis { gas_limit: 0x2fefd8u64, ..Default::default() };
let default_chainspec = ChainSpecBuilder::default()
.chain(Chain::Id(1337))
.chain(Chain::from_id(1337))
.genesis(genesis)
.cancun_activated()
.build();

View File

@@ -138,7 +138,7 @@ mod tests {
{
#[inline(always)]
fn op_cs(f: impl FnOnce(ChainSpecBuilder) -> ChainSpecBuilder) -> ChainSpec {
let cs = ChainSpecBuilder::mainnet().chain(crate::Chain::Id(10));
let cs = ChainSpecBuilder::mainnet().chain(alloy_chains::Chain::from_id(10));
f(cs).build()
}