github.com/DFWallet/tendermint-cosmos@v0.0.2/evidence/reactor.go (about) 1 package evidence 2 3 import ( 4 "fmt" 5 "time" 6 7 clist "github.com/DFWallet/tendermint-cosmos/libs/clist" 8 "github.com/DFWallet/tendermint-cosmos/libs/log" 9 "github.com/DFWallet/tendermint-cosmos/p2p" 10 tmproto "github.com/DFWallet/tendermint-cosmos/proto/tendermint/types" 11 "github.com/DFWallet/tendermint-cosmos/types" 12 ) 13 14 const ( 15 EvidenceChannel = byte(0x38) 16 17 maxMsgSize = 1048576 // 1MB TODO make it configurable 18 19 // broadcast all uncommitted evidence this often. This sets when the reactor 20 // goes back to the start of the list and begins sending the evidence again. 21 // Most evidence should be committed in the very next block that is why we wait 22 // just over the block production rate before sending evidence again. 23 broadcastEvidenceIntervalS = 10 24 // If a message fails wait this much before sending it again 25 peerRetryMessageIntervalMS = 100 26 ) 27 28 // Reactor handles evpool evidence broadcasting amongst peers. 29 type Reactor struct { 30 p2p.BaseReactor 31 evpool *Pool 32 eventBus *types.EventBus 33 } 34 35 // NewReactor returns a new Reactor with the given config and evpool. 36 func NewReactor(evpool *Pool) *Reactor { 37 evR := &Reactor{ 38 evpool: evpool, 39 } 40 evR.BaseReactor = *p2p.NewBaseReactor("Evidence", evR) 41 return evR 42 } 43 44 // SetLogger sets the Logger on the reactor and the underlying Evidence. 45 func (evR *Reactor) SetLogger(l log.Logger) { 46 evR.Logger = l 47 evR.evpool.SetLogger(l) 48 } 49 50 // GetChannels implements Reactor. 51 // It returns the list of channels for this reactor. 52 func (evR *Reactor) GetChannels() []*p2p.ChannelDescriptor { 53 return []*p2p.ChannelDescriptor{ 54 { 55 ID: EvidenceChannel, 56 Priority: 6, 57 RecvMessageCapacity: maxMsgSize, 58 }, 59 } 60 } 61 62 // AddPeer implements Reactor. 63 func (evR *Reactor) AddPeer(peer p2p.Peer) { 64 go evR.broadcastEvidenceRoutine(peer) 65 } 66 67 // Receive implements Reactor. 68 // It adds any received evidence to the evpool. 69 func (evR *Reactor) Receive(chID byte, src p2p.Peer, msgBytes []byte) { 70 evis, err := decodeMsg(msgBytes) 71 if err != nil { 72 evR.Logger.Error("Error decoding message", "src", src, "chId", chID, "err", err) 73 evR.Switch.StopPeerForError(src, err) 74 return 75 } 76 77 for _, ev := range evis { 78 err := evR.evpool.AddEvidence(ev) 79 switch err.(type) { 80 case *types.ErrInvalidEvidence: 81 evR.Logger.Error(err.Error()) 82 // punish peer 83 evR.Switch.StopPeerForError(src, err) 84 return 85 case nil: 86 default: 87 // continue to the next piece of evidence 88 evR.Logger.Error("Evidence has not been added", "evidence", evis, "err", err) 89 } 90 } 91 } 92 93 // SetEventBus implements events.Eventable. 94 func (evR *Reactor) SetEventBus(b *types.EventBus) { 95 evR.eventBus = b 96 } 97 98 // Modeled after the mempool routine. 99 // - Evidence accumulates in a clist. 100 // - Each peer has a routine that iterates through the clist, 101 // sending available evidence to the peer. 102 // - If we're waiting for new evidence and the list is not empty, 103 // start iterating from the beginning again. 104 func (evR *Reactor) broadcastEvidenceRoutine(peer p2p.Peer) { 105 var next *clist.CElement 106 for { 107 // This happens because the CElement we were looking at got garbage 108 // collected (removed). That is, .NextWait() returned nil. Go ahead and 109 // start from the beginning. 110 if next == nil { 111 select { 112 case <-evR.evpool.EvidenceWaitChan(): // Wait until evidence is available 113 if next = evR.evpool.EvidenceFront(); next == nil { 114 continue 115 } 116 case <-peer.Quit(): 117 return 118 case <-evR.Quit(): 119 return 120 } 121 } else if !peer.IsRunning() || !evR.IsRunning() { 122 return 123 } 124 125 ev := next.Value.(types.Evidence) 126 evis := evR.prepareEvidenceMessage(peer, ev) 127 if len(evis) > 0 { 128 evR.Logger.Debug("Gossiping evidence to peer", "ev", ev, "peer", peer) 129 msgBytes, err := encodeMsg(evis) 130 if err != nil { 131 panic(err) 132 } 133 success := peer.Send(EvidenceChannel, msgBytes) 134 if !success { 135 time.Sleep(peerRetryMessageIntervalMS * time.Millisecond) 136 continue 137 } 138 } 139 140 afterCh := time.After(time.Second * broadcastEvidenceIntervalS) 141 select { 142 case <-afterCh: 143 // start from the beginning every tick. 144 // TODO: only do this if we're at the end of the list! 145 next = nil 146 case <-next.NextWaitChan(): 147 // see the start of the for loop for nil check 148 next = next.Next() 149 case <-peer.Quit(): 150 return 151 case <-evR.Quit(): 152 return 153 } 154 } 155 } 156 157 // Returns the message to send to the peer, or nil if the evidence is invalid for the peer. 158 // If message is nil, we should sleep and try again. 159 func (evR Reactor) prepareEvidenceMessage( 160 peer p2p.Peer, 161 ev types.Evidence, 162 ) (evis []types.Evidence) { 163 164 // make sure the peer is up to date 165 evHeight := ev.Height() 166 peerState, ok := peer.Get(types.PeerStateKey).(PeerState) 167 if !ok { 168 // Peer does not have a state yet. We set it in the consensus reactor, but 169 // when we add peer in Switch, the order we call reactors#AddPeer is 170 // different every time due to us using a map. Sometimes other reactors 171 // will be initialized before the consensus reactor. We should wait a few 172 // milliseconds and retry. 173 return nil 174 } 175 176 // NOTE: We only send evidence to peers where 177 // peerHeight - maxAge < evidenceHeight < peerHeight 178 var ( 179 peerHeight = peerState.GetHeight() 180 params = evR.evpool.State().ConsensusParams.Evidence 181 ageNumBlocks = peerHeight - evHeight 182 ) 183 184 if peerHeight <= evHeight { // peer is behind. sleep while he catches up 185 return nil 186 } else if ageNumBlocks > params.MaxAgeNumBlocks { // evidence is too old relative to the peer, skip 187 188 // NOTE: if evidence is too old for an honest peer, then we're behind and 189 // either it already got committed or it never will! 190 evR.Logger.Info("Not sending peer old evidence", 191 "peerHeight", peerHeight, 192 "evHeight", evHeight, 193 "maxAgeNumBlocks", params.MaxAgeNumBlocks, 194 "lastBlockTime", evR.evpool.State().LastBlockTime, 195 "maxAgeDuration", params.MaxAgeDuration, 196 "peer", peer, 197 ) 198 199 return nil 200 } 201 202 // send evidence 203 return []types.Evidence{ev} 204 } 205 206 // PeerState describes the state of a peer. 207 type PeerState interface { 208 GetHeight() int64 209 } 210 211 // encodemsg takes a array of evidence 212 // returns the byte encoding of the List Message 213 func encodeMsg(evis []types.Evidence) ([]byte, error) { 214 evi := make([]tmproto.Evidence, len(evis)) 215 for i := 0; i < len(evis); i++ { 216 ev, err := types.EvidenceToProto(evis[i]) 217 if err != nil { 218 return nil, err 219 } 220 evi[i] = *ev 221 } 222 epl := tmproto.EvidenceList{ 223 Evidence: evi, 224 } 225 226 return epl.Marshal() 227 } 228 229 // decodemsg takes an array of bytes 230 // returns an array of evidence 231 func decodeMsg(bz []byte) (evis []types.Evidence, err error) { 232 lm := tmproto.EvidenceList{} 233 if err := lm.Unmarshal(bz); err != nil { 234 return nil, err 235 } 236 237 evis = make([]types.Evidence, len(lm.Evidence)) 238 for i := 0; i < len(lm.Evidence); i++ { 239 ev, err := types.EvidenceFromProto(&lm.Evidence[i]) 240 if err != nil { 241 return nil, err 242 } 243 evis[i] = ev 244 } 245 246 for i, ev := range evis { 247 if err := ev.ValidateBasic(); err != nil { 248 return nil, fmt.Errorf("invalid evidence (#%d): %v", i, err) 249 } 250 } 251 252 return evis, nil 253 }