Skip to content

Commit

Permalink
fix(swingset): createVatDynamically option to disable metering
Browse files Browse the repository at this point in the history
refs #1307
  • Loading branch information
warner committed Jul 18, 2020
1 parent 1b69c07 commit 56e7061
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 19 deletions.
44 changes: 31 additions & 13 deletions packages/SwingSet/src/kernel/dynamicVat.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,23 +27,37 @@ export function makeDynamicVatCreator(stuff) {
* defines the vat. This should be generated by calling bundle-source on a
* module whose default export is makeRootObject(), which takes E as a
* parameter and returns a root object.
* @param options an options bundle. The only option defined so far is
* 'metered', which defaults to 'true'. If 'true', the new dynamic vat is
* subject to a meter that limits the amount of computation and allocation
* that can occur during any given crank. Stack frames are limited as well.
* The meter is refilled between cranks, but if the meter ever underflows,
* the vat is terminated. If 'false', the vat is unmetered.
*
* @return { vatID } the vatID for a newly created vat. The success or
* failure of the operation will be reported in a message to the admin vat,
* citing this vatID
*/

function createVatDynamically(vatSourceBundle) {
const vatID = allocateUnusedVatID();
function createVatDynamically(vatSourceBundle, options = {}) {
const { metered = true, ...unknownOptions } = options;
if (Object.keys(unknownOptions).length) {
const msg = JSON.stringify(Object.keys(unknownOptions));
throw Error(`createVatDynamically got unknown options ${msg}`);
}

// fail-stop: we refill the meter after each crank (in vatManager
// doProcess()), but if the vat exhausts its meter within a single crank,
// it will never run again. We set refillEachCrank:false because we want
// doProcess to do the refilling itself, so it can count the usage
const meterRecord = makeGetMeter({
refillEachCrank: false,
refillIfExhausted: false,
});
const vatID = allocateUnusedVatID();
let meterRecord = null;
if (metered) {
// fail-stop: we refill the meter after each crank (in vatManager
// doProcess()), but if the vat exhausts its meter within a single crank,
// it will never run again. We set refillEachCrank:false because we want
// doProcess to do the refilling itself, so it can count the usage
meterRecord = makeGetMeter({
refillEachCrank: false,
refillIfExhausted: false,
});
}

let terminated = false;

Expand Down Expand Up @@ -80,9 +94,13 @@ export function makeDynamicVatCreator(stuff) {
`createVatDynamically() requires bundle, not a plain string`,
);
}
const getMeter = meterRecord.getMeter;
const inescapableTransforms = [src => transformMetering(src, getMeter)];
const inescapableGlobalLexicals = { getMeter };
const inescapableTransforms = [];
const inescapableGlobalLexicals = {};
if (metered) {
const getMeter = meterRecord.getMeter;
inescapableTransforms.push(src => transformMetering(src, getMeter));
inescapableGlobalLexicals.getMeter = getMeter;
}

const vatNS = await importBundle(vatSourceBundle, {
filePrefix: vatID,
Expand Down
4 changes: 2 additions & 2 deletions packages/SwingSet/src/kernel/vatAdmin/vatAdmin-src.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ export default function setup(syscall, state, helpers, endowments) {
// Called by the wrapper vat to create a new vat. Gets a new ID from the
// kernel's vat creator fn. Remember that the root object will arrive
// separately. Clean up the outgoing and incoming arguments.
create(bundle) {
const vatID = kernelVatCreationFn(bundle);
create(bundle, options) {
const vatID = kernelVatCreationFn(bundle, options);
return vatID;
},
terminate(_vatID) {
Expand Down
4 changes: 2 additions & 2 deletions packages/SwingSet/src/kernel/vatAdmin/vatAdminWrapper.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ export default function setup(syscall, state, helpers) {

function createVatAdminService(vatAdminNode) {
return harden({
createVat(code) {
const vatID = D(vatAdminNode).create(code);
createVat(code, options) {
const vatID = D(vatAdminNode).create(code, options);

const [promise, pendingRR] = producePRR();
pending.set(vatID, pendingRR);
Expand Down
60 changes: 60 additions & 0 deletions packages/SwingSet/test/metering/test-unmetered-dynamic-vat.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/* global harden */

import '@agoric/install-metering-and-ses';
import bundleSource from '@agoric/bundle-source';
import tap from 'tap';
import { buildVatController } from '../../src/index';
import makeNextLog from '../make-nextlog';

function capdata(body, slots = []) {
return harden({ body, slots });
}

function capargs(args, slots = []) {
return capdata(JSON.stringify(args), slots);
}

// Dynamic vats can be created without metering

tap.test('unmetered dynamic vat', async t => {
const config = {
vats: new Map(),
bootstrapIndexJS: require.resolve('./vat-load-dynamic.js'),
};
const c = await buildVatController(config, []);
const nextLog = makeNextLog(c);

// let the vatAdminService get wired up before we create any new vats
await c.run();

// we'll give this bundle to the loader vat, which will use it to create a
// new (unmetered) dynamic vat
const dynamicVatBundle = await bundleSource(
require.resolve('./metered-dynamic-vat.js'),
);

// 'createVat' will import the bundle
c.queueToVatExport(
'_bootstrap',
'o+0',
'createVat',
capargs([dynamicVatBundle, { metered: false }]),
);
await c.run();
t.deepEqual(nextLog(), ['created'], 'first create');

// First, send a message to the dynamic vat that runs normally
c.queueToVatExport('_bootstrap', 'o+0', 'run', capargs([]));
await c.run();

t.deepEqual(nextLog(), ['did run'], 'first run ok');

// Tell the dynamic vat to call `Array(4e9)`. If metering was in place,
// this would be rejected. Without metering, it's harmless (Arrays are
// lazy).
c.queueToVatExport('_bootstrap', 'o+0', 'explode', capargs(['allocate']));
await c.run();
t.deepEqual(nextLog(), ['failed to explode'], 'metering disabled');

t.end();
});
4 changes: 2 additions & 2 deletions packages/SwingSet/test/metering/vat-load-dynamic.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ function build(buildStuff) {
service = await E(vats.vatAdmin).createVatAdminService(devices.vatAdmin);
},

async createVat(bundle) {
control = await E(service).createVat(bundle);
async createVat(bundle, options) {
control = await E(service).createVat(bundle, options);
E(control.adminNode)
.done()
.then(
Expand Down

0 comments on commit 56e7061

Please sign in to comment.