diff --git a/builtin/v15/miner/miner_types.go b/builtin/v15/miner/miner_types.go index b95cb7f0..d10282c4 100644 --- a/builtin/v15/miner/miner_types.go +++ b/builtin/v15/miner/miner_types.go @@ -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(§or, func(i int64) error { + return cb(abi.SectorNumber(i), §or) + }) +} + // 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. diff --git a/builtin/v16/migration/miner.go b/builtin/v16/migration/miner.go new file mode 100644 index 00000000..b05e07f4 --- /dev/null +++ b/builtin/v16/migration/miner.go @@ -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) diff --git a/builtin/v16/migration/top.go b/builtin/v16/migration/top.go index 930cc10d..7781e992 100644 --- a/builtin/v16/migration/top.go +++ b/builtin/v16/migration/top.go @@ -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 @@ -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) diff --git a/builtin/v16/miner/miner_state.go b/builtin/v16/miner/miner_state.go index f156686e..2195094b 100644 --- a/builtin/v16/miner/miner_state.go +++ b/builtin/v16/miner/miner_state.go @@ -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 diff --git a/builtin/v16/miner/miner_types.go b/builtin/v16/miner/miner_types.go index f689cd7b..07efd315 100644 --- a/builtin/v16/miner/miner_types.go +++ b/builtin/v16/miner/miner_types.go @@ -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, §orOnChain) - 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, §orOnChain) return nil }); err != nil { // Keep the underlying error code, unless the error was from @@ -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.