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(ðereum) 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(ðereum) 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 }