From ba144d898f9773b00c86e84b965fe6f9d42399a9 Mon Sep 17 00:00:00 2001 From: Jerome Date: Tue, 3 May 2022 21:18:28 +1000 Subject: [PATCH] process forensics (#84) * process forensics * Found common signers at same round for forensics * find attackers * add test for forensics * run setCommittedQCs after processForensics --- consensus/XDPoS/engines/engine_v2/engine.go | 2 +- .../XDPoS/engines/engine_v2/forensics.go | 225 ++++++++++++++++-- .../XDPoS/engines/engine_v2/forensics_test.go | 218 +++++++++++++++++ .../tests/engine_v2_tests/adaptor_test.go | 34 +-- .../authorised_masternode_test.go | 10 +- .../tests/engine_v2_tests/forensics_test.go | 101 +++++++- consensus/tests/engine_v2_tests/helper.go | 48 ++-- .../tests/engine_v2_tests/initial_test.go | 14 +- consensus/tests/engine_v2_tests/mine_test.go | 14 +- .../tests/engine_v2_tests/penalty_test.go | 2 +- .../engine_v2_tests/proposed_block_test.go | 30 +-- .../tests/engine_v2_tests/reward_test.go | 4 +- .../tests/engine_v2_tests/sync_info_test.go | 6 +- .../tests/engine_v2_tests/timeout_test.go | 16 +- .../engine_v2_tests/verify_blockinfo_test.go | 4 +- .../engine_v2_tests/verify_header_test.go | 12 +- consensus/tests/engine_v2_tests/vote_test.go | 20 +- 17 files changed, 638 insertions(+), 122 deletions(-) create mode 100644 consensus/XDPoS/engines/engine_v2/forensics_test.go diff --git a/consensus/XDPoS/engines/engine_v2/engine.go b/consensus/XDPoS/engines/engine_v2/engine.go index b2a42f2223..b11b39bdaf 100644 --- a/consensus/XDPoS/engines/engine_v2/engine.go +++ b/consensus/XDPoS/engines/engine_v2/engine.go @@ -933,7 +933,7 @@ func (x *XDPoS_v2) commitBlocks(blockChainReader consensus.ChainReader, proposed // Perform forensics related operation var headerQcToBeCommitted []types.Header headerQcToBeCommitted = append(headerQcToBeCommitted, *parentBlock, *proposedBlockHeader) - go x.forensics.SetCommittedQCs(headerQcToBeCommitted, *incomingQc) + go x.forensics.ForensicsMonitoring(blockChainReader, headerQcToBeCommitted, *incomingQc) return true, nil } // Everything else, fail to commit diff --git a/consensus/XDPoS/engines/engine_v2/forensics.go b/consensus/XDPoS/engines/engine_v2/forensics.go index ef6e3cf07e..a09508144b 100644 --- a/consensus/XDPoS/engines/engine_v2/forensics.go +++ b/consensus/XDPoS/engines/engine_v2/forensics.go @@ -2,27 +2,29 @@ package engine_v2 import ( "fmt" + "math/big" + "reflect" "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/consensus" "github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils" "github.com/XinFinOrg/XDPoSChain/core/types" + "github.com/XinFinOrg/XDPoSChain/crypto" "github.com/XinFinOrg/XDPoSChain/log" ) const ( - NUM_OF_FORENSICS_PARENTS = 2 + NUM_OF_FORENSICS_QC = 3 ) type ForensicProof struct { - QcWithSmallerRound utils.QuorumCert - QcWithLargerRound utils.QuorumCert - DivergingHash common.Hash - HashesTillSmallerRoundQc []common.Hash - HashesTillLargerRoundQc []common.Hash - AcrossEpochs bool - QcWithSmallerRoundAddresses []common.Address - QcWithLargerRoundAddresses []common.Address + QcWithSmallerRound utils.QuorumCert + QcWithLargerRound utils.QuorumCert + DivergingHash common.Hash + HashesTillSmallerRoundQc []common.Hash + HashesTillLargerRoundQc []common.Hash + AcrossEpochs bool + Attackers []common.Address } // Forensics instance. Placeholder for future properties to be added @@ -35,20 +37,15 @@ func NewForensics() *Forensics { return &Forensics{} } -/* - Entry point for processing forensics. - Triggered once processQC is successfully. - Forensics runs in a seperate go routine as its no system critical - Link to the flow diagram: https://hashlabs.atlassian.net/wiki/spaces/HASHLABS/pages/97878029/Forensics+Diagram+flow -*/ -func (f *Forensics) ProcessForensics(chain consensus.ChainReader, incomingQC utils.QuorumCert) { - log.Info("Received a QC in forensics", "QC", incomingQC) +func (f *Forensics) ForensicsMonitoring(chain consensus.ChainReader, headerQcToBeCommitted []types.Header, incomingQC utils.QuorumCert) error { + f.ProcessForensics(chain, incomingQC) + return f.SetCommittedQCs(headerQcToBeCommitted, incomingQC) } // Set the forensics committed QCs list. The order is from grandparent to current header. i.e it shall follow the QC in its header as follow [hcqc1, hcqc2, hcqc3] func (f *Forensics) SetCommittedQCs(headers []types.Header, incomingQC utils.QuorumCert) error { // highestCommitQCs is an array, assign the parentBlockQc and its child as well as its grandchild QC into this array for forensics purposes. - if len(headers) != NUM_OF_FORENSICS_PARENTS { + if len(headers) != NUM_OF_FORENSICS_QC-1 { log.Error("[SetCommittedQcs] Received input length not equal to 2", len(headers)) return fmt.Errorf("Received headers length not equal to 2 ") } @@ -80,13 +77,197 @@ func (f *Forensics) SetCommittedQCs(headers []types.Header, incomingQC utils.Quo return nil } +/* + Entry point for processing forensics. + Triggered once processQC is successfully. + Forensics runs in a seperate go routine as its no system critical + Link to the flow diagram: https://hashlabs.atlassian.net/wiki/spaces/HASHLABS/pages/97878029/Forensics+Diagram+flow +*/ +func (f *Forensics) ProcessForensics(chain consensus.ChainReader, incomingQC utils.QuorumCert) error { + log.Debug("Received a QC in forensics", "QC", incomingQC) + // Clone the values to a temporary variable + highestCommittedQCs := f.HighestCommittedQCs + if len(highestCommittedQCs) != NUM_OF_FORENSICS_QC { + log.Error("[ProcessForensics] HighestCommittedQCs value not set", "incomingQcProposedBlockHash", incomingQC.ProposedBlockInfo.Hash, "incomingQcProposedBlockNumber", incomingQC.ProposedBlockInfo.Number.Uint64(), "incomingQcProposedBlockRound", incomingQC.ProposedBlockInfo.Round) + return fmt.Errorf("HighestCommittedQCs value not set") + } + // Find the QC1 and QC2. We only care 2 parents in front of the incomingQC. The returned value contains QC1, QC2 and QC3(the incomingQC) + incomingQuorunCerts, err := f.findAncestorQCs(chain, incomingQC, 2) + if err != nil { + return err + } + isOnTheChain, err := f.checkQCsOnTheSameChain(chain, highestCommittedQCs, incomingQuorunCerts) + if err != nil { + return err + } + if isOnTheChain { + // Passed the checking, nothing suspecious. + log.Debug("[ProcessForensics] Passed forensics checking, nothing suspecious need to be reported", "incomingQcProposedBlockHash", incomingQC.ProposedBlockInfo.Hash, "incomingQcProposedBlockNumber", incomingQC.ProposedBlockInfo.Number.Uint64(), "incomingQcProposedBlockRound", incomingQC.ProposedBlockInfo.Round) + return nil + } + // Trigger the safety Alarm if failed + // First, find the QC in the two sets that have the same round + foundSameRoundQC, sameRoundHCQC, sameRoundQC := f.findQCsInSameRound(highestCommittedQCs, incomingQuorunCerts) + + if foundSameRoundQC { + attackersAddress := f.findCommonSigners(sameRoundHCQC, sameRoundQC) + f.SendForensicProof(attackersAddress, sameRoundHCQC, sameRoundQC) + } else { + // Not found, need a more complex approach to find the two QC + ancestorQC, lowerRoundQCs, _, err := f.findAncestorQcThroughRound(chain, highestCommittedQCs, incomingQuorunCerts) + if err != nil { + log.Error("[ProcessForensics] Error while trying to find ancestor QC through round number", "Error", err) + } + // Find the common signers within ancestorQC and lowerRoundQCs[NUM_OF_FORENSICS_QC-1] + attackersAddress := f.findCommonSigners(ancestorQC, lowerRoundQCs[NUM_OF_FORENSICS_QC-1]) + f.SendForensicProof(attackersAddress, ancestorQC, lowerRoundQCs[NUM_OF_FORENSICS_QC-1]) + } + + return nil +} + // Last step of forensics which sends out detailed proof to report service. -func (f *Forensics) SendForensicProof() { +func (f *Forensics) SendForensicProof(attackersAddress []common.Address, lqc utils.QuorumCert, hqc utils.QuorumCert) { + } -// Find the blockInfo of the block -2 distance away from the QC. Note: We using block number which means not necessary on the same chain as QC received -func (f *Forensics) findParentsQc(chain consensus.ChainReader, currentQc utils.QuorumCert, distanceFromCurrrentQc int64) { +// Utils function to help find the n-th previous QC. It returns an array of QC in ascending order including the currentQc as the last item in the array +func (f *Forensics) findAncestorQCs(chain consensus.ChainReader, currentQc utils.QuorumCert, distanceFromCurrrentQc int) ([]utils.QuorumCert, error) { + var quorumCerts []utils.QuorumCert + quorumCertificate := currentQc + // Append the initial value + quorumCerts = append(quorumCerts, quorumCertificate) + // Append the parents + for i := 0; i < distanceFromCurrrentQc; i++ { + parentHash := quorumCertificate.ProposedBlockInfo.Hash + parentHeader := chain.GetHeaderByHash(parentHash) + if parentHeader == nil { + log.Error("[findAncestorQCs] Forensics findAncestorQCs unable to find its parent block header", "BlockNum", parentHeader.Number.Int64(), "ParentHash", parentHash.Hex()) + return nil, fmt.Errorf("Unable to find parent block header in forensics") + } + var decodedExtraField utils.ExtraFields_v2 + err := utils.DecodeBytesExtraFields(parentHeader.Extra, &decodedExtraField) + if err != nil { + log.Error("[findAncestorQCs] Error while trying to decode from parent block extra", "BlockNum", parentHeader.Number.Int64(), "ParentHash", parentHash.Hex()) + } + quorumCertificate = *decodedExtraField.QuorumCert + quorumCerts = append(quorumCerts, quorumCertificate) + } + // The quorumCerts is in the reverse order, we need to flip it + var quorumCertsInAscendingOrder []utils.QuorumCert + for i := len(quorumCerts) - 1; i >= 0; i-- { + quorumCertsInAscendingOrder = append(quorumCertsInAscendingOrder, quorumCerts[i]) + } + return quorumCertsInAscendingOrder, nil } -func (f *Forensics) findCommonSigners(currentQc utils.QuorumCert, higherQc utils.QuorumCert) { +// Check whether two provided QC set are on the same chain +func (f *Forensics) checkQCsOnTheSameChain(chain consensus.ChainReader, highestCommittedQCs []utils.QuorumCert, incomingQCandItsParents []utils.QuorumCert) (bool, error) { + // Re-order two sets of QCs by block Number + lowerBlockNumQCs := highestCommittedQCs + higherBlockNumQCs := incomingQCandItsParents + if incomingQCandItsParents[0].ProposedBlockInfo.Number.Cmp(highestCommittedQCs[0].ProposedBlockInfo.Number) == -1 { + lowerBlockNumQCs = incomingQCandItsParents + higherBlockNumQCs = highestCommittedQCs + } + + proposedBlockInfo := higherBlockNumQCs[0].ProposedBlockInfo + for i := 0; i < int((big.NewInt(0).Sub(higherBlockNumQCs[0].ProposedBlockInfo.Number, lowerBlockNumQCs[0].ProposedBlockInfo.Number)).Int64()); i++ { + parentHeader := chain.GetHeaderByHash(proposedBlockInfo.Hash) + var decodedExtraField utils.ExtraFields_v2 + err := utils.DecodeBytesExtraFields(parentHeader.Extra, &decodedExtraField) + if err != nil { + log.Error("[ProcessForensics] Fail to decode extra when checking the two QCs set on the same chain", "Error", err) + return false, err + } + proposedBlockInfo = decodedExtraField.QuorumCert.ProposedBlockInfo + } + // Check the final proposed blockInfo is the same as what we have from lowerBlockNumQCs[0] + if reflect.DeepEqual(proposedBlockInfo, lowerBlockNumQCs[0].ProposedBlockInfo) { + return true, nil + } + + return false, nil +} + +// Given the two QCs set, find if there are any QC that have the same round +func (f *Forensics) findQCsInSameRound(quorumCerts1 []utils.QuorumCert, quorumCerts2 []utils.QuorumCert) (bool, utils.QuorumCert, utils.QuorumCert) { + for _, quorumCert1 := range quorumCerts1 { + for _, quorumCert2 := range quorumCerts2 { + if quorumCert1.ProposedBlockInfo.Round == quorumCert2.ProposedBlockInfo.Round { + return true, quorumCert1, quorumCert2 + } + } + } + return false, utils.QuorumCert{}, utils.QuorumCert{} +} + +// Find the common address that have signed both QCs +func (f *Forensics) findCommonSigners(quorumCert1 utils.QuorumCert, quorumCert2 utils.QuorumCert) []common.Address { + var commonSigners []common.Address + quorumCert1SignersMap := make(map[string]bool) + // The QC signatures are signed by votes special struct VoteForSign + quorumCert1SignedHash := utils.VoteSigHash(&utils.VoteForSign{ + ProposedBlockInfo: quorumCert1.ProposedBlockInfo, + GapNumber: quorumCert1.GapNumber, + }) + for _, signature := range quorumCert1.Signatures { + var signerAddress common.Address + pubkey, err := crypto.Ecrecover(quorumCert1SignedHash.Bytes(), signature) + if err != nil { + log.Error("[findCommonSigners] Fail to Ecrecover signer from the quorumCert1SignedHash", "quorumCert1.GapNumber", quorumCert1.GapNumber, "quorumCert1.ProposedBlockInfo", quorumCert1.ProposedBlockInfo) + } + + copy(signerAddress[:], crypto.Keccak256(pubkey[1:])[12:]) + quorumCert1SignersMap[signerAddress.Hex()] = true + } + // Now, Let's check if quorumCert2 have any signers that have in common with quorumCert1SignersMap(from quorumCert1) + quorumCert2SignedHash := utils.VoteSigHash(&utils.VoteForSign{ + ProposedBlockInfo: quorumCert2.ProposedBlockInfo, + GapNumber: quorumCert2.GapNumber, + }) + for _, signature := range quorumCert2.Signatures { + var signerAddress common.Address + pubkey, err := crypto.Ecrecover(quorumCert2SignedHash.Bytes(), signature) + if err != nil { + log.Error("[findCommonSigners] Fail to Ecrecover signer from the quorumCert2SignedHash", "quorumCert2.GapNumber", quorumCert2.GapNumber, "quorumCert2.ProposedBlockInfo", quorumCert2.ProposedBlockInfo) + } + + copy(signerAddress[:], crypto.Keccak256(pubkey[1:])[12:]) + if quorumCert1SignersMap[signerAddress.Hex()] { + commonSigners = append(commonSigners, signerAddress) + } + } + return commonSigners +} + +// Check whether the given QCs are on the same chain as the stored committed QCs(f.HighestCommittedQCs) regardless their orders +func (f *Forensics) findAncestorQcThroughRound(chain consensus.ChainReader, highestCommittedQCs []utils.QuorumCert, incomingQCandItsParents []utils.QuorumCert) (utils.QuorumCert, []utils.QuorumCert, []utils.QuorumCert, error) { + /* + Re-order two sets of QCs by Round number + */ + lowerRoundQCs := highestCommittedQCs + higherRoundQCs := incomingQCandItsParents + if incomingQCandItsParents[0].ProposedBlockInfo.Round < highestCommittedQCs[0].ProposedBlockInfo.Round { + lowerRoundQCs = incomingQCandItsParents + higherRoundQCs = highestCommittedQCs + } + + // Find the ancestorFromIncomingQC1 that matches round number < lowerRoundQCs3 + ancestorQC := higherRoundQCs[0] + for ancestorQC.ProposedBlockInfo.Round >= lowerRoundQCs[NUM_OF_FORENSICS_QC-1].ProposedBlockInfo.Round { + proposedBlock := chain.GetHeaderByHash(ancestorQC.ProposedBlockInfo.Hash) + var decodedExtraField utils.ExtraFields_v2 + err := utils.DecodeBytesExtraFields(proposedBlock.Extra, &decodedExtraField) + if err != nil { + log.Error("[findAncestorQcThroughRound] Error while trying to decode extra field", "ProposedBlockInfo.Hash", ancestorQC.ProposedBlockInfo.Hash) + return ancestorQC, lowerRoundQCs, higherRoundQCs, err + } + // Found the ancestor QC + if decodedExtraField.QuorumCert.ProposedBlockInfo.Round < lowerRoundQCs[NUM_OF_FORENSICS_QC-1].ProposedBlockInfo.Round { + return ancestorQC, lowerRoundQCs, higherRoundQCs, nil + } + ancestorQC = *decodedExtraField.QuorumCert + } + return ancestorQC, lowerRoundQCs, higherRoundQCs, fmt.Errorf("[findAncestorQcThroughRound] Could not find ancestor QC") } diff --git a/consensus/XDPoS/engines/engine_v2/forensics_test.go b/consensus/XDPoS/engines/engine_v2/forensics_test.go new file mode 100644 index 0000000000..9ccd4239c2 --- /dev/null +++ b/consensus/XDPoS/engines/engine_v2/forensics_test.go @@ -0,0 +1,218 @@ +package engine_v2 + +import ( + "crypto/ecdsa" + "fmt" + "io/ioutil" + "math/big" + "math/rand" + "os" + "testing" + + "github.com/XinFinOrg/XDPoSChain/accounts" + "github.com/XinFinOrg/XDPoSChain/accounts/keystore" + "github.com/XinFinOrg/XDPoSChain/common" + "github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils" + "github.com/XinFinOrg/XDPoSChain/crypto" + "github.com/stretchr/testify/assert" +) + +// Utils to help mocking the signing of signatures +var ( + signer1, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a") + signer2, _ = crypto.HexToECDSA("49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee") + signer3, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") +) + +const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" + +func SignHashByPK(pk *ecdsa.PrivateKey, itemToSign []byte) []byte { + signer, signFn, err := getSignerAndSignFn(pk) + if err != nil { + panic(err) + } + signedHash, err := signFn(accounts.Account{Address: signer}, itemToSign) + if err != nil { + panic(err) + } + return signedHash +} +func RandStringBytes(n int) string { + b := make([]byte, n) + for i := range b { + b[i] = letterBytes[rand.Intn(len(letterBytes))] + } + return string(b) +} + +func getSignerAndSignFn(pk *ecdsa.PrivateKey) (common.Address, func(account accounts.Account, hash []byte) ([]byte, error), error) { + veryLightScryptN := 2 + veryLightScryptP := 1 + dir, _ := ioutil.TempDir("", fmt.Sprintf("eth-getSignerAndSignFn-test-%v", RandStringBytes(5))) + + new := func(kd string) *keystore.KeyStore { + return keystore.NewKeyStore(kd, veryLightScryptN, veryLightScryptP) + } + + defer os.RemoveAll(dir) + ks := new(dir) + pass := "" // not used but required by API + a1, err := ks.ImportECDSA(pk, pass) + if err != nil { + return common.Address{}, nil, fmt.Errorf(err.Error()) + } + if err := ks.Unlock(a1, ""); err != nil { + return a1.Address, nil, fmt.Errorf(err.Error()) + } + return a1.Address, ks.SignHash, nil +} + +func TestFindCommonSigners(t *testing.T) { + forensics := &Forensics{} + proposedBlockInfo := &utils.BlockInfo{ + Hash: common.StringToHash("123"), + Round: utils.Round(10), + Number: big.NewInt(910), + } + gapNumber := 450 + voteForSign := &utils.VoteForSign{ + ProposedBlockInfo: proposedBlockInfo, + GapNumber: uint64(gapNumber), + } + signatureFromSigner1 := SignHashByPK(signer1, utils.VoteSigHash(voteForSign).Bytes()) + signatureFromSigner2 := SignHashByPK(signer2, utils.VoteSigHash(voteForSign).Bytes()) + signatureFromSigner3 := SignHashByPK(signer3, utils.VoteSigHash(voteForSign).Bytes()) + + // If ONE in common + var signaturesForQC1 []utils.Signature + qc1 := &utils.QuorumCert{ + ProposedBlockInfo: proposedBlockInfo, + Signatures: append(signaturesForQC1, signatureFromSigner1, signatureFromSigner2), + GapNumber: uint64(gapNumber), + } + + var signaturesForQC2 []utils.Signature + qc2 := &utils.QuorumCert{ + ProposedBlockInfo: proposedBlockInfo, + Signatures: append(signaturesForQC2, signatureFromSigner2, signatureFromSigner3), + GapNumber: uint64(gapNumber), + } + + commonSigners := forensics.findCommonSigners(*qc1, *qc2) + assert.Equal(t, 1, len(commonSigners)) + assert.Equal(t, crypto.PubkeyToAddress(signer2.PublicKey), commonSigners[0]) + + // If none in common + var signaturesForQC1NoneInCommon []utils.Signature + qc1 = &utils.QuorumCert{ + ProposedBlockInfo: proposedBlockInfo, + Signatures: append(signaturesForQC1NoneInCommon, signatureFromSigner1), + GapNumber: uint64(gapNumber), + } + + var signaturesForQC2NoneInCommon []utils.Signature + qc2 = &utils.QuorumCert{ + ProposedBlockInfo: proposedBlockInfo, + Signatures: append(signaturesForQC2NoneInCommon, signatureFromSigner2, signatureFromSigner3), + GapNumber: uint64(gapNumber), + } + + commonSigners = forensics.findCommonSigners(*qc1, *qc2) + assert.Equal(t, 0, len(commonSigners)) + + // All in common + var signaturesForQC1AllInCommon []utils.Signature + qc1 = &utils.QuorumCert{ + ProposedBlockInfo: proposedBlockInfo, + Signatures: append(signaturesForQC1AllInCommon, signatureFromSigner1, signatureFromSigner2, signatureFromSigner3), + GapNumber: uint64(gapNumber), + } + + var signaturesForQC2AllInCommon []utils.Signature + qc2 = &utils.QuorumCert{ + ProposedBlockInfo: proposedBlockInfo, + Signatures: append(signaturesForQC2AllInCommon, signatureFromSigner1, signatureFromSigner2, signatureFromSigner3), + GapNumber: uint64(gapNumber), + } + + commonSigners = forensics.findCommonSigners(*qc1, *qc2) + assert.Equal(t, 3, len(commonSigners)) + assert.Equal(t, crypto.PubkeyToAddress(signer1.PublicKey), commonSigners[0]) + assert.Equal(t, crypto.PubkeyToAddress(signer2.PublicKey), commonSigners[1]) + assert.Equal(t, crypto.PubkeyToAddress(signer3.PublicKey), commonSigners[2]) +} + +func TestFindQCsInSameRound(t *testing.T) { + forensics := &Forensics{} + gapNumber := 450 + + // If ONE in common + var sig []utils.Signature + qc1 := &utils.QuorumCert{ + ProposedBlockInfo: &utils.BlockInfo{ + Hash: common.StringToHash("qc1"), + Round: utils.Round(10), + Number: big.NewInt(910), + }, + Signatures: sig, + GapNumber: uint64(gapNumber), + } + + qc2 := &utils.QuorumCert{ + ProposedBlockInfo: &utils.BlockInfo{ + Hash: common.StringToHash("qc2"), + Round: utils.Round(12), + Number: big.NewInt(910), + }, + Signatures: sig, + GapNumber: uint64(gapNumber), + } + + qc3 := &utils.QuorumCert{ + ProposedBlockInfo: &utils.BlockInfo{ + Hash: common.StringToHash("qc3"), + Round: utils.Round(13), + Number: big.NewInt(910), + }, + Signatures: sig, + GapNumber: uint64(gapNumber), + } + + qc4 := &utils.QuorumCert{ + ProposedBlockInfo: &utils.BlockInfo{ + Hash: common.StringToHash("qc4"), + Round: utils.Round(12), + Number: big.NewInt(910), + }, + Signatures: sig, + GapNumber: uint64(gapNumber), + } + + qc5 := &utils.QuorumCert{ + ProposedBlockInfo: &utils.BlockInfo{ + Hash: common.StringToHash("qc5"), + Round: utils.Round(13), + Number: big.NewInt(910), + }, + Signatures: sig, + GapNumber: uint64(gapNumber), + } + + qc6 := &utils.QuorumCert{ + ProposedBlockInfo: &utils.BlockInfo{ + Hash: common.StringToHash("qc6"), + Round: utils.Round(15), + Number: big.NewInt(910), + }, + Signatures: sig, + GapNumber: uint64(gapNumber), + } + + var qcSet1 []utils.QuorumCert + var qcSet2 []utils.QuorumCert + + found, first, second := forensics.findQCsInSameRound(append(qcSet1, *qc1, *qc2, *qc3), append(qcSet2, *qc4, *qc5, *qc6)) + assert.True(t, found) + assert.Equal(t, *qc2, first) + assert.Equal(t, *qc4, second) +} diff --git a/consensus/tests/engine_v2_tests/adaptor_test.go b/consensus/tests/engine_v2_tests/adaptor_test.go index 19def80f7e..d097147857 100644 --- a/consensus/tests/engine_v2_tests/adaptor_test.go +++ b/consensus/tests/engine_v2_tests/adaptor_test.go @@ -14,7 +14,7 @@ import ( ) func TestAdaptorShouldGetAuthorForDifferentConsensusVersion(t *testing.T) { - blockchain, _, currentBlock, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, 900, params.TestXDPoSMockChainConfig, 0) + blockchain, _, currentBlock, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, 900, params.TestXDPoSMockChainConfig, nil) adaptor := blockchain.Engine().(*XDPoS.XDPoS) addressFromAdaptor, errorAdaptor := adaptor.Author(currentBlock.Header()) @@ -39,7 +39,7 @@ func TestAdaptorShouldGetAuthorForDifferentConsensusVersion(t *testing.T) { Coinbase: signer, } - header.Extra = generateV2Extra(1, currentBlock, signer, signFn) + header.Extra = generateV2Extra(1, currentBlock, signer, signFn, nil) block901, err := createBlockFromHeader(blockchain, header, nil, signer, signFn, blockchain.Config()) if err != nil { @@ -61,7 +61,7 @@ func TestAdaptorShouldGetAuthorForDifferentConsensusVersion(t *testing.T) { } func TestAdaptorGetMasternodesFromCheckpointHeader(t *testing.T) { - blockchain, _, currentBlock, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 1, params.TestXDPoSMockChainConfig, 0) + blockchain, _, currentBlock, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 1, params.TestXDPoSMockChainConfig, nil) adaptor := blockchain.Engine().(*XDPoS.XDPoS) headerV1 := currentBlock.Header() headerV1.Extra = common.Hex2Bytes("d7830100018358444388676f312e31352e38856c696e757800000000000000000278c350152e15fa6ffc712a5a73d704ce73e2e103d9e17ae3ff2c6712e44e25b09ac5ee91f6c9ff065551f0dcac6f00cae11192d462db709be3758ccef312ee5eea8d7bad5374c6a652150515d744508b61c1a4deb4e4e7bf057e4e3824c11fd2569bcb77a52905cda63b5a58507910bed335e4c9d87ae0ecdfafd400") @@ -73,7 +73,7 @@ func TestAdaptorGetMasternodesFromCheckpointHeader(t *testing.T) { assert.True(t, reflect.DeepEqual(masternodesV1, masternodesV2), "GetMasternodesFromCheckpointHeader in adaptor for v1 v2 not equal", "v1", masternodesV1, "v2", masternodesV2) } func TestAdaptorIsEpochSwitch(t *testing.T) { - blockchain, _, currentBlock, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 1, params.TestXDPoSMockChainConfig, 0) + blockchain, _, currentBlock, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 1, params.TestXDPoSMockChainConfig, nil) adaptor := blockchain.Engine().(*XDPoS.XDPoS) header := currentBlock.Header() // v1 @@ -96,7 +96,7 @@ func TestAdaptorIsEpochSwitch(t *testing.T) { quorumCert := &utils.QuorumCert{ ProposedBlockInfo: parentBlockInfo, Signatures: nil, - GapNumber: blockchain.Config().XDPoS.V2.SwitchBlock.Uint64()-blockchain.Config().XDPoS.Gap, + GapNumber: blockchain.Config().XDPoS.V2.SwitchBlock.Uint64() - blockchain.Config().XDPoS.Gap, } extra := utils.ExtraFields_v2{ Round: 1, @@ -117,7 +117,7 @@ func TestAdaptorIsEpochSwitch(t *testing.T) { quorumCert = &utils.QuorumCert{ ProposedBlockInfo: parentBlockInfo, Signatures: nil, - GapNumber: blockchain.Config().XDPoS.V2.SwitchBlock.Uint64()-blockchain.Config().XDPoS.Gap, + GapNumber: blockchain.Config().XDPoS.V2.SwitchBlock.Uint64() - blockchain.Config().XDPoS.Gap, } extra = utils.ExtraFields_v2{ Round: 2, @@ -138,7 +138,7 @@ func TestAdaptorIsEpochSwitch(t *testing.T) { quorumCert = &utils.QuorumCert{ ProposedBlockInfo: parentBlockInfo, Signatures: nil, - GapNumber: blockchain.Config().XDPoS.V2.SwitchBlock.Uint64()-blockchain.Config().XDPoS.Gap, + GapNumber: blockchain.Config().XDPoS.V2.SwitchBlock.Uint64() - blockchain.Config().XDPoS.Gap, } extra = utils.ExtraFields_v2{ Round: utils.Round(blockchain.Config().XDPoS.Epoch) + 1, @@ -159,7 +159,7 @@ func TestAdaptorIsEpochSwitch(t *testing.T) { quorumCert = &utils.QuorumCert{ ProposedBlockInfo: parentBlockInfo, Signatures: nil, - GapNumber: blockchain.Config().XDPoS.V2.SwitchBlock.Uint64()-blockchain.Config().XDPoS.Gap, + GapNumber: blockchain.Config().XDPoS.V2.SwitchBlock.Uint64() - blockchain.Config().XDPoS.Gap, } extra = utils.ExtraFields_v2{ Round: utils.Round(blockchain.Config().XDPoS.Epoch) + 2, @@ -176,11 +176,11 @@ func TestAdaptorIsEpochSwitch(t *testing.T) { func TestAdaptorGetMasternodesV2(t *testing.T) { // we skip test for v1 since it's hard to make a real genesis block - blockchain, _, currentBlock, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, 900, params.TestXDPoSMockChainConfig, 0) + blockchain, _, currentBlock, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, 900, params.TestXDPoSMockChainConfig, nil) adaptor := blockchain.Engine().(*XDPoS.XDPoS) blockNum := 901 blockCoinBase := "0x111000000000000000000000000000000123" - currentBlock = CreateBlock(blockchain, params.TestXDPoSMockChainConfig, currentBlock, blockNum, 1, blockCoinBase, signer, signFn, nil) + currentBlock = CreateBlock(blockchain, params.TestXDPoSMockChainConfig, currentBlock, blockNum, 1, blockCoinBase, signer, signFn, nil, nil) // block 901 is the first v2 block, and is treated as epoch switch block err := blockchain.InsertBlock(currentBlock) @@ -190,7 +190,7 @@ func TestAdaptorGetMasternodesV2(t *testing.T) { masternodes1ByNumber := adaptor.GetMasternodesByNumber(blockchain, currentBlock.NumberU64()) assert.True(t, reflect.DeepEqual(masternodes1, masternodes1ByNumber), "at block number", blockNum) for blockNum = 902; blockNum < 915; blockNum++ { - currentBlock = CreateBlock(blockchain, params.TestXDPoSMockChainConfig, currentBlock, blockNum, int64(blockNum-900), blockCoinBase, signer, signFn, nil) + currentBlock = CreateBlock(blockchain, params.TestXDPoSMockChainConfig, currentBlock, blockNum, int64(blockNum-900), blockCoinBase, signer, signFn, nil, nil) err = blockchain.InsertBlock(currentBlock) assert.Nil(t, err) masternodes2 := adaptor.GetMasternodes(blockchain, currentBlock.Header()) @@ -201,7 +201,7 @@ func TestAdaptorGetMasternodesV2(t *testing.T) { } func TestGetCurrentEpochSwitchBlock(t *testing.T) { - blockchain, _, currentBlock, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, 900, params.TestXDPoSMockChainConfig, 0) + blockchain, _, currentBlock, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, 900, params.TestXDPoSMockChainConfig, nil) adaptor := blockchain.Engine().(*XDPoS.XDPoS) // V1 @@ -213,7 +213,7 @@ func TestGetCurrentEpochSwitchBlock(t *testing.T) { // V2 blockNum := 901 blockCoinBase := "0x111000000000000000000000000000000123" - currentBlock = CreateBlock(blockchain, params.TestXDPoSMockChainConfig, currentBlock, blockNum, 1, blockCoinBase, signer, signFn, nil) + currentBlock = CreateBlock(blockchain, params.TestXDPoSMockChainConfig, currentBlock, blockNum, 1, blockCoinBase, signer, signFn, nil, nil) err = blockchain.InsertBlock(currentBlock) assert.Nil(t, err) currentCheckpointNumber, epochNum, err = adaptor.GetCurrentEpochSwitchBlock(blockchain, currentBlock.Number()) @@ -222,7 +222,7 @@ func TestGetCurrentEpochSwitchBlock(t *testing.T) { assert.Equal(t, uint64(1), epochNum) for blockNum = 902; blockNum < 915; blockNum++ { - currentBlock = CreateBlock(blockchain, params.TestXDPoSMockChainConfig, currentBlock, blockNum, int64(blockNum-900), blockCoinBase, signer, signFn, nil) + currentBlock = CreateBlock(blockchain, params.TestXDPoSMockChainConfig, currentBlock, blockNum, int64(blockNum-900), blockCoinBase, signer, signFn, nil, nil) err = blockchain.InsertBlock(currentBlock) assert.Nil(t, err) @@ -234,7 +234,7 @@ func TestGetCurrentEpochSwitchBlock(t *testing.T) { } func TestGetParentBlock(t *testing.T) { - blockchain, _, block900, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, 900, params.TestXDPoSMockChainConfig, 0) + blockchain, _, block900, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, 900, params.TestXDPoSMockChainConfig, nil) adaptor := blockchain.Engine().(*XDPoS.XDPoS) // V1 @@ -248,13 +248,13 @@ func TestGetParentBlock(t *testing.T) { // V2 blockNum := 901 blockCoinBase := "0x111000000000000000000000000000000123" - block901 := CreateBlock(blockchain, params.TestXDPoSMockChainConfig, block900, blockNum, 1, blockCoinBase, signer, signFn, nil) + block901 := CreateBlock(blockchain, params.TestXDPoSMockChainConfig, block900, blockNum, 1, blockCoinBase, signer, signFn, nil, nil) err = blockchain.InsertBlock(block901) assert.Nil(t, err) // let's inject another one, but the highestedQC has not been updated, so it shall still point to 900 blockNum = 902 - block902 := CreateBlock(blockchain, params.TestXDPoSMockChainConfig, block901, blockNum, 1, blockCoinBase, signer, signFn, nil) + block902 := CreateBlock(blockchain, params.TestXDPoSMockChainConfig, block901, blockNum, 1, blockCoinBase, signer, signFn, nil, nil) err = blockchain.InsertBlock(block902) assert.Nil(t, err) block = adaptor.FindParentBlockToAssign(blockchain, block902) diff --git a/consensus/tests/engine_v2_tests/authorised_masternode_test.go b/consensus/tests/engine_v2_tests/authorised_masternode_test.go index 99ac58b0fd..017f93cc0a 100644 --- a/consensus/tests/engine_v2_tests/authorised_masternode_test.go +++ b/consensus/tests/engine_v2_tests/authorised_masternode_test.go @@ -12,11 +12,11 @@ import ( func TestIsAuthorisedMNForConsensusV2(t *testing.T) { // we skip test for v1 since it's hard to make a real genesis block - blockchain, _, currentBlock, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, 900, params.TestXDPoSMockChainConfig, 0) + blockchain, _, currentBlock, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, 900, params.TestXDPoSMockChainConfig, nil) adaptor := blockchain.Engine().(*XDPoS.XDPoS) blockNum := 901 blockCoinBase := "0x111000000000000000000000000000000123" - currentBlock = CreateBlock(blockchain, params.TestXDPoSMockChainConfig, currentBlock, blockNum, 1, blockCoinBase, signer, signFn, nil) + currentBlock = CreateBlock(blockchain, params.TestXDPoSMockChainConfig, currentBlock, blockNum, 1, blockCoinBase, signer, signFn, nil, nil) err := blockchain.InsertBlock(currentBlock) assert.Nil(t, err) // As long as the address is in the master node list, they are all valid @@ -32,12 +32,12 @@ func TestIsAuthorisedMNForConsensusV2(t *testing.T) { func TestIsYourTurnConsensusV2(t *testing.T) { // we skip test for v1 since it's hard to make a real genesis block - blockchain, _, currentBlock, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, 900, params.TestXDPoSMockChainConfig, 0) + blockchain, _, currentBlock, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, 900, params.TestXDPoSMockChainConfig, nil) minePeriod := params.TestXDPoSV2Config.MinePeriod adaptor := blockchain.Engine().(*XDPoS.XDPoS) blockNum := 901 blockCoinBase := "0x111000000000000000000000000000000123" - currentBlock = CreateBlock(blockchain, params.TestXDPoSMockChainConfig, currentBlock, blockNum, 1, blockCoinBase, signer, signFn, nil) + currentBlock = CreateBlock(blockchain, params.TestXDPoSMockChainConfig, currentBlock, blockNum, 1, blockCoinBase, signer, signFn, nil, nil) err := blockchain.InsertBlock(currentBlock) assert.Nil(t, err) // Less then Mine Period @@ -61,7 +61,7 @@ func TestIsYourTurnConsensusV2(t *testing.T) { // We continue to grow the chain which will increase the round number blockNum = 902 - currentBlock = CreateBlock(blockchain, params.TestXDPoSMockChainConfig, currentBlock, blockNum, int64(blockNum-900), blockCoinBase, signer, signFn, nil) + currentBlock = CreateBlock(blockchain, params.TestXDPoSMockChainConfig, currentBlock, blockNum, int64(blockNum-900), blockCoinBase, signer, signFn, nil, nil) err = blockchain.InsertBlock(currentBlock) assert.Nil(t, err) time.Sleep(time.Duration(minePeriod) * time.Second) diff --git a/consensus/tests/engine_v2_tests/forensics_test.go b/consensus/tests/engine_v2_tests/forensics_test.go index 52eca4ab6b..d96165487f 100644 --- a/consensus/tests/engine_v2_tests/forensics_test.go +++ b/consensus/tests/engine_v2_tests/forensics_test.go @@ -1,6 +1,7 @@ package engine_v2_tests import ( + "crypto/ecdsa" "math/big" "testing" "time" @@ -15,7 +16,7 @@ import ( ) func TestProcessQcShallSetForensicsCommittedQc(t *testing.T) { - blockchain, _, currentBlock, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, 905, params.TestXDPoSMockChainConfig, 0) + blockchain, _, currentBlock, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, 905, params.TestXDPoSMockChainConfig, nil) engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2 // Assuming we are getting block 906 which have QC pointing at block 905 @@ -81,7 +82,7 @@ func TestProcessQcShallSetForensicsCommittedQc(t *testing.T) { } func TestSetCommittedQCsInOrder(t *testing.T) { - blockchain, _, currentBlock, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 905, params.TestXDPoSMockChainConfig, 0) + blockchain, _, currentBlock, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 905, params.TestXDPoSMockChainConfig, nil) forensics := blockchain.Engine().(*XDPoS.XDPoS).EngineV2.GetForensicsFaker() var headers []types.Header @@ -96,4 +97,100 @@ func TestSetCommittedQCsInOrder(t *testing.T) { err = forensics.SetCommittedQCs(append(headers, *blockchain.GetHeaderByNumber(903), *blockchain.GetHeaderByNumber(904)), *decodedExtraField.QuorumCert) assert.Nil(t, err) assert.Equal(t, 3, len(forensics.HighestCommittedQCs)) + + // Test previous blocks + err = utils.DecodeBytesExtraFields(blockchain.GetHeaderByNumber(904).Extra, &decodedExtraField) + assert.Nil(t, err) + err = forensics.SetCommittedQCs(append(headers, *blockchain.GetHeaderByNumber(902), *blockchain.GetHeaderByNumber(903)), *decodedExtraField.QuorumCert) + assert.Nil(t, err) + assert.Equal(t, 3, len(forensics.HighestCommittedQCs)) +} + +// Happty path +func TestForensicsMonitoring(t *testing.T) { + blockchain, _, currentBlock, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 915, params.TestXDPoSMockChainConfig, nil) + forensics := blockchain.Engine().(*XDPoS.XDPoS).EngineV2.GetForensicsFaker() + var decodedCurrentblockExtraField utils.ExtraFields_v2 + // Decode the QC from latest block + err := utils.DecodeBytesExtraFields(currentBlock.Header().Extra, &decodedCurrentblockExtraField) + assert.Nil(t, err) + incomingQC := decodedCurrentblockExtraField.QuorumCert + // Now, let's try set committed blocks, where the highestedCommitted blocks are 905, 906 and 907 + var headers []types.Header + var decodedBlock905ExtraField utils.ExtraFields_v2 + err = utils.DecodeBytesExtraFields(blockchain.GetHeaderByNumber(905).Extra, &decodedBlock905ExtraField) + assert.Nil(t, err) + + err = forensics.SetCommittedQCs(append(headers, *blockchain.GetHeaderByNumber(903), *blockchain.GetHeaderByNumber(904)), *decodedBlock905ExtraField.QuorumCert) + assert.Nil(t, err) + var newIncomingQcHeaders []types.Header + newIncomingQcHeaders = append(newIncomingQcHeaders, *blockchain.GetHeaderByNumber(913), *blockchain.GetHeaderByNumber(914)) + err = forensics.ForensicsMonitoring(blockchain, newIncomingQcHeaders, *incomingQC) + assert.Nil(t, err) +} + +func TestForensicsMonitoringNotOnSameChainButHaveSameRoundQC(t *testing.T) { + var numOfForks = new(int) + *numOfForks = 10 + var forkRoundDifference = new(int) + *forkRoundDifference = 1 + blockchain, _, _, _, _, currentForkBlock := PrepareXDCTestBlockChainForV2Engine(t, 915, params.TestXDPoSMockChainConfig, &ForkedBlockOptions{numOfForkedBlocks: numOfForks, forkedRoundDifference: forkRoundDifference}) + forensics := blockchain.Engine().(*XDPoS.XDPoS).EngineV2.GetForensicsFaker() + + // Now, let's try set committed blocks, where the highestedCommitted blocks are 913, 914 and 915 + var headers []types.Header + var decodedBlock915ExtraField utils.ExtraFields_v2 + err := utils.DecodeBytesExtraFields(blockchain.GetHeaderByNumber(915).Extra, &decodedBlock915ExtraField) + assert.Nil(t, err) + err = forensics.SetCommittedQCs(append(headers, *blockchain.GetHeaderByNumber(913), *blockchain.GetHeaderByNumber(914)), *decodedBlock915ExtraField.QuorumCert) + assert.Nil(t, err) + + var decodedExtraField utils.ExtraFields_v2 + // Decode the QC from forking chain + err = utils.DecodeBytesExtraFields(currentForkBlock.Header().Extra, &decodedExtraField) + assert.Nil(t, err) + + incomingQC := decodedExtraField.QuorumCert + + var forkedHeaders []types.Header + parentOfForkedHeader := blockchain.GetBlockByHash(currentForkBlock.ParentHash()).Header() + grandParentOfForkedHeader := blockchain.GetBlockByHash(parentOfForkedHeader.ParentHash).Header() + forkedHeaders = append(forkedHeaders, *grandParentOfForkedHeader, *parentOfForkedHeader) + err = forensics.ForensicsMonitoring(blockchain, forkedHeaders, *incomingQC) + assert.Nil(t, err) + // TODO: Check SendForensicProof triggered +} + +func TestForensicsMonitoringNotOnSameChainDoNotHaveSameRoundQC(t *testing.T) { + var numOfForks = new(int) + *numOfForks = 10 + var forkRoundDifference = new(int) + *forkRoundDifference = 10 + var forkedChainSignersKey []*ecdsa.PrivateKey + forkedChainSignersKey = append(forkedChainSignersKey, acc1Key) + blockchain, _, _, _, _, currentForkBlock := PrepareXDCTestBlockChainForV2Engine(t, 915, params.TestXDPoSMockChainConfig, &ForkedBlockOptions{numOfForkedBlocks: numOfForks, forkedRoundDifference: forkRoundDifference, signersKey: forkedChainSignersKey}) + forensics := blockchain.Engine().(*XDPoS.XDPoS).EngineV2.GetForensicsFaker() + + // Now, let's try set committed blocks, where the highestedCommitted blocks are 913, 914 and 915 + var headers []types.Header + var decodedBlock915ExtraField utils.ExtraFields_v2 + err := utils.DecodeBytesExtraFields(blockchain.GetHeaderByNumber(915).Extra, &decodedBlock915ExtraField) + assert.Nil(t, err) + err = forensics.SetCommittedQCs(append(headers, *blockchain.GetHeaderByNumber(913), *blockchain.GetHeaderByNumber(914)), *decodedBlock915ExtraField.QuorumCert) + assert.Nil(t, err) + + var decodedExtraField utils.ExtraFields_v2 + // Decode the QC from forking chain + err = utils.DecodeBytesExtraFields(currentForkBlock.Header().Extra, &decodedExtraField) + assert.Nil(t, err) + + incomingQC := decodedExtraField.QuorumCert + var forkedHeaders []types.Header + parentOfForkedHeader := blockchain.GetBlockByHash(currentForkBlock.ParentHash()).Header() + grandParentOfForkedHeader := blockchain.GetBlockByHash(parentOfForkedHeader.ParentHash).Header() + forkedHeaders = append(forkedHeaders, *grandParentOfForkedHeader, *parentOfForkedHeader) + + err = forensics.ForensicsMonitoring(blockchain, forkedHeaders, *incomingQC) + assert.Nil(t, err) + // TODO: Check SendForensicProof triggered } diff --git a/consensus/tests/engine_v2_tests/helper.go b/consensus/tests/engine_v2_tests/helper.go index 7723afa4f7..89939b716b 100644 --- a/consensus/tests/engine_v2_tests/helper.go +++ b/consensus/tests/engine_v2_tests/helper.go @@ -272,8 +272,14 @@ func GetCandidateFromCurrentSmartContract(backend bind.ContractBackend, t *testi return ms } +type ForkedBlockOptions struct { + numOfForkedBlocks *int + forkedRoundDifference *int // Minimum is 1 + signersKey []*ecdsa.PrivateKey +} + // V2 concensus engine -func PrepareXDCTestBlockChainForV2Engine(t *testing.T, numOfBlocks int, chainConfig *params.ChainConfig, numOfForkedBlocks int) (*BlockChain, *backends.SimulatedBackend, *types.Block, common.Address, func(account accounts.Account, hash []byte) ([]byte, error), *types.Block) { +func PrepareXDCTestBlockChainForV2Engine(t *testing.T, numOfBlocks int, chainConfig *params.ChainConfig, forkedBlockOptions *ForkedBlockOptions) (*BlockChain, *backends.SimulatedBackend, *types.Block, common.Address, func(account accounts.Account, hash []byte) ([]byte, error), *types.Block) { // Preparation var err error signer, signFn, err := backends.SimulateWalletAddressAndSignFn() @@ -308,22 +314,29 @@ func PrepareXDCTestBlockChainForV2Engine(t *testing.T, numOfBlocks int, chainCon blockCoinBase = signer.Hex() } roundNumber := int64(i) - chainConfig.XDPoS.V2.SwitchBlock.Int64() - block := CreateBlock(blockchain, chainConfig, currentBlock, i, roundNumber, blockCoinBase, signer, signFn, nil) + block := CreateBlock(blockchain, chainConfig, currentBlock, i, roundNumber, blockCoinBase, signer, signFn, nil, nil) err = blockchain.InsertBlock(block) if err != nil { t.Fatal(err) } // Produce forked block for the last numOfForkedBlocks'th blocks - if numOfForkedBlocks != 0 && i > numOfBlocks-numOfForkedBlocks { + if forkedBlockOptions != nil && forkedBlockOptions.numOfForkedBlocks != nil && i > numOfBlocks-*forkedBlockOptions.numOfForkedBlocks { if currentForkBlock == nil { currentForkBlock = currentBlock } forkedBlockCoinBase := fmt.Sprintf("0x222000000000000000000000000000000%03d", i) + var forkedBlockRoundNumber int64 + if forkedBlockOptions.forkedRoundDifference != nil { + if *forkedBlockOptions.forkedRoundDifference == 0 { + t.Fatal("forkedRoundDifference minimum is 1") + } + forkedBlockRoundNumber = roundNumber + int64(*forkedBlockOptions.forkedRoundDifference) + } else { + forkedBlockRoundNumber = roundNumber + int64(*forkedBlockOptions.numOfForkedBlocks) + } - forkedBlockRoundNumber := roundNumber + int64(numOfForkedBlocks) - - forkedBlock := CreateBlock(blockchain, chainConfig, currentForkBlock, i, forkedBlockRoundNumber, forkedBlockCoinBase, signer, signFn, nil) + forkedBlock := CreateBlock(blockchain, chainConfig, currentForkBlock, i, forkedBlockRoundNumber, forkedBlockCoinBase, signer, signFn, nil, forkedBlockOptions.signersKey) err = blockchain.InsertBlock(forkedBlock) if err != nil { @@ -388,7 +401,7 @@ func PrepareXDCTestBlockChainWithPenaltyForV2Engine(t *testing.T, numOfBlocks in } roundNumber := int64(i) - chainConfig.XDPoS.V2.SwitchBlock.Int64() // use signer itself as penalty - block := CreateBlock(blockchain, chainConfig, currentBlock, i, roundNumber, blockCoinBase, signer, signFn, signer[:]) + block := CreateBlock(blockchain, chainConfig, currentBlock, i, roundNumber, blockCoinBase, signer, signFn, signer[:], nil) err = blockchain.InsertBlock(block) if err != nil { @@ -406,13 +419,13 @@ func PrepareXDCTestBlockChainWithPenaltyForV2Engine(t *testing.T, numOfBlocks in return blockchain, backend, currentBlock, signer, signFn } -func CreateBlock(blockchain *BlockChain, chainConfig *params.ChainConfig, startingBlock *types.Block, blockNumber int, roundNumber int64, blockCoinBase string, signer common.Address, signFn func(account accounts.Account, hash []byte) ([]byte, error), penalties []byte) *types.Block { +func CreateBlock(blockchain *BlockChain, chainConfig *params.ChainConfig, startingBlock *types.Block, blockNumber int, roundNumber int64, blockCoinBase string, signer common.Address, signFn func(account accounts.Account, hash []byte) ([]byte, error), penalties []byte, signersKey []*ecdsa.PrivateKey) *types.Block { currentBlock := startingBlock merkleRoot := "35999dded35e8db12de7e6c1471eb9670c162eec616ecebbaf4fddd4676fb930" var header *types.Header if big.NewInt(int64(blockNumber)).Cmp(chainConfig.XDPoS.V2.SwitchBlock) == 1 { // Build engine v2 compatible extra data field - extraInBytes := generateV2Extra(roundNumber, currentBlock, signer, signFn) + extraInBytes := generateV2Extra(roundNumber, currentBlock, signer, signFn, signersKey) header = &types.Header{ Root: common.HexToHash(merkleRoot), @@ -618,7 +631,7 @@ func getMasternodesList(signer common.Address) []common.Address { return masternodes } -func generateV2Extra(roundNumber int64, currentBlock *types.Block, signer common.Address, signFn func(account accounts.Account, hash []byte) ([]byte, error)) []byte { +func generateV2Extra(roundNumber int64, currentBlock *types.Block, signer common.Address, signFn func(account accounts.Account, hash []byte) ([]byte, error), accKeys []*ecdsa.PrivateKey) []byte { var extraField utils.ExtraFields_v2 var round utils.Round err := utils.DecodeBytesExtraFields(currentBlock.Extra(), &extraField) @@ -643,12 +656,17 @@ func generateV2Extra(roundNumber int64, currentBlock *types.Block, signer common if err != nil { panic(fmt.Errorf("Error generate QC by creating signedHash: %v", err)) } - // Sign from acc 1, 2, 3 - acc1SignedHash := SignHashByPK(acc1Key, utils.VoteSigHash(voteForSign).Bytes()) - acc2SignedHash := SignHashByPK(acc2Key, utils.VoteSigHash(voteForSign).Bytes()) - acc3SignedHash := SignHashByPK(acc3Key, utils.VoteSigHash(voteForSign).Bytes()) var signatures []utils.Signature - signatures = append(signatures, acc1SignedHash, acc2SignedHash, acc3SignedHash, signedHash) + if len(accKeys) == 0 { + // Sign from acc 1, 2, 3 by default + accKeys = append(accKeys, acc1Key, acc2Key, acc3Key) + } + for _, acc := range accKeys { + h := SignHashByPK(acc, utils.VoteSigHash(voteForSign).Bytes()) + signatures = append(signatures, h) + } + signatures = append(signatures, signedHash) + quorumCert := &utils.QuorumCert{ ProposedBlockInfo: proposedBlockInfo, Signatures: signatures, diff --git a/consensus/tests/engine_v2_tests/initial_test.go b/consensus/tests/engine_v2_tests/initial_test.go index a1983061eb..5693762c24 100644 --- a/consensus/tests/engine_v2_tests/initial_test.go +++ b/consensus/tests/engine_v2_tests/initial_test.go @@ -13,7 +13,7 @@ import ( ) func TestInitialFirstV2Blcok(t *testing.T) { - blockchain, _, currentBlock, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 900, params.TestXDPoSMockChainConfig, 0) + blockchain, _, currentBlock, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 900, params.TestXDPoSMockChainConfig, nil) adaptor := blockchain.Engine().(*XDPoS.XDPoS) header := currentBlock.Header() @@ -33,7 +33,7 @@ func TestInitialFirstV2Blcok(t *testing.T) { expectedQuorumCert := &utils.QuorumCert{ ProposedBlockInfo: blockInfo, Signatures: nil, - GapNumber: blockchain.Config().XDPoS.V2.SwitchBlock.Uint64()-blockchain.Config().XDPoS.Gap, + GapNumber: blockchain.Config().XDPoS.V2.SwitchBlock.Uint64() - blockchain.Config().XDPoS.Gap, } assert.Equal(t, utils.Round(1), round) assert.Equal(t, expectedQuorumCert, highQC) @@ -55,12 +55,12 @@ func TestInitialFirstV2Blcok(t *testing.T) { func TestInitialOtherV2Block(t *testing.T) { // insert new block with new extra fields - blockchain, _, currentBlock, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, 900, params.TestXDPoSMockChainConfig, 0) + blockchain, _, currentBlock, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, 900, params.TestXDPoSMockChainConfig, nil) adaptor := blockchain.Engine().(*XDPoS.XDPoS) blockCoinBase := "0x111000000000000000000000000000000123" for blockNum := 901; blockNum <= 910; blockNum++ { - currentBlock = CreateBlock(blockchain, params.TestXDPoSMockChainConfig, currentBlock, blockNum, int64(blockNum-900), blockCoinBase, signer, signFn, nil) + currentBlock = CreateBlock(blockchain, params.TestXDPoSMockChainConfig, currentBlock, blockNum, int64(blockNum-900), blockCoinBase, signer, signFn, nil, nil) err := blockchain.InsertBlock(currentBlock) assert.Nil(t, err) } @@ -74,7 +74,7 @@ func TestInitialOtherV2Block(t *testing.T) { quorumCert := &utils.QuorumCert{ ProposedBlockInfo: blockInfo, Signatures: nil, // after decode it got default value []utils.Signature{} - GapNumber: 450, + GapNumber: 450, } extra := utils.ExtraFields_v2{ Round: 11, @@ -105,7 +105,7 @@ func TestInitialOtherV2Block(t *testing.T) { expectedQuorumCert := &utils.QuorumCert{ ProposedBlockInfo: blockInfo, Signatures: []utils.Signature{}, - GapNumber: blockchain.Config().XDPoS.V2.SwitchBlock.Uint64()-blockchain.Config().XDPoS.Gap, + GapNumber: blockchain.Config().XDPoS.V2.SwitchBlock.Uint64() - blockchain.Config().XDPoS.Gap, } assert.Equal(t, utils.Round(11), round) assert.Equal(t, expectedQuorumCert, highQC) @@ -118,7 +118,7 @@ func TestInitialOtherV2Block(t *testing.T) { func TestSnapshotShouldAlreadyCreatedByUpdateM1(t *testing.T) { // insert new block with new extra fields - blockchain, _, currentBlock, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 1800, params.TestXDPoSMockChainConfig, 0) + blockchain, _, currentBlock, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 1800, params.TestXDPoSMockChainConfig, nil) adaptor := blockchain.Engine().(*XDPoS.XDPoS) snap, err := adaptor.EngineV2.GetSnapshot(blockchain, currentBlock.Header()) diff --git a/consensus/tests/engine_v2_tests/mine_test.go b/consensus/tests/engine_v2_tests/mine_test.go index 902c1a3900..e4879c2cb7 100644 --- a/consensus/tests/engine_v2_tests/mine_test.go +++ b/consensus/tests/engine_v2_tests/mine_test.go @@ -17,7 +17,7 @@ import ( func TestYourTurnInitialV2(t *testing.T) { config := params.TestXDPoSMockChainConfig - blockchain, _, parentBlock, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, int(config.XDPoS.Epoch)-1, config, 0) + blockchain, _, parentBlock, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, int(config.XDPoS.Epoch)-1, config, nil) minePeriod := config.XDPoS.V2.MinePeriod adaptor := blockchain.Engine().(*XDPoS.XDPoS) @@ -62,7 +62,7 @@ func TestYourTurnInitialV2(t *testing.T) { func TestShouldMineOncePerRound(t *testing.T) { config := params.TestXDPoSMockChainConfig - blockchain, _, block910, signer, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 910, config, 0) + blockchain, _, block910, signer, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 910, config, nil) adaptor := blockchain.Engine().(*XDPoS.XDPoS) minePeriod := config.XDPoS.V2.MinePeriod @@ -77,7 +77,7 @@ func TestShouldMineOncePerRound(t *testing.T) { func TestUpdateMasterNodes(t *testing.T) { config := params.TestXDPoSMockChainConfig - blockchain, _, currentBlock, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, int(config.XDPoS.Epoch+config.XDPoS.Gap)-1, config, 0) + blockchain, _, currentBlock, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, int(config.XDPoS.Epoch+config.XDPoS.Gap)-1, config, nil) adaptor := blockchain.Engine().(*XDPoS.XDPoS) x := adaptor.EngineV2 snap, err := x.GetSnapshot(blockchain, currentBlock.Header()) @@ -102,7 +102,7 @@ func TestUpdateMasterNodes(t *testing.T) { Coinbase: common.HexToAddress(blockCoinbaseA), } - header.Extra = generateV2Extra(450, currentBlock, signer, signFn) + header.Extra = generateV2Extra(450, currentBlock, signer, signFn, nil) parentBlock, err := createBlockFromHeader(blockchain, header, []*types.Transaction{tx}, signer, signFn, config) assert.Nil(t, err) @@ -122,7 +122,7 @@ func TestUpdateMasterNodes(t *testing.T) { Coinbase: common.HexToAddress(blockCoinbase), } - header.Extra = generateV2Extra(int64(i), currentBlock, signer, signFn) + header.Extra = generateV2Extra(int64(i), currentBlock, signer, signFn, nil) block, err := createBlockFromHeader(blockchain, header, nil, signer, signFn, config) if err != nil { @@ -142,7 +142,7 @@ func TestUpdateMasterNodes(t *testing.T) { func TestPrepareFail(t *testing.T) { config := params.TestXDPoSMockChainConfig - blockchain, _, currentBlock, signer, _, _ := PrepareXDCTestBlockChainForV2Engine(t, int(config.XDPoS.Epoch), config, 0) + blockchain, _, currentBlock, signer, _, _ := PrepareXDCTestBlockChainForV2Engine(t, int(config.XDPoS.Epoch), config, nil) adaptor := blockchain.Engine().(*XDPoS.XDPoS) tstamp := time.Now().Unix() @@ -185,7 +185,7 @@ func TestPrepareFail(t *testing.T) { func TestPrepareHappyPath(t *testing.T) { config := params.TestXDPoSMockChainConfig - blockchain, _, currentBlock, signer, _, _ := PrepareXDCTestBlockChainForV2Engine(t, int(config.XDPoS.Epoch), config, 0) + blockchain, _, currentBlock, signer, _, _ := PrepareXDCTestBlockChainForV2Engine(t, int(config.XDPoS.Epoch), config, nil) adaptor := blockchain.Engine().(*XDPoS.XDPoS) // trigger initial _, err := adaptor.YourTurn(blockchain, currentBlock.Header(), signer) diff --git a/consensus/tests/engine_v2_tests/penalty_test.go b/consensus/tests/engine_v2_tests/penalty_test.go index c4182f3e06..faadaaa5b7 100644 --- a/consensus/tests/engine_v2_tests/penalty_test.go +++ b/consensus/tests/engine_v2_tests/penalty_test.go @@ -15,7 +15,7 @@ import ( func TestHookPenaltyV2Mining(t *testing.T) { config := params.TestXDPoSMockChainConfig - blockchain, _, _, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, int(config.XDPoS.Epoch)*7, config, 0) + blockchain, _, _, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, int(config.XDPoS.Epoch)*7, config, nil) adaptor := blockchain.Engine().(*XDPoS.XDPoS) hooks.AttachConsensusV2Hooks(adaptor, blockchain, config) assert.NotNil(t, adaptor.EngineV2.HookPenalty) diff --git a/consensus/tests/engine_v2_tests/proposed_block_test.go b/consensus/tests/engine_v2_tests/proposed_block_test.go index 8c765f2959..265a1a302c 100644 --- a/consensus/tests/engine_v2_tests/proposed_block_test.go +++ b/consensus/tests/engine_v2_tests/proposed_block_test.go @@ -14,7 +14,7 @@ import ( func TestShouldSendVoteMsgAndCommitGrandGrandParentBlock(t *testing.T) { // Block 901 is the first v2 block with round of 1 - blockchain, _, currentBlock, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, 901, params.TestXDPoSMockChainConfig, 0) + blockchain, _, currentBlock, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, 901, params.TestXDPoSMockChainConfig, nil) engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2 var extraField utils.ExtraFields_v2 @@ -44,7 +44,7 @@ func TestShouldSendVoteMsgAndCommitGrandGrandParentBlock(t *testing.T) { // Insert another Block, but it won't trigger commit blockNum := 902 blockCoinBase := fmt.Sprintf("0x111000000000000000000000000000000%03d", blockNum) - block902 := CreateBlock(blockchain, params.TestXDPoSMockChainConfig, currentBlock, blockNum, 2, blockCoinBase, signer, signFn, nil) + block902 := CreateBlock(blockchain, params.TestXDPoSMockChainConfig, currentBlock, blockNum, 2, blockCoinBase, signer, signFn, nil, nil) err = blockchain.InsertBlock(block902) assert.Nil(t, err) err = engineV2.ProposedBlockHandler(blockchain, block902.Header()) @@ -62,7 +62,7 @@ func TestShouldSendVoteMsgAndCommitGrandGrandParentBlock(t *testing.T) { // Insert one more Block, but still won't trigger commit blockNum = 903 blockCoinBase = fmt.Sprintf("0x111000000000000000000000000000000%03d", blockNum) - block903 := CreateBlock(blockchain, params.TestXDPoSMockChainConfig, block902, blockNum, 3, blockCoinBase, signer, signFn, nil) + block903 := CreateBlock(blockchain, params.TestXDPoSMockChainConfig, block902, blockNum, 3, blockCoinBase, signer, signFn, nil, nil) err = blockchain.InsertBlock(block903) assert.Nil(t, err) err = engineV2.ProposedBlockHandler(blockchain, block903.Header()) @@ -81,7 +81,7 @@ func TestShouldSendVoteMsgAndCommitGrandGrandParentBlock(t *testing.T) { // Insert one more Block, this time will trigger commit blockNum = 904 blockCoinBase = fmt.Sprintf("0x111000000000000000000000000000000%03d", blockNum) - block904 := CreateBlock(blockchain, params.TestXDPoSMockChainConfig, block903, blockNum, 4, blockCoinBase, signer, signFn, nil) + block904 := CreateBlock(blockchain, params.TestXDPoSMockChainConfig, block903, blockNum, 4, blockCoinBase, signer, signFn, nil, nil) err = blockchain.InsertBlock(block904) assert.Nil(t, err) err = engineV2.ProposedBlockHandler(blockchain, block904.Header()) @@ -102,7 +102,7 @@ func TestShouldSendVoteMsgAndCommitGrandGrandParentBlock(t *testing.T) { func TestShouldNotCommitIfRoundsNotContinousFor3Rounds(t *testing.T) { // Block 901 is the first v2 block with round of 1 - blockchain, _, currentBlock, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, 905, params.TestXDPoSMockChainConfig, 0) + blockchain, _, currentBlock, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, 905, params.TestXDPoSMockChainConfig, nil) engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2 var extraField utils.ExtraFields_v2 @@ -133,7 +133,7 @@ func TestShouldNotCommitIfRoundsNotContinousFor3Rounds(t *testing.T) { // Injecting new block which have gaps in the round number (Round 7 instead of 6) blockNum := 906 blockCoinBase := fmt.Sprintf("0x111000000000000000000000000000000%03d", blockNum) - block906 := CreateBlock(blockchain, params.TestXDPoSMockChainConfig, currentBlock, blockNum, 7, blockCoinBase, signer, signFn, nil) + block906 := CreateBlock(blockchain, params.TestXDPoSMockChainConfig, currentBlock, blockNum, 7, blockCoinBase, signer, signFn, nil, nil) err = blockchain.InsertBlock(block906) assert.Nil(t, err) err = engineV2.ProposedBlockHandler(blockchain, block906.Header()) @@ -155,7 +155,7 @@ func TestShouldNotCommitIfRoundsNotContinousFor3Rounds(t *testing.T) { blockNum = 907 blockCoinBase = fmt.Sprintf("0x111000000000000000000000000000000%03d", blockNum) - block907 := CreateBlock(blockchain, params.TestXDPoSMockChainConfig, block906, blockNum, 8, blockCoinBase, signer, signFn, nil) + block907 := CreateBlock(blockchain, params.TestXDPoSMockChainConfig, block906, blockNum, 8, blockCoinBase, signer, signFn, nil, nil) err = blockchain.InsertBlock(block907) assert.Nil(t, err) err = engineV2.ProposedBlockHandler(blockchain, block907.Header()) @@ -177,7 +177,7 @@ func TestShouldNotCommitIfRoundsNotContinousFor3Rounds(t *testing.T) { } func TestProposedBlockMessageHandlerSuccessfullyGenerateVote(t *testing.T) { - blockchain, _, currentBlock, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 906, params.TestXDPoSMockChainConfig, 0) + blockchain, _, currentBlock, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 906, params.TestXDPoSMockChainConfig, nil) engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2 // Set current round to 5 @@ -207,7 +207,7 @@ func TestProposedBlockMessageHandlerSuccessfullyGenerateVote(t *testing.T) { // Should not set new round if proposedBlockInfo round is less than currentRound. // NOTE: This shall not even happen because we have `verifyQC` before being passed into ProposedBlockHandler func TestShouldNotSetNewRound(t *testing.T) { - blockchain, _, currentBlock, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 906, params.TestXDPoSMockChainConfig, 0) + blockchain, _, currentBlock, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 906, params.TestXDPoSMockChainConfig, nil) engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2 // Set current round to 6 @@ -231,7 +231,7 @@ func TestShouldNotSetNewRound(t *testing.T) { } func TestShouldNotSendVoteMessageIfAlreadyVoteForThisRound(t *testing.T) { - blockchain, _, currentBlock, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 906, params.TestXDPoSMockChainConfig, 0) + blockchain, _, currentBlock, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 906, params.TestXDPoSMockChainConfig, nil) engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2 // Set current round to 5 @@ -269,7 +269,7 @@ func TestShouldNotSendVoteMessageIfAlreadyVoteForThisRound(t *testing.T) { } func TestShouldNotSendVoteMsgIfBlockInfoRoundNotEqualCurrentRound(t *testing.T) { - blockchain, _, currentBlock, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 906, params.TestXDPoSMockChainConfig, 0) + blockchain, _, currentBlock, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 906, params.TestXDPoSMockChainConfig, nil) engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2 // Set current round to 8 @@ -303,7 +303,9 @@ func TestShouldNotSendVoteMsgIfBlockInfoRoundNotEqualCurrentRound(t *testing.T) */ func TestShouldNotSendVoteMsgIfBlockNotExtendedFromAncestor(t *testing.T) { // Block number 905, 906 have forks and forkedBlock is the 906th - blockchain, _, currentBlock, _, _, forkedBlock := PrepareXDCTestBlockChainForV2Engine(t, 906, params.TestXDPoSMockChainConfig, 3) + var numOfForks = new(int) + *numOfForks = 3 + blockchain, _, currentBlock, _, _, forkedBlock := PrepareXDCTestBlockChainForV2Engine(t, 906, params.TestXDPoSMockChainConfig, &ForkedBlockOptions{numOfForkedBlocks: numOfForks}) engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2 var extraField utils.ExtraFields_v2 @@ -340,7 +342,7 @@ func TestShouldNotSendVoteMsgIfBlockNotExtendedFromAncestor(t *testing.T) { func TestShouldSendVoteMsg(t *testing.T) { // Block number 15, 16 have forks and forkedBlock is the 16th - blockchain, _, _, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 903, params.TestXDPoSMockChainConfig, 0) + blockchain, _, _, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 903, params.TestXDPoSMockChainConfig, nil) engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2 // Block 901 is first v2 block @@ -358,7 +360,7 @@ func TestShouldSendVoteMsg(t *testing.T) { } func TestProposedBlockMessageHandlerNotGenerateVoteIfSignerNotInMNlist(t *testing.T) { - blockchain, _, currentBlock, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 906, params.TestXDPoSMockChainConfig, 0) + blockchain, _, currentBlock, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 906, params.TestXDPoSMockChainConfig, nil) engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2 differentSigner, differentSignFn, err := backends.SimulateWalletAddressAndSignFn() assert.Nil(t, err) diff --git a/consensus/tests/engine_v2_tests/reward_test.go b/consensus/tests/engine_v2_tests/reward_test.go index b7221cf0af..55dc4104ca 100644 --- a/consensus/tests/engine_v2_tests/reward_test.go +++ b/consensus/tests/engine_v2_tests/reward_test.go @@ -25,7 +25,7 @@ func TestHookRewardV2(t *testing.T) { // set switch to 1800, so that it covers 901-1799, 1800-2700 two epochs config.XDPoS.V2.SwitchBlock.SetUint64(1800) - blockchain, _, _, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, int(config.XDPoS.Epoch)*5, &config, 0) + blockchain, _, _, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, int(config.XDPoS.Epoch)*5, &config, nil) adaptor := blockchain.Engine().(*XDPoS.XDPoS) hooks.AttachConsensusV2Hooks(adaptor, blockchain, &config) @@ -106,7 +106,7 @@ func TestHookRewardV2SplitReward(t *testing.T) { // set switch to 1800, so that it covers 901-1799, 1800-2700 two epochs config.XDPoS.V2.SwitchBlock.SetUint64(1800) - blockchain, _, _, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, int(config.XDPoS.Epoch)*3, &config, 0) + blockchain, _, _, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, int(config.XDPoS.Epoch)*3, &config, nil) adaptor := blockchain.Engine().(*XDPoS.XDPoS) hooks.AttachConsensusV2Hooks(adaptor, blockchain, &config) diff --git a/consensus/tests/engine_v2_tests/sync_info_test.go b/consensus/tests/engine_v2_tests/sync_info_test.go index 7f5e69fc76..3e1b095d84 100644 --- a/consensus/tests/engine_v2_tests/sync_info_test.go +++ b/consensus/tests/engine_v2_tests/sync_info_test.go @@ -12,7 +12,7 @@ import ( func TestSyncInfoShouldSuccessfullyUpdateByQC(t *testing.T) { // Block 901 is the first v2 block with starting round of 0 - blockchain, _, currentBlock, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 905, params.TestXDPoSMockChainConfig, 0) + blockchain, _, currentBlock, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 905, params.TestXDPoSMockChainConfig, nil) engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2 var extraField utils.ExtraFields_v2 @@ -43,7 +43,7 @@ func TestSyncInfoShouldSuccessfullyUpdateByQC(t *testing.T) { func TestSyncInfoShouldSuccessfullyUpdateByTC(t *testing.T) { // Block 901 is the first v2 block with starting round of 0 - blockchain, _, currentBlock, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 905, params.TestXDPoSMockChainConfig, 0) + blockchain, _, currentBlock, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 905, params.TestXDPoSMockChainConfig, nil) engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2 var extraField utils.ExtraFields_v2 @@ -72,7 +72,7 @@ func TestSyncInfoShouldSuccessfullyUpdateByTC(t *testing.T) { } func TestSkipVerifySyncInfoIfBothQcTcNotQualified(t *testing.T) { - blockchain, _, _, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 905, params.TestXDPoSMockChainConfig, 0) + blockchain, _, _, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 905, params.TestXDPoSMockChainConfig, nil) engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2 // Make the Highest QC in syncInfo point to an old block to simulate it's no longer qualified diff --git a/consensus/tests/engine_v2_tests/timeout_test.go b/consensus/tests/engine_v2_tests/timeout_test.go index 8663945662..5497b3b2ea 100644 --- a/consensus/tests/engine_v2_tests/timeout_test.go +++ b/consensus/tests/engine_v2_tests/timeout_test.go @@ -16,7 +16,7 @@ import ( ) func TestCountdownTimeoutToSendTimeoutMessage(t *testing.T) { - blockchain, _, _, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 901, params.TestXDPoSMockChainConfig, 0) + blockchain, _, _, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 901, params.TestXDPoSMockChainConfig, nil) engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2 timeoutMsg := <-engineV2.BroadcastCh @@ -28,7 +28,7 @@ func TestCountdownTimeoutToSendTimeoutMessage(t *testing.T) { } func TestCountdownTimeoutNotToSendTimeoutMessageIfNotInMasternodeList(t *testing.T) { - blockchain, _, _, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 901, params.TestXDPoSMockChainConfig, 0) + blockchain, _, _, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 901, params.TestXDPoSMockChainConfig, nil) engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2 differentSigner, differentSignFn, err := backends.SimulateWalletAddressAndSignFn() @@ -46,7 +46,7 @@ func TestCountdownTimeoutNotToSendTimeoutMessageIfNotInMasternodeList(t *testing } func TestSyncInfoAfterReachTimeoutSnycThreadhold(t *testing.T) { - blockchain, _, _, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 901, params.TestXDPoSMockChainConfig, 0) + blockchain, _, _, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 901, params.TestXDPoSMockChainConfig, nil) engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2 engineV2.SetNewRoundFaker(blockchain, 1, true) @@ -85,7 +85,7 @@ func TestSyncInfoAfterReachTimeoutSnycThreadhold(t *testing.T) { // Timeout handler func TestTimeoutMessageHandlerSuccessfullyGenerateTCandSyncInfo(t *testing.T) { - blockchain, _, _, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 11, params.TestXDPoSMockChainConfig, 0) + blockchain, _, _, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 11, params.TestXDPoSMockChainConfig, nil) engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2 // Set round to 1 @@ -153,7 +153,7 @@ func TestTimeoutMessageHandlerSuccessfullyGenerateTCandSyncInfo(t *testing.T) { } func TestThrowErrorIfTimeoutMsgRoundNotEqualToCurrentRound(t *testing.T) { - blockchain, _, _, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 11, params.TestXDPoSMockChainConfig, 0) + blockchain, _, _, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 11, params.TestXDPoSMockChainConfig, nil) engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2 // Set round to 3 @@ -177,7 +177,7 @@ func TestThrowErrorIfTimeoutMsgRoundNotEqualToCurrentRound(t *testing.T) { } func TestShouldVerifyTimeoutMessageForFirstV2Block(t *testing.T) { - blockchain, _, _, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, 901, params.TestXDPoSMockChainConfig, 0) + blockchain, _, _, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, 901, params.TestXDPoSMockChainConfig, nil) engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2 signedHash, err := signFn(accounts.Account{Address: signer}, utils.TimeoutSigHash(&utils.TimeoutForSign{ @@ -212,7 +212,7 @@ func TestShouldVerifyTimeoutMessageForFirstV2Block(t *testing.T) { } func TestShouldVerifyTimeoutMessage(t *testing.T) { - blockchain, _, _, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 2251, params.TestXDPoSMockChainConfig, 0) + blockchain, _, _, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 2251, params.TestXDPoSMockChainConfig, nil) engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2 signedHash := SignHashByPK(acc1Key, utils.TimeoutSigHash(&utils.TimeoutForSign{ @@ -231,7 +231,7 @@ func TestShouldVerifyTimeoutMessage(t *testing.T) { } func TestTimeoutPoolKeeyGoodHygiene(t *testing.T) { - blockchain, _, _, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, 905, params.TestXDPoSMockChainConfig, 0) + blockchain, _, _, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, 905, params.TestXDPoSMockChainConfig, nil) engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2 // Set round to 5 diff --git a/consensus/tests/engine_v2_tests/verify_blockinfo_test.go b/consensus/tests/engine_v2_tests/verify_blockinfo_test.go index 57155bd0e8..36bf0d7dec 100644 --- a/consensus/tests/engine_v2_tests/verify_blockinfo_test.go +++ b/consensus/tests/engine_v2_tests/verify_blockinfo_test.go @@ -12,7 +12,7 @@ import ( func TestShouldVerifyBlockInfo(t *testing.T) { // Block 901 is the first v2 block with round of 1 - blockchain, _, currentBlock, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, 901, params.TestXDPoSMockChainConfig, 0) + blockchain, _, currentBlock, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, 901, params.TestXDPoSMockChainConfig, nil) engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2 blockInfo := &utils.BlockInfo{ @@ -26,7 +26,7 @@ func TestShouldVerifyBlockInfo(t *testing.T) { // Insert another Block, but it won't trigger commit blockNum := 902 blockCoinBase := fmt.Sprintf("0x111000000000000000000000000000000%03d", blockNum) - block902 := CreateBlock(blockchain, params.TestXDPoSMockChainConfig, currentBlock, blockNum, 2, blockCoinBase, signer, signFn, nil) + block902 := CreateBlock(blockchain, params.TestXDPoSMockChainConfig, currentBlock, blockNum, 2, blockCoinBase, signer, signFn, nil, nil) err = blockchain.InsertBlock(block902) assert.Nil(t, err) diff --git a/consensus/tests/engine_v2_tests/verify_header_test.go b/consensus/tests/engine_v2_tests/verify_header_test.go index 3c6a1e7fc3..e8bec43968 100644 --- a/consensus/tests/engine_v2_tests/verify_header_test.go +++ b/consensus/tests/engine_v2_tests/verify_header_test.go @@ -30,7 +30,7 @@ func TestShouldVerifyBlock(t *testing.T) { // Skip the mining time validation by set mine time to 0 config.XDPoS.V2.MinePeriod = 0 // Block 901 is the first v2 block with round of 1 - blockchain, _, _, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, 910, &config, 0) + blockchain, _, _, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, 910, &config, nil) adaptor := blockchain.Engine().(*XDPoS.XDPoS) // Happy path @@ -177,7 +177,7 @@ func TestShouldFailIfNotEnoughQCSignatures(t *testing.T) { // Skip the mining time validation by set mine time to 0 config.XDPoS.V2.MinePeriod = 0 // Block 901 is the first v2 block with round of 1 - blockchain, _, currentBlock, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, 902, &config, 0) + blockchain, _, currentBlock, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, 902, &config, nil) adaptor := blockchain.Engine().(*XDPoS.XDPoS) parentBlock := blockchain.GetBlockByNumber(901) @@ -230,7 +230,7 @@ func TestShouldVerifyHeaders(t *testing.T) { // Skip the mining time validation by set mine time to 0 config.XDPoS.V2.MinePeriod = 0 // Block 901 is the first v2 block with round of 1 - blockchain, _, _, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 910, &config, 0) + blockchain, _, _, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 910, &config, nil) adaptor := blockchain.Engine().(*XDPoS.XDPoS) // Happy path @@ -271,7 +271,7 @@ func TestShouldVerifyHeadersEvenIfParentsNotYetWrittenIntoDB(t *testing.T) { // Skip the mining time validation by set mine time to 0 config.XDPoS.V2.MinePeriod = 0 // Block 901 is the first v2 block with round of 1 - blockchain, _, block910, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, 910, &config, 0) + blockchain, _, block910, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, 910, &config, nil) adaptor := blockchain.Engine().(*XDPoS.XDPoS) var headersTobeVerified []*types.Header @@ -279,12 +279,12 @@ func TestShouldVerifyHeadersEvenIfParentsNotYetWrittenIntoDB(t *testing.T) { // Create block 911 but don't write into DB blockNumber := 911 roundNumber := int64(blockNumber) - config.XDPoS.V2.SwitchBlock.Int64() - block911 := CreateBlock(blockchain, &config, block910, blockNumber, roundNumber, signer.Hex(), signer, signFn, nil) + block911 := CreateBlock(blockchain, &config, block910, blockNumber, roundNumber, signer.Hex(), signer, signFn, nil, nil) // Create block 912 and not write into DB as well blockNumber = 912 roundNumber = int64(blockNumber) - config.XDPoS.V2.SwitchBlock.Int64() - block912 := CreateBlock(blockchain, &config, block911, blockNumber, roundNumber, signer.Hex(), signer, signFn, nil) + block912 := CreateBlock(blockchain, &config, block911, blockNumber, roundNumber, signer.Hex(), signer, signFn, nil, nil) headersTobeVerified = append(headersTobeVerified, block910.Header(), block911.Header(), block912.Header()) // Randomly set full verify diff --git a/consensus/tests/engine_v2_tests/vote_test.go b/consensus/tests/engine_v2_tests/vote_test.go index e1da4ee968..59f047c9f2 100644 --- a/consensus/tests/engine_v2_tests/vote_test.go +++ b/consensus/tests/engine_v2_tests/vote_test.go @@ -19,7 +19,7 @@ import ( // VoteHandler func TestVoteMessageHandlerSuccessfullyGeneratedAndProcessQCForFistV2Round(t *testing.T) { - blockchain, _, currentBlock, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, 901, params.TestXDPoSMockChainConfig, 0) + blockchain, _, currentBlock, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, 901, params.TestXDPoSMockChainConfig, nil) engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2 blockInfo := &utils.BlockInfo{ @@ -88,7 +88,7 @@ func TestVoteMessageHandlerSuccessfullyGeneratedAndProcessQCForFistV2Round(t *te } func TestVoteMessageHandlerSuccessfullyGeneratedAndProcessQC(t *testing.T) { - blockchain, _, currentBlock, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, 905, params.TestXDPoSMockChainConfig, 0) + blockchain, _, currentBlock, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, 905, params.TestXDPoSMockChainConfig, nil) engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2 blockInfo := &utils.BlockInfo{ @@ -176,7 +176,7 @@ func TestVoteMessageHandlerSuccessfullyGeneratedAndProcessQC(t *testing.T) { } func TestThrowErrorIfVoteMsgRoundIsMoreThanOneRoundAwayFromCurrentRound(t *testing.T) { - blockchain, _, _, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 905, params.TestXDPoSMockChainConfig, 0) + blockchain, _, _, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 905, params.TestXDPoSMockChainConfig, nil) engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2 blockInfo := &utils.BlockInfo{ @@ -211,7 +211,7 @@ func TestThrowErrorIfVoteMsgRoundIsMoreThanOneRoundAwayFromCurrentRound(t *testi } func TestProcessVoteMsgThenTimeoutMsg(t *testing.T) { - blockchain, _, currentBlock, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 905, params.TestXDPoSMockChainConfig, 0) + blockchain, _, currentBlock, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 905, params.TestXDPoSMockChainConfig, nil) engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2 // Set round to 5 @@ -331,13 +331,13 @@ func TestProcessVoteMsgThenTimeoutMsg(t *testing.T) { } func TestVoteMessageShallNotThrowErrorIfBlockNotYetExist(t *testing.T) { - blockchain, _, currentBlock, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, 905, params.TestXDPoSMockChainConfig, 0) + blockchain, _, currentBlock, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, 905, params.TestXDPoSMockChainConfig, nil) engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2 // Create a new block but don't inject it into the chain yet blockNum := 906 blockCoinBase := fmt.Sprintf("0x111000000000000000000000000000000%03d", blockNum) - block := CreateBlock(blockchain, params.TestXDPoSMockChainConfig, currentBlock, blockNum, 6, blockCoinBase, signer, signFn, nil) + block := CreateBlock(blockchain, params.TestXDPoSMockChainConfig, currentBlock, blockNum, 6, blockCoinBase, signer, signFn, nil, nil) blockInfo := &utils.BlockInfo{ Hash: block.Header().Hash(), @@ -411,7 +411,7 @@ func TestVoteMessageShallNotThrowErrorIfBlockNotYetExist(t *testing.T) { } func TestProcessVoteMsgFailIfVerifyBlockInfoFail(t *testing.T) { - blockchain, _, currentBlock, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 905, params.TestXDPoSMockChainConfig, 0) + blockchain, _, currentBlock, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 905, params.TestXDPoSMockChainConfig, nil) engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2 // Set round to 5 @@ -467,7 +467,7 @@ func TestProcessVoteMsgFailIfVerifyBlockInfoFail(t *testing.T) { } func TestVerifyVoteMsg(t *testing.T) { - blockchain, _, currentBlock, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, 915, params.TestXDPoSMockChainConfig, 0) + blockchain, _, currentBlock, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, 915, params.TestXDPoSMockChainConfig, nil) engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2 blockInfo := &utils.BlockInfo{ @@ -511,7 +511,7 @@ func TestVerifyVoteMsg(t *testing.T) { } func TestVoteMessageHandlerWrongGapNumber(t *testing.T) { - blockchain, _, currentBlock, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, 905, params.TestXDPoSMockChainConfig, 0) + blockchain, _, currentBlock, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, 905, params.TestXDPoSMockChainConfig, nil) engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2 blockInfo := &utils.BlockInfo{ @@ -562,7 +562,7 @@ func TestVoteMessageHandlerWrongGapNumber(t *testing.T) { } func TestVotePoolKeepGoodHygiene(t *testing.T) { - blockchain, _, currentBlock, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, 905, params.TestXDPoSMockChainConfig, 0) + blockchain, _, currentBlock, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, 905, params.TestXDPoSMockChainConfig, nil) engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2 blockInfo := &utils.BlockInfo{