@cogcoin/client 0.5.5 → 0.5.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 (53) hide show
  1. package/README.md +1 -1
  2. package/dist/bitcoind/bootstrap/chainstate.d.ts +2 -1
  3. package/dist/bitcoind/bootstrap/chainstate.js +4 -1
  4. package/dist/bitcoind/bootstrap/controller.d.ts +4 -1
  5. package/dist/bitcoind/bootstrap/controller.js +42 -5
  6. package/dist/bitcoind/bootstrap/headers.d.ts +12 -0
  7. package/dist/bitcoind/bootstrap/headers.js +95 -10
  8. package/dist/bitcoind/client/factory.js +11 -2
  9. package/dist/bitcoind/client/managed-client.d.ts +1 -1
  10. package/dist/bitcoind/client/managed-client.js +2 -2
  11. package/dist/bitcoind/client/sync-engine.js +48 -13
  12. package/dist/bitcoind/indexer-daemon.d.ts +7 -0
  13. package/dist/bitcoind/indexer-daemon.js +31 -22
  14. package/dist/bitcoind/processing-start-height.d.ts +7 -0
  15. package/dist/bitcoind/processing-start-height.js +9 -0
  16. package/dist/bitcoind/progress/controller.js +1 -0
  17. package/dist/bitcoind/progress/formatting.js +4 -1
  18. package/dist/bitcoind/retryable-rpc.d.ts +11 -0
  19. package/dist/bitcoind/retryable-rpc.js +30 -0
  20. package/dist/bitcoind/service.d.ts +16 -1
  21. package/dist/bitcoind/service.js +228 -115
  22. package/dist/bitcoind/testing.d.ts +1 -1
  23. package/dist/bitcoind/testing.js +1 -1
  24. package/dist/bitcoind/types.d.ts +1 -0
  25. package/dist/cli/commands/follow.js +9 -0
  26. package/dist/cli/commands/service-runtime.js +150 -134
  27. package/dist/cli/commands/sync.js +9 -0
  28. package/dist/cli/commands/wallet-admin.js +77 -21
  29. package/dist/cli/context.js +4 -2
  30. package/dist/cli/mutation-json.js +2 -0
  31. package/dist/cli/output.js +2 -0
  32. package/dist/cli/parse.d.ts +1 -1
  33. package/dist/cli/parse.js +6 -0
  34. package/dist/cli/preview-json.js +2 -0
  35. package/dist/cli/runner.js +1 -0
  36. package/dist/cli/types.d.ts +6 -3
  37. package/dist/cli/types.js +1 -1
  38. package/dist/cli/wallet-format.js +134 -14
  39. package/dist/wallet/lifecycle.d.ts +6 -0
  40. package/dist/wallet/lifecycle.js +109 -37
  41. package/dist/wallet/read/context.js +10 -4
  42. package/dist/wallet/reset.d.ts +61 -2
  43. package/dist/wallet/reset.js +208 -63
  44. package/dist/wallet/root-resolution.d.ts +20 -0
  45. package/dist/wallet/root-resolution.js +37 -0
  46. package/dist/wallet/runtime.d.ts +1 -0
  47. package/dist/wallet/runtime.js +1 -0
  48. package/dist/wallet/state/crypto.d.ts +3 -0
  49. package/dist/wallet/state/crypto.js +3 -0
  50. package/dist/wallet/state/storage.d.ts +7 -1
  51. package/dist/wallet/state/storage.js +39 -0
  52. package/dist/wallet/types.d.ts +1 -0
  53. package/package.json +1 -1
@@ -1,3 +1,3 @@
1
1
  import type { ParsedCliArgs } from "./types.js";
2
- export declare const HELP_TEXT = "Usage: cogcoin <command> [options]\n\nCommands:\n status Show wallet-aware local service and chain status\n status --output json Emit the stable v1 machine-readable status envelope\n bitcoin start Start the managed Bitcoin daemon\n bitcoin stop Stop the managed Bitcoin daemon and paired indexer\n bitcoin status Show managed Bitcoin daemon status without starting it\n indexer start Start the managed Cogcoin indexer (and bitcoind if needed)\n indexer stop Stop the managed Cogcoin indexer only\n indexer status Show managed Cogcoin indexer status without starting it\n init Initialize a new local wallet root\n init --output json Emit the stable v1 machine-readable init result envelope\n restore Restore a fresh local wallet from a 24-word mnemonic; run sync afterward\n reset Factory-reset local Cogcoin state with interactive retention prompts\n repair Recover bounded wallet/indexer/runtime state\n unlock Clear an explicit wallet lock and unlock for a limited duration\n wallet address Alias for address\n wallet ids Alias for ids\n hooks enable mining Enable a validated custom mining hook\n hooks disable mining Return to built-in mining hooks\n hooks status Show mining hook validation and trust status\n mine Run the miner in the foreground\n mine start Start the miner as a background worker\n mine stop Stop the active background miner\n mine setup Configure the built-in mining provider\n mine setup --output json\n Emit the stable v1 machine-readable mine setup result envelope\n mine status Show mining control-plane health and readiness\n mine log Show recent mining control-plane events\n anchor <domain> Anchor an owned unanchored domain with the Tx1/Tx2 family\n register <domain> [--from <identity>]\n Register a root domain or subdomain\n transfer <domain> --to <btc-target>\n Transfer an unanchored domain to another BTC identity\n sell <domain> <price> List an unanchored domain for sale in COG\n unsell <domain> Clear an active domain listing\n buy <domain> [--from <identity>]\n Buy an unanchored listed domain in COG\n send <amount> --to <btc-target>\n Send COG from one local identity to another BTC target\n claim <lock-id> --preimage <32-byte-hex>\n Claim an active COG lock before timeout\n reclaim <lock-id> Reclaim an expired COG lock as the original locker\n cog lock <amount> --to-domain <domain> (--for <blocks-or-duration> | --until-height <height>) --condition <sha256hex>\n Lock COG to an anchored recipient domain\n wallet status Show detailed wallet-local status and service health\n wallet init Initialize a new local wallet root\n wallet restore Restore a fresh local wallet from a 24-word mnemonic; run sync afterward\n wallet unlock Clear an explicit wallet lock and unlock for a limited duration\n wallet lock Lock the local wallet and disable on-demand unlock\n wallet export <path> Export a portable encrypted wallet archive\n wallet import <path> Import a portable encrypted wallet archive\n address Show the BTC funding identity for this wallet\n ids List locally controlled identities\n balance Show per-identity COG balances\n locks Show locally related active COG locks\n domain list Alias for domains\n domain show <domain> Alias for show <domain>\n domains [--anchored] [--listed] [--mineable]\n Show locally related domains\n show <domain> Show one domain and its local-wallet relationship\n fields <domain> List current fields on a domain\n field <domain> <field> Show one current field value\n field create <domain> <field>\n Create a new anchored field, optionally with an initial value\n field set <domain> <field>\n Update an existing anchored field value\n field clear <domain> <field>\n Clear an existing anchored field value\n rep give <source-domain> <target-domain> <amount>\n Burn COG as anchored-domain reputation support\n rep revoke <source-domain> <target-domain> <amount>\n Revoke visible support without refunding burned COG\n\nOptions:\n --db <path> Override the SQLite database path\n --data-dir <path> Override the managed bitcoin datadir\n --for <duration> Unlock duration like 15m, 2h, or 1d when unlocking explicitly\n --message <text> Founding message text for anchor\n --to <btc-target> Transfer or send target as an address or spk:<hex>\n --from <identity> Resolve sender identity as id:<n>, domain:<name>, address, or spk:<hex>\n --to-domain <domain>\n Recipient domain for cog lock\n --condition <sha256hex>\n 32-byte lock condition hash\n --until-height <height>\n Absolute timeout height for cog lock\n --preimage <32-byte-hex>\n Claim preimage for an active lock\n --review <text> Optional public review text for reputation operations\n --text <utf8> UTF-8 payload text for endpoint or field writes\n --json <json> UTF-8 payload JSON text for endpoint or field writes\n --bytes <spec> Payload bytes as hex:<hex> or @<path>\n --permanent Create the field as permanent\n --format <spec> Advanced field format as raw:<u8>\n --value <spec> Advanced field value as hex:<hex>, @<path>, or utf8:<text>\n --claimable Show only currently claimable locks\n --reclaimable Show only currently reclaimable locks\n --anchored Show only anchored domains\n --listed Show only currently listed domains\n --mineable Show only locally mineable root domains\n --limit <n> Limit list rows (1..1000)\n --all Show all rows for list commands\n --verify Run full custom-hook verification\n --follow Follow mining log output\n --output <mode> Output mode: text, json, or preview-json\n --progress <mode> Progress output mode: auto, tty, or none\n --force-race Allow a visible root registration race\n --yes Approve eligible plain yes/no mutation confirmations non-interactively\n --help Show help\n --version Show package version\n\nQuickstart:\n 1. Run `cogcoin init` to create the wallet.\n 2. Run `cogcoin sync` to bootstrap assumeutxo and the managed Bitcoin/indexer state.\n 3. Run `cogcoin address`, then fund the wallet with about 0.0015 BTC so you can buy a 6+ character domain to start mining and still keep BTC available for mining transaction fees.\n\nExamples:\n cogcoin status --output json\n cogcoin bitcoin status\n cogcoin indexer status\n cogcoin init --output json\n cogcoin wallet address\n cogcoin domain list --mineable\n cogcoin register alpha-child\n cogcoin register weatherbot --from id:1\n cogcoin anchor alpha\n cogcoin buy alpha\n cogcoin buy alpha --from id:1\n cogcoin field create alpha bio --text \"hello\"\n cogcoin rep give alpha beta 10 --review \"great operator\"\n cogcoin hooks status\n cogcoin mine setup --output json\n cogcoin register alpha-child --output preview-json\n cogcoin mine status\n";
2
+ export declare const HELP_TEXT = "Usage: cogcoin <command> [options]\n\nCommands:\n status Show wallet-aware local service and chain status\n status --output json Emit the stable v1 machine-readable status envelope\n bitcoin start Start the managed Bitcoin daemon\n bitcoin stop Stop the managed Bitcoin daemon and paired indexer\n bitcoin status Show managed Bitcoin daemon status without starting it\n indexer start Start the managed Cogcoin indexer (and bitcoind if needed)\n indexer stop Stop the managed Cogcoin indexer only\n indexer status Show managed Cogcoin indexer status without starting it\n init Initialize a new local wallet root\n init --output json Emit the stable v1 machine-readable init result envelope\n restore Restore a fresh local wallet from a 24-word mnemonic; run sync afterward\n reset Factory-reset local Cogcoin state with interactive retention prompts\n repair Recover bounded wallet/indexer/runtime state\n unlock Clear an explicit wallet lock and unlock for a limited duration\n wallet address Alias for address\n wallet ids Alias for ids\n hooks enable mining Enable a validated custom mining hook\n hooks disable mining Return to built-in mining hooks\n hooks status Show mining hook validation and trust status\n mine Run the miner in the foreground\n mine start Start the miner as a background worker\n mine stop Stop the active background miner\n mine setup Configure the built-in mining provider\n mine setup --output json\n Emit the stable v1 machine-readable mine setup result envelope\n mine status Show mining control-plane health and readiness\n mine log Show recent mining control-plane events\n anchor <domain> Anchor an owned unanchored domain with the Tx1/Tx2 family\n register <domain> [--from <identity>]\n Register a root domain or subdomain\n transfer <domain> --to <btc-target>\n Transfer an unanchored domain to another BTC identity\n sell <domain> <price> List an unanchored domain for sale in COG\n unsell <domain> Clear an active domain listing\n buy <domain> [--from <identity>]\n Buy an unanchored listed domain in COG\n send <amount> --to <btc-target>\n Send COG from one local identity to another BTC target\n claim <lock-id> --preimage <32-byte-hex>\n Claim an active COG lock before timeout\n reclaim <lock-id> Reclaim an expired COG lock as the original locker\n cog lock <amount> --to-domain <domain> (--for <blocks-or-duration> | --until-height <height>) --condition <sha256hex>\n Lock COG to an anchored recipient domain\n wallet status Show detailed wallet-local status and service health\n wallet init Initialize a new local wallet root\n wallet restore Restore a fresh local wallet from a 24-word mnemonic; run sync afterward\n wallet show-mnemonic Reveal the initialized wallet recovery phrase after typed confirmation\n wallet unlock Clear an explicit wallet lock and unlock for a limited duration\n wallet lock Lock the local wallet and disable on-demand unlock\n wallet export <path> Export a portable encrypted wallet archive\n wallet import <path> Import a portable encrypted wallet archive\n address Show the BTC funding identity for this wallet\n ids List locally controlled identities\n balance Show per-identity COG balances\n locks Show locally related active COG locks\n domain list Alias for domains\n domain show <domain> Alias for show <domain>\n domains [--anchored] [--listed] [--mineable]\n Show locally related domains\n show <domain> Show one domain and its local-wallet relationship\n fields <domain> List current fields on a domain\n field <domain> <field> Show one current field value\n field create <domain> <field>\n Create a new anchored field, optionally with an initial value\n field set <domain> <field>\n Update an existing anchored field value\n field clear <domain> <field>\n Clear an existing anchored field value\n rep give <source-domain> <target-domain> <amount>\n Burn COG as anchored-domain reputation support\n rep revoke <source-domain> <target-domain> <amount>\n Revoke visible support without refunding burned COG\n\nOptions:\n --db <path> Override the SQLite database path\n --data-dir <path> Override the managed bitcoin datadir\n --for <duration> Unlock duration like 15m, 2h, or 1d when unlocking explicitly\n --message <text> Founding message text for anchor\n --to <btc-target> Transfer or send target as an address or spk:<hex>\n --from <identity> Resolve sender identity as id:<n>, domain:<name>, address, or spk:<hex>\n --to-domain <domain>\n Recipient domain for cog lock\n --condition <sha256hex>\n 32-byte lock condition hash\n --until-height <height>\n Absolute timeout height for cog lock\n --preimage <32-byte-hex>\n Claim preimage for an active lock\n --review <text> Optional public review text for reputation operations\n --text <utf8> UTF-8 payload text for endpoint or field writes\n --json <json> UTF-8 payload JSON text for endpoint or field writes\n --bytes <spec> Payload bytes as hex:<hex> or @<path>\n --permanent Create the field as permanent\n --format <spec> Advanced field format as raw:<u8>\n --value <spec> Advanced field value as hex:<hex>, @<path>, or utf8:<text>\n --claimable Show only currently claimable locks\n --reclaimable Show only currently reclaimable locks\n --anchored Show only anchored domains\n --listed Show only currently listed domains\n --mineable Show only locally mineable root domains\n --limit <n> Limit list rows (1..1000)\n --all Show all rows for list commands\n --verify Run full custom-hook verification\n --follow Follow mining log output\n --output <mode> Output mode: text, json, or preview-json\n --progress <mode> Progress output mode: auto, tty, or none\n --force-race Allow a visible root registration race\n --yes Approve eligible plain yes/no mutation confirmations non-interactively\n --help Show help\n --version Show package version\n\nQuickstart:\n 1. Run `cogcoin init` to create the wallet.\n 2. Run `cogcoin sync` to bootstrap assumeutxo and the managed Bitcoin/indexer state.\n 3. Run `cogcoin address`, then fund the wallet with about 0.0015 BTC so you can buy a 6+ character domain to start mining and still keep BTC available for mining transaction fees.\n\nExamples:\n cogcoin status --output json\n cogcoin bitcoin status\n cogcoin indexer status\n cogcoin init --output json\n cogcoin wallet address\n cogcoin domain list --mineable\n cogcoin register alpha-child\n cogcoin register weatherbot --from id:1\n cogcoin anchor alpha\n cogcoin buy alpha\n cogcoin buy alpha --from id:1\n cogcoin field create alpha bio --text \"hello\"\n cogcoin rep give alpha beta 10 --review \"great operator\"\n cogcoin hooks status\n cogcoin mine setup --output json\n cogcoin register alpha-child --output preview-json\n cogcoin mine status\n";
3
3
  export declare function parseCliArgs(argv: string[]): ParsedCliArgs;
