@cogcoin/client 0.5.0
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/LICENSE +21 -0
- package/README.md +136 -0
- package/dist/app-paths.d.ts +38 -0
- package/dist/app-paths.js +121 -0
- package/dist/art/banner.txt +13 -0
- package/dist/art/scroll.txt +13 -0
- package/dist/art/train-car.txt +6 -0
- package/dist/art/train-smoke.txt +6 -0
- package/dist/art/train.txt +6 -0
- package/dist/bitcoind/bootstrap/chainstate.d.ts +4 -0
- package/dist/bitcoind/bootstrap/chainstate.js +13 -0
- package/dist/bitcoind/bootstrap/constants.d.ts +7 -0
- package/dist/bitcoind/bootstrap/constants.js +12 -0
- package/dist/bitcoind/bootstrap/controller.d.ts +29 -0
- package/dist/bitcoind/bootstrap/controller.js +101 -0
- package/dist/bitcoind/bootstrap/download.d.ts +2 -0
- package/dist/bitcoind/bootstrap/download.js +196 -0
- package/dist/bitcoind/bootstrap/headers.d.ts +13 -0
- package/dist/bitcoind/bootstrap/headers.js +61 -0
- package/dist/bitcoind/bootstrap/paths.d.ts +4 -0
- package/dist/bitcoind/bootstrap/paths.js +15 -0
- package/dist/bitcoind/bootstrap/snapshot-file.d.ts +7 -0
- package/dist/bitcoind/bootstrap/snapshot-file.js +42 -0
- package/dist/bitcoind/bootstrap/state.d.ts +40 -0
- package/dist/bitcoind/bootstrap/state.js +70 -0
- package/dist/bitcoind/bootstrap/types.d.ts +28 -0
- package/dist/bitcoind/bootstrap/types.js +1 -0
- package/dist/bitcoind/bootstrap.d.ts +8 -0
- package/dist/bitcoind/bootstrap.js +7 -0
- package/dist/bitcoind/client/factory.d.ts +3 -0
- package/dist/bitcoind/client/factory.js +57 -0
- package/dist/bitcoind/client/follow-block-times.d.ts +8 -0
- package/dist/bitcoind/client/follow-block-times.js +25 -0
- package/dist/bitcoind/client/follow-loop.d.ts +10 -0
- package/dist/bitcoind/client/follow-loop.js +57 -0
- package/dist/bitcoind/client/internal-types.d.ts +63 -0
- package/dist/bitcoind/client/internal-types.js +18 -0
- package/dist/bitcoind/client/managed-client.d.ts +20 -0
- package/dist/bitcoind/client/managed-client.js +197 -0
- package/dist/bitcoind/client/rate-tracker.d.ts +2 -0
- package/dist/bitcoind/client/rate-tracker.js +24 -0
- package/dist/bitcoind/client/sync-engine.d.ts +3 -0
- package/dist/bitcoind/client/sync-engine.js +143 -0
- package/dist/bitcoind/client.d.ts +1 -0
- package/dist/bitcoind/client.js +1 -0
- package/dist/bitcoind/errors.d.ts +1 -0
- package/dist/bitcoind/errors.js +49 -0
- package/dist/bitcoind/index.d.ts +2 -0
- package/dist/bitcoind/index.js +1 -0
- package/dist/bitcoind/indexer-daemon-main.d.ts +1 -0
- package/dist/bitcoind/indexer-daemon-main.js +472 -0
- package/dist/bitcoind/indexer-daemon.d.ts +107 -0
- package/dist/bitcoind/indexer-daemon.js +391 -0
- package/dist/bitcoind/node.d.ts +8 -0
- package/dist/bitcoind/node.js +219 -0
- package/dist/bitcoind/normalize.d.ts +3 -0
- package/dist/bitcoind/normalize.js +47 -0
- package/dist/bitcoind/progress/assets.d.ts +10 -0
- package/dist/bitcoind/progress/assets.js +90 -0
- package/dist/bitcoind/progress/constants.d.ts +48 -0
- package/dist/bitcoind/progress/constants.js +53 -0
- package/dist/bitcoind/progress/controller.d.ts +28 -0
- package/dist/bitcoind/progress/controller.js +188 -0
- package/dist/bitcoind/progress/follow-scene.d.ts +40 -0
- package/dist/bitcoind/progress/follow-scene.js +367 -0
- package/dist/bitcoind/progress/formatting.d.ts +23 -0
- package/dist/bitcoind/progress/formatting.js +227 -0
- package/dist/bitcoind/progress/quote-scene.d.ts +4 -0
- package/dist/bitcoind/progress/quote-scene.js +137 -0
- package/dist/bitcoind/progress/train-scene.d.ts +9 -0
- package/dist/bitcoind/progress/train-scene.js +92 -0
- package/dist/bitcoind/progress/tty-renderer.d.ts +18 -0
- package/dist/bitcoind/progress/tty-renderer.js +150 -0
- package/dist/bitcoind/progress.d.ts +7 -0
- package/dist/bitcoind/progress.js +7 -0
- package/dist/bitcoind/quotes.d.ts +24 -0
- package/dist/bitcoind/quotes.js +195 -0
- package/dist/bitcoind/rpc.d.ts +71 -0
- package/dist/bitcoind/rpc.js +322 -0
- package/dist/bitcoind/service-paths.d.ts +19 -0
- package/dist/bitcoind/service-paths.js +49 -0
- package/dist/bitcoind/service.d.ts +40 -0
- package/dist/bitcoind/service.js +735 -0
- package/dist/bitcoind/testing.d.ts +9 -0
- package/dist/bitcoind/testing.js +9 -0
- package/dist/bitcoind/types.d.ts +396 -0
- package/dist/bitcoind/types.js +3 -0
- package/dist/bytes.d.ts +9 -0
- package/dist/bytes.js +36 -0
- package/dist/cli/commands/follow.d.ts +2 -0
- package/dist/cli/commands/follow.js +43 -0
- package/dist/cli/commands/mining-admin.d.ts +2 -0
- package/dist/cli/commands/mining-admin.js +92 -0
- package/dist/cli/commands/mining-read.d.ts +2 -0
- package/dist/cli/commands/mining-read.js +173 -0
- package/dist/cli/commands/mining-runtime.d.ts +2 -0
- package/dist/cli/commands/mining-runtime.js +108 -0
- package/dist/cli/commands/status.d.ts +2 -0
- package/dist/cli/commands/status.js +31 -0
- package/dist/cli/commands/sync.d.ts +2 -0
- package/dist/cli/commands/sync.js +52 -0
- package/dist/cli/commands/wallet-admin.d.ts +2 -0
- package/dist/cli/commands/wallet-admin.js +175 -0
- package/dist/cli/commands/wallet-mutation.d.ts +2 -0
- package/dist/cli/commands/wallet-mutation.js +681 -0
- package/dist/cli/commands/wallet-read.d.ts +2 -0
- package/dist/cli/commands/wallet-read.js +265 -0
- package/dist/cli/context.d.ts +3 -0
- package/dist/cli/context.js +75 -0
- package/dist/cli/io.d.ts +3 -0
- package/dist/cli/io.js +12 -0
- package/dist/cli/mining-format.d.ts +5 -0
- package/dist/cli/mining-format.js +156 -0
- package/dist/cli/mining-json.d.ts +49 -0
- package/dist/cli/mining-json.js +89 -0
- package/dist/cli/mutation-command-groups.d.ts +15 -0
- package/dist/cli/mutation-command-groups.js +71 -0
- package/dist/cli/mutation-json.d.ts +430 -0
- package/dist/cli/mutation-json.js +311 -0
- package/dist/cli/mutation-resolved-json.d.ts +124 -0
- package/dist/cli/mutation-resolved-json.js +129 -0
- package/dist/cli/mutation-success.d.ts +20 -0
- package/dist/cli/mutation-success.js +47 -0
- package/dist/cli/mutation-text-format.d.ts +22 -0
- package/dist/cli/mutation-text-format.js +171 -0
- package/dist/cli/mutation-text-write.d.ts +13 -0
- package/dist/cli/mutation-text-write.js +16 -0
- package/dist/cli/output.d.ts +185 -0
- package/dist/cli/output.js +1085 -0
- package/dist/cli/parse.d.ts +3 -0
- package/dist/cli/parse.js +971 -0
- package/dist/cli/preview-json.d.ts +416 -0
- package/dist/cli/preview-json.js +293 -0
- package/dist/cli/prompt.d.ts +3 -0
- package/dist/cli/prompt.js +33 -0
- package/dist/cli/read-json.d.ts +187 -0
- package/dist/cli/read-json.js +675 -0
- package/dist/cli/runner.d.ts +2 -0
- package/dist/cli/runner.js +129 -0
- package/dist/cli/signals.d.ts +3 -0
- package/dist/cli/signals.js +63 -0
- package/dist/cli/status-format.d.ts +2 -0
- package/dist/cli/status-format.js +48 -0
- package/dist/cli/types.d.ts +148 -0
- package/dist/cli/types.js +2 -0
- package/dist/cli/wallet-format.d.ts +29 -0
- package/dist/cli/wallet-format.js +637 -0
- package/dist/cli/workflow-hints.d.ts +13 -0
- package/dist/cli/workflow-hints.js +94 -0
- package/dist/cli-runner.d.ts +3 -0
- package/dist/cli-runner.js +3 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +6 -0
- package/dist/client/default-client.d.ts +11 -0
- package/dist/client/default-client.js +118 -0
- package/dist/client/factory.d.ts +2 -0
- package/dist/client/factory.js +15 -0
- package/dist/client/initialization.d.ts +6 -0
- package/dist/client/initialization.js +30 -0
- package/dist/client/persistence.d.ts +5 -0
- package/dist/client/persistence.js +28 -0
- package/dist/client/store-adapter.d.ts +3 -0
- package/dist/client/store-adapter.js +20 -0
- package/dist/client.d.ts +2 -0
- package/dist/client.js +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/passive-status.d.ts +36 -0
- package/dist/passive-status.js +100 -0
- package/dist/sqlite/better-sqlite3.d.ts +26 -0
- package/dist/sqlite/better-sqlite3.js +4 -0
- package/dist/sqlite/checkpoints.d.ts +11 -0
- package/dist/sqlite/checkpoints.js +27 -0
- package/dist/sqlite/driver.d.ts +17 -0
- package/dist/sqlite/driver.js +98 -0
- package/dist/sqlite/index.d.ts +4 -0
- package/dist/sqlite/index.js +9 -0
- package/dist/sqlite/migrate.d.ts +2 -0
- package/dist/sqlite/migrate.js +37 -0
- package/dist/sqlite/store.d.ts +3 -0
- package/dist/sqlite/store.js +122 -0
- package/dist/sqlite/tip-meta.d.ts +26 -0
- package/dist/sqlite/tip-meta.js +97 -0
- package/dist/sqlite/types.d.ts +10 -0
- package/dist/sqlite/types.js +1 -0
- package/dist/types.d.ts +55 -0
- package/dist/types.js +1 -0
- package/dist/wallet/archive.d.ts +4 -0
- package/dist/wallet/archive.js +39 -0
- package/dist/wallet/cogop/constants.d.ts +32 -0
- package/dist/wallet/cogop/constants.js +32 -0
- package/dist/wallet/cogop/index.d.ts +32 -0
- package/dist/wallet/cogop/index.js +213 -0
- package/dist/wallet/cogop/numeric.d.ts +3 -0
- package/dist/wallet/cogop/numeric.js +24 -0
- package/dist/wallet/cogop/scriptpubkey.d.ts +2 -0
- package/dist/wallet/cogop/scriptpubkey.js +13 -0
- package/dist/wallet/cogop/validate-name.d.ts +2 -0
- package/dist/wallet/cogop/validate-name.js +18 -0
- package/dist/wallet/fs/atomic.d.ts +6 -0
- package/dist/wallet/fs/atomic.js +46 -0
- package/dist/wallet/fs/lock.d.ts +19 -0
- package/dist/wallet/fs/lock.js +61 -0
- package/dist/wallet/fs/status-file.d.ts +1 -0
- package/dist/wallet/fs/status-file.js +4 -0
- package/dist/wallet/lifecycle.d.ts +193 -0
- package/dist/wallet/lifecycle.js +1475 -0
- package/dist/wallet/material.d.ts +45 -0
- package/dist/wallet/material.js +118 -0
- package/dist/wallet/mining/config.d.ts +18 -0
- package/dist/wallet/mining/config.js +44 -0
- package/dist/wallet/mining/constants.d.ts +24 -0
- package/dist/wallet/mining/constants.js +24 -0
- package/dist/wallet/mining/control.d.ts +53 -0
- package/dist/wallet/mining/control.js +758 -0
- package/dist/wallet/mining/coordination.d.ts +40 -0
- package/dist/wallet/mining/coordination.js +121 -0
- package/dist/wallet/mining/hook-protocol.d.ts +47 -0
- package/dist/wallet/mining/hook-protocol.js +161 -0
- package/dist/wallet/mining/hook-runner.d.ts +1 -0
- package/dist/wallet/mining/hook-runner.js +52 -0
- package/dist/wallet/mining/hooks.d.ts +38 -0
- package/dist/wallet/mining/hooks.js +520 -0
- package/dist/wallet/mining/index.d.ts +8 -0
- package/dist/wallet/mining/index.js +6 -0
- package/dist/wallet/mining/runner.d.ts +155 -0
- package/dist/wallet/mining/runner.js +2574 -0
- package/dist/wallet/mining/runtime-artifacts.d.ts +17 -0
- package/dist/wallet/mining/runtime-artifacts.js +166 -0
- package/dist/wallet/mining/sentences.d.ts +23 -0
- package/dist/wallet/mining/sentences.js +281 -0
- package/dist/wallet/mining/state.d.ts +9 -0
- package/dist/wallet/mining/state.js +75 -0
- package/dist/wallet/mining/types.d.ts +141 -0
- package/dist/wallet/mining/types.js +1 -0
- package/dist/wallet/mining/visualizer.d.ts +19 -0
- package/dist/wallet/mining/visualizer.js +134 -0
- package/dist/wallet/mining/worker-main.d.ts +1 -0
- package/dist/wallet/mining/worker-main.js +17 -0
- package/dist/wallet/read/context.d.ts +20 -0
- package/dist/wallet/read/context.js +532 -0
- package/dist/wallet/read/filter.d.ts +9 -0
- package/dist/wallet/read/filter.js +42 -0
- package/dist/wallet/read/index.d.ts +4 -0
- package/dist/wallet/read/index.js +3 -0
- package/dist/wallet/read/project.d.ts +11 -0
- package/dist/wallet/read/project.js +300 -0
- package/dist/wallet/read/types.d.ts +144 -0
- package/dist/wallet/read/types.js +1 -0
- package/dist/wallet/runtime.d.ts +26 -0
- package/dist/wallet/runtime.js +28 -0
- package/dist/wallet/state/crypto.d.ts +31 -0
- package/dist/wallet/state/crypto.js +127 -0
- package/dist/wallet/state/provider.d.ts +37 -0
- package/dist/wallet/state/provider.js +312 -0
- package/dist/wallet/state/session.d.ts +12 -0
- package/dist/wallet/state/session.js +23 -0
- package/dist/wallet/state/storage.d.ts +19 -0
- package/dist/wallet/state/storage.js +55 -0
- package/dist/wallet/tx/anchor.d.ts +40 -0
- package/dist/wallet/tx/anchor.js +1210 -0
- package/dist/wallet/tx/cog.d.ts +92 -0
- package/dist/wallet/tx/cog.js +1055 -0
- package/dist/wallet/tx/common.d.ts +89 -0
- package/dist/wallet/tx/common.js +156 -0
- package/dist/wallet/tx/confirm.d.ts +15 -0
- package/dist/wallet/tx/confirm.js +24 -0
- package/dist/wallet/tx/domain-admin.d.ts +105 -0
- package/dist/wallet/tx/domain-admin.js +869 -0
- package/dist/wallet/tx/domain-market.d.ts +112 -0
- package/dist/wallet/tx/domain-market.js +1365 -0
- package/dist/wallet/tx/field.d.ts +101 -0
- package/dist/wallet/tx/field.js +1853 -0
- package/dist/wallet/tx/identity-selector.d.ts +12 -0
- package/dist/wallet/tx/identity-selector.js +52 -0
- package/dist/wallet/tx/index.d.ts +7 -0
- package/dist/wallet/tx/index.js +7 -0
- package/dist/wallet/tx/journal.d.ts +5 -0
- package/dist/wallet/tx/journal.js +31 -0
- package/dist/wallet/tx/register.d.ts +68 -0
- package/dist/wallet/tx/register.js +952 -0
- package/dist/wallet/tx/reputation.d.ts +72 -0
- package/dist/wallet/tx/reputation.js +693 -0
- package/dist/wallet/tx/targets.d.ts +7 -0
- package/dist/wallet/tx/targets.js +122 -0
- package/dist/wallet/types.d.ts +249 -0
- package/dist/wallet/types.js +1 -0
- package/dist/writing_quotes.json +1654 -0
- package/package.json +78 -0
|
@@ -0,0 +1,758 @@
|
|
|
1
|
+
import { randomBytes } from "node:crypto";
|
|
2
|
+
import { acquireFileLock } from "../fs/lock.js";
|
|
3
|
+
import { loadUnlockedWalletState } from "../lifecycle.js";
|
|
4
|
+
import { resolveWalletRuntimePathsForTesting } from "../runtime.js";
|
|
5
|
+
import { saveWalletState } from "../state/storage.js";
|
|
6
|
+
import { createDefaultWalletSecretProvider, createWalletSecretReference, } from "../state/provider.js";
|
|
7
|
+
import { appendMiningEvent, getLastMiningEventTimestamp, loadMiningRuntimeStatus, readMiningEvents, saveMiningRuntimeStatus, followMiningEvents } from "./runtime-artifacts.js";
|
|
8
|
+
import { requestMiningGenerationPreemption } from "./coordination.js";
|
|
9
|
+
import { ensureMiningHookTemplate, inspectMiningHookState, shouldEnterHookCooldown, validateCustomMiningHook, } from "./hooks.js";
|
|
10
|
+
import { normalizeMiningPublishState, normalizeMiningStateRecord } from "./state.js";
|
|
11
|
+
import { loadClientConfig, saveBuiltInMiningProviderConfig } from "./config.js";
|
|
12
|
+
import { MINING_WORKER_API_VERSION } from "./constants.js";
|
|
13
|
+
const WORKER_HEARTBEAT_STALE_MS = 15_000;
|
|
14
|
+
function normalizeHookMode(mode) {
|
|
15
|
+
return mode === "custom" ? "custom" : "builtin";
|
|
16
|
+
}
|
|
17
|
+
function createDefaultHookState() {
|
|
18
|
+
return {
|
|
19
|
+
mode: "builtin",
|
|
20
|
+
validationState: "never",
|
|
21
|
+
lastValidationAtUnixMs: null,
|
|
22
|
+
lastValidationError: null,
|
|
23
|
+
validatedLaunchFingerprint: null,
|
|
24
|
+
validatedFullFingerprint: null,
|
|
25
|
+
fullTrustWarningAcknowledgedAtUnixMs: null,
|
|
26
|
+
consecutiveFailureCount: 0,
|
|
27
|
+
cooldownUntilUnixMs: null,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
function createMiningEvent(kind, message, options = {}) {
|
|
31
|
+
return {
|
|
32
|
+
schemaVersion: 1,
|
|
33
|
+
timestampUnixMs: options.timestampUnixMs ?? Date.now(),
|
|
34
|
+
level: options.level ?? "info",
|
|
35
|
+
kind,
|
|
36
|
+
message,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
function buildProviderInspection(options) {
|
|
40
|
+
if (options.error !== null) {
|
|
41
|
+
return {
|
|
42
|
+
configured: false,
|
|
43
|
+
provider: null,
|
|
44
|
+
status: "error",
|
|
45
|
+
message: options.error,
|
|
46
|
+
modelOverride: null,
|
|
47
|
+
extraPromptConfigured: false,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
if (options.config === null) {
|
|
51
|
+
return {
|
|
52
|
+
configured: false,
|
|
53
|
+
provider: null,
|
|
54
|
+
status: "missing",
|
|
55
|
+
message: "Built-in mining provider is not configured yet.",
|
|
56
|
+
modelOverride: null,
|
|
57
|
+
extraPromptConfigured: false,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
return {
|
|
61
|
+
configured: true,
|
|
62
|
+
provider: options.config.provider,
|
|
63
|
+
status: "ready",
|
|
64
|
+
message: null,
|
|
65
|
+
modelOverride: options.config.modelOverride,
|
|
66
|
+
extraPromptConfigured: options.config.extraPrompt !== null && options.config.extraPrompt.length > 0,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
async function isProcessAlive(pid) {
|
|
70
|
+
if (pid === null) {
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
try {
|
|
74
|
+
process.kill(pid, 0);
|
|
75
|
+
return true;
|
|
76
|
+
}
|
|
77
|
+
catch (error) {
|
|
78
|
+
if (error instanceof Error && "code" in error && error.code === "ESRCH") {
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
return true;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
function mapProviderState(provider, hookMode, localState, nowUnixMs) {
|
|
85
|
+
const miningState = localState.state?.miningState === undefined
|
|
86
|
+
? null
|
|
87
|
+
: normalizeMiningStateRecord(localState.state.miningState);
|
|
88
|
+
const hookState = localState.state?.hookClientState.mining ?? null;
|
|
89
|
+
const hookCooldownUntilUnixMs = hookState?.cooldownUntilUnixMs ?? null;
|
|
90
|
+
const hookCooldownActive = hookCooldownUntilUnixMs !== null
|
|
91
|
+
&& hookCooldownUntilUnixMs > nowUnixMs;
|
|
92
|
+
if (hookMode === "custom") {
|
|
93
|
+
return hookState?.validationState === "failed" || hookCooldownActive
|
|
94
|
+
? "hook-error"
|
|
95
|
+
: "n/a";
|
|
96
|
+
}
|
|
97
|
+
if (miningState?.state === "paused" && miningState.pauseReason?.includes("rate-limit")) {
|
|
98
|
+
return "rate-limited";
|
|
99
|
+
}
|
|
100
|
+
if (miningState?.state === "paused" && miningState.pauseReason?.includes("auth")) {
|
|
101
|
+
return "auth-error";
|
|
102
|
+
}
|
|
103
|
+
if (miningState?.state === "paused" && miningState.pauseReason?.includes("provider")) {
|
|
104
|
+
return "backoff";
|
|
105
|
+
}
|
|
106
|
+
if (provider.status === "ready") {
|
|
107
|
+
return "ready";
|
|
108
|
+
}
|
|
109
|
+
if (provider.status === "error") {
|
|
110
|
+
return "unavailable";
|
|
111
|
+
}
|
|
112
|
+
return "unavailable";
|
|
113
|
+
}
|
|
114
|
+
function mapIndexerDaemonState(indexer) {
|
|
115
|
+
if (indexer.health === "wallet-root-mismatch") {
|
|
116
|
+
return "wallet-root-mismatch";
|
|
117
|
+
}
|
|
118
|
+
if (indexer.health === "service-version-mismatch") {
|
|
119
|
+
return "service-version-mismatch";
|
|
120
|
+
}
|
|
121
|
+
if (indexer.health === "schema-mismatch") {
|
|
122
|
+
return "schema-mismatch";
|
|
123
|
+
}
|
|
124
|
+
if (indexer.status !== null) {
|
|
125
|
+
switch (indexer.status.state) {
|
|
126
|
+
case "synced":
|
|
127
|
+
return indexer.health === "stale-heartbeat" ? "stale-heartbeat" : "synced";
|
|
128
|
+
case "catching-up":
|
|
129
|
+
return indexer.health === "stale-heartbeat" ? "stale-heartbeat" : "catching-up";
|
|
130
|
+
case "reorging":
|
|
131
|
+
return indexer.health === "stale-heartbeat" ? "stale-heartbeat" : "reorging";
|
|
132
|
+
case "starting":
|
|
133
|
+
case "stopping":
|
|
134
|
+
return indexer.health === "stale-heartbeat" ? "stale-heartbeat" : "starting";
|
|
135
|
+
case "failed":
|
|
136
|
+
return "failed";
|
|
137
|
+
case "schema-mismatch":
|
|
138
|
+
return "schema-mismatch";
|
|
139
|
+
case "service-version-mismatch":
|
|
140
|
+
return "service-version-mismatch";
|
|
141
|
+
default:
|
|
142
|
+
break;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
switch (indexer.health) {
|
|
146
|
+
case "failed":
|
|
147
|
+
return "failed";
|
|
148
|
+
case "starting":
|
|
149
|
+
return "starting";
|
|
150
|
+
case "catching-up":
|
|
151
|
+
return "catching-up";
|
|
152
|
+
case "reorging":
|
|
153
|
+
return "reorging";
|
|
154
|
+
case "stale-heartbeat":
|
|
155
|
+
return "stale-heartbeat";
|
|
156
|
+
case "synced":
|
|
157
|
+
return "synced";
|
|
158
|
+
default:
|
|
159
|
+
return "unavailable";
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
function mapCorePublishState(nodeHealth, nodeStatus) {
|
|
163
|
+
if (nodeStatus === null || !nodeStatus.ready) {
|
|
164
|
+
return "unknown";
|
|
165
|
+
}
|
|
166
|
+
if (nodeHealth === "catching-up") {
|
|
167
|
+
return "ibd";
|
|
168
|
+
}
|
|
169
|
+
return "healthy";
|
|
170
|
+
}
|
|
171
|
+
async function deriveBackgroundWorkerHealth(options) {
|
|
172
|
+
const runtime = options.runtime;
|
|
173
|
+
if (runtime?.runMode !== "background") {
|
|
174
|
+
return null;
|
|
175
|
+
}
|
|
176
|
+
if (runtime.walletRootId !== null
|
|
177
|
+
&& options.localState.walletRootId !== null
|
|
178
|
+
&& runtime.walletRootId !== options.localState.walletRootId) {
|
|
179
|
+
return "version-mismatch";
|
|
180
|
+
}
|
|
181
|
+
if (runtime.workerApiVersion !== null && runtime.workerApiVersion !== MINING_WORKER_API_VERSION) {
|
|
182
|
+
return "version-mismatch";
|
|
183
|
+
}
|
|
184
|
+
if (!await isProcessAlive(runtime.backgroundWorkerPid)) {
|
|
185
|
+
return "stale-pid";
|
|
186
|
+
}
|
|
187
|
+
if (runtime.backgroundWorkerHeartbeatAtUnixMs === null
|
|
188
|
+
|| (options.nowUnixMs - runtime.backgroundWorkerHeartbeatAtUnixMs) > WORKER_HEARTBEAT_STALE_MS) {
|
|
189
|
+
return "stale-heartbeat";
|
|
190
|
+
}
|
|
191
|
+
return "healthy";
|
|
192
|
+
}
|
|
193
|
+
async function buildMiningRuntimeSnapshot(options) {
|
|
194
|
+
const state = options.localState.state?.miningState === undefined
|
|
195
|
+
? null
|
|
196
|
+
: normalizeMiningStateRecord(options.localState.state.miningState);
|
|
197
|
+
const backgroundWorkerHealth = await deriveBackgroundWorkerHealth({
|
|
198
|
+
runtime: options.existingRuntime,
|
|
199
|
+
localState: options.localState,
|
|
200
|
+
nowUnixMs: options.nowUnixMs,
|
|
201
|
+
});
|
|
202
|
+
const providerState = mapProviderState(options.provider, options.hookMode, options.localState, options.nowUnixMs);
|
|
203
|
+
const indexerDaemonState = mapIndexerDaemonState(options.indexer);
|
|
204
|
+
const corePublishState = mapCorePublishState(options.nodeHealth, options.nodeStatus);
|
|
205
|
+
const existing = options.existingRuntime;
|
|
206
|
+
return {
|
|
207
|
+
schemaVersion: 1,
|
|
208
|
+
walletRootId: options.localState.walletRootId,
|
|
209
|
+
workerApiVersion: existing?.workerApiVersion ?? null,
|
|
210
|
+
workerBinaryVersion: existing?.workerBinaryVersion ?? null,
|
|
211
|
+
workerBuildId: existing?.workerBuildId ?? null,
|
|
212
|
+
updatedAtUnixMs: options.nowUnixMs,
|
|
213
|
+
runMode: state?.runMode ?? existing?.runMode ?? "stopped",
|
|
214
|
+
backgroundWorkerPid: existing?.backgroundWorkerPid ?? null,
|
|
215
|
+
backgroundWorkerRunId: existing?.backgroundWorkerRunId ?? null,
|
|
216
|
+
backgroundWorkerHeartbeatAtUnixMs: existing?.backgroundWorkerHeartbeatAtUnixMs ?? null,
|
|
217
|
+
backgroundWorkerHealth,
|
|
218
|
+
indexerDaemonState,
|
|
219
|
+
indexerDaemonInstanceId: options.indexer.daemonInstanceId ?? null,
|
|
220
|
+
indexerSnapshotSeq: options.indexer.snapshotSeq ?? null,
|
|
221
|
+
indexerSnapshotOpenedAtUnixMs: options.indexer.openedAtUnixMs ?? null,
|
|
222
|
+
indexerTruthSource: options.indexer.source ?? "none",
|
|
223
|
+
indexerHeartbeatAtUnixMs: options.indexer.status?.heartbeatAtUnixMs ?? null,
|
|
224
|
+
coreBestHeight: options.nodeStatus?.nodeBestHeight ?? options.indexer.status?.coreBestHeight ?? existing?.coreBestHeight ?? null,
|
|
225
|
+
coreBestHash: options.nodeStatus?.nodeBestHashHex ?? options.indexer.status?.coreBestHash ?? existing?.coreBestHash ?? null,
|
|
226
|
+
indexerTipHeight: options.indexer.snapshotTip?.height ?? options.indexer.status?.appliedTipHeight ?? null,
|
|
227
|
+
indexerTipHash: options.indexer.snapshotTip?.blockHashHex ?? options.indexer.status?.appliedTipHash ?? null,
|
|
228
|
+
indexerReorgDepth: options.indexer.status?.reorgDepth ?? null,
|
|
229
|
+
indexerTipAligned: options.tipsAligned,
|
|
230
|
+
corePublishState,
|
|
231
|
+
providerState,
|
|
232
|
+
lastSuspendDetectedAtUnixMs: existing?.lastSuspendDetectedAtUnixMs ?? null,
|
|
233
|
+
reconnectSettledUntilUnixMs: existing?.reconnectSettledUntilUnixMs ?? null,
|
|
234
|
+
tipSettledUntilUnixMs: existing?.tipSettledUntilUnixMs ?? null,
|
|
235
|
+
miningState: state?.state ?? existing?.miningState ?? "idle",
|
|
236
|
+
currentPhase: existing?.currentPhase ?? "idle",
|
|
237
|
+
currentPublishState: normalizeMiningPublishState(state?.currentPublishState ?? options.existingRuntime?.currentPublishState ?? "none"),
|
|
238
|
+
targetBlockHeight: state?.currentBlockTargetHeight ?? existing?.targetBlockHeight ?? null,
|
|
239
|
+
referencedBlockHashDisplay: state?.currentReferencedBlockHashDisplay ?? existing?.referencedBlockHashDisplay ?? null,
|
|
240
|
+
currentDomainId: state?.currentDomainId ?? existing?.currentDomainId ?? null,
|
|
241
|
+
currentDomainName: state?.currentDomain ?? existing?.currentDomainName ?? null,
|
|
242
|
+
currentSentenceDisplay: state?.currentSentence ?? existing?.currentSentenceDisplay ?? null,
|
|
243
|
+
currentCanonicalBlend: state?.currentScore ?? existing?.currentCanonicalBlend ?? null,
|
|
244
|
+
currentTxid: state?.currentTxid ?? existing?.currentTxid ?? null,
|
|
245
|
+
currentWtxid: state?.currentWtxid ?? existing?.currentWtxid ?? null,
|
|
246
|
+
liveMiningFamilyInMempool: state?.liveMiningFamilyInMempool ?? existing?.liveMiningFamilyInMempool ?? null,
|
|
247
|
+
currentFeeRateSatVb: state?.currentFeeRateSatVb ?? existing?.currentFeeRateSatVb ?? null,
|
|
248
|
+
currentAbsoluteFeeSats: state?.currentAbsoluteFeeSats ?? existing?.currentAbsoluteFeeSats ?? null,
|
|
249
|
+
currentBlockFeeSpentSats: state?.currentBlockFeeSpentSats ?? existing?.currentBlockFeeSpentSats ?? "0",
|
|
250
|
+
sessionFeeSpentSats: state?.sessionFeeSpentSats ?? existing?.sessionFeeSpentSats ?? "0",
|
|
251
|
+
lifetimeFeeSpentSats: state?.lifetimeFeeSpentSats ?? existing?.lifetimeFeeSpentSats ?? "0",
|
|
252
|
+
sameDomainCompetitorSuppressed: existing?.sameDomainCompetitorSuppressed ?? null,
|
|
253
|
+
higherRankedCompetitorDomainCount: existing?.higherRankedCompetitorDomainCount ?? null,
|
|
254
|
+
dedupedCompetitorDomainCount: existing?.dedupedCompetitorDomainCount ?? null,
|
|
255
|
+
competitivenessGateIndeterminate: existing?.competitivenessGateIndeterminate ?? null,
|
|
256
|
+
mempoolSequenceCacheStatus: existing?.mempoolSequenceCacheStatus ?? null,
|
|
257
|
+
currentPublishDecision: state?.currentPublishDecision ?? existing?.currentPublishDecision ?? null,
|
|
258
|
+
lastMempoolSequence: existing?.lastMempoolSequence ?? null,
|
|
259
|
+
lastCompetitivenessGateAtUnixMs: existing?.lastCompetitivenessGateAtUnixMs ?? null,
|
|
260
|
+
pauseReason: state?.pauseReason ?? options.existingRuntime?.pauseReason ?? null,
|
|
261
|
+
hookMode: options.hookMode,
|
|
262
|
+
providerConfigured: options.provider.configured,
|
|
263
|
+
providerKind: options.provider.provider,
|
|
264
|
+
bitcoindHealth: options.bitcoind.health,
|
|
265
|
+
bitcoindServiceState: options.nodeStatus?.serviceStatus?.state ?? null,
|
|
266
|
+
bitcoindReplicaStatus: options.nodeStatus?.walletReplica?.proofStatus ?? null,
|
|
267
|
+
nodeHealth: options.nodeHealth,
|
|
268
|
+
indexerHealth: options.indexer.health,
|
|
269
|
+
tipsAligned: options.tipsAligned,
|
|
270
|
+
lastValidationState: options.hookValidationState,
|
|
271
|
+
lastOperatorValidationState: options.hookOperatorValidationState,
|
|
272
|
+
lastValidationAtUnixMs: options.hookValidationAtUnixMs,
|
|
273
|
+
lastEventAtUnixMs: options.lastEventAtUnixMs,
|
|
274
|
+
lastError: existing?.lastError ?? options.provider.message ?? options.indexer.message ?? null,
|
|
275
|
+
note: options.hookMode === "custom" && options.hookCooldownActive
|
|
276
|
+
? "Custom mining hook launch is paused during the post-failure cooldown window."
|
|
277
|
+
: options.hookMode === "custom" && options.hookOperatorValidationState !== "current"
|
|
278
|
+
? "Custom mining hook is selected, but it must be revalidated before it can launch."
|
|
279
|
+
: state?.pauseReason === "zero-reward"
|
|
280
|
+
? "Mining is disabled because the target block reward is zero."
|
|
281
|
+
: existing?.currentPhase === "resuming"
|
|
282
|
+
? "Mining discarded stale in-flight work after a large local runtime gap and is rechecking health."
|
|
283
|
+
: existing?.currentPhase === "waiting-provider"
|
|
284
|
+
? "Mining is waiting for the sentence provider to recover."
|
|
285
|
+
: existing?.currentPhase === "waiting-indexer"
|
|
286
|
+
? "Mining is waiting for Bitcoin Core and the indexer to align."
|
|
287
|
+
: existing?.currentPhase === "waiting-bitcoin-network"
|
|
288
|
+
? "Mining is waiting for the local Bitcoin node to become publishable."
|
|
289
|
+
: state?.state === "repair-required"
|
|
290
|
+
? "Mining is blocked until the current mining family is reconciled or `cogcoin repair` completes."
|
|
291
|
+
: state?.state === "paused-stale" && state.liveMiningFamilyInMempool
|
|
292
|
+
? "A previously broadcast mining family is still in mempool for an older tip context. Wait for confirmation or rerun mining to replace it."
|
|
293
|
+
: state?.state === "paused" && state.liveMiningFamilyInMempool
|
|
294
|
+
? "Mining is paused, but the last mining transaction may still confirm from mempool without further fee bumps."
|
|
295
|
+
: state?.state === "paused"
|
|
296
|
+
? "Mining is paused by another wallet command or local policy."
|
|
297
|
+
: options.provider.status === "missing"
|
|
298
|
+
? "Run `cogcoin mine setup` to configure the built-in mining provider."
|
|
299
|
+
: options.indexer.health === "reorging"
|
|
300
|
+
? "Mining remains stopped while the indexer replays a reorg and refreshes the coherent snapshot."
|
|
301
|
+
: options.indexer.health !== "synced" || options.nodeHealth !== "synced"
|
|
302
|
+
? "Mining remains stopped until Bitcoin Core and the indexer are both healthy and aligned."
|
|
303
|
+
: null,
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
async function persistWalletMiningHookState(options) {
|
|
307
|
+
await saveWalletState({
|
|
308
|
+
primaryPath: options.paths.walletStatePath,
|
|
309
|
+
backupPath: options.paths.walletStateBackupPath,
|
|
310
|
+
}, options.state, {
|
|
311
|
+
provider: options.provider,
|
|
312
|
+
secretReference: createWalletSecretReference(options.state.walletRootId),
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
async function loadMiningProviderConfig(options) {
|
|
316
|
+
try {
|
|
317
|
+
const config = await loadClientConfig({
|
|
318
|
+
path: options.paths.clientConfigPath,
|
|
319
|
+
provider: options.provider,
|
|
320
|
+
});
|
|
321
|
+
return {
|
|
322
|
+
config: config?.mining.builtIn ?? null,
|
|
323
|
+
error: null,
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
catch (error) {
|
|
327
|
+
return {
|
|
328
|
+
config: null,
|
|
329
|
+
error: error instanceof Error ? error.message : String(error),
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
export async function inspectMiningControlPlane(options) {
|
|
334
|
+
const provider = options.provider ?? createDefaultWalletSecretProvider();
|
|
335
|
+
const paths = options.paths ?? resolveWalletRuntimePathsForTesting();
|
|
336
|
+
const nowUnixMs = options.nowUnixMs ?? Date.now();
|
|
337
|
+
const providerConfig = await loadMiningProviderConfig({
|
|
338
|
+
paths,
|
|
339
|
+
provider,
|
|
340
|
+
});
|
|
341
|
+
const hookState = options.localState.state?.hookClientState.mining ?? null;
|
|
342
|
+
const hook = await inspectMiningHookState({
|
|
343
|
+
hookRootPath: paths.hooksMiningDir,
|
|
344
|
+
entrypointPath: paths.hooksMiningEntrypointPath,
|
|
345
|
+
packagePath: paths.hooksMiningPackageJsonPath,
|
|
346
|
+
localState: hookState,
|
|
347
|
+
verify: options.verify ?? false,
|
|
348
|
+
});
|
|
349
|
+
const providerInspection = buildProviderInspection(providerConfig);
|
|
350
|
+
const existingRuntime = await loadMiningRuntimeStatus(paths.miningStatusPath).catch(() => null);
|
|
351
|
+
const lastEventAtUnixMs = await getLastMiningEventTimestamp(paths.miningEventsPath).catch(() => null);
|
|
352
|
+
const nodeBestHeight = options.nodeStatus?.nodeBestHeight ?? null;
|
|
353
|
+
const indexerHeight = options.indexer.snapshotTip?.height ?? null;
|
|
354
|
+
const tipsAligned = nodeBestHeight === null || indexerHeight === null ? null : nodeBestHeight === indexerHeight;
|
|
355
|
+
const runtime = await buildMiningRuntimeSnapshot({
|
|
356
|
+
nowUnixMs,
|
|
357
|
+
localState: options.localState,
|
|
358
|
+
bitcoind: options.bitcoind,
|
|
359
|
+
nodeStatus: options.nodeStatus,
|
|
360
|
+
hookMode: normalizeHookMode(hook.mode === "unavailable" ? null : hook.mode),
|
|
361
|
+
hookValidationState: hook.validationState === "unavailable" ? null : hook.validationState,
|
|
362
|
+
hookOperatorValidationState: hook.operatorValidationState,
|
|
363
|
+
hookValidationAtUnixMs: hook.validatedAtUnixMs,
|
|
364
|
+
hookCooldownActive: hook.cooldownActive,
|
|
365
|
+
provider: providerInspection,
|
|
366
|
+
nodeHealth: options.nodeHealth,
|
|
367
|
+
indexer: options.indexer,
|
|
368
|
+
tipsAligned,
|
|
369
|
+
lastEventAtUnixMs,
|
|
370
|
+
existingRuntime,
|
|
371
|
+
});
|
|
372
|
+
return {
|
|
373
|
+
runtime,
|
|
374
|
+
hook,
|
|
375
|
+
provider: providerInspection,
|
|
376
|
+
lastEventAtUnixMs,
|
|
377
|
+
};
|
|
378
|
+
}
|
|
379
|
+
export async function refreshMiningRuntimeStatus(options) {
|
|
380
|
+
const view = await inspectMiningControlPlane(options);
|
|
381
|
+
const paths = options.paths ?? resolveWalletRuntimePathsForTesting();
|
|
382
|
+
await saveMiningRuntimeStatus(paths.miningStatusPath, view.runtime);
|
|
383
|
+
return view;
|
|
384
|
+
}
|
|
385
|
+
export async function enableMiningHooks(options) {
|
|
386
|
+
if (!options.prompter.isInteractive) {
|
|
387
|
+
throw new Error("mining_hooks_enable_requires_tty");
|
|
388
|
+
}
|
|
389
|
+
const provider = options.provider ?? createDefaultWalletSecretProvider();
|
|
390
|
+
const nowUnixMs = options.nowUnixMs ?? Date.now();
|
|
391
|
+
const paths = options.paths ?? resolveWalletRuntimePathsForTesting();
|
|
392
|
+
const controlLock = await acquireFileLock(paths.miningControlLockPath, {
|
|
393
|
+
purpose: "hooks-enable-mining",
|
|
394
|
+
});
|
|
395
|
+
try {
|
|
396
|
+
const preemption = await requestMiningGenerationPreemption({
|
|
397
|
+
paths,
|
|
398
|
+
reason: "hooks-enable-mining",
|
|
399
|
+
});
|
|
400
|
+
const unlocked = await loadUnlockedWalletState({
|
|
401
|
+
provider,
|
|
402
|
+
nowUnixMs,
|
|
403
|
+
paths,
|
|
404
|
+
});
|
|
405
|
+
try {
|
|
406
|
+
if (unlocked === null) {
|
|
407
|
+
throw new Error("wallet_locked");
|
|
408
|
+
}
|
|
409
|
+
const state = unlocked.state;
|
|
410
|
+
state.hookClientState.mining ??= createDefaultHookState();
|
|
411
|
+
if (state.hookClientState.mining.fullTrustWarningAcknowledgedAtUnixMs === null) {
|
|
412
|
+
options.prompter.writeLine("Enabling a custom mining hook gives local JavaScript code full access to your user account.");
|
|
413
|
+
const confirmation = await options.prompter.prompt("Type TRUST CUSTOM MINING HOOKS to continue: ");
|
|
414
|
+
if (confirmation.trim() !== "TRUST CUSTOM MINING HOOKS") {
|
|
415
|
+
throw new Error("mining_hooks_enable_trust_acknowledgement_required");
|
|
416
|
+
}
|
|
417
|
+
state.hookClientState.mining.fullTrustWarningAcknowledgedAtUnixMs = nowUnixMs;
|
|
418
|
+
}
|
|
419
|
+
const createdTemplate = await ensureMiningHookTemplate({
|
|
420
|
+
hookRootPath: paths.hooksMiningDir,
|
|
421
|
+
entrypointPath: paths.hooksMiningEntrypointPath,
|
|
422
|
+
packagePath: paths.hooksMiningPackageJsonPath,
|
|
423
|
+
});
|
|
424
|
+
if (createdTemplate) {
|
|
425
|
+
await persistWalletMiningHookState({ state, provider, paths });
|
|
426
|
+
await appendMiningEvent(paths.miningEventsPath, createMiningEvent("custom-hook-template-created", "Created the default mining hook template. Edit it, then rerun `cogcoin hooks enable mining`.", { timestampUnixMs: nowUnixMs }));
|
|
427
|
+
await refreshMiningRuntimeStatus({
|
|
428
|
+
provider,
|
|
429
|
+
localState: {
|
|
430
|
+
availability: "ready",
|
|
431
|
+
walletRootId: state.walletRootId,
|
|
432
|
+
state,
|
|
433
|
+
source: unlocked.source,
|
|
434
|
+
unlockUntilUnixMs: unlocked.session.unlockUntilUnixMs,
|
|
435
|
+
hasPrimaryStateFile: true,
|
|
436
|
+
hasBackupStateFile: true,
|
|
437
|
+
hasUnlockSessionFile: true,
|
|
438
|
+
message: null,
|
|
439
|
+
},
|
|
440
|
+
bitcoind: {
|
|
441
|
+
health: "unavailable",
|
|
442
|
+
status: null,
|
|
443
|
+
message: "Managed bitcoind status unavailable during hook setup.",
|
|
444
|
+
},
|
|
445
|
+
nodeStatus: null,
|
|
446
|
+
nodeHealth: "unavailable",
|
|
447
|
+
indexer: {
|
|
448
|
+
health: "unavailable",
|
|
449
|
+
status: null,
|
|
450
|
+
message: "Indexer status unavailable during hook setup.",
|
|
451
|
+
snapshotTip: null,
|
|
452
|
+
source: "none",
|
|
453
|
+
daemonInstanceId: null,
|
|
454
|
+
snapshotSeq: null,
|
|
455
|
+
openedAtUnixMs: null,
|
|
456
|
+
},
|
|
457
|
+
nowUnixMs,
|
|
458
|
+
paths,
|
|
459
|
+
});
|
|
460
|
+
throw new Error(`mining_hooks_enable_template_created:${paths.hooksMiningDir}`);
|
|
461
|
+
}
|
|
462
|
+
try {
|
|
463
|
+
const validation = await validateCustomMiningHook({
|
|
464
|
+
hookRootPath: paths.hooksMiningDir,
|
|
465
|
+
entrypointPath: paths.hooksMiningEntrypointPath,
|
|
466
|
+
packagePath: paths.hooksMiningPackageJsonPath,
|
|
467
|
+
});
|
|
468
|
+
state.hookClientState.mining = {
|
|
469
|
+
...state.hookClientState.mining,
|
|
470
|
+
mode: "custom",
|
|
471
|
+
validationState: "current",
|
|
472
|
+
lastValidationAtUnixMs: nowUnixMs,
|
|
473
|
+
lastValidationError: null,
|
|
474
|
+
validatedLaunchFingerprint: validation.launchFingerprint,
|
|
475
|
+
validatedFullFingerprint: validation.fullFingerprint,
|
|
476
|
+
consecutiveFailureCount: 0,
|
|
477
|
+
cooldownUntilUnixMs: null,
|
|
478
|
+
};
|
|
479
|
+
await persistWalletMiningHookState({ state, provider, paths });
|
|
480
|
+
await appendMiningEvent(paths.miningEventsPath, createMiningEvent("custom-hook-enabled", "Custom mining hook enabled after validation.", { timestampUnixMs: nowUnixMs }));
|
|
481
|
+
}
|
|
482
|
+
catch (error) {
|
|
483
|
+
const validationError = error instanceof Error ? error.message : String(error);
|
|
484
|
+
const consecutiveFailureCount = (state.hookClientState.mining.consecutiveFailureCount ?? 0) + 1;
|
|
485
|
+
state.hookClientState.mining = {
|
|
486
|
+
...state.hookClientState.mining,
|
|
487
|
+
validationState: "failed",
|
|
488
|
+
lastValidationAtUnixMs: nowUnixMs,
|
|
489
|
+
lastValidationError: validationError,
|
|
490
|
+
validatedLaunchFingerprint: null,
|
|
491
|
+
validatedFullFingerprint: null,
|
|
492
|
+
consecutiveFailureCount,
|
|
493
|
+
cooldownUntilUnixMs: shouldEnterHookCooldown({
|
|
494
|
+
consecutiveFailureCount,
|
|
495
|
+
nowUnixMs,
|
|
496
|
+
}),
|
|
497
|
+
};
|
|
498
|
+
await persistWalletMiningHookState({ state, provider, paths });
|
|
499
|
+
await appendMiningEvent(paths.miningEventsPath, createMiningEvent("custom-hook-validation-failed", state.hookClientState.mining.lastValidationError ?? "Custom mining hook validation failed.", {
|
|
500
|
+
level: "error",
|
|
501
|
+
timestampUnixMs: nowUnixMs,
|
|
502
|
+
}));
|
|
503
|
+
throw new Error(`mining_hooks_enable_validation_failed:${validationError}`);
|
|
504
|
+
}
|
|
505
|
+
return refreshMiningRuntimeStatus({
|
|
506
|
+
provider,
|
|
507
|
+
localState: {
|
|
508
|
+
availability: "ready",
|
|
509
|
+
walletRootId: state.walletRootId,
|
|
510
|
+
state,
|
|
511
|
+
source: unlocked.source,
|
|
512
|
+
unlockUntilUnixMs: unlocked.session.unlockUntilUnixMs,
|
|
513
|
+
hasPrimaryStateFile: true,
|
|
514
|
+
hasBackupStateFile: true,
|
|
515
|
+
hasUnlockSessionFile: true,
|
|
516
|
+
message: null,
|
|
517
|
+
},
|
|
518
|
+
bitcoind: {
|
|
519
|
+
health: "unavailable",
|
|
520
|
+
status: null,
|
|
521
|
+
message: "Managed bitcoind status unavailable during hook setup.",
|
|
522
|
+
},
|
|
523
|
+
nodeStatus: null,
|
|
524
|
+
nodeHealth: "unavailable",
|
|
525
|
+
indexer: {
|
|
526
|
+
health: "unavailable",
|
|
527
|
+
status: null,
|
|
528
|
+
message: "Indexer status unavailable during hook setup.",
|
|
529
|
+
snapshotTip: null,
|
|
530
|
+
source: "none",
|
|
531
|
+
daemonInstanceId: null,
|
|
532
|
+
snapshotSeq: null,
|
|
533
|
+
openedAtUnixMs: null,
|
|
534
|
+
},
|
|
535
|
+
nowUnixMs,
|
|
536
|
+
paths,
|
|
537
|
+
});
|
|
538
|
+
}
|
|
539
|
+
finally {
|
|
540
|
+
await preemption.release();
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
finally {
|
|
544
|
+
await controlLock.release();
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
export async function disableMiningHooks(options) {
|
|
548
|
+
const provider = options.provider ?? createDefaultWalletSecretProvider();
|
|
549
|
+
const nowUnixMs = options.nowUnixMs ?? Date.now();
|
|
550
|
+
const paths = options.paths ?? resolveWalletRuntimePathsForTesting();
|
|
551
|
+
const controlLock = await acquireFileLock(paths.miningControlLockPath, {
|
|
552
|
+
purpose: "hooks-disable-mining",
|
|
553
|
+
});
|
|
554
|
+
try {
|
|
555
|
+
const preemption = await requestMiningGenerationPreemption({
|
|
556
|
+
paths,
|
|
557
|
+
reason: "hooks-disable-mining",
|
|
558
|
+
});
|
|
559
|
+
const unlocked = await loadUnlockedWalletState({
|
|
560
|
+
provider,
|
|
561
|
+
nowUnixMs,
|
|
562
|
+
paths,
|
|
563
|
+
});
|
|
564
|
+
try {
|
|
565
|
+
if (unlocked === null) {
|
|
566
|
+
throw new Error("wallet_locked");
|
|
567
|
+
}
|
|
568
|
+
const state = unlocked.state;
|
|
569
|
+
state.hookClientState.mining = {
|
|
570
|
+
...createDefaultHookState(),
|
|
571
|
+
mode: "builtin",
|
|
572
|
+
fullTrustWarningAcknowledgedAtUnixMs: state.hookClientState.mining?.fullTrustWarningAcknowledgedAtUnixMs ?? null,
|
|
573
|
+
};
|
|
574
|
+
await persistWalletMiningHookState({ state, provider, paths });
|
|
575
|
+
await appendMiningEvent(paths.miningEventsPath, createMiningEvent("custom-hook-disabled", "Custom mining hook disabled. Built-in mining mode is active again.", { timestampUnixMs: nowUnixMs }));
|
|
576
|
+
return refreshMiningRuntimeStatus({
|
|
577
|
+
provider,
|
|
578
|
+
localState: {
|
|
579
|
+
availability: "ready",
|
|
580
|
+
walletRootId: state.walletRootId,
|
|
581
|
+
state,
|
|
582
|
+
source: unlocked.source,
|
|
583
|
+
unlockUntilUnixMs: unlocked.session.unlockUntilUnixMs,
|
|
584
|
+
hasPrimaryStateFile: true,
|
|
585
|
+
hasBackupStateFile: true,
|
|
586
|
+
hasUnlockSessionFile: true,
|
|
587
|
+
message: null,
|
|
588
|
+
},
|
|
589
|
+
bitcoind: {
|
|
590
|
+
health: "unavailable",
|
|
591
|
+
status: null,
|
|
592
|
+
message: "Managed bitcoind status unavailable during hook setup.",
|
|
593
|
+
},
|
|
594
|
+
nodeStatus: null,
|
|
595
|
+
nodeHealth: "unavailable",
|
|
596
|
+
indexer: {
|
|
597
|
+
health: "unavailable",
|
|
598
|
+
status: null,
|
|
599
|
+
message: "Indexer status unavailable during hook setup.",
|
|
600
|
+
snapshotTip: null,
|
|
601
|
+
source: "none",
|
|
602
|
+
daemonInstanceId: null,
|
|
603
|
+
snapshotSeq: null,
|
|
604
|
+
openedAtUnixMs: null,
|
|
605
|
+
},
|
|
606
|
+
nowUnixMs,
|
|
607
|
+
paths,
|
|
608
|
+
});
|
|
609
|
+
}
|
|
610
|
+
finally {
|
|
611
|
+
await preemption.release();
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
finally {
|
|
615
|
+
await controlLock.release();
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
function normalizeProviderChoice(raw) {
|
|
619
|
+
const value = raw.trim().toLowerCase();
|
|
620
|
+
return value === "openai" || value === "anthropic" ? value : null;
|
|
621
|
+
}
|
|
622
|
+
function writeBuiltInMiningProviderDisclosure(prompter) {
|
|
623
|
+
prompter.writeLine("Built-in mining provider disclosure:");
|
|
624
|
+
prompter.writeLine("The built-in mining hook will send the following to the selected provider:");
|
|
625
|
+
prompter.writeLine("- eligible anchored root domain names");
|
|
626
|
+
prompter.writeLine("- the required five words for each root domain");
|
|
627
|
+
prompter.writeLine("- target block height");
|
|
628
|
+
prompter.writeLine("- referenced previous-block hash");
|
|
629
|
+
prompter.writeLine("- optional extra prompt when configured");
|
|
630
|
+
}
|
|
631
|
+
async function promptForMiningProviderConfig(prompter) {
|
|
632
|
+
writeBuiltInMiningProviderDisclosure(prompter);
|
|
633
|
+
const providerInput = await prompter.prompt("Provider (openai/anthropic): ");
|
|
634
|
+
const provider = normalizeProviderChoice(providerInput);
|
|
635
|
+
if (provider === null) {
|
|
636
|
+
throw new Error("mining_setup_invalid_provider");
|
|
637
|
+
}
|
|
638
|
+
const apiKey = (await prompter.prompt("API key: ")).trim();
|
|
639
|
+
if (apiKey.length === 0) {
|
|
640
|
+
throw new Error("mining_setup_missing_api_key");
|
|
641
|
+
}
|
|
642
|
+
const extraPrompt = (await prompter.prompt("Extra prompt (optional, blank for none): ")).trim();
|
|
643
|
+
const modelOverride = (await prompter.prompt("Model override (optional, blank for default): ")).trim();
|
|
644
|
+
return {
|
|
645
|
+
provider,
|
|
646
|
+
apiKey,
|
|
647
|
+
extraPrompt: extraPrompt.length === 0 ? null : extraPrompt,
|
|
648
|
+
modelOverride: modelOverride.length === 0 ? null : modelOverride,
|
|
649
|
+
updatedAtUnixMs: Date.now(),
|
|
650
|
+
};
|
|
651
|
+
}
|
|
652
|
+
export async function setupBuiltInMining(options) {
|
|
653
|
+
if (!options.prompter.isInteractive) {
|
|
654
|
+
throw new Error("mine_setup_requires_tty");
|
|
655
|
+
}
|
|
656
|
+
const provider = options.provider ?? createDefaultWalletSecretProvider();
|
|
657
|
+
const nowUnixMs = options.nowUnixMs ?? Date.now();
|
|
658
|
+
const paths = options.paths ?? resolveWalletRuntimePathsForTesting();
|
|
659
|
+
const controlLock = await acquireFileLock(paths.miningControlLockPath, {
|
|
660
|
+
purpose: "mine-setup",
|
|
661
|
+
});
|
|
662
|
+
try {
|
|
663
|
+
const preemption = await requestMiningGenerationPreemption({
|
|
664
|
+
paths,
|
|
665
|
+
reason: "mine-setup",
|
|
666
|
+
});
|
|
667
|
+
const unlocked = await loadUnlockedWalletState({
|
|
668
|
+
provider,
|
|
669
|
+
nowUnixMs,
|
|
670
|
+
paths,
|
|
671
|
+
});
|
|
672
|
+
try {
|
|
673
|
+
if (unlocked === null) {
|
|
674
|
+
throw new Error("wallet_locked");
|
|
675
|
+
}
|
|
676
|
+
const state = unlocked.state;
|
|
677
|
+
await appendMiningEvent(paths.miningEventsPath, createMiningEvent("mine-setup-started", "Started built-in mining provider setup.", { timestampUnixMs: nowUnixMs }));
|
|
678
|
+
try {
|
|
679
|
+
const config = await promptForMiningProviderConfig(options.prompter);
|
|
680
|
+
config.updatedAtUnixMs = nowUnixMs;
|
|
681
|
+
await saveBuiltInMiningProviderConfig({
|
|
682
|
+
path: paths.clientConfigPath,
|
|
683
|
+
provider,
|
|
684
|
+
secretReference: createWalletSecretReference(state.walletRootId),
|
|
685
|
+
config,
|
|
686
|
+
});
|
|
687
|
+
state.hookClientState.mining ??= createDefaultHookState();
|
|
688
|
+
if (state.hookClientState.mining.mode === "disabled") {
|
|
689
|
+
state.hookClientState.mining.mode = "builtin";
|
|
690
|
+
}
|
|
691
|
+
await persistWalletMiningHookState({ state, provider, paths });
|
|
692
|
+
await appendMiningEvent(paths.miningEventsPath, createMiningEvent("mine-setup-completed", `Configured the built-in ${config.provider} mining provider.`, { timestampUnixMs: nowUnixMs }));
|
|
693
|
+
return refreshMiningRuntimeStatus({
|
|
694
|
+
provider,
|
|
695
|
+
localState: {
|
|
696
|
+
availability: "ready",
|
|
697
|
+
walletRootId: state.walletRootId,
|
|
698
|
+
state,
|
|
699
|
+
source: unlocked.source,
|
|
700
|
+
unlockUntilUnixMs: unlocked.session.unlockUntilUnixMs,
|
|
701
|
+
hasPrimaryStateFile: true,
|
|
702
|
+
hasBackupStateFile: true,
|
|
703
|
+
hasUnlockSessionFile: true,
|
|
704
|
+
message: null,
|
|
705
|
+
},
|
|
706
|
+
bitcoind: {
|
|
707
|
+
health: "unavailable",
|
|
708
|
+
status: null,
|
|
709
|
+
message: "Managed bitcoind status unavailable during mining setup.",
|
|
710
|
+
},
|
|
711
|
+
nodeStatus: null,
|
|
712
|
+
nodeHealth: "unavailable",
|
|
713
|
+
indexer: {
|
|
714
|
+
health: "unavailable",
|
|
715
|
+
status: null,
|
|
716
|
+
message: "Indexer status unavailable during mining setup.",
|
|
717
|
+
snapshotTip: null,
|
|
718
|
+
source: "none",
|
|
719
|
+
daemonInstanceId: null,
|
|
720
|
+
snapshotSeq: null,
|
|
721
|
+
openedAtUnixMs: null,
|
|
722
|
+
},
|
|
723
|
+
nowUnixMs,
|
|
724
|
+
paths,
|
|
725
|
+
});
|
|
726
|
+
}
|
|
727
|
+
catch (error) {
|
|
728
|
+
await appendMiningEvent(paths.miningEventsPath, createMiningEvent("mine-setup-failed", error instanceof Error ? error.message : String(error), {
|
|
729
|
+
level: "error",
|
|
730
|
+
timestampUnixMs: nowUnixMs,
|
|
731
|
+
}));
|
|
732
|
+
throw error;
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
finally {
|
|
736
|
+
await preemption.release();
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
finally {
|
|
740
|
+
await controlLock.release();
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
export async function readMiningLog(options) {
|
|
744
|
+
const paths = options.paths ?? resolveWalletRuntimePathsForTesting();
|
|
745
|
+
return readMiningEvents({
|
|
746
|
+
eventsPath: paths.miningEventsPath,
|
|
747
|
+
limit: options.limit,
|
|
748
|
+
all: options.all,
|
|
749
|
+
});
|
|
750
|
+
}
|
|
751
|
+
export async function followMiningLog(options) {
|
|
752
|
+
const paths = options.paths ?? resolveWalletRuntimePathsForTesting();
|
|
753
|
+
return followMiningEvents({
|
|
754
|
+
eventsPath: paths.miningEventsPath,
|
|
755
|
+
signal: options.signal,
|
|
756
|
+
onEvent: options.onEvent,
|
|
757
|
+
});
|
|
758
|
+
}
|