fix(op): discv5 ENR (#7991)

This commit is contained in:
Emilia Hane
2024-04-30 21:18:19 +02:00
committed by GitHub
parent e09895257a
commit d532217afb
12 changed files with 1241 additions and 1025 deletions

View File

@ -9,9 +9,9 @@ use std::{
use derive_more::Display; use derive_more::Display;
use discv5::ListenConfig; use discv5::ListenConfig;
use multiaddr::{Multiaddr, Protocol}; use multiaddr::{Multiaddr, Protocol};
use reth_primitives::{Bytes, EnrForkIdEntry, ForkId, NodeRecord, MAINNET}; use reth_primitives::{Bytes, EnrForkIdEntry, ForkId, NodeRecord};
use crate::{enr::discv4_id_to_multiaddr_id, filter::MustNotIncludeKeys, network_key}; use crate::{enr::discv4_id_to_multiaddr_id, filter::MustNotIncludeKeys, NetworkStackId};
/// Default interval in seconds at which to run a lookup up query. /// Default interval in seconds at which to run a lookup up query.
/// ///
@ -50,7 +50,7 @@ impl ConfigBuilder {
let Config { let Config {
discv5_config, discv5_config,
bootstrap_nodes, bootstrap_nodes,
fork: (network_key, fork_id), fork,
tcp_port, tcp_port,
other_enr_kv_pairs, other_enr_kv_pairs,
lookup_interval, lookup_interval,
@ -60,7 +60,7 @@ impl ConfigBuilder {
Self { Self {
discv5_config: Some(discv5_config), discv5_config: Some(discv5_config),
bootstrap_nodes, bootstrap_nodes,
fork: Some((network_key, fork_id.fork_id)), fork: fork.map(|(key, fork_id)| (key, fork_id.fork_id)),
tcp_port, tcp_port,
other_enr_kv_pairs, other_enr_kv_pairs,
lookup_interval: Some(lookup_interval), lookup_interval: Some(lookup_interval),
@ -117,8 +117,8 @@ impl ConfigBuilder {
/// Set fork ID kv-pair to set in local [`Enr`](discv5::enr::Enr). This lets peers on discovery /// Set fork ID kv-pair to set in local [`Enr`](discv5::enr::Enr). This lets peers on discovery
/// network know which chain this node belongs to. /// network know which chain this node belongs to.
pub fn fork(mut self, network_key: &'static [u8], fork_id: ForkId) -> Self { pub fn fork(mut self, fork_key: &'static [u8], fork_id: ForkId) -> Self {
self.fork = Some((network_key, fork_id)); self.fork = Some((fork_key, fork_id));
self self
} }
@ -160,13 +160,12 @@ impl ConfigBuilder {
let discv5_config = discv5_config let discv5_config = discv5_config
.unwrap_or_else(|| discv5::ConfigBuilder::new(ListenConfig::default()).build()); .unwrap_or_else(|| discv5::ConfigBuilder::new(ListenConfig::default()).build());
let (network_key, fork_id) = fork.unwrap_or((network_key::ETH, MAINNET.latest_fork_id())); let fork = fork.map(|(key, fork_id)| (key, fork_id.into()));
let fork = (network_key, fork_id.into());
let lookup_interval = lookup_interval.unwrap_or(DEFAULT_SECONDS_LOOKUP_INTERVAL); let lookup_interval = lookup_interval.unwrap_or(DEFAULT_SECONDS_LOOKUP_INTERVAL);
let discovered_peer_filter = let discovered_peer_filter = discovered_peer_filter
discovered_peer_filter.unwrap_or_else(|| MustNotIncludeKeys::new(&[network_key::ETH2])); .unwrap_or_else(|| MustNotIncludeKeys::new(&[NetworkStackId::ETH2]));
Config { Config {
discv5_config, discv5_config,
@ -190,7 +189,7 @@ pub struct Config {
pub(super) bootstrap_nodes: HashSet<BootNode>, pub(super) bootstrap_nodes: HashSet<BootNode>,
/// Fork kv-pair to set in local node record. Identifies which network/chain/fork the node /// Fork kv-pair to set in local node record. Identifies which network/chain/fork the node
/// belongs, e.g. `(b"opstack", ChainId)` or `(b"eth", [ForkId])`. /// belongs, e.g. `(b"opstack", ChainId)` or `(b"eth", [ForkId])`.
pub(super) fork: (&'static [u8], EnrForkIdEntry), pub(super) fork: Option<(&'static [u8], EnrForkIdEntry)>,
/// RLPx TCP port to advertise. /// RLPx TCP port to advertise.
pub(super) tcp_port: u16, pub(super) tcp_port: u16,
/// Additional kv-pairs (besides tcp port, udp port and fork) that should be advertised to /// Additional kv-pairs (besides tcp port, udp port and fork) that should be advertised to

View File

@ -11,6 +11,9 @@ pub enum Error {
/// Node record has incompatible key type. /// Node record has incompatible key type.
#[error("incompatible key type (not secp256k1)")] #[error("incompatible key type (not secp256k1)")]
IncompatibleKeyType, IncompatibleKeyType,
/// No key used to identify rlpx network is configured.
#[error("network stack identifier is not configured")]
NetworkStackIdNotConfigured,
/// Missing key used to identify rlpx network. /// Missing key used to identify rlpx network.
#[error("fork missing on enr, key missing")] #[error("fork missing on enr, key missing")]
ForkMissing(&'static [u8]), ForkMissing(&'static [u8]),

View File

@ -96,7 +96,7 @@ mod tests {
use alloy_rlp::Bytes; use alloy_rlp::Bytes;
use discv5::enr::{CombinedKey, Enr}; use discv5::enr::{CombinedKey, Enr};
use crate::network_key::{ETH, ETH2}; use crate::NetworkStackId;
use super::*; use super::*;
@ -104,16 +104,21 @@ mod tests {
fn must_not_include_key_filter() { fn must_not_include_key_filter() {
// rig test // rig test
let filter = MustNotIncludeKeys::new(&[ETH, ETH2]); let filter = MustNotIncludeKeys::new(&[NetworkStackId::ETH, NetworkStackId::ETH2]);
// enr_1 advertises a fork from one of the keys configured in filter // enr_1 advertises a fork from one of the keys configured in filter
let sk = CombinedKey::generate_secp256k1(); let sk = CombinedKey::generate_secp256k1();
let enr_1 = let enr_1 = Enr::builder()
Enr::builder().add_value_rlp(ETH as &[u8], Bytes::from("cancun")).build(&sk).unwrap(); .add_value_rlp(NetworkStackId::ETH as &[u8], Bytes::from("cancun"))
.build(&sk)
.unwrap();
// enr_2 advertises a fork from one the other key configured in filter // enr_2 advertises a fork from one the other key configured in filter
let sk = CombinedKey::generate_secp256k1(); let sk = CombinedKey::generate_secp256k1();
let enr_2 = Enr::builder().add_value_rlp(ETH2, Bytes::from("deneb")).build(&sk).unwrap(); let enr_2 = Enr::builder()
.add_value_rlp(NetworkStackId::ETH2, Bytes::from("deneb"))
.build(&sk)
.unwrap();
// test // test

View File

@ -33,7 +33,7 @@ pub mod enr;
pub mod error; pub mod error;
pub mod filter; pub mod filter;
pub mod metrics; pub mod metrics;
pub mod network_key; pub mod network_stack_id;
pub use discv5::{self, IpMode}; pub use discv5::{self, IpMode};
@ -41,6 +41,7 @@ pub use config::{BootNode, Config, ConfigBuilder};
pub use enr::enr_to_discv4_id; pub use enr::enr_to_discv4_id;
pub use error::Error; pub use error::Error;
pub use filter::{FilterOutcome, MustNotIncludeKeys}; pub use filter::{FilterOutcome, MustNotIncludeKeys};
pub use network_stack_id::NetworkStackId;
use metrics::{DiscoveredPeersMetrics, Discv5Metrics}; use metrics::{DiscoveredPeersMetrics, Discv5Metrics};
@ -75,7 +76,7 @@ pub struct Discv5 {
/// [`IpMode`] of the the node. /// [`IpMode`] of the the node.
ip_mode: IpMode, ip_mode: IpMode,
/// Key used in kv-pair to ID chain, e.g. 'opstack' or 'eth'. /// Key used in kv-pair to ID chain, e.g. 'opstack' or 'eth'.
fork_key: &'static [u8], fork_key: Option<&'static [u8]>,
/// Filter applied to a discovered peers before passing it up to app. /// Filter applied to a discovered peers before passing it up to app.
discovered_peer_filter: MustNotIncludeKeys, discovered_peer_filter: MustNotIncludeKeys,
/// Metrics for underlying [`discv5::Discv5`] node and filtered discovered peers. /// Metrics for underlying [`discv5::Discv5`] node and filtered discovered peers.
@ -217,7 +218,7 @@ impl Discv5 {
fn build_local_enr( fn build_local_enr(
sk: &SecretKey, sk: &SecretKey,
config: &Config, config: &Config,
) -> (Enr<SecretKey>, NodeRecord, &'static [u8], IpMode) { ) -> (Enr<SecretKey>, NodeRecord, Option<&'static [u8]>, IpMode) {
let mut builder = discv5::enr::Enr::builder(); let mut builder = discv5::enr::Enr::builder();
let Config { discv5_config, fork, tcp_port, other_enr_kv_pairs, .. } = config; let Config { discv5_config, fork, tcp_port, other_enr_kv_pairs, .. } = config;
@ -258,8 +259,10 @@ impl Discv5 {
}; };
// identifies which network node is on // identifies which network node is on
let (network, fork_value) = fork; let network_stack_id = fork.as_ref().map(|(network_stack_id, fork_value)| {
builder.add_value_rlp(network, alloy_rlp::encode(fork_value).into()); builder.add_value_rlp(network_stack_id, alloy_rlp::encode(fork_value).into());
*network_stack_id
});
// add other data // add other data
for (key, value) in other_enr_kv_pairs { for (key, value) in other_enr_kv_pairs {
@ -273,7 +276,7 @@ impl Discv5 {
// backwards compatible enr // backwards compatible enr
let bc_enr = NodeRecord::from_secret_key(socket, sk); let bc_enr = NodeRecord::from_secret_key(socket, sk);
(enr, bc_enr, network, ip_mode) (enr, bc_enr, network_stack_id, ip_mode)
} }
/// Bootstraps underlying [`discv5::Discv5`] node with configured peers. /// Bootstraps underlying [`discv5::Discv5`] node with configured peers.
@ -438,8 +441,10 @@ impl Discv5 {
return None return None
} }
let fork_id = // todo: extend for all network stacks in reth-network rlpx logic
(self.fork_key == network_key::ETH).then(|| self.get_fork_id(enr).ok()).flatten(); let fork_id = (self.fork_key == Some(NetworkStackId::ETH))
.then(|| self.get_fork_id(enr).ok())
.flatten();
trace!(target: "net::discovery::discv5", trace!(target: "net::discovery::discv5",
?fork_id, ?fork_id,
@ -483,12 +488,13 @@ impl Discv5 {
self.discovered_peer_filter.filter(enr) self.discovered_peer_filter.filter(enr)
} }
/// Returns the [`ForkId`] of the given [`Enr`](discv5::Enr), if field is set. /// Returns the [`ForkId`] of the given [`Enr`](discv5::Enr) w.r.t. the local node's network
/// stack, if field is set.
fn get_fork_id<K: discv5::enr::EnrKey>( fn get_fork_id<K: discv5::enr::EnrKey>(
&self, &self,
enr: &discv5::enr::Enr<K>, enr: &discv5::enr::Enr<K>,
) -> Result<ForkId, Error> { ) -> Result<ForkId, Error> {
let key = self.fork_key; let Some(key) = self.fork_key else { return Err(Error::NetworkStackIdNotConfigured) };
let fork_id = enr let fork_id = enr
.get_decodable::<EnrForkIdEntry>(key) .get_decodable::<EnrForkIdEntry>(key)
.ok_or(Error::ForkMissing(key))? .ok_or(Error::ForkMissing(key))?
@ -519,7 +525,7 @@ impl Discv5 {
} }
/// Returns the key to use to identify the [`ForkId`] kv-pair on the [`Enr`](discv5::Enr). /// Returns the key to use to identify the [`ForkId`] kv-pair on the [`Enr`](discv5::Enr).
pub fn fork_key(&self) -> &[u8] { pub fn fork_key(&self) -> Option<&[u8]> {
self.fork_key self.fork_key
} }
} }
@ -625,7 +631,7 @@ mod tests {
.unwrap(), .unwrap(),
), ),
ip_mode: IpMode::Ip4, ip_mode: IpMode::Ip4,
fork_key: b"noop", fork_key: None,
discovered_peer_filter: MustNotIncludeKeys::default(), discovered_peer_filter: MustNotIncludeKeys::default(),
metrics: Discv5Metrics::default(), metrics: Discv5Metrics::default(),
} }
@ -831,13 +837,16 @@ mod tests {
const TCP_PORT: u16 = 30303; const TCP_PORT: u16 = 30303;
let fork_id = MAINNET.latest_fork_id(); let fork_id = MAINNET.latest_fork_id();
let config = Config::builder(TCP_PORT).fork(network_key::ETH, fork_id).build(); let config = Config::builder(TCP_PORT).fork(NetworkStackId::ETH, fork_id).build();
let sk = SecretKey::new(&mut thread_rng()); let sk = SecretKey::new(&mut thread_rng());
let (enr, _, _, _) = Discv5::build_local_enr(&sk, &config); let (enr, _, _, _) = Discv5::build_local_enr(&sk, &config);
let decoded_fork_id = let decoded_fork_id = enr
enr.get_decodable::<EnrForkIdEntry>(network_key::ETH).unwrap().map(Into::into).unwrap(); .get_decodable::<EnrForkIdEntry>(NetworkStackId::ETH)
.unwrap()
.map(Into::into)
.unwrap();
assert_eq!(fork_id, decoded_fork_id); assert_eq!(fork_id, decoded_fork_id);
assert_eq!(TCP_PORT, enr.tcp4().unwrap()); // listen config is defaulting to ip mode ipv4 assert_eq!(TCP_PORT, enr.tcp4().unwrap()); // listen config is defaulting to ip mode ipv4

View File

@ -2,7 +2,7 @@
use metrics::{Counter, Gauge}; use metrics::{Counter, Gauge};
use reth_metrics::Metrics; use reth_metrics::Metrics;
use crate::network_key::{ETH, ETH2, OPSTACK}; use crate::NetworkStackId;
/// Information tracked by [`Discv5`](crate::Discv5). /// Information tracked by [`Discv5`](crate::Discv5).
#[derive(Debug, Default, Clone)] #[derive(Debug, Default, Clone)]
@ -91,27 +91,34 @@ impl DiscoveredPeersMetrics {
#[derive(Metrics, Clone)] #[derive(Metrics, Clone)]
#[metrics(scope = "discv5")] #[metrics(scope = "discv5")]
pub struct AdvertisedChainMetrics { pub struct AdvertisedChainMetrics {
/// Frequency of node records with a kv-pair with [`OPSTACK`](crate::network_key) as /// Frequency of node records with a kv-pair with [`OPEL`](NetworkStackId::OPEL) as
/// key.
opel: Counter,
/// Frequency of node records with a kv-pair with [`OPSTACK`](NetworkStackId::OPSTACK) as
/// key. /// key.
opstack: Counter, opstack: Counter,
/// Frequency of node records with a kv-pair with [`ETH`](crate::network_key) as key. /// Frequency of node records with a kv-pair with [`ETH`](NetworkStackId::ETH) as key.
eth: Counter, eth: Counter,
/// Frequency of node records with a kv-pair with [`ETH2`](crate::network_key) as key. /// Frequency of node records with a kv-pair with [`ETH2`](NetworkStackId::ETH2) as key.
eth2: Counter, eth2: Counter,
} }
impl AdvertisedChainMetrics { impl AdvertisedChainMetrics {
/// Counts each recognised network type that is advertised on node record, once. /// Counts each recognised network stack type that is advertised on node record, once.
pub fn increment_once_by_network_type(&self, enr: &discv5::Enr) { pub fn increment_once_by_network_type(&self, enr: &discv5::Enr) {
if enr.get_raw_rlp(OPSTACK).is_some() { if enr.get_raw_rlp(NetworkStackId::OPEL).is_some() {
self.opel.increment(1u64)
}
if enr.get_raw_rlp(NetworkStackId::OPSTACK).is_some() {
self.opstack.increment(1u64) self.opstack.increment(1u64)
} }
if enr.get_raw_rlp(ETH).is_some() { if enr.get_raw_rlp(NetworkStackId::ETH).is_some() {
self.eth.increment(1u64) self.eth.increment(1u64)
} }
if enr.get_raw_rlp(ETH2).is_some() { if enr.get_raw_rlp(NetworkStackId::ETH2).is_some() {
self.eth2.increment(1u64) self.eth2.increment(1u64)
} }
} }

View File

@ -1,11 +0,0 @@
//! Keys of ENR [`ForkId`](reth_primitives::ForkId) kv-pair. Identifies which network a node
//! belongs to.
/// ENR fork ID kv-pair key, for an Ethereum L1 EL node.
pub const ETH: &[u8] = b"eth";
/// ENR fork ID kv-pair key, for an Ethereum L1 CL node.
pub const ETH2: &[u8] = b"eth2";
/// ENR fork ID kv-pair key, for an Optimism CL node.
pub const OPSTACK: &[u8] = b"opstack";

View File

@ -0,0 +1,33 @@
//! Keys of ENR [`ForkId`](reth_primitives::ForkId) kv-pair. Identifies which network stack a node
//! belongs to.
use reth_primitives::ChainSpec;
/// Identifies which Ethereum network stack a node belongs to, on the discovery network.
#[derive(Debug)]
pub struct NetworkStackId;
impl NetworkStackId {
/// ENR fork ID kv-pair key, for an Ethereum L1 EL node.
pub const ETH: &'static [u8] = b"eth";
/// ENR fork ID kv-pair key, for an Ethereum L1 CL node.
pub const ETH2: &'static [u8] = b"eth2";
/// ENR fork ID kv-pair key, for an Optimism EL node.
pub const OPEL: &'static [u8] = b"opel";
/// ENR fork ID kv-pair key, for an Optimism CL node.
pub const OPSTACK: &'static [u8] = b"opstack";
/// Returns the [`NetworkStackId`] that matches the given [`ChainSpec`].
pub fn id(chain: &ChainSpec) -> Option<&'static [u8]> {
if chain.is_optimism() {
return Some(Self::OPEL)
} else if chain.is_eth() {
return Some(Self::ETH)
}
None
}
}

View File

@ -9,17 +9,18 @@ use crate::{
NetworkHandle, NetworkManager, NetworkHandle, NetworkManager,
}; };
use reth_discv4::{Discv4Config, Discv4ConfigBuilder, DEFAULT_DISCOVERY_ADDRESS}; use reth_discv4::{Discv4Config, Discv4ConfigBuilder, DEFAULT_DISCOVERY_ADDRESS};
use reth_discv5::network_key; use reth_discv5::NetworkStackId;
use reth_dns_discovery::DnsDiscoveryConfig; use reth_dns_discovery::DnsDiscoveryConfig;
use reth_eth_wire::{HelloMessage, HelloMessageWithProtocols, Status}; use reth_eth_wire::{HelloMessage, HelloMessageWithProtocols, Status};
use reth_network_types::{pk2id, PeerId}; use reth_network_types::{pk2id, PeerId};
use reth_primitives::{ use reth_primitives::{
mainnet_nodes, sepolia_nodes, ChainSpec, ForkFilter, Head, NamedChain, NodeRecord, MAINNET, mainnet_nodes, sepolia_nodes, ChainSpec, ForkFilter, Head, NodeRecord, MAINNET,
}; };
use reth_provider::{BlockReader, HeaderProvider}; use reth_provider::{BlockReader, HeaderProvider};
use reth_tasks::{TaskSpawner, TokioTaskExecutor}; use reth_tasks::{TaskSpawner, TokioTaskExecutor};
use secp256k1::SECP256K1; use secp256k1::SECP256K1;
use std::{collections::HashSet, net::SocketAddr, sync::Arc}; use std::{collections::HashSet, net::SocketAddr, sync::Arc};
// re-export for convenience // re-export for convenience
use crate::protocol::{IntoRlpxSubProtocol, RlpxSubProtocols}; use crate::protocol::{IntoRlpxSubProtocol, RlpxSubProtocols};
pub use secp256k1::SecretKey; pub use secp256k1::SecretKey;
@ -121,20 +122,16 @@ impl<C> NetworkConfig<C> {
f: impl FnOnce(reth_discv5::ConfigBuilder) -> reth_discv5::Config, f: impl FnOnce(reth_discv5::ConfigBuilder) -> reth_discv5::Config,
) -> Self { ) -> Self {
let rlpx_port = self.listener_addr.port(); let rlpx_port = self.listener_addr.port();
let chain = self.chain_spec.chain; let network_stack_id = NetworkStackId::id(&self.chain_spec);
let fork_id = self.chain_spec.latest_fork_id(); let fork_id = self.chain_spec.latest_fork_id();
let boot_nodes = self.boot_nodes.clone(); let boot_nodes = self.boot_nodes.clone();
let mut builder = let mut builder =
reth_discv5::Config::builder(rlpx_port).add_unsigned_boot_nodes(boot_nodes.into_iter()); reth_discv5::Config::builder(rlpx_port).add_unsigned_boot_nodes(boot_nodes.into_iter());
if chain.named() == Some(NamedChain::Mainnet) { if let Some(id) = network_stack_id {
builder = builder.fork(network_key::ETH, fork_id) builder = builder.fork(id, fork_id);
} }
// todo: set op EL fork id
/*if chain.is_optimism() {
builder = builder.fork(network_key::, fork_id)
}*/
self.set_discovery_v5(f(builder)) self.set_discovery_v5(f(builder))
} }

View File

@ -1,4 +1,4 @@
pub use alloy_chains::{Chain, NamedChain}; pub use alloy_chains::{Chain, ChainKind, NamedChain};
pub use info::ChainInfo; pub use info::ChainInfo;
pub use spec::{ pub use spec::{
AllGenesisFormats, BaseFeeParams, BaseFeeParamsKind, ChainSpec, ChainSpecBuilder, AllGenesisFormats, BaseFeeParams, BaseFeeParamsKind, ChainSpec, ChainSpecBuilder,

View File

@ -4,8 +4,9 @@ use crate::{
net::{goerli_nodes, mainnet_nodes, sepolia_nodes}, net::{goerli_nodes, mainnet_nodes, sepolia_nodes},
proofs::state_root_ref_unhashed, proofs::state_root_ref_unhashed,
revm_primitives::{address, b256}, revm_primitives::{address, b256},
Address, BlockNumber, Chain, ForkFilter, ForkFilterKey, ForkHash, ForkId, Genesis, Hardfork, Address, BlockNumber, Chain, ChainKind, ForkFilter, ForkFilterKey, ForkHash, ForkId, Genesis,
Head, Header, NamedChain, NodeRecord, SealedHeader, B256, EMPTY_OMMER_ROOT_HASH, U256, Hardfork, Head, Header, NamedChain, NodeRecord, SealedHeader, B256, EMPTY_OMMER_ROOT_HASH,
U256,
}; };
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -577,6 +578,24 @@ impl ChainSpec {
self.chain self.chain
} }
/// Returns `true` if this chain contains Ethereum configuration.
#[inline]
pub fn is_eth(&self) -> bool {
matches!(
self.chain.kind(),
ChainKind::Named(
NamedChain::Mainnet |
NamedChain::Morden |
NamedChain::Ropsten |
NamedChain::Rinkeby |
NamedChain::Goerli |
NamedChain::Kovan |
NamedChain::Holesky |
NamedChain::Sepolia
)
)
}
/// Returns `true` if this chain contains Optimism configuration. /// Returns `true` if this chain contains Optimism configuration.
#[inline] #[inline]
pub fn is_optimism(&self) -> bool { pub fn is_optimism(&self) -> bool {

View File

@ -55,7 +55,7 @@ pub use block::{
ForkBlock, RpcBlockHash, SealedBlock, SealedBlockWithSenders, ForkBlock, RpcBlockHash, SealedBlock, SealedBlockWithSenders,
}; };
pub use chain::{ pub use chain::{
AllGenesisFormats, BaseFeeParams, BaseFeeParamsKind, Chain, ChainInfo, ChainSpec, AllGenesisFormats, BaseFeeParams, BaseFeeParamsKind, Chain, ChainInfo, ChainKind, ChainSpec,
ChainSpecBuilder, DisplayHardforks, ForkBaseFeeParams, ForkCondition, ForkTimestamps, ChainSpecBuilder, DisplayHardforks, ForkBaseFeeParams, ForkCondition, ForkTimestamps,
NamedChain, DEV, GOERLI, HOLESKY, MAINNET, SEPOLIA, NamedChain, DEV, GOERLI, HOLESKY, MAINNET, SEPOLIA,
}; };

File diff suppressed because it is too large Load Diff