From 00c5ca3e0c13f9438c314342c180e0238c32d13e Mon Sep 17 00:00:00 2001 From: Brian Warner Date: Fri, 13 May 2022 16:55:08 -0700 Subject: [PATCH] feat(swingset): add vatAdminService.getBundleIDByName() 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. --- packages/SwingSet/docs/bundles.md | 3 ++- .../src/devices/vat-admin/device-vat-admin.js | 14 ++++++++++++++ .../SwingSet/src/vats/vat-admin/vat-vat-admin.js | 3 +++ .../SwingSet/test/bundling/bootstrap-bundles.js | 5 +++++ packages/SwingSet/test/bundling/test-bundles.js | 10 ++++++++++ 5 files changed, 34 insertions(+), 1 deletion(-) diff --git a/packages/SwingSet/docs/bundles.md b/packages/SwingSet/docs/bundles.md index 6ae558bedbf9..b62c421e1179 100644 --- a/packages/SwingSet/docs/bundles.md +++ b/packages/SwingSet/docs/bundles.md @@ -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. @@ -71,6 +71,7 @@ Bundlecaps can be obtained from several methods of `vatAdminService`, which (as * `E(vatAdminService).getBundleCap(bundleID) -> Promise` (rejects if not installed yet) * `E(vatAdminService).waitForBundleCap(bundleID) -> Promise` (waits until installed) * `E(vatAdminService).getNamedBundleCap(name) -> Promise` (rejects if not registered) +* `E(vatAdminService).getBundleIDByName(name) -> Promise` (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`. diff --git a/packages/SwingSet/src/devices/vat-admin/device-vat-admin.js b/packages/SwingSet/src/devices/vat-admin/device-vat-admin.js index 128f92310021..a8d0729874b9 100644 --- a/packages/SwingSet/src/devices/vat-admin/device-vat-admin.js +++ b/packages/SwingSet/src/devices/vat-admin/device-vat-admin.js @@ -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`); } diff --git a/packages/SwingSet/src/vats/vat-admin/vat-vat-admin.js b/packages/SwingSet/src/vats/vat-admin/vat-vat-admin.js index acd0114a2e6d..315ce6a018d8 100644 --- a/packages/SwingSet/src/vats/vat-admin/vat-vat-admin.js +++ b/packages/SwingSet/src/vats/vat-admin/vat-vat-admin.js @@ -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); }, diff --git a/packages/SwingSet/test/bundling/bootstrap-bundles.js b/packages/SwingSet/test/bundling/bootstrap-bundles.js index 02d8cb010f2c..7e2035d58aef 100644 --- a/packages/SwingSet/test/bundling/bootstrap-bundles.js +++ b/packages/SwingSet/test/bundling/bootstrap-bundles.js @@ -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); diff --git a/packages/SwingSet/test/bundling/test-bundles.js b/packages/SwingSet/test/bundling/test-bundles.js index 144167207d40..d34fe81e3749 100644 --- a/packages/SwingSet/test/bundling/test-bundles.js +++ b/packages/SwingSet/test/bundling/test-bundles.js @@ -47,6 +47,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)); @@ -122,6 +123,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',