Skip to content

Commit

Permalink
Make token supply related fns more accurate (#3509)
Browse files Browse the repository at this point in the history
* [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>
  • Loading branch information
Daniel-VDM authored Jan 29, 2021
1 parent 9ab1038 commit 94bf414
Show file tree
Hide file tree
Showing 15 changed files with 328 additions and 102 deletions.
17 changes: 12 additions & 5 deletions api/service/explorer/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,18 @@ 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.
const (
explorerPortDifference = 4000
defaultPageSize = "1000"
maxAddresses = 100000
totalSupply = 12600000000
)

// HTTPError is an HTTP error.
Expand Down Expand Up @@ -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)
Expand All @@ -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)
Expand Down
6 changes: 6 additions & 0 deletions consensus/reward/schedule.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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

Expand Down
1 change: 1 addition & 0 deletions core/blockchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
21 changes: 21 additions & 0 deletions core/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)
Expand Down Expand Up @@ -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
}
10 changes: 5 additions & 5 deletions core/state_transition.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)
Expand Down Expand Up @@ -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)
Expand Down
4 changes: 2 additions & 2 deletions hmy/blockchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)

Expand Down Expand Up @@ -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
Expand Down
21 changes: 11 additions & 10 deletions internal/chain/reward.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)

Expand Down Expand Up @@ -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
}
}

Expand Down Expand Up @@ -460,17 +461,17 @@ 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)
last = cur
}
}

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(
Expand Down
46 changes: 46 additions & 0 deletions internal/chain/supply.go
Original file line number Diff line number Diff line change
@@ -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
}
14 changes: 14 additions & 0 deletions node/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -996,13 +997,26 @@ func New(
}()
}

// update reward values now that node is ready
node.updateInitialRewardValues()

// init metrics
initMetrics()
nodeStringCounterVec.WithLabelValues("version", nodeconfig.GetVersion()).Inc()

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
Expand Down
16 changes: 1 addition & 15 deletions rosetta/services/block_side_effect.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -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)
}
19 changes: 6 additions & 13 deletions rpc/blockchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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.
Expand Down Expand Up @@ -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 ..
Expand Down
Loading

0 comments on commit 94bf414

Please sign in to comment.