mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-16 18:00:46 +00:00
Xin 147 initial for both first v2 block and further, also introduce getExtraField function (#64)
* refactor initial and introduce getExtraField function * add test for initial * refactor snapshot * initial first snapshot only
This commit is contained in:
parent
7fca1a627a
commit
a4b362ae9a
8 changed files with 295 additions and 185 deletions
|
|
@ -63,8 +63,6 @@ type XDPoS struct {
|
||||||
// The exact consensus engine with different versions
|
// The exact consensus engine with different versions
|
||||||
EngineV1 *engine_v1.XDPoS_v1
|
EngineV1 *engine_v1.XDPoS_v1
|
||||||
EngineV2 *engine_v2.XDPoS_v2
|
EngineV2 *engine_v2.XDPoS_v2
|
||||||
|
|
||||||
isV2Initilised bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// New creates a XDPoS delegated-proof-of-stake consensus engine with the initial
|
// New creates a XDPoS delegated-proof-of-stake consensus engine with the initial
|
||||||
|
|
@ -92,7 +90,6 @@ func New(config *params.XDPoSConfig, db ethdb.Database) *XDPoS {
|
||||||
signingTxsCache: signingTxsCache,
|
signingTxsCache: signingTxsCache,
|
||||||
EngineV1: engine_v1.New(config, db),
|
EngineV1: engine_v1.New(config, db),
|
||||||
EngineV2: engine_v2.New(config, db, waitPeriodCh),
|
EngineV2: engine_v2.New(config, db, waitPeriodCh),
|
||||||
isV2Initilised: false,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -317,26 +314,6 @@ func (x *XDPoS) GetMasternodesByNumber(chain consensus.ChainReader, blockNumber
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *XDPoS) YourTurn(chain consensus.ChainReader, parent *types.Header, signer common.Address) (bool, error) {
|
func (x *XDPoS) YourTurn(chain consensus.ChainReader, parent *types.Header, signer common.Address) (bool, error) {
|
||||||
if x.config.V2.SwitchBlock != nil && parent.Number.Cmp(x.config.V2.SwitchBlock) != -1 {
|
|
||||||
if parent.Number.Cmp(x.config.V2.SwitchBlock) == 0 {
|
|
||||||
err := x.initialV2FromLastV1(chain, parent)
|
|
||||||
if err != nil {
|
|
||||||
log.Error("[YourTurn] Error while initilising first v2 block from the last v1 block", "ParentBlockHash", parent.Hash(), "Error", err)
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
x.isV2Initilised = true
|
|
||||||
} else if parent.Number.Cmp(x.config.V2.SwitchBlock) == 1 && !x.isV2Initilised { // TODO: XIN-147, temporary solution for now
|
|
||||||
log.Info("[YourTurn] Initilising v2 after sync or restarted", "currentBlockNum", chain.CurrentHeader().Number, "currentBlockHash", chain.CurrentHeader().Hash())
|
|
||||||
lastv1BlockHeader := chain.GetHeaderByNumber(x.config.V2.SwitchBlock.Uint64())
|
|
||||||
err := x.initialV2FromLastV1(chain, lastv1BlockHeader)
|
|
||||||
if err != nil {
|
|
||||||
log.Error("[YourTurn] Temporary solution! Error when initialise v2", "lastv1BlockHeader", lastv1BlockHeader.Hash(), "Error", err)
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
x.isV2Initilised = true
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
switch x.config.BlockConsensusVersion(big.NewInt(parent.Number.Int64() + 1)) {
|
switch x.config.BlockConsensusVersion(big.NewInt(parent.Number.Int64() + 1)) {
|
||||||
case params.ConsensusEngineVersion2:
|
case params.ConsensusEngineVersion2:
|
||||||
return x.EngineV2.YourTurn(chain, parent, signer)
|
return x.EngineV2.YourTurn(chain, parent, signer)
|
||||||
|
|
@ -353,6 +330,7 @@ func (x *XDPoS) GetValidator(creator common.Address, chain consensus.ChainReader
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *XDPoS) UpdateMasternodes(chain consensus.ChainReader, header *types.Header, ms []utils.Masternode) error {
|
func (x *XDPoS) UpdateMasternodes(chain consensus.ChainReader, header *types.Header, ms []utils.Masternode) error {
|
||||||
|
// fmt.Println("UpdateMasternodes")
|
||||||
switch x.config.BlockConsensusVersion(header.Number) {
|
switch x.config.BlockConsensusVersion(header.Number) {
|
||||||
case params.ConsensusEngineVersion2:
|
case params.ConsensusEngineVersion2:
|
||||||
return x.EngineV2.UpdateMasternodes(chain, header, ms)
|
return x.EngineV2.UpdateMasternodes(chain, header, ms)
|
||||||
|
|
@ -509,15 +487,3 @@ func (x *XDPoS) CacheSigningTxs(hash common.Hash, txs []*types.Transaction) []*t
|
||||||
func (x *XDPoS) GetCachedSigningTxs(hash common.Hash) (interface{}, bool) {
|
func (x *XDPoS) GetCachedSigningTxs(hash common.Hash) (interface{}, bool) {
|
||||||
return x.signingTxsCache.Get(hash)
|
return x.signingTxsCache.Get(hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
// V2 specific helper function to initilise consensus engine variables
|
|
||||||
func (x *XDPoS) initialV2FromLastV1(chain consensus.ChainReader, header *types.Header) error {
|
|
||||||
checkpointBlockNumber := header.Number.Uint64() - header.Number.Uint64()%x.config.Epoch
|
|
||||||
checkpointHeader := chain.GetHeaderByNumber(checkpointBlockNumber)
|
|
||||||
masternodes := x.EngineV1.GetMasternodesFromCheckpointHeader(checkpointHeader)
|
|
||||||
err := x.EngineV2.Initial(chain, masternodes)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -28,8 +28,9 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type XDPoS_v2 struct {
|
type XDPoS_v2 struct {
|
||||||
config *params.XDPoSConfig // Consensus engine configuration parameters
|
config *params.XDPoSConfig // Consensus engine configuration parameters
|
||||||
db ethdb.Database // Database to store and retrieve snapshot checkpoints
|
db ethdb.Database // Database to store and retrieve snapshot checkpoints
|
||||||
|
isInitilised bool // status of v2 variables
|
||||||
|
|
||||||
snapshots *lru.ARCCache // Snapshots for gap block
|
snapshots *lru.ARCCache // Snapshots for gap block
|
||||||
signatures *lru.ARCCache // Signatures of recent blocks to speed up mining
|
signatures *lru.ARCCache // Signatures of recent blocks to speed up mining
|
||||||
|
|
@ -74,8 +75,10 @@ func New(config *params.XDPoSConfig, db ethdb.Database, waitPeriodCh chan int) *
|
||||||
|
|
||||||
votePool := utils.NewPool(config.V2.CertThreshold)
|
votePool := utils.NewPool(config.V2.CertThreshold)
|
||||||
engine := &XDPoS_v2{
|
engine := &XDPoS_v2{
|
||||||
config: config,
|
config: config,
|
||||||
db: db,
|
db: db,
|
||||||
|
isInitilised: false,
|
||||||
|
|
||||||
signatures: signatures,
|
signatures: signatures,
|
||||||
|
|
||||||
verifiedHeaders: verifiedHeaders,
|
verifiedHeaders: verifiedHeaders,
|
||||||
|
|
@ -122,7 +125,7 @@ func (x *XDPoS_v2) SignHash(header *types.Header) (hash common.Hash) {
|
||||||
return sigHash(header)
|
return sigHash(header)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *XDPoS_v2) Initial(chain consensus.ChainReader, masternodes []common.Address) error {
|
func (x *XDPoS_v2) Initial(chain consensus.ChainReader, header *types.Header) error {
|
||||||
log.Info("[Initial] initial v2 related parameters")
|
log.Info("[Initial] initial v2 related parameters")
|
||||||
|
|
||||||
if x.highestQuorumCert.ProposedBlockInfo.Hash != (common.Hash{}) { // already initialized
|
if x.highestQuorumCert.ProposedBlockInfo.Hash != (common.Hash{}) { // already initialized
|
||||||
|
|
@ -130,36 +133,59 @@ func (x *XDPoS_v2) Initial(chain consensus.ChainReader, masternodes []common.Add
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
x.lock.Lock()
|
var quorumCert *utils.QuorumCert
|
||||||
defer x.lock.Unlock()
|
var err error
|
||||||
// Check header if it is the first consensus v2 block, if so, assign initial values to current round and highestQC
|
|
||||||
|
|
||||||
log.Info("[Initial] highest QC for consensus v2 first block")
|
if header.Number.Int64() == x.config.V2.SwitchBlock.Int64() {
|
||||||
// Generate new parent blockInfo and put it into QC
|
log.Info("[Initial] highest QC for consensus v2 first block")
|
||||||
// TODO: XIN-147 to initilise V2 engine in a more dynamic way
|
blockInfo := &utils.BlockInfo{
|
||||||
firstV2BlockHeader := chain.GetHeaderByNumber(x.config.V2.SwitchBlock.Uint64())
|
Hash: header.Hash(),
|
||||||
blockInfo := &utils.BlockInfo{
|
Round: utils.Round(0),
|
||||||
Hash: firstV2BlockHeader.Hash(),
|
Number: header.Number,
|
||||||
Round: utils.Round(0),
|
}
|
||||||
Number: firstV2BlockHeader.Number,
|
quorumCert = &utils.QuorumCert{
|
||||||
|
ProposedBlockInfo: blockInfo,
|
||||||
|
Signatures: nil,
|
||||||
|
}
|
||||||
|
|
||||||
|
// can not call processQC because round is equal to default
|
||||||
|
x.currentRound = 1
|
||||||
|
x.highestQuorumCert = quorumCert
|
||||||
|
|
||||||
|
} else {
|
||||||
|
log.Info("[Initial] highest QC from current header")
|
||||||
|
quorumCert, _, _, err = x.getExtraFields(header)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = x.processQC(chain, quorumCert)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
quorumCert := &utils.QuorumCert{
|
|
||||||
ProposedBlockInfo: blockInfo,
|
|
||||||
Signatures: nil,
|
|
||||||
}
|
|
||||||
x.currentRound = 1
|
|
||||||
x.highestQuorumCert = quorumCert
|
|
||||||
|
|
||||||
// Initial snapshot
|
// Initial first v2 snapshot
|
||||||
lastGapNum := firstV2BlockHeader.Number.Uint64() - firstV2BlockHeader.Number.Uint64()%x.config.Epoch - x.config.Gap
|
if header.Number.Uint64() < x.config.V2.SwitchBlock.Uint64()+x.config.Gap {
|
||||||
lastGapHeader := chain.GetHeaderByNumber(lastGapNum)
|
|
||||||
|
|
||||||
snap := newSnapshot(lastGapNum, lastGapHeader.Hash(), x.currentRound, x.highestQuorumCert, masternodes)
|
checkpointBlockNumber := header.Number.Uint64() - header.Number.Uint64()%x.config.Epoch
|
||||||
x.snapshots.Add(snap.Hash, snap)
|
checkpointHeader := chain.GetHeaderByNumber(checkpointBlockNumber)
|
||||||
err := storeSnapshot(snap, x.db)
|
|
||||||
if err != nil {
|
lastGapNum := checkpointBlockNumber - x.config.Gap
|
||||||
log.Error("[Initial] Error while storo snapshot", "error", err)
|
lastGapHeader := chain.GetHeaderByNumber(lastGapNum)
|
||||||
return err
|
|
||||||
|
log.Info("[Initial] init first snapshot")
|
||||||
|
_, _, masternodes, err := x.getExtraFields(checkpointHeader)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("[Initial] Error while get masternodes", "error", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
snap := newSnapshot(lastGapNum, lastGapHeader.Hash(), masternodes)
|
||||||
|
x.snapshots.Add(snap.Hash, snap)
|
||||||
|
err = storeSnapshot(snap, x.db)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("[Initial] Error while store snapshot", "error", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initial timeout
|
// Initial timeout
|
||||||
|
|
@ -173,6 +199,7 @@ func (x *XDPoS_v2) Initial(chain consensus.ChainReader, masternodes []common.Add
|
||||||
x.timeoutWorker.Reset(chain)
|
x.timeoutWorker.Reset(chain)
|
||||||
|
|
||||||
log.Info("[Initial] finish initialisation")
|
log.Info("[Initial] finish initialisation")
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -186,6 +213,7 @@ func (x *XDPoS_v2) Prepare(chain consensus.ChainReader, header *types.Header) er
|
||||||
x.lock.RUnlock()
|
x.lock.RUnlock()
|
||||||
|
|
||||||
if header.ParentHash != highestQC.ProposedBlockInfo.Hash {
|
if header.ParentHash != highestQC.ProposedBlockInfo.Hash {
|
||||||
|
fmt.Println("[Prepare] parent hash and QC hash does not match", "blockNum", header.Number, "parentHash", header.ParentHash, "QCHash", highestQC.ProposedBlockInfo.Hash, "QCNumber", highestQC.ProposedBlockInfo.Number)
|
||||||
log.Warn("[Prepare] parent hash and QC hash does not match", "blockNum", header.Number, "parentHash", header.ParentHash, "QCHash", highestQC.ProposedBlockInfo.Hash, "QCNumber", highestQC.ProposedBlockInfo.Number)
|
log.Warn("[Prepare] parent hash and QC hash does not match", "blockNum", header.Number, "parentHash", header.ParentHash, "QCHash", highestQC.ProposedBlockInfo.Hash, "QCNumber", highestQC.ProposedBlockInfo.Number)
|
||||||
return consensus.ErrNotReadyToPropose
|
return consensus.ErrNotReadyToPropose
|
||||||
}
|
}
|
||||||
|
|
@ -303,33 +331,12 @@ func (x *XDPoS_v2) Seal(chain consensus.ChainReader, block *types.Block, stop <-
|
||||||
if number == 0 {
|
if number == 0 {
|
||||||
return nil, utils.ErrUnknownBlock
|
return nil, utils.ErrUnknownBlock
|
||||||
}
|
}
|
||||||
// For 0-period chains, refuse to seal empty blocks (no reward but would spin sealing)
|
|
||||||
// checkpoint blocks have no tx
|
|
||||||
isEpochSwitch, _, err := x.IsEpochSwitch(header)
|
|
||||||
if err != nil {
|
|
||||||
log.Error("[Seal] Error while checking whether header is a epoch switch during sealing", "Header", header)
|
|
||||||
}
|
|
||||||
if x.config.Period == 0 && len(block.Transactions()) == 0 && !isEpochSwitch {
|
|
||||||
return nil, utils.ErrWaitTransactions
|
|
||||||
}
|
|
||||||
// Don't hold the signer fields for the entire sealing procedure
|
// Don't hold the signer fields for the entire sealing procedure
|
||||||
x.signLock.RLock()
|
x.signLock.RLock()
|
||||||
signer, signFn := x.signer, x.signFn
|
signer, signFn := x.signer, x.signFn
|
||||||
x.signLock.RUnlock()
|
x.signLock.RUnlock()
|
||||||
|
|
||||||
// Bail out if we're unauthorized to sign a block
|
|
||||||
masternodes := x.GetMasternodes(chain, header)
|
|
||||||
valid := false
|
|
||||||
for _, m := range masternodes {
|
|
||||||
if m == signer {
|
|
||||||
valid = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !valid {
|
|
||||||
return nil, utils.ErrUnauthorized
|
|
||||||
}
|
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case <-stop:
|
case <-stop:
|
||||||
return nil, nil
|
return nil, nil
|
||||||
|
|
@ -364,6 +371,15 @@ func (x *XDPoS_v2) YourTurn(chain consensus.ChainReader, parent *types.Header, s
|
||||||
x.lock.RLock()
|
x.lock.RLock()
|
||||||
defer x.lock.RUnlock()
|
defer x.lock.RUnlock()
|
||||||
|
|
||||||
|
if !x.isInitilised {
|
||||||
|
err := x.Initial(chain, parent)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("[YourTurn] Error while initialising last v2 variables", "ParentBlockHash", parent.Hash(), "Error", err)
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
x.isInitilised = true
|
||||||
|
}
|
||||||
|
|
||||||
waitedTime := time.Now().Unix() - parent.Time.Int64()
|
waitedTime := time.Now().Unix() - parent.Time.Int64()
|
||||||
if waitedTime < int64(x.config.V2.MinePeriod) {
|
if waitedTime < int64(x.config.V2.MinePeriod) {
|
||||||
log.Trace("[YourTurn] wait after mine period", "minePeriod", x.config.V2.MinePeriod, "waitedTime", waitedTime)
|
log.Trace("[YourTurn] wait after mine period", "minePeriod", x.config.V2.MinePeriod, "waitedTime", waitedTime)
|
||||||
|
|
@ -379,13 +395,12 @@ func (x *XDPoS_v2) YourTurn(chain consensus.ChainReader, parent *types.Header, s
|
||||||
var masterNodes []common.Address
|
var masterNodes []common.Address
|
||||||
if isEpochSwitch {
|
if isEpochSwitch {
|
||||||
if x.config.V2.SwitchBlock.Cmp(parent.Number) == 0 {
|
if x.config.V2.SwitchBlock.Cmp(parent.Number) == 0 {
|
||||||
snap, err := x.getSnapshot(chain, x.config.V2.SwitchBlock.Uint64(), false)
|
// the initial master nodes of v1->v2 switch contains penalties node
|
||||||
|
_, _, masterNodes, err = x.getExtraFields(parent)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("[YourTurn] Cannot find snapshot at gap num of last V1", "err", err, "number", x.config.V2.SwitchBlock.Uint64())
|
log.Error("[YourTurn] Cannot find snapshot at gap num of last V1", "err", err, "number", x.config.V2.SwitchBlock.Uint64())
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
// the initial master nodes of v1->v2 switch contains penalties node
|
|
||||||
masterNodes = snap.NextEpochMasterNodes
|
|
||||||
} else {
|
} else {
|
||||||
masterNodes, _, err = x.calcMasternodes(chain, big.NewInt(0).Add(parent.Number, big.NewInt(1)), parent.Hash())
|
masterNodes, _, err = x.calcMasternodes(chain, big.NewInt(0).Add(parent.Number, big.NewInt(1)), parent.Hash())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -400,36 +415,38 @@ func (x *XDPoS_v2) YourTurn(chain consensus.ChainReader, parent *types.Header, s
|
||||||
|
|
||||||
if len(masterNodes) == 0 {
|
if len(masterNodes) == 0 {
|
||||||
log.Error("[YourTurn] Fail to find any master nodes from current block round epoch", "Hash", parent.Hash(), "CurrentRound", round, "Number", parent.Number)
|
log.Error("[YourTurn] Fail to find any master nodes from current block round epoch", "Hash", parent.Hash(), "CurrentRound", round, "Number", parent.Number)
|
||||||
return false, errors.New("Masternodes not found")
|
return false, errors.New("masternodes not found")
|
||||||
}
|
}
|
||||||
leaderIndex := uint64(round) % x.config.Epoch % uint64(len(masterNodes))
|
|
||||||
|
|
||||||
curIndex := utils.Position(masterNodes, signer)
|
curIndex := utils.Position(masterNodes, signer)
|
||||||
if signer == x.signer {
|
if curIndex == -1 {
|
||||||
log.Debug("[YourTurn] masterNodes cycle info", "number of masternodes", len(masterNodes), "current", signer, "position", curIndex, "parentBlock", parent)
|
log.Debug("[YourTurn] Not authorised signer", "MN", masterNodes, "Hash", parent.Hash(), "signer", signer)
|
||||||
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, s := range masterNodes {
|
for i, s := range masterNodes {
|
||||||
log.Debug("[YourTurn] Masternode:", "index", i, "address", s.String(), "parentBlockNum", parent.Number)
|
log.Debug("[YourTurn] Masternode:", "index", i, "address", s.String(), "parentBlockNum", parent.Number)
|
||||||
}
|
}
|
||||||
|
|
||||||
if masterNodes[leaderIndex] == signer {
|
leaderIndex := uint64(round) % x.config.Epoch % uint64(len(masterNodes))
|
||||||
return true, nil
|
if masterNodes[leaderIndex] != signer {
|
||||||
|
log.Debug("[YourTurn] Not my turn", "curIndex", curIndex, "leaderIndex", leaderIndex, "Hash", parent.Hash(), "masterNodes[leaderIndex]", masterNodes[leaderIndex], "signer", signer)
|
||||||
|
return false, nil
|
||||||
}
|
}
|
||||||
log.Warn("[YourTurn] Not authorised signer", "signer", signer, "MN", masterNodes, "Hash", parent.Hash().Hex(), "masterNodes[leaderIndex]", masterNodes[leaderIndex], "signer", signer)
|
|
||||||
return false, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *XDPoS_v2) IsAuthorisedAddress(chain consensus.ChainReader, header *types.Header, address common.Address) bool {
|
func (x *XDPoS_v2) IsAuthorisedAddress(chain consensus.ChainReader, header *types.Header, address common.Address) bool {
|
||||||
x.lock.RLock()
|
x.lock.RLock()
|
||||||
defer x.lock.RUnlock()
|
defer x.lock.RUnlock()
|
||||||
|
|
||||||
var extraField utils.ExtraFields_v2
|
_, round, _, err := x.getExtraFields(header)
|
||||||
err := utils.DecodeBytesExtraFields(header.Extra, &extraField)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("[IsAuthorisedAddress] Fail to decode v2 extra data", "Hash", header.Hash().Hex(), "Extra", header.Extra, "Error", err)
|
log.Error("[IsAuthorisedAddress] Fail to decode v2 extra data", "Hash", header.Hash().Hex(), "Extra", header.Extra, "Error", err)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
blockRound := extraField.Round
|
blockRound := round
|
||||||
|
|
||||||
masterNodes := x.GetMasternodes(chain, header)
|
masterNodes := x.GetMasternodes(chain, header)
|
||||||
|
|
||||||
|
|
@ -483,7 +500,7 @@ func (x *XDPoS_v2) getSnapshot(chain consensus.ChainReader, number uint64, isGap
|
||||||
return snap, nil
|
return snap, nil
|
||||||
}
|
}
|
||||||
// If an on-disk checkpoint snapshot can be found, use that
|
// If an on-disk checkpoint snapshot can be found, use that
|
||||||
snap, err := loadSnapshot(x.signatures, x.db, gapBlockHash)
|
snap, err := loadSnapshot(x.db, gapBlockHash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Cannot find snapshot from last gap block", "err", err, "number", gapBlockNum, "hash", gapBlockHash)
|
log.Error("Cannot find snapshot from last gap block", "err", err, "number", gapBlockNum, "hash", gapBlockHash)
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -504,7 +521,7 @@ func (x *XDPoS_v2) UpdateMasternodes(chain consensus.ChainReader, header *types.
|
||||||
}
|
}
|
||||||
|
|
||||||
x.lock.RLock()
|
x.lock.RLock()
|
||||||
snap := newSnapshot(number, header.Hash(), x.currentRound, x.highestQuorumCert, masterNodes)
|
snap := newSnapshot(number, header.Hash(), masterNodes)
|
||||||
x.lock.RUnlock()
|
x.lock.RUnlock()
|
||||||
|
|
||||||
err := storeSnapshot(snap, x.db)
|
err := storeSnapshot(snap, x.db)
|
||||||
|
|
@ -572,12 +589,12 @@ func (x *XDPoS_v2) verifyHeader(chain consensus.ChainReader, header *types.Heade
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify this is truely a v2 block first
|
// Verify this is truely a v2 block first
|
||||||
var decodedExtraField utils.ExtraFields_v2
|
|
||||||
err := utils.DecodeBytesExtraFields(header.Extra, &decodedExtraField)
|
quorumCert, _, _, err := x.getExtraFields(header)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return utils.ErrInvalidV2Extra
|
return utils.ErrInvalidV2Extra
|
||||||
}
|
}
|
||||||
quorumCert := decodedExtraField.QuorumCert
|
|
||||||
err = x.verifyQC(chain, quorumCert)
|
err = x.verifyQC(chain, quorumCert)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warn("[verifyHeader] fail to verify QC", "QCNumber", quorumCert.ProposedBlockInfo.Number, "QCsigLength", len(quorumCert.Signatures))
|
log.Warn("[verifyHeader] fail to verify QC", "QCNumber", quorumCert.ProposedBlockInfo.Number, "QCsigLength", len(quorumCert.Signatures))
|
||||||
|
|
@ -742,8 +759,9 @@ func (x *XDPoS_v2) voteHandler(chain consensus.ChainReader, voteMsg *utils.Vote)
|
||||||
|
|
||||||
// Collect vote
|
// Collect vote
|
||||||
thresholdReached, numberOfVotesInPool, pooledVotes := x.votePool.Add(voteMsg)
|
thresholdReached, numberOfVotesInPool, pooledVotes := x.votePool.Add(voteMsg)
|
||||||
|
log.Info("[voteHandler] collect votes", "number", numberOfVotesInPool)
|
||||||
if thresholdReached {
|
if thresholdReached {
|
||||||
log.Info(fmt.Sprintf("Vote pool threashold reached: %v, number of items in the pool: %v", thresholdReached, numberOfVotesInPool))
|
log.Info(fmt.Sprintf("[voteHandler] Vote pool threashold reached: %v, number of items in the pool: %v", thresholdReached, numberOfVotesInPool))
|
||||||
|
|
||||||
// Check if the block already exist, otherwise we try luck with the next vote
|
// Check if the block already exist, otherwise we try luck with the next vote
|
||||||
proposedBlockHeader := chain.GetHeaderByHash(voteMsg.ProposedBlockInfo.Hash)
|
proposedBlockHeader := chain.GetHeaderByHash(voteMsg.ProposedBlockInfo.Hash)
|
||||||
|
|
@ -868,6 +886,8 @@ func (x *XDPoS_v2) timeoutHandler(blockChainReader consensus.ChainReader, timeou
|
||||||
}
|
}
|
||||||
// Collect timeout, generate TC
|
// Collect timeout, generate TC
|
||||||
isThresholdReached, numberOfTimeoutsInPool, pooledTimeouts := x.timeoutPool.Add(timeout)
|
isThresholdReached, numberOfTimeoutsInPool, pooledTimeouts := x.timeoutPool.Add(timeout)
|
||||||
|
log.Info("[timeoutHandler] collect timeout", "number", numberOfTimeoutsInPool)
|
||||||
|
|
||||||
// Threshold reached
|
// Threshold reached
|
||||||
if isThresholdReached {
|
if isThresholdReached {
|
||||||
log.Info(fmt.Sprintf("Timeout pool threashold reached: %v, number of items in the pool: %v", isThresholdReached, numberOfTimeoutsInPool))
|
log.Info(fmt.Sprintf("Timeout pool threashold reached: %v, number of items in the pool: %v", isThresholdReached, numberOfTimeoutsInPool))
|
||||||
|
|
@ -928,13 +948,10 @@ func (x *XDPoS_v2) ProposedBlockHandler(chain consensus.ChainReader, blockHeader
|
||||||
5. sendVote()
|
5. sendVote()
|
||||||
*/
|
*/
|
||||||
// Get QC and Round from Extra
|
// Get QC and Round from Extra
|
||||||
var decodedExtraField utils.ExtraFields_v2
|
quorumCert, round, _, err := x.getExtraFields(blockHeader)
|
||||||
err := utils.DecodeBytesExtraFields(blockHeader.Extra, &decodedExtraField)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
quorumCert := decodedExtraField.QuorumCert
|
|
||||||
round := decodedExtraField.Round
|
|
||||||
|
|
||||||
err = x.verifyQC(chain, quorumCert)
|
err = x.verifyQC(chain, quorumCert)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -1001,15 +1018,15 @@ func (x *XDPoS_v2) VerifyBlockInfo(blockChainReader consensus.ChainReader, block
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
// Check round
|
// Check round
|
||||||
var decodedExtraField utils.ExtraFields_v2
|
|
||||||
err := utils.DecodeBytesExtraFields(blockHeader.Extra, &decodedExtraField)
|
_, round, _, err := x.getExtraFields(blockHeader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("[VerifyBlockInfo] Fail to decode extra field", "BlockInfoHash", blockInfo.Hash.Hex(), "BlockInfoNum", blockInfo.Number, "BlockInfoRound", blockInfo.Round, "blockHeaderNum", blockHeader.Number)
|
log.Error("[VerifyBlockInfo] Fail to decode extra field", "BlockInfoHash", blockInfo.Hash.Hex(), "BlockInfoNum", blockInfo.Number, "BlockInfoRound", blockInfo.Round, "blockHeaderNum", blockHeader.Number)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if decodedExtraField.Round != blockInfo.Round {
|
if round != blockInfo.Round {
|
||||||
log.Warn("[VerifyBlockInfo] Block extra round mismatch with blockInfo", "BlockInfoHash", blockInfo.Hash.Hex(), "BlockInfoNum", blockInfo.Number, "BlockInfoRound", blockInfo.Round, "blockHeaderNum", blockHeader.Number, "blockRound", decodedExtraField.Round)
|
log.Warn("[VerifyBlockInfo] Block extra round mismatch with blockInfo", "BlockInfoHash", blockInfo.Hash.Hex(), "BlockInfoNum", blockInfo.Number, "BlockInfoRound", blockInfo.Round, "blockHeaderNum", blockHeader.Number, "blockRound", round)
|
||||||
return fmt.Errorf("[VerifyBlockInfo] chain block's round does not match from blockInfo at hash: %v and block round: %v, blockInfo Round: %v", blockInfo.Hash.Hex(), decodedExtraField.Round, blockInfo.Round)
|
return fmt.Errorf("[VerifyBlockInfo] chain block's round does not match from blockInfo at hash: %v and block round: %v, blockInfo Round: %v", blockInfo.Hash.Hex(), round, blockInfo.Round)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -1046,6 +1063,12 @@ func (x *XDPoS_v2) verifyQC(blockChainReader consensus.ChainReader, quorumCert *
|
||||||
return utils.ErrInvalidQC
|
return utils.ErrInvalidQC
|
||||||
}
|
}
|
||||||
|
|
||||||
|
epochInfo, err = x.getEpochSwitchInfo(blockChainReader, nil, quorumCert.ProposedBlockInfo.Hash)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("[verifyQC] Error when getting epoch switch Info to verify QC", "Error", err)
|
||||||
|
return fmt.Errorf("Fail to verify QC due to failure in getting epoch switch info")
|
||||||
|
}
|
||||||
|
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
wg.Add(len(signatures))
|
wg.Add(len(signatures))
|
||||||
var haveError error
|
var haveError error
|
||||||
|
|
@ -1148,16 +1171,15 @@ func (x *XDPoS_v2) processQC(blockChainReader consensus.ChainReader, quorumCert
|
||||||
}
|
}
|
||||||
if proposedBlockHeader.Number.Cmp(x.config.V2.SwitchBlock) > 0 {
|
if proposedBlockHeader.Number.Cmp(x.config.V2.SwitchBlock) > 0 {
|
||||||
// Extra field contain parent information
|
// Extra field contain parent information
|
||||||
var decodedExtraField utils.ExtraFields_v2
|
quorumCert, round, _, err := x.getExtraFields(proposedBlockHeader)
|
||||||
err := utils.DecodeBytesExtraFields(proposedBlockHeader.Extra, &decodedExtraField)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if x.lockQuorumCert == nil || decodedExtraField.QuorumCert.ProposedBlockInfo.Round > x.lockQuorumCert.ProposedBlockInfo.Round {
|
if x.lockQuorumCert == nil || quorumCert.ProposedBlockInfo.Round > x.lockQuorumCert.ProposedBlockInfo.Round {
|
||||||
x.lockQuorumCert = decodedExtraField.QuorumCert
|
x.lockQuorumCert = quorumCert
|
||||||
}
|
}
|
||||||
|
|
||||||
proposedBlockRound := &decodedExtraField.Round
|
proposedBlockRound := &round
|
||||||
// 3. Update commit block info
|
// 3. Update commit block info
|
||||||
_, err = x.commitBlocks(blockChainReader, proposedBlockHeader, proposedBlockRound)
|
_, err = x.commitBlocks(blockChainReader, proposedBlockHeader, proposedBlockRound)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -1410,34 +1432,33 @@ func (x *XDPoS_v2) commitBlocks(blockChainReader consensus.ChainReader, proposed
|
||||||
// Find the last two parent block and check their rounds are the continuous
|
// Find the last two parent block and check their rounds are the continuous
|
||||||
parentBlock := blockChainReader.GetHeaderByHash(proposedBlockHeader.ParentHash)
|
parentBlock := blockChainReader.GetHeaderByHash(proposedBlockHeader.ParentHash)
|
||||||
|
|
||||||
var decodedExtraField utils.ExtraFields_v2
|
_, round, _, err := x.getExtraFields(parentBlock)
|
||||||
err := utils.DecodeBytesExtraFields(parentBlock.Extra, &decodedExtraField)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Fail to execute first DecodeBytesExtraFields for commiting block", "ProposedBlockHash", proposedBlockHeader.Hash())
|
log.Error("Fail to execute first DecodeBytesExtraFields for commiting block", "ProposedBlockHash", proposedBlockHeader.Hash())
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
if *proposedBlockRound-1 != decodedExtraField.Round {
|
if *proposedBlockRound-1 != round {
|
||||||
log.Debug("[commitBlocks] Rounds not continuous(parent) found when committing block", "proposedBlockRound", proposedBlockRound, "decodedExtraField.Round", decodedExtraField.Round, "proposedBlockHeaderHash", proposedBlockHeader.Hash())
|
log.Debug("[commitBlocks] Rounds not continuous(parent) found when committing block", "proposedBlockRound", proposedBlockRound, "decodedExtraField.Round", round, "proposedBlockHeaderHash", proposedBlockHeader.Hash())
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// If parent round is continuous, we check grandparent
|
// If parent round is continuous, we check grandparent
|
||||||
grandParentBlock := blockChainReader.GetHeaderByHash(parentBlock.ParentHash)
|
grandParentBlock := blockChainReader.GetHeaderByHash(parentBlock.ParentHash)
|
||||||
err = utils.DecodeBytesExtraFields(grandParentBlock.Extra, &decodedExtraField)
|
_, round, _, err = x.getExtraFields(grandParentBlock)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Fail to execute second DecodeBytesExtraFields for commiting block", "parentBlockHash", parentBlock.Hash())
|
log.Error("Fail to execute second DecodeBytesExtraFields for commiting block", "parentBlockHash", parentBlock.Hash())
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
if *proposedBlockRound-2 != decodedExtraField.Round {
|
if *proposedBlockRound-2 != round {
|
||||||
log.Debug("[commitBlocks] Rounds not continuous(grand parent) found when committing block", "proposedBlockRound", proposedBlockRound, "decodedExtraField.Round", decodedExtraField.Round, "proposedBlockHeaderHash", proposedBlockHeader.Hash())
|
log.Debug("[commitBlocks] Rounds not continuous(grand parent) found when committing block", "proposedBlockRound", proposedBlockRound, "decodedExtraField.Round", round, "proposedBlockHeaderHash", proposedBlockHeader.Hash())
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
// Commit the grandParent block
|
// Commit the grandParent block
|
||||||
if x.highestCommitBlock == nil || (x.highestCommitBlock.Round < decodedExtraField.Round && x.highestCommitBlock.Number.Cmp(grandParentBlock.Number) == -1) {
|
if x.highestCommitBlock == nil || (x.highestCommitBlock.Round < round && x.highestCommitBlock.Number.Cmp(grandParentBlock.Number) == -1) {
|
||||||
x.highestCommitBlock = &utils.BlockInfo{
|
x.highestCommitBlock = &utils.BlockInfo{
|
||||||
Number: grandParentBlock.Number,
|
Number: grandParentBlock.Number,
|
||||||
Hash: grandParentBlock.Hash(),
|
Hash: grandParentBlock.Hash(),
|
||||||
Round: decodedExtraField.Round,
|
Round: round,
|
||||||
}
|
}
|
||||||
log.Debug("Successfully committed block", "Committed block Hash", x.highestCommitBlock.Hash, "Committed round", x.highestCommitBlock.Round)
|
log.Debug("Successfully committed block", "Committed block Hash", x.highestCommitBlock.Hash, "Committed round", x.highestCommitBlock.Round)
|
||||||
return true, nil
|
return true, nil
|
||||||
|
|
@ -1522,18 +1543,16 @@ func (x *XDPoS_v2) IsEpochSwitch(header *types.Header) (bool, uint64, error) {
|
||||||
return true, header.Number.Uint64() / x.config.Epoch, nil
|
return true, header.Number.Uint64() / x.config.Epoch, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var decodedExtraField utils.ExtraFields_v2
|
quorumCert, round, _, err := x.getExtraFields(header)
|
||||||
err := utils.DecodeBytesExtraFields(header.Extra, &decodedExtraField)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("[IsEpochSwitch] decode header error", "err", err, "header", header, "extra", common.Bytes2Hex(header.Extra))
|
log.Error("[IsEpochSwitch] decode header error", "err", err, "header", header, "extra", common.Bytes2Hex(header.Extra))
|
||||||
return false, 0, err
|
return false, 0, err
|
||||||
}
|
}
|
||||||
parentRound := decodedExtraField.QuorumCert.ProposedBlockInfo.Round
|
parentRound := quorumCert.ProposedBlockInfo.Round
|
||||||
round := decodedExtraField.Round
|
|
||||||
epochStartRound := round - round%utils.Round(x.config.Epoch)
|
epochStartRound := round - round%utils.Round(x.config.Epoch)
|
||||||
epochNum := x.config.V2.SwitchBlock.Uint64()/x.config.Epoch + uint64(round)/x.config.Epoch
|
epochNum := x.config.V2.SwitchBlock.Uint64()/x.config.Epoch + uint64(round)/x.config.Epoch
|
||||||
// if parent is last v1 block and this is first v2 block, this is treated as epoch switch
|
// if parent is last v1 block and this is first v2 block, this is treated as epoch switch
|
||||||
if decodedExtraField.QuorumCert.ProposedBlockInfo.Number.Cmp(x.config.V2.SwitchBlock) == 0 {
|
if quorumCert.ProposedBlockInfo.Number.Cmp(x.config.V2.SwitchBlock) == 0 {
|
||||||
log.Info("[IsEpochSwitch] true, parent equals V2.SwitchBlock", "round", round, "number", header.Number.Uint64(), "hash", header.Hash())
|
log.Info("[IsEpochSwitch] true, parent equals V2.SwitchBlock", "round", round, "number", header.Number.Uint64(), "hash", header.Hash())
|
||||||
return true, epochNum, nil
|
return true, epochNum, nil
|
||||||
}
|
}
|
||||||
|
|
@ -1548,13 +1567,13 @@ func (x *XDPoS_v2) IsEpochSwitchAtRound(round utils.Round, parentHeader *types.H
|
||||||
if parentHeader.Number.Cmp(x.config.V2.SwitchBlock) == 0 {
|
if parentHeader.Number.Cmp(x.config.V2.SwitchBlock) == 0 {
|
||||||
return true, epochNum, nil
|
return true, epochNum, nil
|
||||||
}
|
}
|
||||||
var decodedExtraField utils.ExtraFields_v2
|
|
||||||
err := utils.DecodeBytesExtraFields(parentHeader.Extra, &decodedExtraField)
|
_, round, _, err := x.getExtraFields(parentHeader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("[IsEpochSwitch] decode header error", "err", err, "header", parentHeader, "extra", common.Bytes2Hex(parentHeader.Extra))
|
log.Error("[IsEpochSwitch] decode header error", "err", err, "header", parentHeader, "extra", common.Bytes2Hex(parentHeader.Extra))
|
||||||
return false, 0, err
|
return false, 0, err
|
||||||
}
|
}
|
||||||
parentRound := decodedExtraField.Round
|
parentRound := round
|
||||||
epochStartRound := round - round%utils.Round(x.config.Epoch)
|
epochStartRound := round - round%utils.Round(x.config.Epoch)
|
||||||
return parentRound < epochStartRound, epochNum, nil
|
return parentRound < epochStartRound, epochNum, nil
|
||||||
}
|
}
|
||||||
|
|
@ -1572,6 +1591,10 @@ func (x *XDPoS_v2) getEpochSwitchInfo(chain consensus.ChainReader, header *types
|
||||||
if h == nil {
|
if h == nil {
|
||||||
log.Debug("[getEpochSwitchInfo] header missing, get header", "hash", hash.Hex())
|
log.Debug("[getEpochSwitchInfo] header missing, get header", "hash", hash.Hex())
|
||||||
h = chain.GetHeaderByHash(hash)
|
h = chain.GetHeaderByHash(hash)
|
||||||
|
if h == nil {
|
||||||
|
log.Warn("[getEpochSwitchInfo] can not find header from db", "hash", hash.Hex())
|
||||||
|
return nil, fmt.Errorf("[getEpochSwitchInfo] can not find header from db hash %v", hash.Hex())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
isEpochSwitch, _, err := x.IsEpochSwitch(h)
|
isEpochSwitch, _, err := x.IsEpochSwitch(h)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -1579,36 +1602,20 @@ func (x *XDPoS_v2) getEpochSwitchInfo(chain consensus.ChainReader, header *types
|
||||||
}
|
}
|
||||||
if isEpochSwitch {
|
if isEpochSwitch {
|
||||||
log.Debug("[getEpochSwitchInfo] header is epoch switch", "hash", hash.Hex(), "number", h.Number.Uint64())
|
log.Debug("[getEpochSwitchInfo] header is epoch switch", "hash", hash.Hex(), "number", h.Number.Uint64())
|
||||||
var epochSwitchInfo *utils.EpochSwitchInfo
|
quorumCert, round, masternodes, err := x.getExtraFields(h)
|
||||||
// Special case, in case of last v1 block, we manually build the epoch switch info
|
if err != nil {
|
||||||
if h.Number.Cmp(x.config.V2.SwitchBlock) == 0 {
|
return nil, err
|
||||||
masternodes := decodeMasternodesFromHeaderExtra(h)
|
}
|
||||||
epochSwitchInfo = &utils.EpochSwitchInfo{
|
epochSwitchInfo := &utils.EpochSwitchInfo{
|
||||||
Masternodes: masternodes,
|
Masternodes: masternodes,
|
||||||
EpochSwitchBlockInfo: &utils.BlockInfo{
|
EpochSwitchBlockInfo: &utils.BlockInfo{
|
||||||
Hash: hash,
|
Hash: hash,
|
||||||
Number: h.Number,
|
Number: h.Number,
|
||||||
Round: utils.Round(0),
|
Round: round,
|
||||||
},
|
},
|
||||||
EpochSwitchParentBlockInfo: nil,
|
}
|
||||||
}
|
if quorumCert != nil {
|
||||||
} else { // v2 normal flow
|
epochSwitchInfo.EpochSwitchParentBlockInfo = quorumCert.ProposedBlockInfo
|
||||||
masternodes := x.GetMasternodesFromEpochSwitchHeader(h)
|
|
||||||
// create the epoch switch info and cache it
|
|
||||||
var decodedExtraField utils.ExtraFields_v2
|
|
||||||
err = utils.DecodeBytesExtraFields(h.Extra, &decodedExtraField)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
epochSwitchInfo = &utils.EpochSwitchInfo{
|
|
||||||
Masternodes: masternodes,
|
|
||||||
EpochSwitchBlockInfo: &utils.BlockInfo{
|
|
||||||
Hash: hash,
|
|
||||||
Number: h.Number,
|
|
||||||
Round: decodedExtraField.Round,
|
|
||||||
},
|
|
||||||
EpochSwitchParentBlockInfo: decodedExtraField.QuorumCert.ProposedBlockInfo,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
x.epochSwitches.Add(hash, epochSwitchInfo)
|
x.epochSwitches.Add(hash, epochSwitchInfo)
|
||||||
|
|
@ -1712,6 +1719,26 @@ func (x *XDPoS_v2) FindParentBlockToAssign(chain consensus.ChainReader) *types.B
|
||||||
return parent
|
return parent
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *XDPoS_v2) getExtraFields(header *types.Header) (*utils.QuorumCert, utils.Round, []common.Address, error) {
|
||||||
|
|
||||||
|
var masternodes []common.Address
|
||||||
|
|
||||||
|
// last v1 block
|
||||||
|
if header.Number.Cmp(x.config.V2.SwitchBlock) == 0 {
|
||||||
|
masternodes = decodeMasternodesFromHeaderExtra(header)
|
||||||
|
return nil, utils.Round(0), masternodes, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// v2 block
|
||||||
|
masternodes = x.GetMasternodesFromEpochSwitchHeader(header)
|
||||||
|
var decodedExtraField utils.ExtraFields_v2
|
||||||
|
err := utils.DecodeBytesExtraFields(header.Extra, &decodedExtraField)
|
||||||
|
if err != nil {
|
||||||
|
return nil, utils.Round(0), masternodes, err
|
||||||
|
}
|
||||||
|
return decodedExtraField.QuorumCert, decodedExtraField.Round, masternodes, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (x *XDPoS_v2) allowedToSend(chain consensus.ChainReader, blockHeader *types.Header, sendType string) error {
|
func (x *XDPoS_v2) allowedToSend(chain consensus.ChainReader, blockHeader *types.Header, sendType string) error {
|
||||||
allowedToSend := false
|
allowedToSend := false
|
||||||
// Don't hold the signFn for the whole signing operation
|
// Don't hold the signFn for the whole signing operation
|
||||||
|
|
|
||||||
|
|
@ -4,16 +4,13 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
|
||||||
"github.com/XinFinOrg/XDPoSChain/common"
|
"github.com/XinFinOrg/XDPoSChain/common"
|
||||||
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils"
|
|
||||||
"github.com/XinFinOrg/XDPoSChain/ethdb"
|
"github.com/XinFinOrg/XDPoSChain/ethdb"
|
||||||
lru "github.com/hashicorp/golang-lru"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Snapshot is the state of the smart contract validator list
|
// Snapshot is the state of the smart contract validator list
|
||||||
// The validator list is used on next epoch master nodes
|
// The validator list is used on next epoch master nodes
|
||||||
// If we don't have the snapshot, then we have to trace back the gap block smart contract state which is very costly
|
// If we don't have the snapshot, then we have to trace back the gap block smart contract state which is very costly
|
||||||
type SnapshotV2 struct {
|
type SnapshotV2 struct {
|
||||||
Round utils.Round `json:"round"` // Round number
|
|
||||||
Number uint64 `json:"number"` // Block number where the snapshot was created
|
Number uint64 `json:"number"` // Block number where the snapshot was created
|
||||||
Hash common.Hash `json:"hash"` // Block hash where the snapshot was created
|
Hash common.Hash `json:"hash"` // Block hash where the snapshot was created
|
||||||
|
|
||||||
|
|
@ -22,9 +19,8 @@ type SnapshotV2 struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// create new snapshot for next epoch to use
|
// create new snapshot for next epoch to use
|
||||||
func newSnapshot(number uint64, hash common.Hash, round utils.Round, qc *utils.QuorumCert, masternodes []common.Address) *SnapshotV2 {
|
func newSnapshot(number uint64, hash common.Hash, masternodes []common.Address) *SnapshotV2 {
|
||||||
snap := &SnapshotV2{
|
snap := &SnapshotV2{
|
||||||
Round: round,
|
|
||||||
Number: number,
|
Number: number,
|
||||||
Hash: hash,
|
Hash: hash,
|
||||||
NextEpochMasterNodes: masternodes,
|
NextEpochMasterNodes: masternodes,
|
||||||
|
|
@ -33,8 +29,8 @@ func newSnapshot(number uint64, hash common.Hash, round utils.Round, qc *utils.Q
|
||||||
}
|
}
|
||||||
|
|
||||||
// loadSnapshot loads an existing snapshot from the database.
|
// loadSnapshot loads an existing snapshot from the database.
|
||||||
func loadSnapshot(sigcache *lru.ARCCache, db ethdb.Database, hash common.Hash) (*SnapshotV2, error) {
|
func loadSnapshot(db ethdb.Database, hash common.Hash) (*SnapshotV2, error) {
|
||||||
blob, err := db.Get(append([]byte("XDPoS-"), hash[:]...))
|
blob, err := db.Get(append([]byte("XDPoS-V2-"), hash[:]...))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -52,7 +48,7 @@ func storeSnapshot(s *SnapshotV2, db ethdb.Database) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return db.Put(append([]byte("XDPoS-"), s.Hash[:]...), blob)
|
return db.Put(append([]byte("XDPoS-V2-"), s.Hash[:]...), blob)
|
||||||
}
|
}
|
||||||
|
|
||||||
// retrieves master nodes list in map type
|
// retrieves master nodes list in map type
|
||||||
|
|
|
||||||
|
|
@ -6,14 +6,13 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/XinFinOrg/XDPoSChain/common"
|
"github.com/XinFinOrg/XDPoSChain/common"
|
||||||
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils"
|
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
|
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
|
||||||
"github.com/XinFinOrg/XDPoSChain/ethdb/leveldb"
|
"github.com/XinFinOrg/XDPoSChain/ethdb/leveldb"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestGetMasterNodes(t *testing.T) {
|
func TestGetMasterNodes(t *testing.T) {
|
||||||
masterNodes := []common.Address{{0x4}, {0x3}, {0x2}, {0x1}}
|
masterNodes := []common.Address{{0x4}, {0x3}, {0x2}, {0x1}}
|
||||||
snap := newSnapshot(1, common.Hash{}, utils.Round(1), nil, masterNodes)
|
snap := newSnapshot(1, common.Hash{}, masterNodes)
|
||||||
|
|
||||||
for _, address := range masterNodes {
|
for _, address := range masterNodes {
|
||||||
if _, ok := snap.GetMappedMasterNodes()[address]; !ok {
|
if _, ok := snap.GetMappedMasterNodes()[address]; !ok {
|
||||||
|
|
@ -24,7 +23,7 @@ func TestGetMasterNodes(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestStoreLoadSnapshot(t *testing.T) {
|
func TestStoreLoadSnapshot(t *testing.T) {
|
||||||
snap := newSnapshot(1, common.Hash{0x1}, utils.Round(1), nil, nil)
|
snap := newSnapshot(1, common.Hash{0x1}, nil)
|
||||||
dir, err := ioutil.TempDir("", "snapshot-test")
|
dir, err := ioutil.TempDir("", "snapshot-test")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(fmt.Sprintf("can't create temporary directory: %v", err))
|
panic(fmt.Sprintf("can't create temporary directory: %v", err))
|
||||||
|
|
@ -40,7 +39,7 @@ func TestStoreLoadSnapshot(t *testing.T) {
|
||||||
t.Error("store snapshot failed", err)
|
t.Error("store snapshot failed", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
restoredSnapshot, err := loadSnapshot(nil, lddb, snap.Hash)
|
restoredSnapshot, err := loadSnapshot(lddb, snap.Hash)
|
||||||
if err != nil || restoredSnapshot.Hash != snap.Hash {
|
if err != nil || restoredSnapshot.Hash != snap.Hash {
|
||||||
t.Error("load snapshot failed", err)
|
t.Error("load snapshot failed", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -236,7 +236,7 @@ func TestGetParentBlock(t *testing.T) {
|
||||||
assert.Equal(t, block, block900)
|
assert.Equal(t, block, block900)
|
||||||
|
|
||||||
// Initialise
|
// Initialise
|
||||||
err := adaptor.EngineV2.Initial(blockchain, []common.Address{})
|
err := adaptor.EngineV2.Initial(blockchain, block.Header())
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
// V2
|
// V2
|
||||||
|
|
|
||||||
123
consensus/tests/initial_test.go
Normal file
123
consensus/tests/initial_test.go
Normal file
|
|
@ -0,0 +1,123 @@
|
||||||
|
package tests
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/big"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/common"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/params"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestInitialFirstV2Blcok(t *testing.T) {
|
||||||
|
blockchain, _, currentBlock, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 900, params.TestXDPoSMockChainConfig, 0)
|
||||||
|
adaptor := blockchain.Engine().(*XDPoS.XDPoS)
|
||||||
|
header := currentBlock.Header()
|
||||||
|
|
||||||
|
// snapshot should not be created before initial
|
||||||
|
snap, _ := adaptor.EngineV2.GetSnapshot(blockchain, currentBlock.Header())
|
||||||
|
assert.Nil(t, snap)
|
||||||
|
|
||||||
|
err := adaptor.EngineV2.Initial(blockchain, header)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
round, _, highQC, _, _ := adaptor.EngineV2.GetProperties()
|
||||||
|
blockInfo := &utils.BlockInfo{
|
||||||
|
Hash: header.Hash(),
|
||||||
|
Round: utils.Round(0),
|
||||||
|
Number: header.Number,
|
||||||
|
}
|
||||||
|
expectedQuorumCert := &utils.QuorumCert{
|
||||||
|
ProposedBlockInfo: blockInfo,
|
||||||
|
Signatures: nil,
|
||||||
|
}
|
||||||
|
assert.Equal(t, utils.Round(1), round)
|
||||||
|
assert.Equal(t, expectedQuorumCert, highQC)
|
||||||
|
|
||||||
|
// Test snapshot
|
||||||
|
snap, err = adaptor.EngineV2.GetSnapshot(blockchain, currentBlock.Header())
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, uint64(450), snap.Number)
|
||||||
|
|
||||||
|
// Test Running channels
|
||||||
|
WaitPeriod := <-adaptor.WaitPeriodCh
|
||||||
|
assert.Equal(t, params.TestXDPoSMockChainConfig.XDPoS.V2.WaitPeriod, WaitPeriod)
|
||||||
|
|
||||||
|
t.Logf("Waiting %d secs for timeout to happen", params.TestXDPoSMockChainConfig.XDPoS.V2.TimeoutPeriod)
|
||||||
|
timeoutMsg := <-adaptor.EngineV2.BroadcastCh
|
||||||
|
assert.NotNil(t, timeoutMsg)
|
||||||
|
assert.Equal(t, utils.Round(1), timeoutMsg.(*utils.Timeout).Round)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInitialOtherV2Block(t *testing.T) {
|
||||||
|
// insert new block with new extra fields
|
||||||
|
blockchain, _, currentBlock, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, 900, params.TestXDPoSMockChainConfig, 0)
|
||||||
|
adaptor := blockchain.Engine().(*XDPoS.XDPoS)
|
||||||
|
|
||||||
|
blockCoinBase := "0x111000000000000000000000000000000123"
|
||||||
|
for blockNum := 901; blockNum <= 910; blockNum++ {
|
||||||
|
currentBlock = CreateBlock(blockchain, params.TestXDPoSMockChainConfig, currentBlock, blockNum, int64(blockNum-900), blockCoinBase, signer, signFn, nil)
|
||||||
|
err := blockchain.InsertBlock(currentBlock)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// v2
|
||||||
|
blockInfo := &utils.BlockInfo{
|
||||||
|
Hash: currentBlock.Header().Hash(),
|
||||||
|
Round: utils.Round(10),
|
||||||
|
Number: big.NewInt(910),
|
||||||
|
}
|
||||||
|
quorumCert := &utils.QuorumCert{
|
||||||
|
ProposedBlockInfo: blockInfo,
|
||||||
|
Signatures: nil, // after decode it got default value []utils.Signature{}
|
||||||
|
}
|
||||||
|
extra := utils.ExtraFields_v2{
|
||||||
|
Round: 11,
|
||||||
|
QuorumCert: quorumCert,
|
||||||
|
}
|
||||||
|
extraBytes, err := extra.EncodeToBytes()
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
header := &types.Header{
|
||||||
|
Root: common.HexToHash("ea465415b60d88429f181fec9fae67c0f19cbf5a4fa10971d96d4faa57d96ffa"),
|
||||||
|
Number: big.NewInt(int64(911)),
|
||||||
|
ParentHash: currentBlock.Hash(),
|
||||||
|
Coinbase: common.HexToAddress("0x111000000000000000000000000000000123"),
|
||||||
|
}
|
||||||
|
header.Extra = extraBytes
|
||||||
|
|
||||||
|
block, err := createBlockFromHeader(blockchain, header, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
blockchain.InsertBlock(block)
|
||||||
|
// Initialise
|
||||||
|
err = adaptor.EngineV2.Initial(blockchain, block.Header())
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
round, _, highQC, _, _ := adaptor.EngineV2.GetProperties()
|
||||||
|
expectedQuorumCert := &utils.QuorumCert{
|
||||||
|
ProposedBlockInfo: blockInfo,
|
||||||
|
Signatures: []utils.Signature{},
|
||||||
|
}
|
||||||
|
assert.Equal(t, utils.Round(11), round)
|
||||||
|
assert.Equal(t, expectedQuorumCert, highQC)
|
||||||
|
|
||||||
|
// Test snapshot
|
||||||
|
snap, err := adaptor.EngineV2.GetSnapshot(blockchain, block.Header())
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, uint64(450), snap.Number)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSnapshotShouldAlreadyCreatedByUpdateM1(t *testing.T) {
|
||||||
|
// insert new block with new extra fields
|
||||||
|
blockchain, _, currentBlock, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 1800, params.TestXDPoSMockChainConfig, 0)
|
||||||
|
adaptor := blockchain.Engine().(*XDPoS.XDPoS)
|
||||||
|
|
||||||
|
snap, err := adaptor.EngineV2.GetSnapshot(blockchain, currentBlock.Header())
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, uint64(1350), snap.Number)
|
||||||
|
}
|
||||||
|
|
@ -16,7 +16,7 @@ import (
|
||||||
|
|
||||||
func TestYourTurnInitialV2(t *testing.T) {
|
func TestYourTurnInitialV2(t *testing.T) {
|
||||||
config := params.TestXDPoSMockChainConfig
|
config := params.TestXDPoSMockChainConfig
|
||||||
blockchain, _, parentBlock, _ := PrepareXDCTestBlockChain(t, int(config.XDPoS.Epoch)-1, config)
|
blockchain, _, parentBlock, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, int(config.XDPoS.Epoch)-1, config, 0)
|
||||||
minePeriod := config.XDPoS.V2.MinePeriod
|
minePeriod := config.XDPoS.V2.MinePeriod
|
||||||
adaptor := blockchain.Engine().(*XDPoS.XDPoS)
|
adaptor := blockchain.Engine().(*XDPoS.XDPoS)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -418,8 +418,7 @@ func PrepareXDCTestBlockChainForV2Engine(t *testing.T, numOfBlocks int, chainCon
|
||||||
lastv1BlockNumber := block.Header().Number.Uint64() - 1
|
lastv1BlockNumber := block.Header().Number.Uint64() - 1
|
||||||
checkpointBlockNumber := lastv1BlockNumber - lastv1BlockNumber%chainConfig.XDPoS.Epoch
|
checkpointBlockNumber := lastv1BlockNumber - lastv1BlockNumber%chainConfig.XDPoS.Epoch
|
||||||
checkpointHeader := blockchain.GetHeaderByNumber(checkpointBlockNumber)
|
checkpointHeader := blockchain.GetHeaderByNumber(checkpointBlockNumber)
|
||||||
masternodes := engine.EngineV1.GetMasternodesFromCheckpointHeader(checkpointHeader)
|
err := engine.EngineV2.Initial(blockchain, checkpointHeader)
|
||||||
err := engine.EngineV2.Initial(blockchain, masternodes)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue