@sidhujag/sysweb3-keyring 2.1.2 → 2.1.4
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/CHANGELOG.md +6 -0
- package/README.md +55 -47
- package/cjs/current-vault-refusal.js +6 -0
- package/cjs/current-vault-refusal.js.map +1 -0
- package/cjs/evm-fees.js +29 -1
- package/cjs/evm-fees.js.map +1 -1
- package/cjs/hardware-transaction-authority.js +36 -0
- package/cjs/hardware-transaction-authority.js.map +1 -0
- package/cjs/index.js +5 -5
- package/cjs/index.js.map +1 -1
- package/cjs/keyring-core.js +466 -250
- package/cjs/keyring-core.js.map +1 -1
- package/cjs/keyring-host.js +113 -0
- package/cjs/keyring-host.js.map +1 -0
- package/cjs/keyring-manager.js +15 -55
- package/cjs/keyring-manager.js.map +1 -1
- package/cjs/ledger/host-types.js.map +1 -1
- package/cjs/ledger/index.js +44 -19
- package/cjs/ledger/index.js.map +1 -1
- package/cjs/providers.js +3 -1
- package/cjs/providers.js.map +1 -1
- package/cjs/software.js +10 -43
- package/cjs/software.js.map +1 -1
- package/cjs/storage-core.js +44 -4
- package/cjs/storage-core.js.map +1 -1
- package/cjs/transactions/ethereum.js +358 -294
- package/cjs/transactions/ethereum.js.map +1 -1
- package/cjs/transactions/evm-cancellation.js +43 -0
- package/cjs/transactions/evm-cancellation.js.map +1 -0
- package/cjs/transactions/evm-local-signer.js +15 -0
- package/cjs/transactions/evm-local-signer.js.map +1 -1
- package/cjs/transactions/evm-submission.js +61 -65
- package/cjs/transactions/evm-submission.js.map +1 -1
- package/cjs/transactions/hardware-psbt.js +11 -8
- package/cjs/transactions/hardware-psbt.js.map +1 -1
- package/cjs/transactions/index.js +10 -2
- package/cjs/transactions/index.js.map +1 -1
- package/cjs/transactions/prepared-evm.js +10 -7
- package/cjs/transactions/prepared-evm.js.map +1 -1
- package/cjs/transactions/syscoin-core.js +282 -202
- package/cjs/transactions/syscoin-core.js.map +1 -1
- package/cjs/trezor/connect.js +19 -2
- package/cjs/trezor/connect.js.map +1 -1
- package/cjs/trezor/index.js +38 -16
- package/cjs/trezor/index.js.map +1 -1
- package/cjs/types.js.map +1 -1
- package/cjs/utils/owned-blockbook-account.js +45 -0
- package/cjs/utils/owned-blockbook-account.js.map +1 -0
- package/esm/current-vault-refusal.js +3 -0
- package/esm/current-vault-refusal.js.map +1 -0
- package/esm/evm-fees.js +242 -0
- package/esm/evm-fees.js.map +1 -0
- package/esm/hardware-transaction-authority.js +33 -0
- package/esm/hardware-transaction-authority.js.map +1 -0
- package/esm/initial-state.js +102 -0
- package/esm/initial-state.js.map +1 -0
- package/esm/keyring-core.js +2781 -0
- package/esm/keyring-core.js.map +1 -0
- package/esm/keyring-host.js +108 -0
- package/esm/keyring-host.js.map +1 -0
- package/esm/ledger/dmk-host.js +31 -0
- package/esm/ledger/dmk-host.js.map +1 -0
- package/esm/ledger/dmk-selected-host.js +6 -0
- package/esm/ledger/dmk-selected-host.js.map +1 -0
- package/esm/ledger/dmk-session.js +154 -34
- package/esm/ledger/dmk-session.js.map +1 -1
- package/esm/ledger/dmk-utxo.js +9 -3
- package/esm/ledger/dmk-utxo.js.map +1 -1
- package/esm/ledger/host-types.js.map +1 -1
- package/esm/ledger/index.js +44 -19
- package/esm/ledger/index.js.map +1 -1
- package/esm/ledger-host.js +3 -27
- package/esm/ledger-host.js.map +1 -1
- package/esm/network-utils.js +70 -0
- package/esm/network-utils.js.map +1 -0
- package/esm/providers.js +3 -1
- package/esm/providers.js.map +1 -1
- package/esm/software-hd-account.js +20 -0
- package/esm/software-hd-account.js.map +1 -0
- package/esm/storage-core.js +258 -0
- package/esm/storage-core.js.map +1 -0
- package/esm/transactions/ethereum.js +2753 -0
- package/esm/transactions/ethereum.js.map +1 -0
- package/esm/transactions/evm-cancellation.js +40 -0
- package/esm/transactions/evm-cancellation.js.map +1 -0
- package/esm/transactions/evm-local-signer.js +202 -0
- package/esm/transactions/evm-local-signer.js.map +1 -0
- package/esm/transactions/evm-submission.js +187 -0
- package/esm/transactions/evm-submission.js.map +1 -0
- package/esm/transactions/prepared-evm.js +194 -0
- package/esm/transactions/prepared-evm.js.map +1 -0
- package/esm/transactions/syscoin-core.js +691 -0
- package/esm/transactions/syscoin-core.js.map +1 -0
- package/esm/trezor/connect.js +19 -2
- package/esm/trezor/connect.js.map +1 -1
- package/esm/trezor/index.js +38 -16
- package/esm/trezor/index.js.map +1 -1
- package/esm/types.js.map +1 -1
- package/esm/utils/excluded-utxos.js +53 -0
- package/esm/utils/excluded-utxos.js.map +1 -0
- package/esm/utils/owned-blockbook-account.js +9 -0
- package/esm/utils/owned-blockbook-account.js.map +1 -0
- package/esm/utils/psbt.js +32 -0
- package/esm/utils/psbt.js.map +1 -0
- package/esm/utils/watch-descriptor.js +138 -0
- package/esm/utils/watch-descriptor.js.map +1 -0
- package/package.json +17 -11
- package/types/current-vault-refusal.d.ts +2 -0
- package/types/evm-fees.d.ts +25 -0
- package/types/hardware-transaction-authority.d.ts +5 -0
- package/types/index.d.ts +3 -3
- package/types/keyring-core.d.ts +48 -44
- package/types/keyring-host.d.ts +46 -0
- package/types/keyring-manager.d.ts +14 -7
- package/types/ledger/dmk-host.d.ts +7 -0
- package/types/ledger/dmk-selected-host.d.ts +7 -0
- package/types/ledger/dmk-session.d.ts +10 -2
- package/types/ledger/host-types.d.ts +4 -1
- package/types/ledger/index.d.ts +1 -1
- package/types/ledger/types.d.ts +3 -2
- package/types/ledger-host.d.ts +6 -2
- package/types/providers.d.ts +1 -1
- package/types/software.d.ts +7 -19
- package/types/storage-core.d.ts +3 -1
- package/types/transactions/ethereum.d.ts +14 -11
- package/types/transactions/evm-cancellation.d.ts +10 -0
- package/types/transactions/evm-local-signer.d.ts +2 -2
- package/types/transactions/evm-submission.d.ts +11 -4
- package/types/transactions/hardware-psbt.d.ts +1 -1
- package/types/transactions/index.d.ts +3 -2
- package/types/transactions/prepared-evm.d.ts +4 -1
- package/types/transactions/syscoin-core.d.ts +48 -21
- package/types/trezor/connect.d.ts +6 -3
- package/types/trezor/index.d.ts +4 -3
- package/types/types.d.ts +95 -63
- package/types/utils/owned-blockbook-account.d.ts +3 -0
- package/cjs/hardware-wallet-manager-singleton.js +0 -34
- package/cjs/hardware-wallet-manager-singleton.js.map +0 -1
- package/cjs/storage.js +0 -20
- package/cjs/storage.js.map +0 -1
- package/cjs/transactions/syscoin.js +0 -16
- package/cjs/transactions/syscoin.js.map +0 -1
- package/cjs/utils/blockbook-cache.js +0 -76
- package/cjs/utils/blockbook-cache.js.map +0 -1
- package/types/hardware-wallet-manager-singleton.d.ts +0 -19
- package/types/storage.d.ts +0 -5
- package/types/transactions/syscoin.d.ts +0 -9
- package/types/utils/blockbook-cache.d.ts +0 -6
package/cjs/keyring-core.js
CHANGED
|
@@ -32,9 +32,21 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
32
32
|
return result;
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
|
+
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
36
|
+
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
37
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
38
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
39
|
+
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
40
|
+
};
|
|
41
|
+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
42
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
43
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
44
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
45
|
+
};
|
|
35
46
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
47
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
48
|
};
|
|
49
|
+
var _KeyringCore_instances, _KeyringCore_deviceUnlockRetired, _KeyringCore_sessionOwnership, _KeyringCore_createDeviceUnlockControls, _KeyringCore_acceptSessionFrom, _KeyringCore_assertSessionOwner, _KeyringCore_captureSessionGuard, _KeyringCore_replaceSessionBuffers, _KeyringCore_finishUnlock, _KeyringCore_installVaultSession, _KeyringCore_recreateSessionFromVault;
|
|
38
50
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
51
|
exports.KeyringCore = void 0;
|
|
40
52
|
const secp256k1_1 = __importDefault(require("@bitcoinerlab/secp256k1"));
|
|
@@ -56,8 +68,10 @@ const SLH_DSA_DERIVATION_VERSION = 1;
|
|
|
56
68
|
// Pali expands this setup secret into the 48-byte SLH skSeed/skPrf/pkSeed tuple.
|
|
57
69
|
const SLH_DSA_SETUP_SECRET_HEX_LENGTH = 64;
|
|
58
70
|
const SLH_DSA_PARAMETER_SET = 'SLH-DSA-SHA2-128-24';
|
|
71
|
+
const current_vault_refusal_1 = require("./current-vault-refusal");
|
|
59
72
|
const ethers_v6_1 = require("./ethers-v6");
|
|
60
73
|
const initial_state_1 = require("./initial-state");
|
|
74
|
+
const keyring_host_1 = require("./keyring-host");
|
|
61
75
|
const private_records_1 = require("./private-records");
|
|
62
76
|
const signers_1 = require("./signers");
|
|
63
77
|
const software_hd_account_1 = require("./software-hd-account");
|
|
@@ -67,10 +81,32 @@ const evm_local_signer_1 = require("./transactions/evm-local-signer");
|
|
|
67
81
|
const prepared_evm_1 = require("./transactions/prepared-evm");
|
|
68
82
|
const syscoin_core_1 = require("./transactions/syscoin-core");
|
|
69
83
|
const types_1 = require("./types");
|
|
70
|
-
const blockbook_cache_1 = require("./utils/blockbook-cache");
|
|
71
84
|
const derivation_paths_1 = require("./utils/derivation-paths");
|
|
85
|
+
const owned_blockbook_account_1 = require("./utils/owned-blockbook-account");
|
|
72
86
|
const psbt_account_scope_1 = require("./utils/psbt-account-scope");
|
|
73
87
|
const watch_descriptor_1 = require("./utils/watch-descriptor");
|
|
88
|
+
const currentVaultSalt = (keys) => {
|
|
89
|
+
const salt = keys && typeof keys === 'object' && Reflect.ownKeys(keys).length === 1
|
|
90
|
+
? Object.getOwnPropertyDescriptor(keys, 'salt')?.value
|
|
91
|
+
: undefined;
|
|
92
|
+
return typeof salt === 'string' && /^[0-9a-f]{32}$/.test(salt)
|
|
93
|
+
? salt
|
|
94
|
+
: undefined;
|
|
95
|
+
};
|
|
96
|
+
const readCurrentVaultPair = async (readPair) => {
|
|
97
|
+
const pair = await readPair();
|
|
98
|
+
if (pair === null)
|
|
99
|
+
return null;
|
|
100
|
+
const { vault, keys, pairFingerprint } = pair;
|
|
101
|
+
const salt = currentVaultSalt(keys);
|
|
102
|
+
if (typeof vault !== 'string' ||
|
|
103
|
+
salt === undefined ||
|
|
104
|
+
typeof pairFingerprint !== 'string' ||
|
|
105
|
+
!/^[0-9a-f]{64}$/.test(pairFingerprint)) {
|
|
106
|
+
throw new Error('Invalid device unlock vault pair');
|
|
107
|
+
}
|
|
108
|
+
return { vault, salt, pairFingerprint };
|
|
109
|
+
};
|
|
74
110
|
// Dynamic ETH HD path generation - will be computed as needed
|
|
75
111
|
/**
|
|
76
112
|
* Secure Buffer implementation for sensitive data
|
|
@@ -111,7 +147,8 @@ class SecureBuffer {
|
|
|
111
147
|
}
|
|
112
148
|
}
|
|
113
149
|
class KeyringCore {
|
|
114
|
-
constructor(
|
|
150
|
+
constructor(binding, hardware) {
|
|
151
|
+
_KeyringCore_instances.add(this);
|
|
115
152
|
// Store getter function for accessing Redux state
|
|
116
153
|
this.getVaultState = null;
|
|
117
154
|
// Method to inject store getter from Pali side
|
|
@@ -156,7 +193,9 @@ class KeyringCore {
|
|
|
156
193
|
this.sessionPassword = null;
|
|
157
194
|
this.sessionMnemonic = null; // can be a mnemonic or a zprv, can be changed to a zprv when using an imported wallet
|
|
158
195
|
this.privateRecordSessionRevision = 0;
|
|
159
|
-
|
|
196
|
+
_KeyringCore_deviceUnlockRetired.set(this, false);
|
|
197
|
+
_KeyringCore_sessionOwnership.set(this, void 0);
|
|
198
|
+
this.createSyscoinTransactions = () => new syscoin_core_1.SyscoinTransactionsCore(this.getSigner, this.getReadOnlySigner, this.getAccountsState, this.syscoinHardwareSigners, this.capturePsbtSigningContext);
|
|
160
199
|
this.requireLedgerSigner = () => {
|
|
161
200
|
if (!this.ledgerSigner)
|
|
162
201
|
throw (0, syscoin_core_1.createHardwareSignerUnavailableError)('Ledger');
|
|
@@ -168,12 +207,6 @@ class KeyringCore {
|
|
|
168
207
|
return this.trezorSigner;
|
|
169
208
|
};
|
|
170
209
|
// ===================================== PUBLIC METHODS - KEYRING MANAGER FOR HD - SYS ALL ===================================== //
|
|
171
|
-
this.setStorage = (client) => {
|
|
172
|
-
if (!this.storage.setClient) {
|
|
173
|
-
throw new Error('Storage replacement is unavailable in this host');
|
|
174
|
-
}
|
|
175
|
-
this.storage.setClient(client);
|
|
176
|
-
};
|
|
177
210
|
this.validateAccountType = (account) => {
|
|
178
211
|
return account.isImported === true
|
|
179
212
|
? types_1.KeyringAccountType.Imported
|
|
@@ -191,6 +224,11 @@ class KeyringCore {
|
|
|
191
224
|
this.sessionMnemonic.clear();
|
|
192
225
|
this.sessionMnemonic = null;
|
|
193
226
|
}
|
|
227
|
+
const ownership = __classPrivateFieldGet(this, _KeyringCore_sessionOwnership, "f");
|
|
228
|
+
if (ownership.tracker.current === ownership.token) {
|
|
229
|
+
ownership.tracker.current = undefined;
|
|
230
|
+
ownership.tracker.epoch++;
|
|
231
|
+
}
|
|
194
232
|
// Clear transaction handlers that may hold HD signers
|
|
195
233
|
if (this.syscoinTransaction) {
|
|
196
234
|
// Replace with empty object to clear references
|
|
@@ -234,40 +272,13 @@ class KeyringCore {
|
|
|
234
272
|
if (!this.sessionPassword || !this.sessionMnemonic) {
|
|
235
273
|
throw new Error('Session data is missing during transfer');
|
|
236
274
|
}
|
|
275
|
+
__classPrivateFieldGet(targetKeyring, _KeyringCore_instances, "m", _KeyringCore_acceptSessionFrom).call(targetKeyring, this);
|
|
237
276
|
this.privateRecordSessionRevision++;
|
|
238
|
-
|
|
277
|
+
__classPrivateFieldSet(this, _KeyringCore_deviceUnlockRetired, true, "f");
|
|
239
278
|
// Null out our references (do NOT clear buffers - target owns them now)
|
|
240
279
|
this.sessionPassword = null;
|
|
241
280
|
this.sessionMnemonic = null;
|
|
242
281
|
};
|
|
243
|
-
// The checked transfer path never dispatches through a public host receiver.
|
|
244
|
-
this.acceptSessionFrom = (source) => {
|
|
245
|
-
if (source.authority !== this.authority ||
|
|
246
|
-
source.storage !== this.storage ||
|
|
247
|
-
source.vaultPersistence !== this.vaultPersistence) {
|
|
248
|
-
throw new Error('Cannot transfer session between different wallet authorities');
|
|
249
|
-
}
|
|
250
|
-
if (!source.sessionPassword || !source.sessionMnemonic) {
|
|
251
|
-
throw new Error('Session data is missing during transfer');
|
|
252
|
-
}
|
|
253
|
-
this.replaceSessionBuffers(source.sessionPassword, source.sessionMnemonic);
|
|
254
|
-
};
|
|
255
|
-
// Legacy trusted-host API. SoftwareKeyring disables direct buffer injection.
|
|
256
|
-
this.receiveSessionOwnership = (sessionPassword, sessionMnemonic) => this.replaceSessionBuffers(sessionPassword, sessionMnemonic);
|
|
257
|
-
this.replaceSessionBuffers = (sessionPassword, sessionMnemonic) => {
|
|
258
|
-
this.privateRecordSessionRevision++;
|
|
259
|
-
// Clear any existing data first
|
|
260
|
-
if (this.sessionPassword) {
|
|
261
|
-
this.sessionPassword.clear();
|
|
262
|
-
}
|
|
263
|
-
if (this.sessionMnemonic) {
|
|
264
|
-
this.sessionMnemonic.clear();
|
|
265
|
-
}
|
|
266
|
-
// Take ownership of the actual SecureBuffer objects
|
|
267
|
-
// No copying - these are the original objects
|
|
268
|
-
this.sessionPassword = sessionPassword;
|
|
269
|
-
this.sessionMnemonic = sessionMnemonic;
|
|
270
|
-
};
|
|
271
282
|
this.addNewAccount = async (label) => {
|
|
272
283
|
// Check if wallet is unlocked
|
|
273
284
|
if (!this.isUnlocked()) {
|
|
@@ -314,91 +325,41 @@ class KeyringCore {
|
|
|
314
325
|
xpub: derived.address,
|
|
315
326
|
};
|
|
316
327
|
};
|
|
317
|
-
this.
|
|
318
|
-
const
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
const {
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
const {
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
!this.isXpubLike(xpub))
|
|
346
|
-
return address;
|
|
347
|
-
return await this.getAddress(xpub, true);
|
|
348
|
-
};
|
|
349
|
-
this.getPubkey = async (id, isChangeAddress) => {
|
|
350
|
-
const vault = this.getVault();
|
|
351
|
-
const { accounts, activeAccount } = vault;
|
|
352
|
-
const account = accounts[activeAccount.type]?.[id];
|
|
353
|
-
if (!account) {
|
|
354
|
-
throw new Error(`Account with id ${id} not found`);
|
|
355
|
-
}
|
|
356
|
-
const { xpub, isImported, address } = account;
|
|
357
|
-
// Guard: single-address imported are watch-only
|
|
358
|
-
if (isImported && xpub === address) {
|
|
359
|
-
throw new Error('Public key not available for single-address imported accounts');
|
|
360
|
-
}
|
|
361
|
-
// Guard: descriptor/xpub watch-only (no xprv and not hardware)
|
|
362
|
-
if (this.isWatchOnlyAccount(account)) {
|
|
363
|
-
throw new Error('Public key not available for watch-only accounts');
|
|
364
|
-
}
|
|
365
|
-
return await this.getCurrentAddressPubkey(xpub, isChangeAddress);
|
|
366
|
-
};
|
|
367
|
-
this.getBip32Path = async (id, isChangeAddress) => {
|
|
368
|
-
const vault = this.getVault();
|
|
369
|
-
const { accounts, activeAccount } = vault;
|
|
370
|
-
const account = accounts[activeAccount.type]?.[id];
|
|
371
|
-
if (!account) {
|
|
372
|
-
throw new Error(`Account with id ${id} not found`);
|
|
373
|
-
}
|
|
374
|
-
const { xpub, isImported, address } = account;
|
|
375
|
-
// Guard: single-address imported are watch-only
|
|
376
|
-
if (isImported && xpub === address) {
|
|
377
|
-
throw new Error('BIP32 path not available for single-address imported accounts');
|
|
378
|
-
}
|
|
379
|
-
// Guard: descriptor/xpub watch-only (no xprv and not hardware)
|
|
380
|
-
if (this.isWatchOnlyAccount(account)) {
|
|
381
|
-
throw new Error('BIP32 path not available for watch-only accounts');
|
|
382
|
-
}
|
|
383
|
-
return await this.getCurrentAddressBip32Path(xpub, isChangeAddress);
|
|
384
|
-
};
|
|
385
|
-
this.updateReceivingAddress = async () => {
|
|
386
|
-
const vault = this.getVault();
|
|
387
|
-
const { accounts, activeAccount } = vault;
|
|
388
|
-
const account = accounts[activeAccount.type]?.[activeAccount.id];
|
|
389
|
-
if (!account) {
|
|
390
|
-
throw new Error('Active account not found');
|
|
391
|
-
}
|
|
328
|
+
this.getActiveUtxoAddressAccount = (id, ownedRead) => {
|
|
329
|
+
const captured = (0, syscoin_core_1.captureOwnedSyscoinRead)(ownedRead, this.getAccountsState, this.capturePsbtSigningContext);
|
|
330
|
+
if (id !== captured.accountId)
|
|
331
|
+
throw new Error('UTXO account is not active');
|
|
332
|
+
const account = this.getVault().accounts[captured.accountType]?.[id];
|
|
333
|
+
captured.read.assertCurrent();
|
|
334
|
+
return { account, read: captured.read };
|
|
335
|
+
};
|
|
336
|
+
this.getNewChangeAddress = (ownedRead) => this.getChangeAddress(this.getVault().activeAccount.id, ownedRead);
|
|
337
|
+
this.getChangeAddress = async (id, ownedRead) => {
|
|
338
|
+
const { account, read } = this.getActiveUtxoAddressAccount(id, ownedRead);
|
|
339
|
+
return this.getAddressForUtxoAccount(account, true, read);
|
|
340
|
+
};
|
|
341
|
+
this.getPubkey = async (id, isChangeAddress, ownedRead) => {
|
|
342
|
+
const { account, read } = this.getActiveUtxoAddressAccount(id, ownedRead);
|
|
343
|
+
this.assertUtxoDerivationAvailable(account, 'Public key');
|
|
344
|
+
return this.getAddressPubkey(isChangeAddress, read);
|
|
345
|
+
};
|
|
346
|
+
this.getBip32Path = async (id, isChangeAddress, ownedRead) => {
|
|
347
|
+
const { account, read } = this.getActiveUtxoAddressAccount(id, ownedRead);
|
|
348
|
+
this.assertUtxoDerivationAvailable(account, 'BIP32 path');
|
|
349
|
+
return this.getAddressBip32Path(id, isChangeAddress, read);
|
|
350
|
+
};
|
|
351
|
+
this.updateReceivingAddress = async (ownedRead) => {
|
|
352
|
+
const { account, read } = this.getActiveUtxoAddressAccount(this.getVault().activeAccount.id, ownedRead);
|
|
353
|
+
return this.getAddressForUtxoAccount(account, false, read);
|
|
354
|
+
};
|
|
355
|
+
this.getAddressForUtxoAccount = (account, change, read) => {
|
|
392
356
|
const { xpub, isImported, address } = account;
|
|
393
357
|
if (isImported &&
|
|
394
358
|
xpub === address &&
|
|
395
359
|
!this.isDescriptor(xpub) &&
|
|
396
360
|
!this.isXpubLike(xpub))
|
|
397
|
-
return address;
|
|
398
|
-
|
|
399
|
-
// NOTE: Address updates should be dispatched to Redux store, not updated here
|
|
400
|
-
// The calling code should handle the Redux dispatch
|
|
401
|
-
return nextAddress;
|
|
361
|
+
return Promise.resolve(address);
|
|
362
|
+
return this.discoverUtxoAddress(change, read);
|
|
402
363
|
};
|
|
403
364
|
this.getAccountById = (id, accountType) => {
|
|
404
365
|
const vault = this.getVault();
|
|
@@ -676,62 +637,56 @@ class KeyringCore {
|
|
|
676
637
|
};
|
|
677
638
|
this.getNetwork = () => this.getVault().activeNetwork;
|
|
678
639
|
this.createEthAccount = (privateKey) => (0, evm_local_signer_1.privateKeyToAccount)(privateKey);
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
const
|
|
682
|
-
|
|
683
|
-
// Use read-only signer for backend calls - works for all account types including hardware wallets
|
|
684
|
-
const { main } = this.getReadOnlySigner();
|
|
685
|
-
const options = 'tokens=used&details=tokens';
|
|
686
|
-
// In-flight deduplicated: concurrent callers within a send flow
|
|
687
|
-
// (fee estimation, PSBT creation, signing) share a single request
|
|
688
|
-
const { tokens } = await (0, blockbook_cache_1.fetchBackendAccountCached)(main.blockbookURL, xpub, options);
|
|
640
|
+
this.fetchCurrentAccountData = async (isChangeAddress, read) => {
|
|
641
|
+
read.assertCurrent();
|
|
642
|
+
const { tokens } = await (0, owned_blockbook_account_1.fetchOwnedBlockbookAccount)(read.network.url, read.xpub, read.transport);
|
|
643
|
+
read.assertCurrent();
|
|
689
644
|
const { receivingIndex, changeIndex } = this.setLatestIndexesFromXPubTokens(tokens);
|
|
690
|
-
// Get network configuration for BIP84 using network-provided pub type macros
|
|
691
|
-
const networkConfig = (0, sysweb3_network_1.getNetworkConfig)(activeNetwork.slip44, activeNetwork.currency);
|
|
692
|
-
const pubTypes = networkConfig?.types?.zPubType;
|
|
693
|
-
if (!pubTypes) {
|
|
694
|
-
throw new Error('Missing zPubType in network configuration');
|
|
695
|
-
}
|
|
696
|
-
const networks = networkConfig.networks;
|
|
697
|
-
const currentAccount = new BIP84.fromZPub(xpub, pubTypes, networks);
|
|
698
|
-
const addressIndex = isChangeAddress ? changeIndex : receivingIndex;
|
|
699
645
|
return {
|
|
700
|
-
currentAccount,
|
|
701
|
-
addressIndex,
|
|
702
|
-
main,
|
|
646
|
+
currentAccount: this.loadBip84AddressAccount(read.xpub, read.network),
|
|
647
|
+
addressIndex: isChangeAddress ? changeIndex : receivingIndex,
|
|
703
648
|
};
|
|
704
649
|
};
|
|
705
|
-
this.
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
const { currentAccount, addressIndex } = await this.fetchCurrentAccountData(xpub, isChangeAddress);
|
|
724
|
-
// BIP84 returns the public key as a hex string directly
|
|
650
|
+
this.discoverUtxoAddress = async (isChangeAddress, read) => {
|
|
651
|
+
read.assertCurrent();
|
|
652
|
+
if (this.isDescriptor(read.xpub))
|
|
653
|
+
throw new Error('Address rotation is not supported for watch-only descriptors');
|
|
654
|
+
const { currentAccount, addressIndex } = await this.fetchCurrentAccountData(isChangeAddress, read);
|
|
655
|
+
read.assertCurrent();
|
|
656
|
+
return currentAccount.getAddress(addressIndex, isChangeAddress, 84);
|
|
657
|
+
};
|
|
658
|
+
this.getAddress = async (xpub, isChangeAddress, ownedRead) => {
|
|
659
|
+
const id = this.getVault().activeAccount.id;
|
|
660
|
+
const { account, read } = this.getActiveUtxoAddressAccount(id, ownedRead);
|
|
661
|
+
if (xpub !== read.xpub)
|
|
662
|
+
throw new Error('UTXO account is not active');
|
|
663
|
+
return this.getAddressForUtxoAccount(account, isChangeAddress, read);
|
|
664
|
+
};
|
|
665
|
+
this.getAddressPubkey = async (isChangeAddress, read) => {
|
|
666
|
+
const { currentAccount, addressIndex } = await this.fetchCurrentAccountData(isChangeAddress, read);
|
|
667
|
+
read.assertCurrent();
|
|
725
668
|
return currentAccount.getPublicKey(addressIndex, isChangeAddress);
|
|
726
669
|
};
|
|
727
|
-
this.
|
|
728
|
-
const
|
|
729
|
-
const {
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
670
|
+
this.getCurrentAddressPubkey = async (xpub, isChangeAddress, ownedRead) => {
|
|
671
|
+
const id = this.getVault().activeAccount.id;
|
|
672
|
+
const { account, read } = this.getActiveUtxoAddressAccount(id, ownedRead);
|
|
673
|
+
if (xpub !== read.xpub)
|
|
674
|
+
throw new Error('UTXO account is not active');
|
|
675
|
+
this.assertUtxoDerivationAvailable(account, 'Public key');
|
|
676
|
+
return this.getAddressPubkey(isChangeAddress, read);
|
|
677
|
+
};
|
|
678
|
+
this.getAddressBip32Path = async (id, isChangeAddress, read) => {
|
|
679
|
+
const { addressIndex } = await this.fetchCurrentAccountData(isChangeAddress, read);
|
|
680
|
+
read.assertCurrent();
|
|
681
|
+
return (0, derivation_paths_1.getAddressDerivationPath)(read.network.currency.toLowerCase(), read.network.slip44, id, isChangeAddress, addressIndex);
|
|
682
|
+
};
|
|
683
|
+
this.getCurrentAddressBip32Path = async (xpub, isChangeAddress, ownedRead) => {
|
|
684
|
+
const id = this.getVault().activeAccount.id;
|
|
685
|
+
const { account, read } = this.getActiveUtxoAddressAccount(id, ownedRead);
|
|
686
|
+
if (xpub !== read.xpub)
|
|
687
|
+
throw new Error('UTXO account is not active');
|
|
688
|
+
this.assertUtxoDerivationAvailable(account, 'BIP32 path');
|
|
689
|
+
return this.getAddressBip32Path(id, isChangeAddress, read);
|
|
735
690
|
};
|
|
736
691
|
this.logout = () => {
|
|
737
692
|
this.lockWallet();
|
|
@@ -817,8 +772,9 @@ class KeyringCore {
|
|
|
817
772
|
throw error;
|
|
818
773
|
}
|
|
819
774
|
};
|
|
820
|
-
this.capturePsbtSigningContext = () => {
|
|
775
|
+
this.capturePsbtSigningContext = (bindSessionRevision = true) => {
|
|
821
776
|
const session = this.sessionPassword;
|
|
777
|
+
const revision = this.privateRecordSessionRevision;
|
|
822
778
|
const vault = this.getVault();
|
|
823
779
|
const selected = { ...vault.activeAccount };
|
|
824
780
|
const account = vault.accounts[selected.type]?.[selected.id];
|
|
@@ -830,6 +786,8 @@ class KeyringCore {
|
|
|
830
786
|
if (!session ||
|
|
831
787
|
session.isCleared() ||
|
|
832
788
|
this.sessionPassword !== session ||
|
|
789
|
+
(bindSessionRevision &&
|
|
790
|
+
this.privateRecordSessionRevision !== revision) ||
|
|
833
791
|
current.activeAccount.id !== selected.id ||
|
|
834
792
|
current.activeAccount.type !== selected.type ||
|
|
835
793
|
currentAccount?.address !== identity.address ||
|
|
@@ -932,22 +890,32 @@ class KeyringCore {
|
|
|
932
890
|
};
|
|
933
891
|
};
|
|
934
892
|
// Read-only version that works when wallet is locked
|
|
935
|
-
this.getReadOnlySigner = () => {
|
|
936
|
-
|
|
893
|
+
this.getReadOnlySigner = (ownedRead) => {
|
|
894
|
+
ownedRead?.assertCurrent();
|
|
895
|
+
if ((ownedRead ? ownedRead.network.kind : this.getActiveChain()) !==
|
|
896
|
+
sysweb3_network_1.INetworkType.Syscoin) {
|
|
937
897
|
throw new Error('Switch to UTXO chain');
|
|
938
898
|
}
|
|
939
899
|
// Create syscoinjs instance without HD signer for read-only operations
|
|
940
|
-
const
|
|
941
|
-
const network = vault.activeNetwork;
|
|
900
|
+
const network = ownedRead?.network ?? this.getVault().activeNetwork;
|
|
942
901
|
const networkConfig = (0, sysweb3_network_1.getNetworkConfig)(network.slip44, network.currency);
|
|
943
902
|
const isTestnet = network.slip44 === 1;
|
|
944
903
|
const bitcoinjsNetwork = isTestnet
|
|
945
904
|
? networkConfig?.networks?.testnet
|
|
946
905
|
: networkConfig?.networks?.mainnet;
|
|
906
|
+
if (ownedRead && !bitcoinjsNetwork)
|
|
907
|
+
throw new Error('Missing owned UTXO network configuration');
|
|
947
908
|
const syscoinMainSigner = new syscoinjs.SyscoinJSLib(null, // No HD signer needed for read-only operations
|
|
948
|
-
network.url, bitcoinjsNetwork);
|
|
909
|
+
network.url, bitcoinjsNetwork, ...(ownedRead ? [ownedRead.transport] : []));
|
|
910
|
+
if (!ownedRead)
|
|
911
|
+
return { main: syscoinMainSigner };
|
|
949
912
|
return {
|
|
950
913
|
main: syscoinMainSigner,
|
|
914
|
+
getOwnedAddress: (xpub, isChangeAddress) => {
|
|
915
|
+
if (xpub !== ownedRead.xpub)
|
|
916
|
+
throw new Error('UTXO account is not active');
|
|
917
|
+
return this.discoverUtxoAddress(isChangeAddress, ownedRead);
|
|
918
|
+
},
|
|
951
919
|
};
|
|
952
920
|
};
|
|
953
921
|
this.getAccountsState = () => {
|
|
@@ -1139,10 +1107,13 @@ class KeyringCore {
|
|
|
1139
1107
|
if (!BIP84.validateMnemonic(seedPhrase)) {
|
|
1140
1108
|
throw new Error('Invalid Seed');
|
|
1141
1109
|
}
|
|
1110
|
+
const assertCurrent = __classPrivateFieldGet(this, _KeyringCore_instances, "m", _KeyringCore_captureSessionGuard).call(this);
|
|
1111
|
+
assertCurrent();
|
|
1142
1112
|
const [vault, vaultKeys] = await Promise.all([
|
|
1143
1113
|
this.storage.get('vault'),
|
|
1144
1114
|
this.storage.get('vault-keys'),
|
|
1145
1115
|
]);
|
|
1116
|
+
assertCurrent();
|
|
1146
1117
|
const hasVault = vault !== undefined && vault !== null;
|
|
1147
1118
|
const hasVaultKeys = vaultKeys !== undefined && vaultKeys !== null;
|
|
1148
1119
|
const livePassword = this.sessionPassword;
|
|
@@ -1157,6 +1128,7 @@ class KeyringCore {
|
|
|
1157
1128
|
throw new Error('Cannot initialize a new vault over existing wallet storage');
|
|
1158
1129
|
}
|
|
1159
1130
|
const sessionPasswordSaltedHash = await this.deriveStoredSessionKey(password, vaultKeys);
|
|
1131
|
+
assertCurrent();
|
|
1160
1132
|
if (livePassword === this.sessionPassword &&
|
|
1161
1133
|
liveMnemonic === this.sessionMnemonic &&
|
|
1162
1134
|
sessionPasswordSaltedHash === this.getSessionPasswordString()) {
|
|
@@ -1172,6 +1144,7 @@ class KeyringCore {
|
|
|
1172
1144
|
}
|
|
1173
1145
|
if (currentMnemonic === seedPhrase) {
|
|
1174
1146
|
const storedVault = await this.vaultPersistence.getDecryptedVault(sessionPasswordSaltedHash);
|
|
1147
|
+
assertCurrent();
|
|
1175
1148
|
if (livePassword === this.sessionPassword &&
|
|
1176
1149
|
liveMnemonic === this.sessionMnemonic &&
|
|
1177
1150
|
!livePassword.isCleared() &&
|
|
@@ -1197,8 +1170,10 @@ class KeyringCore {
|
|
|
1197
1170
|
mnemonic: seedPhrase, // Store plain mnemonic - setEncryptedVault will encrypt the entire vault
|
|
1198
1171
|
},
|
|
1199
1172
|
// v4 vault is encrypted with the derived session password key (PBKDF2 output)
|
|
1200
|
-
sessionPasswordSaltedHash, { salt });
|
|
1201
|
-
|
|
1173
|
+
sessionPasswordSaltedHash, { salt }, assertCurrent);
|
|
1174
|
+
assertCurrent();
|
|
1175
|
+
const assertInstalled = await __classPrivateFieldGet(this, _KeyringCore_instances, "m", _KeyringCore_recreateSessionFromVault).call(this, sessionPasswordSaltedHash, assertCurrent);
|
|
1176
|
+
assertInstalled();
|
|
1202
1177
|
};
|
|
1203
1178
|
// NEW: Create first account without signer setup
|
|
1204
1179
|
this.createFirstAccount = async (label) => {
|
|
@@ -1237,27 +1212,30 @@ class KeyringCore {
|
|
|
1237
1212
|
await this.initializeSession(seedPhrase, password);
|
|
1238
1213
|
return await this.createFirstAccount();
|
|
1239
1214
|
};
|
|
1240
|
-
|
|
1241
|
-
typeof dependencies.authority !== 'object' ||
|
|
1242
|
-
dependencies.authority === null) {
|
|
1243
|
-
throw new TypeError('Keyring core requires an explicit wallet authority');
|
|
1244
|
-
}
|
|
1245
|
-
if (typeof dependencies.vaultPersistence?.getDecryptedVault !== 'function' ||
|
|
1246
|
-
typeof dependencies.vaultPersistence?.setEncryptedVault !== 'function') {
|
|
1247
|
-
throw new TypeError('Keyring core requires bound vault persistence');
|
|
1248
|
-
}
|
|
1215
|
+
const dependencies = (0, keyring_host_1.takeKeyringHostBinding)(binding);
|
|
1249
1216
|
this.authority = dependencies.authority;
|
|
1217
|
+
__classPrivateFieldSet(this, _KeyringCore_sessionOwnership, {
|
|
1218
|
+
tracker: dependencies.sessionOwner,
|
|
1219
|
+
token: Symbol(),
|
|
1220
|
+
}, "f");
|
|
1250
1221
|
this.storage = dependencies.storage;
|
|
1251
1222
|
this.vaultPersistence = dependencies.vaultPersistence;
|
|
1252
|
-
this.ledgerSigner =
|
|
1253
|
-
this.trezorSigner =
|
|
1254
|
-
this.syscoinHardwareSigners = { ...
|
|
1223
|
+
this.ledgerSigner = hardware.ledgerSigner;
|
|
1224
|
+
this.trezorSigner = hardware.trezorSigner;
|
|
1225
|
+
this.syscoinHardwareSigners = { ...hardware.syscoinHardwareSigners };
|
|
1255
1226
|
this.writeUtf8Diagnostic(false);
|
|
1256
1227
|
this.utf8Error = false;
|
|
1257
1228
|
this.initialTrezorAccountState = initial_state_1.initialActiveTrezorAccountState;
|
|
1258
1229
|
this.initialLedgerAccountState = initial_state_1.initialActiveLedgerAccountState;
|
|
1259
1230
|
this.syscoinTransaction = this.createSyscoinTransactions();
|
|
1260
1231
|
this.ethereumTransaction = new ethereum_1.EthereumTransactions(this.getNetwork, this.getDecryptedPrivateKey, this.getAccountsState, this.ledgerSigner, this.trezorSigner, this.capturePsbtSigningContext, dependencies.captureEvmSubmission);
|
|
1232
|
+
if (dependencies.deviceUnlock) {
|
|
1233
|
+
const { port, receive } = dependencies.deviceUnlock;
|
|
1234
|
+
const readVaultPair = this.storage.readDeviceUnlockVaultPair;
|
|
1235
|
+
if (typeof readVaultPair !== 'function')
|
|
1236
|
+
throw new TypeError('Device unlock requires an atomic vault pair reader');
|
|
1237
|
+
receive(__classPrivateFieldGet(this, _KeyringCore_instances, "m", _KeyringCore_createDeviceUnlockControls).call(this, port, readVaultPair.bind(this.storage)));
|
|
1238
|
+
}
|
|
1261
1239
|
}
|
|
1262
1240
|
// Convenience method for complete setup after construction
|
|
1263
1241
|
async initialize(seed, password, network) {
|
|
@@ -1277,18 +1255,31 @@ class KeyringCore {
|
|
|
1277
1255
|
* not charge a failed password attempt for an operational error.
|
|
1278
1256
|
*/
|
|
1279
1257
|
async unlock(password) {
|
|
1258
|
+
let assertCurrent = __classPrivateFieldGet(this, _KeyringCore_instances, "m", _KeyringCore_captureSessionGuard).call(this);
|
|
1280
1259
|
let authenticated = false;
|
|
1281
1260
|
try {
|
|
1261
|
+
assertCurrent();
|
|
1282
1262
|
const vaultKeys = await this.storage.get('vault-keys');
|
|
1283
1263
|
if (!vaultKeys) {
|
|
1264
|
+
assertCurrent();
|
|
1284
1265
|
throw new Error('Vault keys not found. Reload the wallet and try again.');
|
|
1285
1266
|
}
|
|
1286
1267
|
// v4: Validate password by deriving the session key once and decrypting the vault with it.
|
|
1287
1268
|
// This avoids an extra KDF pass just for a stored verifier.
|
|
1288
1269
|
const { salt } = vaultKeys;
|
|
1270
|
+
const readVaultPair = this.storage.readDeviceUnlockVaultPair;
|
|
1271
|
+
const accountingSalt = readVaultPair
|
|
1272
|
+
? currentVaultSalt(vaultKeys)
|
|
1273
|
+
: undefined;
|
|
1274
|
+
// Permit only an authenticated negative verdict for the same atomic
|
|
1275
|
+
// pair after cancellation. Legacy migration and positive results stay guarded.
|
|
1276
|
+
if (accountingSalt === undefined)
|
|
1277
|
+
assertCurrent();
|
|
1289
1278
|
// Derive session encryption key (in-memory only) used to encrypt/decrypt xprv fields
|
|
1290
1279
|
// IMPORTANT: This MUST NOT be the same value as the stored verifier above.
|
|
1291
|
-
let sessionPasswordKey = await this.deriveStoredSessionKey(password, vaultKeys);
|
|
1280
|
+
let sessionPasswordKey = await this.deriveStoredSessionKey(password, accountingSalt === undefined ? vaultKeys : { salt: accountingSalt });
|
|
1281
|
+
if (accountingSalt === undefined)
|
|
1282
|
+
assertCurrent();
|
|
1292
1283
|
// Handle migration from old vault format with currentSessionSalt
|
|
1293
1284
|
if (vaultKeys.currentSessionSalt) {
|
|
1294
1285
|
console.log('[KeyringManager] Detected old vault format, handling session migration...');
|
|
@@ -1299,6 +1290,7 @@ class KeyringCore {
|
|
|
1299
1290
|
// derived key. Selecting by stored format also permits a safe retry
|
|
1300
1291
|
// if the vault write succeeded but the metadata write was interrupted.
|
|
1301
1292
|
const { mnemonic } = await this.vaultPersistence.getDecryptedVault(sessionPasswordKey, password);
|
|
1293
|
+
assertCurrent();
|
|
1302
1294
|
authenticated = true;
|
|
1303
1295
|
const isValidSecret = (value) => {
|
|
1304
1296
|
if (typeof value !== 'string' || !value)
|
|
@@ -1338,13 +1330,16 @@ class KeyringCore {
|
|
|
1338
1330
|
throw new Error('Legacy vault secret could not be recovered. Existing vault preserved.');
|
|
1339
1331
|
}
|
|
1340
1332
|
// Do not change either stored record until a valid secret is recovered.
|
|
1341
|
-
await this.vaultPersistence.setEncryptedVault({ mnemonic: decryptedMnemonic }, sessionPasswordKey);
|
|
1333
|
+
await this.vaultPersistence.setEncryptedVault({ mnemonic: decryptedMnemonic }, sessionPasswordKey, undefined, assertCurrent);
|
|
1334
|
+
assertCurrent();
|
|
1342
1335
|
console.log('[KeyringManager] Vault mnemonic format normalized');
|
|
1343
1336
|
// Remove currentSessionSalt from vault-keys (v4 keeps only the salt on disk)
|
|
1344
1337
|
const migratedVaultKeys = {
|
|
1345
1338
|
salt: vaultKeys.salt,
|
|
1346
1339
|
};
|
|
1340
|
+
assertCurrent();
|
|
1347
1341
|
await this.storage.set('vault-keys', migratedVaultKeys);
|
|
1342
|
+
assertCurrent();
|
|
1348
1343
|
console.log('[KeyringManager] Old vault format migration completed');
|
|
1349
1344
|
}
|
|
1350
1345
|
// v4 vault is encrypted with the derived session password key (PBKDF2 output).
|
|
@@ -1355,6 +1350,25 @@ class KeyringCore {
|
|
|
1355
1350
|
decryptedVault = await this.vaultPersistence.getDecryptedVault(sessionPasswordKey);
|
|
1356
1351
|
}
|
|
1357
1352
|
catch (error) {
|
|
1353
|
+
const matchesRefusedVault = accountingSalt === undefined
|
|
1354
|
+
? undefined
|
|
1355
|
+
: current_vault_refusal_1.currentVaultRefusals.get(error);
|
|
1356
|
+
if (matchesRefusedVault && readVaultPair) {
|
|
1357
|
+
const pair = await readCurrentVaultPair(readVaultPair.bind(this.storage)).catch((readError) => {
|
|
1358
|
+
// A storage rejection cannot attest to password authentication.
|
|
1359
|
+
if (readError instanceof storage_core_1.VaultAuthenticationError)
|
|
1360
|
+
throw new Error('Wallet unlock vault read failed');
|
|
1361
|
+
throw readError;
|
|
1362
|
+
});
|
|
1363
|
+
if (!pair ||
|
|
1364
|
+
!matchesRefusedVault(pair.vault) ||
|
|
1365
|
+
pair.salt !== accountingSalt)
|
|
1366
|
+
throw new Error('Wallet unlock vault changed');
|
|
1367
|
+
// This exact negative result has no session authority. Keep it
|
|
1368
|
+
// available for the host counter even if its owner just retired.
|
|
1369
|
+
return { canLogin: false };
|
|
1370
|
+
}
|
|
1371
|
+
assertCurrent();
|
|
1358
1372
|
// Only the historical CBC fallback used the 20k key without metadata.
|
|
1359
1373
|
// Never try it for GCM, malformed records, or platform/storage failures.
|
|
1360
1374
|
const rawVault = error instanceof storage_core_1.VaultAuthenticationError &&
|
|
@@ -1362,65 +1376,54 @@ class KeyringCore {
|
|
|
1362
1376
|
!vaultKeys.currentSessionSalt
|
|
1363
1377
|
? await this.storage.get('vault')
|
|
1364
1378
|
: undefined;
|
|
1379
|
+
assertCurrent();
|
|
1365
1380
|
if (typeof rawVault !== 'string' || rawVault.trim().startsWith('{')) {
|
|
1366
1381
|
throw error;
|
|
1367
1382
|
}
|
|
1368
1383
|
const legacyKey = await this.deriveLegacySessionKey(password, salt);
|
|
1384
|
+
assertCurrent();
|
|
1369
1385
|
decryptedVault = await this.vaultPersistence.getDecryptedVault(legacyKey);
|
|
1386
|
+
assertCurrent();
|
|
1370
1387
|
authenticated = true;
|
|
1371
1388
|
sessionPasswordKey = legacyKey;
|
|
1372
1389
|
// Keep the historical account wrapping key. Persist its profile before
|
|
1373
1390
|
// GCM rewrapping so either interrupted write remains recoverable.
|
|
1374
|
-
|
|
1391
|
+
const migratedVaultKeys = {
|
|
1375
1392
|
...vaultKeys,
|
|
1376
1393
|
keyDerivation: LEGACY_ENCRYPTION_KDF,
|
|
1377
|
-
}
|
|
1394
|
+
};
|
|
1395
|
+
assertCurrent();
|
|
1396
|
+
await this.storage.set('vault-keys', migratedVaultKeys);
|
|
1397
|
+
assertCurrent();
|
|
1378
1398
|
legacyProfile = true;
|
|
1379
1399
|
}
|
|
1400
|
+
assertCurrent();
|
|
1380
1401
|
authenticated = true;
|
|
1381
1402
|
if (legacyProfile && this.hasWebCrypto()) {
|
|
1382
1403
|
const storedVault = await this.storage.get('vault');
|
|
1404
|
+
assertCurrent();
|
|
1383
1405
|
if (typeof storedVault === 'string' &&
|
|
1384
1406
|
!storedVault.trim().startsWith('{')) {
|
|
1385
1407
|
// A failed earlier rewrap can leave the authenticated profile plus
|
|
1386
1408
|
// CBC. Retry that write without changing the account wrapping key.
|
|
1387
|
-
await this.vaultPersistence.setEncryptedVault(decryptedVault, sessionPasswordKey);
|
|
1409
|
+
await this.vaultPersistence.setEncryptedVault(decryptedVault, sessionPasswordKey, undefined, assertCurrent);
|
|
1410
|
+
assertCurrent();
|
|
1388
1411
|
}
|
|
1389
1412
|
}
|
|
1390
1413
|
// If session data missing or corrupted, recreate from vault
|
|
1391
1414
|
if (!this.sessionMnemonic) {
|
|
1392
|
-
await this.
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
// `lockWallet()` intentionally replaces it with a plain object to drop signer references,
|
|
1396
|
-
// so we must restore the real class instance on unlock.
|
|
1397
|
-
if (!this.syscoinTransaction ||
|
|
1398
|
-
typeof this.syscoinTransaction.getRecommendedFee !== 'function') {
|
|
1399
|
-
this.syscoinTransaction = this.createSyscoinTransactions();
|
|
1400
|
-
}
|
|
1401
|
-
// NOTE: Active account management is now handled by vault state/Redux
|
|
1402
|
-
// No need to explicitly set active account after unlock - it's managed externally
|
|
1403
|
-
const vault = this.getVault();
|
|
1404
|
-
if (vault.activeAccount?.id !== undefined && vault.activeAccount?.type) {
|
|
1405
|
-
// Check if the active account actually exists in the accounts map
|
|
1406
|
-
const accountType = vault.activeAccount.type;
|
|
1407
|
-
const accountId = vault.activeAccount.id;
|
|
1408
|
-
const accountExists = vault.accounts?.[accountType]?.[accountId];
|
|
1409
|
-
if (!accountExists) {
|
|
1410
|
-
console.log(`[KeyringManager] Active account ${accountType}:${accountId} not found in accounts map. This may indicate a migration from old vault format.`);
|
|
1411
|
-
// Signal that accounts need to be created after migration
|
|
1412
|
-
return {
|
|
1413
|
-
canLogin: true,
|
|
1414
|
-
needsAccountCreation: true,
|
|
1415
|
-
};
|
|
1416
|
-
}
|
|
1417
|
-
console.log(`[KeyringManager] Active account ${vault.activeAccount.id} available after unlock`);
|
|
1415
|
+
assertCurrent = await __classPrivateFieldGet(this, _KeyringCore_instances, "m", _KeyringCore_recreateSessionFromVault).call(this, sessionPasswordKey, assertCurrent);
|
|
1416
|
+
assertCurrent();
|
|
1417
|
+
console.log('[KeyringManager] Session data recreated from vault');
|
|
1418
1418
|
}
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1419
|
+
assertCurrent();
|
|
1420
|
+
const result = __classPrivateFieldGet(this, _KeyringCore_instances, "m", _KeyringCore_finishUnlock).call(this);
|
|
1421
|
+
assertCurrent();
|
|
1422
|
+
return result;
|
|
1422
1423
|
}
|
|
1423
1424
|
catch (error) {
|
|
1425
|
+
// A superseded attempt must not report authentication or clear its winner.
|
|
1426
|
+
assertCurrent();
|
|
1424
1427
|
if (!authenticated && error instanceof storage_core_1.VaultAuthenticationError) {
|
|
1425
1428
|
return { canLogin: false };
|
|
1426
1429
|
}
|
|
@@ -1432,6 +1435,12 @@ class KeyringCore {
|
|
|
1432
1435
|
throw error;
|
|
1433
1436
|
}
|
|
1434
1437
|
}
|
|
1438
|
+
assertUtxoDerivationAvailable(account, field) {
|
|
1439
|
+
if (account.isImported && account.xpub === account.address)
|
|
1440
|
+
throw new Error(`${field} not available for single-address imported accounts`);
|
|
1441
|
+
if (this.isWatchOnlyAccount(account))
|
|
1442
|
+
throw new Error(`${field} not available for watch-only accounts`);
|
|
1443
|
+
}
|
|
1435
1444
|
isWatchOnlyAccount(a) {
|
|
1436
1445
|
if (a.isLedgerWallet || a.isTrezorWallet)
|
|
1437
1446
|
return false;
|
|
@@ -1542,6 +1551,17 @@ class KeyringCore {
|
|
|
1542
1551
|
throw error;
|
|
1543
1552
|
}
|
|
1544
1553
|
}
|
|
1554
|
+
loadBip84AddressAccount(xpub, network) {
|
|
1555
|
+
const { types, networks } = (0, sysweb3_network_1.getNetworkConfig)(network.slip44, network.currency);
|
|
1556
|
+
if (!types?.zPubType)
|
|
1557
|
+
throw new Error('Missing zPubType in network configuration');
|
|
1558
|
+
return new BIP84.fromZPub(xpub, types.zPubType, networks);
|
|
1559
|
+
}
|
|
1560
|
+
deriveFirstUtxoAddress(xpub, network) {
|
|
1561
|
+
if (this.isDescriptor(xpub))
|
|
1562
|
+
return (0, watch_descriptor_1.deriveDescriptorAddress)(xpub, network);
|
|
1563
|
+
return this.loadBip84AddressAccount(xpub, network).getAddress(0, false, 84);
|
|
1564
|
+
}
|
|
1545
1565
|
async importAccount(privKey, label, options) {
|
|
1546
1566
|
// Check if wallet is unlocked
|
|
1547
1567
|
if (!this.isUnlocked()) {
|
|
@@ -1579,9 +1599,7 @@ class KeyringCore {
|
|
|
1579
1599
|
? types.zPubType.testnet.vpub
|
|
1580
1600
|
: types.zPubType.mainnet.zpub;
|
|
1581
1601
|
const bip84Key = (0, derivation_paths_1.convertExtendedKeyVersion)(identifier, target);
|
|
1582
|
-
addressToStore =
|
|
1583
|
-
forceIndex0: true,
|
|
1584
|
-
});
|
|
1602
|
+
addressToStore = this.deriveFirstUtxoAddress(bip84Key, activeNetwork);
|
|
1585
1603
|
}
|
|
1586
1604
|
// Validate duplicates across all account buckets, including passkey smart accounts.
|
|
1587
1605
|
if (this.accountAddressExists(accounts, addressToStore) ||
|
|
@@ -1882,7 +1900,7 @@ class KeyringCore {
|
|
|
1882
1900
|
if ((0, ethers_v6_1.getAddress)(key.address) !== request.account.expectedAddress)
|
|
1883
1901
|
throw new Error('Prepared EVM signer address mismatch');
|
|
1884
1902
|
assertCurrent();
|
|
1885
|
-
const signed = (0, prepared_evm_1.signPreparedEvmEnvelope)(request.transaction, key.decryptedPrivateKey);
|
|
1903
|
+
const { signed } = (0, prepared_evm_1.signPreparedEvmEnvelope)(request.transaction, key.decryptedPrivateKey);
|
|
1886
1904
|
hostGuard();
|
|
1887
1905
|
assertCurrent();
|
|
1888
1906
|
return signed;
|
|
@@ -1986,7 +2004,7 @@ class KeyringCore {
|
|
|
1986
2004
|
else {
|
|
1987
2005
|
// For UTXO, use the xpub to derive the address
|
|
1988
2006
|
// Always use first receive address (index 0) for imported account display
|
|
1989
|
-
address =
|
|
2007
|
+
address = this.deriveFirstUtxoAddress(xpub, vault.activeNetwork);
|
|
1990
2008
|
}
|
|
1991
2009
|
if (this.accountAddressExists(accounts, address, isEVM))
|
|
1992
2010
|
throw new Error('Account already exists on your Wallet.');
|
|
@@ -2055,7 +2073,7 @@ class KeyringCore {
|
|
|
2055
2073
|
: types.zPubType.mainnet.zpub;
|
|
2056
2074
|
xpub = (0, derivation_paths_1.convertExtendedKeyVersion)(ledgerXpub, bip84Target);
|
|
2057
2075
|
// Always use first receive address (index 0) for imported account display
|
|
2058
|
-
address =
|
|
2076
|
+
address = this.deriveFirstUtxoAddress(xpub, vault.activeNetwork);
|
|
2059
2077
|
}
|
|
2060
2078
|
if (this.accountAddressExists(accounts, address, (0, derivation_paths_1.isEvmCoin)(coin, slip44)))
|
|
2061
2079
|
throw new Error('Account already exists on your Wallet.');
|
|
@@ -2169,28 +2187,6 @@ class KeyringCore {
|
|
|
2169
2187
|
// No gaps found, return next sequential ID
|
|
2170
2188
|
return existingIds.length;
|
|
2171
2189
|
}
|
|
2172
|
-
async recreateSessionFromVault(saltedHashPassword) {
|
|
2173
|
-
try {
|
|
2174
|
-
// v4 vault is encrypted with the derived session password key (PBKDF2 output)
|
|
2175
|
-
// `saltedHashPassword` is the derived session password key passed by caller.
|
|
2176
|
-
const { mnemonic } = await this.vaultPersistence.getDecryptedVault(saltedHashPassword);
|
|
2177
|
-
if (!mnemonic) {
|
|
2178
|
-
throw new Error('Mnemonic not found in vault');
|
|
2179
|
-
}
|
|
2180
|
-
// Encrypt session data with sessionPassword hash for consistency
|
|
2181
|
-
// This allows keyring manager to decrypt with this.sessionPassword
|
|
2182
|
-
this.privateRecordSessionRevision++;
|
|
2183
|
-
this.sessionPassword = new SecureBuffer(saltedHashPassword);
|
|
2184
|
-
// Encrypt the mnemonic with session password for consistency with the rest of the code
|
|
2185
|
-
const encryptedMnemonic = crypto_js_1.default.AES.encrypt(mnemonic, saltedHashPassword).toString();
|
|
2186
|
-
this.sessionMnemonic = new SecureBuffer(encryptedMnemonic);
|
|
2187
|
-
console.log('[KeyringManager] Session data recreated from vault');
|
|
2188
|
-
}
|
|
2189
|
-
catch (error) {
|
|
2190
|
-
console.error('ERROR recreateSessionFromVault', { error });
|
|
2191
|
-
throw error;
|
|
2192
|
-
}
|
|
2193
|
-
}
|
|
2194
2190
|
async _getPrivateKeyAccountInfos(privKey, label, options) {
|
|
2195
2191
|
const vault = this.getVault();
|
|
2196
2192
|
const { accounts } = vault;
|
|
@@ -2595,6 +2591,8 @@ class KeyringCore {
|
|
|
2595
2591
|
* Clean up all resources
|
|
2596
2592
|
*/
|
|
2597
2593
|
async destroy() {
|
|
2594
|
+
__classPrivateFieldSet(this, _KeyringCore_deviceUnlockRetired, true, "f");
|
|
2595
|
+
__classPrivateFieldGet(this, _KeyringCore_sessionOwnership, "f").destroyed = true;
|
|
2598
2596
|
await this.lockWallet();
|
|
2599
2597
|
// Clear any remaining references
|
|
2600
2598
|
this.ethereumTransaction = {};
|
|
@@ -2602,4 +2600,222 @@ class KeyringCore {
|
|
|
2602
2600
|
}
|
|
2603
2601
|
}
|
|
2604
2602
|
exports.KeyringCore = KeyringCore;
|
|
2603
|
+
_KeyringCore_deviceUnlockRetired = new WeakMap(), _KeyringCore_sessionOwnership = new WeakMap(), _KeyringCore_instances = new WeakSet(), _KeyringCore_createDeviceUnlockControls = function _KeyringCore_createDeviceUnlockControls(port, readVaultPair) {
|
|
2604
|
+
const readPair = () => readCurrentVaultPair(readVaultPair);
|
|
2605
|
+
let pending = false;
|
|
2606
|
+
const run = async (request, assertCurrent) => {
|
|
2607
|
+
if (pending)
|
|
2608
|
+
throw new Error('Device unlock operation is busy');
|
|
2609
|
+
pending = true;
|
|
2610
|
+
const assertOwnership = __classPrivateFieldGet(this, _KeyringCore_instances, "m", _KeyringCore_captureSessionGuard).call(this, 'Device unlock session changed');
|
|
2611
|
+
const issuing = request !== null;
|
|
2612
|
+
const check = () => {
|
|
2613
|
+
const result = assertCurrent();
|
|
2614
|
+
if (result !== undefined) {
|
|
2615
|
+
// Reject async guards and consume a possible rejected promise.
|
|
2616
|
+
if (result &&
|
|
2617
|
+
(typeof result === 'object' || typeof result === 'function'))
|
|
2618
|
+
void Promise.resolve(result).catch(() => undefined);
|
|
2619
|
+
throw new TypeError('Device unlock guard must return undefined synchronously');
|
|
2620
|
+
}
|
|
2621
|
+
if (__classPrivateFieldGet(this, _KeyringCore_deviceUnlockRetired, "f") ||
|
|
2622
|
+
(!issuing && (this.sessionPassword || this.sessionMnemonic))) {
|
|
2623
|
+
throw new Error('Device unlock session changed');
|
|
2624
|
+
}
|
|
2625
|
+
assertOwnership();
|
|
2626
|
+
};
|
|
2627
|
+
let key;
|
|
2628
|
+
try {
|
|
2629
|
+
check();
|
|
2630
|
+
const pair = await readPair();
|
|
2631
|
+
check();
|
|
2632
|
+
if (!pair)
|
|
2633
|
+
return false;
|
|
2634
|
+
const checkPair = async (requireLiveOwner = true) => {
|
|
2635
|
+
const current = await readPair();
|
|
2636
|
+
if (requireLiveOwner)
|
|
2637
|
+
check();
|
|
2638
|
+
if (current?.pairFingerprint !== pair.pairFingerprint ||
|
|
2639
|
+
current.vault !== pair.vault ||
|
|
2640
|
+
current.salt !== pair.salt) {
|
|
2641
|
+
throw new Error('Device unlock vault changed');
|
|
2642
|
+
}
|
|
2643
|
+
};
|
|
2644
|
+
if (request) {
|
|
2645
|
+
key = this.hexToBytes(await this.deriveStoredSessionKey(request.password, {
|
|
2646
|
+
salt: pair.salt,
|
|
2647
|
+
}));
|
|
2648
|
+
}
|
|
2649
|
+
else {
|
|
2650
|
+
const credential = await port.readCredential(pair.pairFingerprint);
|
|
2651
|
+
key = credential?.key;
|
|
2652
|
+
check();
|
|
2653
|
+
if (credential?.pairFingerprint !== pair.pairFingerprint)
|
|
2654
|
+
return false;
|
|
2655
|
+
}
|
|
2656
|
+
check();
|
|
2657
|
+
if (!(key instanceof Uint8Array) || key.length !== 32)
|
|
2658
|
+
return false;
|
|
2659
|
+
const keyHex = Buffer.from(key.buffer, key.byteOffset, key.byteLength).toString('hex');
|
|
2660
|
+
let payload;
|
|
2661
|
+
try {
|
|
2662
|
+
payload = await (0, storage_core_1.decryptCurrentVault)(pair.vault, keyHex);
|
|
2663
|
+
}
|
|
2664
|
+
catch (error) {
|
|
2665
|
+
if (!(error instanceof storage_core_1.VaultAuthenticationError))
|
|
2666
|
+
throw error;
|
|
2667
|
+
// Preserve a definitive wrong-password result for host accounting
|
|
2668
|
+
// even if enrollment was cancelled. The pair must still match and
|
|
2669
|
+
// this branch never installs a session or writes a credential.
|
|
2670
|
+
await checkPair(!issuing);
|
|
2671
|
+
if (!issuing)
|
|
2672
|
+
check();
|
|
2673
|
+
return false;
|
|
2674
|
+
}
|
|
2675
|
+
check();
|
|
2676
|
+
const mnemonic = payload && typeof payload === 'object'
|
|
2677
|
+
? Object.getOwnPropertyDescriptor(payload, 'mnemonic')?.value
|
|
2678
|
+
: undefined;
|
|
2679
|
+
if (typeof mnemonic !== 'string' || !this.isSeedValid(mnemonic)) {
|
|
2680
|
+
throw new Error('Invalid device unlock vault payload');
|
|
2681
|
+
}
|
|
2682
|
+
await checkPair();
|
|
2683
|
+
check();
|
|
2684
|
+
if (request) {
|
|
2685
|
+
const result = await port.writeCredential(Object.freeze({ pairFingerprint: pair.pairFingerprint, key }), check);
|
|
2686
|
+
if (result !== undefined)
|
|
2687
|
+
throw new Error('Device unlock credential write was not acknowledged');
|
|
2688
|
+
await checkPair();
|
|
2689
|
+
check();
|
|
2690
|
+
}
|
|
2691
|
+
else {
|
|
2692
|
+
if (__classPrivateFieldGet(this, _KeyringCore_instances, "m", _KeyringCore_finishUnlock).call(this).needsAccountCreation)
|
|
2693
|
+
return false;
|
|
2694
|
+
__classPrivateFieldGet(this, _KeyringCore_instances, "m", _KeyringCore_installVaultSession).call(this, mnemonic, keyHex, check);
|
|
2695
|
+
}
|
|
2696
|
+
return true;
|
|
2697
|
+
}
|
|
2698
|
+
finally {
|
|
2699
|
+
if (key instanceof Uint8Array)
|
|
2700
|
+
key.fill(0);
|
|
2701
|
+
pending = false;
|
|
2702
|
+
}
|
|
2703
|
+
};
|
|
2704
|
+
return Object.freeze({
|
|
2705
|
+
issue: (password, assertCurrent) => {
|
|
2706
|
+
if (typeof password !== 'string' || !password)
|
|
2707
|
+
return Promise.reject(new TypeError('Wallet password is required'));
|
|
2708
|
+
return run({ password }, assertCurrent);
|
|
2709
|
+
},
|
|
2710
|
+
resume: (assertCurrent) => run(null, assertCurrent),
|
|
2711
|
+
});
|
|
2712
|
+
}, _KeyringCore_acceptSessionFrom = function _KeyringCore_acceptSessionFrom(source) {
|
|
2713
|
+
if (source.authority !== this.authority ||
|
|
2714
|
+
source.storage !== this.storage ||
|
|
2715
|
+
source.vaultPersistence !== this.vaultPersistence ||
|
|
2716
|
+
__classPrivateFieldGet(source, _KeyringCore_sessionOwnership, "f").tracker !== __classPrivateFieldGet(this, _KeyringCore_sessionOwnership, "f").tracker) {
|
|
2717
|
+
throw new Error('Cannot transfer session between different wallet authorities');
|
|
2718
|
+
}
|
|
2719
|
+
if (!source.sessionPassword || !source.sessionMnemonic) {
|
|
2720
|
+
throw new Error('Session data is missing during transfer');
|
|
2721
|
+
}
|
|
2722
|
+
__classPrivateFieldGet(this, _KeyringCore_instances, "m", _KeyringCore_replaceSessionBuffers).call(this, source.sessionPassword, source.sessionMnemonic, __classPrivateFieldGet(source, _KeyringCore_sessionOwnership, "f").token);
|
|
2723
|
+
}, _KeyringCore_assertSessionOwner = function _KeyringCore_assertSessionOwner(sourceToken) {
|
|
2724
|
+
const ownership = __classPrivateFieldGet(this, _KeyringCore_sessionOwnership, "f");
|
|
2725
|
+
if (ownership.destroyed)
|
|
2726
|
+
throw new Error('Wallet keyring was destroyed');
|
|
2727
|
+
const owner = ownership.tracker.current;
|
|
2728
|
+
if (sourceToken === undefined
|
|
2729
|
+
? owner !== undefined && owner !== ownership.token
|
|
2730
|
+
: owner !== sourceToken) {
|
|
2731
|
+
throw new Error('Wallet session is owned by another keyring');
|
|
2732
|
+
}
|
|
2733
|
+
}, _KeyringCore_captureSessionGuard = function _KeyringCore_captureSessionGuard(message = 'Wallet unlock session changed') {
|
|
2734
|
+
const ownership = __classPrivateFieldGet(this, _KeyringCore_sessionOwnership, "f");
|
|
2735
|
+
const revision = this.privateRecordSessionRevision;
|
|
2736
|
+
const epoch = ownership.tracker.epoch;
|
|
2737
|
+
return () => {
|
|
2738
|
+
if (this.privateRecordSessionRevision !== revision)
|
|
2739
|
+
throw new Error(message);
|
|
2740
|
+
__classPrivateFieldGet(this, _KeyringCore_instances, "m", _KeyringCore_assertSessionOwner).call(this);
|
|
2741
|
+
if (ownership.tracker.epoch !== epoch)
|
|
2742
|
+
throw new Error(message);
|
|
2743
|
+
};
|
|
2744
|
+
}, _KeyringCore_replaceSessionBuffers = function _KeyringCore_replaceSessionBuffers(sessionPassword, sessionMnemonic, sourceToken) {
|
|
2745
|
+
__classPrivateFieldGet(this, _KeyringCore_instances, "m", _KeyringCore_assertSessionOwner).call(this, sourceToken);
|
|
2746
|
+
this.privateRecordSessionRevision++;
|
|
2747
|
+
// Clear any existing data first
|
|
2748
|
+
if (this.sessionPassword) {
|
|
2749
|
+
this.sessionPassword.clear();
|
|
2750
|
+
}
|
|
2751
|
+
if (this.sessionMnemonic) {
|
|
2752
|
+
this.sessionMnemonic.clear();
|
|
2753
|
+
}
|
|
2754
|
+
// Take ownership of the actual SecureBuffer objects
|
|
2755
|
+
// No copying - these are the original objects
|
|
2756
|
+
this.sessionPassword = sessionPassword;
|
|
2757
|
+
this.sessionMnemonic = sessionMnemonic;
|
|
2758
|
+
const ownership = __classPrivateFieldGet(this, _KeyringCore_sessionOwnership, "f");
|
|
2759
|
+
ownership.tracker.current = ownership.token;
|
|
2760
|
+
ownership.tracker.epoch++;
|
|
2761
|
+
}, _KeyringCore_finishUnlock = function _KeyringCore_finishUnlock() {
|
|
2762
|
+
// Re-initialize syscoin transaction handler if it was cleared on lock.
|
|
2763
|
+
// `lockWallet()` intentionally replaces it with a plain object to drop signer references,
|
|
2764
|
+
// so we must restore the real class instance on unlock.
|
|
2765
|
+
if (!this.syscoinTransaction ||
|
|
2766
|
+
typeof this.syscoinTransaction.getRecommendedFee !== 'function') {
|
|
2767
|
+
this.syscoinTransaction = this.createSyscoinTransactions();
|
|
2768
|
+
}
|
|
2769
|
+
// NOTE: Active account management is now handled by vault state/Redux
|
|
2770
|
+
// No need to explicitly set active account after unlock - it's managed externally
|
|
2771
|
+
const vault = this.getVault();
|
|
2772
|
+
if (vault.activeAccount?.id !== undefined && vault.activeAccount?.type) {
|
|
2773
|
+
// Check if the active account actually exists in the accounts map
|
|
2774
|
+
const accountType = vault.activeAccount.type;
|
|
2775
|
+
const accountId = vault.activeAccount.id;
|
|
2776
|
+
const accountExists = vault.accounts?.[accountType]?.[accountId];
|
|
2777
|
+
if (!accountExists) {
|
|
2778
|
+
console.log(`[KeyringManager] Active account ${accountType}:${accountId} not found in accounts map. This may indicate a migration from old vault format.`);
|
|
2779
|
+
// Signal that accounts need to be created after migration
|
|
2780
|
+
return {
|
|
2781
|
+
canLogin: true,
|
|
2782
|
+
needsAccountCreation: true,
|
|
2783
|
+
};
|
|
2784
|
+
}
|
|
2785
|
+
console.log(`[KeyringManager] Active account ${vault.activeAccount.id} available after unlock`);
|
|
2786
|
+
}
|
|
2787
|
+
return {
|
|
2788
|
+
canLogin: true,
|
|
2789
|
+
};
|
|
2790
|
+
}, _KeyringCore_installVaultSession = function _KeyringCore_installVaultSession(mnemonic, key, assertCurrent) {
|
|
2791
|
+
const ciphertext = crypto_js_1.default.AES.encrypt(mnemonic, key).toString();
|
|
2792
|
+
const password = new SecureBuffer(key);
|
|
2793
|
+
const encryptedMnemonic = new SecureBuffer(ciphertext);
|
|
2794
|
+
try {
|
|
2795
|
+
assertCurrent?.();
|
|
2796
|
+
__classPrivateFieldGet(this, _KeyringCore_instances, "m", _KeyringCore_replaceSessionBuffers).call(this, password, encryptedMnemonic);
|
|
2797
|
+
}
|
|
2798
|
+
catch (error) {
|
|
2799
|
+
password.clear();
|
|
2800
|
+
encryptedMnemonic.clear();
|
|
2801
|
+
throw error;
|
|
2802
|
+
}
|
|
2803
|
+
}, _KeyringCore_recreateSessionFromVault = async function _KeyringCore_recreateSessionFromVault(saltedHashPassword, assertCurrent) {
|
|
2804
|
+
try {
|
|
2805
|
+
// v4 vault is encrypted with the derived session password key (PBKDF2 output)
|
|
2806
|
+
// `saltedHashPassword` is the derived session password key passed by caller.
|
|
2807
|
+
const { mnemonic } = await this.vaultPersistence.getDecryptedVault(saltedHashPassword);
|
|
2808
|
+
assertCurrent();
|
|
2809
|
+
if (!mnemonic) {
|
|
2810
|
+
throw new Error('Mnemonic not found in vault');
|
|
2811
|
+
}
|
|
2812
|
+
__classPrivateFieldGet(this, _KeyringCore_instances, "m", _KeyringCore_installVaultSession).call(this, mnemonic, saltedHashPassword, assertCurrent);
|
|
2813
|
+
// Capture the installed revision before returning to the async caller.
|
|
2814
|
+
return __classPrivateFieldGet(this, _KeyringCore_instances, "m", _KeyringCore_captureSessionGuard).call(this);
|
|
2815
|
+
}
|
|
2816
|
+
catch (error) {
|
|
2817
|
+
console.error('ERROR recreateSessionFromVault', { error });
|
|
2818
|
+
throw error;
|
|
2819
|
+
}
|
|
2820
|
+
};
|
|
2605
2821
|
//# sourceMappingURL=keyring-core.js.map
|