| 
									
										
										
										
											2020-09-29 17:38:13 +02:00
										 |  |  | package trie | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"bytes" | 
					
						
							|  |  |  | 	"fmt" | 
					
						
							|  |  |  | 	"math/big" | 
					
						
							|  |  |  | 	mrand "math/rand" | 
					
						
							|  |  |  | 	"testing" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/ethereum/go-ethereum/common" | 
					
						
							|  |  |  | 	"github.com/ethereum/go-ethereum/common/hexutil" | 
					
						
							|  |  |  | 	"github.com/ethereum/go-ethereum/core/types" | 
					
						
							|  |  |  | 	"github.com/ethereum/go-ethereum/crypto" | 
					
						
							|  |  |  | 	"github.com/ethereum/go-ethereum/ethdb/memorydb" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func TestSizeBug(t *testing.T) { | 
					
						
							|  |  |  | 	st := NewStackTrie(nil) | 
					
						
							|  |  |  | 	nt, _ := New(common.Hash{}, NewDatabase(memorydb.New())) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	leaf := common.FromHex("290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563") | 
					
						
							|  |  |  | 	value := common.FromHex("94cf40d0d2b44f2b66e07cace1372ca42b73cf21a3") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	nt.TryUpdate(leaf, value) | 
					
						
							|  |  |  | 	st.TryUpdate(leaf, value) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if nt.Hash() != st.Hash() { | 
					
						
							|  |  |  | 		t.Fatalf("error %x != %x", st.Hash(), nt.Hash()) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func TestEmptyBug(t *testing.T) { | 
					
						
							|  |  |  | 	st := NewStackTrie(nil) | 
					
						
							|  |  |  | 	nt, _ := New(common.Hash{}, NewDatabase(memorydb.New())) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	//leaf := common.FromHex("290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563") | 
					
						
							|  |  |  | 	//value := common.FromHex("94cf40d0d2b44f2b66e07cace1372ca42b73cf21a3") | 
					
						
							|  |  |  | 	kvs := []struct { | 
					
						
							|  |  |  | 		K string | 
					
						
							|  |  |  | 		V string | 
					
						
							|  |  |  | 	}{ | 
					
						
							|  |  |  | 		{K: "405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace", V: "9496f4ec2bf9dab484cac6be589e8417d84781be08"}, | 
					
						
							|  |  |  | 		{K: "40edb63a35fcf86c08022722aa3287cdd36440d671b4918131b2514795fefa9c", V: "01"}, | 
					
						
							|  |  |  | 		{K: "b10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6", V: "947a30f7736e48d6599356464ba4c150d8da0302ff"}, | 
					
						
							|  |  |  | 		{K: "c2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b", V: "02"}, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for _, kv := range kvs { | 
					
						
							|  |  |  | 		nt.TryUpdate(common.FromHex(kv.K), common.FromHex(kv.V)) | 
					
						
							|  |  |  | 		st.TryUpdate(common.FromHex(kv.K), common.FromHex(kv.V)) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if nt.Hash() != st.Hash() { | 
					
						
							|  |  |  | 		t.Fatalf("error %x != %x", st.Hash(), nt.Hash()) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func TestValLength56(t *testing.T) { | 
					
						
							|  |  |  | 	st := NewStackTrie(nil) | 
					
						
							|  |  |  | 	nt, _ := New(common.Hash{}, NewDatabase(memorydb.New())) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	//leaf := common.FromHex("290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563") | 
					
						
							|  |  |  | 	//value := common.FromHex("94cf40d0d2b44f2b66e07cace1372ca42b73cf21a3") | 
					
						
							|  |  |  | 	kvs := []struct { | 
					
						
							|  |  |  | 		K string | 
					
						
							|  |  |  | 		V string | 
					
						
							|  |  |  | 	}{ | 
					
						
							|  |  |  | 		{K: "405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace", V: "1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111"}, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for _, kv := range kvs { | 
					
						
							|  |  |  | 		nt.TryUpdate(common.FromHex(kv.K), common.FromHex(kv.V)) | 
					
						
							|  |  |  | 		st.TryUpdate(common.FromHex(kv.K), common.FromHex(kv.V)) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if nt.Hash() != st.Hash() { | 
					
						
							|  |  |  | 		t.Fatalf("error %x != %x", st.Hash(), nt.Hash()) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func genTxs(num uint64) (types.Transactions, error) { | 
					
						
							|  |  |  | 	key, err := crypto.HexToECDSA("deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef") | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	var addr = crypto.PubkeyToAddress(key.PublicKey) | 
					
						
							|  |  |  | 	newTx := func(i uint64) (*types.Transaction, error) { | 
					
						
							|  |  |  | 		signer := types.NewEIP155Signer(big.NewInt(18)) | 
					
						
							|  |  |  | 		tx, err := types.SignTx(types.NewTransaction(i, addr, new(big.Int), 0, new(big.Int).SetUint64(10000000), nil), signer, key) | 
					
						
							|  |  |  | 		return tx, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	var txs types.Transactions | 
					
						
							|  |  |  | 	for i := uint64(0); i < num; i++ { | 
					
						
							|  |  |  | 		tx, err := newTx(i) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		txs = append(txs, tx) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return txs, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func TestDeriveSha(t *testing.T) { | 
					
						
							|  |  |  | 	txs, err := genTxs(0) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		t.Fatal(err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	for len(txs) < 1000 { | 
					
						
							|  |  |  | 		exp := types.DeriveSha(txs, newEmpty()) | 
					
						
							|  |  |  | 		got := types.DeriveSha(txs, NewStackTrie(nil)) | 
					
						
							|  |  |  | 		if !bytes.Equal(got[:], exp[:]) { | 
					
						
							|  |  |  | 			t.Fatalf("%d txs: got %x exp %x", len(txs), got, exp) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		newTxs, err := genTxs(uint64(len(txs) + 1)) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			t.Fatal(err) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		txs = append(txs, newTxs...) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func BenchmarkDeriveSha200(b *testing.B) { | 
					
						
							|  |  |  | 	txs, err := genTxs(200) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		b.Fatal(err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	var exp common.Hash | 
					
						
							|  |  |  | 	var got common.Hash | 
					
						
							|  |  |  | 	b.Run("std_trie", func(b *testing.B) { | 
					
						
							|  |  |  | 		b.ResetTimer() | 
					
						
							|  |  |  | 		b.ReportAllocs() | 
					
						
							|  |  |  | 		for i := 0; i < b.N; i++ { | 
					
						
							|  |  |  | 			exp = types.DeriveSha(txs, newEmpty()) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	b.Run("stack_trie", func(b *testing.B) { | 
					
						
							|  |  |  | 		b.ResetTimer() | 
					
						
							|  |  |  | 		b.ReportAllocs() | 
					
						
							|  |  |  | 		for i := 0; i < b.N; i++ { | 
					
						
							|  |  |  | 			got = types.DeriveSha(txs, NewStackTrie(nil)) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 	if got != exp { | 
					
						
							|  |  |  | 		b.Errorf("got %x exp %x", got, exp) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type dummyDerivableList struct { | 
					
						
							|  |  |  | 	len  int | 
					
						
							|  |  |  | 	seed int | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func newDummy(seed int) *dummyDerivableList { | 
					
						
							|  |  |  | 	d := &dummyDerivableList{} | 
					
						
							|  |  |  | 	src := mrand.NewSource(int64(seed)) | 
					
						
							|  |  |  | 	// don't use lists longer than 4K items | 
					
						
							|  |  |  | 	d.len = int(src.Int63() & 0x0FFF) | 
					
						
							|  |  |  | 	d.seed = seed | 
					
						
							|  |  |  | 	return d | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (d *dummyDerivableList) Len() int { | 
					
						
							|  |  |  | 	return d.len | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (d *dummyDerivableList) GetRlp(i int) []byte { | 
					
						
							|  |  |  | 	src := mrand.NewSource(int64(d.seed + i)) | 
					
						
							|  |  |  | 	// max item size 256, at least 1 byte per item | 
					
						
							|  |  |  | 	size := 1 + src.Int63()&0x00FF | 
					
						
							|  |  |  | 	data := make([]byte, size) | 
					
						
							|  |  |  | 	_, err := mrand.New(src).Read(data) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		panic(err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return data | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func printList(l types.DerivableList) { | 
					
						
							|  |  |  | 	fmt.Printf("list length: %d\n", l.Len()) | 
					
						
							|  |  |  | 	fmt.Printf("{\n") | 
					
						
							|  |  |  | 	for i := 0; i < l.Len(); i++ { | 
					
						
							|  |  |  | 		v := l.GetRlp(i) | 
					
						
							|  |  |  | 		fmt.Printf("\"0x%x\",\n", v) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	fmt.Printf("},\n") | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func TestFuzzDeriveSha(t *testing.T) { | 
					
						
							|  |  |  | 	// increase this for longer runs -- it's set to quite low for travis | 
					
						
							|  |  |  | 	rndSeed := mrand.Int() | 
					
						
							|  |  |  | 	for i := 0; i < 10; i++ { | 
					
						
							|  |  |  | 		seed := rndSeed + i | 
					
						
							|  |  |  | 		exp := types.DeriveSha(newDummy(i), newEmpty()) | 
					
						
							|  |  |  | 		got := types.DeriveSha(newDummy(i), NewStackTrie(nil)) | 
					
						
							|  |  |  | 		if !bytes.Equal(got[:], exp[:]) { | 
					
						
							|  |  |  | 			printList(newDummy(seed)) | 
					
						
							|  |  |  | 			t.Fatalf("seed %d: got %x exp %x", seed, got, exp) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type flatList struct { | 
					
						
							|  |  |  | 	rlpvals []string | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func newFlatList(rlpvals []string) *flatList { | 
					
						
							|  |  |  | 	return &flatList{rlpvals} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | func (f *flatList) Len() int { | 
					
						
							|  |  |  | 	return len(f.rlpvals) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | func (f *flatList) GetRlp(i int) []byte { | 
					
						
							|  |  |  | 	return hexutil.MustDecode(f.rlpvals[i]) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // TestDerivableList contains testcases found via fuzzing | 
					
						
							|  |  |  | func TestDerivableList(t *testing.T) { | 
					
						
							|  |  |  | 	type tcase []string | 
					
						
							|  |  |  | 	tcs := []tcase{ | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			"0xc041", | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			"0xf04cf757812428b0763112efb33b6f4fad7deb445e", | 
					
						
							|  |  |  | 			"0xf04cf757812428b0763112efb33b6f4fad7deb445e", | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			"0xca410605310cdc3bb8d4977ae4f0143df54a724ed873457e2272f39d66e0460e971d9d", | 
					
						
							|  |  |  | 			"0x6cd850eca0a7ac46bb1748d7b9cb88aa3bd21c57d852c28198ad8fa422c4595032e88a4494b4778b36b944fe47a52b8c5cd312910139dfcb4147ab8e972cc456bcb063f25dd78f54c4d34679e03142c42c662af52947d45bdb6e555751334ace76a5080ab5a0256a1d259855dfc5c0b8023b25befbb13fd3684f9f755cbd3d63544c78ee2001452dd54633a7593ade0b183891a0a4e9c7844e1254005fbe592b1b89149a502c24b6e1dca44c158aebedf01beae9c30cabe16a", | 
					
						
							|  |  |  | 			"0x14abd5c47c0be87b0454596baad2", | 
					
						
							|  |  |  | 			"0xca410605310cdc3bb8d4977ae4f0143df54a724ed873457e2272f39d66e0460e971d9d", | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	for i, tc := range tcs[1:] { | 
					
						
							|  |  |  | 		exp := types.DeriveSha(newFlatList(tc), newEmpty()) | 
					
						
							|  |  |  | 		got := types.DeriveSha(newFlatList(tc), NewStackTrie(nil)) | 
					
						
							|  |  |  | 		if !bytes.Equal(got[:], exp[:]) { | 
					
						
							|  |  |  | 			t.Fatalf("case %d: got %x exp %x", i, got, exp) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2020-11-09 15:08:12 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | // TestUpdateSmallNodes tests a case where the leaves are small (both key and value), | 
					
						
							|  |  |  | // which causes a lot of node-within-node. This case was found via fuzzing. | 
					
						
							|  |  |  | func TestUpdateSmallNodes(t *testing.T) { | 
					
						
							|  |  |  | 	st := NewStackTrie(nil) | 
					
						
							|  |  |  | 	nt, _ := New(common.Hash{}, NewDatabase(memorydb.New())) | 
					
						
							|  |  |  | 	kvs := []struct { | 
					
						
							|  |  |  | 		K string | 
					
						
							|  |  |  | 		V string | 
					
						
							|  |  |  | 	}{ | 
					
						
							|  |  |  | 		{"63303030", "3041"}, // stacktrie.Update | 
					
						
							|  |  |  | 		{"65", "3000"},       // stacktrie.Update | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	for _, kv := range kvs { | 
					
						
							|  |  |  | 		nt.TryUpdate(common.FromHex(kv.K), common.FromHex(kv.V)) | 
					
						
							|  |  |  | 		st.TryUpdate(common.FromHex(kv.K), common.FromHex(kv.V)) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if nt.Hash() != st.Hash() { | 
					
						
							|  |  |  | 		t.Fatalf("error %x != %x", st.Hash(), nt.Hash()) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // TestUpdateVariableKeys contains a case which stacktrie fails: when keys of different | 
					
						
							|  |  |  | // sizes are used, and the second one has the same prefix as the first, then the | 
					
						
							|  |  |  | // stacktrie fails, since it's unable to 'expand' on an already added leaf. | 
					
						
							|  |  |  | // For all practical purposes, this is fine, since keys are fixed-size length | 
					
						
							|  |  |  | // in account and storage tries. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // The test is marked as 'skipped', and exists just to have the behaviour documented. | 
					
						
							|  |  |  | // This case was found via fuzzing. | 
					
						
							|  |  |  | func TestUpdateVariableKeys(t *testing.T) { | 
					
						
							|  |  |  | 	t.SkipNow() | 
					
						
							|  |  |  | 	st := NewStackTrie(nil) | 
					
						
							|  |  |  | 	nt, _ := New(common.Hash{}, NewDatabase(memorydb.New())) | 
					
						
							|  |  |  | 	kvs := []struct { | 
					
						
							|  |  |  | 		K string | 
					
						
							|  |  |  | 		V string | 
					
						
							|  |  |  | 	}{ | 
					
						
							|  |  |  | 		{"0x33303534636532393561313031676174", "303030"}, | 
					
						
							|  |  |  | 		{"0x3330353463653239356131303167617430", "313131"}, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	for _, kv := range kvs { | 
					
						
							|  |  |  | 		nt.TryUpdate(common.FromHex(kv.K), common.FromHex(kv.V)) | 
					
						
							|  |  |  | 		st.TryUpdate(common.FromHex(kv.K), common.FromHex(kv.V)) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if nt.Hash() != st.Hash() { | 
					
						
							|  |  |  | 		t.Fatalf("error %x != %x", st.Hash(), nt.Hash()) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } |