| 
									
										
										
										
											2021-09-07 00:25:54 -04:00
										 |  |  | // Copyright 2021 The go-ethereum Authors | 
					
						
							|  |  |  | // This file is part of the go-ethereum library. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // The go-ethereum library is free software: you can redistribute it and/or modify | 
					
						
							|  |  |  | // it under the terms of the GNU Lesser General Public License as published by | 
					
						
							|  |  |  | // the Free Software Foundation, either version 3 of the License, or | 
					
						
							|  |  |  | // (at your option) any later version. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // The go-ethereum library is distributed in the hope that it will be useful, | 
					
						
							|  |  |  | // but WITHOUT ANY WARRANTY; without even the implied warranty of | 
					
						
							|  |  |  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 
					
						
							|  |  |  | // GNU Lesser General Public License for more details. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // You should have received a copy of the GNU Lesser General Public License | 
					
						
							|  |  |  | // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-29 17:38:13 +02:00
										 |  |  | package trie | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2021-04-16 14:21:01 +02:00
										 |  |  | 	"bytes" | 
					
						
							|  |  |  | 	"math/big" | 
					
						
							| 
									
										
										
										
											2020-09-29 17:38:13 +02:00
										 |  |  | 	"testing" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/ethereum/go-ethereum/common" | 
					
						
							| 
									
										
										
										
											2021-04-16 14:21:01 +02:00
										 |  |  | 	"github.com/ethereum/go-ethereum/crypto" | 
					
						
							| 
									
										
										
										
											2020-09-29 17:38:13 +02:00
										 |  |  | 	"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()) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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()) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2021-04-16 14:21:01 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | // TestStacktrieNotModifyValues checks that inserting blobs of data into the | 
					
						
							|  |  |  | // stacktrie does not mutate the blobs | 
					
						
							|  |  |  | func TestStacktrieNotModifyValues(t *testing.T) { | 
					
						
							|  |  |  | 	st := NewStackTrie(nil) | 
					
						
							|  |  |  | 	{ // Test a very small trie | 
					
						
							|  |  |  | 		// Give it the value as a slice with large backing alloc, | 
					
						
							|  |  |  | 		// so if the stacktrie tries to append, it won't have to realloc | 
					
						
							|  |  |  | 		value := make([]byte, 1, 100) | 
					
						
							|  |  |  | 		value[0] = 0x2 | 
					
						
							|  |  |  | 		want := common.CopyBytes(value) | 
					
						
							|  |  |  | 		st.TryUpdate([]byte{0x01}, value) | 
					
						
							|  |  |  | 		st.Hash() | 
					
						
							|  |  |  | 		if have := value; !bytes.Equal(have, want) { | 
					
						
							|  |  |  | 			t.Fatalf("tiny trie: have %#x want %#x", have, want) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		st = NewStackTrie(nil) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// Test with a larger trie | 
					
						
							|  |  |  | 	keyB := big.NewInt(1) | 
					
						
							|  |  |  | 	keyDelta := big.NewInt(1) | 
					
						
							|  |  |  | 	var vals [][]byte | 
					
						
							|  |  |  | 	getValue := func(i int) []byte { | 
					
						
							|  |  |  | 		if i%2 == 0 { // large | 
					
						
							|  |  |  | 			return crypto.Keccak256(big.NewInt(int64(i)).Bytes()) | 
					
						
							|  |  |  | 		} else { //small | 
					
						
							|  |  |  | 			return big.NewInt(int64(i)).Bytes() | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	for i := 0; i < 1000; i++ { | 
					
						
							|  |  |  | 		key := common.BigToHash(keyB) | 
					
						
							|  |  |  | 		value := getValue(i) | 
					
						
							|  |  |  | 		st.TryUpdate(key.Bytes(), value) | 
					
						
							|  |  |  | 		vals = append(vals, value) | 
					
						
							|  |  |  | 		keyB = keyB.Add(keyB, keyDelta) | 
					
						
							|  |  |  | 		keyDelta.Add(keyDelta, common.Big1) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	st.Hash() | 
					
						
							|  |  |  | 	for i := 0; i < 1000; i++ { | 
					
						
							|  |  |  | 		want := getValue(i) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		have := vals[i] | 
					
						
							|  |  |  | 		if !bytes.Equal(have, want) { | 
					
						
							|  |  |  | 			t.Fatalf("item %d, have %#x want %#x", i, have, want) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2021-04-20 10:42:02 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // TestStacktrieSerialization tests that the stacktrie works well if we | 
					
						
							|  |  |  | // serialize/unserialize it a lot | 
					
						
							|  |  |  | func TestStacktrieSerialization(t *testing.T) { | 
					
						
							|  |  |  | 	var ( | 
					
						
							|  |  |  | 		st       = NewStackTrie(nil) | 
					
						
							|  |  |  | 		nt, _    = New(common.Hash{}, NewDatabase(memorydb.New())) | 
					
						
							|  |  |  | 		keyB     = big.NewInt(1) | 
					
						
							|  |  |  | 		keyDelta = big.NewInt(1) | 
					
						
							|  |  |  | 		vals     [][]byte | 
					
						
							|  |  |  | 		keys     [][]byte | 
					
						
							|  |  |  | 	) | 
					
						
							|  |  |  | 	getValue := func(i int) []byte { | 
					
						
							|  |  |  | 		if i%2 == 0 { // large | 
					
						
							|  |  |  | 			return crypto.Keccak256(big.NewInt(int64(i)).Bytes()) | 
					
						
							|  |  |  | 		} else { //small | 
					
						
							|  |  |  | 			return big.NewInt(int64(i)).Bytes() | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	for i := 0; i < 10; i++ { | 
					
						
							|  |  |  | 		vals = append(vals, getValue(i)) | 
					
						
							|  |  |  | 		keys = append(keys, common.BigToHash(keyB).Bytes()) | 
					
						
							|  |  |  | 		keyB = keyB.Add(keyB, keyDelta) | 
					
						
							|  |  |  | 		keyDelta.Add(keyDelta, common.Big1) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	for i, k := range keys { | 
					
						
							|  |  |  | 		nt.TryUpdate(k, common.CopyBytes(vals[i])) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for i, k := range keys { | 
					
						
							|  |  |  | 		blob, err := st.MarshalBinary() | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			t.Fatal(err) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		newSt, err := NewFromBinary(blob, nil) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			t.Fatal(err) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		st = newSt | 
					
						
							|  |  |  | 		st.TryUpdate(k, common.CopyBytes(vals[i])) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if have, want := st.Hash(), nt.Hash(); have != want { | 
					
						
							|  |  |  | 		t.Fatalf("have %#x want %#x", have, want) | 
					
						
							| 
									
										
										
										
											2021-04-16 14:21:01 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | } |