Skip to content

fix(validation): cascade-invalidate + ReplayCache mergeable-entry safety#508

Draft
spreston8 wants to merge 2 commits into
rust/stagingfrom
feat/lfs-validation
Draft

fix(validation): cascade-invalidate + ReplayCache mergeable-entry safety#508
spreston8 wants to merge 2 commits into
rust/stagingfrom
feat/lfs-validation

Conversation

@spreston8

@spreston8 spreston8 commented May 9, 2026

Copy link
Copy Markdown
Collaborator

Summary

Two block-validation correctness fixes surfaced during the bonding-bug investigation. Both close concrete failure modes that produced spurious InvalidTransaction cascades on joiners under load.

Stacked on top of #507 (feat(lfs): joiner-side coverage + orchestrator multi-root pagination fix for multi-bonded shards). This PR's diff shows ONLY the 2 commits below; auto-rebases onto rust/staging once #507 merges.

Commits (2)

776fa361 fix(runtime-manager): ReplayCache mergeable-entry safety check
ad1627ac fix(block-processor): cascade-invalidate children of invalid parents

ad1627ac — Cascade-invalidate children of invalid parents

Problem. When a block X is recorded invalid, its children sit in the buffer waiting on X. Each child eventually gets pulled out for validation, individually fails because its parent is missing a mergeable-channels entry (since invalid parents never wrote one — save_mergeable_channels only runs on the validation success path), and gets recorded as InvalidTransaction one-at-a-time. Slashable cascade for what was originally one parent's failure. Honest validators producing children of a block they didn't know was invalid in their view get punished.

Fix.

  • New InvalidBlock::InvalidParent variant — non-slashable (the child's creator may not have known the parent was invalid in their view)
  • New Readiness enum returned by get_non_validated_dependencies: Ready / Waiting{deps_to_fetch, deps_in_buffer} / InvalidParents(parents)
  • Cascade fires only on invalid PARENTS — invalid justifications stay satisfied (justifications acknowledge a validator's latest message and don't require a mergeable entry)
  • effects_for_invalid_block refactored to take &KeyValueDagRepresentation directly so the cascade path doesn't need to build a full snapshot
  • BlockException arm in validate_with_effects escalated warn → error as a tripwire (should be unreachable post-fix)
  • handle_invalid_block routes InvalidParent through handle_invalid_block_effect so descendants also cascade

New test: check_dependencies_must_cascade_invalidate_when_parent_is_invalid.

776fa361 — ReplayCache mergeable-entry safety check

Problem. ReplayCache is a per-node optimization — on a cache hit, return the cached post-state without re-running replay. But if the mergeable-channels entry for (post_state, sender, seq_num) isn't in the local store, returning the cached post-state lets the block enter the DAG with no mergeable entry. Subsequent children that need the entry hit "Missing mergeable entry" during merge — the exact failure mode the existing StateHashCache check (lines 624-643) was designed to prevent.

Fix. On ReplayCache hit, look up the mergeable key first. If present → fast-return as before. If absent → fall through to full replay so save_mergeable_channels runs. Mirrors the existing StateHashCache safety check pattern.

Test plan

Co-Authored-By: Claude noreply@anthropic.com

@spreston8
spreston8 force-pushed the feat/lfs-validation branch from 34b4773 to 776fa36 Compare May 13, 2026 02:41
Base automatically changed from feat/lfs-coverage to rust/staging May 13, 2026 17:32
spreston8 and others added 2 commits May 13, 2026 10:38
Closes the parent-child mergeable-entry race that produced spurious
InvalidTransaction (slashable) cascades on joiners. A parent recorded
invalid in invalid_blocks_set has no mergeable-channels entry written
(save_mergeable_channels only runs on the validation success path), so
allowing the child through the dependency gate would deterministically
fail merge with "Missing mergeable entry" and the existing
BlockException -> InvalidTransaction arm would mis-record the child.

- New InvalidBlock::InvalidParent variant (non-slashable; the child's
  creator may not have known the parent was invalid in their view)
- New Readiness enum returned by get_non_validated_dependencies:
  Ready / Waiting{deps_to_fetch, deps_in_buffer} / InvalidParents(parents)
- Cascade fires only on invalid PARENTS — invalid justifications stay
  satisfied (justifications acknowledge a validator's latest message and
  don't require a mergeable entry)
- effects_for_invalid_block refactored to take &KeyValueDagRepresentation
  directly so the cascade path doesn't need to build a full snapshot
- BlockException arm in validate_with_effects escalated warn -> error as
  a tripwire — should be unreachable post-fix
- handle_invalid_block routes InvalidParent through handle_invalid_block_effect
  so descendants also cascade

Test: check_dependencies_must_cascade_invalidate_when_parent_is_invalid.

Co-Authored-By: Claude <noreply@anthropic.com>
Mirror the existing StateHashCache safety check on the ReplayCache
shortcut path. Previously a ReplayCache hit returned the cached post-state
without verifying the mergeable-channels entry for (post_state, sender,
seq_num) existed in the store — letting a block enter the DAG with no
mergeable entry. Subsequent children that needed the entry would hit
"Missing mergeable entry" during merge, the exact failure mode the
StateHashCache check (lines 624-643) was already designed to prevent.

Now: on ReplayCache hit, look up the mergeable key first. If present,
fast-return as before. If absent, fall through to full replay so
save_mergeable_channels runs.

Co-Authored-By: Claude <noreply@anthropic.com>
@spreston8
spreston8 force-pushed the feat/lfs-validation branch from 776fa36 to b3c5b49 Compare May 13, 2026 17:40
@spreston8
spreston8 marked this pull request as draft May 17, 2026 00:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant