@cogcoin/client 0.5.13 → 0.5.14
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 +1 -1
- package/dist/bitcoind/bootstrap/getblock-archive.d.ts +23 -1
- package/dist/bitcoind/bootstrap/getblock-archive.js +127 -37
- package/dist/bitcoind/bootstrap.d.ts +1 -1
- package/dist/bitcoind/bootstrap.js +1 -1
- package/dist/bitcoind/client/managed-client.js +62 -40
- package/dist/bitcoind/testing.d.ts +1 -1
- package/dist/bitcoind/testing.js +1 -1
- package/dist/cli/commands/status.js +1 -1
- package/dist/cli/commands/sync.js +99 -1
- package/dist/cli/commands/wallet-mutation.js +39 -2
- package/dist/cli/context.js +20 -3
- package/dist/cli/mutation-success.d.ts +2 -0
- package/dist/cli/mutation-success.js +2 -0
- package/dist/cli/mutation-text-write.d.ts +2 -0
- package/dist/cli/mutation-text-write.js +7 -0
- package/dist/cli/output.js +22 -1
- package/dist/cli/types.d.ts +2 -0
- package/dist/cli/wallet-format.d.ts +1 -1
- package/dist/cli/wallet-format.js +2 -2
- package/dist/wallet/coin-control.d.ts +1 -1
- package/dist/wallet/coin-control.js +56 -15
- package/dist/wallet/tx/anchor.d.ts +2 -0
- package/dist/wallet/tx/anchor.js +49 -14
- package/dist/wallet/tx/cog.js +5 -15
- package/dist/wallet/tx/common.d.ts +4 -0
- package/dist/wallet/tx/common.js +67 -2
- package/dist/wallet/tx/domain-admin.js +3 -2
- package/dist/wallet/tx/domain-market.js +5 -15
- package/dist/wallet/tx/field.js +1 -3
- package/dist/wallet/tx/register.js +5 -13
- package/dist/wallet/tx/reputation.js +3 -2
- package/package.json +1 -1
|
@@ -47,6 +47,7 @@ export async function runWalletMutationCommand(parsed, context) {
|
|
|
47
47
|
const dataDir = parsed.dataDir ?? context.resolveDefaultBitcoindDataDir();
|
|
48
48
|
const dbPath = parsed.dbPath ?? context.resolveDefaultClientDatabasePath();
|
|
49
49
|
const prompter = createCommandPrompter(parsed, context);
|
|
50
|
+
const interactive = prompter.isInteractive;
|
|
50
51
|
if (isAnchorClearMutationCommand(parsed.command)) {
|
|
51
52
|
const result = await context.clearPendingAnchor({
|
|
52
53
|
domainName: parsed.args[0],
|
|
@@ -65,6 +66,7 @@ export async function runWalletMutationCommand(parsed, context) {
|
|
|
65
66
|
previewData: buildAnchorClearPreviewData(result),
|
|
66
67
|
reusedExisting: false,
|
|
67
68
|
reusedMessage: "",
|
|
69
|
+
interactive,
|
|
68
70
|
outcome: result.cleared ? "cleared" : "noop",
|
|
69
71
|
nextSteps,
|
|
70
72
|
text: {
|
|
@@ -83,6 +85,7 @@ export async function runWalletMutationCommand(parsed, context) {
|
|
|
83
85
|
const result = await context.anchorDomain({
|
|
84
86
|
domainName: parsed.args[0],
|
|
85
87
|
foundingMessageText: parsed.anchorMessage,
|
|
88
|
+
promptForFoundingMessageWhenMissing: parsed.anchorMessage === null,
|
|
86
89
|
dataDir,
|
|
87
90
|
databasePath: dbPath,
|
|
88
91
|
provider: context.walletSecretProvider,
|
|
@@ -92,13 +95,15 @@ export async function runWalletMutationCommand(parsed, context) {
|
|
|
92
95
|
const nextSteps = getAnchorNextSteps(result.domainName);
|
|
93
96
|
return writeMutationCommandSuccess(parsed, context, {
|
|
94
97
|
data: buildAnchorMutationData(result, {
|
|
95
|
-
foundingMessageText: parsed.anchorMessage,
|
|
98
|
+
foundingMessageText: result.foundingMessageText ?? parsed.anchorMessage,
|
|
96
99
|
}),
|
|
97
100
|
previewData: buildAnchorPreviewData(result, {
|
|
98
|
-
foundingMessageText: parsed.anchorMessage,
|
|
101
|
+
foundingMessageText: result.foundingMessageText ?? parsed.anchorMessage,
|
|
99
102
|
}),
|
|
100
103
|
reusedExisting: result.reusedExisting,
|
|
101
104
|
reusedMessage: "The existing anchor family was reconciled instead of creating a duplicate.",
|
|
105
|
+
interactive,
|
|
106
|
+
explorerTxid: result.tx2Txid,
|
|
102
107
|
nextSteps: workflowMutationNextSteps(nextSteps),
|
|
103
108
|
text: {
|
|
104
109
|
heading: "Anchor family submitted.",
|
|
@@ -136,6 +141,8 @@ export async function runWalletMutationCommand(parsed, context) {
|
|
|
136
141
|
}),
|
|
137
142
|
reusedExisting: result.reusedExisting,
|
|
138
143
|
reusedMessage: "The existing pending registration was reconciled instead of creating a duplicate.",
|
|
144
|
+
interactive,
|
|
145
|
+
explorerTxid: result.txid,
|
|
139
146
|
nextSteps: workflowMutationNextSteps(nextSteps),
|
|
140
147
|
text: {
|
|
141
148
|
heading: "Registration submitted.",
|
|
@@ -171,6 +178,8 @@ export async function runWalletMutationCommand(parsed, context) {
|
|
|
171
178
|
}),
|
|
172
179
|
reusedExisting: result.reusedExisting,
|
|
173
180
|
reusedMessage: "The existing pending transfer was reconciled instead of creating a duplicate.",
|
|
181
|
+
interactive,
|
|
182
|
+
explorerTxid: result.txid,
|
|
174
183
|
nextSteps: commandMutationNextSteps(`cogcoin show ${result.domainName}`),
|
|
175
184
|
text: {
|
|
176
185
|
heading: "Transfer submitted.",
|
|
@@ -208,6 +217,8 @@ export async function runWalletMutationCommand(parsed, context) {
|
|
|
208
217
|
}),
|
|
209
218
|
reusedExisting: result.reusedExisting,
|
|
210
219
|
reusedMessage: "The existing pending listing mutation was reconciled instead of creating a duplicate.",
|
|
220
|
+
interactive,
|
|
221
|
+
explorerTxid: result.txid,
|
|
211
222
|
nextSteps: commandMutationNextSteps(`cogcoin show ${result.domainName}`),
|
|
212
223
|
text: {
|
|
213
224
|
heading: result.listedPriceCogtoshi === 0n ? "Listing cancellation submitted." : "Listing submitted.",
|
|
@@ -256,6 +267,8 @@ export async function runWalletMutationCommand(parsed, context) {
|
|
|
256
267
|
}),
|
|
257
268
|
reusedExisting: result.reusedExisting,
|
|
258
269
|
reusedMessage: "The existing pending endpoint mutation was reconciled instead of creating a duplicate.",
|
|
270
|
+
interactive,
|
|
271
|
+
explorerTxid: result.txid,
|
|
259
272
|
nextSteps: commandMutationNextSteps(`cogcoin show ${result.domainName}`),
|
|
260
273
|
text: {
|
|
261
274
|
heading: parsed.command === "domain-endpoint-set" ? "Endpoint update submitted." : "Endpoint clear submitted.",
|
|
@@ -300,6 +313,8 @@ export async function runWalletMutationCommand(parsed, context) {
|
|
|
300
313
|
}),
|
|
301
314
|
reusedExisting: result.reusedExisting,
|
|
302
315
|
reusedMessage: "The existing pending delegate mutation was reconciled instead of creating a duplicate.",
|
|
316
|
+
interactive,
|
|
317
|
+
explorerTxid: result.txid,
|
|
303
318
|
nextSteps: commandMutationNextSteps(`cogcoin show ${result.domainName}`),
|
|
304
319
|
text: {
|
|
305
320
|
heading: parsed.command === "domain-delegate-set" ? "Delegate update submitted." : "Delegate clear submitted.",
|
|
@@ -344,6 +359,8 @@ export async function runWalletMutationCommand(parsed, context) {
|
|
|
344
359
|
}),
|
|
345
360
|
reusedExisting: result.reusedExisting,
|
|
346
361
|
reusedMessage: "The existing pending miner mutation was reconciled instead of creating a duplicate.",
|
|
362
|
+
interactive,
|
|
363
|
+
explorerTxid: result.txid,
|
|
347
364
|
nextSteps: commandMutationNextSteps(`cogcoin show ${result.domainName}`),
|
|
348
365
|
text: {
|
|
349
366
|
heading: parsed.command === "domain-miner-set" ? "Miner update submitted." : "Miner clear submitted.",
|
|
@@ -377,6 +394,8 @@ export async function runWalletMutationCommand(parsed, context) {
|
|
|
377
394
|
}),
|
|
378
395
|
reusedExisting: result.reusedExisting,
|
|
379
396
|
reusedMessage: "The existing pending canonical mutation was reconciled instead of creating a duplicate.",
|
|
397
|
+
interactive,
|
|
398
|
+
explorerTxid: result.txid,
|
|
380
399
|
nextSteps: commandMutationNextSteps(`cogcoin show ${result.domainName}`),
|
|
381
400
|
text: {
|
|
382
401
|
heading: "Canonical update submitted.",
|
|
@@ -415,6 +434,8 @@ export async function runWalletMutationCommand(parsed, context) {
|
|
|
415
434
|
reusedMessage: result.family
|
|
416
435
|
? "The existing pending field family was reconciled instead of creating a duplicate."
|
|
417
436
|
: "The existing pending field creation was reconciled instead of creating a duplicate.",
|
|
437
|
+
interactive,
|
|
438
|
+
explorerTxid: result.family ? (result.tx2Txid ?? null) : result.txid,
|
|
418
439
|
nextSteps: commandMutationNextSteps(`cogcoin field show ${result.domainName} ${result.fieldName}`),
|
|
419
440
|
text: {
|
|
420
441
|
heading: result.family ? "Field create+write family submitted." : "Field creation submitted.",
|
|
@@ -450,6 +471,8 @@ export async function runWalletMutationCommand(parsed, context) {
|
|
|
450
471
|
previewData: buildFieldPreviewData(result),
|
|
451
472
|
reusedExisting: result.reusedExisting,
|
|
452
473
|
reusedMessage: "The existing pending field update was reconciled instead of creating a duplicate.",
|
|
474
|
+
interactive,
|
|
475
|
+
explorerTxid: result.txid,
|
|
453
476
|
nextSteps: commandMutationNextSteps(`cogcoin field show ${result.domainName} ${result.fieldName}`),
|
|
454
477
|
text: {
|
|
455
478
|
heading: "Field update submitted.",
|
|
@@ -481,6 +504,8 @@ export async function runWalletMutationCommand(parsed, context) {
|
|
|
481
504
|
previewData: buildFieldPreviewData(result),
|
|
482
505
|
reusedExisting: result.reusedExisting,
|
|
483
506
|
reusedMessage: "The existing pending field clear was reconciled instead of creating a duplicate.",
|
|
507
|
+
interactive,
|
|
508
|
+
explorerTxid: result.txid,
|
|
484
509
|
nextSteps: commandMutationNextSteps(`cogcoin field show ${result.domainName} ${result.fieldName}`),
|
|
485
510
|
text: {
|
|
486
511
|
heading: "Field clear submitted.",
|
|
@@ -518,6 +543,8 @@ export async function runWalletMutationCommand(parsed, context) {
|
|
|
518
543
|
}),
|
|
519
544
|
reusedExisting: result.reusedExisting,
|
|
520
545
|
reusedMessage: "The existing pending COG transfer was reconciled instead of creating a duplicate.",
|
|
546
|
+
interactive,
|
|
547
|
+
explorerTxid: result.txid,
|
|
521
548
|
nextSteps: commandMutationNextSteps("cogcoin balance"),
|
|
522
549
|
text: {
|
|
523
550
|
heading: "COG transfer submitted.",
|
|
@@ -563,6 +590,8 @@ export async function runWalletMutationCommand(parsed, context) {
|
|
|
563
590
|
}),
|
|
564
591
|
reusedExisting: result.reusedExisting,
|
|
565
592
|
reusedMessage: "The existing pending lock was reconciled instead of creating a duplicate.",
|
|
593
|
+
interactive,
|
|
594
|
+
explorerTxid: result.txid,
|
|
566
595
|
nextSteps: commandMutationNextSteps("cogcoin locks"),
|
|
567
596
|
text: {
|
|
568
597
|
heading: "COG lock submitted.",
|
|
@@ -597,6 +626,8 @@ export async function runWalletMutationCommand(parsed, context) {
|
|
|
597
626
|
}),
|
|
598
627
|
reusedExisting: result.reusedExisting,
|
|
599
628
|
reusedMessage: "The existing pending claim was reconciled instead of creating a duplicate.",
|
|
629
|
+
interactive,
|
|
630
|
+
explorerTxid: result.txid,
|
|
600
631
|
nextSteps: commandMutationNextSteps("cogcoin locks --claimable"),
|
|
601
632
|
text: {
|
|
602
633
|
heading: "Lock claim submitted.",
|
|
@@ -631,6 +662,8 @@ export async function runWalletMutationCommand(parsed, context) {
|
|
|
631
662
|
}),
|
|
632
663
|
reusedExisting: result.reusedExisting,
|
|
633
664
|
reusedMessage: "The existing pending reclaim was reconciled instead of creating a duplicate.",
|
|
665
|
+
interactive,
|
|
666
|
+
explorerTxid: result.txid,
|
|
634
667
|
nextSteps: commandMutationNextSteps("cogcoin locks --reclaimable"),
|
|
635
668
|
text: {
|
|
636
669
|
heading: "Lock reclaim submitted.",
|
|
@@ -676,6 +709,8 @@ export async function runWalletMutationCommand(parsed, context) {
|
|
|
676
709
|
previewData: buildReputationPreviewData(result),
|
|
677
710
|
reusedExisting: result.reusedExisting,
|
|
678
711
|
reusedMessage: "The existing pending reputation mutation was reconciled instead of creating a duplicate.",
|
|
712
|
+
interactive,
|
|
713
|
+
explorerTxid: result.txid,
|
|
679
714
|
nextSteps: commandMutationNextSteps(`cogcoin show ${result.targetDomainName}`),
|
|
680
715
|
text: {
|
|
681
716
|
heading: parsed.command === "rep-give" ? "Reputation support submitted." : "Reputation revoke submitted.",
|
|
@@ -714,6 +749,8 @@ export async function runWalletMutationCommand(parsed, context) {
|
|
|
714
749
|
}),
|
|
715
750
|
reusedExisting: result.reusedExisting,
|
|
716
751
|
reusedMessage: "The existing pending purchase was reconciled instead of creating a duplicate.",
|
|
752
|
+
interactive,
|
|
753
|
+
explorerTxid: result.txid,
|
|
717
754
|
nextSteps: commandMutationNextSteps(`cogcoin show ${result.domainName}`),
|
|
718
755
|
text: {
|
|
719
756
|
heading: "Purchase submitted.",
|
package/dist/cli/context.js
CHANGED
|
@@ -17,9 +17,26 @@ import { createLazyDefaultWalletSecretProvider } from "../wallet/state/provider.
|
|
|
17
17
|
import { anchorDomain, clearPendingAnchor, buyDomain, claimCogLock, clearDomainDelegate, clearDomainEndpoint, clearDomainMiner, clearField, createField, giveReputation, lockCogToDomain, registerDomain, reclaimCogLock, revokeReputation, sendCog, setField, setDomainCanonical, setDomainDelegate, setDomainEndpoint, setDomainMiner, sellDomain, transferDomain, } from "../wallet/tx/index.js";
|
|
18
18
|
import { createTerminalPrompter } from "./prompt.js";
|
|
19
19
|
export async function readPackageVersionFromDisk() {
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
const packageUrls = [
|
|
21
|
+
new URL("../../package.json", import.meta.url),
|
|
22
|
+
new URL("../../../package.json", import.meta.url),
|
|
23
|
+
];
|
|
24
|
+
for (const packageUrl of packageUrls) {
|
|
25
|
+
try {
|
|
26
|
+
const raw = await readFile(packageUrl, "utf8");
|
|
27
|
+
const parsed = JSON.parse(raw);
|
|
28
|
+
return parsed.version ?? "0.0.0";
|
|
29
|
+
}
|
|
30
|
+
catch (error) {
|
|
31
|
+
const code = typeof error === "object" && error !== null && "code" in error
|
|
32
|
+
? String(error.code)
|
|
33
|
+
: null;
|
|
34
|
+
if (code !== "ENOENT") {
|
|
35
|
+
throw error;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return "0.0.0";
|
|
23
40
|
}
|
|
24
41
|
export function createDefaultContext(overrides = {}) {
|
|
25
42
|
return {
|
|
@@ -11,6 +11,8 @@ export declare function writeMutationCommandSuccess(parsed: ParsedCliArgs, conte
|
|
|
11
11
|
previewData?: unknown;
|
|
12
12
|
reusedExisting: boolean;
|
|
13
13
|
reusedMessage: string;
|
|
14
|
+
interactive?: boolean;
|
|
15
|
+
explorerTxid?: string | null;
|
|
14
16
|
nextSteps: MutationSuccessNextSteps;
|
|
15
17
|
outcome?: string;
|
|
16
18
|
text: {
|
|
@@ -42,6 +42,8 @@ export function writeMutationCommandSuccess(parsed, context, options) {
|
|
|
42
42
|
reusedExisting: options.reusedExisting,
|
|
43
43
|
reusedMessage: options.reusedMessage,
|
|
44
44
|
trailerLines: options.nextSteps.text,
|
|
45
|
+
interactive: options.interactive,
|
|
46
|
+
explorerTxid: options.explorerTxid,
|
|
45
47
|
});
|
|
46
48
|
return 0;
|
|
47
49
|
}
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import { writeLine } from "./io.js";
|
|
2
|
+
function mutationExplorerUrl(txid) {
|
|
3
|
+
return `https://mempool.space/tx/${txid}`;
|
|
4
|
+
}
|
|
2
5
|
export function writeMutationTextResult(stream, options) {
|
|
3
6
|
writeLine(stream, options.heading);
|
|
4
7
|
for (const field of options.fields) {
|
|
@@ -13,4 +16,8 @@ export function writeMutationTextResult(stream, options) {
|
|
|
13
16
|
for (const line of options.trailerLines ?? []) {
|
|
14
17
|
writeLine(stream, line);
|
|
15
18
|
}
|
|
19
|
+
if (options.interactive === true && options.explorerTxid) {
|
|
20
|
+
writeLine(stream, "");
|
|
21
|
+
writeLine(stream, `View at: ${mutationExplorerUrl(options.explorerTxid)}`);
|
|
22
|
+
}
|
|
16
23
|
}
|
package/dist/cli/output.js
CHANGED
|
@@ -188,7 +188,8 @@ export function classifyCliError(error) {
|
|
|
188
188
|
}
|
|
189
189
|
function isBlockedError(message) {
|
|
190
190
|
if (message === "wallet_control_lock_busy"
|
|
191
|
-
|| message.startsWith("file_lock_busy_")
|
|
191
|
+
|| message.startsWith("file_lock_busy_")
|
|
192
|
+
|| message.startsWith("wallet_anchor_clear_pending_first_")) {
|
|
192
193
|
return true;
|
|
193
194
|
}
|
|
194
195
|
if (message === "wallet_locked"
|
|
@@ -439,6 +440,14 @@ export function createCliErrorPresentation(errorCode, fallbackMessage) {
|
|
|
439
440
|
next: "Run `cogcoin repair`, then inspect the domain again before retrying `cogcoin anchor clear`.",
|
|
440
441
|
};
|
|
441
442
|
}
|
|
443
|
+
if (errorCode.startsWith("wallet_anchor_clear_pending_first_")) {
|
|
444
|
+
const domainName = errorCode.slice("wallet_anchor_clear_pending_first_".length) || "<domain>";
|
|
445
|
+
return {
|
|
446
|
+
what: `A local pending anchor already exists for "${domainName}".`,
|
|
447
|
+
why: "The wallet found a same-domain anchor reservation that is still local-only and safely clearable, so it stopped before creating a conflicting family.",
|
|
448
|
+
next: `Run \`cogcoin anchor clear ${domainName}\`, then rerun \`cogcoin anchor ${domainName}\`.`,
|
|
449
|
+
};
|
|
450
|
+
}
|
|
442
451
|
if (errorCode.startsWith("wallet_anchor_clear_not_clearable_")) {
|
|
443
452
|
return {
|
|
444
453
|
what: "Pending anchor cannot be cleared safely.",
|
|
@@ -488,6 +497,18 @@ export function createCliErrorPresentation(errorCode, fallbackMessage) {
|
|
|
488
497
|
next: "Rerun the command and confirm it. If this command uses a plain yes/no path, you can also add `--yes`.",
|
|
489
498
|
};
|
|
490
499
|
}
|
|
500
|
+
if (errorCode === "wallet_anchor_invalid_message" || errorCode.startsWith("wallet_anchor_invalid_message_")) {
|
|
501
|
+
const reason = errorCode.startsWith("wallet_anchor_invalid_message_")
|
|
502
|
+
? errorCode.slice("wallet_anchor_invalid_message_".length).trim()
|
|
503
|
+
: null;
|
|
504
|
+
return {
|
|
505
|
+
what: "Founding message cannot be encoded in canonical Coglex.",
|
|
506
|
+
why: reason === null || reason === ""
|
|
507
|
+
? "The supplied founding message could not be encoded into the canonical on-chain Coglex sentence format."
|
|
508
|
+
: reason,
|
|
509
|
+
next: "Retry with a different founding message, or rerun `cogcoin anchor <domain>` without `--message` to skip it.",
|
|
510
|
+
};
|
|
511
|
+
}
|
|
491
512
|
if (errorCode === "wallet_prompt_value_required") {
|
|
492
513
|
return {
|
|
493
514
|
what: "Required input was not provided.",
|
package/dist/cli/types.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { inspectPassiveClientStatus } from "../passive-status.js";
|
|
2
2
|
import { createRpcClient } from "../bitcoind/node.js";
|
|
3
|
+
import type { ManagedBitcoindProgressEvent } from "../bitcoind/types.js";
|
|
3
4
|
import { attachOrStartIndexerDaemon, probeIndexerDaemon, readObservedIndexerDaemonStatus, stopIndexerDaemonService } from "../bitcoind/indexer-daemon.js";
|
|
4
5
|
import { attachOrStartManagedBitcoindService, probeManagedBitcoindService, stopManagedBitcoindService } from "../bitcoind/service.js";
|
|
5
6
|
import { openSqliteStore } from "../sqlite/index.js";
|
|
@@ -99,6 +100,7 @@ export interface CliRunnerContext {
|
|
|
99
100
|
dataDir?: string;
|
|
100
101
|
walletRootId?: string;
|
|
101
102
|
progressOutput?: ProgressOutput;
|
|
103
|
+
onProgress?: (event: ManagedBitcoindProgressEvent) => void;
|
|
102
104
|
confirmGetblockArchiveRestart?: (options: {
|
|
103
105
|
currentArchiveEndHeight: number | null;
|
|
104
106
|
nextArchiveEndHeight: number;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { WalletDomainView, WalletReadContext } from "../wallet/read/index.js";
|
|
2
2
|
export declare function getRepairRecommendation(context: WalletReadContext): string | null;
|
|
3
3
|
export declare function getMutationRecommendation(context: WalletReadContext): string | null;
|
|
4
|
-
export declare function formatWalletOverviewReport(context: WalletReadContext): string;
|
|
4
|
+
export declare function formatWalletOverviewReport(context: WalletReadContext, version: string): string;
|
|
5
5
|
export declare function formatDetailedWalletStatusReport(context: WalletReadContext): string;
|
|
6
6
|
export declare function formatFundingAddressReport(context: WalletReadContext): string;
|
|
7
7
|
export declare function formatIdentityListReport(context: WalletReadContext, options?: {
|
|
@@ -459,9 +459,9 @@ function getOverviewNextStep(context) {
|
|
|
459
459
|
}
|
|
460
460
|
return getMutationRecommendation(context);
|
|
461
461
|
}
|
|
462
|
-
export function formatWalletOverviewReport(context) {
|
|
462
|
+
export function formatWalletOverviewReport(context, version) {
|
|
463
463
|
const parts = [
|
|
464
|
-
|
|
464
|
+
`\n⛭ Cogcoin Status v${version} ⛭`,
|
|
465
465
|
formatOverviewSection("Paths", buildOverviewPathsSection(context)),
|
|
466
466
|
formatOverviewSection("Wallet", buildOverviewWalletSection(context)),
|
|
467
467
|
formatOverviewSection("Services", buildOverviewServicesSection(context)),
|
|
@@ -2,7 +2,7 @@ import type { RpcListUnspentEntry, RpcLockedUnspent } from "../bitcoind/types.js
|
|
|
2
2
|
import { persistWalletStateUpdate } from "./descriptor-normalization.js";
|
|
3
3
|
import type { WalletRuntimePaths } from "./runtime.js";
|
|
4
4
|
import type { OutpointRecord, PortableWalletArchivePayloadV1, UnlockSessionStateV1, WalletStateV1 } from "./types.js";
|
|
5
|
-
export declare const DEFAULT_PROACTIVE_RESERVE_SATS =
|
|
5
|
+
export declare const DEFAULT_PROACTIVE_RESERVE_SATS = 1000;
|
|
6
6
|
export interface WalletCoinControlRpc {
|
|
7
7
|
listUnspent(walletName: string, minConf?: number): Promise<RpcListUnspentEntry[]>;
|
|
8
8
|
listLockUnspent(walletName: string): Promise<RpcLockedUnspent[]>;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { saveUnlockSession } from "./state/session.js";
|
|
2
2
|
import { persistWalletStateUpdate } from "./descriptor-normalization.js";
|
|
3
3
|
import { miningFamilyMayStillExist } from "./mining/state.js";
|
|
4
|
-
export const DEFAULT_PROACTIVE_RESERVE_SATS =
|
|
4
|
+
export const DEFAULT_PROACTIVE_RESERVE_SATS = 1_000;
|
|
5
5
|
function btcNumberToSats(value) {
|
|
6
6
|
return BigInt(Math.round(value * 100_000_000));
|
|
7
7
|
}
|
|
@@ -33,7 +33,11 @@ function normalizeReserveSats(raw) {
|
|
|
33
33
|
if (typeof raw !== "number" || !Number.isFinite(raw)) {
|
|
34
34
|
return DEFAULT_PROACTIVE_RESERVE_SATS;
|
|
35
35
|
}
|
|
36
|
-
|
|
36
|
+
const normalized = Math.max(0, Math.trunc(raw));
|
|
37
|
+
if (normalized === 0) {
|
|
38
|
+
return 0;
|
|
39
|
+
}
|
|
40
|
+
return DEFAULT_PROACTIVE_RESERVE_SATS;
|
|
37
41
|
}
|
|
38
42
|
function sameOutpointList(left, right) {
|
|
39
43
|
if (left.length !== (right?.length ?? 0)) {
|
|
@@ -42,8 +46,12 @@ function sameOutpointList(left, right) {
|
|
|
42
46
|
return left.every((outpoint, index) => outpoint.txid === right?.[index]?.txid && outpoint.vout === right?.[index]?.vout);
|
|
43
47
|
}
|
|
44
48
|
export function normalizeWalletStateRecord(state) {
|
|
45
|
-
const
|
|
46
|
-
const
|
|
49
|
+
const rawProactiveReserveSats = state.proactiveReserveSats;
|
|
50
|
+
const proactiveReserveSats = normalizeReserveSats(rawProactiveReserveSats);
|
|
51
|
+
const reserveValueChanged = proactiveReserveSats !== rawProactiveReserveSats;
|
|
52
|
+
const proactiveReserveOutpoints = normalizeOutpointRecordList(proactiveReserveSats <= 0 || reserveValueChanged
|
|
53
|
+
? []
|
|
54
|
+
: state.proactiveReserveOutpoints);
|
|
47
55
|
const pendingMutations = state.pendingMutations ?? [];
|
|
48
56
|
if (proactiveReserveSats === state.proactiveReserveSats
|
|
49
57
|
&& sameOutpointList(proactiveReserveOutpoints, state.proactiveReserveOutpoints)
|
|
@@ -58,8 +66,12 @@ export function normalizeWalletStateRecord(state) {
|
|
|
58
66
|
};
|
|
59
67
|
}
|
|
60
68
|
export function normalizePortableWalletArchivePayload(payload) {
|
|
61
|
-
const
|
|
62
|
-
const
|
|
69
|
+
const rawProactiveReserveSats = payload.proactiveReserveSats;
|
|
70
|
+
const proactiveReserveSats = normalizeReserveSats(rawProactiveReserveSats);
|
|
71
|
+
const reserveValueChanged = proactiveReserveSats !== rawProactiveReserveSats;
|
|
72
|
+
const proactiveReserveOutpoints = normalizeOutpointRecordList(proactiveReserveSats <= 0 || reserveValueChanged
|
|
73
|
+
? []
|
|
74
|
+
: payload.proactiveReserveOutpoints);
|
|
63
75
|
if (proactiveReserveSats === payload.proactiveReserveSats
|
|
64
76
|
&& sameOutpointList(proactiveReserveOutpoints, payload.proactiveReserveOutpoints)) {
|
|
65
77
|
return payload;
|
|
@@ -161,6 +173,9 @@ export function computeDesignatedProactiveReserveOutpoints(state, spendableUtxos
|
|
|
161
173
|
break;
|
|
162
174
|
}
|
|
163
175
|
}
|
|
176
|
+
if (total < target) {
|
|
177
|
+
return [];
|
|
178
|
+
}
|
|
164
179
|
return selected;
|
|
165
180
|
}
|
|
166
181
|
function syncStateWithComputedReserve(state, spendableUtxos) {
|
|
@@ -275,8 +290,11 @@ function collectPersistentPolicyLockedOutpoints(state, spendableUtxos) {
|
|
|
275
290
|
return outpoints;
|
|
276
291
|
}
|
|
277
292
|
export async function reconcilePersistentPolicyLocks(options) {
|
|
293
|
+
const rawReserveOutpoints = normalizeOutpointRecordList(options.state.proactiveReserveOutpoints);
|
|
278
294
|
let state = normalizeWalletStateRecord(options.state);
|
|
279
295
|
let changed = state !== options.state;
|
|
296
|
+
const fixedInputKeys = new Set((options.fixedInputs ?? []).map((outpoint) => outpointKey(outpoint)));
|
|
297
|
+
const temporarilyUnlockedKeys = new Set((options.temporarilyUnlockedOutpoints ?? []).map((outpoint) => outpointKey(outpoint)));
|
|
280
298
|
if (options.cleanupInactiveTemporaryBuilderLocks === true) {
|
|
281
299
|
const cleaned = collectInactiveTemporaryBuilderLockCleanup(state);
|
|
282
300
|
state = cleaned.state;
|
|
@@ -285,12 +303,24 @@ export async function reconcilePersistentPolicyLocks(options) {
|
|
|
285
303
|
await options.rpc.lockUnspent(options.walletName, true, cleaned.staleOutpoints).catch(() => undefined);
|
|
286
304
|
}
|
|
287
305
|
}
|
|
288
|
-
const
|
|
306
|
+
const lockedBeforeReserveInspection = await options.rpc.listLockUnspent(options.walletName).catch(() => []);
|
|
307
|
+
const lockedBeforeReserveInspectionKeys = new Set(lockedBeforeReserveInspection.map((outpoint) => outpointKey(outpoint)));
|
|
308
|
+
const reserveInspectionUnlocks = rawReserveOutpoints.filter((outpoint) => {
|
|
309
|
+
const key = outpointKey(outpoint);
|
|
310
|
+
return lockedBeforeReserveInspectionKeys.has(key) && !fixedInputKeys.has(key);
|
|
311
|
+
});
|
|
312
|
+
if (reserveInspectionUnlocks.length > 0) {
|
|
313
|
+
await options.rpc.lockUnspent(options.walletName, true, reserveInspectionUnlocks).catch(() => undefined);
|
|
314
|
+
}
|
|
315
|
+
const spendableUtxos = reserveInspectionUnlocks.length > 0 || options.spendableUtxos === undefined
|
|
316
|
+
? await options.rpc.listUnspent(options.walletName, 0).catch(() => [])
|
|
317
|
+
: options.spendableUtxos;
|
|
318
|
+
const previouslyProtectedUniverse = collectPersistentPolicyLockedOutpoints(state, spendableUtxos);
|
|
289
319
|
const reserveSynced = syncStateWithComputedReserve(state, spendableUtxos);
|
|
290
320
|
state = reserveSynced.state;
|
|
291
321
|
changed ||= reserveSynced.changed;
|
|
292
322
|
const protectedUniverse = collectPersistentPolicyLockedOutpoints(state, spendableUtxos);
|
|
293
|
-
if (protectedUniverse.length === 0) {
|
|
323
|
+
if (protectedUniverse.length === 0 && previouslyProtectedUniverse.length === 0) {
|
|
294
324
|
return {
|
|
295
325
|
state,
|
|
296
326
|
changed,
|
|
@@ -298,19 +328,30 @@ export async function reconcilePersistentPolicyLocks(options) {
|
|
|
298
328
|
};
|
|
299
329
|
}
|
|
300
330
|
const protectedUniverseKeys = new Set(protectedUniverse.map((outpoint) => outpointKey(outpoint)));
|
|
301
|
-
const
|
|
302
|
-
const
|
|
331
|
+
const previouslyProtectedUniverseKeys = new Set(previouslyProtectedUniverse.map((outpoint) => outpointKey(outpoint)));
|
|
332
|
+
const managedProtectedKeys = new Set([
|
|
333
|
+
...protectedUniverseKeys,
|
|
334
|
+
...previouslyProtectedUniverseKeys,
|
|
335
|
+
]);
|
|
303
336
|
const locked = await options.rpc.listLockUnspent(options.walletName).catch(() => []);
|
|
304
337
|
const spendableKeys = new Set(spendableUtxos.map((entry) => outpointKey(entry)));
|
|
338
|
+
const lockedKeys = new Set(locked.map((outpoint) => outpointKey(outpoint)));
|
|
305
339
|
const expectedLocked = protectedUniverse.filter((outpoint) => {
|
|
306
340
|
const key = outpointKey(outpoint);
|
|
307
|
-
return spendableKeys.has(key)
|
|
341
|
+
return (spendableKeys.has(key) || lockedKeys.has(key))
|
|
342
|
+
&& !fixedInputKeys.has(key)
|
|
343
|
+
&& !temporarilyUnlockedKeys.has(key);
|
|
308
344
|
});
|
|
309
345
|
const expectedLockedKeys = new Set(expectedLocked.map((outpoint) => outpointKey(outpoint)));
|
|
310
|
-
const
|
|
311
|
-
const
|
|
312
|
-
const
|
|
313
|
-
|
|
346
|
+
const lockedManaged = locked.filter((outpoint) => managedProtectedKeys.has(outpointKey(outpoint)));
|
|
347
|
+
const staleLocked = lockedManaged.filter((outpoint) => !expectedLockedKeys.has(outpointKey(outpoint)));
|
|
348
|
+
const missingLocked = protectedUniverse.filter((outpoint) => {
|
|
349
|
+
const key = outpointKey(outpoint);
|
|
350
|
+
return spendableKeys.has(key)
|
|
351
|
+
&& !fixedInputKeys.has(key)
|
|
352
|
+
&& !temporarilyUnlockedKeys.has(key)
|
|
353
|
+
&& !lockedKeys.has(key);
|
|
354
|
+
});
|
|
314
355
|
if (staleLocked.length > 0) {
|
|
315
356
|
await options.rpc.lockUnspent(options.walletName, true, staleLocked).catch(() => undefined);
|
|
316
357
|
}
|
|
@@ -18,6 +18,7 @@ interface WalletAnchorRpcClient extends WalletMutationRpcClient {
|
|
|
18
18
|
export interface AnchorDomainOptions {
|
|
19
19
|
domainName: string;
|
|
20
20
|
foundingMessageText?: string | null;
|
|
21
|
+
promptForFoundingMessageWhenMissing?: boolean;
|
|
21
22
|
dataDir: string;
|
|
22
23
|
databasePath: string;
|
|
23
24
|
provider?: WalletSecretProvider;
|
|
@@ -36,6 +37,7 @@ export interface AnchorDomainResult {
|
|
|
36
37
|
dedicatedIndex: number;
|
|
37
38
|
status: "live" | "confirmed";
|
|
38
39
|
reusedExisting: boolean;
|
|
40
|
+
foundingMessageText?: string | null;
|
|
39
41
|
}
|
|
40
42
|
export interface ClearPendingAnchorOptions {
|
|
41
43
|
domainName: string;
|