Make masternode dynamic (#356)

* make masternode dynamic

* fix all the tests

* remove cmt

* fix test again
This commit is contained in:
Liam 2023-11-16 23:59:19 +11:00 committed by GitHub
parent 041b667fb0
commit c215316744
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 160 additions and 97 deletions

View file

@ -145,8 +145,8 @@ func (w *wizard) makeGenesis() {
genesis.Config.XDPoS.V2.CurrentConfig.TimeoutSyncThreshold = w.readDefaultInt(3) genesis.Config.XDPoS.V2.CurrentConfig.TimeoutSyncThreshold = w.readDefaultInt(3)
fmt.Println() 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) 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.readDefaultInt(common.MaxMasternodesV2)/3*2 + 1 genesis.Config.XDPoS.V2.CurrentConfig.CertThreshold = w.readDefaultFloat(0.666)
genesis.Config.XDPoS.V2.AllConfigs[0] = genesis.Config.XDPoS.V2.CurrentConfig genesis.Config.XDPoS.V2.AllConfigs[0] = genesis.Config.XDPoS.V2.CurrentConfig

View file

@ -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 { 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 { if quorumCert == nil {
log.Warn("[verifyQC] QC is Nil") log.Warn("[verifyQC] QC is Nil")
return utils.ErrInvalidQC return utils.ErrInvalidQC
@ -803,9 +792,9 @@ func (x *XDPoS_v2) verifyQC(blockChainReader consensus.ChainReader, quorumCert *
qcRound := quorumCert.ProposedBlockInfo.Round qcRound := quorumCert.ProposedBlockInfo.Round
certThreshold := x.config.V2.Config(uint64(qcRound)).CertThreshold 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 //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 return utils.ErrInvalidQCSignatures
} }
start := time.Now() start := time.Now()
@ -1045,17 +1034,8 @@ func (x *XDPoS_v2) calcMasternodes(chain consensus.ChainReader, blockNum *big.In
if len(masternodes) > maxMasternodes { if len(masternodes) > maxMasternodes {
masternodes = 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 // Given hash, get master node from the epoch switch block of the epoch

View file

@ -72,9 +72,10 @@ func (x *XDPoS_v2) getEpochSwitchInfo(chain consensus.ChainReader, header *types
} }
epochSwitchInfo := &types.EpochSwitchInfo{ epochSwitchInfo := &types.EpochSwitchInfo{
Penalties: penalties, Penalties: penalties,
Standbynodes: standbynodes, Standbynodes: standbynodes,
Masternodes: masternodes, Masternodes: masternodes,
MasternodesLen: len(masternodes),
EpochSwitchBlockInfo: &types.BlockInfo{ EpochSwitchBlockInfo: &types.BlockInfo{
Hash: hash, Hash: hash,
Number: h.Number, Number: h.Number,

View file

@ -80,10 +80,10 @@ func (f *Forensics) SetCommittedQCs(headers []types.Header, incomingQC types.Quo
} }
/* /*
Entry point for processing forensics. Entry point for processing forensics.
Triggered once processQC is successfully. Triggered once processQC is successfully.
Forensics runs in a seperate go routine as its no system critical 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 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 { func (f *Forensics) ProcessForensics(chain consensus.ChainReader, engine *XDPoS_v2, incomingQC types.QuorumCert) error {
log.Debug("Received a QC in forensics", "QC", incomingQC) 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. Entry point for processing vote equivocation.
Triggered once handle vote is successfully. Triggered once handle vote is successfully.
Forensics runs in a seperate go routine as its no system critical 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 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 { func (f *Forensics) ProcessVoteEquivocation(chain consensus.ChainReader, engine *XDPoS_v2, incomingVote *types.Vote) error {
log.Debug("Received a vote in forensics", "vote", incomingVote) log.Debug("Received a vote in forensics", "vote", incomingVote)

View file

@ -27,9 +27,15 @@ func (x *XDPoS_v2) timeoutHandler(blockChainReader consensus.ChainReader, timeou
numberOfTimeoutsInPool, pooledTimeouts := x.timeoutPool.Add(timeout) numberOfTimeoutsInPool, pooledTimeouts := x.timeoutPool.Add(timeout)
log.Debug("[timeoutHandler] collect timeout", "number", numberOfTimeoutsInPool) 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 // Threshold reached
certThreshold := x.config.V2.Config(uint64(x.currentRound)).CertThreshold certThreshold := x.config.V2.Config(uint64(x.currentRound)).CertThreshold
isThresholdReached := numberOfTimeoutsInPool >= certThreshold isThresholdReached := float64(numberOfTimeoutsInPool) >= float64(epochInfo.MasternodesLen)*certThreshold
if isThresholdReached { if isThresholdReached {
log.Info(fmt.Sprintf("Timeout pool threashold reached: %v, number of items in the pool: %v", isThresholdReached, numberOfTimeoutsInPool)) 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) 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) snap, err := x.getSnapshot(chain, timeoutCert.GapNumber, true)
if err != nil { if err != nil {
log.Error("[verifyTC] Fail to get snapshot when verifying TC!", "TCGapNumber", timeoutCert.GapNumber) 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 { if snap == nil || len(snap.NextEpochMasterNodes) == 0 {
log.Error("[verifyTC] Something wrong with the snapshot from gapNumber", "messageGapNumber", timeoutCert.GapNumber, "snapshot", snap) 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) 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 certThreshold := x.config.V2.Config(uint64(timeoutCert.Round)).CertThreshold
if len(signatures) < 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", 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 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) verified, _, err := x.verifyMsgSignature(signedTimeoutObj, sig, snap.NextEpochMasterNodes)
if err != nil { 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) 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 return
} }
if !verified { if !verified {
log.Warn("[verifyTC] Signature not verified doing TC verification", "timeoutCert.Round", timeoutCert.Round, "timeoutCert.GapNumber", timeoutCert.GapNumber, "Signatures len", len(signatures)) 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 return
} }
}(signature) }(signature)

View file

@ -161,7 +161,6 @@ func (x *XDPoS_v2) verifyHeader(chain consensus.ChainReader, header *types.Heade
return err return err
} }
// Check its validator
verified, validatorAddress, err := x.verifyMsgSignature(sigHash(header), header.Validator, masterNodes) verified, validatorAddress, err := x.verifyMsgSignature(sigHash(header), header.Validator, masterNodes)
if err != nil { if err != nil {
for index, mn := range masterNodes { for index, mn := range masterNodes {
@ -171,11 +170,11 @@ func (x *XDPoS_v2) verifyHeader(chain consensus.ChainReader, header *types.Heade
return err return err
} }
if !verified { 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 return utils.ErrValidatorNotWithinMasternodes
} }
if validatorAddress != header.Coinbase { 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 return utils.ErrCoinbaseAndValidatorMismatch
} }
// Check the proposer is the leader // Check the proposer is the leader

View file

@ -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.DetectEquivocationInVotePool(voteMsg, x.votePool)
go x.ForensicsProcessor.ProcessVoteEquivocation(chain, x, voteMsg) 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 certThreshold := x.config.V2.Config(uint64(voteMsg.ProposedBlockInfo.Round)).CertThreshold
thresholdReached := numberOfVotesInPool >= certThreshold thresholdReached := float64(numberOfVotesInPool) >= float64(epochInfo.MasternodesLen)*certThreshold
if thresholdReached { if thresholdReached {
log.Info(fmt.Sprintf("[voteHandler] Vote pool threashold reached: %v, number of items in the pool: %v", thresholdReached, numberOfVotesInPool)) 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 // 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 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)) log.Warn("[onVotePoolThresholdReached] Not enough valid signatures to generate QC", "VotesSignaturesAfterFilter", validSignatures, "NumberOfValidVotes", len(validSignatures), "NumberOfVotes", len(pooledVotes))
return nil return nil
} }
@ -168,7 +180,7 @@ func (x *XDPoS_v2) onVotePoolThresholdReached(chain consensus.ChainReader, poole
Signatures: validSignatures, Signatures: validSignatures,
GapNumber: currentVoteMsg.(*types.Vote).GapNumber, GapNumber: currentVoteMsg.(*types.Vote).GapNumber,
} }
err := x.processQC(chain, quorumCert) err = x.processQC(chain, quorumCert)
if err != nil { if err != nil {
log.Error("Error while processing QC in the Vote handler after reaching pool threshold, ", err) log.Error("Error while processing QC in the Vote handler after reaching pool threshold, ", err)
return err return err

View file

@ -92,8 +92,8 @@ var (
ErrInvalidTCSignatures = errors.New("Invalid TC Signatures") ErrInvalidTCSignatures = errors.New("Invalid TC Signatures")
ErrEmptyBlockInfoHash = errors.New("BlockInfo hash is empty") ErrEmptyBlockInfoHash = errors.New("BlockInfo hash is empty")
ErrInvalidFieldInNonEpochSwitch = errors.New("Invalid field exist in a non-epoch swtich block") ErrInvalidFieldInNonEpochSwitch = errors.New("Invalid field exist in a non-epoch swtich block")
ErrValidatorNotWithinMasternodes = errors.New("Validaotor address is not in the master node list") ErrValidatorNotWithinMasternodes = errors.New("Validator address is not in the master node list")
ErrCoinbaseAndValidatorMismatch = errors.New("Validaotor and coinbase address in header does not match") ErrCoinbaseAndValidatorMismatch = errors.New("Validator and coinbase address in header does not match")
ErrNotItsTurn = errors.New("Not validator's turn to mine this block") ErrNotItsTurn = errors.New("Not validator's turn to mine this block")
ErrRoundInvalid = errors.New("Invalid Round, it shall be bigger than QC round") ErrRoundInvalid = errors.New("Invalid Round, it shall be bigger than QC round")

View file

@ -34,7 +34,7 @@ func TestProcessQcShallSetForensicsCommittedQc(t *testing.T) {
// Set round to 5 // Set round to 5
engineV2.SetNewRoundFaker(blockchain, types.Round(5), false) 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()) signedHash, err := signFn(accounts.Account{Address: signer}, voteSigningHash.Bytes())
assert.Nil(t, err) assert.Nil(t, err)
voteMsg := &types.Vote{ voteMsg := &types.Vote{
@ -42,9 +42,9 @@ func TestProcessQcShallSetForensicsCommittedQc(t *testing.T) {
Signature: signedHash, Signature: signedHash,
GapNumber: 450, GapNumber: 450,
} }
err = engineV2.VoteHandler(blockchain, voteMsg) err = engineV2.VoteHandler(blockchain, voteMsg)
assert.Nil(t, err) assert.Nil(t, err)
signedHash = SignHashByPK(acc1Key, voteSigningHash.Bytes()) signedHash = SignHashByPK(acc1Key, voteSigningHash.Bytes())
voteMsg = &types.Vote{ voteMsg = &types.Vote{
ProposedBlockInfo: blockInfo, ProposedBlockInfo: blockInfo,
@ -54,6 +54,15 @@ func TestProcessQcShallSetForensicsCommittedQc(t *testing.T) {
err = engineV2.VoteHandler(blockchain, voteMsg) err = engineV2.VoteHandler(blockchain, voteMsg)
assert.Nil(t, err) 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 // Create another vote which is signed by someone not from the master node list
randomSigner, randomSignFn, err := backends.SimulateWalletAddressAndSignFn() randomSigner, randomSignFn, err := backends.SimulateWalletAddressAndSignFn()
assert.Nil(t, err) assert.Nil(t, err)

View file

@ -140,15 +140,14 @@ func TestTimeoutPeriodAndThreadholdConfigChange(t *testing.T) {
// Timeout handler // Timeout handler
func TestTimeoutMessageHandlerSuccessfullyGenerateTCandSyncInfo(t *testing.T) { func TestTimeoutMessageHandlerSuccessfullyGenerateTCandSyncInfo(t *testing.T) {
params.TestXDPoSMockChainConfig.XDPoS.V2.CurrentConfig = params.UnitTestV2Configs[0] blockchain, _, _, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 905, params.TestXDPoSMockChainConfig, nil)
blockchain, _, _, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 11, params.TestXDPoSMockChainConfig, nil)
engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2 engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2
// Set round to 1 // 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 // Create two timeout message which will not reach timeout pool threshold
timeoutMsg := &types.Timeout{ timeoutMsg := &types.Timeout{
Round: types.Round(1), Round: types.Round(5),
Signature: []byte{1}, Signature: []byte{1},
GapNumber: 450, GapNumber: 450,
} }
@ -156,32 +155,39 @@ func TestTimeoutMessageHandlerSuccessfullyGenerateTCandSyncInfo(t *testing.T) {
err := engineV2.TimeoutHandler(blockchain, timeoutMsg) err := engineV2.TimeoutHandler(blockchain, timeoutMsg)
assert.Nil(t, err) assert.Nil(t, err)
currentRound, _, _, _, _, _ := engineV2.GetPropertiesFaker() currentRound, _, _, _, _, _ := engineV2.GetPropertiesFaker()
assert.Equal(t, types.Round(1), currentRound) assert.Equal(t, types.Round(5), currentRound)
timeoutMsg = &types.Timeout{ timeoutMsg = &types.Timeout{
Round: types.Round(1), Round: types.Round(5),
Signature: []byte{2}, Signature: []byte{2},
GapNumber: 450, GapNumber: 450,
} }
err = engineV2.TimeoutHandler(blockchain, timeoutMsg) err = engineV2.TimeoutHandler(blockchain, timeoutMsg)
assert.Nil(t, err) 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() 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 // Send a timeout with different gap number, it shall not trigger timeout pool hook
timeoutMsg = &types.Timeout{ timeoutMsg = &types.Timeout{
Round: types.Round(1), Round: types.Round(5),
Signature: []byte{3}, Signature: []byte{4},
GapNumber: 1350, GapNumber: 1350,
} }
err = engineV2.TimeoutHandler(blockchain, timeoutMsg) err = engineV2.TimeoutHandler(blockchain, timeoutMsg)
assert.Nil(t, err) assert.Nil(t, err)
currentRound, _, _, _, _, _ = engineV2.GetPropertiesFaker() 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 // Create a timeout message that should trigger timeout pool hook
timeoutMsg = &types.Timeout{ timeoutMsg = &types.Timeout{
Round: types.Round(1), Round: types.Round(5),
Signature: []byte{4}, Signature: []byte{5},
GapNumber: 450, GapNumber: 450,
} }
@ -200,12 +206,12 @@ func TestTimeoutMessageHandlerSuccessfullyGenerateTCandSyncInfo(t *testing.T) {
tc := syncInfoMsg.(*types.SyncInfo).HighestTimeoutCert tc := syncInfoMsg.(*types.SyncInfo).HighestTimeoutCert
assert.NotNil(t, tc) 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) assert.Equal(t, uint64(450), tc.GapNumber)
// The signatures shall not include the byte{3} from a different gap number // 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.ElementsMatch(t, tc.Signatures, sigatures)
assert.Equal(t, types.Round(2), currentRound) assert.Equal(t, types.Round(6), currentRound)
} }
func TestThrowErrorIfTimeoutMsgRoundNotEqualToCurrentRound(t *testing.T) { func TestThrowErrorIfTimeoutMsgRoundNotEqualToCurrentRound(t *testing.T) {

View file

@ -235,9 +235,10 @@ func TestConfigSwitchOnDifferentCertThreshold(t *testing.T) {
acc1SignedHash = SignHashByPK(acc1Key, types.VoteSigHash(voteForSign).Bytes()) acc1SignedHash = SignHashByPK(acc1Key, types.VoteSigHash(voteForSign).Bytes())
acc2SignedHash = SignHashByPK(acc2Key, types.VoteSigHash(voteForSign).Bytes()) acc2SignedHash = SignHashByPK(acc2Key, types.VoteSigHash(voteForSign).Bytes())
acc3SignedHash = SignHashByPK(acc3Key, types.VoteSigHash(voteForSign).Bytes()) acc3SignedHash = SignHashByPK(acc3Key, types.VoteSigHash(voteForSign).Bytes())
voteSignedHash := SignHashByPK(voterKey, types.VoteSigHash(voteForSign).Bytes())
var signaturesThr []types.Signature var signaturesThr []types.Signature
signaturesThr = append(signaturesThr, acc1SignedHash, acc2SignedHash, acc3SignedHash) signaturesThr = append(signaturesThr, acc1SignedHash, acc2SignedHash, acc3SignedHash, voteSignedHash)
quorumCert = &types.QuorumCert{ quorumCert = &types.QuorumCert{
ProposedBlockInfo: proposedBlockInfo, ProposedBlockInfo: proposedBlockInfo,
Signatures: signaturesThr, Signatures: signaturesThr,

View file

@ -35,7 +35,7 @@ func TestVoteMessageHandlerSuccessfullyGeneratedAndProcessQCForFistV2Round(t *te
// Set round to 1 // Set round to 1
engineV2.SetNewRoundFaker(blockchain, types.Round(1), false) 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()) signedHash, err := signFn(accounts.Account{Address: signer}, voteSigningHash.Bytes())
assert.Nil(t, err) assert.Nil(t, err)
voteMsg := &types.Vote{ 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(0), highestQuorumCert.ProposedBlockInfo.Round)
assert.Equal(t, types.Round(1), currentRound) 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{ voteMsg = &types.Vote{
ProposedBlockInfo: blockInfo, ProposedBlockInfo: blockInfo,
Signature: signedHash, Signature: signedHash,
@ -104,7 +112,7 @@ func TestVoteMessageHandlerSuccessfullyGeneratedAndProcessQC(t *testing.T) {
// Set round to 5 // Set round to 5
engineV2.SetNewRoundFaker(blockchain, types.Round(5), false) 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()) signedHash, err := signFn(accounts.Account{Address: signer}, voteSigningHash.Bytes())
assert.Nil(t, err) assert.Nil(t, err)
voteMsg := &types.Vote{ voteMsg := &types.Vote{
@ -128,6 +136,15 @@ func TestVoteMessageHandlerSuccessfullyGeneratedAndProcessQC(t *testing.T) {
} }
err = engineV2.VoteHandler(blockchain, voteMsg) err = engineV2.VoteHandler(blockchain, voteMsg)
assert.Nil(t, err) 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() currentRound, lockQuorumCert, highestQuorumCert, _, _, _ = engineV2.GetPropertiesFaker()
// Still using the initlised value because we did not yet go to the next round // Still using the initlised value because we did not yet go to the next round
assert.Nil(t, lockQuorumCert) assert.Nil(t, lockQuorumCert)
@ -213,7 +230,7 @@ func TestThrowErrorIfVoteMsgRoundIsMoreThanOneRoundAwayFromCurrentRound(t *testi
} }
func TestProcessVoteMsgThenTimeoutMsg(t *testing.T) { 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 engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2
// Set round to 5 // Set round to 5
@ -230,15 +247,27 @@ func TestProcessVoteMsgThenTimeoutMsg(t *testing.T) {
GapNumber: 450, GapNumber: 450,
} }
voteSigningHash := types.VoteSigHash(voteForSign) 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{ voteMsg := &types.Vote{
ProposedBlockInfo: blockInfo, ProposedBlockInfo: blockInfo,
Signature: signedHash, Signature: signedHash,
GapNumber: 450, 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) assert.Nil(t, err)
currentRound, lockQuorumCert, highestQuorumCert, _, _, _ := engineV2.GetPropertiesFaker() currentRound, lockQuorumCert, highestQuorumCert, _, _, _ := engineV2.GetPropertiesFaker()
// initialised with nil and 0 round // initialised with nil and 0 round
@ -302,13 +331,19 @@ func TestProcessVoteMsgThenTimeoutMsg(t *testing.T) {
} }
err = engineV2.TimeoutHandler(blockchain, timeoutMsg) err = engineV2.TimeoutHandler(blockchain, timeoutMsg)
assert.Nil(t, err) 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() currentRound, _, _, _, _, _ = engineV2.GetPropertiesFaker()
assert.Equal(t, types.Round(6), currentRound) assert.Equal(t, types.Round(6), currentRound)
// Create a timeout message that should trigger timeout pool hook // Create a timeout message that should trigger timeout pool hook
timeoutMsg = &types.Timeout{ timeoutMsg = &types.Timeout{
Round: types.Round(6), Round: types.Round(6),
Signature: []byte{3}, Signature: []byte{4},
} }
err = engineV2.TimeoutHandler(blockchain, timeoutMsg) err = engineV2.TimeoutHandler(blockchain, timeoutMsg)
@ -325,7 +360,7 @@ func TestProcessVoteMsgThenTimeoutMsg(t *testing.T) {
tc := syncInfoMsg.(*types.SyncInfo).HighestTimeoutCert tc := syncInfoMsg.(*types.SyncInfo).HighestTimeoutCert
assert.NotNil(t, tc) assert.NotNil(t, tc)
assert.Equal(t, types.Round(6), tc.Round) 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) assert.ElementsMatch(t, tc.Signatures, sigatures)
// Round shall be +1 now // Round shall be +1 now
currentRound, _, _, _, _, _ = engineV2.GetPropertiesFaker() currentRound, _, _, _, _, _ = engineV2.GetPropertiesFaker()
@ -430,7 +465,7 @@ func TestProcessVoteMsgFailIfVerifyBlockInfoFail(t *testing.T) {
GapNumber: 450, GapNumber: 450,
} }
voteSigningHash := types.VoteSigHash(voteForSign) 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()) signedHash := SignHashByPK(acc1Key, voteSigningHash.Bytes())
voteMsg := &types.Vote{ voteMsg := &types.Vote{
ProposedBlockInfo: blockInfo, ProposedBlockInfo: blockInfo,
@ -453,13 +488,20 @@ func TestProcessVoteMsgFailIfVerifyBlockInfoFail(t *testing.T) {
} }
err = engineV2.VoteHandler(blockchain, voteMsg) err = engineV2.VoteHandler(blockchain, voteMsg)
assert.Nil(t, err) 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() currentRound, _, _, _, _, _ = engineV2.GetPropertiesFaker()
assert.Equal(t, types.Round(5), currentRound) assert.Equal(t, types.Round(5), currentRound)
// Create a vote message that should trigger vote pool hook // Create a vote message that should trigger vote pool hook
voteMsg = &types.Vote{ voteMsg = &types.Vote{
ProposedBlockInfo: blockInfo, ProposedBlockInfo: blockInfo,
Signature: SignHashByPK(acc3Key, voteSigningHash.Bytes()), Signature: SignHashByPK(voterKey, voteSigningHash.Bytes()),
GapNumber: 450, GapNumber: 450,
} }

View file

@ -114,6 +114,7 @@ type EpochSwitchInfo struct {
Penalties []common.Address Penalties []common.Address
Standbynodes []common.Address Standbynodes []common.Address
Masternodes []common.Address Masternodes []common.Address
MasternodesLen int
EpochSwitchBlockInfo *BlockInfo EpochSwitchBlockInfo *BlockInfo
EpochSwitchParentBlockInfo *BlockInfo EpochSwitchParentBlockInfo *BlockInfo
} }

View file

@ -42,7 +42,7 @@ var (
MainnetV2Configs = map[uint64]*V2Config{ MainnetV2Configs = map[uint64]*V2Config{
Default: { Default: {
SwitchRound: 0, SwitchRound: 0,
CertThreshold: 73, // based on masternode is 108 CertThreshold: 0.667,
TimeoutSyncThreshold: 3, TimeoutSyncThreshold: 3,
TimeoutPeriod: 60, TimeoutPeriod: 60,
MinePeriod: 2, MinePeriod: 2,
@ -52,7 +52,7 @@ var (
TestnetV2Configs = map[uint64]*V2Config{ TestnetV2Configs = map[uint64]*V2Config{
Default: { Default: {
SwitchRound: 0, SwitchRound: 0,
CertThreshold: 7, //based on masternode is 10 CertThreshold: 0.667,
TimeoutSyncThreshold: 3, TimeoutSyncThreshold: 3,
TimeoutPeriod: 60, TimeoutPeriod: 60,
MinePeriod: 2, MinePeriod: 2,
@ -62,7 +62,7 @@ var (
DevnetV2Configs = map[uint64]*V2Config{ DevnetV2Configs = map[uint64]*V2Config{
Default: { Default: {
SwitchRound: 0, SwitchRound: 0,
CertThreshold: 73, // based on masternode is 108 CertThreshold: 0.667,
TimeoutSyncThreshold: 5, TimeoutSyncThreshold: 5,
TimeoutPeriod: 10, TimeoutPeriod: 10,
MinePeriod: 2, MinePeriod: 2,
@ -72,21 +72,21 @@ var (
UnitTestV2Configs = map[uint64]*V2Config{ UnitTestV2Configs = map[uint64]*V2Config{
Default: { Default: {
SwitchRound: 0, SwitchRound: 0,
CertThreshold: 3, CertThreshold: 0.667,
TimeoutSyncThreshold: 2, TimeoutSyncThreshold: 2,
TimeoutPeriod: 4, TimeoutPeriod: 4,
MinePeriod: 2, MinePeriod: 2,
}, },
10: { 10: {
SwitchRound: 10, SwitchRound: 10,
CertThreshold: 5, CertThreshold: 1,
TimeoutSyncThreshold: 2, TimeoutSyncThreshold: 2,
TimeoutPeriod: 4, TimeoutPeriod: 4,
MinePeriod: 3, MinePeriod: 3,
}, },
899: { 899: {
SwitchRound: 899, SwitchRound: 899,
CertThreshold: 5, CertThreshold: 1,
TimeoutSyncThreshold: 4, TimeoutSyncThreshold: 4,
TimeoutPeriod: 5, TimeoutPeriod: 5,
MinePeriod: 2, MinePeriod: 2,
@ -325,11 +325,11 @@ type V2 struct {
} }
type V2Config struct { type V2Config struct {
SwitchRound uint64 `json:"switchRound"` // v1 to v2 switch block number SwitchRound uint64 `json:"switchRound"` // v1 to v2 switch block number
MinePeriod int `json:"minePeriod"` // Miner mine period to mine a block MinePeriod int `json:"minePeriod"` // Miner mine period to mine a block
TimeoutSyncThreshold int `json:"timeoutSyncThreshold"` // send syncInfo after number of timeout TimeoutSyncThreshold int `json:"timeoutSyncThreshold"` // send syncInfo after number of timeout
TimeoutPeriod int `json:"timeoutPeriod"` // Duration in ms TimeoutPeriod int `json:"timeoutPeriod"` // Duration in ms
CertThreshold int `json:"certificateThreshold"` // Necessary number of messages from master nodes to form a certificate CertThreshold float64 `json:"certificateThreshold"` // Necessary number of messages from master nodes to form a certificate
} }
func (c *XDPoSConfig) String() string { func (c *XDPoSConfig) String() string {

View file

@ -85,11 +85,11 @@ func TestCheckCompatible(t *testing.T) {
func TestUpdateV2Config(t *testing.T) { func TestUpdateV2Config(t *testing.T) {
TestXDPoSMockChainConfig.XDPoS.V2.BuildConfigIndex() TestXDPoSMockChainConfig.XDPoS.V2.BuildConfigIndex()
c := TestXDPoSMockChainConfig.XDPoS.V2.CurrentConfig c := TestXDPoSMockChainConfig.XDPoS.V2.CurrentConfig
assert.Equal(t, 3, c.CertThreshold) assert.Equal(t, 0.667, c.CertThreshold)
TestXDPoSMockChainConfig.XDPoS.V2.UpdateConfig(10) TestXDPoSMockChainConfig.XDPoS.V2.UpdateConfig(10)
c = TestXDPoSMockChainConfig.XDPoS.V2.CurrentConfig c = TestXDPoSMockChainConfig.XDPoS.V2.CurrentConfig
assert.Equal(t, 5, c.CertThreshold) assert.Equal(t, float64(1), c.CertThreshold)
TestXDPoSMockChainConfig.XDPoS.V2.UpdateConfig(899) TestXDPoSMockChainConfig.XDPoS.V2.UpdateConfig(899)
c = TestXDPoSMockChainConfig.XDPoS.V2.CurrentConfig c = TestXDPoSMockChainConfig.XDPoS.V2.CurrentConfig
@ -99,16 +99,16 @@ func TestUpdateV2Config(t *testing.T) {
func TestV2Config(t *testing.T) { func TestV2Config(t *testing.T) {
TestXDPoSMockChainConfig.XDPoS.V2.BuildConfigIndex() TestXDPoSMockChainConfig.XDPoS.V2.BuildConfigIndex()
c := TestXDPoSMockChainConfig.XDPoS.V2.Config(1) 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) 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) 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) c = TestXDPoSMockChainConfig.XDPoS.V2.Config(11)
assert.Equal(t, 5, c.CertThreshold) assert.Equal(t, float64(1), c.CertThreshold)
} }
func TestBuildConfigIndex(t *testing.T) { func TestBuildConfigIndex(t *testing.T) {