@cogcoin/client 1.1.4 → 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/progress/tty-renderer.js +3 -2
- package/dist/bitcoind/service.js +1 -1
- 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/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
package/dist/cli/runner.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createDefaultContext } from "./context.js";
|
|
2
2
|
import { writeLine } from "./io.js";
|
|
3
|
-
import { classifyCliError,
|
|
3
|
+
import { classifyCliError, formatCliTextError } from "./output.js";
|
|
4
4
|
import { HELP_TEXT, parseCliArgs } from "./parse.js";
|
|
5
5
|
import { runFollowCommand } from "./commands/follow.js";
|
|
6
6
|
import { runClientAdminCommand } from "./commands/client-admin.js";
|
|
@@ -15,14 +15,6 @@ import { runWalletAdminCommand } from "./commands/wallet-admin.js";
|
|
|
15
15
|
import { runWalletMutationCommand } from "./commands/wallet-mutation.js";
|
|
16
16
|
import { runWalletReadCommand } from "./commands/wallet-read.js";
|
|
17
17
|
import { maybeNotifyAboutCliUpdate } from "./update-notifier.js";
|
|
18
|
-
import { findWalletSeedRecord, loadWalletSeedIndex } from "../wallet/state/seed-index.js";
|
|
19
|
-
function commandUsesExistingWalletSeed(parsed) {
|
|
20
|
-
return parsed.seedName !== null
|
|
21
|
-
&& parsed.seedName !== "main"
|
|
22
|
-
&& parsed.command !== "restore"
|
|
23
|
-
&& parsed.command !== "wallet-delete"
|
|
24
|
-
&& parsed.command !== "wallet-restore";
|
|
25
|
-
}
|
|
26
18
|
export async function runCli(argv, contextOverrides = {}) {
|
|
27
19
|
const context = createDefaultContext(contextOverrides);
|
|
28
20
|
let parsed;
|
|
@@ -31,10 +23,6 @@ export async function runCli(argv, contextOverrides = {}) {
|
|
|
31
23
|
}
|
|
32
24
|
catch (error) {
|
|
33
25
|
const classified = classifyCliError(error);
|
|
34
|
-
if (isStructuredOutputMode(inferOutputMode(argv))) {
|
|
35
|
-
writeJsonValue(context.stdout, createErrorEnvelope("cogcoin/cli/v1", `cogcoin ${argv.join(" ")}`.trim(), classified.errorCode, classified.message));
|
|
36
|
-
return classified.exitCode;
|
|
37
|
-
}
|
|
38
26
|
writeLine(context.stderr, classified.message);
|
|
39
27
|
writeLine(context.stderr, HELP_TEXT.trimEnd());
|
|
40
28
|
return classified.exitCode;
|
|
@@ -44,115 +32,42 @@ export async function runCli(argv, contextOverrides = {}) {
|
|
|
44
32
|
return 0;
|
|
45
33
|
}
|
|
46
34
|
if (parsed.help || parsed.command === null) {
|
|
47
|
-
if (parsed.command === null && isStructuredOutputMode(parsed.outputMode)) {
|
|
48
|
-
writeJsonValue(context.stdout, createErrorEnvelope("cogcoin/cli/v1", "cogcoin", "cli_missing_command", "cli_missing_command"));
|
|
49
|
-
return 2;
|
|
50
|
-
}
|
|
51
35
|
writeLine(context.stdout, HELP_TEXT.trimEnd());
|
|
52
36
|
return parsed.help ? 0 : 2;
|
|
53
37
|
}
|
|
54
38
|
await maybeNotifyAboutCliUpdate(parsed, context);
|
|
55
39
|
try {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
40
|
+
switch (parsed.commandFamily) {
|
|
41
|
+
case "update":
|
|
42
|
+
return runUpdateCommand(parsed, context);
|
|
43
|
+
case "sync":
|
|
44
|
+
return runSyncCommand(parsed, context);
|
|
45
|
+
case "follow":
|
|
46
|
+
return runFollowCommand(parsed, context);
|
|
47
|
+
case "status":
|
|
48
|
+
return runStatusCommand(parsed, context);
|
|
49
|
+
case "client-admin":
|
|
50
|
+
return runClientAdminCommand(parsed, context);
|
|
51
|
+
case "service-runtime":
|
|
52
|
+
return runServiceRuntimeCommand(parsed, context);
|
|
53
|
+
case "wallet-admin":
|
|
54
|
+
return runWalletAdminCommand(parsed, context);
|
|
55
|
+
case "wallet-mutation":
|
|
56
|
+
return runWalletMutationCommand(parsed, context);
|
|
57
|
+
case "wallet-read":
|
|
58
|
+
return runWalletReadCommand(parsed, context);
|
|
59
|
+
case "mining-admin":
|
|
60
|
+
return runMiningAdminCommand(parsed, context);
|
|
61
|
+
case "mining-runtime":
|
|
62
|
+
return runMiningRuntimeCommand(parsed, context);
|
|
63
|
+
case "mining-read":
|
|
64
|
+
return runMiningReadCommand(parsed, context);
|
|
65
|
+
default:
|
|
66
|
+
return runWalletReadCommand(parsed, context);
|
|
76
67
|
}
|
|
77
|
-
if (parsed.command === "client-lock"
|
|
78
|
-
|| parsed.command === "client-unlock"
|
|
79
|
-
|| parsed.command === "client-change-password") {
|
|
80
|
-
return runClientAdminCommand(parsed, context);
|
|
81
|
-
}
|
|
82
|
-
if (parsed.command === "bitcoin-start"
|
|
83
|
-
|| parsed.command === "bitcoin-stop"
|
|
84
|
-
|| parsed.command === "bitcoin-status"
|
|
85
|
-
|| parsed.command === "indexer-start"
|
|
86
|
-
|| parsed.command === "indexer-stop"
|
|
87
|
-
|| parsed.command === "indexer-status") {
|
|
88
|
-
return runServiceRuntimeCommand(parsed, context);
|
|
89
|
-
}
|
|
90
|
-
if (parsed.command === "mine"
|
|
91
|
-
|| parsed.command === "mine-start"
|
|
92
|
-
|| parsed.command === "mine-stop") {
|
|
93
|
-
return runMiningRuntimeCommand(parsed, context);
|
|
94
|
-
}
|
|
95
|
-
if (parsed.command === "mine-setup"
|
|
96
|
-
|| parsed.command === "mine-prompt") {
|
|
97
|
-
return runMiningAdminCommand(parsed, context);
|
|
98
|
-
}
|
|
99
|
-
if (parsed.command === "init"
|
|
100
|
-
|| parsed.command === "restore"
|
|
101
|
-
|| parsed.command === "reset"
|
|
102
|
-
|| parsed.command === "repair"
|
|
103
|
-
|| parsed.command === "wallet-init"
|
|
104
|
-
|| parsed.command === "wallet-delete"
|
|
105
|
-
|| parsed.command === "wallet-restore"
|
|
106
|
-
|| parsed.command === "wallet-show-mnemonic") {
|
|
107
|
-
return runWalletAdminCommand(parsed, context);
|
|
108
|
-
}
|
|
109
|
-
if (parsed.command === "anchor"
|
|
110
|
-
|| parsed.command === "domain-anchor"
|
|
111
|
-
|| parsed.command === "register"
|
|
112
|
-
|| parsed.command === "domain-register"
|
|
113
|
-
|| parsed.command === "transfer"
|
|
114
|
-
|| parsed.command === "domain-transfer"
|
|
115
|
-
|| parsed.command === "sell"
|
|
116
|
-
|| parsed.command === "domain-sell"
|
|
117
|
-
|| parsed.command === "unsell"
|
|
118
|
-
|| parsed.command === "domain-unsell"
|
|
119
|
-
|| parsed.command === "buy"
|
|
120
|
-
|| parsed.command === "domain-buy"
|
|
121
|
-
|| parsed.command === "domain-endpoint-set"
|
|
122
|
-
|| parsed.command === "domain-endpoint-clear"
|
|
123
|
-
|| parsed.command === "domain-delegate-set"
|
|
124
|
-
|| parsed.command === "domain-delegate-clear"
|
|
125
|
-
|| parsed.command === "domain-miner-set"
|
|
126
|
-
|| parsed.command === "domain-miner-clear"
|
|
127
|
-
|| parsed.command === "domain-canonical"
|
|
128
|
-
|| parsed.command === "field-create"
|
|
129
|
-
|| parsed.command === "field-set"
|
|
130
|
-
|| parsed.command === "field-clear"
|
|
131
|
-
|| parsed.command === "bitcoin-transfer"
|
|
132
|
-
|| parsed.command === "send"
|
|
133
|
-
|| parsed.command === "claim"
|
|
134
|
-
|| parsed.command === "reclaim"
|
|
135
|
-
|| parsed.command === "cog-send"
|
|
136
|
-
|| parsed.command === "cog-claim"
|
|
137
|
-
|| parsed.command === "cog-reclaim"
|
|
138
|
-
|| parsed.command === "cog-lock"
|
|
139
|
-
|| parsed.command === "rep-give"
|
|
140
|
-
|| parsed.command === "rep-revoke") {
|
|
141
|
-
return runWalletMutationCommand(parsed, context);
|
|
142
|
-
}
|
|
143
|
-
if (parsed.command === "mine-prompt-list"
|
|
144
|
-
|| parsed.command === "mine-status"
|
|
145
|
-
|| parsed.command === "mine-log") {
|
|
146
|
-
return runMiningReadCommand(parsed, context);
|
|
147
|
-
}
|
|
148
|
-
return runWalletReadCommand(parsed, context);
|
|
149
68
|
}
|
|
150
69
|
catch (error) {
|
|
151
70
|
const classified = classifyCliError(error);
|
|
152
|
-
if (isStructuredOutputMode(parsed.outputMode)) {
|
|
153
|
-
writeJsonValue(context.stdout, createCommandJsonErrorEnvelope(parsed, error));
|
|
154
|
-
return classified.exitCode;
|
|
155
|
-
}
|
|
156
71
|
const formatted = formatCliTextError(error);
|
|
157
72
|
if (formatted !== null) {
|
|
158
73
|
for (const line of formatted) {
|
package/dist/cli/types.d.ts
CHANGED
|
@@ -7,15 +7,15 @@ import { attachOrStartManagedBitcoindService, probeManagedBitcoindService, stopM
|
|
|
7
7
|
import { openSqliteStore } from "../sqlite/index.js";
|
|
8
8
|
import type { ClientStoreAdapter } from "../types.js";
|
|
9
9
|
import type { WalletRuntimePaths } from "../wallet/runtime.js";
|
|
10
|
-
import type { WalletPrompter, initializeWallet,
|
|
10
|
+
import type { WalletPrompter, initializeWallet, previewResetWallet, repairWallet, resetWallet, showWalletMnemonic } from "../wallet/lifecycle.js";
|
|
11
11
|
import type { openWalletReadContext } from "../wallet/read/index.js";
|
|
12
12
|
import { loadRawWalletStateEnvelope, loadWalletState } from "../wallet/state/storage.js";
|
|
13
13
|
import type { WalletSecretProvider } from "../wallet/state/provider.js";
|
|
14
14
|
import type { ensureBuiltInMiningSetupIfNeeded, followMiningLog, inspectMiningControlPlane, inspectMiningDomainPromptState, readMiningLog, runForegroundMining, setupBuiltInMining, startBackgroundMining, stopBackgroundMining, updateMiningDomainPrompt } from "../wallet/mining/index.js";
|
|
15
15
|
import type { anchorDomain, transferBitcoin, 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";
|
|
16
|
+
import type { CommandHandlerFamily, CommandName } from "./command-registry.js";
|
|
17
|
+
export type { CommandHandlerFamily, CommandName } from "./command-registry.js";
|
|
16
18
|
export type ProgressOutput = "auto" | "tty" | "none";
|
|
17
|
-
export type OutputMode = "text" | "json" | "preview-json";
|
|
18
|
-
export type CommandName = "init" | "restore" | "reset" | "repair" | "update" | "sync" | "status" | "client-lock" | "client-change-password" | "client-unlock" | "follow" | "bitcoin-start" | "bitcoin-stop" | "bitcoin-status" | "bitcoin-transfer" | "indexer-start" | "indexer-stop" | "indexer-status" | "anchor" | "domain-anchor" | "register" | "domain-register" | "transfer" | "domain-transfer" | "sell" | "domain-sell" | "unsell" | "domain-unsell" | "buy" | "domain-buy" | "domain-endpoint-set" | "domain-endpoint-clear" | "domain-delegate-set" | "domain-delegate-clear" | "domain-miner-set" | "domain-miner-clear" | "domain-canonical" | "field-list" | "field-show" | "field-create" | "field-set" | "field-clear" | "send" | "claim" | "reclaim" | "cog-send" | "cog-claim" | "cog-reclaim" | "cog-lock" | "rep-give" | "rep-revoke" | "cog-balance" | "cog-locks" | "mine" | "mine-start" | "mine-stop" | "mine-setup" | "mine-prompt" | "mine-prompt-list" | "mine-status" | "mine-log" | "wallet-init" | "wallet-delete" | "wallet-restore" | "wallet-show-mnemonic" | "wallet-status" | "wallet-address" | "wallet-ids" | "address" | "ids" | "balance" | "locks" | "domain-list" | "domains" | "domain-show" | "show" | "fields" | "field";
|
|
19
19
|
export interface WritableLike {
|
|
20
20
|
isTTY?: boolean;
|
|
21
21
|
write(chunk: string): void;
|
|
@@ -29,14 +29,15 @@ export interface SignalSource {
|
|
|
29
29
|
}
|
|
30
30
|
export interface ParsedCliArgs {
|
|
31
31
|
command: CommandName | null;
|
|
32
|
+
commandFamily: CommandHandlerFamily | null;
|
|
33
|
+
invokedCommandTokens: readonly string[] | null;
|
|
34
|
+
invokedCommandPath: string | null;
|
|
32
35
|
args: string[];
|
|
33
36
|
help: boolean;
|
|
34
37
|
version: boolean;
|
|
35
|
-
outputMode: OutputMode;
|
|
36
38
|
dbPath: string | null;
|
|
37
39
|
dataDir: string | null;
|
|
38
40
|
progressOutput: ProgressOutput;
|
|
39
|
-
seedName: string | null;
|
|
40
41
|
unlockFor: string | null;
|
|
41
42
|
assumeYes: boolean;
|
|
42
43
|
force: boolean;
|
|
@@ -125,9 +126,7 @@ export interface CliRunnerContext {
|
|
|
125
126
|
loadWalletState?: typeof loadWalletState;
|
|
126
127
|
loadRawWalletStateEnvelope?: typeof loadRawWalletStateEnvelope;
|
|
127
128
|
initializeWallet?: typeof initializeWallet;
|
|
128
|
-
restoreWalletFromMnemonic?: typeof restoreWalletFromMnemonic;
|
|
129
129
|
previewResetWallet?: typeof previewResetWallet;
|
|
130
|
-
deleteImportedWalletSeed?: typeof deleteImportedWalletSeed;
|
|
131
130
|
showWalletMnemonic?: typeof showWalletMnemonic;
|
|
132
131
|
registerDomain?: typeof registerDomain;
|
|
133
132
|
anchorDomain?: typeof anchorDomain;
|
|
@@ -170,7 +169,7 @@ export interface CliRunnerContext {
|
|
|
170
169
|
resolveDefaultBitcoindDataDir?: () => string;
|
|
171
170
|
resolveDefaultClientDatabasePath?: () => string;
|
|
172
171
|
resolveUpdateCheckStatePath?: () => string;
|
|
173
|
-
resolveWalletRuntimePaths?: (
|
|
172
|
+
resolveWalletRuntimePaths?: () => WalletRuntimePaths;
|
|
174
173
|
}
|
|
175
174
|
export interface StopSignalWatcher {
|
|
176
175
|
cleanup(): void;
|
|
@@ -4,7 +4,7 @@ function isEligibleForUpdateNotification(parsed, context) {
|
|
|
4
4
|
if (parsed.command === "update") {
|
|
5
5
|
return false;
|
|
6
6
|
}
|
|
7
|
-
if (parsed.
|
|
7
|
+
if (parsed.help || parsed.version) {
|
|
8
8
|
return false;
|
|
9
9
|
}
|
|
10
10
|
return context.stdout.isTTY === true || context.stderr.isTTY === true;
|
|
@@ -88,7 +88,7 @@ export function getRepairRecommendation(context) {
|
|
|
88
88
|
return null;
|
|
89
89
|
}
|
|
90
90
|
if (context.localState.availability === "uninitialized") {
|
|
91
|
-
return "Run `cogcoin init` to create
|
|
91
|
+
return "Run `cogcoin init` to create or restore a wallet.";
|
|
92
92
|
}
|
|
93
93
|
if (context.localState.availability === "local-state-corrupt") {
|
|
94
94
|
return "Run `cogcoin repair` to recover local wallet state.";
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { attachOrStartManagedBitcoindService } from "../../bitcoind/service.js";
|
|
2
|
+
import { createRpcClient } from "../../bitcoind/node.js";
|
|
3
|
+
import type { ManagedCoreWalletReplicaStatus } from "../../bitcoind/types.js";
|
|
4
|
+
import type { WalletRuntimePaths } from "../runtime.js";
|
|
5
|
+
import { type WalletSecretProvider } from "../state/provider.js";
|
|
6
|
+
import type { WalletStateV1 } from "../types.js";
|
|
7
|
+
import type { WalletLifecycleRpcClient, WalletManagedCoreDependencies, WalletLifecycleResolvedContext } from "./types.js";
|
|
8
|
+
export declare function sanitizeWalletName(walletRootId: string): string;
|
|
9
|
+
export declare function normalizeLoadedWalletStateIfNeeded(options: WalletLifecycleResolvedContext & {
|
|
10
|
+
state: WalletStateV1;
|
|
11
|
+
source: "primary" | "backup";
|
|
12
|
+
dataDir?: string;
|
|
13
|
+
} & WalletManagedCoreDependencies): Promise<{
|
|
14
|
+
state: WalletStateV1;
|
|
15
|
+
source: "primary" | "backup";
|
|
16
|
+
}>;
|
|
17
|
+
export declare function importDescriptorIntoManagedCoreWallet(state: WalletStateV1, provider: WalletSecretProvider, paths: WalletRuntimePaths, dataDir: string, nowUnixMs: number, attachService?: typeof attachOrStartManagedBitcoindService, rpcFactory?: (config: Parameters<typeof createRpcClient>[0]) => WalletLifecycleRpcClient): Promise<WalletStateV1>;
|
|
18
|
+
export declare function recreateManagedCoreWalletReplica(state: WalletStateV1, provider: WalletSecretProvider, paths: WalletRuntimePaths, dataDir: string, nowUnixMs: number, options?: WalletManagedCoreDependencies): Promise<WalletStateV1>;
|
|
19
|
+
export declare function verifyManagedCoreWalletReplica(state: WalletStateV1, dataDir: string, dependencies?: WalletManagedCoreDependencies & {
|
|
20
|
+
nodeHandle?: {
|
|
21
|
+
rpc: Parameters<typeof createRpcClient>[0];
|
|
22
|
+
};
|
|
23
|
+
}): Promise<ManagedCoreWalletReplicaStatus>;
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
import { access, constants, rename } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { attachOrStartManagedBitcoindService, createManagedWalletReplica } from "../../bitcoind/service.js";
|
|
4
|
+
import { createRpcClient } from "../../bitcoind/node.js";
|
|
5
|
+
import { persistWalletCoinControlStateIfNeeded, normalizeWalletStateRecord } from "../coin-control.js";
|
|
6
|
+
import { persistNormalizedWalletDescriptorStateIfNeeded, resolveNormalizedWalletDescriptorState, } from "../descriptor-normalization.js";
|
|
7
|
+
import { normalizeMiningStateRecord } from "../mining/state.js";
|
|
8
|
+
import { createWalletSecretReference } from "../state/provider.js";
|
|
9
|
+
import { saveWalletState } from "../state/storage.js";
|
|
10
|
+
import { withUnlockedManagedCoreWallet } from "../managed-core-wallet.js";
|
|
11
|
+
export function sanitizeWalletName(walletRootId) {
|
|
12
|
+
return `cogcoin-${walletRootId}`.replace(/[^a-zA-Z0-9._-]+/g, "-").slice(0, 63);
|
|
13
|
+
}
|
|
14
|
+
async function pathExists(path) {
|
|
15
|
+
try {
|
|
16
|
+
await access(path, constants.F_OK);
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
export async function normalizeLoadedWalletStateIfNeeded(options) {
|
|
24
|
+
let state = options.state;
|
|
25
|
+
let source = options.source;
|
|
26
|
+
if (options.dataDir !== undefined) {
|
|
27
|
+
const node = await (options.attachService ?? attachOrStartManagedBitcoindService)({
|
|
28
|
+
dataDir: options.dataDir,
|
|
29
|
+
chain: "main",
|
|
30
|
+
startHeight: 0,
|
|
31
|
+
walletRootId: state.walletRootId,
|
|
32
|
+
});
|
|
33
|
+
try {
|
|
34
|
+
const normalized = await persistNormalizedWalletDescriptorStateIfNeeded({
|
|
35
|
+
state,
|
|
36
|
+
access: {
|
|
37
|
+
provider: options.provider,
|
|
38
|
+
secretReference: createWalletSecretReference(state.walletRootId),
|
|
39
|
+
},
|
|
40
|
+
paths: options.paths,
|
|
41
|
+
nowUnixMs: options.nowUnixMs,
|
|
42
|
+
replacePrimary: options.source === "backup",
|
|
43
|
+
rpc: (options.rpcFactory ?? createRpcClient)(node.rpc),
|
|
44
|
+
});
|
|
45
|
+
state = normalized.state;
|
|
46
|
+
source = normalized.changed ? "primary" : options.source;
|
|
47
|
+
const coinControl = await persistWalletCoinControlStateIfNeeded({
|
|
48
|
+
state,
|
|
49
|
+
access: {
|
|
50
|
+
provider: options.provider,
|
|
51
|
+
secretReference: createWalletSecretReference(state.walletRootId),
|
|
52
|
+
},
|
|
53
|
+
paths: options.paths,
|
|
54
|
+
nowUnixMs: options.nowUnixMs,
|
|
55
|
+
replacePrimary: source === "backup",
|
|
56
|
+
rpc: createRpcClient(node.rpc),
|
|
57
|
+
});
|
|
58
|
+
state = coinControl.state;
|
|
59
|
+
source = coinControl.changed ? "primary" : source;
|
|
60
|
+
}
|
|
61
|
+
finally {
|
|
62
|
+
await node.stop?.().catch(() => undefined);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return {
|
|
66
|
+
state: normalizeWalletStateRecord({
|
|
67
|
+
...state,
|
|
68
|
+
miningState: normalizeMiningStateRecord(state.miningState),
|
|
69
|
+
}),
|
|
70
|
+
source,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
export async function importDescriptorIntoManagedCoreWallet(state, provider, paths, dataDir, nowUnixMs, attachService = attachOrStartManagedBitcoindService, rpcFactory = createRpcClient) {
|
|
74
|
+
const node = await attachService({
|
|
75
|
+
dataDir,
|
|
76
|
+
chain: "main",
|
|
77
|
+
startHeight: 0,
|
|
78
|
+
walletRootId: state.walletRootId,
|
|
79
|
+
managedWalletPassphrase: state.managedCoreWallet.internalPassphrase,
|
|
80
|
+
});
|
|
81
|
+
const rpc = rpcFactory(node.rpc);
|
|
82
|
+
await createManagedWalletReplica(rpc, state.walletRootId, {
|
|
83
|
+
managedWalletPassphrase: state.managedCoreWallet.internalPassphrase,
|
|
84
|
+
});
|
|
85
|
+
const normalizedDescriptors = await resolveNormalizedWalletDescriptorState(state, rpc);
|
|
86
|
+
const walletName = sanitizeWalletName(state.walletRootId);
|
|
87
|
+
await withUnlockedManagedCoreWallet({
|
|
88
|
+
rpc,
|
|
89
|
+
walletName,
|
|
90
|
+
internalPassphrase: state.managedCoreWallet.internalPassphrase,
|
|
91
|
+
run: async () => {
|
|
92
|
+
const importResults = await rpc.importDescriptors(walletName, [{
|
|
93
|
+
desc: normalizedDescriptors.privateExternal,
|
|
94
|
+
timestamp: state.walletBirthTime,
|
|
95
|
+
active: false,
|
|
96
|
+
internal: false,
|
|
97
|
+
range: [0, state.descriptor.rangeEnd],
|
|
98
|
+
}]);
|
|
99
|
+
if (!importResults.every((result) => result.success)) {
|
|
100
|
+
throw new Error(`wallet_descriptor_import_failed_${JSON.stringify(importResults)}`);
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
});
|
|
104
|
+
const derivedFunding = await rpc.deriveAddresses(normalizedDescriptors.publicExternal, [0, 0]);
|
|
105
|
+
if (derivedFunding[0] !== state.funding.address) {
|
|
106
|
+
throw new Error("wallet_funding_address_verification_failed");
|
|
107
|
+
}
|
|
108
|
+
const descriptors = await rpc.listDescriptors(walletName);
|
|
109
|
+
const importedDescriptor = descriptors.descriptors.find((entry) => entry.desc === normalizedDescriptors.publicExternal);
|
|
110
|
+
if (importedDescriptor == null) {
|
|
111
|
+
throw new Error("wallet_descriptor_not_present_after_import");
|
|
112
|
+
}
|
|
113
|
+
const verifiedReplica = {
|
|
114
|
+
walletRootId: state.walletRootId,
|
|
115
|
+
walletName,
|
|
116
|
+
loaded: true,
|
|
117
|
+
descriptors: true,
|
|
118
|
+
privateKeysEnabled: true,
|
|
119
|
+
created: false,
|
|
120
|
+
proofStatus: "ready",
|
|
121
|
+
descriptorChecksum: normalizedDescriptors.checksum,
|
|
122
|
+
fundingAddress0: state.funding.address,
|
|
123
|
+
fundingScriptPubKeyHex0: state.funding.scriptPubKeyHex,
|
|
124
|
+
message: null,
|
|
125
|
+
};
|
|
126
|
+
const nextState = {
|
|
127
|
+
...state,
|
|
128
|
+
stateRevision: state.stateRevision + 1,
|
|
129
|
+
lastWrittenAtUnixMs: nowUnixMs,
|
|
130
|
+
descriptor: {
|
|
131
|
+
...state.descriptor,
|
|
132
|
+
privateExternal: normalizedDescriptors.privateExternal,
|
|
133
|
+
publicExternal: normalizedDescriptors.publicExternal,
|
|
134
|
+
checksum: normalizedDescriptors.checksum,
|
|
135
|
+
},
|
|
136
|
+
managedCoreWallet: {
|
|
137
|
+
...state.managedCoreWallet,
|
|
138
|
+
walletName,
|
|
139
|
+
descriptorChecksum: normalizedDescriptors.checksum,
|
|
140
|
+
walletAddress: verifiedReplica.fundingAddress0 ?? null,
|
|
141
|
+
walletScriptPubKeyHex: verifiedReplica.fundingScriptPubKeyHex0 ?? null,
|
|
142
|
+
proofStatus: "ready",
|
|
143
|
+
lastImportedAtUnixMs: nowUnixMs,
|
|
144
|
+
lastVerifiedAtUnixMs: nowUnixMs,
|
|
145
|
+
},
|
|
146
|
+
};
|
|
147
|
+
await saveWalletState({
|
|
148
|
+
primaryPath: paths.walletStatePath,
|
|
149
|
+
backupPath: paths.walletStateBackupPath,
|
|
150
|
+
}, nextState, {
|
|
151
|
+
provider,
|
|
152
|
+
secretReference: createWalletSecretReference(state.walletRootId),
|
|
153
|
+
});
|
|
154
|
+
return nextState;
|
|
155
|
+
}
|
|
156
|
+
export async function recreateManagedCoreWalletReplica(state, provider, paths, dataDir, nowUnixMs, options = {}) {
|
|
157
|
+
const walletName = sanitizeWalletName(state.walletRootId);
|
|
158
|
+
const walletDir = join(dataDir, "wallets", walletName);
|
|
159
|
+
const quarantineDir = `${walletDir}.quarantine-${nowUnixMs}`;
|
|
160
|
+
const node = await (options.attachService ?? attachOrStartManagedBitcoindService)({
|
|
161
|
+
dataDir,
|
|
162
|
+
chain: "main",
|
|
163
|
+
startHeight: 0,
|
|
164
|
+
walletRootId: state.walletRootId,
|
|
165
|
+
managedWalletPassphrase: state.managedCoreWallet.internalPassphrase,
|
|
166
|
+
});
|
|
167
|
+
const rpc = (options.rpcFactory ?? createRpcClient)(node.rpc);
|
|
168
|
+
if (rpc.unloadWallet != null) {
|
|
169
|
+
await rpc.unloadWallet(walletName, false).catch(() => undefined);
|
|
170
|
+
}
|
|
171
|
+
if (await pathExists(walletDir)) {
|
|
172
|
+
await rename(walletDir, quarantineDir).catch(() => undefined);
|
|
173
|
+
}
|
|
174
|
+
return importDescriptorIntoManagedCoreWallet({
|
|
175
|
+
...state,
|
|
176
|
+
managedCoreWallet: {
|
|
177
|
+
...state.managedCoreWallet,
|
|
178
|
+
proofStatus: "not-proven",
|
|
179
|
+
},
|
|
180
|
+
}, provider, paths, dataDir, nowUnixMs, options.attachService, options.rpcFactory);
|
|
181
|
+
}
|
|
182
|
+
export async function verifyManagedCoreWalletReplica(state, dataDir, dependencies = {}) {
|
|
183
|
+
const walletName = state.managedCoreWallet.walletName;
|
|
184
|
+
try {
|
|
185
|
+
const node = dependencies.nodeHandle ?? await (dependencies.attachService ?? attachOrStartManagedBitcoindService)({
|
|
186
|
+
dataDir,
|
|
187
|
+
chain: "main",
|
|
188
|
+
startHeight: 0,
|
|
189
|
+
walletRootId: state.walletRootId,
|
|
190
|
+
});
|
|
191
|
+
const rpc = (dependencies.rpcFactory ?? createRpcClient)(node.rpc);
|
|
192
|
+
const info = await rpc.getWalletInfo(walletName);
|
|
193
|
+
const descriptors = await rpc.listDescriptors(walletName);
|
|
194
|
+
const matchingDescriptor = state.managedCoreWallet.descriptorChecksum === null
|
|
195
|
+
? null
|
|
196
|
+
: descriptors.descriptors.find((entry) => entry.desc.endsWith(`#${state.managedCoreWallet.descriptorChecksum}`));
|
|
197
|
+
if (matchingDescriptor == null) {
|
|
198
|
+
return {
|
|
199
|
+
walletRootId: state.walletRootId,
|
|
200
|
+
walletName,
|
|
201
|
+
loaded: true,
|
|
202
|
+
descriptors: info.descriptors,
|
|
203
|
+
privateKeysEnabled: info.private_keys_enabled,
|
|
204
|
+
created: false,
|
|
205
|
+
proofStatus: "missing",
|
|
206
|
+
descriptorChecksum: state.managedCoreWallet.descriptorChecksum,
|
|
207
|
+
fundingAddress0: state.managedCoreWallet.walletAddress,
|
|
208
|
+
fundingScriptPubKeyHex0: state.managedCoreWallet.walletScriptPubKeyHex,
|
|
209
|
+
message: "Expected descriptor is missing from the managed Core wallet.",
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
const derived = await rpc.deriveAddresses(state.descriptor.publicExternal, [0, 0]);
|
|
213
|
+
if (derived[0] !== state.funding.address) {
|
|
214
|
+
return {
|
|
215
|
+
walletRootId: state.walletRootId,
|
|
216
|
+
walletName,
|
|
217
|
+
loaded: true,
|
|
218
|
+
descriptors: info.descriptors,
|
|
219
|
+
privateKeysEnabled: info.private_keys_enabled,
|
|
220
|
+
created: false,
|
|
221
|
+
proofStatus: "mismatch",
|
|
222
|
+
descriptorChecksum: state.managedCoreWallet.descriptorChecksum,
|
|
223
|
+
fundingAddress0: derived[0] ?? null,
|
|
224
|
+
fundingScriptPubKeyHex0: null,
|
|
225
|
+
message: "The managed Core wallet funding address does not match the trusted wallet state.",
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
return {
|
|
229
|
+
walletRootId: state.walletRootId,
|
|
230
|
+
walletName,
|
|
231
|
+
loaded: true,
|
|
232
|
+
descriptors: info.descriptors,
|
|
233
|
+
privateKeysEnabled: info.private_keys_enabled,
|
|
234
|
+
created: false,
|
|
235
|
+
proofStatus: "ready",
|
|
236
|
+
descriptorChecksum: state.managedCoreWallet.descriptorChecksum,
|
|
237
|
+
fundingAddress0: state.funding.address,
|
|
238
|
+
fundingScriptPubKeyHex0: state.funding.scriptPubKeyHex,
|
|
239
|
+
message: null,
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
catch (error) {
|
|
243
|
+
return {
|
|
244
|
+
walletRootId: state.walletRootId,
|
|
245
|
+
walletName,
|
|
246
|
+
loaded: false,
|
|
247
|
+
descriptors: false,
|
|
248
|
+
privateKeysEnabled: false,
|
|
249
|
+
created: false,
|
|
250
|
+
proofStatus: "not-proven",
|
|
251
|
+
descriptorChecksum: state.managedCoreWallet.descriptorChecksum,
|
|
252
|
+
fundingAddress0: state.managedCoreWallet.walletAddress,
|
|
253
|
+
fundingScriptPubKeyHex0: state.managedCoreWallet.walletScriptPubKeyHex,
|
|
254
|
+
message: error instanceof Error ? error.message : String(error),
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { MiningRuntimeStatusV1 } from "../mining/types.js";
|
|
2
|
+
import type { WalletRuntimePaths } from "../runtime.js";
|
|
3
|
+
import { type WalletSecretProvider } from "../state/provider.js";
|
|
4
|
+
import type { WalletStateV1 } from "../types.js";
|
|
5
|
+
import type { WalletPrompter, WalletRepairResult } from "./types.js";
|
|
6
|
+
export declare function createSilentNonInteractivePrompter(): WalletPrompter;
|
|
7
|
+
export declare function applyRepairStoppedMiningState(state: WalletStateV1): WalletStateV1;
|
|
8
|
+
export declare function createStoppedMiningRuntimeSnapshotForRepair(options: {
|
|
9
|
+
state: WalletStateV1;
|
|
10
|
+
snapshot: MiningRuntimeStatusV1 | null;
|
|
11
|
+
nowUnixMs: number;
|
|
12
|
+
}): MiningRuntimeStatusV1;
|
|
13
|
+
export declare function persistRepairState(options: {
|
|
14
|
+
state: WalletStateV1;
|
|
15
|
+
provider: WalletSecretProvider;
|
|
16
|
+
paths: WalletRuntimePaths;
|
|
17
|
+
nowUnixMs: number;
|
|
18
|
+
replacePrimary?: boolean;
|
|
19
|
+
}): Promise<WalletStateV1>;
|
|
20
|
+
export declare function cleanupMiningForRepair(options: {
|
|
21
|
+
paths: WalletRuntimePaths;
|
|
22
|
+
state: WalletStateV1;
|
|
23
|
+
snapshot: MiningRuntimeStatusV1 | null;
|
|
24
|
+
nowUnixMs: number;
|
|
25
|
+
}): Promise<{
|
|
26
|
+
preRepairRunMode: WalletRepairResult["miningPreRepairRunMode"];
|
|
27
|
+
}>;
|
|
28
|
+
export declare function canResumeBackgroundMiningAfterRepair(options: {
|
|
29
|
+
provider: WalletSecretProvider;
|
|
30
|
+
paths: WalletRuntimePaths;
|
|
31
|
+
repairedState: WalletStateV1;
|
|
32
|
+
bitcoindPostRepairHealth: WalletRepairResult["bitcoindPostRepairHealth"];
|
|
33
|
+
indexerPostRepairHealth: WalletRepairResult["indexerPostRepairHealth"];
|
|
34
|
+
}): Promise<boolean>;
|
|
35
|
+
export declare function resumeBackgroundMiningAfterRepair(options: {
|
|
36
|
+
miningPreRepairRunMode: WalletRepairResult["miningPreRepairRunMode"];
|
|
37
|
+
provider: WalletSecretProvider;
|
|
38
|
+
paths: WalletRuntimePaths;
|
|
39
|
+
repairedState: WalletStateV1;
|
|
40
|
+
bitcoindPostRepairHealth: WalletRepairResult["bitcoindPostRepairHealth"];
|
|
41
|
+
indexerPostRepairHealth: WalletRepairResult["indexerPostRepairHealth"];
|
|
42
|
+
dataDir: string;
|
|
43
|
+
databasePath: string;
|
|
44
|
+
startBackgroundMining?: typeof import("../mining/runner.js").startBackgroundMining;
|
|
45
|
+
}): Promise<{
|
|
46
|
+
miningResumeAction: WalletRepairResult["miningResumeAction"];
|
|
47
|
+
miningPostRepairRunMode: WalletRepairResult["miningPostRepairRunMode"];
|
|
48
|
+
miningResumeError: string | null;
|
|
49
|
+
}>;
|