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