@cogcoin/client 1.1.9 → 1.1.11

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 (66) hide show
  1. package/README.md +1 -1
  2. package/dist/bitcoind/client/managed-client.d.ts +2 -0
  3. package/dist/bitcoind/client/managed-client.js +6 -0
  4. package/dist/bitcoind/indexer-daemon/background-follow.d.ts +23 -0
  5. package/dist/bitcoind/indexer-daemon/background-follow.js +132 -0
  6. package/dist/bitcoind/indexer-daemon/client.d.ts +12 -0
  7. package/dist/bitcoind/indexer-daemon/client.js +137 -0
  8. package/dist/bitcoind/indexer-daemon/lifecycle.d.ts +30 -0
  9. package/dist/bitcoind/indexer-daemon/lifecycle.js +153 -0
  10. package/dist/bitcoind/indexer-daemon/process.d.ts +35 -0
  11. package/dist/bitcoind/indexer-daemon/process.js +140 -0
  12. package/dist/bitcoind/indexer-daemon/runtime.d.ts +23 -0
  13. package/dist/bitcoind/indexer-daemon/runtime.js +204 -0
  14. package/dist/bitcoind/indexer-daemon/server.d.ts +12 -0
  15. package/dist/bitcoind/indexer-daemon/server.js +87 -0
  16. package/dist/bitcoind/indexer-daemon/snapshot-leases.d.ts +23 -0
  17. package/dist/bitcoind/indexer-daemon/snapshot-leases.js +139 -0
  18. package/dist/bitcoind/indexer-daemon/status.d.ts +23 -0
  19. package/dist/bitcoind/indexer-daemon/status.js +282 -0
  20. package/dist/bitcoind/indexer-daemon/types.d.ts +141 -0
  21. package/dist/bitcoind/indexer-daemon/types.js +1 -0
  22. package/dist/bitcoind/indexer-daemon-main.js +14 -665
  23. package/dist/bitcoind/indexer-daemon.d.ts +4 -132
  24. package/dist/bitcoind/indexer-daemon.js +2 -417
  25. package/dist/bitcoind/managed-bitcoind-service-config.d.ts +30 -0
  26. package/dist/bitcoind/managed-bitcoind-service-config.js +202 -0
  27. package/dist/bitcoind/managed-bitcoind-service-lifecycle.d.ts +28 -0
  28. package/dist/bitcoind/managed-bitcoind-service-lifecycle.js +296 -0
  29. package/dist/bitcoind/managed-bitcoind-service-process.d.ts +8 -0
  30. package/dist/bitcoind/managed-bitcoind-service-process.js +48 -0
  31. package/dist/bitcoind/managed-bitcoind-service-replica.d.ts +8 -0
  32. package/dist/bitcoind/managed-bitcoind-service-replica.js +142 -0
  33. package/dist/bitcoind/managed-bitcoind-service-status.d.ts +42 -0
  34. package/dist/bitcoind/managed-bitcoind-service-status.js +170 -0
  35. package/dist/bitcoind/managed-bitcoind-service-types.d.ts +36 -0
  36. package/dist/bitcoind/managed-bitcoind-service-types.js +1 -0
  37. package/dist/bitcoind/service.d.ts +7 -63
  38. package/dist/bitcoind/service.js +7 -797
  39. package/dist/cli/mining-format.js +6 -1
  40. package/dist/cli/wallet-format/balance.js +1 -1
  41. package/dist/client/default-client.d.ts +3 -1
  42. package/dist/client/default-client.js +22 -0
  43. package/dist/types.d.ts +13 -1
  44. package/dist/wallet/fs/atomic.d.ts +11 -2
  45. package/dist/wallet/fs/atomic.js +45 -5
  46. package/dist/wallet/mining/cycle.js +4 -4
  47. package/dist/wallet/mining/engine-types.d.ts +1 -0
  48. package/dist/wallet/mining/engine-types.js +9 -1
  49. package/dist/wallet/mining/projection.d.ts +1 -0
  50. package/dist/wallet/mining/projection.js +15 -1
  51. package/dist/wallet/mining/publish.js +3 -6
  52. package/dist/wallet/mining/runner.js +30 -18
  53. package/dist/wallet/mining/visualizer-sync.js +7 -9
  54. package/dist/wallet/mining/visualizer.js +9 -7
  55. package/dist/wallet/read/context.d.ts +4 -10
  56. package/dist/wallet/read/context.js +6 -228
  57. package/dist/wallet/read/local-state.d.ts +36 -0
  58. package/dist/wallet/read/local-state.js +259 -0
  59. package/dist/wallet/read/managed-bitcoind.d.ts +30 -0
  60. package/dist/wallet/read/managed-bitcoind.js +138 -0
  61. package/dist/wallet/read/managed-indexer.d.ts +23 -0
  62. package/dist/wallet/read/managed-indexer.js +87 -0
  63. package/dist/wallet/read/managed-services.d.ts +6 -21
  64. package/dist/wallet/read/managed-services.js +23 -196
  65. package/dist/wallet/read/types.d.ts +1 -0
  66. package/package.json +1 -1
@@ -0,0 +1,138 @@
1
+ import { loadBundledGenesisParameters } from "@cogcoin/indexer";
2
+ import { deriveManagedBitcoindWalletStatus, resolveManagedBitcoindProbeDecision, } from "../../bitcoind/managed-runtime/bitcoind-policy.js";
3
+ import { createRpcClient } from "../../bitcoind/node.js";
4
+ import { resolveCogcoinProcessingStartHeight } from "../../bitcoind/processing-start-height.js";
5
+ import { attachOrStartManagedBitcoindService, probeManagedBitcoindService, } from "../../bitcoind/service.js";
6
+ import { verifyManagedCoreWalletReplica } from "../lifecycle.js";
7
+ const TOLERATED_NODE_HEADER_LEAD_BLOCKS = 2;
8
+ 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.";
9
+ const NODE_CATCHING_UP_MESSAGE = "Bitcoin Core is still catching up to headers.";
10
+ const defaultManagedWalletBitcoindReadDeps = {
11
+ loadBundledGenesisParameters,
12
+ probeManagedBitcoindService,
13
+ attachOrStartManagedBitcoindService,
14
+ createRpcClient,
15
+ verifyManagedCoreWalletReplica,
16
+ };
17
+ function deriveNodeHealth(status, bitcoindHealth) {
18
+ if (bitcoindHealth !== "ready" || status === null || !status.ready) {
19
+ return {
20
+ health: "catching-up",
21
+ message: NODE_CATCHING_UP_MESSAGE,
22
+ };
23
+ }
24
+ const headerLead = status.nodeBestHeight !== null && status.nodeHeaderHeight !== null
25
+ ? status.nodeHeaderHeight - status.nodeBestHeight
26
+ : null;
27
+ if (headerLead !== null && headerLead > 0) {
28
+ if (headerLead <= TOLERATED_NODE_HEADER_LEAD_BLOCKS) {
29
+ return {
30
+ health: "synced",
31
+ message: TOLERATED_NODE_HEADER_LEAD_MESSAGE,
32
+ };
33
+ }
34
+ return {
35
+ health: "catching-up",
36
+ message: NODE_CATCHING_UP_MESSAGE,
37
+ };
38
+ }
39
+ return {
40
+ health: "synced",
41
+ message: null,
42
+ };
43
+ }
44
+ export function deriveNodeHealthForTesting(status, bitcoindHealth) {
45
+ return deriveNodeHealth(status, bitcoindHealth);
46
+ }
47
+ async function attachNodeStatus(options, dependencies) {
48
+ try {
49
+ const probe = await dependencies.probeManagedBitcoindService({
50
+ dataDir: options.dataDir,
51
+ chain: "main",
52
+ startHeight: 0,
53
+ walletRootId: options.walletRootId,
54
+ startupTimeoutMs: options.startupTimeoutMs,
55
+ });
56
+ const decision = resolveManagedBitcoindProbeDecision(probe);
57
+ if (decision.action === "reject") {
58
+ return {
59
+ handle: null,
60
+ rpc: null,
61
+ status: null,
62
+ observedStatus: probe.status,
63
+ error: decision.error,
64
+ };
65
+ }
66
+ const genesis = await dependencies.loadBundledGenesisParameters();
67
+ const handle = await dependencies.attachOrStartManagedBitcoindService({
68
+ dataDir: options.dataDir,
69
+ chain: "main",
70
+ startHeight: resolveCogcoinProcessingStartHeight(genesis),
71
+ walletRootId: options.walletRootId,
72
+ startupTimeoutMs: options.startupTimeoutMs,
73
+ });
74
+ const rpc = dependencies.createRpcClient(handle.rpc);
75
+ const [chainInfo, serviceStatus] = await Promise.all([
76
+ rpc.getBlockchainInfo(),
77
+ handle.refreshServiceStatus?.(),
78
+ ]);
79
+ const status = {
80
+ ready: true,
81
+ chain: chainInfo.chain,
82
+ pid: handle.pid,
83
+ walletRootId: handle.walletRootId ?? null,
84
+ nodeBestHeight: chainInfo.blocks,
85
+ nodeBestHashHex: chainInfo.bestblockhash,
86
+ nodeHeaderHeight: chainInfo.headers,
87
+ serviceUpdatedAtUnixMs: serviceStatus?.updatedAtUnixMs ?? null,
88
+ serviceStatus: serviceStatus ?? null,
89
+ walletReplica: serviceStatus?.walletReplica ?? null,
90
+ walletReplicaMessage: serviceStatus?.walletReplica?.message ?? null,
91
+ };
92
+ return {
93
+ handle,
94
+ rpc,
95
+ status,
96
+ observedStatus: serviceStatus ?? null,
97
+ error: null,
98
+ };
99
+ }
100
+ catch (error) {
101
+ return {
102
+ handle: null,
103
+ rpc: null,
104
+ status: null,
105
+ observedStatus: null,
106
+ error: error instanceof Error ? error.message : String(error),
107
+ };
108
+ }
109
+ }
110
+ export async function openManagedWalletBitcoindReadState(options, dependencies = defaultManagedWalletBitcoindReadDeps) {
111
+ const node = await attachNodeStatus({
112
+ dataDir: options.dataDir,
113
+ walletRootId: options.walletRootId,
114
+ startupTimeoutMs: options.startupTimeoutMs,
115
+ }, dependencies);
116
+ if (options.localState.state !== null && node.status !== null) {
117
+ const verifiedReplica = await dependencies.verifyManagedCoreWalletReplica(options.localState.state, options.dataDir, {
118
+ nodeHandle: node.handle ?? undefined,
119
+ });
120
+ node.status = {
121
+ ...node.status,
122
+ walletReplica: verifiedReplica,
123
+ walletReplicaMessage: verifiedReplica.message ?? null,
124
+ };
125
+ }
126
+ const bitcoind = deriveManagedBitcoindWalletStatus({
127
+ status: node.observedStatus,
128
+ nodeStatus: node.status,
129
+ startupError: node.error,
130
+ });
131
+ const nodeDerived = deriveNodeHealth(node.status, bitcoind.health);
132
+ return {
133
+ node,
134
+ bitcoind,
135
+ nodeHealth: nodeDerived.health,
136
+ nodeMessage: nodeDerived.message,
137
+ };
138
+ }
@@ -0,0 +1,23 @@
1
+ import { attachOrStartIndexerDaemon, probeIndexerDaemon, readObservedIndexerDaemonStatus, readSnapshotWithRetry, type IndexerDaemonClient } from "../../bitcoind/indexer-daemon.js";
2
+ import type { ManagedBitcoindNodeHandle } from "../../bitcoind/types.js";
3
+ import type { WalletIndexerStatus, WalletSnapshotView } from "./types.js";
4
+ export type ManagedWalletIndexerReadDeps = {
5
+ probeIndexerDaemon: typeof probeIndexerDaemon;
6
+ attachOrStartIndexerDaemon: typeof attachOrStartIndexerDaemon;
7
+ readSnapshotWithRetry: typeof readSnapshotWithRetry;
8
+ readObservedIndexerDaemonStatus: typeof readObservedIndexerDaemonStatus;
9
+ };
10
+ export interface ManagedWalletIndexerReadState {
11
+ daemonClient: IndexerDaemonClient | null;
12
+ indexer: WalletIndexerStatus;
13
+ snapshot: WalletSnapshotView | null;
14
+ }
15
+ export declare function openManagedWalletIndexerReadState(options: {
16
+ dataDir: string;
17
+ databasePath: string;
18
+ walletRootId: string;
19
+ startupTimeoutMs: number;
20
+ expectedIndexerBinaryVersion: string | null;
21
+ now: number;
22
+ nodeHandle: ManagedBitcoindNodeHandle | null;
23
+ }, dependencies?: ManagedWalletIndexerReadDeps): Promise<ManagedWalletIndexerReadState>;
@@ -0,0 +1,87 @@
1
+ import { deserializeIndexerState } from "@cogcoin/indexer";
2
+ import { attachOrStartIndexerDaemon, INDEXER_DAEMON_BACKGROUND_FOLLOW_RECOVERY_FAILED, probeIndexerDaemon, readObservedIndexerDaemonStatus, readSnapshotWithRetry, } from "../../bitcoind/indexer-daemon.js";
3
+ import { deriveManagedIndexerWalletStatus, resolveIndexerDaemonProbeDecision, } from "../../bitcoind/managed-runtime/indexer-policy.js";
4
+ const defaultManagedWalletIndexerReadDeps = {
5
+ probeIndexerDaemon,
6
+ attachOrStartIndexerDaemon,
7
+ readSnapshotWithRetry,
8
+ readObservedIndexerDaemonStatus,
9
+ };
10
+ export async function openManagedWalletIndexerReadState(options, dependencies = defaultManagedWalletIndexerReadDeps) {
11
+ let daemonClient = null;
12
+ let daemonStatus = null;
13
+ let observedDaemonStatus = null;
14
+ let snapshot = null;
15
+ let indexerSource = "none";
16
+ let daemonError = null;
17
+ try {
18
+ const probe = await dependencies.probeIndexerDaemon({
19
+ dataDir: options.dataDir,
20
+ walletRootId: options.walletRootId,
21
+ });
22
+ const probeDecision = resolveIndexerDaemonProbeDecision({
23
+ probe,
24
+ expectedBinaryVersion: options.expectedIndexerBinaryVersion,
25
+ });
26
+ if (probeDecision.action !== "reject") {
27
+ await probe.client?.close().catch(() => undefined);
28
+ daemonClient = await dependencies.attachOrStartIndexerDaemon({
29
+ dataDir: options.dataDir,
30
+ databasePath: options.databasePath,
31
+ walletRootId: options.walletRootId,
32
+ startupTimeoutMs: options.startupTimeoutMs,
33
+ ensureBackgroundFollow: true,
34
+ expectedBinaryVersion: options.expectedIndexerBinaryVersion,
35
+ });
36
+ }
37
+ else {
38
+ observedDaemonStatus = probe.status;
39
+ indexerSource = probe.status === null ? "none" : "probe";
40
+ daemonError = probeDecision.error;
41
+ }
42
+ if (daemonClient !== null) {
43
+ const lease = await dependencies.readSnapshotWithRetry(daemonClient, options.walletRootId);
44
+ daemonStatus = lease.status;
45
+ observedDaemonStatus = lease.status;
46
+ snapshot = {
47
+ tip: lease.payload.tip,
48
+ state: deserializeIndexerState(Buffer.from(lease.payload.stateBase64, "base64")),
49
+ source: "lease",
50
+ daemonInstanceId: lease.payload.daemonInstanceId,
51
+ snapshotSeq: lease.payload.snapshotSeq,
52
+ openedAtUnixMs: lease.payload.openedAtUnixMs,
53
+ };
54
+ indexerSource = "lease";
55
+ }
56
+ }
57
+ catch (error) {
58
+ daemonError = error instanceof Error ? error.message : String(error);
59
+ if (daemonError === INDEXER_DAEMON_BACKGROUND_FOLLOW_RECOVERY_FAILED) {
60
+ await daemonClient?.close().catch(() => undefined);
61
+ await options.nodeHandle?.stop().catch(() => undefined);
62
+ throw error;
63
+ }
64
+ if (observedDaemonStatus === null) {
65
+ observedDaemonStatus = await dependencies.readObservedIndexerDaemonStatus({
66
+ dataDir: options.dataDir,
67
+ walletRootId: options.walletRootId,
68
+ }).catch(() => null);
69
+ if (observedDaemonStatus !== null) {
70
+ indexerSource = "status-file";
71
+ }
72
+ }
73
+ }
74
+ const indexer = deriveManagedIndexerWalletStatus({
75
+ daemonStatus,
76
+ observedStatus: observedDaemonStatus,
77
+ snapshot,
78
+ source: indexerSource,
79
+ now: options.now,
80
+ startupError: daemonError,
81
+ });
82
+ return {
83
+ daemonClient,
84
+ indexer,
85
+ snapshot,
86
+ };
87
+ }
@@ -1,26 +1,11 @@
1
- import { loadBundledGenesisParameters } from "@cogcoin/indexer";
2
- import { attachOrStartIndexerDaemon, probeIndexerDaemon, readObservedIndexerDaemonStatus, readSnapshotWithRetry, type IndexerDaemonClient } from "../../bitcoind/indexer-daemon.js";
1
+ import { type IndexerDaemonClient } from "../../bitcoind/indexer-daemon.js";
3
2
  import type { ManagedWalletReadServiceBundle } from "../../bitcoind/managed-runtime/types.js";
