Skip to content

Commit

Permalink
feat: add vatstorage syscalls to kernel
Browse files Browse the repository at this point in the history
  • Loading branch information
FUDCo committed Oct 13, 2020
1 parent de1c7f7 commit 90ef974
Show file tree
Hide file tree
Showing 6 changed files with 147 additions and 18 deletions.
16 changes: 8 additions & 8 deletions packages/SwingSet/src/kernel/kernel.js
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@ export default function buildKernel(

const kernelSyscallHandler = makeKernelSyscallHandler({
kernelKeeper,
storage: enhancedCrankBuffer,
ephemeral,
// eslint-disable-next-line no-use-before-define
notify,
Expand Down Expand Up @@ -575,14 +576,13 @@ export default function buildKernel(
try {
// this can fail if kernel or device code is buggy
const kres = kernelSyscallHandler.doKernelSyscall(ksc);
// kres is a KernelResult ([successFlag, capdata]), but since errors
// here are signalled with exceptions, kres is either ['ok', capdata]
// or ['ok', null]. Vats (liveslots) record the response in the
// transcript (which is why we use 'null' instead of 'undefined',
// TODO clean this up), but otherwise most syscalls ignore it. The
// one syscall that pays attention is callNow(), which assumes it's
// capdata.
vres = translators.kernelSyscallResultToVatSyscallResult(kres);
// kres is a KernelResult ([successFlag, value]), but since errors
// here are signalled with exceptions, kres is ['ok', value]. Vats
// (liveslots) record the response in the transcript (which is why we
// use 'null' instead of 'undefined', TODO clean this up), but otherwise
// most syscalls ignore it. The one syscall that pays attention is
// callNow(), which assumes it's capdata.
vres = translators.kernelSyscallResultToVatSyscallResult(ksc[0], kres);
// here, vres is either ['ok', null] or ['ok', capdata]
finish(kres, vres); // TODO call meaningfully on failure too?
} catch (err) {
Expand Down
29 changes: 29 additions & 0 deletions packages/SwingSet/src/kernel/kernelSyscall.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export function doSend(kernelKeeper, target, msg) {
export function makeKernelSyscallHandler(tools) {
const {
kernelKeeper,
storage,
ephemeral,
notify,
notifySubscribersAndQueue,
Expand All @@ -42,6 +43,28 @@ export function makeKernelSyscallHandler(tools) {
return OKNULL;
}

function vatstoreKeyKey(vatID, key) {
return `${vatID}.vs.${key}`;
}

function vatstoreGet(vatID, key) {
const actualKey = vatstoreKeyKey(vatID, key);
const value = storage.get(actualKey);
return harden(['ok', value || null]);
}

function vatstoreSet(vatID, key, value) {
const actualKey = vatstoreKeyKey(vatID, key);
storage.set(actualKey, value);
return OKNULL;
}

function vatstoreDelete(vatID, key) {
const actualKey = vatstoreKeyKey(vatID, key);
storage.delete(actualKey);
return OKNULL;
}

function invoke(deviceSlot, method, args) {
insistKernelType('device', deviceSlot);
insistCapData(args);
Expand Down Expand Up @@ -149,6 +172,12 @@ export function makeKernelSyscallHandler(tools) {
return reject(...args);
case 'exit':
return exit(...args);
case 'vatstoreGet':
return vatstoreGet(...args);
case 'vatstoreSet':
return vatstoreSet(...args);
case 'vatstoreDelete':
return vatstoreDelete(...args);
default:
throw Error(`unknown vatSyscall type ${type}`);
}
Expand Down
1 change: 1 addition & 0 deletions packages/SwingSet/src/kernel/state/kernelKeeper.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ const enableKernelPromiseGC = true;
// v$NN.c.$vatSlot = $kernelSlot = ko$NN/kp$NN/kd$NN
// v$NN.t.$NN = JSON(transcript entry)
// v$NN.t.nextID = $NN
// v$NN.vs.$key = string

// d$NN.o.nextID = $NN
// d$NN.c.$kernelSlot = $deviceSlot = o-$NN/d+$NN/d-$NN
Expand Down
3 changes: 3 additions & 0 deletions packages/SwingSet/src/kernel/vatManager/syscall.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,9 @@ export function createSyscall(transcriptManager) {
fulfillToPresence: (...args) => doSyscall(['fulfillToPresence', ...args]),
reject: (...args) => doSyscall(['reject', ...args]),
exit: (...args) => doSyscall(['exit', ...args]),
vatstoreGet: (...args) => doSyscall(['vatstoreGet', ...args]),
vatstoreSet: (...args) => doSyscall(['vatstoreSet', ...args]),
vatstoreDelete: (...args) => doSyscall(['vatstoreDelete', ...args]),
});

return harden({ syscall, doSyscall, setVatSyscallHandler });
Expand Down
65 changes: 55 additions & 10 deletions packages/SwingSet/src/kernel/vatTranslator.js
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,30 @@ function makeTranslateVatSyscallToKernelSyscall(vatID, kernelKeeper) {
return harden(['exit', vatID, !!isFailure, kernelInfo]);
}

function insistValidVatstoreKey(key) {
assert.typeof(key, 'string');
assert(key.match(/^\w+$/));
}

function translateVatstoreGet(key) {
insistValidVatstoreKey(key);
kdebug(`syscall[${vatID}].vatstoreGet(${key})`);
return harden(['vatstoreGet', vatID, key]);
}

function translateVatstoreSet(key, value) {
insistValidVatstoreKey(key);
assert.typeof(value, 'string');
kdebug(`syscall[${vatID}].vatstoreSet(${key},${value})`);
return harden(['vatstoreSet', vatID, key, value]);
}

function translateVatstoreDelete(key) {
insistValidVatstoreKey(key);
kdebug(`syscall[${vatID}].vatstoreDelete(${key})`);
return harden(['vatstoreDelete', vatID, key]);
}

function translateCallNow(target, method, args) {
insistCapData(args);
const dev = mapVatSlotToKernelSlot(target);
Expand Down Expand Up @@ -240,6 +264,12 @@ function makeTranslateVatSyscallToKernelSyscall(vatID, kernelKeeper) {
return translateReject(...args);
case 'exit':
return translateExit(...args);
case 'vatstoreGet':
return translateVatstoreGet(...args);
case 'vatstoreSet':
return translateVatstoreSet(...args);
case 'vatstoreDelete':
return translateVatstoreDelete(...args);
default:
throw Error(`unknown vatSyscall type ${type}`);
}
Expand All @@ -260,19 +290,34 @@ function makeTranslateKernelSyscallResultToVatSyscallResult(

const { mapKernelSlotToVatSlot } = vatKeeper;

// Most syscalls return ['ok', null], but callNow() returns ['ok',
// capdata]. KernelSyscallResult is never ['error', reason] because errors
// Most syscalls return ['ok', null], but callNow() returns ['ok', capdata]
// and vatstoreGet() returns ['ok', string] or ['ok',
// undefined]. KernelSyscallResult is never ['error', reason] because errors
// (which are kernel-fatal) are signalled with exceptions.
function kernelSyscallResultToVatSyscallResult(kres) {
const [successFlag, kdata] = kres;
function kernelSyscallResultToVatSyscallResult(type, kres) {
const [successFlag, resultData] = kres;
assert(successFlag === 'ok', 'unexpected KSR error');
if (kdata) {
const slots = kdata.slots.map(slot => mapKernelSlotToVatSlot(slot));
const vdata = { ...kdata, slots };
const vres = harden(['ok', vdata]);
return vres;
switch (type) {
case 'invoke': {
insistCapData(resultData);
// prettier-ignore
const slots =
resultData.slots.map(slot => mapKernelSlotToVatSlot(slot));
const vdata = { ...resultData, slots };
const vres = harden(['ok', vdata]);
return vres;
}
case 'vatstoreGet':
if (resultData) {
assert.typeof(resultData, 'string');
return harden(['ok', resultData]);
} else {
return harden(['ok', undefined]);
}
default:
assert(resultData === null);
return harden(['ok', null]);
}
return harden(['ok', null]);
}

return kernelSyscallResultToVatSyscallResult;
Expand Down
51 changes: 51 additions & 0 deletions packages/SwingSet/test/test-kernel.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,57 @@ test('simple call', async t => {
});
});

test('vat store', async t => {
const kernel = makeKernel();
await kernel.start();
const log = [];
function setup(syscall, _state, _helpers, _vatPowers) {
function deliver(facetID, method, args) {
switch (method) {
case 'get': {
const v = syscall.vatstoreGet('zot');
if (v) {
log.push(`"${v}"`);
} else {
log.push(`${v}`);
}
break;
}
case 'store':
syscall.vatstoreSet('zot', args.body);
break;
case 'delete':
syscall.vatstoreDelete('zot');
break;
default:
throw Error(`this can't happen`);
}
}
return { deliver };
}
await kernel.createTestVat('vat', setup);
const vat = kernel.vatNameToID('vat');

kernel.queueToExport(vat, 'o+1', 'get', capdata('[]'));
kernel.queueToExport(vat, 'o+1', 'store', capdata('first value'));
kernel.queueToExport(vat, 'o+1', 'get', capdata('[]'));
kernel.queueToExport(vat, 'o+1', 'store', capdata('second value'));
kernel.queueToExport(vat, 'o+1', 'get', capdata('[]'));
kernel.queueToExport(vat, 'o+1', 'delete', capdata('[]'));
kernel.queueToExport(vat, 'o+1', 'get', capdata('[]'));
t.deepEqual(log, []);
await kernel.run();
t.deepEqual(log, [
'undefined',
'"first value"',
'"second value"',
'undefined',
]);
const data = kernel.dump();
// check that we're not sticking an undefined into the transcript
t.is(data.vatTables[0].state.transcript[1].syscalls[0].response, null);
});

test('map inbound', async t => {
const kernel = makeKernel();
await kernel.start();
Expand Down

0 comments on commit 90ef974

Please sign in to comment.