Add ChainSpec::hardfork_fork_id and ChainSpec::shanghai_fork_id helper fns (#4748)

This commit is contained in:
prames
2023-10-03 18:30:05 +05:30
committed by GitHub
parent 6396e3f400
commit 2cddd0f63e
2 changed files with 363 additions and 14 deletions

View File

@@ -437,6 +437,16 @@ impl ChainSpec {
&self.hardforks
}
/// Get the fork id for the given hardfork.
pub fn hardfork_fork_id(&self, fork: Hardfork) -> Option<ForkId> {
fork.fork_id(self)
}
/// Convenience method to get the fork id for [Hardfork::Shanghai] from a given chainspec.
pub fn shanghai_fork_id(&self) -> Option<ForkId> {
Hardfork::Shanghai.fork_id(self)
}
/// Get the fork condition for the given fork.
pub fn fork(&self, fork: Hardfork) -> ForkCondition {
self.hardforks.get(&fork).copied().unwrap_or(ForkCondition::Never)
@@ -535,6 +545,62 @@ impl ChainSpec {
ForkId { hash: forkhash, next: 0 }
}
/// An internal helper function that returns a head block that satisfies a given Fork condition.
pub(crate) fn satisfy(&self, cond: ForkCondition) -> Head {
match cond {
ForkCondition::Block(number) => Head { number, ..Default::default() },
ForkCondition::Timestamp(timestamp) => {
// to satisfy every timestamp ForkCondition, we find the last ForkCondition::Block
// if one exists, and include its block_num in the returned Head
if let Some(last_block_num) = self.last_block_fork_before_merge_or_timestamp() {
return Head { timestamp, number: last_block_num, ..Default::default() }
}
Head { timestamp, ..Default::default() }
}
ForkCondition::TTD { total_difficulty, .. } => {
Head { total_difficulty, ..Default::default() }
}
ForkCondition::Never => unreachable!(),
}
}
/// An internal helper function that returns the block number of the last block-based
/// fork that occurs before any existing TTD (merge)/timestamp based forks.
///
/// Note: this returns None if the ChainSpec is not configured with a TTD/Timestamp fork.
pub(crate) fn last_block_fork_before_merge_or_timestamp(&self) -> Option<u64> {
let mut hardforks_iter = self.forks_iter().peekable();
while let Some((_, curr_cond)) = hardforks_iter.next() {
if let Some((_, next_cond)) = hardforks_iter.peek() {
// peek and find the first occurence of ForkCondition::TTD (merge) , or in
// custom ChainSpecs, the first occurence of
// ForkCondition::Timestamp. If curr_cond is ForkCondition::Block at
// this point, which it should be in most "normal" ChainSpecs,
// return its block_num
match next_cond {
ForkCondition::TTD { fork_block, .. } => {
// handle Sepolia merge netsplit case
if fork_block.is_some() {
return *fork_block
}
// ensure curr_cond is indeed ForkCondition::Block and return block_num
if let ForkCondition::Block(block_num) = curr_cond {
return Some(block_num)
}
}
ForkCondition::Timestamp(_) => {
// ensure curr_cond is indeed ForkCondition::Block and return block_num
if let ForkCondition::Block(block_num) = curr_cond {
return Some(block_num)
}
}
ForkCondition::Block(_) | ForkCondition::Never => continue,
}
}
}
None
}
/// Build a chainspec using [`ChainSpecBuilder`]
pub fn builder() -> ChainSpecBuilder {
ChainSpecBuilder::default()
@@ -941,18 +1007,6 @@ impl ForkCondition {
}
}
/// An internal helper function that gives a value that satisfies this condition.
pub(crate) fn satisfy(&self) -> Head {
match *self {
ForkCondition::Block(number) => Head { number, ..Default::default() },
ForkCondition::Timestamp(timestamp) => Head { timestamp, ..Default::default() },
ForkCondition::TTD { total_difficulty, .. } => {
Head { total_difficulty, ..Default::default() }
}
ForkCondition::Never => unreachable!(),
}
}
/// Returns the timestamp of the fork condition, if it is timestamp based.
pub fn as_timestamp(&self) -> Option<u64> {
match self {
@@ -1151,6 +1205,29 @@ mod tests {
}
}
fn test_hardfork_fork_ids(spec: &ChainSpec, cases: &[(Hardfork, ForkId)]) {
for (hardfork, expected_id) in cases {
if let Some(computed_id) = spec.hardfork_fork_id(*hardfork) {
assert_eq!(
expected_id, &computed_id,
"Expected fork ID {:?}, computed fork ID {:?} for hardfork {}",
expected_id, computed_id, hardfork
);
if let Hardfork::Shanghai = hardfork {
if let Some(shangai_id) = spec.shanghai_fork_id() {
assert_eq!(
expected_id, &shangai_id,
"Expected fork ID {:?}, computed fork ID {:?} for Shanghai hardfork",
expected_id, computed_id
);
} else {
panic!("Expected ForkCondition to return Some for Hardfork::Shanghai");
}
}
}
}
}
#[test]
fn test_hardfork_list_display_mainnet() {
assert_eq!(
@@ -1288,6 +1365,278 @@ Post-merge hard forks (timestamp based):
);
}
#[test]
fn test_chainspec_satisfy() {
let empty_genesis = Genesis::default();
// happy path test case
let happy_path_case = ChainSpec::builder()
.chain(Chain::mainnet())
.genesis(empty_genesis.clone())
.with_fork(Hardfork::Frontier, ForkCondition::Block(0))
.with_fork(Hardfork::Homestead, ForkCondition::Block(73))
.with_fork(Hardfork::Shanghai, ForkCondition::Timestamp(11313123))
.build();
let happy_path_head = happy_path_case.satisfy(ForkCondition::Timestamp(11313123));
let happy_path_expected = Head { number: 73, timestamp: 11313123, ..Default::default() };
assert_eq!(
happy_path_head, happy_path_expected,
"expected satisfy() to return {:#?}, but got {:#?} ",
happy_path_expected, happy_path_head
);
// multiple timestamp test case (i.e Shanghai -> Cancun)
let multiple_timestamp_fork_case = ChainSpec::builder()
.chain(Chain::mainnet())
.genesis(empty_genesis.clone())
.with_fork(Hardfork::Frontier, ForkCondition::Block(0))
.with_fork(Hardfork::Homestead, ForkCondition::Block(73))
.with_fork(Hardfork::Shanghai, ForkCondition::Timestamp(11313123))
.with_fork(Hardfork::Cancun, ForkCondition::Timestamp(11313398))
.build();
let multi_timestamp_head =
multiple_timestamp_fork_case.satisfy(ForkCondition::Timestamp(11313398));
let mult_timestamp_expected =
Head { number: 73, timestamp: 11313398, ..Default::default() };
assert_eq!(
multi_timestamp_head, mult_timestamp_expected,
"expected satisfy() to return {:#?}, but got {:#?} ",
mult_timestamp_expected, multi_timestamp_head
);
// no ForkCondition::Block test case
let no_block_fork_case = ChainSpec::builder()
.chain(Chain::mainnet())
.genesis(empty_genesis.clone())
.with_fork(Hardfork::Shanghai, ForkCondition::Timestamp(11313123))
.build();
let no_block_fork_head = no_block_fork_case.satisfy(ForkCondition::Timestamp(11313123));
let no_block_fork_expected = Head { number: 0, timestamp: 11313123, ..Default::default() };
assert_eq!(
no_block_fork_head, no_block_fork_expected,
"expected satisfy() to return {:#?}, but got {:#?} ",
no_block_fork_expected, no_block_fork_head
);
// spec w/ ForkCondition::TTD with block_num test case (Sepolia merge netsplit edge case)
let fork_cond_ttd_blocknum_case = ChainSpec::builder()
.chain(Chain::mainnet())
.genesis(empty_genesis.clone())
.with_fork(Hardfork::Frontier, ForkCondition::Block(0))
.with_fork(Hardfork::Homestead, ForkCondition::Block(73))
.with_fork(
Hardfork::Paris,
ForkCondition::TTD {
fork_block: Some(101),
total_difficulty: U256::from(10_790_000),
},
)
.with_fork(Hardfork::Shanghai, ForkCondition::Timestamp(11313123))
.build();
let fork_cond_ttd_blocknum_head =
fork_cond_ttd_blocknum_case.satisfy(ForkCondition::Timestamp(11313123));
let fork_cond_ttd_blocknum_expected =
Head { number: 101, timestamp: 11313123, ..Default::default() };
assert_eq!(
fork_cond_ttd_blocknum_head, fork_cond_ttd_blocknum_expected,
"expected satisfy() to return {:#?}, but got {:#?} ",
fork_cond_ttd_blocknum_expected, fork_cond_ttd_blocknum_head
);
// spec w/ only ForkCondition::Block - test the match arm for ForkCondition::Block to ensure
// no regressions, for these ForkConditions(Block/TTD) - a separate chain spec definition is
// technically unecessary - but we include it here for thoroughness
let fork_cond_block_only_case = ChainSpec::builder()
.chain(Chain::mainnet())
.genesis(empty_genesis.clone())
.with_fork(Hardfork::Frontier, ForkCondition::Block(0))
.with_fork(Hardfork::Homestead, ForkCondition::Block(73))
.build();
let fork_cond_block_only_head = fork_cond_block_only_case.satisfy(ForkCondition::Block(73));
let fork_cond_block_only_expected = Head { number: 73, ..Default::default() };
assert_eq!(
fork_cond_block_only_head, fork_cond_block_only_expected,
"expected satisfy() to return {:#?}, but got {:#?} ",
fork_cond_block_only_expected, fork_cond_block_only_head
);
// Fork::ConditionTTD test case without a new chain spec to demonstrate ChainSpec::satisfy
// is independent of ChainSpec for this(these - including ForkCondition::Block) match arm(s)
let fork_cond_ttd_no_new_spec = fork_cond_block_only_case.satisfy(ForkCondition::TTD {
fork_block: None,
total_difficulty: U256::from(10_790_000),
});
let fork_cond_ttd_no_new_spec_expected =
Head { total_difficulty: U256::from(10_790_000), ..Default::default() };
assert_eq!(
fork_cond_ttd_no_new_spec, fork_cond_ttd_no_new_spec_expected,
"expected satisfy() to return {:#?}, but got {:#?} ",
fork_cond_ttd_no_new_spec_expected, fork_cond_ttd_no_new_spec
);
}
#[test]
fn mainnet_hardfork_fork_ids() {
test_hardfork_fork_ids(
&MAINNET,
&[
(
Hardfork::Frontier,
ForkId { hash: ForkHash([0xfc, 0x64, 0xec, 0x04]), next: 1150000 },
),
(
Hardfork::Homestead,
ForkId { hash: ForkHash([0x97, 0xc2, 0xc3, 0x4c]), next: 1920000 },
),
(Hardfork::Dao, ForkId { hash: ForkHash([0x91, 0xd1, 0xf9, 0x48]), next: 2463000 }),
(
Hardfork::Tangerine,
ForkId { hash: ForkHash([0x7a, 0x64, 0xda, 0x13]), next: 2675000 },
),
(
Hardfork::SpuriousDragon,
ForkId { hash: ForkHash([0x3e, 0xdd, 0x5b, 0x10]), next: 4370000 },
),
(
Hardfork::Byzantium,
ForkId { hash: ForkHash([0xa0, 0x0b, 0xc3, 0x24]), next: 7280000 },
),
(
Hardfork::Constantinople,
ForkId { hash: ForkHash([0x66, 0x8d, 0xb0, 0xaf]), next: 9069000 },
),
(
Hardfork::Petersburg,
ForkId { hash: ForkHash([0x66, 0x8d, 0xb0, 0xaf]), next: 9069000 },
),
(
Hardfork::Istanbul,
ForkId { hash: ForkHash([0x87, 0x9d, 0x6e, 0x30]), next: 9200000 },
),
(
Hardfork::MuirGlacier,
ForkId { hash: ForkHash([0xe0, 0x29, 0xe9, 0x91]), next: 12244000 },
),
(
Hardfork::Berlin,
ForkId { hash: ForkHash([0x0e, 0xb4, 0x40, 0xf6]), next: 12965000 },
),
(
Hardfork::London,
ForkId { hash: ForkHash([0xb7, 0x15, 0x07, 0x7d]), next: 13773000 },
),
(
Hardfork::ArrowGlacier,
ForkId { hash: ForkHash([0x20, 0xc3, 0x27, 0xfc]), next: 15050000 },
),
(
Hardfork::GrayGlacier,
ForkId { hash: ForkHash([0xf0, 0xaf, 0xd0, 0xe3]), next: 1681338455 },
),
(Hardfork::Shanghai, ForkId { hash: ForkHash([0xdc, 0xe9, 0x6c, 0x2d]), next: 0 }),
],
);
}
#[test]
fn goerli_hardfork_fork_ids() {
test_hardfork_fork_ids(
&GOERLI,
&[
(
Hardfork::Frontier,
ForkId { hash: ForkHash([0xa3, 0xf5, 0xab, 0x08]), next: 1561651 },
),
(
Hardfork::Homestead,
ForkId { hash: ForkHash([0xa3, 0xf5, 0xab, 0x08]), next: 1561651 },
),
(
Hardfork::Tangerine,
ForkId { hash: ForkHash([0xa3, 0xf5, 0xab, 0x08]), next: 1561651 },
),
(
Hardfork::SpuriousDragon,
ForkId { hash: ForkHash([0xa3, 0xf5, 0xab, 0x08]), next: 1561651 },
),
(
Hardfork::Byzantium,
ForkId { hash: ForkHash([0xa3, 0xf5, 0xab, 0x08]), next: 1561651 },
),
(
Hardfork::Constantinople,
ForkId { hash: ForkHash([0xa3, 0xf5, 0xab, 0x08]), next: 1561651 },
),
(
Hardfork::Petersburg,
ForkId { hash: ForkHash([0xa3, 0xf5, 0xab, 0x08]), next: 1561651 },
),
(
Hardfork::Istanbul,
ForkId { hash: ForkHash([0xc2, 0x5e, 0xfa, 0x5c]), next: 4460644 },
),
(
Hardfork::Berlin,
ForkId { hash: ForkHash([0x75, 0x7a, 0x1c, 0x47]), next: 5062605 },
),
(
Hardfork::London,
ForkId { hash: ForkHash([0xb8, 0xc6, 0x29, 0x9d]), next: 1678832736 },
),
(Hardfork::Shanghai, ForkId { hash: ForkHash([0xf9, 0x84, 0x3a, 0xbf]), next: 0 }),
],
);
}
#[test]
fn sepolia_hardfork_fork_ids() {
test_hardfork_fork_ids(
&SEPOLIA,
&[
(
Hardfork::Frontier,
ForkId { hash: ForkHash([0xfe, 0x33, 0x66, 0xe7]), next: 1735371 },
),
(
Hardfork::Homestead,
ForkId { hash: ForkHash([0xfe, 0x33, 0x66, 0xe7]), next: 1735371 },
),
(
Hardfork::Tangerine,
ForkId { hash: ForkHash([0xfe, 0x33, 0x66, 0xe7]), next: 1735371 },
),
(
Hardfork::SpuriousDragon,
ForkId { hash: ForkHash([0xfe, 0x33, 0x66, 0xe7]), next: 1735371 },
),
(
Hardfork::Byzantium,
ForkId { hash: ForkHash([0xfe, 0x33, 0x66, 0xe7]), next: 1735371 },
),
(
Hardfork::Constantinople,
ForkId { hash: ForkHash([0xfe, 0x33, 0x66, 0xe7]), next: 1735371 },
),
(
Hardfork::Petersburg,
ForkId { hash: ForkHash([0xfe, 0x33, 0x66, 0xe7]), next: 1735371 },
),
(
Hardfork::Istanbul,
ForkId { hash: ForkHash([0xfe, 0x33, 0x66, 0xe7]), next: 1735371 },
),
(
Hardfork::Berlin,
ForkId { hash: ForkHash([0xfe, 0x33, 0x66, 0xe7]), next: 1735371 },
),
(
Hardfork::London,
ForkId { hash: ForkHash([0xfe, 0x33, 0x66, 0xe7]), next: 1735371 },
),
(
Hardfork::Paris,
ForkId { hash: ForkHash([0xb9, 0x6c, 0xbd, 0x13]), next: 1677557088 },
),
(Hardfork::Shanghai, ForkId { hash: ForkHash([0xf7, 0xf9, 0xbc, 0x08]), next: 0 }),
],
);
}
#[test]
fn mainnet_forkids() {
test_fork_ids(

View File

@@ -48,7 +48,7 @@ impl Hardfork {
pub fn fork_id(&self, spec: &ChainSpec) -> Option<ForkId> {
match spec.fork(*self) {
ForkCondition::Never => None,
_ => Some(spec.fork_id(&spec.fork(*self).satisfy())),
_ => Some(spec.fork_id(&spec.satisfy(spec.fork(*self)))),
}
}
@@ -57,7 +57,7 @@ impl Hardfork {
pub fn fork_filter(&self, spec: &ChainSpec) -> Option<ForkFilter> {
match spec.fork(*self) {
ForkCondition::Never => None,
_ => Some(spec.fork_filter(spec.fork(*self).satisfy())),
_ => Some(spec.fork_filter(spec.satisfy(spec.fork(*self)))),
}
}
}