perf: cache fork timestamps (#2886)

This commit is contained in:
Matthias Seitz
2023-05-29 14:16:59 +02:00
committed by GitHub
parent e703973dc6
commit 01af8e5d4b
2 changed files with 94 additions and 1 deletions

View File

@ -20,6 +20,7 @@ pub static MAINNET: Lazy<ChainSpec> = Lazy::new(|| ChainSpec {
genesis_hash: Some(H256(hex!( genesis_hash: Some(H256(hex!(
"d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3" "d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3"
))), ))),
fork_timestamps: ForkTimestamps::default().shanghai(1681338455),
hardforks: BTreeMap::from([ hardforks: BTreeMap::from([
(Hardfork::Frontier, ForkCondition::Block(0)), (Hardfork::Frontier, ForkCondition::Block(0)),
(Hardfork::Homestead, ForkCondition::Block(1150000)), (Hardfork::Homestead, ForkCondition::Block(1150000)),
@ -54,6 +55,7 @@ pub static GOERLI: Lazy<ChainSpec> = Lazy::new(|| ChainSpec {
genesis_hash: Some(H256(hex!( genesis_hash: Some(H256(hex!(
"bf7e331f7f7c1dd2e05159666b3bf8bc7a8a3a9eb1d518969eab529dd9b88c1a" "bf7e331f7f7c1dd2e05159666b3bf8bc7a8a3a9eb1d518969eab529dd9b88c1a"
))), ))),
fork_timestamps: ForkTimestamps::default().shanghai(1678832736),
hardforks: BTreeMap::from([ hardforks: BTreeMap::from([
(Hardfork::Frontier, ForkCondition::Block(0)), (Hardfork::Frontier, ForkCondition::Block(0)),
(Hardfork::Homestead, ForkCondition::Block(0)), (Hardfork::Homestead, ForkCondition::Block(0)),
@ -82,6 +84,7 @@ pub static SEPOLIA: Lazy<ChainSpec> = Lazy::new(|| ChainSpec {
genesis_hash: Some(H256(hex!( genesis_hash: Some(H256(hex!(
"25a5cc106eea7138acab33231d7160d69cb777ee0c2c553fcddf5138993e6dd9" "25a5cc106eea7138acab33231d7160d69cb777ee0c2c553fcddf5138993e6dd9"
))), ))),
fork_timestamps: ForkTimestamps::default().shanghai(1677557088),
hardforks: BTreeMap::from([ hardforks: BTreeMap::from([
(Hardfork::Frontier, ForkCondition::Block(0)), (Hardfork::Frontier, ForkCondition::Block(0)),
(Hardfork::Homestead, ForkCondition::Block(0)), (Hardfork::Homestead, ForkCondition::Block(0)),
@ -128,6 +131,12 @@ pub struct ChainSpec {
/// The genesis block /// The genesis block
pub genesis: Genesis, pub genesis: Genesis,
/// Timestamps of various hardforks
///
/// This caches entries in `hardforks` map
#[serde(skip, default)]
pub fork_timestamps: ForkTimestamps,
/// The active hard forks and their activation conditions /// The active hard forks and their activation conditions
pub hardforks: BTreeMap<Hardfork, ForkCondition>, pub hardforks: BTreeMap<Hardfork, ForkCondition>,
} }
@ -210,7 +219,10 @@ impl ChainSpec {
/// Convenience method to check if [Hardfork::Shanghai] is active at a given timestamp. /// Convenience method to check if [Hardfork::Shanghai] is active at a given timestamp.
#[inline] #[inline]
pub fn is_shanghai_activated_at_timestamp(&self, timestamp: u64) -> bool { pub fn is_shanghai_activated_at_timestamp(&self, timestamp: u64) -> bool {
self.is_fork_active_at_timestamp(Hardfork::Shanghai, timestamp) self.fork_timestamps
.shanghai
.map(|shanghai| timestamp >= shanghai)
.unwrap_or_else(|| self.is_fork_active_at_timestamp(Hardfork::Shanghai, timestamp))
} }
/// Creates a [`ForkFilter`](crate::ForkFilter) for the block described by [Head]. /// Creates a [`ForkFilter`](crate::ForkFilter) for the block described by [Head].
@ -325,11 +337,36 @@ impl From<EthersGenesis> for ChainSpec {
chain: genesis.config.chain_id.into(), chain: genesis.config.chain_id.into(),
genesis: genesis_block, genesis: genesis_block,
genesis_hash: None, genesis_hash: None,
fork_timestamps: ForkTimestamps::from_hardforks(&hardforks),
hardforks, hardforks,
} }
} }
} }
/// Various timestamps of forks
#[derive(Debug, Clone, Default, Eq, PartialEq)]
pub struct ForkTimestamps {
/// The timestamp of the shanghai fork
pub shanghai: Option<u64>,
}
impl ForkTimestamps {
/// Creates a new [`ForkTimestamps`] from the given hardforks by extracing the timestamps
fn from_hardforks(forks: &BTreeMap<Hardfork, ForkCondition>) -> Self {
let mut timestamps = ForkTimestamps::default();
if let Some(shanghai) = forks.get(&Hardfork::Shanghai).and_then(|f| f.as_timestamp()) {
timestamps = timestamps.shanghai(shanghai);
}
timestamps
}
/// Sets the given shanghai timestamp
pub fn shanghai(mut self, shanghai: u64) -> Self {
self.shanghai = Some(shanghai);
self
}
}
/// A helper type for compatibility with geth's config /// A helper type for compatibility with geth's config
#[derive(Debug, Clone, Deserialize, Serialize)] #[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(untagged)] #[serde(untagged)]
@ -497,6 +534,7 @@ impl ChainSpecBuilder {
chain: self.chain.expect("The chain is required"), chain: self.chain.expect("The chain is required"),
genesis: self.genesis.expect("The genesis is required"), genesis: self.genesis.expect("The genesis is required"),
genesis_hash: None, genesis_hash: None,
fork_timestamps: ForkTimestamps::from_hardforks(&self.hardforks),
hardforks: self.hardforks, hardforks: self.hardforks,
} }
} }
@ -537,6 +575,11 @@ pub enum ForkCondition {
} }
impl ForkCondition { impl ForkCondition {
/// Returns true if the fork condition is timestamp based.
pub fn is_timestamp(&self) -> bool {
matches!(self, ForkCondition::Timestamp(_))
}
/// Checks whether the fork condition is satisfied at the given block. /// Checks whether the fork condition is satisfied at the given block.
/// ///
/// For TTD conditions, this will only return true if the activation block is already known. /// For TTD conditions, this will only return true if the activation block is already known.
@ -622,6 +665,14 @@ impl ForkCondition {
ForkCondition::Never => unreachable!(), ForkCondition::Never => unreachable!(),
} }
} }
/// Returns the timestamp of the fork condition, if it is timestamp based.
pub fn as_timestamp(&self) -> Option<u64> {
match self {
ForkCondition::Timestamp(timestamp) => Some(*timestamp),
_ => None,
}
}
} }
#[cfg(test)] #[cfg(test)]
@ -644,6 +695,46 @@ mod tests {
} }
} }
// Tests that the ForkTimestamps are correctly set up.
#[test]
fn test_fork_timestamps() {
let spec = ChainSpec::builder().chain(Chain::mainnet()).genesis(Genesis::default()).build();
assert!(spec.fork_timestamps.shanghai.is_none());
let spec = ChainSpec::builder()
.chain(Chain::mainnet())
.genesis(Genesis::default())
.with_fork(Hardfork::Shanghai, ForkCondition::Timestamp(1337))
.build();
assert_eq!(spec.fork_timestamps.shanghai, Some(1337));
assert!(spec.is_shanghai_activated_at_timestamp(1337));
assert!(!spec.is_shanghai_activated_at_timestamp(1336));
}
// Tests that all predefined timestamps are correctly set up in the chainspecs
#[test]
fn test_predefined_chain_spec_fork_timestamps() {
fn ensure_timestamp_fork_conditions(spec: &ChainSpec) {
// This is a sanity test that ensures we always set all currently known fork timestamps,
// this will fail if a new timestamp based fork condition has added to the hardforks but
// no corresponding entry in the ForkTimestamp types, See also
// [ForkTimestamps::from_hardforks]
// currently there are only 1 timestamps known: shanghai
let known_timestamp_based_forks = 1;
let num_timestamp_based_forks =
spec.hardforks.values().copied().filter(ForkCondition::is_timestamp).count();
assert_eq!(num_timestamp_based_forks, known_timestamp_based_forks);
// ensures all timestamp forks are set
assert!(spec.fork_timestamps.shanghai.is_some());
}
for spec in [&*MAINNET, &*SEPOLIA] {
ensure_timestamp_fork_conditions(spec);
}
}
// Tests that we skip any fork blocks in block #0 (the genesis ruleset) // Tests that we skip any fork blocks in block #0 (the genesis ruleset)
#[test] #[test]
fn ignores_genesis_fork_blocks() { fn ignores_genesis_fork_blocks() {

View File

@ -158,6 +158,7 @@ mod tests {
genesis: Genesis::default(), genesis: Genesis::default(),
genesis_hash: None, genesis_hash: None,
hardforks: BTreeMap::from([(Hardfork::Frontier, ForkCondition::Never)]), hardforks: BTreeMap::from([(Hardfork::Frontier, ForkCondition::Never)]),
fork_timestamps: Default::default(),
}; };
assert_eq!(Hardfork::Frontier.fork_id(&spec), None); assert_eq!(Hardfork::Frontier.fork_id(&spec), None);
@ -170,6 +171,7 @@ mod tests {
genesis: Genesis::default(), genesis: Genesis::default(),
genesis_hash: None, genesis_hash: None,
hardforks: BTreeMap::from([(Hardfork::Shanghai, ForkCondition::Never)]), hardforks: BTreeMap::from([(Hardfork::Shanghai, ForkCondition::Never)]),
fork_timestamps: Default::default(),
}; };
assert_eq!(Hardfork::Shanghai.fork_filter(&spec), None); assert_eq!(Hardfork::Shanghai.fork_filter(&spec), None);