github.com/ethersphere/bee/v2@v2.2.0/pkg/postage/service_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 "context" 10 crand "crypto/rand" 11 "errors" 12 "io" 13 "math/big" 14 "testing" 15 16 "github.com/ethersphere/bee/v2/pkg/log" 17 "github.com/ethersphere/bee/v2/pkg/postage" 18 pstoremock "github.com/ethersphere/bee/v2/pkg/postage/batchstore/mock" 19 postagetesting "github.com/ethersphere/bee/v2/pkg/postage/testing" 20 "github.com/ethersphere/bee/v2/pkg/storage" 21 "github.com/ethersphere/bee/v2/pkg/storage/inmemstore" 22 "github.com/ethersphere/bee/v2/pkg/swarm" 23 "github.com/ethersphere/bee/v2/pkg/util/testutil" 24 ) 25 26 // TestSaveLoad tests the idempotence of saving and loading the postage.Service 27 // with all the active stamp issuers. 28 func TestSaveLoad(t *testing.T) { 29 store := inmemstore.New() 30 defer store.Close() 31 pstore := pstoremock.New() 32 saved := func(id int64) postage.Service { 33 ps, err := postage.NewService(log.Noop, store, pstore, id) 34 if err != nil { 35 t.Fatal(err) 36 } 37 for i := 0; i < 16; i++ { 38 err := ps.Add(newTestStampIssuer(t, 1000)) 39 if err != nil { 40 t.Fatal(err) 41 } 42 } 43 if err := ps.Close(); err != nil { 44 t.Fatal(err) 45 } 46 return ps 47 } 48 loaded := func(id int64) postage.Service { 49 ps, err := postage.NewService(log.Noop, store, pstore, id) 50 if err != nil { 51 t.Fatal(err) 52 } 53 return ps 54 } 55 test := func(id int64) { 56 psS := saved(id) 57 psL := loaded(id) 58 59 sMap := map[string]struct{}{} 60 stampIssuers := psS.StampIssuers() 61 for _, s := range stampIssuers { 62 sMap[string(s.ID())] = struct{}{} 63 } 64 65 stampIssuers = psL.StampIssuers() 66 for _, s := range stampIssuers { 67 if _, ok := sMap[string(s.ID())]; !ok { 68 t.Fatalf("mismatch between saved and loaded") 69 } 70 } 71 } 72 test(0) 73 test(1) 74 } 75 76 func TestGetStampIssuer(t *testing.T) { 77 store := inmemstore.New() 78 defer store.Close() 79 chainID := int64(0) 80 testChainState := postagetesting.NewChainState() 81 if testChainState.Block < uint64(postage.BlockThreshold) { 82 testChainState.Block += uint64(postage.BlockThreshold + 1) 83 } 84 validBlockNumber := testChainState.Block - uint64(postage.BlockThreshold+1) 85 pstore := pstoremock.New(pstoremock.WithChainState(testChainState)) 86 ps, err := postage.NewService(log.Noop, store, pstore, chainID) 87 if err != nil { 88 t.Fatal(err) 89 } 90 ids := make([][]byte, 8) 91 for i := range ids { 92 id := make([]byte, 32) 93 _, err := io.ReadFull(crand.Reader, id) 94 if err != nil { 95 t.Fatal(err) 96 } 97 ids[i] = id 98 if i == 0 { 99 continue 100 } 101 102 var shift uint64 = 0 103 if i > 3 { 104 shift = uint64(i) 105 } 106 err = ps.Add(postage.NewStampIssuer( 107 string(id), 108 "", 109 id, 110 big.NewInt(3), 111 16, 112 8, 113 validBlockNumber+shift, true), 114 ) 115 if err != nil { 116 t.Fatal(err) 117 } 118 } 119 t.Run("found", func(t *testing.T) { 120 for _, id := range ids[1:4] { 121 st, save, err := ps.GetStampIssuer(id) 122 if err != nil { 123 t.Fatalf("expected no error, got %v", err) 124 } 125 _ = save() 126 if st.Label() != string(id) { 127 t.Fatalf("wrong issuer returned") 128 } 129 } 130 131 // check if the save() call persisted the stamp issuers 132 for _, id := range ids[1:4] { 133 stampIssuerItem := postage.NewStampIssuerItem(id) 134 err := store.Get(stampIssuerItem) 135 if err != nil { 136 t.Fatal(err) 137 } 138 if string(id) != stampIssuerItem.ID() { 139 t.Fatalf("got id %s, want id %s", stampIssuerItem.ID(), string(id)) 140 } 141 } 142 }) 143 t.Run("not found", func(t *testing.T) { 144 _, _, err := ps.GetStampIssuer(ids[0]) 145 if !errors.Is(err, postage.ErrNotFound) { 146 t.Fatalf("expected ErrNotFound, got %v", err) 147 } 148 }) 149 t.Run("not usable", func(t *testing.T) { 150 for _, id := range ids[4:] { 151 _, _, err := ps.GetStampIssuer(id) 152 if !errors.Is(err, postage.ErrNotUsable) { 153 t.Fatalf("expected ErrNotUsable, got %v", err) 154 } 155 } 156 }) 157 t.Run("recovered", func(t *testing.T) { 158 b := postagetesting.MustNewBatch() 159 b.Start = validBlockNumber 160 testAmount := big.NewInt(1) 161 err := ps.HandleCreate(b, testAmount) 162 if err != nil { 163 t.Fatalf("expected no error, got %v", err) 164 } 165 st, sv, err := ps.GetStampIssuer(b.ID) 166 if err != nil { 167 t.Fatalf("expected no error, got %v", err) 168 } 169 if st.Label() != "recovered" { 170 t.Fatal("wrong issuer returned") 171 } 172 err = sv() 173 if err != nil { 174 t.Fatal(err) 175 } 176 }) 177 t.Run("topup", func(t *testing.T) { 178 ps.HandleTopUp(ids[1], big.NewInt(10)) 179 if err != nil { 180 t.Fatal(err) 181 } 182 stampIssuer, save, err := ps.GetStampIssuer(ids[1]) 183 if err != nil { 184 t.Fatalf("expected no error, got %v", err) 185 } 186 _ = save() 187 if stampIssuer.Amount().Cmp(big.NewInt(13)) != 0 { 188 t.Fatalf("expected amount %d got %d", 13, stampIssuer.Amount().Int64()) 189 } 190 }) 191 t.Run("dilute", func(t *testing.T) { 192 ps.HandleDepthIncrease(ids[2], 17) 193 if err != nil { 194 t.Fatal(err) 195 } 196 stampIssuer, save, err := ps.GetStampIssuer(ids[2]) 197 if err != nil { 198 t.Fatalf("expected no error, got %v", err) 199 } 200 _ = save() 201 if stampIssuer.Amount().Cmp(big.NewInt(3)) != 0 { 202 t.Fatalf("expected amount %d got %d", 3, stampIssuer.Amount().Int64()) 203 } 204 if stampIssuer.Depth() != 17 { 205 t.Fatalf("expected depth %d got %d", 17, stampIssuer.Depth()) 206 } 207 }) 208 } 209 210 func TestSetExpired(t *testing.T) { 211 store := inmemstore.New() 212 testutil.CleanupCloser(t, store) 213 214 batch := swarm.RandAddress(t).Bytes() 215 notExistsBatch := swarm.RandAddress(t).Bytes() 216 217 pstore := pstoremock.New(pstoremock.WithExistsFunc(func(b []byte) (bool, error) { 218 return bytes.Equal(b, batch), nil 219 })) 220 221 ps, err := postage.NewService(log.Noop, store, pstore, 1) 222 if err != nil { 223 t.Fatal(err) 224 } 225 226 itemExists := postage.NewStampItem().WithChunkAddress(swarm.RandAddress(t)).WithBatchID(batch) 227 err = store.Put(itemExists) 228 if err != nil { 229 t.Fatal(err) 230 } 231 232 itemNotExists := postage.NewStampItem().WithChunkAddress(swarm.RandAddress(t)).WithBatchID(notExistsBatch) 233 err = store.Put(itemNotExists) 234 if err != nil { 235 t.Fatal(err) 236 } 237 238 err = ps.Add(newTestStampIssuerID(t, 1000, itemExists.BatchID)) 239 if err != nil { 240 t.Fatal(err) 241 } 242 err = ps.Add(newTestStampIssuerID(t, 1000, itemNotExists.BatchID)) 243 if err != nil { 244 t.Fatal(err) 245 } 246 err = ps.HandleStampExpiry(context.Background(), itemNotExists.BatchID) 247 if err != nil { 248 t.Fatal(err) 249 } 250 251 _, _, err = ps.GetStampIssuer(itemNotExists.BatchID) 252 if !errors.Is(err, postage.ErrNotFound) { 253 t.Fatalf("expected %v, got %v", postage.ErrNotFound, err) 254 } 255 256 err = store.Iterate( 257 storage.Query{ 258 Factory: func() storage.Item { 259 return new(postage.StampItem) 260 }, 261 }, func(result storage.Result) (bool, error) { 262 item := result.Entry.(*postage.StampItem) 263 exists, err := pstore.Exists(item.BatchID) 264 if err != nil { 265 return false, err 266 } 267 268 if bytes.Equal(item.BatchID, notExistsBatch) && exists { 269 return false, errors.New("found stamp item belonging to a non-existent batch") 270 } 271 272 if bytes.Equal(item.BatchID, batch) && !exists { 273 return false, errors.New("found stamp item belonging to a batch that should exist") 274 } 275 276 return false, nil 277 }, 278 ) 279 if err != nil { 280 t.Fatal(err) 281 } 282 283 err = store.Get(itemExists) 284 if err != nil { 285 t.Fatal(err) 286 } 287 288 err = store.Get(itemNotExists) 289 if err == nil { 290 t.Fatal(err) 291 } 292 293 testutil.CleanupCloser(t, ps) 294 }