Skip to content

Commit 33a664c

Browse files
committed
feat(power15): FIP0081 migration for power actor
Closes: #313
1 parent 10bf966 commit 33a664c

File tree

6 files changed

+263
-2
lines changed

6 files changed

+263
-2
lines changed

builtin/v15/migration/power.go

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package migration
2+
3+
import (
4+
"context"
5+
6+
power14 "github.com/filecoin-project/go-state-types/builtin/v14/power"
7+
power15 "github.com/filecoin-project/go-state-types/builtin/v15/power"
8+
smoothing15 "github.com/filecoin-project/go-state-types/builtin/v15/util/smoothing"
9+
"github.com/filecoin-project/go-state-types/migration"
10+
"github.com/ipfs/go-cid"
11+
cbor "github.com/ipfs/go-ipld-cbor"
12+
"golang.org/x/xerrors"
13+
)
14+
15+
type powerMigrator struct {
16+
rampStartEpoch int64
17+
rampDurationEpochs uint64
18+
outCodeCID cid.Cid
19+
}
20+
21+
func newPowerMigrator(rampStartEpoch int64, rampDurationEpochs uint64, outCode cid.Cid) (*powerMigrator, error) {
22+
return &powerMigrator{
23+
rampStartEpoch: rampStartEpoch,
24+
rampDurationEpochs: rampDurationEpochs,
25+
outCodeCID: outCode,
26+
}, nil
27+
}
28+
29+
func (p powerMigrator) MigratedCodeCID() cid.Cid { return p.outCodeCID }
30+
31+
func (p powerMigrator) Deferred() bool { return false }
32+
33+
func (p powerMigrator) MigrateState(ctx context.Context, store cbor.IpldStore, in migration.ActorMigrationInput) (*migration.ActorMigrationResult, error) {
34+
var inState power14.State
35+
if err := store.Get(ctx, in.Head, &inState); err != nil {
36+
return nil, xerrors.Errorf("failed to load miner state for %s: %w", in.Address, err)
37+
}
38+
39+
outState := power15.State{
40+
TotalRawBytePower: inState.TotalRawBytePower,
41+
TotalBytesCommitted: inState.TotalBytesCommitted,
42+
TotalQualityAdjPower: inState.TotalQualityAdjPower,
43+
TotalQABytesCommitted: inState.TotalQABytesCommitted,
44+
TotalPledgeCollateral: inState.TotalPledgeCollateral,
45+
ThisEpochRawBytePower: inState.ThisEpochRawBytePower,
46+
ThisEpochQualityAdjPower: inState.ThisEpochQualityAdjPower,
47+
ThisEpochPledgeCollateral: inState.ThisEpochPledgeCollateral,
48+
ThisEpochQAPowerSmoothed: smoothing15.FilterEstimate{
49+
PositionEstimate: inState.ThisEpochQAPowerSmoothed.PositionEstimate,
50+
VelocityEstimate: inState.ThisEpochQAPowerSmoothed.VelocityEstimate,
51+
},
52+
MinerCount: inState.MinerCount,
53+
MinerAboveMinPowerCount: inState.MinerAboveMinPowerCount,
54+
RampStartEpoch: p.rampStartEpoch,
55+
RampDurationEpochs: p.rampDurationEpochs,
56+
CronEventQueue: inState.CronEventQueue,
57+
FirstCronEpoch: inState.FirstCronEpoch,
58+
Claims: inState.Claims,
59+
ProofValidationBatch: inState.ProofValidationBatch,
60+
}
61+
62+
newHead, err := store.Put(ctx, &outState)
63+
if err != nil {
64+
return nil, xerrors.Errorf("failed to put new state: %w", err)
65+
}
66+
67+
return &migration.ActorMigrationResult{
68+
NewCodeCID: p.MigratedCodeCID(),
69+
NewHead: newHead,
70+
}, nil
71+
}

