From 06693fc13b451835ac460688903c7abb660710fb Mon Sep 17 00:00:00 2001 From: Mission Liao Date: Thu, 13 Dec 2018 09:55:14 +0800 Subject: db: rename blockdb to db (#367) * Rename blockdb package to db * Rename 'BlockDB' to 'DB' * Make all methods in db specific for ''block'. * Rename db.BlockDatabase to db.Database * Rename revealer to block-revealer * Rename test.Revealer to test.BlockRevealer --- core/blockdb/interfaces.go | 70 -------- core/blockdb/level-db.go | 127 -------------- core/blockdb/level-db_test.go | 132 --------------- core/blockdb/memory.go | 183 -------------------- core/blockdb/memory_test.go | 134 --------------- core/consensus.go | 12 +- core/consensus_test.go | 6 +- core/db/interfaces.go | 70 ++++++++ core/db/level-db.go | 127 ++++++++++++++ core/db/level-db_test.go | 132 +++++++++++++++ core/db/memory.go | 185 +++++++++++++++++++++ core/db/memory_test.go | 134 +++++++++++++++ core/lattice-data.go | 18 +- core/lattice-data_test.go | 36 ++-- core/lattice.go | 4 +- core/lattice_test.go | 26 +-- core/nonblocking.go | 4 +- core/syncer/consensus.go | 22 +-- core/test/block-revealer.go | 332 +++++++++++++++++++++++++++++++++++++ core/test/block-revealer_test.go | 222 +++++++++++++++++++++++++ core/test/blocks-generator.go | 22 +-- core/test/blocks-generator_test.go | 68 ++++---- core/test/interface.go | 8 +- core/test/revealer.go | 331 ------------------------------------ core/test/revealer_test.go | 222 ------------------------- core/test/stopper.go | 18 +- core/test/stopper_test.go | 54 +++--- core/total-ordering-syncer_test.go | 26 +-- core/total-ordering_test.go | 48 +++--- integration_test/consensus_test.go | 18 +- integration_test/node.go | 16 +- integration_test/utils.go | 8 +- simulation/node.go | 8 +- 33 files changed, 1414 insertions(+), 1409 deletions(-) delete mode 100644 core/blockdb/interfaces.go delete mode 100644 core/blockdb/level-db.go delete mode 100644 core/blockdb/level-db_test.go delete mode 100644 core/blockdb/memory.go delete mode 100644 core/blockdb/memory_test.go create mode 100644 core/db/interfaces.go create mode 100644 core/db/level-db.go create mode 100644 core/db/level-db_test.go create mode 100644 core/db/memory.go create mode 100644 core/db/memory_test.go create mode 100644 core/test/block-revealer.go create mode 100644 core/test/block-revealer_test.go delete mode 100644 core/test/revealer.go delete mode 100644 core/test/revealer_test.go diff --git a/core/blockdb/interfaces.go b/core/blockdb/interfaces.go deleted file mode 100644 index c856307..0000000 --- a/core/blockdb/interfaces.go +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright 2018 The dexon-consensus Authors -// This file is part of the dexon-consensus library. -// -// The dexon-consensus library is free software: you can redistribute it -// and/or modify it under the terms of the GNU Lesser General Public License as -// published by the Free Software Foundation, either version 3 of the License, -// or (at your option) any later version. -// -// The dexon-consensus library is distributed in the hope that it will be -// useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser -// General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the dexon-consensus library. If not, see -// . - -package blockdb - -import ( - "errors" - "fmt" - - "github.com/dexon-foundation/dexon-consensus/common" - "github.com/dexon-foundation/dexon-consensus/core/types" -) - -var ( - // ErrBlockExists is the error when block eixsts. - ErrBlockExists = errors.New("block exists") - // ErrBlockDoesNotExist is the error when block does not eixst. - ErrBlockDoesNotExist = errors.New("block does not exist") - // ErrIterationFinished is the error to check if the iteration is finished. - ErrIterationFinished = errors.New("iteration finished") - // ErrEmptyPath is the error when the required path is empty. - ErrEmptyPath = fmt.Errorf("empty path") - // ErrClosed is the error when using DB after it's closed. - ErrClosed = fmt.Errorf("db closed") - // ErrNotImplemented is the error that some interface is not implemented. - ErrNotImplemented = fmt.Errorf("not implemented") -) - -// BlockDatabase is the interface for a BlockDatabase. -type BlockDatabase interface { - Reader - Writer - - // Close allows database implementation able to - // release resource when finishing. - Close() error -} - -// Reader defines the interface for reading blocks into DB. -type Reader interface { - Has(hash common.Hash) bool - Get(hash common.Hash) (types.Block, error) - GetAll() (BlockIterator, error) -} - -// Writer defines the interface for writing blocks into DB. -type Writer interface { - Update(block types.Block) error - Put(block types.Block) error -} - -// BlockIterator defines an iterator on blocks hold -// in a DB. -type BlockIterator interface { - Next() (types.Block, error) -} diff --git a/core/blockdb/level-db.go b/core/blockdb/level-db.go deleted file mode 100644 index 76730fc..0000000 --- a/core/blockdb/level-db.go +++ /dev/null @@ -1,127 +0,0 @@ -// Copyright 2018 The dexon-consensus Authors -// This file is part of the dexon-consensus library. -// -// The dexon-consensus library is free software: you can redistribute it -// and/or modify it under the terms of the GNU Lesser General Public License as -// published by the Free Software Foundation, either version 3 of the License, -// or (at your option) any later version. -// -// The dexon-consensus library is distributed in the hope that it will be -// useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser -// General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the dexon-consensus library. If not, see -// . - -package blockdb - -import ( - "encoding/json" - - "github.com/syndtr/goleveldb/leveldb" - - "github.com/dexon-foundation/dexon-consensus/common" - "github.com/dexon-foundation/dexon-consensus/core/types" -) - -// LevelDBBackedBlockDB is a leveldb backed BlockDB implementation. -type LevelDBBackedBlockDB struct { - db *leveldb.DB -} - -// NewLevelDBBackedBlockDB initialize a leveldb-backed block database. -func NewLevelDBBackedBlockDB( - path string) (lvl *LevelDBBackedBlockDB, err error) { - - db, err := leveldb.OpenFile(path, nil) - if err != nil { - return - } - lvl = &LevelDBBackedBlockDB{db: db} - return -} - -// Close implement Closer interface, which would release allocated resource. -func (lvl *LevelDBBackedBlockDB) Close() error { - return lvl.db.Close() -} - -// Has implements the Reader.Has method. -func (lvl *LevelDBBackedBlockDB) Has(hash common.Hash) bool { - exists, err := lvl.db.Has([]byte(hash[:]), nil) - if err != nil { - // TODO(missionliao): Modify the interface to return error. - panic(err) - } - return exists -} - -// Get implements the Reader.Get method. -func (lvl *LevelDBBackedBlockDB) Get( - hash common.Hash) (block types.Block, err error) { - - queried, err := lvl.db.Get([]byte(hash[:]), nil) - if err != nil { - if err == leveldb.ErrNotFound { - err = ErrBlockDoesNotExist - } - return - } - err = json.Unmarshal(queried, &block) - if err != nil { - return - } - return -} - -// Update implements the Writer.Update method. -func (lvl *LevelDBBackedBlockDB) Update(block types.Block) (err error) { - // NOTE: we didn't handle changes of block hash (and it - // should not happen). - marshaled, err := json.Marshal(&block) - if err != nil { - return - } - - if !lvl.Has(block.Hash) { - err = ErrBlockDoesNotExist - return - } - err = lvl.db.Put( - []byte(block.Hash[:]), - marshaled, - nil) - if err != nil { - return - } - return -} - -// Put implements the Writer.Put method. -func (lvl *LevelDBBackedBlockDB) Put(block types.Block) (err error) { - marshaled, err := json.Marshal(&block) - if err != nil { - return - } - if lvl.Has(block.Hash) { - err = ErrBlockExists - return - } - err = lvl.db.Put( - []byte(block.Hash[:]), - marshaled, - nil) - if err != nil { - return - } - return -} - -// GetAll implements Reader.GetAll method, which allows callers -// to retrieve all blocks in DB. -func (lvl *LevelDBBackedBlockDB) GetAll() (BlockIterator, error) { - // TODO (mission): Implement this part via goleveldb's iterator. - return nil, ErrNotImplemented -} diff --git a/core/blockdb/level-db_test.go b/core/blockdb/level-db_test.go deleted file mode 100644 index eb47b9c..0000000 --- a/core/blockdb/level-db_test.go +++ /dev/null @@ -1,132 +0,0 @@ -// Copyright 2018 The dexon-consensus Authors -// This file is part of the dexon-consensus library. -// -// The dexon-consensus library is free software: you can redistribute it -// and/or modify it under the terms of the GNU Lesser General Public License as -// published by the Free Software Foundation, either version 3 of the License, -// or (at your option) any later version. -// -// The dexon-consensus library is distributed in the hope that it will be -// useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser -// General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the dexon-consensus library. If not, see -// . - -package blockdb - -import ( - "fmt" - "testing" - "time" - - "os" - - "github.com/stretchr/testify/suite" - - "github.com/dexon-foundation/dexon-consensus/common" - "github.com/dexon-foundation/dexon-consensus/core/types" -) - -type LevelDBTestSuite struct { - suite.Suite -} - -func (s *LevelDBTestSuite) TestBasicUsage() { - dbName := fmt.Sprintf("test-db-%v.db", time.Now().UTC()) - db, err := NewLevelDBBackedBlockDB(dbName) - s.Require().Nil(err) - defer func(dbName string) { - err = db.Close() - s.Nil(err) - err = os.RemoveAll(dbName) - s.Nil(err) - }(dbName) - - // Queried something from an empty database. - hash1 := common.NewRandomHash() - _, err = db.Get(hash1) - s.Equal(ErrBlockDoesNotExist, err) - - // Update on an empty database should not success. - node1 := types.NodeID{Hash: common.NewRandomHash()} - block1 := types.Block{ - ProposerID: node1, - Hash: hash1, - Position: types.Position{ - Height: 1, - }, - } - err = db.Update(block1) - s.Equal(ErrBlockDoesNotExist, err) - - // Put to create a new record should just work fine. - err = db.Put(block1) - s.Nil(err) - - // Get it back should work fine. - queried, err := db.Get(block1.Hash) - s.Nil(err) - s.Equal(queried.ProposerID, block1.ProposerID) - - // Test Update. - now := time.Now().UTC() - queried.Timestamp = now - - err = db.Update(queried) - s.Nil(err) - - // Try to get it back via NodeID and height. - queried, err = db.Get(block1.Hash) - - s.Nil(err) - s.Equal(now, queried.Timestamp) -} - -func (s *LevelDBTestSuite) TestSyncIndex() { - dbName := fmt.Sprintf("test-db-%v-si.db", time.Now().UTC()) - db, err := NewLevelDBBackedBlockDB(dbName) - s.Require().Nil(err) - defer func(dbName string) { - err = db.Close() - s.Nil(err) - err = os.RemoveAll(dbName) - s.Nil(err) - }(dbName) - - // Create some blocks. - blocks := [10]types.Block{} - for i := range blocks { - block := types.Block{ - ProposerID: types.NodeID{Hash: common.NewRandomHash()}, - Hash: common.NewRandomHash(), - Position: types.Position{ - Height: uint64(i), - }, - } - db.Put(block) - blocks[i] = block - } - - // Save blocks to db. - err = db.Close() - s.Nil(err) - - // Load back blocks(syncIndex is called). - db, err = NewLevelDBBackedBlockDB(dbName) - s.Require().Nil(err) - - // Verify result. - for _, block := range blocks { - queried, err := db.Get(block.Hash) - s.Nil(err) - s.Equal(block.ProposerID, queried.ProposerID) - s.Equal(block.Position.Height, queried.Position.Height) - } -} - -func TestLevelDB(t *testing.T) { - suite.Run(t, new(LevelDBTestSuite)) -} diff --git a/core/blockdb/memory.go b/core/blockdb/memory.go deleted file mode 100644 index b45af22..0000000 --- a/core/blockdb/memory.go +++ /dev/null @@ -1,183 +0,0 @@ -// Copyright 2018 The dexon-consensus Authors -// This file is part of the dexon-consensus library. -// -// The dexon-consensus library is free software: you can redistribute it -// and/or modify it under the terms of the GNU Lesser General Public License as -// published by the Free Software Foundation, either version 3 of the License, -// or (at your option) any later version. -// -// The dexon-consensus library is distributed in the hope that it will be -// useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser -// General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the dexon-consensus library. If not, see -// . - -package blockdb - -import ( - "encoding/json" - "io/ioutil" - "os" - "sync" - - "github.com/dexon-foundation/dexon-consensus/common" - "github.com/dexon-foundation/dexon-consensus/core/types" -) - -type seqIterator struct { - idx int - db *MemBackedBlockDB -} - -func (seq *seqIterator) Next() (types.Block, error) { - curIdx := seq.idx - seq.idx++ - return seq.db.getByIndex(curIdx) -} - -// MemBackedBlockDB is a memory backed BlockDB implementation. -type MemBackedBlockDB struct { - blocksMutex sync.RWMutex - blockHashSequence common.Hashes - blocksByHash map[common.Hash]*types.Block - persistantFilePath string -} - -// NewMemBackedBlockDB initialize a memory-backed block database. -func NewMemBackedBlockDB(persistantFilePath ...string) (db *MemBackedBlockDB, err error) { - db = &MemBackedBlockDB{ - blockHashSequence: common.Hashes{}, - blocksByHash: make(map[common.Hash]*types.Block), - } - if len(persistantFilePath) == 0 || len(persistantFilePath[0]) == 0 { - return - } - db.persistantFilePath = persistantFilePath[0] - buf, err := ioutil.ReadFile(db.persistantFilePath) - if err != nil { - if !os.IsNotExist(err) { - // Something unexpected happened. - return - } - // It's expected behavior that file doesn't exists, we should not - // report error on it. - err = nil - return - } - - // Init this instance by file content, it's a temporary way - // to export those private field for JSON encoding. - toLoad := struct { - Sequence common.Hashes - ByHash map[common.Hash]*types.Block - }{} - err = json.Unmarshal(buf, &toLoad) - if err != nil { - return - } - db.blockHashSequence = toLoad.Sequence - db.blocksByHash = toLoad.ByHash - return -} - -// Has returns wheter or not the DB has a block identified with the hash. -func (m *MemBackedBlockDB) Has(hash common.Hash) bool { - m.blocksMutex.RLock() - defer m.blocksMutex.RUnlock() - - _, ok := m.blocksByHash[hash] - return ok -} - -// Get returns a block given a hash. -func (m *MemBackedBlockDB) Get(hash common.Hash) (types.Block, error) { - m.blocksMutex.RLock() - defer m.blocksMutex.RUnlock() - - return m.internalGet(hash) -} - -func (m *MemBackedBlockDB) internalGet(hash common.Hash) (types.Block, error) { - b, ok := m.blocksByHash[hash] - if !ok { - return types.Block{}, ErrBlockDoesNotExist - } - return *b, nil -} - -// Put inserts a new block into the database. -func (m *MemBackedBlockDB) Put(block types.Block) error { - if m.Has(block.Hash) { - return ErrBlockExists - } - - m.blocksMutex.Lock() - defer m.blocksMutex.Unlock() - - m.blockHashSequence = append(m.blockHashSequence, block.Hash) - m.blocksByHash[block.Hash] = &block - return nil -} - -// Update updates a block in the database. -func (m *MemBackedBlockDB) Update(block types.Block) error { - if !m.Has(block.Hash) { - return ErrBlockDoesNotExist - } - - m.blocksMutex.Lock() - defer m.blocksMutex.Unlock() - - m.blocksByHash[block.Hash] = &block - return nil -} - -// Close implement Closer interface, which would release allocated resource. -func (m *MemBackedBlockDB) Close() (err error) { - // Save internal state to a pretty-print json file. It's a temporary way - // to dump private file via JSON encoding. - if len(m.persistantFilePath) == 0 { - return - } - - m.blocksMutex.RLock() - defer m.blocksMutex.RUnlock() - - toDump := struct { - Sequence common.Hashes - ByHash map[common.Hash]*types.Block - }{ - Sequence: m.blockHashSequence, - ByHash: m.blocksByHash, - } - - // Dump to JSON with 2-space indent. - buf, err := json.Marshal(&toDump) - if err != nil { - return - } - - err = ioutil.WriteFile(m.persistantFilePath, buf, 0644) - return -} - -func (m *MemBackedBlockDB) getByIndex(idx int) (types.Block, error) { - m.blocksMutex.RLock() - defer m.blocksMutex.RUnlock() - - if idx >= len(m.blockHashSequence) { - return types.Block{}, ErrIterationFinished - } - - hash := m.blockHashSequence[idx] - return m.internalGet(hash) -} - -// GetAll implement Reader.GetAll method, which allows caller -// to retrieve all blocks in DB. -func (m *MemBackedBlockDB) GetAll() (BlockIterator, error) { - return &seqIterator{db: m}, nil -} diff --git a/core/blockdb/memory_test.go b/core/blockdb/memory_test.go deleted file mode 100644 index e0c0aad..0000000 --- a/core/blockdb/memory_test.go +++ /dev/null @@ -1,134 +0,0 @@ -// Copyright 2018 The dexon-consensus Authors -// This file is part of the dexon-consensus library. -// -// The dexon-consensus library is free software: you can redistribute it and/or -// modify it under the terms of the GNU Lesser General Public License as -// published by the Free Software Foundation, either version 3 of the License, -// or (at your option) any later version. -// -// The dexon-consensus library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the dexon-consensus library. If not, see -// . - -package blockdb - -import ( - "os" - "testing" - - "github.com/dexon-foundation/dexon-consensus/common" - "github.com/dexon-foundation/dexon-consensus/core/types" - "github.com/stretchr/testify/suite" -) - -type MemBackedBlockDBTestSuite struct { - suite.Suite - - v0 types.NodeID - b00, b01, b02 *types.Block -} - -func (s *MemBackedBlockDBTestSuite) SetupSuite() { - s.v0 = types.NodeID{Hash: common.NewRandomHash()} - - genesisHash := common.NewRandomHash() - s.b00 = &types.Block{ - ProposerID: s.v0, - ParentHash: genesisHash, - Hash: genesisHash, - Position: types.Position{ - Height: 0, - }, - Acks: common.NewSortedHashes(common.Hashes{}), - } - s.b01 = &types.Block{ - ProposerID: s.v0, - ParentHash: s.b00.Hash, - Hash: common.NewRandomHash(), - Position: types.Position{ - Height: 1, - }, - Acks: common.NewSortedHashes(common.Hashes{s.b00.Hash}), - } - s.b02 = &types.Block{ - ProposerID: s.v0, - ParentHash: s.b01.Hash, - Hash: common.NewRandomHash(), - Position: types.Position{ - Height: 2, - }, - Acks: common.NewSortedHashes(common.Hashes{s.b01.Hash}), - } -} - -func (s *MemBackedBlockDBTestSuite) TestSaveAndLoad() { - // Make sure we are able to save/load from file. - dbPath := "test-save-and-load.db" - - // Make sure the file pointed by 'dbPath' doesn't exist. - _, err := os.Stat(dbPath) - s.Require().NotNil(err) - - db, err := NewMemBackedBlockDB(dbPath) - s.Require().Nil(err) - s.Require().NotNil(db) - defer func() { - if db != nil { - s.Nil(os.Remove(dbPath)) - db = nil - } - }() - - s.Nil(db.Put(*s.b00)) - s.Nil(db.Put(*s.b01)) - s.Nil(db.Put(*s.b02)) - s.Nil(db.Close()) - - // Load the json file back to check if all inserted blocks - // exists. - db, err = NewMemBackedBlockDB(dbPath) - s.Require().Nil(err) - s.Require().NotNil(db) - s.True(db.Has(s.b00.Hash)) - s.True(db.Has(s.b01.Hash)) - s.True(db.Has(s.b02.Hash)) - s.Nil(db.Close()) -} - -func (s *MemBackedBlockDBTestSuite) TestIteration() { - // Make sure the file pointed by 'dbPath' doesn't exist. - db, err := NewMemBackedBlockDB() - s.Require().Nil(err) - s.Require().NotNil(db) - - // Setup database. - s.Nil(db.Put(*s.b00)) - s.Nil(db.Put(*s.b01)) - s.Nil(db.Put(*s.b02)) - - // Check if we can iterate all 3 blocks. - iter, err := db.GetAll() - s.Require().Nil(err) - touched := common.Hashes{} - for { - b, err := iter.Next() - if err == ErrIterationFinished { - break - } - s.Require().Nil(err) - touched = append(touched, b.Hash) - } - s.Len(touched, 3) - s.Contains(touched, s.b00.Hash) - s.Contains(touched, s.b01.Hash) - s.Contains(touched, s.b02.Hash) -} - -func TestMemBackedBlockDB(t *testing.T) { - suite.Run(t, new(MemBackedBlockDBTestSuite)) -} diff --git a/core/consensus.go b/core/consensus.go index 3f4443f..fdd0fb8 100644 --- a/core/consensus.go +++ b/core/consensus.go @@ -25,8 +25,8 @@ import ( "time" "github.com/dexon-foundation/dexon-consensus/common" - "github.com/dexon-foundation/dexon-consensus/core/blockdb" "github.com/dexon-foundation/dexon-consensus/core/crypto" + "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-consensus/core/utils" @@ -360,7 +360,7 @@ type Consensus struct { toSyncer *totalOrderingSyncer // Interfaces. - db blockdb.BlockDatabase + db db.Database app Application debugApp Debug gov Governance @@ -384,7 +384,7 @@ func NewConsensus( dMoment time.Time, app Application, gov Governance, - db blockdb.BlockDatabase, + db db.Database, network Network, prv crypto.PrivateKey, logger common.Logger) *Consensus { @@ -464,7 +464,7 @@ func NewConsensusFromSyncer( initRoundBeginTime time.Time, app Application, gov Governance, - db blockdb.BlockDatabase, + db db.Database, networkModule Network, prv crypto.PrivateKey, latticeModule *Lattice, @@ -1033,7 +1033,7 @@ func (con *Consensus) deliverBlock(b *types.Block) { // processBlock is the entry point to submit one block to a Consensus instance. func (con *Consensus) processBlock(block *types.Block) (err error) { - if err = con.db.Put(*block); err != nil && err != blockdb.ErrBlockExists { + if err = con.db.PutBlock(*block); err != nil && err != db.ErrBlockExists { return } con.lock.Lock() @@ -1069,7 +1069,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.Update(*b); err != nil { + if err = con.db.UpdateBlock(*b); err != nil { panic(err) } con.cfgModule.untouchTSigHash(b.Hash) diff --git a/core/consensus_test.go b/core/consensus_test.go index c1cdca8..16efae0 100644 --- a/core/consensus_test.go +++ b/core/consensus_test.go @@ -26,8 +26,8 @@ import ( "github.com/stretchr/testify/suite" "github.com/dexon-foundation/dexon-consensus/common" - "github.com/dexon-foundation/dexon-consensus/core/blockdb" "github.com/dexon-foundation/dexon-consensus/core/crypto" + "github.com/dexon-foundation/dexon-consensus/core/db" "github.com/dexon-foundation/dexon-consensus/core/test" "github.com/dexon-foundation/dexon-consensus/core/types" typesDKG "github.com/dexon-foundation/dexon-consensus/core/types/dkg" @@ -203,12 +203,12 @@ func (s *ConsensusTestSuite) prepareConsensus( *test.App, *Consensus) { app := test.NewApp(nil) - db, err := blockdb.NewMemBackedBlockDB() + dbInst, err := db.NewMemBackedDB() s.Require().NoError(err) nID := types.NewNodeID(prvKey.PublicKey()) network := conn.newNetwork(nID) con := NewConsensus( - dMoment, app, gov, db, network, prvKey, &common.NullLogger{}) + dMoment, app, gov, dbInst, network, prvKey, &common.NullLogger{}) con.ccModule.init(&types.Block{}) conn.setCon(nID, con) return app, con diff --git a/core/db/interfaces.go b/core/db/interfaces.go new file mode 100644 index 0000000..5e13dc6 --- /dev/null +++ b/core/db/interfaces.go @@ -0,0 +1,70 @@ +// Copyright 2018 The dexon-consensus Authors +// This file is part of the dexon-consensus library. +// +// The dexon-consensus library is free software: you can redistribute it +// and/or modify it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation, either version 3 of the License, +// or (at your option) any later version. +// +// The dexon-consensus library is distributed in the hope that it will be +// useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser +// General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the dexon-consensus library. If not, see +// . + +package db + +import ( + "errors" + "fmt" + + "github.com/dexon-foundation/dexon-consensus/common" + "github.com/dexon-foundation/dexon-consensus/core/types" +) + +var ( + // ErrBlockExists is the error when block eixsts. + ErrBlockExists = errors.New("block exists") + // ErrBlockDoesNotExist is the error when block does not eixst. + ErrBlockDoesNotExist = errors.New("block does not exist") + // ErrIterationFinished is the error to check if the iteration is finished. + ErrIterationFinished = errors.New("iteration finished") + // ErrEmptyPath is the error when the required path is empty. + ErrEmptyPath = fmt.Errorf("empty path") + // ErrClosed is the error when using DB after it's closed. + ErrClosed = fmt.Errorf("db closed") + // ErrNotImplemented is the error that some interface is not implemented. + ErrNotImplemented = fmt.Errorf("not implemented") +) + +// Database is the interface for a Database. +type Database interface { + Reader + Writer + + // Close allows database implementation able to + // release resource when finishing. + Close() error +} + +// Reader defines the interface for reading blocks into DB. +type Reader interface { + HasBlock(hash common.Hash) bool + GetBlock(hash common.Hash) (types.Block, error) + GetAllBlocks() (BlockIterator, error) +} + +// Writer defines the interface for writing blocks into DB. +type Writer interface { + UpdateBlock(block types.Block) error + PutBlock(block types.Block) error +} + +// BlockIterator defines an iterator on blocks hold +// in a DB. +type BlockIterator interface { + NextBlock() (types.Block, error) +} diff --git a/core/db/level-db.go b/core/db/level-db.go new file mode 100644 index 0000000..6983d3a --- /dev/null +++ b/core/db/level-db.go @@ -0,0 +1,127 @@ +// Copyright 2018 The dexon-consensus Authors +// This file is part of the dexon-consensus library. +// +// The dexon-consensus library is free software: you can redistribute it +// and/or modify it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation, either version 3 of the License, +// or (at your option) any later version. +// +// The dexon-consensus library is distributed in the hope that it will be +// useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser +// General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the dexon-consensus library. If not, see +// . + +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" +) + +// LevelDBBackedDB is a leveldb backed DB implementation. +type LevelDBBackedDB struct { + db *leveldb.DB +} + +// NewLevelDBBackedDB initialize a leveldb-backed database. +func NewLevelDBBackedDB( + path string) (lvl *LevelDBBackedDB, err error) { + + dbInst, err := leveldb.OpenFile(path, nil) + if err != nil { + return + } + lvl = &LevelDBBackedDB{db: dbInst} + return +} + +// Close implement Closer interface, which would release allocated resource. +func (lvl *LevelDBBackedDB) Close() error { + return lvl.db.Close() +} + +// HasBlock implements the Reader.Has method. +func (lvl *LevelDBBackedDB) HasBlock(hash common.Hash) bool { + exists, err := lvl.db.Has([]byte(hash[:]), nil) + if err != nil { + // TODO(missionliao): Modify the interface to return error. + panic(err) + } + return exists +} + +// 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) + if err != nil { + if err == leveldb.ErrNotFound { + err = ErrBlockDoesNotExist + } + return + } + err = json.Unmarshal(queried, &block) + if err != nil { + return + } + return +} + +// UpdateBlock implements the Writer.UpdateBlock method. +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) + if err != nil { + return + } + + if !lvl.HasBlock(block.Hash) { + err = ErrBlockDoesNotExist + return + } + err = lvl.db.Put( + []byte(block.Hash[:]), + marshaled, + nil) + if err != nil { + return + } + return +} + +// PutBlock implements the Writer.PutBlock method. +func (lvl *LevelDBBackedDB) PutBlock(block types.Block) (err error) { + marshaled, err := json.Marshal(&block) + if err != nil { + return + } + if lvl.HasBlock(block.Hash) { + err = ErrBlockExists + return + } + err = lvl.db.Put( + []byte(block.Hash[:]), + marshaled, + nil) + if err != nil { + return + } + return +} + +// GetAllBlocks implements Reader.GetAllBlocks method, which allows callers +// to retrieve all blocks in DB. +func (lvl *LevelDBBackedDB) GetAllBlocks() (BlockIterator, error) { + // TODO (mission): Implement this part via goleveldb's iterator. + return nil, ErrNotImplemented +} diff --git a/core/db/level-db_test.go b/core/db/level-db_test.go new file mode 100644 index 0000000..1335d5d --- /dev/null +++ b/core/db/level-db_test.go @@ -0,0 +1,132 @@ +// Copyright 2018 The dexon-consensus Authors +// This file is part of the dexon-consensus library. +// +// The dexon-consensus library is free software: you can redistribute it +// and/or modify it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation, either version 3 of the License, +// or (at your option) any later version. +// +// The dexon-consensus library is distributed in the hope that it will be +// useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser +// General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the dexon-consensus library. If not, see +// . + +package db + +import ( + "fmt" + "testing" + "time" + + "os" + + "github.com/stretchr/testify/suite" + + "github.com/dexon-foundation/dexon-consensus/common" + "github.com/dexon-foundation/dexon-consensus/core/types" +) + +type LevelDBTestSuite struct { + suite.Suite +} + +func (s *LevelDBTestSuite) TestBasicUsage() { + dbName := fmt.Sprintf("test-db-%v.db", time.Now().UTC()) + dbInst, err := NewLevelDBBackedDB(dbName) + s.Require().Nil(err) + defer func(dbName string) { + err = dbInst.Close() + s.Nil(err) + err = os.RemoveAll(dbName) + s.Nil(err) + }(dbName) + + // Queried something from an empty database. + hash1 := common.NewRandomHash() + _, err = dbInst.GetBlock(hash1) + s.Equal(ErrBlockDoesNotExist, err) + + // Update on an empty database should not success. + node1 := types.NodeID{Hash: common.NewRandomHash()} + block1 := types.Block{ + ProposerID: node1, + Hash: hash1, + Position: types.Position{ + Height: 1, + }, + } + err = dbInst.UpdateBlock(block1) + s.Equal(ErrBlockDoesNotExist, err) + + // Put to create a new record should just work fine. + err = dbInst.PutBlock(block1) + s.Nil(err) + + // Get it back should work fine. + queried, err := dbInst.GetBlock(block1.Hash) + s.Nil(err) + s.Equal(queried.ProposerID, block1.ProposerID) + + // Test Update. + now := time.Now().UTC() + queried.Timestamp = now + + err = dbInst.UpdateBlock(queried) + s.Nil(err) + + // Try to get it back via NodeID and height. + queried, err = dbInst.GetBlock(block1.Hash) + + s.Nil(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) + defer func(dbName string) { + err = dbInst.Close() + s.Nil(err) + err = os.RemoveAll(dbName) + s.Nil(err) + }(dbName) + + // Create some blocks. + blocks := [10]types.Block{} + for i := range blocks { + block := types.Block{ + ProposerID: types.NodeID{Hash: common.NewRandomHash()}, + Hash: common.NewRandomHash(), + Position: types.Position{ + Height: uint64(i), + }, + } + dbInst.PutBlock(block) + blocks[i] = block + } + + // Save blocks to db. + err = dbInst.Close() + s.Nil(err) + + // Load back blocks(syncIndex is called). + dbInst, err = NewLevelDBBackedDB(dbName) + s.Require().Nil(err) + + // Verify result. + for _, block := range blocks { + queried, err := dbInst.GetBlock(block.Hash) + s.Nil(err) + s.Equal(block.ProposerID, queried.ProposerID) + s.Equal(block.Position.Height, queried.Position.Height) + } +} + +func TestLevelDB(t *testing.T) { + suite.Run(t, new(LevelDBTestSuite)) +} diff --git a/core/db/memory.go b/core/db/memory.go new file mode 100644 index 0000000..4246e4f --- /dev/null +++ b/core/db/memory.go @@ -0,0 +1,185 @@ +// Copyright 2018 The dexon-consensus Authors +// This file is part of the dexon-consensus library. +// +// The dexon-consensus library is free software: you can redistribute it +// and/or modify it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation, either version 3 of the License, +// or (at your option) any later version. +// +// The dexon-consensus library is distributed in the hope that it will be +// useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser +// General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the dexon-consensus library. If not, see +// . + +package db + +import ( + "encoding/json" + "io/ioutil" + "os" + "sync" + + "github.com/dexon-foundation/dexon-consensus/common" + "github.com/dexon-foundation/dexon-consensus/core/types" +) + +type blockSeqIterator struct { + idx int + db *MemBackedDB +} + +// NextBlock implemenets BlockIterator.NextBlock method. +func (seq *blockSeqIterator) NextBlock() (types.Block, error) { + curIdx := seq.idx + seq.idx++ + return seq.db.getBlockByIndex(curIdx) +} + +// MemBackedDB is a memory backed DB implementation. +type MemBackedDB struct { + blocksMutex sync.RWMutex + blockHashSequence common.Hashes + blocksByHash map[common.Hash]*types.Block + persistantFilePath string +} + +// NewMemBackedDB initialize a memory-backed database. +func NewMemBackedDB(persistantFilePath ...string) ( + dbInst *MemBackedDB, err error) { + dbInst = &MemBackedDB{ + blockHashSequence: common.Hashes{}, + blocksByHash: make(map[common.Hash]*types.Block), + } + if len(persistantFilePath) == 0 || len(persistantFilePath[0]) == 0 { + return + } + dbInst.persistantFilePath = persistantFilePath[0] + buf, err := ioutil.ReadFile(dbInst.persistantFilePath) + if err != nil { + if !os.IsNotExist(err) { + // Something unexpected happened. + return + } + // It's expected behavior that file doesn't exists, we should not + // report error on it. + err = nil + return + } + + // Init this instance by file content, it's a temporary way + // to export those private field for JSON encoding. + toLoad := struct { + Sequence common.Hashes + ByHash map[common.Hash]*types.Block + }{} + err = json.Unmarshal(buf, &toLoad) + if err != nil { + return + } + dbInst.blockHashSequence = toLoad.Sequence + dbInst.blocksByHash = toLoad.ByHash + return +} + +// 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() + + _, ok := m.blocksByHash[hash] + return ok +} + +// GetBlock returns a block given a hash. +func (m *MemBackedDB) GetBlock(hash common.Hash) (types.Block, error) { + m.blocksMutex.RLock() + defer m.blocksMutex.RUnlock() + + return m.internalGetBlock(hash) +} + +func (m *MemBackedDB) internalGetBlock(hash common.Hash) (types.Block, error) { + b, ok := m.blocksByHash[hash] + if !ok { + return types.Block{}, ErrBlockDoesNotExist + } + return *b, nil +} + +// PutBlock inserts a new block into the database. +func (m *MemBackedDB) PutBlock(block types.Block) error { + if m.HasBlock(block.Hash) { + return ErrBlockExists + } + + m.blocksMutex.Lock() + defer m.blocksMutex.Unlock() + + m.blockHashSequence = append(m.blockHashSequence, block.Hash) + m.blocksByHash[block.Hash] = &block + return nil +} + +// UpdateBlock updates a block in the database. +func (m *MemBackedDB) UpdateBlock(block types.Block) error { + if !m.HasBlock(block.Hash) { + return ErrBlockDoesNotExist + } + + m.blocksMutex.Lock() + defer m.blocksMutex.Unlock() + + m.blocksByHash[block.Hash] = &block + return nil +} + +// 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 + // to dump private file via JSON encoding. + if len(m.persistantFilePath) == 0 { + return + } + + m.blocksMutex.RLock() + defer m.blocksMutex.RUnlock() + + toDump := struct { + Sequence common.Hashes + ByHash map[common.Hash]*types.Block + }{ + Sequence: m.blockHashSequence, + ByHash: m.blocksByHash, + } + + // Dump to JSON with 2-space indent. + buf, err := json.Marshal(&toDump) + if err != nil { + return + } + + err = ioutil.WriteFile(m.persistantFilePath, buf, 0644) + return +} + +func (m *MemBackedDB) getBlockByIndex(idx int) (types.Block, error) { + m.blocksMutex.RLock() + defer m.blocksMutex.RUnlock() + + if idx >= len(m.blockHashSequence) { + return types.Block{}, ErrIterationFinished + } + + hash := m.blockHashSequence[idx] + return m.internalGetBlock(hash) +} + +// GetAllBlocks implement Reader.GetAllBlocks method, which allows caller +// to retrieve all blocks in DB. +func (m *MemBackedDB) GetAllBlocks() (BlockIterator, error) { + return &blockSeqIterator{db: m}, nil +} diff --git a/core/db/memory_test.go b/core/db/memory_test.go new file mode 100644 index 0000000..8fee582 --- /dev/null +++ b/core/db/memory_test.go @@ -0,0 +1,134 @@ +// Copyright 2018 The dexon-consensus Authors +// This file is part of the dexon-consensus library. +// +// The dexon-consensus library is free software: you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation, either version 3 of the License, +// or (at your option) any later version. +// +// The dexon-consensus library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the dexon-consensus library. If not, see +// . + +package db + +import ( + "os" + "testing" + + "github.com/dexon-foundation/dexon-consensus/common" + "github.com/dexon-foundation/dexon-consensus/core/types" + "github.com/stretchr/testify/suite" +) + +type MemBackedDBTestSuite struct { + suite.Suite + + v0 types.NodeID + b00, b01, b02 *types.Block +} + +func (s *MemBackedDBTestSuite) SetupSuite() { + s.v0 = types.NodeID{Hash: common.NewRandomHash()} + + genesisHash := common.NewRandomHash() + s.b00 = &types.Block{ + ProposerID: s.v0, + ParentHash: genesisHash, + Hash: genesisHash, + Position: types.Position{ + Height: 0, + }, + Acks: common.NewSortedHashes(common.Hashes{}), + } + s.b01 = &types.Block{ + ProposerID: s.v0, + ParentHash: s.b00.Hash, + Hash: common.NewRandomHash(), + Position: types.Position{ + Height: 1, + }, + Acks: common.NewSortedHashes(common.Hashes{s.b00.Hash}), + } + s.b02 = &types.Block{ + ProposerID: s.v0, + ParentHash: s.b01.Hash, + Hash: common.NewRandomHash(), + Position: types.Position{ + Height: 2, + }, + Acks: common.NewSortedHashes(common.Hashes{s.b01.Hash}), + } +} + +func (s *MemBackedDBTestSuite) TestSaveAndLoad() { + // Make sure we are able to save/load from file. + dbPath := "test-save-and-load.db" + + // Make sure the file pointed by 'dbPath' doesn't exist. + _, err := os.Stat(dbPath) + s.Require().NotNil(err) + + dbInst, err := NewMemBackedDB(dbPath) + s.Require().Nil(err) + s.Require().NotNil(dbInst) + defer func() { + if dbInst != nil { + s.Nil(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()) + + // Load the json file back to check if all inserted blocks + // exists. + dbInst, err = NewMemBackedDB(dbPath) + s.Require().Nil(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()) +} + +func (s *MemBackedDBTestSuite) TestIteration() { + // Make sure the file pointed by 'dbPath' doesn't exist. + dbInst, err := NewMemBackedDB() + s.Require().Nil(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)) + + // Check if we can iterate all 3 blocks. + iter, err := dbInst.GetAllBlocks() + s.Require().Nil(err) + touched := common.Hashes{} + for { + b, err := iter.NextBlock() + if err == ErrIterationFinished { + break + } + s.Require().Nil(err) + touched = append(touched, b.Hash) + } + s.Len(touched, 3) + s.Contains(touched, s.b00.Hash) + s.Contains(touched, s.b01.Hash) + s.Contains(touched, s.b02.Hash) +} + +func TestMemBackedDB(t *testing.T) { + suite.Run(t, new(MemBackedDBTestSuite)) +} diff --git a/core/lattice-data.go b/core/lattice-data.go index f1ab2de..e55c0db 100644 --- a/core/lattice-data.go +++ b/core/lattice-data.go @@ -24,7 +24,7 @@ import ( "time" "github.com/dexon-foundation/dexon-consensus/common" - "github.com/dexon-foundation/dexon-consensus/core/blockdb" + "github.com/dexon-foundation/dexon-consensus/core/db" "github.com/dexon-foundation/dexon-consensus/core/types" "github.com/dexon-foundation/dexon-consensus/core/utils" ) @@ -105,8 +105,8 @@ func newLatticeDataConfig( // latticeData is a module for storing lattice. type latticeData struct { - // BlockDB for getting blocks purged in memory. - db blockdb.Reader + // DB for getting blocks purged in memory. + db db.Reader // chains stores chains' blocks and other info. chains []*chainStatus // blockByHash stores blocks, indexed by block hash. @@ -117,7 +117,7 @@ type latticeData struct { // newLatticeData creates a new latticeData instance. func newLatticeData( - db blockdb.Reader, + db db.Reader, dMoment time.Time, round uint64, config *types.Config) (data *latticeData) { @@ -146,7 +146,7 @@ func (data *latticeData) checkAckingRelations(b *types.Block) error { for _, hash := range b.Acks { bAck, err := data.findBlock(hash) if err != nil { - if err == blockdb.ErrBlockDoesNotExist { + if err == db.ErrBlockDoesNotExist { return &ErrAckingBlockNotExists{hash} } return err @@ -276,7 +276,7 @@ func (data *latticeData) addBlock( // Update lastAckPos. for _, ack := range block.Acks { if bAck, err = data.findBlock(ack); err != nil { - if err == blockdb.ErrBlockDoesNotExist { + if err == db.ErrBlockDoesNotExist { err = nil continue } @@ -298,7 +298,7 @@ func (data *latticeData) addBlock( allAckingBlockDelivered := true for _, ack := range tip.Acks { if bAck, err = data.findBlock(ack); err != nil { - if err == blockdb.ErrBlockDoesNotExist { + if err == db.ErrBlockDoesNotExist { err = nil allAckingBlockDelivered = false break @@ -525,7 +525,7 @@ func (data *latticeData) findBlock(h common.Hash) (b *types.Block, err error) { return } var tmpB types.Block - if tmpB, err = data.db.Get(h); err != nil { + if tmpB, err = data.db.GetBlock(h); err != nil { return } b = &tmpB @@ -632,7 +632,7 @@ func (s *chainStatus) addBlock(b *types.Block) { } // purgeBlock purges a block from cache, make sure this block is already saved -// in blockdb. +// in db. func (s *chainStatus) purgeBlock(b *types.Block) error { if b.Hash != s.blocks[0].Hash || s.nextOutputIndex <= 0 { return ErrPurgeNotDeliveredBlock diff --git a/core/lattice-data_test.go b/core/lattice-data_test.go index 8d37d21..1cd2c18 100644 --- a/core/lattice-data_test.go +++ b/core/lattice-data_test.go @@ -24,7 +24,7 @@ import ( "time" "github.com/dexon-foundation/dexon-consensus/common" - "github.com/dexon-foundation/dexon-consensus/core/blockdb" + "github.com/dexon-foundation/dexon-consensus/core/db" "github.com/dexon-foundation/dexon-consensus/core/test" "github.com/dexon-foundation/dexon-consensus/core/types" "github.com/dexon-foundation/dexon-consensus/core/utils" @@ -60,9 +60,9 @@ func (s *LatticeDataTestSuite) genTestCase1() ( NumChains: chainNum, MinBlockInterval: 2 * time.Nanosecond, } - db, err := blockdb.NewMemBackedBlockDB() + dbInst, err := db.NewMemBackedDB() req.NoError(err) - data = newLatticeData(db, now, 0, genesisConfig) + data = newLatticeData(dbInst, now, 0, genesisConfig) config := &types.Config{ RoundInterval: 1000 * time.Second, NumChains: chainNum, @@ -365,7 +365,7 @@ func (s *LatticeDataTestSuite) TestRandomlyGeneratedBlocks() { MinBlockInterval: 1 * time.Second, } // Prepare a randomly generated blocks. - db, err := blockdb.NewMemBackedBlockDB() + dbInst, err := db.NewMemBackedDB() req.NoError(err) gen := test.NewBlocksGenerator(&test.BlocksGeneratorConfig{ NumChains: genesisConfig.NumChains, @@ -375,28 +375,28 @@ func (s *LatticeDataTestSuite) TestRandomlyGeneratedBlocks() { 0, genesisTime, genesisTime.Add(genesisConfig.RoundInterval), - db)) - iter, err := db.GetAll() + dbInst)) + iter, err := dbInst.GetAllBlocks() req.NoError(err) // Setup a revealer that would reveal blocks randomly but still form // valid DAG without holes. - revealer, err := test.NewRandomDAGRevealer(iter) + revealer, err := test.NewRandomDAGBlockRevealer(iter) req.Nil(err) revealedHashesAsString := map[string]struct{}{} deliveredHashesAsString := map[string]struct{}{} for i := 0; i < repeat; i++ { - db, err := blockdb.NewMemBackedBlockDB() + dbInst, err := db.NewMemBackedDB() req.NoError(err) - data := newLatticeData(db, genesisTime, 0, genesisConfig) + data := newLatticeData(dbInst, genesisTime, 0, genesisConfig) deliveredHashes := common.Hashes{} revealedHashes := common.Hashes{} revealer.Reset() for { // Reveal next block. - b, err := revealer.Next() + b, err := revealer.NextBlock() if err != nil { - if err == blockdb.ErrIterationFinished { + if err == db.ErrIterationFinished { err = nil break } @@ -475,9 +475,9 @@ func (s *LatticeDataTestSuite) TestPrepareBlock() { NumChains: chainNum, MinBlockInterval: 1 * time.Second, } - db, err := blockdb.NewMemBackedBlockDB() + dbInst, err := db.NewMemBackedDB() req.NoError(err) - data := newLatticeData(db, time.Now().UTC(), 0, genesisConfig) + data := newLatticeData(dbInst, time.Now().UTC(), 0, genesisConfig) // Setup genesis blocks. b00 := s.prepareGenesisBlock(0) time.Sleep(minInterval) @@ -615,11 +615,11 @@ func (s *LatticeDataTestSuite) TestNumChainsChange() { } randObj = rand.New(rand.NewSource(time.Now().UnixNano())) ) - // Setup blockdb instance. - db, err := blockdb.NewMemBackedBlockDB() + // Setup db instance. + dbInst, err := db.NewMemBackedDB() req.NoError(err) // Set up latticeData instance. - lattice := newLatticeData(db, time.Now().UTC(), 0, configs[0]) + lattice := newLatticeData(dbInst, time.Now().UTC(), 0, configs[0]) req.NoError(lattice.appendConfig(1, configs[1])) req.NoError(lattice.appendConfig(2, configs[2])) req.NoError(lattice.appendConfig(3, configs[3])) @@ -689,9 +689,9 @@ func (s *LatticeDataTestSuite) TestAppendConfig() { round = uint64(5) cfg = &types.Config{NumChains: uint32(4)} ) - db, err := blockdb.NewMemBackedBlockDB() + dbInst, err := db.NewMemBackedDB() req.NoError(err) - latticeData := newLatticeData(db, now, round, cfg) + latticeData := newLatticeData(dbInst, now, round, cfg) err = latticeData.appendConfig(6, cfg) req.NoError(err) err = latticeData.appendConfig(10, cfg) diff --git a/core/lattice.go b/core/lattice.go index 6ea5f8b..8780bba 100644 --- a/core/lattice.go +++ b/core/lattice.go @@ -23,7 +23,7 @@ import ( "time" "github.com/dexon-foundation/dexon-consensus/common" - "github.com/dexon-foundation/dexon-consensus/core/blockdb" + "github.com/dexon-foundation/dexon-consensus/core/db" "github.com/dexon-foundation/dexon-consensus/core/types" ) @@ -53,7 +53,7 @@ func NewLattice( authModule *Authenticator, app Application, debug Debug, - db blockdb.BlockDatabase, + db db.Database, logger common.Logger) *Lattice { // Create genesis latticeDataConfig. diff --git a/core/lattice_test.go b/core/lattice_test.go index eecaf74..0c9b018 100644 --- a/core/lattice_test.go +++ b/core/lattice_test.go @@ -23,8 +23,8 @@ import ( "time" "github.com/dexon-foundation/dexon-consensus/common" - "github.com/dexon-foundation/dexon-consensus/core/blockdb" "github.com/dexon-foundation/dexon-consensus/core/crypto/ecdsa" + "github.com/dexon-foundation/dexon-consensus/core/db" "github.com/dexon-foundation/dexon-consensus/core/test" "github.com/dexon-foundation/dexon-consensus/core/types" "github.com/stretchr/testify/suite" @@ -35,7 +35,7 @@ type testLatticeMgr struct { lattice *Lattice ccModule *compactionChain app *test.App - db blockdb.BlockDatabase + db db.Database } func (mgr *testLatticeMgr) prepareBlock( @@ -61,8 +61,8 @@ func (mgr *testLatticeMgr) processBlock(b *types.Block) (err error) { return } } - if err = mgr.db.Put(*b); err != nil { - if err != blockdb.ErrBlockExists { + if err = mgr.db.PutBlock(*b); err != nil { + if err != db.ErrBlockExists { return } err = nil @@ -77,7 +77,7 @@ func (mgr *testLatticeMgr) processBlock(b *types.Block) (err error) { } } for _, b = range mgr.ccModule.extractBlocks() { - if err = mgr.db.Update(*b); err != nil { + if err = mgr.db.UpdateBlock(*b); err != nil { return } mgr.app.BlockDelivered(b.Hash, b.Position, b.Finalization) @@ -98,8 +98,8 @@ func (s *LatticeTestSuite) newTestLatticeMgr( // Setup private key. prvKey, err := ecdsa.NewPrivateKey() req.NoError(err) - // Setup blockdb. - db, err := blockdb.NewMemBackedBlockDB() + // Setup db. + dbInst, err := db.NewMemBackedDB() req.NoError(err) // Setup governance. logger := &common.NullLogger{} @@ -121,7 +121,7 @@ func (s *LatticeTestSuite) newTestLatticeMgr( return &testLatticeMgr{ ccModule: cc, app: app, - db: db, + db: dbInst, lattice: NewLattice( dMoment, 0, @@ -129,7 +129,7 @@ func (s *LatticeTestSuite) newTestLatticeMgr( NewAuthenticator(prvKey), app, app, - db, + dbInst, logger)} } @@ -174,18 +174,18 @@ func (s *LatticeTestSuite) TestBasicUsage() { req.NoError(master.processBlock(b)) } // Now we have some blocks, replay them on different lattices. - iter, err := master.db.GetAll() + iter, err := master.db.GetAllBlocks() req.NoError(err) - revealer, err := test.NewRandomRevealer(iter) + revealer, err := test.NewRandomBlockRevealer(iter) req.NoError(err) for i := 0; i < otherLatticeNum; i++ { revealer.Reset() revealed := "" other := s.newTestLatticeMgr(&cfg, dMoment) for { - b, err := revealer.Next() + b, err := revealer.NextBlock() if err != nil { - if err == blockdb.ErrIterationFinished { + if err == db.ErrIterationFinished { err = nil break } diff --git a/core/nonblocking.go b/core/nonblocking.go index f94d3c6..56c42fe 100644 --- a/core/nonblocking.go +++ b/core/nonblocking.go @@ -44,7 +44,7 @@ type blockDeliveredEvent struct { // them that makes the methods to be non-blocking. // - Application // - Debug -// - It also provides nonblockig for blockdb update. +// - It also provides nonblockig for db update. type nonBlocking struct { app Application debug Debug @@ -75,7 +75,7 @@ func (nb *nonBlocking) addEvent(event interface{}) { func (nb *nonBlocking) run() { // This go routine consume the first event from events and call the - // corresponding methods of Application/Debug/blockdb. + // corresponding methods of Application/Debug/db. for { var event interface{} func() { diff --git a/core/syncer/consensus.go b/core/syncer/consensus.go index dca6112..13a8873 100644 --- a/core/syncer/consensus.go +++ b/core/syncer/consensus.go @@ -26,8 +26,8 @@ import ( "github.com/dexon-foundation/dexon-consensus/common" "github.com/dexon-foundation/dexon-consensus/core" - "github.com/dexon-foundation/dexon-consensus/core/blockdb" "github.com/dexon-foundation/dexon-consensus/core/crypto" + "github.com/dexon-foundation/dexon-consensus/core/db" "github.com/dexon-foundation/dexon-consensus/core/types" "github.com/dexon-foundation/dexon-consensus/core/utils" ) @@ -46,7 +46,7 @@ var ( // Consensus is for syncing consensus module. type Consensus struct { - db blockdb.BlockDatabase + db db.Database gov core.Governance dMoment time.Time logger common.Logger @@ -82,7 +82,7 @@ func NewConsensus( dMoment time.Time, app core.Application, gov core.Governance, - db blockdb.BlockDatabase, + db db.Database, network core.Network, prv crypto.PrivateKey, logger common.Logger) *Consensus { @@ -170,7 +170,7 @@ func (con *Consensus) checkIfSynced(blocks []*types.Block) bool { if (b.Finalization.ParentHash == common.Hash{}) { return false } - b1, err := con.db.Get(b.Finalization.ParentHash) + b1, err := con.db.GetBlock(b.Finalization.ParentHash) if err != nil { panic(err) } @@ -286,7 +286,7 @@ func (con *Consensus) findLatticeSyncBlock( if (lastBlock.Finalization.ParentHash == common.Hash{}) { return nil, ErrGenesisBlockReached } - b, err := con.db.Get(lastBlock.Finalization.ParentHash) + b, err := con.db.GetBlock(lastBlock.Finalization.ParentHash) if err != nil { return nil, err } @@ -302,7 +302,7 @@ func (con *Consensus) findLatticeSyncBlock( if (curBlock.Finalization.ParentHash == common.Hash{}) { return nil, ErrGenesisBlockReached } - b, err := con.db.Get(curBlock.Finalization.ParentHash) + b, err := con.db.GetBlock(curBlock.Finalization.ParentHash) if err != nil { return nil, err } @@ -318,7 +318,7 @@ func (con *Consensus) findLatticeSyncBlock( if (curBlock.Finalization.ParentHash == common.Hash{}) { break } - b, err := con.db.Get(curBlock.Finalization.ParentHash) + b, err := con.db.GetBlock(curBlock.Finalization.ParentHash) if err != nil { return nil, err } @@ -338,7 +338,7 @@ func (con *Consensus) findLatticeSyncBlock( ok = false break } - b, err := con.db.Get(curBlock.Finalization.ParentHash) + b, err := con.db.GetBlock(curBlock.Finalization.ParentHash) if err != nil { return nil, err } @@ -403,8 +403,8 @@ func (con *Consensus) SyncBlocks( con.setupConfigs(blocks) for _, b := range blocks { // TODO(haoping) remove this if lattice puts blocks into db. - if err := con.db.Put(*b); err != nil { - if err != blockdb.ErrBlockExists { + if err := con.db.PutBlock(*b); err != nil { + if err != db.ErrBlockExists { return nil, err } } @@ -433,7 +433,7 @@ func (con *Consensus) SyncBlocks( if b.Hash == syncBlock.Hash { break } - b1, err := con.db.Get(b.Finalization.ParentHash) + b1, err := con.db.GetBlock(b.Finalization.ParentHash) if err != nil { return nil, err } diff --git a/core/test/block-revealer.go b/core/test/block-revealer.go new file mode 100644 index 0000000..ebd2e35 --- /dev/null +++ b/core/test/block-revealer.go @@ -0,0 +1,332 @@ +// Copyright 2018 The dexon-consensus Authors +// This file is part of the dexon-consensus library. +// +// The dexon-consensus library is free software: you can redistribute it +// and/or modify it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation, either version 3 of the License, +// or (at your option) any later version. +// +// The dexon-consensus library is distributed in the hope that it will be +// useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser +// General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the dexon-consensus library. If not, see +// . + +package test + +import ( + "errors" + "math/rand" + "sort" + "time" + + "github.com/dexon-foundation/dexon-consensus/common" + "github.com/dexon-foundation/dexon-consensus/core/db" + "github.com/dexon-foundation/dexon-consensus/core/types" +) + +// Errors returns from block-revealer. +var ( + ErrNotValidCompactionChain = errors.New("not valid compaction chain") +) + +// isAllAckingBlockRevealed is a helper to check if all acking blocks of +// one block are revealed. +func isAllAckingBlockRevealed( + b *types.Block, revealed map[common.Hash]struct{}) bool { + + for _, ack := range b.Acks { + if _, exists := revealed[ack]; !exists { + return false + } + } + return true +} + +// loadAllBlocks is a helper to load all blocks from db.BlockIterator. +func loadAllBlocks(iter db.BlockIterator) ( + blocks map[common.Hash]*types.Block, err error) { + + blocks = make(map[common.Hash]*types.Block) + for { + block, err := iter.NextBlock() + if err != nil { + if err == db.ErrIterationFinished { + // It's safe to ignore iteraion-finished error. + err = nil + } + break + } + blocks[block.Hash] = &block + } + return +} + +// RandomDAGBlockRevealer implements BlockRevealer interface, which would load +// all blocks from db, and randomly pick one block to reveal if it still forms +// a valid DAG in revealed blocks. +type RandomDAGBlockRevealer struct { + // blocksByChain group all blocks by chains and sorting + // them by height. + blocksByChain map[uint32][]*types.Block + // tipIndexes store the height of next block from one chain + // to check if is candidate. + tipIndexes map[uint32]int + // candidate are blocks that forms valid DAG with + // current revealed blocks. + candidates []*types.Block + candidateChains map[uint32]struct{} + // revealed stores block hashes of current revealed blocks. + revealed map[common.Hash]struct{} + randGen *rand.Rand +} + +// NewRandomDAGBlockRevealer constructs RandomDAGBlockRevealer. +func NewRandomDAGBlockRevealer( + iter db.BlockIterator) (r *RandomDAGBlockRevealer, err error) { + + blocks, err := loadAllBlocks(iter) + if err != nil { + return + } + + // Rearrange blocks by nodes and height. + blocksByChain := make(map[uint32][]*types.Block) + for _, block := range blocks { + blocksByChain[block.Position.ChainID] = + append(blocksByChain[block.Position.ChainID], block) + } + // Make sure blocks are sorted by block heights, from lower to higher. + for chainID := range blocksByChain { + sort.Sort(types.ByPosition(blocksByChain[chainID])) + } + r = &RandomDAGBlockRevealer{ + blocksByChain: blocksByChain, + randGen: rand.New(rand.NewSource(time.Now().UnixNano())), + candidateChains: make(map[uint32]struct{}), + } + // Make sure this revealer is ready to use. + r.Reset() + return +} + +// pickCandidates is a helper function to pick candidates from current tips. +func (r *RandomDAGBlockRevealer) pickCandidates() { + for chainID, tip := range r.tipIndexes { + if _, isPicked := r.candidateChains[chainID]; isPicked { + continue + } + blocks, exists := r.blocksByChain[chainID] + if !exists { + continue + } + if tip >= len(blocks) { + continue + } + block := blocks[tip] + if isAllAckingBlockRevealed(block, r.revealed) { + r.tipIndexes[chainID]++ + r.candidates = append(r.candidates, block) + r.candidateChains[chainID] = struct{}{} + } + } +} + +// NextBlock implement Revealer.Next method, which would reveal blocks +// forming valid DAGs. +func (r *RandomDAGBlockRevealer) NextBlock() (types.Block, error) { + if len(r.candidates) == 0 { + r.pickCandidates() + if len(r.candidates) == 0 { + return types.Block{}, db.ErrIterationFinished + } + } + + // Pick next block to be revealed. + picked := r.randGen.Intn(len(r.candidates)) + block := r.candidates[picked] + r.candidates = + append(r.candidates[:picked], r.candidates[picked+1:]...) + delete(r.candidateChains, block.Position.ChainID) + r.revealed[block.Hash] = struct{}{} + r.pickCandidates() + return *block, nil +} + +// Reset implement Revealer.Reset method, which would reset the revealing. +func (r *RandomDAGBlockRevealer) Reset() { + r.tipIndexes = make(map[uint32]int) + for chainID := range r.blocksByChain { + r.tipIndexes[chainID] = 0 + } + r.revealed = make(map[common.Hash]struct{}) + r.candidates = []*types.Block{} +} + +// RandomBlockRevealer implements BlockRevealer interface, which would load +// all blocks from db, and randomly pick one block to reveal. +type RandomBlockRevealer struct { + blocks map[common.Hash]*types.Block + remains common.Hashes + randGen *rand.Rand +} + +// NewRandomBlockRevealer constructs RandomBlockRevealer. +func NewRandomBlockRevealer( + iter db.BlockIterator) (r *RandomBlockRevealer, err error) { + + blocks, err := loadAllBlocks(iter) + if err != nil { + return + } + r = &RandomBlockRevealer{ + blocks: blocks, + randGen: rand.New(rand.NewSource(time.Now().UnixNano())), + } + r.Reset() + return +} + +// NextBlock implements Revealer.NextBlock method, which would reveal blocks +// randomly. +func (r *RandomBlockRevealer) NextBlock() (types.Block, error) { + if len(r.remains) == 0 { + return types.Block{}, db.ErrIterationFinished + } + + picked := r.randGen.Intn(len(r.remains)) + block := r.blocks[r.remains[picked]] + r.remains = + append(r.remains[:picked], r.remains[picked+1:]...) + return *block, nil +} + +// Reset implement Revealer.Reset method, which would reset revealing. +func (r *RandomBlockRevealer) Reset() { + hashes := common.Hashes{} + for hash := range r.blocks { + hashes = append(hashes, hash) + } + r.remains = hashes +} + +// RandomTipBlockRevealer implements BlockRevealer interface, which would load +// all blocks from db, and randomly pick one chain's tip to reveal. +type RandomTipBlockRevealer struct { + chainsBlock []map[uint64]*types.Block + chainTip []uint64 + chainRevealSeq []uint32 + revealed int + randGen *rand.Rand +} + +// NewRandomTipBlockRevealer constructs RandomTipBlockRevealer. +func NewRandomTipBlockRevealer( + iter db.BlockIterator) (r *RandomTipBlockRevealer, err error) { + + blocks, err := loadAllBlocks(iter) + if err != nil { + return + } + r = &RandomTipBlockRevealer{ + randGen: rand.New(rand.NewSource(time.Now().UnixNano())), + } + for _, b := range blocks { + for b.Position.ChainID >= uint32(len(r.chainsBlock)) { + r.chainsBlock = append(r.chainsBlock, make(map[uint64]*types.Block)) + r.chainTip = append(r.chainTip, 0) + } + r.chainsBlock[b.Position.ChainID][b.Position.Height] = b + r.chainRevealSeq = append(r.chainRevealSeq, b.Position.ChainID) + } + r.Reset() + return +} + +// NextBlock implements Revealer.Next method, which would reveal blocks randomly. +func (r *RandomTipBlockRevealer) NextBlock() (types.Block, error) { + if len(r.chainRevealSeq) == r.revealed { + return types.Block{}, db.ErrIterationFinished + } + + picked := r.chainRevealSeq[r.revealed] + r.revealed++ + block := r.chainsBlock[picked][r.chainTip[picked]] + r.chainTip[picked]++ + return *block, nil +} + +// Reset implement Revealer.Reset method, which would reset revealing. +func (r *RandomTipBlockRevealer) Reset() { + r.revealed = 0 + r.randGen.Shuffle(len(r.chainRevealSeq), func(i, j int) { + r.chainRevealSeq[i], r.chainRevealSeq[j] = + r.chainRevealSeq[j], r.chainRevealSeq[i] + }) + for i := range r.chainTip { + r.chainTip[i] = 0 + } +} + +// CompactionChainBlockRevealer implements BlockRevealer interface, which would +// load all blocks from db, reveal them in the order of compaction chain, +// from the genesis block to the latest one. +type CompactionChainBlockRevealer struct { + blocks types.ByFinalizationHeight + nextRevealIndex int +} + +// NewCompactionChainBlockRevealer constructs a block revealer in the order of +// compaction chain. +func NewCompactionChainBlockRevealer(iter db.BlockIterator, + startHeight uint64) (r *CompactionChainBlockRevealer, err error) { + blocksByHash, err := loadAllBlocks(iter) + if err != nil { + return + } + if startHeight == 0 { + startHeight = 1 + } + blocks := types.ByFinalizationHeight{} + for _, b := range blocksByHash { + if b.Finalization.Height < startHeight { + continue + } + blocks = append(blocks, b) + } + sort.Sort(types.ByFinalizationHeight(blocks)) + // Make sure the finalization height of blocks are incremental with step 1. + for idx, b := range blocks { + if idx == 0 { + continue + } + if b.Finalization.Height != blocks[idx-1].Finalization.Height+1 { + err = ErrNotValidCompactionChain + return + } + } + r = &CompactionChainBlockRevealer{ + blocks: blocks, + } + r.Reset() + return +} + +// NextBlock implements Revealer.Next method, which would reveal blocks in the +// order of compaction chain. +func (r *CompactionChainBlockRevealer) NextBlock() (types.Block, error) { + if r.nextRevealIndex == len(r.blocks) { + return types.Block{}, db.ErrIterationFinished + } + b := r.blocks[r.nextRevealIndex] + r.nextRevealIndex++ + return *b, nil +} + +// Reset implement Revealer.Reset method, which would reset revealing. +func (r *CompactionChainBlockRevealer) Reset() { + r.nextRevealIndex = 0 +} diff --git a/core/test/block-revealer_test.go b/core/test/block-revealer_test.go new file mode 100644 index 0000000..ba4cf71 --- /dev/null +++ b/core/test/block-revealer_test.go @@ -0,0 +1,222 @@ +// Copyright 2018 The dexon-consensus Authors +// This file is part of the dexon-consensus library. +// +// The dexon-consensus library is free software: you can redistribute it +// and/or modify it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation, either version 3 of the License, +// or (at your option) any later version. +// +// The dexon-consensus library is distributed in the hope that it will be +// useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser +// General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the dexon-consensus library. If not, see +// . + +package test + +import ( + "testing" + "time" + + "github.com/dexon-foundation/dexon-consensus/common" + "github.com/dexon-foundation/dexon-consensus/core/db" + "github.com/dexon-foundation/dexon-consensus/core/types" + "github.com/stretchr/testify/suite" +) + +type BlockRevealerTestSuite struct { + suite.Suite + + db db.Database + totalBlockCount int +} + +func (s *BlockRevealerTestSuite) SetupSuite() { + var ( + err error + genesisTime = time.Now().UTC() + ) + // Setup block database. + s.db, err = db.NewMemBackedDB() + s.Require().NoError(err) + + // Randomly generate blocks. + config := &BlocksGeneratorConfig{ + NumChains: 19, + MinBlockTimeInterval: 250 * time.Millisecond, + } + gen := NewBlocksGenerator(config, nil, stableRandomHash) + s.Require().NoError(gen.Generate( + 0, + genesisTime, + genesisTime.Add(30*time.Second), + s.db)) + // Cache the count of total generated block. + iter, err := s.db.GetAllBlocks() + s.Require().NoError(err) + blocks, err := loadAllBlocks(iter) + s.Require().NoError(err) + s.totalBlockCount = len(blocks) +} + +func (s *BlockRevealerTestSuite) baseTest( + revealer BlockRevealer, + repeat int, + checkFunc func(*types.Block, map[common.Hash]struct{})) { + + revealingSequence := map[string]struct{}{} + for i := 0; i < repeat; i++ { + revealed := map[common.Hash]struct{}{} + sequence := "" + for { + b, err := revealer.NextBlock() + if err != nil { + if err == db.ErrIterationFinished { + err = nil + break + } + s.Require().NotNil(err) + } + checkFunc(&b, revealed) + revealed[b.Hash] = struct{}{} + sequence += b.Hash.String() + "," + } + s.Len(revealed, s.totalBlockCount) + revealingSequence[sequence] = struct{}{} + revealer.Reset() + } + // It should be reasonable to reveal at least two + // different sequence. + s.True(len(revealingSequence) > 1) + +} + +func (s *BlockRevealerTestSuite) TestRandomBlockReveal() { + // This test case would make sure we could at least generate + // two different revealing sequence when revealing more than + // 10 times. + iter, err := s.db.GetAllBlocks() + s.Require().Nil(err) + revealer, err := NewRandomBlockRevealer(iter) + s.Require().Nil(err) + + checkFunc := func(b *types.Block, revealed map[common.Hash]struct{}) { + // Make sure the revealer won't reveal the same block twice. + _, alreadyRevealed := revealed[b.Hash] + s.False(alreadyRevealed) + } + s.baseTest(revealer, 10, checkFunc) +} + +func (s *BlockRevealerTestSuite) TestRandomDAGBlockReveal() { + // This test case would make sure we could at least generate + // two different revealing sequence when revealing more than + // 10 times, and each of them would form valid DAGs during + // revealing. + + iter, err := s.db.GetAllBlocks() + s.Require().Nil(err) + revealer, err := NewRandomDAGBlockRevealer(iter) + s.Require().Nil(err) + + checkFunc := func(b *types.Block, revealed map[common.Hash]struct{}) { + // Make sure this revealer won't reveal + // the same block twice. + _, alreadyRevealed := revealed[b.Hash] + s.False(alreadyRevealed) + // Make sure the newly revealed block would still + // form a valid DAG after added to revealed blocks. + s.True(isAllAckingBlockRevealed(b, revealed)) + } + s.baseTest(revealer, 10, checkFunc) +} + +func (s *BlockRevealerTestSuite) TestRandomTipBlockReveal() { + // This test case would make sure we could at least generate + // two different revealing sequence when revealing more than + // 10 times. + iter, err := s.db.GetAllBlocks() + s.Require().Nil(err) + revealer, err := NewRandomTipBlockRevealer(iter) + s.Require().Nil(err) + + checkFunc := func(b *types.Block, revealed map[common.Hash]struct{}) { + // Make sure the revealer won't reveal the same block twice. + _, alreadyRevealed := revealed[b.Hash] + s.False(alreadyRevealed) + // Make sure the parent is already revealed. + if b.Position.Height == 0 { + return + } + _, alreadyRevealed = revealed[b.ParentHash] + s.True(alreadyRevealed) + } + s.baseTest(revealer, 10, checkFunc) +} + +func (s *BlockRevealerTestSuite) TestCompactionChainBlockReveal() { + dbInst, err := db.NewMemBackedDB() + s.Require().NoError(err) + // Put several blocks with finalization field ready. + b1 := &types.Block{ + Hash: common.NewRandomHash(), + Finalization: types.FinalizationResult{ + Height: 1, + }} + b2 := &types.Block{ + Hash: common.NewRandomHash(), + Finalization: types.FinalizationResult{ + ParentHash: b1.Hash, + Height: 2, + }} + b3 := &types.Block{ + Hash: common.NewRandomHash(), + Finalization: types.FinalizationResult{ + ParentHash: b2.Hash, + Height: 3, + }} + s.Require().NoError(dbInst.PutBlock(*b1)) + s.Require().NoError(dbInst.PutBlock(*b3)) + iter, err := dbInst.GetAllBlocks() + s.Require().NoError(err) + // The compaction chain is not complete, we can't construct a revealer + // instance successfully. + r, err := NewCompactionChainBlockRevealer(iter, 0) + s.Require().Nil(r) + s.Require().IsType(ErrNotValidCompactionChain, err) + // Put a block to make the compaction chain complete. + s.Require().NoError(dbInst.PutBlock(*b2)) + // We can construct that revealer now. + iter, err = dbInst.GetAllBlocks() + s.Require().NoError(err) + r, err = NewCompactionChainBlockRevealer(iter, 0) + s.Require().NotNil(r) + s.Require().NoError(err) + // The revealing order should be ok. + chk := func(h uint64) { + b, err := r.NextBlock() + s.Require().NoError(err) + s.Require().Equal(b.Finalization.Height, h) + } + chk(1) + chk(2) + chk(3) + // Iteration should be finished + _, err = r.NextBlock() + s.Require().IsType(db.ErrIterationFinished, err) + // Test 'startHeight' parameter. + iter, err = dbInst.GetAllBlocks() + s.Require().NoError(err) + r, err = NewCompactionChainBlockRevealer(iter, 2) + s.Require().NotNil(r) + s.Require().NoError(err) + chk(2) + chk(3) +} + +func TestBlockRevealer(t *testing.T) { + suite.Run(t, new(BlockRevealerTestSuite)) +} diff --git a/core/test/blocks-generator.go b/core/test/blocks-generator.go index e97985f..e92ff5d 100644 --- a/core/test/blocks-generator.go +++ b/core/test/blocks-generator.go @@ -24,9 +24,9 @@ import ( "time" "github.com/dexon-foundation/dexon-consensus/common" - "github.com/dexon-foundation/dexon-consensus/core/blockdb" "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" ) @@ -323,11 +323,11 @@ func NewBlocksGenerator( func (gen *BlocksGenerator) Generate( roundID uint64, roundBegin, roundEnd time.Time, - db blockdb.BlockDatabase) (err error) { + dbInst db.Database) (err error) { // Find tips of previous round if available. tips := make(map[uint32]*types.Block) if roundID > 0 { - tips, err = gen.findTips(roundID-1, db) + tips, err = gen.findTips(roundID-1, dbInst) if err != nil { return } @@ -361,29 +361,29 @@ func (gen *BlocksGenerator) Generate( return } // Persist block to db. - if err = db.Put(*newBlock); err != nil { + if err = dbInst.PutBlock(*newBlock); err != nil { return } } return } -// findTips is an utility to find tips of each chain in that round in blockdb. -func (gen *BlocksGenerator) findTips( - round uint64, db blockdb.Reader) (tips map[uint32]*types.Block, err error) { - iter, err := db.GetAll() +// findTips is an utility to find tips of each chain in that round in db. +func (gen *BlocksGenerator) findTips(round uint64, dbInst db.Reader) ( + tips map[uint32]*types.Block, err error) { + iter, err := dbInst.GetAllBlocks() if err != nil { return } - revealer, err := NewRandomRevealer(iter) + revealer, err := NewRandomBlockRevealer(iter) if err != nil { return } tips = make(map[uint32]*types.Block) for { var b types.Block - if b, err = revealer.Next(); err != nil { - if err == blockdb.ErrIterationFinished { + if b, err = revealer.NextBlock(); err != nil { + if err == db.ErrIterationFinished { err = nil break } diff --git a/core/test/blocks-generator_test.go b/core/test/blocks-generator_test.go index 8dcc2b7..bd7a5a2 100644 --- a/core/test/blocks-generator_test.go +++ b/core/test/blocks-generator_test.go @@ -18,12 +18,13 @@ package test import ( + "fmt" "sort" "testing" "time" "github.com/dexon-foundation/dexon-consensus/common" - "github.com/dexon-foundation/dexon-consensus/core/blockdb" + "github.com/dexon-foundation/dexon-consensus/core/db" "github.com/dexon-foundation/dexon-consensus/core/types" "github.com/stretchr/testify/suite" ) @@ -44,17 +45,17 @@ func (s *BlocksGeneratorTestSuite) TestGenerate() { beginTime = time.Now().UTC() endTime = beginTime.Add(time.Minute) ) - db, err := blockdb.NewMemBackedBlockDB() + dbInst, err := db.NewMemBackedDB() req.NoError(err) - req.NoError(gen.Generate(1, beginTime, endTime, db)) + req.NoError(gen.Generate(1, beginTime, endTime, dbInst)) // Load all blocks in that database for further checking. - iter, err := db.GetAll() + iter, err := dbInst.GetAllBlocks() req.NoError(err) blocksByChain := make(map[uint32][]*types.Block) blocksByHash := make(map[common.Hash]*types.Block) for { - block, err := iter.Next() - if err == blockdb.ErrIterationFinished { + block, err := iter.NextBlock() + if err == db.ErrIterationFinished { break } req.NoError(err) @@ -127,7 +128,7 @@ func (s *BlocksGeneratorTestSuite) TestGenerateWithMaxAckCount() { genesisTime = time.Now().UTC() ) // Generate with 0 acks. - db, err := blockdb.NewMemBackedBlockDB() + dbInst, err := db.NewMemBackedDB() req.NoError(err) gen := NewBlocksGenerator( config, MaxAckingCountGenerator(0), stableRandomHash) @@ -135,13 +136,13 @@ func (s *BlocksGeneratorTestSuite) TestGenerateWithMaxAckCount() { 0, genesisTime, genesisTime.Add(50*time.Second), - db)) + dbInst)) // Load blocks to check their acking count. - iter, err := db.GetAll() + iter, err := dbInst.GetAllBlocks() req.NoError(err) for { - block, err := iter.Next() - if err == blockdb.ErrIterationFinished { + block, err := iter.NextBlock() + if err == db.ErrIterationFinished { break } req.NoError(err) @@ -151,7 +152,7 @@ func (s *BlocksGeneratorTestSuite) TestGenerateWithMaxAckCount() { req.Len(block.Acks, 1) } // Generate with acks as many as possible. - db, err = blockdb.NewMemBackedBlockDB() + dbInst, err = db.NewMemBackedDB() req.NoError(err) gen = NewBlocksGenerator( config, MaxAckingCountGenerator(config.NumChains), stableRandomHash) @@ -159,13 +160,13 @@ func (s *BlocksGeneratorTestSuite) TestGenerateWithMaxAckCount() { 0, genesisTime, genesisTime.Add(50*time.Second), - db)) + dbInst)) // Load blocks to verify the average acking count. - iter, err = db.GetAll() + iter, err = dbInst.GetAllBlocks() req.NoError(err) for { - block, err := iter.Next() - if err == blockdb.ErrIterationFinished { + block, err := iter.NextBlock() + if err == db.ErrIterationFinished { break } req.NoError(err) @@ -191,14 +192,14 @@ func (s *BlocksGeneratorTestSuite) TestFindTips() { endTime = genesisTime.Add(100 * time.Second) ) gen := NewBlocksGenerator(config, nil, stableRandomHash) - db, err := blockdb.NewMemBackedBlockDB() + dbInst, err := db.NewMemBackedDB() req.NoError(err) req.NoError(gen.Generate( 0, genesisTime, endTime, - db)) - tips, err := gen.findTips(0, db) + dbInst)) + tips, err := gen.findTips(0, dbInst) req.NoError(err) req.Len(tips, int(config.NumChains)) for _, b := range tips { @@ -208,13 +209,13 @@ func (s *BlocksGeneratorTestSuite) TestFindTips() { func (s *BlocksGeneratorTestSuite) TestConcateBlocksFromRounds() { // This test case run these steps: - // - generate blocks by round but sharing one blockdb. + // - generate blocks by round but sharing one db. // - if those rounds are continuous, they should be concated. var ( req = s.Require() genesisTime = time.Now().UTC() ) - db, err := blockdb.NewMemBackedBlockDB() + dbInst, err := db.NewMemBackedDB() req.NoError(err) // Generate round 0 blocks. gen := NewBlocksGenerator(&BlocksGeneratorConfig{ @@ -225,8 +226,8 @@ func (s *BlocksGeneratorTestSuite) TestConcateBlocksFromRounds() { 0, genesisTime, genesisTime.Add(10*time.Second), - db)) - tips0, err := gen.findTips(0, db) + dbInst)) + tips0, err := gen.findTips(0, dbInst) req.NoError(err) req.Len(tips0, 4) // Generate round 1 blocks. @@ -238,8 +239,8 @@ func (s *BlocksGeneratorTestSuite) TestConcateBlocksFromRounds() { 1, genesisTime.Add(10*time.Second), genesisTime.Add(20*time.Second), - db)) - tips1, err := gen.findTips(1, db) + dbInst)) + tips1, err := gen.findTips(1, dbInst) req.NoError(err) req.Len(tips1, 10) // Generate round 2 blocks. @@ -251,14 +252,14 @@ func (s *BlocksGeneratorTestSuite) TestConcateBlocksFromRounds() { 2, genesisTime.Add(20*time.Second), genesisTime.Add(30*time.Second), - db)) - tips2, err := gen.findTips(2, db) + dbInst)) + tips2, err := gen.findTips(2, dbInst) req.NoError(err) req.Len(tips2, 7) // Check results, make sure tips0, tips1 are acked by correct blocks. - iter, err := db.GetAll() + iter, err := dbInst.GetAllBlocks() req.NoError(err) - revealer, err := NewRandomRevealer(iter) + revealer, err := NewRandomBlockRevealer(iter) req.NoError(err) removeTip := func(tips map[uint32]*types.Block, b *types.Block) { toRemove := []uint32{} @@ -277,9 +278,9 @@ func (s *BlocksGeneratorTestSuite) TestConcateBlocksFromRounds() { // Make sure all tips are acked by loading blocks from db // and check them one by one. for { - b, err := revealer.Next() + b, err := revealer.NextBlock() if err != nil { - if err == blockdb.ErrIterationFinished { + if err == db.ErrIterationFinished { err = nil break } @@ -302,9 +303,9 @@ func (s *BlocksGeneratorTestSuite) TestConcateBlocksFromRounds() { totalAckCount := 0 revealer.Reset() for { - b, err := revealer.Next() + b, err := revealer.NextBlock() if err != nil { - if err == blockdb.ErrIterationFinished { + if err == db.ErrIterationFinished { err = nil break } @@ -317,6 +318,7 @@ 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/interface.go b/core/test/interface.go index dcecee8..1388dc1 100644 --- a/core/test/interface.go +++ b/core/test/interface.go @@ -18,15 +18,15 @@ package test import ( - "github.com/dexon-foundation/dexon-consensus/core/blockdb" "github.com/dexon-foundation/dexon-consensus/core/crypto" + "github.com/dexon-foundation/dexon-consensus/core/db" "github.com/dexon-foundation/dexon-consensus/core/types" ) -// Revealer defines the interface to reveal a group +// BlockRevealer defines the interface to reveal a group // of pre-generated blocks. -type Revealer interface { - blockdb.BlockIterator +type BlockRevealer interface { + db.BlockIterator // Reset the revealing. Reset() diff --git a/core/test/revealer.go b/core/test/revealer.go deleted file mode 100644 index dcd75a5..0000000 --- a/core/test/revealer.go +++ /dev/null @@ -1,331 +0,0 @@ -// Copyright 2018 The dexon-consensus Authors -// This file is part of the dexon-consensus library. -// -// The dexon-consensus library is free software: you can redistribute it -// and/or modify it under the terms of the GNU Lesser General Public License as -// published by the Free Software Foundation, either version 3 of the License, -// or (at your option) any later version. -// -// The dexon-consensus library is distributed in the hope that it will be -// useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser -// General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the dexon-consensus library. If not, see -// . - -package test - -import ( - "errors" - "math/rand" - "sort" - "time" - - "github.com/dexon-foundation/dexon-consensus/common" - "github.com/dexon-foundation/dexon-consensus/core/blockdb" - "github.com/dexon-foundation/dexon-consensus/core/types" -) - -// Errors returns from revealer. -var ( - ErrNotValidCompactionChain = errors.New("not valid compaction chain") -) - -// isAllAckingBlockRevealed is a helper to check if all acking blocks of -// one block are revealed. -func isAllAckingBlockRevealed( - b *types.Block, revealed map[common.Hash]struct{}) bool { - - for _, ack := range b.Acks { - if _, exists := revealed[ack]; !exists { - return false - } - } - return true -} - -// loadAllBlocks is a helper to load all blocks from blockdb.BlockIterator. -func loadAllBlocks(iter blockdb.BlockIterator) ( - blocks map[common.Hash]*types.Block, err error) { - - blocks = make(map[common.Hash]*types.Block) - for { - block, err := iter.Next() - if err != nil { - if err == blockdb.ErrIterationFinished { - // It's safe to ignore iteraion-finished error. - err = nil - } - break - } - blocks[block.Hash] = &block - } - return -} - -// RandomDAGRevealer implements Revealer interface, which would load -// all blocks from blockdb, and randomly pick one block to reveal if -// it still forms a valid DAG in revealed blocks. -type RandomDAGRevealer struct { - // blocksByChain group all blocks by chains and sorting - // them by height. - blocksByChain map[uint32][]*types.Block - // tipIndexes store the height of next block from one chain - // to check if is candidate. - tipIndexes map[uint32]int - // candidate are blocks that forms valid DAG with - // current revealed blocks. - candidates []*types.Block - candidateChains map[uint32]struct{} - // revealed stores block hashes of current revealed blocks. - revealed map[common.Hash]struct{} - randGen *rand.Rand -} - -// NewRandomDAGRevealer constructs RandomDAGRevealer. -func NewRandomDAGRevealer( - iter blockdb.BlockIterator) (r *RandomDAGRevealer, err error) { - - blocks, err := loadAllBlocks(iter) - if err != nil { - return - } - - // Rearrange blocks by nodes and height. - blocksByChain := make(map[uint32][]*types.Block) - for _, block := range blocks { - blocksByChain[block.Position.ChainID] = - append(blocksByChain[block.Position.ChainID], block) - } - // Make sure blocks are sorted by block heights, from lower to higher. - for chainID := range blocksByChain { - sort.Sort(types.ByPosition(blocksByChain[chainID])) - } - r = &RandomDAGRevealer{ - blocksByChain: blocksByChain, - randGen: rand.New(rand.NewSource(time.Now().UnixNano())), - candidateChains: make(map[uint32]struct{}), - } - // Make sure this revealer is ready to use. - r.Reset() - return -} - -// pickCandidates is a helper function to pick candidates from current tips. -func (r *RandomDAGRevealer) pickCandidates() { - for chainID, tip := range r.tipIndexes { - if _, isPicked := r.candidateChains[chainID]; isPicked { - continue - } - blocks, exists := r.blocksByChain[chainID] - if !exists { - continue - } - if tip >= len(blocks) { - continue - } - block := blocks[tip] - if isAllAckingBlockRevealed(block, r.revealed) { - r.tipIndexes[chainID]++ - r.candidates = append(r.candidates, block) - r.candidateChains[chainID] = struct{}{} - } - } -} - -// Next implement Revealer.Next method, which would reveal blocks -// forming valid DAGs. -func (r *RandomDAGRevealer) Next() (types.Block, error) { - if len(r.candidates) == 0 { - r.pickCandidates() - if len(r.candidates) == 0 { - return types.Block{}, blockdb.ErrIterationFinished - } - } - - // Pick next block to be revealed. - picked := r.randGen.Intn(len(r.candidates)) - block := r.candidates[picked] - r.candidates = - append(r.candidates[:picked], r.candidates[picked+1:]...) - delete(r.candidateChains, block.Position.ChainID) - r.revealed[block.Hash] = struct{}{} - r.pickCandidates() - return *block, nil -} - -// Reset implement Revealer.Reset method, which would reset the revealing. -func (r *RandomDAGRevealer) Reset() { - r.tipIndexes = make(map[uint32]int) - for chainID := range r.blocksByChain { - r.tipIndexes[chainID] = 0 - } - r.revealed = make(map[common.Hash]struct{}) - r.candidates = []*types.Block{} -} - -// RandomRevealer implements Revealer interface, which would load -// all blocks from blockdb, and randomly pick one block to reveal. -type RandomRevealer struct { - blocks map[common.Hash]*types.Block - remains common.Hashes - randGen *rand.Rand -} - -// NewRandomRevealer constructs RandomRevealer. -func NewRandomRevealer( - iter blockdb.BlockIterator) (r *RandomRevealer, err error) { - - blocks, err := loadAllBlocks(iter) - if err != nil { - return - } - r = &RandomRevealer{ - blocks: blocks, - randGen: rand.New(rand.NewSource(time.Now().UnixNano())), - } - r.Reset() - return -} - -// Next implements Revealer.Next method, which would reveal blocks randomly. -func (r *RandomRevealer) Next() (types.Block, error) { - if len(r.remains) == 0 { - return types.Block{}, blockdb.ErrIterationFinished - } - - picked := r.randGen.Intn(len(r.remains)) - block := r.blocks[r.remains[picked]] - r.remains = - append(r.remains[:picked], r.remains[picked+1:]...) - return *block, nil -} - -// Reset implement Revealer.Reset method, which would reset revealing. -func (r *RandomRevealer) Reset() { - hashes := common.Hashes{} - for hash := range r.blocks { - hashes = append(hashes, hash) - } - r.remains = hashes -} - -// RandomTipRevealer implements Revealer interface, which would load -// all blocks from blockdb, and randomly pick one chain's tip to reveal. -type RandomTipRevealer struct { - chainsBlock []map[uint64]*types.Block - chainTip []uint64 - chainRevealSeq []uint32 - revealed int - randGen *rand.Rand -} - -// NewRandomTipRevealer constructs RandomTipRevealer. -func NewRandomTipRevealer( - iter blockdb.BlockIterator) (r *RandomTipRevealer, err error) { - - blocks, err := loadAllBlocks(iter) - if err != nil { - return - } - r = &RandomTipRevealer{ - randGen: rand.New(rand.NewSource(time.Now().UnixNano())), - } - for _, b := range blocks { - for b.Position.ChainID >= uint32(len(r.chainsBlock)) { - r.chainsBlock = append(r.chainsBlock, make(map[uint64]*types.Block)) - r.chainTip = append(r.chainTip, 0) - } - r.chainsBlock[b.Position.ChainID][b.Position.Height] = b - r.chainRevealSeq = append(r.chainRevealSeq, b.Position.ChainID) - } - r.Reset() - return -} - -// Next implements Revealer.Next method, which would reveal blocks randomly. -func (r *RandomTipRevealer) Next() (types.Block, error) { - if len(r.chainRevealSeq) == r.revealed { - return types.Block{}, blockdb.ErrIterationFinished - } - - picked := r.chainRevealSeq[r.revealed] - r.revealed++ - block := r.chainsBlock[picked][r.chainTip[picked]] - r.chainTip[picked]++ - return *block, nil -} - -// Reset implement Revealer.Reset method, which would reset revealing. -func (r *RandomTipRevealer) Reset() { - r.revealed = 0 - r.randGen.Shuffle(len(r.chainRevealSeq), func(i, j int) { - r.chainRevealSeq[i], r.chainRevealSeq[j] = - r.chainRevealSeq[j], r.chainRevealSeq[i] - }) - for i := range r.chainTip { - r.chainTip[i] = 0 - } -} - -// CompactionChainRevealer implements Revealer interface, which would load -// all blocks from blockdb, reveal them in the order of compaction chain, from -// the genesis block to the latest one. -type CompactionChainRevealer struct { - blocks types.ByFinalizationHeight - nextRevealIndex int -} - -// NewCompactionChainRevealer constructs a revealer in the order of compaction -// chain. -func NewCompactionChainRevealer(iter blockdb.BlockIterator, - startHeight uint64) (r *CompactionChainRevealer, err error) { - blocksByHash, err := loadAllBlocks(iter) - if err != nil { - return - } - if startHeight == 0 { - startHeight = 1 - } - blocks := types.ByFinalizationHeight{} - for _, b := range blocksByHash { - if b.Finalization.Height < startHeight { - continue - } - blocks = append(blocks, b) - } - sort.Sort(types.ByFinalizationHeight(blocks)) - // Make sure the finalization height of blocks are incremental with step 1. - for idx, b := range blocks { - if idx == 0 { - continue - } - if b.Finalization.Height != blocks[idx-1].Finalization.Height+1 { - err = ErrNotValidCompactionChain - return - } - } - r = &CompactionChainRevealer{ - blocks: blocks, - } - r.Reset() - return -} - -// Next implements Revealer.Next method, which would reveal blocks in the order -// of compaction chain. -func (r *CompactionChainRevealer) Next() (types.Block, error) { - if r.nextRevealIndex == len(r.blocks) { - return types.Block{}, blockdb.ErrIterationFinished - } - b := r.blocks[r.nextRevealIndex] - r.nextRevealIndex++ - return *b, nil -} - -// Reset implement Revealer.Reset method, which would reset revealing. -func (r *CompactionChainRevealer) Reset() { - r.nextRevealIndex = 0 -} diff --git a/core/test/revealer_test.go b/core/test/revealer_test.go deleted file mode 100644 index 5a1bc07..0000000 --- a/core/test/revealer_test.go +++ /dev/null @@ -1,222 +0,0 @@ -// Copyright 2018 The dexon-consensus Authors -// This file is part of the dexon-consensus library. -// -// The dexon-consensus library is free software: you can redistribute it -// and/or modify it under the terms of the GNU Lesser General Public License as -// published by the Free Software Foundation, either version 3 of the License, -// or (at your option) any later version. -// -// The dexon-consensus library is distributed in the hope that it will be -// useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser -// General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the dexon-consensus library. If not, see -// . - -package test - -import ( - "testing" - "time" - - "github.com/dexon-foundation/dexon-consensus/common" - "github.com/dexon-foundation/dexon-consensus/core/blockdb" - "github.com/dexon-foundation/dexon-consensus/core/types" - "github.com/stretchr/testify/suite" -) - -type RevealerTestSuite struct { - suite.Suite - - db blockdb.BlockDatabase - totalBlockCount int -} - -func (s *RevealerTestSuite) SetupSuite() { - var ( - err error - genesisTime = time.Now().UTC() - ) - // Setup block database. - s.db, err = blockdb.NewMemBackedBlockDB() - s.Require().NoError(err) - - // Randomly generate blocks. - config := &BlocksGeneratorConfig{ - NumChains: 19, - MinBlockTimeInterval: 250 * time.Millisecond, - } - gen := NewBlocksGenerator(config, nil, stableRandomHash) - s.Require().NoError(gen.Generate( - 0, - genesisTime, - genesisTime.Add(30*time.Second), - s.db)) - // Cache the count of total generated block. - iter, err := s.db.GetAll() - s.Require().NoError(err) - blocks, err := loadAllBlocks(iter) - s.Require().NoError(err) - s.totalBlockCount = len(blocks) -} - -func (s *RevealerTestSuite) baseTest( - revealer Revealer, - repeat int, - checkFunc func(*types.Block, map[common.Hash]struct{})) { - - revealingSequence := map[string]struct{}{} - for i := 0; i < repeat; i++ { - revealed := map[common.Hash]struct{}{} - sequence := "" - for { - b, err := revealer.Next() - if err != nil { - if err == blockdb.ErrIterationFinished { - err = nil - break - } - s.Require().NotNil(err) - } - checkFunc(&b, revealed) - revealed[b.Hash] = struct{}{} - sequence += b.Hash.String() + "," - } - s.Len(revealed, s.totalBlockCount) - revealingSequence[sequence] = struct{}{} - revealer.Reset() - } - // It should be reasonable to reveal at least two - // different sequence. - s.True(len(revealingSequence) > 1) - -} - -func (s *RevealerTestSuite) TestRandomReveal() { - // This test case would make sure we could at least generate - // two different revealing sequence when revealing more than - // 10 times. - iter, err := s.db.GetAll() - s.Require().Nil(err) - revealer, err := NewRandomRevealer(iter) - s.Require().Nil(err) - - checkFunc := func(b *types.Block, revealed map[common.Hash]struct{}) { - // Make sure the revealer won't reveal the same block twice. - _, alreadyRevealed := revealed[b.Hash] - s.False(alreadyRevealed) - } - s.baseTest(revealer, 10, checkFunc) -} - -func (s *RevealerTestSuite) TestRandomDAGReveal() { - // This test case would make sure we could at least generate - // two different revealing sequence when revealing more than - // 10 times, and each of them would form valid DAGs during - // revealing. - - iter, err := s.db.GetAll() - s.Require().Nil(err) - revealer, err := NewRandomDAGRevealer(iter) - s.Require().Nil(err) - - checkFunc := func(b *types.Block, revealed map[common.Hash]struct{}) { - // Make sure this revealer won't reveal - // the same block twice. - _, alreadyRevealed := revealed[b.Hash] - s.False(alreadyRevealed) - // Make sure the newly revealed block would still - // form a valid DAG after added to revealed blocks. - s.True(isAllAckingBlockRevealed(b, revealed)) - } - s.baseTest(revealer, 10, checkFunc) -} - -func (s *RevealerTestSuite) TestRandomTipReveal() { - // This test case would make sure we could at least generate - // two different revealing sequence when revealing more than - // 10 times. - iter, err := s.db.GetAll() - s.Require().Nil(err) - revealer, err := NewRandomTipRevealer(iter) - s.Require().Nil(err) - - checkFunc := func(b *types.Block, revealed map[common.Hash]struct{}) { - // Make sure the revealer won't reveal the same block twice. - _, alreadyRevealed := revealed[b.Hash] - s.False(alreadyRevealed) - // Make sure the parent is already revealed. - if b.Position.Height == 0 { - return - } - _, alreadyRevealed = revealed[b.ParentHash] - s.True(alreadyRevealed) - } - s.baseTest(revealer, 10, checkFunc) -} - -func (s *RevealerTestSuite) TestCompactionChainReveal() { - db, err := blockdb.NewMemBackedBlockDB() - s.Require().NoError(err) - // Put several blocks with finalization field ready. - b1 := &types.Block{ - Hash: common.NewRandomHash(), - Finalization: types.FinalizationResult{ - Height: 1, - }} - b2 := &types.Block{ - Hash: common.NewRandomHash(), - Finalization: types.FinalizationResult{ - ParentHash: b1.Hash, - Height: 2, - }} - b3 := &types.Block{ - Hash: common.NewRandomHash(), - Finalization: types.FinalizationResult{ - ParentHash: b2.Hash, - Height: 3, - }} - s.Require().NoError(db.Put(*b1)) - s.Require().NoError(db.Put(*b3)) - iter, err := db.GetAll() - s.Require().NoError(err) - // The compaction chain is not complete, we can't construct a revealer - // instance successfully. - r, err := NewCompactionChainRevealer(iter, 0) - s.Require().Nil(r) - s.Require().IsType(ErrNotValidCompactionChain, err) - // Put a block to make the compaction chain complete. - s.Require().NoError(db.Put(*b2)) - // We can construct that revealer now. - iter, err = db.GetAll() - s.Require().NoError(err) - r, err = NewCompactionChainRevealer(iter, 0) - s.Require().NotNil(r) - s.Require().NoError(err) - // The revealing order should be ok. - chk := func(h uint64) { - b, err := r.Next() - s.Require().NoError(err) - s.Require().Equal(b.Finalization.Height, h) - } - chk(1) - chk(2) - chk(3) - // Iteration should be finished - _, err = r.Next() - s.Require().IsType(blockdb.ErrIterationFinished, err) - // Test 'startHeight' parameter. - iter, err = db.GetAll() - s.Require().NoError(err) - r, err = NewCompactionChainRevealer(iter, 2) - s.Require().NotNil(r) - s.Require().NoError(err) - chk(2) - chk(3) -} - -func TestRevealer(t *testing.T) { - suite.Run(t, new(RevealerTestSuite)) -} diff --git a/core/test/stopper.go b/core/test/stopper.go index 40868d8..2ba31d3 100644 --- a/core/test/stopper.go +++ b/core/test/stopper.go @@ -20,7 +20,7 @@ package test import ( "sync" - "github.com/dexon-foundation/dexon-consensus/core/blockdb" + "github.com/dexon-foundation/dexon-consensus/core/db" "github.com/dexon-foundation/dexon-consensus/core/types" ) @@ -28,7 +28,7 @@ import ( // at least X blocks proposed by itself. type StopByConfirmedBlocks struct { apps map[types.NodeID]*App - dbs map[types.NodeID]blockdb.BlockDatabase + dbs map[types.NodeID]db.Database lastCheckDelivered map[types.NodeID]int confirmedBlocks map[types.NodeID]int blockCount int @@ -39,7 +39,7 @@ type StopByConfirmedBlocks struct { func NewStopByConfirmedBlocks( blockCount int, apps map[types.NodeID]*App, - dbs map[types.NodeID]blockdb.BlockDatabase) *StopByConfirmedBlocks { + dbs map[types.NodeID]db.Database) *StopByConfirmedBlocks { confirmedBlocks := make(map[types.NodeID]int) for nID := range apps { confirmedBlocks[nID] = 0 @@ -60,10 +60,10 @@ func (s *StopByConfirmedBlocks) ShouldStop(nID types.NodeID) bool { // Accumulate confirmed blocks proposed by this node in this round. lastChecked := s.lastCheckDelivered[nID] currentConfirmedBlocks := s.confirmedBlocks[nID] - db := s.dbs[nID] + dbInst := s.dbs[nID] s.apps[nID].WithLock(func(app *App) { for _, h := range app.DeliverSequence[lastChecked:] { - b, err := db.Get(h) + b, err := dbInst.GetBlock(h) if err != nil { panic(err) } @@ -90,7 +90,7 @@ type StopByRound struct { currentRounds map[types.NodeID]uint64 lastCheckDelivered map[types.NodeID]int apps map[types.NodeID]*App - dbs map[types.NodeID]blockdb.BlockDatabase + dbs map[types.NodeID]db.Database lock sync.Mutex } @@ -98,7 +98,7 @@ type StopByRound struct { func NewStopByRound( round uint64, apps map[types.NodeID]*App, - dbs map[types.NodeID]blockdb.BlockDatabase) *StopByRound { + dbs map[types.NodeID]db.Database) *StopByRound { return &StopByRound{ untilRound: round, currentRounds: make(map[types.NodeID]uint64), @@ -115,10 +115,10 @@ func (s *StopByRound) ShouldStop(nID types.NodeID) bool { // Cache latest round of this node. if curRound := s.currentRounds[nID]; curRound < s.untilRound { lastChecked := s.lastCheckDelivered[nID] - db := s.dbs[nID] + dbInst := s.dbs[nID] s.apps[nID].WithLock(func(app *App) { for _, h := range app.DeliverSequence[lastChecked:] { - b, err := db.Get(h) + b, err := dbInst.GetBlock(h) if err != nil { panic(err) } diff --git a/core/test/stopper_test.go b/core/test/stopper_test.go index b34e7b2..d296727 100644 --- a/core/test/stopper_test.go +++ b/core/test/stopper_test.go @@ -23,7 +23,7 @@ import ( "github.com/dexon-foundation/dexon-consensus/common" "github.com/dexon-foundation/dexon-consensus/core" - "github.com/dexon-foundation/dexon-consensus/core/blockdb" + "github.com/dexon-foundation/dexon-consensus/core/db" "github.com/dexon-foundation/dexon-consensus/core/types" "github.com/stretchr/testify/suite" ) @@ -33,11 +33,11 @@ type StopperTestSuite struct { } func (s *StopperTestSuite) deliver( - blocks []*types.Block, app *App, db blockdb.BlockDatabase) { + blocks []*types.Block, app *App, dbInst db.Database) { hashes := common.Hashes{} for _, b := range blocks { hashes = append(hashes, b.Hash) - s.Require().NoError(db.Put(*b)) + s.Require().NoError(dbInst.PutBlock(*b)) } for _, h := range hashes { app.BlockConfirmed(types.Block{Hash: h}) @@ -53,9 +53,9 @@ func (s *StopperTestSuite) deliver( func (s *StopperTestSuite) deliverToAllNodes( blocks []*types.Block, apps map[types.NodeID]*App, - dbs map[types.NodeID]blockdb.BlockDatabase) { + dbInsts map[types.NodeID]db.Database) { for nID := range apps { - s.deliver(blocks, apps[nID], dbs[nID]) + s.deliver(blocks, apps[nID], dbInsts[nID]) } } @@ -64,23 +64,23 @@ func (s *StopperTestSuite) TestStopByConfirmedBlocks() { // all nodes confirmed at least 'x' count of blocks produced // by themselves. var ( - req = s.Require() - apps = make(map[types.NodeID]*App) - dbs = make(map[types.NodeID]blockdb.BlockDatabase) - nodes = GenerateRandomNodeIDs(2) + req = s.Require() + apps = make(map[types.NodeID]*App) + dbInsts = make(map[types.NodeID]db.Database) + nodes = GenerateRandomNodeIDs(2) ) for _, nID := range nodes { apps[nID] = NewApp(nil) - db, err := blockdb.NewMemBackedBlockDB() + dbInst, err := db.NewMemBackedDB() req.NoError(err) - dbs[nID] = db + dbInsts[nID] = dbInst } - stopper := NewStopByConfirmedBlocks(2, apps, dbs) + stopper := NewStopByConfirmedBlocks(2, apps, dbInsts) b00 := &types.Block{ ProposerID: nodes[0], Hash: common.NewRandomHash(), } - s.deliverToAllNodes([]*types.Block{b00}, apps, dbs) + s.deliverToAllNodes([]*types.Block{b00}, apps, dbInsts) b10 := &types.Block{ ProposerID: nodes[1], Hash: common.NewRandomHash(), @@ -90,21 +90,21 @@ func (s *StopperTestSuite) TestStopByConfirmedBlocks() { ParentHash: b10.Hash, Hash: common.NewRandomHash(), } - s.deliverToAllNodes([]*types.Block{b10, b11}, apps, dbs) + s.deliverToAllNodes([]*types.Block{b10, b11}, apps, dbInsts) req.False(stopper.ShouldStop(nodes[1])) b12 := &types.Block{ ProposerID: nodes[1], ParentHash: b11.Hash, Hash: common.NewRandomHash(), } - s.deliverToAllNodes([]*types.Block{b12}, apps, dbs) + s.deliverToAllNodes([]*types.Block{b12}, apps, dbInsts) req.False(stopper.ShouldStop(nodes[1])) b01 := &types.Block{ ProposerID: nodes[0], ParentHash: b00.Hash, Hash: common.NewRandomHash(), } - s.deliverToAllNodes([]*types.Block{b01}, apps, dbs) + s.deliverToAllNodes([]*types.Block{b01}, apps, dbInsts) req.True(stopper.ShouldStop(nodes[0])) } @@ -112,18 +112,18 @@ func (s *StopperTestSuite) TestStopByRound() { // This test case make sure at least one block from round R // is delivered by each node. var ( - req = s.Require() - apps = make(map[types.NodeID]*App) - dbs = make(map[types.NodeID]blockdb.BlockDatabase) - nodes = GenerateRandomNodeIDs(2) + req = s.Require() + apps = make(map[types.NodeID]*App) + dbInsts = make(map[types.NodeID]db.Database) + nodes = GenerateRandomNodeIDs(2) ) for _, nID := range nodes { apps[nID] = NewApp(nil) - db, err := blockdb.NewMemBackedBlockDB() + dbInst, err := db.NewMemBackedDB() req.NoError(err) - dbs[nID] = db + dbInsts[nID] = dbInst } - stopper := NewStopByRound(10, apps, dbs) + stopper := NewStopByRound(10, apps, dbInsts) b00 := &types.Block{ ProposerID: nodes[0], Position: types.Position{ @@ -133,7 +133,7 @@ func (s *StopperTestSuite) TestStopByRound() { }, Hash: common.NewRandomHash(), } - s.deliverToAllNodes([]*types.Block{b00}, apps, dbs) + s.deliverToAllNodes([]*types.Block{b00}, apps, dbInsts) b10 := &types.Block{ ProposerID: nodes[1], Position: types.Position{ @@ -153,7 +153,7 @@ func (s *StopperTestSuite) TestStopByRound() { }, Hash: common.NewRandomHash(), } - s.deliverToAllNodes([]*types.Block{b10, b11}, apps, dbs) + s.deliverToAllNodes([]*types.Block{b10, b11}, apps, dbInsts) req.False(stopper.ShouldStop(nodes[0])) req.False(stopper.ShouldStop(nodes[1])) // Deliver one block at round 10 to node0 @@ -168,11 +168,11 @@ func (s *StopperTestSuite) TestStopByRound() { Hash: common.NewRandomHash(), } // None should stop when only one node reach that round. - s.deliver([]*types.Block{b12}, apps[nodes[0]], dbs[nodes[0]]) + s.deliver([]*types.Block{b12}, apps[nodes[0]], dbInsts[nodes[0]]) req.False(stopper.ShouldStop(nodes[0])) req.False(stopper.ShouldStop(nodes[1])) // Everyone should stop now. - s.deliver([]*types.Block{b12}, apps[nodes[1]], dbs[nodes[1]]) + s.deliver([]*types.Block{b12}, apps[nodes[1]], dbInsts[nodes[1]]) req.True(stopper.ShouldStop(nodes[1])) req.True(stopper.ShouldStop(nodes[0])) } diff --git a/core/total-ordering-syncer_test.go b/core/total-ordering-syncer_test.go index 92b5dac..208c1a3 100644 --- a/core/total-ordering-syncer_test.go +++ b/core/total-ordering-syncer_test.go @@ -24,7 +24,7 @@ import ( "github.com/stretchr/testify/suite" "github.com/dexon-foundation/dexon-consensus/common" - "github.com/dexon-foundation/dexon-consensus/core/blockdb" + "github.com/dexon-foundation/dexon-consensus/core/db" "github.com/dexon-foundation/dexon-consensus/core/test" "github.com/dexon-foundation/dexon-consensus/core/types" ) @@ -34,7 +34,7 @@ type TotalOrderingSyncerTestSuite struct { } func (s *TotalOrderingSyncerTestSuite) genDeliverySet(numChains uint32) ( - deliverySet [][]*types.Block, revealer *test.RandomDAGRevealer) { + deliverySet [][]*types.Block, revealer *test.RandomDAGBlockRevealer) { genesisTime := time.Now().UTC() genesisConfig := &types.Config{ @@ -51,25 +51,25 @@ func (s *TotalOrderingSyncerTestSuite) genDeliverySet(numChains uint32) ( MinBlockTimeInterval: 250 * time.Millisecond, }, nil, hashBlock) - db, err := blockdb.NewMemBackedBlockDB() + dbInst, err := db.NewMemBackedDB() s.Require().NoError(err) s.Require().NoError(gen.Generate( 0, genesisTime, genesisTime.Add(20*time.Second), - db)) - iter, err := db.GetAll() + dbInst)) + iter, err := dbInst.GetAllBlocks() s.Require().NoError(err) - revealer, err = test.NewRandomDAGRevealer(iter) + revealer, err = test.NewRandomDAGBlockRevealer(iter) s.Require().NoError(err) revealer.Reset() for { // Reveal next block. - b, err := revealer.Next() + b, err := revealer.NextBlock() if err != nil { - if err == blockdb.ErrIterationFinished { + if err == db.ErrIterationFinished { err = nil break } @@ -120,7 +120,7 @@ func (s *TotalOrderingSyncerTestSuite) TestRandomSync() { revealer.Reset() for i := 0; i < skipDAG; i++ { - _, err := revealer.Next() + _, err := revealer.NextBlock() s.Require().NoError(err) } @@ -134,9 +134,9 @@ func (s *TotalOrderingSyncerTestSuite) TestRandomSync() { deliverySetMap2 := make(map[int][]common.Hash) for { - b, err := revealer.Next() + b, err := revealer.NextBlock() if err != nil { - if err != blockdb.ErrIterationFinished { + if err != db.ErrIterationFinished { s.Require().NoError(err) } err = nil @@ -284,9 +284,9 @@ func (s *TotalOrderingSyncerTestSuite) TestBootstrap() { actualDeliveredNum := 0 revealer.Reset() for { - b, err := revealer.Next() + b, err := revealer.NextBlock() if err != nil { - if err != blockdb.ErrIterationFinished { + if err != db.ErrIterationFinished { s.Require().NoError(err) } err = nil diff --git a/core/total-ordering_test.go b/core/total-ordering_test.go index 23da4e1..5cee0b0 100644 --- a/core/total-ordering_test.go +++ b/core/total-ordering_test.go @@ -24,7 +24,7 @@ import ( "time" "github.com/dexon-foundation/dexon-consensus/common" - "github.com/dexon-foundation/dexon-consensus/core/blockdb" + "github.com/dexon-foundation/dexon-consensus/core/db" "github.com/dexon-foundation/dexon-consensus/core/test" "github.com/dexon-foundation/dexon-consensus/core/types" "github.com/stretchr/testify/suite" @@ -52,14 +52,14 @@ func (s *TotalOrderingTestSuite) genGenesisBlock( } func (s *TotalOrderingTestSuite) performOneRun( - to *totalOrdering, revealer test.Revealer) (revealed, ordered string) { + to *totalOrdering, revealer test.BlockRevealer) (revealed, ordered string) { revealer.Reset() curRound := uint64(0) for { // Reveal next block. - b, err := revealer.Next() + b, err := revealer.NextBlock() if err != nil { - if err == blockdb.ErrIterationFinished { + if err == db.ErrIterationFinished { err = nil break } @@ -940,18 +940,18 @@ func (s *TotalOrderingTestSuite) baseTestRandomlyGeneratedBlocks( NumChains: chainNum, MinBlockTimeInterval: 250 * time.Millisecond, }, ackingCountGenerator, hashBlock) - db, err := blockdb.NewMemBackedBlockDB() + dbInst, err := db.NewMemBackedDB() req.NoError(err) req.NoError(gen.Generate( 0, genesisTime, genesisTime.Add(20*time.Second), - db)) - iter, err := db.GetAll() + dbInst)) + iter, err := dbInst.GetAllBlocks() req.NoError(err) // Setup a revealer that would reveal blocks forming // valid DAGs. - revealer, err := test.NewRandomDAGRevealer(iter) + revealer, err := test.NewRandomDAGBlockRevealer(iter) req.NoError(err) // TODO (mission): make this part run concurrently. for i := 0; i < repeat; i++ { @@ -1063,7 +1063,7 @@ func (s *TotalOrderingTestSuite) baseTestForRoundChange( req = s.Require() genesisTime = time.Now().UTC() ) - db, err := blockdb.NewMemBackedBlockDB() + dbInst, err := db.NewMemBackedDB() req.NoError(err) // Generate DAG for rounds. // NOTE: the last config won't be tested, just avoid panic @@ -1073,14 +1073,14 @@ func (s *TotalOrderingTestSuite) baseTestForRoundChange( gen := test.NewBlocksGenerator( test.NewBlocksGeneratorConfig(config), nil, hashBlock) end := begin.Add(config.RoundInterval) - req.NoError(gen.Generate(uint64(roundID), begin, end, db)) + req.NoError(gen.Generate(uint64(roundID), begin, end, dbInst)) begin = end } // Test, just dump the whole DAG to total ordering and make sure // repeating it won't change it delivered sequence. - iter, err := db.GetAll() + iter, err := dbInst.GetAllBlocks() req.NoError(err) - revealer, err := test.NewRandomDAGRevealer(iter) + revealer, err := test.NewRandomDAGBlockRevealer(iter) req.NoError(err) revealingSequence := make(map[string]struct{}) orderingSequence := make(map[string]struct{}) @@ -1196,14 +1196,14 @@ func (s *TotalOrderingTestSuite) TestSync() { NumChains: numChains, MinBlockTimeInterval: 250 * time.Millisecond, }, nil, hashBlock) - db, err := blockdb.NewMemBackedBlockDB() + dbInst, err := db.NewMemBackedDB() req.NoError(err) - err = gen.Generate(0, genesisTime, genesisTime.Add(20*time.Second), db) + err = gen.Generate(0, genesisTime, genesisTime.Add(20*time.Second), dbInst) req.NoError(err) - iter, err := db.GetAll() + iter, err := dbInst.GetAllBlocks() req.NoError(err) - revealer, err := test.NewRandomDAGRevealer(iter) + revealer, err := test.NewRandomDAGBlockRevealer(iter) req.NoError(err) genesisConfig := &types.Config{ @@ -1220,9 +1220,9 @@ func (s *TotalOrderingTestSuite) TestSync() { })) deliveredBlockSets1 := [][]*types.Block{} for { - b, err := revealer.Next() + b, err := revealer.NextBlock() if err != nil { - if err == blockdb.ErrIterationFinished { + if err == db.ErrIterationFinished { err = nil break } @@ -1303,7 +1303,7 @@ func (s *TotalOrderingTestSuite) TestSyncWithConfigChange() { } blocks := []*types.Block{} - db, err := blockdb.NewMemBackedBlockDB() + dbInst, err := db.NewMemBackedDB() req.NoError(err) for i, cfg := range configs { @@ -1315,21 +1315,21 @@ func (s *TotalOrderingTestSuite) TestSyncWithConfigChange() { uint64(i), genesisTime.Add(time.Duration(i)*cfg.RoundInterval), genesisTime.Add(time.Duration(i+1)*cfg.RoundInterval), - db, + dbInst, ) req.NoError(err) } - iter, err := db.GetAll() + iter, err := dbInst.GetAllBlocks() req.NoError(err) - revealer, err := test.NewRandomDAGRevealer(iter) + revealer, err := test.NewRandomDAGBlockRevealer(iter) req.NoError(err) for { - b, err := revealer.Next() + b, err := revealer.NextBlock() if err != nil { - if err == blockdb.ErrIterationFinished { + if err == db.ErrIterationFinished { err = nil break } diff --git a/integration_test/consensus_test.go b/integration_test/consensus_test.go index 6d693c8..698305c 100644 --- a/integration_test/consensus_test.go +++ b/integration_test/consensus_test.go @@ -26,8 +26,8 @@ import ( "github.com/dexon-foundation/dexon-consensus/common" "github.com/dexon-foundation/dexon-consensus/core" - "github.com/dexon-foundation/dexon-consensus/core/blockdb" "github.com/dexon-foundation/dexon-consensus/core/crypto" + "github.com/dexon-foundation/dexon-consensus/core/db" "github.com/dexon-foundation/dexon-consensus/core/syncer" "github.com/dexon-foundation/dexon-consensus/core/test" "github.com/dexon-foundation/dexon-consensus/core/types" @@ -46,7 +46,7 @@ type node struct { con *core.Consensus app *test.App gov *test.Governance - db blockdb.BlockDatabase + db db.Database network *test.Network } @@ -65,7 +65,7 @@ func (s *ConsensusTestSuite) setupNodes( nodes := make(map[types.NodeID]*node) wg.Add(len(prvKeys)) for _, k := range prvKeys { - db, err := blockdb.NewMemBackedBlockDB() + dbInst, err := db.NewMemBackedDB() s.Require().NoError(err) // Prepare essential modules: app, gov, db. networkModule := test.NewNetwork( @@ -83,12 +83,12 @@ func (s *ConsensusTestSuite) setupNodes( dMoment, app, gov, - db, + dbInst, networkModule, k, &common.NullLogger{}, ) - nodes[con.ID] = &node{con.ID, con, app, gov, db, networkModule} + nodes[con.ID] = &node{con.ID, con, app, gov, dbInst, networkModule} go func() { defer wg.Done() s.Require().NoError(networkModule.Setup(serverChannel)) @@ -121,11 +121,11 @@ func (s *ConsensusTestSuite) syncBlocksWithSomeNode( syncerHeight = nextSyncHeight // Setup revealer. - DBAll, err := sourceNode.db.GetAll() + DBAll, err := sourceNode.db.GetAllBlocks() if err != nil { return } - r, err := test.NewCompactionChainRevealer(DBAll, nextSyncHeight) + r, err := test.NewCompactionChainBlockRevealer(DBAll, nextSyncHeight) if err != nil { return } @@ -153,9 +153,9 @@ func (s *ConsensusTestSuite) syncBlocksWithSomeNode( } for { var b types.Block - b, err = r.Next() + b, err = r.NextBlock() if err != nil { - if err == blockdb.ErrIterationFinished { + if err == db.ErrIterationFinished { err = nil if syncBlocks() { break diff --git a/integration_test/node.go b/integration_test/node.go index aa3a703..511b5fd 100644 --- a/integration_test/node.go +++ b/integration_test/node.go @@ -23,8 +23,8 @@ import ( "github.com/dexon-foundation/dexon-consensus/common" "github.com/dexon-foundation/dexon-consensus/core" - "github.com/dexon-foundation/dexon-consensus/core/blockdb" "github.com/dexon-foundation/dexon-consensus/core/crypto" + "github.com/dexon-foundation/dexon-consensus/core/db" "github.com/dexon-foundation/dexon-consensus/core/test" "github.com/dexon-foundation/dexon-consensus/core/types" "github.com/dexon-foundation/dexon-consensus/core/utils" @@ -76,7 +76,7 @@ type Node struct { appModule *test.App stateModule *test.State govModule *test.Governance - dbModule blockdb.BlockDatabase + dbModule db.Database broadcastTargets map[types.NodeID]struct{} networkLatency test.LatencyModel proposingLatency test.LatencyModel @@ -99,8 +99,8 @@ func newNode( // Load all configs prepared in core.Governance into core.Lattice. copiedGov := gov.Clone() configs := loadAllConfigs(copiedGov) - // Setup blockdb. - db, err := blockdb.NewMemBackedBlockDB() + // Setup db. + dbInst, err := db.NewMemBackedDB() if err != nil { return nil, err } @@ -114,7 +114,7 @@ func newNode( core.NewAuthenticator(privateKey), app, app, - db, + dbInst, &common.NullLogger{}) n := &Node{ ID: types.NewNodeID(privateKey.PublicKey()), @@ -125,7 +125,7 @@ func newNode( proposingLatency: proposingLatency, appModule: app, stateModule: copiedGov.State(), - dbModule: db, + dbModule: dbInst, govModule: copiedGov, lattice: lattice, latticeMaxNumChains: configs[0].NumChains, @@ -254,7 +254,7 @@ func (n *Node) processBlock(b *types.Block) (events []*test.Event, err error) { } // Deliver blocks. for _, b = range delivered { - if err = n.dbModule.Put(*b); err != nil { + if err = n.dbModule.PutBlock(*b); err != nil { panic(err) } b.Finalization.Height = n.prevFinalHeight + 1 @@ -349,7 +349,7 @@ func (n *Node) app() *test.App { return n.appModule } -func (n *Node) db() blockdb.BlockDatabase { +func (n *Node) db() db.Database { return n.dbModule } diff --git a/integration_test/utils.go b/integration_test/utils.go index a6cc139..2fbfa1a 100644 --- a/integration_test/utils.go +++ b/integration_test/utils.go @@ -22,8 +22,8 @@ import ( "time" "github.com/dexon-foundation/dexon-consensus/core" - "github.com/dexon-foundation/dexon-consensus/core/blockdb" "github.com/dexon-foundation/dexon-consensus/core/crypto" + "github.com/dexon-foundation/dexon-consensus/core/db" "github.com/dexon-foundation/dexon-consensus/core/test" "github.com/dexon-foundation/dexon-consensus/core/types" ) @@ -125,13 +125,13 @@ func VerifyApps(apps map[types.NodeID]*test.App) (err error) { return } -// CollectAppAndDBFromNodes collects test.App and blockdb.BlockDatabase +// CollectAppAndDBFromNodes collects test.App and db.Database // from nodes. func CollectAppAndDBFromNodes(nodes map[types.NodeID]*Node) ( apps map[types.NodeID]*test.App, - dbs map[types.NodeID]blockdb.BlockDatabase) { + dbs map[types.NodeID]db.Database) { apps = make(map[types.NodeID]*test.App) - dbs = make(map[types.NodeID]blockdb.BlockDatabase) + dbs = make(map[types.NodeID]db.Database) for nID, node := range nodes { apps[nID] = node.app() dbs[nID] = node.db() diff --git a/simulation/node.go b/simulation/node.go index bacf86a..4d1e4ff 100644 --- a/simulation/node.go +++ b/simulation/node.go @@ -24,8 +24,8 @@ import ( "github.com/dexon-foundation/dexon-consensus/common" "github.com/dexon-foundation/dexon-consensus/core" - "github.com/dexon-foundation/dexon-consensus/core/blockdb" "github.com/dexon-foundation/dexon-consensus/core/crypto" + "github.com/dexon-foundation/dexon-consensus/core/db" "github.com/dexon-foundation/dexon-consensus/core/test" "github.com/dexon-foundation/dexon-consensus/core/types" "github.com/dexon-foundation/dexon-consensus/simulation/config" @@ -55,7 +55,7 @@ type message struct { // node represents a node in DexCon. type node struct { app core.Application - db blockdb.BlockDatabase + db db.Database gov *test.Governance netModule *test.Network ID types.NodeID @@ -81,7 +81,7 @@ func newNode( PeerPort: peerPort, }) id := types.NewNodeID(pubKey) - db, err := blockdb.NewMemBackedBlockDB(id.String() + ".blockdb") + dbInst, err := db.NewMemBackedDB(id.String() + ".db") if err != nil { panic(err) } @@ -118,7 +118,7 @@ func newNode( prvKey: prvKey, app: newSimApp(id, netModule, gov.State()), gov: gov, - db: db, + db: dbInst, netModule: netModule, } } -- cgit