Seperate hooks from XDPoS adaptor

This commit is contained in:
Jianrong 2021-10-04 16:13:43 +11:00
parent 30811ee58c
commit 0aec2a1e9e
7 changed files with 1449 additions and 4446 deletions

View file

@ -27,6 +27,7 @@ import (
"github.com/XinFinOrg/XDPoSChain/core/state"
"github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/ethdb"
"github.com/XinFinOrg/XDPoSChain/log"
"github.com/XinFinOrg/XDPoSChain/params"
"github.com/XinFinOrg/XDPoSChain/rpc"
lru "github.com/hashicorp/golang-lru"
@ -46,16 +47,14 @@ type XDPoS struct {
config *params.XDPoSConfig // Consensus engine configuration parameters
db ethdb.Database // Database to store and retrieve snapshot checkpoints
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)
// Transaction cache, only make sense for adaptor level
signingTxsCache *lru.Cache
// Trading and lending service
GetXDCXService func() utils.TradingService
GetLendingService func() utils.LendingService
// The exact consensus engine with different versions
EngineV1 engine_v1.XDPoS_v1
}
@ -69,13 +68,13 @@ func New(config *params.XDPoSConfig, db ethdb.Database) *XDPoS {
}
// Allocate the snapshot caches and create the engine
BlockSigners, _ := lru.New(utils.BlockSignersCacheLimit)
signingTxsCache, _ := lru.New(utils.BlockSignersCacheLimit)
return &XDPoS{
config: &conf,
BlockSigners: BlockSigners,
EngineV1: *engine_v1.New(&conf, db),
signingTxsCache: signingTxsCache,
EngineV1: *engine_v1.New(&conf, db),
}
}
@ -248,13 +247,6 @@ func (c *XDPoS) GetMasternodesFromCheckpointHeader(preCheckpointHeader *types.He
}
}
func (c *XDPoS) CacheData(header *types.Header, txs []*types.Transaction, receipts []*types.Receipt) []*types.Transaction {
switch params.BlockConsensusVersion(header.Number) {
default: // Default "1.0"
return c.EngineV1.CacheData(header, txs, receipts)
}
}
// Same DB across all consensus engines
func (c *XDPoS) GetDb() ethdb.Database {
return c.db
@ -283,6 +275,58 @@ func (c *XDPoS) GetAuthorisedSignersFromSnapshot(chain consensus.ChainReader, he
}
}
/**
Caching
*/
// Cache signing transaction data into BlockSingers cache object
func (c *XDPoS) CacheNoneTIPSigningTxs(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.signingTxsCache.Add(header.Hash(), signTxs)
return signTxs
}
// Cache
func (c *XDPoS) CacheSigningTxs(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.signingTxsCache.Add(hash, signTxs)
return signTxs
}
func (c *XDPoS) GetCachedSignerData(hash common.Hash) (interface{}, bool) {
return c.signingTxsCache.Get(hash)
}
// TODO: (Hashlab) Can be further refactored
func (c *XDPoS) CheckMNTurn(chain consensus.ChainReader, parent *types.Header, signer common.Address) bool {
switch params.BlockConsensusVersion(parent.Number) {
@ -291,11 +335,6 @@ func (c *XDPoS) CheckMNTurn(chain consensus.ChainReader, parent *types.Header, s
}
}
// 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()

View file

@ -112,14 +112,12 @@ type XDPoS_v1 struct {
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)
}
@ -131,16 +129,15 @@ func New(config *params.XDPoSConfig, db ethdb.Database) *XDPoS_v1 {
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,
config: &conf,
db: db,
recents: recents,
signatures: signatures,
verifiedHeaders: verifiedHeaders,
@ -989,48 +986,6 @@ func (c *XDPoS_v1) GetMasternodesFromCheckpointHeader(preCheckpointHeader *types
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
}

View file

@ -332,16 +332,16 @@ func GetRewardForCheckpoint(c *XDPoS.XDPoS, chain consensus.ChainReader, header
for i := prevCheckpoint + (rCheckpoint * 2) - 1; i >= startBlockNumber; i-- {
header = chain.GetHeader(header.ParentHash, i)
mapBlkHash[i] = header.Hash()
signData, ok := c.BlockSigners.Get(header.Hash())
signData, ok := c.GetCachedSignerData(header.Hash())
if !ok {
log.Debug("Failed get from cached", "hash", header.Hash().String(), "number", i)
block := chain.GetBlock(header.Hash(), i)
txs := block.Transactions()
if !chain.Config().IsTIPSigning(header.Number) {
receipts := core.GetBlockReceipts(c.GetDb(), header.Hash(), i)
signData = c.CacheData(header, txs, receipts)
signData = c.CacheNoneTIPSigningTxs(header, txs, receipts)
} else {
signData = c.CacheSigner(header.Hash(), txs)
signData = c.CacheSigningTxs(header.Hash(), txs)
}
}
txs := signData.([]*types.Transaction)

View file

@ -674,7 +674,7 @@ func (bc *BlockChain) insert(block *types.Block) {
if bc.chainConfig.XDPoS != nil && !bc.chainConfig.IsTIPSigning(block.Number()) {
engine, ok := bc.Engine().(*XDPoS.XDPoS)
if ok {
engine.CacheData(block.Header(), block.Transactions(), bc.GetReceiptsByHash(block.Hash()))
engine.CacheNoneTIPSigningTxs(block.Header(), block.Transactions(), bc.GetReceiptsByHash(block.Hash()))
}
}
@ -1375,7 +1375,7 @@ func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types.
if bc.chainConfig.XDPoS != nil && bc.chainConfig.IsTIPSigning(block.Number()) {
engine, ok := bc.Engine().(*XDPoS.XDPoS)
if ok {
engine.CacheSigner(block.Header().Hash(), block.Transactions())
engine.CacheSigningTxs(block.Header().Hash(), block.Transactions())
}
}
bc.futureBlocks.Remove(block.Hash())

File diff suppressed because it is too large Load diff

View file

@ -48,7 +48,6 @@ import (
"github.com/XinFinOrg/XDPoSChain/core"
"github.com/XinFinOrg/XDPoSChain/core/bloombits"
//"github.com/XinFinOrg/XDPoSChain/core/state"
"github.com/XinFinOrg/XDPoSChain/XDCx"
"github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/core/vm"
@ -288,20 +287,112 @@ func New(ctx *node.ServiceContext, config *Config, XDCXServ *XDCx.XDCX, lendingS
eth.protocolManager.fetcher.SetSignHook(signHook)
eth.protocolManager.fetcher.SetAppendM2HeaderHook(appendM2HeaderHook)
// Hook prepares validators M2 for the current epoch at checkpoint block
c.HookValidator = func(header *types.Header, signers []common.Address) ([]byte, error) {
start := time.Now()
validators, err := GetValidators(eth.blockchain, signers)
if err != nil {
return []byte{}, err
/*
XDPoS1.0 Specific hooks
*/
// Hook calculates reward for masternodes
c.EngineV1.HookReward = func(chain consensus.ChainReader, stateBlock *state.StateDB, parentState *state.StateDB, header *types.Header) (error, map[string]interface{}) {
number := header.Number.Uint64()
rCheckpoint := chain.Config().XDPoS.RewardCheckpoint
foundationWalletAddr := chain.Config().XDPoS.FoudationWalletAddr
if foundationWalletAddr == (common.Address{}) {
log.Error("Foundation Wallet Address is empty", "error", foundationWalletAddr)
return errors.New("Foundation Wallet Address is empty"), nil
}
header.Validators = validators
log.Debug("Time Calculated HookValidator ", "block", header.Number.Uint64(), "time", common.PrettyDuration(time.Since(start)))
return validators, nil
rewards := make(map[string]interface{})
if number > 0 && number-rCheckpoint > 0 && foundationWalletAddr != (common.Address{}) {
start := time.Now()
// Get signers in blockSigner smartcontract.
// Get reward inflation.
chainReward := new(big.Int).Mul(new(big.Int).SetUint64(chain.Config().XDPoS.Reward), new(big.Int).SetUint64(params.Ether))
chainReward = rewardInflation(chain, chainReward, number, common.BlocksPerYear)
totalSigner := new(uint64)
signers, err := contracts.GetRewardForCheckpoint(c, chain, header, rCheckpoint, totalSigner)
log.Debug("Time Get Signers", "block", header.Number.Uint64(), "time", common.PrettyDuration(time.Since(start)))
if err != nil {
log.Crit("Fail to get signers for reward checkpoint", "error", err)
}
rewards["signers"] = signers
rewardSigners, err := contracts.CalculateRewardForSigner(chainReward, signers, *totalSigner)
if err != nil {
log.Crit("Fail to calculate reward for signers", "error", err)
}
// Add reward for coin holders.
voterResults := make(map[common.Address]interface{})
if len(signers) > 0 {
for signer, calcReward := range rewardSigners {
err, rewards := contracts.CalculateRewardForHolders(foundationWalletAddr, parentState, signer, calcReward, number)
if err != nil {
log.Crit("Fail to calculate reward for holders.", "error", err)
}
if len(rewards) > 0 {
for holder, reward := range rewards {
stateBlock.AddBalance(holder, reward)
}
}
voterResults[signer] = rewards
}
}
rewards["rewards"] = voterResults
log.Debug("Time Calculated HookReward ", "block", header.Number.Uint64(), "time", common.PrettyDuration(time.Since(start)))
}
return nil, rewards
}
/*
HookGetSignersFromContract return list masternode for current state (block)
This is a solution for work around issue return wrong list signers from snapshot
*/
c.EngineV1.HookGetSignersFromContract = func(block common.Hash) ([]common.Address, error) {
client, err := eth.blockchain.GetClient()
if err != nil {
return nil, err
}
addr := common.HexToAddress(common.MasternodeVotingSMC)
validator, err := contractValidator.NewXDCValidator(addr, client)
if err != nil {
return nil, err
}
opts := new(bind.CallOpts)
var (
candidateAddresses []common.Address
candidates []utils.Masternode
)
stateDB, err := eth.blockchain.StateAt(eth.blockchain.GetBlockByHash(block).Root())
candidateAddresses = state.GetCandidates(stateDB)
if err != nil {
return nil, err
}
for _, address := range candidateAddresses {
v, err := validator.GetCandidateCap(opts, address)
if err != nil {
return nil, err
}
if address.String() != "0x0000000000000000000000000000000000000000" {
candidates = append(candidates, utils.Masternode{Address: address, Stake: v})
}
}
// sort candidates by stake descending
sort.Slice(candidates, func(i, j int) bool {
return candidates[i].Stake.Cmp(candidates[j].Stake) >= 0
})
if len(candidates) > 150 {
candidates = candidates[:150]
}
result := []common.Address{}
for _, candidate := range candidates {
result = append(result, candidate.Address)
}
return result, nil
}
// Hook scans for bad masternodes and decide to penalty them
c.HookPenalty = func(chain consensus.ChainReader, blockNumberEpoc uint64) ([]common.Address, error) {
c.EngineV1.HookPenalty = func(chain consensus.ChainReader, blockNumberEpoc uint64) ([]common.Address, error) {
canonicalState, err := eth.blockchain.State()
if canonicalState == nil || err != nil {
log.Crit("Can't get state at head of canonical chain", "head number", eth.blockchain.CurrentHeader().Number.Uint64(), "err", err)
@ -347,7 +438,7 @@ func New(ctx *node.ServiceContext, config *Config, XDCXServ *XDCx.XDCX, lendingS
}
// Hook scans for bad masternodes and decide to penalty them
c.HookPenaltyTIPSigning = func(chain consensus.ChainReader, header *types.Header, candidates []common.Address) ([]common.Address, error) {
c.EngineV1.HookPenaltyTIPSigning = func(chain consensus.ChainReader, header *types.Header, candidates []common.Address) ([]common.Address, error) {
prevEpoc := header.Number.Uint64() - chain.Config().XDPoS.Epoch
combackEpoch := uint64(0)
comebackLength := (common.LimitPenaltyEpoch + 1) * chain.Config().XDPoS.Epoch
@ -419,11 +510,11 @@ func New(ctx *node.ServiceContext, config *Config, XDCXServ *XDCx.XDCX, lendingS
if blockNumber%common.MergeSignRange == 0 {
mapBlockHash[bhash] = true
}
signData, ok := c.BlockSigners.Get(bhash)
signData, ok := c.GetCachedSignerData(bhash)
if !ok {
block := chain.GetBlock(bhash, blockNumber)
txs := block.Transactions()
signData = c.CacheSigner(bhash, txs)
signData = c.CacheSigningTxs(bhash, txs)
}
txs := signData.([]*types.Transaction)
// Check signer signed?
@ -454,113 +545,24 @@ func New(ctx *node.ServiceContext, config *Config, XDCXServ *XDCx.XDCX, lendingS
}
return []common.Address{}, nil
}
/*
HookGetSignersFromContract return list masternode for current state (block)
This is a solution for work around issue return wrong list signers from snapshot
*/
c.HookGetSignersFromContract = func(block common.Hash) ([]common.Address, error) {
client, err := eth.blockchain.GetClient()
// Hook prepares validators M2 for the current epoch at checkpoint block
c.EngineV1.HookValidator = func(header *types.Header, signers []common.Address) ([]byte, error) {
start := time.Now()
validators, err := getValidators(eth.blockchain, signers)
if err != nil {
return nil, err
return []byte{}, err
}
addr := common.HexToAddress(common.MasternodeVotingSMC)
validator, err := contractValidator.NewXDCValidator(addr, client)
if err != nil {
return nil, err
}
opts := new(bind.CallOpts)
var (
candidateAddresses []common.Address
candidates []utils.Masternode
)
stateDB, err := eth.blockchain.StateAt(eth.blockchain.GetBlockByHash(block).Root())
candidateAddresses = state.GetCandidates(stateDB)
if err != nil {
return nil, err
}
for _, address := range candidateAddresses {
v, err := validator.GetCandidateCap(opts, address)
if err != nil {
return nil, err
}
if address.String() != "0x0000000000000000000000000000000000000000" {
candidates = append(candidates, utils.Masternode{Address: address, Stake: v})
}
}
// sort candidates by stake descending
sort.Slice(candidates, func(i, j int) bool {
return candidates[i].Stake.Cmp(candidates[j].Stake) >= 0
})
if len(candidates) > 150 {
candidates = candidates[:150]
}
result := []common.Address{}
for _, candidate := range candidates {
result = append(result, candidate.Address)
}
return result, nil
}
// Hook calculates reward for masternodes
c.HookReward = func(chain consensus.ChainReader, stateBlock *state.StateDB, parentState *state.StateDB, header *types.Header) (error, map[string]interface{}) {
number := header.Number.Uint64()
rCheckpoint := chain.Config().XDPoS.RewardCheckpoint
foundationWalletAddr := chain.Config().XDPoS.FoudationWalletAddr
if foundationWalletAddr == (common.Address{}) {
log.Error("Foundation Wallet Address is empty", "error", foundationWalletAddr)
return errors.New("Foundation Wallet Address is empty"), nil
}
rewards := make(map[string]interface{})
if number > 0 && number-rCheckpoint > 0 && foundationWalletAddr != (common.Address{}) {
start := time.Now()
// Get signers in blockSigner smartcontract.
// Get reward inflation.
chainReward := new(big.Int).Mul(new(big.Int).SetUint64(chain.Config().XDPoS.Reward), new(big.Int).SetUint64(params.Ether))
chainReward = rewardInflation(chain, chainReward, number, common.BlocksPerYear)
totalSigner := new(uint64)
signers, err := contracts.GetRewardForCheckpoint(c, chain, header, rCheckpoint, totalSigner)
log.Debug("Time Get Signers", "block", header.Number.Uint64(), "time", common.PrettyDuration(time.Since(start)))
if err != nil {
log.Crit("Fail to get signers for reward checkpoint", "error", err)
}
rewards["signers"] = signers
rewardSigners, err := contracts.CalculateRewardForSigner(chainReward, signers, *totalSigner)
if err != nil {
log.Crit("Fail to calculate reward for signers", "error", err)
}
// Add reward for coin holders.
voterResults := make(map[common.Address]interface{})
if len(signers) > 0 {
for signer, calcReward := range rewardSigners {
err, rewards := contracts.CalculateRewardForHolders(foundationWalletAddr, parentState, signer, calcReward, number)
if err != nil {
log.Crit("Fail to calculate reward for holders.", "error", err)
}
if len(rewards) > 0 {
for holder, reward := range rewards {
stateBlock.AddBalance(holder, reward)
}
}
voterResults[signer] = rewards
}
}
rewards["rewards"] = voterResults
log.Debug("Time Calculated HookReward ", "block", header.Number.Uint64(), "time", common.PrettyDuration(time.Since(start)))
}
return nil, rewards
header.Validators = validators
log.Debug("Time Calculated HookValidator ", "block", header.Number.Uint64(), "time", common.PrettyDuration(time.Since(start)))
return validators, nil
}
// Hook verifies masternodes set
c.HookVerifyMNs = func(header *types.Header, signers []common.Address) error {
c.EngineV1.HookVerifyMNs = func(header *types.Header, signers []common.Address) error {
number := header.Number.Int64()
if number > 0 && number%common.EpocBlockRandomize == 0 {
start := time.Now()
validators, err := GetValidators(eth.blockchain, signers)
validators, err := getValidators(eth.blockchain, signers)
log.Debug("Time Calculated HookVerifyMNs ", "block", header.Number.Uint64(), "time", common.PrettyDuration(time.Since(start)))
if err != nil {
return err
@ -881,7 +883,7 @@ func (s *Ethereum) Stop() error {
return nil
}
func GetValidators(bc *core.BlockChain, masternodes []common.Address) ([]byte, error) {
func getValidators(bc *core.BlockChain, masternodes []common.Address) ([]byte, error) {
if bc.Config().XDPoS == nil {
return nil, core.ErrNotXDPoS
}

View file

@ -2953,7 +2953,7 @@ func GetSignersFromBlocks(b Backend, blockNumber uint64, blockHash common.Hash,
return addrs, err
}
blockData, err := b.BlockByNumber(nil, rpc.BlockNumber(i))
signTxs := engine.CacheSigner(header.Hash(), blockData.Transactions())
signTxs := engine.CacheSigningTxs(header.Hash(), blockData.Transactions())
for _, signtx := range signTxs {
blkHash := common.BytesToHash(signtx.Data()[len(signtx.Data())-32:])
from, _ := types.Sender(signer, signtx)