github.com/ethersphere/bee/v2@v2.2.0/pkg/postage/stamper_test.go (about) 1 // Copyright 2020 The Swarm Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package postage_test 6 7 import ( 8 "bytes" 9 "errors" 10 "math/big" 11 "testing" 12 13 "github.com/ethersphere/bee/v2/pkg/crypto" 14 "github.com/ethersphere/bee/v2/pkg/postage" 15 "github.com/ethersphere/bee/v2/pkg/storage" 16 "github.com/ethersphere/bee/v2/pkg/storage/inmemstore" 17 "github.com/ethersphere/bee/v2/pkg/swarm" 18 ) 19 20 // TestStamperStamping tests if the stamp created by the stamper is valid. 21 func TestStamperStamping(t *testing.T) { 22 privKey, err := crypto.GenerateSecp256k1Key() 23 if err != nil { 24 t.Fatal(err) 25 } 26 27 owner, err := crypto.NewEthereumAddress(privKey.PublicKey) 28 if err != nil { 29 t.Fatal(err) 30 } 31 signer := crypto.NewDefaultSigner(privKey) 32 createStamp := func(t *testing.T, stamper postage.Stamper) (swarm.Address, *postage.Stamp) { 33 t.Helper() 34 35 chunkAddr := swarm.RandAddress(t) 36 stamp, err := stamper.Stamp(chunkAddr) 37 if err != nil { 38 t.Fatal(err) 39 } 40 return chunkAddr, stamp 41 } 42 43 // tests a valid stamp 44 t.Run("valid stamp", func(t *testing.T) { 45 st := newTestStampIssuer(t, 1000) 46 stamper := postage.NewStamper(inmemstore.New(), st, signer) 47 chunkAddr, stamp := createStamp(t, stamper) 48 if err := stamp.Valid(chunkAddr, owner, 12, 8, true); err != nil { 49 t.Fatalf("expected no error, got %v", err) 50 } 51 }) 52 53 // tests that Stamps returns with postage.ErrBucketMismatch 54 t.Run("bucket mismatch", func(t *testing.T) { 55 st := newTestStampIssuer(t, 1000) 56 stamper := postage.NewStamper(inmemstore.New(), st, signer) 57 chunkAddr, stamp := createStamp(t, stamper) 58 a := chunkAddr.Bytes() 59 a[0] ^= 0xff 60 if err := stamp.Valid(swarm.NewAddress(a), owner, 12, 8, true); !errors.Is(err, postage.ErrBucketMismatch) { 61 t.Fatalf("expected ErrBucketMismatch, got %v", err) 62 } 63 }) 64 65 // tests that Stamps returns with postage.ErrInvalidIndex 66 t.Run("invalid index", func(t *testing.T) { 67 st := newTestStampIssuer(t, 1000) 68 stamper := postage.NewStamper(inmemstore.New(), st, signer) 69 // issue 1 stamp 70 chunkAddr, _ := createStamp(t, stamper) 71 // issue another 15 72 // collision depth is 8, committed batch depth is 12, bucket volume 2^4 73 for i := 0; i < 14; i++ { 74 _, err = stamper.Stamp(swarm.RandAddressAt(t, chunkAddr, 8)) 75 if err != nil { 76 t.Fatalf("error adding stamp at step %d: %v", i, err) 77 } 78 } 79 stamp, err := stamper.Stamp(swarm.RandAddressAt(t, chunkAddr, 8)) 80 if err != nil { 81 t.Fatalf("error adding last stamp: %v", err) 82 } 83 if err := stamp.Valid(chunkAddr, owner, 11, 8, true); !errors.Is(err, postage.ErrInvalidIndex) { 84 t.Fatalf("expected ErrInvalidIndex, got %v", err) 85 } 86 }) 87 88 // tests that Stamps returns with postage.ErrBucketFull iff 89 // issuer has the corresponding collision bucket filled] 90 t.Run("bucket full", func(t *testing.T) { 91 st := postage.NewStampIssuer("", "", newTestStampIssuer(t, 1000).ID(), big.NewInt(3), 12, 8, 1000, true) 92 stamper := postage.NewStamper(inmemstore.New(), st, signer) 93 // issue 1 stamp 94 chunkAddr, _ := createStamp(t, stamper) 95 // issue another 15 96 // collision depth is 8, committed batch depth is 12, bucket volume 2^4 97 for i := 0; i < 15; i++ { 98 _, err = stamper.Stamp(swarm.RandAddressAt(t, chunkAddr, 8)) 99 if err != nil { 100 t.Fatalf("error adding stamp at step %d: %v", i, err) 101 } 102 } 103 // the bucket should now be full, not allowing a stamp for the pivot chunk 104 if _, err = stamper.Stamp(swarm.RandAddressAt(t, chunkAddr, 8)); !errors.Is(err, postage.ErrBucketFull) { 105 t.Fatalf("expected ErrBucketFull, got %v", err) 106 } 107 }) 108 109 t.Run("reuse index but get new timestamp for mutable or immutable batch", func(t *testing.T) { 110 st := newTestStampIssuerMutability(t, 1000, false) 111 chunkAddr := swarm.RandAddress(t) 112 bIdx := postage.ToBucket(st.BucketDepth(), chunkAddr) 113 index := postage.IndexToBytes(bIdx, 4) 114 testItem := postage.NewStampItem(). 115 WithBatchID(st.ID()). 116 WithChunkAddress(chunkAddr). 117 WithBatchIndex(index) 118 testSt := &testStore{Store: inmemstore.New(), stampItem: testItem} 119 stamper := postage.NewStamper(testSt, st, signer) 120 stamp, err := stamper.Stamp(chunkAddr) 121 if err != nil { 122 t.Fatal(err) 123 } 124 for _, mutability := range []bool{true, false} { 125 if err := stamp.Valid(chunkAddr, owner, 12, 8, mutability); err != nil { 126 t.Fatalf("expected no error, got %v", err) 127 } 128 if bytes.Equal(stamp.Timestamp(), testItem.BatchTimestamp) { 129 t.Fatalf("expected timestamp to be different, got %x", stamp.Index()) 130 } 131 if !bytes.Equal(stamp.Index(), testItem.BatchIndex) { 132 t.Fatalf("expected index to be the same, got %x", stamp.Index()) 133 } 134 } 135 }) 136 137 // tests return with ErrOwnerMismatch 138 t.Run("owner mismatch", func(t *testing.T) { 139 owner[0] ^= 0xff // bitflip the owner first byte, this case must come last! 140 st := newTestStampIssuer(t, 1000) 141 stamper := postage.NewStamper(inmemstore.New(), st, signer) 142 chunkAddr, stamp := createStamp(t, stamper) 143 if err := stamp.Valid(chunkAddr, owner, 12, 8, true); !errors.Is(err, postage.ErrOwnerMismatch) { 144 t.Fatalf("expected ErrOwnerMismatch, got %v", err) 145 } 146 }) 147 } 148 149 type testStore struct { 150 storage.Store 151 stampItem *postage.StampItem 152 } 153 154 func (t *testStore) Get(item storage.Item) error { 155 if item.Namespace() == (postage.StampItem{}).Namespace() { 156 if t.stampItem == nil { 157 return storage.ErrNotFound 158 } 159 *item.(*postage.StampItem) = *t.stampItem 160 return nil 161 } 162 return t.Store.Get(item) 163 }