diff --git a/cmd/XDC/main.go b/cmd/XDC/main.go index 6e92867e54..912f412739 100644 --- a/cmd/XDC/main.go +++ b/cmd/XDC/main.go @@ -301,7 +301,7 @@ func startNode(ctx *cli.Context, stack *node.Node, cfg XDCConfig) { if err := stack.Service(ðereum); err != nil { 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() { started := false ok := false @@ -345,6 +345,8 @@ func startNode(ctx *cli.Context, stack *node.Node, cfg XDCConfig) { defer close(core.CheckpointCh) for range core.CheckpointCh { log.Info("Checkpoint!!! It's time to reconcile node's state...") + log.Info("Update consensus parameters") + engine.UpdateParams() if common.IsTestnet { ok, err = ethereum.ValidateMasternodeTestnet() if err != nil { diff --git a/common/countdown/countdown.go b/common/countdown/countdown.go index b26dcd669e..cffba13bf0 100644 --- a/common/countdown/countdown.go +++ b/common/countdown/countdown.go @@ -34,6 +34,10 @@ func (t *CountdownTimer) StopTimer() { <-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` func (t *CountdownTimer) Reset(i interface{}) { if !t.isInitilised() { diff --git a/consensus/XDPoS/XDPoS.go b/consensus/XDPoS/XDPoS.go index 97b99bdb3f..f6b7b5d615 100644 --- a/consensus/XDPoS/XDPoS.go +++ b/consensus/XDPoS/XDPoS.go @@ -87,7 +87,11 @@ func New(config *params.XDPoSConfig, db ethdb.Database) *XDPoS { // For testing and testing project, default to mainnet config if config.V2 == nil { - config.V2 = params.XDPoSV2Config + config.V2 = ¶ms.V2{ + FirstSwitchBlock: params.MainnetV2Configs[0].SwitchBlock, + CurrentConfig: params.MainnetV2Configs[0], + AllConfigs: params.MainnetV2Configs, + } } log.Info("xdc config loading", "config", config) @@ -140,6 +144,11 @@ func NewFaker(db ethdb.Database, chainConfig *params.ChainConfig) *XDPoS { 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 { switch x.config.BlockConsensusVersion(header.Number, header.Extra, ExtraFieldCheck) { case params.ConsensusEngineVersion2: diff --git a/consensus/XDPoS/engines/engine_v2/engine.go b/consensus/XDPoS/engines/engine_v2/engine.go index 4b0c0b3c6b..d8bc577f52 100644 --- a/consensus/XDPoS/engines/engine_v2/engine.go +++ b/consensus/XDPoS/engines/engine_v2/engine.go @@ -66,7 +66,7 @@ type XDPoS_v2 struct { func New(config *params.XDPoSConfig, db ethdb.Database, waitPeriodCh chan int) *XDPoS_v2 { // Setup timeoutTimer - duration := time.Duration(config.V2.TimeoutPeriod) * time.Second + duration := time.Duration(config.V2.CurrentConfig.TimeoutPeriod) * time.Second timeoutTimer := countdown.NewCountDown(duration) 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)) verifiedHeaders, _ := lru.NewARC(utils.InmemorySnapshots) - timeoutPool := utils.NewPool(config.V2.CertThreshold) - votePool := utils.NewPool(config.V2.CertThreshold) + timeoutPool := utils.NewPool() + votePool := utils.NewPool() engine := &XDPoS_v2{ config: config, db: db, @@ -116,6 +116,7 @@ func New(config *params.XDPoSConfig, db ethdb.Database, waitPeriodCh chan int) * timeoutTimer.OnTimeoutFn = engine.OnCountdownTimeout engine.periodicJob() + config.BuildConfigIndex() 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 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) { return sigHash(header) } @@ -150,7 +163,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.SwitchBlock.Int64() { + if header.Number.Int64() == x.config.V2.FirstSwitchBlock.Int64() { log.Info("[initial] highest QC for consensus v2 first block") blockInfo := &types.BlockInfo{ Hash: header.Hash(), @@ -180,13 +193,13 @@ func (x *XDPoS_v2) initial(chain consensus.ChainReader, header *types.Header) er } // 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) snap, _ := loadSnapshot(x.db, lastGapHeader.Hash()) 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") _, _, masternodes, err := x.getExtraFields(checkpointHeader) @@ -204,10 +217,10 @@ func (x *XDPoS_v2) initial(chain consensus.ChainReader, header *types.Header) er } // 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 go func() { - x.waitPeriodCh <- x.config.V2.WaitPeriod + x.waitPeriodCh <- x.config.V2.CurrentConfig.WaitPeriod }() // 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() - if waitedTime < int64(x.config.V2.MinePeriod) { - log.Trace("[YourTurn] wait after mine period", "minePeriod", x.config.V2.MinePeriod, "waitedTime", waitedTime) + if waitedTime < int64(x.config.V2.CurrentConfig.MinePeriod) { + log.Trace("[YourTurn] wait after mine period", "minePeriod", x.config.V2.CurrentConfig.MinePeriod, "waitedTime", waitedTime) 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 - if blockInfo.Number.Cmp(x.config.V2.SwitchBlock) == 0 { + if blockInfo.Number.Cmp(x.config.V2.FirstSwitchBlock) == 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") @@ -753,7 +766,7 @@ func (x *XDPoS_v2) verifyQC(blockChainReader consensus.ChainReader, quorumCert * if quorumCert == nil { log.Warn("[verifyQC] QC is Nil") 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 log.Warn("[verifyHeader] Invalid QC Signature is nil or empty", "QC", quorumCert, "QCNumber", quorumCert.ProposedBlockInfo.Number, "Signatures len", len(signatures)) 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) 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 proposedBlockQuorumCert, round, _, err := x.getExtraFields(proposedBlockHeader) 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) 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.SwitchBlock) <= 0 { + if big.NewInt(0).Sub(proposedBlockHeader.Number, big.NewInt(2)).Cmp(x.config.V2.FirstSwitchBlock) <= 0 { return false, nil } // 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 - 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") return candidates, []common.Address{}, nil } diff --git a/consensus/XDPoS/engines/engine_v2/epochSwitch.go b/consensus/XDPoS/engines/engine_v2/epochSwitch.go index 66feda0617..e94dddd0a2 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.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 parentHeader.Number.Cmp(x.config.V2.SwitchBlock) == 0 { + if parentHeader.Number.Cmp(x.config.V2.FirstSwitchBlock) == 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.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 } 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.SwitchBlock) == 0 { + if header.Number.Cmp(x.config.V2.FirstSwitchBlock) == 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.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 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()) return true, epochNum, nil } diff --git a/consensus/XDPoS/engines/engine_v2/timeout.go b/consensus/XDPoS/engines/engine_v2/timeout.go index 07194701c3..63214c0893 100644 --- a/consensus/XDPoS/engines/engine_v2/timeout.go +++ b/consensus/XDPoS/engines/engine_v2/timeout.go @@ -24,10 +24,11 @@ func (x *XDPoS_v2) timeoutHandler(blockChainReader consensus.ChainReader, timeou } } // Collect timeout, generate TC - isThresholdReached, numberOfTimeoutsInPool, pooledTimeouts := x.timeoutPool.Add(timeout) + numberOfTimeoutsInPool, pooledTimeouts := x.timeoutPool.Add(timeout) log.Debug("[timeoutHandler] collect timeout", "number", numberOfTimeoutsInPool) // Threshold reached + isThresholdReached := numberOfTimeoutsInPool >= x.config.V2.CurrentConfig.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) @@ -94,7 +95,7 @@ func (x *XDPoS_v2) verifyTC(chain consensus.ChainReader, timeoutCert *types.Time if timeoutCert == nil { log.Warn("[verifyTC] TC is Nil") 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)) return utils.ErrInvalidTC } @@ -220,7 +221,7 @@ func (x *XDPoS_v2) OnCountdownTimeout(time time.Time, chain interface{}) error { } 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") syncInfo := x.getSyncInfo() x.broadcastToBftChannel(syncInfo) diff --git a/consensus/XDPoS/engines/engine_v2/utils.go b/consensus/XDPoS/engines/engine_v2/utils.go index 8830bfa21d..90bdd1a71c 100644 --- a/consensus/XDPoS/engines/engine_v2/utils.go +++ b/consensus/XDPoS/engines/engine_v2/utils.go @@ -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) { @@ -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.SwitchBlock) == 0 { + if header.Number.Cmp(x.config.V2.FirstSwitchBlock) == 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 f727e19808..d72bbb013b 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.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 } diff --git a/consensus/XDPoS/engines/engine_v2/vote.go b/consensus/XDPoS/engines/engine_v2/vote.go index ad90a22ac6..98ae838ed6 100644 --- a/consensus/XDPoS/engines/engine_v2/vote.go +++ b/consensus/XDPoS/engines/engine_v2/vote.go @@ -70,10 +70,12 @@ func (x *XDPoS_v2) voteHandler(chain consensus.ChainReader, voteMsg *types.Vote) } // Collect vote - thresholdReached, numberOfVotesInPool, pooledVotes := x.votePool.Add(voteMsg) + numberOfVotesInPool, pooledVotes := x.votePool.Add(voteMsg) log.Debug("[voteHandler] collect votes", "number", numberOfVotesInPool) go x.ForensicsProcessor.DetectEquivocationInVotePool(voteMsg, x.votePool) go x.ForensicsProcessor.ProcessVoteEquivocation(chain, x, voteMsg) + + thresholdReached := numberOfVotesInPool >= x.config.V2.CurrentConfig.CertThreshold if thresholdReached { 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, GapNumber: v.GapNumber, }), v.Signature, masternodes) - if !verified || err != nil { - log.Warn("[onVotePoolThresholdReached] Skip not verified vote signatures when building QC", "Error", err.Error(), "verified", verified) + if err != nil { + 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 } @@ -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 - 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)) return nil } diff --git a/consensus/XDPoS/utils/pool.go b/consensus/XDPoS/utils/pool.go index 98eef6a4e7..91b3e21653 100644 --- a/consensus/XDPoS/utils/pool.go +++ b/consensus/XDPoS/utils/pool.go @@ -11,20 +11,18 @@ type PoolObj interface { PoolKey() string } type Pool struct { - objList map[string]map[common.Hash]PoolObj - threshold int - lock sync.RWMutex // Protects the pool fields + objList map[string]map[common.Hash]PoolObj + lock sync.RWMutex // Protects the pool fields } -func NewPool(threshold int) *Pool { +func NewPool() *Pool { return &Pool{ - objList: make(map[string]map[common.Hash]PoolObj), - threshold: threshold, + objList: make(map[string]map[common.Hash]PoolObj), } } // 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() defer p.lock.Unlock() poolKey := obj.PoolKey() @@ -35,10 +33,7 @@ func (p *Pool) Add(obj PoolObj) (bool, int, map[common.Hash]PoolObj) { } objListKeyed[obj.Hash()] = obj numOfItems := len(objListKeyed) - if numOfItems >= p.threshold { - return true, numOfItems, objListKeyed - } - return false, numOfItems, objListKeyed + return numOfItems, objListKeyed } func (p *Pool) Size(obj PoolObj) int { @@ -85,13 +80,6 @@ func (p *Pool) Clear() { 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 { p.lock.Lock() defer p.lock.Unlock() diff --git a/consensus/XDPoS/utils/pool_test.go b/consensus/XDPoS/utils/pool_test.go index 6a94afa3e8..39d3d37810 100644 --- a/consensus/XDPoS/utils/pool_test.go +++ b/consensus/XDPoS/utils/pool_test.go @@ -10,55 +10,50 @@ import ( func TestPoolAdd(t *testing.T) { assert := assert.New(t) - pool := NewPool(2) // 2 is the cert threshold + pool := NewPool() timeout1 := types.Timeout{Round: 1, Signature: []byte{1}} timeout2 := types.Timeout{Round: 1, Signature: []byte{2}} timeout3 := types.Timeout{Round: 1, Signature: []byte{3}} timeout4 := types.Timeout{Round: 1, Signature: []byte{4}} - thresholdReached, numOfItems, pooledTimeouts := pool.Add(&timeout1) + numOfItems, pooledTimeouts := pool.Add(&timeout1) assert.NotNil(pooledTimeouts) assert.Equal(1, numOfItems) - assert.False(thresholdReached) - thresholdReached, numOfItems, pooledTimeouts = pool.Add(&timeout1) + numOfItems, pooledTimeouts = pool.Add(&timeout1) assert.NotNil(pooledTimeouts) - assert.False(thresholdReached) // Duplicates should not be added assert.Equal(1, numOfItems) // Should add the one that is not a duplicates - thresholdReached, numOfItems, pooledTimeouts = pool.Add(&timeout2) - assert.True(thresholdReached) + numOfItems, pooledTimeouts = pool.Add(&timeout2) + assert.NotNil(pooledTimeouts) assert.Equal(2, numOfItems) // Try to add one more to the same round, it should also trigger threshold - thresholdReached, numOfItems, pooledTimeouts = pool.Add(&timeout3) - assert.True(thresholdReached) + numOfItems, pooledTimeouts = pool.Add(&timeout3) assert.NotNil(pooledTimeouts) assert.Equal(3, numOfItems) // Only after manually clearned the pool at its objKey, we shall not have any value for this particular key pool.ClearPoolKeyByObj(&timeout3) - thresholdReached, numOfItems, pooledTimeouts = pool.Add(&timeout4) - assert.False(thresholdReached) + numOfItems, pooledTimeouts = pool.Add(&timeout4) + assert.NotNil(pooledTimeouts) assert.Equal(1, numOfItems) - pool = NewPool(3) // 3 is the cert size - thresholdReached, numOfItems, pooledTimeouts = pool.Add(&timeout1) - assert.False(thresholdReached) + pool = NewPool() + numOfItems, pooledTimeouts = pool.Add(&timeout1) assert.NotNil(pooledTimeouts) assert.Equal(1, numOfItems) - thresholdReached, numOfItems, pooledTimeouts = pool.Add(&timeout2) - assert.False(thresholdReached) + numOfItems, pooledTimeouts = pool.Add(&timeout2) + assert.Equal(2, numOfItems) assert.NotNil(pooledTimeouts) pool.Clear() // Pool has been cleared. Start from 0 again - thresholdReached, numOfItems, pooledTimeouts = pool.Add(&timeout3) - assert.False(thresholdReached) + numOfItems, pooledTimeouts = pool.Add(&timeout3) assert.Equal(1, numOfItems) assert.NotNil(pooledTimeouts) } diff --git a/consensus/tests/engine_v1_tests/helper.go b/consensus/tests/engine_v1_tests/helper.go index 3b777e487b..3d66302650 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.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 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 6eb2ff65e1..36fae6922a 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.SwitchBlock, big.NewInt(1)) + headerV2.Number.Add(blockchain.Config().XDPoS.V2.FirstSwitchBlock, 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.SwitchBlock), + Number: big.NewInt(0).Set(blockchain.Config().XDPoS.V2.FirstSwitchBlock), } quorumCert := &types.QuorumCert{ ProposedBlockInfo: parentBlockInfo, 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{ 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.SwitchBlock, big.NewInt(1)) + header.Number.Add(blockchain.Config().XDPoS.V2.FirstSwitchBlock, 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.SwitchBlock, big.NewInt(1)), + Number: big.NewInt(0).Add(blockchain.Config().XDPoS.V2.FirstSwitchBlock, big.NewInt(1)), } quorumCert = &types.QuorumCert{ ProposedBlockInfo: parentBlockInfo, 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{ 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.SwitchBlock, big.NewInt(2)) + header.Number.Add(blockchain.Config().XDPoS.V2.FirstSwitchBlock, 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.SwitchBlock, big.NewInt(100)), + Number: big.NewInt(0).Add(blockchain.Config().XDPoS.V2.FirstSwitchBlock, big.NewInt(100)), } quorumCert = &types.QuorumCert{ ProposedBlockInfo: parentBlockInfo, 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{ 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.SwitchBlock, big.NewInt(101)) + header.Number.Add(blockchain.Config().XDPoS.V2.FirstSwitchBlock, 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.SwitchBlock, big.NewInt(100)), + Number: big.NewInt(0).Add(blockchain.Config().XDPoS.V2.FirstSwitchBlock, big.NewInt(100)), } quorumCert = &types.QuorumCert{ ProposedBlockInfo: parentBlockInfo, 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{ 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.SwitchBlock, big.NewInt(101)) + header.Number.Add(blockchain.Config().XDPoS.V2.FirstSwitchBlock, 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 9ac58044e1..98d2f63a20 100644 --- a/consensus/tests/engine_v2_tests/authorised_masternode_test.go +++ b/consensus/tests/engine_v2_tests/authorised_masternode_test.go @@ -1,6 +1,7 @@ package engine_v2_tests import ( + "math/big" "testing" "time" @@ -33,21 +34,23 @@ func TestIsAuthorisedMNForConsensusV2(t *testing.T) { func TestIsYourTurnConsensusV2(t *testing.T) { // 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) - minePeriod := params.TestXDPoSV2Config.MinePeriod + minePeriod := params.TestV2Configs[0].MinePeriod adaptor := blockchain.Engine().(*XDPoS.XDPoS) blockNum := 901 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) // 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.False(t, isYourTurn) time.Sleep(time.Duration(minePeriod) * time.Second) // 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.True(t, isYourTurn) @@ -77,3 +80,30 @@ func TestIsYourTurnConsensusV2(t *testing.T) { 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) +} diff --git a/consensus/tests/engine_v2_tests/forensics_test.go b/consensus/tests/engine_v2_tests/forensics_test.go index a55efaf0e8..35c6463fef 100644 --- a/consensus/tests/engine_v2_tests/forensics_test.go +++ b/consensus/tests/engine_v2_tests/forensics_test.go @@ -177,11 +177,11 @@ func TestForensicsMonitoringNotOnSameChainButHaveSameRoundQC(t *testing.T) { 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, 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, uint64(912), content.LargerRoundInfo.QuorumCert.ProposedBlockInfo.Number.Uint64()) 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 case <-time.After(5 * time.Second): 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, uint64(914), content.SmallerRoundInfo.QuorumCert.ProposedBlockInfo.Number.Uint64()) 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, uint64(906), content.LargerRoundInfo.QuorumCert.ProposedBlockInfo.Number.Uint64()) 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, uint64(1800), content.SmallerRoundInfo.QuorumCert.ProposedBlockInfo.Number.Uint64()) 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, uint64(1792), content.LargerRoundInfo.QuorumCert.ProposedBlockInfo.Number.Uint64()) assert.Equal(t, 2, len(content.LargerRoundInfo.HashPath)) diff --git a/consensus/tests/engine_v2_tests/helper.go b/consensus/tests/engine_v2_tests/helper.go index 0fd2014e20..4b267864bc 100644 --- a/consensus/tests/engine_v2_tests/helper.go +++ b/consensus/tests/engine_v2_tests/helper.go @@ -41,6 +41,8 @@ var ( acc1Key, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a") acc2Key, _ = crypto.HexToECDSA("49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee") acc3Key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + acc4Key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f292") + acc5Key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f293") voterKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee04aefe388d1e14474d32c45c72ce7b7a") acc1Addr = crypto.PubkeyToAddress(acc1Key.PublicKey) //xdc703c4b2bD70c169f5717101CaeE543299Fc946C7 acc2Addr = crypto.PubkeyToAddress(acc2Key.PublicKey) //xdc0D3ab14BBaD3D99F4203bd7a11aCB94882050E7e @@ -310,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.SwitchBlock.Int64() { + if int64(i) > chainConfig.XDPoS.V2.FirstSwitchBlock.Int64() { 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) err = blockchain.InsertBlock(block) @@ -346,7 +348,7 @@ func PrepareXDCTestBlockChainForV2Engine(t *testing.T, numOfBlocks int, chainCon } // 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 checkpointBlockNumber := lastv1BlockNumber - lastv1BlockNumber%chainConfig.XDPoS.Epoch checkpointHeader := blockchain.GetHeaderByNumber(checkpointBlockNumber) @@ -396,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.SwitchBlock.Int64() { + if int64(i) > chainConfig.XDPoS.V2.FirstSwitchBlock.Int64() { blockCoinBase = signer.Hex() } - roundNumber := int64(i) - chainConfig.XDPoS.V2.SwitchBlock.Int64() + roundNumber := int64(i) - chainConfig.XDPoS.V2.FirstSwitchBlock.Int64() // use signer itself as penalty 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" 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) header = &types.Header{ @@ -434,9 +436,9 @@ func CreateBlock(blockchain *BlockChain, chainConfig *params.ChainConfig, starti Coinbase: common.HexToAddress(blockCoinBase), 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 - lastv1Block := blockchain.GetBlockByNumber(chainConfig.XDPoS.V2.SwitchBlock.Uint64()) + lastv1Block := blockchain.GetBlockByNumber(chainConfig.XDPoS.V2.FirstSwitchBlock.Uint64()) masternodesFromV1LastEpoch := decodeMasternodesFromHeaderExtra(lastv1Block.Header()) for _, v := range masternodesFromV1LastEpoch { 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 { // 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.SwitchBlock.Uint64()) + lastv1Block := blockchain.GetBlockByNumber(chainConfig.XDPoS.V2.FirstSwitchBlock.Uint64()) masternodesFromV1LastEpoch := decodeMasternodesFromHeaderExtra(lastv1Block.Header()) for _, v := range masternodesFromV1LastEpoch { 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) - 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 header.Extra = []byte{} if len(header.Extra) < utils.ExtraVanity { @@ -522,7 +524,7 @@ func createBlockFromHeader(bc *BlockChain, customHeader *types.Header, txs []*ty Difficulty: difficulty, Number: customHeader.Number, GasLimit: 1200000000, - Time: big.NewInt(time.Now().Unix()), + Time: big.NewInt(time.Now().Unix() - 1000000 + int64(customHeader.Number.Uint64()*10)), Extra: customHeader.Extra, Validator: customHeader.Validator, Validators: customHeader.Validators, @@ -582,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.SwitchBlock) > 0 { + if header.Number.Cmp(config.XDPoS.V2.FirstSwitchBlock) > 0 { var decodedExtraField types.ExtraFields_v2 err := utils.DecodeBytesExtraFields(header.Extra, &decodedExtraField) if err != nil { @@ -659,7 +661,7 @@ func generateV2Extra(roundNumber int64, currentBlock *types.Block, signer common var signatures []types.Signature if len(accKeys) == 0 { // 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 { h := SignHashByPK(acc, types.VoteSigHash(voteForSign).Bytes()) diff --git a/consensus/tests/engine_v2_tests/initial_test.go b/consensus/tests/engine_v2_tests/initial_test.go index 9d9a7d9b21..a26b7f59aa 100644 --- a/consensus/tests/engine_v2_tests/initial_test.go +++ b/consensus/tests/engine_v2_tests/initial_test.go @@ -32,7 +32,7 @@ func TestInitialFirstV2Blcok(t *testing.T) { expectedQuorumCert := &types.QuorumCert{ ProposedBlockInfo: blockInfo, 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, expectedQuorumCert, highQC) @@ -43,10 +43,10 @@ func TestInitialFirstV2Blcok(t *testing.T) { assert.Equal(t, uint64(450), snap.Number) // Test Running channels - WaitPeriod := <-adaptor.WaitPeriodCh - assert.Equal(t, params.TestXDPoSMockChainConfig.XDPoS.V2.WaitPeriod, WaitPeriod) + waitPeriod := <-adaptor.WaitPeriodCh + 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 assert.NotNil(t, timeoutMsg) assert.Equal(t, types.Round(1), timeoutMsg.(*types.Timeout).Round) @@ -104,7 +104,7 @@ func TestInitialOtherV2Block(t *testing.T) { expectedQuorumCert := &types.QuorumCert{ ProposedBlockInfo: blockInfo, 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, expectedQuorumCert, highQC) diff --git a/consensus/tests/engine_v2_tests/mine_test.go b/consensus/tests/engine_v2_tests/mine_test.go index 61cec63647..a56658e1e8 100644 --- a/consensus/tests/engine_v2_tests/mine_test.go +++ b/consensus/tests/engine_v2_tests/mine_test.go @@ -18,7 +18,7 @@ import ( func TestYourTurnInitialV2(t *testing.T) { config := params.TestXDPoSMockChainConfig 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) // Insert block 900 @@ -64,7 +64,7 @@ func TestShouldMineOncePerRound(t *testing.T) { config := params.TestXDPoSMockChainConfig blockchain, _, block910, signer, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 910, config, nil) adaptor := blockchain.Engine().(*XDPoS.XDPoS) - minePeriod := config.XDPoS.V2.MinePeriod + minePeriod := config.XDPoS.V2.CurrentConfig.MinePeriod // Make sure we seal the parentBlock 910 _, err := adaptor.Seal(blockchain, block910, nil) diff --git a/consensus/tests/engine_v2_tests/proposed_block_test.go b/consensus/tests/engine_v2_tests/proposed_block_test.go index a85a59898d..db7485911b 100644 --- a/consensus/tests/engine_v2_tests/proposed_block_test.go +++ b/consensus/tests/engine_v2_tests/proposed_block_test.go @@ -261,7 +261,7 @@ func TestShouldNotSendVoteMessageIfAlreadyVoteForThisRound(t *testing.T) { select { case <-engineV2.BroadcastCh: t.Fatal("Should not trigger vote") - case <-time.After(5 * time.Second): + case <-time.After(3 * time.Second): // Shoud not trigger setNewRound round, _, _, _, highestVotedRound, _ = engineV2.GetPropertiesFaker() assert.Equal(t, types.Round(6), round) @@ -290,7 +290,7 @@ func TestShouldNotSendVoteMsgIfBlockInfoRoundNotEqualCurrentRound(t *testing.T) select { case <-engineV2.BroadcastCh: t.Fatal("Should not trigger vote") - case <-time.After(5 * time.Second): + case <-time.After(3 * time.Second): // Shoud not trigger setNewRound round, _, _, _, _, _ := engineV2.GetPropertiesFaker() assert.Equal(t, types.Round(8), round) @@ -334,7 +334,7 @@ func TestShouldNotSendVoteMsgIfBlockNotExtendedFromAncestor(t *testing.T) { select { case <-engineV2.BroadcastCh: t.Fatal("Should not trigger vote") - case <-time.After(5 * time.Second): + case <-time.After(3 * time.Second): // Shoud not trigger setNewRound round, _, _, _, _, _ := engineV2.GetPropertiesFaker() assert.Equal(t, types.Round(7), round) diff --git a/consensus/tests/engine_v2_tests/reward_test.go b/consensus/tests/engine_v2_tests/reward_test.go index 55dc4104ca..5b44560f9f 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.SwitchBlock.SetUint64(1800) + config.XDPoS.V2.FirstSwitchBlock.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.SwitchBlock.SetUint64(1800) + config.XDPoS.V2.FirstSwitchBlock.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 54a571eba9..943fd6fbbf 100644 --- a/consensus/tests/engine_v2_tests/timeout_test.go +++ b/consensus/tests/engine_v2_tests/timeout_test.go @@ -1,6 +1,7 @@ package engine_v2_tests import ( + "math/big" "strconv" "strings" "testing" @@ -41,11 +42,11 @@ func TestCountdownTimeoutNotToSendTimeoutMessageIfNotInMasternodeList(t *testing select { case <-engineV2.BroadcastCh: 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) engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2 engineV2.SetNewRoundFaker(blockchain, 1, true) @@ -83,8 +84,63 @@ func TestSyncInfoAfterReachTimeoutSnycThreadhold(t *testing.T) { 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 func TestTimeoutMessageHandlerSuccessfullyGenerateTCandSyncInfo(t *testing.T) { + params.TestXDPoSMockChainConfig.XDPoS.V2.CurrentConfig = params.TestV2Configs[0] blockchain, _, _, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 11, params.TestXDPoSMockChainConfig, nil) engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2 diff --git a/consensus/tests/engine_v2_tests/verify_header_test.go b/consensus/tests/engine_v2_tests/verify_header_test.go index e5e9236953..ff0b95c68d 100644 --- a/consensus/tests/engine_v2_tests/verify_header_test.go +++ b/consensus/tests/engine_v2_tests/verify_header_test.go @@ -27,8 +27,6 @@ func TestShouldVerifyBlock(t *testing.T) { assert.Nil(t, err) // Enable verify 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 blockchain, _, _, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, 910, &config, nil) adaptor := blockchain.Engine().(*XDPoS.XDPoS) @@ -97,8 +95,9 @@ func TestShouldVerifyBlock(t *testing.T) { err = adaptor.VerifyHeader(blockchain, parentNotExistBlock, true) assert.Equal(t, consensus.ErrUnknownAncestor, err) + block901 := blockchain.GetBlockByNumber(901).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) assert.Equal(t, utils.ErrInvalidTimestamp, err) @@ -107,7 +106,7 @@ func TestShouldVerifyBlock(t *testing.T) { err = adaptor.VerifyHeader(blockchain, invalidDifficultyBlock, true) assert.Equal(t, utils.ErrInvalidDifficulty, err) - // Creat an invalid QC round + // Create an invalid QC round proposedBlockInfo := &types.BlockInfo{ Hash: blockchain.GetBlockByNumber(902).Hash(), Round: types.Round(2), @@ -169,6 +168,62 @@ func TestShouldVerifyBlock(t *testing.T) { 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) { b, err := json.Marshal(params.TestXDPoSMockChainConfig) assert.Nil(t, err) @@ -179,8 +234,6 @@ func TestShouldFailIfNotEnoughQCSignatures(t *testing.T) { assert.Nil(t, err) // Enable verify 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 blockchain, _, currentBlock, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, 902, &config, nil) adaptor := blockchain.Engine().(*XDPoS.XDPoS) @@ -232,8 +285,6 @@ func TestShouldVerifyHeaders(t *testing.T) { assert.Nil(t, err) // Enable verify 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 blockchain, _, _, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 910, &config, nil) adaptor := blockchain.Engine().(*XDPoS.XDPoS) @@ -273,8 +324,6 @@ func TestShouldVerifyHeadersEvenIfParentsNotYetWrittenIntoDB(t *testing.T) { assert.Nil(t, err) // Enable verify 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 blockchain, _, block910, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, 910, &config, nil) adaptor := blockchain.Engine().(*XDPoS.XDPoS) @@ -283,12 +332,12 @@ func TestShouldVerifyHeadersEvenIfParentsNotYetWrittenIntoDB(t *testing.T) { // Create block 911 but don't write into DB 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) // Create block 912 and not write into DB as well 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) headersTobeVerified = append(headersTobeVerified, block910.Header(), block911.Header(), block912.Header()) diff --git a/core/blockchain.go b/core/blockchain.go index 9b23f71333..127abcb199 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.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 maxMasternodes = common.MaxMasternodesV2 } else { diff --git a/eth/hooks/engine_v2_hooks.go b/eth/hooks/engine_v2_hooks.go index e1f44305ad..c9ec604635 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.SwitchBlock.Uint64() + comebackHeight := (common.LimitPenaltyEpochV2+1)*chain.Config().XDPoS.Epoch + chain.Config().XDPoS.V2.FirstSwitchBlock.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.SwitchBlock.Uint64()+1 { + if number == chain.Config().XDPoS.V2.FirstSwitchBlock.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.SwitchBlock.Uint64()+1 { + if isEpochSwitch && i != chain.Config().XDPoS.V2.FirstSwitchBlock.Uint64()+1 { epochCount += 1 if epochCount == signEpochCount { endBlockNumber = header.Number.Uint64() - 1 diff --git a/miner/worker.go b/miner/worker.go index 299f9a9aa0..86ea4f125c 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -267,8 +267,7 @@ func (self *worker) update() { defer self.chainSideSub.Unsubscribe() // timeout waiting for v1 inital value - // TODO: Read value from config after we decide where is the config - waitPeriod := 10 + waitPeriod := 2 WaitPeriodCh := self.engine.(*XDPoS.XDPoS).WaitPeriodCh defer close(WaitPeriodCh) diff --git a/params/config.go b/params/config.go index 75454b14b9..9501255804 100644 --- a/params/config.go +++ b/params/config.go @@ -26,6 +26,7 @@ import ( const ( ConsensusEngineVersion1 = "v1" ConsensusEngineVersion2 = "v2" + Default = 0 ) var ( @@ -36,30 +37,92 @@ var ( ) var ( - XDPoSV2Config = &V2{ - SwitchBlock: big.NewInt(9999999999), - CertThreshold: common.MaxMasternodesV2*2/3 + 1, - TimeoutSyncThreshold: 3, - TimeoutPeriod: 60, - WaitPeriod: 10, - MinePeriod: 10, + MainnetV2Configs = map[uint64]*V2Config{ + Default: { + SwitchBlock: big.NewInt(9999999999), + CertThreshold: common.MaxMasternodesV2*2/3 + 1, + TimeoutSyncThreshold: 3, + TimeoutPeriod: 60, + 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{ - SwitchBlock: big.NewInt(900), - CertThreshold: 3, - TimeoutSyncThreshold: 2, - TimeoutPeriod: 10, - WaitPeriod: 1, - MinePeriod: 2, - SkipV2Validation: true, + TestV2Configs = map[uint64]*V2Config{ + Default: { + SwitchBlock: big.NewInt(900), + 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), + 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), - CertThreshold: common.MaxMasternodesV2*2/3 + 1, - TimeoutSyncThreshold: 5, - TimeoutPeriod: 25, - WaitPeriod: 10, - MinePeriod: 10, + + DevnetV2Configs = map[uint64]*V2Config{ + Default: { + SwitchBlock: big.NewInt(7074000), + CertThreshold: 4, + TimeoutSyncThreshold: 5, + 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 @@ -78,7 +141,11 @@ var ( RewardCheckpoint: 900, Gap: 450, FoudationWalletAddr: common.HexToAddress("xdc92a289fe95a85c53b8d0d113cbaef0c1ec98ac65"), - V2: XDPoSV2Config, + V2: &V2{ + FirstSwitchBlock: MainnetV2Configs[0].SwitchBlock, + CurrentConfig: MainnetV2Configs[0], + AllConfigs: MainnetV2Configs, + }, }, } @@ -116,11 +183,16 @@ var ( RewardCheckpoint: 900, Gap: 450, 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{ ChainId: big.NewInt(551), HomesteadBlock: big.NewInt(1), @@ -136,7 +208,11 @@ var ( RewardCheckpoint: 900, Gap: 450, FoudationWalletAddr: common.HexToAddress("0x746249c61f5832c5eed53172776b460491bdcd5c"), - V2: DevnetXDPoSV2Config, + V2: &V2{ + FirstSwitchBlock: DevnetV2Configs[0].SwitchBlock, + CurrentConfig: DevnetV2Configs[0], + AllConfigs: DevnetV2Configs, + }, }, } @@ -155,7 +231,11 @@ var ( XDPoS: &XDPoSConfig{ Period: 15, 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} // 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} TestRules = TestChainConfig.Rules(new(big.Int)) @@ -242,28 +347,69 @@ 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 +} + +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 - SkipV2Validation bool //Skip Block Validation for testing purpose, V2 consensus only } // 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 { 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 c.V2 != nil && c.V2.SwitchBlock != nil && num.Cmp(c.V2.SwitchBlock) > 0 { - if skipExtraCheck { - return ConsensusEngineVersion2 - } - if len(extraByte) == 0 || extraByte[0] != 2 { - return ConsensusEngineVersion1 - } + 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