github.com/intfoundation/intchain@v0.0.0-20220727031208-4316ad31ca73/cmd/intchain/chain_mgr.go (about)

     1  package main
     2  
     3  import (
     4  	"github.com/intfoundation/go-crypto"
     5  	dbm "github.com/intfoundation/go-db"
     6  	"github.com/intfoundation/intchain/accounts"
     7  	"github.com/intfoundation/intchain/cmd/utils"
     8  	"github.com/intfoundation/intchain/common"
     9  	"github.com/intfoundation/intchain/consensus"
    10  	"github.com/intfoundation/intchain/consensus/ipbft/epoch"
    11  	"github.com/intfoundation/intchain/consensus/ipbft/types"
    12  	"github.com/intfoundation/intchain/core"
    13  	"github.com/intfoundation/intchain/core/rawdb"
    14  	"github.com/intfoundation/intchain/intclient"
    15  	"github.com/intfoundation/intchain/intprotocol"
    16  	"github.com/intfoundation/intchain/log"
    17  	"github.com/intfoundation/intchain/node"
    18  	"github.com/pkg/errors"
    19  	"gopkg.in/urfave/cli.v1"
    20  	"io/ioutil"
    21  	"net"
    22  	"os"
    23  	"path"
    24  	"strconv"
    25  	"sync"
    26  )
    27  
    28  type ChainManager struct {
    29  	ctx *cli.Context
    30  
    31  	mainChain     *Chain
    32  	mainQuit      <-chan struct{}
    33  	mainStartDone chan struct{}
    34  
    35  	createChildChainLock sync.Mutex
    36  	childChains          map[string]*Chain
    37  	childQuits           map[string]<-chan struct{}
    38  
    39  	stop chan struct{} // Channel wait for INT CHAIN stop
    40  
    41  	server *utils.IntChainP2PServer
    42  	cch    *CrossChainHelper
    43  }
    44  
    45  var chainMgr *ChainManager
    46  var once sync.Once
    47  
    48  func GetCMInstance(ctx *cli.Context) *ChainManager {
    49  
    50  	once.Do(func() {
    51  		chainMgr = &ChainManager{ctx: ctx}
    52  		chainMgr.stop = make(chan struct{})
    53  		chainMgr.childChains = make(map[string]*Chain)
    54  		chainMgr.childQuits = make(map[string]<-chan struct{})
    55  		chainMgr.cch = &CrossChainHelper{}
    56  	})
    57  	return chainMgr
    58  }
    59  
    60  func (cm *ChainManager) GetNodeID() string {
    61  	return cm.server.Server().NodeInfo().ID
    62  }
    63  
    64  func (cm *ChainManager) InitP2P() {
    65  	cm.server = utils.NewP2PServer(cm.ctx)
    66  }
    67  
    68  func (cm *ChainManager) LoadMainChain() error {
    69  	// Load Main Chain
    70  	chainId := MainChain
    71  	if cm.ctx.GlobalBool(utils.TestnetFlag.Name) {
    72  		chainId = TestnetChain
    73  	}
    74  	cm.mainChain = LoadMainChain(cm.ctx, chainId)
    75  	if cm.mainChain == nil {
    76  		return errors.New("Load main chain failed")
    77  	}
    78  
    79  	return nil
    80  }
    81  
    82  func (cm *ChainManager) LoadChains(childIds []string) error {
    83  
    84  	childChainIds := core.GetChildChainIds(cm.cch.chainInfoDB)
    85  	log.Infof("Before load child chains, child chain IDs are %v, len is %d", childChainIds, len(childChainIds))
    86  
    87  	readyToLoadChains := make(map[string]bool) // Key: Child Chain ID, Value: Enable Mining (deprecated)
    88  
    89  	// Check we are belong to the validator of Child Chain in DB first (Mining Mode)
    90  	for _, chainId := range childChainIds {
    91  		// Check Current Validator is Child Chain Validator
    92  		ci := core.GetChainInfo(cm.cch.chainInfoDB, chainId)
    93  		// Check if we are in this child chain
    94  		if ci.Epoch != nil && cm.checkCoinbaseInChildChain(ci.Epoch) {
    95  			readyToLoadChains[chainId] = true
    96  		}
    97  	}
    98  
    99  	// Check request from Child Chain
   100  	for _, requestId := range childIds {
   101  		if requestId == "" {
   102  			// Ignore the Empty ID
   103  			continue
   104  		}
   105  
   106  		if _, present := readyToLoadChains[requestId]; present {
   107  			// Already loaded, ignore
   108  			continue
   109  		} else {
   110  			// Launch in non-mining mode, including both correct and wrong chain id
   111  			// Wrong chain id will be ignore after loading failed
   112  			readyToLoadChains[requestId] = false
   113  		}
   114  	}
   115  
   116  	log.Infof("Number of child chain to be loaded :%v", len(readyToLoadChains))
   117  	log.Infof("Start to load child chain: %v", readyToLoadChains)
   118  
   119  	for chainId := range readyToLoadChains {
   120  		chain := LoadChildChain(cm.ctx, chainId)
   121  		if chain == nil {
   122  			log.Errorf("Load child chain: %s Failed.", chainId)
   123  			continue
   124  		}
   125  
   126  		cm.childChains[chainId] = chain
   127  		log.Infof("Load child chain: %s Success!", chainId)
   128  	}
   129  	return nil
   130  }
   131  
   132  func (cm *ChainManager) InitCrossChainHelper() {
   133  	cm.cch.chainInfoDB = dbm.NewDB("chaininfo", "leveldb",
   134  		cm.ctx.GlobalString(utils.DataDirFlag.Name))
   135  	cm.cch.localTX3CacheDB, _ = rawdb.NewLevelDBDatabase(path.Join(cm.ctx.GlobalString(utils.DataDirFlag.Name), "tx3cache"), 0, 0, "intchain/db/tx3/")
   136  
   137  	chainId := MainChain
   138  	if cm.ctx.GlobalBool(utils.TestnetFlag.Name) {
   139  		chainId = TestnetChain
   140  	}
   141  	cm.cch.mainChainId = chainId
   142  
   143  	if cm.ctx.GlobalBool(utils.RPCEnabledFlag.Name) {
   144  		host := "127.0.0.1" //cm.ctx.GlobalString(utils.RPCListenAddrFlag.Name)
   145  		port := cm.ctx.GlobalInt(utils.RPCPortFlag.Name)
   146  		url := net.JoinHostPort(host, strconv.Itoa(port))
   147  		url = "http://" + url + "/" + chainId
   148  		client, err := intclient.Dial(url)
   149  		if err != nil {
   150  			log.Errorf("can't connect to %s, err: %v, exit", url, err)
   151  			os.Exit(0)
   152  		}
   153  		cm.cch.client = client
   154  	}
   155  }
   156  
   157  func (cm *ChainManager) StartP2PServer() error {
   158  	srv := cm.server.Server()
   159  	// Append Main Chain Protocols
   160  	srv.Protocols = append(srv.Protocols, cm.mainChain.IntNode.GatherProtocols()...)
   161  	// Append Child Chain Protocols
   162  	//for _, chain := range cm.childChains {
   163  	//	srv.Protocols = append(srv.Protocols, chain.EthNode.GatherProtocols()...)
   164  	//}
   165  	// Start the server
   166  	return srv.Start()
   167  }
   168  
   169  func (cm *ChainManager) StartMainChain() error {
   170  	// Start the Main Chain
   171  	cm.mainStartDone = make(chan struct{})
   172  
   173  	cm.mainChain.IntNode.SetP2PServer(cm.server.Server())
   174  
   175  	if address, ok := cm.getNodeValidator(cm.mainChain.IntNode); ok {
   176  		cm.server.AddLocalValidator(cm.mainChain.Id, address)
   177  	}
   178  
   179  	err := StartChain(cm.ctx, cm.mainChain, cm.mainStartDone)
   180  
   181  	// Wait for Main Chain Start Complete
   182  	<-cm.mainStartDone
   183  	cm.mainQuit = cm.mainChain.IntNode.StopChan()
   184  
   185  	return err
   186  }
   187  
   188  func (cm *ChainManager) StartChains() error {
   189  
   190  	for _, chain := range cm.childChains {
   191  		// Start each Chain
   192  		srv := cm.server.Server()
   193  		childProtocols := chain.IntNode.GatherProtocols()
   194  		// Add Child Protocols to P2P Server Protocols
   195  		srv.Protocols = append(srv.Protocols, childProtocols...)
   196  		// Add Child Protocols to P2P Server Caps
   197  		srv.AddChildProtocolCaps(childProtocols)
   198  
   199  		chain.IntNode.SetP2PServer(srv)
   200  
   201  		if address, ok := cm.getNodeValidator(chain.IntNode); ok {
   202  			cm.server.AddLocalValidator(chain.Id, address)
   203  		}
   204  
   205  		startDone := make(chan struct{})
   206  		StartChain(cm.ctx, chain, startDone)
   207  		<-startDone
   208  
   209  		cm.childQuits[chain.Id] = chain.IntNode.StopChan()
   210  
   211  		// Tell other peers that we have added into a new child chain
   212  		cm.server.BroadcastNewChildChainMsg(chain.Id)
   213  	}
   214  
   215  	return nil
   216  }
   217  
   218  func (cm *ChainManager) StartRPC() error {
   219  
   220  	// Start IntChain RPC
   221  	err := utils.StartRPC(cm.ctx)
   222  	if err != nil {
   223  		return err
   224  	} else {
   225  		if utils.IsHTTPRunning() {
   226  			if h, err := cm.mainChain.IntNode.GetHTTPHandler(); err == nil {
   227  				utils.HookupHTTP(cm.mainChain.Id, h)
   228  			} else {
   229  				log.Errorf("Load Main Chain RPC HTTP handler failed: %v", err)
   230  			}
   231  			for _, chain := range cm.childChains {
   232  				if h, err := chain.IntNode.GetHTTPHandler(); err == nil {
   233  					utils.HookupHTTP(chain.Id, h)
   234  				} else {
   235  					log.Errorf("Load Child Chain RPC HTTP handler failed: %v", err)
   236  				}
   237  			}
   238  		}
   239  
   240  		if utils.IsWSRunning() {
   241  			if h, err := cm.mainChain.IntNode.GetWSHandler(); err == nil {
   242  				utils.HookupWS(cm.mainChain.Id, h)
   243  			} else {
   244  				log.Errorf("Load Main Chain RPC WS handler failed: %v", err)
   245  			}
   246  			for _, chain := range cm.childChains {
   247  				if h, err := chain.IntNode.GetWSHandler(); err == nil {
   248  					utils.HookupWS(chain.Id, h)
   249  				} else {
   250  					log.Errorf("Load Child Chain RPC WS handler failed: %v", err)
   251  				}
   252  			}
   253  		}
   254  	}
   255  
   256  	return nil
   257  }
   258  
   259  func (cm *ChainManager) StartInspectEvent() {
   260  
   261  	createChildChainCh := make(chan core.CreateChildChainEvent, 10)
   262  	createChildChainSub := MustGetIntChainFromNode(cm.mainChain.IntNode).BlockChain().SubscribeCreateChildChainEvent(createChildChainCh)
   263  
   264  	go func() {
   265  		defer createChildChainSub.Unsubscribe()
   266  
   267  		for {
   268  			select {
   269  			case event := <-createChildChainCh:
   270  				log.Infof("CreateChildChainEvent received: %v", event)
   271  
   272  				go func() {
   273  					cm.createChildChainLock.Lock()
   274  					defer cm.createChildChainLock.Unlock()
   275  
   276  					cm.LoadChildChainInRT(event.ChainId)
   277  				}()
   278  			case <-createChildChainSub.Err():
   279  				return
   280  			}
   281  		}
   282  	}()
   283  }
   284  
   285  func (cm *ChainManager) LoadChildChainInRT(chainId string) {
   286  
   287  	// Load Child Chain data from pending data
   288  	cci := core.GetPendingChildChainData(cm.cch.chainInfoDB, chainId)
   289  	if cci == nil {
   290  		log.Errorf("child chain: %s does not exist, can't load", chainId)
   291  		return
   292  	}
   293  
   294  	validators := make([]types.GenesisValidator, 0, len(cci.JoinedValidators))
   295  
   296  	validator := false
   297  
   298  	var ethereum *intprotocol.IntChain
   299  	cm.mainChain.IntNode.Service(&ethereum)
   300  
   301  	var localEtherbase common.Address
   302  	if ipbft, ok := ethereum.Engine().(consensus.IPBFT); ok {
   303  		localEtherbase = ipbft.PrivateValidator()
   304  	}
   305  
   306  	for _, v := range cci.JoinedValidators {
   307  		if v.Address == localEtherbase {
   308  			validator = true
   309  		}
   310  
   311  		// dereference the PubKey
   312  		if pubkey, ok := v.PubKey.(*crypto.BLSPubKey); ok {
   313  			v.PubKey = *pubkey
   314  		}
   315  
   316  		// append the Validator
   317  		validators = append(validators, types.GenesisValidator{
   318  			EthAccount: v.Address,
   319  			PubKey:     v.PubKey,
   320  			Amount:     v.DepositAmount,
   321  		})
   322  	}
   323  
   324  	// Write down the genesis into chain info db when exit the routine
   325  	defer writeGenesisIntoChainInfoDB(cm.cch.chainInfoDB, chainId, validators)
   326  
   327  	if !validator {
   328  		log.Warnf("You are not in the validators of child chain %v, no need to start the child chain", chainId)
   329  		// Update Child Chain to formal
   330  		cm.formalizeChildChain(chainId, *cci, nil)
   331  		return
   332  	}
   333  
   334  	// if child chain already loaded, just return (For catch-up case)
   335  	if _, ok := cm.childChains[chainId]; ok {
   336  		log.Infof("Child Chain [%v] has been already loaded.", chainId)
   337  		return
   338  	}
   339  
   340  	// Load the KeyStore file from MainChain (Optional)
   341  	var keyJson []byte
   342  	wallet, walletErr := cm.mainChain.IntNode.AccountManager().Find(accounts.Account{Address: localEtherbase})
   343  	if walletErr == nil {
   344  		var readKeyErr error
   345  		keyJson, readKeyErr = ioutil.ReadFile(wallet.URL().Path)
   346  		if readKeyErr != nil {
   347  			log.Errorf("Failed to Read the KeyStore %v, Error: %v", localEtherbase, readKeyErr)
   348  		}
   349  	}
   350  
   351  	// child chain uses the same validator with the main chain.
   352  	privValidatorFile := cm.mainChain.Config.GetString("priv_validator_file")
   353  	self := types.LoadPrivValidator(privValidatorFile)
   354  
   355  	err := CreateChildChain(cm.ctx, chainId, *self, keyJson, validators)
   356  	if err != nil {
   357  		log.Errorf("Create Child Chain %v failed! %v", chainId, err)
   358  		return
   359  	}
   360  
   361  	chain := LoadChildChain(cm.ctx, chainId)
   362  	if chain == nil {
   363  		log.Errorf("Child Chain %v load failed!", chainId)
   364  		return
   365  	}
   366  
   367  	//StartChildChain to attach intp2p and intrpc
   368  	//TODO Hookup new Created Child Chain to P2P server
   369  	srv := cm.server.Server()
   370  	childProtocols := chain.IntNode.GatherProtocols()
   371  	// Add Child Protocols to P2P Server Protocols
   372  	srv.Protocols = append(srv.Protocols, childProtocols...)
   373  	// Add Child Protocols to P2P Server Caps
   374  	srv.AddChildProtocolCaps(childProtocols)
   375  
   376  	chain.IntNode.SetP2PServer(srv)
   377  
   378  	if address, ok := cm.getNodeValidator(chain.IntNode); ok {
   379  		srv.AddLocalValidator(chain.Id, address)
   380  	}
   381  
   382  	// Start the new Child Chain, and it will start child chain reactors as well
   383  	startDone := make(chan struct{})
   384  	err = StartChain(cm.ctx, chain, startDone)
   385  	<-startDone
   386  	if err != nil {
   387  		return
   388  	}
   389  
   390  	cm.childQuits[chain.Id] = chain.IntNode.StopChan()
   391  
   392  	var childEthereum *intprotocol.IntChain
   393  	chain.IntNode.Service(&childEthereum)
   394  	firstEpoch := childEthereum.Engine().(consensus.IPBFT).GetEpoch()
   395  	// Child Chain start success, then delete the pending data in chain info db
   396  	cm.formalizeChildChain(chainId, *cci, firstEpoch)
   397  
   398  	// Add Child Chain Id into Chain Manager
   399  	cm.childChains[chainId] = chain
   400  
   401  	//TODO Broadcast Child ID to all Main Chain peers
   402  	go cm.server.BroadcastNewChildChainMsg(chainId)
   403  
   404  	//hookup utils
   405  	if utils.IsHTTPRunning() {
   406  		if h, err := chain.IntNode.GetHTTPHandler(); err == nil {
   407  			utils.HookupHTTP(chain.Id, h)
   408  		} else {
   409  			log.Errorf("Unable Hook up Child Chain (%v) RPC HTTP Handler: %v", chainId, err)
   410  		}
   411  	}
   412  	if utils.IsWSRunning() {
   413  		if h, err := chain.IntNode.GetWSHandler(); err == nil {
   414  			utils.HookupWS(chain.Id, h)
   415  		} else {
   416  			log.Errorf("Unable Hook up Child Chain (%v) RPC WS Handler: %v", chainId, err)
   417  		}
   418  	}
   419  
   420  }
   421  
   422  func (cm *ChainManager) formalizeChildChain(chainId string, cci core.CoreChainInfo, ep *epoch.Epoch) {
   423  	// Child Chain start success, then delete the pending data in chain info db
   424  	core.DeletePendingChildChainData(cm.cch.chainInfoDB, chainId)
   425  	// Convert the Chain Info from Pending to Formal
   426  	core.SaveChainInfo(cm.cch.chainInfoDB, &core.ChainInfo{CoreChainInfo: cci, Epoch: ep})
   427  }
   428  
   429  func (cm *ChainManager) checkCoinbaseInChildChain(childEpoch *epoch.Epoch) bool {
   430  	var ethereum *intprotocol.IntChain
   431  	cm.mainChain.IntNode.Service(&ethereum)
   432  
   433  	var localEtherbase common.Address
   434  	if ipbft, ok := ethereum.Engine().(consensus.IPBFT); ok {
   435  		localEtherbase = ipbft.PrivateValidator()
   436  	}
   437  
   438  	return childEpoch.Validators.HasAddress(localEtherbase[:])
   439  }
   440  
   441  func (cm *ChainManager) StopChain() {
   442  	go func() {
   443  		mainChainError := cm.mainChain.IntNode.Close()
   444  		if mainChainError != nil {
   445  			log.Error("Error when closing main chain", "err", mainChainError)
   446  		} else {
   447  			log.Info("Main Chain Closed")
   448  		}
   449  	}()
   450  	for _, child := range cm.childChains {
   451  		go func() {
   452  			childChainError := child.IntNode.Close()
   453  			if childChainError != nil {
   454  				log.Error("Error when closing child chain", "child id", child.Id, "err", childChainError)
   455  			}
   456  		}()
   457  	}
   458  }
   459  
   460  func (cm *ChainManager) WaitChainsStop() {
   461  	<-cm.mainQuit
   462  	for _, quit := range cm.childQuits {
   463  		<-quit
   464  	}
   465  }
   466  
   467  func (cm *ChainManager) Stop() {
   468  	utils.StopRPC()
   469  	cm.server.Stop()
   470  	cm.cch.localTX3CacheDB.Close()
   471  	cm.cch.chainInfoDB.Close()
   472  
   473  	// Release the main routine
   474  	close(cm.stop)
   475  }
   476  
   477  func (cm *ChainManager) Wait() {
   478  	<-cm.stop
   479  }
   480  
   481  func (cm *ChainManager) getNodeValidator(intNode *node.Node) (common.Address, bool) {
   482  
   483  	var intchain *intprotocol.IntChain
   484  	intNode.Service(&intchain)
   485  
   486  	var coinbase common.Address
   487  	tdm := intchain.Engine()
   488  	epoch := tdm.GetEpoch()
   489  	coinbase = tdm.PrivateValidator()
   490  	log.Debugf("getNodeValidator() coinbase is :%x", coinbase)
   491  	return coinbase, epoch.Validators.HasAddress(coinbase[:])
   492  }
   493  
   494  func writeGenesisIntoChainInfoDB(db dbm.DB, childChainId string, validators []types.GenesisValidator) {
   495  	ethByte, _ := generateETHGenesis(childChainId, validators)
   496  	tdmByte, _ := generateTDMGenesis(childChainId, validators)
   497  	core.SaveChainGenesis(db, childChainId, ethByte, tdmByte)
   498  }