From 9bdbdf5e18a5ad05d3dfbc3af830659d7867d8f8 Mon Sep 17 00:00:00 2001 From: Delweng Zheng Date: Sat, 29 Sep 2018 17:27:46 +0800 Subject: [PATCH] core/state: ensure originStorage is committed I see, the `stateObject.updateTrie` all invoked by the miner, and there exists two stack trace: 1. By `StateDB.Finalise`, and the full stack trace as below: ``` goroutine 83 [running]: github.com/ethereum/go-ethereum/core/state.(*stateObject).updateTrie(0xc009c46e00, 0x11d9ae0, 0xc000209ad0, 0xc000ce6f1c, 0x33b1d98976d74501) /root/.go/src/github.com/ethereum/go-ethereum/build/_workspace/src/github.com/ethereum/go-ethereum/core/state/state_object.go:223 +0xcf github.com/ethereum/go-ethereum/core/state.(*stateObject).updateRoot(0xc009c46e00, 0x11d9ae0, 0xc000209ad0) /root/.go/src/github.com/ethereum/go-ethereum/build/_workspace/src/github.com/ethereum/go-ethereum/core/state/state_object.go:258 +0x43 github.com/ethereum/go-ethereum/core/state.(*StateDB).Finalise(0xc009c46c40, 0xc018596901) /root/.go/src/github.com/ethereum/go-ethereum/build/_workspace/src/github.com/ethereum/go-ethereum/core/state/statedb.go:569 +0x1a2 github.com/ethereum/go-ethereum/core.ApplyTransaction(0xc0001e2180, 0x11ce740, 0xc000382000, 0xc00a8b3558, 0xc0001d8ff0, 0xc009c46c40, 0xc00a75c240, 0xc018578900, 0xc00a75c410, 0x0, ...) /root/.go/src/github.com/ethereum/go-ethereum/build/_workspace/src/github.com/ethereum/go-ethereum/core/state_processor.go:106 +0x32a github.com/ethereum/go-ethereum/miner.(*worker).commitTransaction(0xc0004da1c0, 0xc018578900, 0x0, 0x0, 0x73e9da8b01000000, 0xc0512796de, 0x0, 0x0, 0x440ee1, 0x100e9c8) /root/.go/src/github.com/ethereum/go-ethereum/build/_workspace/src/github.com/ethereum/go-ethereum/miner/worker.go:695 +0x13a github.com/ethereum/go-ethereum/miner.(*worker).commitTransactions(0xc0004da1c0, 0xc00035fd10, 0x0, 0x0, 0x1000000, 0xc0001d8fe0, 0xb1) /root/.go/src/github.com/ethereum/go-ethereum/build/_workspace/src/github.com/ethereum/go-ethereum/miner/worker.go:765 +0x294 github.com/ethereum/go-ethereum/miner.(*worker).commitNewWork(0xc0004da1c0, 0xc0001d8fe0, 0x1, 0x5baf3a7d) /root/.go/src/github.com/ethereum/go-ethereum/build/_workspace/src/github.com/ethereum/go-ethereum/miner/worker.go:933 +0xb75 github.com/ethereum/go-ethereum/miner.(*worker).mainLoop(0xc0004da1c0) /root/.go/src/github.com/ethereum/go-ethereum/build/_workspace/src/github.com/ethereum/go-ethereum/miner/worker.go:411 +0xba1 created by github.com/ethereum/go-ethereum/miner.newWorker /root/.go/src/github.com/ethereum/go-ethereum/build/_workspace/src/github.com/ethereum/go-ethereum/miner/worker.go:219 +0x506 ``` which uses `w.current.state`, and after the `updateTrie` was invoked, the `stateObject.dirtyStorage` turned out to be an empty map, and the items are updated to the SecureTrie. **You only update the Trie, but not commited it, some metadata are only in SecureTrie's cache, not persistenced.** 2. By `StateDB.CommitTrie`, and the full stack trace as below: ``` goroutine 85 [running]: github.com/ethereum/go-ethereum/core/state.(*stateObject).updateTrie(0xc009c47260, 0x11d9ae0, 0xc000209ad0, 0x177614d290fdfd56, 0xcb009d0df74936b9) /root/.go/src/github.com/ethereum/go-ethereum/build/_workspace/src/github.com/ethereum/go-ethereum/core/state/state_object.go:223 +0xcf github.com/ethereum/go-ethereum/core/state.(*stateObject).CommitTrie(0xc009c47260, 0x11d9ae0, 0xc000209ad0, 0xaa71a542df75c534, 0xc2398056166a4ff7) /root/.go/src/github.com/ethereum/go-ethereum/build/_workspace/src/github.com/ethereum/go-ethereum/core/state/state_object.go:265 +0x5a github.com/ethereum/go-ethereum/core/state.(*StateDB).Commit(0xc009c46fc0, 0xc002031a01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0) /root/.go/src/github.com/ethereum/go-ethereum/build/_workspace/src/github.com/ethereum/go-ethereum/core/state/statedb.go:622 +0x30b github.com/ethereum/go-ethereum/core.(*BlockChain).WriteBlockWithState(0xc000382000, 0xc000d8c240, 0xc01855a0c8, 0x1, 0x1, 0xc009c46fc0, 0x0, 0x0, 0x0) /root/.go/src/github.com/ethereum/go-ethereum/build/_workspace/src/github.com/ethereum/go-ethereum/core/blockchain.go:909 +0x502 github.com/ethereum/go-ethereum/miner.(*worker).resultLoop(0xc0004da1c0) /root/.go/src/github.com/ethereum/go-ethereum/build/_workspace/src/github.com/ethereum/go-ethereum/miner/worker.go:580 +0x628 created by github.com/ethereum/go-ethereum/miner.newWorker /root/.go/src/github.com/ethereum/go-ethereum/build/_workspace/src/github.com/ethereum/go-ethereum/miner/worker.go:221 +0x557 ``` which uses `w.current.state.Copy()`, a deep copy of `w.current.state`. So there exists two objects of `StateDB`. So far so good. After the 2th `CommitTrie` invoked, I want to use `debug.dumpBlock` to find the storage of deployed contract, but it failed to print only one no key value, seems I'm missing the preimages of all the storage key. After digging into the implement of SecureTrie, inside the `SecureTrie.Commit`, it will insert the preimages down to Database, but it didn't do it. Finds it depends on `len(t.getSecKeyCache())`, which will decide to use the old `secKeyCache`(copyed from the `w.current.state`) or make a new map by the `t.secKeyCacheOwner`. But after you copyed a new StateDB, the owner is changed, in the meanwhile, you want to Commit the Trie, only the trie is saved to DB, the preimages are abandoned. --- core/state/state_object.go | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/core/state/state_object.go b/core/state/state_object.go index f41ab04092..1e0a319aaf 100644 --- a/core/state/state_object.go +++ b/core/state/state_object.go @@ -217,6 +217,18 @@ func (self *stateObject) setState(key, value common.Hash) { // updateTrie writes cached storage modifications into the object's storage trie. func (self *stateObject) updateTrie(db Database) Trie { tr := self.getTrie(db) + + // save the originStorage first, ensure all non empty keys in originStorage saved to trie. + // if the object is copyed from another one, + // the dirtyStorage is empty and the originStorage is not empty. + for key, value := range self.originStorage { + if value != (common.Hash{}) { + // Encoding []byte cannot fail, ok to ignore the error. + v, _ := rlp.EncodeToBytes(bytes.TrimLeft(value[:], "\x00")) + self.setError(tr.TryUpdate(key[:], v)) + } + } + for key, value := range self.dirtyStorage { delete(self.dirtyStorage, key) @@ -226,7 +238,7 @@ func (self *stateObject) updateTrie(db Database) Trie { } self.originStorage[key] = value - if (value == common.Hash{}) { + if value == (common.Hash{}) { self.setError(tr.TryDelete(key[:])) continue } @@ -237,7 +249,7 @@ func (self *stateObject) updateTrie(db Database) Trie { return tr } -// UpdateRoot sets the trie root to the current root hash of +// updateRoot sets the trie root to the current root hash of func (self *stateObject) updateRoot(db Database) { self.updateTrie(db) self.data.Root = self.trie.Hash()