github.com/ethw3/go-ethereuma@v0.0.0-20221013053120-c14602a4c23c/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/ethw3/go-ethereuma/common/mclock" 24 "github.com/ethw3/go-ethereuma/core" 25 "github.com/ethw3/go-ethereuma/eth/ethconfig" 26 "github.com/ethw3/go-ethereuma/ethdb" 27 "github.com/ethw3/go-ethereuma/les/flowcontrol" 28 vfs "github.com/ethw3/go-ethereuma/les/vflux/server" 29 "github.com/ethw3/go-ethereuma/light" 30 "github.com/ethw3/go-ethereuma/log" 31 "github.com/ethw3/go-ethereuma/node" 32 "github.com/ethw3/go-ethereuma/p2p" 33 "github.com/ethw3/go-ethereuma/p2p/enode" 34 "github.com/ethw3/go-ethereuma/p2p/enr" 35 "github.com/ethw3/go-ethereuma/params" 36 "github.com/ethw3/go-ethereuma/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 Service: NewLightAPI(&s.lesCommons), 163 }, 164 { 165 Namespace: "les", 166 Service: NewLightServerAPI(s), 167 }, 168 { 169 Namespace: "debug", 170 Service: NewDebugAPI(s), 171 }, 172 } 173 } 174 175 func (s *LesServer) Protocols() []p2p.Protocol { 176 ps := s.makeProtocols(ServerProtocolVersions, s.handler.runPeer, func(id enode.ID) interface{} { 177 if p := s.peers.peer(id); p != nil { 178 return p.Info() 179 } 180 return nil 181 }, nil) 182 // Add "les" ENR entries. 183 for i := range ps { 184 ps[i].Attributes = []enr.Entry{&lesEntry{ 185 VfxVersion: 1, 186 }} 187 } 188 return ps 189 } 190 191 // Start starts the LES server 192 func (s *LesServer) Start() error { 193 s.privateKey = s.p2pSrv.PrivateKey 194 s.peers.setSignerKey(s.privateKey) 195 s.handler.start() 196 s.wg.Add(1) 197 go s.capacityManagement() 198 if s.p2pSrv.DiscV5 != nil { 199 s.p2pSrv.DiscV5.RegisterTalkHandler("vfx", s.vfluxServer.ServeEncoded) 200 } 201 return nil 202 } 203 204 // Stop stops the LES service 205 func (s *LesServer) Stop() error { 206 close(s.closeCh) 207 208 s.clientPool.Stop() 209 if s.serverset != nil { 210 s.serverset.close() 211 } 212 s.peers.close() 213 s.fcManager.Stop() 214 s.costTracker.stop() 215 s.handler.stop() 216 s.servingQueue.stop() 217 if s.vfluxServer != nil { 218 s.vfluxServer.Stop() 219 } 220 221 // Note, bloom trie indexer is closed by parent bloombits indexer. 222 if s.chtIndexer != nil { 223 s.chtIndexer.Close() 224 } 225 if s.lesDb != nil { 226 s.lesDb.Close() 227 } 228 s.wg.Wait() 229 log.Info("Les server stopped") 230 231 return nil 232 } 233 234 // capacityManagement starts an event handler loop that updates the recharge curve of 235 // the client manager and adjusts the client pool's size according to the total 236 // capacity updates coming from the client manager 237 func (s *LesServer) capacityManagement() { 238 defer s.wg.Done() 239 240 processCh := make(chan bool, 100) 241 sub := s.handler.blockchain.SubscribeBlockProcessingEvent(processCh) 242 defer sub.Unsubscribe() 243 244 totalRechargeCh := make(chan uint64, 100) 245 totalRecharge := s.costTracker.subscribeTotalRecharge(totalRechargeCh) 246 247 totalCapacityCh := make(chan uint64, 100) 248 totalCapacity := s.fcManager.SubscribeTotalCapacity(totalCapacityCh) 249 s.clientPool.SetLimits(uint64(s.config.LightPeers), totalCapacity) 250 251 var ( 252 busy bool 253 freePeers uint64 254 blockProcess mclock.AbsTime 255 ) 256 updateRecharge := func() { 257 if busy { 258 s.servingQueue.setThreads(s.threadsBusy) 259 s.fcManager.SetRechargeCurve(flowcontrol.PieceWiseLinear{{0, 0}, {totalRecharge, totalRecharge}}) 260 } else { 261 s.servingQueue.setThreads(s.threadsIdle) 262 s.fcManager.SetRechargeCurve(flowcontrol.PieceWiseLinear{{0, 0}, {totalRecharge / 10, totalRecharge}, {totalRecharge, totalRecharge}}) 263 } 264 } 265 updateRecharge() 266 267 for { 268 select { 269 case busy = <-processCh: 270 if busy { 271 blockProcess = mclock.Now() 272 } else { 273 blockProcessingTimer.Update(time.Duration(mclock.Now() - blockProcess)) 274 } 275 updateRecharge() 276 case totalRecharge = <-totalRechargeCh: 277 totalRechargeGauge.Update(int64(totalRecharge)) 278 updateRecharge() 279 case totalCapacity = <-totalCapacityCh: 280 totalCapacityGauge.Update(int64(totalCapacity)) 281 newFreePeers := totalCapacity / s.minCapacity 282 if newFreePeers < freePeers && newFreePeers < uint64(s.config.LightPeers) { 283 log.Warn("Reduced free peer connections", "from", freePeers, "to", newFreePeers) 284 } 285 freePeers = newFreePeers 286 s.clientPool.SetLimits(uint64(s.config.LightPeers), totalCapacity) 287 case <-s.closeCh: 288 return 289 } 290 } 291 }