[consensus] resolve ancestors against digests seen on the wire - #27741
Draft
arun-koshy wants to merge 1 commit into
Draft
[consensus] resolve ancestors against digests seen on the wire#27741arun-koshy wants to merge 1 commit into
arun-koshy wants to merge 1 commit into
Conversation
arun-koshy
deployed
to
sui-typescript-aws-kms-test-env
August 18, 2026 08:11 — with
GitHub Actions
Active
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
arun-koshy
force-pushed
the
ak/minimal-block-lag-gate
branch
from
August 20, 2026 04:13
f761ac5 to
4bb8425
Compare
Records the digest a peer claims for its own block even when that block cannot be rebuilt, and resolves ancestors against those claims where the accepted DAG has nothing. Rebuilding needs an ancestor's 32-byte digest, not the ancestor block, so a digest can be recorded the moment a block is seen rather than when it is accepted. That window is where blocks were being lost. A block that cannot be rebuilt is dropped, so the slot it claims stays unknown, and every later block from any author that references that slot fails too. The own-parent digest breaks this cascade along a single author's chain; nothing covered it across authors. Digests recorded here are hints, not facts, and nothing trusts them. A wrong one produces bytes that do not hash to the sender's claimed block digest, so it costs a fetch rather than admitting a bad block -- which is what makes it safe to act on a peer's word at all. Two different digests at one slot mark it ambiguous rather than letting either win: first-wins would let an equivocating author choose the rebuild bytes by controlling arrival order. Only a block's own reference is recorded, and only after parse_slim has bound its author to the authenticated peer. Recording the ancestors a block claims would let its author write into slots it does not own, turning ambiguity into a denial of service against other authorities. As it stands an author can only poison its own slots, which denies only itself. The map is read only when decoding. An encoder must decide what to omit from what it has accepted, since a hint it happens to hold says nothing about what a receiver knows. Bounds are GC below the collected round, plus a cap on size. Past the cap new slots are refused rather than evicting live ones: refusing costs compression, evicting would change the answer for a slot something is already resolving against. The GC floor is held beside the map so a reference arriving late for a swept round is rejected instead of lingering below it.
arun-koshy
force-pushed
the
ak/minimal-block-seen-digests
branch
from
August 20, 2026 04:17
a3f896e to
d58598c
Compare
arun-koshy
deployed
to
sui-typescript-aws-kms-test-env
August 20, 2026 04:17 — with
GitHub Actions
Active
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.
Description
Records the digest a peer claims for its own block even when that block cannot be rebuilt, and resolves ancestors against those claims where the accepted DAG has nothing.
Rebuilding needs an ancestor's 32-byte digest, not the ancestor block, so a digest can be recorded the moment a block is seen rather than when it is accepted. That window is where blocks were being lost: a block that cannot be rebuilt is dropped, so the slot it claims stays unknown, and every later block from any author referencing that slot fails too. The own-parent digest in #27702 breaks this cascade along a single author's chain — nothing covered it across authors.
Digests here are hints, not facts, and nothing trusts them. A wrong one produces bytes that do not hash to the sender's claimed block digest, so it costs a fetch rather than admitting a bad block. That is what makes it safe to act on a peer's word at all.
Two different digests at one slot mark it ambiguous rather than letting either win: first-wins would let an equivocating author choose the rebuild bytes by controlling arrival order.
Only a block's own reference is recorded, and only after
parse_slimhas bound its author to the authenticated peer. Recording the ancestors a block claims would let its author write into slots it does not own, turning ambiguity into a denial of service against other authorities. As it stands an author can only poison its own slots, which denies only itself.The map is read only when decoding — an encoder must decide what to omit from what it has accepted, since a hint it happens to hold says nothing about what a receiver knows. That is enforced by the type:
encodepassesNone.Bounds are GC below the collected round plus a cap on size. Past the cap new slots are refused rather than evicting live ones: refusing costs compression, evicting would change the answer for a slot something is already resolving against. The GC floor is held beside the map so a reference arriving late for a swept round is rejected instead of lingering below it.
Stacked on #27740 — review that first; this targets its branch.
Test plan
Six unit tests on the map: an unseen slot is
Absent; a seen slot resolves; conflicting digests are ambiguous and stay ambiguous when either is repeated; a repeat of the same digest is not a conflict; GC sweeps its window and the floor rejects late arrivals below it; and at capacity new slots are refused while existing ones still resolve.Two through the codec: an ancestor the receiver has never accepted rebuilds byte-identically because a peer claimed it on the wire — asserted to fail first without the map, so the test proves the map is what resolved it — and a wrong claim at one slot loses to the author's digest and comes back as
DigestMismatchrather than being accepted.Release notes
Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required.
For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates.