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
Daniel Van Der Maden 4 years ago committed by GitHub
parent 9ab1038111
commit 94bf414083
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 17
      api/service/explorer/service.go
  2. 6
      consensus/reward/schedule.go
  3. 1
      core/blockchain.go
  4. 21
      core/genesis.go
  5. 10
      core/state_transition.go
  6. 4
      hmy/blockchain.go
  7. 21
      internal/chain/reward.go
  8. 46
      internal/chain/supply.go
  9. 14
      node/node.go
  10. 16
      rosetta/services/block_side_effect.go
  11. 19
      rpc/blockchain.go
  12. 35
      staking/network/reward.go
  13. 24
      staking/network/reward_test.go
  14. 156
      staking/reward/values.go
  15. 40
      staking/reward/values_test.go

@ -13,11 +13,11 @@ import (
"github.com/gorilla/mux"
msg_pb "github.com/harmony-one/harmony/api/proto/message"
"github.com/harmony-one/harmony/api/service/syncing"
"github.com/harmony-one/harmony/consensus/reward"
"github.com/harmony-one/harmony/core"
"github.com/harmony-one/harmony/internal/chain"
"github.com/harmony-one/harmony/internal/utils"
"github.com/harmony-one/harmony/numeric"
"github.com/harmony-one/harmony/p2p"
stakingReward "github.com/harmony-one/harmony/staking/reward"
)
// Constants for explorer service.
@ -25,7 +25,6 @@ const (
explorerPortDifference = 4000
defaultPageSize = "1000"
maxAddresses = 100000
totalSupply = 12600000000
)
// HTTPError is an HTTP error.
@ -156,8 +155,11 @@ func (s *Service) GetAddresses(w http.ResponseWriter, r *http.Request) {
// GetCirculatingSupply serves /circulating-supply end-point.
func (s *Service) GetCirculatingSupply(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
timestamp := time.Now().Unix()
circulatingSupply := reward.PercentageForTimeStamp(timestamp).Mul(numeric.NewDec(totalSupply))
circulatingSupply, err := chain.GetCirculatingSupply(context.Background(), s.blockchain)
if err != nil {
utils.Logger().Warn().Err(err).Msg("unable to fetch circulating supply")
w.WriteHeader(http.StatusInternalServerError)
}
if err := json.NewEncoder(w).Encode(circulatingSupply); err != nil {
utils.Logger().Warn().Msg("cannot JSON-encode circulating supply")
w.WriteHeader(http.StatusInternalServerError)
@ -167,6 +169,11 @@ func (s *Service) GetCirculatingSupply(w http.ResponseWriter, r *http.Request) {
// GetTotalSupply serves /total-supply end-point.
func (s *Service) GetTotalSupply(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
totalSupply, err := stakingReward.GetTotalTokens(s.blockchain)
if err != nil {
utils.Logger().Warn().Err(err).Msg("unable to fetch total supply")
w.WriteHeader(http.StatusInternalServerError)
}
if err := json.NewEncoder(w).Encode(totalSupply); err != nil {
utils.Logger().Warn().Msg("cannot JSON-encode total supply")
w.WriteHeader(http.StatusInternalServerError)

@ -4,8 +4,10 @@ import (
"sort"
"time"
shardingconfig "github.com/harmony-one/harmony/internal/configs/sharding"
"github.com/harmony-one/harmony/internal/utils"
"github.com/harmony-one/harmony/numeric"
"github.com/harmony-one/harmony/shard"
)
type pair struct {
@ -123,6 +125,10 @@ func mustParse(ts string) int64 {
// PercentageForTimeStamp ..
func PercentageForTimeStamp(ts int64) numeric.Dec {
if shard.Schedule.GetNetworkID() != shardingconfig.MainNet {
return numeric.MustNewDecFromStr("1")
}
bucket := pair{}
i, j := 0, 1

@ -2786,6 +2786,7 @@ func (bc *BlockChain) prepareStakingMetaData(
}
// ReadBlockRewardAccumulator must only be called on beaconchain
// Note that block rewards are only for staking era.
func (bc *BlockChain) ReadBlockRewardAccumulator(number uint64) (*big.Int, error) {
if !bc.chainConfig.IsStaking(shard.Schedule.CalcEpochNumber(number)) {
return big.NewInt(0), nil

@ -42,6 +42,7 @@ import (
"github.com/harmony-one/harmony/core/state"
"github.com/harmony-one/harmony/core/types"
nodeconfig "github.com/harmony-one/harmony/internal/configs/node"
shardingconfig "github.com/harmony-one/harmony/internal/configs/sharding"
"github.com/harmony-one/harmony/internal/utils"
"github.com/harmony-one/harmony/shard"
)
@ -329,3 +330,23 @@ func (g *Genesis) MustCommit(db ethdb.Database) *types.Block {
}
return block
}
// GetGenesisSpec for a given shard
func GetGenesisSpec(shardID uint32) *Genesis {
if shard.Schedule.GetNetworkID() == shardingconfig.MainNet {
return NewGenesisSpec(nodeconfig.Mainnet, shardID)
}
if shard.Schedule.GetNetworkID() == shardingconfig.LocalNet {
return NewGenesisSpec(nodeconfig.Localnet, shardID)
}
return NewGenesisSpec(nodeconfig.Testnet, shardID)
}
// GetInitialFunds for a given shard
func GetInitialFunds(shardID uint32) *big.Int {
spec, total := GetGenesisSpec(shardID), big.NewInt(0)
for _, account := range spec.Alloc {
total = new(big.Int).Add(account.Balance, total)
}
return total
}

@ -29,7 +29,7 @@ import (
"github.com/harmony-one/harmony/internal/params"
"github.com/harmony-one/harmony/internal/utils"
staking2 "github.com/harmony-one/harmony/staking"
"github.com/harmony-one/harmony/staking/network"
stakingReward "github.com/harmony-one/harmony/staking/reward"
staking "github.com/harmony-one/harmony/staking/types"
"github.com/pkg/errors"
)
@ -486,21 +486,21 @@ func (st *StateTransition) verifyAndApplyUndelegateTx(
func (st *StateTransition) verifyAndApplyCollectRewards(collectRewards *staking.CollectRewards) (*big.Int, error) {
if st.bc == nil {
return network.NoReward, errors.New("[CollectRewards] No chain context provided")
return stakingReward.None, errors.New("[CollectRewards] No chain context provided")
}
delegations, err := st.bc.ReadDelegationsByDelegator(collectRewards.DelegatorAddress)
if err != nil {
return network.NoReward, err
return stakingReward.None, err
}
updatedValidatorWrappers, totalRewards, err := VerifyAndCollectRewardsFromDelegation(
st.state, delegations,
)
if err != nil {
return network.NoReward, err
return stakingReward.None, err
}
for _, wrapper := range updatedValidatorWrappers {
if err := st.state.UpdateValidatorWrapper(wrapper.Address, wrapper); err != nil {
return network.NoReward, err
return stakingReward.None, err
}
}
st.state.AddBalance(collectRewards.DelegatorAddress, totalRewards)

@ -21,7 +21,7 @@ import (
"github.com/harmony-one/harmony/internal/utils"
"github.com/harmony-one/harmony/shard"
"github.com/harmony-one/harmony/staking/availability"
stakingNetwork "github.com/harmony-one/harmony/staking/network"
stakingReward "github.com/harmony-one/harmony/staking/reward"
"github.com/pkg/errors"
)
@ -136,7 +136,7 @@ func (hmy *Harmony) GetPreStakingBlockRewards(
rewardsForThisAddr = big.NewInt(0)
}
cur := big.NewInt(0)
cur.Mul(stakingNetwork.BlockReward, big.NewInt(int64(i+1))).Div(cur, count)
cur.Mul(stakingReward.PreStakedBlocks, big.NewInt(int64(i+1))).Div(cur, count)
reward := big.NewInt(0).Sub(cur, last)
rewards[slot.EcdsaAddress] = new(big.Int).Add(reward, rewardsForThisAddr)
last = cur

@ -25,6 +25,7 @@ import (
"github.com/harmony-one/harmony/shard"
"github.com/harmony-one/harmony/staking/availability"
"github.com/harmony-one/harmony/staking/network"
stakingReward "github.com/harmony-one/harmony/staking/reward"
"github.com/pkg/errors"
)
@ -142,30 +143,30 @@ func AccumulateRewardsAndCountSigs(
// After staking
if headerE := header.Epoch(); bc.Config().IsStaking(headerE) &&
bc.CurrentHeader().ShardID() == shard.BeaconChainShardID {
defaultReward := network.BaseStakedReward
defaultReward := stakingReward.StakedBlocks
// the block reward is adjusted accordingly based on 5s and 3s block time forks
if bc.Config().ChainID == params.TestnetChainID && bc.Config().FiveSecondsEpoch.Cmp(big.NewInt(16500)) == 0 {
// Testnet:
// This is testnet requiring the one-off forking logic
if blockNum > 634644 {
defaultReward = network.FiveSecondsBaseStakedReward
defaultReward = stakingReward.FiveSecStakedBlocks
if blockNum > 636507 {
defaultReward = network.BaseStakedReward
defaultReward = stakingReward.StakedBlocks
if blockNum > 639341 {
defaultReward = network.FiveSecondsBaseStakedReward
defaultReward = stakingReward.FiveSecStakedBlocks
}
}
}
if bc.Config().IsTwoSeconds(header.Epoch()) {
defaultReward = network.TwoSecondsBaseStakedReward
defaultReward = stakingReward.TwoSecStakedBlocks
}
} else {
// Mainnet (other nets):
if bc.Config().IsTwoSeconds(header.Epoch()) {
defaultReward = network.TwoSecondsBaseStakedReward
defaultReward = stakingReward.TwoSecStakedBlocks
} else if bc.Config().IsFiveSeconds(header.Epoch()) {
defaultReward = network.FiveSecondsBaseStakedReward
defaultReward = stakingReward.FiveSecStakedBlocks
}
}
@ -460,7 +461,7 @@ func AccumulateRewardsAndCountSigs(
count := big.NewInt(int64(len(signers)))
for i, account := range signers {
cur := big.NewInt(0)
cur.Mul(network.BlockReward, big.NewInt(int64(i+1))).Div(cur, count)
cur.Mul(stakingReward.PreStakedBlocks, big.NewInt(int64(i+1))).Div(cur, count)
diff := big.NewInt(0).Sub(cur, last)
state.AddBalance(account.EcdsaAddress, diff)
totalAmount.Add(totalAmount, diff)
@ -468,9 +469,9 @@ func AccumulateRewardsAndCountSigs(
}
}
if totalAmount.Cmp(network.BlockReward) != 0 {
if totalAmount.Cmp(stakingReward.PreStakedBlocks) != 0 {
utils.Logger().Error().
Int64("block-reward", network.BlockReward.Int64()).
Int64("block-reward", stakingReward.PreStakedBlocks.Int64()).
Int64("total-amount-paid-out", totalAmount.Int64()).
Msg("Total paid out was not equal to block-reward")
return nil, errors.Wrapf(

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

@ -34,6 +34,7 @@ import (
"github.com/harmony-one/harmony/p2p"
"github.com/harmony-one/harmony/shard"
"github.com/harmony-one/harmony/shard/committee"
"github.com/harmony-one/harmony/staking/reward"
"github.com/harmony-one/harmony/staking/slash"
staking "github.com/harmony-one/harmony/staking/types"
"github.com/harmony-one/harmony/webhooks"
@ -996,6 +997,9 @@ func New(
}()
}
// update reward values now that node is ready
node.updateInitialRewardValues()
// init metrics
initMetrics()
nodeStringCounterVec.WithLabelValues("version", nodeconfig.GetVersion()).Inc()
@ -1003,6 +1007,16 @@ func New(
return &node
}
// updateInitialRewardValues using the node data
func (node *Node) updateInitialRewardValues() {
numShards := shard.Schedule.InstanceForEpoch(node.Beaconchain().CurrentHeader().Epoch()).NumShards()
initTotal := big.NewInt(0)
for i := uint32(0); i < numShards; i++ {
initTotal = new(big.Int).Add(core.GetInitialFunds(i), initTotal)
}
reward.SetTotalInitialTokens(initTotal)
}
// InitConsensusWithValidators initialize shard state
// from latest epoch and update committee pub
// keys for consensus

@ -10,10 +10,7 @@ import (
"github.com/harmony-one/harmony/core"
hmytypes "github.com/harmony-one/harmony/core/types"
nodeconfig "github.com/harmony-one/harmony/internal/configs/node"
shardingconfig "github.com/harmony-one/harmony/internal/configs/sharding"
"github.com/harmony-one/harmony/rosetta/common"
"github.com/harmony-one/harmony/shard"
)
// containsSideEffectTransaction checks if the block contains any side effect operations to report.
@ -87,7 +84,7 @@ func (s *BlockAPI) getSideEffectTransaction(
// Handle genesis funds
if blk.NumberU64() == 0 {
ops, rosettaError := GetSideEffectOperationsFromGenesisSpec(getGenesisSpec(s.hmy.ShardID), startingOpIndex)
ops, rosettaError := GetSideEffectOperationsFromGenesisSpec(core.GetGenesisSpec(s.hmy.ShardID), startingOpIndex)
if rosettaError != nil {
return nil, rosettaError
}
@ -155,14 +152,3 @@ func (s *BlockAPI) sideEffectBlockTransaction(
}
return &types.BlockTransactionResponse{Transaction: tx}, nil
}
// getGenesisSpec ..
func getGenesisSpec(shardID uint32) *core.Genesis {
if shard.Schedule.GetNetworkID() == shardingconfig.MainNet {
return core.NewGenesisSpec(nodeconfig.Mainnet, shardID)
}
if shard.Schedule.GetNetworkID() == shardingconfig.LocalNet {
return core.NewGenesisSpec(nodeconfig.Localnet, shardID)
}
return core.NewGenesisSpec(nodeconfig.Testnet, shardID)
}

@ -4,13 +4,13 @@ import (
"context"
"fmt"
"math/big"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/rpc"
"github.com/harmony-one/harmony/consensus/reward"
"github.com/harmony-one/harmony/hmy"
"github.com/harmony-one/harmony/internal/chain"
internal_common "github.com/harmony-one/harmony/internal/common"
nodeconfig "github.com/harmony-one/harmony/internal/configs/node"
"github.com/harmony-one/harmony/internal/utils"
@ -20,10 +20,7 @@ import (
v1 "github.com/harmony-one/harmony/rpc/v1"
v2 "github.com/harmony-one/harmony/rpc/v2"
"github.com/harmony-one/harmony/shard"
)
const (
initSupply = int64(12600000000)
stakingReward "github.com/harmony-one/harmony/staking/reward"
)
// PublicBlockchainService provides an API to access the Harmony blockchain.
@ -624,18 +621,14 @@ func (s *PublicBlockchainService) GetCurrentBadBlocks(
func (s *PublicBlockchainService) GetTotalSupply(
ctx context.Context,
) (numeric.Dec, error) {
// Response output is the same for all versions
return numeric.NewDec(initSupply), nil
return stakingReward.GetTotalTokens(s.hmy.BlockChain)
}
// GetCirculatingSupply ..
// GetCirculatingSupply ...
func (s *PublicBlockchainService) GetCirculatingSupply(
ctx context.Context,
) (numeric.Dec, error) {
timestamp := time.Now()
// Response output is the same for all versions
return numeric.NewDec(initSupply).Mul(reward.PercentageForTimeStamp(timestamp.Unix())), nil
return chain.GetCirculatingSupply(ctx, s.hmy.BlockChain)
}
// GetStakingNetworkInfo ..

@ -10,40 +10,19 @@ import (
"github.com/harmony-one/harmony/internal/utils"
"github.com/harmony-one/harmony/numeric"
"github.com/harmony-one/harmony/shard"
stakingReward "github.com/harmony-one/harmony/staking/reward"
)
var (
// BlockReward is the block reward, to be split evenly among block signers in pre-staking era.
BlockReward = new(big.Int).Mul(big.NewInt(24), big.NewInt(denominations.One))
// BaseStakedReward is the flat-rate block reward for epos staking launch.
// 28 ONE per block
BaseStakedReward = numeric.NewDecFromBigInt(new(big.Int).Mul(
big.NewInt(28), big.NewInt(denominations.One),
))
// FiveSecondsBaseStakedReward is the flat-rate block reward after epoch 230.
// 17.5 ONE per block
FiveSecondsBaseStakedReward = numeric.NewDecFromBigInt(new(big.Int).Mul(
big.NewInt(17.5*denominations.Nano), big.NewInt(denominations.Nano),
))
// TwoSecondsBaseStakedReward is the flat-rate block reward after epoch 360.
// 7 ONE per block
TwoSecondsBaseStakedReward = numeric.NewDecFromBigInt(new(big.Int).Mul(
big.NewInt(7*denominations.Nano), big.NewInt(denominations.Nano),
))
// BlockRewardStakedCase is the baseline block reward in staked case -
totalTokens = numeric.NewDecFromBigInt(
new(big.Int).Mul(big.NewInt(12600000000), big.NewInt(denominations.One)),
)
targetStakedPercentage = numeric.MustNewDecFromStr("0.35")
dynamicAdjust = numeric.MustNewDecFromStr("0.4")
// ErrPayoutNotEqualBlockReward ..
ErrPayoutNotEqualBlockReward = errors.New(
"total payout not equal to blockreward",
)
// NoReward ..
NoReward = big.NewInt(0)
// EmptyPayout ..
EmptyPayout = noReward{}
targetStakedPercentage = numeric.MustNewDecFromStr("0.35")
dynamicAdjust = numeric.MustNewDecFromStr("0.4")
)
type ignoreMissing struct{}
@ -146,7 +125,7 @@ func WhatPercentStakedNow(
return nil, nil, err
}
dole := numeric.NewDecFromBigInt(soFarDoledOut)
dole := numeric.NewDecFromBigIntWithPrec(soFarDoledOut, 18)
for _, electedValAdr := range active.StakedValidators().Addrs {
wrapper, err := beaconchain.ReadValidatorInformation(electedValAdr)
@ -154,10 +133,10 @@ func WhatPercentStakedNow(
return nil, nil, err
}
stakedNow = stakedNow.Add(
numeric.NewDecFromBigInt(wrapper.TotalDelegation()),
numeric.NewDecFromBigIntWithPrec(wrapper.TotalDelegation(), 18),
)
}
percentage := stakedNow.Quo(totalTokens.Mul(
percentage := stakedNow.Quo(stakingReward.TotalInitialTokens.Mul(
reward.PercentageForTimeStamp(timestamp),
).Add(dole))
utils.Logger().Info().

@ -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…
Cancel
Save