From 125f8a8957e7e088f2169e756ea0024c7cf9ac18 Mon Sep 17 00:00:00 2001 From: Jianrong Date: Sat, 19 Feb 2022 16:17:27 +1100 Subject: [PATCH 1/5] Split initialise v2 into two scenarios --- consensus/XDPoS/XDPoS.go | 23 ++++++++++++++------- consensus/XDPoS/engines/engine_v2/engine.go | 10 ++++++--- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/consensus/XDPoS/XDPoS.go b/consensus/XDPoS/XDPoS.go index df2f283a9e..3dfb9efc27 100644 --- a/consensus/XDPoS/XDPoS.go +++ b/consensus/XDPoS/XDPoS.go @@ -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)) { @@ -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 } diff --git a/consensus/XDPoS/engines/engine_v2/engine.go b/consensus/XDPoS/engines/engine_v2/engine.go index edbbc1cfae..1ed03b3633 100644 --- a/consensus/XDPoS/engines/engine_v2/engine.go +++ b/consensus/XDPoS/engines/engine_v2/engine.go @@ -119,7 +119,7 @@ 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 + if x.highestQuorumCert.ProposedBlockInfo.Round != 0 { // already initialized log.Warn("[Initial] Already initialized") return nil } @@ -128,7 +128,7 @@ func (x *XDPoS_v2) Initial(chain consensus.ChainReader, header *types.Header, ma 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(), @@ -148,7 +148,11 @@ 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) From 5733474c141b70de9cc3e6bbc6956cc628d2d26e Mon Sep 17 00:00:00 2001 From: Jianrong Date: Sat, 19 Feb 2022 16:50:00 +1100 Subject: [PATCH 2/5] add v1 snapshot fix --- consensus/XDPoS/engines/engine_v1/engine.go | 7 ++++++- consensus/XDPoS/engines/engine_v2/engine.go | 12 +++++++++--- consensus/XDPoS/engines/engine_v2/utils.go | 9 +++++++++ params/config.go | 2 +- params/version.go | 8 ++++---- 5 files changed, 29 insertions(+), 9 deletions(-) diff --git a/consensus/XDPoS/engines/engine_v1/engine.go b/consensus/XDPoS/engines/engine_v1/engine.go index 731a863e8a..493054c8d2 100644 --- a/consensus/XDPoS/engines/engine_v1/engine.go +++ b/consensus/XDPoS/engines/engine_v1/engine.go @@ -482,7 +482,12 @@ func (x *XDPoS_v1) snapshot(chain consensus.ChainReader, number uint64, hash com if s, err := loadSnapshot(x.config, x.signatures, x.db, hash); err == nil { log.Trace("Loaded voting snapshot form disk", "number", number, "hash", hash) snap = s - break + if len(snap.Signers) > 0 { + break + } else { + log.Warn("skip this snapshot, len of snap signer is 0") + snap = nil + } } } // If we're at block zero, make a snapshot diff --git a/consensus/XDPoS/engines/engine_v2/engine.go b/consensus/XDPoS/engines/engine_v2/engine.go index 1ed03b3633..179e4c0023 100644 --- a/consensus/XDPoS/engines/engine_v2/engine.go +++ b/consensus/XDPoS/engines/engine_v2/engine.go @@ -119,7 +119,7 @@ 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 + if !isEmptyHash(x.highestQuorumCert.ProposedBlockInfo.Hash) { // already initialized log.Warn("[Initial] Already initialized") return nil } @@ -156,12 +156,14 @@ func (x *XDPoS_v2) Initial(chain consensus.ChainReader, header *types.Header, ma // 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 } @@ -434,7 +436,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 } diff --git a/consensus/XDPoS/engines/engine_v2/utils.go b/consensus/XDPoS/engines/engine_v2/utils.go index be99cad26a..336b9f9494 100644 --- a/consensus/XDPoS/engines/engine_v2/utils.go +++ b/consensus/XDPoS/engines/engine_v2/utils.go @@ -68,3 +68,12 @@ func decodeMasternodesFromHeaderExtra(checkpointHeader *types.Header) []common.A } return masternodes } + +func isEmptyHash(hash common.Hash) bool { + for _, b := range hash { + if b != 0 { + return false + } + } + return true +} diff --git a/params/config.go b/params/config.go index 73f04cc114..81e56e59b5 100644 --- a/params/config.go +++ b/params/config.go @@ -47,7 +47,7 @@ var ( SwitchBlock: big.NewInt(900), } DevnetXDPoSV2Config = &V2{ - SwitchBlock: big.NewInt(9999999), // Temporary set it to very high + SwitchBlock: big.NewInt(7218000), TimeoutWorkerDuration: 50, CertThreshold: 6, WaitPeriod: 2, diff --git a/params/version.go b/params/version.go index 70070f932a..435b48e311 100644 --- a/params/version.go +++ b/params/version.go @@ -21,10 +21,10 @@ import ( ) const ( - VersionMajor = 1 // Major version component of the current release - VersionMinor = 4 // Minor version component of the current release - VersionPatch = 4 // Patch version component of the current release - VersionMeta = "stable" // Version metadata to append to the version string + 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 + VersionMeta = "unstable" // Version metadata to append to the version string ) // Version holds the textual version string. From cddeaf2db12b1fd9e31e20024ac04714de91cc1f Mon Sep 17 00:00:00 2001 From: Jianrong Date: Sat, 19 Feb 2022 19:36:32 +1100 Subject: [PATCH 3/5] add v2 verify header --- consensus/XDPoS/XDPoS.go | 2 +- consensus/XDPoS/engines/engine_v1/engine.go | 11 +- consensus/XDPoS/engines/engine_v2/engine.go | 137 ++++++++++++++++++-- consensus/XDPoS/utils/errors.go | 6 + consensus/tests/verify_block_test.go | 33 +++++ params/config.go | 6 +- 6 files changed, 170 insertions(+), 25 deletions(-) create mode 100644 consensus/tests/verify_block_test.go diff --git a/consensus/XDPoS/XDPoS.go b/consensus/XDPoS/XDPoS.go index 3dfb9efc27..234c8d351c 100644 --- a/consensus/XDPoS/XDPoS.go +++ b/consensus/XDPoS/XDPoS.go @@ -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) } diff --git a/consensus/XDPoS/engines/engine_v1/engine.go b/consensus/XDPoS/engines/engine_v1/engine.go index 493054c8d2..c6e9c0053c 100644 --- a/consensus/XDPoS/engines/engine_v1/engine.go +++ b/consensus/XDPoS/engines/engine_v1/engine.go @@ -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 { @@ -482,12 +482,7 @@ func (x *XDPoS_v1) snapshot(chain consensus.ChainReader, number uint64, hash com if s, err := loadSnapshot(x.config, x.signatures, x.db, hash); err == nil { log.Trace("Loaded voting snapshot form disk", "number", number, "hash", hash) snap = s - if len(snap.Signers) > 0 { - break - } else { - log.Warn("skip this snapshot, len of snap signer is 0") - snap = nil - } + break } } // If we're at block zero, make a snapshot @@ -934,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) diff --git a/consensus/XDPoS/engines/engine_v2/engine.go b/consensus/XDPoS/engines/engine_v2/engine.go index 179e4c0023..826b34041e 100644 --- a/consensus/XDPoS/engines/engine_v2/engine.go +++ b/consensus/XDPoS/engines/engine_v2/engine.go @@ -1,6 +1,7 @@ package engine_v2 import ( + "bytes" "encoding/json" "errors" "fmt" @@ -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" @@ -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 @@ -66,6 +69,7 @@ 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{ @@ -73,11 +77,12 @@ func New(config *params.XDPoSConfig, db ethdb.Database, waitPeriodCh chan int) * 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, @@ -120,7 +125,7 @@ func (x *XDPoS_v2) Initial(chain consensus.ChainReader, header *types.Header, ma log.Info("[Initial] initial v2 related parameters") if !isEmptyHash(x.highestQuorumCert.ProposedBlockInfo.Hash) { // already initialized - log.Warn("[Initial] Already initialized") + log.Error("[Initial] Already initialized", "blockNum", header.Number, "Hash", header.Hash()) return nil } @@ -493,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{} @@ -506,22 +515,122 @@ 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 + } + number := header.Number.Uint64() + if fullVerify { + if len(header.Validator) == 0 { + return consensus.ErrNoValidatorSignature + } + // Don't waste time checking blocks from the future + if header.Time.Cmp(big.NewInt(time.Now().Unix())) > 0 { + 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 isEmptyHash(quorumCert.ProposedBlockInfo.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.Cmp(new(big.Int).Add(x.config.V2.SwitchBlock, big.NewInt(1))) == 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 + 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) diff --git a/consensus/XDPoS/utils/errors.go b/consensus/XDPoS/utils/errors.go index b638328511..d4632727f3 100644 --- a/consensus/XDPoS/utils/errors.go +++ b/consensus/XDPoS/utils/errors.go @@ -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 { diff --git a/consensus/tests/verify_block_test.go b/consensus/tests/verify_block_test.go new file mode 100644 index 0000000000..d7ab27220d --- /dev/null +++ b/consensus/tests/verify_block_test.go @@ -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 +} diff --git a/params/config.go b/params/config.go index 81e56e59b5..7ad2cf8ec4 100644 --- a/params/config.go +++ b/params/config.go @@ -45,6 +45,7 @@ var ( WaitPeriod: 1, MinePeriod: 2, SwitchBlock: big.NewInt(900), + SkipV2Validation: true, } DevnetXDPoSV2Config = &V2{ SwitchBlock: big.NewInt(7218000), @@ -139,7 +140,7 @@ var ( AllCliqueProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, &CliqueConfig{Period: 0, Epoch: 30000}, nil} // XDPoS config with v2 engine after block 901 - TestXDPoSMockChainConfig = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, new(EthashConfig), nil, &XDPoSConfig{Epoch: 900, Gap: 450, SkipValidation: true, V2: TestXDPoSV2Config, FoudationWalletAddr: common.HexToAddress("0x0000000000000000000000000000000000000068"), Reward: 250}} + TestXDPoSMockChainConfig = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, new(EthashConfig), nil, &XDPoSConfig{Epoch: 900, Gap: 450, SkipV1Validation: true, V2: TestXDPoSV2Config, FoudationWalletAddr: common.HexToAddress("0x0000000000000000000000000000000000000068"), Reward: 250}} TestChainConfig = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, new(EthashConfig), nil, nil} TestRules = TestChainConfig.Rules(new(big.Int)) @@ -202,7 +203,7 @@ type XDPoSConfig struct { Gap uint64 `json:"gap"` // Gap time preparing for the next epoch FoudationWalletAddr common.Address `json:"foudationWalletAddr"` // Foundation Address Wallet WaitPeriod int `json:"waitPeriod"` // Miner wait period - SkipValidation bool //Skip Block Validation for testing purpose + SkipV1Validation bool //Skip Block Validation for testing purpose, V1 consensus only V2 *V2 `json:"v2"` } @@ -212,6 +213,7 @@ type V2 struct { SwitchBlock *big.Int `json:"switchBlock"` // v1 to v2 switch block number TimeoutWorkerDuration int64 `json:"timeoutWorkerDuration"` // Duration in ms CertThreshold int `json:"certificateThreshold"` // Necessary number of messages from master nodes to form a certificate + SkipV2Validation bool //Skip Block Validation for testing purpose, V2 consensus only } // String implements the stringer interface, returning the consensus engine details. From 221326aafc2688e562e9ae4fb4d1fca0f810aa0d Mon Sep 17 00:00:00 2001 From: Jianrong Date: Sun, 20 Feb 2022 22:10:23 +1100 Subject: [PATCH 4/5] remove isemptyhash --- consensus/XDPoS/engines/engine_v2/engine.go | 4 ++-- consensus/XDPoS/engines/engine_v2/utils.go | 9 --------- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/consensus/XDPoS/engines/engine_v2/engine.go b/consensus/XDPoS/engines/engine_v2/engine.go index 826b34041e..40c0add701 100644 --- a/consensus/XDPoS/engines/engine_v2/engine.go +++ b/consensus/XDPoS/engines/engine_v2/engine.go @@ -124,7 +124,7 @@ 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 !isEmptyHash(x.highestQuorumCert.ProposedBlockInfo.Hash) { // already initialized + if x.highestQuorumCert.ProposedBlockInfo.Hash != (common.Hash{}) { // already initialized log.Error("[Initial] Already initialized", "blockNum", header.Number, "Hash", header.Hash()) return nil } @@ -573,7 +573,7 @@ func (x *XDPoS_v2) verifyHeader(chain consensus.ChainReader, header *types.Heade return utils.ErrInvalidQC } - if isEmptyHash(quorumCert.ProposedBlockInfo.Hash) { + if quorumCert.ProposedBlockInfo.Hash == (common.Hash{}) { return utils.ErrEmptyBlockInfoHash } diff --git a/consensus/XDPoS/engines/engine_v2/utils.go b/consensus/XDPoS/engines/engine_v2/utils.go index 336b9f9494..be99cad26a 100644 --- a/consensus/XDPoS/engines/engine_v2/utils.go +++ b/consensus/XDPoS/engines/engine_v2/utils.go @@ -68,12 +68,3 @@ func decodeMasternodesFromHeaderExtra(checkpointHeader *types.Header) []common.A } return masternodes } - -func isEmptyHash(hash common.Hash) bool { - for _, b := range hash { - if b != 0 { - return false - } - } - return true -} From 491dc911f3cdfacb4dba92d76d1d52dd4ee4b62f Mon Sep 17 00:00:00 2001 From: Liam Lai Date: Mon, 21 Feb 2022 01:16:33 +0300 Subject: [PATCH 5/5] refactor big int compare and fix 1 bug on headers --- consensus/XDPoS/XDPoS.go | 4 ++-- consensus/XDPoS/engines/engine_v2/engine.go | 8 +++++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/consensus/XDPoS/XDPoS.go b/consensus/XDPoS/XDPoS.go index 234c8d351c..2b43f02134 100644 --- a/consensus/XDPoS/XDPoS.go +++ b/consensus/XDPoS/XDPoS.go @@ -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 diff --git a/consensus/XDPoS/engines/engine_v2/engine.go b/consensus/XDPoS/engines/engine_v2/engine.go index 40c0add701..0e5bfb2b69 100644 --- a/consensus/XDPoS/engines/engine_v2/engine.go +++ b/consensus/XDPoS/engines/engine_v2/engine.go @@ -551,13 +551,13 @@ func (x *XDPoS_v2) verifyHeader(chain consensus.ChainReader, header *types.Heade if header.Number == nil { return utils.ErrUnknownBlock } - number := header.Number.Uint64() + if fullVerify { if len(header.Validator) == 0 { return consensus.ErrNoValidatorSignature } // Don't waste time checking blocks from the future - if header.Time.Cmp(big.NewInt(time.Now().Unix())) > 0 { + if header.Time.Int64() > time.Now().Unix() { return consensus.ErrFutureBlock } } @@ -593,7 +593,7 @@ func (x *XDPoS_v2) verifyHeader(chain consensus.ChainReader, header *types.Heade // 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.Cmp(new(big.Int).Add(x.config.V2.SwitchBlock, big.NewInt(1))) == 1 && header.Coinbase != (common.Address{}) { + if header.Number.Uint64() > x.config.V2.SwitchBlock.Uint64()+1 && header.Coinbase != (common.Address{}) { return utils.ErrInvalidCheckpointBeneficiary } if !bytes.Equal(header.Nonce[:], utils.NonceDropVote) { @@ -614,6 +614,8 @@ 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 {