* 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{
ProposedBlockInfo: blockInfo,
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
@ -201,7 +201,7 @@ func (x *XDPoS_v2) Initial(chain consensus.ChainReader, header *types.Header) er
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) {
x.lock.RLock()
defer x.lock.RUnlock()
@ -222,7 +222,7 @@ func (x *XDPoS_v2) YourTurn(chain consensus.ChainReader, parent *types.Header, s
}
round := x.currentRound
isMyTurn, err := x.checkYourturnWithinFinalisedMasternodes(chain, round, parent, signer)
isMyTurn, err := x.yourturn(chain, round, parent, signer)
if err != nil {
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
x.signLock.RUnlock()
isMyTurn, err := x.checkYourturnWithinFinalisedMasternodes(chain, currentRound, parent, signer)
isMyTurn, err := x.yourturn(chain, currentRound, parent, signer)
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)
return err
@ -404,12 +404,6 @@ func (x *XDPoS_v2) CalcDifficulty(chain consensus.ChainReader, time uint64, pare
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 {
x.lock.RLock()
defer x.lock.RUnlock()
@ -523,7 +517,7 @@ func (x *XDPoS_v2) VerifySyncInfoMessage(chain consensus.ChainReader, syncInfo *
return false, nil
}
err := x.verifyQC(chain, syncInfo.HighestQuorumCert)
err := x.verifyQC(chain, syncInfo.HighestQuorumCert, nil)
if err != nil {
log.Warn("SyncInfo message verification failed due to QC", "error", err)
return false, err
@ -653,7 +647,7 @@ func (x *XDPoS_v2) ProposedBlockHandler(chain consensus.ChainReader, blockHeader
return err
}
err = x.verifyQC(chain, quorumCert)
err = x.verifyQC(chain, quorumCert, nil)
if err != nil {
log.Error("[ProposedBlockHandler] Fail to verify QC", "Extra round", round, "QC proposed BlockInfo Hash", quorumCert.ProposedBlockInfo.Hash)
return err
@ -732,7 +726,7 @@ func (x *XDPoS_v2) VerifyBlockInfo(blockChainReader consensus.ChainReader, block
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
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
5. Verify blockInfo
*/
epochInfo, err := x.getEpochSwitchInfo(blockChainReader, nil, quorumCert.ProposedBlockInfo.Hash)
epochInfo, err := x.getEpochSwitchInfo(blockChainReader, parentHeader, 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")
@ -764,12 +758,6 @@ func (x *XDPoS_v2) verifyQC(blockChainReader consensus.ChainReader, quorumCert *
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
wg.Add(len(signatures))
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)
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)
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
}
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
_, _, masterNodes, err = x.getExtraFields(parent)
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
}
} else {
masterNodes, _, err = x.calcMasternodes(chain, big.NewInt(0).Add(parent.Number, big.NewInt(1)), parent.Hash())
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
}
}
@ -40,26 +40,26 @@ func (x *XDPoS_v2) checkYourturnWithinFinalisedMasternodes(chain consensus.Chain
}
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")
}
curIndex := utils.Position(masterNodes, signer)
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
}
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))
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
}
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
}

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
quorumCert, round, _, err := x.getExtraFields(header)
if err != nil {
@ -48,7 +64,7 @@ func (x *XDPoS_v2) verifyHeader(chain consensus.ChainReader, header *types.Heade
return utils.ErrRoundInvalid
}
err = x.verifyQC(chain, quorumCert)
err = x.verifyQC(chain, quorumCert, parent)
if err != nil {
log.Warn("[verifyHeader] fail to verify QC", "QCNumber", quorumCert.ProposedBlockInfo.Number, "QCsigLength", len(quorumCert.Signatures))
return err
@ -105,22 +121,6 @@ func (x *XDPoS_v2) verifyHeader(chain consensus.ChainReader, header *types.Heade
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)
if err != nil {
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)
}
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")
}
}