diff options
author | Mission Liao <mission.liao@dexon.org> | 2018-12-13 18:15:03 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-12-13 18:15:03 +0800 |
commit | 155e31175aeaa3685c57383e386c6e62c46318ef (patch) | |
tree | 58333399ca2e08bc856e3c23ecca7eefe3c5db5e /core | |
parent | 0ead4a7c012af9ddaa4a934729e216539d2caeb1 (diff) | |
download | dexon-consensus-155e31175aeaa3685c57383e386c6e62c46318ef.tar dexon-consensus-155e31175aeaa3685c57383e386c6e62c46318ef.tar.gz dexon-consensus-155e31175aeaa3685c57383e386c6e62c46318ef.tar.bz2 dexon-consensus-155e31175aeaa3685c57383e386c6e62c46318ef.tar.lz dexon-consensus-155e31175aeaa3685c57383e386c6e62c46318ef.tar.xz dexon-consensus-155e31175aeaa3685c57383e386c6e62c46318ef.tar.zst dexon-consensus-155e31175aeaa3685c57383e386c6e62c46318ef.zip |
db: cache compaction chain tip in db (#369)
* Replace JSON with RLP in levelDB implementation.
* Make sure blocks to sync following compaction chain tip
Diffstat (limited to 'core')
-rw-r--r-- | core/consensus.go | 18 | ||||
-rw-r--r-- | core/consensus_test.go | 1 | ||||
-rw-r--r-- | core/db/interfaces.go | 11 | ||||
-rw-r--r-- | core/db/level-db.go | 118 | ||||
-rw-r--r-- | core/db/level-db_test.go | 48 | ||||
-rw-r--r-- | core/db/memory.go | 57 | ||||
-rw-r--r-- | core/db/memory_test.go | 46 | ||||
-rw-r--r-- | core/syncer/consensus.go | 24 | ||||
-rw-r--r-- | core/test/blocks-generator_test.go | 2 | ||||
-rw-r--r-- | core/test/utils.go | 31 |
10 files changed, 281 insertions, 75 deletions
diff --git a/core/consensus.go b/core/consensus.go index a6d8037..a0336ed 100644 --- a/core/consensus.go +++ b/core/consensus.go @@ -1003,6 +1003,14 @@ func (con *Consensus) preProcessBlock(b *types.Block) (err error) { // deliverBlock deliver a block to application layer. func (con *Consensus) deliverBlock(b *types.Block) { + if err := con.db.UpdateBlock(*b); err != nil { + panic(err) + } + if err := con.db.PutCompactionChainTipInfo( + b.Hash, b.Finalization.Height); err != nil { + panic(err) + } + con.cfgModule.untouchTSigHash(b.Hash) con.logger.Debug("Calling Application.BlockDelivered", "block", b) con.app.BlockDelivered(b.Hash, b.Position, b.Finalization.Clone()) if b.Position.Round == con.roundToNotify { @@ -1032,6 +1040,9 @@ func (con *Consensus) deliverBlock(b *types.Block) { con.roundToNotify, b.Finalization.Height) con.roundToNotify++ } + if con.debugApp != nil { + con.debugApp.BlockReady(b.Hash) + } } // processBlock is the entry point to submit one block to a Consensus instance. @@ -1072,14 +1083,7 @@ func (con *Consensus) processBlock(block *types.Block) (err error) { "delivered", con.ccModule.lastDeliveredBlock(), "pending", con.ccModule.lastPendingBlock()) for _, b := range deliveredBlocks { - if err = con.db.UpdateBlock(*b); err != nil { - panic(err) - } - con.cfgModule.untouchTSigHash(b.Hash) con.deliverBlock(b) - if con.debugApp != nil { - con.debugApp.BlockReady(b.Hash) - } } if err = con.lattice.PurgeBlocks(deliveredBlocks); err != nil { return diff --git a/core/consensus_test.go b/core/consensus_test.go index 16efae0..84df645 100644 --- a/core/consensus_test.go +++ b/core/consensus_test.go @@ -451,6 +451,7 @@ func (s *ConsensusTestSuite) TestSimpleDeliverBlock() { nb.wait() } verify(obj.app) + req.NoError(test.VerifyDB(obj.con.db)) } } diff --git a/core/db/interfaces.go b/core/db/interfaces.go index 5e13dc6..3ffba4a 100644 --- a/core/db/interfaces.go +++ b/core/db/interfaces.go @@ -38,6 +38,11 @@ var ( ErrClosed = fmt.Errorf("db closed") // ErrNotImplemented is the error that some interface is not implemented. ErrNotImplemented = fmt.Errorf("not implemented") + // ErrInvalidCompactionChainTipHeight means the newly updated height of + // the tip of compaction chain is invalid, usually means it's smaller than + // current cached one. + ErrInvalidCompactionChainTipHeight = fmt.Errorf( + "invalid compaction chain tip height") ) // Database is the interface for a Database. @@ -55,12 +60,18 @@ type Reader interface { HasBlock(hash common.Hash) bool GetBlock(hash common.Hash) (types.Block, error) GetAllBlocks() (BlockIterator, error) + + // GetCompactionChainTipInfo returns the block hash and finalization height + // of the tip block of compaction chain. Empty hash and zero height means + // the compaction chain is empty. + GetCompactionChainTipInfo() (common.Hash, uint64) } // Writer defines the interface for writing blocks into DB. type Writer interface { UpdateBlock(block types.Block) error PutBlock(block types.Block) error + PutCompactionChainTipInfo(common.Hash, uint64) error } // BlockIterator defines an iterator on blocks hold diff --git a/core/db/level-db.go b/core/db/level-db.go index 6983d3a..238c38e 100644 --- a/core/db/level-db.go +++ b/core/db/level-db.go @@ -18,14 +18,23 @@ package db import ( - "encoding/json" - "github.com/syndtr/goleveldb/leveldb" "github.com/dexon-foundation/dexon-consensus/common" "github.com/dexon-foundation/dexon-consensus/core/types" + "github.com/dexon-foundation/dexon/rlp" +) + +var ( + blockKeyPrefix = []byte("b-") + compactionChainTipInfoKey = []byte("cc-tip") ) +type compactionChainTipInfo struct { + Height uint64 `json:"height"` + Hash common.Hash `json:"hash"` +} + // LevelDBBackedDB is a leveldb backed DB implementation. type LevelDBBackedDB struct { db *leveldb.DB @@ -50,7 +59,7 @@ func (lvl *LevelDBBackedDB) Close() error { // HasBlock implements the Reader.Has method. func (lvl *LevelDBBackedDB) HasBlock(hash common.Hash) bool { - exists, err := lvl.db.Has([]byte(hash[:]), nil) + exists, err := lvl.internalHasBlock(lvl.getBlockKey(hash)) if err != nil { // TODO(missionliao): Modify the interface to return error. panic(err) @@ -58,18 +67,25 @@ func (lvl *LevelDBBackedDB) HasBlock(hash common.Hash) bool { return exists } +func (lvl *LevelDBBackedDB) internalHasBlock(key []byte) (bool, error) { + exists, err := lvl.db.Has(key, nil) + if err != nil { + return false, err + } + return exists, nil +} + // GetBlock implements the Reader.GetBlock method. func (lvl *LevelDBBackedDB) GetBlock( hash common.Hash) (block types.Block, err error) { - - queried, err := lvl.db.Get([]byte(hash[:]), nil) + queried, err := lvl.db.Get(lvl.getBlockKey(hash), nil) if err != nil { if err == leveldb.ErrNotFound { err = ErrBlockDoesNotExist } return } - err = json.Unmarshal(queried, &block) + err = rlp.DecodeBytes(queried, &block) if err != nil { return } @@ -80,20 +96,20 @@ func (lvl *LevelDBBackedDB) GetBlock( func (lvl *LevelDBBackedDB) UpdateBlock(block types.Block) (err error) { // NOTE: we didn't handle changes of block hash (and it // should not happen). - marshaled, err := json.Marshal(&block) + marshaled, err := rlp.EncodeToBytes(&block) if err != nil { return } - - if !lvl.HasBlock(block.Hash) { + blockKey := lvl.getBlockKey(block.Hash) + exists, err := lvl.internalHasBlock(blockKey) + if err != nil { + return + } + if !exists { err = ErrBlockDoesNotExist return } - err = lvl.db.Put( - []byte(block.Hash[:]), - marshaled, - nil) - if err != nil { + if err = lvl.db.Put(blockKey, marshaled, nil); err != nil { return } return @@ -101,19 +117,20 @@ func (lvl *LevelDBBackedDB) UpdateBlock(block types.Block) (err error) { // PutBlock implements the Writer.PutBlock method. func (lvl *LevelDBBackedDB) PutBlock(block types.Block) (err error) { - marshaled, err := json.Marshal(&block) + marshaled, err := rlp.EncodeToBytes(&block) + if err != nil { + return + } + blockKey := lvl.getBlockKey(block.Hash) + exists, err := lvl.internalHasBlock(blockKey) if err != nil { return } - if lvl.HasBlock(block.Hash) { + if exists { err = ErrBlockExists return } - err = lvl.db.Put( - []byte(block.Hash[:]), - marshaled, - nil) - if err != nil { + if err = lvl.db.Put(blockKey, marshaled, nil); err != nil { return } return @@ -125,3 +142,62 @@ func (lvl *LevelDBBackedDB) GetAllBlocks() (BlockIterator, error) { // TODO (mission): Implement this part via goleveldb's iterator. return nil, ErrNotImplemented } + +// PutCompactionChainTipInfo saves tip of compaction chain into the database. +func (lvl *LevelDBBackedDB) PutCompactionChainTipInfo( + blockHash common.Hash, height uint64) error { + marshaled, err := rlp.EncodeToBytes(&compactionChainTipInfo{ + Hash: blockHash, + Height: height, + }) + if err != nil { + return err + } + // Check current cached tip info to make sure the one to be updated is + // valid. + info, err := lvl.internalGetCompactionChainTipInfo() + if err != nil { + return err + } + if info.Height >= height { + return ErrInvalidCompactionChainTipHeight + } + if err = lvl.db.Put(compactionChainTipInfoKey, marshaled, nil); err != nil { + return err + } + return nil +} + +func (lvl *LevelDBBackedDB) internalGetCompactionChainTipInfo() ( + info compactionChainTipInfo, err error) { + queried, err := lvl.db.Get(compactionChainTipInfoKey, nil) + if err != nil { + if err == leveldb.ErrNotFound { + err = nil + } + return + } + if err = rlp.DecodeBytes(queried, &info); err != nil { + return + } + return +} + +// GetCompactionChainTipInfo get the tip info of compaction chain into the +// database. +func (lvl *LevelDBBackedDB) GetCompactionChainTipInfo() ( + hash common.Hash, height uint64) { + info, err := lvl.internalGetCompactionChainTipInfo() + if err != nil { + panic(err) + } + hash, height = info.Hash, info.Height + return +} + +func (lvl *LevelDBBackedDB) getBlockKey(hash common.Hash) (ret []byte) { + ret = make([]byte, len(blockKeyPrefix)+len(hash[:])) + copy(ret, blockKeyPrefix) + copy(ret[len(blockKeyPrefix):], hash[:]) + return +} diff --git a/core/db/level-db_test.go b/core/db/level-db_test.go index 1335d5d..69c8f07 100644 --- a/core/db/level-db_test.go +++ b/core/db/level-db_test.go @@ -37,12 +37,12 @@ type LevelDBTestSuite struct { func (s *LevelDBTestSuite) TestBasicUsage() { dbName := fmt.Sprintf("test-db-%v.db", time.Now().UTC()) dbInst, err := NewLevelDBBackedDB(dbName) - s.Require().Nil(err) + s.Require().NoError(err) defer func(dbName string) { err = dbInst.Close() - s.Nil(err) + s.NoError(err) err = os.RemoveAll(dbName) - s.Nil(err) + s.NoError(err) }(dbName) // Queried something from an empty database. @@ -64,11 +64,11 @@ func (s *LevelDBTestSuite) TestBasicUsage() { // Put to create a new record should just work fine. err = dbInst.PutBlock(block1) - s.Nil(err) + s.NoError(err) // Get it back should work fine. queried, err := dbInst.GetBlock(block1.Hash) - s.Nil(err) + s.NoError(err) s.Equal(queried.ProposerID, block1.ProposerID) // Test Update. @@ -76,24 +76,24 @@ func (s *LevelDBTestSuite) TestBasicUsage() { queried.Timestamp = now err = dbInst.UpdateBlock(queried) - s.Nil(err) + s.NoError(err) // Try to get it back via NodeID and height. queried, err = dbInst.GetBlock(block1.Hash) - s.Nil(err) + s.NoError(err) s.Equal(now, queried.Timestamp) } func (s *LevelDBTestSuite) TestSyncIndex() { dbName := fmt.Sprintf("test-db-%v-si.db", time.Now().UTC()) dbInst, err := NewLevelDBBackedDB(dbName) - s.Require().Nil(err) + s.Require().NoError(err) defer func(dbName string) { err = dbInst.Close() - s.Nil(err) + s.NoError(err) err = os.RemoveAll(dbName) - s.Nil(err) + s.NoError(err) }(dbName) // Create some blocks. @@ -112,21 +112,43 @@ func (s *LevelDBTestSuite) TestSyncIndex() { // Save blocks to db. err = dbInst.Close() - s.Nil(err) + s.NoError(err) // Load back blocks(syncIndex is called). dbInst, err = NewLevelDBBackedDB(dbName) - s.Require().Nil(err) + s.Require().NoError(err) // Verify result. for _, block := range blocks { queried, err := dbInst.GetBlock(block.Hash) - s.Nil(err) + s.NoError(err) s.Equal(block.ProposerID, queried.ProposerID) s.Equal(block.Position.Height, queried.Position.Height) } } +func (s *LevelDBTestSuite) TestCompactionChainTipInfo() { + dbName := fmt.Sprintf("test-db-%v-cc-tip.db", time.Now().UTC()) + dbInst, err := NewLevelDBBackedDB(dbName) + s.Require().NoError(err) + defer func(dbName string) { + err = dbInst.Close() + s.NoError(err) + err = os.RemoveAll(dbName) + s.NoError(err) + }(dbName) + // Save some tip info. + hash := common.NewRandomHash() + s.Require().NoError(dbInst.PutCompactionChainTipInfo(hash, 123)) + // Get it back to check. + hashBack, height := dbInst.GetCompactionChainTipInfo() + s.Require().Equal(hash, hashBack) + s.Require().Equal(height, uint64(123)) + // Unable to put compaction chain tip info with lower height. + err = dbInst.PutCompactionChainTipInfo(hash, 122) + s.Require().IsType(err, ErrInvalidCompactionChainTipHeight) +} + func TestLevelDB(t *testing.T) { suite.Run(t, new(LevelDBTestSuite)) } diff --git a/core/db/memory.go b/core/db/memory.go index 4246e4f..568c64b 100644 --- a/core/db/memory.go +++ b/core/db/memory.go @@ -41,10 +41,13 @@ func (seq *blockSeqIterator) NextBlock() (types.Block, error) { // MemBackedDB is a memory backed DB implementation. type MemBackedDB struct { - blocksMutex sync.RWMutex - blockHashSequence common.Hashes - blocksByHash map[common.Hash]*types.Block - persistantFilePath string + blocksLock sync.RWMutex + blockHashSequence common.Hashes + blocksByHash map[common.Hash]*types.Block + compactionChainTipLock sync.RWMutex + compactionChainTipHash common.Hash + compactionChainTipHeight uint64 + persistantFilePath string } // NewMemBackedDB initialize a memory-backed database. @@ -87,8 +90,8 @@ func NewMemBackedDB(persistantFilePath ...string) ( // HasBlock returns wheter or not the DB has a block identified with the hash. func (m *MemBackedDB) HasBlock(hash common.Hash) bool { - m.blocksMutex.RLock() - defer m.blocksMutex.RUnlock() + m.blocksLock.RLock() + defer m.blocksLock.RUnlock() _, ok := m.blocksByHash[hash] return ok @@ -96,8 +99,8 @@ func (m *MemBackedDB) HasBlock(hash common.Hash) bool { // GetBlock returns a block given a hash. func (m *MemBackedDB) GetBlock(hash common.Hash) (types.Block, error) { - m.blocksMutex.RLock() - defer m.blocksMutex.RUnlock() + m.blocksLock.RLock() + defer m.blocksLock.RUnlock() return m.internalGetBlock(hash) } @@ -116,8 +119,8 @@ func (m *MemBackedDB) PutBlock(block types.Block) error { return ErrBlockExists } - m.blocksMutex.Lock() - defer m.blocksMutex.Unlock() + m.blocksLock.Lock() + defer m.blocksLock.Unlock() m.blockHashSequence = append(m.blockHashSequence, block.Hash) m.blocksByHash[block.Hash] = &block @@ -130,13 +133,35 @@ func (m *MemBackedDB) UpdateBlock(block types.Block) error { return ErrBlockDoesNotExist } - m.blocksMutex.Lock() - defer m.blocksMutex.Unlock() + m.blocksLock.Lock() + defer m.blocksLock.Unlock() m.blocksByHash[block.Hash] = &block return nil } +// PutCompactionChainTipInfo saves tip of compaction chain into the database. +func (m *MemBackedDB) PutCompactionChainTipInfo( + blockHash common.Hash, height uint64) error { + m.compactionChainTipLock.Lock() + defer m.compactionChainTipLock.Unlock() + if m.compactionChainTipHeight >= height { + return ErrInvalidCompactionChainTipHeight + } + m.compactionChainTipHeight = height + m.compactionChainTipHash = blockHash + return nil +} + +// GetCompactionChainTipInfo get the tip info of compaction chain into the +// database. +func (m *MemBackedDB) GetCompactionChainTipInfo() ( + hash common.Hash, height uint64) { + m.compactionChainTipLock.RLock() + defer m.compactionChainTipLock.RUnlock() + return m.compactionChainTipHash, m.compactionChainTipHeight +} + // Close implement Closer interface, which would release allocated resource. func (m *MemBackedDB) Close() (err error) { // Save internal state to a pretty-print json file. It's a temporary way @@ -145,8 +170,8 @@ func (m *MemBackedDB) Close() (err error) { return } - m.blocksMutex.RLock() - defer m.blocksMutex.RUnlock() + m.blocksLock.RLock() + defer m.blocksLock.RUnlock() toDump := struct { Sequence common.Hashes @@ -167,8 +192,8 @@ func (m *MemBackedDB) Close() (err error) { } func (m *MemBackedDB) getBlockByIndex(idx int) (types.Block, error) { - m.blocksMutex.RLock() - defer m.blocksMutex.RUnlock() + m.blocksLock.RLock() + defer m.blocksLock.RUnlock() if idx >= len(m.blockHashSequence) { return types.Block{}, ErrIterationFinished diff --git a/core/db/memory_test.go b/core/db/memory_test.go index 8fee582..7d18c3a 100644 --- a/core/db/memory_test.go +++ b/core/db/memory_test.go @@ -72,55 +72,55 @@ func (s *MemBackedDBTestSuite) TestSaveAndLoad() { // Make sure the file pointed by 'dbPath' doesn't exist. _, err := os.Stat(dbPath) - s.Require().NotNil(err) + s.Require().Error(err) dbInst, err := NewMemBackedDB(dbPath) - s.Require().Nil(err) + s.Require().NoError(err) s.Require().NotNil(dbInst) defer func() { if dbInst != nil { - s.Nil(os.Remove(dbPath)) + s.NoError(os.Remove(dbPath)) dbInst = nil } }() - s.Nil(dbInst.PutBlock(*s.b00)) - s.Nil(dbInst.PutBlock(*s.b01)) - s.Nil(dbInst.PutBlock(*s.b02)) - s.Nil(dbInst.Close()) + s.NoError(dbInst.PutBlock(*s.b00)) + s.NoError(dbInst.PutBlock(*s.b01)) + s.NoError(dbInst.PutBlock(*s.b02)) + s.NoError(dbInst.Close()) // Load the json file back to check if all inserted blocks // exists. dbInst, err = NewMemBackedDB(dbPath) - s.Require().Nil(err) + s.Require().NoError(err) s.Require().NotNil(dbInst) s.True(dbInst.HasBlock(s.b00.Hash)) s.True(dbInst.HasBlock(s.b01.Hash)) s.True(dbInst.HasBlock(s.b02.Hash)) - s.Nil(dbInst.Close()) + s.NoError(dbInst.Close()) } func (s *MemBackedDBTestSuite) TestIteration() { // Make sure the file pointed by 'dbPath' doesn't exist. dbInst, err := NewMemBackedDB() - s.Require().Nil(err) + s.Require().NoError(err) s.Require().NotNil(dbInst) // Setup database. - s.Nil(dbInst.PutBlock(*s.b00)) - s.Nil(dbInst.PutBlock(*s.b01)) - s.Nil(dbInst.PutBlock(*s.b02)) + s.NoError(dbInst.PutBlock(*s.b00)) + s.NoError(dbInst.PutBlock(*s.b01)) + s.NoError(dbInst.PutBlock(*s.b02)) // Check if we can iterate all 3 blocks. iter, err := dbInst.GetAllBlocks() - s.Require().Nil(err) + s.Require().NoError(err) touched := common.Hashes{} for { b, err := iter.NextBlock() if err == ErrIterationFinished { break } - s.Require().Nil(err) + s.Require().NoError(err) touched = append(touched, b.Hash) } s.Len(touched, 3) @@ -129,6 +129,22 @@ func (s *MemBackedDBTestSuite) TestIteration() { s.Contains(touched, s.b02.Hash) } +func (s *MemBackedDBTestSuite) TestCompactionChainTipInfo() { + dbInst, err := NewMemBackedDB() + s.Require().NoError(err) + s.Require().NotNil(dbInst) + // Save some tip info. + hash := common.NewRandomHash() + s.Require().NoError(dbInst.PutCompactionChainTipInfo(hash, 123)) + // Get it back to check. + hashBack, height := dbInst.GetCompactionChainTipInfo() + s.Require().Equal(hash, hashBack) + s.Require().Equal(height, uint64(123)) + // Unable to put compaction chain tip info with lower height. + err = dbInst.PutCompactionChainTipInfo(hash, 122) + s.Require().IsType(err, ErrInvalidCompactionChainTipHeight) +} + func TestMemBackedDB(t *testing.T) { suite.Run(t, new(MemBackedDBTestSuite)) } diff --git a/core/syncer/consensus.go b/core/syncer/consensus.go index 13a8873..901485e 100644 --- a/core/syncer/consensus.go +++ b/core/syncer/consensus.go @@ -42,6 +42,10 @@ var ( // ErrMismatchBlockHashSequence means the delivering sequence is not // correct, compared to finalized blocks. ErrMismatchBlockHashSequence = fmt.Errorf("mismatch block hash sequence") + // ErrInvalidSyncingFinalizationHeight raised when the blocks to sync is + // not following the compaction chain tip in database. + ErrInvalidSyncingFinalizationHeight = fmt.Errorf( + "invalid syncing finalization height") ) // Consensus is for syncing consensus module. @@ -394,6 +398,15 @@ func (con *Consensus) SyncBlocks( return nil, ErrInvalidBlockOrder } } + // Make sure the first block is the next block of current compaction chain + // tip in DB. + _, tipHeight := con.db.GetCompactionChainTipInfo() + if blocks[0].Finalization.Height != tipHeight+1 { + con.logger.Error("mismatched finalization height", + "now", blocks[0].Finalization.Height, + "expected", tipHeight+1) + return nil, ErrInvalidSyncingFinalizationHeight + } con.logger.Info("syncBlocks", "position", &blocks[0].Position, "final height", blocks[0].Finalization.Height, @@ -404,10 +417,19 @@ func (con *Consensus) SyncBlocks( for _, b := range blocks { // TODO(haoping) remove this if lattice puts blocks into db. if err := con.db.PutBlock(*b); err != nil { - if err != db.ErrBlockExists { + // A block might be put into db when confirmed by BA, but not + // finalized yet. + if err == db.ErrBlockExists { + err = con.db.UpdateBlock(*b) + } + if err != nil { return nil, err } } + if err := con.db.PutCompactionChainTipInfo( + b.Hash, b.Finalization.Height); err != nil { + return nil, err + } if err := con.processFinalizedBlock(b); err != nil { return nil, err } diff --git a/core/test/blocks-generator_test.go b/core/test/blocks-generator_test.go index bd7a5a2..78b609b 100644 --- a/core/test/blocks-generator_test.go +++ b/core/test/blocks-generator_test.go @@ -18,7 +18,6 @@ package test import ( - "fmt" "sort" "testing" "time" @@ -318,7 +317,6 @@ func (s *BlocksGeneratorTestSuite) TestConcateBlocksFromRounds() { totalAckCount += len(b.Acks) } // At least all blocks can ack some non-parent block. - fmt.Println(totalAckCount, totalBlockCount) req.True(totalAckCount/totalBlockCount >= 2) } diff --git a/core/test/utils.go b/core/test/utils.go index 56c5eac..6abd0b5 100644 --- a/core/test/utils.go +++ b/core/test/utils.go @@ -18,6 +18,7 @@ package test import ( + "errors" "fmt" "math" "net" @@ -26,6 +27,7 @@ import ( "github.com/dexon-foundation/dexon-consensus/common" "github.com/dexon-foundation/dexon-consensus/core/crypto" "github.com/dexon-foundation/dexon-consensus/core/crypto/ecdsa" + "github.com/dexon-foundation/dexon-consensus/core/db" "github.com/dexon-foundation/dexon-consensus/core/types" typesDKG "github.com/dexon-foundation/dexon-consensus/core/types/dkg" "github.com/dexon-foundation/dexon/rlp" @@ -170,3 +172,32 @@ func cloneBlockRandomnessResult(rand *types.BlockRandomnessResult) ( } return } + +var ( + // ErrCompactionChainTipBlockNotExists raised when the hash of compaction + // chain tip doesn't match a block in database. + ErrCompactionChainTipBlockNotExists = errors.New( + "compaction chain tip block not exists") + // ErrEmptyCompactionChainTipInfo raised when a compaction chain tip info + // is empty. + ErrEmptyCompactionChainTipInfo = errors.New( + "empty compaction chain tip info") + // ErrMismatchBlockHash raise when the hash for that block mismatched. + ErrMismatchBlockHash = errors.New("mismatched block hash") +) + +// VerifyDB check if a database is valid after test. +func VerifyDB(db db.Database) error { + hash, height := db.GetCompactionChainTipInfo() + if (hash == common.Hash{}) || height == 0 { + return ErrEmptyCompactionChainTipInfo + } + b, err := db.GetBlock(hash) + if err != nil { + return err + } + if b.Hash != hash { + return ErrMismatchBlockHash + } + return nil +} |