Skip to content

Commit

Permalink
docs(zone): add some documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
michaelfig committed Mar 20, 2023
1 parent 570793d commit 63e0b89
Show file tree
Hide file tree
Showing 2 changed files with 84 additions and 1 deletion.
44 changes: 43 additions & 1 deletion packages/SwingSet/docs/vat-upgrade.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,22 +43,30 @@ As vats run, they send and receive messages. When these messages carry object re

Each export represents an obligation. Other vats might send a message to the exported object, or they might send a message that references the exported object, and we must be prepared to handle it. So the primary job of the v2 vat code is to satisfy all the obligations incurred by v1. Another way to express this is that the kernel c-list, which maps kernel "krefs" to vat "vrefs" for each object, must be satisfied: every vref must be reassociated somehow, or marked as broken.

There are three basic categories of exports (cf. [A Taxonomy of Exo-making Functions](https://github.com/endojs/endo/blob/master/packages/exo/docs/exo-taxonomy.md#heap-vs-virtual-vs-durable)):
There are three basic categories of exports (cf. [A Taxonomy of Exo-making Functions](https://github.com/endojs/endo/blob/HEAD/packages/exo/docs/exo-taxonomy.md#heap-vs-virtual-vs-durable)):

* Heap objects in vat RAM
* zone API found at `import { ephemeralZone } from '@agoric/zone';`
* one-off objects created with `Far()` or `makeExo()`
* instances created with a "make" function from `defineExoClass()`
* multifaceted kit instances created with a "makeKit" function from `defineExoClassKit()`

* Virtual objects in disk-based storage
* zone API found at `import { virtualZone } from '@agoric/zone/virtual.js';`
* instances created with a "make" function from `defineVirtualExoClass()`
* multifaceted kit instances created with a "makeKit" function from `defineVirtualExoClassKit()`

* Durable objects in disk-based storage
* zone API maker found at `import { makeDurableZone } from '@agoric/zone/durable.js';` and zone API created by `makeDurableZone(baggage)`
* instances created with a "make" function from `defineDurableExoClass()` or `prepareExoClass()`
* multifaceted kit instances created with a "makeKit" function from `defineDurableExoClassKit()` or `prepareExoClassKit()`
* singleton objects created with `prepareExo()`

* objects created via a zone API
* singleton objects created with `zone.exo()`
* instances created with a "make" function from `zone.exoClass()`
* multifaceted kit instances created with a "makeKit" function from `zone.exoClassKit()`

During the upgrade phase, v2 code is obligated to re-define all durable Kinds created by v1 (i.e., those associated with objects in the third category) with the same facets and methods or a superset thereof. Once complete, this allows liveslots to satisfy deserialization of any inbound message addressed to (or referencing) a previously-exported durable object.

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.
Expand All @@ -70,6 +78,13 @@ The v2 code runs in a brand new JavaScript environment; nothing is carried over
Vat code has access to three categories of collection objects, each of which offers both Map and Set collections in both strong and weak forms. The simplest category consists of "_heap_" collections provided by JavaScript as `Map`, `Set`, `WeakMap`, and `WeakSet`; their data is held only in RAM.
The second two categories are both referred to as "[Stores](../../swingset-liveslots/src/vatstore-usage.md#virtualdurable-collections-aka-stores)"; they are created by `makeScalarBigMapStore()`, `makeScalarBigWeakMapStore()`, `makeScalarBigSetStore()`, or `makeScalarBigWeakSetStore()`, and their contents are held in disk-based storage. What differentiates the second two categories from each other is use of the `durable` option: when it is false, the collection is "_[merely-]virtual_" and not preserved across upgrade, but when it is true, the collection is "_durable_" and **is** preserved. Durable collections can only hold durable objects.

The zone API exposes providers for these collections as `zone.mapStore(label)`,
`zone.setStore(label)`, `zone.weakMapStore(label)`, and
`zone.weakSetStore(label)`. They only create a new collection if the `label`
entry in the zone has not been used before. If you want to unconditionally
create a fresh, unnamed collection in the zone, you can use the providers
exposed under `zone.detached()`, such as `zone.detached().mapStore(label)`.

Heap and merely-virtual collections are _ephemeral_ and discarded during upgrade. More precisely, the v2 code has no way to reach anything but durable data, so even if the kernel did not delete the DB records, the v2 code could not ever read them.

The v2 code gets exactly one special object during the upgrade phase, currently known as "baggage". This is a durable Map (i.e., the kind of object returned from `makeScalarBigMapStore('label', { durable: true })`). All versions get access to the baggage: the v1 code should add data to it, so that the v2 code can read it back out. This provides the bridge between versions that allows v2 to assume responsibility for the obligations created by v1. It also provides a way for v1 to deliver authorities (in the form of imported object references) to v2, so v2 can talk to the world as if it were v1.
Expand Down Expand Up @@ -107,6 +122,16 @@ const FooI = M.interface('foo', fooMethodGuards);
const makeFoo = prepareExoClass(someDurableMap, 'foo', fooI, initFoo, fooMethods);
```

or with the zone API:

```js
import { M, makeDurableZone } from '@agoric/zone';
const FooI = M.interface('foo', fooMethodGuards);
// someDurableMap should generally be reachable from baggage.
const zone = makeDurableZone(someDurableMap);
const makeFoo = zone.exoClass('foo', fooI, initFoo, fooMethods);
```

The v1 code can also store imported objects (Presences) and plain data in a durable collection. Durable collections are themselves durable objects, so they can be nested:

```js
Expand All @@ -116,6 +141,13 @@ const childMap = makeScalarBigMapStore(childLabel, { durable: true });
parentMap.init('child', childMap);
```

or with the zone API:

```js
const parentMap = makeDurableZone(baggage).mapStore('parent');
const parentZone = makeDurableZone(parentMap).mapStore('child');
```

The "baggage" is a special instance of `makeScalarBigMapStore`, with backing data is stored in a well-known per-vat location so each version can be given a reference. For every piece of data that v1 wrote into the baggage, v2 can read an equivalent item from the baggage it receives. However, any data associated with such items that v1 did _not_ write into baggage is lost -- v2 is a distinct process from v1, with its own independent heap and virtual memory.

While the baggage can use any suitable keys, at least one of the baggage keys should be a piece of plain data such as a string. The v2 vat starts out with nothing but baggage and a pile of source code, and source code can carry strings but not object references. So to allow v2 to get started, it must be able to do at least one `baggage.get('string')`. The value it retrieves from that initial call can be a durable object, which then might be usable as a second key. But without at least one plain-data key, v2 won't be able to extract anything from the baggage.
Expand All @@ -131,6 +163,16 @@ initializeFoo(fooData);
initializeBar(barData);
```

or with the zone API:

```js
const zone = makeDurableZone(baggage);
const fooData = zone.mapStore('foo data');
const barData = zone.mapStore('bar data');
initializeFoo(fooData);
initializeBar(barData);
```

## From Outside: the AdminNode Upgrade API

An upgrade is triggered by invoking the `upgrade` method of the vat's "adminNode" control facet. This facet is returned when the vat is first created (along with the vat's root object), so the original creator is often the initiator of upgrades later. But a common pattern is to hand that control facet to some sort of governance object. The ability to upgrade a vat is also the ability to control its behavior, and any users who expect some particular vat behavior (e.g. when auditing some contract code) must take into account the possibility of upgrade, which means they'll care about who exactly can cause an upgrade to occur.
Expand Down
41 changes: 41 additions & 0 deletions packages/zone/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Zones

Each Zone provides an API that allows the allocation of Exo objects and Stores
(object collections) which use the same underlying persistence mechanism. This
allows library code to be agnostic to whether its objects are backed purely by
the JS heap (ephemeral), pageable out to disk (virtual) or can be revived after
a vat upgrade (durable).

See (SwingSet vat upgrade documentation)[../SwingSet/docs/vat-upgrade.md] for more details around the use of the zone API.

An example of making a Zone-aware vat might look something like this:

```js
import { makeDurableZone } from '@agoric/zone/durable.js';
import { zoneFrobulator } from 'frob-package';
import { zoneWidget } from 'widget-package';

export const buildRootObject = (vatPowers, _args, baggage) => {
const zone = makeDurableZone(baggage);

// Ensure that Widgets cannot interfere with Frobs.
const makeWidget = zoneWidget(zone.subZone('Widgets'));

// Create a collection of frobulators.
const frobZone = zone.subZone('Frobs');
const makeFrobulator = zoneFrobulator(frobZone);
const widgetToFrob = frobZone.mapStore('widgetToFrob');

return Far('WidgetFrobulator', {
makeWidget,
registerWidget(w) {
const frobulator = makeFrobulator();
widgetToFrob.init(w, frobulator);
},
frobWidget(w) {
const frobulator = widgetToFrob.get(w);
return frobulator.frob(w);
},
});
}
```

0 comments on commit 63e0b89

Please sign in to comment.