import { bytes, hex, utf8 } from '#src/lib/convert.js'\r
import { Entropy } from '#src/lib/entropy.js'\r
import { Rpc } from '#src/lib/rpc.js'\r
-import { Key, KeyPair, WalletType } from '#types'\r
+import { KeyPair, NamedData, WalletType } from '#types'\r
import { SafeWorker } from '#workers'\r
\r
/**\r
*\r
* @returns Array of hexadecimal-formatted wallet IDs\r
*/\r
- static async export (): Promise<string[]> {\r
+ static async export (): Promise<NamedData<string[]> | null> {\r
try {\r
const response = await SafeWorker.request<ArrayBuffer>({\r
method: 'export',\r
store: 'Wallet'\r
})\r
- return Object.keys(response)\r
+ const ids = Object.keys(response)\r
+ const data: NamedData<string[]> = {}\r
+ ids.map(i => {\r
+ const [type, id] = i.split('_')\r
+ if (data[type] == null) {\r
+ data[type] = [id]\r
+ } else {\r
+ data[type].push(id)\r
+ }\r
+ })\r
+ return data\r
} catch (err) {\r
console.error(err)\r
- return []\r
+ return null\r
}\r
}\r
\r
* Locks the wallet and all currently derived accounts with a password that\r
* will be needed to unlock it later.\r
*\r
- * @param {Key} password Used to lock the wallet\r
+ * @param {string} password Used to lock the wallet\r
* @returns True if successfully locked\r
*/\r
- async lock (password: Key): Promise<boolean> {\r
- if (typeof password === 'string') {\r
- password = utf8.toBytes(password)\r
- }\r
+ async lock (password: string): Promise<boolean> {\r
try {\r
- if (password == null || !(password instanceof Uint8Array)) {\r
- throw new Error('password must be string or bytes')\r
+ if (typeof password !== 'string') {\r
+ throw new TypeError('Invalid password')\r
}\r
const serialized = JSON.stringify({\r
id: this.id,\r
mnemonic: this.#m?.phrase,\r
seed: this.#s == null ? this.#s : bytes.toHex(this.#s)\r
})\r
- const encoded = utf8.toBytes(serialized)\r
const success = await SafeWorker.request({\r
method: 'store',\r
store: 'Wallet',\r
- [this.id]: encoded.buffer,\r
- password: password.buffer\r
+ [this.id]: utf8.toBuffer(serialized),\r
+ password: utf8.toBuffer(password)\r
})\r
if (!success) {\r
throw null\r
return this.#locked\r
} catch (err) {\r
throw new Error('failed to lock wallet', { cause: err })\r
- } finally {\r
- bytes.erase(password)\r
}\r
}\r
\r
/**\r
* Unlocks the wallet using the same password as used prior to lock it.\r
*\r
- * @param {Key} password Used previously to lock the wallet\r
+ * @param {string} password Used previously to lock the wallet\r
* @returns True if successfully unlocked\r
*/\r
- async unlock (password: Key): Promise<boolean> {\r
- if (typeof password === 'string') {\r
- password = utf8.toBytes(password)\r
- }\r
- let decoded, deserialized, id, mnemonic, seed\r
+ async unlock (password: string): Promise<boolean> {\r
+ let response: NamedData<ArrayBuffer>\r
try {\r
- if (password == null || !(password instanceof Uint8Array)) {\r
- throw new Error('password must be string or bytes')\r
+ if (typeof password !== 'string') {\r
+ throw new TypeError('Invalid password')\r
}\r
const response = await SafeWorker.request<ArrayBuffer>({\r
method: 'fetch',\r
names: this.id,\r
store: 'Wallet',\r
- password: password.buffer\r
+ password: utf8.toBuffer(password)\r
})\r
- decoded = bytes.toUtf8(new Uint8Array(response[this.id]))\r
- deserialized = JSON.parse(decoded)\r
- id = deserialized.id\r
- mnemonic = deserialized.mnemonic\r
- seed = deserialized.seed\r
+ const decoded = bytes.toUtf8(new Uint8Array(response[this.id]))\r
+ const deserialized = JSON.parse(decoded)\r
+ let { id, mnemonic, seed } = deserialized\r
if (id == null) {\r
throw new Error('ID is null')\r
}\r
return true\r
} catch (err) {\r
throw new Error('failed to unlock wallet', { cause: err })\r
- } finally {\r
- bytes.erase(password)\r
- decoded = deserialized = id = mnemonic = seed = undefined\r
}\r
}\r
\r
await assert.resolves(wallet.destroy())\r
})\r
\r
- await test('locking and unlocking a Bip44Wallet with random bytes', async () => {\r
- const wallet = await Bip44Wallet.fromMnemonic(NANO_TEST_VECTORS.PASSWORD, NANO_TEST_VECTORS.MNEMONIC, NANO_TEST_VECTORS.PASSWORD)\r
- await wallet.unlock(NANO_TEST_VECTORS.PASSWORD)\r
- const key = globalThis.crypto.getRandomValues(new Uint8Array(64))\r
-\r
- const lockResult = await wallet.lock(new Uint8Array(key))\r
- assert.ok(lockResult)\r
- assert.ok('mnemonic' in wallet)\r
- assert.ok('seed' in wallet)\r
- assert.throws(() => wallet.mnemonic)\r
- assert.throws(() => wallet.seed)\r
-\r
- const unlockResult = await wallet.unlock(new Uint8Array(key))\r
-\r
- assert.equal(unlockResult, true)\r
- assert.ok('mnemonic' in wallet)\r
- assert.ok('seed' in wallet)\r
- assert.equal(wallet.mnemonic, NANO_TEST_VECTORS.MNEMONIC)\r
- assert.equal(wallet.seed, NANO_TEST_VECTORS.BIP39_SEED)\r
-\r
- await assert.resolves(wallet.destroy())\r
- })\r
-\r
await test('locking and unlocking a Bip44Wallet Account with a password', async () => {\r
const wallet = await Bip44Wallet.fromMnemonic(NANO_TEST_VECTORS.PASSWORD, NANO_TEST_VECTORS.MNEMONIC, NANO_TEST_VECTORS.PASSWORD)\r
await wallet.unlock(NANO_TEST_VECTORS.PASSWORD)\r
await assert.resolves(wallet.destroy())\r
})\r
\r
- await test('fail to unlock a Bip44Wallet with different random bytes', async () => {\r
- const wallet = await Bip44Wallet.fromMnemonic(NANO_TEST_VECTORS.PASSWORD, NANO_TEST_VECTORS.MNEMONIC, NANO_TEST_VECTORS.PASSWORD)\r
- await wallet.unlock(NANO_TEST_VECTORS.PASSWORD)\r
- const rightKey = globalThis.crypto.getRandomValues(new Uint8Array(64))\r
- const wrongKey = globalThis.crypto.getRandomValues(new Uint8Array(64))\r
- const lockResult = await wallet.lock(new Uint8Array(rightKey))\r
-\r
- await assert.rejects(wallet.unlock(new Uint8Array(wrongKey)), { message: 'Failed to unlock wallet' })\r
- assert.equal(lockResult, true)\r
- assert.ok('mnemonic' in wallet)\r
- assert.ok('seed' in wallet)\r
- assert.throws(() => wallet.mnemonic)\r
- assert.throws(() => wallet.seed)\r
-\r
- await assert.resolves(wallet.destroy())\r
- })\r
-\r
await test('fail to unlock a Bip44Wallet with different valid inputs', async () => {\r
const wallet = await Bip44Wallet.fromMnemonic(NANO_TEST_VECTORS.PASSWORD, NANO_TEST_VECTORS.MNEMONIC, NANO_TEST_VECTORS.PASSWORD)\r
const key = globalThis.crypto.getRandomValues(new Uint8Array(64))\r
await assert.resolves(wallet.destroy())\r
})\r
\r
- await test('locking and unlocking a Blake2bWallet with random bytes', async () => {\r
- const wallet = await Blake2bWallet.fromSeed(NANO_TEST_VECTORS.PASSWORD, TREZOR_TEST_VECTORS.ENTROPY_1)\r
- await wallet.unlock(NANO_TEST_VECTORS.PASSWORD)\r
- const key = globalThis.crypto.getRandomValues(new Uint8Array(64))\r
- const lockResult = await wallet.lock(new Uint8Array(key))\r
-\r
- assert.equal(lockResult, true)\r
- assert.ok('mnemonic' in wallet)\r
- assert.ok('seed' in wallet)\r
- assert.throws(() => wallet.mnemonic)\r
- assert.throws(() => wallet.seed)\r
-\r
- const unlockResult = await wallet.unlock(new Uint8Array(key))\r
-\r
- assert.equal(lockResult, true)\r
- assert.equal(unlockResult, true)\r
- assert.ok('mnemonic' in wallet)\r
- assert.ok('seed' in wallet)\r
- assert.equal(wallet.mnemonic, TREZOR_TEST_VECTORS.MNEMONIC_1)\r
- assert.equal(wallet.seed, TREZOR_TEST_VECTORS.ENTROPY_1)\r
-\r
- await assert.resolves(wallet.destroy())\r
- })\r
-\r
await test('locking and unlocking a Blake2bWallet Account with a password', async () => {\r
const wallet = await Blake2bWallet.fromSeed(NANO_TEST_VECTORS.PASSWORD, TREZOR_TEST_VECTORS.ENTROPY_0)\r
await wallet.unlock(NANO_TEST_VECTORS.PASSWORD)\r
await assert.resolves(wallet.destroy())\r
})\r
\r
- await test('fail to unlock a Blake2bWallet with different keys', async () => {\r
- const wallet = await Blake2bWallet.fromSeed(NANO_TEST_VECTORS.PASSWORD, TREZOR_TEST_VECTORS.ENTROPY_1)\r
- await wallet.unlock(NANO_TEST_VECTORS.PASSWORD)\r
- const rightKey = globalThis.crypto.getRandomValues(new Uint8Array(64))\r
- const wrongKey = globalThis.crypto.getRandomValues(new Uint8Array(64))\r
- const lockResult = await wallet.lock(new Uint8Array(rightKey))\r
-\r
- await assert.rejects(wallet.unlock(new Uint8Array(wrongKey)), { message: 'Failed to unlock wallet' })\r
- assert.equal(lockResult, true)\r
- assert.ok('mnemonic' in wallet)\r
- assert.ok('seed' in wallet)\r
- assert.throws(() => wallet.mnemonic)\r
- assert.throws(() => wallet.seed)\r
-\r
- await assert.resolves(wallet.destroy())\r
- })\r
-\r
await test('fail to unlock a Blake2bWallet with different valid inputs', async () => {\r
const wallet = await Blake2bWallet.fromSeed(NANO_TEST_VECTORS.PASSWORD, TREZOR_TEST_VECTORS.ENTROPY_1)\r
const key = globalThis.crypto.getRandomValues(new Uint8Array(64))\r