add test and fix issue on happy path (#46)

* add test and fix issue on happy path

* add prepare test

* update comment
This commit is contained in:
Liam 2022-01-25 10:51:17 +03:00 committed by GitHub
parent 05d315d2e3
commit ff0fcd3951
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 200 additions and 88 deletions

View file

@ -125,13 +125,13 @@ func (x *XDPoS_v2) Initial(chain consensus.ChainReader, header *types.Header, ma
log.Info("[Initial] highest QC for consensus v2 first block", "Block Num", header.Number.String(), "BlockHash", header.Hash())
// Generate new parent blockInfo and put it into QC
parentBlockInfo := &utils.BlockInfo{
Hash: header.ParentHash,
blockInfo := &utils.BlockInfo{
Hash: header.Hash(),
Round: utils.Round(0),
Number: big.NewInt(0).Sub(header.Number, big.NewInt(1)),
Number: header.Number,
}
quorumCert := &utils.QuorumCert{
ProposedBlockInfo: parentBlockInfo,
ProposedBlockInfo: blockInfo,
Signatures: nil,
}
x.currentRound = 1
@ -156,7 +156,7 @@ func (x *XDPoS_v2) Prepare(chain consensus.ChainReader, header *types.Header) er
highestQC := x.highestQuorumCert
x.lock.RUnlock()
if (highestQC == nil) || (header.ParentHash != highestQC.ProposedBlockInfo.Hash) {
if header.ParentHash != highestQC.ProposedBlockInfo.Hash {
return consensus.ErrNotReadyToPropose
}
@ -165,6 +165,12 @@ func (x *XDPoS_v2) Prepare(chain consensus.ChainReader, header *types.Header) er
QuorumCert: highestQC,
}
extraBytes, err := extra.EncodeToBytes()
if err != nil {
return err
}
header.Extra = extraBytes
header.Nonce = types.BlockNonce{}
number := header.Number.Uint64()
@ -173,40 +179,32 @@ func (x *XDPoS_v2) Prepare(chain consensus.ChainReader, header *types.Header) er
if parent == nil {
return consensus.ErrUnknownAncestor
}
// Set the correct difficulty
header.Difficulty = x.calcDifficulty(chain, parent, x.signer)
log.Debug("CalcDifficulty ", "number", header.Number, "difficulty", header.Difficulty)
// TODO: previous round should sit on previous Epoch and x.currentRound should >= Epoch number
isEpochSwitchBlock, _, err := x.IsEpochSwitch(header)
if err != nil {
log.Error("[Prepare] Error while trying to determine if header is an epoch switch during Prepare", "header", header, "Error", err)
return err
}
if isEpochSwitchBlock {
snap, err := x.getSnapshot(chain, number-1)
snap, err := x.getSnapshot(chain, number)
if err != nil {
return err
}
masternodes := snap.NextEpochMasterNodes
//TODO: remove penalty nodes and add comeback nodes
//TODO: remove penalty nodes and add comeback nodes, or change this logic into yourturn function
for _, v := range masternodes {
header.Validators = append(header.Validators, v[:]...)
}
}
extraBytes, err := extra.EncodeToBytes()
if err != nil {
return err
}
header.Extra = extraBytes
// Mix digest is reserved for now, set to empty
header.MixDigest = common.Hash{}
// Ensure the timestamp has the correct delay
// TODO: Proper deal with time
// TODO: if timestamp > current time, how to deal with future timestamp
header.Time = new(big.Int).Add(parent.Time, new(big.Int).SetUint64(x.config.Period))
@ -337,7 +335,7 @@ func (x *XDPoS_v2) YourTurn(chain consensus.ChainReader, parent *types.Header, s
round := x.currentRound
isEpochSwitch, _, err := x.IsEpochSwitchAtRound(round, parent)
if err != nil {
log.Error("[YourTurn]", "Error", err)
log.Error("[YourTurn] check epoch switch at round failed", "Error", err)
return false, err
}
var masterNodes []common.Address
@ -345,14 +343,19 @@ func (x *XDPoS_v2) YourTurn(chain consensus.ChainReader, parent *types.Header, s
if x.config.XDPoSV2Block.Cmp(parent.Number) == 0 {
snap, err := x.getSnapshot(chain, x.config.XDPoSV2Block.Uint64())
if err != nil {
log.Error("[YourTurn]Cannot find snapshot at gap num of last V1", "err", err, "number", x.config.XDPoSV2Block.Uint64())
log.Error("[YourTurn] Cannot find snapshot at gap num of last V1", "err", err, "number", x.config.XDPoSV2Block.Uint64())
return false, err
}
// the initial snapshot of v1->v2 switch does not need penalty
// the initial snapshot of v1->v2 switch containes penalites node
masterNodes = snap.NextEpochMasterNodes
} else {
// TODO: calc master nodes by smart contract - penalty
// TODO: related to snapshot
snap, err := x.getSnapshot(chain, parent.Number.Uint64()+1)
if err != nil {
log.Error("[YourTurn] Cannot find snapshot at gap block", "err", err, "number", x.config.XDPoSV2Block.Uint64())
return false, err
}
masterNodes = snap.NextEpochMasterNodes
// TODO: calculate master nodes with penalty and comback
}
} else {
// this block and parent belong to the same epoch
@ -1137,7 +1140,7 @@ func (x *XDPoS_v2) IsEpochSwitch(header *types.Header) (bool, uint64, error) {
}
parentRound := decodedExtraField.QuorumCert.ProposedBlockInfo.Round
round := decodedExtraField.Round
epochStart := round - round%utils.Round(x.config.Epoch)
epochStartRound := round - round%utils.Round(x.config.Epoch)
epochNum := x.config.XDPoSV2Block.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 decodedExtraField.QuorumCert.ProposedBlockInfo.Number.Cmp(x.config.XDPoSV2Block) == 0 {
@ -1145,7 +1148,7 @@ func (x *XDPoS_v2) IsEpochSwitch(header *types.Header) (bool, uint64, error) {
return true, epochNum, nil
}
log.Info("[IsEpochSwitch]", "parent round", parentRound, "round", round, "number", header.Number.Uint64(), "hash", header.Hash())
return parentRound < epochStart, epochNum, nil
return parentRound < epochStartRound, epochNum, nil
}
// IsEpochSwitchAtRound() is used by miner to check whether it mines a block in the same epoch with parent
@ -1162,8 +1165,8 @@ func (x *XDPoS_v2) IsEpochSwitchAtRound(round utils.Round, parentHeader *types.H
return false, 0, err
}
parentRound := decodedExtraField.Round
epochStart := round - round%utils.Round(x.config.Epoch)
return parentRound < epochStart, epochNum, nil
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,

View file

@ -63,3 +63,12 @@ func (s *SnapshotV2) GetMappedMasterNodes() map[common.Address]struct{} {
}
return ms
}
func (s *SnapshotV2) IsMasterNodes(address common.Address) bool {
for _, n := range s.NextEpochMasterNodes {
if n.String() == address.String() {
return true
}
}
return false
}

View file

@ -1,54 +0,0 @@
package tests
import (
"math/big"
"testing"
"github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS"
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils"
"github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/params"
"github.com/stretchr/testify/assert"
)
func TestYourTurnInitialV2(t *testing.T) {
config := params.TestXDPoSMockChainConfigWithV2EngineEpochSwitch
blockchain, _, parentBlock, _ := PrepareXDCTestBlockChain(t, int(config.XDPoS.Epoch)-1, config)
adaptor := blockchain.Engine().(*XDPoS.XDPoS)
// Insert block 900
t.Logf("Inserting block with propose at 900...")
blockCoinbaseA := "0xaaa0000000000000000000000000000000000900"
//Get from block validator error message
merkleRoot := "35999dded35e8db12de7e6c1471eb9670c162eec616ecebbaf4fddd4676fb930"
header := &types.Header{
Root: common.HexToHash(merkleRoot),
Number: big.NewInt(int64(900)),
ParentHash: parentBlock.Hash(),
Coinbase: common.HexToAddress(blockCoinbaseA),
Extra: common.Hex2Bytes("d7830100018358444388676f312e31352e38856c696e757800000000000000000278c350152e15fa6ffc712a5a73d704ce73e2e103d9e17ae3ff2c6712e44e25b09ac5ee91f6c9ff065551f0dcac6f00cae11192d462db709be3758ccef312ee5eea8d7bad5374c6a652150515d744508b61c1a4deb4e4e7bf057e4e3824c11fd2569bcb77a52905cda63b5a58507910bed335e4c9d87ae0ecdfafd400"),
}
block900, err := insertBlock(blockchain, header)
if err != nil {
t.Fatal(err)
}
// YourTurn is called before mine first v2 block
b, err := adaptor.YourTurn(blockchain, block900.Header(), common.HexToAddress("xdc0278C350152e15fa6FFC712a5A73D704Ce73E2E1"))
assert.Nil(t, err)
assert.False(t, b)
b, err = adaptor.YourTurn(blockchain, block900.Header(), common.HexToAddress("xdc03d9e17Ae3fF2c6712E44e25B09Ac5ee91f6c9ff"))
assert.Nil(t, err)
// round=1, so masternode[1] has YourTurn = True
assert.True(t, b)
assert.Equal(t, adaptor.EngineV2.GetCurrentRound(), utils.Round(1))
snap, err := adaptor.EngineV2.GetSnapshot(blockchain, block900.Header())
assert.Nil(t, err)
assert.NotNil(t, snap)
masterNodes := adaptor.EngineV1.GetMasternodesFromCheckpointHeader(block900.Header())
for i := 0; i < len(masterNodes); i++ {
assert.Equal(t, masterNodes[i].Hex(), snap.NextEpochMasterNodes[i].Hex())
}
}

View file

@ -0,0 +1,155 @@
package tests
import (
"fmt"
"math/big"
"testing"
"time"
"github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS"
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils"
"github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/params"
"github.com/stretchr/testify/assert"
)
func TestYourTurnInitialV2(t *testing.T) {
config := params.TestXDPoSMockChainConfigWithV2EngineEpochSwitch
blockchain, _, parentBlock, _ := PrepareXDCTestBlockChain(t, int(config.XDPoS.Epoch)-1, config)
adaptor := blockchain.Engine().(*XDPoS.XDPoS)
// Insert block 900
t.Logf("Inserting block with propose at 900...")
blockCoinbaseA := "0xaaa0000000000000000000000000000000000900"
//Get from block validator error message
merkleRoot := "35999dded35e8db12de7e6c1471eb9670c162eec616ecebbaf4fddd4676fb930"
header := &types.Header{
Root: common.HexToHash(merkleRoot),
Number: big.NewInt(int64(900)),
ParentHash: parentBlock.Hash(),
Coinbase: common.HexToAddress(blockCoinbaseA),
Extra: common.Hex2Bytes("d7830100018358444388676f312e31352e38856c696e757800000000000000000278c350152e15fa6ffc712a5a73d704ce73e2e103d9e17ae3ff2c6712e44e25b09ac5ee91f6c9ff065551f0dcac6f00cae11192d462db709be3758ccef312ee5eea8d7bad5374c6a652150515d744508b61c1a4deb4e4e7bf057e4e3824c11fd2569bcb77a52905cda63b5a58507910bed335e4c9d87ae0ecdfafd400"),
}
block900, err := insertBlock(blockchain, header)
if err != nil {
t.Fatal(err)
}
// YourTurn is called before mine first v2 block
b, err := adaptor.YourTurn(blockchain, block900.Header(), common.HexToAddress("xdc0278C350152e15fa6FFC712a5A73D704Ce73E2E1"))
assert.Nil(t, err)
assert.False(t, b)
b, err = adaptor.YourTurn(blockchain, block900.Header(), common.HexToAddress("xdc03d9e17Ae3fF2c6712E44e25B09Ac5ee91f6c9ff"))
assert.Nil(t, err)
// round=1, so masternode[1] has YourTurn = True
assert.True(t, b)
assert.Equal(t, adaptor.EngineV2.GetCurrentRound(), utils.Round(1))
snap, err := adaptor.EngineV2.GetSnapshot(blockchain, block900.Header())
assert.Nil(t, err)
assert.NotNil(t, snap)
masterNodes := adaptor.EngineV1.GetMasternodesFromCheckpointHeader(block900.Header())
for i := 0; i < len(masterNodes); i++ {
assert.Equal(t, masterNodes[i].Hex(), snap.NextEpochMasterNodes[i].Hex())
}
}
func TestUpdateMasterNodes(t *testing.T) {
config := params.TestXDPoSMockChainConfigWithV2EngineEpochSwitch
blockchain, backend, currentBlock, _, _, _ := 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())
assert.Nil(t, err)
assert.Equal(t, int(snap.Number), 450)
// Insert block 1350
t.Logf("Inserting block with propose at 1350...")
blockCoinbaseA := "0xaaa0000000000000000000000000000000001350"
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(1350)),
ParentHash: currentBlock.Hash(),
Coinbase: common.HexToAddress(blockCoinbaseA),
}
// insert header validator
err = generateSignature(backend, adaptor, header)
if err != nil {
t.Fatal(err)
}
parentBlock, err := insertBlockTxs(blockchain, header, []*types.Transaction{tx})
assert.Nil(t, err)
t.Logf("Inserting block from 1351 to 1800...")
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)
if err != nil {
t.Fatal(err)
}
block, err := insertBlock(blockchain, header)
if err != nil {
t.Fatal(err)
}
parentBlock = block
}
snap, err = x.GetSnapshot(blockchain, parentBlock.Header())
assert.Nil(t, err)
assert.False(t, snap.IsMasterNodes(acc3Addr))
assert.True(t, snap.IsMasterNodes(acc1Addr))
assert.Equal(t, int(snap.Number), 1350)
}
func TestPrepare(t *testing.T) {
config := params.TestXDPoSMockChainConfigWithV2EngineEpochSwitch
blockchain, _, currentBlock, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, int(config.XDPoS.Epoch), config, 0)
adaptor := blockchain.Engine().(*XDPoS.XDPoS)
adaptor.YourTurn(blockchain, currentBlock.Header(), common.HexToAddress("xdc0278C350152e15fa6FFC712a5A73D704Ce73E2E1"))
tstamp := time.Now().Unix()
header901 := &types.Header{
ParentHash: currentBlock.Hash(),
Number: big.NewInt(int64(901)),
GasLimit: params.TargetGasLimit,
Time: big.NewInt(tstamp),
}
err := adaptor.Prepare(blockchain, header901)
assert.Nil(t, err)
snap, err := adaptor.EngineV2.GetSnapshot(blockchain, currentBlock.Header())
if err != nil {
t.Fatal(err)
}
validators := []byte{}
for _, v := range snap.NextEpochMasterNodes {
validators = append(validators, v[:]...)
}
assert.Equal(t, validators, header901.Validators)
var decodedExtraField utils.ExtraFields_v2
err = utils.DecodeBytesExtraFields(header901.Extra, &decodedExtraField)
assert.Nil(t, err)
assert.Equal(t, utils.Round(1), decodedExtraField.Round)
assert.Equal(t, utils.Round(0), decodedExtraField.QuorumCert.ProposedBlockInfo.Round)
}

