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