@agoric/cosmic-swingset 0.41.4-dev-7bfadd3.0 → 0.41.4-dev-f2445bc.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agoric/cosmic-swingset",
3
- "version": "0.41.4-dev-7bfadd3.0+7bfadd3",
3
+ "version": "0.41.4-dev-f2445bc.0+f2445bc",
4
4
  "description": "Agoric's Cosmos blockchain integration",
5
5
  "type": "module",
6
6
  "bin": {
@@ -22,15 +22,15 @@
22
22
  "author": "Agoric",
23
23
  "license": "Apache-2.0",
24
24
  "dependencies": {
25
- "@agoric/assert": "0.6.1-dev-7bfadd3.0+7bfadd3",
26
- "@agoric/cosmos": "0.34.2-dev-7bfadd3.0+7bfadd3",
27
- "@agoric/deploy-script-support": "0.10.4-dev-7bfadd3.0+7bfadd3",
28
- "@agoric/internal": "0.3.3-dev-7bfadd3.0+7bfadd3",
29
- "@agoric/store": "0.9.3-dev-7bfadd3.0+7bfadd3",
30
- "@agoric/swing-store": "0.9.2-dev-7bfadd3.0+7bfadd3",
31
- "@agoric/swingset-vat": "0.32.3-dev-7bfadd3.0+7bfadd3",
32
- "@agoric/telemetry": "0.6.3-dev-7bfadd3.0+7bfadd3",
33
- "@agoric/vm-config": "0.1.1-dev-7bfadd3.0+7bfadd3",
25
+ "@agoric/assert": "0.6.1-dev-f2445bc.0+f2445bc",
26
+ "@agoric/cosmos": "0.34.2-dev-f2445bc.0+f2445bc",
27
+ "@agoric/deploy-script-support": "0.10.4-dev-f2445bc.0+f2445bc",
28
+ "@agoric/internal": "0.3.3-dev-f2445bc.0+f2445bc",
29
+ "@agoric/store": "0.9.3-dev-f2445bc.0+f2445bc",
30
+ "@agoric/swing-store": "0.9.2-dev-f2445bc.0+f2445bc",
31
+ "@agoric/swingset-vat": "0.32.3-dev-f2445bc.0+f2445bc",
32
+ "@agoric/telemetry": "0.6.3-dev-f2445bc.0+f2445bc",
33
+ "@agoric/vm-config": "0.1.1-dev-f2445bc.0+f2445bc",
34
34
  "@endo/bundle-source": "^2.5.2",
35
35
  "@endo/far": "^0.2.19",
36
36
  "@endo/import-bundle": "^0.3.5",
@@ -65,5 +65,5 @@
65
65
  ],
66
66
  "timeout": "20m"
67
67
  },
68
- "gitHead": "7bfadd33dc68a625c2df2ad57da7a4c6ee52f7ea"
68
+ "gitHead": "f2445bcb471c26169ea8052d04e96d51ef9f1487"
69
69
  }
@@ -11,7 +11,7 @@ import pathPower from 'path';
11
11
  import { fileURLToPath } from 'url';
12
12
 
13
13
  import { makePromiseKit } from '@endo/promise-kit';
14
- import { Fail } from '@agoric/assert';
14
+ import { Fail, q } from '@agoric/assert';
15
15
  import { makeAggregateError } from '@agoric/internal';
16
16
  import { makeShutdown } from '@agoric/internal/src/node/shutdown.js';
17
17
  import { openSwingStore, makeSwingStoreExporter } from '@agoric/swing-store';
@@ -19,26 +19,66 @@ import { openSwingStore, makeSwingStoreExporter } from '@agoric/swing-store';
19
19
  import { isEntrypoint } from './helpers/is-entrypoint.js';
20
20
  import { makeProcessValue } from './helpers/process-value.js';
21
21
 
22
- /** @typedef {'current' | 'archival' | 'debug'} SwingStoreExportMode */
23
-
24
22
  // ExportManifestFilename is the manifest filename which must be synchronized
