diff --git a/src/controller.js b/src/controller.js index a146ec4..2ce5d66 100644 --- a/src/controller.js +++ b/src/controller.js @@ -12,7 +12,9 @@ import makeDefaultEvaluateOptions from '@agoric/default-evaluate-options'; import kernelSourceFunc from './bundles/kernel'; import buildKernelNonSES from './kernel/index'; import bundleSource from './build-source-bundle'; +import { insist } from './insist'; import { insistCapData } from './capdata'; +import { parseVatSlot } from './parseVatSlots'; const evaluateOptions = makeDefaultEvaluateOptions(); @@ -133,8 +135,8 @@ export async function buildVatController(config, withSES = true, argv = []) { : buildNonSESKernel(initialState); // console.log('kernel', kernel); - async function addGenesisVat(vatID, sourceIndex, options = {}) { - console.log(`= adding vat '${vatID}' from ${sourceIndex}`); + async function addGenesisVat(name, sourceIndex, options = {}) { + console.log(`= adding vat '${name}' from ${sourceIndex}`); if (!(sourceIndex[0] === '.' || path.isAbsolute(sourceIndex))) { throw Error( 'sourceIndex must be relative (./foo) or absolute (/foo) not bare (foo)', @@ -163,7 +165,7 @@ export async function buildVatController(config, withSES = true, argv = []) { // eslint-disable-next-line global-require,import/no-dynamic-require setup = require(`${sourceIndex}`).default; } - kernel.addGenesisVat(vatID, setup, options); + kernel.addGenesisVat(name, setup, options); } async function addGenesisDevice(name, sourceIndex, endowments) { @@ -185,13 +187,35 @@ export async function buildVatController(config, withSES = true, argv = []) { kernel.addGenesisDevice(name, setup, endowments); } + if (config.devices) { + for (const [name, srcpath, endowments] of config.devices) { + // eslint-disable-next-line no-await-in-loop + await addGenesisDevice(name, srcpath, endowments); + } + } + + if (config.vats) { + for (const name of config.vats.keys()) { + const v = config.vats.get(name); + // eslint-disable-next-line no-await-in-loop + await addGenesisVat(name, v.sourcepath, v.options || {}); + } + } + + let bootstrapVatName; + if (config.bootstrapIndexJS) { + bootstrapVatName = '_bootstrap'; + await addGenesisVat(bootstrapVatName, config.bootstrapIndexJS, {}); + } + + // start() may queue bootstrap if state doesn't say we did it already. It + // also replays the transcripts from a previous run, if any, which will + // execute vat code (but all syscalls will be disabled) + await kernel.start(bootstrapVatName, JSON.stringify(argv)); + // the kernel won't leak our objects into the Vats, we must do // the same in this wrapper const controller = harden({ - addVat(vatID, sourceIndex, options = {}) { - return addGenesisVat(vatID, sourceIndex, options); - }, - log(str) { kernel.log(str); }, @@ -212,40 +236,24 @@ export async function buildVatController(config, withSES = true, argv = []) { await kernel.step(); }, - queueToExport(vatID, facetID, method, args) { - insistCapData(args); - kernel.addExport(vatID, facetID); - kernel.queueToExport(vatID, facetID, method, args); + // these are for tests + + vatNameToID(vatName) { + return kernel.vatNameToID(vatName); + }, + deviceNameToID(deviceName) { + return kernel.deviceNameToID(deviceName); }, - callBootstrap(vatID, bootstrapArgv) { - kernel.callBootstrap(`${vatID}`, JSON.stringify(bootstrapArgv)); + queueToVatExport(vatName, exportID, method, args) { + const vatID = kernel.vatNameToID(vatName); + parseVatSlot(exportID); + insist(method === `${method}`); + insistCapData(args); + kernel.addExport(vatID, exportID); + kernel.queueToExport(vatID, exportID, method, args); }, }); - if (config.devices) { - for (const [name, srcpath, endowments] of config.devices) { - // eslint-disable-next-line no-await-in-loop - await addGenesisDevice(name, srcpath, endowments); - } - } - - if (config.vats) { - for (const vatID of config.vats.keys()) { - const v = config.vats.get(vatID); - // eslint-disable-next-line no-await-in-loop - await addGenesisVat(vatID, v.sourcepath, v.options || {}); - } - } - - if (config.bootstrapIndexJS) { - await addGenesisVat('_bootstrap', config.bootstrapIndexJS, {}); - } - - // start() may queue bootstrap if state doesn't say we did it already. It - // also replays the transcripts from a previous run, if any, which will - // execute vat code (but all syscalls will be disabled) - await kernel.start('_bootstrap', JSON.stringify(argv)); - return controller; } diff --git a/src/kernel/id.js b/src/kernel/id.js new file mode 100644 index 0000000..52b46f6 --- /dev/null +++ b/src/kernel/id.js @@ -0,0 +1,67 @@ +import harden from '@agoric/harden'; +import Nat from '@agoric/nat'; + +// Vats are identified by an integer index, which (for typechecking purposes) +// is encoded as `vNN`. Devices are similarly identified as `dNN`. Both have +// human-readable names, which are provided to controller.addGenesisVat(), +// and usually come from the `vat-NAME.js` filenames processed in +// loadBasedir(). These names also appear in the `vats` and `devices` +// arguments to the bootstrap Vat's `bootstrap()` function, and in debug +// messages. + +export function insistVatID(s) { + try { + if (s !== `${s}`) { + throw new Error(`not a string`); + } + s = `${s}`; + if (!s.startsWith(`v`)) { + throw new Error(`does not start with 'v'`); + } + Nat(Number(s.slice(1))); + } catch (e) { + throw new Error(`'${s} is not a 'vNN'-style VatID: ${e.message}`); + } +} + +export function makeVatID(index) { + return `v${Nat(index)}`; +} + +export function insistDeviceID(s) { + try { + if (s !== `${s}`) { + throw new Error(`not a string`); + } + s = `${s}`; + if (!s.startsWith(`d`)) { + throw new Error(`does not start with 'd'`); + } + Nat(Number(s.slice(1))); + } catch (e) { + throw new Error(`'${s} is not a 'dNN'-style DeviceID: ${e.message}`); + } +} + +export function makeDeviceID(index) { + return `d${Nat(index)}`; +} + +export function parseVatOrDeviceID(s) { + if (s !== `${s}`) { + throw new Error(`${s} is not a string, so cannot be a VatID/DevieID`); + } + s = `${s}`; + let type; + let idSuffix; + if (s.startsWith('v')) { + type = 'vat'; + idSuffix = s.slice(1); + } else if (s.startsWith('v')) { + type = 'device'; + idSuffix = s.slice(1); + } else { + throw new Error(`'${s}' is neither a VatID nor a DeviceID`); + } + return harden({ type, id: Nat(Number(idSuffix)) }); +} diff --git a/src/kernel/kernel.js b/src/kernel/kernel.js index 3917bbc..d2bc165 100644 --- a/src/kernel/kernel.js +++ b/src/kernel/kernel.js @@ -8,10 +8,11 @@ import makeVatManager from './vatManager'; import makeDeviceManager from './deviceManager'; import makeKernelKeeper from './state/kernelKeeper'; import { insistKernelType, parseKernelSlot } from './parseKernelSlots'; -import { makeVatSlot } from '../parseVatSlots'; +import { makeVatSlot, parseVatSlot } from '../parseVatSlots'; import { insist } from '../insist'; import { insistCapData } from '../capdata'; import { insistMessage } from '../message'; +import { insistDeviceID, insistVatID } from './id'; function abbreviateReviver(_, arg) { if (typeof arg === 'string' && arg.length >= 40) { @@ -29,14 +30,14 @@ export default function buildKernel(kernelEndowments, initialState = '{}') { let started = false; // this holds externally-added vats, which are present at startup, but not // vats that are added later from within the kernel - const genesisVats = new Map(); // vatID -> { setup, options } + const genesisVats = new Map(); // name -> { setup, options } // we name this 'genesisDevices' for parallelism, but actually all devices // must be present at genesis - const genesisDevices = new Map(); + const genesisDevices = new Map(); // name -> { setup, options } const ephemeral = { - vats: new Map(), - devices: new Map(), + vats: new Map(), // vatID -> { manager, enablePipelining } + devices: new Map(), // deviceID -> { manager } log: [], }; @@ -124,8 +125,9 @@ export default function buildKernel(kernelEndowments, initialState = '{}') { function invoke(deviceSlot, method, args) { insistKernelType('device', deviceSlot); insistCapData(args); - const deviceName = kernelKeeper.ownerOfKernelDevice(deviceSlot); - const dev = ephemeral.devices.get(deviceName); + const deviceID = kernelKeeper.ownerOfKernelDevice(deviceSlot); + insistDeviceID(deviceID); + const dev = ephemeral.devices.get(deviceID); if (!dev) { throw new Error(`unknown deviceRef ${deviceSlot}`); } @@ -133,6 +135,7 @@ export default function buildKernel(kernelEndowments, initialState = '{}') { } function subscribe(vatID, kpid) { + insistVatID(vatID); const p = kernelKeeper.getKernelPromise(kpid); if (p.state === 'unresolved') { kernelKeeper.addSubscriberToPromise(kpid, vatID); @@ -145,6 +148,7 @@ export default function buildKernel(kernelEndowments, initialState = '{}') { function getResolveablePromise(kpid, resolvingVatID) { insistKernelType('promise', kpid); + insistVatID(resolvingVatID); const p = kernelKeeper.getKernelPromise(kpid); insist(p.state === 'unresolved', `${kpid} was already resolved`); insist( @@ -155,12 +159,10 @@ export default function buildKernel(kernelEndowments, initialState = '{}') { } function fulfillToPresence(vatID, kpid, targetSlot) { + insistVatID(vatID); + insistKernelType('promise', kpid); + insistKernelType('object', targetSlot); const p = getResolveablePromise(kpid, vatID); - const { type } = parseKernelSlot(targetSlot); - insist( - type === 'object', - `fulfillToPresence() must fulfill to object, not ${type}`, - ); const { subscribers, queue } = p; kernelKeeper.fulfillKernelPromiseToPresence(kpid, targetSlot); notifySubscribersAndQueue(kpid, subscribers, queue); @@ -173,6 +175,8 @@ export default function buildKernel(kernelEndowments, initialState = '{}') { } function fulfillToData(vatID, kpid, data) { + insistVatID(vatID); + insistKernelType('promise', kpid); insistCapData(data); const p = getResolveablePromise(kpid, vatID); const { subscribers, queue } = p; @@ -181,6 +185,8 @@ export default function buildKernel(kernelEndowments, initialState = '{}') { } function reject(vatID, kpid, data) { + insistVatID(vatID); + insistKernelType('promise', kpid); insistCapData(data); const p = getResolveablePromise(kpid, vatID); const { subscribers, queue } = p; @@ -199,22 +205,38 @@ export default function buildKernel(kernelEndowments, initialState = '{}') { reject, }; + function vatNameToID(name) { + const vatID = kernelKeeper.getVatIDForName(name); + insistVatID(vatID); + return vatID; + } + + function deviceNameToID(name) { + const deviceID = kernelKeeper.getDeviceIDForName(name); + insistDeviceID(deviceID); + return deviceID; + } + function addImport(forVatID, what) { - const kernelSlot = `${what}`; if (!started) { throw new Error('must do kernel.start() before addImport()'); // because otherwise we can't get the vatManager } - const vat = ephemeral.vats.get(`${forVatID}`); + insistVatID(forVatID); + const kernelSlot = `${what}`; + parseKernelSlot(what); + const vat = ephemeral.vats.get(forVatID); return vat.manager.mapKernelSlotToVatSlot(kernelSlot); } function addExport(fromVatID, what) { - const vatSlot = `${what}`; if (!started) { throw new Error('must do kernel.start() before addExport()'); // because otherwise we can't get the vatManager } + insistVatID(fromVatID); + const vatSlot = `${what}`; + parseVatSlot(vatSlot); const vat = ephemeral.vats.get(fromVatID); return vat.manager.mapVatSlotToKernelSlot(vatSlot); } @@ -223,7 +245,9 @@ export default function buildKernel(kernelEndowments, initialState = '{}') { // queue a message on the end of the queue, with 'absolute' kernelSlots. // Use 'step' or 'run' to execute it vatID = `${vatID}`; + insistVatID(vatID); vatSlot = `${vatSlot}`; + parseVatSlot(vatSlot); method = `${method}`; // we can't use insistCapData() here: .slots are from the controller's // Realm, not the kernel Realm, so it's the wrong kind of Array @@ -286,6 +310,7 @@ export default function buildKernel(kernelEndowments, initialState = '{}') { const { type } = parseKernelSlot(target); if (type === 'object') { const vatID = kernelKeeper.ownerOfKernelObject(target); + insistVatID(vatID); await deliverToVat(vatID, target, msg); } else if (type === 'promise') { const kp = kernelKeeper.getKernelPromise(target); @@ -309,6 +334,7 @@ export default function buildKernel(kernelEndowments, initialState = '{}') { if (!kp.decider) { kernelKeeper.addMessageToPromiseQueue(target, msg); } else { + insistVatID(kp.decider); const vat = ephemeral.vats.get(kp.decider); if (vat.enablePipelining) { await deliverToVat(kp.decider, target, msg); @@ -326,6 +352,8 @@ export default function buildKernel(kernelEndowments, initialState = '{}') { async function processNotify(message) { const { vatID, kpid } = message; + insistVatID(vatID); + insistKernelType('promise', kpid); const vat = ephemeral.vats.get(vatID); insist(vat, `unknown vatID ${vatID}`); const p = kernelKeeper.getKernelPromise(kpid); @@ -349,16 +377,62 @@ export default function buildKernel(kernelEndowments, initialState = '{}') { } } - function callBootstrap(vatID, argvString) { + function addGenesisVat(name, setup, options = {}) { + name = `${name}`; + harden(setup); + // 'setup' must be an in-realm function. This test guards against + // accidents, but not against malice. MarkM thinks there is no reliable + // way to test this. + if (!(setup instanceof Function)) { + throw Error('setup is not an in-realm function'); + } + // for now, we guard against 'options' by treating it as JSON-able data + options = JSON.parse(JSON.stringify(options)); + // todo: consider having vats indicate 'enablePipelining' during setup(), + // rather than using options= during kernel.addGenesisVat() + const knownOptions = new Set(['enablePipelining']); + for (const k of Object.getOwnPropertyNames(options)) { + if (!knownOptions.has(k)) { + throw new Error(`unknown option ${k}`); + } + } + + if (started) { + throw new Error(`addGenesisVat() cannot be called after kernel.start`); + } + if (genesisVats.has(name)) { + throw new Error(`vatID ${name} already added`); + } + genesisVats.set(name, { setup, options }); + } + + function addGenesisDevice(name, setup, endowments) { + console.log(`kernel.addDevice(${name})`); + name = `${name}`; + harden(setup); + if (!(setup instanceof Function)) { + throw Error('setup is not an in-realm function'); + } + if (started) { + throw new Error(`addDevice() cannot be called after kernel.start`); + } + if (genesisDevices.has(name)) { + throw new Error(`deviceName ${name} already added`); + } + genesisDevices.set(name, { setup, endowments }); + } + + function callBootstrap(bootstrapVatID, argvString) { // we invoke obj[0].bootstrap with an object that contains 'vats' and // 'argv'. + insistVatID(bootstrapVatID); const argv = JSON.parse(`${argvString}`); // each key of 'vats' will be serialized as a reference to its obj0 const vrefs = new Map(); const vatObj0s = {}; kernelKeeper.getAllVatNames().forEach(name => { - const targetVatID = name; - const vatManager = ephemeral.vats.get(targetVatID).manager; + const vatID = kernelKeeper.getVatIDForName(name); + const { manager } = ephemeral.vats.get(vatID); // we happen to give _bootstrap to itself, because unit tests that // don't have any other vats (bootstrap-only configs) then get a // non-empty object as vatObj0s, since an empty object would be @@ -366,14 +440,14 @@ export default function buildKernel(kernelEndowments, initialState = '{}') { // bootstrap object to call itself, though. const vref = harden({ toString() { - return targetVatID; + return name; }, }); // marker - vatObj0s[targetVatID] = vref; + vatObj0s[name] = vref; const vatSlot = makeVatSlot('object', true, 0); - const kernelSlot = vatManager.mapVatSlotToKernelSlot(vatSlot); + const kernelSlot = manager.mapVatSlotToKernelSlot(vatSlot); vrefs.set(vref, kernelSlot); - console.log(`adding vref ${targetVatID}`); + console.log(`adding vref ${name} [${vatID}]`); }); const drefs = new Map(); @@ -383,14 +457,15 @@ export default function buildKernel(kernelEndowments, initialState = '{}') { // entry to this object so it will serialize as pass-by-copy. We can // remove the dummy entry after we add the 'addVat' device const deviceObj0s = { _dummy: 'dummy' }; - kernelKeeper.getAllDeviceNames().forEach(deviceName => { - const deviceManager = ephemeral.devices.get(deviceName).manager; + kernelKeeper.getAllDeviceNames().forEach(name => { + const deviceID = kernelKeeper.getDeviceIDForName(name); + const { manager } = ephemeral.devices.get(deviceID); const dref = harden({}); - deviceObj0s[deviceName] = dref; + deviceObj0s[name] = dref; const devSlot = makeVatSlot('device', true, 0); - const kernelSlot = deviceManager.mapDeviceSlotToKernelSlot(devSlot); + const kernelSlot = manager.mapDeviceSlotToKernelSlot(devSlot); drefs.set(dref, kernelSlot); - console.log(`adding dref ${deviceName}`); + console.log(`adding dref ${name} [${deviceID}]`); }); if (Object.getOwnPropertyNames(deviceObj0s) === 0) { throw new Error('pass-by-copy rules require at least one device'); @@ -417,10 +492,10 @@ export default function buildKernel(kernelEndowments, initialState = '{}') { const args = harden([argv, vatObj0s, deviceObj0s]); // queueToExport() takes kernel-refs (ko+NN, kd+NN) in s.slots const boot0 = makeVatSlot('object', true, 0); - queueToExport(vatID, boot0, 'bootstrap', m.serialize(args)); + queueToExport(bootstrapVatID, boot0, 'bootstrap', m.serialize(args)); } - async function start(bootstrapVatID, argvString) { + async function start(bootstrapVatName, argvString) { if (started) { throw new Error('kernel.start already called'); } @@ -434,10 +509,13 @@ export default function buildKernel(kernelEndowments, initialState = '{}') { } // instantiate all vats and devices - for (const vatID of genesisVats.keys()) { - const { setup, options } = genesisVats.get(vatID); + for (const name of genesisVats.keys()) { + const { setup, options } = genesisVats.get(name); + const vatID = kernelKeeper.provideVatIDForName(name); + const vatKeeper = kernelKeeper.provideVatKeeper(vatID); + const helpers = harden({ - vatID, + vatID: name, // TODO: rename to 'name', update vats to match makeLiveSlots, log(...args) { const rendered = args.map(arg => @@ -449,10 +527,6 @@ export default function buildKernel(kernelEndowments, initialState = '{}') { }, }); - const vatKeeper = wasInitialized - ? kernelKeeper.getVat(vatID) - : kernelKeeper.createVat(vatID); - // the vatManager invokes setup() to build the userspace image const manager = makeVatManager( vatID, @@ -473,6 +547,9 @@ export default function buildKernel(kernelEndowments, initialState = '{}') { for (const name of genesisDevices.keys()) { const { setup, endowments } = genesisDevices.get(name); + const deviceID = kernelKeeper.provideDeviceIDForName(name); + const deviceKeeper = kernelKeeper.provideDeviceKeeper(deviceID); + const helpers = harden({ name, makeDeviceSlots, @@ -481,10 +558,6 @@ export default function buildKernel(kernelEndowments, initialState = '{}') { }, }); - const deviceKeeper = wasInitialized - ? kernelKeeper.getDevice(name) - : kernelKeeper.createDevice(name); - const manager = makeDeviceManager( name, syscallManager, @@ -494,15 +567,20 @@ export default function buildKernel(kernelEndowments, initialState = '{}') { deviceKeeper, ); // the vat record is not hardened: it holds mutable next-ID values - ephemeral.devices.set(name, { + ephemeral.devices.set(deviceID, { manager, }); } - // and enqueue the bootstrap() call - if (!wasInitialized && ephemeral.vats.has(bootstrapVatID)) { - console.log(`=> queueing bootstrap()`); - callBootstrap(bootstrapVatID, argvString); + // And enqueue the bootstrap() call. If we're reloading from an + // initialized state vector, this call will already be in the bootstrap + // vat's transcript, so we don't re-queue it. + if (!wasInitialized) { + if (bootstrapVatName) { + const bootstrapVatID = vatNameToID(bootstrapVatName); + console.log(`=> queueing bootstrap()`); + callBootstrap(bootstrapVatID, argvString); + } } // if it *was* initialized, replay the transcripts @@ -523,62 +601,48 @@ export default function buildKernel(kernelEndowments, initialState = '{}') { kernelKeeper.setInitialized(); } - const kernel = harden({ - addGenesisVat(vatID0, setup, options = {}) { - const vatID = `${vatID0}`; - harden(setup); - // 'setup' must be an in-realm function. This test guards against - // accidents, but not against malice. MarkM thinks there is no reliable - // way to test this. - if (!(setup instanceof Function)) { - throw Error('setup is not an in-realm function'); - } - // for now, we guard against 'options' by treating it as JSON-able data - options = JSON.parse(JSON.stringify(options)); - // todo: consider having vats indicate 'enablePipelining' during - // setup(), rather than using options= during kernel.addVat() - const knownOptions = new Set(['enablePipelining']); - for (const k of Object.getOwnPropertyNames(options)) { - if (!knownOptions.has(k)) { - throw new Error(`unknown option ${k}`); - } - } + async function step() { + if (!started) { + throw new Error('must do kernel.start() before step()'); + } + // process a single message + if (!kernelKeeper.isRunQueueEmpty()) { + await processQueueMessage(kernelKeeper.getNextMsg()); + } + } - if (started) { - throw new Error(`addGenesisVat() cannot be called after kernel.start`); - } - if (genesisVats.has(vatID)) { - throw new Error(`vatID ${vatID} already added`); - } - genesisVats.set(vatID, { setup, options }); - }, + async function run() { + if (!started) { + throw new Error('must do kernel.start() before run()'); + } + // process all messages, until syscall.pause() is invoked + running = true; + while (running && !kernelKeeper.isRunQueueEmpty()) { + // eslint-disable-next-line no-await-in-loop + await processQueueMessage(kernelKeeper.getNextMsg()); + } + } - addGenesisDevice(deviceName, setup, endowments) { - console.log(`kernel.addDevice(${deviceName})`); - harden(setup); - if (!(setup instanceof Function)) { - throw Error('setup is not an in-realm function'); - } - if (started) { - throw new Error(`addDevice() cannot be called after kernel.start`); - } - if (genesisDevices.has(deviceName)) { - throw new Error(`deviceName ${deviceName} already added`); - } - genesisDevices.set(deviceName, { setup, endowments }); - }, + function getState() { + return kernelKeeper.getState(); + } - addImport, - addExport, + const kernel = harden({ + // these are meant for the controller + addGenesisVat, + addGenesisDevice, + start, + + step, + run, + getState, + + // the rest are for testing and debugging log(str) { ephemeral.log.push(`${str}`); }, - getState() { - return kernelKeeper.getState(); - }, - dump() { const stateDump = kernelKeeper.dump(); // note: dump().log is not deterministic, since log() does not go @@ -590,30 +654,10 @@ export default function buildKernel(kernelEndowments, initialState = '{}') { return stateDump; }, - start, - - async run() { - if (!started) { - throw new Error('must do kernel.start() before run()'); - } - // process all messages, until syscall.pause() is invoked - running = true; - while (running && !kernelKeeper.isRunQueueEmpty()) { - // eslint-disable-next-line no-await-in-loop - await processQueueMessage(kernelKeeper.getNextMsg()); - } - }, - - async step() { - if (!started) { - throw new Error('must do kernel.start() before step()'); - } - // process a single message - if (!kernelKeeper.isRunQueueEmpty()) { - await processQueueMessage(kernelKeeper.getNextMsg()); - } - }, - + addImport, + addExport, + vatNameToID, + deviceNameToID, queueToExport, }); diff --git a/src/kernel/state/deviceKeeper.js b/src/kernel/state/deviceKeeper.js index d23423e..5b882a7 100644 --- a/src/kernel/state/deviceKeeper.js +++ b/src/kernel/state/deviceKeeper.js @@ -2,15 +2,16 @@ import harden from '@agoric/harden'; import { insist } from '../../insist'; import { parseKernelSlot } from '../parseKernelSlots'; import { makeVatSlot, parseVatSlot } from '../../parseVatSlots'; +import { insistDeviceID } from '../id'; -// makeVatKeeper is a pure function: all state is kept in the argument object +export function initializeDeviceState(deviceID, state) { + state.kernelSlotToDevSlot = {}; // kdNN -> d+NN, koNN -> o-NN + state.devSlotToKernelSlot = {}; // d+NN -> kdNN, o-NN -> koNN + state.nextObjectID = 10; // for imports, the NN in o-NN +} -function makeDeviceKeeper(state, deviceName, addKernelObject, addKernelDevice) { - function createStartingDeviceState() { - state.kernelSlotToDevSlot = {}; // kdNN -> d+NN, koNN -> o-NN - state.devSlotToKernelSlot = {}; // d+NN -> kdNN, o-NN -> koNN - state.nextObjectID = 10; // for imports, the NN in o-NN - } +export function makeDeviceKeeper(state, deviceID, addKernelDeviceNode) { + insistDeviceID(deviceID); function mapDeviceSlotToKernelSlot(devSlot) { insist(`${devSlot}` === devSlot, 'non-string devSlot'); @@ -22,11 +23,11 @@ function makeDeviceKeeper(state, deviceName, addKernelObject, addKernelDevice) { if (allocatedByVat) { let kernelSlot; if (type === 'object') { - kernelSlot = addKernelObject(deviceName); + throw new Error(`devices cannot export Objects`); } else if (type === 'promise') { - throw new Error(`devices do not accept Promises`); + throw new Error(`devices cannot export Promises`); } else if (type === 'device') { - kernelSlot = addKernelDevice(deviceName); + kernelSlot = addKernelDeviceNode(deviceID); } else { throw new Error(`unknown type ${type}`); } @@ -82,13 +83,12 @@ function makeDeviceKeeper(state, deviceName, addKernelObject, addKernelDevice) { const res = []; Object.getOwnPropertyNames(state.kernelSlotToDevSlot).forEach(ks => { const ds = state.kernelSlotToDevSlot[ks]; - res.push([ks, deviceName, ds]); + res.push([ks, deviceID, ds]); }); return harden(res); } return harden({ - createStartingDeviceState, mapDeviceSlotToKernelSlot, mapKernelSlotToDeviceSlot, getDeviceState, @@ -96,5 +96,3 @@ function makeDeviceKeeper(state, deviceName, addKernelObject, addKernelDevice) { dumpState, }); } - -export default makeDeviceKeeper; diff --git a/src/kernel/state/kernelKeeper.js b/src/kernel/state/kernelKeeper.js index 7fd9064..2ed1a19 100644 --- a/src/kernel/state/kernelKeeper.js +++ b/src/kernel/state/kernelKeeper.js @@ -1,8 +1,10 @@ import harden from '@agoric/harden'; -import makeVatKeeper from './vatKeeper'; -import makeDeviceKeeper from './deviceKeeper'; +import { initializeVatState, makeVatKeeper } from './vatKeeper'; +import { initializeDeviceState, makeDeviceKeeper } from './deviceKeeper'; +import { insist } from '../../insist'; import { insistKernelType, makeKernelSlot } from '../parseKernelSlots'; import { insistCapData } from '../../capdata'; +import { insistVatID, insistDeviceID, makeDeviceID, makeVatID } from '../id'; // This holds all the kernel state, including that of each Vat and Device, in // a single JSON-serializable object. At any moment (well, really only @@ -10,9 +12,20 @@ import { insistCapData } from '../../capdata'; // be used as the 'initialState' argument of some future makeKernelKeeper() // call, and that future instance should behave identically to this one. +// todo: this is temporary, until we replace the state object with proper +// Maps, but that requires the new state-management scheme +function hasOwnProperty(obj, name) { + return Object.prototype.hasOwnProperty.call(obj, name); +} + function makeKernelKeeper(initialState) { const state = JSON.parse(`${initialState}`); + const ephemeral = harden({ + vatKeepers: new Map(), // vatID -> vatKeeper + deviceKeepers: new Map(), // deviceID -> deviceKeeper + }); + function getInitialized() { return !!Object.getOwnPropertyDescriptor(state, 'initialized'); } @@ -22,8 +35,15 @@ function makeKernelKeeper(initialState) { } function createStartingKernelState() { - state.vats = {}; - state.devices = {}; + // TODO: fear not, all of this will be replaced by DB lookups + state.namedVats = {}; // name -> vatID + state.nextVatID = 1; + state.vats = {}; // vatID -> { kernelSlotToVatSlot, transcript, .. } + + state.namedDevices = {}; // name -> deviceID + state.devices = {}; // deviceID -> { kernelSlotToDevSlot, .. } + state.nextDeviceID = 7; + state.runQueue = []; state.kernelObjects = {}; // kernelObjects[koNN] = { owner: vatID } state.nextObjectIndex = 20; @@ -33,12 +53,13 @@ function makeKernelKeeper(initialState) { state.nextPromiseIndex = 40; } - function addKernelObject(ownerVatID) { + function addKernelObject(ownerID) { + insistVatID(ownerID); const id = state.nextObjectIndex; state.nextObjectIndex = id + 1; const s = makeKernelSlot('object', id); state.kernelObjects[s] = harden({ - owner: ownerVatID, + owner: ownerID, }); return s; } @@ -48,12 +69,13 @@ function makeKernelKeeper(initialState) { return state.kernelObjects[kernelSlot].owner; } - function addKernelDevice(deviceName) { + function addKernelDeviceNode(deviceID) { + insistDeviceID(deviceID); const id = state.nextDeviceIndex; state.nextDeviceIndex = id + 1; const s = makeKernelSlot('device', id); state.kernelDevices[s] = harden({ - owner: deviceName, + owner: deviceID, }); return s; } @@ -64,6 +86,7 @@ function makeKernelKeeper(initialState) { } function addKernelPromise(deciderVatID) { + insistVatID(deciderVatID); const kpid = state.nextPromiseIndex; state.nextPromiseIndex = kpid + 1; const s = makeKernelSlot('promise', kpid); @@ -138,6 +161,7 @@ function makeKernelKeeper(initialState) { function addSubscriberToPromise(kernelSlot, vatID) { insistKernelType('promise', kernelSlot); + insistVatID(vatID); const p = state.kernelPromises[kernelSlot]; if (p === undefined) { throw new Error(`unknown kernelPromise '${kernelSlot}'`); @@ -163,69 +187,93 @@ function makeKernelKeeper(initialState) { return state.runQueue.shift(); } - // vatID must already exist - function getVat(vatID) { - const vatState = state.vats[vatID]; - if (vatState === undefined) { - throw new Error(`unknown vatID id '${vatID}'`); + function getVatIDForName(name) { + insist(name === `${name}`, `${name} is not a string`); + if (!hasOwnProperty(state.namedVats, name)) { + throw new Error(`vat name ${name} must exist, but doesn't`); } - return makeVatKeeper(vatState, vatID, addKernelObject, addKernelPromise); + return state.namedVats[name]; } - function createVat(vatID) { - vatID = `${vatID}`; - if (vatID in state.vats) { - throw new Error(`vatID '${vatID}' already exists in state.vats`); + function provideVatIDForName(name) { + insist(name === `${name}`); + if (!hasOwnProperty(state.namedVats, name)) { + const index = state.nextVatID; + state.nextVatID += 1; + state.namedVats[name] = makeVatID(index); } - const vatState = {}; - state.vats[vatID] = vatState; - const vk = makeVatKeeper( - vatState, - vatID, - addKernelObject, - addKernelPromise, - ); - vk.createStartingVatState(); - return vk; + return state.namedVats[name]; + } + + function provideVatKeeper(vatID) { + insistVatID(vatID); + if (!hasOwnProperty(state.vats, vatID)) { + const vatState = {}; + initializeVatState(vatID, vatState); + state.vats[vatID] = vatState; + } + if (!ephemeral.vatKeepers.has(vatID)) { + const vk = makeVatKeeper( + state.vats[vatID], + vatID, + addKernelObject, + addKernelPromise, + ); + ephemeral.vatKeepers.set(vatID, vk); + } + return ephemeral.vatKeepers.get(vatID); + } + + function getAllVatIDs() { + return Array.from(Object.getOwnPropertyNames(state.vats)).sort(); } function getAllVatNames() { - return Object.getOwnPropertyNames(state.vats).sort(); + return Array.from(Object.getOwnPropertyNames(state.namedVats)).sort(); } - // deviceID must already exist - function getDevice(deviceID) { - const deviceState = state.devices[deviceID]; - if (deviceState === undefined) { - throw new Error(`unknown deviceID id '${deviceID}'`); + function getDeviceIDForName(name) { + insist(name === `${name}`); + if (!hasOwnProperty(state.namedDevices, name)) { + throw new Error(`device name ${name} must exist, but doesn't`); } - return makeDeviceKeeper( - deviceState, - deviceID, - addKernelObject, - addKernelDevice, - ); + return state.namedDevices[name]; } - function createDevice(deviceID) { - deviceID = `${deviceID}`; - if (deviceID in state.devices) { - throw new Error(`deviceID '${deviceID}' already exists in state.devices`); + function provideDeviceIDForName(name) { + insist(name === `${name}`); + if (!hasOwnProperty(state.namedDevices, name)) { + const index = state.nextDeviceID; + state.nextDeviceID += 1; + state.namedDevices[name] = makeDeviceID(index); } - const deviceState = {}; - state.devices[deviceID] = deviceState; - const dk = makeDeviceKeeper( - deviceState, - deviceID, - addKernelObject, - addKernelDevice, - ); - dk.createStartingDeviceState(); - return dk; + return state.namedDevices[name]; + } + + function provideDeviceKeeper(deviceID) { + insistDeviceID(deviceID); + if (!hasOwnProperty(state.devices, deviceID)) { + const deviceState = {}; + initializeDeviceState(deviceID, deviceState); + state.devices[deviceID] = deviceState; + } + if (!ephemeral.deviceKeepers.has(deviceID)) { + const dk = makeDeviceKeeper( + state.devices[deviceID], + deviceID, + addKernelDeviceNode, + ); + ephemeral.deviceKeepers.set(deviceID, dk); + } + return ephemeral.deviceKeepers.get(deviceID); + } + + function getAllDeviceIDs() { + return Array.from(Object.getOwnPropertyNames(state.devices)).sort(); } function getAllDeviceNames() { - return Object.getOwnPropertyNames(state.devices).sort(); + return Array.from(Object.getOwnPropertyNames(state.namedDevices)).sort(); } // used for persistence. This returns a JSON-serialized string, suitable to @@ -242,8 +290,8 @@ function makeKernelKeeper(initialState) { const vatTables = []; const kernelTable = []; - for (const vatID of getAllVatNames()) { - const vk = getVat(vatID); + for (const vatID of getAllVatIDs()) { + const vk = provideVatKeeper(vatID); // TODO: find some way to expose the liveSlots internal tables, the // kernel doesn't see them @@ -255,8 +303,8 @@ function makeKernelKeeper(initialState) { vk.dumpState().forEach(e => kernelTable.push(e)); } - for (const deviceName of getAllDeviceNames()) { - const dk = getDevice(deviceName); + for (const deviceID of getAllDeviceIDs()) { + const dk = provideDeviceKeeper(deviceID); dk.dumpState().forEach(e => kernelTable.push(e)); } @@ -332,12 +380,14 @@ function makeKernelKeeper(initialState) { getRunQueueLength, getNextMsg, - getVat, - createVat, + getVatIDForName, + provideVatIDForName, + provideVatKeeper, getAllVatNames, - getDevice, - createDevice, + getDeviceIDForName, + provideDeviceIDForName, + provideDeviceKeeper, getAllDeviceNames, getState, diff --git a/src/kernel/state/vatKeeper.js b/src/kernel/state/vatKeeper.js index 78b1c30..cbb86b6 100644 --- a/src/kernel/state/vatKeeper.js +++ b/src/kernel/state/vatKeeper.js @@ -2,25 +2,23 @@ import harden from '@agoric/harden'; import { insist } from '../../insist'; import { parseKernelSlot } from '../parseKernelSlots'; import { makeVatSlot, parseVatSlot } from '../../parseVatSlots'; +import { insistVatID } from '../id'; // makeVatKeeper is a pure function: all state is kept in the argument object -export default function makeVatKeeper( - state, - vatID, - addKernelObject, - addKernelPromise, -) { - function createStartingVatState() { - state.kernelSlotToVatSlot = {}; // kpNN -> p+NN, etc - state.vatSlotToKernelSlot = {}; // p+NN -> kpNN, etc - - state.nextObjectID = 50; - state.nextPromiseID = 60; - state.nextDeviceID = 70; - - state.transcript = []; - } +export function initializeVatState(vatID, state) { + state.kernelSlotToVatSlot = {}; // kpNN -> p+NN, etc + state.vatSlotToKernelSlot = {}; // p+NN -> kpNN, etc + + state.nextObjectID = 50; + state.nextPromiseID = 60; + state.nextDeviceID = 70; + + state.transcript = []; +} + +export function makeVatKeeper(state, vatID, addKernelObject, addKernelPromise) { + insistVatID(vatID); function insistVatSlotType(type, slot) { insist( @@ -107,7 +105,6 @@ export default function makeVatKeeper( } return harden({ - createStartingVatState, mapVatSlotToKernelSlot, mapKernelSlotToVatSlot, getTranscript, diff --git a/test/test-controller.js b/test/test-controller.js index a71903d..b80f1a8 100644 --- a/test/test-controller.js +++ b/test/test-controller.js @@ -27,10 +27,11 @@ async function simpleCall(t, withSES) { }; const controller = await buildVatController(config, withSES); const data = controller.dump(); - t.deepEqual(data.vatTables, [{ vatID: 'vat1', state: { transcript: [] } }]); + const vat1 = controller.vatNameToID('vat1'); + t.deepEqual(data.vatTables, [{ vatID: vat1, state: { transcript: [] } }]); t.deepEqual(data.kernelTable, []); - controller.queueToExport('vat1', 'o+1', 'foo', capdata('args')); + controller.queueToVatExport('vat1', 'o+1', 'foo', capdata('args')); t.deepEqual(controller.dump().runQueue, [ { msg: { @@ -105,6 +106,9 @@ async function bootstrapExport(t, withSES) { path.resolve(__dirname, 'basedir-controller-3'), ); const c = await buildVatController(config, withSES); + const bootstrapVatID = c.vatNameToID('_bootstrap'); + const leftVatID = c.vatNameToID('left'); + const rightVatID = c.vatNameToID('right'); // console.log(c.dump()); // console.log('SLOTS: ', c.dump().runQueue[0].slots); @@ -113,9 +117,9 @@ async function bootstrapExport(t, withSES) { const left0 = 'ko21'; const right0 = 'ko22'; const kt = [ - [boot0, '_bootstrap', 'o+0'], - [left0, 'left', 'o+0'], - [right0, 'right', 'o+0'], + [boot0, bootstrapVatID, 'o+0'], + [left0, leftVatID, 'o+0'], + [right0, rightVatID, 'o+0'], ]; checkKT(t, c, kt); @@ -150,9 +154,9 @@ async function bootstrapExport(t, withSES) { 'bootstrap called', 'bootstrap.obj0.bootstrap()', ]); - kt.push([left0, '_bootstrap', 'o-50']); - kt.push([right0, '_bootstrap', 'o-51']); - kt.push([fooP, '_bootstrap', 'p+5']); + kt.push([left0, bootstrapVatID, 'o-50']); + kt.push([right0, bootstrapVatID, 'o-51']); + kt.push([fooP, bootstrapVatID, 'p+5']); checkKT(t, c, kt); t.deepEqual(c.dump().runQueue, [ { @@ -178,9 +182,9 @@ async function bootstrapExport(t, withSES) { 'bootstrap.obj0.bootstrap()', 'left.foo 1', ]); - kt.push([right0, 'left', 'o-50']); - kt.push([fooP, 'left', 'p-60']); - kt.push([barP, 'left', 'p+5']); + kt.push([right0, leftVatID, 'o-50']); + kt.push([fooP, leftVatID, 'p-60']); + kt.push([barP, leftVatID, 'p+5']); checkKT(t, c, kt); t.deepEqual(c.dump().runQueue, [ @@ -196,7 +200,7 @@ async function bootstrapExport(t, withSES) { result: barP, }, }, - { type: 'notify', vatID: '_bootstrap', kpid: fooP }, + { type: 'notify', vatID: bootstrapVatID, kpid: fooP }, ]); await c.step(); @@ -210,12 +214,12 @@ async function bootstrapExport(t, withSES) { 'right.obj0.bar 2 true', ]); - kt.push([barP, 'right', 'p-60']); + kt.push([barP, rightVatID, 'p-60']); checkKT(t, c, kt); t.deepEqual(c.dump().runQueue, [ - { type: 'notify', vatID: '_bootstrap', kpid: fooP }, - { type: 'notify', vatID: 'left', kpid: barP }, + { type: 'notify', vatID: bootstrapVatID, kpid: fooP }, + { type: 'notify', vatID: leftVatID, kpid: barP }, ]); await c.step(); @@ -231,7 +235,7 @@ async function bootstrapExport(t, withSES) { checkKT(t, c, kt); t.deepEqual(c.dump().runQueue, [ - { type: 'notify', vatID: 'left', kpid: barP }, + { type: 'notify', vatID: leftVatID, kpid: barP }, ]); await c.step(); diff --git a/test/test-devices.js b/test/test-devices.js index faf6d1c..cfb5f70 100644 --- a/test/test-devices.js +++ b/test/test-devices.js @@ -60,7 +60,7 @@ async function test1(t, withSES) { }; const c = await buildVatController(config, withSES); await c.step(); - c.queueToExport('_bootstrap', 'o+0', 'step1', capargs([])); + c.queueToVatExport('_bootstrap', 'o+0', 'step1', capargs([])); await c.step(); console.log(c.dump().log); t.deepEqual(c.dump().log, [ @@ -191,10 +191,11 @@ async function testState(t, withSES) { // The initial state should be missing (null). Then we set it with the call // from bootstrap, and read it back. const c1 = await buildVatController(config, withSES, ['write+read']); + const d3 = c1.deviceNameToID('d3'); await c1.run(); t.deepEqual(c1.dump().log, ['undefined', 'w+r', 'called', 'got {"s":"new"}']); - t.deepEqual(JSON.parse(c1.getState()).devices.d3.deviceState, { s: 'new' }); - t.deepEqual(JSON.parse(c1.getState()).devices.d3.nextObjectID, 10); + t.deepEqual(JSON.parse(c1.getState()).devices[d3].deviceState, { s: 'new' }); + t.deepEqual(JSON.parse(c1.getState()).devices[d3].nextObjectID, 10); t.end(); } diff --git a/test/test-kernel.js b/test/test-kernel.js index 64e68e4..755a31c 100644 --- a/test/test-kernel.js +++ b/test/test-kernel.js @@ -51,13 +51,14 @@ export default function runTests() { } kernel.addGenesisVat('vat1', setup1); await kernel.start(); + const vat1 = kernel.vatNameToID('vat1'); let data = kernel.dump(); - t.deepEqual(data.vatTables, [{ vatID: 'vat1', state: { transcript: [] } }]); + t.deepEqual(data.vatTables, [{ vatID: vat1, state: { transcript: [] } }]); t.deepEqual(data.kernelTable, []); t.deepEqual(data.log, []); t.deepEqual(log, []); - kernel.queueToExport('vat1', 'o+1', 'foo', capdata('args')); + kernel.queueToExport(vat1, 'o+1', 'foo', capdata('args')); t.deepEqual(kernel.dump().runQueue, [ { type: 'send', @@ -96,22 +97,19 @@ export default function runTests() { kernel.addGenesisVat('vat1', setup1); kernel.addGenesisVat('vat2', setup1); await kernel.start(); + const vat1 = kernel.vatNameToID('vat1'); + const vat2 = kernel.vatNameToID('vat2'); const data = kernel.dump(); t.deepEqual(data.vatTables, [ - { vatID: 'vat1', state: { transcript: [] } }, - { vatID: 'vat2', state: { transcript: [] } }, + { vatID: vat1, state: { transcript: [] } }, + { vatID: vat2, state: { transcript: [] } }, ]); t.deepEqual(data.kernelTable, []); t.deepEqual(log, []); - const koFor5 = kernel.addExport('vat1', 'o+5'); - const koFor6 = kernel.addExport('vat2', 'o+6'); - kernel.queueToExport( - 'vat1', - 'o+1', - 'foo', - capdata('args', [koFor5, koFor6]), - ); + const koFor5 = kernel.addExport(vat1, 'o+5'); + const koFor6 = kernel.addExport(vat2, 'o+6'); + kernel.queueToExport(vat1, 'o+1', 'foo', capdata('args', [koFor5, koFor6])); t.deepEqual(kernel.dump().runQueue, [ { type: 'send', @@ -127,10 +125,10 @@ export default function runTests() { await kernel.run(); t.deepEqual(log, [['o+1', 'foo', capdata('args', ['o+5', 'o-50'])]]); t.deepEqual(kernel.dump().kernelTable, [ - [koFor5, 'vat1', 'o+5'], - [koFor6, 'vat1', 'o-50'], - [koFor6, 'vat2', 'o+6'], - ['ko22', 'vat1', 'o+1'], + [koFor5, vat1, 'o+5'], + [koFor6, vat1, 'o-50'], + [koFor6, vat2, 'o+6'], + ['ko22', vat1, 'o+1'], ]); t.end(); @@ -145,12 +143,14 @@ export default function runTests() { kernel.addGenesisVat('vat1', setup); kernel.addGenesisVat('vat2', setup); await kernel.start(); + const vat1 = kernel.vatNameToID('vat1'); + const vat2 = kernel.vatNameToID('vat2'); - const slot = kernel.addImport('vat1', kernel.addExport('vat2', 'o+5')); + const slot = kernel.addImport(vat1, kernel.addExport(vat2, 'o+5')); t.deepEqual(slot, 'o-50'); // first import t.deepEqual(kernel.dump().kernelTable, [ - ['ko20', 'vat1', 'o-50'], - ['ko20', 'vat2', 'o+5'], + ['ko20', vat1, 'o-50'], + ['ko20', vat2, 'o+5'], ]); t.end(); }); @@ -193,28 +193,30 @@ export default function runTests() { } kernel.addGenesisVat('vat2', setup2); await kernel.start(); + const vat1 = kernel.vatNameToID('vat1'); + const vat2 = kernel.vatNameToID('vat2'); - const t1 = kernel.addExport('vat1', 'o+1'); - const vat2Obj5 = kernel.addExport('vat2', 'o+5'); - v1tovat25 = kernel.addImport('vat1', vat2Obj5); + const t1 = kernel.addExport(vat1, 'o+1'); + const vat2Obj5 = kernel.addExport(vat2, 'o+5'); + v1tovat25 = kernel.addImport(vat1, vat2Obj5); t.deepEqual(v1tovat25, 'o-50'); // first allocation const data = kernel.dump(); t.deepEqual(data.vatTables, [ - { vatID: 'vat1', state: { transcript: [] } }, - { vatID: 'vat2', state: { transcript: [] } }, + { vatID: vat1, state: { transcript: [] } }, + { vatID: vat2, state: { transcript: [] } }, ]); const kt = [ - [t1, 'vat1', 'o+1'], - ['ko21', 'vat1', v1tovat25], - ['ko21', 'vat2', 'o+5'], + [t1, vat1, 'o+1'], + ['ko21', vat1, v1tovat25], + ['ko21', vat2, 'o+5'], ]; checkKT(t, kernel, kt); t.deepEqual(log, []); // o1!foo(args) - kernel.queueToExport('vat1', 'o+1', 'foo', capdata('args')); + kernel.queueToExport(vat1, 'o+1', 'foo', capdata('args')); t.deepEqual(log, []); t.deepEqual(kernel.dump().runQueue, [ { @@ -249,7 +251,7 @@ export default function runTests() { { id: 'kp40', state: 'unresolved', - decider: 'vat1', + decider: vat1, subscribers: [], queue: [], }, @@ -262,9 +264,9 @@ export default function runTests() { }, ]); - kt.push(['ko22', 'vat1', 'o+7']); - kt.push(['kp40', 'vat1', p7]); - kt.push(['kp41', 'vat1', 'p+5']); + kt.push(['ko22', vat1, 'o+7']); + kt.push(['kp40', vat1, p7]); + kt.push(['kp41', vat1, 'p+5']); checkKT(t, kernel, kt); await kernel.step(); @@ -277,14 +279,14 @@ export default function runTests() { { id: 'kp40', state: 'unresolved', - decider: 'vat1', + decider: vat1, subscribers: [], queue: [], }, { id: 'kp41', state: 'unresolved', - decider: 'vat2', + decider: vat2, subscribers: [], queue: [], }, @@ -292,16 +294,16 @@ export default function runTests() { ], ]); - kt.push(['ko22', 'vat2', 'o-50']); - kt.push(['kp41', 'vat2', 'p-61']); - kt.push(['kp40', 'vat2', 'p-60']); + kt.push(['ko22', vat2, 'o-50']); + kt.push(['kp41', vat2, 'p-61']); + kt.push(['kp40', vat2, 'p-60']); checkKT(t, kernel, kt); t.deepEqual(kernel.dump().promises, [ { id: 'kp40', state: 'unresolved', - decider: 'vat1', + decider: vat1, // Sending a promise from vat1 to vat2 doesn't cause vat2 to be // subscribed unless they want it. Liveslots will always subscribe, // because we don't have enough hooks into Promises to detect a @@ -312,7 +314,7 @@ export default function runTests() { { id: 'kp41', state: 'unresolved', - decider: 'vat2', + decider: vat2, subscribers: [], queue: [], }, @@ -363,36 +365,39 @@ export default function runTests() { kernel.addGenesisVat('vatC', setupC); await kernel.start(); + const vatA = kernel.vatNameToID('vatA'); + const vatB = kernel.vatNameToID('vatB'); + const vatC = kernel.vatNameToID('vatC'); - const alice = kernel.addExport('vatA', 'o+4'); - const bob = kernel.addExport('vatB', 'o+5'); - const carol = kernel.addExport('vatC', 'o+6'); + const alice = kernel.addExport(vatA, 'o+4'); + const bob = kernel.addExport(vatB, 'o+5'); + const carol = kernel.addExport(vatC, 'o+6'); - bobForA = kernel.addImport('vatA', bob); - carolForA = kernel.addImport('vatA', carol); + bobForA = kernel.addImport(vatA, bob); + carolForA = kernel.addImport(vatA, carol); // do an extra allocation to make sure we aren't confusing the indices const extraP = 'p+99'; - const ap = kernel.addExport('vatA', extraP); + const ap = kernel.addExport(vatA, extraP); const data = kernel.dump(); t.deepEqual(data.vatTables, [ - { vatID: 'vatA', state: { transcript: [] } }, - { vatID: 'vatB', state: { transcript: [] } }, - { vatID: 'vatC', state: { transcript: [] } }, + { vatID: vatA, state: { transcript: [] } }, + { vatID: vatB, state: { transcript: [] } }, + { vatID: vatC, state: { transcript: [] } }, ]); const kt = [ - [alice, 'vatA', 'o+4'], - [bob, 'vatA', bobForA], - [bob, 'vatB', 'o+5'], - [carol, 'vatA', carolForA], - [carol, 'vatC', 'o+6'], - [ap, 'vatA', extraP], + [alice, vatA, 'o+4'], + [bob, vatA, bobForA], + [bob, vatB, 'o+5'], + [carol, vatA, carolForA], + [carol, vatC, 'o+6'], + [ap, vatA, extraP], ]; checkKT(t, kernel, kt); t.deepEqual(log, []); - kernel.queueToExport('vatA', 'o+4', 'foo', capdata('args')); + kernel.queueToExport(vatA, 'o+4', 'foo', capdata('args')); await kernel.step(); t.deepEqual(log.shift(), ['vatA', 'o+4', 'foo', capdata('args')]); @@ -414,7 +419,7 @@ export default function runTests() { { id: ap, state: 'unresolved', - decider: 'vatA', + decider: vatA, subscribers: [], queue: [], }, @@ -426,13 +431,13 @@ export default function runTests() { queue: [], }, ]); - kt.push(['kp41', 'vatA', 'p+5']); + kt.push(['kp41', vatA, 'p+5']); checkKT(t, kernel, kt); await kernel.step(); t.deepEqual(log, [['vatB', 'o+5', 'intro', capdata('bargs', ['o-50'])]]); - kt.push([carol, 'vatB', 'o-50']); - kt.push(['kp41', 'vatB', 'p-60']); + kt.push([carol, vatB, 'o-50']); + kt.push(['kp41', vatB, 'p-60']); checkKT(t, kernel, kt); t.end(); @@ -463,21 +468,23 @@ export default function runTests() { kernel.addGenesisVat('vatB', setupB); await kernel.start(); + const vatA = kernel.vatNameToID('vatA'); + const vatB = kernel.vatNameToID('vatB'); - const alice = kernel.addExport('vatA', 'o+6'); - const bob = kernel.addExport('vatB', 'o+5'); + const alice = kernel.addExport(vatA, 'o+6'); + const bob = kernel.addExport(vatB, 'o+5'); - const B = kernel.addImport('vatA', bob); - const A = kernel.addImport('vatB', alice); + const B = kernel.addImport(vatA, bob); + const A = kernel.addImport(vatB, alice); // we send pr1 const pr1 = 'p+6'; const kt = [ - ['ko20', 'vatA', 'o+6'], - ['ko20', 'vatB', 'o-50'], - ['ko21', 'vatA', 'o-50'], - ['ko21', 'vatB', 'o+5'], + ['ko20', vatA, 'o+6'], + ['ko20', vatB, 'o-50'], + ['ko21', vatA, 'o-50'], + ['ko21', vatB, 'o+5'], ]; checkKT(t, kernel, kt); const kp = []; @@ -496,12 +503,12 @@ export default function runTests() { }, }, ]); - kt.push(['kp40', 'vatA', pr1]); + kt.push(['kp40', vatA, pr1]); checkKT(t, kernel, kt); kp.push({ id: 'kp40', state: 'unresolved', - decider: 'vatA', + decider: vatA, subscribers: [], queue: [], }); @@ -509,7 +516,7 @@ export default function runTests() { await kernel.run(); t.deepEqual(logB.shift(), ['o+5', 'foo1', capdata('args', ['p-60'])]); t.deepEqual(logB, []); - kt.push(['kp40', 'vatB', 'p-60']); // pr1 for B + kt.push(['kp40', vatB, 'p-60']); // pr1 for B checkKT(t, kernel, kt); // sending it a second time should arrive as the same thing @@ -554,22 +561,21 @@ export default function runTests() { kernel.addGenesisVat('vat2', emptySetup); await kernel.start(); + const vat1 = kernel.vatNameToID('vat1'); + const vat2 = kernel.vatNameToID('vat2'); - const kp = kernel.addExport('vat2', 'p+5'); - const pr = kernel.addImport('vat1', kp); + const kp = kernel.addExport(vat2, 'p+5'); + const pr = kernel.addImport(vat1, kp); t.deepEqual(pr, 'p-60'); - t.deepEqual(kernel.dump().kernelTable, [ - [kp, 'vat1', pr], - [kp, 'vat2', 'p+5'], - ]); + t.deepEqual(kernel.dump().kernelTable, [[kp, vat1, pr], [kp, vat2, 'p+5']]); syscall.subscribe(pr); t.deepEqual(kernel.dump().promises, [ { id: kp, state: 'unresolved', - decider: 'vat2', - subscribers: ['vat1'], + decider: vat2, + subscribers: [vat1], queue: [], }, ]); @@ -602,14 +608,16 @@ export default function runTests() { } kernel.addGenesisVat('vatB', setupB); await kernel.start(); + const vatA = kernel.vatNameToID('vatA'); + const vatB = kernel.vatNameToID('vatB'); const aliceForA = 'o+6'; const pForB = 'p+5'; - const pForKernel = kernel.addExport('vatB', pForB); - const pForA = kernel.addImport('vatA', pForKernel); + const pForKernel = kernel.addExport(vatB, pForB); + const pForA = kernel.addImport(vatA, pForKernel); t.deepEqual(kernel.dump().kernelTable, [ - [pForKernel, 'vatA', pForA], - [pForKernel, 'vatB', pForB], + [pForKernel, vatA, pForA], + [pForKernel, vatB, pForB], ]); syscallA.subscribe(pForA); @@ -617,8 +625,8 @@ export default function runTests() { { id: pForKernel, state: 'unresolved', - decider: 'vatB', - subscribers: ['vatA'], + decider: vatB, + subscribers: [vatA], queue: [], }, ]); @@ -629,7 +637,7 @@ export default function runTests() { t.deepEqual(kernel.dump().runQueue, [ { type: 'notify', - vatID: 'vatA', + vatID: vatA, kpid: pForKernel, }, ]); @@ -673,19 +681,21 @@ export default function runTests() { } kernel.addGenesisVat('vatB', setupB); await kernel.start(); + const vatA = kernel.vatNameToID('vatA'); + const vatB = kernel.vatNameToID('vatB'); const bobForB = 'o+6'; - const bobForKernel = kernel.addExport('vatB', 'o+6'); - const bobForA = kernel.addImport('vatA', bobForKernel); + const bobForKernel = kernel.addExport(vatB, 'o+6'); + const bobForA = kernel.addImport(vatA, bobForKernel); const pForB = 'p+5'; - const pForKernel = kernel.addExport('vatB', 'p+5'); - const pForA = kernel.addImport('vatA', pForKernel); + const pForKernel = kernel.addExport(vatB, 'p+5'); + const pForA = kernel.addImport(vatA, pForKernel); const kt = [ - [bobForKernel, 'vatB', bobForB], - [bobForKernel, 'vatA', bobForA], - [pForKernel, 'vatA', pForA], - [pForKernel, 'vatB', pForB], + [bobForKernel, vatB, bobForB], + [bobForKernel, vatA, bobForA], + [pForKernel, vatA, pForA], + [pForKernel, vatB, pForB], ]; checkKT(t, kernel, kt); @@ -694,8 +704,8 @@ export default function runTests() { { id: pForKernel, state: 'unresolved', - decider: 'vatB', - subscribers: ['vatA'], + decider: vatB, + subscribers: [vatA], queue: [], }, ]); @@ -705,7 +715,7 @@ export default function runTests() { t.deepEqual(kernel.dump().runQueue, [ { type: 'notify', - vatID: 'vatA', + vatID: vatA, kpid: pForKernel, }, ]); @@ -747,14 +757,16 @@ export default function runTests() { } kernel.addGenesisVat('vatB', setupB); await kernel.start(); + const vatA = kernel.vatNameToID('vatA'); + const vatB = kernel.vatNameToID('vatB'); const aliceForA = 'o+6'; const pForB = 'p+5'; - const pForKernel = kernel.addExport('vatB', pForB); - const pForA = kernel.addImport('vatA', pForKernel); + const pForKernel = kernel.addExport(vatB, pForB); + const pForA = kernel.addImport(vatA, pForKernel); t.deepEqual(kernel.dump().kernelTable, [ - [pForKernel, 'vatA', pForA], - [pForKernel, 'vatB', pForB], + [pForKernel, vatA, pForA], + [pForKernel, vatB, pForB], ]); syscallA.subscribe(pForA); @@ -762,8 +774,8 @@ export default function runTests() { { id: pForKernel, state: 'unresolved', - decider: 'vatB', - subscribers: ['vatA'], + decider: vatB, + subscribers: [vatA], queue: [], }, ]); @@ -774,7 +786,7 @@ export default function runTests() { t.deepEqual(kernel.dump().runQueue, [ { type: 'notify', - vatID: 'vatA', + vatID: vatA, kpid: pForKernel, }, ]); @@ -809,13 +821,15 @@ export default function runTests() { kernel.addGenesisVat('vatA', setup); kernel.addGenesisVat('vatB', emptySetup); await kernel.start(); + const vatA = kernel.vatNameToID('vatA'); + const vatB = kernel.vatNameToID('vatB'); - const alice = kernel.addExport('vatA', aliceForAlice); - const bob = kernel.addExport('vatB', 'o+2'); - const bobForAlice = kernel.addImport('vatA', bob); + const alice = kernel.addExport(vatA, aliceForAlice); + const bob = kernel.addExport(vatB, 'o+2'); + const bobForAlice = kernel.addImport(vatA, bob); kernel.queueToExport( - 'vatA', + vatA, aliceForAlice, 'store', capdata('args string', [alice, bob]), @@ -869,22 +883,24 @@ export default function runTests() { } kernel.addGenesisVat('vatB', setupB); await kernel.start(); + const vatA = kernel.vatNameToID('vatA'); + const vatB = kernel.vatNameToID('vatB'); const bobForB = 'o+6'; - const bobForKernel = kernel.addExport('vatB', bobForB); - const bobForA = kernel.addImport('vatA', bobForKernel); + const bobForKernel = kernel.addExport(vatB, bobForB); + const bobForA = kernel.addImport(vatA, bobForKernel); const p1ForA = 'p+1'; syscall.send(bobForA, 'foo', capdata('fooargs'), p1ForA); - const p1ForKernel = kernel.addExport('vatA', p1ForA); + const p1ForKernel = kernel.addExport(vatA, p1ForA); const p2ForA = 'p+2'; syscall.send(p1ForA, 'bar', capdata('barargs'), p2ForA); - const p2ForKernel = kernel.addExport('vatA', p2ForA); + const p2ForKernel = kernel.addExport(vatA, p2ForA); const p3ForA = 'p+3'; syscall.send(p2ForA, 'urgh', capdata('urghargs'), p3ForA); - const p3ForKernel = kernel.addExport('vatA', p3ForA); + const p3ForKernel = kernel.addExport(vatA, p3ForA); t.deepEqual(kernel.dump().promises, [ { @@ -912,7 +928,7 @@ export default function runTests() { await kernel.run(); - const p1ForB = kernel.addImport('vatB', p1ForKernel); + const p1ForB = kernel.addImport(vatB, p1ForKernel); t.deepEqual(log.shift(), [bobForB, 'foo', capdata('fooargs'), p1ForB]); t.deepEqual(log, []); @@ -920,7 +936,7 @@ export default function runTests() { { id: p1ForKernel, state: 'unresolved', - decider: 'vatB', + decider: vatB, subscribers: [], queue: [ { @@ -978,22 +994,24 @@ export default function runTests() { } kernel.addGenesisVat('vatB', setupB, { enablePipelining: true }); await kernel.start(); + const vatA = kernel.vatNameToID('vatA'); + const vatB = kernel.vatNameToID('vatB'); const bobForB = 'o+6'; - const bobForKernel = kernel.addExport('vatB', bobForB); - const bobForA = kernel.addImport('vatA', bobForKernel); + const bobForKernel = kernel.addExport(vatB, bobForB); + const bobForA = kernel.addImport(vatA, bobForKernel); const p1ForA = 'p+1'; syscall.send(bobForA, 'foo', capdata('fooargs'), p1ForA); - const p1ForKernel = kernel.addExport('vatA', p1ForA); + const p1ForKernel = kernel.addExport(vatA, p1ForA); const p2ForA = 'p+2'; syscall.send(p1ForA, 'bar', capdata('barargs'), p2ForA); - const p2ForKernel = kernel.addExport('vatA', p2ForA); + const p2ForKernel = kernel.addExport(vatA, p2ForA); const p3ForA = 'p+3'; syscall.send(p2ForA, 'urgh', capdata('urghargs'), p3ForA); - const p3ForKernel = kernel.addExport('vatA', p3ForA); + const p3ForKernel = kernel.addExport(vatA, p3ForA); t.deepEqual(kernel.dump().promises, [ { @@ -1021,9 +1039,9 @@ export default function runTests() { await kernel.run(); - const p1ForB = kernel.addImport('vatB', p1ForKernel); - const p2ForB = kernel.addImport('vatB', p2ForKernel); - const p3ForB = kernel.addImport('vatB', p3ForKernel); + const p1ForB = kernel.addImport(vatB, p1ForKernel); + const p2ForB = kernel.addImport(vatB, p2ForKernel); + const p3ForB = kernel.addImport(vatB, p3ForKernel); t.deepEqual(log.shift(), [bobForB, 'foo', capdata('fooargs'), p1ForB]); t.deepEqual(log.shift(), [p1ForB, 'bar', capdata('barargs'), p2ForB]); t.deepEqual(log.shift(), [p2ForB, 'urgh', capdata('urghargs'), p3ForB]); @@ -1033,21 +1051,21 @@ export default function runTests() { { id: p1ForKernel, state: 'unresolved', - decider: 'vatB', + decider: vatB, subscribers: [], queue: [], }, { id: p2ForKernel, state: 'unresolved', - decider: 'vatB', + decider: vatB, subscribers: [], queue: [], }, { id: p3ForKernel, state: 'unresolved', - decider: 'vatB', + decider: vatB, subscribers: [], queue: [], }, diff --git a/test/test-liveslots.js b/test/test-liveslots.js index 900274e..297b0de 100644 --- a/test/test-liveslots.js +++ b/test/test-liveslots.js @@ -46,12 +46,15 @@ test('calls', async t => { kernel.addGenesisVat('vat', setup); await kernel.start('bootstrap', `[]`); + const bootstrapVatID = kernel.vatNameToID('bootstrap'); + const vatID = kernel.vatNameToID('vat'); + // cycle past the bootstrap() call await kernel.step(); log.shift(); t.deepEqual(kernel.dump().runQueue, []); - const root = kernel.addImport('bootstrap', kernel.addExport('vat', 'o+0')); + const root = kernel.addImport(bootstrapVatID, kernel.addExport(vatID, 'o+0')); // root!one() // sendOnly syscall.send(root, 'one', capargs(['args']), undefined); @@ -127,9 +130,12 @@ test('liveslots pipelines to syscall.send', async t => { kernel.addGenesisVat('b', setupB); await kernel.start(); // no bootstrap + const a = kernel.vatNameToID('a'); + const b = kernel.vatNameToID('b'); + t.deepEqual(kernel.dump().runQueue, []); - const root = kernel.addImport('b', kernel.addExport('a', 'o+0')); + const root = kernel.addImport(b, kernel.addExport(a, 'o+0')); // root!one(x) // sendOnly syscall.send(