github.com/klaytn/klaytn@v1.12.1/consensus/istanbul/core/handler_test.go (about)

     1  package core
     2  
     3  import (
     4  	"crypto/ecdsa"
     5  	"fmt"
     6  	"io"
     7  	"math/big"
     8  	"math/rand"
     9  	"os"
    10  	"testing"
    11  	"time"
    12  
    13  	"github.com/golang/mock/gomock"
    14  	"github.com/klaytn/klaytn/blockchain"
    15  	"github.com/klaytn/klaytn/blockchain/types"
    16  	"github.com/klaytn/klaytn/common"
    17  	"github.com/klaytn/klaytn/consensus/istanbul"
    18  	mock_istanbul "github.com/klaytn/klaytn/consensus/istanbul/mocks"
    19  	"github.com/klaytn/klaytn/consensus/istanbul/validator"
    20  	"github.com/klaytn/klaytn/crypto"
    21  	"github.com/klaytn/klaytn/crypto/sha3"
    22  	"github.com/klaytn/klaytn/event"
    23  	"github.com/klaytn/klaytn/fork"
    24  	"github.com/klaytn/klaytn/log"
    25  	"github.com/klaytn/klaytn/log/term"
    26  	"github.com/klaytn/klaytn/params"
    27  	"github.com/klaytn/klaytn/rlp"
    28  	"github.com/mattn/go-colorable"
    29  	"github.com/stretchr/testify/assert"
    30  	"github.com/stretchr/testify/require"
    31  )
    32  
    33  // newMockBackend create a mock-backend initialized with default values
    34  func newMockBackend(t *testing.T, validatorAddrs []common.Address) (*mock_istanbul.MockBackend, *gomock.Controller) {
    35  	committeeSize := uint64(len(validatorAddrs) / 3)
    36  
    37  	istExtra := &types.IstanbulExtra{
    38  		Validators:    validatorAddrs,
    39  		Seal:          []byte{},
    40  		CommittedSeal: [][]byte{},
    41  	}
    42  	extra, err := rlp.EncodeToBytes(istExtra)
    43  	if err != nil {
    44  		t.Fatal(err)
    45  	}
    46  
    47  	initBlock := types.NewBlockWithHeader(&types.Header{
    48  		ParentHash: common.Hash{},
    49  		Number:     common.Big0,
    50  		GasUsed:    0,
    51  		Extra:      append(make([]byte, types.IstanbulExtraVanity), extra...),
    52  		Time:       new(big.Int).SetUint64(1234),
    53  		BlockScore: common.Big0,
    54  	})
    55  
    56  	eventMux := new(event.TypeMux)
    57  	validatorSet := validator.NewWeightedCouncil(validatorAddrs, nil, validatorAddrs, nil, nil,
    58  		istanbul.WeightedRandom, committeeSize, 0, 0, &blockchain.BlockChain{})
    59  
    60  	mockCtrl := gomock.NewController(t)
    61  	mockBackend := mock_istanbul.NewMockBackend(mockCtrl)
    62  
    63  	// Consider the last proposal is "initBlock" and the owner of mockBackend is validatorAddrs[0]
    64  	mockBackend.EXPECT().Address().Return(validatorAddrs[0]).AnyTimes()
    65  	mockBackend.EXPECT().LastProposal().Return(initBlock, validatorAddrs[0]).AnyTimes()
    66  	mockBackend.EXPECT().Validators(initBlock).Return(validatorSet).AnyTimes()
    67  	mockBackend.EXPECT().NodeType().Return(common.CONSENSUSNODE).AnyTimes()
    68  
    69  	// Set an eventMux in which istanbul core will subscribe istanbul events
    70  	mockBackend.EXPECT().EventMux().Return(eventMux).AnyTimes()
    71  
    72  	// Just for bypassing an unused function
    73  	mockBackend.EXPECT().SetCurrentView(gomock.Any()).Return().AnyTimes()
    74  
    75  	// Always return nil for broadcasting related functions
    76  	mockBackend.EXPECT().Sign(gomock.Any()).Return(nil, nil).AnyTimes()
    77  	mockBackend.EXPECT().Broadcast(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
    78  	mockBackend.EXPECT().GossipSubPeer(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
    79  
    80  	// Verify checks whether the proposal of the preprepare message is a valid block. Consider it valid.
    81  	mockBackend.EXPECT().Verify(gomock.Any()).Return(time.Duration(0), nil).AnyTimes()
    82  
    83  	return mockBackend, mockCtrl
    84  }
    85  
    86  // genValidators returns a set of addresses and corresponding keys used for generating a validator set
    87  func genValidators(n int) ([]common.Address, map[common.Address]*ecdsa.PrivateKey) {
    88  	addrs := make([]common.Address, n)
    89  	keyMap := make(map[common.Address]*ecdsa.PrivateKey, n)
    90  
    91  	for i := 0; i < n; i++ {
    92  		key, _ := crypto.GenerateKey()
    93  		addrs[i] = crypto.PubkeyToAddress(key.PublicKey)
    94  		keyMap[addrs[i]] = key
    95  	}
    96  	return addrs, keyMap
    97  }
    98  
    99  // getRandomValidator selects a validator in the given validator set.
   100  // `isCommittee` determines whether it returns a committee or a non-committee.
   101  func getRandomValidator(isCommittee bool, valSet istanbul.ValidatorSet, prevHash common.Hash, view *istanbul.View) istanbul.Validator {
   102  	committee := valSet.SubList(prevHash, view)
   103  
   104  	if isCommittee {
   105  		return committee[rand.Int()%(len(committee)-1)]
   106  	}
   107  
   108  	for _, val := range valSet.List() {
   109  		for _, com := range committee {
   110  			if val.Address() == com.Address() {
   111  				isCommittee = true
   112  			}
   113  		}
   114  		if !isCommittee {
   115  			return val
   116  		}
   117  		isCommittee = false
   118  	}
   119  
   120  	// it should not be happened
   121  	return nil
   122  }
   123  
   124  // signBlock signs the given block with the given private key
   125  func signBlock(block *types.Block, privateKey *ecdsa.PrivateKey) (*types.Block, error) {
   126  	var hash common.Hash
   127  	header := block.Header()
   128  	hasher := sha3.NewKeccak256()
   129  
   130  	// Clean seal is required for calculating proposer seal
   131  	rlp.Encode(hasher, types.IstanbulFilteredHeader(header, false))
   132  	hasher.Sum(hash[:0])
   133  
   134  	seal, err := crypto.Sign(crypto.Keccak256([]byte(hash.Bytes())), privateKey)
   135  	if err != nil {
   136  		return nil, err
   137  	}
   138  
   139  	istanbulExtra, err := types.ExtractIstanbulExtra(header)
   140  	if err != nil {
   141  		return nil, err
   142  	}
   143  	istanbulExtra.Seal = seal
   144  
   145  	payload, err := rlp.EncodeToBytes(&istanbulExtra)
   146  	if err != nil {
   147  		return nil, err
   148  	}
   149  
   150  	header.Extra = append(header.Extra[:types.IstanbulExtraVanity], payload...)
   151  	return block.WithSeal(header), nil
   152  }
   153  
   154  // genBlock generates a signed block indicating prevBlock with ParentHash
   155  func genBlock(prevBlock *types.Block, signerKey *ecdsa.PrivateKey) (*types.Block, error) {
   156  	block := types.NewBlockWithHeader(&types.Header{
   157  		ParentHash: prevBlock.Hash(),
   158  		Number:     new(big.Int).Add(prevBlock.Number(), common.Big1),
   159  		GasUsed:    0,
   160  		Extra:      prevBlock.Extra(),
   161  		Time:       new(big.Int).Add(prevBlock.Time(), common.Big1),
   162  		BlockScore: new(big.Int).Add(prevBlock.BlockScore(), common.Big1),
   163  	})
   164  	return signBlock(block, signerKey)
   165  }
   166  
   167  // genBlockParams generates a signed block indicating prevBlock with ParentHash with additional parameters.
   168  func genBlockParams(prevBlock *types.Block, signerKey *ecdsa.PrivateKey, gasUsed uint64, time int64, blockScore int64) (*types.Block, error) {
   169  	block := types.NewBlockWithHeader(&types.Header{
   170  		ParentHash: prevBlock.Hash(),
   171  		Number:     new(big.Int).Add(prevBlock.Number(), common.Big1),
   172  		GasUsed:    gasUsed,
   173  		Extra:      prevBlock.Extra(),
   174  		Time:       new(big.Int).Add(prevBlock.Time(), big.NewInt(time)),
   175  		BlockScore: new(big.Int).Add(prevBlock.BlockScore(), big.NewInt(blockScore)),
   176  	})
   177  	return signBlock(block, signerKey)
   178  }
   179  
   180  // genIstanbulMsg generates an istanbul message with given values
   181  func genIstanbulMsg(msgType uint64, prevHash common.Hash, proposal *types.Block, signerAddr common.Address, signerKey *ecdsa.PrivateKey) (istanbul.MessageEvent, error) {
   182  	var subject interface{}
   183  
   184  	if msgType == msgPreprepare {
   185  		subject = &istanbul.Preprepare{
   186  			View: &istanbul.View{
   187  				Round:    big.NewInt(0),
   188  				Sequence: proposal.Number(),
   189  			},
   190  			Proposal: proposal,
   191  		}
   192  	} else {
   193  		subject = &istanbul.Subject{
   194  			View: &istanbul.View{
   195  				Round:    big.NewInt(0),
   196  				Sequence: proposal.Number(),
   197  			},
   198  			Digest:   proposal.Hash(),
   199  			PrevHash: prevHash,
   200  		}
   201  	}
   202  
   203  	encodedSubject, err := Encode(subject)
   204  	if err != nil {
   205  		return istanbul.MessageEvent{}, err
   206  	}
   207  
   208  	msg := &message{
   209  		Hash:    prevHash,
   210  		Code:    msgType,
   211  		Msg:     encodedSubject,
   212  		Address: signerAddr,
   213  	}
   214  
   215  	data, err := msg.PayloadNoSig()
   216  	if err != nil {
   217  		return istanbul.MessageEvent{}, err
   218  	}
   219  
   220  	msg.Signature, err = crypto.Sign(crypto.Keccak256([]byte(data)), signerKey)
   221  	if err != nil {
   222  		return istanbul.MessageEvent{}, err
   223  	}
   224  
   225  	encodedPayload, err := msg.Payload()
   226  	if err != nil {
   227  		return istanbul.MessageEvent{}, err
   228  	}
   229  
   230  	istMsg := istanbul.MessageEvent{
   231  		Hash:    msg.Hash,
   232  		Payload: encodedPayload,
   233  	}
   234  
   235  	return istMsg, nil
   236  }
   237  
   238  // TestCore_handleEvents_scenario_invalidSender tests `handleEvents` function of `istanbul.core` with a scenario.
   239  // It posts an invalid message and a valid message of each istanbul message type.
   240  func TestCore_handleEvents_scenario_invalidSender(t *testing.T) {
   241  	fork.SetHardForkBlockNumberConfig(&params.ChainConfig{})
   242  	defer fork.ClearHardForkBlockNumberConfig()
   243  
   244  	validatorAddrs, validatorKeyMap := genValidators(30)
   245  	mockBackend, mockCtrl := newMockBackend(t, validatorAddrs)
   246  	defer mockCtrl.Finish()
   247  
   248  	istConfig := istanbul.DefaultConfig
   249  	istConfig.ProposerPolicy = istanbul.WeightedRandom
   250  
   251  	// When the istanbul core started, a message handling loop in `handleEvents()` waits istanbul messages
   252  	istCore := New(mockBackend, istConfig).(*core)
   253  	if err := istCore.Start(); err != nil {
   254  		t.Fatal(err)
   255  	}
   256  	defer istCore.Stop()
   257  
   258  	// Get variables initialized on `newMockBackend()`
   259  	eventMux := mockBackend.EventMux()
   260  	lastProposal, _ := mockBackend.LastProposal()
   261  	lastBlock := lastProposal.(*types.Block)
   262  	validators := mockBackend.Validators(lastBlock)
   263  
   264  	// Preprepare message originated from invalid sender
   265  	{
   266  		msgSender := getRandomValidator(false, validators, lastBlock.Hash(), istCore.currentView())
   267  		msgSenderKey := validatorKeyMap[msgSender.Address()]
   268  
   269  		newProposal, err := genBlock(lastBlock, msgSenderKey)
   270  		if err != nil {
   271  			t.Fatal(err)
   272  		}
   273  
   274  		istanbulMsg, err := genIstanbulMsg(msgPreprepare, lastProposal.Hash(), newProposal, msgSender.Address(), msgSenderKey)
   275  		if err != nil {
   276  			t.Fatal(err)
   277  		}
   278  
   279  		if err := eventMux.Post(istanbulMsg); err != nil {
   280  			t.Fatal(err)
   281  		}
   282  
   283  		time.Sleep(time.Second)
   284  		assert.Nil(t, istCore.current.Preprepare)
   285  	}
   286  
   287  	// Preprepare message originated from valid sender and set a new proposal in the istanbul core
   288  	{
   289  		msgSender := validators.GetProposer()
   290  		msgSenderKey := validatorKeyMap[msgSender.Address()]
   291  
   292  		newProposal, err := genBlock(lastBlock, msgSenderKey)
   293  		if err != nil {
   294  			t.Fatal(err)
   295  		}
   296  
   297  		istanbulMsg, err := genIstanbulMsg(msgPreprepare, lastBlock.Hash(), newProposal, msgSender.Address(), msgSenderKey)
   298  		if err != nil {
   299  			t.Fatal(err)
   300  		}
   301  
   302  		if err := eventMux.Post(istanbulMsg); err != nil {
   303  			t.Fatal(err)
   304  		}
   305  
   306  		time.Sleep(time.Second)
   307  		assert.Equal(t, istCore.current.Preprepare.Proposal.Header().String(), newProposal.Header().String())
   308  	}
   309  
   310  	// Prepare message originated from invalid sender
   311  	{
   312  		msgSender := getRandomValidator(false, validators, lastBlock.Hash(), istCore.currentView())
   313  		msgSenderKey := validatorKeyMap[msgSender.Address()]
   314  
   315  		istanbulMsg, err := genIstanbulMsg(msgPrepare, lastBlock.Hash(), istCore.current.Preprepare.Proposal.(*types.Block), msgSender.Address(), msgSenderKey)
   316  		if err != nil {
   317  			t.Fatal(err)
   318  		}
   319  
   320  		if err := eventMux.Post(istanbulMsg); err != nil {
   321  			t.Fatal(err)
   322  		}
   323  
   324  		time.Sleep(time.Second)
   325  		assert.Equal(t, 0, len(istCore.current.Prepares.messages))
   326  	}
   327  
   328  	// Prepare message originated from valid sender
   329  	{
   330  		msgSender := getRandomValidator(true, validators, lastBlock.Hash(), istCore.currentView())
   331  		msgSenderKey := validatorKeyMap[msgSender.Address()]
   332  
   333  		istanbulMsg, err := genIstanbulMsg(msgPrepare, lastBlock.Hash(), istCore.current.Preprepare.Proposal.(*types.Block), msgSender.Address(), msgSenderKey)
   334  		if err != nil {
   335  			t.Fatal(err)
   336  		}
   337  
   338  		if err := eventMux.Post(istanbulMsg); err != nil {
   339  			t.Fatal(err)
   340  		}
   341  
   342  		time.Sleep(time.Second)
   343  		assert.Equal(t, 1, len(istCore.current.Prepares.messages))
   344  	}
   345  
   346  	// Commit message originated from invalid sender
   347  	{
   348  		msgSender := getRandomValidator(false, validators, lastBlock.Hash(), istCore.currentView())
   349  		msgSenderKey := validatorKeyMap[msgSender.Address()]
   350  
   351  		istanbulMsg, err := genIstanbulMsg(msgCommit, lastBlock.Hash(), istCore.current.Preprepare.Proposal.(*types.Block), msgSender.Address(), msgSenderKey)
   352  		if err != nil {
   353  			t.Fatal(err)
   354  		}
   355  
   356  		if err := eventMux.Post(istanbulMsg); err != nil {
   357  			t.Fatal(err)
   358  		}
   359  
   360  		time.Sleep(time.Second)
   361  		assert.Equal(t, 0, len(istCore.current.Commits.messages))
   362  	}
   363  
   364  	// Commit message originated from valid sender
   365  	{
   366  		msgSender := getRandomValidator(true, validators, lastBlock.Hash(), istCore.currentView())
   367  		msgSenderKey := validatorKeyMap[msgSender.Address()]
   368  
   369  		istanbulMsg, err := genIstanbulMsg(msgCommit, lastBlock.Hash(), istCore.current.Preprepare.Proposal.(*types.Block), msgSender.Address(), msgSenderKey)
   370  		if err != nil {
   371  			t.Fatal(err)
   372  		}
   373  
   374  		if err := eventMux.Post(istanbulMsg); err != nil {
   375  			t.Fatal(err)
   376  		}
   377  
   378  		time.Sleep(time.Second)
   379  		assert.Equal(t, 1, len(istCore.current.Commits.messages))
   380  	}
   381  
   382  	//// RoundChange message originated from invalid sender
   383  	//{
   384  	//	msgSender := getRandomValidator(false, validators, lastBlock.Hash(), istCore.currentView())
   385  	//	msgSenderKey := validatorKeyMap[msgSender.Address()]
   386  	//
   387  	//	istanbulMsg, err := genIstanbulMsg(msgRoundChange, lastBlock.Hash(), istCore.current.Preprepare.Proposal.(*types.Block), msgSender.Address(), msgSenderKey)
   388  	//	if err != nil {
   389  	//		t.Fatal(err)
   390  	//	}
   391  	//
   392  	//	if err := eventMux.Post(istanbulMsg); err != nil {
   393  	//		t.Fatal(err)
   394  	//	}
   395  	//
   396  	//	time.Sleep(time.Second)
   397  	//	assert.Nil(t, istCore.roundChangeSet.roundChanges[0]) // round is set to 0 in this test
   398  	//}
   399  
   400  	// RoundChange message originated from valid sender
   401  	{
   402  		msgSender := getRandomValidator(true, validators, lastBlock.Hash(), istCore.currentView())
   403  		msgSenderKey := validatorKeyMap[msgSender.Address()]
   404  
   405  		istanbulMsg, err := genIstanbulMsg(msgRoundChange, lastBlock.Hash(), istCore.current.Preprepare.Proposal.(*types.Block), msgSender.Address(), msgSenderKey)
   406  		if err != nil {
   407  			t.Fatal(err)
   408  		}
   409  
   410  		if err := eventMux.Post(istanbulMsg); err != nil {
   411  			t.Fatal(err)
   412  		}
   413  
   414  		time.Sleep(time.Second)
   415  		assert.Equal(t, 1, len(istCore.roundChangeSet.roundChanges[0].messages)) // round is set to 0 in this test
   416  	}
   417  }
   418  
   419  func TestCore_handlerMsg(t *testing.T) {
   420  	fork.SetHardForkBlockNumberConfig(&params.ChainConfig{})
   421  	defer fork.ClearHardForkBlockNumberConfig()
   422  
   423  	validatorAddrs, validatorKeyMap := genValidators(10)
   424  	mockBackend, mockCtrl := newMockBackend(t, validatorAddrs)
   425  	defer mockCtrl.Finish()
   426  
   427  	istConfig := istanbul.DefaultConfig
   428  	istConfig.ProposerPolicy = istanbul.WeightedRandom
   429  
   430  	istCore := New(mockBackend, istConfig).(*core)
   431  	if err := istCore.Start(); err != nil {
   432  		t.Fatal(err)
   433  	}
   434  	defer istCore.Stop()
   435  
   436  	lastProposal, _ := mockBackend.LastProposal()
   437  	lastBlock := lastProposal.(*types.Block)
   438  	validators := mockBackend.Validators(lastBlock)
   439  
   440  	// invalid format
   441  	{
   442  		invalidMsg := []byte{0x1, 0x2, 0x3, 0x4}
   443  		err := istCore.handleMsg(invalidMsg)
   444  		assert.NotNil(t, err)
   445  	}
   446  
   447  	// invali sender (non-validator)
   448  	{
   449  		newAddr, keyMap := genValidators(1)
   450  		nonValidatorAddr := newAddr[0]
   451  		nonValidatorKey := keyMap[nonValidatorAddr]
   452  
   453  		newProposal, err := genBlock(lastBlock, nonValidatorKey)
   454  		if err != nil {
   455  			t.Fatal(err)
   456  		}
   457  
   458  		istanbulMsg, err := genIstanbulMsg(msgPreprepare, lastBlock.Hash(), newProposal, nonValidatorAddr, nonValidatorKey)
   459  		if err != nil {
   460  			t.Fatal(err)
   461  		}
   462  
   463  		err = istCore.handleMsg(istanbulMsg.Payload)
   464  		assert.NotNil(t, err)
   465  	}
   466  
   467  	// valid message
   468  	{
   469  		msgSender := validators.GetProposer()
   470  		msgSenderKey := validatorKeyMap[msgSender.Address()]
   471  
   472  		newProposal, err := genBlock(lastBlock, msgSenderKey)
   473  		if err != nil {
   474  			t.Fatal(err)
   475  		}
   476  
   477  		istanbulMsg, err := genIstanbulMsg(msgPreprepare, lastBlock.Hash(), newProposal, msgSender.Address(), msgSenderKey)
   478  		if err != nil {
   479  			t.Fatal(err)
   480  		}
   481  
   482  		err = istCore.handleMsg(istanbulMsg.Payload)
   483  		assert.Nil(t, err)
   484  	}
   485  }
   486  
   487  // TODO-Klaytn: To enable logging in the test code, we can use the following function.
   488  // This function will be moved to somewhere utility functions are located.
   489  func enableLog() {
   490  	usecolor := term.IsTty(os.Stderr.Fd()) && os.Getenv("TERM") != "dumb"
   491  	output := io.Writer(os.Stderr)
   492  	if usecolor {
   493  		output = colorable.NewColorableStderr()
   494  	}
   495  	glogger := log.NewGlogHandler(log.StreamHandler(output, log.TerminalFormat(usecolor)))
   496  	log.PrintOrigins(true)
   497  	log.ChangeGlobalLogLevel(glogger, log.Lvl(3))
   498  	glogger.Vmodule("")
   499  	glogger.BacktraceAt("")
   500  	log.Root().SetHandler(glogger)
   501  }
   502  
   503  // splitSubList splits a committee into two groups w/o proposer
   504  // one for n nodes, the other for len(committee) - n - 1 nodes
   505  func splitSubList(committee []istanbul.Validator, n int, proposerAddr common.Address) ([]istanbul.Validator, []istanbul.Validator) {
   506  	var subCN, remainingCN []istanbul.Validator
   507  
   508  	for _, val := range committee {
   509  		if val.Address() == proposerAddr {
   510  			// proposer is not included in any group
   511  			continue
   512  		}
   513  		if len(subCN) < n {
   514  			subCN = append(subCN, val)
   515  		} else {
   516  			remainingCN = append(remainingCN, val)
   517  		}
   518  	}
   519  	return subCN, remainingCN
   520  }
   521  
   522  // Simulate a proposer that receives messages from disagreeing groups of CNs.
   523  func simulateMaliciousCN(t *testing.T, numValidators int, numMalicious int) State {
   524  	if testing.Verbose() {
   525  		enableLog()
   526  	}
   527  
   528  	fork.SetHardForkBlockNumberConfig(&params.ChainConfig{})
   529  	defer fork.ClearHardForkBlockNumberConfig()
   530  
   531  	// Note that genValidators(n) will generate n/3 validators.
   532  	// We want n validators, thus calling genValidators(3n).
   533  	validatorAddrs, validatorKeyMap := genValidators(numValidators * 3)
   534  
   535  	// Add more EXPECT()s to remove unexpected call error
   536  	mockBackend, mockCtrl := newMockBackend(t, validatorAddrs)
   537  	mockBackend.EXPECT().Commit(gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
   538  	mockBackend.EXPECT().HasBadProposal(gomock.Any()).Return(true).AnyTimes()
   539  	defer mockCtrl.Finish()
   540  
   541  	var (
   542  		// it creates two pre-defined blocks: one for benign CNs, the other for the malicious
   543  		// newProposal is a block which the proposer has created
   544  		// malProposal is an incorrect block that malicious CNs use to try stop consensus
   545  		lastProposal, _ = mockBackend.LastProposal()
   546  		lastBlock       = lastProposal.(*types.Block)
   547  		validators      = mockBackend.Validators(lastBlock)
   548  		proposer        = validators.GetProposer()
   549  		proposerKey     = validatorKeyMap[proposer.Address()]
   550  		// the proposer generates a block as newProposal
   551  		// malicious CNs does not accept the proposer's block and use malProposal's hash value for consensus
   552  		newProposal, _ = genBlockParams(lastBlock, proposerKey, 0, 1, 1)
   553  		malProposal, _ = genBlockParams(lastBlock, proposerKey, 0, 0, 0)
   554  	)
   555  
   556  	// Start istanbul core
   557  	istConfig := istanbul.DefaultConfig
   558  	istConfig.ProposerPolicy = istanbul.WeightedRandom
   559  	istCore := New(mockBackend, istConfig).(*core)
   560  	err := istCore.Start()
   561  	require.Nil(t, err)
   562  	defer istCore.Stop()
   563  
   564  	// Step 1 - Pre-prepare with correct message
   565  
   566  	// Create pre-prepare message
   567  	istanbulMsg, err := genIstanbulMsg(msgPreprepare, lastBlock.Hash(), newProposal, proposer.Address(), proposerKey)
   568  	require.Nil(t, err)
   569  
   570  	// Handle pre-prepare message
   571  	err = istCore.handleMsg(istanbulMsg.Payload)
   572  	require.Nil(t, err)
   573  
   574  	// splitSubList split current committee into benign CNs and malicious CNs
   575  	subList := validators.SubList(lastBlock.Hash(), istCore.currentView())
   576  	maliciousCNs, benignCNs := splitSubList(subList, numMalicious, proposer.Address())
   577  	benignCNs = append(benignCNs, proposer)
   578  
   579  	// Shortcut for sending consensus message to everyone in `CNList`
   580  	sendMessages := func(state uint64, proposal *types.Block, CNList []istanbul.Validator) {
   581  		for _, val := range CNList {
   582  			valKey := validatorKeyMap[val.Address()]
   583  			istanbulMsg, err := genIstanbulMsg(state, lastBlock.Hash(), proposal, val.Address(), valKey)
   584  			assert.Nil(t, err)
   585  			err = istCore.handleMsg(istanbulMsg.Payload)
   586  			// assert.Nil(t, err)
   587  		}
   588  	}
   589  
   590  	// Step 2 - Receive disagreeing prepare messages
   591  
   592  	sendMessages(msgPrepare, newProposal, benignCNs)
   593  	sendMessages(msgPrepare, malProposal, maliciousCNs)
   594  
   595  	if istCore.state.Cmp(StatePreprepared) == 0 {
   596  		t.Logf("State stuck at preprepared")
   597  		return istCore.state
   598  	}
   599  
   600  	// Step 3 - Receive disagreeing commit messages
   601  
   602  	sendMessages(msgCommit, newProposal, benignCNs)
   603  	sendMessages(msgCommit, malProposal, maliciousCNs)
   604  	return istCore.state
   605  }
   606  
   607  // TestCore_MalCN tests whether the proposer can commit when malicious CNs exist.
   608  func TestCore_malCN(t *testing.T) {
   609  	// If there are less than 'f' malicious CNs, proposer can commit.
   610  	state := simulateMaliciousCN(t, 4, 1)
   611  	assert.Equal(t, StateCommitted, state)
   612  
   613  	// If there are more than 'f' malicious CNs, the proposer cannot commit, stuck at preprepared state.
   614  	state = simulateMaliciousCN(t, 4, 3)
   615  	assert.Equal(t, StatePreprepared, state)
   616  }
   617  
   618  // Simulate chain split depending on the number of numValidators
   619  func simulateChainSplit(t *testing.T, numValidators int) (State, State) {
   620  	if testing.Verbose() {
   621  		enableLog()
   622  	}
   623  
   624  	fork.SetHardForkBlockNumberConfig(&params.ChainConfig{})
   625  	defer fork.ClearHardForkBlockNumberConfig()
   626  
   627  	// Note that genValidators(n) will generate n/3 validators.
   628  	// We want n validators, thus calling genValidators(3n).
   629  	validatorAddrs, validatorKeyMap := genValidators(numValidators * 3)
   630  
   631  	// Add more EXPECT()s to remove unexpected call error
   632  	mockBackend, mockCtrl := newMockBackend(t, validatorAddrs)
   633  	mockBackend.EXPECT().Commit(gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
   634  	mockBackend.EXPECT().HasBadProposal(gomock.Any()).Return(true).AnyTimes()
   635  	defer mockCtrl.Finish()
   636  
   637  	var (
   638  		lastProposal, _ = mockBackend.LastProposal()
   639  		lastBlock       = lastProposal.(*types.Block)
   640  		validators      = mockBackend.Validators(lastBlock)
   641  		proposer        = validators.GetProposer()
   642  		proposerKey     = validatorKeyMap[proposer.Address()]
   643  	)
   644  
   645  	// Start istanbul core
   646  	istConfig := istanbul.DefaultConfig
   647  	istConfig.ProposerPolicy = istanbul.WeightedRandom
   648  	coreProposer := New(mockBackend, istConfig).(*core)
   649  	coreA := New(mockBackend, istConfig).(*core)
   650  	coreB := New(mockBackend, istConfig).(*core)
   651  	require.Nil(t,
   652  		coreProposer.Start(),
   653  		coreA.Start(),
   654  		coreB.Start())
   655  	defer coreProposer.Stop()
   656  	defer coreA.Stop()
   657  	defer coreB.Stop()
   658  
   659  	// make two groups
   660  	// the number of group size is (numValidators-1/2) + 1
   661  	// groupA consists of proposer, coreA, unnamed node(s)
   662  	// groupB consists of proposer, coreB, unnamed node(s)
   663  	subList := validators.SubList(lastBlock.Hash(), coreProposer.currentView())
   664  	groupA, groupB := splitSubList(subList, (numValidators-1)/2, proposer.Address())
   665  	groupA = append(groupA, proposer)
   666  	groupB = append(groupB, proposer)
   667  
   668  	// Step 1 - the malicious proposer generates two blocks
   669  	proposalA, err := genBlockParams(lastBlock, proposerKey, 0, 0, 1)
   670  	assert.Nil(t, err)
   671  
   672  	proposalB, err := genBlockParams(lastBlock, proposerKey, 1000, 10, 1)
   673  	assert.Nil(t, err)
   674  
   675  	// Shortcut for sending message `proposal` to core `c`
   676  	sendMessages := func(state uint64, proposal *types.Block, CNList []istanbul.Validator, c *core) {
   677  		for _, val := range CNList {
   678  			valKey := validatorKeyMap[val.Address()]
   679  			if state == msgPreprepare {
   680  				istanbulMsg, _ := genIstanbulMsg(state, lastBlock.Hash(), proposal, proposer.Address(), valKey)
   681  				err = c.handleMsg(istanbulMsg.Payload)
   682  			} else {
   683  				istanbulMsg, _ := genIstanbulMsg(state, lastBlock.Hash(), proposal, val.Address(), valKey)
   684  				err = c.handleMsg(istanbulMsg.Payload)
   685  			}
   686  			if err != nil {
   687  				t.Logf("handleMsg error: %s", err)
   688  			}
   689  		}
   690  	}
   691  	// Step 2 - exchange consensus messages inside each group
   692  
   693  	// the proposer sends two different blocks to each group
   694  	// each group receives a block and handles the message
   695  	// when chain split occurs, their states become StateCommitted
   696  	// otherwise, their states stay StatePreprepared
   697  	sendMessages(msgPreprepare, proposalA, groupA, coreA)
   698  	sendMessages(msgPrepare, proposalA, groupA, coreA)
   699  	if coreA.state.Cmp(StatePrepared) == 0 {
   700  		sendMessages(msgCommit, proposalA, groupA, coreA)
   701  	}
   702  
   703  	sendMessages(msgPreprepare, proposalB, groupB, coreB)
   704  	sendMessages(msgPrepare, proposalB, groupB, coreB)
   705  	if coreB.state.Cmp(StatePrepared) == 0 {
   706  		sendMessages(msgCommit, proposalB, groupB, coreB)
   707  	}
   708  
   709  	return coreA.state, coreB.state
   710  }
   711  
   712  // TestCore_chainSplit tests whether a chain split occurs in a certain conditions:
   713  //  1. the number of validators does not consist of 3f+1;
   714  //     e.g. if the number of validator is 5, it consists of 3f+2 (f=1)
   715  //  2. the proposer is malicious; it sends two different blocks to each group
   716  //
   717  // After Ceil(2N/3) quorum calculation, the chain should not be split
   718  func TestCore_chainSplit(t *testing.T) {
   719  	// Even though the number of validators is not 3f+1, the chain is not split.
   720  	stateA, stateB := simulateChainSplit(t, 5)
   721  	assert.Equal(t, StatePreprepared, stateA)
   722  	assert.Equal(t, StatePreprepared, stateB)
   723  
   724  	// If the number of validators is 3f+1, the chain cannot be split.
   725  	stateA, stateB = simulateChainSplit(t, 7)
   726  	fmt.Println(stateA, stateB)
   727  	assert.Equal(t, StatePreprepared, stateA)
   728  	assert.Equal(t, StatePreprepared, stateB)
   729  }
   730  
   731  // TestCore_handleTimeoutMsg_race tests a race condition between round change triggers.
   732  // There should be no race condition when round change message and timeout event are handled simultaneously.
   733  func TestCore_handleTimeoutMsg_race(t *testing.T) {
   734  	fork.SetHardForkBlockNumberConfig(&params.ChainConfig{})
   735  	defer fork.ClearHardForkBlockNumberConfig()
   736  
   737  	// important variables to construct test cases
   738  	const sleepTime = 200 * time.Millisecond
   739  	const processingTime = 400 * time.Millisecond
   740  
   741  	type testCase struct {
   742  		name          string
   743  		timeoutTime   time.Duration
   744  		messageRound  int64
   745  		expectedRound int64
   746  	}
   747  	testCases := []testCase{
   748  		{
   749  			// if timeoutTime < sleepTime,
   750  			// timeout event will be posted and then round change message will be processed
   751  			name:          "timeout before processing the (2f+1)th round change message",
   752  			timeoutTime:   50 * time.Millisecond,
   753  			messageRound:  10,
   754  			expectedRound: 10,
   755  		},
   756  		{
   757  			// if timeoutTime > sleepTime && timeoutTime < (processingTime + sleepTime),
   758  			// timeout event will be posted during the processing of (2f+1)th round change message
   759  			name:          "timeout during processing the (2f+1)th round change message",
   760  			timeoutTime:   300 * time.Millisecond,
   761  			messageRound:  20,
   762  			expectedRound: 20,
   763  		},
   764  	}
   765  
   766  	validatorAddrs, _ := genValidators(10)
   767  	mockBackend, mockCtrl := newMockBackend(t, validatorAddrs)
   768  	defer mockCtrl.Finish()
   769  
   770  	istConfig := istanbul.DefaultConfig
   771  	istConfig.ProposerPolicy = istanbul.WeightedRandom
   772  
   773  	istCore := New(mockBackend, istConfig).(*core)
   774  	if err := istCore.Start(); err != nil {
   775  		t.Fatal(err)
   776  	}
   777  	defer istCore.Stop()
   778  
   779  	eventMux := mockBackend.EventMux()
   780  	lastProposal, _ := mockBackend.LastProposal()
   781  	sequence := istCore.current.sequence.Int64()
   782  
   783  	for _, tc := range testCases {
   784  		handler := func(t *testing.T) {
   785  			roundChangeTimer := istCore.roundChangeTimer.Load().(*time.Timer)
   786  
   787  			// reset timeout timer of this round and wait some time
   788  			roundChangeTimer.Reset(tc.timeoutTime)
   789  			time.Sleep(sleepTime)
   790  
   791  			// `istCore.validateFn` will be executed on processing a istanbul message
   792  			istCore.validateFn = func(arg1 []byte, arg2 []byte) (common.Address, error) {
   793  				// postpones the processing of a istanbul message
   794  				time.Sleep(processingTime)
   795  				return common.Address{}, nil
   796  			}
   797  
   798  			// prepare a round change message payload
   799  			payload := makeRCMsgPayload(tc.messageRound, sequence, lastProposal.Hash(), validatorAddrs[0])
   800  			if payload == nil {
   801  				t.Fatal("failed to make a round change message payload")
   802  			}
   803  
   804  			// one round change message changes the round because the committee size of mockBackend is 3
   805  			err := eventMux.Post(istanbul.MessageEvent{
   806  				Hash:    lastProposal.Hash(),
   807  				Payload: payload,
   808  			})
   809  			if err != nil {
   810  				t.Fatal(err)
   811  			}
   812  
   813  			// wait until the istanbul message have processed
   814  			time.Sleep(processingTime + sleepTime)
   815  			roundChangeTimer.Stop()
   816  
   817  			// check the result
   818  			assert.Equal(t, tc.expectedRound, istCore.current.round.Int64())
   819  		}
   820  		t.Run(tc.name, handler)
   821  	}
   822  }
   823  
   824  // makeRCMsgPayload makes a payload of round change message.
   825  func makeRCMsgPayload(round int64, sequence int64, prevHash common.Hash, senderAddr common.Address) []byte {
   826  	subject, err := Encode(&istanbul.Subject{
   827  		View: &istanbul.View{
   828  			Round:    big.NewInt(round),
   829  			Sequence: big.NewInt(sequence),
   830  		},
   831  		Digest:   common.Hash{},
   832  		PrevHash: prevHash,
   833  	})
   834  	if err != nil {
   835  		return nil
   836  	}
   837  
   838  	msg := &message{
   839  		Hash:    prevHash,
   840  		Code:    msgRoundChange,
   841  		Msg:     subject,
   842  		Address: senderAddr,
   843  	}
   844  
   845  	payload, err := msg.Payload()
   846  	if err != nil {
   847  		return nil
   848  	}
   849  
   850  	return payload
   851  }