@cogcoin/client 1.1.6 → 1.1.7

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 (109) hide show
  1. package/README.md +2 -2
  2. package/dist/bitcoind/indexer-daemon.js +29 -79
  3. package/dist/bitcoind/managed-runtime/bitcoind-runtime.d.ts +20 -0
  4. package/dist/bitcoind/managed-runtime/bitcoind-runtime.js +74 -0
  5. package/dist/bitcoind/managed-runtime/bitcoind-status.d.ts +11 -0
  6. package/dist/bitcoind/managed-runtime/bitcoind-status.js +44 -0
  7. package/dist/bitcoind/managed-runtime/indexer-runtime.d.ts +15 -0
  8. package/dist/bitcoind/managed-runtime/indexer-runtime.js +82 -0
  9. package/dist/bitcoind/managed-runtime/types.d.ts +40 -0
  10. package/dist/bitcoind/node.d.ts +2 -2
  11. package/dist/bitcoind/node.js +2 -2
  12. package/dist/bitcoind/rpc.d.ts +2 -1
  13. package/dist/bitcoind/rpc.js +53 -3
  14. package/dist/bitcoind/service.js +46 -126
  15. package/dist/cli/command-registry.d.ts +1 -1
  16. package/dist/cli/command-registry.js +2 -64
  17. package/dist/cli/commands/client-admin.js +3 -18
  18. package/dist/cli/commands/mining-runtime.js +4 -60
  19. package/dist/cli/commands/wallet-admin.js +6 -6
  20. package/dist/cli/context.js +1 -3
  21. package/dist/cli/mining-json.d.ts +1 -22
  22. package/dist/cli/mining-json.js +0 -23
  23. package/dist/cli/output.js +16 -2
  24. package/dist/cli/parse.js +0 -2
  25. package/dist/cli/preview-json.d.ts +1 -22
  26. package/dist/cli/preview-json.js +0 -19
  27. package/dist/cli/types.d.ts +1 -3
  28. package/dist/cli/wallet-format.js +1 -1
  29. package/dist/cli/workflow-hints.d.ts +1 -2
  30. package/dist/cli/workflow-hints.js +5 -8
  31. package/dist/wallet/lifecycle/context.js +0 -1
  32. package/dist/wallet/lifecycle/repair-mining.d.ts +1 -5
  33. package/dist/wallet/lifecycle/repair-mining.js +5 -39
  34. package/dist/wallet/lifecycle/repair.js +0 -3
  35. package/dist/wallet/lifecycle/setup.js +10 -8
  36. package/dist/wallet/lifecycle/types.d.ts +1 -4
  37. package/dist/wallet/managed-core-wallet.d.ts +2 -0
  38. package/dist/wallet/managed-core-wallet.js +27 -1
  39. package/dist/wallet/mining/candidate.d.ts +1 -0
  40. package/dist/wallet/mining/candidate.js +38 -6
  41. package/dist/wallet/mining/competitiveness.d.ts +1 -0
  42. package/dist/wallet/mining/competitiveness.js +6 -0
  43. package/dist/wallet/mining/cycle.d.ts +2 -0
  44. package/dist/wallet/mining/cycle.js +14 -4
  45. package/dist/wallet/mining/engine-types.d.ts +1 -0
  46. package/dist/wallet/mining/index.d.ts +1 -1
  47. package/dist/wallet/mining/index.js +1 -1
  48. package/dist/wallet/mining/publish.d.ts +3 -0
  49. package/dist/wallet/mining/publish.js +78 -6
  50. package/dist/wallet/mining/runner.d.ts +0 -32
  51. package/dist/wallet/mining/runner.js +59 -104
  52. package/dist/wallet/mining/stop.d.ts +7 -0
  53. package/dist/wallet/mining/stop.js +23 -0
  54. package/dist/wallet/mining/supervisor.d.ts +2 -36
  55. package/dist/wallet/mining/supervisor.js +139 -246
  56. package/dist/wallet/read/context.d.ts +1 -5
  57. package/dist/wallet/read/context.js +20 -204
  58. package/dist/wallet/read/managed-services.d.ts +33 -0
  59. package/dist/wallet/read/managed-services.js +222 -0
  60. package/dist/wallet/state/client-password/bootstrap.d.ts +2 -0
  61. package/dist/wallet/state/client-password/bootstrap.js +3 -0
  62. package/dist/wallet/state/client-password/context.d.ts +10 -0
  63. package/dist/wallet/state/client-password/context.js +46 -0
  64. package/dist/wallet/state/client-password/crypto.d.ts +34 -0
  65. package/dist/wallet/state/client-password/crypto.js +117 -0
  66. package/dist/wallet/state/client-password/files.d.ts +10 -0
  67. package/dist/wallet/state/client-password/files.js +109 -0
  68. package/dist/wallet/state/client-password/legacy-cleanup.d.ts +11 -0
  69. package/dist/wallet/state/client-password/legacy-cleanup.js +338 -0
  70. package/dist/wallet/state/client-password/messages.d.ts +3 -0
  71. package/dist/wallet/state/client-password/messages.js +9 -0
  72. package/dist/wallet/state/client-password/migration.d.ts +4 -0
  73. package/dist/wallet/state/client-password/migration.js +32 -0
  74. package/dist/wallet/state/client-password/prompts.d.ts +12 -0
  75. package/dist/wallet/state/client-password/prompts.js +79 -0
  76. package/dist/wallet/state/client-password/protected-secrets.d.ts +13 -0
  77. package/dist/wallet/state/client-password/protected-secrets.js +90 -0
  78. package/dist/wallet/state/client-password/readiness.d.ts +4 -0
  79. package/dist/wallet/state/client-password/readiness.js +48 -0
  80. package/dist/wallet/state/client-password/references.d.ts +1 -0
  81. package/dist/wallet/state/client-password/references.js +56 -0
  82. package/dist/wallet/state/client-password/rotation.d.ts +6 -0
  83. package/dist/wallet/state/client-password/rotation.js +98 -0
  84. package/dist/wallet/state/client-password/session-policy.d.ts +6 -0
  85. package/dist/wallet/state/client-password/session-policy.js +28 -0
  86. package/dist/wallet/state/client-password/session.d.ts +19 -0
  87. package/dist/wallet/state/client-password/session.js +170 -0
  88. package/dist/wallet/state/client-password/setup.d.ts +8 -0
  89. package/dist/wallet/state/client-password/setup.js +49 -0
  90. package/dist/wallet/state/client-password/types.d.ts +82 -0
  91. package/dist/wallet/state/client-password/types.js +5 -0
  92. package/dist/wallet/state/client-password.d.ts +7 -38
  93. package/dist/wallet/state/client-password.js +52 -937
  94. package/dist/wallet/tx/anchor.js +123 -216
  95. package/dist/wallet/tx/cog.js +294 -489
  96. package/dist/wallet/tx/common.d.ts +2 -0
  97. package/dist/wallet/tx/common.js +2 -0
  98. package/dist/wallet/tx/domain-admin.js +111 -220
  99. package/dist/wallet/tx/domain-market.js +401 -681
  100. package/dist/wallet/tx/executor.d.ts +176 -0
  101. package/dist/wallet/tx/executor.js +302 -0
  102. package/dist/wallet/tx/field.js +109 -215
  103. package/dist/wallet/tx/register.js +158 -269
  104. package/dist/wallet/tx/reputation.js +120 -227
  105. package/package.json +1 -1
  106. package/dist/wallet/mining/worker-main.d.ts +0 -1
  107. package/dist/wallet/mining/worker-main.js +0 -17
  108. package/dist/wallet/state/client-password-agent.d.ts +0 -1
  109. package/dist/wallet/state/client-password-agent.js +0 -211
