github.com/core-coin/go-core/v2@v2.1.9/les/server.go (about) 1 // Copyright 2016 by the Authors 2 // This file is part of the go-core library. 3 // 4 // The go-core 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-core 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-core library. If not, see <http://www.gnu.org/licenses/>. 16 17 package les 18 19 import ( 20 "reflect" 21 "time" 22 23 "github.com/core-coin/go-core/v2/common/mclock" 24 "github.com/core-coin/go-core/v2/crypto" 25 "github.com/core-coin/go-core/v2/les/flowcontrol" 26 lps "github.com/core-coin/go-core/v2/les/lespay/server" 27 "github.com/core-coin/go-core/v2/light" 28 "github.com/core-coin/go-core/v2/log" 29 "github.com/core-coin/go-core/v2/node" 30 "github.com/core-coin/go-core/v2/p2p" 31 "github.com/core-coin/go-core/v2/p2p/discv5" 32 "github.com/core-coin/go-core/v2/p2p/enode" 33 "github.com/core-coin/go-core/v2/p2p/enr" 34 "github.com/core-coin/go-core/v2/p2p/nodestate" 35 "github.com/core-coin/go-core/v2/params" 36 "github.com/core-coin/go-core/v2/rpc" 37 "github.com/core-coin/go-core/v2/xcb" 38 ) 39 40 var ( 41 serverSetup = &nodestate.Setup{} 42 clientPeerField = serverSetup.NewField("clientPeer", reflect.TypeOf(&clientPeer{})) 43 clientInfoField = serverSetup.NewField("clientInfo", reflect.TypeOf(&clientInfo{})) 44 connAddressField = serverSetup.NewField("connAddr", reflect.TypeOf("")) 45 balanceTrackerSetup = lps.NewBalanceTrackerSetup(serverSetup) 46 priorityPoolSetup = lps.NewPriorityPoolSetup(serverSetup) 47 ) 48 49 func init() { 50 balanceTrackerSetup.Connect(connAddressField, priorityPoolSetup.CapacityField) 51 priorityPoolSetup.Connect(balanceTrackerSetup.BalanceField, balanceTrackerSetup.UpdateFlag) // NodeBalance implements nodePriority 52 } 53 54 type LesServer struct { 55 lesCommons 56 57 ns *nodestate.NodeStateMachine 58 archiveMode bool // Flag whether the core node runs in archive mode. 59 handler *serverHandler 60 broadcaster *broadcaster 61 lesTopics []discv5.Topic 62 privateKey *crypto.PrivateKey 63 64 // Flow control and capacity management 65 fcManager *flowcontrol.ClientManager 66 costTracker *costTracker 67 defParams flowcontrol.ServerParams 68 servingQueue *servingQueue 69 clientPool *clientPool 70 71 minCapacity, maxCapacity uint64 72 threadsIdle int // Request serving threads count when system is idle. 73 threadsBusy int // Request serving threads count when system is busy(block insertion). 74 75 p2pSrv *p2p.Server 76 } 77 78 func NewLesServer(node *node.Node, e *xcb.Core, config *xcb.Config) (*LesServer, error) { 79 ns := nodestate.NewNodeStateMachine(nil, nil, mclock.System{}, serverSetup) 80 // Collect les protocol version information supported by local node. 81 lesTopics := make([]discv5.Topic, len(AdvertiseProtocolVersions)) 82 for i, pv := range AdvertiseProtocolVersions { 83 lesTopics[i] = lesTopic(e.BlockChain().Genesis().Hash(), pv) 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 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 ns: ns, 104 archiveMode: e.ArchiveMode(), 105 broadcaster: newBroadcaster(ns), 106 lesTopics: lesTopics, 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 srv.handler = newServerHandler(srv, e.BlockChain(), e.ChainDb(), e.TxPool(), e.Synced) 114 srv.costTracker, srv.minCapacity = newCostTracker(e.ChainDb(), config) 115 srv.oracle = srv.setupOracle(node, e.BlockChain().Genesis().Hash(), config) 116 117 // Initialize the bloom trie indexer. 118 e.BloomIndexer().AddChildIndexer(srv.bloomTrieIndexer) 119 120 // Initialize server capacity management fields. 121 srv.defParams = flowcontrol.ServerParams{ 122 BufLimit: srv.minCapacity * bufLimitRatio, 123 MinRecharge: srv.minCapacity, 124 } 125 // LES flow control tries to more or less guarantee the possibility for the 126 // clients to send a certain amount of requests at any time and get a quick 127 // response. Most of the clients want this guarantee but don't actually need 128 // to send requests most of the time. Our goal is to serve as many clients as 129 // possible while the actually used server capacity does not exceed the limits 130 totalRecharge := srv.costTracker.totalRecharge() 131 srv.maxCapacity = srv.minCapacity * uint64(srv.config.LightPeers) 132 if totalRecharge > srv.maxCapacity { 133 srv.maxCapacity = totalRecharge 134 } 135 srv.fcManager.SetCapacityLimits(srv.minCapacity, srv.maxCapacity, srv.minCapacity*2) 136 srv.clientPool = newClientPool(ns, srv.chainDb, srv.minCapacity, defaultConnectedBias, mclock.System{}, srv.dropClient) 137 srv.clientPool.setDefaultFactors(lps.PriceFactors{TimeFactor: 0, CapacityFactor: 1, RequestFactor: 1}, lps.PriceFactors{TimeFactor: 0, CapacityFactor: 1, RequestFactor: 1}) 138 139 checkpoint := srv.latestLocalCheckpoint() 140 if !checkpoint.Empty() { 141 log.Info("Loaded latest checkpoint", "section", checkpoint.SectionIndex, "head", checkpoint.SectionHead, 142 "chtroot", checkpoint.CHTRoot, "bloomroot", checkpoint.BloomRoot) 143 } 144 srv.chtIndexer.Start(e.BlockChain()) 145 146 node.RegisterProtocols(srv.Protocols()) 147 node.RegisterAPIs(srv.APIs()) 148 node.RegisterLifecycle(srv) 149 150 // disconnect all peers at nsm shutdown 151 ns.SubscribeField(clientPeerField, func(node *enode.Node, state nodestate.Flags, oldValue, newValue interface{}) { 152 if state.Equals(serverSetup.OfflineFlag()) && oldValue != nil { 153 oldValue.(*clientPeer).Peer.Disconnect(p2p.DiscRequested) 154 } 155 }) 156 ns.Start() 157 return srv, nil 158 } 159 160 func (s *LesServer) APIs() []rpc.API { 161 return []rpc.API{ 162 { 163 Namespace: "les", 164 Version: "1.0", 165 Service: NewPrivateLightAPI(&s.lesCommons), 166 Public: false, 167 }, 168 { 169 Namespace: "les", 170 Version: "1.0", 171 Service: NewPrivateLightServerAPI(s), 172 Public: false, 173 }, 174 { 175 Namespace: "debug", 176 Version: "1.0", 177 Service: NewPrivateDebugAPI(s), 178 Public: false, 179 }, 180 } 181 } 182 183 func (s *LesServer) Protocols() []p2p.Protocol { 184 ps := s.makeProtocols(ServerProtocolVersions, s.handler.runPeer, func(id enode.ID) interface{} { 185 if p := s.getClient(id); p != nil { 186 return p.Info() 187 } 188 return nil 189 }, nil) 190 // Add "les" ENR entries. 191 for i := range ps { 192 ps[i].Attributes = []enr.Entry{&lesEntry{}} 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.broadcaster.setSignerKey(s.privateKey) 201 s.handler.start() 202 203 s.wg.Add(1) 204 go s.capacityManagement() 205 206 if s.p2pSrv.DiscV5 != nil { 207 for _, topic := range s.lesTopics { 208 topic := topic 209 go func() { 210 logger := log.New("topic", topic) 211 logger.Info("Starting topic registration") 212 defer logger.Info("Terminated topic registration") 213 214 s.p2pSrv.DiscV5.RegisterTopic(topic, s.closeCh) 215 }() 216 } 217 } 218 219 return nil 220 } 221 222 // Stop stops the LES service 223 func (s *LesServer) Stop() error { 224 close(s.closeCh) 225 226 s.clientPool.stop() 227 s.ns.Stop() 228 s.fcManager.Stop() 229 s.costTracker.stop() 230 s.handler.stop() 231 s.servingQueue.stop() 232 233 // Note, bloom trie indexer is closed by parent bloombits indexer. 234 s.chtIndexer.Close() 235 s.wg.Wait() 236 log.Info("Les server stopped") 237 238 return nil 239 } 240 241 // capacityManagement starts an event handler loop that updates the recharge curve of 242 // the client manager and adjusts the client pool's size according to the total 243 // capacity updates coming from the client manager 244 func (s *LesServer) capacityManagement() { 245 defer s.wg.Done() 246 247 processCh := make(chan bool, 100) 248 sub := s.handler.blockchain.SubscribeBlockProcessingEvent(processCh) 249 defer sub.Unsubscribe() 250 251 totalRechargeCh := make(chan uint64, 100) 252 totalRecharge := s.costTracker.subscribeTotalRecharge(totalRechargeCh) 253 254 totalCapacityCh := make(chan uint64, 100) 255 totalCapacity := s.fcManager.SubscribeTotalCapacity(totalCapacityCh) 256 s.clientPool.setLimits(s.config.LightPeers, totalCapacity) 257 258 var ( 259 busy bool 260 freePeers uint64 261 blockProcess mclock.AbsTime 262 ) 263 updateRecharge := func() { 264 if busy { 265 s.servingQueue.setThreads(s.threadsBusy) 266 s.fcManager.SetRechargeCurve(flowcontrol.PieceWiseLinear{{0, 0}, {totalRecharge, totalRecharge}}) 267 } else { 268 s.servingQueue.setThreads(s.threadsIdle) 269 s.fcManager.SetRechargeCurve(flowcontrol.PieceWiseLinear{{0, 0}, {totalRecharge / 10, totalRecharge}, {totalRecharge, totalRecharge}}) 270 } 271 } 272 updateRecharge() 273 274 for { 275 select { 276 case busy = <-processCh: 277 if busy { 278 blockProcess = mclock.Now() 279 } else { 280 blockProcessingTimer.Update(time.Duration(mclock.Now() - blockProcess)) 281 } 282 updateRecharge() 283 case totalRecharge = <-totalRechargeCh: 284 totalRechargeGauge.Update(int64(totalRecharge)) 285 updateRecharge() 286 case totalCapacity = <-totalCapacityCh: 287 totalCapacityGauge.Update(int64(totalCapacity)) 288 newFreePeers := totalCapacity / s.minCapacity 289 if newFreePeers < freePeers && newFreePeers < uint64(s.config.LightPeers) { 290 log.Warn("Reduced free peer connections", "from", freePeers, "to", newFreePeers) 291 } 292 freePeers = newFreePeers 293 s.clientPool.setLimits(s.config.LightPeers, totalCapacity) 294 case <-s.closeCh: 295 return 296 } 297 } 298 } 299 300 func (s *LesServer) getClient(id enode.ID) *clientPeer { 301 if node := s.ns.GetNode(id); node != nil { 302 if p, ok := s.ns.GetField(node, clientPeerField).(*clientPeer); ok { 303 return p 304 } 305 } 306 return nil 307 } 308 309 func (s *LesServer) dropClient(id enode.ID) { 310 if p := s.getClient(id); p != nil { 311 p.Peer.Disconnect(p2p.DiscRequested) 312 } 313 }