@cogcoin/client 0.5.6 → 0.5.8
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 +11 -2
- package/dist/bitcoind/bootstrap/getblock-archive.d.ts +39 -0
- package/dist/bitcoind/bootstrap/getblock-archive.js +548 -0
- package/dist/bitcoind/bootstrap.d.ts +1 -0
- package/dist/bitcoind/bootstrap.js +1 -0
- package/dist/bitcoind/client/factory.js +92 -30
- package/dist/bitcoind/client/managed-client.d.ts +1 -1
- package/dist/bitcoind/client/managed-client.js +22 -2
- package/dist/bitcoind/client/sync-engine.js +7 -0
- package/dist/bitcoind/errors.js +18 -0
- package/dist/bitcoind/indexer-daemon-main.js +78 -0
- package/dist/bitcoind/indexer-daemon.d.ts +3 -1
- package/dist/bitcoind/indexer-daemon.js +13 -6
- package/dist/bitcoind/node.js +2 -0
- package/dist/bitcoind/progress/constants.d.ts +1 -0
- package/dist/bitcoind/progress/constants.js +1 -0
- package/dist/bitcoind/progress/controller.d.ts +22 -0
- package/dist/bitcoind/progress/controller.js +48 -23
- package/dist/bitcoind/progress/formatting.js +25 -0
- package/dist/bitcoind/progress/render-policy.d.ts +35 -0
- package/dist/bitcoind/progress/render-policy.js +81 -0
- package/dist/bitcoind/service-paths.js +2 -6
- package/dist/bitcoind/service.d.ts +5 -1
- package/dist/bitcoind/service.js +92 -54
- package/dist/bitcoind/testing.d.ts +2 -1
- package/dist/bitcoind/testing.js +2 -1
- package/dist/bitcoind/types.d.ts +35 -1
- package/dist/cli/commands/follow.js +2 -0
- package/dist/cli/commands/getblock-archive-restart.d.ts +5 -0
- package/dist/cli/commands/getblock-archive-restart.js +15 -0
- package/dist/cli/commands/mining-admin.js +4 -0
- package/dist/cli/commands/mining-read.js +8 -5
- package/dist/cli/commands/mining-runtime.js +4 -0
- package/dist/cli/commands/status.js +2 -0
- package/dist/cli/commands/sync.js +2 -0
- package/dist/cli/commands/wallet-admin.js +29 -3
- package/dist/cli/commands/wallet-mutation.js +57 -4
- package/dist/cli/commands/wallet-read.js +2 -0
- package/dist/cli/context.js +5 -3
- package/dist/cli/mutation-command-groups.d.ts +2 -1
- package/dist/cli/mutation-command-groups.js +5 -0
- package/dist/cli/mutation-json.d.ts +18 -2
- package/dist/cli/mutation-json.js +47 -0
- package/dist/cli/mutation-success.d.ts +1 -0
- package/dist/cli/mutation-success.js +2 -2
- package/dist/cli/output.js +97 -1
- package/dist/cli/parse.d.ts +1 -1
- package/dist/cli/parse.js +127 -3
- package/dist/cli/preview-json.d.ts +10 -1
- package/dist/cli/preview-json.js +30 -0
- package/dist/cli/prompt.js +1 -1
- package/dist/cli/runner.js +20 -0
- package/dist/cli/signals.js +1 -1
- package/dist/cli/types.d.ts +11 -4
- package/dist/cli/wallet-format.js +6 -0
- package/dist/wallet/lifecycle.d.ts +18 -1
- package/dist/wallet/lifecycle.js +170 -81
- package/dist/wallet/mining/visualizer.d.ts +11 -6
- package/dist/wallet/mining/visualizer.js +32 -15
- package/dist/wallet/reset.js +39 -27
- package/dist/wallet/runtime.d.ts +12 -1
- package/dist/wallet/runtime.js +53 -11
- package/dist/wallet/state/provider.d.ts +1 -0
- package/dist/wallet/state/provider.js +119 -3
- package/dist/wallet/state/seed-index.d.ts +43 -0
- package/dist/wallet/state/seed-index.js +151 -0
- package/dist/wallet/tx/anchor.d.ts +22 -0
- package/dist/wallet/tx/anchor.js +201 -8
- package/dist/wallet/tx/index.d.ts +1 -1
- package/dist/wallet/tx/index.js +1 -1
- package/package.json +1 -1
package/dist/cli/runner.js
CHANGED
|
@@ -12,6 +12,14 @@ import { runSyncCommand } from "./commands/sync.js";
|
|
|
12
12
|
import { runWalletAdminCommand } from "./commands/wallet-admin.js";
|
|
13
13
|
import { runWalletMutationCommand } from "./commands/wallet-mutation.js";
|
|
14
14
|
import { runWalletReadCommand } from "./commands/wallet-read.js";
|
|
15
|
+
import { findWalletSeedRecord, loadWalletSeedIndex } from "../wallet/state/seed-index.js";
|
|
16
|
+
function commandUsesExistingWalletSeed(parsed) {
|
|
17
|
+
return parsed.seedName !== null
|
|
18
|
+
&& parsed.seedName !== "main"
|
|
19
|
+
&& parsed.command !== "restore"
|
|
20
|
+
&& parsed.command !== "wallet-delete"
|
|
21
|
+
&& parsed.command !== "wallet-restore";
|
|
22
|
+
}
|
|
15
23
|
export async function runCli(argv, contextOverrides = {}) {
|
|
16
24
|
const context = createDefaultContext(contextOverrides);
|
|
17
25
|
let parsed;
|
|
@@ -41,6 +49,15 @@ export async function runCli(argv, contextOverrides = {}) {
|
|
|
41
49
|
return parsed.help ? 0 : 2;
|
|
42
50
|
}
|
|
43
51
|
try {
|
|
52
|
+
if (commandUsesExistingWalletSeed(parsed)) {
|
|
53
|
+
const mainPaths = context.resolveWalletRuntimePaths("main");
|
|
54
|
+
const seedIndex = await loadWalletSeedIndex({
|
|
55
|
+
paths: mainPaths,
|
|
56
|
+
});
|
|
57
|
+
if (seedIndex.seeds.length > 0 && findWalletSeedRecord(seedIndex, parsed.seedName) === null) {
|
|
58
|
+
throw new Error("wallet_seed_not_found");
|
|
59
|
+
}
|
|
60
|
+
}
|
|
44
61
|
if (parsed.command === "sync") {
|
|
45
62
|
return runSyncCommand(parsed, context);
|
|
46
63
|
}
|
|
@@ -76,6 +93,7 @@ export async function runCli(argv, contextOverrides = {}) {
|
|
|
76
93
|
|| parsed.command === "wallet-export"
|
|
77
94
|
|| parsed.command === "wallet-import"
|
|
78
95
|
|| parsed.command === "wallet-init"
|
|
96
|
+
|| parsed.command === "wallet-delete"
|
|
79
97
|
|| parsed.command === "wallet-restore"
|
|
80
98
|
|| parsed.command === "wallet-show-mnemonic"
|
|
81
99
|
|| parsed.command === "wallet-unlock"
|
|
@@ -83,7 +101,9 @@ export async function runCli(argv, contextOverrides = {}) {
|
|
|
83
101
|
return runWalletAdminCommand(parsed, context);
|
|
84
102
|
}
|
|
85
103
|
if (parsed.command === "anchor"
|
|
104
|
+
|| parsed.command === "anchor-clear"
|
|
86
105
|
|| parsed.command === "domain-anchor"
|
|
106
|
+
|| parsed.command === "domain-anchor-clear"
|
|
87
107
|
|| parsed.command === "register"
|
|
88
108
|
|| parsed.command === "domain-register"
|
|
89
109
|
|| parsed.command === "transfer"
|
package/dist/cli/signals.js
CHANGED
|
@@ -19,7 +19,7 @@ export function createStopSignalWatcher(signalSource, stderr, client, forceExit)
|
|
|
19
19
|
};
|
|
20
20
|
const onFirstSignal = () => {
|
|
21
21
|
closing = true;
|
|
22
|
-
writeLine(stderr, "
|
|
22
|
+
writeLine(stderr, "Detaching from managed Cogcoin client...");
|
|
23
23
|
void client.close().then(() => {
|
|
24
24
|
settle(0);
|
|
25
25
|
}, () => {
|
package/dist/cli/types.d.ts
CHANGED
|
@@ -5,17 +5,17 @@ import { attachOrStartManagedBitcoindService, probeManagedBitcoindService, stopM
|
|
|
5
5
|
import { openSqliteStore } from "../sqlite/index.js";
|
|
6
6
|
import type { ClientStoreAdapter } from "../types.js";
|
|
7
7
|
import type { WalletRuntimePaths } from "../wallet/runtime.js";
|
|
8
|
-
import type { exportWallet, WalletPrompter, importWallet, initializeWallet, lockWallet, previewResetWallet, repairWallet, resetWallet, restoreWalletFromMnemonic, showWalletMnemonic, unlockWallet } from "../wallet/lifecycle.js";
|
|
8
|
+
import type { exportWallet, WalletPrompter, importWallet, initializeWallet, deleteImportedWalletSeed, lockWallet, previewResetWallet, repairWallet, resetWallet, restoreWalletFromMnemonic, showWalletMnemonic, unlockWallet } from "../wallet/lifecycle.js";
|
|
9
9
|
import type { openWalletReadContext } from "../wallet/read/index.js";
|
|
10
10
|
import { loadWalletExplicitLock } from "../wallet/state/explicit-lock.js";
|
|
11
11
|
import { loadUnlockSession } from "../wallet/state/session.js";
|
|
12
12
|
import { loadRawWalletStateEnvelope, loadWalletState } from "../wallet/state/storage.js";
|
|
13
13
|
import type { WalletSecretProvider } from "../wallet/state/provider.js";
|
|
14
14
|
import type { disableMiningHooks, enableMiningHooks, followMiningLog, inspectMiningControlPlane, readMiningLog, runForegroundMining, setupBuiltInMining, startBackgroundMining, stopBackgroundMining } from "../wallet/mining/index.js";
|
|
15
|
-
import type { anchorDomain, buyDomain, claimCogLock, clearDomainDelegate, clearDomainEndpoint, clearDomainMiner, clearField, createField, giveReputation, lockCogToDomain, registerDomain, reclaimCogLock, revokeReputation, sendCog, setField, setDomainCanonical, setDomainDelegate, setDomainEndpoint, setDomainMiner, sellDomain, transferDomain } from "../wallet/tx/index.js";
|
|
15
|
+
import type { anchorDomain, clearPendingAnchor, buyDomain, claimCogLock, clearDomainDelegate, clearDomainEndpoint, clearDomainMiner, clearField, createField, giveReputation, lockCogToDomain, registerDomain, reclaimCogLock, revokeReputation, sendCog, setField, setDomainCanonical, setDomainDelegate, setDomainEndpoint, setDomainMiner, sellDomain, transferDomain } from "../wallet/tx/index.js";
|
|
16
16
|
export type ProgressOutput = "auto" | "tty" | "none";
|
|
17
17
|
export type OutputMode = "text" | "json" | "preview-json";
|
|
18
|
-
export type CommandName = "init" | "restore" | "reset" | "repair" | "sync" | "status" | "follow" | "bitcoin-start" | "bitcoin-stop" | "bitcoin-status" | "indexer-start" | "indexer-stop" | "indexer-status" | "unlock" | "anchor" | "domain-anchor" | "register" | "domain-register" | "transfer" | "domain-transfer" | "sell" | "domain-sell" | "unsell" | "domain-unsell" | "buy" | "domain-buy" | "domain-endpoint-set" | "domain-endpoint-clear" | "domain-delegate-set" | "domain-delegate-clear" | "domain-miner-set" | "domain-miner-clear" | "domain-canonical" | "field-list" | "field-show" | "field-create" | "field-set" | "field-clear" | "send" | "claim" | "reclaim" | "cog-send" | "cog-claim" | "cog-reclaim" | "cog-lock" | "rep-give" | "rep-revoke" | "cog-balance" | "cog-locks" | "hooks-mining-enable" | "hooks-mining-disable" | "hooks-mining-status" | "mine" | "mine-start" | "mine-stop" | "mine-setup" | "mine-status" | "mine-log" | "wallet-export" | "wallet-import" | "wallet-init" | "wallet-restore" | "wallet-show-mnemonic" | "wallet-lock" | "wallet-unlock" | "wallet-status" | "wallet-address" | "wallet-ids" | "address" | "ids" | "balance" | "locks" | "domain-list" | "domains" | "domain-show" | "show" | "fields" | "field";
|
|
18
|
+
export type CommandName = "init" | "restore" | "reset" | "repair" | "sync" | "status" | "follow" | "bitcoin-start" | "bitcoin-stop" | "bitcoin-status" | "indexer-start" | "indexer-stop" | "indexer-status" | "unlock" | "anchor" | "anchor-clear" | "domain-anchor" | "domain-anchor-clear" | "register" | "domain-register" | "transfer" | "domain-transfer" | "sell" | "domain-sell" | "unsell" | "domain-unsell" | "buy" | "domain-buy" | "domain-endpoint-set" | "domain-endpoint-clear" | "domain-delegate-set" | "domain-delegate-clear" | "domain-miner-set" | "domain-miner-clear" | "domain-canonical" | "field-list" | "field-show" | "field-create" | "field-set" | "field-clear" | "send" | "claim" | "reclaim" | "cog-send" | "cog-claim" | "cog-reclaim" | "cog-lock" | "rep-give" | "rep-revoke" | "cog-balance" | "cog-locks" | "hooks-mining-enable" | "hooks-mining-disable" | "hooks-mining-status" | "mine" | "mine-start" | "mine-stop" | "mine-setup" | "mine-status" | "mine-log" | "wallet-export" | "wallet-import" | "wallet-init" | "wallet-delete" | "wallet-restore" | "wallet-show-mnemonic" | "wallet-lock" | "wallet-unlock" | "wallet-status" | "wallet-address" | "wallet-ids" | "address" | "ids" | "balance" | "locks" | "domain-list" | "domains" | "domain-show" | "show" | "fields" | "field";
|
|
19
19
|
export interface WritableLike {
|
|
20
20
|
isTTY?: boolean;
|
|
21
21
|
write(chunk: string): void;
|
|
@@ -36,6 +36,7 @@ export interface ParsedCliArgs {
|
|
|
36
36
|
dbPath: string | null;
|
|
37
37
|
dataDir: string | null;
|
|
38
38
|
progressOutput: ProgressOutput;
|
|
39
|
+
seedName: string | null;
|
|
39
40
|
unlockFor: string | null;
|
|
40
41
|
assumeYes: boolean;
|
|
41
42
|
forceRace: boolean;
|
|
@@ -95,6 +96,10 @@ export interface CliRunnerContext {
|
|
|
95
96
|
dataDir?: string;
|
|
96
97
|
walletRootId?: string;
|
|
97
98
|
progressOutput?: ProgressOutput;
|
|
99
|
+
confirmGetblockArchiveRestart?: (options: {
|
|
100
|
+
currentArchiveEndHeight: number | null;
|
|
101
|
+
nextArchiveEndHeight: number;
|
|
102
|
+
}) => Promise<boolean>;
|
|
98
103
|
}) => Promise<ManagedClientLike>;
|
|
99
104
|
attachManagedBitcoindService?: typeof attachOrStartManagedBitcoindService;
|
|
100
105
|
probeManagedBitcoindService?: typeof probeManagedBitcoindService;
|
|
@@ -115,11 +120,13 @@ export interface CliRunnerContext {
|
|
|
115
120
|
previewResetWallet?: typeof previewResetWallet;
|
|
116
121
|
exportWallet?: typeof exportWallet;
|
|
117
122
|
importWallet?: typeof importWallet;
|
|
123
|
+
deleteImportedWalletSeed?: typeof deleteImportedWalletSeed;
|
|
118
124
|
showWalletMnemonic?: typeof showWalletMnemonic;
|
|
119
125
|
unlockWallet?: typeof unlockWallet;
|
|
120
126
|
lockWallet?: typeof lockWallet;
|
|
121
127
|
registerDomain?: typeof registerDomain;
|
|
122
128
|
anchorDomain?: typeof anchorDomain;
|
|
129
|
+
clearPendingAnchor?: typeof clearPendingAnchor;
|
|
123
130
|
transferDomain?: typeof transferDomain;
|
|
124
131
|
sellDomain?: typeof sellDomain;
|
|
125
132
|
buyDomain?: typeof buyDomain;
|
|
@@ -156,7 +163,7 @@ export interface CliRunnerContext {
|
|
|
156
163
|
readPackageVersion?: () => Promise<string>;
|
|
157
164
|
resolveDefaultBitcoindDataDir?: () => string;
|
|
158
165
|
resolveDefaultClientDatabasePath?: () => string;
|
|
159
|
-
resolveWalletRuntimePaths?: () => WalletRuntimePaths;
|
|
166
|
+
resolveWalletRuntimePaths?: (seedName?: string | null) => WalletRuntimePaths;
|
|
160
167
|
}
|
|
161
168
|
export interface StopSignalWatcher {
|
|
162
169
|
cleanup(): void;
|
|
@@ -104,6 +104,12 @@ export function getRepairRecommendation(context) {
|
|
|
104
104
|
return null;
|
|
105
105
|
}
|
|
106
106
|
export function getMutationRecommendation(context) {
|
|
107
|
+
const clearableAnchorFamily = (context.localState.state?.proactiveFamilies ?? []).find((family) => family.type === "anchor"
|
|
108
|
+
&& family.status === "draft"
|
|
109
|
+
&& family.currentStep === "reserved");
|
|
110
|
+
if (clearableAnchorFamily?.domainName != null) {
|
|
111
|
+
return `Run \`cogcoin anchor clear ${clearableAnchorFamily.domainName}\` to cancel the local pending anchor reservation, or rerun \`cogcoin anchor ${clearableAnchorFamily.domainName}\` to continue anchoring.`;
|
|
112
|
+
}
|
|
107
113
|
const unresolvedFamily = (context.localState.state?.proactiveFamilies ?? []).find((family) => (family.type === "anchor" || family.type === "field")
|
|
108
114
|
&& (family.status === "broadcast-unknown" || family.status === "repair-required"));
|
|
109
115
|
if (unresolvedFamily !== undefined) {
|
|
@@ -12,7 +12,7 @@ export interface WalletPrompter {
|
|
|
12
12
|
writeLine(message: string): void;
|
|
13
13
|
prompt(message: string): Promise<string>;
|
|
14
14
|
promptHidden?(message: string): Promise<string>;
|
|
15
|
-
clearSensitiveDisplay?(scope: "mnemonic-reveal"): void | Promise<void>;
|
|
15
|
+
clearSensitiveDisplay?(scope: "mnemonic-reveal" | "restore-mnemonic-entry"): void | Promise<void>;
|
|
16
16
|
}
|
|
17
17
|
export interface WalletInitializationResult {
|
|
18
18
|
walletRootId: string;
|
|
@@ -42,12 +42,18 @@ export interface WalletImportResult {
|
|
|
42
42
|
state: WalletStateV1;
|
|
43
43
|
}
|
|
44
44
|
export interface WalletRestoreResult {
|
|
45
|
+
seedName?: string | null;
|
|
45
46
|
walletRootId: string;
|
|
46
47
|
fundingAddress: string;
|
|
47
48
|
unlockUntilUnixMs: number;
|
|
48
49
|
state: WalletStateV1;
|
|
49
50
|
warnings?: string[];
|
|
50
51
|
}
|
|
52
|
+
export interface WalletDeleteResult {
|
|
53
|
+
seedName: string;
|
|
54
|
+
walletRootId: string;
|
|
55
|
+
deleted: boolean;
|
|
56
|
+
}
|
|
51
57
|
export interface WalletRepairResult {
|
|
52
58
|
walletRootId: string;
|
|
53
59
|
recoveredFromBackup: boolean;
|
|
@@ -214,6 +220,17 @@ export declare function restoreWalletFromMnemonic(options: {
|
|
|
214
220
|
attachService?: typeof attachOrStartManagedBitcoindService;
|
|
215
221
|
rpcFactory?: (config: Parameters<typeof createRpcClient>[0]) => WalletLifecycleRpcClient;
|
|
216
222
|
}): Promise<WalletRestoreResult>;
|
|
223
|
+
export declare function deleteImportedWalletSeed(options: {
|
|
224
|
+
dataDir: string;
|
|
225
|
+
provider?: WalletSecretProvider;
|
|
226
|
+
prompter: WalletPrompter;
|
|
227
|
+
assumeYes?: boolean;
|
|
228
|
+
nowUnixMs?: number;
|
|
229
|
+
paths?: WalletRuntimePaths;
|
|
230
|
+
attachService?: typeof attachOrStartManagedBitcoindService;
|
|
231
|
+
probeBitcoindService?: typeof probeManagedBitcoindService;
|
|
232
|
+
rpcFactory?: (config: Parameters<typeof createRpcClient>[0]) => WalletLifecycleRpcClient;
|
|
233
|
+
}): Promise<WalletDeleteResult>;
|
|
217
234
|
export declare function repairWallet(options: {
|
|
218
235
|
dataDir: string;
|
|
219
236
|
databasePath: string;
|
package/dist/wallet/lifecycle.js
CHANGED
|
@@ -11,7 +11,7 @@ import { readPortableWalletArchive, writePortableWalletArchive } from "./archive
|
|
|
11
11
|
import { normalizeWalletDescriptorState, persistNormalizedWalletDescriptorStateIfNeeded, persistWalletStateUpdate, resolveNormalizedWalletDescriptorState, stripDescriptorChecksum, } from "./descriptor-normalization.js";
|
|
12
12
|
import { acquireFileLock, clearOrphanedFileLock } from "./fs/lock.js";
|
|
13
13
|
import { createInternalCoreWalletPassphrase, createMnemonicConfirmationChallenge, deriveWalletMaterialFromMnemonic, generateWalletMaterial, isEnglishMnemonicWord, validateEnglishMnemonic, } from "./material.js";
|
|
14
|
-
import { resolveWalletRuntimePathsForTesting } from "./runtime.js";
|
|
14
|
+
import { deriveWalletRuntimePathsForSeed, resolveWalletRuntimePathsForTesting, } from "./runtime.js";
|
|
15
15
|
import { requestMiningGenerationPreemption } from "./mining/coordination.js";
|
|
16
16
|
import { loadClientConfig } from "./mining/config.js";
|
|
17
17
|
import { inspectMiningHookState } from "./mining/hooks.js";
|
|
@@ -21,6 +21,7 @@ import { renderWalletMnemonicRevealArt } from "./mnemonic-art.js";
|
|
|
21
21
|
import { clearWalletExplicitLock, loadWalletExplicitLock, saveWalletExplicitLock, } from "./state/explicit-lock.js";
|
|
22
22
|
import { clearWalletPendingInitializationState, loadWalletPendingInitializationStateOrNull, saveWalletPendingInitializationState, } from "./state/pending-init.js";
|
|
23
23
|
import { clearUnlockSession, loadUnlockSession, saveUnlockSession } from "./state/session.js";
|
|
24
|
+
import { addImportedWalletSeedRecord, assertValidImportedWalletSeedName, ensureMainWalletSeedIndexRecord, findWalletSeedRecord, loadWalletSeedIndex, removeWalletSeedRecord, } from "./state/seed-index.js";
|
|
24
25
|
import { createDefaultWalletSecretProvider, createWalletPendingInitSecretReference, createWalletRootId, createWalletSecretReference, } from "./state/provider.js";
|
|
25
26
|
import { extractWalletRootIdHintFromWalletStateEnvelope, loadRawWalletStateEnvelope, loadWalletState, saveWalletState, } from "./state/storage.js";
|
|
26
27
|
export const DEFAULT_UNLOCK_DURATION_MS = 15 * 60 * 1000;
|
|
@@ -57,7 +58,7 @@ function resolvePendingInitializationStoragePaths(paths) {
|
|
|
57
58
|
async function clearPendingInitialization(paths, provider) {
|
|
58
59
|
await clearWalletPendingInitializationState(resolvePendingInitializationStoragePaths(paths), {
|
|
59
60
|
provider,
|
|
60
|
-
secretReference: createWalletPendingInitSecretReference(paths.
|
|
61
|
+
secretReference: createWalletPendingInitSecretReference(paths.walletStateRoot),
|
|
61
62
|
});
|
|
62
63
|
}
|
|
63
64
|
async function loadOrCreatePendingInitializationMaterial(options) {
|
|
@@ -73,7 +74,7 @@ async function loadOrCreatePendingInitializationMaterial(options) {
|
|
|
73
74
|
await clearPendingInitialization(options.paths, options.provider);
|
|
74
75
|
}
|
|
75
76
|
const material = generateWalletMaterial();
|
|
76
|
-
const secretReference = createWalletPendingInitSecretReference(options.paths.
|
|
77
|
+
const secretReference = createWalletPendingInitSecretReference(options.paths.walletStateRoot);
|
|
77
78
|
const pendingState = {
|
|
78
79
|
schemaVersion: 1,
|
|
79
80
|
createdAtUnixMs: options.nowUnixMs,
|
|
@@ -372,7 +373,7 @@ async function promptForArchivePassphrase(prompter, promptPrefix) {
|
|
|
372
373
|
async function promptForRestoreMnemonic(prompter) {
|
|
373
374
|
const words = [];
|
|
374
375
|
for (let index = 0; index < 24; index += 1) {
|
|
375
|
-
const word = (await
|
|
376
|
+
const word = (await promptRequiredValue(prompter, `Word ${index + 1} of 24: `)).toLowerCase();
|
|
376
377
|
if (!isEnglishMnemonicWord(word)) {
|
|
377
378
|
throw new Error("wallet_restore_mnemonic_invalid");
|
|
378
379
|
}
|
|
@@ -405,6 +406,12 @@ async function confirmOverwriteIfNeeded(prompter, path) {
|
|
|
405
406
|
throw new Error("wallet_export_overwrite_declined");
|
|
406
407
|
}
|
|
407
408
|
}
|
|
409
|
+
async function confirmYesNo(prompter, message) {
|
|
410
|
+
const answer = (await prompter.prompt(message)).trim().toLowerCase();
|
|
411
|
+
if (answer !== "yes") {
|
|
412
|
+
throw new Error("wallet_delete_confirmation_required");
|
|
413
|
+
}
|
|
414
|
+
}
|
|
408
415
|
async function readManagedSnapshotTip(options) {
|
|
409
416
|
const daemon = await attachOrStartIndexerDaemon({
|
|
410
417
|
dataDir: options.dataDir,
|
|
@@ -693,6 +700,16 @@ function createSilentNonInteractivePrompter() {
|
|
|
693
700
|
},
|
|
694
701
|
};
|
|
695
702
|
}
|
|
703
|
+
function resolveMainWalletPaths(paths) {
|
|
704
|
+
return deriveWalletRuntimePathsForSeed(paths, "main");
|
|
705
|
+
}
|
|
706
|
+
async function loadSharedWalletSeedIndex(paths, nowUnixMs) {
|
|
707
|
+
const mainPaths = resolveMainWalletPaths(paths);
|
|
708
|
+
return await loadWalletSeedIndex({
|
|
709
|
+
paths: mainPaths,
|
|
710
|
+
nowUnixMs,
|
|
711
|
+
});
|
|
712
|
+
}
|
|
696
713
|
function applyRepairStoppedMiningState(state) {
|
|
697
714
|
const miningState = normalizeMiningStateRecord(state.miningState);
|
|
698
715
|
return {
|
|
@@ -1125,6 +1142,9 @@ export async function initializeWallet(options) {
|
|
|
1125
1142
|
const nowUnixMs = options.nowUnixMs ?? Date.now();
|
|
1126
1143
|
const unlockDurationMs = options.unlockDurationMs ?? DEFAULT_UNLOCK_DURATION_MS;
|
|
1127
1144
|
const paths = options.paths ?? resolveWalletRuntimePathsForTesting();
|
|
1145
|
+
if (paths.selectedSeedName !== "main") {
|
|
1146
|
+
throw new Error("wallet_init_seed_not_supported");
|
|
1147
|
+
}
|
|
1128
1148
|
const controlLock = await acquireFileLock(paths.walletControlLockPath, {
|
|
1129
1149
|
purpose: "wallet-init",
|
|
1130
1150
|
walletRootId: null,
|
|
@@ -1185,6 +1205,11 @@ export async function initializeWallet(options) {
|
|
|
1185
1205
|
secretReference,
|
|
1186
1206
|
});
|
|
1187
1207
|
await clearPendingInitialization(paths, provider);
|
|
1208
|
+
await ensureMainWalletSeedIndexRecord({
|
|
1209
|
+
paths: resolveMainWalletPaths(paths),
|
|
1210
|
+
walletRootId,
|
|
1211
|
+
nowUnixMs,
|
|
1212
|
+
});
|
|
1188
1213
|
return {
|
|
1189
1214
|
walletRootId,
|
|
1190
1215
|
fundingAddress: verifiedState.funding.address,
|
|
@@ -1404,6 +1429,9 @@ export async function importWallet(options) {
|
|
|
1404
1429
|
const nowUnixMs = options.nowUnixMs ?? Date.now();
|
|
1405
1430
|
const unlockDurationMs = options.unlockDurationMs ?? DEFAULT_UNLOCK_DURATION_MS;
|
|
1406
1431
|
const paths = options.paths ?? resolveWalletRuntimePathsForTesting();
|
|
1432
|
+
if (paths.selectedSeedName !== "main") {
|
|
1433
|
+
throw new Error("wallet_import_seed_not_supported");
|
|
1434
|
+
}
|
|
1407
1435
|
const controlLock = await acquireFileLock(paths.walletControlLockPath, {
|
|
1408
1436
|
purpose: "wallet-import",
|
|
1409
1437
|
walletRootId: null,
|
|
@@ -1467,6 +1495,11 @@ export async function importWallet(options) {
|
|
|
1467
1495
|
databasePath: options.databasePath,
|
|
1468
1496
|
walletRootId: importedState.walletRootId,
|
|
1469
1497
|
}).then((daemon) => daemon.close());
|
|
1498
|
+
await ensureMainWalletSeedIndexRecord({
|
|
1499
|
+
paths: resolveMainWalletPaths(paths),
|
|
1500
|
+
walletRootId: importedState.walletRootId,
|
|
1501
|
+
nowUnixMs,
|
|
1502
|
+
});
|
|
1470
1503
|
return {
|
|
1471
1504
|
archivePath: options.archivePath,
|
|
1472
1505
|
walletRootId: importedState.walletRootId,
|
|
@@ -1487,98 +1520,154 @@ export async function restoreWalletFromMnemonic(options) {
|
|
|
1487
1520
|
const nowUnixMs = options.nowUnixMs ?? Date.now();
|
|
1488
1521
|
const unlockDurationMs = options.unlockDurationMs ?? DEFAULT_UNLOCK_DURATION_MS;
|
|
1489
1522
|
const paths = options.paths ?? resolveWalletRuntimePathsForTesting();
|
|
1523
|
+
const seedName = assertValidImportedWalletSeedName(paths.selectedSeedName);
|
|
1490
1524
|
const controlLock = await acquireFileLock(paths.walletControlLockPath, {
|
|
1491
1525
|
purpose: "wallet-restore",
|
|
1492
1526
|
walletRootId: null,
|
|
1493
1527
|
});
|
|
1494
1528
|
try {
|
|
1495
|
-
const
|
|
1529
|
+
const mainPaths = resolveMainWalletPaths(paths);
|
|
1530
|
+
const seedIndex = await loadSharedWalletSeedIndex(paths, nowUnixMs);
|
|
1531
|
+
if (findWalletSeedRecord(seedIndex, "main") === null) {
|
|
1532
|
+
throw new Error("wallet_restore_requires_main_wallet");
|
|
1533
|
+
}
|
|
1534
|
+
if (findWalletSeedRecord(seedIndex, seedName) !== null) {
|
|
1535
|
+
throw new Error("wallet_seed_name_exists");
|
|
1536
|
+
}
|
|
1537
|
+
await ensureWalletNotInitialized(paths, provider);
|
|
1538
|
+
let promptPhaseStarted = false;
|
|
1539
|
+
let mnemonicPhrase;
|
|
1540
|
+
try {
|
|
1541
|
+
promptPhaseStarted = true;
|
|
1542
|
+
mnemonicPhrase = await promptForRestoreMnemonic(options.prompter);
|
|
1543
|
+
}
|
|
1544
|
+
finally {
|
|
1545
|
+
if (promptPhaseStarted) {
|
|
1546
|
+
await options.prompter.clearSensitiveDisplay?.("restore-mnemonic-entry");
|
|
1547
|
+
}
|
|
1548
|
+
}
|
|
1549
|
+
await clearPendingInitialization(paths, provider);
|
|
1550
|
+
const material = deriveWalletMaterialFromMnemonic(mnemonicPhrase);
|
|
1551
|
+
const walletRootId = createWalletRootId();
|
|
1552
|
+
const internalCoreWalletPassphrase = createInternalCoreWalletPassphrase();
|
|
1553
|
+
const secretReference = createWalletSecretReference(walletRootId);
|
|
1554
|
+
const secret = randomBytes(32);
|
|
1555
|
+
await provider.storeSecret(secretReference.keyId, secret);
|
|
1556
|
+
const initialState = createInitialWalletState({
|
|
1557
|
+
walletRootId,
|
|
1558
|
+
nowUnixMs,
|
|
1559
|
+
material,
|
|
1560
|
+
internalCoreWalletPassphrase,
|
|
1561
|
+
});
|
|
1562
|
+
await clearUnlockSession(paths.walletUnlockSessionPath);
|
|
1563
|
+
await clearWalletExplicitLock(paths.walletExplicitLockPath);
|
|
1564
|
+
await saveWalletState({
|
|
1496
1565
|
primaryPath: paths.walletStatePath,
|
|
1497
1566
|
backupPath: paths.walletStateBackupPath,
|
|
1567
|
+
}, initialState, {
|
|
1568
|
+
provider,
|
|
1569
|
+
secretReference,
|
|
1570
|
+
});
|
|
1571
|
+
const restoredState = await recreateManagedCoreWalletReplica(initialState, provider, paths, options.dataDir, nowUnixMs, {
|
|
1572
|
+
attachService: options.attachService,
|
|
1573
|
+
rpcFactory: options.rpcFactory,
|
|
1574
|
+
});
|
|
1575
|
+
const unlockUntilUnixMs = nowUnixMs + unlockDurationMs;
|
|
1576
|
+
await clearWalletExplicitLock(paths.walletExplicitLockPath);
|
|
1577
|
+
await saveUnlockSession(paths.walletUnlockSessionPath, createUnlockSession(restoredState, unlockUntilUnixMs, secretReference.keyId, nowUnixMs), {
|
|
1578
|
+
provider,
|
|
1579
|
+
secretReference,
|
|
1498
1580
|
});
|
|
1499
|
-
const replacementStateExists = rawEnvelope !== null
|
|
1500
|
-
|| await pathExists(paths.walletStatePath)
|
|
1501
|
-
|| await pathExists(paths.walletStateBackupPath);
|
|
1502
|
-
const replacementCoreWalletExists = await detectExistingManagedWalletReplica(options.dataDir);
|
|
1503
|
-
const mnemonicPhrase = await promptForRestoreMnemonic(options.prompter);
|
|
1504
1581
|
await clearPendingInitialization(paths, provider);
|
|
1505
|
-
|
|
1506
|
-
|
|
1582
|
+
await addImportedWalletSeedRecord({
|
|
1583
|
+
paths: mainPaths,
|
|
1584
|
+
seedName,
|
|
1585
|
+
walletRootId,
|
|
1586
|
+
nowUnixMs,
|
|
1587
|
+
});
|
|
1588
|
+
return {
|
|
1589
|
+
seedName,
|
|
1590
|
+
walletRootId,
|
|
1591
|
+
fundingAddress: restoredState.funding.address,
|
|
1592
|
+
unlockUntilUnixMs,
|
|
1593
|
+
state: restoredState,
|
|
1594
|
+
warnings: [],
|
|
1595
|
+
};
|
|
1596
|
+
}
|
|
1597
|
+
finally {
|
|
1598
|
+
await controlLock.release();
|
|
1599
|
+
}
|
|
1600
|
+
}
|
|
1601
|
+
export async function deleteImportedWalletSeed(options) {
|
|
1602
|
+
const provider = options.provider ?? createDefaultWalletSecretProvider();
|
|
1603
|
+
const nowUnixMs = options.nowUnixMs ?? Date.now();
|
|
1604
|
+
const paths = options.paths ?? resolveWalletRuntimePathsForTesting();
|
|
1605
|
+
if ((paths.selectedSeedName ?? "main") === "main") {
|
|
1606
|
+
throw new Error("wallet_delete_main_not_supported");
|
|
1607
|
+
}
|
|
1608
|
+
const seedName = assertValidImportedWalletSeedName(paths.selectedSeedName);
|
|
1609
|
+
const controlLock = await acquireFileLock(paths.walletControlLockPath, {
|
|
1610
|
+
purpose: "wallet-delete",
|
|
1611
|
+
walletRootId: null,
|
|
1612
|
+
});
|
|
1613
|
+
try {
|
|
1614
|
+
const mainPaths = resolveMainWalletPaths(paths);
|
|
1615
|
+
const seedIndex = await loadSharedWalletSeedIndex(paths, nowUnixMs);
|
|
1616
|
+
const seedRecord = findWalletSeedRecord(seedIndex, seedName);
|
|
1617
|
+
if (seedRecord === null) {
|
|
1618
|
+
throw new Error("wallet_seed_not_found");
|
|
1507
1619
|
}
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
const loaded = await loadWalletState({
|
|
1511
|
-
primaryPath: paths.walletStatePath,
|
|
1512
|
-
backupPath: paths.walletStateBackupPath,
|
|
1513
|
-
}, {
|
|
1514
|
-
provider,
|
|
1515
|
-
});
|
|
1516
|
-
previousWalletRootId = loaded.state.walletRootId;
|
|
1620
|
+
if (seedRecord.kind !== "imported") {
|
|
1621
|
+
throw new Error("wallet_delete_main_not_supported");
|
|
1517
1622
|
}
|
|
1518
|
-
|
|
1519
|
-
|
|
1623
|
+
if (!options.assumeYes) {
|
|
1624
|
+
await confirmYesNo(options.prompter, `Delete imported seed "${seedName}" and release its local wallet artifacts? Type yes to continue: `);
|
|
1520
1625
|
}
|
|
1521
|
-
const
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
await clearUnlockSession(paths.walletUnlockSessionPath);
|
|
1540
|
-
await clearWalletExplicitLock(paths.walletExplicitLockPath);
|
|
1541
|
-
await saveWalletState({
|
|
1542
|
-
primaryPath: paths.walletStatePath,
|
|
1543
|
-
backupPath: paths.walletStateBackupPath,
|
|
1544
|
-
}, initialState, {
|
|
1545
|
-
provider,
|
|
1546
|
-
secretReference,
|
|
1547
|
-
});
|
|
1548
|
-
const restoredState = await recreateManagedCoreWalletReplica(initialState, provider, paths, options.dataDir, nowUnixMs, {
|
|
1549
|
-
attachService: options.attachService,
|
|
1550
|
-
rpcFactory: options.rpcFactory,
|
|
1551
|
-
});
|
|
1552
|
-
const unlockUntilUnixMs = nowUnixMs + unlockDurationMs;
|
|
1553
|
-
await clearWalletExplicitLock(paths.walletExplicitLockPath);
|
|
1554
|
-
await saveUnlockSession(paths.walletUnlockSessionPath, createUnlockSession(restoredState, unlockUntilUnixMs, secretReference.keyId, nowUnixMs), {
|
|
1555
|
-
provider,
|
|
1556
|
-
secretReference,
|
|
1626
|
+
const probeManagedBitcoind = options.probeBitcoindService ?? probeManagedBitcoindService;
|
|
1627
|
+
const managedBitcoindProbe = await probeManagedBitcoind({
|
|
1628
|
+
dataDir: options.dataDir,
|
|
1629
|
+
chain: "main",
|
|
1630
|
+
startHeight: 0,
|
|
1631
|
+
}).catch(() => ({
|
|
1632
|
+
compatibility: "unreachable",
|
|
1633
|
+
status: null,
|
|
1634
|
+
error: null,
|
|
1635
|
+
}));
|
|
1636
|
+
if (managedBitcoindProbe.compatibility !== "compatible" && managedBitcoindProbe.compatibility !== "unreachable") {
|
|
1637
|
+
throw new Error(managedBitcoindProbe.error ?? "managed_bitcoind_protocol_error");
|
|
1638
|
+
}
|
|
1639
|
+
if (managedBitcoindProbe.compatibility === "compatible") {
|
|
1640
|
+
const node = await (options.attachService ?? attachOrStartManagedBitcoindService)({
|
|
1641
|
+
dataDir: options.dataDir,
|
|
1642
|
+
chain: "main",
|
|
1643
|
+
startHeight: 0,
|
|
1557
1644
|
});
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
dataDir: options.dataDir,
|
|
1563
|
-
walletRootId: previousWalletRootId,
|
|
1564
|
-
});
|
|
1565
|
-
}
|
|
1566
|
-
catch (error) {
|
|
1567
|
-
warnings.push(formatRestoreCleanupWarning(error));
|
|
1568
|
-
}
|
|
1569
|
-
await provider.deleteSecret(createWalletSecretReference(previousWalletRootId).keyId).catch(() => undefined);
|
|
1645
|
+
const rpc = (options.rpcFactory ?? createRpcClient)(node.rpc);
|
|
1646
|
+
const walletName = sanitizeWalletName(seedRecord.walletRootId);
|
|
1647
|
+
if (rpc.unloadWallet != null) {
|
|
1648
|
+
await rpc.unloadWallet(walletName, false).catch(() => undefined);
|
|
1570
1649
|
}
|
|
1571
|
-
return {
|
|
1572
|
-
walletRootId,
|
|
1573
|
-
fundingAddress: restoredState.funding.address,
|
|
1574
|
-
unlockUntilUnixMs,
|
|
1575
|
-
state: restoredState,
|
|
1576
|
-
warnings,
|
|
1577
|
-
};
|
|
1578
|
-
}
|
|
1579
|
-
finally {
|
|
1580
|
-
await miningLock.release();
|
|
1581
1650
|
}
|
|
1651
|
+
await clearUnlockSession(paths.walletUnlockSessionPath).catch(() => undefined);
|
|
1652
|
+
await clearWalletExplicitLock(paths.walletExplicitLockPath).catch(() => undefined);
|
|
1653
|
+
await clearPendingInitialization(paths, provider).catch(() => undefined);
|
|
1654
|
+
await provider.deleteSecret(createWalletSecretReference(seedRecord.walletRootId).keyId).catch(() => undefined);
|
|
1655
|
+
await rm(paths.walletStateRoot, { recursive: true, force: true }).catch(() => undefined);
|
|
1656
|
+
await rm(paths.walletRuntimeRoot, { recursive: true, force: true }).catch(() => undefined);
|
|
1657
|
+
await rm(join(options.dataDir, "wallets", sanitizeWalletName(seedRecord.walletRootId)), {
|
|
1658
|
+
recursive: true,
|
|
1659
|
+
force: true,
|
|
1660
|
+
}).catch(() => undefined);
|
|
1661
|
+
await removeWalletSeedRecord({
|
|
1662
|
+
paths: mainPaths,
|
|
1663
|
+
seedName,
|
|
1664
|
+
nowUnixMs,
|
|
1665
|
+
});
|
|
1666
|
+
return {
|
|
1667
|
+
seedName,
|
|
1668
|
+
walletRootId: seedRecord.walletRootId,
|
|
1669
|
+
deleted: true,
|
|
1670
|
+
};
|
|
1582
1671
|
}
|
|
1583
1672
|
finally {
|
|
1584
1673
|
await controlLock.release();
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import type { ProgressOutputMode } from "../../bitcoind/types.js";
|
|
1
|
+
import type { BootstrapProgress, ProgressOutputMode } from "../../bitcoind/types.js";
|
|
2
|
+
import { createFollowSceneState } from "../../bitcoind/progress/follow-scene.js";
|
|
3
|
+
import { type RenderClock, type TtyRenderStream } from "../../bitcoind/progress/render-policy.js";
|
|
2
4
|
import type { MiningRuntimeStatusV1 } from "./types.js";
|
|
3
|
-
interface
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
write(chunk: string): boolean | void;
|
|
5
|
+
interface VisualizerRendererLike {
|
|
6
|
+
renderFollowScene(progress: BootstrapProgress, cogcoinSyncHeight: number | null, cogcoinSyncTargetHeight: number | null, followScene: ReturnType<typeof createFollowSceneState>, statusFieldText?: string): void;
|
|
7
|
+
close(): void;
|
|
7
8
|
}
|
|
8
9
|
export declare function describeMiningVisualizerStatus(snapshot: MiningRuntimeStatusV1): string;
|
|
9
10
|
export declare function describeMiningVisualizerProgress(snapshot: MiningRuntimeStatusV1): string;
|
|
@@ -11,7 +12,11 @@ export declare class MiningFollowVisualizer {
|
|
|
11
12
|
#private;
|
|
12
13
|
constructor(options?: {
|
|
13
14
|
progressOutput?: ProgressOutputMode;
|
|
14
|
-
stream?:
|
|
15
|
+
stream?: TtyRenderStream;
|
|
16
|
+
platform?: NodeJS.Platform;
|
|
17
|
+
env?: NodeJS.ProcessEnv;
|
|
18
|
+
clock?: RenderClock;
|
|
19
|
+
rendererFactory?: (stream: TtyRenderStream) => VisualizerRendererLike;
|
|
15
20
|
});
|
|
16
21
|
update(snapshot: MiningRuntimeStatusV1): void;
|
|
17
22
|
close(): void;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createBootstrapProgress } from "../../bitcoind/progress/formatting.js";
|
|
2
2
|
import { createFollowSceneState, syncFollowSceneState, } from "../../bitcoind/progress/follow-scene.js";
|
|
3
|
+
import { DEFAULT_RENDER_CLOCK, resolveTtyRenderPolicy, TtyRenderThrottle, } from "../../bitcoind/progress/render-policy.js";
|
|
3
4
|
import { TtyProgressRenderer } from "../../bitcoind/progress/tty-renderer.js";
|
|
4
5
|
const VISUALIZER_PROGRESS_SNAPSHOT = {
|
|
5
6
|
url: "",
|
|
@@ -8,15 +9,6 @@ const VISUALIZER_PROGRESS_SNAPSHOT = {
|
|
|
8
9
|
sha256: "",
|
|
9
10
|
sizeBytes: 1,
|
|
10
11
|
};
|
|
11
|
-
function shouldRenderToTty(progressOutput, stream) {
|
|
12
|
-
if (progressOutput === "none") {
|
|
13
|
-
return false;
|
|
14
|
-
}
|
|
15
|
-
if (progressOutput === "tty") {
|
|
16
|
-
return true;
|
|
17
|
-
}
|
|
18
|
-
return stream.isTTY === true;
|
|
19
|
-
}
|
|
20
12
|
export function describeMiningVisualizerStatus(snapshot) {
|
|
21
13
|
switch (snapshot.currentPhase) {
|
|
22
14
|
case "resuming":
|
|
@@ -99,24 +91,52 @@ export function describeMiningVisualizerProgress(snapshot) {
|
|
|
99
91
|
}
|
|
100
92
|
export class MiningFollowVisualizer {
|
|
101
93
|
#renderer;
|
|
94
|
+
#clock;
|
|
95
|
+
#renderThrottle;
|
|
102
96
|
#progress = createBootstrapProgress("follow_tip", VISUALIZER_PROGRESS_SNAPSHOT);
|
|
103
97
|
#scene = createFollowSceneState();
|
|
98
|
+
#latestSnapshot = null;
|
|
104
99
|
constructor(options = {}) {
|
|
105
100
|
const stream = options.stream ?? process.stderr;
|
|
106
101
|
const progressOutput = options.progressOutput ?? "auto";
|
|
107
|
-
|
|
108
|
-
|
|
102
|
+
const renderPolicy = resolveTtyRenderPolicy(progressOutput, stream, {
|
|
103
|
+
platform: options.platform,
|
|
104
|
+
env: options.env,
|
|
105
|
+
});
|
|
106
|
+
this.#clock = options.clock ?? DEFAULT_RENDER_CLOCK;
|
|
107
|
+
this.#renderer = renderPolicy.enabled
|
|
108
|
+
? options.rendererFactory?.(stream) ?? new TtyProgressRenderer(stream)
|
|
109
109
|
: null;
|
|
110
|
+
this.#renderThrottle = new TtyRenderThrottle({
|
|
111
|
+
clock: this.#clock,
|
|
112
|
+
intervalMs: renderPolicy.repaintIntervalMs,
|
|
113
|
+
onRender: () => {
|
|
114
|
+
this.#renderLatestSnapshot();
|
|
115
|
+
},
|
|
116
|
+
throttled: renderPolicy.linuxHeadlessThrottle,
|
|
117
|
+
});
|
|
110
118
|
}
|
|
111
119
|
update(snapshot) {
|
|
112
120
|
if (this.#renderer === null) {
|
|
113
121
|
return;
|
|
114
122
|
}
|
|
123
|
+
this.#latestSnapshot = snapshot;
|
|
124
|
+
this.#renderThrottle.request();
|
|
125
|
+
}
|
|
126
|
+
close() {
|
|
127
|
+
this.#renderThrottle.flush();
|
|
128
|
+
this.#renderer?.close();
|
|
129
|
+
}
|
|
130
|
+
#renderLatestSnapshot() {
|
|
131
|
+
if (this.#renderer === null || this.#latestSnapshot === null) {
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
const snapshot = this.#latestSnapshot;
|
|
115
135
|
const indexedHeight = snapshot.indexerTipHeight ?? snapshot.coreBestHeight ?? null;
|
|
116
136
|
const nodeHeight = snapshot.coreBestHeight ?? indexedHeight;
|
|
117
137
|
this.#progress.phase = "follow_tip";
|
|
118
138
|
this.#progress.message = describeMiningVisualizerProgress(snapshot);
|
|
119
|
-
this.#progress.updatedAt =
|
|
139
|
+
this.#progress.updatedAt = this.#clock.now();
|
|
120
140
|
this.#progress.blocks = nodeHeight;
|
|
121
141
|
this.#progress.targetHeight = nodeHeight;
|
|
122
142
|
this.#progress.etaSeconds = null;
|
|
@@ -128,7 +148,4 @@ export class MiningFollowVisualizer {
|
|
|
128
148
|
});
|
|
129
149
|
this.#renderer.renderFollowScene(this.#progress, indexedHeight, nodeHeight, this.#scene, describeMiningVisualizerStatus(snapshot));
|
|
130
150
|
}
|
|
131
|
-
close() {
|
|
132
|
-
this.#renderer?.close();
|
|
133
|
-
}
|
|
134
151
|
}
|