@@ -1,6 +1,6 @@
1
1
  import type { AnchorDomainResult, CogMutationResult, DomainAdminMutationResult, DomainMarketMutationResult, FieldMutationResult, RegisterDomainResult, ReputationMutationResult, WalletMutationFeeSummary } from "../wallet/tx/index.js";
2
2
  import type { WalletRepairResult, WalletResetPreview } from "../wallet/lifecycle.js";
3
- import type { MiningControlPlaneView, MiningRuntimeStatusV1 } from "../wallet/mining/index.js";
3
+ import type { MiningControlPlaneView } from "../wallet/mining/index.js";
4
4
  export declare function buildSingleTxMutationPreviewData(options: {
5
5
  kind: string;
6
6
  localStatus: string;
@@ -317,24 +317,3 @@ export declare function buildMineSetupPreviewData(view: MiningControlPlaneView):
317
317
  };
318
318
  state: Record<string, unknown>;
319
319
  };
320
- export declare function buildMineStartPreviewData(result: {
321
- started: boolean;
322
- snapshot: MiningRuntimeStatusV1 | null;
323
- }): {
324
- resultType: "state-change";
325
- stateChange: {
326
- kind: string;
327
- before: Record<string, unknown> | null;
328
- after: Record<string, unknown> | null;
329
- };
330
- state: Record<string, unknown>;
331
- };
332
- export declare function buildMineStopPreviewData(snapshot: MiningRuntimeStatusV1 | null): {
333
- resultType: "state-change";
334
- stateChange: {
335
- kind: string;
336
- before: Record<string, unknown> | null;
337
- after: Record<string, unknown> | null;
338
- };
339
- state: Record<string, unknown>;
340
- };
@@ -252,22 +252,3 @@ export function buildMineSetupPreviewData(view) {
252
252
  },
253
253
  });
254
254
  }
