github.com/wzbox/go-ethereum@v1.9.2/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  	"sync"
    22  	"time"
    23  
    24  	"github.com/ethereum/go-ethereum/accounts/abi/bind"
    25  	"github.com/ethereum/go-ethereum/common"
    26  	"github.com/ethereum/go-ethereum/common/mclock"
    27  	"github.com/ethereum/go-ethereum/core"
    28  	"github.com/ethereum/go-ethereum/core/rawdb"
    29  	"github.com/ethereum/go-ethereum/core/types"
    30  	"github.com/ethereum/go-ethereum/eth"
    31  	"github.com/ethereum/go-ethereum/les/flowcontrol"
    32  	"github.com/ethereum/go-ethereum/light"
    33  	"github.com/ethereum/go-ethereum/log"
    34  	"github.com/ethereum/go-ethereum/p2p"
    35  	"github.com/ethereum/go-ethereum/p2p/discv5"
    36  	"github.com/ethereum/go-ethereum/p2p/enode"
    37  	"github.com/ethereum/go-ethereum/params"
    38  	"github.com/ethereum/go-ethereum/rpc"
    39  )
    40  
    41  const bufLimitRatio = 6000 // fixed bufLimit/MRR ratio
    42  
    43  type LesServer struct {
    44  	lesCommons
    45  
    46  	archiveMode bool // Flag whether the ethereum node runs in archive mode.
    47  
    48  	fcManager    *flowcontrol.ClientManager // nil if our node is client only
    49  	costTracker  *costTracker
    50  	testCost     uint64
    51  	defParams    flowcontrol.ServerParams
    52  	lesTopics    []discv5.Topic
    53  	privateKey   *ecdsa.PrivateKey
    54  	quitSync     chan struct{}
    55  	onlyAnnounce bool
    56  
    57  	thcNormal, thcBlockProcessing int // serving thread count for normal operation and block processing mode
    58  
    59  	maxPeers                                int
    60  	minCapacity, maxCapacity, freeClientCap uint64
    61  	clientPool                              *clientPool
    62  }
    63  
    64  func NewLesServer(e *eth.Ethereum, config *eth.Config) (*LesServer, error) {
    65  	lesTopics := make([]discv5.Topic, len(AdvertiseProtocolVersions))
    66  	for i, pv := range AdvertiseProtocolVersions {
    67  		lesTopics[i] = lesTopic(e.BlockChain().Genesis().Hash(), pv)
    68  	}
    69  	quitSync := make(chan struct{})
    70  	srv := &LesServer{
    71  		lesCommons: lesCommons{
    72  			config:           config,
    73  			iConfig:          light.DefaultServerIndexerConfig,
    74  			chainDb:          e.ChainDb(),
    75  			chtIndexer:       light.NewChtIndexer(e.ChainDb(), nil, params.CHTFrequency, params.HelperTrieProcessConfirmations),
    76  			bloomTrieIndexer: light.NewBloomTrieIndexer(e.ChainDb(), nil, params.BloomBitsBlocks, params.BloomTrieFrequency),
    77  		},
    78  		archiveMode:  e.ArchiveMode(),
    79  		quitSync:     quitSync,
    80  		lesTopics:    lesTopics,
    81  		onlyAnnounce: config.UltraLightOnlyAnnounce,
    82  	}
    83  	srv.costTracker, srv.minCapacity = newCostTracker(e.ChainDb(), config)
    84  
    85  	logger := log.New()
    86  	srv.thcNormal = config.LightServ * 4 / 100
    87  	if srv.thcNormal < 4 {
    88  		srv.thcNormal = 4
    89  	}
    90  	srv.thcBlockProcessing = config.LightServ/100 + 1
    91  	srv.fcManager = flowcontrol.NewClientManager(nil, &mclock.System{})
    92  
    93  	checkpoint := srv.latestLocalCheckpoint()
    94  	if !checkpoint.Empty() {
    95  		logger.Info("Loaded latest checkpoint", "section", checkpoint.SectionIndex, "head", checkpoint.SectionHead,
    96  			"chtroot", checkpoint.CHTRoot, "bloomroot", checkpoint.BloomRoot)
    97  	}
    98  
    99  	srv.chtIndexer.Start(e.BlockChain())
   100  
   101  	oracle := config.CheckpointOracle
   102  	if oracle == nil {
   103  		oracle = params.CheckpointOracles[e.BlockChain().Genesis().Hash()]
   104  	}
   105  	registrar := newCheckpointOracle(oracle, srv.getLocalCheckpoint)
   106  	// TODO(rjl493456442) Checkpoint is useless for les server, separate handler for client and server.
   107  	pm, err := NewProtocolManager(e.BlockChain().Config(), nil, light.DefaultServerIndexerConfig, config.UltraLightServers, config.UltraLightFraction, false, config.NetworkId, e.EventMux(), newPeerSet(), e.BlockChain(), e.TxPool(), e.ChainDb(), nil, nil, registrar, quitSync, new(sync.WaitGroup), e.Synced)
   108  	if err != nil {
   109  		return nil, err
   110  	}
   111  	srv.protocolManager = pm
   112  	pm.servingQueue = newServingQueue(int64(time.Millisecond*10), float64(config.LightServ)/100)
   113  	pm.server = srv
   114  
   115  	return srv, nil
   116  }
   117  
   118  func (s *LesServer) APIs() []rpc.API {
   119  	return []rpc.API{
   120  		{
   121  			Namespace: "les",
   122  			Version:   "1.0",
   123  			Service:   NewPrivateLightAPI(&s.lesCommons, s.protocolManager.reg),
   124  			Public:    false,
   125  		},
   126  	}
   127  }
   128  
   129  // startEventLoop starts an event handler loop that updates the recharge curve of
   130  // the client manager and adjusts the client pool's size according to the total
   131  // capacity updates coming from the client manager
   132  func (s *LesServer) startEventLoop() {
   133  	s.protocolManager.wg.Add(1)
   134  
   135  	var (
   136  		processing, procLast bool
   137  		procStarted          time.Time
   138  	)
   139  	blockProcFeed := make(chan bool, 100)
   140  	s.protocolManager.blockchain.(*core.BlockChain).SubscribeBlockProcessingEvent(blockProcFeed)
   141  	totalRechargeCh := make(chan uint64, 100)
   142  	totalRecharge := s.costTracker.subscribeTotalRecharge(totalRechargeCh)
   143  	totalCapacityCh := make(chan uint64, 100)
   144  	updateRecharge := func() {
   145  		if processing {
   146  			if !procLast {
   147  				procStarted = time.Now()
   148  			}
   149  			s.protocolManager.servingQueue.setThreads(s.thcBlockProcessing)
   150  			s.fcManager.SetRechargeCurve(flowcontrol.PieceWiseLinear{{0, 0}, {totalRecharge, totalRecharge}})
   151  		} else {
   152  			if procLast {
   153  				blockProcessingTimer.UpdateSince(procStarted)
   154  			}
   155  			s.protocolManager.servingQueue.setThreads(s.thcNormal)
   156  			s.fcManager.SetRechargeCurve(flowcontrol.PieceWiseLinear{{0, 0}, {totalRecharge / 16, totalRecharge / 2}, {totalRecharge / 2, totalRecharge / 2}, {totalRecharge, totalRecharge}})
   157  		}
   158  		procLast = processing
   159  	}
   160  	updateRecharge()
   161  	totalCapacity := s.fcManager.SubscribeTotalCapacity(totalCapacityCh)
   162  	s.clientPool.setLimits(s.maxPeers, totalCapacity)
   163  
   164  	var maxFreePeers uint64
   165  	go func() {
   166  		for {
   167  			select {
   168  			case processing = <-blockProcFeed:
   169  				updateRecharge()
   170  			case totalRecharge = <-totalRechargeCh:
   171  				updateRecharge()
   172  			case totalCapacity = <-totalCapacityCh:
   173  				totalCapacityGauge.Update(int64(totalCapacity))
   174  				newFreePeers := totalCapacity / s.freeClientCap
   175  				if newFreePeers < maxFreePeers && newFreePeers < uint64(s.maxPeers) {
   176  					log.Warn("Reduced total capacity", "maxFreePeers", newFreePeers)
   177  				}
   178  				maxFreePeers = newFreePeers
   179  				s.clientPool.setLimits(s.maxPeers, totalCapacity)
   180  			case <-s.protocolManager.quitSync:
   181  				s.protocolManager.wg.Done()
   182  				return
   183  			}
   184  		}
   185  	}()
   186  }
   187  
   188  func (s *LesServer) Protocols() []p2p.Protocol {
   189  	return s.makeProtocols(ServerProtocolVersions)
   190  }
   191  
   192  // Start starts the LES server
   193  func (s *LesServer) Start(srvr *p2p.Server) {
   194  	s.maxPeers = s.config.LightPeers
   195  	totalRecharge := s.costTracker.totalRecharge()
   196  	if s.maxPeers > 0 {
   197  		s.freeClientCap = s.minCapacity //totalRecharge / uint64(s.maxPeers)
   198  		if s.freeClientCap < s.minCapacity {
   199  			s.freeClientCap = s.minCapacity
   200  		}
   201  		if s.freeClientCap > 0 {
   202  			s.defParams = flowcontrol.ServerParams{
   203  				BufLimit:    s.freeClientCap * bufLimitRatio,
   204  				MinRecharge: s.freeClientCap,
   205  			}
   206  		}
   207  	}
   208  
   209  	s.maxCapacity = s.freeClientCap * uint64(s.maxPeers)
   210  	if totalRecharge > s.maxCapacity {
   211  		s.maxCapacity = totalRecharge
   212  	}
   213  	s.fcManager.SetCapacityLimits(s.freeClientCap, s.maxCapacity, s.freeClientCap*2)
   214  	s.clientPool = newClientPool(s.chainDb, s.freeClientCap, 10000, mclock.System{}, func(id enode.ID) { go s.protocolManager.removePeer(peerIdToString(id)) })
   215  	s.clientPool.setPriceFactors(priceFactors{0, 1, 1}, priceFactors{0, 1, 1})
   216  	s.protocolManager.peers.notify(s.clientPool)
   217  	s.startEventLoop()
   218  	s.protocolManager.Start(s.config.LightPeers)
   219  	if srvr.DiscV5 != nil {
   220  		for _, topic := range s.lesTopics {
   221  			topic := topic
   222  			go func() {
   223  				logger := log.New("topic", topic)
   224  				logger.Info("Starting topic registration")
   225  				defer logger.Info("Terminated topic registration")
   226  
   227  				srvr.DiscV5.RegisterTopic(topic, s.quitSync)
   228  			}()
   229  		}
   230  	}
   231  	s.privateKey = srvr.PrivateKey
   232  	s.protocolManager.blockLoop()
   233  }
   234  
   235  func (s *LesServer) SetBloomBitsIndexer(bloomIndexer *core.ChainIndexer) {
   236  	bloomIndexer.AddChildIndexer(s.bloomTrieIndexer)
   237  }
   238  
   239  // SetClient sets the rpc client and starts running checkpoint contract if it is not yet watched.
   240  func (s *LesServer) SetContractBackend(backend bind.ContractBackend) {
   241  	if s.protocolManager.reg != nil {
   242  		s.protocolManager.reg.start(backend)
   243  	}
   244  }
   245  
   246  // Stop stops the LES service
   247  func (s *LesServer) Stop() {
   248  	s.fcManager.Stop()
   249  	s.chtIndexer.Close()
   250  	// bloom trie indexer is closed by parent bloombits indexer
   251  	go func() {
   252  		<-s.protocolManager.noMorePeers
   253  	}()
   254  	s.clientPool.stop()
   255  	s.costTracker.stop()
   256  	s.protocolManager.Stop()
   257  }
   258  
   259  // todo(rjl493456442) separate client and server implementation.
   260  func (pm *ProtocolManager) blockLoop() {
   261  	pm.wg.Add(1)
   262  	headCh := make(chan core.ChainHeadEvent, 10)
   263  	headSub := pm.blockchain.SubscribeChainHeadEvent(headCh)
   264  	go func() {
   265  		var lastHead *types.Header
   266  		lastBroadcastTd := common.Big0
   267  		for {
   268  			select {
   269  			case ev := <-headCh:
   270  				peers := pm.peers.AllPeers()
   271  				if len(peers) > 0 {
   272  					header := ev.Block.Header()
   273  					hash := header.Hash()
   274  					number := header.Number.Uint64()
   275  					td := rawdb.ReadTd(pm.chainDb, hash, number)
   276  					if td != nil && td.Cmp(lastBroadcastTd) > 0 {
   277  						var reorg uint64
   278  						if lastHead != nil {
   279  							reorg = lastHead.Number.Uint64() - rawdb.FindCommonAncestor(pm.chainDb, header, lastHead).Number.Uint64()
   280  						}
   281  						lastHead = header
   282  						lastBroadcastTd = td
   283  
   284  						log.Debug("Announcing block to peers", "number", number, "hash", hash, "td", td, "reorg", reorg)
   285  
   286  						announce := announceData{Hash: hash, Number: number, Td: td, ReorgDepth: reorg}
   287  						var (
   288  							signed         bool
   289  							signedAnnounce announceData
   290  						)
   291  
   292  						for _, p := range peers {
   293  							p := p
   294  							switch p.announceType {
   295  							case announceTypeSimple:
   296  								p.queueSend(func() { p.SendAnnounce(announce) })
   297  							case announceTypeSigned:
   298  								if !signed {
   299  									signedAnnounce = announce
   300  									signedAnnounce.sign(pm.server.privateKey)
   301  									signed = true
   302  								}
   303  								p.queueSend(func() { p.SendAnnounce(signedAnnounce) })
   304  							}
   305  						}
   306  					}
   307  				}
   308  			case <-pm.quitSync:
   309  				headSub.Unsubscribe()
   310  				pm.wg.Done()
   311  				return
   312  			}
   313  		}
   314  	}()
   315  }