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 }