@cogcoin/client 0.5.5 → 0.5.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/bitcoind/bootstrap/chainstate.d.ts +2 -1
- package/dist/bitcoind/bootstrap/chainstate.js +4 -1
- package/dist/bitcoind/bootstrap/controller.d.ts +4 -1
- package/dist/bitcoind/bootstrap/controller.js +42 -5
- package/dist/bitcoind/bootstrap/headers.d.ts +12 -0
- package/dist/bitcoind/bootstrap/headers.js +95 -10
- package/dist/bitcoind/client/factory.js +11 -2
- package/dist/bitcoind/client/managed-client.d.ts +1 -1
- package/dist/bitcoind/client/managed-client.js +2 -2
- package/dist/bitcoind/client/sync-engine.js +48 -13
- package/dist/bitcoind/indexer-daemon.d.ts +7 -0
- package/dist/bitcoind/indexer-daemon.js +31 -22
- package/dist/bitcoind/processing-start-height.d.ts +7 -0
- package/dist/bitcoind/processing-start-height.js +9 -0
- package/dist/bitcoind/progress/controller.js +1 -0
- package/dist/bitcoind/progress/formatting.js +4 -1
- package/dist/bitcoind/retryable-rpc.d.ts +11 -0
- package/dist/bitcoind/retryable-rpc.js +30 -0
- package/dist/bitcoind/service.d.ts +16 -1
- package/dist/bitcoind/service.js +228 -115
- package/dist/bitcoind/testing.d.ts +1 -1
- package/dist/bitcoind/testing.js +1 -1
- package/dist/bitcoind/types.d.ts +1 -0
- package/dist/cli/commands/follow.js +9 -0
- package/dist/cli/commands/service-runtime.js +150 -134
- package/dist/cli/commands/sync.js +9 -0
- package/dist/cli/commands/wallet-admin.js +77 -21
- package/dist/cli/context.js +4 -2
- package/dist/cli/mutation-json.js +2 -0
- package/dist/cli/output.js +2 -0
- package/dist/cli/parse.d.ts +1 -1
- package/dist/cli/parse.js +6 -0
- package/dist/cli/preview-json.js +2 -0
- package/dist/cli/runner.js +1 -0
- package/dist/cli/types.d.ts +6 -3
- package/dist/cli/types.js +1 -1
- package/dist/cli/wallet-format.js +134 -14
- package/dist/wallet/lifecycle.d.ts +6 -0
- package/dist/wallet/lifecycle.js +109 -37
- package/dist/wallet/read/context.js +10 -4
- package/dist/wallet/reset.d.ts +61 -2
- package/dist/wallet/reset.js +208 -63
- package/dist/wallet/root-resolution.d.ts +20 -0
- package/dist/wallet/root-resolution.js +37 -0
- package/dist/wallet/runtime.d.ts +1 -0
- package/dist/wallet/runtime.js +1 -0
- package/dist/wallet/state/crypto.d.ts +3 -0
- package/dist/wallet/state/crypto.js +3 -0
- package/dist/wallet/state/storage.d.ts +7 -1
- package/dist/wallet/state/storage.js +39 -0
- package/dist/wallet/types.d.ts +1 -0
- package/package.json +1 -1
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { dirname } from "node:path";
|
|
2
2
|
import { loadBundledGenesisParameters } from "@cogcoin/indexer";
|
|
3
|
+
import { resolveCogcoinProcessingStartHeight } from "../../bitcoind/processing-start-height.js";
|
|
3
4
|
import { UNINITIALIZED_WALLET_ROOT_ID, resolveManagedServicePaths } from "../../bitcoind/service-paths.js";
|
|
5
|
+
import { resolveWalletRootIdFromLocalArtifacts, } from "../../wallet/root-resolution.js";
|
|
4
6
|
import { writeLine } from "../io.js";
|
|
5
7
|
import { createSuccessEnvelope, describeCanonicalCommand, writeHandledCliError, writeJsonValue, } from "../output.js";
|
|
6
8
|
function formatBool(value) {
|
|
@@ -12,66 +14,39 @@ function formatMaybe(value) {
|
|
|
12
14
|
function formatCompatibility(value) {
|
|
13
15
|
return value.replaceAll("-", " ");
|
|
14
16
|
}
|
|
15
|
-
|
|
16
|
-
const paths = context.resolveWalletRuntimePaths();
|
|
17
|
-
try {
|
|
18
|
-
const loaded = await context.loadWalletState({
|
|
19
|
-
primaryPath: paths.walletStatePath,
|
|
20
|
-
backupPath: paths.walletStateBackupPath,
|
|
21
|
-
}, {
|
|
22
|
-
provider: context.walletSecretProvider,
|
|
23
|
-
});
|
|
24
|
-
return {
|
|
25
|
-
walletRootId: loaded.state.walletRootId,
|
|
26
|
-
source: "wallet-state",
|
|
27
|
-
};
|
|
28
|
-
}
|
|
29
|
-
catch {
|
|
30
|
-
// fall through
|
|
31
|
-
}
|
|
32
|
-
try {
|
|
33
|
-
const unlockSession = await context.loadUnlockSession(paths.walletUnlockSessionPath, {
|
|
34
|
-
provider: context.walletSecretProvider,
|
|
35
|
-
});
|
|
36
|
-
return {
|
|
37
|
-
walletRootId: unlockSession.walletRootId,
|
|
38
|
-
source: "unlock-session",
|
|
39
|
-
};
|
|
40
|
-
}
|
|
41
|
-
catch {
|
|
42
|
-
// fall through
|
|
43
|
-
}
|
|
44
|
-
try {
|
|
45
|
-
const explicitLock = await context.loadWalletExplicitLock(paths.walletExplicitLockPath);
|
|
46
|
-
if (explicitLock?.walletRootId) {
|
|
47
|
-
return {
|
|
48
|
-
walletRootId: explicitLock.walletRootId,
|
|
49
|
-
source: "explicit-lock",
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
catch {
|
|
54
|
-
// fall through
|
|
55
|
-
}
|
|
56
|
-
const fallbackProbe = await context.probeManagedBitcoindService({
|
|
57
|
-
dataDir,
|
|
58
|
-
chain: "main",
|
|
59
|
-
startHeight: 0,
|
|
60
|
-
walletRootId: UNINITIALIZED_WALLET_ROOT_ID,
|
|
61
|
-
});
|
|
62
|
-
if (fallbackProbe.status?.walletRootId) {
|
|
63
|
-
return {
|
|
64
|
-
walletRootId: fallbackProbe.status.walletRootId,
|
|
65
|
-
source: "bitcoind-status",
|
|
66
|
-
};
|
|
67
|
-
}
|
|
17
|
+
function serviceStatusEntry(label, value, ok) {
|
|
68
18
|
return {
|
|
19
|
+
text: `${label}: ${value}`,
|
|
20
|
+
ok,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
function formatServiceStatusSection(header, entries) {
|
|
24
|
+
return [header, ...entries.map((entry) => `${entry.ok ? "✓" : "✗"} ${entry.text}`)].join("\n");
|
|
25
|
+
}
|
|
26
|
+
function formatSectionedServiceStatusReport(options) {
|
|
27
|
+
const parts = [
|
|
28
|
+
`\n⛭ ${options.title} ⛭`,
|
|
29
|
+
...options.sections.map((section) => formatServiceStatusSection(section.header, section.entries)),
|
|
30
|
+
];
|
|
31
|
+
if (options.nextStep !== null) {
|
|
32
|
+
parts.push(`Next step: ${options.nextStep}`);
|
|
33
|
+
}
|
|
34
|
+
return parts.join("\n\n");
|
|
35
|
+
}
|
|
36
|
+
async function resolveEffectiveWalletRootId(context) {
|
|
37
|
+
return resolveWalletRootIdFromLocalArtifacts({
|
|
38
|
+
paths: context.resolveWalletRuntimePaths(),
|
|
39
|
+
provider: context.walletSecretProvider,
|
|
40
|
+
loadRawWalletStateEnvelope: context.loadRawWalletStateEnvelope,
|
|
41
|
+
loadUnlockSession: context.loadUnlockSession,
|
|
42
|
+
loadWalletExplicitLock: context.loadWalletExplicitLock,
|
|
43
|
+
}).catch(() => ({
|
|
69
44
|
walletRootId: UNINITIALIZED_WALLET_ROOT_ID,
|
|
70
45
|
source: "default-uninitialized",
|
|
71
|
-
};
|
|
46
|
+
}));
|
|
72
47
|
}
|
|
73
48
|
async function inspectManagedBitcoindStatus(dataDir, context) {
|
|
74
|
-
const resolution = await resolveEffectiveWalletRootId(
|
|
49
|
+
const resolution = await resolveEffectiveWalletRootId(context);
|
|
75
50
|
const probe = await context.probeManagedBitcoindService({
|
|
76
51
|
dataDir,
|
|
77
52
|
chain: "main",
|
|
@@ -114,7 +89,7 @@ async function inspectManagedBitcoindStatus(dataDir, context) {
|
|
|
114
89
|
};
|
|
115
90
|
}
|
|
116
91
|
async function inspectManagedIndexerStatus(dataDir, context) {
|
|
117
|
-
const resolution = await resolveEffectiveWalletRootId(
|
|
92
|
+
const resolution = await resolveEffectiveWalletRootId(context);
|
|
118
93
|
const runtimeRoot = resolveManagedServicePaths(dataDir, resolution.walletRootId).walletRuntimeRoot;
|
|
119
94
|
const probe = await context.probeIndexerDaemon({
|
|
120
95
|
dataDir,
|
|
@@ -144,96 +119,137 @@ async function inspectManagedIndexerStatus(dataDir, context) {
|
|
|
144
119
|
};
|
|
145
120
|
}
|
|
146
121
|
function formatBitcoinStatusReport(payload) {
|
|
147
|
-
const
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
`Compatibility: ${formatCompatibility(payload.compatibility)}`,
|
|
122
|
+
const compatibilityOk = payload.compatibility === "compatible";
|
|
123
|
+
const serviceStateOk = payload.service?.state === "ready";
|
|
124
|
+
const nodeOk = payload.node !== null && payload.nodeError === null;
|
|
125
|
+
const managedServiceEntries = [
|
|
126
|
+
serviceStatusEntry("Compatibility", formatCompatibility(payload.compatibility), compatibilityOk),
|
|
153
127
|
];
|
|
154
128
|
if (payload.service !== null) {
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
129
|
+
managedServiceEntries.push(serviceStatusEntry("Service state", payload.service.state, serviceStateOk));
|
|
130
|
+
managedServiceEntries.push(serviceStatusEntry("Process id", formatMaybe(payload.service.processId), serviceStateOk));
|
|
131
|
+
managedServiceEntries.push(serviceStatusEntry("Service instance", payload.service.serviceInstanceId, serviceStateOk));
|
|
132
|
+
managedServiceEntries.push(serviceStatusEntry("Runtime root", payload.service.runtimeRoot, serviceStateOk));
|
|
133
|
+
managedServiceEntries.push(serviceStatusEntry("Chain", payload.service.chain, serviceStateOk));
|
|
134
|
+
managedServiceEntries.push(serviceStatusEntry("RPC", payload.service.rpc.url, serviceStateOk));
|
|
135
|
+
managedServiceEntries.push(serviceStatusEntry("RPC cookie", payload.service.rpc.cookieFile, serviceStateOk));
|
|
136
|
+
managedServiceEntries.push(serviceStatusEntry("ZMQ", payload.service.zmq.endpoint, serviceStateOk));
|
|
137
|
+
managedServiceEntries.push(serviceStatusEntry("P2P port", String(payload.service.p2pPort), serviceStateOk));
|
|
138
|
+
managedServiceEntries.push(serviceStatusEntry("Started at", String(payload.service.startedAtUnixMs), serviceStateOk));
|
|
139
|
+
managedServiceEntries.push(serviceStatusEntry("Heartbeat at", String(payload.service.heartbeatAtUnixMs), serviceStateOk));
|
|
140
|
+
managedServiceEntries.push(serviceStatusEntry("Updated at", String(payload.service.updatedAtUnixMs), serviceStateOk));
|
|
141
|
+
managedServiceEntries.push(serviceStatusEntry("Managed Core wallet", payload.service.walletReplica?.proofStatus ?? "unavailable", payload.service.walletReplica?.proofStatus === "ready"));
|
|
168
142
|
if (payload.service.lastError !== null) {
|
|
169
|
-
|
|
143
|
+
managedServiceEntries.push(serviceStatusEntry("Service error", payload.service.lastError, false));
|
|
170
144
|
}
|
|
171
145
|
}
|
|
172
146
|
else {
|
|
173
|
-
|
|
174
|
-
}
|
|
175
|
-
if (payload.node !== null) {
|
|
176
|
-
lines.push(`Bitcoin best height: ${payload.node.bestHeight}`);
|
|
177
|
-
lines.push(`Bitcoin headers: ${payload.node.headerHeight}`);
|
|
178
|
-
lines.push(`Bitcoin best hash: ${payload.node.bestHash}`);
|
|
179
|
-
lines.push(`Verification progress: ${formatMaybe(payload.node.verificationProgress)}`);
|
|
180
|
-
lines.push(`Initial block download: ${formatBool(payload.node.initialBlockDownload)}`);
|
|
181
|
-
lines.push(`Network active: ${formatBool(payload.node.networkActive)}`);
|
|
182
|
-
lines.push(`Connections: ${payload.node.connections}`);
|
|
183
|
-
lines.push(`Inbound connections: ${formatMaybe(payload.node.inboundConnections)}`);
|
|
184
|
-
lines.push(`Outbound connections: ${formatMaybe(payload.node.outboundConnections)}`);
|
|
185
|
-
}
|
|
186
|
-
else {
|
|
187
|
-
lines.push("Bitcoin node: unavailable");
|
|
147
|
+
managedServiceEntries.push(serviceStatusEntry("Service state", "unavailable", false));
|
|
188
148
|
}
|
|
149
|
+
const bitcoinNodeEntries = payload.node !== null
|
|
150
|
+
? [
|
|
151
|
+
serviceStatusEntry("Best height", String(payload.node.bestHeight), nodeOk),
|
|
152
|
+
serviceStatusEntry("Headers", String(payload.node.headerHeight), nodeOk),
|
|
153
|
+
serviceStatusEntry("Best hash", payload.node.bestHash, nodeOk),
|
|
154
|
+
serviceStatusEntry("Verification progress", formatMaybe(payload.node.verificationProgress), nodeOk),
|
|
155
|
+
serviceStatusEntry("Initial block download", formatBool(payload.node.initialBlockDownload), nodeOk),
|
|
156
|
+
serviceStatusEntry("Network active", formatBool(payload.node.networkActive), nodeOk),
|
|
157
|
+
serviceStatusEntry("Connections", String(payload.node.connections), nodeOk),
|
|
158
|
+
serviceStatusEntry("Inbound connections", formatMaybe(payload.node.inboundConnections), nodeOk),
|
|
159
|
+
serviceStatusEntry("Outbound connections", formatMaybe(payload.node.outboundConnections), nodeOk),
|
|
160
|
+
]
|
|
161
|
+
: [serviceStatusEntry("Node state", "unavailable", false)];
|
|
189
162
|
if (payload.nodeError !== null) {
|
|
190
|
-
|
|
191
|
-
}
|
|
192
|
-
if (payload.compatibility === "unreachable") {
|
|
193
|
-
lines.push("Recommended next step: Run `cogcoin bitcoin start` to start the managed Bitcoin service.");
|
|
163
|
+
bitcoinNodeEntries.push(serviceStatusEntry("Node error", payload.nodeError, false));
|
|
194
164
|
}
|
|
195
|
-
return
|
|
165
|
+
return formatSectionedServiceStatusReport({
|
|
166
|
+
title: "Bitcoin Status",
|
|
167
|
+
sections: [
|
|
168
|
+
{
|
|
169
|
+
header: "Paths",
|
|
170
|
+
entries: [
|
|
171
|
+
serviceStatusEntry("Bitcoin datadir", payload.dataDir, true),
|
|
172
|
+
serviceStatusEntry("Wallet root", payload.walletRootId, true),
|
|
173
|
+
serviceStatusEntry("Wallet root source", payload.walletRootSource, true),
|
|
174
|
+
],
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
header: "Managed Service",
|
|
178
|
+
entries: managedServiceEntries,
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
header: "Bitcoin Node",
|
|
182
|
+
entries: bitcoinNodeEntries,
|
|
183
|
+
},
|
|
184
|
+
],
|
|
185
|
+
nextStep: payload.compatibility === "unreachable"
|
|
186
|
+
? "Run `cogcoin bitcoin start` to start the managed Bitcoin service."
|
|
187
|
+
: null,
|
|
188
|
+
});
|
|
196
189
|
}
|
|
197
190
|
function formatIndexerStatusReport(payload) {
|
|
198
|
-
const
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
`Observed source: ${payload.source}`,
|
|
191
|
+
const compatibilityOk = payload.compatibility === "compatible";
|
|
192
|
+
const observedSourceOk = payload.source === "probe";
|
|
193
|
+
const daemonStateOk = payload.daemon?.state === "synced";
|
|
194
|
+
const managedServiceEntries = [
|
|
195
|
+
serviceStatusEntry("Compatibility", formatCompatibility(payload.compatibility), compatibilityOk),
|
|
196
|
+
serviceStatusEntry("Observed source", payload.source, observedSourceOk),
|
|
205
197
|
];
|
|
206
198
|
if (payload.daemon !== null) {
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
lines.push(`Core best height: ${formatMaybe(payload.daemon.coreBestHeight)}`);
|
|
218
|
-
lines.push(`Core best hash: ${formatMaybe(payload.daemon.coreBestHash)}`);
|
|
219
|
-
lines.push(`Applied tip height: ${formatMaybe(payload.daemon.appliedTipHeight)}`);
|
|
220
|
-
lines.push(`Applied tip hash: ${formatMaybe(payload.daemon.appliedTipHash)}`);
|
|
221
|
-
lines.push(`Snapshot sequence: ${formatMaybe(payload.daemon.snapshotSeq)}`);
|
|
222
|
-
lines.push(`Backlog blocks: ${formatMaybe(payload.daemon.backlogBlocks)}`);
|
|
223
|
-
lines.push(`Reorg depth: ${formatMaybe(payload.daemon.reorgDepth)}`);
|
|
224
|
-
lines.push(`Active snapshots: ${payload.daemon.activeSnapshotCount}`);
|
|
225
|
-
lines.push(`Last applied at: ${formatMaybe(payload.daemon.lastAppliedAtUnixMs)}`);
|
|
199
|
+
managedServiceEntries.push(serviceStatusEntry("Daemon state", payload.daemon.state, daemonStateOk));
|
|
200
|
+
managedServiceEntries.push(serviceStatusEntry("Process id", formatMaybe(payload.daemon.processId), daemonStateOk));
|
|
201
|
+
managedServiceEntries.push(serviceStatusEntry("Daemon instance", payload.daemon.daemonInstanceId, daemonStateOk));
|
|
202
|
+
managedServiceEntries.push(serviceStatusEntry("Runtime root", payload.daemon.runtimeRoot, daemonStateOk));
|
|
203
|
+
managedServiceEntries.push(serviceStatusEntry("Schema version", payload.daemon.schemaVersion, daemonStateOk));
|
|
204
|
+
managedServiceEntries.push(serviceStatusEntry("Started at", String(payload.daemon.startedAtUnixMs), daemonStateOk));
|
|
205
|
+
managedServiceEntries.push(serviceStatusEntry("Heartbeat at", String(payload.daemon.heartbeatAtUnixMs), daemonStateOk));
|
|
206
|
+
managedServiceEntries.push(serviceStatusEntry("Updated at", String(payload.daemon.updatedAtUnixMs), daemonStateOk));
|
|
207
|
+
managedServiceEntries.push(serviceStatusEntry("IPC ready", formatBool(payload.daemon.ipcReady), payload.daemon.ipcReady));
|
|
208
|
+
managedServiceEntries.push(serviceStatusEntry("RPC reachable", formatBool(payload.daemon.rpcReachable), payload.daemon.rpcReachable));
|
|
226
209
|
if (payload.daemon.lastError !== null) {
|
|
227
|
-
|
|
210
|
+
managedServiceEntries.push(serviceStatusEntry("Daemon error", payload.daemon.lastError, false));
|
|
228
211
|
}
|
|
229
212
|
}
|
|
230
213
|
else {
|
|
231
|
-
|
|
232
|
-
}
|
|
233
|
-
if (payload.compatibility === "unreachable") {
|
|
234
|
-
lines.push("Recommended next step: Run `cogcoin indexer start` to start the managed Cogcoin indexer.");
|
|
214
|
+
managedServiceEntries.push(serviceStatusEntry("Daemon state", "unavailable", false));
|
|
235
215
|
}
|
|
236
|
-
|
|
216
|
+
const indexerStateEntries = payload.daemon !== null
|
|
217
|
+
? [
|
|
218
|
+
serviceStatusEntry("Core best height", formatMaybe(payload.daemon.coreBestHeight), daemonStateOk),
|
|
219
|
+
serviceStatusEntry("Core best hash", formatMaybe(payload.daemon.coreBestHash), daemonStateOk),
|
|
220
|
+
serviceStatusEntry("Applied tip height", formatMaybe(payload.daemon.appliedTipHeight), daemonStateOk),
|
|
221
|
+
serviceStatusEntry("Applied tip hash", formatMaybe(payload.daemon.appliedTipHash), daemonStateOk),
|
|
222
|
+
serviceStatusEntry("Snapshot sequence", formatMaybe(payload.daemon.snapshotSeq), daemonStateOk),
|
|
223
|
+
serviceStatusEntry("Backlog blocks", formatMaybe(payload.daemon.backlogBlocks), daemonStateOk),
|
|
224
|
+
serviceStatusEntry("Reorg depth", formatMaybe(payload.daemon.reorgDepth), daemonStateOk),
|
|
225
|
+
serviceStatusEntry("Active snapshots", String(payload.daemon.activeSnapshotCount), daemonStateOk),
|
|
226
|
+
serviceStatusEntry("Last applied at", formatMaybe(payload.daemon.lastAppliedAtUnixMs), daemonStateOk),
|
|
227
|
+
]
|
|
228
|
+
: [serviceStatusEntry("Daemon state", "unavailable", false)];
|
|
229
|
+
return formatSectionedServiceStatusReport({
|
|
230
|
+
title: "Indexer Status",
|
|
231
|
+
sections: [
|
|
232
|
+
{
|
|
233
|
+
header: "Paths",
|
|
234
|
+
entries: [
|
|
235
|
+
serviceStatusEntry("Bitcoin datadir", payload.dataDir, true),
|
|
236
|
+
serviceStatusEntry("Wallet root", payload.walletRootId, true),
|
|
237
|
+
serviceStatusEntry("Wallet root source", payload.walletRootSource, true),
|
|
238
|
+
],
|
|
239
|
+
},
|
|
240
|
+
{
|
|
241
|
+
header: "Managed Service",
|
|
242
|
+
entries: managedServiceEntries,
|
|
243
|
+
},
|
|
244
|
+
{
|
|
245
|
+
header: "Indexer State",
|
|
246
|
+
entries: indexerStateEntries,
|
|
247
|
+
},
|
|
248
|
+
],
|
|
249
|
+
nextStep: payload.compatibility === "unreachable"
|
|
250
|
+
? "Run `cogcoin indexer start` to start the managed Cogcoin indexer."
|
|
251
|
+
: null,
|
|
252
|
+
});
|
|
237
253
|
}
|
|
238
254
|
function buildStatusMessages(payload) {
|
|
239
255
|
const warnings = [];
|
|
@@ -287,7 +303,7 @@ export async function runServiceRuntimeCommand(parsed, context) {
|
|
|
287
303
|
return 0;
|
|
288
304
|
}
|
|
289
305
|
if (parsed.command === "bitcoin-start") {
|
|
290
|
-
const resolution = await resolveEffectiveWalletRootId(
|
|
306
|
+
const resolution = await resolveEffectiveWalletRootId(context);
|
|
291
307
|
const probe = await context.probeManagedBitcoindService({
|
|
292
308
|
dataDir,
|
|
293
309
|
chain: "main",
|
|
@@ -298,7 +314,7 @@ export async function runServiceRuntimeCommand(parsed, context) {
|
|
|
298
314
|
await context.attachManagedBitcoindService({
|
|
299
315
|
dataDir,
|
|
300
316
|
chain: "main",
|
|
301
|
-
startHeight: genesis
|
|
317
|
+
startHeight: resolveCogcoinProcessingStartHeight(genesis),
|
|
302
318
|
walletRootId: resolution.walletRootId,
|
|
303
319
|
});
|
|
304
320
|
const bitcoindStatus = probe.compatibility === "compatible" ? "already-running" : "started";
|
|
@@ -324,7 +340,7 @@ export async function runServiceRuntimeCommand(parsed, context) {
|
|
|
324
340
|
return 0;
|
|
325
341
|
}
|
|
326
342
|
if (parsed.command === "bitcoin-stop") {
|
|
327
|
-
const resolution = await resolveEffectiveWalletRootId(
|
|
343
|
+
const resolution = await resolveEffectiveWalletRootId(context);
|
|
328
344
|
const indexer = await context.stopIndexerDaemonService({
|
|
329
345
|
dataDir,
|
|
330
346
|
walletRootId: resolution.walletRootId,
|
|
@@ -349,7 +365,7 @@ export async function runServiceRuntimeCommand(parsed, context) {
|
|
|
349
365
|
return 0;
|
|
350
366
|
}
|
|
351
367
|
if (parsed.command === "indexer-start") {
|
|
352
|
-
const resolution = await resolveEffectiveWalletRootId(
|
|
368
|
+
const resolution = await resolveEffectiveWalletRootId(context);
|
|
353
369
|
const dbPath = parsed.dbPath ?? context.resolveDefaultClientDatabasePath();
|
|
354
370
|
await context.ensureDirectory(dirname(dbPath));
|
|
355
371
|
const genesis = await loadBundledGenesisParameters();
|
|
@@ -362,7 +378,7 @@ export async function runServiceRuntimeCommand(parsed, context) {
|
|
|
362
378
|
await context.attachManagedBitcoindService({
|
|
363
379
|
dataDir,
|
|
364
380
|
chain: "main",
|
|
365
|
-
startHeight: genesis
|
|
381
|
+
startHeight: resolveCogcoinProcessingStartHeight(genesis),
|
|
366
382
|
walletRootId: resolution.walletRootId,
|
|
367
383
|
});
|
|
368
384
|
const indexerProbe = await context.probeIndexerDaemon({
|
|
@@ -401,7 +417,7 @@ export async function runServiceRuntimeCommand(parsed, context) {
|
|
|
401
417
|
return 0;
|
|
402
418
|
}
|
|
403
419
|
if (parsed.command === "indexer-stop") {
|
|
404
|
-
const resolution = await resolveEffectiveWalletRootId(
|
|
420
|
+
const resolution = await resolveEffectiveWalletRootId(context);
|
|
405
421
|
const indexer = await context.stopIndexerDaemonService({
|
|
406
422
|
dataDir,
|
|
407
423
|
walletRootId: resolution.walletRootId,
|
|
@@ -1,11 +1,19 @@
|
|
|
1
1
|
import { dirname } from "node:path";
|
|
2
2
|
import { formatManagedSyncErrorMessage } from "../../bitcoind/errors.js";
|
|
3
|
+
import { resolveWalletRootIdFromLocalArtifacts } from "../../wallet/root-resolution.js";
|
|
3
4
|
import { writeLine } from "../io.js";
|
|
4
5
|
import { classifyCliError } from "../output.js";
|
|
5
6
|
import { createStopSignalWatcher, waitForCompletionOrStop } from "../signals.js";
|
|
6
7
|
export async function runSyncCommand(parsed, context) {
|
|
7
8
|
const dbPath = parsed.dbPath ?? context.resolveDefaultClientDatabasePath();
|
|
8
9
|
const dataDir = parsed.dataDir ?? context.resolveDefaultBitcoindDataDir();
|
|
10
|
+
const walletRoot = await resolveWalletRootIdFromLocalArtifacts({
|
|
11
|
+
paths: context.resolveWalletRuntimePaths(),
|
|
12
|
+
provider: context.walletSecretProvider,
|
|
13
|
+
loadRawWalletStateEnvelope: context.loadRawWalletStateEnvelope,
|
|
14
|
+
loadUnlockSession: context.loadUnlockSession,
|
|
15
|
+
loadWalletExplicitLock: context.loadWalletExplicitLock,
|
|
16
|
+
});
|
|
9
17
|
await context.ensureDirectory(dirname(dbPath));
|
|
10
18
|
const store = await context.openSqliteStore({ filename: dbPath });
|
|
11
19
|
let storeOwned = true;
|
|
@@ -14,6 +22,7 @@ export async function runSyncCommand(parsed, context) {
|
|
|
14
22
|
store,
|
|
15
23
|
databasePath: dbPath,
|
|
16
24
|
dataDir,
|
|
25
|
+
walletRootId: walletRoot.walletRootId,
|
|
17
26
|
progressOutput: parsed.progressOutput,
|
|
18
27
|
});
|
|
19
28
|
storeOwned = false;
|
|
@@ -21,6 +21,66 @@ function getResetWarnings(result) {
|
|
|
21
21
|
? ["Some existing Cogcoin secret-provider entries could not be discovered from the remaining local wallet artifacts and may need manual cleanup."]
|
|
22
22
|
: [];
|
|
23
23
|
}
|
|
24
|
+
function getResetNextSteps(result) {
|
|
25
|
+
return result.walletAction === "deleted" || result.walletAction === "not-present"
|
|
26
|
+
? ["Run `cogcoin init` to create a new wallet."]
|
|
27
|
+
: ["Run `cogcoin sync` to bootstrap assumeutxo and the managed Bitcoin/indexer state."];
|
|
28
|
+
}
|
|
29
|
+
function formatResetBitcoinDataDirStatus(result) {
|
|
30
|
+
if (result.bitcoinDataDir.status === "outside-reset-scope") {
|
|
31
|
+
return "preserved (outside reset scope)";
|
|
32
|
+
}
|
|
33
|
+
return result.bitcoinDataDir.status;
|
|
34
|
+
}
|
|
35
|
+
function resetTextEntry(label, value, ok) {
|
|
36
|
+
return {
|
|
37
|
+
text: `${label}: ${value}`,
|
|
38
|
+
ok,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
function formatResetSection(header, entries) {
|
|
42
|
+
return [header, ...entries.map((entry) => `${entry.ok ? "✓" : "✗"} ${entry.text}`)].join("\n");
|
|
43
|
+
}
|
|
44
|
+
function formatResetResultText(result) {
|
|
45
|
+
const warnings = getResetWarnings(result);
|
|
46
|
+
const nextStep = getResetNextSteps(result)[0] ?? null;
|
|
47
|
+
const secretCleanupOk = result.secretCleanupStatus !== "unknown" && result.secretCleanupStatus !== "failed";
|
|
48
|
+
const managedCleanupOk = result.stoppedProcesses.survivors === 0;
|
|
49
|
+
const outcomeEntries = [
|
|
50
|
+
resetTextEntry("Wallet action", result.walletAction, true),
|
|
51
|
+
resetTextEntry("Snapshot", result.bootstrapSnapshot.status, true),
|
|
52
|
+
resetTextEntry("Bitcoin datadir", formatResetBitcoinDataDirStatus(result), true),
|
|
53
|
+
resetTextEntry("Secret cleanup", result.secretCleanupStatus, secretCleanupOk),
|
|
54
|
+
];
|
|
55
|
+
if (result.walletAction !== "retain-mnemonic" && result.walletOldRootId !== null) {
|
|
56
|
+
outcomeEntries.push(resetTextEntry("Previous wallet root", result.walletOldRootId, true));
|
|
57
|
+
}
|
|
58
|
+
if (result.walletAction !== "retain-mnemonic" && result.walletNewRootId !== null) {
|
|
59
|
+
outcomeEntries.push(resetTextEntry("New wallet root", result.walletNewRootId, true));
|
|
60
|
+
}
|
|
61
|
+
const sections = [
|
|
62
|
+
formatResetSection("Paths", [
|
|
63
|
+
resetTextEntry("Data root", result.dataRoot, true),
|
|
64
|
+
]),
|
|
65
|
+
formatResetSection("Reset Outcome", outcomeEntries),
|
|
66
|
+
formatResetSection("Managed Cleanup", [
|
|
67
|
+
resetTextEntry("Managed bitcoind processes stopped", String(result.stoppedProcesses.managedBitcoind), managedCleanupOk),
|
|
68
|
+
resetTextEntry("Indexer daemons stopped", String(result.stoppedProcesses.indexerDaemon), managedCleanupOk),
|
|
69
|
+
resetTextEntry("Background miners stopped", String(result.stoppedProcesses.backgroundMining), managedCleanupOk),
|
|
70
|
+
]),
|
|
71
|
+
];
|
|
72
|
+
if (warnings.length > 0) {
|
|
73
|
+
sections.push(formatResetSection("Warnings", warnings.map((warning) => resetTextEntry("Warning", warning, false))));
|
|
74
|
+
}
|
|
75
|
+
const parts = [
|
|
76
|
+
"\n⛭ Cogcoin Reset ⛭",
|
|
77
|
+
...sections,
|
|
78
|
+
];
|
|
79
|
+
if (nextStep !== null) {
|
|
80
|
+
parts.push(`Next step: ${nextStep}`);
|
|
81
|
+
}
|
|
82
|
+
return parts.join("\n\n");
|
|
83
|
+
}
|
|
24
84
|
export async function runWalletAdminCommand(parsed, context) {
|
|
25
85
|
const runtimePaths = context.resolveWalletRuntimePaths();
|
|
26
86
|
const stopWatcher = createOwnedLockCleanupSignalWatcher(context.signalSource, context.forceExit, [
|
|
@@ -31,9 +91,9 @@ export async function runWalletAdminCommand(parsed, context) {
|
|
|
31
91
|
]);
|
|
32
92
|
try {
|
|
33
93
|
const outcome = await waitForCompletionOrStop((async () => {
|
|
34
|
-
const dataDir = parsed.dataDir ?? context.resolveDefaultBitcoindDataDir();
|
|
35
94
|
const provider = context.walletSecretProvider;
|
|
36
95
|
if (parsed.command === "init" || parsed.command === "wallet-init") {
|
|
96
|
+
const dataDir = parsed.dataDir ?? context.resolveDefaultBitcoindDataDir();
|
|
37
97
|
const prompter = createCommandPrompter(parsed, context);
|
|
38
98
|
const result = await context.initializeWallet({
|
|
39
99
|
dataDir,
|
|
@@ -59,6 +119,7 @@ export async function runWalletAdminCommand(parsed, context) {
|
|
|
59
119
|
return 0;
|
|
60
120
|
}
|
|
61
121
|
if (parsed.command === "restore" || parsed.command === "wallet-restore") {
|
|
122
|
+
const dataDir = parsed.dataDir ?? context.resolveDefaultBitcoindDataDir();
|
|
62
123
|
const prompter = createCommandPrompter(parsed, context);
|
|
63
124
|
const result = await context.restoreWalletFromMnemonic({
|
|
64
125
|
dataDir,
|
|
@@ -88,6 +149,14 @@ export async function runWalletAdminCommand(parsed, context) {
|
|
|
88
149
|
}
|
|
89
150
|
return 0;
|
|
90
151
|
}
|
|
152
|
+
if (parsed.command === "wallet-show-mnemonic") {
|
|
153
|
+
const prompter = createCommandPrompter(parsed, context);
|
|
154
|
+
await context.showWalletMnemonic({
|
|
155
|
+
provider,
|
|
156
|
+
prompter,
|
|
157
|
+
});
|
|
158
|
+
return 0;
|
|
159
|
+
}
|
|
91
160
|
const dbPath = parsed.dbPath ?? context.resolveDefaultClientDatabasePath();
|
|
92
161
|
if (parsed.command === "unlock" || parsed.command === "wallet-unlock") {
|
|
93
162
|
const durationMs = parseUnlockDurationToMs(parsed.unlockFor);
|
|
@@ -105,6 +174,7 @@ export async function runWalletAdminCommand(parsed, context) {
|
|
|
105
174
|
return 0;
|
|
106
175
|
}
|
|
107
176
|
if (parsed.command === "reset") {
|
|
177
|
+
const dataDir = parsed.dataDir ?? context.resolveDefaultBitcoindDataDir();
|
|
108
178
|
if (parsed.outputMode === "preview-json") {
|
|
109
179
|
const preview = await context.previewResetWallet({
|
|
110
180
|
dataDir,
|
|
@@ -122,32 +192,15 @@ export async function runWalletAdminCommand(parsed, context) {
|
|
|
122
192
|
if (parsed.outputMode === "json") {
|
|
123
193
|
writeJsonValue(context.stdout, createMutationSuccessEnvelope(resolveStableMutationJsonSchema(parsed), describeCanonicalCommand(parsed), "completed", buildResetMutationData(result), {
|
|
124
194
|
warnings: getResetWarnings(result),
|
|
125
|
-
nextSteps: result
|
|
126
|
-
? ["Run `cogcoin init` to create a new wallet."]
|
|
127
|
-
: ["Run `cogcoin status` to inspect the reset local state."],
|
|
195
|
+
nextSteps: getResetNextSteps(result),
|
|
128
196
|
}));
|
|
129
197
|
return 0;
|
|
130
198
|
}
|
|
131
|
-
writeLine(context.stdout,
|
|
132
|
-
writeLine(context.stdout, `Data root: ${result.dataRoot}`);
|
|
133
|
-
writeLine(context.stdout, `Wallet action: ${result.walletAction}`);
|
|
134
|
-
writeLine(context.stdout, `Snapshot: ${result.bootstrapSnapshot.status}`);
|
|
135
|
-
writeLine(context.stdout, `Secret cleanup: ${result.secretCleanupStatus}`);
|
|
136
|
-
writeLine(context.stdout, `Managed bitcoind processes stopped: ${result.stoppedProcesses.managedBitcoind}`);
|
|
137
|
-
writeLine(context.stdout, `Indexer daemons stopped: ${result.stoppedProcesses.indexerDaemon}`);
|
|
138
|
-
writeLine(context.stdout, `Background miners stopped: ${result.stoppedProcesses.backgroundMining}`);
|
|
139
|
-
if (result.walletOldRootId !== null) {
|
|
140
|
-
writeLine(context.stdout, `Previous wallet root: ${result.walletOldRootId}`);
|
|
141
|
-
}
|
|
142
|
-
if (result.walletNewRootId !== null) {
|
|
143
|
-
writeLine(context.stdout, `New wallet root: ${result.walletNewRootId}`);
|
|
144
|
-
}
|
|
145
|
-
for (const warning of getResetWarnings(result)) {
|
|
146
|
-
writeLine(context.stdout, `Warning: ${warning}`);
|
|
147
|
-
}
|
|
199
|
+
writeLine(context.stdout, formatResetResultText(result));
|
|
148
200
|
return 0;
|
|
149
201
|
}
|
|
150
202
|
if (parsed.command === "wallet-export") {
|
|
203
|
+
const dataDir = parsed.dataDir ?? context.resolveDefaultBitcoindDataDir();
|
|
151
204
|
const prompter = createCommandPrompter(parsed, context);
|
|
152
205
|
const result = await context.exportWallet({
|
|
153
206
|
archivePath: parsed.args[0],
|
|
@@ -166,6 +219,7 @@ export async function runWalletAdminCommand(parsed, context) {
|
|
|
166
219
|
return 0;
|
|
167
220
|
}
|
|
168
221
|
if (parsed.command === "wallet-import") {
|
|
222
|
+
const dataDir = parsed.dataDir ?? context.resolveDefaultBitcoindDataDir();
|
|
169
223
|
const prompter = createCommandPrompter(parsed, context);
|
|
170
224
|
const result = await context.importWallet({
|
|
171
225
|
archivePath: parsed.args[0],
|
|
@@ -185,6 +239,7 @@ export async function runWalletAdminCommand(parsed, context) {
|
|
|
185
239
|
return 0;
|
|
186
240
|
}
|
|
187
241
|
if (parsed.command === "wallet-lock") {
|
|
242
|
+
const dataDir = parsed.dataDir ?? context.resolveDefaultBitcoindDataDir();
|
|
188
243
|
const result = await context.lockWallet({
|
|
189
244
|
dataDir,
|
|
190
245
|
provider,
|
|
@@ -202,6 +257,7 @@ export async function runWalletAdminCommand(parsed, context) {
|
|
|
202
257
|
return 0;
|
|
203
258
|
}
|
|
204
259
|
if (parsed.command === "repair") {
|
|
260
|
+
const dataDir = parsed.dataDir ?? context.resolveDefaultBitcoindDataDir();
|
|
205
261
|
const result = await context.repairWallet({
|
|
206
262
|
dataDir,
|
|
207
263
|
databasePath: dbPath,
|
package/dist/cli/context.js
CHANGED
|
@@ -6,12 +6,12 @@ import { resolveDefaultBitcoindDataDirForTesting, resolveDefaultClientDatabasePa
|
|
|
6
6
|
import { openManagedBitcoindClient } from "../bitcoind/index.js";
|
|
7
7
|
import { inspectPassiveClientStatus } from "../passive-status.js";
|
|
8
8
|
import { openSqliteStore } from "../sqlite/index.js";
|
|
9
|
-
import { exportWallet, importWallet, initializeWallet, lockWallet, previewResetWallet, repairWallet, resetWallet, restoreWalletFromMnemonic, unlockWallet, } from "../wallet/lifecycle.js";
|
|
9
|
+
import { exportWallet, importWallet, initializeWallet, lockWallet, previewResetWallet, repairWallet, resetWallet, restoreWalletFromMnemonic, showWalletMnemonic, unlockWallet, } from "../wallet/lifecycle.js";
|
|
10
10
|
import { resolveWalletRuntimePathsForTesting } from "../wallet/runtime.js";
|
|
11
11
|
import { openWalletReadContext } from "../wallet/read/index.js";
|
|
12
12
|
import { loadWalletExplicitLock } from "../wallet/state/explicit-lock.js";
|
|
13
13
|
import { loadUnlockSession } from "../wallet/state/session.js";
|
|
14
|
-
import { loadWalletState } from "../wallet/state/storage.js";
|
|
14
|
+
import { loadRawWalletStateEnvelope, loadWalletState } from "../wallet/state/storage.js";
|
|
15
15
|
import { disableMiningHooks, enableMiningHooks, followMiningLog, inspectMiningControlPlane, readMiningLog, runForegroundMining, setupBuiltInMining, startBackgroundMining, stopBackgroundMining, } from "../wallet/mining/index.js";
|
|
16
16
|
import { createLazyDefaultWalletSecretProvider } from "../wallet/state/provider.js";
|
|
17
17
|
import { anchorDomain, 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";
|
|
@@ -39,6 +39,7 @@ export function createDefaultContext(overrides = {}) {
|
|
|
39
39
|
previewResetWallet: overrides.previewResetWallet ?? previewResetWallet,
|
|
40
40
|
exportWallet: overrides.exportWallet ?? exportWallet,
|
|
41
41
|
importWallet: overrides.importWallet ?? importWallet,
|
|
42
|
+
showWalletMnemonic: overrides.showWalletMnemonic ?? showWalletMnemonic,
|
|
42
43
|
unlockWallet: overrides.unlockWallet ?? unlockWallet,
|
|
43
44
|
lockWallet: overrides.lockWallet ?? lockWallet,
|
|
44
45
|
registerDomain: overrides.registerDomain ?? registerDomain,
|
|
@@ -88,6 +89,7 @@ export function createDefaultContext(overrides = {}) {
|
|
|
88
89
|
stopIndexerDaemonService: overrides.stopIndexerDaemonService ?? stopIndexerDaemonService,
|
|
89
90
|
readPackageVersion: overrides.readPackageVersion ?? readPackageVersionFromDisk,
|
|
90
91
|
loadWalletState: overrides.loadWalletState ?? loadWalletState,
|
|
92
|
+
loadRawWalletStateEnvelope: overrides.loadRawWalletStateEnvelope ?? loadRawWalletStateEnvelope,
|
|
91
93
|
loadUnlockSession: overrides.loadUnlockSession ?? loadUnlockSession,
|
|
92
94
|
loadWalletExplicitLock: overrides.loadWalletExplicitLock ?? loadWalletExplicitLock,
|
|
93
95
|
resolveDefaultBitcoindDataDir: overrides.resolveDefaultBitcoindDataDir ?? resolveDefaultBitcoindDataDirForTesting,
|
|
@@ -150,6 +150,7 @@ export function buildResetMutationData(result) {
|
|
|
150
150
|
walletOldRootId: result.walletOldRootId,
|
|
151
151
|
walletNewRootId: result.walletNewRootId,
|
|
152
152
|
bootstrapSnapshot: result.bootstrapSnapshot,
|
|
153
|
+
bitcoinDataDir: result.bitcoinDataDir,
|
|
153
154
|
stoppedProcesses: result.stoppedProcesses,
|
|
154
155
|
secretCleanupStatus: result.secretCleanupStatus,
|
|
155
156
|
},
|
|
@@ -165,6 +166,7 @@ export function buildResetMutationData(result) {
|
|
|
165
166
|
walletOldRootId: result.walletOldRootId,
|
|
166
167
|
walletNewRootId: result.walletNewRootId,
|
|
167
168
|
bootstrapSnapshot: result.bootstrapSnapshot,
|
|
169
|
+
bitcoinDataDir: result.bitcoinDataDir,
|
|
168
170
|
removedPaths: result.removedPaths,
|
|
169
171
|
},
|
|
170
172
|
});
|
package/dist/cli/output.js
CHANGED
|
@@ -735,6 +735,8 @@ export function describeCanonicalCommand(parsed) {
|
|
|
735
735
|
case "restore":
|
|
736
736
|
case "wallet-restore":
|
|
737
737
|
return "cogcoin restore";
|
|
738
|
+
case "wallet-show-mnemonic":
|
|
739
|
+
return "cogcoin wallet show-mnemonic";
|
|
738
740
|
case "unlock":
|
|
739
741
|
case "wallet-unlock":
|
|
740
742
|
return "cogcoin unlock";
|