Skip to content

Commit

Permalink
feat: new reward hook and some config
Browse files Browse the repository at this point in the history
  • Loading branch information
wgr523 committed Feb 15, 2025
1 parent 0df4a2a commit 215df19
Show file tree
Hide file tree
Showing 3 changed files with 219 additions and 48 deletions.
2 changes: 2 additions & 0 deletions common/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ var LondonBlock = big.NewInt(76321000) // Target 19th June 2024
var MergeBlock = big.NewInt(76321000) // Target 19th June 2024
var ShanghaiBlock = big.NewInt(76321000) // Target 19th June 2024

var TIPUpgradeReward = big.NewInt(9999999999)

var TIPXDCXTestnet = big.NewInt(38383838)
var IsTestnet bool = false
var Enable0xPrefix bool = true
Expand Down
256 changes: 208 additions & 48 deletions eth/hooks/engine_v2_hooks.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@ import (
"time"

"github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/common/sort"
"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"
Expand All @@ -17,16 +19,21 @@ import (
"github.com/XinFinOrg/XDPoSChain/params"
)

// Declaring an enum type beneficiary of reward
type beneficiary int
// Declaring an enum type Beneficiary of reward
type Beneficiary int

// Enumerating reward beneficiary
const (
masterNodeBeneficiary beneficiary = iota
protectorNodeBeneficiary
observerNodeBeneficiary
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, currentHash common.Hash, candidates []common.Address) ([]common.Address, error) {
Expand Down Expand Up @@ -175,62 +182,166 @@ func AttachConsensusV2Hooks(adaptor *XDPoS.XDPoS, bc *core.BlockChain, chainConf
log.Error("Foundation Wallet Address is empty", "error", foundationWalletAddr)
return nil, errors.New("foundation wallet address is empty")
}
rewards := make(map[string]interface{})
rewardsMap := make(map[string]interface{})
// skip hook reward if this is the first v2
if number == chain.Config().XDPoS.V2.SwitchBlock.Uint64()+1 {
return rewards, nil
return rewardsMap, nil
}
start := time.Now()
// Get reward inflation.
chainReward := new(big.Int).Mul(new(big.Int).SetUint64(chain.Config().XDPoS.Reward), new(big.Int).SetUint64(params.Ether))
chainReward = util.RewardInflation(chain, chainReward, number, common.BlocksPerYear)

round, err := adaptor.EngineV2.GetRoundNumber(header)
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
totalSigner := new(uint64)
nodesToKeep := map[beneficiary][]common.Address{}
signers, err := getSigningTxCount(adaptor, chain, header, totalSigner, nodesToKeep)
nodesToKeep := make(map[Beneficiary][]common.Address)
if chain.Config().IsTIPUpgradeReward(header.Number) {
candidates := state.GetCandidates(parentState)
var ms []utils.Masternode
for _, candidate := range candidates {
// ignore "0x0000000000000000000000000000000000000000"
if !candidate.IsZero() {
v := state.GetCandidateCap(parentState, candidate)
ms = append(ms, utils.Masternode{Address: candidate, Stake: v})
}
}
sort.Slice(ms, func(i, j int) bool {
return ms[i].Stake.Cmp(ms[j].Stake) >= 0
})
// find top candidates
// find master nodes plus protector nodes. classify them will be in `fn GetSigningTxCount`
maxMNP := currentConfig.MaxMasternodes + currentConfig.MaxProtectorNodes
protector := []common.Address{}
observer := []common.Address{}
for _, ms := range ms[:maxMNP] {
protector = append(protector, ms.Address)
}
for _, ms := range ms[maxMNP:] {
observer = append(observer, ms.Address)
}
nodesToKeep[ProtectorNodeBeneficiary] = protector
nodesToKeep[ObserverNodeBeneficiary] = observer
}
signers, err := GetSigningTxCount(adaptor, chain, header, nodesToKeep)

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
}
rewards["signers"] = signers
rewardSigners, err := contracts.CalculateRewardForSigner(chainReward, signers, *totalSigner)
if err != nil {
log.Error("[HookReward] Fail to calculate reward for signers", "error", err)
return nil, err
rewardsMap["signers"] = signers[MasterNodeBeneficiary]

rewardSigners := make(map[common.Address]*big.Int)
rewardSignersProtector := make(map[common.Address]*big.Int)
rewardSignersObserver := make(map[common.Address]*big.Int)
if !chain.Config().IsTIPUpgradeReward(header.Number) {
// Get reward inflation.
chainReward := new(big.Int).Mul(new(big.Int).SetUint64(chain.Config().XDPoS.Reward), new(big.Int).SetUint64(params.Ether))
chainReward = util.RewardInflation(chain, chainReward, 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
}
} else {
rewardsMap["signersProtector"] = signers[ProtectorNodeBeneficiary]
rewardsMap["signersObserver"] = signers[ObserverNodeBeneficiary]
// Masternode rewards
chainReward := new(big.Int).Mul(new(big.Int).SetUint64(currentConfig.MasternodeReward), new(big.Int).SetUint64(params.Ether))
chainReward = util.RewardInflation(chain, chainReward, 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
}

// Protector rewards
chainReward = new(big.Int).Mul(new(big.Int).SetUint64(currentConfig.ProtectorReward), new(big.Int).SetUint64(params.Ether))
chainReward = util.RewardInflation(chain, chainReward, number, common.BlocksPerYear)
rewardSignersProtector, err = CalculateRewardForSigner(chainReward, signers[ProtectorNodeBeneficiary])
if err != nil {
log.Error("[HookReward] Fail to calculate reward for protector", "error", err)
return nil, err
}

// Observer rewards
chainReward = new(big.Int).Mul(new(big.Int).SetUint64(currentConfig.ObserverReward), new(big.Int).SetUint64(params.Ether))
chainReward = util.RewardInflation(chain, chainReward, number, common.BlocksPerYear)
rewardSignersObserver, err = CalculateRewardForSigner(chainReward, signers[ProtectorNodeBeneficiary])
if err != nil {
log.Error("[HookReward] Fail to calculate reward for observer", "error", err)
return nil, err
}
}
// Add reward for coin holders.
voterResults := make(map[common.Address]interface{})
if len(signers) > 0 {
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
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)
}
if len(rewards) > 0 {
for holder, reward := range rewards {
stateBlock.AddBalance(holder, reward)
}
}
voterResults[signer] = rewards
}
rewardsMap["rewards"] = voterResults

