github.com/theQRL/go-zond@v0.1.1/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/theQRL/go-zond/common/mclock"
    24  	"github.com/theQRL/go-zond/core"
    25  	"github.com/theQRL/go-zond/core/txpool"
    26  	"github.com/theQRL/go-zond/zond/ethconfig"
    27  	"github.com/theQRL/go-zond/zonddb"
    28  	"github.com/theQRL/go-zond/les/flowcontrol"
    29  	vfs "github.com/theQRL/go-zond/les/vflux/server"
    30  	"github.com/theQRL/go-zond/light"
    31  	"github.com/theQRL/go-zond/log"
    32  	"github.com/theQRL/go-zond/node"
    33  	"github.com/theQRL/go-zond/p2p"
    34  	"github.com/theQRL/go-zond/p2p/enode"
    35  	"github.com/theQRL/go-zond/p2p/enr"
    36  	"github.com/theQRL/go-zond/params"
    37  	"github.com/theQRL/go-zond/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() zonddb.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, "zond/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  
   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  	srv.chtIndexer.Start(e.BlockChain())
   145  
   146  	node.RegisterProtocols(srv.Protocols())
   147  	node.RegisterAPIs(srv.APIs())
   148  	node.RegisterLifecycle(srv)
   149  	return srv, nil
   150  }
   151  
   152  func (s *LesServer) APIs() []rpc.API {
   153  	return []rpc.API{
   154  		{
   155  			Namespace: "les",
   156  			Service:   NewLightServerAPI(s),
   157  		},
   158  		{
   159  			Namespace: "debug",
   160  			Service:   NewDebugAPI(s),
   161  		},
   162  	}
   163  }
   164  
   165  func (s *LesServer) Protocols() []p2p.Protocol {
   166  	ps := s.makeProtocols(ServerProtocolVersions, s.handler.runPeer, func(id enode.ID) interface{} {
   167  		if p := s.peers.peer(id); p != nil {
   168  			return p.Info()
   169  		}
   170  		return nil
   171  	}, nil)
   172  	// Add "les" ENR entries.
   173  	for i := range ps {
   174  		ps[i].Attributes = []enr.Entry{&lesEntry{
   175  			VfxVersion: 1,
   176  		}}
   177  	}
   178  	return ps
   179  }
   180  
   181  // Start starts the LES server
   182  func (s *LesServer) Start() error {
   183  	s.privateKey = s.p2pSrv.PrivateKey
   184  	s.peers.setSignerKey(s.privateKey)
   185  	s.handler.start()
   186  	s.wg.Add(1)
   187  	go s.capacityManagement()
   188  	if s.p2pSrv.DiscV5 != nil {
   189  		s.p2pSrv.DiscV5.RegisterTalkHandler("vfx", s.vfluxServer.ServeEncoded)
   190  	}
   191  	return nil
   192  }
   193  
   194  // Stop stops the LES service
   195  func (s *LesServer) Stop() error {
   196  	close(s.closeCh)
   197  
   198  	s.clientPool.Stop()
   199  	if s.serverset != nil {
   200  		s.serverset.close()
   201  	}
   202  	s.peers.close()
   203  	s.fcManager.Stop()
   204  	s.costTracker.stop()
   205  	s.handler.stop()
   206  	s.servingQueue.stop()
   207  	if s.vfluxServer != nil {
   208  		s.vfluxServer.Stop()
   209  	}
   210  
   211  	// Note, bloom trie indexer is closed by parent bloombits indexer.
   212  	if s.chtIndexer != nil {
   213  		s.chtIndexer.Close()
   214  	}
   215  	if s.lesDb != nil {
   216  		s.lesDb.Close()
   217  	}
   218  	s.wg.Wait()
   219  	log.Info("Les server stopped")
   220  
   221  	return nil
   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(uint64(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.minCapacity
   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(uint64(s.config.LightPeers), totalCapacity)
   277  		case <-s.closeCh:
   278  			return
   279  		}
   280  	}
   281  }