@cogcoin/client 1.1.0 → 1.1.2
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/dist/bitcoind/client/sync-engine.js +1 -1
- package/dist/bitcoind/indexer-daemon-main.js +2 -11
- package/dist/bitcoind/indexer-daemon.js +26 -4
- package/dist/bitcoind/indexer-monitor.js +5 -1
- package/dist/bitcoind/progress/controller.js +4 -1
- package/dist/bitcoind/progress/follow-scene.js +7 -1
- package/dist/cli/context.d.ts +0 -1
- package/dist/cli/context.js +2 -23
- package/dist/package-version.d.ts +1 -0
- package/dist/package-version.js +17 -0
- package/dist/wallet/mining/control.js +18 -3
- package/dist/wallet/mining/runner.d.ts +116 -1
- package/dist/wallet/mining/runner.js +372 -110
- package/dist/wallet/mining/sentences.js +52 -7
- package/dist/wallet/mining/visualizer.d.ts +1 -0
- package/dist/wallet/mining/visualizer.js +13 -0
- package/dist/wallet/read/context.js +5 -1
- package/package.json +1 -1
|
@@ -317,6 +317,6 @@ export async function syncToTip(dependencies) {
|
|
|
317
317
|
lastError: message,
|
|
318
318
|
message: "Managed sync can be resumed after the last error.",
|
|
319
319
|
});
|
|
320
|
-
throw new Error(message);
|
|
320
|
+
throw new Error(message, { cause: error instanceof Error ? error : undefined });
|
|
321
321
|
}
|
|
322
322
|
}
|
|
@@ -5,6 +5,7 @@ import { loadBundledGenesisParameters, serializeIndexerState } from "@cogcoin/in
|
|
|
5
5
|
import { openManagedBitcoindClientInternal } from "./client.js";
|
|
6
6
|
import { DEFAULT_SNAPSHOT_METADATA } from "./bootstrap.js";
|
|
7
7
|
import { openClient } from "../client.js";
|
|
8
|
+
import { readPackageVersionFromDisk } from "../package-version.js";
|
|
8
9
|
import { openSqliteStore } from "../sqlite/index.js";
|
|
9
10
|
import { writeRuntimeStatusFile } from "../wallet/fs/status-file.js";
|
|
10
11
|
import { createRpcClient } from "./node.js";
|
|
@@ -55,16 +56,6 @@ async function withTimeout(promise, timeoutMs, errorCode) {
|
|
|
55
56
|
}
|
|
56
57
|
}
|
|
57
58
|
}
|
|
58
|
-
async function readPackageVersionFromDisk() {
|
|
59
|
-
try {
|
|
60
|
-
const raw = await readFile(new URL("../../package.json", import.meta.url), "utf8");
|
|
61
|
-
const parsed = JSON.parse(raw);
|
|
62
|
-
return parsed.version ?? "0.0.0";
|
|
63
|
-
}
|
|
64
|
-
catch {
|
|
65
|
-
return "0.0.0";
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
59
|
function createSnapshotKey(appliedTip) {
|
|
69
60
|
return appliedTip === null
|
|
70
61
|
? "__null__"
|
|
@@ -177,7 +168,7 @@ async function main() {
|
|
|
177
168
|
const walletRootId = parseArg("wallet-root-id") || UNINITIALIZED_WALLET_ROOT_ID;
|
|
178
169
|
const paths = resolveManagedServicePaths(dataDir, walletRootId);
|
|
179
170
|
const daemonInstanceId = randomUUID();
|
|
180
|
-
const binaryVersion = await readPackageVersionFromDisk();
|
|
171
|
+
const binaryVersion = await readPackageVersionFromDisk().catch(() => "0.0.0");
|
|
181
172
|
const genesisParameters = await loadBundledGenesisParameters();
|
|
182
173
|
const startedAtUnixMs = Date.now();
|
|
183
174
|
const snapshots = new Map();
|
|
@@ -3,12 +3,14 @@ import { spawn } from "node:child_process";
|
|
|
3
3
|
import { mkdir, readFile, rm } from "node:fs/promises";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
5
|
import net from "node:net";
|
|
6
|
-
import { compareSemver } from "../semver.js";
|
|
6
|
+
import { compareSemver, parseSemver } from "../semver.js";
|
|
7
7
|
import { acquireFileLock, FileLockBusyError } from "../wallet/fs/lock.js";
|
|
8
8
|
import { writeRuntimeStatusFile } from "../wallet/fs/status-file.js";
|
|
9
9
|
import { INDEXER_DAEMON_SCHEMA_VERSION, INDEXER_DAEMON_SERVICE_API_VERSION, } from "./types.js";
|
|
10
10
|
import { resolveManagedServicePaths, UNINITIALIZED_WALLET_ROOT_ID } from "./service-paths.js";
|
|
11
11
|
const DEFAULT_STARTUP_TIMEOUT_MS = 30_000;
|
|
12
|
+
const DEFAULT_SHUTDOWN_TIMEOUT_MS = 5_000;
|
|
13
|
+
const FORCE_KILL_TIMEOUT_MS = 5_000;
|
|
12
14
|
const INDEXER_DAEMON_REQUEST_TIMEOUT_MS = 15_000;
|
|
13
15
|
const INDEXER_DAEMON_RESUME_BACKGROUND_FOLLOW_REQUEST_TIMEOUT_MS = 35_000;
|
|
14
16
|
const INDEXER_DAEMON_BACKGROUND_FOLLOW_NOT_ACTIVE = "indexer_daemon_background_follow_not_active";
|
|
@@ -58,6 +60,11 @@ async function clearIndexerDaemonRuntimeArtifacts(paths) {
|
|
|
58
60
|
await rm(paths.indexerDaemonStatusPath, { force: true }).catch(() => undefined);
|
|
59
61
|
await rm(paths.indexerDaemonSocketPath, { force: true }).catch(() => undefined);
|
|
60
62
|
}
|
|
63
|
+
function ignoreProcessNotFound(error) {
|
|
64
|
+
if (!(error instanceof Error && "code" in error && error.code === "ESRCH")) {
|
|
65
|
+
throw error;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
61
68
|
export async function stopIndexerDaemonServiceWithLockHeld(options) {
|
|
62
69
|
const walletRootId = options.walletRootId ?? UNINITIALIZED_WALLET_ROOT_ID;
|
|
63
70
|
const paths = options.paths ?? resolveManagedServicePaths(options.dataDir, walletRootId);
|
|
@@ -74,11 +81,23 @@ export async function stopIndexerDaemonServiceWithLockHeld(options) {
|
|
|
74
81
|
process.kill(processId, "SIGTERM");
|
|
75
82
|
}
|
|
76
83
|
catch (error) {
|
|
77
|
-
|
|
84
|
+
ignoreProcessNotFound(error);
|
|
85
|
+
}
|
|
86
|
+
try {
|
|
87
|
+
await waitForProcessExit(processId, options.shutdownTimeoutMs ?? DEFAULT_SHUTDOWN_TIMEOUT_MS, "indexer_daemon_stop_timeout");
|
|
88
|
+
}
|
|
89
|
+
catch (error) {
|
|
90
|
+
if (!(error instanceof Error) || error.message !== "indexer_daemon_stop_timeout") {
|
|
78
91
|
throw error;
|
|
79
92
|
}
|
|
93
|
+
try {
|
|
94
|
+
process.kill(processId, "SIGKILL");
|
|
95
|
+
}
|
|
96
|
+
catch (killError) {
|
|
97
|
+
ignoreProcessNotFound(killError);
|
|
98
|
+
}
|
|
99
|
+
await waitForProcessExit(processId, FORCE_KILL_TIMEOUT_MS, "indexer_daemon_stop_timeout");
|
|
80
100
|
}
|
|
81
|
-
await waitForProcessExit(processId, options.shutdownTimeoutMs ?? 5_000, "indexer_daemon_stop_timeout");
|
|
82
101
|
await clearIndexerDaemonRuntimeArtifacts(paths);
|
|
83
102
|
return {
|
|
84
103
|
status: "stopped",
|
|
@@ -283,8 +302,11 @@ function isStaleIndexerDaemonVersion(status, expectedBinaryVersion) {
|
|
|
283
302
|
if (status === null || expectedBinaryVersion === null || expectedBinaryVersion === undefined) {
|
|
284
303
|
return false;
|
|
285
304
|
}
|
|
305
|
+
if (parseSemver(expectedBinaryVersion) === null) {
|
|
306
|
+
return false;
|
|
307
|
+
}
|
|
286
308
|
const comparison = compareSemver(status.binaryVersion, expectedBinaryVersion);
|
|
287
|
-
return comparison
|
|
309
|
+
return comparison === null || comparison < 0;
|
|
288
310
|
}
|
|
289
311
|
async function probeIndexerDaemonAtSocket(socketPath, expectedWalletRootId) {
|
|
290
312
|
const client = createIndexerDaemonClient(socketPath);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { readFile } from "node:fs/promises";
|
|
2
2
|
import { loadBundledGenesisParameters } from "@cogcoin/indexer";
|
|
3
|
+
import { readPackageVersionFromDisk } from "../package-version.js";
|
|
3
4
|
import { attachOrStartIndexerDaemon, readObservedIndexerDaemonStatus, } from "./indexer-daemon.js";
|
|
4
5
|
import { resolveCogcoinProcessingStartHeight } from "./processing-start-height.js";
|
|
5
6
|
import { resolveManagedServicePaths, UNINITIALIZED_WALLET_ROOT_ID } from "./service-paths.js";
|
|
@@ -31,6 +32,9 @@ async function resolveStartOptions(options) {
|
|
|
31
32
|
};
|
|
32
33
|
}
|
|
33
34
|
export async function openManagedIndexerMonitor(options) {
|
|
35
|
+
const expectedBinaryVersion = options.expectedBinaryVersion === undefined
|
|
36
|
+
? await readPackageVersionFromDisk()
|
|
37
|
+
: options.expectedBinaryVersion;
|
|
34
38
|
const walletRootId = options.walletRootId ?? UNINITIALIZED_WALLET_ROOT_ID;
|
|
35
39
|
const startOptions = await resolveStartOptions({
|
|
36
40
|
dataDir: options.dataDir,
|
|
@@ -49,7 +53,7 @@ export async function openManagedIndexerMonitor(options) {
|
|
|
49
53
|
walletRootId,
|
|
50
54
|
startupTimeoutMs: options.startupTimeoutMs,
|
|
51
55
|
ensureBackgroundFollow: true,
|
|
52
|
-
expectedBinaryVersion
|
|
56
|
+
expectedBinaryVersion,
|
|
53
57
|
});
|
|
54
58
|
return createManagedIndexerMonitor({
|
|
55
59
|
daemon,
|
|
@@ -128,8 +128,11 @@ export class ManagedProgressController {
|
|
|
128
128
|
this.#cogcoinSyncTargetHeight = null;
|
|
129
129
|
}
|
|
130
130
|
if (this.#followVisualMode) {
|
|
131
|
+
const followIndexedHeight = phase === "follow_tip"
|
|
132
|
+
? this.#cogcoinSyncHeight ?? this.#progress.blocks ?? this.#followScene.indexedHeight
|
|
133
|
+
: undefined;
|
|
131
134
|
syncFollowSceneState(this.#followScene, {
|
|
132
|
-
indexedHeight:
|
|
135
|
+
indexedHeight: followIndexedHeight,
|
|
133
136
|
nodeHeight: this.#progress.blocks,
|
|
134
137
|
liveActivated: phase === "follow_tip" || this.#followScene.liveActivated,
|
|
135
138
|
});
|
|
@@ -110,7 +110,13 @@ function renderFollowStatusField(statusFieldText, options) {
|
|
|
110
110
|
return field;
|
|
111
111
|
}
|
|
112
112
|
function highestTrackedFollowHeight(state) {
|
|
113
|
-
|
|
113
|
+
let highest = Math.max(state.indexedHeight ?? Number.NEGATIVE_INFINITY, state.displayedCenterHeight ?? Number.NEGATIVE_INFINITY, state.animation?.height ?? Number.NEGATIVE_INFINITY);
|
|
114
|
+
for (const height of state.queuedHeights) {
|
|
115
|
+
if (height > highest) {
|
|
116
|
+
highest = height;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return highest;
|
|
114
120
|
}
|
|
115
121
|
function resolveLatestAuthoritativeFollowHeight(indexedHeight, nodeHeight) {
|
|
116
122
|
if (indexedHeight === null) {
|
package/dist/cli/context.d.ts
CHANGED
package/dist/cli/context.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
|
-
import { mkdir
|
|
2
|
+
import { mkdir } from "node:fs/promises";
|
|
3
3
|
import { attachOrStartIndexerDaemon, probeIndexerDaemon, readObservedIndexerDaemonStatus, stopIndexerDaemonService, } from "../bitcoind/indexer-daemon.js";
|
|
4
4
|
import { createRpcClient } from "../bitcoind/node.js";
|
|
5
5
|
import { attachOrStartManagedBitcoindService, probeManagedBitcoindService, stopManagedBitcoindService, } from "../bitcoind/service.js";
|
|
6
6
|
import { resolveDefaultBitcoindDataDirForTesting, resolveDefaultClientDatabasePathForTesting, resolveDefaultUpdateCheckStatePathForTesting, } from "../app-paths.js";
|
|
7
7
|
import { openManagedBitcoindClient } from "../bitcoind/index.js";
|
|
8
8
|
import { openManagedIndexerMonitor } from "../bitcoind/indexer-monitor.js";
|
|
9
|
+
import { readPackageVersionFromDisk } from "../package-version.js";
|
|
9
10
|
import { inspectPassiveClientStatus } from "../passive-status.js";
|
|
10
11
|
import { openSqliteStore } from "../sqlite/index.js";
|
|
11
12
|
import { initializeWallet, deleteImportedWalletSeed, previewResetWallet, repairWallet, resetWallet, restoreWalletFromMnemonic, showWalletMnemonic, } from "../wallet/lifecycle.js";
|
|
@@ -16,28 +17,6 @@ import { ensureBuiltInMiningSetupIfNeeded, followMiningLog, inspectMiningControl
|
|
|
16
17
|
import { createLazyDefaultWalletSecretProvider } from "../wallet/state/provider.js";
|
|
17
18
|
import { anchorDomain, transferBitcoin, buyDomain, claimCogLock, clearDomainDelegate, clearDomainEndpoint, clearDomainMiner, clearField, createField, giveReputation, lockCogToDomain, registerDomain, reclaimCogLock, revokeReputation, sendCog, setField, setDomainCanonical, setDomainDelegate, setDomainEndpoint, setDomainMiner, sellDomain, transferDomain, } from "../wallet/tx/index.js";
|
|
18
19
|
import { createTerminalPrompter } from "./prompt.js";
|
|
19
|
-
export async function readPackageVersionFromDisk() {
|
|
20
|
-
const packageUrls = [
|
|
21
|
-
new URL("../../package.json", import.meta.url),
|
|
22
|
-
new URL("../../../package.json", import.meta.url),
|
|
23
|
-
];
|
|
24
|
-
for (const packageUrl of packageUrls) {
|
|
25
|
-
try {
|
|
26
|
-
const raw = await readFile(packageUrl, "utf8");
|
|
27
|
-
const parsed = JSON.parse(raw);
|
|
28
|
-
return parsed.version ?? "0.0.0";
|
|
29
|
-
}
|
|
30
|
-
catch (error) {
|
|
31
|
-
const code = typeof error === "object" && error !== null && "code" in error
|
|
32
|
-
? String(error.code)
|
|
33
|
-
: null;
|
|
34
|
-
if (code !== "ENOENT") {
|
|
35
|
-
throw error;
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
return "0.0.0";
|
|
40
|
-
}
|
|
41
20
|
async function runGlobalClientUpdateInstall(options) {
|
|
42
21
|
const binary = process.platform === "win32" ? "npm.cmd" : "npm";
|
|
43
22
|
await new Promise((resolve, reject) => {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function readPackageVersionFromDisk(): Promise<string>;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
export async function readPackageVersionFromDisk() {
|
|
3
|
+
try {
|
|
4
|
+
const raw = await readFile(new URL("../package.json", import.meta.url), "utf8");
|
|
5
|
+
const parsed = JSON.parse(raw);
|
|
6
|
+
return parsed.version ?? "0.0.0";
|
|
7
|
+
}
|
|
8
|
+
catch (error) {
|
|
9
|
+
const code = typeof error === "object" && error !== null && "code" in error
|
|
10
|
+
? String(error.code)
|
|
11
|
+
: null;
|
|
12
|
+
if (code === "ENOENT") {
|
|
13
|
+
return "0.0.0";
|
|
14
|
+
}
|
|
15
|
+
throw error;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -105,7 +105,9 @@ function mapProviderState(provider, localState, existingRuntime) {
|
|
|
105
105
|
const miningState = localState.state?.miningState === undefined
|
|
106
106
|
? null
|
|
107
107
|
: normalizeMiningStateRecord(localState.state.miningState);
|
|
108
|
-
if (existingRuntime?.currentPhase === "waiting-provider"
|
|
108
|
+
if (existingRuntime?.currentPhase === "waiting-provider"
|
|
109
|
+
&& existingRuntime.providerState !== null
|
|
110
|
+
&& (miningState === null || miningState.state === "idle")) {
|
|
109
111
|
return existingRuntime.providerState;
|
|
110
112
|
}
|
|
111
113
|
if (miningState?.state === "paused" && miningState.pauseReason?.includes("rate-limit")) {
|
|
@@ -122,6 +124,11 @@ function mapProviderState(provider, localState, existingRuntime) {
|
|
|
122
124
|
}
|
|
123
125
|
return "unavailable";
|
|
124
126
|
}
|
|
127
|
+
function shouldReuseExistingProviderWait(options) {
|
|
128
|
+
return options.existingRuntime?.currentPhase === "waiting-provider"
|
|
129
|
+
&& options.existingRuntime.providerState !== null
|
|
130
|
+
&& (options.miningState === null || options.miningState.state === "idle");
|
|
131
|
+
}
|
|
125
132
|
function mapIndexerDaemonState(indexer) {
|
|
126
133
|
if (indexer.health === "wallet-root-mismatch") {
|
|
127
134
|
return "wallet-root-mismatch";
|
|
@@ -214,6 +221,10 @@ async function buildMiningRuntimeSnapshot(options) {
|
|
|
214
221
|
const indexerDaemonState = mapIndexerDaemonState(options.indexer);
|
|
215
222
|
const corePublishState = mapCorePublishState(options.nodeHealth, options.nodeStatus);
|
|
216
223
|
const existing = options.existingRuntime;
|
|
224
|
+
const reuseExistingProviderWait = shouldReuseExistingProviderWait({
|
|
225
|
+
existingRuntime: existing,
|
|
226
|
+
miningState: state,
|
|
227
|
+
});
|
|
217
228
|
return {
|
|
218
229
|
schemaVersion: 1,
|
|
219
230
|
walletRootId: options.localState.walletRootId,
|
|
@@ -278,12 +289,16 @@ async function buildMiningRuntimeSnapshot(options) {
|
|
|
278
289
|
indexerHealth: options.indexer.health,
|
|
279
290
|
tipsAligned: options.tipsAligned,
|
|
280
291
|
lastEventAtUnixMs: options.lastEventAtUnixMs,
|
|
281
|
-
lastError:
|
|
292
|
+
lastError: reuseExistingProviderWait
|
|
293
|
+
? existing?.lastError ?? null
|
|
294
|
+
: existing?.currentPhase === "waiting-bitcoin-network" || existing?.currentPhase === "waiting-indexer"
|
|
295
|
+
? existing?.lastError ?? options.provider.message ?? options.indexer.message ?? null
|
|
296
|
+
: options.provider.message ?? options.indexer.message ?? null,
|
|
282
297
|
note: state?.pauseReason === "zero-reward"
|
|
283
298
|
? "Mining is disabled because the target block reward is zero."
|
|
284
299
|
: existing?.currentPhase === "resuming"
|
|
285
300
|
? "Mining discarded stale in-flight work after a large local runtime gap and is rechecking health."
|
|
286
|
-
:
|
|
301
|
+
: reuseExistingProviderWait
|
|
287
302
|
? "Mining is waiting for the sentence provider to recover."
|
|
288
303
|
: existing?.currentPhase === "waiting-indexer"
|
|
289
304
|
? "Mining is waiting for Bitcoin Core and the indexer to align."
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
|
+
import { assaySentences } from "@cogcoin/scoring";
|
|
2
3
|
import { attachOrStartManagedBitcoindService, probeManagedBitcoindService, stopManagedBitcoindService } from "../../bitcoind/service.js";
|
|
3
4
|
import { createRpcClient } from "../../bitcoind/node.js";
|
|
4
5
|
import type { ProgressOutputMode } from "../../bitcoind/types.js";
|
|
@@ -158,6 +159,18 @@ type MiningPublishOutcome = ({
|
|
|
158
159
|
note?: null;
|
|
159
160
|
candidate: MiningCandidate;
|
|
160
161
|
} & Awaited<ReturnType<typeof publishCandidateOnce>>) | MiningPublishSkipResult | MiningPublishRetryResult;
|
|
162
|
+
interface CompetitivenessDecision {
|
|
163
|
+
allowed: boolean;
|
|
164
|
+
decision: string;
|
|
165
|
+
sameDomainCompetitorSuppressed: boolean;
|
|
166
|
+
higherRankedCompetitorDomainCount: number;
|
|
167
|
+
dedupedCompetitorDomainCount: number;
|
|
168
|
+
competitivenessGateIndeterminate: boolean;
|
|
169
|
+
mempoolSequenceCacheStatus: MiningRuntimeStatusV1["mempoolSequenceCacheStatus"];
|
|
170
|
+
lastMempoolSequence: string | null;
|
|
171
|
+
visibleBoardEntries: MiningSentenceBoardEntry[];
|
|
172
|
+
candidateRank: number | null;
|
|
173
|
+
}
|
|
161
174
|
interface RunnerDependencies {
|
|
162
175
|
openReadContext?: typeof openWalletReadContext;
|
|
163
176
|
attachService?: typeof attachOrStartManagedBitcoindService;
|
|
@@ -171,6 +184,11 @@ interface RunnerDependencies {
|
|
|
171
184
|
shutdownGraceMs?: number;
|
|
172
185
|
sleepImpl?: typeof sleep;
|
|
173
186
|
}
|
|
187
|
+
interface IndexerTruthKey {
|
|
188
|
+
walletRootId: string;
|
|
189
|
+
daemonInstanceId: string;
|
|
190
|
+
snapshotSeq: string;
|
|
191
|
+
}
|
|
174
192
|
interface MiningLoopState {
|
|
175
193
|
attemptedTipKey: string | null;
|
|
176
194
|
currentTipKey: string | null;
|
|
@@ -178,6 +196,10 @@ interface MiningLoopState {
|
|
|
178
196
|
selectedCandidate: MiningCandidate | null;
|
|
179
197
|
ui: MiningFollowVisualizerState;
|
|
180
198
|
waitingNote: string | null;
|
|
199
|
+
providerWaitState: "backoff" | "rate-limited" | "auth-error" | "not-found" | null;
|
|
200
|
+
providerWaitLastError: string | null;
|
|
201
|
+
providerWaitNextRetryAtUnixMs: number | null;
|
|
202
|
+
providerTransientFailureCount: number;
|
|
181
203
|
bitcoinRecoveryFirstFailureAtUnixMs: number | null;
|
|
182
204
|
bitcoinRecoveryFirstUnreachableAtUnixMs: number | null;
|
|
183
205
|
bitcoinRecoveryLastRestartAttemptAtUnixMs: number | null;
|
|
@@ -186,6 +208,20 @@ interface MiningLoopState {
|
|
|
186
208
|
reconnectSettledUntilUnixMs: number | null;
|
|
187
209
|
tipSettledUntilUnixMs: number | null;
|
|
188
210
|
}
|
|
211
|
+
interface MiningSuspendDetector {
|
|
212
|
+
lastHeartbeatMonotonicMs: number;
|
|
213
|
+
detectedAtUnixMs: number | null;
|
|
214
|
+
monotonicNow: () => number;
|
|
215
|
+
nowUnixMs: () => number;
|
|
216
|
+
stop(): void;
|
|
217
|
+
}
|
|
218
|
+
interface MiningSuspendHeartbeatHandle {
|
|
219
|
+
clear(): void;
|
|
220
|
+
}
|
|
221
|
+
interface MiningSuspendScheduler {
|
|
222
|
+
every(intervalMs: number, callback: () => void): MiningSuspendHeartbeatHandle;
|
|
223
|
+
}
|
|
224
|
+
type MiningCooperativeYield = () => Promise<void>;
|
|
189
225
|
export interface RunForegroundMiningOptions extends RunnerDependencies {
|
|
190
226
|
dataDir: string;
|
|
191
227
|
databasePath: string;
|
|
@@ -243,7 +279,7 @@ export declare function resolveSettledBoardForTesting(options: {
|
|
|
243
279
|
settledBoardEntries: MiningSentenceBoardEntry[];
|
|
244
280
|
};
|
|
245
281
|
export declare function getSelectedCandidateForTipForTesting(loopState: MiningLoopState, tipKey: string | null): MiningCandidate | null;
|
|
246
|
-
export declare function cacheSelectedCandidateForTipForTesting(loopState: MiningLoopState, tipKey: string | null, candidate: MiningCandidate): void;
|
|
282
|
+
export declare function cacheSelectedCandidateForTipForTesting(loopState: MiningLoopState, tipKey: string | null, candidate: MiningCandidate, liveState?: MiningStateRecord | null): void;
|
|
247
283
|
export declare function resolveFundingDisplaySatsForTesting(state: WalletStateV1, rpc: MiningRpcClient): Promise<bigint>;
|
|
248
284
|
export declare function loadMiningVisibleFollowBlockTimesForTesting(options: {
|
|
249
285
|
rpc: MiningRpcClient;
|
|
@@ -302,6 +338,12 @@ export declare function createMiningPlanForTesting(options: {
|
|
|
302
338
|
feeRateSatVb: number;
|
|
303
339
|
};
|
|
304
340
|
export declare function validateMiningDraftForTesting(decoded: Awaited<ReturnType<MiningRpcClient["decodePsbt"]>>, funded: Awaited<ReturnType<MiningRpcClient["walletCreateFundedPsbt"]>>, plan: ReturnType<typeof createMiningPlan>): void;
|
|
341
|
+
declare function resolveEligibleAnchoredRoots(context: WalletReadContext): Array<{
|
|
342
|
+
domainId: number;
|
|
343
|
+
domainName: string;
|
|
344
|
+
localIndex: number;
|
|
345
|
+
sender: MutationSender;
|
|
346
|
+
}>;
|
|
305
347
|
export declare function refreshMiningCandidateFromCurrentStateForTesting(context: ReadyMiningReadContext, candidate: MiningCandidate): MiningCandidate | null;
|
|
306
348
|
export declare function resolveMiningConflictOutpointForTesting(options: {
|
|
307
349
|
state: WalletStateV1;
|
|
@@ -320,6 +362,34 @@ export declare function buildMiningGenerationRequestForTesting(options: {
|
|
|
320
362
|
domainExtraPrompts?: Record<string, string>;
|
|
321
363
|
extraPrompt?: string | null;
|
|
322
364
|
}): MiningSentenceGenerationRequest;
|
|
365
|
+
declare function generateCandidatesForDomains(options: {
|
|
366
|
+
rpc: MiningRpcClient;
|
|
367
|
+
readContext: WalletReadContext & {
|
|
368
|
+
localState: {
|
|
369
|
+
availability: "ready";
|
|
370
|
+
state: WalletStateV1;
|
|
371
|
+
};
|
|
372
|
+
snapshot: NonNullable<WalletReadContext["snapshot"]>;
|
|
373
|
+
model: NonNullable<WalletReadContext["model"]>;
|
|
374
|
+
};
|
|
375
|
+
domains: ReturnType<typeof resolveEligibleAnchoredRoots>;
|
|
376
|
+
provider: WalletSecretProvider;
|
|
377
|
+
paths: WalletRuntimePaths;
|
|
378
|
+
indexerTruthKey: IndexerTruthKey | null;
|
|
379
|
+
runId?: string | null;
|
|
380
|
+
fetchImpl?: typeof fetch;
|
|
381
|
+
}): Promise<MiningCandidate[]>;
|
|
382
|
+
declare function runCompetitivenessGate(options: {
|
|
383
|
+
rpc: MiningRpcClient;
|
|
384
|
+
readContext: WalletReadContext & {
|
|
385
|
+
snapshot: NonNullable<WalletReadContext["snapshot"]>;
|
|
386
|
+
};
|
|
387
|
+
candidate: MiningCandidate;
|
|
388
|
+
currentTxid: string | null;
|
|
389
|
+
assaySentencesImpl?: typeof assaySentences;
|
|
390
|
+
cooperativeYield?: MiningCooperativeYield;
|
|
391
|
+
cooperativeYieldEvery?: number;
|
|
392
|
+
}): Promise<CompetitivenessDecision>;
|
|
323
393
|
declare function publishCandidateOnce(options: {
|
|
324
394
|
readContext: WalletReadContext & {
|
|
325
395
|
localState: {
|
|
@@ -388,9 +458,17 @@ declare function runMiningLoop(options: {
|
|
|
388
458
|
stdout?: {
|
|
389
459
|
write(chunk: string): void;
|
|
390
460
|
};
|
|
461
|
+
loopState?: MiningLoopState;
|
|
391
462
|
visualizer?: MiningFollowVisualizer;
|
|
392
463
|
nowImpl?: () => number;
|
|
393
464
|
sleepImpl?: typeof sleep;
|
|
465
|
+
suspendMonotonicNowImpl?: () => number;
|
|
466
|
+
suspendScheduler?: MiningSuspendScheduler;
|
|
467
|
+
generateCandidatesForDomainsImpl?: typeof generateCandidatesForDomains;
|
|
468
|
+
runCompetitivenessGateImpl?: typeof runCompetitivenessGate;
|
|
469
|
+
assaySentencesImpl?: typeof assaySentences;
|
|
470
|
+
cooperativeYieldImpl?: MiningCooperativeYield;
|
|
471
|
+
cooperativeYieldEvery?: number;
|
|
394
472
|
}): Promise<void>;
|
|
395
473
|
declare function waitForBackgroundHealthy(paths: WalletRuntimePaths): Promise<MiningRuntimeStatusV1 | null>;
|
|
396
474
|
export declare function runForegroundMining(options: RunForegroundMiningOptions): Promise<void>;
|
|
@@ -445,6 +523,11 @@ export declare function performMiningCycleForTesting(options: {
|
|
|
445
523
|
};
|
|
446
524
|
loopState?: MiningLoopState;
|
|
447
525
|
nowImpl?: () => number;
|
|
526
|
+
generateCandidatesForDomainsImpl?: typeof generateCandidatesForDomains;
|
|
527
|
+
runCompetitivenessGateImpl?: typeof runCompetitivenessGate;
|
|
528
|
+
assaySentencesImpl?: typeof assaySentences;
|
|
529
|
+
cooperativeYieldImpl?: MiningCooperativeYield;
|
|
530
|
+
cooperativeYieldEvery?: number;
|
|
448
531
|
}): Promise<void>;
|
|
449
532
|
export declare function runMiningLoopForTesting(options: {
|
|
450
533
|
dataDir: string;
|
|
@@ -464,10 +547,42 @@ export declare function runMiningLoopForTesting(options: {
|
|
|
464
547
|
stdout?: {
|
|
465
548
|
write(chunk: string): void;
|
|
466
549
|
};
|
|
550
|
+
loopState?: MiningLoopState;
|
|
467
551
|
visualizer?: MiningFollowVisualizer;
|
|
468
552
|
nowImpl?: () => number;
|
|
469
553
|
sleepImpl?: typeof sleep;
|
|
554
|
+
suspendMonotonicNowImpl?: () => number;
|
|
555
|
+
suspendScheduler?: MiningSuspendScheduler;
|
|
556
|
+
generateCandidatesForDomainsImpl?: typeof generateCandidatesForDomains;
|
|
557
|
+
runCompetitivenessGateImpl?: typeof runCompetitivenessGate;
|
|
558
|
+
assaySentencesImpl?: typeof assaySentences;
|
|
559
|
+
cooperativeYieldImpl?: MiningCooperativeYield;
|
|
560
|
+
cooperativeYieldEvery?: number;
|
|
470
561
|
}): Promise<void>;
|
|
562
|
+
export declare function runCompetitivenessGateForTesting(options: {
|
|
563
|
+
rpc: MiningRpcClient;
|
|
564
|
+
readContext: WalletReadContext & {
|
|
565
|
+
snapshot: NonNullable<WalletReadContext["snapshot"]>;
|
|
566
|
+
};
|
|
567
|
+
candidate: MiningCandidate;
|
|
568
|
+
currentTxid: string | null;
|
|
569
|
+
assaySentencesImpl?: typeof assaySentences;
|
|
570
|
+
cooperativeYieldImpl?: MiningCooperativeYield;
|
|
571
|
+
cooperativeYieldEvery?: number;
|
|
572
|
+
}): Promise<CompetitivenessDecision>;
|
|
573
|
+
export declare function createMiningSuspendDetectorForTesting(options?: {
|
|
574
|
+
monotonicNow?: () => number;
|
|
575
|
+
nowUnixMs?: () => number;
|
|
576
|
+
scheduler?: MiningSuspendScheduler;
|
|
577
|
+
}): MiningSuspendDetector;
|
|
578
|
+
export declare function throwIfMiningSuspendDetectedForTesting(detector: MiningSuspendDetector): void;
|
|
579
|
+
export declare function topologicallyOrderAncestorTxidsForTesting(options: {
|
|
580
|
+
txid: string;
|
|
581
|
+
txContexts: Map<string, {
|
|
582
|
+
txid: string;
|
|
583
|
+
rawTransaction: Awaited<ReturnType<MiningRpcClient["getRawTransaction"]>>;
|
|
584
|
+
}>;
|
|
585
|
+
}): string[] | null;
|
|
471
586
|
export declare function buildPrePublishStatusOverridesForTesting(options: {
|
|
472
587
|
state: WalletStateV1;
|
|
473
588
|
candidate: MiningCandidate;
|