@cogcoin/client 1.1.5 → 1.1.6
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/indexer-daemon.d.ts +3 -7
- package/dist/bitcoind/indexer-daemon.js +43 -158
- package/dist/bitcoind/managed-runtime/bitcoind-policy.d.ts +16 -0
- package/dist/bitcoind/managed-runtime/bitcoind-policy.js +177 -0
- package/dist/bitcoind/managed-runtime/indexer-policy.d.ts +34 -0
- package/dist/bitcoind/managed-runtime/indexer-policy.js +200 -0
- package/dist/bitcoind/managed-runtime/status.d.ts +11 -0
- package/dist/bitcoind/managed-runtime/status.js +59 -0
- package/dist/bitcoind/managed-runtime/types.d.ts +37 -0
- package/dist/bitcoind/managed-runtime/types.js +1 -0
- package/dist/bitcoind/service.d.ts +2 -7
- package/dist/bitcoind/service.js +46 -94
- package/dist/wallet/lifecycle/access.d.ts +5 -0
- package/dist/wallet/lifecycle/access.js +79 -0
- package/dist/wallet/lifecycle/context.d.ts +26 -0
- package/dist/wallet/lifecycle/context.js +58 -0
- package/dist/wallet/lifecycle/managed-core.d.ts +1 -9
- package/dist/wallet/lifecycle/managed-core.js +3 -63
- package/dist/wallet/lifecycle/repair-bitcoind.d.ts +10 -0
- package/dist/wallet/lifecycle/repair-bitcoind.js +142 -0
- package/dist/wallet/lifecycle/repair-indexer.d.ts +8 -0
- package/dist/wallet/lifecycle/repair-indexer.js +117 -0
- package/dist/wallet/lifecycle/repair.d.ts +2 -4
- package/dist/wallet/lifecycle/repair.js +77 -318
- package/dist/wallet/lifecycle/setup-prompts.d.ts +7 -0
- package/dist/wallet/lifecycle/setup-prompts.js +88 -0
- package/dist/wallet/lifecycle/setup-state.d.ts +26 -0
- package/dist/wallet/lifecycle/setup-state.js +159 -0
- package/dist/wallet/lifecycle/setup.d.ts +3 -4
- package/dist/wallet/lifecycle/setup.js +45 -351
- package/dist/wallet/lifecycle/types.d.ts +33 -2
- package/dist/wallet/read/context.js +13 -188
- package/package.json +1 -1
|
@@ -107,7 +107,18 @@ export interface WalletLifecycleResolvedContext {
|
|
|
107
107
|
paths: WalletRuntimePaths;
|
|
108
108
|
nowUnixMs: number;
|
|
109
109
|
}
|
|
110
|
-
export interface
|
|
110
|
+
export interface WalletManagedCoreContext extends WalletLifecycleResolvedContext {
|
|
111
|
+
attachService: NonNullable<WalletManagedCoreDependencies["attachService"]>;
|
|
112
|
+
rpcFactory: NonNullable<WalletManagedCoreDependencies["rpcFactory"]>;
|
|
113
|
+
}
|
|
114
|
+
export interface WalletAccessContext extends WalletManagedCoreContext {
|
|
115
|
+
dataDir?: string;
|
|
116
|
+
}
|
|
117
|
+
export interface WalletLoadedState {
|
|
118
|
+
state: WalletStateV1;
|
|
119
|
+
source: "primary" | "backup";
|
|
120
|
+
}
|
|
121
|
+
export interface WalletSetupContext extends WalletManagedCoreContext {
|
|
111
122
|
dataDir: string;
|
|
112
123
|
prompter: WalletPrompter;
|
|
113
124
|
}
|
|
@@ -118,8 +129,28 @@ export interface WalletRepairDependencies extends WalletManagedCoreDependencies
|
|
|
118
129
|
requestMiningPreemption?: typeof requestMiningGenerationPreemption;
|
|
119
130
|
startBackgroundMining?: typeof startBackgroundMining;
|
|
120
131
|
}
|
|
121
|
-
export interface WalletRepairContext extends
|
|
132
|
+
export interface WalletRepairContext extends WalletManagedCoreContext {
|
|
122
133
|
dataDir: string;
|
|
123
134
|
databasePath: string;
|
|
124
135
|
assumeYes: boolean;
|
|
136
|
+
probeBitcoindService: NonNullable<WalletRepairDependencies["probeBitcoindService"]>;
|
|
137
|
+
attachIndexerDaemon: NonNullable<WalletRepairDependencies["attachIndexerDaemon"]>;
|
|
138
|
+
probeIndexerDaemon: NonNullable<WalletRepairDependencies["probeIndexerDaemon"]>;
|
|
139
|
+
requestMiningPreemption?: WalletRepairDependencies["requestMiningPreemption"];
|
|
140
|
+
startBackgroundMining?: WalletRepairDependencies["startBackgroundMining"];
|
|
141
|
+
}
|
|
142
|
+
export interface WalletBitcoindRepairStageResult {
|
|
143
|
+
state: WalletStateV1;
|
|
144
|
+
repairStateNeedsPersist: boolean;
|
|
145
|
+
recreatedManagedCoreWallet: boolean;
|
|
146
|
+
bitcoindServiceAction: WalletRepairResult["bitcoindServiceAction"];
|
|
147
|
+
bitcoindCompatibilityIssue: WalletRepairResult["bitcoindCompatibilityIssue"];
|
|
148
|
+
managedCoreReplicaAction: WalletRepairResult["managedCoreReplicaAction"];
|
|
149
|
+
bitcoindPostRepairHealth: WalletRepairResult["bitcoindPostRepairHealth"];
|
|
150
|
+
}
|
|
151
|
+
export interface WalletIndexerRepairStageResult {
|
|
152
|
+
resetIndexerDatabase: boolean;
|
|
153
|
+
indexerDaemonAction: WalletRepairResult["indexerDaemonAction"];
|
|
154
|
+
indexerCompatibilityIssue: WalletRepairResult["indexerCompatibilityIssue"];
|
|
155
|
+
indexerPostRepairHealth: WalletRepairResult["indexerPostRepairHealth"];
|
|
125
156
|
}
|
|
@@ -2,6 +2,8 @@ import { access, constants } from "node:fs/promises";
|
|
|
2
2
|
import { deserializeIndexerState, loadBundledGenesisParameters } from "@cogcoin/indexer";
|
|
3
3
|
import { readPackageVersionFromDisk } from "../../package-version.js";
|
|
4
4
|
import { attachOrStartIndexerDaemon, INDEXER_DAEMON_BACKGROUND_FOLLOW_RECOVERY_FAILED, probeIndexerDaemon, readObservedIndexerDaemonStatus, readSnapshotWithRetry, } from "../../bitcoind/indexer-daemon.js";
|
|
5
|
+
import { deriveManagedBitcoindWalletStatus, resolveManagedBitcoindProbeDecision, } from "../../bitcoind/managed-runtime/bitcoind-policy.js";
|
|
6
|
+
import { deriveManagedIndexerWalletStatus, resolveIndexerDaemonProbeDecision, } from "../../bitcoind/managed-runtime/indexer-policy.js";
|
|
5
7
|
import { createRpcClient } from "../../bitcoind/node.js";
|
|
6
8
|
import { UNINITIALIZED_WALLET_ROOT_ID } from "../../bitcoind/service-paths.js";
|
|
7
9
|
import { attachOrStartManagedBitcoindService, probeManagedBitcoindService, } from "../../bitcoind/service.js";
|
|
@@ -19,7 +21,6 @@ import { createDefaultWalletSecretProvider, createWalletSecretReference, inspect
|
|
|
19
21
|
import { describeClientPasswordLockedMessage, describeClientPasswordMigrationMessage, describeClientPasswordSetupMessage, } from "../state/client-password.js";
|
|
20
22
|
import { createWalletReadModel } from "./project.js";
|
|
21
23
|
const DEFAULT_SERVICE_START_TIMEOUT_MS = 10_000;
|
|
22
|
-
const STALE_HEARTBEAT_THRESHOLD_MS = 15_000;
|
|
23
24
|
const TOLERATED_NODE_HEADER_LEAD_BLOCKS = 2;
|
|
24
25
|
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
26
|
const NODE_CATCHING_UP_MESSAGE = "Bitcoin Core is still catching up to headers.";
|
|
@@ -222,135 +223,6 @@ async function inspectWalletLocalState(options = {}) {
|
|
|
222
223
|
};
|
|
223
224
|
}
|
|
224
225
|
}
|
|
225
|
-
function mapIndexerStartupError(message) {
|
|
226
|
-
switch (message) {
|
|
227
|
-
case "indexer_daemon_start_timeout":
|
|
228
|
-
return {
|
|
229
|
-
health: "starting",
|
|
230
|
-
message: "Indexer daemon is still starting.",
|
|
231
|
-
};
|
|
232
|
-
case "indexer_daemon_service_version_mismatch":
|
|
233
|
-
return {
|
|
234
|
-
health: "service-version-mismatch",
|
|
235
|
-
message: "The live indexer daemon is running an incompatible service API version.",
|
|
236
|
-
};
|
|
237
|
-
case "indexer_daemon_schema_mismatch":
|
|
238
|
-
return {
|
|
239
|
-
health: "schema-mismatch",
|
|
240
|
-
message: "The live indexer daemon is using an incompatible sqlite schema.",
|
|
241
|
-
};
|
|
242
|
-
case "indexer_daemon_wallet_root_mismatch":
|
|
243
|
-
return {
|
|
244
|
-
health: "wallet-root-mismatch",
|
|
245
|
-
message: "The live indexer daemon belongs to a different wallet root.",
|
|
246
|
-
};
|
|
247
|
-
case "indexer_daemon_protocol_error":
|
|
248
|
-
return {
|
|
249
|
-
health: "unavailable",
|
|
250
|
-
message: "The live indexer daemon socket responded with an invalid or incomplete protocol exchange.",
|
|
251
|
-
};
|
|
252
|
-
case INDEXER_DAEMON_BACKGROUND_FOLLOW_RECOVERY_FAILED:
|
|
253
|
-
return {
|
|
254
|
-
health: "failed",
|
|
255
|
-
message: "The managed indexer daemon could not recover automatic background follow.",
|
|
256
|
-
};
|
|
257
|
-
default:
|
|
258
|
-
return {
|
|
259
|
-
health: "unavailable",
|
|
260
|
-
message,
|
|
261
|
-
};
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
function mapBitcoindStartupError(message) {
|
|
265
|
-
switch (message) {
|
|
266
|
-
case "managed_bitcoind_service_start_timeout":
|
|
267
|
-
return {
|
|
268
|
-
health: "starting",
|
|
269
|
-
status: null,
|
|
270
|
-
message: "Managed bitcoind service is still starting.",
|
|
271
|
-
};
|
|
272
|
-
case "managed_bitcoind_service_version_mismatch":
|
|
273
|
-
return {
|
|
274
|
-
health: "service-version-mismatch",
|
|
275
|
-
status: null,
|
|
276
|
-
message: "The live managed bitcoind service is running an incompatible service version.",
|
|
277
|
-
};
|
|
278
|
-
case "managed_bitcoind_wallet_root_mismatch":
|
|
279
|
-
return {
|
|
280
|
-
health: "wallet-root-mismatch",
|
|
281
|
-
status: null,
|
|
282
|
-
message: "The live managed bitcoind service belongs to a different wallet root.",
|
|
283
|
-
};
|
|
284
|
-
case "managed_bitcoind_runtime_mismatch":
|
|
285
|
-
return {
|
|
286
|
-
health: "runtime-mismatch",
|
|
287
|
-
status: null,
|
|
288
|
-
message: "The live managed bitcoind service runtime does not match this wallet's expected data directory or chain.",
|
|
289
|
-
};
|
|
290
|
-
case "managed_bitcoind_protocol_error":
|
|
291
|
-
return {
|
|
292
|
-
health: "unavailable",
|
|
293
|
-
status: null,
|
|
294
|
-
message: "The managed bitcoind runtime artifacts are invalid or incomplete.",
|
|
295
|
-
};
|
|
296
|
-
default:
|
|
297
|
-
return {
|
|
298
|
-
health: "unavailable",
|
|
299
|
-
status: null,
|
|
300
|
-
message,
|
|
301
|
-
};
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
function deriveBitcoindHealth(options) {
|
|
305
|
-
if (options.startupError !== null) {
|
|
306
|
-
const mapped = mapBitcoindStartupError(options.startupError);
|
|
307
|
-
return {
|
|
308
|
-
...mapped,
|
|
309
|
-
status: options.status,
|
|
310
|
-
};
|
|
311
|
-
}
|
|
312
|
-
if (options.status === null) {
|
|
313
|
-
return {
|
|
314
|
-
health: "unavailable",
|
|
315
|
-
status: null,
|
|
316
|
-
message: "Managed bitcoind service is unavailable.",
|
|
317
|
-
};
|
|
318
|
-
}
|
|
319
|
-
if (options.status.state === "starting") {
|
|
320
|
-
return {
|
|
321
|
-
health: "starting",
|
|
322
|
-
status: options.status,
|
|
323
|
-
message: options.status.lastError ?? "Managed bitcoind service is still starting.",
|
|
324
|
-
};
|
|
325
|
-
}
|
|
326
|
-
if (options.status.state === "failed") {
|
|
327
|
-
return {
|
|
328
|
-
health: "failed",
|
|
329
|
-
status: options.status,
|
|
330
|
-
message: options.status.lastError ?? "Managed bitcoind service refresh failed.",
|
|
331
|
-
};
|
|
332
|
-
}
|
|
333
|
-
const proofStatus = options.nodeStatus?.walletReplica?.proofStatus;
|
|
334
|
-
if (proofStatus === "missing") {
|
|
335
|
-
return {
|
|
336
|
-
health: "replica-missing",
|
|
337
|
-
status: options.status,
|
|
338
|
-
message: options.nodeStatus?.walletReplicaMessage ?? "Managed Core wallet replica is missing.",
|
|
339
|
-
};
|
|
340
|
-
}
|
|
341
|
-
if (proofStatus === "mismatch") {
|
|
342
|
-
return {
|
|
343
|
-
health: "replica-mismatch",
|
|
344
|
-
status: options.status,
|
|
345
|
-
message: options.nodeStatus?.walletReplicaMessage ?? "Managed Core wallet replica does not match trusted wallet state.",
|
|
346
|
-
};
|
|
347
|
-
}
|
|
348
|
-
return {
|
|
349
|
-
health: "ready",
|
|
350
|
-
status: options.status,
|
|
351
|
-
message: options.nodeStatus?.walletReplicaMessage ?? options.status.lastError,
|
|
352
|
-
};
|
|
353
|
-
}
|
|
354
226
|
function deriveNodeHealth(status, bitcoindHealth) {
|
|
355
227
|
if (bitcoindHealth !== "ready" || status === null || !status.ready) {
|
|
356
228
|
return {
|
|
@@ -381,58 +253,6 @@ function deriveNodeHealth(status, bitcoindHealth) {
|
|
|
381
253
|
export function deriveNodeHealthForTesting(status, bitcoindHealth) {
|
|
382
254
|
return deriveNodeHealth(status, bitcoindHealth);
|
|
383
255
|
}
|
|
384
|
-
function deriveIndexerHealth(options) {
|
|
385
|
-
const daemonStatus = options.source === "lease"
|
|
386
|
-
? options.daemonStatus
|
|
387
|
-
: options.observedStatus ?? options.daemonStatus;
|
|
388
|
-
const snapshotTip = options.snapshot?.tip ?? null;
|
|
389
|
-
const daemonInstanceId = options.snapshot?.daemonInstanceId ?? daemonStatus?.daemonInstanceId ?? null;
|
|
390
|
-
const snapshotSeq = options.snapshot?.snapshotSeq ?? daemonStatus?.snapshotSeq ?? null;
|
|
391
|
-
const openedAtUnixMs = options.snapshot?.openedAtUnixMs ?? null;
|
|
392
|
-
const source = daemonStatus === null && options.snapshot === null ? "none" : options.source;
|
|
393
|
-
const createResult = (health, message) => ({
|
|
394
|
-
health,
|
|
395
|
-
status: daemonStatus,
|
|
396
|
-
message,
|
|
397
|
-
snapshotTip,
|
|
398
|
-
source,
|
|
399
|
-
daemonInstanceId,
|
|
400
|
-
snapshotSeq,
|
|
401
|
-
openedAtUnixMs,
|
|
402
|
-
});
|
|
403
|
-
if (options.startupError !== null) {
|
|
404
|
-
const mapped = mapIndexerStartupError(options.startupError);
|
|
405
|
-
return createResult(mapped.health, mapped.message);
|
|
406
|
-
}
|
|
407
|
-
if (daemonStatus === null) {
|
|
408
|
-
return createResult("unavailable", "Indexer daemon is unavailable.");
|
|
409
|
-
}
|
|
410
|
-
if ((options.now - daemonStatus.heartbeatAtUnixMs) > STALE_HEARTBEAT_THRESHOLD_MS) {
|
|
411
|
-
return createResult("stale-heartbeat", "Indexer daemon heartbeat is stale.");
|
|
412
|
-
}
|
|
413
|
-
if (daemonStatus.state === "schema-mismatch") {
|
|
414
|
-
return createResult("schema-mismatch", daemonStatus.lastError ?? "Indexer daemon sqlite schema is incompatible.");
|
|
415
|
-
}
|
|
416
|
-
if (daemonStatus.state === "failed") {
|
|
417
|
-
return createResult("failed", daemonStatus.lastError ?? "Indexer daemon refresh failed.");
|
|
418
|
-
}
|
|
419
|
-
if (daemonStatus.state === "service-version-mismatch") {
|
|
420
|
-
return createResult("service-version-mismatch", "Indexer daemon service API is incompatible.");
|
|
421
|
-
}
|
|
422
|
-
if (options.snapshot === null) {
|
|
423
|
-
if (daemonStatus.state === "reorging") {
|
|
424
|
-
return createResult("reorging", "Indexer daemon is replaying a reorg and refreshing the coherent snapshot.");
|
|
425
|
-
}
|
|
426
|
-
return createResult(daemonStatus.state === "catching-up" ? "catching-up" : "starting", "Indexer snapshot is not ready yet.");
|
|
427
|
-
}
|
|
428
|
-
if (daemonStatus.state === "catching-up") {
|
|
429
|
-
return createResult("catching-up", "Indexer daemon is still catching up to the managed Bitcoin tip.");
|
|
430
|
-
}
|
|
431
|
-
if (daemonStatus.state === "reorging") {
|
|
432
|
-
return createResult("reorging", "Indexer daemon is replaying a reorg and refreshing the coherent snapshot.");
|
|
433
|
-
}
|
|
434
|
-
return createResult("synced", null);
|
|
435
|
-
}
|
|
436
256
|
async function attachNodeStatus(options) {
|
|
437
257
|
try {
|
|
438
258
|
const probe = await probeManagedBitcoindService({
|
|
@@ -442,13 +262,14 @@ async function attachNodeStatus(options) {
|
|
|
442
262
|
walletRootId: options.walletRootId,
|
|
443
263
|
startupTimeoutMs: options.startupTimeoutMs,
|
|
444
264
|
});
|
|
445
|
-
|
|
265
|
+
const decision = resolveManagedBitcoindProbeDecision(probe);
|
|
266
|
+
if (decision.action === "reject") {
|
|
446
267
|
return {
|
|
447
268
|
handle: null,
|
|
448
269
|
rpc: null,
|
|
449
270
|
status: null,
|
|
450
271
|
observedStatus: probe.status,
|
|
451
|
-
error:
|
|
272
|
+
error: decision.error,
|
|
452
273
|
};
|
|
453
274
|
}
|
|
454
275
|
const genesis = await loadBundledGenesisParameters();
|
|
@@ -539,7 +360,7 @@ export async function openWalletReadContext(options) {
|
|
|
539
360
|
walletReplicaMessage: verifiedReplica.message ?? null,
|
|
540
361
|
};
|
|
541
362
|
}
|
|
542
|
-
const bitcoind =
|
|
363
|
+
const bitcoind = deriveManagedBitcoindWalletStatus({
|
|
543
364
|
status: node.observedStatus,
|
|
544
365
|
nodeStatus: node.status,
|
|
545
366
|
startupError: node.error,
|
|
@@ -556,7 +377,11 @@ export async function openWalletReadContext(options) {
|
|
|
556
377
|
dataDir: options.dataDir,
|
|
557
378
|
walletRootId,
|
|
558
379
|
});
|
|
559
|
-
|
|
380
|
+
const probeDecision = resolveIndexerDaemonProbeDecision({
|
|
381
|
+
probe,
|
|
382
|
+
expectedBinaryVersion: expectedIndexerBinaryVersion,
|
|
383
|
+
});
|
|
384
|
+
if (probeDecision.action !== "reject") {
|
|
560
385
|
await probe.client?.close().catch(() => undefined);
|
|
561
386
|
daemonClient = await attachOrStartIndexerDaemon({
|
|
562
387
|
dataDir: options.dataDir,
|
|
@@ -570,7 +395,7 @@ export async function openWalletReadContext(options) {
|
|
|
570
395
|
else {
|
|
571
396
|
observedDaemonStatus = probe.status;
|
|
572
397
|
indexerSource = probe.status === null ? "none" : "probe";
|
|
573
|
-
daemonError =
|
|
398
|
+
daemonError = probeDecision.error;
|
|
574
399
|
}
|
|
575
400
|
if (daemonClient !== null) {
|
|
576
401
|
const lease = await readSnapshotWithRetry(daemonClient, walletRootId);
|
|
@@ -604,7 +429,7 @@ export async function openWalletReadContext(options) {
|
|
|
604
429
|
}
|
|
605
430
|
}
|
|
606
431
|
}
|
|
607
|
-
const indexer =
|
|
432
|
+
const indexer = deriveManagedIndexerWalletStatus({
|
|
608
433
|
daemonStatus,
|
|
609
434
|
observedStatus: observedDaemonStatus,
|
|
610
435
|
snapshot,
|
package/package.json
CHANGED