Xin 113 is epoch switch (#37)

* add isEpochSwitch function and refactor utils

* fix broken first v2 epoch switch block

* use adaptor epoch switch function to determine v1 v2 epoch swtich block

* add test for the GetMasternodesByNumber and GetCurrentEpochSwitchBlock function

* add v2 test for isAuthroisedAddress

* Use GetCurrentEpochSwitchBlock in findNearestSignedBlock api
This commit is contained in:
Jerome 2022-01-14 21:38:38 +11:00 committed by GitHub
parent 3ac908be8d
commit f8d3f9f8c6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 589 additions and 349 deletions

View file

@ -35,8 +35,13 @@ import (
lru "github.com/hashicorp/golang-lru"
)
func SigHash(header *types.Header) (hash common.Hash) {
return utils.SigHash(header)
func (x *XDPoS) SigHash(header *types.Header) (hash common.Hash) {
switch x.config.BlockConsensusVersion(header.Number) {
case params.ConsensusEngineVersion2:
return x.EngineV2.SignHash(header)
default: // Default "v1"
return x.EngineV1.SigHash(header)
}
}
// XDPoS is the delegated-proof-of-stake consensus engine proposed to support the
@ -240,12 +245,12 @@ func (x *XDPoS) GetPeriod() uint64 {
return x.config.Period
}
func (x *XDPoS) IsAuthorisedAddress(header *types.Header, chain consensus.ChainReader, address common.Address) bool {
func (x *XDPoS) IsAuthorisedAddress(chain consensus.ChainReader, header *types.Header, address common.Address) bool {
switch x.config.BlockConsensusVersion(header.Number) {
case params.ConsensusEngineVersion2:
return x.EngineV2.IsAuthorisedAddress(header, chain, address)
return x.EngineV2.IsAuthorisedAddress(chain, header, address)
default: // Default "v1"
return x.EngineV1.IsAuthorisedAddress(header, chain, address)
return x.EngineV1.IsAuthorisedAddress(chain, header, address)
}
}
@ -258,6 +263,20 @@ func (x *XDPoS) GetMasternodes(chain consensus.ChainReader, header *types.Header
}
}
func (x *XDPoS) GetMasternodesByNumber(chain consensus.ChainReader, blockNumber uint64) []common.Address {
blockHeader := chain.GetHeaderByNumber(blockNumber)
if blockHeader == nil {
log.Error("[GetMasternodesByNumber] Unable to find block", "Num", blockNumber)
return []common.Address{}
}
switch x.config.BlockConsensusVersion(big.NewInt(int64(blockNumber))) {
case params.ConsensusEngineVersion2:
return x.EngineV2.GetMasternodes(chain, blockHeader)
default: // Default "v1"
return x.EngineV1.GetMasternodes(chain, blockHeader)
}
}
func (x *XDPoS) YourTurn(chain consensus.ChainReader, parent *types.Header, signer common.Address) (int, int, int, bool, error) {
switch x.config.BlockConsensusVersion(parent.Number) {
case params.ConsensusEngineVersion2:
@ -302,30 +321,34 @@ func (x *XDPoS) RecoverValidator(header *types.Header) (common.Address, error) {
}
// Get master nodes over extra data of previous checkpoint block.
func (x *XDPoS) GetMasternodesFromCheckpointHeader(preCheckpointHeader *types.Header, n, e uint64) []common.Address {
switch x.config.BlockConsensusVersion(preCheckpointHeader.Number) {
func (x *XDPoS) GetMasternodesFromCheckpointHeader(checkpointHeader *types.Header) []common.Address {
switch x.config.BlockConsensusVersion(checkpointHeader.Number) {
case params.ConsensusEngineVersion2:
return x.EngineV2.GetMasternodesFromEpochSwitchHeader(preCheckpointHeader)
return x.EngineV2.GetMasternodesFromEpochSwitchHeader(checkpointHeader)
default: // Default "v1"
return x.EngineV1.GetMasternodesFromCheckpointHeader(preCheckpointHeader, n, e)
return x.EngineV1.GetMasternodesFromCheckpointHeader(checkpointHeader)
}
}
// Check is epoch switch (checkpoint) block
func (x *XDPoS) IsEpochSwitch(header *types.Header) bool {
func (x *XDPoS) IsEpochSwitch(header *types.Header) (bool, uint64, error) {
switch x.config.BlockConsensusVersion(header.Number) {
case params.ConsensusEngineVersion2:
b, _, err := x.EngineV2.IsEpochSwitch(header)
if err != nil {
log.Error("[IsEpochSwitch] Adaptor v2 IsEpochSwitch has error", "err", err)
return false
}
return b
return x.EngineV2.IsEpochSwitch(header)
default: // Default "v1"
return x.EngineV1.IsEpochSwitch(header)
}
}
func (x *XDPoS) GetCurrentEpochSwitchBlock(chain consensus.ChainReader, blockNumber *big.Int) (uint64, uint64, error) {
switch x.config.BlockConsensusVersion(blockNumber) {
case params.ConsensusEngineVersion2:
return x.EngineV2.GetCurrentEpochSwitchBlock(chain, blockNumber)
default: // Default "v1"
return x.EngineV1.GetCurrentEpochSwitchBlock(blockNumber)
}
}
// Same DB across all consensus engines
func (x *XDPoS) GetDb() ethdb.Database {
return x.db

View file

@ -53,6 +53,23 @@ type XDPoS_v1 struct {
HookGetSignersFromContract func(blockHash common.Hash) ([]common.Address, error)
}
/* V1 Block
SignerFn is a signer callback function to request a hash to be signed by a
backing account.
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_v1) SigHash(header *types.Header) (hash common.Hash) {
return sigHash(header)
}
// New creates a XDPoS delegated-proof-of-stake consensus engine with the initial
// signers set to the ones provided by the user.
func New(config *params.XDPoSConfig, db ethdb.Database) *XDPoS_v1 {
@ -81,7 +98,7 @@ func New(config *params.XDPoSConfig, db ethdb.Database) *XDPoS_v1 {
// Author implements consensus.Engine, returning the Ethereum address recovered
// from the signature in the header's extra-data section.
func (x *XDPoS_v1) Author(header *types.Header) (common.Address, error) {
return utils.Ecrecover(header, x.signatures)
return ecrecover(header, x.signatures)
}
// VerifyHeader checks whether a header conforms to the consensus rules.
@ -296,7 +313,7 @@ func (x *XDPoS_v1) checkSignersOnCheckpoint(chain consensus.ChainReader, header
return nil
}
func (x *XDPoS_v1) IsAuthorisedAddress(header *types.Header, chain consensus.ChainReader, address common.Address) bool {
func (x *XDPoS_v1) IsAuthorisedAddress(chain consensus.ChainReader, header *types.Header, address common.Address) bool {
snap, err := x.GetSnapshot(chain, header)
if err != nil {
log.Error("[IsAuthorisedAddress] Can't get snapshot with at ", "number", header.Number, "hash", header.Hash().Hex(), "err", err)
@ -330,36 +347,37 @@ func (x *XDPoS_v1) StoreSnapshot(snap *SnapshotV1) error {
return snap.store(x.db)
}
func position(list []common.Address, x common.Address) int {
for i, item := range list {
if item == x {
return i
}
}
return -1
}
func (x *XDPoS_v1) GetMasternodes(chain consensus.ChainReader, header *types.Header) []common.Address {
n := header.Number.Uint64()
e := x.config.Epoch
switch {
case n%e == 0:
return x.GetMasternodesFromCheckpointHeader(header, n, e)
return x.GetMasternodesFromCheckpointHeader(header)
case n%e != 0:
h := chain.GetHeaderByNumber(n - (n % e))
return x.GetMasternodesFromCheckpointHeader(h, n, e)
if h == nil {
log.Warn("[GetMasternodes v1] epoch switch block header nil", "BlockNum", n)
}
return x.GetMasternodesFromCheckpointHeader(h)
default:
return []common.Address{}
}
}
func (x *XDPoS_v1) GetCurrentEpochSwitchBlock(blockNumber *big.Int) (uint64, uint64, error) {
currentBlockNum := blockNumber.Uint64()
currentCheckpointNumber := currentBlockNum - currentBlockNum%x.config.Epoch
epochNumber := currentBlockNum / x.config.Epoch
return currentCheckpointNumber, epochNumber, nil
}
func (x *XDPoS_v1) GetPeriod() uint64 { return x.config.Period }
func whoIsCreator(snap *SnapshotV1, header *types.Header) (common.Address, error) {
func (x *XDPoS_v1) whoIsCreator(snap *SnapshotV1, header *types.Header) (common.Address, error) {
if header.Number.Uint64() == 0 {
return common.Address{}, errors.New("Don't take block 0")
}
m, err := utils.Ecrecover(header, snap.sigcache)
m, err := ecrecover(header, snap.sigcache)
if err != nil {
return common.Address{}, err
}
@ -390,13 +408,13 @@ func (x *XDPoS_v1) YourTurn(chain consensus.ChainReader, parent *types.Header, s
// masternode[0] has chance to create block 1
preIndex := -1
if parent.Number.Uint64() != 0 {
pre, err = whoIsCreator(snap, parent)
pre, err = x.whoIsCreator(snap, parent)
if err != nil {
return 0, 0, 0, false, err
}
preIndex = position(masternodes, pre)
preIndex = utils.Position(masternodes, pre)
}
curIndex := position(masternodes, signer)
curIndex := utils.Position(masternodes, signer)
if signer == x.signer {
log.Debug("Masternodes cycle info", "number of masternodes", len(masternodes), "previous", pre, "position", preIndex, "current", signer, "position", curIndex)
}
@ -524,7 +542,7 @@ func (x *XDPoS_v1) verifySeal(chain consensus.ChainReader, header *types.Header,
}
// Resolve the authorization key and check against signers
creator, err := utils.Ecrecover(header, x.signatures)
creator, err := ecrecover(header, x.signatures)
if err != nil {
return err
}
@ -618,7 +636,7 @@ func (x *XDPoS_v1) GetValidator(creator common.Address, chain consensus.ChainRea
return common.Address{}, fmt.Errorf("couldn't find checkpoint header")
}
}
m, err := utils.GetM1M2FromCheckpointHeader(cpHeader, header, chain.Config())
m, err := getM1M2FromCheckpointHeader(cpHeader, header, chain.Config())
if err != nil {
return common.Address{}, err
}
@ -851,7 +869,7 @@ func (x *XDPoS_v1) Seal(chain consensus.ChainReader, block *types.Block, stop <-
default:
}
// Sign all the things!
sighash, err := signFn(accounts.Account{Address: signer}, utils.SigHash(header).Bytes())
sighash, err := signFn(accounts.Account{Address: signer}, x.SigHash(header).Bytes())
if err != nil {
return nil, err
}
@ -886,7 +904,7 @@ func (x *XDPoS_v1) calcDifficulty(chain consensus.ChainReader, parent *types.Hea
}
func (x *XDPoS_v1) RecoverSigner(header *types.Header) (common.Address, error) {
return utils.Ecrecover(header, x.signatures)
return ecrecover(header, x.signatures)
}
func (x *XDPoS_v1) RecoverValidator(header *types.Header) (common.Address, error) {
@ -901,7 +919,7 @@ func (x *XDPoS_v1) RecoverValidator(header *types.Header) (common.Address, error
return common.Address{}, consensus.ErrFailValidatorSignature
}
// Recover the public key and the Ethereum address
pubkey, err := crypto.Ecrecover(utils.SigHash(header).Bytes(), header.Validator)
pubkey, err := crypto.Ecrecover(x.SigHash(header).Bytes(), header.Validator)
if err != nil {
return common.Address{}, err
}
@ -912,18 +930,13 @@ func (x *XDPoS_v1) RecoverValidator(header *types.Header) (common.Address, error
return signer, nil
}
// Get master nodes over extra data of previous checkpoint block.
func (x *XDPoS_v1) GetMasternodesFromCheckpointHeader(preCheckpointHeader *types.Header, n, e uint64) []common.Address {
if preCheckpointHeader == nil {
log.Info("Previous checkpoint's header is empty", "block number", n, "epoch", e)
// Get master nodes over extra data of checkpoint block.
func (x *XDPoS_v1) GetMasternodesFromCheckpointHeader(checkpointHeader *types.Header) []common.Address {
if checkpointHeader == nil {
log.Warn("Checkpoint's header is empty", "Header", checkpointHeader)
return []common.Address{}
}
masternodes := make([]common.Address, (len(preCheckpointHeader.Extra)-utils.ExtraVanity-utils.ExtraSeal)/common.AddressLength)
for i := 0; i < len(masternodes); i++ {
copy(masternodes[i][:], preCheckpointHeader.Extra[utils.ExtraVanity+i*common.AddressLength:])
}
return masternodes
return decodeMasternodesFromHeaderExtra(checkpointHeader)
}
func (x *XDPoS_v1) GetDb() ethdb.Database {
@ -945,15 +958,6 @@ func removePenaltiesFromBlock(chain consensus.ChainReader, masternodes []common.
return masternodes
}
// Get masternodes address from checkpoint Header.
func GetMasternodesFromCheckpointHeader(checkpointHeader *types.Header) []common.Address {
masternodes := make([]common.Address, (len(checkpointHeader.Extra)-utils.ExtraVanity-utils.ExtraSeal)/common.AddressLength)
for i := 0; i < len(masternodes); i++ {
copy(masternodes[i][:], checkpointHeader.Extra[utils.ExtraVanity+i*common.AddressLength:])
}
return masternodes
}
func (x *XDPoS_v1) getSignersFromContract(chain consensus.ChainReader, checkpointHeader *types.Header) ([]common.Address, error) {
startGapBlockHeader := checkpointHeader
number := checkpointHeader.Number.Uint64()
@ -990,6 +994,8 @@ func NewFaker(db ethdb.Database, config *params.XDPoSConfig) *XDPoS_v1 {
}
// Epoch Switch is also known as checkpoint in v1
func (x *XDPoS_v1) IsEpochSwitch(header *types.Header) bool {
return (header.Number.Uint64() % x.config.Epoch) == 0
func (x *XDPoS_v1) IsEpochSwitch(header *types.Header) (bool, uint64, error) {
epochNumber := header.Number.Uint64() / x.config.Epoch
blockNumInEpoch := header.Number.Uint64() % x.config.Epoch
return blockNumInEpoch == 0, epochNumber, nil
}

View file

@ -187,7 +187,7 @@ func (s *SnapshotV1) apply(headers []*types.Header) (*SnapshotV1, error) {
delete(snap.Recents, number-limit)
}
// Resolve the authorization key and check against signers
signer, err := utils.Ecrecover(header, s.sigcache)
signer, err := ecrecover(header, s.sigcache)
if err != nil {
return nil, err
}

View file

@ -0,0 +1,112 @@
package engine_v1
import (
"errors"
"github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils"
"github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/crypto"
"github.com/XinFinOrg/XDPoSChain/crypto/sha3"
"github.com/XinFinOrg/XDPoSChain/log"
"github.com/XinFinOrg/XDPoSChain/params"
"github.com/XinFinOrg/XDPoSChain/rlp"
lru "github.com/hashicorp/golang-lru"
)
// Get masternodes address from checkpoint Header.
func decodeMasternodesFromHeaderExtra(checkpointHeader *types.Header) []common.Address {
masternodes := make([]common.Address, (len(checkpointHeader.Extra)-utils.ExtraVanity-utils.ExtraSeal)/common.AddressLength)
for i := 0; i < len(masternodes); i++ {
copy(masternodes[i][:], checkpointHeader.Extra[utils.ExtraVanity+i*common.AddressLength:])
}
return masternodes
}
// Get m2 list from checkpoint block.
func getM1M2FromCheckpointHeader(checkpointHeader *types.Header, currentHeader *types.Header, config *params.ChainConfig) (map[common.Address]common.Address, error) {
if checkpointHeader.Number.Uint64()%common.EpocBlockRandomize != 0 {
return nil, errors.New("This block is not checkpoint block epoc.")
}
// Get signers from this block.
masternodes := decodeMasternodesFromHeaderExtra(checkpointHeader)
validators := utils.ExtractValidatorsFromBytes(checkpointHeader.Validators)
m1m2, _, err := getM1M2(masternodes, validators, currentHeader, config)
if err != nil {
return map[common.Address]common.Address{}, err
}
return m1m2, nil
}
func getM1M2(masternodes []common.Address, validators []int64, currentHeader *types.Header, config *params.ChainConfig) (map[common.Address]common.Address, uint64, error) {
m1m2 := map[common.Address]common.Address{}
maxMNs := len(masternodes)
moveM2 := uint64(0)
if len(validators) < maxMNs {
return nil, moveM2, errors.New("len(m2) is less than len(m1)")
}
if maxMNs > 0 {
isForked := config.IsTIPRandomize(currentHeader.Number)
if isForked {
moveM2 = ((currentHeader.Number.Uint64() % config.XDPoS.Epoch) / uint64(maxMNs)) % uint64(maxMNs)
}
for i, m1 := range masternodes {
m2Index := uint64(validators[i] % int64(maxMNs))
m2Index = (m2Index + moveM2) % uint64(maxMNs)
m1m2[m1] = masternodes[m2Index]
}
}
return m1m2, moveM2, nil
}
func sigHash(header *types.Header) (hash common.Hash) {
hasher := sha3.NewKeccak256()
err := rlp.Encode(hasher, []interface{}{
header.ParentHash,
header.UncleHash,
header.Coinbase,
header.Root,
header.TxHash,
header.ReceiptHash,
header.Bloom,
header.Difficulty,
header.Number,
header.GasLimit,
header.GasUsed,
header.Time,
header.Extra[:len(header.Extra)-65], // Yes, this will panic if extra is too short
header.MixDigest,
header.Nonce,
})
if err != nil {
log.Debug("Fail to encode", err)
}
hasher.Sum(hash[:0])
return hash
}
// ecrecover extracts the Ethereum account address from a signed header.
func ecrecover(header *types.Header, sigcache *lru.ARCCache) (common.Address, error) {
// If the signature's already cached, return that
hash := header.Hash()
if address, known := sigcache.Get(hash); known {
return address.(common.Address), nil
}
// Retrieve the signature from the header extra-data
if len(header.Extra) < utils.ExtraSeal {
return common.Address{}, utils.ErrMissingSignature
}
signature := header.Extra[len(header.Extra)-utils.ExtraSeal:]
// Recover the public key and the Ethereum address
pubkey, err := crypto.Ecrecover(sigHash(header).Bytes(), signature)
if err != nil {
return common.Address{}, err
}
var signer common.Address
copy(signer[:], crypto.Keccak256(pubkey[1:])[12:])
sigcache.Add(hash, signer)
return signer, nil
}

View file

@ -0,0 +1,49 @@
package engine_v1
import (
"fmt"
"math/big"
"testing"
"github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/params"
)
func TestGetM1M2FromCheckpointHeader(t *testing.T) {
masternodes := []common.Address{
common.StringToAddress("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"),
common.StringToAddress("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"),
common.StringToAddress("cccccccccccccccccccccccccccccccccccccccc"),
}
validators := []int64{
2,
1,
0,
}
epoch := uint64(900)
config := &params.ChainConfig{
XDPoS: &params.XDPoSConfig{
Epoch: uint64(epoch),
},
}
testMoveM2 := []uint64{0, 0, 0, 1, 1, 1, 2, 2, 2, 0, 0, 0, 1, 1, 1, 2, 2, 2}
//try from block 3410001 to 3410018
for i := uint64(3464001); i <= 3464018; i++ {
currentNumber := int64(i)
currentHeader := &types.Header{
Number: big.NewInt(currentNumber),
}
m1m2, moveM2, err := getM1M2(masternodes, validators, currentHeader, config)
if err != nil {
t.Error("can't get m1m2", "err", err)
}
fmt.Printf("block: %v, moveM2: %v\n", currentHeader.Number.Int64(), moveM2)
for _, k := range masternodes {
fmt.Printf("m1: %v - m2: %v\n", k.Str(), m1m2[k].Str())
}
if moveM2 != testMoveM2[i-3464001] {
t.Error("wrong moveM2", "currentNumber", currentNumber, "want", testMoveM2[i-3464001], "have", moveM2)
}
}
}

View file

@ -98,6 +98,23 @@ func New(config *params.XDPoSConfig, db ethdb.Database) *XDPoS_v2 {
return engine
}
/* V2 Block
SignerFn is a signer callback function to request a hash to be signed by a
backing account.
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)
}
// 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 {
@ -146,7 +163,12 @@ func (x *XDPoS_v2) Prepare(chain consensus.ChainReader, header *types.Header) er
log.Debug("CalcDifficulty ", "number", header.Number, "difficulty", header.Difficulty)
// TODO: previous round should sit on previous Epoch and x.currentRound should >= Epoch number
if number%x.config.Epoch == 0 {
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.snapshot(chain, number-1, header.ParentHash, nil)
if err != nil {
return err
@ -222,7 +244,7 @@ func (x *XDPoS_v2) Authorize(signer common.Address, signFn clique.SignerFn) {
}
func (x *XDPoS_v2) Author(header *types.Header) (common.Address, error) {
return utils.EcrecoverV2(header, x.signatures)
return ecrecover(header, x.signatures)
}
// Seal implements consensus.Engine, attempting to create a sealed block using
@ -237,7 +259,11 @@ func (x *XDPoS_v2) Seal(chain consensus.ChainReader, block *types.Block, stop <-
}
// For 0-period chains, refuse to seal empty blocks (no reward but would spin sealing)
// checkpoint blocks have no tx
if x.config.Period == 0 && len(block.Transactions()) == 0 && number%x.config.Epoch != 0 {
isEpochSwitch, _, err := x.IsEpochSwitch(header)
if err != nil {
log.Error("[Seal] Error while checking whether header is a epoch switch during sealing", "Header", header)
}
if x.config.Period == 0 && len(block.Transactions()) == 0 && !isEpochSwitch {
return nil, utils.ErrWaitTransactions
}
// Don't hold the signer fields for the entire sealing procedure
@ -271,7 +297,7 @@ func (x *XDPoS_v2) Seal(chain consensus.ChainReader, block *types.Block, stop <-
}
// Sign all the things!
signature, err := signFn(accounts.Account{Address: signer}, utils.SigHashV2(header).Bytes())
signature, err := signFn(accounts.Account{Address: signer}, sigHash(header).Bytes())
if err != nil {
return nil, err
}
@ -327,7 +353,7 @@ func (x *XDPoS_v2) YourTurn(chain consensus.ChainReader, parent *types.Header, s
return len(masternodes), preIndex, curIndex, false, nil
}
func (x *XDPoS_v2) IsAuthorisedAddress(header *types.Header, chain consensus.ChainReader, address common.Address) bool {
func (x *XDPoS_v2) IsAuthorisedAddress(chain consensus.ChainReader, header *types.Header, address common.Address) bool {
var extraField utils.ExtraFields_v2
err := utils.DecodeBytesExtraFields(header.Extra, &extraField)
if err != nil {
@ -355,7 +381,7 @@ func whoIsCreator(snap *SnapshotV2, header *types.Header) (common.Address, error
if header.Number.Uint64() == 0 {
return common.Address{}, errors.New("Don't take block 0")
}
m, err := utils.EcrecoverV2(header, snap.sigcache)
m, err := ecrecover(header, snap.sigcache)
if err != nil {
return common.Address{}, err
}
@ -1066,13 +1092,14 @@ 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)
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 {
log.Info("[IsEpochSwitch] true, parent equals XDPoSV2Block", "round", round, "number", header.Number.Uint64(), "hash", header.Hash())
return true, x.config.XDPoSV2Block.Uint64()/x.config.Epoch + uint64(round)/x.config.Epoch, nil
return true, epochNum, nil
}
log.Info("[IsEpochSwitch]", "parent round", parentRound, "round", round, "number", header.Number.Uint64(), "hash", header.Hash())
return parentRound < epochStart, x.config.XDPoSV2Block.Uint64()/x.config.Epoch + uint64(round)/x.config.Epoch, nil
return parentRound < epochStart, epochNum, nil
}
// Given header and its hash, get epoch switch info from the epoch switch block of that epoch,
@ -1133,3 +1160,16 @@ func (x *XDPoS_v2) GetMasternodes(chain consensus.ChainReader, header *types.Hea
}
return epochSwitchInfo.Masternodes
}
func (x *XDPoS_v2) GetCurrentEpochSwitchBlock(chain consensus.ChainReader, blockNum *big.Int) (uint64, uint64, error) {
header := chain.GetHeaderByNumber(blockNum.Uint64())
epochSwitchInfo, err := x.getEpochSwitchInfo(chain, header, header.Hash())
if err != nil {
log.Error("[GetCurrentEpochSwitchBlock] Fail to get epoch switch info", "Num", header.Number, "Hash", header.Hash())
return 0, 0, err
}
currentCheckpointNumber := epochSwitchInfo.EpochSwitchBlockInfo.Number.Uint64()
epochNum := x.config.XDPoSV2Block.Uint64()/x.config.Epoch + uint64(epochSwitchInfo.EpochSwitchBlockInfo.Round)/x.config.Epoch
return currentCheckpointNumber, epochNum, nil
}

View file

@ -0,0 +1,59 @@
package engine_v2
import (
"github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/crypto"
"github.com/XinFinOrg/XDPoSChain/crypto/sha3"
"github.com/XinFinOrg/XDPoSChain/log"
"github.com/XinFinOrg/XDPoSChain/rlp"
lru "github.com/hashicorp/golang-lru"
)
func sigHash(header *types.Header) (hash common.Hash) {
hasher := sha3.NewKeccak256()
err := rlp.Encode(hasher, []interface{}{
header.ParentHash,
header.UncleHash,
header.Coinbase,
header.Root,
header.TxHash,
header.ReceiptHash,
header.Bloom,
header.Difficulty,
header.Number,
header.GasLimit,
header.GasUsed,
header.Time,
header.Extra,
header.MixDigest,
header.Nonce,
header.Validators,
header.Penalties,
})
if err != nil {
log.Debug("Fail to encode", err)
}
hasher.Sum(hash[:0])
return hash
}
func ecrecover(header *types.Header, sigcache *lru.ARCCache) (common.Address, error) {
// If the signature's already cached, return that
hash := header.Hash()
if address, known := sigcache.Get(hash); known {
return address.(common.Address), nil
}
// Recover the public key and the Ethereum address
pubkey, err := crypto.Ecrecover(sigHash(header).Bytes(), header.Validator)
if err != nil {
return common.Address{}, err
}
var signer common.Address
copy(signer[:], crypto.Keccak256(pubkey[1:])[12:])
sigcache.Add(hash, signer)
return signer, nil
}

View file

@ -2,20 +2,14 @@ package utils
import (
"bytes"
"errors"
"fmt"
"reflect"
"sort"
"strconv"
"github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/crypto"
"github.com/XinFinOrg/XDPoSChain/crypto/sha3"
"github.com/XinFinOrg/XDPoSChain/log"
"github.com/XinFinOrg/XDPoSChain/params"
"github.com/XinFinOrg/XDPoSChain/rlp"
lru "github.com/hashicorp/golang-lru"
)
func Position(list []common.Address, x common.Address) int {
@ -55,51 +49,6 @@ func ExtractValidatorsFromBytes(byteValidators []byte) []int64 {
return validators
}
// Get masternodes address from checkpoint Header.
func GetMasternodesFromCheckpointHeader(checkpointHeader *types.Header) []common.Address {
masternodes := make([]common.Address, (len(checkpointHeader.Extra)-ExtraVanity-ExtraSeal)/common.AddressLength)
for i := 0; i < len(masternodes); i++ {
copy(masternodes[i][:], checkpointHeader.Extra[ExtraVanity+i*common.AddressLength:])
}
return masternodes
}
// Get m2 list from checkpoint block.
func GetM1M2FromCheckpointHeader(checkpointHeader *types.Header, currentHeader *types.Header, config *params.ChainConfig) (map[common.Address]common.Address, error) {
if checkpointHeader.Number.Uint64()%common.EpocBlockRandomize != 0 {
return nil, errors.New("This block is not checkpoint block epoc.")
}
// Get signers from this block.
masternodes := GetMasternodesFromCheckpointHeader(checkpointHeader)
validators := ExtractValidatorsFromBytes(checkpointHeader.Validators)
m1m2, _, err := GetM1M2(masternodes, validators, currentHeader, config)
if err != nil {
return map[common.Address]common.Address{}, err
}
return m1m2, nil
}
func GetM1M2(masternodes []common.Address, validators []int64, currentHeader *types.Header, config *params.ChainConfig) (map[common.Address]common.Address, uint64, error) {
m1m2 := map[common.Address]common.Address{}
maxMNs := len(masternodes)
moveM2 := uint64(0)
if len(validators) < maxMNs {
return nil, moveM2, errors.New("len(m2) is less than len(m1)")
}
if maxMNs > 0 {
isForked := config.IsTIPRandomize(currentHeader.Number)
if isForked {
moveM2 = ((currentHeader.Number.Uint64() % config.XDPoS.Epoch) / uint64(maxMNs)) % uint64(maxMNs)
}
for i, m1 := range masternodes {
m2Index := uint64(validators[i] % int64(maxMNs))
m2Index = (m2Index + moveM2) % uint64(maxMNs)
m1m2[m1] = masternodes[m2Index]
}
}
return m1m2, moveM2, nil
}
// compare 2 signers lists
// return true if they are same elements, otherwise return false
func CompareSignersLists(list1 []common.Address, list2 []common.Address) bool {
@ -115,73 +64,6 @@ func CompareSignersLists(list1 []common.Address, list2 []common.Address) bool {
return reflect.DeepEqual(list1, list2)
}
// SignerFn is a signer callback function to request a hash to be signed by a
// backing account.
//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 SigHash(header *types.Header) (hash common.Hash) {
hasher := sha3.NewKeccak256()
err := rlp.Encode(hasher, []interface{}{
header.ParentHash,
header.UncleHash,
header.Coinbase,
header.Root,
header.TxHash,
header.ReceiptHash,
header.Bloom,
header.Difficulty,
header.Number,
header.GasLimit,
header.GasUsed,
header.Time,
header.Extra[:len(header.Extra)-65], // Yes, this will panic if extra is too short
header.MixDigest,
header.Nonce,
})
if err != nil {
log.Debug("Fail to encode", err)
}
hasher.Sum(hash[:0])
return hash
}
func SigHashV2(header *types.Header) (hash common.Hash) {
hasher := sha3.NewKeccak256()
err := rlp.Encode(hasher, []interface{}{
header.ParentHash,
header.UncleHash,
header.Coinbase,
header.Root,
header.TxHash,
header.ReceiptHash,
header.Bloom,
header.Difficulty,
header.Number,
header.GasLimit,
header.GasUsed,
header.Time,
header.Extra,
header.MixDigest,
header.Nonce,
header.Validators,
header.Penalties,
})
if err != nil {
log.Debug("Fail to encode", err)
}
hasher.Sum(hash[:0])
return hash
}
// Decode extra fields for consensus version >= 2 (XDPoS 2.0 and future versions)
func DecodeBytesExtraFields(b []byte, val interface{}) error {
if len(b) == 0 {
@ -196,47 +78,3 @@ func DecodeBytesExtraFields(b []byte, val interface{}) error {
return fmt.Errorf("consensus version %d is not defined", b[0])
}
}
// ecrecover extracts the Ethereum account address from a signed header.
func Ecrecover(header *types.Header, sigcache *lru.ARCCache) (common.Address, error) {
// If the signature's already cached, return that
hash := header.Hash()
if address, known := sigcache.Get(hash); known {
return address.(common.Address), nil
}
// Retrieve the signature from the header extra-data
if len(header.Extra) < ExtraSeal {
return common.Address{}, ErrMissingSignature
}
signature := header.Extra[len(header.Extra)-ExtraSeal:]
// Recover the public key and the Ethereum address
pubkey, err := crypto.Ecrecover(SigHash(header).Bytes(), signature)
if err != nil {
return common.Address{}, err
}
var signer common.Address
copy(signer[:], crypto.Keccak256(pubkey[1:])[12:])
sigcache.Add(hash, signer)
return signer, nil
}
func EcrecoverV2(header *types.Header, sigcache *lru.ARCCache) (common.Address, error) {
// If the signature's already cached, return that
hash := header.Hash()
if address, known := sigcache.Get(hash); known {
return address.(common.Address), nil
}
// Recover the public key and the Ethereum address
pubkey, err := crypto.Ecrecover(SigHashV2(header).Bytes(), header.Validator)
if err != nil {
return common.Address{}, err
}
var signer common.Address
copy(signer[:], crypto.Keccak256(pubkey[1:])[12:])
sigcache.Add(hash, signer)
return signer, nil
}

View file

@ -1,53 +1,11 @@
package utils
import (
"fmt"
"math/big"
"testing"
"github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/params"
)
func TestGetM1M2FromCheckpointHeader(t *testing.T) {
masternodes := []common.Address{
common.StringToAddress("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"),
common.StringToAddress("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"),
common.StringToAddress("cccccccccccccccccccccccccccccccccccccccc"),
}
validators := []int64{
2,
1,
0,
}
epoch := uint64(900)
config := &params.ChainConfig{
XDPoS: &params.XDPoSConfig{
Epoch: uint64(epoch),
},
}
testMoveM2 := []uint64{0, 0, 0, 1, 1, 1, 2, 2, 2, 0, 0, 0, 1, 1, 1, 2, 2, 2}
//try from block 3410001 to 3410018
for i := uint64(3464001); i <= 3464018; i++ {
currentNumber := int64(i)
currentHeader := &types.Header{
Number: big.NewInt(currentNumber),
}
m1m2, moveM2, err := GetM1M2(masternodes, validators, currentHeader, config)
if err != nil {
t.Error("can't get m1m2", "err", err)
}
fmt.Printf("block: %v, moveM2: %v\n", currentHeader.Number.Int64(), moveM2)
for _, k := range masternodes {
fmt.Printf("m1: %v - m2: %v\n", k.Str(), m1m2[k].Str())
}
if moveM2 != testMoveM2[i-3464001] {
t.Error("wrong moveM2", "currentNumber", currentNumber, "want", testMoveM2[i-3464001], "have", moveM2)
}
}
}
func TestCompareSignersLists(t *testing.T) {
list1 := []common.Address{
common.StringToAddress("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"),
@ -81,4 +39,4 @@ func TestCompareSignersLists(t *testing.T) {
if CompareSignersLists([]common.Address{common.StringToAddress("aaaaaaaaaaaaaaaa")}, []common.Address{common.StringToAddress("cccccccccccccccccccccccccccccccccccccccc")}) {
t.Error("Failed with list has only one signer")
}
}
}

View file

@ -40,7 +40,7 @@ func TestAdaptorShouldGetAuthorForDifferentConsensusVersion(t *testing.T) {
ParentHash: currentBlock.Hash(),
Coinbase: common.HexToAddress(blockCoinBase),
}
err := generateSignature(backend, header)
err := generateSignature(backend, adaptor, header)
if err != nil {
t.Fatal(err)
}
@ -66,11 +66,11 @@ func TestAdaptorGetMasternodesFromCheckpointHeader(t *testing.T) {
adaptor := blockchain.Engine().(*XDPoS.XDPoS)
headerV1 := currentBlock.Header()
headerV1.Extra = common.Hex2Bytes("d7830100018358444388676f312e31352e38856c696e757800000000000000000278c350152e15fa6ffc712a5a73d704ce73e2e103d9e17ae3ff2c6712e44e25b09ac5ee91f6c9ff065551f0dcac6f00cae11192d462db709be3758ccef312ee5eea8d7bad5374c6a652150515d744508b61c1a4deb4e4e7bf057e4e3824c11fd2569bcb77a52905cda63b5a58507910bed335e4c9d87ae0ecdfafd400")
masternodesV1 := adaptor.GetMasternodesFromCheckpointHeader(headerV1, 0, 0)
masternodesV1 := adaptor.GetMasternodesFromCheckpointHeader(headerV1)
headerV2 := currentBlock.Header()
headerV2.Number.Add(blockchain.Config().XDPoS.XDPoSV2Block, big.NewInt(1))
headerV2.Validators = common.Hex2Bytes("0278c350152e15fa6ffc712a5a73d704ce73e2e103d9e17ae3ff2c6712e44e25b09ac5ee91f6c9ff065551f0dcac6f00cae11192d462db709be3758c")
masternodesV2 := adaptor.GetMasternodesFromCheckpointHeader(headerV2, 0, 0)
masternodesV2 := adaptor.GetMasternodesFromCheckpointHeader(headerV2)
assert.True(t, reflect.DeepEqual(masternodesV1, masternodesV2), "GetMasternodesFromCheckpointHeader in adaptor for v1 v2 not equal", "v1", masternodesV1, "v2", masternodesV2)
}
func TestAdaptorIsEpochSwitch(t *testing.T) {
@ -79,9 +79,15 @@ func TestAdaptorIsEpochSwitch(t *testing.T) {
header := currentBlock.Header()
// v1
header.Number.SetUint64(0)
assert.True(t, adaptor.IsEpochSwitch(header), "header should be epoch switch", header)
isEpochSwitchBlock, epochNum, err := adaptor.IsEpochSwitch(header)
assert.Nil(t, err)
assert.True(t, isEpochSwitchBlock, "header should be epoch switch", header)
assert.Equal(t, uint64(0), epochNum)
header.Number.SetUint64(1)
assert.False(t, adaptor.IsEpochSwitch(header), "header should not be epoch switch", header)
isEpochSwitchBlock, _, err = adaptor.IsEpochSwitch(header)
assert.Nil(t, err)
assert.False(t, isEpochSwitchBlock, "header should not be epoch switch", header)
// v2
parentBlockInfo := &utils.BlockInfo{
Hash: header.ParentHash,
@ -100,7 +106,9 @@ func TestAdaptorIsEpochSwitch(t *testing.T) {
assert.Nil(t, err)
header.Extra = extraBytes
header.Number.Add(blockchain.Config().XDPoS.XDPoSV2Block, big.NewInt(1))
assert.True(t, adaptor.IsEpochSwitch(header), "header should be epoch switch", header)
isEpochSwitchBlock, _, err = adaptor.IsEpochSwitch(header)
assert.Nil(t, err)
assert.True(t, isEpochSwitchBlock, "header should be epoch switch", header)
parentBlockInfo = &utils.BlockInfo{
Hash: header.ParentHash,
Round: utils.Round(1),
@ -118,7 +126,9 @@ func TestAdaptorIsEpochSwitch(t *testing.T) {
assert.Nil(t, err)
header.Extra = extraBytes
header.Number.Add(blockchain.Config().XDPoS.XDPoSV2Block, big.NewInt(2))
assert.False(t, adaptor.IsEpochSwitch(header), "header should not be epoch switch", header)
isEpochSwitchBlock, _, err = adaptor.IsEpochSwitch(header)
assert.Nil(t, err)
assert.False(t, isEpochSwitchBlock, "header should not be epoch switch", header)
parentBlockInfo = &utils.BlockInfo{
Hash: header.ParentHash,
Round: utils.Round(blockchain.Config().XDPoS.Epoch) - 1,
@ -136,7 +146,9 @@ func TestAdaptorIsEpochSwitch(t *testing.T) {
assert.Nil(t, err)
header.Extra = extraBytes
header.Number.Add(blockchain.Config().XDPoS.XDPoSV2Block, big.NewInt(101))
assert.True(t, adaptor.IsEpochSwitch(header), "header should be epoch switch", header)
isEpochSwitchBlock, _, err = adaptor.IsEpochSwitch(header)
assert.Nil(t, err)
assert.True(t, isEpochSwitchBlock, "header should be epoch switch", header)
parentBlockInfo = &utils.BlockInfo{
Hash: header.ParentHash,
Round: utils.Round(blockchain.Config().XDPoS.Epoch) + 1,
@ -154,7 +166,9 @@ func TestAdaptorIsEpochSwitch(t *testing.T) {
assert.Nil(t, err)
header.Extra = extraBytes
header.Number.Add(blockchain.Config().XDPoS.XDPoSV2Block, big.NewInt(101))
assert.False(t, adaptor.IsEpochSwitch(header), "header should not be epoch switch", header)
isEpochSwitchBlock, _, err = adaptor.IsEpochSwitch(header)
assert.Nil(t, err)
assert.False(t, isEpochSwitchBlock, "header should not be epoch switch", header)
}
func TestAdaptorGetMasternodesV2(t *testing.T) {
@ -173,6 +187,8 @@ func TestAdaptorGetMasternodesV2(t *testing.T) {
}
masternodes1 := adaptor.GetMasternodes(blockchain, currentBlock.Header())
assert.Equal(t, 3, len(masternodes1))
masternodes1ByNumber := adaptor.GetMasternodesByNumber(blockchain, currentBlock.NumberU64())
assert.True(t, reflect.DeepEqual(masternodes1, masternodes1ByNumber), "at block number", blockNum)
for blockNum = 12; blockNum < 15; blockNum++ {
blockHeader = createBlock(params.TestXDPoSMockChainConfigWithV2Engine, currentBlock, blockNum, int64(blockNum-10), blockCoinBase, signer, signFn)
currentBlock, err = insertBlock(blockchain, blockHeader)
@ -181,5 +197,46 @@ func TestAdaptorGetMasternodesV2(t *testing.T) {
}
masternodes2 := adaptor.GetMasternodes(blockchain, currentBlock.Header())
assert.True(t, reflect.DeepEqual(masternodes1, masternodes2), "at block number", blockNum)
masternodes2ByNumber := adaptor.GetMasternodesByNumber(blockchain, currentBlock.NumberU64())
assert.True(t, reflect.DeepEqual(masternodes2, masternodes2ByNumber), "at block number", blockNum)
}
}
func TestGetCurrentEpochSwitchBlock(t *testing.T) {
blockchain, _, currentBlock, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, 10, params.TestXDPoSMockChainConfigWithV2Engine, 0)
adaptor := blockchain.Engine().(*XDPoS.XDPoS)
// V1
currentCheckpointNumber, epochNum, err := adaptor.GetCurrentEpochSwitchBlock(blockchain, big.NewInt(9))
assert.Nil(t, err)
assert.Equal(t, uint64(0), currentCheckpointNumber)
assert.Equal(t, uint64(0), epochNum)
// V2
blockNum := 11
blockCoinBase := "0x111000000000000000000000000000000123"
blockHeader := createBlock(params.TestXDPoSMockChainConfigWithV2Engine, currentBlock, blockNum, 1, blockCoinBase, signer, signFn)
// it contains 3 master nodes
blockHeader.Validators = common.Hex2Bytes("0278c350152e15fa6ffc712a5a73d704ce73e2e103d9e17ae3ff2c6712e44e25b09ac5ee91f6c9ff065551f0dcac6f00cae11192d462db709be3758c")
// block 11 is the first v2 block, and is treated as epoch switch block
currentBlock, err = insertBlock(blockchain, blockHeader)
if err != nil {
t.Fatal(err)
}
currentCheckpointNumber, epochNum, err = adaptor.GetCurrentEpochSwitchBlock(blockchain, currentBlock.Number())
assert.Nil(t, err)
assert.Equal(t, uint64(11), currentCheckpointNumber)
assert.Equal(t, uint64(0), epochNum)
for blockNum = 12; blockNum < 15; blockNum++ {
blockHeader = createBlock(params.TestXDPoSMockChainConfigWithV2Engine, currentBlock, blockNum, int64(blockNum-10), blockCoinBase, signer, signFn)
currentBlock, err = insertBlock(blockchain, blockHeader)
if err != nil {
t.Fatal(err)
}
currentCheckpointNumber, epochNum, err := adaptor.GetCurrentEpochSwitchBlock(blockchain, currentBlock.Number())
assert.Nil(t, err)
assert.Equal(t, uint64(11), currentCheckpointNumber)
assert.Equal(t, uint64(0), epochNum)
}
}

View file

@ -42,10 +42,10 @@ func TestIsAuthorisedMNForConsensusV1(t *testing.T) {
// Acc3 is the default account that is on the signerList
engine := blockchain.Engine().(*XDPoS.XDPoS)
isAuthorisedMN := engine.IsAuthorisedAddress(block449.Header(), blockchain, acc3Addr)
isAuthorisedMN := engine.IsAuthorisedAddress(blockchain, block449.Header(), acc3Addr)
assert.True(t, isAuthorisedMN)
isAuthorisedMN = engine.IsAuthorisedAddress(block449.Header(), blockchain, acc1Addr)
isAuthorisedMN = engine.IsAuthorisedAddress(blockchain, block449.Header(), acc1Addr)
assert.False(t, isAuthorisedMN)
// Now, let's mine another block to trigger the GAP block signerList update
@ -62,9 +62,45 @@ func TestIsAuthorisedMNForConsensusV1(t *testing.T) {
t.Fatal(err)
}
isAuthorisedMN = engine.IsAuthorisedAddress(block450.Header(), blockchain, acc3Addr)
isAuthorisedMN = engine.IsAuthorisedAddress(blockchain, block450.Header(), acc3Addr)
assert.False(t, isAuthorisedMN)
isAuthorisedMN = engine.IsAuthorisedAddress(block450.Header(), blockchain, acc1Addr)
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, 10, params.TestXDPoSMockChainConfigWithV2Engine, 0)
adaptor := blockchain.Engine().(*XDPoS.XDPoS)
blockNum := 11
blockCoinBase := "0x111000000000000000000000000000000123"
blockHeader := createBlock(params.TestXDPoSMockChainConfigWithV2Engine, currentBlock, blockNum, 1, blockCoinBase, signer, signFn)
// it contains 3 master nodes
// xdc0278C350152e15fa6FFC712a5A73D704Ce73E2E1
// xdc03d9e17Ae3fF2c6712E44e25B09Ac5ee91f6c9ff
// xdc065551F0dcAC6f00CAe11192D462db709bE3758c
blockHeader.Validators = common.Hex2Bytes("0278c350152e15fa6ffc712a5a73d704ce73e2e103d9e17ae3ff2c6712e44e25b09ac5ee91f6c9ff065551f0dcac6f00cae11192d462db709be3758c")
// block 11 is the first v2 block, and is treated as epoch switch block
currentBlock, err := insertBlock(blockchain, blockHeader)
if err != nil {
t.Fatal(err)
}
// the first block will start from 1
isAuthorisedMN := adaptor.IsAuthorisedAddress(blockchain, currentBlock.Header(), common.HexToAddress("xdc03d9e17Ae3fF2c6712E44e25B09Ac5ee91f6c9ff"))
assert.True(t, isAuthorisedMN)
// The third address hence not valid
isAuthorisedMN = adaptor.IsAuthorisedAddress(blockchain, currentBlock.Header(), common.HexToAddress("xdc065551F0dcAC6f00CAe11192D462db709bE3758c"))
assert.False(t, isAuthorisedMN)
for blockNum = 12; blockNum < 16; blockNum++ {
blockHeader = createBlock(params.TestXDPoSMockChainConfigWithV2Engine, currentBlock, blockNum, int64(blockNum-10), blockCoinBase, signer, signFn)
currentBlock, err = insertBlock(blockchain, blockHeader)
if err != nil {
t.Fatal(err)
}
}
isAuthorisedMN = adaptor.IsAuthorisedAddress(blockchain, currentBlock.Header(), common.HexToAddress("xdc065551F0dcAC6f00CAe11192D462db709bE3758c"))
assert.True(t, isAuthorisedMN)
}

View file

@ -262,6 +262,10 @@ func PrepareXDCTestBlockChain(t *testing.T, numOfBlocks int, chainConfig *params
if err != nil {
t.Fatal(err)
}
go func() {
checkpointChanMsg := <-core.CheckpointCh
log.Info("[V1] Got a message from core CheckpointChan!", "msg", checkpointChanMsg)
}()
return blockchain, backend, currentBlock, signer
}
@ -320,6 +324,10 @@ func PrepareXDCTestBlockChainForV2Engine(t *testing.T, numOfBlocks int, chainCon
if err != nil {
t.Fatal(err)
}
go func() {
checkpointChanMsg := <-core.CheckpointCh
log.Info("[V2] Got a message from core CheckpointChan!", "msg", checkpointChanMsg)
}()
return blockchain, backend, currentBlock, signer, signFn, currentForkBlock
}
@ -385,13 +393,13 @@ func createBlock(chainConfig *params.ChainConfig, startingBlock *types.Block, bl
return header
}
func generateSignature(backend *backends.SimulatedBackend, header *types.Header) error {
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}, utils.SigHashV2(header).Bytes())
signature, err := signFn(accounts.Account{Address: signer}, adaptor.SigHash(header).Bytes())
if err != nil {
return err
}
@ -488,7 +496,7 @@ func createXDPoSTestBlock(bc *BlockChain, customHeader *types.Header, txs []*typ
Time: big.NewInt(customHeader.Number.Int64() * 10),
Extra: customHeader.Extra,
Validator: customHeader.Validator,
Validators: customHeader.Validators,
Validators: customHeader.Validators,
}
var block *types.Block
if len(txs) == 0 {

View file

@ -352,8 +352,7 @@ func GetRewardForCheckpoint(c *XDPoS.XDPoS, chain consensus.ChainReader, header
}
}
header = chain.GetHeader(header.ParentHash, prevCheckpoint)
//TODO: i think this should be c.GetMasternodesFrom...
masternodes := utils.GetMasternodesFromCheckpointHeader(header)
masternodes := c.GetMasternodesFromCheckpointHeader(header)
for i := startBlockNumber; i <= endBlockNumber; i++ {
if i%common.MergeSignRange == 0 || !chain.Config().IsTIP2019(big.NewInt(int64(i))) {

View file

@ -17,11 +17,12 @@
package core
import (
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
"runtime"
"testing"
"time"
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
"github.com/XinFinOrg/XDPoSChain/consensus/ethash"
"github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/core/vm"

View file

@ -1590,8 +1590,13 @@ func (bc *BlockChain) insertChain(chain types.Blocks) (int, []interface{}, []*ty
bc.reportBlock(block, nil, err)
return i, events, coalescedLogs, err
}
if (block.NumberU64() % bc.chainConfig.XDPoS.Epoch) == 0 {
if err := tradingService.UpdateMediumPriceBeforeEpoch(block.NumberU64()/bc.chainConfig.XDPoS.Epoch, tradingState, statedb); err != nil {
isEpochSwithBlock, epochNumber, err := engine.IsEpochSwitch(block.Header())
if err != nil {
log.Error("[insertChain] Error while checking if the incoming block is epoch switch block", "Hash", block.Hash(), "Number", block.Number())
bc.reportBlock(block, nil, err)
}
if isEpochSwithBlock {
if err := tradingService.UpdateMediumPriceBeforeEpoch(epochNumber, tradingState, statedb); err != nil {
return i, events, coalescedLogs, err
}
} else {
@ -1708,9 +1713,13 @@ func (bc *BlockChain) insertChain(chain types.Blocks) (int, []interface{}, []*ty
stats.report(chain, i, dirty)
if bc.chainConfig.XDPoS != nil {
// epoch block
if (chain[i].NumberU64() % bc.chainConfig.XDPoS.Epoch) == 0 {
isEpochSwithBlock, _, err := engine.IsEpochSwitch(chain[i].Header())
if err != nil {
log.Error("[insertChain] Error while checking and notifying channel CheckpointCh if the incoming block is epoch switch block", "Hash", block.Hash(), "Number", block.Number())
bc.reportBlock(block, nil, err)
}
if isEpochSwithBlock {
CheckpointCh <- 1
}
}
}
@ -1855,8 +1864,15 @@ func (bc *BlockChain) getResultBlock(block *types.Block, verifiedM2 bool) (*Resu
bc.reportBlock(block, nil, err)
return nil, err
}
if (block.NumberU64() % bc.chainConfig.XDPoS.Epoch) == 0 {
if err := tradingService.UpdateMediumPriceBeforeEpoch(block.NumberU64()/bc.chainConfig.XDPoS.Epoch, tradingState, statedb); err != nil {
isEpochSwithBlock, epochNumber, err := engine.IsEpochSwitch(block.Header())
if err != nil {
log.Error("[getResultBlock] Error while checking block is epoch switch block", "Hash", block.Hash(), "Number", block.Number())
bc.reportBlock(block, nil, err)
}
if isEpochSwithBlock {
if err := tradingService.UpdateMediumPriceBeforeEpoch(epochNumber, tradingState, statedb); err != nil {
return nil, err
}
} else {
@ -2029,7 +2045,12 @@ func (bc *BlockChain) insertBlock(block *types.Block) ([]interface{}, []*types.L
stats.report(types.Blocks{block}, 0, dirty)
if bc.chainConfig.XDPoS != nil {
// epoch block
if (block.NumberU64() % bc.chainConfig.XDPoS.Epoch) == 0 {
isEpochSwithBlock, _, err := bc.Engine().(*XDPoS.XDPoS).IsEpochSwitch(block.Header())
if err != nil {
log.Error("[insertBlock] Error while checking if the incoming block is epoch switch block", "Hash", block.Hash(), "Number", block.Number())
bc.reportBlock(block, nil, err)
}
if isEpochSwithBlock {
CheckpointCh <- 1
}

View file

@ -303,7 +303,17 @@ func (b *EthApiBackend) GetVotersRewards(masternodeAddr common.Address) map[comm
number := block.Number().Uint64()
engine := b.GetEngine().(*XDPoS.XDPoS)
foundationWalletAddr := chain.Config().XDPoS.FoudationWalletAddr
lastCheckpointNumber := number - (number % b.ChainConfig().XDPoS.Epoch) - b.ChainConfig().XDPoS.Epoch // calculate for 2 epochs ago
// calculate for 2 epochs ago
currentCheckpointNumber, _, err := engine.GetCurrentEpochSwitchBlock(chain, block.Number())
if err != nil {
log.Error("[GetVotersRewards] Fail to get GetCurrentEpochSwitchBlock for current checkpoint block", "block", block)
}
lastCheckpointNumber, _, err := engine.GetCurrentEpochSwitchBlock(chain, big.NewInt(int64(currentCheckpointNumber-1)))
if err != nil {
log.Error("[GetVotersRewards] Fail to get GetCurrentEpochSwitchBlock for last checkpoint block", "block", block)
}
lastCheckpointBlock := chain.GetBlockByNumber(lastCheckpointNumber)
rCheckpoint := chain.Config().XDPoS.RewardCheckpoint

View file

@ -267,7 +267,7 @@ func New(ctx *node.ServiceContext, config *Config, XDCXServ *XDCx.XDCX, lendingS
return block, false, err
}
header := block.Header()
sighash, err := wallet.SignHash(accounts.Account{Address: eb}, XDPoS.SigHash(header).Bytes())
sighash, err := wallet.SignHash(accounts.Account{Address: eb}, c.SigHash(header).Bytes())
if err != nil || sighash == nil {
log.Error("Can't get signature hash of m2", "sighash", sighash, "err", err)
return block, false, err
@ -296,7 +296,7 @@ func New(ctx *node.ServiceContext, config *Config, XDCXServ *XDCx.XDCX, lendingS
// not genesis block
header = parentHeader
}
return c.IsAuthorisedAddress(header, eth.blockchain, address)
return c.IsAuthorisedAddress(eth.blockchain, header, address)
}
}
@ -364,7 +364,7 @@ func CreateConsensusEngine(ctx *node.ServiceContext, config *ethash.Config, chai
// APIs returns the collection of RPC services the ethereum package offers.
// NOTE, some of these services probably need to be moved to somewhere else.
func (s *Ethereum) APIs() []rpc.API {
apis := ethapi.GetAPIs(s.ApiBackend)
apis := ethapi.GetAPIs(s.ApiBackend, s.BlockChain())
// Append any APIs exposed explicitly by the consensus engine
apis = append(apis, s.engine.APIs(s.BlockChain())...)
@ -464,7 +464,7 @@ func (s *Ethereum) ValidateMasternode() (bool, error) {
//check if miner's wallet is in set of validators
c := s.engine.(*XDPoS.XDPoS)
authorized := c.IsAuthorisedAddress(s.blockchain.CurrentHeader(), s.blockchain, eb)
authorized := c.IsAuthorisedAddress(s.blockchain, s.blockchain.CurrentHeader(), eb)
if !authorized {
//This miner doesn't belong to set of validators
return false, nil

View file

@ -27,6 +27,7 @@ import (
"time"
"github.com/XinFinOrg/XDPoSChain/XDCxlending/lendingstate"
"github.com/XinFinOrg/XDPoSChain/consensus"
"github.com/XinFinOrg/XDPoSChain/XDCx/tradingstate"
@ -497,12 +498,16 @@ func (s *PrivateAccountAPI) SignAndSendTransaction(ctx context.Context, args Sen
// PublicBlockChainAPI provides an API to access the Ethereum blockchain.
// It offers only methods that operate on public data that is freely available to anyone.
type PublicBlockChainAPI struct {
b Backend
b Backend
chainReader consensus.ChainReader
}
// NewPublicBlockChainAPI creates a new Ethereum blockchain API.
func NewPublicBlockChainAPI(b Backend) *PublicBlockChainAPI {
return &PublicBlockChainAPI{b}
func NewPublicBlockChainAPI(b Backend, chainReader consensus.ChainReader) *PublicBlockChainAPI {
return &PublicBlockChainAPI{
b,
chainReader,
}
}
// BlockNumber returns the block number of the chain head.
@ -694,11 +699,7 @@ func (s *PublicBlockChainAPI) GetMasternodes(ctx context.Context, b *types.Block
}
if engine, ok := s.b.GetEngine().(*XDPoS.XDPoS); ok {
// Get block epoc latest.
lastCheckpointNumber := prevBlockNumber - (prevBlockNumber % s.b.ChainConfig().XDPoS.Epoch)
prevCheckpointBlock, _ := s.b.BlockByNumber(ctx, rpc.BlockNumber(lastCheckpointNumber))
if prevCheckpointBlock != nil {
masternodes = engine.GetMasternodesFromCheckpointHeader(prevCheckpointBlock.Header(), curBlockNumber, s.b.ChainConfig().XDPoS.Epoch)
}
return engine.GetMasternodesByNumber(s.chainReader, prevBlockNumber), nil
} else {
log.Error("Undefined XDPoS consensus engine")
}
@ -791,7 +792,7 @@ func (s *PublicBlockChainAPI) GetCandidateStatus(ctx context.Context, coinbaseAd
// Second, Find candidates that have masternode status
if engine, ok := s.b.GetEngine().(*XDPoS.XDPoS); ok {
masternodes = engine.GetMasternodesFromCheckpointHeader(header, block.Number().Uint64(), s.b.ChainConfig().XDPoS.Epoch)
masternodes = engine.GetMasternodesFromCheckpointHeader(header)
if len(masternodes) == 0 {
log.Error("Failed to get masternodes", "err", err, "len(masternodes)", len(masternodes), "blockNum", header.Number.Uint64())
result[fieldSuccess] = false
@ -902,7 +903,7 @@ func (s *PublicBlockChainAPI) GetCandidates(ctx context.Context, epoch rpc.Epoch
// Second, Find candidates that have masternode status
if engine, ok := s.b.GetEngine().(*XDPoS.XDPoS); ok {
masternodes = engine.GetMasternodesFromCheckpointHeader(header, block.Number().Uint64(), s.b.ChainConfig().XDPoS.Epoch)
masternodes = engine.GetMasternodesFromCheckpointHeader(header)
if len(masternodes) == 0 {
log.Error("Failed to get masternodes", "err", err, "len(masternodes)", len(masternodes), "blockNum", header.Number.Uint64())
result[fieldSuccess] = false
@ -965,21 +966,22 @@ func (s *PublicBlockChainAPI) GetCandidates(ctx context.Context, epoch rpc.Epoch
// GetPreviousCheckpointFromEpoch returns header of the previous checkpoint
func (s *PublicBlockChainAPI) GetPreviousCheckpointFromEpoch(ctx context.Context, epochNum rpc.EpochNumber) (rpc.BlockNumber, rpc.EpochNumber) {
var checkpointNumber uint64
epoch := s.b.ChainConfig().XDPoS.Epoch
currentCheckpointNumber, epochNumber, err := s.b.GetEngine().(*XDPoS.XDPoS).GetCurrentEpochSwitchBlock(s.chainReader, s.b.CurrentBlock().Number())
if err != nil {
log.Error("[GetPreviousCheckpointFromEpoch] Error while trying to get current epoch switch block information", "Block", s.b.CurrentBlock(), "Error", err)
}
if epochNum == rpc.LatestEpochNumber {
blockNumer := s.b.CurrentBlock().Number().Uint64()
diff := blockNumer % epoch
// checkpoint number
checkpointNumber = blockNumer - diff
epochNum = rpc.EpochNumber(checkpointNumber / epoch)
if diff > 0 {
epochNum += 1
}
checkpointNumber = currentCheckpointNumber
epochNum = rpc.EpochNumber(epochNumber)
} else if epochNum < 2 {
checkpointNumber = 0
} else {
checkpointNumber = epoch * (uint64(epochNum) - 1)
blockNumberBeforeCurrentEpochSwitch := currentCheckpointNumber - 1
checkpointNumber, _, err = s.b.GetEngine().(*XDPoS.XDPoS).GetCurrentEpochSwitchBlock(s.chainReader, big.NewInt(int64(blockNumberBeforeCurrentEpochSwitch)))
if err != nil {
log.Error("[GetPreviousCheckpointFromEpoch] Error while trying to get last epoch switch block information", "Number", blockNumberBeforeCurrentEpochSwitch, "Error", err)
}
}
return rpc.BlockNumber(checkpointNumber), epochNum
}
@ -1301,7 +1303,11 @@ func (s *PublicBlockChainAPI) findNearestSignedBlock(ctx context.Context, b *typ
}
// Get block epoc latest
checkpointNumber := signedBlockNumber - (signedBlockNumber % s.b.ChainConfig().XDPoS.Epoch)
checkpointNumber, _, err := s.b.GetEngine().(*XDPoS.XDPoS).GetCurrentEpochSwitchBlock(s.chainReader, big.NewInt(int64(signedBlockNumber)))
if err != nil {
log.Error("[findNearestSignedBlock] Error while trying to get current Epoch switch block", "Number", signedBlockNumber)
}
checkpointBlock, _ := s.b.BlockByNumber(ctx, rpc.BlockNumber(checkpointNumber))
if checkpointBlock != nil {
@ -1385,13 +1391,9 @@ func (s *PublicBlockChainAPI) getSigners(ctx context.Context, block *types.Block
var err error
var filterSigners []common.Address
var signers []common.Address
blockNumber := block.Number().Uint64()
// Get block epoc latest.
checkpointNumber := blockNumber - (blockNumber % s.b.ChainConfig().XDPoS.Epoch)
checkpointBlock, _ := s.b.BlockByNumber(ctx, rpc.BlockNumber(checkpointNumber))
masternodes := engine.GetMasternodes(s.chainReader, block.Header())
masternodes := engine.GetMasternodesFromCheckpointHeader(checkpointBlock.Header(), blockNumber, s.b.ChainConfig().XDPoS.Epoch)
signers, err = GetSignersFromBlocks(s.b, block.NumberU64(), block.Hash(), masternodes)
if err != nil {
log.Error("Fail to get signers from block signer SC.", "error", err)

View file

@ -97,7 +97,7 @@ type Backend interface {
GetOrderNonce(address common.Hash) (uint64, error)
}
func GetAPIs(apiBackend Backend) []rpc.API {
func GetAPIs(apiBackend Backend, chainReader consensus.ChainReader) []rpc.API {
nonceLock := new(AddrLocker)
return []rpc.API{
{
@ -108,7 +108,7 @@ func GetAPIs(apiBackend Backend) []rpc.API {
}, {
Namespace: "eth",
Version: "1.0",
Service: NewPublicBlockChainAPI(apiBackend),
Service: NewPublicBlockChainAPI(apiBackend, chainReader),
Public: true,
}, {
Namespace: "eth",

View file

@ -176,7 +176,7 @@ func (s *LightDummyAPI) Mining() bool {
// APIs returns the collection of RPC services the ethereum package offers.
// NOTE, some of these services probably need to be moved to somewhere else.
func (s *LightEthereum) APIs() []rpc.API {
return append(ethapi.GetAPIs(s.ApiBackend), []rpc.API{
return append(ethapi.GetAPIs(s.ApiBackend, nil), []rpc.API{
{
Namespace: "eth",
Version: "1.0",

View file

@ -381,7 +381,11 @@ func (self *worker) wait() {
}
if work.config.XDPoS != nil {
// epoch block
if (block.NumberU64() % work.config.XDPoS.Epoch) == 0 {
isEpochSwitchBlock, _, err := self.engine.(*XDPoS.XDPoS).IsEpochSwitch(block.Header())
if err != nil {
log.Error("[wait] fail to check if block is epoch switch block when worker waiting", "BlockNum", block.Number(), "Hash", block.Hash())
}
if isEpochSwitchBlock {
core.CheckpointCh <- 1
}
}
@ -402,7 +406,7 @@ func (self *worker) wait() {
log.Error("[wait] Unable to handle new proposed block", "err", err, "number", block.Number(), "hash", block.Hash())
}
authorized := c.IsAuthorisedAddress(block.Header(), self.chain, self.coinbase)
authorized := c.IsAuthorisedAddress(self.chain, block.Header(), self.coinbase)
if !authorized {
valid := false
masternodes := c.GetMasternodes(self.chain, block.Header())
@ -635,13 +639,19 @@ func (self *worker) commitNewWork() {
lendingFinalizedTradeTransaction *types.Transaction
)
feeCapacity := state.GetTRC21FeeCapacityFromStateWithCache(parent.Root(), work.state)
if self.config.XDPoS != nil && header.Number.Uint64()%self.config.XDPoS.Epoch != 0 {
pending, err := self.eth.TxPool().Pending()
if self.config.XDPoS != nil {
isEpochSwitchBlock, _, err := self.engine.(*XDPoS.XDPoS).IsEpochSwitch(header)
if err != nil {
log.Error("Failed to fetch pending transactions", "err", err)
return
log.Error("[commitNewWork] fail to check if block is epoch switch block when fetching pending transactions", "BlockNum", header.Number, "Hash", header.Hash())
}
if !isEpochSwitchBlock {
pending, err := self.eth.TxPool().Pending()
if err != nil {
log.Error("Failed to fetch pending transactions", "err", err)
return
}
txs, specialTxs = types.NewTransactionsByPriceAndNonce(self.current.signer, pending, signers, feeCapacity)
}
txs, specialTxs = types.NewTransactionsByPriceAndNonce(self.current.signer, pending, signers, feeCapacity)
}
if atomic.LoadInt32(&self.mining) == 1 {
wallet, err := self.eth.AccountManager().Find(accounts.Account{Address: self.coinbase})
@ -653,16 +663,20 @@ func (self *worker) commitNewWork() {
XDCX := self.eth.GetXDCX()
XDCXLending := self.eth.GetXDCXLending()
if XDCX != nil && header.Number.Uint64() > self.config.XDPoS.Epoch {
if header.Number.Uint64()%self.config.XDPoS.Epoch == 0 {
err := XDCX.UpdateMediumPriceBeforeEpoch(header.Number.Uint64()/self.config.XDPoS.Epoch, work.tradingState, work.state)
isEpochSwitchBlock, epochNumber, err := self.engine.(*XDPoS.XDPoS).IsEpochSwitch(header)
if err != nil {
log.Error("[commitNewWork] fail to check if block is epoch switch block when performing XDCX and XDCXLending operations", "BlockNum", header.Number, "Hash", header.Hash())
}
if isEpochSwitchBlock {
err := XDCX.UpdateMediumPriceBeforeEpoch(epochNumber, work.tradingState, work.state)
if err != nil {
log.Error("Fail when update medium price last epoch", "error", err)
return
}
}
// won't grasp tx at checkpoint
//https://github.com/XinFinOrg/XDPoSChain-v1/pull/416
if header.Number.Uint64()%self.config.XDPoS.Epoch != 0 {
} else {
// won't grasp tx at checkpoint
//https://github.com/XinFinOrg/XDPoSChain-v1/pull/416
log.Debug("Start processing order pending")
tradingOrderPending, _ := self.eth.OrderPool().Pending()
log.Debug("Start processing order pending", "len", len(tradingOrderPending))
@ -680,6 +694,7 @@ func (self *worker) commitNewWork() {
}
}
}
if len(tradingTxMatches) > 0 {
txMatchBatch := &tradingstate.TxMatchBatch{
Data: tradingTxMatches,
@ -1062,10 +1077,16 @@ func (env *Work) commitTransactions(mux *event.TypeMux, balanceFee map[common.Ad
}
go func(logs []*types.Log, tcount int) {
if len(logs) > 0 {
mux.Post(core.PendingLogsEvent{Logs: logs})
err := mux.Post(core.PendingLogsEvent{Logs: logs})
if err != nil {
log.Warn("[commitTransactions] Error when sending PendingLogsEvent", "LogLength", len(logs))
}
}
if tcount > 0 {
mux.Post(core.PendingStateEvent{})
err := mux.Post(core.PendingStateEvent{})
if err != nil {
log.Warn("[commitTransactions] Error when sending PendingStateEvent", "tcount", tcount)
}
}
}(cpy, env.tcount)
}