Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FIP-0081 migration #314

Merged
merged 4 commits into from
Oct 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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)
}
53 changes: 50 additions & 3 deletions builtin/v15/migration/top.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,39 @@ import (
"golang.org/x/xerrors"
)

// MigrateStateTree Migrates the filecoin state tree starting from the global state tree and upgrading all actor state.
// MigrateStateTree migrates the Filecoin state tree starting from the global state tree and upgrading all actor states.
// The store must support concurrent writes (even if the configured worker count is 1).
func MigrateStateTree(ctx context.Context, store cbor.IpldStore, newManifestCID cid.Cid, actorsRootIn cid.Cid, priorEpoch abi.ChainEpoch, cfg migration.Config, log migration.Logger, cache migration.MigrationCache) (cid.Cid, error) {
//
// FIP-0081 constants for the power actor state for pledge calculations that apply only to this migration:
//
// - powerRampStartEpoch: Epoch at which the new pledge calculation starts.
// - powerRampDurationEpochs: Number of epochs over which the new pledge calculation is ramped up.
func MigrateStateTree(
ctx context.Context,
store cbor.IpldStore,
newManifestCID cid.Cid,
actorsRootIn cid.Cid,
priorEpoch abi.ChainEpoch,
powerRampStartEpoch int64,
powerRampDurationEpochs uint64,
cfg migration.Config,
log migration.Logger,
cache migration.MigrationCache,
) (cid.Cid, error) {
if cfg.MaxWorkers <= 0 {
return cid.Undef, xerrors.Errorf("invalid migration config with %d workers", cfg.MaxWorkers)
}

if powerRampStartEpoch == 0 {
return cid.Undef, xerrors.Errorf("powerRampStartEpoch must be non-zero")
}
if powerRampStartEpoch < 0 {
return cid.Undef, xerrors.Errorf("powerRampStartEpoch must be non-negative")
}
if powerRampDurationEpochs == 0 {
return cid.Undef, xerrors.Errorf("powerRampDurationEpochs must be non-zero")
}

adtStore := adt15.WrapStore(ctx, store)

// Load input and output state trees
Expand Down Expand Up @@ -67,12 +93,18 @@ 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)
rvagg marked this conversation as resolved.
Show resolved Hide resolved
}
migrations[oldEntry.Code] = migration.CachedMigration(cache, migration.CodeMigrator{OutCodeCID: newCodeCID})
migrations[oldEntry.Code] = migration.CodeMigrator{OutCodeCID: newCodeCID}
}

// migrations that migrate both code and state, override entries in `migrations`
Expand All @@ -86,6 +118,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(powerRampStartEpoch, powerRampDurationEpochs, power15Cid)
if err != nil {
return cid.Undef, xerrors.Errorf("failed to create miner migrator: %w", err)
}
migrations[power14Cid] = *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
57 changes: 49 additions & 8 deletions builtin/v15/miner/monies.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ var InitialPledgeLockTarget = builtin.BigFrac{
Denominator: big.NewInt(10),
}

const GammaFixedPointFactor = 1000 // 3 decimal places

// The projected block reward a sector would earn over some period.
// Also known as "BR(t)".
// BR(t) = ProjectedRewardFraction(t) * SectorQualityAdjustedPower
Expand Down Expand Up @@ -64,8 +66,9 @@ func PreCommitDepositForPower(rewardEstimate, networkQAPowerEstimate smoothing.F
return ExpectedRewardForPowerClampedAtAttoFIL(rewardEstimate, networkQAPowerEstimate, qaSectorPower, PreCommitDepositProjectionPeriod)
}

// Computes the pledge requirement for committing new quality-adjusted power to the network, given the current
// network total and baseline power, per-epoch reward, and circulating token supply.
// InitialPledgeForPower computes the pledge requirement for committing new quality-adjusted power
// to the network, given the current network total and baseline power, per-epoch reward, and
// circulating token supply.
// The pledge comprises two parts:
// - storage pledge, aka IP base: a multiple of the reward expected to be earned by newly-committed power
// - consensus pledge, aka additional IP: a pro-rata fraction of the circulating money supply
Expand All @@ -75,21 +78,59 @@ func PreCommitDepositForPower(rewardEstimate, networkQAPowerEstimate smoothing.F
// AdditionalIP(t) = LockTarget(t)*PledgeShare(t)
// LockTarget = (LockTargetFactorNum / LockTargetFactorDenom) * FILCirculatingSupply(t)
// PledgeShare(t) = sectorQAPower / max(BaselinePower(t), NetworkQAPower(t))
func InitialPledgeForPower(qaPower, baselinePower abi.StoragePower, rewardEstimate, networkQAPowerEstimate smoothing.FilterEstimate, circulatingSupply abi.TokenAmount) abi.TokenAmount {
func InitialPledgeForPower(
qaPower,
baselinePower abi.StoragePower,
rewardEstimate,
networkQAPowerEstimate smoothing.FilterEstimate,
circulatingSupply abi.TokenAmount,
epochsSinceRampStart int64,
rampDurationEpochs uint64,
) abi.TokenAmount {
ipBase := ExpectedRewardForPowerClampedAtAttoFIL(rewardEstimate, networkQAPowerEstimate, qaPower, InitialPledgeProjectionPeriod)

lockTargetNum := big.Mul(InitialPledgeLockTarget.Numerator, circulatingSupply)
lockTargetDenom := InitialPledgeLockTarget.Denominator
pledgeShareNum := qaPower
networkQAPower := smoothing.Estimate(&networkQAPowerEstimate)
pledgeShareDenom := big.Max(big.Max(networkQAPower, baselinePower), qaPower) // use qaPower in case others are 0

// Once FIP-0081 has fully activated, additional pledge will be 70% baseline
// pledge + 30% simple pledge.
const fip0081ActivationPermille = 300
// Gamma/GAMMA_FIXED_POINT_FACTOR is the share of pledge coming from the
// baseline formulation, with 1-(gamma/GAMMA_FIXED_POINT_FACTOR) coming from
// simple pledge.
// gamma = 1000 - 300 * (epochs_since_ramp_start / ramp_duration_epochs).max(0).min(1)
var skew uint64
switch {
case epochsSinceRampStart < 0:
// No skew before ramp start
skew = 0
case rampDurationEpochs == 0 || epochsSinceRampStart >= int64(rampDurationEpochs):
// 100% skew after ramp end
skew = fip0081ActivationPermille
case epochsSinceRampStart > 0:
skew = (uint64(epochsSinceRampStart*fip0081ActivationPermille) / rampDurationEpochs)
}
gamma := big.NewInt(int64(GammaFixedPointFactor - skew))

additionalIPNum := big.Mul(lockTargetNum, pledgeShareNum)
additionalIPDenom := big.Mul(lockTargetDenom, pledgeShareDenom)
additionalIP := big.Div(additionalIPNum, additionalIPDenom)

pledgeShareDenomBaseline := big.Max(big.Max(networkQAPower, baselinePower), qaPower)
pledgeShareDenomSimple := big.Max(networkQAPower, qaPower)

additionalIPDenomBaseline := big.Mul(pledgeShareDenomBaseline, lockTargetDenom)
additionalIPBaseline := big.Div(big.Mul(gamma, additionalIPNum), big.Mul(additionalIPDenomBaseline, big.NewInt(GammaFixedPointFactor)))
additionalIPDenomSimple := big.Mul(pledgeShareDenomSimple, lockTargetDenom)
additionalIPSimple := big.Div(big.Mul(big.Sub(big.NewInt(GammaFixedPointFactor), gamma), additionalIPNum), big.Mul(additionalIPDenomSimple, big.NewInt(GammaFixedPointFactor)))

// convex combination of simple and baseline pledge
additionalIP := big.Add(additionalIPBaseline, additionalIPSimple)

nominalPledge := big.Add(ipBase, additionalIP)
spaceRacePledgeCap := big.Mul(InitialPledgeMaxPerByte, qaPower)
return big.Min(nominalPledge, spaceRacePledgeCap)
pledgeCap := big.Mul(InitialPledgeMaxPerByte, qaPower)

return big.Min(nominalPledge, pledgeCap)
}

var EstimatedSingleProveCommitGasUsage = big.NewInt(49299973) // PARAM_SPEC
Expand Down
Loading
Loading