Skip to content

Commit

Permalink
feat(swingset): add vatAdminService.getBundleIDByName()
Browse files Browse the repository at this point in the history
This API takes the name of a statically-configured bundle (i.e. a
property name of `config.bundles`) and returns its BundleID (a hash
string). This can be used later in `E(zoe).installBundleID(id)`.

refs #4374 , specifically this API will allow bootstrap to deal with
bundleIDs and bundleCaps rather than bundles.
  • Loading branch information
warner committed May 26, 2022
1 parent 5670b41 commit 1ab58fc
Show file tree
Hide file tree
Showing 5 changed files with 34 additions and 1 deletion.
3 changes: 2 additions & 1 deletion packages/SwingSet/docs/bundles.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ The swingset kernel maintains a "bundle table" in the kernel database. Bundles c

When defining a static vat in the Swingset `config.vats` object, the filename provided as `sourceSpec` is turned into a bundle, the bundle is installed into the bundle table, and resulting bundle ID is stored in the vat's database record. When the static vat is launched, the DB record provides the bundle ID, and the bundle is loaded and evaluated in a new vat worker.

The `config.bundles` object maps names to a bundle specification. These bundles are installed as above, and then a special "named bundles" table is updated with the name-to-bundle-ID mapping. These names are available to `E(vatAdminService).getNamedBundleCap(name) -> bundleCap`. For example, the chain's "core bootstrap" code will use this to define bundles for the core vats (Zoe, etc), and create dynamic vats at bootstrap time from them. It will also provide Zoe with the bundlecap for ZCF this way, so Zoe can later create dynamic ZCF vats. `E(vatAdminService).createVatByName(name)` will continue to be supported until core-bootstrap is updated to retrieve bundlecaps, after which vat-admin will drop `createVatByName` and only support `createVat(bundleCap)`.
The `config.bundles` object maps names to a bundle specification. These bundles are installed as above, and then a special "named bundles" table is updated with the name-to-bundle-ID mapping. These names are available to `E(vatAdminService).getNamedBundleCap(name) -> bundleCap` and `E(vatAdminService).getBundleIDByName(name) -> bundleID`. For example, the chain's "core bootstrap" code will use this to define bundles for the core vats (Zoe, etc), and create dynamic vats at bootstrap time from them. It will also provide Zoe with the bundlecap for ZCF this way, so Zoe can later create dynamic ZCF vats. `E(vatAdminService).createVatByName(name)` will continue to be supported until core-bootstrap is updated to retrieve bundlecaps, after which vat-admin will drop `createVatByName` and only support `createVat(bundleCap)`.

The `initializeSwingset()` function, called when the host application is first configured, creates bundles for built-in vats and devices (timer, vatAdmin, mailbox), as well as liveslots and the kernel, and installs them into the table as well. Internally, the kernel remembers the bundle ID of each one for later use.

Expand All @@ -71,6 +71,7 @@ Bundlecaps can be obtained from several methods of `vatAdminService`, which (as
* `E(vatAdminService).getBundleCap(bundleID) -> Promise<BundleCap>` (rejects if not installed yet)
* `E(vatAdminService).waitForBundleCap(bundleID) -> Promise<BundleCap>` (waits until installed)
* `E(vatAdminService).getNamedBundleCap(name) -> Promise<BundleCap>` (rejects if not registered)
* `E(vatAdminService).getBundleIDByName(name) -> Promise<string>` (rejects if not registered)

Note that the `waitForBundleCap()` method will wait (possibly forever) for the bundle ID to be installed before resolving its Promise, so the Promise will never resolve to `undefined`.

Expand Down
14 changes: 14 additions & 0 deletions packages/SwingSet/src/devices/vat-admin/device-vat-admin.js
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,20 @@ export function buildDevice(tools, endowments) {
}
return returnCapForBundleID(bundleID);
}
// D(devices.bundle).getBundleIDByName(name) -> id
if (method === 'getBundleIDByName') {
const args = unserialize(argsCapdata);
const [name] = args;
assert.typeof(name, 'string', `getBundleIDByName() name`);
let bundleID;
try {
// this throws on a bad name, so make a better error
bundleID = getNamedBundleID(name);
} catch (e) {
throw Error(`unregistered bundle name '${name}'`);
}
return returnFromInvoke(bundleID);
}
throw TypeError(`target[${method}] does not exist`);
}

Expand Down
3 changes: 3 additions & 0 deletions packages/SwingSet/src/vats/vat-admin/vat-vat-admin.js
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,9 @@ export function buildRootObject(vatPowers) {
getNamedBundleCap(name) {
return D(vatAdminNode).getNamedBundleCap(name);
},
getBundleIDByName(name) {
return D(vatAdminNode).getBundleIDByName(name);
},
createMeter(remaining, threshold) {
return makeMeter(remaining, threshold);
},
Expand Down
5 changes: 5 additions & 0 deletions packages/SwingSet/test/bundling/bootstrap-bundles.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,11 @@ export function buildRootObject(vatPowers) {
return [hello];
},

async idByName(name) {
const id = await E(vatAdmin).getBundleIDByName(name);
return id;
},

async vatFromID(id, method) {
const bcap = await E(vatAdmin).getBundleCap(id);
const { root } = await E(vatAdmin).createVat(bcap);
Expand Down
10 changes: 10 additions & 0 deletions packages/SwingSet/test/bundling/test-bundles.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ test('bundles', async t => {
// We save this vat bundle (with 'disk()') to disk, to exercise
// config.bundles.NAME.bundleSpec
const diskBundle = await bundleSource(bfile('vat-disk.js'));
const diskBundleID = `b1-${diskBundle.endoZipBase64Sha512}`;
const diskBundleFilename = bfile('bundle-disk.bundle');
fs.writeFileSync(diskBundleFilename, JSON.stringify(diskBundle));
t.teardown(() => fs.unlinkSync(diskBundleFilename));
Expand Down Expand Up @@ -121,6 +122,15 @@ test('bundles', async t => {
// vatAdminService~.createVatByName() still works, TODO until we remove it
await check('vatByName', ['named', 'hi'], ['hello']);

await check('idByName', ['disk'], diskBundleID);

// prints "unregistered bundle name 'missing'" to log
await checkRejects(
'idByName',
['missing'],
Error('syscall.callNow failed: device.invoke failed, see logs for details'),
);

// vatAdminService~.getBundleCap(invalidBundleID) should reject
await checkRejects(
'getBundleCap',
Expand Down

0 comments on commit 1ab58fc

Please sign in to comment.