@agoric/swingset-vat 0.32.3-dev-2b7c650.0 → 0.32.3-dev-31ed402.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agoric/swingset-vat",
3
- "version": "0.32.3-dev-2b7c650.0+2b7c650",
3
+ "version": "0.32.3-dev-31ed402.0+31ed402",
4
4
  "description": "Vat/Container Launcher",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
@@ -27,16 +27,16 @@
27
27
  "@types/yargs-parser": "^21.0.0"
28
28
  },
29
29
  "dependencies": {
30
- "@agoric/assert": "0.6.1-dev-2b7c650.0+2b7c650",
31
- "@agoric/internal": "0.3.3-dev-2b7c650.0+2b7c650",
32
- "@agoric/store": "0.9.3-dev-2b7c650.0+2b7c650",
33
- "@agoric/swing-store": "0.9.2-dev-2b7c650.0+2b7c650",
34
- "@agoric/swingset-liveslots": "0.10.3-dev-2b7c650.0+2b7c650",
35
- "@agoric/swingset-xsnap-supervisor": "0.10.3-dev-2b7c650.0+2b7c650",
36
- "@agoric/time": "0.3.3-dev-2b7c650.0+2b7c650",
37
- "@agoric/vat-data": "0.5.3-dev-2b7c650.0+2b7c650",
38
- "@agoric/xsnap": "0.14.3-dev-2b7c650.0+2b7c650",
39
- "@agoric/xsnap-lockdown": "0.14.1-dev-2b7c650.0+2b7c650",
30
+ "@agoric/assert": "0.6.1-dev-31ed402.0+31ed402",
31
+ "@agoric/internal": "0.3.3-dev-31ed402.0+31ed402",
32
+ "@agoric/store": "0.9.3-dev-31ed402.0+31ed402",
33
+ "@agoric/swing-store": "0.9.2-dev-31ed402.0+31ed402",
34
+ "@agoric/swingset-liveslots": "0.10.3-dev-31ed402.0+31ed402",
35
+ "@agoric/swingset-xsnap-supervisor": "0.10.3-dev-31ed402.0+31ed402",
36
+ "@agoric/time": "0.3.3-dev-31ed402.0+31ed402",
37
+ "@agoric/vat-data": "0.5.3-dev-31ed402.0+31ed402",
38
+ "@agoric/xsnap": "0.14.3-dev-31ed402.0+31ed402",
39
+ "@agoric/xsnap-lockdown": "0.14.1-dev-31ed402.0+31ed402",
40
40
  "@endo/base64": "^0.2.34",
41
41
  "@endo/bundle-source": "^2.7.0",
42
42
  "@endo/captp": "^3.1.4",
@@ -97,5 +97,5 @@
97
97
  "publishConfig": {
98
98
  "access": "public"
99
99
  },
100
- "gitHead": "2b7c650eaf3b6616cc37afb3ff9ba4d67779835d"
100
+ "gitHead": "31ed40207648bb76e2b37cb82dd068fb1837fdd8"
101
101
  }
@@ -20,7 +20,7 @@ import engineGC from '@agoric/internal/src/lib-nodejs/engine-gc.js';
20
20
  import { startSubprocessWorker } from '@agoric/internal/src/lib-nodejs/spawnSubprocessWorker.js';
21
21
  import { waitUntilQuiescent } from '@agoric/internal/src/lib-nodejs/waitUntilQuiescent.js';
22
22
  import { makeGcAndFinalize } from '@agoric/internal/src/lib-nodejs/gc-and-finalize.js';
23
- import { kslot } from '../lib/kmarshal.js';
23
+ import { kslot, krefOf } from '../lib/kmarshal.js';
24
24
  import { insistStorageAPI } from '../lib/storageAPI.js';
