Make token supply related fns more accurate (#3509)
* [staking] Move reward values from Network pkg to its own * Refactor code for the move * Implement logic to accurately set total supply Signed-off-by: Daniel Van Der Maden <dvandermaden0@berkeley.edu> * [staking] Add totalPreStakingNetworkRewards to reward values * Implement GetTotalTokens for use in other packages Signed-off-by: Daniel Van Der Maden <dvandermaden0@berkeley.edu> * [core] Move getGenesisSpec to core pkg Signed-off-by: Daniel Van Der Maden <dvandermaden0@berkeley.edu> * [core] Update gen spec docs Signed-off-by: Daniel Van Der Maden <dvandermaden0@berkeley.edu> * [staking] Hook in updateInitialRewardValues on node init * Add some docs for clarification Signed-off-by: Daniel Van Der Maden <dvandermaden0@berkeley.edu> * [rpc] Fix GetCirculatingSupply & GetTotalSupply RPCs * Updated err msg in staking reward values.go Signed-off-by: Daniel Van Der Maden <dvandermaden0@berkeley.edu> * [rpc] Move GetCirculatingSupply logic into internal pkg Signed-off-by: Daniel Van Der Maden <dvandermaden0@berkeley.edu> * [explorer] Update circulating supply & total supply vals Signed-off-by: Daniel Van Der Maden <dvandermaden0@berkeley.edu> * [staking] Add Localnet rewards val & Errs * [internal] Make GetCirculatingSupply consistent with WhatPercentStakedNow Signed-off-by: Daniel Van Der Maden <dvandermaden0@berkeley.edu> * [staking] Fix imports Signed-off-by: Daniel Van Der Maden <dvandermaden0@berkeley.edu> * [consensus] Make PercentageForTimeStamp return 1 for non-mainnet chains Signed-off-by: Daniel Van Der Maden <dvandermaden0@berkeley.edu> * [staking] Fix reward dec math + Testnet testnet vals Signed-off-by: Daniel Van Der Maden <dvandermaden0@berkeley.edu> * [staking] Make all const reward vals ONE instead of ATTO Signed-off-by: Daniel Van Der Maden <dvandermaden0@berkeley.edu> * [internal] Correct returned value to ONE instead of Atto Signed-off-by: Daniel Van Der Maden <dvandermaden0@berkeley.edu> * [staking] Fix dec precision Signed-off-by: Daniel Van Der Maden <dvandermaden0@berkeley.edu> * [staking] Fix TestGetPreStakingRewardsFromBlockNumber test Signed-off-by: Daniel Van Der Maden <dvandermaden0@berkeley.edu> * [staking] Use TotalInitialTokens instead of TotalPreStakingTokens * Done so basis is off block 0 to account for phased mainnet release Signed-off-by: Daniel Van Der Maden <dvandermaden0@berkeley.edu> * [internal] Fix GetCirculatingSupply Signed-off-by: Daniel Van Der Maden <dvandermaden0@berkeley.edu>pull/3513/head
parent
9ab1038111
commit
94bf414083
@ -0,0 +1,46 @@ |
|||||||
|
package chain |
||||||
|
|
||||||
|
import ( |
||||||
|
"context" |
||||||
|
"math/big" |
||||||
|
"time" |
||||||
|
|
||||||
|
"github.com/harmony-one/harmony/consensus/engine" |
||||||
|
"github.com/harmony-one/harmony/consensus/reward" |
||||||
|
"github.com/harmony-one/harmony/numeric" |
||||||
|
"github.com/harmony-one/harmony/shard" |
||||||
|
stakingReward "github.com/harmony-one/harmony/staking/reward" |
||||||
|
) |
||||||
|
|
||||||
|
// GetCirculatingSupply using the following formula:
|
||||||
|
// (TotalInitialTokens * percentReleased) + PreStakingBlockRewards + StakingBlockRewards
|
||||||
|
//
|
||||||
|
// Note that PreStakingBlockRewards is set to the amount of rewards given out by the
|
||||||
|
// LAST BLOCK of the pre-staking era regardless of what the current block height is
|
||||||
|
// if network is in the pre-staking era. This is for implementation reasons, reference
|
||||||
|
// stakingReward.GetTotalPreStakingTokens for more details.
|
||||||
|
//
|
||||||
|
// WARNING: only works on beaconchain if in staking era.
|
||||||
|
func GetCirculatingSupply( |
||||||
|
ctx context.Context, chain engine.ChainReader, |
||||||
|
) (ret numeric.Dec, err error) { |
||||||
|
currHeader, timestamp := chain.CurrentHeader(), time.Now().Unix() |
||||||
|
stakingBlockRewards := big.NewInt(0) |
||||||
|
|
||||||
|
if chain.Config().IsStaking(currHeader.Epoch()) { |
||||||
|
if chain.ShardID() != shard.BeaconChainShardID { |
||||||
|
return numeric.Dec{}, stakingReward.ErrInvalidBeaconChain |
||||||
|
} |
||||||
|
if stakingBlockRewards, err = chain.ReadBlockRewardAccumulator(currHeader.Number().Uint64()); err != nil { |
||||||
|
return numeric.Dec{}, err |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
releasedInitSupply := stakingReward.TotalInitialTokens.Mul( |
||||||
|
reward.PercentageForTimeStamp(timestamp), |
||||||
|
) |
||||||
|
preStakingBlockRewards := stakingReward.GetTotalPreStakingTokens().Sub(stakingReward.TotalInitialTokens) |
||||||
|
return releasedInitSupply.Add(preStakingBlockRewards).Add( |
||||||
|
numeric.NewDecFromBigIntWithPrec(stakingBlockRewards, 18), |
||||||
|
), nil |
||||||
|
} |
@ -1,24 +0,0 @@ |
|||||||
package network |
|
||||||
|
|
||||||
import ( |
|
||||||
"testing" |
|
||||||
|
|
||||||
"github.com/harmony-one/harmony/numeric" |
|
||||||
) |
|
||||||
|
|
||||||
func TestFiveSecondsBaseStakedReward(t *testing.T) { |
|
||||||
expectedNewReward := BaseStakedReward.Mul(numeric.MustNewDecFromStr("5")).Quo(numeric.MustNewDecFromStr("8")) |
|
||||||
|
|
||||||
if !expectedNewReward.Equal(FiveSecondsBaseStakedReward) { |
|
||||||
t.Errorf( |
|
||||||
"Expected: %s, Got: %s", FiveSecondsBaseStakedReward.String(), expectedNewReward.String(), |
|
||||||
) |
|
||||||
} |
|
||||||
|
|
||||||
expectedNewReward = BaseStakedReward.Mul(numeric.MustNewDecFromStr("2")).Quo(numeric.MustNewDecFromStr("8")) |
|
||||||
if !expectedNewReward.Equal(TwoSecondsBaseStakedReward) { |
|
||||||
t.Errorf( |
|
||||||
"Expected: %s, Got: %s", TwoSecondsBaseStakedReward.String(), expectedNewReward.String(), |
|
||||||
) |
|
||||||
} |
|
||||||
} |
|
@ -0,0 +1,156 @@ |
|||||||
|
package reward |
||||||
|
|
||||||
|
import ( |
||||||
|
"fmt" |
||||||
|
"math/big" |
||||||
|
|
||||||
|
"github.com/harmony-one/harmony/common/denominations" |
||||||
|
"github.com/harmony-one/harmony/consensus/engine" |
||||||
|
shardingconfig "github.com/harmony-one/harmony/internal/configs/sharding" |
||||||
|
"github.com/harmony-one/harmony/internal/params" |
||||||
|
"github.com/harmony-one/harmony/numeric" |
||||||
|
"github.com/harmony-one/harmony/shard" |
||||||
|
) |
||||||
|
|
||||||
|
var ( |
||||||
|
// PreStakedBlocks is the block reward, to be split evenly among block signers in pre-staking era.
|
||||||
|
// 24 ONE per block
|
||||||
|
PreStakedBlocks = new(big.Int).Mul(big.NewInt(24), big.NewInt(denominations.One)) |
||||||
|
// StakedBlocks is the flat-rate block reward for epos staking launch.
|
||||||
|
// 28 ONE per block.
|
||||||
|
StakedBlocks = numeric.NewDecFromBigInt(new(big.Int).Mul( |
||||||
|
big.NewInt(28), big.NewInt(denominations.One), |
||||||
|
)) |
||||||
|
// FiveSecStakedBlocks is the flat-rate block reward after epoch 230.
|
||||||
|
// 17.5 ONE per block
|
||||||
|
FiveSecStakedBlocks = numeric.NewDecFromBigInt(new(big.Int).Mul( |
||||||
|
big.NewInt(17.5*denominations.Nano), big.NewInt(denominations.Nano), |
||||||
|
)) |
||||||
|
// TwoSecStakedBlocks is the flat-rate block reward after epoch 360.
|
||||||
|
// 7 ONE per block
|
||||||
|
TwoSecStakedBlocks = numeric.NewDecFromBigInt(new(big.Int).Mul( |
||||||
|
big.NewInt(7*denominations.Nano), big.NewInt(denominations.Nano), |
||||||
|
)) |
||||||
|
|
||||||
|
// TotalInitialTokens is the total amount of tokens (in ONE) at block 0 of the network.
|
||||||
|
// This should be set/change on the node's init according to the core.GenesisSpec.
|
||||||
|
TotalInitialTokens = numeric.Dec{Int: big.NewInt(0)} |
||||||
|
|
||||||
|
// None ..
|
||||||
|
None = big.NewInt(0) |
||||||
|
|
||||||
|
// ErrInvalidBeaconChain if given chain is not beacon chain
|
||||||
|
ErrInvalidBeaconChain = fmt.Errorf("given chain is not beaconchain") |
||||||
|
) |
||||||
|
|
||||||
|
// getPreStakingRewardsFromBlockNumber returns the number of tokens injected into the network
|
||||||
|
// in the pre-staking era (epoch < staking epoch) in ATTO.
|
||||||
|
//
|
||||||
|
// If the block number is > than the last block of an epoch, the last block of the epoch is
|
||||||
|
// used for the calculation by default.
|
||||||
|
//
|
||||||
|
// WARNING: This assumes beacon chain is at most the same block height as another shard in the
|
||||||
|
// transition from pre-staking to staking era/epoch.
|
||||||
|
func getPreStakingRewardsFromBlockNumber(id shardingconfig.NetworkID, blockNum *big.Int) *big.Int { |
||||||
|
if blockNum.Cmp(big.NewInt(2)) == -1 { |
||||||
|
// block 0 & 1 does not contain block rewards
|
||||||
|
return big.NewInt(0) |
||||||
|
} |
||||||
|
|
||||||
|
lastBlockInEpoch := blockNum |
||||||
|
|
||||||
|
switch id { |
||||||
|
case shardingconfig.MainNet: |
||||||
|
lastBlockInEpoch = new(big.Int).SetUint64(shardingconfig.MainnetSchedule.EpochLastBlock( |
||||||
|
params.MainnetChainConfig.StakingEpoch.Uint64() - 1, |
||||||
|
)) |
||||||
|
case shardingconfig.TestNet: |
||||||
|
lastBlockInEpoch = new(big.Int).SetUint64(shardingconfig.TestnetSchedule.EpochLastBlock( |
||||||
|
params.TestnetChainConfig.StakingEpoch.Uint64() - 1, |
||||||
|
)) |
||||||
|
case shardingconfig.LocalNet: |
||||||
|
lastBlockInEpoch = new(big.Int).SetUint64(shardingconfig.LocalnetSchedule.EpochLastBlock( |
||||||
|
params.LocalnetChainConfig.StakingEpoch.Uint64() - 1, |
||||||
|
)) |
||||||
|
} |
||||||
|
|
||||||
|
if blockNum.Cmp(lastBlockInEpoch) == 1 { |
||||||
|
blockNum = lastBlockInEpoch |
||||||
|
} |
||||||
|
|
||||||
|
return new(big.Int).Mul(PreStakedBlocks, new(big.Int).Sub(blockNum, big.NewInt(1))) |
||||||
|
} |
||||||
|
|
||||||
|
// WARNING: the data collected here are calculated from a consumer of the Rosetta API.
|
||||||
|
// If data becomes mission critical, implement a cross-link based approach.
|
||||||
|
//
|
||||||
|
// Data Source: https://github.com/harmony-one/jupyter
|
||||||
|
//
|
||||||
|
// TODO (dm): use first crosslink of all shards to compute rewards on network instead of relying on constants.
|
||||||
|
var ( |
||||||
|
totalPreStakingNetworkRewardsInAtto = map[shardingconfig.NetworkID][]*big.Int{ |
||||||
|
shardingconfig.MainNet: { |
||||||
|
// Below are all of the last blocks of pre-staking era for mainnet.
|
||||||
|
getPreStakingRewardsFromBlockNumber(shardingconfig.MainNet, big.NewInt(3375103)), |
||||||
|
getPreStakingRewardsFromBlockNumber(shardingconfig.MainNet, big.NewInt(3286737)), |
||||||
|
getPreStakingRewardsFromBlockNumber(shardingconfig.MainNet, big.NewInt(3326153)), |
||||||
|
getPreStakingRewardsFromBlockNumber(shardingconfig.MainNet, big.NewInt(3313572)), |
||||||
|
}, |
||||||
|
shardingconfig.TestNet: { |
||||||
|
// Below are all of the placeholders 'last blocks' of pre-staking era for testnet.
|
||||||
|
getPreStakingRewardsFromBlockNumber(shardingconfig.TestNet, big.NewInt(999999)), |
||||||
|
getPreStakingRewardsFromBlockNumber(shardingconfig.TestNet, big.NewInt(999999)), |
||||||
|
getPreStakingRewardsFromBlockNumber(shardingconfig.TestNet, big.NewInt(999999)), |
||||||
|
getPreStakingRewardsFromBlockNumber(shardingconfig.TestNet, big.NewInt(999999)), |
||||||
|
}, |
||||||
|
shardingconfig.LocalNet: { |
||||||
|
// Below are all of the placeholders 'last blocks' of pre-staking era for localnet.
|
||||||
|
getPreStakingRewardsFromBlockNumber(shardingconfig.LocalNet, big.NewInt(999999)), |
||||||
|
getPreStakingRewardsFromBlockNumber(shardingconfig.LocalNet, big.NewInt(999999)), |
||||||
|
}, |
||||||
|
} |
||||||
|
) |
||||||
|
|
||||||
|
// getTotalPreStakingNetworkRewards in ATTO for given NetworkID
|
||||||
|
func getTotalPreStakingNetworkRewards(id shardingconfig.NetworkID) *big.Int { |
||||||
|
totalRewards := big.NewInt(0) |
||||||
|
if allRewards, ok := totalPreStakingNetworkRewardsInAtto[id]; ok { |
||||||
|
for _, reward := range allRewards { |
||||||
|
totalRewards = new(big.Int).Add(reward, totalRewards) |
||||||
|
} |
||||||
|
} |
||||||
|
return totalRewards |
||||||
|
} |
||||||
|
|
||||||
|
// GetTotalTokens in the network for all shards in ONE.
|
||||||
|
// This can only be computed with beaconchain if in staking era.
|
||||||
|
// If not in staking era, returns the rewards given out by the start of staking era.
|
||||||
|
func GetTotalTokens(chain engine.ChainReader) (numeric.Dec, error) { |
||||||
|
currHeader := chain.CurrentHeader() |
||||||
|
if !chain.Config().IsStaking(currHeader.Epoch()) { |
||||||
|
return GetTotalPreStakingTokens(), nil |
||||||
|
} |
||||||
|
if chain.ShardID() != shard.BeaconChainShardID { |
||||||
|
return numeric.Dec{}, ErrInvalidBeaconChain |
||||||
|
} |
||||||
|
|
||||||
|
stakingRewards, err := chain.ReadBlockRewardAccumulator(currHeader.Number().Uint64()) |
||||||
|
if err != nil { |
||||||
|
return numeric.Dec{}, err |
||||||
|
} |
||||||
|
return GetTotalPreStakingTokens().Add(numeric.NewDecFromBigIntWithPrec(stakingRewards, 18)), nil |
||||||
|
} |
||||||
|
|
||||||
|
// GetTotalPreStakingTokens returns the total amount of tokens (in ONE) in the
|
||||||
|
// network at the the last block of the pre-staking era (epoch < staking epoch).
|
||||||
|
func GetTotalPreStakingTokens() numeric.Dec { |
||||||
|
preStakingRewards := numeric.NewDecFromBigIntWithPrec( |
||||||
|
getTotalPreStakingNetworkRewards(shard.Schedule.GetNetworkID()), 18, |
||||||
|
) |
||||||
|
return TotalInitialTokens.Add(preStakingRewards) |
||||||
|
} |
||||||
|
|
||||||
|
// SetTotalInitialTokens with the given initial tokens (from genesis in ATTO).
|
||||||
|
func SetTotalInitialTokens(initTokensAsAtto *big.Int) { |
||||||
|
TotalInitialTokens = numeric.NewDecFromBigIntWithPrec(initTokensAsAtto, 18) |
||||||
|
} |
@ -0,0 +1,40 @@ |
|||||||
|
package reward |
||||||
|
|
||||||
|
import ( |
||||||
|
"math/big" |
||||||
|
"testing" |
||||||
|
|
||||||
|
shardingconfig "github.com/harmony-one/harmony/internal/configs/sharding" |
||||||
|
"github.com/harmony-one/harmony/numeric" |
||||||
|
) |
||||||
|
|
||||||
|
func TestFiveSecondsBaseStakedReward(t *testing.T) { |
||||||
|
expectedNewReward := StakedBlocks.Mul(numeric.MustNewDecFromStr("5")).Quo(numeric.MustNewDecFromStr("8")) |
||||||
|
|
||||||
|
if !expectedNewReward.Equal(FiveSecStakedBlocks) { |
||||||
|
t.Errorf( |
||||||
|
"Expected: %s, Got: %s", FiveSecStakedBlocks.String(), expectedNewReward.String(), |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
expectedNewReward = StakedBlocks.Mul(numeric.MustNewDecFromStr("2")).Quo(numeric.MustNewDecFromStr("8")) |
||||||
|
if !expectedNewReward.Equal(TwoSecStakedBlocks) { |
||||||
|
t.Errorf( |
||||||
|
"Expected: %s, Got: %s", TwoSecStakedBlocks.String(), expectedNewReward.String(), |
||||||
|
) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func TestGetPreStakingRewardsFromBlockNumber(t *testing.T) { |
||||||
|
refMainnetRewards, _ := new(big.Int).SetString("319237464000000000000000000", 10) |
||||||
|
mainnetRewards := getTotalPreStakingNetworkRewards(shardingconfig.MainNet) |
||||||
|
if refMainnetRewards.Cmp(mainnetRewards) != 0 { |
||||||
|
t.Errorf("Expected mainnet rewards to be %v NOT %v", refMainnetRewards, mainnetRewards) |
||||||
|
} |
||||||
|
|
||||||
|
refTestnetRewards, _ := new(big.Int).SetString("7104000000000000000000", 10) |
||||||
|
testnetRewards := getTotalPreStakingNetworkRewards(shardingconfig.TestNet) |
||||||
|
if refTestnetRewards.Cmp(testnetRewards) != 0 { |
||||||
|
t.Errorf("Expected testnet rewards to be %v NOT %v", refTestnetRewards, testnetRewards) |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue