Skip to content

Commit

Permalink
feat(power15): FIP0081 migration for power actor
Browse files Browse the repository at this point in the history
Closes: #313
  • Loading branch information
rvagg committed Oct 3, 2024
1 parent 10bf966 commit 33a664c
Show file tree
Hide file tree
Showing 6 changed files with 263 additions and 2 deletions.
71 changes: 71 additions & 0 deletions builtin/v15/migration/power.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package migration

import (
"context"

power14 "github.com/filecoin-project/go-state-types/builtin/v14/power"
power15 "github.com/filecoin-project/go-state-types/builtin/v15/power"
smoothing15 "github.com/filecoin-project/go-state-types/builtin/v15/util/smoothing"
"github.com/filecoin-project/go-state-types/migration"
"github.com/ipfs/go-cid"
cbor "github.com/ipfs/go-ipld-cbor"
"golang.org/x/xerrors"
)

type powerMigrator struct {
rampStartEpoch int64
rampDurationEpochs uint64
outCodeCID cid.Cid
}

func newPowerMigrator(rampStartEpoch int64, rampDurationEpochs uint64, outCode cid.Cid) (*powerMigrator, error) {
return &powerMigrator{
rampStartEpoch: rampStartEpoch,
rampDurationEpochs: rampDurationEpochs,
outCodeCID: outCode,
}, nil
}

func (p powerMigrator) MigratedCodeCID() cid.Cid { return p.outCodeCID }

func (p powerMigrator) Deferred() bool { return false }

func (p powerMigrator) MigrateState(ctx context.Context, store cbor.IpldStore, in migration.ActorMigrationInput) (*migration.ActorMigrationResult, error) {
var inState power14.State
if err := store.Get(ctx, in.Head, &inState); err != nil {
return nil, xerrors.Errorf("failed to load miner state for %s: %w", in.Address, err)
}

outState := power15.State{
TotalRawBytePower: inState.TotalRawBytePower,
TotalBytesCommitted: inState.TotalBytesCommitted,
TotalQualityAdjPower: inState.TotalQualityAdjPower,
TotalQABytesCommitted: inState.TotalQABytesCommitted,
TotalPledgeCollateral: inState.TotalPledgeCollateral,
ThisEpochRawBytePower: inState.ThisEpochRawBytePower,
ThisEpochQualityAdjPower: inState.ThisEpochQualityAdjPower,
ThisEpochPledgeCollateral: inState.ThisEpochPledgeCollateral,
ThisEpochQAPowerSmoothed: smoothing15.FilterEstimate{
PositionEstimate: inState.ThisEpochQAPowerSmoothed.PositionEstimate,
VelocityEstimate: inState.ThisEpochQAPowerSmoothed.VelocityEstimate,
},
MinerCount: inState.MinerCount,
MinerAboveMinPowerCount: inState.MinerAboveMinPowerCount,
RampStartEpoch: p.rampStartEpoch,
RampDurationEpochs: p.rampDurationEpochs,
CronEventQueue: inState.CronEventQueue,
FirstCronEpoch: inState.FirstCronEpoch,
Claims: inState.Claims,
ProofValidationBatch: inState.ProofValidationBatch,
}

newHead, err := store.Put(ctx, &outState)
if err != nil {
return nil, xerrors.Errorf("failed to put new state: %w", err)
}

return &migration.ActorMigrationResult{
NewCodeCID: p.MigratedCodeCID(),
NewHead: newHead,
}, nil
}
88 changes: 88 additions & 0 deletions builtin/v15/migration/power_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package migration

import (
"context"
"testing"
"time"

"github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/go-state-types/big"
power14 "github.com/filecoin-project/go-state-types/builtin/v14/power"
smoothing14 "github.com/filecoin-project/go-state-types/builtin/v14/util/smoothing"
power15 "github.com/filecoin-project/go-state-types/builtin/v15/power"
"github.com/filecoin-project/go-state-types/migration"
"github.com/ipfs/go-cid"
cbor "github.com/ipfs/go-ipld-cbor"
"github.com/stretchr/testify/require"
)

