@cogcoin/client 1.1.3 → 1.1.5

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.
Files changed (88) hide show
  1. package/README.md +4 -5
  2. package/dist/bitcoind/node.js +2 -1
  3. package/dist/bitcoind/progress/tty-renderer.js +3 -2
  4. package/dist/bitcoind/service.js +6 -24
  5. package/dist/bitcoind/types.d.ts +1 -0
  6. package/dist/bitcoind/types.js +1 -0
  7. package/dist/cli/command-registry.d.ts +39 -0
  8. package/dist/cli/command-registry.js +1132 -0
  9. package/dist/cli/commands/client-admin.js +6 -56
  10. package/dist/cli/commands/mining-admin.js +9 -32
  11. package/dist/cli/commands/mining-read.js +15 -56
  12. package/dist/cli/commands/mining-runtime.js +258 -57
  13. package/dist/cli/commands/service-runtime.js +1 -64
  14. package/dist/cli/commands/status.js +2 -15
  15. package/dist/cli/commands/update.js +6 -21
  16. package/dist/cli/commands/wallet-admin.js +18 -120
  17. package/dist/cli/commands/wallet-mutation.js +4 -7
  18. package/dist/cli/commands/wallet-read.js +31 -138
  19. package/dist/cli/context.js +2 -4
  20. package/dist/cli/mining-format.js +8 -2
  21. package/dist/cli/mutation-command-groups.d.ts +11 -11
  22. package/dist/cli/mutation-command-groups.js +9 -18
  23. package/dist/cli/mutation-json.d.ts +1 -17
  24. package/dist/cli/mutation-json.js +1 -28
  25. package/dist/cli/mutation-success.d.ts +0 -1
  26. package/dist/cli/mutation-success.js +0 -19
  27. package/dist/cli/output.d.ts +1 -10
  28. package/dist/cli/output.js +52 -481
  29. package/dist/cli/parse.d.ts +1 -1
  30. package/dist/cli/parse.js +38 -695
  31. package/dist/cli/runner.js +28 -113
  32. package/dist/cli/types.d.ts +7 -8
  33. package/dist/cli/update-notifier.js +1 -1
  34. package/dist/cli/wallet-format.js +1 -1
  35. package/dist/wallet/lifecycle/managed-core.d.ts +23 -0
  36. package/dist/wallet/lifecycle/managed-core.js +257 -0
  37. package/dist/wallet/lifecycle/repair-mining.d.ts +49 -0
  38. package/dist/wallet/lifecycle/repair-mining.js +304 -0
  39. package/dist/wallet/lifecycle/repair-runtime.d.ts +36 -0
  40. package/dist/wallet/lifecycle/repair-runtime.js +206 -0
  41. package/dist/wallet/lifecycle/repair.d.ts +11 -0
  42. package/dist/wallet/lifecycle/repair.js +368 -0
  43. package/dist/wallet/lifecycle/setup.d.ts +16 -0
  44. package/dist/wallet/lifecycle/setup.js +430 -0
  45. package/dist/wallet/lifecycle/types.d.ts +125 -0
  46. package/dist/wallet/lifecycle/types.js +1 -0
  47. package/dist/wallet/lifecycle.d.ts +4 -165
  48. package/dist/wallet/lifecycle.js +3 -1656
  49. package/dist/wallet/mining/candidate.d.ts +60 -0
  50. package/dist/wallet/mining/candidate.js +290 -0
  51. package/dist/wallet/mining/competitiveness.d.ts +22 -0
  52. package/dist/wallet/mining/competitiveness.js +640 -0
  53. package/dist/wallet/mining/control.js +7 -251
  54. package/dist/wallet/mining/cycle.d.ts +39 -0
  55. package/dist/wallet/mining/cycle.js +542 -0
  56. package/dist/wallet/mining/engine-state.d.ts +66 -0
  57. package/dist/wallet/mining/engine-state.js +211 -0
  58. package/dist/wallet/mining/engine-types.d.ts +173 -0
  59. package/dist/wallet/mining/engine-types.js +1 -0
  60. package/dist/wallet/mining/engine-utils.d.ts +7 -0
  61. package/dist/wallet/mining/engine-utils.js +75 -0
  62. package/dist/wallet/mining/events.d.ts +2 -0
  63. package/dist/wallet/mining/events.js +19 -0
  64. package/dist/wallet/mining/lifecycle.d.ts +71 -0
  65. package/dist/wallet/mining/lifecycle.js +355 -0
  66. package/dist/wallet/mining/projection.d.ts +61 -0
  67. package/dist/wallet/mining/projection.js +319 -0
  68. package/dist/wallet/mining/publish.d.ts +79 -0
  69. package/dist/wallet/mining/publish.js +614 -0
  70. package/dist/wallet/mining/runner.d.ts +12 -418
  71. package/dist/wallet/mining/runner.js +274 -3433
  72. package/dist/wallet/mining/supervisor.d.ts +134 -0
  73. package/dist/wallet/mining/supervisor.js +558 -0
  74. package/dist/wallet/mining/visualizer-sync.d.ts +42 -0
  75. package/dist/wallet/mining/visualizer-sync.js +166 -0
  76. package/dist/wallet/mining/visualizer.d.ts +1 -0
  77. package/dist/wallet/mining/visualizer.js +33 -18
  78. package/dist/wallet/read/context.d.ts +5 -1
  79. package/dist/wallet/read/context.js +19 -4
  80. package/dist/wallet/reset.d.ts +1 -1
  81. package/dist/wallet/reset.js +35 -11
  82. package/dist/wallet/runtime.d.ts +0 -6
  83. package/dist/wallet/runtime.js +2 -38
  84. package/dist/wallet/tx/common.d.ts +18 -0
  85. package/dist/wallet/tx/common.js +40 -26
  86. package/package.json +1 -1
  87. package/dist/wallet/state/seed-index.d.ts +0 -43
  88. package/dist/wallet/state/seed-index.js +0 -151