25
23
  // with the golang SwingStoreExportsHandler in golang/cosmos/x/swingset/keeper/swing_store_exports_handler.go
26
24
  export const ExportManifestFileName = 'export-manifest.json';
27
25
 
28
- // eslint-disable-next-line jsdoc/require-returns-check
29
26
  /**
30
- * @param {string | undefined} mode
31
- * @returns {asserts mode is SwingStoreExportMode | undefined}
27
+ * @typedef {'none' // No artifacts included
28
+ * | import("@agoric/swing-store").ArtifactMode
29
+ * } SwingStoreArtifactMode
32
30
  */
33
- const checkExportMode = mode => {
34
- switch (mode) {
35
- case 'current':
31
+
32
+ /**
33
+ * @typedef {'skip' // Do not include any "export data" (artifacts only)
34
+ * | 'repair-metadata' // Add missing artifact metadata (import only)
35
+ * | 'all' // Include all export data, create new swing-store on import
36
+ * } SwingStoreExportDataMode
37
+ */
38
+
39
+ /**
40
+ * @param {SwingStoreArtifactMode | undefined} artifactMode
41
+ * @returns {import("@agoric/swing-store").ArtifactMode}
42
+ */
43
+ export const getEffectiveArtifactMode = artifactMode => {
44
+ switch (artifactMode) {
45
+ case 'none':
46
+ case 'operational':
47
+ return 'operational';
48
+ case undefined:
49
+ case 'replay':
50
+ return 'replay';
36
51
  case 'archival':
37
52
  case 'debug':
53
+ return artifactMode;
54
+ default:
55
+ throw Fail`Invalid value ${q(artifactMode)} for "artifact-mode"`;
56
+ }
57
+ };
58
+
59
+ /** @type {(artifactMode: string | undefined) => asserts artifactMode is SwingStoreArtifactMode | undefined} */
60
+ export const checkArtifactMode = getEffectiveArtifactMode;
61
+
62
+ /**
63
+ * @param {string | undefined} mode
64
+ * @param {boolean} [isImport]
65
+ * @returns {asserts mode is SwingStoreExportDataMode | undefined}
66
+ */
67
+ export const checkExportDataMode = (mode, isImport = false) => {
68
+ switch (mode) {
69
+ case 'skip':
38
70
  case undefined:
39
71
  break;
72
+ case 'all':
73
+ break;
74
+ case 'repair-metadata': {
75
+ if (isImport) {
76
+ break;
77
+ }
78
+ // Fall through
79
+ }
40
80
  default:
41
- throw Fail`Invalid value ${mode} for "export-mode"`;
81
+ throw Fail`Invalid value ${q(mode)} for "export-data-mode"`;
42
82
  }
43
83
  };
44
84
 
@@ -54,7 +94,7 @@ const checkExportMode = mode => {
54
94
  *
55
95
  * @typedef {object} StateSyncManifest
56
96
  * @property {number} blockHeight the block height corresponding to this export
57
- * @property {SwingStoreExportMode} [mode]
97
+ * @property {SwingStoreArtifactMode} [artifactMode]
58
98
  * @property {string} [data] file name containing the swingStore "export data"
59
99
  * @property {Array<[artifactName: string, fileName: string]>} artifacts
60
100
  * List of swingStore export artifacts which can be validated by the export data
@@ -73,8 +113,8 @@ const checkExportMode = mode => {
73
113
  * @property {string} stateDir the directory containing the SwingStore to export
74
114
  * @property {string} exportDir the directory in which to place the exported artifacts and manifest
75
115
  * @property {number} [blockHeight] block height to check for
76
- * @property {SwingStoreExportMode} [exportMode] whether to include historical or debug artifacts in the export
77
- * @property {boolean} [includeExportData] whether to include an artifact for the export data in the export
116
+ * @property {SwingStoreArtifactMode} [artifactMode] the level of artifacts to include in the export
117
+ * @property {SwingStoreExportDataMode} [exportDataMode] include a synthetic artifact for the export data in the export
78
118
  */
79
119
 
80
120
  /**
@@ -90,10 +130,12 @@ export const validateExporterOptions = options => {
90
130
  options.blockHeight == null ||
91
131
  typeof options.blockHeight === 'number' ||
92
132
  Fail`optional blockHeight option not a number`;
93
- checkExportMode(options.exportMode);
94
- options.includeExportData == null ||
95
- typeof options.includeExportData === 'boolean' ||
96
- Fail`optional includeExportData option not a boolean`;
133
+ checkArtifactMode(options.artifactMode);
134
+ checkExportDataMode(options.exportDataMode);
135
+
136
+ options.includeExportData === undefined ||
137
+ Fail`deprecated includeExportData option found`;
138
+ options.exportMode === undefined || Fail`deprecated exportMode option found`;
97
139
  };
98
140
 
99
141
  /**
@@ -107,7 +149,7 @@ export const validateExporterOptions = options => {
107
149
  * @returns {StateSyncExporter}
108
150
  */
109
151
  export const initiateSwingStoreExport = (
110
- { stateDir, exportDir, blockHeight, exportMode, includeExportData },
152
+ { stateDir, exportDir, blockHeight, artifactMode, exportDataMode },
111
153
  {
112
154
  fs: { open, writeFile },
113
155
  pathResolve,
@@ -116,11 +158,7 @@ export const initiateSwingStoreExport = (
116
158
  log = console.log,
117
159
  },
118
160
  ) => {
119
- const effectiveExportMode = exportMode ?? 'current';
120
- if (effectiveExportMode !== 'current' && !includeExportData) {
121
- throw Fail`Must include export data if export mode not "current"`;
122
- }
123
-
161
+ const effectiveArtifactMode = getEffectiveArtifactMode(artifactMode);
124
162
  /** @type {number | undefined} */
125
163
  let savedBlockHeight;
126
164
 
@@ -140,7 +178,9 @@ export const initiateSwingStoreExport = (
140
178
  const manifestFile = await open(manifestPath, 'wx');
141
179
  cleanup.push(async () => manifestFile.close());
142
180
 
143
- const swingStoreExporter = makeExporter(stateDir, exportMode);
181
+ const swingStoreExporter = makeExporter(stateDir, {
182
+ artifactMode: effectiveArtifactMode,
183
+ });
144
184
  cleanup.push(async () => swingStoreExporter.close());
145
185
 
146
186
  const { hostStorage } = openDB(stateDir);
@@ -150,7 +190,9 @@ export const initiateSwingStoreExport = (
150
190
 
151
191
  if (blockHeight) {
152
192
  blockHeight === savedBlockHeight ||
153
- Fail`DB at unexpected block height ${savedBlockHeight} (expected ${blockHeight})`;
193
+ Fail`DB at unexpected block height ${q(savedBlockHeight)} (expected ${q(
194
+ blockHeight,
195
+ )})`;
154
196
  }
155
197
 
156
198
  abortIfStopped();
@@ -160,11 +202,11 @@ export const initiateSwingStoreExport = (
160
202
  /** @type {StateSyncManifest} */
161
203
  const manifest = {
162
204
  blockHeight: savedBlockHeight,
163
- mode: exportMode,
205
+ artifactMode: artifactMode || effectiveArtifactMode,
164
206
  artifacts: [],
165
207
  };
166
208
 
167
- if (includeExportData) {
209
+ if (exportDataMode === 'all') {
168
210
  log?.(`Writing Export Data`);
169
211
  const fileName = `export-data.jsonl`;
170
212
  const exportDataFile = await open(pathResolve(exportDir, fileName), 'wx');
@@ -178,14 +220,16 @@ export const initiateSwingStoreExport = (
178
220
  }
179
221
  abortIfStopped();
180
222
 
181
- for await (const artifactName of swingStoreExporter.getArtifactNames()) {
182
- abortIfStopped();
183
- log?.(`Writing artifact: ${artifactName}`);
184
- const artifactData = swingStoreExporter.getArtifact(artifactName);
185
- // Use artifactName as the file name as we trust swingStore to generate
186
- // artifact names that are valid file names.
187
- await writeFile(pathResolve(exportDir, artifactName), artifactData);
188
- manifest.artifacts.push([artifactName, artifactName]);
223
+ if (artifactMode !== 'none') {
224
+ for await (const artifactName of swingStoreExporter.getArtifactNames()) {
225
+ abortIfStopped();
226
+ log?.(`Writing artifact: ${artifactName}`);
227
+ const artifactData = swingStoreExporter.getArtifact(artifactName);
228
+ // Use artifactName as the file name as we trust swingStore to generate
229
+ // artifact names that are valid file names.
230
+ await writeFile(pathResolve(exportDir, artifactName), artifactData);
231
+ manifest.artifacts.push([artifactName, artifactName]);
232
+ }
189
233
  }
190
234
 
191
235
  await manifestFile.write(JSON.stringify(manifest, null, 2));
@@ -269,11 +313,22 @@ export const main = async (
269
313
  /** @type {string} */ (processValue.getFlag('export-dir', '.')),
270
314
  );
271
315
 
272
- const includeExportData = processValue.getBoolean({
273
- flagName: 'include-export-data',
274
- });
275
- const exportMode = processValue.getFlag('export-mode');
276
- checkExportMode(exportMode);
316
+ const artifactMode = /** @type {SwingStoreArtifactMode | undefined} */ (
317
+ processValue.getFlag('artifact-mode')
318
+ );
319
+ checkArtifactMode(artifactMode);
320
+
321
+ const exportDataMode = processValue.getFlag('export-data-mode');
322
+ checkExportDataMode(exportDataMode);
323
+
324
+ if (
325
+ processValue.getBoolean({ flagName: 'include-export-data' }) !== undefined
326
+ ) {
327
+ throw Fail`deprecated "include-export-data" options, use "export-data-mode" instead`;
328
+ }
329
+ if (processValue.getFlag('export-mode') !== undefined) {
330
+ throw Fail`deprecated "export-mode" options, use "artifact-mode" instead`;
331
+ }
277
332
 
278
333
  const checkBlockHeight = processValue.getInteger({
279
334
  flagName: 'check-block-height',
@@ -290,8 +345,8 @@ export const main = async (
290
345
  stateDir,
291
346
  exportDir,
292
347
  blockHeight: checkBlockHeight,
293
- exportMode,
294
- includeExportData,
348
+ artifactMode,
349
+ exportDataMode,
295
350
  },
296
351
  {
297
352
  fs,
@@ -332,7 +387,7 @@ export const main = async (
332
387
  * @returns {StateSyncExporter}
333
388
  */
334
389
  export const spawnSwingStoreExport = (
335
- { stateDir, exportDir, blockHeight, exportMode, includeExportData },
390
+ { stateDir, exportDir, blockHeight, artifactMode, exportDataMode },
336
391
  { fork, verbose },
337
392
  ) => {
338
393
  const args = ['--state-dir', stateDir, '--export-dir', exportDir];
@@ -341,12 +396,12 @@ export const spawnSwingStoreExport = (
341
396
  args.push('--check-block-height', String(blockHeight));
342
397
  }
343
398
 
344
- if (exportMode) {
345
- args.push('--export-mode', exportMode);
399
+ if (artifactMode) {
400
+ args.push('--artifact-mode', artifactMode);
346
401
  }
347
402
 
348
- if (includeExportData) {
349
- args.push('--include-export-data');
403
+ if (exportDataMode) {
404
+ args.push('--export-data-mode', exportDataMode);
350
405
  }
351
406
 
352
407
  if (verbose) {
@@ -398,7 +453,7 @@ export const spawnSwingStoreExport = (
398
453
  }
399
454
  default: {
400
455
  // @ts-expect-error exhaustive check
401
- Fail`Unexpected ${msg.type} message`;
456
+ Fail`Unexpected ${q(msg.type)} message`;
402
457
  }
403
458
  }
404
459
  };
@@ -12,19 +12,24 @@ import fsPromisesPower from 'fs/promises';
12
12
  import pathPower from 'path';
13
13
 
14
14
  import BufferLineTransform from '@agoric/internal/src/node/buffer-line-transform.js';
15
- import { Fail } from '@agoric/assert';
16
- import { importSwingStore } from '@agoric/swing-store';
15
+ import { Fail, q } from '@agoric/assert';
16
+ import { importSwingStore, openSwingStore } from '@agoric/swing-store';
17
17
 
18
18
  import { isEntrypoint } from './helpers/is-entrypoint.js';
19
19
  import { makeProcessValue } from './helpers/process-value.js';
20
- import { ExportManifestFileName } from './export-kernel-db.js';
20
+ import {
21
+ ExportManifestFileName,
22
+ checkExportDataMode,
23
+ checkArtifactMode,
24
+ } from './export-kernel-db.js';
21
25
 
22
26
  /**
23
27
  * @typedef {object} StateSyncImporterOptions
24
28
  * @property {string} stateDir the directory containing the SwingStore to export
25
29
  * @property {string} exportDir the directory where to place the exported artifacts and manifest
26
30
  * @property {number} [blockHeight] block height to check for
27
- * @property {boolean} [includeHistorical] whether to include historical artifacts in the export
31
+ * @property {import('./export-kernel-db.js').SwingStoreExportDataMode} [exportDataMode] how to handle export data
32
+ * @property {import('./export-kernel-db.js').SwingStoreArtifactMode} [artifactMode] the level of historical artifacts to import
28
33
  */
29
34
 
30
35
  /**
@@ -40,9 +45,57 @@ export const validateImporterOptions = options => {
40
45
  options.blockHeight == null ||
41
46
  typeof options.blockHeight === 'number' ||
42
47
  Fail`optional blockHeight option not a number`;
43
- options.includeHistorical == null ||
44
- typeof options.includeHistorical === 'boolean' ||
45
- Fail`optional includeHistorical option not a boolean`;
48
+ checkExportDataMode(options.exportDataMode, true);
49
+ checkArtifactMode(options.artifactMode);
50
+ options.includeHistorical === undefined ||
51
+ Fail`deprecated includeHistorical option found`;
52
+ };
53
+
54
+ /**
55
+ * @param {Pick<StateSyncImporterOptions, 'artifactMode' | 'exportDataMode' >} options
56
+ * @param {Readonly<import('./export-kernel-db.js').StateSyncManifest>} manifest
57
+ * @returns {import('@agoric/swing-store').ImportSwingStoreOptions}
58
+ */
59
+ const checkAndGetImportSwingStoreOptions = (options, manifest) => {
60
+ typeof manifest.blockHeight === 'number' ||
61
+ Fail`Cannot restore snapshot without block height`;
62
+
63
+ manifest.data || Fail`State-sync manifest missing export data`;
64
+
65
+ const { artifactMode = manifest.artifactMode || 'replay' } = options;
66
+
67
+ if (artifactMode === 'none') {
68
+ throw Fail`Cannot import "export data" without at least "operational" artifacts`;
69
+ }
70
+
71
+ manifest.artifacts?.length ||
72
+ Fail`State-sync manifest missing required artifacts`;
73
+
74
+ switch (artifactMode) {
75
+ case 'debug':
76
+ // eslint-disable-next-line no-fallthrough
77
+ case 'operational':
78
+ if (manifest.artifactMode === 'operational') break;
79
+ // eslint-disable-next-line no-fallthrough
80
+ case 'replay':
81
+ if (manifest.artifactMode === 'replay') break;
82
+ // eslint-disable-next-line no-fallthrough
83
+ case 'archival':
84
+ if (manifest.artifactMode === 'archival') break;
85
+ if (
86
+ manifest.artifactMode === undefined ||
87
+ manifest.artifactMode === 'debug'
88
+ ) {
89
+ // assume the export has sufficient data
90
+ break;
91
+ }
92
+ throw Fail`State-sync manifest has insufficient artifacts: requested import artifact mode: ${q(
93
+ artifactMode,
94
+ )}, manifest has ${q(manifest.artifactMode)} artifacts`;
95
+ default:
96
+ throw Fail`Unexpected artifactMode ${q(artifactMode)}`;
97
+ }
98
+ return { artifactMode };
46
99
  };
47
100
 
48
101
  /**
@@ -51,15 +104,17 @@ export const validateImporterOptions = options => {
51
104
  * @param {Pick<import('fs/promises'), 'readFile'> & Pick<import('fs'), 'createReadStream'>} powers.fs
52
105
  * @param {import('path')['resolve']} powers.pathResolve
53
106
  * @param {typeof import('@agoric/swing-store')['importSwingStore']} [powers.importSwingStore]
107
+ * @param {typeof import('@agoric/swing-store')['openSwingStore']} [powers.openSwingStore]
54
108
  * @param {null | ((...args: any[]) => void)} [powers.log]
55
109
  * @returns {Promise<void>}
56
110
  */
57
111
  export const performStateSyncImport = async (
58
- { stateDir, exportDir, blockHeight, includeHistorical },
112
+ { stateDir, exportDir, blockHeight, exportDataMode = 'all', artifactMode },
59
113
  {
60
114
  fs: { createReadStream, readFile },
61
115
  pathResolve,
62
116
  importSwingStore: importDB = importSwingStore,
117
+ openSwingStore: openDB = openSwingStore,
63
118
  log = console.log,
64
119
  },
65
120
  ) => {
@@ -67,7 +122,9 @@ export const performStateSyncImport = async (
67
122
  const safeExportFileResolve = allegedRelativeFilename => {
68
123
  const resolvedPath = pathResolve(exportDir, allegedRelativeFilename);
69
124
  resolvedPath.startsWith(exportDir) ||
70
- Fail`Exported file ${allegedRelativeFilename} must be in export dir ${exportDir}`;
125
+ Fail`Exported file ${q(
126
+ allegedRelativeFilename,
127
+ )} must be in export dir ${q(exportDir)}`;
71
128
  return resolvedPath;
72
129
  };
73
130
 
@@ -78,26 +135,12 @@ export const performStateSyncImport = async (
78
135
  );
79
136
 
80
137
  if (blockHeight !== undefined && manifest.blockHeight !== blockHeight) {
81
- Fail`State-sync manifest for unexpected block height ${manifest.blockHeight} (expected ${blockHeight})`;
138
+ Fail`State-sync manifest for unexpected block height ${q(
139
+ manifest.blockHeight,
140
+ )} (expected ${q(blockHeight)})`;
82
141
  }
83
142
 
84
- if (!manifest.data) {
85
- throw Fail`State-sync manifest missing export data`;
86
- }
87
-
88
- if (!manifest.artifacts) {
89
- throw Fail`State-sync manifest missing required artifacts`;
90
- }
91
-
92
- const artifacts = harden(Object.fromEntries(manifest.artifacts));
93
-
94
- if (
95
- includeHistorical &&
96
- manifest.mode !== 'archival' &&
97
- manifest.mode !== 'debug'
98
- ) {
99
- throw Fail`State-sync manifest missing historical artifacts`;
100
- }
143
+ const artifacts = harden(Object.fromEntries(manifest.artifacts || []));
101
144
 
102
145
  // Represent the data in `exportDir` as a SwingSetExporter object.
103
146
  /** @type {import('@agoric/swing-store').SwingStoreExporter} */
@@ -128,7 +171,7 @@ export const performStateSyncImport = async (
128
171
  log?.(`importing artifact ${name}`);
129
172
  const fileName = artifacts[name];
130
173
  if (!fileName) {
131
- Fail`invalid artifact ${name}`;
174
+ Fail`invalid artifact ${q(name)}`;
132
175
  }
133
176
  const stream = createReadStream(safeExportFileResolve(fileName));
134
177
  yield* stream;
@@ -141,13 +184,47 @@ export const performStateSyncImport = async (
141
184
  },
142
185
  });
143
186
 
144
- const swingstore = await importDB(exporter, stateDir, { includeHistorical });
187
+ if (exportDataMode === 'all') {
188
+ const importOptions = checkAndGetImportSwingStoreOptions(
189
+ { artifactMode, exportDataMode },
190
+ manifest,
191
+ );
192
+
193
+ const swingstore = await importDB(exporter, stateDir, importOptions);
194
+
195
+ const { hostStorage } = swingstore;
196
+
197
+ hostStorage.kvStore.set('host.height', String(manifest.blockHeight));
198
+ await hostStorage.commit();
199
+ await hostStorage.close();
200
+ } else if (exportDataMode === 'repair-metadata') {
201
+ blockHeight !== 0 || Fail`repair metadata requires a block height`;
202
+
203
+ manifest.data || Fail`State-sync manifest missing export data`;
204
+
205
+ artifactMode === 'none' ||
206
+ Fail`Cannot restore artifacts while repairing metadata`;
207
+
208
+ const { hostStorage } = openDB(stateDir);
209
+
210
+ const savedBlockHeight =
211
+ Number(hostStorage.kvStore.get('host.height')) || 0;
145
212
 
146
- const { hostStorage } = swingstore;
213
+ if (blockHeight !== savedBlockHeight) {
214
+ throw Fail`block height doesn't match. requested=${q(
215
+ blockHeight,
216
+ )}, current=${q(savedBlockHeight)}`;
217
+ }
147
218
 
148
- hostStorage.kvStore.set('host.height', String(manifest.blockHeight));
149
- await hostStorage.commit();
150
- await hostStorage.close();
219
+ await hostStorage.repairMetadata(exporter);
220
+
221
+ await hostStorage.commit();
222
+ await hostStorage.close();
223
+ } else if (exportDataMode === 'skip') {
224
+ throw Fail`Repopulation of artifacts not yet supported`;
225
+ } else {
226
+ throw Fail`Unknown export-data-mode ${exportDataMode}`;
227
+ }
151
228
  };
152
229
 
153
230
  /**
@@ -182,9 +259,20 @@ export const main = async (
182
259
  /** @type {string} */ (processValue.getFlag('export-dir', '.')),
183
260
  );
184
261
 
185
- const includeHistorical = processValue.getBoolean({
186
- flagName: 'include-historical',
187
- });
262
+ const artifactMode =
263
+ /** @type {import('./export-kernel-db.js').SwingStoreArtifactMode | undefined} */ (
264
+ processValue.getFlag('artifact-mode')
265
+ );
266
+ checkArtifactMode(artifactMode);
267
+
268
+ const exportDataMode = processValue.getFlag('export-data-mode');
269
+ checkExportDataMode(exportDataMode, true);
270
+
271
+ if (
272
+ processValue.getBoolean({ flagName: 'include-historical' }) !== undefined
273
+ ) {
274
+ throw Fail`deprecated "include-historical" options, use "artifact-mode" instead`;
275
+ }
188
276
 
189
277
  const checkBlockHeight = processValue.getInteger({
190
278
  flagName: 'check-block-height',
@@ -199,7 +287,8 @@ export const main = async (
199
287
  stateDir,
200
288
  exportDir,
201
289
  blockHeight: checkBlockHeight,
202
- includeHistorical,
290
+ artifactMode,
291
+ exportDataMode,
203
292
  },
204
293
  {
205
294
  fs,