github.com/core-coin/go-core/v2@v2.1.9/les/txrelay.go (about)

     1  // Copyright 2016 by the Authors
     2  // This file is part of the go-core library.
     3  //
     4  // The go-core 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-core 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-core library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package les
    18  
    19  import (
    20  	"context"
    21  	"sync"
    22  
    23  	"github.com/core-coin/go-core/v2/common"
    24  	"github.com/core-coin/go-core/v2/core/types"
    25  	"github.com/core-coin/go-core/v2/rlp"
    26  )
    27  
    28  type ltrInfo struct {
    29  	tx     *types.Transaction
    30  	sentTo map[*serverPeer]struct{}
    31  }
    32  
    33  type lesTxRelay struct {
    34  	txSent       map[common.Hash]*ltrInfo
    35  	txPending    map[common.Hash]struct{}
    36  	peerList     []*serverPeer
    37  	peerStartPos int
    38  	lock         sync.Mutex
    39  	stop         chan struct{}
    40  
    41  	retriever *retrieveManager
    42  }
    43  
    44  func newLesTxRelay(ps *serverPeerSet, retriever *retrieveManager) *lesTxRelay {
    45  	r := &lesTxRelay{
    46  		txSent:    make(map[common.Hash]*ltrInfo),
    47  		txPending: make(map[common.Hash]struct{}),
    48  		retriever: retriever,
    49  		stop:      make(chan struct{}),
    50  	}
    51  	ps.subscribe(r)
    52  	return r
    53  }
    54  
    55  func (ltrx *lesTxRelay) Stop() {
    56  	close(ltrx.stop)
    57  }
    58  
    59  func (ltrx *lesTxRelay) registerPeer(p *serverPeer) {
    60  	ltrx.lock.Lock()
    61  	defer ltrx.lock.Unlock()
    62  
    63  	// Short circuit if the peer is announce only.
    64  	if p.onlyAnnounce {
    65  		return
    66  	}
    67  	ltrx.peerList = append(ltrx.peerList, p)
    68  }
    69  
    70  func (ltrx *lesTxRelay) unregisterPeer(p *serverPeer) {
    71  	ltrx.lock.Lock()
    72  	defer ltrx.lock.Unlock()
    73  
    74  	for i, peer := range ltrx.peerList {
    75  		if peer == p {
    76  			// Remove from the peer list
    77  			ltrx.peerList = append(ltrx.peerList[:i], ltrx.peerList[i+1:]...)
    78  			return
    79  		}
    80  	}
    81  }
    82  
    83  // send sends a list of transactions to at most a given number of peers at
    84  // once, never resending any particular transaction to the same peer twice
    85  func (ltrx *lesTxRelay) send(txs types.Transactions, count int) {
    86  	sendTo := make(map[*serverPeer]types.Transactions)
    87  
    88  	ltrx.peerStartPos++ // rotate the starting position of the peer list
    89  	if ltrx.peerStartPos >= len(ltrx.peerList) {
    90  		ltrx.peerStartPos = 0
    91  	}
    92  
    93  	for _, tx := range txs {
    94  		hash := tx.Hash()
    95  		ltr, ok := ltrx.txSent[hash]
    96  		if !ok {
    97  			ltr = &ltrInfo{
    98  				tx:     tx,
    99  				sentTo: make(map[*serverPeer]struct{}),
   100  			}
   101  			ltrx.txSent[hash] = ltr
   102  			ltrx.txPending[hash] = struct{}{}
   103  		}
   104  
   105  		if len(ltrx.peerList) > 0 {
   106  			cnt := count
   107  			pos := ltrx.peerStartPos
   108  			for {
   109  				peer := ltrx.peerList[pos]
   110  				if _, ok := ltr.sentTo[peer]; !ok {
   111  					sendTo[peer] = append(sendTo[peer], tx)
   112  					ltr.sentTo[peer] = struct{}{}
   113  					cnt--
   114  				}
   115  				if cnt == 0 {
   116  					break // sent it to the desired number of peers
   117  				}
   118  				pos++
   119  				if pos == len(ltrx.peerList) {
   120  					pos = 0
   121  				}
   122  				if pos == ltrx.peerStartPos {
   123  					break // tried all available peers
   124  				}
   125  			}
   126  		}
   127  	}
   128  
   129  	for p, list := range sendTo {
   130  		pp := p
   131  		ll := list
   132  		enc, _ := rlp.EncodeToBytes(ll)
   133  
   134  		reqID := genReqID()
   135  		rq := &distReq{
   136  			getCost: func(dp distPeer) uint64 {
   137  				peer := dp.(*serverPeer)
   138  				return peer.getTxRelayCost(len(ll), len(enc))
   139  			},
   140  			canSend: func(dp distPeer) bool {
   141  				return !dp.(*serverPeer).onlyAnnounce && dp.(*serverPeer) == pp
   142  			},
   143  			request: func(dp distPeer) func() {
   144  				peer := dp.(*serverPeer)
   145  				cost := peer.getTxRelayCost(len(ll), len(enc))
   146  				peer.fcServer.QueuedRequest(reqID, cost)
   147  				return func() { peer.sendTxs(reqID, len(ll), enc) }
   148  			},
   149  		}
   150  		go ltrx.retriever.retrieve(context.Background(), reqID, rq, func(p distPeer, msg *Msg) error { return nil }, ltrx.stop)
   151  	}
   152  }
   153  
   154  func (ltrx *lesTxRelay) Send(txs types.Transactions) {
   155  	ltrx.lock.Lock()
   156  	defer ltrx.lock.Unlock()
   157  
   158  	ltrx.send(txs, 3)
   159  }
   160  
   161  func (ltrx *lesTxRelay) NewHead(head common.Hash, mined []common.Hash, rollback []common.Hash) {
   162  	ltrx.lock.Lock()
   163  	defer ltrx.lock.Unlock()
   164  
   165  	for _, hash := range mined {
   166  		delete(ltrx.txPending, hash)
   167  	}
   168  
   169  	for _, hash := range rollback {
   170  		ltrx.txPending[hash] = struct{}{}
   171  	}
   172  
   173  	if len(ltrx.txPending) > 0 {
   174  		txs := make(types.Transactions, len(ltrx.txPending))
   175  		i := 0
   176  		for hash := range ltrx.txPending {
   177  			txs[i] = ltrx.txSent[hash].tx
   178  			i++
   179  		}
   180  		ltrx.send(txs, 1)
   181  	}
   182  }
   183  
   184  func (ltrx *lesTxRelay) Discard(hashes []common.Hash) {
   185  	ltrx.lock.Lock()
   186  	defer ltrx.lock.Unlock()
   187  
   188  	for _, hash := range hashes {
   189  		delete(ltrx.txSent, hash)
   190  		delete(ltrx.txPending, hash)
   191  	}
   192  }