@agoric/swingset-vat 0.33.0-u19.2 → 0.33.0-u20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -9,7 +9,7 @@ import { tmpName } from 'tmp';
9
9
  import anylogger from 'anylogger';
10
10
  import microtime from 'microtime';
11
11
 
12
- import { assert, Fail } from '@endo/errors';
12
+ import { assert, q, Fail } from '@endo/errors';
13
13
  import { importBundle } from '@endo/import-bundle';
14
14
  import { initSwingStore } from '@agoric/swing-store';
15
15
 
@@ -38,12 +38,17 @@ import { makeStartSubprocessWorkerNode } from './startNodeSubprocess.js';
38
38
 
39
39
  /**
40
40
  * @import {EReturn} from '@endo/far';
41
+ * @import {LimitedConsole} from '@agoric/internal';
42
+ * @import {VatID} from '../types-internal.js';
41
43
  */
42
44
 
43
45
  /**
44
- * @typedef { import('../types-internal.js').VatID } VatID
46
+ * @typedef {Record<string, unknown> & {type: string, time?: never, monotime?: never}} SlogProps
47
+ * @typedef {Omit<SlogProps, 'type'> & {type?: never, seconds?: never}} SlogDurationProps
45
48
  */
46
49
 
50
+ const { hasOwn } = Object;
51
+
47
52
  const endoZipBase64Sha512Shape = harden({
48
53
  moduleFormat: 'endoZipBase64',
49
54
  endoZipBase64: M.string(harden({ stringLengthLimit: Infinity })),
@@ -85,14 +90,29 @@ function makeConsole(prefixer) {
85
90
  });
86
91
  }
87
92
 
93
+ /**
94
+ * A console-like object for logging. It starts as the global console but is
95
+ * immediately replaced with an anylogger instance dedicated to this file, and
96
+ * upon creation of a controller is replaced again with an anylogger dedicated
97
+ * to that controller (and which also emits slog entries).
98
+ *
99
+ * @type {LimitedConsole}
100
+ */
101
+ let sloggingConsole = console;
102
+ /** @type {(newConsole: LimitedConsole) => void} */
103
+ const setSloggingConsole = newConsole => {
104
+ sloggingConsole = newConsole;
105
+ };
106
+ setSloggingConsole(makeConsole('SwingSet:controller'));
107
+
88
108
  /**
89
109
  * @param {unknown} e
90
110
  * @param {Promise} pr
91
111
  */