@@ -0,0 +1,211 @@
1
+ import { createEmptyMiningFollowVisualizerState } from "./visualizer.js";
2
+ import { MiningProviderRequestError } from "./sentences.js";
3
+ import { clearMiningGateCache } from "./competitiveness.js";
4
+ import { MINING_NETWORK_SETTLE_WINDOW_MS, MINING_PROVIDER_BACKOFF_BASE_MS, MINING_PROVIDER_BACKOFF_MAX_MS, MINING_TIP_SETTLE_WINDOW_MS, } from "./constants.js";
5
+ import { miningPublishIsInMempool, normalizeMiningPublishState, normalizeMiningStateRecord, } from "./state.js";
6
+ export function createMiningRuntimeLoopState() {
7
+ return {
8
+ attemptedTipKey: null,
9
+ currentTipKey: null,
10
+ selectedCandidateTipKey: null,
11
+ selectedCandidate: null,
12
+ ui: createEmptyMiningFollowVisualizerState(),
13
+ waitingNote: null,
14
+ providerWaitState: null,
15
+ providerWaitLastError: null,
16
+ providerWaitNextRetryAtUnixMs: null,
17
+ providerTransientFailureCount: 0,
18
+ bitcoinRecoveryFirstFailureAtUnixMs: null,
19
+ bitcoinRecoveryFirstUnreachableAtUnixMs: null,
20
+ bitcoinRecoveryLastRestartAttemptAtUnixMs: null,
21
+ bitcoinRecoveryServiceInstanceId: null,
22
+ bitcoinRecoveryProcessId: null,
23
+ reconnectSettledUntilUnixMs: null,
24
+ tipSettledUntilUnixMs: null,
25
+ };
26
+ }
27
+ export function cloneMiningState(state) {
28
+ const normalized = normalizeMiningStateRecord(state);
29
+ return {
30
+ ...normalized,
31
+ currentBip39WordIndices: normalized.currentBip39WordIndices === null ? null : [...normalized.currentBip39WordIndices],
32
+ sharedMiningConflictOutpoint: normalized.sharedMiningConflictOutpoint === null
33
+ ? null
34
+ : { ...normalized.sharedMiningConflictOutpoint },
35
+ };
36
+ }
37
+ export function defaultMiningStatePatch(state, patch) {
38
+ return {
39
+ ...state,
40
+ miningState: {
41
+ ...cloneMiningState(state.miningState),
42
+ ...patch,
43
+ currentPublishState: normalizeMiningPublishState(patch.currentPublishState ?? state.miningState.currentPublishState),
44
+ },
45
+ };
46
+ }
47
+ export function hasBlockingMutation(state) {
48
+ return (state.pendingMutations ?? []).some((mutation) => mutation.status === "draft"
49
+ || mutation.status === "broadcasting"
50
+ || mutation.status === "broadcast-unknown"
51
+ || mutation.status === "live"
52
+ || mutation.status === "repair-required");
53
+ }
54
+ export function livePublishTargetsCandidateTip(options) {
55
+ const liveState = normalizeMiningStateRecord(options.liveState);
56
+ return liveState.currentTxid !== null
57
+ && liveState.currentPublishState === "in-mempool"
58
+ && liveState.livePublishInMempool === true
59
+ && liveState.currentReferencedBlockHashDisplay === options.candidate.referencedBlockHashDisplay
60
+ && liveState.currentBlockTargetHeight === options.candidate.targetBlockHeight;
61
+ }
62
+ export function miningCandidateIsCurrent(options) {
63
+ return options.state.currentReferencedBlockHashDisplay !== null
64
+ && options.nodeBestHash !== null
65
+ && options.state.currentReferencedBlockHashDisplay === options.nodeBestHash
66
+ && options.state.currentBlockTargetHeight !== null
67
+ && options.nodeBestHeight !== null
68
+ && options.state.currentBlockTargetHeight === (options.nodeBestHeight + 1);
69
+ }
70
+ export function resolveSharedMiningConflictOutpoint(state) {
71
+ const normalizedMiningState = normalizeMiningStateRecord(state);
72
+ if (miningPublishIsInMempool(normalizedMiningState) && normalizedMiningState.sharedMiningConflictOutpoint !== null) {
73
+ return { ...normalizedMiningState.sharedMiningConflictOutpoint };
74
+ }
75
+ return null;
76
+ }
77
+ function resolveMiningProviderBackoffDelayMs(consecutiveFailureCount) {
78
+ const exponent = Math.max(consecutiveFailureCount - 1, 0);
79
+ return Math.min(MINING_PROVIDER_BACKOFF_BASE_MS * (2 ** exponent), MINING_PROVIDER_BACKOFF_MAX_MS);
80
+ }
81
+ export function clearMiningProviderWait(loopState, resetTransientFailureCount = true) {
82
+ loopState.providerWaitState = null;
83
+ loopState.providerWaitLastError = null;
84
+ loopState.providerWaitNextRetryAtUnixMs = null;
85
+ if (resetTransientFailureCount) {
86
+ loopState.providerTransientFailureCount = 0;
87
+ }
88
+ }
89
+ export function recordTransientMiningProviderWait(options) {
90
+ options.loopState.providerTransientFailureCount += 1;
91
+ options.loopState.providerWaitState = options.error.providerState === "rate-limited"
92
+ ? "rate-limited"
93
+ : "backoff";
94
+ options.loopState.providerWaitLastError = options.error.message;
95
+ options.loopState.providerWaitNextRetryAtUnixMs = options.nowUnixMs
96
+ + resolveMiningProviderBackoffDelayMs(options.loopState.providerTransientFailureCount);
97
+ }
98
+ export function recordTerminalMiningProviderWait(options) {
99
+ clearMiningProviderWait(options.loopState);
100
+ if (options.error.providerState !== "auth-error" && options.error.providerState !== "not-found") {
101
+ throw new Error("mining_provider_wait_state_invalid");
102
+ }
103
+ options.loopState.providerWaitState = options.error.providerState;
104
+ options.loopState.providerWaitLastError = options.error.message;
105
+ }
106
+ export function isTransientMiningProviderError(error) {
107
+ return error.providerState === "unavailable" || error.providerState === "rate-limited";
108
+ }
109
+ export function expireMiningSettleWindows(loopState, nowUnixMs) {
110
+ if (loopState.reconnectSettledUntilUnixMs !== null
111
+ && loopState.reconnectSettledUntilUnixMs <= nowUnixMs) {
112
+ loopState.reconnectSettledUntilUnixMs = null;
113
+ }
114
+ if (loopState.tipSettledUntilUnixMs !== null
115
+ && loopState.tipSettledUntilUnixMs <= nowUnixMs) {
116
+ loopState.tipSettledUntilUnixMs = null;
117
+ }
118
+ }
119
+ export function setMiningReconnectSettleWindow(loopState, nowUnixMs) {
120
+ loopState.reconnectSettledUntilUnixMs = nowUnixMs + MINING_NETWORK_SETTLE_WINDOW_MS;
121
+ }
122
+ export function setMiningTipSettleWindow(loopState, nowUnixMs) {
123
+ loopState.tipSettledUntilUnixMs = nowUnixMs + MINING_TIP_SETTLE_WINDOW_MS;
124
+ }
125
+ export function buildMiningSettleWindowStatusOverrides(loopState, nowUnixMs) {
126
+ expireMiningSettleWindows(loopState, nowUnixMs);
127
+ return {
128
+ reconnectSettledUntilUnixMs: loopState.reconnectSettledUntilUnixMs,
129
+ tipSettledUntilUnixMs: loopState.tipSettledUntilUnixMs,
130
+ };
131
+ }
132
+ export function buildMiningTipKey(bestBlockHash, targetBlockHeight) {
133
+ if (bestBlockHash === null || targetBlockHeight === null) {
134
+ return null;
135
+ }
136
+ return `${bestBlockHash}:${targetBlockHeight}`;
137
+ }
138
+ export function resetMiningUiForTip(loopState, _targetBlockHeight) {
139
+ const preservedTxid = loopState.ui.latestTxid;
140
+ const preservedFundingAddress = loopState.ui.fundingAddress;
141
+ loopState.ui = {
142
+ ...createEmptyMiningFollowVisualizerState(),
143
+ fundingAddress: preservedFundingAddress,
144
+ latestTxid: preservedTxid,
145
+ };
146
+ loopState.selectedCandidateTipKey = null;
147
+ loopState.selectedCandidate = null;
148
+ loopState.waitingNote = null;
149
+ }
150
+ function resolveProvisionalBroadcastTxidForCandidate(options) {
151
+ if (options.liveState === null || options.liveState === undefined) {
152
+ return null;
153
+ }
154
+ const liveState = normalizeMiningStateRecord(options.liveState);
155
+ if (liveState.currentTxid === null
156
+ || liveState.currentPublishState !== "in-mempool"
157
+ || liveState.livePublishInMempool !== true) {
158
+ return null;
159
+ }
160
+ if (liveState.currentDomain !== options.candidate.domainName
161
+ || liveState.currentDomainId !== options.candidate.domainId
162
+ || liveState.currentSentence !== options.candidate.sentence
163
+ || liveState.currentBlockTargetHeight !== options.candidate.targetBlockHeight
164
+ || liveState.currentReferencedBlockHashDisplay !== options.candidate.referencedBlockHashDisplay) {
165
+ return null;
166
+ }
167
+ return liveState.currentTxid;
168
+ }
169
+ export function setMiningUiCandidate(loopState, candidate, liveState) {
170
+ loopState.ui.latestSentence = candidate.sentence;
171
+ loopState.ui.provisionalRequiredWords = [...candidate.bip39Words];
172
+ loopState.ui.provisionalEntry = {
173
+ domainName: candidate.domainName,
174
+ sentence: candidate.sentence,
175
+ };
176
+ loopState.ui.provisionalBroadcastTxid = resolveProvisionalBroadcastTxidForCandidate({
177
+ candidate,
178
+ liveState,
179
+ });
180
+ }
181
+ export function getSelectedCandidateForTip(loopState, tipKey) {
182
+ if (tipKey === null || loopState.selectedCandidateTipKey !== tipKey) {
183
+ return null;
184
+ }
185
+ return loopState.selectedCandidate;
186
+ }
187
+ export function cacheSelectedCandidateForTip(loopState, tipKey, candidate, liveState) {
188
+ loopState.selectedCandidateTipKey = tipKey;
189
+ loopState.selectedCandidate = candidate;
190
+ setMiningUiCandidate(loopState, candidate, liveState);
191
+ }
192
+ export function clearSelectedCandidate(loopState) {
193
+ loopState.selectedCandidateTipKey = null;
194
+ loopState.selectedCandidate = null;
195
+ }
196
+ export function clearMiningUiTransientCandidate(loopState) {
197
+ loopState.ui.provisionalRequiredWords = [];
198
+ loopState.ui.provisionalEntry = {
199
+ domainName: null,
200
+ sentence: null,
201
+ };
202
+ loopState.ui.provisionalBroadcastTxid = null;
203
+ loopState.ui.latestSentence = null;
204
+ }
205
+ export function discardMiningLoopTransientWork(loopState, walletRootId) {
206
+ clearMiningGateCache(walletRootId);
207
+ clearSelectedCandidate(loopState);
208
+ clearMiningUiTransientCandidate(loopState);
209
+ loopState.waitingNote = null;
210
+ clearMiningProviderWait(loopState);
211
+ }
@@ -0,0 +1,173 @@
1
+ import type { WalletReadContext } from "../read/index.js";
2
+ import type { FixedWalletInput, MutationSender, WalletMutationRpcClient } from "../tx/common.js";
3
+ import type { WalletStateV1 } from "../types.js";
4
+ import type { MiningRuntimeStatusV1 } from "./types.js";
5
+ import type { MiningFollowVisualizerState, MiningRecentWinSummary, MiningSentenceBoardEntry } from "./visualizer.js";
6
+ export type MiningRpcClient = WalletMutationRpcClient & {
7
+ getBlockchainInfo(): Promise<{
8
+ blocks: number;
9
+ bestblockhash: string;
10
+ initialblockdownload?: boolean;
11
+ }>;
12
+ getNetworkInfo(): Promise<{
13
+ networkactive: boolean;
14
+ connections_out?: number;
15
+ }>;
16
+ getBlockHash(height: number): Promise<string>;
17
+ getBlock(hashHex: string): Promise<{
18
+ hash: string;
19
+ previousblockhash?: string;
20
+ height: number;
21
+ time?: number;
22
+ }>;
23
+ getMempoolInfo(): Promise<{
24
+ loaded: boolean;
25
+ }>;
26
+ getRawMempool(): Promise<string[]>;
27
+ getRawMempoolVerbose(): Promise<{
28
+ txids: string[];
29
+ mempool_sequence: string | number;
30
+ }>;
31
+ getMempoolEntry(txid: string): Promise<{
32
+ vsize: number;
33
+ fees: {
34
+ base: number;
35
+ ancestor: number;
36
+ descendant: number;
37
+ };
38
+ ancestorsize?: number;
39
+ descendantsize?: number;
40
+ }>;
41
+ getRawTransaction(txid: string, verbose?: boolean): Promise<{
42
+ txid: string;
43
+ hash?: string;
44
+ vin: Array<{
45
+ txid?: string;
46
+ prevout?: {
47
+ scriptPubKey?: {
48
+ hex?: string;
49
+ };
50
+ };
51
+ }>;
52
+ vout: Array<{
53
+ n: number;
54
+ value: number | string;
55
+ scriptPubKey?: {
56
+ hex?: string;
57
+ };
58
+ }>;
59
+ }>;
60
+ getTransaction(walletName: string, txid: string): Promise<{
61
+ txid: string;
62
+ confirmations: number;
63
+ blockhash?: string;
64
+ walletconflicts?: string[];
65
+ }>;
66
+ sendRawTransaction(hex: string): Promise<string>;
67
+ saveMempool?(): Promise<null>;
68
+ };
69
+ export interface MiningCandidate {
70
+ domainId: number;
71
+ domainName: string;
72
+ localIndex: number;
73
+ sender: MutationSender;
74
+ sentence: string;
75
+ encodedSentenceBytes: Uint8Array;
76
+ bip39WordIndices: number[];
77
+ bip39Words: readonly string[];
78
+ canonicalBlend: bigint;
79
+ referencedBlockHashDisplay: string;
80
+ referencedBlockHashInternal: Uint8Array;
81
+ targetBlockHeight: number;
82
+ }
83
+ export type ReadyMiningReadContext = WalletReadContext & {
84
+ localState: {
85
+ availability: "ready";
86
+ state: WalletStateV1;
87
+ };
88
+ snapshot: NonNullable<WalletReadContext["snapshot"]>;
89
+ model: NonNullable<WalletReadContext["model"]>;
90
+ };
91
+ export interface MiningPublishSkipResult {
92
+ state: WalletStateV1;
93
+ txid: null;
94
+ decision: "publish-skipped-stale-candidate" | "publish-paused-insufficient-funds";
95
+ note: string;
96
+ lastError?: string | null;
97
+ skipped: true;
98
+ retryable?: false;
99
+ candidate: null;
100
+ }
101
+ export interface MiningPublishRetryResult {
102
+ state: WalletStateV1;
103
+ txid: null;
104
+ decision: "publish-retry-pending";
105
+ note: string;
106
+ skipped?: false;
107
+ retryable: true;
108
+ candidate: MiningCandidate;
109
+ }
110
+ export interface MiningPublishSuccessResult {
111
+ state: WalletStateV1;
112
+ txid: string | null;
113
+ decision: string;
114
+ note?: null;
115
+ skipped?: false;
116
+ retryable?: false;
117
+ candidate: MiningCandidate;
118
+ }
119
+ export type MiningPublishOutcome = MiningPublishSuccessResult | MiningPublishSkipResult | MiningPublishRetryResult;
120
+ export interface CompetitivenessDecision {
121
+ allowed: boolean;
122
+ decision: string;
123
+ sameDomainCompetitorSuppressed: boolean;
124
+ higherRankedCompetitorDomainCount: number;
125
+ dedupedCompetitorDomainCount: number;
126
+ competitivenessGateIndeterminate: boolean;
127
+ mempoolSequenceCacheStatus: MiningRuntimeStatusV1["mempoolSequenceCacheStatus"];
128
+ lastMempoolSequence: string | null;
129
+ visibleBoardEntries: MiningSentenceBoardEntry[];
130
+ candidateRank: number | null;
131
+ }
132
+ export type MiningCyclePhase = MiningRuntimeStatusV1["currentPhase"];
133
+ export interface MiningCycleGateSnapshot {
134
+ higherRankedCompetitorDomainCount: number;
135
+ dedupedCompetitorDomainCount: number;
136
+ mempoolSequenceCacheStatus: MiningRuntimeStatusV1["mempoolSequenceCacheStatus"];
137
+ lastMempoolSequence: string | null;
138
+ }
139
+ export interface MiningCycleState {
140
+ phase: MiningCyclePhase;
141
+ targetBlockHeight: number | null;
142
+ tipKey: string | null;
143
+ selectedCandidate: MiningCandidate | null;
144
+ gateSnapshot: MiningCycleGateSnapshot;
145
+ }
146
+ export interface MiningCycleContext {
147
+ currentPhase: MiningCyclePhase;
148
+ targetBlockHeight: number | null;
149
+ tipKey: string | null;
150
+ selectedCandidate: MiningCandidate | null;
151
+ }
152
+ export interface MiningCycleEffects {
153
+ statusPhase?: MiningCyclePhase;
154
+ persistState?: WalletStateV1 | null;
155
+ followUiState?: MiningFollowVisualizerState | null;
156
+ recentWin?: MiningRecentWinSummary | null;
157
+ }
158
+ export interface MiningMutationPlan {
159
+ sender: MutationSender;
160
+ fixedInputs: FixedWalletInput[];
161
+ outputs: unknown[];
162
+ changeAddress: string;
163
+ changePosition: number;
164
+ expectedOpReturnScriptHex: string;
165
+ allowedFundingScriptPubKeyHex: string;
166
+ eligibleFundingOutpointKeys: Set<string>;
167
+ expectedConflictOutpoint: {
168
+ txid: string;
169
+ vout: number;
170
+ } | null;
171
+ feeRateSatVb: number;
172
+ }
173
+ export type MiningCooperativeYield = () => Promise<void>;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,7 @@
1
+ export declare function resolveBip39WordsFromIndices(indices: readonly number[] | null | undefined): readonly string[];
2
+ export declare function rootDomain(name: string): boolean;
3
+ export declare function getBlockRewardCogtoshi(height: number): bigint;
4
+ export declare function deriveMiningWordIndices(referencedBlockhash: Uint8Array, miningDomainId: number): number[];
5
+ export declare function numberToSats(value: number | string): bigint;
6
+ export declare function compareLexicographically(left: Uint8Array, right: Uint8Array): number;
7
+ export declare function tieBreakHash(blendSeed: Uint8Array, miningDomainId: number): Uint8Array;
@@ -0,0 +1,75 @@
1
+ import { createHash } from "node:crypto";
2
+ import { wordlist as englishWordlist } from "@scure/bip39/wordlists/english.js";
3
+ export function resolveBip39WordsFromIndices(indices) {
4
+ if (indices === null || indices === undefined) {
5
+ return [];
6
+ }
7
+ const words = [];
8
+ for (const index of indices) {
9
+ if (!Number.isInteger(index) || index < 0 || index >= englishWordlist.length) {
10
+ continue;
11
+ }
12
+ words.push(englishWordlist[index]);
13
+ }
14
+ return words;
15
+ }
16
+ export function rootDomain(name) {
17
+ return !name.includes("-");
18
+ }
19
+ function uint32BigEndian(value) {
20
+ const buffer = Buffer.alloc(4);
21
+ buffer.writeUInt32BE(value >>> 0, 0);
22
+ return buffer;
23
+ }
24
+ export function getBlockRewardCogtoshi(height) {
25
+ const halvingEra = Math.floor(height / 210_000);
26
+ if (halvingEra >= 33) {
27
+ return 0n;
28
+ }
29
+ return 5000000000n >> BigInt(halvingEra);
30
+ }
31
+ export function deriveMiningWordIndices(referencedBlockhash, miningDomainId) {
32
+ const seed = createHash("sha256")
33
+ .update(Buffer.from(referencedBlockhash))
34
+ .update(uint32BigEndian(miningDomainId))
35
+ .digest();
36
+ const indices = [];
37
+ for (let index = 0; index < 5; index += 1) {
38
+ const chunkOffset = index * 4;
39
+ let wordIndex = seed.readUInt32BE(chunkOffset) % 2048;
40
+ while (indices.includes(wordIndex)) {
41
+ wordIndex = (wordIndex + 1) % 2048;
42
+ }
43
+ indices.push(wordIndex);
44
+ }
45
+ return indices;
46
+ }
47
+ export function numberToSats(value) {
48
+ const text = typeof value === "number" ? value.toFixed(8) : value;
49
+ const match = /^(-?)(\d+)(?:\.(\d{0,8}))?$/.exec(text.trim());
50
+ if (match == null) {
51
+ throw new Error(`mining_invalid_amount_${text}`);
52
+ }
53
+ const sign = match[1] === "-" ? -1n : 1n;
54
+ const whole = BigInt(match[2] ?? "0");
55
+ const fraction = BigInt((match[3] ?? "").padEnd(8, "0"));
56
+ return sign * ((whole * 100000000n) + fraction);
57
+ }
58
+ export function compareLexicographically(left, right) {
59
+ const length = Math.min(left.length, right.length);
60
+ for (let index = 0; index < length; index += 1) {
61
+ if (left[index] !== right[index]) {
62
+ return left[index] < right[index] ? -1 : 1;
63
+ }
64
+ }
65
+ if (left.length === right.length) {
66
+ return 0;
67
+ }
68
+ return left.length < right.length ? -1 : 1;
69
+ }
70
+ export function tieBreakHash(blendSeed, miningDomainId) {
71
+ return createHash("sha256")
72
+ .update(Buffer.from(blendSeed))
73
+ .update(uint32BigEndian(miningDomainId))
74
+ .digest();
75
+ }
@@ -0,0 +1,2 @@
1
+ import type { MiningEventRecord } from "./types.js";
2
+ export declare function createMiningEventRecord(kind: string, message: string, options?: Partial<MiningEventRecord>): MiningEventRecord;
@@ -0,0 +1,19 @@
1
+ export function createMiningEventRecord(kind, message, options = {}) {
2
+ return {
3
+ schemaVersion: 1,
4
+ timestampUnixMs: options.timestampUnixMs ?? Date.now(),
5
+ level: options.level ?? "info",
6
+ kind,
7
+ message,
8
+ targetBlockHeight: options.targetBlockHeight ?? null,
9
+ referencedBlockHashDisplay: options.referencedBlockHashDisplay ?? null,
10
+ domainId: options.domainId ?? null,
11
+ domainName: options.domainName ?? null,
12
+ txid: options.txid ?? null,
13
+ feeRateSatVb: options.feeRateSatVb ?? null,
14
+ feeSats: options.feeSats ?? null,
15
+ score: options.score ?? null,
16
+ reason: options.reason ?? null,
17
+ runId: options.runId ?? null,
18
+ };
19
+ }
@@ -0,0 +1,71 @@
1
+ import { createRpcClient } from "../../bitcoind/node.js";
2
+ import { attachOrStartManagedBitcoindService, probeManagedBitcoindService, stopManagedBitcoindService } from "../../bitcoind/service.js";
3
+ import type { WalletReadContext } from "../read/index.js";
4
+ import { openWalletReadContext } from "../read/index.js";
5
+ import type { WalletRuntimePaths } from "../runtime.js";
6
+ import type { WalletSecretProvider } from "../state/provider.js";
7
+ import { type MiningRuntimeStatusOverrides } from "./projection.js";
8
+ import { type MiningRuntimeLoopState } from "./engine-state.js";
9
+ import type { MiningRpcClient } from "./engine-types.js";
10
+ import type { MiningRuntimeStatusV1 } from "./types.js";
11
+ import type { MiningFollowVisualizer, MiningFollowVisualizerState } from "./visualizer.js";
12
+ export declare function refreshAndSaveMiningRuntimeStatus(options: {
13
+ paths: WalletRuntimePaths;
14
+ provider: WalletSecretProvider;
15
+ readContext: WalletReadContext;
16
+ overrides?: MiningRuntimeStatusOverrides;
17
+ visualizer?: MiningFollowVisualizer;
18
+ visualizerState?: MiningFollowVisualizerState;
19
+ }): Promise<MiningRuntimeStatusV1>;
20
+ export declare function resetMiningBitcoindRecoveryState(loopState: MiningRuntimeLoopState, value?: {
21
+ serviceInstanceId?: string | null;
22
+ processId?: number | null;
23
+ } | {
24
+ pid?: number | null;
25
+ } | null): boolean;
26
+ export declare function isRecoverableMiningBitcoindError(error: unknown): boolean;
27
+ export declare function handleRecoverableMiningBitcoindFailure(options: {
28
+ error: unknown;
29
+ dataDir: string;
30
+ provider: WalletSecretProvider;
31
+ paths: WalletRuntimePaths;
32
+ runMode: "foreground" | "background";
33
+ readContext: WalletReadContext;
34
+ loopState: MiningRuntimeLoopState;
35
+ attachService: typeof attachOrStartManagedBitcoindService;
36
+ probeService: typeof probeManagedBitcoindService;
37
+ stopService: typeof stopManagedBitcoindService;
38
+ nowUnixMs: number;
39
+ visualizer?: MiningFollowVisualizer;
40
+ }): Promise<void>;
41
+ export declare function handleDetectedMiningRuntimeResume(options: {
42
+ dataDir: string;
43
+ databasePath: string;
44
+ provider: WalletSecretProvider;
45
+ paths: WalletRuntimePaths;
46
+ runMode: "foreground" | "background";
47
+ backgroundWorkerPid: number | null;
48
+ backgroundWorkerRunId: string | null;
49
+ detectedAtUnixMs: number;
50
+ openReadContext?: typeof openWalletReadContext;
51
+ visualizer?: MiningFollowVisualizer;
52
+ loopState: MiningRuntimeLoopState;
53
+ }): Promise<void>;
54
+ export declare function saveStopSnapshot(options: {
55
+ dataDir: string;
56
+ databasePath: string;
57
+ provider: WalletSecretProvider;
58
+ paths: WalletRuntimePaths;
59
+ runMode: "foreground" | "background";
60
+ backgroundWorkerPid: number | null;
61
+ backgroundWorkerRunId: string | null;
62
+ note: string | null;
63
+ openReadContext?: typeof openWalletReadContext;
64
+ attachService?: typeof attachOrStartManagedBitcoindService;
65
+ rpcFactory?: (config: Parameters<typeof createRpcClient>[0]) => MiningRpcClient;
66
+ }): Promise<void>;
67
+ export declare function attemptSaveMempool(options: {
68
+ rpc: MiningRpcClient;
69
+ paths: WalletRuntimePaths;
70
+ runId: string | null;
71
+ }): Promise<void>;