mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-17 18:30:45 +00:00
v2 miner function implementation and happy path (#22)
* New struct in consensus/XDPoS/utils/types.go, util functions, and test. (#14) * define vote, timeout, sync info, qc, tc, extra fields in types.go, add test in types_test.go * add json tag in types.go, refine encoder decoder of extra fields * refactor types.go utils.go * re-write types, comments * add Hash SigHash for types, and tests * define Round type * remove unnecessary logs * add v2 engine functions placeholder * typo fix on the consensus v2 function placeholders * add countdown timer * make initilised private to countdown * add v2 specific config struct * rename some config variables * Implement BFT Message receiver (#13) * fix or skip tests due to PR-136 changes * add bft receiver functions * add bft receiver functions * rename tc to TimeoutCert * implement more functions * New struct in consensus/XDPoS/utils/types.go, util functions, and test. (#14) * define vote, timeout, sync info, qc, tc, extra fields in types.go, add test in types_test.go * add json tag in types.go, refine encoder decoder of extra fields * refactor types.go utils.go * re-write types, comments * add Hash SigHash for types, and tests * define Round type * remove unnecessary logs * add temp functions * add v2 engine functions placeholder * typo fix on the consensus v2 function placeholders * add countdown timer * make initilised private to countdown * push verify function * add test on receiving vote * revert type change * add async on broadcast function * add quit initial * fix test Co-authored-by: Jianrong <wjrjerome@gmail.com> Co-authored-by: wgr523 <wgr523@gmail.com> * generate and verify timeout message * Consensus V2 variable, timeout pool (#19) * fill in XDPoS_v2 variables and processQC/TC * add timeout pool, refine engine variables * refactor type functions * solve a small pointer bug * create general pool and its test, refine engine * refine pool, add xdpos v2 config cert threshold * refine config * vote and timeout handlers * fix pool test * bft miner preparation * review comment improvement * update * relocate tests * add and remove comment * fix the syntax error * update network layer and add handler functions (#23) * update network layer and add handler functions * fix test syntax error * add ProcessQC implementation * add ProcessQC tests * add snapshot test * add wait qc process * remove testing files * add route snapshot * fix merge issue * add default v2 behaviour (#24) * add v2 ecrecover functions and refactor test * fix all the tests * put minimun lock variable * debugging prepare and seal v2 blocks * Trigger proposeBlockHandler after v2 block received and verified in fetcher * skip snapshot apply related tests * update test check * rename bfter to bft handler and ignore normal behviour * fix bugs during local 4 node run * fix test * fix sync info test * fix bugs during local 4 node run * rebase and fix bug * remove hook validators function" Co-authored-by: wgr523 <wgr523@gmail.com> Co-authored-by: Jianrong <wjrjerome@gmail.com>
This commit is contained in:
parent
6e13b4d6a2
commit
6c5fe34615
30 changed files with 1286 additions and 275 deletions
|
|
@ -175,7 +175,7 @@ func (x *XDPoS) VerifySeal(chain consensus.ChainReader, header *types.Header) er
|
||||||
func (x *XDPoS) Prepare(chain consensus.ChainReader, header *types.Header) error {
|
func (x *XDPoS) Prepare(chain consensus.ChainReader, header *types.Header) error {
|
||||||
switch x.config.BlockConsensusVersion(header.Number) {
|
switch x.config.BlockConsensusVersion(header.Number) {
|
||||||
case params.ConsensusEngineVersion2:
|
case params.ConsensusEngineVersion2:
|
||||||
return nil
|
return x.EngineV2.Prepare(chain, header)
|
||||||
default: // Default "v1"
|
default: // Default "v1"
|
||||||
return x.EngineV1.Prepare(chain, header)
|
return x.EngineV1.Prepare(chain, header)
|
||||||
}
|
}
|
||||||
|
|
@ -186,7 +186,7 @@ func (x *XDPoS) Prepare(chain consensus.ChainReader, header *types.Header) error
|
||||||
func (x *XDPoS) Finalize(chain consensus.ChainReader, header *types.Header, state *state.StateDB, parentState *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) {
|
func (x *XDPoS) Finalize(chain consensus.ChainReader, header *types.Header, state *state.StateDB, parentState *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) {
|
||||||
switch x.config.BlockConsensusVersion(header.Number) {
|
switch x.config.BlockConsensusVersion(header.Number) {
|
||||||
case params.ConsensusEngineVersion2:
|
case params.ConsensusEngineVersion2:
|
||||||
return nil, nil
|
return x.EngineV2.Finalize(chain, header, state, parentState, txs, uncles, receipts)
|
||||||
default: // Default "v1"
|
default: // Default "v1"
|
||||||
return x.EngineV1.Finalize(chain, header, state, parentState, txs, uncles, receipts)
|
return x.EngineV1.Finalize(chain, header, state, parentState, txs, uncles, receipts)
|
||||||
}
|
}
|
||||||
|
|
@ -197,7 +197,7 @@ func (x *XDPoS) Finalize(chain consensus.ChainReader, header *types.Header, stat
|
||||||
func (x *XDPoS) Seal(chain consensus.ChainReader, block *types.Block, stop <-chan struct{}) (*types.Block, error) {
|
func (x *XDPoS) Seal(chain consensus.ChainReader, block *types.Block, stop <-chan struct{}) (*types.Block, error) {
|
||||||
switch x.config.BlockConsensusVersion(block.Number()) {
|
switch x.config.BlockConsensusVersion(block.Number()) {
|
||||||
case params.ConsensusEngineVersion2:
|
case params.ConsensusEngineVersion2:
|
||||||
return nil, nil
|
return x.EngineV2.Seal(chain, block, stop)
|
||||||
default: // Default "v1"
|
default: // Default "v1"
|
||||||
return x.EngineV1.Seal(chain, block, stop)
|
return x.EngineV1.Seal(chain, block, stop)
|
||||||
}
|
}
|
||||||
|
|
@ -209,12 +209,21 @@ func (x *XDPoS) Seal(chain consensus.ChainReader, block *types.Block, stop <-cha
|
||||||
func (x *XDPoS) CalcDifficulty(chain consensus.ChainReader, time uint64, parent *types.Header) *big.Int {
|
func (x *XDPoS) CalcDifficulty(chain consensus.ChainReader, time uint64, parent *types.Header) *big.Int {
|
||||||
switch x.config.BlockConsensusVersion(parent.Number) {
|
switch x.config.BlockConsensusVersion(parent.Number) {
|
||||||
case params.ConsensusEngineVersion2:
|
case params.ConsensusEngineVersion2:
|
||||||
return nil
|
return x.EngineV2.CalcDifficulty(chain, time, parent)
|
||||||
default: // Default "v1"
|
default: // Default "v1"
|
||||||
return x.EngineV1.CalcDifficulty(chain, time, parent)
|
return x.EngineV1.CalcDifficulty(chain, time, parent)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *XDPoS) HandleProposedBlock(chain consensus.ChainReader, header *types.Header) error {
|
||||||
|
switch x.config.BlockConsensusVersion(header.Number) {
|
||||||
|
case params.ConsensusEngineVersion2:
|
||||||
|
return x.EngineV2.ProposedBlockHandler(chain, header)
|
||||||
|
default: // Default "v1"
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
XDC specific methods
|
XDC specific methods
|
||||||
*/
|
*/
|
||||||
|
|
@ -243,7 +252,7 @@ func (x *XDPoS) IsAuthorisedAddress(header *types.Header, chain consensus.ChainR
|
||||||
func (x *XDPoS) GetMasternodes(chain consensus.ChainReader, header *types.Header) []common.Address {
|
func (x *XDPoS) GetMasternodes(chain consensus.ChainReader, header *types.Header) []common.Address {
|
||||||
switch x.config.BlockConsensusVersion(header.Number) {
|
switch x.config.BlockConsensusVersion(header.Number) {
|
||||||
case params.ConsensusEngineVersion2:
|
case params.ConsensusEngineVersion2:
|
||||||
return []common.Address{}
|
return x.EngineV2.GetMasternodes(chain, header)
|
||||||
default: // Default "v1"
|
default: // Default "v1"
|
||||||
return x.EngineV1.GetMasternodes(chain, header)
|
return x.EngineV1.GetMasternodes(chain, header)
|
||||||
}
|
}
|
||||||
|
|
@ -251,6 +260,8 @@ func (x *XDPoS) GetMasternodes(chain consensus.ChainReader, header *types.Header
|
||||||
|
|
||||||
func (x *XDPoS) YourTurn(chain consensus.ChainReader, parent *types.Header, signer common.Address) (int, int, int, bool, error) {
|
func (x *XDPoS) YourTurn(chain consensus.ChainReader, parent *types.Header, signer common.Address) (int, int, int, bool, error) {
|
||||||
switch x.config.BlockConsensusVersion(parent.Number) {
|
switch x.config.BlockConsensusVersion(parent.Number) {
|
||||||
|
case params.ConsensusEngineVersion2:
|
||||||
|
return x.EngineV2.YourTurn(chain, parent, signer)
|
||||||
default: // Default "v1"
|
default: // Default "v1"
|
||||||
return x.EngineV1.YourTurn(chain, parent, signer)
|
return x.EngineV1.YourTurn(chain, parent, signer)
|
||||||
}
|
}
|
||||||
|
|
@ -258,7 +269,7 @@ func (x *XDPoS) YourTurn(chain consensus.ChainReader, parent *types.Header, sign
|
||||||
|
|
||||||
func (x *XDPoS) GetValidator(creator common.Address, chain consensus.ChainReader, header *types.Header) (common.Address, error) {
|
func (x *XDPoS) GetValidator(creator common.Address, chain consensus.ChainReader, header *types.Header) (common.Address, error) {
|
||||||
switch x.config.BlockConsensusVersion(header.Number) {
|
switch x.config.BlockConsensusVersion(header.Number) {
|
||||||
default: // Default "v1"
|
default: // Default "v1", v2 does not need this function
|
||||||
return x.EngineV1.GetValidator(creator, chain, header)
|
return x.EngineV1.GetValidator(creator, chain, header)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -307,6 +318,13 @@ func (x *XDPoS) GetDb() ethdb.Database {
|
||||||
|
|
||||||
func (x *XDPoS) GetSnapshot(chain consensus.ChainReader, header *types.Header) (*utils.PublicApiSnapshot, error) {
|
func (x *XDPoS) GetSnapshot(chain consensus.ChainReader, header *types.Header) (*utils.PublicApiSnapshot, error) {
|
||||||
switch x.config.BlockConsensusVersion(header.Number) {
|
switch x.config.BlockConsensusVersion(header.Number) {
|
||||||
|
case params.ConsensusEngineVersion2:
|
||||||
|
sp, err := x.EngineV2.GetSnapshot(chain, header)
|
||||||
|
return &utils.PublicApiSnapshot{
|
||||||
|
Number: sp.Number,
|
||||||
|
Hash: sp.Hash,
|
||||||
|
Signers: sp.MasterNodes,
|
||||||
|
}, err
|
||||||
default: // Default "v1"
|
default: // Default "v1"
|
||||||
sp, err := x.EngineV1.GetSnapshot(chain, header)
|
sp, err := x.EngineV1.GetSnapshot(chain, header)
|
||||||
// Convert to a standard PublicApiSnapshot type, otherwise it's a breaking change to API
|
// Convert to a standard PublicApiSnapshot type, otherwise it's a breaking change to API
|
||||||
|
|
|
||||||
|
|
@ -28,31 +28,6 @@ import (
|
||||||
lru "github.com/hashicorp/golang-lru"
|
lru "github.com/hashicorp/golang-lru"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ecrecover extracts the Ethereum account address from a signed header.
|
|
||||||
func ecrecover(header *types.Header, sigcache *lru.ARCCache) (common.Address, error) {
|
|
||||||
// If the signature's already cached, return that
|
|
||||||
hash := header.Hash()
|
|
||||||
if address, known := sigcache.Get(hash); known {
|
|
||||||
return address.(common.Address), nil
|
|
||||||
}
|
|
||||||
// Retrieve the signature from the header extra-data
|
|
||||||
if len(header.Extra) < utils.ExtraSeal {
|
|
||||||
return common.Address{}, utils.ErrMissingSignature
|
|
||||||
}
|
|
||||||
signature := header.Extra[len(header.Extra)-utils.ExtraSeal:]
|
|
||||||
|
|
||||||
// Recover the public key and the Ethereum address
|
|
||||||
pubkey, err := crypto.Ecrecover(utils.SigHash(header).Bytes(), signature)
|
|
||||||
if err != nil {
|
|
||||||
return common.Address{}, err
|
|
||||||
}
|
|
||||||
var signer common.Address
|
|
||||||
copy(signer[:], crypto.Keccak256(pubkey[1:])[12:])
|
|
||||||
|
|
||||||
sigcache.Add(hash, signer)
|
|
||||||
return signer, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// XDPoS is the delegated-proof-of-stake consensus engine proposed to support the
|
// XDPoS is the delegated-proof-of-stake consensus engine proposed to support the
|
||||||
// Ethereum testnet following the Ropsten attacks.
|
// Ethereum testnet following the Ropsten attacks.
|
||||||
type XDPoS_v1 struct {
|
type XDPoS_v1 struct {
|
||||||
|
|
@ -106,7 +81,7 @@ func New(config *params.XDPoSConfig, db ethdb.Database) *XDPoS_v1 {
|
||||||
// Author implements consensus.Engine, returning the Ethereum address recovered
|
// Author implements consensus.Engine, returning the Ethereum address recovered
|
||||||
// from the signature in the header's extra-data section.
|
// from the signature in the header's extra-data section.
|
||||||
func (x *XDPoS_v1) Author(header *types.Header) (common.Address, error) {
|
func (x *XDPoS_v1) Author(header *types.Header) (common.Address, error) {
|
||||||
return ecrecover(header, x.signatures)
|
return utils.Ecrecover(header, x.signatures)
|
||||||
}
|
}
|
||||||
|
|
||||||
// VerifyHeader checks whether a header conforms to the consensus rules.
|
// VerifyHeader checks whether a header conforms to the consensus rules.
|
||||||
|
|
@ -384,7 +359,7 @@ func whoIsCreator(snap *SnapshotV1, header *types.Header) (common.Address, error
|
||||||
if header.Number.Uint64() == 0 {
|
if header.Number.Uint64() == 0 {
|
||||||
return common.Address{}, errors.New("Don't take block 0")
|
return common.Address{}, errors.New("Don't take block 0")
|
||||||
}
|
}
|
||||||
m, err := ecrecover(header, snap.sigcache)
|
m, err := utils.Ecrecover(header, snap.sigcache)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.Address{}, err
|
return common.Address{}, err
|
||||||
}
|
}
|
||||||
|
|
@ -549,7 +524,7 @@ func (x *XDPoS_v1) verifySeal(chain consensus.ChainReader, header *types.Header,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resolve the authorization key and check against signers
|
// Resolve the authorization key and check against signers
|
||||||
creator, err := ecrecover(header, x.signatures)
|
creator, err := utils.Ecrecover(header, x.signatures)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -643,7 +618,7 @@ func (x *XDPoS_v1) GetValidator(creator common.Address, chain consensus.ChainRea
|
||||||
return common.Address{}, fmt.Errorf("couldn't find checkpoint header")
|
return common.Address{}, fmt.Errorf("couldn't find checkpoint header")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
m, err := GetM1M2FromCheckpointHeader(cpHeader, header, chain.Config())
|
m, err := utils.GetM1M2FromCheckpointHeader(cpHeader, header, chain.Config())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.Address{}, err
|
return common.Address{}, err
|
||||||
}
|
}
|
||||||
|
|
@ -911,7 +886,7 @@ func (x *XDPoS_v1) calcDifficulty(chain consensus.ChainReader, parent *types.Hea
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *XDPoS_v1) RecoverSigner(header *types.Header) (common.Address, error) {
|
func (x *XDPoS_v1) RecoverSigner(header *types.Header) (common.Address, error) {
|
||||||
return ecrecover(header, x.signatures)
|
return utils.Ecrecover(header, x.signatures)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *XDPoS_v1) RecoverValidator(header *types.Header) (common.Address, error) {
|
func (x *XDPoS_v1) RecoverValidator(header *types.Header) (common.Address, error) {
|
||||||
|
|
@ -979,21 +954,6 @@ func GetMasternodesFromCheckpointHeader(checkpointHeader *types.Header) []common
|
||||||
return masternodes
|
return masternodes
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get m2 list from checkpoint block.
|
|
||||||
func GetM1M2FromCheckpointHeader(checkpointHeader *types.Header, currentHeader *types.Header, config *params.ChainConfig) (map[common.Address]common.Address, error) {
|
|
||||||
if checkpointHeader.Number.Uint64()%common.EpocBlockRandomize != 0 {
|
|
||||||
return nil, errors.New("This block is not checkpoint block epoc.")
|
|
||||||
}
|
|
||||||
// Get signers from this block.
|
|
||||||
masternodes := GetMasternodesFromCheckpointHeader(checkpointHeader)
|
|
||||||
validators := utils.ExtractValidatorsFromBytes(checkpointHeader.Validators)
|
|
||||||
m1m2, _, err := utils.GetM1M2(masternodes, validators, currentHeader, config)
|
|
||||||
if err != nil {
|
|
||||||
return map[common.Address]common.Address{}, err
|
|
||||||
}
|
|
||||||
return m1m2, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *XDPoS_v1) getSignersFromContract(chain consensus.ChainReader, checkpointHeader *types.Header) ([]common.Address, error) {
|
func (x *XDPoS_v1) getSignersFromContract(chain consensus.ChainReader, checkpointHeader *types.Header) ([]common.Address, error) {
|
||||||
startGapBlockHeader := checkpointHeader
|
startGapBlockHeader := checkpointHeader
|
||||||
number := checkpointHeader.Number.Uint64()
|
number := checkpointHeader.Number.Uint64()
|
||||||
|
|
|
||||||
|
|
@ -187,7 +187,7 @@ func (s *SnapshotV1) apply(headers []*types.Header) (*SnapshotV1, error) {
|
||||||
delete(snap.Recents, number-limit)
|
delete(snap.Recents, number-limit)
|
||||||
}
|
}
|
||||||
// Resolve the authorization key and check against signers
|
// Resolve the authorization key and check against signers
|
||||||
signer, err := ecrecover(header, s.sigcache)
|
signer, err := utils.Ecrecover(header, s.sigcache)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,12 @@
|
||||||
package engine_v2
|
package engine_v2
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
"path/filepath"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
|
@ -12,25 +16,30 @@ import (
|
||||||
"github.com/XinFinOrg/XDPoSChain/consensus"
|
"github.com/XinFinOrg/XDPoSChain/consensus"
|
||||||
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils"
|
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils"
|
||||||
"github.com/XinFinOrg/XDPoSChain/consensus/clique"
|
"github.com/XinFinOrg/XDPoSChain/consensus/clique"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/core/state"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||||
"github.com/XinFinOrg/XDPoSChain/crypto"
|
"github.com/XinFinOrg/XDPoSChain/crypto"
|
||||||
"github.com/XinFinOrg/XDPoSChain/ethdb"
|
"github.com/XinFinOrg/XDPoSChain/ethdb"
|
||||||
"github.com/XinFinOrg/XDPoSChain/log"
|
"github.com/XinFinOrg/XDPoSChain/log"
|
||||||
"github.com/XinFinOrg/XDPoSChain/params"
|
"github.com/XinFinOrg/XDPoSChain/params"
|
||||||
|
lru "github.com/hashicorp/golang-lru"
|
||||||
)
|
)
|
||||||
|
|
||||||
type XDPoS_v2 struct {
|
type XDPoS_v2 struct {
|
||||||
config *params.XDPoSConfig // Consensus engine configuration parameters
|
config *params.XDPoSConfig // Consensus engine configuration parameters
|
||||||
db ethdb.Database // Database to store and retrieve snapshot checkpoints
|
db ethdb.Database // Database to store and retrieve snapshot checkpoints
|
||||||
|
|
||||||
|
recents *lru.ARCCache // Snapshots for recent block to speed up reorgs
|
||||||
|
signatures *lru.ARCCache // Signatures of recent blocks to speed up mining
|
||||||
|
|
||||||
signer common.Address // Ethereum address of the signing key
|
signer common.Address // Ethereum address of the signing key
|
||||||
signFn clique.SignerFn // Signer function to authorize hashes with
|
signFn clique.SignerFn // Signer function to authorize hashes with
|
||||||
|
lock sync.RWMutex // Protects the signer fields
|
||||||
signLock sync.RWMutex // Protects the signer fields
|
signLock sync.RWMutex // Protects the signer fields
|
||||||
|
|
||||||
BroadcastCh chan interface{}
|
BroadcastCh chan interface{}
|
||||||
timeoutWorker *countdown.CountdownTimer // Timer to generate broadcast timeout msg if threashold reached
|
timeoutWorker *countdown.CountdownTimer // Timer to generate broadcast timeout msg if threashold reached
|
||||||
|
|
||||||
lock sync.RWMutex // Protects the currentRound fields etc
|
|
||||||
timeoutPool *utils.Pool
|
timeoutPool *utils.Pool
|
||||||
votePool *utils.Pool
|
votePool *utils.Pool
|
||||||
currentRound utils.Round
|
currentRound utils.Round
|
||||||
|
|
@ -40,23 +49,33 @@ type XDPoS_v2 struct {
|
||||||
lockQuorumCert *utils.QuorumCert
|
lockQuorumCert *utils.QuorumCert
|
||||||
highestTimeoutCert *utils.TimeoutCert
|
highestTimeoutCert *utils.TimeoutCert
|
||||||
highestCommitBlock *utils.BlockInfo
|
highestCommitBlock *utils.BlockInfo
|
||||||
|
|
||||||
|
HookReward func(chain consensus.ChainReader, state *state.StateDB, parentState *state.StateDB, header *types.Header) (error, map[string]interface{})
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(config *params.XDPoSConfig, db ethdb.Database) *XDPoS_v2 {
|
func New(config *params.XDPoSConfig, db ethdb.Database) *XDPoS_v2 {
|
||||||
// Setup Timer
|
// Setup Timer
|
||||||
duration := time.Duration(config.V2.TimeoutWorkerDuration) * time.Millisecond
|
duration := time.Duration(config.V2.TimeoutWorkerDuration) * time.Second
|
||||||
timer := countdown.NewCountDown(duration)
|
timer := countdown.NewCountDown(duration)
|
||||||
timeoutPool := utils.NewPool(config.V2.CertThreshold)
|
timeoutPool := utils.NewPool(config.V2.CertThreshold)
|
||||||
|
|
||||||
|
recents, _ := lru.NewARC(utils.InmemorySnapshots)
|
||||||
|
signatures, _ := lru.NewARC(utils.InmemorySnapshots)
|
||||||
|
|
||||||
votePool := utils.NewPool(config.V2.CertThreshold)
|
votePool := utils.NewPool(config.V2.CertThreshold)
|
||||||
engine := &XDPoS_v2{
|
engine := &XDPoS_v2{
|
||||||
config: config,
|
config: config,
|
||||||
db: db,
|
db: db,
|
||||||
timeoutWorker: timer,
|
signatures: signatures,
|
||||||
BroadcastCh: make(chan interface{}),
|
|
||||||
timeoutPool: timeoutPool,
|
recents: recents,
|
||||||
votePool: votePool,
|
timeoutWorker: timer,
|
||||||
highestTimeoutCert: &utils.TimeoutCert{},
|
BroadcastCh: make(chan interface{}),
|
||||||
highestQuorumCert: &utils.QuorumCert{},
|
timeoutPool: timeoutPool,
|
||||||
|
votePool: votePool,
|
||||||
|
|
||||||
|
highestTimeoutCert: nil,
|
||||||
|
highestQuorumCert: nil,
|
||||||
highestVotedRound: utils.Round(0),
|
highestVotedRound: utils.Round(0),
|
||||||
}
|
}
|
||||||
// Add callback to the timer
|
// Add callback to the timer
|
||||||
|
|
@ -65,24 +84,118 @@ func New(config *params.XDPoSConfig, db ethdb.Database) *XDPoS_v2 {
|
||||||
return engine
|
return engine
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// Prepare implements consensus.Engine, preparing all the consensus fields of the
|
||||||
Testing tools
|
// header for running the transactions on top.
|
||||||
*/
|
func (x *XDPoS_v2) Prepare(chain consensus.ChainReader, header *types.Header) error {
|
||||||
func (x *XDPoS_v2) SetNewRoundFaker(newRound utils.Round, resetTimer bool) {
|
// Verify mined block parent matches highest QC
|
||||||
x.lock.Lock()
|
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
|
||||||
// Reset a bunch of things
|
if header.Number.Cmp(big.NewInt(0).Add(x.config.XDPoSV2Block, big.NewInt(1))) == 0 {
|
||||||
if resetTimer {
|
log.Info("[Prepare] Initilising highest QC for consensus v2 first block", "Block Num", header.Number.String(), "BlockHash", header.Hash())
|
||||||
x.timeoutWorker.Reset()
|
// Generate new parent blockInfo and put it into QC
|
||||||
|
parentBlockInfo := &utils.BlockInfo{
|
||||||
|
Hash: header.ParentHash,
|
||||||
|
Round: utils.Round(0),
|
||||||
|
Number: big.NewInt(0).Sub(header.Number, big.NewInt(1)),
|
||||||
|
}
|
||||||
|
quorumCert := &utils.QuorumCert{
|
||||||
|
ProposedBlockInfo: parentBlockInfo,
|
||||||
|
Signatures: nil,
|
||||||
|
}
|
||||||
|
x.currentRound = 1
|
||||||
|
x.highestQuorumCert = quorumCert
|
||||||
}
|
}
|
||||||
x.currentRound = newRound
|
|
||||||
|
currentRound := x.currentRound
|
||||||
|
highestQC := x.highestQuorumCert
|
||||||
|
x.lock.Unlock()
|
||||||
|
//parentRound := highestQC.ProposedBlockInfo.Round
|
||||||
|
if (highestQC == nil) || (header.ParentHash != highestQC.ProposedBlockInfo.Hash) {
|
||||||
|
return consensus.ErrNotReadyToPropose
|
||||||
|
}
|
||||||
|
|
||||||
|
extra := utils.ExtraFields_v2{
|
||||||
|
Round: currentRound,
|
||||||
|
QuorumCert: highestQC,
|
||||||
|
}
|
||||||
|
|
||||||
|
header.Nonce = types.BlockNonce{}
|
||||||
|
|
||||||
|
number := header.Number.Uint64()
|
||||||
|
parent := chain.GetHeader(header.ParentHash, number-1)
|
||||||
|
log.Info("Preparing new block!", "Number", number, "Parent Hash", parent.Hash())
|
||||||
|
if parent == nil {
|
||||||
|
return consensus.ErrUnknownAncestor
|
||||||
|
}
|
||||||
|
// Set the correct difficulty
|
||||||
|
header.Difficulty = x.calcDifficulty(chain, parent, x.signer)
|
||||||
|
log.Debug("CalcDifficulty ", "number", header.Number, "difficulty", header.Difficulty)
|
||||||
|
|
||||||
|
// TODO: previous round should sit on previous Epoch and x.currentRound should >= Epoch number
|
||||||
|
if number%x.config.Epoch == 0 {
|
||||||
|
snap, err := x.snapshot(chain, number-1, header.ParentHash, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
masternodes := snap.GetMasterNodes()
|
||||||
|
//TODO: remove penalty nodes and add comeback nodes
|
||||||
|
for _, v := range masternodes {
|
||||||
|
header.Validators = append(header.Validators, v[:]...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extraBytes, err := extra.EncodeToBytes()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
header.Extra = extraBytes
|
||||||
|
|
||||||
|
// Mix digest is reserved for now, set to empty
|
||||||
|
header.MixDigest = common.Hash{}
|
||||||
|
|
||||||
|
// Ensure the timestamp has the correct delay
|
||||||
|
|
||||||
|
// TODO: if timestamp > current time, how to deal with future timestamp
|
||||||
|
header.Time = new(big.Int).Add(parent.Time, new(big.Int).SetUint64(x.config.Period))
|
||||||
|
if header.Time.Int64() < time.Now().Unix() {
|
||||||
|
header.Time = big.NewInt(time.Now().Unix())
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Utils for test to check currentRound value
|
// Finalize implements consensus.Engine, ensuring no uncles are set, nor block
|
||||||
func (x *XDPoS_v2) GetProperties() (utils.Round, *utils.QuorumCert, *utils.QuorumCert) {
|
// rewards given, and returns the final block.
|
||||||
x.lock.Lock()
|
func (x *XDPoS_v2) Finalize(chain consensus.ChainReader, header *types.Header, state *state.StateDB, parentState *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) {
|
||||||
defer x.lock.Unlock()
|
// set block reward
|
||||||
return x.currentRound, x.lockQuorumCert, x.highestQuorumCert
|
number := header.Number.Uint64()
|
||||||
|
rCheckpoint := chain.Config().XDPoS.RewardCheckpoint
|
||||||
|
|
||||||
|
// _ = c.CacheData(header, txs, receipts)
|
||||||
|
|
||||||
|
if x.HookReward != nil && number%rCheckpoint == 0 {
|
||||||
|
err, rewards := x.HookReward(chain, state, parentState, header)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(common.StoreRewardFolder) > 0 {
|
||||||
|
data, err := json.Marshal(rewards)
|
||||||
|
if err == nil {
|
||||||
|
err = ioutil.WriteFile(filepath.Join(common.StoreRewardFolder, header.Number.String()+"."+header.Hash().Hex()), data, 0644)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Error when save reward info ", "number", header.Number, "hash", header.Hash().Hex(), "err", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// the state remains as is and uncles are dropped
|
||||||
|
header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number))
|
||||||
|
header.UncleHash = types.CalcUncleHash(nil)
|
||||||
|
|
||||||
|
// Assemble and return the final block for sealing
|
||||||
|
return types.NewBlock(header, txs, nil, receipts), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Authorize injects a private key into the consensus engine to mint new blocks with.
|
// Authorize injects a private key into the consensus engine to mint new blocks with.
|
||||||
|
|
@ -95,13 +208,241 @@ func (x *XDPoS_v2) Authorize(signer common.Address, signFn clique.SignerFn) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *XDPoS_v2) Author(header *types.Header) (common.Address, error) {
|
func (x *XDPoS_v2) Author(header *types.Header) (common.Address, error) {
|
||||||
return common.Address{}, nil
|
return utils.EcrecoverV2(header, x.signatures)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Seal implements consensus.Engine, attempting to create a sealed block using
|
||||||
|
// the local signing credentials.
|
||||||
|
func (x *XDPoS_v2) Seal(chain consensus.ChainReader, block *types.Block, stop <-chan struct{}) (*types.Block, error) {
|
||||||
|
header := block.Header()
|
||||||
|
|
||||||
|
// Sealing the genesis block is not supported
|
||||||
|
number := header.Number.Uint64()
|
||||||
|
if number == 0 {
|
||||||
|
return nil, utils.ErrUnknownBlock
|
||||||
|
}
|
||||||
|
// For 0-period chains, refuse to seal empty blocks (no reward but would spin sealing)
|
||||||
|
// checkpoint blocks have no tx
|
||||||
|
if x.config.Period == 0 && len(block.Transactions()) == 0 && number%x.config.Epoch != 0 {
|
||||||
|
return nil, utils.ErrWaitTransactions
|
||||||
|
}
|
||||||
|
// Don't hold the signer fields for the entire sealing procedure
|
||||||
|
x.signLock.RLock()
|
||||||
|
signer, signFn := x.signer, x.signFn
|
||||||
|
x.signLock.RUnlock()
|
||||||
|
|
||||||
|
// Bail out if we're unauthorized to sign a block
|
||||||
|
snap, err := x.snapshot(chain, number-1, header.ParentHash, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
masternodes := x.GetMasternodes(chain, header)
|
||||||
|
if _, authorized := snap.MasterNodes[signer]; !authorized {
|
||||||
|
valid := false
|
||||||
|
for _, m := range masternodes {
|
||||||
|
if m == signer {
|
||||||
|
valid = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !valid {
|
||||||
|
return nil, utils.ErrUnauthorized
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-stop:
|
||||||
|
return nil, nil
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sign all the things!
|
||||||
|
signature, err := signFn(accounts.Account{Address: signer}, utils.SigHashV2(header).Bytes())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
header.Validator = signature
|
||||||
|
|
||||||
|
return block.WithSeal(header), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CalcDifficulty is the difficulty adjustment algorithm. It returns the difficulty
|
||||||
|
// that a new block should have based on the previous blocks in the chain and the
|
||||||
|
// current signer.
|
||||||
|
func (x *XDPoS_v2) CalcDifficulty(chain consensus.ChainReader, time uint64, parent *types.Header) *big.Int {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy from v1
|
||||||
|
func (x *XDPoS_v2) YourTurn(chain consensus.ChainReader, parent *types.Header, signer common.Address) (int, int, int, bool, error) {
|
||||||
|
snap, err := x.GetSnapshot(chain, parent)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("[YourTurn] Failed while getting snapshot", "parentHash", parent.Hash(), "err", err)
|
||||||
|
return 0, -1, -1, false, err
|
||||||
|
}
|
||||||
|
masternodes := x.GetMasternodes(chain, parent)
|
||||||
|
if len(masternodes) == 0 {
|
||||||
|
return 0, -1, -1, false, errors.New("Masternodes not found")
|
||||||
|
}
|
||||||
|
pre := common.Address{}
|
||||||
|
// masternode[0] has chance to create block 1
|
||||||
|
preIndex := -1
|
||||||
|
if parent.Number.Uint64() != 0 {
|
||||||
|
pre, err = whoIsCreator(snap, parent)
|
||||||
|
if err != nil {
|
||||||
|
return 0, 0, 0, false, err
|
||||||
|
}
|
||||||
|
preIndex = utils.Position(masternodes, pre)
|
||||||
|
}
|
||||||
|
curIndex := utils.Position(masternodes, signer)
|
||||||
|
if signer == x.signer {
|
||||||
|
log.Debug("Masternodes cycle info", "number of masternodes", len(masternodes), "previous", pre, "position", preIndex, "current", signer, "position", curIndex)
|
||||||
|
}
|
||||||
|
for i, s := range masternodes {
|
||||||
|
log.Debug("Masternode:", "index", i, "address", s.String())
|
||||||
|
}
|
||||||
|
if (preIndex+1)%len(masternodes) == curIndex {
|
||||||
|
return len(masternodes), preIndex, curIndex, true, nil
|
||||||
|
}
|
||||||
|
return len(masternodes), preIndex, curIndex, false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy from v1
|
||||||
|
func whoIsCreator(snap *SnapshotV2, header *types.Header) (common.Address, error) {
|
||||||
|
if header.Number.Uint64() == 0 {
|
||||||
|
return common.Address{}, errors.New("Don't take block 0")
|
||||||
|
}
|
||||||
|
m, err := utils.EcrecoverV2(header, snap.sigcache)
|
||||||
|
if err != nil {
|
||||||
|
return common.Address{}, err
|
||||||
|
}
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy from v1
|
||||||
|
func (x *XDPoS_v2) GetMasternodes(chain consensus.ChainReader, header *types.Header) []common.Address {
|
||||||
|
n := header.Number.Uint64()
|
||||||
|
e := x.config.Epoch
|
||||||
|
switch {
|
||||||
|
case n%e == 0:
|
||||||
|
return utils.GetMasternodesFromCheckpointHeader(header)
|
||||||
|
case n%e != 0:
|
||||||
|
h := chain.GetHeaderByNumber(n - (n % e))
|
||||||
|
return utils.GetMasternodesFromCheckpointHeader(h)
|
||||||
|
default:
|
||||||
|
return []common.Address{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy from v1
|
||||||
|
func (x *XDPoS_v2) GetSnapshot(chain consensus.ChainReader, header *types.Header) (*SnapshotV2, error) {
|
||||||
|
number := header.Number.Uint64()
|
||||||
|
log.Trace("get snapshot", "number", number, "hash", header.Hash())
|
||||||
|
snap, err := x.snapshot(chain, number, header.Hash(), nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return snap, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// snapshot retrieves the authorization snapshot at a given point in time.
|
||||||
|
func (x *XDPoS_v2) snapshot(chain consensus.ChainReader, number uint64, hash common.Hash, parents []*types.Header) (*SnapshotV2, error) {
|
||||||
|
// Search for a SnapshotV2 in memory or on disk for checkpoints
|
||||||
|
var (
|
||||||
|
headers []*types.Header
|
||||||
|
snap *SnapshotV2
|
||||||
|
)
|
||||||
|
for snap == nil {
|
||||||
|
// If an in-memory SnapshotV2 was found, use that
|
||||||
|
if s, ok := x.recents.Get(hash); ok {
|
||||||
|
snap = s.(*SnapshotV2)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
// If an on-disk checkpoint snapshot can be found, use that
|
||||||
|
// checkpoint snapshot = checkpoint - gap
|
||||||
|
if (number+x.config.Gap)%x.config.Epoch == 0 {
|
||||||
|
if s, err := loadSnapshot(x.signatures, x.db, hash); err == nil {
|
||||||
|
log.Trace("Loaded snapshot form disk", "number", number, "hash", hash)
|
||||||
|
snap = s
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If we're at 0 block, make a snapshot
|
||||||
|
// TODO: We may need to store snapshot at the v1 -> v2 switch block
|
||||||
|
if number == 0 {
|
||||||
|
genesis := chain.GetHeaderByNumber(0)
|
||||||
|
if err := x.VerifyHeader(chain, genesis, true); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
signers := make([]common.Address, (len(genesis.Extra)-utils.ExtraVanity-utils.ExtraSeal)/common.AddressLength)
|
||||||
|
for i := 0; i < len(signers); i++ {
|
||||||
|
copy(signers[i][:], genesis.Extra[utils.ExtraVanity+i*common.AddressLength:])
|
||||||
|
}
|
||||||
|
snap = newSnapshot(x.signatures, 0, genesis.Hash(), x.currentRound, x.highestQuorumCert, signers)
|
||||||
|
if err := storeSnapshot(snap, x.db); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
log.Trace("Stored genesis voting snapshot to disk")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
// No snapshot for this header, gather the header and move backward
|
||||||
|
var header *types.Header
|
||||||
|
if len(parents) > 0 {
|
||||||
|
// If we have explicit parents, pick from there (enforced)
|
||||||
|
header = parents[len(parents)-1]
|
||||||
|
if header.Hash() != hash || header.Number.Uint64() != number {
|
||||||
|
return nil, consensus.ErrUnknownAncestor
|
||||||
|
}
|
||||||
|
parents = parents[:len(parents)-1]
|
||||||
|
} else {
|
||||||
|
// No explicit parents (or no more left), reach out to the database
|
||||||
|
header = chain.GetHeader(hash, number)
|
||||||
|
if header == nil {
|
||||||
|
log.Error("[Seal] Failed due to no header found", "hash", hash, "number", number)
|
||||||
|
return nil, consensus.ErrUnknownAncestor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
headers = append(headers, header)
|
||||||
|
number, hash = number-1, header.ParentHash
|
||||||
|
}
|
||||||
|
// Previous snapshot found, apply any pending headers on top of it
|
||||||
|
for i := 0; i < len(headers)/2; i++ {
|
||||||
|
headers[i], headers[len(headers)-1-i] = headers[len(headers)-1-i], headers[i]
|
||||||
|
}
|
||||||
|
snap, err := snap.apply(headers)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
x.recents.Add(snap.Hash, snap)
|
||||||
|
|
||||||
|
// If we've generated a new checkpoint snapshot, save to disk
|
||||||
|
// TODO how to save correct snapshot
|
||||||
|
if uint64(snap.Round)%x.config.Epoch == x.config.Gap {
|
||||||
|
if err = storeSnapshot(snap, x.db); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
log.Trace("Stored snapshot to disk", "round number", snap.Round, "hash", snap.Hash)
|
||||||
|
}
|
||||||
|
return snap, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *XDPoS_v2) VerifyHeader(chain consensus.ChainReader, header *types.Header, fullVerify bool) error {
|
func (x *XDPoS_v2) VerifyHeader(chain consensus.ChainReader, header *types.Header, fullVerify bool) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Utils for test to check currentRound value
|
||||||
|
func (x *XDPoS_v2) GetProperties() (utils.Round, *utils.QuorumCert, *utils.QuorumCert) {
|
||||||
|
x.lock.Lock()
|
||||||
|
defer x.lock.Unlock()
|
||||||
|
return x.currentRound, x.lockQuorumCert, x.highestQuorumCert
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
SyncInfo workflow
|
SyncInfo workflow
|
||||||
*/
|
*/
|
||||||
|
|
@ -152,7 +493,7 @@ func (x *XDPoS_v2) VerifyVoteMessage(vote *utils.Vote) (bool, error) {
|
||||||
2. Verify blockInfo
|
2. Verify blockInfo
|
||||||
3. Broadcast(Not part of consensus)
|
3. Broadcast(Not part of consensus)
|
||||||
*/
|
*/
|
||||||
return x.verifyMsgSignature(utils.VoteSigHash(&vote.ProposedBlockInfo), vote.Signature)
|
return x.verifyMsgSignature(utils.VoteSigHash(vote.ProposedBlockInfo), vote.Signature)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Consensus entry point for processing vote message to produce QC
|
// Consensus entry point for processing vote message to produce QC
|
||||||
|
|
@ -228,7 +569,7 @@ func (x *XDPoS_v2) TimeoutHandler(timeout *utils.Timeout) error {
|
||||||
|
|
||||||
// 1. checkRoundNumber
|
// 1. checkRoundNumber
|
||||||
if timeout.Round != x.currentRound {
|
if timeout.Round != x.currentRound {
|
||||||
return fmt.Errorf("Timeout message round number: %v does not match currentRound: %v", timeout.Round, x.currentRound)
|
return &utils.ErrIncomingMessageRoundNotEqualCurrentRound{timeout.Round, x.currentRound}
|
||||||
}
|
}
|
||||||
// Collect timeout, generate TC
|
// Collect timeout, generate TC
|
||||||
isThresholdReached, numberOfTimeoutsInPool, pooledTimeouts := x.timeoutPool.Add(timeout)
|
isThresholdReached, numberOfTimeoutsInPool, pooledTimeouts := x.timeoutPool.Add(timeout)
|
||||||
|
|
@ -277,19 +618,43 @@ func (x *XDPoS_v2) onTimeoutPoolThresholdReached(pooledTimeouts map[common.Hash]
|
||||||
/*
|
/*
|
||||||
Proposed Block workflow
|
Proposed Block workflow
|
||||||
*/
|
*/
|
||||||
func (x *XDPoS_v2) ProposedBlockHandler(blockChainReader consensus.ChainReader, blockInfo *utils.BlockInfo, quorumCert *utils.QuorumCert) error {
|
func (x *XDPoS_v2) ProposedBlockHandler(blockChainReader consensus.ChainReader, blockHeader *types.Header) error {
|
||||||
x.lock.Lock()
|
x.lock.Lock()
|
||||||
defer x.lock.Unlock()
|
defer x.lock.Unlock()
|
||||||
|
|
||||||
/*
|
/*
|
||||||
1. processQC(): process the QC inside the proposed block
|
1. Verify QC
|
||||||
2. verifyVotingRule(): the proposed block's info is extracted into BlockInfo and verified for voting
|
2. Generate blockInfo
|
||||||
3. sendVote()
|
3. processQC(): process the QC inside the proposed block
|
||||||
|
4. verifyVotingRule(): the proposed block's info is extracted into BlockInfo and verified for voting
|
||||||
|
5. sendVote()
|
||||||
*/
|
*/
|
||||||
err := x.processQC(blockChainReader, quorumCert)
|
// Get QC and Round from Extra
|
||||||
|
var decodedExtraField utils.ExtraFields_v2
|
||||||
|
err := utils.DecodeBytesExtraFields(blockHeader.Extra, &decodedExtraField)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
quorumCert := decodedExtraField.QuorumCert
|
||||||
|
round := decodedExtraField.Round
|
||||||
|
|
||||||
|
err = x.verifyQC(quorumCert)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("[ProposedBlockHandler] Fail to verify QC", "Extra round", round, "QC proposed BlockInfo Hash", quorumCert.ProposedBlockInfo.Hash)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate blockInfo
|
||||||
|
blockInfo := &utils.BlockInfo{
|
||||||
|
Hash: blockHeader.Hash(),
|
||||||
|
Round: round,
|
||||||
|
Number: blockHeader.Number,
|
||||||
|
}
|
||||||
|
err = x.processQC(blockChainReader, quorumCert)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("[ProposedBlockHandler] Fail to processQC", "QC proposed blockInfo round number", quorumCert.ProposedBlockInfo.Round, "QC proposed blockInfo hash", quorumCert.ProposedBlockInfo.Hash)
|
||||||
|
return err
|
||||||
|
}
|
||||||
verified, err := x.verifyVotingRule(blockChainReader, blockInfo, quorumCert)
|
verified, err := x.verifyVotingRule(blockChainReader, blockInfo, quorumCert)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -335,33 +700,41 @@ func (x *XDPoS_v2) verifyTC(timeoutCert *utils.TimeoutCert) error {
|
||||||
|
|
||||||
// Update local QC variables including highestQC & lockQuorumCert, as well as commit the blocks that satisfy the algorithm requirements
|
// Update local QC variables including highestQC & lockQuorumCert, as well as commit the blocks that satisfy the algorithm requirements
|
||||||
func (x *XDPoS_v2) processQC(blockChainReader consensus.ChainReader, quorumCert *utils.QuorumCert) error {
|
func (x *XDPoS_v2) processQC(blockChainReader consensus.ChainReader, quorumCert *utils.QuorumCert) error {
|
||||||
|
log.Trace("[ProcessQC][Before]", "HighQC", x.highestQuorumCert)
|
||||||
// 1. Update HighestQC
|
// 1. Update HighestQC
|
||||||
if x.highestQuorumCert == nil || (quorumCert.ProposedBlockInfo.Round > x.highestQuorumCert.ProposedBlockInfo.Round) {
|
if x.highestQuorumCert == nil || (quorumCert.ProposedBlockInfo.Round > x.highestQuorumCert.ProposedBlockInfo.Round) {
|
||||||
x.highestQuorumCert = quorumCert
|
x.highestQuorumCert = quorumCert
|
||||||
}
|
}
|
||||||
// 2. Get QC from header and update lockQuorumCert(lockQuorumCert is the parent of highestQC)
|
// 2. Get QC from header and update lockQuorumCert(lockQuorumCert is the parent of highestQC)
|
||||||
proposedBlockHeader := blockChainReader.GetHeaderByHash(quorumCert.ProposedBlockInfo.Hash)
|
proposedBlockHeader := blockChainReader.GetHeaderByHash(quorumCert.ProposedBlockInfo.Hash)
|
||||||
// Extra field contain parent information
|
if proposedBlockHeader.Number.Cmp(x.config.XDPoSV2Block) > 0 {
|
||||||
var decodedExtraField utils.ExtraFields_v2
|
// Extra field contain parent information
|
||||||
err := utils.DecodeBytesExtraFields(proposedBlockHeader.Extra, &decodedExtraField)
|
var decodedExtraField utils.ExtraFields_v2
|
||||||
if err != nil {
|
err := utils.DecodeBytesExtraFields(proposedBlockHeader.Extra, &decodedExtraField)
|
||||||
return err
|
if err != nil {
|
||||||
}
|
return err
|
||||||
x.lockQuorumCert = &decodedExtraField.QuorumCert
|
}
|
||||||
|
if x.lockQuorumCert == nil || decodedExtraField.QuorumCert.ProposedBlockInfo.Round > x.lockQuorumCert.ProposedBlockInfo.Round {
|
||||||
|
x.lockQuorumCert = decodedExtraField.QuorumCert
|
||||||
|
}
|
||||||
|
|
||||||
proposedBlockRound := &decodedExtraField.Round
|
proposedBlockRound := &decodedExtraField.Round
|
||||||
// 3. Update commit block info
|
// 3. Update commit block info
|
||||||
_, err = x.commitBlocks(blockChainReader, proposedBlockHeader, proposedBlockRound)
|
_, err = x.commitBlocks(blockChainReader, proposedBlockHeader, proposedBlockRound)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
log.Error("[processQC] Fail to commitBlocks", "proposedBlockRound", proposedBlockRound)
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// 4. Set new round
|
// 4. Set new round
|
||||||
if quorumCert.ProposedBlockInfo.Round >= x.currentRound {
|
if quorumCert.ProposedBlockInfo.Round >= x.currentRound {
|
||||||
err := x.setNewRound(quorumCert.ProposedBlockInfo.Round + 1)
|
err := x.setNewRound(quorumCert.ProposedBlockInfo.Round + 1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
log.Error("[processQC] Fail to setNewRound", "new round to set", quorumCert.ProposedBlockInfo.Round+1)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
log.Trace("[ProcessQC][After]", "HighQC", x.highestQuorumCert)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -411,7 +784,11 @@ func (x *XDPoS_v2) verifyVotingRule(blockChainReader consensus.ChainReader, bloc
|
||||||
if blockInfo.Round != x.currentRound {
|
if blockInfo.Round != x.currentRound {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
isExtended, err := x.isExtendingFromAncestor(blockChainReader, blockInfo, &x.lockQuorumCert.ProposedBlockInfo)
|
// XDPoS v1.0 switch to v2.0, the proposed block can always pass voting rule
|
||||||
|
if x.lockQuorumCert == nil {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
isExtended, err := x.isExtendingFromAncestor(blockChainReader, blockInfo, x.lockQuorumCert.ProposedBlockInfo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
@ -436,7 +813,7 @@ func (x *XDPoS_v2) sendVote(blockInfo *utils.BlockInfo) error {
|
||||||
|
|
||||||
x.highestVotedRound = x.currentRound
|
x.highestVotedRound = x.currentRound
|
||||||
voteMsg := &utils.Vote{
|
voteMsg := &utils.Vote{
|
||||||
ProposedBlockInfo: *blockInfo,
|
ProposedBlockInfo: blockInfo,
|
||||||
Signature: signedHash,
|
Signature: signedHash,
|
||||||
}
|
}
|
||||||
x.broadcastToBftChannel(voteMsg)
|
x.broadcastToBftChannel(voteMsg)
|
||||||
|
|
@ -524,17 +901,44 @@ func (x *XDPoS_v2) getCurrentRoundMasterNodes() []common.Address {
|
||||||
return []common.Address{}
|
return []common.Address{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *XDPoS_v2) getSyncInfo() utils.SyncInfo {
|
/*
|
||||||
return utils.SyncInfo{
|
Testing tools
|
||||||
|
*/
|
||||||
|
|
||||||
|
func (x *XDPoS_v2) SetHighestQuorumCert(qc *utils.QuorumCert) {
|
||||||
|
x.highestQuorumCert = qc
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *XDPoS_v2) getSyncInfo() *utils.SyncInfo {
|
||||||
|
return &utils.SyncInfo{
|
||||||
HighestQuorumCert: x.highestQuorumCert,
|
HighestQuorumCert: x.highestQuorumCert,
|
||||||
HighestTimeoutCert: x.highestTimeoutCert,
|
HighestTimeoutCert: x.highestTimeoutCert,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *XDPoS_v2) SetNewRoundFaker(newRound utils.Round, resetTimer bool) {
|
||||||
|
x.lock.Lock()
|
||||||
|
defer x.lock.Unlock()
|
||||||
|
// Reset a bunch of things
|
||||||
|
if resetTimer {
|
||||||
|
x.timeoutWorker.Reset()
|
||||||
|
}
|
||||||
|
x.currentRound = newRound
|
||||||
|
}
|
||||||
|
|
||||||
|
// Utils for test to check currentRound value
|
||||||
|
func (x *XDPoS_v2) GetCurrentRound() utils.Round {
|
||||||
|
return x.currentRound
|
||||||
|
}
|
||||||
|
|
||||||
//TODO: find parent and grandparent and grandgrandparent block, check round number, if so, commit grandgrandparent
|
//TODO: find parent and grandparent and grandgrandparent block, check round number, if so, commit grandgrandparent
|
||||||
func (x *XDPoS_v2) commitBlocks(blockChainReader consensus.ChainReader, proposedBlockHeader *types.Header, proposedBlockRound *utils.Round) (bool, error) {
|
func (x *XDPoS_v2) commitBlocks(blockCahinReader consensus.ChainReader, proposedBlockHeader *types.Header, proposedBlockRound *utils.Round) (bool, error) {
|
||||||
|
// XDPoS v1.0 switch to v2.0, skip commit
|
||||||
|
if big.NewInt(0).Sub(proposedBlockHeader.Number, big.NewInt(2)).Cmp(x.config.XDPoSV2Block) <= 0 {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
// Find the last two parent block and check their rounds are the continous
|
// Find the last two parent block and check their rounds are the continous
|
||||||
parentBlock := blockChainReader.GetHeaderByHash(proposedBlockHeader.ParentHash)
|
parentBlock := blockCahinReader.GetHeaderByHash(proposedBlockHeader.ParentHash)
|
||||||
|
|
||||||
var decodedExtraField utils.ExtraFields_v2
|
var decodedExtraField utils.ExtraFields_v2
|
||||||
err := utils.DecodeBytesExtraFields(parentBlock.Extra, &decodedExtraField)
|
err := utils.DecodeBytesExtraFields(parentBlock.Extra, &decodedExtraField)
|
||||||
|
|
@ -546,7 +950,7 @@ func (x *XDPoS_v2) commitBlocks(blockChainReader consensus.ChainReader, proposed
|
||||||
}
|
}
|
||||||
|
|
||||||
// If parent round is continous, we check grandparent
|
// If parent round is continous, we check grandparent
|
||||||
grandParentBlock := blockChainReader.GetHeaderByHash(parentBlock.ParentHash)
|
grandParentBlock := blockCahinReader.GetHeaderByHash(parentBlock.ParentHash)
|
||||||
err = utils.DecodeBytesExtraFields(grandParentBlock.Extra, &decodedExtraField)
|
err = utils.DecodeBytesExtraFields(grandParentBlock.Extra, &decodedExtraField)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
|
|
|
||||||
|
|
@ -1 +1,133 @@
|
||||||
package engine_v2
|
package engine_v2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"sort"
|
||||||
|
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/common"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/ethdb"
|
||||||
|
lru "github.com/hashicorp/golang-lru"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Snapshot is the state of the smart contract validator list
|
||||||
|
type SnapshotV2 struct {
|
||||||
|
sigcache *lru.ARCCache // Cache of recent block signatures to speed up ecrecover
|
||||||
|
|
||||||
|
Round utils.Round `json:"round"` // Round number
|
||||||
|
Number uint64 `json:"number"` // Block number where the snapshot was created
|
||||||
|
Hash common.Hash `json:"hash"` // Block hash where the snapshot was created
|
||||||
|
|
||||||
|
// MasterNodes will get assigned on updateM1
|
||||||
|
MasterNodes map[common.Address]struct{} `json:"masterNodes"` // Set of authorized master nodes at this moment
|
||||||
|
}
|
||||||
|
|
||||||
|
// newSnapshot creates a new snapshot with the specified startup parameters. This
|
||||||
|
// method does not initialize the set of recent signers, so only ever use if for
|
||||||
|
// the genesis block.
|
||||||
|
func newSnapshot(sigcache *lru.ARCCache, number uint64, hash common.Hash, round utils.Round, qc *utils.QuorumCert, masternodes []common.Address) *SnapshotV2 {
|
||||||
|
snap := &SnapshotV2{
|
||||||
|
sigcache: sigcache,
|
||||||
|
Round: round,
|
||||||
|
Number: number,
|
||||||
|
Hash: hash,
|
||||||
|
|
||||||
|
MasterNodes: make(map[common.Address]struct{}),
|
||||||
|
}
|
||||||
|
for _, signer := range masternodes {
|
||||||
|
snap.MasterNodes[signer] = struct{}{}
|
||||||
|
}
|
||||||
|
return snap
|
||||||
|
}
|
||||||
|
|
||||||
|
// loadSnapshot loads an existing snapshot from the database.
|
||||||
|
func loadSnapshot(sigcache *lru.ARCCache, db ethdb.Database, hash common.Hash) (*SnapshotV2, error) {
|
||||||
|
blob, err := db.Get(append([]byte("XDPoS-"), hash[:]...))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
snap := new(SnapshotV2)
|
||||||
|
if err := json.Unmarshal(blob, snap); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
snap.sigcache = sigcache
|
||||||
|
|
||||||
|
return snap, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// store inserts the SnapshotV2 into the database.
|
||||||
|
func storeSnapshot(s *SnapshotV2, db ethdb.Database) error {
|
||||||
|
blob, err := json.Marshal(s)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return db.Put(append([]byte("XDPoS-"), s.Hash[:]...), blob)
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy creates a deep copy of the SnapshotV2, though not the individual votes.
|
||||||
|
func (s *SnapshotV2) copy() *SnapshotV2 {
|
||||||
|
cpy := &SnapshotV2{
|
||||||
|
sigcache: s.sigcache,
|
||||||
|
Round: s.Round,
|
||||||
|
Number: s.Number,
|
||||||
|
Hash: s.Hash,
|
||||||
|
MasterNodes: make(map[common.Address]struct{}),
|
||||||
|
}
|
||||||
|
for signer := range s.MasterNodes {
|
||||||
|
cpy.MasterNodes[signer] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
return cpy
|
||||||
|
}
|
||||||
|
|
||||||
|
// apply creates a new authorization SnapshotV2 by applying the given headers to
|
||||||
|
// the original one.
|
||||||
|
// TODO: XIN-100
|
||||||
|
func (s *SnapshotV2) apply(headers []*types.Header) (*SnapshotV2, error) {
|
||||||
|
return s, nil
|
||||||
|
|
||||||
|
// Allow passing in no headers for cleaner code
|
||||||
|
// if len(headers) == 0 {
|
||||||
|
// return s, nil
|
||||||
|
// }
|
||||||
|
// // Sanity check that the headers can be applied
|
||||||
|
// for i := 0; i < len(headers)-1; i++ {
|
||||||
|
// if headers[i+1].Number.Uint64() != headers[i].Number.Uint64()+1 {
|
||||||
|
// return nil, utils.ErrInvalidHeaderOrder
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// if headers[0].Number.Uint64() != s.Number+1 {
|
||||||
|
// return nil, utils.ErrInvalidChild
|
||||||
|
// }
|
||||||
|
// // Iterate through the headers and create a new SnapshotV2
|
||||||
|
// snap := s.copy()
|
||||||
|
// lastHeader := headers[len(headers)-1]
|
||||||
|
|
||||||
|
// snap.Number += uint64(len(headers))
|
||||||
|
// snap.Hash = lastHeader.Hash()
|
||||||
|
|
||||||
|
// extraV2 := new(utils.ExtraFields_v2)
|
||||||
|
// err := utils.DecodeBytesExtraFields(lastHeader.Extra, &extraV2)
|
||||||
|
// if err != nil {
|
||||||
|
// return nil, err
|
||||||
|
// }
|
||||||
|
// snap.Round = extraV2.Round
|
||||||
|
// return snap, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// signers retrieves the list of authorized signers in ascending order, convert into strings then use native sort lib
|
||||||
|
func (s *SnapshotV2) GetMasterNodes() []common.Address {
|
||||||
|
nodes := make([]common.Address, 0, len(s.MasterNodes))
|
||||||
|
nodeStrs := make([]string, 0, len(s.MasterNodes))
|
||||||
|
|
||||||
|
for node := range s.MasterNodes {
|
||||||
|
nodeStrs = append(nodeStrs, node.Str())
|
||||||
|
}
|
||||||
|
sort.Strings(nodeStrs)
|
||||||
|
for _, str := range nodeStrs {
|
||||||
|
nodes = append(nodes, common.StringToAddress(str))
|
||||||
|
}
|
||||||
|
|
||||||
|
return nodes
|
||||||
|
}
|
||||||
|
|
|
||||||
122
consensus/XDPoS/engines/engine_v2/snapshot_test.go
Normal file
122
consensus/XDPoS/engines/engine_v2/snapshot_test.go
Normal file
|
|
@ -0,0 +1,122 @@
|
||||||
|
package engine_v2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"math/big"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/common"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/ethdb/leveldb"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGetMasterNodes(t *testing.T) {
|
||||||
|
masterNodes := []common.Address{
|
||||||
|
{4}, {3}, {2}, {1},
|
||||||
|
}
|
||||||
|
snap := newSnapshot(nil, 1, common.Hash{}, utils.Round(1), nil, masterNodes)
|
||||||
|
sortedNodes := snap.GetMasterNodes()
|
||||||
|
for i := range masterNodes {
|
||||||
|
if masterNodes[i] != sortedNodes[3-i] {
|
||||||
|
t.Error("should get sorted master nodes list", i, sortedNodes[i])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func TestApplyNewSnapshot(t *testing.T) {
|
||||||
|
t.Skip("apply has been temporary commented out")
|
||||||
|
snap := newSnapshot(nil, 1, common.Hash{}, utils.Round(1), nil, nil)
|
||||||
|
extra := utils.ExtraFields_v2{
|
||||||
|
Round: 10,
|
||||||
|
QuorumCert: &utils.QuorumCert{
|
||||||
|
ProposedBlockInfo: &utils.BlockInfo{},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
extraBytes, err := extra.EncodeToBytes()
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
headers := []*types.Header{
|
||||||
|
{Number: big.NewInt(2)},
|
||||||
|
{Number: big.NewInt(3)},
|
||||||
|
{Number: big.NewInt(4)},
|
||||||
|
{
|
||||||
|
Number: big.NewInt(5),
|
||||||
|
Extra: extraBytes,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
newSnap, err := snap.apply(headers)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
if newSnap.Number != 5 {
|
||||||
|
t.Error("newSnapshot number should have last header number")
|
||||||
|
}
|
||||||
|
if newSnap.Hash != headers[3].Hash() {
|
||||||
|
t.Error("newSnapshot hash should equal the last header given")
|
||||||
|
}
|
||||||
|
if newSnap.Round != 10 {
|
||||||
|
t.Error("newSnapshot round number should also have last header round number")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestApplyWithWrongHeader(t *testing.T) {
|
||||||
|
t.Skip("apply has been temporary commented out")
|
||||||
|
snap := newSnapshot(nil, 1, common.Hash{}, utils.Round(1), nil, nil)
|
||||||
|
headers := []*types.Header{
|
||||||
|
{Number: big.NewInt(3)},
|
||||||
|
}
|
||||||
|
_, err := snap.apply(headers)
|
||||||
|
assert.Equal(t, err, utils.ErrInvalidChild)
|
||||||
|
|
||||||
|
snap = newSnapshot(nil, 1, common.Hash{}, utils.Round(1), nil, nil)
|
||||||
|
headers = []*types.Header{
|
||||||
|
{Number: big.NewInt(2)},
|
||||||
|
{Number: big.NewInt(4)},
|
||||||
|
}
|
||||||
|
_, err = snap.apply(headers)
|
||||||
|
assert.Equal(t, err, utils.ErrInvalidHeaderOrder)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should perform deep copy
|
||||||
|
func TestCopySnapshot(t *testing.T) {
|
||||||
|
masterNodes := []common.Address{
|
||||||
|
{4}, {3}, {2}, {1},
|
||||||
|
}
|
||||||
|
snap := newSnapshot(nil, 1, common.Hash{}, utils.Round(1), nil, masterNodes)
|
||||||
|
|
||||||
|
newSnapshot := snap.copy()
|
||||||
|
if newSnapshot == snap {
|
||||||
|
t.Error("should return given different memory address")
|
||||||
|
}
|
||||||
|
|
||||||
|
for node := range snap.MasterNodes {
|
||||||
|
if _, ok := newSnapshot.MasterNodes[node]; !ok {
|
||||||
|
t.Error("snapshot masternodes should copy to new object")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStoreLoadSnapshot(t *testing.T) {
|
||||||
|
snap := newSnapshot(nil, 1, common.Hash{0x1}, utils.Round(1), nil, nil)
|
||||||
|
dir, err := ioutil.TempDir("", "snapshot-test")
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("can't create temporary directory: %v", err))
|
||||||
|
}
|
||||||
|
db, err := leveldb.New(dir, 256, 0, "")
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("can't create temporary database: %v", err))
|
||||||
|
}
|
||||||
|
lddb := rawdb.NewDatabase(db)
|
||||||
|
|
||||||
|
err = storeSnapshot(snap, lddb)
|
||||||
|
if err != nil {
|
||||||
|
t.Error("store snapshot failed", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
restoredSnapshot, err := loadSnapshot(nil, lddb, snap.Hash)
|
||||||
|
if err != nil || restoredSnapshot.Hash != snap.Hash {
|
||||||
|
t.Error("load snapshot failed", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
package utils
|
package utils
|
||||||
|
|
||||||
import "errors"
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
// Various error messages to mark blocks invalid. These should be private to
|
// Various error messages to mark blocks invalid. These should be private to
|
||||||
// prevent engine specific errors from being referenced in the remainder of the
|
// prevent engine specific errors from being referenced in the remainder of the
|
||||||
|
|
@ -60,6 +63,9 @@ var (
|
||||||
// be modified via out-of-range or non-contiguous headers.
|
// be modified via out-of-range or non-contiguous headers.
|
||||||
ErrInvalidVotingChain = errors.New("invalid voting chain")
|
ErrInvalidVotingChain = errors.New("invalid voting chain")
|
||||||
|
|
||||||
|
ErrInvalidHeaderOrder = errors.New("invalid header order")
|
||||||
|
ErrInvalidChild = errors.New("invalid header child")
|
||||||
|
|
||||||
// errUnauthorized is returned if a header is signed by a non-authorized entity.
|
// errUnauthorized is returned if a header is signed by a non-authorized entity.
|
||||||
ErrUnauthorized = errors.New("unauthorized")
|
ErrUnauthorized = errors.New("unauthorized")
|
||||||
|
|
||||||
|
|
@ -72,3 +78,12 @@ var (
|
||||||
|
|
||||||
ErrInvalidCheckpointValidators = errors.New("invalid validators list on checkpoint block")
|
ErrInvalidCheckpointValidators = errors.New("invalid validators list on checkpoint block")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type ErrIncomingMessageRoundNotEqualCurrentRound struct {
|
||||||
|
IncomingRound Round
|
||||||
|
CurrentRound Round
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ErrIncomingMessageRoundNotEqualCurrentRound) Error() string {
|
||||||
|
return fmt.Sprintf("Timeout message round number: %v does not match currentRound: %v", e.IncomingRound, e.CurrentRound)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -73,7 +73,7 @@ type BlockInfo struct {
|
||||||
|
|
||||||
// Vote message in XDPoS 2.0
|
// Vote message in XDPoS 2.0
|
||||||
type Vote struct {
|
type Vote struct {
|
||||||
ProposedBlockInfo BlockInfo
|
ProposedBlockInfo *BlockInfo
|
||||||
Signature Signature
|
Signature Signature
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -91,7 +91,7 @@ type SyncInfo struct {
|
||||||
|
|
||||||
// Quorum Certificate struct in XDPoS 2.0
|
// Quorum Certificate struct in XDPoS 2.0
|
||||||
type QuorumCert struct {
|
type QuorumCert struct {
|
||||||
ProposedBlockInfo BlockInfo
|
ProposedBlockInfo *BlockInfo
|
||||||
Signatures []Signature
|
Signatures []Signature
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -105,7 +105,17 @@ type TimeoutCert struct {
|
||||||
// The version byte (consensus version) is the first byte in header's extra and it's only valid with value >= 2
|
// The version byte (consensus version) is the first byte in header's extra and it's only valid with value >= 2
|
||||||
type ExtraFields_v2 struct {
|
type ExtraFields_v2 struct {
|
||||||
Round Round
|
Round Round
|
||||||
QuorumCert QuorumCert
|
QuorumCert *QuorumCert
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encode XDPoS 2.0 extra fields into bytes
|
||||||
|
func (e *ExtraFields_v2) EncodeToBytes() ([]byte, error) {
|
||||||
|
bytes, err := rlp.EncodeToBytes(e)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
versionByte := []byte{2}
|
||||||
|
return append(versionByte, bytes...), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func rlpHash(x interface{}) (h common.Hash) {
|
func rlpHash(x interface{}) (h common.Hash) {
|
||||||
|
|
|
||||||
|
|
@ -10,10 +10,10 @@ import (
|
||||||
|
|
||||||
func toyExtraFields() *ExtraFields_v2 {
|
func toyExtraFields() *ExtraFields_v2 {
|
||||||
round := Round(307)
|
round := Round(307)
|
||||||
blockInfo := BlockInfo{Hash: common.BigToHash(big.NewInt(2047)), Round: round - 1, Number: big.NewInt(1)}
|
blockInfo := &BlockInfo{Hash: common.BigToHash(big.NewInt(2047)), Round: round - 1, Number: big.NewInt(1)}
|
||||||
signature := []byte{1, 2, 3, 4, 5, 6, 7, 8}
|
signature := []byte{1, 2, 3, 4, 5, 6, 7, 8}
|
||||||
signatures := []Signature{signature}
|
signatures := []Signature{signature}
|
||||||
quorumCert := QuorumCert{ProposedBlockInfo: blockInfo, Signatures: signatures}
|
quorumCert := &QuorumCert{ProposedBlockInfo: blockInfo, Signatures: signatures}
|
||||||
e := &ExtraFields_v2{Round: round, QuorumCert: quorumCert}
|
e := &ExtraFields_v2{Round: round, QuorumCert: quorumCert}
|
||||||
return e
|
return e
|
||||||
}
|
}
|
||||||
|
|
@ -35,14 +35,14 @@ func TestExtraFieldsEncodeDecode(t *testing.T) {
|
||||||
|
|
||||||
func TestHashAndSigHash(t *testing.T) {
|
func TestHashAndSigHash(t *testing.T) {
|
||||||
round := Round(307)
|
round := Round(307)
|
||||||
blockInfo1 := BlockInfo{Hash: common.BigToHash(big.NewInt(2047)), Round: round - 1, Number: big.NewInt(1)}
|
blockInfo1 := &BlockInfo{Hash: common.BigToHash(big.NewInt(2047)), Round: round - 1, Number: big.NewInt(1)}
|
||||||
blockInfo2 := BlockInfo{Hash: common.BigToHash(big.NewInt(4095)), Round: round - 1, Number: big.NewInt(1)}
|
blockInfo2 := &BlockInfo{Hash: common.BigToHash(big.NewInt(4095)), Round: round - 1, Number: big.NewInt(1)}
|
||||||
signature1 := []byte{1, 2, 3, 4, 5, 6, 7, 8}
|
signature1 := []byte{1, 2, 3, 4, 5, 6, 7, 8}
|
||||||
signature2 := []byte{1, 2, 3, 4, 5, 6, 7, 7}
|
signature2 := []byte{1, 2, 3, 4, 5, 6, 7, 7}
|
||||||
signatures1 := []Signature{signature1}
|
signatures1 := []Signature{signature1}
|
||||||
quorumCert1 := QuorumCert{ProposedBlockInfo: blockInfo1, Signatures: signatures1}
|
|
||||||
signatures2 := []Signature{signature2}
|
signatures2 := []Signature{signature2}
|
||||||
quorumCert2 := QuorumCert{ProposedBlockInfo: blockInfo1, Signatures: signatures2}
|
quorumCert1 := &QuorumCert{ProposedBlockInfo: blockInfo1, Signatures: signatures1}
|
||||||
|
quorumCert2 := &QuorumCert{ProposedBlockInfo: blockInfo1, Signatures: signatures2}
|
||||||
vote1 := Vote{ProposedBlockInfo: blockInfo1, Signature: signature1}
|
vote1 := Vote{ProposedBlockInfo: blockInfo1, Signature: signature1}
|
||||||
vote2 := Vote{ProposedBlockInfo: blockInfo1, Signature: signature2}
|
vote2 := Vote{ProposedBlockInfo: blockInfo1, Signature: signature2}
|
||||||
if vote1.Hash() == vote2.Hash() {
|
if vote1.Hash() == vote2.Hash() {
|
||||||
|
|
@ -53,12 +53,12 @@ func TestHashAndSigHash(t *testing.T) {
|
||||||
if timeout1.Hash() == timeout2.Hash() {
|
if timeout1.Hash() == timeout2.Hash() {
|
||||||
t.Fatalf("Hash of two timeouts shouldn't equal")
|
t.Fatalf("Hash of two timeouts shouldn't equal")
|
||||||
}
|
}
|
||||||
syncInfo1 := SyncInfo{HighestQuorumCert: &quorumCert1}
|
syncInfo1 := SyncInfo{HighestQuorumCert: quorumCert1}
|
||||||
syncInfo2 := SyncInfo{HighestQuorumCert: &quorumCert2}
|
syncInfo2 := SyncInfo{HighestQuorumCert: quorumCert2}
|
||||||
if syncInfo1.Hash() == syncInfo2.Hash() {
|
if syncInfo1.Hash() == syncInfo2.Hash() {
|
||||||
t.Fatalf("Hash of two sync info shouldn't equal")
|
t.Fatalf("Hash of two sync info shouldn't equal")
|
||||||
}
|
}
|
||||||
if VoteSigHash(&blockInfo1) == VoteSigHash(&blockInfo2) {
|
if VoteSigHash(blockInfo1) == VoteSigHash(blockInfo2) {
|
||||||
t.Fatalf("SigHash of two block info shouldn't equal")
|
t.Fatalf("SigHash of two block info shouldn't equal")
|
||||||
}
|
}
|
||||||
round2 := Round(999)
|
round2 := Round(999)
|
||||||
|
|
|
||||||
|
|
@ -10,10 +10,12 @@ import (
|
||||||
|
|
||||||
"github.com/XinFinOrg/XDPoSChain/common"
|
"github.com/XinFinOrg/XDPoSChain/common"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/crypto"
|
||||||
"github.com/XinFinOrg/XDPoSChain/crypto/sha3"
|
"github.com/XinFinOrg/XDPoSChain/crypto/sha3"
|
||||||
"github.com/XinFinOrg/XDPoSChain/log"
|
"github.com/XinFinOrg/XDPoSChain/log"
|
||||||
"github.com/XinFinOrg/XDPoSChain/params"
|
"github.com/XinFinOrg/XDPoSChain/params"
|
||||||
"github.com/XinFinOrg/XDPoSChain/rlp"
|
"github.com/XinFinOrg/XDPoSChain/rlp"
|
||||||
|
lru "github.com/hashicorp/golang-lru"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Position(list []common.Address, x common.Address) int {
|
func Position(list []common.Address, x common.Address) int {
|
||||||
|
|
@ -151,14 +153,33 @@ func SigHash(header *types.Header) (hash common.Hash) {
|
||||||
return hash
|
return hash
|
||||||
}
|
}
|
||||||
|
|
||||||
// Encode XDPoS 2.0 extra fields into bytes
|
func SigHashV2(header *types.Header) (hash common.Hash) {
|
||||||
func (e *ExtraFields_v2) EncodeToBytes() ([]byte, error) {
|
hasher := sha3.NewKeccak256()
|
||||||
bytes, err := rlp.EncodeToBytes(e)
|
|
||||||
|
err := rlp.Encode(hasher, []interface{}{
|
||||||
|
header.ParentHash,
|
||||||
|
header.UncleHash,
|
||||||
|
header.Coinbase,
|
||||||
|
header.Root,
|
||||||
|
header.TxHash,
|
||||||
|
header.ReceiptHash,
|
||||||
|
header.Bloom,
|
||||||
|
header.Difficulty,
|
||||||
|
header.Number,
|
||||||
|
header.GasLimit,
|
||||||
|
header.GasUsed,
|
||||||
|
header.Time,
|
||||||
|
header.Extra,
|
||||||
|
header.MixDigest,
|
||||||
|
header.Nonce,
|
||||||
|
header.Validators,
|
||||||
|
header.Penalties,
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
log.Debug("Fail to encode", err)
|
||||||
}
|
}
|
||||||
versionByte := []byte{2}
|
hasher.Sum(hash[:0])
|
||||||
return append(versionByte, bytes...), nil
|
return hash
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decode extra fields for consensus version >= 2 (XDPoS 2.0 and future versions)
|
// Decode extra fields for consensus version >= 2 (XDPoS 2.0 and future versions)
|
||||||
|
|
@ -174,4 +195,48 @@ func DecodeBytesExtraFields(b []byte, val interface{}) error {
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("consensus version %d is not defined", b[0])
|
return fmt.Errorf("consensus version %d is not defined", b[0])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ecrecover extracts the Ethereum account address from a signed header.
|
||||||
|
func Ecrecover(header *types.Header, sigcache *lru.ARCCache) (common.Address, error) {
|
||||||
|
// If the signature's already cached, return that
|
||||||
|
hash := header.Hash()
|
||||||
|
if address, known := sigcache.Get(hash); known {
|
||||||
|
return address.(common.Address), nil
|
||||||
|
}
|
||||||
|
// Retrieve the signature from the header extra-data
|
||||||
|
if len(header.Extra) < ExtraSeal {
|
||||||
|
return common.Address{}, ErrMissingSignature
|
||||||
|
}
|
||||||
|
signature := header.Extra[len(header.Extra)-ExtraSeal:]
|
||||||
|
|
||||||
|
// Recover the public key and the Ethereum address
|
||||||
|
pubkey, err := crypto.Ecrecover(SigHash(header).Bytes(), signature)
|
||||||
|
if err != nil {
|
||||||
|
return common.Address{}, err
|
||||||
|
}
|
||||||
|
var signer common.Address
|
||||||
|
copy(signer[:], crypto.Keccak256(pubkey[1:])[12:])
|
||||||
|
|
||||||
|
sigcache.Add(hash, signer)
|
||||||
|
return signer, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func EcrecoverV2(header *types.Header, sigcache *lru.ARCCache) (common.Address, error) {
|
||||||
|
// If the signature's already cached, return that
|
||||||
|
hash := header.Hash()
|
||||||
|
if address, known := sigcache.Get(hash); known {
|
||||||
|
return address.(common.Address), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recover the public key and the Ethereum address
|
||||||
|
pubkey, err := crypto.Ecrecover(SigHashV2(header).Bytes(), header.Validator)
|
||||||
|
if err != nil {
|
||||||
|
return common.Address{}, err
|
||||||
|
}
|
||||||
|
var signer common.Address
|
||||||
|
copy(signer[:], crypto.Keccak256(pubkey[1:])[12:])
|
||||||
|
|
||||||
|
sigcache.Add(hash, signer)
|
||||||
|
return signer, nil
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -38,4 +38,6 @@ var (
|
||||||
ErrFailValidatorSignature = errors.New("missing validator in header")
|
ErrFailValidatorSignature = errors.New("missing validator in header")
|
||||||
|
|
||||||
ErrNoValidatorSignature = errors.New("no validator in header")
|
ErrNoValidatorSignature = errors.New("no validator in header")
|
||||||
|
|
||||||
|
ErrNotReadyToPropose = errors.New("not ready to propose, QC is not ready")
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,19 @@
|
||||||
package consensus
|
package tests
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math/big"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/common"
|
||||||
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS"
|
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||||
"github.com/XinFinOrg/XDPoSChain/params"
|
"github.com/XinFinOrg/XDPoSChain/params"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestAdaptorShouldGetAuthorForDifferentConsensusVersion(t *testing.T) {
|
func TestAdaptorShouldGetAuthorForDifferentConsensusVersion(t *testing.T) {
|
||||||
blockchain, _, currentBlock, _ := PrepareXDCTestBlockChain(t, 10, params.TestXDPoSMockChainConfigWithV2Engine)
|
blockchain, backend, currentBlock, _ := PrepareXDCTestBlockChainForV2Engine(t, 10, params.TestXDPoSMockChainConfigWithV2Engine)
|
||||||
adaptor := blockchain.Engine().(*XDPoS.XDPoS)
|
adaptor := blockchain.Engine().(*XDPoS.XDPoS)
|
||||||
|
|
||||||
addressFromAdaptor, errorAdaptor := adaptor.Author(currentBlock.Header())
|
addressFromAdaptor, errorAdaptor := adaptor.Author(currentBlock.Header())
|
||||||
|
|
@ -26,9 +29,17 @@ func TestAdaptorShouldGetAuthorForDifferentConsensusVersion(t *testing.T) {
|
||||||
|
|
||||||
// Insert one more block to make it above 10, which means now we are on v2 of consensus engine
|
// Insert one more block to make it above 10, which means now we are on v2 of consensus engine
|
||||||
// Insert block 11
|
// Insert block 11
|
||||||
|
|
||||||
blockCoinBase := fmt.Sprintf("0x111000000000000000000000000000000%03d", 11)
|
blockCoinBase := fmt.Sprintf("0x111000000000000000000000000000000%03d", 11)
|
||||||
merkleRoot := "35999dded35e8db12de7e6c1471eb9670c162eec616ecebbaf4fddd4676fb930"
|
merkleRoot := "35999dded35e8db12de7e6c1471eb9670c162eec616ecebbaf4fddd4676fb930"
|
||||||
block11, err := insertBlock(blockchain, 11, blockCoinBase, currentBlock, merkleRoot, nil, 1)
|
header := &types.Header{
|
||||||
|
Root: common.HexToHash(merkleRoot),
|
||||||
|
Number: big.NewInt(int64(11)),
|
||||||
|
ParentHash: currentBlock.Hash(),
|
||||||
|
Coinbase: common.HexToAddress(blockCoinBase),
|
||||||
|
}
|
||||||
|
generateSignature(backend, header)
|
||||||
|
block11, err := insertBlock(blockchain, header)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package consensus
|
package tests
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/common"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||||
"github.com/XinFinOrg/XDPoSChain/params"
|
"github.com/XinFinOrg/XDPoSChain/params"
|
||||||
)
|
)
|
||||||
|
|
@ -26,7 +27,13 @@ func TestNotUpdateSignerListIfNotOnGapBlock(t *testing.T) {
|
||||||
|
|
||||||
//Get from block validator error message
|
//Get from block validator error message
|
||||||
merkleRoot := "46234e9cd7e85a267f7f0435b15256a794a2f6d65cc98cdbd21dcd10a01d9772"
|
merkleRoot := "46234e9cd7e85a267f7f0435b15256a794a2f6d65cc98cdbd21dcd10a01d9772"
|
||||||
blockA, err := insertBlockTxs(blockchain, 401, blockCoinbaseA, parentBlock, []*types.Transaction{tx}, merkleRoot, 1)
|
header := &types.Header{
|
||||||
|
Root: common.HexToHash(merkleRoot),
|
||||||
|
Number: big.NewInt(int64(401)),
|
||||||
|
ParentHash: parentBlock.Hash(),
|
||||||
|
Coinbase: common.HexToAddress(blockCoinbaseA),
|
||||||
|
}
|
||||||
|
blockA, err := insertBlockTxs(blockchain, header, []*types.Transaction{tx})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
@ -54,7 +61,13 @@ func TestNotChangeSingerListIfNothingProposedOrVoted(t *testing.T) {
|
||||||
// Insert block 450
|
// Insert block 450
|
||||||
blockCoinBase := fmt.Sprintf("0x111000000000000000000000000000000%03d", 450)
|
blockCoinBase := fmt.Sprintf("0x111000000000000000000000000000000%03d", 450)
|
||||||
merkleRoot := "35999dded35e8db12de7e6c1471eb9670c162eec616ecebbaf4fddd4676fb930"
|
merkleRoot := "35999dded35e8db12de7e6c1471eb9670c162eec616ecebbaf4fddd4676fb930"
|
||||||
block, err := insertBlock(blockchain, 450, blockCoinBase, parentBlock, merkleRoot, nil, 1)
|
header := &types.Header{
|
||||||
|
Root: common.HexToHash(merkleRoot),
|
||||||
|
Number: big.NewInt(int64(450)),
|
||||||
|
ParentHash: parentBlock.Hash(),
|
||||||
|
Coinbase: common.HexToAddress(blockCoinBase),
|
||||||
|
}
|
||||||
|
block, err := insertBlock(blockchain, header)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
@ -89,7 +102,13 @@ func TestUpdateSignerListIfVotedBeforeGap(t *testing.T) {
|
||||||
|
|
||||||
//Get from block validator error message
|
//Get from block validator error message
|
||||||
merkleRoot := "46234e9cd7e85a267f7f0435b15256a794a2f6d65cc98cdbd21dcd10a01d9772"
|
merkleRoot := "46234e9cd7e85a267f7f0435b15256a794a2f6d65cc98cdbd21dcd10a01d9772"
|
||||||
block449, err := insertBlockTxs(blockchain, 449, blockCoinbaseA, parentBlock, []*types.Transaction{tx}, merkleRoot, 1)
|
header := &types.Header{
|
||||||
|
Root: common.HexToHash(merkleRoot),
|
||||||
|
Number: big.NewInt(int64(449)),
|
||||||
|
ParentHash: parentBlock.Hash(),
|
||||||
|
Coinbase: common.HexToAddress(blockCoinbaseA),
|
||||||
|
}
|
||||||
|
block449, err := insertBlockTxs(blockchain, header, []*types.Transaction{tx})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
@ -113,7 +132,13 @@ func TestUpdateSignerListIfVotedBeforeGap(t *testing.T) {
|
||||||
// Now, let's mine another block to trigger the GAP block signerList update
|
// Now, let's mine another block to trigger the GAP block signerList update
|
||||||
block450CoinbaseAddress := "0xaaa0000000000000000000000000000000000450"
|
block450CoinbaseAddress := "0xaaa0000000000000000000000000000000000450"
|
||||||
merkleRoot = "46234e9cd7e85a267f7f0435b15256a794a2f6d65cc98cdbd21dcd10a01d9772"
|
merkleRoot = "46234e9cd7e85a267f7f0435b15256a794a2f6d65cc98cdbd21dcd10a01d9772"
|
||||||
block450, err := insertBlock(blockchain, 450, block450CoinbaseAddress, parentBlock, merkleRoot, nil, 1)
|
header = &types.Header{
|
||||||
|
Root: common.HexToHash(merkleRoot),
|
||||||
|
Number: big.NewInt(int64(450)),
|
||||||
|
ParentHash: parentBlock.Hash(),
|
||||||
|
Coinbase: common.HexToAddress(block450CoinbaseAddress),
|
||||||
|
}
|
||||||
|
block450, err := insertBlock(blockchain, header)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
@ -147,7 +172,13 @@ func TestCallUpdateM1WithSmartContractTranscation(t *testing.T) {
|
||||||
|
|
||||||
//Get from block validator error message
|
//Get from block validator error message
|
||||||
merkleRoot := "46234e9cd7e85a267f7f0435b15256a794a2f6d65cc98cdbd21dcd10a01d9772"
|
merkleRoot := "46234e9cd7e85a267f7f0435b15256a794a2f6d65cc98cdbd21dcd10a01d9772"
|
||||||
blockA, err := insertBlockTxs(blockchain, 450, blockCoinbaseA, currentBlock, []*types.Transaction{tx}, merkleRoot, 1)
|
header := &types.Header{
|
||||||
|
Root: common.HexToHash(merkleRoot),
|
||||||
|
Number: big.NewInt(int64(450)),
|
||||||
|
ParentHash: currentBlock.Hash(),
|
||||||
|
Coinbase: common.HexToAddress(blockCoinbaseA),
|
||||||
|
}
|
||||||
|
blockA, err := insertBlockTxs(blockchain, header, []*types.Transaction{tx})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
@ -189,7 +220,13 @@ func TestCallUpdateM1WhenForkedBlockBackToMainChain(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
merkleRoot := "46234e9cd7e85a267f7f0435b15256a794a2f6d65cc98cdbd21dcd10a01d9772"
|
merkleRoot := "46234e9cd7e85a267f7f0435b15256a794a2f6d65cc98cdbd21dcd10a01d9772"
|
||||||
blockA, err := insertBlockTxs(blockchain, 450, blockCoinbaseA, currentBlock, []*types.Transaction{tx}, merkleRoot, 1)
|
header := &types.Header{
|
||||||
|
Root: common.HexToHash(merkleRoot),
|
||||||
|
Number: big.NewInt(int64(450)),
|
||||||
|
ParentHash: currentBlock.Hash(),
|
||||||
|
Coinbase: common.HexToAddress(blockCoinbaseA),
|
||||||
|
}
|
||||||
|
blockA, err := insertBlockTxs(blockchain, header, []*types.Transaction{tx})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
@ -217,7 +254,13 @@ func TestCallUpdateM1WhenForkedBlockBackToMainChain(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
merkleRoot = "068dfa09d7b4093441c0cc4d9807a71bc586f6101c072d939b214c21cd136eb3"
|
merkleRoot = "068dfa09d7b4093441c0cc4d9807a71bc586f6101c072d939b214c21cd136eb3"
|
||||||
block450B, err := insertBlockTxs(blockchain, 450, blockCoinBase450B, currentBlock, []*types.Transaction{tx}, merkleRoot, 1)
|
header = &types.Header{
|
||||||
|
Root: common.HexToHash(merkleRoot),
|
||||||
|
Number: big.NewInt(int64(450)),
|
||||||
|
ParentHash: currentBlock.Hash(),
|
||||||
|
Coinbase: common.HexToAddress(blockCoinBase450B),
|
||||||
|
}
|
||||||
|
block450B, err := insertBlockTxs(blockchain, header, []*types.Transaction{tx})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
@ -240,7 +283,13 @@ func TestCallUpdateM1WhenForkedBlockBackToMainChain(t *testing.T) {
|
||||||
|
|
||||||
blockCoinBase451B := "0xbbb0000000000000000000000000000000000451"
|
blockCoinBase451B := "0xbbb0000000000000000000000000000000000451"
|
||||||
merkleRoot = "068dfa09d7b4093441c0cc4d9807a71bc586f6101c072d939b214c21cd136eb3"
|
merkleRoot = "068dfa09d7b4093441c0cc4d9807a71bc586f6101c072d939b214c21cd136eb3"
|
||||||
block451B, err := insertBlock(blockchain, 451, blockCoinBase451B, block450B, merkleRoot, nil, 1)
|
header = &types.Header{
|
||||||
|
Root: common.HexToHash(merkleRoot),
|
||||||
|
Number: big.NewInt(int64(451)),
|
||||||
|
ParentHash: block450B.Hash(),
|
||||||
|
Coinbase: common.HexToAddress(blockCoinBase451B),
|
||||||
|
}
|
||||||
|
block451B, err := insertBlock(blockchain, header)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
|
@ -316,7 +365,13 @@ func TestStatesShouldBeUpdatedWhenForkedBlockBecameMainChainAtGapBlock(t *testin
|
||||||
transferTransaction := transferTx(t, acc1Addr, 999)
|
transferTransaction := transferTx(t, acc1Addr, 999)
|
||||||
|
|
||||||
merkleRoot := "ea465415b60d88429f181fec9fae67c0f19cbf5a4fa10971d96d4faa57d96ffa"
|
merkleRoot := "ea465415b60d88429f181fec9fae67c0f19cbf5a4fa10971d96d4faa57d96ffa"
|
||||||
blockA, err := insertBlockTxs(blockchain, 450, blockCoinbaseA, parentBlock, []*types.Transaction{tx, transferTransaction}, merkleRoot, 1)
|
header := &types.Header{
|
||||||
|
Root: common.HexToHash(merkleRoot),
|
||||||
|
Number: big.NewInt(int64(450)),
|
||||||
|
ParentHash: parentBlock.Hash(),
|
||||||
|
Coinbase: common.HexToAddress(blockCoinbaseA),
|
||||||
|
}
|
||||||
|
blockA, err := insertBlockTxs(blockchain, header, []*types.Transaction{tx, transferTransaction})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
@ -351,7 +406,13 @@ func TestStatesShouldBeUpdatedWhenForkedBlockBecameMainChainAtGapBlock(t *testin
|
||||||
transferTransaction = transferTx(t, acc1Addr, 888)
|
transferTransaction = transferTx(t, acc1Addr, 888)
|
||||||
|
|
||||||
merkleRoot = "184edaddeafc2404248f896ae46be503ae68949896c8eb6b6ad43695581e5022"
|
merkleRoot = "184edaddeafc2404248f896ae46be503ae68949896c8eb6b6ad43695581e5022"
|
||||||
block450B, err := insertBlockTxs(blockchain, 450, blockCoinBase450B, parentBlock, []*types.Transaction{tx, transferTransaction}, merkleRoot, 1)
|
header = &types.Header{
|
||||||
|
Root: common.HexToHash(merkleRoot),
|
||||||
|
Number: big.NewInt(int64(450)),
|
||||||
|
ParentHash: parentBlock.Hash(),
|
||||||
|
Coinbase: common.HexToAddress(blockCoinBase450B),
|
||||||
|
}
|
||||||
|
block450B, err := insertBlockTxs(blockchain, header, []*types.Transaction{tx, transferTransaction})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
@ -378,7 +439,13 @@ func TestStatesShouldBeUpdatedWhenForkedBlockBecameMainChainAtGapBlock(t *testin
|
||||||
|
|
||||||
blockCoinBase451B := "0xbbb0000000000000000000000000000000000451"
|
blockCoinBase451B := "0xbbb0000000000000000000000000000000000451"
|
||||||
merkleRoot = "184edaddeafc2404248f896ae46be503ae68949896c8eb6b6ad43695581e5022"
|
merkleRoot = "184edaddeafc2404248f896ae46be503ae68949896c8eb6b6ad43695581e5022"
|
||||||
block451B, err := insertBlock(blockchain, 451, blockCoinBase451B, block450B, merkleRoot, nil, 1)
|
header = &types.Header{
|
||||||
|
Root: common.HexToHash(merkleRoot),
|
||||||
|
Number: big.NewInt(int64(451)),
|
||||||
|
ParentHash: block450B.Hash(),
|
||||||
|
Coinbase: common.HexToAddress(blockCoinBase451B),
|
||||||
|
}
|
||||||
|
block451B, err := insertBlock(blockchain, header)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
|
@ -440,7 +507,13 @@ func TestVoteShouldNotBeAffectedByFork(t *testing.T) {
|
||||||
// Insert normal blocks 450 A
|
// Insert normal blocks 450 A
|
||||||
blockCoinBase450A := "0xaaa0000000000000000000000000000000000450"
|
blockCoinBase450A := "0xaaa0000000000000000000000000000000000450"
|
||||||
merkleRoot := "35999dded35e8db12de7e6c1471eb9670c162eec616ecebbaf4fddd4676fb930"
|
merkleRoot := "35999dded35e8db12de7e6c1471eb9670c162eec616ecebbaf4fddd4676fb930"
|
||||||
block450A, err := insertBlock(blockchain, 450, blockCoinBase450A, parentBlock, merkleRoot, nil, 1)
|
header := &types.Header{
|
||||||
|
Root: common.HexToHash(merkleRoot),
|
||||||
|
Number: big.NewInt(int64(450)),
|
||||||
|
ParentHash: parentBlock.Hash(),
|
||||||
|
Coinbase: common.HexToAddress(blockCoinBase450A),
|
||||||
|
}
|
||||||
|
block450A, err := insertBlock(blockchain, header)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
@ -453,7 +526,13 @@ func TestVoteShouldNotBeAffectedByFork(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
merkleRoot = "46234e9cd7e85a267f7f0435b15256a794a2f6d65cc98cdbd21dcd10a01d9772"
|
merkleRoot = "46234e9cd7e85a267f7f0435b15256a794a2f6d65cc98cdbd21dcd10a01d9772"
|
||||||
block451A, err := insertBlockTxs(blockchain, 451, blockCoinbase451A, block450A, []*types.Transaction{tx}, merkleRoot, 1)
|
header = &types.Header{
|
||||||
|
Root: common.HexToHash(merkleRoot),
|
||||||
|
Number: big.NewInt(int64(451)),
|
||||||
|
ParentHash: block450A.Hash(),
|
||||||
|
Coinbase: common.HexToAddress(blockCoinbase451A),
|
||||||
|
}
|
||||||
|
block451A, err := insertBlockTxs(blockchain, header, []*types.Transaction{tx})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
@ -476,21 +555,39 @@ func TestVoteShouldNotBeAffectedByFork(t *testing.T) {
|
||||||
// Insert forked Block 450 B
|
// Insert forked Block 450 B
|
||||||
blockCoinBase450B := "0xbbb0000000000000000000000000000000000450"
|
blockCoinBase450B := "0xbbb0000000000000000000000000000000000450"
|
||||||
merkleRoot = "35999dded35e8db12de7e6c1471eb9670c162eec616ecebbaf4fddd4676fb930"
|
merkleRoot = "35999dded35e8db12de7e6c1471eb9670c162eec616ecebbaf4fddd4676fb930"
|
||||||
block450B, err := insertBlock(blockchain, 450, blockCoinBase450B, parentBlock, merkleRoot, nil, 1)
|
header = &types.Header{
|
||||||
|
Root: common.HexToHash(merkleRoot),
|
||||||
|
Number: big.NewInt(int64(450)),
|
||||||
|
ParentHash: parentBlock.Hash(),
|
||||||
|
Coinbase: common.HexToAddress(blockCoinBase450B),
|
||||||
|
}
|
||||||
|
block450B, err := insertBlock(blockchain, header)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
blockCoinBase451B := "0xbbb0000000000000000000000000000000000451"
|
blockCoinBase451B := "0xbbb0000000000000000000000000000000000451"
|
||||||
merkleRoot = "35999dded35e8db12de7e6c1471eb9670c162eec616ecebbaf4fddd4676fb930"
|
merkleRoot = "35999dded35e8db12de7e6c1471eb9670c162eec616ecebbaf4fddd4676fb930"
|
||||||
block451B, err := insertBlock(blockchain, 451, blockCoinBase451B, block450B, merkleRoot, nil, 1)
|
header = &types.Header{
|
||||||
|
Root: common.HexToHash(merkleRoot),
|
||||||
|
Number: big.NewInt(int64(451)),
|
||||||
|
ParentHash: block450B.Hash(),
|
||||||
|
Coinbase: common.HexToAddress(blockCoinBase451B),
|
||||||
|
}
|
||||||
|
block451B, err := insertBlock(blockchain, header)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
blockCoinBase452B := "0xbbb0000000000000000000000000000000000452"
|
blockCoinBase452B := "0xbbb0000000000000000000000000000000000452"
|
||||||
merkleRoot = "35999dded35e8db12de7e6c1471eb9670c162eec616ecebbaf4fddd4676fb930"
|
merkleRoot = "35999dded35e8db12de7e6c1471eb9670c162eec616ecebbaf4fddd4676fb930"
|
||||||
block452B, err := insertBlock(blockchain, 452, blockCoinBase452B, block451B, merkleRoot, nil, 1)
|
header = &types.Header{
|
||||||
|
Root: common.HexToHash(merkleRoot),
|
||||||
|
Number: big.NewInt(int64(452)),
|
||||||
|
ParentHash: block451B.Hash(),
|
||||||
|
Coinbase: common.HexToAddress(blockCoinBase452B),
|
||||||
|
}
|
||||||
|
block452B, err := insertBlock(blockchain, header)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
@ -1,9 +1,10 @@
|
||||||
package consensus
|
package tests
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"math/big"
|
"math/big"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/common"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||||
"github.com/XinFinOrg/XDPoSChain/params"
|
"github.com/XinFinOrg/XDPoSChain/params"
|
||||||
)
|
)
|
||||||
|
|
@ -39,7 +40,15 @@ func TestRaceConditionOnBlockchainReadAndWrite(t *testing.T) {
|
||||||
transferTransaction := transferTx(t, acc1Addr, 999)
|
transferTransaction := transferTx(t, acc1Addr, 999)
|
||||||
|
|
||||||
merkleRoot := "ea465415b60d88429f181fec9fae67c0f19cbf5a4fa10971d96d4faa57d96ffa"
|
merkleRoot := "ea465415b60d88429f181fec9fae67c0f19cbf5a4fa10971d96d4faa57d96ffa"
|
||||||
blockA, err := insertBlockTxs(blockchain, 450, blockCoinbaseA, parentBlock, []*types.Transaction{tx, transferTransaction}, merkleRoot, 1)
|
|
||||||
|
header := &types.Header{
|
||||||
|
Root: common.HexToHash(merkleRoot),
|
||||||
|
Number: big.NewInt(int64(450)),
|
||||||
|
ParentHash: parentBlock.Hash(),
|
||||||
|
Coinbase: common.HexToAddress(blockCoinbaseA),
|
||||||
|
}
|
||||||
|
|
||||||
|
blockA, err := insertBlockTxs(blockchain, header, []*types.Transaction{tx, transferTransaction})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
@ -74,7 +83,16 @@ func TestRaceConditionOnBlockchainReadAndWrite(t *testing.T) {
|
||||||
transferTransaction = transferTx(t, acc1Addr, 888)
|
transferTransaction = transferTx(t, acc1Addr, 888)
|
||||||
|
|
||||||
merkleRoot = "184edaddeafc2404248f896ae46be503ae68949896c8eb6b6ad43695581e5022"
|
merkleRoot = "184edaddeafc2404248f896ae46be503ae68949896c8eb6b6ad43695581e5022"
|
||||||
block450B, err := insertBlockTxs(blockchain, 450, blockCoinBase450B, parentBlock, []*types.Transaction{tx, transferTransaction}, merkleRoot, 2)
|
|
||||||
|
header = &types.Header{
|
||||||
|
Root: common.HexToHash(merkleRoot),
|
||||||
|
Number: big.NewInt(int64(450)),
|
||||||
|
ParentHash: parentBlock.Hash(),
|
||||||
|
Coinbase: common.HexToAddress(blockCoinBase450B),
|
||||||
|
Difficulty: big.NewInt(2),
|
||||||
|
}
|
||||||
|
|
||||||
|
block450B, err := insertBlockTxs(blockchain, header, []*types.Transaction{tx, transferTransaction})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
@ -104,7 +122,14 @@ func TestRaceConditionOnBlockchainReadAndWrite(t *testing.T) {
|
||||||
|
|
||||||
blockCoinBase451B := "0xbbb0000000000000000000000000000000000451"
|
blockCoinBase451B := "0xbbb0000000000000000000000000000000000451"
|
||||||
merkleRoot = "184edaddeafc2404248f896ae46be503ae68949896c8eb6b6ad43695581e5022"
|
merkleRoot = "184edaddeafc2404248f896ae46be503ae68949896c8eb6b6ad43695581e5022"
|
||||||
block451B, err := insertBlock(blockchain, 451, blockCoinBase451B, block450B, merkleRoot, nil, 3)
|
header = &types.Header{
|
||||||
|
Root: common.HexToHash(merkleRoot),
|
||||||
|
Number: big.NewInt(int64(451)),
|
||||||
|
ParentHash: block450B.Hash(),
|
||||||
|
Coinbase: common.HexToAddress(blockCoinBase451B),
|
||||||
|
Difficulty: big.NewInt(3),
|
||||||
|
}
|
||||||
|
block451B, err := insertBlock(blockchain, header)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package consensus
|
package tests
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
@ -10,7 +10,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestCountdownTimeoutToSendTimeoutMessage(t *testing.T) {
|
func TestCountdownTimeoutToSendTimeoutMessage(t *testing.T) {
|
||||||
blockchain, _, _, _ := PrepareXDCTestBlockChain(t, 11, params.TestXDPoSMockChainConfigWithV2Engine)
|
blockchain, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 11, params.TestXDPoSMockChainConfigWithV2Engine)
|
||||||
engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2
|
engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2
|
||||||
|
|
||||||
engineV2.SetNewRoundFaker(utils.Round(1), true)
|
engineV2.SetNewRoundFaker(utils.Round(1), true)
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
package consensus
|
package tests
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"math/big"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS"
|
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS"
|
||||||
|
|
@ -24,20 +23,14 @@ func TestProposedBlockMessageHandlerSuccessfullyGenerateVote(t *testing.T) {
|
||||||
t.Fatal("Fail to decode extra data", err)
|
t.Fatal("Fail to decode extra data", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
proposedBlockInfo := &utils.BlockInfo{
|
err = engineV2.ProposedBlockHandler(blockchain, currentBlock.Header())
|
||||||
Hash: currentBlock.Hash(),
|
|
||||||
Round: utils.Round(11),
|
|
||||||
Number: big.NewInt(11),
|
|
||||||
}
|
|
||||||
|
|
||||||
err = engineV2.ProposedBlockHandler(blockchain, proposedBlockInfo, &extraField.QuorumCert)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal("Fail propose proposedBlock handler", err)
|
t.Fatal("Fail propose proposedBlock handler", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
voteMsg := <-engineV2.BroadcastCh
|
voteMsg := <-engineV2.BroadcastCh
|
||||||
assert.NotNil(t, voteMsg)
|
assert.NotNil(t, voteMsg)
|
||||||
assert.Equal(t, proposedBlockInfo.Hash, voteMsg.(*utils.Vote).ProposedBlockInfo.Hash)
|
assert.Equal(t, currentBlock.Hash(), voteMsg.(*utils.Vote).ProposedBlockInfo.Hash)
|
||||||
|
|
||||||
round, _, highestQC := engineV2.GetProperties()
|
round, _, highestQC := engineV2.GetProperties()
|
||||||
// Shoud not trigger setNewRound
|
// Shoud not trigger setNewRound
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package consensus
|
package tests
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
|
@ -245,8 +245,13 @@ func PrepareXDCTestBlockChain(t *testing.T, numOfBlocks int, chainConfig *params
|
||||||
for i := 1; i <= numOfBlocks; i++ {
|
for i := 1; i <= numOfBlocks; i++ {
|
||||||
blockCoinBase := fmt.Sprintf("0x111000000000000000000000000000000%03d", i)
|
blockCoinBase := fmt.Sprintf("0x111000000000000000000000000000000%03d", i)
|
||||||
merkleRoot := "35999dded35e8db12de7e6c1471eb9670c162eec616ecebbaf4fddd4676fb930"
|
merkleRoot := "35999dded35e8db12de7e6c1471eb9670c162eec616ecebbaf4fddd4676fb930"
|
||||||
|
header := &types.Header{
|
||||||
block, err := insertBlock(blockchain, i, blockCoinBase, currentBlock, merkleRoot, nil, 1)
|
Root: common.HexToHash(merkleRoot),
|
||||||
|
Number: big.NewInt(int64(i)),
|
||||||
|
ParentHash: currentBlock.Hash(),
|
||||||
|
Coinbase: common.HexToAddress(blockCoinBase),
|
||||||
|
}
|
||||||
|
block, err := insertBlock(blockchain, header)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
@ -283,19 +288,19 @@ func PrepareXDCTestBlockChainForV2Engine(t *testing.T, numOfBlocks int, chainCon
|
||||||
merkleRoot := "35999dded35e8db12de7e6c1471eb9670c162eec616ecebbaf4fddd4676fb930"
|
merkleRoot := "35999dded35e8db12de7e6c1471eb9670c162eec616ecebbaf4fddd4676fb930"
|
||||||
|
|
||||||
// Build engine v2 compatible extra data field
|
// Build engine v2 compatible extra data field
|
||||||
proposedBlockInfo := utils.BlockInfo{
|
proposedBlockInfo := &utils.BlockInfo{
|
||||||
Hash: currentBlock.Hash(),
|
Hash: currentBlock.Hash(),
|
||||||
Round: utils.Round(i - 1),
|
Round: utils.Round(i - 1),
|
||||||
Number: big.NewInt(int64(i - 1)),
|
Number: big.NewInt(int64(i - 1)),
|
||||||
}
|
}
|
||||||
// Genrate QC
|
// Genrate QC
|
||||||
signedHash, err := signFn(accounts.Account{Address: signer}, utils.VoteSigHash(&proposedBlockInfo).Bytes())
|
signedHash, err := signFn(accounts.Account{Address: signer}, utils.VoteSigHash(proposedBlockInfo).Bytes())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(fmt.Errorf("Error generate QC by creating signedHash: %v", err))
|
panic(fmt.Errorf("Error generate QC by creating signedHash: %v", err))
|
||||||
}
|
}
|
||||||
var signatures []utils.Signature
|
var signatures []utils.Signature
|
||||||
signatures = append(signatures, signedHash)
|
signatures = append(signatures, signedHash)
|
||||||
quorumCert := utils.QuorumCert{
|
quorumCert := &utils.QuorumCert{
|
||||||
ProposedBlockInfo: proposedBlockInfo,
|
ProposedBlockInfo: proposedBlockInfo,
|
||||||
Signatures: signatures,
|
Signatures: signatures,
|
||||||
}
|
}
|
||||||
|
|
@ -309,7 +314,15 @@ func PrepareXDCTestBlockChainForV2Engine(t *testing.T, numOfBlocks int, chainCon
|
||||||
panic(fmt.Errorf("Error encode extra into bytes: %v", err))
|
panic(fmt.Errorf("Error encode extra into bytes: %v", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
block, err := insertBlock(blockchain, i, blockCoinBase, currentBlock, merkleRoot, extraInBytes, 1)
|
header := &types.Header{
|
||||||
|
Root: common.HexToHash(merkleRoot),
|
||||||
|
Number: big.NewInt(int64(i)),
|
||||||
|
ParentHash: currentBlock.Hash(),
|
||||||
|
Coinbase: common.HexToAddress(blockCoinBase),
|
||||||
|
Extra: extraInBytes,
|
||||||
|
Validator: signedHash,
|
||||||
|
}
|
||||||
|
block, err := insertBlock(blockchain, header)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
@ -324,16 +337,27 @@ func PrepareXDCTestBlockChainForV2Engine(t *testing.T, numOfBlocks int, chainCon
|
||||||
return blockchain, backend, currentBlock, signer
|
return blockchain, backend, currentBlock, signer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func generateSignature(backend *backends.SimulatedBackend, header *types.Header) error {
|
||||||
|
signer, signFn, err := backends.SimulateWalletAddressAndSignFn()
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Errorf("Error while creating simulated wallet for generating singer address and signer fn: %v", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
signature, err := signFn(accounts.Account{Address: signer}, utils.SigHashV2(header).Bytes())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
header.Validator = signature
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// insert Block without transcation attached
|
// insert Block without transcation attached
|
||||||
func insertBlock(blockchain *BlockChain, blockNum int, blockCoinBase string, parentBlock *types.Block, root string, customExtra []byte, difficulty int64) (*types.Block, error) {
|
func insertBlock(blockchain *BlockChain, header *types.Header) (*types.Block, error) {
|
||||||
|
header.ReceiptHash = common.HexToHash("0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")
|
||||||
block, err := createXDPoSTestBlock(
|
block, err := createXDPoSTestBlock(
|
||||||
blockchain,
|
blockchain,
|
||||||
parentBlock.Hash().Hex(),
|
header,
|
||||||
blockCoinBase, blockNum, nil,
|
nil,
|
||||||
"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
|
|
||||||
common.HexToHash(root),
|
|
||||||
customExtra,
|
|
||||||
difficulty,
|
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -347,15 +371,20 @@ func insertBlock(blockchain *BlockChain, blockNum int, blockCoinBase string, par
|
||||||
}
|
}
|
||||||
|
|
||||||
// insert Block with transcation attached
|
// insert Block with transcation attached
|
||||||
func insertBlockTxs(blockchain *BlockChain, blockNum int, blockCoinBase string, parentBlock *types.Block, txs []*types.Transaction, root string, difficulty int64) (*types.Block, error) {
|
func insertBlockTxs(blockchain *BlockChain, header *types.Header, txs []*types.Transaction) (*types.Block, error) {
|
||||||
|
/*
|
||||||
|
header := types.Header{
|
||||||
|
Root: common.HexToHash(root),
|
||||||
|
Number: big.NewInt(int64(blockNum)),
|
||||||
|
ParentHash: parentBlock.Hash(),
|
||||||
|
Coinbase: common.HexToAddress(blockCoinBase),
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
header.ReceiptHash = common.HexToHash("0x9319777b782ba2c83a33c995481ff894ac96d9a92a1963091346a3e1e386705c")
|
||||||
block, err := createXDPoSTestBlock(
|
block, err := createXDPoSTestBlock(
|
||||||
blockchain,
|
blockchain,
|
||||||
parentBlock.Hash().Hex(),
|
header,
|
||||||
blockCoinBase, blockNum, txs,
|
txs,
|
||||||
"0x9319777b782ba2c83a33c995481ff894ac96d9a92a1963091346a3e1e386705c",
|
|
||||||
common.HexToHash(root),
|
|
||||||
nil,
|
|
||||||
difficulty,
|
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -368,35 +397,56 @@ func insertBlockTxs(blockchain *BlockChain, blockNum int, blockCoinBase string,
|
||||||
return block, nil
|
return block, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func createXDPoSTestBlock(bc *BlockChain, parentHash, coinbase string, number int, txs []*types.Transaction, receiptHash string, root common.Hash, customExtra []byte, difficulty int64) (*types.Block, error) {
|
//func createXDPoSTestBlock(bc *BlockChain, parentHash, coinbase string, number int, txs []*types.Transaction, receiptHash string, root common.Hash, customExtra []byte, signer common.Address) (*types.Block, error) {
|
||||||
if customExtra == nil {
|
func createXDPoSTestBlock(bc *BlockChain, customHeader *types.Header, txs []*types.Transaction) (*types.Block, error) {
|
||||||
|
if customHeader.Extra == nil {
|
||||||
extraSubstring := "d7830100018358444388676f312e31342e31856c696e75780000000000000000b185dc0d0e917d18e5dbf0746be6597d3331dd27ea0554e6db433feb2e81730b20b2807d33a1527bf43cd3bc057aa7f641609c2551ebe2fd575f4db704fbf38101" // Grabbed from existing mainnet block, it does not have any meaning except for the length validation
|
extraSubstring := "d7830100018358444388676f312e31342e31856c696e75780000000000000000b185dc0d0e917d18e5dbf0746be6597d3331dd27ea0554e6db433feb2e81730b20b2807d33a1527bf43cd3bc057aa7f641609c2551ebe2fd575f4db704fbf38101" // Grabbed from existing mainnet block, it does not have any meaning except for the length validation
|
||||||
//ReceiptHash = "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"
|
customHeader.Extra, _ = hex.DecodeString(extraSubstring)
|
||||||
//Root := "0xc99c095e53ff1afe3b86750affd13c7550a2d24d51fb8e41b3c3ef2ea8274bcc"
|
|
||||||
customExtra, _ = hex.DecodeString(extraSubstring)
|
|
||||||
}
|
}
|
||||||
|
var difficulty *big.Int
|
||||||
|
if customHeader.Difficulty == nil {
|
||||||
|
difficulty = big.NewInt(1)
|
||||||
|
} else {
|
||||||
|
difficulty = customHeader.Difficulty
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
header := types.Header{
|
||||||
|
ParentHash: common.HexToHash(parentHash),
|
||||||
|
UncleHash: types.EmptyUncleHash,
|
||||||
|
TxHash: types.EmptyRootHash,
|
||||||
|
// ReceiptHash: types.EmptyRootHash,
|
||||||
|
ReceiptHash: common.HexToHash(receiptHash),
|
||||||
|
Root: root,
|
||||||
|
Coinbase: common.HexToAddress(coinbase),
|
||||||
|
Difficulty: big.NewInt(int64(1)),
|
||||||
|
Number: big.NewInt(int64(number)),
|
||||||
|
GasLimit: 1200000000,
|
||||||
|
Time: big.NewInt(int64(number * 10)),
|
||||||
|
Extra: customExtra,
|
||||||
|
Validator: signer[:],
|
||||||
|
}
|
||||||
|
*/
|
||||||
header := types.Header{
|
header := types.Header{
|
||||||
ParentHash: common.HexToHash(parentHash),
|
ParentHash: customHeader.ParentHash,
|
||||||
UncleHash: types.EmptyUncleHash,
|
UncleHash: types.EmptyUncleHash,
|
||||||
TxHash: types.EmptyRootHash,
|
TxHash: types.EmptyRootHash,
|
||||||
// ReceiptHash: types.EmptyRootHash,
|
// ReceiptHash: types.EmptyRootHash,
|
||||||
ReceiptHash: common.HexToHash(receiptHash),
|
ReceiptHash: customHeader.ReceiptHash,
|
||||||
Root: root,
|
Root: customHeader.Root,
|
||||||
Coinbase: common.HexToAddress(coinbase),
|
Coinbase: customHeader.Coinbase,
|
||||||
Difficulty: big.NewInt(difficulty),
|
Difficulty: difficulty,
|
||||||
Number: big.NewInt(int64(number)),
|
Number: customHeader.Number,
|
||||||
GasLimit: 1200000000,
|
GasLimit: 1200000000,
|
||||||
Time: big.NewInt(int64(number * 10)),
|
Time: big.NewInt(customHeader.Number.Int64() * 10),
|
||||||
Extra: customExtra,
|
Extra: customHeader.Extra,
|
||||||
|
Validator: customHeader.Validator,
|
||||||
}
|
}
|
||||||
|
|
||||||
var block *types.Block
|
var block *types.Block
|
||||||
if len(txs) == 0 {
|
if len(txs) == 0 {
|
||||||
block = types.NewBlockWithHeader(&header)
|
block = types.NewBlockWithHeader(&header)
|
||||||
} else {
|
} else {
|
||||||
// Prepare Receipt
|
// Prepare Receipt
|
||||||
statedb, err := bc.StateAt(bc.GetBlockByNumber(uint64(number - 1)).Root()) //Get parent root
|
statedb, err := bc.StateAt(bc.GetBlockByNumber(customHeader.Number.Uint64() - 1).Root()) //Get parent root
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("%v when get state", err)
|
return nil, fmt.Errorf("%v when get state", err)
|
||||||
}
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package consensus
|
package tests
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
@ -11,7 +11,7 @@ import (
|
||||||
|
|
||||||
// Timeout handler
|
// Timeout handler
|
||||||
func TestTimeoutMessageHandlerSuccessfullyGenerateTCandSyncInfo(t *testing.T) {
|
func TestTimeoutMessageHandlerSuccessfullyGenerateTCandSyncInfo(t *testing.T) {
|
||||||
blockchain, _, _, _ := PrepareXDCTestBlockChain(t, 11, params.TestXDPoSMockChainConfigWithV2Engine)
|
blockchain, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 11, params.TestXDPoSMockChainConfigWithV2Engine)
|
||||||
engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2
|
engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2
|
||||||
|
|
||||||
// Set round to 1
|
// Set round to 1
|
||||||
|
|
@ -49,11 +49,11 @@ func TestTimeoutMessageHandlerSuccessfullyGenerateTCandSyncInfo(t *testing.T) {
|
||||||
|
|
||||||
assert.NotNil(t, syncInfoMsg)
|
assert.NotNil(t, syncInfoMsg)
|
||||||
|
|
||||||
// Should have QC, however, we did not inilise it, hence will show default empty value
|
// Shouldn't have QC, however, we did not inilise it, hence will show default empty value
|
||||||
qc := syncInfoMsg.(utils.SyncInfo).HighestQuorumCert
|
qc := syncInfoMsg.(*utils.SyncInfo).HighestQuorumCert
|
||||||
assert.NotNil(t, qc)
|
assert.Nil(t, qc)
|
||||||
|
|
||||||
tc := syncInfoMsg.(utils.SyncInfo).HighestTimeoutCert
|
tc := syncInfoMsg.(*utils.SyncInfo).HighestTimeoutCert
|
||||||
assert.NotNil(t, tc)
|
assert.NotNil(t, tc)
|
||||||
assert.Equal(t, tc.Round, utils.Round(1))
|
assert.Equal(t, tc.Round, utils.Round(1))
|
||||||
sigatures := []utils.Signature{[]byte{1}, []byte{2}, []byte{3}}
|
sigatures := []utils.Signature{[]byte{1}, []byte{2}, []byte{3}}
|
||||||
|
|
@ -62,7 +62,7 @@ func TestTimeoutMessageHandlerSuccessfullyGenerateTCandSyncInfo(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestThrowErrorIfTimeoutMsgRoundNotEqualToCurrentRound(t *testing.T) {
|
func TestThrowErrorIfTimeoutMsgRoundNotEqualToCurrentRound(t *testing.T) {
|
||||||
blockchain, _, _, _ := PrepareXDCTestBlockChain(t, 11, params.TestXDPoSMockChainConfigWithV2Engine)
|
blockchain, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 11, params.TestXDPoSMockChainConfigWithV2Engine)
|
||||||
engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2
|
engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2
|
||||||
|
|
||||||
// Set round to 3
|
// Set round to 3
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package consensus
|
package tests
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
@ -26,7 +26,7 @@ func TestVoteMessageHandlerSuccessfullyGeneratedAndProcessQC(t *testing.T) {
|
||||||
engineV2.SetNewRoundFaker(utils.Round(11), false)
|
engineV2.SetNewRoundFaker(utils.Round(11), false)
|
||||||
// Create two timeout message which will not reach vote pool threshold
|
// Create two timeout message which will not reach vote pool threshold
|
||||||
voteMsg := &utils.Vote{
|
voteMsg := &utils.Vote{
|
||||||
ProposedBlockInfo: *blockInfo,
|
ProposedBlockInfo: blockInfo,
|
||||||
Signature: []byte{1},
|
Signature: []byte{1},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -35,10 +35,10 @@ func TestVoteMessageHandlerSuccessfullyGeneratedAndProcessQC(t *testing.T) {
|
||||||
currentRound, lockQuorumCert, highestQuorumCert := engineV2.GetProperties()
|
currentRound, lockQuorumCert, highestQuorumCert := engineV2.GetProperties()
|
||||||
// Inilised with nil and 0 round
|
// Inilised with nil and 0 round
|
||||||
assert.Nil(t, lockQuorumCert)
|
assert.Nil(t, lockQuorumCert)
|
||||||
assert.Equal(t, utils.Round(0), highestQuorumCert.ProposedBlockInfo.Round)
|
assert.Nil(t, highestQuorumCert)
|
||||||
assert.Equal(t, utils.Round(11), currentRound)
|
assert.Equal(t, utils.Round(11), currentRound)
|
||||||
voteMsg = &utils.Vote{
|
voteMsg = &utils.Vote{
|
||||||
ProposedBlockInfo: *blockInfo,
|
ProposedBlockInfo: blockInfo,
|
||||||
Signature: []byte{2},
|
Signature: []byte{2},
|
||||||
}
|
}
|
||||||
err = engineV2.VoteHandler(blockchain, voteMsg)
|
err = engineV2.VoteHandler(blockchain, voteMsg)
|
||||||
|
|
@ -46,13 +46,13 @@ func TestVoteMessageHandlerSuccessfullyGeneratedAndProcessQC(t *testing.T) {
|
||||||
currentRound, lockQuorumCert, highestQuorumCert = engineV2.GetProperties()
|
currentRound, lockQuorumCert, highestQuorumCert = engineV2.GetProperties()
|
||||||
// Still using the initlised value because we did not yet go to the next round
|
// Still using the initlised value because we did not yet go to the next round
|
||||||
assert.Nil(t, lockQuorumCert)
|
assert.Nil(t, lockQuorumCert)
|
||||||
assert.Equal(t, utils.Round(0), highestQuorumCert.ProposedBlockInfo.Round)
|
assert.Nil(t, highestQuorumCert)
|
||||||
|
|
||||||
assert.Equal(t, utils.Round(11), currentRound)
|
assert.Equal(t, utils.Round(11), currentRound)
|
||||||
|
|
||||||
// Create a vote message that should trigger vote pool hook and increment the round to 12
|
// Create a vote message that should trigger vote pool hook and increment the round to 12
|
||||||
voteMsg = &utils.Vote{
|
voteMsg = &utils.Vote{
|
||||||
ProposedBlockInfo: *blockInfo,
|
ProposedBlockInfo: blockInfo,
|
||||||
Signature: []byte{3},
|
Signature: []byte{3},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -80,7 +80,7 @@ func TestThrowErrorIfVoteMsgRoundNotEqualToCurrentRound(t *testing.T) {
|
||||||
// Set round to 13
|
// Set round to 13
|
||||||
engineV2.SetNewRoundFaker(utils.Round(13), false)
|
engineV2.SetNewRoundFaker(utils.Round(13), false)
|
||||||
voteMsg := &utils.Vote{
|
voteMsg := &utils.Vote{
|
||||||
ProposedBlockInfo: *blockInfo,
|
ProposedBlockInfo: blockInfo,
|
||||||
Signature: []byte{1},
|
Signature: []byte{1},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -112,7 +112,7 @@ func TestProcessVoteMsgThenTimeoutMsg(t *testing.T) {
|
||||||
}
|
}
|
||||||
// Create two vote message which will not reach vote pool threshold
|
// Create two vote message which will not reach vote pool threshold
|
||||||
voteMsg := &utils.Vote{
|
voteMsg := &utils.Vote{
|
||||||
ProposedBlockInfo: *blockInfo,
|
ProposedBlockInfo: blockInfo,
|
||||||
Signature: []byte{1},
|
Signature: []byte{1},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -121,11 +121,11 @@ func TestProcessVoteMsgThenTimeoutMsg(t *testing.T) {
|
||||||
currentRound, lockQuorumCert, highestQuorumCert := engineV2.GetProperties()
|
currentRound, lockQuorumCert, highestQuorumCert := engineV2.GetProperties()
|
||||||
// Inilised with nil and 0 round
|
// Inilised with nil and 0 round
|
||||||
assert.Nil(t, lockQuorumCert)
|
assert.Nil(t, lockQuorumCert)
|
||||||
assert.Equal(t, utils.Round(0), highestQuorumCert.ProposedBlockInfo.Round)
|
assert.Nil(t, highestQuorumCert)
|
||||||
|
|
||||||
assert.Equal(t, utils.Round(11), currentRound)
|
assert.Equal(t, utils.Round(11), currentRound)
|
||||||
voteMsg = &utils.Vote{
|
voteMsg = &utils.Vote{
|
||||||
ProposedBlockInfo: *blockInfo,
|
ProposedBlockInfo: blockInfo,
|
||||||
Signature: []byte{2},
|
Signature: []byte{2},
|
||||||
}
|
}
|
||||||
err = engineV2.VoteHandler(blockchain, voteMsg)
|
err = engineV2.VoteHandler(blockchain, voteMsg)
|
||||||
|
|
@ -135,7 +135,7 @@ func TestProcessVoteMsgThenTimeoutMsg(t *testing.T) {
|
||||||
|
|
||||||
// Create a vote message that should trigger vote pool hook
|
// Create a vote message that should trigger vote pool hook
|
||||||
voteMsg = &utils.Vote{
|
voteMsg = &utils.Vote{
|
||||||
ProposedBlockInfo: *blockInfo,
|
ProposedBlockInfo: blockInfo,
|
||||||
Signature: []byte{3},
|
Signature: []byte{3},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -194,11 +194,11 @@ func TestProcessVoteMsgThenTimeoutMsg(t *testing.T) {
|
||||||
assert.NotNil(t, syncInfoMsg)
|
assert.NotNil(t, syncInfoMsg)
|
||||||
|
|
||||||
// Should have HighestQuorumCert from previous round votes
|
// Should have HighestQuorumCert from previous round votes
|
||||||
qc := syncInfoMsg.(utils.SyncInfo).HighestQuorumCert
|
qc := syncInfoMsg.(*utils.SyncInfo).HighestQuorumCert
|
||||||
assert.NotNil(t, qc)
|
assert.NotNil(t, qc)
|
||||||
assert.Equal(t, utils.Round(11), qc.ProposedBlockInfo.Round)
|
assert.Equal(t, utils.Round(11), qc.ProposedBlockInfo.Round)
|
||||||
|
|
||||||
tc := syncInfoMsg.(utils.SyncInfo).HighestTimeoutCert
|
tc := syncInfoMsg.(*utils.SyncInfo).HighestTimeoutCert
|
||||||
assert.NotNil(t, tc)
|
assert.NotNil(t, tc)
|
||||||
assert.Equal(t, utils.Round(12), tc.Round)
|
assert.Equal(t, utils.Round(12), tc.Round)
|
||||||
sigatures := []utils.Signature{[]byte{1}, []byte{2}, []byte{3}}
|
sigatures := []utils.Signature{[]byte{1}, []byte{2}, []byte{3}}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package bfter
|
package bft
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
@ -11,13 +11,17 @@ import (
|
||||||
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/engines/engine_v2"
|
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/engines/engine_v2"
|
||||||
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils"
|
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core"
|
"github.com/XinFinOrg/XDPoSChain/core"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
// make different votes based on Signatures
|
// make different votes based on Signatures
|
||||||
func makeVotes(n int) []utils.Vote {
|
func makeVotes(n int) []utils.Vote {
|
||||||
var votes []utils.Vote
|
var votes []utils.Vote
|
||||||
for i := 0; i < n; i++ {
|
for i := 0; i < n; i++ {
|
||||||
votes = append(votes, utils.Vote{Signature: []byte{byte(i)}})
|
votes = append(votes, utils.Vote{
|
||||||
|
ProposedBlockInfo: &utils.BlockInfo{},
|
||||||
|
Signature: []byte{byte(i)},
|
||||||
|
})
|
||||||
}
|
}
|
||||||
return votes
|
return votes
|
||||||
}
|
}
|
||||||
|
|
@ -101,7 +105,7 @@ func TestDuplicateVotes(t *testing.T) {
|
||||||
atomic.AddUint32(&broadcastCounter, 1)
|
atomic.AddUint32(&broadcastCounter, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
vote := utils.Vote{}
|
vote := utils.Vote{ProposedBlockInfo: &utils.BlockInfo{}}
|
||||||
|
|
||||||
// send twice
|
// send twice
|
||||||
tester.bfter.Vote(&vote)
|
tester.bfter.Vote(&vote)
|
||||||
|
|
@ -132,7 +136,7 @@ func TestNotBoardcastInvalidVote(t *testing.T) {
|
||||||
atomic.AddUint32(&broadcastCounter, 1)
|
atomic.AddUint32(&broadcastCounter, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
vote := utils.Vote{}
|
vote := utils.Vote{ProposedBlockInfo: &utils.BlockInfo{}}
|
||||||
tester.bfter.Vote(&vote)
|
tester.bfter.Vote(&vote)
|
||||||
|
|
||||||
time.Sleep(50 * time.Millisecond)
|
time.Sleep(50 * time.Millisecond)
|
||||||
|
|
@ -143,3 +147,59 @@ func TestNotBoardcastInvalidVote(t *testing.T) {
|
||||||
|
|
||||||
// TODO: SyncInfo and Timeout Test, should be same as Vote.
|
// TODO: SyncInfo and Timeout Test, should be same as Vote.
|
||||||
// Once all test on vote covered, then duplicate to others
|
// Once all test on vote covered, then duplicate to others
|
||||||
|
|
||||||
|
func TestTimeoutHandler(t *testing.T) {
|
||||||
|
tester := newTester()
|
||||||
|
verifyCounter := uint32(0)
|
||||||
|
handlerCounter := uint32(0)
|
||||||
|
broadcastCounter := uint32(0)
|
||||||
|
targetVotes := 1
|
||||||
|
|
||||||
|
tester.bfter.consensus.verifyTimeout = func(timeout *utils.Timeout) error {
|
||||||
|
atomic.AddUint32(&verifyCounter, 1)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
tester.bfter.consensus.timeoutHandler = func(timeout *utils.Timeout) error {
|
||||||
|
atomic.AddUint32(&handlerCounter, 1)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
tester.bfter.broadcast.Timeout = func(*utils.Timeout) {
|
||||||
|
atomic.AddUint32(&broadcastCounter, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
timeoutMsg := &utils.Timeout{}
|
||||||
|
|
||||||
|
err := tester.bfter.Timeout(timeoutMsg)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
|
||||||
|
if int(verifyCounter) != targetVotes || int(handlerCounter) != targetVotes || int(broadcastCounter) != targetVotes {
|
||||||
|
t.Fatalf("count mismatch: have %v on verify, %v on handler, %v on broadcast, want %v", verifyCounter, handlerCounter, broadcastCounter, targetVotes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTimeoutHandlerRoundNotEqual(t *testing.T) {
|
||||||
|
tester := newTester()
|
||||||
|
|
||||||
|
tester.bfter.consensus.verifyTimeout = func(timeout *utils.Timeout) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
tester.bfter.consensus.timeoutHandler = func(timeout *utils.Timeout) error {
|
||||||
|
return &utils.ErrIncomingMessageRoundNotEqualCurrentRound{utils.Round(1), utils.Round(2)}
|
||||||
|
}
|
||||||
|
|
||||||
|
tester.bfter.broadcast.Timeout = func(*utils.Timeout) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
timeoutMsg := &utils.Timeout{}
|
||||||
|
|
||||||
|
err := tester.bfter.Timeout(timeoutMsg)
|
||||||
|
assert.Equal(t, "Timeout message round number: 1 does not match currentRound: 2", err.Error())
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package bfter
|
package bft
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/XinFinOrg/XDPoSChain/consensus"
|
"github.com/XinFinOrg/XDPoSChain/consensus"
|
||||||
|
|
@ -79,9 +79,9 @@ func (b *Bfter) SetConsensusFuns(engine consensus.Engine) {
|
||||||
|
|
||||||
// TODO: rename
|
// TODO: rename
|
||||||
func (b *Bfter) Vote(vote *utils.Vote) error {
|
func (b *Bfter) Vote(vote *utils.Vote) error {
|
||||||
log.Trace("Receive Vote", "vote", vote)
|
log.Info("Receive Vote", "voted block hash", vote.ProposedBlockInfo.Hash.Hex(), "number", vote.ProposedBlockInfo.Number, "round", vote.ProposedBlockInfo.Round)
|
||||||
if b.knownVotes.Contains(vote.Hash()) {
|
if b.knownVotes.Contains(vote.Hash()) {
|
||||||
log.Trace("Discarded vote, known vote", "Signature", vote.Signature, "hash", vote.Hash())
|
log.Info("Discarded vote, known vote", "voted block hash", vote.ProposedBlockInfo.Hash.Hex(), "number", vote.ProposedBlockInfo.Number, "round", vote.ProposedBlockInfo.Round)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -116,6 +116,10 @@ func (b *Bfter) Timeout(timeout *utils.Timeout) error {
|
||||||
|
|
||||||
err = b.consensus.timeoutHandler(timeout)
|
err = b.consensus.timeoutHandler(timeout)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if _, ok := err.(*utils.ErrIncomingMessageRoundNotEqualCurrentRound); ok {
|
||||||
|
log.Debug("timeout message round not equal", "error", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
log.Error("handle BFT Timeout", "error", err)
|
log.Error("handle BFT Timeout", "error", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -19,10 +19,11 @@ package fetcher
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"github.com/hashicorp/golang-lru"
|
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
lru "github.com/hashicorp/golang-lru"
|
||||||
|
|
||||||
"github.com/XinFinOrg/XDPoSChain/common"
|
"github.com/XinFinOrg/XDPoSChain/common"
|
||||||
"github.com/XinFinOrg/XDPoSChain/consensus"
|
"github.com/XinFinOrg/XDPoSChain/consensus"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||||
|
|
@ -56,6 +57,8 @@ type bodyRequesterFn func([]common.Hash) error
|
||||||
// headerVerifierFn is a callback type to verify a block's header for fast propagation.
|
// headerVerifierFn is a callback type to verify a block's header for fast propagation.
|
||||||
type headerVerifierFn func(header *types.Header) error
|
type headerVerifierFn func(header *types.Header) error
|
||||||
|
|
||||||
|
type proposeBlockHandlerFn func(header *types.Header) error
|
||||||
|
|
||||||
// blockBroadcasterFn is a callback type for broadcasting a block to connected peers.
|
// blockBroadcasterFn is a callback type for broadcasting a block to connected peers.
|
||||||
type blockBroadcasterFn func(block *types.Block, propagate bool)
|
type blockBroadcasterFn func(block *types.Block, propagate bool)
|
||||||
|
|
||||||
|
|
@ -133,13 +136,14 @@ type Fetcher struct {
|
||||||
queued map[common.Hash]*inject // Set of already queued blocks (to dedup imports)
|
queued map[common.Hash]*inject // Set of already queued blocks (to dedup imports)
|
||||||
knowns *lru.ARCCache
|
knowns *lru.ARCCache
|
||||||
// Callbacks
|
// Callbacks
|
||||||
getBlock blockRetrievalFn // Retrieves a block from the local chain
|
getBlock blockRetrievalFn // Retrieves a block from the local chain
|
||||||
verifyHeader headerVerifierFn // Checks if a block's headers have a valid proof of work
|
verifyHeader headerVerifierFn // Checks if a block's headers have a valid proof of work
|
||||||
broadcastBlock blockBroadcasterFn // Broadcasts a block to connected peers
|
handleProposedBlock proposeBlockHandlerFn // Consensus v2 specific: Hanle new proposed block
|
||||||
chainHeight chainHeightFn // Retrieves the current chain's height
|
broadcastBlock blockBroadcasterFn // Broadcasts a block to connected peers
|
||||||
insertBlock blockInsertFn // Injects a batch of blocks into the chain
|
chainHeight chainHeightFn // Retrieves the current chain's height
|
||||||
prepareBlock blockPrepareFn
|
insertBlock blockInsertFn // Injects a batch of blocks into the chain
|
||||||
dropPeer peerDropFn // Drops a peer for misbehaving
|
prepareBlock blockPrepareFn
|
||||||
|
dropPeer peerDropFn // Drops a peer for misbehaving
|
||||||
|
|
||||||
// Testing hooks
|
// Testing hooks
|
||||||
announceChangeHook func(common.Hash, bool) // Method to call upon adding or deleting a hash from the announce list
|
announceChangeHook func(common.Hash, bool) // Method to call upon adding or deleting a hash from the announce list
|
||||||
|
|
@ -151,32 +155,33 @@ type Fetcher struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// New creates a block fetcher to retrieve blocks based on hash announcements.
|
// New creates a block fetcher to retrieve blocks based on hash announcements.
|
||||||
func New(getBlock blockRetrievalFn, verifyHeader headerVerifierFn, broadcastBlock blockBroadcasterFn, chainHeight chainHeightFn, insertBlock blockInsertFn, prepareBlock blockPrepareFn, dropPeer peerDropFn) *Fetcher {
|
func New(getBlock blockRetrievalFn, verifyHeader headerVerifierFn, handleProposedBlock proposeBlockHandlerFn, broadcastBlock blockBroadcasterFn, chainHeight chainHeightFn, insertBlock blockInsertFn, prepareBlock blockPrepareFn, dropPeer peerDropFn) *Fetcher {
|
||||||
knownBlocks, _ := lru.NewARC(blockLimit)
|
knownBlocks, _ := lru.NewARC(blockLimit)
|
||||||
return &Fetcher{
|
return &Fetcher{
|
||||||
notify: make(chan *announce),
|
notify: make(chan *announce),
|
||||||
inject: make(chan *inject),
|
inject: make(chan *inject),
|
||||||
blockFilter: make(chan chan []*types.Block),
|
blockFilter: make(chan chan []*types.Block),
|
||||||
headerFilter: make(chan chan *headerFilterTask),
|
headerFilter: make(chan chan *headerFilterTask),
|
||||||
bodyFilter: make(chan chan *bodyFilterTask),
|
bodyFilter: make(chan chan *bodyFilterTask),
|
||||||
done: make(chan common.Hash),
|
done: make(chan common.Hash),
|
||||||
quit: make(chan struct{}),
|
quit: make(chan struct{}),
|
||||||
announces: make(map[string]int),
|
announces: make(map[string]int),
|
||||||
announced: make(map[common.Hash][]*announce),
|
announced: make(map[common.Hash][]*announce),
|
||||||
fetching: make(map[common.Hash]*announce),
|
fetching: make(map[common.Hash]*announce),
|
||||||
fetched: make(map[common.Hash][]*announce),
|
fetched: make(map[common.Hash][]*announce),
|
||||||
completing: make(map[common.Hash]*announce),
|
completing: make(map[common.Hash]*announce),
|
||||||
queue: prque.New(),
|
queue: prque.New(),
|
||||||
queues: make(map[string]int),
|
queues: make(map[string]int),
|
||||||
queued: make(map[common.Hash]*inject),
|
queued: make(map[common.Hash]*inject),
|
||||||
knowns: knownBlocks,
|
knowns: knownBlocks,
|
||||||
getBlock: getBlock,
|
getBlock: getBlock,
|
||||||
verifyHeader: verifyHeader,
|
verifyHeader: verifyHeader,
|
||||||
broadcastBlock: broadcastBlock,
|
handleProposedBlock: handleProposedBlock,
|
||||||
chainHeight: chainHeight,
|
broadcastBlock: broadcastBlock,
|
||||||
insertBlock: insertBlock,
|
chainHeight: chainHeight,
|
||||||
prepareBlock: prepareBlock,
|
insertBlock: insertBlock,
|
||||||
dropPeer: dropPeer,
|
prepareBlock: prepareBlock,
|
||||||
|
dropPeer: dropPeer,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -721,6 +726,11 @@ func (f *Fetcher) insert(peer string, block *types.Block) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
err = f.handleProposedBlock(block.Header())
|
||||||
|
if err != nil {
|
||||||
|
log.Error("[insert] Unable to handle new proposed block", "err", err)
|
||||||
|
}
|
||||||
|
// TODO: (XIN-101) Add propose block handler
|
||||||
// If import succeeded, broadcast the block
|
// If import succeeded, broadcast the block
|
||||||
propAnnounceOutTimer.UpdateSince(block.ReceivedAt)
|
propAnnounceOutTimer.UpdateSince(block.ReceivedAt)
|
||||||
if !fastBroadCast {
|
if !fastBroadCast {
|
||||||
|
|
|
||||||
|
|
@ -18,13 +18,14 @@ package fetcher
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
|
|
||||||
"math/big"
|
"math/big"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
|
||||||
|
|
||||||
"github.com/XinFinOrg/XDPoSChain/common"
|
"github.com/XinFinOrg/XDPoSChain/common"
|
||||||
"github.com/XinFinOrg/XDPoSChain/consensus/ethash"
|
"github.com/XinFinOrg/XDPoSChain/consensus/ethash"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core"
|
"github.com/XinFinOrg/XDPoSChain/core"
|
||||||
|
|
@ -92,7 +93,7 @@ func newTester() *fetcherTester {
|
||||||
blocks: map[common.Hash]*types.Block{genesis.Hash(): genesis},
|
blocks: map[common.Hash]*types.Block{genesis.Hash(): genesis},
|
||||||
drops: make(map[string]bool),
|
drops: make(map[string]bool),
|
||||||
}
|
}
|
||||||
tester.fetcher = New(tester.getBlock, tester.verifyHeader, tester.broadcastBlock, tester.chainHeight, tester.insertBlock, tester.prepareBlock, tester.dropPeer)
|
tester.fetcher = New(tester.getBlock, tester.verifyHeader, tester.handleProposedBlock, tester.broadcastBlock, tester.chainHeight, tester.insertBlock, tester.prepareBlock, tester.dropPeer)
|
||||||
tester.fetcher.Start()
|
tester.fetcher.Start()
|
||||||
|
|
||||||
return tester
|
return tester
|
||||||
|
|
@ -111,6 +112,10 @@ func (f *fetcherTester) verifyHeader(header *types.Header) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *fetcherTester) handleProposedBlock(header *types.Header) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// broadcastBlock is a nop placeholder for the block broadcasting.
|
// broadcastBlock is a nop placeholder for the block broadcasting.
|
||||||
func (f *fetcherTester) broadcastBlock(block *types.Block, propagate bool) {
|
func (f *fetcherTester) broadcastBlock(block *types.Block, propagate bool) {
|
||||||
}
|
}
|
||||||
|
|
@ -296,6 +301,14 @@ func verifyImportDone(t *testing.T, imported chan *types.Block) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func verifyProposeBlockHandlerCalled(t *testing.T, proposedBlockChan chan *types.Header) {
|
||||||
|
select {
|
||||||
|
case <-proposedBlockChan:
|
||||||
|
case <-time.After(50 * time.Millisecond):
|
||||||
|
t.Fatalf("did not call propose block handler")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Tests that a fetcher accepts block announcements and initiates retrievals for
|
// Tests that a fetcher accepts block announcements and initiates retrievals for
|
||||||
// them, successfully importing into the local chain.
|
// them, successfully importing into the local chain.
|
||||||
func TestSequentialAnnouncements62(t *testing.T) { testSequentialAnnouncements(t, 62) }
|
func TestSequentialAnnouncements62(t *testing.T) { testSequentialAnnouncements(t, 62) }
|
||||||
|
|
@ -317,12 +330,18 @@ func testSequentialAnnouncements(t *testing.T, protocol int) {
|
||||||
imported <- block
|
imported <- block
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
handleProposedBlockChan := make(chan *types.Header)
|
||||||
|
tester.fetcher.handleProposedBlock = func(header *types.Header) error {
|
||||||
|
go func() { handleProposedBlockChan <- header }()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
for i := len(hashes) - 2; i >= 0; i-- {
|
for i := len(hashes) - 2; i >= 0; i-- {
|
||||||
tester.fetcher.Notify("valid", hashes[i], uint64(len(hashes)-i-1), time.Now().Add(-arriveTimeout), headerFetcher, bodyFetcher)
|
tester.fetcher.Notify("valid", hashes[i], uint64(len(hashes)-i-1), time.Now().Add(-arriveTimeout), headerFetcher, bodyFetcher)
|
||||||
verifyImportEvent(t, imported, true)
|
verifyImportEvent(t, imported, true)
|
||||||
}
|
}
|
||||||
verifyImportDone(t, imported)
|
verifyImportDone(t, imported)
|
||||||
|
verifyProposeBlockHandlerCalled(t, handleProposedBlockChan)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tests that if blocks are announced by multiple peers (or even the same buggy
|
// Tests that if blocks are announced by multiple peers (or even the same buggy
|
||||||
|
|
|
||||||
|
|
@ -29,11 +29,12 @@ import (
|
||||||
|
|
||||||
"github.com/XinFinOrg/XDPoSChain/common"
|
"github.com/XinFinOrg/XDPoSChain/common"
|
||||||
"github.com/XinFinOrg/XDPoSChain/consensus"
|
"github.com/XinFinOrg/XDPoSChain/consensus"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS"
|
||||||
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils"
|
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils"
|
||||||
"github.com/XinFinOrg/XDPoSChain/consensus/misc"
|
"github.com/XinFinOrg/XDPoSChain/consensus/misc"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core"
|
"github.com/XinFinOrg/XDPoSChain/core"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||||
"github.com/XinFinOrg/XDPoSChain/eth/bfter"
|
"github.com/XinFinOrg/XDPoSChain/eth/bft"
|
||||||
"github.com/XinFinOrg/XDPoSChain/eth/downloader"
|
"github.com/XinFinOrg/XDPoSChain/eth/downloader"
|
||||||
"github.com/XinFinOrg/XDPoSChain/eth/fetcher"
|
"github.com/XinFinOrg/XDPoSChain/eth/fetcher"
|
||||||
"github.com/XinFinOrg/XDPoSChain/ethdb"
|
"github.com/XinFinOrg/XDPoSChain/ethdb"
|
||||||
|
|
@ -82,7 +83,7 @@ type ProtocolManager struct {
|
||||||
downloader *downloader.Downloader
|
downloader *downloader.Downloader
|
||||||
fetcher *fetcher.Fetcher
|
fetcher *fetcher.Fetcher
|
||||||
peers *peerSet
|
peers *peerSet
|
||||||
bfter *bfter.Bfter
|
bft *bft.Bfter
|
||||||
|
|
||||||
SubProtocols []p2p.Protocol
|
SubProtocols []p2p.Protocol
|
||||||
|
|
||||||
|
|
@ -198,6 +199,10 @@ func NewProtocolManager(config *params.ChainConfig, mode downloader.SyncMode, ne
|
||||||
validator := func(header *types.Header) error {
|
validator := func(header *types.Header) error {
|
||||||
return engine.VerifyHeader(blockchain, header, true)
|
return engine.VerifyHeader(blockchain, header, true)
|
||||||
}
|
}
|
||||||
|
handleProposedBlock := func(header *types.Header) error {
|
||||||
|
return engine.(*XDPoS.XDPoS).HandleProposedBlock(blockchain, header)
|
||||||
|
}
|
||||||
|
|
||||||
heighter := func() uint64 {
|
heighter := func() uint64 {
|
||||||
return blockchain.CurrentBlock().NumberU64()
|
return blockchain.CurrentBlock().NumberU64()
|
||||||
}
|
}
|
||||||
|
|
@ -220,16 +225,16 @@ func NewProtocolManager(config *params.ChainConfig, mode downloader.SyncMode, ne
|
||||||
atomic.StoreUint32(&manager.acceptTxs, 1) // Mark initial sync done on any fetcher import
|
atomic.StoreUint32(&manager.acceptTxs, 1) // Mark initial sync done on any fetcher import
|
||||||
return manager.blockchain.PrepareBlock(block)
|
return manager.blockchain.PrepareBlock(block)
|
||||||
}
|
}
|
||||||
manager.fetcher = fetcher.New(blockchain.GetBlockByHash, validator, manager.BroadcastBlock, heighter, inserter, prepare, manager.removePeer)
|
manager.fetcher = fetcher.New(blockchain.GetBlockByHash, validator, handleProposedBlock, manager.BroadcastBlock, heighter, inserter, prepare, manager.removePeer)
|
||||||
//Define bft function
|
//Define bft function
|
||||||
broadcasts := bfter.BroadcastFns{
|
broadcasts := bft.BroadcastFns{
|
||||||
Vote: manager.BroadcastVote,
|
Vote: manager.BroadcastVote,
|
||||||
Timeout: manager.BroadcastTimeout,
|
Timeout: manager.BroadcastTimeout,
|
||||||
SyncInfo: manager.BroadcastSyncInfo,
|
SyncInfo: manager.BroadcastSyncInfo,
|
||||||
}
|
}
|
||||||
manager.bfter = bfter.New(broadcasts, blockchain)
|
manager.bft = bft.New(broadcasts, blockchain)
|
||||||
if blockchain.Config().XDPoS != nil {
|
if blockchain.Config().XDPoS != nil {
|
||||||
manager.bfter.SetConsensusFuns(engine)
|
manager.bft.SetConsensusFuns(engine)
|
||||||
}
|
}
|
||||||
|
|
||||||
return manager, nil
|
return manager, nil
|
||||||
|
|
@ -827,7 +832,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
|
||||||
}
|
}
|
||||||
// Mark the peer as owning the vote and process it
|
// Mark the peer as owning the vote and process it
|
||||||
p.MarkVote(vote.Hash())
|
p.MarkVote(vote.Hash())
|
||||||
pm.bfter.Vote(&vote)
|
pm.bft.Vote(&vote)
|
||||||
case msg.Code == TimeoutMsg:
|
case msg.Code == TimeoutMsg:
|
||||||
var timeout utils.Timeout
|
var timeout utils.Timeout
|
||||||
if err := msg.Decode(&timeout); err != nil {
|
if err := msg.Decode(&timeout); err != nil {
|
||||||
|
|
@ -836,7 +841,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
|
||||||
|
|
||||||
// Mark the peer as owning the timeout and process it
|
// Mark the peer as owning the timeout and process it
|
||||||
p.MarkTimeout(timeout.Hash())
|
p.MarkTimeout(timeout.Hash())
|
||||||
pm.bfter.Timeout(&timeout)
|
pm.bft.Timeout(&timeout)
|
||||||
case msg.Code == SyncInfoMsg:
|
case msg.Code == SyncInfoMsg:
|
||||||
var syncInfo utils.SyncInfo
|
var syncInfo utils.SyncInfo
|
||||||
if err := msg.Decode(&syncInfo); err != nil {
|
if err := msg.Decode(&syncInfo); err != nil {
|
||||||
|
|
@ -844,7 +849,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
|
||||||
}
|
}
|
||||||
// Mark the peer as owning the syncInfo and process it
|
// Mark the peer as owning the syncInfo and process it
|
||||||
p.MarkSyncInfo(syncInfo.Hash())
|
p.MarkSyncInfo(syncInfo.Hash())
|
||||||
pm.bfter.SyncInfo(&syncInfo)
|
pm.bft.SyncInfo(&syncInfo)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return errResp(ErrInvalidMsgCode, "%v", msg.Code)
|
return errResp(ErrInvalidMsgCode, "%v", msg.Code)
|
||||||
|
|
@ -904,7 +909,7 @@ func (pm *ProtocolManager) BroadcastVote(vote *utils.Vote) {
|
||||||
for _, peer := range peers {
|
for _, peer := range peers {
|
||||||
peer.SendVote(vote)
|
peer.SendVote(vote)
|
||||||
}
|
}
|
||||||
log.Trace("Propagated Vote", "hash", hash, "recipients", len(peers))
|
log.Info("Propagated Vote", "voted block hash", vote.ProposedBlockInfo.Hash.Hex(), "number", vote.ProposedBlockInfo.Number, "round", vote.ProposedBlockInfo.Round, "recipients", len(peers))
|
||||||
}
|
}
|
||||||
|
|
||||||
// BroadcastTimeout will propagate a Timeout to all peers which are not known to
|
// BroadcastTimeout will propagate a Timeout to all peers which are not known to
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ var ProtocolName = "eth"
|
||||||
var ProtocolVersions = []uint{eth63, eth62}
|
var ProtocolVersions = []uint{eth63, eth62}
|
||||||
|
|
||||||
// Number of implemented message corresponding to different protocol versions.
|
// Number of implemented message corresponding to different protocol versions.
|
||||||
var ProtocolLengths = []uint64{17, 8}
|
var ProtocolLengths = []uint64{227, 8}
|
||||||
|
|
||||||
const ProtocolMaxMsgSize = 10 * 1024 * 1024 // Maximum cap on the size of a protocol message
|
const ProtocolMaxMsgSize = 10 * 1024 * 1024 // Maximum cap on the size of a protocol message
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -134,9 +134,9 @@ func (pm *ProtocolManager) txsyncLoop() {
|
||||||
func (pm *ProtocolManager) syncer() {
|
func (pm *ProtocolManager) syncer() {
|
||||||
// Start and ensure cleanup of sync mechanisms
|
// Start and ensure cleanup of sync mechanisms
|
||||||
pm.fetcher.Start()
|
pm.fetcher.Start()
|
||||||
pm.bfter.Start()
|
pm.bft.Start()
|
||||||
defer pm.fetcher.Stop()
|
defer pm.fetcher.Stop()
|
||||||
defer pm.bfter.Stop()
|
defer pm.bft.Stop()
|
||||||
defer pm.downloader.Terminate()
|
defer pm.downloader.Terminate()
|
||||||
|
|
||||||
// Wait for different events to fire synchronisation operations
|
// Wait for different events to fire synchronisation operations
|
||||||
|
|
|
||||||
1
go.mod
1
go.mod
|
|
@ -63,4 +63,5 @@ require (
|
||||||
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce
|
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce
|
||||||
gopkg.in/olebedev/go-duktape.v3 v3.0.0-20190213234257-ec84240a7772
|
gopkg.in/olebedev/go-duktape.v3 v3.0.0-20190213234257-ec84240a7772
|
||||||
gopkg.in/urfave/cli.v1 v1.20.0
|
gopkg.in/urfave/cli.v1 v1.20.0
|
||||||
|
gotest.tools v2.2.0+incompatible
|
||||||
)
|
)
|
||||||
|
|
|
||||||
3
go.sum
3
go.sum
|
|
@ -118,6 +118,7 @@ github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
|
||||||
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
|
github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg=
|
||||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
github.com/google/uuid v1.0.0 h1:b4Gk+7WdP/d3HZH8EJsZpvV7EtDOgaZLtnaNGIu1adA=
|
github.com/google/uuid v1.0.0 h1:b4Gk+7WdP/d3HZH8EJsZpvV7EtDOgaZLtnaNGIu1adA=
|
||||||
|
|
@ -339,6 +340,7 @@ gopkg.in/jcmturner/gokrb5.v7 v7.2.3/go.mod h1:l8VISx+WGYp+Fp7KRbsiUuXTTOnxIc3Tuv
|
||||||
gopkg.in/jcmturner/rpc.v1 v1.1.0/go.mod h1:YIdkC4XfD6GXbzje11McwsDuOlZQSb9W4vfLvuNnlv8=
|
gopkg.in/jcmturner/rpc.v1 v1.1.0/go.mod h1:YIdkC4XfD6GXbzje11McwsDuOlZQSb9W4vfLvuNnlv8=
|
||||||
gopkg.in/karalabe/cookiejar.v2 v2.0.0-20150724131613-8dcd6a7f4951 h1:DMTcQRFbEH62YPRWwOI647s2e5mHda3oBPMHfrLs2bw=
|
gopkg.in/karalabe/cookiejar.v2 v2.0.0-20150724131613-8dcd6a7f4951 h1:DMTcQRFbEH62YPRWwOI647s2e5mHda3oBPMHfrLs2bw=
|
||||||
gopkg.in/karalabe/cookiejar.v2 v2.0.0-20150724131613-8dcd6a7f4951/go.mod h1:owOxCRGGeAx1uugABik6K9oeNu1cgxP/R9ItzLDxNWA=
|
gopkg.in/karalabe/cookiejar.v2 v2.0.0-20150724131613-8dcd6a7f4951/go.mod h1:owOxCRGGeAx1uugABik6K9oeNu1cgxP/R9ItzLDxNWA=
|
||||||
|
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce h1:+JknDZhAj8YMt7GC73Ei8pv4MzjDUNPHgQWJdtMAaDU=
|
||||||
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c=
|
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c=
|
||||||
gopkg.in/olebedev/go-duktape.v3 v3.0.0-20190213234257-ec84240a7772 h1:hhsSf/5z74Ck/DJYc+R8zpq8KGm7uJvpdLRQED/IedA=
|
gopkg.in/olebedev/go-duktape.v3 v3.0.0-20190213234257-ec84240a7772 h1:hhsSf/5z74Ck/DJYc+R8zpq8KGm7uJvpdLRQED/IedA=
|
||||||
gopkg.in/olebedev/go-duktape.v3 v3.0.0-20190213234257-ec84240a7772/go.mod h1:uAJfkITjFhyEEuUfm7bsmCZRbW5WRq8s9EY8HZ6hCns=
|
gopkg.in/olebedev/go-duktape.v3 v3.0.0-20190213234257-ec84240a7772/go.mod h1:uAJfkITjFhyEEuUfm7bsmCZRbW5WRq8s9EY8HZ6hCns=
|
||||||
|
|
@ -353,5 +355,6 @@ gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
|
||||||
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
|
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
|
||||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
|
|
||||||
|
|
@ -523,6 +523,7 @@ func (self *worker) commitNewWork() {
|
||||||
// only go with XDPoS
|
// only go with XDPoS
|
||||||
if self.config.XDPoS != nil {
|
if self.config.XDPoS != nil {
|
||||||
// get masternodes set from latest checkpoint
|
// get masternodes set from latest checkpoint
|
||||||
|
// TODO: refactor on yourturn with below condition for v1 v2
|
||||||
c := self.engine.(*XDPoS.XDPoS)
|
c := self.engine.(*XDPoS.XDPoS)
|
||||||
len, preIndex, curIndex, ok, err := c.YourTurn(self.chain, parent.Header(), self.coinbase)
|
len, preIndex, curIndex, ok, err := c.YourTurn(self.chain, parent.Header(), self.coinbase)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -581,6 +582,10 @@ func (self *worker) commitNewWork() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := self.engine.Prepare(self.chain, header); err != nil {
|
if err := self.engine.Prepare(self.chain, header); err != nil {
|
||||||
|
if err == consensus.ErrNotReadyToPropose {
|
||||||
|
log.Info("Waiting...", "err", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
log.Error("Failed to prepare header for new block", "err", err)
|
log.Error("Failed to prepare header for new block", "err", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,11 +36,11 @@ var (
|
||||||
|
|
||||||
var (
|
var (
|
||||||
XDPoSV2Config = &V2{
|
XDPoSV2Config = &V2{
|
||||||
TimeoutWorkerDuration: 50000,
|
TimeoutWorkerDuration: 50,
|
||||||
CertThreshold: common.MaxMasternodesV2*2/3 + 1,
|
CertThreshold: common.MaxMasternodesV2*2/3 + 1,
|
||||||
}
|
}
|
||||||
TestXDPoSV2Config = &V2{
|
TestXDPoSV2Config = &V2{
|
||||||
TimeoutWorkerDuration: 5000,
|
TimeoutWorkerDuration: 5,
|
||||||
CertThreshold: 3,
|
CertThreshold: 3,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -195,12 +195,12 @@ type XDPoSConfig struct {
|
||||||
Gap uint64 `json:"gap"` // Gap time preparing for the next epoch
|
Gap uint64 `json:"gap"` // Gap time preparing for the next epoch
|
||||||
FoudationWalletAddr common.Address `json:"foudationWalletAddr"` // Foundation Address Wallet
|
FoudationWalletAddr common.Address `json:"foudationWalletAddr"` // Foundation Address Wallet
|
||||||
SkipValidation bool //Skip Block Validation for testing purpose
|
SkipValidation bool //Skip Block Validation for testing purpose
|
||||||
XDPoSV2Block *big.Int
|
XDPoSV2Block *big.Int `json:"v2Block"`
|
||||||
V2 V2
|
V2 V2 `json:"v2"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type V2 struct {
|
type V2 struct {
|
||||||
TimeoutWorkerDuration int64 `json:"TimeoutWorkerDuration"` // Duration in ms
|
TimeoutWorkerDuration int64 `json:"timeoutWorkerDuration"` // Duration in ms
|
||||||
CertThreshold int `json:"certificateThreshold"` // Necessary number of messages from master nodes to form a certificate
|
CertThreshold int `json:"certificateThreshold"` // Necessary number of messages from master nodes to form a certificate
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue