Skip to content

Reject importing Gloas block until parent's payload is imported#9382

Merged
mergify[bot] merged 14 commits into
sigp:unstablefrom
dapplion:port-import-conditions
Jun 4, 2026
Merged

Reject importing Gloas block until parent's payload is imported#9382
mergify[bot] merged 14 commits into
sigp:unstablefrom
dapplion:port-import-conditions

Conversation

@dapplion
Copy link
Copy Markdown
Collaborator

@dapplion dapplion commented Jun 1, 2026

Gloas spec states:

def on_block(...):
    if is_parent_node_full(store, block):
        assert is_payload_verified(store, block.parent_root)

In other words: if the block is FULL child, the payload+data must have been imported before hand. We need to gate Gossip / REST API and sync import flows on that condition. To de-duplicate logic I added is_parent_imported_status in fork-choice.

  • So if the payload is missing, lookup sync will get a ParentUnknown, handle it and fetch the payload. This part is implemented in Gloas lookup sync #9155

Ports the consensus lessons from the gloas-lookup-sync work to unstable:
when deciding whether a block's parent is "imported", a post-Gloas FULL
child (whose bid commits to the parent's execution payload) must also wait
for the parent's payload to be imported, not just the parent block.

- proto_array: add `Block::is_child_full(child_bid)` to test whether a
  post-Gloas child commits to this block's execution payload.
- fork_choice: add `ParentImportedStatus` and
  `is_parent_imported`/`is_parent_imported_status`, which return
  `UnimportedPayload` for a FULL child whose parent payload has not been
  received yet.
- block_verification: use these in `verify_parent_block_is_known`,
  the `ExecutionPendingBlock` parent-invalid/parent-unknown gate, and
  `load_parent` (replacing the plain `get_block`/`contains_block` checks),
  and drop the now-obsolete gloas TODO comment.
@dapplion dapplion added gloas ready-for-review The code is ready for review labels Jun 1, 2026
/// A child block can only be imported after its parent has been fully imported. For a post-Gloas
/// FULL child (one whose bid commits to the parent's execution payload), "fully imported" also
/// requires the parent's payload to have been received by fork choice.
pub fn is_parent_imported_status(&self, block: &SignedBeaconBlock<E>) -> ParentImportedStatus {
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

This returns an enum as some consumers want the parent's ProtoBlock

Copy link
Copy Markdown
Member

@michaelsproul michaelsproul Jun 2, 2026

Choose a reason for hiding this comment

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

Maybe the name of this function should be get_parent_imported_status, or get_parent_import_status? If we go with the latter, the type could be ParentImportStatus. I think both are more grammatical than "is_parent_imported_status"

Remove redundant explanation about FULL child requirement.
Comment on lines +302 to +304
} else if let Some(execution_block_hash) = self.execution_status.block_hash() {
// Parent is before Gloas, and child is gloas
execution_block_hash == child_bid.message.parent_block_hash
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

This handles the Gloas fork block. We may need additional handling for Gloas genesis once Gloas lookup sync lands

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I deleted this function in 048b958. I think we still handle the fork boundary by treating all pre-Gloas parents as already imported.

@michaelsproul
Copy link
Copy Markdown
Member

How does this relate to:

Replacement? Complement?

@dapplion
Copy link
Copy Markdown
Collaborator Author

dapplion commented Jun 1, 2026

How does this relate to:

Replacement? Complement?

Complement, #9039 does not fix this issue

@mergify
Copy link
Copy Markdown

mergify Bot commented Jun 1, 2026

Some required checks have failed. Could you please take a look @dapplion? 🙏

@mergify mergify Bot added waiting-on-author The reviewer has suggested changes and awaits thier implementation. and removed ready-for-review The code is ready for review labels Jun 1, 2026
Production fix:
- fork_choice: `is_parent_imported_status` no longer gates a Gloas child on
  its parent's payload when the parent is pre-Gloas. A pre-Gloas parent's
  execution payload is embedded in the block and always present, so the first
  Gloas block at the fork-transition boundary must not be rejected with
  `ParentUnknown`.

Test harness fixes (all under FORK_NAME=gloas):
- block_verification: import each block's payload envelope into fork choice
  before importing its child (process_chain_segment imports blocks only); for
  the invalid-signature tests, pre-import ancestors and assert on the segment
  suffix to avoid re-processing finalization-pruned blocks.
- store_tests: mark the weak-subjectivity checkpoint anchor's payload as
  received so the first forward-sync block (a FULL child of the anchor) is
  not rejected.
Copy link
Copy Markdown
Member

@eserilev eserilev left a comment

Choose a reason for hiding this comment

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

halfway through

Comment thread consensus/proto_array/src/proto_array_fork_choice.rs Outdated
Comment thread consensus/fork_choice/src/fork_choice.rs Outdated
Comment thread consensus/fork_choice/src/fork_choice.rs Outdated
Comment thread consensus/fork_choice/src/fork_choice.rs Outdated
- Rename `ProtoBlock::is_child_full` -> `is_parent_full` (per review); the
  method is read from the parent's perspective.
- Reword `ParentImportedStatus` variant docs and the `is_parent_imported_status`
  doc comment to describe the condition in terms of the child's bid.

No behaviour change.
@dapplion dapplion added ready-for-review The code is ready for review and removed waiting-on-author The reviewer has suggested changes and awaits thier implementation. labels Jun 1, 2026
@@ -1869,12 +1865,18 @@ fn verify_parent_block_is_known<T: BeaconChainTypes>(
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Might be good to rename this function. Glancing at the gossip verification logic it wasn't immediately clear to me that we'd added the parent envelope check (it looked like we just deleted some stuff).

Maybe verify_parent_block_and_envelope_are_known? Or just verify_parent_is_known?

Comment thread beacon_node/beacon_chain/src/block_verification.rs
@michaelsproul
Copy link
Copy Markdown
Member

michaelsproul commented Jun 2, 2026

Pushed what I feel is a simplification and clarification in this commit:

It removes the TODO about how to handle pre-Gloas blocks in is_parent_full because that codepath is not relevant in is_parent_imported_status anyway. Any pre-Gloas parent is considered imported, regardless of its execution block hash.

Comment on lines +1383 to +1389
// Reject any block where the parent has an invalid payload. It's impossible for a valid
// block to descend from an invalid parent.
if parent.execution_status.is_invalid() {
return Err(BlockError::ParentExecutionPayloadInvalid {
parent_root: block.parent_root(),
});
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This is kind of a separate issue to this PR, but maybe this check should be part of is_parent_imported_status so that we're forced to handle it at all places we check the parent.

Then again, it is only relevant pre-Gloas, so it's going away soon.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Then should is_parent_imported return false if the parent is invalid?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Then should is_parent_imported return false if the parent is invalid?

Maybe an error?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Would prefer to defer this change to another PR... Doing this now will definitely complicate sync

Comment on lines +3152 to +3160
// Mark the checkpoint block's payload as received in fork choice. The anchor block is a
// trusted, finalized block whose payload we already have, but it is loaded via
// `from_anchor` and so is never marked payload-received. Without this, the first forward
// block (a FULL child of the anchor) would be rejected with `ParentUnknown`.
beacon_chain
.canonical_head
.fork_choice_write_lock()
.on_valid_payload_envelope_received(wss_block_root)
.unwrap();
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

If we need this in the tests, is it possible we need it on the prod codepath for checkpoint sync as well?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

No, this WSS tests re-implement range sync manually. In production we start without the payload and then either lookup or range sync with populate the payload.

Comment on lines +245 to +246
/// imported into fork choice. `process_chain_segment` imports blocks only (in production, payload
/// envelopes are fetched and imported separately), so a multi-block segment of consecutive FULL
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Is this true? Are we planning to keep process_chain_segment only importing blocks rather than blocks and envelopes?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

no this isn't true

Copy link
Copy Markdown
Member

@michaelsproul michaelsproul left a comment

Choose a reason for hiding this comment

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

Few changes I'm not sure about

@michaelsproul michaelsproul added waiting-on-author The reviewer has suggested changes and awaits thier implementation. and removed ready-for-review The code is ready for review labels Jun 4, 2026
dapplion added 4 commits June 4, 2026 12:13
- Rename `verify_parent_block_is_known` -> `verify_parent_block_and_envelope_are_known`
  so the added parent-envelope check is visible from the call site (michaelsproul).
- Rename `is_parent_imported_status` -> `get_parent_import_status` and the
  `ParentImportedStatus` enum -> `ParentImportStatus` (michaelsproul).
- Drop the contested "in production payload envelopes are fetched separately"
  claim from the test helper doc; describe current behaviour only (eserilev).
Range sync does not import payload envelopes yet (coming in sigp#9362), so a
multi-block segment of consecutive post-Gloas FULL blocks cannot be imported
by `process_chain_segment`. Skip the affected `block_verification` tests under
`FORK_NAME=gloas` until then, and drop the `import_chain_segment_with_envelopes`
placeholder helper and the per-test workarounds it required.
Reduce the weak-subjectivity forward-sync loop to the minimum needed to import
post-Gloas blocks: persist the payload envelope and mark it received in fork
choice. The data-column reconstruction was unnecessary (the WSS test chain has
no blobs). Placeholder until range sync imports payload envelopes (sigp#9362).
Comment thread beacon_node/beacon_chain/src/block_verification.rs Outdated
dapplion added 3 commits June 4, 2026 14:58
- Remove the parent-not-known explanation in the gate match (duplicated in
  load_parent) and the invalid-parent comment (error name is self-documenting).
- Remove the stale spec-paraphrase in verify_parent_block_and_envelope_are_known.
- Shorten the get_parent_import_status / verify_parent docs and the WSS anchor
  and TODO(gloas) comments.
@mergify
Copy link
Copy Markdown

mergify Bot commented Jun 4, 2026

Some required checks have failed. Could you please take a look @dapplion? 🙏

@mergify mergify Bot added waiting-on-author The reviewer has suggested changes and awaits thier implementation. and removed ready-for-review The code is ready for review labels Jun 4, 2026
Copy link
Copy Markdown
Member

@eserilev eserilev left a comment

Choose a reason for hiding this comment

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

lgtm

@eserilev eserilev added ready-for-merge This PR is ready to merge. and removed waiting-on-author The reviewer has suggested changes and awaits thier implementation. labels Jun 4, 2026
@mergify mergify Bot added the queued label Jun 4, 2026
@mergify
Copy link
Copy Markdown

mergify Bot commented Jun 4, 2026

Merge Queue Status

This pull request spent 29 minutes 22 seconds in the queue, including 27 minutes 40 seconds running CI.

Required conditions to merge

Reason

Pull request #9382 has been dequeued

The pull request could not be merged. This could be related to an activated branch protection or ruleset rule that prevents us from merging. 1 review requesting changes and 1 approving review by reviewers with write access.

Hint

You should look at the reason for the failure and decide if the pull request needs to be fixed or if you want to requeue it.
If you do update this pull request, it will automatically be requeued once the queue conditions match again.
If you think this was a flaky issue instead, you can requeue the pull request, without updating it, by posting a @mergifyio queue comment.

mergify Bot added a commit that referenced this pull request Jun 4, 2026
@mergify mergify Bot added dequeued and removed queued labels Jun 4, 2026
@dapplion dapplion dismissed michaelsproul’s stale review June 4, 2026 15:21

Review addressed

@dapplion
Copy link
Copy Markdown
Collaborator Author

dapplion commented Jun 4, 2026

@Mergifyio queue

@mergify
Copy link
Copy Markdown

mergify Bot commented Jun 4, 2026

Merge Queue Status

This pull request spent 30 minutes 44 seconds in the queue, including 28 minutes 52 seconds running CI.

Required conditions to merge

@mergify mergify Bot added queued and removed dequeued labels Jun 4, 2026
mergify Bot added a commit that referenced this pull request Jun 4, 2026
@mergify mergify Bot merged commit d98de9f into sigp:unstable Jun 4, 2026
38 checks passed
@mergify mergify Bot removed the queued label Jun 4, 2026
dapplion added a commit to dapplion/lighthouse that referenced this pull request Jun 4, 2026
Adopt sigp#9382's canonical ParentImportStatus / get_parent_import_status (drops the
duplicate is_parent_imported_status from this branch), keeping ParentUnknown's
parent_block_hash field which the lookup-sync peer donation depends on.
dapplion added a commit to pawanjay176/lighthouse that referenced this pull request Jun 4, 2026
Range sync now imports payload envelopes, so the chain_segment and
invalid_signature tests no longer need to early-return under Gloas.
Drop the now-unused fork_name_from_env import.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

gloas ready-for-merge This PR is ready to merge.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants