github.com/theQRL/go-zond@v0.1.1/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/theQRL/go-zond/common/mclock" 24 "github.com/theQRL/go-zond/core" 25 "github.com/theQRL/go-zond/core/txpool" 26 "github.com/theQRL/go-zond/zond/ethconfig" 27 "github.com/theQRL/go-zond/zonddb" 28 "github.com/theQRL/go-zond/les/flowcontrol" 29 vfs "github.com/theQRL/go-zond/les/vflux/server" 30 "github.com/theQRL/go-zond/light" 31 "github.com/theQRL/go-zond/log" 32 "github.com/theQRL/go-zond/node" 33 "github.com/theQRL/go-zond/p2p" 34 "github.com/theQRL/go-zond/p2p/enode" 35 "github.com/theQRL/go-zond/p2p/enr" 36 "github.com/theQRL/go-zond/params" 37 "github.com/theQRL/go-zond/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() zonddb.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, "zond/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 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 srv.chtIndexer.Start(e.BlockChain()) 145 146 node.RegisterProtocols(srv.Protocols()) 147 node.RegisterAPIs(srv.APIs()) 148 node.RegisterLifecycle(srv) 149 return srv, nil 150 } 151 152 func (s *LesServer) APIs() []rpc.API { 153 return []rpc.API{ 154 { 155 Namespace: "les", 156 Service: NewLightServerAPI(s), 157 }, 158 { 159 Namespace: "debug", 160 Service: NewDebugAPI(s), 161 }, 162 } 163 } 164 165 func (s *LesServer) Protocols() []p2p.Protocol { 166 ps := s.makeProtocols(ServerProtocolVersions, s.handler.runPeer, func(id enode.ID) interface{} { 167 if p := s.peers.peer(id); p != nil { 168 return p.Info() 169 } 170 return nil 171 }, nil) 172 // Add "les" ENR entries. 173 for i := range ps { 174 ps[i].Attributes = []enr.Entry{&lesEntry{ 175 VfxVersion: 1, 176 }} 177 } 178 return ps 179 } 180 181 // Start starts the LES server 182 func (s *LesServer) Start() error { 183 s.privateKey = s.p2pSrv.PrivateKey 184 s.peers.setSignerKey(s.privateKey) 185 s.handler.start() 186 s.wg.Add(1) 187 go s.capacityManagement() 188 if s.p2pSrv.DiscV5 != nil { 189 s.p2pSrv.DiscV5.RegisterTalkHandler("vfx", s.vfluxServer.ServeEncoded) 190 } 191 return nil 192 } 193 194 // Stop stops the LES service 195 func (s *LesServer) Stop() error { 196 close(s.closeCh) 197 198 s.clientPool.Stop() 199 if s.serverset != nil { 200 s.serverset.close() 201 } 202 s.peers.close() 203 s.fcManager.Stop() 204 s.costTracker.stop() 205 s.handler.stop() 206 s.servingQueue.stop() 207 if s.vfluxServer != nil { 208 s.vfluxServer.Stop() 209 } 210 211 // Note, bloom trie indexer is closed by parent bloombits indexer. 212 if s.chtIndexer != nil { 213 s.chtIndexer.Close() 214 } 215 if s.lesDb != nil { 216 s.lesDb.Close() 217 } 218 s.wg.Wait() 219 log.Info("Les server stopped") 220 221 return nil 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(uint64(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.minCapacity 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(uint64(s.config.LightPeers), totalCapacity) 277 case <-s.closeCh: 278 return 279 } 280 } 281 }