aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMission Liao <mission.liao@dexon.org>2018-12-13 18:15:03 +0800
committerGitHub <noreply@github.com>2018-12-13 18:15:03 +0800
commit155e31175aeaa3685c57383e386c6e62c46318ef (patch)
tree58333399ca2e08bc856e3c23ecca7eefe3c5db5e
parent0ead4a7c012af9ddaa4a934729e216539d2caeb1 (diff)
downloaddexon-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
-rw-r--r--core/consensus.go18
-rw-r--r--core/consensus_test.go1
-rw-r--r--core/db/interfaces.go11
-rw-r--r--core/db/level-db.go118
-rw-r--r--core/db/level-db_test.go48
-rw-r--r--core/db/memory.go57
-rw-r--r--core/db/memory_test.go46
-rw-r--r--core/syncer/consensus.go24
-rw-r--r--core/test/blocks-generator_test.go2
-rw-r--r--core/test/utils.go31
-rw-r--r--integration_test/consensus_test.go1
11 files changed, 282 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
+}
diff --git a/integration_test/consensus_test.go b/integration_test/consensus_test.go
index 698305c..08a1d10 100644
--- a/integration_test/consensus_test.go
+++ b/integration_test/consensus_test.go
@@ -103,6 +103,7 @@ func (s *ConsensusTestSuite) setupNodes(
func (s *ConsensusTestSuite) verifyNodes(nodes map[types.NodeID]*node) {
for ID, node := range nodes {
+ s.Require().NoError(test.VerifyDB(node.db))
s.Require().NoError(node.app.Verify())
for otherID, otherNode := range nodes {
if ID == otherID {