Skip to content

Commit 396aa6b

Browse files
committed
fix(storer/reserve): enforce deterministic equal-timestamp stamp-hash tie-break
1 parent 1fcfef4 commit 396aa6b

2 files changed

Lines changed: 20 additions & 9 deletions

File tree

pkg/storer/internal/reserve/convergence_test.go

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -364,8 +364,7 @@ func TestPutOrderConvergence(t *testing.T) {
364364
// Three-way conflict across batches: the batch B entry must end
365365
// serving whatever payload the batch A conflict settles on, with
366366
// its sum refreshed accordingly.
367-
name: "divergent socs with a sibling entry under another batch",
368-
unresolved: true,
367+
name: "divergent socs with a sibling entry under another batch",
369368
chunks: func(t *testing.T) []swarm.Chunk {
370369
t.Helper()
371370
stamp := postagetesting.MustNewFields(batchA.ID, 0, 7)
@@ -463,17 +462,17 @@ func TestSOCMultiStampDivergenceCornerCase(t *testing.T) {
463462
t.Fatal("expected payload P2 after putting stampB")
464463
}
465464

466-
// Re-offer stamp A with lower-wrapped P1: same-stamp resolveDivergence
467-
// accepts it, so the store settles on P1.
468-
if err := r.Put(ctx, soc1.WithStamp(stampA)); err != nil {
469-
t.Fatalf("re-offer soc1 stampA: %v", err)
465+
// Re-offering weaker stamp A must be rejected by tie-break against stronger stamp B.
466+
err = r.Put(ctx, soc1.WithStamp(stampA))
467+
if !errors.Is(err, storage.ErrDivergentChunkRejected) {
468+
t.Fatalf("expected ErrDivergentChunkRejected on weaker re-offer, got: %v", err)
470469
}
471470

472471
finalCh, err := ts.ChunkStore().Get(ctx, soc1.Address())
473472
if err != nil {
474473
t.Fatalf("get final chunk: %v", err)
475474
}
476-
if !bytes.Equal(finalCh.Data(), soc1.Data()) {
477-
t.Fatal("expected payload P1 after re-offering lower-wrapped stampA variant")
475+
if !bytes.Equal(finalCh.Data(), soc2.Data()) {
476+
t.Fatal("expected payload P2 to be retained after rejecting weaker stampA re-offer")
478477
}
479478
}

pkg/storer/internal/reserve/reserve.go

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,12 @@ func (r *Reserve) evaluateSOCDivergence(
284284
if bestStoredStamp == nil || ts > highestPrevTimestamp {
285285
highestPrevTimestamp = ts
286286
bestStoredStamp = st
287+
} else if ts == highestPrevTimestamp {
288+
bestHash, err1 := bestStoredStamp.Hash()
289+
currHash, err2 := st.Hash()
290+
if err1 == nil && err2 == nil && bytes.Compare(currHash, bestHash) < 0 {
291+
bestStoredStamp = st
292+
}
287293
}
288294
return false, nil
289295
})
@@ -638,6 +644,12 @@ func (r *Reserve) resolveDivergence(
638644
if bestStoredStamp == nil || ts > highestPrevTimestamp {
639645
highestPrevTimestamp = ts
640646
bestStoredStamp = st
647+
} else if ts == highestPrevTimestamp {
648+
bestHash, err1 := bestStoredStamp.Hash()
649+
currHash, err2 := st.Hash()
650+
if err1 == nil && err2 == nil && bytes.Compare(currHash, bestHash) < 0 {
651+
bestStoredStamp = st
652+
}
641653
}
642654
return false, nil
643655
})
@@ -656,7 +668,7 @@ func (r *Reserve) resolveDivergence(
656668
return fmt.Errorf("overwrite same chunk. prev %d cur %d batch %s: %w", highestPrevTimestamp, currTimestamp, hex.EncodeToString(chunk.Stamp().BatchID()), storage.ErrOverwriteNewerChunk)
657669
}
658670

659-
if highestPrevTimestamp == currTimestamp && chunkType != swarm.ChunkTypeSingleOwner {
671+
if highestPrevTimestamp == currTimestamp {
660672
storedStampHash, err := stored.Stamp().Hash()
661673
if err != nil {
662674
return err

0 commit comments

Comments
 (0)