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