@agoric/swingset-vat 0.32.3-dev-9bb987f.0.9bb987f → 0.32.3-dev-8d12ead.0.8d12ead
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 +12 -12
- package/src/kernel/kernel.js +96 -60
- package/src/kernel/state/kernelKeeper.js +12 -3
- package/src/kernel/vat-warehouse.js +1 -0
- package/tools/bootstrap-relay.js +85 -67
- package/tools/vat-puppet.js +124 -13
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-8d12ead.0.8d12ead",
|
|
4
4
|
"description": "Vat/Container Launcher",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.js",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"lint:eslint": "yarn run -T eslint ."
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
|
-
"@agoric/xsnap": "0.14.3-dev-
|
|
25
|
+
"@agoric/xsnap": "0.14.3-dev-8d12ead.0.8d12ead",
|
|
26
26
|
"@types/better-sqlite3": "^7.6.13",
|
|
27
27
|
"@types/microtime": "^2.1.0",
|
|
28
28
|
"@types/tmp": "^0.2.0",
|
|
@@ -30,15 +30,15 @@
|
|
|
30
30
|
"ava": "^5.3.0"
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@agoric/internal": "0.3.3-dev-
|
|
34
|
-
"@agoric/kmarshal": "0.1.1-dev-
|
|
35
|
-
"@agoric/store": "0.9.3-dev-
|
|
36
|
-
"@agoric/swing-store": "0.9.2-dev-
|
|
37
|
-
"@agoric/swingset-liveslots": "0.10.3-dev-
|
|
38
|
-
"@agoric/swingset-xsnap-supervisor": "0.10.3-dev-
|
|
39
|
-
"@agoric/time": "0.3.3-dev-
|
|
40
|
-
"@agoric/vat-data": "0.5.3-dev-
|
|
41
|
-
"@agoric/xsnap-lockdown": "0.14.1-dev-
|
|
33
|
+
"@agoric/internal": "0.3.3-dev-8d12ead.0.8d12ead",
|
|
34
|
+
"@agoric/kmarshal": "0.1.1-dev-8d12ead.0.8d12ead",
|
|
35
|
+
"@agoric/store": "0.9.3-dev-8d12ead.0.8d12ead",
|
|
36
|
+
"@agoric/swing-store": "0.9.2-dev-8d12ead.0.8d12ead",
|
|
37
|
+
"@agoric/swingset-liveslots": "0.10.3-dev-8d12ead.0.8d12ead",
|
|
38
|
+
"@agoric/swingset-xsnap-supervisor": "0.10.3-dev-8d12ead.0.8d12ead",
|
|
39
|
+
"@agoric/time": "0.3.3-dev-8d12ead.0.8d12ead",
|
|
40
|
+
"@agoric/vat-data": "0.5.3-dev-8d12ead.0.8d12ead",
|
|
41
|
+
"@agoric/xsnap-lockdown": "0.14.1-dev-8d12ead.0.8d12ead",
|
|
42
42
|
"@endo/base64": "^1.0.12",
|
|
43
43
|
"@endo/bundle-source": "^4.1.2",
|
|
44
44
|
"@endo/captp": "^4.4.8",
|
|
@@ -105,5 +105,5 @@
|
|
|
105
105
|
"typeCoverage": {
|
|
106
106
|
"atLeast": 76.59
|
|
107
107
|
},
|
|
108
|
-
"gitHead": "
|
|
108
|
+
"gitHead": "8d12ead740e840027e28a0b6be3bf8f125adc799"
|
|
109
109
|
}
|
package/src/kernel/kernel.js
CHANGED
|
@@ -43,8 +43,41 @@ import { makeVatAdminHooks } from './vat-admin-hooks.js';
|
|
|
43
43
|
/**
|
|
44
44
|
* @import {MeterConsumption, VatDeliveryObject, VatDeliveryResult, VatSyscallObject, VatSyscallResult} from '@agoric/swingset-liveslots';
|
|
45
45
|
* @import {PolicyInputCleanupCounts} from '../types-external.js';
|
|
46
|
+
* @import {
|
|
47
|
+
* VatID,
|
|
48
|
+
* InternalDynamicVatOptions,
|
|
49
|
+
* RunQueueEvent,
|
|
50
|
+
* RunQueueEventNotify,
|
|
51
|
+
* RunQueueEventSend,
|
|
52
|
+
* RunQueueEventCreateVat,
|
|
53
|
+
* RunQueueEventUpgradeVat,
|
|
54
|
+
* RunQueueEventChangeVatOptions,
|
|
55
|
+
* RunQueueEventStartVat,
|
|
56
|
+
* RunQueueEventDropExports,
|
|
57
|
+
* RunQueueEventRetireExports,
|
|
58
|
+
* RunQueueEventRetireImports,
|
|
59
|
+
* RunQueueEventNegatedGCAction,
|
|
60
|
+
* RunQueueEventBringOutYourDead,
|
|
61
|
+
* RunQueueEventCleanupTerminatedVat,
|
|
62
|
+
* } from '../types-internal.js';
|
|
46
63
|
*/
|
|
47
64
|
|
|
65
|
+
/** @typedef {typeof gcMessageTypes[number]} GcMessageType */
|
|
66
|
+
const gcMessageTypes = /** @type {const} */ ([
|
|
67
|
+
'bringOutYourDead',
|
|
68
|
+
'dropExports',
|
|
69
|
+
'retireExports',
|
|
70
|
+
'retireImports',
|
|
71
|
+
]);
|
|
72
|
+
|
|
73
|
+
/** @typedef {Extract<RunQueueEvent, { type: GcMessageType }>} GcMessage */
|
|
74
|
+
|
|
75
|
+
/** @type {Set<RunQueueEvent['type']>} */
|
|
76
|
+
const gcMessageTypesSet = new Set(gcMessageTypes);
|
|
77
|
+
|
|
78
|
+
/** @type {(message: RunQueueEvent) => message is GcMessage} */
|
|
79
|
+
const isGcMessage = message => gcMessageTypesSet.has(message.type);
|
|
80
|
+
|
|
48
81
|
function abbreviateReplacer(_, arg) {
|
|
49
82
|
if (typeof arg === 'bigint') {
|
|
50
83
|
return Number(arg);
|
|
@@ -1135,33 +1168,43 @@ export default function buildKernel(
|
|
|
1135
1168
|
return results;
|
|
1136
1169
|
}
|
|
1137
1170
|
|
|
1171
|
+
/** @type {(message: RunQueueEvent) => string} */
|
|
1138
1172
|
function legibilizeMessage(message) {
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1173
|
+
switch (message.type) {
|
|
1174
|
+
case 'send': {
|
|
1175
|
+
const msg = message.msg;
|
|
1176
|
+
const [method, argList] = legibilizeMessageArgs(msg.methargs);
|
|
1177
|
+
const result = msg.result ? msg.result : 'null';
|
|
1178
|
+
return `@${message.target} <- ${method}(${argList}) : @${result}`;
|
|
1179
|
+
}
|
|
1180
|
+
case 'notify':
|
|
1181
|
+
return `notify(vatID: ${message.vatID}, kpid: @${message.kpid})`;
|
|
1182
|
+
case 'create-vat': {
|
|
1183
|
+
const optsStr = JSON.stringify(message.dynamicOptions);
|
|
1184
|
+
const vatParametersStr = JSON.stringify(message.vatParameters);
|
|
1185
|
+
// prettier-ignore
|
|
1186
|
+
return `create-vat ${message.vatID} opts: ${optsStr} vatParameters: ${vatParametersStr}`;
|
|
1187
|
+
}
|
|
1188
|
+
case 'upgrade-vat': {
|
|
1189
|
+
const vatParametersStr = JSON.stringify(message.vatParameters);
|
|
1190
|
+
// prettier-ignore
|
|
1191
|
+
return `upgrade-vat ${message.vatID} upgradeID: ${message.upgradeID} vatParameters: ${vatParametersStr}`;
|
|
1192
|
+
}
|
|
1193
|
+
case 'changeVatOptions':
|
|
1194
|
+
// prettier-ignore
|
|
1195
|
+
return `changeVatOptions ${message.vatID} options: ${JSON.stringify(message.options)}`;
|
|
1196
|
+
case 'startVat':
|
|
1197
|
+
case 'cleanup-terminated-vat':
|
|
1198
|
+
case 'negated-gc-action':
|
|
1199
|
+
case 'bringOutYourDead':
|
|
1200
|
+
return `${message.type} ${message.vatID}`;
|
|
1201
|
+
default:
|
|
1202
|
+
if (isGcMessage(message)) {
|
|
1203
|
+
const krefs = message.krefs.map(e => `@${e}`).join(' ');
|
|
1204
|
+
return `${message.type} ${message.vatID} ${krefs}`;
|
|
1205
|
+
}
|
|
1206
|
+
// @ts-expect-error unreachable
|
|
1207
|
+
return `unknown message type ${message.type}`;
|
|
1165
1208
|
}
|
|
1166
1209
|
}
|
|
1167
1210
|
|
|
@@ -1264,27 +1307,6 @@ export default function buildKernel(
|
|
|
1264
1307
|
kernelKeeper.decrementRefCount(message.kpid, `deq|notify`);
|
|
1265
1308
|
}
|
|
1266
1309
|
|
|
1267
|
-
const gcMessages = ['dropExports', 'retireExports', 'retireImports'];
|
|
1268
|
-
|
|
1269
|
-
/**
|
|
1270
|
-
* @typedef { import('../types-internal.js').VatID } VatID
|
|
1271
|
-
* @typedef { import('../types-internal.js').InternalDynamicVatOptions } InternalDynamicVatOptions
|
|
1272
|
-
*
|
|
1273
|
-
* @typedef { import('../types-internal.js').RunQueueEventNotify } RunQueueEventNotify
|
|
1274
|
-
* @typedef { import('../types-internal.js').RunQueueEventSend } RunQueueEventSend
|
|
1275
|
-
* @typedef { import('../types-internal.js').RunQueueEventCreateVat } RunQueueEventCreateVat
|
|
1276
|
-
* @typedef { import('../types-internal.js').RunQueueEventUpgradeVat } RunQueueEventUpgradeVat
|
|
1277
|
-
* @typedef { import('../types-internal.js').RunQueueEventChangeVatOptions } RunQueueEventChangeVatOptions
|
|
1278
|
-
* @typedef { import('../types-internal.js').RunQueueEventStartVat } RunQueueEventStartVat
|
|
1279
|
-
* @typedef { import('../types-internal.js').RunQueueEventDropExports } RunQueueEventDropExports
|
|
1280
|
-
* @typedef { import('../types-internal.js').RunQueueEventRetireExports } RunQueueEventRetireExports
|
|
1281
|
-
* @typedef { import('../types-internal.js').RunQueueEventRetireImports } RunQueueEventRetireImports
|
|
1282
|
-
* @typedef { import('../types-internal.js').RunQueueEventNegatedGCAction } RunQueueEventNegatedGCAction
|
|
1283
|
-
* @typedef { import('../types-internal.js').RunQueueEventBringOutYourDead } RunQueueEventBringOutYourDead
|
|
1284
|
-
* @typedef { import('../types-internal.js').RunQueueEventCleanupTerminatedVat } RunQueueEventCleanupTerminatedVat
|
|
1285
|
-
* @typedef { import('../types-internal.js').RunQueueEvent } RunQueueEvent
|
|
1286
|
-
*/
|
|
1287
|
-
|
|
1288
1310
|
/**
|
|
1289
1311
|
*
|
|
1290
1312
|
* Dispatch one delivery event. Eventually, this will be called in a
|
|
@@ -1344,16 +1366,17 @@ export default function buildKernel(
|
|
|
1344
1366
|
deliverP = processUpgradeVat(message);
|
|
1345
1367
|
} else if (message.type === 'changeVatOptions') {
|
|
1346
1368
|
deliverP = processChangeVatOptions(message);
|
|
1347
|
-
} else if (message.type === '
|
|
1348
|
-
deliverP =
|
|
1369
|
+
} else if (message.type === 'cleanup-terminated-vat') {
|
|
1370
|
+
deliverP = processCleanupTerminatedVat(message);
|
|
1349
1371
|
} else if (message.type === 'negated-gc-action') {
|
|
1350
1372
|
// processGCActionSet pruned some negated actions, but had no GC
|
|
1351
1373
|
// action to perform. Record the DB changes in their own crank.
|
|
1352
|
-
} else if (message.type === '
|
|
1353
|
-
deliverP =
|
|
1354
|
-
} else if (
|
|
1374
|
+
} else if (message.type === 'bringOutYourDead') {
|
|
1375
|
+
deliverP = processBringOutYourDead(message);
|
|
1376
|
+
} else if (isGcMessage(message)) {
|
|
1355
1377
|
deliverP = processGCMessage(message);
|
|
1356
1378
|
} else {
|
|
1379
|
+
// @ts-expect-error unreachable
|
|
1357
1380
|
Fail`unable to process message.type ${message.type}`;
|
|
1358
1381
|
}
|
|
1359
1382
|
|
|
@@ -1369,13 +1392,16 @@ export default function buildKernel(
|
|
|
1369
1392
|
*/
|
|
1370
1393
|
async function processDeliveryMessage(message) {
|
|
1371
1394
|
const messageType = message.type;
|
|
1395
|
+
const messageSummary = legibilizeMessage(message);
|
|
1396
|
+
const crankNum = kernelKeeper.getCrankNumber();
|
|
1397
|
+
|
|
1372
1398
|
kdebug('');
|
|
1373
|
-
|
|
1374
|
-
kdebug(
|
|
1375
|
-
|
|
1399
|
+
kdebug(`processQ crank ${crankNum} ${JSON.stringify(message)}`);
|
|
1400
|
+
kdebug(messageSummary);
|
|
1401
|
+
|
|
1376
1402
|
const finish = kernelSlog.startDuration(['crank-start', 'crank-finish'], {
|
|
1377
1403
|
crankType: 'delivery',
|
|
1378
|
-
crankNum
|
|
1404
|
+
crankNum,
|
|
1379
1405
|
messageType,
|
|
1380
1406
|
message,
|
|
1381
1407
|
});
|
|
@@ -1400,7 +1426,7 @@ export default function buildKernel(
|
|
|
1400
1426
|
|
|
1401
1427
|
kernelKeeper.establishCrankSavepoint('deliver');
|
|
1402
1428
|
const crankResults = await deliverRunQueueEvent(message);
|
|
1403
|
-
|
|
1429
|
+
let didSnapshot = false;
|
|
1404
1430
|
|
|
1405
1431
|
if (message.type === 'cleanup-terminated-vat') {
|
|
1406
1432
|
const { cleanups } = crankResults;
|
|
@@ -1433,7 +1459,7 @@ export default function buildKernel(
|
|
|
1433
1459
|
} else {
|
|
1434
1460
|
const vatID = crankResults.didDelivery;
|
|
1435
1461
|
if (vatID) {
|
|
1436
|
-
await vatWarehouse.maybeSaveSnapshot(vatID);
|
|
1462
|
+
didSnapshot = await vatWarehouse.maybeSaveSnapshot(vatID);
|
|
1437
1463
|
}
|
|
1438
1464
|
}
|
|
1439
1465
|
const { computrons, meterID } = crankResults;
|
|
@@ -1475,8 +1501,18 @@ export default function buildKernel(
|
|
|
1475
1501
|
queueToKref(vatAdminRootKref, method, args, 'logFailure');
|
|
1476
1502
|
}
|
|
1477
1503
|
|
|
1478
|
-
kernelKeeper.processRefcounts();
|
|
1479
|
-
|
|
1504
|
+
const { lostKrefs } = kernelKeeper.processRefcounts();
|
|
1505
|
+
// Lost krefs are expected for GC, but can also occur when a crank is rolled
|
|
1506
|
+
// back.
|
|
1507
|
+
if (lostKrefs.length && !isGcMessage(message)) {
|
|
1508
|
+
const vatID = crankResults.didDelivery;
|
|
1509
|
+
console.log(
|
|
1510
|
+
`⚠️ Ignoring lost krefs from crankNum ${crankNum} ${vatID} ${messageSummary}${didSnapshot ? ' + snapshot' : ''}`,
|
|
1511
|
+
lostKrefs,
|
|
1512
|
+
message,
|
|
1513
|
+
crankResults,
|
|
1514
|
+
);
|
|
1515
|
+
}
|
|
1480
1516
|
kernelKeeper.incrementCrankNumber();
|
|
1481
1517
|
const { crankhash, activityhash } = kernelKeeper.emitCrankHashes();
|
|
1482
1518
|
finish({
|
|
@@ -221,12 +221,12 @@ export const getAllDynamicVats = getRequired => {
|
|
|
221
221
|
const getObjectReferenceCount = (kvStore, kref) => {
|
|
222
222
|
const data = kvStore.get(`${kref}.refCount`);
|
|
223
223
|
if (!data) {
|
|
224
|
-
return { reachable: 0, recognizable: 0 };
|
|
224
|
+
return { exists: false, reachable: 0, recognizable: 0 };
|
|
225
225
|
}
|
|
226
226
|
const [reachable, recognizable] = commaSplit(data).map(Number);
|
|
227
227
|
reachable <= recognizable ||
|
|
228
228
|
Fail`refmismatch(get) ${kref} ${reachable},${recognizable}`;
|
|
229
|
-
return { reachable, recognizable };
|
|
229
|
+
return { exists: true, reachable, recognizable };
|
|
230
230
|
};
|
|
231
231
|
|
|
232
232
|
const setObjectReferenceCount = (kvStore, kref, counts) => {
|
|
@@ -1609,12 +1609,17 @@ export default function makeKernelKeeper(
|
|
|
1609
1609
|
// leaving work for the next delivery.
|
|
1610
1610
|
|
|
1611
1611
|
function processRefcounts() {
|
|
1612
|
+
const lostKrefs = [];
|
|
1612
1613
|
if (enableKernelGC) {
|
|
1613
1614
|
const actions = new Set();
|
|
1614
1615
|
for (const kref of maybeFreeKrefs.values()) {
|
|
1615
1616
|
const { type } = parseKernelSlot(kref);
|
|
1616
1617
|
if (type === 'promise') {
|
|
1617
1618
|
const kpid = kref;
|
|
1619
|
+
if (!hasKernelPromise(kpid)) {
|
|
1620
|
+
lostKrefs.push(kref);
|
|
1621
|
+
continue;
|
|
1622
|
+
}
|
|
1618
1623
|
const kp = getKernelPromise(kpid);
|
|
1619
1624
|
if (kp.refCount === 0) {
|
|
1620
1625
|
let idx = 0;
|
|
@@ -1633,7 +1638,10 @@ export default function makeKernelKeeper(
|
|
|
1633
1638
|
}
|
|
1634
1639
|
|
|
1635
1640
|
if (type === 'object') {
|
|
1636
|
-
const { reachable, recognizable } = getObjectRefCount(kref);
|
|
1641
|
+
const { exists, reachable, recognizable } = getObjectRefCount(kref);
|
|
1642
|
+
if (!exists) {
|
|
1643
|
+
lostKrefs.push(kref);
|
|
1644
|
+
}
|
|
1637
1645
|
if (reachable === 0) {
|
|
1638
1646
|
// We avoid ownerOfKernelObject(), which will report
|
|
1639
1647
|
// 'undefined' if the owner is dead (and being slowly
|
|
@@ -1701,6 +1709,7 @@ export default function makeKernelKeeper(
|
|
|
1701
1709
|
addGCActions([...actions]);
|
|
1702
1710
|
}
|
|
1703
1711
|
maybeFreeKrefs.clear();
|
|
1712
|
+
return { lostKrefs };
|
|
1704
1713
|
}
|
|
1705
1714
|
|
|
1706
1715
|
function createVatState(vatID, source, options) {
|
|
@@ -570,6 +570,7 @@ export function makeVatWarehouse({
|
|
|
570
570
|
*
|
|
571
571
|
* @param {VatID} vatID
|
|
572
572
|
* @param {number} [minDeliveryCount]
|
|
573
|
+
* @returns {Promise<boolean>} whether a snapshot was saved
|
|
573
574
|
*/
|
|
574
575
|
async function maybeSaveSnapshot(vatID, minDeliveryCount = snapshotInterval) {
|
|
575
576
|
kernelKeeper.vatIsAlive(vatID) || Fail`${q(vatID)}: not alive`;
|
package/tools/bootstrap-relay.js
CHANGED
|
@@ -2,30 +2,45 @@
|
|
|
2
2
|
* @file Source code for a bootstrap vat that runs blockchain behaviors (such as
|
|
3
3
|
* bridge vat integration) and exposes reflective methods for use in testing.
|
|
4
4
|
*
|
|
5
|
-
* TODO:
|
|
6
|
-
* and share code with packages/vats/tools/vat-reflective-chain-bootstrap.js
|
|
5
|
+
* TODO: Share code with packages/vats/tools/bootstrap-chain-reflective.js
|
|
7
6
|
* (which basically extends this for better [mock] blockchain integration).
|
|
8
7
|
*/
|
|
9
8
|
|
|
10
9
|
import { Fail, q } from '@endo/errors';
|
|
11
|
-
import { objectMap } from '@agoric/internal';
|
|
12
10
|
import { Far, E } from '@endo/far';
|
|
13
|
-
import { makePromiseKit } from '@endo/promise-kit';
|
|
14
11
|
import { buildManualTimer } from './manual-timer.js';
|
|
12
|
+
import { makeReflectionMethods } from './vat-puppet.js';
|
|
15
13
|
|
|
16
|
-
|
|
17
|
-
|
|
14
|
+
/** @import { CreateVatResults } from '../src/types-external.js' */
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* @typedef {{ root: object; incarnationNumber?: number }} VatRecord
|
|
18
|
+
* @typedef {VatRecord & CreateVatResults & { bundleCap: unknown }} DynamicVatRecord
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
export const buildRootObject = (vatPowers, bootstrapParameters, baggage) => {
|
|
22
|
+
const manualTimer = buildManualTimer();
|
|
23
|
+
|
|
24
|
+
// Captured/populated by bootstrap.
|
|
18
25
|
let vatAdmin;
|
|
19
|
-
const vatData = new Map();
|
|
20
26
|
const devicesByName = new Map();
|
|
21
|
-
|
|
27
|
+
/** @type {Map<string, VatRecord | DynamicVatRecord>} */
|
|
28
|
+
const vatRecords = new Map();
|
|
29
|
+
|
|
30
|
+
const reflectionMethods = makeReflectionMethods(
|
|
31
|
+
vatPowers,
|
|
32
|
+
baggage,
|
|
33
|
+
bootstrapParameters,
|
|
34
|
+
);
|
|
22
35
|
|
|
23
36
|
return Far('root', {
|
|
37
|
+
...reflectionMethods,
|
|
38
|
+
|
|
24
39
|
bootstrap: async (vats, devices) => {
|
|
25
40
|
vatAdmin = await E(vats.vatAdmin).createVatAdminService(devices.vatAdmin);
|
|
26
41
|
for (const [name, root] of Object.entries(vats)) {
|
|
27
42
|
if (name !== 'vatAdmin') {
|
|
28
|
-
|
|
43
|
+
vatRecords.set(name, { root });
|
|
29
44
|
}
|
|
30
45
|
}
|
|
31
46
|
for (const [name, device] of Object.entries(devices)) {
|
|
@@ -33,78 +48,81 @@ export const buildRootObject = () => {
|
|
|
33
48
|
}
|
|
34
49
|
},
|
|
35
50
|
|
|
36
|
-
getDevice:
|
|
51
|
+
getDevice: deviceName => devicesByName.get(deviceName),
|
|
37
52
|
|
|
38
|
-
|
|
53
|
+
/** @todo Reconcile with packages/vats/tools/bootstrap-chain-reflective.js */
|
|
54
|
+
getTimer: () => manualTimer,
|
|
39
55
|
|
|
40
|
-
|
|
56
|
+
getVatAdmin: () => vatAdmin,
|
|
41
57
|
|
|
42
|
-
|
|
43
|
-
const vat =
|
|
44
|
-
|
|
45
|
-
|
|
58
|
+
getVatAdminNode: vatName => {
|
|
59
|
+
const vat =
|
|
60
|
+
vatRecords.get(vatName) || Fail`unknown vat name: ${q(vatName)}`;
|
|
61
|
+
const { adminNode } = /** @type {DynamicVatRecord} */ (vat);
|
|
62
|
+
return adminNode;
|
|
46
63
|
},
|
|
47
64
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
const bcap = await E(vatAdmin).getNamedBundleCap(bundleCapName);
|
|
53
|
-
const vatOptions = { ...options, vatParameters };
|
|
54
|
-
const { adminNode, root } = await E(vatAdmin).createVat(bcap, vatOptions);
|
|
55
|
-
vatData.set(name, { adminNode, root });
|
|
65
|
+
getVatRoot: vatName => {
|
|
66
|
+
const vat =
|
|
67
|
+
vatRecords.get(vatName) || Fail`unknown vat name: ${q(vatName)}`;
|
|
68
|
+
const { root } = vat;
|
|
56
69
|
return root;
|
|
57
70
|
},
|
|
58
71
|
|
|
59
|
-
upgradeVat: async ({ name, bundleCapName, vatParameters = {} }) => {
|
|
60
|
-
const vat = vatData.get(name) || Fail`unknown vat name: ${q(name)}`;
|
|
61
|
-
const bcap = await E(vatAdmin).getNamedBundleCap(bundleCapName);
|
|
62
|
-
const options = { vatParameters };
|
|
63
|
-
const incarnationNumber = await E(vat.adminNode).upgrade(bcap, options);
|
|
64
|
-
vat.incarnationNumber = incarnationNumber;
|
|
65
|
-
return incarnationNumber;
|
|
66
|
-
},
|
|
67
|
-
|
|
68
72
|
/**
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
* @param {string}
|
|
72
|
-
* @param {
|
|
73
|
+
* @todo Reconcile with packages/vats/tools/bootstrap-chain-reflective.js
|
|
74
|
+
* @param {object} details
|
|
75
|
+
* @param {string} details.name name by which the new vat can be referenced
|
|
76
|
+
* @param {string} [details.bundleCapName] defaults to name
|
|
77
|
+
* @param {Record<string, unknown>} [details.vatParameters]
|
|
78
|
+
* @param {{ vatParameters?: object } & Record<string, unknown>} [vatOptions] for specifying more than vatParameters
|
|
79
|
+
* @returns {Promise<DynamicVatRecord['root']>} root object of the new vat
|
|
73
80
|
*/
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
return remotable;
|
|
89
|
-
},
|
|
90
|
-
|
|
91
|
-
makePromiseKit: () => {
|
|
92
|
-
const { promise, ...resolverMethods } = makePromiseKit();
|
|
93
|
-
const resolver = Far('resolver', resolverMethods);
|
|
94
|
-
return harden({ promise, resolver });
|
|
81
|
+
createVat: async (
|
|
82
|
+
{ name, bundleCapName = name, vatParameters = undefined },
|
|
83
|
+
vatOptions = {},
|
|
84
|
+
) => {
|
|
85
|
+
if (Object.hasOwn(vatOptions, 'vatParameters') && vatParameters) {
|
|
86
|
+
Fail`Conflicting specification of vatParameters`;
|
|
87
|
+
}
|
|
88
|
+
const bundleCap = await E(vatAdmin).getNamedBundleCap(bundleCapName);
|
|
89
|
+
const { root, adminNode } = await E(vatAdmin).createVat(bundleCap, {
|
|
90
|
+
vatParameters: vatParameters || {},
|
|
91
|
+
...vatOptions,
|
|
92
|
+
});
|
|
93
|
+
vatRecords.set(name, { root, adminNode, bundleCap });
|
|
94
|
+
return root;
|
|
95
95
|
},
|
|
96
96
|
|
|
97
97
|
/**
|
|
98
|
-
*
|
|
99
|
-
*
|
|
100
|
-
* @param {
|
|
98
|
+
* @todo Reconcile with packages/vats/tools/bootstrap-chain-reflective.js
|
|
99
|
+
* @param {object} details
|
|
100
|
+
* @param {string} details.name name of the vat to upgrade
|
|
101
|
+
* @param {string} [details.bundleCapName] defaults to the current vat bundle
|
|
102
|
+
* @param {Record<string, unknown>} [details.vatParameters]
|
|
103
|
+
* @param {{ vatParameters?: object } & Record<string, unknown>} [vatOptions] for specifying more than vatParameters
|
|
104
|
+
* @returns {Promise<{ incarnationNumber: number }>} the resulting incarnation number
|
|
101
105
|
*/
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
106
|
+
upgradeVat: async (
|
|
107
|
+
{ name, bundleCapName = undefined, vatParameters = undefined },
|
|
108
|
+
vatOptions = {},
|
|
109
|
+
) => {
|
|
110
|
+
if (Object.hasOwn(vatOptions, 'vatParameters') && vatParameters) {
|
|
111
|
+
Fail`Conflicting specification of vatParameters`;
|
|
112
|
+
}
|
|
113
|
+
const vatRecord =
|
|
114
|
+
/** @type {DynamicVatRecord} */ (vatRecords.get(name)) ||
|
|
115
|
+
Fail`unknown vat name: ${q(name)}`;
|
|
116
|
+
const bundleCap = await (bundleCapName
|
|
117
|
+
? E(vatAdmin).getNamedBundleCap(bundleCapName)
|
|
118
|
+
: vatRecord.bundleCap);
|
|
119
|
+
const options = { vatParameters: vatParameters || {}, ...vatOptions };
|
|
120
|
+
const { incarnationNumber } = await E(vatRecord.adminNode).upgrade(
|
|
121
|
+
bundleCap,
|
|
122
|
+
options,
|
|
123
|
+
);
|
|
124
|
+
vatRecord.incarnationNumber = incarnationNumber;
|
|
125
|
+
return { incarnationNumber };
|
|
108
126
|
},
|
|
109
127
|
|
|
110
128
|
awaitVatObject: async (presence, path = []) => {
|
package/tools/vat-puppet.js
CHANGED
|
@@ -7,6 +7,8 @@ import { Fail, q } from '@endo/errors';
|
|
|
7
7
|
import { Far, E } from '@endo/far';
|
|
8
8
|
import { makePromiseKit } from '@endo/promise-kit';
|
|
9
9
|
import { objectMap } from '@agoric/internal';
|
|
10
|
+
import { M } from '@agoric/store';
|
|
11
|
+
import { prepareExoClass, watchPromise } from '@agoric/vat-data';
|
|
10
12
|
|
|
11
13
|
/**
|
|
12
14
|
* @callback Die
|
|
@@ -14,15 +16,61 @@ import { objectMap } from '@agoric/internal';
|
|
|
14
16
|
* @param {[target: unknown, method: string, ...args: unknown[]]} [finalSend]
|
|
15
17
|
*/
|
|
16
18
|
|
|
19
|
+
/**
|
|
20
|
+
* @callback SetInStore
|
|
21
|
+
* @param {string} key
|
|
22
|
+
* @param {unknown} value
|
|
23
|
+
* @param {{ mode?: 'init' | 'replace' | 'replaceOrInit' }} [options]
|
|
24
|
+
*/
|
|
25
|
+
|
|
17
26
|
/**
|
|
18
27
|
* @typedef {Array<[name: string, ...args: unknown[]]>} CallLog
|
|
19
28
|
*/
|
|
20
29
|
|
|
30
|
+
const PromiseWatcherI = M.interface('PromiseWatcher', {
|
|
31
|
+
onFulfilled: M.call(M.any()).returns(),
|
|
32
|
+
onRejected: M.call(M.any()).returns(),
|
|
33
|
+
getResult: M.call().returns(
|
|
34
|
+
M.or(['unsettled'], [M.or('fulfilled', 'rejected'), M.any()]),
|
|
35
|
+
),
|
|
36
|
+
});
|
|
37
|
+
/** @param {unknown} [remoteTarget] */
|
|
38
|
+
const initPromiseWatcher = remoteTarget =>
|
|
39
|
+
harden({ remoteTarget, result: ['unsettled'] });
|
|
40
|
+
const promiseWatcherMethods = {
|
|
41
|
+
onFulfilled(value) {
|
|
42
|
+
this.state.result = harden(['fulfilled', value]);
|
|
43
|
+
if (!this.state.remoteTarget) return;
|
|
44
|
+
void E(this.state.remoteTarget).onFulfilled(value);
|
|
45
|
+
},
|
|
46
|
+
onRejected(value) {
|
|
47
|
+
this.state.result = harden(['rejected', value]);
|
|
48
|
+
if (!this.state.remoteTarget) return;
|
|
49
|
+
void E(this.state.remoteTarget).onRejected(value);
|
|
50
|
+
},
|
|
51
|
+
getResult() {
|
|
52
|
+
return this.state.result;
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
|
|
21
56
|
/**
|
|
22
57
|
* @param {import('@agoric/swingset-vat').VatPowers} vatPowers
|
|
23
58
|
* @param {import('@agoric/vat-data').Baggage} baggage
|
|
59
|
+
* @param {unknown} [vatParameters]
|
|
24
60
|
*/
|
|
25
|
-
export const makeReflectionMethods = (vatPowers, baggage) => {
|
|
61
|
+
export const makeReflectionMethods = (vatPowers, baggage, vatParameters) => {
|
|
62
|
+
// Avoid a @agoric/swingset-vat -> @agoric/zone -> @agoric/swingset-vat
|
|
63
|
+
// dependency cycle.
|
|
64
|
+
// const zone = makeDurableZone(baggage);
|
|
65
|
+
// const makePromiseWatcher = zone.exoClass(...);
|
|
66
|
+
const makePromiseWatcher = prepareExoClass(
|
|
67
|
+
baggage,
|
|
68
|
+
'PromiseWatcher',
|
|
69
|
+
PromiseWatcherI,
|
|
70
|
+
initPromiseWatcher,
|
|
71
|
+
promiseWatcherMethods,
|
|
72
|
+
);
|
|
73
|
+
|
|
26
74
|
let baggageHoldCount = 0;
|
|
27
75
|
/** @type {Map<object, CallLog>} */
|
|
28
76
|
const callLogsByRemotable = new Map();
|
|
@@ -52,6 +100,8 @@ export const makeReflectionMethods = (vatPowers, baggage) => {
|
|
|
52
100
|
if (finalSend) send(...finalSend);
|
|
53
101
|
},
|
|
54
102
|
|
|
103
|
+
getVatParameters: () => vatParameters,
|
|
104
|
+
|
|
55
105
|
holdInBaggage: (...values) => {
|
|
56
106
|
for (const value of values) {
|
|
57
107
|
baggage.init(`held-${baggageHoldCount}`, value);
|
|
@@ -62,6 +112,25 @@ export const makeReflectionMethods = (vatPowers, baggage) => {
|
|
|
62
112
|
|
|
63
113
|
holdInHeap: (...values) => heldInHeap.push(...values),
|
|
64
114
|
|
|
115
|
+
/** @type {SetInStore} */
|
|
116
|
+
setInBaggage: (key, value, { mode = 'replaceOrInit' } = {}) => {
|
|
117
|
+
const exists = baggage.has(key);
|
|
118
|
+
if (mode === 'init' || (mode === 'replaceOrInit' && !exists)) {
|
|
119
|
+
baggage.init(key, value);
|
|
120
|
+
} else if (mode === 'replace' || (mode === 'replaceOrInit' && exists)) {
|
|
121
|
+
baggage.set(key, value);
|
|
122
|
+
} else {
|
|
123
|
+
Fail`unknown mode ${q(mode)}`;
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
|
|
127
|
+
getFromBaggage: key => baggage.get(key),
|
|
128
|
+
|
|
129
|
+
callFromBaggage: (key, methodName, ...args) => {
|
|
130
|
+
const obj = baggage.get(key);
|
|
131
|
+
void E(obj)[methodName](...args);
|
|
132
|
+
},
|
|
133
|
+
|
|
65
134
|
makePromiseKit: () => {
|
|
66
135
|
const { promise, ...resolverMethods } = makePromiseKit();
|
|
67
136
|
void promise.catch(() => {});
|
|
@@ -69,6 +138,8 @@ export const makeReflectionMethods = (vatPowers, baggage) => {
|
|
|
69
138
|
return harden({ promise, resolver });
|
|
70
139
|
},
|
|
71
140
|
|
|
141
|
+
makeSettledPromise: settlement => Promise.resolve(settlement),
|
|
142
|
+
|
|
72
143
|
makeUnsettledPromise() {
|
|
73
144
|
const { promise } = makePromiseKit();
|
|
74
145
|
void promise.catch(() => {});
|
|
@@ -76,17 +147,18 @@ export const makeReflectionMethods = (vatPowers, baggage) => {
|
|
|
76
147
|
},
|
|
77
148
|
|
|
78
149
|
/**
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
*
|
|
150
|
+
* Return a remotable with a method for each property of an optional object
|
|
151
|
+
* that returns the corresponding property value. Invocations of those
|
|
152
|
+
* methods and their arguments are captured for later retrieval by
|
|
153
|
+
* `getLogForRemotable`.
|
|
82
154
|
*
|
|
83
155
|
* @param {string} [label]
|
|
84
|
-
* @param {Record<string, any>} [
|
|
156
|
+
* @param {Record<string, any>} [methodReturnValues]
|
|
85
157
|
*/
|
|
86
|
-
makeRemotable: (label = 'Remotable',
|
|
158
|
+
makeRemotable: (label = 'Remotable', methodReturnValues = {}) => {
|
|
87
159
|
/** @type {CallLog} */
|
|
88
160
|
const callLog = [];
|
|
89
|
-
const methods = objectMap(
|
|
161
|
+
const methods = objectMap(methodReturnValues, (value, name) =>
|
|
90
162
|
makeSpy(value, name, callLog),
|
|
91
163
|
);
|
|
92
164
|
const remotable = Far(label, { ...methods });
|
|
@@ -95,17 +167,56 @@ export const makeReflectionMethods = (vatPowers, baggage) => {
|
|
|
95
167
|
},
|
|
96
168
|
|
|
97
169
|
/**
|
|
170
|
+
* Return a copy of a remotable's call log.
|
|
171
|
+
*
|
|
98
172
|
* @param {object} remotable
|
|
99
173
|
* @returns {CallLog}
|
|
100
174
|
*/
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
175
|
+
getLogForRemotable: remotable => {
|
|
176
|
+
const callLog =
|
|
177
|
+
callLogsByRemotable.get(remotable) ||
|
|
178
|
+
Fail`unknown remotable ${q(remotable)}`;
|
|
179
|
+
// Return an immutable copy of the mutable original.
|
|
180
|
+
return harden([...callLog]);
|
|
181
|
+
},
|
|
182
|
+
|
|
183
|
+
sendOnly: (target, methodName, ...args) => {
|
|
184
|
+
void E(target)[methodName](...args);
|
|
185
|
+
},
|
|
186
|
+
|
|
187
|
+
throw: message => {
|
|
188
|
+
throw Error(message);
|
|
189
|
+
},
|
|
190
|
+
|
|
191
|
+
watchSettledPromiseByProxy: (settlement, watcher, ...args) => {
|
|
192
|
+
const watcherProxy = makePromiseWatcher(watcher);
|
|
193
|
+
watchPromise(Promise.resolve(settlement), watcherProxy, ...args);
|
|
194
|
+
},
|
|
104
195
|
};
|
|
105
196
|
};
|
|
106
197
|
harden(makeReflectionMethods);
|
|
107
198
|
|
|
108
|
-
export function buildRootObject(vatPowers,
|
|
109
|
-
const methods = makeReflectionMethods(vatPowers, baggage);
|
|
110
|
-
|
|
199
|
+
export function buildRootObject(vatPowers, vatParameters, baggage) {
|
|
200
|
+
const methods = makeReflectionMethods(vatPowers, baggage, vatParameters);
|
|
201
|
+
const rootObject = Far('root', methods);
|
|
202
|
+
|
|
203
|
+
// Invoke specified methods of the new root object *before* returning,
|
|
204
|
+
// supporting use of previous results as top-level arguments by interpreting
|
|
205
|
+
// registered symbols with decimal-integer descriptions as references into an
|
|
206
|
+
// array of previous results.
|
|
207
|
+
const { initialCalls = [] } = vatParameters || {};
|
|
208
|
+
const results = [];
|
|
209
|
+
const decimalIntPatt = /^-?(?:0|[1-9][0-9]*)$/;
|
|
210
|
+
const translateArg = arg => {
|
|
211
|
+
if (typeof arg !== 'symbol' || !Symbol.keyFor(arg)) return arg;
|
|
212
|
+
if (!arg.description?.match(decimalIntPatt)) return arg;
|
|
213
|
+
return results.at(Number(arg.description));
|
|
214
|
+
};
|
|
215
|
+
for (const [methodName, ...args] of initialCalls) {
|
|
216
|
+
const translatedArgs = args.map(translateArg);
|
|
217
|
+
const result = rootObject[methodName](...translatedArgs);
|
|
218
|
+
results.push(result);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
return rootObject;
|
|
111
222
|
}
|