github.com/iotexproject/iotex-core@v1.14.1-rc1/consensus/scheme/rolldpos/rolldpos_test.go (about)

     1  // Copyright (c) 2019 IoTeX Foundation
     2  // This source code is provided 'as is' and no warranties are given as to title or non-infringement, merchantability
     3  // or fitness for purpose and, to the extent permitted by law, all liability for your use of the code is disclaimed.
     4  // This source code is governed by Apache License 2.0 that can be found in the LICENSE file.
     5  
     6  package rolldpos
     7  
     8  import (
     9  	"encoding/hex"
    10  	"fmt"
    11  	"net"
    12  	"sync"
    13  	"testing"
    14  	"time"
    15  
    16  	"github.com/facebookgo/clock"
    17  	"github.com/golang/mock/gomock"
    18  	"github.com/iotexproject/go-pkgs/crypto"
    19  	"github.com/iotexproject/go-pkgs/hash"
    20  	"github.com/iotexproject/iotex-proto/golang/iotextypes"
    21  	"github.com/pkg/errors"
    22  	"github.com/stretchr/testify/assert"
    23  	"github.com/stretchr/testify/require"
    24  	"golang.org/x/net/context"
    25  	"google.golang.org/protobuf/proto"
    26  	"google.golang.org/protobuf/types/known/timestamppb"
    27  
    28  	"github.com/iotexproject/iotex-core/action/protocol"
    29  	"github.com/iotexproject/iotex-core/action/protocol/account"
    30  	accountutil "github.com/iotexproject/iotex-core/action/protocol/account/util"
    31  	"github.com/iotexproject/iotex-core/action/protocol/rewarding"
    32  	"github.com/iotexproject/iotex-core/action/protocol/rolldpos"
    33  	"github.com/iotexproject/iotex-core/actpool"
    34  	"github.com/iotexproject/iotex-core/blockchain"
    35  	"github.com/iotexproject/iotex-core/blockchain/block"
    36  	"github.com/iotexproject/iotex-core/blockchain/blockdao"
    37  	"github.com/iotexproject/iotex-core/blockchain/filedao"
    38  	"github.com/iotexproject/iotex-core/blockchain/genesis"
    39  	"github.com/iotexproject/iotex-core/consensus/consensusfsm"
    40  	cp "github.com/iotexproject/iotex-core/crypto"
    41  	"github.com/iotexproject/iotex-core/db"
    42  	"github.com/iotexproject/iotex-core/endorsement"
    43  	"github.com/iotexproject/iotex-core/p2p/node"
    44  	"github.com/iotexproject/iotex-core/state/factory"
    45  	"github.com/iotexproject/iotex-core/test/identityset"
    46  	"github.com/iotexproject/iotex-core/test/mock/mock_blockchain"
    47  	"github.com/iotexproject/iotex-core/testutil"
    48  )
    49  
    50  type addrKeyPair struct {
    51  	priKey      crypto.PrivateKey
    52  	encodedAddr string
    53  }
    54  
    55  func TestNewRollDPoS(t *testing.T) {
    56  	t.Parallel()
    57  
    58  	ctrl := gomock.NewController(t)
    59  
    60  	g := genesis.Default
    61  	builderCfg := BuilderConfig{
    62  		Chain:              blockchain.DefaultConfig,
    63  		Consensus:          DefaultConfig,
    64  		DardanellesUpgrade: consensusfsm.DefaultDardanellesUpgradeConfig,
    65  		DB:                 db.DefaultConfig,
    66  		Genesis:            g,
    67  		SystemActive:       true,
    68  	}
    69  	rp := rolldpos.NewProtocol(
    70  		g.NumCandidateDelegates,
    71  		g.NumDelegates,
    72  		g.NumSubEpochs,
    73  	)
    74  	delegatesByEpoch := func(uint64) ([]string, error) { return nil, nil }
    75  	t.Run("normal", func(t *testing.T) {
    76  		sk := identityset.PrivateKey(0)
    77  		r, err := NewRollDPoSBuilder().
    78  			SetConfig(builderCfg).
    79  			SetAddr(identityset.Address(0).String()).
    80  			SetPriKey(sk).
    81  			SetChainManager(NewChainManager(mock_blockchain.NewMockBlockchain(ctrl))).
    82  			SetBroadcast(func(_ proto.Message) error {
    83  				return nil
    84  			}).
    85  			SetDelegatesByEpochFunc(delegatesByEpoch).
    86  			SetProposersByEpochFunc(delegatesByEpoch).
    87  			RegisterProtocol(rp).
    88  			Build()
    89  		assert.NoError(t, err)
    90  		assert.NotNil(t, r)
    91  	})
    92  	t.Run("mock-clock", func(t *testing.T) {
    93  		sk := identityset.PrivateKey(0)
    94  		r, err := NewRollDPoSBuilder().
    95  			SetConfig(builderCfg).
    96  			SetAddr(identityset.Address(0).String()).
    97  			SetPriKey(sk).
    98  			SetChainManager(NewChainManager(mock_blockchain.NewMockBlockchain(ctrl))).
    99  			SetBroadcast(func(_ proto.Message) error {
   100  				return nil
   101  			}).
   102  			SetClock(clock.NewMock()).
   103  			SetDelegatesByEpochFunc(delegatesByEpoch).
   104  			SetProposersByEpochFunc(delegatesByEpoch).
   105  			RegisterProtocol(rp).
   106  			Build()
   107  		assert.NoError(t, err)
   108  		assert.NotNil(t, r)
   109  		_, ok := r.ctx.Clock().(*clock.Mock)
   110  		assert.True(t, ok)
   111  	})
   112  
   113  	t.Run("root chain API", func(t *testing.T) {
   114  		sk := identityset.PrivateKey(0)
   115  		r, err := NewRollDPoSBuilder().
   116  			SetConfig(builderCfg).
   117  			SetAddr(identityset.Address(0).String()).
   118  			SetPriKey(sk).
   119  			SetChainManager(NewChainManager(mock_blockchain.NewMockBlockchain(ctrl))).
   120  			SetBroadcast(func(_ proto.Message) error {
   121  				return nil
   122  			}).
   123  			SetClock(clock.NewMock()).
   124  			SetDelegatesByEpochFunc(delegatesByEpoch).
   125  			SetProposersByEpochFunc(delegatesByEpoch).
   126  			RegisterProtocol(rp).
   127  			Build()
   128  		assert.NoError(t, err)
   129  		assert.NotNil(t, r)
   130  	})
   131  	t.Run("missing-dep", func(t *testing.T) {
   132  		sk := identityset.PrivateKey(0)
   133  		r, err := NewRollDPoSBuilder().
   134  			SetConfig(builderCfg).
   135  			SetAddr(identityset.Address(0).String()).
   136  			SetPriKey(sk).
   137  			SetBroadcast(func(_ proto.Message) error {
   138  				return nil
   139  			}).
   140  			SetDelegatesByEpochFunc(delegatesByEpoch).
   141  			SetProposersByEpochFunc(delegatesByEpoch).
   142  			RegisterProtocol(rp).
   143  			Build()
   144  		assert.Error(t, err)
   145  		assert.Nil(t, r)
   146  	})
   147  }
   148  
   149  func makeBlock(t *testing.T, accountIndex, numOfEndosements int, makeInvalidEndorse bool, height int) *block.Block {
   150  	unixTime := 1500000000
   151  	blkTime := int64(-1)
   152  	if height != 9 {
   153  		height = 9
   154  		blkTime = int64(-7723372030)
   155  	}
   156  	timeT := time.Unix(blkTime, 0)
   157  	rap := block.RunnableActionsBuilder{}
   158  	ra := rap.Build()
   159  	blk, err := block.NewBuilder(ra).
   160  		SetHeight(uint64(height)).
   161  		SetTimestamp(timeT).
   162  		SetVersion(1).
   163  		SetReceiptRoot(hash.Hash256b([]byte("hello, world!"))).
   164  		SetDeltaStateDigest(hash.Hash256b([]byte("world, hello!"))).
   165  		SetPrevBlockHash(hash.Hash256b([]byte("hello, block!"))).
   166  		SignAndBuild(identityset.PrivateKey(accountIndex))
   167  	require.NoError(t, err)
   168  	footerForBlk := &block.Footer{}
   169  	typesFooter := iotextypes.BlockFooter{}
   170  
   171  	for i := 0; i < numOfEndosements; i++ {
   172  		timeTime := time.Unix(int64(unixTime), 0)
   173  		hs := blk.HashBlock()
   174  		var consensusVote *ConsensusVote
   175  		if makeInvalidEndorse {
   176  			consensusVote = NewConsensusVote(hs[:], LOCK)
   177  		} else {
   178  			consensusVote = NewConsensusVote(hs[:], COMMIT)
   179  		}
   180  		en, err := endorsement.Endorse(identityset.PrivateKey(i), consensusVote, timeTime)
   181  		require.NoError(t, err)
   182  		enProto, err := en.Proto()
   183  		require.NoError(t, err)
   184  		typesFooter.Endorsements = append(typesFooter.Endorsements, enProto)
   185  	}
   186  	ts := timestamppb.New(time.Unix(int64(unixTime), 0))
   187  	typesFooter.Timestamp = ts
   188  	require.NotNil(t, typesFooter.Timestamp)
   189  	err = footerForBlk.ConvertFromBlockFooterPb(&typesFooter)
   190  	require.NoError(t, err)
   191  	blk.Footer = *footerForBlk
   192  	return &blk
   193  }
   194  
   195  func TestValidateBlockFooter(t *testing.T) {
   196  	ctrl := gomock.NewController(t)
   197  	candidates := make([]string, 5)
   198  	for i := 0; i < len(candidates); i++ {
   199  		candidates[i] = identityset.Address(i).String()
   200  	}
   201  	clock := clock.NewMock()
   202  	blockHeight := uint64(8)
   203  	footer := &block.Footer{}
   204  	bc := mock_blockchain.NewMockBlockchain(ctrl)
   205  	bc.EXPECT().BlockFooterByHeight(blockHeight).Return(footer, nil).Times(5)
   206  
   207  	sk1 := identityset.PrivateKey(1)
   208  	g := genesis.Default
   209  	g.NumDelegates = 4
   210  	g.NumSubEpochs = 1
   211  	g.BlockInterval = 10 * time.Second
   212  	g.Timestamp = int64(1500000000)
   213  	builderCfg := BuilderConfig{
   214  		Chain:              blockchain.DefaultConfig,
   215  		Consensus:          DefaultConfig,
   216  		DardanellesUpgrade: consensusfsm.DefaultDardanellesUpgradeConfig,
   217  		DB:                 db.DefaultConfig,
   218  		Genesis:            g,
   219  		SystemActive:       true,
   220  	}
   221  	bc.EXPECT().Genesis().Return(g).Times(5)
   222  	rp := rolldpos.NewProtocol(
   223  		g.NumCandidateDelegates,
   224  		g.NumDelegates,
   225  		g.NumSubEpochs,
   226  	)
   227  	delegatesByEpoch := func(uint64) ([]string, error) {
   228  		return []string{
   229  			candidates[0],
   230  			candidates[1],
   231  			candidates[2],
   232  			candidates[3],
   233  		}, nil
   234  	}
   235  	r, err := NewRollDPoSBuilder().
   236  		SetConfig(builderCfg).
   237  		SetAddr(identityset.Address(1).String()).
   238  		SetPriKey(sk1).
   239  		SetChainManager(NewChainManager(bc)).
   240  		SetBroadcast(func(_ proto.Message) error {
   241  			return nil
   242  		}).
   243  		SetDelegatesByEpochFunc(delegatesByEpoch).
   244  		SetProposersByEpochFunc(delegatesByEpoch).
   245  		SetClock(clock).
   246  		RegisterProtocol(rp).
   247  		Build()
   248  	require.NoError(t, err)
   249  	require.NotNil(t, r)
   250  
   251  	// all right
   252  	blk := makeBlock(t, 1, 4, false, 9)
   253  	err = r.ValidateBlockFooter(blk)
   254  	require.NoError(t, err)
   255  
   256  	// Proposer is wrong
   257  	blk = makeBlock(t, 4, 4, false, 9)
   258  	err = r.ValidateBlockFooter(blk)
   259  	require.Error(t, err)
   260  
   261  	// Not enough endorsements
   262  	blk = makeBlock(t, 1, 2, false, 9)
   263  	err = r.ValidateBlockFooter(blk)
   264  	require.Error(t, err)
   265  
   266  	// round information is wrong
   267  	blk = makeBlock(t, 1, 4, false, 0)
   268  	err = r.ValidateBlockFooter(blk)
   269  	require.Error(t, err)
   270  
   271  	// Some endorsement is invalid
   272  	blk = makeBlock(t, 1, 4, true, 9)
   273  	err = r.ValidateBlockFooter(blk)
   274  	require.Error(t, err)
   275  }
   276  
   277  func TestRollDPoS_Metrics(t *testing.T) {
   278  	t.Parallel()
   279  
   280  	ctrl := gomock.NewController(t)
   281  
   282  	candidates := make([]string, 5)
   283  	for i := 0; i < len(candidates); i++ {
   284  		candidates[i] = identityset.Address(i).String()
   285  	}
   286  
   287  	clock := clock.NewMock()
   288  	blockHeight := uint64(8)
   289  	footer := &block.Footer{}
   290  	bc := mock_blockchain.NewMockBlockchain(ctrl)
   291  	bc.EXPECT().TipHeight().Return(blockHeight).Times(1)
   292  	bc.EXPECT().BlockFooterByHeight(blockHeight).Return(footer, nil).Times(2)
   293  
   294  	sk1 := identityset.PrivateKey(1)
   295  	cfg := DefaultConfig
   296  	cfg.ConsensusDBPath = "consensus.db"
   297  	g := genesis.Default
   298  	g.NumDelegates = 4
   299  	g.NumSubEpochs = 1
   300  	g.BlockInterval = 10 * time.Second
   301  	g.Timestamp = int64(1500000000)
   302  	builderCfg := BuilderConfig{
   303  		Chain:              blockchain.DefaultConfig,
   304  		Consensus:          cfg,
   305  		DardanellesUpgrade: consensusfsm.DefaultDardanellesUpgradeConfig,
   306  		DB:                 db.DefaultConfig,
   307  		Genesis:            g,
   308  		SystemActive:       true,
   309  	}
   310  	bc.EXPECT().Genesis().Return(g).Times(2)
   311  	rp := rolldpos.NewProtocol(
   312  		g.NumCandidateDelegates,
   313  		g.NumDelegates,
   314  		g.NumSubEpochs,
   315  	)
   316  	delegatesByEpoch := func(uint64) ([]string, error) {
   317  		return []string{
   318  			candidates[0],
   319  			candidates[1],
   320  			candidates[2],
   321  			candidates[3],
   322  		}, nil
   323  	}
   324  	r, err := NewRollDPoSBuilder().
   325  		SetConfig(builderCfg).
   326  		SetAddr(identityset.Address(1).String()).
   327  		SetPriKey(sk1).
   328  		SetChainManager(NewChainManager(bc)).
   329  		SetBroadcast(func(_ proto.Message) error {
   330  			return nil
   331  		}).
   332  		SetClock(clock).
   333  		SetDelegatesByEpochFunc(delegatesByEpoch).
   334  		SetProposersByEpochFunc(delegatesByEpoch).
   335  		RegisterProtocol(rp).
   336  		Build()
   337  	require.NoError(t, err)
   338  	require.NotNil(t, r)
   339  	ctx := r.ctx.(*rollDPoSCtx)
   340  	clock.Add(ctx.BlockInterval(blockHeight))
   341  	require.NoError(t, ctx.Start(context.Background()))
   342  	ctx.round, err = ctx.roundCalc.UpdateRound(ctx.round, blockHeight+1, ctx.BlockInterval(blockHeight+1), clock.Now(), 2*time.Second)
   343  	require.NoError(t, err)
   344  
   345  	m, err := r.Metrics()
   346  	require.NoError(t, err)
   347  	assert.Equal(t, uint64(3), m.LatestEpoch)
   348  	assert.Equal(t, candidates[:4], m.LatestDelegates)
   349  	assert.Equal(t, candidates[1], m.LatestBlockProducer)
   350  }
   351  
   352  // E2E RollDPoS tests bellow
   353  
   354  type directOverlay struct {
   355  	addr  net.Addr
   356  	peers map[net.Addr]*RollDPoS
   357  }
   358  
   359  func (o *directOverlay) Start(_ context.Context) error { return nil }
   360  
   361  func (o *directOverlay) Stop(_ context.Context) error { return nil }
   362  
   363  func (o *directOverlay) Broadcast(msg proto.Message) error {
   364  	// Only broadcast consensus message
   365  	if cMsg, ok := msg.(*iotextypes.ConsensusMessage); ok {
   366  		for _, r := range o.peers {
   367  			if err := r.HandleConsensusMsg(cMsg); err != nil {
   368  				return errors.Wrap(err, "error when handling consensus message directly")
   369  			}
   370  		}
   371  	}
   372  	return nil
   373  }
   374  
   375  func (o *directOverlay) Tell(uint32, net.Addr, proto.Message) error { return nil }
   376  
   377  func (o *directOverlay) Self() net.Addr { return o.addr }
   378  
   379  func (o *directOverlay) GetPeers() []net.Addr {
   380  	addrs := make([]net.Addr, 0, len(o.peers))
   381  	for addr := range o.peers {
   382  		addrs = append(addrs, addr)
   383  	}
   384  	return addrs
   385  }
   386  
   387  func TestRollDPoSConsensus(t *testing.T) {
   388  	newConsensusComponents := func(numNodes int) ([]*RollDPoS, []*directOverlay, []blockchain.Blockchain) {
   389  		cfg := DefaultConfig
   390  		cfg.ConsensusDBPath = ""
   391  		cfg.Delay = 300 * time.Millisecond
   392  		cfg.FSM.AcceptBlockTTL = 800 * time.Millisecond
   393  		cfg.FSM.AcceptProposalEndorsementTTL = 400 * time.Millisecond
   394  		cfg.FSM.AcceptLockEndorsementTTL = 400 * time.Millisecond
   395  		cfg.FSM.CommitTTL = 400 * time.Millisecond
   396  		cfg.FSM.UnmatchedEventTTL = time.Second
   397  		cfg.FSM.UnmatchedEventInterval = 10 * time.Millisecond
   398  		cfg.ToleratedOvertime = 200 * time.Millisecond
   399  		g := genesis.Default
   400  		g.BlockInterval = 2 * time.Second
   401  		g.Blockchain.NumDelegates = uint64(numNodes)
   402  		g.Blockchain.NumSubEpochs = 1
   403  		g.EnableGravityChainVoting = false
   404  		builderCfg := BuilderConfig{
   405  			Chain:              blockchain.DefaultConfig,
   406  			Consensus:          cfg,
   407  			DardanellesUpgrade: consensusfsm.DefaultDardanellesUpgradeConfig,
   408  			DB:                 db.DefaultConfig,
   409  			Genesis:            g,
   410  			SystemActive:       true,
   411  		}
   412  		chainAddrs := make([]*addrKeyPair, 0, numNodes)
   413  		networkAddrs := make([]net.Addr, 0, numNodes)
   414  		for i := 0; i < numNodes; i++ {
   415  			sk := identityset.PrivateKey(i)
   416  			addr := addrKeyPair{
   417  				encodedAddr: identityset.Address(i).String(),
   418  				priKey:      sk,
   419  			}
   420  			chainAddrs = append(chainAddrs, &addr)
   421  			networkAddrs = append(networkAddrs, node.NewTCPNode(fmt.Sprintf("127.0.0.%d:4689", i+1)))
   422  		}
   423  
   424  		chainRawAddrs := make([]string, 0, numNodes)
   425  		addressMap := make(map[string]*addrKeyPair)
   426  		for _, addr := range chainAddrs {
   427  			chainRawAddrs = append(chainRawAddrs, addr.encodedAddr)
   428  			addressMap[addr.encodedAddr] = addr
   429  		}
   430  		cp.SortCandidates(chainRawAddrs, 1, cp.CryptoSeed)
   431  		for i, rawAddress := range chainRawAddrs {
   432  			chainAddrs[i] = addressMap[rawAddress]
   433  		}
   434  
   435  		delegatesByEpochFunc := func(_ uint64) ([]string, error) {
   436  			candidates := make([]string, 0, numNodes)
   437  			for _, addr := range chainAddrs {
   438  				candidates = append(candidates, addr.encodedAddr)
   439  			}
   440  			return candidates, nil
   441  		}
   442  
   443  		chains := make([]blockchain.Blockchain, 0, numNodes)
   444  		p2ps := make([]*directOverlay, 0, numNodes)
   445  		cs := make([]*RollDPoS, 0, numNodes)
   446  		bc := blockchain.DefaultConfig
   447  		factoryCfg := factory.GenerateConfig(bc, g)
   448  		for i := 0; i < numNodes; i++ {
   449  			ctx := context.Background()
   450  			bc.ProducerPrivKey = hex.EncodeToString(chainAddrs[i].priKey.Bytes())
   451  			registry := protocol.NewRegistry()
   452  			sf, err := factory.NewFactory(factoryCfg, db.NewMemKVStore(), factory.RegistryOption(registry))
   453  			require.NoError(t, err)
   454  			require.NoError(t, sf.Start(genesis.WithGenesisContext(
   455  				protocol.WithRegistry(ctx, registry),
   456  				g,
   457  			)))
   458  			actPool, err := actpool.NewActPool(g, sf, actpool.DefaultConfig)
   459  			require.NoError(t, err)
   460  			require.NoError(t, err)
   461  			acc := account.NewProtocol(rewarding.DepositGas)
   462  			require.NoError(t, acc.Register(registry))
   463  			rp := rolldpos.NewProtocol(g.NumCandidateDelegates, g.NumDelegates, g.NumSubEpochs)
   464  			require.NoError(t, rp.Register(registry))
   465  			store, err := filedao.NewFileDAOInMemForTest()
   466  			require.NoError(t, err)
   467  			dao := blockdao.NewBlockDAOWithIndexersAndCache(store, []blockdao.BlockIndexer{sf}, db.DefaultConfig.MaxCacheSize)
   468  			chain := blockchain.NewBlockchain(
   469  				bc,
   470  				g,
   471  				dao,
   472  				factory.NewMinter(sf, actPool),
   473  				blockchain.BlockValidatorOption(block.NewValidator(
   474  					sf,
   475  					protocol.NewGenericValidator(sf, accountutil.AccountState),
   476  				)),
   477  			)
   478  			chains = append(chains, chain)
   479  
   480  			p2p := &directOverlay{
   481  				addr:  networkAddrs[i],
   482  				peers: make(map[net.Addr]*RollDPoS),
   483  			}
   484  			p2ps = append(p2ps, p2p)
   485  
   486  			consensus, err := NewRollDPoSBuilder().
   487  				SetAddr(chainAddrs[i].encodedAddr).
   488  				SetPriKey(chainAddrs[i].priKey).
   489  				SetConfig(builderCfg).
   490  				SetChainManager(NewChainManager(chain)).
   491  				SetBroadcast(p2p.Broadcast).
   492  				SetDelegatesByEpochFunc(delegatesByEpochFunc).
   493  				SetProposersByEpochFunc(delegatesByEpochFunc).
   494  				RegisterProtocol(rp).
   495  				Build()
   496  			require.NoError(t, err)
   497  
   498  			cs = append(cs, consensus)
   499  		}
   500  		for i := 0; i < numNodes; i++ {
   501  			for j := 0; j < numNodes; j++ {
   502  				if i != j {
   503  					p2ps[i].peers[p2ps[j].addr] = cs[j]
   504  				}
   505  			}
   506  		}
   507  		return cs, p2ps, chains
   508  	}
   509  
   510  	t.Run("1-block", func(t *testing.T) {
   511  		// TODO: fix and enable the test
   512  		t.Skip()
   513  
   514  		ctx := context.Background()
   515  		cs, p2ps, chains := newConsensusComponents(24)
   516  
   517  		for i := 0; i < 24; i++ {
   518  			require.NoError(t, chains[i].Start(ctx))
   519  			require.NoError(t, p2ps[i].Start(ctx))
   520  		}
   521  		wg := sync.WaitGroup{}
   522  		wg.Add(24)
   523  		for i := 0; i < 24; i++ {
   524  			go func(idx int) {
   525  				defer wg.Done()
   526  				err := cs[idx].Start(ctx)
   527  				require.NoError(t, err)
   528  			}(i)
   529  		}
   530  		wg.Wait()
   531  
   532  		defer func() {
   533  			for i := 0; i < 24; i++ {
   534  				require.NoError(t, cs[i].Stop(ctx))
   535  				require.NoError(t, p2ps[i].Stop(ctx))
   536  				require.NoError(t, chains[i].Stop(ctx))
   537  			}
   538  		}()
   539  		assert.NoError(t, testutil.WaitUntil(200*time.Millisecond, 10*time.Second, func() (bool, error) {
   540  			for _, chain := range chains {
   541  				if chain.TipHeight() < 1 {
   542  					return false, nil
   543  				}
   544  			}
   545  			return true, nil
   546  		}))
   547  	})
   548  
   549  	t.Run("1-epoch", func(t *testing.T) {
   550  		if testing.Short() {
   551  			t.Skip("Skip the 1-epoch test in short mode.")
   552  		}
   553  		ctx := context.Background()
   554  		cs, p2ps, chains := newConsensusComponents(24)
   555  
   556  		for i := 0; i < 24; i++ {
   557  			require.NoError(t, chains[i].Start(ctx))
   558  			require.NoError(t, p2ps[i].Start(ctx))
   559  		}
   560  		wg := sync.WaitGroup{}
   561  		wg.Add(24)
   562  		for i := 0; i < 24; i++ {
   563  			go func(idx int) {
   564  				defer wg.Done()
   565  				err := cs[idx].Start(ctx)
   566  				require.NoError(t, err)
   567  			}(i)
   568  		}
   569  		wg.Wait()
   570  
   571  		defer func() {
   572  			for i := 0; i < 24; i++ {
   573  				require.NoError(t, cs[i].Stop(ctx))
   574  				require.NoError(t, p2ps[i].Stop(ctx))
   575  				require.NoError(t, chains[i].Stop(ctx))
   576  			}
   577  		}()
   578  		assert.NoError(t, testutil.WaitUntil(200*time.Millisecond, 100*time.Second, func() (bool, error) {
   579  			for _, chain := range chains {
   580  				if chain.TipHeight() < 48 {
   581  					return false, nil
   582  				}
   583  			}
   584  			return true, nil
   585  		}))
   586  	})
   587  
   588  	t.Run("network-partition-time-rotation", func(t *testing.T) {
   589  		// TODO: fix and enable the test
   590  		t.Skip()
   591  
   592  		ctx := context.Background()
   593  		cs, p2ps, chains := newConsensusComponents(24)
   594  		// 1 should be the block 1's proposer
   595  		for i, p2p := range p2ps {
   596  			if i == 1 {
   597  				p2p.peers = make(map[net.Addr]*RollDPoS)
   598  			} else {
   599  				delete(p2p.peers, p2ps[1].addr)
   600  			}
   601  		}
   602  
   603  		for i := 0; i < 24; i++ {
   604  			require.NoError(t, chains[i].Start(ctx))
   605  			require.NoError(t, p2ps[i].Start(ctx))
   606  		}
   607  		wg := sync.WaitGroup{}
   608  		wg.Add(24)
   609  		for i := 0; i < 24; i++ {
   610  			go func(idx int) {
   611  				defer wg.Done()
   612  				cs[idx].ctx.(*rollDPoSCtx).roundCalc.timeBasedRotation = true
   613  				err := cs[idx].Start(ctx)
   614  				require.NoError(t, err)
   615  			}(i)
   616  		}
   617  		wg.Wait()
   618  
   619  		defer func() {
   620  			for i := 0; i < 24; i++ {
   621  				require.NoError(t, cs[i].Stop(ctx))
   622  				require.NoError(t, p2ps[i].Stop(ctx))
   623  				require.NoError(t, chains[i].Stop(ctx))
   624  			}
   625  		}()
   626  
   627  		assert.NoError(t, testutil.WaitUntil(200*time.Millisecond, 60*time.Second, func() (bool, error) {
   628  			for i, chain := range chains {
   629  				if i == 1 {
   630  					continue
   631  				}
   632  				if chain.TipHeight() < 4 {
   633  					return false, nil
   634  				}
   635  			}
   636  			return true, nil
   637  		}))
   638  	})
   639  
   640  	t.Run("proposer-network-partition-blocking", func(t *testing.T) {
   641  		ctx := context.Background()
   642  		cs, p2ps, chains := newConsensusComponents(24)
   643  		// 1 should be the block 1's proposer
   644  		for i, p2p := range p2ps {
   645  			if i == 1 {
   646  				p2p.peers = make(map[net.Addr]*RollDPoS)
   647  			} else {
   648  				delete(p2p.peers, p2ps[1].addr)
   649  			}
   650  		}
   651  
   652  		for i := 0; i < 24; i++ {
   653  			require.NoError(t, chains[i].Start(ctx))
   654  			require.NoError(t, p2ps[i].Start(ctx))
   655  		}
   656  		wg := sync.WaitGroup{}
   657  		wg.Add(24)
   658  		for i := 0; i < 24; i++ {
   659  			go func(idx int) {
   660  				defer wg.Done()
   661  				err := cs[idx].Start(ctx)
   662  				require.NoError(t, err)
   663  			}(i)
   664  		}
   665  		wg.Wait()
   666  
   667  		defer func() {
   668  			for i := 0; i < 24; i++ {
   669  				require.NoError(t, cs[i].Stop(ctx))
   670  				require.NoError(t, p2ps[i].Stop(ctx))
   671  				require.NoError(t, chains[i].Stop(ctx))
   672  			}
   673  		}()
   674  		time.Sleep(5 * time.Second)
   675  		for _, chain := range chains {
   676  			header, err := chain.BlockHeaderByHeight(1)
   677  			assert.Nil(t, header)
   678  			assert.Error(t, err)
   679  		}
   680  	})
   681  
   682  	t.Run("non-proposer-network-partition-blocking", func(t *testing.T) {
   683  		ctx := context.Background()
   684  		cs, p2ps, chains := newConsensusComponents(24)
   685  		// 1 should be the block 1's proposer
   686  		for i, p2p := range p2ps {
   687  			if i == 0 {
   688  				p2p.peers = make(map[net.Addr]*RollDPoS)
   689  			} else {
   690  				delete(p2p.peers, p2ps[0].addr)
   691  			}
   692  		}
   693  
   694  		for i := 0; i < 24; i++ {
   695  			require.NoError(t, chains[i].Start(ctx))
   696  			require.NoError(t, p2ps[i].Start(ctx))
   697  		}
   698  		wg := sync.WaitGroup{}
   699  		wg.Add(24)
   700  		for i := 0; i < 24; i++ {
   701  			go func(idx int) {
   702  				defer wg.Done()
   703  				err := cs[idx].Start(ctx)
   704  				require.NoError(t, err)
   705  			}(i)
   706  		}
   707  		wg.Wait()
   708  
   709  		defer func() {
   710  			for i := 0; i < 24; i++ {
   711  				require.NoError(t, cs[i].Stop(ctx))
   712  				require.NoError(t, p2ps[i].Stop(ctx))
   713  				require.NoError(t, chains[i].Stop(ctx))
   714  			}
   715  		}()
   716  		assert.NoError(t, testutil.WaitUntil(200*time.Millisecond, 60*time.Second, func() (bool, error) {
   717  			for i, chain := range chains {
   718  				if i == 0 {
   719  					continue
   720  				}
   721  				if chain.TipHeight() < 2 {
   722  					return false, nil
   723  				}
   724  			}
   725  			return true, nil
   726  		}))
   727  		for i, chain := range chains {
   728  			header, err := chain.BlockHeaderByHeight(1)
   729  			if i == 0 {
   730  				assert.Nil(t, header)
   731  				assert.Error(t, err)
   732  			} else {
   733  				assert.NotNil(t, header)
   734  				assert.NoError(t, err)
   735  			}
   736  		}
   737  	})
   738  }