github.com/ebakus/go-ebakus@v1.0.5-0.20200520105415-dbccef9ec421/les/client.go (about)

     1  // Copyright 2019 The ebakus/go-ebakus Authors
     2  // This file is part of the ebakus/go-ebakus library.
     3  //
     4  // The ebakus/go-ebakus 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 ebakus/go-ebakus 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 ebakus/go-ebakus library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  // Package les implements the Light Ebakus Subprotocol.
    18  package les
    19  
    20  import (
    21  	"fmt"
    22  
    23  	"github.com/ebakus/go-ebakus/accounts"
    24  	"github.com/ebakus/go-ebakus/accounts/abi/bind"
    25  	"github.com/ebakus/go-ebakus/common"
    26  	"github.com/ebakus/go-ebakus/common/hexutil"
    27  	"github.com/ebakus/go-ebakus/common/mclock"
    28  	"github.com/ebakus/go-ebakus/consensus"
    29  	"github.com/ebakus/go-ebakus/core"
    30  	"github.com/ebakus/go-ebakus/core/bloombits"
    31  	"github.com/ebakus/go-ebakus/core/rawdb"
    32  	"github.com/ebakus/go-ebakus/core/types"
    33  	"github.com/ebakus/go-ebakus/eth"
    34  	"github.com/ebakus/go-ebakus/eth/downloader"
    35  	"github.com/ebakus/go-ebakus/eth/filters"
    36  	"github.com/ebakus/go-ebakus/eth/gasprice"
    37  	"github.com/ebakus/go-ebakus/event"
    38  	"github.com/ebakus/go-ebakus/internal/ethapi"
    39  	"github.com/ebakus/go-ebakus/light"
    40  	"github.com/ebakus/go-ebakus/log"
    41  	"github.com/ebakus/go-ebakus/node"
    42  	"github.com/ebakus/go-ebakus/p2p"
    43  	"github.com/ebakus/go-ebakus/p2p/enode"
    44  	"github.com/ebakus/go-ebakus/params"
    45  	"github.com/ebakus/go-ebakus/rpc"
    46  )
    47  
    48  type LightEbakus struct {
    49  	lesCommons
    50  
    51  	reqDist    *requestDistributor
    52  	retriever  *retrieveManager
    53  	odr        *LesOdr
    54  	relay      *lesTxRelay
    55  	handler    *clientHandler
    56  	txPool     *light.TxPool
    57  	blockchain *light.LightChain
    58  	serverPool *serverPool
    59  
    60  	bloomRequests chan chan *bloombits.Retrieval // Channel receiving bloom data retrieval requests
    61  	bloomIndexer  *core.ChainIndexer             // Bloom indexer operating during block imports
    62  
    63  	ApiBackend     *LesApiBackend
    64  	eventMux       *event.TypeMux
    65  	engine         consensus.Engine
    66  	accountManager *accounts.Manager
    67  	netRPCService  *ethapi.PublicNetAPI
    68  }
    69  
    70  func New(ctx *node.ServiceContext, config *eth.Config) (*LightEbakus, error) {
    71  	chainDb, err := ctx.OpenDatabase("lightchaindata", config.DatabaseCache, config.DatabaseHandles, "eth/db/chaindata/")
    72  	if err != nil {
    73  		return nil, err
    74  	}
    75  	stateDb, err := eth.CreateEbakusDB(ctx, config, "state.db")
    76  	if err != nil {
    77  		return nil, err
    78  	}
    79  	chainConfig, genesisHash, genesisErr := core.SetupGenesisBlock(chainDb, stateDb, config.Genesis)
    80  	if _, isCompat := genesisErr.(*params.ConfigCompatError); genesisErr != nil && !isCompat {
    81  		return nil, genesisErr
    82  	}
    83  	log.Info("Initialised chain configuration", "config", chainConfig)
    84  
    85  	peers := newPeerSet()
    86  	leth := &LightEbakus{
    87  		lesCommons: lesCommons{
    88  			genesis:     genesisHash,
    89  			config:      config,
    90  			chainConfig: chainConfig,
    91  			iConfig:     light.DefaultClientIndexerConfig,
    92  			chainDb:     chainDb,
    93  			peers:       peers,
    94  			closeCh:     make(chan struct{}),
    95  		},
    96  		eventMux:       ctx.EventMux,
    97  		reqDist:        newRequestDistributor(peers, &mclock.System{}),
    98  		accountManager: ctx.AccountManager,
    99  		engine:         eth.CreateConsensusEngine(ctx, &config.DPOS, chainConfig, chainDb, stateDb, config.Genesis),
   100  		bloomRequests:  make(chan chan *bloombits.Retrieval),
   101  		bloomIndexer:   eth.NewBloomIndexer(chainDb, params.BloomBitsBlocksClient, params.HelperTrieConfirmations),
   102  		serverPool:     newServerPool(chainDb, config.UltraLightServers),
   103  	}
   104  	leth.retriever = newRetrieveManager(peers, leth.reqDist, leth.serverPool)
   105  	leth.relay = newLesTxRelay(peers, leth.retriever)
   106  
   107  	leth.odr = NewLesOdr(chainDb, light.DefaultClientIndexerConfig, leth.retriever)
   108  	leth.chtIndexer = light.NewChtIndexer(chainDb, leth.odr, params.CHTFrequency, params.HelperTrieConfirmations)
   109  	leth.bloomTrieIndexer = light.NewBloomTrieIndexer(chainDb, leth.odr, params.BloomBitsBlocksClient, params.BloomTrieFrequency)
   110  	leth.odr.SetIndexers(leth.chtIndexer, leth.bloomTrieIndexer, leth.bloomIndexer)
   111  
   112  	checkpoint := config.Checkpoint
   113  	// Note: NewLightChain adds the trusted checkpoint so it needs an ODR with
   114  	// indexers already set but not started yet
   115  	if leth.blockchain, err = light.NewLightChain(leth.odr, leth.chainConfig, leth.engine, checkpoint); err != nil {
   116  		return nil, err
   117  	}
   118  	leth.chainReader = leth.blockchain
   119  	leth.txPool = light.NewTxPool(leth.chainConfig, leth.blockchain, leth.relay)
   120  
   121  	// Set up checkpoint oracle.
   122  	oracle := config.CheckpointOracle
   123  	leth.oracle = newCheckpointOracle(oracle, leth.localCheckpoint)
   124  
   125  	// Note: AddChildIndexer starts the update process for the child
   126  	leth.bloomIndexer.AddChildIndexer(leth.bloomTrieIndexer)
   127  	leth.chtIndexer.Start(leth.blockchain)
   128  	leth.bloomIndexer.Start(leth.blockchain)
   129  
   130  	leth.handler = newClientHandler(config.UltraLightServers, config.UltraLightFraction, checkpoint, leth)
   131  	if leth.handler.ulc != nil {
   132  		log.Warn("Ultra light client is enabled", "trustedNodes", len(leth.handler.ulc.keys), "minTrustedFraction", leth.handler.ulc.fraction)
   133  		leth.blockchain.DisableCheckFreq()
   134  	}
   135  	// Rewind the chain in case of an incompatible config upgrade.
   136  	if compat, ok := genesisErr.(*params.ConfigCompatError); ok {
   137  		log.Warn("Rewinding chain to upgrade configuration", "err", compat)
   138  		leth.blockchain.SetHead(compat.RewindTo)
   139  		rawdb.WriteChainConfig(chainDb, genesisHash, chainConfig)
   140  	}
   141  
   142  	leth.ApiBackend = &LesApiBackend{ctx.ExtRPCEnabled(), leth, nil}
   143  	gpoParams := config.GPO
   144  	if gpoParams.Default == nil {
   145  		gpoParams.Default = &config.Miner.GasPrice
   146  	}
   147  	leth.ApiBackend.gpo = gasprice.NewOracle(leth.ApiBackend, gpoParams)
   148  
   149  	return leth, nil
   150  }
   151  
   152  type LightDummyAPI struct{}
   153  
   154  // Etherbase is the address that mining rewards will be send to
   155  func (s *LightDummyAPI) Etherbase() (common.Address, error) {
   156  	return common.Address{}, fmt.Errorf("mining is not supported in light mode")
   157  }
   158  
   159  // Coinbase is the address that mining rewards will be send to (alias for Etherbase)
   160  func (s *LightDummyAPI) Coinbase() (common.Address, error) {
   161  	return common.Address{}, fmt.Errorf("mining is not supported in light mode")
   162  }
   163  
   164  // Hashrate returns the POW hashrate
   165  func (s *LightDummyAPI) Hashrate() hexutil.Uint {
   166  	return 0
   167  }
   168  
   169  // Mining returns an indication if this node is currently mining.
   170  func (s *LightDummyAPI) Mining() bool {
   171  	return false
   172  }
   173  
   174  // APIs returns the collection of RPC services the ebakus package offers.
   175  // NOTE, some of these services probably need to be moved to somewhere else.
   176  func (s *LightEbakus) APIs() []rpc.API {
   177  	return append(ethapi.GetAPIs(s.ApiBackend), []rpc.API{
   178  		{
   179  			Namespace: "eth",
   180  			Version:   "1.0",
   181  			Service:   &LightDummyAPI{},
   182  			Public:    true,
   183  		}, {
   184  			Namespace: "eth",
   185  			Version:   "1.0",
   186  			Service:   downloader.NewPublicDownloaderAPI(s.handler.downloader, s.eventMux),
   187  			Public:    true,
   188  		}, {
   189  			Namespace: "eth",
   190  			Version:   "1.0",
   191  			Service:   filters.NewPublicFilterAPI(s.ApiBackend, true),
   192  			Public:    true,
   193  		}, {
   194  			Namespace: "net",
   195  			Version:   "1.0",
   196  			Service:   s.netRPCService,
   197  			Public:    true,
   198  		}, {
   199  			Namespace: "les",
   200  			Version:   "1.0",
   201  			Service:   NewPrivateLightAPI(&s.lesCommons),
   202  			Public:    false,
   203  		},
   204  	}...)
   205  }
   206  
   207  func (s *LightEbakus) ResetWithGenesisBlock(gb *types.Block) {
   208  	s.blockchain.ResetWithGenesisBlock(gb)
   209  }
   210  
   211  func (s *LightEbakus) BlockChain() *light.LightChain      { return s.blockchain }
   212  func (s *LightEbakus) TxPool() *light.TxPool              { return s.txPool }
   213  func (s *LightEbakus) Engine() consensus.Engine           { return s.engine }
   214  func (s *LightEbakus) LesVersion() int                    { return int(ClientProtocolVersions[0]) }
   215  func (s *LightEbakus) Downloader() *downloader.Downloader { return s.handler.downloader }
   216  func (s *LightEbakus) EventMux() *event.TypeMux           { return s.eventMux }
   217  
   218  // Protocols implements node.Service, returning all the currently configured
   219  // network protocols to start.
   220  func (s *LightEbakus) Protocols() []p2p.Protocol {
   221  	return s.makeProtocols(ClientProtocolVersions, s.handler.runPeer, func(id enode.ID) interface{} {
   222  		if p := s.peers.Peer(peerIdToString(id)); p != nil {
   223  			return p.Info()
   224  		}
   225  		return nil
   226  	})
   227  }
   228  
   229  // Start implements node.Service, starting all internal goroutines needed by the
   230  // light ebakus protocol implementation.
   231  func (s *LightEbakus) Start(srvr *p2p.Server) error {
   232  	log.Warn("Light client mode is an experimental feature")
   233  
   234  	// Start bloom request workers.
   235  	s.wg.Add(bloomServiceThreads)
   236  	s.startBloomHandlers(params.BloomBitsBlocksClient)
   237  
   238  	s.netRPCService = ethapi.NewPublicNetAPI(srvr, s.config.NetworkId)
   239  
   240  	// clients are searching for the first advertised protocol in the list
   241  	protocolVersion := AdvertiseProtocolVersions[0]
   242  	s.serverPool.start(srvr, lesTopic(s.blockchain.Genesis().Hash(), protocolVersion))
   243  	return nil
   244  }
   245  
   246  // Stop implements node.Service, terminating all internal goroutines used by the
   247  // Ebakus protocol.
   248  func (s *LightEbakus) Stop() error {
   249  	close(s.closeCh)
   250  	s.peers.Close()
   251  	s.reqDist.close()
   252  	s.odr.Stop()
   253  	s.relay.Stop()
   254  	s.bloomIndexer.Close()
   255  	s.chtIndexer.Close()
   256  	s.blockchain.Stop()
   257  	s.handler.stop()
   258  	s.txPool.Stop()
   259  	s.engine.Close()
   260  	s.eventMux.Stop()
   261  	s.serverPool.stop()
   262  	s.chainDb.Close()
   263  	s.wg.Wait()
   264  	log.Info("Light ebakus stopped")
   265  	return nil
   266  }
   267  
   268  // SetClient sets the rpc client and binds the registrar contract.
   269  func (s *LightEbakus) SetContractBackend(backend bind.ContractBackend) {
   270  	if s.oracle == nil {
   271  		return
   272  	}
   273  	s.oracle.start(backend)
   274  }