Skip to content
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

common: Refactor and update common.getHardforkByBlockNumber to address post merge hfs #2313

Merged
merged 30 commits into from
Sep 29, 2022

Conversation

g11tech
Copy link
Contributor

@g11tech g11tech commented Sep 26, 2022

The sepolia network even post this

{
      "//_comment": "Post merge forks should always have ttd set with block to distinguish that they come post merge hf (block = null)",
      "name": "mergeForkIdTransition",
      "block": 1735371,
      "forkHash": "0xb96cbd13"
    },

was only giving merge hardfork on common.getHardforkByBlockNumber(1735371,"17000000000000000")
and would give mergeForkIdTransition on common.getHardforkByBlockNumber(1735371)

This PR refactors the getHardforkByBlockNumber as the case handling was not clear and upfront

TODO:

  • Add test cases covering the post merge hardforks as well as validations

@codecov
Copy link

codecov bot commented Sep 26, 2022

Codecov Report

Merging #2313 (1cf6ed1) into master (88bd15d) will increase coverage by 1.82%.
The diff coverage is 100.00%.

Additional details and impacted files

Impacted file tree graph

Flag Coverage Δ
block 92.86% <ø> (ø)
blockchain 90.21% <ø> (ø)
client 86.97% <ø> (ø)
common 98.36% <100.00%> (+<0.01%) ⬆️
devp2p ?
ethash ∅ <ø> (∅)
evm ?
rlp ∅ <ø> (∅)
statemanager ?
trie ?
tx 97.98% <ø> (ø)
util 88.99% <ø> (ø)
vm ?

Flags with carried forward coverage won't be shown. Click here to find out more.

@g11tech g11tech force-pushed the g11tech/fix-post-merge-get-set-hardforks branch from bdff698 to f14520b Compare September 27, 2022 16:14
@g11tech g11tech marked this pull request as ready for review September 27, 2022 16:22
Copy link
Contributor

@acolytec3 acolytec3 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will do more review later but here are some initial questions/feedback.

packages/common/src/common.ts Outdated Show resolved Hide resolved
packages/common/src/common.ts Outdated Show resolved Hide resolved
@g11tech g11tech added PR state: merge ready package: common target: master Work to be done towards master branch labels Sep 27, 2022
packages/common/src/common.ts Outdated Show resolved Hide resolved
packages/common/src/common.ts Outdated Show resolved Hide resolved
packages/common/src/common.ts Outdated Show resolved Hide resolved

