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 }