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