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