github.com/amazechain/amc@v0.1.3/internal/node/node.go (about)

     1  // Copyright 2022 The AmazeChain Authors
     2  // This file is part of the AmazeChain library.
     3  //
     4  // The AmazeChain 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 AmazeChain 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 AmazeChain library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package node
    18  
    19  import (
    20  	"context"
    21  	"crypto/rand"
    22  	"fmt"
    23  	"github.com/amazechain/amc/common/hexutil"
    24  	"github.com/amazechain/amc/contracts/deposit"
    25  	"github.com/amazechain/amc/contracts/deposit/AMT"
    26  	fujideposit "github.com/amazechain/amc/contracts/deposit/FUJI"
    27  	nftdeposit "github.com/amazechain/amc/contracts/deposit/NFT"
    28  	"github.com/amazechain/amc/internal/debug"
    29  	"github.com/amazechain/amc/internal/metrics/prometheus"
    30  	"github.com/amazechain/amc/internal/p2p"
    31  	amcsync "github.com/amazechain/amc/internal/sync"
    32  	initialsync "github.com/amazechain/amc/internal/sync/initial-sync"
    33  	"github.com/amazechain/amc/internal/tracers"
    34  	"github.com/gofrs/flock"
    35  	"github.com/ledgerwatch/erigon-lib/common/cmp"
    36  	"github.com/pkg/errors"
    37  	"github.com/urfave/cli/v2"
    38  	"hash/crc32"
    39  	"net"
    40  	"path"
    41  	"runtime"
    42  	"strings"
    43  
    44  	"github.com/amazechain/amc/internal"
    45  	"github.com/amazechain/amc/internal/api"
    46  
    47  	"github.com/amazechain/amc/modules"
    48  	"github.com/c2h5oh/datasize"
    49  	"github.com/ledgerwatch/erigon-lib/kv"
    50  	"github.com/ledgerwatch/erigon-lib/kv/mdbx"
    51  	"github.com/ledgerwatch/erigon-lib/kv/memdb"
    52  	log2 "github.com/ledgerwatch/log/v3"
    53  	"golang.org/x/sync/semaphore"
    54  
    55  	"github.com/amazechain/amc/log"
    56  	"os"
    57  	"path/filepath"
    58  	"strconv"
    59  
    60  	"github.com/amazechain/amc/accounts"
    61  	"github.com/amazechain/amc/accounts/keystore"
    62  	"github.com/amazechain/amc/common"
    63  	"github.com/amazechain/amc/common/block"
    64  	"github.com/amazechain/amc/common/transaction"
    65  	"github.com/amazechain/amc/common/types"
    66  	"github.com/amazechain/amc/conf"
    67  	"sync"
    68  
    69  	"github.com/amazechain/amc/internal/consensus"
    70  	"github.com/amazechain/amc/internal/consensus/apoa"
    71  	"github.com/amazechain/amc/internal/consensus/apos"
    72  	"github.com/amazechain/amc/internal/miner"
    73  	"github.com/amazechain/amc/internal/txspool"
    74  	"github.com/amazechain/amc/modules/rawdb"
    75  	"github.com/amazechain/amc/modules/rpc/jsonrpc"
    76  	"github.com/amazechain/amc/params"
    77  	"github.com/amazechain/amc/utils"
    78  	"go.uber.org/zap"
    79  )
    80  
    81  const datadirJWTKey = "jwtsecret" // Path within the datadir to the node's jwt secret
    82  
    83  type Node struct {
    84  	cliCtx       *cli.Context
    85  	ctx          context.Context
    86  	cancel       context.CancelFunc
    87  	config       *conf.Config
    88  	genesisBlock block.IBlock
    89  	etherbase    types.Address
    90  
    91  	lock          sync.RWMutex  // Protects the variadic fields (e.g. gas price and etherbase)
    92  	startStopLock sync.Mutex    // Start/Stop are protected by an additional lock
    93  	state         int           // Tracks state of node lifecycle
    94  	shutDown      chan struct{} // Channel to wait for termination notifications
    95  	dirLock       *flock.Flock  // prevents concurrent use of instance directory
    96  
    97  	// s
    98  	miner           *miner.Miner
    99  	blockChain      common.IBlockChain
   100  	engine          consensus.Engine
   101  	db              kv.RwDB
   102  	txspool         common.ITxsPool
   103  	depositContract *deposit.Deposit
   104  	p2p             p2p.P2P
   105  	sync            *amcsync.Service
   106  	is              *initialsync.Service
   107  	accman          *accounts.Manager
   108  
   109  	api     *api.API
   110  	rpcAPIs []jsonrpc.API
   111  
   112  	http          *httpServer
   113  	ipc           *ipcServer
   114  	ws            *httpServer
   115  	httpAuth      *httpServer //
   116  	wsAuth        *httpServer //
   117  	inprocHandler *jsonrpc.Server
   118  
   119  	keyDir     string // key store directory
   120  	keyDirTemp bool   // If true, key directory will be removed by Stop
   121  
   122  }
   123  
   124  const (
   125  	initializingState = iota
   126  	runningState
   127  	closedState
   128  )
   129  
   130  func NewNode(cliCtx *cli.Context, cfg *conf.Config) (*Node, error) {
   131  
   132  	ctx, cancel := context.WithCancel(cliCtx.Context)
   133  
   134  	var (
   135  		genesisBlock    block.IBlock
   136  		node            Node
   137  		engine          consensus.Engine
   138  		depositContract *deposit.Deposit
   139  		genesisHash     types.Hash
   140  		genesisConfig   *conf.Genesis
   141  		chainConfig     *params.ChainConfig
   142  		chainKv         kv.RwDB
   143  		err             error
   144  	)
   145  
   146  	//
   147  	chainKv, err = OpenDatabase(cfg, nil, kv.ChainDB.String())
   148  	if nil != err {
   149  		return nil, err
   150  	}
   151  
   152  	if err := chainKv.View(ctx, func(tx kv.Tx) error {
   153  		//
   154  		genesisHash, err = rawdb.ReadCanonicalHash(tx, 0)
   155  		//
   156  		if genesisHash == (types.Hash{}) && err != nil {
   157  			//return fmt.Errorf("GenesisHash is missing err:%w", err)
   158  			return internal.ErrGenesisNoConfig
   159  		}
   160  		if genesisHash == (types.Hash{}) && err == nil {
   161  			//needs WriteGenesisBlock
   162  			return nil
   163  		}
   164  		//
   165  		chainConfig, err = rawdb.ReadChainConfig(tx, genesisHash)
   166  		if err != nil {
   167  			return err
   168  		}
   169  		//
   170  		if genesisBlock, err = rawdb.ReadBlockByHash(tx, genesisHash); genesisBlock == nil {
   171  			return fmt.Errorf("genesisBlock is missing err:%w", err)
   172  		}
   173  
   174  		return nil
   175  	}); err != nil {
   176  		return nil, err
   177  	}
   178  
   179  	if genesisHash == (types.Hash{}) {
   180  		genesisHash = *params.GenesisHashByChainName(cfg.NodeCfg.Chain)
   181  		genesisConfig = internal.GenesisByChainName(cfg.NodeCfg.Chain)
   182  		chainConfig = params.ChainConfigByChainName(cfg.NodeCfg.Chain)
   183  		if err := chainKv.Update(ctx, func(tx kv.RwTx) error {
   184  			var genesisErr error
   185  			genesisBlock, genesisErr = WriteGenesisBlock(tx, genesisConfig)
   186  			if nil != genesisErr {
   187  				return genesisErr
   188  			}
   189  			return nil
   190  		}); err != nil {
   191  			return nil, err
   192  		}
   193  	}
   194  
   195  	// update ChainConfig everytime
   196  	if cfg.NodeCfg.Chain != "private" {
   197  		if err := chainKv.Update(ctx, func(tx kv.RwTx) error {
   198  			genesisHash = *params.GenesisHashByChainName(cfg.NodeCfg.Chain)
   199  			genesisConfig = internal.GenesisByChainName(cfg.NodeCfg.Chain)
   200  			if err := WriteChainConfig(tx, genesisHash, genesisConfig); err != nil {
   201  				return err
   202  			}
   203  			return nil
   204  		}); err != nil {
   205  			return nil, err
   206  		}
   207  	}
   208  
   209  	// Acquire the instance directory lock.
   210  	if err := node.openDataDir(cfg); err != nil {
   211  		return nil, err
   212  	}
   213  
   214  	cfg.ChainCfg = chainConfig
   215  
   216  	p2p, err := p2p.NewService(ctx, genesisBlock.Hash(), cfg.P2PCfg, cfg.NodeCfg)
   217  	if err != nil {
   218  		return nil, err
   219  	}
   220  
   221  	switch cfg.ChainCfg.Consensus {
   222  	case params.CliqueConsensus:
   223  		engine = apoa.New(cfg.ChainCfg.Clique, chainKv)
   224  	case params.AposConsensu:
   225  		engine = apos.New(cfg.ChainCfg.Apos, chainKv, cfg.ChainCfg)
   226  	default:
   227  		return nil, fmt.Errorf("invalid engine name %s", cfg.ChainCfg.Consensus)
   228  	}
   229  
   230  	bc, _ := internal.NewBlockChain(ctx, genesisBlock, engine, chainKv, p2p, cfg.ChainCfg)
   231  
   232  	if cfg.ChainCfg.Apos != nil {
   233  		depositContracts := make(map[types.Address]deposit.DepositContract, 0)
   234  		if depositContractAddress := cfg.ChainCfg.Apos.DepositContract; depositContractAddress != "" {
   235  			var addr types.Address
   236  			if !addr.DecodeString(depositContractAddress) {
   237  				panic(fmt.Sprintf("cannot decode DepositContract address: %s", depositContractAddress))
   238  			}
   239  			depositContracts[addr] = new(amtdeposit.Contract)
   240  		}
   241  		if depositNFTContractAddress := cfg.ChainCfg.Apos.DepositNFTContract; depositNFTContractAddress != "" {
   242  			var addr types.Address
   243  			if !addr.DecodeString(depositNFTContractAddress) {
   244  				panic(fmt.Sprintf("cannot decode DepositNFTContract address: %s", depositNFTContractAddress))
   245  			}
   246  			depositContracts[addr] = new(nftdeposit.Contract)
   247  		}
   248  
   249  		if depositFUJIContractAddress := cfg.ChainCfg.Apos.DepositFUJIContract; depositFUJIContractAddress != "" {
   250  			var addr types.Address
   251  			if !addr.DecodeString(depositFUJIContractAddress) {
   252  				panic(fmt.Sprintf("cannot decode DepositFUJIContract address: %s", depositFUJIContractAddress))
   253  			}
   254  			depositContracts[addr] = new(fujideposit.Contract)
   255  		}
   256  		depositContract = deposit.NewDeposit(ctx, bc, chainKv, depositContracts)
   257  	}
   258  
   259  	pool, _ := txspool.NewTxsPool(ctx, bc, depositContract)
   260  
   261  	is := initialsync.NewService(ctx, &initialsync.Config{
   262  		Chain: bc,
   263  		P2P:   p2p,
   264  	})
   265  
   266  	syncServer := amcsync.NewService(
   267  		ctx,
   268  		amcsync.WithP2P(p2p),
   269  		amcsync.WithChainService(bc),
   270  		amcsync.WithInitialSync(is),
   271  	)
   272  
   273  	//todo
   274  	var txs []*transaction.Transaction
   275  	pending := pool.Pending(false)
   276  	for _, batch := range pending {
   277  		txs = append(txs, batch...)
   278  	}
   279  	var bloom *types.Bloom
   280  	if len(txs) > 0 {
   281  		bloom, _ = types.NewBloom(uint64(len(txs)))
   282  		for _, tx := range txs {
   283  			hash := tx.Hash()
   284  			bloom.Add(hash.Bytes())
   285  		}
   286  	}
   287  
   288  	miner := miner.NewMiner(ctx, cfg, bc, engine, pool, nil)
   289  
   290  	keyDir, isEphem, err := getKeyStoreDir(&cfg.NodeCfg)
   291  	if err != nil {
   292  		return nil, err
   293  	}
   294  	// Creates an empty AccountManager with no backends. Callers (e.g. cmd/amc)
   295  	// are required to add the backends later on.
   296  	accman := accounts.NewManager(&accounts.Config{InsecureUnlockAllowed: cfg.NodeCfg.InsecureUnlockAllowed})
   297  
   298  	log.Info("new node", "GenesisHash", genesisBlock.Hash(), "CurrentBlockNr", bc.CurrentBlock().Number64().Uint64())
   299  
   300  	node = Node{
   301  		cliCtx:          cliCtx,
   302  		ctx:             ctx,
   303  		cancel:          cancel,
   304  		config:          cfg,
   305  		miner:           miner,
   306  		genesisBlock:    genesisBlock,
   307  		blockChain:      bc,
   308  		db:              chainKv,
   309  		shutDown:        make(chan struct{}),
   310  		txspool:         pool,
   311  		engine:          engine,
   312  		depositContract: depositContract,
   313  
   314  		inprocHandler: jsonrpc.NewServer(),
   315  		http:          newHTTPServer(),
   316  		ws:            newHTTPServer(),
   317  		wsAuth:        newHTTPServer(),
   318  		httpAuth:      newHTTPServer(),
   319  		ipc:           newIPCServer(&cfg.NodeCfg),
   320  		etherbase:     types.HexToAddress(cfg.Miner.Etherbase),
   321  
   322  		accman:     accman,
   323  		keyDir:     keyDir,
   324  		keyDirTemp: isEphem,
   325  
   326  		p2p:  p2p,
   327  		sync: syncServer,
   328  		is:   is,
   329  	}
   330  
   331  	// Apply flags.
   332  	//SetNodeConfig(ctx, &cfg)
   333  	// Node doesn't by default populate account manager backends
   334  	if err = setAccountManagerBackends(&node, &cfg.NodeCfg); err != nil {
   335  		log.Errorf("Failed to set account manager backends: %v", err)
   336  	}
   337  
   338  	gpoParams := cfg.GPO
   339  	if gpoParams.Default == nil {
   340  		gpoParams.Default = cfg.Miner.GasPrice
   341  	}
   342  
   343  	//
   344  	log.Info("")
   345  	log.Info(strings.Repeat("-", 153))
   346  	for _, line := range strings.Split(cfg.ChainCfg.Description(), "\n") {
   347  		log.Info(line)
   348  	}
   349  	log.Info(strings.Repeat("-", 153))
   350  	log.Info("")
   351  
   352  	node.api = api.NewAPI(bc, chainKv, engine, pool, node.AccountManager(), cfg.ChainCfg)
   353  	node.api.SetGpo(api.NewOracle(bc, miner, cfg.ChainCfg, gpoParams))
   354  	return &node, nil
   355  }
   356  
   357  func (n *Node) Start() error {
   358  	n.startStopLock.Lock()
   359  	defer n.startStopLock.Unlock()
   360  
   361  	n.lock.Lock()
   362  	switch n.state {
   363  	case runningState:
   364  		n.lock.Unlock()
   365  		return ErrNodeRunning
   366  	case closedState:
   367  		n.lock.Unlock()
   368  		return ErrNodeStopped
   369  	}
   370  	n.state = runningState
   371  	n.lock.Unlock()
   372  
   373  	if err := n.blockChain.Start(); err != nil {
   374  		log.Errorf("failed setup blockChain service, err: %v", err)
   375  		return err
   376  	}
   377  
   378  	if n.config.NodeCfg.Miner {
   379  
   380  		// Configure the local mining address
   381  		eb, err := n.Etherbase()
   382  		if err != nil {
   383  			log.Error("Cannot start mining without etherbase", "err", err)
   384  			return fmt.Errorf("etherbase missing: %v", err)
   385  		}
   386  
   387  		if poa, ok := n.engine.(*apoa.Apoa); ok {
   388  			wallet, err := n.accman.Find(accounts.Account{Address: eb})
   389  			if wallet == nil || err != nil {
   390  				log.Error("Etherbase account unavailable locally", "err", err)
   391  				return fmt.Errorf("signer missing: %v", err)
   392  			}
   393  			poa.Authorize(eb, wallet.SignData)
   394  		} else if pos, ok := n.engine.(*apos.APos); ok {
   395  			wallet, err := n.accman.Find(accounts.Account{Address: eb})
   396  			if wallet == nil || err != nil {
   397  				log.Error("Etherbase account unavailable locally", "err", err)
   398  				return fmt.Errorf("signer missing: %v", err)
   399  			}
   400  			pos.Authorize(eb, wallet.SignData)
   401  		}
   402  
   403  		n.miner.SetCoinbase(eb)
   404  		n.miner.Start()
   405  	}
   406  
   407  	if pos, ok := n.engine.(*apos.APos); ok {
   408  		pos.SetBlockChain(n.blockChain)
   409  	}
   410  
   411  	n.rpcAPIs = append(n.rpcAPIs, n.engine.APIs(n.blockChain)...)
   412  	n.rpcAPIs = append(n.rpcAPIs, n.api.Apis()...)
   413  	n.rpcAPIs = append(n.rpcAPIs, tracers.APIs(n.api)...)
   414  	n.rpcAPIs = append(n.rpcAPIs, debug.APIs()...)
   415  
   416  	if err := n.startRPC(); err != nil {
   417  		log.Error("failed start jsonrpc service", zap.Error(err))
   418  		return err
   419  	}
   420  
   421  	//n.p2p.AddConnectionHandler()
   422  	n.p2p.Start()
   423  	n.sync.Start()
   424  
   425  	n.SetupMetrics(n.config.MetricsCfg)
   426  
   427  	if n.depositContract != nil {
   428  		n.depositContract.Start()
   429  	}
   430  
   431  	go n.is.Start()
   432  
   433  	log.Debug("node setup success!")
   434  
   435  	return nil
   436  }
   437  
   438  // getAPIs return two sets of APIs, both the ones that do not require
   439  // authentication, and the complete set
   440  func (n *Node) getAPIs() (unauthenticated, all []jsonrpc.API) {
   441  	for _, api := range n.rpcAPIs {
   442  		if !api.Authenticated {
   443  			unauthenticated = append(unauthenticated, api)
   444  		}
   445  	}
   446  	return unauthenticated, n.rpcAPIs
   447  }
   448  
   449  func (n *Node) startInProc() error {
   450  	for _, api := range n.rpcAPIs {
   451  		if err := n.inprocHandler.RegisterName(api.Namespace, api.Service); err != nil {
   452  			return err
   453  		}
   454  	}
   455  	return nil
   456  }
   457  
   458  func (n *Node) stopInProc() {
   459  	n.inprocHandler.Stop()
   460  }
   461  
   462  func (n *Node) openDataDir(cfg *conf.Config) error {
   463  	if cfg.NodeCfg.DataDir == "" {
   464  		return nil // ephemeral
   465  	}
   466  
   467  	if err := os.MkdirAll(cfg.NodeCfg.DataDir, 0700); err != nil {
   468  		return err
   469  	}
   470  	// Lock the instance directory to prevent concurrent use by another instance as well as
   471  	// accidental use of the instance directory as a database.
   472  	n.dirLock = flock.New(filepath.Join(cfg.NodeCfg.DataDir, "LOCK"))
   473  
   474  	if locked, err := n.dirLock.TryLock(); err != nil {
   475  		return err
   476  	} else if !locked {
   477  		return ErrDatadirUsed
   478  	}
   479  	return nil
   480  }
   481  
   482  func (n *Node) closeDataDir() {
   483  	// Release instance directory lock.
   484  	if n.dirLock != nil && n.dirLock.Locked() {
   485  		n.dirLock.Unlock()
   486  		n.dirLock = nil
   487  	}
   488  }
   489  
   490  // obtainJWTSecret loads the jwt-secret, either from the provided config,
   491  // or from the default location. If neither of those are present, it generates
   492  // a new secret and stores to the default location.
   493  func (n *Node) obtainJWTSecret(cliParam string) ([]byte, error) {
   494  	fileName := cliParam
   495  	if len(fileName) == 0 {
   496  		// no path provided, use default
   497  		fileName = path.Join(n.config.NodeCfg.DataDir, datadirJWTKey)
   498  	}
   499  	// try reading from file
   500  	if data, err := os.ReadFile(fileName); err == nil {
   501  		jwtSecret, err := hexutil.Decode(strings.TrimSpace(string(data)))
   502  		if err != nil {
   503  			return nil, errors.Wrap(err, fmt.Sprintf("failed to decode hex (%s) string", strings.TrimSpace(string(data))))
   504  		}
   505  		if len(jwtSecret) == 32 {
   506  			log.Info("Loaded JWT secret file", "path", fileName, "crc32", fmt.Sprintf("%#x", crc32.ChecksumIEEE(jwtSecret)))
   507  			return jwtSecret, nil
   508  		}
   509  		log.Error("Invalid JWT secret", "path", fileName, "length", len(jwtSecret))
   510  		return nil, errors.New("invalid JWT secret")
   511  	}
   512  	// Need to generate one
   513  	jwtSecret := make([]byte, 32)
   514  	rand.Read(jwtSecret)
   515  
   516  	if err := os.WriteFile(fileName, []byte(hexutil.Encode(jwtSecret)), 0600); err != nil {
   517  		return nil, err
   518  	}
   519  	log.Info("Generated JWT secret", "path", fileName)
   520  	return jwtSecret, nil
   521  }
   522  
   523  func (n *Node) startRPC() error {
   524  
   525  	openAPIs, allAPIs := n.getAPIs()
   526  
   527  	if err := n.startInProc(); err != nil {
   528  		return err
   529  	}
   530  
   531  	if n.ipc.endpoint != "" {
   532  		//if err := n.ipc.start(n.rpcAPIs); err != nil {
   533  		//	return err
   534  		//}
   535  	}
   536  	if n.config.NodeCfg.HTTP {
   537  		//todo []string{"eth", "web3", "debug", "net", "apoa", "txpool", "apos"}
   538  		config := httpConfig{
   539  			CorsAllowedOrigins: utils.SplitAndTrim(n.config.NodeCfg.HTTPCors),
   540  			Vhosts:             []string{"*"},
   541  			Modules:            utils.SplitAndTrim(n.config.NodeCfg.HTTPApi),
   542  			prefix:             "",
   543  		}
   544  		port, _ := strconv.Atoi(n.config.NodeCfg.HTTPPort)
   545  		if err := n.http.setListenAddr(n.config.NodeCfg.HTTPHost, port); err != nil {
   546  			return err
   547  		}
   548  		if err := n.http.enableRPC(n.rpcAPIs, config); err != nil {
   549  			return err
   550  		}
   551  		if err := n.http.start(); err != nil {
   552  			return err
   553  		}
   554  	}
   555  
   556  	// Configure WebSocket.
   557  	if n.config.NodeCfg.WS {
   558  		port, _ := strconv.Atoi(n.config.NodeCfg.WSPort)
   559  		if err := n.ws.setListenAddr(n.config.NodeCfg.WSHost, port); err != nil {
   560  			return err
   561  		}
   562  		//todo
   563  		config := wsConfig{
   564  			Modules:   utils.SplitAndTrim(n.config.NodeCfg.WSApi),
   565  			Origins:   utils.SplitAndTrim(n.config.NodeCfg.WSOrigins),
   566  			prefix:    "",
   567  			jwtSecret: []byte{},
   568  		}
   569  		if err := n.ws.enableWS(n.rpcAPIs, config); err != nil {
   570  			return err
   571  		}
   572  		if err := n.ws.start(); err != nil {
   573  			return err
   574  		}
   575  	}
   576  
   577  	// Configure authenticated API
   578  	if len(openAPIs) != len(allAPIs) && n.config.NodeCfg.AuthRPC {
   579  		jwtSecret, err := n.obtainJWTSecret(n.config.NodeCfg.JWTSecret)
   580  		if err != nil {
   581  			return err
   582  		}
   583  		config := httpConfig{
   584  			CorsAllowedOrigins: utils.SplitAndTrim(n.config.NodeCfg.HTTPCors),
   585  			Vhosts:             []string{"*"},
   586  			Modules:            []string{"admin", "apos"},
   587  			prefix:             "",
   588  			jwtSecret:          jwtSecret,
   589  		}
   590  
   591  		if err := n.httpAuth.setListenAddr(n.config.NodeCfg.AuthAddr, n.config.NodeCfg.AuthPort); err != nil {
   592  			return err
   593  		}
   594  		if err := n.httpAuth.enableRPC(n.rpcAPIs, config); err != nil {
   595  			return err
   596  		}
   597  		if err := n.httpAuth.start(); err != nil {
   598  			return err
   599  		}
   600  	}
   601  
   602  	return nil
   603  }
   604  
   605  func (n *Node) stopRPC() {
   606  	n.http.stop()
   607  	n.ws.stop()
   608  	n.ipc.stop()
   609  	n.stopInProc()
   610  }
   611  
   612  // InstanceDir retrieves the instance directory used by the protocol stack.
   613  func (n *Node) InstanceDir() string {
   614  	return n.config.NodeCfg.DataDir
   615  }
   616  
   617  func (n *Node) Close() error {
   618  	n.startStopLock.Lock()
   619  	defer n.startStopLock.Unlock()
   620  
   621  	n.lock.Lock()
   622  	state := n.state
   623  	n.lock.Unlock()
   624  	switch state {
   625  	case initializingState:
   626  		// The node was never started.
   627  		return n.doClose(nil)
   628  	case runningState:
   629  		// The node was started, release resources acquired by Start().
   630  		var errs []error
   631  		if err := n.stopServices(); err != nil {
   632  			errs = append(errs, err...)
   633  		}
   634  		return n.doClose(errs)
   635  	case closedState:
   636  		return ErrNodeStopped
   637  	default:
   638  		panic(fmt.Sprintf("node is in unknown state %d", state))
   639  	}
   640  }
   641  
   642  // stopServices terminates running services, RPC and p2p networking.
   643  // It is the inverse of Start.
   644  func (n *Node) stopServices() []error {
   645  	var errs []error
   646  	n.stopRPC()
   647  
   648  	n.miner.Close()
   649  
   650  	if err := n.blockChain.Close(); err != nil {
   651  		errs = append(errs, err)
   652  	}
   653  
   654  	if err := n.engine.Close(); err != nil {
   655  		errs = append(errs, err)
   656  	}
   657  
   658  	if err := n.txspool.Stop(); err != nil {
   659  		errs = append(errs, err)
   660  	}
   661  
   662  	if err := n.depositContract.Stop(); err != nil {
   663  		errs = append(errs, err)
   664  	}
   665  
   666  	if err := n.is.Stop(); err != nil {
   667  		errs = append(errs, err)
   668  	}
   669  
   670  	if err := n.p2p.Stop(); err != nil {
   671  		errs = append(errs, err)
   672  	}
   673  
   674  	if err := n.sync.Stop(); err != nil {
   675  		errs = append(errs, err)
   676  	}
   677  
   678  	return errs
   679  }
   680  
   681  // doClose releases resources acquired by New(), collecting errors.
   682  func (n *Node) doClose(errs []error) error {
   683  	// Close databases. This needs the lock because it needs to
   684  	// synchronize with OpenDatabase*.
   685  	n.lock.Lock()
   686  	n.state = closedState
   687  	n.db.Close()
   688  	n.lock.Unlock()
   689  
   690  	if err := n.accman.Close(); err != nil {
   691  		errs = append(errs, err)
   692  	}
   693  	if n.keyDirTemp {
   694  		if err := os.RemoveAll(n.keyDir); err != nil {
   695  			errs = append(errs, err)
   696  		}
   697  	}
   698  
   699  	// Release instance directory lock.
   700  	n.closeDataDir()
   701  
   702  	// Unblock n.Wait.
   703  	close(n.shutDown)
   704  
   705  	// Report any errors that might have occurred.
   706  	switch len(errs) {
   707  	case 0:
   708  		return nil
   709  	case 1:
   710  		return errs[0]
   711  	default:
   712  		return fmt.Errorf("%v", errs)
   713  	}
   714  }
   715  
   716  func (n *Node) Wait() {
   717  	<-n.shutDown
   718  }
   719  
   720  // AccountManager retrieves the account manager used by the protocol stack.
   721  func (n *Node) AccountManager() *accounts.Manager {
   722  	return n.accman
   723  }
   724  
   725  // AccountManager retrieves the account manager used by the protocol stack.
   726  func (n *Node) BlockChain() common.IBlockChain {
   727  	return n.blockChain
   728  }
   729  
   730  func (n *Node) Database() kv.RwDB {
   731  	return n.db
   732  }
   733  
   734  // getKeyStoreDir retrieves the key directory and will create
   735  // and ephemeral one if necessary.
   736  func getKeyStoreDir(conf *conf.NodeConfig) (string, bool, error) {
   737  	keydir, err := conf.KeyDirConfig()
   738  	if err != nil {
   739  		return "", false, err
   740  	}
   741  	isEphemeral := false
   742  	if keydir == "" {
   743  		// There is no datadir.
   744  		keydir, err = os.MkdirTemp("", "go-ethereum-keystore")
   745  		isEphemeral = true
   746  	}
   747  
   748  	if err != nil {
   749  		return "", false, err
   750  	}
   751  	if err := os.MkdirAll(keydir, 0700); err != nil {
   752  		return "", false, err
   753  	}
   754  
   755  	return keydir, isEphemeral, nil
   756  }
   757  
   758  func setAccountManagerBackends(stack *Node, conf *conf.NodeConfig) error {
   759  	am := stack.AccountManager()
   760  	keydir := stack.KeyStoreDir()
   761  	scryptN := keystore.StandardScryptN
   762  	scryptP := keystore.StandardScryptP
   763  	if conf.UseLightweightKDF {
   764  		scryptN = keystore.LightScryptN
   765  		scryptP = keystore.LightScryptP
   766  	}
   767  
   768  	// For now, we're using EITHER external signer OR local signers.
   769  	// If/when we implement some form of lockfile for USB and keystore wallets,
   770  	// we can have both, but it's very confusing for the user to see the same
   771  	// accounts in both externally and locally, plus very racey.
   772  	am.AddBackend(keystore.NewKeyStore(keydir, scryptN, scryptP))
   773  
   774  	return nil
   775  }
   776  
   777  // KeyStoreDir retrieves the key directory
   778  func (n *Node) KeyStoreDir() string {
   779  	return n.keyDir
   780  }
   781  
   782  func (n *Node) SetupMetrics(config conf.MetricsConfig) {
   783  	if config.Enable {
   784  		if config.HTTP != "" {
   785  			address := net.JoinHostPort(config.HTTP, fmt.Sprintf("%d", config.Port))
   786  			log.Info("Enabling stand-alone metrics HTTP endpoint", "address", address)
   787  			prometheus.Setup(address, log.Root())
   788  		} else if config.Port != 0 {
   789  			log.Warn(fmt.Sprintf("--%s specified without --%s, metrics server will not start.", "metrics.port", "metrics.addr"))
   790  		}
   791  	}
   792  
   793  }
   794  
   795  func (s *Node) Etherbase() (eb types.Address, err error) {
   796  	s.lock.RLock()
   797  	etherbase := s.etherbase
   798  	s.lock.RUnlock()
   799  
   800  	if etherbase != (types.Address{}) {
   801  		return etherbase, nil
   802  	}
   803  	if wallets := s.AccountManager().Wallets(); len(wallets) > 0 {
   804  		if accounts := wallets[0].Accounts(); len(accounts) > 0 {
   805  			etherbase := accounts[0].Address
   806  
   807  			s.lock.Lock()
   808  			s.etherbase = etherbase
   809  			s.lock.Unlock()
   810  
   811  			log.Info("Etherbase automatically configured", "address", etherbase)
   812  			return etherbase, nil
   813  		}
   814  	}
   815  	return types.Address{}, fmt.Errorf("etherbase must be explicitly specified")
   816  }
   817  
   818  func OpenDatabase(cfg *conf.Config, logger log2.Logger, name string) (kv.RwDB, error) {
   819  	var chainKv kv.RwDB
   820  	if cfg.NodeCfg.DataDir == "" {
   821  		chainKv = memdb.New("")
   822  	}
   823  	var err error
   824  
   825  	dbPath := filepath.Join(cfg.NodeCfg.DataDir, name)
   826  
   827  	var openFunc func(exclusive bool) (kv.RwDB, error)
   828  	log.Info("Opening Database", "label", name, "path", dbPath)
   829  	openFunc = func(exclusive bool) (kv.RwDB, error) {
   830  		//if config.Http.DBReadConcurrency > 0 {
   831  		//	roTxLimit = int64(config.Http.DBReadConcurrency)
   832  		//}
   833  		roTxsLimiter := semaphore.NewWeighted(int64(cmp.Max(32, runtime.GOMAXPROCS(-1)*8))) // 1 less than max to allow unlocking to happen
   834  		opts := mdbx.NewMDBX(logger).
   835  			WriteMergeThreshold(4 * 8192).
   836  			Path(dbPath).Label(kv.ChainDB).
   837  			DBVerbosity(kv.DBVerbosityLvl(2)).RoTxsLimiter(roTxsLimiter)
   838  		if exclusive {
   839  			opts = opts.Exclusive()
   840  		}
   841  
   842  		modules.AmcInit()
   843  		kv.ChaindataTablesCfg = modules.AmcTableCfg
   844  
   845  		opts = opts.MapSize(8 * datasize.TB)
   846  		return opts.Open()
   847  	}
   848  	chainKv, err = openFunc(false)
   849  	if err != nil {
   850  		return nil, err
   851  	}
   852  
   853  	if err = chainKv.Update(context.Background(), func(tx kv.RwTx) (err error) {
   854  		return params.SetAmcVersion(tx, params.VersionKeyCreated)
   855  	}); err != nil {
   856  		return nil, err
   857  	}
   858  	return chainKv, nil
   859  }
   860  
   861  func WriteGenesisBlock(db kv.RwTx, genesis *conf.Genesis) (*block.Block, error) {
   862  	if genesis == nil {
   863  		return nil, internal.ErrGenesisNoConfig
   864  	}
   865  
   866  	g := &internal.GenesisBlock{
   867  		"",
   868  		genesis,
   869  		//config,
   870  	}
   871  	log.Info("Writing genesis block")
   872  	genBlock, _, err := g.Write(db)
   873  	if nil != err {
   874  		return nil, err
   875  	}
   876  
   877  	return genBlock, nil
   878  
   879  }
   880  
   881  func WriteChainConfig(db kv.RwTx, genesisHash types.Hash, genesis *conf.Genesis) error {
   882  	if err := rawdb.WriteChainConfig(db, genesisHash, genesis.Config); err != nil {
   883  		log.Error("cannot get chain config from db", "err", err)
   884  		return err
   885  	}
   886  	return nil
   887  }
   888  
   889  func SplitTagsFlag(tagsFlag string) map[string]string {
   890  	tags := strings.Split(tagsFlag, ",")
   891  	tagsMap := map[string]string{}
   892  
   893  	for _, t := range tags {
   894  		if t != "" {
   895  			kv := strings.Split(t, "=")
   896  
   897  			if len(kv) == 2 {
   898  				tagsMap[kv[0]] = kv[1]
   899  			}
   900  		}
   901  	}
   902  
   903  	return tagsMap
   904  }
   905  
   906  func (n *Node) Miner() common.IMiner {
   907  	return n.miner
   908  }
   909  
   910  func (n *Node) Engine() consensus.Engine {
   911  	return n.engine
   912  }
   913  
   914  func (n *Node) ChainDb() kv.RwDB {
   915  	return n.db
   916  }