Skip to content

Commit a33e3eb

Browse files
authored
feat(ironfish): Add decrypt method to wallet (#5266)
1 parent 3a452da commit a33e3eb

File tree

6 files changed

+396
-1
lines changed

6 files changed

+396
-1
lines changed

ironfish/src/wallet/__fixtures__/wallet.test.ts.fixture

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6958,5 +6958,125 @@
69586958
"sequence": 1
69596959
}
69606960
}
6961+
],
6962+
"Wallet decrypt saves decrypted accounts to disk and updates the wallet account fields": [
6963+
{
6964+
"value": {
6965+
"encrypted": false,
6966+
"version": 4,
6967+
"id": "333e7293-8495-415d-a23c-c5c42299a28c",
6968+
"name": "A",
6969+
"spendingKey": "ab109f4b6f709d52325e3fdc9eb91ad5c5aea8c32241c0d51c59b3079534e5eb",
6970+
"viewKey": "daa445d831d064fd6663448c17e097b4e698e41775e982bd15a6de060bf2cb1ce89a5cd36099ea86618ad38a8a53648ecccdd190d5fb04432c711c2628ce7a23",
6971+
"incomingViewKey": "99495a5e0c7615935cfb8564d87323e5cf50e159892afe966fed576ca9f08e04",
6972+
"outgoingViewKey": "4211e27a7965b806e822ebe99a5a1980bb7aebd5cc6b011dc94e365531ef8674",
6973+
"publicAddress": "e88dc4d5956ff1754dac3f4b9e1acb6a6c31786af3e3cc1e7e9aaf6b6901f3a2",
6974+
"createdAt": {
6975+
"hash": {
6976+
"type": "Buffer",
6977+
"data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY="
6978+
},
6979+
"sequence": 1
6980+
},
6981+
"scanningEnabled": true,
6982+
"proofAuthorizingKey": "60d383fbce19b29d1dc49be91ad51729295fa23c321503828fca0c9987f10f0d"
6983+
},
6984+
"head": {
6985+
"hash": {
6986+
"type": "Buffer",
6987+
"data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY="
6988+
},
6989+
"sequence": 1
6990+
}
6991+
},
6992+
{
6993+
"value": {
6994+
"encrypted": false,
6995+
"version": 4,
6996+
"id": "414ef1bb-88c3-42e8-9cce-73258f6395d6",
6997+
"name": "B",
6998+
"spendingKey": "44617ebb8ee9299500684dd8aea996764609fca2c2bb8b2bf4da1194535fc3e1",
6999+
"viewKey": "9e5035501da8cf3f174533deb8592c1f65930442b09bdf1a610e1441b79fa7ee063662e65ee08477ea938146dd94145ae6a21f5c68b683f9d487ae28f0dbd336",
7000+
"incomingViewKey": "44f517e6d7133af416ff1f733ba99bdf31d6fd6e3dd82e567e82bff872c57803",
7001+
"outgoingViewKey": "8b7cecb7efc2448273d25eacf2124354dc7ad39ac0a81dade10ceecfd7fd5187",
7002+
"publicAddress": "c5a35f571c155bf9c0481c1e82c6a3d0769b0eb5628f31abd9e60f1e9f4c9509",
7003+
"createdAt": {
7004+
"hash": {
7005+
"type": "Buffer",
7006+
"data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY="
7007+
},
7008+
"sequence": 1
7009+
},
7010+
"scanningEnabled": true,
7011+
"proofAuthorizingKey": "ca6de858a4cbb527e9f8d33b64a946ea56840f57baffd5534128f3e325c1c20d"
7012+
},
7013+
"head": {
7014+
"hash": {
7015+
"type": "Buffer",
7016+
"data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY="
7017+
},
7018+
"sequence": 1
7019+
}
7020+
}
7021+
],
7022+
"Wallet decrypt fails with an invalid passphrase": [
7023+
{
7024+
"value": {
7025+
"encrypted": false,
7026+
"version": 4,
7027+
"id": "f55cd33d-3bae-405d-bde9-ff86d80bbf86",
7028+
"name": "A",
7029+
"spendingKey": "e33273c1a8d8c99dc30a87be0be68fc5420e5db5c2143734c03be1bb3d4bcdf7",
7030+
"viewKey": "1a59c5e8c21bf4024f1d175896602d4777a3616ded5d05a3b7316a23290482bd96870ad88d4d85603198f768aeb0ece799bcf090d1c8250c0aa6a338c31f3ad5",
7031+
"incomingViewKey": "0ab4f8a7800a3b2a25bf5e1171b9b14b25318ad31998ce01fe45c7bf815b6603",
7032+
"outgoingViewKey": "579103a34bc8c7d9f1f876bf505ab8bbc75709e84a845de307248d63edd4fe56",
7033+
"publicAddress": "aa31dc223dee0b7190c94f057c9ae1ebfca0604e39696c70f32b70f009142527",
7034+
"createdAt": {
7035+
"hash": {
7036+
"type": "Buffer",
7037+
"data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY="
7038+
},
7039+
"sequence": 1
7040+
},
7041+
"scanningEnabled": true,
7042+
"proofAuthorizingKey": "c796c91e34e0005a2be596751f07f5bb1438591a3ddc538f2ec83dbdf6318005"
7043+
},
7044+
"head": {
7045+
"hash": {
7046+
"type": "Buffer",
7047+
"data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY="
7048+
},
7049+
"sequence": 1
7050+
}
7051+
},
7052+
{
7053+
"value": {
7054+
"encrypted": false,
7055+
"version": 4,
7056+
"id": "21900bff-d690-4b0f-939b-ba3f80b25495",
7057+
"name": "B",
7058+
"spendingKey": "095ef9fd35272a49371ebd0d65e1d3cac64c1e941f4e878ef9d7deaf41530853",
7059+
"viewKey": "fd924ce7abfbc4d67badd5e06d2eb91814090d1f6322da0bef55f1bd76d137b963399defe0138174624cd5f5ea368016668cf145bd039c9b2292598f86c07219",
7060+
"incomingViewKey": "c348f1f2e555cf44b0d9146e0ce1bb2ac9f90fce724a0813b67846d6d187f005",
7061+
"outgoingViewKey": "8e85ca189a708b86328cf9353bc40df764c391c2f1b98ad91470c00681b3c8c9",
7062+
"publicAddress": "105b2517120c59c26e4562662ae02a18a571f8689e55762a60a74fd775814ae7",
7063+
"createdAt": {
7064+
"hash": {
7065+
"type": "Buffer",
7066+
"data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY="
7067+
},
7068+
"sequence": 1
7069+
},
7070+
"scanningEnabled": true,
7071+
"proofAuthorizingKey": "17d26f648e1b94cd89ee8deb24c698632632c6806b21b92212f20de1a3f72d01"
7072+
},
7073+
"head": {
7074+
"hash": {
7075+
"type": "Buffer",
7076+
"data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY="
7077+
},
7078+
"sequence": 1
7079+
}
7080+
}
69617081
]
69627082
}

