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  }