From c7a42fd7c94196f3013ca370be59d30e304dd024 Mon Sep 17 00:00:00 2001 From: Liam Date: Wed, 27 Dec 2023 22:14:13 +1100 Subject: [PATCH] resolve sync issue by passing right round number (#384) --- consensus/XDPoS/engines/engine_v2/engine.go | 8 ++-- consensus/XDPoS/engines/engine_v2/mining.go | 2 +- .../XDPoS/engines/engine_v2/verifyHeader.go | 2 +- .../engine_v2_tests/verify_header_test.go | 44 +++++++++++++++++++ eth/downloader/queue.go | 1 + eth/hooks/engine_v2_hooks.go | 4 +- params/version.go | 2 +- 7 files changed, 54 insertions(+), 9 deletions(-) diff --git a/consensus/XDPoS/engines/engine_v2/engine.go b/consensus/XDPoS/engines/engine_v2/engine.go index 62a34d1a09..3e7eabd8df 100644 --- a/consensus/XDPoS/engines/engine_v2/engine.go +++ b/consensus/XDPoS/engines/engine_v2/engine.go @@ -336,7 +336,7 @@ func (x *XDPoS_v2) Prepare(chain consensus.ChainReader, header *types.Header) er return err } if isEpochSwitchBlock { - masterNodes, penalties, err := x.calcMasternodes(chain, header.Number, header.ParentHash) + masterNodes, penalties, err := x.calcMasternodes(chain, header.Number, header.ParentHash, currentRound) if err != nil { return err } @@ -997,9 +997,9 @@ func (x *XDPoS_v2) GetStandbynodes(chain consensus.ChainReader, header *types.He } // Calculate masternodes for a block number and parent hash. In V2, truncating candidates[:MaxMasternodes] is done in this function. -func (x *XDPoS_v2) calcMasternodes(chain consensus.ChainReader, blockNum *big.Int, parentHash common.Hash) ([]common.Address, []common.Address, error) { +func (x *XDPoS_v2) calcMasternodes(chain consensus.ChainReader, blockNum *big.Int, parentHash common.Hash, round types.Round) ([]common.Address, []common.Address, error) { // using new max masterndoes - maxMasternodes := x.config.V2.Config(uint64(x.currentRound)).MaxMasternodes + maxMasternodes := x.config.V2.Config(uint64(round)).MaxMasternodes snap, err := x.getSnapshot(chain, blockNum.Uint64(), false) if err != nil { log.Error("[calcMasternodes] Adaptor v2 getSnapshot has error", "err", err) @@ -1081,7 +1081,7 @@ func (x *XDPoS_v2) allowedToSend(chain consensus.ChainReader, blockHeader *types for _, mn := range masterNodes { log.Debug("[allowedToSend] Master node list", "masterNodeAddress", mn.Hash()) } - log.Info("[allowedToSend] Not in the Masternode list, not suppose to send message", "sendType", sendType, "MyAddress", signer.Hex()) + log.Debug("[allowedToSend] Not in the Masternode list, not suppose to send message", "sendType", sendType, "MyAddress", signer.Hex()) return false } diff --git a/consensus/XDPoS/engines/engine_v2/mining.go b/consensus/XDPoS/engines/engine_v2/mining.go index 72722de75e..69c861acc7 100644 --- a/consensus/XDPoS/engines/engine_v2/mining.go +++ b/consensus/XDPoS/engines/engine_v2/mining.go @@ -25,7 +25,7 @@ func (x *XDPoS_v2) yourturn(chain consensus.ChainReader, round types.Round, pare } var masterNodes []common.Address if isEpochSwitch { - masterNodes, _, err = x.calcMasternodes(chain, big.NewInt(0).Add(parent.Number, big.NewInt(1)), parent.Hash()) + masterNodes, _, err = x.calcMasternodes(chain, big.NewInt(0).Add(parent.Number, big.NewInt(1)), parent.Hash(), round) if err != nil { log.Error("[yourturn] Cannot calcMasternodes at gap num ", "err", err, "parent number", parent.Number) return false, err diff --git a/consensus/XDPoS/engines/engine_v2/verifyHeader.go b/consensus/XDPoS/engines/engine_v2/verifyHeader.go index 1655a34d94..a759d7455f 100644 --- a/consensus/XDPoS/engines/engine_v2/verifyHeader.go +++ b/consensus/XDPoS/engines/engine_v2/verifyHeader.go @@ -115,7 +115,7 @@ func (x *XDPoS_v2) verifyHeader(chain consensus.ChainReader, header *types.Heade return utils.ErrInvalidCheckpointSigners } - localMasterNodes, localPenalties, err := x.calcMasternodes(chain, header.Number, header.ParentHash) + localMasterNodes, localPenalties, err := x.calcMasternodes(chain, header.Number, header.ParentHash, round) masterNodes = localMasterNodes if err != nil { log.Error("[verifyHeader] Fail to calculate master nodes list with penalty", "Number", header.Number, "Hash", header.Hash()) diff --git a/consensus/tests/engine_v2_tests/verify_header_test.go b/consensus/tests/engine_v2_tests/verify_header_test.go index a1936d44a1..a8b2eaec30 100644 --- a/consensus/tests/engine_v2_tests/verify_header_test.go +++ b/consensus/tests/engine_v2_tests/verify_header_test.go @@ -260,6 +260,50 @@ func TestConfigSwitchOnDifferentCertThreshold(t *testing.T) { assert.Equal(t, utils.ErrValidatorNotWithinMasternodes, err) } +/* + 1. Insert 20 masternode before gap block + 2. Prepare 20 masternode block header with round 9000 + 3. verify this header while node is on round 899, + This is to simulate node is syncing from remote during config switch +*/ +func TestConfigSwitchOnDifferentMasternodeCount(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, _, currentBlock, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, int(config.XDPoS.Epoch)*2, &config, nil) + adaptor := blockchain.Engine().(*XDPoS.XDPoS) + x := adaptor.EngineV2 + + // Generate round 900 header, num 1800 + header1800 := blockchain.GetBlockByNumber(1800).Header() + + snap, err := x.GetSnapshot(blockchain, currentBlock.Header()) + assert.Nil(t, err) + assert.Equal(t, len(snap.NextEpochMasterNodes), 20) + header1800.Validators = []byte{} + for i := 0; i < 20; i++ { + header1800.Validators = append(header1800.Validators, snap.NextEpochMasterNodes[i].Bytes()...) + } + + round, err := x.GetRoundNumber(header1800) + assert.Nil(t, err) + assert.Equal(t, round, types.Round(900)) + + adaptor.EngineV2.SetNewRoundFaker(blockchain, 899, false) + + err = adaptor.VerifyHeader(blockchain, header1800, true) + + // error ErrValidatorNotWithinMasternodes means verifyQC is passed and move to next verification process + assert.Equal(t, utils.ErrValidatorNotWithinMasternodes, err) +} + func TestConfigSwitchOnDifferentMindPeriod(t *testing.T) { b, err := json.Marshal(params.TestXDPoSMockChainConfig) assert.Nil(t, err) diff --git a/eth/downloader/queue.go b/eth/downloader/queue.go index 2a093f075b..a67f12c477 100644 --- a/eth/downloader/queue.go +++ b/eth/downloader/queue.go @@ -510,6 +510,7 @@ func (q *queue) reserveHeaders(p *peerConnection, count int, taskPool map[common // If we're the first to request this task, initialise the result container index := int(header.Number.Int64() - int64(q.resultOffset)) if index >= len(q.resultCache) || index < 0 { + log.Error("index allocation went beyond available resultCache space", "index", index, "len.resultCache", len(q.resultCache), "blockNum", header.Number.Int64(), "resultOffset", q.resultOffset) common.Report("index allocation went beyond available resultCache space") return nil, false, errInvalidChain } diff --git a/eth/hooks/engine_v2_hooks.go b/eth/hooks/engine_v2_hooks.go index 599a3af98d..4047014a4d 100644 --- a/eth/hooks/engine_v2_hooks.go +++ b/eth/hooks/engine_v2_hooks.go @@ -37,9 +37,9 @@ func AttachConsensusV2Hooks(adaptor *XDPoS.XDPoS, bc *core.BlockChain, chainConf break } log.Info("[V2 Hook Penalty] parentHeader is nil, wait block to be writen in disk", "parentNumber", parentNumber) - time.Sleep(200 * time.Millisecond) // 0.2s + time.Sleep(time.Second) // 1s - if timeout > 50 { // wait over 10s + if timeout > 30 { // wait over 30s log.Error("[V2 Hook Penalty] parentHeader is nil, wait too long not writen in to disk", "parentNumber", parentNumber) return []common.Address{}, fmt.Errorf("parentHeader is nil") } diff --git a/params/version.go b/params/version.go index b31c431d86..6bb11eab6f 100644 --- a/params/version.go +++ b/params/version.go @@ -23,7 +23,7 @@ import ( const ( VersionMajor = 2 // Major version component of the current release VersionMinor = 0 // Minor version component of the current release - VersionPatch = 0 // Patch version component of the current release + VersionPatch = 1 // Patch version component of the current release VersionMeta = "beta1" // Version metadata to append to the version string )