Skip to content
This repository has been archived by the owner on Jun 6, 2023. It is now read-only.

Commit

Permalink
Baseline migration (#1215)
Browse files Browse the repository at this point in the history
- Migrate reward actor state
- Cleanup: math.Precision -> math.Precision128
- Cleanup: move ln from smoothing to math
- Cleanup: move expneg from reward to math
- Hook up reward actor with migrated power state
- Move SimpleTotal, BaselineTotal on chain
- Compute new baseline total in migration

Co-authored-by: ZenGround0 <[email protected]>
  • Loading branch information
ZenGround0 and ZenGround0 authored Oct 6, 2020
1 parent 3b6db5b commit 385dfad
Show file tree
Hide file tree
Showing 17 changed files with 482 additions and 201 deletions.
4 changes: 2 additions & 2 deletions actors/builtin/miner/monies.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ var InitialPledgeProjectionPeriod = abi.ChainEpoch(InitialPledgeFactor) * builti
// Cap on initial pledge requirement for sectors.
// The target is 1 FIL (10**18 attoFIL) per 32GiB.
// This does not divide evenly, so the result is fractionally smaller.
var InitialPledgeMaxPerByte = big.Div(big.NewInt(1e18), big.NewInt(32 << 30))
var InitialPledgeMaxPerByte = big.Div(big.NewInt(1e18), big.NewInt(32<<30))

// Multiplier of share of circulating money supply for consensus pledge required to commit a sector.
// This pledge is lost if a sector is terminated before its full committed lifetime.
Expand Down Expand Up @@ -67,7 +67,7 @@ func ExpectedRewardForPower(rewardEstimate, networkQAPowerEstimate smoothing.Fil
}
expectedRewardForProvingPeriod := smoothing.ExtrapolatedCumSumOfRatio(projectionDuration, 0, rewardEstimate, networkQAPowerEstimate)
br128 := big.Mul(qaSectorPower, expectedRewardForProvingPeriod) // Q.0 * Q.128 => Q.128
br := big.Rsh(br128, math.Precision)
br := big.Rsh(br128, math.Precision128)
return big.Max(br, big.Zero()) // negative BR is clamped at 0
}

Expand Down
32 changes: 30 additions & 2 deletions actors/builtin/reward/cbor_gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

50 changes: 26 additions & 24 deletions actors/builtin/reward/reward_logic.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,35 +33,37 @@ var BaselineInitialValue = big.NewInt(2_888_888_880_000_000_000) // Q.0
// Initialize baseline power for epoch -1 so that baseline power at epoch 0 is
// BaselineInitialValue.
func InitBaselinePower() abi.StoragePower {
baselineInitialValue256 := big.Lsh(BaselineInitialValue, 2*math.Precision) // Q.0 => Q.256
baselineInitialValue256 := big.Lsh(BaselineInitialValue, 2*math.Precision128) // Q.0 => Q.256
baselineAtMinusOne := big.Div(baselineInitialValue256, BaselineExponent) // Q.256 / Q.128 => Q.128
return big.Rsh(baselineAtMinusOne, math.Precision) // Q.128 => Q.0
return big.Rsh(baselineAtMinusOne, math.Precision128) // Q.128 => Q.0
}

// Compute BaselinePower(t) from BaselinePower(t-1) with an additional multiplication
// of the base exponent.
func BaselinePowerFromPrev(prevEpochBaselinePower abi.StoragePower) abi.StoragePower {
thisEpochBaselinePower := big.Mul(prevEpochBaselinePower, BaselineExponent) // Q.0 * Q.128 => Q.128
return big.Rsh(thisEpochBaselinePower, math.Precision) // Q.128 => Q.0
return big.Rsh(thisEpochBaselinePower, math.Precision128) // Q.128 => Q.0
}

// These numbers are placeholders, but should be in units of attoFIL, 10^-18 FIL
var SimpleTotal = big.Mul(big.NewInt(330e6), big.NewInt(1e18)) // 330M
var BaselineTotal = big.Mul(big.NewInt(770e6), big.NewInt(1e18)) // 770M
// These numbers are estimates of the onchain constants. They are good for initializing state in
// devnets and testing but will not match the on chain values exactly which depend on storage onboarding
// and upgrade epoch history. They are in units of attoFIL, 10^-18 FIL
var DefaultSimpleTotal = big.Mul(big.NewInt(330e6), big.NewInt(1e18)) // 330M
var DefaultBaselineTotal = big.Mul(big.NewInt(770e6), big.NewInt(1e18)) // 770M

