fix: avoid payload prep failures on gloas reorgs - #9723
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 6cebf799a3
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| await validateGossipProposerPreferences(chain, signed); | ||
|
|
||
| chain.proposerPreferencesPool.add(signed); | ||
| chain.proposerPreferencesPool.add(signed, {local: true}); |
There was a problem hiding this comment.
Defer local marking until gossip publish succeeds
When publishProposerPreferences() rejects after validation has inserted the entry, this line still marks the preference as local even though the API call fails. A retry then hits ALREADY_KNOWN and returns before publishing, while importBlock() can suppress the EL FCU via isKnownLocal(), so a transient publish error can leave builders without the preference but make the node act as if local Gloas prep succeeded. Mark it local only after publish succeeds, or roll back the pool entry on failure.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Good catch, this was valid. validateGossipProposerPreferences() inserts the entry before the API publishes it, so marking it local before the publish await made the retry path unsafe exactly as you described.
Fixed in ba014eeca4: the API now rolls back the just-validated pool entry if publishProposerPreferences() rejects, and only marks it local after publish succeeds. I added a pool regression test so rollback only removes the exact stored entry and clears the local marker.
Verification:
pnpm vitest run --project unit packages/beacon-node/test/unit/chain/opPools/proposerPreferencesPool.test.tspnpm --filter @lodestar/beacon-node check-typespnpm exec biome check packages/beacon-node/src/api/impl/validator/index.ts packages/beacon-node/src/chain/opPools/proposerPreferencesPool.ts packages/beacon-node/test/unit/chain/opPools/proposerPreferencesPool.test.tspnpm lint
|
@lodekeeper what is this? |
|
Sorry for the thin context @nflaig — short version: Problem (Gloas): when local proposer-boost-reorg logic intentionally selects a weak late block's parent as the FCU head, payload prep could still hand the EL a What it does:
History: this replaces #9721 (closed). That was a It's a subtle path, so happy to walk through any of it — and if you think this is still the wrong layer or approach, I'd rather hear that before polishing it further. |
Motivation
Replaces the closed #9721 canonical-head retry workaround. If Gloas proposer-head selection intentionally builds on a weak-block parent, payload preparation must keep the Engine FCU inputs coherent with that selected head and avoid canonicalizing the EL to a weak late child when the next proposer is local.
Description
getSafeExecutionBlockHashForHead()so payload prep sends a safe hash only when the confirmed block is ancestor/equal to the selected FCU head.importBlockFCU suppression, so a weak late block does not advance the EL canonical head before a local proposer-boost reorg duty.Testing
pnpm buildpnpm vitest run --project unit packages/fork-choice/test/unit/forkChoice/safeBlocks.test.ts packages/beacon-node/test/unit/api/impl/validator/produceBlockV4.test.ts packages/beacon-node/test/unit/chain/opPools/proposerPreferencesPool.test.tspnpm --filter @lodestar/fork-choice check-typespnpm --filter @lodestar/beacon-node check-typespnpm lintAI assistance: implemented with Lodekeeper/Codex assistance.