feat: chunks convergence - #5557
Merged
sbackend123 merged 18 commits intoAug 13, 2026
Merged
Conversation
Two valid single owner chunks can share an address, batch and stamp while wrapping different content. The stamp signs the chunk address, so both carry an identical stamp and produce an identical stamp hash. The content-blind existence check at the top of reserve.Put therefore treated the second chunk as already stored and dropped it, leaving each node holding whichever chunk reached it first. Neighborhoods never converged, and the reserve sampler computed different commitments from the same address. Make the check content-aware via the pullsync sum, and settle the divergence here rather than in the protocol: the chunk wrapping the lower CAC address wins. The rule depends only on the two payloads, so every node reaches the same answer regardless of arrival order. On a win the chunk is replaced in place, reusing the stamp index and stamp entries, which are identical for both. The bin ID is bumped so peers that already synced past the old one are offered the replacement. The reserve size is unchanged: one chunk goes in, one comes out. Pullsync treats a lost tie-break as an expected outcome rather than a sync error, since the node already holds the chunk the neighborhood converges on. Divergent chunks under different batches are not covered: they occupy different stamp indices, so no tie-break fires.
Two valid single owner chunks can share an address, batch and stamp while wrapping different content. The stamp signs the chunk address, so both carry an identical stamp and produce an identical stamp hash. The content-blind existence check at the top of reserve.Put therefore treated the second chunk as already stored and dropped it, leaving each node holding whichever chunk reached it first. Neighborhoods never converged, and the reserve sampler computed different commitments from the same address. Make the check content-aware via the pullsync sum, and settle the divergence here rather than in the protocol: the chunk wrapping the lower CAC address wins. The rule depends only on the two payloads, so every node reaches the same answer regardless of arrival order. On a win the chunk is replaced in place, reusing the stamp index and stamp entries, which are identical for both. The bin ID is bumped so peers that already synced past the old one are offered the replacement. The reserve size is unchanged: one chunk goes in, one comes out. Pullsync treats a lost tie-break as an expected outcome rather than a sync error, since the node already holds the chunk the neighborhood converges on. Divergent chunks under different batches are not covered: they occupy different stamp indices, so no tie-break fires.
…nto feat/pullsync-soc-convergence # Conflicts: # pkg/storage/storage_test.go # pkg/storer/internal/reserve/reserve.go # pkg/storer/internal/reserve/reserve_test.go
…SWIP-101) Different nodes receive the same chunks in different orders, so for any set of conflicting chunks every arrival order must leave the reserve in the same final state; an order-dependent outcome means neighborhoods that can never agree. The harness drives conflict sets through every permutation against a fresh reserve and compares canonical state fingerprints (bin IDs excluded: they are order-dependent by design), asserting the sum index invariants on every run as a side effect. Converging on this branch: the equal-timestamp CAC tie-break, the identical-stamp divergent SOC resolution and timestamp ordering. Five constellations are order-dependent, all falling through the same gap: at equal stamp timestamps the tie-break fires only for a content addressed incoming chunk with a different address, and every other case drops into an unconditional replace. These are marked unresolved in the case table: they log the divergence without failing the suite, and setting RESERVE_STRICT_CONVERGENCE=1 turns them into failures, which gives the reserve.Put refactor a concrete target. A case marked unresolved that starts converging fails loudly so the marker is removed and the table stays honest. Testing methodology and findings are documented alongside the refactor notes for reuse.
…nto feat/pullsync-soc-convergence
Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: Akrem Chabchoub <121046693+akrem-chabchoub@users.noreply.github.com> Co-authored-by: Not Darko <93942788+darkobas2@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> Co-authored-by: Ljubiša Gačević <35105035+gacevicljubisa@users.noreply.github.com> Co-authored-by: Janoš Guljaš <janos@users.noreply.github.com> Co-authored-by: acud <12988138+acud@users.noreply.github.com> Co-authored-by: sbackend <sofia.sakharova@ethswarm.org> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…nto feat/pullsync-soc-convergence # Conflicts: # pkg/storer/internal/reserve/reserve.go
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.
Checklist
Description
This PR makes neighborhoods converge on one chunk when several valid conflicting CAC/SOC variants race through push/pull sync. Nodes no longer keep “whatever arrived first”: the reserve applies conflict rules, rejects "losers", and re-announces winners so peers that already synced past the old entry still see the resolution.
Conflicts:
Same batch + stamp index, newer timestamp -> Newer wins (any chunk type)
Same batch + stamp index + timestamp, different addresses -> Lexicographically lower address wins
Same SOC address + identical stamp, different wrapped payloads -> Lexicographically lower wrapped CAC wins
Additionally PR implements refactoring on Put method. Motivation: Put method had grown into a single, multi-screen function that mixed every write / overwrite / conflict path in one place, including behavior that branched on chunk type (CAC vs SOC). That made the control flow hard to reason about and easy to get wrong when adding a new case.
Open API Spec Version Changes (if applicable)
Motivation and Context (Optional)
Related Issue (Optional)
Screenshots (if appropriate):
AI Disclosure