// Computes RewardTheta which is is precise fractional value of effectiveNetworkTime.
// The effectiveNetworkTime is defined by CumsumBaselinePower(theta) == CumsumRealizedPower
// As baseline power is defined over integers and the RewardTheta is required to be fractional,
// we perform linear interpolation between CumsumBaseline(⌊theta⌋) and CumsumBaseline(⌈theta⌉).
// The effectiveNetworkTime argument is ceiling of theta.
// The result is a fractional effectiveNetworkTime (theta) in Q.128 format.
func computeRTheta(effectiveNetworkTime abi.ChainEpoch, baselinePowerAtEffectiveNetworkTime, cumsumRealized, cumsumBaseline big.Int) big.Int {
func ComputeRTheta(effectiveNetworkTime abi.ChainEpoch, baselinePowerAtEffectiveNetworkTime, cumsumRealized, cumsumBaseline big.Int) big.Int {
var rewardTheta big.Int
if effectiveNetworkTime != 0 {
rewardTheta = big.NewInt(int64(effectiveNetworkTime)) // Q.0
rewardTheta = big.Lsh(rewardTheta, math.Precision) // Q.0 => Q.128
rewardTheta = big.Lsh(rewardTheta, math.Precision128) // Q.0 => Q.128
diff := big.Sub(cumsumBaseline, cumsumRealized)
diff = big.Lsh(diff, math.Precision) // Q.0 => Q.128
diff = big.Lsh(diff, math.Precision128) // Q.0 => Q.128
diff = big.Div(diff, baselinePowerAtEffectiveNetworkTime) // Q.128 / Q.0 => Q.128
rewardTheta = big.Sub(rewardTheta, diff) // Q.128
} else {
Expand All @@ -75,42 +77,42 @@ var (
// lambda = ln(2) / (6 * epochsInYear)
// for Q.128: int(lambda * 2^128)
// Calculation here: https://www.wolframalpha.com/input/?i=IntegerPart%5BLog%5B2%5D+%2F+%286+*+%281+year+%2F+30+seconds%29%29+*+2%5E128%5D
lambda = big.MustFromString("37396271439864487274534522888786")
Lambda = big.MustFromString("37396271439864487274534522888786")
// expLamSubOne = e^lambda - 1
// for Q.128: int(expLamSubOne * 2^128)
// Calculation here: https://www.wolframalpha.com/input/?i=IntegerPart%5B%5BExp%5BLog%5B2%5D+%2F+%286+*+%281+year+%2F+30+seconds%29%29%5D+-+1%5D+*+2%5E128%5D
expLamSubOne = big.MustFromString("37396273494747879394193016954629")
ExpLamSubOne = big.MustFromString("37396273494747879394193016954629")
)

// Computes a reward for all expected leaders when effective network time changes from prevTheta to currTheta
// Inputs are in Q.128 format
func computeReward(epoch abi.ChainEpoch, prevTheta, currTheta big.Int) abi.TokenAmount {
simpleReward := big.Mul(SimpleTotal, expLamSubOne) //Q.0 * Q.128 => Q.128
epochLam := big.Mul(big.NewInt(int64(epoch)), lambda) // Q.0 * Q.128 => Q.128
func computeReward(epoch abi.ChainEpoch, prevTheta, currTheta, simpleTotal, baselineTotal big.Int) abi.TokenAmount {
simpleReward := big.Mul(simpleTotal, ExpLamSubOne) //Q.0 * Q.128 => Q.128
epochLam := big.Mul(big.NewInt(int64(epoch)), Lambda) // Q.0 * Q.128 => Q.128

simpleReward = big.Mul(simpleReward, big.NewFromGo(expneg(epochLam.Int))) // Q.128 * Q.128 => Q.256
simpleReward = big.Rsh(simpleReward, math.Precision) // Q.256 >> 128 => Q.128
simpleReward = big.Mul(simpleReward, big.NewFromGo(math.ExpNeg(epochLam.Int))) // Q.128 * Q.128 => Q.256
simpleReward = big.Rsh(simpleReward, math.Precision128) // Q.256 >> 128 => Q.128

baselineReward := big.Sub(computeBaselineSupply(currTheta), computeBaselineSupply(prevTheta)) // Q.128
baselineReward := big.Sub(computeBaselineSupply(currTheta, baselineTotal), computeBaselineSupply(prevTheta, baselineTotal)) // Q.128

reward := big.Add(simpleReward, baselineReward) // Q.128

return big.Rsh(reward, math.Precision) // Q.128 => Q.0
return big.Rsh(reward, math.Precision128) // Q.128 => Q.0
}

// Computes baseline supply based on theta in Q.128 format.
// Return is in Q.128 format
func computeBaselineSupply(theta big.Int) big.Int {
thetaLam := big.Mul(theta, lambda) // Q.128 * Q.128 => Q.256
thetaLam = big.Rsh(thetaLam, math.Precision) // Q.256 >> 128 => Q.128
func computeBaselineSupply(theta, baselineTotal big.Int) big.Int {
thetaLam := big.Mul(theta, Lambda) // Q.128 * Q.128 => Q.256
thetaLam = big.Rsh(thetaLam, math.Precision128) // Q.256 >> 128 => Q.128

eTL := big.NewFromGo(expneg(thetaLam.Int)) // Q.128
eTL := big.NewFromGo(math.ExpNeg(thetaLam.Int)) // Q.128

one := big.NewInt(1)
one = big.Lsh(one, math.Precision) // Q.0 => Q.128
one = big.Lsh(one, math.Precision128) // Q.0 => Q.128
oneSub := big.Sub(one, eTL) // Q.128

return big.Mul(BaselineTotal, oneSub) // Q.0 * Q.128 => Q.128
return big.Mul(baselineTotal, oneSub) // Q.0 * Q.128 => Q.128
}

// SlowConvenientBaselineForEpoch computes baseline power for use in epoch t
Expand Down
18 changes: 9 additions & 9 deletions actors/builtin/reward/reward_logic_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import (

func q128ToF(x big.Int) float64 {
q128 := new(gbig.Int).SetInt64(1)
q128 = q128.Lsh(q128, math.Precision)
q128 = q128.Lsh(q128, math.Precision128)
res, _ := new(gbig.Rat).SetFrac(x.Int, q128).Float64()
return res
}
Expand All @@ -26,37 +26,37 @@ func TestComputeRTeta(t *testing.T) {
return big.Mul(big.NewInt(int64(epoch+1)), big.NewInt(2048))
}

assert.Equal(t, 0.5, q128ToF(computeRTheta(1, baselinePowerAt(1), big.NewInt(2048+2*2048*0.5), big.NewInt(2048+2*2048))))
assert.Equal(t, 0.25, q128ToF(computeRTheta(1, baselinePowerAt(1), big.NewInt(2048+2*2048*0.25), big.NewInt(2048+2*2048))))
assert.Equal(t, 0.5, q128ToF(ComputeRTheta(1, baselinePowerAt(1), big.NewInt(2048+2*2048*0.5), big.NewInt(2048+2*2048))))
assert.Equal(t, 0.25, q128ToF(ComputeRTheta(1, baselinePowerAt(1), big.NewInt(2048+2*2048*0.25), big.NewInt(2048+2*2048))))

cumsum15 := big.NewInt(0)
for i := abi.ChainEpoch(0); i < 16; i++ {
cumsum15 = big.Add(cumsum15, baselinePowerAt(i))
}
assert.Equal(t, 15.25, q128ToF(computeRTheta(16,
assert.Equal(t, 15.25, q128ToF(ComputeRTheta(16,
baselinePowerAt(16),
big.Add(cumsum15, big.Div(baselinePowerAt(16), big.NewInt(4))),
big.Add(cumsum15, baselinePowerAt(16)))))
}

func TestBaselineReward(t *testing.T) {
step := gbig.NewInt(5000)
step = step.Lsh(step, math.Precision)
step = step.Lsh(step, math.Precision128)
step = step.Sub(step, gbig.NewInt(77777777777)) // offset from full integers

delta := gbig.NewInt(1)
delta = delta.Lsh(delta, math.Precision)
delta = delta.Lsh(delta, math.Precision128)
delta = delta.Sub(delta, gbig.NewInt(33333333333)) // offset from full integers

prevTheta := new(gbig.Int)
theta := new(gbig.Int).Set(delta)

b := &bytes.Buffer{}
b.WriteString("t0, t1, y\n")
simple := computeReward(0, big.Zero(), big.Zero())
simple := computeReward(0, big.Zero(), big.Zero(), DefaultSimpleTotal, DefaultBaselineTotal)

for i := 0; i < 512; i++ {
reward := computeReward(0, big.NewFromGo(prevTheta), big.NewFromGo(theta))
reward := computeReward(0, big.NewFromGo(prevTheta), big.NewFromGo(theta), DefaultSimpleTotal, DefaultBaselineTotal)
reward = big.Sub(reward, simple)
fmt.Fprintf(b, "%s,%s,%s\n", prevTheta, theta, reward.Int)
prevTheta = prevTheta.Add(prevTheta, step)
Expand All @@ -71,7 +71,7 @@ func TestSimpleReward(t *testing.T) {
b.WriteString("x, y\n")
for i := int64(0); i < 512; i++ {
x := i * 5000
reward := computeReward(abi.ChainEpoch(x), big.Zero(), big.Zero())
reward := computeReward(abi.ChainEpoch(x), big.Zero(), big.Zero(), DefaultSimpleTotal, DefaultBaselineTotal)
fmt.Fprintf(b, "%d,%s\n", x, reward.Int)
}

Expand Down
17 changes: 14 additions & 3 deletions actors/builtin/reward/reward_state.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,14 @@ type State struct {

// TotalStoragePowerReward tracks the total FIL awarded to block miners
TotalStoragePowerReward abi.TokenAmount

// Simple and Baseline totals are constants used for computing rewards.
// They are on chain because of a historical fix resetting baseline value
// in a way that depended on the history leading immediately up to the
// migration fixing the value. These values can be moved from state back
// into a code constant in a subsequent upgrade.
SimpleTotal abi.TokenAmount
BaselineTotal abi.TokenAmount
}

func ConstructState(currRealizedPower abi.StoragePower) *State {
Expand All @@ -69,6 +77,9 @@ func ConstructState(currRealizedPower abi.StoragePower) *State {

ThisEpochRewardSmoothed: smoothing.NewEstimate(InitialRewardPositionEstimate, InitialRewardVelocityEstimate),
TotalStoragePowerReward: big.Zero(),

SimpleTotal: DefaultSimpleTotal,
BaselineTotal: DefaultBaselineTotal,
}

st.updateToNextEpochWithReward(currRealizedPower)
Expand All @@ -94,11 +105,11 @@ func (st *State) updateToNextEpoch(currRealizedPower abi.StoragePower) {
// Takes in a current realized power for a reward epoch and computes
// and updates reward state to track reward for the next epoch
func (st *State) updateToNextEpochWithReward(currRealizedPower abi.StoragePower) {
prevRewardTheta := computeRTheta(st.EffectiveNetworkTime, st.EffectiveBaselinePower, st.CumsumRealized, st.CumsumBaseline)
prevRewardTheta := ComputeRTheta(st.EffectiveNetworkTime, st.EffectiveBaselinePower, st.CumsumRealized, st.CumsumBaseline)
st.updateToNextEpoch(currRealizedPower)
currRewardTheta := computeRTheta(st.EffectiveNetworkTime, st.EffectiveBaselinePower, st.CumsumRealized, st.CumsumBaseline)
currRewardTheta := ComputeRTheta(st.EffectiveNetworkTime, st.EffectiveBaselinePower, st.CumsumRealized, st.CumsumBaseline)

st.ThisEpochReward = computeReward(st.Epoch, prevRewardTheta, currRewardTheta)
st.ThisEpochReward = computeReward(st.Epoch, prevRewardTheta, currRewardTheta, st.SimpleTotal, st.BaselineTotal)
}

func (st *State) updateSmoothedEstimates(delta abi.ChainEpoch) {
Expand Down
Loading

0 comments on commit 385dfad

Please sign in to comment.