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