92
- function unhandledRejectionHandler(e, pr) {
112
+ function onUnhandledRejection(e, pr) {
93
113
  // Don't trigger sensitive hosts (like AVA).
94
114
  pr.catch(() => {});
95
- console.error('🤞 UnhandledPromiseRejection:', e);
115
+ sloggingConsole.error('🤞 UnhandledPromiseRejection:', e);
96
116
  }
97
117
 
98
118
  /**
@@ -123,11 +143,9 @@ export async function makeSwingsetController(
123
143
  const kvStore = kernelStorage.kvStore;
124
144
  insistStorageAPI(kvStore);
125
145
 
126
- // Use ambient process.env only if caller did not specify.
127
- const { env = process.env } = runtimeOptions;
128
-
129
- // build console early so we can add console.log to diagnose early problems
130
146
  const {
147
+ // Use ambient process.env only if caller did not specify.
148
+ env = process.env,
131
149
  verbose,
132
150
  debugPrefix = '',
133
151
  slogCallbacks,
@@ -138,8 +156,7 @@ export async function makeSwingsetController(
138
156
  xsnapBundleData = makeXsnapBundleData(),
139
157
  profileVats = [],
140
158
  debugVats = [],
141
- } = runtimeOptions;
142
- const {
159
+
143
160
  bundleHandler = makeWorkerBundleHandler(
144
161
  kernelStorage.bundleStore,
145
162
  xsnapBundleData,
@@ -150,6 +167,103 @@ export async function makeSwingsetController(
150
167
  throw Error('SES must be installed before calling makeSwingsetController');
151
168
  }
152
169
 
170
+ /** @type {(obj: SlogProps) => void} */
171
+ function writeSlogObject(obj) {
172
+ if (!slogSender) return;
173
+
174
+ const { type, seconds, ...props } = obj;
175
+ const timings = {
176
+ // microtime gives POSIX gettimeofday() with microsecond resolution
177
+ time: microtime.nowDouble(),
178
+ // this is CLOCK_MONOTONIC, seconds since process start
179
+ monotime: performance.now() / 1000,
180
+ ...(seconds === undefined ? undefined : { seconds }),
181
+ };
182
+
183
+ // rearrange the fields a bit to make it more legible to humans
184
+ slogSender(harden({ type, ...props, ...timings }));
185
+ }
186
+
187
+ /**
188
+ * Capture an extended process in the slog, writing an entry with `type`
189
+ * $startLabel and then later (if the function returns successfully or calls
190
+ * the finish callback provided to it) another entry with `type` $endLabel and
191
+ * a `seconds` property valued with the total elapsed duration in seconds.
192
+ * Finish is implied by settlement of the function's awaited return value, so
193
+ * any explicit use of the finish callback MUST NOT follow that settlement.
194
+ *
195
+ * @template T
196
+ * @template {unknown[]} A
197
+ * @param {readonly [startLabel: string, endLabel: string]} labels
198
+ * @param {SlogDurationProps} startProps for both slog entries
199
+ * @param {(finish: (extraProps?: SlogDurationProps) => void, ...args: A) => (T | Promise<T>)} fn
200
+ * @param {unknown[] & A} args
201
+ * @returns {Promise<T>}
202
+ */
203
+ const slogDuration = async (labels, startProps, fn, ...args) => {
204
+ const [startLabel, endLabel] = labels;
205
+ const props = { ...startProps };
206
+ if (hasOwn(props, 'type') || hasOwn(props, 'seconds')) {
207
+ const msg = 'startProps must not include "type" or "seconds"';
208
+ sloggingConsole.error(Error(msg));
209
+ delete props.type;
210
+ delete props.seconds;
211
+ }
212
+ let finished = false;
213
+ /** @type {(extraProps?: SlogDurationProps) => void} */
214
+ const finish = extraProps => {
215
+ const seconds = (performance.now() - t0) / 1000;
216
+ if (finished) {
217
+ // `finish` should only be called once.
218
+ // Log a stack-bearing error instance, but throw something more opaque.
219
+ const msg = `slog event ${startLabel} ${q(startProps || {})} already finished; ignoring props ${q(extraProps || {})}`;
220
+ sloggingConsole.error(Error(msg));
221
+ Fail`slog event ${startLabel} already finished`;
222
+ }
223
+ finished = true;
224
+ if (extraProps) {
225
+ // Preserve extraProps as an atomic unit by deleting prior occurrences.
226
+ for (const name of Object.keys(extraProps)) delete props[name];
227
+ if (hasOwn(extraProps, 'type') || hasOwn(extraProps, 'seconds')) {
228
+ const msg = `extraProps ${q(extraProps)} must not include "type" or "seconds"`;
229
+ sloggingConsole.error(Error(msg));
230
+ const {
231
+ type: _ignoredType,
232
+ seconds: _ignoredSeconds,
233
+ ...validProps
234
+ } = extraProps;
235
+ extraProps = validProps;
236
+ }
237
+ }
238
+ writeSlogObject({ type: endLabel, ...props, ...extraProps, seconds });
239
+ };
240
+
241
+ writeSlogObject({ type: startLabel, ...props });
242
+ const t0 = performance.now();
243
+ try {
244
+ // We need to synchronously provide the finish function.
245
+ // eslint-disable-next-line @jessie.js/safe-await-separator
246
+ const result = await fn(finish, ...args);
247
+ if (!finished) finish();
248
+ return result;
249
+ } catch (cause) {
250
+ if (!finished) {
251
+ const msg = `unfinished slog event ${startLabel} ${q(startProps || {})}`;
252
+ sloggingConsole.error(Error(msg, { cause }));
253
+ }
254
+ throw cause;
255
+ }
256
+ };
257
+
258
+ const controllerConsole = makeConsole(`${debugPrefix}SwingSet:controller`);
259
+ const controllerSloggingConsole = makeLimitedConsole(level => {
260
+ return (...args) => {
261
+ controllerConsole[level](...args);
262
+ writeSlogObject({ type: 'console', source: 'controller', level, args });
263
+ };
264
+ });
265
+ setSloggingConsole(controllerSloggingConsole);
266
+
153
267
  const startXSnap = makeStartXSnap({
154
268
  bundleHandler,
155
269
  snapStore: kernelStorage.snapStore,
@@ -167,340 +281,316 @@ export async function makeSwingsetController(
167
281
  debugVats,
168
282
  );
169
283
 
170
- function writeSlogObject(obj) {
171
- if (!slogSender) {
172
- // Fast path; nothing to do.
173
- return;
174
- }
175
-
176
- // microtime gives POSIX gettimeofday() with microsecond resolution
177
- const time = microtime.nowDouble();
178
- // this is CLOCK_MONOTONIC, seconds since process start
179
- const monotime = performance.now() / 1000;
180
-
181
- // rearrange the fields a bit to make it more legible to humans
182
- const timedObj = { type: undefined, ...obj, time, monotime };
183
-
184
- // Allow the SwingSet host to do anything they want with slog messages.
185
- slogSender(timedObj);
186
- }
187
-
188
- const console = makeConsole(`${debugPrefix}SwingSet:controller`);
189
- // We can harden this 'console' because it's new, but if we were using the
190
- // original 'console' object (which has a unique prototype), we'd have to
191
- // harden(Object.getPrototypeOf(console));
192
- // see https://github.com/Agoric/SES-shim/issues/292 for details
193
- harden(console);
194
-
195
- writeSlogObject({ type: 'kernel-init-start' });
196
-
197
- writeSlogObject({ type: 'bundle-kernel-start' });
198
- await null;
199
- const { kernelBundle = await buildKernelBundle() } = runtimeOptions;
200
- writeSlogObject({ type: 'bundle-kernel-finish' });
201
-
202
- // FIXME: Put this somewhere better.
203
- const handlers = process.listeners('unhandledRejection');
204
- let haveUnhandledRejectionHandler = false;
205
- for (const handler of handlers) {
206
- if (handler === unhandledRejectionHandler) {
207
- haveUnhandledRejectionHandler = true;
208
- break;
209
- }
210
- }
211
- if (!haveUnhandledRejectionHandler) {
212
- process.on('unhandledRejection', unhandledRejectionHandler);
213
- }
214
-
215
- const kernelConsole = makeConsole(`${debugPrefix}SwingSet:kernel`);
216
- const sloggingKernelConsole = makeLimitedConsole(level => {
217
- return (...args) => {
218
- kernelConsole[level](...args);
219
- writeSlogObject({ type: 'console', source: 'kernel', level, args });
220
- };
221
- });
222
- writeSlogObject({ type: 'import-kernel-start' });
223
- const kernelNS = await importBundle(kernelBundle, {
224
- filePrefix: 'kernel/...',
225
- endowments: {
226
- console: sloggingKernelConsole,
227
- // See https://github.com/Agoric/agoric-sdk/issues/9515
228
- assert: globalThis.assert,
229
- require: harden(
230
- what => Fail`kernelRequire unprepared to satisfy require(${what})`,
231
- ),
232
- URL: globalThis.Base64, // Unavailable only on XSnap
233
- Base64: globalThis.Base64, // Available only on XSnap
234
- },
235
- });
236
- const buildKernel = kernelNS.default;
237
- writeSlogObject({ type: 'import-kernel-finish' });
238
-
239
- // all vats get these in their global scope, plus a vat-specific 'console'
240
- const vatEndowments = harden({});
241
-
242
- const kernelEndowments = {
243
- waitUntilQuiescent,
244
- kernelStorage,
245
- debugPrefix,
246
- vatEndowments,
247
- makeConsole,
248
- startSubprocessWorkerNode,
249
- startXSnap,
250
- slogCallbacks,
251
- writeSlogObject,
252
- WeakRef,
253
- FinalizationRegistry,
254
- gcAndFinalize: makeGcAndFinalize(engineGC),
255
- bundleHandler,
256
- };
257
-
258
- const kernelRuntimeOptions = {
259
- verbose,
260
- warehousePolicy,
261
- overrideVatManagerOptions,
262
- };
263
- /** @type { ReturnType<typeof import('../kernel/kernel.js').default> } */
264
- const kernel = buildKernel(
265
- kernelEndowments,
266
- deviceEndowments,
267
- kernelRuntimeOptions,
268
- );
269
-
270
- if (runtimeOptions.verbose) {
271
- kernel.kdebugEnable(true);
272
- }
273
-
274
- await kernel.start();
284
+ const kernelInitLabels = /** @type {const} */ ([
285
+ 'kernel-init-start',
286
+ 'kernel-init-finish',
287
+ ]);
288
+ const controller = await slogDuration(kernelInitLabels, {}, async () => {
289
+ const kernelBundle = await slogDuration(
290
+ ['bundle-kernel-start', 'bundle-kernel-finish'],
291
+ {},
292
+ async () => runtimeOptions.kernelBundle ?? buildKernelBundle(),
293
+ );
275
294
 
276
- /**
277
- * Validate and install a code bundle.
278
- *
279
- * @param {EndoZipBase64Bundle} bundle
280
- * @param {BundleID} [allegedBundleID]
281
- * @returns {Promise<BundleID>}
282
- */
283
- async function validateAndInstallBundle(bundle, allegedBundleID = undefined) {
284
- // TODO The following assertion may be removed when checkBundle subsumes
285
- // the responsibility to verify the permanence of a bundle's properties.
286
- // https://github.com/endojs/endo/issues/1106
287
- mustMatch(bundle, endoZipBase64Sha512Shape);
288
- await checkBundle(bundle, computeSha512, allegedBundleID);
289
- const { endoZipBase64Sha512 } = bundle;
290
- assert.typeof(endoZipBase64Sha512, 'string');
291
- const bundleID = `b1-${endoZipBase64Sha512}`;
292
- if (allegedBundleID !== undefined) {
293
- bundleID === allegedBundleID ||
294
- Fail`alleged bundleID ${allegedBundleID} does not match actual ${bundleID}`;
295
+ // FIXME: Put this somewhere better.
296
+ const rejectionHandlers = process.listeners('unhandledRejection');
297
+ if (!rejectionHandlers.includes(onUnhandledRejection)) {
298
+ process.on('unhandledRejection', onUnhandledRejection);
295
299
  }
296
- await kernel.installBundle(bundleID, bundle);
297
- return bundleID;
298
- }
299
-
300
- // the kernel won't leak our objects into the Vats, we must do
301
- // the same in this wrapper
302
- const controller = harden({
303
- log(str) {
304
- kernel.log(str);
305
- },
306
300
 
307
- writeSlogObject,
308
-
309
- dump() {
310
- return deepCopyJsonable(kernel.dump());
311
- },
312
-
313
- verboseDebugMode(flag) {
314
- kernel.kdebugEnable(flag);
315
- },
316
-
317
- validateAndInstallBundle,
318
-
319
- /**
320
- * Run the kernel until the policy says to stop, or the queue is empty.
321
- *
322
- * @param {RunPolicy} [policy] - a RunPolicy to limit the work being done
323
- * @returns {Promise<number>} The number of cranks that were executed.
324
- */
325
- async run(policy) {
326
- return kernel.run(policy);
327
- },
328
-
329
- async step() {
330
- return kernel.step();
331
- },
332
-
333
- async shutdown() {
334
- return kernel.shutdown();
335
- },
336
-
337
- reapAllVats() {
338
- kernel.reapAllVats();
339
- },
340
-
341
- changeKernelOptions(options) {
342
- kernel.changeKernelOptions(options);
343
- },
344
-
345
- getStats() {
346
- return deepCopyJsonable(kernel.getStats());
347
- },
348
-
349
- getStatus() {
350
- return deepCopyJsonable(kernel.getStatus());
351
- },
352
-
353
- getActivityhash() {
354
- return kernelStorage.getActivityhash();
355
- },
356
-
357
- // everything beyond here is for tests, and everything should be migrated
358
- // to be on this 'debug' object to make that clear
359
-
360
- debug: {
361
- addDeviceHook: kernel.addDeviceHook,
362
- },
363
-
364
- pinVatRoot(vatName) {
365
- const vatID = kernel.vatNameToID(vatName);
366
- const kref = kernel.getRootObject(vatID);
367
- kernel.pinObject(kref);
368
- kernelStorage.emitCrankHashes();
369
- return kref;
370
- },
371
-
372
- kpRegisterInterest(kpid) {
373
- return kernel.kpRegisterInterest(kpid);
374
- },
375
-
376
- kpStatus(kpid) {
377
- return kernel.kpStatus(kpid);
378
- },
379
-
380
- kpResolution(kpid, options) {
381
- const result = kernel.kpResolution(kpid, options);
382
- // kpResolution does DB write (changes refcounts) so we need emitCrankHashes here
383
- kernelStorage.emitCrankHashes();
384
- return result;
385
- },
301
+ const kernelConsole = makeConsole(`${debugPrefix}SwingSet:kernel`);
302
+ const sloggingKernelConsole = makeLimitedConsole(level => {
303
+ return (...args) => {
304
+ kernelConsole[level](...args);
305
+ writeSlogObject({ type: 'console', source: 'kernel', level, args });
306
+ };
307
+ });
308
+ const buildKernel = await slogDuration(
309
+ ['import-kernel-start', 'import-kernel-finish'],
310
+ {},
311
+ async () => {
312
+ const kernelNS = await importBundle(kernelBundle, {
313
+ filePrefix: 'kernel/...',
314
+ endowments: {
315
+ console: sloggingKernelConsole,
316
+ // See https://github.com/Agoric/agoric-sdk/issues/9515
317
+ assert: globalThis.assert,
318
+ require: harden(
319
+ what =>
320
+ Fail`kernelRequire unprepared to satisfy require(${what})`,
321
+ ),
322
+ URL: globalThis.Base64, // Unavailable only on XSnap
323
+ Base64: globalThis.Base64, // Available only on XSnap
324
+ },
325
+ });
326
+ return kernelNS.default;
327
+ },
328
+ );
386
329
 
387
- vatNameToID(vatName) {
388
- return kernel.vatNameToID(vatName);
389
- },
390
- deviceNameToID(deviceName) {
391
- return kernel.deviceNameToID(deviceName);
392
- },
330
+ const kernelEndowments = {
331
+ waitUntilQuiescent,
332
+ kernelStorage,
333
+ debugPrefix,
334
+ // all vats get these in their global scope, plus a vat-specific 'console'
335
+ vatEndowments: harden({}),
336
+ makeConsole,
337
+ startSubprocessWorkerNode,
338
+ startXSnap,
339
+ slogCallbacks,
340
+ writeSlogObject,
341
+ slogDuration,
342
+ WeakRef,
343
+ FinalizationRegistry,
344
+ gcAndFinalize: makeGcAndFinalize(engineGC),
345
+ bundleHandler,
346
+ };
347
+ const kernelRuntimeOptions = {
348
+ verbose,
349
+ warehousePolicy,
350
+ overrideVatManagerOptions,
351
+ };
393
352
 
394
- injectQueuedUpgradeEvents: () => kernel.injectQueuedUpgradeEvents(),
353
+ /** @type { ReturnType<typeof import('../kernel/kernel.js').default> } */
354
+ const kernel = buildKernel(
355
+ kernelEndowments,
356
+ deviceEndowments,
357
+ kernelRuntimeOptions,
358
+ );
395
359
 
396
- /**
397
- * Queue a method call into the named vat
398
- *
399
- * @param {string} vatName
400
- * @param {string|symbol} method
401
- * @param {unknown[]} args
402
- * @param {ResolutionPolicy} resultPolicy
403
- */
404
- queueToVatRoot(vatName, method, args = [], resultPolicy = 'ignore') {
405
- const vatID = kernel.vatNameToID(vatName);
406
- if (typeof method !== 'symbol') {
407
- assert.typeof(method, 'string');
408
- }
409
- const kref = kernel.getRootObject(vatID);
410
- const kpid = kernel.queueToKref(kref, method, args, resultPolicy);
411
- if (kpid) {
412
- kernel.kpRegisterInterest(kpid);
413
- }
414
- kernelStorage.emitCrankHashes();
415
- return kpid;
416
- },
360
+ await kernel.start();
417
361
 
418
362
  /**
419
- * Queue a method call to an object represented by a kmarshal token
363
+ * Validate and install a code bundle.
420
364
  *
421
- * @param {any} target
422
- * @param {string|symbol} method
423
- * @param {unknown[]} args
424
- * @param {ResolutionPolicy} resultPolicy
365
+ * @param {EndoZipBase64Bundle} bundle
366
+ * @param {BundleID} [allegedBundleID]
367
+ * @returns {Promise<BundleID>}
425
368
  */
426
- queueToVatObject(target, method, args = [], resultPolicy = 'ignore') {
427
- const targetKref = krefOf(target);
428
- assert.typeof(targetKref, 'string');
429
- if (typeof method !== 'symbol') {
430
- assert.typeof(method, 'string');
431
- }
432
- const kpid = kernel.queueToKref(targetKref, method, args, resultPolicy);
433
- if (kpid) {
434
- kernel.kpRegisterInterest(kpid);
369
+ async function validateAndInstallBundle(
370
+ bundle,
371
+ allegedBundleID = undefined,
372
+ ) {
373
+ // TODO The following assertion may be removed when checkBundle subsumes
374
+ // the responsibility to verify the permanence of a bundle's properties.
375
+ // https://github.com/endojs/endo/issues/1106
376
+ mustMatch(bundle, endoZipBase64Sha512Shape);
377
+ await checkBundle(bundle, computeSha512, allegedBundleID);
378
+ const { endoZipBase64Sha512 } = bundle;
379
+ assert.typeof(endoZipBase64Sha512, 'string');
380
+ const bundleID = `b1-${endoZipBase64Sha512}`;
381
+ if (allegedBundleID !== undefined) {
382
+ bundleID === allegedBundleID ||
383
+ Fail`alleged bundleID ${allegedBundleID} does not match actual ${bundleID}`;
435
384
  }
436
- kernelStorage.emitCrankHashes();
437
- return kpid;
438
- },
439
-
440
- upgradeStaticVat(vatName, shouldPauseFirst, bundleID, options = {}) {
441
- const vatID = kernel.vatNameToID(vatName);
442
- let pauseTarget = null;
443
- if (shouldPauseFirst) {
444
- pauseTarget = kslot(kernel.getRootObject(vatID));
445
- }
446
- if (!options.upgradeMessage) {
447
- options.upgradeMessage = `vat ${vatName} upgraded`;
448
- }
449
- const result = controller.queueToVatRoot(
450
- 'vatAdmin',
451
- 'upgradeStaticVat',
452
- [vatID, pauseTarget, bundleID, options],
453
- 'ignore',
454
- );
455
- // no emitCrankHashes here because queueToVatRoot did that
456
- return result;
457
- },
458
-
459
- /**
460
- * terminate a vat by ID
461
- *
462
- * This allows the host app to terminate any vat. The effect is
463
- * equivalent to the holder of the vat's `adminNode` calling
464
- * `E(adminNode).terminateWithFailure(reason)`, or the vat itself
465
- * calling `vatPowers.exitVatWithFailure(reason)`. It accepts a
466
- * reason capdata structure (use 'kser()' to build one), which
467
- * will be included in rejection data for the promise available to
468
- * `E(adminNode).done()`, just like the internal termination APIs.
469
- * Note that no slots/krefs are allowed in 'reason' when
470
- * terminating the vat externally.
471
- *
472
- * This is a superpower available only from the host app, not from
473
- * within vats, since `vatID` is merely a string and can be forged
474
- * trivially. The host app is responsible for supplying the right
475
- * vatID to kill, by looking at the database or logs (note that
476
- * vats do not know their own vatID, and `controller.vatNameToID`
477
- * only works for static vats, not dynamic).
478
- *
479
- * This will cause state changes in the swing-store (specifically
480
- * marking the vat as terminated, and rejection all its
481
- * outstanding promises), which must be committed before they will
482
- * be durable. Either call `hostStorage.commit()` immediately
483
- * after calling this, or call `controller.run()` and *then*
484
- * `hostStorage.commit()` as you would normally do in response to
485
- * other I/O or timer activity.
486
- *
487
- * The first `controller.run()` after this call will delete all
488
- * the old vat's state at once, unless you use a
489
- * [`runPolicy`](../../docs/run-policy.md) to rate-limit cleanups.
490
- *
491
- * @param {VatID} vatID
492
- * @param {SwingSetCapData} reasonCD
493
- */
385
+ await kernel.installBundle(bundleID, bundle);
386
+ return bundleID;
387
+ }
494
388
 
495
- terminateVat(vatID, reasonCD) {
496
- insistCapData(reasonCD);
497
- assert(reasonCD.slots.length === 0, 'no slots allowed in reason');
498
- kernel.terminateVatExternally(vatID, reasonCD);
499
- },
389
+ // the kernel won't leak our objects into the Vats, we must do
390
+ // the same in this wrapper
391
+ return harden({
392
+ log(str) {
393
+ kernel.log(str);
394
+ },
395
+
396
+ writeSlogObject,
397
+
398
+ slogDuration,
399
+
400
+ dump() {
401
+ return deepCopyJsonable(kernel.dump());
402
+ },
403
+
404
+ verboseDebugMode(flag) {
405
+ kernel.kdebugEnable(flag);
406
+ },
407
+
408
+ validateAndInstallBundle,
409
+
410
+ /**
411
+ * Run the kernel until the policy says to stop, or the queue is empty.
412
+ *
413
+ * @param {RunPolicy} [policy] - a RunPolicy to limit the work being done
414
+ * @returns {Promise<number>} The number of cranks that were executed.
415
+ */
416
+ async run(policy) {
417
+ return kernel.run(policy);
418
+ },
419
+
420
+ async step() {
421
+ return kernel.step();
422
+ },
423
+
424
+ async shutdown() {
425
+ return kernel.shutdown();
426
+ },
427
+
428
+ reapAllVats() {
429
+ kernel.reapAllVats();
430
+ },
431
+
432
+ changeKernelOptions(options) {
433
+ kernel.changeKernelOptions(options);
434
+ },
435
+
436
+ getStats() {
437
+ return kernel.getStats();
438
+ },
439
+
440
+ getStatus() {
441
+ return deepCopyJsonable(kernel.getStatus());
442
+ },
443
+
444
+ getActivityhash() {
445
+ return kernelStorage.getActivityhash();
446
+ },
447
+
448
+ // everything beyond here is for tests, and everything should be migrated
449
+ // to be on this 'debug' object to make that clear
450
+
451
+ debug: {
452
+ addDeviceHook: kernel.addDeviceHook,
453
+ },
454
+
455
+ pinVatRoot(vatName) {
456
+ const vatID = kernel.vatNameToID(vatName);
457
+ const kref = kernel.getRootObject(vatID);
458
+ kernel.pinObject(kref);
459
+ kernelStorage.emitCrankHashes();
460
+ return kref;
461
+ },
462
+
463
+ kpRegisterInterest(kpid) {
464
+ return kernel.kpRegisterInterest(kpid);
465
+ },
466
+
467
+ kpStatus(kpid) {
468
+ return kernel.kpStatus(kpid);
469
+ },
470
+
471
+ kpResolution(kpid, options) {
472
+ const result = kernel.kpResolution(kpid, options);
473
+ // kpResolution does DB write (changes refcounts) so we need emitCrankHashes here
474
+ kernelStorage.emitCrankHashes();
475
+ return result;
476
+ },
477
+
478
+ vatNameToID(vatName) {
479
+ return kernel.vatNameToID(vatName);
480
+ },
481
+ deviceNameToID(deviceName) {
482
+ return kernel.deviceNameToID(deviceName);
483
+ },
484
+
485
+ injectQueuedUpgradeEvents: () => kernel.injectQueuedUpgradeEvents(),
486
+
487
+ /**
488
+ * Queue a method call into the named vat
489
+ *
490
+ * @param {string} vatName
491
+ * @param {string|symbol} method
492
+ * @param {unknown[]} args
493
+ * @param {ResolutionPolicy} resultPolicy
494
+ */
495
+ queueToVatRoot(vatName, method, args = [], resultPolicy = 'ignore') {
496
+ const vatID = kernel.vatNameToID(vatName);
497
+ if (typeof method !== 'symbol') {
498
+ assert.typeof(method, 'string');
499
+ }
500
+ const kref = kernel.getRootObject(vatID);
501
+ const kpid = kernel.queueToKref(kref, method, args, resultPolicy);
502
+ if (kpid) {
503
+ kernel.kpRegisterInterest(kpid);
504
+ }
505
+ kernelStorage.emitCrankHashes();
506
+ return kpid;
507
+ },
508
+
509
+ /**
510
+ * Queue a method call to an object represented by a kmarshal token
511
+ *
512
+ * @param {any} target
513
+ * @param {string|symbol} method
514
+ * @param {unknown[]} args
515
+ * @param {ResolutionPolicy} resultPolicy
516
+ */
517
+ queueToVatObject(target, method, args = [], resultPolicy = 'ignore') {
518
+ const targetKref = krefOf(target);
519
+ assert.typeof(targetKref, 'string');
520
+ if (typeof method !== 'symbol') {
521
+ assert.typeof(method, 'string');
522
+ }
523
+ const kpid = kernel.queueToKref(targetKref, method, args, resultPolicy);
524
+ if (kpid) {
525
+ kernel.kpRegisterInterest(kpid);
526
+ }
527
+ kernelStorage.emitCrankHashes();
528
+ return kpid;
529
+ },
530
+
531
+ upgradeStaticVat(vatName, shouldPauseFirst, bundleID, options = {}) {
532
+ const vatID = kernel.vatNameToID(vatName);
533
+ let pauseTarget = null;
534
+ if (shouldPauseFirst) {
535
+ pauseTarget = kslot(kernel.getRootObject(vatID));
536
+ }
537
+ if (!options.upgradeMessage) {
538
+ options.upgradeMessage = `vat ${vatName} upgraded`;
539
+ }
540
+ const result = controller.queueToVatRoot(
541
+ 'vatAdmin',
542
+ 'upgradeStaticVat',
543
+ [vatID, pauseTarget, bundleID, options],
544
+ 'ignore',
545
+ );
546
+ // no emitCrankHashes here because queueToVatRoot did that
547
+ return result;
548
+ },
549
+
550
+ /**
551
+ * terminate a vat by ID
552
+ *
553
+ * This allows the host app to terminate any vat. The effect is
554
+ * equivalent to the holder of the vat's `adminNode` calling
555
+ * `E(adminNode).terminateWithFailure(reason)`, or the vat itself
556
+ * calling `vatPowers.exitVatWithFailure(reason)`. It accepts a
557
+ * reason capdata structure (use 'kser()' to build one), which
558
+ * will be included in rejection data for the promise available to
559
+ * `E(adminNode).done()`, just like the internal termination APIs.
560
+ * Note that no slots/krefs are allowed in 'reason' when
561
+ * terminating the vat externally.
562
+ *
563
+ * This is a superpower available only from the host app, not from
564
+ * within vats, since `vatID` is merely a string and can be forged
565
+ * trivially. The host app is responsible for supplying the right
566
+ * vatID to kill, by looking at the database or logs (note that
567
+ * vats do not know their own vatID, and `controller.vatNameToID`
568
+ * only works for static vats, not dynamic).
569
+ *
570
+ * This will cause state changes in the swing-store (specifically
571
+ * marking the vat as terminated, and rejection all its
572
+ * outstanding promises), which must be committed before they will
573
+ * be durable. Either call `hostStorage.commit()` immediately
574
+ * after calling this, or call `controller.run()` and *then*
575
+ * `hostStorage.commit()` as you would normally do in response to
576
+ * other I/O or timer activity.
577
+ *
578
+ * The first `controller.run()` after this call will delete all
579
+ * the old vat's state at once, unless you use a
580
+ * [`runPolicy`](../../docs/run-policy.md) to rate-limit cleanups.
581
+ *
582
+ * @param {VatID} vatID
583
+ * @param {SwingSetCapData} reasonCD
584
+ */
585
+
586
+ terminateVat(vatID, reasonCD) {
587
+ insistCapData(reasonCD);
588
+ assert(reasonCD.slots.length === 0, 'no slots allowed in reason');
589
+ kernel.terminateVatExternally(vatID, reasonCD);
590
+ },
591
+ });
500
592
  });
501
593
 
502
- writeSlogObject({ type: 'kernel-init-finish' });
503
-
504
594
  return controller;
505
595
  }
506
596
  /** @typedef {EReturn<typeof makeSwingsetController>} SwingsetController */