// 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" "context" "testing" pgipfsethdb "github.com/cerc-io/ipfs-ethdb/v5/postgres/v0" "github.com/cerc-io/plugeth-statediff/indexer/database/sql/postgres" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethdb" "github.com/holiman/uint256" "github.com/cerc-io/ipld-eth-statedb/internal" ) var ( testCtx = context.Background() testConfig, _ = postgres.TestConfig.WithEnv() teardownStatements = []string{`TRUNCATE ipld.blocks`} ) type stateEnv struct { db ethdb.Database state *StateDB } func newStateEnv(t *testing.T) *stateEnv { db := newPgIpfsEthdb(t) sdb, err := New(types.EmptyRootHash, NewDatabase(db), nil) if err != nil { t.Fatal(err) } return &stateEnv{db: db, state: sdb} } func newPgIpfsEthdb(t *testing.T) ethdb.Database { pool, err := postgres.ConnectSQLX(testCtx, testConfig) if err != nil { t.Fatal(err) } db := pgipfsethdb.NewDatabase(pool, internal.MakeCacheConfig(t)) return db } func TestNull(t *testing.T) { s := newStateEnv(t) address := common.HexToAddress("0x823140710bf13990e4500136726d8b55") s.state.CreateAccount(address) //value := common.FromHex("0x823140710bf13990e4500136726d8b55") var value common.Hash s.state.SetState(address, common.Hash{}, value) // s.state.Commit(0, false) if value := s.state.GetState(address, common.Hash{}); value != (common.Hash{}) { t.Errorf("expected empty current value, got %x", value) } if value := s.state.GetCommittedState(address, common.Hash{}); value != (common.Hash{}) { t.Errorf("expected empty committed value, got %x", value) } } func TestSnapshot(t *testing.T) { stateobjaddr := common.BytesToAddress([]byte("aa")) var storageaddr common.Hash data1 := common.BytesToHash([]byte{42}) data2 := common.BytesToHash([]byte{43}) s := newStateEnv(t) // snapshot the genesis state genesis := s.state.Snapshot() // set initial state object value s.state.SetState(stateobjaddr, storageaddr, data1) snapshot := s.state.Snapshot() // set a new state object value, revert it and ensure correct content s.state.SetState(stateobjaddr, storageaddr, data2) s.state.RevertToSnapshot(snapshot) if v := s.state.GetState(stateobjaddr, storageaddr); v != data1 { t.Errorf("wrong storage value %v, want %v", v, data1) } if v := s.state.GetCommittedState(stateobjaddr, storageaddr); v != (common.Hash{}) { t.Errorf("wrong committed storage value %v, want %v", v, common.Hash{}) } // revert up to the genesis state and ensure correct content s.state.RevertToSnapshot(genesis) if v := s.state.GetState(stateobjaddr, storageaddr); v != (common.Hash{}) { t.Errorf("wrong storage value %v, want %v", v, common.Hash{}) } if v := s.state.GetCommittedState(stateobjaddr, storageaddr); v != (common.Hash{}) { t.Errorf("wrong committed storage value %v, want %v", v, common.Hash{}) } } func TestSnapshotEmpty(t *testing.T) { s := newStateEnv(t) s.state.RevertToSnapshot(s.state.Snapshot()) } func TestSnapshot2(t *testing.T) { state, _ := New(types.EmptyRootHash, NewDatabase(newPgIpfsEthdb(t)), nil) stateobjaddr0 := common.BytesToAddress([]byte("so0")) stateobjaddr1 := common.BytesToAddress([]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(uint256.NewInt(42)) so0.SetNonce(43) so0.SetCode(crypto.Keccak256Hash([]byte{'c', 'a', 'f', 'e'}), []byte{'c', 'a', 'f', 'e'}) so0.selfDestructed = false so0.deleted = false state.setStateObject(so0) // root, _ := state.Commit(0, false) // state, _ = New(root, state.db, state.snaps) // and one with deleted == true so1 := state.getStateObject(stateobjaddr1) so1.SetBalance(uint256.NewInt(52)) so1.SetNonce(53) so1.SetCode(crypto.Keccak256Hash([]byte{'c', 'a', 'f', 'e', '2'}), []byte{'c', 'a', 'f', 'e', '2'}) so1.selfDestructed = 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(storageaddr) so0Restored.Code() // 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.dirtyStorage) != len(so0.dirtyStorage) { t.Errorf("Dirty storage size mismatch: have %d, want %d", len(so1.dirtyStorage), len(so0.dirtyStorage)) } for k, v := range so1.dirtyStorage { if so0.dirtyStorage[k] != v { t.Errorf("Dirty storage key %x mismatch: have %v, want %v", k, so0.dirtyStorage[k], v) } } for k, v := range so0.dirtyStorage { if so1.dirtyStorage[k] != v { t.Errorf("Dirty storage key %x mismatch: have %v, want none.", k, v) } } if len(so1.originStorage) != len(so0.originStorage) { t.Errorf("Origin storage size mismatch: have %d, want %d", len(so1.originStorage), len(so0.originStorage)) } for k, v := range so1.originStorage { if so0.originStorage[k] != v { t.Errorf("Origin storage key %x mismatch: have %v, want %v", k, so0.originStorage[k], v) } } for k, v := range so0.originStorage { if so1.originStorage[k] != v { t.Errorf("Origin storage key %x mismatch: have %v, want none.", k, v) } } }