@agoric/swingset-vat 0.33.0-upgrade-18-dev-aaebae4.0 → 0.33.0-upgrade-18-dev-cc4b6b8.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 +31 -31
- package/src/controller/controller.js +4 -10
- package/src/controller/initializeKernel.js +0 -2
- package/src/controller/initializeSwingset.js +71 -62
- package/src/controller/upgradeSwingset.js +50 -37
- package/src/devices/bridge/device-bridge.js +3 -2
- package/src/devices/lib/deviceTools.js +0 -1
- package/src/index.js +1 -0
- package/src/kernel/kernel.js +0 -1
- package/src/kernel/state/kernelKeeper.js +54 -26
- package/src/kernel/state/vatKeeper.js +42 -38
- package/src/kernel/vat-warehouse.js +22 -16
- package/src/supervisors/subprocess-node/supervisor-subprocess-node.js +1 -0
- package/src/typeGuards.js +3 -2
- package/src/vats/comms/delivery.js +0 -2
- package/src/vats/comms/state.js +0 -4
- package/src/vats/timer/vat-timer.js +0 -2
- package/src/vats/vat-admin/vat-vat-admin.js +0 -4
- package/tools/baggage-check.js +0 -2
- package/tools/bootstrap-dvo-test.js +0 -1
- package/tools/bootstrap-relay.js +9 -0
- package/tools/run-utils.js +11 -4
- 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.33.0-upgrade-18-dev-
|
|
3
|
+
"version": "0.33.0-upgrade-18-dev-cc4b6b8.0+cc4b6b8",
|
|
4
4
|
"description": "Vat/Container Launcher",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.js",
|
|
@@ -27,34 +27,33 @@
|
|
|
27
27
|
"@types/yargs-parser": "^21.0.0"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@agoric/internal": "0.4.0-upgrade-18-dev-
|
|
31
|
-
"@agoric/kmarshal": "0.1.1-upgrade-18-dev-
|
|
32
|
-
"@agoric/store": "0.9.3-upgrade-18-dev-
|
|
33
|
-
"@agoric/swing-store": "0.10.0-upgrade-18-dev-
|
|
34
|
-
"@agoric/swingset-liveslots": "0.10.3-upgrade-18-dev-
|
|
35
|
-
"@agoric/swingset-xsnap-supervisor": "0.10.3-upgrade-18-dev-
|
|
36
|
-
"@agoric/time": "0.3.3-upgrade-18-dev-
|
|
37
|
-
"@agoric/vat-data": "0.5.3-upgrade-18-dev-
|
|
38
|
-
"@agoric/xsnap": "0.14.
|
|
39
|
-
"@
|
|
40
|
-
"@endo/
|
|
41
|
-
"@endo/
|
|
42
|
-
"@endo/
|
|
43
|
-
"@endo/
|
|
44
|
-
"@endo/
|
|
45
|
-
"@endo/
|
|
46
|
-
"@endo/
|
|
47
|
-
"@endo/
|
|
48
|
-
"@endo/
|
|
49
|
-
"@endo/
|
|
50
|
-
"@endo/
|
|
51
|
-
"@endo/
|
|
52
|
-
"@endo/
|
|
53
|
-
"@endo/
|
|
54
|
-
"@endo/
|
|
55
|
-
"@endo/
|
|
56
|
-
"@endo/
|
|
57
|
-
"@endo/zip": "^1.0.8",
|
|
30
|
+
"@agoric/internal": "0.4.0-upgrade-18-dev-cc4b6b8.0+cc4b6b8",
|
|
31
|
+
"@agoric/kmarshal": "0.1.1-upgrade-18-dev-cc4b6b8.0+cc4b6b8",
|
|
32
|
+
"@agoric/store": "0.9.3-upgrade-18-dev-cc4b6b8.0+cc4b6b8",
|
|
33
|
+
"@agoric/swing-store": "0.10.0-upgrade-18-dev-cc4b6b8.0+cc4b6b8",
|
|
34
|
+
"@agoric/swingset-liveslots": "0.10.3-upgrade-18-dev-cc4b6b8.0+cc4b6b8",
|
|
35
|
+
"@agoric/swingset-xsnap-supervisor": "0.10.3-upgrade-18-dev-cc4b6b8.0+cc4b6b8",
|
|
36
|
+
"@agoric/time": "0.3.3-upgrade-18-dev-cc4b6b8.0+cc4b6b8",
|
|
37
|
+
"@agoric/vat-data": "0.5.3-upgrade-18-dev-cc4b6b8.0+cc4b6b8",
|
|
38
|
+
"@agoric/xsnap-lockdown": "0.14.1-upgrade-18-dev-cc4b6b8.0+cc4b6b8",
|
|
39
|
+
"@endo/base64": "^1.0.9",
|
|
40
|
+
"@endo/bundle-source": "^3.5.0",
|
|
41
|
+
"@endo/captp": "^4.4.3",
|
|
42
|
+
"@endo/check-bundle": "^1.0.12",
|
|
43
|
+
"@endo/compartment-mapper": "^1.4.0",
|
|
44
|
+
"@endo/errors": "^1.2.8",
|
|
45
|
+
"@endo/eventual-send": "^1.2.8",
|
|
46
|
+
"@endo/far": "^1.1.9",
|
|
47
|
+
"@endo/import-bundle": "^1.3.2",
|
|
48
|
+
"@endo/init": "^1.1.7",
|
|
49
|
+
"@endo/marshal": "^1.6.2",
|
|
50
|
+
"@endo/nat": "^5.0.13",
|
|
51
|
+
"@endo/pass-style": "^1.4.7",
|
|
52
|
+
"@endo/patterns": "^1.4.7",
|
|
53
|
+
"@endo/promise-kit": "^1.1.8",
|
|
54
|
+
"@endo/ses-ava": "^1.2.8",
|
|
55
|
+
"@endo/stream": "^1.2.8",
|
|
56
|
+
"@endo/zip": "^1.0.9",
|
|
58
57
|
"ansi-styles": "^6.2.1",
|
|
59
58
|
"anylogger": "^0.21.0",
|
|
60
59
|
"better-sqlite3": "^9.1.1",
|
|
@@ -65,6 +64,7 @@
|
|
|
65
64
|
"yargs-parser": "^21.1.1"
|
|
66
65
|
},
|
|
67
66
|
"peerDependencies": {
|
|
67
|
+
"@agoric/xsnap": "^0.14.2",
|
|
68
68
|
"ava": "^5.3.0"
|
|
69
69
|
},
|
|
70
70
|
"files": [
|
|
@@ -101,7 +101,7 @@
|
|
|
101
101
|
"access": "public"
|
|
102
102
|
},
|
|
103
103
|
"typeCoverage": {
|
|
104
|
-
"atLeast":
|
|
104
|
+
"atLeast": 76.28
|
|
105
105
|
},
|
|
106
|
-
"gitHead": "
|
|
106
|
+
"gitHead": "cc4b6b8b8131b14b4e50f45ff01d6018f9ee3995"
|
|
107
107
|
}
|
|
@@ -15,6 +15,7 @@ import { initSwingStore } from '@agoric/swing-store';
|
|
|
15
15
|
|
|
16
16
|
import { mustMatch, M } from '@endo/patterns';
|
|
17
17
|
import { checkBundle } from '@endo/check-bundle/lite.js';
|
|
18
|
+
import { deepCopyJsonable } from '@agoric/internal/src/js-utils.js';
|
|
18
19
|
import engineGC from '@agoric/internal/src/lib-nodejs/engine-gc.js';
|
|
19
20
|
import { startSubprocessWorker } from '@agoric/internal/src/lib-nodejs/spawnSubprocessWorker.js';
|
|
20
21
|
import { waitUntilQuiescent } from '@agoric/internal/src/lib-nodejs/waitUntilQuiescent.js';
|
|
@@ -263,13 +264,6 @@ export async function makeSwingsetController(
|
|
|
263
264
|
|
|
264
265
|
await kernel.start();
|
|
265
266
|
|
|
266
|
-
/**
|
|
267
|
-
* @param {T} x
|
|
268
|
-
* @returns {T}
|
|
269
|
-
* @template T
|
|
270
|
-
*/
|
|
271
|
-
const defensiveCopy = x => JSON.parse(JSON.stringify(x));
|
|
272
|
-
|
|
273
267
|
/**
|
|
274
268
|
* Validate and install a code bundle.
|
|
275
269
|
*
|
|
@@ -304,7 +298,7 @@ export async function makeSwingsetController(
|
|
|
304
298
|
writeSlogObject,
|
|
305
299
|
|
|
306
300
|
dump() {
|
|
307
|
-
return
|
|
301
|
+
return deepCopyJsonable(kernel.dump());
|
|
308
302
|
},
|
|
309
303
|
|
|
310
304
|
verboseDebugMode(flag) {
|
|
@@ -340,11 +334,11 @@ export async function makeSwingsetController(
|
|
|
340
334
|
},
|
|
341
335
|
|
|
342
336
|
getStats() {
|
|
343
|
-
return
|
|
337
|
+
return deepCopyJsonable(kernel.getStats());
|
|
344
338
|
},
|
|
345
339
|
|
|
346
340
|
getStatus() {
|
|
347
|
-
return
|
|
341
|
+
return deepCopyJsonable(kernel.getStatus());
|
|
348
342
|
},
|
|
349
343
|
|
|
350
344
|
getActivityhash() {
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
import fs from 'fs';
|
|
3
3
|
import path from 'path';
|
|
4
4
|
|
|
5
|
-
import { assert, Fail } from '@endo/errors';
|
|
6
|
-
import { makeTracer } from '@agoric/internal';
|
|
5
|
+
import { assert, b, Fail } from '@endo/errors';
|
|
6
|
+
import { deepCopyJsonable, makeTracer } from '@agoric/internal';
|
|
7
7
|
import { mustMatch } from '@agoric/store';
|
|
8
8
|
import bundleSource from '@endo/bundle-source';
|
|
9
9
|
import { resolve as resolveModuleSpecifier } from 'import-meta-resolve';
|
|
@@ -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}`);
|
|
@@ -314,7 +323,7 @@ export async function initializeSwingset(
|
|
|
314
323
|
} = runtimeOptions;
|
|
315
324
|
|
|
316
325
|
// copy config so we can safely mess with it even if it's shared or hardened
|
|
317
|
-
config =
|
|
326
|
+
config = deepCopyJsonable(config);
|
|
318
327
|
if (!config.bundles) {
|
|
319
328
|
config.bundles = {};
|
|
320
329
|
}
|
|
@@ -10,13 +10,38 @@ import {
|
|
|
10
10
|
import { enumeratePrefixedKeys } from '../kernel/state/storageHelper.js';
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
|
-
* @import {RunQueueEvent} from '../types-internal.js';
|
|
13
|
+
* @import {ReapDirtThreshold, RunQueueEvent} from '../types-internal.js';
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
/**
|
|
17
|
+
* Parse a string of decimal digits into a number.
|
|
18
|
+
*
|
|
19
|
+
* @param {string} digits
|
|
20
|
+
* @param {string} label
|
|
21
|
+
* @returns {number}
|
|
22
|
+
*/
|
|
23
|
+
const mustParseInt = (digits, label) => {
|
|
24
|
+
assert(
|
|
25
|
+
digits.match(/^\d+$/),
|
|
26
|
+
`expected ${label}=${digits} to be a decimal integer`,
|
|
27
|
+
);
|
|
28
|
+
return Number(digits);
|
|
29
|
+
};
|
|
19
30
|
|
|
31
|
+
/**
|
|
32
|
+
* Called for each vat when upgradeSwingset migrates from v0 to v1.
|
|
33
|
+
*
|
|
34
|
+
* @param {KVStore} kvStore
|
|
35
|
+
* @param {(key: string) => string} getRequired
|
|
36
|
+
* @param {ReapDirtThreshold} defaultReapDirtThreshold
|
|
37
|
+
* @param {string} vatID
|
|
38
|
+
*/
|
|
39
|
+
const upgradeVatV0toV1 = (
|
|
40
|
+
kvStore,
|
|
41
|
+
getRequired,
|
|
42
|
+
defaultReapDirtThreshold,
|
|
43
|
+
vatID,
|
|
44
|
+
) => {
|
|
20
45
|
// schema v0:
|
|
21
46
|
// Each vat has a `vNN.reapInterval` and `vNN.reapCountdown`.
|
|
22
47
|
// vNN.options has a `.reapInterval` property (however it was not
|
|
@@ -33,15 +58,10 @@ const upgradeVatV0toV1 = (kvStore, defaultReapDirtThreshold, vatID) => {
|
|
|
33
58
|
// `defaultReapDirtThreshold`)
|
|
34
59
|
|
|
35
60
|
const reapDirtKey = `${vatID}.reapDirt`;
|
|
36
|
-
|
|
37
|
-
assert(kvStore.has(oldReapIntervalKey), oldReapIntervalKey);
|
|
38
|
-
assert(kvStore.has(oldReapCountdownKey), oldReapCountdownKey);
|
|
39
61
|
assert(!kvStore.has(reapDirtKey), reapDirtKey);
|
|
40
62
|
|
|
41
|
-
const reapIntervalString =
|
|
42
|
-
const reapCountdownString =
|
|
43
|
-
assert(reapIntervalString !== undefined);
|
|
44
|
-
assert(reapCountdownString !== undefined);
|
|
63
|
+
const reapIntervalString = getRequired(oldReapIntervalKey);
|
|
64
|
+
const reapCountdownString = getRequired(oldReapCountdownKey);
|
|
45
65
|
|
|
46
66
|
const intervalIsNever = reapIntervalString === 'never';
|
|
47
67
|
const countdownIsNever = reapCountdownString === 'never';
|
|
@@ -62,8 +82,11 @@ const upgradeVatV0toV1 = (kvStore, defaultReapDirtThreshold, vatID) => {
|
|
|
62
82
|
threshold.never = true;
|
|
63
83
|
} else {
|
|
64
84
|
// deduce delivery count from old countdown values
|
|
65
|
-
const reapInterval =
|
|
66
|
-
const reapCountdown =
|
|
85
|
+
const reapInterval = mustParseInt(reapIntervalString, oldReapIntervalKey);
|
|
86
|
+
const reapCountdown = mustParseInt(
|
|
87
|
+
reapCountdownString,
|
|
88
|
+
oldReapCountdownKey,
|
|
89
|
+
);
|
|
67
90
|
const deliveries = reapInterval - reapCountdown;
|
|
68
91
|
reapDirt.deliveries = Math.max(deliveries, 0); // just in case
|
|
69
92
|
if (reapInterval !== defaultReapDirtThreshold.deliveries) {
|
|
@@ -76,7 +99,7 @@ const upgradeVatV0toV1 = (kvStore, defaultReapDirtThreshold, vatID) => {
|
|
|
76
99
|
kvStore.set(reapDirtKey, JSON.stringify(reapDirt));
|
|
77
100
|
|
|
78
101
|
// Update options to use the new schema.
|
|
79
|
-
const options = JSON.parse(
|
|
102
|
+
const options = JSON.parse(getRequired(vatOptionsKey));
|
|
80
103
|
delete options.reapInterval;
|
|
81
104
|
options.reapDirtThreshold = threshold;
|
|
82
105
|
kvStore.set(vatOptionsKey, JSON.stringify(options));
|
|
@@ -104,14 +127,11 @@ const upgradeVatV0toV1 = (kvStore, defaultReapDirtThreshold, vatID) => {
|
|
|
104
127
|
*/
|
|
105
128
|
export const upgradeSwingset = kernelStorage => {
|
|
106
129
|
const { kvStore } = kernelStorage;
|
|
107
|
-
let modified = false;
|
|
108
130
|
/** @type {RunQueueEvent[]} */
|
|
109
131
|
const upgradeEvents = [];
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
}
|
|
114
|
-
let version = Number(vstring);
|
|
132
|
+
const vstring = kvStore.get('version');
|
|
133
|
+
const version = Number(vstring) || 0;
|
|
134
|
+
let newVersion;
|
|
115
135
|
|
|
116
136
|
/**
|
|
117
137
|
* @param {string} key
|
|
@@ -166,11 +186,7 @@ export const upgradeSwingset = kernelStorage => {
|
|
|
166
186
|
assert(kvStore.has(oldDefaultReapIntervalKey));
|
|
167
187
|
assert(!kvStore.has(DEFAULT_REAP_DIRT_THRESHOLD_KEY));
|
|
168
188
|
|
|
169
|
-
/**
|
|
170
|
-
* @typedef { import('../types-internal.js').ReapDirtThreshold } ReapDirtThreshold
|
|
171
|
-
*/
|
|
172
|
-
|
|
173
|
-
/** @type ReapDirtThreshold */
|
|
189
|
+
/** @type {ReapDirtThreshold} */
|
|
174
190
|
const threshold = {
|
|
175
191
|
deliveries: 'never',
|
|
176
192
|
gcKrefs: 'never',
|
|
@@ -179,9 +195,7 @@ export const upgradeSwingset = kernelStorage => {
|
|
|
179
195
|
|
|
180
196
|
const oldValue = getRequired(oldDefaultReapIntervalKey);
|
|
181
197
|
if (oldValue !== 'never') {
|
|
182
|
-
|
|
183
|
-
assert.typeof(value, 'number');
|
|
184
|
-
threshold.deliveries = value;
|
|
198
|
+
threshold.deliveries = mustParseInt(oldValue, oldDefaultReapIntervalKey);
|
|
185
199
|
// if BOYD wasn't turned off entirely (eg
|
|
186
200
|
// defaultReapInterval='never', which only happens in unit
|
|
187
201
|
// tests), then pretend we wanted a gcKrefs= threshold all
|
|
@@ -196,22 +210,20 @@ export const upgradeSwingset = kernelStorage => {
|
|
|
196
210
|
|
|
197
211
|
// now upgrade all vats
|
|
198
212
|
for (const [_name, vatID] of getAllStaticVats(kvStore)) {
|
|
199
|
-
upgradeVatV0toV1(kvStore, threshold, vatID);
|
|
213
|
+
upgradeVatV0toV1(kvStore, getRequired, threshold, vatID);
|
|
200
214
|
}
|
|
201
215
|
for (const vatID of getAllDynamicVats(getRequired)) {
|
|
202
|
-
upgradeVatV0toV1(kvStore, threshold, vatID);
|
|
216
|
+
upgradeVatV0toV1(kvStore, getRequired, threshold, vatID);
|
|
203
217
|
}
|
|
204
218
|
|
|
205
|
-
|
|
206
|
-
version = 1;
|
|
219
|
+
newVersion = 1;
|
|
207
220
|
}
|
|
208
221
|
|
|
209
222
|
if (version < 2) {
|
|
210
223
|
// schema v2: add vats.terminated = []
|
|
211
224
|
assert(!kvStore.has('vats.terminated'));
|
|
212
225
|
kvStore.set('vats.terminated', JSON.stringify([]));
|
|
213
|
-
|
|
214
|
-
version = 2;
|
|
226
|
+
newVersion = 2;
|
|
215
227
|
}
|
|
216
228
|
|
|
217
229
|
if (version < 3) {
|
|
@@ -322,10 +334,11 @@ export const upgradeSwingset = kernelStorage => {
|
|
|
322
334
|
}
|
|
323
335
|
|
|
324
336
|
console.log(` - #9039 remediation complete, ${count} notifies to inject`);
|
|
325
|
-
|
|
326
|
-
version = 3;
|
|
337
|
+
newVersion = 3;
|
|
327
338
|
}
|
|
328
339
|
|
|
340
|
+
const modified = newVersion !== undefined;
|
|
341
|
+
|
|
329
342
|
if (upgradeEvents.length) {
|
|
330
343
|
assert(modified);
|
|
331
344
|
// stash until host calls controller.injectQueuedUpgradeEvents()
|
|
@@ -335,7 +348,7 @@ export const upgradeSwingset = kernelStorage => {
|
|
|
335
348
|
}
|
|
336
349
|
|
|
337
350
|
if (modified) {
|
|
338
|
-
kvStore.set('version', `${
|
|
351
|
+
kvStore.set('version', `${newVersion}`);
|
|
339
352
|
}
|
|
340
353
|
return harden({ modified });
|
|
341
354
|
};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Fail } from '@endo/errors';
|
|
2
2
|
import { Far } from '@endo/far';
|
|
3
|
+
import { deepCopyJsonable } from '@agoric/internal/src/js-utils.js';
|
|
3
4
|
|
|
4
5
|
function sanitize(data) {
|
|
5
6
|
if (data === undefined) {
|
|
@@ -8,7 +9,7 @@ function sanitize(data) {
|
|
|
8
9
|
if (data instanceof Error) {
|
|
9
10
|
data = data.stack;
|
|
10
11
|
}
|
|
11
|
-
return
|
|
12
|
+
return deepCopyJsonable(data);
|
|
12
13
|
}
|
|
13
14
|
|
|
14
15
|
/**
|
|
@@ -32,7 +33,7 @@ export function buildRootDeviceNode(tools) {
|
|
|
32
33
|
|
|
33
34
|
function inboundCallback(...args) {
|
|
34
35
|
inboundHandler || Fail`inboundHandler not yet registered`;
|
|
35
|
-
const safeArgs =
|
|
36
|
+
const safeArgs = deepCopyJsonable(args);
|
|
36
37
|
try {
|
|
37
38
|
SO(inboundHandler).inbound(...harden(safeArgs));
|
|
38
39
|
} catch (e) {
|
|
@@ -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
|
},
|
package/src/index.js
CHANGED
package/src/kernel/kernel.js
CHANGED
|
@@ -1128,7 +1128,6 @@ export default function buildKernel(
|
|
|
1128
1128
|
} else if (message.type === 'changeVatOptions') {
|
|
1129
1129
|
// prettier-ignore
|
|
1130
1130
|
return `changeVatOptions ${message.vatID} options: ${JSON.stringify(message.options)}`;
|
|
1131
|
-
// eslint-disable-next-line no-use-before-define
|
|
1132
1131
|
} else if (gcMessages.includes(message.type)) {
|
|
1133
1132
|
// prettier-ignore
|
|
1134
1133
|
return `${message.type} ${message.vatID} ${message.krefs.map(e=>`@${e}`).join(' ')}`;
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
/* eslint-disable no-use-before-define */
|
|
2
1
|
import { Nat, isNat } from '@endo/nat';
|
|
3
2
|
import { assert, Fail } from '@endo/errors';
|
|
4
3
|
import {
|
|
@@ -43,6 +42,7 @@ const enableKernelGC = true;
|
|
|
43
42
|
* @typedef { import('../../types-external.js').SnapStore } SnapStore
|
|
44
43
|
* @typedef { import('../../types-external.js').TranscriptStore } TranscriptStore
|
|
45
44
|
* @typedef { import('../../types-external.js').VatKeeper } VatKeeper
|
|
45
|
+
* @typedef { Pick<VatKeeper, 'deleteCListEntry' | 'deleteSnapshots' | 'deleteTranscripts'> } VatUndertaker
|
|
46
46
|
* @typedef { import('../../types-internal.js').InternalKernelOptions } InternalKernelOptions
|
|
47
47
|
* @typedef { import('../../types-internal.js').ReapDirtThreshold } ReapDirtThreshold
|
|
48
48
|
* @import {PromiseRecord} from '../../types-internal.js';
|
|
@@ -422,6 +422,8 @@ export default function makeKernelKeeper(
|
|
|
422
422
|
const ephemeral = harden({
|
|
423
423
|
/** @type { Map<string, VatKeeper> } */
|
|
424
424
|
vatKeepers: new Map(),
|
|
425
|
+
/** @type { Map<string, VatUndertaker> } */
|
|
426
|
+
vatUndertakers: new Map(),
|
|
425
427
|
deviceKeepers: new Map(), // deviceID -> deviceKeeper
|
|
426
428
|
});
|
|
427
429
|
|
|
@@ -1044,7 +1046,7 @@ export default function makeKernelKeeper(
|
|
|
1044
1046
|
// first or vref first), and delete the other one in the same
|
|
1045
1047
|
// call, so we don't wind up with half an entry.
|
|
1046
1048
|
|
|
1047
|
-
const
|
|
1049
|
+
const undertaker = provideVatUndertaker(vatID);
|
|
1048
1050
|
const clistPrefix = `${vatID}.c.`;
|
|
1049
1051
|
const exportPrefix = `${clistPrefix}o+`;
|
|
1050
1052
|
const importPrefix = `${clistPrefix}o-`;
|
|
@@ -1092,7 +1094,7 @@ export default function makeKernelKeeper(
|
|
|
1092
1094
|
// drop+retire
|
|
1093
1095
|
const kref = kvStore.get(k) || Fail`getNextKey ensures get`;
|
|
1094
1096
|
const vref = stripPrefix(clistPrefix, k);
|
|
1095
|
-
|
|
1097
|
+
undertaker.deleteCListEntry(kref, vref);
|
|
1096
1098
|
// that will also delete both db keys
|
|
1097
1099
|
work.imports += 1;
|
|
1098
1100
|
remaining -= 1;
|
|
@@ -1109,7 +1111,7 @@ export default function makeKernelKeeper(
|
|
|
1109
1111
|
for (const k of enumeratePrefixedKeys(kvStore, promisePrefix)) {
|
|
1110
1112
|
const kref = kvStore.get(k) || Fail`getNextKey ensures get`;
|
|
1111
1113
|
const vref = stripPrefix(clistPrefix, k);
|
|
1112
|
-
|
|
1114
|
+
undertaker.deleteCListEntry(kref, vref);
|
|
1113
1115
|
// that will also delete both db keys
|
|
1114
1116
|
work.promises += 1;
|
|
1115
1117
|
remaining -= 1;
|
|
@@ -1131,7 +1133,7 @@ export default function makeKernelKeeper(
|
|
|
1131
1133
|
|
|
1132
1134
|
// this will internally loop through 'budget' deletions
|
|
1133
1135
|
remaining = budget.snapshots ?? budget.default;
|
|
1134
|
-
const dsc =
|
|
1136
|
+
const dsc = undertaker.deleteSnapshots(remaining);
|
|
1135
1137
|
work.snapshots += dsc.cleanups;
|
|
1136
1138
|
remaining -= dsc.cleanups;
|
|
1137
1139
|
if (remaining <= 0) {
|
|
@@ -1140,7 +1142,7 @@ export default function makeKernelKeeper(
|
|
|
1140
1142
|
|
|
1141
1143
|
// same
|
|
1142
1144
|
remaining = budget.transcripts ?? budget.default;
|
|
1143
|
-
const dts =
|
|
1145
|
+
const dts = undertaker.deleteTranscripts(remaining);
|
|
1144
1146
|
work.transcripts += dts.cleanups;
|
|
1145
1147
|
remaining -= dts.cleanups;
|
|
1146
1148
|
// last task, so increment cleanups, but dc.done is authoritative
|
|
@@ -1697,6 +1699,26 @@ export default function makeKernelKeeper(
|
|
|
1697
1699
|
initializeVatState(kvStore, transcriptStore, vatID, source, options);
|
|
1698
1700
|
}
|
|
1699
1701
|
|
|
1702
|
+
/** @type {import('./vatKeeper.js').VatKeeperPowers} */
|
|
1703
|
+
const vatKeeperPowers = {
|
|
1704
|
+
transcriptStore,
|
|
1705
|
+
kernelSlog,
|
|
1706
|
+
addKernelObject,
|
|
1707
|
+
addKernelPromiseForVat,
|
|
1708
|
+
kernelObjectExists,
|
|
1709
|
+
incrementRefCount,
|
|
1710
|
+
decrementRefCount,
|
|
1711
|
+
getObjectRefCount,
|
|
1712
|
+
setObjectRefCount,
|
|
1713
|
+
getReachableAndVatSlot,
|
|
1714
|
+
addMaybeFreeKref,
|
|
1715
|
+
incStat,
|
|
1716
|
+
decStat,
|
|
1717
|
+
getCrankNumber,
|
|
1718
|
+
scheduleReap,
|
|
1719
|
+
snapStore,
|
|
1720
|
+
};
|
|
1721
|
+
|
|
1700
1722
|
function provideVatKeeper(vatID) {
|
|
1701
1723
|
insistVatID(vatID);
|
|
1702
1724
|
const found = ephemeral.vatKeepers.get(vatID);
|
|
@@ -1704,30 +1726,36 @@ export default function makeKernelKeeper(
|
|
|
1704
1726
|
return found;
|
|
1705
1727
|
}
|
|
1706
1728
|
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
|
-
);
|
|
1729
|
+
const vk = makeVatKeeper(vatID, kvStore, vatKeeperPowers);
|
|
1727
1730
|
ephemeral.vatKeepers.set(vatID, vk);
|
|
1728
1731
|
return vk;
|
|
1729
1732
|
}
|
|
1730
1733
|
|
|
1734
|
+
/**
|
|
1735
|
+
* Produce an attenuated vatKeeper for slow vat termination (and that
|
|
1736
|
+
* therefore does not insist on liveness, unlike provideVatKeeper).
|
|
1737
|
+
*
|
|
1738
|
+
* @param {string} vatID
|
|
1739
|
+
*/
|
|
1740
|
+
function provideVatUndertaker(vatID) {
|
|
1741
|
+
insistVatID(vatID);
|
|
1742
|
+
const found = ephemeral.vatUndertakers.get(vatID);
|
|
1743
|
+
if (found !== undefined) {
|
|
1744
|
+
return found;
|
|
1745
|
+
}
|
|
1746
|
+
const { deleteCListEntry, deleteSnapshots, deleteTranscripts } =
|
|
1747
|
+
ephemeral.vatKeepers.get(vatID) ||
|
|
1748
|
+
makeVatKeeper(vatID, kvStore, vatKeeperPowers);
|
|
1749
|
+
/** @type {VatUndertaker} */
|
|
1750
|
+
const undertaker = harden({
|
|
1751
|
+
deleteCListEntry,
|
|
1752
|
+
deleteSnapshots,
|
|
1753
|
+
deleteTranscripts,
|
|
1754
|
+
});
|
|
1755
|
+
ephemeral.vatUndertakers.set(vatID, undertaker);
|
|
1756
|
+
return undertaker;
|
|
1757
|
+
}
|
|
1758
|
+
|
|
1731
1759
|
function vatIsAlive(vatID) {
|
|
1732
1760
|
insistVatID(vatID);
|
|
1733
1761
|
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
|
|
|
@@ -97,6 +97,7 @@ export function makeSyscallSimulator(
|
|
|
97
97
|
deliveryNum,
|
|
98
98
|
transcriptEntry,
|
|
99
99
|
) {
|
|
100
|
+
const context = `anachrophobia in ${vatID} delivery d${deliveryNum}`;
|
|
100
101
|
const syscallsExpected = [...transcriptEntry.sc]; // copy
|
|
101
102
|
const syscallsMade = [];
|
|
102
103
|
// syscallStatus's length will be max(syscallsExpected,
|
|
@@ -107,31 +108,36 @@ export function makeSyscallSimulator(
|
|
|
107
108
|
let replayError; // sticky
|
|
108
109
|
|
|
109
110
|
const explain = () => {
|
|
110
|
-
console.log(
|
|
111
|
+
console.log(
|
|
112
|
+
`anachrophobia strikes ${vatID} delivery d${deliveryNum} syscalls`,
|
|
113
|
+
);
|
|
111
114
|
for (const [idx, status] of syscallStatus.entries()) {
|
|
112
115
|
const expected = syscallsExpected[idx];
|
|
113
116
|
const got = syscallsMade[idx];
|
|
114
117
|
switch (status) {
|
|
115
118
|
case 'ok': {
|
|
116
|
-
console.log(`sc
|
|
119
|
+
console.log(`sc${idx}: ok: ${djson.stringify(got)}`);
|
|
117
120
|
break;
|
|
118
121
|
}
|
|
119
122
|
case 'wrong': {
|
|
120
|
-
console.log(
|
|
121
|
-
|
|
122
|
-
|
|
123
|
+
console.log(
|
|
124
|
+
`
|
|
125
|
+
sc${idx}: WRONG
|
|
126
|
+
expected: ${djson.stringify(expected.s)}
|
|
127
|
+
got : ${djson.stringify(got)}`.trimStart(),
|
|
128
|
+
);
|
|
123
129
|
break;
|
|
124
130
|
}
|
|
125
131
|
case 'extra': {
|
|
126
|
-
console.log(`sc
|
|
132
|
+
console.log(`sc${idx}: EXTRA: ${djson.stringify(got)}`);
|
|
127
133
|
break;
|
|
128
134
|
}
|
|
129
135
|
case 'missing': {
|
|
130
|
-
console.log(`sc
|
|
136
|
+
console.log(`sc${idx}: MISSING: ${djson.stringify(expected.s)}`);
|
|
131
137
|
break;
|
|
132
138
|
}
|
|
133
139
|
default:
|
|
134
|
-
Fail`bad ${status}`;
|
|
140
|
+
Fail`sc${idx}: bad status ${status}`;
|
|
135
141
|
}
|
|
136
142
|
}
|
|
137
143
|
};
|
|
@@ -140,16 +146,16 @@ export function makeSyscallSimulator(
|
|
|
140
146
|
// slog entries have no kernel-translated kso/ksr
|
|
141
147
|
const finish = kernelSlog.syscall(vatID, undefined, vso);
|
|
142
148
|
const expected = syscallsExpected[syscallsMade.length];
|
|
143
|
-
syscallsMade.push(vso);
|
|
149
|
+
const idx = syscallsMade.push(vso) - 1;
|
|
144
150
|
if (!expected) {
|
|
145
151
|
syscallStatus.push('extra');
|
|
146
|
-
const error = Error(
|
|
152
|
+
const error = Error(`${context}: extra syscall at index sc${idx}`);
|
|
147
153
|
replayError ||= error;
|
|
148
154
|
throw error;
|
|
149
155
|
}
|
|
150
156
|
if (!syscallsAreIdentical(expected.s, vso)) {
|
|
151
157
|
syscallStatus.push('wrong');
|
|
152
|
-
const error = Error(
|
|
158
|
+
const error = Error(`${context}: wrong syscall at index sc${idx}`);
|
|
153
159
|
replayError ||= error;
|
|
154
160
|
throw error;
|
|
155
161
|
}
|
|
@@ -159,12 +165,14 @@ export function makeSyscallSimulator(
|
|
|
159
165
|
};
|
|
160
166
|
|
|
161
167
|
const finishSimulation = () => {
|
|
162
|
-
|
|
163
|
-
|
|
168
|
+
const missing = syscallsExpected.length - syscallsMade.length;
|
|
169
|
+
if (missing > 0) {
|
|
164
170
|
for (let i = 0; i < missing; i += 1) {
|
|
165
171
|
syscallStatus.push('missing');
|
|
166
172
|
}
|
|
167
|
-
const error = Error(
|
|
173
|
+
const error = Error(
|
|
174
|
+
`${context}: missing ${missing} syscall(s) at index sc${syscallsMade.length}`,
|
|
175
|
+
);
|
|
168
176
|
replayError ||= error;
|
|
169
177
|
}
|
|
170
178
|
|
|
@@ -389,7 +397,6 @@ export function makeVatWarehouse({
|
|
|
389
397
|
// entriesReplayed, // retval of replayTranscript() above
|
|
390
398
|
// );
|
|
391
399
|
ephemeral.vats.set(vatID, result);
|
|
392
|
-
// eslint-disable-next-line no-use-before-define
|
|
393
400
|
await applyAvailabilityPolicy(vatID);
|
|
394
401
|
return result;
|
|
395
402
|
}
|
|
@@ -596,7 +603,6 @@ export function makeVatWarehouse({
|
|
|
596
603
|
//
|
|
597
604
|
/** @type { KernelDeliveryObject } */
|
|
598
605
|
const kd = harden(['bringOutYourDead']);
|
|
599
|
-
// eslint-disable-next-line no-use-before-define
|
|
600
606
|
const vd = kernelDeliveryToVatDelivery(vatID, kd);
|
|
601
607
|
const vs = kernelSlog.provideVatSlogger(vatID).vatSlog;
|
|
602
608
|
await deliverToVat(vatID, kd, vd, vs);
|
package/src/typeGuards.js
CHANGED
|
@@ -10,10 +10,11 @@ export const ManagerType = M.or(
|
|
|
10
10
|
|
|
11
11
|
const Bundle = M.splitRecord({ moduleType: M.string() });
|
|
12
12
|
|
|
13
|
-
const SwingsetConfigOptions =
|
|
13
|
+
const SwingsetConfigOptions = {
|
|
14
14
|
creationOptions: M.splitRecord({}, { critical: M.boolean() }),
|
|
15
15
|
parameters: M.recordOf(M.string(), M.any()),
|
|
16
|
-
}
|
|
16
|
+
};
|
|
17
|
+
harden(SwingsetConfigOptions);
|
|
17
18
|
|
|
18
19
|
const SwingSetConfigProperties = M.or(
|
|
19
20
|
M.splitRecord({ sourceSpec: M.string() }, SwingsetConfigOptions),
|
package/src/vats/comms/state.js
CHANGED
|
@@ -150,7 +150,6 @@ export function makeState(syscall) {
|
|
|
150
150
|
store.set('r.nextID', '1');
|
|
151
151
|
store.set('initialized', 'true');
|
|
152
152
|
if (controller) {
|
|
153
|
-
// eslint-disable-next-line no-use-before-define
|
|
154
153
|
addMetaObject(controller);
|
|
155
154
|
cdebug(`comms controller is ${controller}`);
|
|
156
155
|
}
|
|
@@ -393,7 +392,6 @@ export function makeState(syscall) {
|
|
|
393
392
|
// the object is unreachable
|
|
394
393
|
|
|
395
394
|
const { owner, isReachable, isRecognizable } =
|
|
396
|
-
// eslint-disable-next-line no-use-before-define
|
|
397
395
|
getOwnerAndStatus(lref);
|
|
398
396
|
if (isReachable) {
|
|
399
397
|
// but the exporter doesn't realize it yet, so schedule a
|
|
@@ -558,7 +556,6 @@ export function makeState(syscall) {
|
|
|
558
556
|
isReachable = isReachableByKernel(lref);
|
|
559
557
|
isRecognizable = !!mapToKernel(lref);
|
|
560
558
|
} else {
|
|
561
|
-
// eslint-disable-next-line no-use-before-define
|
|
562
559
|
const remote = getRemote(owner);
|
|
563
560
|
isReachable = remote.isReachable(lref);
|
|
564
561
|
isRecognizable = !!remote.mapToRemote(lref);
|
|
@@ -794,7 +791,6 @@ export function makeState(syscall) {
|
|
|
794
791
|
insistPromiseIsUnresolved,
|
|
795
792
|
markPromiseAsResolved,
|
|
796
793
|
|
|
797
|
-
// eslint-disable-next-line no-use-before-define
|
|
798
794
|
getRemote,
|
|
799
795
|
addRemote,
|
|
800
796
|
getRemoteIDForName,
|
|
@@ -100,7 +100,6 @@ export function buildRootObject(vatPowers, _vatParameters, baggage) {
|
|
|
100
100
|
// getNotifier: ({ state }) => state.notifier, // XXX RESTORE
|
|
101
101
|
getNotifier: ({ _self }) => Fail`not implemented, see #7234`, // XXX TEMP
|
|
102
102
|
},
|
|
103
|
-
// eslint-disable-next-line no-use-before-define
|
|
104
103
|
{ finish: finishMeter },
|
|
105
104
|
);
|
|
106
105
|
|
|
@@ -120,18 +119,15 @@ export function buildRootObject(vatPowers, _vatParameters, baggage) {
|
|
|
120
119
|
// getNotifier: ({ state }) => state.notifier, // will never fire // XXX RESTORE
|
|
121
120
|
getNotifier: ({ _self }) => Fail`not implemented, see #7234`, // XXX TEMP
|
|
122
121
|
},
|
|
123
|
-
// eslint-disable-next-line no-use-before-define
|
|
124
122
|
{ finish: finishMeter },
|
|
125
123
|
);
|
|
126
124
|
|
|
127
125
|
function finishMeter({ state, self }) {
|
|
128
|
-
// eslint-disable-next-line no-use-before-define
|
|
129
126
|
meterByID.init(
|
|
130
127
|
state.meterID,
|
|
131
128
|
// harden({ meter: self, updater: state.updater }), // XXX RESTORE
|
|
132
129
|
harden({ meter: self }), // XXX TEMP
|
|
133
130
|
);
|
|
134
|
-
// eslint-disable-next-line no-use-before-define
|
|
135
131
|
meterIDByMeter.set(self, state.meterID);
|
|
136
132
|
}
|
|
137
133
|
|
package/tools/baggage-check.js
CHANGED
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';
|
package/tools/run-utils.js
CHANGED
|
@@ -2,12 +2,18 @@ import { Fail, q } from '@endo/errors';
|
|
|
2
2
|
import { kunser } from '@agoric/kmarshal';
|
|
3
3
|
import { makeQueue } from '@endo/stream';
|
|
4
4
|
|
|
5
|
-
/**
|
|
5
|
+
/**
|
|
6
|
+
* @import { ERef } from '@endo/far'
|
|
7
|
+
* @import { RunPolicy } from '../src/types-external.js'
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/** @typedef {{ provideRunPolicy: () => RunPolicy | undefined }} RunHarness */
|
|
6
11
|
|
|
7
12
|
/**
|
|
8
13
|
* @param {import('../src/controller/controller.js').SwingsetController} controller
|
|
14
|
+
* @param {RunHarness} [harness]
|
|
9
15
|
*/
|
|
10
|
-
export const makeRunUtils = controller => {
|
|
16
|
+
export const makeRunUtils = (controller, harness) => {
|
|
11
17
|
const mutex = makeQueue();
|
|
12
18
|
const logRunFailure = reason =>
|
|
13
19
|
console.log('controller.run() failure', reason);
|
|
@@ -17,7 +23,7 @@ export const makeRunUtils = controller => {
|
|
|
17
23
|
* Wait for exclusive access to the controller, then before relinquishing that access,
|
|
18
24
|
* enqueue and process a delivery and return the result.
|
|
19
25
|
*
|
|
20
|
-
* @param {() => ERef<void | ReturnType<controller['queueToVatObject']>>} deliveryThunk
|
|
26
|
+
* @param {() => ERef<void | ReturnType<typeof controller['queueToVatObject']>>} deliveryThunk
|
|
21
27
|
* function for enqueueing a delivery and returning the result kpid (if any)
|
|
22
28
|
* @param {boolean} [voidResult] whether to ignore the result
|
|
23
29
|
* @returns {Promise<any>}
|
|
@@ -25,7 +31,8 @@ export const makeRunUtils = controller => {
|
|
|
25
31
|
const queueAndRun = async (deliveryThunk, voidResult = false) => {
|
|
26
32
|
await mutex.get();
|
|
27
33
|
const kpid = await deliveryThunk();
|
|
28
|
-
const
|
|
34
|
+
const runPolicy = harness && harness.provideRunPolicy();
|
|
35
|
+
const runResultP = controller.run(runPolicy);
|
|
29
36
|
mutex.put(runResultP.catch(logRunFailure));
|
|
30
37
|
await runResultP;
|
|
31
38
|
|
|
@@ -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
|
+
}
|