@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.
Files changed (91) hide show
  1. package/README.md +11 -2
  2. package/dist/bitcoind/bootstrap/chainstate.d.ts +2 -1
  3. package/dist/bitcoind/bootstrap/chainstate.js +4 -1
  4. package/dist/bitcoind/bootstrap/controller.d.ts +4 -1
  5. package/dist/bitcoind/bootstrap/controller.js +42 -5
  6. package/dist/bitcoind/bootstrap/getblock-archive.d.ts +39 -0
  7. package/dist/bitcoind/bootstrap/getblock-archive.js +548 -0
  8. package/dist/bitcoind/bootstrap/headers.d.ts +12 -0
  9. package/dist/bitcoind/bootstrap/headers.js +95 -10
  10. package/dist/bitcoind/bootstrap.d.ts +1 -0
  11. package/dist/bitcoind/bootstrap.js +1 -0
  12. package/dist/bitcoind/client/factory.js +91 -28
  13. package/dist/bitcoind/client/managed-client.d.ts +1 -1
  14. package/dist/bitcoind/client/managed-client.js +4 -3
  15. package/dist/bitcoind/client/sync-engine.js +55 -13
  16. package/dist/bitcoind/errors.js +18 -0
  17. package/dist/bitcoind/indexer-daemon-main.js +78 -0
  18. package/dist/bitcoind/indexer-daemon.d.ts +10 -1
  19. package/dist/bitcoind/indexer-daemon.js +44 -28
  20. package/dist/bitcoind/node.js +2 -0
  21. package/dist/bitcoind/processing-start-height.d.ts +7 -0
  22. package/dist/bitcoind/processing-start-height.js +9 -0
  23. package/dist/bitcoind/progress/constants.d.ts +1 -0
  24. package/dist/bitcoind/progress/constants.js +1 -0
  25. package/dist/bitcoind/progress/controller.d.ts +22 -0
  26. package/dist/bitcoind/progress/controller.js +49 -23
  27. package/dist/bitcoind/progress/formatting.js +29 -1
  28. package/dist/bitcoind/progress/render-policy.d.ts +35 -0
  29. package/dist/bitcoind/progress/render-policy.js +81 -0
  30. package/dist/bitcoind/retryable-rpc.d.ts +11 -0
  31. package/dist/bitcoind/retryable-rpc.js +30 -0
  32. package/dist/bitcoind/service-paths.js +2 -6
  33. package/dist/bitcoind/service.d.ts +21 -2
  34. package/dist/bitcoind/service.js +274 -122
  35. package/dist/bitcoind/testing.d.ts +2 -2
  36. package/dist/bitcoind/testing.js +2 -2
  37. package/dist/bitcoind/types.d.ts +36 -1
  38. package/dist/cli/commands/follow.js +11 -0
  39. package/dist/cli/commands/getblock-archive-restart.d.ts +5 -0
  40. package/dist/cli/commands/getblock-archive-restart.js +15 -0
  41. package/dist/cli/commands/mining-admin.js +4 -0
  42. package/dist/cli/commands/mining-read.js +8 -5
  43. package/dist/cli/commands/mining-runtime.js +4 -0
  44. package/dist/cli/commands/service-runtime.js +150 -134
  45. package/dist/cli/commands/status.js +2 -0
  46. package/dist/cli/commands/sync.js +11 -0
  47. package/dist/cli/commands/wallet-admin.js +106 -24
  48. package/dist/cli/commands/wallet-mutation.js +57 -4
  49. package/dist/cli/commands/wallet-read.js +2 -0
  50. package/dist/cli/context.js +8 -4
  51. package/dist/cli/mutation-command-groups.d.ts +2 -1
  52. package/dist/cli/mutation-command-groups.js +5 -0
  53. package/dist/cli/mutation-json.d.ts +18 -2
  54. package/dist/cli/mutation-json.js +49 -0
  55. package/dist/cli/mutation-success.d.ts +1 -0
  56. package/dist/cli/mutation-success.js +2 -2
  57. package/dist/cli/output.js +86 -1
  58. package/dist/cli/parse.d.ts +1 -1
  59. package/dist/cli/parse.js +133 -3
  60. package/dist/cli/preview-json.d.ts +10 -1
  61. package/dist/cli/preview-json.js +32 -0
  62. package/dist/cli/prompt.js +1 -1
  63. package/dist/cli/runner.js +4 -0
  64. package/dist/cli/types.d.ts +15 -5
  65. package/dist/cli/types.js +1 -1
  66. package/dist/cli/wallet-format.js +140 -14
  67. package/dist/wallet/lifecycle.d.ts +21 -1
  68. package/dist/wallet/lifecycle.js +252 -116
  69. package/dist/wallet/mining/visualizer.d.ts +11 -6
  70. package/dist/wallet/mining/visualizer.js +32 -15
  71. package/dist/wallet/read/context.js +10 -4
  72. package/dist/wallet/reset.d.ts +61 -2
  73. package/dist/wallet/reset.js +246 -89
  74. package/dist/wallet/root-resolution.d.ts +20 -0
  75. package/dist/wallet/root-resolution.js +37 -0
  76. package/dist/wallet/runtime.d.ts +13 -1
  77. package/dist/wallet/runtime.js +54 -11
  78. package/dist/wallet/state/crypto.d.ts +3 -0
  79. package/dist/wallet/state/crypto.js +3 -0
  80. package/dist/wallet/state/provider.d.ts +1 -0
  81. package/dist/wallet/state/provider.js +119 -3
  82. package/dist/wallet/state/seed-index.d.ts +43 -0
  83. package/dist/wallet/state/seed-index.js +151 -0
  84. package/dist/wallet/state/storage.d.ts +7 -1
  85. package/dist/wallet/state/storage.js +39 -0
  86. package/dist/wallet/tx/anchor.d.ts +22 -0
  87. package/dist/wallet/tx/anchor.js +215 -8
  88. package/dist/wallet/tx/index.d.ts +1 -1
  89. package/dist/wallet/tx/index.js +1 -1
  90. package/dist/wallet/types.d.ts +1 -0
  91. package/package.json +1 -1
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # `@cogcoin/client`
2
2
 
