Xin 127 initial v2 function and snapshot (#42)

* initial v2 and v2 snapshot

* update based on pr feedback

* add test for initial v2 parameters

* remove comment
This commit is contained in:
Liam 2022-01-20 13:27:51 +03:00 committed by GitHub
parent e72d4866df
commit 646042a651
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 241 additions and 285 deletions

View file

@ -278,7 +278,10 @@ func (x *XDPoS) GetMasternodesByNumber(chain consensus.ChainReader, blockNumber
}
func (x *XDPoS) YourTurn(chain consensus.ChainReader, parent *types.Header, signer common.Address) (bool, error) {
switch x.config.BlockConsensusVersion(parent.Number) {
if parent.Number.Cmp(x.config.XDPoSV2Block) == 0 {
x.initialV2(chain, parent)
}
switch x.config.BlockConsensusVersion(big.NewInt(parent.Number.Int64() + 1)) {
case params.ConsensusEngineVersion2:
return x.EngineV2.YourTurn(chain, parent, signer)
default: // Default "v1"
@ -296,7 +299,7 @@ func (x *XDPoS) GetValidator(creator common.Address, chain consensus.ChainReader
func (x *XDPoS) UpdateMasternodes(chain consensus.ChainReader, header *types.Header, ms []utils.Masternode) error {
switch x.config.BlockConsensusVersion(header.Number) {
case params.ConsensusEngineVersion2:
return nil
return x.EngineV2.UpdateMasternodes(chain, header, ms)
default: // Default "v1"
return x.EngineV1.UpdateMasternodes(chain, header, ms)
}
@ -361,7 +364,7 @@ func (x *XDPoS) GetSnapshot(chain consensus.ChainReader, header *types.Header) (
return &utils.PublicApiSnapshot{
Number: sp.Number,
Hash: sp.Hash,
Signers: sp.MasterNodes,
Signers: sp.GetMappedMasterNodes(),
}, err
default: // Default "v1"
sp, err := x.EngineV1.GetSnapshot(chain, header)
@ -439,6 +442,14 @@ func (x *XDPoS) GetCachedSigningTxs(hash common.Hash) (interface{}, bool) {
}
//V2
func (x *XDPoS) initialV2(chain consensus.ChainReader, header *types.Header) error {
checkpointBlockNumber := header.Number.Uint64() - header.Number.Uint64()%x.config.Epoch
checkpointHeader := chain.GetHeaderByNumber(checkpointBlockNumber)
masternodes := x.EngineV1.GetMasternodesFromCheckpointHeader(checkpointHeader)
x.EngineV2.Initial(chain, header, masternodes)
return nil
}
func (x *XDPoS) VerifyVote(*utils.Vote) error {
return nil
}

View file

@ -29,7 +29,7 @@ type XDPoS_v2 struct {
config *params.XDPoSConfig // Consensus engine configuration parameters
db ethdb.Database // Database to store and retrieve snapshot checkpoints
recents *lru.ARCCache // Snapshots for recent block to speed up reorgs
snapshots *lru.ARCCache // Snapshots for gap block
signatures *lru.ARCCache // Signatures of recent blocks to speed up mining
epochSwitches *lru.ARCCache // infos of epoch: master nodes, epoch switch block info, parent of that info
@ -60,7 +60,7 @@ func New(config *params.XDPoSConfig, db ethdb.Database) *XDPoS_v2 {
timer := countdown.NewCountDown(duration)
timeoutPool := utils.NewPool(config.V2.CertThreshold)
recents, _ := lru.NewARC(utils.InmemorySnapshots)
snapshots, _ := lru.NewARC(utils.InmemorySnapshots)
signatures, _ := lru.NewARC(utils.InmemorySnapshots)
epochSwitches, _ := lru.NewARC(int(utils.InmemoryEpochs))
@ -70,7 +70,7 @@ func New(config *params.XDPoSConfig, db ethdb.Database) *XDPoS_v2 {
db: db,
signatures: signatures,
recents: recents,
snapshots: snapshots,
epochSwitches: epochSwitches,
timeoutWorker: timer,
BroadcastCh: make(chan interface{}),
@ -106,40 +106,55 @@ type SignerFn func(accounts.Account, []byte) ([]byte, error)
sigHash returns the hash which is used as input for the delegated-proof-of-stake
signing. It is the hash of the entire header apart from the 65 byte signature
contained at the end of the extra data.
Note, the method requires the extra data to be at least 65 bytes, otherwise it
panics. This is done to avoid accidentally using both forms (signature present
or not), which could be abused to produce different hashes for the same header.
*/
func (x *XDPoS_v2) SignHash(header *types.Header) (hash common.Hash) {
return sigHash(header)
}
func (x *XDPoS_v2) Initial(chain consensus.ChainReader, header *types.Header, masternodes []common.Address) error {
log.Info("[Initial] initial v2 related parameters")
if x.highestQuorumCert.ProposedBlockInfo.Round != 0 { //already initialized
log.Warn("[Initial] Already initialized")
return nil
}
x.lock.Lock()
defer x.lock.Unlock()
// Check header if it is the first consensus v2 block, if so, assign initial values to current round and highestQC
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,
Round: utils.Round(0),
Number: big.NewInt(0).Sub(header.Number, big.NewInt(1)),
}
quorumCert := &utils.QuorumCert{
ProposedBlockInfo: parentBlockInfo,
Signatures: nil,
}
x.currentRound = 1
x.highestQuorumCert = quorumCert
// Initial snapshot
lastGapNum := header.Number.Uint64() - header.Number.Uint64()%x.config.Epoch - x.config.Gap
lastGapHeader := chain.GetHeaderByNumber(lastGapNum)
snap := newSnapshot(lastGapNum, lastGapHeader.Hash(), x.currentRound, x.highestQuorumCert, masternodes)
x.snapshots.Add(snap.Hash, snap)
storeSnapshot(snap, x.db)
return nil
}
// Prepare implements consensus.Engine, preparing all the consensus fields of the
// header for running the transactions on top.
func (x *XDPoS_v2) Prepare(chain consensus.ChainReader, header *types.Header) error {
// Verify mined block parent matches highest QC
x.lock.Lock()
// Check header if it is the first consensus v2 block, if so, assign initial values to current round and highestQC
if header.Number.Cmp(big.NewInt(0).Add(x.config.XDPoSV2Block, big.NewInt(1))) == 0 {
log.Info("[Prepare] Initilising highest QC for consensus v2 first block", "Block Num", header.Number.String(), "BlockHash", header.Hash())
// Generate new parent blockInfo and put it into QC
parentBlockInfo := &utils.BlockInfo{
Hash: header.ParentHash,
Round: utils.Round(0),
Number: big.NewInt(0).Sub(header.Number, big.NewInt(1)),
}
quorumCert := &utils.QuorumCert{
ProposedBlockInfo: parentBlockInfo,
Signatures: nil,
}
x.currentRound = 1
x.highestQuorumCert = quorumCert
}
x.lock.RLock()
currentRound := x.currentRound
highestQC := x.highestQuorumCert
x.lock.Unlock()
x.lock.RUnlock()
if (highestQC == nil) || (header.ParentHash != highestQC.ProposedBlockInfo.Hash) {
return consensus.ErrNotReadyToPropose
@ -169,11 +184,11 @@ func (x *XDPoS_v2) Prepare(chain consensus.ChainReader, header *types.Header) er
return err
}
if isEpochSwitchBlock {
snap, err := x.snapshot(chain, number-1, header.ParentHash, nil)
snap, err := x.getSnapshot(chain, number-1)
if err != nil {
return err
}
masternodes := snap.GetMasterNodes()
masternodes := snap.NextEpochMasterNodes
//TODO: remove penalty nodes and add comeback nodes
for _, v := range masternodes {
header.Validators = append(header.Validators, v[:]...)
@ -193,7 +208,7 @@ func (x *XDPoS_v2) Prepare(chain consensus.ChainReader, header *types.Header) er
// Ensure the timestamp has the correct delay
// TODO: Proper deal with time
// TODO: if timestamp > current time, how to deal with future timestamp
// TODO: if timestamp > current time, how to deal with future timestamp
header.Time = new(big.Int).Add(parent.Time, new(big.Int).SetUint64(x.config.Period))
if header.Time.Int64() < time.Now().Unix() {
header.Time = big.NewInt(time.Now().Unix())
@ -273,23 +288,17 @@ func (x *XDPoS_v2) Seal(chain consensus.ChainReader, block *types.Block, stop <-
x.signLock.RUnlock()
// Bail out if we're unauthorized to sign a block
snap, err := x.snapshot(chain, number-1, header.ParentHash, nil)
if err != nil {
return nil, err
}
masternodes := x.GetMasternodes(chain, header)
if _, authorized := snap.MasterNodes[signer]; !authorized {
valid := false
for _, m := range masternodes {
if m == signer {
valid = true
break
}
}
if !valid {
return nil, utils.ErrUnauthorized
valid := false
for _, m := range masternodes {
if m == signer {
valid = true
break
}
}
if !valid {
return nil, utils.ErrUnauthorized
}
select {
case <-stop:
@ -398,8 +407,8 @@ func (x *XDPoS_v2) IsAuthorisedAddress(chain consensus.ChainReader, header *type
// Copy from v1
func (x *XDPoS_v2) GetSnapshot(chain consensus.ChainReader, header *types.Header) (*SnapshotV2, error) {
number := header.Number.Uint64()
log.Trace("get snapshot", "number", number, "hash", header.Hash())
snap, err := x.snapshot(chain, number, header.Hash(), nil)
log.Trace("get snapshot", "number", number)
snap, err := x.getSnapshot(chain, number)
if err != nil {
return nil, err
}
@ -407,84 +416,53 @@ func (x *XDPoS_v2) GetSnapshot(chain consensus.ChainReader, header *types.Header
}
// snapshot retrieves the authorization snapshot at a given point in time.
func (x *XDPoS_v2) snapshot(chain consensus.ChainReader, number uint64, hash common.Hash, parents []*types.Header) (*SnapshotV2, error) {
// Search for a SnapshotV2 in memory or on disk for checkpoints
var (
headers []*types.Header
snap *SnapshotV2
)
for snap == nil {
// If an in-memory SnapshotV2 was found, use that
if s, ok := x.recents.Get(hash); ok {
snap = s.(*SnapshotV2)
break
}
// If an on-disk checkpoint snapshot can be found, use that
// checkpoint snapshot = checkpoint - gap
if (number+x.config.Gap)%x.config.Epoch == 0 {
if s, err := loadSnapshot(x.signatures, x.db, hash); err == nil {
log.Trace("Loaded snapshot form disk", "number", number, "hash", hash)
snap = s
break
}
}
// If we're at 0 block, make a snapshot
// TODO: We may need to store snapshot at the v1 -> v2 switch block
if number == 0 {
genesis := chain.GetHeaderByNumber(0)
if err := x.VerifyHeader(chain, genesis, true); err != nil {
return nil, err
}
signers := make([]common.Address, (len(genesis.Extra)-utils.ExtraVanity-utils.ExtraSeal)/common.AddressLength)
for i := 0; i < len(signers); i++ {
copy(signers[i][:], genesis.Extra[utils.ExtraVanity+i*common.AddressLength:])
}
snap = newSnapshot(x.signatures, 0, genesis.Hash(), x.currentRound, x.highestQuorumCert, signers)
if err := storeSnapshot(snap, x.db); err != nil {
return nil, err
}
log.Trace("Stored genesis voting snapshot to disk")
break
}
// No snapshot for this header, gather the header and move backward
var header *types.Header
if len(parents) > 0 {
// If we have explicit parents, pick from there (enforced)
header = parents[len(parents)-1]
if header.Hash() != hash || header.Number.Uint64() != number {
return nil, consensus.ErrUnknownAncestor
}
parents = parents[:len(parents)-1]
} else {
// No explicit parents (or no more left), reach out to the database
header = chain.GetHeader(hash, number)
if header == nil {
log.Error("[Seal] Failed due to no header found", "hash", hash, "number", number)
return nil, consensus.ErrUnknownAncestor
}
}
headers = append(headers, header)
number, hash = number-1, header.ParentHash
func (x *XDPoS_v2) getSnapshot(chain consensus.ChainReader, number uint64) (*SnapshotV2, error) {
// checkpoint snapshot = checkpoint - gap
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
}
// Previous snapshot found, apply any pending headers on top of it
for i := 0; i < len(headers)/2; i++ {
headers[i], headers[len(headers)-1-i] = headers[len(headers)-1-i], headers[i]
}
snap, err := snap.apply(headers)
// If an on-disk checkpoint snapshot can be found, use that
snap, err := loadSnapshot(x.signatures, x.db, gapBlockHash)
if err != nil {
log.Error("Cannot find snapshot from last gap block", "err", err, "number", gapBlockNum, "hash", gapBlockHash)
return nil, err
}
x.recents.Add(snap.Hash, snap)
// If we've generated a new checkpoint snapshot, save to disk
// TODO how to save correct snapshot
if uint64(snap.Round)%x.config.Epoch == x.config.Gap {
if err = storeSnapshot(snap, x.db); err != nil {
return nil, err
}
log.Trace("Stored snapshot to disk", "round number", snap.Round, "hash", snap.Hash)
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())
masterNodes := []common.Address{}
for _, m := range ms {
masterNodes = append(masterNodes, m.Address)
}
return snap, err
x.lock.RLock()
snap := newSnapshot(number, header.Hash(), x.currentRound, x.highestQuorumCert, masterNodes)
x.lock.RUnlock()
storeSnapshot(snap, x.db)
x.snapshots.Add(snap.Hash, snap)
nm := []string{}
for _, n := range ms {
nm = append(nm, n.Address.String())
}
log.Info("New set of masternodes has been updated to snapshot", "number", snap.Number, "hash", snap.Hash, "new masternodes", nm)
return nil
}
func (x *XDPoS_v2) VerifyHeader(chain consensus.ChainReader, header *types.Header, fullVerify bool) error {

View file

@ -2,41 +2,32 @@ package engine_v2
import (
"encoding/json"
"sort"
"github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils"
"github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/ethdb"
lru "github.com/hashicorp/golang-lru"
)
// Snapshot is the state of the smart contract validator list
// The validator list is used on next epoch master nodes
// If we don't have the snapshot, then we have to trace back the gap block smart contract state which is very costly
type SnapshotV2 struct {
sigcache *lru.ARCCache // Cache of recent block signatures to speed up ecrecover
Round utils.Round `json:"round"` // Round number
Number uint64 `json:"number"` // Block number where the snapshot was created
Hash common.Hash `json:"hash"` // Block hash where the snapshot was created
// MasterNodes will get assigned on updateM1
MasterNodes map[common.Address]struct{} `json:"masterNodes"` // Set of authorized master nodes at this moment
NextEpochMasterNodes []common.Address `json:"masterNodes"` // Set of authorized master nodes at this moment for next epoch
}
// newSnapshot creates a new snapshot with the specified startup parameters. This
// method does not initialize the set of recent signers, so only ever use if for
// the genesis block.
func newSnapshot(sigcache *lru.ARCCache, number uint64, hash common.Hash, round utils.Round, qc *utils.QuorumCert, masternodes []common.Address) *SnapshotV2 {
// create new snapshot for next epoch to use
func newSnapshot(number uint64, hash common.Hash, round utils.Round, qc *utils.QuorumCert, masternodes []common.Address) *SnapshotV2 {
snap := &SnapshotV2{
sigcache: sigcache,
Round: round,
Number: number,
Hash: hash,
MasterNodes: make(map[common.Address]struct{}),
}
for _, signer := range masternodes {
snap.MasterNodes[signer] = struct{}{}
Round: round,
Number: number,
Hash: hash,
NextEpochMasterNodes: masternodes,
}
return snap
}
@ -51,7 +42,6 @@ func loadSnapshot(sigcache *lru.ARCCache, db ethdb.Database, hash common.Hash) (
if err := json.Unmarshal(blob, snap); err != nil {
return nil, err
}
snap.sigcache = sigcache
return snap, nil
}
@ -65,69 +55,11 @@ func storeSnapshot(s *SnapshotV2, db ethdb.Database) error {
return db.Put(append([]byte("XDPoS-"), s.Hash[:]...), blob)
}
// copy creates a deep copy of the SnapshotV2, though not the individual votes.
func (s *SnapshotV2) copy() *SnapshotV2 {
cpy := &SnapshotV2{
sigcache: s.sigcache,
Round: s.Round,
Number: s.Number,
Hash: s.Hash,
MasterNodes: make(map[common.Address]struct{}),
// retrieves master nodes list in map type
func (s *SnapshotV2) GetMappedMasterNodes() map[common.Address]struct{} {
ms := make(map[common.Address]struct{})
for _, n := range s.NextEpochMasterNodes {
ms[n] = struct{}{}
}
for signer := range s.MasterNodes {
cpy.MasterNodes[signer] = struct{}{}
}
return cpy
}
// apply creates a new authorization SnapshotV2 by applying the given headers to
// the original one.
// TODO: XIN-100
func (s *SnapshotV2) apply(headers []*types.Header) (*SnapshotV2, error) {
return s, nil
// Allow passing in no headers for cleaner code
// if len(headers) == 0 {
// return s, nil
// }
// // Sanity check that the headers can be applied
// for i := 0; i < len(headers)-1; i++ {
// if headers[i+1].Number.Uint64() != headers[i].Number.Uint64()+1 {
// return nil, utils.ErrInvalidHeaderOrder
// }
// }
// if headers[0].Number.Uint64() != s.Number+1 {
// return nil, utils.ErrInvalidChild
// }
// // Iterate through the headers and create a new SnapshotV2
// snap := s.copy()
// lastHeader := headers[len(headers)-1]
// snap.Number += uint64(len(headers))
// snap.Hash = lastHeader.Hash()
// extraV2 := new(utils.ExtraFields_v2)
// err := utils.DecodeBytesExtraFields(lastHeader.Extra, &extraV2)
// if err != nil {
// return nil, err
// }
// snap.Round = extraV2.Round
// return snap, nil
}
// signers retrieves the list of authorized signers in ascending order, convert into strings then use native sort lib
func (s *SnapshotV2) GetMasterNodes() []common.Address {
nodes := make([]common.Address, 0, len(s.MasterNodes))
nodeStrs := make([]string, 0, len(s.MasterNodes))
for node := range s.MasterNodes {
nodeStrs = append(nodeStrs, node.Str())
}
sort.Strings(nodeStrs)
for _, str := range nodeStrs {
nodes = append(nodes, common.StringToAddress(str))
}
return nodes
return ms
}

View file

@ -3,103 +3,28 @@ package engine_v2
import (
"fmt"
"io/ioutil"
"math/big"
"testing"
"github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils"
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
"github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/ethdb/leveldb"
"github.com/stretchr/testify/assert"
)
func TestGetMasterNodes(t *testing.T) {
masterNodes := []common.Address{
{4}, {3}, {2}, {1},
}
snap := newSnapshot(nil, 1, common.Hash{}, utils.Round(1), nil, masterNodes)
sortedNodes := snap.GetMasterNodes()
for i := range masterNodes {
if masterNodes[i] != sortedNodes[3-i] {
t.Error("should get sorted master nodes list", i, sortedNodes[i])
masterNodes := []common.Address{{0x4}, {0x3}, {0x2}, {0x1}}
snap := newSnapshot(1, common.Hash{}, utils.Round(1), nil, masterNodes)
for _, address := range masterNodes {
if _, ok := snap.GetMappedMasterNodes()[address]; !ok {
t.Error("should get master node from map", address.Hex(), snap.GetMappedMasterNodes())
return
}
}
}
func TestApplyNewSnapshot(t *testing.T) {
t.Skip("apply has been temporary commented out")
snap := newSnapshot(nil, 1, common.Hash{}, utils.Round(1), nil, nil)
extra := utils.ExtraFields_v2{
Round: 10,
QuorumCert: &utils.QuorumCert{
ProposedBlockInfo: &utils.BlockInfo{},
},
}
extraBytes, err := extra.EncodeToBytes()
assert.Nil(t, err)
headers := []*types.Header{
{Number: big.NewInt(2)},
{Number: big.NewInt(3)},
{Number: big.NewInt(4)},
{
Number: big.NewInt(5),
Extra: extraBytes,
},
}
newSnap, err := snap.apply(headers)
assert.Nil(t, err)
if newSnap.Number != 5 {
t.Error("newSnapshot number should have last header number")
}
if newSnap.Hash != headers[3].Hash() {
t.Error("newSnapshot hash should equal the last header given")
}
if newSnap.Round != 10 {
t.Error("newSnapshot round number should also have last header round number")
}
}
func TestApplyWithWrongHeader(t *testing.T) {
t.Skip("apply has been temporary commented out")
snap := newSnapshot(nil, 1, common.Hash{}, utils.Round(1), nil, nil)
headers := []*types.Header{
{Number: big.NewInt(3)},
}
_, err := snap.apply(headers)
assert.Equal(t, err, utils.ErrInvalidChild)
snap = newSnapshot(nil, 1, common.Hash{}, utils.Round(1), nil, nil)
headers = []*types.Header{
{Number: big.NewInt(2)},
{Number: big.NewInt(4)},
}
_, err = snap.apply(headers)
assert.Equal(t, err, utils.ErrInvalidHeaderOrder)
}
// Should perform deep copy
func TestCopySnapshot(t *testing.T) {
masterNodes := []common.Address{
{4}, {3}, {2}, {1},
}
snap := newSnapshot(nil, 1, common.Hash{}, utils.Round(1), nil, masterNodes)
newSnapshot := snap.copy()
if newSnapshot == snap {
t.Error("should return given different memory address")
}
for node := range snap.MasterNodes {
if _, ok := newSnapshot.MasterNodes[node]; !ok {
t.Error("snapshot masternodes should copy to new object")
}
}
}
func TestStoreLoadSnapshot(t *testing.T) {
snap := newSnapshot(nil, 1, common.Hash{0x1}, utils.Round(1), nil, nil)
snap := newSnapshot(1, common.Hash{0x1}, utils.Round(1), nil, nil)
dir, err := ioutil.TempDir("", "snapshot-test")
if err != nil {
panic(fmt.Sprintf("can't create temporary directory: %v", err))

View file

@ -602,3 +602,63 @@ func TestVoteShouldNotBeAffectedByFork(t *testing.T) {
t.Fatalf("account 3 should sit in the signer list as previos block result")
}
}
/*
V2 Consensus
*/
/*
// Pending for creating cross version blocks
func TestV2UpdateSignerListIfVotedBeforeGap(t *testing.T) {
config := params.TestXDPoSMockChainConfigWithV2EngineEpochSwitch
blockchain, backend, parentBlock, _ := PrepareXDCTestBlockChain(t, int(config.XDPoS.Epoch)+GAP-2, config)
// Insert first Block 1349
t.Logf("Inserting block with propose at 1349...")
blockCoinbaseA := "0xaaa0000000000000000000000000000000001349"
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(1349)),
ParentHash: parentBlock.Hash(),
Coinbase: common.HexToAddress(blockCoinbaseA),
}
block1349, err := insertBlockTxs(blockchain, header, []*types.Transaction{tx})
if err != nil {
t.Fatal(err)
}
parentBlock = block1349
// Now, let's mine another block to trigger the GAP block signerList update
block1350CoinbaseAddress := "0xaaa0000000000000000000000000000000001350"
merkleRoot = "46234e9cd7e85a267f7f0435b15256a794a2f6d65cc98cdbd21dcd10a01d9772"
header = &types.Header{
Root: common.HexToHash(merkleRoot),
Number: big.NewInt(int64(1350)),
ParentHash: parentBlock.Hash(),
Coinbase: common.HexToAddress(block1350CoinbaseAddress),
}
block1350, err := insertBlock(blockchain, header)
if err != nil {
t.Fatal(err)
}
signers, err := GetSnapshotSigner(blockchain, block1350.Header())
if err != nil {
t.Fatalf("Failed while trying to get signers")
}
// Now, we voted acc 1 to be in the signerList, which will kick out acc3 because it has less funds
if signers[acc3Addr.Hex()] == true {
debugMessage(backend, signers, t)
t.Fatalf("account 3 should NOT sit in the signer list")
}
if signers[acc1Addr.Hex()] != true {
debugMessage(backend, signers, t)
t.Fatalf("account 1 should sit in the signer list")
}
}
*/

View file

@ -0,0 +1,48 @@
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
adaptor.YourTurn(blockchain, block900.Header(), common.HexToAddress("xdc0278C350152e15fa6FFC712a5A73D704Ce73E2E1"))
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

@ -133,6 +133,8 @@ var (
TestXDPoSMockChainConfig = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, new(EthashConfig), nil, &XDPoSConfig{Epoch: 900, Gap: 450, SkipValidation: true, V2: *TestXDPoSV2Config}}
// XDPoS config with v2 engine after block 10
TestXDPoSMockChainConfigWithV2Engine = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, new(EthashConfig), nil, &XDPoSConfig{Epoch: 900, Gap: 450, SkipValidation: true, XDPoSV2Block: big.NewInt(10), V2: *TestXDPoSV2Config}}
// XDPoS config with v2 engine after block 901
TestXDPoSMockChainConfigWithV2EngineEpochSwitch = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, new(EthashConfig), nil, &XDPoSConfig{Epoch: 900, Gap: 450, SkipValidation: true, XDPoSV2Block: big.NewInt(900), V2: *TestXDPoSV2Config}}
TestChainConfig = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, new(EthashConfig), nil, nil}
TestRules = TestChainConfig.Rules(new(big.Int))