Skip to content

feat(miner): externalise SectorOnChainInfo from AMT #338

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

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
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
7 changes: 7 additions & 0 deletions builtin/v15/miner/miner_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,13 @@ func (sa Sectors) Get(sectorNumber abi.SectorNumber) (info *SectorOnChainInfo, f
return &res, true, nil
}

func (sa Sectors) ForEach(cb func(abi.SectorNumber, *SectorOnChainInfo) error) error {
var sector SectorOnChainInfo
return sa.Array.ForEach(&sector, func(i int64) error {
return cb(abi.SectorNumber(i), &sector)
})
}

// VestingFunds represents the vesting table state for the miner.
// It is a slice of (VestingEpoch, VestingAmount).
// The slice will always be sorted by the VestingEpoch.
Expand Down
109 changes: 109 additions & 0 deletions builtin/v16/migration/miner.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package migration

import (
"context"

"github.com/filecoin-project/go-state-types/abi"
miner15 "github.com/filecoin-project/go-state-types/builtin/v15/miner"
miner16 "github.com/filecoin-project/go-state-types/builtin/v16/miner"
"github.com/filecoin-project/go-state-types/builtin/v16/util/adt"
"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 minerMigrator struct {
OutCodeCID cid.Cid
}

func newMinerMigrator(_ context.Context, _ cbor.IpldStore, outCode cid.Cid) (*minerMigrator, error) {
return &minerMigrator{
OutCodeCID: outCode,
}, nil
}

func (m *minerMigrator) MigrateState(ctx context.Context, store cbor.IpldStore, in migration.ActorMigrationInput) (result *migration.ActorMigrationResult, err error) {
var inState miner15.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)
}

ctxStore := adt.WrapStore(ctx, store)

inSectors, err := miner15.LoadSectors(ctxStore, inState.Sectors)
if err != nil {
return nil, xerrors.Errorf("failed to load sectors array: %w", err)
}

arr, err := adt.MakeEmptyArray(ctxStore, miner16.SectorsAmtBitwidth)
if err != nil {
return nil, xerrors.Errorf("failed to create sectors array: %w", err)
}
outSectors := miner16.Sectors{Array: arr, Store: ctxStore}

inSectors.ForEach(func(sn abi.SectorNumber, soci *miner15.SectorOnChainInfo) error {
return outSectors.Set(sn, &miner16.SectorOnChainInfo{
SectorNumber: soci.SectorNumber,
SealProof: soci.SealProof,
SealedCID: soci.SealedCID,
DealIDs: soci.DealIDs,
Activation: soci.Activation,
Expiration: soci.Expiration,
DealWeight: soci.DealWeight,
VerifiedDealWeight: soci.VerifiedDealWeight,
InitialPledge: soci.InitialPledge,
ExpectedDayReward: soci.ExpectedDayReward,
ExpectedStoragePledge: soci.ExpectedStoragePledge,
PowerBaseEpoch: soci.PowerBaseEpoch,
ReplacedDayReward: soci.ReplacedDayReward,
SectorKeyCID: soci.SectorKeyCID,
Flags: miner16.SectorOnChainInfoFlags(soci.Flags),
})
})

outSectorsRoot, err := outSectors.Root()
if err != nil {
return nil, xerrors.Errorf("failed to flush sectors: %w", err)
}

// TODO: implement cached migrator with diff, see v13 for example

outState := miner16.State{
Info: inState.Info,
PreCommitDeposits: inState.PreCommitDeposits,
LockedFunds: inState.LockedFunds,
VestingFunds: inState.VestingFunds,
FeeDebt: inState.FeeDebt,
InitialPledge: inState.InitialPledge,
PreCommittedSectors: inState.PreCommittedSectors,
PreCommittedSectorsCleanUp: inState.PreCommittedSectorsCleanUp,
AllocatedSectors: inState.AllocatedSectors,
Sectors: outSectorsRoot,
ProvingPeriodStart: inState.ProvingPeriodStart,
CurrentDeadline: inState.CurrentDeadline,
Deadlines: inState.Deadlines,
EarlyTerminations: inState.EarlyTerminations,
DeadlineCronActive: inState.DeadlineCronActive,
}

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

return &migration.ActorMigrationResult{
NewCodeCID: m.MigratedCodeCID(),
NewHead: newHead,
}, nil
}

func (m *minerMigrator) MigratedCodeCID() cid.Cid {
return m.OutCodeCID
}

func (m *minerMigrator) Deferred() bool {
return false
}

var _ migration.ActorMigration = (*minerMigrator)(nil)
21 changes: 21 additions & 0 deletions builtin/v16/migration/top.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,14 +67,23 @@ 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{})

miner15Cid := cid.Undef

for _, oldEntry := range oldManifestData.Entries {
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)
}
if oldEntry.Name == manifest.MinerKey {
miner15Cid = oldEntry.Code
}
migrations[oldEntry.Code] = migration.CachedMigration(cache, migration.CodeMigrator{OutCodeCID: newCodeCID})
}

if miner15Cid == cid.Undef {
return cid.Undef, xerrors.Errorf("could not find miner actor in old manifest")
}

// migrations that migrate both code and state, override entries in `migrations`

