github.com/hardtosaygoodbye/go-ethereum@v1.10.16-0.20220122011429-97003b9e6c15/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/hardtosaygoodbye/go-ethereum/common/mclock" 24 "github.com/hardtosaygoodbye/go-ethereum/core" 25 "github.com/hardtosaygoodbye/go-ethereum/eth/ethconfig" 26 "github.com/hardtosaygoodbye/go-ethereum/ethdb" 27 "github.com/hardtosaygoodbye/go-ethereum/les/flowcontrol" 28 vfs "github.com/hardtosaygoodbye/go-ethereum/les/vflux/server" 29 "github.com/hardtosaygoodbye/go-ethereum/light" 30 "github.com/hardtosaygoodbye/go-ethereum/log" 31 "github.com/hardtosaygoodbye/go-ethereum/node" 32 "github.com/hardtosaygoodbye/go-ethereum/p2p" 33 "github.com/hardtosaygoodbye/go-ethereum/p2p/enode" 34 "github.com/hardtosaygoodbye/go-ethereum/p2p/enr" 35 "github.com/hardtosaygoodbye/go-ethereum/params" 36 "github.com/hardtosaygoodbye/go-ethereum/rpc" 37 ) 38 39 var ( 40 defaultPosFactors = vfs.PriceFactors{TimeFactor: 0, CapacityFactor: 1, RequestFactor: 1} 41 defaultNegFactors = vfs.PriceFactors{TimeFactor: 0, CapacityFactor: 1, RequestFactor: 1} 42 ) 43 44 const defaultConnectedBias = time.Minute * 3 45 46 type ethBackend interface { 47 ArchiveMode() bool 48 BlockChain() *core.BlockChain 49 BloomIndexer() *core.ChainIndexer 50 ChainDb() ethdb.Database 51 Synced() bool 52 TxPool() *core.TxPool 53 } 54 55 type LesServer struct { 56 lesCommons 57 58 archiveMode bool // Flag whether the ethereum node runs in archive mode. 59 handler *serverHandler 60 peers *clientPeerSet 61 serverset *serverSet 62 vfluxServer *vfs.Server 63 privateKey *ecdsa.PrivateKey 64 65 // Flow control and capacity management 66 fcManager *flowcontrol.ClientManager 67 costTracker *costTracker 68 defParams flowcontrol.ServerParams 69 servingQueue *servingQueue 70 clientPool *vfs.ClientPool 71 72 minCapacity, maxCapacity uint64 73 threadsIdle int // Request serving threads count when system is idle. 74 threadsBusy int // Request serving threads count when system is busy(block insertion). 75 76 p2pSrv *p2p.Server 77 } 78 79 func NewLesServer(node *node.Node, e ethBackend, config *ethconfig.Config) (*LesServer, error) { 80 lesDb, err := node.OpenDatabase("les.server", 0, 0, "eth/db/lesserver/", false) 81 if err != nil { 82 return nil, err 83 } 84 // Calculate the number of threads used to service the light client 85 // requests based on the user-specified value. 86 threads := config.LightServ * 4 / 100 87 if threads < 4 { 88 threads = 4 89 } 90 srv := &LesServer{ 91 lesCommons: lesCommons{ 92 genesis: e.BlockChain().Genesis().Hash(), 93 config: config, 94 chainConfig: e.BlockChain().Config(), 95 iConfig: light.DefaultServerIndexerConfig, 96 chainDb: e.ChainDb(), 97 lesDb: lesDb, 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 archiveMode: e.ArchiveMode(), 104 peers: newClientPeerSet(), 105 serverset: newServerSet(), 106 vfluxServer: vfs.NewServer(time.Millisecond * 10), 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 issync := e.Synced 114 if config.LightNoSyncServe { 115 issync = func() bool { return true } 116 } 117 srv.handler = newServerHandler(srv, e.BlockChain(), e.ChainDb(), e.TxPool(), issync) 118 srv.costTracker, srv.minCapacity = newCostTracker(e.ChainDb(), config) 119 srv.oracle = srv.setupOracle(node, e.BlockChain().Genesis().Hash(), config) 120 121 // Initialize the bloom trie indexer. 122 e.BloomIndexer().AddChildIndexer(srv.bloomTrieIndexer) 123 124 // Initialize server capacity management fields. 125 srv.defParams = flowcontrol.ServerParams{ 126 BufLimit: srv.minCapacity * bufLimitRatio, 127 MinRecharge: srv.minCapacity, 128 } 129 // LES flow control tries to more or less guarantee the possibility for the 130 // clients to send a certain amount of requests at any time and get a quick 131 // response. Most of the clients want this guarantee but don't actually need 132 // to send requests most of the time. Our goal is to serve as many clients as 133 // possible while the actually used server capacity does not exceed the limits 134 totalRecharge := srv.costTracker.totalRecharge() 135 srv.maxCapacity = srv.minCapacity * uint64(srv.config.LightPeers) 136 if totalRecharge > srv.maxCapacity { 137 srv.maxCapacity = totalRecharge 138 } 139 srv.fcManager.SetCapacityLimits(srv.minCapacity, srv.maxCapacity, srv.minCapacity*2) 140 srv.clientPool = vfs.NewClientPool(lesDb, srv.minCapacity, defaultConnectedBias, mclock.System{}, issync) 141 srv.clientPool.Start() 142 srv.clientPool.SetDefaultFactors(defaultPosFactors, defaultNegFactors) 143 srv.vfluxServer.Register(srv.clientPool, "les", "Ethereum light client service") 144 145 checkpoint := srv.latestLocalCheckpoint() 146 if !checkpoint.Empty() { 147 log.Info("Loaded latest checkpoint", "section", checkpoint.SectionIndex, "head", checkpoint.SectionHead, 148 "chtroot", checkpoint.CHTRoot, "bloomroot", checkpoint.BloomRoot) 149 } 150 srv.chtIndexer.Start(e.BlockChain()) 151 152 node.RegisterProtocols(srv.Protocols()) 153 node.RegisterAPIs(srv.APIs()) 154 node.RegisterLifecycle(srv) 155 return srv, nil 156 } 157 158 func (s *LesServer) APIs() []rpc.API { 159 return []rpc.API{ 160 { 161 Namespace: "les", 162 Version: "1.0", 163 Service: NewPrivateLightAPI(&s.lesCommons), 164 Public: false, 165 }, 166 { 167 Namespace: "les", 168 Version: "1.0", 169 Service: NewPrivateLightServerAPI(s), 170 Public: false, 171 }, 172 { 173 Namespace: "debug", 174 Version: "1.0", 175 Service: NewPrivateDebugAPI(s), 176 Public: false, 177 }, 178 } 179 } 180 181 func (s *LesServer) Protocols() []p2p.Protocol { 182 ps := s.makeProtocols(ServerProtocolVersions, s.handler.runPeer, func(id enode.ID) interface{} { 183 if p := s.peers.peer(id); p != nil { 184 return p.Info() 185 } 186 return nil 187 }, nil) 188 // Add "les" ENR entries. 189 for i := range ps { 190 ps[i].Attributes = []enr.Entry{&lesEntry{ 191 VfxVersion: 1, 192 }} 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.peers.setSignerKey(s.privateKey) 201 s.handler.start() 202 s.wg.Add(1) 203 go s.capacityManagement() 204 if s.p2pSrv.DiscV5 != nil { 205 s.p2pSrv.DiscV5.RegisterTalkHandler("vfx", s.vfluxServer.ServeEncoded) 206 } 207 return nil 208 } 209 210 // Stop stops the LES service 211 func (s *LesServer) Stop() error { 212 close(s.closeCh) 213 214 s.clientPool.Stop() 215 if s.serverset != nil { 216 s.serverset.close() 217 } 218 s.peers.close() 219 s.fcManager.Stop() 220 s.costTracker.stop() 221 s.handler.stop() 222 s.servingQueue.stop() 223 if s.vfluxServer != nil { 224 s.vfluxServer.Stop() 225 } 226 227 // Note, bloom trie indexer is closed by parent bloombits indexer. 228 if s.chtIndexer != nil { 229 s.chtIndexer.Close() 230 } 231 if s.lesDb != nil { 232 s.lesDb.Close() 233 } 234 s.wg.Wait() 235 log.Info("Les server stopped") 236 237 return nil 238 } 239 240 // capacityManagement starts an event handler loop that updates the recharge curve of 241 // the client manager and adjusts the client pool's size according to the total 242 // capacity updates coming from the client manager 243 func (s *LesServer) capacityManagement() { 244 defer s.wg.Done() 245 246 processCh := make(chan bool, 100) 247 sub := s.handler.blockchain.SubscribeBlockProcessingEvent(processCh) 248 defer sub.Unsubscribe() 249 250 totalRechargeCh := make(chan uint64, 100) 251 totalRecharge := s.costTracker.subscribeTotalRecharge(totalRechargeCh) 252 253 totalCapacityCh := make(chan uint64, 100) 254 totalCapacity := s.fcManager.SubscribeTotalCapacity(totalCapacityCh) 255 s.clientPool.SetLimits(uint64(s.config.LightPeers), totalCapacity) 256 257 var ( 258 busy bool 259 freePeers uint64 260 blockProcess mclock.AbsTime 261 ) 262 updateRecharge := func() { 263 if busy { 264 s.servingQueue.setThreads(s.threadsBusy) 265 s.fcManager.SetRechargeCurve(flowcontrol.PieceWiseLinear{{0, 0}, {totalRecharge, totalRecharge}}) 266 } else { 267 s.servingQueue.setThreads(s.threadsIdle) 268 s.fcManager.SetRechargeCurve(flowcontrol.PieceWiseLinear{{0, 0}, {totalRecharge / 10, totalRecharge}, {totalRecharge, totalRecharge}}) 269 } 270 } 271 updateRecharge() 272 273 for { 274 select { 275 case busy = <-processCh: 276 if busy { 277 blockProcess = mclock.Now() 278 } else { 279 blockProcessingTimer.Update(time.Duration(mclock.Now() - blockProcess)) 280 } 281 updateRecharge() 282 case totalRecharge = <-totalRechargeCh: 283 totalRechargeGauge.Update(int64(totalRecharge)) 284 updateRecharge() 285 case totalCapacity = <-totalCapacityCh: 286 totalCapacityGauge.Update(int64(totalCapacity)) 287 newFreePeers := totalCapacity / s.minCapacity 288 if newFreePeers < freePeers && newFreePeers < uint64(s.config.LightPeers) { 289 log.Warn("Reduced free peer connections", "from", freePeers, "to", newFreePeers) 290 } 291 freePeers = newFreePeers 292 s.clientPool.SetLimits(uint64(s.config.LightPeers), totalCapacity) 293 case <-s.closeCh: 294 return 295 } 296 } 297 }