diff --git a/packages/SwingSet/docs/static-vats.md b/packages/SwingSet/docs/static-vats.md index 0c90050606e7..e1ea39001f71 100644 --- a/packages/SwingSet/docs/static-vats.md +++ b/packages/SwingSet/docs/static-vats.md @@ -14,12 +14,14 @@ Static vats are defined by a JS module file which exports a function named `buil The `buildRootObject` function will be called with one object, named `vatPowers`. The contents of `vatPowers` are subject to change, but in general it provides pure functions which are inconvenient to access as imports, and vat-specific authorities that are not easy to express through syscalls. See below for the current property list. -`buildRootObject` is expected to return a hardened object with callable methods and no data properties (note that `harden` is available as a global). For example: +`buildRootObject` is expected to return a hardened "Remotable" object with callable methods and no data properties. The best way to do this is with the `Far` function: ```js +import { Far } from '@endo/far'; + export function buildRootObject(vatPowers) { let counter = 0; - return harden({ + return Far('root', { increment() { counter += 1; }, @@ -30,6 +32,8 @@ export function buildRootObject(vatPowers) { } ``` +The root object *must* be an "ephemeral" object, i.e. created with `Far`. It cannot be a virtual or durable object (created with a maker returned by `defineKind` or `defineDurableKind`, or the vat-data convenience wrappers like `prepareSingleton`). This ensures that the root object's identity is stable across upgrade. + Each vat has a name. A *Presence* for this vat's root object will be made available to the bootstrap function, in its `vats` argument. For example, if this vat is named `counter`, then the bootstrap function could do: ```js diff --git a/packages/SwingSet/docs/vat-upgrade.md b/packages/SwingSet/docs/vat-upgrade.md index 686968da07a9..e934e14e75eb 100644 --- a/packages/SwingSet/docs/vat-upgrade.md +++ b/packages/SwingSet/docs/vat-upgrade.md @@ -27,7 +27,7 @@ Vat upgrade is triggered by an `upgrade()` message to the vat's "adminNode" cont business * messages from other vats start to arrive at the v2 code -The first time the v2 code is invoked is called the "upgrade phase", and represents a limited window of time (a single crank) during which v2 must perform a number of tasks. The v2 code can use Promises to defer work into the (very) near future, however all this work must be complete by the time the promise queue is drained. +The first time the v2 code is invoked is called the "upgrade phase", and represents a limited window of time (a single crank) during which v2 must perform a number of tasks. The v2 code can use Promises to defer work into the (very) near future, however all this work must be complete by the time the promise queue is drained. This means `buildRootObject` may not `await` messages sent off-vat, because their responses cannot return before the initial `startVat` delivery is complete. If a large number of data records need updating, the v2 code can (and should) use lazy/on-demand data migration, to avoid doing too much work in a single crank. However, the new vat only has a single upgrade-phase crank to prepare for incoming messages. So any lazy migration must be prepared to handle arbitrary messages despite the migration not being complete. @@ -63,6 +63,8 @@ During the upgrade phase, v2 code is obligated to re-define all durable Kinds cr As a special case, the root object returned from v2's `buildRootObject()` is automatically associated with exportID `o+0` (see [How Liveslots Uses the Vatstore](../../swingset-liveslots/src/vatstore-usage.md#counters)) and is therefore also obligated to support the same methods as its predecessor. This means that the root object is effectively always durable, and should not be explicitly persisted. +To be precise, the root object *must* be an "ephemeral" object, i.e. created with `Far` or `makeExo()`. It cannot be a virtual or durable object (created with a maker returned by `defineKind` or `defineDurableKind`, or the vat-data convenience wrappers like `prepareExo` or `prepareSingleton`). This ensures that the root object's identity is stable across upgrades. + ### Zone API The [zone API](https://github.com/Agoric/agoric-sdk/tree/master/packages/zone#readme) provides a unified model for creating the objects mentioned above, regardless of their backing storage: @@ -196,7 +198,7 @@ An important property of `options` is `vatParameters`. This value is passed to t The v2 code wakes up inside the upgrade phase when `buildRootObject(vatPowers, vatParameters, baggage)` is called, where `vatParameters` will come from the call to `upgrade`. This `buildRootObject()` is expected to return an object, or a Promise that resolves to an object, and that object will assume the identity of the root object. -Before completion of `buildRootObject()` is indicated by either returning a non-promise or by fulfilling a returned promise, the v2 code is obligated to redefine every Kind that was created by the v1 code. If any durable Kinds are defined incompletely or left undefined by the time of that indication, the upgrade fails and the vat is rolled back to v1. +Before completion of `buildRootObject()` is indicated (either by returning a non-promise or by fulfilling a returned promise), the v2 code is obligated to redefine every Kind that was created by the v1 code. If any durable Kinds are defined incompletely or left undefined by the time of that indication, the upgrade fails and the vat is rolled back to v1. ```js import { M } from '@agoric/store'; @@ -206,10 +208,6 @@ const FooI = M.interface('foo', fooMethodGuards); const makeFoo = prepareExoClass(someDurableMap, 'foo', fooI, initFoo, fooMethods); ``` -It also needs to reattach every singleton `Far()` object exported by the v1 code. - -When `buildRootObject()` finishes and the upgrade phase completes successfully, the kernel will reject all Promises that v1 had exported (specifically all promises for which v1 was the "decider"). It will terminate any non-durable exports made by v1, and external vats which imported those objects will find themselves holding a broken reference (i.e. every message sent to it will be rejected with an Error, just as if they were exported by a vat which was then terminated). - -TBD: we might terminate any Durable exported objects which v2 does not reattach, or we might treat that as an error. +When `buildRootObject()` finishes and the upgrade phase completes successfully, the kernel will reject all Promises that v1 had exported (specifically all promises for which v1 was the "decider"). It will abandon any non-durable exports made by v1, and external vats which imported those objects will find themselves holding a broken reference (i.e. every message sent to it will be rejected with an Error, just as if they were exported by a vat which was then terminated). -(TODO) If the v2 code experiences an error during the upgrade phase, the entire upgrade is aborted and the v1 code is reinstated. +If the v2 code experiences an error during the upgrade phase, the entire upgrade is aborted and the v1 code is reinstated. The caller of `E(adminNode).upgrade()` will observe their result promise get rejected. diff --git a/packages/SwingSet/src/vats/vattp/vat-vattp.js b/packages/SwingSet/src/vats/vattp/vat-vattp.js index dc010342841a..0a213150f726 100644 --- a/packages/SwingSet/src/vats/vattp/vat-vattp.js +++ b/packages/SwingSet/src/vats/vattp/vat-vattp.js @@ -6,9 +6,9 @@ import { provideDurableMapStore, provideDurableSetStore, provideKindHandle, - prepareSingleton, } from '@agoric/vat-data'; import { E } from '@endo/eventual-send'; +import { Far } from '@endo/marshal'; // See ../../docs/delivery.md for a description of the architecture of the // comms system. @@ -28,7 +28,6 @@ export function buildRootObject(vatPowers, _vatParams, baggage) { const { D } = vatPowers; // Define all durable baggage keys and kind handles. - const serviceSingletonBaggageKey = 'vat-tp handler'; const mailboxDeviceBaggageKey = 'mailboxDevice'; const mailboxHandle = provideKindHandle(baggage, 'mailboxHandle'); const mailboxMapBaggageKey = 'mailboxes'; @@ -285,8 +284,8 @@ export function buildRootObject(vatPowers, _vatParams, baggage) { }, }; - // Expose a durable service singleton. - return prepareSingleton(baggage, serviceSingletonBaggageKey, { + // Expose the service + return Far('vat-tp handler', { ...serviceMailboxFunctions, ...serviceNetworkFunctions, }); diff --git a/packages/swingset-liveslots/docs/liveslots.md b/packages/swingset-liveslots/docs/liveslots.md index 08978a36b7d3..44bdf6517042 100644 --- a/packages/swingset-liveslots/docs/liveslots.md +++ b/packages/swingset-liveslots/docs/liveslots.md @@ -29,6 +29,8 @@ See `static-vats.md`, `dynamic-vats.md`, and `vat-environment.md` in this direct This function returns the "root object". A remote reference to it will be made available to the bootstrap vat, which can use it to trigger whatever initialization needs to happen. +The root object *must* be an "ephemeral" object, i.e. created with `Far`. It cannot be a virtual or durable object (created with a maker returned by `defineKind` or `defineDurableKind`, or the vat-data convenience wrappers like `prepareSingleton`). This ensures that the root object's identity is stable across upgrade. + ## Returning New Objects ```js diff --git a/packages/swingset-liveslots/src/liveslots.js b/packages/swingset-liveslots/src/liveslots.js index a26424ffe951..f44473787894 100644 --- a/packages/swingset-liveslots/src/liveslots.js +++ b/packages/swingset-liveslots/src/liveslots.js @@ -1437,6 +1437,10 @@ function build( ); getInterfaceOf(rootObject) !== undefined || Fail`buildRootObject() for vat ${forVatID} returned ${rootObject} with no interface`; + if (valToSlot.has(rootObject)) { + Fail`buildRootObject() must return ephemeral, not virtual/durable object`; + } + // Need to load watched promises *after* buildRootObject() so that handler kindIDs // have a chance to be reassociated with their handlers. watchedPromiseManager.loadWatchedPromiseTable(unmeteredRevivePromise);