@cogcoin/client 1.1.1 → 1.1.3
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/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 +117 -12
- package/dist/wallet/mining/visualizer.d.ts +1 -0
- package/dist/wallet/mining/visualizer.js +13 -0
- package/package.json +1 -1
|
@@ -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;
|