Fix APR's nill header issue; Compute APR based on last epoch

pull/2586/head
Rongjian Lan 5 years ago
parent a0dad48abf
commit 4e150779d3
  1. 3
      consensus/votepower/roster.go
  2. 6
      core/blockchain.go
  3. 2
      core/offchain.go
  4. 1
      shard/committee/assignment.go
  5. 204
      staking/apr/compute.go

@ -204,8 +204,11 @@ func Compute(subComm *shard.Committee, epoch *big.Int) (*Roster, error) {
ourPercentage = ourPercentage.Add(member.OverallPercent)
}
// TODO: make sure external user's BLS key can be same as harmony's bls keys
if _, ok := roster.Voters[staked[i].BlsPublicKey]; !ok {
roster.Voters[staked[i].BlsPublicKey] = &member
}
}
// NOTE Enforce voting power sums to one,
// give diff (expect tiny amt) to last staked voter

@ -2266,6 +2266,7 @@ func (bc *BlockChain) ReadValidatorStats(
// UpdateValidatorVotingPower writes the voting power for the committees
func (bc *BlockChain) UpdateValidatorVotingPower(
batch rawdb.DatabaseWriter,
block *types.Block,
newEpochSuperCommittee, currentEpochSuperCommittee *shard.State,
// NOTE Do not update this state, only read from
state *state.DB,
@ -2287,7 +2288,6 @@ func (bc *BlockChain) UpdateValidatorVotingPower(
}
rosters[i] = roster
}
blkPerEpoch := shard.Schedule.BlocksPerEpoch()
networkWide := votepower.AggregateRosters(rosters)
for key, value := range networkWide {
stats, err := rawdb.ReadValidatorStats(bc.db, key)
@ -2304,9 +2304,9 @@ func (bc *BlockChain) UpdateValidatorVotingPower(
if err != nil {
return err
}
aprComputed, err := apr.ComputeForValidator(
bc, newEpochSuperCommittee.Epoch,
state, wrapper, blkPerEpoch,
bc, block, wrapper,
)
if err == nil && aprComputed != nil {
a := *aprComputed

@ -133,7 +133,7 @@ func (bc *BlockChain) CommitOffChainData(
header.ShardState(),
); err == nil {
if err := bc.UpdateValidatorVotingPower(
batch, shardState, currentSuperCommittee, state,
batch, block, shardState, currentSuperCommittee, state,
); err != nil {
utils.Logger().
Err(err).

@ -292,6 +292,7 @@ func eposStakedCommittee(
}
}
// TODO: make sure external validator BLS key are also not duplicate to Harmony's keys
completedEPoSRound, err := NewEPoSRound(stakerReader)
if err != nil {

@ -1,11 +1,12 @@
package apr
import (
"github.com/harmony-one/harmony/core/types"
"github.com/harmony-one/harmony/shard"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/harmony-one/harmony/block"
"github.com/harmony-one/harmony/core/state"
"github.com/harmony-one/harmony/internal/params"
"github.com/harmony-one/harmony/internal/utils"
"github.com/harmony-one/harmony/numeric"
@ -15,6 +16,7 @@ import (
// Reader ..
type Reader interface {
GetHeaderByNumber(number uint64) *block.Header
Config() *params.ChainConfig
GetHeaderByHash(hash common.Hash) *block.Header
// GetHeader retrieves a block header from the database by hash and number.
@ -27,7 +29,7 @@ type Reader interface {
}
const (
secondsInYear = int64(31_557_600)
secondsInYear = int64(31557600)
)
var (
@ -35,14 +37,13 @@ var (
)
func expectedRewardPerYear(
oneEpochAgo, twoEpochAgo *block.Header,
oneSnapshotAgo, twoSnapshotAgo *staking.ValidatorWrapper,
blocksPerEpoch uint64,
now, oneEpochAgo *block.Header,
curValidator, snapshotLastEpoch *staking.ValidatorWrapper,
) (*big.Int, error) {
oneTAgo, twoTAgo := oneEpochAgo.Time(), twoEpochAgo.Time()
timeNow, oneTAgo := now.Time(), oneEpochAgo.Time()
diffTime, diffReward :=
new(big.Int).Sub(twoTAgo, oneTAgo),
new(big.Int).Sub(twoSnapshotAgo.BlockReward, oneSnapshotAgo.BlockReward)
new(big.Int).Sub(timeNow, oneTAgo),
new(big.Int).Sub(curValidator.BlockReward, snapshotLastEpoch.BlockReward)
// impossibility but keep sane
if diffTime.Sign() == -1 {
@ -55,152 +56,81 @@ func expectedRewardPerYear(
// TODO some more sanity checks of some sort?
expectedValue := new(big.Int).Div(diffReward, diffTime)
expectedPerYear := new(big.Int).Mul(expectedValue, oneYear)
utils.Logger().Info().
utils.Logger().Info().Interface("now", curValidator).Interface("before", snapshotLastEpoch).
Uint64("diff-reward", diffReward.Uint64()).
Uint64("diff-time", diffTime.Uint64()).
Uint64("expected-value", expectedValue.Uint64()).
Uint64("expected-per-year", expectedPerYear.Uint64()).
Interface("expected-value", expectedValue).
Interface("expected-per-year", expectedPerYear).
Msg("expected reward per year computed")
return expectedPerYear, nil
}
func pastTwoEpochHeaders(
var (
zero = numeric.ZeroDec()
)
// ComputeForValidator ..
func ComputeForValidator(
bc Reader,
) (*block.Header, *block.Header, error) {
current := bc.CurrentHeader()
epochNow := current.Epoch()
oneEpochAgo, twoEpochAgo :=
new(big.Int).Sub(epochNow, common.Big1),
new(big.Int).Sub(epochNow, common.Big2)
bottomOut := new(big.Int).Add(
bc.Config().StakingEpoch,
common.Big3,
)
block *types.Block,
validatorNow *staking.ValidatorWrapper,
) (*numeric.Dec, error) {
oneEpochAgo, zero :=
new(big.Int).Sub(block.Epoch(), common.Big1),
numeric.ZeroDec()
var oneAgoHeader, twoAgoHeader **block.Header
utils.Logger().Info().
Uint64("now", block.Epoch().Uint64()).
Uint64("one-epoch-ago", oneEpochAgo.Uint64()).
Msg("apr - begin compute for validator ")
for e1, e2 := false, false; ; {
current = bc.GetHeader(current.ParentHash(), current.Number().Uint64()-1)
oneSnapshotAgo, err := bc.ReadValidatorSnapshotAtEpoch(
oneEpochAgo,
validatorNow.Address,
)
if current == nil {
return nil, nil, errors.New("could not go up parent")
if err != nil {
return &zero, nil
}
if current.Epoch().Cmp(bottomOut) == 0 {
if twoAgoHeader == nil || oneAgoHeader == nil {
return nil, nil, errors.New(
"could not find headers for apr computation",
blockNumAtOneEpochAgo := shard.Schedule.EpochLastBlock(oneEpochAgo.Uint64())
headerOneEpochAgo := bc.GetHeaderByNumber(blockNumAtOneEpochAgo)
if block.Header() == nil || headerOneEpochAgo == nil || err != nil {
utils.Logger().Debug().
Msgf("apr compute headers epochs ago %+v %+v %+v",
oneEpochAgo,
blockNumAtOneEpochAgo,
headerOneEpochAgo,
)
}
return &zero, nil
}
switch {
// haven't found either epoch yet
case !e1 && !e2:
if current.Epoch().Cmp(oneEpochAgo) == 0 {
e1 = true
oneAgoHeader = &current
continue
}
case e1 && !e2:
if current.Epoch().Cmp(twoEpochAgo) == 0 {
e2 = true
twoAgoHeader = &current
break
}
}
}
utils.Logger().Info().
RawJSON("current-epoch-header", []byte(bc.CurrentHeader().String())).
RawJSON("one-epoch-ago-header", []byte(headerOneEpochAgo.String())).
Msg("headers used for apr computation")
return *oneAgoHeader, *twoAgoHeader, nil
}
estimatedRewardPerYear, err := expectedRewardPerYear(
block.Header(), headerOneEpochAgo,
validatorNow, oneSnapshotAgo,
)
var (
zero = numeric.ZeroDec()
)
if err != nil {
return nil, err
}
// ComputeForValidator ..
func ComputeForValidator(
bc Reader,
now *big.Int,
state *state.DB,
validatorNow *staking.ValidatorWrapper,
blocksPerEpoch uint64,
) (*numeric.Dec, error) {
if estimatedRewardPerYear.Cmp(common.Big0) == 0 {
return &zero, nil
// twoEpochAgo, oneEpochAgo, zero :=
// new(big.Int).Sub(now, common.Big2),
// new(big.Int).Sub(now, common.Big1),
// numeric.ZeroDec()
// utils.Logger().Info().
// Uint64("now", now.Uint64()).
// Uint64("two-epoch-ago", twoEpochAgo.Uint64()).
// Uint64("one-epoch-ago", oneEpochAgo.Uint64()).
// Msg("apr - begin compute for validator ")
// twoSnapshotAgo, err := bc.ReadValidatorSnapshotAtEpoch(
// twoEpochAgo,
// validatorNow.Address,
// )
// if err != nil {
// return &zero, nil
// }
// oneSnapshotAgo, err := bc.ReadValidatorSnapshotAtEpoch(
// oneEpochAgo,
// validatorNow.Address,
// )
// if err != nil {
// return &zero, nil
// }
// blockNumAtTwoEpochAgo, blockNumAtOneEpochAgo :=
// shard.Schedule.EpochLastBlock(twoEpochAgo.Uint64()),
// shard.Schedule.EpochLastBlock(oneEpochAgo.Uint64())
// headerOneEpochAgo, headerTwoEpochAgo, err := pastTwoEpochHeaders(bc)
// // TODO Figure out why this is happening
// if headerOneEpochAgo == nil || headerTwoEpochAgo == nil || err != nil {
// utils.Logger().Debug().
// Msgf("apr compute headers epochs ago %+v %+v %+v %+v %+v %+v",
// twoEpochAgo, oneEpochAgo,
// blockNumAtTwoEpochAgo, blockNumAtOneEpochAgo,
// headerOneEpochAgo, headerTwoEpochAgo,
// )
// return &zero, nil
// }
// utils.Logger().Info().
// RawJSON("current-epoch-header", []byte(bc.CurrentHeader().String())).
// RawJSON("one-epoch-ago-header", []byte(headerOneEpochAgo.String())).
// RawJSON("two-epoch-ago-header", []byte(headerTwoEpochAgo.String())).
// Msg("headers used for apr computation")
// estimatedRewardPerYear, err := expectedRewardPerYear(
// headerOneEpochAgo, headerTwoEpochAgo,
// oneSnapshotAgo, twoSnapshotAgo,
// blocksPerEpoch,
// )
// if err != nil {
// return nil, err
// }
// if estimatedRewardPerYear.Cmp(common.Big0) == 0 {
// return &zero, nil
// }
// total := numeric.NewDecFromBigInt(validatorNow.TotalDelegation())
// if total.IsZero() {
// return nil, errors.New("zero total delegation will cause div by zero")
// }
// result := numeric.NewDecFromBigInt(estimatedRewardPerYear).Quo(
// total,
// )
// return &result, nil
}
total := numeric.NewDecFromBigInt(validatorNow.TotalDelegation())
if total.IsZero() {
return nil, errors.New("zero total delegation will cause div by zero")
}
result := numeric.NewDecFromBigInt(estimatedRewardPerYear).Quo(
total,
)
return &result, nil
}

Loading…
Cancel
Save