github.com/Gessiux/neatchain@v1.3.1/chain/neatchain/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/Gessiux/go-crypto"
    12  	dbm "github.com/Gessiux/go-db"
    13  	"github.com/Gessiux/neatchain/chain/accounts"
    14  	"github.com/Gessiux/neatchain/chain/consensus"
    15  	"github.com/Gessiux/neatchain/chain/consensus/neatcon/epoch"
    16  	"github.com/Gessiux/neatchain/chain/consensus/neatcon/types"
    17  	"github.com/Gessiux/neatchain/chain/core"
    18  	"github.com/Gessiux/neatchain/chain/core/rawdb"
    19  	"github.com/Gessiux/neatchain/chain/log"
    20  	"github.com/Gessiux/neatchain/neatcli"
    21  	"github.com/Gessiux/neatchain/neatptc"
    22  	"github.com/Gessiux/neatchain/network/node"
    23  	"github.com/Gessiux/neatchain/utilities/common"
    24  	"github.com/Gessiux/neatchain/utilities/utils"
    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{} // Channel wait for NEATChain stop
    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  	// Load Main Chain
    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) // Key: Side Chain ID, Value: Enable Mining (deprecated)
    89  
    90  	// Check we are belong to the validator of Side Chain in DB first (Mining Mode)
    91  	for _, chainId := range sideChainIds {
    92  		// Check Current Validator is Side Chain Validator
    93  		ci := core.GetChainInfo(cm.cch.chainInfoDB, chainId)
    94  		// Check if we are in this side chain
    95  		if ci.Epoch != nil && cm.checkCoinbaseInSideChain(ci.Epoch) {
    96  			readyToLoadChains[chainId] = true
    97  		}
    98  	}
    99  
   100  	// Check request from Side Chain
   101  	for _, requestId := range sideIds {
   102  		if requestId == "" {
   103  			// Ignore the Empty ID
   104  			continue
   105  		}
   106  
   107  		if _, present := readyToLoadChains[requestId]; present {
   108  			// Already loaded, ignore
   109  			continue
   110  		} else {
   111  			// Launch in non-mining mode, including both correct and wrong chain id
   112  			// Wrong chain id will be ignore after loading failed
   113  			readyToLoadChains[requestId] = false
   114  		}
   115  	}
   116  
   117  	//log.Infof("Number of side chain to be loaded :%v", len(readyToLoadChains))
   118  	//log.Infof("Start to load side chain: %v", readyToLoadChains)
   119  
   120  	for chainId := range readyToLoadChains {
   121  		chain := LoadSideChain(cm.ctx, chainId)
   122  		if chain == nil {
   123  			log.Errorf("Load side chain: %s Failed.", chainId)
   124  			continue
   125  		}
   126  
   127  		cm.sideChains[chainId] = chain
   128  		log.Infof("Load side chain: %s Success!", chainId)
   129  	}
   130  	return nil
   131  }
   132  
   133  func (cm *ChainManager) InitCrossChainHelper() {
   134  	cm.cch.chainInfoDB = dbm.NewDB("chaininfo",
   135  		cm.mainChain.Config.GetString("db_backend"),
   136  		cm.ctx.GlobalString(utils.DataDirFlag.Name))
   137  	cm.cch.localTX3CacheDB, _ = rawdb.NewLevelDBDatabase(path.Join(cm.ctx.GlobalString(utils.DataDirFlag.Name), "tx3cache"), 0, 0, "neatchain/db/tx3/")
   138  
   139  	chainId := MainChain
   140  	if cm.ctx.GlobalBool(utils.TestnetFlag.Name) {
   141  		chainId = TestnetChain
   142  	}
   143  	cm.cch.mainChainId = chainId
   144  
   145  	if cm.ctx.GlobalBool(utils.RPCEnabledFlag.Name) {
   146  		host := "127.0.0.1" //cm.ctx.GlobalString(utils.RPCListenAddrFlag.Name)
   147  		port := cm.ctx.GlobalInt(utils.RPCPortFlag.Name)
   148  		url := net.JoinHostPort(host, strconv.Itoa(port))
   149  		url = "http://" + url + "/" + chainId
   150  		client, err := neatcli.Dial(url)
   151  		if err != nil {
   152  			log.Errorf("can't connect to %s, err: %v, exit", url, err)
   153  			os.Exit(0)
   154  		}
   155  		cm.cch.client = client
   156  	}
   157  }
   158  
   159  func (cm *ChainManager) StartP2PServer() error {
   160  	srv := cm.server.Server()
   161  	// Append Main Chain Protocols
   162  	srv.Protocols = append(srv.Protocols, cm.mainChain.NeatNode.GatherProtocols()...)
   163  	// Append Side Chain Protocols
   164  	//for _, chain := range cm.sideChains {
   165  	//	srv.Protocols = append(srv.Protocols, chain.EthNode.GatherProtocols()...)
   166  	//}
   167  	// Start the server
   168  	return srv.Start()
   169  }
   170  
   171  func (cm *ChainManager) StartMainChain() error {
   172  	// Start the Main Chain
   173  	cm.mainStartDone = make(chan struct{})
   174  
   175  	cm.mainChain.NeatNode.SetP2PServer(cm.server.Server())
   176  
   177  	if address, ok := cm.getNodeValidator(cm.mainChain.NeatNode); ok {
   178  		cm.server.AddLocalValidator(cm.mainChain.Id, address)
   179  	}
   180  
   181  	err := StartChain(cm.ctx, cm.mainChain, cm.mainStartDone)
   182  
   183  	// Wait for Main Chain Start Complete
   184  	<-cm.mainStartDone
   185  	cm.mainQuit = cm.mainChain.NeatNode.StopChan()
   186  
   187  	return err
   188  }
   189  
   190  func (cm *ChainManager) StartChains() error {
   191  
   192  	for _, chain := range cm.sideChains {
   193  		// Start each Chain
   194  		srv := cm.server.Server()
   195  		sideProtocols := chain.NeatNode.GatherProtocols()
   196  		// Add Side Protocols to P2P Server Protocols
   197  		srv.Protocols = append(srv.Protocols, sideProtocols...)
   198  		// Add Side Protocols to P2P Server Caps
   199  		srv.AddChildProtocolCaps(sideProtocols)
   200  
   201  		chain.NeatNode.SetP2PServer(srv)
   202  
   203  		if address, ok := cm.getNodeValidator(chain.NeatNode); ok {
   204  			cm.server.AddLocalValidator(chain.Id, address)
   205  		}
   206  
   207  		startDone := make(chan struct{})
   208  		StartChain(cm.ctx, chain, startDone)
   209  		<-startDone
   210  
   211  		cm.sideQuits[chain.Id] = chain.NeatNode.StopChan()
   212  
   213  		// Tell other peers that we have added into a new side chain
   214  		cm.server.BroadcastNewSideChainMsg(chain.Id)
   215  	}
   216  
   217  	return nil
   218  }
   219  
   220  func (cm *ChainManager) StartRPC() error {
   221  
   222  	// Start NeatChain RPC
   223  	err := utils.StartRPC(cm.ctx)
   224  	if err != nil {
   225  		return err
   226  	} else {
   227  		if utils.IsHTTPRunning() {
   228  			if h, err := cm.mainChain.NeatNode.GetHTTPHandler(); err == nil {
   229  				utils.HookupHTTP(cm.mainChain.Id, h)
   230  			} else {
   231  				log.Errorf("Load Main Chain RPC HTTP handler failed: %v", err)
   232  			}
   233  			for _, chain := range cm.sideChains {
   234  				if h, err := chain.NeatNode.GetHTTPHandler(); err == nil {
   235  					utils.HookupHTTP(chain.Id, h)
   236  				} else {
   237  					log.Errorf("Load Side Chain RPC HTTP handler failed: %v", err)
   238  				}
   239  			}
   240  		}
   241  
   242  		if utils.IsWSRunning() {
   243  			if h, err := cm.mainChain.NeatNode.GetWSHandler(); err == nil {
   244  				utils.HookupWS(cm.mainChain.Id, h)
   245  			} else {
   246  				log.Errorf("Load Main Chain RPC WS handler failed: %v", err)
   247  			}
   248  			for _, chain := range cm.sideChains {
   249  				if h, err := chain.NeatNode.GetWSHandler(); err == nil {
   250  					utils.HookupWS(chain.Id, h)
   251  				} else {
   252  					log.Errorf("Load Side Chain RPC WS handler failed: %v", err)
   253  				}
   254  			}
   255  		}
   256  	}
   257  
   258  	return nil
   259  }
   260  
   261  func (cm *ChainManager) StartInspectEvent() {
   262  
   263  	createSideChainCh := make(chan core.CreateSideChainEvent, 10)
   264  	createSideChainSub := MustGetNeatChainFromNode(cm.mainChain.NeatNode).BlockChain().SubscribeCreateSideChainEvent(createSideChainCh)
   265  
   266  	go func() {
   267  		defer createSideChainSub.Unsubscribe()
   268  
   269  		for {
   270  			select {
   271  			case event := <-createSideChainCh:
   272  				log.Infof("CreateSideChainEvent received: %v", event)
   273  
   274  				go func() {
   275  					cm.createSideChainLock.Lock()
   276  					defer cm.createSideChainLock.Unlock()
   277  
   278  					cm.LoadSideChainInRT(event.ChainId)
   279  				}()
   280  			case <-createSideChainSub.Err():
   281  				return
   282  			}
   283  		}
   284  	}()
   285  }
   286  
   287  func (cm *ChainManager) LoadSideChainInRT(chainId string) {
   288  
   289  	// Load Side Chain data from pending data
   290  	cci := core.GetPendingSideChainData(cm.cch.chainInfoDB, chainId)
   291  	if cci == nil {
   292  		log.Errorf("side chain: %s does not exist, can't load", chainId)
   293  		return
   294  	}
   295  
   296  	validators := make([]types.GenesisValidator, 0, len(cci.JoinedValidators))
   297  
   298  	validator := false
   299  
   300  	var neatchain *neatptc.NeatChain
   301  	cm.mainChain.NeatNode.Service(&neatchain)
   302  
   303  	var localEtherbase common.Address
   304  	if neatcon, ok := neatchain.Engine().(consensus.NeatCon); ok {
   305  		localEtherbase = neatcon.PrivateValidator()
   306  	}
   307  
   308  	for _, v := range cci.JoinedValidators {
   309  		if v.Address == localEtherbase {
   310  			validator = true
   311  		}
   312  
   313  		// dereference the PubKey
   314  		if pubkey, ok := v.PubKey.(*crypto.BLSPubKey); ok {
   315  			v.PubKey = *pubkey
   316  		}
   317  
   318  		// append the Validator
   319  		validators = append(validators, types.GenesisValidator{
   320  			EthAccount: v.Address,
   321  			PubKey:     v.PubKey,
   322  			Amount:     v.DepositAmount,
   323  		})
   324  	}
   325  
   326  	// Write down the genesis into chain info db when exit the routine
   327  	defer writeGenesisIntoChainInfoDB(cm.cch.chainInfoDB, chainId, validators)
   328  
   329  	if !validator {
   330  		log.Warnf("You are not in the validators of side chain %v, no need to start the side chain", chainId)
   331  		// Update Side Chain to formal
   332  		cm.formalizeSideChain(chainId, *cci, nil)
   333  		return
   334  	}
   335  
   336  	// if side chain already loaded, just return (For catch-up case)
   337  	if _, ok := cm.sideChains[chainId]; ok {
   338  		log.Infof("Side Chain [%v] has been already loaded.", chainId)
   339  		return
   340  	}
   341  
   342  	// Load the KeyStore file from MainChain (Optional)
   343  	var keyJson []byte
   344  	wallet, walletErr := cm.mainChain.NeatNode.AccountManager().Find(accounts.Account{Address: localEtherbase})
   345  	if walletErr == nil {
   346  		var readKeyErr error
   347  		keyJson, readKeyErr = ioutil.ReadFile(wallet.URL().Path)
   348  		if readKeyErr != nil {
   349  			log.Errorf("Failed to Read the KeyStore %v, Error: %v", localEtherbase, readKeyErr)
   350  		}
   351  	}
   352  
   353  	// side chain uses the same validator with the main chain.
   354  	privValidatorFile := cm.mainChain.Config.GetString("priv_validator_file")
   355  	self := types.LoadPrivValidator(privValidatorFile)
   356  
   357  	err := CreateSideChain(cm.ctx, chainId, *self, keyJson, validators)
   358  	if err != nil {
   359  		log.Errorf("Create Side Chain %v failed! %v", chainId, err)
   360  		return
   361  	}
   362  
   363  	chain := LoadSideChain(cm.ctx, chainId)
   364  	if chain == nil {
   365  		log.Errorf("Side Chain %v load failed!", chainId)
   366  		return
   367  	}
   368  
   369  	//StartSideChain to attach intp2p and intrpc
   370  	//TODO Hookup new Created Side Chain to P2P server
   371  	srv := cm.server.Server()
   372  	sideProtocols := chain.NeatNode.GatherProtocols()
   373  	// Add Side Protocols to P2P Server Protocols
   374  	srv.Protocols = append(srv.Protocols, sideProtocols...)
   375  	// Add Side Protocols to P2P Server Caps
   376  	srv.AddChildProtocolCaps(sideProtocols)
   377  
   378  	chain.NeatNode.SetP2PServer(srv)
   379  
   380  	if address, ok := cm.getNodeValidator(chain.NeatNode); ok {
   381  		srv.AddLocalValidator(chain.Id, address)
   382  	}
   383  
   384  	// Start the new Side Chain, and it will start side chain reactors as well
   385  	startDone := make(chan struct{})
   386  	err = StartChain(cm.ctx, chain, startDone)
   387  	<-startDone
   388  	if err != nil {
   389  		return
   390  	}
   391  
   392  	cm.sideQuits[chain.Id] = chain.NeatNode.StopChan()
   393  
   394  	var sideEthereum *neatptc.NeatChain
   395  	chain.NeatNode.Service(&sideEthereum)
   396  	firstEpoch := sideEthereum.Engine().(consensus.NeatCon).GetEpoch()
   397  	// Side Chain start success, then delete the pending data in chain info db
   398  	cm.formalizeSideChain(chainId, *cci, firstEpoch)
   399  
   400  	// Add Side Chain Id into Chain Manager
   401  	cm.sideChains[chainId] = chain
   402  
   403  	//TODO Broadcast Side ID to all Main Chain peers
   404  	go cm.server.BroadcastNewSideChainMsg(chainId)
   405  
   406  	//hookup utils
   407  	if utils.IsHTTPRunning() {
   408  		if h, err := chain.NeatNode.GetHTTPHandler(); err == nil {
   409  			utils.HookupHTTP(chain.Id, h)
   410  		} else {
   411  			log.Errorf("Unable Hook up Side Chain (%v) RPC HTTP Handler: %v", chainId, err)
   412  		}
   413  	}
   414  	if utils.IsWSRunning() {
   415  		if h, err := chain.NeatNode.GetWSHandler(); err == nil {
   416  			utils.HookupWS(chain.Id, h)
   417  		} else {
   418  			log.Errorf("Unable Hook up Side Chain (%v) RPC WS Handler: %v", chainId, err)
   419  		}
   420  	}
   421  
   422  }
   423  
   424  func (cm *ChainManager) formalizeSideChain(chainId string, cci core.CoreChainInfo, ep *epoch.Epoch) {
   425  	// Side Chain start success, then delete the pending data in chain info db
   426  	core.DeletePendingSideChainData(cm.cch.chainInfoDB, chainId)
   427  	// Convert the Chain Info from Pending to Formal
   428  	core.SaveChainInfo(cm.cch.chainInfoDB, &core.ChainInfo{CoreChainInfo: cci, Epoch: ep})
   429  }
   430  
   431  func (cm *ChainManager) checkCoinbaseInSideChain(sideEpoch *epoch.Epoch) bool {
   432  	var neatchain *neatptc.NeatChain
   433  	cm.mainChain.NeatNode.Service(&neatchain)
   434  
   435  	var localEtherbase common.Address
   436  	if neatcon, ok := neatchain.Engine().(consensus.NeatCon); ok {
   437  		localEtherbase = neatcon.PrivateValidator()
   438  	}
   439  
   440  	return sideEpoch.Validators.HasAddress(localEtherbase[:])
   441  }
   442  
   443  func (cm *ChainManager) StopChain() {
   444  	go func() {
   445  		mainChainError := cm.mainChain.NeatNode.Close()
   446  		if mainChainError != nil {
   447  			log.Error("Error when closing main chain", "err", mainChainError)
   448  		} else {
   449  			log.Info("Main Chain Closed")
   450  		}
   451  	}()
   452  	for _, side := range cm.sideChains {
   453  		go func() {
   454  			sideChainError := side.NeatNode.Close()
   455  			if sideChainError != nil {
   456  				log.Error("Error when closing side chain", "side id", side.Id, "err", sideChainError)
   457  			}
   458  		}()
   459  	}
   460  }
   461  
   462  func (cm *ChainManager) WaitChainsStop() {
   463  	<-cm.mainQuit
   464  	for _, quit := range cm.sideQuits {
   465  		<-quit
   466  	}
   467  }
   468  
   469  func (cm *ChainManager) Stop() {
   470  	utils.StopRPC()
   471  	cm.server.Stop()
   472  	cm.cch.localTX3CacheDB.Close()
   473  	cm.cch.chainInfoDB.Close()
   474  
   475  	// Release the main routine
   476  	close(cm.stop)
   477  }
   478  
   479  func (cm *ChainManager) Wait() {
   480  	<-cm.stop
   481  }
   482  
   483  func (cm *ChainManager) getNodeValidator(neatNode *node.Node) (common.Address, bool) {
   484  
   485  	var neatchain *neatptc.NeatChain
   486  	neatNode.Service(&neatchain)
   487  
   488  	var coinbase common.Address
   489  	ntc := neatchain.Engine()
   490  	epoch := ntc.GetEpoch()
   491  	coinbase = ntc.PrivateValidator()
   492  	log.Debugf("getNodeValidator() coinbase is :%v", coinbase)
   493  	return coinbase, epoch.Validators.HasAddress(coinbase[:])
   494  }
   495  
   496  func writeGenesisIntoChainInfoDB(db dbm.DB, sideChainId string, validators []types.GenesisValidator) {
   497  	ethByte, _ := generateETHGenesis(sideChainId, validators)
   498  	ntcByte, _ := generateNTCGenesis(sideChainId, validators)
   499  	core.SaveChainGenesis(db, sideChainId, ethByte, ntcByte)
   500  }