@cogcoin/client 1.0.2 → 1.1.0
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 +3 -2
- package/dist/bitcoind/client/factory.d.ts +0 -8
- package/dist/bitcoind/client/factory.js +1 -59
- package/dist/bitcoind/client/managed-client.d.ts +1 -3
- package/dist/bitcoind/client/managed-client.js +3 -47
- package/dist/bitcoind/indexer-daemon-main.js +173 -28
- package/dist/bitcoind/indexer-daemon.d.ts +11 -3
- package/dist/bitcoind/indexer-daemon.js +123 -57
- package/dist/bitcoind/indexer-monitor.d.ts +12 -0
- package/dist/bitcoind/indexer-monitor.js +89 -0
- package/dist/bitcoind/progress/follow-scene.d.ts +7 -1
- package/dist/bitcoind/progress/follow-scene.js +87 -4
- package/dist/bitcoind/progress/tty-renderer.d.ts +2 -0
- package/dist/bitcoind/progress/tty-renderer.js +2 -0
- package/dist/bitcoind/testing.d.ts +0 -1
- package/dist/bitcoind/testing.js +0 -1
- package/dist/bitcoind/types.d.ts +5 -2
- package/dist/cli/commands/follow.js +44 -49
- package/dist/cli/commands/mining-admin.js +56 -2
- package/dist/cli/commands/mining-read.js +43 -3
- package/dist/cli/commands/mining-runtime.js +91 -73
- package/dist/cli/commands/service-runtime.js +42 -2
- package/dist/cli/commands/status.js +3 -1
- package/dist/cli/commands/sync.js +50 -90
- package/dist/cli/commands/wallet-admin.js +21 -3
- package/dist/cli/commands/wallet-read.js +2 -0
- package/dist/cli/context.js +5 -1
- package/dist/cli/managed-indexer-observer.d.ts +33 -0
- package/dist/cli/managed-indexer-observer.js +163 -0
- package/dist/cli/mining-format.d.ts +3 -1
- package/dist/cli/mining-format.js +35 -0
- package/dist/cli/mining-json.d.ts +11 -1
- package/dist/cli/mining-json.js +9 -0
- package/dist/cli/output.js +24 -0
- package/dist/cli/parse.d.ts +1 -1
- package/dist/cli/parse.js +23 -0
- package/dist/cli/read-json.d.ts +13 -1
- package/dist/cli/read-json.js +31 -0
- package/dist/cli/runner.js +4 -2
- package/dist/cli/signals.d.ts +12 -0
- package/dist/cli/signals.js +31 -13
- package/dist/cli/types.d.ts +8 -4
- package/dist/cli/update-service.d.ts +2 -12
- package/dist/cli/update-service.js +2 -68
- package/dist/semver.d.ts +12 -0
- package/dist/semver.js +68 -0
- package/dist/wallet/lifecycle.js +0 -6
- package/dist/wallet/mining/config.js +54 -3
- package/dist/wallet/mining/control.d.ts +5 -2
- package/dist/wallet/mining/control.js +153 -34
- package/dist/wallet/mining/domain-prompts.d.ts +17 -0
- package/dist/wallet/mining/domain-prompts.js +130 -0
- package/dist/wallet/mining/index.d.ts +2 -1
- package/dist/wallet/mining/index.js +1 -0
- package/dist/wallet/mining/runner.d.ts +58 -2
- package/dist/wallet/mining/runner.js +553 -331
- package/dist/wallet/mining/sentence-protocol.d.ts +1 -0
- package/dist/wallet/mining/sentences.js +7 -4
- package/dist/wallet/mining/types.d.ts +26 -0
- package/dist/wallet/mining/visualizer.d.ts +3 -0
- package/dist/wallet/mining/visualizer.js +106 -12
- package/dist/wallet/read/context.d.ts +1 -0
- package/dist/wallet/read/context.js +15 -10
- package/dist/wallet/reset.js +0 -1
- package/dist/wallet/state/client-password-agent.js +4 -1
- package/dist/wallet/state/client-password.js +15 -8
- package/dist/wallet/tx/anchor.js +0 -1
- package/dist/wallet/tx/bitcoin-transfer.js +0 -1
- package/dist/wallet/tx/cog.js +0 -3
- package/dist/wallet/tx/common.js +1 -1
- package/dist/wallet/tx/domain-admin.js +0 -1
- package/dist/wallet/tx/domain-market.js +0 -3
- package/dist/wallet/tx/field.js +0 -1
- package/dist/wallet/tx/register.js +0 -1
- package/dist/wallet/tx/reputation.js +0 -1
- package/package.json +1 -1
|
@@ -25,9 +25,13 @@ function buildSystemPrompt(extraPrompt) {
|
|
|
25
25
|
"Every sentence must be a single natural-language sentence.",
|
|
26
26
|
"Do not add commentary, markdown, or code fences.",
|
|
27
27
|
"Do not invent domain IDs or request IDs.",
|
|
28
|
+
"Each rootDomains entry may include an extraPrompt that applies only to that domain.",
|
|
29
|
+
"If rootDomains[i].extraPrompt is present, use it only for candidates for that domainId.",
|
|
30
|
+
"If rootDomains[i].extraPrompt is null, fall back to the request-level extraPrompt when it is present.",
|
|
31
|
+
"Never apply one domain's prompt to another domain's candidates.",
|
|
28
32
|
];
|
|
29
33
|
if (extraPrompt !== null && extraPrompt.trim().length > 0) {
|
|
30
|
-
lines.push(`
|
|
34
|
+
lines.push(`Request-level fallback instruction: ${extraPrompt.trim()}`);
|
|
31
35
|
}
|
|
32
36
|
return lines.join("\n");
|
|
33
37
|
}
|
|
@@ -86,7 +90,7 @@ async function requestBuiltInSentences(options) {
|
|
|
86
90
|
input: [
|
|
87
91
|
{
|
|
88
92
|
role: "system",
|
|
89
|
-
content: buildSystemPrompt(options.extraPrompt),
|
|
93
|
+
content: buildSystemPrompt(options.request.extraPrompt),
|
|
90
94
|
},
|
|
91
95
|
{
|
|
92
96
|
role: "user",
|
|
@@ -133,7 +137,7 @@ async function requestBuiltInSentences(options) {
|
|
|
133
137
|
body: JSON.stringify({
|
|
134
138
|
model,
|
|
135
139
|
max_tokens: 1_200,
|
|
136
|
-
system: buildSystemPrompt(options.extraPrompt),
|
|
140
|
+
system: buildSystemPrompt(options.request.extraPrompt),
|
|
137
141
|
messages: [
|
|
138
142
|
{
|
|
139
143
|
role: "user",
|
|
@@ -245,7 +249,6 @@ export async function generateMiningSentences(request, options) {
|
|
|
245
249
|
provider: builtIn.provider,
|
|
246
250
|
apiKey: builtIn.apiKey,
|
|
247
251
|
modelOverride: builtIn.modelOverride,
|
|
248
|
-
extraPrompt: builtIn.extraPrompt ?? request.extraPrompt,
|
|
249
252
|
request,
|
|
250
253
|
fetchImpl: options.fetchImpl,
|
|
251
254
|
signal: options.signal,
|
|
@@ -10,12 +10,38 @@ export interface MiningProviderConfigRecord {
|
|
|
10
10
|
modelSelectionSource: MiningModelSelectionSource;
|
|
11
11
|
updatedAtUnixMs: number;
|
|
12
12
|
}
|
|
13
|
+
export type MiningProviderConfigByProvider = Partial<Record<MiningProviderKind, MiningProviderConfigRecord>>;
|
|
13
14
|
export interface ClientConfigV1 {
|
|
14
15
|
schemaVersion: 1;
|
|
15
16
|
mining: {
|
|
16
17
|
builtIn: MiningProviderConfigRecord | null;
|
|
18
|
+
builtInByProvider?: MiningProviderConfigByProvider;
|
|
19
|
+
domainExtraPrompts: Record<string, string>;
|
|
17
20
|
};
|
|
18
21
|
}
|
|
22
|
+
export interface MiningDomainPromptEntry {
|
|
23
|
+
domain: {
|
|
24
|
+
name: string;
|
|
25
|
+
domainId: number | null;
|
|
26
|
+
};
|
|
27
|
+
mineable: boolean;
|
|
28
|
+
prompt: string | null;
|
|
29
|
+
effectivePromptSource: "domain" | "global-fallback" | "none";
|
|
30
|
+
}
|
|
31
|
+
export interface MiningDomainPromptListResult {
|
|
32
|
+
fallbackPromptConfigured: boolean;
|
|
33
|
+
prompts: MiningDomainPromptEntry[];
|
|
34
|
+
}
|
|
35
|
+
export interface MiningDomainPromptMutationResult {
|
|
36
|
+
domain: {
|
|
37
|
+
name: string;
|
|
38
|
+
domainId: number | null;
|
|
39
|
+
};
|
|
40
|
+
previousPrompt: string | null;
|
|
41
|
+
prompt: string | null;
|
|
42
|
+
status: "updated" | "cleared";
|
|
43
|
+
fallbackPromptConfigured: boolean;
|
|
44
|
+
}
|
|
19
45
|
export interface MiningEventRecord {
|
|
20
46
|
schemaVersion: 1;
|
|
21
47
|
timestampUnixMs: number;
|
|
@@ -11,6 +11,7 @@ export interface MiningSentenceBoardEntry {
|
|
|
11
11
|
rank: number;
|
|
12
12
|
domainName: string;
|
|
13
13
|
sentence: string;
|
|
14
|
+
requiredWords: readonly string[];
|
|
14
15
|
}
|
|
15
16
|
export interface MiningProvisionalSentenceEntry {
|
|
16
17
|
domainName: string | null;
|
|
@@ -44,6 +45,8 @@ export declare class MiningFollowVisualizer {
|
|
|
44
45
|
platform?: NodeJS.Platform;
|
|
45
46
|
env?: NodeJS.ProcessEnv;
|
|
46
47
|
clock?: RenderClock;
|
|
48
|
+
clientVersion?: string | null;
|
|
49
|
+
updateAvailable?: boolean;
|
|
47
50
|
rendererFactory?: (stream: TtyRenderStream) => VisualizerRendererLike;
|
|
48
51
|
});
|
|
49
52
|
update(snapshot: MiningRuntimeStatusV1, uiState?: MiningFollowVisualizerState): void;
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { createBootstrapProgress } from "../../bitcoind/progress/formatting.js";
|
|
2
|
+
import { normalizeInlineText, truncateLine } from "../../bitcoind/progress/formatting.js";
|
|
2
3
|
import { advanceFollowSceneState, createFollowSceneState, replaceFollowBlockTimes, syncFollowSceneState, } from "../../bitcoind/progress/follow-scene.js";
|
|
3
4
|
import { DEFAULT_RENDER_CLOCK, resolveTtyRenderPolicy, TtyRenderThrottle, } from "../../bitcoind/progress/render-policy.js";
|
|
4
5
|
import { TtyProgressRenderer } from "../../bitcoind/progress/tty-renderer.js";
|
|
5
6
|
const MINING_ARTWORK_COG_WIDTH = 22;
|
|
6
7
|
const MINING_SENTENCE_BOARD_SIZE = 5;
|
|
8
|
+
const MINING_SENTENCE_BOARD_WRAP_WIDTH = 80;
|
|
7
9
|
function formatCogAmountWithDecimals(value, { maxFractionDigits, minFractionDigits, }) {
|
|
8
10
|
const sign = value < 0n ? "-" : "";
|
|
9
11
|
const absolute = value < 0n ? -value : value;
|
|
@@ -44,14 +46,86 @@ function formatCompactCogBalanceText(balanceCogtoshi) {
|
|
|
44
46
|
function formatCompactSatBalanceText(balanceSats) {
|
|
45
47
|
return balanceSats === null ? null : `${balanceSats.toString()} SAT`;
|
|
46
48
|
}
|
|
49
|
+
function formatArtworkVersionText(clientVersion) {
|
|
50
|
+
if (typeof clientVersion !== "string") {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
const normalizedVersion = normalizeInlineText(clientVersion);
|
|
54
|
+
if (normalizedVersion.length === 0) {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
return normalizedVersion.startsWith("v")
|
|
58
|
+
? normalizedVersion
|
|
59
|
+
: `v${normalizedVersion}`;
|
|
60
|
+
}
|
|
47
61
|
function formatRewardCogAmount(value) {
|
|
48
62
|
return `${formatCogAmountWithDecimals(value, {
|
|
49
63
|
maxFractionDigits: 8,
|
|
50
64
|
minFractionDigits: 1,
|
|
51
65
|
})} COG`;
|
|
52
66
|
}
|
|
53
|
-
function
|
|
54
|
-
return
|
|
67
|
+
function escapeRegExp(value) {
|
|
68
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
69
|
+
}
|
|
70
|
+
function consumeWrappedLine(text, capacity) {
|
|
71
|
+
const remaining = text.trimStart();
|
|
72
|
+
if (remaining.length <= capacity) {
|
|
73
|
+
return {
|
|
74
|
+
line: remaining,
|
|
75
|
+
remaining: "",
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
const candidate = remaining.slice(0, capacity + 1);
|
|
79
|
+
const breakIndex = candidate.lastIndexOf(" ");
|
|
80
|
+
if (breakIndex > 0) {
|
|
81
|
+
return {
|
|
82
|
+
line: remaining.slice(0, breakIndex).trimEnd(),
|
|
83
|
+
remaining: remaining.slice(breakIndex + 1).trimStart(),
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
return {
|
|
87
|
+
line: remaining.slice(0, capacity),
|
|
88
|
+
remaining: remaining.slice(capacity).trimStart(),
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
function highlightRequiredWords(sentence, requiredWords) {
|
|
92
|
+
const uniqueWords = [...new Set(requiredWords
|
|
93
|
+
.map((word) => word.trim().toLowerCase())
|
|
94
|
+
.filter((word) => word.length > 0))].sort((left, right) => right.length - left.length);
|
|
95
|
+
if (uniqueWords.length === 0) {
|
|
96
|
+
return sentence;
|
|
97
|
+
}
|
|
98
|
+
const pattern = new RegExp(`\\b(?:${uniqueWords.map(escapeRegExp).join("|")})\\b`, "gi");
|
|
99
|
+
return sentence.replace(pattern, (match) => match.toUpperCase());
|
|
100
|
+
}
|
|
101
|
+
function formatSentenceSlot(prefix, sentence, requiredWords, lineCount) {
|
|
102
|
+
if (sentence === null) {
|
|
103
|
+
return [
|
|
104
|
+
prefix.trimEnd(),
|
|
105
|
+
...Array.from({ length: Math.max(0, lineCount - 1) }, () => ""),
|
|
106
|
+
];
|
|
107
|
+
}
|
|
108
|
+
const normalizedSentence = highlightRequiredWords(normalizeInlineText(sentence), requiredWords);
|
|
109
|
+
const continuationPrefix = " ".repeat(prefix.length);
|
|
110
|
+
const lines = [];
|
|
111
|
+
let remaining = normalizedSentence;
|
|
112
|
+
for (let lineIndex = 0; lineIndex < lineCount; lineIndex += 1) {
|
|
113
|
+
const linePrefix = lineIndex === 0 ? prefix : continuationPrefix;
|
|
114
|
+
const capacity = Math.max(0, MINING_SENTENCE_BOARD_WRAP_WIDTH - linePrefix.length);
|
|
115
|
+
const wrapped = consumeWrappedLine(remaining, capacity);
|
|
116
|
+
const isLastLine = lineIndex === lineCount - 1;
|
|
117
|
+
const lineContent = isLastLine && wrapped.remaining.length > 0
|
|
118
|
+
? truncateLine(`${wrapped.line}\u2026`, capacity)
|
|
119
|
+
: wrapped.line;
|
|
120
|
+
lines.push(lineContent.length === 0
|
|
121
|
+
? ""
|
|
122
|
+
: `${linePrefix}${lineContent}`);
|
|
123
|
+
remaining = wrapped.remaining;
|
|
124
|
+
}
|
|
125
|
+
return lines;
|
|
126
|
+
}
|
|
127
|
+
function formatSentenceRow(entry) {
|
|
128
|
+
return formatSentenceSlot(`${entry.rank}. @${entry.domainName}: `, entry.sentence, entry.requiredWords, 2);
|
|
55
129
|
}
|
|
56
130
|
function formatRequiredWordsLine(words) {
|
|
57
131
|
if (words.length === 0) {
|
|
@@ -59,11 +133,11 @@ function formatRequiredWordsLine(words) {
|
|
|
59
133
|
}
|
|
60
134
|
return `Required words: ${words.map((word) => word.toUpperCase()).join(", ")}`;
|
|
61
135
|
}
|
|
62
|
-
function formatProvisionalSentenceRow(entry) {
|
|
136
|
+
function formatProvisionalSentenceRow(entry, requiredWords) {
|
|
63
137
|
if (entry.domainName === null || entry.sentence === null) {
|
|
64
|
-
return "";
|
|
138
|
+
return ["", "", ""];
|
|
65
139
|
}
|
|
66
|
-
return `@${entry.domainName}:
|
|
140
|
+
return formatSentenceSlot(`@${entry.domainName}: `, entry.sentence, requiredWords, 3);
|
|
67
141
|
}
|
|
68
142
|
export function createEmptyMiningFollowVisualizerState() {
|
|
69
143
|
return {
|
|
@@ -93,6 +167,7 @@ function cloneMiningFollowVisualizerState(state) {
|
|
|
93
167
|
visibleBlockTimesByHeight: { ...state.visibleBlockTimesByHeight },
|
|
94
168
|
settledBoardEntries: state.settledBoardEntries.map((entry) => ({
|
|
95
169
|
...entry,
|
|
170
|
+
requiredWords: [...entry.requiredWords],
|
|
96
171
|
})),
|
|
97
172
|
provisionalRequiredWords: [...state.provisionalRequiredWords],
|
|
98
173
|
provisionalEntry: {
|
|
@@ -105,6 +180,10 @@ function cloneMiningFollowVisualizerState(state) {
|
|
|
105
180
|
},
|
|
106
181
|
};
|
|
107
182
|
}
|
|
183
|
+
function miningFollowSceneShouldSettle(snapshot, nowUnixMs) {
|
|
184
|
+
return ((snapshot.tipSettledUntilUnixMs ?? 0) > nowUnixMs
|
|
185
|
+
|| (snapshot.reconnectSettledUntilUnixMs ?? 0) > nowUnixMs);
|
|
186
|
+
}
|
|
108
187
|
const VISUALIZER_PROGRESS_SNAPSHOT = {
|
|
109
188
|
url: "",
|
|
110
189
|
filename: "mining-follow-visualizer",
|
|
@@ -195,6 +274,8 @@ export function describeMiningVisualizerProgress(snapshot) {
|
|
|
195
274
|
}
|
|
196
275
|
export class MiningFollowVisualizer {
|
|
197
276
|
#renderer;
|
|
277
|
+
#artworkStatusLeftText;
|
|
278
|
+
#artworkStatusRightText;
|
|
198
279
|
#clock;
|
|
199
280
|
#renderThrottle;
|
|
200
281
|
#progress = createBootstrapProgress("follow_tip", VISUALIZER_PROGRESS_SNAPSHOT);
|
|
@@ -210,6 +291,9 @@ export class MiningFollowVisualizer {
|
|
|
210
291
|
env: options.env,
|
|
211
292
|
});
|
|
212
293
|
this.#clock = options.clock ?? DEFAULT_RENDER_CLOCK;
|
|
294
|
+
const artworkVersionText = formatArtworkVersionText(options.clientVersion);
|
|
295
|
+
this.#artworkStatusLeftText = options.updateAvailable === true ? "UPDATE" : null;
|
|
296
|
+
this.#artworkStatusRightText = artworkVersionText;
|
|
213
297
|
this.#renderer = renderPolicy.enabled
|
|
214
298
|
? options.rendererFactory?.(stream) ?? new TtyProgressRenderer(stream)
|
|
215
299
|
: null;
|
|
@@ -238,10 +322,13 @@ export class MiningFollowVisualizer {
|
|
|
238
322
|
replaceFollowBlockTimes(this.#scene, this.#latestUiState.visibleBlockTimesByHeight);
|
|
239
323
|
const indexedHeight = this.#latestSnapshot.indexerTipHeight ?? this.#latestSnapshot.coreBestHeight ?? null;
|
|
240
324
|
const nodeHeight = this.#latestSnapshot.coreBestHeight ?? indexedHeight;
|
|
325
|
+
const settleLatest = miningFollowSceneShouldSettle(this.#latestSnapshot, this.#clock.now());
|
|
241
326
|
syncFollowSceneState(this.#scene, {
|
|
242
327
|
indexedHeight,
|
|
243
328
|
nodeHeight,
|
|
244
329
|
liveActivated: true,
|
|
330
|
+
authoritativeTip: true,
|
|
331
|
+
settleLatest,
|
|
245
332
|
});
|
|
246
333
|
this.#renderThrottle.request();
|
|
247
334
|
}
|
|
@@ -275,22 +362,29 @@ export class MiningFollowVisualizer {
|
|
|
275
362
|
this.#progress.targetHeight = nodeHeight;
|
|
276
363
|
this.#progress.etaSeconds = null;
|
|
277
364
|
this.#progress.lastError = snapshot.lastError;
|
|
278
|
-
|
|
365
|
+
const renderOptions = {
|
|
279
366
|
artworkCogText: formatCompactCogBalanceText(uiState.balanceCogtoshi),
|
|
280
367
|
artworkSatText: formatCompactSatBalanceText(uiState.balanceSats),
|
|
281
368
|
extraLines: [
|
|
282
|
-
`✎
|
|
369
|
+
`✎ Block #${uiState.settledBlockHeight ?? "-----"} Sentences ✎`,
|
|
283
370
|
"",
|
|
284
371
|
...Array.from({ length: MINING_SENTENCE_BOARD_SIZE }, (_value, index) => {
|
|
285
372
|
const entry = uiState.settledBoardEntries[index];
|
|
286
373
|
return entry === undefined
|
|
287
|
-
? `${index + 1}
|
|
288
|
-
: formatSentenceRow(entry
|
|
289
|
-
}),
|
|
374
|
+
? [`${index + 1}.`, ""]
|
|
375
|
+
: formatSentenceRow(entry);
|
|
376
|
+
}).flat(),
|
|
290
377
|
"----------",
|
|
291
378
|
formatRequiredWordsLine(uiState.provisionalRequiredWords),
|
|
292
|
-
formatProvisionalSentenceRow(uiState.provisionalEntry),
|
|
379
|
+
...formatProvisionalSentenceRow(uiState.provisionalEntry, uiState.provisionalRequiredWords),
|
|
293
380
|
],
|
|
294
|
-
}
|
|
381
|
+
};
|
|
382
|
+
if (this.#artworkStatusLeftText !== null) {
|
|
383
|
+
renderOptions.artworkStatusLeftText = this.#artworkStatusLeftText;
|
|
384
|
+
}
|
|
385
|
+
if (this.#artworkStatusRightText !== null) {
|
|
386
|
+
renderOptions.artworkStatusRightText = this.#artworkStatusRightText;
|
|
387
|
+
}
|
|
388
|
+
this.#renderer.renderFollowScene(this.#progress, indexedHeight, nodeHeight, this.#scene, describeMiningVisualizerStatus(snapshot, uiState), renderOptions);
|
|
295
389
|
}
|
|
296
390
|
}
|
|
@@ -15,6 +15,7 @@ export declare function openWalletReadContext(options: {
|
|
|
15
15
|
secretProvider?: WalletSecretProvider;
|
|
16
16
|
walletControlLockHeld?: boolean;
|
|
17
17
|
startupTimeoutMs?: number;
|
|
18
|
+
expectedIndexerBinaryVersion?: string | null;
|
|
18
19
|
now?: number;
|
|
19
20
|
paths?: WalletRuntimePaths;
|
|
20
21
|
}): Promise<WalletReadContext>;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { access, constants } from "node:fs/promises";
|
|
2
2
|
import { deserializeIndexerState, loadBundledGenesisParameters } from "@cogcoin/indexer";
|
|
3
|
-
import { attachOrStartIndexerDaemon, probeIndexerDaemon, readObservedIndexerDaemonStatus, readSnapshotWithRetry, } from "../../bitcoind/indexer-daemon.js";
|
|
3
|
+
import { attachOrStartIndexerDaemon, INDEXER_DAEMON_BACKGROUND_FOLLOW_RECOVERY_FAILED, probeIndexerDaemon, readObservedIndexerDaemonStatus, readSnapshotWithRetry, } from "../../bitcoind/indexer-daemon.js";
|
|
4
4
|
import { createRpcClient } from "../../bitcoind/node.js";
|
|
5
5
|
import { UNINITIALIZED_WALLET_ROOT_ID } from "../../bitcoind/service-paths.js";
|
|
6
6
|
import { attachOrStartManagedBitcoindService, probeManagedBitcoindService, } from "../../bitcoind/service.js";
|
|
@@ -76,7 +76,6 @@ async function normalizeLoadedWalletStateForRead(options) {
|
|
|
76
76
|
dataDir: options.dataDir,
|
|
77
77
|
chain: "main",
|
|
78
78
|
startHeight: 0,
|
|
79
|
-
serviceLifetime: "ephemeral",
|
|
80
79
|
walletRootId: options.loaded.state.walletRootId,
|
|
81
80
|
});
|
|
82
81
|
try {
|
|
@@ -246,6 +245,11 @@ function mapIndexerStartupError(message) {
|
|
|
246
245
|
health: "unavailable",
|
|
247
246
|
message: "The live indexer daemon socket responded with an invalid or incomplete protocol exchange.",
|
|
248
247
|
};
|
|
248
|
+
case INDEXER_DAEMON_BACKGROUND_FOLLOW_RECOVERY_FAILED:
|
|
249
|
+
return {
|
|
250
|
+
health: "failed",
|
|
251
|
+
message: "The managed indexer daemon could not recover automatic background follow.",
|
|
252
|
+
};
|
|
249
253
|
default:
|
|
250
254
|
return {
|
|
251
255
|
health: "unavailable",
|
|
@@ -436,7 +440,6 @@ async function attachNodeStatus(options) {
|
|
|
436
440
|
dataDir: options.dataDir,
|
|
437
441
|
chain: "main",
|
|
438
442
|
startHeight: resolveCogcoinProcessingStartHeight(genesis),
|
|
439
|
-
serviceLifetime: "ephemeral",
|
|
440
443
|
walletRootId: options.walletRootId,
|
|
441
444
|
startupTimeoutMs: options.startupTimeoutMs,
|
|
442
445
|
});
|
|
@@ -534,18 +537,15 @@ export async function openWalletReadContext(options) {
|
|
|
534
537
|
dataDir: options.dataDir,
|
|
535
538
|
walletRootId,
|
|
536
539
|
});
|
|
537
|
-
if (probe.compatibility === "compatible") {
|
|
538
|
-
|
|
539
|
-
observedDaemonStatus = probe.status;
|
|
540
|
-
indexerSource = "probe";
|
|
541
|
-
}
|
|
542
|
-
else if (probe.compatibility === "unreachable") {
|
|
540
|
+
if (probe.compatibility === "compatible" || probe.compatibility === "unreachable") {
|
|
541
|
+
await probe.client?.close().catch(() => undefined);
|
|
543
542
|
daemonClient = await attachOrStartIndexerDaemon({
|
|
544
543
|
dataDir: options.dataDir,
|
|
545
544
|
databasePath: options.databasePath,
|
|
546
|
-
serviceLifetime: "ephemeral",
|
|
547
545
|
walletRootId,
|
|
548
546
|
startupTimeoutMs,
|
|
547
|
+
ensureBackgroundFollow: true,
|
|
548
|
+
expectedBinaryVersion: options.expectedIndexerBinaryVersion,
|
|
549
549
|
});
|
|
550
550
|
}
|
|
551
551
|
else {
|
|
@@ -570,6 +570,11 @@ export async function openWalletReadContext(options) {
|
|
|
570
570
|
}
|
|
571
571
|
catch (error) {
|
|
572
572
|
daemonError = error instanceof Error ? error.message : String(error);
|
|
573
|
+
if (daemonError === INDEXER_DAEMON_BACKGROUND_FOLLOW_RECOVERY_FAILED) {
|
|
574
|
+
await daemonClient?.close().catch(() => undefined);
|
|
575
|
+
await node.handle?.stop().catch(() => undefined);
|
|
576
|
+
throw error;
|
|
577
|
+
}
|
|
573
578
|
if (observedDaemonStatus === null) {
|
|
574
579
|
observedDaemonStatus = await readObservedIndexerDaemonStatus({
|
|
575
580
|
dataDir: options.dataDir,
|
package/dist/wallet/reset.js
CHANGED
|
@@ -232,7 +232,6 @@ async function recreateManagedCoreWalletReplicaForReset(options) {
|
|
|
232
232
|
dataDir: options.dataDir,
|
|
233
233
|
chain: "main",
|
|
234
234
|
startHeight: 0,
|
|
235
|
-
serviceLifetime: "ephemeral",
|
|
236
235
|
walletRootId: options.state.walletRootId,
|
|
237
236
|
managedWalletPassphrase: options.state.managedCoreWallet.internalPassphrase,
|
|
238
237
|
});
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { createCipheriv, createDecipheriv, randomBytes } from "node:crypto";
|
|
2
2
|
import net from "node:net";
|
|
3
3
|
import { rm } from "node:fs/promises";
|
|
4
|
+
function shouldRemoveAgentEndpointPath(endpoint) {
|
|
5
|
+
return !endpoint.startsWith("\\\\.\\pipe\\");
|
|
6
|
+
}
|
|
4
7
|
function zeroizeBuffer(buffer) {
|
|
5
8
|
if (buffer != null) {
|
|
6
9
|
buffer.fill(0);
|
|
@@ -67,7 +70,7 @@ async function main() {
|
|
|
67
70
|
}
|
|
68
71
|
zeroizeBuffer(key);
|
|
69
72
|
key = Buffer.alloc(0);
|
|
70
|
-
if (
|
|
73
|
+
if (shouldRemoveAgentEndpointPath(bootstrap.endpoint)) {
|
|
71
74
|
await rm(bootstrap.endpoint, { force: true }).catch(() => undefined);
|
|
72
75
|
}
|
|
73
76
|
process.exit(0);
|
|
@@ -33,13 +33,18 @@ function resolveClientPasswordStatePath(directoryPath) {
|
|
|
33
33
|
function resolveClientPasswordRotationJournalPath(directoryPath) {
|
|
34
34
|
return join(directoryPath, "client-password-rotation.json");
|
|
35
35
|
}
|
|
36
|
-
function resolveAgentEndpoint(
|
|
36
|
+
function resolveAgentEndpoint(stateRoot) {
|
|
37
37
|
const hash = createHash("sha256").update(stateRoot).digest("hex").slice(0, 24);
|
|
38
|
-
|
|
38
|
+
// Wallet provider tests simulate foreign platforms, but the local agent transport
|
|
39
|
+
// still has to follow the real host runtime.
|
|
40
|
+
if (process.platform === "win32") {
|
|
39
41
|
return `\\\\.\\pipe\\cogcoin-client-password-${hash}`;
|
|
40
42
|
}
|
|
41
43
|
return join(tmpdir(), `cogcoin-client-password-${hash}.sock`);
|
|
42
44
|
}
|
|
45
|
+
function shouldRemoveAgentEndpointPath(endpoint) {
|
|
46
|
+
return !endpoint.startsWith("\\\\.\\pipe\\");
|
|
47
|
+
}
|
|
43
48
|
function isMissingFileError(error) {
|
|
44
49
|
return error instanceof Error
|
|
45
50
|
&& "code" in error
|
|
@@ -352,7 +357,7 @@ async function openAgentConnection(endpoint) {
|
|
|
352
357
|
});
|
|
353
358
|
}
|
|
354
359
|
async function requestAgent(options, request) {
|
|
355
|
-
const endpoint = resolveAgentEndpoint(options.
|
|
360
|
+
const endpoint = resolveAgentEndpoint(options.stateRoot);
|
|
356
361
|
const socket = await openAgentConnection(endpoint);
|
|
357
362
|
return await new Promise((resolve, reject) => {
|
|
358
363
|
let received = "";
|
|
@@ -418,8 +423,9 @@ async function requestAgentOrNull(options, request) {
|
|
|
418
423
|
? String(error.code ?? "")
|
|
419
424
|
: "";
|
|
420
425
|
if (code === "ENOENT" || code === "ECONNREFUSED" || code === "ECONNRESET" || code === "EPIPE") {
|
|
421
|
-
|
|
422
|
-
|
|
426
|
+
const endpoint = resolveAgentEndpoint(options.stateRoot);
|
|
427
|
+
if (shouldRemoveAgentEndpointPath(endpoint)) {
|
|
428
|
+
await rm(endpoint, { force: true }).catch(() => undefined);
|
|
423
429
|
}
|
|
424
430
|
return null;
|
|
425
431
|
}
|
|
@@ -441,8 +447,9 @@ export async function readClientPasswordSessionStatus(options) {
|
|
|
441
447
|
}
|
|
442
448
|
export async function lockClientPasswordSession(options) {
|
|
443
449
|
await requestAgentOrNull(options, { command: "lock" }).catch(() => null);
|
|
444
|
-
|
|
445
|
-
|
|
450
|
+
const endpoint = resolveAgentEndpoint(options.stateRoot);
|
|
451
|
+
if (shouldRemoveAgentEndpointPath(endpoint)) {
|
|
452
|
+
await rm(endpoint, { force: true }).catch(() => undefined);
|
|
446
453
|
}
|
|
447
454
|
return {
|
|
448
455
|
unlocked: false,
|
|
@@ -499,7 +506,7 @@ async function startClientPasswordSession(options) {
|
|
|
499
506
|
}
|
|
500
507
|
async function startClientPasswordSessionWithExpiry(options) {
|
|
501
508
|
const unlockUntilUnixMs = options.unlockUntilUnixMs;
|
|
502
|
-
const endpoint = resolveAgentEndpoint(options.
|
|
509
|
+
const endpoint = resolveAgentEndpoint(options.stateRoot);
|
|
503
510
|
await lockClientPasswordSession(options).catch(() => undefined);
|
|
504
511
|
await mkdir(options.runtimeRoot, { recursive: true }).catch(() => undefined);
|
|
505
512
|
const child = spawn(process.execPath, [fileURLToPath(new URL("./client-password-agent.js", import.meta.url)), endpoint, String(unlockUntilUnixMs)], {
|
package/dist/wallet/tx/anchor.js
CHANGED
|
@@ -394,7 +394,6 @@ export async function anchorDomain(options) {
|
|
|
394
394
|
dataDir: options.dataDir,
|
|
395
395
|
chain: "main",
|
|
396
396
|
startHeight: 0,
|
|
397
|
-
serviceLifetime: "ephemeral",
|
|
398
397
|
walletRootId: state.walletRootId,
|
|
399
398
|
});
|
|
400
399
|
const rpc = (options.rpcFactory ?? createRpcClient)(node.rpc);
|
|
@@ -131,7 +131,6 @@ export async function transferBitcoin(options) {
|
|
|
131
131
|
dataDir: options.dataDir,
|
|
132
132
|
chain: "main",
|
|
133
133
|
startHeight: 0,
|
|
134
|
-
serviceLifetime: "ephemeral",
|
|
135
134
|
walletRootId: state.walletRootId,
|
|
136
135
|
});
|
|
137
136
|
const rpc = (options.rpcFactory ?? createRpcClient)(node.rpc);
|
package/dist/wallet/tx/cog.js
CHANGED
|
@@ -534,7 +534,6 @@ export async function sendCog(options) {
|
|
|
534
534
|
dataDir: options.dataDir,
|
|
535
535
|
chain: "main",
|
|
536
536
|
startHeight: 0,
|
|
537
|
-
serviceLifetime: "ephemeral",
|
|
538
537
|
walletRootId: operation.state.walletRootId,
|
|
539
538
|
});
|
|
540
539
|
const rpc = (options.rpcFactory ?? createRpcClient)(node.rpc);
|
|
@@ -717,7 +716,6 @@ export async function lockCogToDomain(options) {
|
|
|
717
716
|
dataDir: options.dataDir,
|
|
718
717
|
chain: "main",
|
|
719
718
|
startHeight: 0,
|
|
720
|
-
serviceLifetime: "ephemeral",
|
|
721
719
|
walletRootId: operation.state.walletRootId,
|
|
722
720
|
});
|
|
723
721
|
const rpc = (options.rpcFactory ?? createRpcClient)(node.rpc);
|
|
@@ -877,7 +875,6 @@ async function runClaimLikeMutation(options, reclaim) {
|
|
|
877
875
|
dataDir: options.dataDir,
|
|
878
876
|
chain: "main",
|
|
879
877
|
startHeight: 0,
|
|
880
|
-
serviceLifetime: "ephemeral",
|
|
881
878
|
walletRootId: operation.state.walletRootId,
|
|
882
879
|
});
|
|
883
880
|
const rpc = (options.rpcFactory ?? createRpcClient)(node.rpc);
|
package/dist/wallet/tx/common.js
CHANGED
|
@@ -435,7 +435,7 @@ export async function buildWalletMutationTransaction(options) {
|
|
|
435
435
|
const funded = await options.rpc.walletCreateFundedPsbt(options.walletName, options.plan.fixedInputs, options.plan.outputs, 0, {
|
|
436
436
|
add_inputs: true,
|
|
437
437
|
include_unsafe: false,
|
|
438
|
-
minconf:
|
|
438
|
+
minconf: availableFundingMinConf,
|
|
439
439
|
changeAddress: options.plan.changeAddress,
|
|
440
440
|
...(options.plan.changePosition == null ? {} : { changePosition: options.plan.changePosition }),
|
|
441
441
|
lockUnspents: false,
|
|
@@ -518,7 +518,6 @@ async function submitDomainAdminMutation(options) {
|
|
|
518
518
|
dataDir: options.dataDir,
|
|
519
519
|
chain: "main",
|
|
520
520
|
startHeight: 0,
|
|
521
|
-
serviceLifetime: "ephemeral",
|
|
522
521
|
walletRootId: operation.state.walletRootId,
|
|
523
522
|
});
|
|
524
523
|
const rpc = (options.rpcFactory ?? createRpcClient)(node.rpc);
|
|
@@ -609,7 +609,6 @@ export async function transferDomain(options) {
|
|
|
609
609
|
dataDir: options.dataDir,
|
|
610
610
|
chain: "main",
|
|
611
611
|
startHeight: 0,
|
|
612
|
-
serviceLifetime: "ephemeral",
|
|
613
612
|
walletRootId: operation.state.walletRootId,
|
|
614
613
|
});
|
|
615
614
|
const rpc = (options.rpcFactory ?? createRpcClient)(node.rpc);
|
|
@@ -868,7 +867,6 @@ async function runSellMutation(options) {
|
|
|
868
867
|
dataDir: options.dataDir,
|
|
869
868
|
chain: "main",
|
|
870
869
|
startHeight: 0,
|
|
871
|
-
serviceLifetime: "ephemeral",
|
|
872
870
|
walletRootId: operation.state.walletRootId,
|
|
873
871
|
});
|
|
874
872
|
const rpc = (options.rpcFactory ?? createRpcClient)(node.rpc);
|
|
@@ -1138,7 +1136,6 @@ export async function buyDomain(options) {
|
|
|
1138
1136
|
dataDir: options.dataDir,
|
|
1139
1137
|
chain: "main",
|
|
1140
1138
|
startHeight: 0,
|
|
1141
|
-
serviceLifetime: "ephemeral",
|
|
1142
1139
|
walletRootId: operation.state.walletRootId,
|
|
1143
1140
|
});
|
|
1144
1141
|
const rpc = (options.rpcFactory ?? createRpcClient)(node.rpc);
|
package/dist/wallet/tx/field.js
CHANGED
|
@@ -686,7 +686,6 @@ async function submitStandaloneFieldMutation(options) {
|
|
|
686
686
|
dataDir: options.dataDir,
|
|
687
687
|
chain: "main",
|
|
688
688
|
startHeight: 0,
|
|
689
|
-
serviceLifetime: "ephemeral",
|
|
690
689
|
walletRootId: operation.state.walletRootId,
|
|
691
690
|
});
|
|
692
691
|
const rpc = (options.rpcFactory ?? createRpcClient)(node.rpc);
|
|
@@ -532,7 +532,6 @@ export async function registerDomain(options) {
|
|
|
532
532
|
dataDir: options.dataDir,
|
|
533
533
|
chain: "main",
|
|
534
534
|
startHeight: 0,
|
|
535
|
-
serviceLifetime: "ephemeral",
|
|
536
535
|
walletRootId: state.walletRootId,
|
|
537
536
|
});
|
|
538
537
|
const rpc = (options.rpcFactory ?? createRpcClient)(node.rpc);
|
|
@@ -513,7 +513,6 @@ async function submitReputationMutation(options) {
|
|
|
513
513
|
dataDir: options.dataDir,
|
|
514
514
|
chain: "main",
|
|
515
515
|
startHeight: 0,
|
|
516
|
-
serviceLifetime: "ephemeral",
|
|
517
516
|
walletRootId: operation.state.walletRootId,
|
|
518
517
|
});
|
|
519
518
|
const rpc = (options.rpcFactory ?? createRpcClient)(node.rpc);
|
package/package.json
CHANGED