github.com/daragao/go-ethereum@v1.8.14-0.20180809141559-45eaef243198/swarm/storage/chunker_test.go (about) 1 // Copyright 2016 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package storage 18 19 import ( 20 "bytes" 21 "context" 22 "crypto/rand" 23 "encoding/binary" 24 "errors" 25 "fmt" 26 "io" 27 "testing" 28 29 "github.com/ethereum/go-ethereum/crypto/sha3" 30 ) 31 32 /* 33 Tests TreeChunker by splitting and joining a random byte slice 34 */ 35 36 type test interface { 37 Fatalf(string, ...interface{}) 38 Logf(string, ...interface{}) 39 } 40 41 type chunkerTester struct { 42 inputs map[uint64][]byte 43 t test 44 } 45 46 // fakeChunkStore doesn't store anything, just implements the ChunkStore interface 47 // It can be used to inject into a hasherStore if you don't want to actually store data just do the 48 // hashing 49 type fakeChunkStore struct { 50 } 51 52 // Put doesn't store anything it is just here to implement ChunkStore 53 func (f *fakeChunkStore) Put(context.Context, *Chunk) { 54 } 55 56 // Gut doesn't store anything it is just here to implement ChunkStore 57 func (f *fakeChunkStore) Get(context.Context, Address) (*Chunk, error) { 58 return nil, errors.New("FakeChunkStore doesn't support Get") 59 } 60 61 // Close doesn't store anything it is just here to implement ChunkStore 62 func (f *fakeChunkStore) Close() { 63 } 64 65 func newTestHasherStore(chunkStore ChunkStore, hash string) *hasherStore { 66 return NewHasherStore(chunkStore, MakeHashFunc(hash), false) 67 } 68 69 func testRandomBrokenData(n int, tester *chunkerTester) { 70 data := io.LimitReader(rand.Reader, int64(n)) 71 brokendata := brokenLimitReader(data, n, n/2) 72 73 buf := make([]byte, n) 74 _, err := brokendata.Read(buf) 75 if err == nil || err.Error() != "Broken reader" { 76 tester.t.Fatalf("Broken reader is not broken, hence broken. Returns: %v", err) 77 } 78 79 data = io.LimitReader(rand.Reader, int64(n)) 80 brokendata = brokenLimitReader(data, n, n/2) 81 82 putGetter := newTestHasherStore(NewMapChunkStore(), SHA3Hash) 83 84 expectedError := fmt.Errorf("Broken reader") 85 addr, _, err := TreeSplit(context.TODO(), brokendata, int64(n), putGetter) 86 if err == nil || err.Error() != expectedError.Error() { 87 tester.t.Fatalf("Not receiving the correct error! Expected %v, received %v", expectedError, err) 88 } 89 tester.t.Logf(" Key = %v\n", addr) 90 } 91 92 func testRandomData(usePyramid bool, hash string, n int, tester *chunkerTester) Address { 93 if tester.inputs == nil { 94 tester.inputs = make(map[uint64][]byte) 95 } 96 input, found := tester.inputs[uint64(n)] 97 var data io.Reader 98 if !found { 99 data, input = generateRandomData(n) 100 tester.inputs[uint64(n)] = input 101 } else { 102 data = io.LimitReader(bytes.NewReader(input), int64(n)) 103 } 104 105 putGetter := newTestHasherStore(NewMapChunkStore(), hash) 106 107 var addr Address 108 var wait func(context.Context) error 109 var err error 110 ctx := context.TODO() 111 if usePyramid { 112 addr, wait, err = PyramidSplit(ctx, data, putGetter, putGetter) 113 } else { 114 addr, wait, err = TreeSplit(ctx, data, int64(n), putGetter) 115 } 116 if err != nil { 117 tester.t.Fatalf(err.Error()) 118 } 119 tester.t.Logf(" Key = %v\n", addr) 120 err = wait(ctx) 121 if err != nil { 122 tester.t.Fatalf(err.Error()) 123 } 124 125 reader := TreeJoin(context.TODO(), addr, putGetter, 0) 126 output := make([]byte, n) 127 r, err := reader.Read(output) 128 if r != n || err != io.EOF { 129 tester.t.Fatalf("read error read: %v n = %v err = %v\n", r, n, err) 130 } 131 if input != nil { 132 if !bytes.Equal(output, input) { 133 tester.t.Fatalf("input and output mismatch\n IN: %v\nOUT: %v\n", input, output) 134 } 135 } 136 137 // testing partial read 138 for i := 1; i < n; i += 10000 { 139 readableLength := n - i 140 output := make([]byte, readableLength) 141 r, err := reader.ReadAt(output, int64(i)) 142 if r != readableLength || err != io.EOF { 143 tester.t.Fatalf("readAt error with offset %v read: %v n = %v err = %v\n", i, r, readableLength, err) 144 } 145 if input != nil { 146 if !bytes.Equal(output, input[i:]) { 147 tester.t.Fatalf("input and output mismatch\n IN: %v\nOUT: %v\n", input[i:], output) 148 } 149 } 150 } 151 152 return addr 153 } 154 155 func TestSha3ForCorrectness(t *testing.T) { 156 tester := &chunkerTester{t: t} 157 158 size := 4096 159 input := make([]byte, size+8) 160 binary.LittleEndian.PutUint64(input[:8], uint64(size)) 161 162 io.LimitReader(bytes.NewReader(input[8:]), int64(size)) 163 164 rawSha3 := sha3.NewKeccak256() 165 rawSha3.Reset() 166 rawSha3.Write(input) 167 rawSha3Output := rawSha3.Sum(nil) 168 169 sha3FromMakeFunc := MakeHashFunc(SHA3Hash)() 170 sha3FromMakeFunc.ResetWithLength(input[:8]) 171 sha3FromMakeFunc.Write(input[8:]) 172 sha3FromMakeFuncOutput := sha3FromMakeFunc.Sum(nil) 173 174 if len(rawSha3Output) != len(sha3FromMakeFuncOutput) { 175 tester.t.Fatalf("Original SHA3 and abstracted Sha3 has different length %v:%v\n", len(rawSha3Output), len(sha3FromMakeFuncOutput)) 176 } 177 178 if !bytes.Equal(rawSha3Output, sha3FromMakeFuncOutput) { 179 tester.t.Fatalf("Original SHA3 and abstracted Sha3 mismatch %v:%v\n", rawSha3Output, sha3FromMakeFuncOutput) 180 } 181 182 } 183 184 func TestDataAppend(t *testing.T) { 185 sizes := []int{1, 1, 1, 4095, 4096, 4097, 1, 1, 1, 123456, 2345678, 2345678} 186 appendSizes := []int{4095, 4096, 4097, 1, 1, 1, 8191, 8192, 8193, 9000, 3000, 5000} 187 188 tester := &chunkerTester{t: t} 189 for i := range sizes { 190 n := sizes[i] 191 m := appendSizes[i] 192 193 if tester.inputs == nil { 194 tester.inputs = make(map[uint64][]byte) 195 } 196 input, found := tester.inputs[uint64(n)] 197 var data io.Reader 198 if !found { 199 data, input = generateRandomData(n) 200 tester.inputs[uint64(n)] = input 201 } else { 202 data = io.LimitReader(bytes.NewReader(input), int64(n)) 203 } 204 205 chunkStore := NewMapChunkStore() 206 putGetter := newTestHasherStore(chunkStore, SHA3Hash) 207 208 ctx := context.TODO() 209 addr, wait, err := PyramidSplit(ctx, data, putGetter, putGetter) 210 if err != nil { 211 tester.t.Fatalf(err.Error()) 212 } 213 err = wait(ctx) 214 if err != nil { 215 tester.t.Fatalf(err.Error()) 216 } 217 218 //create a append data stream 219 appendInput, found := tester.inputs[uint64(m)] 220 var appendData io.Reader 221 if !found { 222 appendData, appendInput = generateRandomData(m) 223 tester.inputs[uint64(m)] = appendInput 224 } else { 225 appendData = io.LimitReader(bytes.NewReader(appendInput), int64(m)) 226 } 227 228 putGetter = newTestHasherStore(chunkStore, SHA3Hash) 229 newAddr, wait, err := PyramidAppend(ctx, addr, appendData, putGetter, putGetter) 230 if err != nil { 231 tester.t.Fatalf(err.Error()) 232 } 233 err = wait(ctx) 234 if err != nil { 235 tester.t.Fatalf(err.Error()) 236 } 237 238 reader := TreeJoin(ctx, newAddr, putGetter, 0) 239 newOutput := make([]byte, n+m) 240 r, err := reader.Read(newOutput) 241 if r != (n + m) { 242 tester.t.Fatalf("read error read: %v n = %v m = %v err = %v\n", r, n, m, err) 243 } 244 245 newInput := append(input, appendInput...) 246 if !bytes.Equal(newOutput, newInput) { 247 tester.t.Fatalf("input and output mismatch\n IN: %v\nOUT: %v\n", newInput, newOutput) 248 } 249 } 250 } 251 252 func TestRandomData(t *testing.T) { 253 // This test can validate files up to a relatively short length, as tree chunker slows down drastically. 254 // Validation of longer files is done by TestLocalStoreAndRetrieve in swarm package. 255 sizes := []int{1, 60, 83, 179, 253, 1024, 4095, 4096, 4097, 8191, 8192, 8193, 12287, 12288, 12289, 524288, 524288 + 1, 524288 + 4097, 7 * 524288, 7*524288 + 1, 7*524288 + 4097} 256 tester := &chunkerTester{t: t} 257 258 for _, s := range sizes { 259 treeChunkerKey := testRandomData(false, SHA3Hash, s, tester) 260 pyramidChunkerKey := testRandomData(true, SHA3Hash, s, tester) 261 if treeChunkerKey.String() != pyramidChunkerKey.String() { 262 tester.t.Fatalf("tree chunker and pyramid chunker key mismatch for size %v\n TC: %v\n PC: %v\n", s, treeChunkerKey.String(), pyramidChunkerKey.String()) 263 } 264 } 265 266 for _, s := range sizes { 267 treeChunkerKey := testRandomData(false, BMTHash, s, tester) 268 pyramidChunkerKey := testRandomData(true, BMTHash, s, tester) 269 if treeChunkerKey.String() != pyramidChunkerKey.String() { 270 tester.t.Fatalf("tree chunker and pyramid chunker key mismatch for size %v\n TC: %v\n PC: %v\n", s, treeChunkerKey.String(), pyramidChunkerKey.String()) 271 } 272 } 273 } 274 275 func TestRandomBrokenData(t *testing.T) { 276 sizes := []int{1, 60, 83, 179, 253, 1024, 4095, 4096, 4097, 8191, 8192, 8193, 12287, 12288, 12289, 123456, 2345678} 277 tester := &chunkerTester{t: t} 278 for _, s := range sizes { 279 testRandomBrokenData(s, tester) 280 } 281 } 282 283 func benchReadAll(reader LazySectionReader) { 284 size, _ := reader.Size(context.TODO(), nil) 285 output := make([]byte, 1000) 286 for pos := int64(0); pos < size; pos += 1000 { 287 reader.ReadAt(output, pos) 288 } 289 } 290 291 func benchmarkSplitJoin(n int, t *testing.B) { 292 t.ReportAllocs() 293 for i := 0; i < t.N; i++ { 294 data := testDataReader(n) 295 296 putGetter := newTestHasherStore(NewMapChunkStore(), SHA3Hash) 297 ctx := context.TODO() 298 key, wait, err := PyramidSplit(ctx, data, putGetter, putGetter) 299 if err != nil { 300 t.Fatalf(err.Error()) 301 } 302 err = wait(ctx) 303 if err != nil { 304 t.Fatalf(err.Error()) 305 } 306 reader := TreeJoin(ctx, key, putGetter, 0) 307 benchReadAll(reader) 308 } 309 } 310 311 func benchmarkSplitTreeSHA3(n int, t *testing.B) { 312 t.ReportAllocs() 313 for i := 0; i < t.N; i++ { 314 data := testDataReader(n) 315 putGetter := newTestHasherStore(&fakeChunkStore{}, SHA3Hash) 316 317 _, _, err := TreeSplit(context.TODO(), data, int64(n), putGetter) 318 if err != nil { 319 t.Fatalf(err.Error()) 320 } 321 } 322 } 323 324 func benchmarkSplitTreeBMT(n int, t *testing.B) { 325 t.ReportAllocs() 326 for i := 0; i < t.N; i++ { 327 data := testDataReader(n) 328 putGetter := newTestHasherStore(&fakeChunkStore{}, BMTHash) 329 330 _, _, err := TreeSplit(context.TODO(), data, int64(n), putGetter) 331 if err != nil { 332 t.Fatalf(err.Error()) 333 } 334 } 335 } 336 337 func benchmarkSplitPyramidSHA3(n int, t *testing.B) { 338 t.ReportAllocs() 339 for i := 0; i < t.N; i++ { 340 data := testDataReader(n) 341 putGetter := newTestHasherStore(&fakeChunkStore{}, SHA3Hash) 342 343 _, _, err := PyramidSplit(context.TODO(), data, putGetter, putGetter) 344 if err != nil { 345 t.Fatalf(err.Error()) 346 } 347 348 } 349 } 350 351 func benchmarkSplitPyramidBMT(n int, t *testing.B) { 352 t.ReportAllocs() 353 for i := 0; i < t.N; i++ { 354 data := testDataReader(n) 355 putGetter := newTestHasherStore(&fakeChunkStore{}, BMTHash) 356 357 _, _, err := PyramidSplit(context.TODO(), data, putGetter, putGetter) 358 if err != nil { 359 t.Fatalf(err.Error()) 360 } 361 } 362 } 363 364 func benchmarkSplitAppendPyramid(n, m int, t *testing.B) { 365 t.ReportAllocs() 366 for i := 0; i < t.N; i++ { 367 data := testDataReader(n) 368 data1 := testDataReader(m) 369 370 chunkStore := NewMapChunkStore() 371 putGetter := newTestHasherStore(chunkStore, SHA3Hash) 372 373 ctx := context.TODO() 374 key, wait, err := PyramidSplit(ctx, data, putGetter, putGetter) 375 if err != nil { 376 t.Fatalf(err.Error()) 377 } 378 err = wait(ctx) 379 if err != nil { 380 t.Fatalf(err.Error()) 381 } 382 383 putGetter = newTestHasherStore(chunkStore, SHA3Hash) 384 _, wait, err = PyramidAppend(ctx, key, data1, putGetter, putGetter) 385 if err != nil { 386 t.Fatalf(err.Error()) 387 } 388 err = wait(ctx) 389 if err != nil { 390 t.Fatalf(err.Error()) 391 } 392 } 393 } 394 395 func BenchmarkSplitJoin_2(t *testing.B) { benchmarkSplitJoin(100, t) } 396 func BenchmarkSplitJoin_3(t *testing.B) { benchmarkSplitJoin(1000, t) } 397 func BenchmarkSplitJoin_4(t *testing.B) { benchmarkSplitJoin(10000, t) } 398 func BenchmarkSplitJoin_5(t *testing.B) { benchmarkSplitJoin(100000, t) } 399 func BenchmarkSplitJoin_6(t *testing.B) { benchmarkSplitJoin(1000000, t) } 400 func BenchmarkSplitJoin_7(t *testing.B) { benchmarkSplitJoin(10000000, t) } 401 402 // func BenchmarkSplitJoin_8(t *testing.B) { benchmarkJoin(100000000, t) } 403 404 func BenchmarkSplitTreeSHA3_2(t *testing.B) { benchmarkSplitTreeSHA3(100, t) } 405 func BenchmarkSplitTreeSHA3_2h(t *testing.B) { benchmarkSplitTreeSHA3(500, t) } 406 func BenchmarkSplitTreeSHA3_3(t *testing.B) { benchmarkSplitTreeSHA3(1000, t) } 407 func BenchmarkSplitTreeSHA3_3h(t *testing.B) { benchmarkSplitTreeSHA3(5000, t) } 408 func BenchmarkSplitTreeSHA3_4(t *testing.B) { benchmarkSplitTreeSHA3(10000, t) } 409 func BenchmarkSplitTreeSHA3_4h(t *testing.B) { benchmarkSplitTreeSHA3(50000, t) } 410 func BenchmarkSplitTreeSHA3_5(t *testing.B) { benchmarkSplitTreeSHA3(100000, t) } 411 func BenchmarkSplitTreeSHA3_6(t *testing.B) { benchmarkSplitTreeSHA3(1000000, t) } 412 func BenchmarkSplitTreeSHA3_7(t *testing.B) { benchmarkSplitTreeSHA3(10000000, t) } 413 414 // func BenchmarkSplitTreeSHA3_8(t *testing.B) { benchmarkSplitTreeSHA3(100000000, t) } 415 416 func BenchmarkSplitTreeBMT_2(t *testing.B) { benchmarkSplitTreeBMT(100, t) } 417 func BenchmarkSplitTreeBMT_2h(t *testing.B) { benchmarkSplitTreeBMT(500, t) } 418 func BenchmarkSplitTreeBMT_3(t *testing.B) { benchmarkSplitTreeBMT(1000, t) } 419 func BenchmarkSplitTreeBMT_3h(t *testing.B) { benchmarkSplitTreeBMT(5000, t) } 420 func BenchmarkSplitTreeBMT_4(t *testing.B) { benchmarkSplitTreeBMT(10000, t) } 421 func BenchmarkSplitTreeBMT_4h(t *testing.B) { benchmarkSplitTreeBMT(50000, t) } 422 func BenchmarkSplitTreeBMT_5(t *testing.B) { benchmarkSplitTreeBMT(100000, t) } 423 func BenchmarkSplitTreeBMT_6(t *testing.B) { benchmarkSplitTreeBMT(1000000, t) } 424 func BenchmarkSplitTreeBMT_7(t *testing.B) { benchmarkSplitTreeBMT(10000000, t) } 425 426 // func BenchmarkSplitTreeBMT_8(t *testing.B) { benchmarkSplitTreeBMT(100000000, t) } 427 428 func BenchmarkSplitPyramidSHA3_2(t *testing.B) { benchmarkSplitPyramidSHA3(100, t) } 429 func BenchmarkSplitPyramidSHA3_2h(t *testing.B) { benchmarkSplitPyramidSHA3(500, t) } 430 func BenchmarkSplitPyramidSHA3_3(t *testing.B) { benchmarkSplitPyramidSHA3(1000, t) } 431 func BenchmarkSplitPyramidSHA3_3h(t *testing.B) { benchmarkSplitPyramidSHA3(5000, t) } 432 func BenchmarkSplitPyramidSHA3_4(t *testing.B) { benchmarkSplitPyramidSHA3(10000, t) } 433 func BenchmarkSplitPyramidSHA3_4h(t *testing.B) { benchmarkSplitPyramidSHA3(50000, t) } 434 func BenchmarkSplitPyramidSHA3_5(t *testing.B) { benchmarkSplitPyramidSHA3(100000, t) } 435 func BenchmarkSplitPyramidSHA3_6(t *testing.B) { benchmarkSplitPyramidSHA3(1000000, t) } 436 func BenchmarkSplitPyramidSHA3_7(t *testing.B) { benchmarkSplitPyramidSHA3(10000000, t) } 437 438 // func BenchmarkSplitPyramidSHA3_8(t *testing.B) { benchmarkSplitPyramidSHA3(100000000, t) } 439 440 func BenchmarkSplitPyramidBMT_2(t *testing.B) { benchmarkSplitPyramidBMT(100, t) } 441 func BenchmarkSplitPyramidBMT_2h(t *testing.B) { benchmarkSplitPyramidBMT(500, t) } 442 func BenchmarkSplitPyramidBMT_3(t *testing.B) { benchmarkSplitPyramidBMT(1000, t) } 443 func BenchmarkSplitPyramidBMT_3h(t *testing.B) { benchmarkSplitPyramidBMT(5000, t) } 444 func BenchmarkSplitPyramidBMT_4(t *testing.B) { benchmarkSplitPyramidBMT(10000, t) } 445 func BenchmarkSplitPyramidBMT_4h(t *testing.B) { benchmarkSplitPyramidBMT(50000, t) } 446 func BenchmarkSplitPyramidBMT_5(t *testing.B) { benchmarkSplitPyramidBMT(100000, t) } 447 func BenchmarkSplitPyramidBMT_6(t *testing.B) { benchmarkSplitPyramidBMT(1000000, t) } 448 func BenchmarkSplitPyramidBMT_7(t *testing.B) { benchmarkSplitPyramidBMT(10000000, t) } 449 450 // func BenchmarkSplitPyramidBMT_8(t *testing.B) { benchmarkSplitPyramidBMT(100000000, t) } 451 452 func BenchmarkSplitAppendPyramid_2(t *testing.B) { benchmarkSplitAppendPyramid(100, 1000, t) } 453 func BenchmarkSplitAppendPyramid_2h(t *testing.B) { benchmarkSplitAppendPyramid(500, 1000, t) } 454 func BenchmarkSplitAppendPyramid_3(t *testing.B) { benchmarkSplitAppendPyramid(1000, 1000, t) } 455 func BenchmarkSplitAppendPyramid_4(t *testing.B) { benchmarkSplitAppendPyramid(10000, 1000, t) } 456 func BenchmarkSplitAppendPyramid_4h(t *testing.B) { benchmarkSplitAppendPyramid(50000, 1000, t) } 457 func BenchmarkSplitAppendPyramid_5(t *testing.B) { benchmarkSplitAppendPyramid(1000000, 1000, t) } 458 func BenchmarkSplitAppendPyramid_6(t *testing.B) { benchmarkSplitAppendPyramid(1000000, 1000, t) } 459 func BenchmarkSplitAppendPyramid_7(t *testing.B) { benchmarkSplitAppendPyramid(10000000, 1000, t) } 460 461 // func BenchmarkAppendPyramid_8(t *testing.B) { benchmarkAppendPyramid(100000000, 1000, t) } 462 463 // go test -timeout 20m -cpu 4 -bench=./swarm/storage -run no 464 // If you dont add the timeout argument above .. the benchmark will timeout and dump