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