@agoric/swingset-vat 0.33.0-u17.1 → 0.33.0-u18.1

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.
@@ -24,7 +24,6 @@ export function buildSerializationTools(syscall, deviceName) {
24
24
  send(method, args) {
25
25
  assert.typeof(method, 'string');
26
26
  assert(Array.isArray(args), args);
27
- // eslint-disable-next-line no-use-before-define
28
27
  const capdata = serialize([method, args]);
29
28
  syscall.sendOnly(slot, capdata);
30
29
  },
@@ -71,6 +71,23 @@ import { Nat } from '@endo/nat';
71
71
  // replace it with one that tracks which parts of the state have been
72
72
  // modified, to build more efficient Merkle proofs.
73
73
 
74
+ /**
75
+ * @typedef {object} Mailbox
76
+ * @property {bigint} ack
77
+ * @property {Map<bigint, unknown>} outbox
78
+ */
79
+
80
+ /**
81
+ * @typedef {object} MailboxExport
82
+ * @property {number} ack
83
+ * @property {Array<[number, unknown]>} outbox
84
+ */
85
+
86
+ /**
87
+ * @param {MailboxExport} data
88
+ * @param {Partial<Mailbox>} [inout]
89
+ * @returns {Mailbox}
90
+ */
74
91
  export function importMailbox(data, inout = {}) {
75
92
  const outbox = new Map();
76
93
  for (const m of data.outbox) {
@@ -78,10 +95,15 @@ export function importMailbox(data, inout = {}) {
78
95
  }
79
96
  inout.ack = Nat(data.ack);
80
97
  inout.outbox = outbox;
81
- return inout;
98
+ return /** @type {Mailbox} */ (inout);
82
99
  }
83
100
 
101
+ /**
102
+ * @param {Mailbox} inout
103
+ * @returns {MailboxExport}
104
+ */
84
105
  export function exportMailbox(inout) {
106
+ /** @type {MailboxExport['outbox']} */
85
107
  const messages = [];
86
108
  for (const [msgnum, body] of inout.outbox) {
87
109
  messages.push([Number(msgnum), body]);
@@ -93,64 +115,75 @@ export function exportMailbox(inout) {
93
115
  };
94
116
  }
95
117
 
96
- export function buildMailboxStateMap(state = harden(new Map())) {
97
- function getOrCreatePeer(peer) {
98
- if (!state.has(peer)) {
99
- const inout = {
100
- outbox: harden(new Map()),
101
- ack: 0n,
102
- };
103
- state.set(peer, inout);
104
- }
105
- return state.get(peer);
118
+ /**
119
+ * @param {Map<string, Mailbox>} state
120
+ */
121
+ export function exportMailboxData(state) {
122
+ /** @type {Record<string, {inboundAck: MailboxExport['ack'], outbox: MailboxExport['outbox']}>} */
123
+ const data = {};
124
+ for (const [peer, inout] of state.entries()) {
125
+ const exported = exportMailbox(inout);
126
+ data[peer] = {
127
+ inboundAck: exported.ack,
128
+ outbox: exported.outbox,
129
+ };
106
130
  }
131
+ return harden(data);
132
+ }
107
133
 
108
- function add(peer, msgnum, body) {
109
- getOrCreatePeer(`${peer}`).outbox.set(Nat(msgnum), `${body}`);
134
+ function getOrCreatePeer(state, peer) {
135
+ if (!state.has(peer)) {
136
+ const inout = {
137
+ outbox: harden(new Map()),
138
+ ack: 0n,
139
+ };
140
+ state.set(peer, inout);
110
141
  }
142
+ return state.get(peer);
143
+ }
111
144
 
112
- function remove(peer, msgnum) {
113
- const messages = getOrCreatePeer(`${peer}`).outbox;
114
- messages.delete(Nat(msgnum));
145
+ /**
146
+ * @param {ReturnType<exportMailboxData>} data
147
+ * @returns {Map<string, Mailbox>}
148
+ */
149
+ export function makeEphemeralMailboxStorage(data) {
150
+ const state = harden(new Map());
151
+ for (const peer of Object.getOwnPropertyNames(data)) {
152
+ const inout = getOrCreatePeer(state, peer);
153
+ const d = data[peer];
154
+ importMailbox(
155
+ {
156
+ ack: d.inboundAck,
157
+ outbox: d.outbox,
158
+ },
159
+ inout,
160
+ );
115
161
  }
162
+ return state;
163
+ }
116
164
 
117
- function setAcknum(peer, msgnum) {
118
- getOrCreatePeer(`${peer}`).ack = Nat(msgnum);
165
+ /**
166
+ * @template [T=unknown]
167
+ * @param {Pick<Map<string, T>, 'has' | 'get'> & {set: (key: string, value: T) => void}} [state]
168
+ */
169
+ export function buildMailboxStateMap(state = harden(new Map())) {
170
+ function add(peer, msgnum, body) {
171
+ getOrCreatePeer(state, `${peer}`).outbox.set(Nat(msgnum), `${body}`);
119
172
  }
120
173
 
121
- function exportToData() {
122
- const data = {};
123
- for (const [peer, inout] of state.entries()) {
124
- const exported = exportMailbox(inout);
125
- data[peer] = {
126
- inboundAck: exported.ack,
127
- outbox: exported.outbox,
128
- };
129
- }
130
- return harden(data);
174
+ function remove(peer, msgnum) {
175
+ const messages = getOrCreatePeer(state, `${peer}`).outbox;
176
+ messages.delete(Nat(msgnum));
131
177
  }
132
178
 
133
- function populateFromData(data) {
134
- !state.size || Fail`cannot populateFromData: outbox is not empty`;
135
- for (const peer of Object.getOwnPropertyNames(data)) {
136
- const inout = getOrCreatePeer(peer);
137
- const d = data[peer];
138
- importMailbox(
139
- {
140
- ack: d.inboundAck,
141
- outbox: d.outbox,
142
- },
143
- inout,
144
- );
145
- }
179
+ function setAcknum(peer, msgnum) {
180
+ getOrCreatePeer(state, `${peer}`).ack = Nat(msgnum);
146
181
  }
147
182
 
148
183
  return harden({
149
184
  add,
150
185
  remove,
151
186
  setAcknum,
152
- exportToData,
153
- populateFromData,
154
187
  });
155
188
  }
156
189
 
package/src/index.js CHANGED
@@ -8,11 +8,14 @@ export {
8
8
  buildKernelBundles,
9
9
  loadBasedir,
10
10
  loadSwingsetConfigFile,
11
+ normalizeConfig,
11
12
  } from './controller/initializeSwingset.js';
12
13
  export { upgradeSwingset } from './controller/upgradeSwingset.js';
13
14
  export {
14
15
  buildMailboxStateMap,
15
16
  buildMailbox,
17
+ exportMailboxData,
18
+ makeEphemeralMailboxStorage,
16
19
  } from './devices/mailbox/mailbox.js';
17
20
  export { buildTimer } from './devices/timer/timer.js';
18
21
  export { buildBridge } from './devices/bridge/bridge.js';
@@ -69,7 +69,7 @@ function makeDRTranslator(deviceID, kernelKeeper) {
69
69
  *
70
70
  * @param {string} deviceID
71
71
  * @param {string} deviceName
72
- * @param {*} kernelKeeper
72
+ * @param {KernelKeeper} kernelKeeper
73
73
  * @returns {(dsc: DeviceSyscallObject) => KernelSyscallObject}
74
74
  */
75
75
  export function makeDSTranslator(deviceID, deviceName, kernelKeeper) {
@@ -45,7 +45,7 @@ function parseAction(s) {
45
45
  }
46
46
 
47
47
  /**
48
- * @param {*} kernelKeeper
48
+ * @param {KernelKeeper} kernelKeeper
49
49
  * @returns {import('../types-internal.js').RunQueueEvent | undefined}
50
50
  */
51
51
  export function processGCActionSet(kernelKeeper) {
@@ -86,10 +86,9 @@ export function processGCActionSet(kernelKeeper) {
86
86
  const hasCList = vatKeeper.hasCListEntry(kref);
87
87
  const isReachable = hasCList ? vatKeeper.getReachableFlag(kref) : undefined;
88
88
  const exists = kernelKeeper.kernelObjectExists(kref);
89
- // @ts-expect-error xxx
90
89
  const { reachable, recognizable } = exists
91
90
  ? kernelKeeper.getObjectRefCount(kref)
92
- : {};
91
+ : { reachable: 0, recognizable: 0 };
93
92
 
94
93
  if (type === 'dropExport') {
95
94
  if (!exists) return false; // already, shouldn't happen
@@ -39,8 +39,10 @@ import { makeDeviceTranslators } from './deviceTranslator.js';
39
39
  import { notifyTermination } from './notifyTermination.js';
40
40
  import { makeVatAdminHooks } from './vat-admin-hooks.js';
41
41
 
42
- /** @import * as liveslots from '@agoric/swingset-liveslots' */
43
- /** @import {PolicyInputCleanupCounts} from '../types-external.js' */
42
+ /**
43
+ * @import {MeterConsumption, VatDeliveryObject, VatDeliveryResult, VatSyscallObject, VatSyscallResult} from '@agoric/swingset-liveslots';
44
+ * @import {PolicyInputCleanupCounts} from '../types-external.js';
45
+ */
44
46
 
45
47
  function abbreviateReplacer(_, arg) {
46
48
  if (typeof arg === 'bigint') {
@@ -56,7 +58,7 @@ function abbreviateReplacer(_, arg) {
56
58
  /**
57
59
  * Provide the kref of a vat's root object, as if it had been exported.
58
60
  *
59
- * @param {*} kernelKeeper Kernel keeper managing persistent kernel state.
61
+ * @param {KernelKeeper} kernelKeeper Kernel keeper managing persistent kernel state.
60
62
  * @param {string} vatID Vat ID of the vat whose root kref is sought.
61
63
  *
62
64
  * @returns {string} the kref of the root object of the given vat.
@@ -213,6 +215,10 @@ export default function buildKernel(
213
215
  return deviceID;
214
216
  }
215
217
 
218
+ function injectQueuedUpgradeEvents() {
219
+ kernelKeeper.injectQueuedUpgradeEvents();
220
+ }
221
+
216
222
  function addImport(forVatID, what) {
217
223
  if (!started) {
218
224
  throw Error('must do kernel.start() before addImport()');
@@ -285,15 +291,16 @@ export default function buildKernel(
285
291
  const vatKeeper = kernelKeeper.provideVatKeeper(vatID);
286
292
  critical = vatKeeper.getOptions().critical;
287
293
 
288
- // Reject all promises decided by the vat, making sure to capture the list
289
- // of kpids before that data is deleted.
290
- const deadPromises = [...kernelKeeper.enumeratePromisesByDecider(vatID)];
291
- // remove vatID from the list of live vats, and mark for deletion
294
+ // remove vatID from the list of live vats, and mark for
295
+ // deletion (which will happen later, in vat-cleanup events)
292
296
  kernelKeeper.deleteVatID(vatID);
293
297
  kernelKeeper.markVatAsTerminated(vatID);
294
298
  deferred.push(kernelKeeper.removeVatFromSwingStoreExports(vatID));
295
- for (const kpid of deadPromises) {
296
- resolveToError(kpid, makeError('vat terminated'), vatID);
299
+
300
+ // Reject all promises decided by the vat
301
+ const errdata = makeError('vat terminated');
302
+ for (const [kpid, _p] of kernelKeeper.enumeratePromisesByDecider(vatID)) {
303
+ resolveToError(kpid, errdata, vatID);
297
304
  }
298
305
  }
299
306
  if (critical) {
@@ -380,7 +387,6 @@ export default function buildKernel(
380
387
 
381
388
  /**
382
389
  *
383
- * @typedef { import('@agoric/swingset-liveslots').MeterConsumption } MeterConsumption
384
390
  * @typedef { import('../types-internal.js').MeterID } MeterID
385
391
  * @typedef { import('../types-internal.js').Dirt } Dirt
386
392
  *
@@ -416,7 +422,7 @@ export default function buildKernel(
416
422
  *
417
423
  * @param {VatID} vatID
418
424
  * @param {KernelDeliveryObject} kd
419
- * @param {liveslots.VatDeliveryObject} vd
425
+ * @param {VatDeliveryObject} vd
420
426
  */
421
427
  async function deliverAndLogToVat(vatID, kd, vd) {
422
428
  vatRequestedTermination = undefined;
@@ -426,7 +432,7 @@ export default function buildKernel(
426
432
  const vs = kernelSlog.provideVatSlogger(vatID).vatSlog;
427
433
  await null;
428
434
  try {
429
- /** @type { liveslots.VatDeliveryResult } */
435
+ /** @type { VatDeliveryResult } */
430
436
  const deliveryResult = await vatWarehouse.deliverToVat(vatID, kd, vd, vs);
431
437
  insistVatDeliveryResult(deliveryResult);
432
438
  // const [ ok, problem, usage ] = deliveryResult;
@@ -596,7 +602,9 @@ export default function buildKernel(
596
602
  const p = kernelKeeper.getKernelPromise(kpid);
597
603
  kernelKeeper.incStat('dispatchNotify');
598
604
  const vatKeeper = kernelKeeper.provideVatKeeper(vatID);
599
- p.state !== 'unresolved' || Fail`spurious notification ${kpid}`;
605
+ if (p.state === 'unresolved') {
606
+ throw Fail`spurious notification ${kpid}`;
607
+ }
600
608
  /** @type { KernelDeliveryOneNotify[] } */
601
609
  const resolutions = [];
602
610
  if (!vatKeeper.hasCListEntry(kpid)) {
@@ -611,7 +619,9 @@ export default function buildKernel(
611
619
  return NO_DELIVERY_CRANK_RESULTS;
612
620
  }
613
621
  for (const toResolve of targets) {
614
- const { state, data } = kernelKeeper.getKernelPromise(toResolve);
622
+ const tp = kernelKeeper.getKernelPromise(toResolve);
623
+ assert(tp.state !== 'unresolved');
624
+ const { state, data } = tp;
615
625
  resolutions.push([toResolve, { state, data }]);
616
626
  }
617
627
  /** @type { KernelDeliveryNotify } */
@@ -700,6 +710,7 @@ export default function buildKernel(
700
710
  total:
701
711
  work.exports +
702
712
  work.imports +
713
+ work.promises +
703
714
  work.kv +
704
715
  work.snapshots +
705
716
  work.transcripts,
@@ -741,6 +752,8 @@ export default function buildKernel(
741
752
  kdebug(`vat ${vatID} terminated before startVat delivered`);
742
753
  return NO_DELIVERY_CRANK_RESULTS;
743
754
  }
755
+ const vatKeeper = kernelKeeper.provideVatKeeper(vatID);
756
+ vatKeeper.setVatParameters(vatParameters);
744
757
  const { meterID } = vatInfo;
745
758
  /** @type { KernelDeliveryStartVat } */
746
759
  const kd = harden(['startVat', vatParameters]);
@@ -994,9 +1007,30 @@ export default function buildKernel(
994
1007
  return results;
995
1008
  }
996
1009
 
997
- // reject all promises for which the vat was decider
998
- for (const kpid of kernelKeeper.enumeratePromisesByDecider(vatID)) {
999
- resolveToError(kpid, disconnectionCapData, vatID);
1010
+ // We are homesick for a future in which most promises are
1011
+ // durable, and vats do not need to subscribe to their own
1012
+ // promises to make promise-watchers work. In that world, vats
1013
+ // somehow mark the non-durable promises, which we must
1014
+ // reject/disconnect on their behalf during upgrade.
1015
+ //
1016
+ // To handle the present reality, without durable promises, we
1017
+ // pretend that all promises are so marked.
1018
+
1019
+ // reject all ephemeral promises for which the vat was decider
1020
+ for (const [kpid, p] of kernelKeeper.enumeratePromisesByDecider(vatID)) {
1021
+ const isEphemeral = true; // future vats will mark these explicitly
1022
+ const selfSubscribed =
1023
+ p.state === 'unresolved' && p.subscribers.includes(vatID);
1024
+ if (isEphemeral) {
1025
+ resolveToError(kpid, disconnectionCapData, vatID);
1026
+ if (!selfSubscribed) {
1027
+ // If the vat was subscribed to its own promise, the
1028
+ // resolveToError will enqueue a dispatch.notify, whose delivery
1029
+ // will delete the c-list entry. If it is *not* subscribed, we
1030
+ // should delete the c-list entry now, because nobody else will.
1031
+ vatKeeper.deleteCListEntriesForKernelSlots([kpid]);
1032
+ }
1033
+ }
1000
1034
  }
1001
1035
 
1002
1036
  // simulate an abandonExports syscall from the vat,
@@ -1020,6 +1054,7 @@ export default function buildKernel(
1020
1054
  });
1021
1055
  const vatOptions = harden({ ...origOptions, workerOptions });
1022
1056
  vatKeeper.setSourceAndOptions(source, vatOptions);
1057
+ vatKeeper.setVatParameters(vatParameters);
1023
1058
  // TODO: decref the bundleID once setSourceAndOptions increfs it
1024
1059
 
1025
1060
  // pause, take a deep breath, appreciate this moment of silence
@@ -1093,7 +1128,6 @@ export default function buildKernel(
1093
1128
  } else if (message.type === 'changeVatOptions') {
1094
1129
  // prettier-ignore
1095
1130
  return `changeVatOptions ${message.vatID} options: ${JSON.stringify(message.options)}`;
1096
- // eslint-disable-next-line no-use-before-define
1097
1131
  } else if (gcMessages.includes(message.type)) {
1098
1132
  // prettier-ignore
1099
1133
  return `${message.type} ${message.vatID} ${message.krefs.map(e=>`@${e}`).join(' ')}`;
@@ -1185,6 +1219,7 @@ export default function buildKernel(
1185
1219
  }
1186
1220
  }
1187
1221
  default:
1222
+ // @ts-expect-error
1188
1223
  throw Fail`unknown promise resolution '${kp.state}'`;
1189
1224
  }
1190
1225
  }
@@ -1542,8 +1577,8 @@ export default function buildKernel(
1542
1577
  // not
1543
1578
  /**
1544
1579
  *
1545
- * @param {liveslots.VatSyscallObject} vatSyscallObject
1546
- * @returns {liveslots.VatSyscallResult}
1580
+ * @param {VatSyscallObject} vatSyscallObject
1581
+ * @returns {VatSyscallResult}
1547
1582
  */
1548
1583
  function vatSyscallHandler(vatSyscallObject) {
1549
1584
  if (!vatWarehouse.lookup(vatID)) {
@@ -1558,7 +1593,7 @@ export default function buildKernel(
1558
1593
  let ksc;
1559
1594
  /** @type { KernelSyscallResult } */
1560
1595
  let kres = harden(['error', 'incomplete']);
1561
- /** @type { liveslots.VatSyscallResult } */
1596
+ /** @type { VatSyscallResult } */
1562
1597
  let vres = harden(['error', 'incomplete']);
1563
1598
 
1564
1599
  try {
@@ -1819,6 +1854,7 @@ export default function buildKernel(
1819
1854
  {
1820
1855
  exports: M.number(),
1821
1856
  imports: M.number(),
1857
+ promises: M.number(),
1822
1858
  kv: M.number(),
1823
1859
  snapshots: M.number(),
1824
1860
  transcripts: M.number(),
@@ -2080,6 +2116,7 @@ export default function buildKernel(
2080
2116
  return p.data;
2081
2117
  }
2082
2118
  default: {
2119
+ // @ts-expect-error
2083
2120
  throw Fail`invalid state for ${kpid}: ${p.state}`;
2084
2121
  }
2085
2122
  }
@@ -2178,6 +2215,7 @@ export default function buildKernel(
2178
2215
  pinObject,
2179
2216
  vatNameToID,
2180
2217
  deviceNameToID,
2218
+ injectQueuedUpgradeEvents,
2181
2219
  queueToKref,
2182
2220
  kpRegisterInterest,
2183
2221
  kpStatus,