25
25
  import {
26
26
  buildKernelBundle,
@@ -360,6 +360,7 @@ export async function makeSwingsetController(
360
360
  kernelStorage.emitCrankHashes();
361
361
  return result;
362
362
  },
363
+
363
364
  vatNameToID(vatName) {
364
365
  return kernel.vatNameToID(vatName);
365
366
  },
@@ -371,13 +372,15 @@ export async function makeSwingsetController(
371
372
  * Queue a method call into the named vat
372
373
  *
373
374
  * @param {string} vatName
374
- * @param {string} method
375
+ * @param {string|symbol} method
375
376
  * @param {unknown[]} args
376
377
  * @param {ResolutionPolicy} resultPolicy
377
378
  */
378
379
  queueToVatRoot(vatName, method, args = [], resultPolicy = 'ignore') {
379
380
  const vatID = kernel.vatNameToID(vatName);
380
- assert.typeof(method, 'string');
381
+ if (typeof method !== 'symbol') {
382
+ assert.typeof(method, 'string');
383
+ }
381
384
  const kref = kernel.getRootObject(vatID);
382
385
  const kpid = kernel.queueToKref(kref, method, args, resultPolicy);
383
386
  if (kpid) {
@@ -387,6 +390,28 @@ export async function makeSwingsetController(
387
390
  return kpid;
388
391
  },
389
392
 
393
+ /**
394
+ * Queue a method call to an object represented by a kmarshal token
395
+ *
396
+ * @param {any} target
397
+ * @param {string|symbol} method
398
+ * @param {unknown[]} args
399
+ * @param {ResolutionPolicy} resultPolicy
400
+ */
401
+ queueToVatObject(target, method, args = [], resultPolicy = 'ignore') {
402
+ const targetKref = krefOf(target);
403
+ assert.typeof(targetKref, 'string');
404
+ if (typeof method !== 'symbol') {
405
+ assert.typeof(method, 'string');
406
+ }
407
+ const kpid = kernel.queueToKref(targetKref, method, args, resultPolicy);
408
+ if (kpid) {
409
+ kernel.kpRegisterInterest(kpid);
410
+ }
411
+ kernelStorage.emitCrankHashes();
412
+ return kpid;
413
+ },
414
+
390
415
  upgradeStaticVat(vatName, shouldPauseFirst, bundleID, options = {}) {
391
416
  const vatID = kernel.vatNameToID(vatName);
392
417
  let pauseTarget = null;
@@ -84,7 +84,7 @@ export function makeKernelQueueHandler(tools) {
84
84
  * by some other vat. This requires a kref as a target.
85
85
  *
86
86
  * @param {string} kref Target of the message
87
- * @param {string} method The method name
87
+ * @param {string|symbol} method The method name
88
88
  * @param {any[]} args The arguments array
89
89
  * @param {ResolutionPolicy} [policy] How the kernel should handle an eventual
90
90
  * resolution or rejection of the message's result promise. Should be
@@ -1,6 +1,6 @@
1
1
  import { Far, passStyleOf } from '@endo/far';
2
2
  import { makeMarshal } from '@endo/marshal';
3
- import { assert } from '@agoric/assert';
3
+ import { assert, Fail } from '@agoric/assert';
4
4
 
5
5
  // Simple wrapper for serializing and unserializing marshalled values inside the
6
6
  // kernel, where we don't actually want to use clists nor actually allocate real
@@ -9,10 +9,10 @@ import { assert } from '@agoric/assert';
9
9
  // agnostic about the internal details of the serialization encoding.
10
10
 
11
11
  /**
12
- * @typedef {{getKref: () => string, iface: () => string}} KCap
12
+ * @typedef {{getKref: () => string, iface: () => string} | Promise} KCap
13
13
  */
14
14
 
15
- const refMap = new WeakMap();
15
+ const { toStringTag } = Symbol;
16
16
 
17
17
  /**
18
18
  * @param {string} kref
@@ -32,21 +32,26 @@ export const kslot = (kref, iface = 'undefined') => {
32
32
  kref.startsWith('lp') ||
33
33
  kref.startsWith('rp')
34
34
  ) {
35
- // TODO: temporary hack because smallcaps encodes promise references
36
- // differently from remotable object references, and the current version of
37
- // the smallcaps decoder annoyingly insists that if the encoded body string
38
- // says a slot is a promise, then convertSlotToVal had better by damn return
39
- // an actual Promise, even if, as in the intended use case here, we neither
40
- // want nor need a promise, nor the overhead of a map to keep track of it
41
- // with. This behavior is in service of defense against a hypothesized
42
- // security issue whose exact nature has largely been forgotton in the
43
- // months since it was first encountered. MarkM is currently researching
44
- // what the problem was thought to have been, to see if it is real and to
45
- // understand it if so. This will eventually result in either changes to
46
- // the smallcaps encoding or to the marshal setup API to support the purely
47
- // manipulative use case. In the meantime, this ugliness...
48
- const p = new Promise(() => undefined);
49
- refMap.set(p, kref);
35
+ // Bizarro World hack for attaching a string property to a Promise, courtesy
36
+ // of MarkM. Even though the @@toStringTag property nominally *is* a
37
+ // string, some unfortunate stuff in our hardened JS safety checks blows up
38
+ // if it actually is. Eventually that will be fixed and we'll be able to
39
+ // use the @@toStringTag property directly, but for now we need to use this
40
+ // weird construct employing a sneaky getter function. Note that this is
41
+ // only necessary in the first place because smallcaps encodes promise
42
+ // references differently from remotable object references, and the current
43
+ // version of the smallcaps decoder annoyingly insists that if the encoded
44
+ // body string says a slot is a promise, then convertSlotToVal had better by
45
+ // damn return an actual Promise, even if, as in the intended use case here,
46
+ // we neither want nor need a promise nor are capable of making any use of
47
+ // the fact that it is one.
48
+ const p = Promise.resolve(`${kref} stand in`);
49
+ // Bizarro World makes eslint hallucinate
50
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
51
+ Object.defineProperty(p, toStringTag, {
52
+ get: reveal => (reveal ? kref : NaN),
53
+ enumerable: false,
54
+ });
50
55
  return harden(p);
51
56
  } else {
52
57
  const o = Far(iface, {
@@ -62,20 +67,33 @@ export const kslot = (kref, iface = 'undefined') => {
62
67
  * @returns {string}
63
68
  */
64
69
  export const krefOf = obj => {
65
- const fromMap = refMap.get(obj);
66
- if (fromMap) {
67
- return fromMap;
70
+ switch (passStyleOf(obj)) {
71
+ case 'promise': {
72
+ // Other half of Bizarro World hack for handling promises. Note the
73
+ // peculiar way the @@toStringTag getter is used.
74
+ const desc = Object.getOwnPropertyDescriptor(obj, toStringTag);
75
+ assert(desc !== undefined, 'promise lacks toStringTag getter');
76
+ const getter = desc.get;
77
+ assert.typeof(getter, 'function', 'toStringTag getter is not a function');
78
+ // @ts-expect-error violates the norm that getters have zero parameters
79
+ const kref = getter(true);
80
+ assert.typeof(kref, 'string');
81
+ return kref;
82
+ }
83
+ case 'remotable': {
84
+ const getKref = obj.getKref;
85
+ assert.typeof(getKref, 'function', 'object lacks getKref function');
86
+ return getKref();
87
+ }
88
+ default:
89
+ // When krefOf() is called as part of kmarshal.serialize, marshal
90
+ // will only give it things that are 'remotable' (Promises and the
91
+ // Far objects created by kslot()). When krefOf() is called by
92
+ // kernel code (as part of extractSingleSlot() or the vat-comms
93
+ // equivalent), it ought to throw if 'obj' is not one of the
94
+ // objects created by our kslot().
95
+ return Fail`krefOf requires a promise or remotable`;
68
96
  }
69
- // When krefOf() is called as part of kmarshal.serialize, marshal
70
- // will only give it things that are 'remotable' (Promises and the
71
- // Far objects created by kslot()). When krefOf() is called by
72
- // kernel code (as part of extractSingleSlot() or the vat-comms
73
- // equivalent), it ought to throw if 'obj' is not one of the Far
74
- // objects created by our kslot().
75
- assert.equal(passStyleOf(obj), 'remotable', obj);
76
- const getKref = obj.getKref;
77
- assert.typeof(getKref, 'function', 'object lacking getKref function');
78
- return getKref();
79
97
  };
80
98
 
81
99
  const kmarshal = makeMarshal(krefOf, kslot, {
@@ -2,7 +2,6 @@ import { assert } from '@agoric/assert';
2
2
  import { objectMap } from '@agoric/internal';
3
3
  import { Far, E } from '@endo/far';
4
4
  import { buildManualTimer } from './manual-timer.js';
5
- import { makePassableEncoding } from './passableEncoding.js';
6
5
 
7
6
  const { Fail, quote: q } = assert;
8
7
 
@@ -13,8 +12,6 @@ export const buildRootObject = () => {
13
12
  const devicesByName = new Map();
14
13
  const callLogsByRemotable = new Map();
15
14
 
16
- const { encodePassable, decodePassable } = makePassableEncoding();
17
-
18
15
  return Far('root', {
19
16
  bootstrap: async (vats, devices) => {
20
17
  vatAdmin = await E(vats.vatAdmin).createVatAdminService(devices.vatAdmin);
@@ -28,17 +25,16 @@ export const buildRootObject = () => {
28
25
  }
29
26
  },
30
27
 
31
- getDevice: async deviceName =>
32
- encodePassable(devicesByName.get(deviceName)),
28
+ getDevice: async deviceName => devicesByName.get(deviceName),
33
29
 
34
- getVatAdmin: async () => encodePassable(vatAdmin),
30
+ getVatAdmin: async () => vatAdmin,
35
31
 
36
- getTimer: async () => encodePassable(timer),
32
+ getTimer: async () => timer,
37
33
 
38
- getVatRoot: async ({ name, rawOutput = false }) => {
39
- const vat = vatData.get(name) || Fail`unknown vat name: ${q(name)}`;
34
+ getVatRoot: async vatName => {
35
+ const vat = vatData.get(vatName) || Fail`unknown vat name: ${q(vatName)}`;
40
36
  const { root } = vat;
41
- return rawOutput ? root : encodePassable(root);
37
+ return root;
42
38
  },
43
39
 
44
40
  createVat: async ({ name, bundleCapName, vatParameters = {} }) => {
@@ -46,7 +42,7 @@ export const buildRootObject = () => {
46
42
  const options = { vatParameters };
47
43
  const { adminNode, root } = await E(vatAdmin).createVat(bcap, options);
48
44
  vatData.set(name, { adminNode, root });
49
- return encodePassable(root);
45
+ return root;
50
46
  },
51
47
 
52
48
  upgradeVat: async ({ name, bundleCapName, vatParameters = {} }) => {
@@ -60,14 +56,11 @@ export const buildRootObject = () => {
60
56
 
61
57
  /**
62
58
  * Derives a remotable from an object by mapping each object property into a method that returns the value.
63
- * This function is intended to be called from testing code outside of the vat environment,
64
- * but methods of the returned remotable are intended to be called from *inside* that environment
65
- * and therefore do not perform argument/response translation.
66
59
  *
67
60
  * @param {string} label
68
- * @param {Record<string, any>} encodedMethodReturnValues
61
+ * @param {Record<string, any>} returnValues
69
62
  */
70
- makeRemotable: (label, encodedMethodReturnValues) => {
63
+ makeRemotable: (label, returnValues) => {
71
64
  const callLogs = [];
72
65
  const makeGetterFunction = (value, name) => {
73
66
  const getValue = (...args) => {
@@ -76,51 +69,33 @@ export const buildRootObject = () => {
76
69
  };
77
70
  return getValue;
78
71
  };
79
- const methodReturnValues = decodePassable(encodedMethodReturnValues);
80
72
  // `objectMap` hardens its result, but...
81
- const methods = objectMap(methodReturnValues, makeGetterFunction);
73
+ const methods = objectMap(returnValues, makeGetterFunction);
82
74
  // ... `Far` requires its methods argument not to be hardened.
83
- const unhardenedMethods = { ...methods };
84
- const remotable = Far(label, unhardenedMethods);
75
+ const remotable = Far(label, { ...methods });
85
76
  callLogsByRemotable.set(remotable, callLogs);
86
- return encodePassable(remotable);
77
+ return remotable;
87
78
  },
88
79
 
89
80
  /**
90
81
  * Returns a copy of a remotable's logs.
91
82
  *
92
- * @param {object} encodedRemotable
83
+ * @param {object} remotable
93
84
  */
94
- getLogForRemotable: encodedRemotable => {
95
- const remotable = decodePassable(encodedRemotable);
96
- const decodedLogs =
85
+ getLogForRemotable: remotable => {
86
+ const logs =
97
87
  callLogsByRemotable.get(remotable) ||
98
88
  Fail`logs not found for ${q(remotable)}`;
99
89
  // Return a copy so that the original remains mutable.
100
- return encodePassable(harden([...decodedLogs]));
101
- },
102
-
103
- messageVat: async ({ name, methodName, args = [] }) => {
104
- const vat = vatData.get(name) || Fail`unknown vat name: ${q(name)}`;
105
- const { root } = vat;
106
- const decodedArgs = args.map(decodePassable);
107
- const result = await E(root)[methodName](...decodedArgs);
108
- return encodePassable(result);
109
- },
110
-
111
- messageVatObject: async ({ presence, methodName, args = [] }) => {
112
- const object = decodePassable(presence);
113
- const decodedArgs = args.map(decodePassable);
114
- const result = await E(object)[methodName](...decodedArgs);
115
- return encodePassable(result);
90
+ return harden([...logs]);
116
91
  },
117
92
 
118
- awaitVatObject: async ({ presence, path = [], rawOutput = false }) => {
119
- let value = await decodePassable(presence);
93
+ awaitVatObject: async (presence, path = []) => {
94
+ let value = await presence;
120
95
  for (const key of path) {
121
96
  value = await value[key];
122
97
  }
123
- return rawOutput ? value : encodePassable(value);
98
+ return value;
124
99
  },
125
100
  });
126
101
  };
@@ -1,81 +0,0 @@
1
- import { assert } from '@agoric/assert';
2
- import { E } from '@endo/far';
3
- import { isObject, makeMarshal } from '@endo/marshal';
4
-
5
- const { Fail, quote: q } = assert;
6
-
7
- const noop = () => {};
8
-
9
- // TODO define these somewhere more accessible. https://github.com/endojs/endo/issues/1488
10
- /**
11
- * @typedef {import('@endo/eventual-send').FarRef<unknown>} PassByRef
12
- * Gets transformed by a marshaller encoding.
13
- * As opposed to pass-by-copy
14
- */
15
-
16
- export const makePassableEncoding = () => {
17
- // Represent all data as passable by replacing non-passable values
18
- // with special-prefix registered symbols.
19
- /** @type {Map<symbol, PassByRef>} */
20
- const replaced = new Map();
21
- /** @type {Map<PassByRef, symbol>} */
22
- const replacements = new Map(); // inverse of 'replaced'
23
-
24
- // This is testing code, so we don't enforce absence of this prefix
25
- // from manually created symbols.
26
- const replacementPrefix = 'replaced:';
27
- const provideReplacement = value => {
28
- const extant = replacements.get(value);
29
- if (extant) {
30
- return extant;
31
- }
32
-
33
- const replacement = Symbol.for(`${replacementPrefix}${replaced.size}`);
34
- replacements.set(value, replacement);
35
- replaced.set(replacement, value);
36
-
37
- // Suppress unhandled promise rejection warnings.
38
- void E.when(value, noop, noop);
39
-
40
- return replacement;
41
- };
42
- const { serialize: encodeReplacements } = makeMarshal(
43
- provideReplacement,
44
- undefined,
45
- {
46
- marshalSaveError: () => {},
47
- serializeBodyFormat: 'capdata',
48
- },
49
- );
50
- const { unserialize: decodeReplacements } = makeMarshal(
51
- undefined,
52
- undefined,
53
- {
54
- serializeBodyFormat: 'capdata',
55
- },
56
- );
57
- const encodePassable = value => decodeReplacements(encodeReplacements(value));
58
- const decodePassable = arg => {
59
- // Recursively replace our symbols with their non-passable source data.
60
- if (Array.isArray(arg)) {
61
- return arg.map(decodePassable);
62
- } else if (isObject(arg)) {
63
- const entries = Object.entries(arg);
64
- const decodedEntries = entries.map(([key, value]) => [
65
- key,
66
- decodePassable(value),
67
- ]);
68
- return Object.fromEntries(decodedEntries);
69
- } else if (
70
- typeof arg !== 'symbol' ||
71
- !Symbol.keyFor(arg) ||
72
- !arg.description?.startsWith(replacementPrefix)
73
- ) {
74
- return arg;
75
- }
76
- const value =
77
- replaced.get(arg) || Fail`no value for replacement: ${q(arg)}`;
78
- return value;
79
- };
80
- return { encodePassable, decodePassable };
81
- };