github.com/ledgerwatch/erigon-lib@v1.0.0/txpool/send.go (about) 1 /* 2 Copyright 2021 Erigon contributors 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package txpool 18 19 import ( 20 "context" 21 "fmt" 22 "sync" 23 24 "github.com/ledgerwatch/erigon-lib/direct" 25 "github.com/ledgerwatch/erigon-lib/gointerfaces/sentry" 26 "github.com/ledgerwatch/erigon-lib/rlp" 27 types2 "github.com/ledgerwatch/erigon-lib/types" 28 "github.com/ledgerwatch/log/v3" 29 "google.golang.org/grpc" 30 ) 31 32 type SentryClient interface { 33 sentry.SentryClient 34 Protocol() uint 35 } 36 37 // Send - does send concrete P2P messages to Sentry. Same as Fetch but for outbound traffic 38 // does not initiate any messages by self 39 type Send struct { 40 ctx context.Context 41 pool Pool 42 wg *sync.WaitGroup 43 sentryClients []direct.SentryClient // sentry clients that will be used for accessing the network 44 logger log.Logger 45 } 46 47 func NewSend(ctx context.Context, sentryClients []direct.SentryClient, pool Pool, logger log.Logger) *Send { 48 return &Send{ 49 ctx: ctx, 50 pool: pool, 51 sentryClients: sentryClients, 52 logger: logger, 53 } 54 } 55 56 func (f *Send) SetWaitGroup(wg *sync.WaitGroup) { 57 f.wg = wg 58 } 59 60 const ( 61 // This is the target size for the packs of transactions or announcements. A 62 // pack can get larger than this if a single transactions exceeds this size. 63 p2pTxPacketLimit = 100 * 1024 64 ) 65 66 func (f *Send) notifyTests() { 67 if f.wg != nil { 68 f.wg.Done() 69 } 70 } 71 72 // Broadcast given RLPs to random peers 73 func (f *Send) BroadcastPooledTxs(rlps [][]byte) (txSentTo []int) { 74 defer f.notifyTests() 75 if len(rlps) == 0 { 76 return 77 } 78 txSentTo = make([]int, len(rlps)) 79 var prev, size int 80 for i, l := 0, len(rlps); i < len(rlps); i++ { 81 size += len(rlps[i]) 82 // Wait till the combined size of rlps so far is greater than a threshold and 83 // send them all at once. Then wait till end of array or this threshold hits again 84 if i == l-1 || size >= p2pTxPacketLimit { 85 txsData := types2.EncodeTransactions(rlps[prev:i+1], nil) 86 var txs66 *sentry.SendMessageToRandomPeersRequest 87 for _, sentryClient := range f.sentryClients { 88 if !sentryClient.Ready() { 89 continue 90 } 91 if txs66 == nil { 92 txs66 = &sentry.SendMessageToRandomPeersRequest{ 93 Data: &sentry.OutboundMessageData{ 94 Id: sentry.MessageId_TRANSACTIONS_66, 95 Data: txsData, 96 }, 97 MaxPeers: 100, 98 } 99 } 100 peers, err := sentryClient.SendMessageToRandomPeers(f.ctx, txs66) 101 if err != nil { 102 f.logger.Debug("[txpool.send] BroadcastPooledTxs", "err", err) 103 } 104 if peers != nil { 105 for j := prev; j <= i; j++ { 106 txSentTo[j] = len(peers.Peers) 107 } 108 } 109 } 110 prev = i + 1 111 size = 0 112 } 113 } 114 return 115 } 116 117 func (f *Send) AnnouncePooledTxs(types []byte, sizes []uint32, hashes types2.Hashes) (hashSentTo []int) { 118 defer f.notifyTests() 119 hashSentTo = make([]int, len(types)) 120 if len(types) == 0 { 121 return 122 } 123 prevI := 0 124 prevJ := 0 125 for prevI < len(hashes) || prevJ < len(types) { 126 // Prepare two versions of the announcement message, one for pre-eth/68 peers, another for post-eth/68 peers 127 i := prevI 128 for i < len(hashes) && rlp.HashesLen(hashes[prevI:i+32]) < p2pTxPacketLimit { 129 i += 32 130 } 131 j := prevJ 132 for j < len(types) && rlp.AnnouncementsLen(types[prevJ:j+1], sizes[prevJ:j+1], hashes[32*prevJ:32*j+32]) < p2pTxPacketLimit { 133 j++ 134 } 135 iSize := rlp.HashesLen(hashes[prevI:i]) 136 jSize := rlp.AnnouncementsLen(types[prevJ:j], sizes[prevJ:j], hashes[32*prevJ:32*j]) 137 iData := make([]byte, iSize) 138 jData := make([]byte, jSize) 139 if s := rlp.EncodeHashes(hashes[prevI:i], iData); s != iSize { 140 panic(fmt.Sprintf("Serialised hashes encoding len mismatch, expected %d, got %d", iSize, s)) 141 } 142 if s := rlp.EncodeAnnouncements(types[prevJ:j], sizes[prevJ:j], hashes[32*prevJ:32*j], jData); s != jSize { 143 panic(fmt.Sprintf("Serialised announcements encoding len mismatch, expected %d, got %d", jSize, s)) 144 } 145 for _, sentryClient := range f.sentryClients { 146 if !sentryClient.Ready() { 147 continue 148 } 149 switch sentryClient.Protocol() { 150 case direct.ETH66, direct.ETH67: 151 if i > prevI { 152 req := &sentry.OutboundMessageData{ 153 Id: sentry.MessageId_NEW_POOLED_TRANSACTION_HASHES_66, 154 Data: iData, 155 } 156 peers, err := sentryClient.SendMessageToAll(f.ctx, req, &grpc.EmptyCallOption{}) 157 if err != nil { 158 f.logger.Debug("[txpool.send] AnnouncePooledTxs", "err", err) 159 } 160 if peers != nil { 161 for k := prevI; k < i; k += 32 { 162 hashSentTo[k/32] += len(peers.Peers) 163 } 164 } 165 } 166 case direct.ETH68: 167 168 if j > prevJ { 169 req := &sentry.OutboundMessageData{ 170 Id: sentry.MessageId_NEW_POOLED_TRANSACTION_HASHES_68, 171 Data: jData, 172 } 173 peers, err := sentryClient.SendMessageToAll(f.ctx, req, &grpc.EmptyCallOption{}) 174 if err != nil { 175 f.logger.Debug("[txpool.send] AnnouncePooledTxs68", "err", err) 176 } 177 if peers != nil { 178 for k := prevJ; k < j; k++ { 179 hashSentTo[k] += len(peers.Peers) 180 } 181 } 182 } 183 184 } 185 } 186 prevI = i 187 prevJ = j 188 } 189 return 190 } 191 192 func (f *Send) PropagatePooledTxsToPeersList(peers []types2.PeerID, types []byte, sizes []uint32, hashes []byte) { 193 defer f.notifyTests() 194 195 if len(types) == 0 { 196 return 197 } 198 199 prevI := 0 200 prevJ := 0 201 for prevI < len(hashes) || prevJ < len(types) { 202 // Prepare two versions of the annoucement message, one for pre-eth/68 peers, another for post-eth/68 peers 203 i := prevI 204 for i < len(hashes) && rlp.HashesLen(hashes[prevI:i+32]) < p2pTxPacketLimit { 205 i += 32 206 } 207 j := prevJ 208 for j < len(types) && rlp.AnnouncementsLen(types[prevJ:j+1], sizes[prevJ:j+1], hashes[32*prevJ:32*j+32]) < p2pTxPacketLimit { 209 j++ 210 } 211 iSize := rlp.HashesLen(hashes[prevI:i]) 212 jSize := rlp.AnnouncementsLen(types[prevJ:j], sizes[prevJ:j], hashes[32*prevJ:32*j]) 213 iData := make([]byte, iSize) 214 jData := make([]byte, jSize) 215 if s := rlp.EncodeHashes(hashes[prevI:i], iData); s != iSize { 216 panic(fmt.Sprintf("Serialised hashes encoding len mismatch, expected %d, got %d", iSize, s)) 217 } 218 if s := rlp.EncodeAnnouncements(types[prevJ:j], sizes[prevJ:j], hashes[32*prevJ:32*j], jData); s != jSize { 219 panic(fmt.Sprintf("Serialised annoucements encoding len mismatch, expected %d, got %d", jSize, s)) 220 } 221 222 for _, sentryClient := range f.sentryClients { 223 if !sentryClient.Ready() { 224 continue 225 } 226 227 for _, peer := range peers { 228 switch sentryClient.Protocol() { 229 case direct.ETH66, direct.ETH67: 230 if i > prevI { 231 req := &sentry.SendMessageByIdRequest{ 232 PeerId: peer, 233 Data: &sentry.OutboundMessageData{ 234 Id: sentry.MessageId_NEW_POOLED_TRANSACTION_HASHES_66, 235 Data: iData, 236 }, 237 } 238 if _, err := sentryClient.SendMessageById(f.ctx, req, &grpc.EmptyCallOption{}); err != nil { 239 f.logger.Debug("[txpool.send] PropagatePooledTxsToPeersList", "err", err) 240 } 241 } 242 case direct.ETH68: 243 244 if j > prevJ { 245 req := &sentry.SendMessageByIdRequest{ 246 PeerId: peer, 247 Data: &sentry.OutboundMessageData{ 248 Id: sentry.MessageId_NEW_POOLED_TRANSACTION_HASHES_68, 249 Data: jData, 250 }, 251 } 252 if _, err := sentryClient.SendMessageById(f.ctx, req, &grpc.EmptyCallOption{}); err != nil { 253 f.logger.Debug("[txpool.send] PropagatePooledTxsToPeersList68", "err", err) 254 } 255 } 256 257 } 258 } 259 } 260 prevI = i 261 prevJ = j 262 } 263 }