create function for migrating builtin actors with only code changes

This commit is contained in:
Geoff Stuart 2022-06-06 23:14:35 -04:00
parent 5c0f2c8ae6
commit 339692cde5
3 changed files with 266 additions and 6 deletions

View File

@ -868,6 +868,11 @@ workflows:
suite: itest-get_messages_in_ts
target: "./itests/get_messages_in_ts_test.go"
- test:
name: test-itest-lite_migration
suite: itest-lite_migration
target: "./itests/lite_migration_test.go"
- test:
name: test-itest-lookup_robust_address
suite: itest-lookup_robust_address

View File

@ -5,13 +5,7 @@ import (
"runtime"
"time"
"github.com/filecoin-project/specs-actors/v8/actors/migration/nv16"
"github.com/docker/go-units"
"github.com/filecoin-project/specs-actors/v6/actors/migration/nv14"
"github.com/filecoin-project/specs-actors/v7/actors/migration/nv15"
"github.com/ipfs/go-cid"
cbor "github.com/ipfs/go-ipld-cbor"
"golang.org/x/xerrors"
@ -19,9 +13,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/manifest"
"github.com/filecoin-project/go-state-types/network"
"github.com/filecoin-project/go-state-types/rt"
system8 "github.com/filecoin-project/go-state-types/builtin/v8/system"
builtin0 "github.com/filecoin-project/specs-actors/actors/builtin"
miner0 "github.com/filecoin-project/specs-actors/actors/builtin/miner"
multisig0 "github.com/filecoin-project/specs-actors/actors/builtin/multisig"
@ -33,12 +29,18 @@ import (
"github.com/filecoin-project/specs-actors/v3/actors/migration/nv10"
"github.com/filecoin-project/specs-actors/v4/actors/migration/nv12"
"github.com/filecoin-project/specs-actors/v5/actors/migration/nv13"
"github.com/filecoin-project/specs-actors/v6/actors/migration/nv14"
"github.com/filecoin-project/specs-actors/v7/actors/migration/nv15"
"github.com/filecoin-project/specs-actors/v8/actors/migration/nv16"
states8 "github.com/filecoin-project/specs-actors/v8/actors/states"
adt8 "github.com/filecoin-project/specs-actors/v8/actors/util/adt"
"github.com/filecoin-project/lotus/blockstore"
"github.com/filecoin-project/lotus/build"
"github.com/filecoin-project/lotus/chain/actors"
"github.com/filecoin-project/lotus/chain/actors/builtin"
"github.com/filecoin-project/lotus/chain/actors/builtin/multisig"
"github.com/filecoin-project/lotus/chain/actors/builtin/system"
"github.com/filecoin-project/lotus/chain/state"
"github.com/filecoin-project/lotus/chain/stmgr"
"github.com/filecoin-project/lotus/chain/store"
@ -1431,6 +1433,157 @@ func upgradeActorsV8Common(
return newRoot, nil
}
func UpgradeActorsCode(ctx context.Context, sm *stmgr.StateManager, newActorsManifestCid cid.Cid, root cid.Cid) (cid.Cid, error) {
bstore := sm.ChainStore().StateBlockstore()
return LiteMigration(ctx, bstore, newActorsManifestCid, root)
}
func LiteMigration(ctx context.Context, bstore blockstore.Blockstore, newActorsManifestCid cid.Cid, root cid.Cid) (cid.Cid, error) {
buf := blockstore.NewTieredBstore(bstore, blockstore.NewMemorySync())
store := store.ActorStore(ctx, buf)
adtStore := adt8.WrapStore(ctx, store)
// 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.StateTreeVersion4 {
return cid.Undef, xerrors.Errorf(
"expected state root version 4 for actors code upgrade, got %d",
stateRoot.Version,
)
}
// Get old actors
actorsIn, err := states8.LoadTree(adtStore, stateRoot.Actors)
if err != nil {
return cid.Undef, xerrors.Errorf("failed to load state tree: %w", err)
}
systemActor, ok, err := actorsIn.GetActor(system.Address)
if !ok {
return cid.Undef, xerrors.Errorf("failed to get system actor: %w", err)
}
systemActorType := types.Actor{
Code: systemActor.Code,
Head: systemActor.Head,
Nonce: systemActor.CallSeqNum,
Balance: systemActor.Balance,
}
systemActorState, err := system.Load(store, &systemActorType)
if err != nil {
return cid.Undef, xerrors.Errorf("failed to load system actor state: %w", err)
}
oldActorsManifestCid := systemActorState.GetBuiltinActors()
// load old manifest
oldManifest := manifest.Manifest{
Version: 1,
Data: oldActorsManifestCid,
}
if err := oldManifest.Load(ctx, adtStore); err != nil {
return cid.Undef, xerrors.Errorf("error loading old actor manifest: %w", err)
}
// load new manifest
newManifest := manifest.Manifest{}
if err := store.Get(ctx, newActorsManifestCid, &newManifest); err != nil {
return cid.Undef, xerrors.Errorf("error loading new manifest: %w", err)
}
newManifestData := manifest.ManifestData{}
if err := store.Get(ctx, newManifest.Data, &newManifestData); err != nil {
return cid.Undef, xerrors.Errorf("error loading new manifest data: %w", err)
}
// Maps prior version code CIDs to migration functions.
migrations := make(map[cid.Cid]cid.Cid)
for _, entry := range newManifestData.Entries {
oldCodeCid, ok := oldManifest.Get(entry.Name)
if !ok {
return cid.Undef, xerrors.Errorf("code cid for %s actor not found in manifest", entry.Name)
}
migrations[oldCodeCid] = entry.Code
}
if len(migrations) != 11 {
return cid.Undef, xerrors.Errorf("incomplete manifest with %d code CIDs", len(migrations))
}
startTime := time.Now()
// Load output state tree
actorsOut, err := states8.NewTree(adtStore)
if err != nil {
return cid.Undef, err
}
// Insert migrated records in output state tree.
err = actorsIn.ForEach(func(addr address.Address, actorIn *states8.Actor) error {
var newActor states8.Actor
newCid, ok := migrations[actorIn.Code]
if !ok {
return xerrors.Errorf("new code cid for system actor not found in migrations for actor %s", addr)
}
if addr == system.Address {
newSystemState := system8.State{BuiltinActors: newManifest.Data}
systemStateHead, err := store.Put(ctx, &newSystemState)
if err != nil {
return xerrors.Errorf("could not set system actor state head: %w", err)
}
newActor = states8.Actor{
Code: newCid,
Head: systemStateHead,
CallSeqNum: actorIn.CallSeqNum,
Balance: actorIn.Balance,
}
} else {
newActor = states8.Actor{
Code: newCid,
Head: actorIn.Head,
CallSeqNum: actorIn.CallSeqNum,
Balance: actorIn.Balance,
}
}
err = actorsOut.SetActor(addr, &newActor)
if err != nil {
return xerrors.Errorf("could not set actor at address %s: %w", addr, err)
}
return nil
})
elapsed := time.Since(startTime)
log.Infof("All done after %v. Flushing state tree root.", elapsed)
newHamtRoot, err := actorsOut.Flush()
if err != nil {
return cid.Undef, xerrors.Errorf("failed to flush new actors: %w", err)
}
// Persist the result.
newRoot, err := store.Put(ctx, &types.StateRoot{
Version: types.StateTreeVersion4,
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
}
type migrationLogger struct{}
func (ml migrationLogger) Log(level rt.LogLevel, msg string, args ...interface{}) {

View File

@ -0,0 +1,102 @@
package itests
import (
"context"
"fmt"
"testing"
"time"
"github.com/filecoin-project/lotus/chain/actors/builtin/system"
"github.com/filecoin-project/lotus/chain/state"
"github.com/ipfs/go-cid"
mh "github.com/multiformats/go-multihash"
"github.com/filecoin-project/lotus/blockstore"
"github.com/stretchr/testify/require"
"github.com/filecoin-project/go-state-types/manifest"
"github.com/filecoin-project/go-state-types/network"
"github.com/filecoin-project/specs-actors/v8/actors/util/adt"
"github.com/filecoin-project/lotus/chain/consensus/filcns"
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/itests/kit"
)
func TestLiteMigration(t *testing.T) {
ctx := context.Background()
kit.QuietMiningLogs()
client16, _, ens := kit.EnsembleMinimal(t, kit.MockProofs(), kit.GenesisNetworkVersion(network.Version16))
ens.InterconnectAll().BeginMining(10 * time.Millisecond)
client16.WaitTillChain(ctx, func(set *types.TipSet) bool {
return set.Height() > 100
})
bs := blockstore.NewAPIBlockstore(client16)
ctxStore := adt.WrapBlockStore(ctx, bs)
ts, err := client16.ChainHead(ctx)
require.NoError(t, err)
stateRoot := ts.ParentState()
newManifestCid := makeTestManifest(t, ctxStore)
newStateRoot, err := filcns.LiteMigration(ctx, bs, newManifestCid, stateRoot)
require.NoError(t, err)
stateTree, err := state.LoadStateTree(ctxStore, newStateRoot)
require.NoError(t, err)
systemActor, err := stateTree.GetActor(system.Address)
var newManifest manifest.Manifest
err = ctxStore.Get(ctx, newManifestCid, &newManifest)
require.NoError(t, err)
err = newManifest.Load(ctx, ctxStore)
require.NoError(t, err)
manifestSystemCodeCid, ok := newManifest.Get("system")
require.True(t, ok)
require.Equal(t, systemActor.Code, manifestSystemCodeCid)
}
func makeTestManifest(t *testing.T, ctxStore adt.Store) cid.Cid {
builder := cid.V1Builder{Codec: cid.Raw, MhType: mh.IDENTITY}
manifestData := manifest.ManifestData{}
for _, name := range []string{"system", "init", "cron", "account", "storagepower", "storageminer", "storagemarket", "paymentchannel", "multisig", "reward", "verifiedregistry"} {
codeCid, err := builder.Sum([]byte(fmt.Sprintf("fil/8/%s", name)))
if err != nil {
t.Fatal(err)
}
manifestData.Entries = append(manifestData.Entries,
manifest.ManifestEntry{
Name: name,
Code: codeCid,
})
}
manifestDataCid, err := ctxStore.Put(ctxStore.Context(), &manifestData)
if err != nil {
t.Fatal(err)
}
mf := manifest.Manifest{
Version: 1,
Data: manifestDataCid,
}
manifestCid, err := ctxStore.Put(ctxStore.Context(), &mf)
if err != nil {
t.Fatal(err)
}
return manifestCid
}