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