From 28b4d4e2f8ad82c61e078f449c28c5f182923b7a Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Tue, 24 Nov 2020 12:07:58 +0300 Subject: [PATCH] core: remove old blocks and transactions Remove blocks with `height <= current height - MaxTraceableBlocks` together with transactions. --- pkg/config/protocol_config.go | 2 ++ pkg/core/blockchain.go | 12 ++++++++++ pkg/core/dao/dao.go | 41 +++++++++++++++++++++++++++++++++++ 3 files changed, 55 insertions(+) diff --git a/pkg/config/protocol_config.go b/pkg/config/protocol_config.go index c934a5a7a..1ac2f1c80 100644 --- a/pkg/config/protocol_config.go +++ b/pkg/config/protocol_config.go @@ -13,6 +13,8 @@ type ( // If true, DB size will be smaller, but older roots won't be accessible. // This value should remain the same for the same database. KeepOnlyLatestState bool `yaml:"KeepOnlyLatestState"` + // RemoveUntraceableBlocks specifies if old blocks should be removed. + RemoveUntraceableBlocks bool `yaml:"RemoveUntraceableBlocks"` // MaxTraceableBlocks is the length of the chain accessible to smart contracts. MaxTraceableBlocks uint32 `yaml:"MaxTraceableBlocks"` // P2PSigExtensions enables additional signature-related transaction attributes diff --git a/pkg/core/blockchain.go b/pkg/core/blockchain.go index d6a33f9d8..bc6464c86 100644 --- a/pkg/core/blockchain.go +++ b/pkg/core/blockchain.go @@ -697,6 +697,18 @@ func (bc *Blockchain) storeBlock(block *block.Block, txpool *mempool.Pool) error if bc.config.SaveStorageBatch { bc.lastBatch = cache.DAO.GetBatch() } + if bc.config.RemoveUntraceableBlocks { + if block.Index > bc.config.MaxTraceableBlocks { + index := block.Index - bc.config.MaxTraceableBlocks // is at least 1 + err := cache.DeleteBlock(bc.headerHashes[index], writeBuf) + if err != nil { + bc.log.Warn("error while removing old block", + zap.Uint32("index", index), + zap.Error(err)) + } + writeBuf.Reset() + } + } bc.lock.Lock() _, err = cache.Persist() diff --git a/pkg/core/dao/dao.go b/pkg/core/dao/dao.go index bdbb70a25..48ae63b95 100644 --- a/pkg/core/dao/dao.go +++ b/pkg/core/dao/dao.go @@ -32,6 +32,7 @@ var ( type DAO interface { AppendAppExecResult(aer *state.AppExecResult, buf *io.BufBinWriter) error AppendNEP17Transfer(acc util.Uint160, index uint32, tr *state.NEP17Transfer) (bool, error) + DeleteBlock(h util.Uint256, buf *io.BufBinWriter) error DeleteContractState(hash util.Uint160) error DeleteStorageItem(id int32, key []byte) error GetAndDecode(entity io.Serializable, key []byte) error @@ -672,6 +673,46 @@ func (dao *Simple) StoreAsBlock(block *block.Block, buf *io.BufBinWriter) error return dao.Store.Put(key, buf.Bytes()) } +// DeleteBlock removes block from dao. +func (dao *Simple) DeleteBlock(h util.Uint256, w *io.BufBinWriter) error { + batch := dao.Store.Batch() + key := make([]byte, util.Uint256Size+1) + key[0] = byte(storage.DataBlock) + copy(key[1:], h.BytesBE()) + bs, err := dao.Store.Get(key) + if err != nil { + return err + } + + b, err := block.NewBlockFromTrimmedBytes(dao.network, dao.stateRootInHeader, bs) + if err != nil { + return err + } + + if w == nil { + w = io.NewBufBinWriter() + } + b.Header().EncodeBinary(w.BinWriter) + if w.Err != nil { + return w.Err + } + batch.Put(key, w.Bytes()) + + key[0] = byte(storage.DataTransaction) + for _, tx := range b.Transactions { + copy(key[1:], tx.Hash().BytesBE()) + batch.Delete(key) + key[0] = byte(storage.STNotification) + batch.Delete(key) + } + + key[0] = byte(storage.STNotification) + copy(key[1:], h.BytesBE()) + batch.Delete(key) + + return dao.Store.PutBatch(batch) +} + // StoreAsCurrentBlock stores a hash of the given block with prefix // SYSCurrentBlock. It can reuse given buffer for the purpose of value // serialization.