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

Implement EIP 2935 #3268

Merged
merged 24 commits into from
Mar 4, 2024
Merged

Implement EIP 2935 #3268

merged 24 commits into from
Mar 4, 2024

Conversation

jochem-brouwer
Copy link
Member

@jochem-brouwer jochem-brouwer commented Feb 3, 2024

This PR is based upon the EIP 2935 version of: ethereum/EIPs#8166, which is currently not merged.

Note: this also adds functionality to Common which adds the ability to specify new hardforks, where users can describe when they want these forks to be activated (handy for EIP transitioning tests which have not yet been assigned to a hardfork)

Copy link

codecov bot commented Feb 28, 2024

Codecov Report

Attention: Patch coverage is 71.06918% with 46 lines in your changes are missing coverage. Please review.

Project coverage is 86.87%. Comparing base (f502494) to head (f9ce54c).

Additional details and impacted files

Impacted file tree graph

Flag Coverage Δ
block 88.34% <ø> (ø)
blockchain 91.61% <ø> (ø)
client 84.77% <ø> (ø)
common 98.43% <100.00%> (+0.18%) ⬆️
devp2p 82.12% <ø> (ø)
ethash ∅ <ø> (∅)
evm 74.03% <13.88%> (-0.22%) ⬇️
genesis 99.98% <ø> (ø)
rlp ∅ <ø> (∅)
statemanager 77.00% <ø> (ø)
trie 89.24% <ø> (-0.04%) ⬇️
tx 95.45% <ø> (ø)
util 89.13% <ø> (ø)
vm 79.84% <80.76%> (+0.22%) ⬆️
wallet 88.35% <ø> (ø)

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

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.

Left some initial feedback but looks good in the main. WIll need to dig into the tests more tomorrow.

* @returns Block timestamp or null if unscheduled
*/
eipTimestamp(eip: number): bigint | null {
// NOTE: this is not elegant, what if the eip got activated on a block and not timestamp?
Copy link
Contributor

Choose a reason for hiding this comment

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

Don't the older hardfork block headers still have a timestamp in them that we can report here? I'm not sure if this is really a concern. Or, maybe better yet, we just return null for any EIP that's not scheduled or pre-Shanghai.

Copy link
Member Author

Choose a reason for hiding this comment

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

Oh right, this is indeed an interesting line of thought. However this would make the method async and we would need to get the block from blockchain in order to retrieve the timestamp 🤔 Not sure I like that. Especially gets a bit hairy if it reorgs (you save the timestamp, chain reorgs, and now the timestamp differs). I will update the docs to remove the "block" part and just write down that it returns the timestamp when this EIP is scheduled (therefore, on missed slots the first block might actually be later than the scheduled timestamp)

Copy link
Member Author

Choose a reason for hiding this comment

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

Ahh! Wait! I now remember the point I wanted to make here. The same applies to eipBlock actually. If eipTimestamp returns null (for instance, for Dao), then we know it is either (1) not scheduled at a timestamp (but at a block number) or (2) it is not even scheduled at all (so the fork will never happen). I think this is beyond the scope of this PR though and it is also an edge case.

packages/common/test/customChains.spec.ts Show resolved Hide resolved
packages/vm/src/runBlock.ts Outdated Show resolved Hide resolved
Copy link
Member

@holgerd77 holgerd77 left a comment

Choose a reason for hiding this comment

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

Some comments to addresss...

@@ -190,6 +191,23 @@ export const EIPs: EIPsDict = {
},
},
},
2935: {
comment: 'Save historical block hashes in state',
Copy link
Member

Choose a reason for hiding this comment

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

Can we add some addition like ...state (Verkle related usage, likely not stable yet) or so?

(let me know if the comment does not make sense "as is")

Copy link
Member Author

Choose a reason for hiding this comment

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

It does, I normally just copy the EIP title but you are right this definitely should get an unstable warning.

Copy link
Member

Choose a reason for hiding this comment

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

👍

Take whatever text you like, just that people know this is not "one of the normal EIPs they can integrate" (and DRAFT status is not that much of a warning, even EIPs close to hardfork often still in DRAFT 😂 ).

packages/evm/src/opcodes/functions.ts Show resolved Hide resolved
packages/vm/src/runBlock.ts Outdated Show resolved Hide resolved
const forkTime = this.common.eipTimestamp(2935)
if (forkTime === null) {
throw new Error('EIP 2935 should be activated by timestamp')
}
Copy link
Member

Choose a reason for hiding this comment

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

I don't think that we need this extra eipTimestamp() method. Isn't this the normal isActivatedEIP() call??

Also: such a check should likely be at the beginning of the method (?).

Copy link
Member Author

Choose a reason for hiding this comment

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

I have added this because I need to detect we are on the first fork block. The reason is that on the first fork block, we have to store all the MIN_HISTORY_SERVE_WINDOW hashes. I don't know if there is another way to detect if we are on the fork block? I think we need to fork timestamp and then the exact condition if (parentBlock.header.timestamp < forkTime) { (this only happens once and is on the fork block)

Copy link
Member

Choose a reason for hiding this comment

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

Ah, you are right, sorry, please dismiss! 🙂

Should then the whole method get a check at the beginning if 2935 is activated and throw otherwise?

Copy link
Member Author

Choose a reason for hiding this comment

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

I am comparing it with accumulateParentBeaconBlockRoot, this also does not have a check for common.isActivatedEIP(4788), but for consistency I can add (but then I will add it to both methods)

Copy link
Member

Choose a reason for hiding this comment

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

Yes, maybe do (for both).

if (parentBlock.header.timestamp < forkTime) {
let ancestor = parentBlock
const range = this.common.param('vm', 'minHistoryServeWindow')
for (let i = 0; i < Number(range) - 1; i++) {
Copy link
Member

Choose a reason for hiding this comment

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

I would cautiously think that this < usage together with - 1 might produce an "off by one" error?

Since the code from the EIP states for i in range(MIN_HISTORY_SERVE_WINDOW - 1)?

(maybe confirm with edge case tests?)

Copy link
Member Author

Choose a reason for hiding this comment

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

I think this is correct (but this is definitely off-by-one error prone! 😄 )

From EIP: for i in range(MIN_HISTORY_SERVE_WINDOW - 1): this is python code. This loops the for loop MIN_HISTORY_SERVE_WINDOW - 1 time. Note that MIN_HISTORY_SERVE_WINDOW is 256 so we loop this 255 times. In the current TS code we loop i < 256 - 1 = 255 times also. Note that for each loop, we store the ancestor block. Also note that previously we have already stored the parent block hash of the current block. So, in total we have stored 256 blocks and therefore the history contract has access to 256 slots.

Copy link
Member

Choose a reason for hiding this comment

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

👍

)
assert.ok(equalsBytes(storage, genesis.hash()))
})
it('should ensure blocks older than 256 blocks can be retrieved from the history contract', async () => {
Copy link
Member Author

Choose a reason for hiding this comment

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

@holgerd77 had to re-check, but this test should actually test for the off-by-one error.

Copy link
Member

@holgerd77 holgerd77 left a comment

Choose a reason for hiding this comment

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

LGTM

@jochem-brouwer jochem-brouwer merged commit 2fea5cb into master Mar 4, 2024
45 of 46 checks passed
@holgerd77 holgerd77 deleted the eip2935 branch March 4, 2024 13:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants