@cogcoin/client 1.1.5 → 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.
- package/README.md +2 -2
- package/dist/bitcoind/indexer-daemon.d.ts +3 -7
- package/dist/bitcoind/indexer-daemon.js +39 -204
- package/dist/bitcoind/managed-runtime/bitcoind-policy.d.ts +16 -0
- package/dist/bitcoind/managed-runtime/bitcoind-policy.js +177 -0
- package/dist/bitcoind/managed-runtime/bitcoind-runtime.d.ts +20 -0
- package/dist/bitcoind/managed-runtime/bitcoind-runtime.js +74 -0
- package/dist/bitcoind/managed-runtime/bitcoind-status.d.ts +11 -0
- package/dist/bitcoind/managed-runtime/bitcoind-status.js +44 -0
- package/dist/bitcoind/managed-runtime/indexer-policy.d.ts +34 -0
- package/dist/bitcoind/managed-runtime/indexer-policy.js +200 -0
- package/dist/bitcoind/managed-runtime/indexer-runtime.d.ts +15 -0
- package/dist/bitcoind/managed-runtime/indexer-runtime.js +82 -0
- package/dist/bitcoind/managed-runtime/status.d.ts +11 -0
- package/dist/bitcoind/managed-runtime/status.js +59 -0
- package/dist/bitcoind/managed-runtime/types.d.ts +77 -0
- package/dist/bitcoind/node.d.ts +2 -2
- package/dist/bitcoind/node.js +2 -2
- package/dist/bitcoind/rpc.d.ts +2 -1
- package/dist/bitcoind/rpc.js +53 -3
- package/dist/bitcoind/service.d.ts +2 -7
- package/dist/bitcoind/service.js +79 -207
- package/dist/cli/command-registry.d.ts +1 -1
- package/dist/cli/command-registry.js +2 -64
- package/dist/cli/commands/client-admin.js +3 -18
- package/dist/cli/commands/mining-runtime.js +4 -60
- package/dist/cli/commands/wallet-admin.js +6 -6
- package/dist/cli/context.js +1 -3
- package/dist/cli/mining-json.d.ts +1 -22
- package/dist/cli/mining-json.js +0 -23
- package/dist/cli/output.js +16 -2
- package/dist/cli/parse.js +0 -2
- package/dist/cli/preview-json.d.ts +1 -22
- package/dist/cli/preview-json.js +0 -19
- package/dist/cli/types.d.ts +1 -3
- package/dist/cli/wallet-format.js +1 -1
- package/dist/cli/workflow-hints.d.ts +1 -2
- package/dist/cli/workflow-hints.js +5 -8
- package/dist/wallet/lifecycle/access.d.ts +5 -0
- package/dist/wallet/lifecycle/access.js +79 -0
- package/dist/wallet/lifecycle/context.d.ts +26 -0
- package/dist/wallet/lifecycle/context.js +57 -0
- package/dist/wallet/lifecycle/managed-core.d.ts +1 -9
- package/dist/wallet/lifecycle/managed-core.js +3 -63
- package/dist/wallet/lifecycle/repair-bitcoind.d.ts +10 -0
- package/dist/wallet/lifecycle/repair-bitcoind.js +142 -0
- package/dist/wallet/lifecycle/repair-indexer.d.ts +8 -0
- package/dist/wallet/lifecycle/repair-indexer.js +117 -0
- package/dist/wallet/lifecycle/repair-mining.d.ts +1 -5
- package/dist/wallet/lifecycle/repair-mining.js +5 -39
- package/dist/wallet/lifecycle/repair.d.ts +2 -4
- package/dist/wallet/lifecycle/repair.js +74 -318
- package/dist/wallet/lifecycle/setup-prompts.d.ts +7 -0
- package/dist/wallet/lifecycle/setup-prompts.js +88 -0
- package/dist/wallet/lifecycle/setup-state.d.ts +26 -0
- package/dist/wallet/lifecycle/setup-state.js +159 -0
- package/dist/wallet/lifecycle/setup.d.ts +3 -4
- package/dist/wallet/lifecycle/setup.js +47 -351
- package/dist/wallet/lifecycle/types.d.ts +33 -5
- package/dist/wallet/managed-core-wallet.d.ts +2 -0
- package/dist/wallet/managed-core-wallet.js +27 -1
- package/dist/wallet/mining/candidate.d.ts +1 -0
- package/dist/wallet/mining/candidate.js +38 -6
- package/dist/wallet/mining/competitiveness.d.ts +1 -0
- package/dist/wallet/mining/competitiveness.js +6 -0
- package/dist/wallet/mining/cycle.d.ts +2 -0
- package/dist/wallet/mining/cycle.js +14 -4
- package/dist/wallet/mining/engine-types.d.ts +1 -0
- package/dist/wallet/mining/index.d.ts +1 -1
- package/dist/wallet/mining/index.js +1 -1
- package/dist/wallet/mining/publish.d.ts +3 -0
- package/dist/wallet/mining/publish.js +78 -6
- package/dist/wallet/mining/runner.d.ts +0 -32
- package/dist/wallet/mining/runner.js +59 -104
- package/dist/wallet/mining/stop.d.ts +7 -0
- package/dist/wallet/mining/stop.js +23 -0
- package/dist/wallet/mining/supervisor.d.ts +2 -36
- package/dist/wallet/mining/supervisor.js +139 -246
- package/dist/wallet/read/context.d.ts +1 -5
- package/dist/wallet/read/context.js +20 -379
- package/dist/wallet/read/managed-services.d.ts +33 -0
- package/dist/wallet/read/managed-services.js +222 -0
- package/dist/wallet/state/client-password/bootstrap.d.ts +2 -0
- package/dist/wallet/state/client-password/bootstrap.js +3 -0
- package/dist/wallet/state/client-password/context.d.ts +10 -0
- package/dist/wallet/state/client-password/context.js +46 -0
- package/dist/wallet/state/client-password/crypto.d.ts +34 -0
- package/dist/wallet/state/client-password/crypto.js +117 -0
- package/dist/wallet/state/client-password/files.d.ts +10 -0
- package/dist/wallet/state/client-password/files.js +109 -0
- package/dist/wallet/state/client-password/legacy-cleanup.d.ts +11 -0
- package/dist/wallet/state/client-password/legacy-cleanup.js +338 -0
- package/dist/wallet/state/client-password/messages.d.ts +3 -0
- package/dist/wallet/state/client-password/messages.js +9 -0
- package/dist/wallet/state/client-password/migration.d.ts +4 -0
- package/dist/wallet/state/client-password/migration.js +32 -0
- package/dist/wallet/state/client-password/prompts.d.ts +12 -0
- package/dist/wallet/state/client-password/prompts.js +79 -0
- package/dist/wallet/state/client-password/protected-secrets.d.ts +13 -0
- package/dist/wallet/state/client-password/protected-secrets.js +90 -0
- package/dist/wallet/state/client-password/readiness.d.ts +4 -0
- package/dist/wallet/state/client-password/readiness.js +48 -0
- package/dist/wallet/state/client-password/references.d.ts +1 -0
- package/dist/wallet/state/client-password/references.js +56 -0
- package/dist/wallet/state/client-password/rotation.d.ts +6 -0
- package/dist/wallet/state/client-password/rotation.js +98 -0
- package/dist/wallet/state/client-password/session-policy.d.ts +6 -0
- package/dist/wallet/state/client-password/session-policy.js +28 -0
- package/dist/wallet/state/client-password/session.d.ts +19 -0
- package/dist/wallet/state/client-password/session.js +170 -0
- package/dist/wallet/state/client-password/setup.d.ts +8 -0
- package/dist/wallet/state/client-password/setup.js +49 -0
- package/dist/wallet/state/client-password/types.d.ts +82 -0
- package/dist/wallet/state/client-password/types.js +5 -0
- package/dist/wallet/state/client-password.d.ts +7 -38
- package/dist/wallet/state/client-password.js +52 -937
- package/dist/wallet/tx/anchor.js +123 -216
- package/dist/wallet/tx/cog.js +294 -489
- package/dist/wallet/tx/common.d.ts +2 -0
- package/dist/wallet/tx/common.js +2 -0
- package/dist/wallet/tx/domain-admin.js +111 -220
- package/dist/wallet/tx/domain-market.js +401 -681
- package/dist/wallet/tx/executor.d.ts +176 -0
- package/dist/wallet/tx/executor.js +302 -0
- package/dist/wallet/tx/field.js +109 -215
- package/dist/wallet/tx/register.js +158 -269
- package/dist/wallet/tx/reputation.js +120 -227
- package/package.json +1 -1
- package/dist/wallet/mining/worker-main.js +0 -17
- package/dist/wallet/state/client-password-agent.d.ts +0 -1
- package/dist/wallet/state/client-password-agent.js +0 -211
- /package/dist/{wallet/mining/worker-main.d.ts → bitcoind/managed-runtime/types.js} +0 -0
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# `@cogcoin/client`
|
|
2
2
|
|
|
3
|
-
`@cogcoin/client@1.1.
|
|
3
|
+
`@cogcoin/client@1.1.7` is the reference Cogcoin client package for applications that want a local wallet, durable SQLite-backed state, and a managed Bitcoin Core integration around `@cogcoin/indexer`. It publishes the reusable client APIs, the SQLite adapter, the managed `bitcoind` integration, and the first-party `cogcoin` CLI in one package.
|
|
4
4
|
|
|
5
5
|
Use Node 22 or newer.
|
|
6
6
|
|
|
@@ -135,7 +135,7 @@ The installed `cogcoin` command covers the first-party local wallet and node wor
|
|
|
135
135
|
- sync and service commands such as `status`, `sync`, `follow`, `bitcoin start`, `bitcoin stop`, `bitcoin status`, `indexer start`, `indexer stop`, and `indexer status`
|
|
136
136
|
- domain and field commands such as `register`, `anchor`, `show`, `domains`, `fields`, `buy`, `sell`, and `transfer`
|
|
137
137
|
- COG and reputation commands such as `send`, `cog lock`, `claim`, `reclaim`, `rep give`, and `rep revoke`
|
|
138
|
-
- mining commands such as `mine`, `mine
|
|
138
|
+
- mining commands such as `mine`, `mine status`, `mine log`, `mine setup`, `mine prompt`, and `mine prompt list`
|
|
139
139
|
|
|
140
140
|
Use `cogcoin mine prompt <domain>` to set or clear a per-domain mining prompt override for one anchored root domain, and `cogcoin mine prompt list` to inspect the current per-domain prompt state alongside the global fallback prompt.
|
|
141
141
|
Interactive text invocations periodically check the npm registry for newer `@cogcoin/client` releases and print `npm install -g @cogcoin/client` when a newer version is available.
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import type { ManagedIndexerDaemonProbeResult } from "./managed-runtime/types.js";
|
|
1
2
|
import { type BootstrapPhase, type BootstrapProgress, type ManagedIndexerDaemonObservedStatus, type ManagedIndexerDaemonStatus } from "./types.js";
|
|
2
3
|
import { resolveManagedServicePaths } from "./service-paths.js";
|
|
4
|
+
export type { IndexerDaemonCompatibility } from "./managed-runtime/types.js";
|
|
3
5
|
export declare const INDEXER_DAEMON_BACKGROUND_FOLLOW_RECOVERY_FAILED = "indexer_daemon_background_follow_recovery_failed";
|
|
4
6
|
interface DaemonRequest {
|
|
5
7
|
id: string;
|
|
@@ -74,13 +76,7 @@ export interface IndexerDaemonClient {
|
|
|
74
76
|
resumeBackgroundFollow(): Promise<void>;
|
|
75
77
|
close(): Promise<void>;
|
|
76
78
|
}
|
|
77
|
-
export type
|
|
78
|
-
export interface IndexerDaemonProbeResult {
|
|
79
|
-
compatibility: IndexerDaemonCompatibility;
|
|
80
|
-
status: ManagedIndexerDaemonObservedStatus | null;
|
|
81
|
-
client: IndexerDaemonClient | null;
|
|
82
|
-
error: string | null;
|
|
83
|
-
}
|
|
79
|
+
export type IndexerDaemonProbeResult = ManagedIndexerDaemonProbeResult<IndexerDaemonClient>;
|
|
84
80
|
export interface IndexerDaemonStopResult {
|
|
85
81
|
status: "stopped" | "not-running";
|
|
86
82
|
walletRootId: string;
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
2
|
import { spawn } from "node:child_process";
|
|
3
|
-
import { mkdir,
|
|
3
|
+
import { mkdir, rm } from "node:fs/promises";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
5
|
import net from "node:net";
|
|
6
|
-
import { compareSemver, parseSemver } from "../semver.js";
|
|
7
6
|
import { acquireFileLock, FileLockBusyError } from "../wallet/fs/lock.js";
|
|
8
7
|
import { writeRuntimeStatusFile } from "../wallet/fs/status-file.js";
|
|
9
|
-
import {
|
|
8
|
+
import { buildManagedIndexerStatusFromSnapshotHandle, mapIndexerDaemonTransportError, mapIndexerDaemonValidationError, validateIndexerDaemonStatus, validateIndexerSnapshotHandle, validateIndexerSnapshotPayload, } from "./managed-runtime/indexer-policy.js";
|
|
9
|
+
import { attachOrStartManagedIndexerRuntime } from "./managed-runtime/indexer-runtime.js";
|
|
10
|
+
import { readJsonFileIfPresent } from "./managed-runtime/status.js";
|
|
11
|
+
import {} from "./types.js";
|
|
10
12
|
import { resolveManagedServicePaths, UNINITIALIZED_WALLET_ROOT_ID } from "./service-paths.js";
|
|
11
13
|
const DEFAULT_STARTUP_TIMEOUT_MS = 30_000;
|
|
12
14
|
const DEFAULT_SHUTDOWN_TIMEOUT_MS = 5_000;
|
|
@@ -15,17 +17,6 @@ const INDEXER_DAEMON_REQUEST_TIMEOUT_MS = 15_000;
|
|
|
15
17
|
const INDEXER_DAEMON_RESUME_BACKGROUND_FOLLOW_REQUEST_TIMEOUT_MS = 35_000;
|
|
16
18
|
const INDEXER_DAEMON_BACKGROUND_FOLLOW_NOT_ACTIVE = "indexer_daemon_background_follow_not_active";
|
|
17
19
|
export const INDEXER_DAEMON_BACKGROUND_FOLLOW_RECOVERY_FAILED = "indexer_daemon_background_follow_recovery_failed";
|
|
18
|
-
async function readJsonFile(filePath) {
|
|
19
|
-
try {
|
|
20
|
-
return JSON.parse(await readFile(filePath, "utf8"));
|
|
21
|
-
}
|
|
22
|
-
catch (error) {
|
|
23
|
-
if (error instanceof Error && "code" in error && error.code === "ENOENT") {
|
|
24
|
-
return null;
|
|
25
|
-
}
|
|
26
|
-
throw error;
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
20
|
async function isProcessAlive(pid) {
|
|
30
21
|
if (pid === null) {
|
|
31
22
|
return false;
|
|
@@ -68,7 +59,7 @@ function ignoreProcessNotFound(error) {
|
|
|
68
59
|
export async function stopIndexerDaemonServiceWithLockHeld(options) {
|
|
69
60
|
const walletRootId = options.walletRootId ?? UNINITIALIZED_WALLET_ROOT_ID;
|
|
70
61
|
const paths = options.paths ?? resolveManagedServicePaths(options.dataDir, walletRootId);
|
|
71
|
-
const status = await
|
|
62
|
+
const status = await readJsonFileIfPresent(paths.indexerDaemonStatusPath);
|
|
72
63
|
const processId = options.processId ?? status?.processId ?? null;
|
|
73
64
|
if (status === null || processId === null || !await isProcessAlive(processId)) {
|
|
74
65
|
await clearIndexerDaemonRuntimeArtifacts(paths);
|
|
@@ -217,97 +208,6 @@ function createIndexerDaemonClient(socketPath, closeOptions = null) {
|
|
|
217
208
|
},
|
|
218
209
|
};
|
|
219
210
|
}
|
|
220
|
-
function validateIndexerRuntimeIdentity(identity, expectedWalletRootId) {
|
|
221
|
-
if (identity.serviceApiVersion !== INDEXER_DAEMON_SERVICE_API_VERSION) {
|
|
222
|
-
throw new Error("indexer_daemon_service_version_mismatch");
|
|
223
|
-
}
|
|
224
|
-
if (identity.schemaVersion !== INDEXER_DAEMON_SCHEMA_VERSION || identity.state === "schema-mismatch") {
|
|
225
|
-
throw new Error("indexer_daemon_schema_mismatch");
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
function validateIndexerDaemonStatus(status, expectedWalletRootId) {
|
|
229
|
-
validateIndexerRuntimeIdentity(status, expectedWalletRootId);
|
|
230
|
-
}
|
|
231
|
-
function validateIndexerSnapshotHandle(handle, expectedWalletRootId) {
|
|
232
|
-
validateIndexerRuntimeIdentity(handle, expectedWalletRootId);
|
|
233
|
-
}
|
|
234
|
-
function validateIndexerSnapshotPayload(payload, handle, expectedWalletRootId) {
|
|
235
|
-
validateIndexerRuntimeIdentity(payload, expectedWalletRootId);
|
|
236
|
-
if (payload.token !== handle.token
|
|
237
|
-
|| payload.daemonInstanceId !== handle.daemonInstanceId
|
|
238
|
-
|| payload.processId !== handle.processId
|
|
239
|
-
|| payload.startedAtUnixMs !== handle.startedAtUnixMs
|
|
240
|
-
|| payload.snapshotSeq !== handle.snapshotSeq
|
|
241
|
-
|| payload.tipHeight !== handle.tipHeight
|
|
242
|
-
|| payload.tipHash !== handle.tipHash
|
|
243
|
-
|| payload.openedAtUnixMs !== handle.openedAtUnixMs) {
|
|
244
|
-
throw new Error("indexer_daemon_snapshot_identity_mismatch");
|
|
245
|
-
}
|
|
246
|
-
if (payload.tip === null) {
|
|
247
|
-
if (payload.tipHeight !== null || payload.tipHash !== null) {
|
|
248
|
-
throw new Error("indexer_daemon_snapshot_identity_mismatch");
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
else if (payload.tip.height !== payload.tipHeight || payload.tip.blockHashHex !== payload.tipHash) {
|
|
252
|
-
throw new Error("indexer_daemon_snapshot_identity_mismatch");
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
function isUnreachableIndexerDaemonError(error) {
|
|
256
|
-
if (error instanceof Error) {
|
|
257
|
-
if (error.message === "indexer_daemon_connection_closed"
|
|
258
|
-
|| error.message === "indexer_daemon_request_timeout"
|
|
259
|
-
|| error.message === "indexer_daemon_protocol_error") {
|
|
260
|
-
return false;
|
|
261
|
-
}
|
|
262
|
-
if ("code" in error) {
|
|
263
|
-
const code = error.code;
|
|
264
|
-
return code === "ENOENT" || code === "ECONNREFUSED" || code === "ECONNRESET";
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
return false;
|
|
268
|
-
}
|
|
269
|
-
function buildStatusFromSnapshotHandle(handle) {
|
|
270
|
-
return {
|
|
271
|
-
serviceApiVersion: INDEXER_DAEMON_SERVICE_API_VERSION,
|
|
272
|
-
binaryVersion: handle.binaryVersion,
|
|
273
|
-
buildId: handle.buildId,
|
|
274
|
-
updatedAtUnixMs: Math.max(handle.heartbeatAtUnixMs, handle.openedAtUnixMs),
|
|
275
|
-
walletRootId: handle.walletRootId,
|
|
276
|
-
daemonInstanceId: handle.daemonInstanceId,
|
|
277
|
-
schemaVersion: INDEXER_DAEMON_SCHEMA_VERSION,
|
|
278
|
-
state: handle.state,
|
|
279
|
-
processId: handle.processId,
|
|
280
|
-
startedAtUnixMs: handle.startedAtUnixMs,
|
|
281
|
-
heartbeatAtUnixMs: handle.heartbeatAtUnixMs,
|
|
282
|
-
ipcReady: true,
|
|
283
|
-
rpcReachable: handle.rpcReachable,
|
|
284
|
-
coreBestHeight: handle.coreBestHeight,
|
|
285
|
-
coreBestHash: handle.coreBestHash,
|
|
286
|
-
appliedTipHeight: handle.appliedTipHeight,
|
|
287
|
-
appliedTipHash: handle.appliedTipHash,
|
|
288
|
-
snapshotSeq: handle.snapshotSeq,
|
|
289
|
-
backlogBlocks: handle.backlogBlocks,
|
|
290
|
-
reorgDepth: handle.reorgDepth,
|
|
291
|
-
lastAppliedAtUnixMs: handle.lastAppliedAtUnixMs,
|
|
292
|
-
activeSnapshotCount: handle.activeSnapshotCount,
|
|
293
|
-
lastError: handle.lastError,
|
|
294
|
-
backgroundFollowActive: handle.backgroundFollowActive,
|
|
295
|
-
bootstrapPhase: handle.bootstrapPhase,
|
|
296
|
-
bootstrapProgress: handle.bootstrapProgress,
|
|
297
|
-
cogcoinSyncHeight: handle.cogcoinSyncHeight,
|
|
298
|
-
cogcoinSyncTargetHeight: handle.cogcoinSyncTargetHeight,
|
|
299
|
-
};
|
|
300
|
-
}
|
|
301
|
-
function isStaleIndexerDaemonVersion(status, expectedBinaryVersion) {
|
|
302
|
-
if (status === null || expectedBinaryVersion === null || expectedBinaryVersion === undefined) {
|
|
303
|
-
return false;
|
|
304
|
-
}
|
|
305
|
-
if (parseSemver(expectedBinaryVersion) === null) {
|
|
306
|
-
return false;
|
|
307
|
-
}
|
|
308
|
-
const comparison = compareSemver(status.binaryVersion, expectedBinaryVersion);
|
|
309
|
-
return comparison === null || comparison < 0;
|
|
310
|
-
}
|
|
311
211
|
async function probeIndexerDaemonAtSocket(socketPath, expectedWalletRootId) {
|
|
312
212
|
const client = createIndexerDaemonClient(socketPath);
|
|
313
213
|
try {
|
|
@@ -323,30 +223,12 @@ async function probeIndexerDaemonAtSocket(socketPath, expectedWalletRootId) {
|
|
|
323
223
|
}
|
|
324
224
|
catch (error) {
|
|
325
225
|
await client.close().catch(() => undefined);
|
|
326
|
-
return
|
|
327
|
-
compatibility: error instanceof Error
|
|
328
|
-
? error.message === "indexer_daemon_service_version_mismatch"
|
|
329
|
-
? "service-version-mismatch"
|
|
330
|
-
: "schema-mismatch"
|
|
331
|
-
: "protocol-error",
|
|
332
|
-
status,
|
|
333
|
-
client: null,
|
|
334
|
-
error: error instanceof Error ? error.message : "indexer_daemon_protocol_error",
|
|
335
|
-
};
|
|
226
|
+
return mapIndexerDaemonValidationError(error, status);
|
|
336
227
|
}
|
|
337
228
|
}
|
|
338
229
|
catch (error) {
|
|
339
230
|
await client.close().catch(() => undefined);
|
|
340
|
-
return
|
|
341
|
-
compatibility: isUnreachableIndexerDaemonError(error) ? "unreachable" : "protocol-error",
|
|
342
|
-
status: null,
|
|
343
|
-
client: null,
|
|
344
|
-
error: isUnreachableIndexerDaemonError(error)
|
|
345
|
-
? null
|
|
346
|
-
: error instanceof Error
|
|
347
|
-
? "indexer_daemon_protocol_error"
|
|
348
|
-
: "indexer_daemon_protocol_error",
|
|
349
|
-
};
|
|
231
|
+
return mapIndexerDaemonTransportError(error);
|
|
350
232
|
}
|
|
351
233
|
}
|
|
352
234
|
async function waitForIndexerDaemon(dataDir, walletRootId, timeoutMs) {
|
|
@@ -380,7 +262,7 @@ export async function readSnapshotWithRetry(daemon, expectedWalletRootId) {
|
|
|
380
262
|
validateIndexerSnapshotPayload(payload, handle, expectedWalletRootId);
|
|
381
263
|
return {
|
|
382
264
|
payload,
|
|
383
|
-
status:
|
|
265
|
+
status: buildManagedIndexerStatusFromSnapshotHandle(handle),
|
|
384
266
|
};
|
|
385
267
|
}
|
|
386
268
|
catch (error) {
|
|
@@ -400,7 +282,7 @@ export async function readSnapshotWithRetry(daemon, expectedWalletRootId) {
|
|
|
400
282
|
export async function readObservedIndexerDaemonStatus(options) {
|
|
401
283
|
const walletRootId = options.walletRootId ?? UNINITIALIZED_WALLET_ROOT_ID;
|
|
402
284
|
const paths = resolveManagedServicePaths(options.dataDir, walletRootId);
|
|
403
|
-
return
|
|
285
|
+
return readJsonFileIfPresent(paths.indexerDaemonStatusPath);
|
|
404
286
|
}
|
|
405
287
|
export async function attachOrStartIndexerDaemon(options) {
|
|
406
288
|
const requestBackgroundFollow = async (client, observedStatus = null) => {
|
|
@@ -466,82 +348,35 @@ export async function attachOrStartIndexerDaemon(options) {
|
|
|
466
348
|
shutdownTimeoutMs: options.shutdownTimeoutMs,
|
|
467
349
|
});
|
|
468
350
|
};
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
}
|
|
483
|
-
if (existingProbe.compatibility !== "unreachable" && existingProbe.compatibility !== "compatible") {
|
|
484
|
-
throw new Error(existingProbe.error ?? "indexer_daemon_protocol_error");
|
|
485
|
-
}
|
|
486
|
-
try {
|
|
487
|
-
const lock = await acquireFileLock(paths.indexerDaemonLockPath, {
|
|
351
|
+
return attachOrStartManagedIndexerRuntime({
|
|
352
|
+
...options,
|
|
353
|
+
walletRootId,
|
|
354
|
+
startupTimeoutMs,
|
|
355
|
+
expectedBinaryVersion,
|
|
356
|
+
}, {
|
|
357
|
+
getPaths: (runtimeOptions) => resolveManagedServicePaths(runtimeOptions.dataDir, runtimeOptions.walletRootId),
|
|
358
|
+
probeDaemon: async (runtimeOptions, runtimePaths) => probeIndexerDaemonAtSocket(runtimePaths.indexerDaemonSocketPath, runtimeOptions.walletRootId),
|
|
359
|
+
requestBackgroundFollow,
|
|
360
|
+
closeClient: async (client) => {
|
|
361
|
+
await client.close();
|
|
362
|
+
},
|
|
363
|
+
acquireStartLock: async (runtimeOptions, runtimePaths) => acquireFileLock(runtimePaths.indexerDaemonLockPath, {
|
|
488
364
|
purpose: "indexer-daemon-start",
|
|
489
|
-
walletRootId,
|
|
490
|
-
dataDir:
|
|
491
|
-
databasePath:
|
|
492
|
-
})
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
walletRootId,
|
|
505
|
-
shutdownTimeoutMs: options.shutdownTimeoutMs,
|
|
506
|
-
paths,
|
|
507
|
-
processId: liveProbe.status?.processId ?? null,
|
|
508
|
-
});
|
|
509
|
-
}
|
|
510
|
-
}
|
|
511
|
-
else {
|
|
512
|
-
await liveProbe.client.close().catch(() => undefined);
|
|
513
|
-
await stopIndexerDaemonServiceWithLockHeld({
|
|
514
|
-
dataDir: options.dataDir,
|
|
515
|
-
walletRootId,
|
|
516
|
-
shutdownTimeoutMs: options.shutdownTimeoutMs,
|
|
517
|
-
paths,
|
|
518
|
-
processId: liveProbe.status?.processId ?? null,
|
|
519
|
-
});
|
|
520
|
-
}
|
|
521
|
-
}
|
|
522
|
-
else if (liveProbe.compatibility !== "unreachable") {
|
|
523
|
-
throw new Error(liveProbe.error ?? "indexer_daemon_protocol_error");
|
|
524
|
-
}
|
|
525
|
-
const daemon = await startDaemon();
|
|
526
|
-
try {
|
|
527
|
-
return await requestBackgroundFollow(daemon);
|
|
528
|
-
}
|
|
529
|
-
catch (error) {
|
|
530
|
-
await daemon.close().catch(() => undefined);
|
|
531
|
-
throw new Error(INDEXER_DAEMON_BACKGROUND_FOLLOW_RECOVERY_FAILED, { cause: error });
|
|
532
|
-
}
|
|
533
|
-
}
|
|
534
|
-
finally {
|
|
535
|
-
await lock.release();
|
|
536
|
-
}
|
|
537
|
-
}
|
|
538
|
-
catch (error) {
|
|
539
|
-
if (error instanceof FileLockBusyError) {
|
|
540
|
-
await waitForIndexerDaemon(options.dataDir, walletRootId, startupTimeoutMs);
|
|
541
|
-
return attachOrStartIndexerDaemon(options);
|
|
542
|
-
}
|
|
543
|
-
throw error;
|
|
544
|
-
}
|
|
365
|
+
walletRootId: runtimeOptions.walletRootId,
|
|
366
|
+
dataDir: runtimeOptions.dataDir,
|
|
367
|
+
databasePath: runtimeOptions.databasePath,
|
|
368
|
+
}),
|
|
369
|
+
startDaemon: async () => startDaemon(),
|
|
370
|
+
stopWithLockHeld: async (runtimeOptions, runtimePaths, processId) => stopIndexerDaemonServiceWithLockHeld({
|
|
371
|
+
dataDir: runtimeOptions.dataDir,
|
|
372
|
+
walletRootId: runtimeOptions.walletRootId,
|
|
373
|
+
shutdownTimeoutMs: runtimeOptions.shutdownTimeoutMs,
|
|
374
|
+
paths: resolveManagedServicePaths(runtimeOptions.dataDir, runtimeOptions.walletRootId),
|
|
375
|
+
processId,
|
|
376
|
+
}),
|
|
377
|
+
isLockBusyError: (error) => error instanceof FileLockBusyError,
|
|
378
|
+
sleep,
|
|
379
|
+
});
|
|
545
380
|
}
|
|
546
381
|
export async function stopIndexerDaemonService(options) {
|
|
547
382
|
const walletRootId = options.walletRootId ?? UNINITIALIZED_WALLET_ROOT_ID;
|
|
@@ -572,7 +407,7 @@ export async function shutdownIndexerDaemonForTesting(options) {
|
|
|
572
407
|
export async function readIndexerDaemonStatusForTesting(options) {
|
|
573
408
|
const walletRootId = options.walletRootId ?? UNINITIALIZED_WALLET_ROOT_ID;
|
|
574
409
|
const paths = resolveManagedServicePaths(options.dataDir, walletRootId);
|
|
575
|
-
return
|
|
410
|
+
return readJsonFileIfPresent(paths.indexerDaemonStatusPath);
|
|
576
411
|
}
|
|
577
412
|
export async function writeIndexerDaemonStatusForTesting(options, status) {
|
|
578
413
|
const walletRootId = options.walletRootId ?? UNINITIALIZED_WALLET_ROOT_ID;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { ManagedBitcoindObservedStatus } from "../types.js";
|
|
2
|
+
import type { WalletBitcoindStatus, WalletNodeStatus } from "../../wallet/read/types.js";
|
|
3
|
+
import type { ManagedBitcoindProbeDecision, ManagedBitcoindServiceProbeResult } from "./types.js";
|
|
4
|
+
export declare function validateManagedBitcoindObservedStatus(status: ManagedBitcoindObservedStatus, options: {
|
|
5
|
+
chain: "main" | "regtest";
|
|
6
|
+
dataDir: string;
|
|
7
|
+
runtimeRoot: string;
|
|
8
|
+
}): void;
|
|
9
|
+
export declare function mapManagedBitcoindValidationError(error: unknown, status: ManagedBitcoindObservedStatus): ManagedBitcoindServiceProbeResult;
|
|
10
|
+
export declare function mapManagedBitcoindRuntimeProbeFailure(error: unknown, status: ManagedBitcoindObservedStatus): ManagedBitcoindServiceProbeResult;
|
|
11
|
+
export declare function resolveManagedBitcoindProbeDecision(probe: ManagedBitcoindServiceProbeResult): ManagedBitcoindProbeDecision;
|
|
12
|
+
export declare function deriveManagedBitcoindWalletStatus(options: {
|
|
13
|
+
status: ManagedBitcoindObservedStatus | null;
|
|
14
|
+
nodeStatus: WalletNodeStatus | null;
|
|
15
|
+
startupError: string | null;
|
|
16
|
+
}): WalletBitcoindStatus;
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
import { resolveManagedServicePaths } from "../service-paths.js";
|
|
3
|
+
import { MANAGED_BITCOIND_SERVICE_API_VERSION } from "../types.js";
|
|
4
|
+
function isRuntimeMismatchError(error) {
|
|
5
|
+
if (!(error instanceof Error)) {
|
|
6
|
+
return false;
|
|
7
|
+
}
|
|
8
|
+
return error.message.startsWith("bitcoind_chain_expected_")
|
|
9
|
+
|| error.message === "managed_bitcoind_runtime_mismatch";
|
|
10
|
+
}
|
|
11
|
+
function isUnreachableManagedBitcoindError(error) {
|
|
12
|
+
if (error instanceof Error) {
|
|
13
|
+
if ("code" in error) {
|
|
14
|
+
const code = error.code;
|
|
15
|
+
return code === "ENOENT" || code === "ECONNREFUSED" || code === "ECONNRESET";
|
|
16
|
+
}
|
|
17
|
+
return error.message === "bitcoind_cookie_timeout"
|
|
18
|
+
|| error.message.includes("cookie file is unavailable")
|
|
19
|
+
|| error.message.includes("ECONNREFUSED")
|
|
20
|
+
|| error.message.includes("ECONNRESET")
|
|
21
|
+
|| error.message.includes("socket hang up");
|
|
22
|
+
}
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
export function validateManagedBitcoindObservedStatus(status, options) {
|
|
26
|
+
const legacyRuntimeRoot = join(resolveManagedServicePaths(options.dataDir, status.walletRootId).runtimeRoot, status.walletRootId);
|
|
27
|
+
if (status.serviceApiVersion !== MANAGED_BITCOIND_SERVICE_API_VERSION) {
|
|
28
|
+
throw new Error("managed_bitcoind_service_version_mismatch");
|
|
29
|
+
}
|
|
30
|
+
// Managed bitcoind runtimes are adopted across wallet roots when the live
|
|
31
|
+
// runtime still points at the expected data dir and chain.
|
|
32
|
+
if (status.chain !== options.chain
|
|
33
|
+
|| status.dataDir !== options.dataDir
|
|
34
|
+
|| (status.runtimeRoot !== options.runtimeRoot && status.runtimeRoot !== legacyRuntimeRoot)) {
|
|
35
|
+
throw new Error("managed_bitcoind_runtime_mismatch");
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
export function mapManagedBitcoindValidationError(error, status) {
|
|
39
|
+
return {
|
|
40
|
+
compatibility: error instanceof Error
|
|
41
|
+
? error.message === "managed_bitcoind_service_version_mismatch"
|
|
42
|
+
? "service-version-mismatch"
|
|
43
|
+
: "runtime-mismatch"
|
|
44
|
+
: "protocol-error",
|
|
45
|
+
status,
|
|
46
|
+
error: error instanceof Error ? error.message : "managed_bitcoind_protocol_error",
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
export function mapManagedBitcoindRuntimeProbeFailure(error, status) {
|
|
50
|
+
if (isRuntimeMismatchError(error)) {
|
|
51
|
+
return {
|
|
52
|
+
compatibility: "runtime-mismatch",
|
|
53
|
+
status,
|
|
54
|
+
error: "managed_bitcoind_runtime_mismatch",
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
if (isUnreachableManagedBitcoindError(error)) {
|
|
58
|
+
return {
|
|
59
|
+
compatibility: "unreachable",
|
|
60
|
+
status,
|
|
61
|
+
error: null,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
return {
|
|
65
|
+
compatibility: "protocol-error",
|
|
66
|
+
status,
|
|
67
|
+
error: "managed_bitcoind_protocol_error",
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
export function resolveManagedBitcoindProbeDecision(probe) {
|
|
71
|
+
if (probe.compatibility === "compatible") {
|
|
72
|
+
return {
|
|
73
|
+
action: "attach",
|
|
74
|
+
error: null,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
if (probe.compatibility === "unreachable") {
|
|
78
|
+
return {
|
|
79
|
+
action: "start",
|
|
80
|
+
error: null,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
return {
|
|
84
|
+
action: "reject",
|
|
85
|
+
error: probe.error ?? "managed_bitcoind_protocol_error",
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
function mapManagedBitcoindStartupError(message) {
|
|
89
|
+
switch (message) {
|
|
90
|
+
case "managed_bitcoind_service_start_timeout":
|
|
91
|
+
return {
|
|
92
|
+
health: "starting",
|
|
93
|
+
status: null,
|
|
94
|
+
message: "Managed bitcoind service is still starting.",
|
|
95
|
+
};
|
|
96
|
+
case "managed_bitcoind_service_version_mismatch":
|
|
97
|
+
return {
|
|
98
|
+
health: "service-version-mismatch",
|
|
99
|
+
status: null,
|
|
100
|
+
message: "The live managed bitcoind service is running an incompatible service version.",
|
|
101
|
+
};
|
|
102
|
+
case "managed_bitcoind_wallet_root_mismatch":
|
|
103
|
+
return {
|
|
104
|
+
health: "wallet-root-mismatch",
|
|
105
|
+
status: null,
|
|
106
|
+
message: "The live managed bitcoind service belongs to a different wallet root.",
|
|
107
|
+
};
|
|
108
|
+
case "managed_bitcoind_runtime_mismatch":
|
|
109
|
+
return {
|
|
110
|
+
health: "runtime-mismatch",
|
|
111
|
+
status: null,
|
|
112
|
+
message: "The live managed bitcoind service runtime does not match this wallet's expected data directory or chain.",
|
|
113
|
+
};
|
|
114
|
+
case "managed_bitcoind_protocol_error":
|
|
115
|
+
return {
|
|
116
|
+
health: "unavailable",
|
|
117
|
+
status: null,
|
|
118
|
+
message: "The managed bitcoind runtime artifacts are invalid or incomplete.",
|
|
119
|
+
};
|
|
120
|
+
default:
|
|
121
|
+
return {
|
|
122
|
+
health: "unavailable",
|
|
123
|
+
status: null,
|
|
124
|
+
message,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
export function deriveManagedBitcoindWalletStatus(options) {
|
|
129
|
+
if (options.startupError !== null) {
|
|
130
|
+
const mapped = mapManagedBitcoindStartupError(options.startupError);
|
|
131
|
+
return {
|
|
132
|
+
...mapped,
|
|
133
|
+
status: options.status,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
if (options.status === null) {
|
|
137
|
+
return {
|
|
138
|
+
health: "unavailable",
|
|
139
|
+
status: null,
|
|
140
|
+
message: "Managed bitcoind service is unavailable.",
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
if (options.status.state === "starting") {
|
|
144
|
+
return {
|
|
145
|
+
health: "starting",
|
|
146
|
+
status: options.status,
|
|
147
|
+
message: options.status.lastError ?? "Managed bitcoind service is still starting.",
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
if (options.status.state === "failed") {
|
|
151
|
+
return {
|
|
152
|
+
health: "failed",
|
|
153
|
+
status: options.status,
|
|
154
|
+
message: options.status.lastError ?? "Managed bitcoind service refresh failed.",
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
const proofStatus = options.nodeStatus?.walletReplica?.proofStatus;
|
|
158
|
+
if (proofStatus === "missing") {
|
|
159
|
+
return {
|
|
160
|
+
health: "replica-missing",
|
|
161
|
+
status: options.status,
|
|
162
|
+
message: options.nodeStatus?.walletReplicaMessage ?? "Managed Core wallet replica is missing.",
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
if (proofStatus === "mismatch") {
|
|
166
|
+
return {
|
|
167
|
+
health: "replica-mismatch",
|
|
168
|
+
status: options.status,
|
|
169
|
+
message: options.nodeStatus?.walletReplicaMessage ?? "Managed Core wallet replica does not match trusted wallet state.",
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
return {
|
|
173
|
+
health: "ready",
|
|
174
|
+
status: options.status,
|
|
175
|
+
message: options.nodeStatus?.walletReplicaMessage ?? options.status.lastError,
|
|
176
|
+
};
|
|
177
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { ManagedBitcoindObservedStatus } from "../types.js";
|
|
2
|
+
import type { ManagedBitcoindRuntimeOptionsLike, ManagedBitcoindRuntimePathsLike, ManagedBitcoindServiceProbeResult, ManagedBitcoindStatusCandidate, ManagedRuntimeLockLike } from "./types.js";
|
|
3
|
+
type ManagedBitcoindRuntimeDependencies<TOptions, THandle> = {
|
|
4
|
+
getPaths(options: TOptions): ManagedBitcoindRuntimePathsLike;
|
|
5
|
+
listStatusCandidates(options: {
|
|
6
|
+
dataDir: string;
|
|
7
|
+
runtimeRoot: string;
|
|
8
|
+
expectedStatusPath: string;
|
|
9
|
+
}): Promise<ManagedBitcoindStatusCandidate[]>;
|
|
10
|
+
isProcessAlive(processId: number | null): Promise<boolean>;
|
|
11
|
+
probeStatusCandidate(status: ManagedBitcoindObservedStatus, options: TOptions, runtimeRoot: string): Promise<ManagedBitcoindServiceProbeResult>;
|
|
12
|
+
attachExisting(options: TOptions): Promise<THandle | null>;
|
|
13
|
+
acquireStartLock(options: TOptions, paths: ManagedBitcoindRuntimePathsLike): Promise<ManagedRuntimeLockLike>;
|
|
14
|
+
startService(options: TOptions, paths: ManagedBitcoindRuntimePathsLike): Promise<THandle>;
|
|
15
|
+
isLockBusyError(error: unknown): boolean;
|
|
16
|
+
sleep(ms: number): Promise<void>;
|
|
17
|
+
};
|
|
18
|
+
export declare function probeManagedBitcoindRuntime<TOptions extends ManagedBitcoindRuntimeOptionsLike>(options: TOptions, dependencies: Pick<ManagedBitcoindRuntimeDependencies<TOptions, never>, "getPaths" | "listStatusCandidates" | "isProcessAlive" | "probeStatusCandidate">): Promise<ManagedBitcoindServiceProbeResult>;
|
|
19
|
+
export declare function attachOrStartManagedBitcoindRuntime<TOptions extends ManagedBitcoindRuntimeOptionsLike, THandle>(options: TOptions, dependencies: ManagedBitcoindRuntimeDependencies<TOptions, THandle>): Promise<THandle>;
|
|
20
|
+
export {};
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { resolveManagedBitcoindProbeDecision } from "./bitcoind-policy.js";
|
|
2
|
+
async function waitForManagedBitcoindRuntime(options, dependencies) {
|
|
3
|
+
const deadline = Date.now() + options.startupTimeoutMs;
|
|
4
|
+
while (Date.now() < deadline) {
|
|
5
|
+
const attached = await dependencies.attachExisting(options).catch(() => null);
|
|
6
|
+
if (attached !== null) {
|
|
7
|
+
return attached;
|
|
8
|
+
}
|
|
9
|
+
await dependencies.sleep(250);
|
|
10
|
+
}
|
|
11
|
+
throw new Error("managed_bitcoind_service_start_timeout");
|
|
12
|
+
}
|
|
13
|
+
export async function probeManagedBitcoindRuntime(options, dependencies) {
|
|
14
|
+
const paths = dependencies.getPaths(options);
|
|
15
|
+
const candidates = await dependencies.listStatusCandidates({
|
|
16
|
+
dataDir: options.dataDir,
|
|
17
|
+
runtimeRoot: paths.runtimeRoot,
|
|
18
|
+
expectedStatusPath: paths.bitcoindStatusPath,
|
|
19
|
+
});
|
|
20
|
+
const expectedCandidate = candidates.find((candidate) => candidate.statusPath === paths.bitcoindStatusPath) ?? null;
|
|
21
|
+
for (const candidate of candidates) {
|
|
22
|
+
if (!await dependencies.isProcessAlive(candidate.status.processId)) {
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
return dependencies.probeStatusCandidate(candidate.status, options, paths.walletRuntimeRoot);
|
|
26
|
+
}
|
|
27
|
+
return {
|
|
28
|
+
compatibility: "unreachable",
|
|
29
|
+
status: expectedCandidate?.status ?? candidates[0]?.status ?? null,
|
|
30
|
+
error: null,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
export async function attachOrStartManagedBitcoindRuntime(options, dependencies) {
|
|
34
|
+
const existingProbe = await probeManagedBitcoindRuntime(options, dependencies);
|
|
35
|
+
const existingDecision = resolveManagedBitcoindProbeDecision(existingProbe);
|
|
36
|
+
if (existingDecision.action === "attach") {
|
|
37
|
+
const existing = await dependencies.attachExisting(options);
|
|
38
|
+
if (existing !== null) {
|
|
39
|
+
return existing;
|
|
40
|
+
}
|
|
41
|
+
throw new Error("managed_bitcoind_protocol_error");
|
|
42
|
+
}
|
|
43
|
+
if (existingDecision.action === "reject") {
|
|
44
|
+
throw new Error(existingDecision.error ?? "managed_bitcoind_protocol_error");
|
|
45
|
+
}
|
|
46
|
+
const paths = dependencies.getPaths(options);
|
|
47
|
+
try {
|
|
48
|
+
const lock = await dependencies.acquireStartLock(options, paths);
|
|
49
|
+
try {
|
|
50
|
+
const liveProbe = await probeManagedBitcoindRuntime(options, dependencies);
|
|
51
|
+
const liveDecision = resolveManagedBitcoindProbeDecision(liveProbe);
|
|
52
|
+
if (liveDecision.action === "attach") {
|
|
53
|
+
const reattached = await dependencies.attachExisting(options);
|
|
54
|
+
if (reattached !== null) {
|
|
55
|
+
return reattached;
|
|
56
|
+
}
|
|
57
|
+
throw new Error("managed_bitcoind_protocol_error");
|
|
58
|
+
}
|
|
59
|
+
if (liveDecision.action === "reject") {
|
|
60
|
+
throw new Error(liveDecision.error ?? "managed_bitcoind_protocol_error");
|
|
61
|
+
}
|
|
62
|
+
return await dependencies.startService(options, paths);
|
|
63
|
+
}
|
|
64
|
+
finally {
|
|
65
|
+
await lock.release();
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
if (dependencies.isLockBusyError(error)) {
|
|
70
|
+
return waitForManagedBitcoindRuntime(options, dependencies);
|
|
71
|
+
}
|
|
72
|
+
throw error;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ManagedBitcoindObservedStatus } from "../types.js";
|
|
2
|
+
import type { ManagedBitcoindStatusCandidate } from "./types.js";
|
|
3
|
+
export declare function listManagedBitcoindStatusCandidates(options: {
|
|
4
|
+
dataDir: string;
|
|
5
|
+
runtimeRoot: string;
|
|
6
|
+
expectedStatusPath: string;
|
|
7
|
+
}): Promise<ManagedBitcoindStatusCandidate[]>;
|
|
8
|
+
export declare function readManagedBitcoindObservedStatus(options: {
|
|
9
|
+
dataDir: string;
|
|
10
|
+
walletRootId: string;
|
|
11
|
+
}): Promise<ManagedBitcoindObservedStatus | null>;
|