// The System Actor
Expand All @@ -90,6 +99,18 @@ func MigrateStateTree(ctx context.Context, store cbor.IpldStore, newManifestCID
return cid.Undef, xerrors.Errorf("incomplete migration specification with %d code CIDs, need %d", len(migrations)+len(deferredCodeIDs), len(oldManifestData.Entries))
}

miner16Cid, ok := newManifest.Get(manifest.MinerKey)
if !ok {
return cid.Undef, xerrors.Errorf("code cid for miner actor not found in new manifest")
}

minerMig, err := newMinerMigrator(ctx, store, miner16Cid)
if err != nil {
return cid.Undef, xerrors.Errorf("failed to create miner migrator: %w", err)
}

migrations[miner15Cid] = migration.CachedMigration(cache, minerMig)

actorsOut, err := migration.RunMigration(ctx, cfg, cache, store, log, actorsIn, migrations)
if err != nil {
return cid.Undef, xerrors.Errorf("failed to run migration: %w", err)
Expand Down
2 changes: 1 addition & 1 deletion builtin/v16/miner/miner_state.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ type State struct {
//
// Sectors are removed from this AMT when the partition to which the
// sector belongs is compacted.
Sectors cid.Cid // Array, AMT[SectorNumber]SectorOnChainInfo (sparse)
Sectors cid.Cid // Array, AMT[SectorNumber]*SectorOnChainInfo (sparse)

// DEPRECATED. This field will change names and no longer be updated every proving period in a future upgrade
// The first epoch in this miner's current proving period. This is the first epoch in which a PoSt for a
Expand Down
41 changes: 33 additions & 8 deletions builtin/v16/miner/miner_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -272,31 +272,32 @@ func LoadExpirationQueue(store adt.Store, root cid.Cid, quant builtin.QuantSpec,
}
return ExpirationQueue{arr, quant}, nil
}

func LoadSectors(store adt.Store, root cid.Cid) (Sectors, error) {
sectorsArr, err := adt.AsArray(store, root, SectorsAmtBitwidth)
if err != nil {
return Sectors{}, err
}
return Sectors{sectorsArr}, nil
return Sectors{sectorsArr, store}, nil
}

// Sectors is a helper type for accessing/modifying a miner's sectors. It's safe
// to pass this object around as needed.
type Sectors struct {
*adt.Array
adt.Store
}

func (sa Sectors) Load(sectorNos bitfield.BitField) ([]*SectorOnChainInfo, error) {
var sectorInfos []*SectorOnChainInfo
if err := sectorNos.ForEach(func(i uint64) error {
var sectorOnChain SectorOnChainInfo
found, err := sa.Array.Get(i, &sectorOnChain)
if err != nil {
if si, found, err := sa.Get(abi.SectorNumber(i)); err != nil {
return xc.ErrIllegalState.Wrapf("failed to load sector %v: %w", abi.SectorNumber(i), err)
} else if !found {
return xc.ErrNotFound.Wrapf("can't find sector %d", i)
} else {
sectorInfos = append(sectorInfos, si)
}
sectorInfos = append(sectorInfos, &sectorOnChain)
return nil
}); err != nil {
// Keep the underlying error code, unless the error was from
Expand All @@ -308,15 +309,39 @@ func (sa Sectors) Load(sectorNos bitfield.BitField) ([]*SectorOnChainInfo, error
}

func (sa Sectors) Get(sectorNumber abi.SectorNumber) (info *SectorOnChainInfo, found bool, err error) {
var res SectorOnChainInfo
if found, err := sa.Array.Get(uint64(sectorNumber), &res); err != nil {
return nil, false, xerrors.Errorf("failed to get sector %d: %w", sectorNumber, err)
var c cbg.CborCid
if found, err := sa.Array.Get(uint64(sectorNumber), &c); err != nil {
return nil, false, xerrors.Errorf("failed to get sector link %d: %w", sectorNumber, err)
} else if !found {
return nil, false, nil
}
var res SectorOnChainInfo
if err := sa.Store.Get(sa.Store.Context(), cid.Cid(c), &res); err != nil {
return nil, false, xerrors.Errorf("failed to load sector %d: %w", sectorNumber, err)
}
return &res, true, nil
}

func (sa Sectors) ForEach(cb func(abi.SectorNumber, *SectorOnChainInfo) error) error {
var c cbg.CborCid
return sa.Array.ForEach(&c, func(i int64) error {
var info SectorOnChainInfo
if err := sa.Store.Get(sa.Store.Context(), cid.Cid(c), &info); err != nil {
return err
}
return cb(abi.SectorNumber(i), &info)
})
}

func (sa Sectors) Set(sectorNumber abi.SectorNumber, info *SectorOnChainInfo) error {
c, err := sa.Store.Put(sa.Store.Context(), info)
if err != nil {
return xerrors.Errorf("failed to store sector info: %w", err)
}
cb := cbg.CborCid(c)
return sa.Array.Set(uint64(sectorNumber), &cb)
}

// VestingFunds represents the vesting table state for the miner.
// It is a slice of (VestingEpoch, VestingAmount).
// The slice will always be sorted by the VestingEpoch.
Expand Down
Loading