core/types: use stacktrie for derivesha trie: add stacktrie file trie: fix linter core/types: use stacktrie for derivesha rebased: adapt stacktrie to the newer version of DeriveSha Co-authored-by: Martin Holst Swende <martin@swende.se> More linter fixes review feedback: no key offset for nodes converted to hashes trie: use EncodeRLP for full nodes core/types: insert txs in order in derivesha trie: tests for derivesha with stacktrie trie: make stacktrie use pooled hashers trie: make stacktrie reuse tmp slice space trie: minor polishes on stacktrie trie/stacktrie: less rlp dancing core/types: explain the contorsions in DeriveSha ci: fix goimport errors trie: clear mem on subtrie hashing squashme: linter fix stracktrie: use pooling, less allocs (#3) trie: in-place hex prefix, reduce allocs and add rawNode.EncodeRLP Reintroduce the `[]node` method, add the missing `EncodeRLP` implementation for `rawNode` and calculate the hex prefix in place. Co-authored-by: Martin Holst Swende <martin@swende.se> Co-authored-by: Martin Holst Swende <martin@swende.se>
		
			
				
	
	
		
			243 lines
		
	
	
		
			6.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			243 lines
		
	
	
		
			6.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
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)
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 |