fix(validation): cascade-invalidate + ReplayCache mergeable-entry safety#508
Draft
spreston8 wants to merge 2 commits into
Draft
fix(validation): cascade-invalidate + ReplayCache mergeable-entry safety#508spreston8 wants to merge 2 commits into
spreston8 wants to merge 2 commits into
Conversation
This was referenced May 11, 2026
spreston8
force-pushed
the
feat/lfs-validation
branch
from
May 13, 2026 02:41
34b4773 to
776fa36
Compare
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
force-pushed
the
feat/lfs-validation
branch
from
May 13, 2026 17:40
776fa36 to
b3c5b49
Compare
spreston8
marked this pull request as draft
May 17, 2026 00:28
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Two block-validation correctness fixes surfaced during the bonding-bug investigation. Both close concrete failure modes that produced spurious
InvalidTransactioncascades 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/stagingonce #507 merges.Commits (2)
ad1627ac— Cascade-invalidate children of invalid parentsProblem. 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_channelsonly runs on the validation success path), and gets recorded asInvalidTransactionone-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.
InvalidBlock::InvalidParentvariant — non-slashable (the child's creator may not have known the parent was invalid in their view)Readinessenum returned byget_non_validated_dependencies:Ready / Waiting{deps_to_fetch, deps_in_buffer} / InvalidParents(parents)effects_for_invalid_blockrefactored to take&KeyValueDagRepresentationdirectly so the cascade path doesn't need to build a full snapshotBlockExceptionarm invalidate_with_effectsescalated warn → error as a tripwire (should be unreachable post-fix)handle_invalid_blockroutesInvalidParentthroughhandle_invalid_block_effectso descendants also cascadeNew test:
check_dependencies_must_cascade_invalidate_when_parent_is_invalid.776fa361— ReplayCache mergeable-entry safety checkProblem. 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_channelsruns. Mirrors the existing StateHashCache safety check pattern.Test plan
cargo test --release --workspace— 2183 passed, 0 failed (across 70 test suites; +1 over PR feat(lfs): joiner-side coverage + orchestrator multi-root pagination fix for multi-bonded shards #507's 2182, accounting for the new cascade-invalidate test)cargo build --release -p casper— cleanCo-Authored-By: Claude noreply@anthropic.com