diff --git a/golang/cosmos/app/app.go b/golang/cosmos/app/app.go index 100e04ed440..cd2f1ba1fea 100644 --- a/golang/cosmos/app/app.go +++ b/golang/cosmos/app/app.go @@ -213,7 +213,7 @@ type GaiaApp struct { // nolint: golint vibcPort int vstoragePort int - upgradePlan *upgradetypes.Plan + upgradeDetails *upgradeDetails invCheckPeriod uint @@ -828,8 +828,21 @@ func NewAgoricApp( func upgrade13Handler(app *GaiaApp, targetUpgrade string) func(sdk.Context, upgradetypes.Plan, module.VersionMap) (module.VersionMap, error) { return func(ctx sdk.Context, plan upgradetypes.Plan, fromVm module.VersionMap) (module.VersionMap, error) { app.CheckControllerInited(false) - // Record the plan to send to SwingSet - app.upgradePlan = &plan + + // Each CoreProposalStep runs sequentially, and can be constructed from + // one or more modules executing in parallel within the step. + CoreProposalSteps := []vm.CoreProposalStep{ + // vm.CoreProposalStepForModules("@agoric/builders/scripts/vats/init-network.js"), + } + + app.upgradeDetails = &upgradeDetails{ + // Record the plan to send to SwingSet + Plan: plan, + // Core proposals that should run during the upgrade block + // These will be merged with any coreProposals specified in the + // upgradeInfo field of the upgrade plan ran as subsequent steps + CoreProposals: vm.CoreProposalsFromSteps(CoreProposalSteps...), + } // Always run module migrations mvm, err := app.mm.RunMigrations(ctx, app.configurator, fromVm) @@ -864,18 +877,23 @@ func normalizeModuleAccount(ctx sdk.Context, ak authkeeper.AccountKeeper, name s ak.SetModuleAccount(ctx, newAcct) } +type upgradeDetails struct { + Plan upgradetypes.Plan `json:"plan"` + CoreProposals *vm.CoreProposals `json:"coreProposals,omitempty"` +} + type cosmosInitAction struct { vm.ActionHeader `actionType:"AG_COSMOS_INIT"` - ChainID string `json:"chainID"` - IsBootstrap bool `json:"isBootstrap"` - Params swingset.Params `json:"params"` - SupplyCoins sdk.Coins `json:"supplyCoins"` - UpgradePlan *upgradetypes.Plan `json:"upgradePlan,omitempty"` - LienPort int `json:"lienPort"` - StoragePort int `json:"storagePort"` - SwingsetPort int `json:"swingsetPort"` - VbankPort int `json:"vbankPort"` - VibcPort int `json:"vibcPort"` + ChainID string `json:"chainID"` + IsBootstrap bool `json:"isBootstrap"` + UpgradeDetails *upgradeDetails `json:"upgradeDetails,omitempty"` + Params swingset.Params `json:"params"` + SupplyCoins sdk.Coins `json:"supplyCoins"` + LienPort int `json:"lienPort"` + StoragePort int `json:"storagePort"` + SwingsetPort int `json:"swingsetPort"` + VbankPort int `json:"vbankPort"` + VibcPort int `json:"vibcPort"` } // Name returns the name of the App @@ -900,22 +918,22 @@ func (app *GaiaApp) initController(ctx sdk.Context, bootstrap bool) { // Begin initializing the controller here. action := &cosmosInitAction{ - ChainID: ctx.ChainID(), - IsBootstrap: bootstrap, - Params: app.SwingSetKeeper.GetParams(ctx), - SupplyCoins: sdk.NewCoins(app.BankKeeper.GetSupply(ctx, "uist")), - UpgradePlan: app.upgradePlan, - LienPort: app.lienPort, - StoragePort: app.vstoragePort, - SwingsetPort: app.swingsetPort, - VbankPort: app.vbankPort, - VibcPort: app.vibcPort, + ChainID: ctx.ChainID(), + IsBootstrap: bootstrap, + Params: app.SwingSetKeeper.GetParams(ctx), + SupplyCoins: sdk.NewCoins(app.BankKeeper.GetSupply(ctx, "uist")), + UpgradeDetails: app.upgradeDetails, + LienPort: app.lienPort, + StoragePort: app.vstoragePort, + SwingsetPort: app.swingsetPort, + VbankPort: app.vbankPort, + VibcPort: app.vibcPort, } // This uses `BlockingSend` as a friendly wrapper for `sendToController` // // CAVEAT: we are restarting after an in-consensus halt or just because this // node felt like it. The controller must be able to handle either case - // (inConsensus := action.IsBootstrap || action.UpgradePlan != nil). + // (inConsensus := action.IsBootstrap || action.UpgradeDetails != nil). out, err := app.SwingSetKeeper.BlockingSend(ctx, action) // fmt.Fprintf(os.Stderr, "AG_COSMOS_INIT Returned from SwingSet: %s, %v\n", out, err) diff --git a/golang/cosmos/vm/core_proposals.go b/golang/cosmos/vm/core_proposals.go new file mode 100644 index 00000000000..44aa9bd83f9 --- /dev/null +++ b/golang/cosmos/vm/core_proposals.go @@ -0,0 +1,31 @@ +package vm + +// CoreProposalStep is a set of core proposal configs which are executed +// concurrently +type CoreProposalStep []Jsonable + +// CoreProposals is one possible shape for core proposals expressed as a series +// of sequential steps +// see SequentialCoreProposals in packages/deploy-script-support/src/extract-proposal.js +type CoreProposals struct { + Steps []CoreProposalStep `json:"steps"` +} + +// CoreProposalStepForModules generates a single core proposal step from +// the given modules, which will be executed concurrently during that step +func CoreProposalStepForModules(modules ...string) CoreProposalStep { + step := make([]Jsonable, len(modules)) + for i := range modules { + step[i] = modules[i] + } + return step +} + +// CoreProposalsFromSteps returns a CoreProposals from the given steps +func CoreProposalsFromSteps(steps ...CoreProposalStep) *CoreProposals { + if steps == nil { + // Deal with https://github.com/golang/go/issues/37711 + return &CoreProposals{Steps: []CoreProposalStep{}} + } + return &CoreProposals{Steps: steps} +} diff --git a/packages/cosmic-swingset/src/launch-chain.js b/packages/cosmic-swingset/src/launch-chain.js index fda1ce6d002..c8468a87093 100644 --- a/packages/cosmic-swingset/src/launch-chain.js +++ b/packages/cosmic-swingset/src/launch-chain.js @@ -877,30 +877,34 @@ export async function launch({ // ); switch (action.type) { case ActionType.AG_COSMOS_INIT: { - const { blockHeight, isBootstrap, upgradePlan } = action; + const { blockHeight, isBootstrap, upgradeDetails } = action; if (!blockNeedsExecution(blockHeight)) { return true; } - let { coreProposals } = parseUpgradePlanInfo( - upgradePlan, - ActionType.AG_COSMOS_INIT, - ); + const softwareUpgradeCoreProposals = upgradeDetails?.coreProposals; + + const { coreProposals: upgradeInfoCoreProposals } = + parseUpgradePlanInfo(upgradeDetails?.plan, ActionType.AG_COSMOS_INIT); if (isBootstrap) { // This only runs for the very first block on the chain. await doBootstrap(action); - - // Merge the core proposals from the bootstrap block with the - // ones from the upgrade plan. - coreProposals = mergeCoreProposals( - bootstrapCoreProposals, - coreProposals, - ); } - if (coreProposals) { + // Merge the core proposals from the bootstrap block with the + // ones from the upgrade. + const coreProposals = mergeCoreProposals( + bootstrapCoreProposals, + softwareUpgradeCoreProposals, + upgradeInfoCoreProposals, + ); + + if (coreProposals.steps.length) { + upgradeDetails || + isBootstrap || + Fail`Unexpected core proposals outside of consensus start`; await doCoreProposals(action, coreProposals); } return true; diff --git a/packages/deploy-script-support/src/extract-proposal.js b/packages/deploy-script-support/src/extract-proposal.js index 185396ef229..b200253d74a 100644 --- a/packages/deploy-script-support/src/extract-proposal.js +++ b/packages/deploy-script-support/src/extract-proposal.js @@ -15,7 +15,8 @@ import { */ /** - * @typedef {ConfigProposal[] | {steps: ConfigProposal[][]}} CoreProposals + * @typedef {{steps: ConfigProposal[][]}} SequentialCoreProposals + * @typedef {ConfigProposal[] | SequentialCoreProposals} CoreProposals */ const { Fail } = assert; @@ -24,7 +25,7 @@ const req = createRequire(import.meta.url); /** * @param {...(CoreProposals | undefined | null)} args - * @returns {CoreProposals} + * @returns {SequentialCoreProposals} */ export const mergeCoreProposals = (...args) => { /** @type {ConfigProposal[][]} */