diff --git a/core/blockchain.go b/core/blockchain.go index 3dedff6375..d4de132fd6 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -592,6 +592,17 @@ func (bc *BlockChain) Stop() { atomic.StoreInt32(&bc.procInterrupt, 1) bc.wg.Wait() + + // Ensure the state of the latest block is also stored to disk + root := bc.CurrentHeader().Root + + batch := bc.chainDb.NewBatch() + if err := bc.trieMemPool.Commit(root, batch); err != nil { + log.Error("Failed to commit latest state trie", "err", err) + } + if err := batch.Write(); err != nil { + log.Error("Failed to write latest state trie", "err", err) + } log.Info("Blockchain manager stopped") } diff --git a/core/dao_test.go b/core/dao_test.go index 43e2982a52..1b16b2f265 100644 --- a/core/dao_test.go +++ b/core/dao_test.go @@ -79,6 +79,9 @@ func TestDAOForkRangeExtradata(t *testing.T) { if _, err := bc.InsertChain(blocks); err != nil { t.Fatalf("failed to import contra-fork chain for expansion: %v", err) } + if err := bc.trieMemPool.Commit(bc.CurrentHeader().Root, db); err != nil { + t.Fatalf("failed to commit contra-fork head for expansion: %v", err) + } blocks, _ = GenerateChain(&proConf, conBc.CurrentBlock(), ethash.NewFaker(), db, 1, func(i int, gen *BlockGen) {}) if _, err := conBc.InsertChain(blocks); err == nil { t.Fatalf("contra-fork chain accepted pro-fork block: %v", blocks[0]) @@ -101,6 +104,9 @@ func TestDAOForkRangeExtradata(t *testing.T) { if _, err := bc.InsertChain(blocks); err != nil { t.Fatalf("failed to import pro-fork chain for expansion: %v", err) } + if err := bc.trieMemPool.Commit(bc.CurrentHeader().Root, db); err != nil { + t.Fatalf("failed to commit pro-fork head for expansion: %v", err) + } blocks, _ = GenerateChain(&conConf, proBc.CurrentBlock(), ethash.NewFaker(), db, 1, func(i int, gen *BlockGen) {}) if _, err := proBc.InsertChain(blocks); err == nil { t.Fatalf("pro-fork chain accepted contra-fork block: %v", blocks[0]) @@ -124,6 +130,9 @@ func TestDAOForkRangeExtradata(t *testing.T) { if _, err := bc.InsertChain(blocks); err != nil { t.Fatalf("failed to import contra-fork chain for expansion: %v", err) } + if err := bc.trieMemPool.Commit(bc.CurrentHeader().Root, db); err != nil { + t.Fatalf("failed to commit contra-fork head for expansion: %v", err) + } blocks, _ = GenerateChain(&proConf, conBc.CurrentBlock(), ethash.NewFaker(), db, 1, func(i int, gen *BlockGen) {}) if _, err := conBc.InsertChain(blocks); err != nil { t.Fatalf("contra-fork chain didn't accept pro-fork block post-fork: %v", err) @@ -141,6 +150,9 @@ func TestDAOForkRangeExtradata(t *testing.T) { if _, err := bc.InsertChain(blocks); err != nil { t.Fatalf("failed to import pro-fork chain for expansion: %v", err) } + if err := bc.trieMemPool.Commit(bc.CurrentHeader().Root, db); err != nil { + t.Fatalf("failed to commit pro-fork head for expansion: %v", err) + } blocks, _ = GenerateChain(&conConf, proBc.CurrentBlock(), ethash.NewFaker(), db, 1, func(i int, gen *BlockGen) {}) if _, err := proBc.InsertChain(blocks); err != nil { t.Fatalf("pro-fork chain didn't accept contra-fork block post-fork: %v", err) diff --git a/trie/mempool.go b/trie/mempool.go index 7cfb53d8b2..0059599198 100644 --- a/trie/mempool.go +++ b/trie/mempool.go @@ -132,13 +132,15 @@ func (pool *MemPool) dereference(node common.Hash, parent common.Hash) { // Commit iterates over all the children of a particular node, writes them out // to disk, forcefully tearing down all references in both directions. -func (pool *MemPool) Commit(node common.Hash, db DatabaseWriter) { +func (pool *MemPool) Commit(node common.Hash, db DatabaseWriter) error { pool.lock.Lock() defer pool.lock.Unlock() nodes, storage, start := len(pool.cache), pool.size, time.Now() - pool.commit(node, db) - + if err := pool.commit(node, db); err != nil { + log.Error("Failed to commit trie from mempool", "err", err) + return err + } log.Debug("Committed trie from memory pool", "nodes", nodes-len(pool.cache), "size", storage-pool.size, "time", time.Since(start), "gcnodes", pool.gcnodes, "gcsize", pool.gcsize, "gctime", pool.gctime, "livenodes", len(pool.cache), "livesize", pool.size) @@ -152,23 +154,28 @@ func (pool *MemPool) Commit(node common.Hash, db DatabaseWriter) { break } } + return nil } // commit is the private locked version of Commit. -func (pool *MemPool) commit(node common.Hash, db DatabaseWriter) { +func (pool *MemPool) commit(node common.Hash, db DatabaseWriter) error { // If the node does not exist, it's a previously comitted node. blob, ok := pool.cache[node] if !ok { - return + return nil } for child := range pool.children[node] { - pool.commit(child, db) + if err := pool.commit(child, db); err != nil { + return err + } + } + if err := db.Put(node[:], blob); err != nil { + return err } - db.Put(node[:], blob) - delete(pool.cache, node) delete(pool.parents, node) delete(pool.children, node) pool.size -= common.StorageSize(common.HashLength + len(blob)) + return nil }