github.com/tirogen/go-ethereum@v1.10.12-0.20221226051715-250cfede41b6/les/txrelay.go (about)

     1  // Copyright 2016 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-ethereum library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package les
    18  
    19  import (
    20  	"context"
    21  	"math/rand"
    22  	"sync"
    23  
    24  	"github.com/tirogen/go-ethereum/common"
    25  	"github.com/tirogen/go-ethereum/core/types"
    26  	"github.com/tirogen/go-ethereum/rlp"
    27  )
    28  
    29  type lesTxRelay struct {
    30  	txSent       map[common.Hash]*types.Transaction
    31  	txPending    map[common.Hash]struct{}
    32  	peerList     []*serverPeer
    33  	peerStartPos int
    34  	lock         sync.Mutex
    35  	stop         chan struct{}
    36  
    37  	retriever *retrieveManager
    38  }
    39  
    40  func newLesTxRelay(ps *serverPeerSet, retriever *retrieveManager) *lesTxRelay {
    41  	r := &lesTxRelay{
    42  		txSent:    make(map[common.Hash]*types.Transaction),
    43  		txPending: make(map[common.Hash]struct{}),
    44  		retriever: retriever,
    45  		stop:      make(chan struct{}),
    46  	}
    47  	ps.subscribe(r)
    48  	return r
    49  }
    50  
    51  func (ltrx *lesTxRelay) Stop() {
    52  	close(ltrx.stop)
    53  }
    54  
    55  func (ltrx *lesTxRelay) registerPeer(p *serverPeer) {
    56  	ltrx.lock.Lock()
    57  	defer ltrx.lock.Unlock()
    58  
    59  	// Short circuit if the peer is announce only.
    60  	if p.onlyAnnounce {
    61  		return
    62  	}
    63  	ltrx.peerList = append(ltrx.peerList, p)
    64  }
    65  
    66  func (ltrx *lesTxRelay) unregisterPeer(p *serverPeer) {
    67  	ltrx.lock.Lock()
    68  	defer ltrx.lock.Unlock()
    69  
    70  	for i, peer := range ltrx.peerList {
    71  		if peer == p {
    72  			// Remove from the peer list
    73  			ltrx.peerList = append(ltrx.peerList[:i], ltrx.peerList[i+1:]...)
    74  			return
    75  		}
    76  	}
    77  }
    78  
    79  // send sends a list of transactions to at most a given number of peers.
    80  func (ltrx *lesTxRelay) send(txs types.Transactions, count int) {
    81  	sendTo := make(map[*serverPeer]types.Transactions)
    82  
    83  	ltrx.peerStartPos++ // rotate the starting position of the peer list
    84  	if ltrx.peerStartPos >= len(ltrx.peerList) {
    85  		ltrx.peerStartPos = 0
    86  	}
    87  
    88  	for _, tx := range txs {
    89  		hash := tx.Hash()
    90  		_, ok := ltrx.txSent[hash]
    91  		if !ok {
    92  			ltrx.txSent[hash] = tx
    93  			ltrx.txPending[hash] = struct{}{}
    94  		}
    95  		if len(ltrx.peerList) > 0 {
    96  			cnt := count
    97  			pos := ltrx.peerStartPos
    98  			for {
    99  				peer := ltrx.peerList[pos]
   100  				sendTo[peer] = append(sendTo[peer], tx)
   101  				cnt--
   102  				if cnt == 0 {
   103  					break // sent it to the desired number of peers
   104  				}
   105  				pos++
   106  				if pos == len(ltrx.peerList) {
   107  					pos = 0
   108  				}
   109  				if pos == ltrx.peerStartPos {
   110  					break // tried all available peers
   111  				}
   112  			}
   113  		}
   114  	}
   115  
   116  	for p, list := range sendTo {
   117  		pp := p
   118  		ll := list
   119  		enc, _ := rlp.EncodeToBytes(ll)
   120  
   121  		reqID := rand.Uint64()
   122  		rq := &distReq{
   123  			getCost: func(dp distPeer) uint64 {
   124  				peer := dp.(*serverPeer)
   125  				return peer.getTxRelayCost(len(ll), len(enc))
   126  			},
   127  			canSend: func(dp distPeer) bool {
   128  				return !dp.(*serverPeer).onlyAnnounce && dp.(*serverPeer) == pp
   129  			},
   130  			request: func(dp distPeer) func() {
   131  				peer := dp.(*serverPeer)
   132  				cost := peer.getTxRelayCost(len(ll), len(enc))
   133  				peer.fcServer.QueuedRequest(reqID, cost)
   134  				return func() { peer.sendTxs(reqID, len(ll), enc) }
   135  			},
   136  		}
   137  		go ltrx.retriever.retrieve(context.Background(), reqID, rq, func(p distPeer, msg *Msg) error { return nil }, ltrx.stop)
   138  	}
   139  }
   140  
   141  func (ltrx *lesTxRelay) Send(txs types.Transactions) {
   142  	ltrx.lock.Lock()
   143  	defer ltrx.lock.Unlock()
   144  
   145  	ltrx.send(txs, 3)
   146  }
   147  
   148  func (ltrx *lesTxRelay) NewHead(head common.Hash, mined []common.Hash, rollback []common.Hash) {
   149  	ltrx.lock.Lock()
   150  	defer ltrx.lock.Unlock()
   151  
   152  	for _, hash := range mined {
   153  		delete(ltrx.txPending, hash)
   154  	}
   155  
   156  	for _, hash := range rollback {
   157  		ltrx.txPending[hash] = struct{}{}
   158  	}
   159  
   160  	if len(ltrx.txPending) > 0 {
   161  		txs := make(types.Transactions, len(ltrx.txPending))
   162  		i := 0
   163  		for hash := range ltrx.txPending {
   164  			txs[i] = ltrx.txSent[hash]
   165  			i++
   166  		}
   167  		ltrx.send(txs, 1)
   168  	}
   169  }
   170  
   171  func (ltrx *lesTxRelay) Discard(hashes []common.Hash) {
   172  	ltrx.lock.Lock()
   173  	defer ltrx.lock.Unlock()
   174  
   175  	for _, hash := range hashes {
   176  		delete(ltrx.txSent, hash)
   177  		delete(ltrx.txPending, hash)
   178  	}
   179  }