builtin/v15/migration/power_test.go

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
package migration
2+
3+
import (
4+
"context"
5+
"testing"
6+
"time"
7+
8+
"github.com/filecoin-project/go-address"
9+
"github.com/filecoin-project/go-state-types/abi"
10+
"github.com/filecoin-project/go-state-types/big"
11+
power14 "github.com/filecoin-project/go-state-types/builtin/v14/power"
12+
smoothing14 "github.com/filecoin-project/go-state-types/builtin/v14/util/smoothing"
13+
power15 "github.com/filecoin-project/go-state-types/builtin/v15/power"
14+
"github.com/filecoin-project/go-state-types/migration"
15+
"github.com/ipfs/go-cid"
16+
cbor "github.com/ipfs/go-ipld-cbor"
17+
"github.com/stretchr/testify/require"
18+
)
19+
20+
func TestPowerMigration(t *testing.T) {
21+
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
22+
defer cancel()
23+
24+
req := require.New(t)
25+
26+
cst := cbor.NewMemCborStore()
27+
28+
pvb := cid.MustParse("bafy2bzacaf2a")
29+
state14 := power14.State{
30+
TotalRawBytePower: abi.NewStoragePower(101),
31+
TotalBytesCommitted: abi.NewStoragePower(102),
32+
TotalQualityAdjPower: abi.NewStoragePower(103),
33+
TotalQABytesCommitted: abi.NewStoragePower(104),
34+
TotalPledgeCollateral: abi.NewTokenAmount(105),
35+
ThisEpochRawBytePower: abi.NewStoragePower(106),
36+
ThisEpochQualityAdjPower: abi.NewStoragePower(107),
37+
ThisEpochPledgeCollateral: abi.NewTokenAmount(108),
38+
ThisEpochQAPowerSmoothed: smoothing14.NewEstimate(big.NewInt(109), big.NewInt(110)),
39+
MinerCount: 111,
40+
MinerAboveMinPowerCount: 112,
41+
CronEventQueue: cid.MustParse("bafy2bzacafza"),
42+
FirstCronEpoch: 113,
43+
Claims: cid.MustParse("bafy2bzacafzq"),
44+
ProofValidationBatch: &pvb,
45+
}
46+
47+
state14Cid, err := cst.Put(ctx, &state14)
48+
req.NoError(err)
49+
50+
var rampStartEpoch int64 = 101
51+
var rampDurationEpochs uint64 = 202
52+
53+
migrator, err := newPowerMigrator(rampStartEpoch, rampDurationEpochs, cid.MustParse("bafy2bzaca4aaaaaaaaaqk"))
54+
req.NoError(err)
55+
56+
result, err := migrator.MigrateState(ctx, cst, migration.ActorMigrationInput{
57+
Address: address.TestAddress,
58+
Head: state14Cid,
59+
Cache: nil,
60+
})
61+
req.NoError(err)
62+
req.Equal(cid.MustParse("bafy2bzaca4aaaaaaaaaqk"), result.NewCodeCID)
63+
req.NotEqual(cid.Undef, result.NewHead)
64+
65+
newState := power15.State{}
66+
req.NoError(cst.Get(ctx, result.NewHead, &newState))
67+
68+
req.Equal(state14.TotalRawBytePower, newState.TotalRawBytePower)
69+
req.Equal(state14.TotalBytesCommitted, newState.TotalBytesCommitted)
70+
req.Equal(state14.TotalQualityAdjPower, newState.TotalQualityAdjPower)
71+
req.Equal(state14.TotalQABytesCommitted, newState.TotalQABytesCommitted)
72+
req.Equal(state14.TotalPledgeCollateral, newState.TotalPledgeCollateral)
73+
req.Equal(state14.ThisEpochRawBytePower, newState.ThisEpochRawBytePower)
74+
req.Equal(state14.ThisEpochQualityAdjPower, newState.ThisEpochQualityAdjPower)
75+
req.Equal(state14.ThisEpochPledgeCollateral, newState.ThisEpochPledgeCollateral)
76+
req.Equal(state14.ThisEpochQAPowerSmoothed.PositionEstimate, newState.ThisEpochQAPowerSmoothed.PositionEstimate)
77+
req.Equal(state14.ThisEpochQAPowerSmoothed.VelocityEstimate, newState.ThisEpochQAPowerSmoothed.VelocityEstimate)
78+
req.Equal(state14.MinerCount, newState.MinerCount)
79+
req.Equal(state14.MinerAboveMinPowerCount, newState.MinerAboveMinPowerCount)
80+
req.Equal(state14.CronEventQueue, newState.CronEventQueue)
81+
req.Equal(state14.FirstCronEpoch, newState.FirstCronEpoch)
82+
req.Equal(state14.Claims, newState.Claims)
83+
req.Equal(state14.ProofValidationBatch, newState.ProofValidationBatch)
84+
85+
// Ramp parameters
86+
req.Equal(rampStartEpoch, newState.RampStartEpoch)
87+
req.Equal(rampDurationEpochs, newState.RampDurationEpochs)
88+
}

builtin/v15/migration/top.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,16 @@ func MigrateStateTree(ctx context.Context, store cbor.IpldStore, newManifestCID
2424
return cid.Undef, xerrors.Errorf("invalid migration config with %d workers", cfg.MaxWorkers)
2525
}
2626

