github.com/opzlabs/tendermint@v0.34.27-terra.rc.2/mempool/v0/reactor.go (about) 1 package v0 2 3 import ( 4 "errors" 5 "fmt" 6 "time" 7 8 "github.com/gogo/protobuf/proto" 9 10 cfg "github.com/tendermint/tendermint/config" 11 "github.com/tendermint/tendermint/libs/clist" 12 "github.com/tendermint/tendermint/libs/log" 13 cmtsync "github.com/tendermint/tendermint/libs/sync" 14 "github.com/tendermint/tendermint/mempool" 15 "github.com/tendermint/tendermint/p2p" 16 protomem "github.com/tendermint/tendermint/proto/tendermint/mempool" 17 "github.com/tendermint/tendermint/types" 18 ) 19 20 // Reactor handles mempool tx broadcasting amongst peers. 21 // It maintains a map from peer ID to counter, to prevent gossiping txs to the 22 // peers you received it from. 23 type Reactor struct { 24 p2p.BaseReactor 25 config *cfg.MempoolConfig 26 mempool *CListMempool 27 ids *mempoolIDs 28 } 29 30 type mempoolIDs struct { 31 mtx cmtsync.RWMutex 32 peerMap map[p2p.ID]uint16 33 nextID uint16 // assumes that a node will never have over 65536 active peers 34 activeIDs map[uint16]struct{} // used to check if a given peerID key is used, the value doesn't matter 35 } 36 37 // Reserve searches for the next unused ID and assigns it to the 38 // peer. 39 func (ids *mempoolIDs) ReserveForPeer(peer p2p.Peer) { 40 ids.mtx.Lock() 41 defer ids.mtx.Unlock() 42 43 curID := ids.nextPeerID() 44 ids.peerMap[peer.ID()] = curID 45 ids.activeIDs[curID] = struct{}{} 46 } 47 48 // nextPeerID returns the next unused peer ID to use. 49 // This assumes that ids's mutex is already locked. 50 func (ids *mempoolIDs) nextPeerID() uint16 { 51 if len(ids.activeIDs) == mempool.MaxActiveIDs { 52 panic(fmt.Sprintf("node has maximum %d active IDs and wanted to get one more", mempool.MaxActiveIDs)) 53 } 54 55 _, idExists := ids.activeIDs[ids.nextID] 56 for idExists { 57 ids.nextID++ 58 _, idExists = ids.activeIDs[ids.nextID] 59 } 60 curID := ids.nextID 61 ids.nextID++ 62 return curID 63 } 64 65 // Reclaim returns the ID reserved for the peer back to unused pool. 66 func (ids *mempoolIDs) Reclaim(peer p2p.Peer) { 67 ids.mtx.Lock() 68 defer ids.mtx.Unlock() 69 70 removedID, ok := ids.peerMap[peer.ID()] 71 if ok { 72 delete(ids.activeIDs, removedID) 73 delete(ids.peerMap, peer.ID()) 74 } 75 } 76 77 // GetForPeer returns an ID reserved for the peer. 78 func (ids *mempoolIDs) GetForPeer(peer p2p.Peer) uint16 { 79 ids.mtx.RLock() 80 defer ids.mtx.RUnlock() 81 82 return ids.peerMap[peer.ID()] 83 } 84 85 func newMempoolIDs() *mempoolIDs { 86 return &mempoolIDs{ 87 peerMap: make(map[p2p.ID]uint16), 88 activeIDs: map[uint16]struct{}{0: {}}, 89 nextID: 1, // reserve unknownPeerID(0) for mempoolReactor.BroadcastTx 90 } 91 } 92 93 // NewReactor returns a new Reactor with the given config and mempool. 94 func NewReactor(config *cfg.MempoolConfig, mempool *CListMempool) *Reactor { 95 memR := &Reactor{ 96 config: config, 97 mempool: mempool, 98 ids: newMempoolIDs(), 99 } 100 memR.BaseReactor = *p2p.NewBaseReactor("Mempool", memR) 101 return memR 102 } 103 104 // InitPeer implements Reactor by creating a state for the peer. 105 func (memR *Reactor) InitPeer(peer p2p.Peer) p2p.Peer { 106 memR.ids.ReserveForPeer(peer) 107 return peer 108 } 109 110 // SetLogger sets the Logger on the reactor and the underlying mempool. 111 func (memR *Reactor) SetLogger(l log.Logger) { 112 memR.Logger = l 113 memR.mempool.SetLogger(l) 114 } 115 116 // OnStart implements p2p.BaseReactor. 117 func (memR *Reactor) OnStart() error { 118 if !memR.config.Broadcast { 119 memR.Logger.Info("Tx broadcasting is disabled") 120 } 121 return nil 122 } 123 124 // GetChannels implements Reactor by returning the list of channels for this 125 // reactor. 126 func (memR *Reactor) GetChannels() []*p2p.ChannelDescriptor { 127 largestTx := make([]byte, memR.config.MaxTxBytes) 128 batchMsg := protomem.Message{ 129 Sum: &protomem.Message_Txs{ 130 Txs: &protomem.Txs{Txs: [][]byte{largestTx}}, 131 }, 132 } 133 134 return []*p2p.ChannelDescriptor{ 135 { 136 ID: mempool.MempoolChannel, 137 Priority: 5, 138 RecvMessageCapacity: batchMsg.Size(), 139 MessageType: &protomem.Message{}, 140 }, 141 } 142 } 143 144 // AddPeer implements Reactor. 145 // It starts a broadcast routine ensuring all txs are forwarded to the given peer. 146 func (memR *Reactor) AddPeer(peer p2p.Peer) { 147 if memR.config.Broadcast { 148 go memR.broadcastTxRoutine(peer) 149 } 150 } 151 152 // RemovePeer implements Reactor. 153 func (memR *Reactor) RemovePeer(peer p2p.Peer, reason interface{}) { 154 memR.ids.Reclaim(peer) 155 // broadcast routine checks if peer is gone and returns 156 } 157 158 // Receive implements Reactor. 159 // It adds any received transactions to the mempool. 160 func (memR *Reactor) ReceiveEnvelope(e p2p.Envelope) { 161 memR.Logger.Debug("Receive", "src", e.Src, "chId", e.ChannelID, "msg", e.Message) 162 switch msg := e.Message.(type) { 163 case *protomem.Txs: 164 protoTxs := msg.GetTxs() 165 if len(protoTxs) == 0 { 166 memR.Logger.Error("received empty txs from peer", "src", e.Src) 167 return 168 } 169 txInfo := mempool.TxInfo{SenderID: memR.ids.GetForPeer(e.Src)} 170 if e.Src != nil { 171 txInfo.SenderP2PID = e.Src.ID() 172 } 173 174 var err error 175 for _, tx := range protoTxs { 176 ntx := types.Tx(tx) 177 err = memR.mempool.CheckTx(ntx, nil, txInfo) 178 if errors.Is(err, mempool.ErrTxInCache) { 179 memR.Logger.Debug("Tx already exists in cache", "tx", ntx.String()) 180 } else if err != nil { 181 memR.Logger.Info("Could not check tx", "tx", ntx.String(), "err", err) 182 } 183 } 184 default: 185 memR.Logger.Error("unknown message type", "src", e.Src, "chId", e.ChannelID, "msg", e.Message) 186 memR.Switch.StopPeerForError(e.Src, fmt.Errorf("mempool cannot handle message of type: %T", e.Message)) 187 return 188 } 189 190 // broadcasting happens from go routines per peer 191 } 192 193 func (memR *Reactor) Receive(chID byte, peer p2p.Peer, msgBytes []byte) { 194 msg := &protomem.Message{} 195 err := proto.Unmarshal(msgBytes, msg) 196 if err != nil { 197 panic(err) 198 } 199 uw, err := msg.Unwrap() 200 if err != nil { 201 panic(err) 202 } 203 memR.ReceiveEnvelope(p2p.Envelope{ 204 ChannelID: chID, 205 Src: peer, 206 Message: uw, 207 }) 208 } 209 210 // PeerState describes the state of a peer. 211 type PeerState interface { 212 GetHeight() int64 213 } 214 215 // Send new mempool txs to peer. 216 func (memR *Reactor) broadcastTxRoutine(peer p2p.Peer) { 217 peerID := memR.ids.GetForPeer(peer) 218 var next *clist.CElement 219 220 for { 221 // In case of both next.NextWaitChan() and peer.Quit() are variable at the same time 222 if !memR.IsRunning() || !peer.IsRunning() { 223 return 224 } 225 // This happens because the CElement we were looking at got garbage 226 // collected (removed). That is, .NextWait() returned nil. Go ahead and 227 // start from the beginning. 228 if next == nil { 229 select { 230 case <-memR.mempool.TxsWaitChan(): // Wait until a tx is available 231 if next = memR.mempool.TxsFront(); next == nil { 232 continue 233 } 234 case <-peer.Quit(): 235 return 236 case <-memR.Quit(): 237 return 238 } 239 } 240 241 // Make sure the peer is up to date. 242 peerState, ok := peer.Get(types.PeerStateKey).(PeerState) 243 if !ok { 244 // Peer does not have a state yet. We set it in the consensus reactor, but 245 // when we add peer in Switch, the order we call reactors#AddPeer is 246 // different every time due to us using a map. Sometimes other reactors 247 // will be initialized before the consensus reactor. We should wait a few 248 // milliseconds and retry. 249 time.Sleep(mempool.PeerCatchupSleepIntervalMS * time.Millisecond) 250 continue 251 } 252 253 // Allow for a lag of 1 block. 254 memTx := next.Value.(*mempoolTx) 255 if peerState.GetHeight() < memTx.Height()-1 { 256 time.Sleep(mempool.PeerCatchupSleepIntervalMS * time.Millisecond) 257 continue 258 } 259 260 // NOTE: Transaction batching was disabled due to 261 // https://github.com/tendermint/tendermint/issues/5796 262 263 if _, ok := memTx.senders.Load(peerID); !ok { 264 success := p2p.SendEnvelopeShim(peer, p2p.Envelope{ //nolint: staticcheck 265 ChannelID: mempool.MempoolChannel, 266 Message: &protomem.Txs{Txs: [][]byte{memTx.tx}}, 267 }, memR.Logger) 268 if !success { 269 time.Sleep(mempool.PeerCatchupSleepIntervalMS * time.Millisecond) 270 continue 271 } 272 } 273 274 select { 275 case <-next.NextWaitChan(): 276 // see the start of the for loop for nil check 277 next = next.Next() 278 case <-peer.Quit(): 279 return 280 case <-memR.Quit(): 281 return 282 } 283 } 284 } 285 286 // TxsMessage is a Message containing transactions. 287 type TxsMessage struct { 288 Txs []types.Tx 289 } 290 291 // String returns a string representation of the TxsMessage. 292 func (m *TxsMessage) String() string { 293 return fmt.Sprintf("[TxsMessage %v]", m.Txs) 294 }