From c21531674421569d6a65415628c8ddb7e16de18f Mon Sep 17 00:00:00 2001 From: Liam Date: Thu, 16 Nov 2023 23:59:19 +1100 Subject: [PATCH] Make masternode dynamic (#356) * make masternode dynamic * fix all the tests * remove cmt * fix test again --- cmd/puppeth/wizard_genesis.go | 4 +- consensus/XDPoS/engines/engine_v2/engine.go | 26 +------- .../XDPoS/engines/engine_v2/epochSwitch.go | 7 +- .../XDPoS/engines/engine_v2/forensics.go | 16 ++--- consensus/XDPoS/engines/engine_v2/timeout.go | 26 ++++++-- .../XDPoS/engines/engine_v2/verifyHeader.go | 5 +- consensus/XDPoS/engines/engine_v2/vote.go | 18 +++++- consensus/XDPoS/utils/errors.go | 4 +- .../tests/engine_v2_tests/forensics_test.go | 13 +++- .../tests/engine_v2_tests/timeout_test.go | 36 ++++++----- .../engine_v2_tests/verify_header_test.go | 3 +- consensus/tests/engine_v2_tests/vote_test.go | 64 +++++++++++++++---- core/types/consensus_v2.go | 1 + params/config.go | 22 +++---- params/config_test.go | 12 ++-- 15 files changed, 160 insertions(+), 97 deletions(-) diff --git a/cmd/puppeth/wizard_genesis.go b/cmd/puppeth/wizard_genesis.go index 510c99a23d..8e72fea018 100644 --- a/cmd/puppeth/wizard_genesis.go +++ b/cmd/puppeth/wizard_genesis.go @@ -145,8 +145,8 @@ func (w *wizard) makeGenesis() { genesis.Config.XDPoS.V2.CurrentConfig.TimeoutSyncThreshold = w.readDefaultInt(3) fmt.Println() - fmt.Printf("How many v2 vote collection to generate a QC, should be two thirds of masternodes? (default = %d)\n", common.MaxMasternodesV2/3*2+1) - genesis.Config.XDPoS.V2.CurrentConfig.CertThreshold = w.readDefaultInt(common.MaxMasternodesV2)/3*2 + 1 + fmt.Printf("How many v2 vote collection to generate a QC, should be two thirds of masternodes? (default = %f)\n", 0.666) + genesis.Config.XDPoS.V2.CurrentConfig.CertThreshold = w.readDefaultFloat(0.666) genesis.Config.XDPoS.V2.AllConfigs[0] = genesis.Config.XDPoS.V2.CurrentConfig diff --git a/consensus/XDPoS/engines/engine_v2/engine.go b/consensus/XDPoS/engines/engine_v2/engine.go index 7c20c50059..86f22905d0 100644 --- a/consensus/XDPoS/engines/engine_v2/engine.go +++ b/consensus/XDPoS/engines/engine_v2/engine.go @@ -772,17 +772,6 @@ func (x *XDPoS_v2) VerifyBlockInfo(blockChainReader consensus.ChainReader, block } func (x *XDPoS_v2) verifyQC(blockChainReader consensus.ChainReader, quorumCert *types.QuorumCert, parentHeader *types.Header) error { - /* - 1. Check if num of QC signatures is >= x.config.v2.CertThreshold - 2. Get epoch master node list by hash - 3. Verify signer signatures: (List of signatures) - - Use ecRecover to get the public key - - Use the above public key to find out the xdc address - - Use the above xdc address to check against the master node list from step 1(For the received QC epoch) - 4. Verify gapNumber = epochSwitchNumber - epochSwitchNumber%Epoch - Gap - 5. Verify blockInfo - */ - if quorumCert == nil { log.Warn("[verifyQC] QC is Nil") return utils.ErrInvalidQC @@ -803,9 +792,9 @@ func (x *XDPoS_v2) verifyQC(blockChainReader consensus.ChainReader, quorumCert * qcRound := quorumCert.ProposedBlockInfo.Round certThreshold := x.config.V2.Config(uint64(qcRound)).CertThreshold - if (qcRound > 0) && (signatures == nil || (len(signatures) < certThreshold)) { + if (qcRound > 0) && (signatures == nil || float64(len(signatures)) < float64(epochInfo.MasternodesLen)*certThreshold) { //First V2 Block QC, QC Signatures is initial nil - log.Warn("[verifyHeader] Invalid QC Signature is nil or less then config", "QC", quorumCert, "QCNumber", quorumCert.ProposedBlockInfo.Number, "Signatures len", len(signatures), "CertThreshold", certThreshold) + log.Warn("[verifyHeader] Invalid QC Signature is nil or less then config", "QC", quorumCert, "QCNumber", quorumCert.ProposedBlockInfo.Number, "Signatures len", len(signatures), "CertThreshold", float64(epochInfo.MasternodesLen)*certThreshold) return utils.ErrInvalidQCSignatures } start := time.Now() @@ -1045,17 +1034,8 @@ func (x *XDPoS_v2) calcMasternodes(chain consensus.ChainReader, blockNum *big.In if len(masternodes) > maxMasternodes { masternodes = masternodes[:maxMasternodes] } - if len(masternodes) < x.config.V2.CurrentConfig.CertThreshold { - log.Warn("[calcMasternodes] Current epoch masternodes less than threshold", "number", blockNum, "masternodes", len(masternodes), "threshold", x.config.V2.CurrentConfig.CertThreshold) - for i, a := range masternodes { - log.Warn("final masternode", "i", i, "addr", a) - } - for i, a := range penalties { - log.Warn("penalty", "i", i, "addr", a) - } - } - return masternodes, penalties, nil + return masternodes, penalties, nil } // Given hash, get master node from the epoch switch block of the epoch diff --git a/consensus/XDPoS/engines/engine_v2/epochSwitch.go b/consensus/XDPoS/engines/engine_v2/epochSwitch.go index 2f699b1229..5d726b41a9 100644 --- a/consensus/XDPoS/engines/engine_v2/epochSwitch.go +++ b/consensus/XDPoS/engines/engine_v2/epochSwitch.go @@ -72,9 +72,10 @@ func (x *XDPoS_v2) getEpochSwitchInfo(chain consensus.ChainReader, header *types } epochSwitchInfo := &types.EpochSwitchInfo{ - Penalties: penalties, - Standbynodes: standbynodes, - Masternodes: masternodes, + Penalties: penalties, + Standbynodes: standbynodes, + Masternodes: masternodes, + MasternodesLen: len(masternodes), EpochSwitchBlockInfo: &types.BlockInfo{ Hash: hash, Number: h.Number, diff --git a/consensus/XDPoS/engines/engine_v2/forensics.go b/consensus/XDPoS/engines/engine_v2/forensics.go index bf9f0ddfb2..1c4542ab0e 100644 --- a/consensus/XDPoS/engines/engine_v2/forensics.go +++ b/consensus/XDPoS/engines/engine_v2/forensics.go @@ -80,10 +80,10 @@ func (f *Forensics) SetCommittedQCs(headers []types.Header, incomingQC types.Quo } /* - 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 +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, engine *XDPoS_v2, incomingQC types.QuorumCert) error { log.Debug("Received a QC in forensics", "QC", incomingQC) @@ -387,10 +387,10 @@ func generateVoteEquivocationId(signer common.Address, round1, round2 types.Roun } /* - Entry point for processing vote equivocation. - Triggered once handle vote 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/99516417/Vote+Equivocation+detection+specification +Entry point for processing vote equivocation. +Triggered once handle vote 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/99516417/Vote+Equivocation+detection+specification */ func (f *Forensics) ProcessVoteEquivocation(chain consensus.ChainReader, engine *XDPoS_v2, incomingVote *types.Vote) error { log.Debug("Received a vote in forensics", "vote", incomingVote) diff --git a/consensus/XDPoS/engines/engine_v2/timeout.go b/consensus/XDPoS/engines/engine_v2/timeout.go index 9df7b3e82a..bb9f54aeb0 100644 --- a/consensus/XDPoS/engines/engine_v2/timeout.go +++ b/consensus/XDPoS/engines/engine_v2/timeout.go @@ -27,9 +27,15 @@ func (x *XDPoS_v2) timeoutHandler(blockChainReader consensus.ChainReader, timeou numberOfTimeoutsInPool, pooledTimeouts := x.timeoutPool.Add(timeout) log.Debug("[timeoutHandler] collect timeout", "number", numberOfTimeoutsInPool) + epochInfo, err := x.getEpochSwitchInfo(blockChainReader, blockChainReader.CurrentHeader(), blockChainReader.CurrentHeader().Hash()) + if err != nil { + log.Error("[timeoutHandler] Error when getting epoch switch Info", "error", err) + return fmt.Errorf("fail on timeoutHandler due to failure in getting epoch switch info, %s", err) + } + // Threshold reached certThreshold := x.config.V2.Config(uint64(x.currentRound)).CertThreshold - isThresholdReached := numberOfTimeoutsInPool >= certThreshold + isThresholdReached := float64(numberOfTimeoutsInPool) >= float64(epochInfo.MasternodesLen)*certThreshold if isThresholdReached { log.Info(fmt.Sprintf("Timeout pool threashold reached: %v, number of items in the pool: %v", isThresholdReached, numberOfTimeoutsInPool)) err := x.onTimeoutPoolThresholdReached(blockChainReader, pooledTimeouts, timeout, timeout.GapNumber) @@ -89,11 +95,11 @@ func (x *XDPoS_v2) verifyTC(chain consensus.ChainReader, timeoutCert *types.Time snap, err := x.getSnapshot(chain, timeoutCert.GapNumber, true) if err != nil { log.Error("[verifyTC] Fail to get snapshot when verifying TC!", "TCGapNumber", timeoutCert.GapNumber) - return fmt.Errorf("[verifyTC] Unable to get snapshot") + return fmt.Errorf("[verifyTC] Unable to get snapshot, %s", err) } if snap == nil || len(snap.NextEpochMasterNodes) == 0 { log.Error("[verifyTC] Something wrong with the snapshot from gapNumber", "messageGapNumber", timeoutCert.GapNumber, "snapshot", snap) - return fmt.Errorf("Empty master node lists from snapshot") + return fmt.Errorf("empty master node lists from snapshot") } signatures, duplicates := UniqueSignatures(timeoutCert.Signatures) @@ -103,9 +109,15 @@ func (x *XDPoS_v2) verifyTC(chain consensus.ChainReader, timeoutCert *types.Time } } + epochInfo, err := x.getEpochSwitchInfo(chain, chain.CurrentHeader(), chain.CurrentHeader().Hash()) + if err != nil { + log.Error("[verifyTC] Error when getting epoch switch Info", "error", err) + return fmt.Errorf("fail on verifyTC due to failure in getting epoch switch info, %s", err) + } + certThreshold := x.config.V2.Config(uint64(timeoutCert.Round)).CertThreshold - if len(signatures) < certThreshold { - log.Warn("[verifyTC] Invalid TC Signature is nil or empty", "timeoutCert.Round", timeoutCert.Round, "timeoutCert.GapNumber", timeoutCert.GapNumber, "Signatures len", len(timeoutCert.Signatures), "CertThreshold", certThreshold) + if float64(len(signatures)) < float64(epochInfo.MasternodesLen)*certThreshold { + log.Warn("[verifyTC] Invalid TC Signature is nil or empty", "timeoutCert.Round", timeoutCert.Round, "timeoutCert.GapNumber", timeoutCert.GapNumber, "Signatures len", len(timeoutCert.Signatures), "CertThreshold", float64(epochInfo.MasternodesLen)*certThreshold) return utils.ErrInvalidTCSignatures } @@ -124,12 +136,12 @@ func (x *XDPoS_v2) verifyTC(chain consensus.ChainReader, timeoutCert *types.Time verified, _, err := x.verifyMsgSignature(signedTimeoutObj, sig, snap.NextEpochMasterNodes) if err != nil { log.Error("[verifyTC] Error while verfying TC message signatures", "timeoutCert.Round", timeoutCert.Round, "timeoutCert.GapNumber", timeoutCert.GapNumber, "Signatures len", len(signatures), "Error", err) - haveError = fmt.Errorf("Error while verfying TC message signatures") + haveError = fmt.Errorf("error while verfying TC message signatures, %s", err) return } if !verified { log.Warn("[verifyTC] Signature not verified doing TC verification", "timeoutCert.Round", timeoutCert.Round, "timeoutCert.GapNumber", timeoutCert.GapNumber, "Signatures len", len(signatures)) - haveError = fmt.Errorf("Fail to verify TC due to signature mis-match") + haveError = fmt.Errorf("fail to verify TC due to signature mis-match") return } }(signature) diff --git a/consensus/XDPoS/engines/engine_v2/verifyHeader.go b/consensus/XDPoS/engines/engine_v2/verifyHeader.go index f9cb242f62..1655a34d94 100644 --- a/consensus/XDPoS/engines/engine_v2/verifyHeader.go +++ b/consensus/XDPoS/engines/engine_v2/verifyHeader.go @@ -161,7 +161,6 @@ func (x *XDPoS_v2) verifyHeader(chain consensus.ChainReader, header *types.Heade return err } - // Check its validator verified, validatorAddress, err := x.verifyMsgSignature(sigHash(header), header.Validator, masterNodes) if err != nil { for index, mn := range masterNodes { @@ -171,11 +170,11 @@ func (x *XDPoS_v2) verifyHeader(chain consensus.ChainReader, header *types.Heade return err } if !verified { - log.Warn("[verifyHeader] Fail to verify the block validator as the validator address not within the masternode list", header.Number, "Hash", header.Hash().Hex(), "validatorAddress", validatorAddress.Hex()) + log.Warn("[verifyHeader] Fail to verify the block validator as the validator address not within the masternode list", "BlockNumber", header.Number, "Hash", header.Hash().Hex(), "validatorAddress", validatorAddress.Hex()) return utils.ErrValidatorNotWithinMasternodes } if validatorAddress != header.Coinbase { - log.Warn("[verifyHeader] Header validator and coinbase address not match", header.Number, "Hash", header.Hash().Hex(), "validatorAddress", validatorAddress.Hex(), "coinbase", header.Coinbase.Hex()) + log.Warn("[verifyHeader] Header validator and coinbase address not match", "BlockNumber", header.Number, "Hash", header.Hash().Hex(), "validatorAddress", validatorAddress.Hex(), "coinbase", header.Coinbase.Hex()) return utils.ErrCoinbaseAndValidatorMismatch } // Check the proposer is the leader diff --git a/consensus/XDPoS/engines/engine_v2/vote.go b/consensus/XDPoS/engines/engine_v2/vote.go index b2aec7db45..d29a647e1f 100644 --- a/consensus/XDPoS/engines/engine_v2/vote.go +++ b/consensus/XDPoS/engines/engine_v2/vote.go @@ -75,8 +75,14 @@ func (x *XDPoS_v2) voteHandler(chain consensus.ChainReader, voteMsg *types.Vote) go x.ForensicsProcessor.DetectEquivocationInVotePool(voteMsg, x.votePool) go x.ForensicsProcessor.ProcessVoteEquivocation(chain, x, voteMsg) + epochInfo, err := x.getEpochSwitchInfo(chain, chain.CurrentHeader(), chain.CurrentHeader().Hash()) + if err != nil { + log.Error("[voteHandler] Error when getting epoch switch Info", "error", err) + return fmt.Errorf("Fail on voteHandler due to failure in getting epoch switch info") + } + certThreshold := x.config.V2.Config(uint64(voteMsg.ProposedBlockInfo.Round)).CertThreshold - thresholdReached := numberOfVotesInPool >= certThreshold + thresholdReached := float64(numberOfVotesInPool) >= float64(epochInfo.MasternodesLen)*certThreshold if thresholdReached { log.Info(fmt.Sprintf("[voteHandler] Vote pool threashold reached: %v, number of items in the pool: %v", thresholdReached, numberOfVotesInPool)) @@ -156,9 +162,15 @@ func (x *XDPoS_v2) onVotePoolThresholdReached(chain consensus.ChainReader, poole } } + epochInfo, err := x.getEpochSwitchInfo(chain, chain.CurrentHeader(), chain.CurrentHeader().Hash()) + if err != nil { + log.Error("[voteHandler] Error when getting epoch switch Info", "error", err) + return fmt.Errorf("Fail on voteHandler due to failure in getting epoch switch info") + } + // Skip and wait for the next vote to process again if valid votes is less than what we required certThreshold := x.config.V2.Config(uint64(currentVoteMsg.(*types.Vote).ProposedBlockInfo.Round)).CertThreshold - if len(validSignatures) < certThreshold { + if float64(len(validSignatures)) < float64(epochInfo.MasternodesLen)*certThreshold { log.Warn("[onVotePoolThresholdReached] Not enough valid signatures to generate QC", "VotesSignaturesAfterFilter", validSignatures, "NumberOfValidVotes", len(validSignatures), "NumberOfVotes", len(pooledVotes)) return nil } @@ -168,7 +180,7 @@ func (x *XDPoS_v2) onVotePoolThresholdReached(chain consensus.ChainReader, poole Signatures: validSignatures, GapNumber: currentVoteMsg.(*types.Vote).GapNumber, } - err := x.processQC(chain, quorumCert) + err = x.processQC(chain, quorumCert) if err != nil { log.Error("Error while processing QC in the Vote handler after reaching pool threshold, ", err) return err diff --git a/consensus/XDPoS/utils/errors.go b/consensus/XDPoS/utils/errors.go index 7692614fee..4a45d4157a 100644 --- a/consensus/XDPoS/utils/errors.go +++ b/consensus/XDPoS/utils/errors.go @@ -92,8 +92,8 @@ var ( ErrInvalidTCSignatures = errors.New("Invalid TC Signatures") ErrEmptyBlockInfoHash = errors.New("BlockInfo hash is empty") ErrInvalidFieldInNonEpochSwitch = errors.New("Invalid field exist in a non-epoch swtich block") - ErrValidatorNotWithinMasternodes = errors.New("Validaotor address is not in the master node list") - ErrCoinbaseAndValidatorMismatch = errors.New("Validaotor and coinbase address in header does not match") + ErrValidatorNotWithinMasternodes = errors.New("Validator address is not in the master node list") + ErrCoinbaseAndValidatorMismatch = errors.New("Validator and coinbase address in header does not match") ErrNotItsTurn = errors.New("Not validator's turn to mine this block") ErrRoundInvalid = errors.New("Invalid Round, it shall be bigger than QC round") diff --git a/consensus/tests/engine_v2_tests/forensics_test.go b/consensus/tests/engine_v2_tests/forensics_test.go index 35c6463fef..10f031aa89 100644 --- a/consensus/tests/engine_v2_tests/forensics_test.go +++ b/consensus/tests/engine_v2_tests/forensics_test.go @@ -34,7 +34,7 @@ func TestProcessQcShallSetForensicsCommittedQc(t *testing.T) { // Set round to 5 engineV2.SetNewRoundFaker(blockchain, types.Round(5), false) - // Create two vote messages which will not reach vote pool threshold + // Create three vote messages which will not reach vote pool threshold signedHash, err := signFn(accounts.Account{Address: signer}, voteSigningHash.Bytes()) assert.Nil(t, err) voteMsg := &types.Vote{ @@ -42,9 +42,9 @@ func TestProcessQcShallSetForensicsCommittedQc(t *testing.T) { Signature: signedHash, GapNumber: 450, } - err = engineV2.VoteHandler(blockchain, voteMsg) assert.Nil(t, err) + signedHash = SignHashByPK(acc1Key, voteSigningHash.Bytes()) voteMsg = &types.Vote{ ProposedBlockInfo: blockInfo, @@ -54,6 +54,15 @@ func TestProcessQcShallSetForensicsCommittedQc(t *testing.T) { err = engineV2.VoteHandler(blockchain, voteMsg) assert.Nil(t, err) + signedHash = SignHashByPK(acc2Key, voteSigningHash.Bytes()) + voteMsg = &types.Vote{ + ProposedBlockInfo: blockInfo, + Signature: signedHash, + GapNumber: 450, + } + err = engineV2.VoteHandler(blockchain, voteMsg) + assert.Nil(t, err) + // Create another vote which is signed by someone not from the master node list randomSigner, randomSignFn, err := backends.SimulateWalletAddressAndSignFn() assert.Nil(t, err) diff --git a/consensus/tests/engine_v2_tests/timeout_test.go b/consensus/tests/engine_v2_tests/timeout_test.go index c1c9709d5c..0b01f54342 100644 --- a/consensus/tests/engine_v2_tests/timeout_test.go +++ b/consensus/tests/engine_v2_tests/timeout_test.go @@ -140,15 +140,14 @@ func TestTimeoutPeriodAndThreadholdConfigChange(t *testing.T) { // Timeout handler func TestTimeoutMessageHandlerSuccessfullyGenerateTCandSyncInfo(t *testing.T) { - params.TestXDPoSMockChainConfig.XDPoS.V2.CurrentConfig = params.UnitTestV2Configs[0] - blockchain, _, _, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 11, params.TestXDPoSMockChainConfig, nil) + blockchain, _, _, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 905, params.TestXDPoSMockChainConfig, nil) engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2 // Set round to 1 - engineV2.SetNewRoundFaker(blockchain, types.Round(1), false) + engineV2.SetNewRoundFaker(blockchain, types.Round(5), false) // Create two timeout message which will not reach timeout pool threshold timeoutMsg := &types.Timeout{ - Round: types.Round(1), + Round: types.Round(5), Signature: []byte{1}, GapNumber: 450, } @@ -156,32 +155,39 @@ func TestTimeoutMessageHandlerSuccessfullyGenerateTCandSyncInfo(t *testing.T) { err := engineV2.TimeoutHandler(blockchain, timeoutMsg) assert.Nil(t, err) currentRound, _, _, _, _, _ := engineV2.GetPropertiesFaker() - assert.Equal(t, types.Round(1), currentRound) + assert.Equal(t, types.Round(5), currentRound) timeoutMsg = &types.Timeout{ - Round: types.Round(1), + Round: types.Round(5), Signature: []byte{2}, GapNumber: 450, } err = engineV2.TimeoutHandler(blockchain, timeoutMsg) assert.Nil(t, err) + timeoutMsg = &types.Timeout{ + Round: types.Round(5), + Signature: []byte{3}, + GapNumber: 450, + } + err = engineV2.TimeoutHandler(blockchain, timeoutMsg) + assert.Nil(t, err) currentRound, _, _, _, _, _ = engineV2.GetPropertiesFaker() - assert.Equal(t, types.Round(1), currentRound) + assert.Equal(t, types.Round(5), currentRound) // Send a timeout with different gap number, it shall not trigger timeout pool hook timeoutMsg = &types.Timeout{ - Round: types.Round(1), - Signature: []byte{3}, + Round: types.Round(5), + Signature: []byte{4}, GapNumber: 1350, } err = engineV2.TimeoutHandler(blockchain, timeoutMsg) assert.Nil(t, err) currentRound, _, _, _, _, _ = engineV2.GetPropertiesFaker() - assert.Equal(t, types.Round(1), currentRound) + assert.Equal(t, types.Round(5), currentRound) // Create a timeout message that should trigger timeout pool hook timeoutMsg = &types.Timeout{ - Round: types.Round(1), - Signature: []byte{4}, + Round: types.Round(5), + Signature: []byte{5}, GapNumber: 450, } @@ -200,12 +206,12 @@ func TestTimeoutMessageHandlerSuccessfullyGenerateTCandSyncInfo(t *testing.T) { tc := syncInfoMsg.(*types.SyncInfo).HighestTimeoutCert assert.NotNil(t, tc) - assert.Equal(t, tc.Round, types.Round(1)) + assert.Equal(t, tc.Round, types.Round(5)) assert.Equal(t, uint64(450), tc.GapNumber) // The signatures shall not include the byte{3} from a different gap number - sigatures := []types.Signature{[]byte{1}, []byte{2}, []byte{4}} + sigatures := []types.Signature{[]byte{1}, []byte{2}, []byte{3}, []byte{5}} assert.ElementsMatch(t, tc.Signatures, sigatures) - assert.Equal(t, types.Round(2), currentRound) + assert.Equal(t, types.Round(6), currentRound) } func TestThrowErrorIfTimeoutMsgRoundNotEqualToCurrentRound(t *testing.T) { diff --git a/consensus/tests/engine_v2_tests/verify_header_test.go b/consensus/tests/engine_v2_tests/verify_header_test.go index 4e1cb8b7e3..1c54bccf96 100644 --- a/consensus/tests/engine_v2_tests/verify_header_test.go +++ b/consensus/tests/engine_v2_tests/verify_header_test.go @@ -235,9 +235,10 @@ func TestConfigSwitchOnDifferentCertThreshold(t *testing.T) { acc1SignedHash = SignHashByPK(acc1Key, types.VoteSigHash(voteForSign).Bytes()) acc2SignedHash = SignHashByPK(acc2Key, types.VoteSigHash(voteForSign).Bytes()) acc3SignedHash = SignHashByPK(acc3Key, types.VoteSigHash(voteForSign).Bytes()) + voteSignedHash := SignHashByPK(voterKey, types.VoteSigHash(voteForSign).Bytes()) var signaturesThr []types.Signature - signaturesThr = append(signaturesThr, acc1SignedHash, acc2SignedHash, acc3SignedHash) + signaturesThr = append(signaturesThr, acc1SignedHash, acc2SignedHash, acc3SignedHash, voteSignedHash) quorumCert = &types.QuorumCert{ ProposedBlockInfo: proposedBlockInfo, Signatures: signaturesThr, diff --git a/consensus/tests/engine_v2_tests/vote_test.go b/consensus/tests/engine_v2_tests/vote_test.go index 311e0488eb..53ac60eed1 100644 --- a/consensus/tests/engine_v2_tests/vote_test.go +++ b/consensus/tests/engine_v2_tests/vote_test.go @@ -35,7 +35,7 @@ func TestVoteMessageHandlerSuccessfullyGeneratedAndProcessQCForFistV2Round(t *te // Set round to 1 engineV2.SetNewRoundFaker(blockchain, types.Round(1), false) - // Create two vote messages which will not reach vote pool threshold + // Create three vote messages which will not reach vote pool threshold signedHash, err := signFn(accounts.Account{Address: signer}, voteSigningHash.Bytes()) assert.Nil(t, err) voteMsg := &types.Vote{ @@ -52,8 +52,16 @@ func TestVoteMessageHandlerSuccessfullyGeneratedAndProcessQCForFistV2Round(t *te assert.Equal(t, types.Round(0), highestQuorumCert.ProposedBlockInfo.Round) assert.Equal(t, types.Round(1), currentRound) - signedHash = SignHashByPK(acc2Key, voteSigningHash.Bytes()) + signedHash = SignHashByPK(acc1Key, voteSigningHash.Bytes()) + voteMsg = &types.Vote{ + ProposedBlockInfo: blockInfo, + Signature: signedHash, + GapNumber: 450, + } + err = engineV2.VoteHandler(blockchain, voteMsg) + assert.Nil(t, err) + signedHash = SignHashByPK(acc2Key, voteSigningHash.Bytes()) voteMsg = &types.Vote{ ProposedBlockInfo: blockInfo, Signature: signedHash, @@ -104,7 +112,7 @@ func TestVoteMessageHandlerSuccessfullyGeneratedAndProcessQC(t *testing.T) { // Set round to 5 engineV2.SetNewRoundFaker(blockchain, types.Round(5), false) - // Create two vote messages which will not reach vote pool threshold + // Create three vote messages which will not reach vote pool threshold signedHash, err := signFn(accounts.Account{Address: signer}, voteSigningHash.Bytes()) assert.Nil(t, err) voteMsg := &types.Vote{ @@ -128,6 +136,15 @@ func TestVoteMessageHandlerSuccessfullyGeneratedAndProcessQC(t *testing.T) { } err = engineV2.VoteHandler(blockchain, voteMsg) assert.Nil(t, err) + + signedHash = SignHashByPK(acc2Key, voteSigningHash.Bytes()) + voteMsg = &types.Vote{ + ProposedBlockInfo: blockInfo, + Signature: signedHash, + GapNumber: 450, + } + err = engineV2.VoteHandler(blockchain, voteMsg) + assert.Nil(t, err) currentRound, lockQuorumCert, highestQuorumCert, _, _, _ = engineV2.GetPropertiesFaker() // Still using the initlised value because we did not yet go to the next round assert.Nil(t, lockQuorumCert) @@ -213,7 +230,7 @@ func TestThrowErrorIfVoteMsgRoundIsMoreThanOneRoundAwayFromCurrentRound(t *testi } func TestProcessVoteMsgThenTimeoutMsg(t *testing.T) { - blockchain, _, currentBlock, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 905, params.TestXDPoSMockChainConfig, nil) + blockchain, _, currentBlock, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, 905, params.TestXDPoSMockChainConfig, nil) engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2 // Set round to 5 @@ -230,15 +247,27 @@ func TestProcessVoteMsgThenTimeoutMsg(t *testing.T) { GapNumber: 450, } voteSigningHash := types.VoteSigHash(voteForSign) - // Create two vote message which will not reach vote pool threshold - signedHash := SignHashByPK(acc1Key, voteSigningHash.Bytes()) + + // Create three vote message which will not reach vote pool threshold + signedHash, err := signFn(accounts.Account{Address: signer}, voteSigningHash.Bytes()) + assert.Nil(t, err) voteMsg := &types.Vote{ ProposedBlockInfo: blockInfo, Signature: signedHash, GapNumber: 450, } - err := engineV2.VoteHandler(blockchain, voteMsg) + err = engineV2.VoteHandler(blockchain, voteMsg) + assert.Nil(t, err) + + signedHash = SignHashByPK(acc1Key, voteSigningHash.Bytes()) + voteMsg = &types.Vote{ + ProposedBlockInfo: blockInfo, + Signature: signedHash, + GapNumber: 450, + } + + err = engineV2.VoteHandler(blockchain, voteMsg) assert.Nil(t, err) currentRound, lockQuorumCert, highestQuorumCert, _, _, _ := engineV2.GetPropertiesFaker() // initialised with nil and 0 round @@ -302,13 +331,19 @@ func TestProcessVoteMsgThenTimeoutMsg(t *testing.T) { } err = engineV2.TimeoutHandler(blockchain, timeoutMsg) assert.Nil(t, err) + timeoutMsg = &types.Timeout{ + Round: types.Round(6), + Signature: []byte{3}, + } + err = engineV2.TimeoutHandler(blockchain, timeoutMsg) + assert.Nil(t, err) currentRound, _, _, _, _, _ = engineV2.GetPropertiesFaker() assert.Equal(t, types.Round(6), currentRound) // Create a timeout message that should trigger timeout pool hook timeoutMsg = &types.Timeout{ Round: types.Round(6), - Signature: []byte{3}, + Signature: []byte{4}, } err = engineV2.TimeoutHandler(blockchain, timeoutMsg) @@ -325,7 +360,7 @@ func TestProcessVoteMsgThenTimeoutMsg(t *testing.T) { tc := syncInfoMsg.(*types.SyncInfo).HighestTimeoutCert assert.NotNil(t, tc) assert.Equal(t, types.Round(6), tc.Round) - sigatures := []types.Signature{[]byte{1}, []byte{2}, []byte{3}} + sigatures := []types.Signature{[]byte{1}, []byte{2}, []byte{3}, []byte{4}} assert.ElementsMatch(t, tc.Signatures, sigatures) // Round shall be +1 now currentRound, _, _, _, _, _ = engineV2.GetPropertiesFaker() @@ -430,7 +465,7 @@ func TestProcessVoteMsgFailIfVerifyBlockInfoFail(t *testing.T) { GapNumber: 450, } voteSigningHash := types.VoteSigHash(voteForSign) - // Create two vote message which will not reach vote pool threshold + // Create three vote message which will not reach vote pool threshold signedHash := SignHashByPK(acc1Key, voteSigningHash.Bytes()) voteMsg := &types.Vote{ ProposedBlockInfo: blockInfo, @@ -453,13 +488,20 @@ func TestProcessVoteMsgFailIfVerifyBlockInfoFail(t *testing.T) { } err = engineV2.VoteHandler(blockchain, voteMsg) assert.Nil(t, err) + voteMsg = &types.Vote{ + ProposedBlockInfo: blockInfo, + Signature: SignHashByPK(acc3Key, voteSigningHash.Bytes()), + GapNumber: 450, + } + err = engineV2.VoteHandler(blockchain, voteMsg) + assert.Nil(t, err) currentRound, _, _, _, _, _ = engineV2.GetPropertiesFaker() assert.Equal(t, types.Round(5), currentRound) // Create a vote message that should trigger vote pool hook voteMsg = &types.Vote{ ProposedBlockInfo: blockInfo, - Signature: SignHashByPK(acc3Key, voteSigningHash.Bytes()), + Signature: SignHashByPK(voterKey, voteSigningHash.Bytes()), GapNumber: 450, } diff --git a/core/types/consensus_v2.go b/core/types/consensus_v2.go index 3f4c7cec8c..00db4c9488 100644 --- a/core/types/consensus_v2.go +++ b/core/types/consensus_v2.go @@ -114,6 +114,7 @@ type EpochSwitchInfo struct { Penalties []common.Address Standbynodes []common.Address Masternodes []common.Address + MasternodesLen int EpochSwitchBlockInfo *BlockInfo EpochSwitchParentBlockInfo *BlockInfo } diff --git a/params/config.go b/params/config.go index e93238a8dd..30ca5fd1bc 100644 --- a/params/config.go +++ b/params/config.go @@ -42,7 +42,7 @@ var ( MainnetV2Configs = map[uint64]*V2Config{ Default: { SwitchRound: 0, - CertThreshold: 73, // based on masternode is 108 + CertThreshold: 0.667, TimeoutSyncThreshold: 3, TimeoutPeriod: 60, MinePeriod: 2, @@ -52,7 +52,7 @@ var ( TestnetV2Configs = map[uint64]*V2Config{ Default: { SwitchRound: 0, - CertThreshold: 7, //based on masternode is 10 + CertThreshold: 0.667, TimeoutSyncThreshold: 3, TimeoutPeriod: 60, MinePeriod: 2, @@ -62,7 +62,7 @@ var ( DevnetV2Configs = map[uint64]*V2Config{ Default: { SwitchRound: 0, - CertThreshold: 73, // based on masternode is 108 + CertThreshold: 0.667, TimeoutSyncThreshold: 5, TimeoutPeriod: 10, MinePeriod: 2, @@ -72,21 +72,21 @@ var ( UnitTestV2Configs = map[uint64]*V2Config{ Default: { SwitchRound: 0, - CertThreshold: 3, + CertThreshold: 0.667, TimeoutSyncThreshold: 2, TimeoutPeriod: 4, MinePeriod: 2, }, 10: { SwitchRound: 10, - CertThreshold: 5, + CertThreshold: 1, TimeoutSyncThreshold: 2, TimeoutPeriod: 4, MinePeriod: 3, }, 899: { SwitchRound: 899, - CertThreshold: 5, + CertThreshold: 1, TimeoutSyncThreshold: 4, TimeoutPeriod: 5, MinePeriod: 2, @@ -325,11 +325,11 @@ type V2 struct { } type V2Config struct { - SwitchRound uint64 `json:"switchRound"` // v1 to v2 switch block number - MinePeriod int `json:"minePeriod"` // Miner mine period to mine a block - TimeoutSyncThreshold int `json:"timeoutSyncThreshold"` // send syncInfo after number of timeout - TimeoutPeriod int `json:"timeoutPeriod"` // Duration in ms - CertThreshold int `json:"certificateThreshold"` // Necessary number of messages from master nodes to form a certificate + SwitchRound uint64 `json:"switchRound"` // v1 to v2 switch block number + MinePeriod int `json:"minePeriod"` // Miner mine period to mine a block + TimeoutSyncThreshold int `json:"timeoutSyncThreshold"` // send syncInfo after number of timeout + TimeoutPeriod int `json:"timeoutPeriod"` // Duration in ms + CertThreshold float64 `json:"certificateThreshold"` // Necessary number of messages from master nodes to form a certificate } func (c *XDPoSConfig) String() string { diff --git a/params/config_test.go b/params/config_test.go index 7d31c57337..063d1c187f 100644 --- a/params/config_test.go +++ b/params/config_test.go @@ -85,11 +85,11 @@ func TestCheckCompatible(t *testing.T) { func TestUpdateV2Config(t *testing.T) { TestXDPoSMockChainConfig.XDPoS.V2.BuildConfigIndex() c := TestXDPoSMockChainConfig.XDPoS.V2.CurrentConfig - assert.Equal(t, 3, c.CertThreshold) + assert.Equal(t, 0.667, c.CertThreshold) TestXDPoSMockChainConfig.XDPoS.V2.UpdateConfig(10) c = TestXDPoSMockChainConfig.XDPoS.V2.CurrentConfig - assert.Equal(t, 5, c.CertThreshold) + assert.Equal(t, float64(1), c.CertThreshold) TestXDPoSMockChainConfig.XDPoS.V2.UpdateConfig(899) c = TestXDPoSMockChainConfig.XDPoS.V2.CurrentConfig @@ -99,16 +99,16 @@ func TestUpdateV2Config(t *testing.T) { func TestV2Config(t *testing.T) { TestXDPoSMockChainConfig.XDPoS.V2.BuildConfigIndex() c := TestXDPoSMockChainConfig.XDPoS.V2.Config(1) - assert.Equal(t, 3, c.CertThreshold) + assert.Equal(t, 0.667, c.CertThreshold) c = TestXDPoSMockChainConfig.XDPoS.V2.Config(5) - assert.Equal(t, 3, c.CertThreshold) + assert.Equal(t, 0.667, c.CertThreshold) c = TestXDPoSMockChainConfig.XDPoS.V2.Config(10) - assert.Equal(t, 3, c.CertThreshold) + assert.Equal(t, 0.667, c.CertThreshold) c = TestXDPoSMockChainConfig.XDPoS.V2.Config(11) - assert.Equal(t, 5, c.CertThreshold) + assert.Equal(t, float64(1), c.CertThreshold) } func TestBuildConfigIndex(t *testing.T) {