github.com/okex/exchain@v1.8.0/libs/tendermint/evidence/reactor_test.go (about) 1 package evidence 2 3 import ( 4 "fmt" 5 "net" 6 "sync" 7 "testing" 8 "time" 9 10 "github.com/go-kit/kit/log/term" 11 "github.com/okex/exchain/libs/tendermint/libs/cmap" 12 "github.com/stretchr/testify/assert" 13 14 dbm "github.com/okex/exchain/libs/tm-db" 15 16 cfg "github.com/okex/exchain/libs/tendermint/config" 17 "github.com/okex/exchain/libs/tendermint/crypto/ed25519" 18 "github.com/okex/exchain/libs/tendermint/crypto/secp256k1" 19 "github.com/okex/exchain/libs/tendermint/libs/log" 20 "github.com/okex/exchain/libs/tendermint/libs/service" 21 "github.com/okex/exchain/libs/tendermint/p2p" 22 "github.com/okex/exchain/libs/tendermint/types" 23 ) 24 25 // mockPeer for testing the PeerSet 26 type mockIllPeer struct { 27 service.BaseService 28 ip net.IP 29 id p2p.ID 30 Data *cmap.CMap 31 } 32 33 func (mp *mockIllPeer) FlushStop() { mp.Stop() } 34 func (mp *mockIllPeer) TrySend(chID byte, msgBytes []byte) bool { return true } 35 func (mp *mockIllPeer) Send(chID byte, msgBytes []byte) bool { return false } 36 func (mp *mockIllPeer) NodeInfo() p2p.NodeInfo { return p2p.DefaultNodeInfo{} } 37 func (mp *mockIllPeer) Status() p2p.ConnectionStatus { return p2p.ConnectionStatus{} } 38 func (mp *mockIllPeer) ID() p2p.ID { return mp.id } 39 func (mp *mockIllPeer) IsOutbound() bool { return false } 40 func (mp *mockIllPeer) IsPersistent() bool { return true } 41 func (mp *mockIllPeer) Get(k string) interface{} { return mp.Data.Get(k) } 42 func (mp *mockIllPeer) Set(k string, v interface{}) { mp.Data.Set(k, v) } 43 func (mp *mockIllPeer) RemoteIP() net.IP { return mp.ip } 44 func (mp *mockIllPeer) SocketAddr() *p2p.NetAddress { return nil } 45 func (mp *mockIllPeer) RemoteAddr() net.Addr { return &net.TCPAddr{IP: mp.ip, Port: 8800} } 46 func (mp *mockIllPeer) CloseConn() error { return nil } 47 48 // Returns a mock peer 49 func newMockPeer(ip net.IP) *mockIllPeer { 50 if ip == nil { 51 ip = net.IP{127, 0, 0, 1} 52 } 53 nodeKey := p2p.NodeKey{PrivKey: ed25519.GenPrivKey()} 54 return &mockIllPeer{ 55 ip: ip, 56 id: nodeKey.ID(), 57 Data: cmap.NewCMap(), 58 } 59 } 60 61 // evidenceLogger is a TestingLogger which uses a different 62 // color for each validator ("validator" key must exist). 63 func evidenceLogger() log.Logger { 64 return log.TestingLoggerWithColorFn(func(keyvals ...interface{}) term.FgBgColor { 65 for i := 0; i < len(keyvals)-1; i += 2 { 66 if keyvals[i] == "validator" { 67 return term.FgBgColor{Fg: term.Color(uint8(keyvals[i+1].(int) + 1))} 68 } 69 } 70 return term.FgBgColor{} 71 }) 72 } 73 74 // connect N evidence reactors through N switches 75 func makeAndConnectReactors(config *cfg.Config, stateDBs []dbm.DB) []*Reactor { 76 N := len(stateDBs) 77 reactors := make([]*Reactor, N) 78 logger := evidenceLogger() 79 for i := 0; i < N; i++ { 80 81 evidenceDB := dbm.NewMemDB() 82 pool := NewPool(stateDBs[i], evidenceDB) 83 reactors[i] = NewReactor(pool) 84 reactors[i].SetLogger(logger.With("validator", i)) 85 } 86 87 p2p.MakeConnectedSwitches(config.P2P, N, func(i int, s *p2p.Switch) *p2p.Switch { 88 s.AddReactor("EVIDENCE", reactors[i]) 89 return s 90 91 }, p2p.Connect2Switches) 92 return reactors 93 } 94 95 // wait for all evidence on all reactors 96 func waitForEvidence(t *testing.T, evs types.EvidenceList, reactors []*Reactor) { 97 // wait for the evidence in all evpools 98 wg := new(sync.WaitGroup) 99 for i := 0; i < len(reactors); i++ { 100 wg.Add(1) 101 go _waitForEvidence(t, wg, evs, i, reactors) 102 } 103 104 done := make(chan struct{}) 105 go func() { 106 wg.Wait() 107 close(done) 108 }() 109 110 timer := time.After(Timeout) 111 select { 112 case <-timer: 113 t.Fatal("Timed out waiting for evidence") 114 case <-done: 115 } 116 } 117 118 // wait for all evidence on a single evpool 119 func _waitForEvidence( 120 t *testing.T, 121 wg *sync.WaitGroup, 122 evs types.EvidenceList, 123 reactorIdx int, 124 reactors []*Reactor, 125 ) { 126 evpool := reactors[reactorIdx].evpool 127 for len(evpool.PendingEvidence(-1)) != len(evs) { 128 time.Sleep(time.Millisecond * 100) 129 } 130 131 reapedEv := evpool.PendingEvidence(-1) 132 // put the reaped evidence in a map so we can quickly check we got everything 133 evMap := make(map[string]types.Evidence) 134 for _, e := range reapedEv { 135 evMap[string(e.Hash())] = e 136 } 137 for i, expectedEv := range evs { 138 gotEv := evMap[string(expectedEv.Hash())] 139 assert.Equal(t, expectedEv, gotEv, 140 fmt.Sprintf("evidence at index %d on reactor %d don't match: %v vs %v", 141 i, reactorIdx, expectedEv, gotEv)) 142 } 143 144 wg.Done() 145 } 146 147 func sendEvidence(t *testing.T, evpool *Pool, valAddr []byte, n int) types.EvidenceList { 148 evList := make([]types.Evidence, n) 149 for i := 0; i < n; i++ { 150 ev := types.NewMockEvidence(int64(i+1), time.Now().UTC(), 0, valAddr) 151 err := evpool.AddEvidence(ev) 152 assert.Nil(t, err) 153 evList[i] = ev 154 } 155 return evList 156 } 157 158 var ( 159 NumEvidence = 10 160 Timeout = 120 * time.Second // ridiculously high because CircleCI is slow 161 ) 162 163 func TestReactorBroadcastEvidence(t *testing.T) { 164 config := cfg.TestConfig() 165 N := 7 166 167 // create statedb for everyone 168 stateDBs := make([]dbm.DB, N) 169 valAddr := []byte("myval") 170 // we need validators saved for heights at least as high as we have evidence for 171 height := int64(NumEvidence) + 10 172 for i := 0; i < N; i++ { 173 stateDBs[i] = initializeValidatorState(valAddr, height) 174 } 175 176 // make reactors from statedb 177 reactors := makeAndConnectReactors(config, stateDBs) 178 179 // set the peer height on each reactor 180 for _, r := range reactors { 181 for _, peer := range r.Switch.Peers().List() { 182 ps := peerState{height} 183 peer.Set(types.PeerStateKey, ps) 184 } 185 } 186 187 // send a bunch of valid evidence to the first reactor's evpool 188 // and wait for them all to be received in the others 189 evList := sendEvidence(t, reactors[0].evpool, valAddr, NumEvidence) 190 waitForEvidence(t, evList, reactors) 191 } 192 193 type peerState struct { 194 height int64 195 } 196 197 func (ps peerState) GetHeight() int64 { 198 return ps.height 199 } 200 201 func TestReactorSelectiveBroadcast(t *testing.T) { 202 config := cfg.TestConfig() 203 204 valAddr := []byte("myval") 205 height1 := int64(NumEvidence) + 10 206 height2 := int64(NumEvidence) / 2 207 208 // DB1 is ahead of DB2 209 stateDB1 := initializeValidatorState(valAddr, height1) 210 stateDB2 := initializeValidatorState(valAddr, height2) 211 212 // make reactors from statedb 213 reactors := makeAndConnectReactors(config, []dbm.DB{stateDB1, stateDB2}) 214 215 // set the peer height on each reactor 216 for _, r := range reactors { 217 for _, peer := range r.Switch.Peers().List() { 218 ps := peerState{height1} 219 peer.Set(types.PeerStateKey, ps) 220 } 221 } 222 223 // update the first reactor peer's height to be very small 224 peer := reactors[0].Switch.Peers().List()[0] 225 ps := peerState{height2} 226 peer.Set(types.PeerStateKey, ps) 227 228 // send a bunch of valid evidence to the first reactor's evpool 229 evList := sendEvidence(t, reactors[0].evpool, valAddr, NumEvidence) 230 231 // only ones less than the peers height should make it through 232 waitForEvidence(t, evList[:NumEvidence/2], reactors[1:2]) 233 234 // peers should still be connected 235 peers := reactors[1].Switch.Peers().List() 236 assert.Equal(t, 1, len(peers)) 237 } 238 func TestListMessageValidationBasic(t *testing.T) { 239 240 testCases := []struct { 241 testName string 242 malleateEvListMsg func(*ListMessage) 243 expectErr bool 244 }{ 245 {"Good ListMessage", func(evList *ListMessage) {}, false}, 246 {"Invalid ListMessage", func(evList *ListMessage) { 247 evList.Evidence = append(evList.Evidence, 248 &types.DuplicateVoteEvidence{PubKey: secp256k1.GenPrivKey().PubKey()}) 249 }, true}, 250 } 251 for _, tc := range testCases { 252 tc := tc 253 t.Run(tc.testName, func(t *testing.T) { 254 evListMsg := &ListMessage{} 255 n := 3 256 valAddr := []byte("myval") 257 evListMsg.Evidence = make([]types.Evidence, n) 258 for i := 0; i < n; i++ { 259 evListMsg.Evidence[i] = types.NewMockEvidence(int64(i+1), time.Now(), 0, valAddr) 260 } 261 tc.malleateEvListMsg(evListMsg) 262 assert.Equal(t, tc.expectErr, evListMsg.ValidateBasic() != nil, "Validate Basic had an unexpected result") 263 }) 264 } 265 } 266 267 func TestAddPeer(t *testing.T) { 268 valAddr := []byte("val1") 269 //config := cfg.TestConfig() 270 271 height := int64(NumEvidence) + 10 272 stateDB := initializeValidatorState(valAddr, height) 273 274 evidenceDB := dbm.NewMemDB() 275 logger := evidenceLogger() 276 pool := NewPool(stateDB, evidenceDB) 277 278 evReactor := NewReactor(pool) 279 evReactor.SetLogger(logger.With("module", "evidence")) 280 281 p := newMockPeer(net.IP{127, 0, 0, 1}) 282 evReactor.AddPeer(p) 283 ps := peerState{height} 284 p.Set(types.PeerStateKey, ps) 285 286 ev := types.NewMockEvidence(1, time.Now().UTC(), 0, valAddr) 287 pool.AddEvidence(ev) 288 289 //timer := time.After(Timeout) 290 //select { 291 //case <-timer: 292 // t.Log("Timed out waiting for evidence") 293 //} 294 }