@cogcoin/client 1.1.4 → 1.1.6
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/indexer-daemon.d.ts +3 -7
- package/dist/bitcoind/indexer-daemon.js +43 -158
- package/dist/bitcoind/managed-runtime/bitcoind-policy.d.ts +16 -0
- package/dist/bitcoind/managed-runtime/bitcoind-policy.js +177 -0
- package/dist/bitcoind/managed-runtime/indexer-policy.d.ts +34 -0
- package/dist/bitcoind/managed-runtime/indexer-policy.js +200 -0
- package/dist/bitcoind/managed-runtime/status.d.ts +11 -0
- package/dist/bitcoind/managed-runtime/status.js +59 -0
- package/dist/bitcoind/managed-runtime/types.d.ts +37 -0
- package/dist/bitcoind/managed-runtime/types.js +1 -0
- package/dist/bitcoind/progress/tty-renderer.js +3 -2
- package/dist/bitcoind/service.d.ts +2 -7
- package/dist/bitcoind/service.js +46 -94
- 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/access.d.ts +5 -0
- package/dist/wallet/lifecycle/access.js +79 -0
- package/dist/wallet/lifecycle/context.d.ts +26 -0
- package/dist/wallet/lifecycle/context.js +58 -0
- package/dist/wallet/lifecycle/managed-core.d.ts +15 -0
- package/dist/wallet/lifecycle/managed-core.js +197 -0
- package/dist/wallet/lifecycle/repair-bitcoind.d.ts +10 -0
- package/dist/wallet/lifecycle/repair-bitcoind.js +142 -0
- package/dist/wallet/lifecycle/repair-indexer.d.ts +8 -0
- package/dist/wallet/lifecycle/repair-indexer.js +117 -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 +9 -0
- package/dist/wallet/lifecycle/repair.js +127 -0
- package/dist/wallet/lifecycle/setup-prompts.d.ts +7 -0
- package/dist/wallet/lifecycle/setup-prompts.js +88 -0
- package/dist/wallet/lifecycle/setup-state.d.ts +26 -0
- package/dist/wallet/lifecycle/setup-state.js +159 -0
- package/dist/wallet/lifecycle/setup.d.ts +15 -0
- package/dist/wallet/lifecycle/setup.js +124 -0
- package/dist/wallet/lifecycle/types.d.ts +156 -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.js +13 -188
- 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,42 @@
|
|
|
1
|
+
import type { WalletReadContext } from "../read/index.js";
|
|
2
|
+
import type { WalletStateV1 } from "../types.js";
|
|
3
|
+
import type { MiningRpcClient } from "./engine-types.js";
|
|
4
|
+
import type { MiningRuntimeLoopState } from "./engine-state.js";
|
|
5
|
+
import type { MiningFollowVisualizerState, MiningRecentWinSummary, MiningSentenceBoardEntry } from "./visualizer.js";
|
|
6
|
+
export declare function resolveSettledBoard(options: {
|
|
7
|
+
snapshotState: NonNullable<WalletReadContext["snapshot"]>["state"] | null | undefined;
|
|
8
|
+
snapshotTipHeight: number | null;
|
|
9
|
+
snapshotTipPreviousHashHex?: string | null;
|
|
10
|
+
nodeBestHeight: number | null;
|
|
11
|
+
}): {
|
|
12
|
+
settledBlockHeight: number | null;
|
|
13
|
+
settledBoardEntries: MiningSentenceBoardEntry[];
|
|
14
|
+
};
|
|
15
|
+
export declare function syncMiningUiForCurrentTip(options: {
|
|
16
|
+
loopState: MiningRuntimeLoopState;
|
|
17
|
+
snapshotState: NonNullable<WalletReadContext["snapshot"]>["state"] | null | undefined;
|
|
18
|
+
snapshotTipHeight: number | null;
|
|
19
|
+
snapshotTipPreviousHashHex: string | null;
|
|
20
|
+
nodeBestHeight: number | null;
|
|
21
|
+
nodeBestHash: string | null;
|
|
22
|
+
recentWin: MiningRecentWinSummary | null;
|
|
23
|
+
}): {
|
|
24
|
+
targetBlockHeight: number | null;
|
|
25
|
+
tipKey: string | null;
|
|
26
|
+
tipChanged: boolean;
|
|
27
|
+
};
|
|
28
|
+
export declare function resolveFundingDisplaySats(state: WalletStateV1, rpc: MiningRpcClient): Promise<bigint>;
|
|
29
|
+
export declare function loadMiningVisibleFollowBlockTimes(options: {
|
|
30
|
+
rpc: MiningRpcClient;
|
|
31
|
+
indexedTipHeight: number | null;
|
|
32
|
+
indexedTipHashHex: string | null;
|
|
33
|
+
}): Promise<Record<number, number>>;
|
|
34
|
+
export declare function syncMiningVisualizerBalances(loopState: MiningRuntimeLoopState, readContext: WalletReadContext & {
|
|
35
|
+
localState: {
|
|
36
|
+
availability: "ready";
|
|
37
|
+
state: WalletStateV1;
|
|
38
|
+
};
|
|
39
|
+
}, balanceSats: bigint | null): void;
|
|
40
|
+
export declare function createIndexedMiningFollowVisualizerState(readContext: WalletReadContext): MiningFollowVisualizerState;
|
|
41
|
+
export declare function syncMiningVisualizerBlockTimes(loopState: MiningRuntimeLoopState, blockTimesByHeight: Record<number, number>): void;
|
|
42
|
+
export declare function findRecentMiningWin(snapshotState: NonNullable<WalletReadContext["snapshot"]>["state"] | null | undefined, txid: string | null, targetBlockHeight: number | null): MiningRecentWinSummary | null;
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { getBalance, getBlockWinners, lookupDomainById, } from "@cogcoin/indexer/queries";
|
|
2
|
+
import { displayToInternalBlockhash } from "@cogcoin/scoring";
|
|
3
|
+
import { FOLLOW_VISIBLE_PRIOR_BLOCKS } from "../../bitcoind/client/follow-block-times.js";
|
|
4
|
+
import { buildMiningTipKey, resetMiningUiForTip } from "./engine-state.js";
|
|
5
|
+
import { deriveMiningWordIndices, numberToSats, resolveBip39WordsFromIndices, } from "./engine-utils.js";
|
|
6
|
+
import { createEmptyMiningFollowVisualizerState } from "./visualizer.js";
|
|
7
|
+
function resolveSettledWinnerRequiredWords(options) {
|
|
8
|
+
const storedWords = resolveBip39WordsFromIndices(options.bip39WordIndices);
|
|
9
|
+
if (storedWords.length > 0) {
|
|
10
|
+
return storedWords;
|
|
11
|
+
}
|
|
12
|
+
if (options.snapshotTipPreviousHashHex === null
|
|
13
|
+
|| options.snapshotTipPreviousHashHex === undefined
|
|
14
|
+
|| !Number.isInteger(options.domainId)
|
|
15
|
+
|| options.domainId <= 0) {
|
|
16
|
+
return [];
|
|
17
|
+
}
|
|
18
|
+
return resolveBip39WordsFromIndices(deriveMiningWordIndices(Buffer.from(displayToInternalBlockhash(options.snapshotTipPreviousHashHex), "hex"), options.domainId));
|
|
19
|
+
}
|
|
20
|
+
function fallbackSettledWinnerDomainName(domainId) {
|
|
21
|
+
return `domain-${domainId}`;
|
|
22
|
+
}
|
|
23
|
+
function resolveCurrentMinedBlockBoard(options) {
|
|
24
|
+
const settledBlockHeight = options.snapshotTipHeight ?? null;
|
|
25
|
+
if (settledBlockHeight === null) {
|
|
26
|
+
return {
|
|
27
|
+
settledBlockHeight,
|
|
28
|
+
settledBoardEntries: [],
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
if (options.snapshotState === null || options.snapshotState === undefined) {
|
|
32
|
+
return {
|
|
33
|
+
settledBlockHeight,
|
|
34
|
+
settledBoardEntries: [],
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
const settledBoardEntries = (getBlockWinners(options.snapshotState, settledBlockHeight) ?? [])
|
|
38
|
+
.slice()
|
|
39
|
+
.sort((left, right) => left.rank - right.rank || left.txIndex - right.txIndex)
|
|
40
|
+
.slice(0, 5)
|
|
41
|
+
.map((winner) => ({
|
|
42
|
+
rank: winner.rank,
|
|
43
|
+
domainName: lookupDomainById(options.snapshotState, winner.domainId)?.name ?? fallbackSettledWinnerDomainName(winner.domainId),
|
|
44
|
+
sentence: winner.sentenceText ?? "[unavailable]",
|
|
45
|
+
requiredWords: resolveSettledWinnerRequiredWords({
|
|
46
|
+
domainId: winner.domainId,
|
|
47
|
+
bip39WordIndices: winner.bip39WordIndices,
|
|
48
|
+
snapshotTipPreviousHashHex: options.snapshotTipPreviousHashHex,
|
|
49
|
+
}),
|
|
50
|
+
}));
|
|
51
|
+
return {
|
|
52
|
+
settledBlockHeight,
|
|
53
|
+
settledBoardEntries,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
export function resolveSettledBoard(options) {
|
|
57
|
+
void options.nodeBestHeight;
|
|
58
|
+
return resolveCurrentMinedBlockBoard({
|
|
59
|
+
snapshotState: options.snapshotState,
|
|
60
|
+
snapshotTipHeight: options.snapshotTipHeight,
|
|
61
|
+
snapshotTipPreviousHashHex: options.snapshotTipPreviousHashHex ?? null,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
function syncMiningUiSettledBoard(loopState, snapshotState, snapshotTipHeight, snapshotTipPreviousHashHex) {
|
|
65
|
+
const settledBoard = resolveCurrentMinedBlockBoard({
|
|
66
|
+
snapshotState,
|
|
67
|
+
snapshotTipHeight,
|
|
68
|
+
snapshotTipPreviousHashHex,
|
|
69
|
+
});
|
|
70
|
+
loopState.ui.settledBlockHeight = settledBoard.settledBlockHeight;
|
|
71
|
+
loopState.ui.settledBoardEntries = settledBoard.settledBoardEntries;
|
|
72
|
+
}
|
|
73
|
+
export function syncMiningUiForCurrentTip(options) {
|
|
74
|
+
const targetBlockHeight = options.nodeBestHeight === null
|
|
75
|
+
? null
|
|
76
|
+
: options.nodeBestHeight + 1;
|
|
77
|
+
const tipKey = buildMiningTipKey(options.nodeBestHash, targetBlockHeight);
|
|
78
|
+
const priorTipKey = options.loopState.currentTipKey;
|
|
79
|
+
const tipChanged = tipKey !== null && tipKey !== priorTipKey;
|
|
80
|
+
if (tipKey !== priorTipKey) {
|
|
81
|
+
options.loopState.currentTipKey = tipKey;
|
|
82
|
+
resetMiningUiForTip(options.loopState, targetBlockHeight);
|
|
83
|
+
if (options.recentWin !== null) {
|
|
84
|
+
options.loopState.ui.recentWin = options.recentWin;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
syncMiningUiSettledBoard(options.loopState, options.snapshotState, options.snapshotTipHeight, options.snapshotTipPreviousHashHex);
|
|
88
|
+
return {
|
|
89
|
+
targetBlockHeight,
|
|
90
|
+
tipKey,
|
|
91
|
+
tipChanged,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
export async function resolveFundingDisplaySats(state, rpc) {
|
|
95
|
+
const utxos = await rpc.listUnspent(state.managedCoreWallet.walletName, 0);
|
|
96
|
+
return utxos.reduce((sum, entry) => {
|
|
97
|
+
if (entry.scriptPubKey !== state.funding.scriptPubKeyHex
|
|
98
|
+
|| entry.spendable === false) {
|
|
99
|
+
return sum;
|
|
100
|
+
}
|
|
101
|
+
return sum + numberToSats(entry.amount);
|
|
102
|
+
}, 0n);
|
|
103
|
+
}
|
|
104
|
+
export async function loadMiningVisibleFollowBlockTimes(options) {
|
|
105
|
+
if (options.indexedTipHeight === null || options.indexedTipHashHex === null) {
|
|
106
|
+
return {};
|
|
107
|
+
}
|
|
108
|
+
const blockTimesByHeight = {};
|
|
109
|
+
let currentHeight = options.indexedTipHeight;
|
|
110
|
+
let currentHashHex = options.indexedTipHashHex;
|
|
111
|
+
for (let offset = 0; offset <= FOLLOW_VISIBLE_PRIOR_BLOCKS; offset += 1) {
|
|
112
|
+
if (currentHeight < 0 || currentHashHex === null) {
|
|
113
|
+
break;
|
|
114
|
+
}
|
|
115
|
+
const block = await options.rpc.getBlock(currentHashHex);
|
|
116
|
+
if (typeof block.time === "number") {
|
|
117
|
+
blockTimesByHeight[currentHeight] = block.time;
|
|
118
|
+
}
|
|
119
|
+
currentHashHex = block.previousblockhash ?? null;
|
|
120
|
+
currentHeight -= 1;
|
|
121
|
+
}
|
|
122
|
+
return blockTimesByHeight;
|
|
123
|
+
}
|
|
124
|
+
export function syncMiningVisualizerBalances(loopState, readContext, balanceSats) {
|
|
125
|
+
loopState.ui.fundingAddress = readContext.model?.walletAddress ?? readContext.localState.state.funding.address;
|
|
126
|
+
loopState.ui.balanceCogtoshi = readContext.snapshot === null
|
|
127
|
+
? null
|
|
128
|
+
: getBalance(readContext.snapshot.state, readContext.localState.state.funding.scriptPubKeyHex);
|
|
129
|
+
loopState.ui.balanceSats = balanceSats;
|
|
130
|
+
}
|
|
131
|
+
export function createIndexedMiningFollowVisualizerState(readContext) {
|
|
132
|
+
const uiState = createEmptyMiningFollowVisualizerState();
|
|
133
|
+
const localState = readContext.localState;
|
|
134
|
+
const settledBoard = resolveCurrentMinedBlockBoard({
|
|
135
|
+
snapshotState: readContext.snapshot?.state ?? null,
|
|
136
|
+
snapshotTipHeight: readContext.snapshot?.tip?.height ?? readContext.indexer.snapshotTip?.height ?? null,
|
|
137
|
+
snapshotTipPreviousHashHex: readContext.snapshot?.tip?.previousHashHex ?? readContext.indexer.snapshotTip?.previousHashHex ?? null,
|
|
138
|
+
});
|
|
139
|
+
uiState.settledBlockHeight = settledBoard.settledBlockHeight;
|
|
140
|
+
uiState.settledBoardEntries = settledBoard.settledBoardEntries;
|
|
141
|
+
if (localState.availability === "ready" && localState.state !== null) {
|
|
142
|
+
uiState.fundingAddress = readContext.model?.walletAddress ?? localState.state.funding.address;
|
|
143
|
+
}
|
|
144
|
+
if (readContext.snapshot !== null && localState.availability === "ready" && localState.state !== null) {
|
|
145
|
+
uiState.balanceCogtoshi = getBalance(readContext.snapshot.state, localState.state.funding.scriptPubKeyHex);
|
|
146
|
+
}
|
|
147
|
+
return uiState;
|
|
148
|
+
}
|
|
149
|
+
export function syncMiningVisualizerBlockTimes(loopState, blockTimesByHeight) {
|
|
150
|
+
loopState.ui.visibleBlockTimesByHeight = { ...blockTimesByHeight };
|
|
151
|
+
}
|
|
152
|
+
export function findRecentMiningWin(snapshotState, txid, targetBlockHeight) {
|
|
153
|
+
if (snapshotState === null || snapshotState === undefined || txid === null || targetBlockHeight === null) {
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
const winners = getBlockWinners(snapshotState, targetBlockHeight) ?? [];
|
|
157
|
+
const winner = winners.find((entry) => entry.txidHex === txid) ?? null;
|
|
158
|
+
if (winner === null) {
|
|
159
|
+
return null;
|
|
160
|
+
}
|
|
161
|
+
return {
|
|
162
|
+
rank: winner.rank,
|
|
163
|
+
rewardCogtoshi: winner.rewardCogtoshi,
|
|
164
|
+
blockHeight: winner.height,
|
|
165
|
+
};
|
|
166
|
+
}
|
|
@@ -25,6 +25,7 @@ export interface MiningRecentWinSummary {
|
|
|
25
25
|
export interface MiningFollowVisualizerState {
|
|
26
26
|
balanceCogtoshi: bigint | null;
|
|
27
27
|
balanceSats: bigint | null;
|
|
28
|
+
fundingAddress: string | null;
|
|
28
29
|
visibleBlockTimesByHeight: Record<number, number>;
|
|
29
30
|
settledBlockHeight: number | null;
|
|
30
31
|
settledBoardEntries: MiningSentenceBoardEntry[];
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createBootstrapProgress } from "../../bitcoind/progress/formatting.js";
|
|
2
|
-
import { normalizeInlineText, truncateLine } from "../../bitcoind/progress/formatting.js";
|
|
2
|
+
import { centerLine, normalizeInlineText, truncateLine } from "../../bitcoind/progress/formatting.js";
|
|
3
3
|
import { advanceFollowSceneState, createFollowSceneState, replaceFollowBlockTimes, syncFollowSceneState, } from "../../bitcoind/progress/follow-scene.js";
|
|
4
4
|
import { DEFAULT_RENDER_CLOCK, resolveTtyRenderPolicy, TtyRenderThrottle, } from "../../bitcoind/progress/render-policy.js";
|
|
5
5
|
import { TtyProgressRenderer } from "../../bitcoind/progress/tty-renderer.js";
|
|
@@ -125,18 +125,27 @@ function formatSentenceSlot(prefix, sentence, requiredWords, lineCount) {
|
|
|
125
125
|
return lines;
|
|
126
126
|
}
|
|
127
127
|
function formatSentenceRow(entry) {
|
|
128
|
-
|
|
128
|
+
const prefix = `${entry.rank}. @${entry.domainName}: `;
|
|
129
|
+
const highlightedSentence = highlightRequiredWords(normalizeInlineText(entry.sentence), entry.requiredWords);
|
|
130
|
+
return `${prefix}${highlightedSentence}`;
|
|
129
131
|
}
|
|
130
|
-
function
|
|
131
|
-
|
|
132
|
-
|
|
132
|
+
function formatSentenceBoardTitle(settledBlockHeight) {
|
|
133
|
+
return centerLine(`✎ Block #${settledBlockHeight ?? "-----"} Sentences ✎`, MINING_SENTENCE_BOARD_WRAP_WIDTH);
|
|
134
|
+
}
|
|
135
|
+
function formatProvisionalFundingLine(snapshot, ui) {
|
|
136
|
+
if (snapshot.currentPublishDecision === "publish-paused-insufficient-funds") {
|
|
137
|
+
const fundingAddress = normalizeInlineText(ui.fundingAddress ?? "");
|
|
138
|
+
return fundingAddress.length > 0
|
|
139
|
+
? `Deposit BTC to ${fundingAddress} to mine.`
|
|
140
|
+
: "Deposit BTC to this wallet address to mine.";
|
|
133
141
|
}
|
|
134
|
-
return
|
|
142
|
+
return "";
|
|
135
143
|
}
|
|
136
|
-
function formatProvisionalTxLinkLine(
|
|
137
|
-
if (
|
|
144
|
+
function formatProvisionalTxLinkLine(ui) {
|
|
145
|
+
if (ui.provisionalEntry.domainName === null || ui.provisionalEntry.sentence === null || ui.provisionalBroadcastTxid === null) {
|
|
138
146
|
return "";
|
|
139
147
|
}
|
|
148
|
+
const txid = ui.provisionalBroadcastTxid;
|
|
140
149
|
const normalizedTxid = normalizeInlineText(txid);
|
|
141
150
|
if (normalizedTxid.length === 0) {
|
|
142
151
|
return "";
|
|
@@ -145,14 +154,23 @@ function formatProvisionalTxLinkLine(entry, txid) {
|
|
|
145
154
|
}
|
|
146
155
|
function formatProvisionalSentenceRow(entry, requiredWords) {
|
|
147
156
|
if (entry.domainName === null || entry.sentence === null) {
|
|
148
|
-
return ["", ""
|
|
157
|
+
return ["", ""];
|
|
158
|
+
}
|
|
159
|
+
return formatSentenceSlot(`@${entry.domainName}: `, entry.sentence, requiredWords, 2);
|
|
160
|
+
}
|
|
161
|
+
function formatProvisionalSectionLines(snapshot, ui) {
|
|
162
|
+
const sentenceLines = formatProvisionalSentenceRow(ui.provisionalEntry, ui.provisionalRequiredWords);
|
|
163
|
+
const fundingLine = formatProvisionalFundingLine(snapshot, ui);
|
|
164
|
+
if (fundingLine.length > 0) {
|
|
165
|
+
return [...sentenceLines, fundingLine];
|
|
149
166
|
}
|
|
150
|
-
return
|
|
167
|
+
return [...sentenceLines, formatProvisionalTxLinkLine(ui)];
|
|
151
168
|
}
|
|
152
169
|
export function createEmptyMiningFollowVisualizerState() {
|
|
153
170
|
return {
|
|
154
171
|
balanceCogtoshi: null,
|
|
155
172
|
balanceSats: null,
|
|
173
|
+
fundingAddress: null,
|
|
156
174
|
visibleBlockTimesByHeight: {},
|
|
157
175
|
settledBlockHeight: null,
|
|
158
176
|
settledBoardEntries: [],
|
|
@@ -185,6 +203,7 @@ function cloneMiningFollowVisualizerState(state) {
|
|
|
185
203
|
...state.provisionalEntry,
|
|
186
204
|
},
|
|
187
205
|
provisionalBroadcastTxid: state.provisionalBroadcastTxid,
|
|
206
|
+
fundingAddress: state.fundingAddress,
|
|
188
207
|
recentWin: state.recentWin === null
|
|
189
208
|
? null
|
|
190
209
|
: {
|
|
@@ -378,18 +397,14 @@ export class MiningFollowVisualizer {
|
|
|
378
397
|
artworkCogText: formatCompactCogBalanceText(uiState.balanceCogtoshi),
|
|
379
398
|
artworkSatText: formatCompactSatBalanceText(uiState.balanceSats),
|
|
380
399
|
extraLines: [
|
|
381
|
-
|
|
382
|
-
"",
|
|
400
|
+
formatSentenceBoardTitle(uiState.settledBlockHeight),
|
|
383
401
|
...Array.from({ length: MINING_SENTENCE_BOARD_SIZE }, (_value, index) => {
|
|
384
402
|
const entry = uiState.settledBoardEntries[index];
|
|
385
403
|
return entry === undefined
|
|
386
|
-
?
|
|
404
|
+
? `${index + 1}.`
|
|
387
405
|
: formatSentenceRow(entry);
|
|
388
|
-
})
|
|
389
|
-
|
|
390
|
-
formatProvisionalTxLinkLine(uiState.provisionalEntry, uiState.provisionalBroadcastTxid),
|
|
391
|
-
formatRequiredWordsLine(uiState.provisionalRequiredWords),
|
|
392
|
-
...formatProvisionalSentenceRow(uiState.provisionalEntry, uiState.provisionalRequiredWords),
|
|
406
|
+
}),
|
|
407
|
+
...formatProvisionalSectionLines(snapshot, uiState),
|
|
393
408
|
],
|
|
394
409
|
};
|
|
395
410
|
if (this.#artworkStatusLeftText !== null) {
|
|
@@ -2,6 +2,8 @@ import { access, constants } from "node:fs/promises";
|
|
|
2
2
|
import { deserializeIndexerState, loadBundledGenesisParameters } from "@cogcoin/indexer";
|
|
3
3
|
import { readPackageVersionFromDisk } from "../../package-version.js";
|
|
4
4
|
import { attachOrStartIndexerDaemon, INDEXER_DAEMON_BACKGROUND_FOLLOW_RECOVERY_FAILED, probeIndexerDaemon, readObservedIndexerDaemonStatus, readSnapshotWithRetry, } from "../../bitcoind/indexer-daemon.js";
|
|
5
|
+
import { deriveManagedBitcoindWalletStatus, resolveManagedBitcoindProbeDecision, } from "../../bitcoind/managed-runtime/bitcoind-policy.js";
|
|
6
|
+
import { deriveManagedIndexerWalletStatus, resolveIndexerDaemonProbeDecision, } from "../../bitcoind/managed-runtime/indexer-policy.js";
|
|
5
7
|
import { createRpcClient } from "../../bitcoind/node.js";
|
|
6
8
|
import { UNINITIALIZED_WALLET_ROOT_ID } from "../../bitcoind/service-paths.js";
|
|
7
9
|
import { attachOrStartManagedBitcoindService, probeManagedBitcoindService, } from "../../bitcoind/service.js";
|
|
@@ -19,7 +21,6 @@ import { createDefaultWalletSecretProvider, createWalletSecretReference, inspect
|
|
|
19
21
|
import { describeClientPasswordLockedMessage, describeClientPasswordMigrationMessage, describeClientPasswordSetupMessage, } from "../state/client-password.js";
|
|
20
22
|
import { createWalletReadModel } from "./project.js";
|
|
21
23
|
const DEFAULT_SERVICE_START_TIMEOUT_MS = 10_000;
|
|
22
|
-
const STALE_HEARTBEAT_THRESHOLD_MS = 15_000;
|
|
23
24
|
const TOLERATED_NODE_HEADER_LEAD_BLOCKS = 2;
|
|
24
25
|
const TOLERATED_NODE_HEADER_LEAD_MESSAGE = "Bitcoin headers can briefly lead validated blocks; a short 1-2 block lead is normal and is being tolerated.";
|
|
25
26
|
const NODE_CATCHING_UP_MESSAGE = "Bitcoin Core is still catching up to headers.";
|
|
@@ -222,135 +223,6 @@ async function inspectWalletLocalState(options = {}) {
|
|
|
222
223
|
};
|
|
223
224
|
}
|
|
224
225
|
}
|
|
225
|
-
function mapIndexerStartupError(message) {
|
|
226
|
-
switch (message) {
|
|
227
|
-
case "indexer_daemon_start_timeout":
|
|
228
|
-
return {
|
|
229
|
-
health: "starting",
|
|
230
|
-
message: "Indexer daemon is still starting.",
|
|
231
|
-
};
|
|
232
|
-
case "indexer_daemon_service_version_mismatch":
|
|
233
|
-
return {
|
|
234
|
-
health: "service-version-mismatch",
|
|
235
|
-
message: "The live indexer daemon is running an incompatible service API version.",
|
|
236
|
-
};
|
|
237
|
-
case "indexer_daemon_schema_mismatch":
|
|
238
|
-
return {
|
|
239
|
-
health: "schema-mismatch",
|
|
240
|
-
message: "The live indexer daemon is using an incompatible sqlite schema.",
|
|
241
|
-
};
|
|
242
|
-
case "indexer_daemon_wallet_root_mismatch":
|
|
243
|
-
return {
|
|
244
|
-
health: "wallet-root-mismatch",
|
|
245
|
-
message: "The live indexer daemon belongs to a different wallet root.",
|
|
246
|
-
};
|
|
247
|
-
case "indexer_daemon_protocol_error":
|
|
248
|
-
return {
|
|
249
|
-
health: "unavailable",
|
|
250
|
-
message: "The live indexer daemon socket responded with an invalid or incomplete protocol exchange.",
|
|
251
|
-
};
|
|
252
|
-
case INDEXER_DAEMON_BACKGROUND_FOLLOW_RECOVERY_FAILED:
|
|
253
|
-
return {
|
|
254
|
-
health: "failed",
|
|
255
|
-
message: "The managed indexer daemon could not recover automatic background follow.",
|
|
256
|
-
};
|
|
257
|
-
default:
|
|
258
|
-
return {
|
|
259
|
-
health: "unavailable",
|
|
260
|
-
message,
|
|
261
|
-
};
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
function mapBitcoindStartupError(message) {
|
|
265
|
-
switch (message) {
|
|
266
|
-
case "managed_bitcoind_service_start_timeout":
|
|
267
|
-
return {
|
|
268
|
-
health: "starting",
|
|
269
|
-
status: null,
|
|
270
|
-
message: "Managed bitcoind service is still starting.",
|
|
271
|
-
};
|
|
272
|
-
case "managed_bitcoind_service_version_mismatch":
|
|
273
|
-
return {
|
|
274
|
-
health: "service-version-mismatch",
|
|
275
|
-
status: null,
|
|
276
|
-
message: "The live managed bitcoind service is running an incompatible service version.",
|
|
277
|
-
};
|
|
278
|
-
case "managed_bitcoind_wallet_root_mismatch":
|
|
279
|
-
return {
|
|
280
|
-
health: "wallet-root-mismatch",
|
|
281
|
-
status: null,
|
|
282
|
-
message: "The live managed bitcoind service belongs to a different wallet root.",
|
|
283
|
-
};
|
|
284
|
-
case "managed_bitcoind_runtime_mismatch":
|
|
285
|
-
return {
|
|
286
|
-
health: "runtime-mismatch",
|
|
287
|
-
status: null,
|
|
288
|
-
message: "The live managed bitcoind service runtime does not match this wallet's expected data directory or chain.",
|
|
289
|
-
};
|
|
290
|
-
case "managed_bitcoind_protocol_error":
|
|
291
|
-
return {
|
|
292
|
-
health: "unavailable",
|
|
293
|
-
status: null,
|
|
294
|
-
message: "The managed bitcoind runtime artifacts are invalid or incomplete.",
|
|
295
|
-
};
|
|
296
|
-
default:
|
|
297
|
-
return {
|
|
298
|
-
health: "unavailable",
|
|
299
|
-
status: null,
|
|
300
|
-
message,
|
|
301
|
-
};
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
function deriveBitcoindHealth(options) {
|
|
305
|
-
if (options.startupError !== null) {
|
|
306
|
-
const mapped = mapBitcoindStartupError(options.startupError);
|
|
307
|
-
return {
|
|
308
|
-
...mapped,
|
|
309
|
-
status: options.status,
|
|
310
|
-
};
|
|
311
|
-
}
|
|
312
|
-
if (options.status === null) {
|
|
313
|
-
return {
|
|
314
|
-
health: "unavailable",
|
|
315
|
-
status: null,
|
|
316
|
-
message: "Managed bitcoind service is unavailable.",
|
|
317
|
-
};
|
|
318
|
-
}
|
|
319
|
-
if (options.status.state === "starting") {
|
|
320
|
-
return {
|
|
321
|
-
health: "starting",
|
|
322
|
-
status: options.status,
|
|
323
|
-
message: options.status.lastError ?? "Managed bitcoind service is still starting.",
|
|
324
|
-
};
|
|
325
|
-
}
|
|
326
|
-
if (options.status.state === "failed") {
|
|
327
|
-
return {
|
|
328
|
-
health: "failed",
|
|
329
|
-
status: options.status,
|
|
330
|
-
message: options.status.lastError ?? "Managed bitcoind service refresh failed.",
|
|
331
|
-
};
|
|
332
|
-
}
|
|
333
|
-
const proofStatus = options.nodeStatus?.walletReplica?.proofStatus;
|
|
334
|
-
if (proofStatus === "missing") {
|
|
335
|
-
return {
|
|
336
|
-
health: "replica-missing",
|
|
337
|
-
status: options.status,
|
|
338
|
-
message: options.nodeStatus?.walletReplicaMessage ?? "Managed Core wallet replica is missing.",
|
|
339
|
-
};
|
|
340
|
-
}
|
|
341
|
-
if (proofStatus === "mismatch") {
|
|
342
|
-
return {
|
|
343
|
-
health: "replica-mismatch",
|
|
344
|
-
status: options.status,
|
|
345
|
-
message: options.nodeStatus?.walletReplicaMessage ?? "Managed Core wallet replica does not match trusted wallet state.",
|
|
346
|
-
};
|
|
347
|
-
}
|
|
348
|
-
return {
|
|
349
|
-
health: "ready",
|
|
350
|
-
status: options.status,
|
|
351
|
-
message: options.nodeStatus?.walletReplicaMessage ?? options.status.lastError,
|
|
352
|
-
};
|
|
353
|
-
}
|
|
354
226
|
function deriveNodeHealth(status, bitcoindHealth) {
|
|
355
227
|
if (bitcoindHealth !== "ready" || status === null || !status.ready) {
|
|
356
228
|
return {
|
|
@@ -381,58 +253,6 @@ function deriveNodeHealth(status, bitcoindHealth) {
|
|
|
381
253
|
export function deriveNodeHealthForTesting(status, bitcoindHealth) {
|
|
382
254
|
return deriveNodeHealth(status, bitcoindHealth);
|
|
383
255
|
}
|
|
384
|
-
function deriveIndexerHealth(options) {
|
|
385
|
-
const daemonStatus = options.source === "lease"
|
|
386
|
-
? options.daemonStatus
|
|
387
|
-
: options.observedStatus ?? options.daemonStatus;
|
|
388
|
-
const snapshotTip = options.snapshot?.tip ?? null;
|
|
389
|
-
const daemonInstanceId = options.snapshot?.daemonInstanceId ?? daemonStatus?.daemonInstanceId ?? null;
|
|
390
|
-
const snapshotSeq = options.snapshot?.snapshotSeq ?? daemonStatus?.snapshotSeq ?? null;
|
|
391
|
-
const openedAtUnixMs = options.snapshot?.openedAtUnixMs ?? null;
|
|
392
|
-
const source = daemonStatus === null && options.snapshot === null ? "none" : options.source;
|
|
393
|
-
const createResult = (health, message) => ({
|
|
394
|
-
health,
|
|
395
|
-
status: daemonStatus,
|
|
396
|
-
message,
|
|
397
|
-
snapshotTip,
|
|
398
|
-
source,
|
|
399
|
-
daemonInstanceId,
|
|
400
|
-
snapshotSeq,
|
|
401
|
-
openedAtUnixMs,
|
|
402
|
-
});
|
|
403
|
-
if (options.startupError !== null) {
|
|
404
|
-
const mapped = mapIndexerStartupError(options.startupError);
|
|
405
|
-
return createResult(mapped.health, mapped.message);
|
|
406
|
-
}
|
|
407
|
-
if (daemonStatus === null) {
|
|
408
|
-
return createResult("unavailable", "Indexer daemon is unavailable.");
|
|
409
|
-
}
|
|
410
|
-
if ((options.now - daemonStatus.heartbeatAtUnixMs) > STALE_HEARTBEAT_THRESHOLD_MS) {
|
|
411
|
-
return createResult("stale-heartbeat", "Indexer daemon heartbeat is stale.");
|
|
412
|
-
}
|
|
413
|
-
if (daemonStatus.state === "schema-mismatch") {
|
|
414
|
-
return createResult("schema-mismatch", daemonStatus.lastError ?? "Indexer daemon sqlite schema is incompatible.");
|
|
415
|
-
}
|
|
416
|
-
if (daemonStatus.state === "failed") {
|
|
417
|
-
return createResult("failed", daemonStatus.lastError ?? "Indexer daemon refresh failed.");
|
|
418
|
-
}
|
|
419
|
-
if (daemonStatus.state === "service-version-mismatch") {
|
|
420
|
-
return createResult("service-version-mismatch", "Indexer daemon service API is incompatible.");
|
|
421
|
-
}
|
|
422
|
-
if (options.snapshot === null) {
|
|
423
|
-
if (daemonStatus.state === "reorging") {
|
|
424
|
-
return createResult("reorging", "Indexer daemon is replaying a reorg and refreshing the coherent snapshot.");
|
|
425
|
-
}
|
|
426
|
-
return createResult(daemonStatus.state === "catching-up" ? "catching-up" : "starting", "Indexer snapshot is not ready yet.");
|
|
427
|
-
}
|
|
428
|
-
if (daemonStatus.state === "catching-up") {
|
|
429
|
-
return createResult("catching-up", "Indexer daemon is still catching up to the managed Bitcoin tip.");
|
|
430
|
-
}
|
|
431
|
-
if (daemonStatus.state === "reorging") {
|
|
432
|
-
return createResult("reorging", "Indexer daemon is replaying a reorg and refreshing the coherent snapshot.");
|
|
433
|
-
}
|
|
434
|
-
return createResult("synced", null);
|
|
435
|
-
}
|
|
436
256
|
async function attachNodeStatus(options) {
|
|
437
257
|
try {
|
|
438
258
|
const probe = await probeManagedBitcoindService({
|
|
@@ -442,13 +262,14 @@ async function attachNodeStatus(options) {
|
|
|
442
262
|
walletRootId: options.walletRootId,
|
|
443
263
|
startupTimeoutMs: options.startupTimeoutMs,
|
|
444
264
|
});
|
|
445
|
-
|
|
265
|
+
const decision = resolveManagedBitcoindProbeDecision(probe);
|
|
266
|
+
if (decision.action === "reject") {
|
|
446
267
|
return {
|
|
447
268
|
handle: null,
|
|
448
269
|
rpc: null,
|
|
449
270
|
status: null,
|
|
450
271
|
observedStatus: probe.status,
|
|
451
|
-
error:
|
|
272
|
+
error: decision.error,
|
|
452
273
|
};
|
|
453
274
|
}
|
|
454
275
|
const genesis = await loadBundledGenesisParameters();
|
|
@@ -539,7 +360,7 @@ export async function openWalletReadContext(options) {
|
|
|
539
360
|
walletReplicaMessage: verifiedReplica.message ?? null,
|
|
540
361
|
};
|
|
541
362
|
}
|
|
542
|
-
const bitcoind =
|
|
363
|
+
const bitcoind = deriveManagedBitcoindWalletStatus({
|
|
543
364
|
status: node.observedStatus,
|
|
544
365
|
nodeStatus: node.status,
|
|
545
366
|
startupError: node.error,
|
|
@@ -556,7 +377,11 @@ export async function openWalletReadContext(options) {
|
|
|
556
377
|
dataDir: options.dataDir,
|
|
557
378
|
walletRootId,
|
|
558
379
|
});
|
|
559
|
-
|
|
380
|
+
const probeDecision = resolveIndexerDaemonProbeDecision({
|
|
381
|
+
probe,
|
|
382
|
+
expectedBinaryVersion: expectedIndexerBinaryVersion,
|
|
383
|
+
});
|
|
384
|
+
if (probeDecision.action !== "reject") {
|
|
560
385
|
await probe.client?.close().catch(() => undefined);
|
|
561
386
|
daemonClient = await attachOrStartIndexerDaemon({
|
|
562
387
|
dataDir: options.dataDir,
|
|
@@ -570,7 +395,7 @@ export async function openWalletReadContext(options) {
|
|
|
570
395
|
else {
|
|
571
396
|
observedDaemonStatus = probe.status;
|
|
572
397
|
indexerSource = probe.status === null ? "none" : "probe";
|
|
573
|
-
daemonError =
|
|
398
|
+
daemonError = probeDecision.error;
|
|
574
399
|
}
|
|
575
400
|
if (daemonClient !== null) {
|
|
576
401
|
const lease = await readSnapshotWithRetry(daemonClient, walletRootId);
|
|
@@ -604,7 +429,7 @@ export async function openWalletReadContext(options) {
|
|
|
604
429
|
}
|
|
605
430
|
}
|
|
606
431
|
}
|
|
607
|
-
const indexer =
|
|
432
|
+
const indexer = deriveManagedIndexerWalletStatus({
|
|
608
433
|
daemonStatus,
|
|
609
434
|
observedStatus: observedDaemonStatus,
|
|
610
435
|
snapshot,
|
package/dist/wallet/reset.d.ts
CHANGED
|
@@ -38,7 +38,7 @@ export interface WalletResetPreview {
|
|
|
38
38
|
confirmationPhrase: "permanently reset";
|
|
39
39
|
walletPrompt: null | {
|
|
40
40
|
defaultAction: "retain-mnemonic";
|
|
41
|
-
acceptedInputs: ["", "skip", "
|
|
41
|
+
acceptedInputs: ["", "skip", "clear wallet entropy"];
|
|
42
42
|
entropyRetainingResetAvailable: boolean;
|
|
43
43
|
envelopeSource: "primary" | "backup" | null;
|
|
44
44
|
};
|