@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.
- package/README.md +4 -5
- package/dist/bitcoind/node.js +2 -1
- package/dist/bitcoind/progress/tty-renderer.js +3 -2
- package/dist/bitcoind/service.js +6 -24
- package/dist/bitcoind/types.d.ts +1 -0
- package/dist/bitcoind/types.js +1 -0
- package/dist/cli/command-registry.d.ts +39 -0
- package/dist/cli/command-registry.js +1132 -0
- package/dist/cli/commands/client-admin.js +6 -56
- package/dist/cli/commands/mining-admin.js +9 -32
- package/dist/cli/commands/mining-read.js +15 -56
- package/dist/cli/commands/mining-runtime.js +258 -57
- package/dist/cli/commands/service-runtime.js +1 -64
- package/dist/cli/commands/status.js +2 -15
- package/dist/cli/commands/update.js +6 -21
- package/dist/cli/commands/wallet-admin.js +18 -120
- package/dist/cli/commands/wallet-mutation.js +4 -7
- package/dist/cli/commands/wallet-read.js +31 -138
- package/dist/cli/context.js +2 -4
- package/dist/cli/mining-format.js +8 -2
- package/dist/cli/mutation-command-groups.d.ts +11 -11
- package/dist/cli/mutation-command-groups.js +9 -18
- package/dist/cli/mutation-json.d.ts +1 -17
- package/dist/cli/mutation-json.js +1 -28
- package/dist/cli/mutation-success.d.ts +0 -1
- package/dist/cli/mutation-success.js +0 -19
- package/dist/cli/output.d.ts +1 -10
- package/dist/cli/output.js +52 -481
- package/dist/cli/parse.d.ts +1 -1
- package/dist/cli/parse.js +38 -695
- package/dist/cli/runner.js +28 -113
- package/dist/cli/types.d.ts +7 -8
- package/dist/cli/update-notifier.js +1 -1
- package/dist/cli/wallet-format.js +1 -1
- package/dist/wallet/lifecycle/managed-core.d.ts +23 -0
- package/dist/wallet/lifecycle/managed-core.js +257 -0
- package/dist/wallet/lifecycle/repair-mining.d.ts +49 -0
- package/dist/wallet/lifecycle/repair-mining.js +304 -0
- package/dist/wallet/lifecycle/repair-runtime.d.ts +36 -0
- package/dist/wallet/lifecycle/repair-runtime.js +206 -0
- package/dist/wallet/lifecycle/repair.d.ts +11 -0
- package/dist/wallet/lifecycle/repair.js +368 -0
- package/dist/wallet/lifecycle/setup.d.ts +16 -0
- package/dist/wallet/lifecycle/setup.js +430 -0
- package/dist/wallet/lifecycle/types.d.ts +125 -0
- package/dist/wallet/lifecycle/types.js +1 -0
- package/dist/wallet/lifecycle.d.ts +4 -165
- package/dist/wallet/lifecycle.js +3 -1656
- package/dist/wallet/mining/candidate.d.ts +60 -0
- package/dist/wallet/mining/candidate.js +290 -0
- package/dist/wallet/mining/competitiveness.d.ts +22 -0
- package/dist/wallet/mining/competitiveness.js +640 -0
- package/dist/wallet/mining/control.js +7 -251
- package/dist/wallet/mining/cycle.d.ts +39 -0
- package/dist/wallet/mining/cycle.js +542 -0
- package/dist/wallet/mining/engine-state.d.ts +66 -0
- package/dist/wallet/mining/engine-state.js +211 -0
- package/dist/wallet/mining/engine-types.d.ts +173 -0
- package/dist/wallet/mining/engine-types.js +1 -0
- package/dist/wallet/mining/engine-utils.d.ts +7 -0
- package/dist/wallet/mining/engine-utils.js +75 -0
- package/dist/wallet/mining/events.d.ts +2 -0
- package/dist/wallet/mining/events.js +19 -0
- package/dist/wallet/mining/lifecycle.d.ts +71 -0
- package/dist/wallet/mining/lifecycle.js +355 -0
- package/dist/wallet/mining/projection.d.ts +61 -0
- package/dist/wallet/mining/projection.js +319 -0
- package/dist/wallet/mining/publish.d.ts +79 -0
- package/dist/wallet/mining/publish.js +614 -0
- package/dist/wallet/mining/runner.d.ts +12 -418
- package/dist/wallet/mining/runner.js +274 -3433
- package/dist/wallet/mining/supervisor.d.ts +134 -0
- package/dist/wallet/mining/supervisor.js +558 -0
- package/dist/wallet/mining/visualizer-sync.d.ts +42 -0
- package/dist/wallet/mining/visualizer-sync.js +166 -0
- package/dist/wallet/mining/visualizer.d.ts +1 -0
- package/dist/wallet/mining/visualizer.js +33 -18
- package/dist/wallet/read/context.d.ts +5 -1
- package/dist/wallet/read/context.js +19 -4
- package/dist/wallet/reset.d.ts +1 -1
- package/dist/wallet/reset.js +35 -11
- package/dist/wallet/runtime.d.ts +0 -6
- package/dist/wallet/runtime.js +2 -38
- package/dist/wallet/tx/common.d.ts +18 -0
- package/dist/wallet/tx/common.js +40 -26
- package/package.json +1 -1
- package/dist/wallet/state/seed-index.d.ts +0 -43
- 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,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>;
|