@agoric/swingset-liveslots 0.10.3-dev-4733782.0 → 0.10.3-dev-af3ced9.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-liveslots",
3
- "version": "0.10.3-dev-4733782.0+4733782",
3
+ "version": "0.10.3-dev-af3ced9.0+af3ced9",
4
4
  "description": "SwingSet ocap support layer",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
@@ -17,9 +17,9 @@
17
17
  "lint:eslint": "eslint ."
18
18
  },
19
19
  "dependencies": {
20
- "@agoric/assert": "0.6.1-dev-4733782.0+4733782",
21
- "@agoric/internal": "0.3.3-dev-4733782.0+4733782",
22
- "@agoric/store": "0.9.3-dev-4733782.0+4733782",
20
+ "@agoric/assert": "0.6.1-dev-af3ced9.0+af3ced9",
21
+ "@agoric/internal": "0.3.3-dev-af3ced9.0+af3ced9",
22
+ "@agoric/store": "0.9.3-dev-af3ced9.0+af3ced9",
23
23
  "@endo/env-options": "^1.1.2",
24
24
  "@endo/errors": "^1.2.0",
25
25
  "@endo/eventual-send": "^1.2.0",
@@ -33,7 +33,7 @@
33
33
  "@endo/promise-kit": "^1.1.0"
34
34
  },
35
35
  "devDependencies": {
36
- "@agoric/kmarshal": "0.1.1-dev-4733782.0+4733782",
36
+ "@agoric/kmarshal": "0.1.1-dev-af3ced9.0+af3ced9",
37
37
  "ava": "^5.3.0"
38
38
  },
39
39
  "files": [
@@ -69,5 +69,5 @@
69
69
  "typeCoverage": {
70
70
  "atLeast": 75.22
71
71
  },
72
- "gitHead": "47337826034f90078930b0135aa0a81f3a1d773e"
72
+ "gitHead": "af3ced91861731353e10a45e4eae63450f74a0ea"
73
73
  }
