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