Skip to content

Commit

Permalink
fix(swingset): make vatAdmin provide getBundlecap/getNamedBundlecap
Browse files Browse the repository at this point in the history
This changes `vatAdminService` to expose/re-export the `getBundlecap()` and
`getNamedBundlecap()` functionality from `devices.bundle`. Since vats can
return Promises, the new vatAdmin.getBundlecap lets you wait for the given
bundle to be installed, which should simplify some contract install flows.

As a result, `createVatAdminService` changes: you must connect it to
`devices.bundle`. This requires a change to the bootstrap function all
tests (and production uses of swingset) from:

```js
const vatAdminService = await E(vats.vatAdmin).createVatAdminService(
  devices.vatAdmin);
```

to:

```js
const vatAdminService = await E(vats.vatAdmin).createVatAdminService(
  devices.vatAdmin, devices.bundle);
```

This commit updates all swingset tests to call createVatAdminService the new
way. The next commit will update all other packages in the tree. External
dapps don't usually perform full-swingset tests, so they are unlikely to be
affected.

closes #4521
  • Loading branch information
warner committed Feb 12, 2022
1 parent 933d8c6 commit 8de4838
Show file tree
Hide file tree
Showing 15 changed files with 130 additions and 57 deletions.
65 changes: 42 additions & 23 deletions packages/SwingSet/docs/bundles.md

Large diffs are not rendered by default.

4 changes: 0 additions & 4 deletions packages/SwingSet/src/kernel/kernel.js
Original file line number Diff line number Diff line change
Expand Up @@ -1050,10 +1050,6 @@ export default function buildKernel(
// later when it is created and a root object is available
return vatID;
},
getBundleIDByName(bundleName) {
// this throws if the name is unknown
return kernelKeeper.getNamedBundleID(bundleName);
},
terminate: (vatID, reason) => terminateVat(vatID, true, reason),
meterCreate: (remaining, threshold) =>
kernelKeeper.allocateMeter(remaining, threshold),
Expand Down
12 changes: 0 additions & 12 deletions packages/SwingSet/src/kernel/vatAdmin/vatAdmin-src.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ export function buildRootDeviceNode({ endowments, serialize }) {
const {
pushCreateVatBundleEvent,
pushCreateVatIDEvent,
getBundleIDByName,
terminate: kernelTerminateVatFn,
meterCreate,
meterAddRemaining,
Expand Down Expand Up @@ -63,17 +62,6 @@ export function buildRootDeviceNode({ endowments, serialize }) {
const vatID = pushCreateVatIDEvent(bundleID, options);
return vatID;
},
createByName(bundleName, options = {}) {
assert.typeof(bundleName, 'string');
let bundleID;
try {
bundleID = getBundleIDByName(bundleName);
} catch (e) {
throw Error(`unregistered bundle name '${bundleName}'`);
}
const vatID = pushCreateVatIDEvent(bundleID, options);
return vatID;
},
terminateWithFailure(vatID, reason) {
kernelTerminateVatFn(vatID, serialize(reason));
},
Expand Down
53 changes: 45 additions & 8 deletions packages/SwingSet/src/kernel/vatAdmin/vatAdminWrapper.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,34 @@ export function buildRootObject(vatPowers) {
const running = new Map(); // vatID -> { resolve, reject } for doneP
const meterByID = new Map(); // meterID -> { meter, updater }
const meterIDByMeter = new WeakMap(); // meter -> meterID
let bundledev; // set by createVatAdminService() TODO(4523) initialize()
const pendingBundles = new Map(); // bundleID -> Promise<bundlecap>

function waitForBundlecap(bundleID) {
const bundlecap = D(bundledev).getBundlecap(bundleID);
if (bundlecap) {
return bundlecap;
}
if (!pendingBundles.has(bundleID)) {
pendingBundles.set(bundleID, makePromiseKit());
}
return pendingBundles.get(bundleID).promise;
}

// this message is queued to us by kernel.installBundle()
function bundleInstalled(bundleID) {
if (pendingBundles.has(bundleID)) {
const bundlecap = D(bundledev).getBundlecap(bundleID);
if (!bundlecap) {
// if the bundle got uninstalled by the time we got the message, keep
// waiting, maybe it will get reinstalled some day
console.log(`bundle ${bundleID} missing, hoping for reinstall`);
return;
}
pendingBundles.get(bundleID).resolve(bundlecap);
pendingBundles.delete(bundleID);
}
}

function makeMeter(vatAdminNode, remaining, threshold) {
Nat(remaining);
Expand Down Expand Up @@ -89,8 +117,15 @@ export function buildRootObject(vatPowers) {
return harden(options);
}

function createVatAdminService(vatAdminNode) {
function createVatAdminService(vatAdminNode, bundleDeviceNode) {
bundledev = bundleDeviceNode;
return Far('vatAdminService', {
getBundlecap(bundleID) {
return waitForBundlecap(bundleID);
},
getNamedBundlecap(name) {
return D(bundledev).getNamedBundlecap(name);
},
createMeter(remaining, threshold) {
return makeMeter(vatAdminNode, remaining, threshold);
},
Expand Down Expand Up @@ -122,15 +157,16 @@ export function buildRootObject(vatPowers) {
},
createVatByName(bundleName, options = {}) {
// eventually this option will go away: userspace will be obligated
// to use D(devices.bundle).getNamedBundleId(name), probably during
// bootstrap (so devices.bundle can be closely held), to fetch the
// named bundlecaps early, and then distribute specific bundlecaps to
// any vat which wants to make a vat from them (e.g. zoe with ZCF).
// That requires chain-side changes that I want to coordinate
// separately, so I'll leave this in place until later.
// to use getNamedBundlecap(), probably during bootstrap, to fetch
// the named bundlecaps early, and then distribute specific
// bundlecaps to any vat which wants to make a vat from them (e.g.
// zoe with ZCF). That requires chain-side changes that I want to
// coordinate separately, so I'll leave this in place until later.
assert.typeof(bundleName, 'string');
const co = convertOptions(options);
const vatID = D(vatAdminNode).createByName(bundleName, co);
const bundlecap = D(bundledev).getNamedBundlecap(bundleName);
const bundleID = D(bundlecap).getBundleID();
const vatID = D(vatAdminNode).createByBundleID(bundleID, co);
return finishVatCreation(vatAdminNode, vatID);
},
});
Expand Down Expand Up @@ -172,6 +208,7 @@ export function buildRootObject(vatPowers) {

return Far('root', {
createVatAdminService,
bundleInstalled,
newVatCallback,
vatTerminated,
meterCrossedThreshold,
Expand Down
1 change: 1 addition & 0 deletions packages/SwingSet/test/bootstrap-syscall-failure.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export function buildRootObject(vatPowers, vatParameters) {
if (vatParameters.beDynamic) {
const vatMaker = E(vats.vatAdmin).createVatAdminService(
devices.vatAdmin,
devices.bundle,
);
const vat = await E(vatMaker).createVatByName('badvat', {
enableSetup: true,
Expand Down
5 changes: 4 additions & 1 deletion packages/SwingSet/test/bundles/bootstrap-bundles.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,10 @@ export function buildRootObject(vatPowers) {
devices = d0;
// we exercise a little bit of vatAdmin, but this test is mostly about
// bundles
vatAdmin = await E(vats.vatAdmin).createVatAdminService(devices.vatAdmin);
vatAdmin = await E(vats.vatAdmin).createVatAdminService(
devices.vatAdmin,
devices.bundle,
);
},

async checkConfiguredVats() {
Expand Down
5 changes: 4 additions & 1 deletion packages/SwingSet/test/gc-dead-vat/bootstrap.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@ export function buildRootObject() {
const pk1 = makePromiseKit();
return Far('root', {
async bootstrap(vats, devices) {
const vatMaker = E(vats.vatAdmin).createVatAdminService(devices.vatAdmin);
const vatMaker = E(vats.vatAdmin).createVatAdminService(
devices.vatAdmin,
devices.bundle,
);
vat = await E(vatMaker).createVatByName('doomed');
doomedRoot = vat.root;
await sendExport(doomedRoot);
Expand Down
5 changes: 4 additions & 1 deletion packages/SwingSet/test/metering/vat-load-dynamic.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ export function buildRootObject(vatPowers) {

return Far('root', {
async bootstrap(vats, devices) {
service = await E(vats.vatAdmin).createVatAdminService(devices.vatAdmin);
service = await E(vats.vatAdmin).createVatAdminService(
devices.vatAdmin,
devices.bundle,
);
bundleDev = devices.bundle;
},

Expand Down
5 changes: 4 additions & 1 deletion packages/SwingSet/test/vat-admin/bootstrap.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ export function buildRootObject(vatPowers) {

return Far('root', {
async bootstrap(vats, devices) {
admin = await E(vats.vatAdmin).createVatAdminService(devices.vatAdmin);
admin = await E(vats.vatAdmin).createVatAdminService(
devices.vatAdmin,
devices.bundle,
);
bundleDevice = devices.bundle;
},

Expand Down
7 changes: 6 additions & 1 deletion packages/SwingSet/test/vat-admin/replay-bootstrap.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,12 @@ export function buildRootObject(vatPowers) {
return Far('root', {
async bootstrap(vats, devs) {
devices = devs;
gotVatAdminSvc(E(vats.vatAdmin).createVatAdminService(devices.vatAdmin));
gotVatAdminSvc(
E(vats.vatAdmin).createVatAdminService(
devices.vatAdmin,
devices.bundle,
),
);
},

async createVat() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ export function buildRootObject() {

const self = Far('root', {
async bootstrap(vats, devices) {
const vatMaker = E(vats.vatAdmin).createVatAdminService(devices.vatAdmin);
const vatMaker = E(vats.vatAdmin).createVatAdminService(
devices.vatAdmin,
devices.bundle,
);

// create a dynamic vat, send it a message and let it respond, to make
// sure everything is working
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ export function buildRootObject(vatPowers) {
const self = Far('root', {
async bootstrap(vats, devices) {
testLog('preparing dynamic vat');
const vatMaker = E(vats.vatAdmin).createVatAdminService(devices.vatAdmin);
const vatMaker = E(vats.vatAdmin).createVatAdminService(
devices.vatAdmin,
devices.bundle,
);
const dude = await E(vatMaker).createVatByName('dude');
E(dude.root).dieReturningAPresence(self);
const doneP = E(dude.adminNode).done();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ import { Far } from '@endo/marshal';
export function buildRootObject() {
const self = Far('root', {
async bootstrap(vats, devices) {
const vatMaker = E(vats.vatAdmin).createVatAdminService(devices.vatAdmin);
const vatMaker = E(vats.vatAdmin).createVatAdminService(
devices.vatAdmin,
devices.bundle,
);

// create a dynamic vat, send it a message and let it respond, to make
// sure everything is working
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ export function buildRootObject(vatPowers) {

const self = Far('root', {
async bootstrap(vats, devices) {
const vatMaker = E(vats.vatAdmin).createVatAdminService(devices.vatAdmin);
const vatMaker = E(vats.vatAdmin).createVatAdminService(
devices.vatAdmin,
devices.bundle,
);
mediumRoot = vats.medium;

// create a dynamic vat, then kill it, then try to send it a message
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ export function buildRootObject(vatPowers, vatParameters) {

const self = Far('root', {
async bootstrap(vats, devices) {
const vatMaker = E(vats.vatAdmin).createVatAdminService(devices.vatAdmin);
const vatMaker = E(vats.vatAdmin).createVatAdminService(
devices.vatAdmin,
devices.bundle,
);

// create a dynamic vat, send it a message and let it respond, to make
// sure everything is working
Expand Down

0 comments on commit 8de4838

Please sign in to comment.