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  }