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