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