// hf qualifies else we would have broken out of the loop till now
if (hardfork?.ttd !== undefined && hardfork?.ttd !== null) {
passedMergeHF = hardfork!
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need this in addition to preMergeHf? I think I must not be tracking all of the logic yet because there can be only one hardfork with TTD set. So, if we have that, can't we just use that hardfork (set above as preMergeHf). Also, it seems really odd to call the merge hardfork preMergeHf. The "pre-merge hardfork" is London so maybe we can find a better way to name this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, thats what preMergeHf should represent, its london, and passedMergedHF is assigned merge hardfork if its passed, i guess i can change it to boolean and store the merge hardfork as mergeHF as this later needs to be accessed.

st.equal(c.getHardforkByBlockNumber(0), Hardfork.London, msg)
//using Hardfork.London even though Merge is expected after terminal block 1450408
st.equal(c.getHardforkByBlockNumber(1450409), Hardfork.London, msg)
st.equal(c.getHardforkByBlockNumber(1450409, BigInt('17000000000000000')), Hardfork.Merge, msg)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this result is counterintuitive. The function is called getHardforkByBlockNumber and yet when we set TTD and a block number prior to the actual merge block, it's telling me the hardfork is the Merge hardfork. If we're checking the hardfork and specifying a block number prior to the actual block where the merge happens, we shouldn't report a hardfork that is happening later. I know there's screwiness around Merge and MergeForkTransitionId but this doesn't make a ton of sense to me.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still think this result is counterintuitive. If you provide a block number and the merge hasn't happened yet, regardless of what you pass in for TD, this method should return the previous hardfork. Block number should trump hardfork in terms of which fork is returned.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

merge HAS happened if we provide td > ttd (L73), and hasn't happened if we don't provide td L72

@holgerd77
Copy link
Member

holgerd77 commented Sep 28, 2022

TBH: I have no idea what we are actually solving here.

was only giving merge hardfork on common.getHardforkByBlockNumber(1735371,"17000000000000000")
and would give mergeForkIdTransition on common.getHardforkByBlockNumber(1735371)

This seems like the correct behavior to me. When a TTD is given the TTD takes precedence (and therefore returns the Merge HF with the TTD set, otherwise respectively in the only-block-number case the mergeForkIdTransition HF is returned.

What is the problematic behavior respectively what case can not be modelled with the current method behavior? thinking

@g11tech
Copy link
Contributor Author

g11tech commented Sep 28, 2022

This seems like the correct behavior to me. When a TTD is given the TTD takes precedence (and therefore returns the Merge HF with the TTD set, otherwise respectively in the only-block-number case the mergeForkIdTransition HF is returned.

What is the problematic behavior respectively what case can not be modelled with the current method behavior? thinking

It (common.getHardforkByBlockNumber(1735371,"17000000000000000")) should give mergeForkIdTransition on/after 1735371 as mergeForkIdTransition is scheduled post merge.
currently if the merge block is not specified in hardfork config, it will always return merge even for hardforks scheduled post merge.

The current change is to get the correct mergeForkIdTransition even if one has not specified the block of the merge hardfork.

@holgerd77
Copy link
Member

It (common.getHardforkByBlockNumber(1735371,"17000000000000000")) should give mergeForkIdTransition on/after 1735371 as mergeForkIdTransition is scheduled post merge. i.e. if the merge block is not specified in hardfork config, it will always return merge even for hardforks scheduled post merge.
The current change is to get the correct mergeForkIdTransition even if one has not specified the block of the merge hardfork.

Yes, you should only call the getHardforkByBlockNumber method with TTD if you want to get the Merge HF itself, in all other cases, you should call it without TTD (also for mergeForkIdTransition). That's how I intended it when writing this method. Is this behavior problematic? 🤔

You should just be able to call with TTD on all code parts where you want the Merge HF and otherwise do without.

@g11tech
Copy link
Contributor Author

g11tech commented Sep 28, 2022

You should just be able to call with TTD on all code parts where you want the Merge HF and otherwise do without.

in post merge world, how would one know if the current hardfork is a post merge scheduled hardfork? I was under the impression that is interface (with td) would also provide me the same.

Else I can write a wrapper in the client to combine me the functionality of the hardforkByNumber as well as td

@g11tech
Copy link
Contributor Author

g11tech commented Sep 28, 2022

OR you mean to say, we selectively call with/without td based on if we want to access some merge related functionality?

@acolytec3
Copy link
Contributor

Yes, you should only call the getHardforkByBlockNumber method with TTD if you want to get the Merge HF itself, in all other cases, you should call it without TTD (also for mergeForkIdTransition). That's how I intended it when writing this method. Is this behavior problematic? thinking

If we pass with TTD only, it just becomes a boolean of provided ttd >= mergefork.ttd, right? That doesn't seem like it fits the intended use case for this method and we should create a special purpose method for such.

To my way of thinking, if a user wants to know if a given block is passed the merge hardfork using this method, we should require them to pass both block number and ttd, run through the list of hardforks and find the one that we're on by block number, then check ttd only if the hf by blocknumber says we're on london. If we find a later hardfork with a block number (e.g. in some future world where Shanghai has passed already), then we shouldn't need to look at ttd at all.

Does that logic make sense at all?

@acolytec3
Copy link
Contributor

acolytec3 commented Sep 28, 2022

Could we replace the whole thing with something like this?

 // Filter out hardforks with no block number and no ttd (i.e. unapplied hardforks)
  const hfs = common
    .hardforks()
    .filter((hf) => hf.block !== null || (hf.ttd !== null && hf.ttd !== undefined))
  // The index of the hardfork in the hf array that matches our hardfork based on block number
  const hfIndex = hfs.findIndex((entry) => entry.block !== null && entry.block > blockNumber) - 1
  if (hfs[hfIndex].block === null) {
    // We're on the merge hardfork.  Let's check the TTD
    if (BigInt(hfs[hfIndex].ttd!) > td) {
      // Merge ttd greater than current td so we're on hardfork before merge
      hf = hfs[hfIndex - 1]
    } else {
      // Merge ttd equal or less than current td so we're on merge hardfork
      hf = hfs[hfIndex]
    }
  }
  return hf

This assumes that the hardfork array is in sequentially ascending order (as today) and that hardforks with a null block number and undefined/null ttd are not applied on the current chain and fulfills the logic I outlined above.

packages/common/src/common.ts Outdated Show resolved Hide resolved
st.equal(c.getHardforkByBlockNumber(0), Hardfork.London, msg)
//using Hardfork.London even though Merge is expected after terminal block 1450408
st.equal(c.getHardforkByBlockNumber(1450409), Hardfork.London, msg)
st.equal(c.getHardforkByBlockNumber(1450409, BigInt('17000000000000000')), Hardfork.Merge, msg)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still think this result is counterintuitive. If you provide a block number and the merge hasn't happened yet, regardless of what you pass in for TD, this method should return the previous hardfork. Block number should trump hardfork in terms of which fork is returned.

const prevMergeBlockVal = mergeHf.block
mergeHf.block = null

// should get Hardfork.London even though happened with 1450408 as terminal as config doesn't have that info
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@acolytec3 see if these comments help making sense of the situation

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as terminal as config doesn't have that info -- I don't think this is true any more. You updated the sepolia config to have a block number for the merge.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes but for this test case i made block null, see L209, i have made this test to be independent of what we specify config in json

@g11tech g11tech force-pushed the g11tech/fix-post-merge-get-set-hardforks branch from db7d9ef to d07f8b3 Compare September 29, 2022 11:36
@g11tech g11tech changed the title common: Refactor and update the hardfork cal to address post merge hfs common: Refactor and update common.getHardforkByBlockNumber to address post merge hfs Sep 29, 2022

try {
c.getHardforkByBlockNumber(0)
t.fail('should have thrown since no hardfork should quality')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
t.fail('should have thrown since no hardfork should quality')
t.fail('should have thrown since no hardfork should qualify')

// all hardforks apply, set hfIndex to the last one as thats the candidate
hfIndex = hfs.length - 1
} else if (hfIndex === 0) {
// Cant move back, ideally we should throw??
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's remove this comment

hfIndex = hfs.length - 1
} else if (hfIndex === 0) {
// Cant move back, ideally we should throw??
throw Error('No hardfork qualitifies')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
throw Error('No hardfork qualitifies')
throw Error('Must have at least one hardfork at block 0')

Copy link
Contributor

@acolytec3 acolytec3 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@g11tech g11tech merged commit 7fb9ad8 into master Sep 29, 2022
ScottyPoi pushed a commit that referenced this pull request Sep 29, 2022
…s post merge hfs (#2313)

* common: Refactor and update the hardfork cal to address post merge hfs

* make strick check optional for post merge hardforks

* add tests for post merge hardforks using sepolia

* simplify getHardforkByBlockNumber

* remove duplicate hf

* clarify comment

* add post merge hf

* further simplify the fn and add test to enhance coverage

* update comments

* remove hash to add it as a separate pr to not pollute this one

* fix typo

Co-authored-by: acolytec3 <[email protected]>

* comment improvement

* undo mainnet json changes

* simplify dup ttd check

* fix spec

* fix typo

Co-authored-by: acolytec3 <[email protected]>

* fix typo

Co-authored-by: acolytec3 <[email protected]>

* improve clarity

* simpilfy futher based on acolytec3s logic

* fix some more validations

* remove extra if

* improve wording

Co-authored-by: acolytec3 <[email protected]>

* move spec, add test for merge hf with block num

* relocate test

* relocate test

* lint

* add merge block numbers

* add merge forkhash

* handle no hardfork found case

* Fix nits

Co-authored-by: acolytec3 <[email protected]>
@holgerd77 holgerd77 deleted the g11tech/fix-post-merge-get-set-hardforks branch September 30, 2022 07:41
@holgerd77
Copy link
Member

Ok, cool, thanks for finalizing!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants