diff --git a/consensus/XDPoS/engines/engine_v2/engine.go b/consensus/XDPoS/engines/engine_v2/engine.go index 6fa6dd51b8..a87650c022 100644 --- a/consensus/XDPoS/engines/engine_v2/engine.go +++ b/consensus/XDPoS/engines/engine_v2/engine.go @@ -45,6 +45,7 @@ type XDPoS_v2 struct { waitPeriodCh chan int timeoutWorker *countdown.CountdownTimer // Timer to generate broadcast timeout msg if threashold reached + timeoutCount int // number of timeout being sent timeoutPool *utils.Pool votePool *utils.Pool @@ -62,7 +63,7 @@ type XDPoS_v2 struct { func New(config *params.XDPoSConfig, db ethdb.Database, waitPeriodCh chan int) *XDPoS_v2 { // Setup Timer - duration := time.Duration(config.V2.TimeoutWorkerDuration) * time.Second + duration := time.Duration(config.V2.TimeoutPeriod) * time.Second timer := countdown.NewCountDown(duration) timeoutPool := utils.NewPool(config.V2.CertThreshold) @@ -162,7 +163,7 @@ func (x *XDPoS_v2) Initial(chain consensus.ChainReader, masternodes []common.Add } // Initial timeout - log.Info("[Initial] miner wait period", "period", x.config.WaitPeriod) + log.Info("[Initial] miner wait period", "period", x.config.V2.WaitPeriod) // avoid deadlock go func() { x.waitPeriodCh <- x.config.V2.WaitPeriod @@ -1184,6 +1185,7 @@ func (x *XDPoS_v2) processTC(blockChainReader consensus.ChainReader, timeoutCert */ func (x *XDPoS_v2) setNewRound(blockChainReader consensus.ChainReader, round utils.Round) error { x.currentRound = round + x.timeoutCount = 0 //TODO: tell miner now it's a new round and start mine if it's leader x.timeoutWorker.Reset(blockChainReader) //TODO: vote pools @@ -1352,6 +1354,14 @@ func (x *XDPoS_v2) OnCountdownTimeout(time time.Time, chain interface{}) error { log.Error("Error while sending out timeout message at time: ", time) return err } + + x.timeoutCount++ + if x.timeoutCount%x.config.V2.TimeoutSyncThreshold == 0 { + log.Info("[OnCountdownTimeout] timeout sync threadhold reached, send syncInfo message") + syncInfo := x.getSyncInfo() + x.broadcastToBftChannel(syncInfo) + } + return nil } diff --git a/consensus/tests/timeout_test.go b/consensus/tests/timeout_test.go index 82942ff94f..aa1b2920f7 100644 --- a/consensus/tests/timeout_test.go +++ b/consensus/tests/timeout_test.go @@ -1,11 +1,13 @@ package tests import ( + "fmt" "testing" "github.com/XinFinOrg/XDPoSChain/accounts" "github.com/XinFinOrg/XDPoSChain/consensus/XDPoS" "github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils" + "github.com/XinFinOrg/XDPoSChain/log" "github.com/XinFinOrg/XDPoSChain/params" "github.com/stretchr/testify/assert" ) @@ -19,9 +21,47 @@ func TestCountdownTimeoutToSendTimeoutMessage(t *testing.T) { assert.Equal(t, poolSize, 1) assert.NotNil(t, timeoutMsg) assert.Equal(t, uint64(1350), timeoutMsg.(*utils.Timeout).GapNumber) + fmt.Println(timeoutMsg.(*utils.Timeout).GapNumber) assert.Equal(t, utils.Round(1), timeoutMsg.(*utils.Timeout).Round) } +func TestSyncInfoAfterReachTimeoutSnycThreadhold(t *testing.T) { + blockchain, _, _, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 2251, params.TestXDPoSMockChainConfig, 0) + engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2 + + // 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 *utils.Timeout: + timeoutCounter++ + case *utils.SyncInfo: + syncInfoCounter++ + default: + log.Error("Unknown message type received", "value", v) + } + } + assert.Equal(t, 2, timeoutCounter) + assert.Equal(t, 1, syncInfoCounter) + + t.Log("waiting for another consecutive period") + // another consecutive period + for i := 0; i < 3; i++ { + obj := <-engineV2.BroadcastCh + switch v := obj.(type) { + case *utils.Timeout: + timeoutCounter++ + case *utils.SyncInfo: + syncInfoCounter++ + default: + log.Error("Unknown message type received", "value", v) + } + } + assert.Equal(t, 4, timeoutCounter) + assert.Equal(t, 2, syncInfoCounter) +} + // Timeout handler func TestTimeoutMessageHandlerSuccessfullyGenerateTCandSyncInfo(t *testing.T) { blockchain, _, _, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 11, params.TestXDPoSMockChainConfig, 0) diff --git a/params/config.go b/params/config.go index 7ad2cf8ec4..d7b1c031a8 100644 --- a/params/config.go +++ b/params/config.go @@ -36,23 +36,25 @@ var ( var ( XDPoSV2Config = &V2{ - TimeoutWorkerDuration: 50, - CertThreshold: common.MaxMasternodesV2*2/3 + 1, + TimeoutPeriod: 50, + CertThreshold: common.MaxMasternodesV2*2/3 + 1, } TestXDPoSV2Config = &V2{ - TimeoutWorkerDuration: 10, - CertThreshold: 3, - WaitPeriod: 1, - MinePeriod: 2, - SwitchBlock: big.NewInt(900), - SkipV2Validation: true, + SwitchBlock: big.NewInt(900), + CertThreshold: 3, + TimeoutSyncThreshold: 2, + TimeoutPeriod: 10, + WaitPeriod: 1, + MinePeriod: 2, + SkipV2Validation: true, } DevnetXDPoSV2Config = &V2{ - SwitchBlock: big.NewInt(7218000), - TimeoutWorkerDuration: 50, - CertThreshold: 6, - WaitPeriod: 2, - MinePeriod: 10, + SwitchBlock: big.NewInt(7218000), + CertThreshold: 6, + TimeoutSyncThreshold: 5, + TimeoutPeriod: 50, + WaitPeriod: 2, + MinePeriod: 10, } // XDPoSChain mainnet config @@ -202,18 +204,18 @@ type XDPoSConfig struct { RewardCheckpoint uint64 `json:"rewardCheckpoint"` // Checkpoint block for calculate rewards. Gap uint64 `json:"gap"` // Gap time preparing for the next epoch FoudationWalletAddr common.Address `json:"foudationWalletAddr"` // Foundation Address Wallet - WaitPeriod int `json:"waitPeriod"` // Miner wait period SkipV1Validation bool //Skip Block Validation for testing purpose, V1 consensus only V2 *V2 `json:"v2"` } type V2 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 - TimeoutWorkerDuration int64 `json:"timeoutWorkerDuration"` // 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 + 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.