@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
@@ -1,6 +1,6 @@
1
1
  import { createDefaultContext } from "./context.js";
2
2
  import { writeLine } from "./io.js";
3
- import { classifyCliError, createCommandJsonErrorEnvelope, createErrorEnvelope, formatCliTextError, inferOutputMode, isStructuredOutputMode, writeJsonValue, } from "./output.js";
3
+ import { classifyCliError, formatCliTextError } from "./output.js";
4
4
  import { HELP_TEXT, parseCliArgs } from "./parse.js";
5
5
  import { runFollowCommand } from "./commands/follow.js";
6
6
  import { runClientAdminCommand } from "./commands/client-admin.js";
@@ -15,14 +15,6 @@ import { runWalletAdminCommand } from "./commands/wallet-admin.js";
15
15
  import { runWalletMutationCommand } from "./commands/wallet-mutation.js";
16
16
  import { runWalletReadCommand } from "./commands/wallet-read.js";
17
17
  import { maybeNotifyAboutCliUpdate } from "./update-notifier.js";
18
- import { findWalletSeedRecord, loadWalletSeedIndex } from "../wallet/state/seed-index.js";
19
- function commandUsesExistingWalletSeed(parsed) {
20
- return parsed.seedName !== null
21
- && parsed.seedName !== "main"
22
- && parsed.command !== "restore"
23
- && parsed.command !== "wallet-delete"
24
- && parsed.command !== "wallet-restore";
25
- }
26
18
  export async function runCli(argv, contextOverrides = {}) {
27
19
  const context = createDefaultContext(contextOverrides);
28
20
  let parsed;
@@ -31,10 +23,6 @@ export async function runCli(argv, contextOverrides = {}) {
31
23
  }
32
24
  catch (error) {
33
25
  const classified = classifyCliError(error);
34
- if (isStructuredOutputMode(inferOutputMode(argv))) {
35
- writeJsonValue(context.stdout, createErrorEnvelope("cogcoin/cli/v1", `cogcoin ${argv.join(" ")}`.trim(), classified.errorCode, classified.message));
36
- return classified.exitCode;
37
- }
38
26
  writeLine(context.stderr, classified.message);
39
27
  writeLine(context.stderr, HELP_TEXT.trimEnd());
40
28
  return classified.exitCode;
@@ -44,115 +32,42 @@ export async function runCli(argv, contextOverrides = {}) {
44
32
  return 0;
45
33
  }
46
34
  if (parsed.help || parsed.command === null) {
47
- if (parsed.command === null && isStructuredOutputMode(parsed.outputMode)) {
48
- writeJsonValue(context.stdout, createErrorEnvelope("cogcoin/cli/v1", "cogcoin", "cli_missing_command", "cli_missing_command"));
49
- return 2;
50
- }
51
35
  writeLine(context.stdout, HELP_TEXT.trimEnd());
52
36
  return parsed.help ? 0 : 2;
53
37
  }
54
38
  await maybeNotifyAboutCliUpdate(parsed, context);
55
39
  try {
56
- if (parsed.command === "update") {
57
- return runUpdateCommand(parsed, context);
58
- }
59
- if (commandUsesExistingWalletSeed(parsed)) {
60
- const mainPaths = context.resolveWalletRuntimePaths("main");
61
- const seedIndex = await loadWalletSeedIndex({
62
- paths: mainPaths,
63
- });
64
- if (seedIndex.seeds.length > 0 && findWalletSeedRecord(seedIndex, parsed.seedName) === null) {
65
- throw new Error("wallet_seed_not_found");
66
- }
67
- }
68
- if (parsed.command === "sync") {
69
- return runSyncCommand(parsed, context);
70
- }
71
- if (parsed.command === "follow") {
72
- return runFollowCommand(parsed, context);
73
- }
74
- if (parsed.command === "status") {
75
- return runStatusCommand(parsed, context);
40
+ switch (parsed.commandFamily) {
41
+ case "update":
42
+ return runUpdateCommand(parsed, context);
43
+ case "sync":
44
+ return runSyncCommand(parsed, context);
45
+ case "follow":
46
+ return runFollowCommand(parsed, context);
47
+ case "status":
48
+ return runStatusCommand(parsed, context);
49
+ case "client-admin":
50
+ return runClientAdminCommand(parsed, context);
51
+ case "service-runtime":
52
+ return runServiceRuntimeCommand(parsed, context);
53
+ case "wallet-admin":
54
+ return runWalletAdminCommand(parsed, context);
55
+ case "wallet-mutation":
56
+ return runWalletMutationCommand(parsed, context);
57
+ case "wallet-read":
58
+ return runWalletReadCommand(parsed, context);
59
+ case "mining-admin":
60
+ return runMiningAdminCommand(parsed, context);
61
+ case "mining-runtime":
62
+ return runMiningRuntimeCommand(parsed, context);
63
+ case "mining-read":
64
+ return runMiningReadCommand(parsed, context);
65
+ default:
66
+ return runWalletReadCommand(parsed, context);
76
67
  }
77
- if (parsed.command === "client-lock"
78
- || parsed.command === "client-unlock"
79
- || parsed.command === "client-change-password") {
80
- return runClientAdminCommand(parsed, context);
81
- }
82
- if (parsed.command === "bitcoin-start"
83
- || parsed.command === "bitcoin-stop"
84
- || parsed.command === "bitcoin-status"
85
- || parsed.command === "indexer-start"
86
- || parsed.command === "indexer-stop"
87
- || parsed.command === "indexer-status") {
88
- return runServiceRuntimeCommand(parsed, context);
89
- }
90
- if (parsed.command === "mine"
91
- || parsed.command === "mine-start"
92
- || parsed.command === "mine-stop") {
93
- return runMiningRuntimeCommand(parsed, context);
94
- }
95
- if (parsed.command === "mine-setup"
96
- || parsed.command === "mine-prompt") {
97
- return runMiningAdminCommand(parsed, context);
98
- }
99
- if (parsed.command === "init"
100
- || parsed.command === "restore"
101
- || parsed.command === "reset"
102
- || parsed.command === "repair"
103
- || parsed.command === "wallet-init"
104
- || parsed.command === "wallet-delete"
105
- || parsed.command === "wallet-restore"
106
- || parsed.command === "wallet-show-mnemonic") {
107
- return runWalletAdminCommand(parsed, context);
108
- }
109
- if (parsed.command === "anchor"
110
- || parsed.command === "domain-anchor"
111
- || parsed.command === "register"
112
- || parsed.command === "domain-register"
113
- || parsed.command === "transfer"
114
- || parsed.command === "domain-transfer"
115
- || parsed.command === "sell"
116
- || parsed.command === "domain-sell"
117
- || parsed.command === "unsell"
118
- || parsed.command === "domain-unsell"
119
- || parsed.command === "buy"
120
- || parsed.command === "domain-buy"
121
- || parsed.command === "domain-endpoint-set"
122
- || parsed.command === "domain-endpoint-clear"
123
- || parsed.command === "domain-delegate-set"
124
- || parsed.command === "domain-delegate-clear"
125
- || parsed.command === "domain-miner-set"
126
- || parsed.command === "domain-miner-clear"
127
- || parsed.command === "domain-canonical"
128
- || parsed.command === "field-create"
129
- || parsed.command === "field-set"
130
- || parsed.command === "field-clear"
131
- || parsed.command === "bitcoin-transfer"
132
- || parsed.command === "send"
133
- || parsed.command === "claim"
134
- || parsed.command === "reclaim"
135
- || parsed.command === "cog-send"
136
- || parsed.command === "cog-claim"
137
- || parsed.command === "cog-reclaim"
138
- || parsed.command === "cog-lock"
139
- || parsed.command === "rep-give"
140
- || parsed.command === "rep-revoke") {
141
- return runWalletMutationCommand(parsed, context);
142
- }
143
- if (parsed.command === "mine-prompt-list"
144
- || parsed.command === "mine-status"
145
- || parsed.command === "mine-log") {
146
- return runMiningReadCommand(parsed, context);
147
- }
148
- return runWalletReadCommand(parsed, context);
149
68
  }
150
69
  catch (error) {
151
70
  const classified = classifyCliError(error);
152
- if (isStructuredOutputMode(parsed.outputMode)) {
153
- writeJsonValue(context.stdout, createCommandJsonErrorEnvelope(parsed, error));
154
- return classified.exitCode;
155
- }
156
71
  const formatted = formatCliTextError(error);
157
72
  if (formatted !== null) {
158
73
  for (const line of formatted) {
@@ -7,15 +7,15 @@ import { attachOrStartManagedBitcoindService, probeManagedBitcoindService, stopM
7
7
  import { openSqliteStore } from "../sqlite/index.js";
8
8
  import type { ClientStoreAdapter } from "../types.js";
9
9
  import type { WalletRuntimePaths } from "../wallet/runtime.js";
10
- import type { WalletPrompter, initializeWallet, deleteImportedWalletSeed, previewResetWallet, repairWallet, resetWallet, restoreWalletFromMnemonic, showWalletMnemonic } from "../wallet/lifecycle.js";
10
+ import type { WalletPrompter, initializeWallet, previewResetWallet, repairWallet, resetWallet, showWalletMnemonic } from "../wallet/lifecycle.js";
11
11
  import type { openWalletReadContext } from "../wallet/read/index.js";
12
12
  import { loadRawWalletStateEnvelope, loadWalletState } from "../wallet/state/storage.js";
13
13
  import type { WalletSecretProvider } from "../wallet/state/provider.js";
14
14
  import type { ensureBuiltInMiningSetupIfNeeded, followMiningLog, inspectMiningControlPlane, inspectMiningDomainPromptState, readMiningLog, runForegroundMining, setupBuiltInMining, startBackgroundMining, stopBackgroundMining, updateMiningDomainPrompt } from "../wallet/mining/index.js";
15
15
  import type { anchorDomain, transferBitcoin, buyDomain, claimCogLock, clearDomainDelegate, clearDomainEndpoint, clearDomainMiner, clearField, createField, giveReputation, lockCogToDomain, registerDomain, reclaimCogLock, revokeReputation, sendCog, setField, setDomainCanonical, setDomainDelegate, setDomainEndpoint, setDomainMiner, sellDomain, transferDomain } from "../wallet/tx/index.js";
16
+ import type { CommandHandlerFamily, CommandName } from "./command-registry.js";
17
+ export type { CommandHandlerFamily, CommandName } from "./command-registry.js";
16
18
  export type ProgressOutput = "auto" | "tty" | "none";
17
- export type OutputMode = "text" | "json" | "preview-json";
18
- export type CommandName = "init" | "restore" | "reset" | "repair" | "update" | "sync" | "status" | "client-lock" | "client-change-password" | "client-unlock" | "follow" | "bitcoin-start" | "bitcoin-stop" | "bitcoin-status" | "bitcoin-transfer" | "indexer-start" | "indexer-stop" | "indexer-status" | "anchor" | "domain-anchor" | "register" | "domain-register" | "transfer" | "domain-transfer" | "sell" | "domain-sell" | "unsell" | "domain-unsell" | "buy" | "domain-buy" | "domain-endpoint-set" | "domain-endpoint-clear" | "domain-delegate-set" | "domain-delegate-clear" | "domain-miner-set" | "domain-miner-clear" | "domain-canonical" | "field-list" | "field-show" | "field-create" | "field-set" | "field-clear" | "send" | "claim" | "reclaim" | "cog-send" | "cog-claim" | "cog-reclaim" | "cog-lock" | "rep-give" | "rep-revoke" | "cog-balance" | "cog-locks" | "mine" | "mine-start" | "mine-stop" | "mine-setup" | "mine-prompt" | "mine-prompt-list" | "mine-status" | "mine-log" | "wallet-init" | "wallet-delete" | "wallet-restore" | "wallet-show-mnemonic" | "wallet-status" | "wallet-address" | "wallet-ids" | "address" | "ids" | "balance" | "locks" | "domain-list" | "domains" | "domain-show" | "show" | "fields" | "field";
19
19
  export interface WritableLike {
20
20
  isTTY?: boolean;
21
21
  write(chunk: string): void;
@@ -29,14 +29,15 @@ export interface SignalSource {
29
29
  }
30
30
  export interface ParsedCliArgs {
31
31
  command: CommandName | null;
32
+ commandFamily: CommandHandlerFamily | null;
33
+ invokedCommandTokens: readonly string[] | null;
34
+ invokedCommandPath: string | null;
32
35
  args: string[];
33
36
  help: boolean;
34
37
  version: boolean;
35
- outputMode: OutputMode;
36
38
  dbPath: string | null;
37
39
  dataDir: string | null;
38
40
  progressOutput: ProgressOutput;
39
- seedName: string | null;
40
41
  unlockFor: string | null;
41
42
  assumeYes: boolean;
42
43
  force: boolean;
@@ -125,9 +126,7 @@ export interface CliRunnerContext {
125
126
  loadWalletState?: typeof loadWalletState;
126
127
  loadRawWalletStateEnvelope?: typeof loadRawWalletStateEnvelope;
127
128
  initializeWallet?: typeof initializeWallet;
128
- restoreWalletFromMnemonic?: typeof restoreWalletFromMnemonic;
129
129
  previewResetWallet?: typeof previewResetWallet;
130
- deleteImportedWalletSeed?: typeof deleteImportedWalletSeed;
131
130
  showWalletMnemonic?: typeof showWalletMnemonic;
132
131
  registerDomain?: typeof registerDomain;
133
132
  anchorDomain?: typeof anchorDomain;
@@ -170,7 +169,7 @@ export interface CliRunnerContext {
170
169
  resolveDefaultBitcoindDataDir?: () => string;
171
170
  resolveDefaultClientDatabasePath?: () => string;
172
171
  resolveUpdateCheckStatePath?: () => string;
173
- resolveWalletRuntimePaths?: (seedName?: string | null) => WalletRuntimePaths;
172
+ resolveWalletRuntimePaths?: () => WalletRuntimePaths;
174
173
  }
175
174
  export interface StopSignalWatcher {
176
175
  cleanup(): void;
@@ -4,7 +4,7 @@ function isEligibleForUpdateNotification(parsed, context) {
4
4
  if (parsed.command === "update") {
5
5
  return false;
6
6
  }
7
- if (parsed.outputMode !== "text" || parsed.help || parsed.version) {
7
+ if (parsed.help || parsed.version) {
8
8
  return false;
9
9
  }
10
10
  return context.stdout.isTTY === true || context.stderr.isTTY === true;
@@ -88,7 +88,7 @@ export function getRepairRecommendation(context) {
88
88
  return null;
89
89
  }
90
90
  if (context.localState.availability === "uninitialized") {
91
- return "Run `cogcoin init` to create a new local wallet root.";
91
+ return "Run `cogcoin init` to create or restore a wallet.";
92
92
  }
93
93
  if (context.localState.availability === "local-state-corrupt") {
94
94
  return "Run `cogcoin repair` to recover local wallet state.";
@@ -0,0 +1,5 @@
1
+ import type { WalletAccessContext, WalletLoadedState } from "./types.js";
2
+ export declare function isWalletSecretAccessError(error: unknown): boolean;
3
+ export declare function mapWalletReadAccessError(error: unknown): Error;
4
+ export declare function normalizeLoadedWalletStateIfNeeded(options: WalletAccessContext & WalletLoadedState): Promise<WalletLoadedState>;
5
+ export declare function loadWalletStateForAccess(options: WalletAccessContext): Promise<WalletLoadedState>;
@@ -0,0 +1,79 @@
1
+ import { normalizeWalletStateRecord, persistWalletCoinControlStateIfNeeded, } from "../coin-control.js";
2
+ import { persistNormalizedWalletDescriptorStateIfNeeded } from "../descriptor-normalization.js";
3
+ import { normalizeMiningStateRecord } from "../mining/state.js";
4
+ import { createWalletSecretReference } from "../state/provider.js";
5
+ import { loadWalletState } from "../state/storage.js";
6
+ export function isWalletSecretAccessError(error) {
7
+ const message = error instanceof Error ? error.message : String(error);
8
+ return message.startsWith("wallet_secret_missing_")
9
+ || message.startsWith("wallet_secret_provider_");
10
+ }
11
+ export function mapWalletReadAccessError(error) {
12
+ if (isWalletSecretAccessError(error)) {
13
+ return new Error("wallet_secret_provider_unavailable");
14
+ }
15
+ return new Error("local-state-corrupt");
16
+ }
17
+ export async function normalizeLoadedWalletStateIfNeeded(options) {
18
+ let state = options.state;
19
+ let source = options.source;
20
+ if (options.dataDir !== undefined) {
21
+ const node = await options.attachService({
22
+ dataDir: options.dataDir,
23
+ chain: "main",
24
+ startHeight: 0,
25
+ walletRootId: state.walletRootId,
26
+ });
27
+ try {
28
+ const normalizedDescriptorState = await persistNormalizedWalletDescriptorStateIfNeeded({
29
+ state,
30
+ access: {
31
+ provider: options.provider,
32
+ secretReference: createWalletSecretReference(state.walletRootId),
33
+ },
34
+ paths: options.paths,
35
+ nowUnixMs: options.nowUnixMs,
36
+ replacePrimary: source === "backup",
37
+ rpc: options.rpcFactory(node.rpc),
38
+ });
39
+ state = normalizedDescriptorState.state;
40
+ source = normalizedDescriptorState.changed ? "primary" : source;
41
+ const reconciledCoinControl = await persistWalletCoinControlStateIfNeeded({
42
+ state,
43
+ access: {
44
+ provider: options.provider,
45
+ secretReference: createWalletSecretReference(state.walletRootId),
46
+ },
47
+ paths: options.paths,
48
+ nowUnixMs: options.nowUnixMs,
49
+ replacePrimary: source === "backup",
50
+ rpc: options.rpcFactory(node.rpc),
51
+ });
52
+ state = reconciledCoinControl.state;
53
+ source = reconciledCoinControl.changed ? "primary" : source;
54
+ }
55
+ finally {
56
+ await node.stop?.().catch(() => undefined);
57
+ }
58
+ }
59
+ return {
60
+ state: normalizeWalletStateRecord({
61
+ ...state,
62
+ miningState: normalizeMiningStateRecord(state.miningState),
63
+ }),
64
+ source,
65
+ };
66
+ }
67
+ export async function loadWalletStateForAccess(options) {
68
+ const loaded = await loadWalletState({
69
+ primaryPath: options.paths.walletStatePath,
70
+ backupPath: options.paths.walletStateBackupPath,
71
+ }, {
72
+ provider: options.provider,
73
+ });
74
+ return await normalizeLoadedWalletStateIfNeeded({
75
+ ...options,
76
+ state: loaded.state,
77
+ source: loaded.source,
78
+ });
79
+ }
@@ -0,0 +1,26 @@
1
+ import { type WalletRuntimePaths } from "../runtime.js";
2
+ import { type WalletSecretProvider } from "../state/provider.js";
3
+ import type { WalletManagedCoreContext, WalletManagedCoreDependencies, WalletPrompter, WalletRepairContext, WalletRepairDependencies, WalletSetupContext, WalletSetupDependencies } from "./types.js";
4
+ export declare function pathExists(path: string): Promise<boolean>;
5
+ export declare function walletStateExists(paths: WalletRuntimePaths): Promise<boolean>;
6
+ export declare function resolveWalletManagedCoreContext(options: {
7
+ provider?: WalletSecretProvider;
8
+ nowUnixMs?: number;
9
+ paths?: WalletRuntimePaths;
10
+ } & WalletManagedCoreDependencies): WalletManagedCoreContext;
11
+ export declare function resolveWalletSetupContext(options: {
12
+ dataDir: string;
13
+ prompter: WalletPrompter;
14
+ provider?: WalletSecretProvider;
15
+ nowUnixMs?: number;
16
+ paths?: WalletRuntimePaths;
17
+ } & WalletSetupDependencies): WalletSetupContext;
18
+ export declare function resolveWalletRepairContext(options: {
19
+ dataDir: string;
20
+ databasePath: string;
21
+ provider?: WalletSecretProvider;
22
+ assumeYes?: boolean;
23
+ nowUnixMs?: number;
24
+ paths?: WalletRuntimePaths;
25
+ } & WalletRepairDependencies): WalletRepairContext;
26
+ export declare function acquireWalletControlLock(paths: WalletRuntimePaths, purpose: "wallet-init" | "wallet-show-mnemonic" | "wallet-repair"): Promise<import("../fs/lock.js").FileLockHandle>;
@@ -0,0 +1,58 @@
1
+ import { access, constants } from "node:fs/promises";
2
+ import { attachOrStartIndexerDaemon, probeIndexerDaemon, } from "../../bitcoind/indexer-daemon.js";
3
+ import { createRpcClient } from "../../bitcoind/node.js";
4
+ import { attachOrStartManagedBitcoindService, probeManagedBitcoindService, } from "../../bitcoind/service.js";
5
+ import { acquireFileLock } from "../fs/lock.js";
6
+ import { resolveWalletRuntimePathsForTesting } from "../runtime.js";
7
+ import { createDefaultWalletSecretProvider, } from "../state/provider.js";
8
+ export async function pathExists(path) {
9
+ try {
10
+ await access(path, constants.F_OK);
11
+ return true;
12
+ }
13
+ catch {
14
+ return false;
15
+ }
16
+ }
17
+ export async function walletStateExists(paths) {
18
+ const [hasPrimaryState, hasBackupState] = await Promise.all([
19
+ pathExists(paths.walletStatePath),
20
+ pathExists(paths.walletStateBackupPath),
21
+ ]);
22
+ return hasPrimaryState || hasBackupState;
23
+ }
24
+ export function resolveWalletManagedCoreContext(options) {
25
+ return {
26
+ provider: options.provider ?? createDefaultWalletSecretProvider(),
27
+ paths: options.paths ?? resolveWalletRuntimePathsForTesting(),
28
+ nowUnixMs: options.nowUnixMs ?? Date.now(),
29
+ attachService: options.attachService ?? attachOrStartManagedBitcoindService,
30
+ rpcFactory: options.rpcFactory ?? createRpcClient,
31
+ };
32
+ }
33
+ export function resolveWalletSetupContext(options) {
34
+ return {
35
+ ...resolveWalletManagedCoreContext(options),
36
+ dataDir: options.dataDir,
37
+ prompter: options.prompter,
38
+ };
39
+ }
40
+ export function resolveWalletRepairContext(options) {
41
+ return {
42
+ ...resolveWalletManagedCoreContext(options),
43
+ dataDir: options.dataDir,
44
+ databasePath: options.databasePath,
45
+ assumeYes: options.assumeYes ?? false,
46
+ probeBitcoindService: options.probeBitcoindService ?? probeManagedBitcoindService,
47
+ attachIndexerDaemon: options.attachIndexerDaemon ?? attachOrStartIndexerDaemon,
48
+ probeIndexerDaemon: options.probeIndexerDaemon ?? probeIndexerDaemon,
49
+ requestMiningPreemption: options.requestMiningPreemption,
50
+ startBackgroundMining: options.startBackgroundMining,
51
+ };
52
+ }
53
+ export async function acquireWalletControlLock(paths, purpose) {
54
+ return await acquireFileLock(paths.walletControlLockPath, {
55
+ purpose,
56
+ walletRootId: null,
57
+ });
58
+ }
@@ -0,0 +1,15 @@
1
+ import { attachOrStartManagedBitcoindService } from "../../bitcoind/service.js";
2
+ import { createRpcClient } from "../../bitcoind/node.js";
3
+ import type { ManagedCoreWalletReplicaStatus } from "../../bitcoind/types.js";
4
+ import type { WalletRuntimePaths } from "../runtime.js";
5
+ import { type WalletSecretProvider } from "../state/provider.js";
6
+ import type { WalletStateV1 } from "../types.js";
7
+ import type { WalletLifecycleRpcClient, WalletManagedCoreDependencies } from "./types.js";
8
+ export declare function sanitizeWalletName(walletRootId: string): string;
9
+ export declare function importDescriptorIntoManagedCoreWallet(state: WalletStateV1, provider: WalletSecretProvider, paths: WalletRuntimePaths, dataDir: string, nowUnixMs: number, attachService?: typeof attachOrStartManagedBitcoindService, rpcFactory?: (config: Parameters<typeof createRpcClient>[0]) => WalletLifecycleRpcClient): Promise<WalletStateV1>;
10
+ export declare function recreateManagedCoreWalletReplica(state: WalletStateV1, provider: WalletSecretProvider, paths: WalletRuntimePaths, dataDir: string, nowUnixMs: number, options?: WalletManagedCoreDependencies): Promise<WalletStateV1>;
11
+ export declare function verifyManagedCoreWalletReplica(state: WalletStateV1, dataDir: string, dependencies?: WalletManagedCoreDependencies & {
12
+ nodeHandle?: {
13
+ rpc: Parameters<typeof createRpcClient>[0];
14
+ };
15
+ }): Promise<ManagedCoreWalletReplicaStatus>;
@@ -0,0 +1,197 @@
1
+ import { rename } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+ import { attachOrStartManagedBitcoindService, createManagedWalletReplica } from "../../bitcoind/service.js";
4
+ import { createRpcClient } from "../../bitcoind/node.js";
5
+ import { resolveNormalizedWalletDescriptorState, } from "../descriptor-normalization.js";
6
+ import { createWalletSecretReference } from "../state/provider.js";
7
+ import { saveWalletState } from "../state/storage.js";
8
+ import { withUnlockedManagedCoreWallet } from "../managed-core-wallet.js";
9
+ import { pathExists } from "./context.js";
10
+ export function sanitizeWalletName(walletRootId) {
11
+ return `cogcoin-${walletRootId}`.replace(/[^a-zA-Z0-9._-]+/g, "-").slice(0, 63);
12
+ }
13
+ export async function importDescriptorIntoManagedCoreWallet(state, provider, paths, dataDir, nowUnixMs, attachService = attachOrStartManagedBitcoindService, rpcFactory = createRpcClient) {
14
+ const node = await attachService({
15
+ dataDir,
16
+ chain: "main",
17
+ startHeight: 0,
18
+ walletRootId: state.walletRootId,
19
+ managedWalletPassphrase: state.managedCoreWallet.internalPassphrase,
20
+ });
21
+ const rpc = rpcFactory(node.rpc);
22
+ await createManagedWalletReplica(rpc, state.walletRootId, {
23
+ managedWalletPassphrase: state.managedCoreWallet.internalPassphrase,
24
+ });
25
+ const normalizedDescriptors = await resolveNormalizedWalletDescriptorState(state, rpc);
26
+ const walletName = sanitizeWalletName(state.walletRootId);
27
+ await withUnlockedManagedCoreWallet({
28
+ rpc,
29
+ walletName,
30
+ internalPassphrase: state.managedCoreWallet.internalPassphrase,
31
+ run: async () => {
32
+ const importResults = await rpc.importDescriptors(walletName, [{
33
+ desc: normalizedDescriptors.privateExternal,
34
+ timestamp: state.walletBirthTime,
35
+ active: false,
36
+ internal: false,
37
+ range: [0, state.descriptor.rangeEnd],
38
+ }]);
39
+ if (!importResults.every((result) => result.success)) {
40
+ throw new Error(`wallet_descriptor_import_failed_${JSON.stringify(importResults)}`);
41
+ }
42
+ },
43
+ });
44
+ const derivedFunding = await rpc.deriveAddresses(normalizedDescriptors.publicExternal, [0, 0]);
45
+ if (derivedFunding[0] !== state.funding.address) {
46
+ throw new Error("wallet_funding_address_verification_failed");
47
+ }
48
+ const descriptors = await rpc.listDescriptors(walletName);
49
+ const importedDescriptor = descriptors.descriptors.find((entry) => entry.desc === normalizedDescriptors.publicExternal);
50
+ if (importedDescriptor == null) {
51
+ throw new Error("wallet_descriptor_not_present_after_import");
52
+ }
53
+ const verifiedReplica = {
54
+ walletRootId: state.walletRootId,
55
+ walletName,
56
+ loaded: true,
57
+ descriptors: true,
58
+ privateKeysEnabled: true,
59
+ created: false,
60
+ proofStatus: "ready",
61
+ descriptorChecksum: normalizedDescriptors.checksum,
62
+ fundingAddress0: state.funding.address,
63
+ fundingScriptPubKeyHex0: state.funding.scriptPubKeyHex,
64
+ message: null,
65
+ };
66
+ const nextState = {
67
+ ...state,
68
+ stateRevision: state.stateRevision + 1,
69
+ lastWrittenAtUnixMs: nowUnixMs,
70
+ descriptor: {
71
+ ...state.descriptor,
72
+ privateExternal: normalizedDescriptors.privateExternal,
73
+ publicExternal: normalizedDescriptors.publicExternal,
74
+ checksum: normalizedDescriptors.checksum,
75
+ },
76
+ managedCoreWallet: {
77
+ ...state.managedCoreWallet,
78
+ walletName,
79
+ descriptorChecksum: normalizedDescriptors.checksum,
80
+ walletAddress: verifiedReplica.fundingAddress0 ?? null,
81
+ walletScriptPubKeyHex: verifiedReplica.fundingScriptPubKeyHex0 ?? null,
82
+ proofStatus: "ready",
83
+ lastImportedAtUnixMs: nowUnixMs,
84
+ lastVerifiedAtUnixMs: nowUnixMs,
85
+ },
86
+ };
87
+ await saveWalletState({
88
+ primaryPath: paths.walletStatePath,
89
+ backupPath: paths.walletStateBackupPath,
90
+ }, nextState, {
91
+ provider,
92
+ secretReference: createWalletSecretReference(state.walletRootId),
93
+ });
94
+ return nextState;
95
+ }
96
+ export async function recreateManagedCoreWalletReplica(state, provider, paths, dataDir, nowUnixMs, options = {}) {
97
+ const walletName = sanitizeWalletName(state.walletRootId);
98
+ const walletDir = join(dataDir, "wallets", walletName);
99
+ const quarantineDir = `${walletDir}.quarantine-${nowUnixMs}`;
100
+ const node = await (options.attachService ?? attachOrStartManagedBitcoindService)({
101
+ dataDir,
102
+ chain: "main",
103
+ startHeight: 0,
104
+ walletRootId: state.walletRootId,
105
+ managedWalletPassphrase: state.managedCoreWallet.internalPassphrase,
106
+ });
107
+ const rpc = (options.rpcFactory ?? createRpcClient)(node.rpc);
108
+ if (rpc.unloadWallet != null) {
109
+ await rpc.unloadWallet(walletName, false).catch(() => undefined);
110
+ }
111
+ if (await pathExists(walletDir)) {
112
+ await rename(walletDir, quarantineDir).catch(() => undefined);
113
+ }
114
+ return importDescriptorIntoManagedCoreWallet({
115
+ ...state,
116
+ managedCoreWallet: {
117
+ ...state.managedCoreWallet,
118
+ proofStatus: "not-proven",
119
+ },
120
+ }, provider, paths, dataDir, nowUnixMs, options.attachService, options.rpcFactory);
121
+ }
122
+ export async function verifyManagedCoreWalletReplica(state, dataDir, dependencies = {}) {
123
+ const walletName = state.managedCoreWallet.walletName;
124
+ try {
125
+ const node = dependencies.nodeHandle ?? await (dependencies.attachService ?? attachOrStartManagedBitcoindService)({
126
+ dataDir,
127
+ chain: "main",
128
+ startHeight: 0,
129
+ walletRootId: state.walletRootId,
130
+ });
131
+ const rpc = (dependencies.rpcFactory ?? createRpcClient)(node.rpc);
132
+ const info = await rpc.getWalletInfo(walletName);
133
+ const descriptors = await rpc.listDescriptors(walletName);
134
+ const matchingDescriptor = state.managedCoreWallet.descriptorChecksum === null
135
+ ? null
136
+ : descriptors.descriptors.find((entry) => entry.desc.endsWith(`#${state.managedCoreWallet.descriptorChecksum}`));
137
+ if (matchingDescriptor == null) {
138
+ return {
139
+ walletRootId: state.walletRootId,
140
+ walletName,
141
+ loaded: true,
142
+ descriptors: info.descriptors,
143
+ privateKeysEnabled: info.private_keys_enabled,
144
+ created: false,
145
+ proofStatus: "missing",
146
+ descriptorChecksum: state.managedCoreWallet.descriptorChecksum,
147
+ fundingAddress0: state.managedCoreWallet.walletAddress,
148
+ fundingScriptPubKeyHex0: state.managedCoreWallet.walletScriptPubKeyHex,
149
+ message: "Expected descriptor is missing from the managed Core wallet.",
150
+ };
151
+ }
152
+ const derived = await rpc.deriveAddresses(state.descriptor.publicExternal, [0, 0]);
153
+ if (derived[0] !== state.funding.address) {
154
+ return {
155
+ walletRootId: state.walletRootId,
156
+ walletName,
157
+ loaded: true,
158
+ descriptors: info.descriptors,
159
+ privateKeysEnabled: info.private_keys_enabled,
160
+ created: false,
161
+ proofStatus: "mismatch",
162
+ descriptorChecksum: state.managedCoreWallet.descriptorChecksum,
163
+ fundingAddress0: derived[0] ?? null,
164
+ fundingScriptPubKeyHex0: null,
165
+ message: "The managed Core wallet funding address does not match the trusted wallet state.",
166
+ };
167
+ }
168
+ return {
169
+ walletRootId: state.walletRootId,
170
+ walletName,
171
+ loaded: true,
172
+ descriptors: info.descriptors,
173
+ privateKeysEnabled: info.private_keys_enabled,
174
+ created: false,
175
+ proofStatus: "ready",
176
+ descriptorChecksum: state.managedCoreWallet.descriptorChecksum,
177
+ fundingAddress0: state.funding.address,
178
+ fundingScriptPubKeyHex0: state.funding.scriptPubKeyHex,
179
+ message: null,
180
+ };
181
+ }
182
+ catch (error) {
183
+ return {
184
+ walletRootId: state.walletRootId,
185
+ walletName,
186
+ loaded: false,
187
+ descriptors: false,
188
+ privateKeysEnabled: false,
189
+ created: false,
190
+ proofStatus: "not-proven",
191
+ descriptorChecksum: state.managedCoreWallet.descriptorChecksum,
192
+ fundingAddress0: state.managedCoreWallet.walletAddress,
193
+ fundingScriptPubKeyHex0: state.managedCoreWallet.walletScriptPubKeyHex,
194
+ message: error instanceof Error ? error.message : String(error),
195
+ };
196
+ }
197
+ }
@@ -0,0 +1,10 @@
1
+ import type { ManagedServicePaths } from "../../bitcoind/service-paths.js";
2
+ import type { WalletStateV1 } from "../types.js";
3
+ import type { WalletBitcoindRepairStageResult, WalletRepairContext } from "./types.js";
4
+ export declare function repairManagedBitcoindStage(options: {
5
+ context: WalletRepairContext;
6
+ servicePaths: ManagedServicePaths;
7
+ state: WalletStateV1;
8
+ recoveredFromBackup: boolean;
9
+ repairStateNeedsPersist: boolean;
10
+ }): Promise<WalletBitcoindRepairStageResult>;