diff --git a/common/types.go b/common/types.go index 2e2136d4a6..678e7d611e 100644 --- a/common/types.go +++ b/common/types.go @@ -68,6 +68,7 @@ var ( XDCXLendingFinalizedTradeAddressBinary = HexToAddress("0x0000000000000000000000000000000000000094") XDCNativeAddressBinary = HexToAddress("0x0000000000000000000000000000000000000001") LendingLockAddressBinary = HexToAddress("0x0000000000000000000000000000000000000011") + MintedRecordAddressBinary = HexToAddress("0x000000000000000000000000000000000000009a") ) var ( diff --git a/consensus/tests/engine_v2_tests/reward_test.go b/consensus/tests/engine_v2_tests/reward_test.go index 9d095b3c3f..46b914fc3f 100644 --- a/consensus/tests/engine_v2_tests/reward_test.go +++ b/consensus/tests/engine_v2_tests/reward_test.go @@ -175,6 +175,7 @@ func TestHookRewardAfterUpgrade(t *testing.T) { assert.Nil(t, err) // set switch to 1800, so that it covers 901-1799, 1800-2700 two epochs config.XDPoS.V2.SwitchBlock.SetUint64(1800) + config.XDPoS.V2.SwitchEpoch = 2 // set upgrade number to 0 backup := common.TIPUpgradeReward common.TIPUpgradeReward = big.NewInt(0) @@ -296,6 +297,11 @@ func TestHookRewardAfterUpgrade(t *testing.T) { b, _ := big.NewInt(0).SetString("30012500000000000000", 10) // this value tests the float64 reward assert.Zero(t, b.Cmp(r[config.XDPoS.FoudationWalletAddr]), "real reward is", r[config.XDPoS.FoudationWalletAddr]) } + totalMinted := state.GetTotalMinted(statedb).Big() + totalExpect, _ := big.NewInt(0).SetString("2100125000000000000000", 10) + assert.Zero(t, totalMinted.Cmp(totalExpect), "statedb records wrong total minted") + lastEpochNum := state.GetLastEpochNum(statedb).Big().Int64() + assert.Equal(t, 3, int(lastEpochNum)) common.TIPUpgradeReward = backup } diff --git a/core/state/statedb_utils.go b/core/state/statedb_utils.go index 7bd2c4d355..bc459235a8 100644 --- a/core/state/statedb_utils.go +++ b/core/state/statedb_utils.go @@ -152,3 +152,30 @@ func GetVoterCap(statedb *StateDB, candidate, voter common.Address) *big.Int { ret := statedb.GetState(common.MasternodeVotingSMCBinary, common.BytesToHash(retByte)) return ret.Big() } + +var ( + slotMintedRecordTotalMinted uint64 = 0 + slotMintedRecordLastEpochNum uint64 = 1 +) + +func GetTotalMinted(statedb *StateDB) common.Hash { + hash := GetLocSimpleVariable(slotMintedRecordTotalMinted) + totalMinted := statedb.GetState(common.MintedRecordAddressBinary, hash) + return totalMinted +} + +func PutTotalMinted(statedb *StateDB, value common.Hash) { + hash := GetLocSimpleVariable(slotMintedRecordTotalMinted) + statedb.SetState(common.MintedRecordAddressBinary, hash, value) +} + +func GetLastEpochNum(statedb *StateDB) common.Hash { + hash := GetLocSimpleVariable(slotMintedRecordLastEpochNum) + totalMinted := statedb.GetState(common.MintedRecordAddressBinary, hash) + return totalMinted +} + +func PutLastEpochNum(statedb *StateDB, value common.Hash) { + hash := GetLocSimpleVariable(slotMintedRecordLastEpochNum) + statedb.SetState(common.MintedRecordAddressBinary, hash, value) +} diff --git a/eth/hooks/engine_v2_hooks.go b/eth/hooks/engine_v2_hooks.go index 4cfb228456..521c9944d6 100644 --- a/eth/hooks/engine_v2_hooks.go +++ b/eth/hooks/engine_v2_hooks.go @@ -190,6 +190,7 @@ func AttachConsensusV2Hooks(adaptor *XDPoS.XDPoS, bc *core.BlockChain, chainConf 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 @@ -233,6 +234,7 @@ func AttachConsensusV2Hooks(adaptor *XDPoS.XDPoS, bc *core.BlockChain, chainConf } else { rewardsMap["signersProtector"] = signers[ProtectorNodeBeneficiary] rewardsMap["signersObserver"] = signers[ObserverNodeBeneficiary] + rewardSum := new(big.Int) type rewardWithType struct { r float64 t Beneficiary @@ -262,12 +264,32 @@ func AttachConsensusV2Hooks(adaptor *XDPoS.XDPoS, bc *core.BlockChain, chainConf if len(rewards) > 0 { for holder, reward := range rewards { stateBlock.AddBalance(holder, reward) + rewardSum.Add(rewardSum, reward) } } rewardResults[signer] = rewards } rewardsMap[rwt.key] = rewardResults } + // record the total reward into state db + totalMinted := state.GetTotalMinted(stateBlock).Big() + lastEpochNum := state.GetLastEpochNum(stateBlock) + if lastEpochNum.IsZero() { + // if `lastEpochNum` is zero, the total minted has not included tokens before TIPUpgradeReward + // calculate the tokens before TIPUpgradeReward and set to totalMinted + // for now no-do + } + totalMinted.Add(totalMinted, rewardSum) + bigPower256 := new(big.Int).Lsh(big.NewInt(1), 256) + bigMaxU256 := new(big.Int).Sub(bigPower256, big.NewInt(1)) + // if overflow, set to maxU256 and log a warning + if totalMinted.Cmp(bigMaxU256) >= 0 { + totalMinted.Set(bigMaxU256) + log.Warn("[HookReward] total minted overflow max u256") + } + log.Debug("[HookReward] total minted in hook", "value", totalMinted) + state.PutTotalMinted(stateBlock, common.BigToHash(totalMinted)) + state.PutLastEpochNum(stateBlock, common.Uint64ToHash(epochNum)) } log.Debug("Time Calculated HookReward ", "block", header.Number.Uint64(), "time", common.PrettyDuration(time.Since(start))) return rewardsMap, nil diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 26f4e10897..cb03503b7a 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -3639,3 +3639,26 @@ func (s *PublicBlockChainAPI) GetStakerROIMasternode(masternode common.Address) return 100.0 / float64(totalCap.Div(totalCap, voterRewardAYear).Uint64()) } + +type currentTotalMinted struct { + TotalMinted *hexutil.Big `json:"totalMinted"` + LastEpochNum *hexutil.Big `json:"lastEpochNum"` + BlockHash common.Hash `json:"blockHash"` + BlockNumber *hexutil.Big `json:"blockNumber"` +} + +func (s *PublicBlockChainAPI) GetCurrentTotalMinted(ctx context.Context) (*currentTotalMinted, error) { + statedb, header, err := s.b.StateAndHeaderByNumber(ctx, rpc.LatestBlockNumber) + if err != nil { + return nil, err + } + totalMinted := state.GetTotalMinted(statedb).Big() + lastEpochNum := state.GetLastEpochNum(statedb).Big() + result := ¤tTotalMinted{ + TotalMinted: (*hexutil.Big)(totalMinted), + LastEpochNum: (*hexutil.Big)(lastEpochNum), + BlockHash: header.Hash(), + BlockNumber: (*hexutil.Big)(header.Number), + } + return result, nil +} diff --git a/internal/web3ext/web3ext.go b/internal/web3ext/web3ext.go index 568d7f0718..60517e6e30 100644 --- a/internal/web3ext/web3ext.go +++ b/internal/web3ext/web3ext.go @@ -555,6 +555,11 @@ web3._extend({ call: 'eth_getBlockReceipts', params: 1, }), + new web3._extend.Method({ + name: 'getCurrentTotalMinted', + call: 'eth_getCurrentTotalMinted', + params: 0, + }), ], properties: [ new web3._extend.Property({