@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,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) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { readSnapshotWithRetry } from "../../bitcoind/indexer-daemon.js";
|
|
2
2
|
import { type WalletSecretProvider } from "../state/provider.js";
|
|
3
|
-
import type { WalletLocalStateStatus, WalletReadContext } from "./types.js";
|
|
3
|
+
import type { WalletBitcoindStatus, WalletLocalStateStatus, WalletNodeStatus, WalletReadContext, WalletServiceHealth } from "./types.js";
|
|
4
4
|
import type { WalletRuntimePaths } from "../runtime.js";
|
|
5
5
|
declare function inspectWalletLocalState(options?: {
|
|
6
6
|
dataDir?: string;
|
|
@@ -9,6 +9,10 @@ declare function inspectWalletLocalState(options?: {
|
|
|
9
9
|
paths?: WalletRuntimePaths;
|
|
10
10
|
walletControlLockHeld?: boolean;
|
|
11
11
|
}): Promise<WalletLocalStateStatus>;
|
|
12
|
+
export declare function deriveNodeHealthForTesting(status: WalletNodeStatus | null, bitcoindHealth: WalletBitcoindStatus["health"]): {
|
|
13
|
+
health: WalletServiceHealth;
|
|
14
|
+
message: string | null;
|
|
15
|
+
};
|
|
12
16
|
export declare function openWalletReadContext(options: {
|
|
13
17
|
dataDir: string;
|
|
14
18
|
databasePath: string;
|
|
@@ -20,6 +20,9 @@ import { describeClientPasswordLockedMessage, describeClientPasswordMigrationMes
|
|
|
20
20
|
import { createWalletReadModel } from "./project.js";
|
|
21
21
|
const DEFAULT_SERVICE_START_TIMEOUT_MS = 10_000;
|
|
22
22
|
const STALE_HEARTBEAT_THRESHOLD_MS = 15_000;
|
|
23
|
+
const TOLERATED_NODE_HEADER_LEAD_BLOCKS = 2;
|
|
24
|
+
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
|
+
const NODE_CATCHING_UP_MESSAGE = "Bitcoin Core is still catching up to headers.";
|
|
23
26
|
function btcAmountToSats(value) {
|
|
24
27
|
return BigInt(Math.round(value * 100_000_000));
|
|
25
28
|
}
|
|
@@ -351,14 +354,23 @@ function deriveBitcoindHealth(options) {
|
|
|
351
354
|
function deriveNodeHealth(status, bitcoindHealth) {
|
|
352
355
|
if (bitcoindHealth !== "ready" || status === null || !status.ready) {
|
|
353
356
|
return {
|
|
354
|
-
health: "
|
|
355
|
-
message:
|
|
357
|
+
health: "catching-up",
|
|
358
|
+
message: NODE_CATCHING_UP_MESSAGE,
|
|
356
359
|
};
|
|
357
360
|
}
|
|
358
|
-
|
|
361
|
+
const headerLead = status.nodeBestHeight !== null && status.nodeHeaderHeight !== null
|
|
362
|
+
? status.nodeHeaderHeight - status.nodeBestHeight
|
|
363
|
+
: null;
|
|
364
|
+
if (headerLead !== null && headerLead > 0) {
|
|
365
|
+
if (headerLead <= TOLERATED_NODE_HEADER_LEAD_BLOCKS) {
|
|
366
|
+
return {
|
|
367
|
+
health: "synced",
|
|
368
|
+
message: TOLERATED_NODE_HEADER_LEAD_MESSAGE,
|
|
369
|
+
};
|
|
370
|
+
}
|
|
359
371
|
return {
|
|
360
372
|
health: "catching-up",
|
|
361
|
-
message:
|
|
373
|
+
message: NODE_CATCHING_UP_MESSAGE,
|
|
362
374
|
};
|
|
363
375
|
}
|
|
364
376
|
return {
|
|
@@ -366,6 +378,9 @@ function deriveNodeHealth(status, bitcoindHealth) {
|
|
|
366
378
|
message: null,
|
|
367
379
|
};
|
|
368
380
|
}
|
|
381
|
+
export function deriveNodeHealthForTesting(status, bitcoindHealth) {
|
|
382
|
+
return deriveNodeHealth(status, bitcoindHealth);
|
|
383
|
+
}
|
|
369
384
|
function deriveIndexerHealth(options) {
|
|
370
385
|
const daemonStatus = options.source === "lease"
|
|
371
386
|
? options.daemonStatus
|
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
|
};
|
package/dist/wallet/reset.js
CHANGED
|
@@ -14,7 +14,6 @@ import { loadMiningRuntimeStatus } from "./mining/runtime-artifacts.js";
|
|
|
14
14
|
import { clearLegacyWalletLockArtifacts, withUnlockedManagedCoreWallet } from "./managed-core-wallet.js";
|
|
15
15
|
import { resolveWalletRuntimePathsForTesting } from "./runtime.js";
|
|
16
16
|
import { createDefaultWalletSecretProvider, createWalletRootId, createWalletSecretReference, } from "./state/provider.js";
|
|
17
|
-
import { loadWalletSeedIndex } from "./state/seed-index.js";
|
|
18
17
|
import { extractWalletRootIdHintFromWalletStateEnvelope, loadRawWalletStateEnvelope, loadWalletState, saveWalletState, } from "./state/storage.js";
|
|
19
18
|
import { confirmTypedAcknowledgement } from "./tx/confirm.js";
|
|
20
19
|
function sanitizeWalletName(walletRootId) {
|
|
@@ -50,6 +49,36 @@ async function readJsonFileOrNull(path) {
|
|
|
50
49
|
return null;
|
|
51
50
|
}
|
|
52
51
|
}
|
|
52
|
+
async function collectLegacyImportedSeedSecretProviderKeyIds(paths) {
|
|
53
|
+
const seedsRoot = join(paths.stateRoot, "seeds");
|
|
54
|
+
const entries = await readdir(seedsRoot, { withFileTypes: true }).catch((error) => {
|
|
55
|
+
if (error instanceof Error && "code" in error && error.code === "ENOENT") {
|
|
56
|
+
return [];
|
|
57
|
+
}
|
|
58
|
+
throw error;
|
|
59
|
+
});
|
|
60
|
+
const keyIds = new Set();
|
|
61
|
+
for (const entry of entries) {
|
|
62
|
+
if (!entry.isDirectory()) {
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
const seedRoot = join(seedsRoot, entry.name);
|
|
66
|
+
const candidatePaths = [
|
|
67
|
+
join(seedRoot, "wallet-state.enc"),
|
|
68
|
+
join(seedRoot, "wallet-state.enc.bak"),
|
|
69
|
+
join(seedRoot, "wallet-init-pending.enc"),
|
|
70
|
+
join(seedRoot, "wallet-init-pending.enc.bak"),
|
|
71
|
+
];
|
|
72
|
+
for (const candidatePath of candidatePaths) {
|
|
73
|
+
const envelope = await readJsonFileOrNull(candidatePath);
|
|
74
|
+
const keyId = envelope?.secretProvider?.keyId?.trim() ?? "";
|
|
75
|
+
if (keyId.length > 0) {
|
|
76
|
+
keyIds.add(keyId);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return [...keyIds].sort((left, right) => left.localeCompare(right));
|
|
81
|
+
}
|
|
53
82
|
async function isProcessAlive(pid) {
|
|
54
83
|
if (pid === null) {
|
|
55
84
|
return false;
|
|
@@ -454,12 +483,7 @@ async function preflightReset(options) {
|
|
|
454
483
|
}
|
|
455
484
|
const tracked = await collectTrackedManagedProcesses(options.paths);
|
|
456
485
|
const secretProviderKeyId = rawEnvelope?.envelope.secretProvider?.keyId ?? null;
|
|
457
|
-
const
|
|
458
|
-
paths: options.paths,
|
|
459
|
-
}).catch(() => null);
|
|
460
|
-
const importedSeedSecretProviderKeyIds = [...new Set((seedIndex?.seeds ?? [])
|
|
461
|
-
.filter((seed) => seed.kind === "imported")
|
|
462
|
-
.map((seed) => createWalletSecretReference(seed.walletRootId).keyId))];
|
|
486
|
+
const importedSeedSecretProviderKeyIds = await collectLegacyImportedSeedSecretProviderKeyIds(options.paths);
|
|
463
487
|
return {
|
|
464
488
|
dataRoot: options.paths.dataRoot,
|
|
465
489
|
removedRoots,
|
|
@@ -555,8 +579,8 @@ async function resolveResetExecutionDecision(options) {
|
|
|
555
579
|
let walletChoice = "";
|
|
556
580
|
let loadedWalletForEntropyReset = null;
|
|
557
581
|
if (options.preflight.wallet.present) {
|
|
558
|
-
const answer = (await options.prompter.prompt("Wallet reset choice ([Enter] retain base entropy, \"skip\", or \"
|
|
559
|
-
if (answer !== "" && answer !== "skip" && answer !== "
|
|
582
|
+
const answer = (await options.prompter.prompt("Wallet reset choice ([Enter] retain base entropy, \"skip\", or \"clear wallet entropy\"): ")).trim();
|
|
583
|
+
if (answer !== "" && answer !== "skip" && answer !== "clear wallet entropy") {
|
|
560
584
|
throw new Error("reset_wallet_choice_invalid");
|
|
561
585
|
}
|
|
562
586
|
walletChoice = answer;
|
|
@@ -592,7 +616,7 @@ function determineWalletAction(walletPresent, walletChoice) {
|
|
|
592
616
|
if (walletChoice === "skip") {
|
|
593
617
|
return "kept-unchanged";
|
|
594
618
|
}
|
|
595
|
-
if (walletChoice === "
|
|
619
|
+
if (walletChoice === "clear wallet entropy") {
|
|
596
620
|
return "deleted";
|
|
597
621
|
}
|
|
598
622
|
return "retain-mnemonic";
|
|
@@ -636,7 +660,7 @@ export async function previewResetWallet(options) {
|
|
|
636
660
|
walletPrompt: preflight.wallet.present
|
|
637
661
|
? {
|
|
638
662
|
defaultAction: "retain-mnemonic",
|
|
639
|
-
acceptedInputs: ["", "skip", "
|
|
663
|
+
acceptedInputs: ["", "skip", "clear wallet entropy"],
|
|
640
664
|
entropyRetainingResetAvailable: preflight.wallet.mode === "provider-backed",
|
|
641
665
|
envelopeSource: preflight.wallet.envelopeSource,
|
|
642
666
|
}
|
package/dist/wallet/runtime.d.ts
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import type { CogcoinPathResolution } from "../app-paths.js";
|
|
2
|
-
export type WalletSeedKind = "main" | "imported";
|
|
3
2
|
export interface WalletRuntimePathResolution extends CogcoinPathResolution {
|
|
4
|
-
seedName?: string | null;
|
|
5
3
|
}
|
|
6
4
|
export interface WalletRuntimePaths {
|
|
7
5
|
dataRoot: string;
|
|
@@ -11,9 +9,6 @@ export interface WalletRuntimePaths {
|
|
|
11
9
|
walletRuntimeRoot: string;
|
|
12
10
|
stateRoot: string;
|
|
13
11
|
walletStateRoot: string;
|
|
14
|
-
seedRegistryPath: string;
|
|
15
|
-
selectedSeedName: string;
|
|
16
|
-
selectedSeedKind: WalletSeedKind;
|
|
17
12
|
bitcoinDataDir: string;
|
|
18
13
|
indexerRoot: string;
|
|
19
14
|
walletStateDirectory: string;
|
|
@@ -31,5 +26,4 @@ export interface WalletRuntimePaths {
|
|
|
31
26
|
miningEventsPath: string;
|
|
32
27
|
miningControlLockPath: string;
|
|
33
28
|
}
|
|
34
|
-
export declare function deriveWalletRuntimePathsForSeed(basePaths: WalletRuntimePaths, seedName: string | null | undefined): WalletRuntimePaths;
|
|
35
29
|
export declare function resolveWalletRuntimePathsForTesting(resolution?: WalletRuntimePathResolution): WalletRuntimePaths;
|
package/dist/wallet/runtime.js
CHANGED
|
@@ -1,41 +1,8 @@
|
|
|
1
1
|
import { join } from "node:path";
|
|
2
2
|
import { resolveCogcoinPathsForTesting } from "../app-paths.js";
|
|
3
|
-
function resolveSeedLayout(sharedStateRoot, sharedRuntimeRoot, seedName) {
|
|
4
|
-
if (seedName === "main") {
|
|
5
|
-
return {
|
|
6
|
-
seedKind: "main",
|
|
7
|
-
walletStateRoot: sharedStateRoot,
|
|
8
|
-
walletRuntimeRoot: sharedRuntimeRoot,
|
|
9
|
-
};
|
|
10
|
-
}
|
|
11
|
-
return {
|
|
12
|
-
seedKind: "imported",
|
|
13
|
-
walletStateRoot: join(sharedStateRoot, "seeds", seedName),
|
|
14
|
-
walletRuntimeRoot: join(sharedRuntimeRoot, "seeds", seedName),
|
|
15
|
-
};
|
|
16
|
-
}
|
|
17
|
-
export function deriveWalletRuntimePathsForSeed(basePaths, seedName) {
|
|
18
|
-
const resolvedSeedName = seedName ?? "main";
|
|
19
|
-
const seedLayout = resolveSeedLayout(basePaths.stateRoot, basePaths.runtimeRoot, resolvedSeedName);
|
|
20
|
-
return {
|
|
21
|
-
...basePaths,
|
|
22
|
-
walletRuntimeRoot: seedLayout.walletRuntimeRoot,
|
|
23
|
-
walletStateRoot: seedLayout.walletStateRoot,
|
|
24
|
-
selectedSeedName: resolvedSeedName,
|
|
25
|
-
selectedSeedKind: seedLayout.seedKind,
|
|
26
|
-
walletStateDirectory: seedLayout.walletStateRoot,
|
|
27
|
-
walletStatePath: join(seedLayout.walletStateRoot, "wallet-state.enc"),
|
|
28
|
-
walletStateBackupPath: join(seedLayout.walletStateRoot, "wallet-state.enc.bak"),
|
|
29
|
-
walletInitPendingPath: join(seedLayout.walletStateRoot, "wallet-init-pending.enc"),
|
|
30
|
-
walletInitPendingBackupPath: join(seedLayout.walletStateRoot, "wallet-init-pending.enc.bak"),
|
|
31
|
-
miningRoot: join(seedLayout.walletRuntimeRoot, "mining"),
|
|
32
|
-
miningStatusPath: join(seedLayout.walletRuntimeRoot, "mining", "status.json"),
|
|
33
|
-
miningEventsPath: join(seedLayout.walletRuntimeRoot, "mining", "events.jsonl"),
|
|
34
|
-
};
|
|
35
|
-
}
|
|
36
3
|
export function resolveWalletRuntimePathsForTesting(resolution = {}) {
|
|
37
4
|
const paths = resolveCogcoinPathsForTesting(resolution);
|
|
38
|
-
return
|
|
5
|
+
return {
|
|
39
6
|
dataRoot: paths.dataRoot,
|
|
40
7
|
clientDataDir: paths.clientDataDir,
|
|
41
8
|
clientConfigPath: paths.clientConfigPath,
|
|
@@ -43,9 +10,6 @@ export function resolveWalletRuntimePathsForTesting(resolution = {}) {
|
|
|
43
10
|
walletRuntimeRoot: paths.runtimeRoot,
|
|
44
11
|
stateRoot: paths.stateRoot,
|
|
45
12
|
walletStateRoot: paths.stateRoot,
|
|
46
|
-
seedRegistryPath: join(paths.stateRoot, "seed-index.json"),
|
|
47
|
-
selectedSeedName: "main",
|
|
48
|
-
selectedSeedKind: "main",
|
|
49
13
|
bitcoinDataDir: paths.bitcoinDataDir,
|
|
50
14
|
indexerRoot: paths.indexerRoot,
|
|
51
15
|
walletStateDirectory: paths.stateRoot,
|
|
@@ -62,5 +26,5 @@ export function resolveWalletRuntimePathsForTesting(resolution = {}) {
|
|
|
62
26
|
miningStatusPath: join(paths.runtimeRoot, "mining", "status.json"),
|
|
63
27
|
miningEventsPath: join(paths.runtimeRoot, "mining", "events.jsonl"),
|
|
64
28
|
miningControlLockPath: paths.miningControlLockPath,
|
|
65
|
-
}
|
|
29
|
+
};
|
|
66
30
|
}
|
|
@@ -168,6 +168,24 @@ export declare function buildWalletMutationTransaction<TPlan>(options: {
|
|
|
168
168
|
availableFundingMinConf?: number;
|
|
169
169
|
temporarilyUnlockedPolicyOutpoints?: readonly OutpointRecord[];
|
|
170
170
|
}): Promise<BuiltWalletMutationTransaction>;
|
|
171
|
+
export declare function fundAndValidateWalletMutationDraft<TPlan>(options: {
|
|
172
|
+
rpc: WalletMutationRpcClient;
|
|
173
|
+
walletName: string;
|
|
174
|
+
plan: TPlan & {
|
|
175
|
+
fixedInputs: FixedWalletInput[];
|
|
176
|
+
outputs: unknown[];
|
|
177
|
+
changeAddress: string;
|
|
178
|
+
changePosition?: number | null;
|
|
179
|
+
allowedFundingScriptPubKeyHex: string;
|
|
180
|
+
eligibleFundingOutpointKeys: Set<string>;
|
|
181
|
+
};
|
|
182
|
+
validateFundedDraft(decoded: RpcDecodedPsbt, funded: RpcWalletCreateFundedPsbtResult, plan: TPlan): void;
|
|
183
|
+
feeRate?: number;
|
|
184
|
+
availableFundingMinConf?: number;
|
|
185
|
+
}): Promise<{
|
|
186
|
+
funded: RpcWalletCreateFundedPsbtResult;
|
|
187
|
+
decoded: RpcDecodedPsbt;
|
|
188
|
+
}>;
|
|
171
189
|
export declare function buildWalletMutationTransactionWithReserveFallback<TPlan>(options: {
|
|
172
190
|
rpc: WalletMutationRpcClient;
|
|
173
191
|
walletName: string;
|