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.
This commit is contained in:
Delweng Zheng 2018-09-29 17:27:46 +08:00
parent efa11fe534
commit 9bdbdf5e18

View file

@ -217,6 +217,18 @@ func (self *stateObject) setState(key, value common.Hash) {
// updateTrie writes cached storage modifications into the object's storage trie. // updateTrie writes cached storage modifications into the object's storage trie.
func (self *stateObject) updateTrie(db Database) Trie { func (self *stateObject) updateTrie(db Database) Trie {
tr := self.getTrie(db) 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 { for key, value := range self.dirtyStorage {
delete(self.dirtyStorage, key) delete(self.dirtyStorage, key)
@ -226,7 +238,7 @@ func (self *stateObject) updateTrie(db Database) Trie {
} }
self.originStorage[key] = value self.originStorage[key] = value
if (value == common.Hash{}) { if value == (common.Hash{}) {
self.setError(tr.TryDelete(key[:])) self.setError(tr.TryDelete(key[:]))
continue continue
} }
@ -237,7 +249,7 @@ func (self *stateObject) updateTrie(db Database) Trie {
return tr 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) { func (self *stateObject) updateRoot(db Database) {
self.updateTrie(db) self.updateTrie(db)
self.data.Root = self.trie.Hash() self.data.Root = self.trie.Hash()