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  }