Skip to content

[consensus] resolve ancestors against digests seen on the wire - #27741

Draft
arun-koshy wants to merge 1 commit into
ak/minimal-block-lag-gatefrom
ak/minimal-block-seen-digests
Draft

[consensus] resolve ancestors against digests seen on the wire#27741
arun-koshy wants to merge 1 commit into
ak/minimal-block-lag-gatefrom
ak/minimal-block-seen-digests

Conversation

@arun-koshy

@arun-koshy arun-koshy commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

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_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. That is enforced by the type: encode passes None.

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 DigestMismatch rather 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.

  • Protocol:
  • Nodes (Validators and Full nodes):
  • gRPC:
  • JSON-RPC:
  • GraphQL:
  • CLI:
  • Rust SDK:
  • Indexing Framework:

@arun-koshy
arun-koshy deployed to sui-typescript-aws-kms-test-env August 18, 2026 08:11 — with GitHub Actions Active
@vercel

vercel Bot commented Aug 18, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
sui-docs Ready Ready Preview Aug 20, 2026 4:20am
2 Skipped Deployments
Project Deployment Actions Updated (UTC)
multisig-toolkit Ignored Ignored Preview Aug 20, 2026 4:20am
sui-kiosk Ignored Ignored Preview Aug 20, 2026 4:20am

Request Review

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.
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