diff --git a/.gitignore b/.gitignore
index be40968a3b..6c6f16ee66 100644
--- a/.gitignore
+++ b/.gitignore
@@ -48,3 +48,5 @@ profile.cov
/dashboard/assets/package-lock.json
**/yarn-error.log
+coverage.txt
+go.sum
\ No newline at end of file
diff --git a/XDCxlending/XDCxlending.go b/XDCxlending/XDCxlending.go
index 200de4bc4c..7b1d591280 100644
--- a/XDCxlending/XDCxlending.go
+++ b/XDCxlending/XDCxlending.go
@@ -4,6 +4,10 @@ import (
"encoding/json"
"errors"
"fmt"
+ "math/big"
+ "strconv"
+ "time"
+
"github.com/XinFinOrg/XDPoSChain/XDCx"
"github.com/XinFinOrg/XDPoSChain/XDCx/tradingstate"
"github.com/XinFinOrg/XDPoSChain/XDCxDAO"
@@ -12,9 +16,6 @@ import (
"github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/p2p"
"gopkg.in/karalabe/cookiejar.v2/collections/prque"
- "math/big"
- "strconv"
- "time"
"github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/core/state"
diff --git a/consensus/XDPoS/XDPoS.go b/consensus/XDPoS/XDPoS.go
index d4b19bba84..32da29c597 100644
--- a/consensus/XDPoS/XDPoS.go
+++ b/consensus/XDPoS/XDPoS.go
@@ -1,4 +1,4 @@
-// Copyright (c) 2018 XDPoSChain
+// Copyright (c) 2021 XDPoSChain
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
@@ -13,235 +13,31 @@
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see .
-// Package XDPoS implements the delegated-proof-of-stake consensus engine.
+// Package XDPoS is the adaptor for different consensus engine.
package XDPoS
import (
- "bytes"
- "encoding/json"
- "errors"
- "fmt"
- "io/ioutil"
"math/big"
- "math/rand"
- "path/filepath"
- "reflect"
- "sort"
- "strconv"
- "sync"
- "time"
- "github.com/XinFinOrg/XDPoSChain/XDCx/tradingstate"
- "github.com/XinFinOrg/XDPoSChain/XDCxlending/lendingstate"
- "gopkg.in/karalabe/cookiejar.v2/collections/prque"
-
- "github.com/XinFinOrg/XDPoSChain/accounts"
"github.com/XinFinOrg/XDPoSChain/common"
- "github.com/XinFinOrg/XDPoSChain/common/hexutil"
"github.com/XinFinOrg/XDPoSChain/consensus"
+ "github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/engines/engine_v1"
+ "github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils"
"github.com/XinFinOrg/XDPoSChain/consensus/clique"
- "github.com/XinFinOrg/XDPoSChain/consensus/misc"
"github.com/XinFinOrg/XDPoSChain/core/state"
"github.com/XinFinOrg/XDPoSChain/core/types"
- "github.com/XinFinOrg/XDPoSChain/crypto"
- "github.com/XinFinOrg/XDPoSChain/crypto/sha3"
"github.com/XinFinOrg/XDPoSChain/ethdb"
- "github.com/XinFinOrg/XDPoSChain/log"
"github.com/XinFinOrg/XDPoSChain/params"
- "github.com/XinFinOrg/XDPoSChain/rlp"
"github.com/XinFinOrg/XDPoSChain/rpc"
lru "github.com/hashicorp/golang-lru"
)
-const (
- inmemorySnapshots = 128 // Number of recent vote snapshots to keep in memory
- blockSignersCacheLimit = 9000
- M2ByteLength = 4
-)
-
-type Masternode struct {
- Address common.Address
- Stake *big.Int
-}
-
-type TradingService interface {
- GetTradingStateRoot(block *types.Block, author common.Address) (common.Hash, error)
- GetTradingState(block *types.Block, author common.Address) (*tradingstate.TradingStateDB, error)
- HasTradingState(block *types.Block, author common.Address) bool
- GetStateCache() tradingstate.Database
- GetTriegc() *prque.Prque
- ApplyOrder(header *types.Header, coinbase common.Address, chain consensus.ChainContext, statedb *state.StateDB, XDCXstatedb *tradingstate.TradingStateDB, orderBook common.Hash, order *tradingstate.OrderItem) ([]map[string]string, []*tradingstate.OrderItem, error)
- UpdateMediumPriceBeforeEpoch(epochNumber uint64, tradingStateDB *tradingstate.TradingStateDB, statedb *state.StateDB) error
- IsSDKNode() bool
- SyncDataToSDKNode(takerOrder *tradingstate.OrderItem, txHash common.Hash, txMatchTime time.Time, statedb *state.StateDB, trades []map[string]string, rejectedOrders []*tradingstate.OrderItem, dirtyOrderCount *uint64) error
- RollbackReorgTxMatch(txhash common.Hash) error
- GetTokenDecimal(chain consensus.ChainContext, statedb *state.StateDB, tokenAddr common.Address) (*big.Int, error)
-}
-
-type LendingService interface {
- GetLendingStateRoot(block *types.Block, author common.Address) (common.Hash, error)
- GetLendingState(block *types.Block, author common.Address) (*lendingstate.LendingStateDB, error)
- HasLendingState(block *types.Block, author common.Address) bool
- GetStateCache() lendingstate.Database
- GetTriegc() *prque.Prque
- ApplyOrder(header *types.Header, coinbase common.Address, chain consensus.ChainContext, statedb *state.StateDB, lendingStateDB *lendingstate.LendingStateDB, tradingStateDb *tradingstate.TradingStateDB, lendingOrderBook common.Hash, order *lendingstate.LendingItem) ([]*lendingstate.LendingTrade, []*lendingstate.LendingItem, error)
- GetCollateralPrices(header *types.Header, chain consensus.ChainContext, statedb *state.StateDB, tradingStateDb *tradingstate.TradingStateDB, collateralToken common.Address, lendingToken common.Address) (*big.Int, *big.Int, error)
- GetMediumTradePriceBeforeEpoch(chain consensus.ChainContext, statedb *state.StateDB, tradingStateDb *tradingstate.TradingStateDB, baseToken common.Address, quoteToken common.Address) (*big.Int, error)
- ProcessLiquidationData(header *types.Header, chain consensus.ChainContext, statedb *state.StateDB, tradingState *tradingstate.TradingStateDB, lendingState *lendingstate.LendingStateDB) (updatedTrades map[common.Hash]*lendingstate.LendingTrade, liquidatedTrades, autoRepayTrades, autoTopUpTrades, autoRecallTrades []*lendingstate.LendingTrade, err error)
- SyncDataToSDKNode(chain consensus.ChainContext, state *state.StateDB, block *types.Block, takerOrderInTx *lendingstate.LendingItem, txHash common.Hash, txMatchTime time.Time, trades []*lendingstate.LendingTrade, rejectedOrders []*lendingstate.LendingItem, dirtyOrderCount *uint64) error
- UpdateLiquidatedTrade(blockTime uint64, result lendingstate.FinalizedResult, trades map[common.Hash]*lendingstate.LendingTrade) error
- RollbackLendingData(txhash common.Hash) error
-}
-
-// XDPoS delegated-proof-of-stake protocol constants.
-var (
- epochLength = uint64(900) // Default number of blocks after which to checkpoint and reset the pending votes
-
- extraVanity = 32 // Fixed number of extra-data prefix bytes reserved for signer vanity
- extraSeal = 65 // Fixed number of extra-data suffix bytes reserved for signer seal
-
- nonceAuthVote = hexutil.MustDecode("0xffffffffffffffff") // Magic nonce number to vote on adding a new signer
- nonceDropVote = hexutil.MustDecode("0x0000000000000000") // Magic nonce number to vote on removing a signer.
-
- uncleHash = types.CalcUncleHash(nil) // Always Keccak256(RLP([])) as uncles are meaningless outside of PoW.
-
- diffInTurn = big.NewInt(2) // Block difficulty for in-turn signatures
- diffNoTurn = big.NewInt(1) // Block difficulty for out-of-turn signatures
-)
-
-// Various error messages to mark blocks invalid. These should be private to
-// prevent engine specific errors from being referenced in the remainder of the
-// codebase, inherently breaking if the engine is swapped out. Please put common
-// error types into the consensus package.
-var (
- // errUnknownBlock is returned when the list of signers is requested for a block
- // that is not part of the local blockchain.
- errUnknownBlock = errors.New("unknown block")
-
- // errInvalidCheckpointBeneficiary is returned if a checkpoint/epoch transition
- // block has a beneficiary set to non-zeroes.
- errInvalidCheckpointBeneficiary = errors.New("beneficiary in checkpoint block non-zero")
-
- // errInvalidVote is returned if a nonce value is something else that the two
- // allowed constants of 0x00..0 or 0xff..f.
- errInvalidVote = errors.New("vote nonce not 0x00..0 or 0xff..f")
-
- // errInvalidCheckpointVote is returned if a checkpoint/epoch transition block
- // has a vote nonce set to non-zeroes.
- errInvalidCheckpointVote = errors.New("vote nonce in checkpoint block non-zero")
-
- // errMissingVanity is returned if a block's extra-data section is shorter than
- // 32 bytes, which is required to store the signer vanity.
- errMissingVanity = errors.New("extra-data 32 byte vanity prefix missing")
-
- // errMissingSignature is returned if a block's extra-data section doesn't seem
- // to contain a 65 byte secp256k1 signature.
- errMissingSignature = errors.New("extra-data 65 byte suffix signature missing")
-
- // errExtraSigners is returned if non-checkpoint block contain signer data in
- // their extra-data fields.
- errExtraSigners = errors.New("non-checkpoint block contains extra signer list")
-
- // errInvalidCheckpointSigners is returned if a checkpoint block contains an
- // invalid list of signers (i.e. non divisible by 20 bytes, or not the correct
- // ones).
- errInvalidCheckpointSigners = errors.New("invalid signer list on checkpoint block")
-
- errInvalidCheckpointPenalties = errors.New("invalid penalty list on checkpoint block")
-
- // errInvalidMixDigest is returned if a block's mix digest is non-zero.
- errInvalidMixDigest = errors.New("non-zero mix digest")
-
- // errInvalidUncleHash is returned if a block contains an non-empty uncle list.
- errInvalidUncleHash = errors.New("non empty uncle hash")
-
- // errInvalidDifficulty is returned if the difficulty of a block is not either
- // of 1 or 2, or if the value does not match the turn of the signer.
- errInvalidDifficulty = errors.New("invalid difficulty")
-
- // ErrInvalidTimestamp is returned if the timestamp of a block is lower than
- // the previous block's timestamp + the minimum block period.
- ErrInvalidTimestamp = errors.New("invalid timestamp")
-
- // errInvalidVotingChain is returned if an authorization list is attempted to
- // be modified via out-of-range or non-contiguous headers.
- errInvalidVotingChain = errors.New("invalid voting chain")
-
- // errUnauthorized is returned if a header is signed by a non-authorized entity.
- errUnauthorized = errors.New("unauthorized")
-
- errFailedDoubleValidation = errors.New("wrong pair of creator-validator in double validation")
-
- // errWaitTransactions is returned if an empty block is attempted to be sealed
- // on an instant chain (0 second period). It's important to refuse these as the
- // block reward is zero, so an empty block just bloats the chain... fast.
- errWaitTransactions = errors.New("waiting for transactions")
-
- ErrInvalidCheckpointValidators = errors.New("invalid validators list on checkpoint 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 sigHash(header *types.Header) (hash common.Hash) {
- hasher := sha3.NewKeccak256()
-
- 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,
- })
- hasher.Sum(hash[:0])
- return hash
-}
-
func SigHash(header *types.Header) (hash common.Hash) {
- return sigHash(header)
-}
-
-// 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
+ switch params.BlockConsensusVersion(header.Number) {
+ // TODO: Add switch case for 2.0 later
+ default: // Default "1.0"
+ return engine_v1.SigHash(header)
}
- // 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
}
// XDPoS is the delegated-proof-of-stake consensus engine proposed to support the
@@ -250,25 +46,17 @@ type XDPoS 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
- signatures *lru.ARCCache // Signatures of recent blocks to speed up mining
- validatorSignatures *lru.ARCCache // Signatures of recent blocks to speed up mining
- verifiedHeaders *lru.ARCCache
- proposals map[common.Address]bool // Current list of proposals we are pushing
-
- signer common.Address // Ethereum address of the signing key
- signFn clique.SignerFn // Signer function to authorize hashes with
- lock sync.RWMutex // Protects the signer fields
-
BlockSigners *lru.Cache
HookReward func(chain consensus.ChainReader, state *state.StateDB, parentState *state.StateDB, header *types.Header) (error, map[string]interface{})
HookPenalty func(chain consensus.ChainReader, blockNumberEpoc uint64) ([]common.Address, error)
HookPenaltyTIPSigning func(chain consensus.ChainReader, header *types.Header, candidate []common.Address) ([]common.Address, error)
HookValidator func(header *types.Header, signers []common.Address) ([]byte, error)
HookVerifyMNs func(header *types.Header, signers []common.Address) error
- GetXDCXService func() TradingService
- GetLendingService func() LendingService
+ GetXDCXService func() utils.TradingService
+ GetLendingService func() utils.LendingService
HookGetSignersFromContract func(blockHash common.Hash) ([]common.Address, error)
+
+ EngineV1 engine_v1.XDPoS_v1
}
// New creates a XDPoS delegated-proof-of-stake consensus engine with the initial
@@ -277,854 +65,26 @@ func New(config *params.XDPoSConfig, db ethdb.Database) *XDPoS {
// Set any missing consensus parameters to their defaults
conf := *config
if conf.Epoch == 0 {
- conf.Epoch = epochLength
+ conf.Epoch = utils.EpochLength
}
+
// Allocate the snapshot caches and create the engine
- BlockSigners, _ := lru.New(blockSignersCacheLimit)
- recents, _ := lru.NewARC(inmemorySnapshots)
- signatures, _ := lru.NewARC(inmemorySnapshots)
- validatorSignatures, _ := lru.NewARC(inmemorySnapshots)
- verifiedHeaders, _ := lru.NewARC(inmemorySnapshots)
+ BlockSigners, _ := lru.New(utils.BlockSignersCacheLimit)
+
return &XDPoS{
- config: &conf,
- db: db,
- BlockSigners: BlockSigners,
- recents: recents,
- signatures: signatures,
- verifiedHeaders: verifiedHeaders,
- validatorSignatures: validatorSignatures,
- proposals: make(map[common.Address]bool),
+ config: &conf,
+
+ BlockSigners: BlockSigners,
+ EngineV1: *engine_v1.New(&conf, db),
}
}
// NewFullFaker creates an ethash consensus engine with a full fake scheme that
// accepts all blocks as valid, without checking any consensus rules whatsoever.
-func NewFaker(db ethdb.Database) *XDPoS {
- var fakeEngine *XDPoS
- // Set any missing consensus parameters to their defaults
- conf := params.TestXDPoSMockChainConfig.XDPoS
-
- // Allocate the snapshot caches and create the engine
- BlockSigners, _ := lru.New(blockSignersCacheLimit)
- recents, _ := lru.NewARC(inmemorySnapshots)
- signatures, _ := lru.NewARC(inmemorySnapshots)
- validatorSignatures, _ := lru.NewARC(inmemorySnapshots)
- verifiedHeaders, _ := lru.NewARC(inmemorySnapshots)
- fakeEngine = &XDPoS{
- config: conf,
- db: db,
- BlockSigners: BlockSigners,
- recents: recents,
- signatures: signatures,
- verifiedHeaders: verifiedHeaders,
- validatorSignatures: validatorSignatures,
- proposals: make(map[common.Address]bool),
- }
- return fakeEngine
-}
-
-// Author implements consensus.Engine, returning the Ethereum address recovered
-// from the signature in the header's extra-data section.
-func (c *XDPoS) Author(header *types.Header) (common.Address, error) {
- return ecrecover(header, c.signatures)
-}
-
-// Get signer coinbase
-func (c *XDPoS) Signer() common.Address { return c.signer }
-
-// VerifyHeader checks whether a header conforms to the consensus rules.
-func (c *XDPoS) VerifyHeader(chain consensus.ChainReader, header *types.Header, fullVerify bool) error {
- return c.verifyHeaderWithCache(chain, header, nil, fullVerify)
-}
-
-// VerifyHeaders is similar to VerifyHeader, but verifies a batch of headers. The
-// method returns a quit channel to abort the operations and a results channel to
-// retrieve the async verifications (the order is that of the input slice).
-func (c *XDPoS) VerifyHeaders(chain consensus.ChainReader, headers []*types.Header, fullVerifies []bool) (chan<- struct{}, <-chan error) {
- abort := make(chan struct{})
- results := make(chan error, len(headers))
-
- go func() {
- for i, header := range headers {
- err := c.verifyHeaderWithCache(chain, header, headers[:i], fullVerifies[i])
-
- select {
- case <-abort:
- return
- case results <- err:
- }
- }
- }()
- return abort, results
-}
-
-func (c *XDPoS) verifyHeaderWithCache(chain consensus.ChainReader, header *types.Header, parents []*types.Header, fullVerify bool) error {
- _, check := c.verifiedHeaders.Get(header.Hash())
- if check {
- return nil
- }
- err := c.verifyHeader(chain, header, parents, fullVerify)
- if err == nil {
- c.verifiedHeaders.Add(header.Hash(), true)
- }
- return err
-}
-
-// verifyHeader checks whether a header conforms to the consensus rules.The
-// caller may optionally pass in a batch of parents (ascending order) to avoid
-// looking those up from the database. This is useful for concurrently verifying
-// a batch of new headers.
-func (c *XDPoS) verifyHeader(chain consensus.ChainReader, header *types.Header, parents []*types.Header, fullVerify bool) error {
- // If we're running a engine faking, accept any block as valid
- if c.config.SkipValidation {
- return nil
- }
- if common.IsTestnet {
- fullVerify = false
- }
- if header.Number == nil {
- return errUnknownBlock
- }
- number := header.Number.Uint64()
- if fullVerify {
- if header.Number.Uint64() > c.config.Epoch && len(header.Validator) == 0 {
- return consensus.ErrNoValidatorSignature
- }
- // Don't waste time checking blocks from the future
- if header.Time.Cmp(big.NewInt(time.Now().Unix())) > 0 {
- return consensus.ErrFutureBlock
- }
- }
- // Checkpoint blocks need to enforce zero beneficiary
- checkpoint := (number % c.config.Epoch) == 0
- if checkpoint && header.Coinbase != (common.Address{}) {
- return errInvalidCheckpointBeneficiary
- }
-
- // Nonces must be 0x00..0 or 0xff..f, zeroes enforced on checkpoints
- if !bytes.Equal(header.Nonce[:], nonceAuthVote) && !bytes.Equal(header.Nonce[:], nonceDropVote) {
- return errInvalidVote
- }
- if checkpoint && !bytes.Equal(header.Nonce[:], nonceDropVote) {
- return errInvalidCheckpointVote
- }
- // Check that the extra-data contains both the vanity and signature
- if len(header.Extra) < extraVanity {
- return errMissingVanity
- }
- if len(header.Extra) < extraVanity+extraSeal {
- return errMissingSignature
- }
- // Ensure that the extra-data contains a signer list on checkpoint, but none otherwise
- signersBytes := len(header.Extra) - extraVanity - extraSeal
- if !checkpoint && signersBytes != 0 {
- return errExtraSigners
- }
- if checkpoint && signersBytes%common.AddressLength != 0 {
- return errInvalidCheckpointSigners
- }
- // Ensure that the mix digest is zero as we don't have fork protection currently
- if header.MixDigest != (common.Hash{}) {
- return errInvalidMixDigest
- }
- // Ensure that the block doesn't contain any uncles which are meaningless in XDPoS
- if header.UncleHash != uncleHash {
- return errInvalidUncleHash
- }
- // If all checks passed, validate any special fields for hard forks
- if err := misc.VerifyForkHashes(chain.Config(), header, false); err != nil {
- return err
- }
- // All basic checks passed, verify cascading fields
- return c.verifyCascadingFields(chain, header, parents, fullVerify)
-}
-
-// verifyCascadingFields verifies all the header fields that are not standalone,
-// rather depend on a batch of previous headers. The caller may optionally pass
-// in a batch of parents (ascending order) to avoid looking those up from the
-// database. This is useful for concurrently verifying a batch of new headers.
-func (c *XDPoS) verifyCascadingFields(chain consensus.ChainReader, header *types.Header, parents []*types.Header, fullVerify bool) error {
- // The genesis block is the always valid dead-end
- number := header.Number.Uint64()
- if number == 0 {
- return nil
- }
- // Ensure that the block's timestamp isn't too close to it's parent
- var parent *types.Header
- if len(parents) > 0 {
- parent = parents[len(parents)-1]
- } else {
- parent = chain.GetHeader(header.ParentHash, number-1)
- }
- if parent == nil || parent.Number.Uint64() != number-1 || parent.Hash() != header.ParentHash {
- return consensus.ErrUnknownAncestor
- }
- if parent.Time.Uint64()+c.config.Period > header.Time.Uint64() {
- return ErrInvalidTimestamp
- }
-
- if number%c.config.Epoch != 0 {
- return c.verifySeal(chain, header, parents, fullVerify)
- }
-
- /*
- BUG: snapshot returns wrong signers sometimes
- when it happens we get the signers list by requesting smart contract
- */
- // Retrieve the snapshot needed to verify this header and cache it
- snap, err := c.snapshot(chain, number-1, header.ParentHash, parents)
- if err != nil {
- return err
- }
-
- signers := snap.GetSigners()
- err = c.checkSignersOnCheckpoint(chain, header, signers)
- if err == nil {
- return c.verifySeal(chain, header, parents, fullVerify)
- }
-
- signers, err = c.GetSignersFromContract(chain, header)
- if err != nil {
- return err
- }
- err = c.checkSignersOnCheckpoint(chain, header, signers)
- if err == nil {
- return c.verifySeal(chain, header, parents, fullVerify)
- }
-
- return err
-}
-
-func (c *XDPoS) checkSignersOnCheckpoint(chain consensus.ChainReader, header *types.Header, signers []common.Address) error {
- number := header.Number.Uint64()
- // ignore signerCheck at checkpoint block 14458500 due to wrong snapshot at gap 14458495
- if number == common.IgnoreSignerCheckBlock {
- return nil
- }
- penPenalties := []common.Address{}
- if c.HookPenalty != nil || c.HookPenaltyTIPSigning != nil {
- var err error
- if chain.Config().IsTIPSigning(header.Number) {
- penPenalties, err = c.HookPenaltyTIPSigning(chain, header, signers)
- } else {
- penPenalties, err = c.HookPenalty(chain, number)
- }
- if err != nil {
- return err
- }
- for _, address := range penPenalties {
- log.Debug("Penalty Info", "address", address, "number", number)
- }
- bytePenalties := common.ExtractAddressToBytes(penPenalties)
- if !bytes.Equal(header.Penalties, bytePenalties) {
- return errInvalidCheckpointPenalties
- }
- }
- signers = common.RemoveItemFromArray(signers, penPenalties)
- for i := 1; i <= common.LimitPenaltyEpoch; i++ {
- if number > uint64(i)*c.config.Epoch {
- signers = RemovePenaltiesFromBlock(chain, signers, number-uint64(i)*c.config.Epoch)
- }
- }
- extraSuffix := len(header.Extra) - extraSeal
- masternodesFromCheckpointHeader := common.ExtractAddressFromBytes(header.Extra[extraVanity:extraSuffix])
- validSigners := compareSignersLists(masternodesFromCheckpointHeader, signers)
-
- if !validSigners {
- log.Error("Masternodes lists are different in checkpoint header and snapshot", "number", number, "masternodes_from_checkpoint_header", masternodesFromCheckpointHeader, "masternodes_in_snapshot", signers, "penList", penPenalties)
- return errInvalidCheckpointSigners
- }
- if c.HookVerifyMNs != nil {
- err := c.HookVerifyMNs(header, signers)
- if err != nil {
- return err
- }
- }
-
- return nil
-}
-
-// compare 2 signers lists
-// return true if they are same elements, otherwise return false
-func compareSignersLists(list1 []common.Address, list2 []common.Address) bool {
- if len(list1) == 0 && len(list2) == 0 {
- return true
- }
- sort.Slice(list1, func(i, j int) bool {
- return list1[i].String() <= list1[j].String()
- })
- sort.Slice(list2, func(i, j int) bool {
- return list2[i].String() <= list2[j].String()
- })
- return reflect.DeepEqual(list1, list2)
-}
-
-func (c *XDPoS) GetSnapshot(chain consensus.ChainReader, header *types.Header) (*Snapshot, error) {
- number := header.Number.Uint64()
- log.Trace("take snapshot", "number", number, "hash", header.Hash())
- snap, err := c.snapshot(chain, number, header.Hash(), nil)
- if err != nil {
- return nil, err
- }
- return snap, nil
-}
-
-func (c *XDPoS) StoreSnapshot(snap *Snapshot) error {
- return snap.store(c.db)
-}
-
-func position(list []common.Address, x common.Address) int {
- for i, item := range list {
- if item == x {
- return i
- }
- }
- return -1
-}
-
-func (c *XDPoS) GetMasternodes(chain consensus.ChainReader, header *types.Header) []common.Address {
- n := header.Number.Uint64()
- e := c.config.Epoch
- switch {
- case n%e == 0:
- return c.GetMasternodesFromCheckpointHeader(header, n, e)
- case n%e != 0:
- h := chain.GetHeaderByNumber(n - (n % e))
- return c.GetMasternodesFromCheckpointHeader(h, n, e)
- default:
- return []common.Address{}
- }
-}
-
-func (c *XDPoS) GetPeriod() uint64 { return c.config.Period }
-
-func whoIsCreator(snap *Snapshot, header *types.Header) (common.Address, error) {
- if header.Number.Uint64() == 0 {
- return common.Address{}, errors.New("Don't take block 0")
- }
- m, err := ecrecover(header, snap.sigcache)
- if err != nil {
- return common.Address{}, err
- }
- return m, nil
-}
-
-func (c *XDPoS) YourTurn(chain consensus.ChainReader, parent *types.Header, signer common.Address) (int, int, int, bool, error) {
- masternodes := c.GetMasternodes(chain, parent)
-
- // if common.IsTestnet {
- // // Only three mns hard code for XDC testnet.
- // masternodes = []common.Address{
- // common.HexToAddress("0x3Ea0A3555f9B1dE983572BfF6444aeb1899eC58C"),
- // common.HexToAddress("0x4F7900282F3d371d585ab1361205B0940aB1789C"),
- // common.HexToAddress("0x942a5885A8844Ee5587C8AC5e371Fc39FFE61896"),
- // }
- // }
-
- snap, err := c.GetSnapshot(chain, parent)
- if err != nil {
- log.Warn("Failed when trying to commit new work", "err", err)
- return 0, -1, -1, false, err
- }
- if len(masternodes) == 0 {
- return 0, -1, -1, false, errors.New("Masternodes not found")
- }
- pre := common.Address{}
- // masternode[0] has chance to create block 1
- preIndex := -1
- if parent.Number.Uint64() != 0 {
- pre, err = whoIsCreator(snap, parent)
- if err != nil {
- return 0, 0, 0, false, err
- }
- preIndex = position(masternodes, pre)
- }
- curIndex := position(masternodes, signer)
- if signer == c.signer {
- log.Debug("Masternodes cycle info", "number of masternodes", len(masternodes), "previous", pre, "position", preIndex, "current", signer, "position", curIndex)
- }
- for i, s := range masternodes {
- log.Debug("Masternode:", "index", i, "address", s.String())
- }
- if (preIndex+1)%len(masternodes) == curIndex {
- return len(masternodes), preIndex, curIndex, true, nil
- }
- return len(masternodes), preIndex, curIndex, false, nil
-}
-
-// snapshot retrieves the authorization snapshot at a given point in time.
-func (c *XDPoS) snapshot(chain consensus.ChainReader, number uint64, hash common.Hash, parents []*types.Header) (*Snapshot, error) {
- // Search for a snapshot in memory or on disk for checkpoints
- var (
- headers []*types.Header
- snap *Snapshot
- )
- for snap == nil {
- // If an in-memory snapshot was found, use that
- if s, ok := c.recents.Get(hash); ok {
- snap = s.(*Snapshot)
- break
- }
- // If an on-disk checkpoint snapshot can be found, use that
- // checkpoint snapshot = checkpoint - gap
- if (number+c.config.Gap)%c.config.Epoch == 0 {
- if s, err := loadSnapshot(c.config, c.signatures, c.db, hash); err == nil {
- log.Trace("Loaded voting snapshot form disk", "number", number, "hash", hash)
- snap = s
- break
- }
- }
- // If we're at block zero, make a snapshot
- if number == 0 {
- genesis := chain.GetHeaderByNumber(0)
- if err := c.VerifyHeader(chain, genesis, true); err != nil {
- return nil, err
- }
- signers := make([]common.Address, (len(genesis.Extra)-extraVanity-extraSeal)/common.AddressLength)
- for i := 0; i < len(signers); i++ {
- copy(signers[i][:], genesis.Extra[extraVanity+i*common.AddressLength:])
- }
- snap = newSnapshot(c.config, c.signatures, 0, genesis.Hash(), signers)
- if err := snap.store(c.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 {
- return nil, consensus.ErrUnknownAncestor
- }
- }
- headers = append(headers, header)
- number, hash = number-1, header.ParentHash
- }
- // 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 err != nil {
- return nil, err
- }
- c.recents.Add(snap.Hash, snap)
-
- // If we've generated a new checkpoint snapshot, save to disk
- if (snap.Number+c.config.Gap)%c.config.Epoch == 0 {
- if err = snap.store(c.db); err != nil {
- return nil, err
- }
- log.Trace("Stored voting snapshot to disk", "number", snap.Number, "hash", snap.Hash)
- }
- return snap, err
-}
-
-// VerifyUncles implements consensus.Engine, always returning an error for any
-// uncles as this consensus mechanism doesn't permit uncles.
-func (c *XDPoS) VerifyUncles(chain consensus.ChainReader, block *types.Block) error {
- if len(block.Uncles()) > 0 {
- return errors.New("uncles not allowed")
- }
- return nil
-}
-
-// VerifySeal implements consensus.Engine, checking whether the signature contained
-// in the header satisfies the consensus protocol requirements.
-func (c *XDPoS) VerifySeal(chain consensus.ChainReader, header *types.Header) error {
- return c.verifySeal(chain, header, nil, true)
-}
-
-// verifySeal checks whether the signature contained in the header satisfies the
-// consensus protocol requirements. The method accepts an optional list of parent
-// headers that aren't yet part of the local blockchain to generate the snapshots
-// from.
-// verifySeal also checks the pair of creator-validator set in the header satisfies
-// the double validation.
-func (c *XDPoS) verifySeal(chain consensus.ChainReader, header *types.Header, parents []*types.Header, fullVerify bool) error {
- // Verifying the genesis block is not supported
- number := header.Number.Uint64()
- if number == 0 {
- return errUnknownBlock
- }
- // Retrieve the snapshot needed to verify this header and cache it
- snap, err := c.snapshot(chain, number-1, header.ParentHash, parents)
- if err != nil {
- return err
- }
-
- // Resolve the authorization key and check against signers
- creator, err := ecrecover(header, c.signatures)
- if err != nil {
- return err
- }
- var parent *types.Header
- if len(parents) > 0 {
- parent = parents[len(parents)-1]
- } else {
- parent = chain.GetHeader(header.ParentHash, number-1)
- }
- difficulty := c.calcDifficulty(chain, parent, creator)
- log.Debug("verify seal block", "number", header.Number, "hash", header.Hash(), "block difficulty", header.Difficulty, "calc difficulty", difficulty, "creator", creator)
- // Ensure that the block's difficulty is meaningful (may not be correct at this point)
- if number > 0 {
- if header.Difficulty.Int64() != difficulty.Int64() {
- return errInvalidDifficulty
- }
- }
- masternodes := c.GetMasternodes(chain, header)
- mstring := []string{}
- for _, m := range masternodes {
- mstring = append(mstring, m.String())
- }
- nstring := []string{}
- for _, n := range snap.GetSigners() {
- nstring = append(nstring, n.String())
- }
- if _, ok := snap.Signers[creator]; !ok {
- valid := false
- for _, m := range masternodes {
- if m == creator {
- valid = true
- break
- }
- }
- if !valid {
- log.Debug("Unauthorized creator found", "block number", number, "creator", creator.String(), "masternodes", mstring, "snapshot from parent block", nstring)
- return errUnauthorized
- }
- }
- if len(masternodes) > 1 {
- for seen, recent := range snap.Recents {
- if recent == creator {
- // Signer is among recents, only fail if the current block doesn't shift it out
- // There is only case that we don't allow signer to create two continuous blocks.
- if limit := uint64(2); seen > number-limit {
- // Only take into account the non-epoch blocks
- if number%c.config.Epoch != 0 {
- return errUnauthorized
- }
- }
- }
- }
- }
-
- // header must contain validator info following double validation design
- // start checking from epoch 2nd.
- if header.Number.Uint64() > c.config.Epoch && fullVerify {
- validator, err := c.RecoverValidator(header)
- if err != nil {
- return err
- }
-
- // verify validator
- assignedValidator, err := c.GetValidator(creator, chain, header)
- if err != nil {
- return err
- }
- if validator != assignedValidator {
- log.Debug("Bad block detected. Header contains wrong pair of creator-validator", "creator", creator, "assigned validator", assignedValidator, "wrong validator", validator)
- return errFailedDoubleValidation
- }
- }
- return nil
-}
-
-func (c *XDPoS) GetValidator(creator common.Address, chain consensus.ChainReader, header *types.Header) (common.Address, error) {
- epoch := c.config.Epoch
- no := header.Number.Uint64()
- cpNo := no
- if no%epoch != 0 {
- cpNo = no - (no % epoch)
- }
- if cpNo == 0 {
- return common.Address{}, nil
- }
- cpHeader := chain.GetHeaderByNumber(cpNo)
- if cpHeader == nil {
- if no%epoch == 0 {
- cpHeader = header
- } else {
- return common.Address{}, fmt.Errorf("couldn't find checkpoint header")
- }
- }
- m, err := GetM1M2FromCheckpointHeader(cpHeader, header, chain.Config())
- if err != nil {
- return common.Address{}, err
- }
- return m[creator], nil
-}
-
-// Prepare implements consensus.Engine, preparing all the consensus fields of the
-// header for running the transactions on top.
-func (c *XDPoS) Prepare(chain consensus.ChainReader, header *types.Header) error {
- // If the block isn't a checkpoint, cast a random vote (good enough for now)
- header.Coinbase = common.Address{}
- header.Nonce = types.BlockNonce{}
-
- number := header.Number.Uint64()
- // Assemble the voting snapshot to check which votes make sense
- snap, err := c.snapshot(chain, number-1, header.ParentHash, nil)
- if err != nil {
- return err
- }
- if number%c.config.Epoch != 0 {
- c.lock.RLock()
-
- // Gather all the proposals that make sense voting on
- addresses := make([]common.Address, 0, len(c.proposals))
- for address, authorize := range c.proposals {
- if snap.validVote(address, authorize) {
- addresses = append(addresses, address)
- }
- }
- // If there's pending proposals, cast a vote on them
- if len(addresses) > 0 {
- header.Coinbase = addresses[rand.Intn(len(addresses))]
- if c.proposals[header.Coinbase] {
- copy(header.Nonce[:], nonceAuthVote)
- } else {
- copy(header.Nonce[:], nonceDropVote)
- }
- }
- c.lock.RUnlock()
- }
- parent := chain.GetHeader(header.ParentHash, number-1)
- if parent == nil {
- return consensus.ErrUnknownAncestor
- }
- // Set the correct difficulty
- header.Difficulty = c.calcDifficulty(chain, parent, c.signer)
- log.Debug("CalcDifficulty ", "number", header.Number, "difficulty", header.Difficulty)
- // Ensure the extra data has all it's components
- if len(header.Extra) < extraVanity {
- header.Extra = append(header.Extra, bytes.Repeat([]byte{0x00}, extraVanity-len(header.Extra))...)
- }
- header.Extra = header.Extra[:extraVanity]
- masternodes := snap.GetSigners()
- if number >= c.config.Epoch && number%c.config.Epoch == 0 {
- if c.HookPenalty != nil || c.HookPenaltyTIPSigning != nil {
- var penMasternodes []common.Address = nil
- var err error = nil
- if chain.Config().IsTIPSigning(header.Number) {
- penMasternodes, err = c.HookPenaltyTIPSigning(chain, header, masternodes)
- } else {
- penMasternodes, err = c.HookPenalty(chain, number)
- }
- if err != nil {
- return err
- }
- if len(penMasternodes) > 0 {
- // penalize bad masternode(s)
- masternodes = common.RemoveItemFromArray(masternodes, penMasternodes)
- for _, address := range penMasternodes {
- log.Debug("Penalty status", "address", address, "number", number)
- }
- header.Penalties = common.ExtractAddressToBytes(penMasternodes)
- }
- }
- // Prevent penalized masternode(s) within 4 recent epochs
- for i := 1; i <= common.LimitPenaltyEpoch; i++ {
- if number > uint64(i)*c.config.Epoch {
- masternodes = RemovePenaltiesFromBlock(chain, masternodes, number-uint64(i)*c.config.Epoch)
- }
- }
- for _, masternode := range masternodes {
- header.Extra = append(header.Extra, masternode[:]...)
- }
- if c.HookValidator != nil {
- validators, err := c.HookValidator(header, masternodes)
- if err != nil {
- return err
- }
- header.Validators = validators
- }
- }
- header.Extra = append(header.Extra, make([]byte, extraSeal)...)
-
- // Mix digest is reserved for now, set to empty
- header.MixDigest = common.Hash{}
-
- // Ensure the timestamp has the correct delay
-
- header.Time = new(big.Int).Add(parent.Time, new(big.Int).SetUint64(c.config.Period))
- if header.Time.Int64() < time.Now().Unix() {
- header.Time = big.NewInt(time.Now().Unix())
- }
- return nil
-}
-
-func (c *XDPoS) UpdateMasternodes(chain consensus.ChainReader, header *types.Header, ms []Masternode) error {
- number := header.Number.Uint64()
- log.Trace("take snapshot", "number", number, "hash", header.Hash())
- // get snapshot
- snap, err := c.snapshot(chain, number, header.Hash(), nil)
- if err != nil {
- return err
- }
- newMasternodes := make(map[common.Address]struct{})
- for _, m := range ms {
- newMasternodes[m.Address] = struct{}{}
- }
- snap.Signers = newMasternodes
- nm := []string{}
- for _, n := range ms {
- nm = append(nm, n.Address.String())
- }
- c.recents.Add(snap.Hash, snap)
- log.Info("New set of masternodes has been updated to snapshot", "number", snap.Number, "hash", snap.Hash, "new masternodes", nm)
- return nil
-}
-
-// Finalize implements consensus.Engine, ensuring no uncles are set, nor block
-// rewards given, and returns the final block.
-func (c *XDPoS) Finalize(chain consensus.ChainReader, header *types.Header, state *state.StateDB, parentState *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) {
- // set block reward
- number := header.Number.Uint64()
- rCheckpoint := chain.Config().XDPoS.RewardCheckpoint
-
- // _ = c.CacheData(header, txs, receipts)
-
- if c.HookReward != nil && number%rCheckpoint == 0 {
- err, rewards := c.HookReward(chain, state, parentState, header)
- if err != nil {
- return nil, err
- }
- if len(common.StoreRewardFolder) > 0 {
- data, err := json.Marshal(rewards)
- if err == nil {
- err = ioutil.WriteFile(filepath.Join(common.StoreRewardFolder, header.Number.String()+"."+header.Hash().Hex()), data, 0644)
- }
- if err != nil {
- log.Error("Error when save reward info ", "number", header.Number, "hash", header.Hash().Hex(), "err", err)
- }
- }
- }
-
- // the state remains as is and uncles are dropped
- header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number))
- header.UncleHash = types.CalcUncleHash(nil)
-
- // Assemble and return the final block for sealing
- return types.NewBlock(header, txs, nil, receipts), nil
-}
-
-// Authorize injects a private key into the consensus engine to mint new blocks
-// with.
-func (c *XDPoS) Authorize(signer common.Address, signFn clique.SignerFn) {
- c.lock.Lock()
- defer c.lock.Unlock()
-
- c.signer = signer
- c.signFn = signFn
-}
-
-// Seal implements consensus.Engine, attempting to create a sealed block using
-// the local signing credentials.
-func (c *XDPoS) Seal(chain consensus.ChainReader, block *types.Block, stop <-chan struct{}) (*types.Block, error) {
- header := block.Header()
-
- // Sealing the genesis block is not supported
- number := header.Number.Uint64()
- if number == 0 {
- return nil, errUnknownBlock
- }
- // For 0-period chains, refuse to seal empty blocks (no reward but would spin sealing)
- // checkpoint blocks have no tx
- if c.config.Period == 0 && len(block.Transactions()) == 0 && number%c.config.Epoch != 0 {
- return nil, errWaitTransactions
- }
- // Don't hold the signer fields for the entire sealing procedure
- c.lock.RLock()
- signer, signFn := c.signer, c.signFn
- c.lock.RUnlock()
-
- // Bail out if we're unauthorized to sign a block
- snap, err := c.snapshot(chain, number-1, header.ParentHash, nil)
- if err != nil {
- return nil, err
- }
- masternodes := c.GetMasternodes(chain, header)
- if _, authorized := snap.Signers[signer]; !authorized {
- valid := false
- for _, m := range masternodes {
- if m == signer {
- valid = true
- break
- }
- }
- if !valid {
- return nil, errUnauthorized
- }
- }
- // If we're amongst the recent signers, wait for the next block
- // only check recent signers if there are more than one signer.
- if len(masternodes) > 1 {
- for seen, recent := range snap.Recents {
- if recent == signer {
- // Signer is among recents, only wait if the current block doesn't shift it out
- // There is only case that we don't allow signer to create two continuous blocks.
- if limit := uint64(2); number < limit || seen > number-limit {
- // Only take into account the non-epoch blocks
- if number%c.config.Epoch != 0 {
- log.Info("Signed recently, must wait for others ", "len(masternodes)", len(masternodes), "number", number, "limit", limit, "seen", seen, "recent", recent.String(), "snap.Recents", snap.Recents)
- <-stop
- return nil, nil
- }
- }
- }
- }
- }
- select {
- case <-stop:
- return nil, nil
- default:
- }
- // Sign all the things!
- sighash, err := signFn(accounts.Account{Address: signer}, sigHash(header).Bytes())
- if err != nil {
- return nil, err
- }
- copy(header.Extra[len(header.Extra)-extraSeal:], sighash)
- m2, err := c.GetValidator(signer, chain, header)
- if err != nil {
- return nil, fmt.Errorf("can't get block validator: %v", err)
- }
- if m2 == signer {
- header.Validator = sighash
- }
- return block.WithSeal(header), nil
-}
-
-// CalcDifficulty is the difficulty adjustment algorithm. It returns the difficulty
-// that a new block should have based on the previous blocks in the chain and the
-// current signer.
-func (c *XDPoS) CalcDifficulty(chain consensus.ChainReader, time uint64, parent *types.Header) *big.Int {
- return c.calcDifficulty(chain, parent, c.signer)
-}
-
-func (c *XDPoS) calcDifficulty(chain consensus.ChainReader, parent *types.Header, signer common.Address) *big.Int {
- // If we're running a engine faking, skip calculation
- if c.config.SkipValidation {
- return big.NewInt(1)
- }
- len, preIndex, curIndex, _, err := c.YourTurn(chain, parent, signer)
- if err != nil {
- return big.NewInt(int64(len + curIndex - preIndex))
- }
- return big.NewInt(int64(len - Hop(len, preIndex, curIndex)))
-}
+/*
+ Eth Consensus engine interface implementation
+*/
// APIs implements consensus.Engine, returning the user facing RPC API to allow
// controlling the signer voting.
func (c *XDPoS) APIs(chain consensus.ChainReader) []rpc.API {
@@ -1136,227 +96,207 @@ func (c *XDPoS) APIs(chain consensus.ChainReader) []rpc.API {
}}
}
+// Author implements consensus.Engine, returning the Ethereum address recovered
+// from the signature in the header's extra-data section.
+func (c *XDPoS) Author(header *types.Header) (common.Address, error) {
+ switch params.BlockConsensusVersion(header.Number) {
+ default: // Default "1.0"
+ return c.EngineV1.Author(header)
+ }
+}
+
+// VerifyHeader checks whether a header conforms to the consensus rules.
+func (c *XDPoS) VerifyHeader(chain consensus.ChainReader, header *types.Header, fullVerify bool) error {
+ switch params.BlockConsensusVersion(header.Number) {
+ default: // Default "1.0"
+ return c.EngineV1.VerifyHeader(chain, header, fullVerify)
+ }
+}
+
+// VerifyHeaders is similar to VerifyHeader, but verifies a batch of headers. The
+// method returns a quit channel to abort the operations and a results channel to
+// retrieve the async verifications (the order is that of the input slice).
+func (c *XDPoS) VerifyHeaders(chain consensus.ChainReader, headers []*types.Header, fullVerifies []bool) (chan<- struct{}, <-chan error) {
+ // TODO: (Hashlab) This funciton is a special case
+ return c.EngineV1.VerifyHeaders(chain, headers, fullVerifies)
+}
+
+// VerifyUncles implements consensus.Engine, always returning an error for any
+// uncles as this consensus mechanism doesn't permit uncles.
+func (c *XDPoS) VerifyUncles(chain consensus.ChainReader, block *types.Block) error {
+ switch params.BlockConsensusVersion(block.Number()) {
+ default: // Default "1.0"
+ return c.EngineV1.VerifyUncles(chain, block)
+ }
+}
+
+// VerifySeal implements consensus.Engine, checking whether the signature contained
+// in the header satisfies the consensus protocol requirements.
+func (c *XDPoS) VerifySeal(chain consensus.ChainReader, header *types.Header) error {
+ switch params.BlockConsensusVersion(header.Number) {
+ default: // Default "1.0"
+ return c.EngineV1.VerifySeal(chain, header)
+ }
+}
+
+// Prepare implements consensus.Engine, preparing all the consensus fields of the
+// header for running the transactions on top.
+func (c *XDPoS) Prepare(chain consensus.ChainReader, header *types.Header) error {
+ switch params.BlockConsensusVersion(header.Number) {
+ default: // Default "1.0"
+ return c.EngineV1.Prepare(chain, header)
+ }
+}
+
+// Finalize implements consensus.Engine, ensuring no uncles are set, nor block
+// rewards given, and returns the final block.
+func (c *XDPoS) Finalize(chain consensus.ChainReader, header *types.Header, state *state.StateDB, parentState *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) {
+ switch params.BlockConsensusVersion(header.Number) {
+ default: // Default "1.0"
+ return c.EngineV1.Finalize(chain, header, state, parentState, txs, uncles, receipts)
+ }
+}
+
+// Seal implements consensus.Engine, attempting to create a sealed block using
+// the local signing credentials.
+func (c *XDPoS) Seal(chain consensus.ChainReader, block *types.Block, stop <-chan struct{}) (*types.Block, error) {
+ switch params.BlockConsensusVersion(block.Number()) {
+ default: // Default "1.0"
+ return c.EngineV1.Seal(chain, block, stop)
+ }
+}
+
+// CalcDifficulty is the difficulty adjustment algorithm. It returns the difficulty
+// that a new block should have based on the previous blocks in the chain and the
+// current signer.
+func (c *XDPoS) CalcDifficulty(chain consensus.ChainReader, time uint64, parent *types.Header) *big.Int {
+ switch params.BlockConsensusVersion(parent.Number) {
+ default: // Default "1.0"
+ return c.EngineV1.CalcDifficulty(chain, time, parent)
+ }
+}
+
+/*
+ XDC specific methods
+*/
+
+// Authorize injects a private key into the consensus engine to mint new blocks
+// with.
+func (c *XDPoS) Authorize(signer common.Address, signFn clique.SignerFn) {
+ // Authorize each consensus individually
+ c.EngineV1.Authorize(signer, signFn)
+}
+
+func (c *XDPoS) GetPeriod() uint64 {
+ return c.config.Period
+}
+
+func (c *XDPoS) IsAuthorisedAddress(header *types.Header, chain consensus.ChainReader, address common.Address) bool {
+ switch params.BlockConsensusVersion(header.Number) {
+ default: // Default "1.0"
+ return c.EngineV1.IsAuthorisedAddress(header, chain, address)
+ }
+}
+
+func (c *XDPoS) GetMasternodes(chain consensus.ChainReader, header *types.Header) []common.Address {
+ switch params.BlockConsensusVersion(header.Number) {
+ default: // Default "1.0"
+ return c.EngineV1.GetMasternodes(chain, header)
+ }
+}
+
+func (c *XDPoS) YourTurn(chain consensus.ChainReader, parent *types.Header, signer common.Address) (int, int, int, bool, error) {
+ switch params.BlockConsensusVersion(parent.Number) {
+ default: // Default "1.0"
+ return c.EngineV1.YourTurn(chain, parent, signer)
+ }
+}
+
+func (c *XDPoS) GetValidator(creator common.Address, chain consensus.ChainReader, header *types.Header) (common.Address, error) {
+ switch params.BlockConsensusVersion(header.Number) {
+ default: // Default "1.0"
+ return c.EngineV1.GetValidator(creator, chain, header)
+ }
+}
+
+func (c *XDPoS) UpdateMasternodes(chain consensus.ChainReader, header *types.Header, ms []utils.Masternode) error {
+ switch params.BlockConsensusVersion(header.Number) {
+ default: // Default "1.0"
+ return c.EngineV1.UpdateMasternodes(chain, header, ms)
+ }
+}
+
func (c *XDPoS) RecoverSigner(header *types.Header) (common.Address, error) {
- return ecrecover(header, c.signatures)
+ switch params.BlockConsensusVersion(header.Number) {
+ default: // Default "1.0"
+ return c.EngineV1.RecoverSigner(header)
+ }
}
func (c *XDPoS) RecoverValidator(header *types.Header) (common.Address, error) {
- // If the signature's already cached, return that
- hash := header.Hash()
- if address, known := c.validatorSignatures.Get(hash); known {
- return address.(common.Address), nil
+ switch params.BlockConsensusVersion(header.Number) {
+ default: // Default "1.0"
+ return c.EngineV1.RecoverValidator(header)
}
- // Retrieve the signature from the header.Validator
- // len equals 65 bytes
- if len(header.Validator) != extraSeal {
- return common.Address{}, consensus.ErrFailValidatorSignature
- }
- // 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:])
-
- c.validatorSignatures.Add(hash, signer)
- return signer, nil
}
// Get master nodes over extra data of previous checkpoint block.
func (c *XDPoS) 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)
- return []common.Address{}
+ switch params.BlockConsensusVersion(preCheckpointHeader.Number) {
+ default: // Default "1.0"
+ return c.EngineV1.GetMasternodesFromCheckpointHeader(preCheckpointHeader, n, e)
}
- masternodes := make([]common.Address, (len(preCheckpointHeader.Extra)-extraVanity-extraSeal)/common.AddressLength)
- for i := 0; i < len(masternodes); i++ {
- copy(masternodes[i][:], preCheckpointHeader.Extra[extraVanity+i*common.AddressLength:])
- }
-
- return masternodes
}
func (c *XDPoS) CacheData(header *types.Header, txs []*types.Transaction, receipts []*types.Receipt) []*types.Transaction {
- signTxs := []*types.Transaction{}
- for _, tx := range txs {
- if tx.IsSigningTransaction() {
- var b uint
- for _, r := range receipts {
- if r.TxHash == tx.Hash() {
- if len(r.PostState) > 0 {
- b = types.ReceiptStatusSuccessful
- } else {
- b = r.Status
- }
- break
- }
- }
-
- if b == types.ReceiptStatusFailed {
- continue
- }
-
- signTxs = append(signTxs, tx)
- }
+ switch params.BlockConsensusVersion(header.Number) {
+ default: // Default "1.0"
+ return c.EngineV1.CacheData(header, txs, receipts)
}
-
- log.Debug("Save tx signers to cache", "hash", header.Hash().String(), "number", header.Number, "len(txs)", len(signTxs))
- c.BlockSigners.Add(header.Hash(), signTxs)
-
- return signTxs
-}
-
-func (c *XDPoS) CacheSigner(hash common.Hash, txs []*types.Transaction) []*types.Transaction {
- signTxs := []*types.Transaction{}
- for _, tx := range txs {
- if tx.IsSigningTransaction() {
- signTxs = append(signTxs, tx)
- }
- }
- log.Debug("Save tx signers to cache", "hash", hash.String(), "len(txs)", len(signTxs))
- c.BlockSigners.Add(hash, signTxs)
- return signTxs
}
+// Same DB across all consensus engines
func (c *XDPoS) GetDb() ethdb.Database {
return c.db
}
-// Extract validators from byte array.
-func RemovePenaltiesFromBlock(chain consensus.ChainReader, masternodes []common.Address, epochNumber uint64) []common.Address {
- if epochNumber <= 0 {
- return masternodes
- }
- header := chain.GetHeaderByNumber(epochNumber)
- block := chain.GetBlock(header.Hash(), epochNumber)
- penalties := block.Penalties()
- if penalties != nil {
- prevPenalties := common.ExtractAddressFromBytes(penalties)
- masternodes = common.RemoveItemFromArray(masternodes, prevPenalties)
- }
- return masternodes
-}
-
-// 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
-}
-
-// Extract validators from byte array.
-func ExtractValidatorsFromBytes(byteValidators []byte) []int64 {
- lenValidator := len(byteValidators) / M2ByteLength
- var validators []int64
- for i := 0; i < lenValidator; i++ {
- trimByte := bytes.Trim(byteValidators[i*M2ByteLength:(i+1)*M2ByteLength], "\x00")
- intNumber, err := strconv.Atoi(string(trimByte))
- if err != nil {
- log.Error("Can not convert string to integer", "error", err)
- return []int64{}
- }
- validators = append(validators, int64(intNumber))
- }
-
- return validators
-}
-
-func Hop(len, pre, cur int) int {
- switch {
- case pre < cur:
- return cur - (pre + 1)
- case pre > cur:
- return (len - pre) + (cur - 1)
- default:
- return len - 1
+func (c *XDPoS) GetSnapshot(chain consensus.ChainReader, header *types.Header) (*utils.PublicApiSnapshot, error) {
+ switch params.BlockConsensusVersion(header.Number) {
+ default: // Default "1.0"
+ sp, err := c.EngineV1.GetSnapshot(chain, header)
+ // Convert to a standard PublicApiSnapshot type, otherwise it's a breaking change to API
+ return &utils.PublicApiSnapshot{
+ Number: sp.Number,
+ Hash: sp.Hash,
+ Signers: sp.Signers,
+ Recents: sp.Recents,
+ Votes: sp.Votes,
+ Tally: sp.Tally,
+ }, err
}
}
+func (c *XDPoS) GetAuthorisedSignersFromSnapshot(chain consensus.ChainReader, header *types.Header) ([]common.Address, error) {
+ switch params.BlockConsensusVersion(header.Number) {
+ default: // Default "1.0"
+ return c.EngineV1.GetAuthorisedSignersFromSnapshot(chain, header)
+ }
+}
+
+// TODO: (Hashlab) Can be further refactored
func (c *XDPoS) CheckMNTurn(chain consensus.ChainReader, parent *types.Header, signer common.Address) bool {
- masternodes := c.GetMasternodes(chain, parent)
-
- if common.IsTestnet {
- // Only three mns hard code for XDC testnet.
- masternodes = []common.Address{
- common.HexToAddress("0x3Ea0A3555f9B1dE983572BfF6444aeb1899eC58C"),
- common.HexToAddress("0x4F7900282F3d371d585ab1361205B0940aB1789C"),
- common.HexToAddress("0x942a5885A8844Ee5587C8AC5e371Fc39FFE61896"),
- }
+ switch params.BlockConsensusVersion(parent.Number) {
+ default: // Default "1.0"
+ return c.EngineV1.CheckMNTurn(chain, parent, signer)
}
-
- snap, err := c.GetSnapshot(chain, parent)
- if err != nil {
- log.Warn("Failed when trying to commit new work", "err", err)
- return false
- }
- if len(masternodes) == 0 {
- return false
- }
- pre := common.Address{}
- // masternode[0] has chance to create block 1
- preIndex := -1
- if parent.Number.Uint64() != 0 {
- pre, err = whoIsCreator(snap, parent)
- if err != nil {
- return false
- }
- preIndex = position(masternodes, pre)
- }
- curIndex := position(masternodes, signer)
- if (preIndex)%len(masternodes) == curIndex {
- return true
- }
- return false
}
-func (c *XDPoS) GetSignersFromContract(chain consensus.ChainReader, checkpointHeader *types.Header) ([]common.Address, error) {
- startGapBlockHeader := checkpointHeader
- number := checkpointHeader.Number.Uint64()
- for step := uint64(1); step <= chain.Config().XDPoS.Gap; step++ {
- startGapBlockHeader = chain.GetHeader(startGapBlockHeader.ParentHash, number-step)
- }
- signers, err := c.HookGetSignersFromContract(startGapBlockHeader.Hash())
- if err != nil {
- return []common.Address{}, fmt.Errorf("Can't get signers from Smart Contract . Err: %v", err)
- }
- return signers, nil
+// TODO: (Hashlab) Need further work on refactor this method
+func (c *XDPoS) CacheSigner(hash common.Hash, txs []*types.Transaction) []*types.Transaction {
+ return c.EngineV1.CacheSigner(hash, txs)
+}
+
+// TODO: (Hashlab)Get signer coinbase
+func (c *XDPoS) Signer() common.Address {
+ return c.EngineV1.Signer()
}
diff --git a/consensus/XDPoS/XDPoS_test.go b/consensus/XDPoS/XDPoS_test.go
index e00d3eed03..4d16514a00 100644
--- a/consensus/XDPoS/XDPoS_test.go
+++ b/consensus/XDPoS/XDPoS_test.go
@@ -6,6 +6,7 @@ import (
"testing"
"github.com/XinFinOrg/XDPoSChain/common"
+ "github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils"
"github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/params"
)
@@ -34,7 +35,7 @@ func TestGetM1M2FromCheckpointHeader(t *testing.T) {
currentHeader := &types.Header{
Number: big.NewInt(currentNumber),
}
- m1m2, moveM2, err := getM1M2(masternodes, validators, currentHeader, config)
+ m1m2, moveM2, err := utils.GetM1M2(masternodes, validators, currentHeader, config)
if err != nil {
t.Error("can't get m1m2", "err", err)
}
@@ -66,19 +67,19 @@ func TestCompareSignersLists(t *testing.T) {
common.StringToAddress("dddddddddddddddddddddddddddddddddddddddd"),
common.StringToAddress("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"),
}
- if !compareSignersLists(list1, list2) {
+ if !utils.CompareSignersLists(list1, list2) {
t.Error("list1 should be equal to list2", "list1", list1, "list2", list2)
}
- if compareSignersLists(list1, list3) {
+ if utils.CompareSignersLists(list1, list3) {
t.Error("list1 and list3 should not be same", "list1", list1, "list3", list3)
}
- if !compareSignersLists([]common.Address{}, []common.Address{}) {
+ if !utils.CompareSignersLists([]common.Address{}, []common.Address{}) {
t.Error("Failed with empty list")
}
- if !compareSignersLists([]common.Address{common.StringToAddress("cccccccccccccccccccccccccccccccccccccccc")}, []common.Address{common.StringToAddress("cccccccccccccccccccccccccccccccccccccccc")}) {
+ if !utils.CompareSignersLists([]common.Address{common.StringToAddress("cccccccccccccccccccccccccccccccccccccccc")}, []common.Address{common.StringToAddress("cccccccccccccccccccccccccccccccccccccccc")}) {
t.Error("Failed with list has only one signer")
}
- if compareSignersLists([]common.Address{common.StringToAddress("aaaaaaaaaaaaaaaa")}, []common.Address{common.StringToAddress("cccccccccccccccccccccccccccccccccccccccc")}) {
+ if utils.CompareSignersLists([]common.Address{common.StringToAddress("aaaaaaaaaaaaaaaa")}, []common.Address{common.StringToAddress("cccccccccccccccccccccccccccccccccccccccc")}) {
t.Error("Failed with list has only one signer")
}
}
diff --git a/consensus/XDPoS/api.go b/consensus/XDPoS/api.go
index 252ad07ff1..3db252bb15 100644
--- a/consensus/XDPoS/api.go
+++ b/consensus/XDPoS/api.go
@@ -16,11 +16,13 @@
package XDPoS
import (
+ "math/big"
+
"github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/consensus"
+ "github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils"
"github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/rpc"
- "math/big"
)
// API is a user facing RPC API to allow controlling the signer and voting
@@ -39,7 +41,7 @@ type NetworkInformation struct {
}
// GetSnapshot retrieves the state snapshot at a given block.
-func (api *API) GetSnapshot(number *rpc.BlockNumber) (*Snapshot, error) {
+func (api *API) GetSnapshot(number *rpc.BlockNumber) (*utils.PublicApiSnapshot, error) {
// Retrieve the requested block number (or current if none requested)
var header *types.Header
if number == nil || *number == rpc.LatestBlockNumber {
@@ -49,18 +51,18 @@ func (api *API) GetSnapshot(number *rpc.BlockNumber) (*Snapshot, error) {
}
// Ensure we have an actually valid block and return its snapshot
if header == nil {
- return nil, errUnknownBlock
+ return nil, utils.ErrUnknownBlock
}
- return api.XDPoS.snapshot(api.chain, header.Number.Uint64(), header.Hash(), nil)
+ return api.XDPoS.GetSnapshot(api.chain, header)
}
// GetSnapshotAtHash retrieves the state snapshot at a given block.
-func (api *API) GetSnapshotAtHash(hash common.Hash) (*Snapshot, error) {
+func (api *API) GetSnapshotAtHash(hash common.Hash) (*utils.PublicApiSnapshot, error) {
header := api.chain.GetHeaderByHash(hash)
if header == nil {
- return nil, errUnknownBlock
+ return nil, utils.ErrUnknownBlock
}
- return api.XDPoS.snapshot(api.chain, header.Number.Uint64(), header.Hash(), nil)
+ return api.XDPoS.GetSnapshot(api.chain, header)
}
// GetSigners retrieves the list of authorized signers at the specified block.
@@ -74,31 +76,22 @@ func (api *API) GetSigners(number *rpc.BlockNumber) ([]common.Address, error) {
}
// Ensure we have an actually valid block and return the signers from its snapshot
if header == nil {
- return nil, errUnknownBlock
+ return nil, utils.ErrUnknownBlock
}
- snap, err := api.XDPoS.snapshot(api.chain, header.Number.Uint64(), header.Hash(), nil)
- if err != nil {
- return nil, err
- }
- return snap.GetSigners(), nil
+
+ return api.XDPoS.GetAuthorisedSignersFromSnapshot(api.chain, header)
}
// GetSignersAtHash retrieves the state snapshot at a given block.
func (api *API) GetSignersAtHash(hash common.Hash) ([]common.Address, error) {
header := api.chain.GetHeaderByHash(hash)
if header == nil {
- return nil, errUnknownBlock
+ return nil, utils.ErrUnknownBlock
}
- snap, err := api.XDPoS.snapshot(api.chain, header.Number.Uint64(), header.Hash(), nil)
- if err != nil {
- return nil, err
- }
- return snap.GetSigners(), nil
+ return api.XDPoS.GetAuthorisedSignersFromSnapshot(api.chain, header)
}
func (api *API) NetworkInformation() NetworkInformation {
- api.XDPoS.lock.RLock()
- defer api.XDPoS.lock.RUnlock()
info := NetworkInformation{}
info.NetworkId = api.chain.Config().ChainId
info.XDCValidatorAddress = common.HexToAddress(common.MasternodeVotingSMC)
diff --git a/consensus/XDPoS/engines/engine_v1/engine.go b/consensus/XDPoS/engines/engine_v1/engine.go
new file mode 100644
index 0000000000..ec1be48a11
--- /dev/null
+++ b/consensus/XDPoS/engines/engine_v1/engine.go
@@ -0,0 +1,1145 @@
+package engine_v1
+
+import (
+ "bytes"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "io/ioutil"
+ "math/big"
+ "math/rand"
+ "path/filepath"
+ "sync"
+ "time"
+
+ "github.com/XinFinOrg/XDPoSChain/accounts"
+ "github.com/XinFinOrg/XDPoSChain/common"
+ "github.com/XinFinOrg/XDPoSChain/consensus"
+ "github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils"
+ "github.com/XinFinOrg/XDPoSChain/consensus/clique"
+ "github.com/XinFinOrg/XDPoSChain/consensus/misc"
+ "github.com/XinFinOrg/XDPoSChain/core/state"
+ "github.com/XinFinOrg/XDPoSChain/core/types"
+ "github.com/XinFinOrg/XDPoSChain/crypto"
+ "github.com/XinFinOrg/XDPoSChain/crypto/sha3"
+ "github.com/XinFinOrg/XDPoSChain/ethdb"
+ "github.com/XinFinOrg/XDPoSChain/log"
+ "github.com/XinFinOrg/XDPoSChain/params"
+ "github.com/XinFinOrg/XDPoSChain/rlp"
+ lru "github.com/hashicorp/golang-lru"
+)
+
+// 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 SigHash(header *types.Header) (hash common.Hash) {
+ return sigHash(header)
+}
+
+// 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
+}
+
+// XDPoS is the delegated-proof-of-stake consensus engine proposed to support the
+// Ethereum testnet following the Ropsten attacks.
+type XDPoS_v1 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
+ signatures *lru.ARCCache // Signatures of recent blocks to speed up mining
+ validatorSignatures *lru.ARCCache // Signatures of recent blocks to speed up mining
+ verifiedHeaders *lru.ARCCache
+ proposals map[common.Address]bool // Current list of proposals we are pushing
+
+ signer common.Address // Ethereum address of the signing key
+ signFn clique.SignerFn // Signer function to authorize hashes with
+ lock sync.RWMutex // Protects the signer fields
+
+ BlockSigners *lru.Cache
+ HookReward func(chain consensus.ChainReader, state *state.StateDB, parentState *state.StateDB, header *types.Header) (error, map[string]interface{})
+ HookPenalty func(chain consensus.ChainReader, blockNumberEpoc uint64) ([]common.Address, error)
+ HookPenaltyTIPSigning func(chain consensus.ChainReader, header *types.Header, candidate []common.Address) ([]common.Address, error)
+ HookValidator func(header *types.Header, signers []common.Address) ([]byte, error)
+ HookVerifyMNs func(header *types.Header, signers []common.Address) error
+ // GetXDCXService func() utils.TradingService
+ // GetLendingService func() utils.LendingService
+ HookGetSignersFromContract func(blockHash common.Hash) ([]common.Address, error)
+}
+
+// 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 {
+ // Set any missing consensus parameters to their defaults
+ conf := *config
+ if conf.Epoch == 0 {
+ conf.Epoch = utils.EpochLength
+ }
+ // Allocate the snapshot caches and create the engine
+ BlockSigners, _ := lru.New(utils.BlockSignersCacheLimit)
+ recents, _ := lru.NewARC(utils.InmemorySnapshots)
+ signatures, _ := lru.NewARC(utils.InmemorySnapshots)
+ validatorSignatures, _ := lru.NewARC(utils.InmemorySnapshots)
+ verifiedHeaders, _ := lru.NewARC(utils.InmemorySnapshots)
+ return &XDPoS_v1{
+ config: &conf,
+ db: db,
+ BlockSigners: BlockSigners,
+ recents: recents,
+ signatures: signatures,
+ verifiedHeaders: verifiedHeaders,
+ validatorSignatures: validatorSignatures,
+ proposals: make(map[common.Address]bool),
+ }
+}
+
+// Author implements consensus.Engine, returning the Ethereum address recovered
+// from the signature in the header's extra-data section.
+func (c *XDPoS_v1) Author(header *types.Header) (common.Address, error) {
+ return ecrecover(header, c.signatures)
+}
+
+// Get signer coinbase
+func (c *XDPoS_v1) Signer() common.Address { return c.signer }
+
+// VerifyHeader checks whether a header conforms to the consensus rules.
+func (c *XDPoS_v1) VerifyHeader(chain consensus.ChainReader, header *types.Header, fullVerify bool) error {
+ return c.verifyHeaderWithCache(chain, header, nil, fullVerify)
+}
+
+// VerifyHeaders is similar to VerifyHeader, but verifies a batch of headers. The
+// method returns a quit channel to abort the operations and a results channel to
+// retrieve the async verifications (the order is that of the input slice).
+func (c *XDPoS_v1) VerifyHeaders(chain consensus.ChainReader, headers []*types.Header, fullVerifies []bool) (chan<- struct{}, <-chan error) {
+ abort := make(chan struct{})
+ results := make(chan error, len(headers))
+
+ go func() {
+ for i, header := range headers {
+ err := c.verifyHeaderWithCache(chain, header, headers[:i], fullVerifies[i])
+
+ select {
+ case <-abort:
+ return
+ case results <- err:
+ }
+ }
+ }()
+ return abort, results
+}
+
+func (c *XDPoS_v1) verifyHeaderWithCache(chain consensus.ChainReader, header *types.Header, parents []*types.Header, fullVerify bool) error {
+ _, check := c.verifiedHeaders.Get(header.Hash())
+ if check {
+ return nil
+ }
+ err := c.verifyHeader(chain, header, parents, fullVerify)
+ if err == nil {
+ c.verifiedHeaders.Add(header.Hash(), true)
+ }
+ return err
+}
+
+// verifyHeader checks whether a header conforms to the consensus rules.The
+// caller may optionally pass in a batch of parents (ascending order) to avoid
+// looking those up from the database. This is useful for concurrently verifying
+// a batch of new headers.
+func (c *XDPoS_v1) verifyHeader(chain consensus.ChainReader, header *types.Header, parents []*types.Header, fullVerify bool) error {
+ if common.IsTestnet {
+ fullVerify = false
+ }
+ if header.Number == nil {
+ return utils.ErrUnknownBlock
+ }
+ number := header.Number.Uint64()
+ if fullVerify {
+ if header.Number.Uint64() > c.config.Epoch && len(header.Validator) == 0 {
+ return consensus.ErrNoValidatorSignature
+ }
+ // Don't waste time checking blocks from the future
+ if header.Time.Cmp(big.NewInt(time.Now().Unix())) > 0 {
+ return consensus.ErrFutureBlock
+ }
+ }
+ // Checkpoint blocks need to enforce zero beneficiary
+ checkpoint := (number % c.config.Epoch) == 0
+ if checkpoint && header.Coinbase != (common.Address{}) {
+ return utils.ErrInvalidCheckpointBeneficiary
+ }
+
+ // Nonces must be 0x00..0 or 0xff..f, zeroes enforced on checkpoints
+ if !bytes.Equal(header.Nonce[:], utils.NonceAuthVote) && !bytes.Equal(header.Nonce[:], utils.NonceDropVote) {
+ return utils.ErrInvalidVote
+ }
+ if checkpoint && !bytes.Equal(header.Nonce[:], utils.NonceDropVote) {
+ return utils.ErrInvalidCheckpointVote
+ }
+ // Check that the extra-data contains both the vanity and signature
+ if len(header.Extra) < utils.ExtraVanity {
+ return utils.ErrMissingVanity
+ }
+ if len(header.Extra) < utils.ExtraVanity+utils.ExtraSeal {
+ return utils.ErrMissingSignature
+ }
+ // Ensure that the extra-data contains a signer list on checkpoint, but none otherwise
+ signersBytes := len(header.Extra) - utils.ExtraVanity - utils.ExtraSeal
+ if !checkpoint && signersBytes != 0 {
+ return utils.ErrExtraSigners
+ }
+ if checkpoint && signersBytes%common.AddressLength != 0 {
+ return utils.ErrInvalidCheckpointSigners
+ }
+ // Ensure that the mix digest is zero as we don't have fork protection currently
+ if header.MixDigest != (common.Hash{}) {
+ return utils.ErrInvalidMixDigest
+ }
+ // Ensure that the block doesn't contain any uncles which are meaningless in XDPoS_v1
+ if header.UncleHash != utils.UncleHash {
+ return utils.ErrInvalidUncleHash
+ }
+ // If all checks passed, validate any special fields for hard forks
+ if err := misc.VerifyForkHashes(chain.Config(), header, false); err != nil {
+ return err
+ }
+ // All basic checks passed, verify cascading fields
+ return c.verifyCascadingFields(chain, header, parents, fullVerify)
+}
+
+// verifyCascadingFields verifies all the header fields that are not standalone,
+// rather depend on a batch of previous headers. The caller may optionally pass
+// in a batch of parents (ascending order) to avoid looking those up from the
+// database. This is useful for concurrently verifying a batch of new headers.
+func (c *XDPoS_v1) verifyCascadingFields(chain consensus.ChainReader, header *types.Header, parents []*types.Header, fullVerify bool) error {
+ // The genesis block is the always valid dead-end
+ number := header.Number.Uint64()
+ if number == 0 {
+ return nil
+ }
+ // Ensure that the block's timestamp isn't too close to it's parent
+ var parent *types.Header
+ if len(parents) > 0 {
+ parent = parents[len(parents)-1]
+ } else {
+ parent = chain.GetHeader(header.ParentHash, number-1)
+ }
+ if parent == nil || parent.Number.Uint64() != number-1 || parent.Hash() != header.ParentHash {
+ return consensus.ErrUnknownAncestor
+ }
+ if parent.Time.Uint64()+c.config.Period > header.Time.Uint64() {
+ return utils.ErrInvalidTimestamp
+ }
+
+ if number%c.config.Epoch != 0 {
+ return c.verifySeal(chain, header, parents, fullVerify)
+ }
+
+ /*
+ BUG: snapshot returns wrong signers sometimes
+ when it happens we get the signers list by requesting smart contract
+ */
+ // Retrieve the snapshot needed to verify this header and cache it
+ snap, err := c.snapshot(chain, number-1, header.ParentHash, parents)
+ if err != nil {
+ return err
+ }
+
+ signers := snap.GetSigners()
+ err = c.checkSignersOnCheckpoint(chain, header, signers)
+ if err == nil {
+ return c.verifySeal(chain, header, parents, fullVerify)
+ }
+
+ signers, err = c.getSignersFromContract(chain, header)
+ if err != nil {
+ return err
+ }
+ err = c.checkSignersOnCheckpoint(chain, header, signers)
+ if err == nil {
+ return c.verifySeal(chain, header, parents, fullVerify)
+ }
+
+ return err
+}
+
+func (c *XDPoS_v1) checkSignersOnCheckpoint(chain consensus.ChainReader, header *types.Header, signers []common.Address) error {
+ number := header.Number.Uint64()
+ // ignore signerCheck at checkpoint block 14458500 due to wrong snapshot at gap 14458495
+ if number == common.IgnoreSignerCheckBlock {
+ return nil
+ }
+ penPenalties := []common.Address{}
+ if c.HookPenalty != nil || c.HookPenaltyTIPSigning != nil {
+ var err error
+ if chain.Config().IsTIPSigning(header.Number) {
+ penPenalties, err = c.HookPenaltyTIPSigning(chain, header, signers)
+ } else {
+ penPenalties, err = c.HookPenalty(chain, number)
+ }
+ if err != nil {
+ return err
+ }
+ for _, address := range penPenalties {
+ log.Debug("Penalty Info", "address", address, "number", number)
+ }
+ bytePenalties := common.ExtractAddressToBytes(penPenalties)
+ if !bytes.Equal(header.Penalties, bytePenalties) {
+ return utils.ErrInvalidCheckpointPenalties
+ }
+ }
+ signers = common.RemoveItemFromArray(signers, penPenalties)
+ for i := 1; i <= common.LimitPenaltyEpoch; i++ {
+ if number > uint64(i)*c.config.Epoch {
+ signers = removePenaltiesFromBlock(chain, signers, number-uint64(i)*c.config.Epoch)
+ }
+ }
+ extraSuffix := len(header.Extra) - utils.ExtraSeal
+ masternodesFromCheckpointHeader := common.ExtractAddressFromBytes(header.Extra[utils.ExtraVanity:extraSuffix])
+ validSigners := utils.CompareSignersLists(masternodesFromCheckpointHeader, signers)
+
+ if !validSigners {
+ log.Error("Masternodes lists are different in checkpoint header and snapshot", "number", number, "masternodes_from_checkpoint_header", masternodesFromCheckpointHeader, "masternodes_in_snapshot", signers, "penList", penPenalties)
+ return utils.ErrInvalidCheckpointSigners
+ }
+ if c.HookVerifyMNs != nil {
+ err := c.HookVerifyMNs(header, signers)
+ if err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+func (c *XDPoS_v1) IsAuthorisedAddress(header *types.Header, chain consensus.ChainReader, address common.Address) bool {
+ snap, err := c.GetSnapshot(chain, header)
+ if err != nil {
+ log.Error("[IsAuthorisedAddress] Can't get snapshot with at ", "number", header.Number, "hash", header.Hash().Hex(), "err", err)
+ return false
+ }
+ if _, ok := snap.Signers[address]; ok {
+ return true
+ }
+ return false
+}
+
+func (c *XDPoS_v1) GetSnapshot(chain consensus.ChainReader, header *types.Header) (*SnapshotV1, error) {
+ number := header.Number.Uint64()
+ log.Trace("get snapshot", "number", number, "hash", header.Hash())
+ snap, err := c.snapshot(chain, number, header.Hash(), nil)
+ if err != nil {
+ return nil, err
+ }
+ return snap, nil
+}
+
+func (c *XDPoS_v1) GetAuthorisedSignersFromSnapshot(chain consensus.ChainReader, header *types.Header) ([]common.Address, error) {
+ snap, err := c.GetSnapshot(chain, header)
+ if err != nil {
+ return nil, err
+ }
+ return snap.GetSigners(), nil
+}
+
+func (c *XDPoS_v1) StoreSnapshot(snap *SnapshotV1) error {
+ return snap.store(c.db)
+}
+
+func position(list []common.Address, x common.Address) int {
+ for i, item := range list {
+ if item == x {
+ return i
+ }
+ }
+ return -1
+}
+
+func (c *XDPoS_v1) GetMasternodes(chain consensus.ChainReader, header *types.Header) []common.Address {
+ n := header.Number.Uint64()
+ e := c.config.Epoch
+ switch {
+ case n%e == 0:
+ return c.GetMasternodesFromCheckpointHeader(header, n, e)
+ case n%e != 0:
+ h := chain.GetHeaderByNumber(n - (n % e))
+ return c.GetMasternodesFromCheckpointHeader(h, n, e)
+ default:
+ return []common.Address{}
+ }
+}
+
+func (c *XDPoS_v1) GetPeriod() uint64 { return c.config.Period }
+
+func 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 := ecrecover(header, snap.sigcache)
+ if err != nil {
+ return common.Address{}, err
+ }
+ return m, nil
+}
+
+func (c *XDPoS_v1) YourTurn(chain consensus.ChainReader, parent *types.Header, signer common.Address) (int, int, int, bool, error) {
+ masternodes := c.GetMasternodes(chain, parent)
+
+ // if common.IsTestnet {
+ // // Only three mns hard code for XDC testnet.
+ // masternodes = []common.Address{
+ // common.HexToAddress("0x3Ea0A3555f9B1dE983572BfF6444aeb1899eC58C"),
+ // common.HexToAddress("0x4F7900282F3d371d585ab1361205B0940aB1789C"),
+ // common.HexToAddress("0x942a5885A8844Ee5587C8AC5e371Fc39FFE61896"),
+ // }
+ // }
+
+ snap, err := c.GetSnapshot(chain, parent)
+ if err != nil {
+ log.Warn("Failed when trying to commit new work", "err", err)
+ return 0, -1, -1, false, err
+ }
+ if len(masternodes) == 0 {
+ return 0, -1, -1, false, errors.New("Masternodes not found")
+ }
+ pre := common.Address{}
+ // masternode[0] has chance to create block 1
+ preIndex := -1
+ if parent.Number.Uint64() != 0 {
+ pre, err = whoIsCreator(snap, parent)
+ if err != nil {
+ return 0, 0, 0, false, err
+ }
+ preIndex = position(masternodes, pre)
+ }
+ curIndex := position(masternodes, signer)
+ if signer == c.signer {
+ log.Debug("Masternodes cycle info", "number of masternodes", len(masternodes), "previous", pre, "position", preIndex, "current", signer, "position", curIndex)
+ }
+ for i, s := range masternodes {
+ log.Debug("Masternode:", "index", i, "address", s.String())
+ }
+ if (preIndex+1)%len(masternodes) == curIndex {
+ return len(masternodes), preIndex, curIndex, true, nil
+ }
+ return len(masternodes), preIndex, curIndex, false, nil
+}
+
+// snapshot retrieves the authorization snapshot at a given point in time.
+func (c *XDPoS_v1) snapshot(chain consensus.ChainReader, number uint64, hash common.Hash, parents []*types.Header) (*SnapshotV1, error) {
+ // Search for a SnapshotV1 in memory or on disk for checkpoints
+ var (
+ headers []*types.Header
+ snap *SnapshotV1
+ )
+ for snap == nil {
+ // If an in-memory SnapshotV1 was found, use that
+ if s, ok := c.recents.Get(hash); ok {
+ snap = s.(*SnapshotV1)
+ break
+ }
+ // If an on-disk checkpoint snapshot can be found, use that
+ // checkpoint snapshot = checkpoint - gap
+ if (number+c.config.Gap)%c.config.Epoch == 0 {
+ if s, err := loadSnapshot(c.config, c.signatures, c.db, hash); err == nil {
+ log.Trace("Loaded voting snapshot form disk", "number", number, "hash", hash)
+ snap = s
+ break
+ }
+ }
+ // If we're at block zero, make a snapshot
+ if number == 0 {
+ genesis := chain.GetHeaderByNumber(0)
+ if err := c.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(c.config, c.signatures, 0, genesis.Hash(), signers)
+ if err := snap.store(c.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 {
+ return nil, consensus.ErrUnknownAncestor
+ }
+ }
+ headers = append(headers, header)
+ number, hash = number-1, header.ParentHash
+ }
+ // 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 err != nil {
+ return nil, err
+ }
+ c.recents.Add(snap.Hash, snap)
+
+ // If we've generated a new checkpoint snapshot, save to disk
+ if (snap.Number+c.config.Gap)%c.config.Epoch == 0 {
+ if err = snap.store(c.db); err != nil {
+ return nil, err
+ }
+ log.Trace("Stored voting snapshot to disk", "number", snap.Number, "hash", snap.Hash)
+ }
+ return snap, err
+}
+
+// VerifyUncles implements consensus.Engine, always returning an error for any
+// uncles as this consensus mechanism doesn't permit uncles.
+func (c *XDPoS_v1) VerifyUncles(chain consensus.ChainReader, block *types.Block) error {
+ if len(block.Uncles()) > 0 {
+ return errors.New("uncles not allowed")
+ }
+ return nil
+}
+
+// VerifySeal implements consensus.Engine, checking whether the signature contained
+// in the header satisfies the consensus protocol requirements.
+func (c *XDPoS_v1) VerifySeal(chain consensus.ChainReader, header *types.Header) error {
+ return c.verifySeal(chain, header, nil, true)
+}
+
+// verifySeal checks whether the signature contained in the header satisfies the
+// consensus protocol requirements. The method accepts an optional list of parent
+// headers that aren't yet part of the local blockchain to generate the snapshots
+// from.
+// verifySeal also checks the pair of creator-validator set in the header satisfies
+// the double validation.
+func (c *XDPoS_v1) verifySeal(chain consensus.ChainReader, header *types.Header, parents []*types.Header, fullVerify bool) error {
+ // Verifying the genesis block is not supported
+ number := header.Number.Uint64()
+ if number == 0 {
+ return utils.ErrUnknownBlock
+ }
+ // Retrieve the snapshot needed to verify this header and cache it
+ snap, err := c.snapshot(chain, number-1, header.ParentHash, parents)
+ if err != nil {
+ return err
+ }
+
+ // Resolve the authorization key and check against signers
+ creator, err := ecrecover(header, c.signatures)
+ if err != nil {
+ return err
+ }
+ var parent *types.Header
+ if len(parents) > 0 {
+ parent = parents[len(parents)-1]
+ } else {
+ parent = chain.GetHeader(header.ParentHash, number-1)
+ }
+ difficulty := c.calcDifficulty(chain, parent, creator)
+ log.Debug("verify seal block", "number", header.Number, "hash", header.Hash(), "block difficulty", header.Difficulty, "calc difficulty", difficulty, "creator", creator)
+ // Ensure that the block's difficulty is meaningful (may not be correct at this point)
+ if number > 0 {
+ if header.Difficulty.Int64() != difficulty.Int64() {
+ return utils.ErrInvalidDifficulty
+ }
+ }
+ masternodes := c.GetMasternodes(chain, header)
+ mstring := []string{}
+ for _, m := range masternodes {
+ mstring = append(mstring, m.String())
+ }
+ nstring := []string{}
+ for _, n := range snap.GetSigners() {
+ nstring = append(nstring, n.String())
+ }
+ if _, ok := snap.Signers[creator]; !ok {
+ valid := false
+ for _, m := range masternodes {
+ if m == creator {
+ valid = true
+ break
+ }
+ }
+ if !valid {
+ log.Debug("Unauthorized creator found", "block number", number, "creator", creator.String(), "masternodes", mstring, "snapshot from parent block", nstring)
+ return utils.ErrUnauthorized
+ }
+ }
+ if len(masternodes) > 1 {
+ for seen, recent := range snap.Recents {
+ if recent == creator {
+ // Signer is among recents, only fail if the current block doesn't shift it out
+ // There is only case that we don't allow signer to create two continuous blocks.
+ if limit := uint64(2); seen > number-limit {
+ // Only take into account the non-epoch blocks
+ if number%c.config.Epoch != 0 {
+ return utils.ErrUnauthorized
+ }
+ }
+ }
+ }
+ }
+
+ // header must contain validator info following double validation design
+ // start checking from epoch 2nd.
+ if header.Number.Uint64() > c.config.Epoch && fullVerify {
+ validator, err := c.RecoverValidator(header)
+ if err != nil {
+ return err
+ }
+
+ // verify validator
+ assignedValidator, err := c.GetValidator(creator, chain, header)
+ if err != nil {
+ return err
+ }
+ if validator != assignedValidator {
+ log.Debug("Bad block detected. Header contains wrong pair of creator-validator", "creator", creator, "assigned validator", assignedValidator, "wrong validator", validator)
+ return utils.ErrFailedDoubleValidation
+ }
+ }
+ return nil
+}
+
+func (c *XDPoS_v1) GetValidator(creator common.Address, chain consensus.ChainReader, header *types.Header) (common.Address, error) {
+ epoch := c.config.Epoch
+ no := header.Number.Uint64()
+ cpNo := no
+ if no%epoch != 0 {
+ cpNo = no - (no % epoch)
+ }
+ if cpNo == 0 {
+ return common.Address{}, nil
+ }
+ cpHeader := chain.GetHeaderByNumber(cpNo)
+ if cpHeader == nil {
+ if no%epoch == 0 {
+ cpHeader = header
+ } else {
+ return common.Address{}, fmt.Errorf("couldn't find checkpoint header")
+ }
+ }
+ m, err := GetM1M2FromCheckpointHeader(cpHeader, header, chain.Config())
+ if err != nil {
+ return common.Address{}, err
+ }
+ return m[creator], nil
+}
+
+// Prepare implements consensus.Engine, preparing all the consensus fields of the
+// header for running the transactions on top.
+func (c *XDPoS_v1) Prepare(chain consensus.ChainReader, header *types.Header) error {
+ // If the block isn't a checkpoint, cast a random vote (good enough for now)
+ header.Coinbase = common.Address{}
+ header.Nonce = types.BlockNonce{}
+
+ number := header.Number.Uint64()
+ // Assemble the voting snapshot to check which votes make sense
+ snap, err := c.snapshot(chain, number-1, header.ParentHash, nil)
+ if err != nil {
+ return err
+ }
+ if number%c.config.Epoch != 0 {
+ c.lock.RLock()
+
+ // Gather all the proposals that make sense voting on
+ addresses := make([]common.Address, 0, len(c.proposals))
+ for address, authorize := range c.proposals {
+ if snap.validVote(address, authorize) {
+ addresses = append(addresses, address)
+ }
+ }
+ // If there's pending proposals, cast a vote on them
+ if len(addresses) > 0 {
+ header.Coinbase = addresses[rand.Intn(len(addresses))]
+ if c.proposals[header.Coinbase] {
+ copy(header.Nonce[:], utils.NonceAuthVote)
+ } else {
+ copy(header.Nonce[:], utils.NonceDropVote)
+ }
+ }
+ c.lock.RUnlock()
+ }
+ parent := chain.GetHeader(header.ParentHash, number-1)
+ if parent == nil {
+ return consensus.ErrUnknownAncestor
+ }
+ // Set the correct difficulty
+ header.Difficulty = c.calcDifficulty(chain, parent, c.signer)
+ log.Debug("CalcDifficulty ", "number", header.Number, "difficulty", header.Difficulty)
+ // Ensure the extra data has all it's components
+ if len(header.Extra) < utils.ExtraVanity {
+ header.Extra = append(header.Extra, bytes.Repeat([]byte{0x00}, utils.ExtraVanity-len(header.Extra))...)
+ }
+ header.Extra = header.Extra[:utils.ExtraVanity]
+ masternodes := snap.GetSigners()
+ if number >= c.config.Epoch && number%c.config.Epoch == 0 {
+ if c.HookPenalty != nil || c.HookPenaltyTIPSigning != nil {
+ var penMasternodes []common.Address
+ var err error
+ if chain.Config().IsTIPSigning(header.Number) {
+ penMasternodes, err = c.HookPenaltyTIPSigning(chain, header, masternodes)
+ } else {
+ penMasternodes, err = c.HookPenalty(chain, number)
+ }
+ if err != nil {
+ return err
+ }
+ if len(penMasternodes) > 0 {
+ // penalize bad masternode(s)
+ masternodes = common.RemoveItemFromArray(masternodes, penMasternodes)
+ for _, address := range penMasternodes {
+ log.Debug("Penalty status", "address", address, "number", number)
+ }
+ header.Penalties = common.ExtractAddressToBytes(penMasternodes)
+ }
+ }
+ // Prevent penalized masternode(s) within 4 recent epochs
+ for i := 1; i <= common.LimitPenaltyEpoch; i++ {
+ if number > uint64(i)*c.config.Epoch {
+ masternodes = removePenaltiesFromBlock(chain, masternodes, number-uint64(i)*c.config.Epoch)
+ }
+ }
+ for _, masternode := range masternodes {
+ header.Extra = append(header.Extra, masternode[:]...)
+ }
+ if c.HookValidator != nil {
+ validators, err := c.HookValidator(header, masternodes)
+ if err != nil {
+ return err
+ }
+ header.Validators = validators
+ }
+ }
+ header.Extra = append(header.Extra, make([]byte, utils.ExtraSeal)...)
+
+ // Mix digest is reserved for now, set to empty
+ header.MixDigest = common.Hash{}
+
+ // Ensure the timestamp has the correct delay
+
+ header.Time = new(big.Int).Add(parent.Time, new(big.Int).SetUint64(c.config.Period))
+ if header.Time.Int64() < time.Now().Unix() {
+ header.Time = big.NewInt(time.Now().Unix())
+ }
+ return nil
+}
+
+func (c *XDPoS_v1) UpdateMasternodes(chain consensus.ChainReader, header *types.Header, ms []utils.Masternode) error {
+ number := header.Number.Uint64()
+ log.Trace("take snapshot", "number", number, "hash", header.Hash())
+ // get snapshot
+ snap, err := c.snapshot(chain, number, header.Hash(), nil)
+ if err != nil {
+ return err
+ }
+ newMasternodes := make(map[common.Address]struct{})
+ for _, m := range ms {
+ newMasternodes[m.Address] = struct{}{}
+ }
+ snap.Signers = newMasternodes
+ nm := []string{}
+ for _, n := range ms {
+ nm = append(nm, n.Address.String())
+ }
+ c.recents.Add(snap.Hash, snap)
+ log.Info("New set of masternodes has been updated to snapshot", "number", snap.Number, "hash", snap.Hash, "new masternodes", nm)
+ return nil
+}
+
+// Finalize implements consensus.Engine, ensuring no uncles are set, nor block
+// rewards given, and returns the final block.
+func (c *XDPoS_v1) Finalize(chain consensus.ChainReader, header *types.Header, state *state.StateDB, parentState *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) {
+ // set block reward
+ number := header.Number.Uint64()
+ rCheckpoint := chain.Config().XDPoS.RewardCheckpoint
+
+ // _ = c.CacheData(header, txs, receipts)
+
+ if c.HookReward != nil && number%rCheckpoint == 0 {
+ err, rewards := c.HookReward(chain, state, parentState, header)
+ if err != nil {
+ return nil, err
+ }
+ if len(common.StoreRewardFolder) > 0 {
+ data, err := json.Marshal(rewards)
+ if err == nil {
+ err = ioutil.WriteFile(filepath.Join(common.StoreRewardFolder, header.Number.String()+"."+header.Hash().Hex()), data, 0644)
+ }
+ if err != nil {
+ log.Error("Error when save reward info ", "number", header.Number, "hash", header.Hash().Hex(), "err", err)
+ }
+ }
+ }
+
+ // the state remains as is and uncles are dropped
+ header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number))
+ header.UncleHash = types.CalcUncleHash(nil)
+
+ // Assemble and return the final block for sealing
+ return types.NewBlock(header, txs, nil, receipts), nil
+}
+
+// Authorize injects a private key into the consensus engine to mint new blocks
+// with.
+func (c *XDPoS_v1) Authorize(signer common.Address, signFn clique.SignerFn) {
+ c.lock.Lock()
+ defer c.lock.Unlock()
+
+ c.signer = signer
+ c.signFn = signFn
+}
+
+// Seal implements consensus.Engine, attempting to create a sealed block using
+// the local signing credentials.
+func (c *XDPoS_v1) Seal(chain consensus.ChainReader, block *types.Block, stop <-chan struct{}) (*types.Block, error) {
+ header := block.Header()
+
+ // Sealing the genesis block is not supported
+ number := header.Number.Uint64()
+ if number == 0 {
+ return nil, utils.ErrUnknownBlock
+ }
+ // For 0-period chains, refuse to seal empty blocks (no reward but would spin sealing)
+ // checkpoint blocks have no tx
+ if c.config.Period == 0 && len(block.Transactions()) == 0 && number%c.config.Epoch != 0 {
+ return nil, utils.ErrWaitTransactions
+ }
+ // Don't hold the signer fields for the entire sealing procedure
+ c.lock.RLock()
+ signer, signFn := c.signer, c.signFn
+ c.lock.RUnlock()
+
+ // Bail out if we're unauthorized to sign a block
+ snap, err := c.snapshot(chain, number-1, header.ParentHash, nil)
+ if err != nil {
+ return nil, err
+ }
+ masternodes := c.GetMasternodes(chain, header)
+ if _, authorized := snap.Signers[signer]; !authorized {
+ valid := false
+ for _, m := range masternodes {
+ if m == signer {
+ valid = true
+ break
+ }
+ }
+ if !valid {
+ return nil, utils.ErrUnauthorized
+ }
+ }
+ // If we're amongst the recent signers, wait for the next block
+ // only check recent signers if there are more than one signer.
+ if len(masternodes) > 1 {
+ for seen, recent := range snap.Recents {
+ if recent == signer {
+ // Signer is among recents, only wait if the current block doesn't shift it out
+ // There is only case that we don't allow signer to create two continuous blocks.
+ if limit := uint64(2); number < limit || seen > number-limit {
+ // Only take into account the non-epoch blocks
+ if number%c.config.Epoch != 0 {
+ log.Info("Signed recently, must wait for others ", "len(masternodes)", len(masternodes), "number", number, "limit", limit, "seen", seen, "recent", recent.String(), "snap.Recents", snap.Recents)
+ <-stop
+ return nil, nil
+ }
+ }
+ }
+ }
+ }
+ select {
+ case <-stop:
+ return nil, nil
+ default:
+ }
+ // Sign all the things!
+ sighash, err := signFn(accounts.Account{Address: signer}, sigHash(header).Bytes())
+ if err != nil {
+ return nil, err
+ }
+ copy(header.Extra[len(header.Extra)-utils.ExtraSeal:], sighash)
+ m2, err := c.GetValidator(signer, chain, header)
+ if err != nil {
+ return nil, fmt.Errorf("can't get block validator: %v", err)
+ }
+ if m2 == signer {
+ header.Validator = sighash
+ }
+ return block.WithSeal(header), nil
+}
+
+// CalcDifficulty is the difficulty adjustment algorithm. It returns the difficulty
+// that a new block should have based on the previous blocks in the chain and the
+// current signer.
+func (c *XDPoS_v1) CalcDifficulty(chain consensus.ChainReader, time uint64, parent *types.Header) *big.Int {
+ return c.calcDifficulty(chain, parent, c.signer)
+}
+
+func (c *XDPoS_v1) calcDifficulty(chain consensus.ChainReader, parent *types.Header, signer common.Address) *big.Int {
+ len, preIndex, curIndex, _, err := c.YourTurn(chain, parent, signer)
+ if err != nil {
+ return big.NewInt(int64(len + curIndex - preIndex))
+ }
+ return big.NewInt(int64(len - utils.Hop(len, preIndex, curIndex)))
+}
+
+func (c *XDPoS_v1) RecoverSigner(header *types.Header) (common.Address, error) {
+ return ecrecover(header, c.signatures)
+}
+
+func (c *XDPoS_v1) RecoverValidator(header *types.Header) (common.Address, error) {
+ // If the signature's already cached, return that
+ hash := header.Hash()
+ if address, known := c.validatorSignatures.Get(hash); known {
+ return address.(common.Address), nil
+ }
+ // Retrieve the signature from the header.Validator
+ // len equals 65 bytes
+ if len(header.Validator) != utils.ExtraSeal {
+ return common.Address{}, consensus.ErrFailValidatorSignature
+ }
+ // 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:])
+
+ c.validatorSignatures.Add(hash, signer)
+ return signer, nil
+}
+
+// Get master nodes over extra data of previous checkpoint block.
+func (c *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)
+ 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
+}
+
+func (c *XDPoS_v1) CacheData(header *types.Header, txs []*types.Transaction, receipts []*types.Receipt) []*types.Transaction {
+ signTxs := []*types.Transaction{}
+ for _, tx := range txs {
+ if tx.IsSigningTransaction() {
+ var b uint
+ for _, r := range receipts {
+ if r.TxHash == tx.Hash() {
+ if len(r.PostState) > 0 {
+ b = types.ReceiptStatusSuccessful
+ } else {
+ b = r.Status
+ }
+ break
+ }
+ }
+
+ if b == types.ReceiptStatusFailed {
+ continue
+ }
+
+ signTxs = append(signTxs, tx)
+ }
+ }
+
+ log.Debug("Save tx signers to cache", "hash", header.Hash().String(), "number", header.Number, "len(txs)", len(signTxs))
+ c.BlockSigners.Add(header.Hash(), signTxs)
+
+ return signTxs
+}
+
+func (c *XDPoS_v1) CacheSigner(hash common.Hash, txs []*types.Transaction) []*types.Transaction {
+ signTxs := []*types.Transaction{}
+ for _, tx := range txs {
+ if tx.IsSigningTransaction() {
+ signTxs = append(signTxs, tx)
+ }
+ }
+ log.Debug("Save tx signers to cache", "hash", hash.String(), "len(txs)", len(signTxs))
+ c.BlockSigners.Add(hash, signTxs)
+ return signTxs
+}
+
+func (c *XDPoS_v1) GetDb() ethdb.Database {
+ return c.db
+}
+
+// Extract validators from byte array.
+func removePenaltiesFromBlock(chain consensus.ChainReader, masternodes []common.Address, epochNumber uint64) []common.Address {
+ if epochNumber <= 0 {
+ return masternodes
+ }
+ header := chain.GetHeaderByNumber(epochNumber)
+ block := chain.GetBlock(header.Hash(), epochNumber)
+ penalties := block.Penalties()
+ if penalties != nil {
+ prevPenalties := common.ExtractAddressFromBytes(penalties)
+ masternodes = common.RemoveItemFromArray(masternodes, prevPenalties)
+ }
+ 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
+}
+
+// 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 := utils.ExtractValidatorsFromBytes(checkpointHeader.Validators)
+ m1m2, _, err := utils.GetM1M2(masternodes, validators, currentHeader, config)
+ if err != nil {
+ return map[common.Address]common.Address{}, err
+ }
+ return m1m2, nil
+}
+
+func (c *XDPoS_v1) CheckMNTurn(chain consensus.ChainReader, parent *types.Header, signer common.Address) bool {
+ masternodes := c.GetMasternodes(chain, parent)
+
+ if common.IsTestnet {
+ // Only three mns hard code for XDC testnet.
+ masternodes = []common.Address{
+ common.HexToAddress("0x3Ea0A3555f9B1dE983572BfF6444aeb1899eC58C"),
+ common.HexToAddress("0x4F7900282F3d371d585ab1361205B0940aB1789C"),
+ common.HexToAddress("0x942a5885A8844Ee5587C8AC5e371Fc39FFE61896"),
+ }
+ }
+
+ snap, err := c.GetSnapshot(chain, parent)
+ if err != nil {
+ log.Warn("Failed when trying to commit new work", "err", err)
+ return false
+ }
+ if len(masternodes) == 0 {
+ return false
+ }
+ // masternode[0] has chance to create block 1
+ preIndex := -1
+ if parent.Number.Uint64() != 0 {
+ pre, err := whoIsCreator(snap, parent)
+ if err != nil {
+ return false
+ }
+ preIndex = position(masternodes, pre)
+ }
+ curIndex := position(masternodes, signer)
+ return ((preIndex)%len(masternodes) == curIndex)
+}
+
+func (c *XDPoS_v1) getSignersFromContract(chain consensus.ChainReader, checkpointHeader *types.Header) ([]common.Address, error) {
+ startGapBlockHeader := checkpointHeader
+ number := checkpointHeader.Number.Uint64()
+ for step := uint64(1); step <= chain.Config().XDPoS.Gap; step++ {
+ startGapBlockHeader = chain.GetHeader(startGapBlockHeader.ParentHash, number-step)
+ }
+ signers, err := c.HookGetSignersFromContract(startGapBlockHeader.Hash())
+ if err != nil {
+ return []common.Address{}, fmt.Errorf("Can't get signers from Smart Contract . Err: %v", err)
+ }
+ return signers, nil
+}
+
+func NewFaker(db ethdb.Database) *XDPoS_v1 {
+ var fakeEngine *XDPoS_v1
+ // Set any missing consensus parameters to their defaults
+ conf := params.TestXDPoSMockChainConfig.XDPoS
+
+ // Allocate the snapshot caches and create the engine
+ BlockSigners, _ := lru.New(utils.BlockSignersCacheLimit)
+ recents, _ := lru.NewARC(utils.InmemorySnapshots)
+ signatures, _ := lru.NewARC(utils.InmemorySnapshots)
+ validatorSignatures, _ := lru.NewARC(utils.InmemorySnapshots)
+ verifiedHeaders, _ := lru.NewARC(utils.InmemorySnapshots)
+ fakeEngine = &XDPoS_v1{
+ config: conf,
+ db: db,
+ BlockSigners: BlockSigners,
+ recents: recents,
+ signatures: signatures,
+ verifiedHeaders: verifiedHeaders,
+ validatorSignatures: validatorSignatures,
+ proposals: make(map[common.Address]bool),
+ }
+ return fakeEngine
+}
diff --git a/consensus/XDPoS/snapshot.go b/consensus/XDPoS/engines/engine_v1/snapshot.go
similarity index 80%
rename from consensus/XDPoS/snapshot.go
rename to consensus/XDPoS/engines/engine_v1/snapshot.go
index 8642f86b52..3ad8b22189 100644
--- a/consensus/XDPoS/snapshot.go
+++ b/consensus/XDPoS/engines/engine_v1/snapshot.go
@@ -1,25 +1,11 @@
-// Copyright (c) 2018 XDPoSChain
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with this program. If not, see .
-
-package XDPoS
+package engine_v1
import (
"bytes"
"encoding/json"
"github.com/XinFinOrg/XDPoSChain/common"
+ "github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils"
"github.com/XinFinOrg/XDPoSChain/consensus/clique"
"github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/ethdb"
@@ -44,7 +30,7 @@ import (
//}
// Snapshot is the state of the authorization voting at a given point in time.
-type Snapshot struct {
+type SnapshotV1 struct {
config *params.XDPoSConfig // Consensus engine parameters to fine tune behavior
sigcache *lru.ARCCache // Cache of recent block signatures to speed up ecrecover
@@ -59,8 +45,8 @@ type Snapshot struct {
// 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(config *params.XDPoSConfig, sigcache *lru.ARCCache, number uint64, hash common.Hash, signers []common.Address) *Snapshot {
- snap := &Snapshot{
+func newSnapshot(config *params.XDPoSConfig, sigcache *lru.ARCCache, number uint64, hash common.Hash, signers []common.Address) *SnapshotV1 {
+ snap := &SnapshotV1{
config: config,
sigcache: sigcache,
Number: number,
@@ -76,12 +62,12 @@ func newSnapshot(config *params.XDPoSConfig, sigcache *lru.ARCCache, number uint
}
// loadSnapshot loads an existing snapshot from the database.
-func loadSnapshot(config *params.XDPoSConfig, sigcache *lru.ARCCache, db ethdb.Database, hash common.Hash) (*Snapshot, error) {
+func loadSnapshot(config *params.XDPoSConfig, sigcache *lru.ARCCache, db ethdb.Database, hash common.Hash) (*SnapshotV1, error) {
blob, err := db.Get(append([]byte("XDPoS-"), hash[:]...))
if err != nil {
return nil, err
}
- snap := new(Snapshot)
+ snap := new(SnapshotV1)
if err := json.Unmarshal(blob, snap); err != nil {
return nil, err
}
@@ -91,8 +77,8 @@ func loadSnapshot(config *params.XDPoSConfig, sigcache *lru.ARCCache, db ethdb.D
return snap, nil
}
-// store inserts the snapshot into the database.
-func (s *Snapshot) store(db ethdb.Database) error {
+// store inserts the SnapshotV1 into the database.
+func (s *SnapshotV1) store(db ethdb.Database) error {
blob, err := json.Marshal(s)
if err != nil {
return err
@@ -100,9 +86,9 @@ func (s *Snapshot) store(db ethdb.Database) error {
return db.Put(append([]byte("XDPoS-"), s.Hash[:]...), blob)
}
-// copy creates a deep copy of the snapshot, though not the individual votes.
-func (s *Snapshot) copy() *Snapshot {
- cpy := &Snapshot{
+// copy creates a deep copy of the SnapshotV1, though not the individual votes.
+func (s *SnapshotV1) copy() *SnapshotV1 {
+ cpy := &SnapshotV1{
config: s.config,
sigcache: s.sigcache,
Number: s.Number,
@@ -128,13 +114,13 @@ func (s *Snapshot) copy() *Snapshot {
// validVote returns whether it makes sense to cast the specified vote in the
// given snapshot context (e.g. don't try to add an already authorized signer).
-func (s *Snapshot) validVote(address common.Address, authorize bool) bool {
+func (s *SnapshotV1) validVote(address common.Address, authorize bool) bool {
_, signer := s.Signers[address]
return (signer && !authorize) || (!signer && authorize)
}
// cast adds a new vote into the tally.
-func (s *Snapshot) cast(address common.Address, authorize bool) bool {
+func (s *SnapshotV1) cast(address common.Address, authorize bool) bool {
// Ensure the vote is meaningful
if !s.validVote(address, authorize) {
return false
@@ -150,7 +136,7 @@ func (s *Snapshot) cast(address common.Address, authorize bool) bool {
}
// uncast removes a previously cast vote from the tally.
-func (s *Snapshot) uncast(address common.Address, authorize bool) bool {
+func (s *SnapshotV1) uncast(address common.Address, authorize bool) bool {
// If there's no tally, it's a dangling vote, just drop
tally, ok := s.Tally[address]
if !ok {
@@ -170,9 +156,9 @@ func (s *Snapshot) uncast(address common.Address, authorize bool) bool {
return true
}
-// apply creates a new authorization snapshot by applying the given headers to
+// apply creates a new authorization SnapshotV1 by applying the given headers to
// the original one.
-func (s *Snapshot) apply(headers []*types.Header) (*Snapshot, error) {
+func (s *SnapshotV1) apply(headers []*types.Header) (*SnapshotV1, error) {
// Allow passing in no headers for cleaner code
if len(headers) == 0 {
return s, nil
@@ -180,13 +166,13 @@ func (s *Snapshot) apply(headers []*types.Header) (*Snapshot, error) {
// 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, errInvalidVotingChain
+ return nil, utils.ErrInvalidVotingChain
}
}
if headers[0].Number.Uint64() != s.Number+1 {
- return nil, errInvalidVotingChain
+ return nil, utils.ErrInvalidVotingChain
}
- // Iterate through the headers and create a new snapshot
+ // Iterate through the headers and create a new SnapshotV1
snap := s.copy()
for _, header := range headers {
@@ -230,12 +216,12 @@ func (s *Snapshot) apply(headers []*types.Header) (*Snapshot, error) {
// Tally up the new vote from the signer
var authorize bool
switch {
- case bytes.Equal(header.Nonce[:], nonceAuthVote):
+ case bytes.Equal(header.Nonce[:], utils.NonceAuthVote):
authorize = true
- case bytes.Equal(header.Nonce[:], nonceDropVote):
+ case bytes.Equal(header.Nonce[:], utils.NonceDropVote):
authorize = false
default:
- return nil, errInvalidVote
+ return nil, utils.ErrInvalidVote
}
if snap.cast(header.Coinbase, authorize) {
snap.Votes = append(snap.Votes, &clique.Vote{
@@ -286,7 +272,7 @@ func (s *Snapshot) apply(headers []*types.Header) (*Snapshot, error) {
}
// signers retrieves the list of authorized signers in ascending order.
-func (s *Snapshot) GetSigners() []common.Address {
+func (s *SnapshotV1) GetSigners() []common.Address {
signers := make([]common.Address, 0, len(s.Signers))
for signer := range s.Signers {
signers = append(signers, signer)
@@ -300,12 +286,3 @@ func (s *Snapshot) GetSigners() []common.Address {
}
return signers
}
-
-// inturn returns if a signer at a given block height is in-turn or not.
-func (s *Snapshot) inturn(number uint64, signer common.Address) bool {
- signers, offset := s.GetSigners(), 0
- for offset < len(signers) && signers[offset] != signer {
- offset++
- }
- return (number % uint64(len(signers))) == uint64(offset)
-}
diff --git a/consensus/XDPoS/engines/engine_v2/engine.go b/consensus/XDPoS/engines/engine_v2/engine.go
new file mode 100644
index 0000000000..da3b04244d
--- /dev/null
+++ b/consensus/XDPoS/engines/engine_v2/engine.go
@@ -0,0 +1 @@
+package engine_v2
diff --git a/consensus/XDPoS/engines/engine_v2/snapshot.go b/consensus/XDPoS/engines/engine_v2/snapshot.go
new file mode 100644
index 0000000000..da3b04244d
--- /dev/null
+++ b/consensus/XDPoS/engines/engine_v2/snapshot.go
@@ -0,0 +1 @@
+package engine_v2
diff --git a/consensus/XDPoS/utils/constants.go b/consensus/XDPoS/utils/constants.go
new file mode 100644
index 0000000000..f2c7490ccc
--- /dev/null
+++ b/consensus/XDPoS/utils/constants.go
@@ -0,0 +1,25 @@
+package utils
+
+import (
+ "github.com/XinFinOrg/XDPoSChain/common/hexutil"
+ "github.com/XinFinOrg/XDPoSChain/core/types"
+)
+
+// XDPoS delegated-proof-of-stake protocol constants.
+var (
+ EpochLength = uint64(900) // Default number of blocks after which to checkpoint and reset the pending votes
+
+ ExtraVanity = 32 // Fixed number of extra-data prefix bytes reserved for signer vanity
+ ExtraSeal = 65 // Fixed number of extra-data suffix bytes reserved for signer seal
+
+ NonceAuthVote = hexutil.MustDecode("0xffffffffffffffff") // Magic nonce number to vote on adding a new signer
+ NonceDropVote = hexutil.MustDecode("0x0000000000000000") // Magic nonce number to vote on removing a signer.
+
+ UncleHash = types.CalcUncleHash(nil) // Always Keccak256(RLP([])) as uncles are meaningless outside of PoW.
+)
+
+const (
+ InmemorySnapshots = 128 // Number of recent vote snapshots to keep in memory
+ BlockSignersCacheLimit = 9000
+ M2ByteLength = 4
+)
diff --git a/consensus/XDPoS/utils/errors.go b/consensus/XDPoS/utils/errors.go
new file mode 100644
index 0000000000..5d7faf1a81
--- /dev/null
+++ b/consensus/XDPoS/utils/errors.go
@@ -0,0 +1,74 @@
+package utils
+
+import "errors"
+
+// Various error messages to mark blocks invalid. These should be private to
+// prevent engine specific errors from being referenced in the remainder of the
+// codebase, inherently breaking if the engine is swapped out. Please put common
+// error types into the consensus package.
+var (
+ // errUnknownBlock is returned when the list of signers is requested for a block
+ // that is not part of the local blockchain.
+ ErrUnknownBlock = errors.New("unknown block")
+
+ // errInvalidCheckpointBeneficiary is returned if a checkpoint/epoch transition
+ // block has a beneficiary set to non-zeroes.
+ ErrInvalidCheckpointBeneficiary = errors.New("beneficiary in checkpoint block non-zero")
+
+ // errInvalidVote is returned if a nonce value is something else that the two
+ // allowed constants of 0x00..0 or 0xff..f.
+ ErrInvalidVote = errors.New("vote nonce not 0x00..0 or 0xff..f")
+
+ // errInvalidCheckpointVote is returned if a checkpoint/epoch transition block
+ // has a vote nonce set to non-zeroes.
+ ErrInvalidCheckpointVote = errors.New("vote nonce in checkpoint block non-zero")
+
+ // errMissingVanity is returned if a block's extra-data section is shorter than
+ // 32 bytes, which is required to store the signer vanity.
+ ErrMissingVanity = errors.New("extra-data 32 byte vanity prefix missing")
+
+ // errMissingSignature is returned if a block's extra-data section doesn't seem
+ // to contain a 65 byte secp256k1 signature.
+ ErrMissingSignature = errors.New("extra-data 65 byte suffix signature missing")
+
+ // errExtraSigners is returned if non-checkpoint block contain signer data in
+ // their extra-data fields.
+ ErrExtraSigners = errors.New("non-checkpoint block contains extra signer list")
+
+ // errInvalidCheckpointSigners is returned if a checkpoint block contains an
+ // invalid list of signers (i.e. non divisible by 20 bytes, or not the correct
+ // ones).
+ ErrInvalidCheckpointSigners = errors.New("invalid signer list on checkpoint block")
+
+ ErrInvalidCheckpointPenalties = errors.New("invalid penalty list on checkpoint block")
+
+ // errInvalidMixDigest is returned if a block's mix digest is non-zero.
+ ErrInvalidMixDigest = errors.New("non-zero mix digest")
+
+ // errInvalidUncleHash is returned if a block contains an non-empty uncle list.
+ ErrInvalidUncleHash = errors.New("non empty uncle hash")
+
+ // errInvalidDifficulty is returned if the difficulty of a block is not either
+ // of 1 or 2, or if the value does not match the turn of the signer.
+ ErrInvalidDifficulty = errors.New("invalid difficulty")
+
+ // ErrInvalidTimestamp is returned if the timestamp of a block is lower than
+ // the previous block's timestamp + the minimum block period.
+ ErrInvalidTimestamp = errors.New("invalid timestamp")
+
+ // errInvalidVotingChain is returned if an authorization list is attempted to
+ // be modified via out-of-range or non-contiguous headers.
+ ErrInvalidVotingChain = errors.New("invalid voting chain")
+
+ // errUnauthorized is returned if a header is signed by a non-authorized entity.
+ ErrUnauthorized = errors.New("unauthorized")
+
+ ErrFailedDoubleValidation = errors.New("wrong pair of creator-validator in double validation")
+
+ // errWaitTransactions is returned if an empty block is attempted to be sealed
+ // on an instant chain (0 second period). It's important to refuse these as the
+ // block reward is zero, so an empty block just bloats the chain... fast.
+ ErrWaitTransactions = errors.New("waiting for transactions")
+
+ ErrInvalidCheckpointValidators = errors.New("invalid validators list on checkpoint block")
+)
diff --git a/consensus/XDPoS/utils/types.go b/consensus/XDPoS/utils/types.go
new file mode 100644
index 0000000000..754194c728
--- /dev/null
+++ b/consensus/XDPoS/utils/types.go
@@ -0,0 +1,58 @@
+package utils
+
+import (
+ "math/big"
+ "time"
+
+ "github.com/XinFinOrg/XDPoSChain/XDCx/tradingstate"
+ "github.com/XinFinOrg/XDPoSChain/XDCxlending/lendingstate"
+ "github.com/XinFinOrg/XDPoSChain/common"
+ "github.com/XinFinOrg/XDPoSChain/consensus"
+ "github.com/XinFinOrg/XDPoSChain/consensus/clique"
+ "github.com/XinFinOrg/XDPoSChain/core/state"
+ "github.com/XinFinOrg/XDPoSChain/core/types"
+ "gopkg.in/karalabe/cookiejar.v2/collections/prque"
+)
+
+type Masternode struct {
+ Address common.Address
+ Stake *big.Int
+}
+
+type TradingService interface {
+ GetTradingStateRoot(block *types.Block, author common.Address) (common.Hash, error)
+ GetTradingState(block *types.Block, author common.Address) (*tradingstate.TradingStateDB, error)
+ HasTradingState(block *types.Block, author common.Address) bool
+ GetStateCache() tradingstate.Database
+ GetTriegc() *prque.Prque
+ ApplyOrder(header *types.Header, coinbase common.Address, chain consensus.ChainContext, statedb *state.StateDB, XDCXstatedb *tradingstate.TradingStateDB, orderBook common.Hash, order *tradingstate.OrderItem) ([]map[string]string, []*tradingstate.OrderItem, error)
+ UpdateMediumPriceBeforeEpoch(epochNumber uint64, tradingStateDB *tradingstate.TradingStateDB, statedb *state.StateDB) error
+ IsSDKNode() bool
+ SyncDataToSDKNode(takerOrder *tradingstate.OrderItem, txHash common.Hash, txMatchTime time.Time, statedb *state.StateDB, trades []map[string]string, rejectedOrders []*tradingstate.OrderItem, dirtyOrderCount *uint64) error
+ RollbackReorgTxMatch(txhash common.Hash) error
+ GetTokenDecimal(chain consensus.ChainContext, statedb *state.StateDB, tokenAddr common.Address) (*big.Int, error)
+}
+
+type LendingService interface {
+ GetLendingStateRoot(block *types.Block, author common.Address) (common.Hash, error)
+ GetLendingState(block *types.Block, author common.Address) (*lendingstate.LendingStateDB, error)
+ HasLendingState(block *types.Block, author common.Address) bool
+ GetStateCache() lendingstate.Database
+ GetTriegc() *prque.Prque
+ ApplyOrder(header *types.Header, coinbase common.Address, chain consensus.ChainContext, statedb *state.StateDB, lendingStateDB *lendingstate.LendingStateDB, tradingStateDb *tradingstate.TradingStateDB, lendingOrderBook common.Hash, order *lendingstate.LendingItem) ([]*lendingstate.LendingTrade, []*lendingstate.LendingItem, error)
+ GetCollateralPrices(header *types.Header, chain consensus.ChainContext, statedb *state.StateDB, tradingStateDb *tradingstate.TradingStateDB, collateralToken common.Address, lendingToken common.Address) (*big.Int, *big.Int, error)
+ GetMediumTradePriceBeforeEpoch(chain consensus.ChainContext, statedb *state.StateDB, tradingStateDb *tradingstate.TradingStateDB, baseToken common.Address, quoteToken common.Address) (*big.Int, error)
+ ProcessLiquidationData(header *types.Header, chain consensus.ChainContext, statedb *state.StateDB, tradingState *tradingstate.TradingStateDB, lendingState *lendingstate.LendingStateDB) (updatedTrades map[common.Hash]*lendingstate.LendingTrade, liquidatedTrades, autoRepayTrades, autoTopUpTrades, autoRecallTrades []*lendingstate.LendingTrade, err error)
+ SyncDataToSDKNode(chain consensus.ChainContext, state *state.StateDB, block *types.Block, takerOrderInTx *lendingstate.LendingItem, txHash common.Hash, txMatchTime time.Time, trades []*lendingstate.LendingTrade, rejectedOrders []*lendingstate.LendingItem, dirtyOrderCount *uint64) error
+ UpdateLiquidatedTrade(blockTime uint64, result lendingstate.FinalizedResult, trades map[common.Hash]*lendingstate.LendingTrade) error
+ RollbackLendingData(txhash common.Hash) error
+}
+
+type PublicApiSnapshot struct {
+ Number uint64 `json:"number"` // Block number where the snapshot was created
+ Hash common.Hash `json:"hash"` // Block hash where the snapshot was created
+ Signers map[common.Address]struct{} `json:"signers"` // Set of authorized signers at this moment
+ Recents map[uint64]common.Address `json:"recents"` // Set of recent signers for spam protections
+ Votes []*clique.Vote `json:"votes"` // List of votes cast in chronological order
+ Tally map[common.Address]clique.Tally `json:"tally"` // Current vote tally to avoid recalculating
+}
diff --git a/consensus/XDPoS/utils/utils.go b/consensus/XDPoS/utils/utils.go
new file mode 100644
index 0000000000..a117984509
--- /dev/null
+++ b/consensus/XDPoS/utils/utils.go
@@ -0,0 +1,111 @@
+package utils
+
+import (
+ "bytes"
+ "errors"
+ "reflect"
+ "sort"
+ "strconv"
+
+ "github.com/XinFinOrg/XDPoSChain/common"
+ "github.com/XinFinOrg/XDPoSChain/core/types"
+ "github.com/XinFinOrg/XDPoSChain/log"
+ "github.com/XinFinOrg/XDPoSChain/params"
+)
+
+func Position(list []common.Address, x common.Address) int {
+ for i, item := range list {
+ if item == x {
+ return i
+ }
+ }
+ return -1
+}
+
+func Hop(len, pre, cur int) int {
+ switch {
+ case pre < cur:
+ return cur - (pre + 1)
+ case pre > cur:
+ return (len - pre) + (cur - 1)
+ default:
+ return len - 1
+ }
+}
+
+// Extract validators from byte array.
+func ExtractValidatorsFromBytes(byteValidators []byte) []int64 {
+ lenValidator := len(byteValidators) / M2ByteLength
+ var validators []int64
+ for i := 0; i < lenValidator; i++ {
+ trimByte := bytes.Trim(byteValidators[i*M2ByteLength:(i+1)*M2ByteLength], "\x00")
+ intNumber, err := strconv.Atoi(string(trimByte))
+ if err != nil {
+ log.Error("Can not convert string to integer", "error", err)
+ return []int64{}
+ }
+ validators = append(validators, int64(intNumber))
+ }
+
+ 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 {
+ if len(list1) == 0 && len(list2) == 0 {
+ return true
+ }
+ sort.Slice(list1, func(i, j int) bool {
+ return list1[i].String() <= list1[j].String()
+ })
+ sort.Slice(list2, func(i, j int) bool {
+ return list2[i].String() <= list2[j].String()
+ })
+ return reflect.DeepEqual(list1, list2)
+}
diff --git a/contracts/utils.go b/contracts/utils.go
index 7179a03cf1..373e8d0121 100644
--- a/contracts/utils.go
+++ b/contracts/utils.go
@@ -35,7 +35,9 @@ import (
"github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/common/hexutil"
"github.com/XinFinOrg/XDPoSChain/consensus"
+
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS"
+ "github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils"
"github.com/XinFinOrg/XDPoSChain/contracts/blocksigner/contract"
randomizeContract "github.com/XinFinOrg/XDPoSChain/contracts/randomize/contract"
"github.com/XinFinOrg/XDPoSChain/core"
@@ -278,7 +280,7 @@ func BuildValidatorFromM2(listM2 []int64) []byte {
var validatorBytes []byte
for _, numberM2 := range listM2 {
// Convert number to byte.
- m2Byte := common.LeftPadBytes([]byte(fmt.Sprintf("%d", numberM2)), XDPoS.M2ByteLength)
+ m2Byte := common.LeftPadBytes([]byte(fmt.Sprintf("%d", numberM2)), utils.M2ByteLength)
validatorBytes = append(validatorBytes, m2Byte...)
}
@@ -292,7 +294,7 @@ func DecodeValidatorsHexData(validatorsStr string) ([]int64, error) {
return nil, err
}
- return XDPoS.ExtractValidatorsFromBytes(validatorsByte), nil
+ return utils.ExtractValidatorsFromBytes(validatorsByte), nil
}
// Decrypt randomize from secrets and opening.
@@ -350,7 +352,7 @@ func GetRewardForCheckpoint(c *XDPoS.XDPoS, chain consensus.ChainReader, header
}
}
header = chain.GetHeader(header.ParentHash, prevCheckpoint)
- masternodes := XDPoS.GetMasternodesFromCheckpointHeader(header)
+ masternodes := utils.GetMasternodesFromCheckpointHeader(header)
for i := startBlockNumber; i <= endBlockNumber; i++ {
if i%common.MergeSignRange == 0 || !chain.Config().IsTIP2019(big.NewInt(int64(i))) {
diff --git a/contracts/utils_test.go b/contracts/utils_test.go
index d267eda83e..4473502570 100644
--- a/contracts/utils_test.go
+++ b/contracts/utils_test.go
@@ -27,7 +27,7 @@ import (
"github.com/XinFinOrg/XDPoSChain/accounts/abi/bind"
"github.com/XinFinOrg/XDPoSChain/accounts/abi/bind/backends"
"github.com/XinFinOrg/XDPoSChain/common"
- "github.com/XinFinOrg/XDPoSChain/consensus/XDPoS"
+ "github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils"
"github.com/XinFinOrg/XDPoSChain/contracts/blocksigner"
"github.com/XinFinOrg/XDPoSChain/core"
"github.com/XinFinOrg/XDPoSChain/core/types"
@@ -191,7 +191,7 @@ func TestGenM2FromRandomize(t *testing.T) {
func TestBuildValidatorFromM2(t *testing.T) {
a := []int64{84, 58, 27, 96, 127, 60, 136, 20, 121, 31, 87, 85, 40, 120, 149, 109, 141, 145, 11, 110, 147, 35, 76, 46, 34, 108, 72, 103, 102, 12, 23, 47, 70, 86, 125, 112, 128, 13, 130, 98, 126, 62, 132, 111, 134, 6, 106, 67, 24, 91, 101, 50, 94, 43, 77, 73, 129, 71, 51, 10, 92, 29, 80, 95, 33, 100, 124, 75, 38, 133, 79, 83, 61, 36, 122, 99, 16, 28, 18, 116, 140, 97, 119, 82, 148, 48, 56, 32, 93, 107, 69, 68, 123, 81, 22, 137, 25, 115, 44, 8, 42, 131, 143, 17, 55, 89, 9, 15, 19, 59, 146, 54, 5, 30, 41, 144, 117, 1, 104, 49, 105, 45, 88, 78, 74, 135, 0, 21, 57, 3, 66, 52, 63, 138, 4, 114, 37, 118, 14, 2, 26, 7, 65, 139, 39, 64, 90, 142, 53, 113}
b := BuildValidatorFromM2(a)
- c := XDPoS.ExtractValidatorsFromBytes(b)
+ c := utils.ExtractValidatorsFromBytes(b)
if !isArrayEqual([][]int64{a}, [][]int64{c}) {
t.Errorf("Fail to get m2 result %v", b)
}
diff --git a/core/blockchain.go b/core/blockchain.go
index cf9bb23a28..16a03ae5dd 100644
--- a/core/blockchain.go
+++ b/core/blockchain.go
@@ -37,6 +37,7 @@ import (
"github.com/XinFinOrg/XDPoSChain/common/mclock"
"github.com/XinFinOrg/XDPoSChain/consensus"
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS"
+ "github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils"
contractValidator "github.com/XinFinOrg/XDPoSChain/contracts/validator/contract"
"github.com/XinFinOrg/XDPoSChain/core/state"
"github.com/XinFinOrg/XDPoSChain/core/types"
@@ -877,8 +878,8 @@ func (bc *BlockChain) SaveData() {
var lendingTriedb *trie.Database
engine, _ := bc.Engine().(*XDPoS.XDPoS)
triedb := bc.stateCache.TrieDB()
- var tradingService XDPoS.TradingService
- var lendingService XDPoS.LendingService
+ var tradingService utils.TradingService
+ var lendingService utils.LendingService
if bc.Config().IsTIPXDCX(bc.CurrentBlock().Number()) && bc.chainConfig.XDPoS != nil && bc.CurrentBlock().NumberU64() > bc.chainConfig.XDPoS.Epoch && engine != nil {
tradingService = engine.GetXDCXService()
if tradingService != nil && tradingService.GetStateCache() != nil {
@@ -1201,9 +1202,9 @@ func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types.
}
engine, _ := bc.Engine().(*XDPoS.XDPoS)
var tradingTrieDb *trie.Database
- var tradingService XDPoS.TradingService
+ var tradingService utils.TradingService
var lendingTrieDb *trie.Database
- var lendingService XDPoS.LendingService
+ var lendingService utils.LendingService
if bc.Config().IsTIPXDCX(block.Number()) && bc.chainConfig.XDPoS != nil && block.NumberU64() > bc.chainConfig.XDPoS.Epoch && engine != nil {
tradingService = engine.GetXDCXService()
if tradingService != nil {
@@ -1541,8 +1542,8 @@ func (bc *BlockChain) insertChain(chain types.Blocks) (int, []interface{}, []*ty
// clear the previous dry-run cache
var tradingState *tradingstate.TradingStateDB
var lendingState *lendingstate.LendingStateDB
- var tradingService XDPoS.TradingService
- var lendingService XDPoS.LendingService
+ var tradingService utils.TradingService
+ var lendingService utils.LendingService
isSDKNode := false
if bc.Config().IsTIPXDCX(block.Number()) && bc.chainConfig.XDPoS != nil && engine != nil && block.NumberU64() > bc.chainConfig.XDPoS.Epoch {
tradingService = engine.GetXDCXService()
@@ -1827,8 +1828,8 @@ func (bc *BlockChain) getResultBlock(block *types.Block, verifiedM2 bool) (*Resu
var tradingState *tradingstate.TradingStateDB
var lendingState *lendingstate.LendingStateDB
- var tradingService XDPoS.TradingService
- var lendingService XDPoS.LendingService
+ var tradingService utils.TradingService
+ var lendingService utils.LendingService
isSDKNode := false
if bc.Config().IsTIPXDCX(block.Number()) && bc.chainConfig.XDPoS != nil && engine != nil && block.NumberU64() > bc.chainConfig.XDPoS.Epoch {
tradingService = engine.GetXDCXService()
@@ -2481,7 +2482,7 @@ func (bc *BlockChain) UpdateM1() error {
return err
}
- var ms []XDPoS.Masternode
+ var ms []utils.Masternode
for _, candidate := range candidates {
v, err := validator.GetCandidateCap(opts, candidate)
if err != nil {
@@ -2489,7 +2490,7 @@ func (bc *BlockChain) UpdateM1() error {
}
//TODO: smart contract shouldn't return "0x0000000000000000000000000000000000000000"
if candidate.String() != "xdc0000000000000000000000000000000000000000" {
- ms = append(ms, XDPoS.Masternode{Address: candidate, Stake: v})
+ ms = append(ms, utils.Masternode{Address: candidate, Stake: v})
}
}
if len(ms) == 0 {
diff --git a/eth/backend.go b/eth/backend.go
index e455f5720b..02a0fc0b37 100644
--- a/eth/backend.go
+++ b/eth/backend.go
@@ -41,6 +41,7 @@ import (
"github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/consensus"
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS"
+ "github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils"
"github.com/XinFinOrg/XDPoSChain/consensus/ethash"
"github.com/XinFinOrg/XDPoSChain/contracts"
contractValidator "github.com/XinFinOrg/XDPoSChain/contracts/validator/contract"
@@ -172,10 +173,10 @@ func New(ctx *node.ServiceContext, config *Config, XDCXServ *XDCx.XDCX, lendingS
)
if eth.chainConfig.XDPoS != nil {
c := eth.engine.(*XDPoS.XDPoS)
- c.GetXDCXService = func() XDPoS.TradingService {
+ c.GetXDCXService = func() utils.TradingService {
return eth.XDCX
}
- c.GetLendingService = func() XDPoS.LendingService {
+ c.GetLendingService = func() utils.LendingService {
return eth.Lending
}
}
@@ -471,7 +472,7 @@ func New(ctx *node.ServiceContext, config *Config, XDCXServ *XDCx.XDCX, lendingS
opts := new(bind.CallOpts)
var (
candidateAddresses []common.Address
- candidates []XDPoS.Masternode
+ candidates []utils.Masternode
)
stateDB, err := eth.blockchain.StateAt(eth.blockchain.GetBlockByHash(block).Root())
@@ -486,7 +487,7 @@ func New(ctx *node.ServiceContext, config *Config, XDCXServ *XDCx.XDCX, lendingS
return nil, err
}
if address.String() != "0x0000000000000000000000000000000000000000" {
- candidates = append(candidates, XDPoS.Masternode{Address: address, Stake: v})
+ candidates = append(candidates, utils.Masternode{Address: address, Stake: v})
}
}
// sort candidates by stake descending
@@ -565,7 +566,7 @@ func New(ctx *node.ServiceContext, config *Config, XDCXServ *XDCx.XDCX, lendingS
return err
}
if !bytes.Equal(header.Validators, validators) {
- return XDPoS.ErrInvalidCheckpointValidators
+ return utils.ErrInvalidCheckpointValidators
}
}
return nil
@@ -581,15 +582,7 @@ func New(ctx *node.ServiceContext, config *Config, XDCXServ *XDCx.XDCX, lendingS
// not genesis block
header = parentHeader
}
- snap, err := c.GetSnapshot(eth.blockchain, header)
- if err != nil {
- log.Error("Can't get snapshot with at ", "number", header.Number, "hash", header.Hash().Hex(), "err", err)
- return false
- }
- if _, ok := snap.Signers[address]; ok {
- return true
- }
- return false
+ return c.IsAuthorisedAddress(header, eth.blockchain, address)
}
}
@@ -756,11 +749,9 @@ func (s *Ethereum) ValidateMasternode() (bool, error) {
if s.chainConfig.XDPoS != nil {
//check if miner's wallet is in set of validators
c := s.engine.(*XDPoS.XDPoS)
- snap, err := c.GetSnapshot(s.blockchain, s.blockchain.CurrentHeader())
- if err != nil {
- return false, fmt.Errorf("Can't verify masternode permission: %v", err)
- }
- if _, authorized := snap.Signers[eb]; !authorized {
+
+ authorized := c.IsAuthorisedAddress(s.blockchain.CurrentHeader(), s.blockchain, eb)
+ if !authorized {
//This miner doesn't belong to set of validators
return false, nil
}
diff --git a/go.sum b/go.sum
index 59d71d648f..0394f8a511 100644
--- a/go.sum
+++ b/go.sum
@@ -210,6 +210,7 @@ github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
@@ -250,6 +251,7 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/syndtr/goleveldb v1.0.1-0.20190923125748-758128399b1d h1:gZZadD8H+fF+n9CmNhYL1Y0dJB+kLOmKd7FbPJLeGHs=
github.com/syndtr/goleveldb v1.0.1-0.20190923125748-758128399b1d/go.mod h1:9OrXJhf154huy1nPWmuSrkgjPUtUNhA+Zmy+6AESzuA=
@@ -334,6 +336,7 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWD
gopkg.in/urfave/cli.v1 v1.20.0 h1:NdAVW6RYxDif9DhDHaAortIu956m2c0v+09AZBPTbE0=
gopkg.in/urfave/cli.v1 v1.20.0/go.mod h1:vuBzUtMdQeixQj8LVd+/98pzhxNGQoyuPBlsXHOQNO0=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go
index 9a415ca4de..f337ee5228 100644
--- a/internal/ethapi/api.go
+++ b/internal/ethapi/api.go
@@ -37,6 +37,7 @@ import (
"github.com/XinFinOrg/XDPoSChain/common/hexutil"
"github.com/XinFinOrg/XDPoSChain/common/math"
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS"
+ "github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils"
"github.com/XinFinOrg/XDPoSChain/consensus/ethash"
contractValidator "github.com/XinFinOrg/XDPoSChain/contracts/validator/contract"
"github.com/XinFinOrg/XDPoSChain/core"
@@ -713,7 +714,7 @@ func (s *PublicBlockChainAPI) GetCandidateStatus(ctx context.Context, coinbaseAd
checkpointNumber rpc.BlockNumber
epochNumber rpc.EpochNumber // if epoch == "latest", print the latest epoch number to epochNumber
masternodes, penaltyList []common.Address
- candidates []XDPoS.Masternode
+ candidates []utils.Masternode
penalties []byte
err error
)
@@ -755,7 +756,7 @@ func (s *PublicBlockChainAPI) GetCandidateStatus(ctx context.Context, coinbaseAd
for _, address := range candidatesAddresses {
v := state.GetCandidateCap(statedb, address)
if address.String() != "0x0000000000000000000000000000000000000000" {
- candidates = append(candidates, XDPoS.Masternode{Address: address, Stake: v})
+ candidates = append(candidates, utils.Masternode{Address: address, Stake: v})
}
}
}
@@ -843,7 +844,7 @@ func (s *PublicBlockChainAPI) GetCandidates(ctx context.Context, epoch rpc.Epoch
epochNumber rpc.EpochNumber
masternodes []common.Address
penaltyList []common.Address
- candidates []XDPoS.Masternode
+ candidates []utils.Masternode
penalties []byte
err error
)
@@ -881,7 +882,7 @@ func (s *PublicBlockChainAPI) GetCandidates(ctx context.Context, epoch rpc.Epoch
for _, address := range candidatesAddresses {
v := state.GetCandidateCap(statedb, address)
if address.String() != "0x0000000000000000000000000000000000000000" {
- candidates = append(candidates, XDPoS.Masternode{Address: address, Stake: v})
+ candidates = append(candidates, utils.Masternode{Address: address, Stake: v})
}
}
}
@@ -939,7 +940,7 @@ func (s *PublicBlockChainAPI) GetCandidates(ctx context.Context, epoch rpc.Epoch
}
penaltyList = common.ExtractAddressFromBytes(penalties)
- var topCandidates []XDPoS.Masternode
+ var topCandidates []utils.Masternode
if len(candidates) > common.MaxMasternodes {
topCandidates = candidates[:common.MaxMasternodes]
} else {
@@ -984,33 +985,33 @@ func (s *PublicBlockChainAPI) GetPreviousCheckpointFromEpoch(ctx context.Context
}
// getCandidatesFromSmartContract returns all candidates with their capacities at the current time
-func (s *PublicBlockChainAPI) getCandidatesFromSmartContract() ([]XDPoS.Masternode, error) {
+func (s *PublicBlockChainAPI) getCandidatesFromSmartContract() ([]utils.Masternode, error) {
client, err := s.b.GetIPCClient()
if err != nil {
- return []XDPoS.Masternode{}, err
+ return []utils.Masternode{}, err
}
addr := common.HexToAddress(common.MasternodeVotingSMC)
validator, err := contractValidator.NewXDCValidator(addr, client)
if err != nil {
- return []XDPoS.Masternode{}, err
+ return []utils.Masternode{}, err
}
opts := new(bind.CallOpts)
candidates, err := validator.GetCandidates(opts)
if err != nil {
- return []XDPoS.Masternode{}, err
+ return []utils.Masternode{}, err
}
- var candidatesWithStakeInfo []XDPoS.Masternode
+ var candidatesWithStakeInfo []utils.Masternode
for _, candidate := range candidates {
v, err := validator.GetCandidateCap(opts, candidate)
if err != nil {
- return []XDPoS.Masternode{}, err
+ return []utils.Masternode{}, err
}
if candidate.String() != "0x0000000000000000000000000000000000000000" {
- candidatesWithStakeInfo = append(candidatesWithStakeInfo, XDPoS.Masternode{Address: candidate, Stake: v})
+ candidatesWithStakeInfo = append(candidatesWithStakeInfo, utils.Masternode{Address: candidate, Stake: v})
}
if len(candidatesWithStakeInfo) > 0 {
diff --git a/miner/worker.go b/miner/worker.go
index 0ea43a9457..114511b4d6 100644
--- a/miner/worker.go
+++ b/miner/worker.go
@@ -34,6 +34,7 @@ import (
"github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/consensus"
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS"
+ "github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils"
"github.com/XinFinOrg/XDPoSChain/consensus/misc"
"github.com/XinFinOrg/XDPoSChain/contracts"
"github.com/XinFinOrg/XDPoSChain/core"
@@ -396,12 +397,9 @@ func (self *worker) wait() {
if self.config.XDPoS != nil {
c := self.engine.(*XDPoS.XDPoS)
- snap, err := c.GetSnapshot(self.chain, block.Header())
- if err != nil {
- log.Error("Fail to get snapshot for sign tx signer.")
- return
- }
- if _, authorized := snap.Signers[self.coinbase]; !authorized {
+
+ authorized := c.IsAuthorisedAddress(block.Header(), self.chain, self.coinbase)
+ if !authorized {
valid := false
masternodes := c.GetMasternodes(self.chain, block.Header())
for _, m := range masternodes {
@@ -542,7 +540,7 @@ func (self *worker) commitNewWork() {
// you're not allowed to create this block
return
}
- h := XDPoS.Hop(len, preIndex, curIndex)
+ h := utils.Hop(len, preIndex, curIndex)
gap := waitPeriod * int64(h)
// Check nearest checkpoint block in hop range.
nearest := self.config.XDPoS.Epoch - (parent.Header().Number.Uint64() % self.config.XDPoS.Epoch)
diff --git a/params/config.go b/params/config.go
index 2523925021..9f218c582b 100644
--- a/params/config.go
+++ b/params/config.go
@@ -183,6 +183,14 @@ func (c *XDPoSConfig) String() string {
return "XDPoS"
}
+/**
+ConsensusVersion will return the consensus version to use for the provided block number. The returned int represent its version
+TODO: It's a dummy value for now until the 2.0 consensus engine is fully implemented.
+*/
+func BlockConsensusVersion(num *big.Int) int {
+ return 1
+}
+
// String implements the fmt.Stringer interface.
func (c *ChainConfig) String() string {
var engine interface{}