github.com/hardtosaygoodbye/go-ethereum@v1.10.16-0.20220122011429-97003b9e6c15/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/hardtosaygoodbye/go-ethereum/common/mclock"
    24  	"github.com/hardtosaygoodbye/go-ethereum/core"
    25  	"github.com/hardtosaygoodbye/go-ethereum/eth/ethconfig"
    26  	"github.com/hardtosaygoodbye/go-ethereum/ethdb"
    27  	"github.com/hardtosaygoodbye/go-ethereum/les/flowcontrol"
    28  	vfs "github.com/hardtosaygoodbye/go-ethereum/les/vflux/server"
    29  	"github.com/hardtosaygoodbye/go-ethereum/light"
    30  	"github.com/hardtosaygoodbye/go-ethereum/log"
    31  	"github.com/hardtosaygoodbye/go-ethereum/node"
    32  	"github.com/hardtosaygoodbye/go-ethereum/p2p"
    33  	"github.com/hardtosaygoodbye/go-ethereum/p2p/enode"
    34  	"github.com/hardtosaygoodbye/go-ethereum/p2p/enr"
    35  	"github.com/hardtosaygoodbye/go-ethereum/params"
    36  	"github.com/hardtosaygoodbye/go-ethereum/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  			Version:   "1.0",
   163  			Service:   NewPrivateLightAPI(&s.lesCommons),
   164  			Public:    false,
   165  		},
   166  		{
   167  			Namespace: "les",
   168  			Version:   "1.0",
   169  			Service:   NewPrivateLightServerAPI(s),
   170  			Public:    false,
   171  		},
   172  		{
   173  			Namespace: "debug",
   174  			Version:   "1.0",
   175  			Service:   NewPrivateDebugAPI(s),
   176  			Public:    false,
   177  		},
   178  	}
   179  }
   180  
   181  func (s *LesServer) Protocols() []p2p.Protocol {
   182  	ps := s.makeProtocols(ServerProtocolVersions, s.handler.runPeer, func(id enode.ID) interface{} {
   183  		if p := s.peers.peer(id); p != nil {
   184  			return p.Info()
   185  		}
   186  		return nil
   187  	}, nil)
   188  	// Add "les" ENR entries.
   189  	for i := range ps {
   190  		ps[i].Attributes = []enr.Entry{&lesEntry{
   191  			VfxVersion: 1,
   192  		}}
   193  	}
   194  	return ps
   195  }
   196  
   197  // Start starts the LES server
   198  func (s *LesServer) Start() error {
   199  	s.privateKey = s.p2pSrv.PrivateKey
   200  	s.peers.setSignerKey(s.privateKey)
   201  	s.handler.start()
   202  	s.wg.Add(1)
   203  	go s.capacityManagement()
   204  	if s.p2pSrv.DiscV5 != nil {
   205  		s.p2pSrv.DiscV5.RegisterTalkHandler("vfx", s.vfluxServer.ServeEncoded)
   206  	}
   207  	return nil
   208  }
   209  
   210  // Stop stops the LES service
   211  func (s *LesServer) Stop() error {
   212  	close(s.closeCh)
   213  
   214  	s.clientPool.Stop()
   215  	if s.serverset != nil {
   216  		s.serverset.close()
   217  	}
   218  	s.peers.close()
   219  	s.fcManager.Stop()
   220  	s.costTracker.stop()
   221  	s.handler.stop()
   222  	s.servingQueue.stop()
   223  	if s.vfluxServer != nil {
   224  		s.vfluxServer.Stop()
   225  	}
   226  
   227  	// Note, bloom trie indexer is closed by parent bloombits indexer.
   228  	if s.chtIndexer != nil {
   229  		s.chtIndexer.Close()
   230  	}
   231  	if s.lesDb != nil {
   232  		s.lesDb.Close()
   233  	}
   234  	s.wg.Wait()
   235  	log.Info("Les server stopped")
   236  
   237  	return nil
   238  }
   239  
   240  // capacityManagement starts an event handler loop that updates the recharge curve of
   241  // the client manager and adjusts the client pool's size according to the total
   242  // capacity updates coming from the client manager
   243  func (s *LesServer) capacityManagement() {
   244  	defer s.wg.Done()
   245  
   246  	processCh := make(chan bool, 100)
   247  	sub := s.handler.blockchain.SubscribeBlockProcessingEvent(processCh)
   248  	defer sub.Unsubscribe()
   249  
   250  	totalRechargeCh := make(chan uint64, 100)
   251  	totalRecharge := s.costTracker.subscribeTotalRecharge(totalRechargeCh)
   252  
   253  	totalCapacityCh := make(chan uint64, 100)
   254  	totalCapacity := s.fcManager.SubscribeTotalCapacity(totalCapacityCh)
   255  	s.clientPool.SetLimits(uint64(s.config.LightPeers), totalCapacity)
   256  
   257  	var (
   258  		busy         bool
   259  		freePeers    uint64
   260  		blockProcess mclock.AbsTime
   261  	)
   262  	updateRecharge := func() {
   263  		if busy {
   264  			s.servingQueue.setThreads(s.threadsBusy)
   265  			s.fcManager.SetRechargeCurve(flowcontrol.PieceWiseLinear{{0, 0}, {totalRecharge, totalRecharge}})
   266  		} else {
   267  			s.servingQueue.setThreads(s.threadsIdle)
   268  			s.fcManager.SetRechargeCurve(flowcontrol.PieceWiseLinear{{0, 0}, {totalRecharge / 10, totalRecharge}, {totalRecharge, totalRecharge}})
   269  		}
   270  	}
   271  	updateRecharge()
   272  
   273  	for {
   274  		select {
   275  		case busy = <-processCh:
   276  			if busy {
   277  				blockProcess = mclock.Now()
   278  			} else {
   279  				blockProcessingTimer.Update(time.Duration(mclock.Now() - blockProcess))
   280  			}
   281  			updateRecharge()
   282  		case totalRecharge = <-totalRechargeCh:
   283  			totalRechargeGauge.Update(int64(totalRecharge))
   284  			updateRecharge()
   285  		case totalCapacity = <-totalCapacityCh:
   286  			totalCapacityGauge.Update(int64(totalCapacity))
   287  			newFreePeers := totalCapacity / s.minCapacity
   288  			if newFreePeers < freePeers && newFreePeers < uint64(s.config.LightPeers) {
   289  				log.Warn("Reduced free peer connections", "from", freePeers, "to", newFreePeers)
   290  			}
   291  			freePeers = newFreePeers
   292  			s.clientPool.SetLimits(uint64(s.config.LightPeers), totalCapacity)
   293  		case <-s.closeCh:
   294  			return
   295  		}
   296  	}
   297  }