From d67f0df516df4bad8408cef44a8821b66ad36289 Mon Sep 17 00:00:00 2001 From: Anna Shaleva Date: Sun, 20 Nov 2022 20:55:45 +0300 Subject: [PATCH] core: reset block headers together with header height info We need to keep the headers information consistent with header batches and headers. This comit fixes the bug with failing blockchain initialization on recovering from state reset interrupted after the second stage (blocks/txs/AERs removal): ``` anna@kiwi:~/Documents/GitProjects/nspcc-dev/neo-go$ ./bin/neo-go db reset -t --height 83000 2022-11-20T16:28:29.437+0300 INFO MaxValidUntilBlockIncrement is not set or wrong, using default value {"MaxValidUntilBlockIncrement": 5760} 2022-11-20T16:28:29.440+0300 INFO restoring blockchain {"version": "0.2.6"} failed to create Blockchain instance: could not initialize blockchain: could not get header 1898cd356a4a2688ed1c6c7ba1fd6ba7d516959d8add3f8dd26232474d4539bd: key not found ``` --- pkg/core/blockchain.go | 12 ++++++++---- pkg/core/dao/dao.go | 30 +++++++++++------------------- 2 files changed, 19 insertions(+), 23 deletions(-) diff --git a/pkg/core/blockchain.go b/pkg/core/blockchain.go index 5696e6bd7..0f277b504 100644 --- a/pkg/core/blockchain.go +++ b/pkg/core/blockchain.go @@ -672,7 +672,7 @@ func (bc *Blockchain) Reset(height uint32) error { } func (bc *Blockchain) resetStateInternal(height uint32, stage stateChangeStage) error { - // Cache isn't yet initialized, so retrieve header right from DAO. + // Cache isn't yet initialized, so retrieve header height right from DAO. currHeight, err := bc.dao.GetCurrentBlockHeight() if err != nil { return fmt.Errorf("failed to retrieve current block height: %w", err) @@ -723,9 +723,10 @@ func (bc *Blockchain) resetStateInternal(height uint32, stage stateChangeStage) fallthrough case stateJumpStarted: bc.log.Info("trying to reset blocks, transactions and AERs") - // Remove headers/blocks/transactions/aers from currHeight down to height (not including height itself). - for i := height + 1; i <= hHeight; i++ { - err := cache.PurgeBlock(bc.headerHashes[i]) + // Remove blocks/transactions/aers from currHeight down to height (not including height itself). + // Keep headers for now, they'll be removed later. + for i := height + 1; i <= currHeight; i++ { + err := cache.DeleteBlock(bc.GetHeaderHash(int(i))) if err != nil { return fmt.Errorf("error while removing block %d: %w", i, err) } @@ -836,6 +837,9 @@ func (bc *Blockchain) resetStateInternal(height uint32, stage stateChangeStage) case newStorageItemsAdded: // Reset SYS-prefixed and IX-prefixed information. bc.log.Info("trying to reset headers information") + for i := height + 1; i <= hHeight; i++ { + cache.PurgeHeader(bc.GetHeaderHash(int(i))) + } cache.DeleteHeaderHashes(height+1, headerBatchCount) cache.StoreAsCurrentBlock(b) cache.PutCurrentHeader(b.Hash(), height) diff --git a/pkg/core/dao/dao.go b/pkg/core/dao/dao.go index 14f4df1d6..f802dab52 100644 --- a/pkg/core/dao/dao.go +++ b/pkg/core/dao/dao.go @@ -759,31 +759,15 @@ func (dao *Simple) StoreAsBlock(block *block.Block, aer1 *state.AppExecResult, a // DeleteBlock removes the block from dao. It's not atomic, so make sure you're // using private MemCached instance here. func (dao *Simple) DeleteBlock(h util.Uint256) error { - return dao.deleteBlock(h, true) -} - -// PurgeBlock completely removes specified block (or just block header) from dao. -// It differs from DeleteBlock in that it removes header anyway. It's not atomic, -// so make sure you're using private MemCached instance here. -func (dao *Simple) PurgeBlock(h util.Uint256) error { - return dao.deleteBlock(h, false) -} - -func (dao *Simple) deleteBlock(h util.Uint256, keepHeader bool) error { key := dao.makeExecutableKey(h) b, err := dao.getBlock(key) if err != nil { return err } - - if keepHeader { - err = dao.storeHeader(key, &b.Header) - if err != nil { - return err - } - } else { - dao.Store.Delete(key) + err = dao.storeHeader(key, &b.Header) + if err != nil { + return err } for _, tx := range b.Transactions { @@ -801,6 +785,14 @@ func (dao *Simple) deleteBlock(h util.Uint256, keepHeader bool) error { return nil } +// PurgeHeader completely removes specified header from dao. It differs from +// DeleteBlock in that it removes header anyway and does nothing except removing +// header. It does no checks for header existence. +func (dao *Simple) PurgeHeader(h util.Uint256) { + key := dao.makeExecutableKey(h) + dao.Store.Delete(key) +} + // StoreHeader saves the block header into the store. func (dao *Simple) StoreHeader(h *block.Header) error { return dao.storeHeader(dao.makeExecutableKey(h.Hash()), h)