@agoric/cosmic-swingset 0.41.4-dev-7f8ede3.0 → 0.41.4-dev-85bac81.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 +13 -11
- package/src/chain-main.js +211 -150
- package/src/export-kernel-db.js +2 -1
- package/src/helpers/maybe-unsafe-lockdown.js +8 -0
- package/src/import-kernel-db.js +2 -1
- package/tools/test-kit.js +71 -66
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agoric/cosmic-swingset",
|
|
3
|
-
"version": "0.41.4-dev-
|
|
3
|
+
"version": "0.41.4-dev-85bac81.0+85bac81",
|
|
4
4
|
"description": "Agoric's Cosmos blockchain integration",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -22,18 +22,19 @@
|
|
|
22
22
|
"author": "Agoric",
|
|
23
23
|
"license": "Apache-2.0",
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@agoric/builders": "0.1.1-dev-
|
|
26
|
-
"@agoric/cosmos": "0.34.2-dev-
|
|
27
|
-
"@agoric/deploy-script-support": "0.10.4-dev-
|
|
28
|
-
"@agoric/internal": "0.3.3-dev-
|
|
29
|
-
"@agoric/store": "0.9.3-dev-
|
|
30
|
-
"@agoric/swing-store": "0.9.2-dev-
|
|
31
|
-
"@agoric/swingset-vat": "0.32.3-dev-
|
|
32
|
-
"@agoric/telemetry": "0.6.3-dev-
|
|
33
|
-
"@agoric/vm-config": "0.1.1-dev-
|
|
25
|
+
"@agoric/builders": "0.1.1-dev-85bac81.0+85bac81",
|
|
26
|
+
"@agoric/cosmos": "0.34.2-dev-85bac81.0+85bac81",
|
|
27
|
+
"@agoric/deploy-script-support": "0.10.4-dev-85bac81.0+85bac81",
|
|
28
|
+
"@agoric/internal": "0.3.3-dev-85bac81.0+85bac81",
|
|
29
|
+
"@agoric/store": "0.9.3-dev-85bac81.0+85bac81",
|
|
30
|
+
"@agoric/swing-store": "0.9.2-dev-85bac81.0+85bac81",
|
|
31
|
+
"@agoric/swingset-vat": "0.32.3-dev-85bac81.0+85bac81",
|
|
32
|
+
"@agoric/telemetry": "0.6.3-dev-85bac81.0+85bac81",
|
|
33
|
+
"@agoric/vm-config": "0.1.1-dev-85bac81.0+85bac81",
|
|
34
34
|
"@endo/bundle-source": "^3.5.1",
|
|
35
35
|
"@endo/env-options": "^1.1.8",
|
|
36
36
|
"@endo/errors": "^1.2.9",
|
|
37
|
+
"@endo/eventual-send": "^1.3.0",
|
|
37
38
|
"@endo/far": "^1.1.10",
|
|
38
39
|
"@endo/import-bundle": "^1.3.3",
|
|
39
40
|
"@endo/init": "^1.1.8",
|
|
@@ -47,6 +48,7 @@
|
|
|
47
48
|
"anylogger": "^0.21.0",
|
|
48
49
|
"deterministic-json": "^1.0.5",
|
|
49
50
|
"import-meta-resolve": "^2.2.1",
|
|
51
|
+
"ses": "^1.11.0",
|
|
50
52
|
"tmp": "^0.2.1"
|
|
51
53
|
},
|
|
52
54
|
"devDependencies": {
|
|
@@ -71,5 +73,5 @@
|
|
|
71
73
|
"typeCoverage": {
|
|
72
74
|
"atLeast": 83.4
|
|
73
75
|
},
|
|
74
|
-
"gitHead": "
|
|
76
|
+
"gitHead": "85bac810ac4684978b3fac16cea9af04996cc9f1"
|
|
75
77
|
}
|
package/src/chain-main.js
CHANGED
|
@@ -112,6 +112,28 @@ const validateSwingsetConfig = swingsetConfig => {
|
|
|
112
112
|
Fail`maxVatsOnline must be a positive integer`;
|
|
113
113
|
};
|
|
114
114
|
|
|
115
|
+
/**
|
|
116
|
+
* Capture "port numbers" for communicating with cosmos modules.
|
|
117
|
+
*
|
|
118
|
+
* @param {object} action
|
|
119
|
+
*/
|
|
120
|
+
export const extractPortNums = action => {
|
|
121
|
+
const portNums = {};
|
|
122
|
+
for (const [key, value] of Object.entries(action)) {
|
|
123
|
+
const portAlias = CosmosInitKeyToBridgeId[key];
|
|
124
|
+
if (portAlias) {
|
|
125
|
+
// Use the alias if it exists.
|
|
126
|
+
portNums[portAlias] = value;
|
|
127
|
+
} else if (key.endsWith(PORT_SUFFIX)) {
|
|
128
|
+
// Anything else that ends in the suffix is assumed to be a port
|
|
129
|
+
// number, as described in app.go/cosmosInitAction.
|
|
130
|
+
const portName = key.slice(0, key.length - PORT_SUFFIX.length);
|
|
131
|
+
portNums[portName] = value;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return harden(portNums);
|
|
135
|
+
};
|
|
136
|
+
|
|
115
137
|
/**
|
|
116
138
|
* @template {unknown} [T=unknown]
|
|
117
139
|
* @param {(req: string) => string} call
|
|
@@ -174,88 +196,57 @@ const makePrefixedBridgeStorage = (
|
|
|
174
196
|
});
|
|
175
197
|
};
|
|
176
198
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
) {
|
|
182
|
-
const
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
const cosmosHome = processValue.getFlag(
|
|
191
|
-
'home',
|
|
192
|
-
`${homedir}/.ag-chain-cosmos`,
|
|
199
|
+
/**
|
|
200
|
+
* @param {(req: string) => string} call
|
|
201
|
+
* @param {string} queuePath
|
|
202
|
+
*/
|
|
203
|
+
export const makeQueueStorage = (call, queuePath) => {
|
|
204
|
+
const { kvStore, commit, abort } = makeBufferedStorage(
|
|
205
|
+
makePrefixedBridgeStorage(
|
|
206
|
+
call,
|
|
207
|
+
`${queuePath}.`,
|
|
208
|
+
'setWithoutNotify',
|
|
209
|
+
x => x,
|
|
210
|
+
x => x,
|
|
211
|
+
),
|
|
193
212
|
);
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
// console.log('Have AG_COSMOS', agcc);
|
|
198
|
-
|
|
199
|
-
const portHandlers = {};
|
|
200
|
-
let lastPort = 0;
|
|
201
|
-
function registerPortHandler(portHandler) {
|
|
202
|
-
lastPort += 1;
|
|
203
|
-
const port = lastPort;
|
|
204
|
-
portHandlers[port] = async (...phArgs) =>
|
|
205
|
-
E.resolve(portHandler(...phArgs)).catch(e => {
|
|
206
|
-
console.error('portHandler threw', e);
|
|
207
|
-
throw e;
|
|
208
|
-
});
|
|
209
|
-
return port;
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
function fromGo(port, str, replier) {
|
|
213
|
-
// console.error(`inbound ${port} ${str}`);
|
|
214
|
-
const handler = portHandlers[port];
|
|
215
|
-
if (!handler) {
|
|
216
|
-
replier.reject(`invalid requested port ${port}`);
|
|
217
|
-
return;
|
|
218
|
-
}
|
|
219
|
-
const action = JSON.parse(str);
|
|
220
|
-
const p = Promise.resolve(handler(action));
|
|
221
|
-
void E.when(
|
|
222
|
-
p,
|
|
223
|
-
res => {
|
|
224
|
-
// console.error(`Replying in Node to ${str} with`, res);
|
|
225
|
-
replier.resolve(stringify(res !== undefined ? res : null));
|
|
226
|
-
},
|
|
227
|
-
rej => {
|
|
228
|
-
// console.error(`Rejecting in Node to ${str} with`, rej);
|
|
229
|
-
replier.reject(`${(rej && rej.stack) || rej}`);
|
|
230
|
-
},
|
|
231
|
-
);
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
// Actually run the main ag-chain-cosmos program. Before we start the daemon,
|
|
235
|
-
// there will be a call to nodePort/AG_COSMOS_INIT, otherwise exit.
|
|
236
|
-
const nodePort = registerPortHandler(toSwingSet);
|
|
237
|
-
|
|
238
|
-
// Need to keep the process alive until Go exits.
|
|
239
|
-
whenHellFreezesOver = new Promise(() => {});
|
|
240
|
-
agcc.runAgCosmosDaemon(nodePort, fromGo, [progname, ...args]);
|
|
241
|
-
|
|
242
|
-
/**
|
|
243
|
-
* @type {undefined | {
|
|
244
|
-
* blockHeight: number,
|
|
245
|
-
* exporter?: import('./export-kernel-db.js').StateSyncExporter,
|
|
246
|
-
* exportDir?: string,
|
|
247
|
-
* cleanup?: () => Promise<void>,
|
|
248
|
-
* }}
|
|
249
|
-
*/
|
|
250
|
-
let stateSyncExport;
|
|
251
|
-
|
|
252
|
-
async function discardStateSyncExport() {
|
|
253
|
-
const exportData = stateSyncExport;
|
|
254
|
-
stateSyncExport = undefined;
|
|
255
|
-
await exportData?.exporter?.stop();
|
|
256
|
-
await exportData?.cleanup?.();
|
|
257
|
-
}
|
|
213
|
+
return harden({ ...kvStore, commit, abort });
|
|
214
|
+
};
|
|
258
215
|
|
|
216
|
+
/**
|
|
217
|
+
* @param {any} agcc
|
|
218
|
+
* @param {string} stateDBDir
|
|
219
|
+
* @param {object} options
|
|
220
|
+
* @param {typeof process.env} [options.env]
|
|
221
|
+
* @param {Pick<import('fs'), 'createWriteStream' | 'mkdirSync' | 'renameSync'>} options.fs
|
|
222
|
+
* @param {Pick<import('path'), 'join' | 'resolve'>} options.path
|
|
223
|
+
* @param {import('tmp')} [options.tmp] required to support vatSnapshotArchiveDir/vatTranscriptArchiveDir
|
|
224
|
+
* @param {ReturnType<typeof makeProcessValue>} [options.processValue]
|
|
225
|
+
* @param {() => Promise<void>} [options.readyForCommit]
|
|
226
|
+
* @param {{
|
|
227
|
+
* debugName?: string,
|
|
228
|
+
* slogSender?: ERef<EReturn<typeof makeSlogSender>>,
|
|
229
|
+
* swingStore?: import('@agoric/swing-store').SwingStore,
|
|
230
|
+
* vatconfig?: Parameters<typeof launch>[0]['vatconfig'],
|
|
231
|
+
* }} [options.testingOverrides]
|
|
232
|
+
*/
|
|
233
|
+
export const makeLaunchChain = (
|
|
234
|
+
agcc,
|
|
235
|
+
stateDBDir,
|
|
236
|
+
{
|
|
237
|
+
env = {},
|
|
238
|
+
// eslint-disable-next-line no-shadow
|
|
239
|
+
fs,
|
|
240
|
+
path,
|
|
241
|
+
// eslint-disable-next-line no-shadow
|
|
242
|
+
tmp,
|
|
243
|
+
processValue = makeProcessValue({ env, args: [] }),
|
|
244
|
+
readyForCommit,
|
|
245
|
+
testingOverrides = {},
|
|
246
|
+
},
|
|
247
|
+
) => {
|
|
248
|
+
/** @typedef {[args: Parameters<typeof agcc.send>, ret: ReturnType<typeof agcc.send>]} SavedChainSend */
|
|
249
|
+
/** @type {SavedChainSend[]} */
|
|
259
250
|
let savedChainSends = [];
|
|
260
251
|
|
|
261
252
|
// Send a chain downcall, recording what we sent and received.
|
|
@@ -266,8 +257,10 @@ export default async function main(
|
|
|
266
257
|
}
|
|
267
258
|
|
|
268
259
|
const clearChainSends = async () => {
|
|
269
|
-
//
|
|
270
|
-
|
|
260
|
+
// TODO: Make `readyForCommit` an explicit `launch` input rather than
|
|
261
|
+
// having an overloaded async `clearChainSends` with the expectation that
|
|
262
|
+
// it is called right before `commit()`.
|
|
263
|
+
await readyForCommit?.();
|
|
271
264
|
|
|
272
265
|
const chainSends = savedChainSends;
|
|
273
266
|
savedChainSends = [];
|
|
@@ -282,7 +275,9 @@ export default async function main(
|
|
|
282
275
|
|
|
283
276
|
// Just send all the things we saved.
|
|
284
277
|
while (chainSends.length > 0) {
|
|
285
|
-
const [sendArgs, expectedRet] =
|
|
278
|
+
const [sendArgs, expectedRet] = /** @type {SavedChainSend} */ (
|
|
279
|
+
chainSends.shift()
|
|
280
|
+
);
|
|
286
281
|
const actualRet = agcc.send(...sendArgs);
|
|
287
282
|
|
|
288
283
|
// Enforce that we got back what we expected.
|
|
@@ -296,14 +291,9 @@ export default async function main(
|
|
|
296
291
|
}
|
|
297
292
|
}
|
|
298
293
|
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
// In the past, storagePort could change with every message. It's defined out
|
|
303
|
-
// here so 'sendToChainStorage' can close over the single mutable instance,
|
|
304
|
-
// when we updated the 'portNums.storage' value each time toSwingSet was called.
|
|
305
|
-
async function launchAndInitializeSwingSet(initAction) {
|
|
306
|
-
const { XSNAP_KEEP_SNAPSHOTS, NODE_HEAP_SNAPSHOTS = -1 } = env;
|
|
294
|
+
const launchChain = async initAction => {
|
|
295
|
+
const { XSNAP_KEEP_SNAPSHOTS = '', NODE_HEAP_SNAPSHOTS = '-1' } = env;
|
|
296
|
+
const portNums = extractPortNums(initAction);
|
|
307
297
|
|
|
308
298
|
/** @type {CosmosSwingsetConfig} */
|
|
309
299
|
const swingsetConfig = harden(initAction.resolvedConfig || {});
|
|
@@ -342,20 +332,12 @@ export default async function main(
|
|
|
342
332
|
val => stringify(exportMailbox(val)),
|
|
343
333
|
),
|
|
344
334
|
);
|
|
345
|
-
const
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
`${queuePath}.`,
|
|
350
|
-
'setWithoutNotify',
|
|
351
|
-
x => x,
|
|
352
|
-
x => x,
|
|
353
|
-
),
|
|
354
|
-
);
|
|
355
|
-
return harden({ ...kvStore, commit, abort });
|
|
356
|
-
};
|
|
357
|
-
const actionQueueStorage = makeQueueStorage(STORAGE_PATH.ACTION_QUEUE);
|
|
335
|
+
const actionQueueStorage = makeQueueStorage(
|
|
336
|
+
sendToChainStorage,
|
|
337
|
+
STORAGE_PATH.ACTION_QUEUE,
|
|
338
|
+
);
|
|
358
339
|
const highPriorityQueueStorage = makeQueueStorage(
|
|
340
|
+
sendToChainStorage,
|
|
359
341
|
STORAGE_PATH.HIGH_PRIORITY_QUEUE,
|
|
360
342
|
);
|
|
361
343
|
/**
|
|
@@ -451,20 +433,18 @@ export default async function main(
|
|
|
451
433
|
return pathname;
|
|
452
434
|
};
|
|
453
435
|
|
|
454
|
-
// Delay makeShutdown to override the golang interrupts
|
|
455
|
-
const { registerShutdown } = makeShutdown();
|
|
456
|
-
|
|
457
436
|
const { metricsProvider } = getTelemetryProviders({
|
|
458
437
|
console,
|
|
459
438
|
env,
|
|
460
439
|
serviceName: TELEMETRY_SERVICE_NAME,
|
|
461
440
|
});
|
|
462
441
|
|
|
463
|
-
const slogSender = await
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
442
|
+
const slogSender = await (testingOverrides.slogSender ||
|
|
443
|
+
makeSlogSender({
|
|
444
|
+
stateDir: stateDBDir,
|
|
445
|
+
env,
|
|
446
|
+
serviceName: TELEMETRY_SERVICE_NAME,
|
|
447
|
+
}));
|
|
468
448
|
|
|
469
449
|
const swingStoreTraceFile = processValue.getPath({
|
|
470
450
|
envName: 'SWING_STORE_TRACE',
|
|
@@ -546,19 +526,23 @@ export default async function main(
|
|
|
546
526
|
const s = await launch({
|
|
547
527
|
actionQueueStorage,
|
|
548
528
|
highPriorityQueueStorage,
|
|
549
|
-
|
|
529
|
+
swingStore: testingOverrides.swingStore,
|
|
530
|
+
kernelStateDBDir: testingOverrides.swingStore ? undefined : stateDBDir,
|
|
550
531
|
makeInstallationPublisher,
|
|
551
532
|
mailboxStorage,
|
|
552
533
|
clearChainSends,
|
|
553
534
|
replayChainSends,
|
|
554
535
|
bridgeOutbound: doOutboundBridge,
|
|
555
|
-
vatconfig: getVatConfig,
|
|
536
|
+
vatconfig: testingOverrides.vatconfig || getVatConfig,
|
|
556
537
|
argv,
|
|
557
538
|
env,
|
|
558
539
|
verboseBlocks: true,
|
|
559
540
|
metricsProvider,
|
|
541
|
+
debugName: testingOverrides.debugName,
|
|
560
542
|
slogSender,
|
|
561
|
-
swingStoreExportCallback
|
|
543
|
+
swingStoreExportCallback: testingOverrides.swingStore
|
|
544
|
+
? undefined
|
|
545
|
+
: swingStoreExportCallback,
|
|
562
546
|
swingStoreTraceFile,
|
|
563
547
|
keepSnapshots,
|
|
564
548
|
keepTranscripts,
|
|
@@ -567,30 +551,94 @@ export default async function main(
|
|
|
567
551
|
afterCommitCallback,
|
|
568
552
|
swingsetConfig,
|
|
569
553
|
});
|
|
554
|
+
savedChainSends = s.savedChainSends;
|
|
555
|
+
return s;
|
|
556
|
+
};
|
|
570
557
|
|
|
571
|
-
|
|
572
|
-
|
|
558
|
+
return launchChain;
|
|
559
|
+
};
|
|
573
560
|
|
|
574
|
-
|
|
561
|
+
export default async function main(
|
|
562
|
+
progname,
|
|
563
|
+
args,
|
|
564
|
+
{ env, homedir, path = nativePath, agcc },
|
|
565
|
+
) {
|
|
566
|
+
// TODO: use the 'basedir' pattern
|
|
575
567
|
|
|
576
|
-
|
|
577
|
-
await Promise.all([
|
|
578
|
-
interrupted && pendingBlockingSend.then(shutdown),
|
|
579
|
-
discardStateSyncExport(),
|
|
580
|
-
]);
|
|
581
|
-
});
|
|
568
|
+
const processValue = makeProcessValue({ env, args });
|
|
582
569
|
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
}
|
|
588
|
-
|
|
589
|
-
}
|
|
570
|
+
// We try to find the actual cosmos state directory (default=~/.ag-chain-cosmos), which
|
|
571
|
+
// is better than scribbling into the current directory.
|
|
572
|
+
const cosmosHome = processValue.getFlag(
|
|
573
|
+
'home',
|
|
574
|
+
`${homedir}/.ag-chain-cosmos`,
|
|
575
|
+
);
|
|
576
|
+
const stateDBDir = `${cosmosHome}/data/agoric`;
|
|
577
|
+
fs.mkdirSync(stateDBDir, { recursive: true });
|
|
590
578
|
|
|
591
579
|
/** @type {EReturn<typeof launch>['blockingSend'] | undefined} */
|
|
592
580
|
let blockingSend;
|
|
593
581
|
|
|
582
|
+
const portHandlers = {};
|
|
583
|
+
let lastPort = 0;
|
|
584
|
+
function registerPortHandler(portHandler) {
|
|
585
|
+
lastPort += 1;
|
|
586
|
+
const port = lastPort;
|
|
587
|
+
portHandlers[port] = async (...phArgs) =>
|
|
588
|
+
E.resolve(portHandler(...phArgs)).catch(e => {
|
|
589
|
+
console.error('portHandler threw', e);
|
|
590
|
+
throw e;
|
|
591
|
+
});
|
|
592
|
+
return port;
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
function fromGo(port, str, replier) {
|
|
596
|
+
// console.error(`inbound ${port} ${str}`);
|
|
597
|
+
const handler = portHandlers[port];
|
|
598
|
+
if (!handler) {
|
|
599
|
+
replier.reject(`invalid requested port ${port}`);
|
|
600
|
+
return;
|
|
601
|
+
}
|
|
602
|
+
const action = JSON.parse(str);
|
|
603
|
+
const p = Promise.resolve(handler(action));
|
|
604
|
+
void E.when(
|
|
605
|
+
p,
|
|
606
|
+
res => {
|
|
607
|
+
// console.error(`Replying in Node to ${str} with`, res);
|
|
608
|
+
replier.resolve(stringify(res !== undefined ? res : null));
|
|
609
|
+
},
|
|
610
|
+
rej => {
|
|
611
|
+
// console.error(`Rejecting in Node to ${str} with`, rej);
|
|
612
|
+
replier.reject(`${(rej && rej.stack) || rej}`);
|
|
613
|
+
},
|
|
614
|
+
);
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
// Actually run the main ag-chain-cosmos program. Before we start the daemon,
|
|
618
|
+
// there will be a call to nodePort/AG_COSMOS_INIT, otherwise exit.
|
|
619
|
+
const nodePort = registerPortHandler(toSwingSet);
|
|
620
|
+
|
|
621
|
+
// Need to keep the process alive until Go exits.
|
|
622
|
+
whenHellFreezesOver = new Promise(() => {});
|
|
623
|
+
agcc.runAgCosmosDaemon(nodePort, fromGo, [progname, ...args]);
|
|
624
|
+
|
|
625
|
+
/**
|
|
626
|
+
* @type {undefined | {
|
|
627
|
+
* blockHeight: number,
|
|
628
|
+
* exporter?: import('./export-kernel-db.js').StateSyncExporter,
|
|
629
|
+
* exportDir?: string,
|
|
630
|
+
* cleanup?: () => Promise<void>,
|
|
631
|
+
* }}
|
|
632
|
+
*/
|
|
633
|
+
let stateSyncExport;
|
|
634
|
+
|
|
635
|
+
async function discardStateSyncExport() {
|
|
636
|
+
const exportData = stateSyncExport;
|
|
637
|
+
stateSyncExport = undefined;
|
|
638
|
+
await exportData?.exporter?.stop();
|
|
639
|
+
await exportData?.cleanup?.();
|
|
640
|
+
}
|
|
641
|
+
|
|
594
642
|
async function handleSwingStoreExport(blockHeight, request, requestArgs) {
|
|
595
643
|
await null;
|
|
596
644
|
switch (request) {
|
|
@@ -729,6 +777,19 @@ export default async function main(
|
|
|
729
777
|
}
|
|
730
778
|
}
|
|
731
779
|
|
|
780
|
+
const launchChain = makeLaunchChain(agcc, stateDBDir, {
|
|
781
|
+
env,
|
|
782
|
+
fs,
|
|
783
|
+
path,
|
|
784
|
+
tmp,
|
|
785
|
+
processValue,
|
|
786
|
+
readyForCommit: async () =>
|
|
787
|
+
stateSyncExport?.exporter?.onStarted().then(ignore, ignore),
|
|
788
|
+
});
|
|
789
|
+
|
|
790
|
+
/** @type {((obj: object) => void) | undefined} */
|
|
791
|
+
let writeSlogObject;
|
|
792
|
+
|
|
732
793
|
async function toSwingSet(action, _replier) {
|
|
733
794
|
// console.log(`toSwingSet`, action);
|
|
734
795
|
|
|
@@ -736,27 +797,27 @@ export default async function main(
|
|
|
736
797
|
|
|
737
798
|
switch (action.type) {
|
|
738
799
|
case ActionType.AG_COSMOS_INIT: {
|
|
739
|
-
// console.error('got AG_COSMOS_INIT', action);
|
|
740
|
-
|
|
741
800
|
!blockingSend || Fail`Swingset already initialized`;
|
|
742
801
|
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
}
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
802
|
+
const s = await launchChain(action);
|
|
803
|
+
const { blockingSend: sendToSwingset, shutdown } = s;
|
|
804
|
+
({ writeSlogObject } = s);
|
|
805
|
+
|
|
806
|
+
// Wrap `blockingSend` with a spy for supporting clean shutdowns.
|
|
807
|
+
const { registerShutdown } = makeShutdown();
|
|
808
|
+
let pendingBlockingSend = Promise.resolve();
|
|
809
|
+
registerShutdown(async interrupted => {
|
|
810
|
+
await Promise.all([
|
|
811
|
+
interrupted && pendingBlockingSend.then(shutdown),
|
|
812
|
+
discardStateSyncExport(),
|
|
813
|
+
]);
|
|
814
|
+
});
|
|
815
|
+
// eslint-disable-next-line no-shadow
|
|
816
|
+
blockingSend = async action => {
|
|
817
|
+
const result = sendToSwingset(action);
|
|
818
|
+
pendingBlockingSend = Promise.resolve(result).then(ignore, ignore);
|
|
819
|
+
return result;
|
|
820
|
+
};
|
|
760
821
|
|
|
761
822
|
return blockingSend(action);
|
|
762
823
|
}
|
package/src/export-kernel-db.js
CHANGED
package/src/import-kernel-db.js
CHANGED
package/tools/test-kit.js
CHANGED
|
@@ -1,27 +1,30 @@
|
|
|
1
1
|
/* eslint-env node */
|
|
2
2
|
|
|
3
|
+
import fs from 'node:fs';
|
|
3
4
|
import * as fsPromises from 'node:fs/promises';
|
|
4
|
-
import
|
|
5
|
+
import nativePath from 'node:path';
|
|
6
|
+
|
|
7
|
+
import tmp from 'tmp';
|
|
5
8
|
import { Fail } from '@endo/errors';
|
|
9
|
+
|
|
6
10
|
import {
|
|
7
11
|
SwingsetMessageType,
|
|
8
12
|
QueuedActionType,
|
|
9
13
|
} from '@agoric/internal/src/action-types.js';
|
|
10
|
-
import
|
|
14
|
+
import * as STORAGE_PATH from '@agoric/internal/src/chain-storage-paths.js';
|
|
11
15
|
import { deepCopyJsonable } from '@agoric/internal/src/js-utils.js';
|
|
12
16
|
import { initSwingStore } from '@agoric/swing-store';
|
|
13
|
-
|
|
14
|
-
import { launch } from '../src/launch-chain.js';
|
|
15
|
-
import { DEFAULT_SIM_SWINGSET_PARAMS } from '../src/sim-params.js';
|
|
17
|
+
|
|
16
18
|
import {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
extractPortNums,
|
|
20
|
+
makeLaunchChain,
|
|
21
|
+
makeQueueStorage,
|
|
22
|
+
} from '../src/chain-main.js';
|
|
23
|
+
import { DEFAULT_SIM_SWINGSET_PARAMS } from '../src/sim-params.js';
|
|
24
|
+
import { makeQueue } from '../src/helpers/make-queue.js';
|
|
21
25
|
|
|
22
26
|
/** @import { BlockInfo, InitMsg } from '@agoric/internal/src/chain-utils.js' */
|
|
23
|
-
/** @import {
|
|
24
|
-
/** @import { KVStore } from '../src/helpers/bufferedStorage.js' */
|
|
27
|
+
/** @import { ManagerType, SwingSetConfig } from '@agoric/swingset-vat' */
|
|
25
28
|
/** @import { InboundQueue } from '../src/launch-chain.js'; */
|
|
26
29
|
|
|
27
30
|
/**
|
|
@@ -36,30 +39,28 @@ const stripUndefined = obj =>
|
|
|
36
39
|
);
|
|
37
40
|
|
|
38
41
|
/** @type {InitMsg} */
|
|
39
|
-
export const defaultInitMessage = harden(
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
supplyCoins: [],
|
|
42
|
+
export const defaultInitMessage = harden({
|
|
43
|
+
type: SwingsetMessageType.AG_COSMOS_INIT,
|
|
44
|
+
blockHeight: 100,
|
|
45
|
+
blockTime: Math.floor(Date.parse('2020-01-01T00:00Z') / 1000),
|
|
46
|
+
chainID: 'localtest',
|
|
47
|
+
params: DEFAULT_SIM_SWINGSET_PARAMS,
|
|
48
|
+
supplyCoins: [],
|
|
47
49
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
);
|
|
50
|
+
// cosmos-sdk module port mappings are generally ignored in testing, but
|
|
51
|
+
// relevant in live blockchains.
|
|
52
|
+
// Include them with unpredictable values.
|
|
53
|
+
...Object.fromEntries(
|
|
54
|
+
Object.entries({
|
|
55
|
+
storagePort: 0,
|
|
56
|
+
swingsetPort: 0,
|
|
57
|
+
vbankPort: 0,
|
|
58
|
+
vibcPort: 0,
|
|
59
|
+
})
|
|
60
|
+
.sort(() => Math.random() - 0.5)
|
|
61
|
+
.map(([name, _zero], i) => [name, i + 1]),
|
|
62
|
+
),
|
|
63
|
+
});
|
|
63
64
|
/** @type {InitMsg} */
|
|
64
65
|
export const defaultBootstrapMessage = harden({
|
|
65
66
|
...deepCopyJsonable(defaultInitMessage),
|
|
@@ -170,7 +171,7 @@ export const makeCosmicSwingsetTestKit = async (
|
|
|
170
171
|
fixupInitMessage,
|
|
171
172
|
fixupConfig,
|
|
172
173
|
slogSender,
|
|
173
|
-
swingsetConfig
|
|
174
|
+
swingsetConfig,
|
|
174
175
|
swingStore,
|
|
175
176
|
vats,
|
|
176
177
|
|
|
@@ -179,7 +180,7 @@ export const makeCosmicSwingsetTestKit = async (
|
|
|
179
180
|
addBootstrapBehaviors,
|
|
180
181
|
bootstrapCoreEvals,
|
|
181
182
|
} = {},
|
|
182
|
-
{ fsp = fsPromises, resolvePath =
|
|
183
|
+
{ fsp = fsPromises, resolvePath = nativePath.resolve } = {},
|
|
183
184
|
) => {
|
|
184
185
|
await null;
|
|
185
186
|
/** @type {SwingSetConfig} */
|
|
@@ -216,42 +217,46 @@ export const makeCosmicSwingsetTestKit = async (
|
|
|
216
217
|
if (initMessage.isBootstrap === undefined && !swingStore) {
|
|
217
218
|
initMessage.isBootstrap = true;
|
|
218
219
|
}
|
|
220
|
+
const portNums = extractPortNums(initMessage);
|
|
221
|
+
const knownPorts = new Map(
|
|
222
|
+
Object.entries(portNums).map(([name, value]) => [value, name]),
|
|
223
|
+
);
|
|
219
224
|
|
|
220
225
|
if (!swingStore) swingStore = initSwingStore(); // in-memory
|
|
221
226
|
const { hostStorage } = swingStore;
|
|
222
227
|
|
|
223
|
-
const
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
const clearChainSends = async () => savedChainSends.splice(0);
|
|
233
|
-
const replayChainSends = (..._args) => {
|
|
234
|
-
throw Error('not implemented');
|
|
228
|
+
const fakeAgcc = {
|
|
229
|
+
/** @type {(destPort: unknown, msgJson: string) => string} */
|
|
230
|
+
send: (destPort, msgJson) => {
|
|
231
|
+
const portName =
|
|
232
|
+
knownPorts.get(destPort) || Fail`unknown cosmos port ${destPort}`;
|
|
233
|
+
const msg = JSON.parse(msgJson);
|
|
234
|
+
const result = receiveBridgeSend(portName, msg);
|
|
235
|
+
return JSON.stringify(result);
|
|
236
|
+
},
|
|
235
237
|
};
|
|
238
|
+
const actionQueueStorage = makeQueueStorage(
|
|
239
|
+
msg => fakeAgcc.send(portNums.storage, msg),
|
|
240
|
+
STORAGE_PATH.ACTION_QUEUE,
|
|
241
|
+
);
|
|
242
|
+
const highPriorityQueueStorage = makeQueueStorage(
|
|
243
|
+
msg => fakeAgcc.send(portNums.storage, msg),
|
|
244
|
+
STORAGE_PATH.HIGH_PRIORITY_QUEUE,
|
|
245
|
+
);
|
|
236
246
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
const
|
|
242
|
-
swingStore,
|
|
243
|
-
actionQueueStorage,
|
|
244
|
-
highPriorityQueueStorage,
|
|
245
|
-
mailboxStorage,
|
|
246
|
-
clearChainSends,
|
|
247
|
-
replayChainSends,
|
|
248
|
-
bridgeOutbound: receiveBridgeSend,
|
|
249
|
-
vatconfig: config,
|
|
250
|
-
argv: { bootMsg: initMessage },
|
|
247
|
+
const { name: dbDir, removeCallback: cleanupDB } = tmp.dirSync({
|
|
248
|
+
prefix: debugName || 'testdb',
|
|
249
|
+
unsafeCleanup: true,
|
|
250
|
+
});
|
|
251
|
+
const launchChain = makeLaunchChain(fakeAgcc, dbDir, {
|
|
251
252
|
env,
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
253
|
+
fs,
|
|
254
|
+
path: nativePath,
|
|
255
|
+
testingOverrides: { debugName, slogSender, swingStore, vatconfig: config },
|
|
256
|
+
});
|
|
257
|
+
const launchResult = await launchChain({
|
|
258
|
+
...initMessage,
|
|
259
|
+
resolvedConfig: swingsetConfig,
|
|
255
260
|
});
|
|
256
261
|
const { blockingSend, shutdown: shutdownKernel } = launchResult;
|
|
257
262
|
/** @type {(options?: { kernelOnly?: boolean }) => Promise<void>} */
|
|
@@ -259,6 +264,7 @@ export const makeCosmicSwingsetTestKit = async (
|
|
|
259
264
|
await shutdownKernel();
|
|
260
265
|
if (kernelOnly) return;
|
|
261
266
|
await hostStorage.close();
|
|
267
|
+
await cleanupDB();
|
|
262
268
|
};
|
|
263
269
|
|
|
264
270
|
// Remember information about the current block, starting with the init
|
|
@@ -379,7 +385,6 @@ export const makeCosmicSwingsetTestKit = async (
|
|
|
379
385
|
// SwingSet-oriented references.
|
|
380
386
|
actionQueue,
|
|
381
387
|
highPriorityQueue,
|
|
382
|
-
mailboxStorage,
|
|
383
388
|
shutdown,
|
|
384
389
|
swingStore,
|
|
385
390
|
|