@cogcoin/client 1.1.3 → 1.1.4

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # `@cogcoin/client`
2
2
 
3
- `@cogcoin/client@1.1.0` is the reference 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@1.1.4` is the reference 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
 
@@ -5,6 +5,7 @@ import { promisify } from "node:util";
5
5
  import net from "node:net";
6
6
  import { getBitcoindPath } from "@cogcoin/bitcoin";
7
7
  import { resolveDefaultBitcoindDataDirForTesting } from "../app-paths.js";
8
+ import { DEFAULT_MANAGED_BITCOIND_FOLLOW_POLL_INTERVAL_MS } from "./types.js";
8
9
  import { BitcoinRpcClient } from "./rpc.js";
9
10
  const execFileAsync = promisify(execFile);
10
11
  const SUPPORTED_BITCOIND_VERSION = "30.2.0";
@@ -156,7 +157,7 @@ export async function launchManagedBitcoindNode(options) {
156
157
  endpoint: zmqEndpoint,
157
158
  topic: "hashblock",
158
159
  port: zmqPort,
159
- pollIntervalMs: options.pollIntervalMs ?? 15_000,
160
+ pollIntervalMs: options.pollIntervalMs ?? DEFAULT_MANAGED_BITCOIND_FOLLOW_POLL_INTERVAL_MS,
160
161
  };
161
162
  const child = spawn(bitcoindPath, buildBitcoindArgsForTesting(resolvedOptions, rpcPort, zmqPort, p2pPort), {
162
163
  stdio: ["ignore", "pipe", "pipe"],
@@ -2,7 +2,6 @@ import { randomBytes } from "node:crypto";
2
2
  import { execFile, spawn } from "node:child_process";
3
3
  import { access, constants, mkdir, readFile, readdir, rm } from "node:fs/promises";
4
4
  import { dirname, join } from "node:path";
5
- import { totalmem } from "node:os";
6
5
  import { promisify } from "node:util";
7
6
  import net from "node:net";
8
7
  import { getBitcoindPath } from "@cogcoin/bitcoin";
@@ -12,37 +11,20 @@ import { writeRuntimeStatusFile } from "../wallet/fs/status-file.js";
12
11
  import { stopIndexerDaemonServiceWithLockHeld } from "./indexer-daemon.js";
13
12
  import { createRpcClient, validateNodeConfigForTesting } from "./node.js";
14
13
  import { resolveManagedServicePaths, UNINITIALIZED_WALLET_ROOT_ID } from "./service-paths.js";
15
- import { MANAGED_BITCOIND_SERVICE_API_VERSION as MANAGED_BITCOIND_SERVICE_API_VERSION_VALUE } from "./types.js";
14
+ import { DEFAULT_MANAGED_BITCOIND_FOLLOW_POLL_INTERVAL_MS, MANAGED_BITCOIND_SERVICE_API_VERSION as MANAGED_BITCOIND_SERVICE_API_VERSION_VALUE, } from "./types.js";
16
15
  const execFileAsync = promisify(execFile);
17
16
  const LOCAL_HOST = "127.0.0.1";
18
17
  const SUPPORTED_BITCOIND_VERSION = "30.2.0";
19
18
  const DEFAULT_STARTUP_TIMEOUT_MS = 30_000;
20
19
  const DEFAULT_SHUTDOWN_TIMEOUT_MS = 15_000;
21
- const DEFAULT_DBCACHE_MIB = 450;
20
+ const DEFAULT_DBCACHE_MIB = 4096;
22
21
  const claimedUninitializedRuntimeKeys = new Set();
23
- const GIB = 1024 ** 3;
24
22
  export function resolveManagedBitcoindDbcacheMiB(totalRamBytes) {
25
- if (!Number.isFinite(totalRamBytes) || totalRamBytes <= 0) {
26
- return DEFAULT_DBCACHE_MIB;
27
- }
28
- if (totalRamBytes < 8 * GIB) {
29
- return 450;
30
- }
31
- if (totalRamBytes < 16 * GIB) {
32
- return 768;
33
- }
34
- if (totalRamBytes < 32 * GIB) {
35
- return 1024;
36
- }
37
- return 2048;
23
+ void totalRamBytes;
24
+ return DEFAULT_DBCACHE_MIB;
38
25
  }
39
26
  function detectManagedBitcoindDbcacheMiB() {
40
- try {
41
- return resolveManagedBitcoindDbcacheMiB(totalmem());
42
- }
43
- catch {
44
- return DEFAULT_DBCACHE_MIB;
45
- }
27
+ return DEFAULT_DBCACHE_MIB;
46
28
  }
47
29
  function sleep(ms) {
48
30
  return new Promise((resolve) => {
@@ -826,7 +808,7 @@ export async function attachOrStartManagedBitcoindService(options) {
826
808
  endpoint: `tcp://${LOCAL_HOST}:${runtimeConfig.zmqPort}`,
827
809
  topic: "hashblock",
828
810
  port: runtimeConfig.zmqPort,
829
- pollIntervalMs: startOptions.pollIntervalMs ?? 15_000,
811
+ pollIntervalMs: startOptions.pollIntervalMs ?? DEFAULT_MANAGED_BITCOIND_FOLLOW_POLL_INTERVAL_MS,
830
812
  };
831
813
  const spawnOptions = startOptions.serviceLifetime === "ephemeral"
