@cogcoin/client 0.5.13 → 0.5.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/bitcoind/bootstrap/getblock-archive.d.ts +23 -1
- package/dist/bitcoind/bootstrap/getblock-archive.js +127 -37
- package/dist/bitcoind/bootstrap.d.ts +1 -1
- package/dist/bitcoind/bootstrap.js +1 -1
- package/dist/bitcoind/client/managed-client.js +62 -40
- package/dist/bitcoind/testing.d.ts +1 -1
- package/dist/bitcoind/testing.js +1 -1
- package/dist/cli/commands/status.js +1 -1
- package/dist/cli/commands/sync.js +99 -1
- package/dist/cli/commands/wallet-mutation.js +39 -2
- package/dist/cli/context.js +20 -3
- package/dist/cli/mutation-success.d.ts +2 -0
- package/dist/cli/mutation-success.js +2 -0
- package/dist/cli/mutation-text-write.d.ts +2 -0
- package/dist/cli/mutation-text-write.js +7 -0
- package/dist/cli/output.js +22 -1
- package/dist/cli/types.d.ts +2 -0
- package/dist/cli/wallet-format.d.ts +1 -1
- package/dist/cli/wallet-format.js +2 -2
- package/dist/wallet/coin-control.d.ts +1 -1
- package/dist/wallet/coin-control.js +56 -15
- package/dist/wallet/tx/anchor.d.ts +2 -0
- package/dist/wallet/tx/anchor.js +49 -14
- package/dist/wallet/tx/cog.js +5 -15
- package/dist/wallet/tx/common.d.ts +4 -0
- package/dist/wallet/tx/common.js +67 -2
- package/dist/wallet/tx/domain-admin.js +3 -2
- package/dist/wallet/tx/domain-market.js +5 -15
- package/dist/wallet/tx/field.js +1 -3
- package/dist/wallet/tx/register.js +5 -13
- package/dist/wallet/tx/reputation.js +3 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# `@cogcoin/client`
|
|
2
2
|
|
|
3
|
-
`@cogcoin/client@0.5.
|
|
3
|
+
`@cogcoin/client@0.5.14` is the store-backed Cogcoin client package for applications that want a local wallet, durable SQLite-backed state, and a managed Bitcoin Core integration around `@cogcoin/indexer`. It publishes the reusable client APIs, the SQLite adapter, the managed `bitcoind` integration, and the first-party `cogcoin` CLI in one package.
|
|
4
4
|
|
|
5
5
|
Use Node 22 or newer.
|
|
6
6
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ManagedProgressController } from "../progress.js";
|
|
2
|
-
import type { GetblockArchiveManifest } from "../types.js";
|
|
2
|
+
import type { GetblockArchiveManifest, GetblockRangeManifest } from "../types.js";
|
|
3
3
|
interface GetblockArchivePaths {
|
|
4
4
|
directory: string;
|
|
5
5
|
artifactPath: string;
|
|
@@ -10,8 +10,27 @@ export interface ReadyGetblockArchive {
|
|
|
10
10
|
manifest: GetblockArchiveManifest;
|
|
11
11
|
artifactPath: string;
|
|
12
12
|
}
|
|
13
|
+
export interface RefreshedGetblockManifest {
|
|
14
|
+
manifest: GetblockRangeManifest | null;
|
|
15
|
+
source: "remote" | "cache" | "none";
|
|
16
|
+
}
|
|
17
|
+
export declare function refreshGetblockManifestCache(options: {
|
|
18
|
+
dataDir: string;
|
|
19
|
+
fetchImpl?: typeof fetch;
|
|
20
|
+
signal?: AbortSignal;
|
|
21
|
+
persist?: boolean;
|
|
22
|
+
}): Promise<RefreshedGetblockManifest>;
|
|
23
|
+
export declare function resolveGetblockArchiveRange(manifest: GetblockRangeManifest, firstBlockHeight: number, lastBlockHeight: number): GetblockArchiveManifest | null;
|
|
24
|
+
export declare function resolveGetblockArchiveRangeForHeight(manifest: GetblockRangeManifest, height: number): GetblockArchiveManifest | null;
|
|
13
25
|
export declare function resolveGetblockArchivePathsForTesting(dataDir: string, firstBlockHeight?: number, lastBlockHeight?: number): GetblockArchivePaths;
|
|
14
26
|
export declare function resolveReadyGetblockArchiveForTesting(dataDir: string, manifest: GetblockArchiveManifest): Promise<ReadyGetblockArchive | null>;
|
|
27
|
+
export declare function preparePublishedGetblockArchiveRange(options: {
|
|
28
|
+
dataDir: string;
|
|
29
|
+
progress: Pick<ManagedProgressController, "setPhase">;
|
|
30
|
+
manifest: GetblockArchiveManifest;
|
|
31
|
+
fetchImpl?: typeof fetch;
|
|
32
|
+
signal?: AbortSignal;
|
|
33
|
+
}): Promise<ReadyGetblockArchive>;
|
|
15
34
|
export declare function prepareGetblockArchiveRange(options: {
|
|
16
35
|
dataDir: string;
|
|
17
36
|
progress: Pick<ManagedProgressController, "setPhase">;
|
|
@@ -33,7 +52,10 @@ export declare function prepareLatestGetblockArchive(options: {
|
|
|
33
52
|
}): Promise<ReadyGetblockArchive | null>;
|
|
34
53
|
export declare const prepareLatestGetblockArchiveForTesting: typeof prepareLatestGetblockArchive;
|
|
35
54
|
export declare const prepareGetblockArchiveRangeForTesting: typeof prepareGetblockArchiveRange;
|
|
55
|
+
export declare const preparePublishedGetblockArchiveRangeForTesting: typeof preparePublishedGetblockArchiveRange;
|
|
36
56
|
export declare const deleteGetblockArchiveRangeForTesting: typeof deleteGetblockArchiveRange;
|
|
57
|
+
export declare const refreshGetblockManifestCacheForTesting: typeof refreshGetblockManifestCache;
|
|
58
|
+
export declare const resolveGetblockArchiveRangeForHeightForTesting: typeof resolveGetblockArchiveRangeForHeight;
|
|
37
59
|
export declare function waitForGetblockArchiveImportForTesting(rpc: Pick<{
|
|
38
60
|
getBlockchainInfo(): Promise<{
|
|
39
61
|
blocks: number;
|
|
@@ -6,6 +6,7 @@ const GETBLOCK_ARCHIVE_STATE_VERSION = 1;
|
|
|
6
6
|
const GETBLOCK_ARCHIVE_BASE_HEIGHT = 910_000;
|
|
7
7
|
const GETBLOCK_ARCHIVE_RANGE_SIZE = 500;
|
|
8
8
|
const GETBLOCK_ARCHIVE_FIRST_HEIGHT = GETBLOCK_ARCHIVE_BASE_HEIGHT + 1;
|
|
9
|
+
const GETBLOCK_ARCHIVE_MANIFEST_FILENAME = "getblock-manifest.json";
|
|
9
10
|
const GETBLOCK_ARCHIVE_REMOTE_BASE_URL = "https://snapshots.cogcoin.org/";
|
|
10
11
|
const GETBLOCK_ARCHIVE_REMOTE_MANIFEST_URL = `${GETBLOCK_ARCHIVE_REMOTE_BASE_URL}getblock-manifest.json`;
|
|
11
12
|
const TRUSTED_FRONTIER_REVERIFY_CHUNKS = 2;
|
|
@@ -14,6 +15,9 @@ const IMPORT_POLL_MS = 2_000;
|
|
|
14
15
|
function buildRangeFilename(firstBlockHeight, lastBlockHeight) {
|
|
15
16
|
return `getblock-${firstBlockHeight}-${lastBlockHeight}.dat`;
|
|
16
17
|
}
|
|
18
|
+
function resolveManifestCachePath(dataDir) {
|
|
19
|
+
return join(dataDir, "bootstrap", "getblock", GETBLOCK_ARCHIVE_MANIFEST_FILENAME);
|
|
20
|
+
}
|
|
17
21
|
function createInitialState() {
|
|
18
22
|
return {
|
|
19
23
|
metadataVersion: GETBLOCK_ARCHIVE_STATE_VERSION,
|
|
@@ -117,6 +121,12 @@ async function writeJsonAtomic(path, payload) {
|
|
|
117
121
|
await writeFile(tempPath, JSON.stringify(payload, null, 2));
|
|
118
122
|
await rename(tempPath, path);
|
|
119
123
|
}
|
|
124
|
+
async function writeTextAtomic(path, payload) {
|
|
125
|
+
await mkdir(dirname(path), { recursive: true });
|
|
126
|
+
const tempPath = `${path}.tmp`;
|
|
127
|
+
await writeFile(tempPath, payload);
|
|
128
|
+
await rename(tempPath, path);
|
|
129
|
+
}
|
|
120
130
|
async function loadState(paths) {
|
|
121
131
|
try {
|
|
122
132
|
const parsed = JSON.parse(await readFile(paths.statePath, "utf8"));
|
|
@@ -466,16 +476,74 @@ async function downloadRemoteArchive(paths, manifest, state, progress, fetchImpl
|
|
|
466
476
|
}
|
|
467
477
|
}
|
|
468
478
|
async function fetchManifestRange(fetchImpl, firstBlockHeight, lastBlockHeight, signal) {
|
|
469
|
-
const
|
|
470
|
-
|
|
479
|
+
const refreshed = await refreshGetblockManifestCache({
|
|
480
|
+
dataDir: "",
|
|
481
|
+
fetchImpl,
|
|
482
|
+
signal,
|
|
483
|
+
persist: false,
|
|
484
|
+
});
|
|
485
|
+
if (refreshed.manifest === null) {
|
|
471
486
|
return null;
|
|
472
487
|
}
|
|
488
|
+
return resolveGetblockArchiveRange(refreshed.manifest, firstBlockHeight, lastBlockHeight);
|
|
489
|
+
}
|
|
490
|
+
async function fetchAggregateManifest(fetchImpl, signal) {
|
|
491
|
+
const response = await fetchImpl(GETBLOCK_ARCHIVE_REMOTE_MANIFEST_URL, { signal });
|
|
473
492
|
if (!response.ok) {
|
|
474
493
|
throw new Error(`managed_getblock_archive_manifest_http_${response.status}`);
|
|
475
494
|
}
|
|
476
|
-
const
|
|
495
|
+
const rawText = await response.text();
|
|
496
|
+
return {
|
|
497
|
+
manifest: assertAggregateManifestShape(JSON.parse(rawText)),
|
|
498
|
+
rawText,
|
|
499
|
+
};
|
|
500
|
+
}
|
|
501
|
+
async function loadCachedAggregateManifest(dataDir) {
|
|
502
|
+
try {
|
|
503
|
+
return assertAggregateManifestShape(JSON.parse(await readFile(resolveManifestCachePath(dataDir), "utf8")));
|
|
504
|
+
}
|
|
505
|
+
catch (error) {
|
|
506
|
+
if (error instanceof Error && "code" in error && error.code === "ENOENT") {
|
|
507
|
+
return null;
|
|
508
|
+
}
|
|
509
|
+
return null;
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
async function saveCachedAggregateManifest(dataDir, rawText) {
|
|
513
|
+
await writeTextAtomic(resolveManifestCachePath(dataDir), rawText);
|
|
514
|
+
}
|
|
515
|
+
export async function refreshGetblockManifestCache(options) {
|
|
516
|
+
const fetchImpl = options.fetchImpl ?? fetch;
|
|
517
|
+
try {
|
|
518
|
+
const { manifest, rawText } = await fetchAggregateManifest(fetchImpl, options.signal);
|
|
519
|
+
if (options.persist !== false) {
|
|
520
|
+
await saveCachedAggregateManifest(options.dataDir, rawText).catch(() => undefined);
|
|
521
|
+
}
|
|
522
|
+
return {
|
|
523
|
+
manifest,
|
|
524
|
+
source: "remote",
|
|
525
|
+
};
|
|
526
|
+
}
|
|
527
|
+
catch {
|
|
528
|
+
if (options.persist === false) {
|
|
529
|
+
return {
|
|
530
|
+
manifest: null,
|
|
531
|
+
source: "none",
|
|
532
|
+
};
|
|
533
|
+
}
|
|
534
|
+
const cachedManifest = await loadCachedAggregateManifest(options.dataDir);
|
|
535
|
+
return {
|
|
536
|
+
manifest: cachedManifest,
|
|
537
|
+
source: cachedManifest === null ? "none" : "cache",
|
|
538
|
+
};
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
export function resolveGetblockArchiveRange(manifest, firstBlockHeight, lastBlockHeight) {
|
|
477
542
|
return manifest.ranges.find((range) => range.firstBlockHeight === firstBlockHeight && range.lastBlockHeight === lastBlockHeight) ?? null;
|
|
478
543
|
}
|
|
544
|
+
export function resolveGetblockArchiveRangeForHeight(manifest, height) {
|
|
545
|
+
return manifest.ranges.find((range) => range.firstBlockHeight <= height && height <= range.lastBlockHeight) ?? null;
|
|
546
|
+
}
|
|
479
547
|
export function resolveGetblockArchivePathsForTesting(dataDir, firstBlockHeight = GETBLOCK_ARCHIVE_FIRST_HEIGHT, lastBlockHeight = GETBLOCK_ARCHIVE_BASE_HEIGHT + GETBLOCK_ARCHIVE_RANGE_SIZE) {
|
|
480
548
|
return resolvePaths(dataDir, firstBlockHeight, lastBlockHeight);
|
|
481
549
|
}
|
|
@@ -512,49 +580,68 @@ async function resolveReadyLocalGetblockArchive(paths, manifest, state = null) {
|
|
|
512
580
|
export async function resolveReadyGetblockArchiveForTesting(dataDir, manifest) {
|
|
513
581
|
return resolveReadyLocalGetblockArchive(resolvePaths(dataDir, manifest.firstBlockHeight, manifest.lastBlockHeight), manifest);
|
|
514
582
|
}
|
|
583
|
+
export async function preparePublishedGetblockArchiveRange(options) {
|
|
584
|
+
const paths = resolvePaths(options.dataDir, options.manifest.firstBlockHeight, options.manifest.lastBlockHeight);
|
|
585
|
+
await mkdir(paths.directory, { recursive: true });
|
|
586
|
+
const state = await loadState(paths);
|
|
587
|
+
const readyLocal = await resolveReadyLocalGetblockArchive(paths, options.manifest, state);
|
|
588
|
+
if (readyLocal !== null) {
|
|
589
|
+
return readyLocal;
|
|
590
|
+
}
|
|
591
|
+
await downloadRemoteArchive(paths, options.manifest, state, options.progress, options.fetchImpl ?? fetch, options.signal);
|
|
592
|
+
const ready = await resolveReadyLocalGetblockArchive(paths, options.manifest, state);
|
|
593
|
+
if (ready === null) {
|
|
594
|
+
throw new Error("managed_getblock_archive_ready_resolution_failed");
|
|
595
|
+
}
|
|
596
|
+
return ready;
|
|
597
|
+
}
|
|
515
598
|
export async function prepareGetblockArchiveRange(options) {
|
|
516
599
|
const paths = resolvePaths(options.dataDir, options.firstBlockHeight, options.lastBlockHeight);
|
|
517
600
|
await mkdir(paths.directory, { recursive: true });
|
|
518
601
|
const state = await loadState(paths);
|
|
519
602
|
const fetchImpl = options.fetchImpl ?? fetch;
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
603
|
+
const refreshed = await refreshGetblockManifestCache({
|
|
604
|
+
dataDir: options.dataDir,
|
|
605
|
+
fetchImpl,
|
|
606
|
+
signal: options.signal,
|
|
607
|
+
});
|
|
608
|
+
const publishedRange = refreshed.manifest === null
|
|
609
|
+
? null
|
|
610
|
+
: resolveGetblockArchiveRange(refreshed.manifest, options.firstBlockHeight, options.lastBlockHeight);
|
|
611
|
+
if (publishedRange !== null) {
|
|
612
|
+
return preparePublishedGetblockArchiveRange({
|
|
613
|
+
dataDir: options.dataDir,
|
|
614
|
+
progress: options.progress,
|
|
615
|
+
manifest: publishedRange,
|
|
616
|
+
fetchImpl,
|
|
617
|
+
signal: options.signal,
|
|
618
|
+
});
|
|
523
619
|
}
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
return resolved;
|
|
544
|
-
}
|
|
620
|
+
const readyLocal = {
|
|
621
|
+
formatVersion: state.formatVersion,
|
|
622
|
+
chain: "main",
|
|
623
|
+
baseSnapshotHeight: GETBLOCK_ARCHIVE_BASE_HEIGHT,
|
|
624
|
+
firstBlockHeight: state.firstBlockHeight ?? options.firstBlockHeight,
|
|
625
|
+
lastBlockHeight: state.lastBlockHeight ?? options.lastBlockHeight,
|
|
626
|
+
artifactFilename: buildRangeFilename(state.firstBlockHeight ?? options.firstBlockHeight, state.lastBlockHeight ?? options.lastBlockHeight),
|
|
627
|
+
artifactSizeBytes: state.artifactSizeBytes,
|
|
628
|
+
artifactSha256: state.artifactSha256 ?? "",
|
|
629
|
+
chunkSizeBytes: state.chunkSizeBytes,
|
|
630
|
+
chunkSha256s: [],
|
|
631
|
+
};
|
|
632
|
+
if (state.validated
|
|
633
|
+
&& state.firstBlockHeight === options.firstBlockHeight
|
|
634
|
+
&& state.lastBlockHeight === options.lastBlockHeight
|
|
635
|
+
&& state.artifactSha256 !== null) {
|
|
636
|
+
const resolved = await resolveReadyLocalGetblockArchive(paths, readyLocal, state).catch(() => null);
|
|
637
|
+
if (resolved !== null) {
|
|
638
|
+
return resolved;
|
|
545
639
|
}
|
|
546
|
-
throw new Error("managed_getblock_archive_manifest_refresh_failed");
|
|
547
|
-
}
|
|
548
|
-
if (remoteManifest === null) {
|
|
549
|
-
return null;
|
|
550
640
|
}
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
&& readyLocal.manifest.artifactSha256 === remoteManifest.artifactSha256) {
|
|
554
|
-
return readyLocal;
|
|
641
|
+
if (refreshed.source === "none") {
|
|
642
|
+
throw new Error("managed_getblock_archive_manifest_refresh_failed");
|
|
555
643
|
}
|
|
556
|
-
|
|
557
|
-
return resolveReadyLocalGetblockArchive(paths, remoteManifest, state);
|
|
644
|
+
return null;
|
|
558
645
|
}
|
|
559
646
|
export async function deleteGetblockArchiveRange(options) {
|
|
560
647
|
const paths = resolvePaths(options.dataDir, options.firstBlockHeight, options.lastBlockHeight);
|
|
@@ -571,7 +658,10 @@ export async function prepareLatestGetblockArchive(options) {
|
|
|
571
658
|
}
|
|
572
659
|
export const prepareLatestGetblockArchiveForTesting = prepareLatestGetblockArchive;
|
|
573
660
|
export const prepareGetblockArchiveRangeForTesting = prepareGetblockArchiveRange;
|
|
661
|
+
export const preparePublishedGetblockArchiveRangeForTesting = preparePublishedGetblockArchiveRange;
|
|
574
662
|
export const deleteGetblockArchiveRangeForTesting = deleteGetblockArchiveRange;
|
|
663
|
+
export const refreshGetblockManifestCacheForTesting = refreshGetblockManifestCache;
|
|
664
|
+
export const resolveGetblockArchiveRangeForHeightForTesting = resolveGetblockArchiveRangeForHeight;
|
|
575
665
|
export async function waitForGetblockArchiveImportForTesting(rpc, progress, targetEndHeight, signal) {
|
|
576
666
|
await waitForGetblockArchiveImport(rpc, progress, targetEndHeight, signal);
|
|
577
667
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { DEFAULT_SNAPSHOT_METADATA } from "./bootstrap/constants.js";
|
|
2
2
|
export { AssumeUtxoBootstrapController } from "./bootstrap/controller.js";
|
|
3
3
|
export { downloadSnapshotFileForTesting } from "./bootstrap/download.js";
|
|
4
|
-
export { deleteGetblockArchiveRange, deleteGetblockArchiveRangeForTesting, prepareGetblockArchiveRange, prepareGetblockArchiveRangeForTesting, prepareLatestGetblockArchive, prepareLatestGetblockArchiveForTesting, resolveGetblockArchivePathsForTesting, resolveReadyGetblockArchiveForTesting, waitForGetblockArchiveImport, waitForGetblockArchiveImportForTesting, } from "./bootstrap/getblock-archive.js";
|
|
4
|
+
export { deleteGetblockArchiveRange, deleteGetblockArchiveRangeForTesting, preparePublishedGetblockArchiveRange, preparePublishedGetblockArchiveRangeForTesting, prepareGetblockArchiveRange, prepareGetblockArchiveRangeForTesting, prepareLatestGetblockArchive, prepareLatestGetblockArchiveForTesting, refreshGetblockManifestCache, refreshGetblockManifestCacheForTesting, resolveGetblockArchiveRange, resolveGetblockArchiveRangeForHeight, resolveGetblockArchiveRangeForHeightForTesting, resolveGetblockArchivePathsForTesting, resolveReadyGetblockArchiveForTesting, waitForGetblockArchiveImport, waitForGetblockArchiveImportForTesting, } from "./bootstrap/getblock-archive.js";
|
|
5
5
|
export { waitForHeadersForTesting } from "./bootstrap/headers.js";
|
|
6
6
|
export { resolveBootstrapPathsForTesting } from "./bootstrap/paths.js";
|
|
7
7
|
export { loadBootstrapStateForTesting, saveBootstrapStateForTesting, createBootstrapStateForTesting, } from "./bootstrap/state.js";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { DEFAULT_SNAPSHOT_METADATA } from "./bootstrap/constants.js";
|
|
2
2
|
export { AssumeUtxoBootstrapController } from "./bootstrap/controller.js";
|
|
3
3
|
export { downloadSnapshotFileForTesting } from "./bootstrap/download.js";
|
|
4
|
-
export { deleteGetblockArchiveRange, deleteGetblockArchiveRangeForTesting, prepareGetblockArchiveRange, prepareGetblockArchiveRangeForTesting, prepareLatestGetblockArchive, prepareLatestGetblockArchiveForTesting, resolveGetblockArchivePathsForTesting, resolveReadyGetblockArchiveForTesting, waitForGetblockArchiveImport, waitForGetblockArchiveImportForTesting, } from "./bootstrap/getblock-archive.js";
|
|
4
|
+
export { deleteGetblockArchiveRange, deleteGetblockArchiveRangeForTesting, preparePublishedGetblockArchiveRange, preparePublishedGetblockArchiveRangeForTesting, prepareGetblockArchiveRange, prepareGetblockArchiveRangeForTesting, prepareLatestGetblockArchive, prepareLatestGetblockArchiveForTesting, refreshGetblockManifestCache, refreshGetblockManifestCacheForTesting, resolveGetblockArchiveRange, resolveGetblockArchiveRangeForHeight, resolveGetblockArchiveRangeForHeightForTesting, resolveGetblockArchivePathsForTesting, resolveReadyGetblockArchiveForTesting, waitForGetblockArchiveImport, waitForGetblockArchiveImportForTesting, } from "./bootstrap/getblock-archive.js";
|
|
5
5
|
export { waitForHeadersForTesting } from "./bootstrap/headers.js";
|
|
6
6
|
export { resolveBootstrapPathsForTesting } from "./bootstrap/paths.js";
|
|
7
7
|
export { loadBootstrapStateForTesting, saveBootstrapStateForTesting, createBootstrapStateForTesting, } from "./bootstrap/state.js";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AssumeUtxoBootstrapController, deleteGetblockArchiveRange,
|
|
1
|
+
import { AssumeUtxoBootstrapController, deleteGetblockArchiveRange, preparePublishedGetblockArchiveRange, refreshGetblockManifestCache, resolveGetblockArchiveRangeForHeight, } from "../bootstrap.js";
|
|
2
2
|
import { createRpcClient } from "../node.js";
|
|
3
3
|
import { attachOrStartManagedBitcoindService, stopManagedBitcoindService, } from "../service.js";
|
|
4
4
|
import { closeFollowLoopResources, scheduleSync, startFollowingTipLoop } from "./follow-loop.js";
|
|
@@ -6,21 +6,6 @@ import { loadVisibleFollowBlockTimes } from "./follow-block-times.js";
|
|
|
6
6
|
import { createBlockRateTracker, createInitialSyncResult, } from "./internal-types.js";
|
|
7
7
|
import { syncToTip as runManagedSync } from "./sync-engine.js";
|
|
8
8
|
const GETBLOCK_RANGE_BASE_HEIGHT = 910_000;
|
|
9
|
-
const GETBLOCK_RANGE_SIZE = 500;
|
|
10
|
-
function isBoundaryHeight(height) {
|
|
11
|
-
return height >= GETBLOCK_RANGE_BASE_HEIGHT
|
|
12
|
-
&& (height - GETBLOCK_RANGE_BASE_HEIGHT) % GETBLOCK_RANGE_SIZE === 0;
|
|
13
|
-
}
|
|
14
|
-
function resolveNextBoundary(height) {
|
|
15
|
-
if (height < GETBLOCK_RANGE_BASE_HEIGHT) {
|
|
16
|
-
return GETBLOCK_RANGE_BASE_HEIGHT;
|
|
17
|
-
}
|
|
18
|
-
if (isBoundaryHeight(height)) {
|
|
19
|
-
return height;
|
|
20
|
-
}
|
|
21
|
-
return GETBLOCK_RANGE_BASE_HEIGHT
|
|
22
|
-
+ Math.ceil((height - GETBLOCK_RANGE_BASE_HEIGHT) / GETBLOCK_RANGE_SIZE) * GETBLOCK_RANGE_SIZE;
|
|
23
|
-
}
|
|
24
9
|
function mergeSyncResults(target, source) {
|
|
25
10
|
target.appliedBlocks += source.appliedBlocks;
|
|
26
11
|
target.rewoundBlocks += source.rewoundBlocks;
|
|
@@ -218,56 +203,93 @@ export class DefaultManagedBitcoindClient {
|
|
|
218
203
|
this.#assertOpen();
|
|
219
204
|
await this.#progress.playCompletionScene();
|
|
220
205
|
}
|
|
206
|
+
async #setGetblockStatusMessage(currentHeight, message, targetHeight = currentHeight) {
|
|
207
|
+
const safeTargetHeight = Math.max(currentHeight, targetHeight);
|
|
208
|
+
await this.#progress.setPhase("bitcoin_sync", {
|
|
209
|
+
blocks: currentHeight,
|
|
210
|
+
headers: safeTargetHeight,
|
|
211
|
+
targetHeight: safeTargetHeight,
|
|
212
|
+
etaSeconds: null,
|
|
213
|
+
lastError: null,
|
|
214
|
+
message,
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
async #refreshGetblockManifest(currentHeight, abortSignal, mode) {
|
|
218
|
+
await this.#setGetblockStatusMessage(currentHeight, mode === "startup"
|
|
219
|
+
? "Fetching Getblock manifest."
|
|
220
|
+
: "Refreshing Getblock manifest.");
|
|
221
|
+
const refreshed = await refreshGetblockManifestCache({
|
|
222
|
+
dataDir: this.#dataDir,
|
|
223
|
+
fetchImpl: this.#fetchImpl,
|
|
224
|
+
signal: abortSignal,
|
|
225
|
+
});
|
|
226
|
+
if (refreshed.source === "remote" && refreshed.manifest !== null) {
|
|
227
|
+
await this.#setGetblockStatusMessage(currentHeight, `Getblock manifest ready through height ${refreshed.manifest.publishedThroughHeight.toLocaleString()}.`, refreshed.manifest.publishedThroughHeight);
|
|
228
|
+
return refreshed.manifest;
|
|
229
|
+
}
|
|
230
|
+
if (refreshed.source === "cache" && refreshed.manifest !== null) {
|
|
231
|
+
await this.#setGetblockStatusMessage(currentHeight, `Warning: Getblock manifest fetch failed; using cached manifest through height ${refreshed.manifest.publishedThroughHeight.toLocaleString()}.`, refreshed.manifest.publishedThroughHeight);
|
|
232
|
+
return refreshed.manifest;
|
|
233
|
+
}
|
|
234
|
+
await this.#setGetblockStatusMessage(currentHeight, "Warning: Getblock manifest fetch failed and no cached manifest is available; continuing with ordinary Bitcoin sync.");
|
|
235
|
+
return null;
|
|
236
|
+
}
|
|
221
237
|
async #syncWithStagedRanges(abortSignal) {
|
|
222
238
|
const aggregate = createInitialSyncResult();
|
|
223
|
-
let stagedModeEnabled = true;
|
|
224
239
|
await this.#ensureBootstrapReady(abortSignal);
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
240
|
+
let info = await this.#rpc.getBlockchainInfo();
|
|
241
|
+
if (info.blocks < GETBLOCK_RANGE_BASE_HEIGHT) {
|
|
242
|
+
mergeSyncResults(aggregate, await this.#runManagedSyncPass(null, abortSignal));
|
|
243
|
+
return aggregate;
|
|
244
|
+
}
|
|
245
|
+
let manifest = await this.#refreshGetblockManifest(info.blocks, abortSignal, "startup");
|
|
246
|
+
while (manifest !== null) {
|
|
247
|
+
const nextMissingHeight = info.blocks + 1;
|
|
248
|
+
if (nextMissingHeight > manifest.publishedThroughHeight) {
|
|
249
|
+
const refreshed = await this.#refreshGetblockManifest(info.blocks, abortSignal, "refresh");
|
|
250
|
+
if (refreshed === null || refreshed.publishedThroughHeight < nextMissingHeight) {
|
|
251
|
+
manifest = refreshed;
|
|
252
|
+
break;
|
|
253
|
+
}
|
|
254
|
+
manifest = refreshed;
|
|
229
255
|
}
|
|
230
|
-
|
|
231
|
-
if (nextBoundary === null) {
|
|
256
|
+
if (manifest === null) {
|
|
232
257
|
break;
|
|
233
258
|
}
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
259
|
+
const selectedRange = resolveGetblockArchiveRangeForHeight(manifest, nextMissingHeight);
|
|
260
|
+
if (selectedRange === null) {
|
|
261
|
+
await this.#setGetblockStatusMessage(info.blocks, `Warning: Getblock manifest has no published range for next missing block ${nextMissingHeight.toLocaleString()}; continuing with ordinary Bitcoin sync.`);
|
|
262
|
+
break;
|
|
237
263
|
}
|
|
238
|
-
|
|
239
|
-
const lastBlockHeight = info.blocks + GETBLOCK_RANGE_SIZE;
|
|
264
|
+
await this.#setGetblockStatusMessage(info.blocks, `Using Getblock range ${selectedRange.firstBlockHeight.toLocaleString()}-${selectedRange.lastBlockHeight.toLocaleString()} for current Bitcoin height ${info.blocks.toLocaleString()} (next missing block ${nextMissingHeight.toLocaleString()}).`, selectedRange.lastBlockHeight);
|
|
240
265
|
let readyRange;
|
|
241
266
|
try {
|
|
242
|
-
readyRange = await
|
|
267
|
+
readyRange = await preparePublishedGetblockArchiveRange({
|
|
243
268
|
dataDir: this.#dataDir,
|
|
244
269
|
progress: this.#progress,
|
|
245
|
-
|
|
246
|
-
lastBlockHeight,
|
|
270
|
+
manifest: selectedRange,
|
|
247
271
|
fetchImpl: this.#fetchImpl,
|
|
248
272
|
signal: abortSignal,
|
|
249
273
|
});
|
|
250
274
|
}
|
|
251
275
|
catch {
|
|
252
|
-
|
|
253
|
-
break;
|
|
254
|
-
}
|
|
255
|
-
if (readyRange === null) {
|
|
256
|
-
stagedModeEnabled = false;
|
|
276
|
+
await this.#setGetblockStatusMessage(info.blocks, "Warning: Getblock range staging failed; continuing with ordinary Bitcoin sync.");
|
|
257
277
|
break;
|
|
258
278
|
}
|
|
259
279
|
const stagedRestartActive = await this.#restartManagedNodeWithRange(readyRange, abortSignal);
|
|
260
|
-
mergeSyncResults(aggregate, await this.#runManagedSyncPass(lastBlockHeight, abortSignal));
|
|
280
|
+
mergeSyncResults(aggregate, await this.#runManagedSyncPass(selectedRange.lastBlockHeight, abortSignal));
|
|
261
281
|
if (stagedRestartActive) {
|
|
262
282
|
await deleteGetblockArchiveRange({
|
|
263
283
|
dataDir: this.#dataDir,
|
|
264
|
-
firstBlockHeight,
|
|
265
|
-
lastBlockHeight,
|
|
284
|
+
firstBlockHeight: selectedRange.firstBlockHeight,
|
|
285
|
+
lastBlockHeight: selectedRange.lastBlockHeight,
|
|
266
286
|
}).catch(() => undefined);
|
|
267
287
|
}
|
|
268
288
|
else {
|
|
269
|
-
|
|
289
|
+
await this.#setGetblockStatusMessage(selectedRange.lastBlockHeight, "Warning: Restarting with the Getblock archive failed; continuing with ordinary Bitcoin sync.");
|
|
290
|
+
break;
|
|
270
291
|
}
|
|
292
|
+
info = await this.#rpc.getBlockchainInfo();
|
|
271
293
|
}
|
|
272
294
|
mergeSyncResults(aggregate, await this.#runManagedSyncPass(null, abortSignal));
|
|
273
295
|
return aggregate;
|
|
@@ -5,7 +5,7 @@ export { attachOrStartIndexerDaemon, readIndexerDaemonStatusForTesting, stopInde
|
|
|
5
5
|
export { normalizeRpcBlock } from "./normalize.js";
|
|
6
6
|
export { BitcoinRpcClient } from "./rpc.js";
|
|
7
7
|
export { attachOrStartManagedBitcoindService, buildManagedServiceArgsForTesting, readManagedBitcoindServiceStatusForTesting, resolveManagedBitcoindDbcacheMiB, stopManagedBitcoindService, shutdownManagedBitcoindServiceForTesting, writeBitcoinConfForTesting, } from "./service.js";
|
|
8
|
-
export { AssumeUtxoBootstrapController, DEFAULT_SNAPSHOT_METADATA, createBootstrapStateForTesting, deleteGetblockArchiveRangeForTesting, downloadSnapshotFileForTesting, loadBootstrapStateForTesting, prepareGetblockArchiveRangeForTesting, prepareLatestGetblockArchiveForTesting, resolveBootstrapPathsForTesting, resolveGetblockArchivePathsForTesting, resolveReadyGetblockArchiveForTesting, saveBootstrapStateForTesting, validateSnapshotFileForTesting, waitForGetblockArchiveImportForTesting, waitForHeadersForTesting, } from "./bootstrap.js";
|
|
8
|
+
export { AssumeUtxoBootstrapController, DEFAULT_SNAPSHOT_METADATA, createBootstrapStateForTesting, deleteGetblockArchiveRangeForTesting, downloadSnapshotFileForTesting, loadBootstrapStateForTesting, preparePublishedGetblockArchiveRangeForTesting, prepareGetblockArchiveRangeForTesting, prepareLatestGetblockArchiveForTesting, refreshGetblockManifestCacheForTesting, resolveBootstrapPathsForTesting, resolveGetblockArchivePathsForTesting, resolveGetblockArchiveRangeForHeightForTesting, resolveReadyGetblockArchiveForTesting, saveBootstrapStateForTesting, validateSnapshotFileForTesting, waitForGetblockArchiveImportForTesting, waitForHeadersForTesting, } from "./bootstrap.js";
|
|
9
9
|
export { buildBitcoindArgsForTesting, createRpcClient, launchManagedBitcoindNode, resolveDefaultBitcoindDataDirForTesting, validateNodeConfigForTesting, } from "./node.js";
|
|
10
10
|
export { ManagedProgressController, TtyProgressRenderer, advanceFollowSceneStateForTesting, createFollowSceneStateForTesting, createBootstrapProgressForTesting, formatCompactFollowAgeLabelForTesting, loadBannerArtForTesting, loadScrollArtForTesting, loadTrainCarArtForTesting, loadTrainArtForTesting, loadTrainSmokeArtForTesting, formatProgressLineForTesting, formatQuoteLineForTesting, renderArtFrameForTesting, renderCompletionFrameForTesting, renderFollowFrameForTesting, renderIntroFrameForTesting, resolveCompletionMessageForTesting, resolveIntroMessageForTesting, resolveStatusFieldTextForTesting, setFollowBlockTimeForTesting, setFollowBlockTimesForTesting, syncFollowSceneStateForTesting, } from "./progress.js";
|
|
11
11
|
export { WritingQuoteRotator, loadWritingQuotesForTesting, shuffleIndicesForTesting, } from "./quotes.js";
|
package/dist/bitcoind/testing.js
CHANGED
|
@@ -5,7 +5,7 @@ export { attachOrStartIndexerDaemon, readIndexerDaemonStatusForTesting, stopInde
|
|
|
5
5
|
export { normalizeRpcBlock } from "./normalize.js";
|
|
6
6
|
export { BitcoinRpcClient } from "./rpc.js";
|
|
7
7
|
export { attachOrStartManagedBitcoindService, buildManagedServiceArgsForTesting, readManagedBitcoindServiceStatusForTesting, resolveManagedBitcoindDbcacheMiB, stopManagedBitcoindService, shutdownManagedBitcoindServiceForTesting, writeBitcoinConfForTesting, } from "./service.js";
|
|
8
|
-
export { AssumeUtxoBootstrapController, DEFAULT_SNAPSHOT_METADATA, createBootstrapStateForTesting, deleteGetblockArchiveRangeForTesting, downloadSnapshotFileForTesting, loadBootstrapStateForTesting, prepareGetblockArchiveRangeForTesting, prepareLatestGetblockArchiveForTesting, resolveBootstrapPathsForTesting, resolveGetblockArchivePathsForTesting, resolveReadyGetblockArchiveForTesting, saveBootstrapStateForTesting, validateSnapshotFileForTesting, waitForGetblockArchiveImportForTesting, waitForHeadersForTesting, } from "./bootstrap.js";
|
|
8
|
+
export { AssumeUtxoBootstrapController, DEFAULT_SNAPSHOT_METADATA, createBootstrapStateForTesting, deleteGetblockArchiveRangeForTesting, downloadSnapshotFileForTesting, loadBootstrapStateForTesting, preparePublishedGetblockArchiveRangeForTesting, prepareGetblockArchiveRangeForTesting, prepareLatestGetblockArchiveForTesting, refreshGetblockManifestCacheForTesting, resolveBootstrapPathsForTesting, resolveGetblockArchivePathsForTesting, resolveGetblockArchiveRangeForHeightForTesting, resolveReadyGetblockArchiveForTesting, saveBootstrapStateForTesting, validateSnapshotFileForTesting, waitForGetblockArchiveImportForTesting, waitForHeadersForTesting, } from "./bootstrap.js";
|
|
9
9
|
export { buildBitcoindArgsForTesting, createRpcClient, launchManagedBitcoindNode, resolveDefaultBitcoindDataDirForTesting, validateNodeConfigForTesting, } from "./node.js";
|
|
10
10
|
export { ManagedProgressController, TtyProgressRenderer, advanceFollowSceneStateForTesting, createFollowSceneStateForTesting, createBootstrapProgressForTesting, formatCompactFollowAgeLabelForTesting, loadBannerArtForTesting, loadScrollArtForTesting, loadTrainCarArtForTesting, loadTrainArtForTesting, loadTrainSmokeArtForTesting, formatProgressLineForTesting, formatQuoteLineForTesting, renderArtFrameForTesting, renderCompletionFrameForTesting, renderFollowFrameForTesting, renderIntroFrameForTesting, resolveCompletionMessageForTesting, resolveIntroMessageForTesting, resolveStatusFieldTextForTesting, setFollowBlockTimeForTesting, setFollowBlockTimesForTesting, syncFollowSceneStateForTesting, } from "./progress.js";
|
|
11
11
|
export { WritingQuoteRotator, loadWritingQuotesForTesting, shuffleIndicesForTesting, } from "./quotes.js";
|
|
@@ -24,7 +24,7 @@ export async function runStatusCommand(parsed, context) {
|
|
|
24
24
|
}));
|
|
25
25
|
return 0;
|
|
26
26
|
}
|
|
27
|
-
writeLine(context.stdout, formatWalletOverviewReport(readContext));
|
|
27
|
+
writeLine(context.stdout, formatWalletOverviewReport(readContext, await context.readPackageVersion()));
|
|
28
28
|
return 0;
|
|
29
29
|
}
|
|
30
30
|
finally {
|
|
@@ -1,14 +1,106 @@
|
|
|
1
1
|
import { dirname } from "node:path";
|
|
2
2
|
import { formatManagedSyncErrorMessage } from "../../bitcoind/errors.js";
|
|
3
|
+
import { formatBytes, formatDuration } from "../../bitcoind/progress/formatting.js";
|
|
3
4
|
import { FileLockBusyError, acquireFileLock } from "../../wallet/fs/lock.js";
|
|
4
5
|
import { resolveWalletRootIdFromLocalArtifacts } from "../../wallet/root-resolution.js";
|
|
5
|
-
import { writeLine } from "../io.js";
|
|
6
|
+
import { usesTtyProgress, writeLine } from "../io.js";
|
|
6
7
|
import { classifyCliError, formatCliTextError } from "../output.js";
|
|
7
8
|
import { createStopSignalWatcher, waitForCompletionOrStop } from "../signals.js";
|
|
9
|
+
const SYNC_PROGRESS_LOG_INTERVAL_MS = 5_000;
|
|
10
|
+
function createSyncProgressReporter(options) {
|
|
11
|
+
let lastPhase = null;
|
|
12
|
+
let lastMessage = "";
|
|
13
|
+
let lastDownloadPrintedAt = 0;
|
|
14
|
+
let lastDownloadBytes = null;
|
|
15
|
+
let lastImportPrintedAt = 0;
|
|
16
|
+
let lastImportBlocks = null;
|
|
17
|
+
const infoEnabled = options.progressOutput !== "none";
|
|
18
|
+
function shouldPrintEntryMessage(message, phase) {
|
|
19
|
+
if (message === "Waiting to start managed sync." || message === "Sync complete.") {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
if (message.startsWith("Warning:")) {
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
if (!infoEnabled) {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
if (phase === "getblock_archive_download" || phase === "getblock_archive_import") {
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
return phase === "snapshot_download"
|
|
32
|
+
|| phase === "wait_headers_for_snapshot"
|
|
33
|
+
|| phase === "load_snapshot"
|
|
34
|
+
|| phase === "bitcoin_sync"
|
|
35
|
+
|| phase === "cogcoin_sync"
|
|
36
|
+
|| message.includes("Getblock manifest")
|
|
37
|
+
|| message.startsWith("Fetching Getblock manifest.")
|
|
38
|
+
|| message.startsWith("Refreshing Getblock manifest.")
|
|
39
|
+
|| message.startsWith("Using Getblock range ");
|
|
40
|
+
}
|
|
41
|
+
function formatDownloadLine(label, event) {
|
|
42
|
+
const current = event.progress.downloadedBytes ?? 0;
|
|
43
|
+
const total = event.progress.totalBytes ?? 0;
|
|
44
|
+
const percent = event.progress.percent ?? (total > 0 ? (current / total) * 100 : 0);
|
|
45
|
+
const speed = event.progress.bytesPerSecond === null ? "--" : `${formatBytes(event.progress.bytesPerSecond)}/s`;
|
|
46
|
+
return `${label}: ${percent.toFixed(2)}% (${formatBytes(current)} / ${formatBytes(total)}, ${speed}, ETA ${formatDuration(event.progress.etaSeconds)})`;
|
|
47
|
+
}
|
|
48
|
+
return (event) => {
|
|
49
|
+
const message = event.progress.message.trim();
|
|
50
|
+
const phaseChanged = event.phase !== lastPhase;
|
|
51
|
+
const messageChanged = message !== lastMessage;
|
|
52
|
+
if ((phaseChanged || messageChanged) && shouldPrintEntryMessage(message, event.phase)) {
|
|
53
|
+
options.write(message);
|
|
54
|
+
}
|
|
55
|
+
if (infoEnabled && event.phase === "getblock_archive_download") {
|
|
56
|
+
const now = Date.now();
|
|
57
|
+
const currentBytes = event.progress.downloadedBytes ?? 0;
|
|
58
|
+
const isComplete = (event.progress.percent ?? 0) >= 100;
|
|
59
|
+
const shouldPrintMilestone = phaseChanged
|
|
60
|
+
|| lastDownloadBytes !== currentBytes && (isComplete
|
|
61
|
+
|| now - lastDownloadPrintedAt >= SYNC_PROGRESS_LOG_INTERVAL_MS);
|
|
62
|
+
if (shouldPrintMilestone) {
|
|
63
|
+
options.write(formatDownloadLine("Getblock download", event));
|
|
64
|
+
lastDownloadPrintedAt = now;
|
|
65
|
+
lastDownloadBytes = currentBytes;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
else if (infoEnabled && event.phase === "snapshot_download") {
|
|
69
|
+
const now = Date.now();
|
|
70
|
+
const currentBytes = event.progress.downloadedBytes ?? 0;
|
|
71
|
+
const isComplete = (event.progress.percent ?? 0) >= 100;
|
|
72
|
+
const shouldPrintMilestone = phaseChanged
|
|
73
|
+
|| lastDownloadBytes !== currentBytes && (isComplete
|
|
74
|
+
|| now - lastDownloadPrintedAt >= SYNC_PROGRESS_LOG_INTERVAL_MS);
|
|
75
|
+
if (shouldPrintMilestone) {
|
|
76
|
+
options.write(formatDownloadLine("Snapshot download", event));
|
|
77
|
+
lastDownloadPrintedAt = now;
|
|
78
|
+
lastDownloadBytes = currentBytes;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
else if (infoEnabled && event.phase === "getblock_archive_import") {
|
|
82
|
+
const now = Date.now();
|
|
83
|
+
const currentBlocks = event.progress.blocks ?? 0;
|
|
84
|
+
const targetBlocks = event.progress.targetHeight ?? currentBlocks;
|
|
85
|
+
const isComplete = currentBlocks >= targetBlocks;
|
|
86
|
+
const shouldPrintMilestone = phaseChanged
|
|
87
|
+
|| lastImportBlocks !== currentBlocks && (isComplete
|
|
88
|
+
|| now - lastImportPrintedAt >= SYNC_PROGRESS_LOG_INTERVAL_MS);
|
|
89
|
+
if (shouldPrintMilestone) {
|
|
90
|
+
options.write(`Getblock import: Bitcoin ${currentBlocks.toLocaleString()} / ${targetBlocks.toLocaleString()}`);
|
|
91
|
+
lastImportPrintedAt = now;
|
|
92
|
+
lastImportBlocks = currentBlocks;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
lastPhase = event.phase;
|
|
96
|
+
lastMessage = message;
|
|
97
|
+
};
|
|
98
|
+
}
|
|
8
99
|
export async function runSyncCommand(parsed, context) {
|
|
9
100
|
const dbPath = parsed.dbPath ?? context.resolveDefaultClientDatabasePath();
|
|
10
101
|
const dataDir = parsed.dataDir ?? context.resolveDefaultBitcoindDataDir();
|
|
11
102
|
const runtimePaths = context.resolveWalletRuntimePaths();
|
|
103
|
+
const ttyProgressActive = usesTtyProgress(parsed.progressOutput, context.stderr);
|
|
12
104
|
let controlLock = null;
|
|
13
105
|
let store = null;
|
|
14
106
|
let storeOwned = true;
|
|
@@ -40,6 +132,12 @@ export async function runSyncCommand(parsed, context) {
|
|
|
40
132
|
dataDir,
|
|
41
133
|
walletRootId: walletRoot.walletRootId,
|
|
42
134
|
progressOutput: parsed.progressOutput,
|
|
135
|
+
onProgress: ttyProgressActive ? undefined : createSyncProgressReporter({
|
|
136
|
+
progressOutput: parsed.progressOutput,
|
|
137
|
+
write: (line) => {
|
|
138
|
+
writeLine(context.stderr, line);
|
|
139
|
+
},
|
|
140
|
+
}),
|
|
43
141
|
});
|
|
44
142
|
storeOwned = false;
|
|
45
143
|
const stopWatcher = createStopSignalWatcher(context.signalSource, context.stderr, client, context.forceExit, [runtimePaths.walletControlLockPath]);
|