github.com/ethersphere/bee/v2@v2.2.0/pkg/storageincentives/proof_test.go (about)

     1  // Copyright 2023 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 storageincentives_test
     6  
     7  import (
     8  	"bytes"
     9  	_ "embed"
    10  	"encoding/json"
    11  	"fmt"
    12  	"math/big"
    13  	"testing"
    14  
    15  	"github.com/ethersphere/bee/v2/pkg/cac"
    16  	"github.com/ethersphere/bee/v2/pkg/crypto"
    17  	"github.com/ethersphere/bee/v2/pkg/postage"
    18  	postagetesting "github.com/ethersphere/bee/v2/pkg/postage/testing"
    19  	"github.com/ethersphere/bee/v2/pkg/soc"
    20  	"github.com/ethersphere/bee/v2/pkg/storageincentives"
    21  	"github.com/ethersphere/bee/v2/pkg/storageincentives/redistribution"
    22  	storer "github.com/ethersphere/bee/v2/pkg/storer"
    23  	"github.com/ethersphere/bee/v2/pkg/swarm"
    24  	"github.com/ethersphere/bee/v2/pkg/util/testutil"
    25  	"github.com/google/go-cmp/cmp"
    26  )
    27  
    28  // Test asserts valid case for MakeInclusionProofs.
    29  func TestMakeInclusionProofs_FLAKY(t *testing.T) {
    30  	t.Parallel()
    31  
    32  	anchor := testutil.RandBytes(t, 1)
    33  	sample := storer.RandSample(t, anchor)
    34  
    35  	_, err := storageincentives.MakeInclusionProofs(sample.Items, anchor, anchor)
    36  	if err != nil {
    37  		t.Fatal(err)
    38  	}
    39  }
    40  
    41  //go:embed testdata/inclusion-proofs.json
    42  var testData []byte
    43  
    44  // Test asserts that MakeInclusionProofs will generate the same
    45  // output for given sample.
    46  func TestMakeInclusionProofsRegression(t *testing.T) {
    47  	t.Parallel()
    48  
    49  	const sampleSize = 16
    50  
    51  	keyRaw := `00000000000000000000000000000000`
    52  	privKey, err := crypto.DecodeSecp256k1PrivateKey([]byte(keyRaw))
    53  	if err != nil {
    54  		t.Fatal(err)
    55  	}
    56  	signer := crypto.NewDefaultSigner(privKey)
    57  
    58  	stampID, _ := crypto.LegacyKeccak256([]byte("The Inverted Jenny"))
    59  	index := []byte{0, 0, 0, 0, 0, 8, 3, 3}
    60  	timestamp := []byte{0, 0, 0, 0, 0, 3, 3, 8}
    61  	stamper := func(addr swarm.Address) *postage.Stamp {
    62  		sig := postagetesting.MustNewValidSignature(signer, addr, stampID, index, timestamp)
    63  		return postage.NewStamp(stampID, index, timestamp, sig)
    64  	}
    65  
    66  	anchor1 := big.NewInt(100).Bytes()
    67  	anchor2 := big.NewInt(30).Bytes() // this anchor will pick chunks 3, 6, 15
    68  
    69  	// generate chunks that will be used as sample
    70  	sampleChunks := make([]swarm.Chunk, 0, sampleSize)
    71  	for i := 0; i < sampleSize; i++ {
    72  		ch, err := cac.New([]byte(fmt.Sprintf("Unstoppable data! Chunk #%d", i+1)))
    73  		if err != nil {
    74  			t.Fatal(err)
    75  		}
    76  
    77  		if i%2 == 0 {
    78  			id, err := crypto.LegacyKeccak256([]byte(fmt.Sprintf("ID #%d", i+1)))
    79  			if err != nil {
    80  				t.Fatal(err)
    81  			}
    82  
    83  			socCh, err := soc.New(id, ch).Sign(signer)
    84  			if err != nil {
    85  				t.Fatal(err)
    86  			}
    87  
    88  			ch = socCh
    89  		}
    90  
    91  		ch = ch.WithStamp(stamper(ch.Address()))
    92  
    93  		sampleChunks = append(sampleChunks, ch)
    94  	}
    95  
    96  	// make sample from chunks
    97  	sample, err := storer.MakeSampleUsingChunks(sampleChunks, anchor1)
    98  	if err != nil {
    99  		t.Fatal(err)
   100  	}
   101  
   102  	// assert that sample chunk hash/address does not change
   103  	sch, err := storageincentives.SampleChunk(sample.Items)
   104  	if err != nil {
   105  		t.Fatal(err)
   106  	}
   107  	if want := swarm.MustParseHexAddress("b012904b0c3e6462158b4416556caa888031a79bad46d2ffa7012408c9c38aa8"); !sch.Address().Equal(want) {
   108  		t.Fatalf("expecting sample chunk address %v, got %v", want, sch.Address())
   109  	}
   110  
   111  	// assert that inclusion proofs values does not change
   112  	proofs, err := storageincentives.MakeInclusionProofs(sample.Items, anchor1, anchor2)
   113  	if err != nil {
   114  		t.Fatal(err)
   115  	}
   116  
   117  	var expectedProofs redistribution.ChunkInclusionProofs
   118  
   119  	err = json.Unmarshal(testData, &expectedProofs)
   120  	if err != nil {
   121  		t.Fatal(err)
   122  	}
   123  
   124  	if diff := cmp.Diff(expectedProofs, proofs); diff != "" {
   125  		t.Fatalf("unexpected inclusion proofs (-want +have):\n%s", diff)
   126  	}
   127  }
   128  
   129  // Test asserts cases when MakeInclusionProofs should return error.
   130  func TestMakeInclusionProofsExpectedError(t *testing.T) {
   131  	t.Parallel()
   132  
   133  	t.Run("invalid sample length", func(t *testing.T) {
   134  		anchor := testutil.RandBytes(t, 8)
   135  		sample := storer.RandSample(t, anchor)
   136  
   137  		_, err := storageincentives.MakeInclusionProofs(sample.Items[:1], anchor, anchor)
   138  		if err == nil {
   139  			t.Fatal("expecting error")
   140  		}
   141  	})
   142  
   143  	t.Run("empty anchor", func(t *testing.T) {
   144  		sample := storer.RandSample(t, []byte{})
   145  
   146  		_, err := storageincentives.MakeInclusionProofs(sample.Items[:1], []byte{}, []byte{})
   147  		if err == nil {
   148  			t.Fatal("expecting error")
   149  		}
   150  	})
   151  }
   152  
   153  // Tests asserts that creating sample chunk is valid for all lengths [1-MaxSampleSize]
   154  func TestSampleChunk(t *testing.T) {
   155  	t.Parallel()
   156  
   157  	sample := storer.RandSample(t, nil)
   158  
   159  	for i := 0; i < len(sample.Items); i++ {
   160  		items := sample.Items[:i]
   161  
   162  		chunk, err := storageincentives.SampleChunk(items)
   163  		if err != nil {
   164  			t.Fatal(err)
   165  		}
   166  
   167  		data := chunk.Data()[swarm.SpanSize:]
   168  		pos := 0
   169  		for _, item := range items {
   170  			if !bytes.Equal(data[pos:pos+swarm.HashSize], item.ChunkAddress.Bytes()) {
   171  				t.Error("expected chunk address")
   172  			}
   173  			pos += swarm.HashSize
   174  
   175  			if !bytes.Equal(data[pos:pos+swarm.HashSize], item.TransformedAddress.Bytes()) {
   176  				t.Error("expected transformed address")
   177  			}
   178  			pos += swarm.HashSize
   179  		}
   180  
   181  		if !chunk.Address().IsValidNonEmpty() {
   182  			t.Error("address shouldn't be empty")
   183  		}
   184  	}
   185  }
   186  
   187  // Tests asserts that creating sample chunk should fail because it will exceed
   188  // capacity of chunk data.
   189  func TestSampleChunkExpectedError(t *testing.T) {
   190  	t.Parallel()
   191  
   192  	sampleItem := storer.RandSample(t, nil).Items[0]
   193  
   194  	items := make([]storer.SampleItem, 65)
   195  	for i := range items {
   196  		items[i] = sampleItem
   197  	}
   198  
   199  	_, err := storageincentives.SampleChunk(items)
   200  	if err == nil {
   201  		t.Fatal("expecting error")
   202  	}
   203  }