func TestPowerMigration(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
defer cancel()

req := require.New(t)

cst := cbor.NewMemCborStore()

pvb := cid.MustParse("bafy2bzacaf2a")
state14 := power14.State{
TotalRawBytePower: abi.NewStoragePower(101),
TotalBytesCommitted: abi.NewStoragePower(102),
TotalQualityAdjPower: abi.NewStoragePower(103),
TotalQABytesCommitted: abi.NewStoragePower(104),
TotalPledgeCollateral: abi.NewTokenAmount(105),
ThisEpochRawBytePower: abi.NewStoragePower(106),
ThisEpochQualityAdjPower: abi.NewStoragePower(107),
ThisEpochPledgeCollateral: abi.NewTokenAmount(108),
ThisEpochQAPowerSmoothed: smoothing14.NewEstimate(big.NewInt(109), big.NewInt(110)),
MinerCount: 111,
MinerAboveMinPowerCount: 112,
CronEventQueue: cid.MustParse("bafy2bzacafza"),
FirstCronEpoch: 113,
Claims: cid.MustParse("bafy2bzacafzq"),
ProofValidationBatch: &pvb,
}

state14Cid, err := cst.Put(ctx, &state14)
req.NoError(err)

var rampStartEpoch int64 = 101
var rampDurationEpochs uint64 = 202

migrator, err := newPowerMigrator(rampStartEpoch, rampDurationEpochs, cid.MustParse("bafy2bzaca4aaaaaaaaaqk"))
req.NoError(err)

result, err := migrator.MigrateState(ctx, cst, migration.ActorMigrationInput{
Address: address.TestAddress,
Head: state14Cid,
Cache: nil,
})
req.NoError(err)
req.Equal(cid.MustParse("bafy2bzaca4aaaaaaaaaqk"), result.NewCodeCID)
req.NotEqual(cid.Undef, result.NewHead)

newState := power15.State{}
req.NoError(cst.Get(ctx, result.NewHead, &newState))

req.Equal(state14.TotalRawBytePower, newState.TotalRawBytePower)
req.Equal(state14.TotalBytesCommitted, newState.TotalBytesCommitted)
req.Equal(state14.TotalQualityAdjPower, newState.TotalQualityAdjPower)
req.Equal(state14.TotalQABytesCommitted, newState.TotalQABytesCommitted)
req.Equal(state14.TotalPledgeCollateral, newState.TotalPledgeCollateral)
req.Equal(state14.ThisEpochRawBytePower, newState.ThisEpochRawBytePower)
req.Equal(state14.ThisEpochQualityAdjPower, newState.ThisEpochQualityAdjPower)
req.Equal(state14.ThisEpochPledgeCollateral, newState.ThisEpochPledgeCollateral)
req.Equal(state14.ThisEpochQAPowerSmoothed.PositionEstimate, newState.ThisEpochQAPowerSmoothed.PositionEstimate)
req.Equal(state14.ThisEpochQAPowerSmoothed.VelocityEstimate, newState.ThisEpochQAPowerSmoothed.VelocityEstimate)
req.Equal(state14.MinerCount, newState.MinerCount)
req.Equal(state14.MinerAboveMinPowerCount, newState.MinerAboveMinPowerCount)
req.Equal(state14.CronEventQueue, newState.CronEventQueue)
req.Equal(state14.FirstCronEpoch, newState.FirstCronEpoch)
req.Equal(state14.Claims, newState.Claims)
req.Equal(state14.ProofValidationBatch, newState.ProofValidationBatch)

// Ramp parameters
req.Equal(rampStartEpoch, newState.RampStartEpoch)
req.Equal(rampDurationEpochs, newState.RampDurationEpochs)
}
31 changes: 31 additions & 0 deletions builtin/v15/migration/top.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,16 @@ func MigrateStateTree(ctx context.Context, store cbor.IpldStore, newManifestCID
return cid.Undef, xerrors.Errorf("invalid migration config with %d workers", cfg.MaxWorkers)
}

