diff --git a/core/state/dump.go b/core/state/dump.go index c67697ff0..4ed9c159b 100644 --- a/core/state/dump.go +++ b/core/state/dump.go @@ -26,6 +26,7 @@ import ( "sync" "fmt" + "github.com/eth-classic/go-ethereum/common" "github.com/eth-classic/go-ethereum/rlp" "github.com/eth-classic/go-ethereum/trie" @@ -80,7 +81,7 @@ func (self *StateDB) RawDump(addresses []common.Address) Dump { panic(err) } - obj := newObject(nil, addrA, data, nil) + obj := newObject(nil, addrA, data) account := DumpAccount{ Balance: data.Balance.String(), Nonce: data.Nonce, @@ -178,7 +179,7 @@ func iterator(sdb *StateDB, addresses []common.Address, c chan *AddressedRawAcco panic(err) } - obj := newObject(nil, addrA, data, nil) + obj := newObject(nil, addrA, data) account := AddressedRawAccount{ DumpAccount: DumpAccount{ Balance: data.Balance.String(), diff --git a/core/state/journal.go b/core/state/journal.go index 9c6c20b0d..7540a1003 100644 --- a/core/state/journal.go +++ b/core/state/journal.go @@ -22,11 +22,67 @@ import ( "github.com/eth-classic/go-ethereum/common" ) +// journalEntry is a modification entry in the state change journal that can be +// reverted on demand. type journalEntry interface { - undo(*StateDB) + // revert undoes the changes introduced by this journal entry. + revert(*StateDB) + + // dirtied returns the Ethereum address modified by this journal entry. + dirtied() *common.Address +} + +// journal contains the list of state modifications applied since the last state +// commit. These are tracked to be able to be reverted in case of an execution +// exception or revertal request. +type journal struct { + entries []journalEntry // Current changes tracked by the journal + dirties map[common.Address]int // Dirty accounts and the number of changes +} + +// newJournal create a new initialized journal. +func newJournal() *journal { + return &journal{ + dirties: make(map[common.Address]int), + } +} + +// append inserts a new modification entry to the end of the change journal. +func (j *journal) append(entry journalEntry) { + j.entries = append(j.entries, entry) + if addr := entry.dirtied(); addr != nil { + j.dirties[*addr]++ + } +} + +// revert undoes a batch of journalled modifications along with any reverted +// dirty handling too. +func (j *journal) revert(statedb *StateDB, snapshot int) { + for i := len(j.entries) - 1; i >= snapshot; i-- { + // Undo the changes made by the operation + j.entries[i].revert(statedb) + + // Drop any dirty tracking induced by the change + if addr := j.entries[i].dirtied(); addr != nil { + if j.dirties[*addr]--; j.dirties[*addr] == 0 { + delete(j.dirties, *addr) + } + } + } + j.entries = j.entries[:snapshot] +} + +// dirty explicitly sets an address to dirty, even if the change entries would +// otherwise suggest it as clean. This method is an ugly hack to handle the RIPEMD +// precompile consensus exception. +func (j *journal) dirty(addr common.Address) { + j.dirties[addr]++ } -type journal []journalEntry +// length returns the current number of entries in the journal. +func (j *journal) length() int { + return len(j.entries) +} type ( // Changes to the account trie. @@ -77,16 +133,24 @@ type ( } ) -func (ch createObjectChange) undo(s *StateDB) { +func (ch createObjectChange) revert(s *StateDB) { delete(s.stateObjects, *ch.account) delete(s.stateObjectsDirty, *ch.account) } -func (ch resetObjectChange) undo(s *StateDB) { +func (ch createObjectChange) dirtied() *common.Address { + return ch.account +} + +func (ch resetObjectChange) revert(s *StateDB) { s.setStateObject(ch.prev) } -func (ch suicideChange) undo(s *StateDB) { +func (ch resetObjectChange) dirtied() *common.Address { + return nil +} + +func (ch suicideChange) revert(s *StateDB) { obj := s.getStateObject(*ch.account) if obj != nil { obj.suicided = ch.prev @@ -94,38 +158,60 @@ func (ch suicideChange) undo(s *StateDB) { } } +func (ch suicideChange) dirtied() *common.Address { + return ch.account +} + var ripemd = common.HexToAddress("0000000000000000000000000000000000000003") -func (ch touchChange) undo(s *StateDB) { - if !ch.prev && *ch.account != ripemd { - s.getStateObject(*ch.account).touched = ch.prev - if !ch.prevDirty { - delete(s.stateObjectsDirty, *ch.account) - } - } +func (ch touchChange) revert(s *StateDB) { +} + +func (ch touchChange) dirtied() *common.Address { + return ch.account } -func (ch balanceChange) undo(s *StateDB) { +func (ch balanceChange) revert(s *StateDB) { s.getStateObject(*ch.account).setBalance(ch.prev) } -func (ch nonceChange) undo(s *StateDB) { +func (ch balanceChange) dirtied() *common.Address { + return ch.account +} + +func (ch nonceChange) revert(s *StateDB) { s.getStateObject(*ch.account).setNonce(ch.prev) } -func (ch codeChange) undo(s *StateDB) { +func (ch nonceChange) dirtied() *common.Address { + return ch.account +} + +func (ch codeChange) revert(s *StateDB) { s.getStateObject(*ch.account).setCode(common.BytesToHash(ch.prevhash), ch.prevcode) } -func (ch storageChange) undo(s *StateDB) { +func (ch codeChange) dirtied() *common.Address { + return ch.account +} + +func (ch storageChange) revert(s *StateDB) { s.getStateObject(*ch.account).setState(ch.key, ch.prevalue) } -func (ch refundChange) undo(s *StateDB) { +func (ch storageChange) dirtied() *common.Address { + return ch.account +} + +func (ch refundChange) revert(s *StateDB) { s.refund = ch.prev } -func (ch addLogChange) undo(s *StateDB) { +func (ch refundChange) dirtied() *common.Address { + return nil +} + +func (ch addLogChange) revert(s *StateDB) { logs := s.logs[ch.txhash] if len(logs) == 1 { delete(s.logs, ch.txhash) @@ -135,6 +221,14 @@ func (ch addLogChange) undo(s *StateDB) { s.logSize-- } -func (ch addPreimageChange) undo(s *StateDB) { +func (ch addLogChange) dirtied() *common.Address { + return nil +} + +func (ch addPreimageChange) revert(s *StateDB) { delete(s.preimages, ch.hash) } + +func (ch addPreimageChange) dirtied() *common.Address { + return nil +} diff --git a/core/state/managed_state.go b/core/state/managed_state.go index fc46b1602..ee6562416 100644 --- a/core/state/managed_state.go +++ b/core/state/managed_state.go @@ -84,8 +84,8 @@ func (ms *ManagedState) NewNonce(addr common.Address) uint64 { // GetNonce returns the canonical nonce for the managed or unmanaged account func (ms *ManagedState) GetNonce(addr common.Address) uint64 { - ms.mu.RLock() - defer ms.mu.RUnlock() + ms.mu.Lock() + defer ms.mu.Unlock() if ms.hasAccount(addr) { account := ms.getAccount(addr) diff --git a/core/state/state_object.go b/core/state/state_object.go index 0864dc96e..7fd883f85 100644 --- a/core/state/state_object.go +++ b/core/state/state_object.go @@ -88,9 +88,7 @@ type StateObject struct { // during the "update" phase of the state transition. dirtyCode bool // true if the code was updated suicided bool - touched bool deleted bool - onDirty func(addr common.Address) // Callback method to mark a state object newly dirty } // empty returns whether the account is considered empty. @@ -108,7 +106,7 @@ type Account struct { } // newObject creates a state object. -func newObject(db *StateDB, address common.Address, data Account, onDirty func(addr common.Address)) *StateObject { +func newObject(db *StateDB, address common.Address, data Account) *StateObject { if data.Balance == nil { data.Balance = new(big.Int) } @@ -122,7 +120,6 @@ func newObject(db *StateDB, address common.Address, data Account, onDirty func(a data: data, cachedStorage: make(Storage), dirtyStorage: make(Storage), - onDirty: onDirty, } } @@ -140,23 +137,17 @@ func (self *StateObject) setError(err error) { func (self *StateObject) markSuicided() { self.suicided = true - if self.onDirty != nil { - self.onDirty(self.Address()) - self.onDirty = nil - } } func (c *StateObject) touch() { - c.db.journal = append(c.db.journal, touchChange{ - account: &c.address, - prev: c.touched, - prevDirty: c.onDirty == nil, + c.db.journal.append(touchChange{ + account: &c.address, }) - if c.onDirty != nil { - c.onDirty(c.Address()) - c.onDirty = nil + if c.address == ripemd { + // Explicitly put it in the dirty-cache, which is otherwise generated from + // flattened journals. + c.db.journal.dirty(c.address) } - c.touched = true } func (c *StateObject) getTrie(db Database) Trie { @@ -198,7 +189,7 @@ func (self *StateObject) GetState(db Database, key common.Hash) common.Hash { // SetState updates a value in account storage. func (self *StateObject) SetState(db Database, key, value common.Hash) { - self.db.journal = append(self.db.journal, storageChange{ + self.db.journal.append(storageChange{ account: &self.address, key: key, prevalue: self.GetState(db, key), @@ -209,11 +200,6 @@ func (self *StateObject) SetState(db Database, key, value common.Hash) { func (self *StateObject) setState(key, value common.Hash) { self.cachedStorage[key] = value self.dirtyStorage[key] = value - - if self.onDirty != nil { - self.onDirty(self.Address()) - self.onDirty = nil - } } // updateTrie writes cached storage modifications into the object's storage trie. @@ -299,7 +285,7 @@ func (c *StateObject) SubBalance(amount *big.Int) { } func (self *StateObject) SetBalance(amount *big.Int) { - self.db.journal = append(self.db.journal, balanceChange{ + self.db.journal.append(balanceChange{ account: &self.address, prev: new(big.Int).Set(self.data.Balance), }) @@ -308,17 +294,13 @@ func (self *StateObject) SetBalance(amount *big.Int) { func (self *StateObject) setBalance(amount *big.Int) { self.data.Balance = amount - if self.onDirty != nil { - self.onDirty(self.Address()) - self.onDirty = nil - } } // Return the gas back to the origin. Used by the Virtual machine or Closures func (c *StateObject) ReturnGas(*big.Int, *big.Int) {} -func (self *StateObject) deepCopy(db *StateDB, onDirty func(addr common.Address)) *StateObject { - stateObject := newObject(db, self.address, self.data, onDirty) +func (self *StateObject) deepCopy(db *StateDB) *StateObject { + stateObject := newObject(db, self.address, self.data) if self.trie != nil { stateObject.trie = db.db.CopyTrie(self.trie) } @@ -358,7 +340,7 @@ func (self *StateObject) Code(db Database) []byte { func (self *StateObject) SetCode(codeHash common.Hash, code []byte) { prevcode := self.Code(self.db.db) - self.db.journal = append(self.db.journal, codeChange{ + self.db.journal.append(codeChange{ account: &self.address, prevhash: self.CodeHash(), prevcode: prevcode, @@ -370,14 +352,10 @@ func (self *StateObject) setCode(codeHash common.Hash, code []byte) { self.code = code self.data.CodeHash = codeHash[:] self.dirtyCode = true - if self.onDirty != nil { - self.onDirty(self.Address()) - self.onDirty = nil - } } func (self *StateObject) SetNonce(nonce uint64) { - self.db.journal = append(self.db.journal, nonceChange{ + self.db.journal.append(nonceChange{ account: &self.address, prev: self.data.Nonce, }) @@ -386,10 +364,6 @@ func (self *StateObject) SetNonce(nonce uint64) { func (self *StateObject) setNonce(nonce uint64) { self.data.Nonce = nonce - if self.onDirty != nil { - self.onDirty(self.Address()) - self.onDirty = nil - } } func (self *StateObject) CodeHash() []byte { diff --git a/core/state/statedb.go b/core/state/statedb.go index 8548ffe18..82ede1580 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -87,7 +87,7 @@ type StateDB struct { // Journal of state modifications. This is the backbone of // Snapshot and RevertToSnapshot. - journal journal + journal *journal validRevisions []revision nextRevisionId int @@ -110,6 +110,7 @@ func New(root common.Hash, db Database) (*StateDB, error) { refund: new(big.Int), logs: make(map[common.Hash]vm.Logs), preimages: make(map[common.Hash][]byte), + journal: newJournal(), }, nil } @@ -164,7 +165,7 @@ func (self *StateDB) StartRecord(thash, bhash common.Hash, ti int) { } func (self *StateDB) AddLog(log vm.Log) { - self.journal = append(self.journal, addLogChange{txhash: self.thash}) + self.journal.append(addLogChange{txhash: self.thash}) log.TxHash = self.thash log.BlockHash = self.bhash @@ -187,7 +188,7 @@ func (self *StateDB) Logs() vm.Logs { } func (self *StateDB) AddRefund(gas *big.Int) { - self.journal = append(self.journal, refundChange{prev: new(big.Int).Set(self.refund)}) + self.journal.append(refundChange{prev: new(big.Int).Set(self.refund)}) self.refund.Add(self.refund, gas) } @@ -321,7 +322,7 @@ func (self *StateDB) Suicide(addr common.Address) bool { if stateObject == nil { return false } - self.journal = append(self.journal, suicideChange{ + self.journal.append(suicideChange{ account: &addr, prev: stateObject.suicided, prevbalance: new(big.Int).Set(stateObject.Balance()), @@ -377,7 +378,7 @@ func (self *StateDB) getStateObject(addr common.Address) (stateObject *StateObje return nil } // Insert into the live set. - obj = newObject(self, addr, data, self.MarkStateObjectDirty) + obj = newObject(self, addr, data) self.setStateObject(obj) return obj } @@ -397,17 +398,11 @@ func (self *StateDB) GetOrNewStateObject(addr common.Address) *StateObject { return stateObject } -// MarkStateObjectDirty adds the specified object to the dirty map to avoid costly -// state object cache iteration to find a handful of modified ones. -func (self *StateDB) MarkStateObjectDirty(addr common.Address) { - self.stateObjectsDirty[addr] = struct{}{} -} - // createObject creates a new state object. If there is an existing account with // the given address, it is overwritten and returned as the second return value. func (self *StateDB) createObject(addr common.Address) (newobj, prev *StateObject) { prev = self.getStateObject(addr) - newobj = newObject(self, addr, Account{}, self.MarkStateObjectDirty) + newobj = newObject(self, addr, Account{}) newobj.setNonce(StartingNonce) // sets the object to dirty if prev == nil { if logger.MlogEnabled() { @@ -419,7 +414,7 @@ func (self *StateDB) createObject(addr common.Address) (newobj, prev *StateObjec if glog.V(logger.Detail) { glog.Infof("(+) %x\n", addr) } - self.journal = append(self.journal, createObjectChange{account: &addr}) + self.journal.append(createObjectChange{account: &addr}) } else { if logger.MlogEnabled() { mlogStateCreateObject.AssignDetails( @@ -427,7 +422,7 @@ func (self *StateDB) createObject(addr common.Address) (newobj, prev *StateObjec prev.address.Hex(), ).Send(mlogState) } - self.journal = append(self.journal, resetObjectChange{prev: prev}) + self.journal.append(resetObjectChange{prev: prev}) } self.setStateObject(newobj) return newobj, prev @@ -461,18 +456,30 @@ func (self *StateDB) Copy() *StateDB { state := &StateDB{ db: self.db, trie: self.db.CopyTrie(self.trie), - stateObjects: make(map[common.Address]*StateObject, len(self.stateObjectsDirty)), - stateObjectsDirty: make(map[common.Address]struct{}, len(self.stateObjectsDirty)), + stateObjects: make(map[common.Address]*StateObject, len(self.journal.dirties)), + stateObjectsDirty: make(map[common.Address]struct{}, len(self.journal.dirties)), refund: new(big.Int).Set(self.refund), logs: make(map[common.Hash]vm.Logs, len(self.logs)), logSize: self.logSize, preimages: make(map[common.Hash][]byte), + journal: newJournal(), } // Copy the dirty states, logs, and preimages - for addr := range self.stateObjectsDirty { - state.stateObjects[addr] = self.stateObjects[addr].deepCopy(state, state.MarkStateObjectDirty) + for addr := range self.journal.dirties { + state.stateObjects[addr] = self.stateObjects[addr].deepCopy(state) state.stateObjectsDirty[addr] = struct{}{} } + + // Above, we don't copy the actual journal. This means that if the copy is copied, the + // loop above will be a no-op, since the copy's journal is empty. + // Thus, here we iterate over stateObjects, to enable copies of copies + for addr := range self.stateObjectsDirty { + if _, exist := state.stateObjects[addr]; !exist { + state.stateObjects[addr] = self.stateObjects[addr].deepCopy(state) + state.stateObjectsDirty[addr] = struct{}{} + } + } + for hash, logs := range self.logs { state.logs[hash] = make(vm.Logs, len(logs)) copy(state.logs[hash], logs) @@ -487,7 +494,7 @@ func (self *StateDB) Copy() *StateDB { func (self *StateDB) Snapshot() int { id := self.nextRevisionId self.nextRevisionId++ - self.validRevisions = append(self.validRevisions, revision{id, len(self.journal)}) + self.validRevisions = append(self.validRevisions, revision{id, self.journal.length()}) return id } @@ -503,10 +510,7 @@ func (self *StateDB) RevertToSnapshot(revid int) { snapshot := self.validRevisions[idx].journalIndex // Replay the journal to undo changes. - for i := len(self.journal) - 1; i >= snapshot; i-- { - self.journal[i].undo(self) - } - self.journal = self.journal[:snapshot] + self.journal.revert(self, snapshot) // Remove invalidated snapshots from the stack. self.validRevisions = self.validRevisions[:idx] @@ -530,9 +534,15 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash { // Finalise finalises the state by removing the self destructed objects // and clears the journal as well as the refunds. func (s *StateDB) Finalise(deleteEmptyObjects bool) { - for addr := range s.stateObjectsDirty { + for addr := range s.journal.dirties { stateObject, exist := s.stateObjects[addr] if !exist { + // ripeMD is 'touched' at block 1714175, in tx 0x1237f737031e40bcde4a8b7e717b2d15e3ecadfe49bb1bbc71ee9deb09c6fcf2 + // That tx goes out of gas, and although the notion of 'touched' does not exist there, the + // touch-event will still be recorded in the journal. Since ripeMD is a special snowflake, + // it will persist in the journal even though the journal is reverted. In this special circumstance, + // it may exist in `s.journal.dirties` but not in `s.stateObjects`. + // Thus, we can safely ignore it here continue } @@ -570,7 +580,7 @@ func (s *StateDB) DeleteSuicides() { } func (s *StateDB) clearJournalAndRefund() { - s.journal = nil + s.journal = newJournal() s.validRevisions = s.validRevisions[:0] s.refund = new(big.Int) } @@ -579,6 +589,10 @@ func (s *StateDB) clearJournalAndRefund() { func (s *StateDB) CommitTo(dbw trie.DatabaseWriter, deleteEmptyObjects bool) (root common.Hash, err error) { defer s.clearJournalAndRefund() + for addr := range s.journal.dirties { + s.stateObjectsDirty[addr] = struct{}{} + } + // Commit objects to the trie. for addr, stateObject := range s.stateObjects { _, isDirty := s.stateObjectsDirty[addr] diff --git a/core/state/statedb_test.go b/core/state/statedb_test.go index 4a17a9bab..466644e21 100644 --- a/core/state/statedb_test.go +++ b/core/state/statedb_test.go @@ -167,6 +167,21 @@ func TestCopy(t *testing.T) { } } +// TestCopyOfCopy tests that modified objects are carried over to the copy, and the copy of the copy. +func TestCopyOfCopy(t *testing.T) { + db, _ := ethdb.NewMemDatabase() + sdb, _ := New(common.Hash{}, NewDatabase(db)) + addr := common.HexToAddress("aaaa") + sdb.SetBalance(addr, big.NewInt(42)) + + if got := sdb.Copy().GetBalance(addr).Uint64(); got != 42 { + t.Fatalf("1st copy fail, expected 42, got %v", got) + } + if got := sdb.Copy().Copy().GetBalance(addr).Uint64(); got != 42 { + t.Fatalf("2nd copy fail, expected 42, got %v", got) + } +} + func TestSnapshotRandom(t *testing.T) { config := &quick.Config{MaxCount: 1000} err := quick.Check((*snapshotTest).run, config) @@ -413,12 +428,12 @@ func (s *StateSuite) TestTouchDelete(c *check.C) { snapshot := s.state.Snapshot() s.state.AddBalance(common.Address{}, new(big.Int)) - if len(s.state.stateObjectsDirty) != 1 { + if len(s.state.journal.dirties) != 1 { c.Fatal("expected one dirty state object") } s.state.RevertToSnapshot(snapshot) - if len(s.state.stateObjectsDirty) != 0 { + if len(s.state.journal.dirties) != 0 { c.Fatal("expected no dirty state object") } } diff --git a/core/tx_pool.go b/core/tx_pool.go index 1ae0b9278..a42e8f2a4 100644 --- a/core/tx_pool.go +++ b/core/tx_pool.go @@ -124,6 +124,13 @@ func (pool *TxPool) eventLoop() { } } +func (pool *TxPool) lockedReset() { + pool.mu.Lock() + defer pool.mu.Unlock() + + pool.resetState() +} + func (pool *TxPool) resetState() { currentState, err := pool.currentState() if err != nil { diff --git a/core/tx_pool_test.go b/core/tx_pool_test.go index d59f3e3bd..c4c62fa35 100644 --- a/core/tx_pool_test.go +++ b/core/tx_pool_test.go @@ -41,7 +41,7 @@ func setupTxPool() (*TxPool, *ecdsa.PrivateKey) { var m event.TypeMux key, _ := crypto.GenerateKey() newPool := NewTxPool(testChainConfig(), &m, func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) }) - newPool.resetState() + newPool.lockedReset() return newPool, key } @@ -184,7 +184,7 @@ func TestTransactionChainFork(t *testing.T) { pool.currentState = func() (*state.StateDB, error) { return statedb, nil } currentState, _ := pool.currentState() currentState.AddBalance(addr, big.NewInt(100000000000000)) - pool.resetState() + pool.lockedReset() } resetState() @@ -210,7 +210,7 @@ func TestTransactionDoubleNonce(t *testing.T) { pool.currentState = func() (*state.StateDB, error) { return statedb, nil } currentState, _ := pool.currentState() currentState.AddBalance(addr, big.NewInt(100000000000000)) - pool.resetState() + pool.lockedReset() } resetState() @@ -253,14 +253,14 @@ func TestNonceRecovery(t *testing.T) { currentState, _ := pool.currentState() currentState.SetNonce(addr, n) currentState.AddBalance(addr, big.NewInt(100000000000000)) - pool.resetState() + pool.lockedReset() tx := transaction(n, big.NewInt(100000), key) if err := pool.Add(tx); err != nil { t.Error(err) } // simulate some weird re-order of transactions and missing nonce(s) currentState.SetNonce(addr, n-1) - pool.resetState() + pool.lockedReset() if fn := pool.pendingState.GetNonce(addr); fn != n+1 { t.Errorf("expected nonce to be %d, got %d", n+1, fn) } @@ -308,7 +308,7 @@ func TestTransactionDropping(t *testing.T) { if len(pool.queue[account]) != 2 { t.Errorf("queued transaction mismatch: have %d, want %d", len(pool.queue), 2) } - pool.resetState() + pool.lockedReset() if len(pool.pending) != 2 { t.Errorf("pending transaction mismatch: have %d, want %d", len(pool.pending), 2) } @@ -317,7 +317,7 @@ func TestTransactionDropping(t *testing.T) { } // Reduce the balance of the account, and check that invalidated transactions are dropped state.AddBalance(account, big.NewInt(-750)) - pool.resetState() + pool.lockedReset() if _, ok := pool.pending[tx0.Hash()]; !ok { t.Errorf("funded pending transaction missing: %v", tx0) @@ -363,7 +363,7 @@ func TestTransactionPostponing(t *testing.T) { if len(pool.queue[account]) != 0 { t.Errorf("queued transaction mismatch: have %d, want %d", len(pool.queue), 0) } - pool.resetState() + pool.lockedReset() if len(pool.pending) != len(txns) { t.Errorf("pending transaction mismatch: have %d, want %d", len(pool.pending), len(txns)) } @@ -372,7 +372,7 @@ func TestTransactionPostponing(t *testing.T) { } // Reduce the balance of the account, and check that transactions are reorganised state.AddBalance(account, big.NewInt(-750)) - pool.resetState() + pool.lockedReset() if _, ok := pool.pending[txns[0].Hash()]; !ok { t.Errorf("tx %d: valid and funded transaction missing from pending pool: %v", 0, txns[0]) diff --git a/tests/state_test.go b/tests/state_test.go index 0cd98e141..ab315e90e 100644 --- a/tests/state_test.go +++ b/tests/state_test.go @@ -702,10 +702,6 @@ func TestAllETH(t *testing.T) { skipTests["randomStatetest644.json"] = "random unimplemented" skipTests["randomStatetest645.json"] = "random unimplemented" - // EIP 158/161 skipped tests - skipTests["RevertPrefoundEmptyOOG.json"] = "State trie clearing unimplemented" - skipTests["FailedCreateRevertsDeletion.json"] = "State trie clearing unimplemented" - unsupportedDirs := map[string]bool{ "stZeroKnowledge": true, "stZeroKnowledge2": true,