github.com/metanonia/go-metanonia@v0.0.0-20210204160406-0aae19ce8e12/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/metanonia/go-metanonia/accounts" 25 "github.com/metanonia/go-metanonia/common" 26 "github.com/metanonia/go-metanonia/common/hexutil" 27 "github.com/metanonia/go-metanonia/common/mclock" 28 "github.com/metanonia/go-metanonia/consensus" 29 "github.com/metanonia/go-metanonia/core" 30 "github.com/metanonia/go-metanonia/core/bloombits" 31 "github.com/metanonia/go-metanonia/core/rawdb" 32 "github.com/metanonia/go-metanonia/core/types" 33 "github.com/metanonia/go-metanonia/eth" 34 "github.com/metanonia/go-metanonia/eth/downloader" 35 "github.com/metanonia/go-metanonia/eth/filters" 36 "github.com/metanonia/go-metanonia/eth/gasprice" 37 "github.com/metanonia/go-metanonia/event" 38 "github.com/metanonia/go-metanonia/internal/ethapi" 39 lpc "github.com/metanonia/go-metanonia/les/lespay/client" 40 "github.com/metanonia/go-metanonia/light" 41 "github.com/metanonia/go-metanonia/log" 42 "github.com/metanonia/go-metanonia/node" 43 "github.com/metanonia/go-metanonia/p2p" 44 "github.com/metanonia/go-metanonia/p2p/enode" 45 "github.com/metanonia/go-metanonia/params" 46 "github.com/metanonia/go-metanonia/rpc" 47 ) 48 49 type LightEthereum struct { 50 lesCommons 51 52 peers *serverPeerSet 53 reqDist *requestDistributor 54 retriever *retrieveManager 55 odr *LesOdr 56 relay *lesTxRelay 57 handler *clientHandler 58 txPool *light.TxPool 59 blockchain *light.LightChain 60 serverPool *serverPool 61 valueTracker *lpc.ValueTracker 62 dialCandidates enode.Iterator 63 pruner *pruner 64 65 bloomRequests chan chan *bloombits.Retrieval // Channel receiving bloom data retrieval requests 66 bloomIndexer *core.ChainIndexer // Bloom indexer operating during block imports 67 68 ApiBackend *LesApiBackend 69 eventMux *event.TypeMux 70 engine consensus.Engine 71 accountManager *accounts.Manager 72 netRPCService *ethapi.PublicNetAPI 73 74 p2pServer *p2p.Server 75 } 76 77 // New creates an instance of the light client. 78 func New(stack *node.Node, config *eth.Config) (*LightEthereum, error) { 79 chainDb, err := stack.OpenDatabase("lightchaindata", config.DatabaseCache, config.DatabaseHandles, "eth/db/chaindata/") 80 if err != nil { 81 return nil, err 82 } 83 lespayDb, err := stack.OpenDatabase("lespay", 0, 0, "eth/db/lespay") 84 if err != nil { 85 return nil, err 86 } 87 chainConfig, genesisHash, genesisErr := core.SetupGenesisBlock(chainDb, config.Genesis) 88 if _, isCompat := genesisErr.(*params.ConfigCompatError); genesisErr != nil && !isCompat { 89 return nil, genesisErr 90 } 91 log.Info("Initialised chain configuration", "config", chainConfig) 92 93 peers := newServerPeerSet() 94 leth := &LightEthereum{ 95 lesCommons: lesCommons{ 96 genesis: genesisHash, 97 config: config, 98 chainConfig: chainConfig, 99 iConfig: light.DefaultClientIndexerConfig, 100 chainDb: chainDb, 101 closeCh: make(chan struct{}), 102 }, 103 peers: peers, 104 eventMux: stack.EventMux(), 105 reqDist: newRequestDistributor(peers, &mclock.System{}), 106 accountManager: stack.AccountManager(), 107 engine: eth.CreateConsensusEngine(stack, chainConfig, &config.Ethash, nil, false, chainDb), 108 bloomRequests: make(chan chan *bloombits.Retrieval), 109 bloomIndexer: eth.NewBloomIndexer(chainDb, params.BloomBitsBlocksClient, params.HelperTrieConfirmations), 110 valueTracker: lpc.NewValueTracker(lespayDb, &mclock.System{}, requestList, time.Minute, 1/float64(time.Hour), 1/float64(time.Hour*100), 1/float64(time.Hour*1000)), 111 p2pServer: stack.Server(), 112 } 113 peers.subscribe((*vtSubscription)(leth.valueTracker)) 114 115 dnsdisc, err := leth.setupDiscovery() 116 if err != nil { 117 return nil, err 118 } 119 leth.serverPool = newServerPool(lespayDb, []byte("serverpool:"), leth.valueTracker, dnsdisc, time.Second, nil, &mclock.System{}, config.UltraLightServers) 120 peers.subscribe(leth.serverPool) 121 leth.dialCandidates = leth.serverPool.dialIterator 122 123 leth.retriever = newRetrieveManager(peers, leth.reqDist, leth.serverPool.getTimeout) 124 leth.relay = newLesTxRelay(peers, leth.retriever) 125 126 leth.odr = NewLesOdr(chainDb, light.DefaultClientIndexerConfig, leth.retriever) 127 leth.chtIndexer = light.NewChtIndexer(chainDb, leth.odr, params.CHTFrequency, params.HelperTrieConfirmations, config.LightNoPrune) 128 leth.bloomTrieIndexer = light.NewBloomTrieIndexer(chainDb, leth.odr, params.BloomBitsBlocksClient, params.BloomTrieFrequency, config.LightNoPrune) 129 leth.odr.SetIndexers(leth.chtIndexer, leth.bloomTrieIndexer, leth.bloomIndexer) 130 131 checkpoint := config.Checkpoint 132 if checkpoint == nil { 133 checkpoint = params.TrustedCheckpoints[genesisHash] 134 } 135 // Note: NewLightChain adds the trusted checkpoint so it needs an ODR with 136 // indexers already set but not started yet 137 if leth.blockchain, err = light.NewLightChain(leth.odr, leth.chainConfig, leth.engine, checkpoint); err != nil { 138 return nil, err 139 } 140 leth.chainReader = leth.blockchain 141 leth.txPool = light.NewTxPool(leth.chainConfig, leth.blockchain, leth.relay) 142 143 // Set up checkpoint oracle. 144 leth.oracle = leth.setupOracle(stack, genesisHash, config) 145 146 // Note: AddChildIndexer starts the update process for the child 147 leth.bloomIndexer.AddChildIndexer(leth.bloomTrieIndexer) 148 leth.chtIndexer.Start(leth.blockchain) 149 leth.bloomIndexer.Start(leth.blockchain) 150 151 // Start a light chain pruner to delete useless historical data. 152 leth.pruner = newPruner(chainDb, leth.chtIndexer, leth.bloomTrieIndexer) 153 154 // Rewind the chain in case of an incompatible config upgrade. 155 if compat, ok := genesisErr.(*params.ConfigCompatError); ok { 156 log.Warn("Rewinding chain to upgrade configuration", "err", compat) 157 leth.blockchain.SetHead(compat.RewindTo) 158 rawdb.WriteChainConfig(chainDb, genesisHash, chainConfig) 159 } 160 161 leth.ApiBackend = &LesApiBackend{stack.Config().ExtRPCEnabled(), leth, nil} 162 gpoParams := config.GPO 163 if gpoParams.Default == nil { 164 gpoParams.Default = config.Miner.GasPrice 165 } 166 leth.ApiBackend.gpo = gasprice.NewOracle(leth.ApiBackend, gpoParams) 167 168 leth.handler = newClientHandler(config.UltraLightServers, config.UltraLightFraction, checkpoint, leth) 169 if leth.handler.ulc != nil { 170 log.Warn("Ultra light client is enabled", "trustedNodes", len(leth.handler.ulc.keys), "minTrustedFraction", leth.handler.ulc.fraction) 171 leth.blockchain.DisableCheckFreq() 172 } 173 174 leth.netRPCService = ethapi.NewPublicNetAPI(leth.p2pServer, leth.config.NetworkId) 175 176 // Register the backend on the node 177 stack.RegisterAPIs(leth.APIs()) 178 stack.RegisterProtocols(leth.Protocols()) 179 stack.RegisterLifecycle(leth) 180 181 // Check for unclean shutdown 182 if uncleanShutdowns, discards, err := rawdb.PushUncleanShutdownMarker(chainDb); err != nil { 183 log.Error("Could not update unclean-shutdown-marker list", "error", err) 184 } else { 185 if discards > 0 { 186 log.Warn("Old unclean shutdowns found", "count", discards) 187 } 188 for _, tstamp := range uncleanShutdowns { 189 t := time.Unix(int64(tstamp), 0) 190 log.Warn("Unclean shutdown detected", "booted", t, 191 "age", common.PrettyAge(t)) 192 } 193 } 194 return leth, nil 195 } 196 197 // vtSubscription implements serverPeerSubscriber 198 type vtSubscription lpc.ValueTracker 199 200 // registerPeer implements serverPeerSubscriber 201 func (v *vtSubscription) registerPeer(p *serverPeer) { 202 vt := (*lpc.ValueTracker)(v) 203 p.setValueTracker(vt, vt.Register(p.ID())) 204 p.updateVtParams() 205 } 206 207 // unregisterPeer implements serverPeerSubscriber 208 func (v *vtSubscription) unregisterPeer(p *serverPeer) { 209 vt := (*lpc.ValueTracker)(v) 210 vt.Unregister(p.ID()) 211 p.setValueTracker(nil, nil) 212 } 213 214 type LightDummyAPI struct{} 215 216 // Etherbase is the address that mining rewards will be send to 217 func (s *LightDummyAPI) Etherbase() (common.Address, error) { 218 return common.Address{}, fmt.Errorf("mining is not supported in light mode") 219 } 220 221 // Coinbase is the address that mining rewards will be send to (alias for Etherbase) 222 func (s *LightDummyAPI) Coinbase() (common.Address, error) { 223 return common.Address{}, fmt.Errorf("mining is not supported in light mode") 224 } 225 226 // Hashrate returns the POW hashrate 227 func (s *LightDummyAPI) Hashrate() hexutil.Uint { 228 return 0 229 } 230 231 // Mining returns an indication if this node is currently mining. 232 func (s *LightDummyAPI) Mining() bool { 233 return false 234 } 235 236 // APIs returns the collection of RPC services the ethereum package offers. 237 // NOTE, some of these services probably need to be moved to somewhere else. 238 func (s *LightEthereum) APIs() []rpc.API { 239 apis := ethapi.GetAPIs(s.ApiBackend) 240 apis = append(apis, s.engine.APIs(s.BlockChain().HeaderChain())...) 241 return append(apis, []rpc.API{ 242 { 243 Namespace: "eth", 244 Version: "1.0", 245 Service: &LightDummyAPI{}, 246 Public: true, 247 }, { 248 Namespace: "eth", 249 Version: "1.0", 250 Service: downloader.NewPublicDownloaderAPI(s.handler.downloader, s.eventMux), 251 Public: true, 252 }, { 253 Namespace: "eth", 254 Version: "1.0", 255 Service: filters.NewPublicFilterAPI(s.ApiBackend, true), 256 Public: true, 257 }, { 258 Namespace: "net", 259 Version: "1.0", 260 Service: s.netRPCService, 261 Public: true, 262 }, { 263 Namespace: "les", 264 Version: "1.0", 265 Service: NewPrivateLightAPI(&s.lesCommons), 266 Public: false, 267 }, { 268 Namespace: "lespay", 269 Version: "1.0", 270 Service: lpc.NewPrivateClientAPI(s.valueTracker), 271 Public: false, 272 }, 273 }...) 274 } 275 276 func (s *LightEthereum) ResetWithGenesisBlock(gb *types.Block) { 277 s.blockchain.ResetWithGenesisBlock(gb) 278 } 279 280 func (s *LightEthereum) BlockChain() *light.LightChain { return s.blockchain } 281 func (s *LightEthereum) TxPool() *light.TxPool { return s.txPool } 282 func (s *LightEthereum) Engine() consensus.Engine { return s.engine } 283 func (s *LightEthereum) LesVersion() int { return int(ClientProtocolVersions[0]) } 284 func (s *LightEthereum) Downloader() *downloader.Downloader { return s.handler.downloader } 285 func (s *LightEthereum) EventMux() *event.TypeMux { return s.eventMux } 286 287 // Protocols returns all the currently configured network protocols to start. 288 func (s *LightEthereum) Protocols() []p2p.Protocol { 289 return s.makeProtocols(ClientProtocolVersions, s.handler.runPeer, func(id enode.ID) interface{} { 290 if p := s.peers.peer(id.String()); p != nil { 291 return p.Info() 292 } 293 return nil 294 }, s.dialCandidates) 295 } 296 297 // Start implements node.Lifecycle, starting all internal goroutines needed by the 298 // light ethereum protocol implementation. 299 func (s *LightEthereum) Start() error { 300 log.Warn("Light client mode is an experimental feature") 301 302 s.serverPool.start() 303 // Start bloom request workers. 304 s.wg.Add(bloomServiceThreads) 305 s.startBloomHandlers(params.BloomBitsBlocksClient) 306 s.handler.start() 307 308 return nil 309 } 310 311 // Stop implements node.Lifecycle, terminating all internal goroutines used by the 312 // Ethereum protocol. 313 func (s *LightEthereum) Stop() error { 314 close(s.closeCh) 315 s.serverPool.stop() 316 s.valueTracker.Stop() 317 s.peers.close() 318 s.reqDist.close() 319 s.odr.Stop() 320 s.relay.Stop() 321 s.bloomIndexer.Close() 322 s.chtIndexer.Close() 323 s.blockchain.Stop() 324 s.handler.stop() 325 s.txPool.Stop() 326 s.engine.Close() 327 s.pruner.close() 328 s.eventMux.Stop() 329 rawdb.PopUncleanShutdownMarker(s.chainDb) 330 s.chainDb.Close() 331 s.wg.Wait() 332 log.Info("Light ethereum stopped") 333 return nil 334 }