mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 10:59:55 +00:00
fix: filter out pre-genesis timestamp forks (#4501)
This commit is contained in:
@ -333,7 +333,7 @@ mod tests {
|
|||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn can_handshake() {
|
async fn can_handshake() {
|
||||||
let genesis = H256::random();
|
let genesis = H256::random();
|
||||||
let fork_filter = ForkFilter::new(Head::default(), genesis, Vec::new());
|
let fork_filter = ForkFilter::new(Head::default(), genesis, 0, Vec::new());
|
||||||
|
|
||||||
let status = Status {
|
let status = Status {
|
||||||
version: EthVersion::Eth67 as u8,
|
version: EthVersion::Eth67 as u8,
|
||||||
@ -380,7 +380,7 @@ mod tests {
|
|||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn pass_handshake_on_low_td_bitlen() {
|
async fn pass_handshake_on_low_td_bitlen() {
|
||||||
let genesis = H256::random();
|
let genesis = H256::random();
|
||||||
let fork_filter = ForkFilter::new(Head::default(), genesis, Vec::new());
|
let fork_filter = ForkFilter::new(Head::default(), genesis, 0, Vec::new());
|
||||||
|
|
||||||
let status = Status {
|
let status = Status {
|
||||||
version: EthVersion::Eth67 as u8,
|
version: EthVersion::Eth67 as u8,
|
||||||
@ -427,7 +427,7 @@ mod tests {
|
|||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn fail_handshake_on_high_td_bitlen() {
|
async fn fail_handshake_on_high_td_bitlen() {
|
||||||
let genesis = H256::random();
|
let genesis = H256::random();
|
||||||
let fork_filter = ForkFilter::new(Head::default(), genesis, Vec::new());
|
let fork_filter = ForkFilter::new(Head::default(), genesis, 0, Vec::new());
|
||||||
|
|
||||||
let status = Status {
|
let status = Status {
|
||||||
version: EthVersion::Eth67 as u8,
|
version: EthVersion::Eth67 as u8,
|
||||||
@ -568,7 +568,7 @@ mod tests {
|
|||||||
);
|
);
|
||||||
|
|
||||||
let genesis = H256::random();
|
let genesis = H256::random();
|
||||||
let fork_filter = ForkFilter::new(Head::default(), genesis, Vec::new());
|
let fork_filter = ForkFilter::new(Head::default(), genesis, 0, Vec::new());
|
||||||
|
|
||||||
let status = Status {
|
let status = Status {
|
||||||
version: EthVersion::Eth67 as u8,
|
version: EthVersion::Eth67 as u8,
|
||||||
|
|||||||
@ -339,6 +339,11 @@ impl ChainSpec {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the timestamp of the genesis block.
|
||||||
|
pub fn genesis_timestamp(&self) -> u64 {
|
||||||
|
self.genesis.timestamp
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the final total difficulty if the given block number is after the Paris hardfork.
|
/// Returns the final total difficulty if the given block number is after the Paris hardfork.
|
||||||
///
|
///
|
||||||
/// Note: technically this would also be valid for the block before the paris upgrade, but this
|
/// Note: technically this would also be valid for the block before the paris upgrade, but this
|
||||||
@ -405,7 +410,7 @@ impl ChainSpec {
|
|||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
ForkFilter::new(head, self.genesis_hash(), forks)
|
ForkFilter::new(head, self.genesis_hash(), self.genesis_timestamp(), forks)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Compute the [`ForkId`] for the given [`Head`] folowing eip-6122 spec
|
/// Compute the [`ForkId`] for the given [`Head`] folowing eip-6122 spec
|
||||||
@ -434,19 +439,22 @@ impl ChainSpec {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// timestamp are ALWAYS applied after the merge.
|
// timestamp are ALWAYS applied after the merge.
|
||||||
for (_, cond) in self.forks_iter() {
|
//
|
||||||
if let ForkCondition::Timestamp(timestamp) = cond {
|
// this filter ensures that no block-based forks are returned
|
||||||
if cond.active_at_head(head) {
|
for timestamp in self.forks_iter().filter_map(|(_, cond)| {
|
||||||
if timestamp != current_applied {
|
cond.as_timestamp().filter(|time| time > &self.genesis.timestamp)
|
||||||
forkhash += timestamp;
|
}) {
|
||||||
current_applied = timestamp;
|
let cond = ForkCondition::Timestamp(timestamp);
|
||||||
}
|
if cond.active_at_head(head) {
|
||||||
} else {
|
if timestamp != current_applied {
|
||||||
// can safely return here because we have already handled all block forks and
|
forkhash += timestamp;
|
||||||
// have handled all active timestamp forks, and set the next value to the
|
current_applied = timestamp;
|
||||||
// timestamp that is known but not active yet
|
|
||||||
return ForkId { hash: forkhash, next: timestamp }
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// can safely return here because we have already handled all block forks and
|
||||||
|
// have handled all active timestamp forks, and set the next value to the
|
||||||
|
// timestamp that is known but not active yet
|
||||||
|
return ForkId { hash: forkhash, next: timestamp }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -594,7 +602,7 @@ impl From<AllGenesisFormats> for ChainSpec {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// A helper to build custom chain specs
|
/// A helper to build custom chain specs
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default, Clone)]
|
||||||
pub struct ChainSpecBuilder {
|
pub struct ChainSpecBuilder {
|
||||||
chain: Option<Chain>,
|
chain: Option<Chain>,
|
||||||
genesis: Option<Genesis>,
|
genesis: Option<Genesis>,
|
||||||
@ -1493,6 +1501,61 @@ Post-merge hard forks (timestamp based):
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Constructs a [ChainSpec] with the given [ChainSpecBuilder], shanghai, and cancun fork
|
||||||
|
/// timestamps.
|
||||||
|
fn construct_chainspec(
|
||||||
|
builder: ChainSpecBuilder,
|
||||||
|
shanghai_time: u64,
|
||||||
|
cancun_time: u64,
|
||||||
|
) -> ChainSpec {
|
||||||
|
builder
|
||||||
|
.with_fork(Hardfork::Shanghai, ForkCondition::Timestamp(shanghai_time))
|
||||||
|
.with_fork(Hardfork::Cancun, ForkCondition::Timestamp(cancun_time))
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Tests that time-based forks which are active at genesis are not included in forkid hash.
|
||||||
|
///
|
||||||
|
/// This is based off of the test vectors here:
|
||||||
|
/// <https://github.com/ethereum/go-ethereum/blob/2e02c1ffd9dffd1ec9e43c6b66f6c9bd1e556a0b/core/forkid/forkid_test.go#L390-L440>
|
||||||
|
#[test]
|
||||||
|
fn test_timestamp_fork_in_genesis() {
|
||||||
|
let timestamp = 1690475657u64;
|
||||||
|
let default_spec_builder = ChainSpecBuilder::default()
|
||||||
|
.chain(Chain::Id(1337))
|
||||||
|
.genesis(Genesis::default().with_timestamp(timestamp))
|
||||||
|
.paris_activated();
|
||||||
|
|
||||||
|
// test format: (chain spec, expected next value) - the forkhash will be determined by the
|
||||||
|
// genesis hash of the constructed chainspec
|
||||||
|
let tests = [
|
||||||
|
(
|
||||||
|
construct_chainspec(default_spec_builder.clone(), timestamp - 1, timestamp + 1),
|
||||||
|
timestamp + 1,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
construct_chainspec(default_spec_builder.clone(), timestamp, timestamp + 1),
|
||||||
|
timestamp + 1,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
construct_chainspec(default_spec_builder.clone(), timestamp + 1, timestamp + 2),
|
||||||
|
timestamp + 1,
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
||||||
|
for (spec, expected_timestamp) in tests {
|
||||||
|
let got_forkid = spec.fork_id(&Head { number: 0, timestamp: 0, ..Default::default() });
|
||||||
|
// This is slightly different from the geth test because we use the shanghai timestamp
|
||||||
|
// to determine whether or not to include a withdrawals root in the genesis header.
|
||||||
|
// This makes the genesis hash different, and as a result makes the ChainSpec fork hash
|
||||||
|
// different.
|
||||||
|
let genesis_hash = spec.genesis_hash();
|
||||||
|
let expected_forkid =
|
||||||
|
ForkId { hash: ForkHash::from(genesis_hash), next: expected_timestamp };
|
||||||
|
assert_eq!(got_forkid, expected_forkid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Checks that the fork is not active at a terminal ttd block.
|
/// Checks that the fork is not active at a terminal ttd block.
|
||||||
#[test]
|
#[test]
|
||||||
fn check_terminal_ttd() {
|
fn check_terminal_ttd() {
|
||||||
|
|||||||
@ -165,6 +165,7 @@ pub struct ForkFilter {
|
|||||||
/// [eip-6122]: https://eips.ethereum.org/EIPS/eip-6122
|
/// [eip-6122]: https://eips.ethereum.org/EIPS/eip-6122
|
||||||
forks: BTreeMap<ForkFilterKey, ForkHash>,
|
forks: BTreeMap<ForkFilterKey, ForkHash>,
|
||||||
|
|
||||||
|
/// The current head, used to select forks that are active locally.
|
||||||
head: Head,
|
head: Head,
|
||||||
|
|
||||||
cache: Cache,
|
cache: Cache,
|
||||||
@ -173,17 +174,22 @@ pub struct ForkFilter {
|
|||||||
impl ForkFilter {
|
impl ForkFilter {
|
||||||
/// Create the filter from provided head, genesis block hash, past forks and expected future
|
/// Create the filter from provided head, genesis block hash, past forks and expected future
|
||||||
/// forks.
|
/// forks.
|
||||||
pub fn new<F>(head: Head, genesis: H256, forks: F) -> Self
|
pub fn new<F>(head: Head, genesis_hash: H256, genesis_timestamp: u64, forks: F) -> Self
|
||||||
where
|
where
|
||||||
F: IntoIterator<Item = ForkFilterKey>,
|
F: IntoIterator<Item = ForkFilterKey>,
|
||||||
{
|
{
|
||||||
let genesis_fork_hash = ForkHash::from(genesis);
|
let genesis_fork_hash = ForkHash::from(genesis_hash);
|
||||||
let mut forks = forks.into_iter().collect::<BTreeSet<_>>();
|
let mut forks = forks.into_iter().collect::<BTreeSet<_>>();
|
||||||
forks.remove(&ForkFilterKey::Time(0));
|
forks.remove(&ForkFilterKey::Time(0));
|
||||||
forks.remove(&ForkFilterKey::Block(0));
|
forks.remove(&ForkFilterKey::Block(0));
|
||||||
|
|
||||||
let forks = forks
|
let forks = forks
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
// filter out forks that are pre-genesis by timestamp
|
||||||
|
.filter(|key| match key {
|
||||||
|
ForkFilterKey::Block(_) => true,
|
||||||
|
ForkFilterKey::Time(time) => *time > genesis_timestamp,
|
||||||
|
})
|
||||||
.fold(
|
.fold(
|
||||||
(BTreeMap::from([(ForkFilterKey::Block(0), genesis_fork_hash)]), genesis_fork_hash),
|
(BTreeMap::from([(ForkFilterKey::Block(0), genesis_fork_hash)]), genesis_fork_hash),
|
||||||
|(mut acc, base_hash), key| {
|
|(mut acc, base_hash), key| {
|
||||||
@ -395,6 +401,7 @@ mod tests {
|
|||||||
let mut filter = ForkFilter::new(
|
let mut filter = ForkFilter::new(
|
||||||
Head { number: 0, ..Default::default() },
|
Head { number: 0, ..Default::default() },
|
||||||
GENESIS_HASH,
|
GENESIS_HASH,
|
||||||
|
0,
|
||||||
vec![
|
vec![
|
||||||
ForkFilterKey::Block(1_150_000),
|
ForkFilterKey::Block(1_150_000),
|
||||||
ForkFilterKey::Block(1_920_000),
|
ForkFilterKey::Block(1_920_000),
|
||||||
@ -568,6 +575,7 @@ mod tests {
|
|||||||
let mut fork_filter = ForkFilter::new(
|
let mut fork_filter = ForkFilter::new(
|
||||||
Head { number: 0, ..Default::default() },
|
Head { number: 0, ..Default::default() },
|
||||||
GENESIS_HASH,
|
GENESIS_HASH,
|
||||||
|
0,
|
||||||
vec![ForkFilterKey::Block(b1), ForkFilterKey::Block(b2)],
|
vec![ForkFilterKey::Block(b1), ForkFilterKey::Block(b2)],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user