Skip to content

Commit

Permalink
fix(zoe): get ZCF bundlecap from vatAdminService
Browse files Browse the repository at this point in the history
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
  • Loading branch information
warner committed Feb 18, 2022
1 parent e7c8fb3 commit 1f74b20
Show file tree
Hide file tree
Showing 9 changed files with 61 additions and 39 deletions.
8 changes: 3 additions & 5 deletions packages/swingset-runner/demo/exchangeBenchmark/vat-zoe.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
},
});
Expand Down
8 changes: 3 additions & 5 deletions packages/swingset-runner/demo/swapBenchmark/vat-zoe.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
},
});
Expand Down
8 changes: 3 additions & 5 deletions packages/swingset-runner/demo/zoeTests/vat-zoe.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
},
});
Expand Down
2 changes: 1 addition & 1 deletion packages/vats/src/vat-zoe.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export function buildRootObject(vatPowers, vatParameters) {
adminVat,
shutdownZoeVat,
feeIssuerConfig,
vatParameters.zcfBundleName,
{ name: vatParameters.zcfBundleName },
);
return harden({
zoeService,
Expand Down
18 changes: 14 additions & 4 deletions packages/zoe/src/zoeService/createZCFVat.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};
Expand Down
13 changes: 11 additions & 2 deletions packages/zoe/src/zoeService/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/

/**
Expand Down
6 changes: 3 additions & 3 deletions packages/zoe/src/zoeService/zoe.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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.
Expand All @@ -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.
Expand Down
17 changes: 8 additions & 9 deletions packages/zoe/test/unitTests/zoe/test-createZCFVat.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
});
Expand Down
20 changes: 15 additions & 5 deletions packages/zoe/tools/fakeVatAdmin.js
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down Expand Up @@ -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(
Expand All @@ -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,
Expand Down

0 comments on commit 1f74b20

Please sign in to comment.