Xin 259 support multi v2 config (#210)

* update timeout period and waittime

* remove wrong comment

* update config for preparing test
This commit is contained in:
Liam 2022-12-06 20:08:56 +08:00 committed by GitHub
parent ff6ee67462
commit 6ffbd3e141
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
26 changed files with 469 additions and 171 deletions

View file

@ -301,7 +301,7 @@ func startNode(ctx *cli.Context, stack *node.Node, cfg XDCConfig) {
if err := stack.Service(&ethereum); err != nil { if err := stack.Service(&ethereum); err != nil {
utils.Fatalf("Ethereum service not running: %v", err) utils.Fatalf("Ethereum service not running: %v", err)
} }
if _, ok := ethereum.Engine().(*XDPoS.XDPoS); ok { if engine, ok := ethereum.Engine().(*XDPoS.XDPoS); ok {
go func() { go func() {
started := false started := false
ok := false ok := false
@ -345,6 +345,8 @@ func startNode(ctx *cli.Context, stack *node.Node, cfg XDCConfig) {
defer close(core.CheckpointCh) defer close(core.CheckpointCh)
for range core.CheckpointCh { for range core.CheckpointCh {
log.Info("Checkpoint!!! It's time to reconcile node's state...") log.Info("Checkpoint!!! It's time to reconcile node's state...")
log.Info("Update consensus parameters")
engine.UpdateParams()
if common.IsTestnet { if common.IsTestnet {
ok, err = ethereum.ValidateMasternodeTestnet() ok, err = ethereum.ValidateMasternodeTestnet()
if err != nil { if err != nil {

View file

@ -34,6 +34,10 @@ func (t *CountdownTimer) StopTimer() {
<-q <-q
} }
func (t *CountdownTimer) SetTimeoutDuration(duration time.Duration) {
t.timeoutDuration = duration
}
// Reset will start the countdown timer if it's already stopped, or simply reset the countdown time back to the defual `duration` // Reset will start the countdown timer if it's already stopped, or simply reset the countdown time back to the defual `duration`
func (t *CountdownTimer) Reset(i interface{}) { func (t *CountdownTimer) Reset(i interface{}) {
if !t.isInitilised() { if !t.isInitilised() {

View file

@ -87,7 +87,11 @@ func New(config *params.XDPoSConfig, db ethdb.Database) *XDPoS {
// For testing and testing project, default to mainnet config // For testing and testing project, default to mainnet config
if config.V2 == nil { if config.V2 == nil {
config.V2 = params.XDPoSV2Config config.V2 = &params.V2{
FirstSwitchBlock: params.MainnetV2Configs[0].SwitchBlock,
CurrentConfig: params.MainnetV2Configs[0],
AllConfigs: params.MainnetV2Configs,
}
} }
log.Info("xdc config loading", "config", config) log.Info("xdc config loading", "config", config)
@ -140,6 +144,11 @@ func NewFaker(db ethdb.Database, chainConfig *params.ChainConfig) *XDPoS {
return fakeEngine return fakeEngine
} }
// Reset parameters after checkpoint due to config may change
func (x *XDPoS) UpdateParams() {
x.EngineV2.UpdateParams()
}
func (x *XDPoS) Initial(chain consensus.ChainReader, header *types.Header) error { func (x *XDPoS) Initial(chain consensus.ChainReader, header *types.Header) error {
switch x.config.BlockConsensusVersion(header.Number, header.Extra, ExtraFieldCheck) { switch x.config.BlockConsensusVersion(header.Number, header.Extra, ExtraFieldCheck) {
case params.ConsensusEngineVersion2: case params.ConsensusEngineVersion2:

View file

@ -66,7 +66,7 @@ type XDPoS_v2 struct {
func New(config *params.XDPoSConfig, db ethdb.Database, waitPeriodCh chan int) *XDPoS_v2 { func New(config *params.XDPoSConfig, db ethdb.Database, waitPeriodCh chan int) *XDPoS_v2 {
// Setup timeoutTimer // Setup timeoutTimer
duration := time.Duration(config.V2.TimeoutPeriod) * time.Second duration := time.Duration(config.V2.CurrentConfig.TimeoutPeriod) * time.Second
timeoutTimer := countdown.NewCountDown(duration) timeoutTimer := countdown.NewCountDown(duration)
snapshots, _ := lru.NewARC(utils.InmemorySnapshots) snapshots, _ := lru.NewARC(utils.InmemorySnapshots)
@ -74,8 +74,8 @@ func New(config *params.XDPoSConfig, db ethdb.Database, waitPeriodCh chan int) *
epochSwitches, _ := lru.NewARC(int(utils.InmemoryEpochs)) epochSwitches, _ := lru.NewARC(int(utils.InmemoryEpochs))
verifiedHeaders, _ := lru.NewARC(utils.InmemorySnapshots) verifiedHeaders, _ := lru.NewARC(utils.InmemorySnapshots)
timeoutPool := utils.NewPool(config.V2.CertThreshold) timeoutPool := utils.NewPool()
votePool := utils.NewPool(config.V2.CertThreshold) votePool := utils.NewPool()
engine := &XDPoS_v2{ engine := &XDPoS_v2{
config: config, config: config,
db: db, db: db,
@ -116,6 +116,7 @@ func New(config *params.XDPoSConfig, db ethdb.Database, waitPeriodCh chan int) *
timeoutTimer.OnTimeoutFn = engine.OnCountdownTimeout timeoutTimer.OnTimeoutFn = engine.OnCountdownTimeout
engine.periodicJob() engine.periodicJob()
config.BuildConfigIndex()
return engine return engine
} }
@ -129,6 +130,18 @@ sigHash returns the hash which is used as input for the delegated-proof-of-stake
signing. It is the hash of the entire header apart from the 65 byte signature signing. It is the hash of the entire header apart from the 65 byte signature
contained at the end of the extra data. contained at the end of the extra data.
*/ */
func (x *XDPoS_v2) UpdateParams() {
// Setup timeoutTimer
duration := time.Duration(x.config.V2.CurrentConfig.TimeoutPeriod) * time.Second
x.timeoutWorker.SetTimeoutDuration(duration)
// avoid deadlock
go func() {
x.waitPeriodCh <- x.config.V2.CurrentConfig.WaitPeriod
}()
}
func (x *XDPoS_v2) SignHash(header *types.Header) (hash common.Hash) { func (x *XDPoS_v2) SignHash(header *types.Header) (hash common.Hash) {
return sigHash(header) return sigHash(header)
} }
@ -150,7 +163,7 @@ func (x *XDPoS_v2) initial(chain consensus.ChainReader, header *types.Header) er
var quorumCert *types.QuorumCert var quorumCert *types.QuorumCert
var err error var err error
if header.Number.Int64() == x.config.V2.SwitchBlock.Int64() { if header.Number.Int64() == x.config.V2.FirstSwitchBlock.Int64() {
log.Info("[initial] highest QC for consensus v2 first block") log.Info("[initial] highest QC for consensus v2 first block")
blockInfo := &types.BlockInfo{ blockInfo := &types.BlockInfo{
Hash: header.Hash(), Hash: header.Hash(),
@ -180,13 +193,13 @@ func (x *XDPoS_v2) initial(chain consensus.ChainReader, header *types.Header) er
} }
// Initial first v2 snapshot // Initial first v2 snapshot
lastGapNum := x.config.V2.SwitchBlock.Uint64() - x.config.Gap lastGapNum := x.config.V2.FirstSwitchBlock.Uint64() - x.config.Gap
lastGapHeader := chain.GetHeaderByNumber(lastGapNum) lastGapHeader := chain.GetHeaderByNumber(lastGapNum)
snap, _ := loadSnapshot(x.db, lastGapHeader.Hash()) snap, _ := loadSnapshot(x.db, lastGapHeader.Hash())
if snap == nil { if snap == nil {
checkpointHeader := chain.GetHeaderByNumber(x.config.V2.SwitchBlock.Uint64()) checkpointHeader := chain.GetHeaderByNumber(x.config.V2.FirstSwitchBlock.Uint64())
log.Info("[initial] init first snapshot") log.Info("[initial] init first snapshot")
_, _, masternodes, err := x.getExtraFields(checkpointHeader) _, _, masternodes, err := x.getExtraFields(checkpointHeader)
@ -204,10 +217,10 @@ func (x *XDPoS_v2) initial(chain consensus.ChainReader, header *types.Header) er
} }
// Initial timeout // Initial timeout
log.Info("[initial] miner wait period", "period", x.config.V2.WaitPeriod) log.Info("[initial] miner wait period", "period", x.config.V2.CurrentConfig.WaitPeriod)
// avoid deadlock // avoid deadlock
go func() { go func() {
x.waitPeriodCh <- x.config.V2.WaitPeriod x.waitPeriodCh <- x.config.V2.CurrentConfig.WaitPeriod
}() }()
// Kick-off the countdown timer // Kick-off the countdown timer
@ -233,8 +246,8 @@ func (x *XDPoS_v2) YourTurn(chain consensus.ChainReader, parent *types.Header, s
} }
waitedTime := time.Now().Unix() - parent.Time.Int64() waitedTime := time.Now().Unix() - parent.Time.Int64()
if waitedTime < int64(x.config.V2.MinePeriod) { if waitedTime < int64(x.config.V2.CurrentConfig.MinePeriod) {
log.Trace("[YourTurn] wait after mine period", "minePeriod", x.config.V2.MinePeriod, "waitedTime", waitedTime) log.Trace("[YourTurn] wait after mine period", "minePeriod", x.config.V2.CurrentConfig.MinePeriod, "waitedTime", waitedTime)
return false, nil return false, nil
} }
@ -705,7 +718,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 // Switch block is a v1 block, there is no valid extra to decode, nor its round
if blockInfo.Number.Cmp(x.config.V2.SwitchBlock) == 0 { if blockInfo.Number.Cmp(x.config.V2.FirstSwitchBlock) == 0 {
if blockInfo.Round != 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) 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") return fmt.Errorf("[VerifyBlockInfo] switch block round have to be 0")
@ -753,7 +766,7 @@ func (x *XDPoS_v2) verifyQC(blockChainReader consensus.ChainReader, quorumCert *
if quorumCert == nil { if quorumCert == nil {
log.Warn("[verifyQC] QC is Nil") log.Warn("[verifyQC] QC is Nil")
return utils.ErrInvalidQC return utils.ErrInvalidQC
} else if (quorumCert.ProposedBlockInfo.Number.Uint64() > x.config.V2.SwitchBlock.Uint64()) && (signatures == nil || (len(signatures) < x.config.V2.CertThreshold)) { } else if (quorumCert.ProposedBlockInfo.Number.Uint64() > x.config.V2.FirstSwitchBlock.Uint64()) && (signatures == nil || (len(signatures) < x.config.V2.CurrentConfig.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 empty", "QC", quorumCert, "QCNumber", quorumCert.ProposedBlockInfo.Number, "Signatures len", len(signatures)) log.Warn("[verifyHeader] Invalid QC Signature is nil or empty", "QC", quorumCert, "QCNumber", quorumCert.ProposedBlockInfo.Number, "Signatures len", len(signatures))
return utils.ErrInvalidQC return utils.ErrInvalidQC
@ -813,7 +826,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) 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) return fmt.Errorf("block not found, number: %v, hash: %v", incomingQuorumCert.ProposedBlockInfo.Number, incomingQuorumCert.ProposedBlockInfo.Hash)
} }
if proposedBlockHeader.Number.Cmp(x.config.V2.SwitchBlock) > 0 { if proposedBlockHeader.Number.Cmp(x.config.V2.FirstSwitchBlock) > 0 {
// Extra field contain parent information // Extra field contain parent information
proposedBlockQuorumCert, round, _, err := x.getExtraFields(proposedBlockHeader) proposedBlockQuorumCert, round, _, err := x.getExtraFields(proposedBlockHeader)
if err != nil { if err != nil {
@ -869,7 +882,7 @@ func (x *XDPoS_v2) getSyncInfo() *types.SyncInfo {
//Find parent and grandparent, check round number, if so, commit grandparent(grandGrandParent of currentBlock) //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) { 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 // XDPoS v1.0 switch to v2.0, skip commit
if big.NewInt(0).Sub(proposedBlockHeader.Number, big.NewInt(2)).Cmp(x.config.V2.SwitchBlock) <= 0 { if big.NewInt(0).Sub(proposedBlockHeader.Number, big.NewInt(2)).Cmp(x.config.V2.FirstSwitchBlock) <= 0 {
return false, nil return false, nil
} }
// Find the last two parent block and check their rounds are the continuous // Find the last two parent block and check their rounds are the continuous
@ -946,7 +959,7 @@ func (x *XDPoS_v2) calcMasternodes(chain consensus.ChainReader, blockNum *big.In
} }
candidates := snap.NextEpochMasterNodes candidates := snap.NextEpochMasterNodes
if blockNum.Uint64() == x.config.V2.SwitchBlock.Uint64()+1 { if blockNum.Uint64() == x.config.V2.FirstSwitchBlock.Uint64()+1 {
log.Info("[calcMasternodes] examing first v2 block") log.Info("[calcMasternodes] examing first v2 block")
return candidates, []common.Address{}, nil return candidates, []common.Address{}, nil
} }

View file

@ -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 // 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) { func (x *XDPoS_v2) isEpochSwitchAtRound(round types.Round, parentHeader *types.Header) (bool, uint64, error) {
epochNum := x.config.V2.SwitchBlock.Uint64()/x.config.Epoch + uint64(round)/x.config.Epoch epochNum := x.config.V2.FirstSwitchBlock.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 parent is last v1 block and this is first v2 block, this is treated as epoch switch
if parentHeader.Number.Cmp(x.config.V2.SwitchBlock) == 0 { if parentHeader.Number.Cmp(x.config.V2.FirstSwitchBlock) == 0 {
return true, epochNum, nil return true, epochNum, nil
} }
@ -111,13 +111,13 @@ func (x *XDPoS_v2) GetCurrentEpochSwitchBlock(chain consensus.ChainReader, block
} }
currentCheckpointNumber := epochSwitchInfo.EpochSwitchBlockInfo.Number.Uint64() currentCheckpointNumber := epochSwitchInfo.EpochSwitchBlockInfo.Number.Uint64()
epochNum := x.config.V2.SwitchBlock.Uint64()/x.config.Epoch + uint64(epochSwitchInfo.EpochSwitchBlockInfo.Round)/x.config.Epoch epochNum := x.config.V2.FirstSwitchBlock.Uint64()/x.config.Epoch + uint64(epochSwitchInfo.EpochSwitchBlockInfo.Round)/x.config.Epoch
return currentCheckpointNumber, epochNum, nil return currentCheckpointNumber, epochNum, nil
} }
func (x *XDPoS_v2) IsEpochSwitch(header *types.Header) (bool, uint64, error) { 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 // 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.SwitchBlock) == 0 { if header.Number.Cmp(x.config.V2.FirstSwitchBlock) == 0 {
log.Info("[IsEpochSwitch] examing last v1 block") log.Info("[IsEpochSwitch] examing last v1 block")
return true, header.Number.Uint64() / x.config.Epoch, nil 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 parentRound := quorumCert.ProposedBlockInfo.Round
epochStartRound := round - round%types.Round(x.config.Epoch) epochStartRound := round - round%types.Round(x.config.Epoch)
epochNum := x.config.V2.SwitchBlock.Uint64()/x.config.Epoch + uint64(round)/x.config.Epoch epochNum := x.config.V2.FirstSwitchBlock.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 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.SwitchBlock) == 0 { if quorumCert.ProposedBlockInfo.Number.Cmp(x.config.V2.FirstSwitchBlock) == 0 {
log.Info("[IsEpochSwitch] true, parent equals V2.SwitchBlock", "round", round, "number", header.Number.Uint64(), "hash", header.Hash()) log.Info("[IsEpochSwitch] true, parent equals V2.SwitchBlock", "round", round, "number", header.Number.Uint64(), "hash", header.Hash())
return true, epochNum, nil return true, epochNum, nil
} }

View file

@ -24,10 +24,11 @@ func (x *XDPoS_v2) timeoutHandler(blockChainReader consensus.ChainReader, timeou
} }
} }
// Collect timeout, generate TC // Collect timeout, generate TC
isThresholdReached, 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)
// Threshold reached // Threshold reached
isThresholdReached := numberOfTimeoutsInPool >= x.config.V2.CurrentConfig.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)
@ -94,7 +95,7 @@ func (x *XDPoS_v2) verifyTC(chain consensus.ChainReader, timeoutCert *types.Time
if timeoutCert == nil { if timeoutCert == nil {
log.Warn("[verifyTC] TC is Nil") log.Warn("[verifyTC] TC is Nil")
return utils.ErrInvalidTC return utils.ErrInvalidTC
} else if timeoutCert.Signatures == nil || (len(timeoutCert.Signatures) < x.config.V2.CertThreshold) { } 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)) 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 return utils.ErrInvalidTC
} }
@ -220,7 +221,7 @@ func (x *XDPoS_v2) OnCountdownTimeout(time time.Time, chain interface{}) error {
} }
x.timeoutCount++ x.timeoutCount++
if x.timeoutCount%x.config.V2.TimeoutSyncThreshold == 0 { if x.timeoutCount%x.config.V2.CurrentConfig.TimeoutSyncThreshold == 0 {
log.Warn("[OnCountdownTimeout] timeout sync threadhold reached, send syncInfo message") log.Warn("[OnCountdownTimeout] timeout sync threadhold reached, send syncInfo message")
syncInfo := x.getSyncInfo() syncInfo := x.getSyncInfo()
x.broadcastToBftChannel(syncInfo) x.broadcastToBftChannel(syncInfo)

View file

@ -119,7 +119,7 @@ func (x *XDPoS_v2) verifyMsgSignature(signedHashToBeVerified common.Hash, signat
} }
} }
return false, signerAddress, fmt.Errorf("Masternodes list does not contain signer address, Signer address: %v", signerAddress.Hex()) return false, signerAddress, nil
} }
func (x *XDPoS_v2) getExtraFields(header *types.Header) (*types.QuorumCert, types.Round, []common.Address, error) { func (x *XDPoS_v2) getExtraFields(header *types.Header) (*types.QuorumCert, types.Round, []common.Address, error) {
@ -127,7 +127,7 @@ func (x *XDPoS_v2) getExtraFields(header *types.Header) (*types.QuorumCert, type
var masternodes []common.Address var masternodes []common.Address
// last v1 block // last v1 block
if header.Number.Cmp(x.config.V2.SwitchBlock) == 0 { if header.Number.Cmp(x.config.V2.FirstSwitchBlock) == 0 {
masternodes = decodeMasternodesFromHeaderExtra(header) masternodes = decodeMasternodesFromHeaderExtra(header)
return nil, types.Round(0), masternodes, nil return nil, types.Round(0), masternodes, nil
} }

View file

@ -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 { if parent == nil || parent.Number.Uint64() != number-1 || parent.Hash() != header.ParentHash {
return consensus.ErrUnknownAncestor return consensus.ErrUnknownAncestor
} }
if parent.Number.Uint64() > x.config.V2.SwitchBlock.Uint64() && parent.Time.Uint64()+uint64(x.config.V2.MinePeriod) > header.Time.Uint64() { if parent.Number.Uint64() > x.config.V2.FirstSwitchBlock.Uint64() && parent.Time.Uint64()+uint64(x.config.V2.CurrentConfig.MinePeriod) > header.Time.Uint64() {
return utils.ErrInvalidTimestamp return utils.ErrInvalidTimestamp
} }

View file

@ -70,10 +70,12 @@ func (x *XDPoS_v2) voteHandler(chain consensus.ChainReader, voteMsg *types.Vote)
} }
// Collect vote // Collect vote
thresholdReached, numberOfVotesInPool, pooledVotes := x.votePool.Add(voteMsg) numberOfVotesInPool, pooledVotes := x.votePool.Add(voteMsg)
log.Debug("[voteHandler] collect votes", "number", numberOfVotesInPool) log.Debug("[voteHandler] collect votes", "number", numberOfVotesInPool)
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)
thresholdReached := numberOfVotesInPool >= x.config.V2.CurrentConfig.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))
@ -120,8 +122,10 @@ func (x *XDPoS_v2) onVotePoolThresholdReached(chain consensus.ChainReader, poole
ProposedBlockInfo: v.ProposedBlockInfo, ProposedBlockInfo: v.ProposedBlockInfo,
GapNumber: v.GapNumber, GapNumber: v.GapNumber,
}), v.Signature, masternodes) }), v.Signature, masternodes)
if !verified || err != nil { if err != nil {
log.Warn("[onVotePoolThresholdReached] Skip not verified vote signatures when building QC", "Error", err.Error(), "verified", verified) 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 { } else {
signatureSlice[i] = v.Signature signatureSlice[i] = v.Signature
} }
@ -141,7 +145,7 @@ func (x *XDPoS_v2) onVotePoolThresholdReached(chain consensus.ChainReader, poole
} }
// 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
if len(validSignatureSlice) < x.config.V2.CertThreshold { 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)) log.Warn("[onVotePoolThresholdReached] Not enough valid signatures to generate QC", "VotesSignaturesAfterFilter", validSignatureSlice, "NumberOfValidVotes", len(validSignatureSlice), "NumberOfVotes", len(pooledVotes))
return nil return nil
} }

View file

@ -11,20 +11,18 @@ type PoolObj interface {
PoolKey() string PoolKey() string
} }
type Pool struct { type Pool struct {
objList map[string]map[common.Hash]PoolObj objList map[string]map[common.Hash]PoolObj
threshold int lock sync.RWMutex // Protects the pool fields
lock sync.RWMutex // Protects the pool fields
} }
func NewPool(threshold int) *Pool { func NewPool() *Pool {
return &Pool{ return &Pool{
objList: make(map[string]map[common.Hash]PoolObj), objList: make(map[string]map[common.Hash]PoolObj),
threshold: threshold,
} }
} }
// return true if it has reached threshold // return true if it has reached threshold
func (p *Pool) Add(obj PoolObj) (bool, int, map[common.Hash]PoolObj) { func (p *Pool) Add(obj PoolObj) (int, map[common.Hash]PoolObj) {
p.lock.Lock() p.lock.Lock()
defer p.lock.Unlock() defer p.lock.Unlock()
poolKey := obj.PoolKey() poolKey := obj.PoolKey()
@ -35,10 +33,7 @@ func (p *Pool) Add(obj PoolObj) (bool, int, map[common.Hash]PoolObj) {
} }
objListKeyed[obj.Hash()] = obj objListKeyed[obj.Hash()] = obj
numOfItems := len(objListKeyed) numOfItems := len(objListKeyed)
if numOfItems >= p.threshold { return numOfItems, objListKeyed
return true, numOfItems, objListKeyed
}
return false, numOfItems, objListKeyed
} }
func (p *Pool) Size(obj PoolObj) int { func (p *Pool) Size(obj PoolObj) int {
@ -85,13 +80,6 @@ func (p *Pool) Clear() {
p.objList = make(map[string]map[common.Hash]PoolObj) p.objList = make(map[string]map[common.Hash]PoolObj)
} }
func (p *Pool) SetThreshold(t int) {
p.lock.Lock()
defer p.lock.Unlock()
p.threshold = t
}
func (p *Pool) GetObjsByKey(poolKey string) []PoolObj { func (p *Pool) GetObjsByKey(poolKey string) []PoolObj {
p.lock.Lock() p.lock.Lock()
defer p.lock.Unlock() defer p.lock.Unlock()

View file

@ -10,55 +10,50 @@ import (
func TestPoolAdd(t *testing.T) { func TestPoolAdd(t *testing.T) {
assert := assert.New(t) assert := assert.New(t)
pool := NewPool(2) // 2 is the cert threshold pool := NewPool()
timeout1 := types.Timeout{Round: 1, Signature: []byte{1}} timeout1 := types.Timeout{Round: 1, Signature: []byte{1}}
timeout2 := types.Timeout{Round: 1, Signature: []byte{2}} timeout2 := types.Timeout{Round: 1, Signature: []byte{2}}
timeout3 := types.Timeout{Round: 1, Signature: []byte{3}} timeout3 := types.Timeout{Round: 1, Signature: []byte{3}}
timeout4 := types.Timeout{Round: 1, Signature: []byte{4}} timeout4 := types.Timeout{Round: 1, Signature: []byte{4}}
thresholdReached, numOfItems, pooledTimeouts := pool.Add(&timeout1) numOfItems, pooledTimeouts := pool.Add(&timeout1)
assert.NotNil(pooledTimeouts) assert.NotNil(pooledTimeouts)
assert.Equal(1, numOfItems) assert.Equal(1, numOfItems)
assert.False(thresholdReached) numOfItems, pooledTimeouts = pool.Add(&timeout1)
thresholdReached, numOfItems, pooledTimeouts = pool.Add(&timeout1)
assert.NotNil(pooledTimeouts) assert.NotNil(pooledTimeouts)
assert.False(thresholdReached)
// Duplicates should not be added // Duplicates should not be added
assert.Equal(1, numOfItems) assert.Equal(1, numOfItems)
// Should add the one that is not a duplicates // Should add the one that is not a duplicates
thresholdReached, numOfItems, pooledTimeouts = pool.Add(&timeout2) numOfItems, pooledTimeouts = pool.Add(&timeout2)
assert.True(thresholdReached)
assert.NotNil(pooledTimeouts) assert.NotNil(pooledTimeouts)
assert.Equal(2, numOfItems) assert.Equal(2, numOfItems)
// Try to add one more to the same round, it should also trigger threshold // Try to add one more to the same round, it should also trigger threshold
thresholdReached, numOfItems, pooledTimeouts = pool.Add(&timeout3) numOfItems, pooledTimeouts = pool.Add(&timeout3)
assert.True(thresholdReached)
assert.NotNil(pooledTimeouts) assert.NotNil(pooledTimeouts)
assert.Equal(3, numOfItems) assert.Equal(3, numOfItems)
// Only after manually clearned the pool at its objKey, we shall not have any value for this particular key // Only after manually clearned the pool at its objKey, we shall not have any value for this particular key
pool.ClearPoolKeyByObj(&timeout3) pool.ClearPoolKeyByObj(&timeout3)
thresholdReached, numOfItems, pooledTimeouts = pool.Add(&timeout4) numOfItems, pooledTimeouts = pool.Add(&timeout4)
assert.False(thresholdReached)
assert.NotNil(pooledTimeouts) assert.NotNil(pooledTimeouts)
assert.Equal(1, numOfItems) assert.Equal(1, numOfItems)
pool = NewPool(3) // 3 is the cert size pool = NewPool()
thresholdReached, numOfItems, pooledTimeouts = pool.Add(&timeout1) numOfItems, pooledTimeouts = pool.Add(&timeout1)
assert.False(thresholdReached)
assert.NotNil(pooledTimeouts) assert.NotNil(pooledTimeouts)
assert.Equal(1, numOfItems) assert.Equal(1, numOfItems)
thresholdReached, numOfItems, pooledTimeouts = pool.Add(&timeout2) numOfItems, pooledTimeouts = pool.Add(&timeout2)
assert.False(thresholdReached)
assert.Equal(2, numOfItems) assert.Equal(2, numOfItems)
assert.NotNil(pooledTimeouts) assert.NotNil(pooledTimeouts)
pool.Clear() pool.Clear()
// Pool has been cleared. Start from 0 again // Pool has been cleared. Start from 0 again
thresholdReached, numOfItems, pooledTimeouts = pool.Add(&timeout3) numOfItems, pooledTimeouts = pool.Add(&timeout3)
assert.False(thresholdReached)
assert.Equal(1, numOfItems) assert.Equal(1, numOfItems)
assert.NotNil(pooledTimeouts) assert.NotNil(pooledTimeouts)
} }

View file

@ -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) // 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.SwitchBlock) == 0 || blockNumber%int(chainConfig.XDPoS.Epoch) == 0 { if big.NewInt(int64(blockNumber)).Cmp(chainConfig.XDPoS.V2.FirstSwitchBlock) == 0 || blockNumber%int(chainConfig.XDPoS.Epoch) == 0 {
// reset extra // reset extra
header.Extra = []byte{} header.Extra = []byte{}
if len(header.Extra) < utils.ExtraVanity { if len(header.Extra) < utils.ExtraVanity {

View file

@ -66,7 +66,7 @@ func TestAdaptorGetMasternodesFromCheckpointHeader(t *testing.T) {
headerV1.Extra = common.Hex2Bytes("d7830100018358444388676f312e31352e38856c696e757800000000000000000278c350152e15fa6ffc712a5a73d704ce73e2e103d9e17ae3ff2c6712e44e25b09ac5ee91f6c9ff065551f0dcac6f00cae11192d462db709be3758ccef312ee5eea8d7bad5374c6a652150515d744508b61c1a4deb4e4e7bf057e4e3824c11fd2569bcb77a52905cda63b5a58507910bed335e4c9d87ae0ecdfafd400") headerV1.Extra = common.Hex2Bytes("d7830100018358444388676f312e31352e38856c696e757800000000000000000278c350152e15fa6ffc712a5a73d704ce73e2e103d9e17ae3ff2c6712e44e25b09ac5ee91f6c9ff065551f0dcac6f00cae11192d462db709be3758ccef312ee5eea8d7bad5374c6a652150515d744508b61c1a4deb4e4e7bf057e4e3824c11fd2569bcb77a52905cda63b5a58507910bed335e4c9d87ae0ecdfafd400")
masternodesV1 := adaptor.GetMasternodesFromCheckpointHeader(headerV1) masternodesV1 := adaptor.GetMasternodesFromCheckpointHeader(headerV1)
headerV2 := currentBlock.Header() headerV2 := currentBlock.Header()
headerV2.Number.Add(blockchain.Config().XDPoS.V2.SwitchBlock, big.NewInt(1)) headerV2.Number.Add(blockchain.Config().XDPoS.V2.FirstSwitchBlock, big.NewInt(1))
headerV2.Validators = common.Hex2Bytes("0278c350152e15fa6ffc712a5a73d704ce73e2e103d9e17ae3ff2c6712e44e25b09ac5ee91f6c9ff065551f0dcac6f00cae11192d462db709be3758c") headerV2.Validators = common.Hex2Bytes("0278c350152e15fa6ffc712a5a73d704ce73e2e103d9e17ae3ff2c6712e44e25b09ac5ee91f6c9ff065551f0dcac6f00cae11192d462db709be3758c")
headerV2.Extra = []byte{2} headerV2.Extra = []byte{2}
masternodesV2 := adaptor.GetMasternodesFromCheckpointHeader(headerV2) masternodesV2 := adaptor.GetMasternodesFromCheckpointHeader(headerV2)
@ -91,12 +91,12 @@ func TestAdaptorIsEpochSwitch(t *testing.T) {
parentBlockInfo := &types.BlockInfo{ parentBlockInfo := &types.BlockInfo{
Hash: header.ParentHash, Hash: header.ParentHash,
Round: types.Round(0), Round: types.Round(0),
Number: big.NewInt(0).Set(blockchain.Config().XDPoS.V2.SwitchBlock), Number: big.NewInt(0).Set(blockchain.Config().XDPoS.V2.FirstSwitchBlock),
} }
quorumCert := &types.QuorumCert{ quorumCert := &types.QuorumCert{
ProposedBlockInfo: parentBlockInfo, ProposedBlockInfo: parentBlockInfo,
Signatures: nil, Signatures: nil,
GapNumber: blockchain.Config().XDPoS.V2.SwitchBlock.Uint64() - blockchain.Config().XDPoS.Gap, GapNumber: blockchain.Config().XDPoS.V2.FirstSwitchBlock.Uint64() - blockchain.Config().XDPoS.Gap,
} }
extra := types.ExtraFields_v2{ extra := types.ExtraFields_v2{
Round: 1, Round: 1,
@ -105,19 +105,19 @@ func TestAdaptorIsEpochSwitch(t *testing.T) {
extraBytes, err := extra.EncodeToBytes() extraBytes, err := extra.EncodeToBytes()
assert.Nil(t, err) assert.Nil(t, err)
header.Extra = extraBytes header.Extra = extraBytes
header.Number.Add(blockchain.Config().XDPoS.V2.SwitchBlock, big.NewInt(1)) header.Number.Add(blockchain.Config().XDPoS.V2.FirstSwitchBlock, big.NewInt(1))
isEpochSwitchBlock, _, err = adaptor.IsEpochSwitch(header) isEpochSwitchBlock, _, err = adaptor.IsEpochSwitch(header)
assert.Nil(t, err) assert.Nil(t, err)
assert.True(t, isEpochSwitchBlock, "header should be epoch switch", header) assert.True(t, isEpochSwitchBlock, "header should be epoch switch", header)
parentBlockInfo = &types.BlockInfo{ parentBlockInfo = &types.BlockInfo{
Hash: header.ParentHash, Hash: header.ParentHash,
Round: types.Round(1), Round: types.Round(1),
Number: big.NewInt(0).Add(blockchain.Config().XDPoS.V2.SwitchBlock, big.NewInt(1)), Number: big.NewInt(0).Add(blockchain.Config().XDPoS.V2.FirstSwitchBlock, big.NewInt(1)),
} }
quorumCert = &types.QuorumCert{ quorumCert = &types.QuorumCert{
ProposedBlockInfo: parentBlockInfo, ProposedBlockInfo: parentBlockInfo,
Signatures: nil, Signatures: nil,
GapNumber: blockchain.Config().XDPoS.V2.SwitchBlock.Uint64() - blockchain.Config().XDPoS.Gap, GapNumber: blockchain.Config().XDPoS.V2.FirstSwitchBlock.Uint64() - blockchain.Config().XDPoS.Gap,
} }
extra = types.ExtraFields_v2{ extra = types.ExtraFields_v2{
Round: 2, Round: 2,
@ -126,19 +126,19 @@ func TestAdaptorIsEpochSwitch(t *testing.T) {
extraBytes, err = extra.EncodeToBytes() extraBytes, err = extra.EncodeToBytes()
assert.Nil(t, err) assert.Nil(t, err)
header.Extra = extraBytes header.Extra = extraBytes
header.Number.Add(blockchain.Config().XDPoS.V2.SwitchBlock, big.NewInt(2)) header.Number.Add(blockchain.Config().XDPoS.V2.FirstSwitchBlock, big.NewInt(2))
isEpochSwitchBlock, _, err = adaptor.IsEpochSwitch(header) isEpochSwitchBlock, _, err = adaptor.IsEpochSwitch(header)
assert.Nil(t, err) assert.Nil(t, err)
assert.False(t, isEpochSwitchBlock, "header should not be epoch switch", header) assert.False(t, isEpochSwitchBlock, "header should not be epoch switch", header)
parentBlockInfo = &types.BlockInfo{ parentBlockInfo = &types.BlockInfo{
Hash: header.ParentHash, Hash: header.ParentHash,
Round: types.Round(blockchain.Config().XDPoS.Epoch) - 1, Round: types.Round(blockchain.Config().XDPoS.Epoch) - 1,
Number: big.NewInt(0).Add(blockchain.Config().XDPoS.V2.SwitchBlock, big.NewInt(100)), Number: big.NewInt(0).Add(blockchain.Config().XDPoS.V2.FirstSwitchBlock, big.NewInt(100)),
} }
quorumCert = &types.QuorumCert{ quorumCert = &types.QuorumCert{
ProposedBlockInfo: parentBlockInfo, ProposedBlockInfo: parentBlockInfo,
Signatures: nil, Signatures: nil,
GapNumber: blockchain.Config().XDPoS.V2.SwitchBlock.Uint64() - blockchain.Config().XDPoS.Gap, GapNumber: blockchain.Config().XDPoS.V2.FirstSwitchBlock.Uint64() - blockchain.Config().XDPoS.Gap,
} }
extra = types.ExtraFields_v2{ extra = types.ExtraFields_v2{
Round: types.Round(blockchain.Config().XDPoS.Epoch) + 1, Round: types.Round(blockchain.Config().XDPoS.Epoch) + 1,
@ -147,19 +147,19 @@ func TestAdaptorIsEpochSwitch(t *testing.T) {
extraBytes, err = extra.EncodeToBytes() extraBytes, err = extra.EncodeToBytes()
assert.Nil(t, err) assert.Nil(t, err)
header.Extra = extraBytes header.Extra = extraBytes
header.Number.Add(blockchain.Config().XDPoS.V2.SwitchBlock, big.NewInt(101)) header.Number.Add(blockchain.Config().XDPoS.V2.FirstSwitchBlock, big.NewInt(101))
isEpochSwitchBlock, _, err = adaptor.IsEpochSwitch(header) isEpochSwitchBlock, _, err = adaptor.IsEpochSwitch(header)
assert.Nil(t, err) assert.Nil(t, err)
assert.True(t, isEpochSwitchBlock, "header should be epoch switch", header) assert.True(t, isEpochSwitchBlock, "header should be epoch switch", header)
parentBlockInfo = &types.BlockInfo{ parentBlockInfo = &types.BlockInfo{
Hash: header.ParentHash, Hash: header.ParentHash,
Round: types.Round(blockchain.Config().XDPoS.Epoch) + 1, Round: types.Round(blockchain.Config().XDPoS.Epoch) + 1,
Number: big.NewInt(0).Add(blockchain.Config().XDPoS.V2.SwitchBlock, big.NewInt(100)), Number: big.NewInt(0).Add(blockchain.Config().XDPoS.V2.FirstSwitchBlock, big.NewInt(100)),
} }
quorumCert = &types.QuorumCert{ quorumCert = &types.QuorumCert{
ProposedBlockInfo: parentBlockInfo, ProposedBlockInfo: parentBlockInfo,
Signatures: nil, Signatures: nil,
GapNumber: blockchain.Config().XDPoS.V2.SwitchBlock.Uint64() - blockchain.Config().XDPoS.Gap, GapNumber: blockchain.Config().XDPoS.V2.FirstSwitchBlock.Uint64() - blockchain.Config().XDPoS.Gap,
} }
extra = types.ExtraFields_v2{ extra = types.ExtraFields_v2{
Round: types.Round(blockchain.Config().XDPoS.Epoch) + 2, Round: types.Round(blockchain.Config().XDPoS.Epoch) + 2,
@ -168,7 +168,7 @@ func TestAdaptorIsEpochSwitch(t *testing.T) {
extraBytes, err = extra.EncodeToBytes() extraBytes, err = extra.EncodeToBytes()
assert.Nil(t, err) assert.Nil(t, err)
header.Extra = extraBytes header.Extra = extraBytes
header.Number.Add(blockchain.Config().XDPoS.V2.SwitchBlock, big.NewInt(101)) header.Number.Add(blockchain.Config().XDPoS.V2.FirstSwitchBlock, big.NewInt(101))
isEpochSwitchBlock, _, err = adaptor.IsEpochSwitch(header) isEpochSwitchBlock, _, err = adaptor.IsEpochSwitch(header)
assert.Nil(t, err) assert.Nil(t, err)
assert.False(t, isEpochSwitchBlock, "header should not be epoch switch", header) assert.False(t, isEpochSwitchBlock, "header should not be epoch switch", header)

View file

@ -1,6 +1,7 @@
package engine_v2_tests package engine_v2_tests
import ( import (
"math/big"
"testing" "testing"
"time" "time"
@ -33,21 +34,23 @@ func TestIsAuthorisedMNForConsensusV2(t *testing.T) {
func TestIsYourTurnConsensusV2(t *testing.T) { func TestIsYourTurnConsensusV2(t *testing.T) {
// we skip test for v1 since it's hard to make a real genesis block // we skip test for v1 since it's hard to make a real genesis block
blockchain, _, currentBlock, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, 900, params.TestXDPoSMockChainConfig, nil) blockchain, _, currentBlock, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, 900, params.TestXDPoSMockChainConfig, nil)
minePeriod := params.TestXDPoSV2Config.MinePeriod minePeriod := params.TestV2Configs[0].MinePeriod
adaptor := blockchain.Engine().(*XDPoS.XDPoS) adaptor := blockchain.Engine().(*XDPoS.XDPoS)
blockNum := 901 blockNum := 901
blockCoinBase := "0x111000000000000000000000000000000123" blockCoinBase := "0x111000000000000000000000000000000123"
currentBlock = CreateBlock(blockchain, params.TestXDPoSMockChainConfig, currentBlock, blockNum, 1, blockCoinBase, signer, signFn, nil, nil) currentBlock = CreateBlock(blockchain, params.TestXDPoSMockChainConfig, currentBlock, blockNum, 1, blockCoinBase, signer, signFn, nil, nil)
currentBlockHeader := currentBlock.Header()
currentBlockHeader.Time = big.NewInt(time.Now().Unix())
err := blockchain.InsertBlock(currentBlock) err := blockchain.InsertBlock(currentBlock)
assert.Nil(t, err) assert.Nil(t, err)
// Less then Mine Period // Less then Mine Period
isYourTurn, err := adaptor.YourTurn(blockchain, currentBlock.Header(), common.HexToAddress("xdc0D3ab14BBaD3D99F4203bd7a11aCB94882050E7e")) isYourTurn, err := adaptor.YourTurn(blockchain, currentBlockHeader, common.HexToAddress("xdc0D3ab14BBaD3D99F4203bd7a11aCB94882050E7e"))
assert.Nil(t, err) assert.Nil(t, err)
assert.False(t, isYourTurn) assert.False(t, isYourTurn)
time.Sleep(time.Duration(minePeriod) * time.Second) time.Sleep(time.Duration(minePeriod) * time.Second)
// The second address is valid as the round starting from 1 // The second address is valid as the round starting from 1
isYourTurn, err = adaptor.YourTurn(blockchain, currentBlock.Header(), common.HexToAddress("xdc0D3ab14BBaD3D99F4203bd7a11aCB94882050E7e")) isYourTurn, err = adaptor.YourTurn(blockchain, currentBlockHeader, common.HexToAddress("xdc0D3ab14BBaD3D99F4203bd7a11aCB94882050E7e"))
assert.Nil(t, err) assert.Nil(t, err)
assert.True(t, isYourTurn) assert.True(t, isYourTurn)
@ -77,3 +80,30 @@ func TestIsYourTurnConsensusV2(t *testing.T) {
assert.False(t, isYourTurn) assert.False(t, isYourTurn)
} }
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)
firstMinePeriod := blockchain.Config().XDPoS.V2.CurrentConfig.MinePeriod
adaptor := blockchain.Engine().(*XDPoS.XDPoS)
blockNum := 911 // 911 is new config switch block
blockCoinBase := "0x111000000000000000000000000000000123"
currentBlock = CreateBlock(blockchain, params.TestXDPoSMockChainConfig, currentBlock, blockNum, 1, blockCoinBase, signer, signFn, nil, nil)
currentBlockHeader := currentBlock.Header()
currentBlockHeader.Time = big.NewInt(time.Now().Unix())
err := blockchain.InsertBlock(currentBlock)
assert.Nil(t, err)
// after first mine period
time.Sleep(time.Duration(firstMinePeriod) * time.Second)
isYourTurn, err := adaptor.YourTurn(blockchain, currentBlockHeader, common.HexToAddress("xdc0D3ab14BBaD3D99F4203bd7a11aCB94882050E7e"))
assert.Nil(t, err)
assert.False(t, isYourTurn)
// 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)
assert.True(t, isYourTurn)
}

View file

@ -177,11 +177,11 @@ func TestForensicsMonitoringNotOnSameChainButHaveSameRoundQC(t *testing.T) {
assert.Equal(t, types.Round(13), content.SmallerRoundInfo.QuorumCert.ProposedBlockInfo.Round) assert.Equal(t, types.Round(13), content.SmallerRoundInfo.QuorumCert.ProposedBlockInfo.Round)
assert.Equal(t, uint64(913), content.SmallerRoundInfo.QuorumCert.ProposedBlockInfo.Number.Uint64()) assert.Equal(t, uint64(913), content.SmallerRoundInfo.QuorumCert.ProposedBlockInfo.Number.Uint64())
assert.Equal(t, 9, len(content.SmallerRoundInfo.HashPath)) assert.Equal(t, 9, len(content.SmallerRoundInfo.HashPath))
assert.Equal(t, 4, len(content.SmallerRoundInfo.SignerAddresses)) assert.Equal(t, 5, len(content.SmallerRoundInfo.SignerAddresses))
assert.Equal(t, types.Round(13), content.LargerRoundInfo.QuorumCert.ProposedBlockInfo.Round) assert.Equal(t, types.Round(13), content.LargerRoundInfo.QuorumCert.ProposedBlockInfo.Round)
assert.Equal(t, uint64(912), content.LargerRoundInfo.QuorumCert.ProposedBlockInfo.Number.Uint64()) assert.Equal(t, uint64(912), content.LargerRoundInfo.QuorumCert.ProposedBlockInfo.Number.Uint64())
assert.Equal(t, 8, len(content.LargerRoundInfo.HashPath)) assert.Equal(t, 8, len(content.LargerRoundInfo.HashPath))
assert.Equal(t, 4, len(content.LargerRoundInfo.SignerAddresses)) assert.Equal(t, 5, len(content.LargerRoundInfo.SignerAddresses))
return return
case <-time.After(5 * time.Second): case <-time.After(5 * time.Second):
t.FailNow() t.FailNow()
@ -237,7 +237,7 @@ func TestForensicsMonitoringNotOnSameChainDoNotHaveSameRoundQC(t *testing.T) {
assert.Equal(t, types.Round(14), content.SmallerRoundInfo.QuorumCert.ProposedBlockInfo.Round) assert.Equal(t, types.Round(14), content.SmallerRoundInfo.QuorumCert.ProposedBlockInfo.Round)
assert.Equal(t, uint64(914), content.SmallerRoundInfo.QuorumCert.ProposedBlockInfo.Number.Uint64()) assert.Equal(t, uint64(914), content.SmallerRoundInfo.QuorumCert.ProposedBlockInfo.Number.Uint64())
assert.Equal(t, 10, len(content.SmallerRoundInfo.HashPath)) assert.Equal(t, 10, len(content.SmallerRoundInfo.HashPath))
assert.Equal(t, 4, len(content.SmallerRoundInfo.SignerAddresses)) assert.Equal(t, 5, len(content.SmallerRoundInfo.SignerAddresses))
assert.Equal(t, types.Round(16), content.LargerRoundInfo.QuorumCert.ProposedBlockInfo.Round) assert.Equal(t, types.Round(16), content.LargerRoundInfo.QuorumCert.ProposedBlockInfo.Round)
assert.Equal(t, uint64(906), content.LargerRoundInfo.QuorumCert.ProposedBlockInfo.Number.Uint64()) assert.Equal(t, uint64(906), content.LargerRoundInfo.QuorumCert.ProposedBlockInfo.Number.Uint64())
assert.Equal(t, 2, len(content.LargerRoundInfo.HashPath)) assert.Equal(t, 2, len(content.LargerRoundInfo.HashPath))
@ -300,7 +300,7 @@ func TestForensicsAcrossEpoch(t *testing.T) {
assert.Equal(t, types.Round(900), content.SmallerRoundInfo.QuorumCert.ProposedBlockInfo.Round) assert.Equal(t, types.Round(900), content.SmallerRoundInfo.QuorumCert.ProposedBlockInfo.Round)
assert.Equal(t, uint64(1800), content.SmallerRoundInfo.QuorumCert.ProposedBlockInfo.Number.Uint64()) assert.Equal(t, uint64(1800), content.SmallerRoundInfo.QuorumCert.ProposedBlockInfo.Number.Uint64())
assert.Equal(t, 10, len(content.SmallerRoundInfo.HashPath)) assert.Equal(t, 10, len(content.SmallerRoundInfo.HashPath))
assert.Equal(t, 4, len(content.SmallerRoundInfo.SignerAddresses)) assert.Equal(t, 5, len(content.SmallerRoundInfo.SignerAddresses))
assert.Equal(t, types.Round(902), content.LargerRoundInfo.QuorumCert.ProposedBlockInfo.Round) assert.Equal(t, types.Round(902), content.LargerRoundInfo.QuorumCert.ProposedBlockInfo.Round)
assert.Equal(t, uint64(1792), content.LargerRoundInfo.QuorumCert.ProposedBlockInfo.Number.Uint64()) assert.Equal(t, uint64(1792), content.LargerRoundInfo.QuorumCert.ProposedBlockInfo.Number.Uint64())
assert.Equal(t, 2, len(content.LargerRoundInfo.HashPath)) assert.Equal(t, 2, len(content.LargerRoundInfo.HashPath))

View file

@ -41,6 +41,8 @@ var (
acc1Key, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a") acc1Key, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a")
acc2Key, _ = crypto.HexToECDSA("49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee") acc2Key, _ = crypto.HexToECDSA("49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee")
acc3Key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") acc3Key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
acc4Key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f292")
acc5Key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f293")
voterKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee04aefe388d1e14474d32c45c72ce7b7a") voterKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee04aefe388d1e14474d32c45c72ce7b7a")
acc1Addr = crypto.PubkeyToAddress(acc1Key.PublicKey) //xdc703c4b2bD70c169f5717101CaeE543299Fc946C7 acc1Addr = crypto.PubkeyToAddress(acc1Key.PublicKey) //xdc703c4b2bD70c169f5717101CaeE543299Fc946C7
acc2Addr = crypto.PubkeyToAddress(acc2Key.PublicKey) //xdc0D3ab14BBaD3D99F4203bd7a11aCB94882050E7e acc2Addr = crypto.PubkeyToAddress(acc2Key.PublicKey) //xdc0D3ab14BBaD3D99F4203bd7a11aCB94882050E7e
@ -310,10 +312,10 @@ func PrepareXDCTestBlockChainForV2Engine(t *testing.T, numOfBlocks int, chainCon
for i := 1; i <= numOfBlocks; i++ { for i := 1; i <= numOfBlocks; i++ {
blockCoinBase := fmt.Sprintf("0x111000000000000000000000000000000%03d", i) blockCoinBase := fmt.Sprintf("0x111000000000000000000000000000000%03d", i)
// for v2 blocks, fill in correct coinbase // for v2 blocks, fill in correct coinbase
if int64(i) > chainConfig.XDPoS.V2.SwitchBlock.Int64() { if int64(i) > chainConfig.XDPoS.V2.FirstSwitchBlock.Int64() {
blockCoinBase = signer.Hex() blockCoinBase = signer.Hex()
} }
roundNumber := int64(i) - chainConfig.XDPoS.V2.SwitchBlock.Int64() roundNumber := int64(i) - chainConfig.XDPoS.V2.FirstSwitchBlock.Int64()
block := CreateBlock(blockchain, chainConfig, currentBlock, i, roundNumber, blockCoinBase, signer, signFn, nil, nil) block := CreateBlock(blockchain, chainConfig, currentBlock, i, roundNumber, blockCoinBase, signer, signFn, nil, nil)
err = blockchain.InsertBlock(block) err = blockchain.InsertBlock(block)
@ -346,7 +348,7 @@ func PrepareXDCTestBlockChainForV2Engine(t *testing.T, numOfBlocks int, chainCon
} }
// First v2 block // First v2 block
if (int64(i) - chainConfig.XDPoS.V2.SwitchBlock.Int64()) == 1 { if (int64(i) - chainConfig.XDPoS.V2.FirstSwitchBlock.Int64()) == 1 {
lastv1BlockNumber := block.Header().Number.Uint64() - 1 lastv1BlockNumber := block.Header().Number.Uint64() - 1
checkpointBlockNumber := lastv1BlockNumber - lastv1BlockNumber%chainConfig.XDPoS.Epoch checkpointBlockNumber := lastv1BlockNumber - lastv1BlockNumber%chainConfig.XDPoS.Epoch
checkpointHeader := blockchain.GetHeaderByNumber(checkpointBlockNumber) checkpointHeader := blockchain.GetHeaderByNumber(checkpointBlockNumber)
@ -396,10 +398,10 @@ func PrepareXDCTestBlockChainWithPenaltyForV2Engine(t *testing.T, numOfBlocks in
for i := 1; i <= numOfBlocks; i++ { for i := 1; i <= numOfBlocks; i++ {
blockCoinBase := fmt.Sprintf("0x111000000000000000000000000000000%03d", i) blockCoinBase := fmt.Sprintf("0x111000000000000000000000000000000%03d", i)
// for v2 blocks, fill in correct coinbase // for v2 blocks, fill in correct coinbase
if int64(i) > chainConfig.XDPoS.V2.SwitchBlock.Int64() { if int64(i) > chainConfig.XDPoS.V2.FirstSwitchBlock.Int64() {
blockCoinBase = signer.Hex() blockCoinBase = signer.Hex()
} }
roundNumber := int64(i) - chainConfig.XDPoS.V2.SwitchBlock.Int64() roundNumber := int64(i) - chainConfig.XDPoS.V2.FirstSwitchBlock.Int64()
// use signer itself as penalty // use signer itself as penalty
block := CreateBlock(blockchain, chainConfig, currentBlock, i, roundNumber, blockCoinBase, signer, signFn, signer[:], nil) block := CreateBlock(blockchain, chainConfig, currentBlock, i, roundNumber, blockCoinBase, signer, signFn, signer[:], nil)
@ -424,7 +426,7 @@ func CreateBlock(blockchain *BlockChain, chainConfig *params.ChainConfig, starti
merkleRoot := "35999dded35e8db12de7e6c1471eb9670c162eec616ecebbaf4fddd4676fb930" merkleRoot := "35999dded35e8db12de7e6c1471eb9670c162eec616ecebbaf4fddd4676fb930"
var header *types.Header var header *types.Header
if big.NewInt(int64(blockNumber)).Cmp(chainConfig.XDPoS.V2.SwitchBlock) == 1 { // Build engine v2 compatible extra data field if big.NewInt(int64(blockNumber)).Cmp(chainConfig.XDPoS.V2.FirstSwitchBlock) == 1 { // Build engine v2 compatible extra data field
extraInBytes := generateV2Extra(roundNumber, currentBlock, signer, signFn, signersKey) extraInBytes := generateV2Extra(roundNumber, currentBlock, signer, signFn, signersKey)
header = &types.Header{ header = &types.Header{
@ -434,9 +436,9 @@ func CreateBlock(blockchain *BlockChain, chainConfig *params.ChainConfig, starti
Coinbase: common.HexToAddress(blockCoinBase), Coinbase: common.HexToAddress(blockCoinBase),
Extra: extraInBytes, Extra: extraInBytes,
} }
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 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
// Get last master node list from last v1 block // Get last master node list from last v1 block
lastv1Block := blockchain.GetBlockByNumber(chainConfig.XDPoS.V2.SwitchBlock.Uint64()) lastv1Block := blockchain.GetBlockByNumber(chainConfig.XDPoS.V2.FirstSwitchBlock.Uint64())
masternodesFromV1LastEpoch := decodeMasternodesFromHeaderExtra(lastv1Block.Header()) masternodesFromV1LastEpoch := decodeMasternodesFromHeaderExtra(lastv1Block.Header())
for _, v := range masternodesFromV1LastEpoch { for _, v := range masternodesFromV1LastEpoch {
header.Validators = append(header.Validators, v[:]...) header.Validators = append(header.Validators, v[:]...)
@ -444,7 +446,7 @@ func CreateBlock(blockchain *BlockChain, chainConfig *params.ChainConfig, starti
} else if roundNumber%int64(chainConfig.XDPoS.Epoch) == 0 { } else if roundNumber%int64(chainConfig.XDPoS.Epoch) == 0 {
// epoch switch blocks, copy the master node list and inject into v2 validators // epoch switch blocks, copy the master node list and inject into v2 validators
// Get last master node list from last v1 block // Get last master node list from last v1 block
lastv1Block := blockchain.GetBlockByNumber(chainConfig.XDPoS.V2.SwitchBlock.Uint64()) lastv1Block := blockchain.GetBlockByNumber(chainConfig.XDPoS.V2.FirstSwitchBlock.Uint64())
masternodesFromV1LastEpoch := decodeMasternodesFromHeaderExtra(lastv1Block.Header()) masternodesFromV1LastEpoch := decodeMasternodesFromHeaderExtra(lastv1Block.Header())
for _, v := range masternodesFromV1LastEpoch { for _, v := range masternodesFromV1LastEpoch {
header.Validators = append(header.Validators, v[:]...) header.Validators = append(header.Validators, v[:]...)
@ -463,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) // 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.SwitchBlock) == 0 || blockNumber%int(chainConfig.XDPoS.Epoch) == 0 { if big.NewInt(int64(blockNumber)).Cmp(chainConfig.XDPoS.V2.FirstSwitchBlock) == 0 || blockNumber%int(chainConfig.XDPoS.Epoch) == 0 {
// reset extra // reset extra
header.Extra = []byte{} header.Extra = []byte{}
if len(header.Extra) < utils.ExtraVanity { if len(header.Extra) < utils.ExtraVanity {
@ -522,7 +524,7 @@ func createBlockFromHeader(bc *BlockChain, customHeader *types.Header, txs []*ty
Difficulty: difficulty, Difficulty: difficulty,
Number: customHeader.Number, Number: customHeader.Number,
GasLimit: 1200000000, GasLimit: 1200000000,
Time: big.NewInt(time.Now().Unix()), Time: big.NewInt(time.Now().Unix() - 1000000 + int64(customHeader.Number.Uint64()*10)),
Extra: customHeader.Extra, Extra: customHeader.Extra,
Validator: customHeader.Validator, Validator: customHeader.Validator,
Validators: customHeader.Validators, Validators: customHeader.Validators,
@ -582,7 +584,7 @@ func findSignerAndSignFn(bc *BlockChain, header *types.Header, signer common.Add
addressedSignFn := signFn 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 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.SwitchBlock) > 0 { if header.Number.Cmp(config.XDPoS.V2.FirstSwitchBlock) > 0 {
var decodedExtraField types.ExtraFields_v2 var decodedExtraField types.ExtraFields_v2
err := utils.DecodeBytesExtraFields(header.Extra, &decodedExtraField) err := utils.DecodeBytesExtraFields(header.Extra, &decodedExtraField)
if err != nil { if err != nil {
@ -659,7 +661,7 @@ func generateV2Extra(roundNumber int64, currentBlock *types.Block, signer common
var signatures []types.Signature var signatures []types.Signature
if len(accKeys) == 0 { if len(accKeys) == 0 {
// Sign from acc 1, 2, 3 by default // Sign from acc 1, 2, 3 by default
accKeys = append(accKeys, acc1Key, acc2Key, acc3Key) accKeys = append(accKeys, acc1Key, acc2Key, acc3Key, voterKey)
} }
for _, acc := range accKeys { for _, acc := range accKeys {
h := SignHashByPK(acc, types.VoteSigHash(voteForSign).Bytes()) h := SignHashByPK(acc, types.VoteSigHash(voteForSign).Bytes())

View file

@ -32,7 +32,7 @@ func TestInitialFirstV2Blcok(t *testing.T) {
expectedQuorumCert := &types.QuorumCert{ expectedQuorumCert := &types.QuorumCert{
ProposedBlockInfo: blockInfo, ProposedBlockInfo: blockInfo,
Signatures: nil, Signatures: nil,
GapNumber: blockchain.Config().XDPoS.V2.SwitchBlock.Uint64() - blockchain.Config().XDPoS.Gap, GapNumber: blockchain.Config().XDPoS.V2.FirstSwitchBlock.Uint64() - blockchain.Config().XDPoS.Gap,
} }
assert.Equal(t, types.Round(1), round) assert.Equal(t, types.Round(1), round)
assert.Equal(t, expectedQuorumCert, highQC) assert.Equal(t, expectedQuorumCert, highQC)
@ -43,10 +43,10 @@ func TestInitialFirstV2Blcok(t *testing.T) {
assert.Equal(t, uint64(450), snap.Number) assert.Equal(t, uint64(450), snap.Number)
// Test Running channels // Test Running channels
WaitPeriod := <-adaptor.WaitPeriodCh waitPeriod := <-adaptor.WaitPeriodCh
assert.Equal(t, params.TestXDPoSMockChainConfig.XDPoS.V2.WaitPeriod, WaitPeriod) assert.Equal(t, params.TestXDPoSMockChainConfig.XDPoS.V2.CurrentConfig.WaitPeriod, waitPeriod)
t.Logf("Waiting %d secs for timeout to happen", params.TestXDPoSMockChainConfig.XDPoS.V2.TimeoutPeriod) t.Logf("Waiting %d secs for timeout to happen", params.TestXDPoSMockChainConfig.XDPoS.V2.CurrentConfig.TimeoutPeriod)
timeoutMsg := <-adaptor.EngineV2.BroadcastCh timeoutMsg := <-adaptor.EngineV2.BroadcastCh
assert.NotNil(t, timeoutMsg) assert.NotNil(t, timeoutMsg)
assert.Equal(t, types.Round(1), timeoutMsg.(*types.Timeout).Round) assert.Equal(t, types.Round(1), timeoutMsg.(*types.Timeout).Round)
@ -104,7 +104,7 @@ func TestInitialOtherV2Block(t *testing.T) {
expectedQuorumCert := &types.QuorumCert{ expectedQuorumCert := &types.QuorumCert{
ProposedBlockInfo: blockInfo, ProposedBlockInfo: blockInfo,
Signatures: []types.Signature{}, Signatures: []types.Signature{},
GapNumber: blockchain.Config().XDPoS.V2.SwitchBlock.Uint64() - blockchain.Config().XDPoS.Gap, GapNumber: blockchain.Config().XDPoS.V2.FirstSwitchBlock.Uint64() - blockchain.Config().XDPoS.Gap,
} }
assert.Equal(t, types.Round(11), round) assert.Equal(t, types.Round(11), round)
assert.Equal(t, expectedQuorumCert, highQC) assert.Equal(t, expectedQuorumCert, highQC)

View file

@ -18,7 +18,7 @@ import (
func TestYourTurnInitialV2(t *testing.T) { func TestYourTurnInitialV2(t *testing.T) {
config := params.TestXDPoSMockChainConfig config := params.TestXDPoSMockChainConfig
blockchain, _, parentBlock, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, int(config.XDPoS.Epoch)-1, config, nil) blockchain, _, parentBlock, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, int(config.XDPoS.Epoch)-1, config, nil)
minePeriod := config.XDPoS.V2.MinePeriod minePeriod := config.XDPoS.V2.CurrentConfig.MinePeriod
adaptor := blockchain.Engine().(*XDPoS.XDPoS) adaptor := blockchain.Engine().(*XDPoS.XDPoS)
// Insert block 900 // Insert block 900
@ -64,7 +64,7 @@ func TestShouldMineOncePerRound(t *testing.T) {
config := params.TestXDPoSMockChainConfig config := params.TestXDPoSMockChainConfig
blockchain, _, block910, signer, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 910, config, nil) blockchain, _, block910, signer, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 910, config, nil)
adaptor := blockchain.Engine().(*XDPoS.XDPoS) adaptor := blockchain.Engine().(*XDPoS.XDPoS)
minePeriod := config.XDPoS.V2.MinePeriod minePeriod := config.XDPoS.V2.CurrentConfig.MinePeriod
// Make sure we seal the parentBlock 910 // Make sure we seal the parentBlock 910
_, err := adaptor.Seal(blockchain, block910, nil) _, err := adaptor.Seal(blockchain, block910, nil)

View file

@ -261,7 +261,7 @@ func TestShouldNotSendVoteMessageIfAlreadyVoteForThisRound(t *testing.T) {
select { select {
case <-engineV2.BroadcastCh: case <-engineV2.BroadcastCh:
t.Fatal("Should not trigger vote") t.Fatal("Should not trigger vote")
case <-time.After(5 * time.Second): case <-time.After(3 * time.Second):
// Shoud not trigger setNewRound // Shoud not trigger setNewRound
round, _, _, _, highestVotedRound, _ = engineV2.GetPropertiesFaker() round, _, _, _, highestVotedRound, _ = engineV2.GetPropertiesFaker()
assert.Equal(t, types.Round(6), round) assert.Equal(t, types.Round(6), round)
@ -290,7 +290,7 @@ func TestShouldNotSendVoteMsgIfBlockInfoRoundNotEqualCurrentRound(t *testing.T)
select { select {
case <-engineV2.BroadcastCh: case <-engineV2.BroadcastCh:
t.Fatal("Should not trigger vote") t.Fatal("Should not trigger vote")
case <-time.After(5 * time.Second): case <-time.After(3 * time.Second):
// Shoud not trigger setNewRound // Shoud not trigger setNewRound
round, _, _, _, _, _ := engineV2.GetPropertiesFaker() round, _, _, _, _, _ := engineV2.GetPropertiesFaker()
assert.Equal(t, types.Round(8), round) assert.Equal(t, types.Round(8), round)
@ -334,7 +334,7 @@ func TestShouldNotSendVoteMsgIfBlockNotExtendedFromAncestor(t *testing.T) {
select { select {
case <-engineV2.BroadcastCh: case <-engineV2.BroadcastCh:
t.Fatal("Should not trigger vote") t.Fatal("Should not trigger vote")
case <-time.After(5 * time.Second): case <-time.After(3 * time.Second):
// Shoud not trigger setNewRound // Shoud not trigger setNewRound
round, _, _, _, _, _ := engineV2.GetPropertiesFaker() round, _, _, _, _, _ := engineV2.GetPropertiesFaker()
assert.Equal(t, types.Round(7), round) assert.Equal(t, types.Round(7), round)

View file

@ -23,7 +23,7 @@ func TestHookRewardV2(t *testing.T) {
err = json.Unmarshal([]byte(configString), &config) err = json.Unmarshal([]byte(configString), &config)
assert.Nil(t, err) assert.Nil(t, err)
// set switch to 1800, so that it covers 901-1799, 1800-2700 two epochs // set switch to 1800, so that it covers 901-1799, 1800-2700 two epochs
config.XDPoS.V2.SwitchBlock.SetUint64(1800) config.XDPoS.V2.FirstSwitchBlock.SetUint64(1800)
blockchain, _, _, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, int(config.XDPoS.Epoch)*5, &config, nil) 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) err = json.Unmarshal([]byte(configString), &config)
assert.Nil(t, err) assert.Nil(t, err)
// set switch to 1800, so that it covers 901-1799, 1800-2700 two epochs // set switch to 1800, so that it covers 901-1799, 1800-2700 two epochs
config.XDPoS.V2.SwitchBlock.SetUint64(1800) config.XDPoS.V2.FirstSwitchBlock.SetUint64(1800)
blockchain, _, _, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, int(config.XDPoS.Epoch)*3, &config, nil) blockchain, _, _, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, int(config.XDPoS.Epoch)*3, &config, nil)

View file

@ -1,6 +1,7 @@
package engine_v2_tests package engine_v2_tests
import ( import (
"math/big"
"strconv" "strconv"
"strings" "strings"
"testing" "testing"
@ -41,11 +42,11 @@ func TestCountdownTimeoutNotToSendTimeoutMessageIfNotInMasternodeList(t *testing
select { select {
case <-engineV2.BroadcastCh: case <-engineV2.BroadcastCh:
t.Fatalf("Not suppose to receive timeout msg") t.Fatalf("Not suppose to receive timeout msg")
case <-time.After(15 * time.Second): //Countdown is only 1s wait, let's wait for 3s here case <-time.After(10 * time.Second): //Countdown is only 1s wait, let's wait for 3s here
} }
} }
func TestSyncInfoAfterReachTimeoutSnycThreadhold(t *testing.T) { func TestSyncInfoAfterReachTimeoutSyncThreadhold(t *testing.T) {
blockchain, _, _, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 901, params.TestXDPoSMockChainConfig, nil) blockchain, _, _, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 901, params.TestXDPoSMockChainConfig, nil)
engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2 engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2
engineV2.SetNewRoundFaker(blockchain, 1, true) engineV2.SetNewRoundFaker(blockchain, 1, true)
@ -83,8 +84,63 @@ func TestSyncInfoAfterReachTimeoutSnycThreadhold(t *testing.T) {
assert.Equal(t, 2, syncInfoCounter) assert.Equal(t, 2, syncInfoCounter)
} }
func TestTimeoutPeriodAndThreadholdConfigChange(t *testing.T) {
blockchain, _, currentBlock, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, 1799, params.TestXDPoSMockChainConfig, nil)
engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2
// engineV2.SetNewRoundFaker(blockchain, 1, true)
// Because messages are sending async and on random order, so use this way to test
var timeoutCounter, syncInfoCounter int
for i := 0; i < 3; i++ {
obj := <-engineV2.BroadcastCh
switch v := obj.(type) {
case *types.Timeout:
timeoutCounter++
case *types.SyncInfo:
syncInfoCounter++
default:
log.Error("Unknown message type received", "value", v)
}
}
assert.Equal(t, 2, timeoutCounter)
assert.Equal(t, 1, syncInfoCounter)
// Create another block to trigger update parameters
blockNum := 1800
blockCoinBase := "0x111000000000000000000000000000000123"
currentBlock = CreateBlock(blockchain, params.TestXDPoSMockChainConfig, currentBlock, blockNum, 900, blockCoinBase, signer, signFn, nil, nil)
currentBlockHeader := currentBlock.Header()
currentBlockHeader.Time = big.NewInt(time.Now().Unix())
err := blockchain.InsertBlock(currentBlock)
assert.Nil(t, err)
engineV2.UpdateParams() // it will be triggered automatically on the real code by other process
t.Log("waiting for another consecutive period")
// another consecutive period
t1 := time.Now()
for i := 0; i < 5; i++ {
obj := <-engineV2.BroadcastCh
switch v := obj.(type) {
case *types.Timeout:
timeoutCounter++
case *types.SyncInfo:
syncInfoCounter++
default:
log.Error("Unknown message type received", "value", v)
}
}
t2 := time.Now()
timediff := t2.Sub(t1).Seconds()
assert.Equal(t, 6, timeoutCounter)
assert.Equal(t, 2, syncInfoCounter)
assert.Less(t, timediff, float64(20))
}
// Timeout handler // Timeout handler
func TestTimeoutMessageHandlerSuccessfullyGenerateTCandSyncInfo(t *testing.T) { func TestTimeoutMessageHandlerSuccessfullyGenerateTCandSyncInfo(t *testing.T) {
params.TestXDPoSMockChainConfig.XDPoS.V2.CurrentConfig = params.TestV2Configs[0]
blockchain, _, _, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 11, params.TestXDPoSMockChainConfig, nil) blockchain, _, _, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 11, params.TestXDPoSMockChainConfig, nil)
engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2 engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2

View file

@ -27,8 +27,6 @@ func TestShouldVerifyBlock(t *testing.T) {
assert.Nil(t, err) assert.Nil(t, err)
// Enable verify // Enable verify
config.XDPoS.V2.SkipV2Validation = false config.XDPoS.V2.SkipV2Validation = false
// Skip the mining time validation by set mine time to 0
config.XDPoS.V2.MinePeriod = 0
// Block 901 is the first v2 block with round of 1 // Block 901 is the first v2 block with round of 1
blockchain, _, _, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, 910, &config, nil) blockchain, _, _, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, 910, &config, nil)
adaptor := blockchain.Engine().(*XDPoS.XDPoS) adaptor := blockchain.Engine().(*XDPoS.XDPoS)
@ -97,8 +95,9 @@ func TestShouldVerifyBlock(t *testing.T) {
err = adaptor.VerifyHeader(blockchain, parentNotExistBlock, true) err = adaptor.VerifyHeader(blockchain, parentNotExistBlock, true)
assert.Equal(t, consensus.ErrUnknownAncestor, err) assert.Equal(t, consensus.ErrUnknownAncestor, err)
block901 := blockchain.GetBlockByNumber(901).Header()
tooFastMinedBlock := blockchain.GetBlockByNumber(902).Header() tooFastMinedBlock := blockchain.GetBlockByNumber(902).Header()
tooFastMinedBlock.Time = big.NewInt(time.Now().Unix() - 10) tooFastMinedBlock.Time = big.NewInt(block901.Time.Int64() - 10)
err = adaptor.VerifyHeader(blockchain, tooFastMinedBlock, true) err = adaptor.VerifyHeader(blockchain, tooFastMinedBlock, true)
assert.Equal(t, utils.ErrInvalidTimestamp, err) assert.Equal(t, utils.ErrInvalidTimestamp, err)
@ -107,7 +106,7 @@ func TestShouldVerifyBlock(t *testing.T) {
err = adaptor.VerifyHeader(blockchain, invalidDifficultyBlock, true) err = adaptor.VerifyHeader(blockchain, invalidDifficultyBlock, true)
assert.Equal(t, utils.ErrInvalidDifficulty, err) assert.Equal(t, utils.ErrInvalidDifficulty, err)
// Creat an invalid QC round // Create an invalid QC round
proposedBlockInfo := &types.BlockInfo{ proposedBlockInfo := &types.BlockInfo{
Hash: blockchain.GetBlockByNumber(902).Hash(), Hash: blockchain.GetBlockByNumber(902).Hash(),
Round: types.Round(2), Round: types.Round(2),
@ -169,6 +168,62 @@ func TestShouldVerifyBlock(t *testing.T) {
assert.Equal(t, utils.ErrPenaltiesNotLegit, err) assert.Equal(t, utils.ErrPenaltiesNotLegit, err)
} }
func TestConfigSwitchOnDifferentCertThreshold(t *testing.T) {
b, err := json.Marshal(params.TestXDPoSMockChainConfig)
assert.Nil(t, err)
configString := string(b)
var config params.ChainConfig
err = json.Unmarshal([]byte(configString), &config)
assert.Nil(t, err)
// Enable verify
config.XDPoS.V2.SkipV2Validation = false
// Block 901 is the first v2 block with round of 1
blockchain, _, _, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 915, &config, nil)
adaptor := blockchain.Engine().(*XDPoS.XDPoS)
// Genrate QC
proposedBlockInfo := &types.BlockInfo{
Hash: blockchain.GetBlockByNumber(911).Hash(),
Round: types.Round(11),
Number: blockchain.GetBlockByNumber(911).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 signatures []types.Signature
signatures = append(signatures, acc1SignedHash, acc2SignedHash, acc3SignedHash)
quorumCert := &types.QuorumCert{
ProposedBlockInfo: proposedBlockInfo,
Signatures: signatures,
GapNumber: 450,
}
extra := types.ExtraFields_v2{
Round: types.Round(12),
QuorumCert: quorumCert,
}
extraInBytes, err := extra.EncodeToBytes()
if err != nil {
panic(fmt.Errorf("Error encode extra into bytes: %v", err))
}
// 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)
}
func TestShouldFailIfNotEnoughQCSignatures(t *testing.T) { func TestShouldFailIfNotEnoughQCSignatures(t *testing.T) {
b, err := json.Marshal(params.TestXDPoSMockChainConfig) b, err := json.Marshal(params.TestXDPoSMockChainConfig)
assert.Nil(t, err) assert.Nil(t, err)
@ -179,8 +234,6 @@ func TestShouldFailIfNotEnoughQCSignatures(t *testing.T) {
assert.Nil(t, err) assert.Nil(t, err)
// Enable verify // Enable verify
config.XDPoS.V2.SkipV2Validation = false config.XDPoS.V2.SkipV2Validation = false
// Skip the mining time validation by set mine time to 0
config.XDPoS.V2.MinePeriod = 0
// Block 901 is the first v2 block with round of 1 // Block 901 is the first v2 block with round of 1
blockchain, _, currentBlock, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, 902, &config, nil) blockchain, _, currentBlock, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, 902, &config, nil)
adaptor := blockchain.Engine().(*XDPoS.XDPoS) adaptor := blockchain.Engine().(*XDPoS.XDPoS)
@ -232,8 +285,6 @@ func TestShouldVerifyHeaders(t *testing.T) {
assert.Nil(t, err) assert.Nil(t, err)
// Enable verify // Enable verify
config.XDPoS.V2.SkipV2Validation = false config.XDPoS.V2.SkipV2Validation = false
// Skip the mining time validation by set mine time to 0
config.XDPoS.V2.MinePeriod = 0
// Block 901 is the first v2 block with round of 1 // Block 901 is the first v2 block with round of 1
blockchain, _, _, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 910, &config, nil) blockchain, _, _, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 910, &config, nil)
adaptor := blockchain.Engine().(*XDPoS.XDPoS) adaptor := blockchain.Engine().(*XDPoS.XDPoS)
@ -273,8 +324,6 @@ func TestShouldVerifyHeadersEvenIfParentsNotYetWrittenIntoDB(t *testing.T) {
assert.Nil(t, err) assert.Nil(t, err)
// Enable verify // Enable verify
config.XDPoS.V2.SkipV2Validation = false config.XDPoS.V2.SkipV2Validation = false
// Skip the mining time validation by set mine time to 0
config.XDPoS.V2.MinePeriod = 0
// Block 901 is the first v2 block with round of 1 // Block 901 is the first v2 block with round of 1
blockchain, _, block910, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, 910, &config, nil) blockchain, _, block910, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, 910, &config, nil)
adaptor := blockchain.Engine().(*XDPoS.XDPoS) adaptor := blockchain.Engine().(*XDPoS.XDPoS)
@ -283,12 +332,12 @@ func TestShouldVerifyHeadersEvenIfParentsNotYetWrittenIntoDB(t *testing.T) {
// Create block 911 but don't write into DB // Create block 911 but don't write into DB
blockNumber := 911 blockNumber := 911
roundNumber := int64(blockNumber) - config.XDPoS.V2.SwitchBlock.Int64() roundNumber := int64(blockNumber) - config.XDPoS.V2.FirstSwitchBlock.Int64()
block911 := CreateBlock(blockchain, &config, block910, blockNumber, roundNumber, signer.Hex(), signer, signFn, nil, nil) block911 := CreateBlock(blockchain, &config, block910, blockNumber, roundNumber, signer.Hex(), signer, signFn, nil, nil)
// Create block 912 and not write into DB as well // Create block 912 and not write into DB as well
blockNumber = 912 blockNumber = 912
roundNumber = int64(blockNumber) - config.XDPoS.V2.SwitchBlock.Int64() roundNumber = int64(blockNumber) - config.XDPoS.V2.FirstSwitchBlock.Int64()
block912 := CreateBlock(blockchain, &config, block911, blockNumber, roundNumber, signer.Hex(), signer, signFn, nil, nil) block912 := CreateBlock(blockchain, &config, block911, blockNumber, roundNumber, signer.Hex(), signer, signFn, nil, nil)
headersTobeVerified = append(headersTobeVerified, block910.Header(), block911.Header(), block912.Header()) headersTobeVerified = append(headersTobeVerified, block910.Header(), block911.Header(), block912.Header())

View file

@ -2541,7 +2541,7 @@ func (bc *BlockChain) UpdateM1() error {
header := bc.CurrentHeader() header := bc.CurrentHeader()
var maxMasternodes int var maxMasternodes int
// check if block number is increase ms checkpoint // check if block number is increase ms checkpoint
if bc.chainConfig.IsTIPIncreaseMasternodes(header.Number) || (bc.chainConfig.XDPoS.V2.SwitchBlock != nil && header.Number.Cmp(bc.chainConfig.XDPoS.V2.SwitchBlock) == 1) { if bc.chainConfig.IsTIPIncreaseMasternodes(header.Number) || (bc.chainConfig.XDPoS.V2.FirstSwitchBlock != nil && header.Number.Cmp(bc.chainConfig.XDPoS.V2.FirstSwitchBlock) == 1) {
// using new masterndoes // using new masterndoes
maxMasternodes = common.MaxMasternodesV2 maxMasternodes = common.MaxMasternodesV2
} else { } else {

View file

@ -86,7 +86,7 @@ func AttachConsensusV2Hooks(adaptor *XDPoS.XDPoS, bc *core.BlockChain, chainConf
// get list check penalties signing block & list master nodes wil comeback // get list check penalties signing block & list master nodes wil comeback
// start to calc comeback at v2 block + limitPenaltyEpochV2 to avoid reading v1 blocks // 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.SwitchBlock.Uint64() comebackHeight := (common.LimitPenaltyEpochV2+1)*chain.Config().XDPoS.Epoch + chain.Config().XDPoS.V2.FirstSwitchBlock.Uint64()
penComebacks := []common.Address{} penComebacks := []common.Address{}
if number.Uint64() > comebackHeight { if number.Uint64() > comebackHeight {
pens := adaptor.EngineV2.GetPreviousPenaltyByHash(chain, currentHash, common.LimitPenaltyEpochV2) 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{}) rewards := make(map[string]interface{})
// skip hook reward if this is the first v2 // skip hook reward if this is the first v2
if number == chain.Config().XDPoS.V2.SwitchBlock.Uint64()+1 { if number == chain.Config().XDPoS.V2.FirstSwitchBlock.Uint64()+1 {
return rewards, nil return rewards, nil
} }
start := time.Now() start := time.Now()
@ -235,7 +235,7 @@ func GetSigningTxCount(c *XDPoS.XDPoS, chain consensus.ChainReader, header *type
if err != nil { if err != nil {
return nil, err return nil, err
} }
if isEpochSwitch && i != chain.Config().XDPoS.V2.SwitchBlock.Uint64()+1 { if isEpochSwitch && i != chain.Config().XDPoS.V2.FirstSwitchBlock.Uint64()+1 {
epochCount += 1 epochCount += 1
if epochCount == signEpochCount { if epochCount == signEpochCount {
endBlockNumber = header.Number.Uint64() - 1 endBlockNumber = header.Number.Uint64() - 1

View file

@ -267,8 +267,7 @@ func (self *worker) update() {
defer self.chainSideSub.Unsubscribe() defer self.chainSideSub.Unsubscribe()
// timeout waiting for v1 inital value // timeout waiting for v1 inital value
// TODO: Read value from config after we decide where is the config waitPeriod := 2
waitPeriod := 10
WaitPeriodCh := self.engine.(*XDPoS.XDPoS).WaitPeriodCh WaitPeriodCh := self.engine.(*XDPoS.XDPoS).WaitPeriodCh
defer close(WaitPeriodCh) defer close(WaitPeriodCh)

View file

@ -26,6 +26,7 @@ import (
const ( const (
ConsensusEngineVersion1 = "v1" ConsensusEngineVersion1 = "v1"
ConsensusEngineVersion2 = "v2" ConsensusEngineVersion2 = "v2"
Default = 0
) )
var ( var (
@ -36,30 +37,92 @@ var (
) )
var ( var (
XDPoSV2Config = &V2{ MainnetV2Configs = map[uint64]*V2Config{
SwitchBlock: big.NewInt(9999999999), Default: {
CertThreshold: common.MaxMasternodesV2*2/3 + 1, SwitchBlock: big.NewInt(9999999999),
TimeoutSyncThreshold: 3, CertThreshold: common.MaxMasternodesV2*2/3 + 1,
TimeoutPeriod: 60, TimeoutSyncThreshold: 3,
WaitPeriod: 10, TimeoutPeriod: 60,
MinePeriod: 10, WaitPeriod: 10,
MinePeriod: 10,
},
9999999999: {
SwitchBlock: big.NewInt(9999999999),
CertThreshold: common.MaxMasternodesV2*2/3 + 1,
TimeoutSyncThreshold: 3,
TimeoutPeriod: 60,
WaitPeriod: 10,
MinePeriod: 10,
},
} }
TestXDPoSV2Config = &V2{ TestV2Configs = map[uint64]*V2Config{
SwitchBlock: big.NewInt(900), Default: {
CertThreshold: 3, SwitchBlock: big.NewInt(900),
TimeoutSyncThreshold: 2, CertThreshold: 3,
TimeoutPeriod: 10, TimeoutSyncThreshold: 2,
WaitPeriod: 1, TimeoutPeriod: 4,
MinePeriod: 2, WaitPeriod: 1,
SkipV2Validation: true, MinePeriod: 2,
},
900: {
SwitchBlock: big.NewInt(900),
CertThreshold: 3,
TimeoutSyncThreshold: 2,
TimeoutPeriod: 4,
WaitPeriod: 1,
MinePeriod: 2,
},
910: {
SwitchBlock: big.NewInt(910),
CertThreshold: 5,
TimeoutSyncThreshold: 2,
TimeoutPeriod: 4,
WaitPeriod: 2,
MinePeriod: 3,
},
1799: {
SwitchBlock: big.NewInt(1799),
CertThreshold: 5,
TimeoutSyncThreshold: 4,
TimeoutPeriod: 5,
WaitPeriod: 2,
MinePeriod: 3,
},
} }
DevnetXDPoSV2Config = &V2{
SwitchBlock: big.NewInt(7074000), DevnetV2Configs = map[uint64]*V2Config{
CertThreshold: common.MaxMasternodesV2*2/3 + 1, Default: {
TimeoutSyncThreshold: 5, SwitchBlock: big.NewInt(7074000),
TimeoutPeriod: 25, CertThreshold: 4,
WaitPeriod: 10, TimeoutSyncThreshold: 5,
MinePeriod: 10, TimeoutPeriod: 10,
WaitPeriod: 5,
MinePeriod: 5,
},
7074000: {
SwitchBlock: big.NewInt(7074000),
CertThreshold: common.MaxMasternodesV2*2/3 + 1,
TimeoutSyncThreshold: 5,
TimeoutPeriod: 25,
WaitPeriod: 10,
MinePeriod: 10,
},
9900000: {
SwitchBlock: big.NewInt(9900000),
CertThreshold: common.MaxMasternodesV2*3/5 + 1,
TimeoutSyncThreshold: 5,
TimeoutPeriod: 25,
WaitPeriod: 5,
MinePeriod: 20,
},
10000000: {
SwitchBlock: big.NewInt(10000000),
CertThreshold: common.MaxMasternodesV2*2/3 + 1,
TimeoutSyncThreshold: 5,
TimeoutPeriod: 25,
WaitPeriod: 10,
MinePeriod: 10,
},
} }
// XDPoSChain mainnet config // XDPoSChain mainnet config
@ -78,7 +141,11 @@ var (
RewardCheckpoint: 900, RewardCheckpoint: 900,
Gap: 450, Gap: 450,
FoudationWalletAddr: common.HexToAddress("xdc92a289fe95a85c53b8d0d113cbaef0c1ec98ac65"), FoudationWalletAddr: common.HexToAddress("xdc92a289fe95a85c53b8d0d113cbaef0c1ec98ac65"),
V2: XDPoSV2Config, V2: &V2{
FirstSwitchBlock: MainnetV2Configs[0].SwitchBlock,
CurrentConfig: MainnetV2Configs[0],
AllConfigs: MainnetV2Configs,
},
}, },
} }
@ -116,11 +183,16 @@ var (
RewardCheckpoint: 900, RewardCheckpoint: 900,
Gap: 450, Gap: 450,
FoudationWalletAddr: common.HexToAddress("xdc746249c61f5832c5eed53172776b460491bdcd5c"), FoudationWalletAddr: common.HexToAddress("xdc746249c61f5832c5eed53172776b460491bdcd5c"),
V2: TestXDPoSV2Config, V2: &V2{
FirstSwitchBlock: TestV2Configs[0].SwitchBlock,
CurrentConfig: TestV2Configs[0],
AllConfigs: TestV2Configs,
SkipV2Validation: true,
},
}, },
} }
// TestnetChainConfig contains the chain parameters to run a node on the Ropsten test network. // DevnetChainConfig contains the chain parameters to run a node on the Ropsten test network.
DevnetChainConfig = &ChainConfig{ DevnetChainConfig = &ChainConfig{
ChainId: big.NewInt(551), ChainId: big.NewInt(551),
HomesteadBlock: big.NewInt(1), HomesteadBlock: big.NewInt(1),
@ -136,7 +208,11 @@ var (
RewardCheckpoint: 900, RewardCheckpoint: 900,
Gap: 450, Gap: 450,
FoudationWalletAddr: common.HexToAddress("0x746249c61f5832c5eed53172776b460491bdcd5c"), FoudationWalletAddr: common.HexToAddress("0x746249c61f5832c5eed53172776b460491bdcd5c"),
V2: DevnetXDPoSV2Config, V2: &V2{
FirstSwitchBlock: DevnetV2Configs[0].SwitchBlock,
CurrentConfig: DevnetV2Configs[0],
AllConfigs: DevnetV2Configs,
},
}, },
} }
@ -155,7 +231,11 @@ var (
XDPoS: &XDPoSConfig{ XDPoS: &XDPoSConfig{
Period: 15, Period: 15,
Epoch: 30000, Epoch: 30000,
V2: XDPoSV2Config, V2: &V2{
FirstSwitchBlock: MainnetV2Configs[0].SwitchBlock,
CurrentConfig: MainnetV2Configs[0],
AllConfigs: MainnetV2Configs,
},
}, },
} }
@ -175,7 +255,32 @@ var (
AllCliqueProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, &CliqueConfig{Period: 0, Epoch: 30000}, nil} AllCliqueProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, &CliqueConfig{Period: 0, Epoch: 30000}, nil}
// XDPoS config with v2 engine after block 901 // XDPoS config with v2 engine after block 901
TestXDPoSMockChainConfig = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, new(EthashConfig), nil, &XDPoSConfig{Epoch: 900, Gap: 450, SkipV1Validation: true, V2: TestXDPoSV2Config, FoudationWalletAddr: common.HexToAddress("0x0000000000000000000000000000000000000068"), Reward: 250}} TestXDPoSMockChainConfig = &ChainConfig{
big.NewInt(1337),
big.NewInt(0),
nil,
false,
big.NewInt(0),
common.Hash{},
big.NewInt(0),
big.NewInt(0),
big.NewInt(0),
nil,
new(EthashConfig),
nil,
&XDPoSConfig{
Epoch: 900,
Gap: 450,
SkipV1Validation: true,
FoudationWalletAddr: common.HexToAddress("0x0000000000000000000000000000000000000068"),
Reward: 250,
V2: &V2{
FirstSwitchBlock: TestV2Configs[0].SwitchBlock,
CurrentConfig: TestV2Configs[0],
AllConfigs: TestV2Configs,
},
},
}
TestChainConfig = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, new(EthashConfig), nil, nil} TestChainConfig = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, new(EthashConfig), nil, nil}
TestRules = TestChainConfig.Rules(new(big.Int)) TestRules = TestChainConfig.Rules(new(big.Int))
@ -242,28 +347,69 @@ type XDPoSConfig struct {
} }
type V2 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
}
type V2Config struct {
WaitPeriod int `json:"waitPeriod"` // Miner wait period to check mine event WaitPeriod int `json:"waitPeriod"` // Miner wait period to check mine event
MinePeriod int `json:"minePeriod"` // Miner mine period to mine a block MinePeriod int `json:"minePeriod"` // Miner mine period to mine a block
SwitchBlock *big.Int `json:"switchBlock"` // v1 to v2 switch block number SwitchBlock *big.Int `json:"switchBlock"` // v1 to v2 switch block number
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 int `json:"certificateThreshold"` // Necessary number of messages from master nodes to form a certificate
SkipV2Validation bool //Skip Block Validation for testing purpose, V2 consensus only
} }
// String implements the stringer interface, returning the consensus engine details. // String implements the stringer interface, returning the consensus engine details.
func (c *XDPoSConfig) BuildConfigIndex() {
var list []uint64
for i := range c.V2.AllConfigs {
list = append(list, i)
}
// sort, sort lib doesn't support type uint64, it's ok to have O(n^2) because only few items in the list
for i := 0; i < len(list)-1; i++ {
for j := i + 1; j < len(list); j++ {
if list[i] > list[j] {
list[i], list[j] = list[j], list[i]
}
}
}
c.V2.configIndex = list
}
func (c *XDPoSConfig) String() string { func (c *XDPoSConfig) String() string {
return "XDPoS" 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 { func (c *XDPoSConfig) BlockConsensusVersion(num *big.Int, extraByte []byte, skipExtraCheck bool) string {
if c.V2 != nil && c.V2.SwitchBlock != nil && num.Cmp(c.V2.SwitchBlock) > 0 { if !skipExtraCheck && (len(extraByte) == 0 || extraByte[0] != 2) {
if skipExtraCheck { return ConsensusEngineVersion1
return ConsensusEngineVersion2 }
}
if len(extraByte) == 0 || extraByte[0] != 2 { if c.V2 != nil && c.V2.FirstSwitchBlock != nil && num.Cmp(c.V2.FirstSwitchBlock) > 0 {
return ConsensusEngineVersion1 // 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 ConsensusEngineVersion2
} }
return ConsensusEngineVersion1 return ConsensusEngineVersion1