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