View file

@ -242,8 +242,10 @@ func PrepareXDCTestBlockChain(t *testing.T, numOfBlocks int, chainConfig *params
currentBlock := blockchain.Genesis()
go func() {
checkpointChanMsg := <-core.CheckpointCh
log.Info("[V1] Got a message from core CheckpointChan!", "msg", checkpointChanMsg)
for range core.CheckpointCh {
checkpointChanMsg := <-core.CheckpointCh
log.Info("[V1] Got a message from core CheckpointChan!", "msg", checkpointChanMsg)
}
}()
// Insert initial blocks
@ -290,8 +292,10 @@ func PrepareXDCTestBlockChainForV2Engine(t *testing.T, numOfBlocks int, chainCon
var currentForkBlock *types.Block
go func() {
checkpointChanMsg := <-core.CheckpointCh
log.Info("[V2] Got a message from core CheckpointChan!", "msg", checkpointChanMsg)
for range core.CheckpointCh {
checkpointChanMsg := <-core.CheckpointCh
log.Info("[V2] Got a message from core CheckpointChan!", "msg", checkpointChanMsg)
}
}()
var masternodesFromV1LastEpoch []common.Address

View file

@ -2052,7 +2052,6 @@ func (bc *BlockChain) insertBlock(block *types.Block) ([]interface{}, []*types.L
}
if isEpochSwithBlock {
CheckpointCh <- 1
}
}
// Append a single chain head event if we've progressed the chain
@ -2480,16 +2479,12 @@ func (bc *BlockChain) UpdateM1() error {
// if can't get anything, request from contracts
stateDB, err := bc.State()
if err != nil {
candidates, err = validator.GetCandidates(opts)
if err != nil {
return err
}
} else {
candidates = state.GetCandidates(stateDB)
}
var ms []utils.Masternode