@cogcoin/client 0.5.3 → 0.5.5

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 (70) hide show
  1. package/README.md +11 -3
  2. package/dist/app-paths.d.ts +2 -0
  3. package/dist/app-paths.js +4 -0
  4. package/dist/art/wallet.txt +10 -0
  5. package/dist/bitcoind/bootstrap/chunk-manifest.d.ts +14 -0
  6. package/dist/bitcoind/bootstrap/chunk-manifest.js +85 -0
  7. package/dist/bitcoind/bootstrap/chunk-recovery.d.ts +4 -0
  8. package/dist/bitcoind/bootstrap/chunk-recovery.js +122 -0
  9. package/dist/bitcoind/bootstrap/constants.d.ts +3 -1
  10. package/dist/bitcoind/bootstrap/constants.js +3 -1
  11. package/dist/bitcoind/bootstrap/controller.d.ts +6 -1
  12. package/dist/bitcoind/bootstrap/controller.js +14 -7
  13. package/dist/bitcoind/bootstrap/default-snapshot-chunk-manifest.d.ts +2 -0
  14. package/dist/bitcoind/bootstrap/default-snapshot-chunk-manifest.js +2309 -0
  15. package/dist/bitcoind/bootstrap/download.js +177 -83
  16. package/dist/bitcoind/bootstrap/headers.d.ts +4 -2
  17. package/dist/bitcoind/bootstrap/headers.js +29 -4
  18. package/dist/bitcoind/bootstrap/state.d.ts +11 -1
  19. package/dist/bitcoind/bootstrap/state.js +50 -23
  20. package/dist/bitcoind/bootstrap/types.d.ts +12 -1
  21. package/dist/bitcoind/client/internal-types.d.ts +1 -0
  22. package/dist/bitcoind/client/managed-client.js +27 -13
  23. package/dist/bitcoind/client/sync-engine.js +42 -5
  24. package/dist/bitcoind/errors.js +9 -0
  25. package/dist/bitcoind/indexer-daemon.d.ts +9 -0
  26. package/dist/bitcoind/indexer-daemon.js +51 -14
  27. package/dist/bitcoind/service.d.ts +9 -0
  28. package/dist/bitcoind/service.js +65 -24
  29. package/dist/bitcoind/testing.d.ts +2 -2
  30. package/dist/bitcoind/testing.js +2 -2
  31. package/dist/bitcoind/types.d.ts +9 -0
  32. package/dist/cli/commands/service-runtime.d.ts +2 -0
  33. package/dist/cli/commands/service-runtime.js +432 -0
  34. package/dist/cli/commands/wallet-admin.js +227 -132
  35. package/dist/cli/commands/wallet-mutation.js +597 -580
  36. package/dist/cli/context.js +23 -1
  37. package/dist/cli/mutation-json.d.ts +17 -1
  38. package/dist/cli/mutation-json.js +42 -0
  39. package/dist/cli/output.js +113 -2
  40. package/dist/cli/parse.d.ts +1 -1
  41. package/dist/cli/parse.js +65 -0
  42. package/dist/cli/preview-json.d.ts +19 -1
  43. package/dist/cli/preview-json.js +31 -0
  44. package/dist/cli/prompt.js +40 -12
  45. package/dist/cli/runner.js +12 -0
  46. package/dist/cli/signals.d.ts +1 -0
  47. package/dist/cli/signals.js +44 -0
  48. package/dist/cli/types.d.ts +24 -2
  49. package/dist/cli/types.js +6 -0
  50. package/dist/cli/wallet-format.js +3 -0
  51. package/dist/cli/workflow-hints.d.ts +1 -0
  52. package/dist/cli/workflow-hints.js +3 -0
  53. package/dist/wallet/fs/lock.d.ts +2 -0
  54. package/dist/wallet/fs/lock.js +32 -0
  55. package/dist/wallet/lifecycle.d.ts +19 -1
  56. package/dist/wallet/lifecycle.js +315 -8
  57. package/dist/wallet/material.d.ts +2 -0
  58. package/dist/wallet/material.js +8 -1
  59. package/dist/wallet/mnemonic-art.d.ts +2 -0
  60. package/dist/wallet/mnemonic-art.js +54 -0
  61. package/dist/wallet/reset.d.ts +61 -0
  62. package/dist/wallet/reset.js +781 -0
  63. package/dist/wallet/runtime.d.ts +2 -0
  64. package/dist/wallet/runtime.js +2 -0
  65. package/dist/wallet/state/pending-init.d.ts +24 -0
  66. package/dist/wallet/state/pending-init.js +59 -0
  67. package/dist/wallet/state/provider.d.ts +1 -0
  68. package/dist/wallet/state/provider.js +7 -1
  69. package/dist/wallet/types.d.ts +8 -0
  70. package/package.json +6 -4
