aboutsummaryrefslogblamecommitdiffstats
path: root/core/test/blocks-generator_test.go
blob: 1baa9c531b90c751a1ac24bb15d9e3cc19f27722 (plain) (tree)
1
2
3


                                                         













                                                                               





                 
                                                                 
                                                                       









                                                                            



                                     
                                                        


                                                
                                                              
                            
                                            



                                                                 
                                                             







                                                          




                                                                               


                                                                         







                                                                            

                                                              



                                            
                                                               
                                                                




                                                      
                                                        








                                                                                
                                                                                             
                                 
                                                                                                     



                                                                               
                                                                               





                                           

                                                                 





                                                                            




                                                

                                                                   
                    
                                    

















                                                        

                                                                          
                    
                                    
                                                          














                                                        
                                                                         

 


                                                  
// Copyright 2018 The dexon-consensus-core Authors
// This file is part of the dexon-consensus-core library.
//
// The dexon-consensus-core 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-core 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-core library. If not, see
// <http://www.gnu.org/licenses/>.

package test

import (
    "sort"
    "testing"

    "github.com/dexon-foundation/dexon-consensus-core/common"
    "github.com/dexon-foundation/dexon-consensus-core/core/blockdb"
    "github.com/dexon-foundation/dexon-consensus-core/core/types"
    "github.com/stretchr/testify/suite"
)

type BlocksGeneratorTestCase struct {
    suite.Suite
}

func (s *BlocksGeneratorTestCase) TestGenerate() {
    // This test case is to make sure the generated blocks are legimate.
    var (
        chainNum = uint32(19)
        blockNum = 50
    )
    gen := NewBlocksGenerator(nil, stableRandomHash)
    db, err := blockdb.NewMemBackedBlockDB()
    s.Require().Nil(err)

    keys, err := gen.Generate(chainNum, blockNum, nil, db)
    s.Require().Nil(err)
    s.Require().Len(keys, int(chainNum))

    // Load all blocks in that database for further checking.
    iter, err := db.GetAll()
    s.Require().Nil(err)
    blocksByNode := make(map[types.NodeID][]*types.Block)
    blocksByHash := make(map[common.Hash]*types.Block)
    for {
        block, err := iter.Next()
        if err == blockdb.ErrIterationFinished {
            break
        }
        s.Nil(err)

        // TODO(mission): Make sure each block is correctly signed once
        //                we have a way to access core.hashBlock.
        s.Require().NotEqual(block.Hash, common.Hash{})
        s.Require().NotEmpty(block.Signature)

        blocksByNode[block.ProposerID] =
            append(blocksByNode[block.ProposerID], &block)
        sort.Sort(types.ByHeight(blocksByNode[block.ProposerID]))
        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.
    //  - No Acks in genesis bloc
    for _, blocks := range blocksByNode {
        lastAckingHeights := map[types.NodeID]uint64{}
        s.Require().NotEmpty(blocks)

        // Check genesis block.
        genesisBlock := blocks[0]
        s.Equal(genesisBlock.ParentHash, common.Hash{})
        s.Equal(genesisBlock.Position.Height, uint64(0))
        s.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]
                s.Require().NotNil(ackedBlock)
                prevAckingHeight, exists :=
                    lastAckingHeights[ackedBlock.ProposerID]
                if exists {
                    s.True(prevAckingHeight < ackedBlock.Position.Height)
                }
                lastAckingHeights[ackedBlock.ProposerID] = 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.
                s.Equal(block.Position.Height, uint64(index+1))
            }
            s.True(parentAcked)
        }
    }
}

func (s *BlocksGeneratorTestCase) TestGenerateWithMaxAckCount() {
    var (
        chainNum         = uint32(13)
        blockNum         = 50
        gen              = NewBlocksGenerator(nil, stableRandomHash)
        req              = s.Require()
        totalAckingCount = 0
        totalBlockCount  = 0
    )

    // Generate with 0 acks.
    db, err := blockdb.NewMemBackedBlockDB()
    req.Nil(err)
    keys, err := gen.Generate(
        chainNum, blockNum, MaxAckingCountGenerator(0), db)
    req.Nil(err)
    req.Len(keys, int(chainNum))
    // Load blocks to check their acking count.
    iter, err := db.GetAll()
    req.Nil(err)
    for {
        block, err := iter.Next()
        if err == blockdb.ErrIterationFinished {
            break
        }
        req.Nil(err)
        if block.IsGenesis() {
            continue
        }
        req.Len(block.Acks, 1)
    }

    // Generate with acks as many as possible.
    db, err = blockdb.NewMemBackedBlockDB()
    req.Nil(err)
    keys, err = gen.Generate(
        chainNum, blockNum, MaxAckingCountGenerator(chainNum), db)
    req.Nil(err)
    req.Len(keys, int(chainNum))
    // Load blocks to verify the average acking count.
    iter, err = db.GetAll()
    req.Nil(err)
    for {
        block, err := iter.Next()
        if err == blockdb.ErrIterationFinished {
            break
        }
        req.Nil(err)
        if block.IsGenesis() {
            continue
        }
        totalAckingCount += len(block.Acks)
        totalBlockCount++
    }
    req.NotZero(totalBlockCount)
    req.True((totalAckingCount / totalBlockCount) >= int(chainNum/2))
}

func TestBlocksGenerator(t *testing.T) {
    suite.Run(t, new(BlocksGeneratorTestCase))
}