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  }