github.com/jimmyx0x/go-ethereum@v1.10.28/les/server_handler.go (about)

     1  // Copyright 2019 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  	"errors"
    21  	"sync"
    22  	"sync/atomic"
    23  	"time"
    24  
    25  	"github.com/ethereum/go-ethereum/common"
    26  	"github.com/ethereum/go-ethereum/common/mclock"
    27  	"github.com/ethereum/go-ethereum/core"
    28  	"github.com/ethereum/go-ethereum/core/forkid"
    29  	"github.com/ethereum/go-ethereum/core/rawdb"
    30  	"github.com/ethereum/go-ethereum/core/txpool"
    31  	"github.com/ethereum/go-ethereum/core/types"
    32  	"github.com/ethereum/go-ethereum/ethdb"
    33  	"github.com/ethereum/go-ethereum/les/flowcontrol"
    34  	"github.com/ethereum/go-ethereum/light"
    35  	"github.com/ethereum/go-ethereum/log"
    36  	"github.com/ethereum/go-ethereum/metrics"
    37  	"github.com/ethereum/go-ethereum/p2p"
    38  	"github.com/ethereum/go-ethereum/rlp"
    39  	"github.com/ethereum/go-ethereum/trie"
    40  )
    41  
    42  const (
    43  	softResponseLimit = 2 * 1024 * 1024 // Target maximum size of returned blocks, headers or node data.
    44  	estHeaderRlpSize  = 500             // Approximate size of an RLP encoded block header
    45  
    46  	MaxHeaderFetch           = 192 // Amount of block headers to be fetched per retrieval request
    47  	MaxBodyFetch             = 32  // Amount of block bodies to be fetched per retrieval request
    48  	MaxReceiptFetch          = 128 // Amount of transaction receipts to allow fetching per request
    49  	MaxCodeFetch             = 64  // Amount of contract codes to allow fetching per request
    50  	MaxProofsFetch           = 64  // Amount of merkle proofs to be fetched per retrieval request
    51  	MaxHelperTrieProofsFetch = 64  // Amount of helper tries to be fetched per retrieval request
    52  	MaxTxSend                = 64  // Amount of transactions to be send per request
    53  	MaxTxStatus              = 256 // Amount of transactions to queried per request
    54  )
    55  
    56  var (
    57  	errTooManyInvalidRequest = errors.New("too many invalid requests made")
    58  )
    59  
    60  // serverHandler is responsible for serving light client and process
    61  // all incoming light requests.
    62  type serverHandler struct {
    63  	forkFilter forkid.Filter
    64  	blockchain *core.BlockChain
    65  	chainDb    ethdb.Database
    66  	txpool     *txpool.TxPool
    67  	server     *LesServer
    68  
    69  	closeCh chan struct{}  // Channel used to exit all background routines of handler.
    70  	wg      sync.WaitGroup // WaitGroup used to track all background routines of handler.
    71  	synced  func() bool    // Callback function used to determine whether local node is synced.
    72  
    73  	// Testing fields
    74  	addTxsSync bool
    75  }
    76  
    77  func newServerHandler(server *LesServer, blockchain *core.BlockChain, chainDb ethdb.Database, txpool *txpool.TxPool, synced func() bool) *serverHandler {
    78  	handler := &serverHandler{
    79  		forkFilter: forkid.NewFilter(blockchain),
    80  		server:     server,
    81  		blockchain: blockchain,
    82  		chainDb:    chainDb,
    83  		txpool:     txpool,
    84  		closeCh:    make(chan struct{}),
    85  		synced:     synced,
    86  	}
    87  	return handler
    88  }
    89  
    90  // start starts the server handler.
    91  func (h *serverHandler) start() {
    92  	h.wg.Add(1)
    93  	go h.broadcastLoop()
    94  }
    95  
    96  // stop stops the server handler.
    97  func (h *serverHandler) stop() {
    98  	close(h.closeCh)
    99  	h.wg.Wait()
   100  }
   101  
   102  // runPeer is the p2p protocol run function for the given version.
   103  func (h *serverHandler) runPeer(version uint, p *p2p.Peer, rw p2p.MsgReadWriter) error {
   104  	peer := newClientPeer(int(version), h.server.config.NetworkId, p, newMeteredMsgWriter(rw, int(version)))
   105  	defer peer.close()
   106  	h.wg.Add(1)
   107  	defer h.wg.Done()
   108  	return h.handle(peer)
   109  }
   110  
   111  func (h *serverHandler) handle(p *clientPeer) error {
   112  	p.Log().Debug("Light Ethereum peer connected", "name", p.Name())
   113  
   114  	// Execute the LES handshake
   115  	var (
   116  		head   = h.blockchain.CurrentHeader()
   117  		hash   = head.Hash()
   118  		number = head.Number.Uint64()
   119  		td     = h.blockchain.GetTd(hash, number)
   120  		forkID = forkid.NewID(h.blockchain.Config(), h.blockchain.Genesis().Hash(), number, head.Time)
   121  	)
   122  	if err := p.Handshake(td, hash, number, h.blockchain.Genesis().Hash(), forkID, h.forkFilter, h.server); err != nil {
   123  		p.Log().Debug("Light Ethereum handshake failed", "err", err)
   124  		return err
   125  	}
   126  	// Connected to another server, no messages expected, just wait for disconnection
   127  	if p.server {
   128  		if err := h.server.serverset.register(p); err != nil {
   129  			return err
   130  		}
   131  		_, err := p.rw.ReadMsg()
   132  		h.server.serverset.unregister(p)
   133  		return err
   134  	}
   135  	// Setup flow control mechanism for the peer
   136  	p.fcClient = flowcontrol.NewClientNode(h.server.fcManager, p.fcParams)
   137  	defer p.fcClient.Disconnect()
   138  
   139  	// Reject light clients if server is not synced. Put this checking here, so
   140  	// that "non-synced" les-server peers are still allowed to keep the connection.
   141  	if !h.synced() {
   142  		p.Log().Debug("Light server not synced, rejecting peer")
   143  		return p2p.DiscRequested
   144  	}
   145  
   146  	// Register the peer into the peerset and clientpool
   147  	if err := h.server.peers.register(p); err != nil {
   148  		return err
   149  	}
   150  	if p.balance = h.server.clientPool.Register(p); p.balance == nil {
   151  		h.server.peers.unregister(p.ID())
   152  		p.Log().Debug("Client pool already closed")
   153  		return p2p.DiscRequested
   154  	}
   155  	p.connectedAt = mclock.Now()
   156  
   157  	var wg sync.WaitGroup // Wait group used to track all in-flight task routines.
   158  	defer func() {
   159  		wg.Wait() // Ensure all background task routines have exited.
   160  		h.server.clientPool.Unregister(p)
   161  		h.server.peers.unregister(p.ID())
   162  		p.balance = nil
   163  		connectionTimer.Update(time.Duration(mclock.Now() - p.connectedAt))
   164  	}()
   165  
   166  	// Mark the peer as being served.
   167  	atomic.StoreUint32(&p.serving, 1)
   168  	defer atomic.StoreUint32(&p.serving, 0)
   169  
   170  	// Spawn a main loop to handle all incoming messages.
   171  	for {
   172  		select {
   173  		case err := <-p.errCh:
   174  			p.Log().Debug("Failed to send light ethereum response", "err", err)
   175  			return err
   176  		default:
   177  		}
   178  		if err := h.handleMsg(p, &wg); err != nil {
   179  			p.Log().Debug("Light Ethereum message handling failed", "err", err)
   180  			return err
   181  		}
   182  	}
   183  }
   184  
   185  // beforeHandle will do a series of prechecks before handling message.
   186  func (h *serverHandler) beforeHandle(p *clientPeer, reqID, responseCount uint64, msg p2p.Msg, reqCnt uint64, maxCount uint64) (*servingTask, uint64) {
   187  	// Ensure that the request sent by client peer is valid
   188  	inSizeCost := h.server.costTracker.realCost(0, msg.Size, 0)
   189  	if reqCnt == 0 || reqCnt > maxCount {
   190  		p.fcClient.OneTimeCost(inSizeCost)
   191  		return nil, 0
   192  	}
   193  	// Ensure that the client peer complies with the flow control
   194  	// rules agreed by both sides.
   195  	if p.isFrozen() {
   196  		p.fcClient.OneTimeCost(inSizeCost)
   197  		return nil, 0
   198  	}
   199  	maxCost := p.fcCosts.getMaxCost(msg.Code, reqCnt)
   200  	accepted, bufShort, priority := p.fcClient.AcceptRequest(reqID, responseCount, maxCost)
   201  	if !accepted {
   202  		p.freeze()
   203  		p.Log().Error("Request came too early", "remaining", common.PrettyDuration(time.Duration(bufShort*1000000/p.fcParams.MinRecharge)))
   204  		p.fcClient.OneTimeCost(inSizeCost)
   205  		return nil, 0
   206  	}
   207  	// Create a multi-stage task, estimate the time it takes for the task to
   208  	// execute, and cache it in the request service queue.
   209  	factor := h.server.costTracker.globalFactor()
   210  	if factor < 0.001 {
   211  		factor = 1
   212  		p.Log().Error("Invalid global cost factor", "factor", factor)
   213  	}
   214  	maxTime := uint64(float64(maxCost) / factor)
   215  	task := h.server.servingQueue.newTask(p, maxTime, priority)
   216  	if !task.start() {
   217  		p.fcClient.RequestProcessed(reqID, responseCount, maxCost, inSizeCost)
   218  		return nil, 0
   219  	}
   220  	return task, maxCost
   221  }
   222  
   223  // Afterhandle will perform a series of operations after message handling,
   224  // such as updating flow control data, sending reply, etc.
   225  func (h *serverHandler) afterHandle(p *clientPeer, reqID, responseCount uint64, msg p2p.Msg, maxCost uint64, reqCnt uint64, task *servingTask, reply *reply) {
   226  	if reply != nil {
   227  		task.done()
   228  	}
   229  	p.responseLock.Lock()
   230  	defer p.responseLock.Unlock()
   231  
   232  	// Short circuit if the client is already frozen.
   233  	if p.isFrozen() {
   234  		realCost := h.server.costTracker.realCost(task.servingTime, msg.Size, 0)
   235  		p.fcClient.RequestProcessed(reqID, responseCount, maxCost, realCost)
   236  		return
   237  	}
   238  	// Positive correction buffer value with real cost.
   239  	var replySize uint32
   240  	if reply != nil {
   241  		replySize = reply.size()
   242  	}
   243  	var realCost uint64
   244  	if h.server.costTracker.testing {
   245  		realCost = maxCost // Assign a fake cost for testing purpose
   246  	} else {
   247  		realCost = h.server.costTracker.realCost(task.servingTime, msg.Size, replySize)
   248  		if realCost > maxCost {
   249  			realCost = maxCost
   250  		}
   251  	}
   252  	bv := p.fcClient.RequestProcessed(reqID, responseCount, maxCost, realCost)
   253  	if reply != nil {
   254  		// Feed cost tracker request serving statistic.
   255  		h.server.costTracker.updateStats(msg.Code, reqCnt, task.servingTime, realCost)
   256  		// Reduce priority "balance" for the specific peer.
   257  		p.balance.RequestServed(realCost)
   258  		p.queueSend(func() {
   259  			if err := reply.send(bv); err != nil {
   260  				select {
   261  				case p.errCh <- err:
   262  				default:
   263  				}
   264  			}
   265  		})
   266  	}
   267  }
   268  
   269  // handleMsg is invoked whenever an inbound message is received from a remote
   270  // peer. The remote connection is torn down upon returning any error.
   271  func (h *serverHandler) handleMsg(p *clientPeer, wg *sync.WaitGroup) error {
   272  	// Read the next message from the remote peer, and ensure it's fully consumed
   273  	msg, err := p.rw.ReadMsg()
   274  	if err != nil {
   275  		return err
   276  	}
   277  	p.Log().Trace("Light Ethereum message arrived", "code", msg.Code, "bytes", msg.Size)
   278  
   279  	// Discard large message which exceeds the limitation.
   280  	if msg.Size > ProtocolMaxMsgSize {
   281  		clientErrorMeter.Mark(1)
   282  		return errResp(ErrMsgTooLarge, "%v > %v", msg.Size, ProtocolMaxMsgSize)
   283  	}
   284  	defer msg.Discard()
   285  
   286  	// Lookup the request handler table, ensure it's supported
   287  	// message type by the protocol.
   288  	req, ok := Les3[msg.Code]
   289  	if !ok {
   290  		p.Log().Trace("Received invalid message", "code", msg.Code)
   291  		clientErrorMeter.Mark(1)
   292  		return errResp(ErrInvalidMsgCode, "%v", msg.Code)
   293  	}
   294  	p.Log().Trace("Received " + req.Name)
   295  
   296  	// Decode the p2p message, resolve the concrete handler for it.
   297  	serve, reqID, reqCnt, err := req.Handle(msg)
   298  	if err != nil {
   299  		clientErrorMeter.Mark(1)
   300  		return errResp(ErrDecode, "%v: %v", msg, err)
   301  	}
   302  	if metrics.EnabledExpensive {
   303  		req.InPacketsMeter.Mark(1)
   304  		req.InTrafficMeter.Mark(int64(msg.Size))
   305  	}
   306  	p.responseCount++
   307  	responseCount := p.responseCount
   308  
   309  	// First check this client message complies all rules before
   310  	// handling it and return a processor if all checks are passed.
   311  	task, maxCost := h.beforeHandle(p, reqID, responseCount, msg, reqCnt, req.MaxCount)
   312  	if task == nil {
   313  		return nil
   314  	}
   315  	wg.Add(1)
   316  	go func() {
   317  		defer wg.Done()
   318  
   319  		reply := serve(h, p, task.waitOrStop)
   320  		h.afterHandle(p, reqID, responseCount, msg, maxCost, reqCnt, task, reply)
   321  
   322  		if metrics.EnabledExpensive {
   323  			size := uint32(0)
   324  			if reply != nil {
   325  				size = reply.size()
   326  			}
   327  			req.OutPacketsMeter.Mark(1)
   328  			req.OutTrafficMeter.Mark(int64(size))
   329  			req.ServingTimeMeter.Update(time.Duration(task.servingTime))
   330  		}
   331  	}()
   332  	// If the client has made too much invalid request(e.g. request a non-existent data),
   333  	// reject them to prevent SPAM attack.
   334  	if p.getInvalid() > maxRequestErrors {
   335  		clientErrorMeter.Mark(1)
   336  		return errTooManyInvalidRequest
   337  	}
   338  	return nil
   339  }
   340  
   341  // BlockChain implements serverBackend
   342  func (h *serverHandler) BlockChain() *core.BlockChain {
   343  	return h.blockchain
   344  }
   345  
   346  // TxPool implements serverBackend
   347  func (h *serverHandler) TxPool() *txpool.TxPool {
   348  	return h.txpool
   349  }
   350  
   351  // ArchiveMode implements serverBackend
   352  func (h *serverHandler) ArchiveMode() bool {
   353  	return h.server.archiveMode
   354  }
   355  
   356  // AddTxsSync implements serverBackend
   357  func (h *serverHandler) AddTxsSync() bool {
   358  	return h.addTxsSync
   359  }
   360  
   361  // getAccount retrieves an account from the state based on root.
   362  func getAccount(triedb *trie.Database, root, hash common.Hash) (types.StateAccount, error) {
   363  	trie, err := trie.New(trie.StateTrieID(root), triedb)
   364  	if err != nil {
   365  		return types.StateAccount{}, err
   366  	}
   367  	blob, err := trie.TryGet(hash[:])
   368  	if err != nil {
   369  		return types.StateAccount{}, err
   370  	}
   371  	var acc types.StateAccount
   372  	if err = rlp.DecodeBytes(blob, &acc); err != nil {
   373  		return types.StateAccount{}, err
   374  	}
   375  	return acc, nil
   376  }
   377  
   378  // GetHelperTrie returns the post-processed trie root for the given trie ID and section index
   379  func (h *serverHandler) GetHelperTrie(typ uint, index uint64) *trie.Trie {
   380  	var (
   381  		root   common.Hash
   382  		prefix string
   383  	)
   384  	switch typ {
   385  	case htCanonical:
   386  		sectionHead := rawdb.ReadCanonicalHash(h.chainDb, (index+1)*h.server.iConfig.ChtSize-1)
   387  		root, prefix = light.GetChtRoot(h.chainDb, index, sectionHead), string(rawdb.ChtTablePrefix)
   388  	case htBloomBits:
   389  		sectionHead := rawdb.ReadCanonicalHash(h.chainDb, (index+1)*h.server.iConfig.BloomTrieSize-1)
   390  		root, prefix = light.GetBloomTrieRoot(h.chainDb, index, sectionHead), string(rawdb.BloomTrieTablePrefix)
   391  	}
   392  	if root == (common.Hash{}) {
   393  		return nil
   394  	}
   395  	trie, _ := trie.New(trie.TrieID(root), trie.NewDatabase(rawdb.NewTable(h.chainDb, prefix)))
   396  	return trie
   397  }
   398  
   399  // broadcastLoop broadcasts new block information to all connected light
   400  // clients. According to the agreement between client and server, server should
   401  // only broadcast new announcement if the total difficulty is higher than the
   402  // last one. Besides server will add the signature if client requires.
   403  func (h *serverHandler) broadcastLoop() {
   404  	defer h.wg.Done()
   405  
   406  	headCh := make(chan core.ChainHeadEvent, 10)
   407  	headSub := h.blockchain.SubscribeChainHeadEvent(headCh)
   408  	defer headSub.Unsubscribe()
   409  
   410  	var (
   411  		lastHead = h.blockchain.CurrentHeader()
   412  		lastTd   = common.Big0
   413  	)
   414  	for {
   415  		select {
   416  		case ev := <-headCh:
   417  			header := ev.Block.Header()
   418  			hash, number := header.Hash(), header.Number.Uint64()
   419  			td := h.blockchain.GetTd(hash, number)
   420  			if td == nil || td.Cmp(lastTd) <= 0 {
   421  				continue
   422  			}
   423  			var reorg uint64
   424  			if lastHead != nil {
   425  				// If a setHead has been performed, the common ancestor can be nil.
   426  				if ancestor := rawdb.FindCommonAncestor(h.chainDb, header, lastHead); ancestor != nil {
   427  					reorg = lastHead.Number.Uint64() - ancestor.Number.Uint64()
   428  				}
   429  			}
   430  			lastHead, lastTd = header, td
   431  			log.Debug("Announcing block to peers", "number", number, "hash", hash, "td", td, "reorg", reorg)
   432  			h.server.peers.broadcast(announceData{Hash: hash, Number: number, Td: td, ReorgDepth: reorg})
   433  		case <-h.closeCh:
   434  			return
   435  		}
   436  	}
   437  }