| 
									
										
										
										
											2016-04-14 18:18:24 +02:00
										 |  |  | // Copyright 2016 The go-ethereum Authors | 
					
						
							| 
									
										
										
										
											2016-01-20 12:09:24 +02:00
										 |  |  | // 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/>. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | package state | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2016-10-04 12:36:02 +02:00
										 |  |  | 	"bytes" | 
					
						
							|  |  |  | 	"encoding/binary" | 
					
						
							|  |  |  | 	"fmt" | 
					
						
							|  |  |  | 	"math" | 
					
						
							| 
									
										
										
										
											2016-01-20 12:09:24 +02:00
										 |  |  | 	"math/big" | 
					
						
							| 
									
										
										
										
											2016-10-04 12:36:02 +02:00
										 |  |  | 	"math/rand" | 
					
						
							|  |  |  | 	"reflect" | 
					
						
							|  |  |  | 	"strings" | 
					
						
							| 
									
										
										
										
											2019-09-26 17:09:59 +08:00
										 |  |  | 	"sync" | 
					
						
							| 
									
										
										
										
											2016-01-20 12:09:24 +02:00
										 |  |  | 	"testing" | 
					
						
							| 
									
										
										
										
											2016-10-04 12:36:02 +02:00
										 |  |  | 	"testing/quick" | 
					
						
							| 
									
										
										
										
											2016-01-20 12:09:24 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/ethereum/go-ethereum/common" | 
					
						
							| 
									
										
										
										
											2018-09-24 15:57:49 +03:00
										 |  |  | 	"github.com/ethereum/go-ethereum/core/rawdb" | 
					
						
							| 
									
										
										
										
											2017-01-05 14:03:50 +01:00
										 |  |  | 	"github.com/ethereum/go-ethereum/core/types" | 
					
						
							| 
									
										
										
										
											2016-01-20 12:09:24 +02:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Tests that updating a state trie does not leak any database writes prior to | 
					
						
							|  |  |  | // actually committing the state. | 
					
						
							|  |  |  | func TestUpdateLeaks(t *testing.T) { | 
					
						
							|  |  |  | 	// Create an empty state database | 
					
						
							| 
									
										
										
										
											2018-09-24 15:57:49 +03:00
										 |  |  | 	db := rawdb.NewMemoryDatabase() | 
					
						
							| 
									
										
										
										
											2020-02-24 13:26:34 +02:00
										 |  |  | 	state, _ := New(common.Hash{}, NewDatabase(db), nil) | 
					
						
							| 
									
										
										
										
											2016-01-20 12:09:24 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Update it with some accounts | 
					
						
							|  |  |  | 	for i := byte(0); i < 255; i++ { | 
					
						
							| 
									
										
										
										
											2016-10-04 12:36:02 +02:00
										 |  |  | 		addr := common.BytesToAddress([]byte{i}) | 
					
						
							|  |  |  | 		state.AddBalance(addr, big.NewInt(int64(11*i))) | 
					
						
							|  |  |  | 		state.SetNonce(addr, uint64(42*i)) | 
					
						
							| 
									
										
										
										
											2016-01-20 12:09:24 +02:00
										 |  |  | 		if i%2 == 0 { | 
					
						
							| 
									
										
										
										
											2016-10-04 12:36:02 +02:00
										 |  |  | 			state.SetState(addr, common.BytesToHash([]byte{i, i, i}), common.BytesToHash([]byte{i, i, i, i})) | 
					
						
							| 
									
										
										
										
											2016-01-20 12:09:24 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		if i%3 == 0 { | 
					
						
							| 
									
										
										
										
											2016-10-04 12:36:02 +02:00
										 |  |  | 			state.SetCode(addr, []byte{i, i, i, i, i}) | 
					
						
							| 
									
										
										
										
											2016-01-20 12:09:24 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-09-26 17:09:59 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	root := state.IntermediateRoot(false) | 
					
						
							| 
									
										
										
										
											2020-07-13 17:02:54 +08:00
										 |  |  | 	if err := state.Database().TrieDB().Commit(root, false, nil); err != nil { | 
					
						
							| 
									
										
										
										
											2019-09-26 17:09:59 +08:00
										 |  |  | 		t.Errorf("can not commit trie %v to persistent database", root.Hex()) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-20 12:09:24 +02:00
										 |  |  | 	// Ensure that no data was leaked into the database | 
					
						
							| 
									
										
										
										
											2020-04-15 13:08:53 +02:00
										 |  |  | 	it := db.NewIterator(nil, nil) | 
					
						
							| 
									
										
										
										
											2018-09-24 15:57:49 +03:00
										 |  |  | 	for it.Next() { | 
					
						
							|  |  |  | 		t.Errorf("State leaked into database: %x -> %x", it.Key(), it.Value()) | 
					
						
							| 
									
										
										
										
											2016-01-20 12:09:24 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-09-24 15:57:49 +03:00
										 |  |  | 	it.Release() | 
					
						
							| 
									
										
										
										
											2016-01-20 12:09:24 +02:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2016-01-20 16:06:28 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | // Tests that no intermediate state of an object is stored into the database, | 
					
						
							|  |  |  | // only the one right before the commit. | 
					
						
							|  |  |  | func TestIntermediateLeaks(t *testing.T) { | 
					
						
							|  |  |  | 	// Create two state databases, one transitioning to the final state, the other final from the beginning | 
					
						
							| 
									
										
										
										
											2018-09-24 15:57:49 +03:00
										 |  |  | 	transDb := rawdb.NewMemoryDatabase() | 
					
						
							|  |  |  | 	finalDb := rawdb.NewMemoryDatabase() | 
					
						
							| 
									
										
										
										
											2020-02-24 13:26:34 +02:00
										 |  |  | 	transState, _ := New(common.Hash{}, NewDatabase(transDb), nil) | 
					
						
							|  |  |  | 	finalState, _ := New(common.Hash{}, NewDatabase(finalDb), nil) | 
					
						
							| 
									
										
										
										
											2016-01-20 16:06:28 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-04 12:36:02 +02:00
										 |  |  | 	modify := func(state *StateDB, addr common.Address, i, tweak byte) { | 
					
						
							|  |  |  | 		state.SetBalance(addr, big.NewInt(int64(11*i)+int64(tweak))) | 
					
						
							|  |  |  | 		state.SetNonce(addr, uint64(42*i+tweak)) | 
					
						
							| 
									
										
										
										
											2016-01-20 16:06:28 +02:00
										 |  |  | 		if i%2 == 0 { | 
					
						
							| 
									
										
										
										
											2016-10-04 12:36:02 +02:00
										 |  |  | 			state.SetState(addr, common.Hash{i, i, i, 0}, common.Hash{}) | 
					
						
							|  |  |  | 			state.SetState(addr, common.Hash{i, i, i, tweak}, common.Hash{i, i, i, i, tweak}) | 
					
						
							| 
									
										
										
										
											2016-01-20 16:06:28 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		if i%3 == 0 { | 
					
						
							| 
									
										
										
										
											2016-10-04 12:36:02 +02:00
										 |  |  | 			state.SetCode(addr, []byte{i, i, i, i, i, tweak}) | 
					
						
							| 
									
										
										
										
											2016-01-20 16:06:28 +02:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-10-04 12:36:02 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-01-20 16:06:28 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-04 12:36:02 +02:00
										 |  |  | 	// Modify the transient state. | 
					
						
							|  |  |  | 	for i := byte(0); i < 255; i++ { | 
					
						
							| 
									
										
										
										
											2019-07-22 09:30:09 +02:00
										 |  |  | 		modify(transState, common.Address{i}, i, 0) | 
					
						
							| 
									
										
										
										
											2016-10-04 12:36:02 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	// Write modifications to trie. | 
					
						
							| 
									
										
										
										
											2016-10-20 13:36:29 +02:00
										 |  |  | 	transState.IntermediateRoot(false) | 
					
						
							| 
									
										
										
										
											2016-01-20 16:06:28 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-04 12:36:02 +02:00
										 |  |  | 	// Overwrite all the data with new values in the transient database. | 
					
						
							|  |  |  | 	for i := byte(0); i < 255; i++ { | 
					
						
							| 
									
										
										
										
											2019-07-22 09:30:09 +02:00
										 |  |  | 		modify(transState, common.Address{i}, i, 99) | 
					
						
							|  |  |  | 		modify(finalState, common.Address{i}, i, 99) | 
					
						
							| 
									
										
										
										
											2016-01-20 16:06:28 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-10-04 12:36:02 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Commit and cross check the databases. | 
					
						
							| 
									
										
										
										
											2019-09-26 17:09:59 +08:00
										 |  |  | 	transRoot, err := transState.Commit(false) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2016-01-20 16:06:28 +02:00
										 |  |  | 		t.Fatalf("failed to commit transition state: %v", err) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-07-13 17:02:54 +08:00
										 |  |  | 	if err = transState.Database().TrieDB().Commit(transRoot, false, nil); err != nil { | 
					
						
							| 
									
										
										
										
											2019-09-26 17:09:59 +08:00
										 |  |  | 		t.Errorf("can not commit trie %v to persistent database", transRoot.Hex()) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	finalRoot, err := finalState.Commit(false) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2016-01-20 16:06:28 +02:00
										 |  |  | 		t.Fatalf("failed to commit final state: %v", err) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-07-13 17:02:54 +08:00
										 |  |  | 	if err = finalState.Database().TrieDB().Commit(finalRoot, false, nil); err != nil { | 
					
						
							| 
									
										
										
										
											2019-09-26 17:09:59 +08:00
										 |  |  | 		t.Errorf("can not commit trie %v to persistent database", finalRoot.Hex()) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-15 13:08:53 +02:00
										 |  |  | 	it := finalDb.NewIterator(nil, nil) | 
					
						
							| 
									
										
										
										
											2018-09-24 15:57:49 +03:00
										 |  |  | 	for it.Next() { | 
					
						
							| 
									
										
										
										
											2019-09-26 17:09:59 +08:00
										 |  |  | 		key, fvalue := it.Key(), it.Value() | 
					
						
							|  |  |  | 		tvalue, err := transDb.Get(key) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			t.Errorf("entry missing from the transition database: %x -> %x", key, fvalue) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if !bytes.Equal(fvalue, tvalue) { | 
					
						
							|  |  |  | 			t.Errorf("the value associate key %x is mismatch,: %x in transition database ,%x in final database", key, tvalue, fvalue) | 
					
						
							| 
									
										
										
										
											2016-01-20 16:06:28 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-09-24 15:57:49 +03:00
										 |  |  | 	it.Release() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-15 13:08:53 +02:00
										 |  |  | 	it = transDb.NewIterator(nil, nil) | 
					
						
							| 
									
										
										
										
											2018-09-24 15:57:49 +03:00
										 |  |  | 	for it.Next() { | 
					
						
							| 
									
										
										
										
											2019-09-26 17:09:59 +08:00
										 |  |  | 		key, tvalue := it.Key(), it.Value() | 
					
						
							|  |  |  | 		fvalue, err := finalDb.Get(key) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2018-09-24 15:57:49 +03:00
										 |  |  | 			t.Errorf("extra entry in the transition database: %x -> %x", key, it.Value()) | 
					
						
							| 
									
										
										
										
											2016-01-20 16:06:28 +02:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-09-26 17:09:59 +08:00
										 |  |  | 		if !bytes.Equal(fvalue, tvalue) { | 
					
						
							|  |  |  | 			t.Errorf("the value associate key %x is mismatch,: %x in transition database ,%x in final database", key, tvalue, fvalue) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-01-20 16:06:28 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2016-10-04 12:36:02 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-19 08:54:21 +02:00
										 |  |  | // TestCopy tests that copying a StateDB object indeed makes the original and | 
					
						
							| 
									
										
										
										
											2017-11-24 11:02:25 +02:00
										 |  |  | // the copy independent of each other. This test is a regression test against | 
					
						
							|  |  |  | // https://github.com/ethereum/go-ethereum/pull/15549. | 
					
						
							|  |  |  | func TestCopy(t *testing.T) { | 
					
						
							|  |  |  | 	// Create a random state test to copy and modify "independently" | 
					
						
							| 
									
										
										
										
											2020-02-24 13:26:34 +02:00
										 |  |  | 	orig, _ := New(common.Hash{}, NewDatabase(rawdb.NewMemoryDatabase()), nil) | 
					
						
							| 
									
										
										
										
											2017-11-24 11:02:25 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	for i := byte(0); i < 255; i++ { | 
					
						
							|  |  |  | 		obj := orig.GetOrNewStateObject(common.BytesToAddress([]byte{i})) | 
					
						
							|  |  |  | 		obj.AddBalance(big.NewInt(int64(i))) | 
					
						
							|  |  |  | 		orig.updateStateObject(obj) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	orig.Finalise(false) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-26 17:09:59 +08:00
										 |  |  | 	// Copy the state | 
					
						
							| 
									
										
										
										
											2017-11-24 11:02:25 +02:00
										 |  |  | 	copy := orig.Copy() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-26 17:09:59 +08:00
										 |  |  | 	// Copy the copy state | 
					
						
							|  |  |  | 	ccopy := copy.Copy() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// modify all in memory | 
					
						
							| 
									
										
										
										
											2017-11-24 11:02:25 +02:00
										 |  |  | 	for i := byte(0); i < 255; i++ { | 
					
						
							|  |  |  | 		origObj := orig.GetOrNewStateObject(common.BytesToAddress([]byte{i})) | 
					
						
							|  |  |  | 		copyObj := copy.GetOrNewStateObject(common.BytesToAddress([]byte{i})) | 
					
						
							| 
									
										
										
										
											2019-09-26 17:09:59 +08:00
										 |  |  | 		ccopyObj := ccopy.GetOrNewStateObject(common.BytesToAddress([]byte{i})) | 
					
						
							| 
									
										
										
										
											2017-11-24 11:02:25 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		origObj.AddBalance(big.NewInt(2 * int64(i))) | 
					
						
							|  |  |  | 		copyObj.AddBalance(big.NewInt(3 * int64(i))) | 
					
						
							| 
									
										
										
										
											2019-09-26 17:09:59 +08:00
										 |  |  | 		ccopyObj.AddBalance(big.NewInt(4 * int64(i))) | 
					
						
							| 
									
										
										
										
											2017-11-24 11:02:25 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		orig.updateStateObject(origObj) | 
					
						
							|  |  |  | 		copy.updateStateObject(copyObj) | 
					
						
							| 
									
										
										
										
											2019-09-26 17:09:59 +08:00
										 |  |  | 		ccopy.updateStateObject(copyObj) | 
					
						
							| 
									
										
										
										
											2017-11-24 11:02:25 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-26 17:09:59 +08:00
										 |  |  | 	// Finalise the changes on all concurrently | 
					
						
							|  |  |  | 	finalise := func(wg *sync.WaitGroup, db *StateDB) { | 
					
						
							|  |  |  | 		defer wg.Done() | 
					
						
							|  |  |  | 		db.Finalise(true) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	var wg sync.WaitGroup | 
					
						
							|  |  |  | 	wg.Add(3) | 
					
						
							|  |  |  | 	go finalise(&wg, orig) | 
					
						
							|  |  |  | 	go finalise(&wg, copy) | 
					
						
							|  |  |  | 	go finalise(&wg, ccopy) | 
					
						
							|  |  |  | 	wg.Wait() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Verify that the three states have been updated independently | 
					
						
							| 
									
										
										
										
											2017-11-24 11:02:25 +02:00
										 |  |  | 	for i := byte(0); i < 255; i++ { | 
					
						
							|  |  |  | 		origObj := orig.GetOrNewStateObject(common.BytesToAddress([]byte{i})) | 
					
						
							|  |  |  | 		copyObj := copy.GetOrNewStateObject(common.BytesToAddress([]byte{i})) | 
					
						
							| 
									
										
										
										
											2019-09-26 17:09:59 +08:00
										 |  |  | 		ccopyObj := ccopy.GetOrNewStateObject(common.BytesToAddress([]byte{i})) | 
					
						
							| 
									
										
										
										
											2017-11-24 11:02:25 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		if want := big.NewInt(3 * int64(i)); origObj.Balance().Cmp(want) != 0 { | 
					
						
							|  |  |  | 			t.Errorf("orig obj %d: balance mismatch: have %v, want %v", i, origObj.Balance(), want) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if want := big.NewInt(4 * int64(i)); copyObj.Balance().Cmp(want) != 0 { | 
					
						
							|  |  |  | 			t.Errorf("copy obj %d: balance mismatch: have %v, want %v", i, copyObj.Balance(), want) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-09-26 17:09:59 +08:00
										 |  |  | 		if want := big.NewInt(5 * int64(i)); ccopyObj.Balance().Cmp(want) != 0 { | 
					
						
							|  |  |  | 			t.Errorf("copy obj %d: balance mismatch: have %v, want %v", i, ccopyObj.Balance(), want) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2017-11-24 11:02:25 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-04 12:36:02 +02:00
										 |  |  | func TestSnapshotRandom(t *testing.T) { | 
					
						
							|  |  |  | 	config := &quick.Config{MaxCount: 1000} | 
					
						
							|  |  |  | 	err := quick.Check((*snapshotTest).run, config) | 
					
						
							|  |  |  | 	if cerr, ok := err.(*quick.CheckError); ok { | 
					
						
							|  |  |  | 		test := cerr.In[0].(*snapshotTest) | 
					
						
							|  |  |  | 		t.Errorf("%v:\n%s", test.err, test) | 
					
						
							|  |  |  | 	} else if err != nil { | 
					
						
							|  |  |  | 		t.Error(err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // A snapshotTest checks that reverting StateDB snapshots properly undoes all changes | 
					
						
							|  |  |  | // captured by the snapshot. Instances of this test with pseudorandom content are created | 
					
						
							|  |  |  | // by Generate. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // The test works as follows: | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // A new state is created and all actions are applied to it. Several snapshots are taken | 
					
						
							|  |  |  | // in between actions. The test then reverts each snapshot. For each snapshot the actions | 
					
						
							|  |  |  | // leading up to it are replayed on a fresh, empty state. The behaviour of all public | 
					
						
							|  |  |  | // accessor methods on the reverted state must match the return value of the equivalent | 
					
						
							|  |  |  | // methods on the replayed state. | 
					
						
							|  |  |  | type snapshotTest struct { | 
					
						
							|  |  |  | 	addrs     []common.Address // all account addresses | 
					
						
							|  |  |  | 	actions   []testAction     // modifications to the state | 
					
						
							|  |  |  | 	snapshots []int            // actions indexes at which snapshot is taken | 
					
						
							|  |  |  | 	err       error            // failure details are reported through this field | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type testAction struct { | 
					
						
							|  |  |  | 	name   string | 
					
						
							|  |  |  | 	fn     func(testAction, *StateDB) | 
					
						
							|  |  |  | 	args   []int64 | 
					
						
							|  |  |  | 	noAddr bool | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // newTestAction creates a random action that changes state. | 
					
						
							|  |  |  | func newTestAction(addr common.Address, r *rand.Rand) testAction { | 
					
						
							|  |  |  | 	actions := []testAction{ | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			name: "SetBalance", | 
					
						
							|  |  |  | 			fn: func(a testAction, s *StateDB) { | 
					
						
							|  |  |  | 				s.SetBalance(addr, big.NewInt(a.args[0])) | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			args: make([]int64, 1), | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			name: "AddBalance", | 
					
						
							|  |  |  | 			fn: func(a testAction, s *StateDB) { | 
					
						
							|  |  |  | 				s.AddBalance(addr, big.NewInt(a.args[0])) | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			args: make([]int64, 1), | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			name: "SetNonce", | 
					
						
							|  |  |  | 			fn: func(a testAction, s *StateDB) { | 
					
						
							|  |  |  | 				s.SetNonce(addr, uint64(a.args[0])) | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			args: make([]int64, 1), | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			name: "SetState", | 
					
						
							|  |  |  | 			fn: func(a testAction, s *StateDB) { | 
					
						
							|  |  |  | 				var key, val common.Hash | 
					
						
							|  |  |  | 				binary.BigEndian.PutUint16(key[:], uint16(a.args[0])) | 
					
						
							|  |  |  | 				binary.BigEndian.PutUint16(val[:], uint16(a.args[1])) | 
					
						
							|  |  |  | 				s.SetState(addr, key, val) | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			args: make([]int64, 2), | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			name: "SetCode", | 
					
						
							|  |  |  | 			fn: func(a testAction, s *StateDB) { | 
					
						
							|  |  |  | 				code := make([]byte, 16) | 
					
						
							|  |  |  | 				binary.BigEndian.PutUint64(code, uint64(a.args[0])) | 
					
						
							|  |  |  | 				binary.BigEndian.PutUint64(code[8:], uint64(a.args[1])) | 
					
						
							|  |  |  | 				s.SetCode(addr, code) | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			args: make([]int64, 2), | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			name: "CreateAccount", | 
					
						
							|  |  |  | 			fn: func(a testAction, s *StateDB) { | 
					
						
							|  |  |  | 				s.CreateAccount(addr) | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							| 
									
										
										
										
											2016-10-05 22:22:31 +02:00
										 |  |  | 			name: "Suicide", | 
					
						
							| 
									
										
										
										
											2016-10-04 12:36:02 +02:00
										 |  |  | 			fn: func(a testAction, s *StateDB) { | 
					
						
							| 
									
										
										
										
											2016-10-05 22:22:31 +02:00
										 |  |  | 				s.Suicide(addr) | 
					
						
							| 
									
										
										
										
											2016-10-04 12:36:02 +02:00
										 |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			name: "AddRefund", | 
					
						
							|  |  |  | 			fn: func(a testAction, s *StateDB) { | 
					
						
							| 
									
										
										
										
											2017-11-13 13:47:27 +02:00
										 |  |  | 				s.AddRefund(uint64(a.args[0])) | 
					
						
							| 
									
										
										
										
											2016-10-04 12:36:02 +02:00
										 |  |  | 			}, | 
					
						
							|  |  |  | 			args:   make([]int64, 1), | 
					
						
							|  |  |  | 			noAddr: true, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			name: "AddLog", | 
					
						
							|  |  |  | 			fn: func(a testAction, s *StateDB) { | 
					
						
							|  |  |  | 				data := make([]byte, 2) | 
					
						
							|  |  |  | 				binary.BigEndian.PutUint16(data, uint16(a.args[0])) | 
					
						
							| 
									
										
										
										
											2017-01-05 14:03:50 +01:00
										 |  |  | 				s.AddLog(&types.Log{Address: addr, Data: data}) | 
					
						
							| 
									
										
										
										
											2016-10-04 12:36:02 +02:00
										 |  |  | 			}, | 
					
						
							|  |  |  | 			args: make([]int64, 1), | 
					
						
							|  |  |  | 		}, | 
					
						
							| 
									
										
										
										
											2019-02-07 18:44:45 +09:00
										 |  |  | 		{ | 
					
						
							|  |  |  | 			name: "AddPreimage", | 
					
						
							|  |  |  | 			fn: func(a testAction, s *StateDB) { | 
					
						
							|  |  |  | 				preimage := []byte{1} | 
					
						
							|  |  |  | 				hash := common.BytesToHash(preimage) | 
					
						
							|  |  |  | 				s.AddPreimage(hash, preimage) | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			args: make([]int64, 1), | 
					
						
							|  |  |  | 		}, | 
					
						
							| 
									
										
										
										
											2020-10-23 08:26:57 +02:00
										 |  |  | 		{ | 
					
						
							|  |  |  | 			name: "AddAddressToAccessList", | 
					
						
							|  |  |  | 			fn: func(a testAction, s *StateDB) { | 
					
						
							|  |  |  | 				s.AddAddressToAccessList(addr) | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			name: "AddSlotToAccessList", | 
					
						
							|  |  |  | 			fn: func(a testAction, s *StateDB) { | 
					
						
							|  |  |  | 				s.AddSlotToAccessList(addr, | 
					
						
							|  |  |  | 					common.Hash{byte(a.args[0])}) | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			args: make([]int64, 1), | 
					
						
							|  |  |  | 		}, | 
					
						
							| 
									
										
										
										
											2016-10-04 12:36:02 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	action := actions[r.Intn(len(actions))] | 
					
						
							|  |  |  | 	var nameargs []string | 
					
						
							|  |  |  | 	if !action.noAddr { | 
					
						
							|  |  |  | 		nameargs = append(nameargs, addr.Hex()) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-07-09 16:32:28 +08:00
										 |  |  | 	for i := range action.args { | 
					
						
							| 
									
										
										
										
											2016-10-04 12:36:02 +02:00
										 |  |  | 		action.args[i] = rand.Int63n(100) | 
					
						
							|  |  |  | 		nameargs = append(nameargs, fmt.Sprint(action.args[i])) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	action.name += strings.Join(nameargs, ", ") | 
					
						
							|  |  |  | 	return action | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Generate returns a new snapshot test of the given size. All randomness is | 
					
						
							|  |  |  | // derived from r. | 
					
						
							|  |  |  | func (*snapshotTest) Generate(r *rand.Rand, size int) reflect.Value { | 
					
						
							|  |  |  | 	// Generate random actions. | 
					
						
							|  |  |  | 	addrs := make([]common.Address, 50) | 
					
						
							|  |  |  | 	for i := range addrs { | 
					
						
							|  |  |  | 		addrs[i][0] = byte(i) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	actions := make([]testAction, size) | 
					
						
							|  |  |  | 	for i := range actions { | 
					
						
							|  |  |  | 		addr := addrs[r.Intn(len(addrs))] | 
					
						
							|  |  |  | 		actions[i] = newTestAction(addr, r) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// Generate snapshot indexes. | 
					
						
							|  |  |  | 	nsnapshots := int(math.Sqrt(float64(size))) | 
					
						
							|  |  |  | 	if size > 0 && nsnapshots == 0 { | 
					
						
							|  |  |  | 		nsnapshots = 1 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	snapshots := make([]int, nsnapshots) | 
					
						
							|  |  |  | 	snaplen := len(actions) / nsnapshots | 
					
						
							|  |  |  | 	for i := range snapshots { | 
					
						
							|  |  |  | 		// Try to place the snapshots some number of actions apart from each other. | 
					
						
							|  |  |  | 		snapshots[i] = (i * snaplen) + r.Intn(snaplen) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return reflect.ValueOf(&snapshotTest{addrs, actions, snapshots, nil}) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (test *snapshotTest) String() string { | 
					
						
							|  |  |  | 	out := new(bytes.Buffer) | 
					
						
							|  |  |  | 	sindex := 0 | 
					
						
							|  |  |  | 	for i, action := range test.actions { | 
					
						
							|  |  |  | 		if len(test.snapshots) > sindex && i == test.snapshots[sindex] { | 
					
						
							|  |  |  | 			fmt.Fprintf(out, "---- snapshot %d ----\n", sindex) | 
					
						
							|  |  |  | 			sindex++ | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		fmt.Fprintf(out, "%4d: %s\n", i, action.name) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return out.String() | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (test *snapshotTest) run() bool { | 
					
						
							|  |  |  | 	// Run all actions and create snapshots. | 
					
						
							|  |  |  | 	var ( | 
					
						
							| 
									
										
										
										
											2020-02-24 13:26:34 +02:00
										 |  |  | 		state, _     = New(common.Hash{}, NewDatabase(rawdb.NewMemoryDatabase()), nil) | 
					
						
							| 
									
										
										
										
											2016-10-04 12:36:02 +02:00
										 |  |  | 		snapshotRevs = make([]int, len(test.snapshots)) | 
					
						
							|  |  |  | 		sindex       = 0 | 
					
						
							|  |  |  | 	) | 
					
						
							|  |  |  | 	for i, action := range test.actions { | 
					
						
							|  |  |  | 		if len(test.snapshots) > sindex && i == test.snapshots[sindex] { | 
					
						
							|  |  |  | 			snapshotRevs[sindex] = state.Snapshot() | 
					
						
							|  |  |  | 			sindex++ | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		action.fn(action, state) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// Revert all snapshots in reverse order. Each revert must yield a state | 
					
						
							|  |  |  | 	// that is equivalent to fresh state with all actions up the snapshot applied. | 
					
						
							|  |  |  | 	for sindex--; sindex >= 0; sindex-- { | 
					
						
							| 
									
										
										
										
											2020-02-24 13:26:34 +02:00
										 |  |  | 		checkstate, _ := New(common.Hash{}, state.Database(), nil) | 
					
						
							| 
									
										
										
										
											2016-10-04 12:36:02 +02:00
										 |  |  | 		for _, action := range test.actions[:test.snapshots[sindex]] { | 
					
						
							|  |  |  | 			action.fn(action, checkstate) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		state.RevertToSnapshot(snapshotRevs[sindex]) | 
					
						
							|  |  |  | 		if err := test.checkEqual(state, checkstate); err != nil { | 
					
						
							|  |  |  | 			test.err = fmt.Errorf("state mismatch after revert to snapshot %d\n%v", sindex, err) | 
					
						
							|  |  |  | 			return false | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return true | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // checkEqual checks that methods of state and checkstate return the same values. | 
					
						
							|  |  |  | func (test *snapshotTest) checkEqual(state, checkstate *StateDB) error { | 
					
						
							|  |  |  | 	for _, addr := range test.addrs { | 
					
						
							|  |  |  | 		var err error | 
					
						
							|  |  |  | 		checkeq := func(op string, a, b interface{}) bool { | 
					
						
							|  |  |  | 			if err == nil && !reflect.DeepEqual(a, b) { | 
					
						
							|  |  |  | 				err = fmt.Errorf("got %s(%s) == %v, want %v", op, addr.Hex(), a, b) | 
					
						
							|  |  |  | 				return false | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			return true | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		// Check basic accessor methods. | 
					
						
							|  |  |  | 		checkeq("Exist", state.Exist(addr), checkstate.Exist(addr)) | 
					
						
							| 
									
										
										
										
											2016-10-05 22:22:31 +02:00
										 |  |  | 		checkeq("HasSuicided", state.HasSuicided(addr), checkstate.HasSuicided(addr)) | 
					
						
							| 
									
										
										
										
											2016-10-04 12:36:02 +02:00
										 |  |  | 		checkeq("GetBalance", state.GetBalance(addr), checkstate.GetBalance(addr)) | 
					
						
							|  |  |  | 		checkeq("GetNonce", state.GetNonce(addr), checkstate.GetNonce(addr)) | 
					
						
							|  |  |  | 		checkeq("GetCode", state.GetCode(addr), checkstate.GetCode(addr)) | 
					
						
							|  |  |  | 		checkeq("GetCodeHash", state.GetCodeHash(addr), checkstate.GetCodeHash(addr)) | 
					
						
							|  |  |  | 		checkeq("GetCodeSize", state.GetCodeSize(addr), checkstate.GetCodeSize(addr)) | 
					
						
							|  |  |  | 		// Check storage. | 
					
						
							| 
									
										
										
										
											2017-02-22 23:29:59 +01:00
										 |  |  | 		if obj := state.getStateObject(addr); obj != nil { | 
					
						
							| 
									
										
										
										
											2018-09-18 16:24:35 +03:00
										 |  |  | 			state.ForEachStorage(addr, func(key, value common.Hash) bool { | 
					
						
							|  |  |  | 				return checkeq("GetState("+key.Hex()+")", checkstate.GetState(addr, key), value) | 
					
						
							| 
									
										
										
										
											2016-10-04 12:36:02 +02:00
										 |  |  | 			}) | 
					
						
							| 
									
										
										
										
											2018-09-18 16:24:35 +03:00
										 |  |  | 			checkstate.ForEachStorage(addr, func(key, value common.Hash) bool { | 
					
						
							|  |  |  | 				return checkeq("GetState("+key.Hex()+")", checkstate.GetState(addr, key), value) | 
					
						
							| 
									
										
										
										
											2016-10-04 12:36:02 +02:00
										 |  |  | 			}) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-13 13:47:27 +02:00
										 |  |  | 	if state.GetRefund() != checkstate.GetRefund() { | 
					
						
							| 
									
										
										
										
											2016-10-04 12:36:02 +02:00
										 |  |  | 		return fmt.Errorf("got GetRefund() == %d, want GetRefund() == %d", | 
					
						
							|  |  |  | 			state.GetRefund(), checkstate.GetRefund()) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if !reflect.DeepEqual(state.GetLogs(common.Hash{}), checkstate.GetLogs(common.Hash{})) { | 
					
						
							|  |  |  | 		return fmt.Errorf("got GetLogs(common.Hash{}) == %v, want GetLogs(common.Hash{}) == %v", | 
					
						
							|  |  |  | 			state.GetLogs(common.Hash{}), checkstate.GetLogs(common.Hash{})) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2016-11-24 16:24:04 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-22 15:56:05 +01:00
										 |  |  | func TestTouchDelete(t *testing.T) { | 
					
						
							|  |  |  | 	s := newStateTest() | 
					
						
							| 
									
										
										
										
											2017-06-27 15:57:06 +02:00
										 |  |  | 	s.state.GetOrNewStateObject(common.Address{}) | 
					
						
							| 
									
										
										
										
											2018-02-05 18:40:32 +02:00
										 |  |  | 	root, _ := s.state.Commit(false) | 
					
						
							| 
									
										
										
										
											2017-06-27 15:57:06 +02:00
										 |  |  | 	s.state.Reset(root) | 
					
						
							| 
									
										
										
										
											2016-11-24 16:24:04 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-27 15:57:06 +02:00
										 |  |  | 	snapshot := s.state.Snapshot() | 
					
						
							|  |  |  | 	s.state.AddBalance(common.Address{}, new(big.Int)) | 
					
						
							| 
									
										
										
										
											2017-10-01 21:07:30 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-27 15:13:30 +03:00
										 |  |  | 	if len(s.state.journal.dirties) != 1 { | 
					
						
							| 
									
										
										
										
											2019-11-22 15:56:05 +01:00
										 |  |  | 		t.Fatal("expected one dirty state object") | 
					
						
							| 
									
										
										
										
											2016-11-24 16:24:04 +01:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-06-27 15:57:06 +02:00
										 |  |  | 	s.state.RevertToSnapshot(snapshot) | 
					
						
							| 
									
										
										
										
											2018-03-27 15:13:30 +03:00
										 |  |  | 	if len(s.state.journal.dirties) != 0 { | 
					
						
							| 
									
										
										
										
											2019-11-22 15:56:05 +01:00
										 |  |  | 		t.Fatal("expected no dirty state object") | 
					
						
							| 
									
										
										
										
											2016-11-24 16:24:04 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2018-04-10 20:59:07 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | // TestCopyOfCopy tests that modified objects are carried over to the copy, and the copy of the copy. | 
					
						
							|  |  |  | // See https://github.com/ethereum/go-ethereum/pull/15225#issuecomment-380191512 | 
					
						
							|  |  |  | func TestCopyOfCopy(t *testing.T) { | 
					
						
							| 
									
										
										
										
											2020-02-24 13:26:34 +02:00
										 |  |  | 	state, _ := New(common.Hash{}, NewDatabase(rawdb.NewMemoryDatabase()), nil) | 
					
						
							| 
									
										
										
										
											2018-04-10 20:59:07 +02:00
										 |  |  | 	addr := common.HexToAddress("aaaa") | 
					
						
							| 
									
										
										
										
											2019-09-24 10:49:59 +03:00
										 |  |  | 	state.SetBalance(addr, big.NewInt(42)) | 
					
						
							| 
									
										
										
										
											2018-04-10 20:59:07 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-24 10:49:59 +03:00
										 |  |  | 	if got := state.Copy().GetBalance(addr).Uint64(); got != 42 { | 
					
						
							| 
									
										
										
										
											2018-04-10 20:59:07 +02:00
										 |  |  | 		t.Fatalf("1st copy fail, expected 42, got %v", got) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-09-24 10:49:59 +03:00
										 |  |  | 	if got := state.Copy().Copy().GetBalance(addr).Uint64(); got != 42 { | 
					
						
							| 
									
										
										
										
											2018-04-10 20:59:07 +02:00
										 |  |  | 		t.Fatalf("2nd copy fail, expected 42, got %v", got) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2019-08-12 22:56:07 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-24 10:49:59 +03:00
										 |  |  | // Tests a regression where committing a copy lost some internal meta information, | 
					
						
							|  |  |  | // leading to corrupted subsequent copies. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // See https://github.com/ethereum/go-ethereum/issues/20106. | 
					
						
							|  |  |  | func TestCopyCommitCopy(t *testing.T) { | 
					
						
							| 
									
										
										
										
											2020-02-24 13:26:34 +02:00
										 |  |  | 	state, _ := New(common.Hash{}, NewDatabase(rawdb.NewMemoryDatabase()), nil) | 
					
						
							| 
									
										
										
										
											2019-09-24 10:49:59 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Create an account and check if the retrieved balance is correct | 
					
						
							|  |  |  | 	addr := common.HexToAddress("0xaffeaffeaffeaffeaffeaffeaffeaffeaffeaffe") | 
					
						
							|  |  |  | 	skey := common.HexToHash("aaa") | 
					
						
							|  |  |  | 	sval := common.HexToHash("bbb") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	state.SetBalance(addr, big.NewInt(42)) // Change the account trie | 
					
						
							|  |  |  | 	state.SetCode(addr, []byte("hello"))   // Change an external metadata | 
					
						
							|  |  |  | 	state.SetState(addr, skey, sval)       // Change the storage trie | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if balance := state.GetBalance(addr); balance.Cmp(big.NewInt(42)) != 0 { | 
					
						
							|  |  |  | 		t.Fatalf("initial balance mismatch: have %v, want %v", balance, 42) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if code := state.GetCode(addr); !bytes.Equal(code, []byte("hello")) { | 
					
						
							|  |  |  | 		t.Fatalf("initial code mismatch: have %x, want %x", code, []byte("hello")) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if val := state.GetState(addr, skey); val != sval { | 
					
						
							|  |  |  | 		t.Fatalf("initial non-committed storage slot mismatch: have %x, want %x", val, sval) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if val := state.GetCommittedState(addr, skey); val != (common.Hash{}) { | 
					
						
							|  |  |  | 		t.Fatalf("initial committed storage slot mismatch: have %x, want %x", val, common.Hash{}) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// Copy the non-committed state database and check pre/post commit balance | 
					
						
							|  |  |  | 	copyOne := state.Copy() | 
					
						
							|  |  |  | 	if balance := copyOne.GetBalance(addr); balance.Cmp(big.NewInt(42)) != 0 { | 
					
						
							|  |  |  | 		t.Fatalf("first copy pre-commit balance mismatch: have %v, want %v", balance, 42) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if code := copyOne.GetCode(addr); !bytes.Equal(code, []byte("hello")) { | 
					
						
							|  |  |  | 		t.Fatalf("first copy pre-commit code mismatch: have %x, want %x", code, []byte("hello")) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if val := copyOne.GetState(addr, skey); val != sval { | 
					
						
							|  |  |  | 		t.Fatalf("first copy pre-commit non-committed storage slot mismatch: have %x, want %x", val, sval) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if val := copyOne.GetCommittedState(addr, skey); val != (common.Hash{}) { | 
					
						
							|  |  |  | 		t.Fatalf("first copy pre-commit committed storage slot mismatch: have %x, want %x", val, common.Hash{}) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	copyOne.Commit(false) | 
					
						
							|  |  |  | 	if balance := copyOne.GetBalance(addr); balance.Cmp(big.NewInt(42)) != 0 { | 
					
						
							|  |  |  | 		t.Fatalf("first copy post-commit balance mismatch: have %v, want %v", balance, 42) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if code := copyOne.GetCode(addr); !bytes.Equal(code, []byte("hello")) { | 
					
						
							|  |  |  | 		t.Fatalf("first copy post-commit code mismatch: have %x, want %x", code, []byte("hello")) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if val := copyOne.GetState(addr, skey); val != sval { | 
					
						
							|  |  |  | 		t.Fatalf("first copy post-commit non-committed storage slot mismatch: have %x, want %x", val, sval) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if val := copyOne.GetCommittedState(addr, skey); val != sval { | 
					
						
							|  |  |  | 		t.Fatalf("first copy post-commit committed storage slot mismatch: have %x, want %x", val, sval) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// Copy the copy and check the balance once more | 
					
						
							|  |  |  | 	copyTwo := copyOne.Copy() | 
					
						
							|  |  |  | 	if balance := copyTwo.GetBalance(addr); balance.Cmp(big.NewInt(42)) != 0 { | 
					
						
							|  |  |  | 		t.Fatalf("second copy balance mismatch: have %v, want %v", balance, 42) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if code := copyTwo.GetCode(addr); !bytes.Equal(code, []byte("hello")) { | 
					
						
							|  |  |  | 		t.Fatalf("second copy code mismatch: have %x, want %x", code, []byte("hello")) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if val := copyTwo.GetState(addr, skey); val != sval { | 
					
						
							|  |  |  | 		t.Fatalf("second copy non-committed storage slot mismatch: have %x, want %x", val, sval) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if val := copyTwo.GetCommittedState(addr, skey); val != sval { | 
					
						
							|  |  |  | 		t.Fatalf("second copy post-commit committed storage slot mismatch: have %x, want %x", val, sval) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Tests a regression where committing a copy lost some internal meta information, | 
					
						
							|  |  |  | // leading to corrupted subsequent copies. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // See https://github.com/ethereum/go-ethereum/issues/20106. | 
					
						
							|  |  |  | func TestCopyCopyCommitCopy(t *testing.T) { | 
					
						
							| 
									
										
										
										
											2020-02-24 13:26:34 +02:00
										 |  |  | 	state, _ := New(common.Hash{}, NewDatabase(rawdb.NewMemoryDatabase()), nil) | 
					
						
							| 
									
										
										
										
											2019-09-24 10:49:59 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Create an account and check if the retrieved balance is correct | 
					
						
							|  |  |  | 	addr := common.HexToAddress("0xaffeaffeaffeaffeaffeaffeaffeaffeaffeaffe") | 
					
						
							|  |  |  | 	skey := common.HexToHash("aaa") | 
					
						
							|  |  |  | 	sval := common.HexToHash("bbb") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	state.SetBalance(addr, big.NewInt(42)) // Change the account trie | 
					
						
							|  |  |  | 	state.SetCode(addr, []byte("hello"))   // Change an external metadata | 
					
						
							|  |  |  | 	state.SetState(addr, skey, sval)       // Change the storage trie | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if balance := state.GetBalance(addr); balance.Cmp(big.NewInt(42)) != 0 { | 
					
						
							|  |  |  | 		t.Fatalf("initial balance mismatch: have %v, want %v", balance, 42) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if code := state.GetCode(addr); !bytes.Equal(code, []byte("hello")) { | 
					
						
							|  |  |  | 		t.Fatalf("initial code mismatch: have %x, want %x", code, []byte("hello")) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if val := state.GetState(addr, skey); val != sval { | 
					
						
							|  |  |  | 		t.Fatalf("initial non-committed storage slot mismatch: have %x, want %x", val, sval) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if val := state.GetCommittedState(addr, skey); val != (common.Hash{}) { | 
					
						
							|  |  |  | 		t.Fatalf("initial committed storage slot mismatch: have %x, want %x", val, common.Hash{}) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// Copy the non-committed state database and check pre/post commit balance | 
					
						
							|  |  |  | 	copyOne := state.Copy() | 
					
						
							|  |  |  | 	if balance := copyOne.GetBalance(addr); balance.Cmp(big.NewInt(42)) != 0 { | 
					
						
							|  |  |  | 		t.Fatalf("first copy balance mismatch: have %v, want %v", balance, 42) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if code := copyOne.GetCode(addr); !bytes.Equal(code, []byte("hello")) { | 
					
						
							|  |  |  | 		t.Fatalf("first copy code mismatch: have %x, want %x", code, []byte("hello")) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if val := copyOne.GetState(addr, skey); val != sval { | 
					
						
							|  |  |  | 		t.Fatalf("first copy non-committed storage slot mismatch: have %x, want %x", val, sval) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if val := copyOne.GetCommittedState(addr, skey); val != (common.Hash{}) { | 
					
						
							|  |  |  | 		t.Fatalf("first copy committed storage slot mismatch: have %x, want %x", val, common.Hash{}) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// Copy the copy and check the balance once more | 
					
						
							|  |  |  | 	copyTwo := copyOne.Copy() | 
					
						
							|  |  |  | 	if balance := copyTwo.GetBalance(addr); balance.Cmp(big.NewInt(42)) != 0 { | 
					
						
							|  |  |  | 		t.Fatalf("second copy pre-commit balance mismatch: have %v, want %v", balance, 42) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if code := copyTwo.GetCode(addr); !bytes.Equal(code, []byte("hello")) { | 
					
						
							|  |  |  | 		t.Fatalf("second copy pre-commit code mismatch: have %x, want %x", code, []byte("hello")) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if val := copyTwo.GetState(addr, skey); val != sval { | 
					
						
							|  |  |  | 		t.Fatalf("second copy pre-commit non-committed storage slot mismatch: have %x, want %x", val, sval) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if val := copyTwo.GetCommittedState(addr, skey); val != (common.Hash{}) { | 
					
						
							|  |  |  | 		t.Fatalf("second copy pre-commit committed storage slot mismatch: have %x, want %x", val, common.Hash{}) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	copyTwo.Commit(false) | 
					
						
							|  |  |  | 	if balance := copyTwo.GetBalance(addr); balance.Cmp(big.NewInt(42)) != 0 { | 
					
						
							|  |  |  | 		t.Fatalf("second copy post-commit balance mismatch: have %v, want %v", balance, 42) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if code := copyTwo.GetCode(addr); !bytes.Equal(code, []byte("hello")) { | 
					
						
							|  |  |  | 		t.Fatalf("second copy post-commit code mismatch: have %x, want %x", code, []byte("hello")) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if val := copyTwo.GetState(addr, skey); val != sval { | 
					
						
							|  |  |  | 		t.Fatalf("second copy post-commit non-committed storage slot mismatch: have %x, want %x", val, sval) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if val := copyTwo.GetCommittedState(addr, skey); val != sval { | 
					
						
							|  |  |  | 		t.Fatalf("second copy post-commit committed storage slot mismatch: have %x, want %x", val, sval) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// Copy the copy-copy and check the balance once more | 
					
						
							|  |  |  | 	copyThree := copyTwo.Copy() | 
					
						
							|  |  |  | 	if balance := copyThree.GetBalance(addr); balance.Cmp(big.NewInt(42)) != 0 { | 
					
						
							|  |  |  | 		t.Fatalf("third copy balance mismatch: have %v, want %v", balance, 42) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if code := copyThree.GetCode(addr); !bytes.Equal(code, []byte("hello")) { | 
					
						
							|  |  |  | 		t.Fatalf("third copy code mismatch: have %x, want %x", code, []byte("hello")) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if val := copyThree.GetState(addr, skey); val != sval { | 
					
						
							|  |  |  | 		t.Fatalf("third copy non-committed storage slot mismatch: have %x, want %x", val, sval) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if val := copyThree.GetCommittedState(addr, skey); val != sval { | 
					
						
							|  |  |  | 		t.Fatalf("third copy committed storage slot mismatch: have %x, want %x", val, sval) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-12 22:56:07 +03:00
										 |  |  | // TestDeleteCreateRevert tests a weird state transition corner case that we hit | 
					
						
							| 
									
										
										
										
											2020-08-19 08:54:21 +02:00
										 |  |  | // while changing the internals of StateDB. The workflow is that a contract is | 
					
						
							|  |  |  | // self-destructed, then in a follow-up transaction (but same block) it's created | 
					
						
							| 
									
										
										
										
											2019-08-12 22:56:07 +03:00
										 |  |  | // again and the transaction reverted. | 
					
						
							|  |  |  | // | 
					
						
							| 
									
										
										
										
											2020-08-19 08:54:21 +02:00
										 |  |  | // The original StateDB implementation flushed dirty objects to the tries after | 
					
						
							| 
									
										
										
										
											2019-08-12 22:56:07 +03:00
										 |  |  | // each transaction, so this works ok. The rework accumulated writes in memory | 
					
						
							|  |  |  | // first, but the journal wiped the entire state object on create-revert. | 
					
						
							|  |  |  | func TestDeleteCreateRevert(t *testing.T) { | 
					
						
							|  |  |  | 	// Create an initial state with a single contract | 
					
						
							| 
									
										
										
										
											2020-02-24 13:26:34 +02:00
										 |  |  | 	state, _ := New(common.Hash{}, NewDatabase(rawdb.NewMemoryDatabase()), nil) | 
					
						
							| 
									
										
										
										
											2019-08-12 22:56:07 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	addr := toAddr([]byte("so")) | 
					
						
							|  |  |  | 	state.SetBalance(addr, big.NewInt(1)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	root, _ := state.Commit(false) | 
					
						
							|  |  |  | 	state.Reset(root) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Simulate self-destructing in one transaction, then create-reverting in another | 
					
						
							|  |  |  | 	state.Suicide(addr) | 
					
						
							|  |  |  | 	state.Finalise(true) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	id := state.Snapshot() | 
					
						
							|  |  |  | 	state.SetBalance(addr, big.NewInt(2)) | 
					
						
							|  |  |  | 	state.RevertToSnapshot(id) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Commit the entire state and make sure we don't crash and have the correct state | 
					
						
							|  |  |  | 	root, _ = state.Commit(true) | 
					
						
							|  |  |  | 	state.Reset(root) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if state.getStateObject(addr) != nil { | 
					
						
							|  |  |  | 		t.Fatalf("self-destructed contract came alive") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2020-05-07 15:13:34 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-19 08:54:21 +02:00
										 |  |  | // TestMissingTrieNodes tests that if the StateDB fails to load parts of the trie, | 
					
						
							| 
									
										
										
										
											2020-05-07 15:13:34 +02:00
										 |  |  | // the Commit operation fails with an error | 
					
						
							|  |  |  | // If we are missing trie nodes, we should not continue writing to the trie | 
					
						
							|  |  |  | func TestMissingTrieNodes(t *testing.T) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Create an initial state with a few accounts | 
					
						
							|  |  |  | 	memDb := rawdb.NewMemoryDatabase() | 
					
						
							|  |  |  | 	db := NewDatabase(memDb) | 
					
						
							|  |  |  | 	var root common.Hash | 
					
						
							|  |  |  | 	state, _ := New(common.Hash{}, db, nil) | 
					
						
							|  |  |  | 	addr := toAddr([]byte("so")) | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		state.SetBalance(addr, big.NewInt(1)) | 
					
						
							|  |  |  | 		state.SetCode(addr, []byte{1, 2, 3}) | 
					
						
							|  |  |  | 		a2 := toAddr([]byte("another")) | 
					
						
							|  |  |  | 		state.SetBalance(a2, big.NewInt(100)) | 
					
						
							|  |  |  | 		state.SetCode(a2, []byte{1, 2, 4}) | 
					
						
							|  |  |  | 		root, _ = state.Commit(false) | 
					
						
							|  |  |  | 		t.Logf("root: %x", root) | 
					
						
							|  |  |  | 		// force-flush | 
					
						
							|  |  |  | 		state.Database().TrieDB().Cap(0) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// Create a new state on the old root | 
					
						
							|  |  |  | 	state, _ = New(root, db, nil) | 
					
						
							|  |  |  | 	// Now we clear out the memdb | 
					
						
							|  |  |  | 	it := memDb.NewIterator(nil, nil) | 
					
						
							|  |  |  | 	for it.Next() { | 
					
						
							|  |  |  | 		k := it.Key() | 
					
						
							|  |  |  | 		// Leave the root intact | 
					
						
							|  |  |  | 		if !bytes.Equal(k, root[:]) { | 
					
						
							|  |  |  | 			t.Logf("key: %x", k) | 
					
						
							|  |  |  | 			memDb.Delete(k) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	balance := state.GetBalance(addr) | 
					
						
							|  |  |  | 	// The removed elem should lead to it returning zero balance | 
					
						
							|  |  |  | 	if exp, got := uint64(0), balance.Uint64(); got != exp { | 
					
						
							|  |  |  | 		t.Errorf("expected %d, got %d", exp, got) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// Modify the state | 
					
						
							|  |  |  | 	state.SetBalance(addr, big.NewInt(2)) | 
					
						
							|  |  |  | 	root, err := state.Commit(false) | 
					
						
							|  |  |  | 	if err == nil { | 
					
						
							|  |  |  | 		t.Fatalf("expected error, got root :%x", root) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2020-10-23 08:26:57 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | func TestStateDBAccessList(t *testing.T) { | 
					
						
							|  |  |  | 	// Some helpers | 
					
						
							|  |  |  | 	addr := func(a string) common.Address { | 
					
						
							|  |  |  | 		return common.HexToAddress(a) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	slot := func(a string) common.Hash { | 
					
						
							|  |  |  | 		return common.HexToHash(a) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	memDb := rawdb.NewMemoryDatabase() | 
					
						
							|  |  |  | 	db := NewDatabase(memDb) | 
					
						
							|  |  |  | 	state, _ := New(common.Hash{}, db, nil) | 
					
						
							|  |  |  | 	state.accessList = newAccessList() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	verifyAddrs := func(astrings ...string) { | 
					
						
							|  |  |  | 		t.Helper() | 
					
						
							|  |  |  | 		// convert to common.Address form | 
					
						
							|  |  |  | 		var addresses []common.Address | 
					
						
							|  |  |  | 		var addressMap = make(map[common.Address]struct{}) | 
					
						
							|  |  |  | 		for _, astring := range astrings { | 
					
						
							|  |  |  | 			address := addr(astring) | 
					
						
							|  |  |  | 			addresses = append(addresses, address) | 
					
						
							|  |  |  | 			addressMap[address] = struct{}{} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		// Check that the given addresses are in the access list | 
					
						
							|  |  |  | 		for _, address := range addresses { | 
					
						
							|  |  |  | 			if !state.AddressInAccessList(address) { | 
					
						
							|  |  |  | 				t.Fatalf("expected %x to be in access list", address) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		// Check that only the expected addresses are present in the acesslist | 
					
						
							|  |  |  | 		for address := range state.accessList.addresses { | 
					
						
							|  |  |  | 			if _, exist := addressMap[address]; !exist { | 
					
						
							|  |  |  | 				t.Fatalf("extra address %x in access list", address) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	verifySlots := func(addrString string, slotStrings ...string) { | 
					
						
							|  |  |  | 		if !state.AddressInAccessList(addr(addrString)) { | 
					
						
							|  |  |  | 			t.Fatalf("scope missing address/slots %v", addrString) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		var address = addr(addrString) | 
					
						
							|  |  |  | 		// convert to common.Hash form | 
					
						
							|  |  |  | 		var slots []common.Hash | 
					
						
							|  |  |  | 		var slotMap = make(map[common.Hash]struct{}) | 
					
						
							|  |  |  | 		for _, slotString := range slotStrings { | 
					
						
							|  |  |  | 			s := slot(slotString) | 
					
						
							|  |  |  | 			slots = append(slots, s) | 
					
						
							|  |  |  | 			slotMap[s] = struct{}{} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		// Check that the expected items are in the access list | 
					
						
							|  |  |  | 		for i, s := range slots { | 
					
						
							|  |  |  | 			if _, slotPresent := state.SlotInAccessList(address, s); !slotPresent { | 
					
						
							|  |  |  | 				t.Fatalf("input %d: scope missing slot %v (address %v)", i, s, addrString) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		// Check that no extra elements are in the access list | 
					
						
							|  |  |  | 		index := state.accessList.addresses[address] | 
					
						
							|  |  |  | 		if index >= 0 { | 
					
						
							|  |  |  | 			stateSlots := state.accessList.slots[index] | 
					
						
							|  |  |  | 			for s := range stateSlots { | 
					
						
							|  |  |  | 				if _, slotPresent := slotMap[s]; !slotPresent { | 
					
						
							|  |  |  | 					t.Fatalf("scope has extra slot %v (address %v)", s, addrString) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	state.AddAddressToAccessList(addr("aa"))          // 1 | 
					
						
							|  |  |  | 	state.AddSlotToAccessList(addr("bb"), slot("01")) // 2,3 | 
					
						
							|  |  |  | 	state.AddSlotToAccessList(addr("bb"), slot("02")) // 4 | 
					
						
							|  |  |  | 	verifyAddrs("aa", "bb") | 
					
						
							|  |  |  | 	verifySlots("bb", "01", "02") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Make a copy | 
					
						
							|  |  |  | 	stateCopy1 := state.Copy() | 
					
						
							|  |  |  | 	if exp, got := 4, state.journal.length(); exp != got { | 
					
						
							|  |  |  | 		t.Fatalf("journal length mismatch: have %d, want %d", got, exp) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// same again, should cause no journal entries | 
					
						
							|  |  |  | 	state.AddSlotToAccessList(addr("bb"), slot("01")) | 
					
						
							|  |  |  | 	state.AddSlotToAccessList(addr("bb"), slot("02")) | 
					
						
							|  |  |  | 	state.AddAddressToAccessList(addr("aa")) | 
					
						
							|  |  |  | 	if exp, got := 4, state.journal.length(); exp != got { | 
					
						
							|  |  |  | 		t.Fatalf("journal length mismatch: have %d, want %d", got, exp) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// some new ones | 
					
						
							|  |  |  | 	state.AddSlotToAccessList(addr("bb"), slot("03")) // 5 | 
					
						
							|  |  |  | 	state.AddSlotToAccessList(addr("aa"), slot("01")) // 6 | 
					
						
							|  |  |  | 	state.AddSlotToAccessList(addr("cc"), slot("01")) // 7,8 | 
					
						
							|  |  |  | 	state.AddAddressToAccessList(addr("cc")) | 
					
						
							|  |  |  | 	if exp, got := 8, state.journal.length(); exp != got { | 
					
						
							|  |  |  | 		t.Fatalf("journal length mismatch: have %d, want %d", got, exp) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	verifyAddrs("aa", "bb", "cc") | 
					
						
							|  |  |  | 	verifySlots("aa", "01") | 
					
						
							|  |  |  | 	verifySlots("bb", "01", "02", "03") | 
					
						
							|  |  |  | 	verifySlots("cc", "01") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// now start rolling back changes | 
					
						
							|  |  |  | 	state.journal.revert(state, 7) | 
					
						
							|  |  |  | 	if _, ok := state.SlotInAccessList(addr("cc"), slot("01")); ok { | 
					
						
							|  |  |  | 		t.Fatalf("slot present, expected missing") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	verifyAddrs("aa", "bb", "cc") | 
					
						
							|  |  |  | 	verifySlots("aa", "01") | 
					
						
							|  |  |  | 	verifySlots("bb", "01", "02", "03") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	state.journal.revert(state, 6) | 
					
						
							|  |  |  | 	if state.AddressInAccessList(addr("cc")) { | 
					
						
							|  |  |  | 		t.Fatalf("addr present, expected missing") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	verifyAddrs("aa", "bb") | 
					
						
							|  |  |  | 	verifySlots("aa", "01") | 
					
						
							|  |  |  | 	verifySlots("bb", "01", "02", "03") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	state.journal.revert(state, 5) | 
					
						
							|  |  |  | 	if _, ok := state.SlotInAccessList(addr("aa"), slot("01")); ok { | 
					
						
							|  |  |  | 		t.Fatalf("slot present, expected missing") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	verifyAddrs("aa", "bb") | 
					
						
							|  |  |  | 	verifySlots("bb", "01", "02", "03") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	state.journal.revert(state, 4) | 
					
						
							|  |  |  | 	if _, ok := state.SlotInAccessList(addr("bb"), slot("03")); ok { | 
					
						
							|  |  |  | 		t.Fatalf("slot present, expected missing") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	verifyAddrs("aa", "bb") | 
					
						
							|  |  |  | 	verifySlots("bb", "01", "02") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	state.journal.revert(state, 3) | 
					
						
							|  |  |  | 	if _, ok := state.SlotInAccessList(addr("bb"), slot("02")); ok { | 
					
						
							|  |  |  | 		t.Fatalf("slot present, expected missing") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	verifyAddrs("aa", "bb") | 
					
						
							|  |  |  | 	verifySlots("bb", "01") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	state.journal.revert(state, 2) | 
					
						
							|  |  |  | 	if _, ok := state.SlotInAccessList(addr("bb"), slot("01")); ok { | 
					
						
							|  |  |  | 		t.Fatalf("slot present, expected missing") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	verifyAddrs("aa", "bb") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	state.journal.revert(state, 1) | 
					
						
							|  |  |  | 	if state.AddressInAccessList(addr("bb")) { | 
					
						
							|  |  |  | 		t.Fatalf("addr present, expected missing") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	verifyAddrs("aa") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	state.journal.revert(state, 0) | 
					
						
							|  |  |  | 	if state.AddressInAccessList(addr("aa")) { | 
					
						
							|  |  |  | 		t.Fatalf("addr present, expected missing") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if got, exp := len(state.accessList.addresses), 0; got != exp { | 
					
						
							|  |  |  | 		t.Fatalf("expected empty, got %d", got) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if got, exp := len(state.accessList.slots), 0; got != exp { | 
					
						
							|  |  |  | 		t.Fatalf("expected empty, got %d", got) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// Check the copy | 
					
						
							|  |  |  | 	// Make a copy | 
					
						
							|  |  |  | 	state = stateCopy1 | 
					
						
							|  |  |  | 	verifyAddrs("aa", "bb") | 
					
						
							|  |  |  | 	verifySlots("bb", "01", "02") | 
					
						
							|  |  |  | 	if got, exp := len(state.accessList.addresses), 2; got != exp { | 
					
						
							|  |  |  | 		t.Fatalf("expected empty, got %d", got) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if got, exp := len(state.accessList.slots), 1; got != exp { | 
					
						
							|  |  |  | 		t.Fatalf("expected empty, got %d", got) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } |