// Copyright 2014 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 . package state import ( "bytes" "math/big" "testing" checker "gopkg.in/check.v1" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethdb" ) type StateSuite struct { state *StateDB } var _ = checker.Suite(&StateSuite{}) var toAddr = common.BytesToAddress func (s *StateSuite) TestDump(c *checker.C) { // generate a few entries obj1 := s.state.GetOrNewStateObject(toAddr([]byte{0x01})) obj1.AddBalance(big.NewInt(22)) obj2 := s.state.GetOrNewStateObject(toAddr([]byte{0x01, 0x02})) obj2.SetCode(crypto.Keccak256Hash([]byte{3, 3, 3, 3, 3, 3, 3}), []byte{3, 3, 3, 3, 3, 3, 3}) obj3 := s.state.GetOrNewStateObject(toAddr([]byte{0x02})) obj3.SetBalance(big.NewInt(44)) // write some of them to the trie s.state.updateStateObject(obj1) s.state.updateStateObject(obj2) s.state.Commit(false) // check that dump contains the state objects that are in trie got := string(s.state.Dump()) want := `{ "root": "71edff0130dd2385947095001c73d9e28d862fc286fca2b922ca6f6f3cddfdd2", "accounts": { "0000000000000000000000000000000000000001": { "balance": "22", "nonce": 0, "root": "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", "codeHash": "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", "code": "", "storage": {} }, "0000000000000000000000000000000000000002": { "balance": "44", "nonce": 0, "root": "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", "codeHash": "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", "code": "", "storage": {} }, "0000000000000000000000000000000000000102": { "balance": "0", "nonce": 0, "root": "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", "codeHash": "87874902497a5bb968da31a2998d8f22e949d1ef6214bcdedd8bae24cca4b9e3", "code": "03030303030303", "storage": {} } } }` if got != want { c.Errorf("dump mismatch:\ngot: %s\nwant: %s\n", got, want) } } func (s *StateSuite) SetUpTest(c *checker.C) { db, _ := ethdb.NewMemDatabase() s.state, _ = New(common.Hash{}, db) } func TestNull(t *testing.T) { db, _ := ethdb.NewMemDatabase() state, _ := New(common.Hash{}, db) address := common.HexToAddress("0x823140710bf13990e4500136726d8b55") state.CreateAccount(address) //value := common.FromHex("0x823140710bf13990e4500136726d8b55") var value common.Hash state.SetState(address, common.Hash{}, value) state.Commit(false) value = state.GetState(address, common.Hash{}) if !common.EmptyHash(value) { t.Errorf("expected empty hash. got %x", value) } } func (s *StateSuite) TestSnapshot(c *checker.C) { stateobjaddr := toAddr([]byte("aa")) var storageaddr common.Hash data1 := common.BytesToHash([]byte{42}) data2 := common.BytesToHash([]byte{43}) // set initial state object value s.state.SetState(stateobjaddr, storageaddr, data1) // get snapshot of current state snapshot := s.state.Snapshot() // set new state object value s.state.SetState(stateobjaddr, storageaddr, data2) // restore snapshot s.state.RevertToSnapshot(snapshot) // get state storage value res := s.state.GetState(stateobjaddr, storageaddr) c.Assert(data1, checker.DeepEquals, res) } func TestSnapshotEmpty(t *testing.T) { db, _ := ethdb.NewMemDatabase() state, _ := New(common.Hash{}, db) state.RevertToSnapshot(state.Snapshot()) } // use testing instead of checker because checker does not support // printing/logging in tests (-check.vv does not work) func TestSnapshot2(t *testing.T) { db, _ := ethdb.NewMemDatabase() state, _ := New(common.Hash{}, db) stateobjaddr0 := toAddr([]byte("so0")) stateobjaddr1 := toAddr([]byte("so1")) var storageaddr common.Hash data0 := common.BytesToHash([]byte{17}) data1 := common.BytesToHash([]byte{18}) state.SetState(stateobjaddr0, storageaddr, data0) state.SetState(stateobjaddr1, storageaddr, data1) // db, trie are already non-empty values so0 := state.getStateObject(stateobjaddr0) so0.SetBalance(big.NewInt(42)) so0.SetNonce(43) so0.SetCode(crypto.Keccak256Hash([]byte{'c', 'a', 'f', 'e'}), []byte{'c', 'a', 'f', 'e'}) so0.suicided = false so0.deleted = false state.setStateObject(so0) root, _ := state.Commit(false) state.Reset(root) // and one with deleted == true so1 := state.getStateObject(stateobjaddr1) so1.SetBalance(big.NewInt(52)) so1.SetNonce(53) so1.SetCode(crypto.Keccak256Hash([]byte{'c', 'a', 'f', 'e', '2'}), []byte{'c', 'a', 'f', 'e', '2'}) so1.suicided = true so1.deleted = true state.setStateObject(so1) so1 = state.getStateObject(stateobjaddr1) if so1 != nil { t.Fatalf("deleted object not nil when getting") } snapshot := state.Snapshot() state.RevertToSnapshot(snapshot) so0Restored := state.getStateObject(stateobjaddr0) // Update lazily-loaded values before comparing. so0Restored.GetState(db, storageaddr) so0Restored.Code(db) // non-deleted is equal (restored) compareStateObjects(so0Restored, so0, t) // deleted should be nil, both before and after restore of state copy so1Restored := state.getStateObject(stateobjaddr1) if so1Restored != nil { t.Fatalf("deleted object not nil after restoring snapshot: %+v", so1Restored) } } func compareStateObjects(so0, so1 *stateObject, t *testing.T) { if so0.Address() != so1.Address() { t.Fatalf("Address mismatch: have %v, want %v", so0.address, so1.address) } if so0.Balance().Cmp(so1.Balance()) != 0 { t.Fatalf("Balance mismatch: have %v, want %v", so0.Balance(), so1.Balance()) } if so0.Nonce() != so1.Nonce() { t.Fatalf("Nonce mismatch: have %v, want %v", so0.Nonce(), so1.Nonce()) } if so0.data.Root != so1.data.Root { t.Errorf("Root mismatch: have %x, want %x", so0.data.Root[:], so1.data.Root[:]) } if !bytes.Equal(so0.CodeHash(), so1.CodeHash()) { t.Fatalf("CodeHash mismatch: have %v, want %v", so0.CodeHash(), so1.CodeHash()) } if !bytes.Equal(so0.code, so1.code) { t.Fatalf("Code mismatch: have %v, want %v", so0.code, so1.code) } if len(so1.cachedStorage) != len(so0.cachedStorage) { t.Errorf("Storage size mismatch: have %d, want %d", len(so1.cachedStorage), len(so0.cachedStorage)) } for k, v := range so1.cachedStorage { if so0.cachedStorage[k] != v { t.Errorf("Storage key %x mismatch: have %v, want %v", k, so0.cachedStorage[k], v) } } for k, v := range so0.cachedStorage { if so1.cachedStorage[k] != v { t.Errorf("Storage key %x mismatch: have %v, want none.", k, v) } } if so0.suicided != so1.suicided { t.Fatalf("suicided mismatch: have %v, want %v", so0.suicided, so1.suicided) } if so0.deleted != so1.deleted { t.Fatalf("Deleted mismatch: have %v, want %v", so0.deleted, so1.deleted) } }