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

design better vat-facing upgrade protocol (buildRootObject()? upgrade()?) #4382

Open
warner opened this issue Jan 25, 2022 · 5 comments
Open
Labels
enhancement New feature or request needs-design SwingSet package: SwingSet

Comments

@warner
Copy link
Member

warner commented Jan 25, 2022

What is the Problem Being Solved?

We need to define how a new version of a vat is launched.

We expect the initial version of a vat to export a function named buildRootObject, which is called exactly once during that version's lifetime, and is supposed to return a single remotable object. The calling vat will get this object as root in the result of vatAdminService~.createVat(), so it can start interacting with its creation.

However we haven't yet defined what the second (and subsequent) versions of a vat must do, when triggered by vatAdminFacet~.upgrade(newBundleCap, newVatParameters). We know there will be some well-defined calling pattern, with either one function being exported, or one per version upgrade. This function will get vatPowers just like buildRootObject, and it will get a vatParameters from the upgrading caller. The function must reassociate all the virtual-object Kinds that were created by the earlier version. I expect it must also reassociate vrefs for any non-virtual "precious" Remotables that were exported by its predecessor (but we might dodge that by instructing vat authors to use virtual objects even for singletons).

#1848 is about what you (as a calling vat, e.g. Zoe) would use to tell the kernel about some other vat that you want to upgrade. This ticket is about what happens within the upgraded vat.

The questions we need to answer include:

  • What function (or functions) should each version export?
    • maybe each version exports buildRootObject
    • maybe only version-1 uses buildRootObject, and version-2++ provides upgrade or upgradeRootObject
    • maybe each version must export a delta-specific upgrade function, e.g. upgradeV1ToV2 and upgradeV2ToV3
    • or a target-version -specific function: upgradeToV2, upgradeToV3
    • this has deep consequences for how vat authors organize their source code repository, and how they think about code edits/commits vs deployment/upgrade
  • Can vats reassociate arbitrary exported Remotables, or just the root object?
    • What combination of authorities are necessary to perform this reassociation? Per-object/obligation handles in baggage, plus a reassociation facility in vatPowers?
    • Ideally this should parallel the way makeKind reassociation is performed
    • If there are remaining exports (those not reassociated by the end of the initial crank), should that flunk the upgrade process? Or should they be disowned by the vat? Or should liveslots "ground" them by wiring them (locally) to error-producing dead-ends?

Description of the Design

The simplest option is to just keep using buildRootObject in all versions of a vat. The return value from the version-2++ function would be reassociated with vat's o+0 export, as if the function had used the reassociation API directly (and had some handle which gave it the authority to claim o+0). We'd disown or ground all other exported remotables (except TODO how could liveslots enumerate them??). Vat authors would need to use a pattern where the first thing buildRootObject does is to use vatstore to find out what the previous version was (where "nothing was stored" means this is the first time), and then switch behavior as appropriate. If there was no previous version, the code should initialize using the latest version. Otherwise it will need to upgrade from saved state of some particular version. Depending upon how quickly versions are deployed, it may need to be prepared to handle any previous version.

The next most featureful thing I can think of would be to keep using buildRootObject but provide a way to reassociate more Remotables than just the root. This option would imply that the root object is special enough to warrant a special-case upgrade tool (return the new root object, instead of call a reassociation function with a handle for it), which is a bit non-orthogonal but maybe ok.

The next most principled thing I can think of is to use upgrade(vatPowers, vatParameters) for version-2 and all subsequent versions. This would not have any special return value (it returns a Promise, and the first crank isn't complete until it fires, but the resolution value isn't used for anything). Reassociating the root object must be done with a reassociation function (from vatPowers) and some handle (from vatstore), just like all the non-root objects. In a mature vat source repository that has seen many upgrades, you'd see two exports: buildRootObject to initialize new vats, and upgrade to upgrade old ones. Both would get modified for each new version.

