From 74567a16d1591d56e4f654edbcacd0babf206bca Mon Sep 17 00:00:00 2001 From: Aayush Date: Thu, 6 Apr 2023 14:40:11 -0400 Subject: [PATCH] Add and test the FIP-0061 migration --- chain/consensus/filcns/upgrades.go | 108 +++++++++++++++++- go.mod | 2 +- go.sum | 4 +- itests/migration_test.go | 172 ++++++++++++++++++++++++++++- 4 files changed, 281 insertions(+), 5 deletions(-) diff --git a/chain/consensus/filcns/upgrades.go b/chain/consensus/filcns/upgrades.go index 583a174f2..4c5f3bd30 100644 --- a/chain/consensus/filcns/upgrades.go +++ b/chain/consensus/filcns/upgrades.go @@ -19,6 +19,7 @@ import ( actorstypes "github.com/filecoin-project/go-state-types/actors" "github.com/filecoin-project/go-state-types/big" nv18 "github.com/filecoin-project/go-state-types/builtin/v10/migration" + nv19 "github.com/filecoin-project/go-state-types/builtin/v11/migration" nv17 "github.com/filecoin-project/go-state-types/builtin/v9/migration" "github.com/filecoin-project/go-state-types/manifest" "github.com/filecoin-project/go-state-types/migration" @@ -1663,7 +1664,7 @@ func upgradeActorsV10Common( if stateRoot.Version != types.StateTreeVersion4 { return cid.Undef, xerrors.Errorf( - "expected state root version 4 for actors v9 upgrade, got %d", + "expected state root version 4 for actors v10 upgrade, got %d", stateRoot.Version, ) } @@ -1704,6 +1705,111 @@ func upgradeActorsV10Common( return newRoot, nil } +func PreUpgradeActorsV11(ctx context.Context, sm *stmgr.StateManager, cache stmgr.MigrationCache, root cid.Cid, epoch abi.ChainEpoch, ts *types.TipSet) error { + // Use half the CPUs for pre-migration, but leave at least 3. + workerCount := MigrationMaxWorkerCount + if workerCount <= 4 { + workerCount = 1 + } else { + workerCount /= 2 + } + + lbts, lbRoot, err := stmgr.GetLookbackTipSetForRound(ctx, sm, ts, epoch) + if err != nil { + return xerrors.Errorf("error getting lookback ts for premigration: %w", err) + } + + config := migration.Config{ + MaxWorkers: uint(workerCount), + ProgressLogPeriod: time.Minute * 5, + } + + _, err = upgradeActorsV11Common(ctx, sm, cache, lbRoot, epoch, lbts, config) + return err +} + +func UpgradeActorsV11(ctx context.Context, sm *stmgr.StateManager, cache stmgr.MigrationCache, cb stmgr.ExecMonitor, + root cid.Cid, epoch abi.ChainEpoch, ts *types.TipSet) (cid.Cid, error) { + // Use all the CPUs except 2. + workerCount := MigrationMaxWorkerCount - 3 + if workerCount <= 0 { + workerCount = 1 + } + config := migration.Config{ + MaxWorkers: uint(workerCount), + JobQueueSize: 1000, + ResultQueueSize: 100, + ProgressLogPeriod: 10 * time.Second, + } + newRoot, err := upgradeActorsV11Common(ctx, sm, cache, root, epoch, ts, config) + if err != nil { + return cid.Undef, xerrors.Errorf("migrating actors v11 state: %w", err) + } + return newRoot, nil +} + +func upgradeActorsV11Common( + ctx context.Context, sm *stmgr.StateManager, cache stmgr.MigrationCache, + root cid.Cid, epoch abi.ChainEpoch, ts *types.TipSet, + config migration.Config, +) (cid.Cid, error) { + buf := blockstore.NewTieredBstore(sm.ChainStore().StateBlockstore(), blockstore.NewMemorySync()) + store := store.ActorStore(ctx, buf) + + // ensure that the manifest is loaded in the blockstore + if err := bundle.LoadBundles(ctx, sm.ChainStore().StateBlockstore(), actorstypes.Version11); err != nil { + return cid.Undef, xerrors.Errorf("failed to load manifest bundle: %w", err) + } + + // Load the state root. + var stateRoot types.StateRoot + if err := store.Get(ctx, root, &stateRoot); err != nil { + return cid.Undef, xerrors.Errorf("failed to decode state root: %w", err) + } + + if stateRoot.Version != types.StateTreeVersion5 { + return cid.Undef, xerrors.Errorf( + "expected state root version 5 for actors v11 upgrade, got %d", + stateRoot.Version, + ) + } + + manifest, ok := actors.GetManifest(actorstypes.Version11) + if !ok { + return cid.Undef, xerrors.Errorf("no manifest CID for v11 upgrade") + } + + // Perform the migration + newHamtRoot, err := nv19.MigrateStateTree(ctx, store, manifest, stateRoot.Actors, epoch, config, + migrationLogger{}, cache) + if err != nil { + return cid.Undef, xerrors.Errorf("upgrading to actors v11: %w", err) + } + + // Persist the result. + newRoot, err := store.Put(ctx, &types.StateRoot{ + Version: types.StateTreeVersion5, + Actors: newHamtRoot, + Info: stateRoot.Info, + }) + if err != nil { + return cid.Undef, xerrors.Errorf("failed to persist new state root: %w", err) + } + + // Persist the new tree. + + { + from := buf + to := buf.Read() + + if err := vm.Copy(ctx, from, to, newRoot); err != nil { + return cid.Undef, xerrors.Errorf("copying migrated tree: %w", err) + } + } + + return newRoot, nil +} + // Example upgrade function if upgrade requires only code changes //func UpgradeActorsV9(ctx context.Context, sm *stmgr.StateManager, _ stmgr.MigrationCache, _ stmgr.ExecMonitor, root cid.Cid, _ abi.ChainEpoch, _ *types.TipSet) (cid.Cid, error) { // buf := blockstore.NewTieredBstore(sm.ChainStore().StateBlockstore(), blockstore.NewMemorySync()) diff --git a/go.mod b/go.mod index 34b3e7281..7648f4545 100644 --- a/go.mod +++ b/go.mod @@ -46,7 +46,7 @@ require ( github.com/filecoin-project/go-legs v0.4.4 github.com/filecoin-project/go-padreader v0.0.1 github.com/filecoin-project/go-paramfetch v0.0.4 - github.com/filecoin-project/go-state-types v0.11.0-alpha-3.0.20230405192832-39cd7e4a3ed7 + github.com/filecoin-project/go-state-types v0.11.0-alpha-3.0.20230406171822-54b19fe787f2 github.com/filecoin-project/go-statemachine v1.0.2 github.com/filecoin-project/go-statestore v0.2.0 github.com/filecoin-project/go-storedcounter v0.1.0 diff --git a/go.sum b/go.sum index bb44ba7e5..81a9d3e39 100644 --- a/go.sum +++ b/go.sum @@ -355,8 +355,8 @@ github.com/filecoin-project/go-state-types v0.1.0/go.mod h1:ezYnPf0bNkTsDibL/psS github.com/filecoin-project/go-state-types v0.1.6/go.mod h1:UwGVoMsULoCK+bWjEdd/xLCvLAQFBC7EDT477SKml+Q= github.com/filecoin-project/go-state-types v0.1.10/go.mod h1:UwGVoMsULoCK+bWjEdd/xLCvLAQFBC7EDT477SKml+Q= github.com/filecoin-project/go-state-types v0.11.0-alpha-2.0.20230331142914-3c737db98e58/go.mod h1:aLIas+W8BWAfpLWEPUOGMPBdhcVwoCG4pIQSQk26024= -github.com/filecoin-project/go-state-types v0.11.0-alpha-3.0.20230405192832-39cd7e4a3ed7 h1:xH2EDAv9s2WltPmRU8arGs8bfCKW8lIwKNZmSijsoXQ= -github.com/filecoin-project/go-state-types v0.11.0-alpha-3.0.20230405192832-39cd7e4a3ed7/go.mod h1:SyNPwTsU7I22gL2r0OAPcImvLoTVfgRwdK/Y5rR1zz8= +github.com/filecoin-project/go-state-types v0.11.0-alpha-3.0.20230406171822-54b19fe787f2 h1:ReRX+cQ43cmnJLsTywIXUwnUxKRiLjPMQWnEYmodP8g= +github.com/filecoin-project/go-state-types v0.11.0-alpha-3.0.20230406171822-54b19fe787f2/go.mod h1:SyNPwTsU7I22gL2r0OAPcImvLoTVfgRwdK/Y5rR1zz8= github.com/filecoin-project/go-statemachine v0.0.0-20200925024713-05bd7c71fbfe/go.mod h1:FGwQgZAt2Gh5mjlwJUlVB62JeYdo+if0xWxSEfBD9ig= github.com/filecoin-project/go-statemachine v1.0.2 h1:421SSWBk8GIoCoWYYTE/d+qCWccgmRH0uXotXRDjUbc= github.com/filecoin-project/go-statemachine v1.0.2/go.mod h1:jZdXXiHa61n4NmgWFG4w8tnqgvZVHYbJ3yW7+y8bF54= diff --git a/itests/migration_test.go b/itests/migration_test.go index 483b31553..3b8868f35 100644 --- a/itests/migration_test.go +++ b/itests/migration_test.go @@ -7,6 +7,12 @@ import ( "testing" "time" + miner11 "github.com/filecoin-project/go-state-types/builtin/v11/miner" + "github.com/filecoin-project/lotus/build" + + power11 "github.com/filecoin-project/go-state-types/builtin/v11/power" + adt11 "github.com/filecoin-project/go-state-types/builtin/v11/util/adt" + "github.com/stretchr/testify/require" "github.com/filecoin-project/go-address" @@ -526,7 +532,7 @@ func TestMigrationNV17(t *testing.T) { require.Nil(t, dc) } -func TestMigrationNV19(t *testing.T) { +func TestMigrationNV18(t *testing.T) { kit.QuietMiningLogs() nv18epoch := abi.ChainEpoch(100) @@ -594,3 +600,167 @@ func TestMigrationNV19(t *testing.T) { return nil })) } + +func TestMigrationNV19(t *testing.T) { + kit.QuietMiningLogs() + + blockTime := 5 * time.Millisecond + nv19epoch := abi.ChainEpoch(100) + nv20epoch := nv19epoch + builtin.EpochsInDay + testClient, testMiner, ens := kit.EnsembleMinimal(t, kit.MockProofs(), + kit.UpgradeSchedule(stmgr.Upgrade{ + Network: network.Version18, + Height: -1, + }, stmgr.Upgrade{ + Network: network.Version19, + Height: nv19epoch, + Migration: filcns.UpgradeActorsV11, + }, stmgr.Upgrade{ + Network: network.Version20, + Height: nv20epoch, + Migration: nil, + }, + )) + + ens.InterconnectAll().BeginMining(blockTime) + + clientApi := testClient.FullNode.(*impl.FullNodeAPI) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + testClient.WaitTillChain(ctx, kit.HeightAtLeast(nv19epoch+5)) + + bs := blockstore.NewAPIBlockstore(testClient) + ctxStore := gstStore.WrapBlockStore(ctx, bs) + + postMigrationTs, err := clientApi.ChainHead(ctx) + require.NoError(t, err) + + newStateTree, err := state.LoadStateTree(ctxStore, postMigrationTs.Blocks()[0].ParentStateRoot) + require.NoError(t, err) + + require.Equal(t, types.StateTreeVersion5, newStateTree.Version()) + + // Now that we have upgraded, we need to check that: + + // - a PoSt is successfully submitted in nv19 + // - a PoSt is successfully submitted in nv20 + // - all claims in the Power actor are of v1_1 type + // - the miner's info has been updated to the v1_1 type + + // Wait for an nv19 PoSt + + mi, err := testClient.StateMinerInfo(ctx, testMiner.ActorAddr, types.EmptyTSK) + require.NoError(t, err) + + wact19, err := testClient.StateGetActor(ctx, mi.Worker, types.EmptyTSK) + require.NoError(t, err) + en19 := wact19.Nonce + + // wait for a new message to be sent from worker address, it will be a PoSt + +waitForProof19: + for { + //stm: @CHAIN_STATE_GET_ACTOR_001 + wact, err := testClient.StateGetActor(ctx, mi.Worker, types.EmptyTSK) + require.NoError(t, err) + if wact.Nonce > en19 { + break waitForProof19 + } + + build.Clock.Sleep(blockTime) + } + + slm19, err := testClient.StateListMessages(ctx, &api.MessageMatch{To: testMiner.ActorAddr}, types.EmptyTSK, 0) + require.NoError(t, err) + + pmr19, err := testClient.StateSearchMsg(ctx, types.EmptyTSK, slm19[0], -1, false) + require.NoError(t, err) + + nv19, err := testClient.StateNetworkVersion(ctx, pmr19.TipSet) + require.NoError(t, err) + require.Equal(t, network.Version19, nv19) + + require.True(t, pmr19.Receipt.ExitCode.IsSuccess()) + + slmsg19, err := testClient.ChainGetMessage(ctx, slm19[0]) + require.NoError(t, err) + + var params19 miner11.SubmitWindowedPoStParams + require.NoError(t, params19.UnmarshalCBOR(bytes.NewBuffer(slmsg19.Params))) + require.Equal(t, abi.RegisteredPoStProof_StackedDrgWindow2KiBV1_1, params19.Proofs[0].PoStProof) + + // Wait for nv20 + + testClient.WaitTillChain(ctx, kit.HeightAtLeast(nv20epoch+5)) + + // Wait for an nv20 PoSt + + wact20, err := testClient.StateGetActor(ctx, mi.Worker, types.EmptyTSK) + require.NoError(t, err) + en20 := wact20.Nonce + + // wait for a new message to be sent from worker address, it will be a PoSt + +waitForProof20: + for { + //stm: @CHAIN_STATE_GET_ACTOR_001 + wact, err := testClient.StateGetActor(ctx, mi.Worker, types.EmptyTSK) + require.NoError(t, err) + if wact.Nonce > en20 { + break waitForProof20 + } + + build.Clock.Sleep(blockTime) + } + + slm20, err := testClient.StateListMessages(ctx, &api.MessageMatch{To: testMiner.ActorAddr}, types.EmptyTSK, 0) + require.NoError(t, err) + + pmr20, err := testClient.StateSearchMsg(ctx, types.EmptyTSK, slm20[0], -1, false) + require.NoError(t, err) + + nv20, err := testClient.StateNetworkVersion(ctx, pmr20.TipSet) + require.NoError(t, err) + require.Equal(t, network.Version20, nv20) + + require.True(t, pmr20.Receipt.ExitCode.IsSuccess()) + + slmsg20, err := testClient.ChainGetMessage(ctx, slm20[0]) + require.NoError(t, err) + + var params20 miner11.SubmitWindowedPoStParams + require.NoError(t, params20.UnmarshalCBOR(bytes.NewBuffer(slmsg20.Params))) + require.Equal(t, abi.RegisteredPoStProof_StackedDrgWindow2KiBV1_1, params20.Proofs[0].PoStProof) + + // check claims in the Power actor + + powerActor, err := newStateTree.GetActor(builtin.StoragePowerActorAddr) + require.NoError(t, err) + + var powerSt power11.State + require.NoError(t, ctxStore.Get(ctx, powerActor.Head, &powerSt)) + + powerClaims, err := adt11.AsMap(ctxStore, powerSt.Claims, builtin.DefaultHamtBitwidth) + require.NoError(t, err) + + var claim power11.Claim + require.NoError(t, powerClaims.ForEach(&claim, func(key string) error { + v1proof, err := claim.WindowPoStProofType.ToV1_1PostProof() + require.NoError(t, err) + + require.Equal(t, v1proof, claim.WindowPoStProofType) + return nil + })) + + // check MinerInfo + + minerInfo, err := testClient.StateMinerInfo(ctx, testMiner.ActorAddr, types.EmptyTSK) + require.NoError(t, err) + + v1proof, err := minerInfo.WindowPoStProofType.ToV1_1PostProof() + require.NoError(t, err) + + require.Equal(t, v1proof, minerInfo.WindowPoStProofType) + +}