@cogcoin/client 0.5.5 → 0.5.7
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 +11 -2
- 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/getblock-archive.d.ts +39 -0
- package/dist/bitcoind/bootstrap/getblock-archive.js +548 -0
- package/dist/bitcoind/bootstrap/headers.d.ts +12 -0
- package/dist/bitcoind/bootstrap/headers.js +95 -10
- package/dist/bitcoind/bootstrap.d.ts +1 -0
- package/dist/bitcoind/bootstrap.js +1 -0
- package/dist/bitcoind/client/factory.js +91 -28
- package/dist/bitcoind/client/managed-client.d.ts +1 -1
- package/dist/bitcoind/client/managed-client.js +4 -3
- package/dist/bitcoind/client/sync-engine.js +55 -13
- package/dist/bitcoind/errors.js +18 -0
- package/dist/bitcoind/indexer-daemon-main.js +78 -0
- package/dist/bitcoind/indexer-daemon.d.ts +10 -1
- package/dist/bitcoind/indexer-daemon.js +44 -28
- package/dist/bitcoind/node.js +2 -0
- package/dist/bitcoind/processing-start-height.d.ts +7 -0
- package/dist/bitcoind/processing-start-height.js +9 -0
- package/dist/bitcoind/progress/constants.d.ts +1 -0
- package/dist/bitcoind/progress/constants.js +1 -0
- package/dist/bitcoind/progress/controller.d.ts +22 -0
- package/dist/bitcoind/progress/controller.js +49 -23
- package/dist/bitcoind/progress/formatting.js +29 -1
- package/dist/bitcoind/progress/render-policy.d.ts +35 -0
- package/dist/bitcoind/progress/render-policy.js +81 -0
- package/dist/bitcoind/retryable-rpc.d.ts +11 -0
- package/dist/bitcoind/retryable-rpc.js +30 -0
- package/dist/bitcoind/service-paths.js +2 -6
- package/dist/bitcoind/service.d.ts +21 -2
- package/dist/bitcoind/service.js +274 -122
- package/dist/bitcoind/testing.d.ts +2 -2
- package/dist/bitcoind/testing.js +2 -2
- package/dist/bitcoind/types.d.ts +36 -1
- package/dist/cli/commands/follow.js +11 -0
- package/dist/cli/commands/getblock-archive-restart.d.ts +5 -0
- package/dist/cli/commands/getblock-archive-restart.js +15 -0
- package/dist/cli/commands/mining-admin.js +4 -0
- package/dist/cli/commands/mining-read.js +8 -5
- package/dist/cli/commands/mining-runtime.js +4 -0
- package/dist/cli/commands/service-runtime.js +150 -134
- package/dist/cli/commands/status.js +2 -0
- package/dist/cli/commands/sync.js +11 -0
- package/dist/cli/commands/wallet-admin.js +106 -24
- package/dist/cli/commands/wallet-mutation.js +57 -4
- package/dist/cli/commands/wallet-read.js +2 -0
- package/dist/cli/context.js +8 -4
- package/dist/cli/mutation-command-groups.d.ts +2 -1
- package/dist/cli/mutation-command-groups.js +5 -0
- package/dist/cli/mutation-json.d.ts +18 -2
- package/dist/cli/mutation-json.js +49 -0
- package/dist/cli/mutation-success.d.ts +1 -0
- package/dist/cli/mutation-success.js +2 -2
- package/dist/cli/output.js +86 -1
- package/dist/cli/parse.d.ts +1 -1
- package/dist/cli/parse.js +133 -3
- package/dist/cli/preview-json.d.ts +10 -1
- package/dist/cli/preview-json.js +32 -0
- package/dist/cli/prompt.js +1 -1
- package/dist/cli/runner.js +4 -0
- package/dist/cli/types.d.ts +15 -5
- package/dist/cli/types.js +1 -1
- package/dist/cli/wallet-format.js +140 -14
- package/dist/wallet/lifecycle.d.ts +21 -1
- package/dist/wallet/lifecycle.js +252 -116
- package/dist/wallet/mining/visualizer.d.ts +11 -6
- package/dist/wallet/mining/visualizer.js +32 -15
- package/dist/wallet/read/context.js +10 -4
- package/dist/wallet/reset.d.ts +61 -2
- package/dist/wallet/reset.js +246 -89
- package/dist/wallet/root-resolution.d.ts +20 -0
- package/dist/wallet/root-resolution.js +37 -0
- package/dist/wallet/runtime.d.ts +13 -1
- package/dist/wallet/runtime.js +54 -11
- package/dist/wallet/state/crypto.d.ts +3 -0
- package/dist/wallet/state/crypto.js +3 -0
- package/dist/wallet/state/provider.d.ts +1 -0
- package/dist/wallet/state/provider.js +119 -3
- package/dist/wallet/state/seed-index.d.ts +43 -0
- package/dist/wallet/state/seed-index.js +151 -0
- package/dist/wallet/state/storage.d.ts +7 -1
- package/dist/wallet/state/storage.js +39 -0
- package/dist/wallet/tx/anchor.d.ts +22 -0
- package/dist/wallet/tx/anchor.js +215 -8
- package/dist/wallet/tx/index.d.ts +1 -1
- package/dist/wallet/tx/index.js +1 -1
- package/dist/wallet/types.d.ts +1 -0
- package/package.json +1 -1
|
@@ -1,10 +1,19 @@
|
|
|
1
1
|
import { dirname } from "node:path";
|
|
2
|
+
import { resolveWalletRootIdFromLocalArtifacts } from "../../wallet/root-resolution.js";
|
|
2
3
|
import { usesTtyProgress, writeLine } from "../io.js";
|
|
3
4
|
import { classifyCliError } from "../output.js";
|
|
4
5
|
import { createStopSignalWatcher } from "../signals.js";
|
|
6
|
+
import { confirmGetblockArchiveRestart } from "./getblock-archive-restart.js";
|
|
5
7
|
export async function runFollowCommand(parsed, context) {
|
|
6
8
|
const dbPath = parsed.dbPath ?? context.resolveDefaultClientDatabasePath();
|
|
7
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
|
+
});
|
|
8
17
|
await context.ensureDirectory(dirname(dbPath));
|
|
9
18
|
const store = await context.openSqliteStore({ filename: dbPath });
|
|
10
19
|
let storeOwned = true;
|
|
@@ -13,7 +22,9 @@ export async function runFollowCommand(parsed, context) {
|
|
|
13
22
|
store,
|
|
14
23
|
databasePath: dbPath,
|
|
15
24
|
dataDir,
|
|
25
|
+
walletRootId: walletRoot.walletRootId,
|
|
16
26
|
progressOutput: parsed.progressOutput,
|
|
27
|
+
confirmGetblockArchiveRestart: async (options) => confirmGetblockArchiveRestart(parsed, context, options),
|
|
17
28
|
});
|
|
18
29
|
storeOwned = false;
|
|
19
30
|
const stopWatcher = createStopSignalWatcher(context.signalSource, context.stderr, client, context.forceExit);
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { ParsedCliArgs, RequiredCliRunnerContext } from "../types.js";
|
|
2
|
+
export declare function confirmGetblockArchiveRestart(parsed: ParsedCliArgs, context: RequiredCliRunnerContext, options: {
|
|
3
|
+
currentArchiveEndHeight: number | null;
|
|
4
|
+
nextArchiveEndHeight: number;
|
|
5
|
+
}): Promise<boolean>;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export async function confirmGetblockArchiveRestart(parsed, context, options) {
|
|
2
|
+
if (parsed.assumeYes) {
|
|
3
|
+
return true;
|
|
4
|
+
}
|
|
5
|
+
const prompter = context.createPrompter();
|
|
6
|
+
if (!prompter.isInteractive) {
|
|
7
|
+
return false;
|
|
8
|
+
}
|
|
9
|
+
const currentLabel = options.currentArchiveEndHeight === null
|
|
10
|
+
? "without a getblock archive"
|
|
11
|
+
: `with a getblock archive through height ${options.currentArchiveEndHeight.toLocaleString()}`;
|
|
12
|
+
prompter.writeLine(`Managed bitcoind is already running ${currentLabel}. A newer getblock archive through height ${options.nextArchiveEndHeight.toLocaleString()} is available.`);
|
|
13
|
+
const answer = (await prompter.prompt(`Restart managed bitcoind to load the getblock archive through height ${options.nextArchiveEndHeight.toLocaleString()}? [y/N]: `)).trim().toLowerCase();
|
|
14
|
+
return answer === "y" || answer === "yes";
|
|
15
|
+
}
|
|
@@ -12,11 +12,13 @@ function createCommandPrompter(parsed, context) {
|
|
|
12
12
|
export async function runMiningAdminCommand(parsed, context) {
|
|
13
13
|
try {
|
|
14
14
|
const provider = context.walletSecretProvider;
|
|
15
|
+
const runtimePaths = context.resolveWalletRuntimePaths(parsed.seedName);
|
|
15
16
|
if (parsed.command === "hooks-mining-enable") {
|
|
16
17
|
const prompter = createCommandPrompter(parsed, context);
|
|
17
18
|
const view = await context.enableMiningHooks({
|
|
18
19
|
provider,
|
|
19
20
|
prompter,
|
|
21
|
+
paths: runtimePaths,
|
|
20
22
|
});
|
|
21
23
|
const nextSteps = getHooksEnableMiningNextSteps();
|
|
22
24
|
if (parsed.outputMode === "preview-json") {
|
|
@@ -40,6 +42,7 @@ export async function runMiningAdminCommand(parsed, context) {
|
|
|
40
42
|
if (parsed.command === "hooks-mining-disable") {
|
|
41
43
|
const view = await context.disableMiningHooks({
|
|
42
44
|
provider,
|
|
45
|
+
paths: runtimePaths,
|
|
43
46
|
});
|
|
44
47
|
if (parsed.outputMode === "preview-json") {
|
|
45
48
|
writeJsonValue(context.stdout, createPreviewSuccessEnvelope(resolvePreviewJsonSchema(parsed), describeCanonicalCommand(parsed), "disabled", buildHooksPreviewData("hooks-disable-mining", view)));
|
|
@@ -57,6 +60,7 @@ export async function runMiningAdminCommand(parsed, context) {
|
|
|
57
60
|
const view = await context.setupBuiltInMining({
|
|
58
61
|
provider,
|
|
59
62
|
prompter,
|
|
63
|
+
paths: runtimePaths,
|
|
60
64
|
});
|
|
61
65
|
const nextSteps = getMineSetupNextSteps();
|
|
62
66
|
if (parsed.outputMode === "preview-json") {
|
|
@@ -5,9 +5,7 @@ import { writeLine } from "../io.js";
|
|
|
5
5
|
import { inspectWalletLocalState } from "../../wallet/read/index.js";
|
|
6
6
|
import { createErrorEnvelope, createSuccessEnvelope, describeCanonicalCommand, normalizeListPage, writeJsonValue, } from "../output.js";
|
|
7
7
|
import { buildHooksStatusJson, buildMineLogJson, buildMineStatusJson } from "../read-json.js";
|
|
8
|
-
|
|
9
|
-
async function readRotationIndices() {
|
|
10
|
-
const paths = resolveWalletRuntimePathsForTesting();
|
|
8
|
+
async function readRotationIndices(paths) {
|
|
11
9
|
const rotation = [];
|
|
12
10
|
for (let index = 1; index <= 4; index += 1) {
|
|
13
11
|
try {
|
|
@@ -26,10 +24,12 @@ export async function runMiningReadCommand(parsed, context) {
|
|
|
26
24
|
try {
|
|
27
25
|
const dbPath = parsed.dbPath ?? context.resolveDefaultClientDatabasePath();
|
|
28
26
|
const dataDir = parsed.dataDir ?? context.resolveDefaultBitcoindDataDir();
|
|
27
|
+
const runtimePaths = context.resolveWalletRuntimePaths(parsed.seedName);
|
|
29
28
|
await context.ensureDirectory(dirname(dbPath));
|
|
30
29
|
if (parsed.command === "hooks-mining-status") {
|
|
31
30
|
const localState = await inspectWalletLocalState({
|
|
32
31
|
secretProvider: context.walletSecretProvider,
|
|
32
|
+
paths: runtimePaths,
|
|
33
33
|
});
|
|
34
34
|
const view = await context.inspectMiningControlPlane({
|
|
35
35
|
provider: context.walletSecretProvider,
|
|
@@ -52,6 +52,7 @@ export async function runMiningReadCommand(parsed, context) {
|
|
|
52
52
|
openedAtUnixMs: null,
|
|
53
53
|
},
|
|
54
54
|
verify: parsed.verify,
|
|
55
|
+
paths: runtimePaths,
|
|
55
56
|
});
|
|
56
57
|
if (parsed.outputMode === "json") {
|
|
57
58
|
const result = buildHooksStatusJson(view);
|
|
@@ -79,7 +80,7 @@ export async function runMiningReadCommand(parsed, context) {
|
|
|
79
80
|
const events = normalized.items.slice().reverse();
|
|
80
81
|
if (events.length === 0) {
|
|
81
82
|
if (parsed.outputMode === "json") {
|
|
82
|
-
const result = buildMineLogJson(events, normalized.page, await readRotationIndices());
|
|
83
|
+
const result = buildMineLogJson(events, normalized.page, await readRotationIndices(runtimePaths));
|
|
83
84
|
writeJsonValue(context.stdout, createSuccessEnvelope("cogcoin/mine-log/v1", describeCanonicalCommand(parsed), result.data, {
|
|
84
85
|
warnings: result.warnings,
|
|
85
86
|
explanations: result.explanations,
|
|
@@ -91,7 +92,7 @@ export async function runMiningReadCommand(parsed, context) {
|
|
|
91
92
|
return 0;
|
|
92
93
|
}
|
|
93
94
|
if (parsed.outputMode === "json") {
|
|
94
|
-
const result = buildMineLogJson(events, normalized.page, await readRotationIndices());
|
|
95
|
+
const result = buildMineLogJson(events, normalized.page, await readRotationIndices(runtimePaths));
|
|
95
96
|
writeJsonValue(context.stdout, createSuccessEnvelope("cogcoin/mine-log/v1", describeCanonicalCommand(parsed), result.data, {
|
|
96
97
|
warnings: result.warnings,
|
|
97
98
|
explanations: result.explanations,
|
|
@@ -131,6 +132,7 @@ export async function runMiningReadCommand(parsed, context) {
|
|
|
131
132
|
dataDir,
|
|
132
133
|
databasePath: dbPath,
|
|
133
134
|
secretProvider: context.walletSecretProvider,
|
|
135
|
+
paths: runtimePaths,
|
|
134
136
|
});
|
|
135
137
|
try {
|
|
136
138
|
const mining = readContext.mining ?? await context.inspectMiningControlPlane({
|
|
@@ -140,6 +142,7 @@ export async function runMiningReadCommand(parsed, context) {
|
|
|
140
142
|
nodeStatus: readContext.nodeStatus,
|
|
141
143
|
nodeHealth: readContext.nodeHealth,
|
|
142
144
|
indexer: readContext.indexer,
|
|
145
|
+
paths: runtimePaths,
|
|
143
146
|
});
|
|
144
147
|
if (parsed.outputMode === "json") {
|
|
145
148
|
const result = buildMineStatusJson(mining);
|
|
@@ -15,6 +15,7 @@ export async function runMiningRuntimeCommand(parsed, context) {
|
|
|
15
15
|
const dbPath = parsed.dbPath ?? context.resolveDefaultClientDatabasePath();
|
|
16
16
|
const dataDir = parsed.dataDir ?? context.resolveDefaultBitcoindDataDir();
|
|
17
17
|
const provider = context.walletSecretProvider;
|
|
18
|
+
const runtimePaths = context.resolveWalletRuntimePaths(parsed.seedName);
|
|
18
19
|
await context.ensureDirectory(dirname(dbPath));
|
|
19
20
|
if (parsed.command === "mine") {
|
|
20
21
|
const abortController = new AbortController();
|
|
@@ -33,6 +34,7 @@ export async function runMiningRuntimeCommand(parsed, context) {
|
|
|
33
34
|
stdout: context.stdout,
|
|
34
35
|
stderr: context.stderr,
|
|
35
36
|
progressOutput: parsed.progressOutput,
|
|
37
|
+
paths: runtimePaths,
|
|
36
38
|
});
|
|
37
39
|
}
|
|
38
40
|
finally {
|
|
@@ -47,6 +49,7 @@ export async function runMiningRuntimeCommand(parsed, context) {
|
|
|
47
49
|
databasePath: dbPath,
|
|
48
50
|
provider,
|
|
49
51
|
prompter: createCommandPrompter(parsed, context),
|
|
52
|
+
paths: runtimePaths,
|
|
50
53
|
});
|
|
51
54
|
if (parsed.outputMode === "preview-json") {
|
|
52
55
|
writeJsonValue(context.stdout, createPreviewSuccessEnvelope(resolvePreviewJsonSchema(parsed), describeCanonicalCommand(parsed), result.started ? "started" : "already-active", buildMineStartPreviewData(result)));
|
|
@@ -74,6 +77,7 @@ export async function runMiningRuntimeCommand(parsed, context) {
|
|
|
74
77
|
dataDir,
|
|
75
78
|
databasePath: dbPath,
|
|
76
79
|
provider,
|
|
80
|
+
paths: runtimePaths,
|
|
77
81
|
});
|
|
78
82
|
const nextSteps = getMineStopNextSteps();
|
|
79
83
|
if (parsed.outputMode === "preview-json") {
|
|
@@ -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,
|
|
@@ -6,11 +6,13 @@ import { createSuccessEnvelope, describeCanonicalCommand, writeJsonValue } from
|
|
|
6
6
|
export async function runStatusCommand(parsed, context) {
|
|
7
7
|
const dbPath = parsed.dbPath ?? context.resolveDefaultClientDatabasePath();
|
|
8
8
|
const dataDir = parsed.dataDir ?? context.resolveDefaultBitcoindDataDir();
|
|
9
|
+
const runtimePaths = context.resolveWalletRuntimePaths(parsed.seedName);
|
|
9
10
|
await context.ensureDirectory(dirname(dbPath));
|
|
10
11
|
const readContext = await context.openWalletReadContext({
|
|
11
12
|
dataDir,
|
|
12
13
|
databasePath: dbPath,
|
|
13
14
|
secretProvider: context.walletSecretProvider,
|
|
15
|
+
paths: runtimePaths,
|
|
14
16
|
});
|
|
15
17
|
try {
|
|
16
18
|
if (parsed.outputMode === "json") {
|
|
@@ -1,11 +1,20 @@
|
|
|
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";
|
|
7
|
+
import { confirmGetblockArchiveRestart } from "./getblock-archive-restart.js";
|
|
6
8
|
export async function runSyncCommand(parsed, context) {
|
|
7
9
|
const dbPath = parsed.dbPath ?? context.resolveDefaultClientDatabasePath();
|
|
8
10
|
const dataDir = parsed.dataDir ?? context.resolveDefaultBitcoindDataDir();
|
|
11
|
+
const walletRoot = await resolveWalletRootIdFromLocalArtifacts({
|
|
12
|
+
paths: context.resolveWalletRuntimePaths(),
|
|
13
|
+
provider: context.walletSecretProvider,
|
|
14
|
+
loadRawWalletStateEnvelope: context.loadRawWalletStateEnvelope,
|
|
15
|
+
loadUnlockSession: context.loadUnlockSession,
|
|
16
|
+
loadWalletExplicitLock: context.loadWalletExplicitLock,
|
|
17
|
+
});
|
|
9
18
|
await context.ensureDirectory(dirname(dbPath));
|
|
10
19
|
const store = await context.openSqliteStore({ filename: dbPath });
|
|
11
20
|
let storeOwned = true;
|
|
@@ -14,7 +23,9 @@ export async function runSyncCommand(parsed, context) {
|
|
|
14
23
|
store,
|
|
15
24
|
databasePath: dbPath,
|
|
16
25
|
dataDir,
|
|
26
|
+
walletRootId: walletRoot.walletRootId,
|
|
17
27
|
progressOutput: parsed.progressOutput,
|
|
28
|
+
confirmGetblockArchiveRestart: async (options) => confirmGetblockArchiveRestart(parsed, context, options),
|
|
18
29
|
});
|
|
19
30
|
storeOwned = false;
|
|
20
31
|
const stopWatcher = createStopSignalWatcher(context.signalSource, context.stderr, client, context.forceExit);
|