package/dist/cli/parse.js CHANGED
@@ -48,6 +48,7 @@ Commands:
48
48
  wallet status Show detailed wallet-local status and service health
49
49
  wallet init Initialize a new local wallet root
50
50
  wallet restore Restore a fresh local wallet from a 24-word mnemonic; run sync afterward
51
+ wallet show-mnemonic Reveal the initialized wallet recovery phrase after typed confirmation
51
52
  wallet unlock Clear an explicit wallet lock and unlock for a limited duration
52
53
  wallet lock Lock the local wallet and disable on-demand unlock
53
54
  wallet export <path> Export a portable encrypted wallet archive
@@ -451,6 +452,11 @@ export function parseCliArgs(argv) {
451
452
  index += 1;
452
453
  continue;
453
454
  }
455
+ if (subcommand === "show-mnemonic") {
456
+ command = "wallet-show-mnemonic";
457
+ index += 1;
458
+ continue;
459
+ }
454
460
  if (subcommand === "unlock") {
455
461
  command = "wallet-unlock";
456
462
  index += 1;
@@ -234,6 +234,7 @@ export function buildResetPreviewData(result) {
234
234
  trackedProcessKinds: result.trackedProcessKinds,
235
235
  willDeleteOsSecrets: result.willDeleteOsSecrets,
236
236
  bootstrapSnapshot: result.bootstrapSnapshot,
237
+ bitcoinDataDir: result.bitcoinDataDir,
237
238
  walletPrompt: result.walletPrompt,
238
239
  },
239
240
  operation: {
@@ -241,6 +242,7 @@ export function buildResetPreviewData(result) {
241
242
  confirmationPhrase: result.confirmationPhrase,
242
243
  walletPrompt: result.walletPrompt,
243
244
  bootstrapSnapshot: result.bootstrapSnapshot,
245
+ bitcoinDataDir: result.bitcoinDataDir,
244
246
  trackedProcessKinds: result.trackedProcessKinds,
245
247
  willDeleteOsSecrets: result.willDeleteOsSecrets,
246
248
  removedPaths: result.removedPaths,
@@ -77,6 +77,7 @@ export async function runCli(argv, contextOverrides = {}) {
77
77
  || parsed.command === "wallet-import"
78
78
  || parsed.command === "wallet-init"
79
79
  || parsed.command === "wallet-restore"
80
+ || parsed.command === "wallet-show-mnemonic"
80
81
  || parsed.command === "wallet-unlock"
81
82
  || parsed.command === "wallet-lock") {
82
83
  return runWalletAdminCommand(parsed, context);
@@ -5,17 +5,17 @@ import { attachOrStartManagedBitcoindService, probeManagedBitcoindService, stopM
5
5
  import { openSqliteStore } from "../sqlite/index.js";
6
6
  import type { ClientStoreAdapter } from "../types.js";
7
7
  import type { WalletRuntimePaths } from "../wallet/runtime.js";
8
- import type { exportWallet, WalletPrompter, importWallet, initializeWallet, lockWallet, previewResetWallet, repairWallet, resetWallet, restoreWalletFromMnemonic, unlockWallet } from "../wallet/lifecycle.js";
8
+ import type { exportWallet, WalletPrompter, importWallet, initializeWallet, lockWallet, previewResetWallet, repairWallet, resetWallet, restoreWalletFromMnemonic, showWalletMnemonic, unlockWallet } from "../wallet/lifecycle.js";
9
9
  import type { openWalletReadContext } from "../wallet/read/index.js";
10
10
  import { loadWalletExplicitLock } from "../wallet/state/explicit-lock.js";
11
11
  import { loadUnlockSession } from "../wallet/state/session.js";
12
- import { loadWalletState } from "../wallet/state/storage.js";
12
+ import { loadRawWalletStateEnvelope, loadWalletState } from "../wallet/state/storage.js";
13
13
  import type { WalletSecretProvider } from "../wallet/state/provider.js";
14
14
  import type { disableMiningHooks, enableMiningHooks, followMiningLog, inspectMiningControlPlane, readMiningLog, runForegroundMining, setupBuiltInMining, startBackgroundMining, stopBackgroundMining } from "../wallet/mining/index.js";
15
15
  import type { anchorDomain, 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
16
  export type ProgressOutput = "auto" | "tty" | "none";
17
17
  export type OutputMode = "text" | "json" | "preview-json";
18
- export type CommandName = "init" | "restore" | "reset" | "repair" | "sync" | "status" | "follow" | "bitcoin-start" | "bitcoin-stop" | "bitcoin-status" | "indexer-start" | "indexer-stop" | "indexer-status" | "unlock" | "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" | "hooks-mining-enable" | "hooks-mining-disable" | "hooks-mining-status" | "mine" | "mine-start" | "mine-stop" | "mine-setup" | "mine-status" | "mine-log" | "wallet-export" | "wallet-import" | "wallet-init" | "wallet-restore" | "wallet-lock" | "wallet-unlock" | "wallet-status" | "wallet-address" | "wallet-ids" | "address" | "ids" | "balance" | "locks" | "domain-list" | "domains" | "domain-show" | "show" | "fields" | "field";
18
+ export type CommandName = "init" | "restore" | "reset" | "repair" | "sync" | "status" | "follow" | "bitcoin-start" | "bitcoin-stop" | "bitcoin-status" | "indexer-start" | "indexer-stop" | "indexer-status" | "unlock" | "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" | "hooks-mining-enable" | "hooks-mining-disable" | "hooks-mining-status" | "mine" | "mine-start" | "mine-stop" | "mine-setup" | "mine-status" | "mine-log" | "wallet-export" | "wallet-import" | "wallet-init" | "wallet-restore" | "wallet-show-mnemonic" | "wallet-lock" | "wallet-unlock" | "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;
@@ -93,6 +93,7 @@ export interface CliRunnerContext {
93
93
  store: ClientStoreAdapter;
94
94
  databasePath?: string;
95
95
  dataDir?: string;
96
+ walletRootId?: string;
96
97
  progressOutput?: ProgressOutput;
97
98
  }) => Promise<ManagedClientLike>;
98
99
  attachManagedBitcoindService?: typeof attachOrStartManagedBitcoindService;
@@ -106,6 +107,7 @@ export interface CliRunnerContext {
106
107
  inspectPassiveClientStatus?: typeof inspectPassiveClientStatus;
107
108
  openWalletReadContext?: typeof openWalletReadContext;
108
109
  loadWalletState?: typeof loadWalletState;
110
+ loadRawWalletStateEnvelope?: typeof loadRawWalletStateEnvelope;
109
111
  loadUnlockSession?: typeof loadUnlockSession;
110
112
  loadWalletExplicitLock?: typeof loadWalletExplicitLock;
111
113
  initializeWallet?: typeof initializeWallet;
@@ -113,6 +115,7 @@ export interface CliRunnerContext {
113
115
  previewResetWallet?: typeof previewResetWallet;
114
116
  exportWallet?: typeof exportWallet;
115
117
  importWallet?: typeof importWallet;
118
+ showWalletMnemonic?: typeof showWalletMnemonic;
116
119
  unlockWallet?: typeof unlockWallet;
117
120
  lockWallet?: typeof lockWallet;
118
121
  registerDomain?: typeof registerDomain;
package/dist/cli/types.js CHANGED
@@ -5,4 +5,4 @@ import { attachOrStartManagedBitcoindService, probeManagedBitcoindService, stopM
5
5
  import { openSqliteStore } from "../sqlite/index.js";
6
6
  import { loadWalletExplicitLock } from "../wallet/state/explicit-lock.js";
7
7
  import { loadUnlockSession } from "../wallet/state/session.js";
8
- import { loadWalletState } from "../wallet/state/storage.js";
8
+ import { loadRawWalletStateEnvelope, loadWalletState } from "../wallet/state/storage.js";
@@ -329,24 +329,144 @@ function appendWalletAvailability(lines, context) {
329
329
  lines.push(`Mutation note: ${mutationRecommendation}`);
330
330
  }
331
331
  }
332
- export function formatWalletOverviewReport(context) {
332
+ function overviewEntry(text, ok) {
333
+ return { text, ok };
334
+ }
335
+ function formatOverviewSection(header, entries) {
336
+ return [header, ...entries.map((entry) => `${entry.ok ? "✓" : "✗"} ${entry.text}`)].join("\n");
337
+ }
338
+ function isMiningOverviewOk(mining) {
339
+ return mining.runtime.bitcoindHealth === "ready"
340
+ && mining.runtime.nodeHealth === "synced"
341
+ && mining.runtime.indexerHealth === "synced"
342
+ && mining.runtime.miningState !== "repair-required"
343
+ && mining.runtime.miningState !== "paused-stale"
344
+ && !(mining.runtime.miningState === "paused" && mining.runtime.liveMiningFamilyInMempool === true);
345
+ }
346
+ function buildOverviewPathsSection(context) {
347
+ return [
348
+ overviewEntry(`DB path: ${context.databasePath}`, true),
349
+ overviewEntry(`Bitcoin datadir: ${context.dataDir}`, true),
350
+ ];
351
+ }
352
+ function buildOverviewWalletSection(context) {
353
+ const walletRoot = context.model?.walletRootId ?? context.localState.walletRootId ?? context.nodeStatus?.walletRootId ?? "none";
333
354
  const lines = [
334
- "Cogcoin Status",
335
- `DB path: ${context.databasePath}`,
336
- `Bitcoin datadir: ${context.dataDir}`,
355
+ overviewEntry(`State: ${context.localState.availability}`, context.localState.availability === "ready"),
356
+ overviewEntry(`Root: ${walletRoot}`, walletRoot !== "none"),
357
+ overviewEntry(`Unlock: ${formatUnlockExpiry(context.localState.unlockUntilUnixMs)}`, context.localState.unlockUntilUnixMs !== null),
337
358
  ];
338
- appendWalletAvailability(lines, context);
339
- appendServiceSummary(lines, context);
340
- if (context.model !== null) {
341
- lines.push(`Local identities: ${context.model.identities.length}`);
342
- lines.push(`Locally related domains: ${context.model.domains.length}`);
343
- lines.push(`Read-only identities: ${context.model.readOnlyIdentityCount}`);
359
+ if (context.localState.message !== null) {
360
+ lines.push(overviewEntry(`Note: ${context.localState.message}`, false));
344
361
  }
345
- else {
346
- lines.push("Wallet-derived sections: unavailable");
362
+ const nodeStatus = context.nodeStatus;
363
+ const replica = nodeStatus?.walletReplica ?? null;
364
+ if (replica !== null) {
365
+ lines.push(overviewEntry(`Managed Core wallet: ${replica.proofStatus ?? "not-proven"}`, replica.proofStatus === "ready"));
347
366
  }
348
- appendPendingMutationSummary(lines, context);
349
- return lines.join("\n");
367
+ if (nodeStatus?.walletReplicaMessage) {
368
+ lines.push(overviewEntry(`Managed Core note: ${nodeStatus.walletReplicaMessage}`, replica?.proofStatus === "ready"));
369
+ }
370
+ return lines;
371
+ }
372
+ function buildOverviewServicesSection(context) {
373
+ const bitcoindOk = context.bitcoind.health === "ready";
374
+ const nodeOk = context.nodeHealth === "synced";
375
+ const indexerOk = context.indexer.health === "synced";
376
+ const lines = [
377
+ overviewEntry(`Managed bitcoind: ${formatServiceHealth(context.bitcoind.health)}`, bitcoindOk),
378
+ ];
379
+ if (context.bitcoind.message !== null) {
380
+ lines.push(overviewEntry(`Managed bitcoind note: ${context.bitcoind.message}`, bitcoindOk));
381
+ }
382
+ lines.push(overviewEntry(`Bitcoin service: ${formatServiceHealth(context.nodeHealth)}`, nodeOk));
383
+ if (context.nodeStatus !== null) {
384
+ lines.push(overviewEntry(`Bitcoin best height: ${formatMaybe(context.nodeStatus.nodeBestHeight)}`, nodeOk));
385
+ lines.push(overviewEntry(`Bitcoin headers: ${formatMaybe(context.nodeStatus.nodeHeaderHeight)}`, nodeOk));
386
+ }
387
+ if (context.nodeMessage !== null) {
388
+ lines.push(overviewEntry(`Bitcoin note: ${context.nodeMessage}`, nodeOk));
389
+ }
390
+ lines.push(overviewEntry(`Indexer service: ${formatServiceHealth(context.indexer.health)}`, indexerOk));
391
+ lines.push(overviewEntry(`Indexer truth source: ${formatIndexerTruthSource(context.indexer.source)}`, indexerOk));
392
+ if (context.indexer.daemonInstanceId !== null && context.indexer.daemonInstanceId !== undefined) {
393
+ lines.push(overviewEntry(`Indexer daemon instance: ${context.indexer.daemonInstanceId}`, indexerOk));
394
+ }
395
+ if (context.indexer.snapshotSeq !== null && context.indexer.snapshotSeq !== undefined) {
396
+ lines.push(overviewEntry(`Indexer snapshot sequence: ${context.indexer.snapshotSeq}`, indexerOk));
397
+ }
398
+ if (context.indexer.status?.reorgDepth !== null && context.indexer.status?.reorgDepth !== undefined) {
399
+ lines.push(overviewEntry(`Indexer reorg depth: ${context.indexer.status.reorgDepth}`, indexerOk));
400
+ }
401
+ lines.push(overviewEntry(`Indexer tip height: ${context.indexer.snapshotTip === null ? "unavailable" : context.indexer.snapshotTip.height}`, indexerOk));
402
+ if (context.indexer.message !== null) {
403
+ lines.push(overviewEntry(`Indexer note: ${context.indexer.message}`, indexerOk));
404
+ }
405
+ if (context.mining !== undefined) {
406
+ const miningOk = isMiningOverviewOk(context.mining);
407
+ lines.push(overviewEntry(`Mining: ${formatMiningSummaryLine(context.mining)}`, miningOk));
408
+ if (context.mining.runtime.note !== null) {
409
+ lines.push(overviewEntry(`Mining note: ${context.mining.runtime.note}`, miningOk));
410
+ }
411
+ }
412
+ return lines;
413
+ }
414
+ function buildOverviewLocalInventorySection(context) {
415
+ if (context.model === null) {
416
+ return [overviewEntry("Status: Wallet-derived sections unavailable", false)];
417
+ }
418
+ return [
419
+ overviewEntry(`Local identities: ${context.model.identities.length}`, true),
420
+ overviewEntry(`Locally related domains: ${context.model.domains.length}`, true),
421
+ overviewEntry(`Read-only identities: ${context.model.readOnlyIdentityCount}`, true),
422
+ ];
423
+ }
424
+ function buildOverviewPendingWorkSection(context) {
425
+ const pendingFamilies = (context.localState.state?.proactiveFamilies ?? [])
426
+ .filter((family) => (family.type === "anchor" || family.type === "field")
427
+ && family.status !== "confirmed"
428
+ && family.status !== "canceled");
429
+ const pendingMutations = (context.localState.state?.pendingMutations ?? [])
430
+ .filter((mutation) => mutation.status !== "confirmed" && mutation.status !== "canceled");
431
+ if (pendingFamilies.length === 0 && pendingMutations.length === 0) {
432
+ return [overviewEntry("Status: none", true)];
433
+ }
434
+ const lines = [];
435
+ for (const family of pendingFamilies) {
436
+ const label = family.type === "field"
437
+ ? `Field family: ${family.domainName ?? "unknown"}.${family.fieldName ?? "unknown"}`
438
+ : `Anchor family: ${family.domainName ?? "unknown"}`;
439
+ lines.push(overviewEntry(`${label} ${family.status}${family.currentStep === null || family.currentStep === undefined ? "" : ` step ${family.currentStep}`}${family.reservedDedicatedIndex == null ? "" : ` index ${family.reservedDedicatedIndex}`}`, false));
440
+ }
441
+ for (const mutation of pendingMutations) {
442
+ lines.push(overviewEntry(`Mutation: ${formatPendingMutationSummaryLabel(mutation)} ${mutation.status} sender spk:${mutation.senderScriptPubKeyHex}${mutation.priceCogtoshi === undefined || mutation.priceCogtoshi === null ? "" : ` price ${formatCogAmount(mutation.priceCogtoshi)}`}${mutation.amountCogtoshi === undefined || mutation.amountCogtoshi === null ? "" : ` amount ${formatCogAmount(mutation.amountCogtoshi)}`}${isReputationMutation(mutation) ? "" : mutation.recipientDomainName === undefined || mutation.recipientDomainName === null ? "" : ` domain ${mutation.recipientDomainName}`}${mutation.lockId === undefined || mutation.lockId === null ? "" : ` lock ${mutation.lockId}`}${mutation.recipientScriptPubKeyHex === undefined || mutation.recipientScriptPubKeyHex === null ? "" : ` recipient spk:${mutation.recipientScriptPubKeyHex}`}${mutation.kind === "endpoint" ? (mutation.endpointValueHex === "" ? " endpoint clear" : ` endpoint-bytes ${(mutation.endpointValueHex?.length ?? 0) / 2}`) : ""}${mutation.kind === "field-create" || mutation.kind === "field-set" ? ` format ${formatFieldFormat(mutation.fieldFormat ?? 0)}` : ""}${mutation.kind === "field-clear" ? " clear" : ""}${mutation.reviewPayloadHex === undefined || mutation.reviewPayloadHex === null ? "" : " review"}`, false));
443
+ }
444
+ return lines;
445
+ }
446
+ function getOverviewNextStep(context) {
447
+ const repairRecommendation = getRepairRecommendation(context);
448
+ if (repairRecommendation !== null) {
449
+ return repairRecommendation;
450
+ }
451
+ if (getBootstrapSyncNextStep(context) !== null) {
452
+ return "Run `cogcoin sync` to bootstrap assumeutxo and the managed Bitcoin/indexer state.";
453
+ }
454
+ return getMutationRecommendation(context);
455
+ }
456
+ export function formatWalletOverviewReport(context) {
457
+ const parts = [
458
+ "\n⛭ Cogcoin Status ⛭",
459
+ formatOverviewSection("Paths", buildOverviewPathsSection(context)),
460
+ formatOverviewSection("Wallet", buildOverviewWalletSection(context)),
461
+ formatOverviewSection("Services", buildOverviewServicesSection(context)),
462
+ formatOverviewSection("Local Inventory", buildOverviewLocalInventorySection(context)),
463
+ formatOverviewSection("Pending Work", buildOverviewPendingWorkSection(context)),
464
+ ];
465
+ const nextStep = getOverviewNextStep(context);
466
+ if (nextStep !== null) {
467
+ parts.push(`Next step: ${nextStep}`);
468
+ }
469
+ return parts.join("\n\n");
350
470
  }
351
471
  export function formatDetailedWalletStatusReport(context) {
352
472
  const lines = ["Cogcoin Wallet Status"];
@@ -156,6 +156,12 @@ export declare function initializeWallet(options: {
156
156
  attachService?: typeof attachOrStartManagedBitcoindService;
157
157
  rpcFactory?: (config: Parameters<typeof createRpcClient>[0]) => WalletLifecycleRpcClient;
158
158
  }): Promise<WalletInitializationResult>;
159
+ export declare function showWalletMnemonic(options: {
160
+ provider?: WalletSecretProvider;
161
+ prompter: WalletPrompter;
162
+ nowUnixMs?: number;
163
+ paths?: WalletRuntimePaths;
164
+ }): Promise<void>;
159
165
  export declare function unlockWallet(options?: {
160
166
  provider?: WalletSecretProvider;
161
167
  nowUnixMs?: number;
@@ -3,7 +3,7 @@ import { access, constants, mkdir, readFile, readdir, rename, rm } from "node:fs
3
3
  import { dirname, join } from "node:path";
4
4
  import { openClient } from "../client.js";
5
5
  import { attachOrStartIndexerDaemon, probeIndexerDaemon, readSnapshotWithRetry, } from "../bitcoind/indexer-daemon.js";
6
- import { attachOrStartManagedBitcoindService, createManagedWalletReplica, probeManagedBitcoindService, } from "../bitcoind/service.js";
6
+ import { attachOrStartManagedBitcoindService, createManagedWalletReplica, probeManagedBitcoindService, withClaimedUninitializedManagedRuntime, } from "../bitcoind/service.js";
7
7
  import { resolveManagedServicePaths } from "../bitcoind/service-paths.js";
8
8
  import { createRpcClient } from "../bitcoind/node.js";
9
9
  import { openSqliteStore } from "../sqlite/index.js";
@@ -22,7 +22,7 @@ import { clearWalletExplicitLock, loadWalletExplicitLock, saveWalletExplicitLock
22
22
  import { clearWalletPendingInitializationState, loadWalletPendingInitializationStateOrNull, saveWalletPendingInitializationState, } from "./state/pending-init.js";
23
23
  import { clearUnlockSession, loadUnlockSession, saveUnlockSession } from "./state/session.js";
24
24
  import { createDefaultWalletSecretProvider, createWalletPendingInitSecretReference, createWalletRootId, createWalletSecretReference, } from "./state/provider.js";
25
- import { loadWalletState, saveWalletState } from "./state/storage.js";
25
+ import { extractWalletRootIdHintFromWalletStateEnvelope, loadRawWalletStateEnvelope, loadWalletState, saveWalletState, } from "./state/storage.js";
26
26
  export const DEFAULT_UNLOCK_DURATION_MS = 15 * 60 * 1000;
27
27
  export { previewResetWallet, resetWallet, } from "./reset.js";
28
28
  function sanitizeWalletName(walletRootId) {
@@ -48,21 +48,6 @@ async function readJsonFileOrNull(path) {
48
48
  return null;
49
49
  }
50
50
  }
51
- async function loadRawWalletEnvelope(paths) {
52
- const primary = await readJsonFileOrNull(paths.walletStatePath);
53
- if (primary !== null) {
54
- return primary;
55
- }
56
- return readJsonFileOrNull(paths.walletStateBackupPath);
57
- }
58
- function extractWalletRootIdFromEnvelope(envelope) {
59
- const keyId = envelope?.secretProvider?.keyId ?? null;
60
- const prefix = "wallet-state:";
61
- if (keyId === null || !keyId.startsWith(prefix)) {
62
- return null;
63
- }
64
- return keyId.slice(prefix.length);
65
- }
66
51
  function resolvePendingInitializationStoragePaths(paths) {
67
52
  return {
68
53
  primaryPath: paths.walletInitPendingPath,
@@ -399,10 +384,10 @@ async function promptForRestoreMnemonic(prompter) {
399
384
  }
400
385
  return phrase;
401
386
  }
402
- async function confirmTypedAcknowledgement(prompter, expected, message) {
387
+ async function confirmTypedAcknowledgement(prompter, expected, message, errorCode = "wallet_typed_confirmation_rejected") {
403
388
  const answer = (await prompter.prompt(message)).trim();
404
389
  if (answer !== expected) {
405
- throw new Error("wallet_typed_confirmation_rejected");
390
+ throw new Error(errorCode);
406
391
  }
407
392
  }
408
393
  async function confirmRestoreReplacement(prompter) {
@@ -834,6 +819,23 @@ async function ensureWalletNotInitialized(paths, provider) {
834
819
  throw new Error("wallet_already_initialized");
835
820
  }
836
821
  }
822
+ function isWalletSecretAccessError(error) {
823
+ const message = error instanceof Error ? error.message : String(error);
824
+ return message === "wallet_envelope_missing_secret_provider"
825
+ || message.startsWith("wallet_secret_missing_")
826
+ || message.startsWith("wallet_secret_provider_");
827
+ }
828
+ function writeMnemonicReveal(prompter, phrase, introLines) {
829
+ const words = phrase.trim().split(/\s+/);
830
+ for (const line of introLines) {
831
+ prompter.writeLine(line);
832
+ }
833
+ for (const line of renderWalletMnemonicRevealArt(words)) {
834
+ prompter.writeLine(line);
835
+ }
836
+ prompter.writeLine("Single-line copy:");
837
+ prompter.writeLine(phrase);
838
+ }
837
839
  async function confirmMnemonic(prompter, words) {
838
840
  const challenge = createMnemonicConfirmationChallenge(words);
839
841
  for (const entry of challenge) {
@@ -1135,15 +1137,12 @@ export async function initializeWallet(options) {
1135
1137
  nowUnixMs,
1136
1138
  });
1137
1139
  let mnemonicRevealed = false;
1138
- options.prompter.writeLine("Cogcoin Wallet Initialization");
1139
- options.prompter.writeLine("Write down this 24-word recovery phrase.");
1140
- options.prompter.writeLine("The same phrase will be shown again until confirmation succeeds:");
1141
- options.prompter.writeLine("");
1142
- for (const line of renderWalletMnemonicRevealArt(material.mnemonic.words)) {
1143
- options.prompter.writeLine(line);
1144
- }
1145
- options.prompter.writeLine("Single-line copy:");
1146
- options.prompter.writeLine(material.mnemonic.phrase);
1140
+ writeMnemonicReveal(options.prompter, material.mnemonic.phrase, [
1141
+ "Cogcoin Wallet Initialization",
1142
+ "Write down this 24-word recovery phrase.",
1143
+ "The same phrase will be shown again until confirmation succeeds:",
1144
+ "",
1145
+ ]);
1147
1146
  mnemonicRevealed = true;
1148
1147
  try {
1149
1148
  await confirmMnemonic(options.prompter, material.mnemonic.words);
@@ -1166,14 +1165,19 @@ export async function initializeWallet(options) {
1166
1165
  material,
1167
1166
  internalCoreWalletPassphrase,
1168
1167
  });
1169
- await saveWalletState({
1170
- primaryPath: paths.walletStatePath,
1171
- backupPath: paths.walletStateBackupPath,
1172
- }, initialState, {
1173
- provider,
1174
- secretReference,
1168
+ const verifiedState = await withClaimedUninitializedManagedRuntime({
1169
+ dataDir: options.dataDir,
1170
+ walletRootId,
1171
+ }, async () => {
1172
+ await saveWalletState({
1173
+ primaryPath: paths.walletStatePath,
1174
+ backupPath: paths.walletStateBackupPath,
1175
+ }, initialState, {
1176
+ provider,
1177
+ secretReference,
1178
+ });
1179
+ return importDescriptorIntoManagedCoreWallet(initialState, provider, paths, options.dataDir, nowUnixMs, options.attachService, options.rpcFactory);
1175
1180
  });
1176
- const verifiedState = await importDescriptorIntoManagedCoreWallet(initialState, provider, paths, options.dataDir, nowUnixMs, options.attachService, options.rpcFactory);
1177
1181
  const unlockUntilUnixMs = nowUnixMs + unlockDurationMs;
1178
1182
  await clearWalletExplicitLock(paths.walletExplicitLockPath);
1179
1183
  await saveUnlockSession(paths.walletUnlockSessionPath, createUnlockSession(verifiedState, unlockUntilUnixMs, secretReference.keyId, nowUnixMs), {
@@ -1192,6 +1196,71 @@ export async function initializeWallet(options) {
1192
1196
  await controlLock.release();
1193
1197
  }
1194
1198
  }
1199
+ export async function showWalletMnemonic(options) {
1200
+ if (!options.prompter.isInteractive) {
1201
+ throw new Error("wallet_show_mnemonic_requires_tty");
1202
+ }
1203
+ const provider = options.provider ?? createDefaultWalletSecretProvider();
1204
+ const nowUnixMs = options.nowUnixMs ?? Date.now();
1205
+ const paths = options.paths ?? resolveWalletRuntimePathsForTesting();
1206
+ const controlLock = await acquireFileLock(paths.walletControlLockPath, {
1207
+ purpose: "wallet-show-mnemonic",
1208
+ walletRootId: null,
1209
+ });
1210
+ try {
1211
+ const [hasPrimaryStateFile, hasBackupStateFile] = await Promise.all([
1212
+ pathExists(paths.walletStatePath),
1213
+ pathExists(paths.walletStateBackupPath),
1214
+ ]);
1215
+ if (!hasPrimaryStateFile && !hasBackupStateFile) {
1216
+ throw new Error("wallet_uninitialized");
1217
+ }
1218
+ const unlocked = await loadOrAutoUnlockWalletState({
1219
+ provider,
1220
+ nowUnixMs,
1221
+ paths,
1222
+ controlLockHeld: true,
1223
+ });
1224
+ if (unlocked === null) {
1225
+ try {
1226
+ await loadWalletState({
1227
+ primaryPath: paths.walletStatePath,
1228
+ backupPath: paths.walletStateBackupPath,
1229
+ }, {
1230
+ provider,
1231
+ });
1232
+ }
1233
+ catch (error) {
1234
+ if (isWalletSecretAccessError(error)) {
1235
+ throw new Error("wallet_locked");
1236
+ }
1237
+ throw new Error("local-state-corrupt");
1238
+ }
1239
+ throw new Error("wallet_locked");
1240
+ }
1241
+ await confirmTypedAcknowledgement(options.prompter, "show mnemonic", "Type \"show mnemonic\" to continue: ", "wallet_show_mnemonic_typed_ack_required");
1242
+ let mnemonicRevealed = false;
1243
+ writeMnemonicReveal(options.prompter, unlocked.state.mnemonic.phrase, [
1244
+ "Cogcoin Wallet Recovery Phrase",
1245
+ "This 24-word recovery phrase controls the wallet.",
1246
+ "",
1247
+ ]);
1248
+ mnemonicRevealed = true;
1249
+ try {
1250
+ await options.prompter.prompt("Press Enter to clear the recovery phrase from the screen: ");
1251
+ }
1252
+ finally {
1253
+ if (mnemonicRevealed) {
1254
+ await Promise.resolve()
1255
+ .then(() => options.prompter.clearSensitiveDisplay?.("mnemonic-reveal"))
1256
+ .catch(() => undefined);
1257
+ }
1258
+ }
1259
+ }
1260
+ finally {
1261
+ await controlLock.release();
1262
+ }
1263
+ }
1195
1264
  export async function unlockWallet(options = {}) {
1196
1265
  const provider = options.provider ?? createDefaultWalletSecretProvider();
1197
1266
  const nowUnixMs = options.nowUnixMs ?? Date.now();
@@ -1423,7 +1492,10 @@ export async function restoreWalletFromMnemonic(options) {
1423
1492
  walletRootId: null,
1424
1493
  });
1425
1494
  try {
1426
- const rawEnvelope = await loadRawWalletEnvelope(paths);
1495
+ const rawEnvelope = await loadRawWalletStateEnvelope({
1496
+ primaryPath: paths.walletStatePath,
1497
+ backupPath: paths.walletStateBackupPath,
1498
+ });
1427
1499
  const replacementStateExists = rawEnvelope !== null
1428
1500
  || await pathExists(paths.walletStatePath)
1429
1501
  || await pathExists(paths.walletStateBackupPath);
@@ -1433,7 +1505,7 @@ export async function restoreWalletFromMnemonic(options) {
1433
1505
  if (replacementStateExists || replacementCoreWalletExists) {
1434
1506
  await confirmRestoreReplacement(options.prompter);
1435
1507
  }
1436
- let previousWalletRootId = extractWalletRootIdFromEnvelope(rawEnvelope);
1508
+ let previousWalletRootId = extractWalletRootIdHintFromWalletStateEnvelope(rawEnvelope?.envelope ?? null);
1437
1509
  try {
1438
1510
  const loaded = await loadWalletState({
1439
1511
  primaryPath: paths.walletStatePath,
@@ -4,11 +4,13 @@ import { attachOrStartIndexerDaemon, probeIndexerDaemon, readObservedIndexerDaem
4
4
  import { createRpcClient } from "../../bitcoind/node.js";
5
5
  import { UNINITIALIZED_WALLET_ROOT_ID } from "../../bitcoind/service-paths.js";
6
6
  import { attachOrStartManagedBitcoindService, probeManagedBitcoindService, } from "../../bitcoind/service.js";
7
+ import { resolveCogcoinProcessingStartHeight } from "../../bitcoind/processing-start-height.js";
7
8
  import {} from "../../bitcoind/types.js";
8
9
  import { loadOrAutoUnlockWalletState, verifyManagedCoreWalletReplica, } from "../lifecycle.js";
9
10
  import { persistNormalizedWalletDescriptorStateIfNeeded } from "../descriptor-normalization.js";
10
11
  import { inspectMiningControlPlane } from "../mining/index.js";
11
12
  import { normalizeMiningStateRecord } from "../mining/state.js";
13
+ import { resolveWalletRootIdFromLocalArtifacts } from "../root-resolution.js";
12
14
  import { resolveWalletRuntimePathsForTesting } from "../runtime.js";
13
15
  import { loadWalletExplicitLock } from "../state/explicit-lock.js";
14
16
  import { loadWalletState } from "../state/storage.js";
@@ -86,6 +88,7 @@ async function normalizeLoadedWalletStateForRead(options) {
86
88
  async function inspectWalletLocalState(options = {}) {
87
89
  const paths = options.paths ?? resolveWalletRuntimePathsForTesting();
88
90
  const now = options.now ?? Date.now();
91
+ const provider = options.secretProvider ?? createDefaultWalletSecretProvider();
89
92
  const [hasPrimaryStateFile, hasBackupStateFile, hasUnlockSessionFile] = await Promise.all([
90
93
  pathExists(paths.walletStatePath),
91
94
  pathExists(paths.walletStateBackupPath),
@@ -106,7 +109,6 @@ async function inspectWalletLocalState(options = {}) {
106
109
  }
107
110
  if (options.passphrase === undefined) {
108
111
  try {
109
- const provider = options.secretProvider ?? createDefaultWalletSecretProvider();
110
112
  const unlocked = await loadOrAutoUnlockWalletState({
111
113
  provider,
112
114
  nowUnixMs: now,
@@ -147,10 +149,14 @@ async function inspectWalletLocalState(options = {}) {
147
149
  };
148
150
  }
149
151
  catch (error) {
152
+ const resolvedRoot = await resolveWalletRootIdFromLocalArtifacts({
153
+ paths,
154
+ provider,
155
+ }).catch(() => null);
150
156
  if (isLockedWalletAccessError(error)) {
151
157
  return {
152
158
  availability: "locked",
153
- walletRootId: null,
159
+ walletRootId: resolvedRoot?.walletRootId ?? null,
154
160
  state: null,
155
161
  source: null,
156
162
  unlockUntilUnixMs: null,
@@ -166,7 +172,7 @@ async function inspectWalletLocalState(options = {}) {
166
172
  }
167
173
  return {
168
174
  availability: "local-state-corrupt",
169
- walletRootId: null,
175
+ walletRootId: resolvedRoot?.walletRootId ?? null,
170
176
  state: null,
171
177
  source: null,
172
178
  unlockUntilUnixMs: null,
@@ -461,7 +467,7 @@ async function attachNodeStatus(options) {
461
467
  const handle = await attachOrStartManagedBitcoindService({
462
468
  dataDir: options.dataDir,
463
469
  chain: "main",
464
- startHeight: genesis.genesisBlock,
470
+ startHeight: resolveCogcoinProcessingStartHeight(genesis),
465
471
  walletRootId: options.walletRootId,
466
472
  startupTimeoutMs: options.startupTimeoutMs,
467
473
  });