@agoric/swingset-vat 0.32.3-dev-02c06c4.0 → 0.32.3-dev-fd68b6a.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 +11 -11
- package/src/controller/initializeSwingset.js +69 -60
- package/src/index.js +1 -0
- package/src/kernel/state/kernelKeeper.js +54 -25
- package/src/kernel/state/vatKeeper.js +42 -38
- package/tools/bootstrap-relay.js +9 -0
- package/tools/vat-puppet.js +111 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agoric/swingset-vat",
|
|
3
|
-
"version": "0.32.3-dev-
|
|
3
|
+
"version": "0.32.3-dev-fd68b6a.0+fd68b6a",
|
|
4
4
|
"description": "Vat/Container Launcher",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.js",
|
|
@@ -27,15 +27,15 @@
|
|
|
27
27
|
"@types/yargs-parser": "^21.0.0"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@agoric/internal": "0.3.3-dev-
|
|
31
|
-
"@agoric/kmarshal": "0.1.1-dev-
|
|
32
|
-
"@agoric/store": "0.9.3-dev-
|
|
33
|
-
"@agoric/swing-store": "0.9.2-dev-
|
|
34
|
-
"@agoric/swingset-liveslots": "0.10.3-dev-
|
|
35
|
-
"@agoric/swingset-xsnap-supervisor": "0.10.3-dev-
|
|
36
|
-
"@agoric/time": "0.3.3-dev-
|
|
37
|
-
"@agoric/vat-data": "0.5.3-dev-
|
|
38
|
-
"@agoric/xsnap-lockdown": "0.14.1-dev-
|
|
30
|
+
"@agoric/internal": "0.3.3-dev-fd68b6a.0+fd68b6a",
|
|
31
|
+
"@agoric/kmarshal": "0.1.1-dev-fd68b6a.0+fd68b6a",
|
|
32
|
+
"@agoric/store": "0.9.3-dev-fd68b6a.0+fd68b6a",
|
|
33
|
+
"@agoric/swing-store": "0.9.2-dev-fd68b6a.0+fd68b6a",
|
|
34
|
+
"@agoric/swingset-liveslots": "0.10.3-dev-fd68b6a.0+fd68b6a",
|
|
35
|
+
"@agoric/swingset-xsnap-supervisor": "0.10.3-dev-fd68b6a.0+fd68b6a",
|
|
36
|
+
"@agoric/time": "0.3.3-dev-fd68b6a.0+fd68b6a",
|
|
37
|
+
"@agoric/vat-data": "0.5.3-dev-fd68b6a.0+fd68b6a",
|
|
38
|
+
"@agoric/xsnap-lockdown": "0.14.1-dev-fd68b6a.0+fd68b6a",
|
|
39
39
|
"@endo/base64": "^1.0.8",
|
|
40
40
|
"@endo/bundle-source": "^3.4.2",
|
|
41
41
|
"@endo/captp": "^4.4.2",
|
|
@@ -103,5 +103,5 @@
|
|
|
103
103
|
"typeCoverage": {
|
|
104
104
|
"atLeast": 76.19
|
|
105
105
|
},
|
|
106
|
-
"gitHead": "
|
|
106
|
+
"gitHead": "fd68b6ac1620d3ac9b6e98eeee7a507c3ccb3d79"
|
|
107
107
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import fs from 'fs';
|
|
3
3
|
import path from 'path';
|
|
4
4
|
|
|
5
|
-
import { assert, Fail } from '@endo/errors';
|
|
5
|
+
import { assert, b, Fail } from '@endo/errors';
|
|
6
6
|
import { makeTracer } from '@agoric/internal';
|
|
7
7
|
import { mustMatch } from '@agoric/store';
|
|
8
8
|
import bundleSource from '@endo/bundle-source';
|
|
@@ -86,16 +86,6 @@ export async function buildKernelBundles() {
|
|
|
86
86
|
return harden({ kernel: kernelBundle, ...vdBundles });
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
-
function byName(a, b) {
|
|
90
|
-
if (a.name < b.name) {
|
|
91
|
-
return -1;
|
|
92
|
-
}
|
|
93
|
-
if (a.name > b.name) {
|
|
94
|
-
return 1;
|
|
95
|
-
}
|
|
96
|
-
return 0;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
89
|
/**
|
|
100
90
|
* Scan a directory for files defining the vats to bootstrap for a swingset, and
|
|
101
91
|
* produce a swingset config object for what was found there. Looks for files
|
|
@@ -126,18 +116,18 @@ export function loadBasedir(basedir, options = {}) {
|
|
|
126
116
|
const { includeDevDependencies = false, bundleFormat = undefined } = options;
|
|
127
117
|
/** @type { SwingSetConfigDescriptor } */
|
|
128
118
|
const vats = {};
|
|
129
|
-
const
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
}
|
|
119
|
+
const rVatName = /^vat-(.*)\.js$/s;
|
|
120
|
+
const files = fs.readdirSync(basedir, { withFileTypes: true });
|
|
121
|
+
const vatFiles = files.flatMap(dirent => {
|
|
122
|
+
const file = dirent.name;
|
|
123
|
+
const m = rVatName.exec(file);
|
|
124
|
+
return m && dirent.isFile() ? [{ file, label: m[1] }] : [];
|
|
125
|
+
});
|
|
126
|
+
// eslint-disable-next-line no-shadow,no-nested-ternary
|
|
127
|
+
vatFiles.sort((a, b) => (a.label < b.label ? -1 : a.label > b.label ? 1 : 0));
|
|
128
|
+
for (const { file, label } of vatFiles) {
|
|
129
|
+
const vatSourcePath = path.resolve(basedir, file);
|
|
130
|
+
vats[label] = { sourceSpec: vatSourcePath, parameters: {} };
|
|
141
131
|
}
|
|
142
132
|
/** @type {string | void} */
|
|
143
133
|
let bootstrapPath = path.resolve(basedir, 'bootstrap.js');
|
|
@@ -185,37 +175,42 @@ async function resolveSpecFromConfig(referrer, specPath) {
|
|
|
185
175
|
}
|
|
186
176
|
|
|
187
177
|
/**
|
|
188
|
-
*
|
|
189
|
-
*
|
|
190
|
-
*
|
|
178
|
+
* Convert each entry in a config descriptor group (`vats`/`bundles`/etc.) to
|
|
179
|
+
* normal form: resolve each pathname to a context-insensitive absolute path and
|
|
180
|
+
* run any other appropriate fixup.
|
|
191
181
|
*
|
|
192
|
-
* @param {
|
|
193
|
-
* @param {
|
|
194
|
-
* config file
|
|
195
|
-
* @param {
|
|
196
|
-
*
|
|
182
|
+
* @param {SwingSetConfig} config
|
|
183
|
+
* @param {'vats' | 'bundles' | 'devices'} groupName
|
|
184
|
+
* @param {string | undefined} configPath of the containing config file
|
|
185
|
+
* @param {string} referrer URL
|
|
186
|
+
* @param {(entry: SwingSetConfigProperties, name?: string) => void} [fixupEntry]
|
|
187
|
+
* A function to call on each entry to e.g. add defaults for missing fields
|
|
188
|
+
* such as vat `parameters`.
|
|
197
189
|
*/
|
|
198
|
-
async function normalizeConfigDescriptor(
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
190
|
+
async function normalizeConfigDescriptor(
|
|
191
|
+
config,
|
|
192
|
+
groupName,
|
|
193
|
+
configPath,
|
|
194
|
+
referrer,
|
|
195
|
+
fixupEntry,
|
|
196
|
+
) {
|
|
197
|
+
const normalizeSpec = async (entry, specKey, name) => {
|
|
198
|
+
const sourcePath = await resolveSpecFromConfig(referrer, entry[specKey]);
|
|
199
|
+
fs.existsSync(sourcePath) ||
|
|
200
|
+
Fail`${sourcePath} for ${b(groupName)}[${name}].${b(specKey)} in ${configPath} config file does not exist`;
|
|
201
|
+
entry[specKey] = sourcePath;
|
|
205
202
|
};
|
|
206
203
|
|
|
207
204
|
const jobs = [];
|
|
205
|
+
const desc = config[groupName];
|
|
208
206
|
if (desc) {
|
|
209
|
-
for (const name of Object.
|
|
210
|
-
|
|
207
|
+
for (const [name, entry] of Object.entries(desc)) {
|
|
208
|
+
fixupEntry?.(entry, name);
|
|
211
209
|
if ('sourceSpec' in entry) {
|
|
212
|
-
jobs.push(normalizeSpec(entry, 'sourceSpec'));
|
|
210
|
+
jobs.push(normalizeSpec(entry, 'sourceSpec', name));
|
|
213
211
|
}
|
|
214
212
|
if ('bundleSpec' in entry) {
|
|
215
|
-
jobs.push(normalizeSpec(entry, 'bundleSpec'));
|
|
216
|
-
}
|
|
217
|
-
if (expectParameters && !entry.parameters) {
|
|
218
|
-
entry.parameters = {};
|
|
213
|
+
jobs.push(normalizeSpec(entry, 'bundleSpec', name));
|
|
219
214
|
}
|
|
220
215
|
}
|
|
221
216
|
}
|
|
@@ -223,27 +218,41 @@ async function normalizeConfigDescriptor(desc, referrer, expectParameters) {
|
|
|
223
218
|
}
|
|
224
219
|
|
|
225
220
|
/**
|
|
226
|
-
*
|
|
227
|
-
*
|
|
228
|
-
* @
|
|
229
|
-
*
|
|
230
|
-
|
|
231
|
-
|
|
221
|
+
* @param {SwingSetConfig} config
|
|
222
|
+
* @param {string} [configPath]
|
|
223
|
+
* @returns {Promise<void>}
|
|
224
|
+
* @throws {Error} if the config is invalid
|
|
225
|
+
*/
|
|
226
|
+
export async function normalizeConfig(config, configPath) {
|
|
227
|
+
const base = `file://${process.cwd()}/`;
|
|
228
|
+
const referrer = configPath
|
|
229
|
+
? new URL(configPath, base).href
|
|
230
|
+
: new URL(base).href;
|
|
231
|
+
const fixupVat = vat => (vat.parameters ||= {});
|
|
232
|
+
await Promise.all([
|
|
233
|
+
normalizeConfigDescriptor(config, 'vats', configPath, referrer, fixupVat),
|
|
234
|
+
normalizeConfigDescriptor(config, 'bundles', configPath, referrer),
|
|
235
|
+
// TODO: represent devices
|
|
236
|
+
// normalizeConfigDescriptor(config, 'devices', configPath, referrer),
|
|
237
|
+
]);
|
|
238
|
+
config.bootstrap ||
|
|
239
|
+
Fail`no designated bootstrap vat in ${configPath} config file`;
|
|
240
|
+
(config.vats && config.vats[/** @type {string} */ (config.bootstrap)]) ||
|
|
241
|
+
Fail`bootstrap vat ${config.bootstrap} not found in ${configPath} config file`;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Read and normalize a swingset config file.
|
|
232
246
|
*
|
|
233
|
-
* @
|
|
234
|
-
*
|
|
247
|
+
* @param {string} configPath
|
|
248
|
+
* @returns {Promise<SwingSetConfig | null>} the normalized config,
|
|
249
|
+
* or null if the file did not exist
|
|
235
250
|
*/
|
|
236
251
|
export async function loadSwingsetConfigFile(configPath) {
|
|
237
252
|
await null;
|
|
238
253
|
try {
|
|
239
254
|
const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
240
|
-
|
|
241
|
-
await normalizeConfigDescriptor(config.vats, referrer, true);
|
|
242
|
-
await normalizeConfigDescriptor(config.bundles, referrer, false);
|
|
243
|
-
// await normalizeConfigDescriptor(config.devices, referrer, true); // TODO: represent devices
|
|
244
|
-
config.bootstrap || Fail`no designated bootstrap vat in ${configPath}`;
|
|
245
|
-
(config.vats && config.vats[config.bootstrap]) ||
|
|
246
|
-
Fail`bootstrap vat ${config.bootstrap} not found in ${configPath}`;
|
|
255
|
+
await normalizeConfig(config, configPath);
|
|
247
256
|
return config;
|
|
248
257
|
} catch (e) {
|
|
249
258
|
console.error(`failed to load ${configPath}`);
|
package/src/index.js
CHANGED
|
@@ -43,6 +43,7 @@ const enableKernelGC = true;
|
|
|
43
43
|
* @typedef { import('../../types-external.js').SnapStore } SnapStore
|
|
44
44
|
* @typedef { import('../../types-external.js').TranscriptStore } TranscriptStore
|
|
45
45
|
* @typedef { import('../../types-external.js').VatKeeper } VatKeeper
|
|
46
|
+
* @typedef { Pick<VatKeeper, 'deleteCListEntry' | 'deleteSnapshots' | 'deleteTranscripts'> } VatUndertaker
|
|
46
47
|
* @typedef { import('../../types-internal.js').InternalKernelOptions } InternalKernelOptions
|
|
47
48
|
* @typedef { import('../../types-internal.js').ReapDirtThreshold } ReapDirtThreshold
|
|
48
49
|
* @import {PromiseRecord} from '../../types-internal.js';
|
|
@@ -422,6 +423,8 @@ export default function makeKernelKeeper(
|
|
|
422
423
|
const ephemeral = harden({
|
|
423
424
|
/** @type { Map<string, VatKeeper> } */
|
|
424
425
|
vatKeepers: new Map(),
|
|
426
|
+
/** @type { Map<string, VatUndertaker> } */
|
|
427
|
+
vatUndertakers: new Map(),
|
|
425
428
|
deviceKeepers: new Map(), // deviceID -> deviceKeeper
|
|
426
429
|
});
|
|
427
430
|
|
|
@@ -1044,7 +1047,7 @@ export default function makeKernelKeeper(
|
|
|
1044
1047
|
// first or vref first), and delete the other one in the same
|
|
1045
1048
|
// call, so we don't wind up with half an entry.
|
|
1046
1049
|
|
|
1047
|
-
const
|
|
1050
|
+
const undertaker = provideVatUndertaker(vatID);
|
|
1048
1051
|
const clistPrefix = `${vatID}.c.`;
|
|
1049
1052
|
const exportPrefix = `${clistPrefix}o+`;
|
|
1050
1053
|
const importPrefix = `${clistPrefix}o-`;
|
|
@@ -1092,7 +1095,7 @@ export default function makeKernelKeeper(
|
|
|
1092
1095
|
// drop+retire
|
|
1093
1096
|
const kref = kvStore.get(k) || Fail`getNextKey ensures get`;
|
|
1094
1097
|
const vref = stripPrefix(clistPrefix, k);
|
|
1095
|
-
|
|
1098
|
+
undertaker.deleteCListEntry(kref, vref);
|
|
1096
1099
|
// that will also delete both db keys
|
|
1097
1100
|
work.imports += 1;
|
|
1098
1101
|
remaining -= 1;
|
|
@@ -1109,7 +1112,7 @@ export default function makeKernelKeeper(
|
|
|
1109
1112
|
for (const k of enumeratePrefixedKeys(kvStore, promisePrefix)) {
|
|
1110
1113
|
const kref = kvStore.get(k) || Fail`getNextKey ensures get`;
|
|
1111
1114
|
const vref = stripPrefix(clistPrefix, k);
|
|
1112
|
-
|
|
1115
|
+
undertaker.deleteCListEntry(kref, vref);
|
|
1113
1116
|
// that will also delete both db keys
|
|
1114
1117
|
work.promises += 1;
|
|
1115
1118
|
remaining -= 1;
|
|
@@ -1131,7 +1134,7 @@ export default function makeKernelKeeper(
|
|
|
1131
1134
|
|
|
1132
1135
|
// this will internally loop through 'budget' deletions
|
|
1133
1136
|
remaining = budget.snapshots ?? budget.default;
|
|
1134
|
-
const dsc =
|
|
1137
|
+
const dsc = undertaker.deleteSnapshots(remaining);
|
|
1135
1138
|
work.snapshots += dsc.cleanups;
|
|
1136
1139
|
remaining -= dsc.cleanups;
|
|
1137
1140
|
if (remaining <= 0) {
|
|
@@ -1140,7 +1143,7 @@ export default function makeKernelKeeper(
|
|
|
1140
1143
|
|
|
1141
1144
|
// same
|
|
1142
1145
|
remaining = budget.transcripts ?? budget.default;
|
|
1143
|
-
const dts =
|
|
1146
|
+
const dts = undertaker.deleteTranscripts(remaining);
|
|
1144
1147
|
work.transcripts += dts.cleanups;
|
|
1145
1148
|
remaining -= dts.cleanups;
|
|
1146
1149
|
// last task, so increment cleanups, but dc.done is authoritative
|
|
@@ -1697,6 +1700,26 @@ export default function makeKernelKeeper(
|
|
|
1697
1700
|
initializeVatState(kvStore, transcriptStore, vatID, source, options);
|
|
1698
1701
|
}
|
|
1699
1702
|
|
|
1703
|
+
/** @type {import('./vatKeeper.js').VatKeeperPowers} */
|
|
1704
|
+
const vatKeeperPowers = {
|
|
1705
|
+
transcriptStore,
|
|
1706
|
+
kernelSlog,
|
|
1707
|
+
addKernelObject,
|
|
1708
|
+
addKernelPromiseForVat,
|
|
1709
|
+
kernelObjectExists,
|
|
1710
|
+
incrementRefCount,
|
|
1711
|
+
decrementRefCount,
|
|
1712
|
+
getObjectRefCount,
|
|
1713
|
+
setObjectRefCount,
|
|
1714
|
+
getReachableAndVatSlot,
|
|
1715
|
+
addMaybeFreeKref,
|
|
1716
|
+
incStat,
|
|
1717
|
+
decStat,
|
|
1718
|
+
getCrankNumber,
|
|
1719
|
+
scheduleReap,
|
|
1720
|
+
snapStore,
|
|
1721
|
+
};
|
|
1722
|
+
|
|
1700
1723
|
function provideVatKeeper(vatID) {
|
|
1701
1724
|
insistVatID(vatID);
|
|
1702
1725
|
const found = ephemeral.vatKeepers.get(vatID);
|
|
@@ -1704,30 +1727,36 @@ export default function makeKernelKeeper(
|
|
|
1704
1727
|
return found;
|
|
1705
1728
|
}
|
|
1706
1729
|
assert(kvStore.has(`${vatID}.o.nextID`), `${vatID} was not initialized`);
|
|
1707
|
-
const vk = makeVatKeeper(
|
|
1708
|
-
kvStore,
|
|
1709
|
-
transcriptStore,
|
|
1710
|
-
kernelSlog,
|
|
1711
|
-
vatID,
|
|
1712
|
-
addKernelObject,
|
|
1713
|
-
addKernelPromiseForVat,
|
|
1714
|
-
kernelObjectExists,
|
|
1715
|
-
incrementRefCount,
|
|
1716
|
-
decrementRefCount,
|
|
1717
|
-
getObjectRefCount,
|
|
1718
|
-
setObjectRefCount,
|
|
1719
|
-
getReachableAndVatSlot,
|
|
1720
|
-
addMaybeFreeKref,
|
|
1721
|
-
incStat,
|
|
1722
|
-
decStat,
|
|
1723
|
-
getCrankNumber,
|
|
1724
|
-
scheduleReap,
|
|
1725
|
-
snapStore,
|
|
1726
|
-
);
|
|
1730
|
+
const vk = makeVatKeeper(vatID, kvStore, vatKeeperPowers);
|
|
1727
1731
|
ephemeral.vatKeepers.set(vatID, vk);
|
|
1728
1732
|
return vk;
|
|
1729
1733
|
}
|
|
1730
1734
|
|
|
1735
|
+
/**
|
|
1736
|
+
* Produce an attenuated vatKeeper for slow vat termination (and that
|
|
1737
|
+
* therefore does not insist on liveness, unlike provideVatKeeper).
|
|
1738
|
+
*
|
|
1739
|
+
* @param {string} vatID
|
|
1740
|
+
*/
|
|
1741
|
+
function provideVatUndertaker(vatID) {
|
|
1742
|
+
insistVatID(vatID);
|
|
1743
|
+
const found = ephemeral.vatUndertakers.get(vatID);
|
|
1744
|
+
if (found !== undefined) {
|
|
1745
|
+
return found;
|
|
1746
|
+
}
|
|
1747
|
+
const { deleteCListEntry, deleteSnapshots, deleteTranscripts } =
|
|
1748
|
+
ephemeral.vatKeepers.get(vatID) ||
|
|
1749
|
+
makeVatKeeper(vatID, kvStore, vatKeeperPowers);
|
|
1750
|
+
/** @type {VatUndertaker} */
|
|
1751
|
+
const undertaker = harden({
|
|
1752
|
+
deleteCListEntry,
|
|
1753
|
+
deleteSnapshots,
|
|
1754
|
+
deleteTranscripts,
|
|
1755
|
+
});
|
|
1756
|
+
ephemeral.vatUndertakers.set(vatID, undertaker);
|
|
1757
|
+
return undertaker;
|
|
1758
|
+
}
|
|
1759
|
+
|
|
1731
1760
|
function vatIsAlive(vatID) {
|
|
1732
1761
|
insistVatID(vatID);
|
|
1733
1762
|
return kvStore.has(`${vatID}.o.nextID`) && !terminatedVats.includes(vatID);
|
|
@@ -84,49 +84,53 @@ export function initializeVatState(
|
|
|
84
84
|
}
|
|
85
85
|
|
|
86
86
|
/**
|
|
87
|
-
*
|
|
87
|
+
* @typedef {object} VatKeeperPowers
|
|
88
|
+
* @property {TranscriptStore} transcriptStore Accompanying transcript store, for the transcripts
|
|
89
|
+
* @property {*} kernelSlog
|
|
90
|
+
* @property {*} addKernelObject Kernel function to add a new object to the kernel's mapping tables.
|
|
91
|
+
* @property {*} addKernelPromiseForVat Kernel function to add a new promise to the kernel's mapping tables.
|
|
92
|
+
* @property {(kernelSlot: string) => boolean} kernelObjectExists
|
|
93
|
+
* @property {*} incrementRefCount
|
|
94
|
+
* @property {*} decrementRefCount
|
|
95
|
+
* @property {(kernelSlot: string) => {reachable: number, recognizable: number}} getObjectRefCount
|
|
96
|
+
* @property {(kernelSlot: string, o: { reachable: number, recognizable: number }) => void} setObjectRefCount
|
|
97
|
+
* @property {(vatID: string, kernelSlot: string) => {isReachable: boolean, vatSlot: string}} getReachableAndVatSlot
|
|
98
|
+
* @property {(kernelSlot: string) => void} addMaybeFreeKref
|
|
99
|
+
* @property {*} incStat
|
|
100
|
+
* @property {*} decStat
|
|
101
|
+
* @property {*} getCrankNumber
|
|
102
|
+
* @property {*} scheduleReap
|
|
103
|
+
* @property {SnapStore} snapStore
|
|
104
|
+
*/
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Produce a "vat keeper" for the kernel state of a vat.
|
|
88
108
|
*
|
|
89
|
-
* @param {KVStore} kvStore The keyValue store in which the persistent state will be kept
|
|
90
|
-
* @param {TranscriptStore} transcriptStore Accompanying transcript store, for the transcripts
|
|
91
|
-
* @param {*} kernelSlog
|
|
92
109
|
* @param {string} vatID The vat ID string of the vat in question
|
|
93
|
-
* @param {
|
|
94
|
-
*
|
|
95
|
-
* @param {*} addKernelPromiseForVat Kernel function to add a new promise to the
|
|
96
|
-
* kernel's mapping tables.
|
|
97
|
-
* @param {(kernelSlot: string) => boolean} kernelObjectExists
|
|
98
|
-
* @param {*} incrementRefCount
|
|
99
|
-
* @param {*} decrementRefCount
|
|
100
|
-
* @param {(kernelSlot: string) => {reachable: number, recognizable: number}} getObjectRefCount
|
|
101
|
-
* @param {(kernelSlot: string, o: { reachable: number, recognizable: number }) => void} setObjectRefCount
|
|
102
|
-
* @param {(vatID: string, kernelSlot: string) => {isReachable: boolean, vatSlot: string}} getReachableAndVatSlot
|
|
103
|
-
* @param {(kernelSlot: string) => void} addMaybeFreeKref
|
|
104
|
-
* @param {*} incStat
|
|
105
|
-
* @param {*} decStat
|
|
106
|
-
* @param {*} getCrankNumber
|
|
107
|
-
* @param {*} scheduleReap
|
|
108
|
-
* @param {SnapStore} [snapStore]
|
|
109
|
-
* returns an object to hold and access the kernel's state for the given vat
|
|
110
|
+
* @param {KVStore} kvStore The keyValue store in which the persistent state will be kept
|
|
111
|
+
* @param {VatKeeperPowers} powers
|
|
110
112
|
*/
|
|
111
113
|
export function makeVatKeeper(
|
|
112
|
-
kvStore,
|
|
113
|
-
transcriptStore,
|
|
114
|
-
kernelSlog,
|
|
115
114
|
vatID,
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
115
|
+
kvStore,
|
|
116
|
+
{
|
|
117
|
+
transcriptStore,
|
|
118
|
+
kernelSlog,
|
|
119
|
+
addKernelObject,
|
|
120
|
+
addKernelPromiseForVat,
|
|
121
|
+
kernelObjectExists,
|
|
122
|
+
incrementRefCount,
|
|
123
|
+
decrementRefCount,
|
|
124
|
+
getObjectRefCount,
|
|
125
|
+
setObjectRefCount,
|
|
126
|
+
getReachableAndVatSlot,
|
|
127
|
+
addMaybeFreeKref,
|
|
128
|
+
incStat,
|
|
129
|
+
decStat,
|
|
130
|
+
getCrankNumber,
|
|
131
|
+
scheduleReap,
|
|
132
|
+
snapStore,
|
|
133
|
+
},
|
|
130
134
|
) {
|
|
131
135
|
insistVatID(vatID);
|
|
132
136
|
|
package/tools/bootstrap-relay.js
CHANGED
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Source code for a bootstrap vat that runs blockchain behaviors (such as
|
|
3
|
+
* bridge vat integration) and exposes reflective methods for use in testing.
|
|
4
|
+
*
|
|
5
|
+
* TODO: Build from ./vat-puppet.js makeReflectionMethods
|
|
6
|
+
* and share code with packages/vats/tools/vat-reflective-chain-bootstrap.js
|
|
7
|
+
* (which basically extends this for better [mock] blockchain integration).
|
|
8
|
+
*/
|
|
9
|
+
|
|
1
10
|
import { Fail, q } from '@endo/errors';
|
|
2
11
|
import { objectMap } from '@agoric/internal';
|
|
3
12
|
import { Far, E } from '@endo/far';
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Source code for a vat that exposes reflective methods for use in
|
|
3
|
+
* testing.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { Fail, q } from '@endo/errors';
|
|
7
|
+
import { Far, E } from '@endo/far';
|
|
8
|
+
import { makePromiseKit } from '@endo/promise-kit';
|
|
9
|
+
import { objectMap } from '@agoric/internal';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @callback Die
|
|
13
|
+
* @param {unknown} completion
|
|
14
|
+
* @param {[target: unknown, method: string, ...args: unknown[]]} [finalSend]
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @typedef {Array<[name: string, ...args: unknown[]]>} CallLog
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @param {import('@agoric/swingset-vat').VatPowers} vatPowers
|
|
23
|
+
* @param {import('@agoric/vat-data').Baggage} baggage
|
|
24
|
+
*/
|
|
25
|
+
export const makeReflectionMethods = (vatPowers, baggage) => {
|
|
26
|
+
let baggageHoldCount = 0;
|
|
27
|
+
/** @type {Map<object, CallLog>} */
|
|
28
|
+
const callLogsByRemotable = new Map();
|
|
29
|
+
const heldInHeap = [];
|
|
30
|
+
const send = (target, method, ...args) => E(target)[method](...args);
|
|
31
|
+
const makeSpy = (value, name, callLog) => {
|
|
32
|
+
const spyName = `get ${name}`;
|
|
33
|
+
const spy = {
|
|
34
|
+
[spyName](...args) {
|
|
35
|
+
callLog.push([name, ...args]);
|
|
36
|
+
return value;
|
|
37
|
+
},
|
|
38
|
+
}[spyName];
|
|
39
|
+
return spy;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
return {
|
|
43
|
+
/** @type {Die} */
|
|
44
|
+
dieHappy: (completion, finalSend) => {
|
|
45
|
+
vatPowers.exitVat(completion);
|
|
46
|
+
if (finalSend) send(...finalSend);
|
|
47
|
+
},
|
|
48
|
+
|
|
49
|
+
/** @type {Die} */
|
|
50
|
+
dieSad: (reason, finalSend) => {
|
|
51
|
+
vatPowers.exitVatWithFailure(/** @type {Error} */ (reason));
|
|
52
|
+
if (finalSend) send(...finalSend);
|
|
53
|
+
},
|
|
54
|
+
|
|
55
|
+
holdInBaggage: (...values) => {
|
|
56
|
+
for (const value of values) {
|
|
57
|
+
baggage.init(`held-${baggageHoldCount}`, value);
|
|
58
|
+
baggageHoldCount += 1;
|
|
59
|
+
}
|
|
60
|
+
return baggageHoldCount;
|
|
61
|
+
},
|
|
62
|
+
|
|
63
|
+
holdInHeap: (...values) => heldInHeap.push(...values),
|
|
64
|
+
|
|
65
|
+
makePromiseKit: () => {
|
|
66
|
+
const { promise, ...resolverMethods } = makePromiseKit();
|
|
67
|
+
void promise.catch(() => {});
|
|
68
|
+
const resolver = Far('resolver', resolverMethods);
|
|
69
|
+
return harden({ promise, resolver });
|
|
70
|
+
},
|
|
71
|
+
|
|
72
|
+
makeUnsettledPromise() {
|
|
73
|
+
const { promise } = makePromiseKit();
|
|
74
|
+
void promise.catch(() => {});
|
|
75
|
+
return promise;
|
|
76
|
+
},
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Returns a remotable with methods that return provided values. Invocations
|
|
80
|
+
* of those methods and their arguments are captured for later retrieval by
|
|
81
|
+
* `getCallLogForRemotable`.
|
|
82
|
+
*
|
|
83
|
+
* @param {string} [label]
|
|
84
|
+
* @param {Record<string, any>} [fields]
|
|
85
|
+
*/
|
|
86
|
+
makeRemotable: (label = 'Remotable', fields = {}) => {
|
|
87
|
+
/** @type {CallLog} */
|
|
88
|
+
const callLog = [];
|
|
89
|
+
const methods = objectMap(fields, (value, name) =>
|
|
90
|
+
makeSpy(value, name, callLog),
|
|
91
|
+
);
|
|
92
|
+
const remotable = Far(label, { ...methods });
|
|
93
|
+
callLogsByRemotable.set(remotable, callLog);
|
|
94
|
+
return remotable;
|
|
95
|
+
},
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* @param {object} remotable
|
|
99
|
+
* @returns {CallLog}
|
|
100
|
+
*/
|
|
101
|
+
getCallLogForRemotable: remotable =>
|
|
102
|
+
callLogsByRemotable.get(remotable) ||
|
|
103
|
+
Fail`unknown remotable ${q(remotable)}`,
|
|
104
|
+
};
|
|
105
|
+
};
|
|
106
|
+
harden(makeReflectionMethods);
|
|
107
|
+
|
|
108
|
+
export function buildRootObject(vatPowers, _vatParameters, baggage) {
|
|
109
|
+
const methods = makeReflectionMethods(vatPowers, baggage);
|
|
110
|
+
return Far('root', methods);
|
|
111
|
+
}
|