cl: reuse one converter between consensus and execution withdrawals - #23271
Conversation
There was a problem hiding this comment.
Pull request overview
This PR refactors Caplin’s consensus↔execution withdrawal conversion to reuse a single shared converter, avoiding duplicated hand-written mapping at call sites while keeping behavior unchanged (including nil vs empty-slice semantics where noted).
Changes:
- Export
cltypes.ConvertConsensusWithdrawalsToExecutionWithdrawalsas the shared conversion helper. - Update forkchoice’s payload-attributes emission to use the shared converter.
- Update
cacheExecutionBodyto use the shared converter while preservingnilwithdrawals for empty lists.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
cl/phase1/stages/forkchoice.go |
Switch payload-attributes withdrawals construction to the shared converter. |
cl/cltypes/withdrawal.go |
Export plural consensus→execution withdrawals converter. |
cl/beacon/handler/block_production.go |
Reuse shared converter when caching execution payload withdrawals (preserving nil behavior for empty lists). |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
48577e9 to
b0516f5
Compare
b0516f5 to
362e2e3
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.
Suppressed comments (1)
cl/beacon/handler/block_production.go:2600
- This introduces an extra allocation + extra pass by first materializing
consensusWithdrawalsand then allocatingretinsideConvertConsensusWithdrawalsToExecutionWithdrawals. Sincepayload.Withdrawalsis already an iterable list, consider adding an exported converter that works directly from the SSZ list (or exporting the singular converter) sowscan be filled in one pass without the intermediate slice, while still keeping a single conversion implementation.
if payload.Withdrawals != nil && payload.Withdrawals.Len() > 0 {
consensusWithdrawals := make([]*cltypes.Withdrawal, payload.Withdrawals.Len())
payload.Withdrawals.Range(func(idx int, w *cltypes.Withdrawal, _ int) bool {
consensusWithdrawals[idx] = w
return true
})
ws = cltypes.ConvertConsensusWithdrawalsToExecutionWithdrawals(consensusWithdrawals)
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.
Suppressed comments (1)
cl/cltypes/withdrawal.go:91
- This claims the helper is the single crossing point, but
eth1_block.gostill builds execution-withdrawal lists through the singular converter. Since that invariant is already false, describe the function's behavior instead.
// ConvertConsensusWithdrawalsToExecutionWithdrawals is the single crossing point between the two
// representations, so callers building the same list cannot drift apart.
|
No blocking correctness findings. The conversion preserves field values, order, pointer independence, and both call sites’ nil/empty behavior. One low-severity issue: the new doc comment claims this helper is the “single crossing point,” but three manual conversions remain, plus internal singular conversions in Reviewed head |
Split out of #23105 so the mechanical cleanup can be reviewed on its own. The conversion between cltypes.Withdrawal and types.Withdrawal was hand-written at each call site. Export the existing converter and use it, so the lists cannot drift apart. No behaviour change: both call sites produce the same slice, including its nil-ness.
Pre-size the intermediate slice and assign by index, since Range already supplies it.
The doc comment claimed to be the single crossing point between the two representations, which is not true while eth1_block.go still converts through the singular helper. Describe the behaviour instead, and pin it: order, no shared pointers, and a result that is never nil because the execution layer rejects a nil list and an empty one under opposite conditions.
362e2e3 to
9577312
Compare
|
You are right, and the count is worse than the comment suggested: three hand-written conversions remain in Reworded to describe the behaviour instead: // ConvertConsensusWithdrawalsToExecutionWithdrawals converts a withdrawal list to its execution
// representation, in order and with no shared pointers. The result is never nil, which matters
// because the execution layer rejects a nil list and an empty one under opposite conditions.Added Head is now |
Split out of #23105, which grew too large to review in one piece. This is the mechanical, behaviour-neutral part.
The conversion between
cltypes.Withdrawalandtypes.Withdrawalwas hand-written at each call site.cl/cltypes/withdrawal.goalready had a private singular converter, so this exports a plural one and uses it in the two places that built the list by hand: the payload-attributes emitter in the forkchoice stage, andcacheExecutionBody.No behaviour change. Both sites produce the same slice as before, including whether it comes back nil —
cacheExecutionBodykeeps returning nil for an empty withdrawals list rather than an empty slice.Part of a series splitting #23105 into reviewable units. The remaining parts follow separately; this one stands alone and depends on nothing else in the series.