github.com/tdcblockchain/tdcblockchain@v0.0.0-20191111034745-805c65ade158/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/ethereum/go-ethereum/accounts/abi/bind"
    24  	"github.com/ethereum/go-ethereum/common/mclock"
    25  	"github.com/ethereum/go-ethereum/core"
    26  	"github.com/ethereum/go-ethereum/eth"
    27  	"github.com/ethereum/go-ethereum/les/flowcontrol"
    28  	"github.com/ethereum/go-ethereum/light"
    29  	"github.com/ethereum/go-ethereum/log"
    30  	"github.com/ethereum/go-ethereum/p2p"
    31  	"github.com/ethereum/go-ethereum/p2p/discv5"
    32  	"github.com/ethereum/go-ethereum/p2p/enode"
    33  	"github.com/ethereum/go-ethereum/params"
    34  	"github.com/ethereum/go-ethereum/rpc"
    35  )
    36  
    37  type LesServer struct {
    38  	lesCommons
    39  
    40  	archiveMode bool // Flag whether the ethereum node runs in archive mode.
    41  	handler     *serverHandler
    42  	lesTopics   []discv5.Topic
    43  	privateKey  *ecdsa.PrivateKey
    44  
    45  	// Flow control and capacity management
    46  	fcManager    *flowcontrol.ClientManager
    47  	costTracker  *costTracker
    48  	defParams    flowcontrol.ServerParams
    49  	servingQueue *servingQueue
    50  	clientPool   *clientPool
    51  
    52  	freeCapacity uint64 // The minimal client capacity used for free client.
    53  	threadsIdle  int    // Request serving threads count when system is idle.
    54  	threadsBusy  int    // Request serving threads count when system is busy(block insertion).
    55  }
    56  
    57  func NewLesServer(e *eth.Ethereum, config *eth.Config) (*LesServer, error) {
    58  	// Collect les protocol version information supported by local node.
    59  	lesTopics := make([]discv5.Topic, len(AdvertiseProtocolVersions))
    60  	for i, pv := range AdvertiseProtocolVersions {
    61  		lesTopics[i] = lesTopic(e.BlockChain().Genesis().Hash(), pv)
    62  	}
    63  	// Calculate the number of threads used to service the light client
    64  	// requests based on the user-specified value.
    65  	threads := config.LightServ * 4 / 100
    66  	if threads < 4 {
    67  		threads = 4
    68  	}
    69  	srv := &LesServer{
    70  		lesCommons: lesCommons{
    71  			genesis:          e.BlockChain().Genesis().Hash(),
    72  			config:           config,
    73  			chainConfig:      e.BlockChain().Config(),
    74  			iConfig:          light.DefaultServerIndexerConfig,
    75  			chainDb:          e.ChainDb(),
    76  			peers:            newPeerSet(),
    77  			chainReader:      e.BlockChain(),
    78  			chtIndexer:       light.NewChtIndexer(e.ChainDb(), nil, params.CHTFrequency, params.HelperTrieProcessConfirmations),
    79  			bloomTrieIndexer: light.NewBloomTrieIndexer(e.ChainDb(), nil, params.BloomBitsBlocks, params.BloomTrieFrequency),
    80  			closeCh:          make(chan struct{}),
    81  		},
    82  		archiveMode:  e.ArchiveMode(),
    83  		lesTopics:    lesTopics,
    84  		fcManager:    flowcontrol.NewClientManager(nil, &mclock.System{}),
    85  		servingQueue: newServingQueue(int64(time.Millisecond*10), float64(config.LightServ)/100),
    86  		threadsBusy:  config.LightServ/100 + 1,
    87  		threadsIdle:  threads,
    88  	}
    89  	srv.handler = newServerHandler(srv, e.BlockChain(), e.ChainDb(), e.TxPool(), e.Synced)
    90  	srv.costTracker, srv.freeCapacity = newCostTracker(e.ChainDb(), config)
    91  
    92  	// Set up checkpoint oracle.
    93  	oracle := config.CheckpointOracle
    94  	if oracle == nil {
    95  		oracle = params.CheckpointOracles[e.BlockChain().Genesis().Hash()]
    96  	}
    97  	srv.oracle = newCheckpointOracle(oracle, srv.localCheckpoint)
    98  
    99  	// Initialize server capacity management fields.
   100  	srv.defParams = flowcontrol.ServerParams{
   101  		BufLimit:    srv.freeCapacity * bufLimitRatio,
   102  		MinRecharge: srv.freeCapacity,
   103  	}
   104  	// LES flow control tries to more or less guarantee the possibility for the
   105  	// clients to send a certain amount of requests at any time and get a quick
   106  	// response. Most of the clients want this guarantee but don't actually need
   107  	// to send requests most of the time. Our goal is to serve as many clients as
   108  	// possible while the actually used server capacity does not exceed the limits
   109  	totalRecharge := srv.costTracker.totalRecharge()
   110  	maxCapacity := srv.freeCapacity * uint64(srv.config.LightPeers)
   111  	if totalRecharge > maxCapacity {
   112  		maxCapacity = totalRecharge
   113  	}
   114  	srv.fcManager.SetCapacityLimits(srv.freeCapacity, maxCapacity, srv.freeCapacity*2)
   115  	srv.clientPool = newClientPool(srv.chainDb, srv.freeCapacity, 10000, mclock.System{}, func(id enode.ID) { go srv.peers.Unregister(peerIdToString(id)) })
   116  
   117  	checkpoint := srv.latestLocalCheckpoint()
   118  	if !checkpoint.Empty() {
   119  		log.Info("Loaded latest checkpoint", "section", checkpoint.SectionIndex, "head", checkpoint.SectionHead,
   120  			"chtroot", checkpoint.CHTRoot, "bloomroot", checkpoint.BloomRoot)
   121  	}
   122  	srv.chtIndexer.Start(e.BlockChain())
   123  	return srv, nil
   124  }
   125  
   126  func (s *LesServer) APIs() []rpc.API {
   127  	return []rpc.API{
   128  		{
   129  			Namespace: "les",
   130  			Version:   "1.0",
   131  			Service:   NewPrivateLightAPI(&s.lesCommons),
   132  			Public:    false,
   133  		},
   134  	}
   135  }
   136  
   137  func (s *LesServer) Protocols() []p2p.Protocol {
   138  	return s.makeProtocols(ServerProtocolVersions, s.handler.runPeer, func(id enode.ID) interface{} {
   139  		if p := s.peers.Peer(peerIdToString(id)); p != nil {
   140  			return p.Info()
   141  		}
   142  		return nil
   143  	})
   144  }
   145  
   146  // Start starts the LES server
   147  func (s *LesServer) Start(srvr *p2p.Server) {
   148  	s.privateKey = srvr.PrivateKey
   149  	s.handler.start()
   150  
   151  	s.wg.Add(1)
   152  	go s.capacityManagement()
   153  
   154  	if srvr.DiscV5 != nil {
   155  		for _, topic := range s.lesTopics {
   156  			topic := topic
   157  			go func() {
   158  				logger := log.New("topic", topic)
   159  				logger.Info("Starting topic registration")
   160  				defer logger.Info("Terminated topic registration")
   161  
   162  				srvr.DiscV5.RegisterTopic(topic, s.closeCh)
   163  			}()
   164  		}
   165  	}
   166  }
   167  
   168  // Stop stops the LES service
   169  func (s *LesServer) Stop() {
   170  	close(s.closeCh)
   171  
   172  	// Disconnect existing sessions.
   173  	// This also closes the gate for any new registrations on the peer set.
   174  	// sessions which are already established but not added to pm.peers yet
   175  	// will exit when they try to register.
   176  	s.peers.Close()
   177  
   178  	s.fcManager.Stop()
   179  	s.clientPool.stop()
   180  	s.costTracker.stop()
   181  	s.handler.stop()
   182  	s.servingQueue.stop()
   183  
   184  	// Note, bloom trie indexer is closed by parent bloombits indexer.
   185  	s.chtIndexer.Close()
   186  	s.wg.Wait()
   187  	log.Info("Les server stopped")
   188  }
   189  
   190  func (s *LesServer) SetBloomBitsIndexer(bloomIndexer *core.ChainIndexer) {
   191  	bloomIndexer.AddChildIndexer(s.bloomTrieIndexer)
   192  }
   193  
   194  // SetClient sets the rpc client and starts running checkpoint contract if it is not yet watched.
   195  func (s *LesServer) SetContractBackend(backend bind.ContractBackend) {
   196  	if s.oracle == nil {
   197  		return
   198  	}
   199  	s.oracle.start(backend)
   200  }
   201  
   202  // capacityManagement starts an event handler loop that updates the recharge curve of
   203  // the client manager and adjusts the client pool's size according to the total
   204  // capacity updates coming from the client manager
   205  func (s *LesServer) capacityManagement() {
   206  	defer s.wg.Done()
   207  
   208  	processCh := make(chan bool, 100)
   209  	sub := s.handler.blockchain.SubscribeBlockProcessingEvent(processCh)
   210  	defer sub.Unsubscribe()
   211  
   212  	totalRechargeCh := make(chan uint64, 100)
   213  	totalRecharge := s.costTracker.subscribeTotalRecharge(totalRechargeCh)
   214  
   215  	totalCapacityCh := make(chan uint64, 100)
   216  	totalCapacity := s.fcManager.SubscribeTotalCapacity(totalCapacityCh)
   217  	s.clientPool.setLimits(s.config.LightPeers, totalCapacity)
   218  
   219  	var (
   220  		busy         bool
   221  		freePeers    uint64
   222  		blockProcess mclock.AbsTime
   223  	)
   224  	updateRecharge := func() {
   225  		if busy {
   226  			s.servingQueue.setThreads(s.threadsBusy)
   227  			s.fcManager.SetRechargeCurve(flowcontrol.PieceWiseLinear{{0, 0}, {totalRecharge, totalRecharge}})
   228  		} else {
   229  			s.servingQueue.setThreads(s.threadsIdle)
   230  			s.fcManager.SetRechargeCurve(flowcontrol.PieceWiseLinear{{0, 0}, {totalRecharge / 10, totalRecharge}, {totalRecharge, totalRecharge}})
   231  		}
   232  	}
   233  	updateRecharge()
   234  
   235  	for {
   236  		select {
   237  		case busy = <-processCh:
   238  			if busy {
   239  				blockProcess = mclock.Now()
   240  			} else {
   241  				blockProcessingTimer.Update(time.Duration(mclock.Now() - blockProcess))
   242  			}
   243  			updateRecharge()
   244  		case totalRecharge = <-totalRechargeCh:
   245  			totalRechargeGauge.Update(int64(totalRecharge))
   246  			updateRecharge()
   247  		case totalCapacity = <-totalCapacityCh:
   248  			totalCapacityGauge.Update(int64(totalCapacity))
   249  			newFreePeers := totalCapacity / s.freeCapacity
   250  			if newFreePeers < freePeers && newFreePeers < uint64(s.config.LightPeers) {
   251  				log.Warn("Reduced free peer connections", "from", freePeers, "to", newFreePeers)
   252  			}
   253  			freePeers = newFreePeers
   254  			s.clientPool.setLimits(s.config.LightPeers, totalCapacity)
   255  		case <-s.closeCh:
   256  			return
   257  		}
   258  	}
   259  }