27+
if cfg.PowerRampStartEpoch == 0 {
28+
return cid.Undef, xerrors.Errorf("PowerRampStartEpoch must be set")
29+
}
30+
if cfg.PowerRampStartEpoch < 0 {
31+
return cid.Undef, xerrors.Errorf("PowerRampStartEpoch must be non-negative")
32+
}
33+
if cfg.PowerRampDurationEpochs == 0 {
34+
return cid.Undef, xerrors.Errorf("PowerRampDurationEpochs must be set")
35+
}
36+
2737
adtStore := adt15.WrapStore(ctx, store)
2838

2939
// Load input and output state trees
@@ -67,7 +77,13 @@ func MigrateStateTree(ctx context.Context, store cbor.IpldStore, newManifestCID
6777
// Set of prior version code CIDs for actors to defer during iteration, for explicit migration afterwards.
6878
deferredCodeIDs := make(map[cid.Cid]struct{})
6979

80+
power14Cid := cid.Undef
81+
7082
for _, oldEntry := range oldManifestData.Entries {
83+
if oldEntry.Name == manifest.PowerKey {
84+
power14Cid = oldEntry.Code
85+
}
86+
7187
newCodeCID, ok := newManifest.Get(oldEntry.Name)
7288
if !ok {
7389
return cid.Undef, xerrors.Errorf("code cid for %s actor not found in new manifest", oldEntry.Name)
@@ -86,6 +102,21 @@ func MigrateStateTree(ctx context.Context, store cbor.IpldStore, newManifestCID
86102

87103
migrations[systemActor.Code] = systemActorMigrator{OutCodeCID: newSystemCodeCID, ManifestData: newManifest.Data}
88104

105+
// The Power Actor
106+
if power14Cid == cid.Undef {
107+
return cid.Undef, xerrors.Errorf("code cid for power actor not found in old manifest")
108+
}
109+
power15Cid, ok := newManifest.Get(manifest.PowerKey)
110+
if !ok {
111+
return cid.Undef, xerrors.Errorf("code cid for power actor not found in new manifest")
112+
}
113+
114+
pm, err := newPowerMigrator(cfg.PowerRampStartEpoch, cfg.PowerRampDurationEpochs, power15Cid)
115+
if err != nil {
116+
return cid.Undef, xerrors.Errorf("failed to create miner migrator: %w", err)
117+
}
118+
migrations[power14Cid] = migration.CachedMigration(cache, *pm)
119+
89120
if len(migrations)+len(deferredCodeIDs) != len(oldManifestData.Entries) {
90121
return cid.Undef, xerrors.Errorf("incomplete migration specification with %d code CIDs, need %d", len(migrations)+len(deferredCodeIDs), len(oldManifestData.Entries))
91122
}

builtin/v15/power/cbor_gen.go

Lines changed: 58 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

builtin/v15/power/power_state.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,15 @@ type State struct {
6161
// Number of miners having proven the minimum consensus power.
6262
MinerAboveMinPowerCount int64
6363

64+
// FIP0081 changed pledge calculations, moving from ruleset A to ruleset B.
65+
// This change is spread over several epochs to avoid sharp jumps in pledge
66+
// amounts. At `RampStartEpoch`, we use the old ruleset. At
67+
// `RampStartEpoch + RampDurationEpochs`, we use 70% old rules + 30%
68+
// new rules. See FIP0081 for more details.
69+
RampStartEpoch int64
70+
// Number of epochs over which the new pledge calculation is ramped up.
71+
RampDurationEpochs uint64
72+
6473
// A queue of events to be triggered by cron, indexed by epoch.
6574
CronEventQueue cid.Cid // Multimap, (HAMT[ChainEpoch]AMT[CronEvent])
6675

@@ -99,6 +108,8 @@ func ConstructState(store adt.Store) (*State, error) {
99108
Claims: emptyClaimsMapCid,
100109
MinerCount: 0,
101110
MinerAboveMinPowerCount: 0,
111+
RampStartEpoch: 0,
112+
RampDurationEpochs: 0,
102113
}, nil
103114
}
104115

migration/util.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,10 @@ type Config struct {
8787
ProgressLogPeriod time.Duration
8888
// The epoch at which the upgrade will run.
8989
UpgradeEpoch abi.ChainEpoch
90+
91+
// FIP-0081 constants for the power actor state for pledge calculations.
92+
PowerRampStartEpoch int64 // Epoch at which the new pledge calculation starts.
93+
PowerRampDurationEpochs uint64 // Number of epochs over which the new pledge calculation is ramped up.
9094
}
9195

9296
type Logger interface {

0 commit comments

Comments
 (0)