diff --git a/packages/SwingSet/src/controller/initializeKernel.js b/packages/SwingSet/src/controller/initializeKernel.js index 757902300b0..535477a7c94 100644 --- a/packages/SwingSet/src/controller/initializeKernel.js +++ b/packages/SwingSet/src/controller/initializeKernel.js @@ -27,6 +27,7 @@ export function initializeKernel(config, hostStorage, verbose = false) { kernelKeeper.createStartingKernelState( config.defaultManagerType || 'local', config.defaultReapInterval || 1, + !!config.enableFakeDurable, ); for (const id of Object.keys(config.idToBundle || {})) { diff --git a/packages/SwingSet/src/kernel/state/kernelKeeper.js b/packages/SwingSet/src/kernel/state/kernelKeeper.js index bfbd0a7487e..fca76163e4b 100644 --- a/packages/SwingSet/src/kernel/state/kernelKeeper.js +++ b/packages/SwingSet/src/kernel/state/kernelKeeper.js @@ -58,6 +58,7 @@ const enableKernelGC = true; // // kernel.defaultManagerType = managerType // kernel.defaultReapInterval = $NN +// kernel.enableFakeDurable = missing | 'true' // v$NN.source = JSON({ bundle }) or JSON({ bundleName }) // v$NN.options = JSON @@ -259,8 +260,13 @@ export default function makeKernelKeeper( /** * @param { ManagerType } defaultManagerType * @param { number } defaultReapInterval + * @param { boolean } enableFakeDurable */ - function createStartingKernelState(defaultManagerType, defaultReapInterval) { + function createStartingKernelState( + defaultManagerType, + defaultReapInterval, + enableFakeDurable, + ) { kvStore.set('vat.names', '[]'); kvStore.set('vat.dynamicIDs', '[]'); kvStore.set('vat.nextID', `${FIRST_VAT_ID}`); @@ -278,6 +284,9 @@ export default function makeKernelKeeper( kvStore.set('crankNumber', `${FIRST_CRANK_NUMBER}`); kvStore.set('kernel.defaultManagerType', defaultManagerType); kvStore.set('kernel.defaultReapInterval', `${defaultReapInterval}`); + if (enableFakeDurable) { + kvStore.set('kernel.enableFakeDurable', 'true'); + } // Will be saved in the bootstrap commit initializeStats(); } @@ -317,6 +326,13 @@ export default function makeKernelKeeper( return ri; } + /** + * @returns { boolean } + */ + function getEnableFakeDurable() { + return !!kvStore.get('kernel.enableFakeDurable'); + } + const bundleIDRE = new RegExp('^b1-[0-9a-f]{128}$'); /** @@ -1474,6 +1490,7 @@ export default function makeKernelKeeper( createStartingKernelState, getDefaultManagerType, getDefaultReapInterval, + getEnableFakeDurable, addNamedBundleID, getNamedBundleID, diff --git a/packages/SwingSet/src/kernel/vat-loader/manager-helper.js b/packages/SwingSet/src/kernel/vat-loader/manager-helper.js index 5a437fc379e..23e3664c6d5 100644 --- a/packages/SwingSet/src/kernel/vat-loader/manager-helper.js +++ b/packages/SwingSet/src/kernel/vat-loader/manager-helper.js @@ -51,6 +51,7 @@ import { makeTranscriptManager } from './transcript.js'; * makeSnapshot?: (ss: SnapStore) => Promise) => VatManager, * syscallFromWorker: (vso: VatSyscallObject) => VatSyscallResult, * setDeliverToWorker: (dtw: unknown) => void, + * getEnableFakeDurable: () => boolean, * } } ManagerKit * */ @@ -264,6 +265,13 @@ function makeManagerKit( return vres; } + /** + * @returns { boolean } + */ + function getEnableFakeDurable() { + return kernelKeeper.getEnableFakeDurable(); + } + /** * * @param { () => Promise} shutdown @@ -280,7 +288,12 @@ function makeManagerKit( }); } - return harden({ getManager, syscallFromWorker, setDeliverToWorker }); + return harden({ + getManager, + syscallFromWorker, + setDeliverToWorker, + getEnableFakeDurable, + }); } harden(makeManagerKit); export { makeManagerKit }; diff --git a/packages/SwingSet/src/kernel/vat-loader/manager-local.js b/packages/SwingSet/src/kernel/vat-loader/manager-local.js index d6999a91794..7cf638be7e8 100644 --- a/packages/SwingSet/src/kernel/vat-loader/manager-local.js +++ b/packages/SwingSet/src/kernel/vat-loader/manager-local.js @@ -143,6 +143,7 @@ export function makeLocalVatManagerFactory(tools) { gcTools, makeVatConsole(makeLogMaker('ls')), buildVatNamespace, + kernelKeeper.getEnableFakeDurable(), ); assert(ls.dispatch); return finish(ls.dispatch); diff --git a/packages/SwingSet/src/kernel/vat-loader/manager-nodeworker.js b/packages/SwingSet/src/kernel/vat-loader/manager-nodeworker.js index 232f8e70f10..e2cfc26d682 100644 --- a/packages/SwingSet/src/kernel/vat-loader/manager-nodeworker.js +++ b/packages/SwingSet/src/kernel/vat-loader/manager-nodeworker.js @@ -105,7 +105,13 @@ export function makeNodeWorkerVatManagerFactory(tools) { gotWorker(worker); parentLog(`instructing worker to load bundle..`); - sendToWorker(['setBundle', bundle, virtualObjectCacheSize, enableDisavow]); + sendToWorker([ + 'setBundle', + bundle, + virtualObjectCacheSize, + enableDisavow, + mk.getEnableFakeDurable(), + ]); function deliverToWorker(delivery) { parentLog(`sending delivery`, delivery); diff --git a/packages/SwingSet/src/kernel/vat-loader/manager-subprocess-node.js b/packages/SwingSet/src/kernel/vat-loader/manager-subprocess-node.js index f083021acd9..b5e8f997c2d 100644 --- a/packages/SwingSet/src/kernel/vat-loader/manager-subprocess-node.js +++ b/packages/SwingSet/src/kernel/vat-loader/manager-subprocess-node.js @@ -100,7 +100,13 @@ export function makeNodeSubprocessFactory(tools) { fromChild.on('data', handleUpstream); parentLog(`instructing worker to load bundle..`); - sendToWorker(['setBundle', bundle, virtualObjectCacheSize, enableDisavow]); + sendToWorker([ + 'setBundle', + bundle, + virtualObjectCacheSize, + enableDisavow, + mk.getEnableFakeDurable(), + ]); function shutdown() { terminate(); diff --git a/packages/SwingSet/src/kernel/vat-loader/manager-subprocess-xsnap.js b/packages/SwingSet/src/kernel/vat-loader/manager-subprocess-xsnap.js index 6d49ee2a2a6..4e4cddbb829 100644 --- a/packages/SwingSet/src/kernel/vat-loader/manager-subprocess-xsnap.js +++ b/packages/SwingSet/src/kernel/vat-loader/manager-subprocess-xsnap.js @@ -145,6 +145,7 @@ export function makeXsSubprocessFactory({ bundle, virtualObjectCacheSize, enableDisavow, + mk.getEnableFakeDurable(), gcEveryCrank, ]); if (bundleReply[0] === 'dispatchReady') { diff --git a/packages/SwingSet/src/liveslots/collectionManager.js b/packages/SwingSet/src/liveslots/collectionManager.js index a1903173eb0..7286358f43d 100644 --- a/packages/SwingSet/src/liveslots/collectionManager.js +++ b/packages/SwingSet/src/liveslots/collectionManager.js @@ -99,6 +99,7 @@ export function makeCollectionManager( registerValue, serialize, unserialize, + enableFakeDurable, ) { // TODO(#5058): we hold a list of all collections (both virtual and // durable) in RAM, so we can delete the virtual ones during @@ -781,6 +782,17 @@ export function makeCollectionManager( return Far('weakSetStore', weakSetStore); } + function assertCorrectDurabilityFlags(durable, fakeDurable) { + assert( + enableFakeDurable || !fakeDurable, + 'fakeDurable may only be used if enableFakeDurable is true', + ); + assert( + !durable || !fakeDurable, + 'durable and fakeDurable are mutually exclusive', + ); + } + /** * Produce a *scalar* big map: keys can only be atomic values, primitives, or * remotables. @@ -799,10 +811,7 @@ export function makeCollectionManager( fakeDurable = false, } = {}, ) { - assert( - !durable || !fakeDurable, - 'durable and fakeDurable are mutually exclusive', - ); + assertCorrectDurabilityFlags(durable, fakeDurable); const kindName = durable ? 'scalarDurableMapStore' : 'scalarMapStore'; const [vobjID, collection] = makeCollection( label, @@ -853,10 +862,7 @@ export function makeCollectionManager( fakeDurable = false, } = {}, ) { - assert( - !durable || !fakeDurable, - 'durable and fakeDurable are mutually exclusive', - ); + assertCorrectDurabilityFlags(durable, fakeDurable); const kindName = durable ? 'scalarDurableWeakMapStore' : 'scalarWeakMapStore'; @@ -892,10 +898,7 @@ export function makeCollectionManager( fakeDurable = false, } = {}, ) { - assert( - !durable || !fakeDurable, - 'durable and fakeDurable are mutually exclusive', - ); + assertCorrectDurabilityFlags(durable, fakeDurable); const kindName = durable ? 'scalarDurableSetStore' : 'scalarSetStore'; const [vobjID, collection] = makeCollection( label, @@ -929,10 +932,7 @@ export function makeCollectionManager( fakeDurable = false, } = {}, ) { - assert( - !durable || !fakeDurable, - 'durable and fakeDurable are mutually exclusive', - ); + assertCorrectDurabilityFlags(durable, fakeDurable); const kindName = durable ? 'scalarDurableWeakSetStore' : 'scalarWeakSetStore'; diff --git a/packages/SwingSet/src/liveslots/liveslots.js b/packages/SwingSet/src/liveslots/liveslots.js index c894f616ed9..e7128ad05fa 100644 --- a/packages/SwingSet/src/liveslots/liveslots.js +++ b/packages/SwingSet/src/liveslots/liveslots.js @@ -40,6 +40,7 @@ const DEFAULT_VIRTUAL_OBJECT_CACHE_SIZE = 3; // XXX ridiculously small value to * meterControl } * @param {Console} console * @param {*} buildVatNamespace + * @param {boolean} enableFakeDurable * * @returns {*} { dispatch } */ @@ -52,6 +53,7 @@ function build( gcTools, console, buildVatNamespace, + enableFakeDurable, ) { const { WeakRef, FinalizationRegistry, meterControl } = gcTools; const enableLSDebug = false; @@ -591,6 +593,7 @@ function build( m.serialize, unmeteredUnserialize, cacheSize, + enableFakeDurable, ); const collectionManager = makeCollectionManager( @@ -605,6 +608,7 @@ function build( registerValue, m.serialize, unmeteredUnserialize, + enableFakeDurable, ); const watchedPromiseManager = makeWatchedPromiseManager( @@ -1452,6 +1456,7 @@ function build( * @param {*} gcTools { WeakRef, FinalizationRegistry, waitUntilQuiescent } * @param {Pick} [liveSlotsConsole] * @param {*} buildVatNamespace + * @param {boolean} enableFakeDurable * * @returns {*} { vatGlobals, inescapableGlobalProperties, dispatch } * @@ -1486,6 +1491,7 @@ export function makeLiveSlots( gcTools, liveSlotsConsole = console, buildVatNamespace, + enableFakeDurable, ) { const allVatPowers = { ...vatPowers, @@ -1500,6 +1506,7 @@ export function makeLiveSlots( gcTools, liveSlotsConsole, buildVatNamespace, + enableFakeDurable, ); const { dispatch, startVat, possiblyDeadSet, testHooks } = r; // omit 'm' return harden({ diff --git a/packages/SwingSet/src/liveslots/virtualObjectManager.js b/packages/SwingSet/src/liveslots/virtualObjectManager.js index 65616c9d997..ffcc6f5dbe4 100644 --- a/packages/SwingSet/src/liveslots/virtualObjectManager.js +++ b/packages/SwingSet/src/liveslots/virtualObjectManager.js @@ -149,6 +149,7 @@ export function makeCache(size, fetch, store) { * @param {*} unserialize Unserializer for this vat * @param {number} cacheSize How many virtual objects this manager should cache * in memory. + * @param {boolean} enableFakeDurable True iff the associated kernel is running in dev mode. * * @returns a new virtual object manager. * @@ -189,6 +190,7 @@ export function makeVirtualObjectManager( serialize, unserialize, cacheSize, + enableFakeDurable, ) { const cache = makeCache(cacheSize, fetch, store); @@ -578,6 +580,10 @@ export function makeVirtualObjectManager( ({ finish, fakeDurable } = options); } if (fakeDurable) { + assert( + enableFakeDurable, + `fakeDurable may only be used if enableFakeDurable is true`, + ); assert( durable, `the fakeDurable option may only be applied to durable objects`, diff --git a/packages/SwingSet/src/supervisors/nodeworker/supervisor-nodeworker.js b/packages/SwingSet/src/supervisors/nodeworker/supervisor-nodeworker.js index fcee7dcd82e..da8564462cb 100644 --- a/packages/SwingSet/src/supervisors/nodeworker/supervisor-nodeworker.js +++ b/packages/SwingSet/src/supervisors/nodeworker/supervisor-nodeworker.js @@ -45,7 +45,8 @@ parentPort.on('message', ([type, ...margs]) => { workerLog(`got start`); sendUplink(['gotStart']); } else if (type === 'setBundle') { - const [bundle, virtualObjectCacheSize, enableDisavow] = margs; + const [bundle, virtualObjectCacheSize, enableDisavow, enableFakeDurable] = + margs; function testLog(...args) { sendUplink(['testLog', ...args]); @@ -111,6 +112,7 @@ parentPort.on('message', ([type, ...margs]) => { gcTools, makeVatConsole(makeLogMaker(`SwingSet:ls:${vatID}`)), buildVatNamespace, + enableFakeDurable, ); sendUplink(['gotBundle']); diff --git a/packages/SwingSet/src/supervisors/subprocess-node/supervisor-subprocess-node.js b/packages/SwingSet/src/supervisors/subprocess-node/supervisor-subprocess-node.js index 05118dd45fa..52eaf3cf460 100644 --- a/packages/SwingSet/src/supervisors/subprocess-node/supervisor-subprocess-node.js +++ b/packages/SwingSet/src/supervisors/subprocess-node/supervisor-subprocess-node.js @@ -65,7 +65,8 @@ fromParent.on('data', ([type, ...margs]) => { workerLog(`got start`); sendUplink(['gotStart']); } else if (type === 'setBundle') { - const [bundle, virtualObjectCacheSize, enableDisavow] = margs; + const [bundle, virtualObjectCacheSize, enableDisavow, enableFakeDurable] = + margs; function testLog(...args) { sendUplink(['testLog', ...args]); @@ -131,6 +132,7 @@ fromParent.on('data', ([type, ...margs]) => { gcTools, makeVatConsole(makeLogMaker(`SwingSet:ls:${vatID}`)), buildVatNamespace, + enableFakeDurable, ); sendUplink(['gotBundle']); diff --git a/packages/SwingSet/src/supervisors/subprocess-xsnap/supervisor-subprocess-xsnap.js b/packages/SwingSet/src/supervisors/subprocess-xsnap/supervisor-subprocess-xsnap.js index 2b28a4da4b2..1405330fb00 100644 --- a/packages/SwingSet/src/supervisors/subprocess-xsnap/supervisor-subprocess-xsnap.js +++ b/packages/SwingSet/src/supervisors/subprocess-xsnap/supervisor-subprocess-xsnap.js @@ -185,6 +185,7 @@ function makeWorker(port) { * @param {unknown} bundle * @param {unknown} virtualObjectCacheSize * @param {boolean} enableDisavow + * @param {boolean} enableFakeDurable * @param {boolean} [gcEveryCrank] * @returns { Promise } */ @@ -193,6 +194,7 @@ function makeWorker(port) { bundle, virtualObjectCacheSize, enableDisavow, + enableFakeDurable, gcEveryCrank, ) { /** @type { (vso: VatSyscallObject) => VatSyscallResult } */ @@ -293,6 +295,7 @@ function makeWorker(port) { gcTools, makeVatConsole(makeLogMaker('ls')), buildVatNamespace, + enableFakeDurable, ); assert(ls.dispatch); @@ -308,12 +311,14 @@ function makeWorker(port) { case 'setBundle': { assert(!dispatch, 'cannot setBundle again'); const enableDisavow = !!args[3]; - const gcEveryCrank = args[4] === undefined ? true : !!args[4]; + const enableFakeDurable = !!args[4]; + const gcEveryCrank = args[5] === undefined ? true : !!args[5]; return setBundle( args[0], args[1], args[2], enableDisavow, + enableFakeDurable, gcEveryCrank, ); } diff --git a/packages/SwingSet/test/enable-fake-durable/bootstrap-enable-fake-durable.js b/packages/SwingSet/test/enable-fake-durable/bootstrap-enable-fake-durable.js new file mode 100644 index 00000000000..79a40bb70e9 --- /dev/null +++ b/packages/SwingSet/test/enable-fake-durable/bootstrap-enable-fake-durable.js @@ -0,0 +1,21 @@ +import { Far } from '@endo/marshal'; +import { + makeKindHandle, + defineDurableKind, + makeScalarBigMapStore, +} from '@agoric/vat-data'; + +export function buildRootObject() { + return Far('root', { + bootstrap() {}, + + testStore() { + makeScalarBigMapStore('dfstore', { fakeDurable: true }); + }, + + testObj() { + const kh = makeKindHandle('kh'); + defineDurableKind(kh, () => ({}), {}, { fakeDurable: true }); + }, + }); +} diff --git a/packages/SwingSet/test/enable-fake-durable/test-enable-fake-durable.js b/packages/SwingSet/test/enable-fake-durable/test-enable-fake-durable.js new file mode 100644 index 00000000000..1318a1d6122 --- /dev/null +++ b/packages/SwingSet/test/enable-fake-durable/test-enable-fake-durable.js @@ -0,0 +1,70 @@ +// eslint-disable-next-line import/order +import { test } from '../../tools/prepare-test-env-ava.js'; + +// eslint-disable-next-line import/order +import { assert } from '@agoric/assert'; +// eslint-disable-next-line import/order +import { provideHostStorage } from '../../src/controller/hostStorage.js'; +import { initializeSwingset, makeSwingsetController } from '../../src/index.js'; + +function bfile(name) { + return new URL(name, import.meta.url).pathname; +} + +async function testEnableFakeDurable(t, enableFakeDurable) { + const config = { + bootstrap: 'bootstrap', + includeDevDependencies: true, // for vat-data + enableFakeDurable, + vats: { + bootstrap: { sourceSpec: bfile('bootstrap-enable-fake-durable.js') }, + }, + }; + + const hostStorage = provideHostStorage(); + await initializeSwingset(config, [], hostStorage); + const c = await makeSwingsetController(hostStorage); + c.pinVatRoot('bootstrap'); + await c.run(); + + const run = async (method, args = []) => { + assert(Array.isArray(args)); + const kpid = c.queueToVatRoot('bootstrap', method, args); + await c.run(); + const status = c.kpStatus(kpid); + const capdata = c.kpResolution(kpid); + return [status, capdata]; + }; + + const [storeStatus, storeCapdata] = await run('testStore'); + if (enableFakeDurable) { + t.is(storeStatus, 'fulfilled'); + t.deepEqual(storeCapdata, { body: '{"@qclass":"undefined"}', slots: [] }); + } else { + t.is(storeStatus, 'rejected'); + t.deepEqual(storeCapdata, { + body: '{"@qclass":"error","errorId":"error:liveSlots:v1#70001","message":"fakeDurable may only be used if enableFakeDurable is true","name":"Error"}', + slots: [], + }); + } + + const [objStatus, objCapdata] = await run('testObj'); + if (enableFakeDurable) { + t.is(objStatus, 'fulfilled'); + t.deepEqual(objCapdata, { body: '{"@qclass":"undefined"}', slots: [] }); + } else { + t.is(objStatus, 'rejected'); + t.deepEqual(objCapdata, { + body: '{"@qclass":"error","errorId":"error:liveSlots:v1#70002","message":"fakeDurable may only be used if enableFakeDurable is true","name":"Error"}', + slots: [], + }); + } +} + +test('enableFakeDurable=true', async t => { + return testEnableFakeDurable(t, true); +}); + +test('enableFakeDurable=false', async t => { + return testEnableFakeDurable(t, false); +}); diff --git a/packages/SwingSet/test/liveslots-helpers.js b/packages/SwingSet/test/liveslots-helpers.js index 3457e85c840..b30908732de 100644 --- a/packages/SwingSet/test/liveslots-helpers.js +++ b/packages/SwingSet/test/liveslots-helpers.js @@ -146,6 +146,7 @@ export async function makeDispatch( () => { return { buildRootObject: build }; }, + false, ); await startVat(capargs()); if (returnTestHooks) { diff --git a/packages/SwingSet/test/test-liveslots.js b/packages/SwingSet/test/test-liveslots.js index 4e449d1fd45..815f3a7458b 100644 --- a/packages/SwingSet/test/test-liveslots.js +++ b/packages/SwingSet/test/test-liveslots.js @@ -1005,6 +1005,7 @@ test('dropImports', async t => { () => { return { buildRootObject: build }; }, + false, ); const { dispatch, startVat, possiblyDeadSet } = ls; await startVat(capargs()); @@ -1142,6 +1143,7 @@ test('buildVatNamespace not called until after startVat', async t => { gcTools, undefined, () => ({ buildRootObject }), + false, ); t.falsy(buildCalled); await ls.startVat(capargs()); diff --git a/packages/SwingSet/test/test-xsnap-errors.js b/packages/SwingSet/test/test-xsnap-errors.js index b49f20c09b3..bb4adcab53a 100644 --- a/packages/SwingSet/test/test-xsnap-errors.js +++ b/packages/SwingSet/test/test-xsnap-errors.js @@ -41,6 +41,7 @@ test('child termination distinguished from meter exhaustion', async t => { getLastSnapshot: () => undefined, addToTranscript: () => undefined, }), + getEnableFakeDurable: () => false, }; const xsWorkerFactory = makeXsSubprocessFactory({ diff --git a/packages/SwingSet/tools/fakeCollectionManager.js b/packages/SwingSet/tools/fakeCollectionManager.js index bf3f3b96d26..3cd01d71675 100644 --- a/packages/SwingSet/tools/fakeCollectionManager.js +++ b/packages/SwingSet/tools/fakeCollectionManager.js @@ -17,6 +17,7 @@ export function makeFakeCollectionManager(vrm, fakeStuff, _options = {}) { fakeStuff.registerEntry, fakeStuff.marshal.serialize, fakeStuff.marshal.unserialize, + true, ); initializeStoreKindInfo(); diff --git a/packages/SwingSet/tools/fakeVirtualObjectManager.js b/packages/SwingSet/tools/fakeVirtualObjectManager.js index a6c685ce0d0..ea7a84c3dbe 100644 --- a/packages/SwingSet/tools/fakeVirtualObjectManager.js +++ b/packages/SwingSet/tools/fakeVirtualObjectManager.js @@ -33,6 +33,7 @@ export function makeFakeVirtualObjectManager(vrm, fakeStuff, options = {}) { fakeStuff.marshal.serialize, fakeStuff.marshal.unserialize, cacheSize, + true, ); const normalVOM = {