mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-06-19 21:31:37 +00:00
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:
parent
e72d4866df
commit
646042a651
7 changed files with 241 additions and 285 deletions
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
|
|
|||
48
consensus/tests/initialize_v2_test.go
Normal file
48
consensus/tests/initialize_v2_test.go
Normal 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())
|
||||
}
|
||||
}
|
||||
|
|
@ -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))
|
||||
|
|
|
|||
Loading…
Reference in a new issue