github.com/neatlab/neatio@v1.7.3-0.20220425043230-d903e92fcc75/neatptc/backend.go (about)

     1  package neatptc
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"math/big"
     7  	"runtime"
     8  	"sync"
     9  	"sync/atomic"
    10  
    11  	"github.com/neatlab/neatio/chain/accounts"
    12  	"github.com/neatlab/neatio/chain/consensus"
    13  	"github.com/neatlab/neatio/chain/consensus/neatcon"
    14  	ntcBackend "github.com/neatlab/neatio/chain/consensus/neatcon"
    15  	"github.com/neatlab/neatio/chain/core"
    16  	"github.com/neatlab/neatio/chain/core/bloombits"
    17  	"github.com/neatlab/neatio/chain/core/datareduction"
    18  	"github.com/neatlab/neatio/chain/core/rawdb"
    19  	"github.com/neatlab/neatio/chain/core/types"
    20  	"github.com/neatlab/neatio/chain/core/vm"
    21  	"github.com/neatlab/neatio/chain/log"
    22  	"github.com/neatlab/neatio/internal/neatapi"
    23  	"github.com/neatlab/neatio/neatdb"
    24  	"github.com/neatlab/neatio/neatptc/downloader"
    25  	"github.com/neatlab/neatio/neatptc/filters"
    26  	"github.com/neatlab/neatio/neatptc/gasprice"
    27  	"github.com/neatlab/neatio/network/node"
    28  	"github.com/neatlab/neatio/network/p2p"
    29  	"github.com/neatlab/neatio/network/rpc"
    30  	"github.com/neatlab/neatio/params"
    31  	"github.com/neatlab/neatio/utilities/common"
    32  	"github.com/neatlab/neatio/utilities/common/hexutil"
    33  	"github.com/neatlab/neatio/utilities/event"
    34  	"github.com/neatlab/neatio/utilities/miner"
    35  	"github.com/neatlab/neatio/utilities/rlp"
    36  	"gopkg.in/urfave/cli.v1"
    37  )
    38  
    39  type LesServer interface {
    40  	Start(srvr *p2p.Server)
    41  	Stop()
    42  	Protocols() []p2p.Protocol
    43  	SetBloomBitsIndexer(bbIndexer *core.ChainIndexer)
    44  }
    45  
    46  type NeatIO struct {
    47  	config      *Config
    48  	chainConfig *params.ChainConfig
    49  
    50  	shutdownChan chan bool
    51  
    52  	txPool          *core.TxPool
    53  	blockchain      *core.BlockChain
    54  	protocolManager *ProtocolManager
    55  
    56  	chainDb neatdb.Database
    57  	pruneDb neatdb.Database
    58  
    59  	eventMux       *event.TypeMux
    60  	engine         consensus.NeatCon
    61  	accountManager *accounts.Manager
    62  
    63  	bloomRequests chan chan *bloombits.Retrieval
    64  	bloomIndexer  *core.ChainIndexer
    65  
    66  	ApiBackend *EthApiBackend
    67  
    68  	miner    *miner.Miner
    69  	gasPrice *big.Int
    70  	coinbase common.Address
    71  	solcPath string
    72  
    73  	networkId     uint64
    74  	netRPCService *neatapi.PublicNetAPI
    75  
    76  	lock sync.RWMutex
    77  }
    78  
    79  func New(ctx *node.ServiceContext, config *Config, cliCtx *cli.Context,
    80  	cch core.CrossChainHelper, logger log.Logger, isTestnet bool) (*NeatIO, error) {
    81  
    82  	if !config.SyncMode.IsValid() {
    83  		return nil, fmt.Errorf("invalid sync mode %d", config.SyncMode)
    84  	}
    85  	chainDb, err := ctx.OpenDatabase("chaindata", config.DatabaseCache, config.DatabaseHandles, "neatio/db/chaindata/")
    86  	if err != nil {
    87  		return nil, err
    88  	}
    89  	pruneDb, err := ctx.OpenDatabase("prunedata", config.DatabaseCache, config.DatabaseHandles, "neatio/db/prune/")
    90  	if err != nil {
    91  		return nil, err
    92  	}
    93  
    94  	isMainChain := params.IsMainChain(ctx.ChainId())
    95  
    96  	chainConfig, genesisHash, genesisErr := core.SetupGenesisBlockWithDefault(chainDb, config.Genesis, isMainChain, isTestnet)
    97  	if _, ok := genesisErr.(*params.ConfigCompatError); genesisErr != nil && !ok {
    98  		return nil, genesisErr
    99  	}
   100  
   101  	chainConfig.ConstantinopleBlock = big.NewInt(0)
   102  	chainConfig.PetersburgBlock = big.NewInt(0)
   103  	chainConfig.IstanbulBlock = big.NewInt(0)
   104  
   105  	chainConfig.ChainLogger = logger
   106  	logger.Info("Initialised chain configuration", "config", chainConfig)
   107  
   108  	neatChain := &NeatIO{
   109  		config:         config,
   110  		chainDb:        chainDb,
   111  		pruneDb:        pruneDb,
   112  		chainConfig:    chainConfig,
   113  		eventMux:       ctx.EventMux,
   114  		accountManager: ctx.AccountManager,
   115  		engine:         CreateConsensusEngine(ctx, config, chainConfig, chainDb, cliCtx, cch),
   116  		shutdownChan:   make(chan bool),
   117  		networkId:      config.NetworkId,
   118  		gasPrice:       config.MinerGasPrice,
   119  		coinbase:       config.Coinbase,
   120  		solcPath:       config.SolcPath,
   121  		bloomRequests:  make(chan chan *bloombits.Retrieval),
   122  		bloomIndexer:   NewBloomIndexer(chainDb, params.BloomBitsBlocks),
   123  	}
   124  
   125  	bcVersion := rawdb.ReadDatabaseVersion(chainDb)
   126  	var dbVer = "<nil>"
   127  	if bcVersion != nil {
   128  		dbVer = fmt.Sprintf("%d", *bcVersion)
   129  	}
   130  	logger.Info("Initialising NeatIO protocol", "versions", neatChain.engine.Protocol().Versions, "network", config.NetworkId, "dbversion", dbVer)
   131  
   132  	if !config.SkipBcVersionCheck {
   133  		if bcVersion != nil && *bcVersion > core.BlockChainVersion {
   134  			return nil, fmt.Errorf("database version is v%d, Neatio %s only supports v%d", *bcVersion, params.VersionWithMeta, core.BlockChainVersion)
   135  		} else if bcVersion == nil || *bcVersion < core.BlockChainVersion {
   136  			logger.Warn("Upgrade blockchain database version", "from", dbVer, "to", core.BlockChainVersion)
   137  			rawdb.WriteDatabaseVersion(chainDb, core.BlockChainVersion)
   138  		}
   139  	}
   140  	var (
   141  		vmConfig    = vm.Config{EnablePreimageRecording: config.EnablePreimageRecording}
   142  		cacheConfig = &core.CacheConfig{
   143  			TrieCleanLimit: config.TrieCleanCache,
   144  
   145  			TrieDirtyLimit:    config.TrieDirtyCache,
   146  			TrieDirtyDisabled: config.NoPruning,
   147  			TrieTimeLimit:     config.TrieTimeout,
   148  		}
   149  	)
   150  
   151  	neatChain.blockchain, err = core.NewBlockChain(chainDb, cacheConfig, neatChain.chainConfig, neatChain.engine, vmConfig, cch)
   152  	if err != nil {
   153  		return nil, err
   154  	}
   155  
   156  	if compat, ok := genesisErr.(*params.ConfigCompatError); ok {
   157  		logger.Warn("Rewinding chain to upgrade configuration", "err", compat)
   158  		neatChain.blockchain.SetHead(compat.RewindTo)
   159  		rawdb.WriteChainConfig(chainDb, genesisHash, chainConfig)
   160  	}
   161  	neatChain.bloomIndexer.Start(neatChain.blockchain)
   162  
   163  	if config.TxPool.Journal != "" {
   164  		config.TxPool.Journal = ctx.ResolvePath(config.TxPool.Journal)
   165  	}
   166  	neatChain.txPool = core.NewTxPool(config.TxPool, neatChain.chainConfig, neatChain.blockchain, cch)
   167  
   168  	if neatChain.protocolManager, err = NewProtocolManager(neatChain.chainConfig, config.SyncMode, config.NetworkId, neatChain.eventMux, neatChain.txPool, neatChain.engine, neatChain.blockchain, chainDb, cch); err != nil {
   169  		return nil, err
   170  	}
   171  	neatChain.miner = miner.New(neatChain, neatChain.chainConfig, neatChain.EventMux(), neatChain.engine, config.MinerGasFloor, config.MinerGasCeil, cch)
   172  	neatChain.miner.SetExtra(makeExtraData(config.ExtraData))
   173  
   174  	neatChain.ApiBackend = &EthApiBackend{neatChain, nil, cch}
   175  	gpoParams := config.GPO
   176  	if gpoParams.Default == nil {
   177  		gpoParams.Default = config.MinerGasPrice
   178  	}
   179  	neatChain.ApiBackend.gpo = gasprice.NewOracle(neatChain.ApiBackend, gpoParams)
   180  
   181  	return neatChain, nil
   182  }
   183  
   184  func makeExtraData(extra []byte) []byte {
   185  	if len(extra) == 0 {
   186  
   187  		extra, _ = rlp.EncodeToBytes([]interface{}{
   188  			uint(params.VersionMajor<<16 | params.VersionMinor<<8 | params.VersionPatch),
   189  			"neatio",
   190  			runtime.Version(),
   191  			runtime.GOOS,
   192  		})
   193  	}
   194  	if uint64(len(extra)) > params.MaximumExtraDataSize {
   195  		log.Warn("Miner extra data exceed limit", "extra", hexutil.Bytes(extra), "limit", params.MaximumExtraDataSize)
   196  		extra = nil
   197  	}
   198  	return extra
   199  }
   200  
   201  func CreateConsensusEngine(ctx *node.ServiceContext, config *Config, chainConfig *params.ChainConfig, db neatdb.Database,
   202  	cliCtx *cli.Context, cch core.CrossChainHelper) consensus.NeatCon {
   203  
   204  	if chainConfig.NeatCon.Epoch != 0 {
   205  		config.NeatCon.Epoch = chainConfig.NeatCon.Epoch
   206  	}
   207  	config.NeatCon.ProposerPolicy = neatcon.ProposerPolicy(chainConfig.NeatCon.ProposerPolicy)
   208  	return ntcBackend.New(chainConfig, cliCtx, ctx.NodeKey(), cch)
   209  }
   210  
   211  func (s *NeatIO) APIs() []rpc.API {
   212  
   213  	apis := neatapi.GetAPIs(s.ApiBackend, s.solcPath)
   214  
   215  	apis = append(apis, s.engine.APIs(s.BlockChain())...)
   216  
   217  	apis = append(apis, []rpc.API{
   218  		{
   219  			Namespace: "eth",
   220  			Version:   "1.0",
   221  			Service:   NewPublicEthereumAPI(s),
   222  			Public:    true,
   223  		}, {
   224  			Namespace: "eth",
   225  			Version:   "1.0",
   226  			Service:   NewPublicMinerAPI(s),
   227  			Public:    true,
   228  		}, {
   229  			Namespace: "eth",
   230  			Version:   "1.0",
   231  			Service:   downloader.NewPublicDownloaderAPI(s.protocolManager.downloader, s.eventMux),
   232  			Public:    true,
   233  		}, {
   234  			Namespace: "neat",
   235  			Version:   "1.0",
   236  			Service:   NewPublicEthereumAPI(s),
   237  			Public:    true,
   238  		}, {
   239  			Namespace: "neat",
   240  			Version:   "1.0",
   241  			Service:   NewPublicMinerAPI(s),
   242  			Public:    true,
   243  		}, {
   244  			Namespace: "neat",
   245  			Version:   "1.0",
   246  			Service:   downloader.NewPublicDownloaderAPI(s.protocolManager.downloader, s.eventMux),
   247  			Public:    true,
   248  		}, {
   249  			Namespace: "miner",
   250  			Version:   "1.0",
   251  			Service:   NewPrivateMinerAPI(s),
   252  			Public:    false,
   253  		}, {
   254  			Namespace: "eth",
   255  			Version:   "1.0",
   256  			Service:   filters.NewPublicFilterAPI(s.ApiBackend, false),
   257  			Public:    true,
   258  		}, {
   259  			Namespace: "neat",
   260  			Version:   "1.0",
   261  			Service:   filters.NewPublicFilterAPI(s.ApiBackend, false),
   262  			Public:    true,
   263  		}, {
   264  			Namespace: "admin",
   265  			Version:   "1.0",
   266  			Service:   NewPrivateAdminAPI(s),
   267  		}, {
   268  			Namespace: "debug",
   269  			Version:   "1.0",
   270  			Service:   NewPublicDebugAPI(s),
   271  			Public:    true,
   272  		}, {
   273  			Namespace: "debug",
   274  			Version:   "1.0",
   275  			Service:   NewPrivateDebugAPI(s.chainConfig, s),
   276  		}, {
   277  			Namespace: "net",
   278  			Version:   "1.0",
   279  			Service:   s.netRPCService,
   280  			Public:    true,
   281  		},
   282  	}...)
   283  	return apis
   284  }
   285  
   286  func (s *NeatIO) ResetWithGenesisBlock(gb *types.Block) {
   287  	s.blockchain.ResetWithGenesisBlock(gb)
   288  }
   289  
   290  func (s *NeatIO) Coinbase() (eb common.Address, err error) {
   291  	if neatcon, ok := s.engine.(consensus.NeatCon); ok {
   292  		eb = neatcon.PrivateValidator()
   293  		if eb != (common.Address{}) {
   294  			return eb, nil
   295  		} else {
   296  			return eb, errors.New("private validator missing")
   297  		}
   298  	} else {
   299  		s.lock.RLock()
   300  		coinbase := s.coinbase
   301  		s.lock.RUnlock()
   302  
   303  		if coinbase != (common.Address{}) {
   304  			return coinbase, nil
   305  		}
   306  		if wallets := s.AccountManager().Wallets(); len(wallets) > 0 {
   307  			if accounts := wallets[0].Accounts(); len(accounts) > 0 {
   308  				coinbase := accounts[0].Address
   309  
   310  				s.lock.Lock()
   311  				s.coinbase = coinbase
   312  				s.lock.Unlock()
   313  
   314  				log.Info("Coinbase automatically configured", "address", coinbase)
   315  				return coinbase, nil
   316  			}
   317  		}
   318  	}
   319  	return common.Address{}, fmt.Errorf("etherbase must be explicitly specified")
   320  }
   321  
   322  func (self *NeatIO) SetCoinbase(coinbase common.Address) {
   323  
   324  	if _, ok := self.engine.(consensus.NeatCon); ok {
   325  		log.Error("Cannot set etherbase in NeatCon consensus")
   326  		return
   327  	}
   328  
   329  	self.lock.Lock()
   330  	self.coinbase = coinbase
   331  	self.lock.Unlock()
   332  
   333  	self.miner.SetCoinbase(coinbase)
   334  }
   335  
   336  func (s *NeatIO) StartMining(local bool) error {
   337  	var eb common.Address
   338  	if neatcon, ok := s.engine.(consensus.NeatCon); ok {
   339  		eb = neatcon.PrivateValidator()
   340  		if (eb == common.Address{}) {
   341  			log.Error("Cannot start mining without private validator")
   342  			return errors.New("private validator file missing")
   343  		}
   344  	} else {
   345  		_, err := s.Coinbase()
   346  		if err != nil {
   347  			log.Error("Cannot start mining without etherbase", "err", err)
   348  			return fmt.Errorf("etherbase missing: %v", err)
   349  		}
   350  	}
   351  
   352  	if local {
   353  
   354  		atomic.StoreUint32(&s.protocolManager.acceptTxs, 1)
   355  	}
   356  	go s.miner.Start(eb)
   357  	return nil
   358  }
   359  
   360  func (s *NeatIO) StopMining()         { s.miner.Stop() }
   361  func (s *NeatIO) IsMining() bool      { return s.miner.Mining() }
   362  func (s *NeatIO) Miner() *miner.Miner { return s.miner }
   363  
   364  func (s *NeatIO) ChainConfig() *params.ChainConfig   { return s.chainConfig }
   365  func (s *NeatIO) AccountManager() *accounts.Manager  { return s.accountManager }
   366  func (s *NeatIO) BlockChain() *core.BlockChain       { return s.blockchain }
   367  func (s *NeatIO) TxPool() *core.TxPool               { return s.txPool }
   368  func (s *NeatIO) EventMux() *event.TypeMux           { return s.eventMux }
   369  func (s *NeatIO) Engine() consensus.NeatCon          { return s.engine }
   370  func (s *NeatIO) ChainDb() neatdb.Database           { return s.chainDb }
   371  func (s *NeatIO) IsListening() bool                  { return true }
   372  func (s *NeatIO) EthVersion() int                    { return int(s.protocolManager.SubProtocols[0].Version) }
   373  func (s *NeatIO) NetVersion() uint64                 { return s.networkId }
   374  func (s *NeatIO) Downloader() *downloader.Downloader { return s.protocolManager.downloader }
   375  
   376  func (s *NeatIO) Protocols() []p2p.Protocol {
   377  	return s.protocolManager.SubProtocols
   378  }
   379  
   380  func (s *NeatIO) Start(srvr *p2p.Server) error {
   381  
   382  	s.startBloomHandlers()
   383  
   384  	s.netRPCService = neatapi.NewPublicNetAPI(srvr, s.NetVersion())
   385  
   386  	maxPeers := srvr.MaxPeers
   387  
   388  	s.protocolManager.Start(maxPeers)
   389  
   390  	go s.loopForMiningEvent()
   391  
   392  	if s.config.PruneStateData && s.chainConfig.NeatChainId == "side_0" {
   393  		go s.StartScanAndPrune(0)
   394  	}
   395  
   396  	return nil
   397  }
   398  
   399  func (s *NeatIO) Stop() error {
   400  	s.bloomIndexer.Close()
   401  	s.blockchain.Stop()
   402  	s.protocolManager.Stop()
   403  	s.txPool.Stop()
   404  	s.miner.Stop()
   405  	s.engine.Close()
   406  	s.miner.Close()
   407  	s.eventMux.Stop()
   408  
   409  	s.chainDb.Close()
   410  	s.pruneDb.Close()
   411  	close(s.shutdownChan)
   412  
   413  	return nil
   414  }
   415  
   416  func (s *NeatIO) loopForMiningEvent() {
   417  
   418  	startMiningCh := make(chan core.StartMiningEvent, 1)
   419  	startMiningSub := s.blockchain.SubscribeStartMiningEvent(startMiningCh)
   420  
   421  	stopMiningCh := make(chan core.StopMiningEvent, 1)
   422  	stopMiningSub := s.blockchain.SubscribeStopMiningEvent(stopMiningCh)
   423  
   424  	defer startMiningSub.Unsubscribe()
   425  	defer stopMiningSub.Unsubscribe()
   426  
   427  	for {
   428  		select {
   429  		case <-startMiningCh:
   430  			if !s.IsMining() {
   431  				s.lock.RLock()
   432  				price := s.gasPrice
   433  				s.lock.RUnlock()
   434  				s.txPool.SetGasPrice(price)
   435  				s.chainConfig.ChainLogger.Info("NeatCon Consensus Engine will start shortly")
   436  				s.engine.(consensus.NeatCon).ForceStart()
   437  				s.StartMining(true)
   438  			} else {
   439  				s.chainConfig.ChainLogger.Info("NeatCon Consensus Engine already started")
   440  			}
   441  		case <-stopMiningCh:
   442  			if s.IsMining() {
   443  				s.chainConfig.ChainLogger.Info("NeatCon Consensus Engine will stop shortly")
   444  				s.StopMining()
   445  			} else {
   446  				s.chainConfig.ChainLogger.Info("NeatCon Consensus Engine already stopped")
   447  			}
   448  		case <-startMiningSub.Err():
   449  			return
   450  		case <-stopMiningSub.Err():
   451  			return
   452  		}
   453  	}
   454  }
   455  
   456  func (s *NeatIO) StartScanAndPrune(blockNumber uint64) {
   457  
   458  	if datareduction.StartPruning() {
   459  		log.Info("Data Reduction - Start")
   460  	} else {
   461  		log.Info("Data Reduction - Pruning is already running")
   462  		return
   463  	}
   464  
   465  	latestBlockNumber := s.blockchain.CurrentHeader().Number.Uint64()
   466  	if blockNumber == 0 || blockNumber >= latestBlockNumber {
   467  		blockNumber = latestBlockNumber
   468  		log.Infof("Data Reduction - Last block number %v", blockNumber)
   469  	} else {
   470  		log.Infof("Data Reduction - User defined Last block number %v", blockNumber)
   471  	}
   472  
   473  	ps := rawdb.ReadHeadScanNumber(s.pruneDb)
   474  	var scanNumber uint64
   475  	if ps != nil {
   476  		scanNumber = *ps
   477  	}
   478  
   479  	pp := rawdb.ReadHeadPruneNumber(s.pruneDb)
   480  	var pruneNumber uint64
   481  	if pp != nil {
   482  		pruneNumber = *pp
   483  	}
   484  	log.Infof("Data Reduction - Last scan number %v, prune number %v", scanNumber, pruneNumber)
   485  
   486  	pruneProcessor := datareduction.NewPruneProcessor(s.chainDb, s.pruneDb, s.blockchain, s.config.PruneBlockData)
   487  
   488  	lastScanNumber, lastPruneNumber := pruneProcessor.Process(blockNumber, scanNumber, pruneNumber)
   489  	log.Infof("Data Reduction - After prune, last number scan %v, prune number %v", lastScanNumber, lastPruneNumber)
   490  	if s.config.PruneBlockData {
   491  		for i := uint64(1); i < lastPruneNumber; i++ {
   492  			rawdb.DeleteBody(s.chainDb, rawdb.ReadCanonicalHash(s.chainDb, i), i)
   493  		}
   494  		log.Infof("deleted block from 1 to %v", lastPruneNumber)
   495  	}
   496  	log.Info("Data Reduction - Completed")
   497  
   498  	datareduction.StopPruning()
   499  }