diff --git a/packages/SwingSet/src/controller.js b/packages/SwingSet/src/controller.js index 4f0b38dab62..48081e194e4 100644 --- a/packages/SwingSet/src/controller.js +++ b/packages/SwingSet/src/controller.js @@ -59,7 +59,6 @@ export async function makeSwingsetController( slogCallbacks, slogFile, testTrackDecref, - defaultManagerType = env.WORKER_TYPE || 'local', } = runtimeOptions; if (typeof Compartment === 'undefined') { throw Error('SES must be installed before calling makeSwingsetController'); @@ -230,7 +229,7 @@ export async function makeSwingsetController( FinalizationRegistry, }; - const kernelOptions = { verbose, testTrackDecref, defaultManagerType }; + const kernelOptions = { verbose, testTrackDecref }; const kernel = buildKernel(kernelEndowments, deviceEndowments, kernelOptions); if (runtimeOptions.verbose) { @@ -324,14 +323,12 @@ export async function buildVatController( debugPrefix, slogCallbacks, testTrackDecref, - defaultManagerType, } = runtimeOptions; const actualRuntimeOptions = { verbose, debugPrefix, testTrackDecref, slogCallbacks, - defaultManagerType, }; const initializationOptions = { verbose, kernelBundles }; let bootstrapResult; diff --git a/packages/SwingSet/src/initializeSwingset.js b/packages/SwingSet/src/initializeSwingset.js index b9e1e48c799..2bb94bd45cb 100644 --- a/packages/SwingSet/src/initializeSwingset.js +++ b/packages/SwingSet/src/initializeSwingset.js @@ -1,4 +1,4 @@ -/* global require */ +/* global require, process */ // @ts-check import fs from 'fs'; import path from 'path'; @@ -75,6 +75,7 @@ function byName(a, b) { /** * @typedef {Object} SwingSetConfig a swingset config object * @property {string} [bootstrap] + * @property { ManagerType } [defaultManagerType] * @property {SwingSetConfigDescriptor} [vats] * @property {SwingSetConfigDescriptor} [bundles] * @property {*} [devices] @@ -212,7 +213,6 @@ function normalizeConfigDescriptor(desc, dirname, expectParameters) { */ export function loadSwingsetConfigFile(configPath) { try { - /** @type { SwingSetConfig } */ const config = JSON.parse(fs.readFileSync(configPath, 'utf-8')); const dirname = path.dirname(configPath); normalizeConfigDescriptor(config.vats, dirname, true); @@ -238,17 +238,18 @@ export function swingsetIsInitialized(storage) { } /** - * * @param {SwingSetConfig} config * @param {string[]} argv * @param {*} hostStorage * @param {{ kernelBundles?: Record }} initializationOptions + * @param {{ env?: Record }} runtimeOptions */ export async function initializeSwingset( config, argv = [], hostStorage = initSwingStore().storage, initializationOptions = {}, + runtimeOptions = {}, ) { insistStorageAPI(hostStorage); @@ -269,6 +270,23 @@ export async function initializeSwingset( config.devices = {}; } + // Use ambient process.env only if caller did not specify. + const { env: { WORKER_TYPE } = process.env } = runtimeOptions; + const defaultManagerType = config.defaultManagerType || WORKER_TYPE; + switch (defaultManagerType) { + case 'local': + case 'nodeWorker': + case 'node-subprocess': + case 'xs-worker': + config.defaultManagerType = defaultManagerType; + break; + case undefined: + config.defaultManagerType = 'local'; + break; + default: + assert.fail(X`unknown manager type ${defaultManagerType}`); + } + const { kernelBundles = await buildKernelBundles() } = initializationOptions; hostStorage.set('kernelBundle', JSON.stringify(kernelBundles.kernel)); diff --git a/packages/SwingSet/src/kernel/initializeKernel.js b/packages/SwingSet/src/kernel/initializeKernel.js index 39c990ba346..105325260af 100644 --- a/packages/SwingSet/src/kernel/initializeKernel.js +++ b/packages/SwingSet/src/kernel/initializeKernel.js @@ -24,7 +24,7 @@ export function initializeKernel(config, hostStorage, verbose = false) { const wasInitialized = kernelKeeper.getInitialized(); assert(!wasInitialized); - kernelKeeper.createStartingKernelState(); + kernelKeeper.createStartingKernelState(config.defaultManagerType || 'local'); if (config.bundles) { for (const name of Object.keys(config.bundles)) { @@ -68,6 +68,9 @@ export function initializeKernel(config, hostStorage, verbose = false) { creationOptions.vatParameters = vatParameters; creationOptions.description = `static name=${name}`; creationOptions.name = name; + if (!creationOptions.managerType) { + creationOptions.managerType = kernelKeeper.getDefaultManagerType(); + } const vatID = kernelKeeper.allocateVatIDForNameIfNeeded(name); logStartup(`assigned VatID ${vatID} for genesis vat ${name}`); diff --git a/packages/SwingSet/src/kernel/loadVat.js b/packages/SwingSet/src/kernel/loadVat.js index fe1f4646244..542e345efd6 100644 --- a/packages/SwingSet/src/kernel/loadVat.js +++ b/packages/SwingSet/src/kernel/loadVat.js @@ -33,6 +33,12 @@ export function makeVatLoader(stuff) { const vatID = allocateUnusedVatID(); kernelKeeper.addDynamicVatID(vatID); const vatKeeper = kernelKeeper.allocateVatKeeper(vatID); + if (!dynamicOptions.managerType) { + dynamicOptions = { + ...dynamicOptions, + managerType: kernelKeeper.getDefaultManagerType(), + }; + } vatKeeper.setSourceAndOptions(source, dynamicOptions); // eslint-disable-next-line no-use-before-define create(vatID, source, dynamicOptions, true, true); diff --git a/packages/SwingSet/src/kernel/state/kernelKeeper.js b/packages/SwingSet/src/kernel/state/kernelKeeper.js index 510a4cf0e21..6dd0860187a 100644 --- a/packages/SwingSet/src/kernel/state/kernelKeeper.js +++ b/packages/SwingSet/src/kernel/state/kernelKeeper.js @@ -198,7 +198,10 @@ export default function makeKernelKeeper(storage, kernelSlog) { storage.set('crankNumber', `${crankNumber + 1n}`); } - function createStartingKernelState() { + /** + * @param { ManagerType } defaultManagerType + */ + function createStartingKernelState(defaultManagerType) { storage.set('vat.names', '[]'); storage.set('vat.dynamicIDs', '[]'); storage.set('vat.nextID', `${FIRST_VAT_ID}`); @@ -209,6 +212,11 @@ export default function makeKernelKeeper(storage, kernelSlog) { storage.set('kp.nextID', `${FIRST_PROMISE_ID}`); storage.set('runQueue', JSON.stringify([])); storage.set('crankNumber', `${FIRST_CRANK_NUMBER}`); + storage.set('kernel.defaultManagerType', defaultManagerType); + } + + function getDefaultManagerType() { + return getRequired('kernel.defaultManagerType'); } function addBundle(name, bundle) { @@ -819,6 +827,7 @@ export default function makeKernelKeeper(storage, kernelSlog) { getInitialized, setInitialized, createStartingKernelState, + getDefaultManagerType, addBundle, getBundle, diff --git a/packages/SwingSet/src/kernel/state/vatKeeper.js b/packages/SwingSet/src/kernel/state/vatKeeper.js index 96a9730b2f4..22f4538f433 100644 --- a/packages/SwingSet/src/kernel/state/vatKeeper.js +++ b/packages/SwingSet/src/kernel/state/vatKeeper.js @@ -63,6 +63,8 @@ export function makeVatKeeper( insistVatID(vatID); function setSourceAndOptions(source, options) { + // take care with API change + assert(options.managerType, X`vat options missing managerType`); assert.typeof(source, 'object'); assert(source.bundle || source.bundleName); assert.typeof(options, 'object'); diff --git a/packages/SwingSet/src/types.js b/packages/SwingSet/src/types.js index 3492bbefd9d..ae5ac3e794d 100644 --- a/packages/SwingSet/src/types.js +++ b/packages/SwingSet/src/types.js @@ -17,8 +17,9 @@ * TODO: metered... * * See validateManagerOptions() in factory.js + * @typedef { 'local' | 'nodeWorker' | 'node-subprocess' | 'xs-worker' } ManagerType * @typedef {{ - * managerType: 'local' | 'nodeWorker' | 'node-subprocess' | 'xs-worker', + * managerType: ManagerType, * metered?: boolean, * enableInternalMetering?: boolean, * enableDisavow?: boolean, diff --git a/packages/SwingSet/test/test-controller.js b/packages/SwingSet/test/test-controller.js index 9d6633754f0..22b088eee0b 100644 --- a/packages/SwingSet/test/test-controller.js +++ b/packages/SwingSet/test/test-controller.js @@ -1,8 +1,9 @@ /* global require __dirname */ +// eslint-disable-next-line import/order import { test } from '../tools/prepare-test-env-ava'; -// eslint-disable-next-line import/order import path from 'path'; +import { initSwingStore } from '@agoric/swing-store-simple'; import { buildVatController, loadBasedir } from '../src/index'; import { checkKT } from './util'; @@ -94,6 +95,36 @@ test('bootstrap', async t => { t.deepEqual(c.dump().log, ['bootstrap called']); }); +test('XS bootstrap', async t => { + const config = await loadBasedir( + path.resolve(__dirname, 'basedir-controller-2'), + ); + config.defaultManagerType = 'xs-worker'; + const hostStorage = initSwingStore().storage; + const c = await buildVatController(config, [], { hostStorage }); + t.deepEqual(c.dump().log, ['bootstrap called']); + t.is( + hostStorage.get('kernel.defaultManagerType'), + 'xs-worker', + 'defaultManagerType is saved by kernelKeeper', + ); + const vatID = c.vatNameToID('bootstrap'); + const options = JSON.parse(hostStorage.get(`${vatID}.options`)); + t.is( + options.managerType, + 'xs-worker', + 'managerType gets recorded for the bootstrap vat', + ); +}); + +test('validate config.defaultManagerType', async t => { + const config = await loadBasedir( + path.resolve(__dirname, 'basedir-controller-2'), + ); + config.defaultManagerType = 'XYZ'; + await t.throwsAsync(buildVatController(config), { message: /XYZ/ }); +}); + test('bootstrap export', async t => { const config = await loadBasedir( path.resolve(__dirname, 'basedir-controller-3'), diff --git a/packages/SwingSet/test/test-kernel.js b/packages/SwingSet/test/test-kernel.js index e5afefefd68..39beeb9aa09 100644 --- a/packages/SwingSet/test/test-kernel.js +++ b/packages/SwingSet/test/test-kernel.js @@ -1229,3 +1229,13 @@ test('pipelined promise queueing', async t => { }, ]); }); + +test('xs-worker default manager type', async t => { + const endowments = makeEndowments(); + initializeKernel({ defaultManagerType: 'xs-worker' }, endowments.hostStorage); + buildKernel(endowments, {}, {}); + t.deepEqual( + endowments.hostStorage.get('kernel.defaultManagerType'), + 'xs-worker', + ); +}); diff --git a/packages/SwingSet/test/test-state.js b/packages/SwingSet/test/test-state.js index 304c17fb7eb..defd3c98d5c 100644 --- a/packages/SwingSet/test/test-state.js +++ b/packages/SwingSet/test/test-state.js @@ -271,7 +271,7 @@ test('kernel state', async t => { const { kstorage, getState, commitCrank } = buildKeeperStorageInMemory(); const k = makeKernelKeeper(kstorage); t.truthy(!k.getInitialized()); - k.createStartingKernelState(); + k.createStartingKernelState('local'); k.setInitialized(); commitCrank(); @@ -287,13 +287,14 @@ test('kernel state', async t => { ['ko.nextID', '20'], ['kd.nextID', '30'], ['kp.nextID', '40'], + ['kernel.defaultManagerType', 'local'], ]); }); test('kernelKeeper vat names', async t => { const { kstorage, getState, commitCrank } = buildKeeperStorageInMemory(); const k = makeKernelKeeper(kstorage); - k.createStartingKernelState(); + k.createStartingKernelState('local'); const v1 = k.allocateVatIDForNameIfNeeded('vatname5'); const v2 = k.allocateVatIDForNameIfNeeded('Frank'); @@ -314,6 +315,7 @@ test('kernelKeeper vat names', async t => { ['kp.nextID', '40'], ['vat.name.vatname5', 'v1'], ['vat.name.Frank', 'v2'], + ['kernel.defaultManagerType', 'local'], ]); t.deepEqual(k.getStaticVats(), [ ['Frank', 'v2'], @@ -334,7 +336,7 @@ test('kernelKeeper vat names', async t => { test('kernelKeeper device names', async t => { const { kstorage, getState, commitCrank } = buildKeeperStorageInMemory(); const k = makeKernelKeeper(kstorage); - k.createStartingKernelState(); + k.createStartingKernelState('local'); const d7 = k.allocateDeviceIDForNameIfNeeded('devicename5'); const d8 = k.allocateDeviceIDForNameIfNeeded('Frank'); @@ -355,6 +357,7 @@ test('kernelKeeper device names', async t => { ['kp.nextID', '40'], ['device.name.devicename5', 'd7'], ['device.name.Frank', 'd8'], + ['kernel.defaultManagerType', 'local'], ]); t.deepEqual(k.getDevices(), [ ['Frank', 'd8'], @@ -375,7 +378,7 @@ test('kernelKeeper device names', async t => { test('kernelKeeper runQueue', async t => { const { kstorage, getState, commitCrank } = buildKeeperStorageInMemory(); const k = makeKernelKeeper(kstorage); - k.createStartingKernelState(); + k.createStartingKernelState('local'); t.truthy(k.isRunQueueEmpty()); t.is(k.getRunQueueLength(), 0); @@ -411,7 +414,7 @@ test('kernelKeeper runQueue', async t => { test('kernelKeeper promises', async t => { const { kstorage, getState, commitCrank } = buildKeeperStorageInMemory(); const k = makeKernelKeeper(kstorage); - k.createStartingKernelState(); + k.createStartingKernelState('local'); const p1 = k.addKernelPromiseForVat('v4'); t.deepEqual(k.getKernelPromise(p1), { @@ -519,13 +522,14 @@ test('kernelKeeper promises', async t => { ['kp40.data.slots', 'ko44'], ['kp40.state', 'fulfilled'], ['kp40.refCount', '0'], + ['kernel.defaultManagerType', 'local'], ]); }); test('kernelKeeper promise resolveToData', async t => { const { kstorage } = buildKeeperStorageInMemory(); const k = makeKernelKeeper(kstorage); - k.createStartingKernelState(); + k.createStartingKernelState('local'); const p1 = k.addKernelPromiseForVat('v4'); const capdata = harden({ @@ -546,7 +550,7 @@ test('kernelKeeper promise resolveToData', async t => { test('kernelKeeper promise reject', async t => { const { kstorage } = buildKeeperStorageInMemory(); const k = makeKernelKeeper(kstorage); - k.createStartingKernelState(); + k.createStartingKernelState('local'); const p1 = k.addKernelPromiseForVat('v4'); const capdata = harden({ @@ -567,7 +571,7 @@ test('kernelKeeper promise reject', async t => { test('vatKeeper', async t => { const { kstorage, getState, commitCrank } = buildKeeperStorageInMemory(); const k = makeKernelKeeper(kstorage); - k.createStartingKernelState(); + k.createStartingKernelState('local'); const v1 = k.allocateVatIDForNameIfNeeded('name1'); const vk = k.allocateVatKeeper(v1); @@ -595,3 +599,10 @@ test('vatKeeper', async t => { t.is(vk2.mapKernelSlotToVatSlot(kernelImport2), vatImport2); t.is(vk2.mapVatSlotToKernelSlot(vatImport2), kernelImport2); }); + +test('XS vatKeeper defaultManagerType', async t => { + const { kstorage } = buildKeeperStorageInMemory(); + const k = makeKernelKeeper(kstorage); + k.createStartingKernelState('xs-worker'); + t.is(k.getDefaultManagerType(), 'xs-worker'); +});