@@ -73,6 +73,41 @@ export type DefineKindOptions<C> = {
73
73
  finish?: (context: C) => void;
74
74
 
75
75
  /**
76
+ * If provided, it describes the shape of all state records of instances
77
+ * of this kind.
78
+ */
79
+ stateShape?: StateShape;
80
+
81
+ /**
82
+ * If a `receiveAmplifier` function is provided to an exo class kit definition,
83
+ * it will be called with an `Amplify` function. If provided to the definition
84
+ * of a normal exo or exo class, the definition will throw, since only
85
+ * exo kits can be amplified.
86
+ * An `Amplify` function is a function that takes a facet instance of
87
+ * this class kit as an argument, in which case it will return the facets
88
+ * record, giving access to all the facet instances of the same cohort.
89
+ */
90
+ receiveAmplifier?: ReceivePower<Amplify<F>>;
91
+
92
+ /**
93
+ * If a `receiveInstanceTester` function is provided, it will be called
94
+ * during the definition of the exo class or exo class kit with an
95
+ * `IsInstance` function. The first argument of `IsInstance`
96
+ * is the value to be tested. When it may be a facet instance of an
97
+ * exo class kit, the optional second argument, if provided, is
98
+ * a `facetName`. In that case, the function tests only if the first
99
+ * argument is an instance of that facet of the associated exo class kit.
100
+ */
101
+ receiveInstanceTester?: ReceivePower<IsInstance>;
102
+
103
+ // TODO properties above are identical to those in FarClassOptions.
104
+ // These are the only options that should be exposed by
105
+ // vat-data's public virtual/durable exo APIs. This DefineKindOptions
106
+ // should explicitly be a subtype, where the methods below are only for
107
+ // internal use, i.e., below the exo level.
108
+
109
+ /**
110
+ * As a kind option, intended for internal use only.
76
111
  * Meaningful to `makeScalarBigMapStore` and its siblings. These maker
77
112
  * fuctions will make either virtual or durable stores, depending on
78
113
  * this flag. Defaults to off, making virtual but not durable collections.
@@ -84,12 +119,6 @@ export type DefineKindOptions<C> = {
84
119
  */
85
120
  durable?: boolean;
86
121
 
87
- /**
88
- * If provided, it describes the shape of all state records of instances
89
- * of this kind.
90
- */
91
- stateShape?: StateShape;
92
-
93
122
  /**
94
123
  * Intended for internal use only.
95
124
  * Should the raw methods receive their `context` argument as their first
@@ -28,8 +28,18 @@ import {
28
28
  * @typedef {import('@endo/exo/src/exo-tools.js').KitContextProvider } KitContextProvider
29
29
  */
30
30
 
31
- const { hasOwn, defineProperty, getOwnPropertyNames, entries, fromEntries } =
32
- Object;
31
+ /**
32
+ *
33
+ */
34
+
35
+ const {
36
+ hasOwn,
37
+ defineProperty,
38
+ getOwnPropertyNames,
39
+ values,
40
+ entries,
41
+ fromEntries,
42
+ } = Object;
33
43
  const { ownKeys } = Reflect;
34
44
 
35
45
  // Turn on to give each exo instance its own toStringTag value which exposes
@@ -679,6 +689,8 @@ export const makeVirtualObjectManager = (
679
689
  const {
680
690
  finish = undefined,
681
691
  stateShape = undefined,
692
+ receiveAmplifier = undefined,
693
+ receiveInstanceTester = undefined,
682
694
  thisfulMethods = false,
683
695
  } = options;
684
696
  let {
@@ -766,6 +778,11 @@ export const makeVirtualObjectManager = (
766
778
  Fail`A stateShape must be a copyRecord: ${q(stateShape)}`;
767
779
  assertPattern(stateShape);
768
780
 
781
+ if (!multifaceted) {
782
+ receiveAmplifier === undefined ||
783
+ Fail`Only facets of an exo class kit can be amplified, not ${q(tag)}`;
784
+ }
785
+
769
786
  let facetNames;
770
787
 
771
788
  if (isDurable) {
@@ -948,14 +965,20 @@ export const makeVirtualObjectManager = (
948
965
  // and into method-invocation time (which is not).
949
966
 
950
967
  let proto;
968
+ /** @type {ClassContextProvider | undefined} */
969
+ let contextProviderVar;
970
+ /** @type { Record<string, KitContextProvider> | undefined } */
971
+ let contextProviderKitVar;
972
+
951
973
  if (multifaceted) {
952
- /** @type { Record<string, KitContextProvider> } */
953
- const contextProviderKit = fromEntries(
974
+ contextProviderKitVar = fromEntries(
954
975
  facetNames.map((name, index) => [
955
976
  name,
956
977
  rep => {
957
978
  const vref = getSlotForVal(rep);
958
- assert(vref !== undefined);
979
+ if (vref === undefined) {
980
+ return undefined;
981
+ }
959
982
  const { baseRef, facet } = parseVatSlot(vref);
960
983
 
961
984
  // Without this check, an attacker (with access to both
@@ -966,7 +989,9 @@ export const makeVirtualObjectManager = (
966
989
  // objects, but they could invoke all their equivalent methods,
967
990
  // by using e.g.
968
991
  // cohort1.facetA.foo.apply(cohort2.facetB, [...args])
969
- Number(facet) === index || Fail`illegal cross-facet access`;
992
+ if (Number(facet) !== index) {
993
+ return undefined;
994
+ }
970
995
 
971
996
  return harden(contextCache.get(baseRef));
972
997
  },
@@ -975,21 +1000,22 @@ export const makeVirtualObjectManager = (
975
1000
 
976
1001
  proto = defendPrototypeKit(
977
1002
  tag,
978
- harden(contextProviderKit),
1003
+ harden(contextProviderKitVar),
979
1004
  behavior,
980
1005
  thisfulMethods,
981
1006
  interfaceGuardKit,
982
1007
  );
983
1008
  } else {
984
- /** @type {ClassContextProvider} */
985
- const contextProvider = rep => {
1009
+ contextProviderVar = rep => {
986
1010
  const slot = getSlotForVal(rep);
987
- assert(slot !== undefined);
1011
+ if (slot === undefined) {
1012
+ return undefined;
1013
+ }
988
1014
  return harden(contextCache.get(slot));
989
1015
  };
990
1016
  proto = defendPrototype(
991
1017
  tag,
992
- harden(contextProvider),
1018
+ harden(contextProviderVar),
993
1019
  behavior,
994
1020
  thisfulMethods,
995
1021
  interfaceGuard,
@@ -997,6 +1023,10 @@ export const makeVirtualObjectManager = (
997
1023
  }
998
1024
  harden(proto);
999
1025
 
1026
+ // All this to let typescript know that it won't vary during a closure
1027
+ const contextProvider = contextProviderVar;
1028
+ const contextProviderKit = contextProviderKitVar;
1029
+
1000
1030
  // this builds new Representatives, both when creating a new instance and
1001
1031
  // for reanimating an existing one when the old rep gets GCed
1002
1032
 
@@ -1074,6 +1104,59 @@ export const makeVirtualObjectManager = (
1074
1104
  return val;
1075
1105
  };
1076
1106
 
1107
+ if (receiveAmplifier) {
1108
+ assert(contextProviderKit);
1109
+
1110
+ // Amplify a facet to a cohort
1111
+ const amplify = exoFacet => {
1112
+ for (const cp of values(contextProviderKit)) {
1113
+ const context = cp(exoFacet);
1114
+ if (context !== undefined) {
1115
+ return context.facets;
1116
+ }
1117
+ }
1118
+ throw Fail`Must be a facet of ${q(tag)}: ${exoFacet}`;
1119
+ };
1120
+ harden(amplify);
1121
+ receiveAmplifier(amplify);
1122
+ }
1123
+
1124
+ if (receiveInstanceTester) {
1125
+ if (multifaceted) {
1126
+ assert(contextProviderKit);
1127
+
1128
+ const isInstance = (exoFacet, facetName = undefined) => {
1129
+ if (facetName === undefined) {
1130
+ // Is exoFacet and instance of any facet of this class kit?
1131
+ return values(contextProviderKit).some(
1132
+ cp => cp(exoFacet) !== undefined,
1133
+ );
1134
+ }
1135
+ // Is this exoFacet an instance of this specific facet column
1136
+ // of this class kit?
1137
+ assert.typeof(facetName, 'string');
1138
+ const cp = contextProviderKit[facetName];
1139
+ cp !== undefined ||
1140
+ Fail`exo class kit ${q(tag)} has no facet named ${q(facetName)}`;
1141
+ return cp(exoFacet) !== undefined;
1142
+ };
1143
+ harden(isInstance);
1144
+ receiveInstanceTester(isInstance);
1145
+ } else {
1146
+ assert(contextProvider);
1147
+ // Is this exo an instance of this class?
1148
+ const isInstance = (exo, facetName = undefined) => {
1149
+ facetName === undefined ||
1150
+ Fail`facetName can only be used with an exo class kit: ${q(
1151
+ tag,
1152
+ )} has no facet ${q(facetName)}`;
1153
+ return contextProvider(exo) !== undefined;
1154
+ };
1155
+ harden(isInstance);
1156
+ receiveInstanceTester(isInstance);
1157
+ }
1158
+ }
1159
+
1077
1160
  return makeNewInstance;
1078
1161
  };
1079
1162
 
@@ -33,10 +33,12 @@ test('forbid cross-facet prototype attack', t => {
33
33
  thing2.mutable.set(2);
34
34
 
35
35
  t.throws(() => attack1(thing1.mutable, thing2.immutable), {
36
- message: /^illegal cross-facet access/,
36
+ message:
37
+ '"In \\"set\\" method of (thing mutable)" may only be applied to a valid instance: "[Alleged: thing immutable]"',
37
38
  });
38
39
  t.throws(() => attack2(thing1.mutable, thing2.immutable), {
39
- message: /^illegal cross-facet access/,
40
+ message:
41
+ '"In \\"set\\" method of (thing mutable)" may only be applied to a valid instance: "[Alleged: thing immutable]"',
40
42
  });
41
43
  t.is(thing1.immutable.get(), 1);
42
44
  t.is(thing2.immutable.get(), 2);