* typo and checkYourturnWithinFinalisedMasternodes func name to yourturn

* remove redundant code from verifyQC

* Verify QC to optionally pass parent header. This is used to help verifyHeaders

* move difficulty into its own file
This commit is contained in:
Jerome 2022-03-27 20:39:40 +11:00 committed by GitHub
parent b790b077c9
commit b98005a8dd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 82 additions and 46 deletions

View file

@ -0,0 +1,14 @@
package engine_v2
import (
"math/big"
"github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/consensus"
"github.com/XinFinOrg/XDPoSChain/core/types"
)
// TODO: what should be new difficulty
func (x *XDPoS_v2) calcDifficulty(chain consensus.ChainReader, parent *types.Header, signer common.Address) *big.Int {
return big.NewInt(1)
}

View file

@ -143,7 +143,7 @@ func (x *XDPoS_v2) Initial(chain consensus.ChainReader, header *types.Header) er
quorumCert = &utils.QuorumCert{ quorumCert = &utils.QuorumCert{
ProposedBlockInfo: blockInfo, ProposedBlockInfo: blockInfo,
Signatures: nil, Signatures: nil,
GapNumber: header.Number.Uint64()-x.config.Gap, GapNumber: header.Number.Uint64() - x.config.Gap,
} }
// can not call processQC because round is equal to default // can not call processQC because round is equal to default
@ -201,7 +201,7 @@ func (x *XDPoS_v2) Initial(chain consensus.ChainReader, header *types.Header) er
return nil return nil
} }
// Check if it's my turm to mine a block. Note: The second return value `preIndex` is useless in V2 engine // Check if it's my turn to mine a block. Note: The second return value `preIndex` is useless in V2 engine
func (x *XDPoS_v2) YourTurn(chain consensus.ChainReader, parent *types.Header, signer common.Address) (bool, error) { func (x *XDPoS_v2) YourTurn(chain consensus.ChainReader, parent *types.Header, signer common.Address) (bool, error) {
x.lock.RLock() x.lock.RLock()
defer x.lock.RUnlock() defer x.lock.RUnlock()
@ -222,7 +222,7 @@ func (x *XDPoS_v2) YourTurn(chain consensus.ChainReader, parent *types.Header, s
} }
round := x.currentRound round := x.currentRound
isMyTurn, err := x.checkYourturnWithinFinalisedMasternodes(chain, round, parent, signer) isMyTurn, err := x.yourturn(chain, round, parent, signer)
if err != nil { if err != nil {
log.Error("[Yourturn] Error while checking if i am qualified to mine", "round", round, "error", err) log.Error("[Yourturn] Error while checking if i am qualified to mine", "round", round, "error", err)
} }
@ -269,7 +269,7 @@ func (x *XDPoS_v2) Prepare(chain consensus.ChainReader, header *types.Header) er
signer := x.signer signer := x.signer
x.signLock.RUnlock() x.signLock.RUnlock()
isMyTurn, err := x.checkYourturnWithinFinalisedMasternodes(chain, currentRound, parent, signer) isMyTurn, err := x.yourturn(chain, currentRound, parent, signer)
if err != nil { if err != nil {
log.Error("[Prepare] Error while checking if it's still my turn to mine", "round", currentRound, "ParentHash", parent.Hash().Hex(), "ParentNumber", parent.Number.Uint64(), "error", err) log.Error("[Prepare] Error while checking if it's still my turn to mine", "round", currentRound, "ParentHash", parent.Hash().Hex(), "ParentNumber", parent.Number.Uint64(), "error", err)
return err return err
@ -404,12 +404,6 @@ func (x *XDPoS_v2) CalcDifficulty(chain consensus.ChainReader, time uint64, pare
return x.calcDifficulty(chain, parent, x.signer) return x.calcDifficulty(chain, parent, x.signer)
} }
// TODO: what should be new difficulty
func (x *XDPoS_v2) calcDifficulty(chain consensus.ChainReader, parent *types.Header, signer common.Address) *big.Int {
// TODO: The difference of round number between parent round and current round
return big.NewInt(1)
}
func (x *XDPoS_v2) IsAuthorisedAddress(chain consensus.ChainReader, header *types.Header, address common.Address) bool { func (x *XDPoS_v2) IsAuthorisedAddress(chain consensus.ChainReader, header *types.Header, address common.Address) bool {
x.lock.RLock() x.lock.RLock()
defer x.lock.RUnlock() defer x.lock.RUnlock()
@ -523,7 +517,7 @@ func (x *XDPoS_v2) VerifySyncInfoMessage(chain consensus.ChainReader, syncInfo *
return false, nil return false, nil
} }
err := x.verifyQC(chain, syncInfo.HighestQuorumCert) err := x.verifyQC(chain, syncInfo.HighestQuorumCert, nil)
if err != nil { if err != nil {
log.Warn("SyncInfo message verification failed due to QC", "error", err) log.Warn("SyncInfo message verification failed due to QC", "error", err)
return false, err return false, err
@ -653,7 +647,7 @@ func (x *XDPoS_v2) ProposedBlockHandler(chain consensus.ChainReader, blockHeader
return err return err
} }
err = x.verifyQC(chain, quorumCert) err = x.verifyQC(chain, quorumCert, nil)
if err != nil { if err != nil {
log.Error("[ProposedBlockHandler] Fail to verify QC", "Extra round", round, "QC proposed BlockInfo Hash", quorumCert.ProposedBlockInfo.Hash) log.Error("[ProposedBlockHandler] Fail to verify QC", "Extra round", round, "QC proposed BlockInfo Hash", quorumCert.ProposedBlockInfo.Hash)
return err return err
@ -732,7 +726,7 @@ func (x *XDPoS_v2) VerifyBlockInfo(blockChainReader consensus.ChainReader, block
return nil return nil
} }
func (x *XDPoS_v2) verifyQC(blockChainReader consensus.ChainReader, quorumCert *utils.QuorumCert) error { func (x *XDPoS_v2) verifyQC(blockChainReader consensus.ChainReader, quorumCert *utils.QuorumCert, parentHeader *types.Header) error {
/* /*
1. Check if num of QC signatures is >= x.config.v2.CertThreshold 1. Check if num of QC signatures is >= x.config.v2.CertThreshold
2. Get epoch master node list by hash 2. Get epoch master node list by hash
@ -743,7 +737,7 @@ func (x *XDPoS_v2) verifyQC(blockChainReader consensus.ChainReader, quorumCert *
4. Verify gapNumber = epochSwitchNumber - epochSwitchNumber%Epoch - Gap 4. Verify gapNumber = epochSwitchNumber - epochSwitchNumber%Epoch - Gap
5. Verify blockInfo 5. Verify blockInfo
*/ */
epochInfo, err := x.getEpochSwitchInfo(blockChainReader, nil, quorumCert.ProposedBlockInfo.Hash) epochInfo, err := x.getEpochSwitchInfo(blockChainReader, parentHeader, quorumCert.ProposedBlockInfo.Hash)
if err != nil { if err != nil {
log.Error("[verifyQC] Error when getting epoch switch Info to verify QC", "Error", err) log.Error("[verifyQC] Error when getting epoch switch Info to verify QC", "Error", err)
return fmt.Errorf("Fail to verify QC due to failure in getting epoch switch info") return fmt.Errorf("Fail to verify QC due to failure in getting epoch switch info")
@ -764,12 +758,6 @@ func (x *XDPoS_v2) verifyQC(blockChainReader consensus.ChainReader, quorumCert *
return utils.ErrInvalidQC return utils.ErrInvalidQC
} }
epochInfo, err = x.getEpochSwitchInfo(blockChainReader, nil, quorumCert.ProposedBlockInfo.Hash)
if err != nil {
log.Error("[verifyQC] Error when getting epoch switch Info to verify QC", "Error", err)
return fmt.Errorf("Fail to verify QC due to failure in getting epoch switch info")
}
var wg sync.WaitGroup var wg sync.WaitGroup
wg.Add(len(signatures)) wg.Add(len(signatures))
var haveError error var haveError error

View file

@ -12,10 +12,10 @@ import (
) )
// Using parent and current round to find the finalised master node list(with penalties applied from last epoch) // Using parent and current round to find the finalised master node list(with penalties applied from last epoch)
func (x *XDPoS_v2) checkYourturnWithinFinalisedMasternodes(chain consensus.ChainReader, round utils.Round, parent *types.Header, signer common.Address) (bool, error) { func (x *XDPoS_v2) yourturn(chain consensus.ChainReader, round utils.Round, parent *types.Header, signer common.Address) (bool, error) {
isEpochSwitch, _, err := x.isEpochSwitchAtRound(round, parent) isEpochSwitch, _, err := x.isEpochSwitchAtRound(round, parent)
if err != nil { if err != nil {
log.Error("[checkYourturnWithinFinalisedMasternodes] check epoch switch at round failed", "Error", err) log.Error("[yourturn] check epoch switch at round failed", "Error", err)
return false, err return false, err
} }
var masterNodes []common.Address var masterNodes []common.Address
@ -24,13 +24,13 @@ func (x *XDPoS_v2) checkYourturnWithinFinalisedMasternodes(chain consensus.Chain
// the initial master nodes of v1->v2 switch contains penalties node // the initial master nodes of v1->v2 switch contains penalties node
_, _, masterNodes, err = x.getExtraFields(parent) _, _, masterNodes, err = x.getExtraFields(parent)
if err != nil { if err != nil {
log.Error("[checkYourturnWithinFinalisedMasternodes] Cannot find snapshot at gap num of last V1", "err", err, "number", x.config.V2.SwitchBlock.Uint64()) log.Error("[yourturn] Cannot find snapshot at gap num of last V1", "err", err, "number", x.config.V2.SwitchBlock.Uint64())
return false, err return false, err
} }
} else { } else {
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())
if err != nil { if err != nil {
log.Error("[checkYourturnWithinFinalisedMasternodes] Cannot calcMasternodes at gap num ", "err", err, "parent number", parent.Number) log.Error("[yourturn] Cannot calcMasternodes at gap num ", "err", err, "parent number", parent.Number)
return false, err return false, err
} }
} }
@ -40,26 +40,26 @@ func (x *XDPoS_v2) checkYourturnWithinFinalisedMasternodes(chain consensus.Chain
} }
if len(masterNodes) == 0 { if len(masterNodes) == 0 {
log.Error("[checkYourturnWithinFinalisedMasternodes] Fail to find any master nodes from current block round epoch", "Hash", parent.Hash(), "CurrentRound", round, "Number", parent.Number) log.Error("[yourturn] Fail to find any master nodes from current block round epoch", "Hash", parent.Hash(), "CurrentRound", round, "Number", parent.Number)
return false, errors.New("masternodes not found") return false, errors.New("masternodes not found")
} }
curIndex := utils.Position(masterNodes, signer) curIndex := utils.Position(masterNodes, signer)
if curIndex == -1 { if curIndex == -1 {
log.Debug("[checkYourturnWithinFinalisedMasternodes] Not authorised signer", "MN", masterNodes, "Hash", parent.Hash(), "signer", signer) log.Debug("[yourturn] Not authorised signer", "MN", masterNodes, "Hash", parent.Hash(), "signer", signer)
return false, nil return false, nil
} }
for i, s := range masterNodes { for i, s := range masterNodes {
log.Debug("[checkYourturnWithinFinalisedMasternodes] Masternode:", "index", i, "address", s.String(), "parentBlockNum", parent.Number) log.Debug("[yourturn] Masternode:", "index", i, "address", s.String(), "parentBlockNum", parent.Number)
} }
leaderIndex := uint64(round) % x.config.Epoch % uint64(len(masterNodes)) leaderIndex := uint64(round) % x.config.Epoch % uint64(len(masterNodes))
if masterNodes[leaderIndex] != signer { if masterNodes[leaderIndex] != signer {
log.Debug("[checkYourturnWithinFinalisedMasternodes] Not my turn", "curIndex", curIndex, "leaderIndex", leaderIndex, "Hash", parent.Hash().Hex(), "masterNodes[leaderIndex]", masterNodes[leaderIndex], "signer", signer) log.Debug("[yourturn] Not my turn", "curIndex", curIndex, "leaderIndex", leaderIndex, "Hash", parent.Hash().Hex(), "masterNodes[leaderIndex]", masterNodes[leaderIndex], "signer", signer)
return false, nil return false, nil
} }
log.Debug("[checkYourturnWithinFinalisedMasternodes] Yes, it's my turn based on parent block", "ParentHash", parent.Hash().Hex(), "ParentBlockNumber", parent.Number.Uint64()) log.Debug("[yourturn] Yes, it's my turn based on parent block", "ParentHash", parent.Hash().Hex(), "ParentBlockNumber", parent.Number.Uint64())
return true, nil return true, nil
} }

View file

@ -39,6 +39,22 @@ func (x *XDPoS_v2) verifyHeader(chain consensus.ChainReader, header *types.Heade
} }
} }
// Ensure that the block's timestamp isn't too close to it's parent
var parent *types.Header
number := header.Number.Uint64()
if len(parents) > 0 {
parent = parents[len(parents)-1]
} else {
parent = chain.GetHeader(header.ParentHash, number-1)
}
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() {
return utils.ErrInvalidTimestamp
}
// Verify this is truely a v2 block first // Verify this is truely a v2 block first
quorumCert, round, _, err := x.getExtraFields(header) quorumCert, round, _, err := x.getExtraFields(header)
if err != nil { if err != nil {
@ -48,7 +64,7 @@ func (x *XDPoS_v2) verifyHeader(chain consensus.ChainReader, header *types.Heade
return utils.ErrRoundInvalid return utils.ErrRoundInvalid
} }
err = x.verifyQC(chain, quorumCert) err = x.verifyQC(chain, quorumCert, parent)
if err != nil { if err != nil {
log.Warn("[verifyHeader] fail to verify QC", "QCNumber", quorumCert.ProposedBlockInfo.Number, "QCsigLength", len(quorumCert.Signatures)) log.Warn("[verifyHeader] fail to verify QC", "QCNumber", quorumCert.ProposedBlockInfo.Number, "QCsigLength", len(quorumCert.Signatures))
return err return err
@ -105,22 +121,6 @@ func (x *XDPoS_v2) verifyHeader(chain consensus.ChainReader, header *types.Heade
return err return err
} }
// Ensure that the block's timestamp isn't too close to it's parent
var parent *types.Header
number := header.Number.Uint64()
if len(parents) > 0 {
parent = parents[len(parents)-1]
} else {
parent = chain.GetHeader(header.ParentHash, number-1)
}
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() {
return utils.ErrInvalidTimestamp
}
_, penalties, err := x.calcMasternodes(chain, header.Number, header.ParentHash) _, penalties, err := x.calcMasternodes(chain, header.Number, header.ParentHash)
if err != nil { if err != nil {
log.Error("[verifyHeader] Fail to calculate master nodes list with penalty", "Number", header.Number, "Hash", header.Hash()) log.Error("[verifyHeader] Fail to calculate master nodes list with penalty", "Number", header.Number, "Hash", header.Hash())

View file

@ -218,3 +218,37 @@ func TestShouldFailIfNotEnoughQCSignatures(t *testing.T) {
assert.Equal(t, utils.ErrInvalidQC, err) assert.Equal(t, utils.ErrInvalidQC, err)
} }
func TestShouldVerifyHeaders(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
// 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, 0)
adaptor := blockchain.Engine().(*XDPoS.XDPoS)
// var results <-chan error
// var abort <-chan struct{}
// Happy path
var happyPathHeaders []*types.Header
happyPathHeaders = append(happyPathHeaders, blockchain.GetBlockByNumber(899).Header(), blockchain.GetBlockByNumber(900).Header(), blockchain.GetBlockByNumber(901).Header(), blockchain.GetBlockByNumber(902).Header())
// Randomly set full verify
var fullVerifies []bool
fullVerifies = append(fullVerifies, false, true, true, false)
_, results := adaptor.VerifyHeaders(blockchain, happyPathHeaders, fullVerifies)
select {
case result := <-results:
assert.Nil(t, result)
case <-time.After(time.Duration(2) * time.Second): // It should be very fast to verify headers
t.Fatalf("Taking too long to verify headers")
}
}