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-0045: Implement migrations #85

Merged
merged 13 commits into from
Oct 7, 2022
2 changes: 2 additions & 0 deletions builtin/shared.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import (
// This value has been empirically chosen, but the optimal value for maps with different mutation profiles may differ.
const DefaultHamtBitwidth = 5

const DefaultTokenActorBitwidth = 3
anorth marked this conversation as resolved.
Show resolved Hide resolved

type BigFrac struct {
Numerator big.Int
Denominator big.Int
Expand Down
47 changes: 47 additions & 0 deletions builtin/v8/miner/deadline_state.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,10 +95,57 @@ const DeadlineExpirationAmtBitwidth = 5
// only exceed the partition AMT's height at ~0.75EiB of storage.
const DeadlineOptimisticPoStSubmissionsAmtBitwidth = 2

//
// Deadline (singular)
//

func ConstructDeadline(store adt.Store) (*Deadline, error) {
emptyPartitionsArrayCid, err := adt.StoreEmptyArray(store, DeadlinePartitionsAmtBitwidth)
if err != nil {
return nil, xerrors.Errorf("failed to construct empty partitions array: %w", err)
}
emptyDeadlineExpirationArrayCid, err := adt.StoreEmptyArray(store, DeadlineExpirationAmtBitwidth)
if err != nil {
return nil, xerrors.Errorf("failed to construct empty deadline expiration array: %w", err)
}

emptySectorsSnapshotArrayCid, err := adt.StoreEmptyArray(store, SectorsAmtBitwidth)
if err != nil {
return nil, xerrors.Errorf("failed to construct empty sectors snapshot array: %w", err)
}

emptyPoStSubmissionsArrayCid, err := adt.StoreEmptyArray(store, DeadlineOptimisticPoStSubmissionsAmtBitwidth)
if err != nil {
return nil, xerrors.Errorf("failed to construct empty proofs array: %w", err)
}

return &Deadline{
Partitions: emptyPartitionsArrayCid,
ExpirationsEpochs: emptyDeadlineExpirationArrayCid,
EarlyTerminations: bitfield.New(),
LiveSectors: 0,
TotalSectors: 0,
FaultyPower: NewPowerPairZero(),
PartitionsPoSted: bitfield.New(),
OptimisticPoStSubmissions: emptyPoStSubmissionsArrayCid,
PartitionsSnapshot: emptyPartitionsArrayCid,
SectorsSnapshot: emptySectorsSnapshotArrayCid,
OptimisticPoStSubmissionsSnapshot: emptyPoStSubmissionsArrayCid,
}, nil
}

//
// Deadlines (plural)
//

func ConstructDeadlines(emptyDeadlineCid cid.Cid) *Deadlines {
d := new(Deadlines)
for i := range d.Due {
d.Due[i] = emptyDeadlineCid
}
return d
}

func (d *Deadlines) LoadDeadline(store adt.Store, dlIdx uint64) (*Deadline, error) {
if dlIdx >= uint64(len(d.Due)) {
return nil, xc.ErrIllegalArgument.Wrapf("invalid deadline %d", dlIdx)
Expand Down
71 changes: 71 additions & 0 deletions builtin/v8/util/adt/set.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package adt

import (
"github.com/filecoin-project/go-state-types/abi"
"github.com/ipfs/go-cid"
)

// Set interprets a Map as a set, storing keys (with empty values) in a HAMT.
arajasek marked this conversation as resolved.
Show resolved Hide resolved
type Set struct {
m *Map
}

// AsSet interprets a store as a HAMT-based set with root `r`.
// The HAMT is interpreted with branching factor 2^bitwidth.
func AsSet(s Store, r cid.Cid, bitwidth int) (*Set, error) {
m, err := AsMap(s, r, bitwidth)
if err != nil {
return nil, err
}

return &Set{
m: m,
}, nil
}

// NewSet creates a new HAMT with root `r` and store `s`.
// The HAMT has branching factor 2^bitwidth.
func MakeEmptySet(s Store, bitwidth int) (*Set, error) {
m, err := MakeEmptyMap(s, bitwidth)
if err != nil {
return nil, err
}
return &Set{m}, nil
}

// Root return the root cid of HAMT.
func (h *Set) Root() (cid.Cid, error) {
return h.m.Root()
}

// Put adds `k` to the set.
func (h *Set) Put(k abi.Keyer) error {
return h.m.Put(k, nil)
}

// Has returns true iff `k` is in the set.
func (h *Set) Has(k abi.Keyer) (bool, error) {
return h.m.Get(k, nil)
}

// Removes `k` from the set, if present.
// Returns whether the key was previously present.
func (h *Set) TryDelete(k abi.Keyer) (bool, error) {
return h.m.TryDelete(k)
}

// Removes `k` from the set, expecting it to be present.
func (h *Set) Delete(k abi.Keyer) error {
return h.m.Delete(k)
}

// ForEach iterates over all values in the set, calling the callback for each value.
// Returning error from the callback stops the iteration.
func (h *Set) ForEach(cb func(k string) error) error {
return h.m.ForEach(nil, cb)
}

// Collects all the keys from the set into a slice of strings.
func (h *Set) CollectKeys() (out []string, err error) {
return h.m.CollectKeys()
}
3 changes: 0 additions & 3 deletions builtin/v9/datacap/datacap_state.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,11 @@ import (
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/go-state-types/big"
"github.com/filecoin-project/go-state-types/builtin"
"github.com/filecoin-project/go-state-types/builtin/v9/util/adt"
"github.com/ipfs/go-cid"
"golang.org/x/xerrors"
)

var DatacapGranularity = builtin.TokenPrecision

type State struct {
Governor address.Address
Token TokenState
arajasek marked this conversation as resolved.
Show resolved Hide resolved
Expand Down
4 changes: 4 additions & 0 deletions builtin/v9/datacap/datacap_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,12 @@ package datacap
import (
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/go-state-types/big"
"github.com/filecoin-project/go-state-types/builtin"
)

var InfiniteAllowance = big.Mul(big.MustFromString("1000000000000000000000"), builtin.TokenPrecision)

type MintParams struct {
To address.Address
Amount abi.TokenAmount
Expand Down
2 changes: 1 addition & 1 deletion builtin/v9/market/market_state.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ type State struct {
TotalClientStorageFee abi.TokenAmount

// Verified registry allocation IDs for deals that are not yet activated.
PendingDealAllocationIds cid.Cid
PendingDealAllocationIds cid.Cid // HAMT[DealID]AllocationID
}

func ConstructState(store adt.Store) (*State, error) {
Expand Down
108 changes: 108 additions & 0 deletions builtin/v9/migration/datacap.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
package migration

import (
"context"

verifreg8 "github.com/filecoin-project/go-state-types/builtin/v8/verifreg"

cbor "github.com/ipfs/go-ipld-cbor"

"github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/go-state-types/big"
"github.com/filecoin-project/go-state-types/builtin"
adt8 "github.com/filecoin-project/go-state-types/builtin/v8/util/adt"
datacap9 "github.com/filecoin-project/go-state-types/builtin/v9/datacap"
adt9 "github.com/filecoin-project/go-state-types/builtin/v9/util/adt"
verifreg9 "github.com/filecoin-project/go-state-types/builtin/v9/verifreg"
"github.com/ipfs/go-cid"
"golang.org/x/xerrors"
)

type datacapMigrator struct {
emptyMapCid cid.Cid
verifregStateV8 verifreg8.State
OutCodeCID cid.Cid
}

func (d *datacapMigrator) migratedCodeCID() cid.Cid {
return d.OutCodeCID

}
func (d *datacapMigrator) migrateState(ctx context.Context, store cbor.IpldStore, input actorMigrationInput) (result *actorMigrationResult, err error) {
adtStore := adt9.WrapStore(ctx, store)
verifiedClients, err := adt8.AsMap(adtStore, d.verifregStateV8.VerifiedClients, builtin.DefaultHamtBitwidth)
if err != nil {
return nil, xerrors.Errorf("failed to get verified clients: %w", err)
}

tokenSupply := big.Zero()

balancesMap, err := adt9.AsMap(adtStore, d.emptyMapCid, builtin.DefaultHamtBitwidth)
if err != nil {
return nil, xerrors.Errorf("failed to load empty map: %w", err)
}

allowancesMap, err := adt9.AsMap(adtStore, d.emptyMapCid, builtin.DefaultHamtBitwidth)
if err != nil {
return nil, xerrors.Errorf("failed to load empty map: %w", err)
}

var dcap abi.StoragePower
if err = verifiedClients.ForEach(&dcap, func(key string) error {
a, err := address.NewFromBytes([]byte(key))
if err != nil {
return err
}

tokenAmount := verifreg9.DataCapToTokens(dcap)
tokenSupply = big.Add(tokenSupply, tokenAmount)
if err = balancesMap.Put(abi.IdAddrKey(a), &tokenAmount); err != nil {
return xerrors.Errorf("failed to put new balancesMap entry: %w", err)
}

allowancesMapEntry, err := adt9.AsMap(adtStore, d.emptyMapCid, builtin.DefaultHamtBitwidth)
if err != nil {
return xerrors.Errorf("failed to load empty map: %w", err)
}

if err = allowancesMapEntry.Put(abi.IdAddrKey(builtin.StorageMarketActorAddr), &datacap9.InfiniteAllowance); err != nil {
return xerrors.Errorf("failed to populate allowance map: %w", err)
}

return allowancesMap.Put(abi.IdAddrKey(a), allowancesMapEntry)
}); err != nil {
return nil, xerrors.Errorf("failed to loop over verified clients: %w", err)
}

balancesMapRoot, err := balancesMap.Root()
if err != nil {
return nil, xerrors.Errorf("failed to flush balances map: %w", err)
}

allowancesMapRoot, err := allowancesMap.Root()
if err != nil {
return nil, xerrors.Errorf("failed to flush allowances map: %w", err)
}

dataCapState := datacap9.State{
Governor: builtin.VerifiedRegistryActorAddr,
Token: datacap9.TokenState{
Supply: tokenSupply,
Balances: balancesMapRoot,
Allowances: allowancesMapRoot,
HamtBitWidth: builtin.DefaultHamtBitwidth,
},
}

dataCapHead, err := adtStore.Put(ctx, &dataCapState)
if err != nil {
return nil, xerrors.Errorf("failed to put data cap state: %w", err)
}

return &actorMigrationResult{
newCodeCID: d.OutCodeCID,
newHead: dataCapHead,
}, nil

}
57 changes: 57 additions & 0 deletions builtin/v9/migration/market.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package migration

import (
"context"

"github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/go-state-types/builtin"
market8 "github.com/filecoin-project/go-state-types/builtin/v8/market"
adt8 "github.com/filecoin-project/go-state-types/builtin/v8/util/adt"
market9 "github.com/filecoin-project/go-state-types/builtin/v9/market"
adt9 "github.com/filecoin-project/go-state-types/builtin/v9/util/adt"
verifreg9 "github.com/filecoin-project/go-state-types/builtin/v9/verifreg"
"github.com/ipfs/go-cid"
typegen "github.com/whyrusleeping/cbor-gen"
"golang.org/x/xerrors"
)

func migrateMarket(ctx context.Context, adtStore adt8.Store, dealsToAllocations map[abi.DealID]verifreg9.AllocationId, marketStateV8 market8.State, emptyMapCid cid.Cid) (cid.Cid, error) {
pendingDealAllocationIdsMap, err := adt9.AsMap(adtStore, emptyMapCid, builtin.DefaultHamtBitwidth)
if err != nil {
return cid.Undef, xerrors.Errorf("failed to load empty map: %w", err)
}

for dealID, allocationID := range dealsToAllocations {
cborAllocationID := typegen.CborInt(allocationID)
if err = pendingDealAllocationIdsMap.Put(abi.UIntKey(uint64(dealID)), &cborAllocationID); err != nil {
return cid.Undef, xerrors.Errorf("failed to populate pending deal allocations map: %w", err)
}
}

pendingDealAllocationIdsMapRoot, err := pendingDealAllocationIdsMap.Root()
if err != nil {
return cid.Undef, xerrors.Errorf("failed to flush pending deal allocations map: %w", err)
}

marketStateV9 := market9.State{
Proposals: marketStateV8.Proposals,
States: marketStateV8.States,
PendingProposals: marketStateV8.PendingProposals,
EscrowTable: marketStateV8.EscrowTable,
LockedTable: marketStateV8.LockedTable,
NextID: marketStateV8.NextID,
DealOpsByEpoch: marketStateV8.DealOpsByEpoch,
LastCron: marketStateV8.LastCron,
TotalClientLockedCollateral: marketStateV8.TotalClientLockedCollateral,
TotalProviderLockedCollateral: marketStateV8.TotalProviderLockedCollateral,
TotalClientStorageFee: marketStateV8.TotalClientStorageFee,
PendingDealAllocationIds: pendingDealAllocationIdsMapRoot,
}

marketHead, err := adtStore.Put(ctx, &marketStateV9)
if err != nil {
return cid.Undef, xerrors.Errorf("failed to put market state: %w", err)
}

return marketHead, nil
}
Loading