github.com/ebakus/go-ebakus@v1.0.5-0.20200520105415-dbccef9ec421/les/server.go (about) 1 // Copyright 2019 The ebakus/go-ebakus Authors 2 // This file is part of the ebakus/go-ebakus library. 3 // 4 // The ebakus/go-ebakus 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 ebakus/go-ebakus 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 ebakus/go-ebakus 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/ebakus/go-ebakus/accounts/abi/bind" 24 "github.com/ebakus/go-ebakus/common/mclock" 25 "github.com/ebakus/go-ebakus/core" 26 "github.com/ebakus/go-ebakus/eth" 27 "github.com/ebakus/go-ebakus/les/flowcontrol" 28 "github.com/ebakus/go-ebakus/light" 29 "github.com/ebakus/go-ebakus/log" 30 "github.com/ebakus/go-ebakus/p2p" 31 "github.com/ebakus/go-ebakus/p2p/discv5" 32 "github.com/ebakus/go-ebakus/p2p/enode" 33 "github.com/ebakus/go-ebakus/p2p/enr" 34 "github.com/ebakus/go-ebakus/params" 35 "github.com/ebakus/go-ebakus/rpc" 36 ) 37 38 type LesServer struct { 39 lesCommons 40 41 archiveMode bool // Flag whether the ebakus node runs in archive mode. 42 handler *serverHandler 43 lesTopics []discv5.Topic 44 privateKey *ecdsa.PrivateKey 45 46 // Flow control and capacity management 47 fcManager *flowcontrol.ClientManager 48 costTracker *costTracker 49 defParams flowcontrol.ServerParams 50 servingQueue *servingQueue 51 clientPool *clientPool 52 53 freeCapacity uint64 // The minimal client capacity used for free client. 54 threadsIdle int // Request serving threads count when system is idle. 55 threadsBusy int // Request serving threads count when system is busy(block insertion). 56 } 57 58 func NewLesServer(e *eth.Ebakus, config *eth.Config) (*LesServer, error) { 59 // Collect les protocol version information supported by local node. 60 lesTopics := make([]discv5.Topic, len(AdvertiseProtocolVersions)) 61 for i, pv := range AdvertiseProtocolVersions { 62 lesTopics[i] = lesTopic(e.BlockChain().Genesis().Hash(), pv) 63 } 64 // Calculate the number of threads used to service the light client 65 // requests based on the user-specified value. 66 threads := config.LightServ * 4 / 100 67 if threads < 4 { 68 threads = 4 69 } 70 srv := &LesServer{ 71 lesCommons: lesCommons{ 72 genesis: e.BlockChain().Genesis().Hash(), 73 config: config, 74 chainConfig: e.BlockChain().Config(), 75 iConfig: light.DefaultServerIndexerConfig, 76 chainDb: e.ChainDb(), 77 peers: newPeerSet(), 78 chainReader: e.BlockChain(), 79 chtIndexer: light.NewChtIndexer(e.ChainDb(), nil, params.CHTFrequency, params.HelperTrieProcessConfirmations), 80 bloomTrieIndexer: light.NewBloomTrieIndexer(e.ChainDb(), nil, params.BloomBitsBlocks, params.BloomTrieFrequency), 81 closeCh: make(chan struct{}), 82 }, 83 archiveMode: e.ArchiveMode(), 84 lesTopics: lesTopics, 85 fcManager: flowcontrol.NewClientManager(nil, &mclock.System{}), 86 servingQueue: newServingQueue(int64(time.Millisecond*10), float64(config.LightServ)/100), 87 threadsBusy: config.LightServ/100 + 1, 88 threadsIdle: threads, 89 } 90 srv.handler = newServerHandler(srv, e.BlockChain(), e.ChainDb(), e.TxPool(), e.Synced) 91 srv.costTracker, srv.freeCapacity = newCostTracker(e.ChainDb(), config) 92 93 // Set up checkpoint oracle. 94 oracle := config.CheckpointOracle 95 srv.oracle = newCheckpointOracle(oracle, srv.localCheckpoint) 96 97 // Initialize server capacity management fields. 98 srv.defParams = flowcontrol.ServerParams{ 99 BufLimit: srv.freeCapacity * bufLimitRatio, 100 MinRecharge: srv.freeCapacity, 101 } 102 // LES flow control tries to more or less guarantee the possibility for the 103 // clients to send a certain amount of requests at any time and get a quick 104 // response. Most of the clients want this guarantee but don't actually need 105 // to send requests most of the time. Our goal is to serve as many clients as 106 // possible while the actually used server capacity does not exceed the limits 107 totalRecharge := srv.costTracker.totalRecharge() 108 maxCapacity := srv.freeCapacity * uint64(srv.config.LightPeers) 109 if totalRecharge > maxCapacity { 110 maxCapacity = totalRecharge 111 } 112 srv.fcManager.SetCapacityLimits(srv.freeCapacity, maxCapacity, srv.freeCapacity*2) 113 srv.clientPool = newClientPool(srv.chainDb, srv.freeCapacity, mclock.System{}, func(id enode.ID) { go srv.peers.Unregister(peerIdToString(id)) }) 114 srv.clientPool.setPriceFactors(priceFactors{0, 1, 1}, priceFactors{0, 1, 1}) 115 116 checkpoint := srv.latestLocalCheckpoint() 117 if !checkpoint.Empty() { 118 log.Info("Loaded latest checkpoint", "section", checkpoint.SectionIndex, "head", checkpoint.SectionHead, 119 "chtroot", checkpoint.CHTRoot, "bloomroot", checkpoint.BloomRoot) 120 } 121 srv.chtIndexer.Start(e.BlockChain()) 122 return srv, nil 123 } 124 125 func (s *LesServer) APIs() []rpc.API { 126 return []rpc.API{ 127 { 128 Namespace: "les", 129 Version: "1.0", 130 Service: NewPrivateLightAPI(&s.lesCommons), 131 Public: false, 132 }, 133 } 134 } 135 136 func (s *LesServer) Protocols() []p2p.Protocol { 137 ps := s.makeProtocols(ServerProtocolVersions, s.handler.runPeer, func(id enode.ID) interface{} { 138 if p := s.peers.Peer(peerIdToString(id)); p != nil { 139 return p.Info() 140 } 141 return nil 142 }) 143 // Add "les" ENR entries. 144 for i := range ps { 145 ps[i].Attributes = []enr.Entry{&lesEntry{}} 146 } 147 return ps 148 } 149 150 // Start starts the LES server 151 func (s *LesServer) Start(srvr *p2p.Server) { 152 s.privateKey = srvr.PrivateKey 153 s.handler.start() 154 155 s.wg.Add(1) 156 go s.capacityManagement() 157 158 if srvr.DiscV5 != nil { 159 for _, topic := range s.lesTopics { 160 topic := topic 161 go func() { 162 logger := log.New("topic", topic) 163 logger.Info("Starting topic registration") 164 defer logger.Info("Terminated topic registration") 165 166 srvr.DiscV5.RegisterTopic(topic, s.closeCh) 167 }() 168 } 169 } 170 } 171 172 // Stop stops the LES service 173 func (s *LesServer) Stop() { 174 close(s.closeCh) 175 176 // Disconnect existing sessions. 177 // This also closes the gate for any new registrations on the peer set. 178 // sessions which are already established but not added to pm.peers yet 179 // will exit when they try to register. 180 s.peers.Close() 181 182 s.fcManager.Stop() 183 s.costTracker.stop() 184 s.handler.stop() 185 s.clientPool.stop() // client pool should be closed after handler. 186 s.servingQueue.stop() 187 188 // Note, bloom trie indexer is closed by parent bloombits indexer. 189 s.chtIndexer.Close() 190 s.wg.Wait() 191 log.Info("Les server stopped") 192 } 193 194 func (s *LesServer) SetBloomBitsIndexer(bloomIndexer *core.ChainIndexer) { 195 bloomIndexer.AddChildIndexer(s.bloomTrieIndexer) 196 } 197 198 // SetClient sets the rpc client and starts running checkpoint contract if it is not yet watched. 199 func (s *LesServer) SetContractBackend(backend bind.ContractBackend) { 200 if s.oracle == nil { 201 return 202 } 203 s.oracle.start(backend) 204 } 205 206 // capacityManagement starts an event handler loop that updates the recharge curve of 207 // the client manager and adjusts the client pool's size according to the total 208 // capacity updates coming from the client manager 209 func (s *LesServer) capacityManagement() { 210 defer s.wg.Done() 211 212 processCh := make(chan bool, 100) 213 sub := s.handler.blockchain.SubscribeBlockProcessingEvent(processCh) 214 defer sub.Unsubscribe() 215 216 totalRechargeCh := make(chan uint64, 100) 217 totalRecharge := s.costTracker.subscribeTotalRecharge(totalRechargeCh) 218 219 totalCapacityCh := make(chan uint64, 100) 220 totalCapacity := s.fcManager.SubscribeTotalCapacity(totalCapacityCh) 221 s.clientPool.setLimits(s.config.LightPeers, totalCapacity) 222 223 var ( 224 busy bool 225 freePeers uint64 226 blockProcess mclock.AbsTime 227 ) 228 updateRecharge := func() { 229 if busy { 230 s.servingQueue.setThreads(s.threadsBusy) 231 s.fcManager.SetRechargeCurve(flowcontrol.PieceWiseLinear{{0, 0}, {totalRecharge, totalRecharge}}) 232 } else { 233 s.servingQueue.setThreads(s.threadsIdle) 234 s.fcManager.SetRechargeCurve(flowcontrol.PieceWiseLinear{{0, 0}, {totalRecharge / 10, totalRecharge}, {totalRecharge, totalRecharge}}) 235 } 236 } 237 updateRecharge() 238 239 for { 240 select { 241 case busy = <-processCh: 242 if busy { 243 blockProcess = mclock.Now() 244 } else { 245 blockProcessingTimer.Update(time.Duration(mclock.Now() - blockProcess)) 246 } 247 updateRecharge() 248 case totalRecharge = <-totalRechargeCh: 249 totalRechargeGauge.Update(int64(totalRecharge)) 250 updateRecharge() 251 case totalCapacity = <-totalCapacityCh: 252 totalCapacityGauge.Update(int64(totalCapacity)) 253 newFreePeers := totalCapacity / s.freeCapacity 254 if newFreePeers < freePeers && newFreePeers < uint64(s.config.LightPeers) { 255 log.Warn("Reduced free peer connections", "from", freePeers, "to", newFreePeers) 256 } 257 freePeers = newFreePeers 258 s.clientPool.setLimits(s.config.LightPeers, totalCapacity) 259 case <-s.closeCh: 260 return 261 } 262 } 263 }