diff options
Diffstat (limited to 'core/test/blocks-generator_test.go')
-rw-r--r-- | core/test/blocks-generator_test.go | 323 |
1 files changed, 0 insertions, 323 deletions
diff --git a/core/test/blocks-generator_test.go b/core/test/blocks-generator_test.go deleted file mode 100644 index bcbd749..0000000 --- a/core/test/blocks-generator_test.go +++ /dev/null @@ -1,323 +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 -// <http://www.gnu.org/licenses/>. - -package test - -import ( - "sort" - "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 BlocksGeneratorTestSuite struct { - suite.Suite -} - -func (s *BlocksGeneratorTestSuite) TestGenerate() { - // This test case is to make sure the generated blocks are legimate. - var ( - config = &BlocksGeneratorConfig{ - NumChains: 19, - MinBlockTimeInterval: 200 * time.Millisecond, - } - gen = NewBlocksGenerator(config, nil) - req = s.Require() - beginTime = time.Now().UTC() - endTime = beginTime.Add(time.Minute) - ) - dbInst, err := db.NewMemBackedDB() - req.NoError(err) - req.NoError(gen.Generate(1, beginTime, endTime, dbInst)) - // Load all blocks in that database for further checking. - iter, err := dbInst.GetAllBlocks() - req.NoError(err) - blocksByChain := make(map[uint32][]*types.Block) - blocksByHash := make(map[common.Hash]*types.Block) - for { - block, err := iter.NextBlock() - if err == db.ErrIterationFinished { - break - } - req.NoError(err) - // TODO(mission): Make sure each block is correctly signed once - // we have a way to access core.hashBlock. - req.NotEqual(block.Hash, common.Hash{}) - if !block.IsEmpty() { - req.NotEmpty(block.Signature) - } - req.Equal(block.Position.Round, uint64(1)) - blocksByChain[block.Position.ChainID] = - append(blocksByChain[block.Position.ChainID], &block) - sort.Sort(types.ByPosition(blocksByChain[block.Position.ChainID])) - blocksByHash[block.Hash] = &block - } - // Make sure these two rules are hold for these blocks: - // - No backward acking: the later block should only ack new blocks - // compared to its parent block. - // - Parent Ack: always ack its parent block. - // - Timestamp: timestamp are increasing, and with valid interval to - // previous block. - // - The last block of each chain should pass endTime. - // - No Acks in genesis bloc - for _, blocks := range blocksByChain { - lastAckingHeights := map[uint32]uint64{} - req.NotEmpty(blocks) - // Check genesis block. - genesisBlock := blocks[0] - req.Equal(genesisBlock.ParentHash, common.Hash{}) - req.Equal(genesisBlock.Position.Height, uint64(0)) - req.Empty(genesisBlock.Acks) - // Check normal blocks. - for index, block := range blocks[1:] { - parentAcked := false - for _, ack := range block.Acks { - if ack == block.ParentHash { - parentAcked = true - } - ackedBlock := blocksByHash[ack] - req.NotNil(ackedBlock) - prevAckingHeight, exists := - lastAckingHeights[ackedBlock.Position.ChainID] - if exists { - s.True(prevAckingHeight < ackedBlock.Position.Height) - } - lastAckingHeights[ackedBlock.Position.ChainID] = - ackedBlock.Position.Height - // Block Height should always incremental by 1. - // - // Because we iterate blocks slice from 1, - // we need to add 1 to the index. - req.Equal(block.Position.Height, uint64(index+1)) - } - req.True(parentAcked) - } - // The block time of the last block should be after end time. - req.True(blocks[len(blocks)-1].Timestamp.After(endTime)) - } -} - -func (s *BlocksGeneratorTestSuite) TestGenerateWithMaxAckCount() { - var ( - config = &BlocksGeneratorConfig{ - NumChains: 13, - MinBlockTimeInterval: 250 * time.Millisecond, - } - req = s.Require() - totalAckingCount = 0 - totalBlockCount = 0 - genesisTime = time.Now().UTC() - ) - // Generate with 0 acks. - dbInst, err := db.NewMemBackedDB() - req.NoError(err) - gen := NewBlocksGenerator(config, MaxAckingCountGenerator(0)) - req.NoError(gen.Generate( - 0, - genesisTime, - genesisTime.Add(50*time.Second), - dbInst)) - // Load blocks to check their acking count. - iter, err := dbInst.GetAllBlocks() - req.NoError(err) - for { - block, err := iter.NextBlock() - if err == db.ErrIterationFinished { - break - } - req.NoError(err) - if block.IsGenesis() { - continue - } - req.Len(block.Acks, 1) - } - // Generate with acks as many as possible. - dbInst, err = db.NewMemBackedDB() - req.NoError(err) - gen = NewBlocksGenerator(config, MaxAckingCountGenerator(config.NumChains)) - req.NoError(gen.Generate( - 0, - genesisTime, - genesisTime.Add(50*time.Second), - dbInst)) - // Load blocks to verify the average acking count. - iter, err = dbInst.GetAllBlocks() - req.NoError(err) - for { - block, err := iter.NextBlock() - if err == db.ErrIterationFinished { - break - } - req.NoError(err) - if block.IsGenesis() { - continue - } - totalAckingCount += len(block.Acks) - totalBlockCount++ - } - req.NotZero(totalBlockCount) - req.True((totalAckingCount / totalBlockCount) >= int(config.NumChains/2)) -} - -// TestFindTips make sure findTips works as expected. -func (s *BlocksGeneratorTestSuite) TestFindTips() { - var ( - config = &BlocksGeneratorConfig{ - NumChains: 10, - MinBlockTimeInterval: 250 * time.Millisecond, - } - req = s.Require() - genesisTime = time.Now().UTC() - endTime = genesisTime.Add(100 * time.Second) - ) - gen := NewBlocksGenerator(config, nil) - dbInst, err := db.NewMemBackedDB() - req.NoError(err) - req.NoError(gen.Generate( - 0, - genesisTime, - endTime, - dbInst)) - tips, err := gen.findTips(0, dbInst) - req.NoError(err) - req.Len(tips, int(config.NumChains)) - for _, b := range tips { - req.True(b.Timestamp.After(endTime)) - } -} - -func (s *BlocksGeneratorTestSuite) TestConcateBlocksFromRounds() { - // This test case run these steps: - // - 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() - ) - dbInst, err := db.NewMemBackedDB() - req.NoError(err) - // Generate round 0 blocks. - gen := NewBlocksGenerator(&BlocksGeneratorConfig{ - NumChains: 4, - MinBlockTimeInterval: 250 * time.Millisecond, - }, MaxAckingCountGenerator(4)) - req.NoError(gen.Generate( - 0, - genesisTime, - genesisTime.Add(10*time.Second), - dbInst)) - tips0, err := gen.findTips(0, dbInst) - req.NoError(err) - req.Len(tips0, 4) - // Generate round 1 blocks. - gen = NewBlocksGenerator(&BlocksGeneratorConfig{ - NumChains: 10, - MinBlockTimeInterval: 250 * time.Millisecond, - }, MaxAckingCountGenerator(10)) - req.NoError(gen.Generate( - 1, - genesisTime.Add(10*time.Second), - genesisTime.Add(20*time.Second), - dbInst)) - tips1, err := gen.findTips(1, dbInst) - req.NoError(err) - req.Len(tips1, 10) - // Generate round 2 blocks. - gen = NewBlocksGenerator(&BlocksGeneratorConfig{ - NumChains: 7, - MinBlockTimeInterval: 250 * time.Millisecond, - }, MaxAckingCountGenerator(7)) - req.NoError(gen.Generate( - 2, - genesisTime.Add(20*time.Second), - genesisTime.Add(30*time.Second), - 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 := dbInst.GetAllBlocks() - req.NoError(err) - revealer, err := NewRandomBlockRevealer(iter) - req.NoError(err) - removeTip := func(tips map[uint32]*types.Block, b *types.Block) { - toRemove := []uint32{} - for chainID, tip := range tips { - if b.ParentHash == tip.Hash { - req.Equal(b.Position.Height, tip.Position.Height+1) - req.Equal(b.Position.Round, tip.Position.Round+1) - req.True(b.IsAcking(tip.Hash)) - toRemove = append(toRemove, chainID) - } - } - for _, ID := range toRemove { - delete(tips, ID) - } - } - // Make sure all tips are acked by loading blocks from db - // and check them one by one. - for { - b, err := revealer.NextBlock() - if err != nil { - if err == db.ErrIterationFinished { - err = nil - break - } - req.NoError(err) - } - switch b.Position.Round { - case 1: - removeTip(tips0, &b) - case 2: - removeTip(tips1, &b) - } - } - req.Empty(tips0) - req.Len(tips1, 3) - req.Contains(tips1, uint32(7)) - req.Contains(tips1, uint32(8)) - req.Contains(tips1, uint32(9)) - // Check the acking frequency of last round, it might be wrong. - totalBlockCount := 0 - totalAckCount := 0 - revealer.Reset() - for { - b, err := revealer.NextBlock() - if err != nil { - if err == db.ErrIterationFinished { - err = nil - break - } - req.NoError(err) - } - if b.Position.Round != 2 { - continue - } - totalBlockCount++ - totalAckCount += len(b.Acks) - } - // At least all blocks can ack some non-parent block. - req.True(totalAckCount/totalBlockCount >= 2) -} - -func TestBlocksGenerator(t *testing.T) { - suite.Run(t, new(BlocksGeneratorTestSuite)) -} |