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

     1  package main
     2  
     3  import (
     4  	"io/ioutil"
     5  	"net"
     6  	"os"
     7  	"path"
     8  	"strconv"
     9  	"sync"
    10  
    11  	"github.com/neatlab/neatio/chain/accounts"
    12  	"github.com/neatlab/neatio/chain/consensus"
    13  	"github.com/neatlab/neatio/chain/consensus/neatcon/epoch"
    14  	"github.com/neatlab/neatio/chain/consensus/neatcon/types"
    15  	"github.com/neatlab/neatio/chain/core"
    16  	"github.com/neatlab/neatio/chain/core/rawdb"
    17  	"github.com/neatlab/neatio/chain/log"
    18  	"github.com/neatlab/neatio/neatcli"
    19  	"github.com/neatlab/neatio/neatptc"
    20  	"github.com/neatlab/neatio/network/node"
    21  	"github.com/neatlab/neatio/utilities/common"
    22  	"github.com/neatlab/neatio/utilities/utils"
    23  	"github.com/neatlib/crypto-go"
    24  	dbm "github.com/neatlib/db-go"
    25  	"github.com/pkg/errors"
    26  	"gopkg.in/urfave/cli.v1"
    27  )
    28  
    29  type ChainManager struct {
    30  	ctx *cli.Context
    31  
    32  	mainChain     *Chain
    33  	mainQuit      <-chan struct{}
    34  	mainStartDone chan struct{}
    35  
    36  	createSideChainLock sync.Mutex
    37  	sideChains          map[string]*Chain
    38  	sideQuits           map[string]<-chan struct{}
    39  
    40  	stop chan struct{}
    41  
    42  	server *utils.NeatChainP2PServer
    43  	cch    *CrossChainHelper
    44  }
    45  
    46  var chainMgr *ChainManager
    47  var once sync.Once
    48  
    49  func GetCMInstance(ctx *cli.Context) *ChainManager {
    50  
    51  	once.Do(func() {
    52  		chainMgr = &ChainManager{ctx: ctx}
    53  		chainMgr.stop = make(chan struct{})
    54  		chainMgr.sideChains = make(map[string]*Chain)
    55  		chainMgr.sideQuits = make(map[string]<-chan struct{})
    56  		chainMgr.cch = &CrossChainHelper{}
    57  	})
    58  	return chainMgr
    59  }
    60  
    61  func (cm *ChainManager) GetNodeID() string {
    62  	return cm.server.Server().NodeInfo().ID
    63  }
    64  
    65  func (cm *ChainManager) InitP2P() {
    66  	cm.server = utils.NewP2PServer(cm.ctx)
    67  }
    68  
    69  func (cm *ChainManager) LoadMainChain() error {
    70  
    71  	chainId := MainChain
    72  	if cm.ctx.GlobalBool(utils.TestnetFlag.Name) {
    73  		chainId = TestnetChain
    74  	}
    75  	cm.mainChain = LoadMainChain(cm.ctx, chainId)
    76  	if cm.mainChain == nil {
    77  		return errors.New("Load main chain failed")
    78  	}
    79  
    80  	return nil
    81  }
    82  
    83  func (cm *ChainManager) LoadChains(sideIds []string) error {
    84  
    85  	sideChainIds := core.GetSideChainIds(cm.cch.chainInfoDB)
    86  	log.Infof("Before load side chains, side chain IDs are %v, len is %d", sideChainIds, len(sideChainIds))
    87  
    88  	readyToLoadChains := make(map[string]bool)
    89  
    90  	for _, chainId := range sideChainIds {
    91  
    92  		ci := core.GetChainInfo(cm.cch.chainInfoDB, chainId)
    93  
    94  		if ci.Epoch != nil && cm.checkCoinbaseInSideChain(ci.Epoch) {
    95  			readyToLoadChains[chainId] = true
    96  		}
    97  	}
    98  
    99  	for _, requestId := range sideIds {
   100  		if requestId == "" {
   101  
   102  			continue
   103  		}
   104  
   105  		if _, present := readyToLoadChains[requestId]; present {
   106  
   107  			continue
   108  		} else {
   109  
   110  			readyToLoadChains[requestId] = false
   111  		}
   112  	}
   113  
   114  	log.Infof("Number of side chain to be loaded :%v", len(readyToLoadChains))
   115  	log.Infof("Start to load side chain: %v", readyToLoadChains)
   116  
   117  	for chainId := range readyToLoadChains {
   118  		chain := LoadSideChain(cm.ctx, chainId)
   119  		if chain == nil {
   120  			log.Errorf("Load side chain: %s Failed.", chainId)
   121  			continue
   122  		}
   123  
   124  		cm.sideChains[chainId] = chain
   125  		log.Infof("Load side chain: %s Success!", chainId)
   126  	}
   127  	return nil
   128  }
   129  
   130  func (cm *ChainManager) InitCrossChainHelper() {
   131  	cm.cch.chainInfoDB = dbm.NewDB("chaininfo", "leveldb",
   132  		cm.ctx.GlobalString(utils.DataDirFlag.Name))
   133  	cm.cch.localTX3CacheDB, _ = rawdb.NewLevelDBDatabase(path.Join(cm.ctx.GlobalString(utils.DataDirFlag.Name), "tx3cache"), 0, 0, "neatio/db/tx3/")
   134  
   135  	chainId := MainChain
   136  	if cm.ctx.GlobalBool(utils.TestnetFlag.Name) {
   137  		chainId = TestnetChain
   138  	}
   139  	cm.cch.mainChainId = chainId
   140  
   141  	if cm.ctx.GlobalBool(utils.RPCEnabledFlag.Name) {
   142  		host := "127.0.0.1"
   143  		port := cm.ctx.GlobalInt(utils.RPCPortFlag.Name)
   144  		url := net.JoinHostPort(host, strconv.Itoa(port))
   145  		url = "http://" + url + "/" + chainId
   146  		client, err := neatcli.Dial(url)
   147  		if err != nil {
   148  			log.Errorf("can't connect to %s, err: %v, exit", url, err)
   149  			os.Exit(0)
   150  		}
   151  		cm.cch.client = client
   152  	}
   153  }
   154  
   155  func (cm *ChainManager) StartP2PServer() error {
   156  	srv := cm.server.Server()
   157  	srv.Protocols = append(srv.Protocols, cm.mainChain.NeatNode.GatherProtocols()...)
   158  	return srv.Start()
   159  }
   160  
   161  func (cm *ChainManager) StartMainChain() error {
   162  	cm.mainStartDone = make(chan struct{})
   163  
   164  	cm.mainChain.NeatNode.SetP2PServer(cm.server.Server())
   165  
   166  	if address, ok := cm.getNodeValidator(cm.mainChain.NeatNode); ok {
   167  		cm.server.AddLocalValidator(cm.mainChain.Id, address)
   168  	}
   169  
   170  	err := StartChain(cm.ctx, cm.mainChain, cm.mainStartDone)
   171  
   172  	<-cm.mainStartDone
   173  	cm.mainQuit = cm.mainChain.NeatNode.StopChan()
   174  
   175  	return err
   176  }
   177  
   178  func (cm *ChainManager) StartChains() error {
   179  
   180  	for _, chain := range cm.sideChains {
   181  		srv := cm.server.Server()
   182  		sideProtocols := chain.NeatNode.GatherProtocols()
   183  		srv.Protocols = append(srv.Protocols, sideProtocols...)
   184  		srv.AddChildProtocolCaps(sideProtocols)
   185  
   186  		chain.NeatNode.SetP2PServer(srv)
   187  
   188  		if address, ok := cm.getNodeValidator(chain.NeatNode); ok {
   189  			cm.server.AddLocalValidator(chain.Id, address)
   190  		}
   191  
   192  		startDone := make(chan struct{})
   193  		StartChain(cm.ctx, chain, startDone)
   194  		<-startDone
   195  
   196  		cm.sideQuits[chain.Id] = chain.NeatNode.StopChan()
   197  
   198  		cm.server.BroadcastNewSideChainMsg(chain.Id)
   199  	}
   200  
   201  	return nil
   202  }
   203  
   204  func (cm *ChainManager) StartRPC() error {
   205  
   206  	err := utils.StartRPC(cm.ctx)
   207  	if err != nil {
   208  		return err
   209  	} else {
   210  		if utils.IsHTTPRunning() {
   211  			if h, err := cm.mainChain.NeatNode.GetHTTPHandler(); err == nil {
   212  				utils.HookupHTTP(cm.mainChain.Id, h)
   213  			} else {
   214  				log.Errorf("Load Main Chain RPC HTTP handler failed: %v", err)
   215  			}
   216  			for _, chain := range cm.sideChains {
   217  				if h, err := chain.NeatNode.GetHTTPHandler(); err == nil {
   218  					utils.HookupHTTP(chain.Id, h)
   219  				} else {
   220  					log.Errorf("Load Side Chain RPC HTTP handler failed: %v", err)
   221  				}
   222  			}
   223  		}
   224  
   225  		if utils.IsWSRunning() {
   226  			if h, err := cm.mainChain.NeatNode.GetWSHandler(); err == nil {
   227  				utils.HookupWS(cm.mainChain.Id, h)
   228  			} else {
   229  				log.Errorf("Load Main Chain RPC WS handler failed: %v", err)
   230  			}
   231  			for _, chain := range cm.sideChains {
   232  				if h, err := chain.NeatNode.GetWSHandler(); err == nil {
   233  					utils.HookupWS(chain.Id, h)
   234  				} else {
   235  					log.Errorf("Load Side Chain RPC WS handler failed: %v", err)
   236  				}
   237  			}
   238  		}
   239  	}
   240  
   241  	return nil
   242  }
   243  
   244  func (cm *ChainManager) StartInspectEvent() {
   245  
   246  	createSideChainCh := make(chan core.CreateSideChainEvent, 10)
   247  	createSideChainSub := MustGetNeatChainFromNode(cm.mainChain.NeatNode).BlockChain().SubscribeCreateSideChainEvent(createSideChainCh)
   248  
   249  	go func() {
   250  		defer createSideChainSub.Unsubscribe()
   251  
   252  		for {
   253  			select {
   254  			case event := <-createSideChainCh:
   255  				log.Infof("CreateSideChainEvent received: %v", event)
   256  
   257  				go func() {
   258  					cm.createSideChainLock.Lock()
   259  					defer cm.createSideChainLock.Unlock()
   260  
   261  					cm.LoadSideChainInRT(event.ChainId)
   262  				}()
   263  			case <-createSideChainSub.Err():
   264  				return
   265  			}
   266  		}
   267  	}()
   268  }
   269  
   270  func (cm *ChainManager) LoadSideChainInRT(chainId string) {
   271  
   272  	cci := core.GetPendingSideChainData(cm.cch.chainInfoDB, chainId)
   273  	if cci == nil {
   274  		log.Errorf("side chain: %s does not exist, can't load", chainId)
   275  		return
   276  	}
   277  
   278  	validators := make([]types.GenesisValidator, 0, len(cci.JoinedValidators))
   279  
   280  	validator := false
   281  
   282  	var neatio *neatptc.NeatIO
   283  	cm.mainChain.NeatNode.Service(&neatio)
   284  
   285  	var localEtherbase common.Address
   286  	if neatcon, ok := neatio.Engine().(consensus.NeatCon); ok {
   287  		localEtherbase = neatcon.PrivateValidator()
   288  	}
   289  
   290  	for _, v := range cci.JoinedValidators {
   291  		if v.Address == localEtherbase {
   292  			validator = true
   293  		}
   294  
   295  		if pubkey, ok := v.PubKey.(*crypto.BLSPubKey); ok {
   296  			v.PubKey = *pubkey
   297  		}
   298  
   299  		validators = append(validators, types.GenesisValidator{
   300  			EthAccount: v.Address,
   301  			PubKey:     v.PubKey,
   302  			Amount:     v.DepositAmount,
   303  		})
   304  	}
   305  
   306  	defer writeGenesisIntoChainInfoDB(cm.cch.chainInfoDB, chainId, validators)
   307  
   308  	if !validator {
   309  		log.Warnf("You are not in the validators of side chain %v, no need to start the side chain", chainId)
   310  		cm.formalizeSideChain(chainId, *cci, nil)
   311  		return
   312  	}
   313  
   314  	if _, ok := cm.sideChains[chainId]; ok {
   315  		log.Infof("Side Chain [%v] has been already loaded.", chainId)
   316  		return
   317  	}
   318  
   319  	var keyJson []byte
   320  	wallet, walletErr := cm.mainChain.NeatNode.AccountManager().Find(accounts.Account{Address: localEtherbase})
   321  	if walletErr == nil {
   322  		var readKeyErr error
   323  		keyJson, readKeyErr = ioutil.ReadFile(wallet.URL().Path)
   324  		if readKeyErr != nil {
   325  			log.Errorf("Failed to Read the KeyStore %v, Error: %v", localEtherbase, readKeyErr)
   326  		}
   327  	}
   328  
   329  	privValidatorFile := cm.mainChain.Config.GetString("priv_validator_file")
   330  	self := types.LoadPrivValidator(privValidatorFile)
   331  
   332  	err := CreateSideChain(cm.ctx, chainId, *self, keyJson, validators)
   333  	if err != nil {
   334  		log.Errorf("Create Side Chain %v failed! %v", chainId, err)
   335  		return
   336  	}
   337  
   338  	chain := LoadSideChain(cm.ctx, chainId)
   339  	if chain == nil {
   340  		log.Errorf("Side Chain %v load failed!", chainId)
   341  		return
   342  	}
   343  
   344  	srv := cm.server.Server()
   345  	sideProtocols := chain.NeatNode.GatherProtocols()
   346  	srv.Protocols = append(srv.Protocols, sideProtocols...)
   347  	srv.AddChildProtocolCaps(sideProtocols)
   348  
   349  	chain.NeatNode.SetP2PServer(srv)
   350  
   351  	if address, ok := cm.getNodeValidator(chain.NeatNode); ok {
   352  		srv.AddLocalValidator(chain.Id, address)
   353  	}
   354  
   355  	startDone := make(chan struct{})
   356  	err = StartChain(cm.ctx, chain, startDone)
   357  	<-startDone
   358  	if err != nil {
   359  		return
   360  	}
   361  
   362  	cm.sideQuits[chain.Id] = chain.NeatNode.StopChan()
   363  
   364  	var sideEthereum *neatptc.NeatIO
   365  	chain.NeatNode.Service(&sideEthereum)
   366  	firstEpoch := sideEthereum.Engine().(consensus.NeatCon).GetEpoch()
   367  	cm.formalizeSideChain(chainId, *cci, firstEpoch)
   368  
   369  	cm.sideChains[chainId] = chain
   370  
   371  	go cm.server.BroadcastNewSideChainMsg(chainId)
   372  
   373  	if utils.IsHTTPRunning() {
   374  		if h, err := chain.NeatNode.GetHTTPHandler(); err == nil {
   375  			utils.HookupHTTP(chain.Id, h)
   376  		} else {
   377  			log.Errorf("Unable Hook up Side Chain (%v) RPC HTTP Handler: %v", chainId, err)
   378  		}
   379  	}
   380  	if utils.IsWSRunning() {
   381  		if h, err := chain.NeatNode.GetWSHandler(); err == nil {
   382  			utils.HookupWS(chain.Id, h)
   383  		} else {
   384  			log.Errorf("Unable Hook up Side Chain (%v) RPC WS Handler: %v", chainId, err)
   385  		}
   386  	}
   387  
   388  }
   389  
   390  func (cm *ChainManager) formalizeSideChain(chainId string, cci core.CoreChainInfo, ep *epoch.Epoch) {
   391  	core.DeletePendingSideChainData(cm.cch.chainInfoDB, chainId)
   392  	core.SaveChainInfo(cm.cch.chainInfoDB, &core.ChainInfo{CoreChainInfo: cci, Epoch: ep})
   393  }
   394  
   395  func (cm *ChainManager) checkCoinbaseInSideChain(sideEpoch *epoch.Epoch) bool {
   396  	var neatio *neatptc.NeatIO
   397  	cm.mainChain.NeatNode.Service(&neatio)
   398  
   399  	var localEtherbase common.Address
   400  	if neatcon, ok := neatio.Engine().(consensus.NeatCon); ok {
   401  		localEtherbase = neatcon.PrivateValidator()
   402  	}
   403  
   404  	return sideEpoch.Validators.HasAddress(localEtherbase[:])
   405  }
   406  
   407  func (cm *ChainManager) StopChain() {
   408  	go func() {
   409  		mainChainError := cm.mainChain.NeatNode.Close()
   410  		if mainChainError != nil {
   411  			log.Error("Error when closing main chain", "err", mainChainError)
   412  		} else {
   413  			log.Info("Main Chain Closed")
   414  		}
   415  	}()
   416  	for _, side := range cm.sideChains {
   417  		go func() {
   418  			sideChainError := side.NeatNode.Close()
   419  			if sideChainError != nil {
   420  				log.Error("Error when closing side chain", "side id", side.Id, "err", sideChainError)
   421  			}
   422  		}()
   423  	}
   424  }
   425  
   426  func (cm *ChainManager) WaitChainsStop() {
   427  	<-cm.mainQuit
   428  	for _, quit := range cm.sideQuits {
   429  		<-quit
   430  	}
   431  }
   432  
   433  func (cm *ChainManager) Stop() {
   434  	utils.StopRPC()
   435  	cm.server.Stop()
   436  	cm.cch.localTX3CacheDB.Close()
   437  	cm.cch.chainInfoDB.Close()
   438  
   439  	close(cm.stop)
   440  }
   441  
   442  func (cm *ChainManager) Wait() {
   443  	<-cm.stop
   444  }
   445  
   446  func (cm *ChainManager) getNodeValidator(neatNode *node.Node) (common.Address, bool) {
   447  
   448  	var neatio *neatptc.NeatIO
   449  	neatNode.Service(&neatio)
   450  
   451  	var coinbase common.Address
   452  	ntc := neatio.Engine()
   453  	epoch := ntc.GetEpoch()
   454  	coinbase = ntc.PrivateValidator()
   455  	log.Debugf("getNodeValidator() coinbase is :%x", coinbase)
   456  	return coinbase, epoch.Validators.HasAddress(coinbase[:])
   457  }
   458  
   459  func writeGenesisIntoChainInfoDB(db dbm.DB, sideChainId string, validators []types.GenesisValidator) {
   460  	ethByte, _ := generateETHGenesis(sideChainId, validators)
   461  	ntcByte, _ := generateNTCGenesis(sideChainId, validators)
   462  	core.SaveChainGenesis(db, sideChainId, ethByte, ntcByte)
   463  }