@cogcoin/client 0.5.6 → 0.5.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +84 -30
- package/dist/bitcoind/client/managed-client.js +2 -1
- 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 +93 -54
- package/dist/bitcoind/testing.d.ts +1 -1
- package/dist/bitcoind/testing.js +1 -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 +84 -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 +3 -0
- package/dist/cli/types.d.ts +11 -4
- package/dist/cli/wallet-format.js +6 -0
- package/dist/wallet/lifecycle.d.ts +15 -1
- package/dist/wallet/lifecycle.js +147 -83
- 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 +215 -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/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,129 @@ 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
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1626
|
+
await clearUnlockSession(paths.walletUnlockSessionPath).catch(() => undefined);
|
|
1627
|
+
await clearWalletExplicitLock(paths.walletExplicitLockPath).catch(() => undefined);
|
|
1628
|
+
await clearPendingInitialization(paths, provider).catch(() => undefined);
|
|
1629
|
+
await provider.deleteSecret(createWalletSecretReference(seedRecord.walletRootId).keyId).catch(() => undefined);
|
|
1630
|
+
await rm(paths.walletStateRoot, { recursive: true, force: true }).catch(() => undefined);
|
|
1631
|
+
await rm(paths.walletRuntimeRoot, { recursive: true, force: true }).catch(() => undefined);
|
|
1632
|
+
await rm(join(options.dataDir, "wallets", sanitizeWalletName(seedRecord.walletRootId)), {
|
|
1633
|
+
recursive: true,
|
|
1634
|
+
force: true,
|
|
1635
|
+
}).catch(() => undefined);
|
|
1636
|
+
await removeWalletSeedRecord({
|
|
1637
|
+
paths: mainPaths,
|
|
1638
|
+
seedName,
|
|
1639
|
+
nowUnixMs,
|
|
1524
1640
|
});
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
const secretReference = createWalletSecretReference(walletRootId);
|
|
1531
|
-
const secret = randomBytes(32);
|
|
1532
|
-
await provider.storeSecret(secretReference.keyId, secret);
|
|
1533
|
-
const initialState = createInitialWalletState({
|
|
1534
|
-
walletRootId,
|
|
1535
|
-
nowUnixMs,
|
|
1536
|
-
material,
|
|
1537
|
-
internalCoreWalletPassphrase,
|
|
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,
|
|
1557
|
-
});
|
|
1558
|
-
await clearPendingInitialization(paths, provider);
|
|
1559
|
-
if (previousWalletRootId !== null && previousWalletRootId !== walletRootId) {
|
|
1560
|
-
try {
|
|
1561
|
-
await clearPreviousManagedWalletRuntime({
|
|
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);
|
|
1570
|
-
}
|
|
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
|
-
}
|
|
1641
|
+
return {
|
|
1642
|
+
seedName,
|
|
1643
|
+
walletRootId: seedRecord.walletRootId,
|
|
1644
|
+
deleted: true,
|
|
1645
|
+
};
|
|
1582
1646
|
}
|
|
1583
1647
|
finally {
|
|
1584
1648
|
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
|
}
|
package/dist/wallet/reset.js
CHANGED
|
@@ -13,6 +13,7 @@ import { loadMiningRuntimeStatus } from "./mining/runtime-artifacts.js";
|
|
|
13
13
|
import { resolveWalletRuntimePathsForTesting } from "./runtime.js";
|
|
14
14
|
import { loadWalletExplicitLock } from "./state/explicit-lock.js";
|
|
15
15
|
import { createDefaultWalletSecretProvider, createWalletRootId, createWalletSecretReference, } from "./state/provider.js";
|
|
16
|
+
import { loadWalletSeedIndex } from "./state/seed-index.js";
|
|
16
17
|
import { extractWalletRootIdHintFromWalletStateEnvelope, loadRawWalletStateEnvelope, loadWalletState, saveWalletState, } from "./state/storage.js";
|
|
17
18
|
import { confirmTypedAcknowledgement } from "./tx/confirm.js";
|
|
18
19
|
function sanitizeWalletName(walletRootId) {
|
|
@@ -519,6 +520,12 @@ async function preflightReset(options) {
|
|
|
519
520
|
}
|
|
520
521
|
const tracked = await collectTrackedManagedProcesses(options.paths);
|
|
521
522
|
const secretProviderKeyId = rawEnvelope?.envelope.secretProvider?.keyId ?? null;
|
|
523
|
+
const seedIndex = await loadWalletSeedIndex({
|
|
524
|
+
paths: options.paths,
|
|
525
|
+
}).catch(() => null);
|
|
526
|
+
const importedSeedSecretProviderKeyIds = [...new Set((seedIndex?.seeds ?? [])
|
|
527
|
+
.filter((seed) => seed.kind === "imported")
|
|
528
|
+
.map((seed) => createWalletSecretReference(seed.walletRootId).keyId))];
|
|
522
529
|
return {
|
|
523
530
|
dataRoot: options.paths.dataRoot,
|
|
524
531
|
removedRoots,
|
|
@@ -531,6 +538,7 @@ async function preflightReset(options) {
|
|
|
531
538
|
: "passphrase-wrapped",
|
|
532
539
|
envelopeSource: rawEnvelope?.source ?? null,
|
|
533
540
|
secretProviderKeyId,
|
|
541
|
+
importedSeedSecretProviderKeyIds,
|
|
534
542
|
explicitLock,
|
|
535
543
|
rawEnvelope,
|
|
536
544
|
},
|
|
@@ -719,7 +727,8 @@ export async function previewResetWallet(options) {
|
|
|
719
727
|
: null,
|
|
720
728
|
},
|
|
721
729
|
trackedProcessKinds: preflight.trackedProcessKinds,
|
|
722
|
-
willDeleteOsSecrets: preflight.wallet.secretProviderKeyId !== null
|
|
730
|
+
willDeleteOsSecrets: preflight.wallet.secretProviderKeyId !== null
|
|
731
|
+
|| preflight.wallet.importedSeedSecretProviderKeyIds.length > 0,
|
|
723
732
|
removedPaths,
|
|
724
733
|
};
|
|
725
734
|
}
|
|
@@ -766,9 +775,7 @@ export async function resetWallet(options) {
|
|
|
766
775
|
let rootsDeleted = false;
|
|
767
776
|
let committed = false;
|
|
768
777
|
let newProviderKeyId = null;
|
|
769
|
-
let secretCleanupStatus =
|
|
770
|
-
? "not-found"
|
|
771
|
-
: "not-found";
|
|
778
|
+
let secretCleanupStatus = "not-found";
|
|
772
779
|
const deletedSecretRefs = [];
|
|
773
780
|
const failedSecretRefs = [];
|
|
774
781
|
const preservedSecretRefs = [];
|
|
@@ -850,41 +857,46 @@ export async function resetWallet(options) {
|
|
|
850
857
|
await restoreStagedArtifacts(stagedSnapshotArtifacts);
|
|
851
858
|
}
|
|
852
859
|
committed = true;
|
|
853
|
-
|
|
854
|
-
if (preflight.wallet.secretProviderKeyId !== null) {
|
|
855
|
-
try {
|
|
856
|
-
await provider.deleteSecret(preflight.wallet.secretProviderKeyId);
|
|
857
|
-
deletedSecretRefs.push(preflight.wallet.secretProviderKeyId);
|
|
858
|
-
secretCleanupStatus = "deleted";
|
|
859
|
-
}
|
|
860
|
-
catch {
|
|
861
|
-
failedSecretRefs.push(preflight.wallet.secretProviderKeyId);
|
|
862
|
-
secretCleanupStatus = "failed";
|
|
863
|
-
throw new Error("reset_secret_cleanup_failed");
|
|
864
|
-
}
|
|
865
|
-
}
|
|
866
|
-
}
|
|
867
|
-
else if (walletAction === "retain-mnemonic" && preflight.wallet.secretProviderKeyId !== null) {
|
|
860
|
+
const deleteTrackedSecretReference = async (keyId) => {
|
|
868
861
|
try {
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
deletedSecretRefs.push(preflight.wallet.secretProviderKeyId);
|
|
872
|
-
secretCleanupStatus = "deleted";
|
|
873
|
-
}
|
|
862
|
+
await provider.deleteSecret(keyId);
|
|
863
|
+
deletedSecretRefs.push(keyId);
|
|
874
864
|
}
|
|
875
865
|
catch {
|
|
876
|
-
failedSecretRefs.push(
|
|
866
|
+
failedSecretRefs.push(keyId);
|
|
877
867
|
secretCleanupStatus = "failed";
|
|
878
868
|
throw new Error("reset_secret_cleanup_failed");
|
|
879
869
|
}
|
|
870
|
+
};
|
|
871
|
+
for (const importedSecretKeyId of preflight.wallet.importedSeedSecretProviderKeyIds) {
|
|
872
|
+
await deleteTrackedSecretReference(importedSecretKeyId);
|
|
873
|
+
}
|
|
874
|
+
if (walletAction === "deleted") {
|
|
875
|
+
if (preflight.wallet.secretProviderKeyId !== null) {
|
|
876
|
+
await deleteTrackedSecretReference(preflight.wallet.secretProviderKeyId);
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
else if (walletAction === "retain-mnemonic" && preflight.wallet.secretProviderKeyId !== null) {
|
|
880
|
+
if (preflight.wallet.secretProviderKeyId !== newProviderKeyId) {
|
|
881
|
+
await deleteTrackedSecretReference(preflight.wallet.secretProviderKeyId);
|
|
882
|
+
}
|
|
880
883
|
}
|
|
881
884
|
else if (preflight.wallet.secretProviderKeyId !== null) {
|
|
882
885
|
preservedSecretRefs.push(preflight.wallet.secretProviderKeyId);
|
|
883
886
|
}
|
|
884
|
-
if (
|
|
887
|
+
if (failedSecretRefs.length > 0) {
|
|
888
|
+
secretCleanupStatus = "failed";
|
|
889
|
+
}
|
|
890
|
+
else if (deletedSecretRefs.length > 0) {
|
|
891
|
+
secretCleanupStatus = "deleted";
|
|
892
|
+
}
|
|
893
|
+
else if (preflight.wallet.secretProviderKeyId === null
|
|
894
|
+
&& preflight.wallet.importedSeedSecretProviderKeyIds.length === 0
|
|
895
|
+
&& preflight.wallet.present
|
|
896
|
+
&& preflight.wallet.rawEnvelope === null) {
|
|
885
897
|
secretCleanupStatus = "unknown";
|
|
886
898
|
}
|
|
887
|
-
else if (deletedSecretRefs.length === 0
|
|
899
|
+
else if (deletedSecretRefs.length === 0) {
|
|
888
900
|
secretCleanupStatus = "not-found";
|
|
889
901
|
}
|
|
890
902
|
return {
|
package/dist/wallet/runtime.d.ts
CHANGED
|
@@ -1,13 +1,23 @@
|
|
|
1
1
|
import type { CogcoinPathResolution } from "../app-paths.js";
|
|
2
|
+
export type WalletSeedKind = "main" | "imported";
|
|
3
|
+
export interface WalletRuntimePathResolution extends CogcoinPathResolution {
|
|
4
|
+
seedName?: string | null;
|
|
5
|
+
}
|
|
2
6
|
export interface WalletRuntimePaths {
|
|
3
7
|
dataRoot: string;
|
|
4
8
|
clientDataDir: string;
|
|
5
9
|
clientConfigPath: string;
|
|
6
10
|
runtimeRoot: string;
|
|
11
|
+
walletRuntimeRoot: string;
|
|
7
12
|
hooksRoot: string;
|
|
8
13
|
stateRoot: string;
|
|
14
|
+
walletStateRoot: string;
|
|
15
|
+
seedRegistryPath: string;
|
|
16
|
+
selectedSeedName: string;
|
|
17
|
+
selectedSeedKind: WalletSeedKind;
|
|
9
18
|
bitcoinDataDir: string;
|
|
10
19
|
indexerRoot: string;
|
|
20
|
+
walletStateDirectory: string;
|
|
11
21
|
walletStatePath: string;
|
|
12
22
|
walletStateBackupPath: string;
|
|
13
23
|
walletInitPendingPath: string;
|
|
@@ -27,4 +37,5 @@ export interface WalletRuntimePaths {
|
|
|
27
37
|
miningEventsPath: string;
|
|
28
38
|
miningControlLockPath: string;
|
|
29
39
|
}
|
|
30
|
-
export declare function
|
|
40
|
+
export declare function deriveWalletRuntimePathsForSeed(basePaths: WalletRuntimePaths, seedName: string | null | undefined): WalletRuntimePaths;
|
|
41
|
+
export declare function resolveWalletRuntimePathsForTesting(resolution?: WalletRuntimePathResolution): WalletRuntimePaths;
|