Skip to content

Commit a547849

Browse files
authored
feat(ironfish): Add wallet/lock (#5322)
1 parent 9780475 commit a547849

File tree

6 files changed

+220
-9
lines changed

6 files changed

+220
-9
lines changed

ironfish/src/rpc/clients/client.ts

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,18 @@ import type {
3535
CreateTransactionResponse,
3636
CreateTrustedDealerKeyPackageRequest,
3737
CreateTrustedDealerKeyPackageResponse,
38+
DecryptWalletRequest,
39+
DecryptWalletResponse,
40+
DeleteTransactionRequest,
41+
DeleteTransactionResponse,
3842
DkgRound1Request,
3943
DkgRound1Response,
4044
DkgRound2Request,
4145
DkgRound2Response,
4246
DkgRound3Request,
4347
DkgRound3Response,
48+
EncryptWalletRequest,
49+
EncryptWalletResponse,
4450
EstimateFeeRateRequest,
4551
EstimateFeeRateResponse,
4652
EstimateFeeRatesRequest,
@@ -135,6 +141,8 @@ import type {
135141
ImportResponse,
136142
IsValidPublicAddressRequest,
137143
IsValidPublicAddressResponse,
144+
LockWalletRequest,
145+
LockWalletResponse,
138146
MintAssetRequest,
139147
MintAssetResponse,
140148
OnGossipRequest,
@@ -166,6 +174,8 @@ import type {
166174
StopNodeResponse,
167175
SubmitBlockRequest,
168176
SubmitBlockResponse,
177+
UnlockWalletRequest,
178+
UnlockWalletResponse,
169179
UnsetConfigRequest,
170180
UnsetConfigResponse,
171181
UploadConfigRequest,
@@ -174,13 +184,6 @@ import type {
174184
UseAccountResponse,
175185
} from '../routes'
176186
import { ApiNamespace } from '../routes/namespaces'
177-
import { DecryptWalletRequest, DecryptWalletResponse } from '../routes/wallet/decrypt'
178-
import {
179-
DeleteTransactionRequest,
180-
DeleteTransactionResponse,
181-
} from '../routes/wallet/deleteTransaction'
182-
import { EncryptWalletRequest, EncryptWalletResponse } from '../routes/wallet/encrypt'
183-
import { UnlockWalletRequest, UnlockWalletResponse } from '../routes/wallet/unlock'
184187

185188
export abstract class RpcClient {
186189
abstract close(): void
@@ -668,6 +671,13 @@ export abstract class RpcClient {
668671
params,
669672
).waitForEnd()
670673
},
674+
675+
lock: (params?: LockWalletRequest): Promise<RpcResponseEnded<LockWalletResponse>> => {
676+
return this.request<LockWalletResponse>(
677+
`${ApiNamespace.wallet}/lock`,
678+
params,
679+
).waitForEnd()
680+
},
671681
}
672682

673683
mempool = {
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
{
2+
"Route wallet/lock does nothing if the wallet is decrypted": [
3+
{
4+
"value": {
5+
"encrypted": false,
6+
"version": 4,
7+
"id": "9374e12f-9da1-4ed0-9992-179a78bbbf44",
8+
"name": "A",
9+
"spendingKey": "a4145ffbb03d8b66f80f27761cff36c1cacb583e5b09086bea79ef46884538fd",
10+
"viewKey": "29b2cc1d84fbd7944127148f7b8a92aed187ebad05c9494b5f92a95e726bdb430f2ffb722ccda7bb2d95da01fd336cce0874c700db44c41d9f4f3955225819ae",
11+
"incomingViewKey": "a58eb026316a992d578632f05667bc63a3e84365662758ba64307fec8067e005",
12+
"outgoingViewKey": "6ea17f4ed69dbaa60e771df2efa46ebad3f3a683cc660be74d96eb09b9f304a2",
13+
"publicAddress": "59d05112781447a20a9129f86ca31a8f9eae4a28da238d76b3e73dbbf86517a6",
14+
"createdAt": {
15+
"hash": {
16+
"type": "Buffer",
17+
"data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY="
18+
},
19+
"sequence": 1
20+
},
21+
"scanningEnabled": true,
22+
"proofAuthorizingKey": "602989a565416bd3fc015a7bc3a085c8911c4eea8484854614fd02528ee9e108"
23+
},
24+
"head": {
25+
"hash": {
26+
"type": "Buffer",
27+
"data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY="
28+
},
29+
"sequence": 1
30+
}
31+
},
32+
{
33+
"value": {
34+
"encrypted": false,
35+
"version": 4,
36+
"id": "adb72bdc-4216-4d8d-96a5-a42209243b73",
37+
"name": "B",
38+
"spendingKey": "74786da4227610d50c99525e1e63e5fdfa647952cbe29185c37e72a508a90463",
39+
"viewKey": "b62c45bee3cff9769378da7bc809f822778fb1d9ef799ac4f695476db9d48316e9cb447bcff80e4ea54573b59b308a15666b0579aed21430547d0bd845b2de19",
40+
"incomingViewKey": "a6936e0c99b1172ff9f76dfafff18694ce9e57c02bf6a632085991e4982c9003",
41+
"outgoingViewKey": "69846586a824f6a927c4465bb90520794b25574ffd92f93147a75eba9657c554",
42+
"publicAddress": "7f990dd2606ab6e718a19e87fe848a8e9ba917424e9f07ea87ec4704f0651ee4",
43+
"createdAt": {
44+
"hash": {
45+
"type": "Buffer",
46+
"data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY="
47+
},
48+
"sequence": 1
49+
},
50+
"scanningEnabled": true,
51+
"proofAuthorizingKey": "517cc469b43f901a89454df9d287bda126f775b15172424f4fcfbf459cafc800"
52+
},
53+
"head": {
54+
"hash": {
55+
"type": "Buffer",
56+
"data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY="
57+
},
58+
"sequence": 1
59+
}
60+
}
61+
],
62+
"Route wallet/lock locks the wallet": [
63+
{
64+
"value": {
65+
"encrypted": false,
66+
"version": 4,
67+
"id": "2a75f388-bce2-4e15-aec8-265c8413c501",
68+
"name": "A",
69+
"spendingKey": "361761823e0cc1af973072d48b013216d15468c5946a8ff7200249a024499cfb",
70+
"viewKey": "466f05770f70e964393210e2b0d71bb3315c61a761d87597dfdca24855b8bd1ccb3dcc69b24019f92d73d4312ccb3c8ee37cde3b976aed1f4a2ab911fa07a3e4",
71+
"incomingViewKey": "ec2d7f8626701a8f28b75306e65544d1158e3b4ff544023a9b5b1220d918a504",
72+
"outgoingViewKey": "25e124b435374aff46a01201be3ccfc1afbbab70c13465ac82af38778a6cd950",
73+
"publicAddress": "9735e4531c754982e6a59bfbdbfa43c6236a3fae37b281da2e1d88213831be66",
74+
"createdAt": {
75+
"hash": {
76+
"type": "Buffer",
77+
"data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY="
78+
},
79+
"sequence": 1
80+
},
81+
"scanningEnabled": true,
82+
"proofAuthorizingKey": "0d441f53c61cd43a56955fcef78d6c1cb52c4b463e16812752b7c5e777ff240a"
83+
},
84+
"head": {
85+
"hash": {
86+
"type": "Buffer",
87+
"data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY="
88+
},
89+
"sequence": 1
90+
}
91+
},
92+
{
93+
"value": {
94+
"encrypted": false,
95+
"version": 4,
96+
"id": "747df61f-cb52-4419-aa73-857c9f0bfce5",
97+
"name": "B",
98+
"spendingKey": "c0f9e1b439485b03d19d25eb06937201dea447cff67b5483e556fec4816c3be6",
99+
"viewKey": "b2d78fb858d9cbf950270923f18c8de6c3b2b60e66e3658951f86ed0b65712e1640b556c858699c26de7fdc11b60131fbe1d9ed947493d08ea34d30193c1fcb3",
100+
"incomingViewKey": "e9725a3336d4f65346b4e5c68b7e6f50a2c00d015be64d8e1cc2a6fb8ce75a01",
101+
"outgoingViewKey": "5a17bdeefebe53add6747ae4e3b32c0229d6b9023953a2f1bf1d0664cc36506b",
102+
"publicAddress": "257deef75e2eb67584ec560c44fb06d254f2d5576eb43e1788e50d65b80ccd27",
103+
"createdAt": {
104+
"hash": {
105+
"type": "Buffer",
106+
"data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY="
107+
},
108+
"sequence": 1
109+
},
110+
"scanningEnabled": true,
111+
"proofAuthorizingKey": "456290795a50d3602d12aa856c78b1a09098f971cc05bbabd905f42008b95e04"
112+
},
113+
"head": {
114+
"hash": {
115+
"type": "Buffer",
116+
"data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY="
117+
},
118+
"sequence": 1
119+
}
120+
}
121+
]
122+
}

ironfish/src/rpc/routes/wallet/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ export * from './getPublicKey'
3131
export * from './getTransactionNotes'
3232
export * from './getUnsignedTransactionNotes'
3333
export * from './importAccount'
34+
export * from './lock'
3435
export * from './mintAsset'
3536
export * from './multisig'
3637
export * from './postTransaction'
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/* This Source Code Form is subject to the terms of the Mozilla Public
2+
* License, v. 2.0. If a copy of the MPL was not distributed with this
3+
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4+
import { useAccountFixture } from '../../../testUtilities'
5+
import { createRouteTest } from '../../../testUtilities/routeTest'
6+
7+
describe('Route wallet/lock', () => {
8+
const routeTest = createRouteTest()
9+
10+
it('does nothing if the wallet is decrypted', async () => {
11+
await useAccountFixture(routeTest.node.wallet, 'A')
12+
await useAccountFixture(routeTest.node.wallet, 'B')
13+
14+
await routeTest.client.wallet.lock()
15+
16+
const status = await routeTest.client.wallet.getAccountsStatus()
17+
expect(status.content.encrypted).toBe(false)
18+
expect(status.content.locked).toBe(false)
19+
})
20+
21+
it('locks the wallet', async () => {
22+
const passphrase = 'foobar'
23+
24+
const accountA = await useAccountFixture(routeTest.node.wallet, 'A')
25+
const accountB = await useAccountFixture(routeTest.node.wallet, 'B')
26+
27+
await routeTest.client.wallet.encrypt({ passphrase })
28+
29+
let status = await routeTest.client.wallet.getAccountsStatus()
30+
expect(status.content.encrypted).toBe(true)
31+
expect(status.content.locked).toBe(true)
32+
33+
await routeTest.client.wallet.unlock({ passphrase })
34+
35+
status = await routeTest.client.wallet.getAccountsStatus()
36+
expect(status.content.encrypted).toBe(true)
37+
expect(status.content.locked).toBe(false)
38+
39+
let decryptedAccounts = await routeTest.client.wallet.getAccounts()
40+
expect(decryptedAccounts.content.accounts.sort()).toEqual([accountA.name, accountB.name])
41+
42+
await routeTest.client.wallet.lock()
43+
44+
status = await routeTest.client.wallet.getAccountsStatus()
45+
expect(status.content.encrypted).toBe(true)
46+
expect(status.content.locked).toBe(true)
47+
48+
decryptedAccounts = await routeTest.client.wallet.getAccounts()
49+
expect(decryptedAccounts.content.accounts).toHaveLength(0)
50+
})
51+
})
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/* This Source Code Form is subject to the terms of the Mozilla Public
2+
* License, v. 2.0. If a copy of the MPL was not distributed with this
3+
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4+
import * as yup from 'yup'
5+
import { ApiNamespace } from '../namespaces'
6+
import { routes } from '../router'
7+
import { AssertHasRpcContext } from '../rpcContext'
8+
9+
export type LockWalletRequest = undefined
10+
export type LockWalletResponse = undefined
11+
12+
export const LockWalletRequestSchema: yup.MixedSchema<LockWalletRequest> = yup
13+
.mixed()
14+
.oneOf([undefined] as const)
15+
16+
export const LockWalletResponseSchema: yup.MixedSchema<LockWalletResponse> = yup
17+
.mixed()
18+
.oneOf([undefined] as const)
19+
20+
routes.register<typeof LockWalletRequestSchema, LockWalletResponse>(
21+
`${ApiNamespace.wallet}/lock`,
22+
LockWalletRequestSchema,
23+
async (request, context): Promise<void> => {
24+
AssertHasRpcContext(request, context, 'wallet')
25+
await context.wallet.lock()
26+
request.end()
27+
},
28+
)

ironfish/src/rpc/routes/wallet/unlock.test.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,6 @@ describe('Route wallet/unlock', () => {
7070
const decryptedAccounts = await routeTest.client.wallet.getAccounts()
7171
expect(decryptedAccounts.content.accounts.sort()).toEqual([accountA.name, accountB.name])
7272

73-
// Temporary until the lock RPC is added
74-
await routeTest.node.wallet.lock()
73+
await routeTest.client.wallet.lock()
7574
})
7675
})

0 commit comments

Comments
 (0)