github.com/core-coin/go-core/v2@v2.1.9/les/server.go (about)

     1  // Copyright 2016 by the Authors
     2  // This file is part of the go-core library.
     3  //
     4  // The go-core 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-core 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-core library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package les
    18  
    19  import (
    20  	"reflect"
    21  	"time"
    22  
    23  	"github.com/core-coin/go-core/v2/common/mclock"
    24  	"github.com/core-coin/go-core/v2/crypto"
    25  	"github.com/core-coin/go-core/v2/les/flowcontrol"
    26  	lps "github.com/core-coin/go-core/v2/les/lespay/server"
    27  	"github.com/core-coin/go-core/v2/light"
    28  	"github.com/core-coin/go-core/v2/log"
    29  	"github.com/core-coin/go-core/v2/node"
    30  	"github.com/core-coin/go-core/v2/p2p"
    31  	"github.com/core-coin/go-core/v2/p2p/discv5"
    32  	"github.com/core-coin/go-core/v2/p2p/enode"
    33  	"github.com/core-coin/go-core/v2/p2p/enr"
    34  	"github.com/core-coin/go-core/v2/p2p/nodestate"
    35  	"github.com/core-coin/go-core/v2/params"
    36  	"github.com/core-coin/go-core/v2/rpc"
    37  	"github.com/core-coin/go-core/v2/xcb"
    38  )
    39  
    40  var (
    41  	serverSetup         = &nodestate.Setup{}
    42  	clientPeerField     = serverSetup.NewField("clientPeer", reflect.TypeOf(&clientPeer{}))
    43  	clientInfoField     = serverSetup.NewField("clientInfo", reflect.TypeOf(&clientInfo{}))
    44  	connAddressField    = serverSetup.NewField("connAddr", reflect.TypeOf(""))
    45  	balanceTrackerSetup = lps.NewBalanceTrackerSetup(serverSetup)
    46  	priorityPoolSetup   = lps.NewPriorityPoolSetup(serverSetup)
    47  )
    48  
    49  func init() {
    50  	balanceTrackerSetup.Connect(connAddressField, priorityPoolSetup.CapacityField)
    51  	priorityPoolSetup.Connect(balanceTrackerSetup.BalanceField, balanceTrackerSetup.UpdateFlag) // NodeBalance implements nodePriority
    52  }
    53  
    54  type LesServer struct {
    55  	lesCommons
    56  
    57  	ns          *nodestate.NodeStateMachine
    58  	archiveMode bool // Flag whether the core node runs in archive mode.
    59  	handler     *serverHandler
    60  	broadcaster *broadcaster
    61  	lesTopics   []discv5.Topic
    62  	privateKey  *crypto.PrivateKey
    63  
    64  	// Flow control and capacity management
    65  	fcManager    *flowcontrol.ClientManager
    66  	costTracker  *costTracker
    67  	defParams    flowcontrol.ServerParams
    68  	servingQueue *servingQueue
    69  	clientPool   *clientPool
    70  
    71  	minCapacity, maxCapacity uint64
    72  	threadsIdle              int // Request serving threads count when system is idle.
    73  	threadsBusy              int // Request serving threads count when system is busy(block insertion).
    74  
    75  	p2pSrv *p2p.Server
    76  }
    77  
    78  func NewLesServer(node *node.Node, e *xcb.Core, config *xcb.Config) (*LesServer, error) {
    79  	ns := nodestate.NewNodeStateMachine(nil, nil, mclock.System{}, serverSetup)
    80  	// Collect les protocol version information supported by local node.
    81  	lesTopics := make([]discv5.Topic, len(AdvertiseProtocolVersions))
    82  	for i, pv := range AdvertiseProtocolVersions {
    83  		lesTopics[i] = lesTopic(e.BlockChain().Genesis().Hash(), pv)
    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  			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  		ns:           ns,
   104  		archiveMode:  e.ArchiveMode(),
   105  		broadcaster:  newBroadcaster(ns),
   106  		lesTopics:    lesTopics,
   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  	srv.handler = newServerHandler(srv, e.BlockChain(), e.ChainDb(), e.TxPool(), e.Synced)
   114  	srv.costTracker, srv.minCapacity = newCostTracker(e.ChainDb(), config)
   115  	srv.oracle = srv.setupOracle(node, e.BlockChain().Genesis().Hash(), config)
   116  
   117  	// Initialize the bloom trie indexer.
   118  	e.BloomIndexer().AddChildIndexer(srv.bloomTrieIndexer)
   119  
   120  	// Initialize server capacity management fields.
   121  	srv.defParams = flowcontrol.ServerParams{
   122  		BufLimit:    srv.minCapacity * bufLimitRatio,
   123  		MinRecharge: srv.minCapacity,
   124  	}
   125  	// LES flow control tries to more or less guarantee the possibility for the
   126  	// clients to send a certain amount of requests at any time and get a quick
   127  	// response. Most of the clients want this guarantee but don't actually need
   128  	// to send requests most of the time. Our goal is to serve as many clients as
   129  	// possible while the actually used server capacity does not exceed the limits
   130  	totalRecharge := srv.costTracker.totalRecharge()
   131  	srv.maxCapacity = srv.minCapacity * uint64(srv.config.LightPeers)
   132  	if totalRecharge > srv.maxCapacity {
   133  		srv.maxCapacity = totalRecharge
   134  	}
   135  	srv.fcManager.SetCapacityLimits(srv.minCapacity, srv.maxCapacity, srv.minCapacity*2)
   136  	srv.clientPool = newClientPool(ns, srv.chainDb, srv.minCapacity, defaultConnectedBias, mclock.System{}, srv.dropClient)
   137  	srv.clientPool.setDefaultFactors(lps.PriceFactors{TimeFactor: 0, CapacityFactor: 1, RequestFactor: 1}, lps.PriceFactors{TimeFactor: 0, CapacityFactor: 1, RequestFactor: 1})
   138  
   139  	checkpoint := srv.latestLocalCheckpoint()
   140  	if !checkpoint.Empty() {
   141  		log.Info("Loaded latest checkpoint", "section", checkpoint.SectionIndex, "head", checkpoint.SectionHead,
   142  			"chtroot", checkpoint.CHTRoot, "bloomroot", checkpoint.BloomRoot)
   143  	}
   144  	srv.chtIndexer.Start(e.BlockChain())
   145  
   146  	node.RegisterProtocols(srv.Protocols())
   147  	node.RegisterAPIs(srv.APIs())
   148  	node.RegisterLifecycle(srv)
   149  
   150  	// disconnect all peers at nsm shutdown
   151  	ns.SubscribeField(clientPeerField, func(node *enode.Node, state nodestate.Flags, oldValue, newValue interface{}) {
   152  		if state.Equals(serverSetup.OfflineFlag()) && oldValue != nil {
   153  			oldValue.(*clientPeer).Peer.Disconnect(p2p.DiscRequested)
   154  		}
   155  	})
   156  	ns.Start()
   157  	return srv, nil
   158  }
   159  
   160  func (s *LesServer) APIs() []rpc.API {
   161  	return []rpc.API{
   162  		{
   163  			Namespace: "les",
   164  			Version:   "1.0",
   165  			Service:   NewPrivateLightAPI(&s.lesCommons),
   166  			Public:    false,
   167  		},
   168  		{
   169  			Namespace: "les",
   170  			Version:   "1.0",
   171  			Service:   NewPrivateLightServerAPI(s),
   172  			Public:    false,
   173  		},
   174  		{
   175  			Namespace: "debug",
   176  			Version:   "1.0",
   177  			Service:   NewPrivateDebugAPI(s),
   178  			Public:    false,
   179  		},
   180  	}
   181  }
   182  
   183  func (s *LesServer) Protocols() []p2p.Protocol {
   184  	ps := s.makeProtocols(ServerProtocolVersions, s.handler.runPeer, func(id enode.ID) interface{} {
   185  		if p := s.getClient(id); p != nil {
   186  			return p.Info()
   187  		}
   188  		return nil
   189  	}, nil)
   190  	// Add "les" ENR entries.
   191  	for i := range ps {
   192  		ps[i].Attributes = []enr.Entry{&lesEntry{}}
   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.broadcaster.setSignerKey(s.privateKey)
   201  	s.handler.start()
   202  
   203  	s.wg.Add(1)
   204  	go s.capacityManagement()
   205  
   206  	if s.p2pSrv.DiscV5 != nil {
   207  		for _, topic := range s.lesTopics {
   208  			topic := topic
   209  			go func() {
   210  				logger := log.New("topic", topic)
   211  				logger.Info("Starting topic registration")
   212  				defer logger.Info("Terminated topic registration")
   213  
   214  				s.p2pSrv.DiscV5.RegisterTopic(topic, s.closeCh)
   215  			}()
   216  		}
   217  	}
   218  
   219  	return nil
   220  }
   221  
   222  // Stop stops the LES service
   223  func (s *LesServer) Stop() error {
   224  	close(s.closeCh)
   225  
   226  	s.clientPool.stop()
   227  	s.ns.Stop()
   228  	s.fcManager.Stop()
   229  	s.costTracker.stop()
   230  	s.handler.stop()
   231  	s.servingQueue.stop()
   232  
   233  	// Note, bloom trie indexer is closed by parent bloombits indexer.
   234  	s.chtIndexer.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  }