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