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 }