From 1f74b20ff237680f0689db01824cacc932391cd0 Mon Sep 17 00:00:00 2001 From: Brian Warner Date: Sat, 12 Feb 2022 17:51:54 -0800 Subject: [PATCH] fix(zoe): get ZCF bundlecap from vatAdminService Zoe needs access to the vatAdminSvc, to create a new ZCF vat to host each contract. `makeZoeKit()` provides an optional argument to control which bundle is used for this purpose. This 4th argument was a string, defaulting to 'zcf', and relied upon `E(vatAdminSvc).createVatByName` (which is going away). To move everything closer to bundlecaps, the `makeZoeKit()` optional argument now takes `{ name }` or `{ id }` or `{ bundlecap }`, instead of only a name. Zoe uses the new `E(vatAdminSvc).getNamedBundleCap()` to convert a name into a bundlecap, but in the future I expect our chain-side bootstrap() to call `makeZoeKit()` with a specific bundlecap. In the long run, I'm trying to make room for Zoe to accomodate multiple versions of ZCF, each indexed by its bundlecap. Non-swingset-based zoe-using unit tests generally use `fakeVatAdmin.js` to build a mock `vatAdminSvc`. This commit changes `fakeVatAdmin.js` to include an additional `zcfBundlecap` export, which is an empty marker object that serves as a stand-in for the bundlecap. The fake `vatAdminSvc` can accept this fake `zcfBundlecap`, and will evaluate the ZCF code appropriately. However the fake `vatAdminSvc` also knows how to convert the default `'zcf'` name into that bundlecap, so downstream code should not need to change. Code that uses `makeZoeKit()` and provides the optional fourth argument needs to be updated to the new signature. This commit changes all such instances in the current codebase. refs #4487 --- .../demo/exchangeBenchmark/vat-zoe.js | 8 +++----- .../demo/swapBenchmark/vat-zoe.js | 8 +++----- .../swingset-runner/demo/zoeTests/vat-zoe.js | 8 +++----- packages/vats/src/vat-zoe.js | 2 +- packages/zoe/src/zoeService/createZCFVat.js | 18 +++++++++++++---- packages/zoe/src/zoeService/types.js | 13 ++++++++++-- packages/zoe/src/zoeService/zoe.js | 6 +++--- .../test/unitTests/zoe/test-createZCFVat.js | 17 ++++++++-------- packages/zoe/tools/fakeVatAdmin.js | 20 ++++++++++++++----- 9 files changed, 61 insertions(+), 39 deletions(-) diff --git a/packages/swingset-runner/demo/exchangeBenchmark/vat-zoe.js b/packages/swingset-runner/demo/exchangeBenchmark/vat-zoe.js index 3c06d9702447..2df07232cb1d 100644 --- a/packages/swingset-runner/demo/exchangeBenchmark/vat-zoe.js +++ b/packages/swingset-runner/demo/exchangeBenchmark/vat-zoe.js @@ -8,11 +8,9 @@ export function buildRootObject(vatPowers, vatParameters) { return Far('root', { buildZoe: vatAdminSvc => { const shutdownZoeVat = vatPowers.exitVatWithFailure; - const { zoeService: zoe } = makeZoeKit( - vatAdminSvc, - shutdownZoeVat, - vatParameters.zcfBundleName, - ); + const { zoeService: zoe } = makeZoeKit(vatAdminSvc, shutdownZoeVat, { + name: vatParameters.zcfBundleName, + }); return zoe; }, }); diff --git a/packages/swingset-runner/demo/swapBenchmark/vat-zoe.js b/packages/swingset-runner/demo/swapBenchmark/vat-zoe.js index 3c06d9702447..2df07232cb1d 100644 --- a/packages/swingset-runner/demo/swapBenchmark/vat-zoe.js +++ b/packages/swingset-runner/demo/swapBenchmark/vat-zoe.js @@ -8,11 +8,9 @@ export function buildRootObject(vatPowers, vatParameters) { return Far('root', { buildZoe: vatAdminSvc => { const shutdownZoeVat = vatPowers.exitVatWithFailure; - const { zoeService: zoe } = makeZoeKit( - vatAdminSvc, - shutdownZoeVat, - vatParameters.zcfBundleName, - ); + const { zoeService: zoe } = makeZoeKit(vatAdminSvc, shutdownZoeVat, { + name: vatParameters.zcfBundleName, + }); return zoe; }, }); diff --git a/packages/swingset-runner/demo/zoeTests/vat-zoe.js b/packages/swingset-runner/demo/zoeTests/vat-zoe.js index 3c06d9702447..2df07232cb1d 100644 --- a/packages/swingset-runner/demo/zoeTests/vat-zoe.js +++ b/packages/swingset-runner/demo/zoeTests/vat-zoe.js @@ -8,11 +8,9 @@ export function buildRootObject(vatPowers, vatParameters) { return Far('root', { buildZoe: vatAdminSvc => { const shutdownZoeVat = vatPowers.exitVatWithFailure; - const { zoeService: zoe } = makeZoeKit( - vatAdminSvc, - shutdownZoeVat, - vatParameters.zcfBundleName, - ); + const { zoeService: zoe } = makeZoeKit(vatAdminSvc, shutdownZoeVat, { + name: vatParameters.zcfBundleName, + }); return zoe; }, }); diff --git a/packages/vats/src/vat-zoe.js b/packages/vats/src/vat-zoe.js index eb9ce0b0f30d..39362e5c5807 100644 --- a/packages/vats/src/vat-zoe.js +++ b/packages/vats/src/vat-zoe.js @@ -9,7 +9,7 @@ export function buildRootObject(vatPowers, vatParameters) { adminVat, shutdownZoeVat, feeIssuerConfig, - vatParameters.zcfBundleName, + { name: vatParameters.zcfBundleName }, ); return harden({ zoeService, diff --git a/packages/zoe/src/zoeService/createZCFVat.js b/packages/zoe/src/zoeService/createZCFVat.js index 03d8c289b891..199f9b5ffadd 100644 --- a/packages/zoe/src/zoeService/createZCFVat.js +++ b/packages/zoe/src/zoeService/createZCFVat.js @@ -5,14 +5,24 @@ import { E } from '@agoric/eventual-send'; * ZCF Vats can be created. * * @param {VatAdminSvc} vatAdminSvc - * @param {string=} zcfBundleName + * @param {ZCFSpec} zcfSpec * @returns {CreateZCFVat} */ -export const setupCreateZCFVat = (vatAdminSvc, zcfBundleName = 'zcf') => { +export const setupCreateZCFVat = (vatAdminSvc, zcfSpec) => { /** @type {CreateZCFVat} */ const createZCFVat = async () => { - assert.typeof(zcfBundleName, 'string'); - const rootAndAdminNodeP = E(vatAdminSvc).createVatByName(zcfBundleName); + let bundlecapP; + if (zcfSpec.bundlecap) { + bundlecapP = Promise.resolve(zcfSpec.bundlecap); + } else if (zcfSpec.name) { + bundlecapP = E(vatAdminSvc).getNamedBundlecap(zcfSpec.name); + } else { + assert(zcfSpec.id); + bundlecapP = E(vatAdminSvc).getBundlecap(zcfSpec.id); + } + const bundlecap = await bundlecapP; + assert(bundlecap); + const rootAndAdminNodeP = E(vatAdminSvc).createVat(bundlecap); const rootAndAdminNode = await rootAndAdminNodeP; return rootAndAdminNode; }; diff --git a/packages/zoe/src/zoeService/types.js b/packages/zoe/src/zoeService/types.js index 43139f4bbb29..8a6f80683231 100644 --- a/packages/zoe/src/zoeService/types.js +++ b/packages/zoe/src/zoeService/types.js @@ -266,10 +266,19 @@ * @typedef {Handle<'Instance'>} Instance */ +/** + * @typedef {Handle<'Bundlecap'>} Bundlecap + */ + /** * @typedef {Object} VatAdminSvc - * @property {(bundle: SourceBundle) => RootAndAdminNode} createVat - * @property {(BundleName: string) => RootAndAdminNode} createVatByName + * @property {(BundleID: id) => Bundlecap} getBundlecap + * @property {(name: string) => Bundlecap} getNamedBundlecap + * @property {(bundlecap: Bundlecap) => RootAndAdminNode} createVat + */ + +/** + * @typedef {{bundlecap: Bundlecap } | {name: string} | {id: BundleID}} ZCFSpec */ /** diff --git a/packages/zoe/src/zoeService/zoe.js b/packages/zoe/src/zoeService/zoe.js index 6b7889d1f547..0e060e423aa3 100644 --- a/packages/zoe/src/zoeService/zoe.js +++ b/packages/zoe/src/zoeService/zoe.js @@ -35,7 +35,7 @@ import { createFeeMint } from './feeMint.js'; * shutdown the Zoe Vat. This function needs to use the vatPowers * available to a vat. * @param {FeeIssuerConfig} feeIssuerConfig - * @param {string} [zcfBundleName] - The name of the contract facet bundle. + * @param {ZCFSpec} [zcfSpec] - Pointer to the contract facet bundle. * @returns {{ * zoeService: ZoeService, * feeMintAccess: FeeMintAccess, @@ -49,7 +49,7 @@ const makeZoeKit = ( assetKind: AssetKind.NAT, displayInfo: harden({ decimalPlaces: 6, assetKind: AssetKind.NAT }), }, - zcfBundleName = undefined, + zcfSpec = { name: 'zcf' }, ) => { // We must pass the ZoeService to `makeStartInstance` before it is // defined. See below where the promise is resolved. @@ -64,7 +64,7 @@ const makeZoeKit = ( // This method contains the power to create a new ZCF Vat, and must // be closely held. vatAdminSvc is even more powerful - any vat can // be created. We severely restrict access to vatAdminSvc for this reason. - const createZCFVat = setupCreateZCFVat(vatAdminSvc, zcfBundleName); + const createZCFVat = setupCreateZCFVat(vatAdminSvc, zcfSpec); // The ZoeStorageManager composes and consolidates capabilities // needed by Zoe according to POLA. diff --git a/packages/zoe/test/unitTests/zoe/test-createZCFVat.js b/packages/zoe/test/unitTests/zoe/test-createZCFVat.js index 5c1570be8da2..c94cf4db2bdd 100644 --- a/packages/zoe/test/unitTests/zoe/test-createZCFVat.js +++ b/packages/zoe/test/unitTests/zoe/test-createZCFVat.js @@ -11,22 +11,21 @@ test('setupCreateZCFVat', async t => { // This is difficult to unit test, since the real functionality // creates a new vat + const zcfBundlecap = Far('zcfBundlecap', {}); const fakeVatAdminSvc = Far('fakeVatAdminSvc', { - createVatByName: _name => { - return harden({ adminNode: undefined, root: undefined }); + getNamedBundlecap: name => { + assert.equal(name, 'zcf'); + return zcfBundlecap; }, - createVat: _bundle => { + createVat: bundlecap => { + assert.equal(bundlecap, zcfBundlecap); return harden({ adminNode: undefined, root: undefined }); }, }); // @ts-ignore fakeVatAdminSvc is mocked - t.deepEqual(await setupCreateZCFVat(fakeVatAdminSvc, undefined)(), { - adminNode: undefined, - root: undefined, - }); - // @ts-ignore fakeVatAdminSvc is mocked - t.deepEqual(await setupCreateZCFVat(fakeVatAdminSvc, 'myVat')(), { + t.deepEqual(await setupCreateZCFVat(fakeVatAdminSvc, { name: 'zcf' })(), { + // @ts-ignore fakeVatAdminSvc is mocked adminNode: undefined, root: undefined, }); diff --git a/packages/zoe/tools/fakeVatAdmin.js b/packages/zoe/tools/fakeVatAdmin.js index 9a27cbfd7896..d182c9a130ed 100644 --- a/packages/zoe/tools/fakeVatAdmin.js +++ b/packages/zoe/tools/fakeVatAdmin.js @@ -7,8 +7,13 @@ import { Far } from '@endo/marshal'; import { assert } from '@agoric/assert'; import { evalContractBundle } from '../src/contractFacet/evalContractCode.js'; import { handlePKitWarning } from '../src/handleWarning.js'; +import { makeHandle } from '../src/makeHandle.js'; import zcfContractBundle from '../bundles/bundle-contractFacet.js'; +// this simulates a bundlecap, which is normally a swingset "device node" +/** @type {Bundlecap} */ +export const zcfBundlecap = makeHandle('Bundlecap'); + /** * @param { (...args) => unknown } [testContextSetter] * @param { (x: unknown) => unknown } [makeRemote] @@ -36,7 +41,16 @@ function makeFakeVatAdmin(testContextSetter = undefined, makeRemote = x => x) { // test-only state can be provided from contracts // to their tests. const admin = Far('vatAdmin', { - createVat: bundle => { + getBundlecap: _bundleID => { + assert.fail(`fakeVatAdmin.getBundlecap() not yet implemented`); + }, + getNamedBundlecap: name => { + assert.equal(name, 'zcf', 'fakeVatAdmin only knows ZCF'); + return zcfBundlecap; + }, + createVat: bundlecap => { + assert.equal(bundlecap, zcfBundlecap, 'fakeVatAdmin only knows ZCF'); + const bundle = zcfContractBundle; return harden({ root: makeRemote( E(evalContractBundle(bundle)).buildRootObject( @@ -55,10 +69,6 @@ function makeFakeVatAdmin(testContextSetter = undefined, makeRemote = x => x) { }), }); }, - createVatByName: name => { - assert.equal(name, 'zcf', `only name='zcf' accepted, not ${name}`); - return admin.createVat(zcfContractBundle); - }, }); const vatAdminState = { getExitMessage: () => exitMessage,