255
- export function buildMineStartPreviewData(result) {
256
- return buildStateChangePreviewData({
257
- kind: "mine-start",
258
- state: {
259
- started: result.started,
260
- runtime: summarizeRuntime(result.snapshot),
261
- },
262
- });
263
- }
264
- export function buildMineStopPreviewData(snapshot) {
265
- return buildStateChangePreviewData({
266
- kind: "mine-stop",
267
- state: {
268
- stopped: snapshot !== null,
269
- runtime: summarizeRuntime(snapshot),
270
- note: snapshot?.note ?? "Background mining was not active.",
271
- },
272
- });
273
- }
@@ -11,7 +11,7 @@ import type { WalletPrompter, initializeWallet, previewResetWallet, repairWallet
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
- import type { ensureBuiltInMiningSetupIfNeeded, followMiningLog, inspectMiningControlPlane, inspectMiningDomainPromptState, readMiningLog, runForegroundMining, setupBuiltInMining, startBackgroundMining, stopBackgroundMining, updateMiningDomainPrompt } from "../wallet/mining/index.js";
14
+ import type { ensureBuiltInMiningSetupIfNeeded, followMiningLog, inspectMiningControlPlane, inspectMiningDomainPromptState, readMiningLog, runForegroundMining, setupBuiltInMining, 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
16
  import type { CommandHandlerFamily, CommandName } from "./command-registry.js";
17
17
  export type { CommandHandlerFamily, CommandName } from "./command-registry.js";
@@ -154,8 +154,6 @@ export interface CliRunnerContext {
154
154
  inspectMiningDomainPromptState?: typeof inspectMiningDomainPromptState;
155
155
  ensureBuiltInMiningSetupIfNeeded?: typeof ensureBuiltInMiningSetupIfNeeded;
156
156
  runForegroundMining?: typeof runForegroundMining;
157
- startBackgroundMining?: typeof startBackgroundMining;
158
- stopBackgroundMining?: typeof stopBackgroundMining;
159
157
  setupBuiltInMining?: typeof setupBuiltInMining;
160
158
  updateMiningDomainPrompt?: typeof updateMiningDomainPrompt;
161
159
  readMiningLog?: typeof readMiningLog;
@@ -115,7 +115,7 @@ export function getRepairRecommendation(context) {
115
115
  }
116
116
  export function getClientUnlockRecommendation(context) {
117
117
  if (context.localState.unlockRequired) {
118
- return "Run `cogcoin client unlock` to temporarily unlock wallet-local secrets.";
118
+ return "Rerun this command in an interactive terminal so Cogcoin can prompt for the client password.";
119
119
  }
120
120
  return null;
121
121
  }
@@ -3,12 +3,11 @@ export declare function formatNextStepLines(nextSteps: readonly string[]): strin
3
3
  export declare function getFundingQuickstartGuidance(): string;
4
4
  export declare function getInitNextSteps(): string[];
5
5
  export declare function getRestoreNextSteps(): string[];
6
- export declare function getSetupUnlockGuidanceLines(unlockSeconds: number): string[];
6
+ export declare function getInitUnlockGuidanceLines(): string[];
7
7
  export declare function getBootstrapSyncNextStep(context: Pick<WalletReadContext, "bitcoind" | "indexer" | "nodeHealth">): string | null;
8
8
  export declare function getRegisterNextSteps(domainName: string, registerKind: "root" | "subdomain"): string[];
9
9
  export declare function getAnchorNextSteps(domainName: string): string[];
10
10
  export declare function getMineSetupNextSteps(): string[];
11
- export declare function getMineStopNextSteps(): string[];
12
11
  export declare function getAddressNextSteps(context: Pick<WalletReadContext, "bitcoind" | "indexer" | "nodeHealth">, address: string | null | undefined): string[];
13
12
  export declare function getIdsNextSteps(walletAddress: string | null | undefined): string[];
14
13
  export declare function getLocksNextSteps(locks: readonly WalletLockView[] | null | undefined): string[];
@@ -13,10 +13,10 @@ export function getInitNextSteps() {
13
13
  export function getRestoreNextSteps() {
14
14
  return ["cogcoin sync", "cogcoin address"];
15
15
  }
16
- export function getSetupUnlockGuidanceLines(unlockSeconds) {
16
+ export function getInitUnlockGuidanceLines() {
17
17
  return [
18
- `Client unlock: active for ${unlockSeconds} seconds.`,
19
- "Use `cogcoin client unlock` to lengthen this unlock window, or `cogcoin client lock` to lock immediately.",
18
+ "Client password reuse stays active in memory for the rest of this long-running init command, up to about 24 hours.",
19
+ "Future Cogcoin commands will prompt again when they need wallet-local secrets.",
20
20
  ];
21
21
  }
22
22
  function blocksSyncBootstrap(context) {
@@ -52,15 +52,12 @@ export function getRegisterNextSteps(domainName, registerKind) {
52
52
  export function getAnchorNextSteps(domainName) {
53
53
  const nextSteps = [`cogcoin show ${domainName}`];
54
54
  if (isRootDomainName(domainName)) {
55
- nextSteps.push("cogcoin mine", "cogcoin mine start");
55
+ nextSteps.push("cogcoin mine");
56
56
  }
57
57
  return nextSteps;
58
58
  }
59
59
  export function getMineSetupNextSteps() {
60
- return ["cogcoin mine", "cogcoin mine start"];
61
- }
62
- export function getMineStopNextSteps() {
63
- return ["cogcoin mine log"];
60
+ return ["cogcoin mine"];
64
61
  }
65
62
  export function getAddressNextSteps(context, address) {
66
63
  if (address === null || address === undefined || address.length === 0) {
@@ -47,7 +47,6 @@ export function resolveWalletRepairContext(options) {
47
47
  attachIndexerDaemon: options.attachIndexerDaemon ?? attachOrStartIndexerDaemon,
48
48
  probeIndexerDaemon: options.probeIndexerDaemon ?? probeIndexerDaemon,
49
49
  requestMiningPreemption: options.requestMiningPreemption,
50
- startBackgroundMining: options.startBackgroundMining,
51
50
  };
52
51
  }
53
52
  export async function acquireWalletControlLock(paths, purpose) {
@@ -2,8 +2,7 @@ import type { MiningRuntimeStatusV1 } from "../mining/types.js";
2
2
  import type { WalletRuntimePaths } from "../runtime.js";
3
3
  import { type WalletSecretProvider } from "../state/provider.js";
4
4
  import type { WalletStateV1 } from "../types.js";
5
- import type { WalletPrompter, WalletRepairResult } from "./types.js";
6
- export declare function createSilentNonInteractivePrompter(): WalletPrompter;
5
+ import type { WalletRepairResult } from "./types.js";
7
6
  export declare function applyRepairStoppedMiningState(state: WalletStateV1): WalletStateV1;
8
7
  export declare function createStoppedMiningRuntimeSnapshotForRepair(options: {
9
8
  state: WalletStateV1;
@@ -39,9 +38,6 @@ export declare function resumeBackgroundMiningAfterRepair(options: {
39
38
  repairedState: WalletStateV1;
40
39
  bitcoindPostRepairHealth: WalletRepairResult["bitcoindPostRepairHealth"];
41
40
  indexerPostRepairHealth: WalletRepairResult["indexerPostRepairHealth"];
42
- dataDir: string;
43
- databasePath: string;
44
- startBackgroundMining?: typeof import("../mining/runner.js").startBackgroundMining;
45
41
  }): Promise<{
46
42
  miningResumeAction: WalletRepairResult["miningResumeAction"];
47
43
  miningPostRepairRunMode: WalletRepairResult["miningPostRepairRunMode"];
@@ -8,15 +8,6 @@ import { normalizeMiningStateRecord } from "../mining/state.js";
8
8
  import { createWalletSecretReference } from "../state/provider.js";
9
9
  import { persistWalletStateUpdate } from "../descriptor-normalization.js";
10
10
  import { isProcessAlive, stopRecordedManagedProcess } from "./repair-runtime.js";
11
- export function createSilentNonInteractivePrompter() {
12
- return {
13
- isInteractive: false,
14
- writeLine() { },
15
- async prompt() {
16
- return "";
17
- },
18
- };
19
- }
20
11
  export function applyRepairStoppedMiningState(state) {
21
12
  const miningState = normalizeMiningStateRecord(state.miningState);
22
13
  return {
@@ -271,34 +262,9 @@ export async function resumeBackgroundMiningAfterRepair(options) {
271
262
  miningResumeError: null,
272
263
  };
273
264
  }
274
- try {
275
- const startBackgroundMining = options.startBackgroundMining
276
- ?? (await import("../mining/runner.js")).startBackgroundMining;
277
- const resumed = await startBackgroundMining({
278
- dataDir: options.dataDir,
279
- databasePath: options.databasePath,
280
- provider: options.provider,
281
- paths: options.paths,
282
- prompter: createSilentNonInteractivePrompter(),
283
- });
284
- if (resumed.snapshot?.runMode === "background") {
285
- return {
286
- miningResumeAction: "resumed-background",
287
- miningPostRepairRunMode: "background",
288
- miningResumeError: null,
289
- };
290
- }
291
- return {
292
- miningResumeAction: "resume-failed",
293
- miningPostRepairRunMode: "stopped",
294
- miningResumeError: "Background mining did not report a background runtime after repair.",
295
- };
296
- }
297
- catch (error) {
298
- return {
299
- miningResumeAction: "resume-failed",
300
- miningPostRepairRunMode: "stopped",
301
- miningResumeError: error instanceof Error ? error.message : String(error),
302
- };
303
- }
265
+ return {
266
+ miningResumeAction: "skipped-background-mode-removed",
267
+ miningPostRepairRunMode: "stopped",
268
+ miningResumeError: null,
269
+ };
304
270
  }
@@ -95,9 +95,6 @@ export async function repairWallet(options) {
95
95
  repairedState,
96
96
  bitcoindPostRepairHealth: bitcoindStage.bitcoindPostRepairHealth,
97
97
  indexerPostRepairHealth: indexerStage.indexerPostRepairHealth,
98
- dataDir: context.dataDir,
99
- databasePath: context.databasePath,
100
- startBackgroundMining: context.startBackgroundMining,
101
98
  });
102
99
  await clearLegacyWalletLockArtifacts(context.paths.walletRuntimeRoot);
103
100
  return {
@@ -1,5 +1,6 @@
1
1
  import { deriveWalletMaterialFromMnemonic } from "../material.js";
2
2
  import { ensureClientPasswordConfigured, withInteractiveWalletSecretProvider, } from "../state/provider.js";
3
+ import { bindClientPasswordPromptSessionPolicy } from "../state/client-password/session-policy.js";
3
4
  import { loadWalletStateForAccess, mapWalletReadAccessError } from "./access.js";
4
5
  import { acquireWalletControlLock, resolveWalletManagedCoreContext, resolveWalletSetupContext, walletStateExists, } from "./context.js";
5
6
  import { clearSensitiveDisplay, confirmMnemonic, confirmTypedAcknowledgement, promptForInitializationMode, promptForRestoreMnemonic, writeMnemonicReveal, } from "./setup-prompts.js";
@@ -9,10 +10,11 @@ export async function initializeWallet(options) {
9
10
  if (!context.prompter.isInteractive) {
10
11
  throw new Error("wallet_init_requires_tty");
11
12
  }
12
- const interactiveProvider = withInteractiveWalletSecretProvider(context.provider, context.prompter);
13
+ const initPrompter = bindClientPasswordPromptSessionPolicy(context.prompter, "init-24h");
14
+ const interactiveProvider = withInteractiveWalletSecretProvider(context.provider, initPrompter);
13
15
  const controlLock = await acquireWalletControlLock(context.paths, "wallet-init");
14
16
  try {
15
- const passwordAction = await ensureClientPasswordConfigured(context.provider, context.prompter);
17
+ const passwordAction = await ensureClientPasswordConfigured(context.provider, initPrompter);
16
18
  if (await walletStateExists(context.paths)) {
17
19
  await clearPendingInitialization(context.paths, interactiveProvider);
18
20
  const loaded = await loadWalletStateForAccess({
@@ -29,7 +31,7 @@ export async function initializeWallet(options) {
29
31
  state: loaded.state,
30
32
  };
31
33
  }
32
- const setupMode = await promptForInitializationMode(context.prompter);
34
+ const setupMode = await promptForInitializationMode(initPrompter);
33
35
  let material;
34
36
  if (setupMode === "generated") {
35
37
  material = await loadOrCreatePendingInitializationMaterial({
@@ -37,26 +39,26 @@ export async function initializeWallet(options) {
37
39
  paths: context.paths,
38
40
  nowUnixMs: context.nowUnixMs,
39
41
  });
40
- writeMnemonicReveal(context.prompter, material.mnemonic.phrase, [
42
+ writeMnemonicReveal(initPrompter, material.mnemonic.phrase, [
41
43
  "Cogcoin Wallet Initialization",
42
44
  "Write down this 24-word recovery phrase.",
43
45
  "The same phrase will be shown again until confirmation succeeds:",
44
46
  "",
45
47
  ]);
46
48
  try {
47
- await confirmMnemonic(context.prompter, material.mnemonic.words);
49
+ await confirmMnemonic(initPrompter, material.mnemonic.words);
48
50
  }
49
51
  finally {
50
- await clearSensitiveDisplay(context.prompter, "mnemonic-reveal");
52
+ await clearSensitiveDisplay(initPrompter, "mnemonic-reveal");
51
53
  }
52
54
  }
53
55
  else {
54
56
  let mnemonicPhrase;
55
57
  try {
56
- mnemonicPhrase = await promptForRestoreMnemonic(context.prompter);
58
+ mnemonicPhrase = await promptForRestoreMnemonic(initPrompter);
57
59
  }
58
60
  finally {
59
- await clearSensitiveDisplay(context.prompter, "restore-mnemonic-entry");
61
+ await clearSensitiveDisplay(initPrompter, "restore-mnemonic-entry");
60
62
  }
61
63
  await clearPendingInitialization(context.paths, interactiveProvider);
62
64
  material = deriveWalletMaterialFromMnemonic(mnemonicPhrase);
@@ -3,7 +3,6 @@ import type { createRpcClient } from "../../bitcoind/node.js";
3
3
  import type { attachOrStartManagedBitcoindService, probeManagedBitcoindService } from "../../bitcoind/service.js";
4
4
  import type { RpcListUnspentEntry } from "../../bitcoind/types.js";
5
5
  import type { requestMiningGenerationPreemption } from "../mining/coordination.js";
6
- import type { startBackgroundMining } from "../mining/runner.js";
7
6
  import type { WalletRuntimePaths } from "../runtime.js";
8
7
  import type { WalletSecretProvider } from "../state/provider.js";
9
8
  import type { WalletStateV1 } from "../types.js";
@@ -45,7 +44,7 @@ export interface WalletRepairResult {
45
44
  indexerCompatibilityIssue: "none" | "service-version-mismatch" | "wallet-root-mismatch" | "schema-mismatch";
46
45
  indexerPostRepairHealth: "starting" | "catching-up" | "synced" | "failed";
47
46
  miningPreRepairRunMode: "stopped" | "foreground" | "background";
48
- miningResumeAction: "none" | "skipped-not-resumable" | "skipped-post-repair-blocked" | "resumed-background" | "resume-failed";
47
+ miningResumeAction: "none" | "skipped-not-resumable" | "skipped-post-repair-blocked" | "skipped-background-mode-removed";
49
48
  miningPostRepairRunMode: "stopped" | "background";
50
49
  miningResumeError: string | null;
51
50
  note: string | null;
@@ -127,7 +126,6 @@ export interface WalletRepairDependencies extends WalletManagedCoreDependencies
127
126
  attachIndexerDaemon?: typeof attachOrStartIndexerDaemon;
128
127
  probeIndexerDaemon?: typeof probeIndexerDaemon;
129
128
  requestMiningPreemption?: typeof requestMiningGenerationPreemption;
130
- startBackgroundMining?: typeof startBackgroundMining;
131
129
  }
132
130
  export interface WalletRepairContext extends WalletManagedCoreContext {
133
131
  dataDir: string;
@@ -137,7 +135,6 @@ export interface WalletRepairContext extends WalletManagedCoreContext {
137
135
  attachIndexerDaemon: NonNullable<WalletRepairDependencies["attachIndexerDaemon"]>;
138
136
  probeIndexerDaemon: NonNullable<WalletRepairDependencies["probeIndexerDaemon"]>;
139
137
  requestMiningPreemption?: WalletRepairDependencies["requestMiningPreemption"];
140
- startBackgroundMining?: WalletRepairDependencies["startBackgroundMining"];
141
138
  }
142
139
  export interface WalletBitcoindRepairStageResult {
143
140
  state: WalletStateV1;
@@ -8,6 +8,8 @@ export declare function withUnlockedManagedCoreWallet<T>(options: {
8
8
  walletName: string;
9
9
  internalPassphrase: string;
10
10
  timeoutSeconds?: number;
11
+ recoverLockedWalletOnce?: boolean;
12
+ onLockedWalletRecoveryOutcome?: (outcome: "recovered" | "still-locked") => void;
11
13
  run: () => Promise<T>;
12
14
  }): Promise<T>;
13
15
  export declare function clearLegacyWalletLockArtifacts(walletRuntimeRoot: string): Promise<void>;
@@ -3,10 +3,36 @@ import { join } from "node:path";
3
3
  export const MANAGED_CORE_WALLET_UNLOCK_TIMEOUT_SECONDS = 10;
4
4
  const LEGACY_UNLOCK_SESSION_FILENAME = "wallet-unlock-session.enc";
5
5
  const LEGACY_EXPLICIT_LOCK_FILENAME = "wallet-explicit-lock.json";
6
+ const MANAGED_CORE_WALLET_PROCESS_PSBT_LOCKED_ERROR_PREFIX = "bitcoind_rpc_walletprocesspsbt_-13_";
7
+ const MANAGED_CORE_WALLET_PROCESS_PSBT_LOCKED_ERROR_SUFFIX = "Please enter the wallet passphrase with walletpassphrase first.";
8
+ function isManagedCoreWalletProcessPsbtLockedError(error) {
9
+ return error instanceof Error
10
+ && error.message.startsWith(MANAGED_CORE_WALLET_PROCESS_PSBT_LOCKED_ERROR_PREFIX)
11
+ && error.message.endsWith(MANAGED_CORE_WALLET_PROCESS_PSBT_LOCKED_ERROR_SUFFIX);
12
+ }
6
13
  export async function withUnlockedManagedCoreWallet(options) {
7
14
  await options.rpc.walletPassphrase(options.walletName, options.internalPassphrase, options.timeoutSeconds ?? MANAGED_CORE_WALLET_UNLOCK_TIMEOUT_SECONDS);
8
15
  try {
9
- return await options.run();
16
+ try {
17
+ return await options.run();
18
+ }
19
+ catch (error) {
20
+ if (!options.recoverLockedWalletOnce || !isManagedCoreWalletProcessPsbtLockedError(error)) {
21
+ throw error;
22
+ }
23
+ await options.rpc.walletPassphrase(options.walletName, options.internalPassphrase, options.timeoutSeconds ?? MANAGED_CORE_WALLET_UNLOCK_TIMEOUT_SECONDS);
24
+ try {
25
+ const result = await options.run();
26
+ options.onLockedWalletRecoveryOutcome?.("recovered");
27
+ return result;
28
+ }
29
+ catch (retryError) {
30
+ if (isManagedCoreWalletProcessPsbtLockedError(retryError)) {
31
+ options.onLockedWalletRecoveryOutcome?.("still-locked");
32
+ }
33
+ throw retryError;
34
+ }
35
+ }
10
36
  }
11
37
  finally {
12
38
  await options.rpc.walletLock(options.walletName).catch(() => undefined);
@@ -56,5 +56,6 @@ export declare function generateCandidatesForDomains(options: {
56
56
  indexerTruthKey: IndexerTruthKey | null;
57
57
  runId?: string | null;
58
58
  fetchImpl?: typeof fetch;
59
+ signal?: AbortSignal;
59
60
  }): Promise<MiningCandidate[]>;
60
61
  export declare function chooseBestLocalCandidate(candidates: MiningCandidate[]): Promise<MiningCandidate | null>;
@@ -5,6 +5,7 @@ import { loadClientConfig } from "./config.js";
5
5
  import { isMiningGenerationAbortRequested, markMiningGenerationActive, markMiningGenerationInactive, } from "./coordination.js";
6
6
  import { createMiningSentenceRequestLimits } from "./sentence-protocol.js";
7
7
  import { generateMiningSentences } from "./sentences.js";
8
+ import { createMiningStopRequestedError } from "./stop.js";
8
9
  import { isMineableWalletDomain } from "../read/index.js";
9
10
  import { lookupDomain } from "@cogcoin/indexer/queries";
10
11
  const BEST_BLOCK_POLL_INTERVAL_MS = 500;
@@ -142,6 +143,11 @@ export async function generateCandidatesForDomains(options) {
142
143
  let stale = false;
143
144
  let staleIndexerTruth = false;
144
145
  let preempted = false;
146
+ let stopRequested = false;
147
+ const handleStop = () => {
148
+ stopRequested = true;
149
+ abortController.abort(options.signal?.reason ?? createMiningStopRequestedError());
150
+ };
145
151
  const timer = setInterval(async () => {
146
152
  try {
147
153
  const [current, truthCurrent] = await Promise.all([
@@ -171,6 +177,12 @@ export async function generateCandidatesForDomains(options) {
171
177
  }
172
178
  }, BEST_BLOCK_POLL_INTERVAL_MS);
173
179
  try {
180
+ if (options.signal?.aborted) {
181
+ handleStop();
182
+ }
183
+ else {
184
+ options.signal?.addEventListener("abort", handleStop, { once: true });
185
+ }
174
186
  await markMiningGenerationActive({
175
187
  paths: options.paths,
176
188
  runId: options.runId ?? null,
@@ -185,12 +197,26 @@ export async function generateCandidatesForDomains(options) {
185
197
  });
186
198
  let generated;
187
199
  try {
188
- generated = await generateMiningSentences(generationRequest, {
189
- paths: options.paths,
190
- provider: options.provider,
191
- signal: abortController.signal,
192
- fetchImpl: options.fetchImpl,
193
- });
200
+ generated = await Promise.race([
201
+ generateMiningSentences(generationRequest, {
202
+ paths: options.paths,
203
+ provider: options.provider,
204
+ signal: abortController.signal,
205
+ fetchImpl: options.fetchImpl,
206
+ }),
207
+ new Promise((_resolve, reject) => {
208
+ const handleAbort = () => {
209
+ reject(abortController.signal.reason instanceof Error
210
+ ? abortController.signal.reason
211
+ : createMiningStopRequestedError());
212
+ };
213
+ if (abortController.signal.aborted) {
214
+ handleAbort();
215
+ return;
216
+ }
217
+ abortController.signal.addEventListener("abort", handleAbort, { once: true });
218
+ }),
219
+ ]);
194
220
  }
195
221
  catch (error) {
196
222
  if (stale) {
@@ -202,6 +228,11 @@ export async function generateCandidatesForDomains(options) {
202
228
  if (preempted) {
203
229
  throw new Error("mining_generation_preempted");
204
230
  }
231
+ if (stopRequested || options.signal?.aborted) {
232
+ throw options.signal?.reason instanceof Error
233
+ ? options.signal.reason
234
+ : createMiningStopRequestedError();
235
+ }
205
236
  throw error;
206
237
  }
207
238
  if (stale) {
@@ -253,6 +284,7 @@ export async function generateCandidatesForDomains(options) {
253
284
  }
254
285
  finally {
255
286
  clearInterval(timer);
287
+ options.signal?.removeEventListener("abort", handleStop);
256
288
  await markMiningGenerationInactive({
257
289
  paths: options.paths,
258
290
  runId: options.runId ?? null,
@@ -19,4 +19,5 @@ export declare function runCompetitivenessGate(options: {
19
19
  assaySentencesImpl?: typeof assaySentences;
20
20
  cooperativeYield?: MiningCooperativeYield;
21
21
  cooperativeYieldEvery?: number;
22
+ throwIfStopping?: () => void;
22
23
  }): Promise<CompetitivenessDecision>;
@@ -371,6 +371,7 @@ export async function runCompetitivenessGate(options) {
371
371
  let mempoolVerbose;
372
372
  try {
373
373
  mempoolVerbose = await options.rpc.getRawMempoolVerbose();
374
+ options.throwIfStopping?.();
374
375
  }
375
376
  catch {
376
377
  return createDecision({
@@ -415,6 +416,7 @@ export async function runCompetitivenessGate(options) {
415
416
  cooperativeYield: options.cooperativeYield,
416
417
  cooperativeYieldEvery: options.cooperativeYieldEvery,
417
418
  });
419
+ options.throwIfStopping?.();
418
420
  const txid = visibleTxids[index];
419
421
  if (txContexts.has(txid)) {
420
422
  continue;
@@ -423,6 +425,7 @@ export async function runCompetitivenessGate(options) {
423
425
  options.rpc.getRawTransaction(txid, true).catch(() => null),
424
426
  options.rpc.getMempoolEntry(txid).catch(() => null),
425
427
  ]);
428
+ options.throwIfStopping?.();
426
429
  if (tx === null || mempoolEntry === null) {
427
430
  continue;
428
431
  }
@@ -447,6 +450,7 @@ export async function runCompetitivenessGate(options) {
447
450
  cooperativeYield: options.cooperativeYield,
448
451
  cooperativeYieldEvery: options.cooperativeYieldEvery,
449
452
  });
453
+ options.throwIfStopping?.();
450
454
  const txid = visibleTxids[index];
451
455
  const context = txContexts.get(txid);
452
456
  if (context === undefined || context.payload === null || context.senderScriptHex === null) {
@@ -463,6 +467,7 @@ export async function runCompetitivenessGate(options) {
463
467
  domainId: decoded.domainId,
464
468
  senderScriptHex: context.senderScriptHex,
465
469
  });
470
+ options.throwIfStopping?.();
466
471
  if (overlayDomain === "indeterminate") {
467
472
  const decision = createDecision({
468
473
  competitivenessGateIndeterminate: true,
@@ -487,6 +492,7 @@ export async function runCompetitivenessGate(options) {
487
492
  continue;
488
493
  }
489
494
  const assayed = await assaySentencesImpl(decoded.domainId, options.candidate.referencedBlockHashInternal, [Buffer.from(decoded.sentenceBytes).toString("utf8")]).catch(() => []);
495
+ options.throwIfStopping?.();
490
496
  const scored = assayed[0];
491
497
  if (scored === undefined || !scored.gatesPass || scored.encodedSentenceBytes === null || scored.canonicalBlend === null) {
492
498
  continue;
@@ -27,6 +27,7 @@ export declare function runMiningPhaseMachine(options: {
27
27
  attachService: typeof attachOrStartManagedBitcoindService;
28
28
  rpcFactory: (config: Parameters<typeof createRpcClient>[0]) => MiningRpcClient;
29
29
  fetchImpl?: typeof fetch;
30
+ stopSignal?: AbortSignal;
30
31
  generateCandidatesForDomainsImpl?: typeof generateCandidatesForDomains;
31
32
  runCompetitivenessGateImpl?: typeof runCompetitivenessGate;
32
33
  assaySentencesImpl?: typeof assaySentences;
@@ -35,5 +36,6 @@ export declare function runMiningPhaseMachine(options: {
35
36
  nowImpl?: () => number;
36
37
  saveCycleStatus: (readContext: WalletReadContext, overrides: MiningRuntimeStatusOverrides) => Promise<MiningRuntimeStatusV1>;
37
38
  appendEvent: (event: MiningEventRecord) => Promise<void>;
39
+ throwIfStopping?: () => void;
38
40
  throwIfSuspendDetected?: () => void;
39
41
  }): Promise<void>;
@@ -59,7 +59,12 @@ export async function runMiningPhaseMachine(options) {
59
59
  return false;
60
60
  }
61
61
  };
62
+ const throwIfInterrupted = () => {
63
+ options.throwIfSuspendDetected?.();
64
+ options.throwIfStopping?.();
65
+ };
62
66
  while (true) {
67
+ throwIfInterrupted();
63
68
  switch (state.phase) {
64
69
  case "idle": {
65
70
  if (options.corePublishState !== "healthy") {
@@ -198,8 +203,9 @@ export async function runMiningPhaseMachine(options) {
198
203
  indexerTruthKey,
199
204
  runId: options.backgroundWorkerRunId,
200
205
  fetchImpl: options.fetchImpl,
206
+ signal: options.stopSignal,
201
207
  });
202
- options.throwIfSuspendDetected?.();
208
+ throwIfInterrupted();
203
209
  }
204
210
  catch (error) {
205
211
  if (error instanceof MiningProviderRequestError) {
@@ -341,8 +347,9 @@ export async function runMiningPhaseMachine(options) {
341
347
  assaySentencesImpl: options.assaySentencesImpl,
342
348
  cooperativeYield: options.cooperativeYieldImpl,
343
349
  cooperativeYieldEvery: options.cooperativeYieldEvery,
350
+ throwIfStopping: options.throwIfStopping,
344
351
  });
345
- options.throwIfSuspendDetected?.();
352
+ throwIfInterrupted();
346
353
  state.gateSnapshot = {
347
354
  higherRankedCompetitorDomainCount: gate.higherRankedCompetitorDomainCount,
348
355
  dedupedCompetitorDomainCount: gate.dedupedCompetitorDomainCount,
@@ -421,7 +428,7 @@ export async function runMiningPhaseMachine(options) {
421
428
  if (!await ensureCurrentIndexerTruthOrRestart()) {
422
429
  return;
423
430
  }
424
- options.throwIfSuspendDetected?.();
431
+ throwIfInterrupted();
425
432
  const published = await publishCandidate({
426
433
  dataDir: options.dataDir,
427
434
  databasePath: options.databasePath,
@@ -436,6 +443,7 @@ export async function runMiningPhaseMachine(options) {
436
443
  appendEventFn: async (_paths, event) => {
437
444
  await options.appendEvent(event);
438
445
  },
446
+ throwIfStopping: options.throwIfStopping,
439
447
  });
440
448
  if (state.tipKey !== null
441
449
  && published.retryable !== true
@@ -462,6 +470,7 @@ export async function runMiningPhaseMachine(options) {
462
470
  mempoolSequenceCacheStatus: state.gateSnapshot.mempoolSequenceCacheStatus,
463
471
  lastMempoolSequence: state.gateSnapshot.lastMempoolSequence,
464
472
  lastCompetitivenessGateAtUnixMs: now(),
473
+ lastError: published.lastError ?? null,
465
474
  note: published.note,
466
475
  livePublishInMempool: published.state.miningState.livePublishInMempool,
467
476
  });
@@ -473,7 +482,7 @@ export async function runMiningPhaseMachine(options) {
473
482
  options.loopState.waitingNote = published.note;
474
483
  const lastError = published.decision === "publish-paused-insufficient-funds"
475
484
  ? published.lastError ?? createInsufficientFundsMiningPublishErrorMessage()
476
- : undefined;
485
+ : null;
477
486
  await options.saveCycleStatus({
478
487
  ...options.readContext,
479
488
  localState: {
@@ -526,6 +535,7 @@ export async function runMiningPhaseMachine(options) {
526
535
  mempoolSequenceCacheStatus: state.gateSnapshot.mempoolSequenceCacheStatus,
527
536
  lastMempoolSequence: state.gateSnapshot.lastMempoolSequence,
528
537
  lastCompetitivenessGateAtUnixMs: now(),
538
+ lastError: null,
529
539
  note: options.loopState.waitingNote,
530
540
  livePublishInMempool: published.state.miningState.livePublishInMempool,
531
541
  });
@@ -103,6 +103,7 @@ export interface MiningPublishRetryResult {
103
103
  txid: null;
104
104
  decision: "publish-retry-pending";
105
105
  note: string;
106
+ lastError?: string | null;
106
107
  skipped?: false;
107
108
  retryable: true;
108
109
  candidate: MiningCandidate;
@@ -1,7 +1,7 @@
1
1
  export { isMiningGenerationAbortRequested, markMiningGenerationActive, markMiningGenerationInactive, readMiningGenerationActivity, readMiningPreemptionRequest, requestMiningGenerationPreemption, } from "./coordination.js";
2
2
  export { canonicalizeMiningDomainPromptName, inspectMiningDomainPromptState, updateMiningDomainPrompt, } from "./domain-prompts.js";
3
3
  export { followMiningLog, inspectMiningControlPlane, readMiningLog, refreshMiningRuntimeStatus, setupBuiltInMining, } from "./control.js";
4
- export { ensureBuiltInMiningSetupIfNeeded, runBackgroundMiningWorker, runForegroundMining, startBackgroundMining, stopBackgroundMining, type MiningStartResult, } from "./runner.js";
4
+ export { ensureBuiltInMiningSetupIfNeeded, runForegroundMining, } from "./runner.js";
5
5
  export { appendMiningEvent, loadMiningRuntimeStatus, readMiningEvents, resolveRotatedMiningEventsPath, saveMiningRuntimeStatus, } from "./runtime-artifacts.js";
6
6
  export type { MiningSentenceCandidateV1, MiningSentenceGenerationRequestV1, MiningSentenceGenerationResponseV1, } from "./sentence-protocol.js";
7
7
  export { loadClientConfig, saveBuiltInMiningProviderConfig, saveClientConfig, } from "./config.js";
@@ -1,6 +1,6 @@
1
1
  export { isMiningGenerationAbortRequested, markMiningGenerationActive, markMiningGenerationInactive, readMiningGenerationActivity, readMiningPreemptionRequest, requestMiningGenerationPreemption, } from "./coordination.js";
2
2
  export { canonicalizeMiningDomainPromptName, inspectMiningDomainPromptState, updateMiningDomainPrompt, } from "./domain-prompts.js";
3
3
  export { followMiningLog, inspectMiningControlPlane, readMiningLog, refreshMiningRuntimeStatus, setupBuiltInMining, } from "./control.js";
4
- export { ensureBuiltInMiningSetupIfNeeded, runBackgroundMiningWorker, runForegroundMining, startBackgroundMining, stopBackgroundMining, } from "./runner.js";
4
+ export { ensureBuiltInMiningSetupIfNeeded, runForegroundMining, } from "./runner.js";
5
5
  export { appendMiningEvent, loadMiningRuntimeStatus, readMiningEvents, resolveRotatedMiningEventsPath, saveMiningRuntimeStatus, } from "./runtime-artifacts.js";
6
6
  export { loadClientConfig, saveBuiltInMiningProviderConfig, saveClientConfig, } from "./config.js";