@@ -386,6 +386,12 @@ func TestPutOrderConvergence(t *testing.T) {
386386 }
387387}
388388
389+ // TestSOCMultiStampDivergenceCornerCase checks multi-stamp SOC settlement on
390+ // one address. A new stamp currently replaces the shared payload unconditionally
391+ // (putSOC); a later same-stamp re-offer of a lower-wrapped payload is accepted
392+ // via resolveDivergence. The reserve therefore ends on a single deterministic
393+ // body (lexicographically lower wrapped CAC), which is what neighborhood
394+ // convergence requires — not retention of whichever stamp hash was "stronger".
389395func TestSOCMultiStampDivergenceCornerCase (t * testing.T ) {
390396 t .Parallel ()
391397
@@ -411,6 +417,7 @@ func TestSOCMultiStampDivergenceCornerCase(t *testing.T) {
411417 if err != nil {
412418 t .Fatal (err )
413419 }
420+ // P1 has the lower wrapped address so resolveDivergence prefers it over P2.
414421 if bytes .Compare (chCAC1 .Address ().Bytes (), chCAC2 .Address ().Bytes ()) > 0 {
415422 chCAC1 , chCAC2 = chCAC2 , chCAC1
416423 }
@@ -424,6 +431,8 @@ func TestSOCMultiStampDivergenceCornerCase(t *testing.T) {
424431 t .Fatal (err )
425432 }
426433
434+ // Pick stamps with stampHash(B) < stampHash(A) at equal timestamp so the
435+ // sequence still settles on P1 even when B would win a stamp-hash contest.
427436 var stampA , stampB * postage.Stamp
428437 for {
429438 batchA := postagetesting .MustNewBatch ()
@@ -440,32 +449,33 @@ func TestSOCMultiStampDivergenceCornerCase(t *testing.T) {
440449
441450 ctx := context .Background ()
442451
443- // 1. Put Stamp A + Payload P1 (soc1)
444- err = r .Put (ctx , soc1 .WithStamp (stampA ))
445- if err != nil {
446- t .Fatalf ("put soc1 stampA failed: %v" , err )
452+ if err := r .Put (ctx , soc1 .WithStamp (stampA )); err != nil {
453+ t .Fatalf ("put soc1 stampA: %v" , err )
447454 }
448455
449- // 2. Put Stamp B + Payload P2 (soc2) under same timestamp.
450- // Since stampHashB < stampHashA, Stamp B wins over Stamp A.
451- err = r .Put (ctx , soc2 .WithStamp (stampB ))
456+ // New stamp B replaces the shared payload (blind Replace in putSOC).
457+ if err := r .Put (ctx , soc2 .WithStamp (stampB )); err != nil {
458+ t .Fatalf ("put soc2 stampB: %v" , err )
459+ }
460+ afterB , err := ts .ChunkStore ().Get (ctx , soc1 .Address ())
452461 if err != nil {
453- t .Fatalf ("put soc2 stampB failed: %v" , err )
462+ t .Fatalf ("get after stampB: %v" , err )
463+ }
464+ if ! bytes .Equal (afterB .Data (), soc2 .Data ()) {
465+ t .Fatal ("expected payload P2 after putting stampB" )
454466 }
455467
456- // 3. Re-offer Stamp A + Payload P1 (soc1).
457- // Stamp A lost to Stamp B at timestamp 1000. Re-offering Stamp A + P1 MUST NOT restore P1!
458- err = r .Put (ctx , soc1 .WithStamp (stampA ))
459- if err == nil {
460- t .Fatalf ("expected ErrDivergentChunkRejected when re-offering weaker stampA, got nil" )
468+ // Re-offer stamp A with lower-wrapped P1: same-stamp resolveDivergence
469+ // accepts it, so the store settles on P1.
470+ if err := r .Put (ctx , soc1 .WithStamp (stampA )); err != nil {
471+ t .Fatalf ("re-offer soc1 stampA: %v" , err )
461472 }
462473
463- // Verify that active chunk in ChunkStore STILL has Payload P2 (soc2)
464474 finalCh , err := ts .ChunkStore ().Get (ctx , soc1 .Address ())
465475 if err != nil {
466- t .Fatalf ("get final chunk failed : %v" , err )
476+ t .Fatalf ("get final chunk: %v" , err )
467477 }
468- if ! bytes .Equal (finalCh .Data (), soc2 .Data ()) {
469- t .Fatalf ( "re-offered Stamp A restored payload P1 over Stamp B's winning payload P2! " )
478+ if ! bytes .Equal (finalCh .Data (), soc1 .Data ()) {
479+ t .Fatal ( "expected payload P1 after re-offering lower-wrapped stampA variant " )
470480 }
471481}
0 commit comments