github.com/snowblossomcoin/go-ethereum@v1.9.25/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/ethereum/go-ethereum/accounts"
    25  	"github.com/ethereum/go-ethereum/common"
    26  	"github.com/ethereum/go-ethereum/common/hexutil"
    27  	"github.com/ethereum/go-ethereum/common/mclock"
    28  	"github.com/ethereum/go-ethereum/consensus"
    29  	"github.com/ethereum/go-ethereum/core"
    30  	"github.com/ethereum/go-ethereum/core/bloombits"
    31  	"github.com/ethereum/go-ethereum/core/rawdb"
    32  	"github.com/ethereum/go-ethereum/core/types"
    33  	"github.com/ethereum/go-ethereum/eth"
    34  	"github.com/ethereum/go-ethereum/eth/downloader"
    35  	"github.com/ethereum/go-ethereum/eth/filters"
    36  	"github.com/ethereum/go-ethereum/eth/gasprice"
    37  	"github.com/ethereum/go-ethereum/event"
    38  	"github.com/ethereum/go-ethereum/internal/ethapi"
    39  	lpc "github.com/ethereum/go-ethereum/les/lespay/client"
    40  	"github.com/ethereum/go-ethereum/light"
    41  	"github.com/ethereum/go-ethereum/log"
    42  	"github.com/ethereum/go-ethereum/node"
    43  	"github.com/ethereum/go-ethereum/p2p"
    44  	"github.com/ethereum/go-ethereum/p2p/enode"
    45  	"github.com/ethereum/go-ethereum/params"
    46  	"github.com/ethereum/go-ethereum/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(&stack.Config().P2P)
   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  	return leth, nil
   182  }
   183  
   184  // vtSubscription implements serverPeerSubscriber
   185  type vtSubscription lpc.ValueTracker
   186  
   187  // registerPeer implements serverPeerSubscriber
   188  func (v *vtSubscription) registerPeer(p *serverPeer) {
   189  	vt := (*lpc.ValueTracker)(v)
   190  	p.setValueTracker(vt, vt.Register(p.ID()))
   191  	p.updateVtParams()
   192  }
   193  
   194  // unregisterPeer implements serverPeerSubscriber
   195  func (v *vtSubscription) unregisterPeer(p *serverPeer) {
   196  	vt := (*lpc.ValueTracker)(v)
   197  	vt.Unregister(p.ID())
   198  	p.setValueTracker(nil, nil)
   199  }
   200  
   201  type LightDummyAPI struct{}
   202  
   203  // Etherbase is the address that mining rewards will be send to
   204  func (s *LightDummyAPI) Etherbase() (common.Address, error) {
   205  	return common.Address{}, fmt.Errorf("mining is not supported in light mode")
   206  }
   207  
   208  // Coinbase is the address that mining rewards will be send to (alias for Etherbase)
   209  func (s *LightDummyAPI) Coinbase() (common.Address, error) {
   210  	return common.Address{}, fmt.Errorf("mining is not supported in light mode")
   211  }
   212  
   213  // Hashrate returns the POW hashrate
   214  func (s *LightDummyAPI) Hashrate() hexutil.Uint {
   215  	return 0
   216  }
   217  
   218  // Mining returns an indication if this node is currently mining.
   219  func (s *LightDummyAPI) Mining() bool {
   220  	return false
   221  }
   222  
   223  // APIs returns the collection of RPC services the ethereum package offers.
   224  // NOTE, some of these services probably need to be moved to somewhere else.
   225  func (s *LightEthereum) APIs() []rpc.API {
   226  	apis := ethapi.GetAPIs(s.ApiBackend)
   227  	apis = append(apis, s.engine.APIs(s.BlockChain().HeaderChain())...)
   228  	return append(apis, []rpc.API{
   229  		{
   230  			Namespace: "eth",
   231  			Version:   "1.0",
   232  			Service:   &LightDummyAPI{},
   233  			Public:    true,
   234  		}, {
   235  			Namespace: "eth",
   236  			Version:   "1.0",
   237  			Service:   downloader.NewPublicDownloaderAPI(s.handler.downloader, s.eventMux),
   238  			Public:    true,
   239  		}, {
   240  			Namespace: "eth",
   241  			Version:   "1.0",
   242  			Service:   filters.NewPublicFilterAPI(s.ApiBackend, true),
   243  			Public:    true,
   244  		}, {
   245  			Namespace: "net",
   246  			Version:   "1.0",
   247  			Service:   s.netRPCService,
   248  			Public:    true,
   249  		}, {
   250  			Namespace: "les",
   251  			Version:   "1.0",
   252  			Service:   NewPrivateLightAPI(&s.lesCommons),
   253  			Public:    false,
   254  		}, {
   255  			Namespace: "lespay",
   256  			Version:   "1.0",
   257  			Service:   lpc.NewPrivateClientAPI(s.valueTracker),
   258  			Public:    false,
   259  		},
   260  	}...)
   261  }
   262  
   263  func (s *LightEthereum) ResetWithGenesisBlock(gb *types.Block) {
   264  	s.blockchain.ResetWithGenesisBlock(gb)
   265  }
   266  
   267  func (s *LightEthereum) BlockChain() *light.LightChain      { return s.blockchain }
   268  func (s *LightEthereum) TxPool() *light.TxPool              { return s.txPool }
   269  func (s *LightEthereum) Engine() consensus.Engine           { return s.engine }
   270  func (s *LightEthereum) LesVersion() int                    { return int(ClientProtocolVersions[0]) }
   271  func (s *LightEthereum) Downloader() *downloader.Downloader { return s.handler.downloader }
   272  func (s *LightEthereum) EventMux() *event.TypeMux           { return s.eventMux }
   273  
   274  // Protocols returns all the currently configured network protocols to start.
   275  func (s *LightEthereum) Protocols() []p2p.Protocol {
   276  	return s.makeProtocols(ClientProtocolVersions, s.handler.runPeer, func(id enode.ID) interface{} {
   277  		if p := s.peers.peer(id.String()); p != nil {
   278  			return p.Info()
   279  		}
   280  		return nil
   281  	}, s.dialCandidates)
   282  }
   283  
   284  // Start implements node.Lifecycle, starting all internal goroutines needed by the
   285  // light ethereum protocol implementation.
   286  func (s *LightEthereum) Start() error {
   287  	log.Warn("Light client mode is an experimental feature")
   288  
   289  	s.serverPool.start()
   290  	// Start bloom request workers.
   291  	s.wg.Add(bloomServiceThreads)
   292  	s.startBloomHandlers(params.BloomBitsBlocksClient)
   293  	s.handler.start()
   294  
   295  	return nil
   296  }
   297  
   298  // Stop implements node.Lifecycle, terminating all internal goroutines used by the
   299  // Ethereum protocol.
   300  func (s *LightEthereum) Stop() error {
   301  	close(s.closeCh)
   302  	s.serverPool.stop()
   303  	s.valueTracker.Stop()
   304  	s.peers.close()
   305  	s.reqDist.close()
   306  	s.odr.Stop()
   307  	s.relay.Stop()
   308  	s.bloomIndexer.Close()
   309  	s.chtIndexer.Close()
   310  	s.blockchain.Stop()
   311  	s.handler.stop()
   312  	s.txPool.Stop()
   313  	s.engine.Close()
   314  	s.pruner.close()
   315  	s.eventMux.Stop()
   316  	s.chainDb.Close()
   317  	s.wg.Wait()
   318  	log.Info("Light ethereum stopped")
   319  	return nil
   320  }