github.com/core-coin/go-core/v2@v2.1.9/les/client.go (about)

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