github.com/theQRL/go-zond@v0.2.1/zond/backend.go (about)

     1  // Copyright 2014 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 zond implements the Zond protocol.
    18  package zond
    19  
    20  import (
    21  	"fmt"
    22  	"math/big"
    23  	"runtime"
    24  	"sync"
    25  
    26  	"github.com/theQRL/go-zond/accounts"
    27  	"github.com/theQRL/go-zond/common"
    28  	"github.com/theQRL/go-zond/common/hexutil"
    29  	"github.com/theQRL/go-zond/consensus"
    30  	"github.com/theQRL/go-zond/core"
    31  	"github.com/theQRL/go-zond/core/bloombits"
    32  	"github.com/theQRL/go-zond/core/rawdb"
    33  	"github.com/theQRL/go-zond/core/state/pruner"
    34  	"github.com/theQRL/go-zond/core/txpool"
    35  	"github.com/theQRL/go-zond/core/txpool/legacypool"
    36  	"github.com/theQRL/go-zond/core/types"
    37  	"github.com/theQRL/go-zond/core/vm"
    38  	"github.com/theQRL/go-zond/event"
    39  	"github.com/theQRL/go-zond/internal/shutdowncheck"
    40  	"github.com/theQRL/go-zond/internal/zondapi"
    41  	"github.com/theQRL/go-zond/log"
    42  	"github.com/theQRL/go-zond/miner"
    43  	"github.com/theQRL/go-zond/node"
    44  	"github.com/theQRL/go-zond/p2p"
    45  	"github.com/theQRL/go-zond/p2p/dnsdisc"
    46  	"github.com/theQRL/go-zond/p2p/enode"
    47  	"github.com/theQRL/go-zond/params"
    48  	"github.com/theQRL/go-zond/rlp"
    49  	"github.com/theQRL/go-zond/rpc"
    50  	"github.com/theQRL/go-zond/zond/downloader"
    51  	"github.com/theQRL/go-zond/zond/gasprice"
    52  	"github.com/theQRL/go-zond/zond/protocols/snap"
    53  	"github.com/theQRL/go-zond/zond/protocols/zond"
    54  	"github.com/theQRL/go-zond/zond/zondconfig"
    55  	"github.com/theQRL/go-zond/zonddb"
    56  )
    57  
    58  // Zond implements the Zond full node service.
    59  type Zond struct {
    60  	config *zondconfig.Config
    61  
    62  	// Handlers
    63  	txPool *txpool.TxPool
    64  
    65  	blockchain         *core.BlockChain
    66  	handler            *handler
    67  	zondDialCandidates enode.Iterator
    68  	snapDialCandidates enode.Iterator
    69  
    70  	// DB interfaces
    71  	chainDb zonddb.Database // Block chain database
    72  
    73  	eventMux       *event.TypeMux
    74  	engine         consensus.Engine
    75  	accountManager *accounts.Manager
    76  
    77  	bloomRequests     chan chan *bloombits.Retrieval // Channel receiving bloom data retrieval requests
    78  	bloomIndexer      *core.ChainIndexer             // Bloom indexer operating during block imports
    79  	closeBloomHandler chan struct{}
    80  
    81  	APIBackend *ZondAPIBackend
    82  
    83  	miner    *miner.Miner
    84  	gasPrice *big.Int
    85  
    86  	networkID     uint64
    87  	netRPCService *zondapi.NetAPI
    88  
    89  	p2pServer *p2p.Server
    90  
    91  	lock sync.RWMutex // Protects the variadic fields (e.g. gas price and etherbase)
    92  
    93  	shutdownTracker *shutdowncheck.ShutdownTracker // Tracks if and when the node has shutdown ungracefully
    94  }
    95  
    96  // New creates a new Zond object (including the initialisation of the common Zond object),
    97  // whose lifecycle will be managed by the provided node.
    98  func New(stack *node.Node, config *zondconfig.Config) (*Zond, error) {
    99  	// Ensure configuration values are compatible and sane
   100  	if !config.SyncMode.IsValid() {
   101  		return nil, fmt.Errorf("invalid sync mode %d", config.SyncMode)
   102  	}
   103  	if config.Miner.GasPrice == nil || config.Miner.GasPrice.Sign() <= 0 {
   104  		log.Warn("Sanitizing invalid miner gas price", "provided", config.Miner.GasPrice, "updated", zondconfig.Defaults.Miner.GasPrice)
   105  		config.Miner.GasPrice = new(big.Int).Set(zondconfig.Defaults.Miner.GasPrice)
   106  	}
   107  	if config.NoPruning && config.TrieDirtyCache > 0 {
   108  		if config.SnapshotCache > 0 {
   109  			config.TrieCleanCache += config.TrieDirtyCache * 3 / 5
   110  			config.SnapshotCache += config.TrieDirtyCache * 2 / 5
   111  		} else {
   112  			config.TrieCleanCache += config.TrieDirtyCache
   113  		}
   114  		config.TrieDirtyCache = 0
   115  	}
   116  	log.Info("Allocated trie memory caches", "clean", common.StorageSize(config.TrieCleanCache)*1024*1024, "dirty", common.StorageSize(config.TrieDirtyCache)*1024*1024)
   117  
   118  	// Assemble the Zond object
   119  	chainDb, err := stack.OpenDatabaseWithFreezer("chaindata", config.DatabaseCache, config.DatabaseHandles, config.DatabaseFreezer, "eth/db/chaindata/", false)
   120  	if err != nil {
   121  		return nil, err
   122  	}
   123  	// Try to recover offline state pruning only in hash-based.
   124  	if config.StateScheme == rawdb.HashScheme {
   125  		if err := pruner.RecoverPruning(stack.ResolvePath(""), chainDb); err != nil {
   126  			log.Error("Failed to recover state", "error", err)
   127  		}
   128  	}
   129  	chainConfig, err := core.LoadChainConfig(chainDb, config.Genesis)
   130  	if err != nil {
   131  		return nil, err
   132  	}
   133  	engine := zondconfig.CreateConsensusEngine()
   134  	networkID := config.NetworkId
   135  	if networkID == 0 {
   136  		networkID = chainConfig.ChainID.Uint64()
   137  	}
   138  	zond := &Zond{
   139  		config:            config,
   140  		chainDb:           chainDb,
   141  		eventMux:          stack.EventMux(),
   142  		accountManager:    stack.AccountManager(),
   143  		engine:            engine,
   144  		closeBloomHandler: make(chan struct{}),
   145  		networkID:         networkID,
   146  		gasPrice:          config.Miner.GasPrice,
   147  		bloomRequests:     make(chan chan *bloombits.Retrieval),
   148  		bloomIndexer:      core.NewBloomIndexer(chainDb, params.BloomBitsBlocks, params.BloomConfirms),
   149  		p2pServer:         stack.Server(),
   150  		shutdownTracker:   shutdowncheck.NewShutdownTracker(chainDb),
   151  	}
   152  	bcVersion := rawdb.ReadDatabaseVersion(chainDb)
   153  	var dbVer = "<nil>"
   154  	if bcVersion != nil {
   155  		dbVer = fmt.Sprintf("%d", *bcVersion)
   156  	}
   157  	log.Info("Initialising Zond protocol", "network", networkID, "dbversion", dbVer)
   158  
   159  	if !config.SkipBcVersionCheck {
   160  		if bcVersion != nil && *bcVersion > core.BlockChainVersion {
   161  			return nil, fmt.Errorf("database version is v%d, Gzond %s only supports v%d", *bcVersion, params.VersionWithMeta, core.BlockChainVersion)
   162  		} else if bcVersion == nil || *bcVersion < core.BlockChainVersion {
   163  			if bcVersion != nil { // only print warning on upgrade, not on init
   164  				log.Warn("Upgrade blockchain database version", "from", dbVer, "to", core.BlockChainVersion)
   165  			}
   166  			rawdb.WriteDatabaseVersion(chainDb, core.BlockChainVersion)
   167  		}
   168  	}
   169  	var (
   170  		vmConfig = vm.Config{
   171  			EnablePreimageRecording: config.EnablePreimageRecording,
   172  		}
   173  		cacheConfig = &core.CacheConfig{
   174  			TrieCleanLimit:      config.TrieCleanCache,
   175  			TrieCleanNoPrefetch: config.NoPrefetch,
   176  			TrieDirtyLimit:      config.TrieDirtyCache,
   177  			TrieDirtyDisabled:   config.NoPruning,
   178  			TrieTimeLimit:       config.TrieTimeout,
   179  			SnapshotLimit:       config.SnapshotCache,
   180  			Preimages:           config.Preimages,
   181  			StateHistory:        config.StateHistory,
   182  			StateScheme:         config.StateScheme,
   183  		}
   184  	)
   185  	zond.blockchain, err = core.NewBlockChain(chainDb, cacheConfig, config.Genesis, zond.engine, vmConfig, &config.TransactionHistory)
   186  	if err != nil {
   187  		return nil, err
   188  	}
   189  	zond.bloomIndexer.Start(zond.blockchain)
   190  
   191  	if config.TxPool.Journal != "" {
   192  		config.TxPool.Journal = stack.ResolvePath(config.TxPool.Journal)
   193  	}
   194  	legacyPool := legacypool.New(config.TxPool, zond.blockchain)
   195  	zond.txPool, err = txpool.New(new(big.Int).SetUint64(config.TxPool.PriceLimit), zond.blockchain, []txpool.SubPool{legacyPool})
   196  	if err != nil {
   197  		return nil, err
   198  	}
   199  	// Permit the downloader to use the trie cache allowance during fast sync
   200  	cacheLimit := cacheConfig.TrieCleanLimit + cacheConfig.TrieDirtyLimit + cacheConfig.SnapshotLimit
   201  	if zond.handler, err = newHandler(&handlerConfig{
   202  		NodeID:         zond.p2pServer.Self().ID(),
   203  		Database:       chainDb,
   204  		Chain:          zond.blockchain,
   205  		TxPool:         zond.txPool,
   206  		Network:        networkID,
   207  		Sync:           config.SyncMode,
   208  		BloomCache:     uint64(cacheLimit),
   209  		EventMux:       zond.eventMux,
   210  		RequiredBlocks: config.RequiredBlocks,
   211  	}); err != nil {
   212  		return nil, err
   213  	}
   214  
   215  	zond.miner = miner.New(zond, config.Miner, zond.engine)
   216  	zond.miner.SetExtra(makeExtraData(config.Miner.ExtraData))
   217  
   218  	zond.APIBackend = &ZondAPIBackend{stack.Config().ExtRPCEnabled(), zond, nil}
   219  
   220  	gpoParams := config.GPO
   221  	if gpoParams.Default == nil {
   222  		gpoParams.Default = config.Miner.GasPrice
   223  	}
   224  	zond.APIBackend.gpo = gasprice.NewOracle(zond.APIBackend, gpoParams)
   225  
   226  	// Setup DNS discovery iterators.
   227  	dnsclient := dnsdisc.NewClient(dnsdisc.Config{})
   228  	zond.zondDialCandidates, err = dnsclient.NewIterator(zond.config.ZondDiscoveryURLs...)
   229  	if err != nil {
   230  		return nil, err
   231  	}
   232  	zond.snapDialCandidates, err = dnsclient.NewIterator(zond.config.SnapDiscoveryURLs...)
   233  	if err != nil {
   234  		return nil, err
   235  	}
   236  
   237  	// Start the RPC service
   238  	zond.netRPCService = zondapi.NewNetAPI(zond.p2pServer, networkID)
   239  
   240  	// Register the backend on the node
   241  	stack.RegisterAPIs(zond.APIs())
   242  	stack.RegisterProtocols(zond.Protocols())
   243  	stack.RegisterLifecycle(zond)
   244  
   245  	// Successful startup; push a marker and check previous unclean shutdowns.
   246  	zond.shutdownTracker.MarkStartup()
   247  
   248  	return zond, nil
   249  }
   250  
   251  func makeExtraData(extra []byte) []byte {
   252  	if len(extra) == 0 {
   253  		// create default extradata
   254  		extra, _ = rlp.EncodeToBytes([]interface{}{
   255  			uint(params.VersionMajor<<16 | params.VersionMinor<<8 | params.VersionPatch),
   256  			"gzond",
   257  			runtime.Version(),
   258  			runtime.GOOS,
   259  		})
   260  	}
   261  	if uint64(len(extra)) > params.MaximumExtraDataSize {
   262  		log.Warn("Miner extra data exceed limit", "extra", hexutil.Bytes(extra), "limit", params.MaximumExtraDataSize)
   263  		extra = nil
   264  	}
   265  	return extra
   266  }
   267  
   268  // APIs return the collection of RPC services the zond package offers.
   269  // NOTE, some of these services probably need to be moved to somewhere else.
   270  func (s *Zond) APIs() []rpc.API {
   271  	apis := zondapi.GetAPIs(s.APIBackend)
   272  
   273  	// Append any APIs exposed explicitly by the consensus engine
   274  	apis = append(apis, s.engine.APIs(s.BlockChain())...)
   275  
   276  	// Append all the local APIs and return
   277  	return append(apis, []rpc.API{
   278  		{
   279  			Namespace: "miner",
   280  			Service:   NewMinerAPI(s),
   281  		}, {
   282  			Namespace: "zond",
   283  			Service:   downloader.NewDownloaderAPI(s.handler.downloader, s.eventMux),
   284  		}, {
   285  			Namespace: "admin",
   286  			Service:   NewAdminAPI(s),
   287  		}, {
   288  			Namespace: "debug",
   289  			Service:   NewDebugAPI(s),
   290  		}, {
   291  			Namespace: "net",
   292  			Service:   s.netRPCService,
   293  		},
   294  	}...)
   295  }
   296  
   297  func (s *Zond) ResetWithGenesisBlock(gb *types.Block) {
   298  	s.blockchain.ResetWithGenesisBlock(gb)
   299  }
   300  
   301  func (s *Zond) Miner() *miner.Miner { return s.miner }
   302  
   303  func (s *Zond) AccountManager() *accounts.Manager  { return s.accountManager }
   304  func (s *Zond) BlockChain() *core.BlockChain       { return s.blockchain }
   305  func (s *Zond) TxPool() *txpool.TxPool             { return s.txPool }
   306  func (s *Zond) EventMux() *event.TypeMux           { return s.eventMux }
   307  func (s *Zond) Engine() consensus.Engine           { return s.engine }
   308  func (s *Zond) ChainDb() zonddb.Database           { return s.chainDb }
   309  func (s *Zond) IsListening() bool                  { return true } // Always listening
   310  func (s *Zond) Downloader() *downloader.Downloader { return s.handler.downloader }
   311  func (s *Zond) Synced() bool                       { return s.handler.synced.Load() }
   312  func (s *Zond) SetSynced()                         { s.handler.enableSyncedFeatures() }
   313  func (s *Zond) ArchiveMode() bool                  { return s.config.NoPruning }
   314  func (s *Zond) BloomIndexer() *core.ChainIndexer   { return s.bloomIndexer }
   315  
   316  // Protocols returns all the currently configured
   317  // network protocols to start.
   318  func (s *Zond) Protocols() []p2p.Protocol {
   319  	protos := zond.MakeProtocols((*zondHandler)(s.handler), s.networkID, s.zondDialCandidates)
   320  	if s.config.SnapshotCache > 0 {
   321  		protos = append(protos, snap.MakeProtocols((*snapHandler)(s.handler), s.snapDialCandidates)...)
   322  	}
   323  	return protos
   324  }
   325  
   326  // Start implements node.Lifecycle, starting all internal goroutines needed by the
   327  // Zond protocol implementation.
   328  func (s *Zond) Start() error {
   329  	zond.StartENRUpdater(s.blockchain, s.p2pServer.LocalNode())
   330  
   331  	// Start the bloom bits servicing goroutines
   332  	s.startBloomHandlers(params.BloomBitsBlocks)
   333  
   334  	// Regularly update shutdown marker
   335  	s.shutdownTracker.Start()
   336  
   337  	// Figure out a max peers count based on the server limits
   338  	maxPeers := s.p2pServer.MaxPeers
   339  
   340  	// Start the networking layer if requested
   341  	s.handler.Start(maxPeers)
   342  	return nil
   343  }
   344  
   345  // Stop implements node.Lifecycle, terminating all internal goroutines used by the
   346  // Zond protocol.
   347  func (s *Zond) Stop() error {
   348  	// Stop all the peer-related stuff first.
   349  	s.zondDialCandidates.Close()
   350  	s.snapDialCandidates.Close()
   351  	s.handler.Stop()
   352  
   353  	// Then stop everything else.
   354  	s.bloomIndexer.Close()
   355  	close(s.closeBloomHandler)
   356  	s.txPool.Close()
   357  	s.blockchain.Stop()
   358  	s.engine.Close()
   359  
   360  	// Clean shutdown marker as the last thing before closing db
   361  	s.shutdownTracker.Stop()
   362  
   363  	s.chainDb.Close()
   364  	s.eventMux.Stop()
   365  
   366  	return nil
   367  }
   368  
   369  // SyncMode retrieves the current sync mode, either explicitly set, or derived
   370  // from the chain status.
   371  func (s *Zond) SyncMode() downloader.SyncMode {
   372  	// If we're in snap sync mode, return that directly
   373  	if s.handler.snapSync.Load() {
   374  		return downloader.SnapSync
   375  	}
   376  	// We are probably in full sync, but we might have rewound to before the
   377  	// snap sync pivot, check if we should re-enable snap sync.
   378  	head := s.blockchain.CurrentBlock()
   379  	if pivot := rawdb.ReadLastPivotNumber(s.chainDb); pivot != nil {
   380  		if head.Number.Uint64() < *pivot {
   381  			return downloader.SnapSync
   382  		}
   383  	}
   384  	// We are in a full sync, but the associated head state is missing. To complete
   385  	// the head state, forcefully rerun the snap sync. Note it doesn't mean the
   386  	// persistent state is corrupted, just mismatch with the head block.
   387  	if !s.blockchain.HasState(head.Root) {
   388  		log.Info("Reenabled snap sync as chain is stateless")
   389  		return downloader.SnapSync
   390  	}
   391  	// Nope, we're really full syncing
   392  	return downloader.FullSync
   393  }