Skip to content

Commit

Permalink
Xin 166 (ethereum#75)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
wjrjerome authored Mar 27, 2022
1 parent b790b07 commit b98005a
Show file tree
Hide file tree
Showing 5 changed files with 82 additions and 46 deletions.
14 changes: 14 additions & 0 deletions consensus/XDPoS/engines/engine_v2/difficulty.go
Original file line number Diff line number Diff line change
@@ -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)
}
28 changes: 8 additions & 20 deletions consensus/XDPoS/engines/engine_v2/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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()
Expand All @@ -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)
}
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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")
Expand All @@ -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
Expand Down
18 changes: 9 additions & 9 deletions consensus/XDPoS/engines/engine_v2/mining.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
}
}
Expand All @@ -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
}
34 changes: 17 additions & 17 deletions consensus/XDPoS/engines/engine_v2/verifyHeader.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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
Expand Down Expand Up @@ -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())
Expand Down
34 changes: 34 additions & 0 deletions consensus/tests/engine_v2_tests/verify_header_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
}
}

0 comments on commit b98005a

Please sign in to comment.