Skip to content

Commit

Permalink
Merge pull request ethereum#58 from hash-laboratories-au/XIN-125-happ…
Browse files Browse the repository at this point in the history
…y-path-fix

Xin 125 happy path fix
  • Loading branch information
liam-lai authored Feb 20, 2022
2 parents 18a64d3 + 491dc91 commit 0ab7bfb
Show file tree
Hide file tree
Showing 7 changed files with 208 additions and 39 deletions.
29 changes: 18 additions & 11 deletions consensus/XDPoS/XDPoS.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ func (x *XDPoS) Author(header *types.Header) (common.Address, error) {
func (x *XDPoS) VerifyHeader(chain consensus.ChainReader, header *types.Header, fullVerify bool) error {
switch x.config.BlockConsensusVersion(header.Number) {
case params.ConsensusEngineVersion2:
return nil
return x.EngineV2.VerifyHeader(chain, header, fullVerify)
default: // Default "v1"
return x.EngineV1.VerifyHeader(chain, header, fullVerify)
}
Expand All @@ -180,10 +180,10 @@ func (x *XDPoS) VerifyHeaders(chain consensus.ChainReader, headers []*types.Head
}

if v1headers != nil {
x.EngineV1.VerifyHeaders(chain, headers, fullVerifies, abort, results)
x.EngineV1.VerifyHeaders(chain, v1headers, fullVerifies, abort, results)
}
if v2headers != nil {
x.EngineV2.VerifyHeaders(chain, headers, fullVerifies, abort, results)
x.EngineV2.VerifyHeaders(chain, v2headers, fullVerifies, abort, results)
}

return abort, results
Expand Down Expand Up @@ -314,11 +314,15 @@ func (x *XDPoS) GetMasternodesByNumber(chain consensus.ChainReader, blockNumber
}

func (x *XDPoS) YourTurn(chain consensus.ChainReader, parent *types.Header, signer common.Address) (bool, error) {
if x.config.V2.SwitchBlock != nil && parent.Number.Cmp(x.config.V2.SwitchBlock) == 0 {
err := x.initialV2(chain, parent)
if err != nil {
log.Error("[YourTurn] Error when initialise v2", "Error", err, "ParentBlock", parent)
return false, err
if x.config.V2.SwitchBlock != nil && parent.Number.Cmp(x.config.V2.SwitchBlock) != -1 {
if parent.Number.Cmp(x.config.V2.SwitchBlock) == 0 {
err := x.initialV2FromLastV1(chain, parent)
if err != nil {
log.Error("[YourTurn] Error while initilising first v2 block from the last v1 block", "ParentBlockHash", parent.Hash(), "Error", err)
return false, err
}
} else if parent.Number.Cmp(x.config.V2.SwitchBlock) == 1 { // TODO: XIN-147
log.Info("[YourTurn] Initilising v2 after sync or restarted", "currentBlockNum", chain.CurrentHeader().Number, "currentBlockHash", chain.CurrentHeader().Hash())
}
}
switch x.config.BlockConsensusVersion(big.NewInt(parent.Number.Int64() + 1)) {
Expand Down Expand Up @@ -481,12 +485,15 @@ func (x *XDPoS) GetCachedSigningTxs(hash common.Hash) (interface{}, bool) {
return x.signingTxsCache.Get(hash)
}

//V2
func (x *XDPoS) initialV2(chain consensus.ChainReader, header *types.Header) error {
// V2 specific helper function to initilise consensus engine variables
func (x *XDPoS) initialV2FromLastV1(chain consensus.ChainReader, header *types.Header) error {
checkpointBlockNumber := header.Number.Uint64() - header.Number.Uint64()%x.config.Epoch
checkpointHeader := chain.GetHeaderByNumber(checkpointBlockNumber)
masternodes := x.EngineV1.GetMasternodesFromCheckpointHeader(checkpointHeader)
x.EngineV2.Initial(chain, header, masternodes)
err := x.EngineV2.Initial(chain, header, masternodes)
if err != nil {
return err
}
return nil
}

Expand Down
4 changes: 2 additions & 2 deletions consensus/XDPoS/engines/engine_v1/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ func (x *XDPoS_v1) verifyHeaderWithCache(chain consensus.ChainReader, header *ty
// a batch of new headers.
func (x *XDPoS_v1) verifyHeader(chain consensus.ChainReader, header *types.Header, parents []*types.Header, fullVerify bool) error {
// If we're running a engine faking, accept any block as valid
if x.config.SkipValidation {
if x.config.SkipV1Validation {
return nil
}
if common.IsTestnet {
Expand Down Expand Up @@ -929,7 +929,7 @@ func (x *XDPoS_v1) CalcDifficulty(chain consensus.ChainReader, time uint64, pare

func (x *XDPoS_v1) calcDifficulty(chain consensus.ChainReader, parent *types.Header, signer common.Address) *big.Int {
// If we're running a engine faking, skip calculation
if x.config.SkipValidation {
if x.config.SkipV1Validation {
return big.NewInt(1)
}
len, preIndex, curIndex, _, err := x.yourTurn(chain, parent, signer)
Expand Down
159 changes: 140 additions & 19 deletions consensus/XDPoS/engines/engine_v2/engine.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package engine_v2

import (
"bytes"
"encoding/json"
"errors"
"fmt"
Expand All @@ -16,6 +17,7 @@ import (
"github.com/XinFinOrg/XDPoSChain/consensus"
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils"
"github.com/XinFinOrg/XDPoSChain/consensus/clique"
"github.com/XinFinOrg/XDPoSChain/consensus/misc"
"github.com/XinFinOrg/XDPoSChain/core/state"
"github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/crypto"
Expand All @@ -29,9 +31,10 @@ type XDPoS_v2 struct {
config *params.XDPoSConfig // Consensus engine configuration parameters
db ethdb.Database // Database to store and retrieve snapshot checkpoints

snapshots *lru.ARCCache // Snapshots for gap block
signatures *lru.ARCCache // Signatures of recent blocks to speed up mining
epochSwitches *lru.ARCCache // infos of epoch: master nodes, epoch switch block info, parent of that info
snapshots *lru.ARCCache // Snapshots for gap block
signatures *lru.ARCCache // Signatures of recent blocks to speed up mining
epochSwitches *lru.ARCCache // infos of epoch: master nodes, epoch switch block info, parent of that info
verifiedHeaders *lru.ARCCache

signer common.Address // Ethereum address of the signing key
signFn clique.SignerFn // Signer function to authorize hashes with
Expand Down Expand Up @@ -66,18 +69,20 @@ func New(config *params.XDPoSConfig, db ethdb.Database, waitPeriodCh chan int) *
snapshots, _ := lru.NewARC(utils.InmemorySnapshots)
signatures, _ := lru.NewARC(utils.InmemorySnapshots)
epochSwitches, _ := lru.NewARC(int(utils.InmemoryEpochs))
verifiedHeaders, _ := lru.NewARC(utils.InmemorySnapshots)

votePool := utils.NewPool(config.V2.CertThreshold)
engine := &XDPoS_v2{
config: config,
db: db,
signatures: signatures,

snapshots: snapshots,
epochSwitches: epochSwitches,
timeoutWorker: timer,
BroadcastCh: make(chan interface{}),
waitPeriodCh: waitPeriodCh,
verifiedHeaders: verifiedHeaders,
snapshots: snapshots,
epochSwitches: epochSwitches,
timeoutWorker: timer,
BroadcastCh: make(chan interface{}),
waitPeriodCh: waitPeriodCh,

timeoutPool: timeoutPool,
votePool: votePool,
Expand Down Expand Up @@ -119,16 +124,16 @@ func (x *XDPoS_v2) SignHash(header *types.Header) (hash common.Hash) {
func (x *XDPoS_v2) Initial(chain consensus.ChainReader, header *types.Header, masternodes []common.Address) error {
log.Info("[Initial] initial v2 related parameters")

if x.highestQuorumCert.ProposedBlockInfo.Round != 0 { //already initialized
log.Warn("[Initial] Already initialized")
if x.highestQuorumCert.ProposedBlockInfo.Hash != (common.Hash{}) { // already initialized
log.Error("[Initial] Already initialized", "blockNum", header.Number, "Hash", header.Hash())
return nil
}

x.lock.Lock()
defer x.lock.Unlock()
// Check header if it is the first consensus v2 block, if so, assign initial values to current round and highestQC

log.Info("[Initial] highest QC for consensus v2 first block", "Block Num", header.Number.String(), "BlockHash", header.Hash())
log.Info("[Initial] highest QC for consensus v2 first block", "BlockNum", header.Number.String(), "BlockHash", header.Hash())
// Generate new parent blockInfo and put it into QC
blockInfo := &utils.BlockInfo{
Hash: header.Hash(),
Expand All @@ -148,16 +153,22 @@ func (x *XDPoS_v2) Initial(chain consensus.ChainReader, header *types.Header, ma

snap := newSnapshot(lastGapNum, lastGapHeader.Hash(), x.currentRound, x.highestQuorumCert, masternodes)
x.snapshots.Add(snap.Hash, snap)
storeSnapshot(snap, x.db)
err := storeSnapshot(snap, x.db)
if err != nil {
log.Error("[Initial] Error while storo snapshot", "error", err)
return err
}

// Initial timeout
log.Info("[Initial] miner wait period", "period", x.config.WaitPeriod)

// avoid deadlock
go func() {
x.waitPeriodCh <- x.config.V2.WaitPeriod
}()

// Kick-off the countdown timer
x.timeoutWorker.Reset()

log.Info("[Initial] finish initialisation")
return nil
}
Expand Down Expand Up @@ -430,7 +441,11 @@ func (x *XDPoS_v2) IsAuthorisedAddress(chain consensus.ChainReader, header *type
}
}

log.Warn("Not authorised address", "Address", address, "MN", masterNodes, "Hash", header.Hash())
log.Warn("Not authorised address", "Address", address.Hex(), "Hash", header.Hash())
for index, mn := range masterNodes {
log.Warn("Master node list item", "mn", mn.Hex(), "index", index)
}

return false
}

Expand Down Expand Up @@ -483,7 +498,11 @@ func (x *XDPoS_v2) UpdateMasternodes(chain consensus.ChainReader, header *types.
snap := newSnapshot(number, header.Hash(), x.currentRound, x.highestQuorumCert, masterNodes)
x.lock.RUnlock()

storeSnapshot(snap, x.db)
err := storeSnapshot(snap, x.db)
if err != nil {
log.Error("[UpdateMasternodes] Error while store snashot", "hash", header.Hash(), "currentRound", x.currentRound, "error", err)
return err
}
x.snapshots.Add(snap.Hash, snap)

nm := []string{}
Expand All @@ -496,22 +515,124 @@ func (x *XDPoS_v2) UpdateMasternodes(chain consensus.ChainReader, header *types.
}

func (x *XDPoS_v2) VerifyHeader(chain consensus.ChainReader, header *types.Header, fullVerify bool) error {
return nil
err := x.verifyHeader(chain, header, nil, fullVerify)
if err != nil {
log.Warn("[VerifyHeader] Fail to verify header", "fullVerify", fullVerify, "blockNum", header.Number, "blockHash", header.Hash(), "error", err)
}
return err
}

// TODO: Yet to be implemented XIN-135
// Verify a list of headers
func (x *XDPoS_v2) VerifyHeaders(chain consensus.ChainReader, headers []*types.Header, fullVerifies []bool, abort <-chan struct{}, results chan<- error) {
go func() {
for range headers {
for i, header := range headers {
err := x.verifyHeader(chain, header, headers[:i], fullVerifies[i])
log.Warn("[VerifyHeaders] Fail to verify header", "fullVerify", fullVerifies[i], "blockNum", header.Number, "blockHash", header.Hash(), "error", err)
select {
case <-abort:
return
case results <- nil:
case results <- err:
}
}
}()
}

// Verify individual header
func (x *XDPoS_v2) verifyHeader(chain consensus.ChainReader, header *types.Header, parents []*types.Header, fullVerify bool) error {
// If we're running a engine faking, accept any block as valid
if x.config.V2.SkipV2Validation {
return nil
}
_, check := x.verifiedHeaders.Get(header.Hash())
if check {
return nil
}

if header.Number == nil {
return utils.ErrUnknownBlock
}

if fullVerify {
if len(header.Validator) == 0 {
return consensus.ErrNoValidatorSignature
}
// Don't waste time checking blocks from the future
if header.Time.Int64() > time.Now().Unix() {
return consensus.ErrFutureBlock
}
}

// Verify this is truely a v2 block first
var decodedExtraField utils.ExtraFields_v2
err := utils.DecodeBytesExtraFields(header.Extra, &decodedExtraField)
if err != nil {
return utils.ErrInvalidV2Extra
}
quorumCert := decodedExtraField.QuorumCert
if quorumCert == nil || quorumCert.Signatures == nil || len(quorumCert.Signatures) == 0 {
return utils.ErrInvalidQC
}

if quorumCert.ProposedBlockInfo.Hash == (common.Hash{}) {
return utils.ErrEmptyBlockInfoHash
}

// Nonces must be 0x00..0 or 0xff..f, zeroes enforced on checkpoints
if !bytes.Equal(header.Nonce[:], utils.NonceAuthVote) && !bytes.Equal(header.Nonce[:], utils.NonceDropVote) {
return utils.ErrInvalidVote
}
// Ensure that the mix digest is zero as we don't have fork protection currently
if header.MixDigest != (common.Hash{}) {
return utils.ErrInvalidMixDigest
}
// Ensure that the block doesn't contain any uncles which are meaningless in XDPoS_v1
if header.UncleHash != utils.UncleHash {
return utils.ErrInvalidUncleHash
}

// Verify v2 block that is on the epoch switch
if header.Validators != nil {
// Skip if it's the first v2 block as it wil inherit from last v1 epoch block
if header.Number.Uint64() > x.config.V2.SwitchBlock.Uint64()+1 && header.Coinbase != (common.Address{}) {
return utils.ErrInvalidCheckpointBeneficiary
}
if !bytes.Equal(header.Nonce[:], utils.NonceDropVote) {
return utils.ErrInvalidCheckpointVote
}
if len(header.Validators) == 0 {
return utils.ErrEmptyEpochSwitchValidators
}
if len(header.Validators)%common.AddressLength != 0 {
return utils.ErrInvalidCheckpointSigners
}
}

// If all checks passed, validate any special fields for hard forks
if err := misc.VerifyForkHashes(chain.Config(), header, false); err != nil {
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.Time.Uint64()+uint64(x.config.V2.MinePeriod) > header.Time.Uint64() {
return utils.ErrInvalidTimestamp
}
// TODO: verifySeal XIN-135

x.verifiedHeaders.Add(header.Hash(), true)
return nil
}

// Utils for test to get current Pool size
func (x *XDPoS_v2) GetVotePoolSize(vote *utils.Vote) int {
return x.votePool.Size(vote)
Expand Down
6 changes: 6 additions & 0 deletions consensus/XDPoS/utils/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,12 @@ var (
ErrWaitTransactions = errors.New("waiting for transactions")

ErrInvalidCheckpointValidators = errors.New("invalid validators list on checkpoint block")

ErrEmptyEpochSwitchValidators = errors.New("empty validators list on epoch switch block")

ErrInvalidV2Extra = errors.New("Invalid v2 extra in the block")
ErrInvalidQC = errors.New("Invalid QC content")
ErrEmptyBlockInfoHash = errors.New("BlockInfo hash is empty")
)

type ErrIncomingMessageRoundNotEqualCurrentRound struct {
Expand Down
33 changes: 33 additions & 0 deletions consensus/tests/verify_block_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package tests

import (
"encoding/json"
"testing"

"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS"
"github.com/XinFinOrg/XDPoSChain/params"
"github.com/stretchr/testify/assert"
)

func TestShouldVerifyBlock(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, _, currentBlock, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 901, &config, 0)
adaptor := blockchain.Engine().(*XDPoS.XDPoS)

// Happy path
err = adaptor.VerifyHeader(blockchain, currentBlock.Header(), true)
assert.Nil(t, err)

// TODO: unhappy path XIN-135: https://hashlabs.atlassian.net/wiki/spaces/HASHLABS/pages/95944705/Verify+header
}
Loading

0 comments on commit 0ab7bfb

Please sign in to comment.