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