@@ -6,6 +6,7 @@ import { runFollowCommand } from "./commands/follow.js";
6
6
  import { runMiningAdminCommand } from "./commands/mining-admin.js";
7
7
  import { runMiningReadCommand } from "./commands/mining-read.js";
8
8
  import { runMiningRuntimeCommand } from "./commands/mining-runtime.js";
9
+ import { runServiceRuntimeCommand } from "./commands/service-runtime.js";
9
10
  import { runStatusCommand } from "./commands/status.js";
10
11
  import { runSyncCommand } from "./commands/sync.js";
11
12
  import { runWalletAdminCommand } from "./commands/wallet-admin.js";
@@ -49,6 +50,14 @@ export async function runCli(argv, contextOverrides = {}) {
49
50
  if (parsed.command === "status") {
50
51
  return runStatusCommand(parsed, context);
51
52
  }
53
+ if (parsed.command === "bitcoin-start"
54
+ || parsed.command === "bitcoin-stop"
55
+ || parsed.command === "bitcoin-status"
56
+ || parsed.command === "indexer-start"
57
+ || parsed.command === "indexer-stop"
58
+ || parsed.command === "indexer-status") {
59
+ return runServiceRuntimeCommand(parsed, context);
60
+ }
52
61
  if (parsed.command === "mine"
53
62
  || parsed.command === "mine-start"
54
63
  || parsed.command === "mine-stop") {
@@ -60,11 +69,14 @@ export async function runCli(argv, contextOverrides = {}) {
60
69
  return runMiningAdminCommand(parsed, context);
61
70
  }
62
71
  if (parsed.command === "init"
72
+ || parsed.command === "restore"
73
+ || parsed.command === "reset"
63
74
  || parsed.command === "repair"
64
75
  || parsed.command === "unlock"
65
76
  || parsed.command === "wallet-export"
66
77
  || parsed.command === "wallet-import"
67
78
  || parsed.command === "wallet-init"
79
+ || parsed.command === "wallet-restore"
68
80
  || parsed.command === "wallet-unlock"
69
81
  || parsed.command === "wallet-lock") {
70
82
  return runWalletAdminCommand(parsed, context);
@@ -1,3 +1,4 @@
1
1
  import type { InterruptibleOutcome, ManagedClientLike, SignalSource, StopSignalWatcher, WritableLike } from "./types.js";
2
2
  export declare function createStopSignalWatcher(signalSource: SignalSource, stderr: WritableLike, client: ManagedClientLike, forceExit: (code: number) => never | void): StopSignalWatcher;
3
+ export declare function createOwnedLockCleanupSignalWatcher(signalSource: SignalSource, forceExit: (code: number) => never | void, lockPaths: readonly string[]): StopSignalWatcher;
3
4
  export declare function waitForCompletionOrStop<T>(promise: Promise<T>, stopWatcher: StopSignalWatcher): Promise<InterruptibleOutcome<T>>;
@@ -1,4 +1,5 @@
1
1
  import { writeLine } from "./io.js";
2
+ import { clearLockIfOwnedByCurrentProcess } from "../wallet/fs/lock.js";
2
3
  export function createStopSignalWatcher(signalSource, stderr, client, forceExit) {
3
4
  let closing = false;
4
5
  let resolved = false;
@@ -42,6 +43,49 @@ export function createStopSignalWatcher(signalSource, stderr, client, forceExit)
42
43
  promise,
43
44
  };
44
45
  }
46
+ export function createOwnedLockCleanupSignalWatcher(signalSource, forceExit, lockPaths) {
47
+ let stopping = false;
48
+ let resolved = false;
49
+ let onSignal = () => { };
50
+ const cleanup = () => {
51
+ signalSource.off("SIGINT", onSignal);
52
+ signalSource.off("SIGTERM", onSignal);
53
+ };
54
+ const promise = new Promise((resolve) => {
55
+ const settle = (code) => {
56
+ if (resolved) {
57
+ return;
58
+ }
59
+ resolved = true;
60
+ cleanup();
61
+ resolve(code);
62
+ };
63
+ const releaseOwnedLocks = async () => {
64
+ await Promise.allSettled([...new Set(lockPaths)].map(async (lockPath) => {
65
+ await clearLockIfOwnedByCurrentProcess(lockPath);
66
+ }));
67
+ };
68
+ onSignal = () => {
69
+ if (stopping) {
70
+ settle(130);
71
+ forceExit(130);
72
+ return;
73
+ }
74
+ stopping = true;
75
+ settle(130);
76
+ void releaseOwnedLocks().finally(() => {
77
+ forceExit(130);
78
+ });
79
+ };
80
+ });
81
+ signalSource.on("SIGINT", onSignal);
82
+ signalSource.on("SIGTERM", onSignal);
83
+ return {
84
+ cleanup,
85
+ isStopping: () => stopping,
86
+ promise,
87
+ };
88
+ }
45
89
  export async function waitForCompletionOrStop(promise, stopWatcher) {
46
90
  const outcome = await Promise.race([
47
91
  promise.then((value) => ({ kind: "completed", value }), (error) => ({ kind: "error", error })),
@@ -1,14 +1,21 @@
1
1
  import type { inspectPassiveClientStatus } from "../passive-status.js";
2
+ import { createRpcClient } from "../bitcoind/node.js";
3
+ import { attachOrStartIndexerDaemon, probeIndexerDaemon, readObservedIndexerDaemonStatus, stopIndexerDaemonService } from "../bitcoind/indexer-daemon.js";
4
+ import { attachOrStartManagedBitcoindService, probeManagedBitcoindService, stopManagedBitcoindService } from "../bitcoind/service.js";
2
5
  import { openSqliteStore } from "../sqlite/index.js";
3
6
  import type { ClientStoreAdapter } from "../types.js";
4
- import type { exportWallet, WalletPrompter, importWallet, initializeWallet, lockWallet, repairWallet, unlockWallet } from "../wallet/lifecycle.js";
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";
5
9
  import type { openWalletReadContext } from "../wallet/read/index.js";
10
+ import { loadWalletExplicitLock } from "../wallet/state/explicit-lock.js";
11
+ import { loadUnlockSession } from "../wallet/state/session.js";
12
+ import { loadWalletState } from "../wallet/state/storage.js";
6
13
  import type { WalletSecretProvider } from "../wallet/state/provider.js";
7
14
  import type { disableMiningHooks, enableMiningHooks, followMiningLog, inspectMiningControlPlane, readMiningLog, runForegroundMining, setupBuiltInMining, startBackgroundMining, stopBackgroundMining } from "../wallet/mining/index.js";
8
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";
9
16
  export type ProgressOutput = "auto" | "tty" | "none";
10
17
  export type OutputMode = "text" | "json" | "preview-json";
11
- export type CommandName = "init" | "repair" | "sync" | "status" | "follow" | "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-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-lock" | "wallet-unlock" | "wallet-status" | "wallet-address" | "wallet-ids" | "address" | "ids" | "balance" | "locks" | "domain-list" | "domains" | "domain-show" | "show" | "fields" | "field";
12
19
  export interface WritableLike {
13
20
  isTTY?: boolean;
14
21
  write(chunk: string): void;
@@ -88,9 +95,22 @@ export interface CliRunnerContext {
88
95
  dataDir?: string;
89
96
  progressOutput?: ProgressOutput;
90
97
  }) => Promise<ManagedClientLike>;
98
+ attachManagedBitcoindService?: typeof attachOrStartManagedBitcoindService;
99
+ probeManagedBitcoindService?: typeof probeManagedBitcoindService;
100
+ stopManagedBitcoindService?: typeof stopManagedBitcoindService;
101
+ createBitcoinRpcClient?: typeof createRpcClient;
102
+ attachIndexerDaemon?: typeof attachOrStartIndexerDaemon;
103
+ probeIndexerDaemon?: typeof probeIndexerDaemon;
104
+ readObservedIndexerDaemonStatus?: typeof readObservedIndexerDaemonStatus;
105
+ stopIndexerDaemonService?: typeof stopIndexerDaemonService;
91
106
  inspectPassiveClientStatus?: typeof inspectPassiveClientStatus;
92
107
  openWalletReadContext?: typeof openWalletReadContext;
108
+ loadWalletState?: typeof loadWalletState;
109
+ loadUnlockSession?: typeof loadUnlockSession;
110
+ loadWalletExplicitLock?: typeof loadWalletExplicitLock;
93
111
  initializeWallet?: typeof initializeWallet;
112
+ restoreWalletFromMnemonic?: typeof restoreWalletFromMnemonic;
113
+ previewResetWallet?: typeof previewResetWallet;
94
114
  exportWallet?: typeof exportWallet;
95
115
  importWallet?: typeof importWallet;
96
116
  unlockWallet?: typeof unlockWallet;
@@ -126,12 +146,14 @@ export interface CliRunnerContext {
126
146
  readMiningLog?: typeof readMiningLog;
127
147
  followMiningLog?: typeof followMiningLog;
128
148
  repairWallet?: typeof repairWallet;
149
+ resetWallet?: typeof resetWallet;
129
150
  walletSecretProvider?: WalletSecretProvider;
130
151
  createPrompter?: () => WalletPrompter;
131
152
  ensureDirectory?: (path: string) => Promise<void>;
132
153
  readPackageVersion?: () => Promise<string>;
133
154
  resolveDefaultBitcoindDataDir?: () => string;
134
155
  resolveDefaultClientDatabasePath?: () => string;
156
+ resolveWalletRuntimePaths?: () => WalletRuntimePaths;
135
157
  }
136
158
  export interface StopSignalWatcher {
137
159
  cleanup(): void;
package/dist/cli/types.js CHANGED
@@ -1,2 +1,8 @@
1
1
  import { openManagedBitcoindClient } from "../bitcoind/index.js";
2
+ import { createRpcClient } from "../bitcoind/node.js";
3
+ import { attachOrStartIndexerDaemon, probeIndexerDaemon, readObservedIndexerDaemonStatus, stopIndexerDaemonService, } from "../bitcoind/indexer-daemon.js";
4
+ import { attachOrStartManagedBitcoindService, probeManagedBitcoindService, stopManagedBitcoindService, } from "../bitcoind/service.js";
2
5
  import { openSqliteStore } from "../sqlite/index.js";
6
+ import { loadWalletExplicitLock } from "../wallet/state/explicit-lock.js";
7
+ import { loadUnlockSession } from "../wallet/state/session.js";
8
+ import { loadWalletState } from "../wallet/state/storage.js";
@@ -75,6 +75,9 @@ function formatPendingMutationDomainLabel(mutation) {
75
75
  return kind;
76
76
  }
77
77
  export function getRepairRecommendation(context) {
78
+ if (context.localState.availability === "uninitialized") {
79
+ return "Run `cogcoin init` to create a new local wallet root.";
80
+ }
78
81
  if (context.localState.availability === "local-state-corrupt") {
79
82
  return "Run `cogcoin repair` to recover local wallet state.";
80
83
  }
@@ -2,6 +2,7 @@ import type { WalletIdentityView, WalletLockView, WalletReadContext } from "../w
2
2
  export declare function formatNextStepLines(nextSteps: readonly string[]): string[];
3
3
  export declare function getFundingQuickstartGuidance(): string;
4
4
  export declare function getInitNextSteps(): string[];
5
+ export declare function getRestoreNextSteps(): string[];
5
6
  export declare function getBootstrapSyncNextStep(context: Pick<WalletReadContext, "bitcoind" | "indexer" | "nodeHealth">): string | null;
6
7
  export declare function getRegisterNextSteps(domainName: string, registerKind: "root" | "subdomain"): string[];
7
8
  export declare function getAnchorNextSteps(domainName: string): string[];
@@ -10,6 +10,9 @@ export function getFundingQuickstartGuidance() {
10
10
  export function getInitNextSteps() {
11
11
  return ["cogcoin sync", "cogcoin address"];
12
12
  }
13
+ export function getRestoreNextSteps() {
14
+ return ["cogcoin sync", "cogcoin address"];
15
+ }
13
16
  function blocksSyncBootstrap(context) {
14
17
  return context.bitcoind.health === "service-version-mismatch"
15
18
  || context.bitcoind.health === "wallet-root-mismatch"
@@ -16,4 +16,6 @@ export declare class FileLockBusyError extends Error {
16
16
  constructor(lockPath: string, existingMetadata: FileLockMetadata | null);
17
17
  }
18
18
  export declare function readLockMetadata(lockPath: string): Promise<FileLockMetadata | null>;
19
+ export declare function clearLockIfOwnedByCurrentProcess(lockPath: string): Promise<boolean>;
20
+ export declare function clearOrphanedFileLock(lockPath: string, isProcessAlive: (pid: number) => Promise<boolean>): Promise<boolean>;
19
21
  export declare function acquireFileLock(lockPath: string, metadata?: Partial<FileLockMetadata>): Promise<FileLockHandle>;
@@ -22,6 +22,38 @@ export async function readLockMetadata(lockPath) {
22
22
  throw error;
23
23
  }
24
24
  }
25
+ export async function clearLockIfOwnedByCurrentProcess(lockPath) {
26
+ const metadata = await readLockMetadata(lockPath);
27
+ if (metadata === null || metadata.processId !== (process.pid ?? null)) {
28
+ return false;
29
+ }
30
+ await rm(lockPath, { force: true });
31
+ return true;
32
+ }
33
+ export async function clearOrphanedFileLock(lockPath, isProcessAlive) {
34
+ let metadata = null;
35
+ try {
36
+ metadata = await readLockMetadata(lockPath);
37
+ }
38
+ catch (error) {
39
+ if (error instanceof Error && "code" in error && error.code === "ENOENT") {
40
+ return false;
41
+ }
42
+ await rm(lockPath, { force: true });
43
+ return true;
44
+ }
45
+ if (metadata === null) {
46
+ return false;
47
+ }
48
+ const processId = typeof metadata.processId === "number" && Number.isInteger(metadata.processId)
49
+ ? metadata.processId
50
+ : null;
51
+ if (processId === null || !await isProcessAlive(processId)) {
52
+ await rm(lockPath, { force: true });
53
+ return true;
54
+ }
55
+ return false;
56
+ }
25
57
  export async function acquireFileLock(lockPath, metadata = {}) {
26
58
  await mkdir(dirname(lockPath), { recursive: true });
27
59
  const fullMetadata = {
@@ -11,6 +11,7 @@ export interface WalletPrompter {
11
11
  readonly isInteractive: boolean;
12
12
  writeLine(message: string): void;
13
13
  prompt(message: string): Promise<string>;
14
+ promptHidden?(message: string): Promise<string>;
14
15
  clearSensitiveDisplay?(scope: "mnemonic-reveal"): void | Promise<void>;
15
16
  }
16
17
  export interface WalletInitializationResult {
@@ -40,6 +41,13 @@ export interface WalletImportResult {
40
41
  unlockUntilUnixMs: number;
41
42
  state: WalletStateV1;
42
43
  }
44
+ export interface WalletRestoreResult {
45
+ walletRootId: string;
46
+ fundingAddress: string;
47
+ unlockUntilUnixMs: number;
48
+ state: WalletStateV1;
49
+ warnings?: string[];
50
+ }
43
51
  export interface WalletRepairResult {
44
52
  walletRootId: string;
45
53
  recoveredFromBackup: boolean;
@@ -58,6 +66,7 @@ export interface WalletRepairResult {
58
66
  miningResumeError: string | null;
59
67
  note: string | null;
60
68
  }
69
+ export { previewResetWallet, resetWallet, type WalletResetPreview, type WalletResetResult, } from "./reset.js";
61
70
  interface WalletLifecycleRpcClient {
62
71
  getDescriptorInfo(descriptor: string): Promise<{
63
72
  descriptor: string;
@@ -189,6 +198,16 @@ export declare function importWallet(options: {
189
198
  attachIndexerDaemon?: typeof attachOrStartIndexerDaemon;
190
199
  rpcFactory?: (config: Parameters<typeof createRpcClient>[0]) => WalletLifecycleRpcClient;
191
200
  }): Promise<WalletImportResult>;
201
+ export declare function restoreWalletFromMnemonic(options: {
202
+ dataDir: string;
203
+ provider?: WalletSecretProvider;
204
+ prompter: WalletPrompter;
205
+ nowUnixMs?: number;
206
+ unlockDurationMs?: number;
207
+ paths?: WalletRuntimePaths;
208
+ attachService?: typeof attachOrStartManagedBitcoindService;
209
+ rpcFactory?: (config: Parameters<typeof createRpcClient>[0]) => WalletLifecycleRpcClient;
210
+ }): Promise<WalletRestoreResult>;
192
211
  export declare function repairWallet(options: {
193
212
  dataDir: string;
194
213
  databasePath: string;
@@ -204,4 +223,3 @@ export declare function repairWallet(options: {
204
223
  requestMiningPreemption?: typeof requestMiningGenerationPreemption;
205
224
  startBackgroundMining?: typeof import("./mining/runner.js").startBackgroundMining;
206
225
  }): Promise<WalletRepairResult>;
207
- export {};