832
814
  ? {
@@ -87,6 +87,7 @@ export interface ManagedBitcoindRuntimeConfig {
87
87
  getblockArchiveEndHeight?: number | null;
88
88
  getblockArchiveSha256?: string | null;
89
89
  }
90
+ export declare const DEFAULT_MANAGED_BITCOIND_FOLLOW_POLL_INTERVAL_MS = 2000;
90
91
  export declare const MANAGED_BITCOIND_SERVICE_API_VERSION = "cogcoin/bitcoind-service/v1";
91
92
  export type ManagedBitcoindServiceState = "starting" | "ready" | "stopping" | "failed";
92
93
  export interface ManagedBitcoindServiceStatus {
@@ -1,3 +1,4 @@
1
+ export const DEFAULT_MANAGED_BITCOIND_FOLLOW_POLL_INTERVAL_MS = 2_000;
1
2
  export const MANAGED_BITCOIND_SERVICE_API_VERSION = "cogcoin/bitcoind-service/v1";
2
3
  export const INDEXER_DAEMON_SERVICE_API_VERSION = "cogcoin/indexer-ipc/v1";
3
4
  export const INDEXER_DAEMON_SCHEMA_VERSION = "cogcoin/indexer-db/v1";
@@ -1,6 +1,6 @@
1
1
  import { readSnapshotWithRetry } from "../../bitcoind/indexer-daemon.js";
2
2
  import { type WalletSecretProvider } from "../state/provider.js";
3
- import type { WalletLocalStateStatus, WalletReadContext } from "./types.js";
3
+ import type { WalletBitcoindStatus, WalletLocalStateStatus, WalletNodeStatus, WalletReadContext, WalletServiceHealth } from "./types.js";
4
4
  import type { WalletRuntimePaths } from "../runtime.js";
5
5
  declare function inspectWalletLocalState(options?: {
6
6
  dataDir?: string;
@@ -9,6 +9,10 @@ declare function inspectWalletLocalState(options?: {
9
9
  paths?: WalletRuntimePaths;
10
10
  walletControlLockHeld?: boolean;
11
11
  }): Promise<WalletLocalStateStatus>;
12
+ export declare function deriveNodeHealthForTesting(status: WalletNodeStatus | null, bitcoindHealth: WalletBitcoindStatus["health"]): {
13
+ health: WalletServiceHealth;
14
+ message: string | null;
15
+ };
12
16
  export declare function openWalletReadContext(options: {
13
17
  dataDir: string;
14
18
  databasePath: string;
@@ -20,6 +20,9 @@ import { describeClientPasswordLockedMessage, describeClientPasswordMigrationMes
20
20
  import { createWalletReadModel } from "./project.js";
21
21
  const DEFAULT_SERVICE_START_TIMEOUT_MS = 10_000;
22
22
  const STALE_HEARTBEAT_THRESHOLD_MS = 15_000;
23
+ const TOLERATED_NODE_HEADER_LEAD_BLOCKS = 2;
24
+ const TOLERATED_NODE_HEADER_LEAD_MESSAGE = "Bitcoin headers can briefly lead validated blocks; a short 1-2 block lead is normal and is being tolerated.";
25
+ const NODE_CATCHING_UP_MESSAGE = "Bitcoin Core is still catching up to headers.";
23
26
  function btcAmountToSats(value) {
24
27
  return BigInt(Math.round(value * 100_000_000));
25
28
  }
@@ -351,14 +354,23 @@ function deriveBitcoindHealth(options) {
351
354
  function deriveNodeHealth(status, bitcoindHealth) {
352
355
  if (bitcoindHealth !== "ready" || status === null || !status.ready) {
353
356
  return {
354
- health: "unavailable",
355
- message: "Bitcoin service is unavailable.",
357
+ health: "catching-up",
358
+ message: NODE_CATCHING_UP_MESSAGE,
356
359
  };
357
360
  }
358
- if (status.nodeBestHeight !== null && status.nodeHeaderHeight !== null && status.nodeBestHeight < status.nodeHeaderHeight) {
361
+ const headerLead = status.nodeBestHeight !== null && status.nodeHeaderHeight !== null
362
+ ? status.nodeHeaderHeight - status.nodeBestHeight
363
+ : null;
364
+ if (headerLead !== null && headerLead > 0) {
365
+ if (headerLead <= TOLERATED_NODE_HEADER_LEAD_BLOCKS) {
366
+ return {
367
+ health: "synced",
368
+ message: TOLERATED_NODE_HEADER_LEAD_MESSAGE,
369
+ };
370
+ }
359
371
  return {
360
372
  health: "catching-up",
361
- message: "Bitcoin Core is still catching up to headers.",
373
+ message: NODE_CATCHING_UP_MESSAGE,
362
374
  };
363
375
  }
364
376
  return {
@@ -366,6 +378,9 @@ function deriveNodeHealth(status, bitcoindHealth) {
366
378
  message: null,
367
379
  };
368
380
  }
381
+ export function deriveNodeHealthForTesting(status, bitcoindHealth) {
382
+ return deriveNodeHealth(status, bitcoindHealth);
383
+ }
369
384
  function deriveIndexerHealth(options) {
370
385
  const daemonStatus = options.source === "lease"
371
386
  ? options.daemonStatus
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cogcoin/client",
3
- "version": "1.1.3",
3
+ "version": "1.1.4",
4
4
  "description": "Store-backed Cogcoin client with wallet flows, SQLite persistence, and managed Bitcoin Core integration.",
5
5
  "license": "MIT",
6
6
  "type": "module",