ironfish/src/wallet/wallet.test.ts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import {
2323
import { AsyncUtils, BufferUtils, ORE_TO_IRON } from '../utils'
2424
import { Account, TransactionStatus, TransactionType } from '../wallet'
2525
import {
26+
AccountDecryptionFailedError,
2627
DuplicateAccountNameError,
2728
DuplicateSpendingKeyError,
2829
MaxMemoLengthError,
@@ -2402,4 +2403,50 @@ describe('Wallet', () => {
24022403
expect(accountB.serialize()).toMatchObject(decryptedAccountB.serialize())
24032404
})
24042405
})
2406+
2407+
describe('decrypt', () => {
2408+
it('saves decrypted accounts to disk and updates the wallet account fields', async () => {
2409+
const { node } = nodeTest
2410+
const passphrase = 'foo'
2411+
2412+
const accountA = await useAccountFixture(node.wallet, 'A')
2413+
const accountB = await useAccountFixture(node.wallet, 'B')
2414+
2415+
await node.wallet.encrypt(passphrase)
2416+
expect(node.wallet.accounts).toHaveLength(0)
2417+
expect(node.wallet.encryptedAccounts).toHaveLength(2)
2418+
2419+
await node.wallet.decrypt(passphrase)
2420+
expect(node.wallet.accounts).toHaveLength(2)
2421+
expect(node.wallet.encryptedAccounts).toHaveLength(0)
2422+
2423+
const decryptedAccountA = node.wallet.accountById.get(accountA.id)
2424+
Assert.isNotUndefined(decryptedAccountA)
2425+
expect(accountA.serialize()).toMatchObject(decryptedAccountA.serialize())
2426+
2427+
const decryptedAccountB = node.wallet.accountById.get(accountB.id)
2428+
Assert.isNotUndefined(decryptedAccountB)
2429+
expect(accountB.serialize()).toMatchObject(decryptedAccountB.serialize())
2430+
})
2431+
2432+
it('fails with an invalid passphrase', async () => {
2433+
const { node } = nodeTest
2434+
const passphrase = 'foo'
2435+
const invalidPassphrase = 'bar'
2436+
2437+
await useAccountFixture(node.wallet, 'A')
2438+
await useAccountFixture(node.wallet, 'B')
2439+
2440+
await node.wallet.encrypt(passphrase)
2441+
expect(node.wallet.accounts).toHaveLength(0)
2442+
expect(node.wallet.encryptedAccounts).toHaveLength(2)
2443+
2444+
await expect(node.wallet.decrypt(invalidPassphrase)).rejects.toThrow(
2445+
AccountDecryptionFailedError,
2446+
)
2447+
2448+
expect(node.wallet.accounts).toHaveLength(0)
2449+
expect(node.wallet.encryptedAccounts).toHaveLength(2)
2450+
})
2451+
})
24052452
})

