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 }