github.com/neatio-net/neatio@v1.7.3-0.20231114194659-f4d7a2226baa/chain/neatio/cross_chain.go (about) 1 package main 2 3 import ( 4 "bytes" 5 "errors" 6 "fmt" 7 "math/big" 8 "regexp" 9 "strings" 10 "sync" 11 "time" 12 "unicode/utf8" 13 14 "github.com/neatio-net/crypto-go" 15 dbm "github.com/neatio-net/db-go" 16 "github.com/neatio-net/neatio/chain/consensus" 17 "github.com/neatio-net/neatio/chain/consensus/neatcon/epoch" 18 ntcTypes "github.com/neatio-net/neatio/chain/consensus/neatcon/types" 19 "github.com/neatio-net/neatio/chain/core" 20 "github.com/neatio-net/neatio/chain/core/rawdb" 21 "github.com/neatio-net/neatio/chain/core/state" 22 "github.com/neatio-net/neatio/chain/core/types" 23 "github.com/neatio-net/neatio/chain/log" 24 "github.com/neatio-net/neatio/chain/trie" 25 neatAbi "github.com/neatio-net/neatio/neatabi/abi" 26 "github.com/neatio-net/neatio/neatcli" 27 "github.com/neatio-net/neatio/neatdb" 28 "github.com/neatio-net/neatio/neatptc" 29 "github.com/neatio-net/neatio/network/node" 30 "github.com/neatio-net/neatio/params" 31 "github.com/neatio-net/neatio/utilities/common" 32 "github.com/neatio-net/neatio/utilities/common/math" 33 "github.com/neatio-net/neatio/utilities/rlp" 34 ) 35 36 type CrossChainHelper struct { 37 mtx sync.Mutex 38 chainInfoDB dbm.DB 39 localTX3CacheDB neatdb.Database 40 41 client *neatcli.Client 42 mainChainId string 43 } 44 45 func (cch *CrossChainHelper) GetMutex() *sync.Mutex { 46 return &cch.mtx 47 } 48 49 func (cch *CrossChainHelper) GetChainInfoDB() dbm.DB { 50 return cch.chainInfoDB 51 } 52 53 func (cch *CrossChainHelper) GetClient() *neatcli.Client { 54 return cch.client 55 } 56 57 func (cch *CrossChainHelper) GetMainChainId() string { 58 return cch.mainChainId 59 } 60 61 func (cch *CrossChainHelper) CanCreateSideChain(from common.Address, chainId string, minValidators uint16, minDepositAmount, startupCost *big.Int, startBlock, endBlock *big.Int) error { 62 63 if chainId == "" || strings.Contains(chainId, ";") { 64 return errors.New("chainId is nil or empty, or contains ';', should be meaningful") 65 } 66 67 pass, _ := regexp.MatchString("^[a-z]+[a-z0-9_]*$", chainId) 68 if !pass { 69 return errors.New("chainId must be start with letter (a-z) and contains alphanumeric(lower case) or underscore, try use other name instead") 70 } 71 72 if utf8.RuneCountInString(chainId) > 30 { 73 return errors.New("max characters of chain id is 30, try use other name instead") 74 } 75 76 if chainId == MainChain || chainId == TestnetChain { 77 return errors.New("you can't create NeatIO as a side chain, try use other name instead") 78 } 79 80 ci := core.GetChainInfo(cch.chainInfoDB, chainId) 81 if ci != nil { 82 return fmt.Errorf("Chain %s has already exist, try use other name instead", chainId) 83 } 84 85 cci := core.GetPendingSideChainData(cch.chainInfoDB, chainId) 86 if cci != nil { 87 return fmt.Errorf("Chain %s has already applied, try use other name instead", chainId) 88 } 89 90 if minValidators < core.OFFICIAL_MINIMUM_VALIDATORS { 91 return fmt.Errorf("Validators count is not meet the minimum official validator count (%v)", core.OFFICIAL_MINIMUM_VALIDATORS) 92 } 93 94 officialMinimumDeposit := math.MustParseBig256(core.OFFICIAL_MINIMUM_DEPOSIT) 95 if minDepositAmount.Cmp(officialMinimumDeposit) == -1 { 96 return fmt.Errorf("Deposit amount is not meet the minimum official deposit amount (%v NIO)", new(big.Int).Div(officialMinimumDeposit, big.NewInt(params.Nio))) 97 } 98 99 if startupCost.Cmp(officialMinimumDeposit) != 0 { 100 return fmt.Errorf("Startup cost is not meet the required amount (%v NIO)", new(big.Int).Div(officialMinimumDeposit, big.NewInt(params.Nio))) 101 } 102 103 if startBlock.Cmp(endBlock) >= 0 { 104 return errors.New("start block number must be less than end block number") 105 } 106 107 neatio := MustGetNeatChainFromNode(chainMgr.mainChain.NeatNode) 108 currentBlock := neatio.BlockChain().CurrentBlock() 109 if endBlock.Cmp(currentBlock.Number()) <= 0 { 110 return errors.New("end block number has already passed") 111 } 112 113 return nil 114 } 115 116 func (cch *CrossChainHelper) CreateSideChain(from common.Address, chainId string, minValidators uint16, minDepositAmount *big.Int, startBlock, endBlock *big.Int) error { 117 log.Debug("CreateSideChain - start") 118 119 cci := &core.CoreChainInfo{ 120 Owner: from, 121 ChainId: chainId, 122 MinValidators: minValidators, 123 MinDepositAmount: minDepositAmount, 124 StartBlock: startBlock, 125 EndBlock: endBlock, 126 JoinedValidators: make([]core.JoinedValidator, 0), 127 } 128 core.CreatePendingSideChainData(cch.chainInfoDB, cci) 129 130 log.Debug("CreateSideChain - end") 131 return nil 132 } 133 134 func (cch *CrossChainHelper) ValidateJoinSideChain(from common.Address, consensusPubkey []byte, chainId string, depositAmount *big.Int, signature []byte) error { 135 log.Debug("ValidateJoinSideChain - start") 136 137 if chainId == MainChain || chainId == TestnetChain { 138 return errors.New("you can't join NeatIO as a side chain, try use other name instead") 139 } 140 141 if err := crypto.CheckConsensusPubKey(from, consensusPubkey, signature); err != nil { 142 return err 143 } 144 145 ci := core.GetPendingSideChainData(cch.chainInfoDB, chainId) 146 if ci == nil { 147 if core.GetChainInfo(cch.chainInfoDB, chainId) != nil { 148 return fmt.Errorf("chain %s has already created/started, try use other name instead", chainId) 149 } else { 150 return fmt.Errorf("side chain %s not exist, try use other name instead", chainId) 151 } 152 } 153 154 find := false 155 for _, joined := range ci.JoinedValidators { 156 if from == joined.Address { 157 find = true 158 break 159 } 160 } 161 162 if find { 163 return errors.New(fmt.Sprintf("You have already joined the Side Chain %s", chainId)) 164 } 165 166 if !(depositAmount != nil && depositAmount.Sign() == 1) { 167 return errors.New("deposit amount must be greater than 0") 168 } 169 170 log.Debug("ValidateJoinSideChain - end") 171 return nil 172 } 173 174 func (cch *CrossChainHelper) JoinSideChain(from common.Address, pubkey crypto.PubKey, chainId string, depositAmount *big.Int) error { 175 log.Debug("JoinSideChain - start") 176 177 ci := core.GetPendingSideChainData(cch.chainInfoDB, chainId) 178 if ci == nil { 179 log.Errorf("JoinSideChain - Side Chain %s not exist, you can't join the chain", chainId) 180 return fmt.Errorf("Side Chain %s not exist, you can't join the chain", chainId) 181 } 182 183 for _, joined := range ci.JoinedValidators { 184 if from == joined.Address { 185 return nil 186 } 187 } 188 189 jv := core.JoinedValidator{ 190 PubKey: pubkey, 191 Address: from, 192 DepositAmount: depositAmount, 193 } 194 195 ci.JoinedValidators = append(ci.JoinedValidators, jv) 196 197 core.UpdatePendingSideChainData(cch.chainInfoDB, ci) 198 199 log.Debug("JoinSideChain - end") 200 return nil 201 } 202 203 func (cch *CrossChainHelper) ReadyForLaunchSideChain(height *big.Int, stateDB *state.StateDB) ([]string, []byte, []string) { 204 205 readyId, updateBytes, removedId := core.GetSideChainForLaunch(cch.chainInfoDB, height, stateDB) 206 if len(readyId) == 0 { 207 208 } else { 209 210 } 211 212 return readyId, updateBytes, removedId 213 } 214 215 func (cch *CrossChainHelper) ProcessPostPendingData(newPendingIdxBytes []byte, deleteSideChainIds []string) { 216 core.ProcessPostPendingData(cch.chainInfoDB, newPendingIdxBytes, deleteSideChainIds) 217 } 218 219 func (cch *CrossChainHelper) VoteNextEpoch(ep *epoch.Epoch, from common.Address, voteHash common.Hash, txHash common.Hash) error { 220 221 voteSet := ep.GetNextEpoch().GetEpochValidatorVoteSet() 222 if voteSet == nil { 223 voteSet = epoch.NewEpochValidatorVoteSet() 224 } 225 226 vote, exist := voteSet.GetVoteByAddress(from) 227 228 if exist { 229 230 vote.VoteHash = voteHash 231 vote.TxHash = txHash 232 } else { 233 234 vote = &epoch.EpochValidatorVote{ 235 Address: from, 236 VoteHash: voteHash, 237 TxHash: txHash, 238 } 239 voteSet.StoreVote(vote) 240 } 241 242 epoch.SaveEpochVoteSet(ep.GetDB(), ep.GetNextEpoch().Number, voteSet) 243 return nil 244 } 245 246 func (cch *CrossChainHelper) RevealVote(ep *epoch.Epoch, from common.Address, pubkey crypto.PubKey, depositAmount *big.Int, salt string, txHash common.Hash) error { 247 248 voteSet := ep.GetNextEpoch().GetEpochValidatorVoteSet() 249 vote, exist := voteSet.GetVoteByAddress(from) 250 251 if exist { 252 253 vote.PubKey = pubkey 254 vote.Amount = depositAmount 255 vote.Salt = salt 256 vote.TxHash = txHash 257 } 258 259 epoch.SaveEpochVoteSet(ep.GetDB(), ep.GetNextEpoch().Number, voteSet) 260 return nil 261 } 262 263 func (cch *CrossChainHelper) UpdateNextEpoch(ep *epoch.Epoch, from common.Address, pubkey crypto.PubKey, depositAmount *big.Int, salt string, txHash common.Hash) error { 264 voteSet := ep.GetNextEpoch().GetEpochValidatorVoteSet() 265 if voteSet == nil { 266 voteSet = epoch.NewEpochValidatorVoteSet() 267 } 268 269 vote, exist := voteSet.GetVoteByAddress(from) 270 271 if exist { 272 vote.Amount = depositAmount 273 vote.TxHash = txHash 274 } else { 275 vote = &epoch.EpochValidatorVote{ 276 Address: from, 277 PubKey: pubkey, 278 Amount: depositAmount, 279 Salt: "neatio", 280 TxHash: txHash, 281 } 282 283 voteSet.StoreVote(vote) 284 } 285 286 epoch.SaveEpochVoteSet(ep.GetDB(), ep.GetNextEpoch().Number, voteSet) 287 return nil 288 } 289 290 func (cch *CrossChainHelper) GetHeightFromMainChain() *big.Int { 291 neatio := MustGetNeatChainFromNode(chainMgr.mainChain.NeatNode) 292 return neatio.BlockChain().CurrentBlock().Number() 293 } 294 295 func (cch *CrossChainHelper) GetTxFromMainChain(txHash common.Hash) *types.Transaction { 296 neatio := MustGetNeatChainFromNode(chainMgr.mainChain.NeatNode) 297 chainDb := neatio.ChainDb() 298 299 tx, _, _, _ := rawdb.ReadTransaction(chainDb, txHash) 300 return tx 301 } 302 303 func (cch *CrossChainHelper) GetEpochFromMainChain() (string, *epoch.Epoch) { 304 neatio := MustGetNeatChainFromNode(chainMgr.mainChain.NeatNode) 305 var ep *epoch.Epoch 306 if neatcon, ok := neatio.Engine().(consensus.NeatCon); ok { 307 ep = neatcon.GetEpoch() 308 } 309 return neatio.ChainConfig().NeatChainId, ep 310 } 311 312 func (cch *CrossChainHelper) ChangeValidators(chainId string) { 313 314 if chainMgr == nil { 315 return 316 } 317 318 var chain *Chain = nil 319 if chainId == MainChain || chainId == TestnetChain { 320 chain = chainMgr.mainChain 321 } else if chn, ok := chainMgr.sideChains[chainId]; ok { 322 chain = chn 323 } 324 325 if chain == nil || chain.NeatNode == nil { 326 return 327 } 328 329 if address, ok := chainMgr.getNodeValidator(chain.NeatNode); ok { 330 chainMgr.server.AddLocalValidator(chainId, address) 331 } 332 } 333 334 func (cch *CrossChainHelper) VerifySideChainProofData(bs []byte) error { 335 336 log.Debug("VerifySideChainProofData - start") 337 338 var proofData types.SideChainProofData 339 err := rlp.DecodeBytes(bs, &proofData) 340 if err != nil { 341 return err 342 } 343 344 header := proofData.Header 345 346 if header.Time.Cmp(big.NewInt(time.Now().Unix())) > 0 { 347 348 } 349 350 ncExtra, err := ntcTypes.ExtractNeatConExtra(header) 351 if err != nil { 352 return err 353 } 354 355 chainId := ncExtra.ChainID 356 if chainId == "" || chainId == MainChain || chainId == TestnetChain { 357 return fmt.Errorf("invalid side chain id: %s", chainId) 358 } 359 360 if header.Nonce != (types.NeatConEmptyNonce) && !bytes.Equal(header.Nonce[:], types.NeatConNonce) { 361 return errors.New("invalid nonce") 362 } 363 364 if header.MixDigest != types.NeatConDigest { 365 return errors.New("invalid mix digest") 366 } 367 368 if header.UncleHash != types.NeatConNilUncleHash { 369 return errors.New("invalid uncle Hash") 370 } 371 372 if header.Difficulty == nil || header.Difficulty.Cmp(types.NeatConDefaultDifficulty) != 0 { 373 return errors.New("invalid difficulty") 374 } 375 376 if ncExtra.EpochBytes != nil && len(ncExtra.EpochBytes) != 0 { 377 ep := epoch.FromBytes(ncExtra.EpochBytes) 378 if ep != nil && ep.Number == 0 { 379 return nil 380 } 381 } 382 383 if chainId != "side_0" { 384 ci := core.GetChainInfo(cch.chainInfoDB, chainId) 385 if ci == nil { 386 return fmt.Errorf("chain info %s not found", chainId) 387 } 388 epoch := ci.GetEpochByBlockNumber(ncExtra.Height) 389 if epoch == nil { 390 return fmt.Errorf("could not get epoch for block height %v", ncExtra.Height) 391 } 392 393 if epoch.Number > ci.EpochNumber { 394 ci.EpochNumber = epoch.Number 395 ci.Epoch = epoch 396 core.SaveChainInfo(cch.chainInfoDB, ci) 397 } 398 399 valSet := epoch.Validators 400 if !bytes.Equal(valSet.Hash(), ncExtra.ValidatorsHash) { 401 return errors.New("inconsistent validator set") 402 } 403 404 seenCommit := ncExtra.SeenCommit 405 if !bytes.Equal(ncExtra.SeenCommitHash, seenCommit.Hash()) { 406 return errors.New("invalid committed seals") 407 } 408 409 if err = valSet.VerifyCommit(ncExtra.ChainID, ncExtra.Height, seenCommit); err != nil { 410 return err 411 } 412 } 413 414 log.Debug("VerifySideChainProofData - end") 415 return nil 416 } 417 418 func (cch *CrossChainHelper) SaveSideChainProofDataToMainChain(bs []byte) error { 419 log.Debug("SaveSideChainProofDataToMainChain - start") 420 421 var proofData types.SideChainProofData 422 err := rlp.DecodeBytes(bs, &proofData) 423 if err != nil { 424 return err 425 } 426 427 header := proofData.Header 428 ncExtra, err := ntcTypes.ExtractNeatConExtra(header) 429 if err != nil { 430 return err 431 } 432 433 chainId := ncExtra.ChainID 434 if chainId == "" || chainId == MainChain || chainId == TestnetChain { 435 return fmt.Errorf("invalid side chain id: %s", chainId) 436 } 437 438 if len(ncExtra.EpochBytes) != 0 { 439 ep := epoch.FromBytes(ncExtra.EpochBytes) 440 if ep != nil { 441 ci := core.GetChainInfo(cch.chainInfoDB, ncExtra.ChainID) 442 443 if ci == nil { 444 for { 445 446 time.Sleep(3 * time.Second) 447 ci = core.GetChainInfo(cch.chainInfoDB, ncExtra.ChainID) 448 if ci != nil { 449 break 450 } 451 } 452 } 453 454 futureEpoch := ep.Number > ci.EpochNumber && ncExtra.Height < ep.StartBlock 455 if futureEpoch { 456 457 core.SaveFutureEpoch(cch.chainInfoDB, ep, chainId) 458 log.Infof("Future epoch saved from chain: %s, epoch: %v", chainId, ep) 459 } else if ep.Number == 0 || ep.Number >= ci.EpochNumber { 460 461 ci.EpochNumber = ep.Number 462 ci.Epoch = ep 463 core.SaveChainInfo(cch.chainInfoDB, ci) 464 log.Infof("Epoch saved from chain: %s, epoch: %v", chainId, ep) 465 } 466 } 467 } 468 469 log.Debug("SaveSideChainProofDataToMainChain - end") 470 return nil 471 } 472 473 func (cch *CrossChainHelper) ValidateTX3ProofData(proofData *types.TX3ProofData) error { 474 log.Debug("ValidateTX3ProofData - start") 475 476 header := proofData.Header 477 478 if header.Time.Cmp(big.NewInt(time.Now().Unix())) > 0 { 479 480 } 481 482 ncExtra, err := ntcTypes.ExtractNeatConExtra(header) 483 if err != nil { 484 return err 485 } 486 487 chainId := ncExtra.ChainID 488 if chainId == "" || chainId == MainChain || chainId == TestnetChain { 489 return fmt.Errorf("invalid side chain id: %s", chainId) 490 } 491 492 if header.Nonce != (types.NeatConEmptyNonce) && !bytes.Equal(header.Nonce[:], types.NeatConNonce) { 493 return errors.New("invalid nonce") 494 } 495 496 if header.MixDigest != types.NeatConDigest { 497 return errors.New("invalid mix digest") 498 } 499 500 if header.UncleHash != types.NeatConNilUncleHash { 501 return errors.New("invalid uncle Hash") 502 } 503 504 if header.Difficulty == nil || header.Difficulty.Cmp(types.NeatConDefaultDifficulty) != 0 { 505 return errors.New("invalid difficulty") 506 } 507 508 if ncExtra.EpochBytes != nil && len(ncExtra.EpochBytes) != 0 { 509 ep := epoch.FromBytes(ncExtra.EpochBytes) 510 if ep != nil && ep.Number == 0 { 511 return nil 512 } 513 } 514 515 ci := core.GetChainInfo(cch.chainInfoDB, chainId) 516 if ci == nil { 517 return fmt.Errorf("chain info %s not found", chainId) 518 } 519 epoch := ci.GetEpochByBlockNumber(ncExtra.Height) 520 if epoch == nil { 521 return fmt.Errorf("could not get epoch for block height %v", ncExtra.Height) 522 } 523 524 if epoch.Number > ci.EpochNumber { 525 ci.EpochNumber = epoch.Number 526 ci.Epoch = epoch 527 core.SaveChainInfo(cch.chainInfoDB, ci) 528 } 529 530 valSet := epoch.Validators 531 if !bytes.Equal(valSet.Hash(), ncExtra.ValidatorsHash) { 532 return errors.New("inconsistent validator set") 533 } 534 535 seenCommit := ncExtra.SeenCommit 536 if !bytes.Equal(ncExtra.SeenCommitHash, seenCommit.Hash()) { 537 return errors.New("invalid committed seals") 538 } 539 540 if err = valSet.VerifyCommit(ncExtra.ChainID, ncExtra.Height, seenCommit); err != nil { 541 return err 542 } 543 544 keybuf := new(bytes.Buffer) 545 for i, txIndex := range proofData.TxIndexs { 546 keybuf.Reset() 547 rlp.Encode(keybuf, uint(txIndex)) 548 _, _, err := trie.VerifyProof(header.TxHash, keybuf.Bytes(), proofData.TxProofs[i]) 549 if err != nil { 550 return err 551 } 552 } 553 554 log.Debug("ValidateTX3ProofData - end") 555 return nil 556 } 557 558 func (cch *CrossChainHelper) ValidateTX4WithInMemTX3ProofData(tx4 *types.Transaction, tx3ProofData *types.TX3ProofData) error { 559 560 signer := types.NewEIP155Signer(tx4.ChainId()) 561 from, err := types.Sender(signer, tx4) 562 if err != nil { 563 return core.ErrInvalidSender 564 } 565 566 var args neatAbi.WithdrawFromMainChainArgs 567 568 if !neatAbi.IsNeatChainContractAddr(tx4.To()) { 569 return errors.New("invalid TX4: wrong To()") 570 } 571 572 data := tx4.Data() 573 function, err := neatAbi.FunctionTypeFromId(data[:4]) 574 if err != nil { 575 return err 576 } 577 578 if function != neatAbi.WithdrawFromMainChain { 579 return errors.New("invalid TX4: wrong function") 580 } 581 582 if err := neatAbi.ChainABI.UnpackMethodInputs(&args, neatAbi.WithdrawFromMainChain.String(), data[4:]); err != nil { 583 return err 584 } 585 586 header := tx3ProofData.Header 587 if err != nil { 588 return err 589 } 590 keybuf := new(bytes.Buffer) 591 rlp.Encode(keybuf, tx3ProofData.TxIndexs[0]) 592 val, _, err := trie.VerifyProof(header.TxHash, keybuf.Bytes(), tx3ProofData.TxProofs[0]) 593 if err != nil { 594 return err 595 } 596 597 var tx3 types.Transaction 598 err = rlp.DecodeBytes(val, &tx3) 599 if err != nil { 600 return err 601 } 602 603 signer2 := types.NewEIP155Signer(tx3.ChainId()) 604 tx3From, err := types.Sender(signer2, &tx3) 605 if err != nil { 606 return core.ErrInvalidSender 607 } 608 609 var tx3Args neatAbi.WithdrawFromSideChainArgs 610 tx3Data := tx3.Data() 611 if err := neatAbi.ChainABI.UnpackMethodInputs(&tx3Args, neatAbi.WithdrawFromSideChain.String(), tx3Data[4:]); err != nil { 612 return err 613 } 614 615 if from != tx3From || args.ChainId != tx3Args.ChainId || args.Amount.Cmp(tx3.Value()) != 0 { 616 return errors.New("params are not consistent with tx in side chain") 617 } 618 619 return nil 620 } 621 622 func (cch *CrossChainHelper) GetTX3(chainId string, txHash common.Hash) *types.Transaction { 623 return rawdb.GetTX3(cch.localTX3CacheDB, chainId, txHash) 624 } 625 626 func (cch *CrossChainHelper) DeleteTX3(chainId string, txHash common.Hash) { 627 rawdb.DeleteTX3(cch.localTX3CacheDB, chainId, txHash) 628 } 629 630 func (cch *CrossChainHelper) WriteTX3ProofData(proofData *types.TX3ProofData) error { 631 return rawdb.WriteTX3ProofData(cch.localTX3CacheDB, proofData) 632 } 633 634 func (cch *CrossChainHelper) GetTX3ProofData(chainId string, txHash common.Hash) *types.TX3ProofData { 635 return rawdb.GetTX3ProofData(cch.localTX3CacheDB, chainId, txHash) 636 } 637 638 func (cch *CrossChainHelper) GetAllTX3ProofData() []*types.TX3ProofData { 639 return rawdb.GetAllTX3ProofData(cch.localTX3CacheDB) 640 } 641 642 func MustGetNeatChainFromNode(node *node.Node) *neatptc.NeatIO { 643 neatChain, err := getNeatChainFromNode(node) 644 if err != nil { 645 panic("getNeatChainFromNode error: " + err.Error()) 646 } 647 return neatChain 648 } 649 650 func getNeatChainFromNode(node *node.Node) (*neatptc.NeatIO, error) { 651 var neatChain *neatptc.NeatIO 652 if err := node.Service(&neatChain); err != nil { 653 return nil, err 654 } 655 656 return neatChain, nil 657 }