diff --git a/chain/stmgr/forks.go b/chain/stmgr/forks.go index ccb6221d7..96cdd1f08 100644 --- a/chain/stmgr/forks.go +++ b/chain/stmgr/forks.go @@ -38,17 +38,70 @@ type UpgradeFunc func(context.Context, *StateManager, ExecCallback, cid.Cid, *ty type Upgrade struct { Height abi.ChainEpoch + Network network.Version Migration UpgradeFunc } -type UpgradeSchedule map[network.Version]Upgrade +type UpgradeSchedule []Upgrade -var DefaultUpgradeSchedule = UpgradeSchedule{ - network.Version1: {build.UpgradeBreezeHeight, UpgradeFaucetBurnRecovery}, - network.Version2: {build.UpgradeSmokeHeight, nil}, - network.Version3: {build.UpgradeIgnitionHeight, UpgradeIgnition}, - network.Version4: {build.UpgradeActorsV2Height, UpgradeActorsV2}, - network.Version5: {build.UpgradeLiftoffHeight, UpgradeLiftoff}, +func DefaultUpgradeSchedule() UpgradeSchedule { + var us UpgradeSchedule + + for _, u := range []Upgrade{{ + Height: build.UpgradeBreezeHeight, + Network: network.Version1, + Migration: UpgradeFaucetBurnRecovery, + }, { + Height: build.UpgradeSmokeHeight, + Network: network.Version2, + Migration: nil, + }, { + Height: build.UpgradeIgnitionHeight, + Network: network.Version3, + Migration: UpgradeIgnition, + }, { + Height: build.UpgradeActorsV2Height, + Network: network.Version4, + Migration: UpgradeActorsV2, + }, { + Height: build.UpgradeLiftoffHeight, + Network: network.Version4, + Migration: UpgradeLiftoff, + }} { + if u.Height < 0 { + // upgrade disabled + continue + } + us = append(us, u) + } + return us +} + +func (us UpgradeSchedule) Validate() error { + // Make sure we're not trying to upgrade to version 0. + for _, u := range us { + if u.Network <= 0 { + return xerrors.Errorf("cannot upgrade to version <= 0: %d", u.Network) + } + } + + // Make sure all the upgrades make sense. + for i := 1; i < len(us); i++ { + prev := &us[i-1] + curr := &us[i] + if !(prev.Network <= curr.Network) { + return xerrors.Errorf("cannot downgrade from version %d to version %d", prev.Network, curr.Network) + } + // Make sure the heights make sense. + if prev.Height < 0 { + // Previous upgrade was disabled. + continue + } + if !(prev.Height < curr.Height) { + return xerrors.Errorf("upgrade heights must be strictly increasing: upgrade %d was at height %d, followed by upgrade %d at height %d", i-1, prev.Height, i, curr.Height) + } + } + return nil } func (sm *StateManager) handleStateForks(ctx context.Context, root cid.Cid, height abi.ChainEpoch, cb ExecCallback, ts *types.TipSet) (cid.Cid, error) { diff --git a/chain/stmgr/forks_test.go b/chain/stmgr/forks_test.go index db65d2485..bb03f13b9 100644 --- a/chain/stmgr/forks_test.go +++ b/chain/stmgr/forks_test.go @@ -115,10 +115,11 @@ func TestForkHeightTriggers(t *testing.T) { t.Fatal(err) } - sm := NewStateManagerWithUpgradeSchedule( - cg.ChainStore(), - UpgradeSchedule{ - 1: {Height: testForkHeight, Migration: func(ctx context.Context, sm *StateManager, cb ExecCallback, + sm, err := NewStateManagerWithUpgradeSchedule( + cg.ChainStore(), UpgradeSchedule{{ + Network: 1, + Height: testForkHeight, + Migration: func(ctx context.Context, sm *StateManager, cb ExecCallback, root cid.Cid, ts *types.TipSet) (cid.Cid, error) { cst := ipldcbor.NewCborStore(sm.ChainStore().Blockstore()) @@ -151,8 +152,10 @@ func TestForkHeightTriggers(t *testing.T) { } return st.Flush(ctx) - }}, - }) + }}}) + if err != nil { + t.Fatal(err) + } inv := vm.NewActorRegistry() inv.Register(nil, testActor{}) diff --git a/chain/stmgr/stmgr.go b/chain/stmgr/stmgr.go index 3efe26f53..91109d9bd 100644 --- a/chain/stmgr/stmgr.go +++ b/chain/stmgr/stmgr.go @@ -39,12 +39,18 @@ import ( var log = logging.Logger("statemgr") +type versionSpec struct { + networkVersion network.Version + atOrBelow abi.ChainEpoch +} + type StateManager struct { cs *store.ChainStore - // Sorted network upgrade epochs (starting at version 1). - // -1 means the version is skipped. - networkVersions []abi.ChainEpoch + // Determines the network version at any given epoch. + networkVersions []versionSpec + latestVersion network.Version + // Maps chain epochs to upgrade functions. stateMigrations map[abi.ChainEpoch]UpgradeFunc @@ -58,39 +64,49 @@ type StateManager struct { } func NewStateManager(cs *store.ChainStore) *StateManager { - return NewStateManagerWithUpgradeSchedule(cs, DefaultUpgradeSchedule) + sm, err := NewStateManagerWithUpgradeSchedule(cs, DefaultUpgradeSchedule()) + if err != nil { + panic(fmt.Sprintf("default upgrade schedule is invalid: %s", err)) + } + return sm } -func NewStateManagerWithUpgradeSchedule(cs *store.ChainStore, us UpgradeSchedule) *StateManager { +func NewStateManagerWithUpgradeSchedule(cs *store.ChainStore, us UpgradeSchedule) (*StateManager, error) { + // If we have upgrades, make sure they're in-order and make sense. + if err := us.Validate(); err != nil { + return nil, err + } + stateMigrations := make(map[abi.ChainEpoch]UpgradeFunc, len(us)) - networkVersions := make([]abi.ChainEpoch, 0, len(stateMigrations)) - - // Iterate version by version, to make sure we handle skipped version numbers. - // Always skip version 0. - for i, version := 0, network.Version(1); i < len(us); version++ { - upgrade, ok := us[version] - if ok { - // We've processed an upgrade. - i++ + var networkVersions []versionSpec + lastVersion := network.Version0 + if len(us) > 0 { + // If we have any upgrades, process them and create a version + // schedule. + for _, upgrade := range us { + if upgrade.Migration != nil { + stateMigrations[upgrade.Height] = upgrade.Migration + } + networkVersions = append(networkVersions, versionSpec{ + networkVersion: lastVersion, + atOrBelow: upgrade.Height, + }) + lastVersion = upgrade.Network } - - epoch := abi.ChainEpoch(-1) - if ok && upgrade.Height >= 0 { - epoch = upgrade.Height - stateMigrations[epoch] = upgrade.Migration - } - - networkVersions = append(networkVersions, epoch) + } else { + // Otherwise, go directly to the latest version. + lastVersion = build.NewestNetworkVersion } return &StateManager{ networkVersions: networkVersions, + latestVersion: lastVersion, stateMigrations: stateMigrations, newVM: vm.NewVM, cs: cs, stCache: make(map[string][]cid.Cid), compWait: make(map[string]chan struct{}), - } + }, nil } func cidsToKey(cids []cid.Cid) string { @@ -1294,13 +1310,12 @@ func (sm *StateManager) GetCirculatingSupply(ctx context.Context, height abi.Cha func (sm *StateManager) GetNtwkVersion(ctx context.Context, height abi.ChainEpoch) network.Version { // The epochs here are the _last_ epoch for every version, or -1 if the // version is disabled. - for v, epoch := range sm.networkVersions { - if epoch >= 0 && height <= epoch { - // Use the _previous_ version - return network.Version(v) + for _, spec := range sm.networkVersions { + if height <= spec.atOrBelow { + return spec.networkVersion } } - return network.Version(len(sm.networkVersions)) + return sm.latestVersion } func (sm *StateManager) GetPaychState(ctx context.Context, addr address.Address, ts *types.TipSet) (*types.Actor, paych.State, error) { diff --git a/go.mod b/go.mod index 50e5aecc1..44dd4acfa 100644 --- a/go.mod +++ b/go.mod @@ -33,12 +33,12 @@ require ( github.com/filecoin-project/go-multistore v0.0.3 github.com/filecoin-project/go-padreader v0.0.0-20200903213702-ed5fae088b20 github.com/filecoin-project/go-paramfetch v0.0.2-0.20200701152213-3e0f0afdc261 - github.com/filecoin-project/go-state-types v0.0.0-20201001162932-93a412d8e9fa + github.com/filecoin-project/go-state-types v0.0.0-20200928172055-2df22083d8ab github.com/filecoin-project/go-statemachine v0.0.0-20200925024713-05bd7c71fbfe github.com/filecoin-project/go-statestore v0.1.0 github.com/filecoin-project/go-storedcounter v0.0.0-20200421200003-1c99c62e8a5b github.com/filecoin-project/specs-actors v0.9.12 - github.com/filecoin-project/specs-actors/v2 v2.0.0-20201001041506-c7ff44a3ce9b + github.com/filecoin-project/specs-actors/v2 v2.0.0-20201002200957-bdd876b3bbe9 github.com/filecoin-project/specs-storage v0.1.1-0.20200907031224-ed2e5cd13796 github.com/filecoin-project/test-vectors/schema v0.0.3 github.com/gbrlsnchs/jwt/v3 v3.0.0-beta.1 diff --git a/go.sum b/go.sum index aa8e3f633..78daf3af2 100644 --- a/go.sum +++ b/go.sum @@ -263,8 +263,6 @@ github.com/filecoin-project/go-state-types v0.0.0-20200904021452-1883f36ca2f4/go github.com/filecoin-project/go-state-types v0.0.0-20200905071437-95828685f9df/go.mod h1:IQ0MBPnonv35CJHtWSN3YY1Hz2gkPru1Q9qoaYLxx9I= github.com/filecoin-project/go-state-types v0.0.0-20200928172055-2df22083d8ab h1:cEDC5Ei8UuT99hPWhCjA72SM9AuRtnpvdSTIYbnzN8I= github.com/filecoin-project/go-state-types v0.0.0-20200928172055-2df22083d8ab/go.mod h1:ezYnPf0bNkTsDibL/psSz5dy4B5awOJ/E7P2Saeep8g= -github.com/filecoin-project/go-state-types v0.0.0-20201001162932-93a412d8e9fa h1:WVxLrmc6w5naeX3QikfRvc1G7EFfBZbhXuzM8Lj2J+o= -github.com/filecoin-project/go-state-types v0.0.0-20201001162932-93a412d8e9fa/go.mod h1:ezYnPf0bNkTsDibL/psSz5dy4B5awOJ/E7P2Saeep8g= github.com/filecoin-project/go-statemachine v0.0.0-20200714194326-a77c3ae20989/go.mod h1:FGwQgZAt2Gh5mjlwJUlVB62JeYdo+if0xWxSEfBD9ig= github.com/filecoin-project/go-statemachine v0.0.0-20200925024713-05bd7c71fbfe h1:dF8u+LEWeIcTcfUcCf3WFVlc81Fr2JKg8zPzIbBDKDw= github.com/filecoin-project/go-statemachine v0.0.0-20200925024713-05bd7c71fbfe/go.mod h1:FGwQgZAt2Gh5mjlwJUlVB62JeYdo+if0xWxSEfBD9ig= @@ -276,8 +274,8 @@ github.com/filecoin-project/specs-actors v0.9.4/go.mod h1:BStZQzx5x7TmCkLv0Bpa07 github.com/filecoin-project/specs-actors v0.9.7/go.mod h1:wM2z+kwqYgXn5Z7scV1YHLyd1Q1cy0R8HfTIWQ0BFGU= github.com/filecoin-project/specs-actors v0.9.12 h1:iIvk58tuMtmloFNHhAOQHG+4Gci6Lui0n7DYQGi3cJk= github.com/filecoin-project/specs-actors v0.9.12/go.mod h1:TS1AW/7LbG+615j4NsjMK1qlpAwaFsG9w0V2tg2gSao= -github.com/filecoin-project/specs-actors/v2 v2.0.0-20201001041506-c7ff44a3ce9b h1:8JRxR0rKdiuCHIDKi7Zbs+/jiygr6eXEX47QXhjX+kA= -github.com/filecoin-project/specs-actors/v2 v2.0.0-20201001041506-c7ff44a3ce9b/go.mod h1:52FuQUNDXq2WDg+6+UOhkqBuNc2e62h9BCIB67Bluxg= +github.com/filecoin-project/specs-actors/v2 v2.0.0-20201002200957-bdd876b3bbe9 h1:UXVcGwUD9GJYNcgfGbf7ok790wrunvUhl5ZlgApGhGs= +github.com/filecoin-project/specs-actors/v2 v2.0.0-20201002200957-bdd876b3bbe9/go.mod h1:52FuQUNDXq2WDg+6+UOhkqBuNc2e62h9BCIB67Bluxg= github.com/filecoin-project/specs-storage v0.1.1-0.20200907031224-ed2e5cd13796 h1:dJsTPWpG2pcTeojO2pyn0c6l+x/3MZYCBgo/9d11JEk= github.com/filecoin-project/specs-storage v0.1.1-0.20200907031224-ed2e5cd13796/go.mod h1:nJRRM7Aa9XVvygr3W9k6xGF46RWzr2zxF/iGoAIfA/g= github.com/filecoin-project/test-vectors/schema v0.0.3 h1:1zuBo25B3016inbygYLgYFdpJ2m1BDTbAOCgABRleiU= diff --git a/node/builder.go b/node/builder.go index c738740ec..d267ac229 100644 --- a/node/builder.go +++ b/node/builder.go @@ -259,7 +259,7 @@ func Online() Option { Override(new(ffiwrapper.Verifier), ffiwrapper.ProofVerifier), Override(new(vm.SyscallBuilder), vm.Syscalls), Override(new(*store.ChainStore), modules.ChainStore), - Override(new(stmgr.UpgradeSchedule), stmgr.DefaultUpgradeSchedule), + Override(new(stmgr.UpgradeSchedule), stmgr.DefaultUpgradeSchedule()), Override(new(*stmgr.StateManager), stmgr.NewStateManagerWithUpgradeSchedule), Override(new(*wallet.Wallet), wallet.NewWallet), Override(new(*messagesigner.MessageSigner), messagesigner.NewMessageSigner),