The approach with the fewest footguns, in my mind, would be to explicitly break out all the upgrade stages into separately exported functions, and allowing the calling code to know what version number is used for each (so vatAdminFacet~.upgrade({ toVersion: 3, bundlecap, vatParameters })). The calling code would invoke just the new vat's upgradeV2ToV3 method, skipping previous ones. If the vat were being upgraded from v1 to v4 in a single shot, the calling code would invoke the upgraders in sequence. It would be unwieldy, but I suspect it would cause fewer mistakes.

Security Considerations

Test Plan

@mhofman
Copy link
Member

mhofman commented Feb 8, 2022

I need to fully read the issue, but my thinking was a single buildRootObject, and let code figure out from both it's baggage (saved state) and current code (version const or whatever it decide), what to do on start. We could potentially include some info as a param to buildRootObject to help if the contract forgot to version its baggage, but I'm not convinced it's necessary.

This would also unify the case of sublibraries and sub baggage.

@warner warner removed their assignment Feb 22, 2022
FUDCo added a commit that referenced this issue Mar 19, 2022
This implements the primary vat-side interface for upgrade (#4325, #4382).
However, it does not yet implement the set of post upgrade checks (#3062) that
will be required (e.g., check upon return from `buildRootObject` to verify that
all the durable kinds have had behavior associated with them or have been
explicitly dismissed); the ability to test or otherwise exercise the latter
portion of the upgrade machinery will have to wait until the kernel-side
interfaces (#1848, #3062) are in place.

I am going to tentative declarely that this PR closes #4325, leaving the
remaining work to be covered by #3062.
FUDCo added a commit that referenced this issue Mar 20, 2022
This implements the primary vat-side interface for upgrade (#4325, #4382).
However, it does not yet implement the set of post upgrade checks (#3062) that
will be required (e.g., check upon return from `buildRootObject` to verify that
all the durable kinds have had behavior associated with them or have been
explicitly dismissed); the ability to test or otherwise exercise the latter
portion of the upgrade machinery will have to wait until the kernel-side
interfaces (#1848, #3062) are in place.

I am going to tentative declarely that this PR closes #4325, leaving the
remaining work to be covered by #3062.
FUDCo added a commit that referenced this issue Mar 21, 2022
This implements the primary vat-side interface for upgrade (#4325, #4382).
However, it does not yet implement the set of post upgrade checks (#3062) that
will be required (e.g., check upon return from `buildRootObject` to verify that
all the durable kinds have had behavior associated with them or have been
explicitly dismissed); the ability to test or otherwise exercise the latter
portion of the upgrade machinery will have to wait until the kernel-side
interfaces (#1848, #3062) are in place.

I am going to tentative declarely that this PR closes #4325, leaving the
remaining work to be covered by #3062.
@Tartuffo Tartuffo added this to the Mainnet 1 milestone Mar 23, 2022
@FUDCo
Copy link
Contributor

FUDCo commented Apr 26, 2022

@warner As with the other upgrade issues, aside from contracts (which are outside the scope of SwingSet per se), can we treat this as done?

@FUDCo
Copy link
Contributor

FUDCo commented Apr 29, 2022

Kicking out to Mainnet 2 pending a more thorough conversation about upgrade of third party contracts.

@FUDCo FUDCo removed this from the Mainnet 1 milestone Apr 29, 2022
@Tartuffo
Copy link
Contributor

@warner Please either close this or remove this from the upgrade Epic.

@warner
Copy link
Member Author

warner commented May 11, 2022

For MN-1, we're proceeding with the simple plan: buildRootObject() is called once at the beginning of each version of the vat, it uses baggage` to communicate between versions, and everything else is left up to the vat.

For MN-2 (especially before external vat/contract developers are exposed to this pattern), I'd like to revisit it. I'm hoping we'll have some more experience with the practicalities of managing the upgraded source code by then, to inform the discussion.

@warner warner changed the title define vat-facing upgrade protocol (buildRootObject()? upgrade()?) design better vat-facing upgrade protocol (buildRootObject()? upgrade()?) May 11, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request needs-design SwingSet package: SwingSet
Projects
None yet
Development

No branches or pull requests

4 participants