voterResultsProtector := make(map[common.Address]interface{})
for signer, calcReward := range rewardSignersProtector {
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)
}
}
voterResultsProtector[signer] = rewards
}
if len(voterResultsProtector) > 0 {
rewardsMap["rewardsProtector"] = voterResultsProtector
}
voterResultsObserver := make(map[common.Address]interface{})
for signer, calcReward := range rewardSignersObserver {
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)
}
voterResults[signer] = rewards
}
voterResultsObserver[signer] = rewards
}
if len(voterResultsObserver) > 0 {
rewardsMap["rewardsObserver"] = voterResultsObserver
}
rewards["rewards"] = voterResults
log.Debug("Time Calculated HookReward ", "block", header.Number.Uint64(), "time", common.PrettyDuration(time.Since(start)))
return rewards, nil
return rewardsMap, nil
}
}

// get signing transaction sender count
func getSigningTxCount(c *XDPoS.XDPoS, chain consensus.ChainReader, header *types.Header, totalSigner *uint64, nodesToKeep map[beneficiary][]common.Address) (map[common.Address]*contracts.RewardLog, error) {
func GetSigningTxCount(c *XDPoS.XDPoS, chain consensus.ChainReader, header *types.Header, nodesToKeep map[Beneficiary][]common.Address) (map[Beneficiary]map[common.Address]*RewardLog, error) {
// header should be a new epoch switch block
number := header.Number.Uint64()
rewardEpochCount := 2
signEpochCount := 1
signers := make(map[common.Address]*contracts.RewardLog)
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{}

// prevent overflow
Expand All @@ -254,8 +365,20 @@ func getSigningTxCount(c *XDPoS.XDPoS, chain consensus.ChainReader, header *type
}
if epochCount == rewardEpochCount {
startBlockNumber = header.Number.Uint64() + 1
if _, ok := nodesToKeep[masterNodeBeneficiary]; !ok {
nodesToKeep[masterNodeBeneficiary] = c.GetMasternodesFromCheckpointHeader(header)
if _, ok := nodesToKeep[MasterNodeBeneficiary]; !ok {
nodesToKeep[MasterNodeBeneficiary] = c.GetMasternodesFromCheckpointHeader(header)
// remove masternodes from nodesToKeep[Protector]
for i := 0; i < len(nodesToKeep[ProtectorNodeBeneficiary]); i++ {
for _, masternode := range nodesToKeep[MasterNodeBeneficiary] {
if nodesToKeep[ProtectorNodeBeneficiary][i] == masternode {
// remove from nodesToKeep[Protector]
nodesToKeep[ProtectorNodeBeneficiary] = append(nodesToKeep[ProtectorNodeBeneficiary][:i], nodesToKeep[ProtectorNodeBeneficiary][i+1:]...)
i--
break
}
}

}
}
break
}
Expand Down Expand Up @@ -284,26 +407,35 @@ func getSigningTxCount(c *XDPoS.XDPoS, chain consensus.ChainReader, header *type
addrs := data[mapBlkHash[i]]
// Filter duplicate address.
if len(addrs) > 0 {
addrSigners := make(map[common.Address]bool)
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 _, masternode := range nodesToKeep[masterNodeBeneficiary] {
if addr == masternode {
if _, ok := addrSigners[addr]; !ok {
addrSigners[addr] = true
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
}
}
break
}
}
}

for addr := range addrSigners {
_, exist := signers[addr]
if exist {
signers[addr].Sign++
} else {
signers[addr] = &contracts.RewardLog{Sign: 1, Reward: new(big.Int)}
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)}
}
}
*totalSigner++
}
}
}
Expand All @@ -313,3 +445,31 @@ func getSigningTxCount(c *XDPoS.XDPoS, chain consensus.ChainReader, header *type

return signers, 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
}
9 changes: 9 additions & 0 deletions params/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -454,12 +454,17 @@ type V2 struct {

type V2Config struct {
MaxMasternodes int `json:"maxMasternodes"` // v2 max masternodes
MaxProtectorNodes int `json:"maxProtectorNodes"` // v2 max ProtectorNodes
SwitchRound uint64 `json:"switchRound"` // v1 to v2 switch block number
MinePeriod int `json:"minePeriod"` // Miner mine period to mine a block
TimeoutSyncThreshold int `json:"timeoutSyncThreshold"` // send syncInfo after number of timeout
TimeoutPeriod int `json:"timeoutPeriod"` // Duration in ms
CertThreshold float64 `json:"certificateThreshold"` // Necessary number of messages from master nodes to form a certificate

MasternodeReward uint64 `json:"masternodeReward"` // Block reward for master nodes (core validators) - unit Ether
ProtectorReward uint64 `json:"protectorReward"` // Block reward for protectors - unit Ether
ObserverReward uint64 `json:"observerReward"` // Block reward for observer - unit Ether

ExpTimeoutConfig ExpTimeoutConfig `json:"expTimeoutConfig"`
}

Expand Down Expand Up @@ -698,6 +703,10 @@ func (c *ChainConfig) IsTIPXDCXCancellationFee(num *big.Int) bool {
return isForked(common.TIPXDCXCancellationFee, num)
}

func (c *ChainConfig) IsTIPUpgradeReward(num *big.Int) bool {
return isForked(common.TIPUpgradeReward, num)
}

// GasTable returns the gas table corresponding to the current phase (homestead or homestead reprice).
//
// The returned GasTable's fields shouldn't, under any circumstances, be changed.
Expand Down

0 comments on commit 215df19

Please sign in to comment.