@cogcoin/client 1.1.4 → 1.1.6

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.
Files changed (107) hide show
  1. package/README.md +4 -5
  2. package/dist/bitcoind/indexer-daemon.d.ts +3 -7
  3. package/dist/bitcoind/indexer-daemon.js +43 -158
  4. package/dist/bitcoind/managed-runtime/bitcoind-policy.d.ts +16 -0
  5. package/dist/bitcoind/managed-runtime/bitcoind-policy.js +177 -0
  6. package/dist/bitcoind/managed-runtime/indexer-policy.d.ts +34 -0
  7. package/dist/bitcoind/managed-runtime/indexer-policy.js +200 -0
  8. package/dist/bitcoind/managed-runtime/status.d.ts +11 -0
  9. package/dist/bitcoind/managed-runtime/status.js +59 -0
  10. package/dist/bitcoind/managed-runtime/types.d.ts +37 -0
  11. package/dist/bitcoind/managed-runtime/types.js +1 -0
  12. package/dist/bitcoind/progress/tty-renderer.js +3 -2
  13. package/dist/bitcoind/service.d.ts +2 -7
  14. package/dist/bitcoind/service.js +46 -94
  15. package/dist/cli/command-registry.d.ts +39 -0
  16. package/dist/cli/command-registry.js +1132 -0
  17. package/dist/cli/commands/client-admin.js +6 -56
  18. package/dist/cli/commands/mining-admin.js +9 -32
  19. package/dist/cli/commands/mining-read.js +15 -56
  20. package/dist/cli/commands/mining-runtime.js +258 -57
  21. package/dist/cli/commands/service-runtime.js +1 -64
  22. package/dist/cli/commands/status.js +2 -15
  23. package/dist/cli/commands/update.js +6 -21
  24. package/dist/cli/commands/wallet-admin.js +18 -120
  25. package/dist/cli/commands/wallet-mutation.js +4 -7
  26. package/dist/cli/commands/wallet-read.js +31 -138
  27. package/dist/cli/context.js +2 -4
  28. package/dist/cli/mining-format.js +8 -2
  29. package/dist/cli/mutation-command-groups.d.ts +11 -11
  30. package/dist/cli/mutation-command-groups.js +9 -18
  31. package/dist/cli/mutation-json.d.ts +1 -17
  32. package/dist/cli/mutation-json.js +1 -28
  33. package/dist/cli/mutation-success.d.ts +0 -1
  34. package/dist/cli/mutation-success.js +0 -19
  35. package/dist/cli/output.d.ts +1 -10
  36. package/dist/cli/output.js +52 -481
  37. package/dist/cli/parse.d.ts +1 -1
  38. package/dist/cli/parse.js +38 -695
  39. package/dist/cli/runner.js +28 -113
  40. package/dist/cli/types.d.ts +7 -8
  41. package/dist/cli/update-notifier.js +1 -1
  42. package/dist/cli/wallet-format.js +1 -1
  43. package/dist/wallet/lifecycle/access.d.ts +5 -0
  44. package/dist/wallet/lifecycle/access.js +79 -0
  45. package/dist/wallet/lifecycle/context.d.ts +26 -0
  46. package/dist/wallet/lifecycle/context.js +58 -0
  47. package/dist/wallet/lifecycle/managed-core.d.ts +15 -0
  48. package/dist/wallet/lifecycle/managed-core.js +197 -0
  49. package/dist/wallet/lifecycle/repair-bitcoind.d.ts +10 -0
  50. package/dist/wallet/lifecycle/repair-bitcoind.js +142 -0
  51. package/dist/wallet/lifecycle/repair-indexer.d.ts +8 -0
  52. package/dist/wallet/lifecycle/repair-indexer.js +117 -0
  53. package/dist/wallet/lifecycle/repair-mining.d.ts +49 -0
  54. package/dist/wallet/lifecycle/repair-mining.js +304 -0
  55. package/dist/wallet/lifecycle/repair-runtime.d.ts +36 -0
  56. package/dist/wallet/lifecycle/repair-runtime.js +206 -0
  57. package/dist/wallet/lifecycle/repair.d.ts +9 -0
  58. package/dist/wallet/lifecycle/repair.js +127 -0
  59. package/dist/wallet/lifecycle/setup-prompts.d.ts +7 -0
  60. package/dist/wallet/lifecycle/setup-prompts.js +88 -0
  61. package/dist/wallet/lifecycle/setup-state.d.ts +26 -0
  62. package/dist/wallet/lifecycle/setup-state.js +159 -0
  63. package/dist/wallet/lifecycle/setup.d.ts +15 -0
  64. package/dist/wallet/lifecycle/setup.js +124 -0
  65. package/dist/wallet/lifecycle/types.d.ts +156 -0
  66. package/dist/wallet/lifecycle/types.js +1 -0
  67. package/dist/wallet/lifecycle.d.ts +4 -165
  68. package/dist/wallet/lifecycle.js +3 -1656
  69. package/dist/wallet/mining/candidate.d.ts +60 -0
  70. package/dist/wallet/mining/candidate.js +290 -0
  71. package/dist/wallet/mining/competitiveness.d.ts +22 -0
  72. package/dist/wallet/mining/competitiveness.js +640 -0
  73. package/dist/wallet/mining/control.js +7 -251
  74. package/dist/wallet/mining/cycle.d.ts +39 -0
  75. package/dist/wallet/mining/cycle.js +542 -0
  76. package/dist/wallet/mining/engine-state.d.ts +66 -0
  77. package/dist/wallet/mining/engine-state.js +211 -0
  78. package/dist/wallet/mining/engine-types.d.ts +173 -0
  79. package/dist/wallet/mining/engine-types.js +1 -0
  80. package/dist/wallet/mining/engine-utils.d.ts +7 -0
  81. package/dist/wallet/mining/engine-utils.js +75 -0
  82. package/dist/wallet/mining/events.d.ts +2 -0
  83. package/dist/wallet/mining/events.js +19 -0
  84. package/dist/wallet/mining/lifecycle.d.ts +71 -0
  85. package/dist/wallet/mining/lifecycle.js +355 -0
  86. package/dist/wallet/mining/projection.d.ts +61 -0
  87. package/dist/wallet/mining/projection.js +319 -0
  88. package/dist/wallet/mining/publish.d.ts +79 -0
  89. package/dist/wallet/mining/publish.js +614 -0
  90. package/dist/wallet/mining/runner.d.ts +12 -418
  91. package/dist/wallet/mining/runner.js +274 -3433
  92. package/dist/wallet/mining/supervisor.d.ts +134 -0
  93. package/dist/wallet/mining/supervisor.js +558 -0
  94. package/dist/wallet/mining/visualizer-sync.d.ts +42 -0
  95. package/dist/wallet/mining/visualizer-sync.js +166 -0
  96. package/dist/wallet/mining/visualizer.d.ts +1 -0
  97. package/dist/wallet/mining/visualizer.js +33 -18
  98. package/dist/wallet/read/context.js +13 -188
  99. package/dist/wallet/reset.d.ts +1 -1
  100. package/dist/wallet/reset.js +35 -11
  101. package/dist/wallet/runtime.d.ts +0 -6
  102. package/dist/wallet/runtime.js +2 -38
  103. package/dist/wallet/tx/common.d.ts +18 -0
  104. package/dist/wallet/tx/common.js +40 -26
  105. package/package.json +1 -1
  106. package/dist/wallet/state/seed-index.d.ts +0 -43
  107. package/dist/wallet/state/seed-index.js +0 -151
@@ -14,7 +14,6 @@ import { loadMiningRuntimeStatus } from "./mining/runtime-artifacts.js";
14
14
  import { clearLegacyWalletLockArtifacts, withUnlockedManagedCoreWallet } from "./managed-core-wallet.js";
15
15
  import { resolveWalletRuntimePathsForTesting } from "./runtime.js";
16
16
  import { createDefaultWalletSecretProvider, createWalletRootId, createWalletSecretReference, } from "./state/provider.js";
17
- import { loadWalletSeedIndex } from "./state/seed-index.js";
18
17
  import { extractWalletRootIdHintFromWalletStateEnvelope, loadRawWalletStateEnvelope, loadWalletState, saveWalletState, } from "./state/storage.js";
19
18
  import { confirmTypedAcknowledgement } from "./tx/confirm.js";
20
19
  function sanitizeWalletName(walletRootId) {
@@ -50,6 +49,36 @@ async function readJsonFileOrNull(path) {
50
49
  return null;
51
50
  }
52
51
  }
52
+ async function collectLegacyImportedSeedSecretProviderKeyIds(paths) {
53
+ const seedsRoot = join(paths.stateRoot, "seeds");
54
+ const entries = await readdir(seedsRoot, { withFileTypes: true }).catch((error) => {
55
+ if (error instanceof Error && "code" in error && error.code === "ENOENT") {
56
+ return [];
57
+ }
58
+ throw error;
59
+ });
60
+ const keyIds = new Set();
61
+ for (const entry of entries) {
62
+ if (!entry.isDirectory()) {
63
+ continue;
64
+ }
65
+ const seedRoot = join(seedsRoot, entry.name);
66
+ const candidatePaths = [
67
+ join(seedRoot, "wallet-state.enc"),
68
+ join(seedRoot, "wallet-state.enc.bak"),
69
+ join(seedRoot, "wallet-init-pending.enc"),
70
+ join(seedRoot, "wallet-init-pending.enc.bak"),
71
+ ];
72
+ for (const candidatePath of candidatePaths) {
73
+ const envelope = await readJsonFileOrNull(candidatePath);
74
+ const keyId = envelope?.secretProvider?.keyId?.trim() ?? "";
75
+ if (keyId.length > 0) {
76
+ keyIds.add(keyId);
77
+ }
78
+ }
79
+ }
80
+ return [...keyIds].sort((left, right) => left.localeCompare(right));
81
+ }
53
82
  async function isProcessAlive(pid) {
54
83
  if (pid === null) {
55
84
  return false;
@@ -454,12 +483,7 @@ async function preflightReset(options) {
454
483
  }
455
484
  const tracked = await collectTrackedManagedProcesses(options.paths);
456
485
  const secretProviderKeyId = rawEnvelope?.envelope.secretProvider?.keyId ?? null;
457
- const seedIndex = await loadWalletSeedIndex({
458
- paths: options.paths,
459
- }).catch(() => null);
460
- const importedSeedSecretProviderKeyIds = [...new Set((seedIndex?.seeds ?? [])
461
- .filter((seed) => seed.kind === "imported")
462
- .map((seed) => createWalletSecretReference(seed.walletRootId).keyId))];
486
+ const importedSeedSecretProviderKeyIds = await collectLegacyImportedSeedSecretProviderKeyIds(options.paths);
463
487
  return {
464
488
  dataRoot: options.paths.dataRoot,
465
489
  removedRoots,
@@ -555,8 +579,8 @@ async function resolveResetExecutionDecision(options) {
555
579
  let walletChoice = "";
556
580
  let loadedWalletForEntropyReset = null;
557
581
  if (options.preflight.wallet.present) {
558
- const answer = (await options.prompter.prompt("Wallet reset choice ([Enter] retain base entropy, \"skip\", or \"delete wallet\"): ")).trim();
559
- if (answer !== "" && answer !== "skip" && answer !== "delete wallet") {
582
+ const answer = (await options.prompter.prompt("Wallet reset choice ([Enter] retain base entropy, \"skip\", or \"clear wallet entropy\"): ")).trim();
583
+ if (answer !== "" && answer !== "skip" && answer !== "clear wallet entropy") {
560
584
  throw new Error("reset_wallet_choice_invalid");
561
585
  }
562
586
  walletChoice = answer;
@@ -592,7 +616,7 @@ function determineWalletAction(walletPresent, walletChoice) {
592
616
  if (walletChoice === "skip") {
593
617
  return "kept-unchanged";
594
618
  }
595
- if (walletChoice === "delete wallet") {
619
+ if (walletChoice === "clear wallet entropy") {
596
620
  return "deleted";
597
621
  }
598
622
  return "retain-mnemonic";
@@ -636,7 +660,7 @@ export async function previewResetWallet(options) {
636
660
  walletPrompt: preflight.wallet.present
637
661
  ? {
638
662
  defaultAction: "retain-mnemonic",
639
- acceptedInputs: ["", "skip", "delete wallet"],
663
+ acceptedInputs: ["", "skip", "clear wallet entropy"],
640
664
  entropyRetainingResetAvailable: preflight.wallet.mode === "provider-backed",
641
665
  envelopeSource: preflight.wallet.envelopeSource,
642
666
  }
@@ -1,7 +1,5 @@
1
1
  import type { CogcoinPathResolution } from "../app-paths.js";
2
- export type WalletSeedKind = "main" | "imported";
3
2
  export interface WalletRuntimePathResolution extends CogcoinPathResolution {
4
- seedName?: string | null;
5
3
  }
6
4
  export interface WalletRuntimePaths {
7
5
  dataRoot: string;
@@ -11,9 +9,6 @@ export interface WalletRuntimePaths {
11
9
  walletRuntimeRoot: string;
12
10
  stateRoot: string;
13
11
  walletStateRoot: string;
14
- seedRegistryPath: string;
15
- selectedSeedName: string;
16
- selectedSeedKind: WalletSeedKind;
17
12
  bitcoinDataDir: string;
18
13
  indexerRoot: string;
19
14
  walletStateDirectory: string;
@@ -31,5 +26,4 @@ export interface WalletRuntimePaths {
31
26
  miningEventsPath: string;
32
27
  miningControlLockPath: string;
33
28
  }
34
- export declare function deriveWalletRuntimePathsForSeed(basePaths: WalletRuntimePaths, seedName: string | null | undefined): WalletRuntimePaths;
35
29
  export declare function resolveWalletRuntimePathsForTesting(resolution?: WalletRuntimePathResolution): WalletRuntimePaths;
@@ -1,41 +1,8 @@
1
1
  import { join } from "node:path";
2
2
  import { resolveCogcoinPathsForTesting } from "../app-paths.js";
3
- function resolveSeedLayout(sharedStateRoot, sharedRuntimeRoot, seedName) {
4
- if (seedName === "main") {
5
- return {
6
- seedKind: "main",
7
- walletStateRoot: sharedStateRoot,
8
- walletRuntimeRoot: sharedRuntimeRoot,
9
- };
10
- }
11
- return {
12
- seedKind: "imported",
13
- walletStateRoot: join(sharedStateRoot, "seeds", seedName),
14
- walletRuntimeRoot: join(sharedRuntimeRoot, "seeds", seedName),
15
- };
16
- }
17
- export function deriveWalletRuntimePathsForSeed(basePaths, seedName) {
18
- const resolvedSeedName = seedName ?? "main";
19
- const seedLayout = resolveSeedLayout(basePaths.stateRoot, basePaths.runtimeRoot, resolvedSeedName);
20
- return {
21
- ...basePaths,
22
- walletRuntimeRoot: seedLayout.walletRuntimeRoot,
23
- walletStateRoot: seedLayout.walletStateRoot,
24
- selectedSeedName: resolvedSeedName,
25
- selectedSeedKind: seedLayout.seedKind,
26
- walletStateDirectory: seedLayout.walletStateRoot,
27
- walletStatePath: join(seedLayout.walletStateRoot, "wallet-state.enc"),
28
- walletStateBackupPath: join(seedLayout.walletStateRoot, "wallet-state.enc.bak"),
29
- walletInitPendingPath: join(seedLayout.walletStateRoot, "wallet-init-pending.enc"),
30
- walletInitPendingBackupPath: join(seedLayout.walletStateRoot, "wallet-init-pending.enc.bak"),
31
- miningRoot: join(seedLayout.walletRuntimeRoot, "mining"),
32
- miningStatusPath: join(seedLayout.walletRuntimeRoot, "mining", "status.json"),
33
- miningEventsPath: join(seedLayout.walletRuntimeRoot, "mining", "events.jsonl"),
34
- };
35
- }
36
3
  export function resolveWalletRuntimePathsForTesting(resolution = {}) {
37
4
  const paths = resolveCogcoinPathsForTesting(resolution);
38
- return deriveWalletRuntimePathsForSeed({
5
+ return {
39
6
  dataRoot: paths.dataRoot,
40
7
  clientDataDir: paths.clientDataDir,
41
8
  clientConfigPath: paths.clientConfigPath,
@@ -43,9 +10,6 @@ export function resolveWalletRuntimePathsForTesting(resolution = {}) {
43
10
  walletRuntimeRoot: paths.runtimeRoot,
44
11
  stateRoot: paths.stateRoot,
45
12
  walletStateRoot: paths.stateRoot,
46
- seedRegistryPath: join(paths.stateRoot, "seed-index.json"),
47
- selectedSeedName: "main",
48
- selectedSeedKind: "main",
49
13
  bitcoinDataDir: paths.bitcoinDataDir,
50
14
  indexerRoot: paths.indexerRoot,
51
15
  walletStateDirectory: paths.stateRoot,
@@ -62,5 +26,5 @@ export function resolveWalletRuntimePathsForTesting(resolution = {}) {
62
26
  miningStatusPath: join(paths.runtimeRoot, "mining", "status.json"),
63
27
  miningEventsPath: join(paths.runtimeRoot, "mining", "events.jsonl"),
64
28
  miningControlLockPath: paths.miningControlLockPath,
65
- }, resolution.seedName);
29
+ };
66
30
  }
@@ -168,6 +168,24 @@ export declare function buildWalletMutationTransaction<TPlan>(options: {
168
168
  availableFundingMinConf?: number;
169
169
  temporarilyUnlockedPolicyOutpoints?: readonly OutpointRecord[];
170
170
  }): Promise<BuiltWalletMutationTransaction>;
171
+ export declare function fundAndValidateWalletMutationDraft<TPlan>(options: {
172
+ rpc: WalletMutationRpcClient;
173
+ walletName: string;
174
+ plan: TPlan & {
175
+ fixedInputs: FixedWalletInput[];
176
+ outputs: unknown[];
177
+ changeAddress: string;
178
+ changePosition?: number | null;
179
+ allowedFundingScriptPubKeyHex: string;
180
+ eligibleFundingOutpointKeys: Set<string>;
181
+ };
182
+ validateFundedDraft(decoded: RpcDecodedPsbt, funded: RpcWalletCreateFundedPsbtResult, plan: TPlan): void;
183
+ feeRate?: number;
184
+ availableFundingMinConf?: number;
185
+ }): Promise<{
186
+ funded: RpcWalletCreateFundedPsbtResult;
187
+ decoded: RpcDecodedPsbt;
188
+ }>;
171
189
  export declare function buildWalletMutationTransactionWithReserveFallback<TPlan>(options: {
172
190
  rpc: WalletMutationRpcClient;
173
191
  walletName: string;
@@ -416,35 +416,16 @@ export async function pauseMiningForWalletMutation(options) {
416
416
  });
417
417
  }
418
418
  export async function buildWalletMutationTransaction(options) {
419
- const availableFundingMinConf = options.availableFundingMinConf ?? 1;
420
- const availableFundingUtxos = (await options.rpc.listUnspent(options.walletName, availableFundingMinConf))
421
- .filter((entry) => isSpendableFundingUtxo(entry, options.plan.allowedFundingScriptPubKeyHex, availableFundingMinConf));
422
- const availableFundingValueByKey = new Map(availableFundingUtxos.map((entry) => [
423
- outpointKey({ txid: entry.txid, vout: entry.vout }),
424
- btcNumberToSats(entry.amount),
425
- ]));
426
- const validationPlan = {
427
- ...options.plan,
428
- eligibleFundingOutpointKeys: new Set([
429
- ...options.plan.eligibleFundingOutpointKeys,
430
- ...availableFundingUtxos.map((entry) => outpointKey({ txid: entry.txid, vout: entry.vout })),
431
- ]),
432
- };
433
419
  const temporaryBuilderLockedOutpoints = [];
434
420
  try {
435
- const funded = await options.rpc.walletCreateFundedPsbt(options.walletName, options.plan.fixedInputs, options.plan.outputs, 0, {
436
- add_inputs: true,
437
- include_unsafe: false,
438
- minconf: availableFundingMinConf,
439
- changeAddress: options.plan.changeAddress,
440
- ...(options.plan.changePosition == null ? {} : { changePosition: options.plan.changePosition }),
441
- lockUnspents: false,
442
- fee_rate: options.feeRate ?? DEFAULT_WALLET_MUTATION_FEE_RATE_SAT_VB,
443
- replaceable: true,
444
- subtractFeeFromOutputs: [],
421
+ const { funded, decoded } = await fundAndValidateWalletMutationDraft({
422
+ rpc: options.rpc,
423
+ walletName: options.walletName,
424
+ plan: options.plan,
425
+ validateFundedDraft: options.validateFundedDraft,
426
+ feeRate: options.feeRate,
427
+ availableFundingMinConf: options.availableFundingMinConf,
445
428
  });
446
- const decoded = await options.rpc.decodePsbt(funded.psbt);
447
- options.validateFundedDraft(decoded, funded, validationPlan);
448
429
  let signed;
449
430
  let finalized;
450
431
  let rawHex;
@@ -490,6 +471,39 @@ export async function buildWalletMutationTransaction(options) {
490
471
  throw error;
491
472
  }
492
473
  }
474
+ export async function fundAndValidateWalletMutationDraft(options) {
475
+ const availableFundingMinConf = options.availableFundingMinConf ?? 1;
476
+ const availableFundingUtxos = (await options.rpc.listUnspent(options.walletName, availableFundingMinConf))
477
+ .filter((entry) => isSpendableFundingUtxo(entry, options.plan.allowedFundingScriptPubKeyHex, availableFundingMinConf));
478
+ const availableFundingValueByKey = new Map(availableFundingUtxos.map((entry) => [
479
+ outpointKey({ txid: entry.txid, vout: entry.vout }),
480
+ btcNumberToSats(entry.amount),
481
+ ]));
482
+ const validationPlan = {
483
+ ...options.plan,
484
+ eligibleFundingOutpointKeys: new Set([
485
+ ...options.plan.eligibleFundingOutpointKeys,
486
+ ...availableFundingUtxos.map((entry) => outpointKey({ txid: entry.txid, vout: entry.vout })),
487
+ ]),
488
+ };
489
+ const funded = await options.rpc.walletCreateFundedPsbt(options.walletName, options.plan.fixedInputs, options.plan.outputs, 0, {
490
+ add_inputs: true,
491
+ include_unsafe: false,
492
+ minconf: availableFundingMinConf,
493
+ changeAddress: options.plan.changeAddress,
494
+ ...(options.plan.changePosition == null ? {} : { changePosition: options.plan.changePosition }),
495
+ lockUnspents: false,
496
+ fee_rate: options.feeRate ?? DEFAULT_WALLET_MUTATION_FEE_RATE_SAT_VB,
497
+ replaceable: true,
498
+ subtractFeeFromOutputs: [],
499
+ });
500
+ const decoded = await options.rpc.decodePsbt(funded.psbt);
501
+ options.validateFundedDraft(decoded, funded, validationPlan);
502
+ return {
503
+ funded,
504
+ decoded,
505
+ };
506
+ }
493
507
  export async function buildWalletMutationTransactionWithReserveFallback(options) {
494
508
  return buildWalletMutationTransaction({
495
509
  rpc: options.rpc,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cogcoin/client",
3
- "version": "1.1.4",
3
+ "version": "1.1.6",
4
4
  "description": "Store-backed Cogcoin client with wallet flows, SQLite persistence, and managed Bitcoin Core integration.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -1,43 +0,0 @@
1
- import type { WalletSeedKind, WalletRuntimePaths } from "../runtime.js";
2
- import { type RawWalletStateEnvelope } from "./storage.js";
3
- export interface WalletSeedRecord {
4
- name: string;
5
- kind: WalletSeedKind;
6
- walletRootId: string;
7
- createdAtUnixMs: number;
8
- restoredAtUnixMs: number | null;
9
- }
10
- export interface WalletSeedIndexV1 {
11
- schemaVersion: 1;
12
- lastWrittenAtUnixMs: number;
13
- seeds: WalletSeedRecord[];
14
- }
15
- export declare function normalizeWalletSeedName(name: string): string;
16
- export declare function isValidWalletSeedName(name: string): boolean;
17
- export declare function assertValidImportedWalletSeedName(name: string): string;
18
- export declare function findWalletSeedRecord(index: WalletSeedIndexV1, seedName: string): WalletSeedRecord | null;
19
- export declare function loadWalletSeedIndex(options: {
20
- paths: Pick<WalletRuntimePaths, "seedRegistryPath" | "walletStatePath" | "walletStateBackupPath">;
21
- nowUnixMs?: number;
22
- loadRawWalletStateEnvelope?: (paths: {
23
- primaryPath: string;
24
- backupPath: string;
25
- }) => Promise<RawWalletStateEnvelope | null>;
26
- }): Promise<WalletSeedIndexV1>;
27
- export declare function saveWalletSeedIndex(paths: Pick<WalletRuntimePaths, "seedRegistryPath">, index: WalletSeedIndexV1): Promise<void>;
28
- export declare function ensureMainWalletSeedIndexRecord(options: {
29
- paths: Pick<WalletRuntimePaths, "seedRegistryPath" | "walletStatePath" | "walletStateBackupPath">;
30
- walletRootId: string;
31
- nowUnixMs?: number;
32
- }): Promise<WalletSeedIndexV1>;
33
- export declare function addImportedWalletSeedRecord(options: {
34
- paths: Pick<WalletRuntimePaths, "seedRegistryPath" | "walletStatePath" | "walletStateBackupPath">;
35
- seedName: string;
36
- walletRootId: string;
37
- nowUnixMs?: number;
38
- }): Promise<WalletSeedIndexV1>;
39
- export declare function removeWalletSeedRecord(options: {
40
- paths: Pick<WalletRuntimePaths, "seedRegistryPath" | "walletStatePath" | "walletStateBackupPath">;
41
- seedName: string;
42
- nowUnixMs?: number;
43
- }): Promise<WalletSeedIndexV1>;
@@ -1,151 +0,0 @@
1
- import { mkdir, readFile } from "node:fs/promises";
2
- import { dirname } from "node:path";
3
- import { writeFileAtomic } from "../fs/atomic.js";
4
- import { extractWalletRootIdHintFromWalletStateEnvelope, loadRawWalletStateEnvelope, } from "./storage.js";
5
- function createEmptySeedIndex(nowUnixMs) {
6
- return {
7
- schemaVersion: 1,
8
- lastWrittenAtUnixMs: nowUnixMs,
9
- seeds: [],
10
- };
11
- }
12
- function sortSeedRecords(seeds) {
13
- return [...seeds].sort((left, right) => left.name.localeCompare(right.name));
14
- }
15
- async function readSeedIndexFile(path) {
16
- try {
17
- const parsed = JSON.parse(await readFile(path, "utf8"));
18
- if (parsed?.schemaVersion !== 1 || !Array.isArray(parsed.seeds)) {
19
- throw new Error("wallet_seed_index_invalid");
20
- }
21
- return {
22
- schemaVersion: 1,
23
- lastWrittenAtUnixMs: typeof parsed.lastWrittenAtUnixMs === "number" ? parsed.lastWrittenAtUnixMs : 0,
24
- seeds: sortSeedRecords(parsed.seeds),
25
- };
26
- }
27
- catch (error) {
28
- if (error instanceof Error && "code" in error && error.code === "ENOENT") {
29
- return null;
30
- }
31
- throw new Error("wallet_seed_index_invalid");
32
- }
33
- }
34
- export function normalizeWalletSeedName(name) {
35
- return name.trim().toLowerCase();
36
- }
37
- export function isValidWalletSeedName(name) {
38
- return /^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(name);
39
- }
40
- export function assertValidImportedWalletSeedName(name) {
41
- const normalized = normalizeWalletSeedName(name);
42
- if (!isValidWalletSeedName(normalized)) {
43
- throw new Error("wallet_seed_name_invalid");
44
- }
45
- if (normalized === "main") {
46
- throw new Error("wallet_seed_name_reserved");
47
- }
48
- return normalized;
49
- }
50
- export function findWalletSeedRecord(index, seedName) {
51
- const normalized = normalizeWalletSeedName(seedName);
52
- return index.seeds.find((seed) => seed.name === normalized) ?? null;
53
- }
54
- export async function loadWalletSeedIndex(options) {
55
- const nowUnixMs = options.nowUnixMs ?? Date.now();
56
- const stored = await readSeedIndexFile(options.paths.seedRegistryPath);
57
- if (stored !== null) {
58
- return stored;
59
- }
60
- const rawEnvelope = await (options.loadRawWalletStateEnvelope ?? loadRawWalletStateEnvelope)({
61
- primaryPath: options.paths.walletStatePath,
62
- backupPath: options.paths.walletStateBackupPath,
63
- }).catch(() => null);
64
- const mainWalletRootId = extractWalletRootIdHintFromWalletStateEnvelope(rawEnvelope?.envelope ?? null);
65
- if (mainWalletRootId === null) {
66
- return createEmptySeedIndex(nowUnixMs);
67
- }
68
- return {
69
- schemaVersion: 1,
70
- lastWrittenAtUnixMs: nowUnixMs,
71
- seeds: [{
72
- name: "main",
73
- kind: "main",
74
- walletRootId: mainWalletRootId,
75
- createdAtUnixMs: nowUnixMs,
76
- restoredAtUnixMs: null,
77
- }],
78
- };
79
- }
80
- export async function saveWalletSeedIndex(paths, index) {
81
- await mkdir(dirname(paths.seedRegistryPath), { recursive: true });
82
- await writeFileAtomic(paths.seedRegistryPath, `${JSON.stringify({
83
- ...index,
84
- seeds: sortSeedRecords(index.seeds),
85
- }, null, 2)}\n`, { mode: 0o600 });
86
- }
87
- export async function ensureMainWalletSeedIndexRecord(options) {
88
- const nowUnixMs = options.nowUnixMs ?? Date.now();
89
- const index = await loadWalletSeedIndex({
90
- paths: options.paths,
91
- nowUnixMs,
92
- });
93
- const existing = findWalletSeedRecord(index, "main");
94
- const seeds = index.seeds.filter((seed) => seed.name !== "main");
95
- seeds.push({
96
- name: "main",
97
- kind: "main",
98
- walletRootId: options.walletRootId,
99
- createdAtUnixMs: existing?.createdAtUnixMs ?? nowUnixMs,
100
- restoredAtUnixMs: existing?.restoredAtUnixMs ?? null,
101
- });
102
- const nextIndex = {
103
- schemaVersion: 1,
104
- lastWrittenAtUnixMs: nowUnixMs,
105
- seeds: sortSeedRecords(seeds),
106
- };
107
- await saveWalletSeedIndex(options.paths, nextIndex);
108
- return nextIndex;
109
- }
110
- export async function addImportedWalletSeedRecord(options) {
111
- const nowUnixMs = options.nowUnixMs ?? Date.now();
112
- const seedName = assertValidImportedWalletSeedName(options.seedName);
113
- const index = await loadWalletSeedIndex({
114
- paths: options.paths,
115
- nowUnixMs,
116
- });
117
- if (findWalletSeedRecord(index, seedName) !== null) {
118
- throw new Error("wallet_seed_name_exists");
119
- }
120
- const nextIndex = {
121
- schemaVersion: 1,
122
- lastWrittenAtUnixMs: nowUnixMs,
123
- seeds: sortSeedRecords([
124
- ...index.seeds,
125
- {
126
- name: seedName,
127
- kind: "imported",
128
- walletRootId: options.walletRootId,
129
- createdAtUnixMs: nowUnixMs,
130
- restoredAtUnixMs: nowUnixMs,
131
- },
132
- ]),
133
- };
134
- await saveWalletSeedIndex(options.paths, nextIndex);
135
- return nextIndex;
136
- }
137
- export async function removeWalletSeedRecord(options) {
138
- const nowUnixMs = options.nowUnixMs ?? Date.now();
139
- const seedName = normalizeWalletSeedName(options.seedName);
140
- const index = await loadWalletSeedIndex({
141
- paths: options.paths,
142
- nowUnixMs,
143
- });
144
- const nextIndex = {
145
- schemaVersion: 1,
146
- lastWrittenAtUnixMs: nowUnixMs,
147
- seeds: sortSeedRecords(index.seeds.filter((seed) => seed.name !== seedName)),
148
- };
149
- await saveWalletSeedIndex(options.paths, nextIndex);
150
- return nextIndex;
151
- }