Conversation
8da3e00 to
bcfaff8
Compare
api/server/structs/state.go
Outdated
| InactivityScores []string `json:"inactivity_scores"` | ||
| CurrentSyncCommittee *SyncCommittee `json:"current_sync_committee"` | ||
| NextSyncCommittee *SyncCommittee `json:"next_sync_committee"` | ||
| LatestExecutionPayloadHeader *ExecutionPayloadHeaderElectra `json:"latest_execution_payload_header"` |
There was a problem hiding this comment.
| LatestExecutionPayloadHeader *ExecutionPayloadHeaderElectra `json:"latest_execution_payload_header"` | |
| LatestExecutionPayloadHeader *ExecutionPayloadHeaderFulu `json:"latest_execution_payload_header"` |
| @@ -852,30 +862,35 @@ func encodeBlock(blk interfaces.ReadOnlySignedBeaconBlock) ([]byte, error) { | |||
|
|
|||
| func keyForBlock(blk interfaces.ReadOnlySignedBeaconBlock) ([]byte, error) { | |||
There was a problem hiding this comment.
The reason why we have it backwards is because during normal operations the block/state will always be in the latest fork. Although a micro-optimization, it does make sense to do it this way. I think we should write such code for every if or switch
| }, | ||
| bytesutil.ToBytes4(params.BeaconConfig().FuluForkVersion): func() (interfaces.ReadOnlySignedBeaconBlock, error) { | ||
| return blocks.NewSignedBeaconBlock( | ||
| ðpb.SignedBeaconBlockFulu{Block: ðpb.BeaconBlockFulu{Body: ðpb.BeaconBlockBodyFulu{ExecutionPayload: &enginev1.ExecutionPayloadDeneb{}}}}, |
There was a problem hiding this comment.
You should probably create ExecutionPayloadFulu and ExecutionPayloadHeaderFulu somewhere in the /proto/engine/v1 directory, just as Electra types are created in electra.go. And use it here and everywhere else.
There was a problem hiding this comment.
Actually my target was to remove ExecutionPayloadElectra and ExecutionPayloadHeaderElectra as well and to do the exact contrary.
I thought I did it, I probably messed up something writing this PR.
Rationale: In all parts of the code, when a struct does not change between fork <n> and fork <n+1>, we just use the struct of fork <n> without aliasing it to the new fork.
Aka, we don't write things such as:
ExecutionPayloadElectra = ExecutionPayloadDenebSo, my idea here is to remove those 2 type aliases, and use the Deneb version both in Electra and Fulu.
There was a problem hiding this comment.
Right, looks like that's what we should do
| v1alpha1Server.EXPECT().ProposeBeaconBlock(gomock.Any(), mock.MatchedBy(func(req *eth.GenericSignedBeaconBlock) bool { | ||
| block, ok := req.Block.(*eth.GenericSignedBeaconBlock_Electra) | ||
| converted, err := structs.SignedBeaconBlockContentsElectraFromConsensus(block.Electra) | ||
| // Convert back Fulu to Electra when there is at least one difference between Electra and Fulu blocks. |
There was a problem hiding this comment.
I don't understand this. Why can't we have a simple Electra test?
There was a problem hiding this comment.
Because of the publishBlock function, which is called in the PublishBlock function, which itself is called in the test.
The way the publishBlock function works is it goes, in reverse, fork after fork to determine if a block if of type Fulu, Electra, Deneb etc...
To decide if a block is an Fulu block, it only looks if the block can be used as an Fulu block (by using unmarshalStrict.)
Currently, there is no difference between a Fulu block and an Electra block. So, when trying to unmarshal an Electra block, it will actually be considered as a Fulu block.
Note that this is absolutely not an issue for the publishBlock function itself: An Electra block is correctly published, and a Fulu block is correctly published as well.
Because this failed test will stop to fail once there is at least one difference between an Electra block and a Fulu block, then I think it's simpler to, at the moment, patch the test. This patch will simply need to be removed once there is at least one difference between and Electra block and a Fulu block.
| require.ErrorContains(t, "block cannot be nil", err) | ||
| }) | ||
|
|
||
| // Test for Fulu version |
There was a problem hiding this comment.
Nitpick: These comments don't have any value since the test case is already named after the fork
There was a problem hiding this comment.
Hum indeed I over-commented it.
My idea was to have one comment by Fork (and not by test), as fixed in 83b895f.
Why?
Because if a new test appears (or a new fork), I would ensure the next developer to follow this logic, and to group tests by fork, and not by test type (block, blinded block...)
| return b, nil | ||
| } | ||
|
|
||
| // InitializeFromProtoUnsafeElectra directly uses the beacon state protobuf fields |
There was a problem hiding this comment.
| // InitializeFromProtoUnsafeElectra directly uses the beacon state protobuf fields | |
| // InitializeFromProtoUnsafeFulu directly uses the beacon state protobuf fields |
| b.sharedFieldReferences[types.PreviousEpochParticipationBits] = stateutil.NewRef(1) | ||
| b.sharedFieldReferences[types.CurrentEpochParticipationBits] = stateutil.NewRef(1) | ||
| b.sharedFieldReferences[types.LatestExecutionPayloadHeaderDeneb] = stateutil.NewRef(1) // New in Electra. | ||
| b.sharedFieldReferences[types.HistoricalSummaries] = stateutil.NewRef(1) // New in Capella. |
There was a problem hiding this comment.
I know it's not exactly the scope of this PR since the wrong comment had been there before, but HistoricalSummaries were added in Deneb, not in Electra.
There was a problem hiding this comment.
Hum, it seems it has been introduced in Capella (as stated by the comment).
https://github.com/ethereum/consensus-specs/blob/dev/specs/capella/beacon-chain.md#beaconstate
api/server/structs/block.go
Outdated
| type ( | ||
| ExecutionPayloadFulu = ExecutionPayloadDeneb | ||
| ExecutionPayloadHeaderFulu = ExecutionPayloadHeaderDeneb | ||
| ) |
There was a problem hiding this comment.
Given the discussion we had, looks like we can remove this (and the Electra equivalent, if there is one)
| }, | ||
| bytesutil.ToBytes4(params.BeaconConfig().FuluForkVersion): func() (interfaces.ReadOnlySignedBeaconBlock, error) { | ||
| return blocks.NewSignedBeaconBlock( | ||
| ðpb.SignedBeaconBlockFulu{Block: ðpb.BeaconBlockFulu{Body: ðpb.BeaconBlockBodyFulu{ExecutionPayload: &enginev1.ExecutionPayloadDeneb{}}}}, |
There was a problem hiding this comment.
Right, looks like that's what we should do
f40b4d9 to
b43d1a6
Compare
b43d1a6 to
2d08eca
Compare
Rationale: This log is the only one notifying the user a new fork happened. A new fork is always a little bit stressful for a node operator. Having at least one log indicating the client switched fork is something useful.
Co-authored-by: Radosław Kapka <rkapka@wp.pl>
Co-authored-by: Radosław Kapka <rkapka@wp.pl>
Co-authored-by: Radosław Kapka <rkapka@wp.pl>
2d08eca to
8a699c0
Compare
What type of PR is this?
Other
What does this PR do? Why is it needed?
This PR implements the Fulu fork Boilerplate.
It does not implement any Fulu feature.
Within this PR, the Fulu fork behaves exactly as the Electra Fork.
Other notes for review
For an complete, optimal, and enjoyable experience, please read commit by commit.
The current state of the Fulu specification does not modify neither the beacon state nor the beacon block.
However, this pull request does add a new Fulu beacon state and beacon block definition, which are identical to Electra beacon state and block.
There is no benefit to copy the beacon block and state declaration if there is no change in the Fulu update.
However, I'm pretty confident, knowing the Ethereum history, that before the Fulu fork effectively happens, at least one EIP will be introduced needing at least one beacon state/block modified/added field.
I won't launch a polymarket about that, but all the previous forks both modified the Beacon block and the Beacon state ...
So I chose to do the heavy lifting work now so nobody has to do it later.
If, finally no change occurs the Beacon Block or State, we will just have to delete the proto files, re-run code generation, and quickly fix the newly xxx undefined errors in the codebase.