github.com/tacshi/go-ethereum@v0.0.0-20230616113857-84a434e20921/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/tacshi/go-ethereum/common/mclock"
    24  	"github.com/tacshi/go-ethereum/core"
    25  	"github.com/tacshi/go-ethereum/core/txpool"
    26  	"github.com/tacshi/go-ethereum/eth/ethconfig"
    27  	"github.com/tacshi/go-ethereum/ethdb"
    28  	"github.com/tacshi/go-ethereum/les/flowcontrol"
    29  	vfs "github.com/tacshi/go-ethereum/les/vflux/server"
    30  	"github.com/tacshi/go-ethereum/light"
    31  	"github.com/tacshi/go-ethereum/log"
    32  	"github.com/tacshi/go-ethereum/node"
    33  	"github.com/tacshi/go-ethereum/p2p"
    34  	"github.com/tacshi/go-ethereum/p2p/enode"
    35  	"github.com/tacshi/go-ethereum/p2p/enr"
    36  	"github.com/tacshi/go-ethereum/params"
    37  	"github.com/tacshi/go-ethereum/rpc"
    38  )
    39  
    40  var (
    41  	defaultPosFactors = vfs.PriceFactors{TimeFactor: 0, CapacityFactor: 1, RequestFactor: 1}
    42  	defaultNegFactors = vfs.PriceFactors{TimeFactor: 0, CapacityFactor: 1, RequestFactor: 1}
    43  )
    44  
    45  const defaultConnectedBias = time.Minute * 3
    46  
    47  type ethBackend interface {
    48  	ArchiveMode() bool
    49  	BlockChain() *core.BlockChain
    50  	BloomIndexer() *core.ChainIndexer
    51  	ChainDb() ethdb.Database
    52  	Synced() bool
    53  	TxPool() *txpool.TxPool
    54  }
    55  
    56  type LesServer struct {
    57  	lesCommons
    58  
    59  	archiveMode bool // Flag whether the ethereum node runs in archive mode.
    60  	handler     *serverHandler
    61  	peers       *clientPeerSet
    62  	serverset   *serverSet
    63  	vfluxServer *vfs.Server
    64  	privateKey  *ecdsa.PrivateKey
    65  
    66  	// Flow control and capacity management
    67  	fcManager    *flowcontrol.ClientManager
    68  	costTracker  *costTracker
    69  	defParams    flowcontrol.ServerParams
    70  	servingQueue *servingQueue
    71  	clientPool   *vfs.ClientPool
    72  
    73  	minCapacity, maxCapacity uint64
    74  	threadsIdle              int // Request serving threads count when system is idle.
    75  	threadsBusy              int // Request serving threads count when system is busy(block insertion).
    76  
    77  	p2pSrv *p2p.Server
    78  }
    79  
    80  func NewLesServer(node *node.Node, e ethBackend, config *ethconfig.Config) (*LesServer, error) {
    81  	lesDb, err := node.OpenDatabase("les.server", 0, 0, "eth/db/lesserver/", false)
    82  	if err != nil {
    83  		return nil, err
    84  	}
    85  	// Calculate the number of threads used to service the light client
    86  	// requests based on the user-specified value.
    87  	threads := config.LightServ * 4 / 100
    88  	if threads < 4 {
    89  		threads = 4
    90  	}
    91  	srv := &LesServer{
    92  		lesCommons: lesCommons{
    93  			genesis:          e.BlockChain().Genesis().Hash(),
    94  			config:           config,
    95  			chainConfig:      e.BlockChain().Config(),
    96  			iConfig:          light.DefaultServerIndexerConfig,
    97  			chainDb:          e.ChainDb(),
    98  			lesDb:            lesDb,
    99  			chainReader:      e.BlockChain(),
   100  			chtIndexer:       light.NewChtIndexer(e.ChainDb(), nil, params.CHTFrequency, params.HelperTrieProcessConfirmations, true),
   101  			bloomTrieIndexer: light.NewBloomTrieIndexer(e.ChainDb(), nil, params.BloomBitsBlocks, params.BloomTrieFrequency, true),
   102  			closeCh:          make(chan struct{}),
   103  		},
   104  		archiveMode:  e.ArchiveMode(),
   105  		peers:        newClientPeerSet(),
   106  		serverset:    newServerSet(),
   107  		vfluxServer:  vfs.NewServer(time.Millisecond * 10),
   108  		fcManager:    flowcontrol.NewClientManager(nil, &mclock.System{}),
   109  		servingQueue: newServingQueue(int64(time.Millisecond*10), float64(config.LightServ)/100),
   110  		threadsBusy:  config.LightServ/100 + 1,
   111  		threadsIdle:  threads,
   112  		p2pSrv:       node.Server(),
   113  	}
   114  	issync := e.Synced
   115  	if config.LightNoSyncServe {
   116  		issync = func() bool { return true }
   117  	}
   118  	srv.handler = newServerHandler(srv, e.BlockChain(), e.ChainDb(), e.TxPool(), issync)
   119  	srv.costTracker, srv.minCapacity = newCostTracker(e.ChainDb(), config)
   120  	srv.oracle = srv.setupOracle(node, e.BlockChain().Genesis().Hash(), config)
   121  
   122  	// Initialize the bloom trie indexer.
   123  	e.BloomIndexer().AddChildIndexer(srv.bloomTrieIndexer)
   124  
   125  	// Initialize server capacity management fields.
   126  	srv.defParams = flowcontrol.ServerParams{
   127  		BufLimit:    srv.minCapacity * bufLimitRatio,
   128  		MinRecharge: srv.minCapacity,
   129  	}
   130  	// LES flow control tries to more or less guarantee the possibility for the
   131  	// clients to send a certain amount of requests at any time and get a quick
   132  	// response. Most of the clients want this guarantee but don't actually need
   133  	// to send requests most of the time. Our goal is to serve as many clients as
   134  	// possible while the actually used server capacity does not exceed the limits
   135  	totalRecharge := srv.costTracker.totalRecharge()
   136  	srv.maxCapacity = srv.minCapacity * uint64(srv.config.LightPeers)
   137  	if totalRecharge > srv.maxCapacity {
   138  		srv.maxCapacity = totalRecharge
   139  	}
   140  	srv.fcManager.SetCapacityLimits(srv.minCapacity, srv.maxCapacity, srv.minCapacity*2)
   141  	srv.clientPool = vfs.NewClientPool(lesDb, srv.minCapacity, defaultConnectedBias, mclock.System{}, issync)
   142  	srv.clientPool.Start()
   143  	srv.clientPool.SetDefaultFactors(defaultPosFactors, defaultNegFactors)
   144  	srv.vfluxServer.Register(srv.clientPool, "les", "Ethereum light client service")
   145  
   146  	checkpoint := srv.latestLocalCheckpoint()
   147  	if !checkpoint.Empty() {
   148  		log.Info("Loaded latest checkpoint", "section", checkpoint.SectionIndex, "head", checkpoint.SectionHead,
   149  			"chtroot", checkpoint.CHTRoot, "bloomroot", checkpoint.BloomRoot)
   150  	}
   151  	srv.chtIndexer.Start(e.BlockChain())
   152  
   153  	node.RegisterProtocols(srv.Protocols())
   154  	node.RegisterAPIs(srv.APIs())
   155  	node.RegisterLifecycle(srv)
   156  	return srv, nil
   157  }
   158  
   159  func (s *LesServer) APIs() []rpc.API {
   160  	return []rpc.API{
   161  		{
   162  			Namespace: "les",
   163  			Service:   NewLightAPI(&s.lesCommons),
   164  		},
   165  		{
   166  			Namespace: "les",
   167  			Service:   NewLightServerAPI(s),
   168  		},
   169  		{
   170  			Namespace: "debug",
   171  			Service:   NewDebugAPI(s),
   172  		},
   173  	}
   174  }
   175  
   176  func (s *LesServer) Protocols() []p2p.Protocol {
   177  	ps := s.makeProtocols(ServerProtocolVersions, s.handler.runPeer, func(id enode.ID) interface{} {
   178  		if p := s.peers.peer(id); p != nil {
   179  			return p.Info()
   180  		}
   181  		return nil
   182  	}, nil)
   183  	// Add "les" ENR entries.
   184  	for i := range ps {
   185  		ps[i].Attributes = []enr.Entry{&lesEntry{
   186  			VfxVersion: 1,
   187  		}}
   188  	}
   189  	return ps
   190  }
   191  
   192  // Start starts the LES server
   193  func (s *LesServer) Start() error {
   194  	s.privateKey = s.p2pSrv.PrivateKey
   195  	s.peers.setSignerKey(s.privateKey)
   196  	s.handler.start()
   197  	s.wg.Add(1)
   198  	go s.capacityManagement()
   199  	if s.p2pSrv.DiscV5 != nil {
   200  		s.p2pSrv.DiscV5.RegisterTalkHandler("vfx", s.vfluxServer.ServeEncoded)
   201  	}
   202  	return nil
   203  }
   204  
   205  // Stop stops the LES service
   206  func (s *LesServer) Stop() error {
   207  	close(s.closeCh)
   208  
   209  	s.clientPool.Stop()
   210  	if s.serverset != nil {
   211  		s.serverset.close()
   212  	}
   213  	s.peers.close()
   214  	s.fcManager.Stop()
   215  	s.costTracker.stop()
   216  	s.handler.stop()
   217  	s.servingQueue.stop()
   218  	if s.vfluxServer != nil {
   219  		s.vfluxServer.Stop()
   220  	}
   221  
   222  	// Note, bloom trie indexer is closed by parent bloombits indexer.
   223  	if s.chtIndexer != nil {
   224  		s.chtIndexer.Close()
   225  	}
   226  	if s.lesDb != nil {
   227  		s.lesDb.Close()
   228  	}
   229  	s.wg.Wait()
   230  	log.Info("Les server stopped")
   231  
   232  	return nil
   233  }
   234  
   235  // capacityManagement starts an event handler loop that updates the recharge curve of
   236  // the client manager and adjusts the client pool's size according to the total
   237  // capacity updates coming from the client manager
   238  func (s *LesServer) capacityManagement() {
   239  	defer s.wg.Done()
   240  
   241  	processCh := make(chan bool, 100)
   242  	sub := s.handler.blockchain.SubscribeBlockProcessingEvent(processCh)
   243  	defer sub.Unsubscribe()
   244  
   245  	totalRechargeCh := make(chan uint64, 100)
   246  	totalRecharge := s.costTracker.subscribeTotalRecharge(totalRechargeCh)
   247  
   248  	totalCapacityCh := make(chan uint64, 100)
   249  	totalCapacity := s.fcManager.SubscribeTotalCapacity(totalCapacityCh)
   250  	s.clientPool.SetLimits(uint64(s.config.LightPeers), totalCapacity)
   251  
   252  	var (
   253  		busy         bool
   254  		freePeers    uint64
   255  		blockProcess mclock.AbsTime
   256  	)
   257  	updateRecharge := func() {
   258  		if busy {
   259  			s.servingQueue.setThreads(s.threadsBusy)
   260  			s.fcManager.SetRechargeCurve(flowcontrol.PieceWiseLinear{{0, 0}, {totalRecharge, totalRecharge}})
   261  		} else {
   262  			s.servingQueue.setThreads(s.threadsIdle)
   263  			s.fcManager.SetRechargeCurve(flowcontrol.PieceWiseLinear{{0, 0}, {totalRecharge / 10, totalRecharge}, {totalRecharge, totalRecharge}})
   264  		}
   265  	}
   266  	updateRecharge()
   267  
   268  	for {
   269  		select {
   270  		case busy = <-processCh:
   271  			if busy {
   272  				blockProcess = mclock.Now()
   273  			} else {
   274  				blockProcessingTimer.Update(time.Duration(mclock.Now() - blockProcess))
   275  			}
   276  			updateRecharge()
   277  		case totalRecharge = <-totalRechargeCh:
   278  			totalRechargeGauge.Update(int64(totalRecharge))
   279  			updateRecharge()
   280  		case totalCapacity = <-totalCapacityCh:
   281  			totalCapacityGauge.Update(int64(totalCapacity))
   282  			newFreePeers := totalCapacity / s.minCapacity
   283  			if newFreePeers < freePeers && newFreePeers < uint64(s.config.LightPeers) {
   284  				log.Warn("Reduced free peer connections", "from", freePeers, "to", newFreePeers)
   285  			}
   286  			freePeers = newFreePeers
   287  			s.clientPool.SetLimits(uint64(s.config.LightPeers), totalCapacity)
   288  		case <-s.closeCh:
   289  			return
   290  		}
   291  	}
   292  }