@cogcoin/client 0.5.4 → 0.5.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/app-paths.d.ts +2 -0
- package/dist/app-paths.js +4 -0
- package/dist/art/wallet.txt +9 -9
- package/dist/bitcoind/bootstrap/chainstate.d.ts +2 -1
- package/dist/bitcoind/bootstrap/chainstate.js +4 -1
- package/dist/bitcoind/bootstrap/chunk-manifest.d.ts +14 -0
- package/dist/bitcoind/bootstrap/chunk-manifest.js +85 -0
- package/dist/bitcoind/bootstrap/chunk-recovery.d.ts +4 -0
- package/dist/bitcoind/bootstrap/chunk-recovery.js +122 -0
- package/dist/bitcoind/bootstrap/constants.d.ts +3 -1
- package/dist/bitcoind/bootstrap/constants.js +3 -1
- package/dist/bitcoind/bootstrap/controller.d.ts +10 -2
- package/dist/bitcoind/bootstrap/controller.js +56 -12
- package/dist/bitcoind/bootstrap/default-snapshot-chunk-manifest.d.ts +2 -0
- package/dist/bitcoind/bootstrap/default-snapshot-chunk-manifest.js +2309 -0
- package/dist/bitcoind/bootstrap/download.js +177 -83
- package/dist/bitcoind/bootstrap/headers.d.ts +16 -2
- package/dist/bitcoind/bootstrap/headers.js +124 -14
- package/dist/bitcoind/bootstrap/state.d.ts +11 -1
- package/dist/bitcoind/bootstrap/state.js +50 -23
- package/dist/bitcoind/bootstrap/types.d.ts +12 -1
- package/dist/bitcoind/client/factory.js +11 -2
- package/dist/bitcoind/client/internal-types.d.ts +1 -0
- package/dist/bitcoind/client/managed-client.d.ts +1 -1
- package/dist/bitcoind/client/managed-client.js +29 -15
- package/dist/bitcoind/client/sync-engine.js +88 -16
- package/dist/bitcoind/errors.js +9 -0
- package/dist/bitcoind/indexer-daemon.d.ts +7 -0
- package/dist/bitcoind/indexer-daemon.js +31 -22
- package/dist/bitcoind/processing-start-height.d.ts +7 -0
- package/dist/bitcoind/processing-start-height.js +9 -0
- package/dist/bitcoind/progress/controller.js +1 -0
- package/dist/bitcoind/progress/formatting.js +4 -1
- package/dist/bitcoind/retryable-rpc.d.ts +11 -0
- package/dist/bitcoind/retryable-rpc.js +30 -0
- package/dist/bitcoind/service.d.ts +16 -1
- package/dist/bitcoind/service.js +228 -115
- package/dist/bitcoind/testing.d.ts +1 -1
- package/dist/bitcoind/testing.js +1 -1
- package/dist/bitcoind/types.d.ts +10 -0
- package/dist/cli/commands/follow.js +9 -0
- package/dist/cli/commands/service-runtime.js +150 -134
- package/dist/cli/commands/sync.js +9 -0
- package/dist/cli/commands/wallet-admin.js +77 -21
- package/dist/cli/context.js +4 -2
- package/dist/cli/mutation-json.js +2 -0
- package/dist/cli/output.js +3 -1
- package/dist/cli/parse.d.ts +1 -1
- package/dist/cli/parse.js +6 -0
- package/dist/cli/preview-json.js +2 -0
- package/dist/cli/runner.js +1 -0
- package/dist/cli/types.d.ts +6 -3
- package/dist/cli/types.js +1 -1
- package/dist/cli/wallet-format.js +134 -14
- package/dist/wallet/lifecycle.d.ts +6 -0
- package/dist/wallet/lifecycle.js +168 -37
- package/dist/wallet/read/context.js +10 -4
- package/dist/wallet/reset.d.ts +61 -2
- package/dist/wallet/reset.js +208 -63
- package/dist/wallet/root-resolution.d.ts +20 -0
- package/dist/wallet/root-resolution.js +37 -0
- package/dist/wallet/runtime.d.ts +3 -0
- package/dist/wallet/runtime.js +3 -0
- package/dist/wallet/state/crypto.d.ts +3 -0
- package/dist/wallet/state/crypto.js +3 -0
- package/dist/wallet/state/pending-init.d.ts +24 -0
- package/dist/wallet/state/pending-init.js +59 -0
- package/dist/wallet/state/provider.d.ts +1 -0
- package/dist/wallet/state/provider.js +7 -1
- package/dist/wallet/state/storage.d.ts +7 -1
- package/dist/wallet/state/storage.js +39 -0
- package/dist/wallet/types.d.ts +9 -0
- package/package.json +4 -2
package/dist/cli/parse.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import type { ParsedCliArgs } from "./types.js";
|
|
2
|
-
export declare const HELP_TEXT = "Usage: cogcoin <command> [options]\n\nCommands:\n status Show wallet-aware local service and chain status\n status --output json Emit the stable v1 machine-readable status envelope\n bitcoin start Start the managed Bitcoin daemon\n bitcoin stop Stop the managed Bitcoin daemon and paired indexer\n bitcoin status Show managed Bitcoin daemon status without starting it\n indexer start Start the managed Cogcoin indexer (and bitcoind if needed)\n indexer stop Stop the managed Cogcoin indexer only\n indexer status Show managed Cogcoin indexer status without starting it\n init Initialize a new local wallet root\n init --output json Emit the stable v1 machine-readable init result envelope\n restore Restore a fresh local wallet from a 24-word mnemonic; run sync afterward\n reset Factory-reset local Cogcoin state with interactive retention prompts\n repair Recover bounded wallet/indexer/runtime state\n unlock Clear an explicit wallet lock and unlock for a limited duration\n wallet address Alias for address\n wallet ids Alias for ids\n hooks enable mining Enable a validated custom mining hook\n hooks disable mining Return to built-in mining hooks\n hooks status Show mining hook validation and trust status\n mine Run the miner in the foreground\n mine start Start the miner as a background worker\n mine stop Stop the active background miner\n mine setup Configure the built-in mining provider\n mine setup --output json\n Emit the stable v1 machine-readable mine setup result envelope\n mine status Show mining control-plane health and readiness\n mine log Show recent mining control-plane events\n anchor <domain> Anchor an owned unanchored domain with the Tx1/Tx2 family\n register <domain> [--from <identity>]\n Register a root domain or subdomain\n transfer <domain> --to <btc-target>\n Transfer an unanchored domain to another BTC identity\n sell <domain> <price> List an unanchored domain for sale in COG\n unsell <domain> Clear an active domain listing\n buy <domain> [--from <identity>]\n Buy an unanchored listed domain in COG\n send <amount> --to <btc-target>\n Send COG from one local identity to another BTC target\n claim <lock-id> --preimage <32-byte-hex>\n Claim an active COG lock before timeout\n reclaim <lock-id> Reclaim an expired COG lock as the original locker\n cog lock <amount> --to-domain <domain> (--for <blocks-or-duration> | --until-height <height>) --condition <sha256hex>\n Lock COG to an anchored recipient domain\n wallet status Show detailed wallet-local status and service health\n wallet init Initialize a new local wallet root\n wallet restore Restore a fresh local wallet from a 24-word mnemonic; run sync afterward\n wallet unlock Clear an explicit wallet lock and unlock for a limited duration\n wallet lock Lock the local wallet and disable on-demand unlock\n wallet export <path> Export a portable encrypted wallet archive\n wallet import <path> Import a portable encrypted wallet archive\n address Show the BTC funding identity for this wallet\n ids List locally controlled identities\n balance Show per-identity COG balances\n locks Show locally related active COG locks\n domain list Alias for domains\n domain show <domain> Alias for show <domain>\n domains [--anchored] [--listed] [--mineable]\n Show locally related domains\n show <domain> Show one domain and its local-wallet relationship\n fields <domain> List current fields on a domain\n field <domain> <field> Show one current field value\n field create <domain> <field>\n Create a new anchored field, optionally with an initial value\n field set <domain> <field>\n Update an existing anchored field value\n field clear <domain> <field>\n Clear an existing anchored field value\n rep give <source-domain> <target-domain> <amount>\n Burn COG as anchored-domain reputation support\n rep revoke <source-domain> <target-domain> <amount>\n Revoke visible support without refunding burned COG\n\nOptions:\n --db <path> Override the SQLite database path\n --data-dir <path> Override the managed bitcoin datadir\n --for <duration> Unlock duration like 15m, 2h, or 1d when unlocking explicitly\n --message <text> Founding message text for anchor\n --to <btc-target> Transfer or send target as an address or spk:<hex>\n --from <identity> Resolve sender identity as id:<n>, domain:<name>, address, or spk:<hex>\n --to-domain <domain>\n Recipient domain for cog lock\n --condition <sha256hex>\n 32-byte lock condition hash\n --until-height <height>\n Absolute timeout height for cog lock\n --preimage <32-byte-hex>\n Claim preimage for an active lock\n --review <text> Optional public review text for reputation operations\n --text <utf8> UTF-8 payload text for endpoint or field writes\n --json <json> UTF-8 payload JSON text for endpoint or field writes\n --bytes <spec> Payload bytes as hex:<hex> or @<path>\n --permanent Create the field as permanent\n --format <spec> Advanced field format as raw:<u8>\n --value <spec> Advanced field value as hex:<hex>, @<path>, or utf8:<text>\n --claimable Show only currently claimable locks\n --reclaimable Show only currently reclaimable locks\n --anchored Show only anchored domains\n --listed Show only currently listed domains\n --mineable Show only locally mineable root domains\n --limit <n> Limit list rows (1..1000)\n --all Show all rows for list commands\n --verify Run full custom-hook verification\n --follow Follow mining log output\n --output <mode> Output mode: text, json, or preview-json\n --progress <mode> Progress output mode: auto, tty, or none\n --force-race Allow a visible root registration race\n --yes Approve eligible plain yes/no mutation confirmations non-interactively\n --help Show help\n --version Show package version\n\nQuickstart:\n 1. Run `cogcoin init` to create the wallet.\n 2. Run `cogcoin sync` to bootstrap assumeutxo and the managed Bitcoin/indexer state.\n 3. Run `cogcoin address`, then fund the wallet with about 0.0015 BTC so you can buy a 6+ character domain to start mining and still keep BTC available for mining transaction fees.\n\nExamples:\n cogcoin status --output json\n cogcoin bitcoin status\n cogcoin indexer status\n cogcoin init --output json\n cogcoin wallet address\n cogcoin domain list --mineable\n cogcoin register alpha-child\n cogcoin register weatherbot --from id:1\n cogcoin anchor alpha\n cogcoin buy alpha\n cogcoin buy alpha --from id:1\n cogcoin field create alpha bio --text \"hello\"\n cogcoin rep give alpha beta 10 --review \"great operator\"\n cogcoin hooks status\n cogcoin mine setup --output json\n cogcoin register alpha-child --output preview-json\n cogcoin mine status\n";
|
|
2
|
+
export declare const HELP_TEXT = "Usage: cogcoin <command> [options]\n\nCommands:\n status Show wallet-aware local service and chain status\n status --output json Emit the stable v1 machine-readable status envelope\n bitcoin start Start the managed Bitcoin daemon\n bitcoin stop Stop the managed Bitcoin daemon and paired indexer\n bitcoin status Show managed Bitcoin daemon status without starting it\n indexer start Start the managed Cogcoin indexer (and bitcoind if needed)\n indexer stop Stop the managed Cogcoin indexer only\n indexer status Show managed Cogcoin indexer status without starting it\n init Initialize a new local wallet root\n init --output json Emit the stable v1 machine-readable init result envelope\n restore Restore a fresh local wallet from a 24-word mnemonic; run sync afterward\n reset Factory-reset local Cogcoin state with interactive retention prompts\n repair Recover bounded wallet/indexer/runtime state\n unlock Clear an explicit wallet lock and unlock for a limited duration\n wallet address Alias for address\n wallet ids Alias for ids\n hooks enable mining Enable a validated custom mining hook\n hooks disable mining Return to built-in mining hooks\n hooks status Show mining hook validation and trust status\n mine Run the miner in the foreground\n mine start Start the miner as a background worker\n mine stop Stop the active background miner\n mine setup Configure the built-in mining provider\n mine setup --output json\n Emit the stable v1 machine-readable mine setup result envelope\n mine status Show mining control-plane health and readiness\n mine log Show recent mining control-plane events\n anchor <domain> Anchor an owned unanchored domain with the Tx1/Tx2 family\n register <domain> [--from <identity>]\n Register a root domain or subdomain\n transfer <domain> --to <btc-target>\n Transfer an unanchored domain to another BTC identity\n sell <domain> <price> List an unanchored domain for sale in COG\n unsell <domain> Clear an active domain listing\n buy <domain> [--from <identity>]\n Buy an unanchored listed domain in COG\n send <amount> --to <btc-target>\n Send COG from one local identity to another BTC target\n claim <lock-id> --preimage <32-byte-hex>\n Claim an active COG lock before timeout\n reclaim <lock-id> Reclaim an expired COG lock as the original locker\n cog lock <amount> --to-domain <domain> (--for <blocks-or-duration> | --until-height <height>) --condition <sha256hex>\n Lock COG to an anchored recipient domain\n wallet status Show detailed wallet-local status and service health\n wallet init Initialize a new local wallet root\n wallet restore Restore a fresh local wallet from a 24-word mnemonic; run sync afterward\n wallet show-mnemonic Reveal the initialized wallet recovery phrase after typed confirmation\n wallet unlock Clear an explicit wallet lock and unlock for a limited duration\n wallet lock Lock the local wallet and disable on-demand unlock\n wallet export <path> Export a portable encrypted wallet archive\n wallet import <path> Import a portable encrypted wallet archive\n address Show the BTC funding identity for this wallet\n ids List locally controlled identities\n balance Show per-identity COG balances\n locks Show locally related active COG locks\n domain list Alias for domains\n domain show <domain> Alias for show <domain>\n domains [--anchored] [--listed] [--mineable]\n Show locally related domains\n show <domain> Show one domain and its local-wallet relationship\n fields <domain> List current fields on a domain\n field <domain> <field> Show one current field value\n field create <domain> <field>\n Create a new anchored field, optionally with an initial value\n field set <domain> <field>\n Update an existing anchored field value\n field clear <domain> <field>\n Clear an existing anchored field value\n rep give <source-domain> <target-domain> <amount>\n Burn COG as anchored-domain reputation support\n rep revoke <source-domain> <target-domain> <amount>\n Revoke visible support without refunding burned COG\n\nOptions:\n --db <path> Override the SQLite database path\n --data-dir <path> Override the managed bitcoin datadir\n --for <duration> Unlock duration like 15m, 2h, or 1d when unlocking explicitly\n --message <text> Founding message text for anchor\n --to <btc-target> Transfer or send target as an address or spk:<hex>\n --from <identity> Resolve sender identity as id:<n>, domain:<name>, address, or spk:<hex>\n --to-domain <domain>\n Recipient domain for cog lock\n --condition <sha256hex>\n 32-byte lock condition hash\n --until-height <height>\n Absolute timeout height for cog lock\n --preimage <32-byte-hex>\n Claim preimage for an active lock\n --review <text> Optional public review text for reputation operations\n --text <utf8> UTF-8 payload text for endpoint or field writes\n --json <json> UTF-8 payload JSON text for endpoint or field writes\n --bytes <spec> Payload bytes as hex:<hex> or @<path>\n --permanent Create the field as permanent\n --format <spec> Advanced field format as raw:<u8>\n --value <spec> Advanced field value as hex:<hex>, @<path>, or utf8:<text>\n --claimable Show only currently claimable locks\n --reclaimable Show only currently reclaimable locks\n --anchored Show only anchored domains\n --listed Show only currently listed domains\n --mineable Show only locally mineable root domains\n --limit <n> Limit list rows (1..1000)\n --all Show all rows for list commands\n --verify Run full custom-hook verification\n --follow Follow mining log output\n --output <mode> Output mode: text, json, or preview-json\n --progress <mode> Progress output mode: auto, tty, or none\n --force-race Allow a visible root registration race\n --yes Approve eligible plain yes/no mutation confirmations non-interactively\n --help Show help\n --version Show package version\n\nQuickstart:\n 1. Run `cogcoin init` to create the wallet.\n 2. Run `cogcoin sync` to bootstrap assumeutxo and the managed Bitcoin/indexer state.\n 3. Run `cogcoin address`, then fund the wallet with about 0.0015 BTC so you can buy a 6+ character domain to start mining and still keep BTC available for mining transaction fees.\n\nExamples:\n cogcoin status --output json\n cogcoin bitcoin status\n cogcoin indexer status\n cogcoin init --output json\n cogcoin wallet address\n cogcoin domain list --mineable\n cogcoin register alpha-child\n cogcoin register weatherbot --from id:1\n cogcoin anchor alpha\n cogcoin buy alpha\n cogcoin buy alpha --from id:1\n cogcoin field create alpha bio --text \"hello\"\n cogcoin rep give alpha beta 10 --review \"great operator\"\n cogcoin hooks status\n cogcoin mine setup --output json\n cogcoin register alpha-child --output preview-json\n cogcoin mine status\n";
|
|
3
3
|
export declare function parseCliArgs(argv: string[]): ParsedCliArgs;
|
package/dist/cli/parse.js
CHANGED
|
@@ -48,6 +48,7 @@ Commands:
|
|
|
48
48
|
wallet status Show detailed wallet-local status and service health
|
|
49
49
|
wallet init Initialize a new local wallet root
|
|
50
50
|
wallet restore Restore a fresh local wallet from a 24-word mnemonic; run sync afterward
|
|
51
|
+
wallet show-mnemonic Reveal the initialized wallet recovery phrase after typed confirmation
|
|
51
52
|
wallet unlock Clear an explicit wallet lock and unlock for a limited duration
|
|
52
53
|
wallet lock Lock the local wallet and disable on-demand unlock
|
|
53
54
|
wallet export <path> Export a portable encrypted wallet archive
|
|
@@ -451,6 +452,11 @@ export function parseCliArgs(argv) {
|
|
|
451
452
|
index += 1;
|
|
452
453
|
continue;
|
|
453
454
|
}
|
|
455
|
+
if (subcommand === "show-mnemonic") {
|
|
456
|
+
command = "wallet-show-mnemonic";
|
|
457
|
+
index += 1;
|
|
458
|
+
continue;
|
|
459
|
+
}
|
|
454
460
|
if (subcommand === "unlock") {
|
|
455
461
|
command = "wallet-unlock";
|
|
456
462
|
index += 1;
|
package/dist/cli/preview-json.js
CHANGED
|
@@ -234,6 +234,7 @@ export function buildResetPreviewData(result) {
|
|
|
234
234
|
trackedProcessKinds: result.trackedProcessKinds,
|
|
235
235
|
willDeleteOsSecrets: result.willDeleteOsSecrets,
|
|
236
236
|
bootstrapSnapshot: result.bootstrapSnapshot,
|
|
237
|
+
bitcoinDataDir: result.bitcoinDataDir,
|
|
237
238
|
walletPrompt: result.walletPrompt,
|
|
238
239
|
},
|
|
239
240
|
operation: {
|
|
@@ -241,6 +242,7 @@ export function buildResetPreviewData(result) {
|
|
|
241
242
|
confirmationPhrase: result.confirmationPhrase,
|
|
242
243
|
walletPrompt: result.walletPrompt,
|
|
243
244
|
bootstrapSnapshot: result.bootstrapSnapshot,
|
|
245
|
+
bitcoinDataDir: result.bitcoinDataDir,
|
|
244
246
|
trackedProcessKinds: result.trackedProcessKinds,
|
|
245
247
|
willDeleteOsSecrets: result.willDeleteOsSecrets,
|
|
246
248
|
removedPaths: result.removedPaths,
|
package/dist/cli/runner.js
CHANGED
|
@@ -77,6 +77,7 @@ export async function runCli(argv, contextOverrides = {}) {
|
|
|
77
77
|
|| parsed.command === "wallet-import"
|
|
78
78
|
|| parsed.command === "wallet-init"
|
|
79
79
|
|| parsed.command === "wallet-restore"
|
|
80
|
+
|| parsed.command === "wallet-show-mnemonic"
|
|
80
81
|
|| parsed.command === "wallet-unlock"
|
|
81
82
|
|| parsed.command === "wallet-lock") {
|
|
82
83
|
return runWalletAdminCommand(parsed, context);
|
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, unlockWallet } from "../wallet/lifecycle.js";
|
|
8
|
+
import type { exportWallet, WalletPrompter, importWallet, initializeWallet, 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
|
-
import { loadWalletState } from "../wallet/state/storage.js";
|
|
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
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";
|
|
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-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" | "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";
|
|
19
19
|
export interface WritableLike {
|
|
20
20
|
isTTY?: boolean;
|
|
21
21
|
write(chunk: string): void;
|
|
@@ -93,6 +93,7 @@ export interface CliRunnerContext {
|
|
|
93
93
|
store: ClientStoreAdapter;
|
|
94
94
|
databasePath?: string;
|
|
95
95
|
dataDir?: string;
|
|
96
|
+
walletRootId?: string;
|
|
96
97
|
progressOutput?: ProgressOutput;
|
|
97
98
|
}) => Promise<ManagedClientLike>;
|
|
98
99
|
attachManagedBitcoindService?: typeof attachOrStartManagedBitcoindService;
|
|
@@ -106,6 +107,7 @@ export interface CliRunnerContext {
|
|
|
106
107
|
inspectPassiveClientStatus?: typeof inspectPassiveClientStatus;
|
|
107
108
|
openWalletReadContext?: typeof openWalletReadContext;
|
|
108
109
|
loadWalletState?: typeof loadWalletState;
|
|
110
|
+
loadRawWalletStateEnvelope?: typeof loadRawWalletStateEnvelope;
|
|
109
111
|
loadUnlockSession?: typeof loadUnlockSession;
|
|
110
112
|
loadWalletExplicitLock?: typeof loadWalletExplicitLock;
|
|
111
113
|
initializeWallet?: typeof initializeWallet;
|
|
@@ -113,6 +115,7 @@ export interface CliRunnerContext {
|
|
|
113
115
|
previewResetWallet?: typeof previewResetWallet;
|
|
114
116
|
exportWallet?: typeof exportWallet;
|
|
115
117
|
importWallet?: typeof importWallet;
|
|
118
|
+
showWalletMnemonic?: typeof showWalletMnemonic;
|
|
116
119
|
unlockWallet?: typeof unlockWallet;
|
|
117
120
|
lockWallet?: typeof lockWallet;
|
|
118
121
|
registerDomain?: typeof registerDomain;
|
package/dist/cli/types.js
CHANGED
|
@@ -5,4 +5,4 @@ import { attachOrStartManagedBitcoindService, probeManagedBitcoindService, stopM
|
|
|
5
5
|
import { openSqliteStore } from "../sqlite/index.js";
|
|
6
6
|
import { loadWalletExplicitLock } from "../wallet/state/explicit-lock.js";
|
|
7
7
|
import { loadUnlockSession } from "../wallet/state/session.js";
|
|
8
|
-
import { loadWalletState } from "../wallet/state/storage.js";
|
|
8
|
+
import { loadRawWalletStateEnvelope, loadWalletState } from "../wallet/state/storage.js";
|
|
@@ -329,24 +329,144 @@ function appendWalletAvailability(lines, context) {
|
|
|
329
329
|
lines.push(`Mutation note: ${mutationRecommendation}`);
|
|
330
330
|
}
|
|
331
331
|
}
|
|
332
|
-
|
|
332
|
+
function overviewEntry(text, ok) {
|
|
333
|
+
return { text, ok };
|
|
334
|
+
}
|
|
335
|
+
function formatOverviewSection(header, entries) {
|
|
336
|
+
return [header, ...entries.map((entry) => `${entry.ok ? "✓" : "✗"} ${entry.text}`)].join("\n");
|
|
337
|
+
}
|
|
338
|
+
function isMiningOverviewOk(mining) {
|
|
339
|
+
return mining.runtime.bitcoindHealth === "ready"
|
|
340
|
+
&& mining.runtime.nodeHealth === "synced"
|
|
341
|
+
&& mining.runtime.indexerHealth === "synced"
|
|
342
|
+
&& mining.runtime.miningState !== "repair-required"
|
|
343
|
+
&& mining.runtime.miningState !== "paused-stale"
|
|
344
|
+
&& !(mining.runtime.miningState === "paused" && mining.runtime.liveMiningFamilyInMempool === true);
|
|
345
|
+
}
|
|
346
|
+
function buildOverviewPathsSection(context) {
|
|
347
|
+
return [
|
|
348
|
+
overviewEntry(`DB path: ${context.databasePath}`, true),
|
|
349
|
+
overviewEntry(`Bitcoin datadir: ${context.dataDir}`, true),
|
|
350
|
+
];
|
|
351
|
+
}
|
|
352
|
+
function buildOverviewWalletSection(context) {
|
|
353
|
+
const walletRoot = context.model?.walletRootId ?? context.localState.walletRootId ?? context.nodeStatus?.walletRootId ?? "none";
|
|
333
354
|
const lines = [
|
|
334
|
-
|
|
335
|
-
`
|
|
336
|
-
`
|
|
355
|
+
overviewEntry(`State: ${context.localState.availability}`, context.localState.availability === "ready"),
|
|
356
|
+
overviewEntry(`Root: ${walletRoot}`, walletRoot !== "none"),
|
|
357
|
+
overviewEntry(`Unlock: ${formatUnlockExpiry(context.localState.unlockUntilUnixMs)}`, context.localState.unlockUntilUnixMs !== null),
|
|
337
358
|
];
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
if (context.model !== null) {
|
|
341
|
-
lines.push(`Local identities: ${context.model.identities.length}`);
|
|
342
|
-
lines.push(`Locally related domains: ${context.model.domains.length}`);
|
|
343
|
-
lines.push(`Read-only identities: ${context.model.readOnlyIdentityCount}`);
|
|
359
|
+
if (context.localState.message !== null) {
|
|
360
|
+
lines.push(overviewEntry(`Note: ${context.localState.message}`, false));
|
|
344
361
|
}
|
|
345
|
-
|
|
346
|
-
|
|
362
|
+
const nodeStatus = context.nodeStatus;
|
|
363
|
+
const replica = nodeStatus?.walletReplica ?? null;
|
|
364
|
+
if (replica !== null) {
|
|
365
|
+
lines.push(overviewEntry(`Managed Core wallet: ${replica.proofStatus ?? "not-proven"}`, replica.proofStatus === "ready"));
|
|
347
366
|
}
|
|
348
|
-
|
|
349
|
-
|
|
367
|
+
if (nodeStatus?.walletReplicaMessage) {
|
|
368
|
+
lines.push(overviewEntry(`Managed Core note: ${nodeStatus.walletReplicaMessage}`, replica?.proofStatus === "ready"));
|
|
369
|
+
}
|
|
370
|
+
return lines;
|
|
371
|
+
}
|
|
372
|
+
function buildOverviewServicesSection(context) {
|
|
373
|
+
const bitcoindOk = context.bitcoind.health === "ready";
|
|
374
|
+
const nodeOk = context.nodeHealth === "synced";
|
|
375
|
+
const indexerOk = context.indexer.health === "synced";
|
|
376
|
+
const lines = [
|
|
377
|
+
overviewEntry(`Managed bitcoind: ${formatServiceHealth(context.bitcoind.health)}`, bitcoindOk),
|
|
378
|
+
];
|
|
379
|
+
if (context.bitcoind.message !== null) {
|
|
380
|
+
lines.push(overviewEntry(`Managed bitcoind note: ${context.bitcoind.message}`, bitcoindOk));
|
|
381
|
+
}
|
|
382
|
+
lines.push(overviewEntry(`Bitcoin service: ${formatServiceHealth(context.nodeHealth)}`, nodeOk));
|
|
383
|
+
if (context.nodeStatus !== null) {
|
|
384
|
+
lines.push(overviewEntry(`Bitcoin best height: ${formatMaybe(context.nodeStatus.nodeBestHeight)}`, nodeOk));
|
|
385
|
+
lines.push(overviewEntry(`Bitcoin headers: ${formatMaybe(context.nodeStatus.nodeHeaderHeight)}`, nodeOk));
|
|
386
|
+
}
|
|
387
|
+
if (context.nodeMessage !== null) {
|
|
388
|
+
lines.push(overviewEntry(`Bitcoin note: ${context.nodeMessage}`, nodeOk));
|
|
389
|
+
}
|
|
390
|
+
lines.push(overviewEntry(`Indexer service: ${formatServiceHealth(context.indexer.health)}`, indexerOk));
|
|
391
|
+
lines.push(overviewEntry(`Indexer truth source: ${formatIndexerTruthSource(context.indexer.source)}`, indexerOk));
|
|
392
|
+
if (context.indexer.daemonInstanceId !== null && context.indexer.daemonInstanceId !== undefined) {
|
|
393
|
+
lines.push(overviewEntry(`Indexer daemon instance: ${context.indexer.daemonInstanceId}`, indexerOk));
|
|
394
|
+
}
|
|
395
|
+
if (context.indexer.snapshotSeq !== null && context.indexer.snapshotSeq !== undefined) {
|
|
396
|
+
lines.push(overviewEntry(`Indexer snapshot sequence: ${context.indexer.snapshotSeq}`, indexerOk));
|
|
397
|
+
}
|
|
398
|
+
if (context.indexer.status?.reorgDepth !== null && context.indexer.status?.reorgDepth !== undefined) {
|
|
399
|
+
lines.push(overviewEntry(`Indexer reorg depth: ${context.indexer.status.reorgDepth}`, indexerOk));
|
|
400
|
+
}
|
|
401
|
+
lines.push(overviewEntry(`Indexer tip height: ${context.indexer.snapshotTip === null ? "unavailable" : context.indexer.snapshotTip.height}`, indexerOk));
|
|
402
|
+
if (context.indexer.message !== null) {
|
|
403
|
+
lines.push(overviewEntry(`Indexer note: ${context.indexer.message}`, indexerOk));
|
|
404
|
+
}
|
|
405
|
+
if (context.mining !== undefined) {
|
|
406
|
+
const miningOk = isMiningOverviewOk(context.mining);
|
|
407
|
+
lines.push(overviewEntry(`Mining: ${formatMiningSummaryLine(context.mining)}`, miningOk));
|
|
408
|
+
if (context.mining.runtime.note !== null) {
|
|
409
|
+
lines.push(overviewEntry(`Mining note: ${context.mining.runtime.note}`, miningOk));
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
return lines;
|
|
413
|
+
}
|
|
414
|
+
function buildOverviewLocalInventorySection(context) {
|
|
415
|
+
if (context.model === null) {
|
|
416
|
+
return [overviewEntry("Status: Wallet-derived sections unavailable", false)];
|
|
417
|
+
}
|
|
418
|
+
return [
|
|
419
|
+
overviewEntry(`Local identities: ${context.model.identities.length}`, true),
|
|
420
|
+
overviewEntry(`Locally related domains: ${context.model.domains.length}`, true),
|
|
421
|
+
overviewEntry(`Read-only identities: ${context.model.readOnlyIdentityCount}`, true),
|
|
422
|
+
];
|
|
423
|
+
}
|
|
424
|
+
function buildOverviewPendingWorkSection(context) {
|
|
425
|
+
const pendingFamilies = (context.localState.state?.proactiveFamilies ?? [])
|
|
426
|
+
.filter((family) => (family.type === "anchor" || family.type === "field")
|
|
427
|
+
&& family.status !== "confirmed"
|
|
428
|
+
&& family.status !== "canceled");
|
|
429
|
+
const pendingMutations = (context.localState.state?.pendingMutations ?? [])
|
|
430
|
+
.filter((mutation) => mutation.status !== "confirmed" && mutation.status !== "canceled");
|
|
431
|
+
if (pendingFamilies.length === 0 && pendingMutations.length === 0) {
|
|
432
|
+
return [overviewEntry("Status: none", true)];
|
|
433
|
+
}
|
|
434
|
+
const lines = [];
|
|
435
|
+
for (const family of pendingFamilies) {
|
|
436
|
+
const label = family.type === "field"
|
|
437
|
+
? `Field family: ${family.domainName ?? "unknown"}.${family.fieldName ?? "unknown"}`
|
|
438
|
+
: `Anchor family: ${family.domainName ?? "unknown"}`;
|
|
439
|
+
lines.push(overviewEntry(`${label} ${family.status}${family.currentStep === null || family.currentStep === undefined ? "" : ` step ${family.currentStep}`}${family.reservedDedicatedIndex == null ? "" : ` index ${family.reservedDedicatedIndex}`}`, false));
|
|
440
|
+
}
|
|
441
|
+
for (const mutation of pendingMutations) {
|
|
442
|
+
lines.push(overviewEntry(`Mutation: ${formatPendingMutationSummaryLabel(mutation)} ${mutation.status} sender spk:${mutation.senderScriptPubKeyHex}${mutation.priceCogtoshi === undefined || mutation.priceCogtoshi === null ? "" : ` price ${formatCogAmount(mutation.priceCogtoshi)}`}${mutation.amountCogtoshi === undefined || mutation.amountCogtoshi === null ? "" : ` amount ${formatCogAmount(mutation.amountCogtoshi)}`}${isReputationMutation(mutation) ? "" : mutation.recipientDomainName === undefined || mutation.recipientDomainName === null ? "" : ` domain ${mutation.recipientDomainName}`}${mutation.lockId === undefined || mutation.lockId === null ? "" : ` lock ${mutation.lockId}`}${mutation.recipientScriptPubKeyHex === undefined || mutation.recipientScriptPubKeyHex === null ? "" : ` recipient spk:${mutation.recipientScriptPubKeyHex}`}${mutation.kind === "endpoint" ? (mutation.endpointValueHex === "" ? " endpoint clear" : ` endpoint-bytes ${(mutation.endpointValueHex?.length ?? 0) / 2}`) : ""}${mutation.kind === "field-create" || mutation.kind === "field-set" ? ` format ${formatFieldFormat(mutation.fieldFormat ?? 0)}` : ""}${mutation.kind === "field-clear" ? " clear" : ""}${mutation.reviewPayloadHex === undefined || mutation.reviewPayloadHex === null ? "" : " review"}`, false));
|
|
443
|
+
}
|
|
444
|
+
return lines;
|
|
445
|
+
}
|
|
446
|
+
function getOverviewNextStep(context) {
|
|
447
|
+
const repairRecommendation = getRepairRecommendation(context);
|
|
448
|
+
if (repairRecommendation !== null) {
|
|
449
|
+
return repairRecommendation;
|
|
450
|
+
}
|
|
451
|
+
if (getBootstrapSyncNextStep(context) !== null) {
|
|
452
|
+
return "Run `cogcoin sync` to bootstrap assumeutxo and the managed Bitcoin/indexer state.";
|
|
453
|
+
}
|
|
454
|
+
return getMutationRecommendation(context);
|
|
455
|
+
}
|
|
456
|
+
export function formatWalletOverviewReport(context) {
|
|
457
|
+
const parts = [
|
|
458
|
+
"\n⛭ Cogcoin Status ⛭",
|
|
459
|
+
formatOverviewSection("Paths", buildOverviewPathsSection(context)),
|
|
460
|
+
formatOverviewSection("Wallet", buildOverviewWalletSection(context)),
|
|
461
|
+
formatOverviewSection("Services", buildOverviewServicesSection(context)),
|
|
462
|
+
formatOverviewSection("Local Inventory", buildOverviewLocalInventorySection(context)),
|
|
463
|
+
formatOverviewSection("Pending Work", buildOverviewPendingWorkSection(context)),
|
|
464
|
+
];
|
|
465
|
+
const nextStep = getOverviewNextStep(context);
|
|
466
|
+
if (nextStep !== null) {
|
|
467
|
+
parts.push(`Next step: ${nextStep}`);
|
|
468
|
+
}
|
|
469
|
+
return parts.join("\n\n");
|
|
350
470
|
}
|
|
351
471
|
export function formatDetailedWalletStatusReport(context) {
|
|
352
472
|
const lines = ["Cogcoin Wallet Status"];
|
|
@@ -156,6 +156,12 @@ export declare function initializeWallet(options: {
|
|
|
156
156
|
attachService?: typeof attachOrStartManagedBitcoindService;
|
|
157
157
|
rpcFactory?: (config: Parameters<typeof createRpcClient>[0]) => WalletLifecycleRpcClient;
|
|
158
158
|
}): Promise<WalletInitializationResult>;
|
|
159
|
+
export declare function showWalletMnemonic(options: {
|
|
160
|
+
provider?: WalletSecretProvider;
|
|
161
|
+
prompter: WalletPrompter;
|
|
162
|
+
nowUnixMs?: number;
|
|
163
|
+
paths?: WalletRuntimePaths;
|
|
164
|
+
}): Promise<void>;
|
|
159
165
|
export declare function unlockWallet(options?: {
|
|
160
166
|
provider?: WalletSecretProvider;
|
|
161
167
|
nowUnixMs?: number;
|
package/dist/wallet/lifecycle.js
CHANGED
|
@@ -3,7 +3,7 @@ import { access, constants, mkdir, readFile, readdir, rename, rm } from "node:fs
|
|
|
3
3
|
import { dirname, join } from "node:path";
|
|
4
4
|
import { openClient } from "../client.js";
|
|
5
5
|
import { attachOrStartIndexerDaemon, probeIndexerDaemon, readSnapshotWithRetry, } from "../bitcoind/indexer-daemon.js";
|
|
6
|
-
import { attachOrStartManagedBitcoindService, createManagedWalletReplica, probeManagedBitcoindService, } from "../bitcoind/service.js";
|
|
6
|
+
import { attachOrStartManagedBitcoindService, createManagedWalletReplica, probeManagedBitcoindService, withClaimedUninitializedManagedRuntime, } from "../bitcoind/service.js";
|
|
7
7
|
import { resolveManagedServicePaths } from "../bitcoind/service-paths.js";
|
|
8
8
|
import { createRpcClient } from "../bitcoind/node.js";
|
|
9
9
|
import { openSqliteStore } from "../sqlite/index.js";
|
|
@@ -19,9 +19,10 @@ import { loadMiningRuntimeStatus, saveMiningRuntimeStatus } from "./mining/runti
|
|
|
19
19
|
import { normalizeMiningStateRecord } from "./mining/state.js";
|
|
20
20
|
import { renderWalletMnemonicRevealArt } from "./mnemonic-art.js";
|
|
21
21
|
import { clearWalletExplicitLock, loadWalletExplicitLock, saveWalletExplicitLock, } from "./state/explicit-lock.js";
|
|
22
|
+
import { clearWalletPendingInitializationState, loadWalletPendingInitializationStateOrNull, saveWalletPendingInitializationState, } from "./state/pending-init.js";
|
|
22
23
|
import { clearUnlockSession, loadUnlockSession, saveUnlockSession } from "./state/session.js";
|
|
23
|
-
import { createDefaultWalletSecretProvider, createWalletRootId, createWalletSecretReference, } from "./state/provider.js";
|
|
24
|
-
import { loadWalletState, saveWalletState } from "./state/storage.js";
|
|
24
|
+
import { createDefaultWalletSecretProvider, createWalletPendingInitSecretReference, createWalletRootId, createWalletSecretReference, } from "./state/provider.js";
|
|
25
|
+
import { extractWalletRootIdHintFromWalletStateEnvelope, loadRawWalletStateEnvelope, loadWalletState, saveWalletState, } from "./state/storage.js";
|
|
25
26
|
export const DEFAULT_UNLOCK_DURATION_MS = 15 * 60 * 1000;
|
|
26
27
|
export { previewResetWallet, resetWallet, } from "./reset.js";
|
|
27
28
|
function sanitizeWalletName(walletRootId) {
|
|
@@ -47,20 +48,52 @@ async function readJsonFileOrNull(path) {
|
|
|
47
48
|
return null;
|
|
48
49
|
}
|
|
49
50
|
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
}
|
|
55
|
-
return readJsonFileOrNull(paths.walletStateBackupPath);
|
|
51
|
+
function resolvePendingInitializationStoragePaths(paths) {
|
|
52
|
+
return {
|
|
53
|
+
primaryPath: paths.walletInitPendingPath,
|
|
54
|
+
backupPath: paths.walletInitPendingBackupPath,
|
|
55
|
+
};
|
|
56
56
|
}
|
|
57
|
-
function
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
57
|
+
async function clearPendingInitialization(paths, provider) {
|
|
58
|
+
await clearWalletPendingInitializationState(resolvePendingInitializationStoragePaths(paths), {
|
|
59
|
+
provider,
|
|
60
|
+
secretReference: createWalletPendingInitSecretReference(paths.stateRoot),
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
async function loadOrCreatePendingInitializationMaterial(options) {
|
|
64
|
+
try {
|
|
65
|
+
const loaded = await loadWalletPendingInitializationStateOrNull(resolvePendingInitializationStoragePaths(options.paths), {
|
|
66
|
+
provider: options.provider,
|
|
67
|
+
});
|
|
68
|
+
if (loaded !== null) {
|
|
69
|
+
return deriveWalletMaterialFromMnemonic(loaded.state.mnemonic.phrase);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
await clearPendingInitialization(options.paths, options.provider);
|
|
74
|
+
}
|
|
75
|
+
const material = generateWalletMaterial();
|
|
76
|
+
const secretReference = createWalletPendingInitSecretReference(options.paths.stateRoot);
|
|
77
|
+
const pendingState = {
|
|
78
|
+
schemaVersion: 1,
|
|
79
|
+
createdAtUnixMs: options.nowUnixMs,
|
|
80
|
+
mnemonic: {
|
|
81
|
+
phrase: material.mnemonic.phrase,
|
|
82
|
+
language: material.mnemonic.language,
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
await options.provider.storeSecret(secretReference.keyId, randomBytes(32));
|
|
86
|
+
try {
|
|
87
|
+
await saveWalletPendingInitializationState(resolvePendingInitializationStoragePaths(options.paths), pendingState, {
|
|
88
|
+
provider: options.provider,
|
|
89
|
+
secretReference,
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
catch (error) {
|
|
93
|
+
await options.provider.deleteSecret(secretReference.keyId).catch(() => undefined);
|
|
94
|
+
throw error;
|
|
62
95
|
}
|
|
63
|
-
return
|
|
96
|
+
return material;
|
|
64
97
|
}
|
|
65
98
|
function createInitialWalletState(options) {
|
|
66
99
|
return {
|
|
@@ -351,10 +384,10 @@ async function promptForRestoreMnemonic(prompter) {
|
|
|
351
384
|
}
|
|
352
385
|
return phrase;
|
|
353
386
|
}
|
|
354
|
-
async function confirmTypedAcknowledgement(prompter, expected, message) {
|
|
387
|
+
async function confirmTypedAcknowledgement(prompter, expected, message, errorCode = "wallet_typed_confirmation_rejected") {
|
|
355
388
|
const answer = (await prompter.prompt(message)).trim();
|
|
356
389
|
if (answer !== expected) {
|
|
357
|
-
throw new Error(
|
|
390
|
+
throw new Error(errorCode);
|
|
358
391
|
}
|
|
359
392
|
}
|
|
360
393
|
async function confirmRestoreReplacement(prompter) {
|
|
@@ -780,11 +813,29 @@ export function parseUnlockDurationToMs(raw) {
|
|
|
780
813
|
}
|
|
781
814
|
return duration;
|
|
782
815
|
}
|
|
783
|
-
async function ensureWalletNotInitialized(paths) {
|
|
816
|
+
async function ensureWalletNotInitialized(paths, provider) {
|
|
784
817
|
if (await pathExists(paths.walletStatePath) || await pathExists(paths.walletStateBackupPath)) {
|
|
818
|
+
await clearPendingInitialization(paths, provider);
|
|
785
819
|
throw new Error("wallet_already_initialized");
|
|
786
820
|
}
|
|
787
821
|
}
|
|
822
|
+
function isWalletSecretAccessError(error) {
|
|
823
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
824
|
+
return message === "wallet_envelope_missing_secret_provider"
|
|
825
|
+
|| message.startsWith("wallet_secret_missing_")
|
|
826
|
+
|| message.startsWith("wallet_secret_provider_");
|
|
827
|
+
}
|
|
828
|
+
function writeMnemonicReveal(prompter, phrase, introLines) {
|
|
829
|
+
const words = phrase.trim().split(/\s+/);
|
|
830
|
+
for (const line of introLines) {
|
|
831
|
+
prompter.writeLine(line);
|
|
832
|
+
}
|
|
833
|
+
for (const line of renderWalletMnemonicRevealArt(words)) {
|
|
834
|
+
prompter.writeLine(line);
|
|
835
|
+
}
|
|
836
|
+
prompter.writeLine("Single-line copy:");
|
|
837
|
+
prompter.writeLine(phrase);
|
|
838
|
+
}
|
|
788
839
|
async function confirmMnemonic(prompter, words) {
|
|
789
840
|
const challenge = createMnemonicConfirmationChallenge(words);
|
|
790
841
|
for (const entry of challenge) {
|
|
@@ -1079,17 +1130,19 @@ export async function initializeWallet(options) {
|
|
|
1079
1130
|
walletRootId: null,
|
|
1080
1131
|
});
|
|
1081
1132
|
try {
|
|
1082
|
-
await ensureWalletNotInitialized(paths);
|
|
1083
|
-
const material =
|
|
1133
|
+
await ensureWalletNotInitialized(paths, provider);
|
|
1134
|
+
const material = await loadOrCreatePendingInitializationMaterial({
|
|
1135
|
+
provider,
|
|
1136
|
+
paths,
|
|
1137
|
+
nowUnixMs,
|
|
1138
|
+
});
|
|
1084
1139
|
let mnemonicRevealed = false;
|
|
1085
|
-
options.prompter.
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
options.prompter.writeLine("Single-line copy:");
|
|
1092
|
-
options.prompter.writeLine(material.mnemonic.phrase);
|
|
1140
|
+
writeMnemonicReveal(options.prompter, material.mnemonic.phrase, [
|
|
1141
|
+
"Cogcoin Wallet Initialization",
|
|
1142
|
+
"Write down this 24-word recovery phrase.",
|
|
1143
|
+
"The same phrase will be shown again until confirmation succeeds:",
|
|
1144
|
+
"",
|
|
1145
|
+
]);
|
|
1093
1146
|
mnemonicRevealed = true;
|
|
1094
1147
|
try {
|
|
1095
1148
|
await confirmMnemonic(options.prompter, material.mnemonic.words);
|
|
@@ -1112,20 +1165,26 @@ export async function initializeWallet(options) {
|
|
|
1112
1165
|
material,
|
|
1113
1166
|
internalCoreWalletPassphrase,
|
|
1114
1167
|
});
|
|
1115
|
-
await
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
},
|
|
1119
|
-
|
|
1120
|
-
|
|
1168
|
+
const verifiedState = await withClaimedUninitializedManagedRuntime({
|
|
1169
|
+
dataDir: options.dataDir,
|
|
1170
|
+
walletRootId,
|
|
1171
|
+
}, async () => {
|
|
1172
|
+
await saveWalletState({
|
|
1173
|
+
primaryPath: paths.walletStatePath,
|
|
1174
|
+
backupPath: paths.walletStateBackupPath,
|
|
1175
|
+
}, initialState, {
|
|
1176
|
+
provider,
|
|
1177
|
+
secretReference,
|
|
1178
|
+
});
|
|
1179
|
+
return importDescriptorIntoManagedCoreWallet(initialState, provider, paths, options.dataDir, nowUnixMs, options.attachService, options.rpcFactory);
|
|
1121
1180
|
});
|
|
1122
|
-
const verifiedState = await importDescriptorIntoManagedCoreWallet(initialState, provider, paths, options.dataDir, nowUnixMs, options.attachService, options.rpcFactory);
|
|
1123
1181
|
const unlockUntilUnixMs = nowUnixMs + unlockDurationMs;
|
|
1124
1182
|
await clearWalletExplicitLock(paths.walletExplicitLockPath);
|
|
1125
1183
|
await saveUnlockSession(paths.walletUnlockSessionPath, createUnlockSession(verifiedState, unlockUntilUnixMs, secretReference.keyId, nowUnixMs), {
|
|
1126
1184
|
provider,
|
|
1127
1185
|
secretReference,
|
|
1128
1186
|
});
|
|
1187
|
+
await clearPendingInitialization(paths, provider);
|
|
1129
1188
|
return {
|
|
1130
1189
|
walletRootId,
|
|
1131
1190
|
fundingAddress: verifiedState.funding.address,
|
|
@@ -1137,6 +1196,71 @@ export async function initializeWallet(options) {
|
|
|
1137
1196
|
await controlLock.release();
|
|
1138
1197
|
}
|
|
1139
1198
|
}
|
|
1199
|
+
export async function showWalletMnemonic(options) {
|
|
1200
|
+
if (!options.prompter.isInteractive) {
|
|
1201
|
+
throw new Error("wallet_show_mnemonic_requires_tty");
|
|
1202
|
+
}
|
|
1203
|
+
const provider = options.provider ?? createDefaultWalletSecretProvider();
|
|
1204
|
+
const nowUnixMs = options.nowUnixMs ?? Date.now();
|
|
1205
|
+
const paths = options.paths ?? resolveWalletRuntimePathsForTesting();
|
|
1206
|
+
const controlLock = await acquireFileLock(paths.walletControlLockPath, {
|
|
1207
|
+
purpose: "wallet-show-mnemonic",
|
|
1208
|
+
walletRootId: null,
|
|
1209
|
+
});
|
|
1210
|
+
try {
|
|
1211
|
+
const [hasPrimaryStateFile, hasBackupStateFile] = await Promise.all([
|
|
1212
|
+
pathExists(paths.walletStatePath),
|
|
1213
|
+
pathExists(paths.walletStateBackupPath),
|
|
1214
|
+
]);
|
|
1215
|
+
if (!hasPrimaryStateFile && !hasBackupStateFile) {
|
|
1216
|
+
throw new Error("wallet_uninitialized");
|
|
1217
|
+
}
|
|
1218
|
+
const unlocked = await loadOrAutoUnlockWalletState({
|
|
1219
|
+
provider,
|
|
1220
|
+
nowUnixMs,
|
|
1221
|
+
paths,
|
|
1222
|
+
controlLockHeld: true,
|
|
1223
|
+
});
|
|
1224
|
+
if (unlocked === null) {
|
|
1225
|
+
try {
|
|
1226
|
+
await loadWalletState({
|
|
1227
|
+
primaryPath: paths.walletStatePath,
|
|
1228
|
+
backupPath: paths.walletStateBackupPath,
|
|
1229
|
+
}, {
|
|
1230
|
+
provider,
|
|
1231
|
+
});
|
|
1232
|
+
}
|
|
1233
|
+
catch (error) {
|
|
1234
|
+
if (isWalletSecretAccessError(error)) {
|
|
1235
|
+
throw new Error("wallet_locked");
|
|
1236
|
+
}
|
|
1237
|
+
throw new Error("local-state-corrupt");
|
|
1238
|
+
}
|
|
1239
|
+
throw new Error("wallet_locked");
|
|
1240
|
+
}
|
|
1241
|
+
await confirmTypedAcknowledgement(options.prompter, "show mnemonic", "Type \"show mnemonic\" to continue: ", "wallet_show_mnemonic_typed_ack_required");
|
|
1242
|
+
let mnemonicRevealed = false;
|
|
1243
|
+
writeMnemonicReveal(options.prompter, unlocked.state.mnemonic.phrase, [
|
|
1244
|
+
"Cogcoin Wallet Recovery Phrase",
|
|
1245
|
+
"This 24-word recovery phrase controls the wallet.",
|
|
1246
|
+
"",
|
|
1247
|
+
]);
|
|
1248
|
+
mnemonicRevealed = true;
|
|
1249
|
+
try {
|
|
1250
|
+
await options.prompter.prompt("Press Enter to clear the recovery phrase from the screen: ");
|
|
1251
|
+
}
|
|
1252
|
+
finally {
|
|
1253
|
+
if (mnemonicRevealed) {
|
|
1254
|
+
await Promise.resolve()
|
|
1255
|
+
.then(() => options.prompter.clearSensitiveDisplay?.("mnemonic-reveal"))
|
|
1256
|
+
.catch(() => undefined);
|
|
1257
|
+
}
|
|
1258
|
+
}
|
|
1259
|
+
}
|
|
1260
|
+
finally {
|
|
1261
|
+
await controlLock.release();
|
|
1262
|
+
}
|
|
1263
|
+
}
|
|
1140
1264
|
export async function unlockWallet(options = {}) {
|
|
1141
1265
|
const provider = options.provider ?? createDefaultWalletSecretProvider();
|
|
1142
1266
|
const nowUnixMs = options.nowUnixMs ?? Date.now();
|
|
@@ -1290,6 +1414,7 @@ export async function importWallet(options) {
|
|
|
1290
1414
|
const replacementStateExists = await pathExists(paths.walletStatePath) || await pathExists(paths.walletStateBackupPath);
|
|
1291
1415
|
const importedWalletDir = join(options.dataDir, "wallets", sanitizeWalletName(payload.walletRootId));
|
|
1292
1416
|
const replacementCoreWalletExists = await pathExists(importedWalletDir);
|
|
1417
|
+
await clearPendingInitialization(paths, provider);
|
|
1293
1418
|
if (replacementStateExists || replacementCoreWalletExists) {
|
|
1294
1419
|
await confirmTypedAcknowledgement(options.prompter, "IMPORT", "Type IMPORT to replace the existing local wallet state and managed Core wallet replica: ");
|
|
1295
1420
|
}
|
|
@@ -1333,6 +1458,7 @@ export async function importWallet(options) {
|
|
|
1333
1458
|
provider,
|
|
1334
1459
|
secretReference,
|
|
1335
1460
|
});
|
|
1461
|
+
await clearPendingInitialization(paths, provider);
|
|
1336
1462
|
if (previousWalletRootId !== null && previousWalletRootId !== payload.walletRootId) {
|
|
1337
1463
|
await provider.deleteSecret(createWalletSecretReference(previousWalletRootId).keyId).catch(() => undefined);
|
|
1338
1464
|
}
|
|
@@ -1366,16 +1492,20 @@ export async function restoreWalletFromMnemonic(options) {
|
|
|
1366
1492
|
walletRootId: null,
|
|
1367
1493
|
});
|
|
1368
1494
|
try {
|
|
1369
|
-
const rawEnvelope = await
|
|
1495
|
+
const rawEnvelope = await loadRawWalletStateEnvelope({
|
|
1496
|
+
primaryPath: paths.walletStatePath,
|
|
1497
|
+
backupPath: paths.walletStateBackupPath,
|
|
1498
|
+
});
|
|
1370
1499
|
const replacementStateExists = rawEnvelope !== null
|
|
1371
1500
|
|| await pathExists(paths.walletStatePath)
|
|
1372
1501
|
|| await pathExists(paths.walletStateBackupPath);
|
|
1373
1502
|
const replacementCoreWalletExists = await detectExistingManagedWalletReplica(options.dataDir);
|
|
1374
1503
|
const mnemonicPhrase = await promptForRestoreMnemonic(options.prompter);
|
|
1504
|
+
await clearPendingInitialization(paths, provider);
|
|
1375
1505
|
if (replacementStateExists || replacementCoreWalletExists) {
|
|
1376
1506
|
await confirmRestoreReplacement(options.prompter);
|
|
1377
1507
|
}
|
|
1378
|
-
let previousWalletRootId =
|
|
1508
|
+
let previousWalletRootId = extractWalletRootIdHintFromWalletStateEnvelope(rawEnvelope?.envelope ?? null);
|
|
1379
1509
|
try {
|
|
1380
1510
|
const loaded = await loadWalletState({
|
|
1381
1511
|
primaryPath: paths.walletStatePath,
|
|
@@ -1425,6 +1555,7 @@ export async function restoreWalletFromMnemonic(options) {
|
|
|
1425
1555
|
provider,
|
|
1426
1556
|
secretReference,
|
|
1427
1557
|
});
|
|
1558
|
+
await clearPendingInitialization(paths, provider);
|
|
1428
1559
|
if (previousWalletRootId !== null && previousWalletRootId !== walletRootId) {
|
|
1429
1560
|
try {
|
|
1430
1561
|
await clearPreviousManagedWalletRuntime({
|