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