From 7c4668fa91a46c464848950a6b3ebd84ab8d0ffb Mon Sep 17 00:00:00 2001 From: Wang Gerui Date: Sat, 8 Mar 2025 12:01:00 +0800 Subject: [PATCH 1/3] style: clean comment --- eth/hooks/engine_v2_hooks.go | 1 - 1 file changed, 1 deletion(-) diff --git a/eth/hooks/engine_v2_hooks.go b/eth/hooks/engine_v2_hooks.go index 17f71fd176..0cf11149cb 100644 --- a/eth/hooks/engine_v2_hooks.go +++ b/eth/hooks/engine_v2_hooks.go @@ -366,7 +366,6 @@ func GetSigningTxCount(c *XDPoS.XDPoS, chain consensus.ChainReader, header *type filterMap[addr] = struct{}{} } // find top candidates - // maxMNP := currentConfig.MaxMasternodes + currentConfig.MaxProtectorNodes protector := []common.Address{} observer := []common.Address{} for _, node := range ms { From 84ae914350e6cb8f355ad79cbef48b9802e79c64 Mon Sep 17 00:00:00 2001 From: Gerui Wang Date: Tue, 11 Mar 2025 00:31:07 +0800 Subject: [PATCH 2/3] feat: add TIPEpochHalving, better style --- common/constants.all.go | 3 + common/constants.devnet.go | 1 + common/constants.mainnet.go | 1 + common/constants.testnet.go | 1 + .../tests/engine_v2_tests/reward_test.go | 55 ++++++ eth/hooks/engine_v2_hooks.go | 165 +++++++++--------- eth/util/util.go | 31 ++++ params/config.go | 4 + 8 files changed, 183 insertions(+), 78 deletions(-) diff --git a/common/constants.all.go b/common/constants.all.go index 92ae30f8c6..28732c7d4c 100644 --- a/common/constants.all.go +++ b/common/constants.all.go @@ -62,6 +62,7 @@ type constant struct { tipXDCXMinerDisable *big.Int tipXDCXReceiverDisable *big.Int tipUpgradeReward *big.Int + tipEpochHalving *big.Int eip1559Block *big.Int cancunBlock *big.Int @@ -104,6 +105,7 @@ var ( TIPXDCXMinerDisable = MaintnetConstant.tipXDCXMinerDisable TIPXDCXReceiverDisable = MaintnetConstant.tipXDCXReceiverDisable TIPUpgradeReward = MaintnetConstant.tipUpgradeReward + TIPEpochHalving = MaintnetConstant.tipEpochHalving Eip1559Block = MaintnetConstant.eip1559Block CancunBlock = MaintnetConstant.cancunBlock @@ -170,6 +172,7 @@ func CopyConstans(chainID uint64) { TIPXDCXMinerDisable = c.tipXDCXMinerDisable TIPXDCXReceiverDisable = c.tipXDCXReceiverDisable TIPUpgradeReward = c.tipUpgradeReward + TIPEpochHalving = c.tipEpochHalving Eip1559Block = c.eip1559Block CancunBlock = c.cancunBlock diff --git a/common/constants.devnet.go b/common/constants.devnet.go index 2e6bbb9782..0d2f6bdc80 100644 --- a/common/constants.devnet.go +++ b/common/constants.devnet.go @@ -31,6 +31,7 @@ var DevnetConstant = constant{ eip1559Block: big.NewInt(0), cancunBlock: big.NewInt(1702800), tipUpgradeReward: big.NewInt(1773000), + tipEpochHalving: big.NewInt(9999999999), trc21IssuerSMCTestNet: HexToAddress("0x0E2C88753131CE01c7551B726b28BFD04e44003F"), trc21IssuerSMC: HexToAddress("0x8c0faeb5C6bEd2129b8674F262Fd45c4e9468bee"), diff --git a/common/constants.mainnet.go b/common/constants.mainnet.go index 14f5870a7e..7ec193a3cc 100644 --- a/common/constants.mainnet.go +++ b/common/constants.mainnet.go @@ -31,6 +31,7 @@ var MaintnetConstant = constant{ eip1559Block: big.NewInt(9999999999), cancunBlock: big.NewInt(9999999999), tipUpgradeReward: big.NewInt(9999999999), + tipEpochHalving: big.NewInt(9999999999), trc21IssuerSMCTestNet: HexToAddress("0x0E2C88753131CE01c7551B726b28BFD04e44003F"), trc21IssuerSMC: HexToAddress("0x8c0faeb5C6bEd2129b8674F262Fd45c4e9468bee"), diff --git a/common/constants.testnet.go b/common/constants.testnet.go index b72493ac1a..eff027da36 100644 --- a/common/constants.testnet.go +++ b/common/constants.testnet.go @@ -31,6 +31,7 @@ var TestnetConstant = constant{ eip1559Block: big.NewInt(71550000), // Target 14th Feb 2025 cancunBlock: big.NewInt(9999999999), tipUpgradeReward: big.NewInt(9999999999), + tipEpochHalving: big.NewInt(9999999999), trc21IssuerSMCTestNet: HexToAddress("0x0E2C88753131CE01c7551B726b28BFD04e44003F"), trc21IssuerSMC: HexToAddress("0x8c0faeb5C6bEd2129b8674F262Fd45c4e9468bee"), diff --git a/consensus/tests/engine_v2_tests/reward_test.go b/consensus/tests/engine_v2_tests/reward_test.go index 6caed67cc0..c3e34742f8 100644 --- a/consensus/tests/engine_v2_tests/reward_test.go +++ b/consensus/tests/engine_v2_tests/reward_test.go @@ -10,6 +10,7 @@ import ( "github.com/XinFinOrg/XDPoSChain/core/state" "github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/eth/hooks" + "github.com/XinFinOrg/XDPoSChain/eth/util" "github.com/XinFinOrg/XDPoSChain/params" "github.com/stretchr/testify/assert" ) @@ -297,3 +298,57 @@ func TestHookRewardAfterUpgrade(t *testing.T) { } common.TIPUpgradeReward = backup } + +func TestRewardHalvingVanishing(t *testing.T) { + billion := big.NewInt(1000000000) + epochRewardTotal := big.NewInt(16000) + epochRewardTotal.Mul(epochRewardTotal, billion) + epochReward1 := big.NewInt(10000) + epochReward1.Mul(epochReward1, billion) + epochReward2 := big.NewInt(4000) + epochReward2.Mul(epochReward2, billion) + epochReward3 := big.NewInt(2000) + epochReward3.Mul(epochReward3, billion) + // 45 Billion - 39 Billion XDC (1 XDC = 10^9 wei) + halvingSupply := big.NewInt(6000000000) + halvingSupply.Mul(halvingSupply, billion) + sum := big.NewInt(0) + iterMax := uint64(30000000) + for i := uint64(0); i < iterMax; i++ { + r := new(big.Int).Add(util.RewardHalving(epochReward1, epochRewardTotal, halvingSupply, i), util.RewardHalving(epochReward2, epochRewardTotal, halvingSupply, i)) + r.Add(r, util.RewardHalving(epochReward3, epochRewardTotal, halvingSupply, i)) + if r.BitLen() == 0 { + t.Log("reward be 0 at i=", i) // reward be 0 at i= 11225088, wich is more than 200 years in the future + break + } + sum.Add(sum, r) + if i == iterMax-1 { + t.Fatal("reward should be 0 at end") + } + } + t.Log("sum", sum) // sum 5999999999982635022, which is less than total, and never reach totoal + assert.True(t, sum.Cmp(halvingSupply) < 0) +} + +func TestRewardHalvingSplit(t *testing.T) { + billion := big.NewInt(1000000000) + epochRewardTotal := big.NewInt(16000) + epochRewardTotal.Mul(epochRewardTotal, billion) + epochReward1 := big.NewInt(10000) + epochReward1.Mul(epochReward1, billion) + epochReward2 := big.NewInt(4000) + epochReward2.Mul(epochReward2, billion) + epochReward3 := big.NewInt(2000) + epochReward3.Mul(epochReward3, billion) + // 45 Billion - 39 Billion XDC (1 XDC = 10^9 wei) + halvingSupply := big.NewInt(6000000000) + halvingSupply.Mul(halvingSupply, billion) + i := uint64(50000) // a random number suffice + r1 := util.RewardHalving(epochReward1, epochRewardTotal, halvingSupply, i) + r2 := util.RewardHalving(epochReward2, epochRewardTotal, halvingSupply, i) + r3 := util.RewardHalving(epochReward3, epochRewardTotal, halvingSupply, i) + t.Log(r1, r2, r3) + + assert.Equal(t, int64(5), r1.Div(r1, r3).Int64()) // since epochReward1/epochReward3=5 + assert.Equal(t, int64(2), r2.Div(r2, r3).Int64()) // since epochReward2/epochReward3=2 +} diff --git a/eth/hooks/engine_v2_hooks.go b/eth/hooks/engine_v2_hooks.go index 0cf11149cb..d7189ca24c 100644 --- a/eth/hooks/engine_v2_hooks.go +++ b/eth/hooks/engine_v2_hooks.go @@ -205,98 +205,80 @@ func AttachConsensusV2Hooks(adaptor *XDPoS.XDPoS, bc *core.BlockChain, chainConf } 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]) + 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. + voterResults := 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) + } + } + voterResults[signer] = rewards + } + rewardsMap["rewards"] = voterResults } 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 + epochRewardTotal := new(big.Int).SetUint64(currentConfig.MasternodeReward + currentConfig.ProtectorReward + currentConfig.ObserverReward) + type rewardWithType struct { + r uint64 + t Beneficiary + key string } - - // 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[ObserverNodeBeneficiary]) - 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{}) - 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) + for _, rwt := range []rewardWithType{ + {currentConfig.MasternodeReward, MasterNodeBeneficiary, "rewards"}, + {currentConfig.ProtectorReward, ProtectorNodeBeneficiary, "rewardsProtector"}, + {currentConfig.ObserverReward, ObserverNodeBeneficiary, "rewardsObserver"}, + } { + originalReward := new(big.Int).Mul(new(big.Int).SetUint64(rwt.r), new(big.Int).SetUint64(params.Ether)) + chainReward := new(big.Int) + if !chain.Config().IsTIPEpochHalving(header.Number) { + chainReward = util.RewardInflation(chain, originalReward, number, common.BlocksPerYear) + } else { + halvingSupply := big.NewInt(9000000000) // TODO use config.halvingSupply + _, epochNum, err := adaptor.EngineV2.IsEpochSwitch(header) + if err != nil { + return nil, err + } + epochSinceHalving := epochNum // TODO Minus config.epochHalvingOnset + chainReward = util.RewardHalving(originalReward, epochRewardTotal, halvingSupply, epochSinceHalving) } - } - 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) + rewardSigners, err := CalculateRewardForSigner(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 } - } - 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) + // Add reward for coin holders. + voterResults := 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) + } + } + voterResults[signer] = rewards } + rewardsMap[rwt.key] = voterResults } - voterResultsObserver[signer] = rewards - } - if len(voterResultsObserver) > 0 { - rewardsMap["rewardsObserver"] = voterResultsObserver } log.Debug("Time Calculated HookReward ", "block", header.Number.Uint64(), "time", common.PrettyDuration(time.Since(start))) return rewardsMap, nil @@ -474,3 +456,30 @@ func CalculateRewardForSigner(chainReward *big.Int, signers map[common.Address]* return resultSigners, nil } + +// func TestRewardBeZero(t *testing.T) { +// billion := big.NewInt(1000000000) +// epochRewardTotal := big.NewInt(16000) +// epochRewardTotal.Mul(epochRewardTotal, billion) +// epochReward1 := big.NewInt(10000) +// epochReward1.Mul(epochReward1, billion) +// epochReward2 := big.NewInt(4000) +// epochReward2.Mul(epochReward2, billion) +// epochReward3 := big.NewInt(2000) +// epochReward3.Mul(epochReward3, billion) +// // 45 Billion - 39 Billion XDC (1 XDC = 10^9 wei) +// halvingSupply := big.NewInt(6000000000) +// halvingSupply.Mul(halvingSupply, billion) +// sum := big.NewInt(0) +// for i := uint64(0); i < 30000000; i++ { +// r := new(big.Int).Add(RewardHalving(epochReward1, epochRewardTotal, halvingSupply, i), RewardHalving(epochReward2, epochRewardTotal, halvingSupply, i)) +// r.Add(r, RewardHalving(epochReward3, epochRewardTotal, halvingSupply, i)) +// if r.BitLen() == 0 { +// t.Log("reward be 0 at i=", i) // reward be 0 at i= 11225088, wich is more than 200 years in the future +// break +// } +// sum.Add(sum, r) +// } +// t.Log("sum", sum) // sum 5999999999982635022, which is less than total, and never reach totoal +// assert.True(t, sum.Cmp(halvingSupply) < 0) +// } diff --git a/eth/util/util.go b/eth/util/util.go index 317f8d2079..89138993ee 100644 --- a/eth/util/util.go +++ b/eth/util/util.go @@ -20,3 +20,34 @@ func RewardInflation(chain consensus.ChainReader, chainReward *big.Int, number u return chainReward } + +// RewardHalving computes the reward for Masternode/Protector/Observer based on epoch total reward, supply after halving is enabled, and epoch after halving is enabled +// The sequence is a geometric sequence in order to make supply be limited +func RewardHalving(epochRewardSingle *big.Int, epochRewardTotal *big.Int, halvingSupply *big.Int, epochSinceHalving uint64) *big.Int { + rt := new(big.Float).SetInt(epochRewardTotal) + hs := new(big.Float).SetInt(halvingSupply) + // zero cause Quo panic so return early + // or epoch reward > halving supply, return early + if halvingSupply.BitLen() == 0 || epochRewardTotal.Cmp(halvingSupply) > 0 { + return big.NewInt(0) + } + quo := new(big.Float).Quo(rt, hs) + // base = 1- reward/supply + base := new(big.Float).Sub(big.NewFloat(1), quo) + r := new(big.Float).SetInt(epochRewardSingle) + result := new(big.Float).Mul(r, FloatPower(base, epochSinceHalving)) + resultInt, _ := result.Int(nil) + return resultInt +} + +func FloatPower(base *big.Float, exp uint64) *big.Float { + result := big.NewFloat(1) + for exp > 0 { + if exp%2 == 1 { + result.Mul(result, base) + } + base.Mul(base, base) + exp >>= 1 // same as: exp = exp / 2 + } + return result +} diff --git a/params/config.go b/params/config.go index 88d7fb31e2..11d5412552 100644 --- a/params/config.go +++ b/params/config.go @@ -764,6 +764,10 @@ func (c *ChainConfig) IsTIPUpgradeReward(num *big.Int) bool { return isForked(common.TIPUpgradeReward, num) } +func (c *ChainConfig) IsTIPEpochHalving(num *big.Int) bool { + return isForked(common.TIPEpochHalving, 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. From 6bac06b1e7f0db9891ec1f48322b209dd19914d8 Mon Sep 17 00:00:00 2001 From: Gerui Wang Date: Mon, 24 Mar 2025 12:03:24 +0800 Subject: [PATCH 3/3] feat: fixed reward per node (per capita) --- .../tests/engine_v2_tests/reward_test.go | 20 ++--- eth/hooks/engine_v2_hooks.go | 74 +++++++------------ params/config.go | 8 +- 3 files changed, 42 insertions(+), 60 deletions(-) diff --git a/consensus/tests/engine_v2_tests/reward_test.go b/consensus/tests/engine_v2_tests/reward_test.go index c3e34742f8..60eba07ada 100644 --- a/consensus/tests/engine_v2_tests/reward_test.go +++ b/consensus/tests/engine_v2_tests/reward_test.go @@ -224,21 +224,21 @@ func TestHookRewardAfterUpgrade(t *testing.T) { assert.Nil(t, err) result := reward["rewards"].(map[common.Address]interface{}) assert.Equal(t, 2, len(result)) - // two signing account, 3 txs, reward is split by 1:2 (total reward is 250...000) + // two signing account, both get fixed reward for addr, x := range result { if addr == acc1Addr { r := x.(map[common.Address]*big.Int) owner := state.GetCandidateOwner(parentState, addr) - a, _ := big.NewInt(0).SetString("299999999999999999998", 10) + a, _ := big.NewInt(0).SetString("450000000000000000000", 10) assert.Zero(t, a.Cmp(r[owner]), "real reward is", r[owner]) - b, _ := big.NewInt(0).SetString("33333333333333333333", 10) + b, _ := big.NewInt(0).SetString("50000000000000000000", 10) assert.Zero(t, b.Cmp(r[config.XDPoS.FoudationWalletAddr]), "real reward is", r[config.XDPoS.FoudationWalletAddr]) } else if addr == signer { r := x.(map[common.Address]*big.Int) owner := state.GetCandidateOwner(parentState, addr) - a, _ := big.NewInt(0).SetString("149999999999999999999", 10) + a, _ := big.NewInt(0).SetString("450000000000000000000", 10) assert.Zero(t, a.Cmp(r[owner]), "real reward is", r[owner]) - b, _ := big.NewInt(0).SetString("16666666666666666666", 10) + b, _ := big.NewInt(0).SetString("50000000000000000000", 10) assert.Zero(t, b.Cmp(r[config.XDPoS.FoudationWalletAddr]), "real reward is", r[config.XDPoS.FoudationWalletAddr]) } else { assert.Fail(t, "wrong reward") @@ -262,22 +262,22 @@ func TestHookRewardAfterUpgrade(t *testing.T) { // only protector1 and 2 has signingtx. resultProtector := reward["rewardsProtector"].(map[common.Address]interface{}) - // 2 protector and split by 1:2 + // 2 protector both get fixed reward assert.Equal(t, 2, len(resultProtector)) for addr, x := range resultProtector { if addr == protector1Addr { r := x.(map[common.Address]*big.Int) owner := state.GetCandidateOwner(parentState, addr) - a, _ := big.NewInt(0).SetString("119999999999999999999", 10) + a, _ := big.NewInt(0).SetString("360000000000000000000", 10) assert.Zero(t, a.Cmp(r[owner]), "real reward is", r[owner]) - b, _ := big.NewInt(0).SetString("13333333333333333333", 10) + b, _ := big.NewInt(0).SetString("40000000000000000000", 10) assert.Zero(t, b.Cmp(r[config.XDPoS.FoudationWalletAddr]), "real reward is", r[config.XDPoS.FoudationWalletAddr]) } else if addr == protector2Addr { r := x.(map[common.Address]*big.Int) owner := state.GetCandidateOwner(parentState, addr) - a, _ := big.NewInt(0).SetString("239999999999999999999", 10) + a, _ := big.NewInt(0).SetString("360000000000000000000", 10) assert.Zero(t, a.Cmp(r[owner]), "real reward is", r[owner]) - b, _ := big.NewInt(0).SetString("26666666666666666666", 10) + b, _ := big.NewInt(0).SetString("40000000000000000000", 10) assert.Zero(t, b.Cmp(r[config.XDPoS.FoudationWalletAddr]), "real reward is", r[config.XDPoS.FoudationWalletAddr]) } else { assert.Fail(t, "wrong reward") diff --git a/eth/hooks/engine_v2_hooks.go b/eth/hooks/engine_v2_hooks.go index d7189ca24c..3a8f578b74 100644 --- a/eth/hooks/engine_v2_hooks.go +++ b/eth/hooks/engine_v2_hooks.go @@ -215,7 +215,7 @@ func AttachConsensusV2Hooks(adaptor *XDPoS.XDPoS, bc *core.BlockChain, chainConf return nil, err } // Add reward for coin holders. - voterResults := make(map[common.Address]interface{}) + rewardResults := make(map[common.Address]interface{}) for signer, calcReward := range rewardSigners { rewards, err := contracts.CalculateRewardForHolders(foundationWalletAddr, parentState, signer, calcReward, number) if err != nil { @@ -227,13 +227,12 @@ func AttachConsensusV2Hooks(adaptor *XDPoS.XDPoS, bc *core.BlockChain, chainConf stateBlock.AddBalance(holder, reward) } } - voterResults[signer] = rewards + rewardResults[signer] = rewards } - rewardsMap["rewards"] = voterResults + rewardsMap["rewards"] = rewardResults } else { rewardsMap["signersProtector"] = signers[ProtectorNodeBeneficiary] rewardsMap["signersObserver"] = signers[ObserverNodeBeneficiary] - epochRewardTotal := new(big.Int).SetUint64(currentConfig.MasternodeReward + currentConfig.ProtectorReward + currentConfig.ObserverReward) type rewardWithType struct { r uint64 t Beneficiary @@ -245,25 +244,14 @@ func AttachConsensusV2Hooks(adaptor *XDPoS.XDPoS, bc *core.BlockChain, chainConf {currentConfig.ObserverReward, ObserverNodeBeneficiary, "rewardsObserver"}, } { originalReward := new(big.Int).Mul(new(big.Int).SetUint64(rwt.r), new(big.Int).SetUint64(params.Ether)) - chainReward := new(big.Int) - if !chain.Config().IsTIPEpochHalving(header.Number) { - chainReward = util.RewardInflation(chain, originalReward, number, common.BlocksPerYear) - } else { - halvingSupply := big.NewInt(9000000000) // TODO use config.halvingSupply - _, epochNum, err := adaptor.EngineV2.IsEpochSwitch(header) - if err != nil { - return nil, err - } - epochSinceHalving := epochNum // TODO Minus config.epochHalvingOnset - chainReward = util.RewardHalving(originalReward, epochRewardTotal, halvingSupply, epochSinceHalving) - } - rewardSigners, err := CalculateRewardForSigner(chainReward, signers[rwt.t]) + 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. - voterResults := make(map[common.Address]interface{}) + rewardResults := make(map[common.Address]interface{}) for signer, calcReward := range rewardSigners { rewards, err := contracts.CalculateRewardForHolders(foundationWalletAddr, parentState, signer, calcReward, number) if err != nil { @@ -275,9 +263,9 @@ func AttachConsensusV2Hooks(adaptor *XDPoS.XDPoS, bc *core.BlockChain, chainConf stateBlock.AddBalance(holder, reward) } } - voterResults[signer] = rewards + rewardResults[signer] = rewards } - rewardsMap[rwt.key] = voterResults + rewardsMap[rwt.key] = rewardResults } } log.Debug("Time Calculated HookReward ", "block", header.Number.Uint64(), "time", common.PrettyDuration(time.Since(start))) @@ -356,7 +344,7 @@ func GetSigningTxCount(c *XDPoS.XDPoS, chain consensus.ChainReader, header *type } if len(protector) < currentConfig.MaxProtectorNodes { protector = append(protector, node.Address) - } else { + } else if len(observer) < currentConfig.MaxObverserNodes { observer = append(observer, node.Address) } } @@ -457,29 +445,21 @@ func CalculateRewardForSigner(chainReward *big.Int, signers map[common.Address]* return resultSigners, nil } -// func TestRewardBeZero(t *testing.T) { -// billion := big.NewInt(1000000000) -// epochRewardTotal := big.NewInt(16000) -// epochRewardTotal.Mul(epochRewardTotal, billion) -// epochReward1 := big.NewInt(10000) -// epochReward1.Mul(epochReward1, billion) -// epochReward2 := big.NewInt(4000) -// epochReward2.Mul(epochReward2, billion) -// epochReward3 := big.NewInt(2000) -// epochReward3.Mul(epochReward3, billion) -// // 45 Billion - 39 Billion XDC (1 XDC = 10^9 wei) -// halvingSupply := big.NewInt(6000000000) -// halvingSupply.Mul(halvingSupply, billion) -// sum := big.NewInt(0) -// for i := uint64(0); i < 30000000; i++ { -// r := new(big.Int).Add(RewardHalving(epochReward1, epochRewardTotal, halvingSupply, i), RewardHalving(epochReward2, epochRewardTotal, halvingSupply, i)) -// r.Add(r, RewardHalving(epochReward3, epochRewardTotal, halvingSupply, i)) -// if r.BitLen() == 0 { -// t.Log("reward be 0 at i=", i) // reward be 0 at i= 11225088, wich is more than 200 years in the future -// break -// } -// sum.Add(sum, r) -// } -// t.Log("sum", sum) // sum 5999999999982635022, which is less than total, and never reach totoal -// assert.True(t, sum.Cmp(halvingSupply) < 0) -// } +// 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 +} diff --git a/params/config.go b/params/config.go index 11d5412552..ad453ae91a 100644 --- a/params/config.go +++ b/params/config.go @@ -171,6 +171,7 @@ var ( 900: { MaxMasternodes: 20, MaxProtectorNodes: 17, + MaxObverserNodes: 1, SwitchRound: 900, CertThreshold: 0.667, TimeoutSyncThreshold: 4, @@ -454,15 +455,16 @@ type V2 struct { type V2Config struct { MaxMasternodes int `json:"maxMasternodes"` // v2 max masternodes MaxProtectorNodes int `json:"maxProtectorNodes"` // v2 max ProtectorNodes + MaxObverserNodes int `json:"maxObserverNodes"` // v2 max ObserverNodes 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 + MasternodeReward uint64 `json:"masternodeReward"` // Block reward per master node (core validator) - unit Ether + ProtectorReward uint64 `json:"protectorReward"` // Block reward per protector - unit Ether + ObserverReward uint64 `json:"observerReward"` // Block reward per observer - unit Ether ExpTimeoutConfig ExpTimeoutConfig `json:"expTimeoutConfig"` }