@cogcoin/client 1.1.6 → 1.1.8
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.js +29 -79
- 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-runtime.d.ts +15 -0
- package/dist/bitcoind/managed-runtime/indexer-runtime.js +82 -0
- package/dist/bitcoind/managed-runtime/types.d.ts +40 -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.js +47 -127
- 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/context.js +0 -1
- package/dist/wallet/lifecycle/repair-mining.d.ts +1 -5
- package/dist/wallet/lifecycle/repair-mining.js +5 -39
- package/dist/wallet/lifecycle/repair.js +0 -3
- package/dist/wallet/lifecycle/setup.js +10 -8
- package/dist/wallet/lifecycle/types.d.ts +1 -4
- 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-state.js +10 -0
- 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/mining/visualizer-sync.js +79 -15
- package/dist/wallet/read/context.d.ts +1 -5
- package/dist/wallet/read/context.js +21 -205
- package/dist/wallet/read/managed-services.d.ts +33 -0
- package/dist/wallet/read/managed-services.js +222 -0
- package/dist/wallet/reset/artifacts.d.ts +16 -0
- package/dist/wallet/reset/artifacts.js +141 -0
- package/dist/wallet/reset/execution.d.ts +38 -0
- package/dist/wallet/reset/execution.js +458 -0
- package/dist/wallet/reset/preflight.d.ts +7 -0
- package/dist/wallet/reset/preflight.js +116 -0
- package/dist/wallet/reset/preview.d.ts +2 -0
- package/dist/wallet/reset/preview.js +50 -0
- package/dist/wallet/reset/process-cleanup.d.ts +12 -0
- package/dist/wallet/reset/process-cleanup.js +179 -0
- package/dist/wallet/reset/types.d.ts +189 -0
- package/dist/wallet/reset/types.js +1 -0
- package/dist/wallet/reset.d.ts +4 -119
- package/dist/wallet/reset.js +4 -882
- 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.d.ts +0 -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
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { spawn } from "node:child_process";
|
|
2
1
|
import { rm } from "node:fs/promises";
|
|
3
2
|
import { createRpcClient } from "../../bitcoind/node.js";
|
|
4
3
|
import { attachOrStartManagedBitcoindService } from "../../bitcoind/service.js";
|
|
@@ -8,7 +7,6 @@ import { openWalletReadContext } from "../read/index.js";
|
|
|
8
7
|
import type { WalletRuntimePaths } from "../runtime.js";
|
|
9
8
|
import type { WalletSecretProvider } from "../state/provider.js";
|
|
10
9
|
import { requestMiningGenerationPreemption } from "./coordination.js";
|
|
11
|
-
import { inspectMiningControlPlane } from "./control.js";
|
|
12
10
|
import { saveStopSnapshot } from "./lifecycle.js";
|
|
13
11
|
import type { MiningRpcClient } from "./engine-types.js";
|
|
14
12
|
import { loadMiningRuntimeStatus, saveMiningRuntimeStatus } from "./runtime-artifacts.js";
|
|
@@ -19,9 +17,8 @@ type AttachService = typeof attachOrStartManagedBitcoindService;
|
|
|
19
17
|
type RpcFactory = (config: Parameters<typeof createRpcClient>[0]) => MiningRpcClient;
|
|
20
18
|
type RequestMiningPreemption = typeof requestMiningGenerationPreemption;
|
|
21
19
|
type SaveStopSnapshot = typeof saveStopSnapshot;
|
|
22
|
-
type SpawnWorkerProcess = typeof spawn;
|
|
23
20
|
type ProcessKill = typeof process.kill;
|
|
24
|
-
type
|
|
21
|
+
type ForceExit = (code: number) => never | void;
|
|
25
22
|
interface MiningLoopRunnerOptions {
|
|
26
23
|
dataDir: string;
|
|
27
24
|
databasePath: string;
|
|
@@ -51,9 +48,7 @@ export interface MiningSupervisorRuntimeContext {
|
|
|
51
48
|
interface MiningSupervisorDependencies {
|
|
52
49
|
requestMiningPreemption: RequestMiningPreemption;
|
|
53
50
|
saveStopSnapshot: SaveStopSnapshot;
|
|
54
|
-
spawnWorkerProcess: SpawnWorkerProcess;
|
|
55
51
|
runMiningLoop: RunMiningLoop;
|
|
56
|
-
inspectMiningControlPlane: InspectMiningControlPlane;
|
|
57
52
|
loadRuntimeStatus: typeof loadMiningRuntimeStatus;
|
|
58
53
|
saveRuntimeStatus: typeof saveMiningRuntimeStatus;
|
|
59
54
|
acquireLock: typeof acquireFileLock;
|
|
@@ -64,12 +59,7 @@ interface MiningSupervisorDependencies {
|
|
|
64
59
|
nowUnixMs: () => number;
|
|
65
60
|
processKill: ProcessKill;
|
|
66
61
|
processPid: number;
|
|
67
|
-
|
|
68
|
-
resolveWorkerMainPath: () => string;
|
|
69
|
-
}
|
|
70
|
-
export interface MiningSupervisorStartResult {
|
|
71
|
-
started: boolean;
|
|
72
|
-
snapshot: MiningRuntimeStatusV1 | null;
|
|
62
|
+
forceExit: ForceExit;
|
|
73
63
|
}
|
|
74
64
|
export interface MiningSupervisorTakeoverResult {
|
|
75
65
|
controlLockCleared: boolean;
|
|
@@ -86,7 +76,6 @@ export declare function takeOverMiningRuntime(options: {
|
|
|
86
76
|
shutdownGraceMs?: number;
|
|
87
77
|
deps?: Partial<MiningSupervisorDependencies>;
|
|
88
78
|
}): Promise<MiningSupervisorTakeoverResult>;
|
|
89
|
-
export declare function waitForBackgroundHealthy(paths: WalletRuntimePaths, depsOverrides?: Partial<MiningSupervisorDependencies>): Promise<MiningRuntimeStatusV1 | null>;
|
|
90
79
|
export declare function runForegroundMining(options: {
|
|
91
80
|
dataDir: string;
|
|
92
81
|
databasePath: string;
|
|
@@ -108,27 +97,4 @@ export declare function runForegroundMining(options: {
|
|
|
108
97
|
runtime: MiningSupervisorRuntimeContext;
|
|
109
98
|
deps?: Partial<MiningSupervisorDependencies>;
|
|
110
99
|
}): Promise<void>;
|
|
111
|
-
export declare function startBackgroundMining(options: {
|
|
112
|
-
dataDir: string;
|
|
113
|
-
databasePath: string;
|
|
114
|
-
shutdownGraceMs?: number;
|
|
115
|
-
waitForBackgroundHealthy?: (paths: WalletRuntimePaths) => Promise<MiningRuntimeStatusV1 | null>;
|
|
116
|
-
runtime: MiningSupervisorRuntimeContext;
|
|
117
|
-
deps?: Partial<MiningSupervisorDependencies>;
|
|
118
|
-
}): Promise<MiningSupervisorStartResult>;
|
|
119
|
-
export declare function stopBackgroundMining(options: {
|
|
120
|
-
dataDir: string;
|
|
121
|
-
databasePath: string;
|
|
122
|
-
shutdownGraceMs?: number;
|
|
123
|
-
runtime: MiningSupervisorRuntimeContext;
|
|
124
|
-
deps?: Partial<MiningSupervisorDependencies>;
|
|
125
|
-
}): Promise<MiningRuntimeStatusV1 | null>;
|
|
126
|
-
export declare function runBackgroundMiningWorker(options: {
|
|
127
|
-
dataDir: string;
|
|
128
|
-
databasePath: string;
|
|
129
|
-
runId: string;
|
|
130
|
-
fetchImpl?: typeof fetch;
|
|
131
|
-
runtime: MiningSupervisorRuntimeContext;
|
|
132
|
-
deps?: Partial<MiningSupervisorDependencies>;
|
|
133
|
-
}): Promise<void>;
|
|
134
100
|
export {};
|
|
@@ -1,21 +1,22 @@
|
|
|
1
|
-
import { randomBytes } from "node:crypto";
|
|
2
|
-
import { spawn } from "node:child_process";
|
|
3
1
|
import { rm } from "node:fs/promises";
|
|
4
2
|
import { join } from "node:path";
|
|
5
|
-
import { fileURLToPath } from "node:url";
|
|
6
3
|
import { createRpcClient } from "../../bitcoind/node.js";
|
|
7
4
|
import { attachOrStartManagedBitcoindService } from "../../bitcoind/service.js";
|
|
8
5
|
import { FileLockBusyError, acquireFileLock, clearOrphanedFileLock, readLockMetadata, } from "../fs/lock.js";
|
|
9
6
|
import { openWalletReadContext } from "../read/index.js";
|
|
7
|
+
import { destroyAllClientPasswordSessionsResolved } from "../state/client-password/session.js";
|
|
10
8
|
import { readMiningGenerationActivity, requestMiningGenerationPreemption, } from "./coordination.js";
|
|
11
|
-
import {
|
|
12
|
-
import { MINING_SHUTDOWN_GRACE_MS, MINING_WORKER_API_VERSION, } from "./constants.js";
|
|
9
|
+
import { MINING_SHUTDOWN_GRACE_MS, } from "./constants.js";
|
|
13
10
|
import { saveStopSnapshot } from "./lifecycle.js";
|
|
14
11
|
import { loadMiningRuntimeStatus, saveMiningRuntimeStatus, } from "./runtime-artifacts.js";
|
|
12
|
+
import { createMiningStopRequestedError } from "./stop.js";
|
|
15
13
|
import { MiningFollowVisualizer } from "./visualizer.js";
|
|
16
|
-
const BACKGROUND_START_TIMEOUT_MS = 15_000;
|
|
17
14
|
function sleep(ms, signal) {
|
|
18
15
|
return new Promise((resolve) => {
|
|
16
|
+
if (signal?.aborted) {
|
|
17
|
+
resolve();
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
19
20
|
const timer = setTimeout(resolve, ms);
|
|
20
21
|
signal?.addEventListener("abort", () => {
|
|
21
22
|
clearTimeout(timer);
|
|
@@ -23,15 +24,23 @@ function sleep(ms, signal) {
|
|
|
23
24
|
}, { once: true });
|
|
24
25
|
});
|
|
25
26
|
}
|
|
27
|
+
function createOneShotClientPasswordSessionDestroyer() {
|
|
28
|
+
let destroyed = false;
|
|
29
|
+
return () => {
|
|
30
|
+
if (destroyed) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
destroyed = true;
|
|
34
|
+
destroyAllClientPasswordSessionsResolved();
|
|
35
|
+
};
|
|
36
|
+
}
|
|
26
37
|
function resolveSupervisorDependencies(overrides = {}) {
|
|
27
38
|
return {
|
|
28
39
|
requestMiningPreemption: overrides.requestMiningPreemption ?? requestMiningGenerationPreemption,
|
|
29
40
|
saveStopSnapshot: overrides.saveStopSnapshot ?? saveStopSnapshot,
|
|
30
|
-
spawnWorkerProcess: overrides.spawnWorkerProcess ?? spawn,
|
|
31
41
|
runMiningLoop: overrides.runMiningLoop ?? (() => {
|
|
32
42
|
throw new Error("mining_supervisor_run_loop_missing");
|
|
33
43
|
}),
|
|
34
|
-
inspectMiningControlPlane: overrides.inspectMiningControlPlane ?? inspectMiningControlPlane,
|
|
35
44
|
loadRuntimeStatus: overrides.loadRuntimeStatus ?? loadMiningRuntimeStatus,
|
|
36
45
|
saveRuntimeStatus: overrides.saveRuntimeStatus ?? saveMiningRuntimeStatus,
|
|
37
46
|
acquireLock: overrides.acquireLock ?? acquireFileLock,
|
|
@@ -42,11 +51,17 @@ function resolveSupervisorDependencies(overrides = {}) {
|
|
|
42
51
|
nowUnixMs: overrides.nowUnixMs ?? Date.now,
|
|
43
52
|
processKill: overrides.processKill ?? process.kill.bind(process),
|
|
44
53
|
processPid: overrides.processPid ?? process.pid,
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
54
|
+
forceExit: overrides.forceExit ?? ((code) => {
|
|
55
|
+
process.exit(code);
|
|
56
|
+
}),
|
|
48
57
|
};
|
|
49
58
|
}
|
|
59
|
+
class ForegroundMiningShutdownTimeoutError extends Error {
|
|
60
|
+
constructor() {
|
|
61
|
+
super("mining_foreground_shutdown_timeout");
|
|
62
|
+
this.name = "ForegroundMiningShutdownTimeoutError";
|
|
63
|
+
}
|
|
64
|
+
}
|
|
50
65
|
async function isProcessAlive(pid, deps) {
|
|
51
66
|
if (pid === null) {
|
|
52
67
|
return false;
|
|
@@ -294,265 +309,143 @@ async function acquireMiningStartControlLock(options) {
|
|
|
294
309
|
}
|
|
295
310
|
}
|
|
296
311
|
}
|
|
297
|
-
export async function waitForBackgroundHealthy(paths, depsOverrides = {}) {
|
|
298
|
-
const deps = resolveSupervisorDependencies(depsOverrides);
|
|
299
|
-
const deadline = deps.nowUnixMs() + BACKGROUND_START_TIMEOUT_MS;
|
|
300
|
-
while (deps.nowUnixMs() < deadline) {
|
|
301
|
-
const snapshot = await deps.loadRuntimeStatus(paths.miningStatusPath).catch(() => null);
|
|
302
|
-
if (snapshot !== null
|
|
303
|
-
&& snapshot.runMode === "background"
|
|
304
|
-
&& snapshot.backgroundWorkerHealth === "healthy") {
|
|
305
|
-
return snapshot;
|
|
306
|
-
}
|
|
307
|
-
await deps.sleep(250);
|
|
308
|
-
}
|
|
309
|
-
return deps.loadRuntimeStatus(paths.miningStatusPath).catch(() => null);
|
|
310
|
-
}
|
|
311
312
|
export async function runForegroundMining(options) {
|
|
312
313
|
const deps = resolveSupervisorDependencies(options.deps);
|
|
314
|
+
const shutdownGraceMs = options.shutdownGraceMs ?? MINING_SHUTDOWN_GRACE_MS;
|
|
315
|
+
const usesExternalSignal = options.signal !== undefined;
|
|
313
316
|
let visualizer = options.visualizer ?? null;
|
|
314
317
|
const ownsVisualizer = visualizer === null;
|
|
318
|
+
const destroyClientPasswordSessions = createOneShotClientPasswordSessionDestroyer();
|
|
315
319
|
const controlLock = await acquireMiningStartControlLock({
|
|
316
320
|
paths: options.runtime.paths,
|
|
317
321
|
purpose: "mine-foreground",
|
|
318
322
|
takeoverReason: "mine-foreground-replace",
|
|
319
|
-
shutdownGraceMs
|
|
323
|
+
shutdownGraceMs,
|
|
320
324
|
deps,
|
|
321
325
|
});
|
|
322
326
|
const abortController = new AbortController();
|
|
327
|
+
let stopRequested = false;
|
|
328
|
+
let shutdownDeadlineTimer = null;
|
|
329
|
+
let rejectShutdownDeadline = null;
|
|
330
|
+
let forceExitIssued = false;
|
|
331
|
+
const shutdownDeadline = new Promise((_, reject) => {
|
|
332
|
+
rejectShutdownDeadline = reject;
|
|
333
|
+
});
|
|
334
|
+
const requestStop = () => {
|
|
335
|
+
if (stopRequested) {
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
stopRequested = true;
|
|
339
|
+
if (!abortController.signal.aborted) {
|
|
340
|
+
abortController.abort(createMiningStopRequestedError());
|
|
341
|
+
}
|
|
342
|
+
shutdownDeadlineTimer = setTimeout(() => {
|
|
343
|
+
rejectShutdownDeadline?.(new ForegroundMiningShutdownTimeoutError());
|
|
344
|
+
}, shutdownGraceMs);
|
|
345
|
+
shutdownDeadlineTimer.unref?.();
|
|
346
|
+
};
|
|
323
347
|
const abortListener = () => {
|
|
324
|
-
|
|
348
|
+
requestStop();
|
|
325
349
|
};
|
|
326
|
-
const handleSigint = () =>
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
350
|
+
const handleSigint = () => {
|
|
351
|
+
requestStop();
|
|
352
|
+
};
|
|
353
|
+
const handleSigterm = () => {
|
|
354
|
+
requestStop();
|
|
355
|
+
};
|
|
356
|
+
const issueForcedExit = (code) => {
|
|
357
|
+
if (forceExitIssued) {
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
360
|
+
forceExitIssued = true;
|
|
361
|
+
visualizer?.close();
|
|
362
|
+
destroyClientPasswordSessions();
|
|
363
|
+
return deps.forceExit(code);
|
|
364
|
+
};
|
|
365
|
+
const gracefulRun = async () => {
|
|
366
|
+
try {
|
|
367
|
+
await takeOverMiningRuntime({
|
|
368
|
+
paths: options.runtime.paths,
|
|
369
|
+
reason: "mine-foreground-replace",
|
|
370
|
+
shutdownGraceMs,
|
|
371
|
+
deps,
|
|
372
|
+
});
|
|
373
|
+
if (visualizer === null) {
|
|
374
|
+
visualizer = new MiningFollowVisualizer({
|
|
375
|
+
clientVersion: options.clientVersion,
|
|
376
|
+
updateAvailable: options.updateAvailable,
|
|
377
|
+
progressOutput: options.progressOutput ?? "auto",
|
|
378
|
+
stream: options.stderr,
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
await deps.runMiningLoop({
|
|
382
|
+
dataDir: options.dataDir,
|
|
383
|
+
databasePath: options.databasePath,
|
|
384
|
+
provider: options.runtime.provider,
|
|
385
|
+
paths: options.runtime.paths,
|
|
386
|
+
runMode: "foreground",
|
|
387
|
+
backgroundWorkerPid: null,
|
|
388
|
+
backgroundWorkerRunId: null,
|
|
389
|
+
signal: abortController.signal,
|
|
390
|
+
fetchImpl: options.fetchImpl,
|
|
391
|
+
openReadContext: options.runtime.openReadContext,
|
|
392
|
+
attachService: options.runtime.attachService,
|
|
393
|
+
rpcFactory: options.runtime.rpcFactory,
|
|
394
|
+
stdout: options.stdout,
|
|
395
|
+
visualizer,
|
|
396
|
+
});
|
|
397
|
+
await deps.saveStopSnapshot({
|
|
398
|
+
dataDir: options.dataDir,
|
|
399
|
+
databasePath: options.databasePath,
|
|
400
|
+
provider: options.runtime.provider,
|
|
401
|
+
paths: options.runtime.paths,
|
|
402
|
+
runMode: "foreground",
|
|
403
|
+
backgroundWorkerPid: null,
|
|
404
|
+
backgroundWorkerRunId: null,
|
|
405
|
+
note: "Foreground mining stopped cleanly.",
|
|
341
406
|
});
|
|
342
407
|
}
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
provider: options.runtime.provider,
|
|
350
|
-
paths: options.runtime.paths,
|
|
351
|
-
runMode: "foreground",
|
|
352
|
-
backgroundWorkerPid: null,
|
|
353
|
-
backgroundWorkerRunId: null,
|
|
354
|
-
signal: abortController.signal,
|
|
355
|
-
fetchImpl: options.fetchImpl,
|
|
356
|
-
openReadContext: options.runtime.openReadContext,
|
|
357
|
-
attachService: options.runtime.attachService,
|
|
358
|
-
rpcFactory: options.runtime.rpcFactory,
|
|
359
|
-
stdout: options.stdout,
|
|
360
|
-
visualizer,
|
|
361
|
-
});
|
|
362
|
-
await deps.saveStopSnapshot({
|
|
363
|
-
dataDir: options.dataDir,
|
|
364
|
-
databasePath: options.databasePath,
|
|
365
|
-
provider: options.runtime.provider,
|
|
366
|
-
paths: options.runtime.paths,
|
|
367
|
-
runMode: "foreground",
|
|
368
|
-
backgroundWorkerPid: null,
|
|
369
|
-
backgroundWorkerRunId: null,
|
|
370
|
-
note: "Foreground mining stopped cleanly.",
|
|
371
|
-
});
|
|
372
|
-
}
|
|
373
|
-
finally {
|
|
374
|
-
options.signal?.removeEventListener("abort", abortListener);
|
|
375
|
-
process.off("SIGINT", handleSigint);
|
|
376
|
-
process.off("SIGTERM", handleSigterm);
|
|
377
|
-
if (ownsVisualizer) {
|
|
378
|
-
visualizer?.close();
|
|
408
|
+
finally {
|
|
409
|
+
if (ownsVisualizer) {
|
|
410
|
+
visualizer?.close();
|
|
411
|
+
}
|
|
412
|
+
await controlLock.release();
|
|
413
|
+
destroyClientPasswordSessions();
|
|
379
414
|
}
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
}
|
|
383
|
-
export async function startBackgroundMining(options) {
|
|
384
|
-
const deps = resolveSupervisorDependencies(options.deps);
|
|
385
|
-
const waitForHealthy = options.waitForBackgroundHealthy
|
|
386
|
-
?? (async (paths) => await waitForBackgroundHealthy(paths, deps));
|
|
387
|
-
let controlLock;
|
|
415
|
+
};
|
|
416
|
+
const gracefulRunPromise = gracefulRun();
|
|
388
417
|
try {
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
418
|
+
if (options.signal?.aborted) {
|
|
419
|
+
requestStop();
|
|
420
|
+
}
|
|
421
|
+
else {
|
|
422
|
+
options.signal?.addEventListener("abort", abortListener, { once: true });
|
|
423
|
+
}
|
|
424
|
+
if (!usesExternalSignal) {
|
|
425
|
+
process.on("SIGINT", handleSigint);
|
|
426
|
+
process.on("SIGTERM", handleSigterm);
|
|
427
|
+
}
|
|
428
|
+
await Promise.race([
|
|
429
|
+
gracefulRunPromise,
|
|
430
|
+
shutdownDeadline,
|
|
431
|
+
]);
|
|
396
432
|
}
|
|
397
433
|
catch (error) {
|
|
398
|
-
if (error instanceof
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
};
|
|
434
|
+
if (error instanceof ForegroundMiningShutdownTimeoutError) {
|
|
435
|
+
gracefulRunPromise.catch(() => undefined);
|
|
436
|
+
issueForcedExit(130);
|
|
437
|
+
return;
|
|
403
438
|
}
|
|
404
439
|
throw error;
|
|
405
440
|
}
|
|
406
|
-
try {
|
|
407
|
-
await takeOverMiningRuntime({
|
|
408
|
-
paths: options.runtime.paths,
|
|
409
|
-
reason: "mine-start-replace",
|
|
410
|
-
shutdownGraceMs: options.shutdownGraceMs,
|
|
411
|
-
deps,
|
|
412
|
-
});
|
|
413
|
-
const runId = randomBytes(16).toString("hex");
|
|
414
|
-
const child = deps.spawnWorkerProcess(deps.processExecPath, [
|
|
415
|
-
deps.resolveWorkerMainPath(),
|
|
416
|
-
`--data-dir=${options.dataDir}`,
|
|
417
|
-
`--database-path=${options.databasePath}`,
|
|
418
|
-
`--run-id=${runId}`,
|
|
419
|
-
], {
|
|
420
|
-
detached: true,
|
|
421
|
-
stdio: "ignore",
|
|
422
|
-
});
|
|
423
|
-
child.unref();
|
|
424
|
-
const snapshot = await waitForHealthy(options.runtime.paths);
|
|
425
|
-
return {
|
|
426
|
-
started: true,
|
|
427
|
-
snapshot,
|
|
428
|
-
};
|
|
429
|
-
}
|
|
430
441
|
finally {
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
}
|
|
434
|
-
export async function stopBackgroundMining(options) {
|
|
435
|
-
const deps = resolveSupervisorDependencies(options.deps);
|
|
436
|
-
const shutdownGraceMs = options.shutdownGraceMs ?? MINING_SHUTDOWN_GRACE_MS;
|
|
437
|
-
const controlLock = await deps.acquireLock(options.runtime.paths.miningControlLockPath, {
|
|
438
|
-
purpose: "mine-stop",
|
|
439
|
-
});
|
|
440
|
-
try {
|
|
441
|
-
const snapshot = await deps.loadRuntimeStatus(options.runtime.paths.miningStatusPath).catch(() => null);
|
|
442
|
-
if (snapshot === null || snapshot.runMode !== "background" || snapshot.backgroundWorkerPid === null) {
|
|
443
|
-
return snapshot;
|
|
444
|
-
}
|
|
445
|
-
const preemption = await deps.requestMiningPreemption({
|
|
446
|
-
paths: options.runtime.paths,
|
|
447
|
-
reason: "mine-stop",
|
|
448
|
-
timeoutMs: Math.min(shutdownGraceMs, 15_000),
|
|
449
|
-
}).catch(() => null);
|
|
450
|
-
try {
|
|
451
|
-
try {
|
|
452
|
-
deps.processKill(snapshot.backgroundWorkerPid, "SIGTERM");
|
|
453
|
-
}
|
|
454
|
-
catch (error) {
|
|
455
|
-
if (!(error instanceof Error && "code" in error && error.code === "ESRCH")) {
|
|
456
|
-
throw error;
|
|
457
|
-
}
|
|
458
|
-
}
|
|
459
|
-
const deadline = deps.nowUnixMs() + shutdownGraceMs;
|
|
460
|
-
while (deps.nowUnixMs() < deadline) {
|
|
461
|
-
if (!await isProcessAlive(snapshot.backgroundWorkerPid, deps)) {
|
|
462
|
-
break;
|
|
463
|
-
}
|
|
464
|
-
await deps.sleep(250);
|
|
465
|
-
}
|
|
466
|
-
if (await isProcessAlive(snapshot.backgroundWorkerPid, deps)) {
|
|
467
|
-
try {
|
|
468
|
-
deps.processKill(snapshot.backgroundWorkerPid, "SIGKILL");
|
|
469
|
-
}
|
|
470
|
-
catch {
|
|
471
|
-
// ignore
|
|
472
|
-
}
|
|
473
|
-
}
|
|
442
|
+
if (shutdownDeadlineTimer !== null) {
|
|
443
|
+
clearTimeout(shutdownDeadlineTimer);
|
|
474
444
|
}
|
|
475
|
-
|
|
476
|
-
|
|
445
|
+
options.signal?.removeEventListener("abort", abortListener);
|
|
446
|
+
if (!usesExternalSignal) {
|
|
447
|
+
process.off("SIGINT", handleSigint);
|
|
448
|
+
process.off("SIGTERM", handleSigterm);
|
|
477
449
|
}
|
|
478
|
-
await deps.saveStopSnapshot({
|
|
479
|
-
dataDir: options.dataDir,
|
|
480
|
-
databasePath: options.databasePath,
|
|
481
|
-
provider: options.runtime.provider,
|
|
482
|
-
paths: options.runtime.paths,
|
|
483
|
-
runMode: "background",
|
|
484
|
-
backgroundWorkerPid: snapshot.backgroundWorkerPid,
|
|
485
|
-
backgroundWorkerRunId: snapshot.backgroundWorkerRunId,
|
|
486
|
-
note: snapshot.livePublishInMempool
|
|
487
|
-
? "Background mining stopped. The last mining transaction may still confirm from mempool."
|
|
488
|
-
: "Background mining stopped.",
|
|
489
|
-
});
|
|
490
|
-
return deps.loadRuntimeStatus(options.runtime.paths.miningStatusPath).catch(() => null);
|
|
491
|
-
}
|
|
492
|
-
finally {
|
|
493
|
-
await controlLock.release();
|
|
494
|
-
}
|
|
495
|
-
}
|
|
496
|
-
export async function runBackgroundMiningWorker(options) {
|
|
497
|
-
const deps = resolveSupervisorDependencies(options.deps);
|
|
498
|
-
const abortController = new AbortController();
|
|
499
|
-
process.on("SIGINT", () => abortController.abort());
|
|
500
|
-
process.on("SIGTERM", () => abortController.abort());
|
|
501
|
-
const initialContext = await options.runtime.openReadContext({
|
|
502
|
-
dataDir: options.dataDir,
|
|
503
|
-
databasePath: options.databasePath,
|
|
504
|
-
secretProvider: options.runtime.provider,
|
|
505
|
-
paths: options.runtime.paths,
|
|
506
|
-
});
|
|
507
|
-
try {
|
|
508
|
-
const initialView = await deps.inspectMiningControlPlane({
|
|
509
|
-
provider: options.runtime.provider,
|
|
510
|
-
localState: initialContext.localState,
|
|
511
|
-
bitcoind: initialContext.bitcoind,
|
|
512
|
-
nodeStatus: initialContext.nodeStatus,
|
|
513
|
-
nodeHealth: initialContext.nodeHealth,
|
|
514
|
-
indexer: initialContext.indexer,
|
|
515
|
-
paths: options.runtime.paths,
|
|
516
|
-
});
|
|
517
|
-
await deps.saveRuntimeStatus(options.runtime.paths.miningStatusPath, {
|
|
518
|
-
...initialView.runtime,
|
|
519
|
-
walletRootId: initialContext.localState.walletRootId,
|
|
520
|
-
workerApiVersion: MINING_WORKER_API_VERSION,
|
|
521
|
-
workerBinaryVersion: process.version,
|
|
522
|
-
workerBuildId: options.runId,
|
|
523
|
-
runMode: "background",
|
|
524
|
-
backgroundWorkerPid: deps.processPid,
|
|
525
|
-
backgroundWorkerRunId: options.runId,
|
|
526
|
-
backgroundWorkerHeartbeatAtUnixMs: deps.nowUnixMs(),
|
|
527
|
-
currentPhase: "idle",
|
|
528
|
-
updatedAtUnixMs: deps.nowUnixMs(),
|
|
529
|
-
});
|
|
530
|
-
}
|
|
531
|
-
finally {
|
|
532
|
-
await initialContext.close();
|
|
533
450
|
}
|
|
534
|
-
await deps.runMiningLoop({
|
|
535
|
-
dataDir: options.dataDir,
|
|
536
|
-
databasePath: options.databasePath,
|
|
537
|
-
provider: options.runtime.provider,
|
|
538
|
-
paths: options.runtime.paths,
|
|
539
|
-
runMode: "background",
|
|
540
|
-
backgroundWorkerPid: deps.processPid,
|
|
541
|
-
backgroundWorkerRunId: options.runId,
|
|
542
|
-
signal: abortController.signal,
|
|
543
|
-
fetchImpl: options.fetchImpl,
|
|
544
|
-
openReadContext: options.runtime.openReadContext,
|
|
545
|
-
attachService: options.runtime.attachService,
|
|
546
|
-
rpcFactory: options.runtime.rpcFactory,
|
|
547
|
-
});
|
|
548
|
-
await deps.saveStopSnapshot({
|
|
549
|
-
dataDir: options.dataDir,
|
|
550
|
-
databasePath: options.databasePath,
|
|
551
|
-
provider: options.runtime.provider,
|
|
552
|
-
paths: options.runtime.paths,
|
|
553
|
-
runMode: "background",
|
|
554
|
-
backgroundWorkerPid: deps.processPid,
|
|
555
|
-
backgroundWorkerRunId: options.runId,
|
|
556
|
-
note: "Background mining worker stopped cleanly.",
|
|
557
|
-
});
|
|
558
451
|
}
|
|
@@ -4,6 +4,12 @@ import { FOLLOW_VISIBLE_PRIOR_BLOCKS } from "../../bitcoind/client/follow-block-
|
|
|
4
4
|
import { buildMiningTipKey, resetMiningUiForTip } from "./engine-state.js";
|
|
5
5
|
import { deriveMiningWordIndices, numberToSats, resolveBip39WordsFromIndices, } from "./engine-utils.js";
|
|
6
6
|
import { createEmptyMiningFollowVisualizerState } from "./visualizer.js";
|
|
7
|
+
function cloneSettledBoardEntries(entries) {
|
|
8
|
+
return entries.map((entry) => ({
|
|
9
|
+
...entry,
|
|
10
|
+
requiredWords: [...entry.requiredWords],
|
|
11
|
+
}));
|
|
12
|
+
}
|
|
7
13
|
function resolveSettledWinnerRequiredWords(options) {
|
|
8
14
|
const storedWords = resolveBip39WordsFromIndices(options.bip39WordIndices);
|
|
9
15
|
if (storedWords.length > 0) {
|
|
@@ -20,6 +26,46 @@ function resolveSettledWinnerRequiredWords(options) {
|
|
|
20
26
|
function fallbackSettledWinnerDomainName(domainId) {
|
|
21
27
|
return `domain-${domainId}`;
|
|
22
28
|
}
|
|
29
|
+
function resolveSettledBoardEntriesForHeight(options) {
|
|
30
|
+
if (options.snapshotState === null || options.snapshotState === undefined) {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
const winners = getBlockWinners(options.snapshotState, options.blockHeight);
|
|
34
|
+
if (winners === null) {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
const snapshotState = options.snapshotState;
|
|
38
|
+
return winners
|
|
39
|
+
.slice()
|
|
40
|
+
.sort((left, right) => left.rank - right.rank || left.txIndex - right.txIndex)
|
|
41
|
+
.slice(0, 5)
|
|
42
|
+
.map((winner) => ({
|
|
43
|
+
rank: winner.rank,
|
|
44
|
+
domainName: lookupDomainById(snapshotState, winner.domainId)?.name ?? fallbackSettledWinnerDomainName(winner.domainId),
|
|
45
|
+
sentence: winner.sentenceText ?? "[unavailable]",
|
|
46
|
+
requiredWords: resolveSettledWinnerRequiredWords({
|
|
47
|
+
domainId: winner.domainId,
|
|
48
|
+
bip39WordIndices: winner.bip39WordIndices,
|
|
49
|
+
snapshotTipPreviousHashHex: options.blockPreviousHashHex,
|
|
50
|
+
}),
|
|
51
|
+
}));
|
|
52
|
+
}
|
|
53
|
+
function resolveLatestPriorNonEmptySettledBoard(options) {
|
|
54
|
+
for (let blockHeight = options.snapshotTipHeight - 1; blockHeight >= 0; blockHeight -= 1) {
|
|
55
|
+
const settledBoardEntries = resolveSettledBoardEntriesForHeight({
|
|
56
|
+
snapshotState: options.snapshotState,
|
|
57
|
+
blockHeight,
|
|
58
|
+
blockPreviousHashHex: null,
|
|
59
|
+
});
|
|
60
|
+
if (settledBoardEntries !== null && settledBoardEntries.length > 0) {
|
|
61
|
+
return {
|
|
62
|
+
settledBlockHeight: blockHeight,
|
|
63
|
+
settledBoardEntries,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
23
69
|
function resolveCurrentMinedBlockBoard(options) {
|
|
24
70
|
const settledBlockHeight = options.snapshotTipHeight ?? null;
|
|
25
71
|
if (settledBlockHeight === null) {
|
|
@@ -34,23 +80,37 @@ function resolveCurrentMinedBlockBoard(options) {
|
|
|
34
80
|
settledBoardEntries: [],
|
|
35
81
|
};
|
|
36
82
|
}
|
|
37
|
-
const settledBoardEntries = (
|
|
38
|
-
.
|
|
39
|
-
|
|
40
|
-
.
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
83
|
+
const settledBoardEntries = resolveSettledBoardEntriesForHeight({
|
|
84
|
+
snapshotState: options.snapshotState,
|
|
85
|
+
blockHeight: settledBlockHeight,
|
|
86
|
+
blockPreviousHashHex: options.snapshotTipPreviousHashHex,
|
|
87
|
+
});
|
|
88
|
+
if (settledBoardEntries !== null) {
|
|
89
|
+
return {
|
|
90
|
+
settledBlockHeight,
|
|
91
|
+
settledBoardEntries,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
const currentDisplayedBlockHeight = options.currentDisplayedBoard?.settledBlockHeight ?? null;
|
|
95
|
+
const currentDisplayedEntries = options.currentDisplayedBoard?.settledBoardEntries ?? [];
|
|
96
|
+
if (currentDisplayedBlockHeight !== null
|
|
97
|
+
&& currentDisplayedBlockHeight <= settledBlockHeight
|
|
98
|
+
&& currentDisplayedEntries.length > 0) {
|
|
99
|
+
return {
|
|
100
|
+
settledBlockHeight: currentDisplayedBlockHeight,
|
|
101
|
+
settledBoardEntries: cloneSettledBoardEntries(currentDisplayedEntries),
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
const latestPriorNonEmptyBoard = resolveLatestPriorNonEmptySettledBoard({
|
|
105
|
+
snapshotState: options.snapshotState,
|
|
106
|
+
snapshotTipHeight: settledBlockHeight,
|
|
107
|
+
});
|
|
108
|
+
if (latestPriorNonEmptyBoard !== null) {
|
|
109
|
+
return latestPriorNonEmptyBoard;
|
|
110
|
+
}
|
|
51
111
|
return {
|
|
52
112
|
settledBlockHeight,
|
|
53
|
-
settledBoardEntries,
|
|
113
|
+
settledBoardEntries: [],
|
|
54
114
|
};
|
|
55
115
|
}
|
|
56
116
|
export function resolveSettledBoard(options) {
|
|
@@ -66,6 +126,10 @@ function syncMiningUiSettledBoard(loopState, snapshotState, snapshotTipHeight, s
|
|
|
66
126
|
snapshotState,
|
|
67
127
|
snapshotTipHeight,
|
|
68
128
|
snapshotTipPreviousHashHex,
|
|
129
|
+
currentDisplayedBoard: {
|
|
130
|
+
settledBlockHeight: loopState.ui.settledBlockHeight,
|
|
131
|
+
settledBoardEntries: loopState.ui.settledBoardEntries,
|
|
132
|
+
},
|
|
69
133
|
});
|
|
70
134
|
loopState.ui.settledBlockHeight = settledBoard.settledBlockHeight;
|
|
71
135
|
loopState.ui.settledBoardEntries = settledBoard.settledBoardEntries;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { readSnapshotWithRetry } from "../../bitcoind/indexer-daemon.js";
|
|
2
2
|
import { type WalletSecretProvider } from "../state/provider.js";
|
|
3
|
-
import type {
|
|
3
|
+
import type { WalletLocalStateStatus, WalletReadContext } from "./types.js";
|
|
4
4
|
import type { WalletRuntimePaths } from "../runtime.js";
|
|
5
5
|
declare function inspectWalletLocalState(options?: {
|
|
6
6
|
dataDir?: string;
|
|
@@ -9,10 +9,6 @@ declare function inspectWalletLocalState(options?: {
|
|
|
9
9
|
paths?: WalletRuntimePaths;
|
|
10
10
|
walletControlLockHeld?: boolean;
|
|
11
11
|
}): Promise<WalletLocalStateStatus>;
|
|
12
|
-
export declare function deriveNodeHealthForTesting(status: WalletNodeStatus | null, bitcoindHealth: WalletBitcoindStatus["health"]): {
|
|
13
|
-
health: WalletServiceHealth;
|
|
14
|
-
message: string | null;
|
|
15
|
-
};
|
|
16
12
|
export declare function openWalletReadContext(options: {
|
|
17
13
|
dataDir: string;
|
|
18
14
|
databasePath: string;
|