mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-06-19 21:31:37 +00:00
* feat: GetTokenSupply API, total minted and burned * feat: token supply API finish burned token. rename minted record functions * fix(api): handle edge case about minus 1 for epoch in token supply * fix: check both total minted and burned before breaking loop * style: modify minor style * style: modify by comment and rebase code * chore: modify test based on statedb_utils
612 lines
22 KiB
Go
612 lines
22 KiB
Go
package hooks
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"math/big"
|
|
"slices"
|
|
"time"
|
|
|
|
"github.com/XinFinOrg/XDPoSChain/common"
|
|
"github.com/XinFinOrg/XDPoSChain/common/math"
|
|
"github.com/XinFinOrg/XDPoSChain/consensus"
|
|
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS"
|
|
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils"
|
|
"github.com/XinFinOrg/XDPoSChain/contracts"
|
|
"github.com/XinFinOrg/XDPoSChain/core"
|
|
"github.com/XinFinOrg/XDPoSChain/core/state"
|
|
"github.com/XinFinOrg/XDPoSChain/core/tracing"
|
|
"github.com/XinFinOrg/XDPoSChain/core/types"
|
|
"github.com/XinFinOrg/XDPoSChain/eth/util"
|
|
"github.com/XinFinOrg/XDPoSChain/log"
|
|
"github.com/XinFinOrg/XDPoSChain/params"
|
|
)
|
|
|
|
// Declaring an enum type Beneficiary of reward
|
|
type Beneficiary int
|
|
|
|
// Enumerating reward beneficiary
|
|
const (
|
|
MasterNodeBeneficiary Beneficiary = iota
|
|
ProtectorNodeBeneficiary
|
|
ObserverNodeBeneficiary
|
|
)
|
|
|
|
type RewardLog struct {
|
|
Sign uint64 `json:"sign"`
|
|
Reward *big.Int `json:"reward"`
|
|
}
|
|
|
|
func AttachConsensusV2Hooks(adaptor *XDPoS.XDPoS, bc *core.BlockChain, chainConfig *params.ChainConfig) {
|
|
// Hook scans for bad masternodes and decide to penalty them
|
|
adaptor.EngineV2.HookPenalty = func(chain consensus.ChainReader, number *big.Int, parentHash common.Hash, candidates []common.Address) ([]common.Address, error) {
|
|
start := time.Now()
|
|
listBlockHash := []common.Hash{}
|
|
// get list block hash & stats total created block
|
|
statMiners := make(map[common.Address]int)
|
|
listBlockHash = append(listBlockHash, parentHash)
|
|
parentNumber := number.Uint64() - 1
|
|
currentHash := parentHash
|
|
|
|
var round types.Round
|
|
// check and wait the latest block is already in the disk
|
|
// sometimes blocks are yet inserted into block
|
|
for timeout := 0; ; timeout++ {
|
|
parentHeader := chain.GetHeader(parentHash, parentNumber)
|
|
if parentHeader != nil { // found the latest block in the disk
|
|
// extract round number from the lastest block
|
|
r, err := adaptor.EngineV2.GetRoundNumber(parentHeader)
|
|
if err != nil {
|
|
log.Error("[V2 Hook Penalty] Fail to get round", "error", err)
|
|
return nil, err
|
|
}
|
|
round = r
|
|
break
|
|
}
|
|
log.Info("[V2 Hook Penalty] parentHeader is nil, wait block to be written in disk", "parentNumber", parentNumber)
|
|
time.Sleep(time.Second) // 1s
|
|
|
|
if timeout > 30 { // wait over 30s
|
|
log.Error("[V2 Hook Penalty] parentHeader is nil, wait too long not written in to disk", "parentNumber", parentNumber)
|
|
return []common.Address{}, errors.New("parentHeader is nil")
|
|
}
|
|
}
|
|
|
|
for i := uint64(1); ; i++ {
|
|
parentHeader := chain.GetHeader(parentHash, parentNumber)
|
|
if parentHeader == nil {
|
|
log.Error("[HookPenalty] fail to get parent header")
|
|
return []common.Address{}, fmt.Errorf("hook penalty fail to get parent header at number: %v, hash: %v", parentNumber, parentHash)
|
|
}
|
|
isEpochSwitch, _, err := adaptor.EngineV2.IsEpochSwitch(parentHeader)
|
|
if err != nil {
|
|
log.Error("[HookPenalty] isEpochSwitch", "err", err)
|
|
return []common.Address{}, err
|
|
}
|
|
if isEpochSwitch {
|
|
break
|
|
}
|
|
miner := parentHeader.Coinbase // we can directly use coinbase, since it's verified
|
|
_, exist := statMiners[miner]
|
|
if exist {
|
|
statMiners[miner]++
|
|
} else {
|
|
statMiners[miner] = 1
|
|
}
|
|
parentNumber--
|
|
parentHash = parentHeader.ParentHash
|
|
listBlockHash = append(listBlockHash, parentHash)
|
|
}
|
|
|
|
currentConfig := chain.Config().XDPoS.V2.Config(uint64(round))
|
|
// add list not miner to penalties
|
|
preMasternodes := adaptor.EngineV2.GetMasternodesByHash(chain, currentHash)
|
|
penalties := []common.Address{}
|
|
minimunMinerBlockPerEpoch := common.MinimunMinerBlockPerEpoch
|
|
if chain.Config().IsTIPUpgradePenalty(number) {
|
|
minimunMinerBlockPerEpoch = currentConfig.MinimumMinerBlockPerEpoch
|
|
}
|
|
for miner, total := range statMiners {
|
|
if total < minimunMinerBlockPerEpoch {
|
|
log.Info("[HookPenalty] Find a node does not create enough block", "addr", miner.Hex(), "total", total, "require", minimunMinerBlockPerEpoch)
|
|
penalties = append(penalties, miner)
|
|
}
|
|
}
|
|
for _, addr := range preMasternodes {
|
|
if _, exist := statMiners[addr]; !exist {
|
|
log.Info("[HookPenalty] Find a node do not create any block", "addr", addr.Hex())
|
|
penalties = append(penalties, addr)
|
|
}
|
|
}
|
|
|
|
// get list check penalties signing block & list master nodes wil comeback
|
|
// start to calc comeback at v2 block + limitPenaltyEpochV2 to avoid reading v1 blocks
|
|
|
|
if !chain.Config().IsTIPUpgradePenalty(number) {
|
|
comebackHeight := (common.LimitPenaltyEpochV2+1)*chain.Config().XDPoS.Epoch + chain.Config().XDPoS.V2.SwitchBlock.Uint64()
|
|
penComebacks := []common.Address{}
|
|
if number.Uint64() > comebackHeight {
|
|
pens := adaptor.EngineV2.GetPreviousPenaltyByHash(chain, currentHash, common.LimitPenaltyEpochV2)
|
|
for _, p := range pens {
|
|
for _, addr := range candidates {
|
|
if p == addr {
|
|
log.Info("[HookPenalty] get previous penalty node and add into comeback list", "addr", addr)
|
|
penComebacks = append(penComebacks, p)
|
|
break
|
|
}
|
|
}
|
|
}
|
|
// Loop for each block to check missing sign. with comeback nodes
|
|
mapBlockHash := map[common.Hash]bool{}
|
|
startRange := common.RangeReturnSigner - 1
|
|
// to prevent visiting outside index of listBlockHash
|
|
if startRange >= len(listBlockHash) {
|
|
startRange = len(listBlockHash) - 1
|
|
}
|
|
for i := startRange; i >= 0; i-- {
|
|
if len(penComebacks) == 0 {
|
|
break
|
|
}
|
|
blockNumber := number.Uint64() - uint64(i) - 1
|
|
bhash := listBlockHash[i]
|
|
if blockNumber%common.MergeSignRange == 0 {
|
|
mapBlockHash[bhash] = true
|
|
}
|
|
signingTxs, ok := adaptor.GetCachedSigningTxs(bhash)
|
|
if !ok {
|
|
block := chain.GetBlock(bhash, blockNumber)
|
|
if block != nil {
|
|
txs := block.Transactions()
|
|
signingTxs = adaptor.CacheSigningTxs(bhash, txs)
|
|
}
|
|
}
|
|
// Check signer signed?
|
|
for _, tx := range signingTxs {
|
|
blkHash := common.BytesToHash(tx.Data()[len(tx.Data())-32:])
|
|
from := *tx.From()
|
|
if mapBlockHash[blkHash] {
|
|
for j, addr := range penComebacks {
|
|
if from == addr {
|
|
// Remove it from dupSigners.
|
|
penComebacks = append(penComebacks[:j], penComebacks[j+1:]...)
|
|
break
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for _, comeback := range penComebacks {
|
|
ok := true
|
|
for _, p := range penalties {
|
|
if p == comeback {
|
|
ok = false
|
|
break
|
|
}
|
|
}
|
|
if ok {
|
|
penalties = append(penalties, comeback)
|
|
}
|
|
}
|
|
}
|
|
} else { // after penalty upgrade
|
|
limitPenaltyEpoch := 1
|
|
if currentConfig.LimitPenaltyEpoch > 0 {
|
|
// if non-zero parameter, use it
|
|
limitPenaltyEpoch = currentConfig.LimitPenaltyEpoch
|
|
}
|
|
comebackHeight := uint64(limitPenaltyEpoch)*chain.Config().XDPoS.Epoch + chain.Config().XDPoS.V2.SwitchBlock.Uint64()
|
|
if number.Uint64() > comebackHeight {
|
|
// penParolees record those who stayed enough epoch of LimitPenaltyEpoch
|
|
penParoleeMap := map[common.Address]int{}
|
|
// lastPenalty record the last epoch penalties
|
|
lastPenalty := []common.Address{}
|
|
for i := 0; i < limitPenaltyEpoch; i++ {
|
|
pens := adaptor.EngineV2.GetPreviousPenaltyByHash(chain, currentHash, i)
|
|
for _, p := range pens {
|
|
penParoleeMap[p]++
|
|
}
|
|
if i == 0 {
|
|
// record the last epoch penalties
|
|
lastPenalty = pens
|
|
}
|
|
}
|
|
|
|
// Loop for each block to check missing sign. with comeback nodes
|
|
mapBlockHash := map[common.Hash]bool{}
|
|
txSignerMap := map[common.Address]int{}
|
|
startRange := int(chain.Config().XDPoS.Epoch) - 1
|
|
// to prevent visiting outside index of listBlockHash
|
|
if startRange >= len(listBlockHash) {
|
|
startRange = len(listBlockHash) - 1
|
|
}
|
|
for i := startRange; i >= 0; i-- {
|
|
blockNumber := number.Uint64() - uint64(i) - 1
|
|
bhash := listBlockHash[i]
|
|
if blockNumber%common.MergeSignRange == 0 {
|
|
mapBlockHash[bhash] = true
|
|
}
|
|
signingTxs, ok := adaptor.GetCachedSigningTxs(bhash)
|
|
if !ok {
|
|
block := chain.GetBlock(bhash, blockNumber)
|
|
if block != nil {
|
|
txs := block.Transactions()
|
|
signingTxs = adaptor.CacheSigningTxs(bhash, txs)
|
|
}
|
|
}
|
|
// Check signer signed?
|
|
for _, tx := range signingTxs {
|
|
blkHash := common.BytesToHash(tx.Data()[len(tx.Data())-32:])
|
|
from := *tx.From()
|
|
if mapBlockHash[blkHash] {
|
|
txSignerMap[from]++
|
|
}
|
|
}
|
|
}
|
|
// check addr in lastPenalty, and if they does not meet condition, add them to penalty
|
|
for _, p := range lastPenalty {
|
|
if penParoleeMap[p] == limitPenaltyEpoch {
|
|
// check if this node signs enough
|
|
if txSignerMap[p] >= currentConfig.MinimumSigningTx {
|
|
continue
|
|
}
|
|
}
|
|
// reaches here means that the node should still stays in penalty list
|
|
penalties = append(penalties, p)
|
|
}
|
|
}
|
|
}
|
|
|
|
for i, p := range penalties {
|
|
log.Info("[HookPenalty] Final penalty list", "index", i, "addr", p)
|
|
}
|
|
log.Info("[HookPenalty] Time Calculated HookPenaltyV2 ", "block", number, "time", common.PrettyDuration(time.Since(start)))
|
|
return penalties, nil
|
|
}
|
|
|
|
// Hook calculates reward for masternodes
|
|
adaptor.EngineV2.HookReward = func(chain consensus.ChainReader, stateBlock *state.StateDB, parentState *state.StateDB, header *types.Header) (map[string]interface{}, error) {
|
|
number := header.Number.Uint64()
|
|
foundationWalletAddr := chain.Config().XDPoS.FoudationWalletAddr
|
|
if foundationWalletAddr == (common.Address{}) {
|
|
log.Error("Foundation Wallet Address is empty", "error", foundationWalletAddr)
|
|
return nil, errors.New("foundation wallet address is empty")
|
|
}
|
|
rewardsMap := make(map[string]interface{})
|
|
// skip hook reward if this is the first v2
|
|
if number == chain.Config().XDPoS.V2.SwitchBlock.Uint64()+1 {
|
|
return rewardsMap, nil
|
|
}
|
|
start := time.Now()
|
|
|
|
round, err := adaptor.EngineV2.GetRoundNumber(header)
|
|
epochNum := chain.Config().XDPoS.V2.SwitchEpoch + uint64(round)/chain.Config().XDPoS.Epoch
|
|
if err != nil {
|
|
log.Error("[HookReward] Fail to get round", "error", err)
|
|
return nil, err
|
|
}
|
|
currentConfig := chain.Config().XDPoS.V2.Config(uint64(round))
|
|
// Get signers/signing tx count, and burned tokens in one epoch
|
|
signers, burnedInOneEpoch, err := GetSigningTxCount(adaptor, chain, header, parentState, currentConfig)
|
|
|
|
log.Debug("Time Get Signers", "block", header.Number.Uint64(), "time", common.PrettyDuration(time.Since(start)))
|
|
if err != nil {
|
|
log.Error("[HookReward] Fail to get signers count for reward checkpoint", "error", err)
|
|
return nil, err
|
|
}
|
|
rewardsMap["signers"] = signers[MasterNodeBeneficiary]
|
|
|
|
if !chain.Config().IsTIPUpgradeReward(header.Number) {
|
|
// Get reward inflation.
|
|
originalReward := new(big.Int).Mul(new(big.Int).SetUint64(chain.Config().XDPoS.Reward), new(big.Int).SetUint64(params.Ether))
|
|
chainReward := util.RewardInflation(chain, originalReward, number, common.BlocksPerYear)
|
|
rewardSigners, err := CalculateRewardForSigner(chainReward, signers[MasterNodeBeneficiary])
|
|
if err != nil {
|
|
log.Error("[HookReward] Fail to calculate reward for masternode", "error", err)
|
|
return nil, err
|
|
}
|
|
// Add reward for coin holders.
|
|
rewardResults := make(map[common.Address]interface{})
|
|
for signer, calcReward := range rewardSigners {
|
|
rewards, err := contracts.CalculateRewardForHolders(foundationWalletAddr, parentState, signer, calcReward, number)
|
|
if err != nil {
|
|
log.Error("[HookReward] Fail to calculate reward for holders.", "error", err)
|
|
return nil, err
|
|
}
|
|
if len(rewards) > 0 {
|
|
for holder, reward := range rewards {
|
|
stateBlock.AddBalance(holder, reward, tracing.BalanceIncreaseRewardMineBlock)
|
|
}
|
|
}
|
|
rewardResults[signer] = rewards
|
|
}
|
|
rewardsMap["rewards"] = rewardResults
|
|
} else {
|
|
rewardsMap["signersProtector"] = signers[ProtectorNodeBeneficiary]
|
|
rewardsMap["signersObserver"] = signers[ObserverNodeBeneficiary]
|
|
rewardSum := new(big.Int)
|
|
type rewardWithType struct {
|
|
r float64
|
|
t Beneficiary
|
|
key string
|
|
}
|
|
for _, rwt := range []rewardWithType{
|
|
{currentConfig.MasternodeReward, MasterNodeBeneficiary, "rewards"},
|
|
{currentConfig.ProtectorReward, ProtectorNodeBeneficiary, "rewardsProtector"},
|
|
{currentConfig.ObserverReward, ObserverNodeBeneficiary, "rewardsObserver"},
|
|
} {
|
|
originalRewardFloat := new(big.Float).Mul(new(big.Float).SetFloat64(rwt.r), new(big.Float).SetUint64(params.Ether))
|
|
originalReward, _ := originalRewardFloat.Int(nil)
|
|
chainReward := util.RewardInflation(chain, originalReward, number, common.BlocksPerYear)
|
|
rewardSigners, err := CalculateRewardForSignerFixed(chainReward, signers[rwt.t])
|
|
if err != nil {
|
|
log.Error("[HookReward] Fail to calculate reward type 0 for masternode, 1 for protector, 2 for observer", "error", err, "type", rwt.t)
|
|
return nil, err
|
|
}
|
|
// Add reward for coin holders.
|
|
rewardResults := make(map[common.Address]interface{})
|
|
for signer, calcReward := range rewardSigners {
|
|
rewards, err := contracts.CalculateRewardForHolders(foundationWalletAddr, parentState, signer, calcReward, number)
|
|
if err != nil {
|
|
log.Error("[HookReward] Fail to calculate reward for holders.", "error", err)
|
|
return nil, err
|
|
}
|
|
if len(rewards) > 0 {
|
|
for holder, reward := range rewards {
|
|
stateBlock.AddBalance(holder, reward, tracing.BalanceIncreaseRewardMineBlock)
|
|
rewardSum.Add(rewardSum, reward)
|
|
}
|
|
}
|
|
rewardResults[signer] = rewards
|
|
}
|
|
rewardsMap[rwt.key] = rewardResults
|
|
}
|
|
// record the total reward into state db
|
|
totalMinted := new(big.Int)
|
|
totalBurned := new(big.Int)
|
|
|
|
nonce := stateBlock.GetNonce(common.MintedRecordAddressBinary)
|
|
if nonce == 0 {
|
|
// initialize MintedRecordAddress
|
|
stateBlock.PutMintedRecordOnsetEpoch(common.Uint64ToHash(epochNum))
|
|
stateBlock.PutMintedRecordOnsetBlock(common.Uint64ToHash(number))
|
|
} else {
|
|
epochNumIter := epochNum
|
|
for epochNumIter > 0 {
|
|
epochNumIter--
|
|
totalMinted = stateBlock.GetPostMinted(epochNumIter).Big()
|
|
totalBurned = stateBlock.GetPostBurned(epochNumIter).Big()
|
|
if totalMinted.Sign() != 0 || totalBurned.Sign() != 0 {
|
|
// if previous epoch has non-zero total minted or non-zero total burned, break the loop
|
|
break
|
|
}
|
|
}
|
|
}
|
|
totalMinted.Add(totalMinted, rewardSum)
|
|
// if overflow, set to maxU256 and log a warning
|
|
if totalMinted.Cmp(math.MaxBig256) > 0 {
|
|
totalMinted.Set(math.MaxBig256)
|
|
log.Warn("[HookReward] total minted overflow max u256")
|
|
}
|
|
log.Debug("[HookReward] total minted in hook", "value", totalMinted)
|
|
stateBlock.PutPostMinted(epochNum, common.BigToHash(totalMinted))
|
|
stateBlock.PutPostRewardBlock(epochNum, common.Uint64ToHash(number))
|
|
// Record total burned into statedb
|
|
totalBurned.Add(totalBurned, burnedInOneEpoch)
|
|
// if overflow, set to maxU256 and log a warning
|
|
if totalBurned.Cmp(math.MaxBig256) > 0 {
|
|
totalBurned.Set(math.MaxBig256)
|
|
log.Warn("[HookReward] total burned overflow max u256")
|
|
}
|
|
stateBlock.PutPostBurned(epochNum, common.BigToHash(totalBurned))
|
|
// Increment nonce so that statedb does not treat it as empty account
|
|
stateBlock.IncrementMintedRecordNonce()
|
|
}
|
|
log.Debug("Time Calculated HookReward ", "block", header.Number.Uint64(), "time", common.PrettyDuration(time.Since(start)))
|
|
return rewardsMap, nil
|
|
}
|
|
}
|
|
|
|
// get signing transaction sender count
|
|
func GetSigningTxCount(c *XDPoS.XDPoS, chain consensus.ChainReader, header *types.Header, parentState *state.StateDB, currentConfig *params.V2Config) (map[Beneficiary]map[common.Address]*RewardLog, *big.Int, error) {
|
|
// header should be a new epoch switch block
|
|
number := header.Number.Uint64()
|
|
rewardEpochCount := 2
|
|
signEpochCount := 1
|
|
signers := make(map[Beneficiary]map[common.Address]*RewardLog)
|
|
signers[MasterNodeBeneficiary] = make(map[common.Address]*RewardLog)
|
|
signers[ProtectorNodeBeneficiary] = make(map[common.Address]*RewardLog)
|
|
signers[ObserverNodeBeneficiary] = make(map[common.Address]*RewardLog)
|
|
|
|
mapBlkHash := map[uint64]common.Hash{}
|
|
|
|
burnedInOneEpoch := new(big.Int)
|
|
|
|
// prevent overflow
|
|
if number == 0 {
|
|
return signers, burnedInOneEpoch, nil
|
|
}
|
|
|
|
data := make(map[common.Hash][]common.Address)
|
|
epochCount := 0
|
|
var startBlockNumber, endBlockNumber uint64
|
|
|
|
nodesToKeep := make(map[Beneficiary][]common.Address)
|
|
|
|
h := header
|
|
for i := number - 1; ; i-- {
|
|
parentHash := h.ParentHash
|
|
h = chain.GetHeader(parentHash, i)
|
|
if h == nil {
|
|
log.Error("[GetSigningTxCount] fail to get header", "number", i, "hash", parentHash)
|
|
return nil, burnedInOneEpoch, fmt.Errorf("fail to get header in GetSigningTxCount at number: %v, hash: %v", i, parentHash)
|
|
}
|
|
if epochCount == 0 && h.BaseFee != nil {
|
|
// add burned for the first epoch during loop
|
|
burnedInOneEpoch.Add(burnedInOneEpoch, new(big.Int).Mul(h.BaseFee, new(big.Int).SetUint64(h.GasUsed)))
|
|
}
|
|
isEpochSwitch, _, err := c.IsEpochSwitch(h)
|
|
if err != nil {
|
|
return nil, burnedInOneEpoch, err
|
|
}
|
|
if isEpochSwitch && i != chain.Config().XDPoS.V2.SwitchBlock.Uint64()+1 {
|
|
epochCount += 1
|
|
if epochCount == signEpochCount {
|
|
endBlockNumber = h.Number.Uint64() - 1
|
|
}
|
|
if epochCount == rewardEpochCount {
|
|
startBlockNumber = h.Number.Uint64() + 1
|
|
nodesToKeep[MasterNodeBeneficiary] = c.GetMasternodesFromCheckpointHeader(h)
|
|
// in reward upgrade, add protector and observer nodes
|
|
if chain.Config().IsTIPUpgradeReward(header.Number) {
|
|
candidates := parentState.GetCandidates()
|
|
var ms []utils.Masternode
|
|
for _, candidate := range candidates {
|
|
// ignore "0x0000000000000000000000000000000000000000"
|
|
if !candidate.IsZero() {
|
|
v := parentState.GetCandidateCap(candidate)
|
|
ms = append(ms, utils.Masternode{Address: candidate, Stake: v})
|
|
}
|
|
}
|
|
slices.SortStableFunc(ms, func(a, b utils.Masternode) int {
|
|
return b.Stake.Cmp(a.Stake)
|
|
})
|
|
// find penalty and filter them out
|
|
penalties := common.ExtractAddressFromBytes(h.Penalties)
|
|
filterMap := make(map[common.Address]struct{})
|
|
for _, addr := range penalties {
|
|
filterMap[addr] = struct{}{}
|
|
}
|
|
for _, addr := range nodesToKeep[MasterNodeBeneficiary] {
|
|
filterMap[addr] = struct{}{}
|
|
}
|
|
// find top candidates
|
|
protector := []common.Address{}
|
|
observer := []common.Address{}
|
|
for _, node := range ms {
|
|
if _, ok := filterMap[node.Address]; ok {
|
|
continue
|
|
}
|
|
if len(protector) < currentConfig.MaxProtectorNodes {
|
|
protector = append(protector, node.Address)
|
|
} else if len(observer) < currentConfig.MaxObverserNodes {
|
|
observer = append(observer, node.Address)
|
|
}
|
|
}
|
|
nodesToKeep[ProtectorNodeBeneficiary] = protector
|
|
nodesToKeep[ObserverNodeBeneficiary] = observer
|
|
}
|
|
break
|
|
}
|
|
}
|
|
mapBlkHash[i] = h.Hash()
|
|
signingTxs, ok := c.GetCachedSigningTxs(h.Hash())
|
|
if !ok {
|
|
log.Debug("Failed get from cached", "hash", h.Hash().String(), "number", i)
|
|
block := chain.GetBlock(h.Hash(), i)
|
|
if block != nil {
|
|
txs := block.Transactions()
|
|
signingTxs = c.CacheSigningTxs(h.Hash(), txs)
|
|
}
|
|
}
|
|
for _, tx := range signingTxs {
|
|
blkHash := common.BytesToHash(tx.Data()[len(tx.Data())-32:])
|
|
from := *tx.From()
|
|
data[blkHash] = append(data[blkHash], from)
|
|
}
|
|
// prevent overflow
|
|
if i == 0 {
|
|
return signers, burnedInOneEpoch, nil
|
|
}
|
|
}
|
|
|
|
for i := startBlockNumber; i <= endBlockNumber; i++ {
|
|
if i%common.MergeSignRange == 0 {
|
|
addrs := data[mapBlkHash[i]]
|
|
// Filter duplicate address.
|
|
if len(addrs) > 0 {
|
|
addrSigners := make(map[Beneficiary]map[common.Address]bool)
|
|
addrSigners[MasterNodeBeneficiary] = make(map[common.Address]bool)
|
|
addrSigners[ProtectorNodeBeneficiary] = make(map[common.Address]bool)
|
|
addrSigners[ObserverNodeBeneficiary] = make(map[common.Address]bool)
|
|
|
|
for _, addr := range addrs {
|
|
for _, beneficiary := range []Beneficiary{MasterNodeBeneficiary, ProtectorNodeBeneficiary, ObserverNodeBeneficiary} {
|
|
if _, ok := nodesToKeep[beneficiary]; ok {
|
|
for _, protector := range nodesToKeep[beneficiary] {
|
|
if addr == protector {
|
|
if _, ok := addrSigners[beneficiary][addr]; !ok {
|
|
addrSigners[beneficiary][addr] = true
|
|
}
|
|
break
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for _, beneficiary := range []Beneficiary{MasterNodeBeneficiary, ProtectorNodeBeneficiary, ObserverNodeBeneficiary} {
|
|
for addr := range addrSigners[beneficiary] {
|
|
_, exist := signers[beneficiary][addr]
|
|
if exist {
|
|
signers[beneficiary][addr].Sign++
|
|
} else {
|
|
signers[beneficiary][addr] = &RewardLog{Sign: 1, Reward: new(big.Int)}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
log.Info("Calculate reward at checkpoint", "startBlock", startBlockNumber, "endBlock", endBlockNumber)
|
|
|
|
return signers, burnedInOneEpoch, nil
|
|
}
|
|
|
|
// Calculate reward for signers.
|
|
func CalculateRewardForSigner(chainReward *big.Int, signers map[common.Address]*RewardLog) (map[common.Address]*big.Int, error) {
|
|
totalSignerCount := uint64(0)
|
|
for _, rLog := range signers {
|
|
totalSignerCount += rLog.Sign
|
|
}
|
|
resultSigners := make(map[common.Address]*big.Int)
|
|
// Add reward for signers.
|
|
if totalSignerCount > 0 {
|
|
for signer, rLog := range signers {
|
|
// Add reward for signer.
|
|
calcReward := new(big.Int)
|
|
calcReward.Div(chainReward, new(big.Int).SetUint64(totalSignerCount))
|
|
calcReward.Mul(calcReward, new(big.Int).SetUint64(rLog.Sign))
|
|
rLog.Reward = calcReward
|
|
|
|
resultSigners[signer] = calcReward
|
|
}
|
|
}
|
|
|
|
log.Info("Signers data", "totalSigner", totalSignerCount, "totalReward", chainReward)
|
|
for addr, signer := range signers {
|
|
log.Debug("Signer reward", "signer", addr, "sign", signer.Sign, "reward", signer.Reward)
|
|
}
|
|
|
|
return resultSigners, nil
|
|
}
|
|
|
|
// Calculate reward for signers with fixed reward.
|
|
func CalculateRewardForSignerFixed(chainReward *big.Int, signers map[common.Address]*RewardLog) (map[common.Address]*big.Int, error) {
|
|
resultSigners := make(map[common.Address]*big.Int)
|
|
// Add reward for signers.
|
|
for signer, rLog := range signers {
|
|
// Add reward for signer.
|
|
calcReward := new(big.Int).SetBytes(chainReward.Bytes())
|
|
rLog.Reward = calcReward
|
|
resultSigners[signer] = calcReward
|
|
}
|
|
|
|
log.Info("Signers data", "percapitaReward", chainReward)
|
|
for addr, signer := range signers {
|
|
log.Debug("Signer reward", "signer", addr, "sign", signer.Sign, "reward", signer.Reward)
|
|
}
|
|
|
|
return resultSigners, nil
|
|
}
|