github.com/zhiqiangxu/go-ethereum@v1.9.16-0.20210824055606-be91cfdebc48/les/server.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  	"crypto/ecdsa"
    21  	"time"
    22  
    23  	"github.com/zhiqiangxu/go-ethereum/accounts/abi/bind"
    24  	"github.com/zhiqiangxu/go-ethereum/common/mclock"
    25  	"github.com/zhiqiangxu/go-ethereum/core"
    26  	"github.com/zhiqiangxu/go-ethereum/eth"
    27  	"github.com/zhiqiangxu/go-ethereum/les/checkpointoracle"
    28  	"github.com/zhiqiangxu/go-ethereum/les/flowcontrol"
    29  	"github.com/zhiqiangxu/go-ethereum/light"
    30  	"github.com/zhiqiangxu/go-ethereum/log"
    31  	"github.com/zhiqiangxu/go-ethereum/p2p"
    32  	"github.com/zhiqiangxu/go-ethereum/p2p/discv5"
    33  	"github.com/zhiqiangxu/go-ethereum/p2p/enode"
    34  	"github.com/zhiqiangxu/go-ethereum/p2p/enr"
    35  	"github.com/zhiqiangxu/go-ethereum/params"
    36  	"github.com/zhiqiangxu/go-ethereum/rpc"
    37  )
    38  
    39  type LesServer struct {
    40  	lesCommons
    41  
    42  	archiveMode bool // Flag whether the ethereum node runs in archive mode.
    43  	peers       *clientPeerSet
    44  	handler     *serverHandler
    45  	lesTopics   []discv5.Topic
    46  	privateKey  *ecdsa.PrivateKey
    47  
    48  	// Flow control and capacity management
    49  	fcManager    *flowcontrol.ClientManager
    50  	costTracker  *costTracker
    51  	defParams    flowcontrol.ServerParams
    52  	servingQueue *servingQueue
    53  	clientPool   *clientPool
    54  
    55  	minCapacity, maxCapacity, freeCapacity uint64
    56  	threadsIdle                            int // Request serving threads count when system is idle.
    57  	threadsBusy                            int // Request serving threads count when system is busy(block insertion).
    58  }
    59  
    60  func NewLesServer(e *eth.Ethereum, config *eth.Config) (*LesServer, error) {
    61  	// Collect les protocol version information supported by local node.
    62  	lesTopics := make([]discv5.Topic, len(AdvertiseProtocolVersions))
    63  	for i, pv := range AdvertiseProtocolVersions {
    64  		lesTopics[i] = lesTopic(e.BlockChain().Genesis().Hash(), pv)
    65  	}
    66  	// Calculate the number of threads used to service the light client
    67  	// requests based on the user-specified value.
    68  	threads := config.LightServ * 4 / 100
    69  	if threads < 4 {
    70  		threads = 4
    71  	}
    72  	srv := &LesServer{
    73  		lesCommons: lesCommons{
    74  			genesis:          e.BlockChain().Genesis().Hash(),
    75  			config:           config,
    76  			chainConfig:      e.BlockChain().Config(),
    77  			iConfig:          light.DefaultServerIndexerConfig,
    78  			chainDb:          e.ChainDb(),
    79  			chainReader:      e.BlockChain(),
    80  			chtIndexer:       light.NewChtIndexer(e.ChainDb(), nil, params.CHTFrequency, params.HelperTrieProcessConfirmations),
    81  			bloomTrieIndexer: light.NewBloomTrieIndexer(e.ChainDb(), nil, params.BloomBitsBlocks, params.BloomTrieFrequency),
    82  			closeCh:          make(chan struct{}),
    83  		},
    84  		archiveMode:  e.ArchiveMode(),
    85  		peers:        newClientPeerSet(),
    86  		lesTopics:    lesTopics,
    87  		fcManager:    flowcontrol.NewClientManager(nil, &mclock.System{}),
    88  		servingQueue: newServingQueue(int64(time.Millisecond*10), float64(config.LightServ)/100),
    89  		threadsBusy:  config.LightServ/100 + 1,
    90  		threadsIdle:  threads,
    91  	}
    92  	srv.handler = newServerHandler(srv, e.BlockChain(), e.ChainDb(), e.TxPool(), e.Synced)
    93  	srv.costTracker, srv.minCapacity = newCostTracker(e.ChainDb(), config)
    94  	srv.freeCapacity = srv.minCapacity
    95  
    96  	// Set up checkpoint oracle.
    97  	oracle := config.CheckpointOracle
    98  	if oracle == nil {
    99  		oracle = params.CheckpointOracles[e.BlockChain().Genesis().Hash()]
   100  	}
   101  	srv.oracle = checkpointoracle.New(oracle, srv.localCheckpoint)
   102  
   103  	// Initialize server capacity management fields.
   104  	srv.defParams = flowcontrol.ServerParams{
   105  		BufLimit:    srv.freeCapacity * bufLimitRatio,
   106  		MinRecharge: srv.freeCapacity,
   107  	}
   108  	// LES flow control tries to more or less guarantee the possibility for the
   109  	// clients to send a certain amount of requests at any time and get a quick
   110  	// response. Most of the clients want this guarantee but don't actually need
   111  	// to send requests most of the time. Our goal is to serve as many clients as
   112  	// possible while the actually used server capacity does not exceed the limits
   113  	totalRecharge := srv.costTracker.totalRecharge()
   114  	srv.maxCapacity = srv.freeCapacity * uint64(srv.config.LightPeers)
   115  	if totalRecharge > srv.maxCapacity {
   116  		srv.maxCapacity = totalRecharge
   117  	}
   118  	srv.fcManager.SetCapacityLimits(srv.freeCapacity, srv.maxCapacity, srv.freeCapacity*2)
   119  	srv.clientPool = newClientPool(srv.chainDb, srv.freeCapacity, mclock.System{}, func(id enode.ID) { go srv.peers.unregister(peerIdToString(id)) })
   120  	srv.clientPool.setDefaultFactors(priceFactors{0, 1, 1}, priceFactors{0, 1, 1})
   121  
   122  	checkpoint := srv.latestLocalCheckpoint()
   123  	if !checkpoint.Empty() {
   124  		log.Info("Loaded latest checkpoint", "section", checkpoint.SectionIndex, "head", checkpoint.SectionHead,
   125  			"chtroot", checkpoint.CHTRoot, "bloomroot", checkpoint.BloomRoot)
   126  	}
   127  	srv.chtIndexer.Start(e.BlockChain())
   128  	return srv, nil
   129  }
   130  
   131  func (s *LesServer) APIs() []rpc.API {
   132  	return []rpc.API{
   133  		{
   134  			Namespace: "les",
   135  			Version:   "1.0",
   136  			Service:   NewPrivateLightAPI(&s.lesCommons),
   137  			Public:    false,
   138  		},
   139  		{
   140  			Namespace: "les",
   141  			Version:   "1.0",
   142  			Service:   NewPrivateLightServerAPI(s),
   143  			Public:    false,
   144  		},
   145  		{
   146  			Namespace: "debug",
   147  			Version:   "1.0",
   148  			Service:   NewPrivateDebugAPI(s),
   149  			Public:    false,
   150  		},
   151  	}
   152  }
   153  
   154  func (s *LesServer) Protocols() []p2p.Protocol {
   155  	ps := s.makeProtocols(ServerProtocolVersions, s.handler.runPeer, func(id enode.ID) interface{} {
   156  		if p := s.peers.peer(peerIdToString(id)); p != nil {
   157  			return p.Info()
   158  		}
   159  		return nil
   160  	}, nil)
   161  	// Add "les" ENR entries.
   162  	for i := range ps {
   163  		ps[i].Attributes = []enr.Entry{&lesEntry{}}
   164  	}
   165  	return ps
   166  }
   167  
   168  // Start starts the LES server
   169  func (s *LesServer) Start(srvr *p2p.Server) {
   170  	s.privateKey = srvr.PrivateKey
   171  	s.handler.start()
   172  
   173  	s.wg.Add(1)
   174  	go s.capacityManagement()
   175  
   176  	if srvr.DiscV5 != nil {
   177  		for _, topic := range s.lesTopics {
   178  			topic := topic
   179  			go func() {
   180  				logger := log.New("topic", topic)
   181  				logger.Info("Starting topic registration")
   182  				defer logger.Info("Terminated topic registration")
   183  
   184  				srvr.DiscV5.RegisterTopic(topic, s.closeCh)
   185  			}()
   186  		}
   187  	}
   188  }
   189  
   190  // Stop stops the LES service
   191  func (s *LesServer) Stop() {
   192  	close(s.closeCh)
   193  
   194  	// Disconnect existing sessions.
   195  	// This also closes the gate for any new registrations on the peer set.
   196  	// sessions which are already established but not added to pm.peers yet
   197  	// will exit when they try to register.
   198  	s.peers.close()
   199  
   200  	s.fcManager.Stop()
   201  	s.costTracker.stop()
   202  	s.handler.stop()
   203  	s.clientPool.stop() // client pool should be closed after handler.
   204  	s.servingQueue.stop()
   205  
   206  	// Note, bloom trie indexer is closed by parent bloombits indexer.
   207  	s.chtIndexer.Close()
   208  	s.wg.Wait()
   209  	log.Info("Les server stopped")
   210  }
   211  
   212  func (s *LesServer) SetBloomBitsIndexer(bloomIndexer *core.ChainIndexer) {
   213  	bloomIndexer.AddChildIndexer(s.bloomTrieIndexer)
   214  }
   215  
   216  // SetClient sets the rpc client and starts running checkpoint contract if it is not yet watched.
   217  func (s *LesServer) SetContractBackend(backend bind.ContractBackend) {
   218  	if s.oracle == nil {
   219  		return
   220  	}
   221  	s.oracle.Start(backend)
   222  }
   223  
   224  // capacityManagement starts an event handler loop that updates the recharge curve of
   225  // the client manager and adjusts the client pool's size according to the total
   226  // capacity updates coming from the client manager
   227  func (s *LesServer) capacityManagement() {
   228  	defer s.wg.Done()
   229  
   230  	processCh := make(chan bool, 100)
   231  	sub := s.handler.blockchain.SubscribeBlockProcessingEvent(processCh)
   232  	defer sub.Unsubscribe()
   233  
   234  	totalRechargeCh := make(chan uint64, 100)
   235  	totalRecharge := s.costTracker.subscribeTotalRecharge(totalRechargeCh)
   236  
   237  	totalCapacityCh := make(chan uint64, 100)
   238  	totalCapacity := s.fcManager.SubscribeTotalCapacity(totalCapacityCh)
   239  	s.clientPool.setLimits(s.config.LightPeers, totalCapacity)
   240  
   241  	var (
   242  		busy         bool
   243  		freePeers    uint64
   244  		blockProcess mclock.AbsTime
   245  	)
   246  	updateRecharge := func() {
   247  		if busy {
   248  			s.servingQueue.setThreads(s.threadsBusy)
   249  			s.fcManager.SetRechargeCurve(flowcontrol.PieceWiseLinear{{0, 0}, {totalRecharge, totalRecharge}})
   250  		} else {
   251  			s.servingQueue.setThreads(s.threadsIdle)
   252  			s.fcManager.SetRechargeCurve(flowcontrol.PieceWiseLinear{{0, 0}, {totalRecharge / 10, totalRecharge}, {totalRecharge, totalRecharge}})
   253  		}
   254  	}
   255  	updateRecharge()
   256  
   257  	for {
   258  		select {
   259  		case busy = <-processCh:
   260  			if busy {
   261  				blockProcess = mclock.Now()
   262  			} else {
   263  				blockProcessingTimer.Update(time.Duration(mclock.Now() - blockProcess))
   264  			}
   265  			updateRecharge()
   266  		case totalRecharge = <-totalRechargeCh:
   267  			totalRechargeGauge.Update(int64(totalRecharge))
   268  			updateRecharge()
   269  		case totalCapacity = <-totalCapacityCh:
   270  			totalCapacityGauge.Update(int64(totalCapacity))
   271  			newFreePeers := totalCapacity / s.freeCapacity
   272  			if newFreePeers < freePeers && newFreePeers < uint64(s.config.LightPeers) {
   273  				log.Warn("Reduced free peer connections", "from", freePeers, "to", newFreePeers)
   274  			}
   275  			freePeers = newFreePeers
   276  			s.clientPool.setLimits(s.config.LightPeers, totalCapacity)
   277  		case <-s.closeCh:
   278  			return
   279  		}
   280  	}
   281  }