github.com/bytom/bytom@v1.1.2-0.20221014091027-bbcba3df6075/node/node.go (about)

     1  package node
     2  
     3  import (
     4  	"errors"
     5  	"net"
     6  	"net/http"
     7  	_ "net/http/pprof"
     8  	"path/filepath"
     9  
    10  	log "github.com/sirupsen/logrus"
    11  	cmn "github.com/tendermint/tmlibs/common"
    12  	browser "github.com/toqueteos/webbrowser"
    13  
    14  	"github.com/bytom/bytom/proposal/blockproposer"
    15  	"github.com/prometheus/prometheus/util/flock"
    16  
    17  	"github.com/bytom/bytom/accesstoken"
    18  	"github.com/bytom/bytom/account"
    19  	"github.com/bytom/bytom/api"
    20  	"github.com/bytom/bytom/asset"
    21  	"github.com/bytom/bytom/blockchain/pseudohsm"
    22  	cfg "github.com/bytom/bytom/config"
    23  	"github.com/bytom/bytom/consensus"
    24  	"github.com/bytom/bytom/contract"
    25  	"github.com/bytom/bytom/database"
    26  	dbm "github.com/bytom/bytom/database/leveldb"
    27  	"github.com/bytom/bytom/env"
    28  	"github.com/bytom/bytom/event"
    29  	bytomLog "github.com/bytom/bytom/log"
    30  	"github.com/bytom/bytom/net/websocket"
    31  	"github.com/bytom/bytom/netsync"
    32  	"github.com/bytom/bytom/protocol"
    33  	w "github.com/bytom/bytom/wallet"
    34  )
    35  
    36  const (
    37  	webHost   = "http://127.0.0.1"
    38  	logModule = "node"
    39  )
    40  
    41  // Node represent bytom node
    42  type Node struct {
    43  	cmn.BaseService
    44  
    45  	config          *cfg.Config
    46  	eventDispatcher *event.Dispatcher
    47  	syncManager     *netsync.SyncManager
    48  
    49  	wallet          *w.Wallet
    50  	accessTokens    *accesstoken.CredentialStore
    51  	notificationMgr *websocket.WSNotificationManager
    52  	api             *api.API
    53  	chain           *protocol.Chain
    54  	traceService    *contract.TraceService
    55  	blockProposer   *blockproposer.BlockProposer
    56  	miningEnable    bool
    57  }
    58  
    59  // NewNode create bytom node
    60  func NewNode(config *cfg.Config) *Node {
    61  	if err := initNodeConfig(config); err != nil {
    62  		cmn.Exit(cmn.Fmt("Failed to init config: %v", err))
    63  	}
    64  
    65  	// Get store
    66  	if config.DBBackend != "memdb" && config.DBBackend != "leveldb" {
    67  		cmn.Exit(cmn.Fmt("Param db_backend [%v] is invalid, use leveldb or memdb", config.DBBackend))
    68  	}
    69  	coreDB := dbm.NewDB("core", config.DBBackend, config.DBDir())
    70  	store := database.NewStore(coreDB)
    71  
    72  	tokenDB := dbm.NewDB("accesstoken", config.DBBackend, config.DBDir())
    73  	accessTokens := accesstoken.NewStore(tokenDB)
    74  
    75  	dispatcher := event.NewDispatcher()
    76  	txPool := protocol.NewTxPool(store, dispatcher)
    77  
    78  	chain, err := protocol.NewChain(store, txPool, dispatcher)
    79  	if err != nil {
    80  		cmn.Exit(cmn.Fmt("Failed to create chain structure: %v", err))
    81  	}
    82  
    83  	traceService := startTraceUpdater(chain, config)
    84  
    85  	var accounts *account.Manager
    86  	var assets *asset.Registry
    87  	var wallet *w.Wallet
    88  
    89  	hsm, err := pseudohsm.New(config.KeysDir())
    90  	if err != nil {
    91  		cmn.Exit(cmn.Fmt("initialize HSM failed: %v", err))
    92  	}
    93  
    94  	if !config.Wallet.Disable {
    95  		walletDB := dbm.NewDB("wallet", config.DBBackend, config.DBDir())
    96  		accounts = account.NewManager(walletDB, chain)
    97  		assets = asset.NewRegistry(walletDB, chain)
    98  		contracts := contract.NewRegistry(walletDB)
    99  		wallet, err = w.NewWallet(walletDB, accounts, assets, contracts, hsm, chain, dispatcher, config.Wallet.TxIndex)
   100  		if err != nil {
   101  			log.WithFields(log.Fields{"module": logModule, "error": err}).Error("init NewWallet")
   102  		}
   103  
   104  		// trigger rescan wallet
   105  		if config.Wallet.Rescan {
   106  			wallet.RescanBlocks()
   107  		}
   108  	}
   109  
   110  	fastSyncDB := dbm.NewDB("fastsync", config.DBBackend, config.DBDir())
   111  	syncManager, err := netsync.NewSyncManager(config, chain, txPool, dispatcher, fastSyncDB)
   112  	if err != nil {
   113  		cmn.Exit(cmn.Fmt("Failed to create sync manager: %v", err))
   114  	}
   115  
   116  	notificationMgr := websocket.NewWsNotificationManager(config.Websocket.MaxNumWebsockets, config.Websocket.MaxNumConcurrentReqs, chain, dispatcher)
   117  
   118  	// run the profile server
   119  	profileHost := config.ProfListenAddress
   120  	if profileHost != "" {
   121  		// Profiling bytomd programs.see (https://blog.golang.org/profiling-go-programs)
   122  		// go tool pprof http://profileHose/debug/pprof/heap
   123  		go func() {
   124  			if err = http.ListenAndServe(profileHost, nil); err != nil {
   125  				cmn.Exit(cmn.Fmt("Failed to register tcp profileHost: %v", err))
   126  			}
   127  		}()
   128  	}
   129  
   130  	node := &Node{
   131  		eventDispatcher: dispatcher,
   132  		config:          config,
   133  		syncManager:     syncManager,
   134  		accessTokens:    accessTokens,
   135  		wallet:          wallet,
   136  		chain:           chain,
   137  		traceService:    traceService,
   138  		miningEnable:    config.Mining,
   139  		notificationMgr: notificationMgr,
   140  	}
   141  
   142  	node.BaseService = *cmn.NewBaseService(nil, "Node", node)
   143  	node.blockProposer = blockproposer.NewBlockProposer(chain, accounts, dispatcher)
   144  	return node
   145  }
   146  
   147  func startTraceUpdater(chain *protocol.Chain, cfg *cfg.Config) *contract.TraceService {
   148  	db := dbm.NewDB("trace", cfg.DBBackend, cfg.DBDir())
   149  	store := contract.NewTraceStore(db)
   150  	tracerService := contract.NewTraceService(contract.NewInfrastructure(chain, store))
   151  	traceUpdater := contract.NewTraceUpdater(tracerService, chain)
   152  	go traceUpdater.Sync()
   153  	return tracerService
   154  }
   155  
   156  func initNodeConfig(config *cfg.Config) error {
   157  	if err := lockDataDirectory(config); err != nil {
   158  		cmn.Exit("Error: " + err.Error())
   159  	}
   160  
   161  	if err := bytomLog.InitLogFile(config); err != nil {
   162  		log.WithField("err", err).Fatalln("InitLogFile failed")
   163  	}
   164  
   165  	initActiveNetParams(config)
   166  	initCommonConfig(config)
   167  	return nil
   168  }
   169  
   170  // Lock data directory after daemonization
   171  func lockDataDirectory(config *cfg.Config) error {
   172  	_, _, err := flock.New(filepath.Join(config.RootDir, "LOCK"))
   173  	if err != nil {
   174  		return errors.New("datadir already used by another process")
   175  	}
   176  	return nil
   177  }
   178  
   179  func initActiveNetParams(config *cfg.Config) {
   180  	var exist bool
   181  	consensus.ActiveNetParams, exist = consensus.NetParams[config.ChainID]
   182  	if !exist {
   183  		cmn.Exit(cmn.Fmt("chain_id[%v] don't exist", config.ChainID))
   184  	}
   185  }
   186  
   187  func initCommonConfig(config *cfg.Config) {
   188  	cfg.CommonConfig = config
   189  }
   190  
   191  // Lanch web broser or not
   192  func launchWebBrowser(port string) {
   193  	webAddress := webHost + ":" + port
   194  	log.Info("Launching System Browser with :", webAddress)
   195  	if err := browser.Open(webAddress); err != nil {
   196  		log.Error(err.Error())
   197  		return
   198  	}
   199  }
   200  
   201  func (n *Node) initAndstartAPIServer() {
   202  	n.api = api.NewAPI(n.syncManager, n.wallet, n.blockProposer, n.chain, n.traceService, n.config, n.accessTokens, n.eventDispatcher, n.notificationMgr)
   203  
   204  	listenAddr := env.String("LISTEN", n.config.ApiAddress)
   205  	env.Parse()
   206  	n.api.StartServer(*listenAddr)
   207  }
   208  
   209  func (n *Node) OnStart() error {
   210  	if n.miningEnable {
   211  		if _, err := n.wallet.AccountMgr.GetMiningAddress(); err != nil {
   212  			n.miningEnable = false
   213  			log.Error(err)
   214  		} else {
   215  			n.blockProposer.Start()
   216  		}
   217  	}
   218  	if !n.config.VaultMode {
   219  		if err := n.syncManager.Start(); err != nil {
   220  			return err
   221  		}
   222  	}
   223  
   224  	n.initAndstartAPIServer()
   225  	if err := n.notificationMgr.Start(); err != nil {
   226  		return err
   227  	}
   228  
   229  	if !n.config.Web.Closed {
   230  		_, port, err := net.SplitHostPort(n.config.ApiAddress)
   231  		if err != nil {
   232  			log.Error("Invalid api address")
   233  			return err
   234  		}
   235  		launchWebBrowser(port)
   236  	}
   237  	return nil
   238  }
   239  
   240  func (n *Node) OnStop() {
   241  	n.notificationMgr.Shutdown()
   242  	n.notificationMgr.WaitForShutdown()
   243  	n.BaseService.OnStop()
   244  	if n.miningEnable {
   245  		n.blockProposer.Stop()
   246  	}
   247  	if !n.config.VaultMode {
   248  		n.syncManager.Stop()
   249  	}
   250  	n.eventDispatcher.Stop()
   251  }
   252  
   253  func (n *Node) RunForever() {
   254  	// Sleep forever and then...
   255  	cmn.TrapSignal(func() {
   256  		n.Stop()
   257  	})
   258  }