mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-06-19 21:31:37 +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 {
|
||||
switch x.config.BlockConsensusVersion(header.Number) {
|
||||
case params.ConsensusEngineVersion2:
|
||||
return nil
|
||||
return x.EngineV2.Prepare(chain, header)
|
||||
default: // Default "v1"
|
||||
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) {
|
||||
switch x.config.BlockConsensusVersion(header.Number) {
|
||||
case params.ConsensusEngineVersion2:
|
||||
return nil, nil
|
||||
return x.EngineV2.Finalize(chain, header, state, parentState, txs, uncles, receipts)
|
||||
default: // Default "v1"
|
||||
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) {
|
||||
switch x.config.BlockConsensusVersion(block.Number()) {
|
||||
case params.ConsensusEngineVersion2:
|
||||
return nil, nil
|
||||
return x.EngineV2.Seal(chain, block, stop)
|
||||
default: // Default "v1"
|
||||
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 {
|
||||
switch x.config.BlockConsensusVersion(parent.Number) {
|
||||
case params.ConsensusEngineVersion2:
|
||||
return nil
|
||||
return x.EngineV2.CalcDifficulty(chain, time, parent)
|
||||
default: // Default "v1"
|
||||
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
|
||||
*/
|
||||
|
|
@ -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 {
|
||||
switch x.config.BlockConsensusVersion(header.Number) {
|
||||
case params.ConsensusEngineVersion2:
|
||||
return []common.Address{}
|
||||
return x.EngineV2.GetMasternodes(chain, header)
|
||||
default: // Default "v1"
|
||||
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) {
|
||||
switch x.config.BlockConsensusVersion(parent.Number) {
|
||||
case params.ConsensusEngineVersion2:
|
||||
return x.EngineV2.YourTurn(chain, parent, signer)
|
||||
default: // Default "v1"
|
||||
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) {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
|
@ -307,6 +318,13 @@ func (x *XDPoS) GetDb() ethdb.Database {
|
|||
|
||||
func (x *XDPoS) GetSnapshot(chain consensus.ChainReader, header *types.Header) (*utils.PublicApiSnapshot, error) {
|
||||
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"
|
||||
sp, err := x.EngineV1.GetSnapshot(chain, header)
|
||||
// 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"
|
||||
)
|
||||
|
||||
// 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
|
||||
// Ethereum testnet following the Ropsten attacks.
|
||||
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
|
||||
// from the signature in the header's extra-data section.
|
||||
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.
|
||||
|
|
@ -384,7 +359,7 @@ func whoIsCreator(snap *SnapshotV1, header *types.Header) (common.Address, error
|
|||
if header.Number.Uint64() == 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 {
|
||||
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
|
||||
creator, err := ecrecover(header, x.signatures)
|
||||
creator, err := utils.Ecrecover(header, x.signatures)
|
||||
if err != nil {
|
||||
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")
|
||||
}
|
||||
}
|
||||
m, err := GetM1M2FromCheckpointHeader(cpHeader, header, chain.Config())
|
||||
m, err := utils.GetM1M2FromCheckpointHeader(cpHeader, header, chain.Config())
|
||||
if err != nil {
|
||||
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) {
|
||||
return ecrecover(header, x.signatures)
|
||||
return utils.Ecrecover(header, x.signatures)
|
||||
}
|
||||
|
||||
func (x *XDPoS_v1) RecoverValidator(header *types.Header) (common.Address, error) {
|
||||
|
|
@ -979,21 +954,6 @@ func GetMasternodesFromCheckpointHeader(checkpointHeader *types.Header) []common
|
|||
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) {
|
||||
startGapBlockHeader := checkpointHeader
|
||||
number := checkpointHeader.Number.Uint64()
|
||||
|
|
|
|||
|
|
@ -187,7 +187,7 @@ func (s *SnapshotV1) apply(headers []*types.Header) (*SnapshotV1, error) {
|
|||
delete(snap.Recents, number-limit)
|
||||
}
|
||||
// Resolve the authorization key and check against signers
|
||||
signer, err := ecrecover(header, s.sigcache)
|
||||
signer, err := utils.Ecrecover(header, s.sigcache)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,12 @@
|
|||
package engine_v2
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"math/big"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
|
|
@ -12,25 +16,30 @@ import (
|
|||
"github.com/XinFinOrg/XDPoSChain/consensus"
|
||||
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils"
|
||||
"github.com/XinFinOrg/XDPoSChain/consensus/clique"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/state"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||
"github.com/XinFinOrg/XDPoSChain/crypto"
|
||||
"github.com/XinFinOrg/XDPoSChain/ethdb"
|
||||
"github.com/XinFinOrg/XDPoSChain/log"
|
||||
"github.com/XinFinOrg/XDPoSChain/params"
|
||||
lru "github.com/hashicorp/golang-lru"
|
||||
)
|
||||
|
||||
type XDPoS_v2 struct {
|
||||
config *params.XDPoSConfig // Consensus engine configuration parameters
|
||||
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
|
||||
signFn clique.SignerFn // Signer function to authorize hashes with
|
||||
lock sync.RWMutex // Protects the signer fields
|
||||
signLock sync.RWMutex // Protects the signer fields
|
||||
|
||||
BroadcastCh chan interface{}
|
||||
timeoutWorker *countdown.CountdownTimer // Timer to generate broadcast timeout msg if threashold reached
|
||||
|
||||
lock sync.RWMutex // Protects the currentRound fields etc
|
||||
timeoutPool *utils.Pool
|
||||
votePool *utils.Pool
|
||||
currentRound utils.Round
|
||||
|
|
@ -40,23 +49,33 @@ type XDPoS_v2 struct {
|
|||
lockQuorumCert *utils.QuorumCert
|
||||
highestTimeoutCert *utils.TimeoutCert
|
||||
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 {
|
||||
// Setup Timer
|
||||
duration := time.Duration(config.V2.TimeoutWorkerDuration) * time.Millisecond
|
||||
duration := time.Duration(config.V2.TimeoutWorkerDuration) * time.Second
|
||||
timer := countdown.NewCountDown(duration)
|
||||
timeoutPool := utils.NewPool(config.V2.CertThreshold)
|
||||
|
||||
recents, _ := lru.NewARC(utils.InmemorySnapshots)
|
||||
signatures, _ := lru.NewARC(utils.InmemorySnapshots)
|
||||
|
||||
votePool := utils.NewPool(config.V2.CertThreshold)
|
||||
engine := &XDPoS_v2{
|
||||
config: config,
|
||||
db: db,
|
||||
timeoutWorker: timer,
|
||||
BroadcastCh: make(chan interface{}),
|
||||
timeoutPool: timeoutPool,
|
||||
votePool: votePool,
|
||||
highestTimeoutCert: &utils.TimeoutCert{},
|
||||
highestQuorumCert: &utils.QuorumCert{},
|
||||
config: config,
|
||||
db: db,
|
||||
signatures: signatures,
|
||||
|
||||
recents: recents,
|
||||
timeoutWorker: timer,
|
||||
BroadcastCh: make(chan interface{}),
|
||||
timeoutPool: timeoutPool,
|
||||
votePool: votePool,
|
||||
|
||||
highestTimeoutCert: nil,
|
||||
highestQuorumCert: nil,
|
||||
highestVotedRound: utils.Round(0),
|
||||
}
|
||||
// Add callback to the timer
|
||||
|
|
@ -65,24 +84,118 @@ func New(config *params.XDPoSConfig, db ethdb.Database) *XDPoS_v2 {
|
|||
return engine
|
||||
}
|
||||
|
||||
/*
|
||||
Testing tools
|
||||
*/
|
||||
func (x *XDPoS_v2) SetNewRoundFaker(newRound utils.Round, resetTimer bool) {
|
||||
// Prepare implements consensus.Engine, preparing all the consensus fields of the
|
||||
// header for running the transactions on top.
|
||||
func (x *XDPoS_v2) Prepare(chain consensus.ChainReader, header *types.Header) error {
|
||||
// Verify mined block parent matches highest QC
|
||||
x.lock.Lock()
|
||||
defer x.lock.Unlock()
|
||||
// Reset a bunch of things
|
||||
if resetTimer {
|
||||
x.timeoutWorker.Reset()
|
||||
// Check header if it is the first consensus v2 block, if so, assign initial values to current round and highestQC
|
||||
if header.Number.Cmp(big.NewInt(0).Add(x.config.XDPoSV2Block, big.NewInt(1))) == 0 {
|
||||
log.Info("[Prepare] Initilising highest QC for consensus v2 first block", "Block Num", header.Number.String(), "BlockHash", header.Hash())
|
||||
// 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
|
||||
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
|
||||
// Finalize implements consensus.Engine, ensuring no uncles are set, nor block
|
||||
// rewards given, and returns the final block.
|
||||
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) {
|
||||
// set block reward
|
||||
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.
|
||||
|
|
@ -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) {
|
||||
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 {
|
||||
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
|
||||
*/
|
||||
|
|
@ -152,7 +493,7 @@ func (x *XDPoS_v2) VerifyVoteMessage(vote *utils.Vote) (bool, error) {
|
|||
2. Verify blockInfo
|
||||
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
|
||||
|
|
@ -228,7 +569,7 @@ func (x *XDPoS_v2) TimeoutHandler(timeout *utils.Timeout) error {
|
|||
|
||||
// 1. checkRoundNumber
|
||||
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
|
||||
isThresholdReached, numberOfTimeoutsInPool, pooledTimeouts := x.timeoutPool.Add(timeout)
|
||||
|
|
@ -277,19 +618,43 @@ func (x *XDPoS_v2) onTimeoutPoolThresholdReached(pooledTimeouts map[common.Hash]
|
|||
/*
|
||||
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()
|
||||
defer x.lock.Unlock()
|
||||
|
||||
/*
|
||||
1. processQC(): process the QC inside the proposed block
|
||||
2. verifyVotingRule(): the proposed block's info is extracted into BlockInfo and verified for voting
|
||||
3. sendVote()
|
||||
1. Verify QC
|
||||
2. Generate blockInfo
|
||||
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 {
|
||||
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)
|
||||
if err != nil {
|
||||
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
|
||||
func (x *XDPoS_v2) processQC(blockChainReader consensus.ChainReader, quorumCert *utils.QuorumCert) error {
|
||||
log.Trace("[ProcessQC][Before]", "HighQC", x.highestQuorumCert)
|
||||
// 1. Update HighestQC
|
||||
if x.highestQuorumCert == nil || (quorumCert.ProposedBlockInfo.Round > x.highestQuorumCert.ProposedBlockInfo.Round) {
|
||||
x.highestQuorumCert = quorumCert
|
||||
}
|
||||
// 2. Get QC from header and update lockQuorumCert(lockQuorumCert is the parent of highestQC)
|
||||
proposedBlockHeader := blockChainReader.GetHeaderByHash(quorumCert.ProposedBlockInfo.Hash)
|
||||
// Extra field contain parent information
|
||||
var decodedExtraField utils.ExtraFields_v2
|
||||
err := utils.DecodeBytesExtraFields(proposedBlockHeader.Extra, &decodedExtraField)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
x.lockQuorumCert = &decodedExtraField.QuorumCert
|
||||
if proposedBlockHeader.Number.Cmp(x.config.XDPoSV2Block) > 0 {
|
||||
// Extra field contain parent information
|
||||
var decodedExtraField utils.ExtraFields_v2
|
||||
err := utils.DecodeBytesExtraFields(proposedBlockHeader.Extra, &decodedExtraField)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if x.lockQuorumCert == nil || decodedExtraField.QuorumCert.ProposedBlockInfo.Round > x.lockQuorumCert.ProposedBlockInfo.Round {
|
||||
x.lockQuorumCert = decodedExtraField.QuorumCert
|
||||
}
|
||||
|
||||
proposedBlockRound := &decodedExtraField.Round
|
||||
// 3. Update commit block info
|
||||
_, err = x.commitBlocks(blockChainReader, proposedBlockHeader, proposedBlockRound)
|
||||
if err != nil {
|
||||
return err
|
||||
proposedBlockRound := &decodedExtraField.Round
|
||||
// 3. Update commit block info
|
||||
_, err = x.commitBlocks(blockChainReader, proposedBlockHeader, proposedBlockRound)
|
||||
if err != nil {
|
||||
log.Error("[processQC] Fail to commitBlocks", "proposedBlockRound", proposedBlockRound)
|
||||
return err
|
||||
}
|
||||
}
|
||||
// 4. Set new round
|
||||
if quorumCert.ProposedBlockInfo.Round >= x.currentRound {
|
||||
err := x.setNewRound(quorumCert.ProposedBlockInfo.Round + 1)
|
||||
if err != nil {
|
||||
log.Error("[processQC] Fail to setNewRound", "new round to set", quorumCert.ProposedBlockInfo.Round+1)
|
||||
return err
|
||||
}
|
||||
}
|
||||
log.Trace("[ProcessQC][After]", "HighQC", x.highestQuorumCert)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -411,7 +784,11 @@ func (x *XDPoS_v2) verifyVotingRule(blockChainReader consensus.ChainReader, bloc
|
|||
if blockInfo.Round != x.currentRound {
|
||||
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 {
|
||||
return false, err
|
||||
}
|
||||
|
|
@ -436,7 +813,7 @@ func (x *XDPoS_v2) sendVote(blockInfo *utils.BlockInfo) error {
|
|||
|
||||
x.highestVotedRound = x.currentRound
|
||||
voteMsg := &utils.Vote{
|
||||
ProposedBlockInfo: *blockInfo,
|
||||
ProposedBlockInfo: blockInfo,
|
||||
Signature: signedHash,
|
||||
}
|
||||
x.broadcastToBftChannel(voteMsg)
|
||||
|
|
@ -524,17 +901,44 @@ func (x *XDPoS_v2) getCurrentRoundMasterNodes() []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,
|
||||
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
|
||||
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
|
||||
parentBlock := blockChainReader.GetHeaderByHash(proposedBlockHeader.ParentHash)
|
||||
parentBlock := blockCahinReader.GetHeaderByHash(proposedBlockHeader.ParentHash)
|
||||
|
||||
var decodedExtraField utils.ExtraFields_v2
|
||||
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
|
||||
grandParentBlock := blockChainReader.GetHeaderByHash(parentBlock.ParentHash)
|
||||
grandParentBlock := blockCahinReader.GetHeaderByHash(parentBlock.ParentHash)
|
||||
err = utils.DecodeBytesExtraFields(grandParentBlock.Extra, &decodedExtraField)
|
||||
if err != nil {
|
||||
return false, err
|
||||
|
|
|
|||
|
|
@ -1 +1,133 @@
|
|||
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
|
||||
|
||||
import "errors"
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Various error messages to mark blocks invalid. These should be private to
|
||||
// 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.
|
||||
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 = errors.New("unauthorized")
|
||||
|
||||
|
|
@ -72,3 +78,12 @@ var (
|
|||
|
||||
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
|
||||
type Vote struct {
|
||||
ProposedBlockInfo BlockInfo
|
||||
ProposedBlockInfo *BlockInfo
|
||||
Signature Signature
|
||||
}
|
||||
|
||||
|
|
@ -91,7 +91,7 @@ type SyncInfo struct {
|
|||
|
||||
// Quorum Certificate struct in XDPoS 2.0
|
||||
type QuorumCert struct {
|
||||
ProposedBlockInfo BlockInfo
|
||||
ProposedBlockInfo *BlockInfo
|
||||
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
|
||||
type ExtraFields_v2 struct {
|
||||
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) {
|
||||
|
|
|
|||
|
|
@ -10,10 +10,10 @@ import (
|
|||
|
||||
func toyExtraFields() *ExtraFields_v2 {
|
||||
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}
|
||||
signatures := []Signature{signature}
|
||||
quorumCert := QuorumCert{ProposedBlockInfo: blockInfo, Signatures: signatures}
|
||||
quorumCert := &QuorumCert{ProposedBlockInfo: blockInfo, Signatures: signatures}
|
||||
e := &ExtraFields_v2{Round: round, QuorumCert: quorumCert}
|
||||
return e
|
||||
}
|
||||
|
|
@ -35,14 +35,14 @@ func TestExtraFieldsEncodeDecode(t *testing.T) {
|
|||
|
||||
func TestHashAndSigHash(t *testing.T) {
|
||||
round := Round(307)
|
||||
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)}
|
||||
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)}
|
||||
signature1 := []byte{1, 2, 3, 4, 5, 6, 7, 8}
|
||||
signature2 := []byte{1, 2, 3, 4, 5, 6, 7, 7}
|
||||
signatures1 := []Signature{signature1}
|
||||
quorumCert1 := QuorumCert{ProposedBlockInfo: blockInfo1, Signatures: signatures1}
|
||||
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}
|
||||
vote2 := Vote{ProposedBlockInfo: blockInfo1, Signature: signature2}
|
||||
if vote1.Hash() == vote2.Hash() {
|
||||
|
|
@ -53,12 +53,12 @@ func TestHashAndSigHash(t *testing.T) {
|
|||
if timeout1.Hash() == timeout2.Hash() {
|
||||
t.Fatalf("Hash of two timeouts shouldn't equal")
|
||||
}
|
||||
syncInfo1 := SyncInfo{HighestQuorumCert: &quorumCert1}
|
||||
syncInfo2 := SyncInfo{HighestQuorumCert: &quorumCert2}
|
||||
syncInfo1 := SyncInfo{HighestQuorumCert: quorumCert1}
|
||||
syncInfo2 := SyncInfo{HighestQuorumCert: quorumCert2}
|
||||
if syncInfo1.Hash() == syncInfo2.Hash() {
|
||||
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")
|
||||
}
|
||||
round2 := Round(999)
|
||||
|
|
|
|||
|
|
@ -10,10 +10,12 @@ import (
|
|||
|
||||
"github.com/XinFinOrg/XDPoSChain/common"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||
"github.com/XinFinOrg/XDPoSChain/crypto"
|
||||
"github.com/XinFinOrg/XDPoSChain/crypto/sha3"
|
||||
"github.com/XinFinOrg/XDPoSChain/log"
|
||||
"github.com/XinFinOrg/XDPoSChain/params"
|
||||
"github.com/XinFinOrg/XDPoSChain/rlp"
|
||||
lru "github.com/hashicorp/golang-lru"
|
||||
)
|
||||
|
||||
func Position(list []common.Address, x common.Address) int {
|
||||
|
|
@ -151,14 +153,33 @@ func SigHash(header *types.Header) (hash common.Hash) {
|
|||
return hash
|
||||
}
|
||||
|
||||
// Encode XDPoS 2.0 extra fields into bytes
|
||||
func (e *ExtraFields_v2) EncodeToBytes() ([]byte, error) {
|
||||
bytes, err := rlp.EncodeToBytes(e)
|
||||
func SigHashV2(header *types.Header) (hash common.Hash) {
|
||||
hasher := sha3.NewKeccak256()
|
||||
|
||||
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 {
|
||||
return nil, err
|
||||
log.Debug("Fail to encode", err)
|
||||
}
|
||||
versionByte := []byte{2}
|
||||
return append(versionByte, bytes...), nil
|
||||
hasher.Sum(hash[:0])
|
||||
return hash
|
||||
}
|
||||
|
||||
// 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:
|
||||
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")
|
||||
|
||||
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 (
|
||||
"fmt"
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/common"
|
||||
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||
"github.com/XinFinOrg/XDPoSChain/params"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
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)
|
||||
|
||||
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 block 11
|
||||
|
||||
blockCoinBase := fmt.Sprintf("0x111000000000000000000000000000000%03d", 11)
|
||||
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 {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package consensus
|
||||
package tests
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
|
@ -6,6 +6,7 @@ import (
|
|||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/common"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||
"github.com/XinFinOrg/XDPoSChain/params"
|
||||
)
|
||||
|
|
@ -26,7 +27,13 @@ func TestNotUpdateSignerListIfNotOnGapBlock(t *testing.T) {
|
|||
|
||||
//Get from block validator error message
|
||||
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 {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
@ -54,7 +61,13 @@ func TestNotChangeSingerListIfNothingProposedOrVoted(t *testing.T) {
|
|||
// Insert block 450
|
||||
blockCoinBase := fmt.Sprintf("0x111000000000000000000000000000000%03d", 450)
|
||||
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 {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
@ -89,7 +102,13 @@ func TestUpdateSignerListIfVotedBeforeGap(t *testing.T) {
|
|||
|
||||
//Get from block validator error message
|
||||
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 {
|
||||
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
|
||||
block450CoinbaseAddress := "0xaaa0000000000000000000000000000000000450"
|
||||
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 {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
@ -147,7 +172,13 @@ func TestCallUpdateM1WithSmartContractTranscation(t *testing.T) {
|
|||
|
||||
//Get from block validator error message
|
||||
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 {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
@ -189,7 +220,13 @@ func TestCallUpdateM1WhenForkedBlockBackToMainChain(t *testing.T) {
|
|||
}
|
||||
|
||||
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 {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
@ -217,7 +254,13 @@ func TestCallUpdateM1WhenForkedBlockBackToMainChain(t *testing.T) {
|
|||
}
|
||||
|
||||
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 {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
@ -240,7 +283,13 @@ func TestCallUpdateM1WhenForkedBlockBackToMainChain(t *testing.T) {
|
|||
|
||||
blockCoinBase451B := "0xbbb0000000000000000000000000000000000451"
|
||||
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 {
|
||||
t.Fatal(err)
|
||||
|
|
@ -316,7 +365,13 @@ func TestStatesShouldBeUpdatedWhenForkedBlockBecameMainChainAtGapBlock(t *testin
|
|||
transferTransaction := transferTx(t, acc1Addr, 999)
|
||||
|
||||
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 {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
@ -351,7 +406,13 @@ func TestStatesShouldBeUpdatedWhenForkedBlockBecameMainChainAtGapBlock(t *testin
|
|||
transferTransaction = transferTx(t, acc1Addr, 888)
|
||||
|
||||
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 {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
@ -378,7 +439,13 @@ func TestStatesShouldBeUpdatedWhenForkedBlockBecameMainChainAtGapBlock(t *testin
|
|||
|
||||
blockCoinBase451B := "0xbbb0000000000000000000000000000000000451"
|
||||
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 {
|
||||
t.Fatal(err)
|
||||
|
|
@ -440,7 +507,13 @@ func TestVoteShouldNotBeAffectedByFork(t *testing.T) {
|
|||
// Insert normal blocks 450 A
|
||||
blockCoinBase450A := "0xaaa0000000000000000000000000000000000450"
|
||||
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 {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
@ -453,7 +526,13 @@ func TestVoteShouldNotBeAffectedByFork(t *testing.T) {
|
|||
}
|
||||
|
||||
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 {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
@ -476,21 +555,39 @@ func TestVoteShouldNotBeAffectedByFork(t *testing.T) {
|
|||
// Insert forked Block 450 B
|
||||
blockCoinBase450B := "0xbbb0000000000000000000000000000000000450"
|
||||
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 {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
blockCoinBase451B := "0xbbb0000000000000000000000000000000000451"
|
||||
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 {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
blockCoinBase452B := "0xbbb0000000000000000000000000000000000452"
|
||||
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 {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
@ -1,9 +1,10 @@
|
|||
package consensus
|
||||
package tests
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/common"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||
"github.com/XinFinOrg/XDPoSChain/params"
|
||||
)
|
||||
|
|
@ -39,7 +40,15 @@ func TestRaceConditionOnBlockchainReadAndWrite(t *testing.T) {
|
|||
transferTransaction := transferTx(t, acc1Addr, 999)
|
||||
|
||||
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 {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
@ -74,7 +83,16 @@ func TestRaceConditionOnBlockchainReadAndWrite(t *testing.T) {
|
|||
transferTransaction = transferTx(t, acc1Addr, 888)
|
||||
|
||||
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 {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
@ -104,7 +122,14 @@ func TestRaceConditionOnBlockchainReadAndWrite(t *testing.T) {
|
|||
|
||||
blockCoinBase451B := "0xbbb0000000000000000000000000000000000451"
|
||||
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 {
|
||||
t.Fatal(err)
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package consensus
|
||||
package tests
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
|
@ -10,7 +10,7 @@ import (
|
|||
)
|
||||
|
||||
func TestCountdownTimeoutToSendTimeoutMessage(t *testing.T) {
|
||||
blockchain, _, _, _ := PrepareXDCTestBlockChain(t, 11, params.TestXDPoSMockChainConfigWithV2Engine)
|
||||
blockchain, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 11, params.TestXDPoSMockChainConfigWithV2Engine)
|
||||
engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2
|
||||
|
||||
engineV2.SetNewRoundFaker(utils.Round(1), true)
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
package consensus
|
||||
package tests
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS"
|
||||
|
|
@ -24,20 +23,14 @@ func TestProposedBlockMessageHandlerSuccessfullyGenerateVote(t *testing.T) {
|
|||
t.Fatal("Fail to decode extra data", err)
|
||||
}
|
||||
|
||||
proposedBlockInfo := &utils.BlockInfo{
|
||||
Hash: currentBlock.Hash(),
|
||||
Round: utils.Round(11),
|
||||
Number: big.NewInt(11),
|
||||
}
|
||||
|
||||
err = engineV2.ProposedBlockHandler(blockchain, proposedBlockInfo, &extraField.QuorumCert)
|
||||
err = engineV2.ProposedBlockHandler(blockchain, currentBlock.Header())
|
||||
if err != nil {
|
||||
t.Fatal("Fail propose proposedBlock handler", err)
|
||||
}
|
||||
|
||||
voteMsg := <-engineV2.BroadcastCh
|
||||
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()
|
||||
// Shoud not trigger setNewRound
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package consensus
|
||||
package tests
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
|
@ -245,8 +245,13 @@ func PrepareXDCTestBlockChain(t *testing.T, numOfBlocks int, chainConfig *params
|
|||
for i := 1; i <= numOfBlocks; i++ {
|
||||
blockCoinBase := fmt.Sprintf("0x111000000000000000000000000000000%03d", i)
|
||||
merkleRoot := "35999dded35e8db12de7e6c1471eb9670c162eec616ecebbaf4fddd4676fb930"
|
||||
|
||||
block, err := insertBlock(blockchain, i, blockCoinBase, currentBlock, merkleRoot, nil, 1)
|
||||
header := &types.Header{
|
||||
Root: common.HexToHash(merkleRoot),
|
||||
Number: big.NewInt(int64(i)),
|
||||
ParentHash: currentBlock.Hash(),
|
||||
Coinbase: common.HexToAddress(blockCoinBase),
|
||||
}
|
||||
block, err := insertBlock(blockchain, header)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
@ -283,19 +288,19 @@ func PrepareXDCTestBlockChainForV2Engine(t *testing.T, numOfBlocks int, chainCon
|
|||
merkleRoot := "35999dded35e8db12de7e6c1471eb9670c162eec616ecebbaf4fddd4676fb930"
|
||||
|
||||
// Build engine v2 compatible extra data field
|
||||
proposedBlockInfo := utils.BlockInfo{
|
||||
proposedBlockInfo := &utils.BlockInfo{
|
||||
Hash: currentBlock.Hash(),
|
||||
Round: utils.Round(i - 1),
|
||||
Number: big.NewInt(int64(i - 1)),
|
||||
}
|
||||
// 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 {
|
||||
panic(fmt.Errorf("Error generate QC by creating signedHash: %v", err))
|
||||
}
|
||||
var signatures []utils.Signature
|
||||
signatures = append(signatures, signedHash)
|
||||
quorumCert := utils.QuorumCert{
|
||||
quorumCert := &utils.QuorumCert{
|
||||
ProposedBlockInfo: proposedBlockInfo,
|
||||
Signatures: signatures,
|
||||
}
|
||||
|
|
@ -309,7 +314,15 @@ func PrepareXDCTestBlockChainForV2Engine(t *testing.T, numOfBlocks int, chainCon
|
|||
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 {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
@ -324,16 +337,27 @@ func PrepareXDCTestBlockChainForV2Engine(t *testing.T, numOfBlocks int, chainCon
|
|||
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
|
||||
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(
|
||||
blockchain,
|
||||
parentBlock.Hash().Hex(),
|
||||
blockCoinBase, blockNum, nil,
|
||||
"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
|
||||
common.HexToHash(root),
|
||||
customExtra,
|
||||
difficulty,
|
||||
header,
|
||||
nil,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -347,15 +371,20 @@ func insertBlock(blockchain *BlockChain, blockNum int, blockCoinBase string, par
|
|||
}
|
||||
|
||||
// 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(
|
||||
blockchain,
|
||||
parentBlock.Hash().Hex(),
|
||||
blockCoinBase, blockNum, txs,
|
||||
"0x9319777b782ba2c83a33c995481ff894ac96d9a92a1963091346a3e1e386705c",
|
||||
common.HexToHash(root),
|
||||
nil,
|
||||
difficulty,
|
||||
header,
|
||||
txs,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -368,35 +397,56 @@ func insertBlockTxs(blockchain *BlockChain, blockNum int, blockCoinBase string,
|
|||
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) {
|
||||
if customExtra == nil {
|
||||
//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) {
|
||||
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
|
||||
//ReceiptHash = "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"
|
||||
//Root := "0xc99c095e53ff1afe3b86750affd13c7550a2d24d51fb8e41b3c3ef2ea8274bcc"
|
||||
customExtra, _ = hex.DecodeString(extraSubstring)
|
||||
customHeader.Extra, _ = 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{
|
||||
ParentHash: common.HexToHash(parentHash),
|
||||
ParentHash: customHeader.ParentHash,
|
||||
UncleHash: types.EmptyUncleHash,
|
||||
TxHash: types.EmptyRootHash,
|
||||
// ReceiptHash: types.EmptyRootHash,
|
||||
ReceiptHash: common.HexToHash(receiptHash),
|
||||
Root: root,
|
||||
Coinbase: common.HexToAddress(coinbase),
|
||||
Difficulty: big.NewInt(difficulty),
|
||||
Number: big.NewInt(int64(number)),
|
||||
ReceiptHash: customHeader.ReceiptHash,
|
||||
Root: customHeader.Root,
|
||||
Coinbase: customHeader.Coinbase,
|
||||
Difficulty: difficulty,
|
||||
Number: customHeader.Number,
|
||||
GasLimit: 1200000000,
|
||||
Time: big.NewInt(int64(number * 10)),
|
||||
Extra: customExtra,
|
||||
Time: big.NewInt(customHeader.Number.Int64() * 10),
|
||||
Extra: customHeader.Extra,
|
||||
Validator: customHeader.Validator,
|
||||
}
|
||||
|
||||
var block *types.Block
|
||||
if len(txs) == 0 {
|
||||
block = types.NewBlockWithHeader(&header)
|
||||
} else {
|
||||
// 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 {
|
||||
return nil, fmt.Errorf("%v when get state", err)
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package consensus
|
||||
package tests
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
|
@ -11,7 +11,7 @@ import (
|
|||
|
||||
// Timeout handler
|
||||
func TestTimeoutMessageHandlerSuccessfullyGenerateTCandSyncInfo(t *testing.T) {
|
||||
blockchain, _, _, _ := PrepareXDCTestBlockChain(t, 11, params.TestXDPoSMockChainConfigWithV2Engine)
|
||||
blockchain, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 11, params.TestXDPoSMockChainConfigWithV2Engine)
|
||||
engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2
|
||||
|
||||
// Set round to 1
|
||||
|
|
@ -49,11 +49,11 @@ func TestTimeoutMessageHandlerSuccessfullyGenerateTCandSyncInfo(t *testing.T) {
|
|||
|
||||
assert.NotNil(t, syncInfoMsg)
|
||||
|
||||
// Should have QC, however, we did not inilise it, hence will show default empty value
|
||||
qc := syncInfoMsg.(utils.SyncInfo).HighestQuorumCert
|
||||
assert.NotNil(t, qc)
|
||||
// Shouldn't have QC, however, we did not inilise it, hence will show default empty value
|
||||
qc := syncInfoMsg.(*utils.SyncInfo).HighestQuorumCert
|
||||
assert.Nil(t, qc)
|
||||
|
||||
tc := syncInfoMsg.(utils.SyncInfo).HighestTimeoutCert
|
||||
tc := syncInfoMsg.(*utils.SyncInfo).HighestTimeoutCert
|
||||
assert.NotNil(t, tc)
|
||||
assert.Equal(t, tc.Round, utils.Round(1))
|
||||
sigatures := []utils.Signature{[]byte{1}, []byte{2}, []byte{3}}
|
||||
|
|
@ -62,7 +62,7 @@ func TestTimeoutMessageHandlerSuccessfullyGenerateTCandSyncInfo(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
|
||||
|
||||
// Set round to 3
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package consensus
|
||||
package tests
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
|
|
@ -26,7 +26,7 @@ func TestVoteMessageHandlerSuccessfullyGeneratedAndProcessQC(t *testing.T) {
|
|||
engineV2.SetNewRoundFaker(utils.Round(11), false)
|
||||
// Create two timeout message which will not reach vote pool threshold
|
||||
voteMsg := &utils.Vote{
|
||||
ProposedBlockInfo: *blockInfo,
|
||||
ProposedBlockInfo: blockInfo,
|
||||
Signature: []byte{1},
|
||||
}
|
||||
|
||||
|
|
@ -35,10 +35,10 @@ func TestVoteMessageHandlerSuccessfullyGeneratedAndProcessQC(t *testing.T) {
|
|||
currentRound, lockQuorumCert, highestQuorumCert := engineV2.GetProperties()
|
||||
// Inilised with nil and 0 round
|
||||
assert.Nil(t, lockQuorumCert)
|
||||
assert.Equal(t, utils.Round(0), highestQuorumCert.ProposedBlockInfo.Round)
|
||||
assert.Nil(t, highestQuorumCert)
|
||||
assert.Equal(t, utils.Round(11), currentRound)
|
||||
voteMsg = &utils.Vote{
|
||||
ProposedBlockInfo: *blockInfo,
|
||||
ProposedBlockInfo: blockInfo,
|
||||
Signature: []byte{2},
|
||||
}
|
||||
err = engineV2.VoteHandler(blockchain, voteMsg)
|
||||
|
|
@ -46,13 +46,13 @@ func TestVoteMessageHandlerSuccessfullyGeneratedAndProcessQC(t *testing.T) {
|
|||
currentRound, lockQuorumCert, highestQuorumCert = engineV2.GetProperties()
|
||||
// Still using the initlised value because we did not yet go to the next round
|
||||
assert.Nil(t, lockQuorumCert)
|
||||
assert.Equal(t, utils.Round(0), highestQuorumCert.ProposedBlockInfo.Round)
|
||||
assert.Nil(t, highestQuorumCert)
|
||||
|
||||
assert.Equal(t, utils.Round(11), currentRound)
|
||||
|
||||
// Create a vote message that should trigger vote pool hook and increment the round to 12
|
||||
voteMsg = &utils.Vote{
|
||||
ProposedBlockInfo: *blockInfo,
|
||||
ProposedBlockInfo: blockInfo,
|
||||
Signature: []byte{3},
|
||||
}
|
||||
|
||||
|
|
@ -80,7 +80,7 @@ func TestThrowErrorIfVoteMsgRoundNotEqualToCurrentRound(t *testing.T) {
|
|||
// Set round to 13
|
||||
engineV2.SetNewRoundFaker(utils.Round(13), false)
|
||||
voteMsg := &utils.Vote{
|
||||
ProposedBlockInfo: *blockInfo,
|
||||
ProposedBlockInfo: blockInfo,
|
||||
Signature: []byte{1},
|
||||
}
|
||||
|
||||
|
|
@ -112,7 +112,7 @@ func TestProcessVoteMsgThenTimeoutMsg(t *testing.T) {
|
|||
}
|
||||
// Create two vote message which will not reach vote pool threshold
|
||||
voteMsg := &utils.Vote{
|
||||
ProposedBlockInfo: *blockInfo,
|
||||
ProposedBlockInfo: blockInfo,
|
||||
Signature: []byte{1},
|
||||
}
|
||||
|
||||
|
|
@ -121,11 +121,11 @@ func TestProcessVoteMsgThenTimeoutMsg(t *testing.T) {
|
|||
currentRound, lockQuorumCert, highestQuorumCert := engineV2.GetProperties()
|
||||
// Inilised with nil and 0 round
|
||||
assert.Nil(t, lockQuorumCert)
|
||||
assert.Equal(t, utils.Round(0), highestQuorumCert.ProposedBlockInfo.Round)
|
||||
assert.Nil(t, highestQuorumCert)
|
||||
|
||||
assert.Equal(t, utils.Round(11), currentRound)
|
||||
voteMsg = &utils.Vote{
|
||||
ProposedBlockInfo: *blockInfo,
|
||||
ProposedBlockInfo: blockInfo,
|
||||
Signature: []byte{2},
|
||||
}
|
||||
err = engineV2.VoteHandler(blockchain, voteMsg)
|
||||
|
|
@ -135,7 +135,7 @@ func TestProcessVoteMsgThenTimeoutMsg(t *testing.T) {
|
|||
|
||||
// Create a vote message that should trigger vote pool hook
|
||||
voteMsg = &utils.Vote{
|
||||
ProposedBlockInfo: *blockInfo,
|
||||
ProposedBlockInfo: blockInfo,
|
||||
Signature: []byte{3},
|
||||
}
|
||||
|
||||
|
|
@ -194,11 +194,11 @@ func TestProcessVoteMsgThenTimeoutMsg(t *testing.T) {
|
|||
assert.NotNil(t, syncInfoMsg)
|
||||
|
||||
// Should have HighestQuorumCert from previous round votes
|
||||
qc := syncInfoMsg.(utils.SyncInfo).HighestQuorumCert
|
||||
qc := syncInfoMsg.(*utils.SyncInfo).HighestQuorumCert
|
||||
assert.NotNil(t, qc)
|
||||
assert.Equal(t, utils.Round(11), qc.ProposedBlockInfo.Round)
|
||||
|
||||
tc := syncInfoMsg.(utils.SyncInfo).HighestTimeoutCert
|
||||
tc := syncInfoMsg.(*utils.SyncInfo).HighestTimeoutCert
|
||||
assert.NotNil(t, tc)
|
||||
assert.Equal(t, utils.Round(12), tc.Round)
|
||||
sigatures := []utils.Signature{[]byte{1}, []byte{2}, []byte{3}}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package bfter
|
||||
package bft
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
|
@ -11,13 +11,17 @@ import (
|
|||
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/engines/engine_v2"
|
||||
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils"
|
||||
"github.com/XinFinOrg/XDPoSChain/core"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
// make different votes based on Signatures
|
||||
func makeVotes(n int) []utils.Vote {
|
||||
var votes []utils.Vote
|
||||
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
|
||||
}
|
||||
|
|
@ -101,7 +105,7 @@ func TestDuplicateVotes(t *testing.T) {
|
|||
atomic.AddUint32(&broadcastCounter, 1)
|
||||
}
|
||||
|
||||
vote := utils.Vote{}
|
||||
vote := utils.Vote{ProposedBlockInfo: &utils.BlockInfo{}}
|
||||
|
||||
// send twice
|
||||
tester.bfter.Vote(&vote)
|
||||
|
|
@ -132,7 +136,7 @@ func TestNotBoardcastInvalidVote(t *testing.T) {
|
|||
atomic.AddUint32(&broadcastCounter, 1)
|
||||
}
|
||||
|
||||
vote := utils.Vote{}
|
||||
vote := utils.Vote{ProposedBlockInfo: &utils.BlockInfo{}}
|
||||
tester.bfter.Vote(&vote)
|
||||
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
|
|
@ -143,3 +147,59 @@ func TestNotBoardcastInvalidVote(t *testing.T) {
|
|||
|
||||
// TODO: SyncInfo and Timeout Test, should be same as Vote.
|
||||
// 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 (
|
||||
"github.com/XinFinOrg/XDPoSChain/consensus"
|
||||
|
|
@ -79,9 +79,9 @@ func (b *Bfter) SetConsensusFuns(engine consensus.Engine) {
|
|||
|
||||
// TODO: rename
|
||||
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()) {
|
||||
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
|
||||
}
|
||||
|
||||
|
|
@ -116,6 +116,10 @@ func (b *Bfter) Timeout(timeout *utils.Timeout) error {
|
|||
|
||||
err = b.consensus.timeoutHandler(timeout)
|
||||
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)
|
||||
return err
|
||||
}
|
||||
|
|
@ -19,10 +19,11 @@ package fetcher
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/hashicorp/golang-lru"
|
||||
"math/rand"
|
||||
"time"
|
||||
|
||||
lru "github.com/hashicorp/golang-lru"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/common"
|
||||
"github.com/XinFinOrg/XDPoSChain/consensus"
|
||||
"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.
|
||||
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.
|
||||
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)
|
||||
knowns *lru.ARCCache
|
||||
// Callbacks
|
||||
getBlock blockRetrievalFn // Retrieves a block from the local chain
|
||||
verifyHeader headerVerifierFn // Checks if a block's headers have a valid proof of work
|
||||
broadcastBlock blockBroadcasterFn // Broadcasts a block to connected peers
|
||||
chainHeight chainHeightFn // Retrieves the current chain's height
|
||||
insertBlock blockInsertFn // Injects a batch of blocks into the chain
|
||||
prepareBlock blockPrepareFn
|
||||
dropPeer peerDropFn // Drops a peer for misbehaving
|
||||
getBlock blockRetrievalFn // Retrieves a block from the local chain
|
||||
verifyHeader headerVerifierFn // Checks if a block's headers have a valid proof of work
|
||||
handleProposedBlock proposeBlockHandlerFn // Consensus v2 specific: Hanle new proposed block
|
||||
broadcastBlock blockBroadcasterFn // Broadcasts a block to connected peers
|
||||
chainHeight chainHeightFn // Retrieves the current chain's height
|
||||
insertBlock blockInsertFn // Injects a batch of blocks into the chain
|
||||
prepareBlock blockPrepareFn
|
||||
dropPeer peerDropFn // Drops a peer for misbehaving
|
||||
|
||||
// Testing hooks
|
||||
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.
|
||||
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)
|
||||
return &Fetcher{
|
||||
notify: make(chan *announce),
|
||||
inject: make(chan *inject),
|
||||
blockFilter: make(chan chan []*types.Block),
|
||||
headerFilter: make(chan chan *headerFilterTask),
|
||||
bodyFilter: make(chan chan *bodyFilterTask),
|
||||
done: make(chan common.Hash),
|
||||
quit: make(chan struct{}),
|
||||
announces: make(map[string]int),
|
||||
announced: make(map[common.Hash][]*announce),
|
||||
fetching: make(map[common.Hash]*announce),
|
||||
fetched: make(map[common.Hash][]*announce),
|
||||
completing: make(map[common.Hash]*announce),
|
||||
queue: prque.New(),
|
||||
queues: make(map[string]int),
|
||||
queued: make(map[common.Hash]*inject),
|
||||
knowns: knownBlocks,
|
||||
getBlock: getBlock,
|
||||
verifyHeader: verifyHeader,
|
||||
broadcastBlock: broadcastBlock,
|
||||
chainHeight: chainHeight,
|
||||
insertBlock: insertBlock,
|
||||
prepareBlock: prepareBlock,
|
||||
dropPeer: dropPeer,
|
||||
notify: make(chan *announce),
|
||||
inject: make(chan *inject),
|
||||
blockFilter: make(chan chan []*types.Block),
|
||||
headerFilter: make(chan chan *headerFilterTask),
|
||||
bodyFilter: make(chan chan *bodyFilterTask),
|
||||
done: make(chan common.Hash),
|
||||
quit: make(chan struct{}),
|
||||
announces: make(map[string]int),
|
||||
announced: make(map[common.Hash][]*announce),
|
||||
fetching: make(map[common.Hash]*announce),
|
||||
fetched: make(map[common.Hash][]*announce),
|
||||
completing: make(map[common.Hash]*announce),
|
||||
queue: prque.New(),
|
||||
queues: make(map[string]int),
|
||||
queued: make(map[common.Hash]*inject),
|
||||
knowns: knownBlocks,
|
||||
getBlock: getBlock,
|
||||
verifyHeader: verifyHeader,
|
||||
handleProposedBlock: handleProposedBlock,
|
||||
broadcastBlock: broadcastBlock,
|
||||
chainHeight: chainHeight,
|
||||
insertBlock: insertBlock,
|
||||
prepareBlock: prepareBlock,
|
||||
dropPeer: dropPeer,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -721,6 +726,11 @@ func (f *Fetcher) insert(peer string, block *types.Block) {
|
|||
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
|
||||
propAnnounceOutTimer.UpdateSince(block.ReceivedAt)
|
||||
if !fastBroadCast {
|
||||
|
|
|
|||
|
|
@ -18,13 +18,14 @@ package fetcher
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
|
||||
"math/big"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/common"
|
||||
"github.com/XinFinOrg/XDPoSChain/consensus/ethash"
|
||||
"github.com/XinFinOrg/XDPoSChain/core"
|
||||
|
|
@ -92,7 +93,7 @@ func newTester() *fetcherTester {
|
|||
blocks: map[common.Hash]*types.Block{genesis.Hash(): genesis},
|
||||
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()
|
||||
|
||||
return tester
|
||||
|
|
@ -111,6 +112,10 @@ func (f *fetcherTester) verifyHeader(header *types.Header) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (f *fetcherTester) handleProposedBlock(header *types.Header) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// broadcastBlock is a nop placeholder for the block broadcasting.
|
||||
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
|
||||
// them, successfully importing into the local chain.
|
||||
func TestSequentialAnnouncements62(t *testing.T) { testSequentialAnnouncements(t, 62) }
|
||||
|
|
@ -317,12 +330,18 @@ func testSequentialAnnouncements(t *testing.T, protocol int) {
|
|||
imported <- block
|
||||
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-- {
|
||||
tester.fetcher.Notify("valid", hashes[i], uint64(len(hashes)-i-1), time.Now().Add(-arriveTimeout), headerFetcher, bodyFetcher)
|
||||
verifyImportEvent(t, imported, true)
|
||||
}
|
||||
verifyImportDone(t, imported)
|
||||
verifyProposeBlockHandlerCalled(t, handleProposedBlockChan)
|
||||
}
|
||||
|
||||
// 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/consensus"
|
||||
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS"
|
||||
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils"
|
||||
"github.com/XinFinOrg/XDPoSChain/consensus/misc"
|
||||
"github.com/XinFinOrg/XDPoSChain/core"
|
||||
"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/fetcher"
|
||||
"github.com/XinFinOrg/XDPoSChain/ethdb"
|
||||
|
|
@ -82,7 +83,7 @@ type ProtocolManager struct {
|
|||
downloader *downloader.Downloader
|
||||
fetcher *fetcher.Fetcher
|
||||
peers *peerSet
|
||||
bfter *bfter.Bfter
|
||||
bft *bft.Bfter
|
||||
|
||||
SubProtocols []p2p.Protocol
|
||||
|
||||
|
|
@ -198,6 +199,10 @@ func NewProtocolManager(config *params.ChainConfig, mode downloader.SyncMode, ne
|
|||
validator := func(header *types.Header) error {
|
||||
return engine.VerifyHeader(blockchain, header, true)
|
||||
}
|
||||
handleProposedBlock := func(header *types.Header) error {
|
||||
return engine.(*XDPoS.XDPoS).HandleProposedBlock(blockchain, header)
|
||||
}
|
||||
|
||||
heighter := func() uint64 {
|
||||
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
|
||||
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
|
||||
broadcasts := bfter.BroadcastFns{
|
||||
broadcasts := bft.BroadcastFns{
|
||||
Vote: manager.BroadcastVote,
|
||||
Timeout: manager.BroadcastTimeout,
|
||||
SyncInfo: manager.BroadcastSyncInfo,
|
||||
}
|
||||
manager.bfter = bfter.New(broadcasts, blockchain)
|
||||
manager.bft = bft.New(broadcasts, blockchain)
|
||||
if blockchain.Config().XDPoS != nil {
|
||||
manager.bfter.SetConsensusFuns(engine)
|
||||
manager.bft.SetConsensusFuns(engine)
|
||||
}
|
||||
|
||||
return manager, nil
|
||||
|
|
@ -827,7 +832,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
|
|||
}
|
||||
// Mark the peer as owning the vote and process it
|
||||
p.MarkVote(vote.Hash())
|
||||
pm.bfter.Vote(&vote)
|
||||
pm.bft.Vote(&vote)
|
||||
case msg.Code == TimeoutMsg:
|
||||
var timeout utils.Timeout
|
||||
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
|
||||
p.MarkTimeout(timeout.Hash())
|
||||
pm.bfter.Timeout(&timeout)
|
||||
pm.bft.Timeout(&timeout)
|
||||
case msg.Code == SyncInfoMsg:
|
||||
var syncInfo utils.SyncInfo
|
||||
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
|
||||
p.MarkSyncInfo(syncInfo.Hash())
|
||||
pm.bfter.SyncInfo(&syncInfo)
|
||||
pm.bft.SyncInfo(&syncInfo)
|
||||
|
||||
default:
|
||||
return errResp(ErrInvalidMsgCode, "%v", msg.Code)
|
||||
|
|
@ -904,7 +909,7 @@ func (pm *ProtocolManager) BroadcastVote(vote *utils.Vote) {
|
|||
for _, peer := range peers {
|
||||
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
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ var ProtocolName = "eth"
|
|||
var ProtocolVersions = []uint{eth63, eth62}
|
||||
|
||||
// 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
|
||||
|
||||
|
|
|
|||
|
|
@ -134,9 +134,9 @@ func (pm *ProtocolManager) txsyncLoop() {
|
|||
func (pm *ProtocolManager) syncer() {
|
||||
// Start and ensure cleanup of sync mechanisms
|
||||
pm.fetcher.Start()
|
||||
pm.bfter.Start()
|
||||
pm.bft.Start()
|
||||
defer pm.fetcher.Stop()
|
||||
defer pm.bfter.Stop()
|
||||
defer pm.bft.Stop()
|
||||
defer pm.downloader.Terminate()
|
||||
|
||||
// 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/olebedev/go-duktape.v3 v3.0.0-20190213234257-ec84240a7772
|
||||
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/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.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg=
|
||||
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/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/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/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/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=
|
||||
|
|
@ -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.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
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=
|
||||
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
|
||||
if self.config.XDPoS != nil {
|
||||
// get masternodes set from latest checkpoint
|
||||
// TODO: refactor on yourturn with below condition for v1 v2
|
||||
c := self.engine.(*XDPoS.XDPoS)
|
||||
len, preIndex, curIndex, ok, err := c.YourTurn(self.chain, parent.Header(), self.coinbase)
|
||||
if err != nil {
|
||||
|
|
@ -581,6 +582,10 @@ func (self *worker) commitNewWork() {
|
|||
}
|
||||
|
||||
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)
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,11 +36,11 @@ var (
|
|||
|
||||
var (
|
||||
XDPoSV2Config = &V2{
|
||||
TimeoutWorkerDuration: 50000,
|
||||
TimeoutWorkerDuration: 50,
|
||||
CertThreshold: common.MaxMasternodesV2*2/3 + 1,
|
||||
}
|
||||
TestXDPoSV2Config = &V2{
|
||||
TimeoutWorkerDuration: 5000,
|
||||
TimeoutWorkerDuration: 5,
|
||||
CertThreshold: 3,
|
||||
}
|
||||
|
||||
|
|
@ -195,12 +195,12 @@ type XDPoSConfig struct {
|
|||
Gap uint64 `json:"gap"` // Gap time preparing for the next epoch
|
||||
FoudationWalletAddr common.Address `json:"foudationWalletAddr"` // Foundation Address Wallet
|
||||
SkipValidation bool //Skip Block Validation for testing purpose
|
||||
XDPoSV2Block *big.Int
|
||||
V2 V2
|
||||
XDPoSV2Block *big.Int `json:"v2Block"`
|
||||
V2 V2 `json:"v2"`
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue