Skip to content

Commit

Permalink
feat(spawner): implement basic metering
Browse files Browse the repository at this point in the history
  • Loading branch information
michaelfig committed Feb 26, 2020
1 parent 9138801 commit 8bd495c
Show file tree
Hide file tree
Showing 6 changed files with 80 additions and 7 deletions.
1 change: 0 additions & 1 deletion packages/SwingSet/src/controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,6 @@ function makeEvaluate(e) {
});
}


function makeSESEvaluator() {
const evaluateOptions = makeDefaultEvaluateOptions();
const { transforms = [], shims = [], ...otherOptions } = evaluateOptions;
Expand Down
3 changes: 2 additions & 1 deletion packages/spawner/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@
"@agoric/make-promise": "^0.0.1",
"@agoric/nat": "^2.0.1",
"@agoric/same-structure": "^0.0.1",
"@agoric/store": "^0.0.1"
"@agoric/store": "^0.0.1",
"@agoric/transform-metering": "^1.1.0"
},
"devDependencies": {
"@agoric/swingset-vat": "^0.3.0",
Expand Down
26 changes: 25 additions & 1 deletion packages/spawner/src/contractHost.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* global replaceGlobalMeter */
// Copyright (C) 2019 Agoric, under Apache License 2.0

import Nat from '@agoric/nat';
Expand All @@ -12,6 +13,7 @@ import {
import { inviteConfig } from '@agoric/ertp/src/config/inviteConfig';
import { makeMint } from '@agoric/ertp';
import makePromise from '@agoric/make-promise';
import { makeMeter } from '@agoric/transform-metering/src/meter';

import { allSettled } from './allSettled';

Expand Down Expand Up @@ -68,7 +70,29 @@ function makeContractHost(E, evaluate, additionalEndowments = {}) {
typeof functionSrcString === 'string',
`"${functionSrcString}" must be a string, but was ${typeof functionSrcString}`,
);
const fn = evaluate(functionSrcString, fullEndowments);

// Refill a meter each time.
// NOTE: We need 1e7 or the autoswap contract exhausts
// the compute meter.
const { meter, refillFacet } = makeMeter({ budgetCombined: 1e7 });
const doRefill = () => {
// Refill the meter, since we're leaving a crank.
Object.values(refillFacet, r => r());
};

// Make an endowment to get our meter.
const getGlobalMeter = m => {
if (m !== true && typeof replaceGlobalMeter !== 'undefined') {
// Replace the global meter and register our refiller.
replaceGlobalMeter(meter, doRefill);
}
return meter;
};

const fn = evaluate(functionSrcString, {
...fullEndowments,
getGlobalMeter,
});
assert(
typeof fn === 'function',
`"${functionSrcString}" must be a string for a function, but produced ${typeof fn}`,
Expand Down
39 changes: 39 additions & 0 deletions packages/spawner/test/swingsetTests/contractHost/bootstrap.js
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,41 @@ function build(E, log) {
});
}

function exhaustedContractTest(host) {
log('starting exhaustedContractTest');

const exhContract = harden({
start: (terms, _inviteMaker) => {
if (terms === 'loop forever') {
for (;;) {
// Do nothing.
}
} else {
return 123;
}
},
});
const contractSrcs = harden({ start: `${exhContract.start} ` });

const installationP = E(host).install(contractSrcs);

return E(host)
.getInstallationSourceCode(installationP)
.then(src => {
log('Does source match? ', src.start === contractSrcs.start);

return E(installationP)
.spawn('loop forever')
.catch(e => log('spawn rejected: ', e.message));
})
.then(_ => E(host).install(contractSrcs))
.then(installation2P => E(installation2P).spawn('bar terms'))
.then(
ret => log('got bar ret: ', ret),
err => log('got bar err: ', err.message),
);
}

function betterContractTestAliceFirst(host, mint, aliceMaker, bobMaker) {
const escrowExchangeInstallationP = E(host).install(escrowExchangeSrcs);
const coveredCallInstallationP = E(host).install(coveredCallSrcs);
Expand Down Expand Up @@ -321,6 +356,10 @@ function build(E, log) {
const host = await E(vats.host).makeHost();
return trivialContractTest(host);
}
case 'exhaust': {
const host = await E(vats.host).makeHost();
return exhaustedContractTest(host);
}
case 'alice-first': {
const host = await E(vats.host).makeHost();
const aliceMaker = await E(vats.alice).makeAliceMaker(host);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,20 @@ test('run contractHost Demo --trivial without SES', async t => {
t.end();
});

const contractExhaustedGolden = [
'=> setup called',
'starting exhaustedContractTest',
'Does source match? true',
'spawn rejected: Compute meter exceeded',
'got bar ret: 123',
];

test('run contractHost Demo -- exhaust with SES', async t => {
const dump = await main(true, 'contractHost', ['exhaust']);
t.deepEquals(dump.log, contractExhaustedGolden);
t.end();
});

const contractAliceFirstGolden = [
'=> setup called',
'++ alice.payBobWell starting',
Expand Down
4 changes: 0 additions & 4 deletions packages/zoe/src/evalContractCode.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,6 @@ const evalContractCode = (code, additionalEndowments) => {
// the compute meter.
const { meter, refillFacet } = makeMeter({ budgetCombined: 1e7 });
const doRefill = () => {
if (meter.isExhausted()) {
// Don't refill.
return;
}
// Refill the meter, since we're leaving a crank.
Object.values(refillFacet, r => r());
};
Expand Down

0 comments on commit 8bd495c

Please sign in to comment.