github.com/evdatsion/aphelion-dpos-bft@v0.32.1/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/evdatsion/aphelion-dpos-bft/libs/clist" 11 "github.com/evdatsion/aphelion-dpos-bft/libs/log" 12 "github.com/evdatsion/aphelion-dpos-bft/p2p" 13 "github.com/evdatsion/aphelion-dpos-bft/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 // EvidenceReactor handles evpool evidence broadcasting amongst peers. 26 type EvidenceReactor struct { 27 p2p.BaseReactor 28 evpool *EvidencePool 29 eventBus *types.EventBus 30 } 31 32 // NewEvidenceReactor returns a new EvidenceReactor with the given config and evpool. 33 func NewEvidenceReactor(evpool *EvidencePool) *EvidenceReactor { 34 evR := &EvidenceReactor{ 35 evpool: evpool, 36 } 37 evR.BaseReactor = *p2p.NewBaseReactor("EvidenceReactor", evR) 38 return evR 39 } 40 41 // SetLogger sets the Logger on the reactor and the underlying Evidence. 42 func (evR *EvidenceReactor) 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 *EvidenceReactor) 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 *EvidenceReactor) 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 *EvidenceReactor) 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 *EvidenceListMessage: 83 for _, ev := range msg.Evidence { 84 err := evR.evpool.AddEvidence(ev) 85 if err != nil { 86 evR.Logger.Info("Evidence is not valid", "evidence", msg.Evidence, "err", err) 87 // punish peer 88 evR.Switch.StopPeerForError(src, err) 89 } 90 } 91 default: 92 evR.Logger.Error(fmt.Sprintf("Unknown message type %v", reflect.TypeOf(msg))) 93 } 94 } 95 96 // SetEventSwitch implements events.Eventable. 97 func (evR *EvidenceReactor) SetEventBus(b *types.EventBus) { 98 evR.eventBus = b 99 } 100 101 // Modeled after the mempool routine. 102 // - Evidence accumulates in a clist. 103 // - Each peer has a routien that iterates through the clist, 104 // sending available evidence to the peer. 105 // - If we're waiting for new evidence and the list is not empty, 106 // start iterating from the beginning again. 107 func (evR *EvidenceReactor) broadcastEvidenceRoutine(peer p2p.Peer) { 108 var next *clist.CElement 109 for { 110 // This happens because the CElement we were looking at got garbage 111 // collected (removed). That is, .NextWait() returned nil. Go ahead and 112 // start from the beginning. 113 if next == nil { 114 select { 115 case <-evR.evpool.EvidenceWaitChan(): // Wait until evidence is available 116 if next = evR.evpool.EvidenceFront(); next == nil { 117 continue 118 } 119 case <-peer.Quit(): 120 return 121 case <-evR.Quit(): 122 return 123 } 124 } 125 126 ev := next.Value.(types.Evidence) 127 msg, retry := evR.checkSendEvidenceMessage(peer, ev) 128 if msg != nil { 129 success := peer.Send(EvidenceChannel, cdc.MustMarshalBinaryBare(msg)) 130 retry = !success 131 } 132 133 if retry { 134 time.Sleep(peerCatchupSleepIntervalMS * time.Millisecond) 135 continue 136 } 137 138 afterCh := time.After(time.Second * broadcastEvidenceIntervalS) 139 select { 140 case <-afterCh: 141 // start from the beginning every tick. 142 // TODO: only do this if we're at the end of the list! 143 next = nil 144 case <-next.NextWaitChan(): 145 // see the start of the for loop for nil check 146 next = next.Next() 147 case <-peer.Quit(): 148 return 149 case <-evR.Quit(): 150 return 151 } 152 } 153 } 154 155 // Returns the message to send the peer, or nil if the evidence is invalid for the peer. 156 // If message is nil, return true if we should sleep and try again. 157 func (evR EvidenceReactor) checkSendEvidenceMessage(peer p2p.Peer, ev types.Evidence) (msg EvidenceMessage, retry bool) { 158 // make sure the peer is up to date 159 evHeight := ev.Height() 160 peerState, ok := peer.Get(types.PeerStateKey).(PeerState) 161 if !ok { 162 // Peer does not have a state yet. We set it in the consensus reactor, but 163 // when we add peer in Switch, the order we call reactors#AddPeer is 164 // different every time due to us using a map. Sometimes other reactors 165 // will be initialized before the consensus reactor. We should wait a few 166 // milliseconds and retry. 167 return nil, true 168 } 169 170 // NOTE: We only send evidence to peers where 171 // peerHeight - maxAge < evidenceHeight < peerHeight 172 maxAge := evR.evpool.State().ConsensusParams.Evidence.MaxAge 173 peerHeight := peerState.GetHeight() 174 if peerHeight < evHeight { 175 // peer is behind. sleep while he catches up 176 return nil, true 177 } else if peerHeight > evHeight+maxAge { 178 // evidence is too old, skip 179 // NOTE: if evidence is too old for an honest peer, 180 // then we're behind and either it already got committed or it never will! 181 evR.Logger.Info("Not sending peer old evidence", "peerHeight", peerHeight, "evHeight", evHeight, "maxAge", maxAge, "peer", peer) 182 return nil, false 183 } 184 185 // send evidence 186 msg = &EvidenceListMessage{[]types.Evidence{ev}} 187 return msg, false 188 } 189 190 // PeerState describes the state of a peer. 191 type PeerState interface { 192 GetHeight() int64 193 } 194 195 //----------------------------------------------------------------------------- 196 // Messages 197 198 // EvidenceMessage is a message sent or received by the EvidenceReactor. 199 type EvidenceMessage interface { 200 ValidateBasic() error 201 } 202 203 func RegisterEvidenceMessages(cdc *amino.Codec) { 204 cdc.RegisterInterface((*EvidenceMessage)(nil), nil) 205 cdc.RegisterConcrete(&EvidenceListMessage{}, 206 "tendermint/evidence/EvidenceListMessage", nil) 207 } 208 209 func decodeMsg(bz []byte) (msg EvidenceMessage, err error) { 210 if len(bz) > maxMsgSize { 211 return msg, fmt.Errorf("Msg exceeds max size (%d > %d)", len(bz), maxMsgSize) 212 } 213 err = cdc.UnmarshalBinaryBare(bz, &msg) 214 return 215 } 216 217 //------------------------------------- 218 219 // EvidenceListMessage contains a list of evidence. 220 type EvidenceListMessage struct { 221 Evidence []types.Evidence 222 } 223 224 // ValidateBasic performs basic validation. 225 func (m *EvidenceListMessage) ValidateBasic() error { 226 for i, ev := range m.Evidence { 227 if err := ev.ValidateBasic(); err != nil { 228 return fmt.Errorf("Invalid evidence (#%d): %v", i, err) 229 } 230 } 231 return nil 232 } 233 234 // String returns a string representation of the EvidenceListMessage. 235 func (m *EvidenceListMessage) String() string { 236 return fmt.Sprintf("[EvidenceListMessage %v]", m.Evidence) 237 }