github.com/evdatsion/aphelion-dpos-bft@v0.32.1/evidence/reactor_test.go (about) 1 package evidence 2 3 import ( 4 "fmt" 5 "sync" 6 "testing" 7 "time" 8 9 "github.com/go-kit/kit/log/term" 10 "github.com/stretchr/testify/assert" 11 12 cfg "github.com/evdatsion/aphelion-dpos-bft/config" 13 "github.com/evdatsion/aphelion-dpos-bft/crypto/secp256k1" 14 dbm "github.com/evdatsion/aphelion-dpos-bft/libs/db" 15 "github.com/evdatsion/aphelion-dpos-bft/libs/log" 16 "github.com/evdatsion/aphelion-dpos-bft/p2p" 17 "github.com/evdatsion/aphelion-dpos-bft/types" 18 ) 19 20 // evidenceLogger is a TestingLogger which uses a different 21 // color for each validator ("validator" key must exist). 22 func evidenceLogger() log.Logger { 23 return log.TestingLoggerWithColorFn(func(keyvals ...interface{}) term.FgBgColor { 24 for i := 0; i < len(keyvals)-1; i += 2 { 25 if keyvals[i] == "validator" { 26 return term.FgBgColor{Fg: term.Color(uint8(keyvals[i+1].(int) + 1))} 27 } 28 } 29 return term.FgBgColor{} 30 }) 31 } 32 33 // connect N evidence reactors through N switches 34 func makeAndConnectEvidenceReactors(config *cfg.Config, stateDBs []dbm.DB) []*EvidenceReactor { 35 N := len(stateDBs) 36 reactors := make([]*EvidenceReactor, N) 37 logger := evidenceLogger() 38 for i := 0; i < N; i++ { 39 40 evidenceDB := dbm.NewMemDB() 41 pool := NewEvidencePool(stateDBs[i], evidenceDB) 42 reactors[i] = NewEvidenceReactor(pool) 43 reactors[i].SetLogger(logger.With("validator", i)) 44 } 45 46 p2p.MakeConnectedSwitches(config.P2P, N, func(i int, s *p2p.Switch) *p2p.Switch { 47 s.AddReactor("EVIDENCE", reactors[i]) 48 return s 49 50 }, p2p.Connect2Switches) 51 return reactors 52 } 53 54 // wait for all evidence on all reactors 55 func waitForEvidence(t *testing.T, evs types.EvidenceList, reactors []*EvidenceReactor) { 56 // wait for the evidence in all evpools 57 wg := new(sync.WaitGroup) 58 for i := 0; i < len(reactors); i++ { 59 wg.Add(1) 60 go _waitForEvidence(t, wg, evs, i, reactors) 61 } 62 63 done := make(chan struct{}) 64 go func() { 65 wg.Wait() 66 close(done) 67 }() 68 69 timer := time.After(TIMEOUT) 70 select { 71 case <-timer: 72 t.Fatal("Timed out waiting for evidence") 73 case <-done: 74 } 75 } 76 77 // wait for all evidence on a single evpool 78 func _waitForEvidence(t *testing.T, wg *sync.WaitGroup, evs types.EvidenceList, reactorIdx int, reactors []*EvidenceReactor) { 79 80 evpool := reactors[reactorIdx].evpool 81 for len(evpool.PendingEvidence(-1)) != len(evs) { 82 time.Sleep(time.Millisecond * 100) 83 } 84 85 reapedEv := evpool.PendingEvidence(-1) 86 // put the reaped evidence in a map so we can quickly check we got everything 87 evMap := make(map[string]types.Evidence) 88 for _, e := range reapedEv { 89 evMap[string(e.Hash())] = e 90 } 91 for i, expectedEv := range evs { 92 gotEv := evMap[string(expectedEv.Hash())] 93 assert.Equal(t, expectedEv, gotEv, 94 fmt.Sprintf("evidence at index %d on reactor %d don't match: %v vs %v", 95 i, reactorIdx, expectedEv, gotEv)) 96 } 97 98 wg.Done() 99 } 100 101 func sendEvidence(t *testing.T, evpool *EvidencePool, valAddr []byte, n int) types.EvidenceList { 102 evList := make([]types.Evidence, n) 103 for i := 0; i < n; i++ { 104 ev := types.NewMockGoodEvidence(int64(i+1), 0, valAddr) 105 err := evpool.AddEvidence(ev) 106 assert.Nil(t, err) 107 evList[i] = ev 108 } 109 return evList 110 } 111 112 var ( 113 NUM_EVIDENCE = 10 114 TIMEOUT = 120 * time.Second // ridiculously high because CircleCI is slow 115 ) 116 117 func TestReactorBroadcastEvidence(t *testing.T) { 118 config := cfg.TestConfig() 119 N := 7 120 121 // create statedb for everyone 122 stateDBs := make([]dbm.DB, N) 123 valAddr := []byte("myval") 124 // we need validators saved for heights at least as high as we have evidence for 125 height := int64(NUM_EVIDENCE) + 10 126 for i := 0; i < N; i++ { 127 stateDBs[i] = initializeValidatorState(valAddr, height) 128 } 129 130 // make reactors from statedb 131 reactors := makeAndConnectEvidenceReactors(config, stateDBs) 132 133 // set the peer height on each reactor 134 for _, r := range reactors { 135 for _, peer := range r.Switch.Peers().List() { 136 ps := peerState{height} 137 peer.Set(types.PeerStateKey, ps) 138 } 139 } 140 141 // send a bunch of valid evidence to the first reactor's evpool 142 // and wait for them all to be received in the others 143 evList := sendEvidence(t, reactors[0].evpool, valAddr, NUM_EVIDENCE) 144 waitForEvidence(t, evList, reactors) 145 } 146 147 type peerState struct { 148 height int64 149 } 150 151 func (ps peerState) GetHeight() int64 { 152 return ps.height 153 } 154 155 func TestReactorSelectiveBroadcast(t *testing.T) { 156 config := cfg.TestConfig() 157 158 valAddr := []byte("myval") 159 height1 := int64(NUM_EVIDENCE) + 10 160 height2 := int64(NUM_EVIDENCE) / 2 161 162 // DB1 is ahead of DB2 163 stateDB1 := initializeValidatorState(valAddr, height1) 164 stateDB2 := initializeValidatorState(valAddr, height2) 165 166 // make reactors from statedb 167 reactors := makeAndConnectEvidenceReactors(config, []dbm.DB{stateDB1, stateDB2}) 168 169 // set the peer height on each reactor 170 for _, r := range reactors { 171 for _, peer := range r.Switch.Peers().List() { 172 ps := peerState{height1} 173 peer.Set(types.PeerStateKey, ps) 174 } 175 } 176 177 // update the first reactor peer's height to be very small 178 peer := reactors[0].Switch.Peers().List()[0] 179 ps := peerState{height2} 180 peer.Set(types.PeerStateKey, ps) 181 182 // send a bunch of valid evidence to the first reactor's evpool 183 evList := sendEvidence(t, reactors[0].evpool, valAddr, NUM_EVIDENCE) 184 185 // only ones less than the peers height should make it through 186 waitForEvidence(t, evList[:NUM_EVIDENCE/2], reactors[1:2]) 187 188 // peers should still be connected 189 peers := reactors[1].Switch.Peers().List() 190 assert.Equal(t, 1, len(peers)) 191 } 192 func TestEvidenceListMessageValidationBasic(t *testing.T) { 193 194 testCases := []struct { 195 testName string 196 malleateEvListMsg func(*EvidenceListMessage) 197 expectErr bool 198 }{ 199 {"Good EvidenceListMessage", func(evList *EvidenceListMessage) {}, false}, 200 {"Invalid EvidenceListMessage", func(evList *EvidenceListMessage) { 201 evList.Evidence = append(evList.Evidence, 202 &types.DuplicateVoteEvidence{PubKey: secp256k1.GenPrivKey().PubKey()}) 203 }, true}, 204 } 205 for _, tc := range testCases { 206 t.Run(tc.testName, func(t *testing.T) { 207 evListMsg := &EvidenceListMessage{} 208 n := 3 209 valAddr := []byte("myval") 210 evListMsg.Evidence = make([]types.Evidence, n) 211 for i := 0; i < n; i++ { 212 evListMsg.Evidence[i] = types.NewMockGoodEvidence(int64(i+1), 0, valAddr) 213 } 214 tc.malleateEvListMsg(evListMsg) 215 assert.Equal(t, tc.expectErr, evListMsg.ValidateBasic() != nil, "Validate Basic had an unexpected result") 216 }) 217 } 218 }