mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-06-19 21:31:37 +00:00
verify header including validator (#71)
* verify header including validator * re-structure v1 v2 tests * remove unused test function * add test to check coinbase and validator address matches * refactor engine v2 to group private functions into same file
This commit is contained in:
parent
fbb9e87251
commit
d55229677d
26 changed files with 1657 additions and 1084 deletions
|
|
@ -1,7 +1,6 @@
|
|||
package engine_v2
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
|
@ -17,10 +16,8 @@ import (
|
|||
"github.com/XinFinOrg/XDPoSChain/consensus"
|
||||
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils"
|
||||
"github.com/XinFinOrg/XDPoSChain/consensus/clique"
|
||||
"github.com/XinFinOrg/XDPoSChain/consensus/misc"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/state"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||
"github.com/XinFinOrg/XDPoSChain/crypto"
|
||||
"github.com/XinFinOrg/XDPoSChain/ethdb"
|
||||
"github.com/XinFinOrg/XDPoSChain/log"
|
||||
"github.com/XinFinOrg/XDPoSChain/params"
|
||||
|
|
@ -271,6 +268,15 @@ func (x *XDPoS_v2) Prepare(chain consensus.ChainReader, header *types.Header) er
|
|||
header.Time = big.NewInt(time.Now().Unix())
|
||||
}
|
||||
|
||||
x.signLock.RLock()
|
||||
signer := x.signer
|
||||
x.signLock.RUnlock()
|
||||
|
||||
if header.Coinbase != signer {
|
||||
log.Error("[Prepare] The mined blocker header coinbase address mismatch with waller address", "headerCoinbase", header.Coinbase.Hex(), "WalletAddress", signer.Hex())
|
||||
return consensus.ErrCoinbaseMismatch
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -387,7 +393,7 @@ func (x *XDPoS_v2) YourTurn(chain consensus.ChainReader, parent *types.Header, s
|
|||
}
|
||||
|
||||
round := x.currentRound
|
||||
isEpochSwitch, _, err := x.IsEpochSwitchAtRound(round, parent)
|
||||
isEpochSwitch, _, err := x.isEpochSwitchAtRound(round, parent)
|
||||
if err != nil {
|
||||
log.Error("[YourTurn] check epoch switch at round failed", "Error", err)
|
||||
return false, err
|
||||
|
|
@ -470,7 +476,6 @@ func (x *XDPoS_v2) IsAuthorisedAddress(chain consensus.ChainReader, header *type
|
|||
return false
|
||||
}
|
||||
|
||||
// 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)
|
||||
|
|
@ -481,36 +486,6 @@ func (x *XDPoS_v2) GetSnapshot(chain consensus.ChainReader, header *types.Header
|
|||
return snap, nil
|
||||
}
|
||||
|
||||
// snapshot retrieves the authorization snapshot at a given point in time.
|
||||
func (x *XDPoS_v2) getSnapshot(chain consensus.ChainReader, number uint64, isGapNumber bool) (*SnapshotV2, error) {
|
||||
var gapBlockNum uint64
|
||||
if isGapNumber {
|
||||
gapBlockNum = number
|
||||
} else {
|
||||
gapBlockNum = number - number%x.config.Epoch - x.config.Gap
|
||||
}
|
||||
|
||||
gapBlockHash := chain.GetHeaderByNumber(gapBlockNum).Hash()
|
||||
log.Debug("get snapshot from gap block", "number", gapBlockNum, "hash", gapBlockHash.Hex())
|
||||
|
||||
// If an in-memory SnapshotV2 was found, use that
|
||||
if s, ok := x.snapshots.Get(gapBlockHash); ok {
|
||||
snap := s.(*SnapshotV2)
|
||||
log.Trace("Loaded snapshot from memory", "number", gapBlockNum, "hash", gapBlockHash)
|
||||
return snap, nil
|
||||
}
|
||||
// If an on-disk checkpoint snapshot can be found, use that
|
||||
snap, err := loadSnapshot(x.db, gapBlockHash)
|
||||
if err != nil {
|
||||
log.Error("Cannot find snapshot from last gap block", "err", err, "number", gapBlockNum, "hash", gapBlockHash)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Trace("Loaded snapshot from disk", "number", gapBlockNum, "hash", gapBlockHash)
|
||||
x.snapshots.Add(snap.Hash, snap)
|
||||
return snap, nil
|
||||
}
|
||||
|
||||
func (x *XDPoS_v2) UpdateMasternodes(chain consensus.ChainReader, header *types.Header, ms []utils.Masternode) error {
|
||||
number := header.Number.Uint64()
|
||||
log.Trace("take snapshot", "number", number, "hash", header.Hash())
|
||||
|
|
@ -563,122 +538,6 @@ func (x *XDPoS_v2) VerifyHeaders(chain consensus.ChainReader, headers []*types.H
|
|||
}()
|
||||
}
|
||||
|
||||
// Verify individual header
|
||||
func (x *XDPoS_v2) verifyHeader(chain consensus.ChainReader, header *types.Header, parents []*types.Header, fullVerify bool) error {
|
||||
// If we're running a engine faking, accept any block as valid
|
||||
if x.config.V2.SkipV2Validation {
|
||||
return nil
|
||||
}
|
||||
_, check := x.verifiedHeaders.Get(header.Hash())
|
||||
if check {
|
||||
return nil
|
||||
}
|
||||
|
||||
if header.Number == nil {
|
||||
return utils.ErrUnknownBlock
|
||||
}
|
||||
|
||||
if fullVerify {
|
||||
if len(header.Validator) == 0 {
|
||||
return consensus.ErrNoValidatorSignature
|
||||
}
|
||||
// Don't waste time checking blocks from the future
|
||||
if header.Time.Int64() > time.Now().Unix() {
|
||||
return consensus.ErrFutureBlock
|
||||
}
|
||||
}
|
||||
|
||||
// Verify this is truely a v2 block first
|
||||
|
||||
quorumCert, round, _, err := x.getExtraFields(header)
|
||||
if err != nil {
|
||||
return utils.ErrInvalidV2Extra
|
||||
}
|
||||
if round <= quorumCert.ProposedBlockInfo.Round {
|
||||
return utils.ErrRoundInvalid
|
||||
}
|
||||
|
||||
err = x.verifyQC(chain, quorumCert)
|
||||
if err != nil {
|
||||
log.Warn("[verifyHeader] fail to verify QC", "QCNumber", quorumCert.ProposedBlockInfo.Number, "QCsigLength", len(quorumCert.Signatures))
|
||||
return err
|
||||
}
|
||||
// Nonces must be 0x00..0 or 0xff..f, zeroes enforced on checkpoints
|
||||
if !bytes.Equal(header.Nonce[:], utils.NonceAuthVote) && !bytes.Equal(header.Nonce[:], utils.NonceDropVote) {
|
||||
return utils.ErrInvalidVote
|
||||
}
|
||||
// Ensure that the mix digest is zero as we don't have fork protection currently
|
||||
if header.MixDigest != (common.Hash{}) {
|
||||
return utils.ErrInvalidMixDigest
|
||||
}
|
||||
// Ensure that the block doesn't contain any uncles which are meaningless in XDPoS_v1
|
||||
if header.UncleHash != utils.UncleHash {
|
||||
return utils.ErrInvalidUncleHash
|
||||
}
|
||||
|
||||
if header.Difficulty.Cmp(big.NewInt(1)) != 0 {
|
||||
return utils.ErrInvalidDifficulty
|
||||
}
|
||||
|
||||
isEpochSwitch, _, err := x.IsEpochSwitch(header) // Verify v2 block that is on the epoch switch
|
||||
if err != nil {
|
||||
log.Error("[verifyHeader] error when checking if header is epoch switch header", "Hash", header.Hash(), "Number", header.Number, "Error", err)
|
||||
return err
|
||||
}
|
||||
if isEpochSwitch {
|
||||
if !bytes.Equal(header.Nonce[:], utils.NonceDropVote) {
|
||||
return utils.ErrInvalidCheckpointVote
|
||||
}
|
||||
if header.Validators == nil || len(header.Validators) == 0 {
|
||||
return utils.ErrEmptyEpochSwitchValidators
|
||||
}
|
||||
if len(header.Validators)%common.AddressLength != 0 {
|
||||
return utils.ErrInvalidCheckpointSigners
|
||||
}
|
||||
// TODO: Add checkMasternodesOnEpochSwitch
|
||||
} else {
|
||||
if len(header.Validators) != 0 {
|
||||
log.Warn("[verifyHeader] Validators shall not have values in non-epochSwitch block", "Hash", header.Hash(), "Number", header.Number, "Validators", header.Validators)
|
||||
return utils.ErrInvalidFieldInNonEpochSwitch
|
||||
}
|
||||
}
|
||||
|
||||
// If all checks passed, validate any special fields for hard forks
|
||||
if err := misc.VerifyForkHashes(chain.Config(), header, false); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Ensure that the block's timestamp isn't too close to it's parent
|
||||
var parent *types.Header
|
||||
number := header.Number.Uint64()
|
||||
|
||||
if len(parents) > 0 {
|
||||
parent = parents[len(parents)-1]
|
||||
} else {
|
||||
parent = chain.GetHeader(header.ParentHash, number-1)
|
||||
}
|
||||
if parent == nil || parent.Number.Uint64() != number-1 || parent.Hash() != header.ParentHash {
|
||||
return consensus.ErrUnknownAncestor
|
||||
}
|
||||
if parent.Number.Uint64() > x.config.V2.SwitchBlock.Uint64() && parent.Time.Uint64()+uint64(x.config.V2.MinePeriod) > header.Time.Uint64() {
|
||||
return utils.ErrInvalidTimestamp
|
||||
}
|
||||
// TODO: item 9. check validator
|
||||
|
||||
_, penalties, err := x.calcMasternodes(chain, header.Number, header.ParentHash)
|
||||
if err != nil {
|
||||
log.Error("[verifyHeader] Fail to calculate master nodes list with penalty", "Number", header.Number, "Hash", header.Hash())
|
||||
return err
|
||||
}
|
||||
|
||||
if !utils.CompareSignersLists(common.ExtractAddressFromBytes(header.Penalties), penalties) {
|
||||
return utils.ErrPenaltyListDoesNotMatch
|
||||
}
|
||||
|
||||
x.verifiedHeaders.Add(header.Hash(), true)
|
||||
return nil
|
||||
}
|
||||
|
||||
/*
|
||||
SyncInfo workflow
|
||||
*/
|
||||
|
|
@ -746,7 +605,7 @@ func (x *XDPoS_v2) VerifyVoteMessage(chain consensus.ChainReader, vote *utils.Vo
|
|||
if err != nil {
|
||||
log.Error("[VerifyVoteMessage] fail to get snapshot for a vote message", "BlockNum", vote.ProposedBlockInfo.Number, "Hash", vote.ProposedBlockInfo.Hash, "Error", err.Error())
|
||||
}
|
||||
verified, err := x.verifyMsgSignature(utils.VoteSigHash(vote.ProposedBlockInfo), vote.Signature, snapshot.NextEpochMasterNodes)
|
||||
verified, _, err := x.verifyMsgSignature(utils.VoteSigHash(vote.ProposedBlockInfo), vote.Signature, snapshot.NextEpochMasterNodes)
|
||||
if err != nil {
|
||||
for i, mn := range snapshot.NextEpochMasterNodes {
|
||||
log.Warn("[VerifyVoteMessage] Master node list item", "index", i, "Master node", mn.Hex())
|
||||
|
|
@ -763,101 +622,6 @@ func (x *XDPoS_v2) VoteHandler(chain consensus.ChainReader, voteMsg *utils.Vote)
|
|||
return x.voteHandler(chain, voteMsg)
|
||||
}
|
||||
|
||||
func (x *XDPoS_v2) voteHandler(chain consensus.ChainReader, voteMsg *utils.Vote) error {
|
||||
|
||||
// 1. checkRoundNumber
|
||||
if (voteMsg.ProposedBlockInfo.Round != x.currentRound) && (voteMsg.ProposedBlockInfo.Round != x.currentRound+1) {
|
||||
return &utils.ErrIncomingMessageRoundTooFarFromCurrentRound{
|
||||
Type: "vote",
|
||||
IncomingRound: voteMsg.ProposedBlockInfo.Round,
|
||||
CurrentRound: x.currentRound,
|
||||
}
|
||||
}
|
||||
|
||||
// Collect vote
|
||||
thresholdReached, numberOfVotesInPool, pooledVotes := x.votePool.Add(voteMsg)
|
||||
log.Info("[voteHandler] collect votes", "number", numberOfVotesInPool)
|
||||
if thresholdReached {
|
||||
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
|
||||
proposedBlockHeader := chain.GetHeaderByHash(voteMsg.ProposedBlockInfo.Hash)
|
||||
if proposedBlockHeader == nil {
|
||||
log.Warn("[voteHandler] The proposed block from vote message does not exist yet, wait for the next vote to try again", "Hash", voteMsg.ProposedBlockInfo.Hash, "Round", voteMsg.ProposedBlockInfo.Round)
|
||||
return nil
|
||||
}
|
||||
|
||||
err := x.VerifyBlockInfo(chain, voteMsg.ProposedBlockInfo)
|
||||
if err != nil {
|
||||
x.votePool.ClearPoolKeyByObj(voteMsg)
|
||||
return err
|
||||
}
|
||||
|
||||
err = x.onVotePoolThresholdReached(chain, pooledVotes, voteMsg, proposedBlockHeader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
/*
|
||||
Function that will be called by votePool when it reached threshold.
|
||||
In the engine v2, we will need to generate and process QC
|
||||
*/
|
||||
func (x *XDPoS_v2) onVotePoolThresholdReached(chain consensus.ChainReader, pooledVotes map[common.Hash]utils.PoolObj, currentVoteMsg utils.PoolObj, proposedBlockHeader *types.Header) error {
|
||||
|
||||
masternodes := x.GetMasternodes(chain, proposedBlockHeader)
|
||||
|
||||
// Filter out non-Master nodes signatures
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(len(pooledVotes))
|
||||
signatureSlice := make([]utils.Signature, len(pooledVotes))
|
||||
counter := 0
|
||||
for h, vote := range pooledVotes {
|
||||
go func(hash common.Hash, v *utils.Vote, i int) {
|
||||
defer wg.Done()
|
||||
verified, err := x.verifyMsgSignature(utils.VoteSigHash(v.ProposedBlockInfo), v.Signature, masternodes)
|
||||
if !verified || err != nil {
|
||||
log.Warn("[onVotePoolThresholdReached] Skip not verified vote signatures when building QC", "Error", err.Error(), "verified", verified)
|
||||
} else {
|
||||
signatureSlice[i] = v.Signature
|
||||
}
|
||||
}(h, vote.(*utils.Vote), counter)
|
||||
counter++
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
// The signature list may contain empty entey. we only care the ones with values
|
||||
var validSignatureSlice []utils.Signature
|
||||
for _, v := range signatureSlice {
|
||||
if len(v) != 0 {
|
||||
validSignatureSlice = append(validSignatureSlice, v)
|
||||
}
|
||||
}
|
||||
|
||||
// Skip and wait for the next vote to process again if valid votes is less than what we required
|
||||
if len(validSignatureSlice) < x.config.V2.CertThreshold {
|
||||
log.Warn("[onVotePoolThresholdReached] Not enough valid signatures to generate QC", "VotesSignaturesAfterFilter", validSignatureSlice, "NumberOfValidVotes", len(validSignatureSlice), "NumberOfVotes", len(pooledVotes))
|
||||
return nil
|
||||
}
|
||||
// Genrate QC
|
||||
quorumCert := &utils.QuorumCert{
|
||||
ProposedBlockInfo: currentVoteMsg.(*utils.Vote).ProposedBlockInfo,
|
||||
Signatures: validSignatureSlice,
|
||||
}
|
||||
err := x.processQC(chain, quorumCert)
|
||||
if err != nil {
|
||||
log.Error("Error while processing QC in the Vote handler after reaching pool threshold, ", err)
|
||||
return err
|
||||
}
|
||||
log.Info("Successfully processed the vote and produced QC!", "QcRound", quorumCert.ProposedBlockInfo.Round, "QcNumOfSig", len(quorumCert.Signatures), "QcHash", quorumCert.ProposedBlockInfo.Hash, "QcNumber", quorumCert.ProposedBlockInfo.Number.Uint64())
|
||||
// clean up vote at the same poolKey. and pookKey is proposed block hash
|
||||
x.votePool.ClearPoolKeyByObj(currentVoteMsg)
|
||||
return nil
|
||||
}
|
||||
|
||||
/*
|
||||
Timeout workflow
|
||||
*/
|
||||
|
|
@ -880,10 +644,11 @@ func (x *XDPoS_v2) VerifyTimeoutMessage(chain consensus.ChainReader, timeoutMsg
|
|||
return false, fmt.Errorf("Empty master node lists from snapshot")
|
||||
}
|
||||
|
||||
return x.verifyMsgSignature(utils.TimeoutSigHash(&utils.TimeoutForSign{
|
||||
verified, _, err := x.verifyMsgSignature(utils.TimeoutSigHash(&utils.TimeoutForSign{
|
||||
Round: timeoutMsg.Round,
|
||||
GapNumber: timeoutMsg.GapNumber,
|
||||
}), timeoutMsg.Signature, snap.NextEpochMasterNodes)
|
||||
return verified, err
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -898,64 +663,6 @@ func (x *XDPoS_v2) TimeoutHandler(blockChainReader consensus.ChainReader, timeou
|
|||
return x.timeoutHandler(blockChainReader, timeout)
|
||||
}
|
||||
|
||||
func (x *XDPoS_v2) timeoutHandler(blockChainReader consensus.ChainReader, timeout *utils.Timeout) error {
|
||||
// 1. checkRoundNumber
|
||||
if timeout.Round != x.currentRound {
|
||||
return &utils.ErrIncomingMessageRoundNotEqualCurrentRound{
|
||||
Type: "timeout",
|
||||
IncomingRound: timeout.Round,
|
||||
CurrentRound: x.currentRound,
|
||||
}
|
||||
}
|
||||
// Collect timeout, generate TC
|
||||
isThresholdReached, numberOfTimeoutsInPool, pooledTimeouts := x.timeoutPool.Add(timeout)
|
||||
log.Info("[timeoutHandler] collect timeout", "number", numberOfTimeoutsInPool)
|
||||
|
||||
// Threshold reached
|
||||
if isThresholdReached {
|
||||
log.Info(fmt.Sprintf("Timeout pool threashold reached: %v, number of items in the pool: %v", isThresholdReached, numberOfTimeoutsInPool))
|
||||
err := x.onTimeoutPoolThresholdReached(blockChainReader, pooledTimeouts, timeout, timeout.GapNumber)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// clean up timeout message, regardless its GapNumber or round
|
||||
x.timeoutPool.Clear()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
/*
|
||||
Function that will be called by timeoutPool when it reached threshold.
|
||||
In the engine v2, we will need to:
|
||||
1. Genrate TC
|
||||
2. processTC()
|
||||
3. generateSyncInfo()
|
||||
*/
|
||||
func (x *XDPoS_v2) onTimeoutPoolThresholdReached(blockChainReader consensus.ChainReader, pooledTimeouts map[common.Hash]utils.PoolObj, currentTimeoutMsg utils.PoolObj, gapNumber uint64) error {
|
||||
signatures := []utils.Signature{}
|
||||
for _, v := range pooledTimeouts {
|
||||
signatures = append(signatures, v.(*utils.Timeout).Signature)
|
||||
}
|
||||
// Genrate TC
|
||||
timeoutCert := &utils.TimeoutCert{
|
||||
Round: currentTimeoutMsg.(*utils.Timeout).Round,
|
||||
Signatures: signatures,
|
||||
GapNumber: gapNumber,
|
||||
}
|
||||
// Process TC
|
||||
err := x.processTC(blockChainReader, timeoutCert)
|
||||
if err != nil {
|
||||
log.Error("Error while processing TC in the Timeout handler after reaching pool threshold", "TcRound", timeoutCert.Round, "NumberOfTcSig", len(timeoutCert.Signatures), "GapNumber", gapNumber, "Error", err)
|
||||
return err
|
||||
}
|
||||
// Generate and broadcast syncInfo
|
||||
syncInfo := x.getSyncInfo()
|
||||
x.broadcastToBftChannel(syncInfo)
|
||||
|
||||
log.Info("Successfully processed the timeout message and produced TC & SyncInfo!", "TcRound", timeoutCert.Round, "NumberOfTcSig", len(timeoutCert.Signatures))
|
||||
return nil
|
||||
}
|
||||
|
||||
/*
|
||||
Proposed Block workflow
|
||||
*/
|
||||
|
|
@ -1099,7 +806,7 @@ func (x *XDPoS_v2) verifyQC(blockChainReader consensus.ChainReader, quorumCert *
|
|||
for _, signature := range signatures {
|
||||
go func(sig utils.Signature) {
|
||||
defer wg.Done()
|
||||
verified, err := x.verifyMsgSignature(utils.VoteSigHash(quorumCert.ProposedBlockInfo), sig, epochInfo.Masternodes)
|
||||
verified, _, err := x.verifyMsgSignature(utils.VoteSigHash(quorumCert.ProposedBlockInfo), sig, epochInfo.Masternodes)
|
||||
if err != nil {
|
||||
log.Error("[verifyQC] Error while verfying QC message signatures", "Error", err)
|
||||
haveError = fmt.Errorf("Error while verfying QC message signatures")
|
||||
|
|
@ -1120,65 +827,6 @@ func (x *XDPoS_v2) verifyQC(blockChainReader consensus.ChainReader, quorumCert *
|
|||
return x.VerifyBlockInfo(blockChainReader, quorumCert.ProposedBlockInfo)
|
||||
}
|
||||
|
||||
func (x *XDPoS_v2) verifyTC(chain consensus.ChainReader, timeoutCert *utils.TimeoutCert) error {
|
||||
/*
|
||||
1. Get epoch master node list by gapNumber
|
||||
2. Check number of signatures > threshold, as well as it's format. (Same as verifyQC)
|
||||
2. Verify signer signature: (List of signatures)
|
||||
- Use ecRecover to get the public key
|
||||
- Use the above public key to find out the xdc address
|
||||
- Use the above xdc address to check against the master node list from step 1(For the received TC epoch)
|
||||
*/
|
||||
snap, err := x.getSnapshot(chain, timeoutCert.GapNumber, true)
|
||||
if err != nil {
|
||||
log.Error("[verifyTC] Fail to get snapshot when verifying TC!", "TCGapNumber", timeoutCert.GapNumber)
|
||||
return fmt.Errorf("[verifyTC] Unable to get snapshot")
|
||||
}
|
||||
if snap == nil || len(snap.NextEpochMasterNodes) == 0 {
|
||||
log.Error("[verifyTC] Something wrong with the snapshot from gapNumber", "messageGapNumber", timeoutCert.GapNumber, "snapshot", snap)
|
||||
return fmt.Errorf("Empty master node lists from snapshot")
|
||||
}
|
||||
|
||||
if timeoutCert == nil {
|
||||
log.Warn("[verifyTC] TC is Nil")
|
||||
return utils.ErrInvalidTC
|
||||
} else if timeoutCert.Signatures == nil || (len(timeoutCert.Signatures) < x.config.V2.CertThreshold) {
|
||||
log.Warn("[verifyTC] Invalid TC Signature is nil or empty", "timeoutCert.Round", timeoutCert.Round, "timeoutCert.GapNumber", timeoutCert.GapNumber, "Signatures len", len(timeoutCert.Signatures))
|
||||
return utils.ErrInvalidTC
|
||||
}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(len(timeoutCert.Signatures))
|
||||
var haveError error
|
||||
|
||||
signedTimeoutObj := utils.TimeoutSigHash(&utils.TimeoutForSign{
|
||||
Round: timeoutCert.Round,
|
||||
GapNumber: timeoutCert.GapNumber,
|
||||
})
|
||||
|
||||
for _, signature := range timeoutCert.Signatures {
|
||||
go func(sig utils.Signature) {
|
||||
defer wg.Done()
|
||||
verified, err := x.verifyMsgSignature(signedTimeoutObj, sig, snap.NextEpochMasterNodes)
|
||||
if err != nil {
|
||||
log.Error("[verifyTC] Error while verfying TC message signatures", "timeoutCert.Round", timeoutCert.Round, "timeoutCert.GapNumber", timeoutCert.GapNumber, "Signatures len", len(timeoutCert.Signatures), "Error", err)
|
||||
haveError = fmt.Errorf("Error while verfying TC message signatures")
|
||||
return
|
||||
}
|
||||
if !verified {
|
||||
log.Warn("[verifyTC] Signature not verified doing TC verification", "timeoutCert.Round", timeoutCert.Round, "timeoutCert.GapNumber", timeoutCert.GapNumber, "Signatures len", len(timeoutCert.Signatures))
|
||||
haveError = fmt.Errorf("Fail to verify TC due to signature mis-match")
|
||||
return
|
||||
}
|
||||
}(signature)
|
||||
}
|
||||
wg.Wait()
|
||||
if haveError != nil {
|
||||
return haveError
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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)
|
||||
|
|
@ -1222,23 +870,6 @@ func (x *XDPoS_v2) processQC(blockChainReader consensus.ChainReader, quorumCert
|
|||
return nil
|
||||
}
|
||||
|
||||
/*
|
||||
1. Update highestTC
|
||||
2. Check TC round >= node's currentRound. If yes, call setNewRound
|
||||
*/
|
||||
func (x *XDPoS_v2) processTC(blockChainReader consensus.ChainReader, timeoutCert *utils.TimeoutCert) error {
|
||||
if timeoutCert.Round > x.highestTimeoutCert.Round {
|
||||
x.highestTimeoutCert = timeoutCert
|
||||
}
|
||||
if timeoutCert.Round >= x.currentRound {
|
||||
err := x.setNewRound(blockChainReader, timeoutCert.Round+1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
/*
|
||||
1. Set currentRound = QC round + 1 (or TC round +1)
|
||||
2. Reset timer
|
||||
|
|
@ -1254,185 +885,6 @@ func (x *XDPoS_v2) setNewRound(blockChainReader consensus.ChainReader, round uti
|
|||
return nil
|
||||
}
|
||||
|
||||
// Hot stuff rule to decide whether this node is eligible to vote for the received block
|
||||
func (x *XDPoS_v2) verifyVotingRule(blockChainReader consensus.ChainReader, blockInfo *utils.BlockInfo, quorumCert *utils.QuorumCert) (bool, error) {
|
||||
// Make sure this node has not voted for this round.
|
||||
if x.currentRound <= x.highestVotedRound {
|
||||
return false, nil
|
||||
}
|
||||
/*
|
||||
HotStuff Voting rule:
|
||||
header's round == local current round, AND (one of the following two:)
|
||||
header's block extends lockQuorumCert's ProposedBlockInfo (we need a isExtending(block_a, block_b) function), OR
|
||||
header's QC's ProposedBlockInfo.Round > lockQuorumCert's ProposedBlockInfo.Round
|
||||
*/
|
||||
if blockInfo.Round != x.currentRound {
|
||||
return false, nil
|
||||
}
|
||||
// XDPoS v1.0 switch to v2.0, the proposed block can always pass voting rule
|
||||
if x.lockQuorumCert == nil {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
if quorumCert.ProposedBlockInfo.Round > x.lockQuorumCert.ProposedBlockInfo.Round {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
isExtended, err := x.isExtendingFromAncestor(blockChainReader, blockInfo, x.lockQuorumCert.ProposedBlockInfo)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if isExtended {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// Once Hot stuff voting rule has verified, this node can then send vote
|
||||
func (x *XDPoS_v2) sendVote(chainReader consensus.ChainReader, blockInfo *utils.BlockInfo) error {
|
||||
// First step: Update the highest Voted round
|
||||
// Second step: Generate the signature by using node's private key(The signature is the blockInfo signature)
|
||||
// Third step: Construct the vote struct with the above signature & blockinfo struct
|
||||
// Forth step: Send the vote to broadcast channel
|
||||
|
||||
signedHash, err := x.signSignature(utils.VoteSigHash(blockInfo))
|
||||
if err != nil {
|
||||
log.Error("signSignature when sending out Vote", "BlockInfoHash", blockInfo.Hash, "Error", err)
|
||||
return err
|
||||
}
|
||||
|
||||
x.highestVotedRound = x.currentRound
|
||||
voteMsg := &utils.Vote{
|
||||
ProposedBlockInfo: blockInfo,
|
||||
Signature: signedHash,
|
||||
}
|
||||
|
||||
err = x.voteHandler(chainReader, voteMsg)
|
||||
if err != nil {
|
||||
log.Error("sendVote error", "BlockInfoHash", blockInfo.Hash, "Error", err)
|
||||
return err
|
||||
}
|
||||
x.broadcastToBftChannel(voteMsg)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Generate and send timeout into BFT channel.
|
||||
/*
|
||||
1. timeout.round = currentRound
|
||||
2. Sign the signature
|
||||
3. send to broadcast channel
|
||||
*/
|
||||
func (x *XDPoS_v2) sendTimeout(chain consensus.ChainReader) error {
|
||||
// Construct the gapNumber
|
||||
var gapNumber uint64
|
||||
currentBlockHeader := chain.CurrentHeader()
|
||||
isEpochSwitch, epochNum, err := x.IsEpochSwitchAtRound(x.currentRound, currentBlockHeader)
|
||||
if err != nil {
|
||||
log.Error("[sendTimeout] Error while checking if the currentBlock is epoch switch", "currentRound", x.currentRound, "currentBlockNum", currentBlockHeader.Number, "currentBlockHash", currentBlockHeader.Hash(), "epochNum", epochNum)
|
||||
return err
|
||||
}
|
||||
|
||||
if isEpochSwitch {
|
||||
// Notice this +1 is because we expect a block whos is the child of currentHeader
|
||||
currentNumber := currentBlockHeader.Number.Uint64() + 1
|
||||
gapNumber = currentNumber - currentNumber%x.config.Epoch - x.config.Gap
|
||||
log.Debug("[sendTimeout] is epoch switch when sending out timeout message", "currentNumber", currentNumber, "gapNumber", gapNumber)
|
||||
} else {
|
||||
epochSwitchInfo, err := x.getEpochSwitchInfo(chain, currentBlockHeader, currentBlockHeader.Hash())
|
||||
if err != nil {
|
||||
log.Error("[sendTimeout] Error when trying to get current epoch switch info for a non-epoch block", "currentRound", x.currentRound, "currentBlockNum", currentBlockHeader.Number, "currentBlockHash", currentBlockHeader.Hash(), "epochNum", epochNum)
|
||||
}
|
||||
gapNumber = epochSwitchInfo.EpochSwitchBlockInfo.Number.Uint64() - epochSwitchInfo.EpochSwitchBlockInfo.Number.Uint64()%x.config.Epoch - x.config.Gap
|
||||
log.Debug("[sendTimeout] non-epoch-switch block found its epoch block and calculated the gapNumber", "epochSwitchInfo.EpochSwitchBlockInfo.Number", epochSwitchInfo.EpochSwitchBlockInfo.Number.Uint64(), "gapNumber", gapNumber)
|
||||
}
|
||||
|
||||
signedHash, err := x.signSignature(utils.TimeoutSigHash(&utils.TimeoutForSign{
|
||||
Round: x.currentRound,
|
||||
GapNumber: gapNumber,
|
||||
}))
|
||||
if err != nil {
|
||||
log.Error("[sendTimeout] signSignature when sending out TC", "Error", err)
|
||||
return err
|
||||
}
|
||||
timeoutMsg := &utils.Timeout{
|
||||
Round: x.currentRound,
|
||||
Signature: signedHash,
|
||||
GapNumber: gapNumber,
|
||||
}
|
||||
log.Info("[sendTimeout] Timeout message generated, ready to send!", "timeoutMsgRound", timeoutMsg.Round, "timeoutMsgGapNumber", timeoutMsg.GapNumber)
|
||||
err = x.timeoutHandler(chain, timeoutMsg)
|
||||
if err != nil {
|
||||
log.Error("TimeoutHandler error", "TimeoutRound", timeoutMsg.Round, "Error", err)
|
||||
return err
|
||||
}
|
||||
x.broadcastToBftChannel(timeoutMsg)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *XDPoS_v2) signSignature(signingHash common.Hash) (utils.Signature, error) {
|
||||
// Don't hold the signFn for the whole signing operation
|
||||
x.signLock.RLock()
|
||||
signer, signFn := x.signer, x.signFn
|
||||
x.signLock.RUnlock()
|
||||
|
||||
signedHash, err := signFn(accounts.Account{Address: signer}, signingHash.Bytes())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error while signing hash")
|
||||
}
|
||||
return signedHash, nil
|
||||
}
|
||||
|
||||
func (x *XDPoS_v2) verifyMsgSignature(signedHashToBeVerified common.Hash, signature utils.Signature, masternodes []common.Address) (bool, error) {
|
||||
if len(masternodes) == 0 {
|
||||
return false, fmt.Errorf("Empty masternode list detected when verifying message signatures")
|
||||
}
|
||||
// Recover the public key and the Ethereum address
|
||||
pubkey, err := crypto.Ecrecover(signedHashToBeVerified.Bytes(), signature)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("Error while verifying message: %v", err)
|
||||
}
|
||||
var signerAddress common.Address
|
||||
copy(signerAddress[:], crypto.Keccak256(pubkey[1:])[12:])
|
||||
for _, mn := range masternodes {
|
||||
if mn == signerAddress {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
|
||||
return false, fmt.Errorf("Masternodes list does not contain signer address, Signer address: %v", signerAddress.Hex())
|
||||
}
|
||||
|
||||
/*
|
||||
Function that will be called by timer when countdown reaches its threshold.
|
||||
In the engine v2, we would need to broadcast timeout messages to other peers
|
||||
*/
|
||||
func (x *XDPoS_v2) OnCountdownTimeout(time time.Time, chain interface{}) error {
|
||||
x.lock.Lock()
|
||||
defer x.lock.Unlock()
|
||||
|
||||
// Check if we are within the master node list
|
||||
err := x.allowedToSend(chain.(consensus.ChainReader), chain.(consensus.ChainReader).CurrentHeader(), "timeout")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = x.sendTimeout(chain.(consensus.ChainReader))
|
||||
if err != nil {
|
||||
log.Error("Error while sending out timeout message at time: ", time)
|
||||
return err
|
||||
}
|
||||
|
||||
x.timeoutCount++
|
||||
if x.timeoutCount%x.config.V2.TimeoutSyncThreshold == 0 {
|
||||
log.Info("[OnCountdownTimeout] timeout sync threadhold reached, send syncInfo message")
|
||||
syncInfo := x.getSyncInfo()
|
||||
x.broadcastToBftChannel(syncInfo)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *XDPoS_v2) broadcastToBftChannel(msg interface{}) {
|
||||
go func() {
|
||||
x.BroadcastCh <- msg
|
||||
|
|
@ -1490,26 +942,6 @@ func (x *XDPoS_v2) commitBlocks(blockChainReader consensus.ChainReader, proposed
|
|||
return false, nil
|
||||
}
|
||||
|
||||
func (x *XDPoS_v2) isExtendingFromAncestor(blockChainReader consensus.ChainReader, currentBlock *utils.BlockInfo, ancestorBlock *utils.BlockInfo) (bool, error) {
|
||||
blockNumDiff := int(big.NewInt(0).Sub(currentBlock.Number, ancestorBlock.Number).Int64())
|
||||
|
||||
nextBlockHash := currentBlock.Hash
|
||||
for i := 0; i < blockNumDiff; i++ {
|
||||
parentBlock := blockChainReader.GetHeaderByHash(nextBlockHash)
|
||||
if parentBlock == nil {
|
||||
return false, fmt.Errorf("Could not find its parent block when checking whether currentBlock %v with hash %v is extending from the ancestorBlock %v", currentBlock.Number, currentBlock.Hash, ancestorBlock.Number)
|
||||
} else {
|
||||
nextBlockHash = parentBlock.ParentHash
|
||||
}
|
||||
log.Debug("[isExtendingFromAncestor] Found parent block", "CurrentBlockHash", currentBlock.Hash, "ParentHash", nextBlockHash)
|
||||
}
|
||||
|
||||
if nextBlockHash == ancestorBlock.Hash {
|
||||
return true, nil
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// Get master nodes over extra data of epoch switch block.
|
||||
func (x *XDPoS_v2) GetMasternodesFromEpochSwitchHeader(epochSwitchHeader *types.Header) []common.Address {
|
||||
if epochSwitchHeader == nil {
|
||||
|
|
@ -1548,77 +980,6 @@ func (x *XDPoS_v2) IsEpochSwitch(header *types.Header) (bool, uint64, error) {
|
|||
return parentRound < epochStartRound, epochNum, nil
|
||||
}
|
||||
|
||||
// IsEpochSwitchAtRound() is used by miner to check whether it mines a block in the same epoch with parent
|
||||
func (x *XDPoS_v2) IsEpochSwitchAtRound(round utils.Round, parentHeader *types.Header) (bool, uint64, error) {
|
||||
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 parentHeader.Number.Cmp(x.config.V2.SwitchBlock) == 0 {
|
||||
return true, epochNum, nil
|
||||
}
|
||||
|
||||
_, round, _, err := x.getExtraFields(parentHeader)
|
||||
if err != nil {
|
||||
log.Error("[IsEpochSwitch] decode header error", "err", err, "header", parentHeader, "extra", common.Bytes2Hex(parentHeader.Extra))
|
||||
return false, 0, err
|
||||
}
|
||||
parentRound := round
|
||||
epochStartRound := round - round%utils.Round(x.config.Epoch)
|
||||
return parentRound < epochStartRound, epochNum, nil
|
||||
}
|
||||
|
||||
// Given header and its hash, get epoch switch info from the epoch switch block of that epoch,
|
||||
// header is allow to be nil.
|
||||
func (x *XDPoS_v2) getEpochSwitchInfo(chain consensus.ChainReader, header *types.Header, hash common.Hash) (*utils.EpochSwitchInfo, error) {
|
||||
e, ok := x.epochSwitches.Get(hash)
|
||||
if ok {
|
||||
log.Debug("[getEpochSwitchInfo] cache hit", "hash", hash.Hex())
|
||||
epochSwitchInfo := e.(*utils.EpochSwitchInfo)
|
||||
return epochSwitchInfo, nil
|
||||
}
|
||||
h := header
|
||||
if h == nil {
|
||||
log.Debug("[getEpochSwitchInfo] header missing, get header", "hash", hash.Hex())
|
||||
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)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if isEpochSwitch {
|
||||
log.Debug("[getEpochSwitchInfo] header is epoch switch", "hash", hash.Hex(), "number", h.Number.Uint64())
|
||||
quorumCert, round, masternodes, err := x.getExtraFields(h)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
epochSwitchInfo := &utils.EpochSwitchInfo{
|
||||
Masternodes: masternodes,
|
||||
EpochSwitchBlockInfo: &utils.BlockInfo{
|
||||
Hash: hash,
|
||||
Number: h.Number,
|
||||
Round: round,
|
||||
},
|
||||
}
|
||||
if quorumCert != nil {
|
||||
epochSwitchInfo.EpochSwitchParentBlockInfo = quorumCert.ProposedBlockInfo
|
||||
}
|
||||
|
||||
x.epochSwitches.Add(hash, epochSwitchInfo)
|
||||
return epochSwitchInfo, nil
|
||||
}
|
||||
epochSwitchInfo, err := x.getEpochSwitchInfo(chain, nil, h.ParentHash)
|
||||
if err != nil {
|
||||
log.Error("[getEpochSwitchInfo] recursive error", "err", err, "hash", hash.Hex(), "number", h.Number.Uint64())
|
||||
return nil, err
|
||||
}
|
||||
log.Debug("[getEpochSwitchInfo] get epoch switch info recursively", "hash", hash.Hex(), "number", h.Number.Uint64())
|
||||
x.epochSwitches.Add(hash, epochSwitchInfo)
|
||||
return epochSwitchInfo, nil
|
||||
}
|
||||
|
||||
// Given header, get master node from the epoch switch block of that epoch
|
||||
func (x *XDPoS_v2) GetMasternodes(chain consensus.ChainReader, header *types.Header) []common.Address {
|
||||
epochSwitchInfo, err := x.getEpochSwitchInfo(chain, header, header.Hash())
|
||||
|
|
@ -1671,23 +1032,6 @@ func (x *XDPoS_v2) GetMasternodesByHash(chain consensus.ChainReader, hash common
|
|||
return epochSwitchInfo.Masternodes
|
||||
}
|
||||
|
||||
// get epoch switch of the previous `limit` epoch
|
||||
func (x *XDPoS_v2) getPreviousEpochSwitchInfoByHash(chain consensus.ChainReader, hash common.Hash, limit int) (*utils.EpochSwitchInfo, error) {
|
||||
epochSwitchInfo, err := x.getEpochSwitchInfo(chain, nil, hash)
|
||||
if err != nil {
|
||||
log.Error("[getPreviousEpochSwitchInfoByHash] Adaptor v2 getEpochSwitchInfo has error, potentially bug", "err", err)
|
||||
return nil, err
|
||||
}
|
||||
for i := 0; i < limit; i++ {
|
||||
epochSwitchInfo, err = x.getEpochSwitchInfo(chain, nil, epochSwitchInfo.EpochSwitchParentBlockInfo.Hash)
|
||||
if err != nil {
|
||||
log.Error("[getPreviousEpochSwitchInfoByHash] Adaptor v2 getEpochSwitchInfo has error, potentially bug", "err", err)
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return epochSwitchInfo, nil
|
||||
}
|
||||
|
||||
// Given hash, get master node from the epoch switch block of the previous `limit` epoch
|
||||
func (x *XDPoS_v2) GetPreviousPenaltyByHash(chain consensus.ChainReader, hash common.Hash, limit int) []common.Address {
|
||||
epochSwitchInfo, err := x.getPreviousEpochSwitchInfoByHash(chain, hash, limit)
|
||||
|
|
@ -1707,26 +1051,6 @@ func (x *XDPoS_v2) FindParentBlockToAssign(chain consensus.ChainReader) *types.B
|
|||
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 {
|
||||
allowedToSend := false
|
||||
// Don't hold the signFn for the whole signing operation
|
||||
|
|
|
|||
99
consensus/XDPoS/engines/engine_v2/epochSwitch.go
Normal file
99
consensus/XDPoS/engines/engine_v2/epochSwitch.go
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
package engine_v2
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/common"
|
||||
"github.com/XinFinOrg/XDPoSChain/consensus"
|
||||
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||
"github.com/XinFinOrg/XDPoSChain/log"
|
||||
)
|
||||
|
||||
// get epoch switch of the previous `limit` epoch
|
||||
func (x *XDPoS_v2) getPreviousEpochSwitchInfoByHash(chain consensus.ChainReader, hash common.Hash, limit int) (*utils.EpochSwitchInfo, error) {
|
||||
epochSwitchInfo, err := x.getEpochSwitchInfo(chain, nil, hash)
|
||||
if err != nil {
|
||||
log.Error("[getPreviousEpochSwitchInfoByHash] Adaptor v2 getEpochSwitchInfo has error, potentially bug", "err", err)
|
||||
return nil, err
|
||||
}
|
||||
for i := 0; i < limit; i++ {
|
||||
epochSwitchInfo, err = x.getEpochSwitchInfo(chain, nil, epochSwitchInfo.EpochSwitchParentBlockInfo.Hash)
|
||||
if err != nil {
|
||||
log.Error("[getPreviousEpochSwitchInfoByHash] Adaptor v2 getEpochSwitchInfo has error, potentially bug", "err", err)
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return epochSwitchInfo, nil
|
||||
}
|
||||
|
||||
// Given header and its hash, get epoch switch info from the epoch switch block of that epoch,
|
||||
// header is allow to be nil.
|
||||
func (x *XDPoS_v2) getEpochSwitchInfo(chain consensus.ChainReader, header *types.Header, hash common.Hash) (*utils.EpochSwitchInfo, error) {
|
||||
e, ok := x.epochSwitches.Get(hash)
|
||||
if ok {
|
||||
log.Debug("[getEpochSwitchInfo] cache hit", "hash", hash.Hex())
|
||||
epochSwitchInfo := e.(*utils.EpochSwitchInfo)
|
||||
return epochSwitchInfo, nil
|
||||
}
|
||||
h := header
|
||||
if h == nil {
|
||||
log.Debug("[getEpochSwitchInfo] header missing, get header", "hash", hash.Hex())
|
||||
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)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if isEpochSwitch {
|
||||
log.Debug("[getEpochSwitchInfo] header is epoch switch", "hash", hash.Hex(), "number", h.Number.Uint64())
|
||||
quorumCert, round, masternodes, err := x.getExtraFields(h)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
epochSwitchInfo := &utils.EpochSwitchInfo{
|
||||
Masternodes: masternodes,
|
||||
EpochSwitchBlockInfo: &utils.BlockInfo{
|
||||
Hash: hash,
|
||||
Number: h.Number,
|
||||
Round: round,
|
||||
},
|
||||
}
|
||||
if quorumCert != nil {
|
||||
epochSwitchInfo.EpochSwitchParentBlockInfo = quorumCert.ProposedBlockInfo
|
||||
}
|
||||
|
||||
x.epochSwitches.Add(hash, epochSwitchInfo)
|
||||
return epochSwitchInfo, nil
|
||||
}
|
||||
epochSwitchInfo, err := x.getEpochSwitchInfo(chain, nil, h.ParentHash)
|
||||
if err != nil {
|
||||
log.Error("[getEpochSwitchInfo] recursive error", "err", err, "hash", hash.Hex(), "number", h.Number.Uint64())
|
||||
return nil, err
|
||||
}
|
||||
log.Debug("[getEpochSwitchInfo] get epoch switch info recursively", "hash", hash.Hex(), "number", h.Number.Uint64())
|
||||
x.epochSwitches.Add(hash, epochSwitchInfo)
|
||||
return epochSwitchInfo, nil
|
||||
}
|
||||
|
||||
// IsEpochSwitchAtRound() is used by miner to check whether it mines a block in the same epoch with parent
|
||||
func (x *XDPoS_v2) isEpochSwitchAtRound(round utils.Round, parentHeader *types.Header) (bool, uint64, error) {
|
||||
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 parentHeader.Number.Cmp(x.config.V2.SwitchBlock) == 0 {
|
||||
return true, epochNum, nil
|
||||
}
|
||||
|
||||
_, round, _, err := x.getExtraFields(parentHeader)
|
||||
if err != nil {
|
||||
log.Error("[IsEpochSwitch] decode header error", "err", err, "header", parentHeader, "extra", common.Bytes2Hex(parentHeader.Extra))
|
||||
return false, 0, err
|
||||
}
|
||||
parentRound := round
|
||||
epochStartRound := round - round%utils.Round(x.config.Epoch)
|
||||
return parentRound < epochStartRound, epochNum, nil
|
||||
}
|
||||
|
|
@ -4,7 +4,9 @@ import (
|
|||
"encoding/json"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/common"
|
||||
"github.com/XinFinOrg/XDPoSChain/consensus"
|
||||
"github.com/XinFinOrg/XDPoSChain/ethdb"
|
||||
"github.com/XinFinOrg/XDPoSChain/log"
|
||||
)
|
||||
|
||||
// Snapshot is the state of the smart contract validator list
|
||||
|
|
@ -68,3 +70,33 @@ func (s *SnapshotV2) IsMasterNodes(address common.Address) bool {
|
|||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// snapshot retrieves the authorization snapshot at a given point in time.
|
||||
func (x *XDPoS_v2) getSnapshot(chain consensus.ChainReader, number uint64, isGapNumber bool) (*SnapshotV2, error) {
|
||||
var gapBlockNum uint64
|
||||
if isGapNumber {
|
||||
gapBlockNum = number
|
||||
} else {
|
||||
gapBlockNum = number - number%x.config.Epoch - x.config.Gap
|
||||
}
|
||||
|
||||
gapBlockHash := chain.GetHeaderByNumber(gapBlockNum).Hash()
|
||||
log.Debug("get snapshot from gap block", "number", gapBlockNum, "hash", gapBlockHash.Hex())
|
||||
|
||||
// If an in-memory SnapshotV2 was found, use that
|
||||
if s, ok := x.snapshots.Get(gapBlockHash); ok {
|
||||
snap := s.(*SnapshotV2)
|
||||
log.Trace("Loaded snapshot from memory", "number", gapBlockNum, "hash", gapBlockHash)
|
||||
return snap, nil
|
||||
}
|
||||
// If an on-disk checkpoint snapshot can be found, use that
|
||||
snap, err := loadSnapshot(x.db, gapBlockHash)
|
||||
if err != nil {
|
||||
log.Error("Cannot find snapshot from last gap block", "err", err, "number", gapBlockNum, "hash", gapBlockHash)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Trace("Loaded snapshot from disk", "number", gapBlockNum, "hash", gapBlockHash)
|
||||
x.snapshots.Add(snap.Hash, snap)
|
||||
return snap, nil
|
||||
}
|
||||
|
|
|
|||
229
consensus/XDPoS/engines/engine_v2/timeout.go
Normal file
229
consensus/XDPoS/engines/engine_v2/timeout.go
Normal file
|
|
@ -0,0 +1,229 @@
|
|||
package engine_v2
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/common"
|
||||
"github.com/XinFinOrg/XDPoSChain/consensus"
|
||||
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils"
|
||||
"github.com/XinFinOrg/XDPoSChain/log"
|
||||
)
|
||||
|
||||
func (x *XDPoS_v2) timeoutHandler(blockChainReader consensus.ChainReader, timeout *utils.Timeout) error {
|
||||
// 1. checkRoundNumber
|
||||
if timeout.Round != x.currentRound {
|
||||
return &utils.ErrIncomingMessageRoundNotEqualCurrentRound{
|
||||
Type: "timeout",
|
||||
IncomingRound: timeout.Round,
|
||||
CurrentRound: x.currentRound,
|
||||
}
|
||||
}
|
||||
// Collect timeout, generate TC
|
||||
isThresholdReached, numberOfTimeoutsInPool, pooledTimeouts := x.timeoutPool.Add(timeout)
|
||||
log.Info("[timeoutHandler] collect timeout", "number", numberOfTimeoutsInPool)
|
||||
|
||||
// Threshold reached
|
||||
if isThresholdReached {
|
||||
log.Info(fmt.Sprintf("Timeout pool threashold reached: %v, number of items in the pool: %v", isThresholdReached, numberOfTimeoutsInPool))
|
||||
err := x.onTimeoutPoolThresholdReached(blockChainReader, pooledTimeouts, timeout, timeout.GapNumber)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// clean up timeout message, regardless its GapNumber or round
|
||||
x.timeoutPool.Clear()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
/*
|
||||
Function that will be called by timeoutPool when it reached threshold.
|
||||
In the engine v2, we will need to:
|
||||
1. Genrate TC
|
||||
2. processTC()
|
||||
3. generateSyncInfo()
|
||||
*/
|
||||
func (x *XDPoS_v2) onTimeoutPoolThresholdReached(blockChainReader consensus.ChainReader, pooledTimeouts map[common.Hash]utils.PoolObj, currentTimeoutMsg utils.PoolObj, gapNumber uint64) error {
|
||||
signatures := []utils.Signature{}
|
||||
for _, v := range pooledTimeouts {
|
||||
signatures = append(signatures, v.(*utils.Timeout).Signature)
|
||||
}
|
||||
// Genrate TC
|
||||
timeoutCert := &utils.TimeoutCert{
|
||||
Round: currentTimeoutMsg.(*utils.Timeout).Round,
|
||||
Signatures: signatures,
|
||||
GapNumber: gapNumber,
|
||||
}
|
||||
// Process TC
|
||||
err := x.processTC(blockChainReader, timeoutCert)
|
||||
if err != nil {
|
||||
log.Error("Error while processing TC in the Timeout handler after reaching pool threshold", "TcRound", timeoutCert.Round, "NumberOfTcSig", len(timeoutCert.Signatures), "GapNumber", gapNumber, "Error", err)
|
||||
return err
|
||||
}
|
||||
// Generate and broadcast syncInfo
|
||||
syncInfo := x.getSyncInfo()
|
||||
x.broadcastToBftChannel(syncInfo)
|
||||
|
||||
log.Info("Successfully processed the timeout message and produced TC & SyncInfo!", "TcRound", timeoutCert.Round, "NumberOfTcSig", len(timeoutCert.Signatures))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *XDPoS_v2) verifyTC(chain consensus.ChainReader, timeoutCert *utils.TimeoutCert) error {
|
||||
/*
|
||||
1. Get epoch master node list by gapNumber
|
||||
2. Check number of signatures > threshold, as well as it's format. (Same as verifyQC)
|
||||
2. Verify signer signature: (List of signatures)
|
||||
- Use ecRecover to get the public key
|
||||
- Use the above public key to find out the xdc address
|
||||
- Use the above xdc address to check against the master node list from step 1(For the received TC epoch)
|
||||
*/
|
||||
snap, err := x.getSnapshot(chain, timeoutCert.GapNumber, true)
|
||||
if err != nil {
|
||||
log.Error("[verifyTC] Fail to get snapshot when verifying TC!", "TCGapNumber", timeoutCert.GapNumber)
|
||||
return fmt.Errorf("[verifyTC] Unable to get snapshot")
|
||||
}
|
||||
if snap == nil || len(snap.NextEpochMasterNodes) == 0 {
|
||||
log.Error("[verifyTC] Something wrong with the snapshot from gapNumber", "messageGapNumber", timeoutCert.GapNumber, "snapshot", snap)
|
||||
return fmt.Errorf("Empty master node lists from snapshot")
|
||||
}
|
||||
|
||||
if timeoutCert == nil {
|
||||
log.Warn("[verifyTC] TC is Nil")
|
||||
return utils.ErrInvalidTC
|
||||
} else if timeoutCert.Signatures == nil || (len(timeoutCert.Signatures) < x.config.V2.CertThreshold) {
|
||||
log.Warn("[verifyTC] Invalid TC Signature is nil or empty", "timeoutCert.Round", timeoutCert.Round, "timeoutCert.GapNumber", timeoutCert.GapNumber, "Signatures len", len(timeoutCert.Signatures))
|
||||
return utils.ErrInvalidTC
|
||||
}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(len(timeoutCert.Signatures))
|
||||
var haveError error
|
||||
|
||||
signedTimeoutObj := utils.TimeoutSigHash(&utils.TimeoutForSign{
|
||||
Round: timeoutCert.Round,
|
||||
GapNumber: timeoutCert.GapNumber,
|
||||
})
|
||||
|
||||
for _, signature := range timeoutCert.Signatures {
|
||||
go func(sig utils.Signature) {
|
||||
defer wg.Done()
|
||||
verified, _, err := x.verifyMsgSignature(signedTimeoutObj, sig, snap.NextEpochMasterNodes)
|
||||
if err != nil {
|
||||
log.Error("[verifyTC] Error while verfying TC message signatures", "timeoutCert.Round", timeoutCert.Round, "timeoutCert.GapNumber", timeoutCert.GapNumber, "Signatures len", len(timeoutCert.Signatures), "Error", err)
|
||||
haveError = fmt.Errorf("Error while verfying TC message signatures")
|
||||
return
|
||||
}
|
||||
if !verified {
|
||||
log.Warn("[verifyTC] Signature not verified doing TC verification", "timeoutCert.Round", timeoutCert.Round, "timeoutCert.GapNumber", timeoutCert.GapNumber, "Signatures len", len(timeoutCert.Signatures))
|
||||
haveError = fmt.Errorf("Fail to verify TC due to signature mis-match")
|
||||
return
|
||||
}
|
||||
}(signature)
|
||||
}
|
||||
wg.Wait()
|
||||
if haveError != nil {
|
||||
return haveError
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
/*
|
||||
1. Update highestTC
|
||||
2. Check TC round >= node's currentRound. If yes, call setNewRound
|
||||
*/
|
||||
func (x *XDPoS_v2) processTC(blockChainReader consensus.ChainReader, timeoutCert *utils.TimeoutCert) error {
|
||||
if timeoutCert.Round > x.highestTimeoutCert.Round {
|
||||
x.highestTimeoutCert = timeoutCert
|
||||
}
|
||||
if timeoutCert.Round >= x.currentRound {
|
||||
err := x.setNewRound(blockChainReader, timeoutCert.Round+1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Generate and send timeout into BFT channel.
|
||||
/*
|
||||
1. timeout.round = currentRound
|
||||
2. Sign the signature
|
||||
3. send to broadcast channel
|
||||
*/
|
||||
func (x *XDPoS_v2) sendTimeout(chain consensus.ChainReader) error {
|
||||
// Construct the gapNumber
|
||||
var gapNumber uint64
|
||||
currentBlockHeader := chain.CurrentHeader()
|
||||
isEpochSwitch, epochNum, err := x.isEpochSwitchAtRound(x.currentRound, currentBlockHeader)
|
||||
if err != nil {
|
||||
log.Error("[sendTimeout] Error while checking if the currentBlock is epoch switch", "currentRound", x.currentRound, "currentBlockNum", currentBlockHeader.Number, "currentBlockHash", currentBlockHeader.Hash(), "epochNum", epochNum)
|
||||
return err
|
||||
}
|
||||
|
||||
if isEpochSwitch {
|
||||
// Notice this +1 is because we expect a block whos is the child of currentHeader
|
||||
currentNumber := currentBlockHeader.Number.Uint64() + 1
|
||||
gapNumber = currentNumber - currentNumber%x.config.Epoch - x.config.Gap
|
||||
log.Debug("[sendTimeout] is epoch switch when sending out timeout message", "currentNumber", currentNumber, "gapNumber", gapNumber)
|
||||
} else {
|
||||
epochSwitchInfo, err := x.getEpochSwitchInfo(chain, currentBlockHeader, currentBlockHeader.Hash())
|
||||
if err != nil {
|
||||
log.Error("[sendTimeout] Error when trying to get current epoch switch info for a non-epoch block", "currentRound", x.currentRound, "currentBlockNum", currentBlockHeader.Number, "currentBlockHash", currentBlockHeader.Hash(), "epochNum", epochNum)
|
||||
}
|
||||
gapNumber = epochSwitchInfo.EpochSwitchBlockInfo.Number.Uint64() - epochSwitchInfo.EpochSwitchBlockInfo.Number.Uint64()%x.config.Epoch - x.config.Gap
|
||||
log.Debug("[sendTimeout] non-epoch-switch block found its epoch block and calculated the gapNumber", "epochSwitchInfo.EpochSwitchBlockInfo.Number", epochSwitchInfo.EpochSwitchBlockInfo.Number.Uint64(), "gapNumber", gapNumber)
|
||||
}
|
||||
|
||||
signedHash, err := x.signSignature(utils.TimeoutSigHash(&utils.TimeoutForSign{
|
||||
Round: x.currentRound,
|
||||
GapNumber: gapNumber,
|
||||
}))
|
||||
if err != nil {
|
||||
log.Error("[sendTimeout] signSignature when sending out TC", "Error", err)
|
||||
return err
|
||||
}
|
||||
timeoutMsg := &utils.Timeout{
|
||||
Round: x.currentRound,
|
||||
Signature: signedHash,
|
||||
GapNumber: gapNumber,
|
||||
}
|
||||
log.Info("[sendTimeout] Timeout message generated, ready to send!", "timeoutMsgRound", timeoutMsg.Round, "timeoutMsgGapNumber", timeoutMsg.GapNumber)
|
||||
err = x.timeoutHandler(chain, timeoutMsg)
|
||||
if err != nil {
|
||||
log.Error("TimeoutHandler error", "TimeoutRound", timeoutMsg.Round, "Error", err)
|
||||
return err
|
||||
}
|
||||
x.broadcastToBftChannel(timeoutMsg)
|
||||
return nil
|
||||
}
|
||||
|
||||
/*
|
||||
Function that will be called by timer when countdown reaches its threshold.
|
||||
In the engine v2, we would need to broadcast timeout messages to other peers
|
||||
*/
|
||||
func (x *XDPoS_v2) OnCountdownTimeout(time time.Time, chain interface{}) error {
|
||||
x.lock.Lock()
|
||||
defer x.lock.Unlock()
|
||||
|
||||
// Check if we are within the master node list
|
||||
err := x.allowedToSend(chain.(consensus.ChainReader), chain.(consensus.ChainReader).CurrentHeader(), "timeout")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = x.sendTimeout(chain.(consensus.ChainReader))
|
||||
if err != nil {
|
||||
log.Error("Error while sending out timeout message at time: ", time)
|
||||
return err
|
||||
}
|
||||
|
||||
x.timeoutCount++
|
||||
if x.timeoutCount%x.config.V2.TimeoutSyncThreshold == 0 {
|
||||
log.Info("[OnCountdownTimeout] timeout sync threadhold reached, send syncInfo message")
|
||||
syncInfo := x.getSyncInfo()
|
||||
x.broadcastToBftChannel(syncInfo)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -1,6 +1,9 @@
|
|||
package engine_v2
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/accounts"
|
||||
"github.com/XinFinOrg/XDPoSChain/common"
|
||||
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||
|
|
@ -84,3 +87,57 @@ func UniqueSignatures(signatureSlice []utils.Signature) ([]utils.Signature, []ut
|
|||
}
|
||||
return list, duplicates
|
||||
}
|
||||
|
||||
func (x *XDPoS_v2) signSignature(signingHash common.Hash) (utils.Signature, error) {
|
||||
// Don't hold the signFn for the whole signing operation
|
||||
x.signLock.RLock()
|
||||
signer, signFn := x.signer, x.signFn
|
||||
x.signLock.RUnlock()
|
||||
|
||||
signedHash, err := signFn(accounts.Account{Address: signer}, signingHash.Bytes())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error while signing hash")
|
||||
}
|
||||
return signedHash, nil
|
||||
}
|
||||
|
||||
func (x *XDPoS_v2) verifyMsgSignature(signedHashToBeVerified common.Hash, signature utils.Signature, masternodes []common.Address) (bool, common.Address, error) {
|
||||
var signerAddress common.Address
|
||||
if len(masternodes) == 0 {
|
||||
return false, signerAddress, fmt.Errorf("Empty masternode list detected when verifying message signatures")
|
||||
}
|
||||
// Recover the public key and the Ethereum address
|
||||
pubkey, err := crypto.Ecrecover(signedHashToBeVerified.Bytes(), signature)
|
||||
if err != nil {
|
||||
return false, signerAddress, fmt.Errorf("Error while verifying message: %v", err)
|
||||
}
|
||||
|
||||
copy(signerAddress[:], crypto.Keccak256(pubkey[1:])[12:])
|
||||
for _, mn := range masternodes {
|
||||
if mn == signerAddress {
|
||||
return true, signerAddress, nil
|
||||
}
|
||||
}
|
||||
|
||||
return false, signerAddress, fmt.Errorf("Masternodes list does not contain signer address, Signer address: %v", signerAddress.Hex())
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
|
|
|||
188
consensus/XDPoS/engines/engine_v2/verifyHeader.go
Normal file
188
consensus/XDPoS/engines/engine_v2/verifyHeader.go
Normal file
|
|
@ -0,0 +1,188 @@
|
|||
package engine_v2
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"math/big"
|
||||
"time"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/common"
|
||||
"github.com/XinFinOrg/XDPoSChain/consensus"
|
||||
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils"
|
||||
"github.com/XinFinOrg/XDPoSChain/consensus/misc"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||
"github.com/XinFinOrg/XDPoSChain/log"
|
||||
)
|
||||
|
||||
// Verify individual header
|
||||
func (x *XDPoS_v2) verifyHeader(chain consensus.ChainReader, header *types.Header, parents []*types.Header, fullVerify bool) error {
|
||||
// If we're running a engine faking, accept any block as valid
|
||||
if x.config.V2.SkipV2Validation {
|
||||
return nil
|
||||
}
|
||||
_, check := x.verifiedHeaders.Get(header.Hash())
|
||||
if check {
|
||||
return nil
|
||||
}
|
||||
|
||||
if header.Number == nil {
|
||||
return utils.ErrUnknownBlock
|
||||
}
|
||||
|
||||
if len(header.Validator) == 0 {
|
||||
return consensus.ErrNoValidatorSignature
|
||||
}
|
||||
|
||||
if fullVerify {
|
||||
// Don't waste time checking blocks from the future
|
||||
if header.Time.Int64() > time.Now().Unix() {
|
||||
return consensus.ErrFutureBlock
|
||||
}
|
||||
}
|
||||
|
||||
// Verify this is truely a v2 block first
|
||||
quorumCert, round, _, err := x.getExtraFields(header)
|
||||
if err != nil {
|
||||
return utils.ErrInvalidV2Extra
|
||||
}
|
||||
if round <= quorumCert.ProposedBlockInfo.Round {
|
||||
return utils.ErrRoundInvalid
|
||||
}
|
||||
|
||||
err = x.verifyQC(chain, quorumCert)
|
||||
if err != nil {
|
||||
log.Warn("[verifyHeader] fail to verify QC", "QCNumber", quorumCert.ProposedBlockInfo.Number, "QCsigLength", len(quorumCert.Signatures))
|
||||
return err
|
||||
}
|
||||
// Nonces must be 0x00..0 or 0xff..f, zeroes enforced on checkpoints
|
||||
if !bytes.Equal(header.Nonce[:], utils.NonceAuthVote) && !bytes.Equal(header.Nonce[:], utils.NonceDropVote) {
|
||||
return utils.ErrInvalidVote
|
||||
}
|
||||
// Ensure that the mix digest is zero as we don't have fork protection currently
|
||||
if header.MixDigest != (common.Hash{}) {
|
||||
return utils.ErrInvalidMixDigest
|
||||
}
|
||||
// Ensure that the block doesn't contain any uncles which are meaningless in XDPoS_v1
|
||||
if header.UncleHash != utils.UncleHash {
|
||||
return utils.ErrInvalidUncleHash
|
||||
}
|
||||
|
||||
if header.Difficulty.Cmp(big.NewInt(1)) != 0 {
|
||||
return utils.ErrInvalidDifficulty
|
||||
}
|
||||
|
||||
isEpochSwitch, _, err := x.IsEpochSwitch(header) // Verify v2 block that is on the epoch switch
|
||||
if err != nil {
|
||||
log.Error("[verifyHeader] error when checking if header is epoch switch header", "Hash", header.Hash(), "Number", header.Number, "Error", err)
|
||||
return err
|
||||
}
|
||||
if isEpochSwitch {
|
||||
if !bytes.Equal(header.Nonce[:], utils.NonceDropVote) {
|
||||
return utils.ErrInvalidCheckpointVote
|
||||
}
|
||||
if header.Validators == nil || len(header.Validators) == 0 {
|
||||
return utils.ErrEmptyEpochSwitchValidators
|
||||
}
|
||||
if len(header.Validators)%common.AddressLength != 0 {
|
||||
return utils.ErrInvalidCheckpointSigners
|
||||
}
|
||||
isLegit, err := x.isValidatorsLegit(chain, header)
|
||||
if err != nil {
|
||||
log.Error("[verifyHeader] Error while trying to check if the validators are legit", "Hash", header.Hash(), "Number", header.Number, "ValidatorsLength", len(header.Validators))
|
||||
return err
|
||||
}
|
||||
if !isLegit {
|
||||
return utils.ErrValidatorsNotLegit
|
||||
}
|
||||
} else {
|
||||
if len(header.Validators) != 0 {
|
||||
log.Warn("[verifyHeader] Validators shall not have values in non-epochSwitch block", "Hash", header.Hash(), "Number", header.Number, "ValidatorsLength", len(header.Validators))
|
||||
return utils.ErrInvalidFieldInNonEpochSwitch
|
||||
}
|
||||
}
|
||||
|
||||
// If all checks passed, validate any special fields for hard forks
|
||||
if err := misc.VerifyForkHashes(chain.Config(), header, false); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Ensure that the block's timestamp isn't too close to it's parent
|
||||
var parent *types.Header
|
||||
number := header.Number.Uint64()
|
||||
|
||||
if len(parents) > 0 {
|
||||
parent = parents[len(parents)-1]
|
||||
} else {
|
||||
parent = chain.GetHeader(header.ParentHash, number-1)
|
||||
}
|
||||
if parent == nil || parent.Number.Uint64() != number-1 || parent.Hash() != header.ParentHash {
|
||||
return consensus.ErrUnknownAncestor
|
||||
}
|
||||
if parent.Number.Uint64() > x.config.V2.SwitchBlock.Uint64() && parent.Time.Uint64()+uint64(x.config.V2.MinePeriod) > header.Time.Uint64() {
|
||||
return utils.ErrInvalidTimestamp
|
||||
}
|
||||
|
||||
_, penalties, err := x.calcMasternodes(chain, header.Number, header.ParentHash)
|
||||
if err != nil {
|
||||
log.Error("[verifyHeader] Fail to calculate master nodes list with penalty", "Number", header.Number, "Hash", header.Hash())
|
||||
return err
|
||||
}
|
||||
|
||||
if !utils.CompareSignersLists(common.ExtractAddressFromBytes(header.Penalties), penalties) {
|
||||
return utils.ErrPenaltyListDoesNotMatch
|
||||
}
|
||||
|
||||
// Check its validator
|
||||
masterNodes := x.GetMasternodes(chain, header)
|
||||
verified, validatorAddress, err := x.verifyMsgSignature(sigHash(header), header.Validator, masterNodes)
|
||||
if err != nil {
|
||||
for index, mn := range masterNodes {
|
||||
log.Error("[verifyHeader] masternode list during validator verification", "Masternode Address", mn.Hex(), "index", index)
|
||||
}
|
||||
log.Error("[verifyHeader] Error while verifying header validator signature", "BlockNumber", header.Number, "Hash", header.Hash().Hex(), "validator in hex", common.ToHex(header.Validator))
|
||||
return err
|
||||
}
|
||||
if !verified {
|
||||
log.Warn("[verifyHeader] Fail to verify the block validator as the validator address not within the masternode list", header.Number, "Hash", header.Hash().Hex(), "validatorAddress", validatorAddress.Hex())
|
||||
return utils.ErrValidatorNotWithinMasternodes
|
||||
}
|
||||
if validatorAddress != header.Coinbase {
|
||||
log.Warn("[verifyHeader] Header validator and coinbase address not match", header.Number, "Hash", header.Hash().Hex(), "validatorAddress", validatorAddress.Hex(), "coinbase", header.Coinbase.Hex())
|
||||
return utils.ErrCoinbaseAndValidatorMismatch
|
||||
}
|
||||
// Check the proposer is the leader
|
||||
curIndex := utils.Position(masterNodes, validatorAddress)
|
||||
leaderIndex := uint64(round) % x.config.Epoch % uint64(len(masterNodes))
|
||||
if masterNodes[leaderIndex] != validatorAddress {
|
||||
log.Warn("[verifyHeader] Invalid blocker proposer, not its turn", "curIndex", curIndex, "leaderIndex", leaderIndex, "Hash", header.Hash().Hex(), "masterNodes[leaderIndex]", masterNodes[leaderIndex], "validatorAddress", validatorAddress)
|
||||
return utils.ErrNotItsTurn
|
||||
}
|
||||
|
||||
x.verifiedHeaders.Add(header.Hash(), true)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Verify the header validators address is legit by checking against its snapshot masternode list minutes the penalty list, we also ensure the order matches
|
||||
func (x *XDPoS_v2) isValidatorsLegit(chain consensus.ChainReader, header *types.Header) (bool, error) {
|
||||
snap, err := x.getSnapshot(chain, header.Number.Uint64(), false)
|
||||
if err != nil {
|
||||
log.Error("[checkMasternodesOnEpochSwitch] Error while trying to get snapshot", "BlockNumber", header.Number.Int64(), "Hash", header.Hash().Hex(), "error", err)
|
||||
return false, err
|
||||
}
|
||||
// snap.NextEpochMasterNodes
|
||||
penaltyList := common.ExtractAddressFromBytes(header.Penalties)
|
||||
penaltyMap := make(map[common.Address]bool)
|
||||
for _, item := range penaltyList {
|
||||
penaltyMap[item] = true
|
||||
}
|
||||
|
||||
var finalValidMasternodes []common.Address
|
||||
for _, mn := range snap.NextEpochMasterNodes {
|
||||
if penaltyMap[mn] {
|
||||
continue
|
||||
} else {
|
||||
finalValidMasternodes = append(finalValidMasternodes, mn)
|
||||
}
|
||||
}
|
||||
validatorsAddress := common.ExtractAddressFromBytes(header.Validators)
|
||||
return utils.CompareSignersLists(finalValidMasternodes, validatorsAddress), nil
|
||||
}
|
||||
191
consensus/XDPoS/engines/engine_v2/vote.go
Normal file
191
consensus/XDPoS/engines/engine_v2/vote.go
Normal file
|
|
@ -0,0 +1,191 @@
|
|||
package engine_v2
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
"sync"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/common"
|
||||
"github.com/XinFinOrg/XDPoSChain/consensus"
|
||||
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||
"github.com/XinFinOrg/XDPoSChain/log"
|
||||
)
|
||||
|
||||
// Once Hot stuff voting rule has verified, this node can then send vote
|
||||
func (x *XDPoS_v2) sendVote(chainReader consensus.ChainReader, blockInfo *utils.BlockInfo) error {
|
||||
// First step: Update the highest Voted round
|
||||
// Second step: Generate the signature by using node's private key(The signature is the blockInfo signature)
|
||||
// Third step: Construct the vote struct with the above signature & blockinfo struct
|
||||
// Forth step: Send the vote to broadcast channel
|
||||
|
||||
signedHash, err := x.signSignature(utils.VoteSigHash(blockInfo))
|
||||
if err != nil {
|
||||
log.Error("signSignature when sending out Vote", "BlockInfoHash", blockInfo.Hash, "Error", err)
|
||||
return err
|
||||
}
|
||||
|
||||
x.highestVotedRound = x.currentRound
|
||||
voteMsg := &utils.Vote{
|
||||
ProposedBlockInfo: blockInfo,
|
||||
Signature: signedHash,
|
||||
}
|
||||
|
||||
err = x.voteHandler(chainReader, voteMsg)
|
||||
if err != nil {
|
||||
log.Error("sendVote error", "BlockInfoHash", blockInfo.Hash, "Error", err)
|
||||
return err
|
||||
}
|
||||
x.broadcastToBftChannel(voteMsg)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *XDPoS_v2) voteHandler(chain consensus.ChainReader, voteMsg *utils.Vote) error {
|
||||
|
||||
// 1. checkRoundNumber
|
||||
if (voteMsg.ProposedBlockInfo.Round != x.currentRound) && (voteMsg.ProposedBlockInfo.Round != x.currentRound+1) {
|
||||
return &utils.ErrIncomingMessageRoundTooFarFromCurrentRound{
|
||||
Type: "vote",
|
||||
IncomingRound: voteMsg.ProposedBlockInfo.Round,
|
||||
CurrentRound: x.currentRound,
|
||||
}
|
||||
}
|
||||
|
||||
// Collect vote
|
||||
thresholdReached, numberOfVotesInPool, pooledVotes := x.votePool.Add(voteMsg)
|
||||
log.Info("[voteHandler] collect votes", "number", numberOfVotesInPool)
|
||||
if thresholdReached {
|
||||
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
|
||||
proposedBlockHeader := chain.GetHeaderByHash(voteMsg.ProposedBlockInfo.Hash)
|
||||
if proposedBlockHeader == nil {
|
||||
log.Warn("[voteHandler] The proposed block from vote message does not exist yet, wait for the next vote to try again", "Hash", voteMsg.ProposedBlockInfo.Hash, "Round", voteMsg.ProposedBlockInfo.Round)
|
||||
return nil
|
||||
}
|
||||
|
||||
err := x.VerifyBlockInfo(chain, voteMsg.ProposedBlockInfo)
|
||||
if err != nil {
|
||||
x.votePool.ClearPoolKeyByObj(voteMsg)
|
||||
return err
|
||||
}
|
||||
|
||||
err = x.onVotePoolThresholdReached(chain, pooledVotes, voteMsg, proposedBlockHeader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
/*
|
||||
Function that will be called by votePool when it reached threshold.
|
||||
In the engine v2, we will need to generate and process QC
|
||||
*/
|
||||
func (x *XDPoS_v2) onVotePoolThresholdReached(chain consensus.ChainReader, pooledVotes map[common.Hash]utils.PoolObj, currentVoteMsg utils.PoolObj, proposedBlockHeader *types.Header) error {
|
||||
|
||||
masternodes := x.GetMasternodes(chain, proposedBlockHeader)
|
||||
|
||||
// Filter out non-Master nodes signatures
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(len(pooledVotes))
|
||||
signatureSlice := make([]utils.Signature, len(pooledVotes))
|
||||
counter := 0
|
||||
for h, vote := range pooledVotes {
|
||||
go func(hash common.Hash, v *utils.Vote, i int) {
|
||||
defer wg.Done()
|
||||
verified, _, err := x.verifyMsgSignature(utils.VoteSigHash(v.ProposedBlockInfo), v.Signature, masternodes)
|
||||
if !verified || err != nil {
|
||||
log.Warn("[onVotePoolThresholdReached] Skip not verified vote signatures when building QC", "Error", err.Error(), "verified", verified)
|
||||
} else {
|
||||
signatureSlice[i] = v.Signature
|
||||
}
|
||||
}(h, vote.(*utils.Vote), counter)
|
||||
counter++
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
// The signature list may contain empty entey. we only care the ones with values
|
||||
var validSignatureSlice []utils.Signature
|
||||
for _, v := range signatureSlice {
|
||||
if len(v) != 0 {
|
||||
validSignatureSlice = append(validSignatureSlice, v)
|
||||
}
|
||||
}
|
||||
|
||||
// Skip and wait for the next vote to process again if valid votes is less than what we required
|
||||
if len(validSignatureSlice) < x.config.V2.CertThreshold {
|
||||
log.Warn("[onVotePoolThresholdReached] Not enough valid signatures to generate QC", "VotesSignaturesAfterFilter", validSignatureSlice, "NumberOfValidVotes", len(validSignatureSlice), "NumberOfVotes", len(pooledVotes))
|
||||
return nil
|
||||
}
|
||||
// Genrate QC
|
||||
quorumCert := &utils.QuorumCert{
|
||||
ProposedBlockInfo: currentVoteMsg.(*utils.Vote).ProposedBlockInfo,
|
||||
Signatures: validSignatureSlice,
|
||||
}
|
||||
err := x.processQC(chain, quorumCert)
|
||||
if err != nil {
|
||||
log.Error("Error while processing QC in the Vote handler after reaching pool threshold, ", err)
|
||||
return err
|
||||
}
|
||||
log.Info("Successfully processed the vote and produced QC!", "QcRound", quorumCert.ProposedBlockInfo.Round, "QcNumOfSig", len(quorumCert.Signatures), "QcHash", quorumCert.ProposedBlockInfo.Hash, "QcNumber", quorumCert.ProposedBlockInfo.Number.Uint64())
|
||||
// clean up vote at the same poolKey. and pookKey is proposed block hash
|
||||
x.votePool.ClearPoolKeyByObj(currentVoteMsg)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Hot stuff rule to decide whether this node is eligible to vote for the received block
|
||||
func (x *XDPoS_v2) verifyVotingRule(blockChainReader consensus.ChainReader, blockInfo *utils.BlockInfo, quorumCert *utils.QuorumCert) (bool, error) {
|
||||
// Make sure this node has not voted for this round.
|
||||
if x.currentRound <= x.highestVotedRound {
|
||||
return false, nil
|
||||
}
|
||||
/*
|
||||
HotStuff Voting rule:
|
||||
header's round == local current round, AND (one of the following two:)
|
||||
header's block extends lockQuorumCert's ProposedBlockInfo (we need a isExtending(block_a, block_b) function), OR
|
||||
header's QC's ProposedBlockInfo.Round > lockQuorumCert's ProposedBlockInfo.Round
|
||||
*/
|
||||
if blockInfo.Round != x.currentRound {
|
||||
return false, nil
|
||||
}
|
||||
// XDPoS v1.0 switch to v2.0, the proposed block can always pass voting rule
|
||||
if x.lockQuorumCert == nil {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
if quorumCert.ProposedBlockInfo.Round > x.lockQuorumCert.ProposedBlockInfo.Round {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
isExtended, err := x.isExtendingFromAncestor(blockChainReader, blockInfo, x.lockQuorumCert.ProposedBlockInfo)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if isExtended {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (x *XDPoS_v2) isExtendingFromAncestor(blockChainReader consensus.ChainReader, currentBlock *utils.BlockInfo, ancestorBlock *utils.BlockInfo) (bool, error) {
|
||||
blockNumDiff := int(big.NewInt(0).Sub(currentBlock.Number, ancestorBlock.Number).Int64())
|
||||
|
||||
nextBlockHash := currentBlock.Hash
|
||||
for i := 0; i < blockNumDiff; i++ {
|
||||
parentBlock := blockChainReader.GetHeaderByHash(nextBlockHash)
|
||||
if parentBlock == nil {
|
||||
return false, fmt.Errorf("Could not find its parent block when checking whether currentBlock %v with hash %v is extending from the ancestorBlock %v", currentBlock.Number, currentBlock.Hash, ancestorBlock.Number)
|
||||
} else {
|
||||
nextBlockHash = parentBlock.ParentHash
|
||||
}
|
||||
log.Debug("[isExtendingFromAncestor] Found parent block", "CurrentBlockHash", currentBlock.Hash, "ParentHash", nextBlockHash)
|
||||
}
|
||||
|
||||
if nextBlockHash == ancestorBlock.Hash {
|
||||
return true, nil
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
|
@ -45,6 +45,8 @@ var (
|
|||
|
||||
ErrInvalidCheckpointPenalties = errors.New("invalid penalty list on checkpoint block")
|
||||
|
||||
ErrValidatorsNotLegit = errors.New("Validators does not match what's stored in snapshot minutes its penalty")
|
||||
|
||||
// errInvalidMixDigest is returned if a block's mix digest is non-zero.
|
||||
ErrInvalidMixDigest = errors.New("non-zero mix digest")
|
||||
|
||||
|
|
@ -80,11 +82,14 @@ var (
|
|||
|
||||
ErrEmptyEpochSwitchValidators = errors.New("empty validators list on epoch switch block")
|
||||
|
||||
ErrInvalidV2Extra = errors.New("Invalid v2 extra in the block")
|
||||
ErrInvalidQC = errors.New("Invalid QC content")
|
||||
ErrInvalidTC = errors.New("Invalid TC content")
|
||||
ErrEmptyBlockInfoHash = errors.New("BlockInfo hash is empty")
|
||||
ErrInvalidFieldInNonEpochSwitch = errors.New("Invalid field exist in a non-epoch swtich block")
|
||||
ErrInvalidV2Extra = errors.New("Invalid v2 extra in the block")
|
||||
ErrInvalidQC = errors.New("Invalid QC content")
|
||||
ErrInvalidTC = errors.New("Invalid TC content")
|
||||
ErrEmptyBlockInfoHash = errors.New("BlockInfo hash is empty")
|
||||
ErrInvalidFieldInNonEpochSwitch = errors.New("Invalid field exist in a non-epoch swtich block")
|
||||
ErrValidatorNotWithinMasternodes = errors.New("Validaotor address is not in the master node list")
|
||||
ErrCoinbaseAndValidatorMismatch = errors.New("Validaotor and coinbase address in header does not match")
|
||||
ErrNotItsTurn = errors.New("Not validator's turn to mine this block")
|
||||
|
||||
ErrPenaltyListDoesNotMatch = errors.New("Incoming block penalty list does not match")
|
||||
ErrRoundInvalid = errors.New("Invalid Round, it shall be bigger than QC round")
|
||||
|
|
|
|||
|
|
@ -40,4 +40,6 @@ var (
|
|||
ErrNoValidatorSignature = errors.New("no validator in header")
|
||||
|
||||
ErrNotReadyToPropose = errors.New("not ready to propose, QC is not ready")
|
||||
|
||||
ErrCoinbaseMismatch = errors.New("Block Coinbase address does not match its wallte address")
|
||||
)
|
||||
|
|
|
|||
75
consensus/tests/engine_v1_tests/authorised_test.go
Normal file
75
consensus/tests/engine_v1_tests/authorised_test.go
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
package engine_v1_tests
|
||||
|
||||
import (
|
||||
"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 TestIsAuthorisedMNForConsensusV1(t *testing.T) {
|
||||
/*
|
||||
V1 consensus engine
|
||||
*/
|
||||
blockchain, _, parentBlock, signer, signFn := PrepareXDCTestBlockChain(t, GAP-2, params.TestXDPoSMockChainConfig)
|
||||
// Insert first Block 449
|
||||
t.Logf("Inserting block with propose at 449...")
|
||||
blockCoinbaseA := "0xaaa0000000000000000000000000000000000449"
|
||||
tx, err := voteTX(37117, 0, acc1Addr.String())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
//Get from block validator error message
|
||||
merkleRoot := "46234e9cd7e85a267f7f0435b15256a794a2f6d65cc98cdbd21dcd10a01d9772"
|
||||
header := &types.Header{
|
||||
Root: common.HexToHash(merkleRoot),
|
||||
Number: big.NewInt(int64(449)),
|
||||
ParentHash: parentBlock.Hash(),
|
||||
Coinbase: common.HexToAddress(blockCoinbaseA),
|
||||
}
|
||||
block449, err := createBlockFromHeader(blockchain, header, []*types.Transaction{tx}, signer, signFn, blockchain.Config())
|
||||
assert.Nil(t, err)
|
||||
err = blockchain.InsertBlock(block449)
|
||||
assert.Nil(t, err)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
parentBlock = block449
|
||||
|
||||
// At block 449, we should not update signerList. we need to update it till block 450 gap block.
|
||||
// Acc3 is the default account that is on the signerList
|
||||
|
||||
engine := blockchain.Engine().(*XDPoS.XDPoS)
|
||||
isAuthorisedMN := engine.IsAuthorisedAddress(blockchain, block449.Header(), acc3Addr)
|
||||
assert.True(t, isAuthorisedMN)
|
||||
|
||||
isAuthorisedMN = engine.IsAuthorisedAddress(blockchain, block449.Header(), acc1Addr)
|
||||
assert.False(t, isAuthorisedMN)
|
||||
|
||||
// Now, let's mine another block to trigger the GAP block signerList update
|
||||
block450CoinbaseAddress := "0xaaa0000000000000000000000000000000000450"
|
||||
merkleRoot = "46234e9cd7e85a267f7f0435b15256a794a2f6d65cc98cdbd21dcd10a01d9772"
|
||||
header = &types.Header{
|
||||
Root: common.HexToHash(merkleRoot),
|
||||
Number: big.NewInt(int64(450)),
|
||||
ParentHash: parentBlock.Hash(),
|
||||
Coinbase: common.HexToAddress(block450CoinbaseAddress),
|
||||
}
|
||||
block450, err := createBlockFromHeader(blockchain, header, nil, signer, signFn, blockchain.Config())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = blockchain.InsertBlock(block450)
|
||||
assert.Nil(t, err)
|
||||
|
||||
isAuthorisedMN = engine.IsAuthorisedAddress(blockchain, block450.Header(), acc3Addr)
|
||||
assert.False(t, isAuthorisedMN)
|
||||
|
||||
isAuthorisedMN = engine.IsAuthorisedAddress(blockchain, block450.Header(), acc1Addr)
|
||||
assert.True(t, isAuthorisedMN)
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package tests
|
||||
package engine_v1_tests
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
|
@ -9,11 +9,12 @@ import (
|
|||
"github.com/XinFinOrg/XDPoSChain/common"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||
"github.com/XinFinOrg/XDPoSChain/params"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
// Should NOT update signerList if not on the gap block
|
||||
func TestNotUpdateSignerListIfNotOnGapBlock(t *testing.T) {
|
||||
blockchain, backend, parentBlock, _ := PrepareXDCTestBlockChain(t, 400, params.TestXDPoSMockChainConfig)
|
||||
blockchain, backend, parentBlock, signer, signFn := PrepareXDCTestBlockChain(t, 400, params.TestXDPoSMockChainConfig)
|
||||
parentSigners, err := GetSnapshotSigner(blockchain, parentBlock.Header())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
|
@ -33,12 +34,12 @@ func TestNotUpdateSignerListIfNotOnGapBlock(t *testing.T) {
|
|||
ParentHash: parentBlock.Hash(),
|
||||
Coinbase: common.HexToAddress(blockCoinbaseA),
|
||||
}
|
||||
blockA, err := createBlockFromHeader(blockchain, header, []*types.Transaction{tx})
|
||||
blockA, err := createBlockFromHeader(blockchain, header, []*types.Transaction{tx}, signer, signFn, blockchain.Config())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
blockchain.InsertBlock(blockA)
|
||||
|
||||
err = blockchain.InsertBlock(blockA)
|
||||
assert.Nil(t, err)
|
||||
signers, err := GetSnapshotSigner(blockchain, blockA.Header())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
|
@ -58,7 +59,7 @@ func TestNotUpdateSignerListIfNotOnGapBlock(t *testing.T) {
|
|||
|
||||
// Should call updateM1 at the gap block, and have the same snapshot values as the parent block if no SM transaction is involved
|
||||
func TestNotChangeSingerListIfNothingProposedOrVoted(t *testing.T) {
|
||||
blockchain, _, parentBlock, _ := PrepareXDCTestBlockChain(t, GAP-1, params.TestXDPoSMockChainConfig)
|
||||
blockchain, _, parentBlock, signer, signFn := PrepareXDCTestBlockChain(t, GAP-1, params.TestXDPoSMockChainConfig)
|
||||
// Insert block 450
|
||||
blockCoinBase := fmt.Sprintf("0x111000000000000000000000000000000%03d", 450)
|
||||
merkleRoot := "35999dded35e8db12de7e6c1471eb9670c162eec616ecebbaf4fddd4676fb930"
|
||||
|
|
@ -68,11 +69,12 @@ func TestNotChangeSingerListIfNothingProposedOrVoted(t *testing.T) {
|
|||
ParentHash: parentBlock.Hash(),
|
||||
Coinbase: common.HexToAddress(blockCoinBase),
|
||||
}
|
||||
block, err := createBlockFromHeader(blockchain, header, nil)
|
||||
block, err := createBlockFromHeader(blockchain, header, nil, signer, signFn, blockchain.Config())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
blockchain.InsertBlock(block)
|
||||
err = blockchain.InsertBlock(block)
|
||||
assert.Nil(t, err)
|
||||
parentSigners, err := GetSnapshotSigner(blockchain, parentBlock.Header())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
|
@ -93,7 +95,7 @@ func TestNotChangeSingerListIfNothingProposedOrVoted(t *testing.T) {
|
|||
//Should call updateM1 at gap block, and update the snapshot if there are SM transactions involved
|
||||
func TestUpdateSignerListIfVotedBeforeGap(t *testing.T) {
|
||||
|
||||
blockchain, backend, parentBlock, _ := PrepareXDCTestBlockChain(t, GAP-2, params.TestXDPoSMockChainConfig)
|
||||
blockchain, backend, parentBlock, signer, signFn := PrepareXDCTestBlockChain(t, GAP-2, params.TestXDPoSMockChainConfig)
|
||||
// Insert first Block 449
|
||||
t.Logf("Inserting block with propose at 449...")
|
||||
blockCoinbaseA := "0xaaa0000000000000000000000000000000000449"
|
||||
|
|
@ -110,12 +112,12 @@ func TestUpdateSignerListIfVotedBeforeGap(t *testing.T) {
|
|||
ParentHash: parentBlock.Hash(),
|
||||
Coinbase: common.HexToAddress(blockCoinbaseA),
|
||||
}
|
||||
block449, err := createBlockFromHeader(blockchain, header, []*types.Transaction{tx})
|
||||
block449, err := createBlockFromHeader(blockchain, header, []*types.Transaction{tx}, signer, signFn, blockchain.Config())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
blockchain.InsertBlock(block449)
|
||||
|
||||
err = blockchain.InsertBlock(block449)
|
||||
assert.Nil(t, err)
|
||||
parentBlock = block449
|
||||
|
||||
signers, err := GetSnapshotSigner(blockchain, block449.Header())
|
||||
|
|
@ -142,12 +144,12 @@ func TestUpdateSignerListIfVotedBeforeGap(t *testing.T) {
|
|||
ParentHash: parentBlock.Hash(),
|
||||
Coinbase: common.HexToAddress(block450CoinbaseAddress),
|
||||
}
|
||||
block450, err := createBlockFromHeader(blockchain, header, nil)
|
||||
block450, err := createBlockFromHeader(blockchain, header, nil, signer, signFn, blockchain.Config())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
blockchain.InsertBlock(block450)
|
||||
|
||||
err = blockchain.InsertBlock(block450)
|
||||
assert.Nil(t, err)
|
||||
signers, err = GetSnapshotSigner(blockchain, block450.Header())
|
||||
if err != nil {
|
||||
t.Fatalf("Failed while trying to get signers")
|
||||
|
|
@ -166,7 +168,7 @@ func TestUpdateSignerListIfVotedBeforeGap(t *testing.T) {
|
|||
//Should call updateM1 before gap block, and update the snapshot if there are SM transactions involved
|
||||
func TestCallUpdateM1WithSmartContractTranscation(t *testing.T) {
|
||||
|
||||
blockchain, backend, currentBlock, _ := PrepareXDCTestBlockChain(t, GAP-1, params.TestXDPoSMockChainConfig)
|
||||
blockchain, backend, currentBlock, signer, signFn := PrepareXDCTestBlockChain(t, GAP-1, params.TestXDPoSMockChainConfig)
|
||||
// Insert first Block 450 A
|
||||
t.Logf("Inserting block with propose at 450 A...")
|
||||
blockCoinbaseA := "0xaaa0000000000000000000000000000000000450"
|
||||
|
|
@ -183,12 +185,12 @@ func TestCallUpdateM1WithSmartContractTranscation(t *testing.T) {
|
|||
ParentHash: currentBlock.Hash(),
|
||||
Coinbase: common.HexToAddress(blockCoinbaseA),
|
||||
}
|
||||
blockA, err := createBlockFromHeader(blockchain, header, []*types.Transaction{tx})
|
||||
blockA, err := createBlockFromHeader(blockchain, header, []*types.Transaction{tx}, signer, signFn, blockchain.Config())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
blockchain.InsertBlock(blockA)
|
||||
|
||||
err = blockchain.InsertBlock(blockA)
|
||||
assert.Nil(t, err)
|
||||
signers, err := GetSnapshotSigner(blockchain, blockA.Header())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
|
@ -202,7 +204,7 @@ func TestCallUpdateM1WithSmartContractTranscation(t *testing.T) {
|
|||
// Should call updateM1 and update snapshot when a forked block(at gap block number) is inserted back into main chain (Edge case)
|
||||
func TestCallUpdateM1WhenForkedBlockBackToMainChain(t *testing.T) {
|
||||
|
||||
blockchain, backend, currentBlock, _ := PrepareXDCTestBlockChain(t, GAP-1, params.TestXDPoSMockChainConfig)
|
||||
blockchain, backend, currentBlock, signer, signFn := PrepareXDCTestBlockChain(t, GAP-1, params.TestXDPoSMockChainConfig)
|
||||
// Check initial signer, by default, acc3 is in the signerList
|
||||
signers, err := GetSnapshotSigner(blockchain, blockchain.CurrentBlock().Header())
|
||||
if err != nil {
|
||||
|
|
@ -232,12 +234,12 @@ func TestCallUpdateM1WhenForkedBlockBackToMainChain(t *testing.T) {
|
|||
ParentHash: currentBlock.Hash(),
|
||||
Coinbase: common.HexToAddress(blockCoinbaseA),
|
||||
}
|
||||
blockA, err := createBlockFromHeader(blockchain, header, []*types.Transaction{tx})
|
||||
blockA, err := createBlockFromHeader(blockchain, header, []*types.Transaction{tx}, signer, signFn, blockchain.Config())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
blockchain.InsertBlock(blockA)
|
||||
|
||||
err = blockchain.InsertBlock(blockA)
|
||||
assert.Nil(t, err)
|
||||
signers, err = GetSnapshotSigner(blockchain, blockA.Header())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
|
@ -267,11 +269,12 @@ func TestCallUpdateM1WhenForkedBlockBackToMainChain(t *testing.T) {
|
|||
ParentHash: currentBlock.Hash(),
|
||||
Coinbase: common.HexToAddress(blockCoinBase450B),
|
||||
}
|
||||
block450B, err := createBlockFromHeader(blockchain, header, []*types.Transaction{tx})
|
||||
block450B, err := createBlockFromHeader(blockchain, header, []*types.Transaction{tx}, signer, signFn, blockchain.Config())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
blockchain.InsertBlock(block450B)
|
||||
err = blockchain.InsertBlock(block450B)
|
||||
assert.Nil(t, err)
|
||||
signers, err = GetSnapshotSigner(blockchain, block450B.Header())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
|
@ -297,9 +300,10 @@ func TestCallUpdateM1WhenForkedBlockBackToMainChain(t *testing.T) {
|
|||
ParentHash: block450B.Hash(),
|
||||
Coinbase: common.HexToAddress(blockCoinBase451B),
|
||||
}
|
||||
block451B, err := createBlockFromHeader(blockchain, header, nil)
|
||||
blockchain.InsertBlock(block451B)
|
||||
|
||||
block451B, err := createBlockFromHeader(blockchain, header, nil, signer, signFn, blockchain.Config())
|
||||
assert.Nil(t, err)
|
||||
err = blockchain.InsertBlock(block451B)
|
||||
assert.Nil(t, err)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
@ -346,7 +350,7 @@ func TestCallUpdateM1WhenForkedBlockBackToMainChain(t *testing.T) {
|
|||
|
||||
func TestStatesShouldBeUpdatedWhenForkedBlockBecameMainChainAtGapBlock(t *testing.T) {
|
||||
|
||||
blockchain, backend, parentBlock, _ := PrepareXDCTestBlockChain(t, GAP-1, params.TestXDPoSMockChainConfig)
|
||||
blockchain, backend, parentBlock, signer, signFn := PrepareXDCTestBlockChain(t, GAP-1, params.TestXDPoSMockChainConfig)
|
||||
|
||||
state, err := blockchain.State()
|
||||
if err != nil {
|
||||
|
|
@ -380,11 +384,12 @@ func TestStatesShouldBeUpdatedWhenForkedBlockBecameMainChainAtGapBlock(t *testin
|
|||
ParentHash: parentBlock.Hash(),
|
||||
Coinbase: common.HexToAddress(blockCoinbaseA),
|
||||
}
|
||||
blockA, err := createBlockFromHeader(blockchain, header, []*types.Transaction{tx, transferTransaction})
|
||||
blockA, err := createBlockFromHeader(blockchain, header, []*types.Transaction{tx, transferTransaction}, signer, signFn, blockchain.Config())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
blockchain.InsertBlock(blockA)
|
||||
err = blockchain.InsertBlock(blockA)
|
||||
assert.Nil(t, err)
|
||||
state, err = blockchain.State()
|
||||
if err != nil {
|
||||
t.Fatalf("Failed while trying to get blockchain state")
|
||||
|
|
@ -422,11 +427,12 @@ func TestStatesShouldBeUpdatedWhenForkedBlockBecameMainChainAtGapBlock(t *testin
|
|||
ParentHash: parentBlock.Hash(),
|
||||
Coinbase: common.HexToAddress(blockCoinBase450B),
|
||||
}
|
||||
block450B, err := createBlockFromHeader(blockchain, header, []*types.Transaction{tx, transferTransaction})
|
||||
block450B, err := createBlockFromHeader(blockchain, header, []*types.Transaction{tx, transferTransaction}, signer, signFn, blockchain.Config())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
blockchain.InsertBlock(block450B)
|
||||
err = blockchain.InsertBlock(block450B)
|
||||
assert.Nil(t, err)
|
||||
state, err = blockchain.State()
|
||||
if err != nil {
|
||||
t.Fatalf("Failed while trying to get blockchain state")
|
||||
|
|
@ -456,11 +462,12 @@ func TestStatesShouldBeUpdatedWhenForkedBlockBecameMainChainAtGapBlock(t *testin
|
|||
ParentHash: block450B.Hash(),
|
||||
Coinbase: common.HexToAddress(blockCoinBase451B),
|
||||
}
|
||||
block451B, err := createBlockFromHeader(blockchain, header, nil)
|
||||
block451B, err := createBlockFromHeader(blockchain, header, nil, signer, signFn, blockchain.Config())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
blockchain.InsertBlock(block451B)
|
||||
err = blockchain.InsertBlock(block451B)
|
||||
assert.Nil(t, err)
|
||||
|
||||
signers, err = GetSnapshotSigner(blockchain, block450B.Header())
|
||||
if err != nil {
|
||||
|
|
@ -500,7 +507,7 @@ func TestStatesShouldBeUpdatedWhenForkedBlockBecameMainChainAtGapBlock(t *testin
|
|||
}
|
||||
|
||||
func TestVoteShouldNotBeAffectedByFork(t *testing.T) {
|
||||
blockchain, backend, parentBlock, _ := PrepareXDCTestBlockChain(t, GAP-1, params.TestXDPoSMockChainConfig)
|
||||
blockchain, backend, parentBlock, signer, signFn := PrepareXDCTestBlockChain(t, GAP-1, params.TestXDPoSMockChainConfig)
|
||||
// Check initial signer, by default, acc3 is in the signerList
|
||||
signers, err := GetSnapshotSigner(blockchain, blockchain.CurrentBlock().Header())
|
||||
if err != nil {
|
||||
|
|
@ -524,11 +531,12 @@ func TestVoteShouldNotBeAffectedByFork(t *testing.T) {
|
|||
ParentHash: parentBlock.Hash(),
|
||||
Coinbase: common.HexToAddress(blockCoinBase450A),
|
||||
}
|
||||
block450A, err := createBlockFromHeader(blockchain, header, nil)
|
||||
block450A, err := createBlockFromHeader(blockchain, header, nil, signer, signFn, blockchain.Config())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
blockchain.InsertBlock(block450A)
|
||||
err = blockchain.InsertBlock(block450A)
|
||||
assert.Nil(t, err)
|
||||
|
||||
// Insert 451 A with vote
|
||||
blockCoinbase451A := "0xaaa0000000000000000000000000000000000451"
|
||||
|
|
@ -544,11 +552,12 @@ func TestVoteShouldNotBeAffectedByFork(t *testing.T) {
|
|||
ParentHash: block450A.Hash(),
|
||||
Coinbase: common.HexToAddress(blockCoinbase451A),
|
||||
}
|
||||
block451A, err := createBlockFromHeader(blockchain, header, []*types.Transaction{tx})
|
||||
block451A, err := createBlockFromHeader(blockchain, header, []*types.Transaction{tx}, signer, signFn, blockchain.Config())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
blockchain.InsertBlock(block451A)
|
||||
err = blockchain.InsertBlock(block451A)
|
||||
assert.Nil(t, err)
|
||||
|
||||
// SignerList should be unchanged as the vote happen after GAP block
|
||||
signers, err = GetSnapshotSigner(blockchain, block451A.Header())
|
||||
|
|
@ -574,11 +583,12 @@ func TestVoteShouldNotBeAffectedByFork(t *testing.T) {
|
|||
ParentHash: parentBlock.Hash(),
|
||||
Coinbase: common.HexToAddress(blockCoinBase450B),
|
||||
}
|
||||
block450B, err := createBlockFromHeader(blockchain, header, nil)
|
||||
block450B, err := createBlockFromHeader(blockchain, header, nil, signer, signFn, blockchain.Config())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
blockchain.InsertBlock(block450B)
|
||||
err = blockchain.InsertBlock(block450B)
|
||||
assert.Nil(t, err)
|
||||
|
||||
blockCoinBase451B := "0xbbb0000000000000000000000000000000000451"
|
||||
merkleRoot = "35999dded35e8db12de7e6c1471eb9670c162eec616ecebbaf4fddd4676fb930"
|
||||
|
|
@ -588,11 +598,12 @@ func TestVoteShouldNotBeAffectedByFork(t *testing.T) {
|
|||
ParentHash: block450B.Hash(),
|
||||
Coinbase: common.HexToAddress(blockCoinBase451B),
|
||||
}
|
||||
block451B, err := createBlockFromHeader(blockchain, header, nil)
|
||||
block451B, err := createBlockFromHeader(blockchain, header, nil, signer, signFn, blockchain.Config())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
blockchain.InsertBlock(block451B)
|
||||
err = blockchain.InsertBlock(block451B)
|
||||
assert.Nil(t, err)
|
||||
|
||||
blockCoinBase452B := "0xbbb0000000000000000000000000000000000452"
|
||||
merkleRoot = "35999dded35e8db12de7e6c1471eb9670c162eec616ecebbaf4fddd4676fb930"
|
||||
|
|
@ -602,11 +613,12 @@ func TestVoteShouldNotBeAffectedByFork(t *testing.T) {
|
|||
ParentHash: block451B.Hash(),
|
||||
Coinbase: common.HexToAddress(blockCoinBase452B),
|
||||
}
|
||||
block452B, err := createBlockFromHeader(blockchain, header, nil)
|
||||
block452B, err := createBlockFromHeader(blockchain, header, nil, signer, signFn, blockchain.Config())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
blockchain.InsertBlock(block452B)
|
||||
err = blockchain.InsertBlock(block452B)
|
||||
assert.Nil(t, err)
|
||||
signers, err = GetSnapshotSigner(blockchain, block452B.Header())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
|
@ -626,7 +638,7 @@ func TestVoteShouldNotBeAffectedByFork(t *testing.T) {
|
|||
// Pending for creating cross version blocks
|
||||
func TestV2UpdateSignerListIfVotedBeforeGap(t *testing.T) {
|
||||
config := params.TestXDPoSMockChainConfig
|
||||
blockchain, backend, parentBlock, _ := PrepareXDCTestBlockChain(t, int(config.XDPoS.Epoch)+GAP-2, config)
|
||||
blockchain, backend, parentBlock, signer, signFn := PrepareXDCTestBlockChain(t, int(config.XDPoS.Epoch)+GAP-2, config)
|
||||
// Insert first Block 1349
|
||||
t.Logf("Inserting block with propose at 1349...")
|
||||
blockCoinbaseA := "0xaaa0000000000000000000000000000000001349"
|
||||
|
|
@ -643,7 +655,7 @@ func TestV2UpdateSignerListIfVotedBeforeGap(t *testing.T) {
|
|||
ParentHash: parentBlock.Hash(),
|
||||
Coinbase: common.HexToAddress(blockCoinbaseA),
|
||||
}
|
||||
block1349, err := insertBlockTxs(blockchain, header, []*types.Transaction{tx})
|
||||
block1349, err := insertBlockTxs(blockchain, header, []*types.Transaction{tx}, signer, signFn, blockchain.Config())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package tests
|
||||
package engine_v1_tests
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
|
|
@ -7,12 +7,13 @@ import (
|
|||
"github.com/XinFinOrg/XDPoSChain/common"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||
"github.com/XinFinOrg/XDPoSChain/params"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
// Snapshot try to read before blockchain is written
|
||||
func TestRaceConditionOnBlockchainReadAndWrite(t *testing.T) {
|
||||
|
||||
blockchain, backend, parentBlock, _ := PrepareXDCTestBlockChain(t, GAP-1, params.TestXDPoSMockChainConfig)
|
||||
blockchain, backend, parentBlock, signer, signFn := PrepareXDCTestBlockChain(t, GAP-1, params.TestXDPoSMockChainConfig)
|
||||
|
||||
state, err := blockchain.State()
|
||||
if err != nil {
|
||||
|
|
@ -48,11 +49,12 @@ func TestRaceConditionOnBlockchainReadAndWrite(t *testing.T) {
|
|||
Coinbase: common.HexToAddress(blockCoinbaseA),
|
||||
}
|
||||
|
||||
blockA, err := createBlockFromHeader(blockchain, header, []*types.Transaction{tx, transferTransaction})
|
||||
blockA, err := createBlockFromHeader(blockchain, header, []*types.Transaction{tx, transferTransaction}, signer, signFn, blockchain.Config())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
blockchain.InsertBlock(blockA)
|
||||
err = blockchain.InsertBlock(blockA)
|
||||
assert.Nil(t, err)
|
||||
state, err = blockchain.State()
|
||||
if err != nil {
|
||||
t.Fatalf("Failed while trying to get blockchain state")
|
||||
|
|
@ -93,11 +95,12 @@ func TestRaceConditionOnBlockchainReadAndWrite(t *testing.T) {
|
|||
Difficulty: big.NewInt(2),
|
||||
}
|
||||
|
||||
block450B, err := createBlockFromHeader(blockchain, header, []*types.Transaction{tx, transferTransaction})
|
||||
block450B, err := createBlockFromHeader(blockchain, header, []*types.Transaction{tx, transferTransaction}, signer, signFn, blockchain.Config())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
blockchain.InsertBlock(block450B)
|
||||
err = blockchain.InsertBlock(block450B)
|
||||
assert.Nil(t, err)
|
||||
if blockchain.CurrentHeader().Hash() != block450B.Hash() {
|
||||
t.Fatalf("the block with higher difficulty should be current header")
|
||||
}
|
||||
|
|
@ -131,11 +134,12 @@ func TestRaceConditionOnBlockchainReadAndWrite(t *testing.T) {
|
|||
Coinbase: common.HexToAddress(blockCoinBase451B),
|
||||
Difficulty: big.NewInt(3),
|
||||
}
|
||||
block451B, err := createBlockFromHeader(blockchain, header, nil)
|
||||
block451B, err := createBlockFromHeader(blockchain, header, nil, signer, signFn, blockchain.Config())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
blockchain.InsertBlock(block451B)
|
||||
err = blockchain.InsertBlock(block451B)
|
||||
assert.Nil(t, err)
|
||||
|
||||
signers, err = GetSnapshotSigner(blockchain, block450B.Header())
|
||||
if err != nil {
|
||||
420
consensus/tests/engine_v1_tests/helper.go
Normal file
420
consensus/tests/engine_v1_tests/helper.go
Normal file
|
|
@ -0,0 +1,420 @@
|
|||
package engine_v1_tests
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"math/rand"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/accounts"
|
||||
"github.com/XinFinOrg/XDPoSChain/accounts/abi/bind"
|
||||
"github.com/XinFinOrg/XDPoSChain/accounts/abi/bind/backends"
|
||||
"github.com/XinFinOrg/XDPoSChain/common"
|
||||
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS"
|
||||
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils"
|
||||
contractValidator "github.com/XinFinOrg/XDPoSChain/contracts/validator/contract"
|
||||
"github.com/XinFinOrg/XDPoSChain/core"
|
||||
. "github.com/XinFinOrg/XDPoSChain/core"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/vm"
|
||||
"github.com/XinFinOrg/XDPoSChain/crypto"
|
||||
"github.com/XinFinOrg/XDPoSChain/log"
|
||||
"github.com/XinFinOrg/XDPoSChain/params"
|
||||
"github.com/XinFinOrg/XDPoSChain/rlp"
|
||||
)
|
||||
|
||||
type masterNodes map[string]big.Int
|
||||
type signersList map[string]bool
|
||||
|
||||
const GAP = int(450)
|
||||
|
||||
var (
|
||||
acc1Key, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a")
|
||||
acc2Key, _ = crypto.HexToECDSA("49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee")
|
||||
acc3Key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||
voterKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee04aefe388d1e14474d32c45c72ce7b7a")
|
||||
acc1Addr = crypto.PubkeyToAddress(acc1Key.PublicKey) //xdc703c4b2bD70c169f5717101CaeE543299Fc946C7
|
||||
acc2Addr = crypto.PubkeyToAddress(acc2Key.PublicKey) //xdc0D3ab14BBaD3D99F4203bd7a11aCB94882050E7e
|
||||
acc3Addr = crypto.PubkeyToAddress(acc3Key.PublicKey) //xdc71562b71999873DB5b286dF957af199Ec94617F7
|
||||
voterAddr = crypto.PubkeyToAddress(voterKey.PublicKey) //xdc5F74529C0338546f82389402a01c31fB52c6f434
|
||||
chainID = int64(1337)
|
||||
)
|
||||
|
||||
func debugMessage(backend *backends.SimulatedBackend, signers signersList, t *testing.T) {
|
||||
ms := GetCandidateFromCurrentSmartContract(backend, t)
|
||||
fmt.Println("=== current smart contract")
|
||||
for nodeAddr, cap := range ms {
|
||||
if !strings.Contains(nodeAddr, "000000000000000000000000000000000000") { //remove defaults
|
||||
fmt.Println(nodeAddr, cap)
|
||||
}
|
||||
}
|
||||
fmt.Println("=== this block signer list")
|
||||
for signer := range signers {
|
||||
if !strings.Contains(signer, "000000000000000000000000000000000000") { //remove defaults
|
||||
fmt.Println(signer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
|
||||
func RandStringBytes(n int) string {
|
||||
b := make([]byte, n)
|
||||
for i := range b {
|
||||
b[i] = letterBytes[rand.Intn(len(letterBytes))]
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
|
||||
func getCommonBackend(t *testing.T, chainConfig *params.ChainConfig) *backends.SimulatedBackend {
|
||||
|
||||
// initial helper backend
|
||||
contractBackendForSC := backends.NewXDCSimulatedBackend(core.GenesisAlloc{
|
||||
voterAddr: {Balance: new(big.Int).SetUint64(10000000000)},
|
||||
}, 10000000, chainConfig)
|
||||
|
||||
transactOpts := bind.NewKeyedTransactor(voterKey)
|
||||
|
||||
var candidates []common.Address
|
||||
var caps []*big.Int
|
||||
defalutCap := new(big.Int)
|
||||
defalutCap.SetString("1000000000", 10)
|
||||
|
||||
for i := 1; i <= 16; i++ {
|
||||
addr := fmt.Sprintf("%02d", i)
|
||||
candidates = append(candidates, common.StringToAddress(addr)) // StringToAddress does not exist
|
||||
caps = append(caps, defalutCap)
|
||||
}
|
||||
|
||||
acc1Cap, acc2Cap, acc3Cap, voterCap := new(big.Int), new(big.Int), new(big.Int), new(big.Int)
|
||||
|
||||
acc1Cap.SetString("10000001", 10)
|
||||
acc2Cap.SetString("10000002", 10)
|
||||
acc3Cap.SetString("10000003", 10)
|
||||
voterCap.SetString("1000000000", 10)
|
||||
|
||||
caps = append(caps, voterCap, acc1Cap, acc2Cap, acc3Cap)
|
||||
candidates = append(candidates, voterAddr, acc1Addr, acc2Addr, acc3Addr)
|
||||
// create validator smart contract
|
||||
validatorSCAddr, _, _, err := contractValidator.DeployXDCValidator(
|
||||
transactOpts,
|
||||
contractBackendForSC,
|
||||
candidates,
|
||||
caps,
|
||||
voterAddr, // first owner, not used
|
||||
big.NewInt(50000),
|
||||
big.NewInt(1),
|
||||
big.NewInt(99),
|
||||
big.NewInt(100),
|
||||
big.NewInt(100),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("can't deploy root registry: %v", err)
|
||||
}
|
||||
|
||||
contractBackendForSC.Commit() // Write into database(state)
|
||||
|
||||
// Prepare Code and Storage
|
||||
d := time.Now().Add(1000 * time.Millisecond)
|
||||
ctx, cancel := context.WithDeadline(context.Background(), d)
|
||||
defer cancel()
|
||||
|
||||
code, _ := contractBackendForSC.CodeAt(ctx, validatorSCAddr, nil)
|
||||
storage := make(map[common.Hash]common.Hash)
|
||||
f := func(key, val common.Hash) bool {
|
||||
decode := []byte{}
|
||||
trim := bytes.TrimLeft(val.Bytes(), "\x00")
|
||||
err := rlp.DecodeBytes(trim, &decode)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed while decode byte")
|
||||
}
|
||||
storage[key] = common.BytesToHash(decode)
|
||||
log.Info("DecodeBytes", "value", val.String(), "decode", storage[key].String())
|
||||
return true
|
||||
}
|
||||
err = contractBackendForSC.ForEachStorageAt(ctx, validatorSCAddr, nil, f)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed while trying to read all keys from SC")
|
||||
}
|
||||
|
||||
// create test backend with smart contract in it
|
||||
contractBackend2 := backends.NewXDCSimulatedBackend(core.GenesisAlloc{
|
||||
acc1Addr: {Balance: new(big.Int).SetUint64(10000000000)},
|
||||
acc2Addr: {Balance: new(big.Int).SetUint64(10000000000)},
|
||||
acc3Addr: {Balance: new(big.Int).SetUint64(10000000000)},
|
||||
voterAddr: {Balance: new(big.Int).SetUint64(10000000000)},
|
||||
common.HexToAddress(common.MasternodeVotingSMC): {Balance: new(big.Int).SetUint64(1), Code: code, Storage: storage}, // Binding the MasternodeVotingSMC with newly created 'code' for SC execution
|
||||
}, 10000000, chainConfig)
|
||||
|
||||
return contractBackend2
|
||||
|
||||
}
|
||||
|
||||
func transferTx(t *testing.T, to common.Address, transferAmount int64) *types.Transaction {
|
||||
t.Logf("Transfering %v to address: %v", transferAmount, to.String())
|
||||
data := []byte{}
|
||||
gasPrice := big.NewInt(int64(0))
|
||||
gasLimit := uint64(21000)
|
||||
amount := big.NewInt(transferAmount)
|
||||
nonce := uint64(1)
|
||||
tx := types.NewTransaction(nonce, to, amount, gasLimit, gasPrice, data)
|
||||
signedTX, err := types.SignTx(tx, types.NewEIP155Signer(big.NewInt(chainID)), voterKey)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return signedTX
|
||||
}
|
||||
|
||||
func voteTX(gasLimit uint64, nonce uint64, addr string) (*types.Transaction, error) {
|
||||
vote := "6dd7d8ea" // VoteMethod = "0x6dd7d8ea"
|
||||
action := fmt.Sprintf("%s%s%s", vote, "000000000000000000000000", addr[3:])
|
||||
data := common.Hex2Bytes(action)
|
||||
gasPrice := big.NewInt(int64(0))
|
||||
amountInt := new(big.Int)
|
||||
amount, ok := amountInt.SetString("60000", 10)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("big int init failed")
|
||||
}
|
||||
to := common.HexToAddress(common.MasternodeVotingSMC)
|
||||
tx := types.NewTransaction(nonce, to, amount, gasLimit, gasPrice, data)
|
||||
|
||||
signedTX, err := types.SignTx(tx, types.NewEIP155Signer(big.NewInt(chainID)), voterKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return signedTX, nil
|
||||
}
|
||||
|
||||
func UpdateSigner(bc *BlockChain) error {
|
||||
err := bc.UpdateM1()
|
||||
return err
|
||||
}
|
||||
|
||||
func GetSnapshotSigner(bc *BlockChain, header *types.Header) (signersList, error) {
|
||||
engine := bc.Engine().(*XDPoS.XDPoS)
|
||||
snap, err := engine.GetSnapshot(bc, header)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
}
|
||||
ms := make(signersList)
|
||||
|
||||
for addr := range snap.Signers {
|
||||
ms[addr.Hex()] = true
|
||||
}
|
||||
return ms, nil
|
||||
|
||||
}
|
||||
|
||||
func GetCandidateFromCurrentSmartContract(backend bind.ContractBackend, t *testing.T) masterNodes {
|
||||
addr := common.HexToAddress(common.MasternodeVotingSMC)
|
||||
validator, err := contractValidator.NewXDCValidator(addr, backend)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
opts := new(bind.CallOpts)
|
||||
candidates, err := validator.GetCandidates(opts)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
ms := make(masterNodes)
|
||||
for _, candidate := range candidates {
|
||||
v, err := validator.GetCandidateCap(opts, candidate)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ms[candidate.String()] = *v
|
||||
}
|
||||
return ms
|
||||
}
|
||||
|
||||
// V1 consensus engine
|
||||
func PrepareXDCTestBlockChain(t *testing.T, numOfBlocks int, chainConfig *params.ChainConfig) (*BlockChain, *backends.SimulatedBackend, *types.Block, common.Address, func(account accounts.Account, hash []byte) ([]byte, error)) {
|
||||
// Preparation
|
||||
var err error
|
||||
// Authorise
|
||||
signer, signFn, err := backends.SimulateWalletAddressAndSignFn()
|
||||
|
||||
backend := getCommonBackend(t, chainConfig)
|
||||
blockchain := backend.GetBlockChain()
|
||||
blockchain.Client = backend
|
||||
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("Error while creating simulated wallet for generating singer address and signer fn: %v", err))
|
||||
}
|
||||
blockchain.Engine().(*XDPoS.XDPoS).Authorize(signer, signFn)
|
||||
|
||||
currentBlock := blockchain.Genesis()
|
||||
|
||||
go func() {
|
||||
for range core.CheckpointCh {
|
||||
checkpointChanMsg := <-core.CheckpointCh
|
||||
log.Info("[V1] Got a message from core CheckpointChan!", "msg", checkpointChanMsg)
|
||||
}
|
||||
}()
|
||||
|
||||
// Insert initial blocks
|
||||
for i := 1; i <= numOfBlocks; i++ {
|
||||
blockCoinBase := fmt.Sprintf("0x111000000000000000000000000000000%03d", i)
|
||||
merkleRoot := "35999dded35e8db12de7e6c1471eb9670c162eec616ecebbaf4fddd4676fb930"
|
||||
header := &types.Header{
|
||||
Root: common.HexToHash(merkleRoot),
|
||||
Number: big.NewInt(int64(i)),
|
||||
ParentHash: currentBlock.Hash(),
|
||||
Coinbase: common.HexToAddress(blockCoinBase),
|
||||
}
|
||||
block, err := createBlockFromHeader(blockchain, header, nil, signer, signFn, chainConfig)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = blockchain.InsertBlock(block)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
currentBlock = block
|
||||
}
|
||||
// Update Signer as there is no previous signer assigned
|
||||
err = UpdateSigner(blockchain)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
return blockchain, backend, currentBlock, signer, signFn
|
||||
}
|
||||
|
||||
func CreateBlock(blockchain *BlockChain, chainConfig *params.ChainConfig, startingBlock *types.Block, blockNumber int, roundNumber int64, blockCoinBase string, signer common.Address, signFn func(account accounts.Account, hash []byte) ([]byte, error), penalties []byte) *types.Block {
|
||||
currentBlock := startingBlock
|
||||
merkleRoot := "35999dded35e8db12de7e6c1471eb9670c162eec616ecebbaf4fddd4676fb930"
|
||||
|
||||
header := &types.Header{
|
||||
Root: common.HexToHash(merkleRoot),
|
||||
Number: big.NewInt(int64(blockNumber)),
|
||||
ParentHash: currentBlock.Hash(),
|
||||
Coinbase: common.HexToAddress(blockCoinBase),
|
||||
}
|
||||
|
||||
// Inject the hardcoded master node list for the last v1 epoch block and all v1 epoch switch blocks (excluding genesis)
|
||||
if big.NewInt(int64(blockNumber)).Cmp(chainConfig.XDPoS.V2.SwitchBlock) == 0 || blockNumber%int(chainConfig.XDPoS.Epoch) == 0 {
|
||||
// reset extra
|
||||
header.Extra = []byte{}
|
||||
if len(header.Extra) < utils.ExtraVanity {
|
||||
header.Extra = append(header.Extra, bytes.Repeat([]byte{0x00}, utils.ExtraVanity-len(header.Extra))...)
|
||||
}
|
||||
header.Extra = header.Extra[:utils.ExtraVanity]
|
||||
var masternodes []common.Address
|
||||
// Place the test's signer address to the last
|
||||
masternodes = append(masternodes, acc1Addr, acc2Addr, acc3Addr, voterAddr, signer)
|
||||
// masternodesFromV1LastEpoch = masternodes
|
||||
for _, masternode := range masternodes {
|
||||
header.Extra = append(header.Extra, masternode[:]...)
|
||||
}
|
||||
header.Extra = append(header.Extra, make([]byte, utils.ExtraSeal)...)
|
||||
|
||||
// Sign all the things for v1 block use v1 sigHash function
|
||||
sighash, err := signFn(accounts.Account{Address: signer}, blockchain.Engine().(*XDPoS.XDPoS).SigHash(header).Bytes())
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("Error when sign last v1 block hash during test block creation"))
|
||||
}
|
||||
copy(header.Extra[len(header.Extra)-utils.ExtraSeal:], sighash)
|
||||
}
|
||||
block, err := createBlockFromHeader(blockchain, header, nil, signer, signFn, chainConfig)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("Fail to create block in test helper, %v", err))
|
||||
}
|
||||
return block
|
||||
}
|
||||
|
||||
func createBlockFromHeader(bc *BlockChain, customHeader *types.Header, txs []*types.Transaction, signer common.Address, signFn func(account accounts.Account, hash []byte) ([]byte, error), config *params.ChainConfig) (*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
|
||||
customHeader.Extra, _ = hex.DecodeString(extraSubstring)
|
||||
}
|
||||
var difficulty *big.Int
|
||||
if customHeader.Difficulty == nil {
|
||||
difficulty = big.NewInt(1)
|
||||
} else {
|
||||
difficulty = customHeader.Difficulty
|
||||
}
|
||||
|
||||
// TODO: check if this is needed
|
||||
if len(txs) != 0 {
|
||||
customHeader.ReceiptHash = common.HexToHash("0x9319777b782ba2c83a33c995481ff894ac96d9a92a1963091346a3e1e386705c")
|
||||
} else {
|
||||
customHeader.ReceiptHash = common.HexToHash("0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")
|
||||
}
|
||||
|
||||
header := types.Header{
|
||||
ParentHash: customHeader.ParentHash,
|
||||
UncleHash: types.EmptyUncleHash,
|
||||
TxHash: types.EmptyRootHash,
|
||||
ReceiptHash: customHeader.ReceiptHash,
|
||||
Root: customHeader.Root,
|
||||
Coinbase: customHeader.Coinbase,
|
||||
Difficulty: difficulty,
|
||||
Number: customHeader.Number,
|
||||
GasLimit: 1200000000,
|
||||
Time: big.NewInt(time.Now().Unix()),
|
||||
Extra: customHeader.Extra,
|
||||
Validator: customHeader.Validator,
|
||||
Validators: customHeader.Validators,
|
||||
Penalties: customHeader.Penalties,
|
||||
}
|
||||
var block *types.Block
|
||||
if len(txs) == 0 {
|
||||
block = types.NewBlockWithHeader(&header)
|
||||
} else {
|
||||
// Prepare Receipt
|
||||
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)
|
||||
}
|
||||
gp := new(GasPool).AddGas(header.GasLimit)
|
||||
|
||||
var gasUsed = new(uint64)
|
||||
var receipts types.Receipts
|
||||
for i, tx := range txs {
|
||||
statedb.Prepare(tx.Hash(), header.Hash(), i)
|
||||
receipt, _, err, _ := ApplyTransaction(bc.Config(), nil, bc, &header.Coinbase, gp, statedb, nil, &header, tx, gasUsed, vm.Config{})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%v when applying transaction", err)
|
||||
}
|
||||
receipts = append(receipts, receipt)
|
||||
}
|
||||
|
||||
header.GasUsed = *gasUsed
|
||||
block = types.NewBlock(&header, txs, nil, receipts)
|
||||
}
|
||||
|
||||
return block, nil
|
||||
}
|
||||
|
||||
// /*
|
||||
// func proposeTX(t *testing.T) *types.Transaction {
|
||||
// data := common.Hex2Bytes("012679510000000000000000000000000d3ab14bbad3d99f4203bd7a11acb94882050e7e")
|
||||
// //data := []byte{}
|
||||
// fmt.Println("data", string(data[:]))
|
||||
// gasPrice := big.NewInt(int64(0))
|
||||
// gasLimit := uint64(22680)
|
||||
// amountInt := new(big.Int)
|
||||
// amount, ok := amountInt.SetString("11000000000000000000000000", 10)
|
||||
// if !ok {
|
||||
// t.Fatal("big int init failed")
|
||||
// }
|
||||
// nonce := uint64(0)
|
||||
// to := common.HexToAddress("xdc35658f7b2a9e7701e65e7a654659eb1c481d1dc5")
|
||||
// tx := types.NewTransaction(nonce, to, amount, gasLimit, gasPrice, data)
|
||||
// signedTX, err := types.SignTx(tx, types.NewEIP155Signer(big.NewInt(chainID)), acc4Key)
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
// return signedTX
|
||||
// }
|
||||
// */
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
package tests
|
||||
package engine_v2_tests
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
|
@ -15,7 +14,7 @@ import (
|
|||
)
|
||||
|
||||
func TestAdaptorShouldGetAuthorForDifferentConsensusVersion(t *testing.T) {
|
||||
blockchain, backend, currentBlock, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 900, params.TestXDPoSMockChainConfig, 0)
|
||||
blockchain, _, currentBlock, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, 900, params.TestXDPoSMockChainConfig, 0)
|
||||
adaptor := blockchain.Engine().(*XDPoS.XDPoS)
|
||||
|
||||
addressFromAdaptor, errorAdaptor := adaptor.Author(currentBlock.Header())
|
||||
|
|
@ -32,23 +31,22 @@ 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 901
|
||||
|
||||
blockCoinBase := fmt.Sprintf("0x111000000000000000000000000000000%03d", 901)
|
||||
merkleRoot := "35999dded35e8db12de7e6c1471eb9670c162eec616ecebbaf4fddd4676fb930"
|
||||
header := &types.Header{
|
||||
Root: common.HexToHash(merkleRoot),
|
||||
Number: big.NewInt(int64(901)),
|
||||
ParentHash: currentBlock.Hash(),
|
||||
Coinbase: common.HexToAddress(blockCoinBase),
|
||||
Coinbase: signer,
|
||||
}
|
||||
err := generateSignature(backend, adaptor, header)
|
||||
|
||||
header.Extra = generateV2Extra(1, currentBlock, signer, signFn)
|
||||
|
||||
block901, err := createBlockFromHeader(blockchain, header, nil, signer, signFn, blockchain.Config())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
block901, err := createBlockFromHeader(blockchain, header, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
blockchain.InsertBlock(block901)
|
||||
err = blockchain.InsertBlock(block901)
|
||||
assert.Nil(t, err)
|
||||
|
||||
addressFromAdaptor, errorAdaptor = adaptor.Author(block901.Header())
|
||||
if errorAdaptor != nil {
|
||||
|
|
@ -181,14 +179,16 @@ func TestAdaptorGetMasternodesV2(t *testing.T) {
|
|||
currentBlock = CreateBlock(blockchain, params.TestXDPoSMockChainConfig, currentBlock, blockNum, 1, blockCoinBase, signer, signFn, nil)
|
||||
|
||||
// block 901 is the first v2 block, and is treated as epoch switch block
|
||||
blockchain.InsertBlock(currentBlock)
|
||||
err := blockchain.InsertBlock(currentBlock)
|
||||
assert.Nil(t, err)
|
||||
masternodes1 := adaptor.GetMasternodes(blockchain, currentBlock.Header())
|
||||
assert.Equal(t, 4, len(masternodes1))
|
||||
assert.Equal(t, 5, len(masternodes1))
|
||||
masternodes1ByNumber := adaptor.GetMasternodesByNumber(blockchain, currentBlock.NumberU64())
|
||||
assert.True(t, reflect.DeepEqual(masternodes1, masternodes1ByNumber), "at block number", blockNum)
|
||||
for blockNum = 902; blockNum < 915; blockNum++ {
|
||||
currentBlock = CreateBlock(blockchain, params.TestXDPoSMockChainConfig, currentBlock, blockNum, int64(blockNum-900), blockCoinBase, signer, signFn, nil)
|
||||
blockchain.InsertBlock(currentBlock)
|
||||
err = blockchain.InsertBlock(currentBlock)
|
||||
assert.Nil(t, err)
|
||||
masternodes2 := adaptor.GetMasternodes(blockchain, currentBlock.Header())
|
||||
assert.True(t, reflect.DeepEqual(masternodes1, masternodes2), "at block number", blockNum)
|
||||
masternodes2ByNumber := adaptor.GetMasternodesByNumber(blockchain, currentBlock.NumberU64())
|
||||
|
|
@ -210,7 +210,8 @@ func TestGetCurrentEpochSwitchBlock(t *testing.T) {
|
|||
blockNum := 901
|
||||
blockCoinBase := "0x111000000000000000000000000000000123"
|
||||
currentBlock = CreateBlock(blockchain, params.TestXDPoSMockChainConfig, currentBlock, blockNum, 1, blockCoinBase, signer, signFn, nil)
|
||||
blockchain.InsertBlock(currentBlock)
|
||||
err = blockchain.InsertBlock(currentBlock)
|
||||
assert.Nil(t, err)
|
||||
currentCheckpointNumber, epochNum, err = adaptor.GetCurrentEpochSwitchBlock(blockchain, currentBlock.Number())
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, uint64(901), currentCheckpointNumber)
|
||||
|
|
@ -219,7 +220,8 @@ func TestGetCurrentEpochSwitchBlock(t *testing.T) {
|
|||
for blockNum = 902; blockNum < 915; blockNum++ {
|
||||
currentBlock = CreateBlock(blockchain, params.TestXDPoSMockChainConfig, currentBlock, blockNum, int64(blockNum-900), blockCoinBase, signer, signFn, nil)
|
||||
|
||||
blockchain.InsertBlock(currentBlock)
|
||||
err = blockchain.InsertBlock(currentBlock)
|
||||
assert.Nil(t, err)
|
||||
currentCheckpointNumber, epochNum, err := adaptor.GetCurrentEpochSwitchBlock(blockchain, currentBlock.Number())
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, uint64(901), currentCheckpointNumber)
|
||||
|
|
@ -243,13 +245,14 @@ func TestGetParentBlock(t *testing.T) {
|
|||
blockNum := 901
|
||||
blockCoinBase := "0x111000000000000000000000000000000123"
|
||||
block901 := CreateBlock(blockchain, params.TestXDPoSMockChainConfig, block900, blockNum, 1, blockCoinBase, signer, signFn, nil)
|
||||
blockchain.InsertBlock(block901)
|
||||
err = blockchain.InsertBlock(block901)
|
||||
assert.Nil(t, err)
|
||||
|
||||
// let's inject another one, but the highestedQC has not been updated, so it shall still point to 900
|
||||
blockNum = 902
|
||||
block902 := CreateBlock(blockchain, params.TestXDPoSMockChainConfig, block901, blockNum, 1, blockCoinBase, signer, signFn, nil)
|
||||
blockchain.InsertBlock(block902)
|
||||
|
||||
err = blockchain.InsertBlock(block902)
|
||||
assert.Nil(t, err)
|
||||
block = adaptor.FindParentBlockToAssign(blockchain, block902)
|
||||
|
||||
assert.Equal(t, block900.Hash(), block.Hash())
|
||||
|
|
@ -1,77 +1,15 @@
|
|||
package tests
|
||||
package engine_v2_tests
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"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 TestIsAuthorisedMNForConsensusV1(t *testing.T) {
|
||||
/*
|
||||
V1 consensus engine
|
||||
*/
|
||||
blockchain, _, parentBlock, _ := PrepareXDCTestBlockChain(t, GAP-2, params.TestXDPoSMockChainConfig)
|
||||
// Insert first Block 449
|
||||
t.Logf("Inserting block with propose at 449...")
|
||||
blockCoinbaseA := "0xaaa0000000000000000000000000000000000449"
|
||||
tx, err := voteTX(37117, 0, acc1Addr.String())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
//Get from block validator error message
|
||||
merkleRoot := "46234e9cd7e85a267f7f0435b15256a794a2f6d65cc98cdbd21dcd10a01d9772"
|
||||
header := &types.Header{
|
||||
Root: common.HexToHash(merkleRoot),
|
||||
Number: big.NewInt(int64(449)),
|
||||
ParentHash: parentBlock.Hash(),
|
||||
Coinbase: common.HexToAddress(blockCoinbaseA),
|
||||
}
|
||||
block449, err := createBlockFromHeader(blockchain, header, []*types.Transaction{tx})
|
||||
blockchain.InsertBlock(block449)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
parentBlock = block449
|
||||
|
||||
// At block 449, we should not update signerList. we need to update it till block 450 gap block.
|
||||
// Acc3 is the default account that is on the signerList
|
||||
|
||||
engine := blockchain.Engine().(*XDPoS.XDPoS)
|
||||
isAuthorisedMN := engine.IsAuthorisedAddress(blockchain, block449.Header(), acc3Addr)
|
||||
assert.True(t, isAuthorisedMN)
|
||||
|
||||
isAuthorisedMN = engine.IsAuthorisedAddress(blockchain, block449.Header(), acc1Addr)
|
||||
assert.False(t, isAuthorisedMN)
|
||||
|
||||
// Now, let's mine another block to trigger the GAP block signerList update
|
||||
block450CoinbaseAddress := "0xaaa0000000000000000000000000000000000450"
|
||||
merkleRoot = "46234e9cd7e85a267f7f0435b15256a794a2f6d65cc98cdbd21dcd10a01d9772"
|
||||
header = &types.Header{
|
||||
Root: common.HexToHash(merkleRoot),
|
||||
Number: big.NewInt(int64(450)),
|
||||
ParentHash: parentBlock.Hash(),
|
||||
Coinbase: common.HexToAddress(block450CoinbaseAddress),
|
||||
}
|
||||
block450, err := createBlockFromHeader(blockchain, header, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
blockchain.InsertBlock(block450)
|
||||
|
||||
isAuthorisedMN = engine.IsAuthorisedAddress(blockchain, block450.Header(), acc3Addr)
|
||||
assert.False(t, isAuthorisedMN)
|
||||
|
||||
isAuthorisedMN = engine.IsAuthorisedAddress(blockchain, block450.Header(), acc1Addr)
|
||||
assert.True(t, isAuthorisedMN)
|
||||
}
|
||||
|
||||
func TestIsAuthorisedMNForConsensusV2(t *testing.T) {
|
||||
// we skip test for v1 since it's hard to make a real genesis block
|
||||
blockchain, _, currentBlock, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, 900, params.TestXDPoSMockChainConfig, 0)
|
||||
|
|
@ -79,8 +17,8 @@ func TestIsAuthorisedMNForConsensusV2(t *testing.T) {
|
|||
blockNum := 901
|
||||
blockCoinBase := "0x111000000000000000000000000000000123"
|
||||
currentBlock = CreateBlock(blockchain, params.TestXDPoSMockChainConfig, currentBlock, blockNum, 1, blockCoinBase, signer, signFn, nil)
|
||||
blockchain.InsertBlock(currentBlock)
|
||||
|
||||
err := blockchain.InsertBlock(currentBlock)
|
||||
assert.Nil(t, err)
|
||||
// As long as the address is in the master node list, they are all valid
|
||||
isAuthorisedMN := adaptor.IsAuthorisedAddress(blockchain, currentBlock.Header(), common.HexToAddress("xdc0D3ab14BBaD3D99F4203bd7a11aCB94882050E7e"))
|
||||
assert.True(t, isAuthorisedMN)
|
||||
|
|
@ -100,8 +38,8 @@ func TestIsYourTurnConsensusV2(t *testing.T) {
|
|||
blockNum := 901
|
||||
blockCoinBase := "0x111000000000000000000000000000000123"
|
||||
currentBlock = CreateBlock(blockchain, params.TestXDPoSMockChainConfig, currentBlock, blockNum, 1, blockCoinBase, signer, signFn, nil)
|
||||
blockchain.InsertBlock(currentBlock)
|
||||
|
||||
err := blockchain.InsertBlock(currentBlock)
|
||||
assert.Nil(t, err)
|
||||
// Less then Mine Period
|
||||
isYourTurn, err := adaptor.YourTurn(blockchain, currentBlock.Header(), common.HexToAddress("xdc0D3ab14BBaD3D99F4203bd7a11aCB94882050E7e"))
|
||||
assert.Nil(t, err)
|
||||
|
|
@ -124,7 +62,8 @@ func TestIsYourTurnConsensusV2(t *testing.T) {
|
|||
// We continue to grow the chain which will increase the round number
|
||||
blockNum = 902
|
||||
currentBlock = CreateBlock(blockchain, params.TestXDPoSMockChainConfig, currentBlock, blockNum, int64(blockNum-900), blockCoinBase, signer, signFn, nil)
|
||||
blockchain.InsertBlock(currentBlock)
|
||||
err = blockchain.InsertBlock(currentBlock)
|
||||
assert.Nil(t, err)
|
||||
time.Sleep(time.Duration(minePeriod) * time.Second)
|
||||
|
||||
adaptor.EngineV2.SetNewRoundFaker(blockchain, 2, false)
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package tests
|
||||
package engine_v2_tests
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
|
@ -10,7 +10,6 @@ import (
|
|||
"math/big"
|
||||
"math/rand"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
|
|
@ -50,22 +49,6 @@ var (
|
|||
chainID = int64(1337)
|
||||
)
|
||||
|
||||
func debugMessage(backend *backends.SimulatedBackend, signers signersList, t *testing.T) {
|
||||
ms := GetCandidateFromCurrentSmartContract(backend, t)
|
||||
fmt.Println("=== current smart contract")
|
||||
for nodeAddr, cap := range ms {
|
||||
if !strings.Contains(nodeAddr, "000000000000000000000000000000000000") { //remove defaults
|
||||
fmt.Println(nodeAddr, cap)
|
||||
}
|
||||
}
|
||||
fmt.Println("=== this block signer list")
|
||||
for signer := range signers {
|
||||
if !strings.Contains(signer, "000000000000000000000000000000000000") { //remove defaults
|
||||
fmt.Println(signer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func SignHashByPK(pk *ecdsa.PrivateKey, itemToSign []byte) []byte {
|
||||
signer, signFn, err := getSignerAndSignFn(pk)
|
||||
if err != nil {
|
||||
|
|
@ -110,6 +93,27 @@ func getSignerAndSignFn(pk *ecdsa.PrivateKey) (common.Address, func(account acco
|
|||
return a1.Address, ks.SignHash, nil
|
||||
}
|
||||
|
||||
func voteTX(gasLimit uint64, nonce uint64, addr string) (*types.Transaction, error) {
|
||||
vote := "6dd7d8ea" // VoteMethod = "0x6dd7d8ea"
|
||||
action := fmt.Sprintf("%s%s%s", vote, "000000000000000000000000", addr[3:])
|
||||
data := common.Hex2Bytes(action)
|
||||
gasPrice := big.NewInt(int64(0))
|
||||
amountInt := new(big.Int)
|
||||
amount, ok := amountInt.SetString("60000", 10)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("big int init failed")
|
||||
}
|
||||
to := common.HexToAddress(common.MasternodeVotingSMC)
|
||||
tx := types.NewTransaction(nonce, to, amount, gasLimit, gasPrice, data)
|
||||
|
||||
signedTX, err := types.SignTx(tx, types.NewEIP155Signer(big.NewInt(chainID)), voterKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return signedTX, nil
|
||||
}
|
||||
|
||||
func getCommonBackend(t *testing.T, chainConfig *params.ChainConfig) *backends.SimulatedBackend {
|
||||
|
||||
// initial helper backend
|
||||
|
|
@ -191,50 +195,13 @@ func getCommonBackend(t *testing.T, chainConfig *params.ChainConfig) *backends.S
|
|||
}, 10000000, chainConfig)
|
||||
|
||||
return contractBackend2
|
||||
|
||||
}
|
||||
|
||||
func transferTx(t *testing.T, to common.Address, transferAmount int64) *types.Transaction {
|
||||
t.Logf("Transfering %v to address: %v", transferAmount, to.String())
|
||||
data := []byte{}
|
||||
gasPrice := big.NewInt(int64(0))
|
||||
gasLimit := uint64(21000)
|
||||
amount := big.NewInt(transferAmount)
|
||||
nonce := uint64(1)
|
||||
tx := types.NewTransaction(nonce, to, amount, gasLimit, gasPrice, data)
|
||||
signedTX, err := types.SignTx(tx, types.NewEIP155Signer(big.NewInt(chainID)), voterKey)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return signedTX
|
||||
}
|
||||
|
||||
func voteTX(gasLimit uint64, nonce uint64, addr string) (*types.Transaction, error) {
|
||||
vote := "6dd7d8ea" // VoteMethod = "0x6dd7d8ea"
|
||||
action := fmt.Sprintf("%s%s%s", vote, "000000000000000000000000", addr[3:])
|
||||
data := common.Hex2Bytes(action)
|
||||
gasPrice := big.NewInt(int64(0))
|
||||
amountInt := new(big.Int)
|
||||
amount, ok := amountInt.SetString("60000", 10)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("big int init failed")
|
||||
}
|
||||
to := common.HexToAddress(common.MasternodeVotingSMC)
|
||||
tx := types.NewTransaction(nonce, to, amount, gasLimit, gasPrice, data)
|
||||
|
||||
signedTX, err := types.SignTx(tx, types.NewEIP155Signer(big.NewInt(chainID)), voterKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return signedTX, nil
|
||||
}
|
||||
|
||||
func signingTxWithSignerFn(header *types.Header, nonce uint64, signer common.Address, signFn func(account accounts.Account, hash []byte) ([]byte, error)) (*types.Transaction, error) {
|
||||
func signingTxWithKey(header *types.Header, nonce uint64, privateKey *ecdsa.PrivateKey) (*types.Transaction, error) {
|
||||
tx := contracts.CreateTxSign(header.Number, header.Hash(), nonce, common.HexToAddress(common.BlockSigners))
|
||||
s := types.NewEIP155Signer(big.NewInt(chainID))
|
||||
h := s.Hash(tx)
|
||||
sig, err := signFn(accounts.Account{Address: signer}, h[:])
|
||||
sig, err := crypto.Sign(h[:], privateKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -245,11 +212,11 @@ func signingTxWithSignerFn(header *types.Header, nonce uint64, signer common.Add
|
|||
return signedTx, nil
|
||||
}
|
||||
|
||||
func signingTxWithKey(header *types.Header, nonce uint64, privateKey *ecdsa.PrivateKey) (*types.Transaction, error) {
|
||||
func signingTxWithSignerFn(header *types.Header, nonce uint64, signer common.Address, signFn func(account accounts.Account, hash []byte) ([]byte, error)) (*types.Transaction, error) {
|
||||
tx := contracts.CreateTxSign(header.Number, header.Hash(), nonce, common.HexToAddress(common.BlockSigners))
|
||||
s := types.NewEIP155Signer(big.NewInt(chainID))
|
||||
h := s.Hash(tx)
|
||||
sig, err := crypto.Sign(h[:], privateKey)
|
||||
sig, err := signFn(accounts.Account{Address: signer}, h[:])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -305,57 +272,6 @@ func GetCandidateFromCurrentSmartContract(backend bind.ContractBackend, t *testi
|
|||
return ms
|
||||
}
|
||||
|
||||
// V1 consensus engine
|
||||
func PrepareXDCTestBlockChain(t *testing.T, numOfBlocks int, chainConfig *params.ChainConfig) (*BlockChain, *backends.SimulatedBackend, *types.Block, common.Address) {
|
||||
// Preparation
|
||||
var err error
|
||||
// Authorise
|
||||
signer, signFn, err := backends.SimulateWalletAddressAndSignFn()
|
||||
|
||||
backend := getCommonBackend(t, chainConfig)
|
||||
blockchain := backend.GetBlockChain()
|
||||
blockchain.Client = backend
|
||||
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("Error while creating simulated wallet for generating singer address and signer fn: %v", err))
|
||||
}
|
||||
blockchain.Engine().(*XDPoS.XDPoS).Authorize(signer, signFn)
|
||||
|
||||
currentBlock := blockchain.Genesis()
|
||||
|
||||
go func() {
|
||||
for range core.CheckpointCh {
|
||||
checkpointChanMsg := <-core.CheckpointCh
|
||||
log.Info("[V1] Got a message from core CheckpointChan!", "msg", checkpointChanMsg)
|
||||
}
|
||||
}()
|
||||
|
||||
// Insert initial blocks
|
||||
for i := 1; i <= numOfBlocks; i++ {
|
||||
blockCoinBase := fmt.Sprintf("0x111000000000000000000000000000000%03d", i)
|
||||
merkleRoot := "35999dded35e8db12de7e6c1471eb9670c162eec616ecebbaf4fddd4676fb930"
|
||||
header := &types.Header{
|
||||
Root: common.HexToHash(merkleRoot),
|
||||
Number: big.NewInt(int64(i)),
|
||||
ParentHash: currentBlock.Hash(),
|
||||
Coinbase: common.HexToAddress(blockCoinBase),
|
||||
}
|
||||
block, err := createBlockFromHeader(blockchain, header, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
blockchain.InsertBlock(block)
|
||||
currentBlock = block
|
||||
}
|
||||
// Update Signer as there is no previous signer assigned
|
||||
err = UpdateSigner(blockchain)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
return blockchain, backend, currentBlock, signer
|
||||
}
|
||||
|
||||
// V2 concensus engine
|
||||
func PrepareXDCTestBlockChainForV2Engine(t *testing.T, numOfBlocks int, chainConfig *params.ChainConfig, numOfForkedBlocks int) (*BlockChain, *backends.SimulatedBackend, *types.Block, common.Address, func(account accounts.Account, hash []byte) ([]byte, error), *types.Block) {
|
||||
// Preparation
|
||||
|
|
@ -409,7 +325,10 @@ func PrepareXDCTestBlockChainForV2Engine(t *testing.T, numOfBlocks int, chainCon
|
|||
|
||||
forkedBlock := CreateBlock(blockchain, chainConfig, currentForkBlock, i, forkedBlockRoundNumber, forkedBlockCoinBase, signer, signFn, nil)
|
||||
|
||||
blockchain.InsertBlock(forkedBlock)
|
||||
err = blockchain.InsertBlock(forkedBlock)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
currentForkBlock = forkedBlock
|
||||
}
|
||||
|
||||
|
|
@ -493,51 +412,14 @@ func CreateBlock(blockchain *BlockChain, chainConfig *params.ChainConfig, starti
|
|||
var header *types.Header
|
||||
|
||||
if big.NewInt(int64(blockNumber)).Cmp(chainConfig.XDPoS.V2.SwitchBlock) == 1 { // Build engine v2 compatible extra data field
|
||||
var extraField utils.ExtraFields_v2
|
||||
var round utils.Round
|
||||
err := utils.DecodeBytesExtraFields(currentBlock.Extra(), &extraField)
|
||||
if err != nil {
|
||||
round = utils.Round(0)
|
||||
} else {
|
||||
round = extraField.Round
|
||||
}
|
||||
extraInBytes := generateV2Extra(roundNumber, currentBlock, signer, signFn)
|
||||
|
||||
proposedBlockInfo := &utils.BlockInfo{
|
||||
Hash: currentBlock.Hash(),
|
||||
Round: round,
|
||||
Number: currentBlock.Number(),
|
||||
}
|
||||
// Genrate QC
|
||||
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))
|
||||
}
|
||||
// Sign from acc 1, 2, 3
|
||||
acc1SignedHash := SignHashByPK(acc1Key, utils.VoteSigHash(proposedBlockInfo).Bytes())
|
||||
acc2SignedHash := SignHashByPK(acc2Key, utils.VoteSigHash(proposedBlockInfo).Bytes())
|
||||
acc3SignedHash := SignHashByPK(acc3Key, utils.VoteSigHash(proposedBlockInfo).Bytes())
|
||||
var signatures []utils.Signature
|
||||
signatures = append(signatures, signedHash, acc1SignedHash, acc2SignedHash, acc3SignedHash)
|
||||
quorumCert := &utils.QuorumCert{
|
||||
ProposedBlockInfo: proposedBlockInfo,
|
||||
Signatures: signatures,
|
||||
}
|
||||
|
||||
extra := utils.ExtraFields_v2{
|
||||
Round: utils.Round(roundNumber),
|
||||
QuorumCert: quorumCert,
|
||||
}
|
||||
extraInBytes, err := extra.EncodeToBytes()
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("Error encode extra into bytes: %v", err))
|
||||
}
|
||||
header = &types.Header{
|
||||
Root: common.HexToHash(merkleRoot),
|
||||
Number: big.NewInt(int64(blockNumber)),
|
||||
ParentHash: currentBlock.Hash(),
|
||||
Coinbase: common.HexToAddress(blockCoinBase),
|
||||
Extra: extraInBytes,
|
||||
Validator: signedHash,
|
||||
}
|
||||
if int64(blockNumber) == (chainConfig.XDPoS.V2.SwitchBlock.Int64() + 1) { // This is the first v2 block, we need to copy the last v1 epoch master node list and inject into v2 validators
|
||||
// Get last master node list from last v1 block
|
||||
|
|
@ -576,7 +458,8 @@ func CreateBlock(blockchain *BlockChain, chainConfig *params.ChainConfig, starti
|
|||
}
|
||||
header.Extra = header.Extra[:utils.ExtraVanity]
|
||||
var masternodes []common.Address
|
||||
masternodes = append(masternodes, acc1Addr, acc2Addr, acc3Addr, signer)
|
||||
// Place the test's signer address to the last
|
||||
masternodes = append(masternodes, acc1Addr, acc2Addr, acc3Addr, voterAddr, signer)
|
||||
// masternodesFromV1LastEpoch = masternodes
|
||||
for _, masternode := range masternodes {
|
||||
header.Extra = append(header.Extra, masternode[:]...)
|
||||
|
|
@ -591,28 +474,14 @@ func CreateBlock(blockchain *BlockChain, chainConfig *params.ChainConfig, starti
|
|||
copy(header.Extra[len(header.Extra)-utils.ExtraSeal:], sighash)
|
||||
}
|
||||
}
|
||||
block, err := createBlockFromHeader(blockchain, header, nil)
|
||||
block, err := createBlockFromHeader(blockchain, header, nil, signer, signFn, chainConfig)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("Fail to create block in test helper, %v", err))
|
||||
}
|
||||
return block
|
||||
}
|
||||
|
||||
func generateSignature(backend *backends.SimulatedBackend, adaptor *XDPoS.XDPoS, 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}, adaptor.SigHash(header).Bytes())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
header.Validator = signature
|
||||
return nil
|
||||
}
|
||||
|
||||
func createBlockFromHeader(bc *BlockChain, customHeader *types.Header, txs []*types.Transaction) (*types.Block, error) {
|
||||
func createBlockFromHeader(bc *BlockChain, customHeader *types.Header, txs []*types.Transaction, signer common.Address, signFn func(account accounts.Account, hash []byte) ([]byte, error), config *params.ChainConfig) (*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
|
||||
customHeader.Extra, _ = hex.DecodeString(extraSubstring)
|
||||
|
|
@ -624,7 +493,6 @@ func createBlockFromHeader(bc *BlockChain, customHeader *types.Header, txs []*ty
|
|||
difficulty = customHeader.Difficulty
|
||||
}
|
||||
|
||||
// TODO: check if this is needed
|
||||
if len(txs) != 0 {
|
||||
customHeader.ReceiptHash = common.HexToHash("0x9319777b782ba2c83a33c995481ff894ac96d9a92a1963091346a3e1e386705c")
|
||||
} else {
|
||||
|
|
@ -649,6 +517,11 @@ func createBlockFromHeader(bc *BlockChain, customHeader *types.Header, txs []*ty
|
|||
}
|
||||
var block *types.Block
|
||||
if len(txs) == 0 {
|
||||
// Sign all the things and seal it
|
||||
signerAddress, signerFunction := findSignerAndSignFn(bc, &header, signer, signFn, config)
|
||||
header.Coinbase = signerAddress
|
||||
sealHeader(bc, &header, signerAddress, signerFunction)
|
||||
|
||||
block = types.NewBlockWithHeader(&header)
|
||||
} else {
|
||||
// Prepare Receipt
|
||||
|
|
@ -671,35 +544,17 @@ func createBlockFromHeader(bc *BlockChain, customHeader *types.Header, txs []*ty
|
|||
|
||||
header.GasUsed = *gasUsed
|
||||
|
||||
// Sign all the things and seal it
|
||||
signerAddress, signerFunction := findSignerAndSignFn(bc, &header, signer, signFn, config)
|
||||
header.Coinbase = signerAddress
|
||||
sealHeader(bc, &header, signerAddress, signerFunction)
|
||||
|
||||
block = types.NewBlock(&header, txs, nil, receipts)
|
||||
}
|
||||
|
||||
return block, nil
|
||||
}
|
||||
|
||||
// /*
|
||||
// func proposeTX(t *testing.T) *types.Transaction {
|
||||
// data := common.Hex2Bytes("012679510000000000000000000000000d3ab14bbad3d99f4203bd7a11acb94882050e7e")
|
||||
// //data := []byte{}
|
||||
// fmt.Println("data", string(data[:]))
|
||||
// gasPrice := big.NewInt(int64(0))
|
||||
// gasLimit := uint64(22680)
|
||||
// amountInt := new(big.Int)
|
||||
// amount, ok := amountInt.SetString("11000000000000000000000000", 10)
|
||||
// if !ok {
|
||||
// t.Fatal("big int init failed")
|
||||
// }
|
||||
// nonce := uint64(0)
|
||||
// to := common.HexToAddress("xdc35658f7b2a9e7701e65e7a654659eb1c481d1dc5")
|
||||
// tx := types.NewTransaction(nonce, to, amount, gasLimit, gasPrice, data)
|
||||
// signedTX, err := types.SignTx(tx, types.NewEIP155Signer(big.NewInt(chainID)), acc4Key)
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
// return signedTX
|
||||
// }
|
||||
// */
|
||||
|
||||
// Get masternodes address from checkpoint Header. Only used for v1 last block
|
||||
func decodeMasternodesFromHeaderExtra(checkpointHeader *types.Header) []common.Address {
|
||||
masternodes := make([]common.Address, (len(checkpointHeader.Extra)-utils.ExtraVanity-utils.ExtraSeal)/common.AddressLength)
|
||||
|
|
@ -708,3 +563,99 @@ func decodeMasternodesFromHeaderExtra(checkpointHeader *types.Header) []common.A
|
|||
}
|
||||
return masternodes
|
||||
}
|
||||
|
||||
func findSignerAndSignFn(bc *BlockChain, header *types.Header, signer common.Address, signFn func(account accounts.Account, hash []byte) ([]byte, error), config *params.ChainConfig) (common.Address, func(account accounts.Account, hash []byte) ([]byte, error)) {
|
||||
addressToSign := signer
|
||||
addressedSignFn := signFn
|
||||
|
||||
// If v2 block, we need to use extra data's round to find who is creating the block in order to verify the validator
|
||||
if header.Number.Cmp(config.XDPoS.V2.SwitchBlock) > 0 {
|
||||
var decodedExtraField utils.ExtraFields_v2
|
||||
err := utils.DecodeBytesExtraFields(header.Extra, &decodedExtraField)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("fail to seal header for v2 block"))
|
||||
}
|
||||
round := decodedExtraField.Round
|
||||
masterNodes := getMasternodesList(signer)
|
||||
|
||||
index := uint64(round) % config.XDPoS.Epoch % uint64(len(masterNodes))
|
||||
// index 0 to 2 are acc1Addr, acc2Addr, acc3Addr
|
||||
addressToSign = masterNodes[index]
|
||||
if index == 0 {
|
||||
_, signFn, err = getSignerAndSignFn(acc1Key)
|
||||
} else if index == 1 {
|
||||
_, signFn, err = getSignerAndSignFn(acc2Key)
|
||||
} else if index == 2 {
|
||||
_, signFn, err = getSignerAndSignFn(acc3Key)
|
||||
} else if index == 3 {
|
||||
// Skip signing anything for voterAddress to simulate penalty
|
||||
return signer, signFn
|
||||
}
|
||||
addressedSignFn = signFn
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("Error trying to use one of the pre-defined private key to sign"))
|
||||
}
|
||||
}
|
||||
|
||||
return addressToSign, addressedSignFn
|
||||
}
|
||||
|
||||
func sealHeader(bc *BlockChain, header *types.Header, signer common.Address, signFn func(account accounts.Account, hash []byte) ([]byte, error)) {
|
||||
// Sign all the things and seal it
|
||||
signedBlockHeader := bc.Engine().(*XDPoS.XDPoS).SigHash(header)
|
||||
|
||||
signature, err := signFn(accounts.Account{Address: signer}, signedBlockHeader.Bytes())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
header.Validator = signature
|
||||
}
|
||||
|
||||
func getMasternodesList(signer common.Address) []common.Address {
|
||||
var masternodes []common.Address
|
||||
// Place the test's signer address to the last
|
||||
masternodes = append(masternodes, acc1Addr, acc2Addr, acc3Addr, voterAddr, signer)
|
||||
return masternodes
|
||||
}
|
||||
|
||||
func generateV2Extra(roundNumber int64, currentBlock *types.Block, signer common.Address, signFn func(account accounts.Account, hash []byte) ([]byte, error)) []byte {
|
||||
var extraField utils.ExtraFields_v2
|
||||
var round utils.Round
|
||||
err := utils.DecodeBytesExtraFields(currentBlock.Extra(), &extraField)
|
||||
if err != nil {
|
||||
round = utils.Round(0)
|
||||
} else {
|
||||
round = extraField.Round
|
||||
}
|
||||
|
||||
proposedBlockInfo := &utils.BlockInfo{
|
||||
Hash: currentBlock.Hash(),
|
||||
Round: round,
|
||||
Number: currentBlock.Number(),
|
||||
}
|
||||
|
||||
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))
|
||||
}
|
||||
// Sign from acc 1, 2, 3
|
||||
acc1SignedHash := SignHashByPK(acc1Key, utils.VoteSigHash(proposedBlockInfo).Bytes())
|
||||
acc2SignedHash := SignHashByPK(acc2Key, utils.VoteSigHash(proposedBlockInfo).Bytes())
|
||||
acc3SignedHash := SignHashByPK(acc3Key, utils.VoteSigHash(proposedBlockInfo).Bytes())
|
||||
var signatures []utils.Signature
|
||||
signatures = append(signatures, acc1SignedHash, acc2SignedHash, acc3SignedHash, signedHash)
|
||||
quorumCert := &utils.QuorumCert{
|
||||
ProposedBlockInfo: proposedBlockInfo,
|
||||
Signatures: signatures,
|
||||
}
|
||||
|
||||
extra := utils.ExtraFields_v2{
|
||||
Round: utils.Round(roundNumber),
|
||||
QuorumCert: quorumCert,
|
||||
}
|
||||
extraInBytes, err := extra.EncodeToBytes()
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("Error encode extra into bytes: %v", err))
|
||||
}
|
||||
return extraInBytes
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package tests
|
||||
package engine_v2_tests
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
|
|
@ -82,18 +82,19 @@ func TestInitialOtherV2Block(t *testing.T) {
|
|||
assert.Nil(t, err)
|
||||
|
||||
header := &types.Header{
|
||||
Root: common.HexToHash("ea465415b60d88429f181fec9fae67c0f19cbf5a4fa10971d96d4faa57d96ffa"),
|
||||
Root: common.HexToHash("35999dded35e8db12de7e6c1471eb9670c162eec616ecebbaf4fddd4676fb930"),
|
||||
Number: big.NewInt(int64(911)),
|
||||
ParentHash: currentBlock.Hash(),
|
||||
Coinbase: common.HexToAddress("0x111000000000000000000000000000000123"),
|
||||
}
|
||||
header.Extra = extraBytes
|
||||
|
||||
block, err := createBlockFromHeader(blockchain, header, nil)
|
||||
block, err := createBlockFromHeader(blockchain, header, nil, signer, signFn, blockchain.Config())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
blockchain.InsertBlock(block)
|
||||
err = blockchain.InsertBlock(block)
|
||||
assert.Nil(t, err)
|
||||
// Initialise
|
||||
err = adaptor.EngineV2.Initial(blockchain, block.Header())
|
||||
assert.Nil(t, err)
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package tests
|
||||
package engine_v2_tests
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
|
@ -7,6 +7,7 @@ import (
|
|||
"time"
|
||||
|
||||
"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/core/types"
|
||||
|
|
@ -16,7 +17,7 @@ import (
|
|||
|
||||
func TestYourTurnInitialV2(t *testing.T) {
|
||||
config := params.TestXDPoSMockChainConfig
|
||||
blockchain, _, parentBlock, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, int(config.XDPoS.Epoch)-1, config, 0)
|
||||
blockchain, _, parentBlock, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, int(config.XDPoS.Epoch)-1, config, 0)
|
||||
minePeriod := config.XDPoS.V2.MinePeriod
|
||||
adaptor := blockchain.Engine().(*XDPoS.XDPoS)
|
||||
|
||||
|
|
@ -32,11 +33,12 @@ func TestYourTurnInitialV2(t *testing.T) {
|
|||
Coinbase: common.HexToAddress(blockCoinbaseA),
|
||||
Extra: common.Hex2Bytes("d7830100018358444388676f312e31352e38856c696e757800000000000000000278c350152e15fa6ffc712a5a73d704ce73e2e103d9e17ae3ff2c6712e44e25b09ac5ee91f6c9ff065551f0dcac6f00cae11192d462db709be3758ccef312ee5eea8d7bad5374c6a652150515d744508b61c1a4deb4e4e7bf057e4e3824c11fd2569bcb77a52905cda63b5a58507910bed335e4c9d87ae0ecdfafd400"),
|
||||
}
|
||||
block900, err := createBlockFromHeader(blockchain, header, nil)
|
||||
block900, err := createBlockFromHeader(blockchain, header, nil, signer, signFn, config)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
blockchain.InsertBlock(block900)
|
||||
err = blockchain.InsertBlock(block900)
|
||||
assert.Nil(t, err)
|
||||
time.Sleep(time.Duration(minePeriod) * time.Second)
|
||||
|
||||
// YourTurn is called before mine first v2 block
|
||||
|
|
@ -60,7 +62,7 @@ func TestYourTurnInitialV2(t *testing.T) {
|
|||
|
||||
func TestUpdateMasterNodes(t *testing.T) {
|
||||
config := params.TestXDPoSMockChainConfig
|
||||
blockchain, backend, currentBlock, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, int(config.XDPoS.Epoch+config.XDPoS.Gap)-1, config, 0)
|
||||
blockchain, _, currentBlock, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, int(config.XDPoS.Epoch+config.XDPoS.Gap)-1, config, 0)
|
||||
adaptor := blockchain.Engine().(*XDPoS.XDPoS)
|
||||
x := adaptor.EngineV2
|
||||
snap, err := x.GetSnapshot(blockchain, currentBlock.Header())
|
||||
|
|
@ -84,12 +86,10 @@ func TestUpdateMasterNodes(t *testing.T) {
|
|||
ParentHash: currentBlock.Hash(),
|
||||
Coinbase: common.HexToAddress(blockCoinbaseA),
|
||||
}
|
||||
// insert header validator
|
||||
err = generateSignature(backend, adaptor, header)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
parentBlock, err := createBlockFromHeader(blockchain, header, []*types.Transaction{tx})
|
||||
|
||||
header.Extra = generateV2Extra(450, currentBlock, signer, signFn)
|
||||
|
||||
parentBlock, err := createBlockFromHeader(blockchain, header, []*types.Transaction{tx}, signer, signFn, config)
|
||||
assert.Nil(t, err)
|
||||
err = blockchain.InsertBlock(parentBlock)
|
||||
assert.Nil(t, err)
|
||||
|
|
@ -100,22 +100,21 @@ func TestUpdateMasterNodes(t *testing.T) {
|
|||
for i := 1351; i <= 1800; i++ {
|
||||
blockCoinbase := fmt.Sprintf("0xaaa000000000000000000000000000000000%4d", i)
|
||||
//Get from block validator error message
|
||||
merkleRoot := "46234e9cd7e85a267f7f0435b15256a794a2f6d65cc98cdbd21dcd10a01d9772"
|
||||
header = &types.Header{
|
||||
Root: common.HexToHash(merkleRoot),
|
||||
Number: big.NewInt(int64(i)),
|
||||
ParentHash: parentBlock.Hash(),
|
||||
Coinbase: common.HexToAddress(blockCoinbase),
|
||||
}
|
||||
err = generateSignature(backend, adaptor, header)
|
||||
|
||||
header.Extra = generateV2Extra(int64(i), currentBlock, signer, signFn)
|
||||
|
||||
block, err := createBlockFromHeader(blockchain, header, nil, signer, signFn, config)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
block, err := createBlockFromHeader(blockchain, header, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
blockchain.InsertBlock(block)
|
||||
err = blockchain.InsertBlock(block)
|
||||
assert.Nil(t, err)
|
||||
parentBlock = block
|
||||
}
|
||||
|
||||
|
|
@ -128,20 +127,32 @@ func TestUpdateMasterNodes(t *testing.T) {
|
|||
|
||||
func TestPrepare(t *testing.T) {
|
||||
config := params.TestXDPoSMockChainConfig
|
||||
blockchain, _, currentBlock, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, int(config.XDPoS.Epoch), config, 0)
|
||||
blockchain, _, currentBlock, signer, _, _ := PrepareXDCTestBlockChainForV2Engine(t, int(config.XDPoS.Epoch), config, 0)
|
||||
adaptor := blockchain.Engine().(*XDPoS.XDPoS)
|
||||
|
||||
adaptor.YourTurn(blockchain, currentBlock.Header(), common.HexToAddress("xdc0278C350152e15fa6FFC712a5A73D704Ce73E2E1"))
|
||||
|
||||
_, err := adaptor.YourTurn(blockchain, currentBlock.Header(), common.HexToAddress("xdc0278C350152e15fa6FFC712a5A73D704Ce73E2E1"))
|
||||
assert.Nil(t, err)
|
||||
tstamp := time.Now().Unix()
|
||||
header901 := &types.Header{
|
||||
|
||||
header901WithoutCoinbase := &types.Header{
|
||||
ParentHash: currentBlock.Hash(),
|
||||
Number: big.NewInt(int64(901)),
|
||||
GasLimit: params.TargetGasLimit,
|
||||
Time: big.NewInt(tstamp),
|
||||
}
|
||||
|
||||
err := adaptor.Prepare(blockchain, header901)
|
||||
err = adaptor.Prepare(blockchain, header901WithoutCoinbase)
|
||||
assert.Equal(t, consensus.ErrCoinbaseMismatch, err)
|
||||
|
||||
header901 := &types.Header{
|
||||
ParentHash: currentBlock.Hash(),
|
||||
Number: big.NewInt(int64(901)),
|
||||
GasLimit: params.TargetGasLimit,
|
||||
Time: big.NewInt(tstamp),
|
||||
Coinbase: signer,
|
||||
}
|
||||
|
||||
err = adaptor.Prepare(blockchain, header901)
|
||||
assert.Nil(t, err)
|
||||
|
||||
snap, err := adaptor.EngineV2.GetSnapshot(blockchain, currentBlock.Header())
|
||||
|
|
@ -1,8 +1,7 @@
|
|||
package tests
|
||||
package engine_v2_tests
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/common"
|
||||
|
|
@ -16,7 +15,7 @@ import (
|
|||
|
||||
func TestHookPenaltyV2Mining(t *testing.T) {
|
||||
config := params.TestXDPoSMockChainConfig
|
||||
blockchain, _, _, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, int(config.XDPoS.Epoch)*7, config, 0)
|
||||
blockchain, _, _, signer, _, _ := PrepareXDCTestBlockChainForV2Engine(t, int(config.XDPoS.Epoch)*7, config, 0)
|
||||
adaptor := blockchain.Engine().(*XDPoS.XDPoS)
|
||||
hooks.AttachConsensusV2Hooks(adaptor, blockchain, config)
|
||||
assert.NotNil(t, adaptor.EngineV2.HookPenalty)
|
||||
|
|
@ -26,13 +25,19 @@ func TestHookPenaltyV2Mining(t *testing.T) {
|
|||
err := utils.DecodeBytesExtraFields(header901.Extra, &extraField)
|
||||
assert.Nil(t, err)
|
||||
masternodes := adaptor.GetMasternodesFromCheckpointHeader(header901)
|
||||
assert.Equal(t, 4, len(masternodes))
|
||||
assert.Equal(t, 5, len(masternodes))
|
||||
header6300 := blockchain.GetHeaderByNumber(config.XDPoS.Epoch * 7)
|
||||
penalty, err := adaptor.EngineV2.HookPenalty(blockchain, big.NewInt(int64(config.XDPoS.Epoch*7)), header6300.ParentHash, masternodes)
|
||||
assert.Nil(t, err)
|
||||
// miner (coinbase) is not in penalty. all others are in penalty
|
||||
assert.Equal(t, 3, len(penalty))
|
||||
assert.True(t, reflect.DeepEqual([]common.Address{header901.Coinbase}, common.RemoveItemFromArray(masternodes, penalty)))
|
||||
// only 1 signer address not in the masternode list
|
||||
assert.Equal(t, 1, len(penalty))
|
||||
contains := false
|
||||
for _, mn := range common.RemoveItemFromArray(masternodes, penalty) {
|
||||
if mn == header901.Coinbase {
|
||||
contains = true
|
||||
}
|
||||
}
|
||||
assert.True(t, contains)
|
||||
// set adaptor round/qc to that of 6299
|
||||
err = utils.DecodeBytesExtraFields(header6300.Extra, &extraField)
|
||||
assert.Nil(t, err)
|
||||
|
|
@ -43,12 +48,13 @@ func TestHookPenaltyV2Mining(t *testing.T) {
|
|||
Number: header6300.Number,
|
||||
GasLimit: params.TargetGasLimit,
|
||||
Time: header6300.Time,
|
||||
Coinbase: signer,
|
||||
}
|
||||
err = adaptor.Prepare(blockchain, headerMining)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 3, len(headerMining.Penalties)/common.AddressLength)
|
||||
assert.Equal(t, 1, len(headerMining.Penalties)/common.AddressLength)
|
||||
// 20 candidates (set by PrepareXDCTestBlockChainForV2Engine) - 3 penalty = 17
|
||||
assert.Equal(t, 17, len(headerMining.Validators)/common.AddressLength)
|
||||
assert.Equal(t, 19, len(headerMining.Validators)/common.AddressLength)
|
||||
}
|
||||
|
||||
func TestHookPenaltyV2Comeback(t *testing.T) {
|
||||
|
|
@ -63,12 +69,12 @@ func TestHookPenaltyV2Comeback(t *testing.T) {
|
|||
err := utils.DecodeBytesExtraFields(header901.Extra, &extraField)
|
||||
assert.Nil(t, err)
|
||||
masternodes := adaptor.GetMasternodesFromCheckpointHeader(header901)
|
||||
assert.Equal(t, 4, len(masternodes))
|
||||
assert.Equal(t, 5, len(masternodes))
|
||||
header6300 := blockchain.GetHeaderByNumber(config.XDPoS.Epoch * 7)
|
||||
penalty, err := adaptor.EngineV2.HookPenalty(blockchain, big.NewInt(int64(config.XDPoS.Epoch*7)), header6300.ParentHash, masternodes)
|
||||
assert.Nil(t, err)
|
||||
// miner (coinbase) is in comeback. so all addresses are in penalty
|
||||
assert.Equal(t, 4, len(penalty))
|
||||
assert.Equal(t, 2, len(penalty))
|
||||
header6285 := blockchain.GetHeaderByNumber(config.XDPoS.Epoch*7 - common.MergeSignRange)
|
||||
// forcely insert signing tx into cache, to cancel comeback. since no comeback, penalty is 3
|
||||
tx, err := signingTxWithSignerFn(header6285, 0, signer, signFn)
|
||||
|
|
@ -76,7 +82,7 @@ func TestHookPenaltyV2Comeback(t *testing.T) {
|
|||
adaptor.CacheSigningTxs(header6285.Hash(), []*types.Transaction{tx})
|
||||
penalty, err = adaptor.EngineV2.HookPenalty(blockchain, big.NewInt(int64(config.XDPoS.Epoch*7)), header6300.ParentHash, masternodes)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 3, len(penalty))
|
||||
assert.Equal(t, 1, len(penalty))
|
||||
}
|
||||
|
||||
func TestHookPenaltyV2Jump(t *testing.T) {
|
||||
|
|
@ -92,11 +98,11 @@ func TestHookPenaltyV2Jump(t *testing.T) {
|
|||
err := utils.DecodeBytesExtraFields(header901.Extra, &extraField)
|
||||
assert.Nil(t, err)
|
||||
masternodes := adaptor.GetMasternodesFromCheckpointHeader(header901)
|
||||
assert.Equal(t, 4, len(masternodes))
|
||||
assert.Equal(t, 5, len(masternodes))
|
||||
header6285 := blockchain.GetHeaderByNumber(uint64(end))
|
||||
adaptor.EngineV2.SetNewRoundFaker(blockchain, utils.Round(config.XDPoS.Epoch*7), false)
|
||||
// round 6285-6300 miss blocks, penalty should work as usual
|
||||
penalty, err := adaptor.EngineV2.HookPenalty(blockchain, header6285.Number, header6285.ParentHash, masternodes)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 4, len(penalty))
|
||||
assert.Equal(t, 2, len(penalty))
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package tests
|
||||
package engine_v2_tests
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
|
@ -45,7 +45,8 @@ func TestShouldSendVoteMsgAndCommitGrandGrandParentBlock(t *testing.T) {
|
|||
blockNum := 902
|
||||
blockCoinBase := fmt.Sprintf("0x111000000000000000000000000000000%03d", blockNum)
|
||||
block902 := CreateBlock(blockchain, params.TestXDPoSMockChainConfig, currentBlock, blockNum, 2, blockCoinBase, signer, signFn, nil)
|
||||
blockchain.InsertBlock(block902)
|
||||
err = blockchain.InsertBlock(block902)
|
||||
assert.Nil(t, err)
|
||||
err = engineV2.ProposedBlockHandler(blockchain, block902.Header())
|
||||
if err != nil {
|
||||
t.Fatal("Fail propose proposedBlock handler", err)
|
||||
|
|
@ -62,7 +63,8 @@ func TestShouldSendVoteMsgAndCommitGrandGrandParentBlock(t *testing.T) {
|
|||
blockNum = 903
|
||||
blockCoinBase = fmt.Sprintf("0x111000000000000000000000000000000%03d", blockNum)
|
||||
block903 := CreateBlock(blockchain, params.TestXDPoSMockChainConfig, block902, blockNum, 3, blockCoinBase, signer, signFn, nil)
|
||||
blockchain.InsertBlock(block903)
|
||||
err = blockchain.InsertBlock(block903)
|
||||
assert.Nil(t, err)
|
||||
err = engineV2.ProposedBlockHandler(blockchain, block903.Header())
|
||||
if err != nil {
|
||||
t.Fatal("Fail propose proposedBlock handler", err)
|
||||
|
|
@ -80,7 +82,8 @@ func TestShouldSendVoteMsgAndCommitGrandGrandParentBlock(t *testing.T) {
|
|||
blockNum = 904
|
||||
blockCoinBase = fmt.Sprintf("0x111000000000000000000000000000000%03d", blockNum)
|
||||
block904 := CreateBlock(blockchain, params.TestXDPoSMockChainConfig, block903, blockNum, 4, blockCoinBase, signer, signFn, nil)
|
||||
blockchain.InsertBlock(block904)
|
||||
err = blockchain.InsertBlock(block904)
|
||||
assert.Nil(t, err)
|
||||
err = engineV2.ProposedBlockHandler(blockchain, block904.Header())
|
||||
if err != nil {
|
||||
t.Fatal("Fail propose proposedBlock handler", err)
|
||||
|
|
@ -131,7 +134,8 @@ func TestShouldNotCommitIfRoundsNotContinousFor3Rounds(t *testing.T) {
|
|||
blockNum := 906
|
||||
blockCoinBase := fmt.Sprintf("0x111000000000000000000000000000000%03d", blockNum)
|
||||
block906 := CreateBlock(blockchain, params.TestXDPoSMockChainConfig, currentBlock, blockNum, 7, blockCoinBase, signer, signFn, nil)
|
||||
blockchain.InsertBlock(block906)
|
||||
err = blockchain.InsertBlock(block906)
|
||||
assert.Nil(t, err)
|
||||
err = engineV2.ProposedBlockHandler(blockchain, block906.Header())
|
||||
if err != nil {
|
||||
t.Fatal("Fail propose proposedBlock handler", err)
|
||||
|
|
@ -152,7 +156,8 @@ func TestShouldNotCommitIfRoundsNotContinousFor3Rounds(t *testing.T) {
|
|||
blockNum = 907
|
||||
blockCoinBase = fmt.Sprintf("0x111000000000000000000000000000000%03d", blockNum)
|
||||
block907 := CreateBlock(blockchain, params.TestXDPoSMockChainConfig, block906, blockNum, 8, blockCoinBase, signer, signFn, nil)
|
||||
blockchain.InsertBlock(block907)
|
||||
err = blockchain.InsertBlock(block907)
|
||||
assert.Nil(t, err)
|
||||
err = engineV2.ProposedBlockHandler(blockchain, block907.Header())
|
||||
if err != nil {
|
||||
t.Fatal("Fail propose proposedBlock handler", err)
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package tests
|
||||
package engine_v2_tests
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package tests
|
||||
package engine_v2_tests
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package tests
|
||||
package engine_v2_tests
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package tests
|
||||
package engine_v2_tests
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
|
@ -27,7 +27,8 @@ func TestShouldVerifyBlockInfo(t *testing.T) {
|
|||
blockNum := 902
|
||||
blockCoinBase := fmt.Sprintf("0x111000000000000000000000000000000%03d", blockNum)
|
||||
block902 := CreateBlock(blockchain, params.TestXDPoSMockChainConfig, currentBlock, blockNum, 2, blockCoinBase, signer, signFn, nil)
|
||||
blockchain.InsertBlock(block902)
|
||||
err = blockchain.InsertBlock(block902)
|
||||
assert.Nil(t, err)
|
||||
|
||||
blockInfo = &utils.BlockInfo{
|
||||
Hash: block902.Hash(),
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package tests
|
||||
package engine_v2_tests
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
|
@ -34,7 +34,8 @@ func TestShouldVerifyBlock(t *testing.T) {
|
|||
adaptor := blockchain.Engine().(*XDPoS.XDPoS)
|
||||
|
||||
// Happy path
|
||||
err = adaptor.VerifyHeader(blockchain, blockchain.GetBlockByNumber(901).Header(), true)
|
||||
happyPathHeader := blockchain.GetBlockByNumber(901).Header()
|
||||
err = adaptor.VerifyHeader(blockchain, happyPathHeader, true)
|
||||
assert.Nil(t, err)
|
||||
|
||||
// Unhappy path
|
||||
|
|
@ -142,6 +143,22 @@ func TestShouldVerifyBlock(t *testing.T) {
|
|||
err = adaptor.VerifyHeader(blockchain, invalidPenaltiesExistBlock, true)
|
||||
assert.Equal(t, utils.ErrPenaltyListDoesNotMatch, err)
|
||||
|
||||
// Not valid validator
|
||||
coinbaseValidatorMismatchBlock := blockchain.GetBlockByNumber(902).Header()
|
||||
notQualifiedSigner, notQualifiedSignFn, err := getSignerAndSignFn(voterKey)
|
||||
assert.Nil(t, err)
|
||||
sealHeader(blockchain, coinbaseValidatorMismatchBlock, notQualifiedSigner, notQualifiedSignFn)
|
||||
err = adaptor.VerifyHeader(blockchain, coinbaseValidatorMismatchBlock, true)
|
||||
assert.Equal(t, utils.ErrCoinbaseAndValidatorMismatch, err)
|
||||
|
||||
// Make the validators not legit by adding something to the penalty
|
||||
validatorsNotLegit := blockchain.GetBlockByNumber(901).Header()
|
||||
penalties := []common.Address{acc1Addr}
|
||||
for _, v := range penalties {
|
||||
validatorsNotLegit.Penalties = append(validatorsNotLegit.Penalties, v[:]...)
|
||||
}
|
||||
err = adaptor.VerifyHeader(blockchain, validatorsNotLegit, true)
|
||||
assert.Equal(t, utils.ErrValidatorsNotLegit, err)
|
||||
}
|
||||
|
||||
func TestShouldFailIfNotEnoughQCSignatures(t *testing.T) {
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package tests
|
||||
package engine_v2_tests
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
|
@ -354,7 +354,8 @@ func TestVoteMessageShallNotThrowErrorIfBlockNotYetExist(t *testing.T) {
|
|||
assert.Equal(t, utils.Round(6), currentRound)
|
||||
|
||||
// Now, inject the block into the chain
|
||||
blockchain.InsertBlock(block)
|
||||
err = blockchain.InsertBlock(block)
|
||||
assert.Nil(t, err)
|
||||
|
||||
voteMsg = &utils.Vote{
|
||||
ProposedBlockInfo: blockInfo,
|
||||
Loading…
Reference in a new issue