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