github.com/phillinzzz/newBsc@v1.1.6/les/client.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 implements the Light Ethereum Subprotocol. 18 package les 19 20 import ( 21 "fmt" 22 "time" 23 24 "github.com/phillinzzz/newBsc/accounts" 25 "github.com/phillinzzz/newBsc/common" 26 "github.com/phillinzzz/newBsc/common/hexutil" 27 "github.com/phillinzzz/newBsc/common/mclock" 28 "github.com/phillinzzz/newBsc/consensus" 29 "github.com/phillinzzz/newBsc/core" 30 "github.com/phillinzzz/newBsc/core/bloombits" 31 "github.com/phillinzzz/newBsc/core/rawdb" 32 "github.com/phillinzzz/newBsc/core/types" 33 "github.com/phillinzzz/newBsc/eth/downloader" 34 "github.com/phillinzzz/newBsc/eth/ethconfig" 35 "github.com/phillinzzz/newBsc/eth/filters" 36 "github.com/phillinzzz/newBsc/eth/gasprice" 37 "github.com/phillinzzz/newBsc/event" 38 "github.com/phillinzzz/newBsc/internal/ethapi" 39 "github.com/phillinzzz/newBsc/les/vflux" 40 vfc "github.com/phillinzzz/newBsc/les/vflux/client" 41 "github.com/phillinzzz/newBsc/light" 42 "github.com/phillinzzz/newBsc/log" 43 "github.com/phillinzzz/newBsc/node" 44 "github.com/phillinzzz/newBsc/p2p" 45 "github.com/phillinzzz/newBsc/p2p/enode" 46 "github.com/phillinzzz/newBsc/p2p/enr" 47 "github.com/phillinzzz/newBsc/params" 48 "github.com/phillinzzz/newBsc/rlp" 49 "github.com/phillinzzz/newBsc/rpc" 50 ) 51 52 type LightEthereum struct { 53 lesCommons 54 55 peers *serverPeerSet 56 reqDist *requestDistributor 57 retriever *retrieveManager 58 odr *LesOdr 59 relay *lesTxRelay 60 handler *clientHandler 61 txPool *light.TxPool 62 blockchain *light.LightChain 63 serverPool *vfc.ServerPool 64 serverPoolIterator enode.Iterator 65 pruner *pruner 66 67 bloomRequests chan chan *bloombits.Retrieval // Channel receiving bloom data retrieval requests 68 bloomIndexer *core.ChainIndexer // Bloom indexer operating during block imports 69 70 ApiBackend *LesApiBackend 71 eventMux *event.TypeMux 72 engine consensus.Engine 73 accountManager *accounts.Manager 74 netRPCService *ethapi.PublicNetAPI 75 76 p2pServer *p2p.Server 77 p2pConfig *p2p.Config 78 udpEnabled bool 79 } 80 81 // New creates an instance of the light client. 82 func New(stack *node.Node, config *ethconfig.Config) (*LightEthereum, error) { 83 chainDb, err := stack.OpenDatabase("lightchaindata", config.DatabaseCache, config.DatabaseHandles, "eth/db/chaindata/", false) 84 if err != nil { 85 return nil, err 86 } 87 lesDb, err := stack.OpenDatabase("les.client", 0, 0, "eth/db/lesclient/", false) 88 if err != nil { 89 return nil, err 90 } 91 chainConfig, genesisHash, genesisErr := core.SetupGenesisBlockWithOverride(chainDb, config.Genesis, config.OverrideBerlin) 92 if _, isCompat := genesisErr.(*params.ConfigCompatError); genesisErr != nil && !isCompat { 93 return nil, genesisErr 94 } 95 log.Info("Initialised chain configuration", "config", chainConfig) 96 97 peers := newServerPeerSet() 98 leth := &LightEthereum{ 99 lesCommons: lesCommons{ 100 genesis: genesisHash, 101 config: config, 102 chainConfig: chainConfig, 103 iConfig: light.DefaultClientIndexerConfig, 104 chainDb: chainDb, 105 lesDb: lesDb, 106 closeCh: make(chan struct{}), 107 }, 108 peers: peers, 109 eventMux: stack.EventMux(), 110 reqDist: newRequestDistributor(peers, &mclock.System{}), 111 accountManager: stack.AccountManager(), 112 engine: ethconfig.CreateConsensusEngine(stack, chainConfig, &config.Ethash, nil, false, chainDb, nil, genesisHash), 113 bloomRequests: make(chan chan *bloombits.Retrieval), 114 bloomIndexer: core.NewBloomIndexer(chainDb, params.BloomBitsBlocksClient, params.HelperTrieConfirmations), 115 p2pServer: stack.Server(), 116 p2pConfig: &stack.Config().P2P, 117 udpEnabled: stack.Config().P2P.DiscoveryV5, 118 } 119 120 var prenegQuery vfc.QueryFunc 121 if leth.udpEnabled { 122 prenegQuery = leth.prenegQuery 123 } 124 leth.serverPool, leth.serverPoolIterator = vfc.NewServerPool(lesDb, []byte("serverpool:"), time.Second, prenegQuery, &mclock.System{}, config.UltraLightServers, requestList) 125 leth.serverPool.AddMetrics(suggestedTimeoutGauge, totalValueGauge, serverSelectableGauge, serverConnectedGauge, sessionValueMeter, serverDialedMeter) 126 127 leth.retriever = newRetrieveManager(peers, leth.reqDist, leth.serverPool.GetTimeout) 128 leth.relay = newLesTxRelay(peers, leth.retriever) 129 130 leth.odr = NewLesOdr(chainDb, light.DefaultClientIndexerConfig, leth.peers, leth.retriever) 131 leth.chtIndexer = light.NewChtIndexer(chainDb, leth.odr, params.CHTFrequency, params.HelperTrieConfirmations, config.LightNoPrune) 132 leth.bloomTrieIndexer = light.NewBloomTrieIndexer(chainDb, leth.odr, params.BloomBitsBlocksClient, params.BloomTrieFrequency, config.LightNoPrune) 133 leth.odr.SetIndexers(leth.chtIndexer, leth.bloomTrieIndexer, leth.bloomIndexer) 134 135 checkpoint := config.Checkpoint 136 if checkpoint == nil { 137 checkpoint = params.TrustedCheckpoints[genesisHash] 138 } 139 // Note: NewLightChain adds the trusted checkpoint so it needs an ODR with 140 // indexers already set but not started yet 141 if leth.blockchain, err = light.NewLightChain(leth.odr, leth.chainConfig, leth.engine, checkpoint); err != nil { 142 return nil, err 143 } 144 leth.chainReader = leth.blockchain 145 leth.txPool = light.NewTxPool(leth.chainConfig, leth.blockchain, leth.relay) 146 147 // Set up checkpoint oracle. 148 leth.oracle = leth.setupOracle(stack, genesisHash, config) 149 150 // Note: AddChildIndexer starts the update process for the child 151 leth.bloomIndexer.AddChildIndexer(leth.bloomTrieIndexer) 152 leth.chtIndexer.Start(leth.blockchain) 153 leth.bloomIndexer.Start(leth.blockchain) 154 155 // Start a light chain pruner to delete useless historical data. 156 leth.pruner = newPruner(chainDb, leth.chtIndexer, leth.bloomTrieIndexer) 157 158 // Rewind the chain in case of an incompatible config upgrade. 159 if compat, ok := genesisErr.(*params.ConfigCompatError); ok { 160 log.Warn("Rewinding chain to upgrade configuration", "err", compat) 161 leth.blockchain.SetHead(compat.RewindTo) 162 rawdb.WriteChainConfig(chainDb, genesisHash, chainConfig) 163 } 164 165 leth.ApiBackend = &LesApiBackend{stack.Config().ExtRPCEnabled(), stack.Config().AllowUnprotectedTxs, leth, nil} 166 gpoParams := config.GPO 167 if gpoParams.Default == nil { 168 gpoParams.Default = config.Miner.GasPrice 169 } 170 leth.ApiBackend.gpo = gasprice.NewOracle(leth.ApiBackend, gpoParams) 171 172 leth.handler = newClientHandler(config.UltraLightServers, config.UltraLightFraction, checkpoint, leth) 173 if leth.handler.ulc != nil { 174 log.Warn("Ultra light client is enabled", "trustedNodes", len(leth.handler.ulc.keys), "minTrustedFraction", leth.handler.ulc.fraction) 175 leth.blockchain.DisableCheckFreq() 176 } 177 178 leth.netRPCService = ethapi.NewPublicNetAPI(leth.p2pServer, leth.config.NetworkId) 179 180 // Register the backend on the node 181 stack.RegisterAPIs(leth.APIs()) 182 stack.RegisterProtocols(leth.Protocols()) 183 stack.RegisterLifecycle(leth) 184 185 // Check for unclean shutdown 186 if uncleanShutdowns, discards, err := rawdb.PushUncleanShutdownMarker(chainDb); err != nil { 187 log.Error("Could not update unclean-shutdown-marker list", "error", err) 188 } else { 189 if discards > 0 { 190 log.Warn("Old unclean shutdowns found", "count", discards) 191 } 192 for _, tstamp := range uncleanShutdowns { 193 t := time.Unix(int64(tstamp), 0) 194 log.Warn("Unclean shutdown detected", "booted", t, 195 "age", common.PrettyAge(t)) 196 } 197 } 198 return leth, nil 199 } 200 201 // VfluxRequest sends a batch of requests to the given node through discv5 UDP TalkRequest and returns the responses 202 func (s *LightEthereum) VfluxRequest(n *enode.Node, reqs vflux.Requests) vflux.Replies { 203 if !s.udpEnabled { 204 return nil 205 } 206 reqsEnc, _ := rlp.EncodeToBytes(&reqs) 207 repliesEnc, _ := s.p2pServer.DiscV5.TalkRequest(s.serverPool.DialNode(n), "vfx", reqsEnc) 208 var replies vflux.Replies 209 if len(repliesEnc) == 0 || rlp.DecodeBytes(repliesEnc, &replies) != nil { 210 return nil 211 } 212 return replies 213 } 214 215 // vfxVersion returns the version number of the "les" service subdomain of the vflux UDP 216 // service, as advertised in the ENR record 217 func (s *LightEthereum) vfxVersion(n *enode.Node) uint { 218 if n.Seq() == 0 { 219 var err error 220 if !s.udpEnabled { 221 return 0 222 } 223 if n, err = s.p2pServer.DiscV5.RequestENR(n); n != nil && err == nil && n.Seq() != 0 { 224 s.serverPool.Persist(n) 225 } else { 226 return 0 227 } 228 } 229 230 var les []rlp.RawValue 231 if err := n.Load(enr.WithEntry("les", &les)); err != nil || len(les) < 1 { 232 return 0 233 } 234 var version uint 235 rlp.DecodeBytes(les[0], &version) // Ignore additional fields (for forward compatibility). 236 return version 237 } 238 239 // prenegQuery sends a capacity query to the given server node to determine whether 240 // a connection slot is immediately available 241 func (s *LightEthereum) prenegQuery(n *enode.Node) int { 242 if s.vfxVersion(n) < 1 { 243 // UDP query not supported, always try TCP connection 244 return 1 245 } 246 247 var requests vflux.Requests 248 requests.Add("les", vflux.CapacityQueryName, vflux.CapacityQueryReq{ 249 Bias: 180, 250 AddTokens: []vflux.IntOrInf{{}}, 251 }) 252 replies := s.VfluxRequest(n, requests) 253 var cqr vflux.CapacityQueryReply 254 if replies.Get(0, &cqr) != nil || len(cqr) != 1 { // Note: Get returns an error if replies is nil 255 return -1 256 } 257 if cqr[0] > 0 { 258 return 1 259 } 260 return 0 261 } 262 263 type LightDummyAPI struct{} 264 265 // Etherbase is the address that mining rewards will be send to 266 func (s *LightDummyAPI) Etherbase() (common.Address, error) { 267 return common.Address{}, fmt.Errorf("mining is not supported in light mode") 268 } 269 270 // Coinbase is the address that mining rewards will be send to (alias for Etherbase) 271 func (s *LightDummyAPI) Coinbase() (common.Address, error) { 272 return common.Address{}, fmt.Errorf("mining is not supported in light mode") 273 } 274 275 // Hashrate returns the POW hashrate 276 func (s *LightDummyAPI) Hashrate() hexutil.Uint { 277 return 0 278 } 279 280 // Mining returns an indication if this node is currently mining. 281 func (s *LightDummyAPI) Mining() bool { 282 return false 283 } 284 285 // APIs returns the collection of RPC services the ethereum package offers. 286 // NOTE, some of these services probably need to be moved to somewhere else. 287 func (s *LightEthereum) APIs() []rpc.API { 288 apis := ethapi.GetAPIs(s.ApiBackend) 289 apis = append(apis, s.engine.APIs(s.BlockChain().HeaderChain())...) 290 return append(apis, []rpc.API{ 291 { 292 Namespace: "eth", 293 Version: "1.0", 294 Service: &LightDummyAPI{}, 295 Public: true, 296 }, { 297 Namespace: "eth", 298 Version: "1.0", 299 Service: downloader.NewPublicDownloaderAPI(s.handler.downloader, s.eventMux), 300 Public: true, 301 }, { 302 Namespace: "eth", 303 Version: "1.0", 304 Service: filters.NewPublicFilterAPI(s.ApiBackend, true, 5*time.Minute, s.config.RangeLimit), 305 Public: true, 306 }, { 307 Namespace: "net", 308 Version: "1.0", 309 Service: s.netRPCService, 310 Public: true, 311 }, { 312 Namespace: "les", 313 Version: "1.0", 314 Service: NewPrivateLightAPI(&s.lesCommons), 315 Public: false, 316 }, { 317 Namespace: "vflux", 318 Version: "1.0", 319 Service: s.serverPool.API(), 320 Public: false, 321 }, 322 }...) 323 } 324 325 func (s *LightEthereum) ResetWithGenesisBlock(gb *types.Block) { 326 s.blockchain.ResetWithGenesisBlock(gb) 327 } 328 329 func (s *LightEthereum) BlockChain() *light.LightChain { return s.blockchain } 330 func (s *LightEthereum) TxPool() *light.TxPool { return s.txPool } 331 func (s *LightEthereum) Engine() consensus.Engine { return s.engine } 332 func (s *LightEthereum) LesVersion() int { return int(ClientProtocolVersions[0]) } 333 func (s *LightEthereum) Downloader() *downloader.Downloader { return s.handler.downloader } 334 func (s *LightEthereum) EventMux() *event.TypeMux { return s.eventMux } 335 336 // Protocols returns all the currently configured network protocols to start. 337 func (s *LightEthereum) Protocols() []p2p.Protocol { 338 return s.makeProtocols(ClientProtocolVersions, s.handler.runPeer, func(id enode.ID) interface{} { 339 if p := s.peers.peer(id.String()); p != nil { 340 return p.Info() 341 } 342 return nil 343 }, s.serverPoolIterator) 344 } 345 346 // Start implements node.Lifecycle, starting all internal goroutines needed by the 347 // light ethereum protocol implementation. 348 func (s *LightEthereum) Start() error { 349 log.Warn("Light client mode is an experimental feature") 350 351 if s.udpEnabled && s.p2pServer.DiscV5 == nil { 352 s.udpEnabled = false 353 log.Error("Discovery v5 is not initialized") 354 } 355 discovery, err := s.setupDiscovery() 356 if err != nil { 357 return err 358 } 359 s.serverPool.AddSource(discovery) 360 s.serverPool.Start() 361 // Start bloom request workers. 362 s.wg.Add(bloomServiceThreads) 363 s.startBloomHandlers(params.BloomBitsBlocksClient) 364 s.handler.start() 365 366 return nil 367 } 368 369 // Stop implements node.Lifecycle, terminating all internal goroutines used by the 370 // Ethereum protocol. 371 func (s *LightEthereum) Stop() error { 372 close(s.closeCh) 373 s.serverPool.Stop() 374 s.peers.close() 375 s.reqDist.close() 376 s.odr.Stop() 377 s.relay.Stop() 378 s.bloomIndexer.Close() 379 s.chtIndexer.Close() 380 s.blockchain.Stop() 381 s.handler.stop() 382 s.txPool.Stop() 383 s.engine.Close() 384 s.pruner.close() 385 s.eventMux.Stop() 386 rawdb.PopUncleanShutdownMarker(s.chainDb) 387 s.chainDb.Close() 388 s.lesDb.Close() 389 s.wg.Wait() 390 log.Info("Light ethereum stopped") 391 return nil 392 }