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