Skip to content

Commit ca7f9a4

Browse files
committed
feat(power15): update InitialPledgeForPower for FIP-0081
1 parent 33a664c commit ca7f9a4

File tree

2 files changed

+226
-8
lines changed

2 files changed

+226
-8
lines changed

builtin/v15/miner/monies.go

Lines changed: 48 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ var InitialPledgeLockTarget = builtin.BigFrac{
3030
Denominator: big.NewInt(10),
3131
}
3232

33+
const GammaFixedPointFactor = 1000 // 3 decimal places
34+
3335
// The projected block reward a sector would earn over some period.
3436
// Also known as "BR(t)".
3537
// BR(t) = ProjectedRewardFraction(t) * SectorQualityAdjustedPower
@@ -64,8 +66,8 @@ func PreCommitDepositForPower(rewardEstimate, networkQAPowerEstimate smoothing.F
6466
return ExpectedRewardForPowerClampedAtAttoFIL(rewardEstimate, networkQAPowerEstimate, qaSectorPower, PreCommitDepositProjectionPeriod)
6567
}
6668

67-
// Computes the pledge requirement for committing new quality-adjusted power to the network, given the current
68-
// network total and baseline power, per-epoch reward, and circulating token supply.
69+
// Computes the pledge requirement for committing new quality-adjusted power to the network, given
70+
// the current network total and baseline power, per-epoch reward, and circulating token supply.
6971
// The pledge comprises two parts:
7072
// - storage pledge, aka IP base: a multiple of the reward expected to be earned by newly-committed power
7173
// - consensus pledge, aka additional IP: a pro-rata fraction of the circulating money supply
@@ -75,21 +77,59 @@ func PreCommitDepositForPower(rewardEstimate, networkQAPowerEstimate smoothing.F
7577
// AdditionalIP(t) = LockTarget(t)*PledgeShare(t)
7678
// LockTarget = (LockTargetFactorNum / LockTargetFactorDenom) * FILCirculatingSupply(t)
7779
// PledgeShare(t) = sectorQAPower / max(BaselinePower(t), NetworkQAPower(t))
78-
func InitialPledgeForPower(qaPower, baselinePower abi.StoragePower, rewardEstimate, networkQAPowerEstimate smoothing.FilterEstimate, circulatingSupply abi.TokenAmount) abi.TokenAmount {
80+
func InitialPledgeForPower(
81+
qaPower,
82+
baselinePower abi.StoragePower,
83+
rewardEstimate,
84+
networkQAPowerEstimate smoothing.FilterEstimate,
85+
circulatingSupply abi.TokenAmount,
86+
epochsSinceRampStart int64,
87+
rampDurationEpochs uint64,
88+
) abi.TokenAmount {
7989
ipBase := ExpectedRewardForPowerClampedAtAttoFIL(rewardEstimate, networkQAPowerEstimate, qaPower, InitialPledgeProjectionPeriod)
8090

8191
lockTargetNum := big.Mul(InitialPledgeLockTarget.Numerator, circulatingSupply)
8292
lockTargetDenom := InitialPledgeLockTarget.Denominator
8393
pledgeShareNum := qaPower
8494
networkQAPower := smoothing.Estimate(&networkQAPowerEstimate)
85-
pledgeShareDenom := big.Max(big.Max(networkQAPower, baselinePower), qaPower) // use qaPower in case others are 0
95+
96+
// Once FIP-0081 has fully activated, additional pledge will be 70% baseline
97+
// pledge + 30% simple pledge.
98+
const fip0081ActivationPermille = 300
99+
// Gamma/GAMMA_FIXED_POINT_FACTOR is the share of pledge coming from the
100+
// baseline formulation, with 1-(gamma/GAMMA_FIXED_POINT_FACTOR) coming from
101+
// simple pledge.
102+
// gamma = 1000 - 300 * (epochs_since_ramp_start / ramp_duration_epochs).max(0).min(1)
103+
var skew uint64
104+
switch {
105+
case epochsSinceRampStart < 0:
106+
// No skew before ramp start
107+
skew = 0
108+
case rampDurationEpochs == 0 || epochsSinceRampStart >= int64(rampDurationEpochs):
109+
// 100% skew after ramp end
110+
skew = fip0081ActivationPermille
111+
case epochsSinceRampStart > 0:
112+
skew = (uint64(epochsSinceRampStart*fip0081ActivationPermille) / rampDurationEpochs)
113+
}
114+
gamma := big.NewInt(int64(GammaFixedPointFactor - skew))
115+
86116
additionalIPNum := big.Mul(lockTargetNum, pledgeShareNum)
87-
additionalIPDenom := big.Mul(lockTargetDenom, pledgeShareDenom)
88-
additionalIP := big.Div(additionalIPNum, additionalIPDenom)
117+
118+
pledgeShareDenomBaseline := big.Max(big.Max(networkQAPower, baselinePower), qaPower)
119+
pledgeShareDenomSimple := big.Max(networkQAPower, qaPower)
120+
121+
additionalIPDenomBaseline := big.Mul(pledgeShareDenomBaseline, lockTargetDenom)
122+
additionalIPBaseline := big.Div(big.Mul(gamma, additionalIPNum), big.Mul(additionalIPDenomBaseline, big.NewInt(GammaFixedPointFactor)))
123+
additionalIPDenomSimple := big.Mul(pledgeShareDenomSimple, lockTargetDenom)
124+
additionalIPSimple := big.Div(big.Mul(big.Sub(big.NewInt(GammaFixedPointFactor), gamma), additionalIPNum), big.Mul(additionalIPDenomSimple, big.NewInt(GammaFixedPointFactor)))
125+
126+
// convex combination of simple and baseline pledge
127+
additionalIP := big.Add(additionalIPBaseline, additionalIPSimple)
89128

90129
nominalPledge := big.Add(ipBase, additionalIP)
91-
spaceRacePledgeCap := big.Mul(InitialPledgeMaxPerByte, qaPower)
92-
return big.Min(nominalPledge, spaceRacePledgeCap)
130+
pledgeCap := big.Mul(InitialPledgeMaxPerByte, qaPower)
131+
132+
return big.Min(nominalPledge, pledgeCap)
93133
}
94134

95135
var EstimatedSingleProveCommitGasUsage = big.NewInt(49299973) // PARAM_SPEC

builtin/v15/miner/monies_test.go

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
package miner_test
2+
3+
import (
4+
"testing"
5+
6+
abi "github.com/filecoin-project/go-state-types/abi"
7+
"github.com/filecoin-project/go-state-types/big"
8+
"github.com/filecoin-project/go-state-types/builtin/v15/miner"
9+
"github.com/filecoin-project/go-state-types/builtin/v15/util/smoothing"
10+
)
11+
12+
// See filecoin-project/builtin-actors actors/miner/tests/fip0081_initial_pledge.rs
13+
func TestInitialPledgeForPowerFip0081(t *testing.T) {
14+
filPrecision := big.NewInt(1_000_000_000_000_000_000)
15+
16+
epochTargetReward := abi.TokenAmount(big.Zero())
17+
qaSectorPower := abi.StoragePower(big.NewInt(1 << 36))
18+
networkQAPower := abi.StoragePower(big.NewInt(1 << 10))
19+
powerRateOfChange := abi.StoragePower(big.NewInt(1 << 10))
20+
rewardEstimate := smoothing.FilterEstimate{
21+
PositionEstimate: epochTargetReward,
22+
VelocityEstimate: big.Zero(),
23+
}
24+
powerEstimate := smoothing.FilterEstimate{
25+
PositionEstimate: networkQAPower,
26+
VelocityEstimate: powerRateOfChange,
27+
}
28+
circulatingSupply := abi.TokenAmount(filPrecision)
29+
30+
testCases := []struct {
31+
name string
32+
qaSectorPower abi.StoragePower
33+
rewardEstimate smoothing.FilterEstimate
34+
powerEstimate smoothing.FilterEstimate
35+
circulatingSupply abi.TokenAmount
36+
epochsSinceRampStart int64
37+
rampDurationEpochs uint64
38+
expectedInitialPledge abi.TokenAmount
39+
}{
40+
{
41+
name: "pre-ramp where 'baseline power' dominates (negative epochsSinceRampStart)",
42+
qaSectorPower: qaSectorPower,
43+
rewardEstimate: rewardEstimate,
44+
powerEstimate: powerEstimate,
45+
circulatingSupply: circulatingSupply,
46+
epochsSinceRampStart: -100,
47+
rampDurationEpochs: 100,
48+
expectedInitialPledge: big.Add(big.Div(big.Mul(big.NewInt(1500), filPrecision), big.NewInt(10000)), big.NewInt(1)),
49+
},
50+
{
51+
name: "zero ramp duration",
52+
qaSectorPower: qaSectorPower,
53+
rewardEstimate: rewardEstimate,
54+
powerEstimate: powerEstimate,
55+
circulatingSupply: circulatingSupply,
56+
epochsSinceRampStart: 0,
57+
rampDurationEpochs: 0,
58+
expectedInitialPledge: big.Add(big.Div(big.Mul(big.NewInt(1950), filPrecision), big.NewInt(10000)), big.NewInt(1)),
59+
},
60+
{
61+
name: "zero ramp duration (10 epochs since)",
62+
qaSectorPower: qaSectorPower,
63+
rewardEstimate: rewardEstimate,
64+
powerEstimate: powerEstimate,
65+
circulatingSupply: circulatingSupply,
66+
epochsSinceRampStart: 10,
67+
rampDurationEpochs: 0,
68+
expectedInitialPledge: big.Add(big.Div(big.Mul(big.NewInt(1950), filPrecision), big.NewInt(10000)), big.NewInt(1)),
69+
},
70+
{
71+
name: "pre-ramp where 'baseline power' dominates",
72+
qaSectorPower: qaSectorPower,
73+
rewardEstimate: rewardEstimate,
74+
powerEstimate: powerEstimate,
75+
circulatingSupply: circulatingSupply,
76+
epochsSinceRampStart: 0,
77+
rampDurationEpochs: 100,
78+
expectedInitialPledge: big.Add(big.Div(big.Mul(big.NewInt(1500), filPrecision), big.NewInt(10000)), big.NewInt(1)),
79+
},
80+
{
81+
name: "on-ramp where 'baseline power' is at 85% and `simple power` is at 15%",
82+
qaSectorPower: qaSectorPower,
83+
rewardEstimate: rewardEstimate,
84+
powerEstimate: powerEstimate,
85+
circulatingSupply: circulatingSupply,
86+
epochsSinceRampStart: 50,
87+
rampDurationEpochs: 100,
88+
expectedInitialPledge: big.Add(big.Div(big.Mul(big.NewInt(1725), filPrecision), big.NewInt(10000)), big.NewInt(1)),
89+
},
90+
{
91+
name: "after-ramp where 'baseline power' is at 70% and `simple power` is at 30%",
92+
qaSectorPower: qaSectorPower,
93+
rewardEstimate: rewardEstimate,
94+
powerEstimate: powerEstimate,
95+
circulatingSupply: circulatingSupply,
96+
epochsSinceRampStart: 150,
97+
rampDurationEpochs: 100,
98+
expectedInitialPledge: big.Add(big.Div(big.Mul(big.NewInt(1950), filPrecision), big.NewInt(10000)), big.NewInt(1)),
99+
},
100+
{
101+
name: "on-ramp where 'baseline power' has reduced effect (97%)",
102+
qaSectorPower: qaSectorPower,
103+
rewardEstimate: rewardEstimate,
104+
powerEstimate: powerEstimate,
105+
circulatingSupply: circulatingSupply,
106+
epochsSinceRampStart: 10,
107+
rampDurationEpochs: 100,
108+
expectedInitialPledge: big.Add(big.Div(big.Mul(big.NewInt(1545), filPrecision), big.NewInt(10000)), big.NewInt(1)),
109+
},
110+
{
111+
name: "on-ramp, first epoch, pledge should be 97% 'baseline' + 3% simple",
112+
qaSectorPower: qaSectorPower,
113+
rewardEstimate: rewardEstimate,
114+
powerEstimate: powerEstimate,
115+
circulatingSupply: circulatingSupply,
116+
epochsSinceRampStart: 1,
117+
rampDurationEpochs: 10,
118+
expectedInitialPledge: big.Add(big.Div(big.Mul(big.NewInt(1545), filPrecision), big.NewInt(10000)), big.NewInt(1)),
119+
},
120+
{
121+
name: "validate pledges 1 epoch before and after ramp start: before ramp start",
122+
qaSectorPower: qaSectorPower,
123+
rewardEstimate: rewardEstimate,
124+
powerEstimate: powerEstimate,
125+
circulatingSupply: circulatingSupply,
126+
epochsSinceRampStart: -1,
127+
rampDurationEpochs: 10,
128+
expectedInitialPledge: big.Add(big.Div(big.Mul(big.NewInt(1500), filPrecision), big.NewInt(10000)), big.NewInt(1)),
129+
},
130+
{
131+
name: "validate pledges 1 epoch before and after ramp start: at ramp start",
132+
qaSectorPower: qaSectorPower,
133+
rewardEstimate: rewardEstimate,
134+
powerEstimate: powerEstimate,
135+
circulatingSupply: circulatingSupply,
136+
epochsSinceRampStart: 0,
137+
rampDurationEpochs: 10,
138+
expectedInitialPledge: big.Add(big.Div(big.Mul(big.NewInt(1500), filPrecision), big.NewInt(10000)), big.NewInt(1)),
139+
},
140+
{
141+
name: "validate pledges 1 epoch before and after ramp start: on ramp start",
142+
qaSectorPower: qaSectorPower,
143+
rewardEstimate: rewardEstimate,
144+
powerEstimate: powerEstimate,
145+
circulatingSupply: circulatingSupply,
146+
epochsSinceRampStart: 1,
147+
rampDurationEpochs: 10,
148+
expectedInitialPledge: big.Add(big.Div(big.Mul(big.NewInt(1545), filPrecision), big.NewInt(10000)), big.NewInt(1)),
149+
},
150+
{
151+
name: "post-ramp where 'baseline power' has reduced effect (70%)",
152+
qaSectorPower: qaSectorPower,
153+
rewardEstimate: rewardEstimate,
154+
powerEstimate: powerEstimate,
155+
circulatingSupply: circulatingSupply,
156+
epochsSinceRampStart: 500,
157+
rampDurationEpochs: 100,
158+
expectedInitialPledge: big.Add(big.Div(big.Mul(big.NewInt(1950), filPrecision), big.NewInt(10000)), big.NewInt(1)),
159+
},
160+
}
161+
162+
for _, tc := range testCases {
163+
t.Run(tc.name, func(t *testing.T) {
164+
initialPledge := miner.InitialPledgeForPower(
165+
tc.qaSectorPower,
166+
abi.StoragePower(big.NewInt(1<<37)),
167+
tc.rewardEstimate,
168+
tc.powerEstimate,
169+
tc.circulatingSupply,
170+
tc.epochsSinceRampStart,
171+
tc.rampDurationEpochs,
172+
)
173+
if !initialPledge.Equals(tc.expectedInitialPledge) {
174+
t.Fatalf("expected initial pledge %v, got %v", tc.expectedInitialPledge, initialPledge)
175+
}
176+
})
177+
}
178+
}

0 commit comments

Comments
 (0)