Skip to content

Commit

Permalink
feat: implement vat baggage
Browse files Browse the repository at this point in the history
This implements the primary vat-side interface for upgrade (#4325, #4382).
However, it does not yet implement the set of post upgrade checks (#3062) that
will be required (e.g., check upon return from `buildRootObject` to verify that
all the durable kinds have had behavior associated with them or have been
explicitly dismissed); the ability to test or otherwise exercise the latter
portion of the upgrade machinery will have to wait until the kernel-side
interfaces (#1848, #3062) are in place.

I am going to tentative declarely that this PR closes #4325, leaving the
remaining work to be covered by #3062.
  • Loading branch information
FUDCo committed Mar 21, 2022
1 parent 45723b4 commit 032486b
Show file tree
Hide file tree
Showing 16 changed files with 886 additions and 672 deletions.
2 changes: 1 addition & 1 deletion packages/SwingSet/src/lib/djson.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
* @param {any} val
*/
function replacer(_, val) {
if (typeof val === 'object') {
if (val && typeof val === 'object') {
const sortedObject = {};
const names = Array.from(Object.getOwnPropertyNames(val));
names.sort();
Expand Down
15 changes: 15 additions & 0 deletions packages/SwingSet/src/liveslots/collectionManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -664,6 +664,20 @@ export function makeCollectionManager(
return store;
}

function provideBaggage() {
const baggageID = syscall.vatstoreGet('baggageID');
if (baggageID) {
return convertSlotToVal(baggageID);
} else {
const baggage = makeScalarBigMapStore('baggage', {
keySchema: M.string(),
durable: true,
});
syscall.vatstoreSet('baggageID', convertValToSlot(baggage));
return baggage;
}
}

/**
* Produce a *scalar* weak big map: keys can only be atomic values,
* primitives, or remotables.
Expand Down Expand Up @@ -788,6 +802,7 @@ export function makeCollectionManager(
makeScalarBigWeakMapStore,
makeScalarBigSetStore,
makeScalarBigWeakSetStore,
provideBaggage,
testHooks,
});
}
8 changes: 7 additions & 1 deletion packages/SwingSet/src/liveslots/liveslots.js
Original file line number Diff line number Diff line change
Expand Up @@ -1097,6 +1097,7 @@ function build(
assert(key.match(/^[-\w.+/]+$/), X`invalid vatstore key`);
}

let baggage;
async function startVat(vatParametersCapData) {
insistCapData(vatParametersCapData);
assert(!didStartVat);
Expand Down Expand Up @@ -1183,7 +1184,12 @@ function build(
);

// here we finally invoke the vat code, and get back the root object
const rootObject = buildRootObject(harden(vpow), harden(vatParameters));
baggage = collectionManager.provideBaggage();
const rootObject = buildRootObject(
harden(vpow),
harden(vatParameters),
baggage,
);
assert.equal(
passStyleOf(rootObject),
'remotable',
Expand Down
11 changes: 5 additions & 6 deletions packages/SwingSet/src/supervisors/supervisor-helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -120,12 +120,11 @@ function makeSupervisorSyscall(syscallToManager, workerCanBlock) {
},
vatstoreSet: (key, value) => doSyscall(['vatstoreSet', key, value]),
vatstoreGetAfter: (priorKey, lowerBound, upperBound) => {
const result = doSyscall([
'vatstoreGetAfter',
priorKey,
lowerBound,
upperBound,
]);
const syscall = ['vatstoreGetAfter', priorKey, lowerBound];
if (upperBound) {
syscall.push(upperBound);
}
const result = doSyscall(syscall);
return result === null ? [undefined, undefined] : result;
},
vatstoreDelete: key => doSyscall(['vatstoreDelete', key]),
Expand Down
4 changes: 2 additions & 2 deletions packages/SwingSet/test/gc/test-gc-vat.js
Original file line number Diff line number Diff line change
Expand Up @@ -134,8 +134,8 @@ test('forward to fake zoe', async t => {
console.log(`targetID: ${targetID}`);

// confirm that zoe is exporting it
t.is(findClist(c, zoeID, invitation), 'o+1');
t.true(dumpClist(c).includes(`${invitation}/${zoeID}/o+1`));
t.is(findClist(c, zoeID, invitation), 'o+9');
t.true(dumpClist(c).includes(`${invitation}/${zoeID}/o+9`));
// confirm that vat-target has not seen it yet
t.is(findClist(c, targetID, invitation), undefined);

Expand Down
130 changes: 129 additions & 1 deletion packages/SwingSet/test/liveslots-helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,14 @@ import { waitUntilQuiescent } from '../src/lib-nodejs/waitUntilQuiescent.js';
import { makeGcAndFinalize } from '../src/lib-nodejs/gc-and-finalize.js';
import { makeDummyMeterControl } from '../src/kernel/dummyMeterControl.js';
import { makeLiveSlots } from '../src/liveslots/liveslots.js';
import { capargs } from './vat-util.js';
import {
capargs,
makeMessage,
makeDropExports,
makeRetireImports,
makeRetireExports,
makeBringOutYourDead,
} from './util.js';

export function buildSyscall() {
const log = [];
Expand Down Expand Up @@ -138,3 +145,124 @@ export async function makeDispatch(
}
return dispatch;
}

function makeRPMaker() {
let idx = 0;
return () => {
idx += 1;
return `p-${idx}`;
};
}

export async function setupTestLiveslots(t, buildRootObject, vatName, forceGC) {
const { log, syscall } = buildSyscall();
const nextRP = makeRPMaker();
const th = [];
const dispatch = await makeDispatch(
syscall,
buildRootObject,
vatName,
false,
0,
th,
);
const [testHooks] = th;

async function dispatchMessage(message, args = capargs([])) {
const rp = nextRP();
await dispatch(makeMessage('o+0', message, args, rp));
if (forceGC) {
// XXX TERRIBLE HACK WARNING XXX The following GC call is terrible but
// apparently sometimes necessary. Without it, certain tests in some
// test files will fail under Node 16 if run non-selectively (that is,
// without the "-m 'your testname here'" flag) even though all tests in
// the file will succeed under Node 14 regardless of how initiated and the
// single test will succeed under Node 16 if run standalone.
//
// This nonsense suggests that under Node 16 there may be a problem with
// Ava's logic for running multiple tests that is allowing one test to
// side effect others even when they are nominally run sequentially. In
// particular, forcing a GC at the start of setupTestLiveslots (which you
// would think would reset the heap to a consistent initial state at the
// start of each test) does not change the circumstances of the failure,
// but inserting the GC after message dispatch does. None of this makes
// any sense that I can discern, but I don't have time to diagnose this
// right now. Since this hack is part of mocking the kernel side here
// anyway, I suspect that the likely large investment in time and effort
// to puzzle out what's going on here won't have much payoff; it seems
// plausible that whatever the issue is it may only impact the mock
// environment. Nevertheless there's a chance we may be courting some
// deeper problem, hence this comment.
engineGC();
}
await dispatch(makeBringOutYourDead());
return rp;
}
async function dispatchDropExports(...vrefs) {
await dispatch(makeDropExports(...vrefs));
await dispatch(makeBringOutYourDead());
}
async function dispatchRetireImports(...vrefs) {
await dispatch(makeRetireImports(...vrefs));
await dispatch(makeBringOutYourDead());
}
async function dispatchRetireExports(...vrefs) {
await dispatch(makeRetireExports(...vrefs));
await dispatch(makeBringOutYourDead());
}

const v = { t, log };

return {
v,
dispatchMessage,
dispatchDropExports,
dispatchRetireExports,
dispatchRetireImports,
testHooks,
};
}

export function matchResolveOne(vref, value) {
return { type: 'resolve', resolutions: [[vref, false, value]] };
}

export function matchVatstoreGet(key, result) {
return { type: 'vatstoreGet', key, result };
}

export function matchVatstoreGetAfter(priorKey, start, end, result) {
return { type: 'vatstoreGetAfter', priorKey, start, end, result };
}

export function matchVatstoreDelete(key) {
return { type: 'vatstoreDelete', key };
}

export function matchVatstoreSet(key, value) {
return { type: 'vatstoreSet', key, value };
}

export function matchRetireExports(...slots) {
return { type: 'retireExports', slots };
}

export function matchDropImports(...slots) {
return { type: 'dropImports', slots };
}

export function matchRetireImports(...slots) {
return { type: 'retireImports', slots };
}

export function validate(v, match) {
v.t.deepEqual(v.log.shift(), match);
}

export function validateDone(v) {
v.t.deepEqual(v.log, []);
}

export function validateReturned(v, rp) {
validate(v, matchResolveOne(rp, capargs({ '@qclass': 'undefined' })));
}
Loading

0 comments on commit 032486b

Please sign in to comment.