github.com/wzbox/go-ethereum@v1.9.2/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 "sync" 22 "time" 23 24 "github.com/ethereum/go-ethereum/accounts/abi/bind" 25 "github.com/ethereum/go-ethereum/common" 26 "github.com/ethereum/go-ethereum/common/mclock" 27 "github.com/ethereum/go-ethereum/core" 28 "github.com/ethereum/go-ethereum/core/rawdb" 29 "github.com/ethereum/go-ethereum/core/types" 30 "github.com/ethereum/go-ethereum/eth" 31 "github.com/ethereum/go-ethereum/les/flowcontrol" 32 "github.com/ethereum/go-ethereum/light" 33 "github.com/ethereum/go-ethereum/log" 34 "github.com/ethereum/go-ethereum/p2p" 35 "github.com/ethereum/go-ethereum/p2p/discv5" 36 "github.com/ethereum/go-ethereum/p2p/enode" 37 "github.com/ethereum/go-ethereum/params" 38 "github.com/ethereum/go-ethereum/rpc" 39 ) 40 41 const bufLimitRatio = 6000 // fixed bufLimit/MRR ratio 42 43 type LesServer struct { 44 lesCommons 45 46 archiveMode bool // Flag whether the ethereum node runs in archive mode. 47 48 fcManager *flowcontrol.ClientManager // nil if our node is client only 49 costTracker *costTracker 50 testCost uint64 51 defParams flowcontrol.ServerParams 52 lesTopics []discv5.Topic 53 privateKey *ecdsa.PrivateKey 54 quitSync chan struct{} 55 onlyAnnounce bool 56 57 thcNormal, thcBlockProcessing int // serving thread count for normal operation and block processing mode 58 59 maxPeers int 60 minCapacity, maxCapacity, freeClientCap uint64 61 clientPool *clientPool 62 } 63 64 func NewLesServer(e *eth.Ethereum, config *eth.Config) (*LesServer, error) { 65 lesTopics := make([]discv5.Topic, len(AdvertiseProtocolVersions)) 66 for i, pv := range AdvertiseProtocolVersions { 67 lesTopics[i] = lesTopic(e.BlockChain().Genesis().Hash(), pv) 68 } 69 quitSync := make(chan struct{}) 70 srv := &LesServer{ 71 lesCommons: lesCommons{ 72 config: config, 73 iConfig: light.DefaultServerIndexerConfig, 74 chainDb: e.ChainDb(), 75 chtIndexer: light.NewChtIndexer(e.ChainDb(), nil, params.CHTFrequency, params.HelperTrieProcessConfirmations), 76 bloomTrieIndexer: light.NewBloomTrieIndexer(e.ChainDb(), nil, params.BloomBitsBlocks, params.BloomTrieFrequency), 77 }, 78 archiveMode: e.ArchiveMode(), 79 quitSync: quitSync, 80 lesTopics: lesTopics, 81 onlyAnnounce: config.UltraLightOnlyAnnounce, 82 } 83 srv.costTracker, srv.minCapacity = newCostTracker(e.ChainDb(), config) 84 85 logger := log.New() 86 srv.thcNormal = config.LightServ * 4 / 100 87 if srv.thcNormal < 4 { 88 srv.thcNormal = 4 89 } 90 srv.thcBlockProcessing = config.LightServ/100 + 1 91 srv.fcManager = flowcontrol.NewClientManager(nil, &mclock.System{}) 92 93 checkpoint := srv.latestLocalCheckpoint() 94 if !checkpoint.Empty() { 95 logger.Info("Loaded latest checkpoint", "section", checkpoint.SectionIndex, "head", checkpoint.SectionHead, 96 "chtroot", checkpoint.CHTRoot, "bloomroot", checkpoint.BloomRoot) 97 } 98 99 srv.chtIndexer.Start(e.BlockChain()) 100 101 oracle := config.CheckpointOracle 102 if oracle == nil { 103 oracle = params.CheckpointOracles[e.BlockChain().Genesis().Hash()] 104 } 105 registrar := newCheckpointOracle(oracle, srv.getLocalCheckpoint) 106 // TODO(rjl493456442) Checkpoint is useless for les server, separate handler for client and server. 107 pm, err := NewProtocolManager(e.BlockChain().Config(), nil, light.DefaultServerIndexerConfig, config.UltraLightServers, config.UltraLightFraction, false, config.NetworkId, e.EventMux(), newPeerSet(), e.BlockChain(), e.TxPool(), e.ChainDb(), nil, nil, registrar, quitSync, new(sync.WaitGroup), e.Synced) 108 if err != nil { 109 return nil, err 110 } 111 srv.protocolManager = pm 112 pm.servingQueue = newServingQueue(int64(time.Millisecond*10), float64(config.LightServ)/100) 113 pm.server = srv 114 115 return srv, nil 116 } 117 118 func (s *LesServer) APIs() []rpc.API { 119 return []rpc.API{ 120 { 121 Namespace: "les", 122 Version: "1.0", 123 Service: NewPrivateLightAPI(&s.lesCommons, s.protocolManager.reg), 124 Public: false, 125 }, 126 } 127 } 128 129 // startEventLoop starts an event handler loop that updates the recharge curve of 130 // the client manager and adjusts the client pool's size according to the total 131 // capacity updates coming from the client manager 132 func (s *LesServer) startEventLoop() { 133 s.protocolManager.wg.Add(1) 134 135 var ( 136 processing, procLast bool 137 procStarted time.Time 138 ) 139 blockProcFeed := make(chan bool, 100) 140 s.protocolManager.blockchain.(*core.BlockChain).SubscribeBlockProcessingEvent(blockProcFeed) 141 totalRechargeCh := make(chan uint64, 100) 142 totalRecharge := s.costTracker.subscribeTotalRecharge(totalRechargeCh) 143 totalCapacityCh := make(chan uint64, 100) 144 updateRecharge := func() { 145 if processing { 146 if !procLast { 147 procStarted = time.Now() 148 } 149 s.protocolManager.servingQueue.setThreads(s.thcBlockProcessing) 150 s.fcManager.SetRechargeCurve(flowcontrol.PieceWiseLinear{{0, 0}, {totalRecharge, totalRecharge}}) 151 } else { 152 if procLast { 153 blockProcessingTimer.UpdateSince(procStarted) 154 } 155 s.protocolManager.servingQueue.setThreads(s.thcNormal) 156 s.fcManager.SetRechargeCurve(flowcontrol.PieceWiseLinear{{0, 0}, {totalRecharge / 16, totalRecharge / 2}, {totalRecharge / 2, totalRecharge / 2}, {totalRecharge, totalRecharge}}) 157 } 158 procLast = processing 159 } 160 updateRecharge() 161 totalCapacity := s.fcManager.SubscribeTotalCapacity(totalCapacityCh) 162 s.clientPool.setLimits(s.maxPeers, totalCapacity) 163 164 var maxFreePeers uint64 165 go func() { 166 for { 167 select { 168 case processing = <-blockProcFeed: 169 updateRecharge() 170 case totalRecharge = <-totalRechargeCh: 171 updateRecharge() 172 case totalCapacity = <-totalCapacityCh: 173 totalCapacityGauge.Update(int64(totalCapacity)) 174 newFreePeers := totalCapacity / s.freeClientCap 175 if newFreePeers < maxFreePeers && newFreePeers < uint64(s.maxPeers) { 176 log.Warn("Reduced total capacity", "maxFreePeers", newFreePeers) 177 } 178 maxFreePeers = newFreePeers 179 s.clientPool.setLimits(s.maxPeers, totalCapacity) 180 case <-s.protocolManager.quitSync: 181 s.protocolManager.wg.Done() 182 return 183 } 184 } 185 }() 186 } 187 188 func (s *LesServer) Protocols() []p2p.Protocol { 189 return s.makeProtocols(ServerProtocolVersions) 190 } 191 192 // Start starts the LES server 193 func (s *LesServer) Start(srvr *p2p.Server) { 194 s.maxPeers = s.config.LightPeers 195 totalRecharge := s.costTracker.totalRecharge() 196 if s.maxPeers > 0 { 197 s.freeClientCap = s.minCapacity //totalRecharge / uint64(s.maxPeers) 198 if s.freeClientCap < s.minCapacity { 199 s.freeClientCap = s.minCapacity 200 } 201 if s.freeClientCap > 0 { 202 s.defParams = flowcontrol.ServerParams{ 203 BufLimit: s.freeClientCap * bufLimitRatio, 204 MinRecharge: s.freeClientCap, 205 } 206 } 207 } 208 209 s.maxCapacity = s.freeClientCap * uint64(s.maxPeers) 210 if totalRecharge > s.maxCapacity { 211 s.maxCapacity = totalRecharge 212 } 213 s.fcManager.SetCapacityLimits(s.freeClientCap, s.maxCapacity, s.freeClientCap*2) 214 s.clientPool = newClientPool(s.chainDb, s.freeClientCap, 10000, mclock.System{}, func(id enode.ID) { go s.protocolManager.removePeer(peerIdToString(id)) }) 215 s.clientPool.setPriceFactors(priceFactors{0, 1, 1}, priceFactors{0, 1, 1}) 216 s.protocolManager.peers.notify(s.clientPool) 217 s.startEventLoop() 218 s.protocolManager.Start(s.config.LightPeers) 219 if srvr.DiscV5 != nil { 220 for _, topic := range s.lesTopics { 221 topic := topic 222 go func() { 223 logger := log.New("topic", topic) 224 logger.Info("Starting topic registration") 225 defer logger.Info("Terminated topic registration") 226 227 srvr.DiscV5.RegisterTopic(topic, s.quitSync) 228 }() 229 } 230 } 231 s.privateKey = srvr.PrivateKey 232 s.protocolManager.blockLoop() 233 } 234 235 func (s *LesServer) SetBloomBitsIndexer(bloomIndexer *core.ChainIndexer) { 236 bloomIndexer.AddChildIndexer(s.bloomTrieIndexer) 237 } 238 239 // SetClient sets the rpc client and starts running checkpoint contract if it is not yet watched. 240 func (s *LesServer) SetContractBackend(backend bind.ContractBackend) { 241 if s.protocolManager.reg != nil { 242 s.protocolManager.reg.start(backend) 243 } 244 } 245 246 // Stop stops the LES service 247 func (s *LesServer) Stop() { 248 s.fcManager.Stop() 249 s.chtIndexer.Close() 250 // bloom trie indexer is closed by parent bloombits indexer 251 go func() { 252 <-s.protocolManager.noMorePeers 253 }() 254 s.clientPool.stop() 255 s.costTracker.stop() 256 s.protocolManager.Stop() 257 } 258 259 // todo(rjl493456442) separate client and server implementation. 260 func (pm *ProtocolManager) blockLoop() { 261 pm.wg.Add(1) 262 headCh := make(chan core.ChainHeadEvent, 10) 263 headSub := pm.blockchain.SubscribeChainHeadEvent(headCh) 264 go func() { 265 var lastHead *types.Header 266 lastBroadcastTd := common.Big0 267 for { 268 select { 269 case ev := <-headCh: 270 peers := pm.peers.AllPeers() 271 if len(peers) > 0 { 272 header := ev.Block.Header() 273 hash := header.Hash() 274 number := header.Number.Uint64() 275 td := rawdb.ReadTd(pm.chainDb, hash, number) 276 if td != nil && td.Cmp(lastBroadcastTd) > 0 { 277 var reorg uint64 278 if lastHead != nil { 279 reorg = lastHead.Number.Uint64() - rawdb.FindCommonAncestor(pm.chainDb, header, lastHead).Number.Uint64() 280 } 281 lastHead = header 282 lastBroadcastTd = td 283 284 log.Debug("Announcing block to peers", "number", number, "hash", hash, "td", td, "reorg", reorg) 285 286 announce := announceData{Hash: hash, Number: number, Td: td, ReorgDepth: reorg} 287 var ( 288 signed bool 289 signedAnnounce announceData 290 ) 291 292 for _, p := range peers { 293 p := p 294 switch p.announceType { 295 case announceTypeSimple: 296 p.queueSend(func() { p.SendAnnounce(announce) }) 297 case announceTypeSigned: 298 if !signed { 299 signedAnnounce = announce 300 signedAnnounce.sign(pm.server.privateKey) 301 signed = true 302 } 303 p.queueSend(func() { p.SendAnnounce(signedAnnounce) }) 304 } 305 } 306 } 307 } 308 case <-pm.quitSync: 309 headSub.Unsubscribe() 310 pm.wg.Done() 311 return 312 } 313 } 314 }() 315 }