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)
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

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 {
/*
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

View file

@ -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,

View file

@ -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)

View file

@ -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)

View file

@ -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

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.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

View file

@ -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")

View file

@ -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)

View file

@ -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) {

View file

@ -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,

View file

@ -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,
}

View file

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

View file

@ -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 {

View file

@ -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) {