4
3
  import { createRpcClient } from "../../bitcoind/node.js";
5
- import { attachOrStartManagedBitcoindService, probeManagedBitcoindService } from "../../bitcoind/service.js";
6
4
  import type { ManagedBitcoindNodeHandle } from "../../bitcoind/types.js";
7
- import { verifyManagedCoreWalletReplica } from "../lifecycle.js";
8
- import type { WalletBitcoindStatus, WalletLocalStateStatus, WalletNodeStatus, WalletServiceHealth } from "./types.js";
9
- type ManagedWalletReadServiceDeps = {
10
- loadBundledGenesisParameters: typeof loadBundledGenesisParameters;
11
- probeManagedBitcoindService: typeof probeManagedBitcoindService;
12
- attachOrStartManagedBitcoindService: typeof attachOrStartManagedBitcoindService;
13
- createRpcClient: typeof createRpcClient;
14
- verifyManagedCoreWalletReplica: typeof verifyManagedCoreWalletReplica;
15
- probeIndexerDaemon: typeof probeIndexerDaemon;
16
- attachOrStartIndexerDaemon: typeof attachOrStartIndexerDaemon;
17
- readSnapshotWithRetry: typeof readSnapshotWithRetry;
18
- readObservedIndexerDaemonStatus: typeof readObservedIndexerDaemonStatus;
19
- };
20
- export declare function deriveNodeHealthForTesting(status: WalletNodeStatus | null, bitcoindHealth: WalletBitcoindStatus["health"]): {
21
- health: WalletServiceHealth;
22
- message: string | null;
23
- };
5
+ import type { WalletLocalStateStatus } from "./types.js";
6
+ import { deriveNodeHealthForTesting, type ManagedWalletBitcoindReadDeps } from "./managed-bitcoind.js";
7
+ import { type ManagedWalletIndexerReadDeps } from "./managed-indexer.js";
8
+ type ManagedWalletReadServiceDeps = ManagedWalletBitcoindReadDeps & ManagedWalletIndexerReadDeps;
24
9
  export declare function openManagedWalletReadServiceBundle(options: {
25
10
  dataDir: string;
26
11
  databasePath: string;
@@ -30,4 +15,4 @@ export declare function openManagedWalletReadServiceBundle(options: {
30
15
  expectedIndexerBinaryVersion: string | null;
31
16
  now: number;
32
17
  }, dependencies?: ManagedWalletReadServiceDeps): Promise<ManagedWalletReadServiceBundle<ManagedBitcoindNodeHandle, ReturnType<typeof createRpcClient>, IndexerDaemonClient>>;
33
- export {};
18
+ export { deriveNodeHealthForTesting };
@@ -1,14 +1,10 @@
1
- import { deserializeIndexerState, loadBundledGenesisParameters } from "@cogcoin/indexer";
1
+ import { loadBundledGenesisParameters } from "@cogcoin/indexer";
2
2
  import { attachOrStartIndexerDaemon, INDEXER_DAEMON_BACKGROUND_FOLLOW_RECOVERY_FAILED, probeIndexerDaemon, readObservedIndexerDaemonStatus, readSnapshotWithRetry, } from "../../bitcoind/indexer-daemon.js";
3
- import { deriveManagedBitcoindWalletStatus, resolveManagedBitcoindProbeDecision, } from "../../bitcoind/managed-runtime/bitcoind-policy.js";
4
- import { deriveManagedIndexerWalletStatus, resolveIndexerDaemonProbeDecision, } from "../../bitcoind/managed-runtime/indexer-policy.js";
5
3
  import { createRpcClient } from "../../bitcoind/node.js";
6
- import { resolveCogcoinProcessingStartHeight } from "../../bitcoind/processing-start-height.js";
7
4
  import { attachOrStartManagedBitcoindService, probeManagedBitcoindService, } from "../../bitcoind/service.js";
8
5
  import { verifyManagedCoreWalletReplica } from "../lifecycle.js";
9
- const TOLERATED_NODE_HEADER_LEAD_BLOCKS = 2;
10
- 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.";
11
- const NODE_CATCHING_UP_MESSAGE = "Bitcoin Core is still catching up to headers.";
6
+ import { deriveNodeHealthForTesting, openManagedWalletBitcoindReadState, } from "./managed-bitcoind.js";
7
+ import { openManagedWalletIndexerReadState, } from "./managed-indexer.js";
12
8
  const defaultManagedWalletReadServiceDeps = {
13
9
  loadBundledGenesisParameters,
14
10
  probeManagedBitcoindService,
@@ -20,203 +16,34 @@ const defaultManagedWalletReadServiceDeps = {
20
16
  readSnapshotWithRetry,
21
17
  readObservedIndexerDaemonStatus,
22
18
  };
23
- function deriveNodeHealth(status, bitcoindHealth) {
24
- if (bitcoindHealth !== "ready" || status === null || !status.ready) {
25
- return {
26
- health: "catching-up",
27
- message: NODE_CATCHING_UP_MESSAGE,
28
- };
29
- }
30
- const headerLead = status.nodeBestHeight !== null && status.nodeHeaderHeight !== null
31
- ? status.nodeHeaderHeight - status.nodeBestHeight
32
- : null;
33
- if (headerLead !== null && headerLead > 0) {
34
- if (headerLead <= TOLERATED_NODE_HEADER_LEAD_BLOCKS) {
35
- return {
36
- health: "synced",
37
- message: TOLERATED_NODE_HEADER_LEAD_MESSAGE,
38
- };
39
- }
40
- return {
41
- health: "catching-up",
42
- message: NODE_CATCHING_UP_MESSAGE,
43
- };
44
- }
45
- return {
46
- health: "synced",
47
- message: null,
48
- };
49
- }
50
- export function deriveNodeHealthForTesting(status, bitcoindHealth) {
51
- return deriveNodeHealth(status, bitcoindHealth);
52
- }
53
- async function attachNodeStatus(options, dependencies) {
54
- try {
55
- const probe = await dependencies.probeManagedBitcoindService({
56
- dataDir: options.dataDir,
57
- chain: "main",
58
- startHeight: 0,
59
- walletRootId: options.walletRootId,
60
- startupTimeoutMs: options.startupTimeoutMs,
61
- });
62
- const decision = resolveManagedBitcoindProbeDecision(probe);
63
- if (decision.action === "reject") {
64
- return {
65
- handle: null,
66
- rpc: null,
67
- status: null,
68
- observedStatus: probe.status,
69
- error: decision.error,
70
- };
71
- }
72
- const genesis = await dependencies.loadBundledGenesisParameters();
73
- const handle = await dependencies.attachOrStartManagedBitcoindService({
74
- dataDir: options.dataDir,
75
- chain: "main",
76
- startHeight: resolveCogcoinProcessingStartHeight(genesis),
77
- walletRootId: options.walletRootId,
78
- startupTimeoutMs: options.startupTimeoutMs,
79
- });
80
- const rpc = dependencies.createRpcClient(handle.rpc);
81
- const [chainInfo, serviceStatus] = await Promise.all([
82
- rpc.getBlockchainInfo(),
83
- handle.refreshServiceStatus?.(),
84
- ]);
85
- const status = {
86
- ready: true,
87
- chain: chainInfo.chain,
88
- pid: handle.pid,
89
- walletRootId: handle.walletRootId ?? null,
90
- nodeBestHeight: chainInfo.blocks,
91
- nodeBestHashHex: chainInfo.bestblockhash,
92
- nodeHeaderHeight: chainInfo.headers,
93
- serviceUpdatedAtUnixMs: serviceStatus?.updatedAtUnixMs ?? null,
94
- serviceStatus: serviceStatus ?? null,
95
- walletReplica: serviceStatus?.walletReplica ?? null,
96
- walletReplicaMessage: serviceStatus?.walletReplica?.message ?? null,
97
- };
98
- return {
99
- handle,
100
- rpc,
101
- status,
102
- observedStatus: serviceStatus ?? null,
103
- error: null,
104
- };
105
- }
106
- catch (error) {
107
- return {
108
- handle: null,
109
- rpc: null,
110
- status: null,
111
- observedStatus: null,
112
- error: error instanceof Error ? error.message : String(error),
113
- };
114
- }
115
- }
116
19
  export async function openManagedWalletReadServiceBundle(options, dependencies = defaultManagedWalletReadServiceDeps) {
117
- const node = await attachNodeStatus({
20
+ const bitcoindState = await openManagedWalletBitcoindReadState({
118
21
  dataDir: options.dataDir,
119
22
  walletRootId: options.walletRootId,
23
+ localState: options.localState,
120
24
  startupTimeoutMs: options.startupTimeoutMs,
121
25
  }, dependencies);
122
- if (options.localState.state !== null && node.status !== null) {
123
- const verifiedReplica = await dependencies.verifyManagedCoreWalletReplica(options.localState.state, options.dataDir, {
124
- nodeHandle: node.handle ?? undefined,
125
- });
126
- node.status = {
127
- ...node.status,
128
- walletReplica: verifiedReplica,
129
- walletReplicaMessage: verifiedReplica.message ?? null,
130
- };
131
- }
132
- const bitcoind = deriveManagedBitcoindWalletStatus({
133
- status: node.observedStatus,
134
- nodeStatus: node.status,
135
- startupError: node.error,
136
- });
137
- const nodeDerived = deriveNodeHealth(node.status, bitcoind.health);
138
- let daemonClient = null;
139
- let daemonStatus = null;
140
- let observedDaemonStatus = null;
141
- let snapshot = null;
142
- let indexerSource = "none";
143
- let daemonError = null;
144
- try {
145
- const probe = await dependencies.probeIndexerDaemon({
146
- dataDir: options.dataDir,
147
- walletRootId: options.walletRootId,
148
- });
149
- const probeDecision = resolveIndexerDaemonProbeDecision({
150
- probe,
151
- expectedBinaryVersion: options.expectedIndexerBinaryVersion,
152
- });
153
- if (probeDecision.action !== "reject") {
154
- await probe.client?.close().catch(() => undefined);
155
- daemonClient = await dependencies.attachOrStartIndexerDaemon({
156
- dataDir: options.dataDir,
157
- databasePath: options.databasePath,
158
- walletRootId: options.walletRootId,
159
- startupTimeoutMs: options.startupTimeoutMs,
160
- ensureBackgroundFollow: true,
161
- expectedBinaryVersion: options.expectedIndexerBinaryVersion,
162
- });
163
- }
164
- else {
165
- observedDaemonStatus = probe.status;
166
- indexerSource = probe.status === null ? "none" : "probe";
167
- daemonError = probeDecision.error;
168
- }
169
- if (daemonClient !== null) {
170
- const lease = await dependencies.readSnapshotWithRetry(daemonClient, options.walletRootId);
171
- daemonStatus = lease.status;
172
- observedDaemonStatus = lease.status;
173
- snapshot = {
174
- tip: lease.payload.tip,
175
- state: deserializeIndexerState(Buffer.from(lease.payload.stateBase64, "base64")),
176
- source: "lease",
177
- daemonInstanceId: lease.payload.daemonInstanceId,
178
- snapshotSeq: lease.payload.snapshotSeq,
179
- openedAtUnixMs: lease.payload.openedAtUnixMs,
180
- };
181
- indexerSource = "lease";
182
- }
183
- }
184
- catch (error) {
185
- daemonError = error instanceof Error ? error.message : String(error);
186
- if (daemonError === INDEXER_DAEMON_BACKGROUND_FOLLOW_RECOVERY_FAILED) {
187
- await daemonClient?.close().catch(() => undefined);
188
- await node.handle?.stop().catch(() => undefined);
189
- throw error;
190
- }
191
- if (observedDaemonStatus === null) {
192
- observedDaemonStatus = await dependencies.readObservedIndexerDaemonStatus({
193
- dataDir: options.dataDir,
194
- walletRootId: options.walletRootId,
195
- }).catch(() => null);
196
- if (observedDaemonStatus !== null) {
197
- indexerSource = "status-file";
198
- }
199
- }
200
- }
201
- const indexer = deriveManagedIndexerWalletStatus({
202
- daemonStatus,
203
- observedStatus: observedDaemonStatus,
204
- snapshot,
205
- source: indexerSource,
26
+ const indexerState = await openManagedWalletIndexerReadState({
27
+ dataDir: options.dataDir,
28
+ databasePath: options.databasePath,
29
+ walletRootId: options.walletRootId,
30
+ startupTimeoutMs: options.startupTimeoutMs,
31
+ expectedIndexerBinaryVersion: options.expectedIndexerBinaryVersion,
206
32
  now: options.now,
207
- startupError: daemonError,
208
- });
33
+ nodeHandle: bitcoindState.node.handle,
34
+ }, dependencies);
209
35
  return {
210
- node,
211
- bitcoind,
212
- nodeHealth: nodeDerived.health,
213
- nodeMessage: nodeDerived.message,
214
- daemonClient,
215
- indexer,
216
- snapshot,
36
+ node: bitcoindState.node,
37
+ bitcoind: bitcoindState.bitcoind,
38
+ nodeHealth: bitcoindState.nodeHealth,
39
+ nodeMessage: bitcoindState.nodeMessage,
40
+ daemonClient: indexerState.daemonClient,
41
+ indexer: indexerState.indexer,
42
+ snapshot: indexerState.snapshot,
217
43
  async close() {
218
- await daemonClient?.close().catch(() => undefined);
219
- await node.handle?.stop().catch(() => undefined);
44
+ await indexerState.daemonClient?.close().catch(() => undefined);
45
+ await bitcoindState.node.handle?.stop().catch(() => undefined);
220
46
  },
221
47
  };
222
48
  }
49
+ export { deriveNodeHealthForTesting };
@@ -94,6 +94,7 @@ export interface WalletReadContext {
94
94
  indexer: WalletIndexerStatus;
95
95
  snapshot: WalletSnapshotView | null;
96
96
  model: WalletReadModel | null;
97
+ fundingDisplaySats: bigint | null;
97
98
  fundingSpendableSats: bigint | null;
98
99
  mining?: MiningControlPlaneView;
99
100
  close(): Promise<void>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cogcoin/client",
3
- "version": "1.1.9",
3
+ "version": "1.1.11",
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",