diff --git a/cmd/XDC/main.go b/cmd/XDC/main.go index 912f412739..eb6119fbfe 100644 --- a/cmd/XDC/main.go +++ b/cmd/XDC/main.go @@ -346,7 +346,8 @@ func startNode(ctx *cli.Context, stack *node.Node, cfg XDCConfig) { for range core.CheckpointCh { log.Info("Checkpoint!!! It's time to reconcile node's state...") log.Info("Update consensus parameters") - engine.UpdateParams() + chain := ethereum.BlockChain() + engine.UpdateParams(chain.CurrentHeader()) if common.IsTestnet { ok, err = ethereum.ValidateMasternodeTestnet() if err != nil { diff --git a/consensus/XDPoS/XDPoS.go b/consensus/XDPoS/XDPoS.go index f6b7b5d615..869d0199d6 100644 --- a/consensus/XDPoS/XDPoS.go +++ b/consensus/XDPoS/XDPoS.go @@ -37,8 +37,8 @@ import ( ) const ( - ExtraFieldCheck = false - SkipExtraFieldCheck = true + ExtraFieldCheck = true + SkipExtraFieldCheck = false ) func (x *XDPoS) SigHash(header *types.Header) (hash common.Hash) { @@ -79,7 +79,7 @@ func (x *XDPoS) SubscribeForensicsEvent(ch chan<- types.ForensicsEvent) event.Su // New creates a XDPoS delegated-proof-of-stake consensus engine with the initial // signers set to the ones provided by the user. func New(config *params.XDPoSConfig, db ethdb.Database) *XDPoS { - log.Info("[New] initial conensus engines") + log.Info("[New] initialise consensus engines") // Set any missing consensus parameters to their defaults if config.Epoch == 0 { config.Epoch = utils.EpochLength @@ -88,9 +88,9 @@ func New(config *params.XDPoSConfig, db ethdb.Database) *XDPoS { // For testing and testing project, default to mainnet config if config.V2 == nil { config.V2 = ¶ms.V2{ - FirstSwitchBlock: params.MainnetV2Configs[0].SwitchBlock, - CurrentConfig: params.MainnetV2Configs[0], - AllConfigs: params.MainnetV2Configs, + SwitchBlock: params.XDCMainnetChainConfig.XDPoS.V2.SwitchBlock, + CurrentConfig: params.MainnetV2Configs[0], + AllConfigs: params.MainnetV2Configs, } } @@ -145,8 +145,14 @@ func NewFaker(db ethdb.Database, chainConfig *params.ChainConfig) *XDPoS { } // Reset parameters after checkpoint due to config may change -func (x *XDPoS) UpdateParams() { - x.EngineV2.UpdateParams() +func (x *XDPoS) UpdateParams(header *types.Header) { + switch x.config.BlockConsensusVersion(header.Number, header.Extra, ExtraFieldCheck) { + case params.ConsensusEngineVersion2: + x.EngineV2.UpdateParams(header) + return + default: // Default "v1" + return + } } func (x *XDPoS) Initial(chain consensus.ChainReader, header *types.Header) error { diff --git a/consensus/XDPoS/engines/engine_v2/engine.go b/consensus/XDPoS/engines/engine_v2/engine.go index d8bc577f52..ab37fd820f 100644 --- a/consensus/XDPoS/engines/engine_v2/engine.go +++ b/consensus/XDPoS/engines/engine_v2/engine.go @@ -116,7 +116,7 @@ func New(config *params.XDPoSConfig, db ethdb.Database, waitPeriodCh chan int) * timeoutTimer.OnTimeoutFn = engine.OnCountdownTimeout engine.periodicJob() - config.BuildConfigIndex() + config.V2.BuildConfigIndex() return engine } @@ -131,7 +131,13 @@ signing. It is the hash of the entire header apart from the 65 byte signature contained at the end of the extra data. */ -func (x *XDPoS_v2) UpdateParams() { +func (x *XDPoS_v2) UpdateParams(header *types.Header) { + _, round, _, err := x.getExtraFields(header) + if err != nil { + log.Error("[UpdateParams] retrieve round failed", "block", header.Number.Uint64(), "err", err) + } + x.config.V2.UpdateConfig(uint64(round)) + // Setup timeoutTimer duration := time.Duration(x.config.V2.CurrentConfig.TimeoutPeriod) * time.Second x.timeoutWorker.SetTimeoutDuration(duration) @@ -163,7 +169,7 @@ func (x *XDPoS_v2) initial(chain consensus.ChainReader, header *types.Header) er var quorumCert *types.QuorumCert var err error - if header.Number.Int64() == x.config.V2.FirstSwitchBlock.Int64() { + if header.Number.Int64() == x.config.V2.SwitchBlock.Int64() { log.Info("[initial] highest QC for consensus v2 first block") blockInfo := &types.BlockInfo{ Hash: header.Hash(), @@ -193,13 +199,13 @@ func (x *XDPoS_v2) initial(chain consensus.ChainReader, header *types.Header) er } // Initial first v2 snapshot - lastGapNum := x.config.V2.FirstSwitchBlock.Uint64() - x.config.Gap + lastGapNum := x.config.V2.SwitchBlock.Uint64() - x.config.Gap lastGapHeader := chain.GetHeaderByNumber(lastGapNum) snap, _ := loadSnapshot(x.db, lastGapHeader.Hash()) if snap == nil { - checkpointHeader := chain.GetHeaderByNumber(x.config.V2.FirstSwitchBlock.Uint64()) + checkpointHeader := chain.GetHeaderByNumber(x.config.V2.SwitchBlock.Uint64()) log.Info("[initial] init first snapshot") _, _, masternodes, err := x.getExtraFields(checkpointHeader) @@ -246,7 +252,9 @@ func (x *XDPoS_v2) YourTurn(chain consensus.ChainReader, parent *types.Header, s } waitedTime := time.Now().Unix() - parent.Time.Int64() - if waitedTime < int64(x.config.V2.CurrentConfig.MinePeriod) { + _, parentRound, _, err := x.getExtraFields(parent) + minePeriod := x.config.V2.Config(uint64(parentRound) + 1).MinePeriod // plus 1 means current block + if waitedTime < int64(minePeriod) { log.Trace("[YourTurn] wait after mine period", "minePeriod", x.config.V2.CurrentConfig.MinePeriod, "waitedTime", waitedTime) return false, nil } @@ -718,7 +726,7 @@ func (x *XDPoS_v2) VerifyBlockInfo(blockChainReader consensus.ChainReader, block } // Switch block is a v1 block, there is no valid extra to decode, nor its round - if blockInfo.Number.Cmp(x.config.V2.FirstSwitchBlock) == 0 { + if blockInfo.Number.Cmp(x.config.V2.SwitchBlock) == 0 { if blockInfo.Round != 0 { log.Error("[VerifyBlockInfo] Switch block round is not 0", "BlockInfoHash", blockInfo.Hash.Hex(), "BlockInfoNum", blockInfo.Number, "BlockInfoRound", blockInfo.Round, "blockHeaderNum", blockHeader.Number) return fmt.Errorf("[VerifyBlockInfo] switch block round have to be 0") @@ -751,6 +759,12 @@ func (x *XDPoS_v2) verifyQC(blockChainReader consensus.ChainReader, quorumCert * 4. Verify gapNumber = epochSwitchNumber - epochSwitchNumber%Epoch - Gap 5. Verify blockInfo */ + + if quorumCert == nil { + log.Warn("[verifyQC] QC is Nil") + return utils.ErrInvalidQC + } + epochInfo, err := x.getEpochSwitchInfo(blockChainReader, parentHeader, quorumCert.ProposedBlockInfo.Hash) if err != nil { log.Error("[verifyQC] Error when getting epoch switch Info to verify QC", "Error", err) @@ -763,13 +777,13 @@ func (x *XDPoS_v2) verifyQC(blockChainReader consensus.ChainReader, quorumCert * log.Warn("[verifyQC] duplicated signature in QC", "duplicate", common.Bytes2Hex(d)) } } - if quorumCert == nil { - log.Warn("[verifyQC] QC is Nil") - return utils.ErrInvalidQC - } else if (quorumCert.ProposedBlockInfo.Number.Uint64() > x.config.V2.FirstSwitchBlock.Uint64()) && (signatures == nil || (len(signatures) < x.config.V2.CurrentConfig.CertThreshold)) { + + qcRound := quorumCert.ProposedBlockInfo.Round + certThreshold := x.config.V2.Config(uint64(qcRound)).CertThreshold + if (qcRound > 0) && (signatures == nil || (len(signatures) < certThreshold)) { //First V2 Block QC, QC Signatures is initial nil - log.Warn("[verifyHeader] Invalid QC Signature is nil or empty", "QC", quorumCert, "QCNumber", quorumCert.ProposedBlockInfo.Number, "Signatures len", len(signatures)) - return utils.ErrInvalidQC + log.Warn("[verifyHeader] Invalid QC Signature is nil or less then config", "QC", quorumCert, "QCNumber", quorumCert.ProposedBlockInfo.Number, "Signatures len", len(signatures), "CertThreshold", certThreshold) + return utils.ErrInvalidQCSignatures } start := time.Now() @@ -798,7 +812,7 @@ func (x *XDPoS_v2) verifyQC(blockChainReader consensus.ChainReader, quorumCert * } wg.Wait() elapsed := time.Since(start) - log.Info("[verifyQC] time verify message signatures of qc", "elapsed", elapsed) + log.Debug("[verifyQC] time verify message signatures of qc", "elapsed", elapsed) if haveError != nil { return haveError } @@ -826,7 +840,7 @@ func (x *XDPoS_v2) processQC(blockChainReader consensus.ChainReader, incomingQuo log.Error("[processQC] Block not found using the QC", "quorumCert.ProposedBlockInfo.Hash", incomingQuorumCert.ProposedBlockInfo.Hash, "incomingQuorumCert.ProposedBlockInfo.Number", incomingQuorumCert.ProposedBlockInfo.Number) return fmt.Errorf("block not found, number: %v, hash: %v", incomingQuorumCert.ProposedBlockInfo.Number, incomingQuorumCert.ProposedBlockInfo.Hash) } - if proposedBlockHeader.Number.Cmp(x.config.V2.FirstSwitchBlock) > 0 { + if proposedBlockHeader.Number.Cmp(x.config.V2.SwitchBlock) > 0 { // Extra field contain parent information proposedBlockQuorumCert, round, _, err := x.getExtraFields(proposedBlockHeader) if err != nil { @@ -882,7 +896,7 @@ func (x *XDPoS_v2) getSyncInfo() *types.SyncInfo { //Find parent and grandparent, check round number, if so, commit grandparent(grandGrandParent of currentBlock) func (x *XDPoS_v2) commitBlocks(blockChainReader consensus.ChainReader, proposedBlockHeader *types.Header, proposedBlockRound *types.Round, incomingQc *types.QuorumCert) (bool, error) { // XDPoS v1.0 switch to v2.0, skip commit - if big.NewInt(0).Sub(proposedBlockHeader.Number, big.NewInt(2)).Cmp(x.config.V2.FirstSwitchBlock) <= 0 { + if big.NewInt(0).Sub(proposedBlockHeader.Number, big.NewInt(2)).Cmp(x.config.V2.SwitchBlock) <= 0 { return false, nil } // Find the last two parent block and check their rounds are the continuous @@ -959,7 +973,7 @@ func (x *XDPoS_v2) calcMasternodes(chain consensus.ChainReader, blockNum *big.In } candidates := snap.NextEpochMasterNodes - if blockNum.Uint64() == x.config.V2.FirstSwitchBlock.Uint64()+1 { + if blockNum.Uint64() == x.config.V2.SwitchBlock.Uint64()+1 { log.Info("[calcMasternodes] examing first v2 block") return candidates, []common.Address{}, nil } diff --git a/consensus/XDPoS/engines/engine_v2/epochSwitch.go b/consensus/XDPoS/engines/engine_v2/epochSwitch.go index e94dddd0a2..66feda0617 100644 --- a/consensus/XDPoS/engines/engine_v2/epochSwitch.go +++ b/consensus/XDPoS/engines/engine_v2/epochSwitch.go @@ -82,9 +82,9 @@ func (x *XDPoS_v2) getEpochSwitchInfo(chain consensus.ChainReader, header *types // IsEpochSwitchAtRound() is used by miner to check whether it mines a block in the same epoch with parent func (x *XDPoS_v2) isEpochSwitchAtRound(round types.Round, parentHeader *types.Header) (bool, uint64, error) { - epochNum := x.config.V2.FirstSwitchBlock.Uint64()/x.config.Epoch + uint64(round)/x.config.Epoch + epochNum := x.config.V2.SwitchBlock.Uint64()/x.config.Epoch + uint64(round)/x.config.Epoch // if parent is last v1 block and this is first v2 block, this is treated as epoch switch - if parentHeader.Number.Cmp(x.config.V2.FirstSwitchBlock) == 0 { + if parentHeader.Number.Cmp(x.config.V2.SwitchBlock) == 0 { return true, epochNum, nil } @@ -111,13 +111,13 @@ func (x *XDPoS_v2) GetCurrentEpochSwitchBlock(chain consensus.ChainReader, block } currentCheckpointNumber := epochSwitchInfo.EpochSwitchBlockInfo.Number.Uint64() - epochNum := x.config.V2.FirstSwitchBlock.Uint64()/x.config.Epoch + uint64(epochSwitchInfo.EpochSwitchBlockInfo.Round)/x.config.Epoch + epochNum := x.config.V2.SwitchBlock.Uint64()/x.config.Epoch + uint64(epochSwitchInfo.EpochSwitchBlockInfo.Round)/x.config.Epoch return currentCheckpointNumber, epochNum, nil } func (x *XDPoS_v2) IsEpochSwitch(header *types.Header) (bool, uint64, error) { // Return true directly if we are examing the last v1 block. This could happen if the calling function is examing parent block - if header.Number.Cmp(x.config.V2.FirstSwitchBlock) == 0 { + if header.Number.Cmp(x.config.V2.SwitchBlock) == 0 { log.Info("[IsEpochSwitch] examing last v1 block") return true, header.Number.Uint64() / x.config.Epoch, nil } @@ -129,9 +129,9 @@ func (x *XDPoS_v2) IsEpochSwitch(header *types.Header) (bool, uint64, error) { } parentRound := quorumCert.ProposedBlockInfo.Round epochStartRound := round - round%types.Round(x.config.Epoch) - epochNum := x.config.V2.FirstSwitchBlock.Uint64()/x.config.Epoch + uint64(round)/x.config.Epoch + epochNum := x.config.V2.SwitchBlock.Uint64()/x.config.Epoch + uint64(round)/x.config.Epoch // if parent is last v1 block and this is first v2 block, this is treated as epoch switch - if quorumCert.ProposedBlockInfo.Number.Cmp(x.config.V2.FirstSwitchBlock) == 0 { + if quorumCert.ProposedBlockInfo.Number.Cmp(x.config.V2.SwitchBlock) == 0 { log.Info("[IsEpochSwitch] true, parent equals V2.SwitchBlock", "round", round, "number", header.Number.Uint64(), "hash", header.Hash()) return true, epochNum, nil } diff --git a/consensus/XDPoS/engines/engine_v2/timeout.go b/consensus/XDPoS/engines/engine_v2/timeout.go index 63214c0893..75a74de32c 100644 --- a/consensus/XDPoS/engines/engine_v2/timeout.go +++ b/consensus/XDPoS/engines/engine_v2/timeout.go @@ -28,15 +28,14 @@ func (x *XDPoS_v2) timeoutHandler(blockChainReader consensus.ChainReader, timeou log.Debug("[timeoutHandler] collect timeout", "number", numberOfTimeoutsInPool) // Threshold reached - isThresholdReached := numberOfTimeoutsInPool >= x.config.V2.CurrentConfig.CertThreshold + certThreshold := x.config.V2.Config(uint64(x.currentRound)).CertThreshold + isThresholdReached := numberOfTimeoutsInPool >= 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) if err != nil { return err } - // clean up timeout message, regardless its GapNumber or round - x.timeoutPool.Clear() } return nil } @@ -82,6 +81,11 @@ func (x *XDPoS_v2) verifyTC(chain consensus.ChainReader, timeoutCert *types.Time - 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 TC epoch) */ + if timeoutCert == nil || timeoutCert.Signatures == nil { + log.Warn("[verifyTC] TC or TC signatures is Nil") + return utils.ErrInvalidTC + } + snap, err := x.getSnapshot(chain, timeoutCert.GapNumber, true) if err != nil { log.Error("[verifyTC] Fail to get snapshot when verifying TC!", "TCGapNumber", timeoutCert.GapNumber) @@ -92,16 +96,21 @@ func (x *XDPoS_v2) verifyTC(chain consensus.ChainReader, timeoutCert *types.Time return fmt.Errorf("Empty master node lists from snapshot") } - if timeoutCert == nil { - log.Warn("[verifyTC] TC is Nil") - return utils.ErrInvalidTC - } else if timeoutCert.Signatures == nil || (len(timeoutCert.Signatures) < x.config.V2.CurrentConfig.CertThreshold) { - log.Warn("[verifyTC] Invalid TC Signature is nil or empty", "timeoutCert.Round", timeoutCert.Round, "timeoutCert.GapNumber", timeoutCert.GapNumber, "Signatures len", len(timeoutCert.Signatures)) - return utils.ErrInvalidTC + signatures, duplicates := UniqueSignatures(timeoutCert.Signatures) + if len(duplicates) != 0 { + for _, d := range duplicates { + log.Warn("[verifyQC] duplicated signature in QC", "duplicate", common.Bytes2Hex(d)) + } + } + + 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) + return utils.ErrInvalidTCSignatures } var wg sync.WaitGroup - wg.Add(len(timeoutCert.Signatures)) + wg.Add(len(signatures)) var haveError error signedTimeoutObj := types.TimeoutSigHash(&types.TimeoutForSign{ @@ -109,17 +118,17 @@ func (x *XDPoS_v2) verifyTC(chain consensus.ChainReader, timeoutCert *types.Time GapNumber: timeoutCert.GapNumber, }) - for _, signature := range timeoutCert.Signatures { + for _, signature := range signatures { go func(sig types.Signature) { defer wg.Done() 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(timeoutCert.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") return } if !verified { - log.Warn("[verifyTC] Signature not verified doing TC verification", "timeoutCert.Round", timeoutCert.Round, "timeoutCert.GapNumber", timeoutCert.GapNumber, "Signatures len", len(timeoutCert.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") return } diff --git a/consensus/XDPoS/engines/engine_v2/utils.go b/consensus/XDPoS/engines/engine_v2/utils.go index 90bdd1a71c..59115729ab 100644 --- a/consensus/XDPoS/engines/engine_v2/utils.go +++ b/consensus/XDPoS/engines/engine_v2/utils.go @@ -127,7 +127,7 @@ func (x *XDPoS_v2) getExtraFields(header *types.Header) (*types.QuorumCert, type var masternodes []common.Address // last v1 block - if header.Number.Cmp(x.config.V2.FirstSwitchBlock) == 0 { + if header.Number.Cmp(x.config.V2.SwitchBlock) == 0 { masternodes = decodeMasternodesFromHeaderExtra(header) return nil, types.Round(0), masternodes, nil } diff --git a/consensus/XDPoS/engines/engine_v2/verifyHeader.go b/consensus/XDPoS/engines/engine_v2/verifyHeader.go index d72bbb013b..6ea6cc1ae3 100644 --- a/consensus/XDPoS/engines/engine_v2/verifyHeader.go +++ b/consensus/XDPoS/engines/engine_v2/verifyHeader.go @@ -58,7 +58,7 @@ func (x *XDPoS_v2) verifyHeader(chain consensus.ChainReader, header *types.Heade if parent == nil || parent.Number.Uint64() != number-1 || parent.Hash() != header.ParentHash { return consensus.ErrUnknownAncestor } - if parent.Number.Uint64() > x.config.V2.FirstSwitchBlock.Uint64() && parent.Time.Uint64()+uint64(x.config.V2.CurrentConfig.MinePeriod) > header.Time.Uint64() { + if parent.Number.Uint64() > x.config.V2.SwitchBlock.Uint64() && parent.Time.Uint64()+uint64(x.config.V2.CurrentConfig.MinePeriod) > header.Time.Uint64() { return utils.ErrInvalidTimestamp } diff --git a/consensus/XDPoS/engines/engine_v2/vote.go b/consensus/XDPoS/engines/engine_v2/vote.go index 98ae838ed6..4a367a3761 100644 --- a/consensus/XDPoS/engines/engine_v2/vote.go +++ b/consensus/XDPoS/engines/engine_v2/vote.go @@ -75,7 +75,8 @@ 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) - thresholdReached := numberOfVotesInPool >= x.config.V2.CurrentConfig.CertThreshold + certThreshold := x.config.V2.Config(uint64(voteMsg.ProposedBlockInfo.Round)).CertThreshold + thresholdReached := numberOfVotesInPool >= certThreshold if thresholdReached { log.Info(fmt.Sprintf("[voteHandler] Vote pool threashold reached: %v, number of items in the pool: %v", thresholdReached, numberOfVotesInPool)) @@ -113,46 +114,48 @@ func (x *XDPoS_v2) onVotePoolThresholdReached(chain consensus.ChainReader, poole // Filter out non-Master nodes signatures var wg sync.WaitGroup wg.Add(len(pooledVotes)) - signatureSlice := make([]types.Signature, len(pooledVotes)) + signatures := make([]types.Signature, len(pooledVotes)) counter := 0 for h, vote := range pooledVotes { go func(hash common.Hash, v *types.Vote, i int) { defer wg.Done() - verified, _, err := x.verifyMsgSignature(types.VoteSigHash(&types.VoteForSign{ + signedVote := types.VoteSigHash(&types.VoteForSign{ ProposedBlockInfo: v.ProposedBlockInfo, GapNumber: v.GapNumber, - }), v.Signature, masternodes) + }) + verified, _, err := x.verifyMsgSignature(signedVote, v.Signature, masternodes) if err != nil { - log.Warn("[onVotePoolThresholdReached] Skip not verified vote signatures when building QC", "Error", err.Error()) + log.Warn("[onVotePoolThresholdReached] Skip not verified vote signatures when building QC", "error", err.Error()) } else if !verified { log.Warn("[onVotePoolThresholdReached] Skip not verified vote signatures when building QC", "verified", verified) } else { - signatureSlice[i] = v.Signature + signatures[i] = v.Signature } }(h, vote.(*types.Vote), counter) counter++ } wg.Wait() elapsed := time.Since(start) - log.Info("[onVotePoolThresholdReached] verify message signatures of vote pool took", "elapsed", elapsed) + log.Debug("[onVotePoolThresholdReached] verify message signatures of vote pool took", "elapsed", elapsed) // The signature list may contain empty entey. we only care the ones with values - var validSignatureSlice []types.Signature - for _, v := range signatureSlice { + var validSignatures []types.Signature + for _, v := range signatures { if len(v) != 0 { - validSignatureSlice = append(validSignatureSlice, v) + validSignatures = append(validSignatures, v) } } // Skip and wait for the next vote to process again if valid votes is less than what we required - if len(validSignatureSlice) < x.config.V2.CurrentConfig.CertThreshold { - log.Warn("[onVotePoolThresholdReached] Not enough valid signatures to generate QC", "VotesSignaturesAfterFilter", validSignatureSlice, "NumberOfValidVotes", len(validSignatureSlice), "NumberOfVotes", len(pooledVotes)) + certThreshold := x.config.V2.Config(uint64(currentVoteMsg.(*types.Vote).ProposedBlockInfo.Round)).CertThreshold + if len(validSignatures) < certThreshold { + log.Warn("[onVotePoolThresholdReached] Not enough valid signatures to generate QC", "VotesSignaturesAfterFilter", validSignatures, "NumberOfValidVotes", len(validSignatures), "NumberOfVotes", len(pooledVotes)) return nil } // Genrate QC quorumCert := &types.QuorumCert{ ProposedBlockInfo: currentVoteMsg.(*types.Vote).ProposedBlockInfo, - Signatures: validSignatureSlice, + Signatures: validSignatures, GapNumber: currentVoteMsg.(*types.Vote).GapNumber, } err := x.processQC(chain, quorumCert) diff --git a/consensus/XDPoS/utils/errors.go b/consensus/XDPoS/utils/errors.go index 3a096e03eb..3db4cc8c8b 100644 --- a/consensus/XDPoS/utils/errors.go +++ b/consensus/XDPoS/utils/errors.go @@ -87,7 +87,9 @@ var ( ErrInvalidV2Extra = errors.New("Invalid v2 extra in the block") ErrInvalidQC = errors.New("Invalid QC content") + ErrInvalidQCSignatures = errors.New("Invalid QC Signatures") ErrInvalidTC = errors.New("Invalid TC content") + 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") diff --git a/consensus/tests/engine_v1_tests/helper.go b/consensus/tests/engine_v1_tests/helper.go index 3d66302650..3b777e487b 100644 --- a/consensus/tests/engine_v1_tests/helper.go +++ b/consensus/tests/engine_v1_tests/helper.go @@ -302,7 +302,7 @@ func CreateBlock(blockchain *BlockChain, chainConfig *params.ChainConfig, starti } // Inject the hardcoded master node list for the last v1 epoch block and all v1 epoch switch blocks (excluding genesis) - if big.NewInt(int64(blockNumber)).Cmp(chainConfig.XDPoS.V2.FirstSwitchBlock) == 0 || blockNumber%int(chainConfig.XDPoS.Epoch) == 0 { + if big.NewInt(int64(blockNumber)).Cmp(chainConfig.XDPoS.V2.SwitchBlock) == 0 || blockNumber%int(chainConfig.XDPoS.Epoch) == 0 { // reset extra header.Extra = []byte{} if len(header.Extra) < utils.ExtraVanity { diff --git a/consensus/tests/engine_v2_tests/adaptor_test.go b/consensus/tests/engine_v2_tests/adaptor_test.go index 36fae6922a..6eb2ff65e1 100644 --- a/consensus/tests/engine_v2_tests/adaptor_test.go +++ b/consensus/tests/engine_v2_tests/adaptor_test.go @@ -66,7 +66,7 @@ func TestAdaptorGetMasternodesFromCheckpointHeader(t *testing.T) { headerV1.Extra = common.Hex2Bytes("d7830100018358444388676f312e31352e38856c696e757800000000000000000278c350152e15fa6ffc712a5a73d704ce73e2e103d9e17ae3ff2c6712e44e25b09ac5ee91f6c9ff065551f0dcac6f00cae11192d462db709be3758ccef312ee5eea8d7bad5374c6a652150515d744508b61c1a4deb4e4e7bf057e4e3824c11fd2569bcb77a52905cda63b5a58507910bed335e4c9d87ae0ecdfafd400") masternodesV1 := adaptor.GetMasternodesFromCheckpointHeader(headerV1) headerV2 := currentBlock.Header() - headerV2.Number.Add(blockchain.Config().XDPoS.V2.FirstSwitchBlock, big.NewInt(1)) + headerV2.Number.Add(blockchain.Config().XDPoS.V2.SwitchBlock, big.NewInt(1)) headerV2.Validators = common.Hex2Bytes("0278c350152e15fa6ffc712a5a73d704ce73e2e103d9e17ae3ff2c6712e44e25b09ac5ee91f6c9ff065551f0dcac6f00cae11192d462db709be3758c") headerV2.Extra = []byte{2} masternodesV2 := adaptor.GetMasternodesFromCheckpointHeader(headerV2) @@ -91,12 +91,12 @@ func TestAdaptorIsEpochSwitch(t *testing.T) { parentBlockInfo := &types.BlockInfo{ Hash: header.ParentHash, Round: types.Round(0), - Number: big.NewInt(0).Set(blockchain.Config().XDPoS.V2.FirstSwitchBlock), + Number: big.NewInt(0).Set(blockchain.Config().XDPoS.V2.SwitchBlock), } quorumCert := &types.QuorumCert{ ProposedBlockInfo: parentBlockInfo, Signatures: nil, - GapNumber: blockchain.Config().XDPoS.V2.FirstSwitchBlock.Uint64() - blockchain.Config().XDPoS.Gap, + GapNumber: blockchain.Config().XDPoS.V2.SwitchBlock.Uint64() - blockchain.Config().XDPoS.Gap, } extra := types.ExtraFields_v2{ Round: 1, @@ -105,19 +105,19 @@ func TestAdaptorIsEpochSwitch(t *testing.T) { extraBytes, err := extra.EncodeToBytes() assert.Nil(t, err) header.Extra = extraBytes - header.Number.Add(blockchain.Config().XDPoS.V2.FirstSwitchBlock, big.NewInt(1)) + header.Number.Add(blockchain.Config().XDPoS.V2.SwitchBlock, big.NewInt(1)) isEpochSwitchBlock, _, err = adaptor.IsEpochSwitch(header) assert.Nil(t, err) assert.True(t, isEpochSwitchBlock, "header should be epoch switch", header) parentBlockInfo = &types.BlockInfo{ Hash: header.ParentHash, Round: types.Round(1), - Number: big.NewInt(0).Add(blockchain.Config().XDPoS.V2.FirstSwitchBlock, big.NewInt(1)), + Number: big.NewInt(0).Add(blockchain.Config().XDPoS.V2.SwitchBlock, big.NewInt(1)), } quorumCert = &types.QuorumCert{ ProposedBlockInfo: parentBlockInfo, Signatures: nil, - GapNumber: blockchain.Config().XDPoS.V2.FirstSwitchBlock.Uint64() - blockchain.Config().XDPoS.Gap, + GapNumber: blockchain.Config().XDPoS.V2.SwitchBlock.Uint64() - blockchain.Config().XDPoS.Gap, } extra = types.ExtraFields_v2{ Round: 2, @@ -126,19 +126,19 @@ func TestAdaptorIsEpochSwitch(t *testing.T) { extraBytes, err = extra.EncodeToBytes() assert.Nil(t, err) header.Extra = extraBytes - header.Number.Add(blockchain.Config().XDPoS.V2.FirstSwitchBlock, big.NewInt(2)) + header.Number.Add(blockchain.Config().XDPoS.V2.SwitchBlock, big.NewInt(2)) isEpochSwitchBlock, _, err = adaptor.IsEpochSwitch(header) assert.Nil(t, err) assert.False(t, isEpochSwitchBlock, "header should not be epoch switch", header) parentBlockInfo = &types.BlockInfo{ Hash: header.ParentHash, Round: types.Round(blockchain.Config().XDPoS.Epoch) - 1, - Number: big.NewInt(0).Add(blockchain.Config().XDPoS.V2.FirstSwitchBlock, big.NewInt(100)), + Number: big.NewInt(0).Add(blockchain.Config().XDPoS.V2.SwitchBlock, big.NewInt(100)), } quorumCert = &types.QuorumCert{ ProposedBlockInfo: parentBlockInfo, Signatures: nil, - GapNumber: blockchain.Config().XDPoS.V2.FirstSwitchBlock.Uint64() - blockchain.Config().XDPoS.Gap, + GapNumber: blockchain.Config().XDPoS.V2.SwitchBlock.Uint64() - blockchain.Config().XDPoS.Gap, } extra = types.ExtraFields_v2{ Round: types.Round(blockchain.Config().XDPoS.Epoch) + 1, @@ -147,19 +147,19 @@ func TestAdaptorIsEpochSwitch(t *testing.T) { extraBytes, err = extra.EncodeToBytes() assert.Nil(t, err) header.Extra = extraBytes - header.Number.Add(blockchain.Config().XDPoS.V2.FirstSwitchBlock, big.NewInt(101)) + header.Number.Add(blockchain.Config().XDPoS.V2.SwitchBlock, big.NewInt(101)) isEpochSwitchBlock, _, err = adaptor.IsEpochSwitch(header) assert.Nil(t, err) assert.True(t, isEpochSwitchBlock, "header should be epoch switch", header) parentBlockInfo = &types.BlockInfo{ Hash: header.ParentHash, Round: types.Round(blockchain.Config().XDPoS.Epoch) + 1, - Number: big.NewInt(0).Add(blockchain.Config().XDPoS.V2.FirstSwitchBlock, big.NewInt(100)), + Number: big.NewInt(0).Add(blockchain.Config().XDPoS.V2.SwitchBlock, big.NewInt(100)), } quorumCert = &types.QuorumCert{ ProposedBlockInfo: parentBlockInfo, Signatures: nil, - GapNumber: blockchain.Config().XDPoS.V2.FirstSwitchBlock.Uint64() - blockchain.Config().XDPoS.Gap, + GapNumber: blockchain.Config().XDPoS.V2.SwitchBlock.Uint64() - blockchain.Config().XDPoS.Gap, } extra = types.ExtraFields_v2{ Round: types.Round(blockchain.Config().XDPoS.Epoch) + 2, @@ -168,7 +168,7 @@ func TestAdaptorIsEpochSwitch(t *testing.T) { extraBytes, err = extra.EncodeToBytes() assert.Nil(t, err) header.Extra = extraBytes - header.Number.Add(blockchain.Config().XDPoS.V2.FirstSwitchBlock, big.NewInt(101)) + header.Number.Add(blockchain.Config().XDPoS.V2.SwitchBlock, big.NewInt(101)) isEpochSwitchBlock, _, err = adaptor.IsEpochSwitch(header) assert.Nil(t, err) assert.False(t, isEpochSwitchBlock, "header should not be epoch switch", header) diff --git a/consensus/tests/engine_v2_tests/authorised_masternode_test.go b/consensus/tests/engine_v2_tests/authorised_masternode_test.go index 98d2f63a20..316adb0f15 100644 --- a/consensus/tests/engine_v2_tests/authorised_masternode_test.go +++ b/consensus/tests/engine_v2_tests/authorised_masternode_test.go @@ -83,13 +83,13 @@ func TestIsYourTurnConsensusV2(t *testing.T) { func TestIsYourTurnConsensusV2CrossConfig(t *testing.T) { // we skip test for v1 since it's hard to make a real genesis block - blockchain, _, currentBlock, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, 910, params.TestXDPoSMockChainConfig, nil) + blockchain, _, currentBlock, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, 909, params.TestXDPoSMockChainConfig, nil) firstMinePeriod := blockchain.Config().XDPoS.V2.CurrentConfig.MinePeriod adaptor := blockchain.Engine().(*XDPoS.XDPoS) - blockNum := 911 // 911 is new config switch block + blockNum := 910 // 910 is new config switch block blockCoinBase := "0x111000000000000000000000000000000123" - currentBlock = CreateBlock(blockchain, params.TestXDPoSMockChainConfig, currentBlock, blockNum, 1, blockCoinBase, signer, signFn, nil, nil) + currentBlock = CreateBlock(blockchain, params.TestXDPoSMockChainConfig, currentBlock, blockNum, 10, blockCoinBase, signer, signFn, nil, nil) currentBlockHeader := currentBlock.Header() currentBlockHeader.Time = big.NewInt(time.Now().Unix()) err := blockchain.InsertBlock(currentBlock) @@ -100,8 +100,11 @@ func TestIsYourTurnConsensusV2CrossConfig(t *testing.T) { assert.Nil(t, err) assert.False(t, isYourTurn) + adaptor.UpdateParams(currentBlockHeader) // it will be triggered automatically on the real code by other process + // after new mine period secondMinePeriod := blockchain.Config().XDPoS.V2.CurrentConfig.MinePeriod + time.Sleep(time.Duration(secondMinePeriod-firstMinePeriod) * time.Second) isYourTurn, err = adaptor.YourTurn(blockchain, currentBlockHeader, common.HexToAddress("xdc0D3ab14BBaD3D99F4203bd7a11aCB94882050E7e")) assert.Nil(t, err) diff --git a/consensus/tests/engine_v2_tests/helper.go b/consensus/tests/engine_v2_tests/helper.go index 4b267864bc..648de3a373 100644 --- a/consensus/tests/engine_v2_tests/helper.go +++ b/consensus/tests/engine_v2_tests/helper.go @@ -312,10 +312,10 @@ func PrepareXDCTestBlockChainForV2Engine(t *testing.T, numOfBlocks int, chainCon for i := 1; i <= numOfBlocks; i++ { blockCoinBase := fmt.Sprintf("0x111000000000000000000000000000000%03d", i) // for v2 blocks, fill in correct coinbase - if int64(i) > chainConfig.XDPoS.V2.FirstSwitchBlock.Int64() { + if int64(i) > chainConfig.XDPoS.V2.SwitchBlock.Int64() { blockCoinBase = signer.Hex() } - roundNumber := int64(i) - chainConfig.XDPoS.V2.FirstSwitchBlock.Int64() + roundNumber := int64(i) - chainConfig.XDPoS.V2.SwitchBlock.Int64() block := CreateBlock(blockchain, chainConfig, currentBlock, i, roundNumber, blockCoinBase, signer, signFn, nil, nil) err = blockchain.InsertBlock(block) @@ -348,7 +348,7 @@ func PrepareXDCTestBlockChainForV2Engine(t *testing.T, numOfBlocks int, chainCon } // First v2 block - if (int64(i) - chainConfig.XDPoS.V2.FirstSwitchBlock.Int64()) == 1 { + if (int64(i) - chainConfig.XDPoS.V2.SwitchBlock.Int64()) == 1 { lastv1BlockNumber := block.Header().Number.Uint64() - 1 checkpointBlockNumber := lastv1BlockNumber - lastv1BlockNumber%chainConfig.XDPoS.Epoch checkpointHeader := blockchain.GetHeaderByNumber(checkpointBlockNumber) @@ -398,10 +398,10 @@ func PrepareXDCTestBlockChainWithPenaltyForV2Engine(t *testing.T, numOfBlocks in for i := 1; i <= numOfBlocks; i++ { blockCoinBase := fmt.Sprintf("0x111000000000000000000000000000000%03d", i) // for v2 blocks, fill in correct coinbase - if int64(i) > chainConfig.XDPoS.V2.FirstSwitchBlock.Int64() { + if int64(i) > chainConfig.XDPoS.V2.SwitchBlock.Int64() { blockCoinBase = signer.Hex() } - roundNumber := int64(i) - chainConfig.XDPoS.V2.FirstSwitchBlock.Int64() + roundNumber := int64(i) - chainConfig.XDPoS.V2.SwitchBlock.Int64() // use signer itself as penalty block := CreateBlock(blockchain, chainConfig, currentBlock, i, roundNumber, blockCoinBase, signer, signFn, signer[:], nil) @@ -426,7 +426,7 @@ func CreateBlock(blockchain *BlockChain, chainConfig *params.ChainConfig, starti merkleRoot := "35999dded35e8db12de7e6c1471eb9670c162eec616ecebbaf4fddd4676fb930" var header *types.Header - if big.NewInt(int64(blockNumber)).Cmp(chainConfig.XDPoS.V2.FirstSwitchBlock) == 1 { // Build engine v2 compatible extra data field + if big.NewInt(int64(blockNumber)).Cmp(chainConfig.XDPoS.V2.SwitchBlock) == 1 { // Build engine v2 compatible extra data field extraInBytes := generateV2Extra(roundNumber, currentBlock, signer, signFn, signersKey) header = &types.Header{ @@ -436,9 +436,9 @@ func CreateBlock(blockchain *BlockChain, chainConfig *params.ChainConfig, starti Coinbase: common.HexToAddress(blockCoinBase), Extra: extraInBytes, } - if int64(blockNumber) == (chainConfig.XDPoS.V2.FirstSwitchBlock.Int64() + 1) { // This is the first v2 block, we need to copy the last v1 epoch master node list and inject into v2 validators + if int64(blockNumber) == (chainConfig.XDPoS.V2.SwitchBlock.Int64() + 1) { // This is the first v2 block, we need to copy the last v1 epoch master node list and inject into v2 validators // Get last master node list from last v1 block - lastv1Block := blockchain.GetBlockByNumber(chainConfig.XDPoS.V2.FirstSwitchBlock.Uint64()) + lastv1Block := blockchain.GetBlockByNumber(chainConfig.XDPoS.V2.SwitchBlock.Uint64()) masternodesFromV1LastEpoch := decodeMasternodesFromHeaderExtra(lastv1Block.Header()) for _, v := range masternodesFromV1LastEpoch { header.Validators = append(header.Validators, v[:]...) @@ -446,7 +446,7 @@ func CreateBlock(blockchain *BlockChain, chainConfig *params.ChainConfig, starti } else if roundNumber%int64(chainConfig.XDPoS.Epoch) == 0 { // epoch switch blocks, copy the master node list and inject into v2 validators // Get last master node list from last v1 block - lastv1Block := blockchain.GetBlockByNumber(chainConfig.XDPoS.V2.FirstSwitchBlock.Uint64()) + lastv1Block := blockchain.GetBlockByNumber(chainConfig.XDPoS.V2.SwitchBlock.Uint64()) masternodesFromV1LastEpoch := decodeMasternodesFromHeaderExtra(lastv1Block.Header()) for _, v := range masternodesFromV1LastEpoch { header.Validators = append(header.Validators, v[:]...) @@ -465,7 +465,7 @@ func CreateBlock(blockchain *BlockChain, chainConfig *params.ChainConfig, starti } // Inject the hardcoded master node list for the last v1 epoch block and all v1 epoch switch blocks (excluding genesis) - if big.NewInt(int64(blockNumber)).Cmp(chainConfig.XDPoS.V2.FirstSwitchBlock) == 0 || blockNumber%int(chainConfig.XDPoS.Epoch) == 0 { + if big.NewInt(int64(blockNumber)).Cmp(chainConfig.XDPoS.V2.SwitchBlock) == 0 || blockNumber%int(chainConfig.XDPoS.Epoch) == 0 { // reset extra header.Extra = []byte{} if len(header.Extra) < utils.ExtraVanity { @@ -584,7 +584,7 @@ func findSignerAndSignFn(bc *BlockChain, header *types.Header, signer common.Add addressedSignFn := signFn // If v2 block, we need to use extra data's round to find who is creating the block in order to verify the validator - if header.Number.Cmp(config.XDPoS.V2.FirstSwitchBlock) > 0 { + if header.Number.Cmp(config.XDPoS.V2.SwitchBlock) > 0 { var decodedExtraField types.ExtraFields_v2 err := utils.DecodeBytesExtraFields(header.Extra, &decodedExtraField) if err != nil { diff --git a/consensus/tests/engine_v2_tests/initial_test.go b/consensus/tests/engine_v2_tests/initial_test.go index a26b7f59aa..09446171d4 100644 --- a/consensus/tests/engine_v2_tests/initial_test.go +++ b/consensus/tests/engine_v2_tests/initial_test.go @@ -11,7 +11,7 @@ import ( "github.com/stretchr/testify/assert" ) -func TestInitialFirstV2Blcok(t *testing.T) { +func TestInitialFirstV2Block(t *testing.T) { blockchain, _, currentBlock, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 900, params.TestXDPoSMockChainConfig, nil) adaptor := blockchain.Engine().(*XDPoS.XDPoS) header := currentBlock.Header() @@ -32,7 +32,7 @@ func TestInitialFirstV2Blcok(t *testing.T) { expectedQuorumCert := &types.QuorumCert{ ProposedBlockInfo: blockInfo, Signatures: nil, - GapNumber: blockchain.Config().XDPoS.V2.FirstSwitchBlock.Uint64() - blockchain.Config().XDPoS.Gap, + GapNumber: blockchain.Config().XDPoS.V2.SwitchBlock.Uint64() - blockchain.Config().XDPoS.Gap, } assert.Equal(t, types.Round(1), round) assert.Equal(t, expectedQuorumCert, highQC) @@ -104,7 +104,7 @@ func TestInitialOtherV2Block(t *testing.T) { expectedQuorumCert := &types.QuorumCert{ ProposedBlockInfo: blockInfo, Signatures: []types.Signature{}, - GapNumber: blockchain.Config().XDPoS.V2.FirstSwitchBlock.Uint64() - blockchain.Config().XDPoS.Gap, + GapNumber: blockchain.Config().XDPoS.V2.SwitchBlock.Uint64() - blockchain.Config().XDPoS.Gap, } assert.Equal(t, types.Round(11), round) assert.Equal(t, expectedQuorumCert, highQC) diff --git a/consensus/tests/engine_v2_tests/reward_test.go b/consensus/tests/engine_v2_tests/reward_test.go index 5b44560f9f..55dc4104ca 100644 --- a/consensus/tests/engine_v2_tests/reward_test.go +++ b/consensus/tests/engine_v2_tests/reward_test.go @@ -23,7 +23,7 @@ func TestHookRewardV2(t *testing.T) { err = json.Unmarshal([]byte(configString), &config) assert.Nil(t, err) // set switch to 1800, so that it covers 901-1799, 1800-2700 two epochs - config.XDPoS.V2.FirstSwitchBlock.SetUint64(1800) + config.XDPoS.V2.SwitchBlock.SetUint64(1800) blockchain, _, _, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, int(config.XDPoS.Epoch)*5, &config, nil) @@ -104,7 +104,7 @@ func TestHookRewardV2SplitReward(t *testing.T) { err = json.Unmarshal([]byte(configString), &config) assert.Nil(t, err) // set switch to 1800, so that it covers 901-1799, 1800-2700 two epochs - config.XDPoS.V2.FirstSwitchBlock.SetUint64(1800) + config.XDPoS.V2.SwitchBlock.SetUint64(1800) blockchain, _, _, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, int(config.XDPoS.Epoch)*3, &config, nil) diff --git a/consensus/tests/engine_v2_tests/timeout_test.go b/consensus/tests/engine_v2_tests/timeout_test.go index 943fd6fbbf..870fb73c57 100644 --- a/consensus/tests/engine_v2_tests/timeout_test.go +++ b/consensus/tests/engine_v2_tests/timeout_test.go @@ -115,7 +115,7 @@ func TestTimeoutPeriodAndThreadholdConfigChange(t *testing.T) { err := blockchain.InsertBlock(currentBlock) assert.Nil(t, err) - engineV2.UpdateParams() // it will be triggered automatically on the real code by other process + engineV2.UpdateParams(currentBlockHeader) // it will be triggered automatically on the real code by other process t.Log("waiting for another consecutive period") // another consecutive period diff --git a/consensus/tests/engine_v2_tests/verify_header_test.go b/consensus/tests/engine_v2_tests/verify_header_test.go index ff0b95c68d..503f56c54f 100644 --- a/consensus/tests/engine_v2_tests/verify_header_test.go +++ b/consensus/tests/engine_v2_tests/verify_header_test.go @@ -183,7 +183,7 @@ func TestConfigSwitchOnDifferentCertThreshold(t *testing.T) { adaptor := blockchain.Engine().(*XDPoS.XDPoS) - // Genrate QC + // Genrate 911 QC proposedBlockInfo := &types.BlockInfo{ Hash: blockchain.GetBlockByNumber(911).Hash(), Round: types.Round(11), @@ -198,11 +198,11 @@ 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()) - var signatures []types.Signature - signatures = append(signatures, acc1SignedHash, acc2SignedHash, acc3SignedHash) + var signaturesFirst []types.Signature + signaturesFirst = append(signaturesFirst, acc1SignedHash, acc2SignedHash, acc3SignedHash) quorumCert := &types.QuorumCert{ ProposedBlockInfo: proposedBlockInfo, - Signatures: signatures, + Signatures: signaturesFirst, GapNumber: 450, } @@ -210,18 +210,53 @@ func TestConfigSwitchOnDifferentCertThreshold(t *testing.T) { Round: types.Round(12), QuorumCert: quorumCert, } - extraInBytes, err := extra.EncodeToBytes() - if err != nil { - panic(fmt.Errorf("Error encode extra into bytes: %v", err)) - } + extraInBytes, _ := extra.EncodeToBytes() // after 910 require 5 signs, but we only give 3 signs block912 := blockchain.GetBlockByNumber(912).Header() block912.Extra = extraInBytes err = adaptor.VerifyHeader(blockchain, block912, true) - // Error happens after verify QC, means verify QC passed - assert.Equal(t, utils.ErrInvalidQC, err) + assert.Equal(t, utils.ErrInvalidQCSignatures, err) + + // Make we verification process use the corresponding config + // Genrate 910 QC + proposedBlockInfo = &types.BlockInfo{ + Hash: blockchain.GetBlockByNumber(910).Hash(), + Round: types.Round(10), + Number: blockchain.GetBlockByNumber(910).Number(), + } + voteForSign = &types.VoteForSign{ + ProposedBlockInfo: proposedBlockInfo, + GapNumber: 450, + } + + // Sign from acc 1, 2, 3 + acc1SignedHash = SignHashByPK(acc1Key, types.VoteSigHash(voteForSign).Bytes()) + acc2SignedHash = SignHashByPK(acc2Key, types.VoteSigHash(voteForSign).Bytes()) + acc3SignedHash = SignHashByPK(acc3Key, types.VoteSigHash(voteForSign).Bytes()) + + var signaturesThr []types.Signature + signaturesThr = append(signaturesThr, acc1SignedHash, acc2SignedHash, acc3SignedHash) + quorumCert = &types.QuorumCert{ + ProposedBlockInfo: proposedBlockInfo, + Signatures: signaturesThr, + GapNumber: 450, + } + + extra = types.ExtraFields_v2{ + Round: types.Round(11), + QuorumCert: quorumCert, + } + extraInBytes, _ = extra.EncodeToBytes() + + // QC contains 910, so it requires 3 signatures, not use block number to determine which config to use + block911 := blockchain.GetBlockByNumber(911).Header() + block911.Extra = extraInBytes + err = adaptor.VerifyHeader(blockchain, block911, true) + + // error ErrValidatorNotWithinMasternodes means verifyQC is passed and move to next verification process + assert.Equal(t, utils.ErrValidatorNotWithinMasternodes, err) } func TestShouldFailIfNotEnoughQCSignatures(t *testing.T) { @@ -271,7 +306,7 @@ func TestShouldFailIfNotEnoughQCSignatures(t *testing.T) { headerWithDuplicatedSignatures.Extra = extraInBytes // Happy path err = adaptor.VerifyHeader(blockchain, headerWithDuplicatedSignatures, true) - assert.Equal(t, utils.ErrInvalidQC, err) + assert.Equal(t, utils.ErrInvalidQCSignatures, err) } @@ -332,12 +367,12 @@ func TestShouldVerifyHeadersEvenIfParentsNotYetWrittenIntoDB(t *testing.T) { // Create block 911 but don't write into DB blockNumber := 911 - roundNumber := int64(blockNumber) - config.XDPoS.V2.FirstSwitchBlock.Int64() + roundNumber := int64(blockNumber) - config.XDPoS.V2.SwitchBlock.Int64() block911 := CreateBlock(blockchain, &config, block910, blockNumber, roundNumber, signer.Hex(), signer, signFn, nil, nil) // Create block 912 and not write into DB as well blockNumber = 912 - roundNumber = int64(blockNumber) - config.XDPoS.V2.FirstSwitchBlock.Int64() + roundNumber = int64(blockNumber) - config.XDPoS.V2.SwitchBlock.Int64() block912 := CreateBlock(blockchain, &config, block911, blockNumber, roundNumber, signer.Hex(), signer, signFn, nil, nil) headersTobeVerified = append(headersTobeVerified, block910.Header(), block911.Header(), block912.Header()) diff --git a/core/blockchain.go b/core/blockchain.go index 127abcb199..9b23f71333 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -2541,7 +2541,7 @@ func (bc *BlockChain) UpdateM1() error { header := bc.CurrentHeader() var maxMasternodes int // check if block number is increase ms checkpoint - if bc.chainConfig.IsTIPIncreaseMasternodes(header.Number) || (bc.chainConfig.XDPoS.V2.FirstSwitchBlock != nil && header.Number.Cmp(bc.chainConfig.XDPoS.V2.FirstSwitchBlock) == 1) { + if bc.chainConfig.IsTIPIncreaseMasternodes(header.Number) || (bc.chainConfig.XDPoS.V2.SwitchBlock != nil && header.Number.Cmp(bc.chainConfig.XDPoS.V2.SwitchBlock) == 1) { // using new masterndoes maxMasternodes = common.MaxMasternodesV2 } else { diff --git a/core/genesis.go b/core/genesis.go index 214f5a335c..79a73f34db 100644 --- a/core/genesis.go +++ b/core/genesis.go @@ -321,7 +321,6 @@ func GenesisBlockForTesting(db ethdb.Database, addr common.Address, balance *big // DefaultGenesisBlock returns the Ethereum main net genesis block. func DefaultGenesisBlock() *Genesis { config := params.XDCMainnetChainConfig - config.XDPoS.V2 = nil return &Genesis{ Config: config, Nonce: 0, diff --git a/eth/hooks/engine_v2_hooks.go b/eth/hooks/engine_v2_hooks.go index c9ec604635..e1f44305ad 100644 --- a/eth/hooks/engine_v2_hooks.go +++ b/eth/hooks/engine_v2_hooks.go @@ -86,7 +86,7 @@ func AttachConsensusV2Hooks(adaptor *XDPoS.XDPoS, bc *core.BlockChain, chainConf // get list check penalties signing block & list master nodes wil comeback // start to calc comeback at v2 block + limitPenaltyEpochV2 to avoid reading v1 blocks - comebackHeight := (common.LimitPenaltyEpochV2+1)*chain.Config().XDPoS.Epoch + chain.Config().XDPoS.V2.FirstSwitchBlock.Uint64() + comebackHeight := (common.LimitPenaltyEpochV2+1)*chain.Config().XDPoS.Epoch + chain.Config().XDPoS.V2.SwitchBlock.Uint64() penComebacks := []common.Address{} if number.Uint64() > comebackHeight { pens := adaptor.EngineV2.GetPreviousPenaltyByHash(chain, currentHash, common.LimitPenaltyEpochV2) @@ -170,7 +170,7 @@ func AttachConsensusV2Hooks(adaptor *XDPoS.XDPoS, bc *core.BlockChain, chainConf } rewards := make(map[string]interface{}) // skip hook reward if this is the first v2 - if number == chain.Config().XDPoS.V2.FirstSwitchBlock.Uint64()+1 { + if number == chain.Config().XDPoS.V2.SwitchBlock.Uint64()+1 { return rewards, nil } start := time.Now() @@ -235,7 +235,7 @@ func GetSigningTxCount(c *XDPoS.XDPoS, chain consensus.ChainReader, header *type if err != nil { return nil, err } - if isEpochSwitch && i != chain.Config().XDPoS.V2.FirstSwitchBlock.Uint64()+1 { + if isEpochSwitch && i != chain.Config().XDPoS.V2.SwitchBlock.Uint64()+1 { epochCount += 1 if epochCount == signEpochCount { endBlockNumber = header.Number.Uint64() - 1 diff --git a/miner/worker.go b/miner/worker.go index 86ea4f125c..2acbc6a869 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -291,7 +291,7 @@ func (self *worker) update() { // A real event arrived, process interesting content select { case v := <-WaitPeriodCh: - log.Info("[worker] update mine period", "period", v) + log.Info("[worker] update wait period", "period", v) waitPeriod = v timeout.Reset(time.Duration(waitPeriod) * time.Second) diff --git a/params/config.go b/params/config.go index 7e7ab323b5..0540de7b0d 100644 --- a/params/config.go +++ b/params/config.go @@ -19,8 +19,10 @@ package params import ( "fmt" "math/big" + "sync" "github.com/XinFinOrg/XDPoSChain/common" + "github.com/XinFinOrg/XDPoSChain/log" ) const ( @@ -39,7 +41,7 @@ var ( var ( MainnetV2Configs = map[uint64]*V2Config{ Default: { - SwitchBlock: big.NewInt(9999999999), + SwitchRound: 0, CertThreshold: common.MaxMasternodesV2*2/3 + 1, TimeoutSyncThreshold: 3, TimeoutPeriod: 60, @@ -47,7 +49,7 @@ var ( MinePeriod: 10, }, 9999999999: { - SwitchBlock: big.NewInt(9999999999), + SwitchRound: 9999999999, CertThreshold: common.MaxMasternodesV2*2/3 + 1, TimeoutSyncThreshold: 3, TimeoutPeriod: 60, @@ -57,31 +59,23 @@ var ( } TestV2Configs = map[uint64]*V2Config{ Default: { - SwitchBlock: big.NewInt(900), + SwitchRound: 0, CertThreshold: 3, TimeoutSyncThreshold: 2, TimeoutPeriod: 4, WaitPeriod: 1, MinePeriod: 2, }, - 900: { - SwitchBlock: big.NewInt(900), - CertThreshold: 3, - TimeoutSyncThreshold: 2, - TimeoutPeriod: 4, - WaitPeriod: 1, - MinePeriod: 2, - }, - 910: { - SwitchBlock: big.NewInt(910), + 10: { + SwitchRound: 10, CertThreshold: 5, TimeoutSyncThreshold: 2, TimeoutPeriod: 4, WaitPeriod: 2, MinePeriod: 3, }, - 1799: { - SwitchBlock: big.NewInt(1799), + 899: { + SwitchRound: 899, CertThreshold: 5, TimeoutSyncThreshold: 4, TimeoutPeriod: 5, @@ -92,31 +86,23 @@ var ( DevnetV2Configs = map[uint64]*V2Config{ Default: { - SwitchBlock: big.NewInt(7074000), - CertThreshold: 4, - TimeoutSyncThreshold: 5, - TimeoutPeriod: 10, - WaitPeriod: 5, - MinePeriod: 5, - }, - 7074000: { - SwitchBlock: big.NewInt(7074000), + SwitchRound: 0, CertThreshold: common.MaxMasternodesV2*2/3 + 1, TimeoutSyncThreshold: 5, TimeoutPeriod: 25, WaitPeriod: 10, MinePeriod: 10, }, - 7224300: { - SwitchBlock: big.NewInt(7224300), + 151919: { + SwitchRound: 151919, CertThreshold: common.MaxMasternodesV2*1/2 + 1, TimeoutSyncThreshold: 8, TimeoutPeriod: 50, WaitPeriod: 5, MinePeriod: 20, }, - 7242300: { - SwitchBlock: big.NewInt(7242300), + 171000: { + SwitchRound: 171000, CertThreshold: common.MaxMasternodesV2*2/3 + 1, TimeoutSyncThreshold: 5, TimeoutPeriod: 25, @@ -142,9 +128,9 @@ var ( Gap: 450, FoudationWalletAddr: common.HexToAddress("xdc92a289fe95a85c53b8d0d113cbaef0c1ec98ac65"), V2: &V2{ - FirstSwitchBlock: MainnetV2Configs[0].SwitchBlock, - CurrentConfig: MainnetV2Configs[0], - AllConfigs: MainnetV2Configs, + SwitchBlock: big.NewInt(9999999999), + CurrentConfig: MainnetV2Configs[0], + AllConfigs: MainnetV2Configs, }, }, } @@ -184,7 +170,7 @@ var ( Gap: 450, FoudationWalletAddr: common.HexToAddress("xdc746249c61f5832c5eed53172776b460491bdcd5c"), V2: &V2{ - FirstSwitchBlock: TestV2Configs[0].SwitchBlock, + SwitchBlock: big.NewInt(900), CurrentConfig: TestV2Configs[0], AllConfigs: TestV2Configs, SkipV2Validation: true, @@ -209,9 +195,9 @@ var ( Gap: 450, FoudationWalletAddr: common.HexToAddress("0x746249c61f5832c5eed53172776b460491bdcd5c"), V2: &V2{ - FirstSwitchBlock: DevnetV2Configs[0].SwitchBlock, - CurrentConfig: DevnetV2Configs[0], - AllConfigs: DevnetV2Configs, + SwitchBlock: big.NewInt(7074000), + CurrentConfig: DevnetV2Configs[0], + AllConfigs: DevnetV2Configs, }, }, } @@ -232,9 +218,9 @@ var ( Period: 15, Epoch: 30000, V2: &V2{ - FirstSwitchBlock: MainnetV2Configs[0].SwitchBlock, - CurrentConfig: MainnetV2Configs[0], - AllConfigs: MainnetV2Configs, + SwitchBlock: big.NewInt(9999999999), + CurrentConfig: MainnetV2Configs[0], + AllConfigs: MainnetV2Configs, }, }, } @@ -275,9 +261,9 @@ var ( FoudationWalletAddr: common.HexToAddress("0x0000000000000000000000000000000000000068"), Reward: 250, V2: &V2{ - FirstSwitchBlock: TestV2Configs[0].SwitchBlock, - CurrentConfig: TestV2Configs[0], - AllConfigs: TestV2Configs, + SwitchBlock: big.NewInt(900), + CurrentConfig: TestV2Configs[0], + AllConfigs: TestV2Configs, }, }, } @@ -347,27 +333,78 @@ type XDPoSConfig struct { } type V2 struct { - FirstSwitchBlock *big.Int `json:"switchBlock"` - CurrentConfig *V2Config `json:"config"` - AllConfigs map[uint64]*V2Config `json:"allConfigs"` - configIndex []uint64 //list of switch block of configs - SkipV2Validation bool //Skip Block Validation for testing purpose, V2 consensus only + lock sync.RWMutex // Protects the signer fields + + SwitchBlock *big.Int `json:"switchBlock"` + CurrentConfig *V2Config `json:"config"` + AllConfigs map[uint64]*V2Config `json:"allConfigs"` + configIndex []uint64 //list of switch block of configs + + SkipV2Validation bool //Skip Block Validation for testing purpose, V2 consensus only } type V2Config struct { - WaitPeriod int `json:"waitPeriod"` // Miner wait period to check mine event - MinePeriod int `json:"minePeriod"` // Miner mine period to mine a block - SwitchBlock *big.Int `json:"switchBlock"` // v1 to v2 switch block number - 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 + WaitPeriod int `json:"waitPeriod"` // Miner wait period to check mine event + 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 } -// String implements the stringer interface, returning the consensus engine details. -func (c *XDPoSConfig) BuildConfigIndex() { +func (c *XDPoSConfig) String() string { + return "XDPoS" +} + +func (c *XDPoSConfig) BlockConsensusVersion(num *big.Int, extraByte []byte, extraCheck bool) string { + if extraCheck && (len(extraByte) == 0 || extraByte[0] != 2) { + return ConsensusEngineVersion1 + } + + if c.V2 != nil && c.V2.SwitchBlock != nil && num.Cmp(c.V2.SwitchBlock) > 0 { + return ConsensusEngineVersion2 + } + return ConsensusEngineVersion1 +} + +func (v *V2) UpdateConfig(round uint64) { + v.lock.Lock() + defer v.lock.Unlock() + + var index uint64 + + //find the right config + for i := range v.configIndex { + if v.configIndex[i] <= round { + index = v.configIndex[i] + } else { + break + } + } + // update to current config + log.Info("[updateV2Config] Update config", "index", index, "round", round, "SwitchRound", v.AllConfigs[index].SwitchRound) + v.CurrentConfig = v.AllConfigs[index] +} + +func (v *V2) Config(round uint64) *V2Config { + configRound := round - 1 //start from next block from SwitchRound number + var index uint64 + + //find the right config + for i := range v.configIndex { + if v.configIndex[i] <= configRound { + index = v.configIndex[i] + } else { + break + } + } + return v.AllConfigs[index] +} + +func (v *V2) BuildConfigIndex() { var list []uint64 - for i := range c.V2.AllConfigs { + for i := range v.AllConfigs { list = append(list, i) } @@ -379,40 +416,12 @@ func (c *XDPoSConfig) BuildConfigIndex() { } } } - c.V2.configIndex = list + log.Info("[BuildConfigIndex] config list", "list", list) + v.configIndex = list } -func (c *XDPoSConfig) String() string { - return "XDPoS" -} - -func (c *XDPoSConfig) updateV2Config(num uint64) { - var index uint64 - - //find the right config - for i := range c.V2.configIndex { - if c.V2.configIndex[i] <= num { - index = c.V2.configIndex[i] - } else { - break - } - } - // update to current config - c.V2.CurrentConfig = c.V2.AllConfigs[index] -} - -func (c *XDPoSConfig) BlockConsensusVersion(num *big.Int, extraByte []byte, skipExtraCheck bool) string { - if !skipExtraCheck && (len(extraByte) == 0 || extraByte[0] != 2) { - return ConsensusEngineVersion1 - } - - if c.V2 != nil && c.V2.FirstSwitchBlock != nil && num.Cmp(c.V2.FirstSwitchBlock) > 0 { - // We have to check each block configuration due to reorg chain case - // Block may get rollback and old config need to apply to verify block - c.updateV2Config(num.Uint64() - 1) - return ConsensusEngineVersion2 - } - return ConsensusEngineVersion1 +func (v *V2) ConfigIndex() []uint64 { + return v.configIndex } // String implements the fmt.Stringer interface. diff --git a/params/config_test.go b/params/config_test.go index 02c5fe2917..9143762d23 100644 --- a/params/config_test.go +++ b/params/config_test.go @@ -20,6 +20,8 @@ import ( "math/big" "reflect" "testing" + + "github.com/stretchr/testify/assert" ) func TestCheckCompatible(t *testing.T) { @@ -79,3 +81,39 @@ func TestCheckCompatible(t *testing.T) { } } } + +func TestUpdateV2Config(t *testing.T) { + TestnetChainConfig.XDPoS.V2.BuildConfigIndex() + c := TestnetChainConfig.XDPoS.V2.CurrentConfig + assert.Equal(t, 3, c.CertThreshold) + + TestnetChainConfig.XDPoS.V2.UpdateConfig(10) + c = TestnetChainConfig.XDPoS.V2.CurrentConfig + assert.Equal(t, 5, c.CertThreshold) + + TestnetChainConfig.XDPoS.V2.UpdateConfig(899) + c = TestnetChainConfig.XDPoS.V2.CurrentConfig + assert.Equal(t, 4, c.TimeoutSyncThreshold) +} + +func TestV2Config(t *testing.T) { + TestnetChainConfig.XDPoS.V2.BuildConfigIndex() + c := TestnetChainConfig.XDPoS.V2.Config(1) + assert.Equal(t, 3, c.CertThreshold) + + c = TestnetChainConfig.XDPoS.V2.Config(5) + assert.Equal(t, 3, c.CertThreshold) + + c = TestnetChainConfig.XDPoS.V2.Config(10) + assert.Equal(t, 3, c.CertThreshold) + + c = TestnetChainConfig.XDPoS.V2.Config(11) + assert.Equal(t, 5, c.CertThreshold) +} + +func TestBuildConfigIndex(t *testing.T) { + TestnetChainConfig.XDPoS.V2.BuildConfigIndex() + index := TestnetChainConfig.XDPoS.V2.ConfigIndex() + expected := []uint64{0, 10, 899} + assert.Equal(t, expected, index) +}