-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add ChainSpec::hardfork_fork_id and ChainSpec::shanghai_fork_id helper fns #4748
Add ChainSpec::hardfork_fork_id and ChainSpec::shanghai_fork_id helper fns #4748
Conversation
Codecov Report
... and 8 files with indirect coverage changes
Flags with carried forward coverage won't be shown. Click here to find out more.
|
d62d1c6
to
f84c60e
Compare
6c0ad8f
to
5aef0a5
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ptal @Rjected
crates/primitives/src/chain/spec.rs
Outdated
// the timestamp check here is a bit brittle as its tied to the self.satisfy() impl | ||
// returning a non-0 head.timestamp for ForkCondition::Timestamp, and a 0 | ||
// timestamp for the other ForkCondition types | ||
// this check is required though - without it, we will drop into the else block and | ||
// shortcircuit for heads valid during timestamp-based forks, leading to an | ||
// incorrect ForkId for Shanghai, Cancun etc. An alternative fix | ||
// here would be to modify the Head returned in self.satisfy() to | ||
// include a valid blocknum for ForkCondition::Timestamp, thus not | ||
// excercising the else block, and calculating an incorrect fork_hash |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I really don't understand why we need this now,
this PR was supposed to add support for this on Chainspec
reth/crates/primitives/src/hardfork.rs
Lines 47 to 53 in b7a6eed
/// Get the [ForkId] for this hardfork in the given spec, if the fork is activated at any point. | |
pub fn fork_id(&self, spec: &ChainSpec) -> Option<ForkId> { | |
match spec.fork(*self) { | |
ForkCondition::Never => None, | |
_ => Some(spec.fork_id(&spec.fork(*self).satisfy())), | |
} | |
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
spec.fork_id(&spec.fork(*self).satisfy())
this line has a bug for Hardfork::Shanghai
and all timestamp based HardForks - I have removed the fix above and push up the assoc. test failures.
I caught it while adding unit tests - The existing test coverage differs from the satisfy()
actual implementation, and the tests I've added in this PR hook into the underlying satisfy()
call.
See:
running 1 test
thread 'chain::spec::tests::mainnet_hardfork_fork_ids' panicked at 'assertion failed: `(left == right)`
left: `ForkId { hash: ForkHash("dce96c2d"), next: 0 }`,
right: `ForkId { hash: ForkHash("fc64ec04"), next: 1150000 }`: Expected fork ID ForkId { hash: ForkHash("dce96c2d"), next: 0 }, computed fork ID ForkId { hash: ForkHash("fc64ec04"), next: 1150000 } for hardfork Shanghai', crates/primitives/src/chain/spec.rs:1179:17
stack backtrace:
Happy to sync with @Rjected on this further.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Without my change the ForkId
for Shanghai and presumably all the future timestamp based forks (Cancun etc) will short-circuit the existing logic and return the ForkId
for Frontier
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is probably a bug in satisfy
, since to obtain the ForkId
for shanghai, the fork_id
method uses the block number of the Head
argument to apply the block-based forks, before adding the timestamp-based forks. Satisfy does this:
reth/crates/primitives/src/chain/spec.rs
Lines 939 to 943 in 7a0781c
ForkCondition::Block(number) => Head { number, ..Default::default() }, | |
ForkCondition::Timestamp(timestamp) => Head { timestamp, ..Default::default() }, | |
ForkCondition::TTD { total_difficulty, .. } => { | |
Head { total_difficulty, ..Default::default() } | |
} |
fork_id
does this:
reth/crates/primitives/src/chain/spec.rs
Lines 488 to 498 in 7a0781c
for (_, cond) in self.forks_iter() { | |
// handle block based forks and the sepolia merge netsplit block edge case (TTD | |
// ForkCondition with Some(block)) | |
if let ForkCondition::Block(block) | | |
ForkCondition::TTD { fork_block: Some(block), .. } = cond | |
{ | |
if cond.active_at_head(head) { | |
if block != current_applied { | |
forkhash += block; | |
current_applied = block; | |
} |
So for satisfy
to work with timestamp forks, it needs to return a Head
with a block and TTD greater than the highest known fork block / TTD respectively. It should also not use Default::default
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah exactly!
called similar out in the comments:
An alternative fix
// here would be to modify the Head returned in self.satisfy() to
// include a valid blocknum for ForkCondition::Timestamp,
I can fix it there instead of this timestamp fix, I'll push that up in the next rev
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ah so... I spent a bit of time looking into this, and fixing it in satisfy()
is a little bit more difficult than I thought.
That method is type ForkCondition::satisfy()
and effectively only has access each enum variant's values. So in this case. satisfy()
only has access to either
ForkCondition::Block(block_num) => Head { block_num, .. Default())
ForkCondition::Timestamp(timestamp) = Head {timestamp, .. Default())
so if we want to fix it in satisfy()
we may need to change ForkCondition::Timestamp(u64,u64)
to include both the block_num
and timestamp
- and change it everywhere its been declared/initalized.
It will be a larger code change - which I'm open to take on. The alternative to fixing it in satisfy()
is to instead just add
if cond.active_at_head(head) || head.timestamp > 0
like it was in the first push up the prev commit to this PR.
Wdyt @Rjected - should we refactor ForkCondition::Timestamp
to include a block_num in the variant vals? It feels like this may take away from the whole "timestamp based fork condition" aspect, but it may also be necessary.
Happy to implement another solution as well - if one exists.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
also - it doesn't make sense to just hardcode a blocknum/ttd in that Head{}
return because it varies per chain.
so mainnet's forkcondition::timestamp for Shanghai/Cancun will be different than sepolias.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't really want to add in block nums to timestamp forks. Maybe ForkCondition
is the wrong place to put satisfy though - looking at the two places where it's used:
reth/crates/primitives/src/hardfork.rs
Lines 47 to 62 in 4603dc3
/// Get the [ForkId] for this hardfork in the given spec, if the fork is activated at any point. | |
pub fn fork_id(&self, spec: &ChainSpec) -> Option<ForkId> { | |
match spec.fork(*self) { | |
ForkCondition::Never => None, | |
_ => Some(spec.fork_id(&spec.fork(*self).satisfy())), | |
} | |
} | |
/// Get the [ForkFilter] for this hardfork in the given spec, if the fork is activated at any | |
/// point. | |
pub fn fork_filter(&self, spec: &ChainSpec) -> Option<ForkFilter> { | |
match spec.fork(*self) { | |
ForkCondition::Never => None, | |
_ => Some(spec.fork_filter(spec.fork(*self).satisfy())), | |
} | |
} |
It looks like we have access to the ChainSpec
- so this could be ChainSpec::satisfy
instead of ForkCondition::satisfy
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
alright, let me take a look.
crates/primitives/src/chain/spec.rs
Outdated
/// Get the fork id for the given hardfork. | ||
pub fn hardfork_fork_id(&self, fork: Hardfork) -> ForkId { | ||
self.fork_id(&self.fork(fork).satisfy()) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why is this not an option here? but on hardfork it is.
looks like this does not handle the ::Never case
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ah sorry about that - added.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is a bug in satisfy
, see comment for why
crates/primitives/src/chain/spec.rs
Outdated
// the timestamp check here is a bit brittle as its tied to the self.satisfy() impl | ||
// returning a non-0 head.timestamp for ForkCondition::Timestamp, and a 0 | ||
// timestamp for the other ForkCondition types | ||
// this check is required though - without it, we will drop into the else block and | ||
// shortcircuit for heads valid during timestamp-based forks, leading to an | ||
// incorrect ForkId for Shanghai, Cancun etc. An alternative fix | ||
// here would be to modify the Head returned in self.satisfy() to | ||
// include a valid blocknum for ForkCondition::Timestamp, thus not | ||
// excercising the else block, and calculating an incorrect fork_hash |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is probably a bug in satisfy
, since to obtain the ForkId
for shanghai, the fork_id
method uses the block number of the Head
argument to apply the block-based forks, before adding the timestamp-based forks. Satisfy does this:
reth/crates/primitives/src/chain/spec.rs
Lines 939 to 943 in 7a0781c
ForkCondition::Block(number) => Head { number, ..Default::default() }, | |
ForkCondition::Timestamp(timestamp) => Head { timestamp, ..Default::default() }, | |
ForkCondition::TTD { total_difficulty, .. } => { | |
Head { total_difficulty, ..Default::default() } | |
} |
fork_id
does this:
reth/crates/primitives/src/chain/spec.rs
Lines 488 to 498 in 7a0781c
for (_, cond) in self.forks_iter() { | |
// handle block based forks and the sepolia merge netsplit block edge case (TTD | |
// ForkCondition with Some(block)) | |
if let ForkCondition::Block(block) | | |
ForkCondition::TTD { fork_block: Some(block), .. } = cond | |
{ | |
if cond.active_at_head(head) { | |
if block != current_applied { | |
forkhash += block; | |
current_applied = block; | |
} |
So for satisfy
to work with timestamp forks, it needs to return a Head
with a block and TTD greater than the highest known fork block / TTD respectively. It should also not use Default::default
.
8daa6f0
to
76285dd
Compare
should be ready for another review @Rjected |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not familiar with how this works,
defer to @Rjected
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
logic-wise this looks good, could we add some tests for the new satisfy method?
…isfy() in fork_filter
c347a47
to
01e3801
Compare
cool, I added a relatively comprehensive unit test to cover the cases not already covered in the should cover all conditional/match arms |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lgtm, pending @Rjected
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lgtm, the tests are great!
should close #4745
opening this PR as draft - want to add some tests for these helpers