3
- `@cogcoin/client@0.5.5` 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.
3
+ `@cogcoin/client@0.5.7` 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
 
@@ -107,6 +107,7 @@ The built-in managed-node integration:
107
107
  - uses RPC for durable reads and ZMQ `hashblock` notifications for tip following
108
108
  - launches a local full node with cookie auth
109
109
  - defaults to an assumeutxo-first mainnet bootstrap using `https://snapshots.cogcoin.org/utxo-910000.dat`
110
+ - opportunistically loads the public getblock archive family from `https://snapshots.cogcoin.org/getblock-910000-latest.{json,dat}` to accelerate post-`910000` Bitcoin Core catch-up
110
111
  - composes the existing SQLite-backed client rather than replacing it
111
112
 
112
113
  If `dataDir` is omitted, the managed node defaults to:
@@ -120,7 +121,15 @@ On a fresh mainnet managed sync, `syncToTip()` or `startFollowingTip()`:
120
121
  1. downloads the pinned Cogcoin UTXO snapshot with resume support
121
122
  2. validates its known size and SHA-256
122
123
  3. loads it with Bitcoin Core assumeutxo
123
- 4. continues Bitcoin sync and Cogcoin replay from the managed node until the live tip is caught up
124
+ 4. opportunistically downloads and validates the public getblock archive for raw post-snapshot Bitcoin blocks
125
+ 5. loads that archive into managed Bitcoin Core when available
126
+ 6. continues Bitcoin sync and Cogcoin replay from the managed node until the live tip is caught up
127
+
128
+ The public getblock archive provenance is tracked in the companion scraper repository:
129
+
130
+ - [`github.com/cogcoin/bitcoin-scrape`](https://github.com/cogcoin/bitcoin-scrape)
131
+
132
+ That repo documents how the `getblock-910000-latest.dat` and `getblock-910000-latest.json` artifacts are assembled from `bitcoin-cli getblockhash` plus `bitcoin-cli getblock <hash> 0`, including the blk-style file layout, manifest format, durability guarantees, and height-based cache-busting rules.
124
133
 
125
134
  The managed `bitcoind` client also exposes:
126
135
 
@@ -1,4 +1,5 @@
1
1
  import type { BitcoinRpcClient } from "../rpc.js";
2
- import type { SnapshotMetadata } from "../types.js";
2
+ import type { RpcChainState, SnapshotMetadata } from "../types.js";
3
3
  import type { BootstrapPersistentState } from "./types.js";
4
4
  export declare function isSnapshotAlreadyLoaded(rpc: Pick<BitcoinRpcClient, "getChainStates">, snapshot: SnapshotMetadata, state: BootstrapPersistentState): Promise<boolean>;
5
+ export declare function findLoadedSnapshotChainState(rpc: Pick<BitcoinRpcClient, "getChainStates">, snapshot: SnapshotMetadata, state: BootstrapPersistentState): Promise<RpcChainState | null>;
@@ -8,6 +8,9 @@ function chainStateMatches(chainState, snapshot, baseHeight, tipHashHex) {
8
8
  return chainState.blocks === snapshot.height && chainState.validated === false;
9
9
  }
10
10
  export async function isSnapshotAlreadyLoaded(rpc, snapshot, state) {
11
+ return (await findLoadedSnapshotChainState(rpc, snapshot, state)) !== null;
12
+ }
13
+ export async function findLoadedSnapshotChainState(rpc, snapshot, state) {
11
14
  const chainStates = await rpc.getChainStates();
12
- return chainStates.chainstates.some((chainState) => chainStateMatches(chainState, snapshot, state.baseHeight, state.tipHashHex));
15
+ return chainStates.chainstates.find((chainState) => chainStateMatches(chainState, snapshot, state.baseHeight, state.tipHashHex)) ?? null;
13
16
  }
@@ -1,7 +1,8 @@
1
1
  import type { ClientTip } from "../../types.js";
2
2
  import { ManagedProgressController } from "../progress.js";
3
3
  import { BitcoinRpcClient } from "../rpc.js";
4
- import type { BootstrapPhase, SnapshotMetadata } from "../types.js";
4
+ import { type ManagedRpcRetryState } from "../retryable-rpc.js";
5
+ import type { BootstrapPhase, SnapshotMetadata, SnapshotChunkManifest } from "../types.js";
5
6
  export declare class AssumeUtxoBootstrapController {
6
7
  #private;
7
8
  constructor(options: {
@@ -9,12 +10,14 @@ export declare class AssumeUtxoBootstrapController {
9
10
  dataDir: string;
10
11
  progress: ManagedProgressController;
11
12
  snapshot?: SnapshotMetadata;
13
+ manifest?: SnapshotChunkManifest;
12
14
  fetchImpl?: typeof fetch;
13
15
  });
14
16
  get quoteStatePath(): string;
15
17
  get snapshot(): SnapshotMetadata;
16
18
  ensureReady(indexedTip: ClientTip | null, expectedChain: "main" | "regtest", options?: {
17
19
  signal?: AbortSignal;
20
+ retryState?: ManagedRpcRetryState;
18
21
  }): Promise<void>;
19
22
  getStateForTesting(): Promise<{
20
23
  metadataVersion: number;
@@ -1,3 +1,4 @@
1
+ import { join } from "node:path";
1
2
  import { createBootstrapProgressForTesting, ManagedProgressController } from "../progress.js";
2
3
  import { BitcoinRpcClient } from "../rpc.js";
3
4
  import { DEFAULT_SNAPSHOT_METADATA } from "./constants.js";
@@ -5,7 +6,8 @@ import { downloadSnapshotFileForTesting } from "./download.js";
5
6
  import { waitForHeaders } from "./headers.js";
6
7
  import { resolveBootstrapPaths } from "./paths.js";
7
8
  import { loadBootstrapStateRecord, saveBootstrapState } from "./state.js";
8
- import { isSnapshotAlreadyLoaded } from "./chainstate.js";
9
+ import { findLoadedSnapshotChainState, isSnapshotAlreadyLoaded } from "./chainstate.js";
10
+ import { describeManagedRpcRetryError, isRetryableManagedRpcError, } from "../retryable-rpc.js";
9
11
  async function loadSnapshotIntoNode(rpc, snapshotPath) {
10
12
  return rpc.loadTxOutSet(snapshotPath);
11
13
  }
@@ -14,13 +16,17 @@ export class AssumeUtxoBootstrapController {
14
16
  #paths;
15
17
  #progress;
16
18
  #snapshot;
19
+ #debugLogPath;
20
+ #manifest;
17
21
  #fetchImpl;
18
22
  #stateRecordPromise = null;
19
23
  constructor(options) {
20
24
  this.#rpc = options.rpc;
21
25
  this.#progress = options.progress;
22
26
  this.#snapshot = options.snapshot ?? DEFAULT_SNAPSHOT_METADATA;
27
+ this.#manifest = options.manifest;
23
28
  this.#paths = resolveBootstrapPaths(options.dataDir, this.#snapshot);
29
+ this.#debugLogPath = join(options.dataDir, "debug.log");
24
30
  this.#fetchImpl = options.fetchImpl;
25
31
  }
26
32
  get quoteStatePath() {
@@ -47,18 +53,23 @@ export class AssumeUtxoBootstrapController {
47
53
  }
48
54
  const { state, snapshotIdentity } = await this.#loadStateRecord();
49
55
  if (state.loadTxOutSetComplete && await isSnapshotAlreadyLoaded(this.#rpc, this.#snapshot, state)) {
56
+ if (state.lastError !== null) {
57
+ state.lastError = null;
58
+ await saveBootstrapState(this.#paths, state);
59
+ }
50
60
  await this.#progress.setPhase("bitcoin_sync", {
51
61
  blocks: state.baseHeight,
52
62
  targetHeight: state.baseHeight ?? this.#snapshot.height,
53
63
  baseHeight: state.baseHeight,
54
64
  tipHashHex: state.tipHashHex,
55
65
  message: "Using the previously loaded assumeutxo chainstate.",
56
- lastError: state.lastError,
66
+ lastError: null,
57
67
  });
58
68
  return;
59
69
  }
60
70
  await downloadSnapshotFileForTesting({
61
71
  fetchImpl: this.#fetchImpl,
72
+ manifest: this.#manifest,
62
73
  metadata: this.#snapshot,
63
74
  paths: this.#paths,
64
75
  progress: this.#progress,
@@ -69,22 +80,48 @@ export class AssumeUtxoBootstrapController {
69
80
  if (!await isSnapshotAlreadyLoaded(this.#rpc, this.#snapshot, state)) {
70
81
  await waitForHeaders(this.#rpc, this.#snapshot, this.#progress, {
71
82
  signal: options.signal,
83
+ retryState: options.retryState,
84
+ debugLogPath: this.#debugLogPath,
72
85
  });
73
86
  await this.#progress.setPhase("load_snapshot", {
74
87
  downloadedBytes: this.#snapshot.sizeBytes,
75
88
  totalBytes: this.#snapshot.sizeBytes,
76
89
  percent: 100,
77
90
  message: "Loading the UTXO snapshot into bitcoind.",
91
+ lastError: null,
78
92
  });
79
- const loadResult = await loadSnapshotIntoNode(this.#rpc, this.#paths.snapshotPath);
93
+ let loadResult;
94
+ try {
95
+ loadResult = await loadSnapshotIntoNode(this.#rpc, this.#paths.snapshotPath);
96
+ }
97
+ catch (error) {
98
+ if (!isRetryableManagedRpcError(error)) {
99
+ throw error;
100
+ }
101
+ state.lastError = describeManagedRpcRetryError(error);
102
+ await saveBootstrapState(this.#paths, state);
103
+ const loadedChainState = await findLoadedSnapshotChainState(this.#rpc, this.#snapshot, state);
104
+ if (loadedChainState === null) {
105
+ throw error;
106
+ }
107
+ loadResult = {
108
+ base_height: loadedChainState.blocks ?? this.#snapshot.height,
109
+ coins_loaded: 0,
110
+ tip_hash: loadedChainState.snapshot_blockhash ?? state.tipHashHex ?? "",
111
+ };
112
+ }
80
113
  state.loadTxOutSetComplete = true;
81
114
  state.baseHeight = loadResult.base_height;
82
- state.tipHashHex = loadResult.tip_hash;
115
+ state.tipHashHex = loadResult.tip_hash === "" ? state.tipHashHex : loadResult.tip_hash;
83
116
  state.phase = "bitcoin_sync";
84
117
  state.lastError = null;
85
118
  await saveBootstrapState(this.#paths, state);
86
119
  }
87
120
  const info = await this.#rpc.getBlockchainInfo();
121
+ if (state.lastError !== null) {
122
+ state.lastError = null;
123
+ await saveBootstrapState(this.#paths, state);
124
+ }
88
125
  await this.#progress.setPhase("bitcoin_sync", {
89
126
  blocks: info.blocks,
90
127
  headers: info.headers,
@@ -92,7 +129,7 @@ export class AssumeUtxoBootstrapController {
92
129
  baseHeight: state.baseHeight,
93
130
  tipHashHex: state.tipHashHex,
94
131
  message: "Bitcoin Core is syncing blocks after assumeutxo bootstrap.",
95
- lastError: state.lastError,
132
+ lastError: null,
96
133
  });
97
134
  }
98
135
  async getStateForTesting() {
@@ -0,0 +1,39 @@
1
+ import type { ManagedProgressController } from "../progress.js";
2
+ import type { GetblockArchiveManifest } from "../types.js";
3
+ interface GetblockArchivePaths {
4
+ directory: string;
5
+ artifactPath: string;
6
+ partialArtifactPath: string;
7
+ manifestPath: string;
8
+ partialManifestPath: string;
9
+ statePath: string;
10
+ }
11
+ export interface ReadyGetblockArchive {
12
+ manifest: GetblockArchiveManifest;
13
+ artifactPath: string;
14
+ manifestPath: string;
15
+ }
16
+ export declare function resolveGetblockArchivePathsForTesting(dataDir: string): GetblockArchivePaths;
17
+ export declare function resolveReadyGetblockArchiveForTesting(dataDir: string): Promise<ReadyGetblockArchive | null>;
18
+ export declare function prepareLatestGetblockArchive(options: {
19
+ dataDir: string;
20
+ progress: Pick<ManagedProgressController, "setPhase">;
21
+ fetchImpl?: typeof fetch;
22
+ signal?: AbortSignal;
23
+ }): Promise<ReadyGetblockArchive | null>;
24
+ export declare const prepareLatestGetblockArchiveForTesting: typeof prepareLatestGetblockArchive;
25
+ export declare function waitForGetblockArchiveImportForTesting(rpc: Pick<{
26
+ getBlockchainInfo(): Promise<{
27
+ blocks: number;
28
+ headers: number;
29
+ bestblockhash: string;
30
+ }>;
31
+ }, "getBlockchainInfo">, progress: Pick<ManagedProgressController, "setPhase">, targetEndHeight: number, signal?: AbortSignal): Promise<void>;
32
+ export declare function waitForGetblockArchiveImport(rpc: Pick<{
33
+ getBlockchainInfo(): Promise<{
34
+ blocks: number;
35
+ headers: number;
36
+ bestblockhash: string;
37
+ }>;
38
+ }, "getBlockchainInfo">, progress: Pick<ManagedProgressController, "setPhase">, targetEndHeight: number, signal?: AbortSignal): Promise<void>;
39
+ export {};