ironfish/src/wallet/wallet.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ export class Wallet {
9494
readonly onAccountImported = new Event<[account: Account]>()
9595
readonly onAccountRemoved = new Event<[account: Account]>()
9696

97-
protected readonly accountById = new Map<string, Account>()
97+
readonly accountById = new Map<string, Account>()
9898
readonly encryptedAccountById = new Map<string, EncryptedAccount>()
9999
readonly walletDb: WalletDB
100100
private readonly logger: Logger
@@ -1785,4 +1785,15 @@ export class Wallet {
17851785
unlock()
17861786
}
17871787
}
1788+
1789+
async decrypt(passphrase: string, tx?: IDatabaseTransaction): Promise<void> {
1790+
const unlock = await this.createTransactionMutex.lock()
1791+
1792+
try {
1793+
await this.walletDb.decryptAccounts(this.encryptedAccounts, passphrase, tx)
1794+
await this.load()
1795+
} finally {
1796+
unlock()
1797+
}
1798+
}
17881799
}

ironfish/src/wallet/walletdb/__fixtures__/walletdb.test.ts.fixture

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -850,5 +850,125 @@
850850
"sequence": 1
851851
}
852852
}
853+
],
854+
"WalletDB decryptAccounts stores decrypted accounts": [
855+
{
856+
"value": {
857+
"encrypted": false,
858+
"version": 4,
859+
"id": "99b2f779-24b3-4280-9373-60e754b91c65",
860+
"name": "A",
861+
"spendingKey": "147de6b6b6054328b5409f59e38de933bbdae76d3b6b0bf42dd89b90906a19aa",
862+
"viewKey": "37f35e558e47decd960a65e30ff310c8dc1e726a321dca21532d412c929b4e43a9a645aed8934724bde1fd8f6d1814341efb0e2f7e2edaf821ff7e0a08eee8aa",
863+
"incomingViewKey": "48f5ae1959a4fd4117825fbf6791700c975bc93cc4423c47a77ea96362d1e904",
864+
"outgoingViewKey": "dec94d98c3ec4a4991b7344abd6ccc24bd8857b7605dee2a97fb76d594d9a629",
865+
"publicAddress": "fd02b46d7addd5c6f58ff6d04e560c9be3de64c5361601bb850a94ce8585f5c0",
866+
"createdAt": {
867+
"hash": {
868+
"type": "Buffer",
869+
"data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY="
870+
},
871+
"sequence": 1
872+
},
873+
"scanningEnabled": true,
874+
"proofAuthorizingKey": "9f90335923ded9f0bf8fff2c9a494f5e13c9ff781db239d009e2927f1ee80908"
875+
},
876+
"head": {
877+
"hash": {
878+
"type": "Buffer",
879+
"data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY="
880+
},
881+
"sequence": 1
882+
}
883+
},
884+
{
885+
"value": {
886+
"encrypted": false,
887+
"version": 4,
888+
"id": "16a2badd-c7a6-4bf3-ab5b-3fe79901df20",
889+
"name": "B",
890+
"spendingKey": "5ba5b547ce1009727b66d6e8b1d25815f88611fe04f7b30ae5b447b52b8e7910",
891+
"viewKey": "b7646fefef3547c784a145487683400f238b2d2a97fc22fd4d45a9df1fa3e7a9c4b72f8757cc5aabb5ff8c7ea947833a6ec2368ad0cd5014bd33806c272ea993",
892+
"incomingViewKey": "6e051e304f17ebd4a35d0b35ca9eb39a77009d0d0a92a423be4be3cf341f3d00",
893+
"outgoingViewKey": "07cbedcdb1226d283378f2d48d173384fbd3c39abf10ce796b9bed8543b85407",
894+
"publicAddress": "71fb617acf602a2da55fd4857978a29416903bbd240ab52886097d569b85d41b",
895+
"createdAt": {
896+
"hash": {
897+
"type": "Buffer",
898+
"data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY="
899+
},
900+
"sequence": 1
901+
},
902+
"scanningEnabled": true,
903+
"proofAuthorizingKey": "e425c3f41ed47adb266a210760489cf8c5dc1981fa306ae55a3e3e8283aa0907"
904+
},
905+
"head": {
906+
"hash": {
907+
"type": "Buffer",
908+
"data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY="
909+
},
910+
"sequence": 1
911+
}
912+
}
913+
],
914+
"WalletDB decryptAccounts throws an error with an invalid passphrase": [
915+
{
916+
"value": {
917+
"encrypted": false,
918+
"version": 4,
919+
"id": "5c0154e9-e7ba-4516-bf89-c94a6a8ee396",
920+
"name": "A",
921+
"spendingKey": "7a64a08fd618327398f612f00d9c53939630e19c0176826585704ed0c4e5cb87",
922+
"viewKey": "a75051978830c2361fd2fe1275a06c5ad8ab266d9da1d608d1ccb639e92f3068f1e99b7037b83498b1250265eb2d695724e646d51f732ffeac5b36a77f982a3f",
923+
"incomingViewKey": "5284789b53acfa30f00e81d28fc1308e7e99e2ef854d514fe6d763f81bb9b007",
924+
"outgoingViewKey": "21bc65190cc9ad6e2c7a32d101246e2caef87a8e323d56868196e8799564d24f",
925+
"publicAddress": "23df85a64f5a2fe410cac9be7b3426e96ce014657d82d9aefb9a92bb063ee08a",
926+
"createdAt": {
927+
"hash": {
928+
"type": "Buffer",
929+
"data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY="
930+
},
931+
"sequence": 1
932+
},
933+
"scanningEnabled": true,
934+
"proofAuthorizingKey": "74fb92bf84c0c871fb9b20b8ec3b7528fc88382a84e72d0506d29cb6ac484b08"
935+
},
936+
"head": {
937+
"hash": {
938+
"type": "Buffer",
939+
"data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY="
940+
},
941+
"sequence": 1
942+
}
943+
},
944+
{
945+
"value": {
946+
"encrypted": false,
947+
"version": 4,
948+
"id": "595ea740-96ea-43e4-8d13-a4f864f08244",
949+
"name": "B",
950+
"spendingKey": "8bc528f23c04739f2083d2367d6b81ba9ca2cc41d2d920dd4c417197ac3bf289",
951+
"viewKey": "cc3c5859bc104fd104cdcfef85ffa9ff5984ea0d2b7aa4843afa0c3268166f05d51eef5f16b081b8dd252c72bf93a4446efa9f0cd6514610d9b18a215874850b",
952+
"incomingViewKey": "a899c9f5d96de698af6ac81831c5470f839baca8d8faf76fa2c8f60b831e3502",
953+
"outgoingViewKey": "d0a6fc11f89cfc40c72699cded70137ce8da9b2236cada30a5ce922627ae12fb",
954+
"publicAddress": "03eb191ec40f5153bae739c162f56a80abc2e819f08b7ff2ee160d64aec24373",
955+
"createdAt": {
956+
"hash": {
957+
"type": "Buffer",
958+
"data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY="
959+
},
960+
"sequence": 1
961+
},
962+
"scanningEnabled": true,
963+
"proofAuthorizingKey": "1a628719c1704b0553ffcf8daab62507c2e5829d878b07b6f1573b7023d4110a"
964+
},
965+
"head": {
966+
"hash": {
967+
"type": "Buffer",
968+
"data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY="
969+
},
970+
"sequence": 1
971+
}
972+
}
853973
]
854974
}

0 commit comments

Comments
 (0)