github.com/badrootd/nibiru-cometbft@v0.37.5-0.20240307173500-2a75559eee9b/consensus/invalid_test.go (about)

     1  package consensus
     2  
     3  import (
     4  	"testing"
     5  
     6  	"github.com/badrootd/nibiru-cometbft/libs/bytes"
     7  	"github.com/badrootd/nibiru-cometbft/libs/log"
     8  	cmtrand "github.com/badrootd/nibiru-cometbft/libs/rand"
     9  	"github.com/badrootd/nibiru-cometbft/p2p"
    10  	cmtcons "github.com/badrootd/nibiru-cometbft/proto/tendermint/consensus"
    11  	cmtproto "github.com/badrootd/nibiru-cometbft/proto/tendermint/types"
    12  	"github.com/badrootd/nibiru-cometbft/types"
    13  )
    14  
    15  //----------------------------------------------
    16  // byzantine failures
    17  
    18  // one byz val sends a precommit for a random block at each height
    19  // Ensure a testnet makes blocks
    20  func TestReactorInvalidPrecommit(t *testing.T) {
    21  	N := 4
    22  	css, cleanup := randConsensusNet(N, "consensus_reactor_test", newMockTickerFunc(true), newKVStore)
    23  	defer cleanup()
    24  
    25  	for i := 0; i < 4; i++ {
    26  		ticker := NewTimeoutTicker()
    27  		ticker.SetLogger(css[i].Logger)
    28  		css[i].SetTimeoutTicker(ticker)
    29  
    30  	}
    31  
    32  	reactors, blocksSubs, eventBuses := startConsensusNet(t, css, N)
    33  
    34  	// this val sends a random precommit at each height
    35  	byzValIdx := 0
    36  	byzVal := css[byzValIdx]
    37  	byzR := reactors[byzValIdx]
    38  
    39  	// update the doPrevote function to just send a valid precommit for a random block
    40  	// and otherwise disable the priv validator
    41  	byzVal.mtx.Lock()
    42  	pv := byzVal.privValidator
    43  	byzVal.doPrevote = func(height int64, round int32) {
    44  		invalidDoPrevoteFunc(t, height, round, byzVal, byzR.Switch, pv)
    45  	}
    46  	byzVal.mtx.Unlock()
    47  	defer stopConsensusNet(log.TestingLogger(), reactors, eventBuses)
    48  
    49  	// wait for a bunch of blocks
    50  	// TODO: make this tighter by ensuring the halt happens by block 2
    51  	for i := 0; i < 10; i++ {
    52  		timeoutWaitGroup(t, N, func(j int) {
    53  			<-blocksSubs[j].Out()
    54  		}, css)
    55  	}
    56  }
    57  
    58  func invalidDoPrevoteFunc(t *testing.T, height int64, round int32, cs *State, sw *p2p.Switch, pv types.PrivValidator) {
    59  	// routine to:
    60  	// - precommit for a random block
    61  	// - send precommit to all peers
    62  	// - disable privValidator (so we don't do normal precommits)
    63  	go func() {
    64  		cs.mtx.Lock()
    65  		cs.privValidator = pv
    66  		pubKey, err := cs.privValidator.GetPubKey()
    67  		if err != nil {
    68  			panic(err)
    69  		}
    70  		addr := pubKey.Address()
    71  		valIndex, _ := cs.Validators.GetByAddress(addr)
    72  
    73  		// precommit a random block
    74  		blockHash := bytes.HexBytes(cmtrand.Bytes(32))
    75  		precommit := &types.Vote{
    76  			ValidatorAddress: addr,
    77  			ValidatorIndex:   valIndex,
    78  			Height:           cs.Height,
    79  			Round:            cs.Round,
    80  			Timestamp:        cs.voteTime(),
    81  			Type:             cmtproto.PrecommitType,
    82  			BlockID: types.BlockID{
    83  				Hash:          blockHash,
    84  				PartSetHeader: types.PartSetHeader{Total: 1, Hash: cmtrand.Bytes(32)}},
    85  		}
    86  		p := precommit.ToProto()
    87  		err = cs.privValidator.SignVote(cs.state.ChainID, p)
    88  		if err != nil {
    89  			t.Error(err)
    90  		}
    91  		precommit.Signature = p.Signature
    92  		cs.privValidator = nil // disable priv val so we don't do normal votes
    93  		cs.mtx.Unlock()
    94  
    95  		peers := sw.Peers().List()
    96  		for _, peer := range peers {
    97  			cs.Logger.Info("Sending bad vote", "block", blockHash, "peer", peer)
    98  			peer.SendEnvelope(p2p.Envelope{
    99  				Message:   &cmtcons.Vote{Vote: precommit.ToProto()},
   100  				ChannelID: VoteChannel,
   101  			})
   102  		}
   103  	}()
   104  }