github.com/jimmyx0x/go-ethereum@v1.10.28/les/odr.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  	"sort"
    23  	"time"
    24  
    25  	"github.com/ethereum/go-ethereum/common/mclock"
    26  	"github.com/ethereum/go-ethereum/core"
    27  	"github.com/ethereum/go-ethereum/core/txpool"
    28  	"github.com/ethereum/go-ethereum/ethdb"
    29  	"github.com/ethereum/go-ethereum/light"
    30  )
    31  
    32  // LesOdr implements light.OdrBackend
    33  type LesOdr struct {
    34  	db                                         ethdb.Database
    35  	indexerConfig                              *light.IndexerConfig
    36  	chtIndexer, bloomTrieIndexer, bloomIndexer *core.ChainIndexer
    37  	peers                                      *serverPeerSet
    38  	retriever                                  *retrieveManager
    39  	stop                                       chan struct{}
    40  }
    41  
    42  func NewLesOdr(db ethdb.Database, config *light.IndexerConfig, peers *serverPeerSet, retriever *retrieveManager) *LesOdr {
    43  	return &LesOdr{
    44  		db:            db,
    45  		indexerConfig: config,
    46  		peers:         peers,
    47  		retriever:     retriever,
    48  		stop:          make(chan struct{}),
    49  	}
    50  }
    51  
    52  // Stop cancels all pending retrievals
    53  func (odr *LesOdr) Stop() {
    54  	close(odr.stop)
    55  }
    56  
    57  // Database returns the backing database
    58  func (odr *LesOdr) Database() ethdb.Database {
    59  	return odr.db
    60  }
    61  
    62  // SetIndexers adds the necessary chain indexers to the ODR backend
    63  func (odr *LesOdr) SetIndexers(chtIndexer, bloomTrieIndexer, bloomIndexer *core.ChainIndexer) {
    64  	odr.chtIndexer = chtIndexer
    65  	odr.bloomTrieIndexer = bloomTrieIndexer
    66  	odr.bloomIndexer = bloomIndexer
    67  }
    68  
    69  // ChtIndexer returns the CHT chain indexer
    70  func (odr *LesOdr) ChtIndexer() *core.ChainIndexer {
    71  	return odr.chtIndexer
    72  }
    73  
    74  // BloomTrieIndexer returns the bloom trie chain indexer
    75  func (odr *LesOdr) BloomTrieIndexer() *core.ChainIndexer {
    76  	return odr.bloomTrieIndexer
    77  }
    78  
    79  // BloomIndexer returns the bloombits chain indexer
    80  func (odr *LesOdr) BloomIndexer() *core.ChainIndexer {
    81  	return odr.bloomIndexer
    82  }
    83  
    84  // IndexerConfig returns the indexer config.
    85  func (odr *LesOdr) IndexerConfig() *light.IndexerConfig {
    86  	return odr.indexerConfig
    87  }
    88  
    89  const (
    90  	MsgBlockHeaders = iota
    91  	MsgBlockBodies
    92  	MsgCode
    93  	MsgReceipts
    94  	MsgProofsV2
    95  	MsgHelperTrieProofs
    96  	MsgTxStatus
    97  )
    98  
    99  // Msg encodes a LES message that delivers reply data for a request
   100  type Msg struct {
   101  	MsgType int
   102  	ReqID   uint64
   103  	Obj     interface{}
   104  }
   105  
   106  // peerByTxHistory is a heap.Interface implementation which can sort
   107  // the peerset by transaction history.
   108  type peerByTxHistory []*serverPeer
   109  
   110  func (h peerByTxHistory) Len() int { return len(h) }
   111  func (h peerByTxHistory) Less(i, j int) bool {
   112  	if h[i].txHistory == txIndexUnlimited {
   113  		return false
   114  	}
   115  	if h[j].txHistory == txIndexUnlimited {
   116  		return true
   117  	}
   118  	return h[i].txHistory < h[j].txHistory
   119  }
   120  func (h peerByTxHistory) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
   121  
   122  const (
   123  	maxTxStatusRetry      = 3 // The maximum retries will be made for tx status request.
   124  	maxTxStatusCandidates = 5 // The maximum les servers the tx status requests will be sent to.
   125  )
   126  
   127  // RetrieveTxStatus retrieves the transaction status from the LES network.
   128  // There is no guarantee in the LES protocol that the mined transaction will
   129  // be retrieved back for sure because of different reasons(the transaction
   130  // is unindexed, the malicious server doesn't reply it deliberately, etc).
   131  // Therefore, unretrieved transactions(UNKNOWN) will receive a certain number
   132  // of retries, thus giving a weak guarantee.
   133  func (odr *LesOdr) RetrieveTxStatus(ctx context.Context, req *light.TxStatusRequest) error {
   134  	// Sort according to the transaction history supported by the peer and
   135  	// select the peers with longest history.
   136  	var (
   137  		retries int
   138  		peers   []*serverPeer
   139  		missing = len(req.Hashes)
   140  		result  = make([]light.TxStatus, len(req.Hashes))
   141  		canSend = make(map[string]bool)
   142  	)
   143  	for _, peer := range odr.peers.allPeers() {
   144  		if peer.txHistory == txIndexDisabled {
   145  			continue
   146  		}
   147  		peers = append(peers, peer)
   148  	}
   149  	sort.Sort(sort.Reverse(peerByTxHistory(peers)))
   150  	for i := 0; i < maxTxStatusCandidates && i < len(peers); i++ {
   151  		canSend[peers[i].id] = true
   152  	}
   153  	// Send out the request and assemble the result.
   154  	for {
   155  		if retries >= maxTxStatusRetry || len(canSend) == 0 {
   156  			break
   157  		}
   158  		var (
   159  			// Deep copy the request, so that the partial result won't be mixed.
   160  			req     = &TxStatusRequest{Hashes: req.Hashes}
   161  			id      = rand.Uint64()
   162  			distreq = &distReq{
   163  				getCost: func(dp distPeer) uint64 { return req.GetCost(dp.(*serverPeer)) },
   164  				canSend: func(dp distPeer) bool { return canSend[dp.(*serverPeer).id] },
   165  				request: func(dp distPeer) func() {
   166  					p := dp.(*serverPeer)
   167  					p.fcServer.QueuedRequest(id, req.GetCost(p))
   168  					delete(canSend, p.id)
   169  					return func() { req.Request(id, p) }
   170  				},
   171  			}
   172  		)
   173  		if err := odr.retriever.retrieve(ctx, id, distreq, func(p distPeer, msg *Msg) error { return req.Validate(odr.db, msg) }, odr.stop); err != nil {
   174  			return err
   175  		}
   176  		// Collect the response and assemble them to the final result.
   177  		// All the response is not verifiable, so always pick the first
   178  		// one we get.
   179  		for index, status := range req.Status {
   180  			if result[index].Status != txpool.TxStatusUnknown {
   181  				continue
   182  			}
   183  			if status.Status == txpool.TxStatusUnknown {
   184  				continue
   185  			}
   186  			result[index], missing = status, missing-1
   187  		}
   188  		// Abort the procedure if all the status are retrieved
   189  		if missing == 0 {
   190  			break
   191  		}
   192  		retries += 1
   193  	}
   194  	req.Status = result
   195  	return nil
   196  }
   197  
   198  // Retrieve tries to fetch an object from the LES network. It's a common API
   199  // for most of the LES requests except for the TxStatusRequest which needs
   200  // the additional retry mechanism.
   201  // If the network retrieval was successful, it stores the object in local db.
   202  func (odr *LesOdr) Retrieve(ctx context.Context, req light.OdrRequest) (err error) {
   203  	lreq := LesRequest(req)
   204  
   205  	reqID := rand.Uint64()
   206  	rq := &distReq{
   207  		getCost: func(dp distPeer) uint64 {
   208  			return lreq.GetCost(dp.(*serverPeer))
   209  		},
   210  		canSend: func(dp distPeer) bool {
   211  			p := dp.(*serverPeer)
   212  			if !p.onlyAnnounce {
   213  				return lreq.CanSend(p)
   214  			}
   215  			return false
   216  		},
   217  		request: func(dp distPeer) func() {
   218  			p := dp.(*serverPeer)
   219  			cost := lreq.GetCost(p)
   220  			p.fcServer.QueuedRequest(reqID, cost)
   221  			return func() { lreq.Request(reqID, p) }
   222  		},
   223  	}
   224  
   225  	defer func(sent mclock.AbsTime) {
   226  		if err != nil {
   227  			return
   228  		}
   229  		requestRTT.Update(time.Duration(mclock.Now() - sent))
   230  	}(mclock.Now())
   231  
   232  	if err := odr.retriever.retrieve(ctx, reqID, rq, func(p distPeer, msg *Msg) error { return lreq.Validate(odr.db, msg) }, odr.stop); err != nil {
   233  		return err
   234  	}
   235  	req.StoreResult(odr.db)
   236  	return nil
   237  }