@cogcoin/client 1.1.5 → 1.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/bitcoind/indexer-daemon.d.ts +3 -7
- package/dist/bitcoind/indexer-daemon.js +43 -158
- package/dist/bitcoind/managed-runtime/bitcoind-policy.d.ts +16 -0
- package/dist/bitcoind/managed-runtime/bitcoind-policy.js +177 -0
- package/dist/bitcoind/managed-runtime/indexer-policy.d.ts +34 -0
- package/dist/bitcoind/managed-runtime/indexer-policy.js +200 -0
- package/dist/bitcoind/managed-runtime/status.d.ts +11 -0
- package/dist/bitcoind/managed-runtime/status.js +59 -0
- package/dist/bitcoind/managed-runtime/types.d.ts +37 -0
- package/dist/bitcoind/managed-runtime/types.js +1 -0
- package/dist/bitcoind/service.d.ts +2 -7
- package/dist/bitcoind/service.js +46 -94
- package/dist/wallet/lifecycle/access.d.ts +5 -0
- package/dist/wallet/lifecycle/access.js +79 -0
- package/dist/wallet/lifecycle/context.d.ts +26 -0
- package/dist/wallet/lifecycle/context.js +58 -0
- package/dist/wallet/lifecycle/managed-core.d.ts +1 -9
- package/dist/wallet/lifecycle/managed-core.js +3 -63
- package/dist/wallet/lifecycle/repair-bitcoind.d.ts +10 -0
- package/dist/wallet/lifecycle/repair-bitcoind.js +142 -0
- package/dist/wallet/lifecycle/repair-indexer.d.ts +8 -0
- package/dist/wallet/lifecycle/repair-indexer.js +117 -0
- package/dist/wallet/lifecycle/repair.d.ts +2 -4
- package/dist/wallet/lifecycle/repair.js +77 -318
- package/dist/wallet/lifecycle/setup-prompts.d.ts +7 -0
- package/dist/wallet/lifecycle/setup-prompts.js +88 -0
- package/dist/wallet/lifecycle/setup-state.d.ts +26 -0
- package/dist/wallet/lifecycle/setup-state.js +159 -0
- package/dist/wallet/lifecycle/setup.d.ts +3 -4
- package/dist/wallet/lifecycle/setup.js +45 -351
- package/dist/wallet/lifecycle/types.d.ts +33 -2
- package/dist/wallet/read/context.js +13 -188
- package/package.json +1 -1
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { randomBytes } from "node:crypto";
|
|
2
|
+
import { withClaimedUninitializedManagedRuntime } from "../../bitcoind/service.js";
|
|
3
|
+
import { createInternalCoreWalletPassphrase, deriveWalletMaterialFromMnemonic, generateWalletMaterial, } from "../material.js";
|
|
4
|
+
import { clearLegacyWalletLockArtifacts } from "../managed-core-wallet.js";
|
|
5
|
+
import { clearWalletPendingInitializationState, loadWalletPendingInitializationStateOrNull, saveWalletPendingInitializationState, } from "../state/pending-init.js";
|
|
6
|
+
import { createWalletPendingInitSecretReference, createWalletRootId, createWalletSecretReference, } from "../state/provider.js";
|
|
7
|
+
import { saveWalletState } from "../state/storage.js";
|
|
8
|
+
import { importDescriptorIntoManagedCoreWallet, sanitizeWalletName } from "./managed-core.js";
|
|
9
|
+
function resolvePendingInitializationStoragePaths(paths) {
|
|
10
|
+
return {
|
|
11
|
+
primaryPath: paths.walletInitPendingPath,
|
|
12
|
+
backupPath: paths.walletInitPendingBackupPath,
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
export async function clearPendingInitialization(paths, provider) {
|
|
16
|
+
await clearWalletPendingInitializationState(resolvePendingInitializationStoragePaths(paths), {
|
|
17
|
+
provider,
|
|
18
|
+
secretReference: createWalletPendingInitSecretReference(paths.walletStateRoot),
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
export async function loadOrCreatePendingInitializationMaterial(options) {
|
|
22
|
+
try {
|
|
23
|
+
const loaded = await loadWalletPendingInitializationStateOrNull(resolvePendingInitializationStoragePaths(options.paths), {
|
|
24
|
+
provider: options.provider,
|
|
25
|
+
});
|
|
26
|
+
if (loaded !== null) {
|
|
27
|
+
return deriveWalletMaterialFromMnemonic(loaded.state.mnemonic.phrase);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
await clearPendingInitialization(options.paths, options.provider);
|
|
32
|
+
}
|
|
33
|
+
const material = generateWalletMaterial();
|
|
34
|
+
const secretReference = createWalletPendingInitSecretReference(options.paths.walletStateRoot);
|
|
35
|
+
const pendingState = {
|
|
36
|
+
schemaVersion: 1,
|
|
37
|
+
createdAtUnixMs: options.nowUnixMs,
|
|
38
|
+
mnemonic: {
|
|
39
|
+
phrase: material.mnemonic.phrase,
|
|
40
|
+
language: material.mnemonic.language,
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
await options.provider.storeSecret(secretReference.keyId, randomBytes(32));
|
|
44
|
+
try {
|
|
45
|
+
await saveWalletPendingInitializationState(resolvePendingInitializationStoragePaths(options.paths), pendingState, {
|
|
46
|
+
provider: options.provider,
|
|
47
|
+
secretReference,
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
await options.provider.deleteSecret(secretReference.keyId).catch(() => undefined);
|
|
52
|
+
throw error;
|
|
53
|
+
}
|
|
54
|
+
return material;
|
|
55
|
+
}
|
|
56
|
+
export function createInitialWalletState(options) {
|
|
57
|
+
return {
|
|
58
|
+
schemaVersion: 5,
|
|
59
|
+
stateRevision: 1,
|
|
60
|
+
lastWrittenAtUnixMs: options.nowUnixMs,
|
|
61
|
+
walletRootId: options.walletRootId,
|
|
62
|
+
network: "mainnet",
|
|
63
|
+
localScriptPubKeyHexes: [options.material.funding.scriptPubKeyHex],
|
|
64
|
+
mnemonic: {
|
|
65
|
+
phrase: options.material.mnemonic.phrase,
|
|
66
|
+
language: options.material.mnemonic.language,
|
|
67
|
+
},
|
|
68
|
+
keys: {
|
|
69
|
+
masterFingerprintHex: options.material.keys.masterFingerprintHex,
|
|
70
|
+
accountPath: options.material.keys.accountPath,
|
|
71
|
+
accountXprv: options.material.keys.accountXprv,
|
|
72
|
+
accountXpub: options.material.keys.accountXpub,
|
|
73
|
+
},
|
|
74
|
+
descriptor: {
|
|
75
|
+
privateExternal: options.material.descriptor.privateExternal,
|
|
76
|
+
publicExternal: options.material.descriptor.publicExternal,
|
|
77
|
+
checksum: options.material.descriptor.checksum,
|
|
78
|
+
rangeEnd: options.material.descriptor.rangeEnd,
|
|
79
|
+
safetyMargin: options.material.descriptor.safetyMargin,
|
|
80
|
+
},
|
|
81
|
+
funding: {
|
|
82
|
+
address: options.material.funding.address,
|
|
83
|
+
scriptPubKeyHex: options.material.funding.scriptPubKeyHex,
|
|
84
|
+
},
|
|
85
|
+
walletBirthTime: Math.floor(options.nowUnixMs / 1000),
|
|
86
|
+
managedCoreWallet: {
|
|
87
|
+
walletName: sanitizeWalletName(options.walletRootId),
|
|
88
|
+
internalPassphrase: options.internalCoreWalletPassphrase,
|
|
89
|
+
descriptorChecksum: null,
|
|
90
|
+
walletAddress: null,
|
|
91
|
+
walletScriptPubKeyHex: null,
|
|
92
|
+
proofStatus: "not-proven",
|
|
93
|
+
lastImportedAtUnixMs: null,
|
|
94
|
+
lastVerifiedAtUnixMs: null,
|
|
95
|
+
},
|
|
96
|
+
domains: [],
|
|
97
|
+
miningState: {
|
|
98
|
+
runMode: "stopped",
|
|
99
|
+
state: "idle",
|
|
100
|
+
pauseReason: null,
|
|
101
|
+
currentPublishState: "none",
|
|
102
|
+
currentDomain: null,
|
|
103
|
+
currentDomainId: null,
|
|
104
|
+
currentDomainIndex: null,
|
|
105
|
+
currentSenderScriptPubKeyHex: null,
|
|
106
|
+
currentTxid: null,
|
|
107
|
+
currentWtxid: null,
|
|
108
|
+
currentFeeRateSatVb: null,
|
|
109
|
+
currentAbsoluteFeeSats: null,
|
|
110
|
+
currentScore: null,
|
|
111
|
+
currentSentence: null,
|
|
112
|
+
currentEncodedSentenceBytesHex: null,
|
|
113
|
+
currentBip39WordIndices: null,
|
|
114
|
+
currentBlendSeedHex: null,
|
|
115
|
+
currentBlockTargetHeight: null,
|
|
116
|
+
currentReferencedBlockHashDisplay: null,
|
|
117
|
+
currentIntentFingerprintHex: null,
|
|
118
|
+
livePublishInMempool: null,
|
|
119
|
+
currentPublishDecision: null,
|
|
120
|
+
replacementCount: 0,
|
|
121
|
+
currentBlockFeeSpentSats: "0",
|
|
122
|
+
sessionFeeSpentSats: "0",
|
|
123
|
+
lifetimeFeeSpentSats: "0",
|
|
124
|
+
sharedMiningConflictOutpoint: null,
|
|
125
|
+
},
|
|
126
|
+
pendingMutations: [],
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
export async function persistInitializedWallet(options) {
|
|
130
|
+
const walletRootId = createWalletRootId();
|
|
131
|
+
const internalCoreWalletPassphrase = createInternalCoreWalletPassphrase();
|
|
132
|
+
const secretReference = createWalletSecretReference(walletRootId);
|
|
133
|
+
await options.provider.storeSecret(secretReference.keyId, randomBytes(32));
|
|
134
|
+
const initialState = createInitialWalletState({
|
|
135
|
+
walletRootId,
|
|
136
|
+
nowUnixMs: options.context.nowUnixMs,
|
|
137
|
+
material: options.material,
|
|
138
|
+
internalCoreWalletPassphrase,
|
|
139
|
+
});
|
|
140
|
+
const verifiedState = await withClaimedUninitializedManagedRuntime({
|
|
141
|
+
dataDir: options.context.dataDir,
|
|
142
|
+
walletRootId,
|
|
143
|
+
}, async () => {
|
|
144
|
+
await saveWalletState({
|
|
145
|
+
primaryPath: options.context.paths.walletStatePath,
|
|
146
|
+
backupPath: options.context.paths.walletStateBackupPath,
|
|
147
|
+
}, initialState, {
|
|
148
|
+
provider: options.provider,
|
|
149
|
+
secretReference,
|
|
150
|
+
});
|
|
151
|
+
return await importDescriptorIntoManagedCoreWallet(initialState, options.provider, options.context.paths, options.context.dataDir, options.context.nowUnixMs, options.context.attachService, options.context.rpcFactory);
|
|
152
|
+
});
|
|
153
|
+
await clearLegacyWalletLockArtifacts(options.context.paths.walletRuntimeRoot);
|
|
154
|
+
await clearPendingInitialization(options.context.paths, options.provider);
|
|
155
|
+
return {
|
|
156
|
+
walletRootId,
|
|
157
|
+
state: verifiedState,
|
|
158
|
+
};
|
|
159
|
+
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { type WalletRuntimePaths } from "../runtime.js";
|
|
2
1
|
import { type WalletSecretProvider } from "../state/provider.js";
|
|
3
2
|
import type { WalletInitializationResult, WalletPrompter, WalletSetupDependencies } from "./types.js";
|
|
4
3
|
export declare function initializeWallet(options: {
|
|
@@ -6,11 +5,11 @@ export declare function initializeWallet(options: {
|
|
|
6
5
|
provider?: WalletSecretProvider;
|
|
7
6
|
prompter: WalletPrompter;
|
|
8
7
|
nowUnixMs?: number;
|
|
9
|
-
paths?: WalletRuntimePaths;
|
|
8
|
+
paths?: import("../runtime.js").WalletRuntimePaths;
|
|
10
9
|
} & WalletSetupDependencies): Promise<WalletInitializationResult>;
|
|
11
10
|
export declare function showWalletMnemonic(options: {
|
|
12
11
|
provider?: WalletSecretProvider;
|
|
13
12
|
prompter: WalletPrompter;
|
|
14
13
|
nowUnixMs?: number;
|
|
15
|
-
paths?: WalletRuntimePaths;
|
|
16
|
-
}): Promise<void>;
|
|
14
|
+
paths?: import("../runtime.js").WalletRuntimePaths;
|
|
15
|
+
} & WalletSetupDependencies): Promise<void>;
|
|
@@ -1,306 +1,24 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import { resolveWalletRuntimePathsForTesting } from "../runtime.js";
|
|
8
|
-
import { clearWalletPendingInitializationState, loadWalletPendingInitializationStateOrNull, saveWalletPendingInitializationState, } from "../state/pending-init.js";
|
|
9
|
-
import { createDefaultWalletSecretProvider, createWalletPendingInitSecretReference, createWalletRootId, createWalletSecretReference, ensureClientPasswordConfigured, withInteractiveWalletSecretProvider, } from "../state/provider.js";
|
|
10
|
-
import { clearLegacyWalletLockArtifacts, } from "../managed-core-wallet.js";
|
|
11
|
-
import { loadWalletState, saveWalletState } from "../state/storage.js";
|
|
12
|
-
import { importDescriptorIntoManagedCoreWallet, normalizeLoadedWalletStateIfNeeded, sanitizeWalletName, } from "./managed-core.js";
|
|
13
|
-
function resolvePendingInitializationStoragePaths(paths) {
|
|
14
|
-
return {
|
|
15
|
-
primaryPath: paths.walletInitPendingPath,
|
|
16
|
-
backupPath: paths.walletInitPendingBackupPath,
|
|
17
|
-
};
|
|
18
|
-
}
|
|
19
|
-
async function pathExists(path) {
|
|
20
|
-
try {
|
|
21
|
-
await access(path, constants.F_OK);
|
|
22
|
-
return true;
|
|
23
|
-
}
|
|
24
|
-
catch {
|
|
25
|
-
return false;
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
async function clearPendingInitialization(paths, provider) {
|
|
29
|
-
await clearWalletPendingInitializationState(resolvePendingInitializationStoragePaths(paths), {
|
|
30
|
-
provider,
|
|
31
|
-
secretReference: createWalletPendingInitSecretReference(paths.walletStateRoot),
|
|
32
|
-
});
|
|
33
|
-
}
|
|
34
|
-
async function loadOrCreatePendingInitializationMaterial(options) {
|
|
35
|
-
try {
|
|
36
|
-
const loaded = await loadWalletPendingInitializationStateOrNull(resolvePendingInitializationStoragePaths(options.paths), {
|
|
37
|
-
provider: options.provider,
|
|
38
|
-
});
|
|
39
|
-
if (loaded !== null) {
|
|
40
|
-
return deriveWalletMaterialFromMnemonic(loaded.state.mnemonic.phrase);
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
catch {
|
|
44
|
-
await clearPendingInitialization(options.paths, options.provider);
|
|
45
|
-
}
|
|
46
|
-
const material = generateWalletMaterial();
|
|
47
|
-
const secretReference = createWalletPendingInitSecretReference(options.paths.walletStateRoot);
|
|
48
|
-
const pendingState = {
|
|
49
|
-
schemaVersion: 1,
|
|
50
|
-
createdAtUnixMs: options.nowUnixMs,
|
|
51
|
-
mnemonic: {
|
|
52
|
-
phrase: material.mnemonic.phrase,
|
|
53
|
-
language: material.mnemonic.language,
|
|
54
|
-
},
|
|
55
|
-
};
|
|
56
|
-
await options.provider.storeSecret(secretReference.keyId, randomBytes(32));
|
|
57
|
-
try {
|
|
58
|
-
await saveWalletPendingInitializationState(resolvePendingInitializationStoragePaths(options.paths), pendingState, {
|
|
59
|
-
provider: options.provider,
|
|
60
|
-
secretReference,
|
|
61
|
-
});
|
|
62
|
-
}
|
|
63
|
-
catch (error) {
|
|
64
|
-
await options.provider.deleteSecret(secretReference.keyId).catch(() => undefined);
|
|
65
|
-
throw error;
|
|
66
|
-
}
|
|
67
|
-
return material;
|
|
68
|
-
}
|
|
69
|
-
function createInitialWalletState(options) {
|
|
70
|
-
return {
|
|
71
|
-
schemaVersion: 5,
|
|
72
|
-
stateRevision: 1,
|
|
73
|
-
lastWrittenAtUnixMs: options.nowUnixMs,
|
|
74
|
-
walletRootId: options.walletRootId,
|
|
75
|
-
network: "mainnet",
|
|
76
|
-
localScriptPubKeyHexes: [options.material.funding.scriptPubKeyHex],
|
|
77
|
-
mnemonic: {
|
|
78
|
-
phrase: options.material.mnemonic.phrase,
|
|
79
|
-
language: options.material.mnemonic.language,
|
|
80
|
-
},
|
|
81
|
-
keys: {
|
|
82
|
-
masterFingerprintHex: options.material.keys.masterFingerprintHex,
|
|
83
|
-
accountPath: options.material.keys.accountPath,
|
|
84
|
-
accountXprv: options.material.keys.accountXprv,
|
|
85
|
-
accountXpub: options.material.keys.accountXpub,
|
|
86
|
-
},
|
|
87
|
-
descriptor: {
|
|
88
|
-
privateExternal: options.material.descriptor.privateExternal,
|
|
89
|
-
publicExternal: options.material.descriptor.publicExternal,
|
|
90
|
-
checksum: options.material.descriptor.checksum,
|
|
91
|
-
rangeEnd: options.material.descriptor.rangeEnd,
|
|
92
|
-
safetyMargin: options.material.descriptor.safetyMargin,
|
|
93
|
-
},
|
|
94
|
-
funding: {
|
|
95
|
-
address: options.material.funding.address,
|
|
96
|
-
scriptPubKeyHex: options.material.funding.scriptPubKeyHex,
|
|
97
|
-
},
|
|
98
|
-
walletBirthTime: Math.floor(options.nowUnixMs / 1000),
|
|
99
|
-
managedCoreWallet: {
|
|
100
|
-
walletName: sanitizeWalletName(options.walletRootId),
|
|
101
|
-
internalPassphrase: options.internalCoreWalletPassphrase,
|
|
102
|
-
descriptorChecksum: null,
|
|
103
|
-
walletAddress: null,
|
|
104
|
-
walletScriptPubKeyHex: null,
|
|
105
|
-
proofStatus: "not-proven",
|
|
106
|
-
lastImportedAtUnixMs: null,
|
|
107
|
-
lastVerifiedAtUnixMs: null,
|
|
108
|
-
},
|
|
109
|
-
domains: [],
|
|
110
|
-
miningState: {
|
|
111
|
-
runMode: "stopped",
|
|
112
|
-
state: "idle",
|
|
113
|
-
pauseReason: null,
|
|
114
|
-
currentPublishState: "none",
|
|
115
|
-
currentDomain: null,
|
|
116
|
-
currentDomainId: null,
|
|
117
|
-
currentDomainIndex: null,
|
|
118
|
-
currentSenderScriptPubKeyHex: null,
|
|
119
|
-
currentTxid: null,
|
|
120
|
-
currentWtxid: null,
|
|
121
|
-
currentFeeRateSatVb: null,
|
|
122
|
-
currentAbsoluteFeeSats: null,
|
|
123
|
-
currentScore: null,
|
|
124
|
-
currentSentence: null,
|
|
125
|
-
currentEncodedSentenceBytesHex: null,
|
|
126
|
-
currentBip39WordIndices: null,
|
|
127
|
-
currentBlendSeedHex: null,
|
|
128
|
-
currentBlockTargetHeight: null,
|
|
129
|
-
currentReferencedBlockHashDisplay: null,
|
|
130
|
-
currentIntentFingerprintHex: null,
|
|
131
|
-
livePublishInMempool: null,
|
|
132
|
-
currentPublishDecision: null,
|
|
133
|
-
replacementCount: 0,
|
|
134
|
-
currentBlockFeeSpentSats: "0",
|
|
135
|
-
sessionFeeSpentSats: "0",
|
|
136
|
-
lifetimeFeeSpentSats: "0",
|
|
137
|
-
sharedMiningConflictOutpoint: null,
|
|
138
|
-
},
|
|
139
|
-
pendingMutations: [],
|
|
140
|
-
};
|
|
141
|
-
}
|
|
142
|
-
async function promptRequiredValue(prompter, message) {
|
|
143
|
-
const value = (await prompter.prompt(message)).trim();
|
|
144
|
-
if (value === "") {
|
|
145
|
-
throw new Error("wallet_prompt_value_required");
|
|
146
|
-
}
|
|
147
|
-
return value;
|
|
148
|
-
}
|
|
149
|
-
async function promptForRestoreMnemonic(prompter) {
|
|
150
|
-
const words = [];
|
|
151
|
-
for (let index = 0; index < 24; index += 1) {
|
|
152
|
-
const word = (await promptRequiredValue(prompter, `Word ${index + 1} of 24: `)).toLowerCase();
|
|
153
|
-
if (!isEnglishMnemonicWord(word)) {
|
|
154
|
-
throw new Error("wallet_restore_mnemonic_invalid");
|
|
155
|
-
}
|
|
156
|
-
words.push(word);
|
|
157
|
-
}
|
|
158
|
-
const phrase = words.join(" ");
|
|
159
|
-
if (!validateEnglishMnemonic(phrase)) {
|
|
160
|
-
throw new Error("wallet_restore_mnemonic_invalid");
|
|
161
|
-
}
|
|
162
|
-
return phrase;
|
|
163
|
-
}
|
|
164
|
-
async function promptForInitializationMode(prompter) {
|
|
165
|
-
if (prompter.selectOption != null) {
|
|
166
|
-
return await prompter.selectOption({
|
|
167
|
-
message: "How should Cogcoin set up this wallet?",
|
|
168
|
-
options: [
|
|
169
|
-
{
|
|
170
|
-
label: "Create new wallet",
|
|
171
|
-
description: "Generate a fresh 24-word recovery phrase.",
|
|
172
|
-
value: "generated",
|
|
173
|
-
},
|
|
174
|
-
{
|
|
175
|
-
label: "Restore existing wallet",
|
|
176
|
-
description: "Enter an existing 24-word recovery phrase.",
|
|
177
|
-
value: "restored",
|
|
178
|
-
},
|
|
179
|
-
],
|
|
180
|
-
initialValue: "generated",
|
|
181
|
-
});
|
|
182
|
-
}
|
|
183
|
-
prompter.writeLine("How should Cogcoin set up this wallet?");
|
|
184
|
-
prompter.writeLine("1. Create new wallet");
|
|
185
|
-
prompter.writeLine("2. Restore existing wallet");
|
|
186
|
-
while (true) {
|
|
187
|
-
const answer = (await prompter.prompt("Choice [1-2]: ")).trim();
|
|
188
|
-
if (answer === "1") {
|
|
189
|
-
return "generated";
|
|
190
|
-
}
|
|
191
|
-
if (answer === "2") {
|
|
192
|
-
return "restored";
|
|
193
|
-
}
|
|
194
|
-
prompter.writeLine("Enter 1 or 2.");
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
async function confirmTypedAcknowledgement(prompter, expected, message, errorCode = "wallet_typed_confirmation_rejected") {
|
|
198
|
-
const answer = (await prompter.prompt(message)).trim();
|
|
199
|
-
if (answer !== expected) {
|
|
200
|
-
throw new Error(errorCode);
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
function isWalletSecretAccessError(error) {
|
|
204
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
205
|
-
return message.startsWith("wallet_secret_missing_")
|
|
206
|
-
|| message.startsWith("wallet_secret_provider_");
|
|
207
|
-
}
|
|
208
|
-
async function persistInitializedWallet(options) {
|
|
209
|
-
const walletRootId = createWalletRootId();
|
|
210
|
-
const internalCoreWalletPassphrase = createInternalCoreWalletPassphrase();
|
|
211
|
-
const secretReference = createWalletSecretReference(walletRootId);
|
|
212
|
-
await options.provider.storeSecret(secretReference.keyId, randomBytes(32));
|
|
213
|
-
const initialState = createInitialWalletState({
|
|
214
|
-
walletRootId,
|
|
215
|
-
nowUnixMs: options.nowUnixMs,
|
|
216
|
-
material: options.material,
|
|
217
|
-
internalCoreWalletPassphrase,
|
|
218
|
-
});
|
|
219
|
-
const verifiedState = await withClaimedUninitializedManagedRuntime({
|
|
220
|
-
dataDir: options.dataDir,
|
|
221
|
-
walletRootId,
|
|
222
|
-
}, async () => {
|
|
223
|
-
await saveWalletState({
|
|
224
|
-
primaryPath: options.paths.walletStatePath,
|
|
225
|
-
backupPath: options.paths.walletStateBackupPath,
|
|
226
|
-
}, initialState, {
|
|
227
|
-
provider: options.provider,
|
|
228
|
-
secretReference,
|
|
229
|
-
});
|
|
230
|
-
return importDescriptorIntoManagedCoreWallet(initialState, options.provider, options.paths, options.dataDir, options.nowUnixMs, options.attachService, options.rpcFactory);
|
|
231
|
-
});
|
|
232
|
-
await clearLegacyWalletLockArtifacts(options.paths.walletRuntimeRoot);
|
|
233
|
-
await clearPendingInitialization(options.paths, options.provider);
|
|
234
|
-
return {
|
|
235
|
-
walletRootId,
|
|
236
|
-
state: verifiedState,
|
|
237
|
-
};
|
|
238
|
-
}
|
|
239
|
-
function writeMnemonicReveal(prompter, phrase, introLines) {
|
|
240
|
-
const words = phrase.trim().split(/\s+/);
|
|
241
|
-
for (const line of introLines) {
|
|
242
|
-
prompter.writeLine(line);
|
|
243
|
-
}
|
|
244
|
-
for (const line of renderWalletMnemonicRevealArt(words)) {
|
|
245
|
-
prompter.writeLine(line);
|
|
246
|
-
}
|
|
247
|
-
prompter.writeLine("Single-line copy:");
|
|
248
|
-
prompter.writeLine(phrase);
|
|
249
|
-
}
|
|
250
|
-
async function confirmMnemonic(prompter, words) {
|
|
251
|
-
const challenge = createMnemonicConfirmationChallenge(words);
|
|
252
|
-
for (const entry of challenge) {
|
|
253
|
-
const answer = (await prompter.prompt(`Confirm word #${entry.index + 1}: `)).trim().toLowerCase();
|
|
254
|
-
if (answer !== entry.word) {
|
|
255
|
-
throw new Error(`wallet_init_confirmation_failed_word_${entry.index + 1}`);
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
async function loadWalletStateForAccess(options = {}) {
|
|
260
|
-
const provider = options.provider ?? createDefaultWalletSecretProvider();
|
|
261
|
-
const nowUnixMs = options.nowUnixMs ?? Date.now();
|
|
262
|
-
const paths = options.paths ?? resolveWalletRuntimePathsForTesting();
|
|
263
|
-
const loaded = await loadWalletState({
|
|
264
|
-
primaryPath: paths.walletStatePath,
|
|
265
|
-
backupPath: paths.walletStateBackupPath,
|
|
266
|
-
}, {
|
|
267
|
-
provider,
|
|
268
|
-
});
|
|
269
|
-
return normalizeLoadedWalletStateIfNeeded({
|
|
270
|
-
provider,
|
|
271
|
-
state: loaded.state,
|
|
272
|
-
source: loaded.source,
|
|
273
|
-
nowUnixMs,
|
|
274
|
-
paths,
|
|
275
|
-
dataDir: options.dataDir,
|
|
276
|
-
attachService: options.attachService,
|
|
277
|
-
rpcFactory: options.rpcFactory,
|
|
278
|
-
});
|
|
279
|
-
}
|
|
1
|
+
import { deriveWalletMaterialFromMnemonic } from "../material.js";
|
|
2
|
+
import { ensureClientPasswordConfigured, withInteractiveWalletSecretProvider, } from "../state/provider.js";
|
|
3
|
+
import { loadWalletStateForAccess, mapWalletReadAccessError } from "./access.js";
|
|
4
|
+
import { acquireWalletControlLock, resolveWalletManagedCoreContext, resolveWalletSetupContext, walletStateExists, } from "./context.js";
|
|
5
|
+
import { clearSensitiveDisplay, confirmMnemonic, confirmTypedAcknowledgement, promptForInitializationMode, promptForRestoreMnemonic, writeMnemonicReveal, } from "./setup-prompts.js";
|
|
6
|
+
import { clearPendingInitialization, loadOrCreatePendingInitializationMaterial, persistInitializedWallet, } from "./setup-state.js";
|
|
280
7
|
export async function initializeWallet(options) {
|
|
281
|
-
|
|
8
|
+
const context = resolveWalletSetupContext(options);
|
|
9
|
+
if (!context.prompter.isInteractive) {
|
|
282
10
|
throw new Error("wallet_init_requires_tty");
|
|
283
11
|
}
|
|
284
|
-
const
|
|
285
|
-
const
|
|
286
|
-
const nowUnixMs = options.nowUnixMs ?? Date.now();
|
|
287
|
-
const paths = options.paths ?? resolveWalletRuntimePathsForTesting();
|
|
288
|
-
const controlLock = await acquireFileLock(paths.walletControlLockPath, {
|
|
289
|
-
purpose: "wallet-init",
|
|
290
|
-
walletRootId: null,
|
|
291
|
-
});
|
|
12
|
+
const interactiveProvider = withInteractiveWalletSecretProvider(context.provider, context.prompter);
|
|
13
|
+
const controlLock = await acquireWalletControlLock(context.paths, "wallet-init");
|
|
292
14
|
try {
|
|
293
|
-
const passwordAction = await ensureClientPasswordConfigured(provider,
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
await clearPendingInitialization(paths, interactiveProvider);
|
|
15
|
+
const passwordAction = await ensureClientPasswordConfigured(context.provider, context.prompter);
|
|
16
|
+
if (await walletStateExists(context.paths)) {
|
|
17
|
+
await clearPendingInitialization(context.paths, interactiveProvider);
|
|
297
18
|
const loaded = await loadWalletStateForAccess({
|
|
19
|
+
...context,
|
|
298
20
|
provider: interactiveProvider,
|
|
299
|
-
|
|
300
|
-
paths,
|
|
301
|
-
dataDir: options.dataDir,
|
|
302
|
-
attachService: options.attachService,
|
|
303
|
-
rpcFactory: options.rpcFactory,
|
|
21
|
+
dataDir: context.dataDir,
|
|
304
22
|
});
|
|
305
23
|
return {
|
|
306
24
|
setupMode: "existing",
|
|
@@ -311,56 +29,42 @@ export async function initializeWallet(options) {
|
|
|
311
29
|
state: loaded.state,
|
|
312
30
|
};
|
|
313
31
|
}
|
|
314
|
-
const setupMode = await promptForInitializationMode(
|
|
32
|
+
const setupMode = await promptForInitializationMode(context.prompter);
|
|
315
33
|
let material;
|
|
316
34
|
if (setupMode === "generated") {
|
|
317
35
|
material = await loadOrCreatePendingInitializationMaterial({
|
|
318
36
|
provider: interactiveProvider,
|
|
319
|
-
paths,
|
|
320
|
-
nowUnixMs,
|
|
37
|
+
paths: context.paths,
|
|
38
|
+
nowUnixMs: context.nowUnixMs,
|
|
321
39
|
});
|
|
322
|
-
|
|
323
|
-
writeMnemonicReveal(options.prompter, material.mnemonic.phrase, [
|
|
40
|
+
writeMnemonicReveal(context.prompter, material.mnemonic.phrase, [
|
|
324
41
|
"Cogcoin Wallet Initialization",
|
|
325
42
|
"Write down this 24-word recovery phrase.",
|
|
326
43
|
"The same phrase will be shown again until confirmation succeeds:",
|
|
327
44
|
"",
|
|
328
45
|
]);
|
|
329
|
-
mnemonicRevealed = true;
|
|
330
46
|
try {
|
|
331
|
-
await confirmMnemonic(
|
|
47
|
+
await confirmMnemonic(context.prompter, material.mnemonic.words);
|
|
332
48
|
}
|
|
333
49
|
finally {
|
|
334
|
-
|
|
335
|
-
await Promise.resolve()
|
|
336
|
-
.then(() => options.prompter.clearSensitiveDisplay?.("mnemonic-reveal"))
|
|
337
|
-
.catch(() => undefined);
|
|
338
|
-
}
|
|
50
|
+
await clearSensitiveDisplay(context.prompter, "mnemonic-reveal");
|
|
339
51
|
}
|
|
340
52
|
}
|
|
341
53
|
else {
|
|
342
|
-
let promptPhaseStarted = false;
|
|
343
54
|
let mnemonicPhrase;
|
|
344
55
|
try {
|
|
345
|
-
|
|
346
|
-
mnemonicPhrase = await promptForRestoreMnemonic(options.prompter);
|
|
56
|
+
mnemonicPhrase = await promptForRestoreMnemonic(context.prompter);
|
|
347
57
|
}
|
|
348
58
|
finally {
|
|
349
|
-
|
|
350
|
-
await options.prompter.clearSensitiveDisplay?.("restore-mnemonic-entry");
|
|
351
|
-
}
|
|
59
|
+
await clearSensitiveDisplay(context.prompter, "restore-mnemonic-entry");
|
|
352
60
|
}
|
|
353
|
-
await clearPendingInitialization(paths, interactiveProvider);
|
|
61
|
+
await clearPendingInitialization(context.paths, interactiveProvider);
|
|
354
62
|
material = deriveWalletMaterialFromMnemonic(mnemonicPhrase);
|
|
355
63
|
}
|
|
356
64
|
const initialized = await persistInitializedWallet({
|
|
357
|
-
|
|
65
|
+
context,
|
|
358
66
|
provider: interactiveProvider,
|
|
359
67
|
material,
|
|
360
|
-
nowUnixMs,
|
|
361
|
-
paths,
|
|
362
|
-
attachService: options.attachService,
|
|
363
|
-
rpcFactory: options.rpcFactory,
|
|
364
68
|
});
|
|
365
69
|
return {
|
|
366
70
|
setupMode,
|
|
@@ -376,52 +80,42 @@ export async function initializeWallet(options) {
|
|
|
376
80
|
}
|
|
377
81
|
}
|
|
378
82
|
export async function showWalletMnemonic(options) {
|
|
379
|
-
|
|
83
|
+
const context = {
|
|
84
|
+
...resolveWalletManagedCoreContext({
|
|
85
|
+
provider: options.provider,
|
|
86
|
+
nowUnixMs: options.nowUnixMs,
|
|
87
|
+
paths: options.paths,
|
|
88
|
+
attachService: options.attachService,
|
|
89
|
+
rpcFactory: options.rpcFactory,
|
|
90
|
+
}),
|
|
91
|
+
prompter: options.prompter,
|
|
92
|
+
};
|
|
93
|
+
if (!context.prompter.isInteractive) {
|
|
380
94
|
throw new Error("wallet_show_mnemonic_requires_tty");
|
|
381
95
|
}
|
|
382
|
-
const
|
|
383
|
-
const
|
|
384
|
-
const nowUnixMs = options.nowUnixMs ?? Date.now();
|
|
385
|
-
const paths = options.paths ?? resolveWalletRuntimePathsForTesting();
|
|
386
|
-
const controlLock = await acquireFileLock(paths.walletControlLockPath, {
|
|
387
|
-
purpose: "wallet-show-mnemonic",
|
|
388
|
-
walletRootId: null,
|
|
389
|
-
});
|
|
96
|
+
const interactiveProvider = withInteractiveWalletSecretProvider(context.provider, context.prompter);
|
|
97
|
+
const controlLock = await acquireWalletControlLock(context.paths, "wallet-show-mnemonic");
|
|
390
98
|
try {
|
|
391
|
-
|
|
392
|
-
pathExists(paths.walletStatePath),
|
|
393
|
-
pathExists(paths.walletStateBackupPath),
|
|
394
|
-
]);
|
|
395
|
-
if (!hasPrimaryStateFile && !hasBackupStateFile) {
|
|
99
|
+
if (!await walletStateExists(context.paths)) {
|
|
396
100
|
throw new Error("wallet_uninitialized");
|
|
397
101
|
}
|
|
398
102
|
const loaded = await loadWalletStateForAccess({
|
|
103
|
+
...context,
|
|
399
104
|
provider: interactiveProvider,
|
|
400
|
-
nowUnixMs,
|
|
401
|
-
paths,
|
|
402
105
|
}).catch((error) => {
|
|
403
|
-
|
|
404
|
-
throw new Error("wallet_secret_provider_unavailable");
|
|
405
|
-
}
|
|
406
|
-
throw new Error("local-state-corrupt");
|
|
106
|
+
throw mapWalletReadAccessError(error);
|
|
407
107
|
});
|
|
408
|
-
await confirmTypedAcknowledgement(
|
|
409
|
-
|
|
410
|
-
writeMnemonicReveal(options.prompter, loaded.state.mnemonic.phrase, [
|
|
108
|
+
await confirmTypedAcknowledgement(context.prompter, "show mnemonic", "Type \"show mnemonic\" to continue: ", "wallet_show_mnemonic_typed_ack_required");
|
|
109
|
+
writeMnemonicReveal(context.prompter, loaded.state.mnemonic.phrase, [
|
|
411
110
|
"Cogcoin Wallet Recovery Phrase",
|
|
412
111
|
"This 24-word recovery phrase controls the wallet.",
|
|
413
112
|
"",
|
|
414
113
|
]);
|
|
415
|
-
mnemonicRevealed = true;
|
|
416
114
|
try {
|
|
417
|
-
await
|
|
115
|
+
await context.prompter.prompt("Press Enter to clear the recovery phrase from the screen: ");
|
|
418
116
|
}
|
|
419
117
|
finally {
|
|
420
|
-
|
|
421
|
-
await Promise.resolve()
|
|
422
|
-
.then(() => options.prompter.clearSensitiveDisplay?.("mnemonic-reveal"))
|
|
423
|
-
.catch(() => undefined);
|
|
424
|
-
}
|
|
118
|
+
await clearSensitiveDisplay(context.prompter, "mnemonic-reveal");
|
|
425
119
|
}
|
|
426
120
|
}
|
|
427
121
|
finally {
|