if cfg.PowerRampStartEpoch == 0 {
return cid.Undef, xerrors.Errorf("PowerRampStartEpoch must be set")
}
if cfg.PowerRampStartEpoch < 0 {
return cid.Undef, xerrors.Errorf("PowerRampStartEpoch must be non-negative")
}
if cfg.PowerRampDurationEpochs == 0 {
return cid.Undef, xerrors.Errorf("PowerRampDurationEpochs must be set")
}

adtStore := adt15.WrapStore(ctx, store)

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

power14Cid := cid.Undef

for _, oldEntry := range oldManifestData.Entries {
if oldEntry.Name == manifest.PowerKey {
power14Cid = oldEntry.Code
}

newCodeCID, ok := newManifest.Get(oldEntry.Name)
if !ok {
return cid.Undef, xerrors.Errorf("code cid for %s actor not found in new manifest", oldEntry.Name)
Expand All @@ -86,6 +102,21 @@ func MigrateStateTree(ctx context.Context, store cbor.IpldStore, newManifestCID

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

// The Power Actor
if power14Cid == cid.Undef {
return cid.Undef, xerrors.Errorf("code cid for power actor not found in old manifest")
}
power15Cid, ok := newManifest.Get(manifest.PowerKey)
if !ok {
return cid.Undef, xerrors.Errorf("code cid for power actor not found in new manifest")
}

pm, err := newPowerMigrator(cfg.PowerRampStartEpoch, cfg.PowerRampDurationEpochs, power15Cid)
if err != nil {
return cid.Undef, xerrors.Errorf("failed to create miner migrator: %w", err)
}
migrations[power14Cid] = migration.CachedMigration(cache, *pm)

if len(migrations)+len(deferredCodeIDs) != len(oldManifestData.Entries) {
return cid.Undef, xerrors.Errorf("incomplete migration specification with %d code CIDs, need %d", len(migrations)+len(deferredCodeIDs), len(oldManifestData.Entries))
}
Expand Down
60 changes: 58 additions & 2 deletions builtin/v15/power/cbor_gen.go

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

11 changes: 11 additions & 0 deletions builtin/v15/power/power_state.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,15 @@ type State struct {
// Number of miners having proven the minimum consensus power.
MinerAboveMinPowerCount int64

// FIP0081 changed pledge calculations, moving from ruleset A to ruleset B.
// This change is spread over several epochs to avoid sharp jumps in pledge
// amounts. At `RampStartEpoch`, we use the old ruleset. At
// `RampStartEpoch + RampDurationEpochs`, we use 70% old rules + 30%
// new rules. See FIP0081 for more details.
RampStartEpoch int64
// Number of epochs over which the new pledge calculation is ramped up.
RampDurationEpochs uint64

// A queue of events to be triggered by cron, indexed by epoch.
CronEventQueue cid.Cid // Multimap, (HAMT[ChainEpoch]AMT[CronEvent])

Expand Down Expand Up @@ -99,6 +108,8 @@ func ConstructState(store adt.Store) (*State, error) {
Claims: emptyClaimsMapCid,
MinerCount: 0,
MinerAboveMinPowerCount: 0,
RampStartEpoch: 0,
RampDurationEpochs: 0,
}, nil
}

Expand Down
4 changes: 4 additions & 0 deletions migration/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,10 @@ type Config struct {
ProgressLogPeriod time.Duration
// The epoch at which the upgrade will run.
UpgradeEpoch abi.ChainEpoch

// FIP-0081 constants for the power actor state for pledge calculations.
PowerRampStartEpoch int64 // Epoch at which the new pledge calculation starts.
PowerRampDurationEpochs uint64 // Number of epochs over which the new pledge calculation is ramped up.
}

type Logger interface {
Expand Down

0 comments on commit 33a664c

Please sign in to comment.