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