record total minted into smart contract (#1026)

* feat: record total minted into smart contract

* feat: GetCurrentTotalMinted API
This commit is contained in:
wgr523 2025-06-16 17:31:33 +08:00 committed by GitHub
parent ae70b5dc14
commit e13265a7d7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 84 additions and 0 deletions

View file

@ -68,6 +68,7 @@ var (
XDCXLendingFinalizedTradeAddressBinary = HexToAddress("0x0000000000000000000000000000000000000094")
XDCNativeAddressBinary = HexToAddress("0x0000000000000000000000000000000000000001")
LendingLockAddressBinary = HexToAddress("0x0000000000000000000000000000000000000011")
MintedRecordAddressBinary = HexToAddress("0x000000000000000000000000000000000000009a")
)
var (

View file

@ -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
}

View file

@ -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)
}

View file

@ -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

View file

@ -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 := &currentTotalMinted{
TotalMinted: (*hexutil.Big)(totalMinted),
LastEpochNum: (*hexutil.Big)(lastEpochNum),
BlockHash: header.Hash(),
BlockNumber: (*hexutil.Big)(header.Number),
}
return result, nil
}

View file

@ -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({