Skip to content

Commit 24078a5

Browse files
committed
feat(express): migrate unlocklightningwallet to typed routes
TICKET: WP-5446
1 parent 5cd32b7 commit 24078a5

File tree

6 files changed

+69
-37
lines changed

6 files changed

+69
-37
lines changed

modules/express/src/clientRoutes.ts

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1566,6 +1566,10 @@ export function setupAPIRoutes(app: express.Application, config: Config): void {
15661566
router.post('express.decrypt', [prepareBitGo(config), typedPromiseWrapper(handleDecrypt)]);
15671567
router.post('express.encrypt', [prepareBitGo(config), typedPromiseWrapper(handleEncrypt)]);
15681568
router.post('express.verifyaddress', [prepareBitGo(config), typedPromiseWrapper(handleVerifyAddress)]);
1569+
router.post('express.lightning.unlockWallet', [
1570+
prepareBitGo(config),
1571+
typedPromiseWrapper(handleUnlockLightningWallet),
1572+
]);
15691573
app.post(
15701574
'/api/v[12]/calculateminerfeeinfo',
15711575
parseBody,
@@ -1802,11 +1806,5 @@ export function setupLightningSignerNodeRoutes(app: express.Application, config:
18021806
prepareBitGo(config),
18031807
promiseWrapper(handleCreateSignerMacaroon)
18041808
);
1805-
app.post(
1806-
'/api/v2/:coin/wallet/:id/unlockwallet',
1807-
parseBody,
1808-
prepareBitGo(config),
1809-
promiseWrapper(handleUnlockLightningWallet)
1810-
);
18111809
app.get('/api/v2/:coin/wallet/:id/state', prepareBitGo(config), promiseWrapper(handleGetLightningWalletState));
18121810
}

modules/express/src/lightning/lightningSignerRoutes.ts

Lines changed: 6 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,9 @@ import {
1313
} from '@bitgo/abstract-lightning';
1414
import * as utxolib from '@bitgo/utxo-lib';
1515
import { Buffer } from 'buffer';
16+
import { ExpressApiRouteRequest } from '../typedRoutes/api';
1617

17-
import {
18-
CreateSignerMacaroonRequest,
19-
GetWalletStateResponse,
20-
InitLightningWalletRequest,
21-
UnlockLightningWalletRequest,
22-
} from './codecs';
18+
import { CreateSignerMacaroonRequest, GetWalletStateResponse, InitLightningWalletRequest } from './codecs';
2319
import { LndSignerClient } from './lndSignerClient';
2420
import { ApiResponseError } from '../errors';
2521

@@ -204,25 +200,13 @@ export async function handleGetLightningWalletState(req: express.Request): Promi
204200
/**
205201
* Handle the request to unlock a wallet in the signer.
206202
*/
207-
export async function handleUnlockLightningWallet(req: express.Request): Promise<{ message: string }> {
208-
const coinName = req.params.coin;
203+
export async function handleUnlockLightningWallet(
204+
req: ExpressApiRouteRequest<'express.lightning.unlockWallet', 'post'>
205+
): Promise<{ message: string }> {
206+
const { coin: coinName, id: walletId, passphrase } = req.decoded;
209207
if (!isLightningCoinName(coinName)) {
210208
throw new ApiResponseError(`Invalid coin to unlock lightning wallet: ${coinName}`, 400);
211209
}
212-
const walletId = req.params.id;
213-
if (typeof walletId !== 'string') {
214-
throw new ApiResponseError(`Invalid wallet id: ${walletId}`, 400);
215-
}
216-
217-
const { passphrase } = decodeOrElse(
218-
UnlockLightningWalletRequest.name,
219-
UnlockLightningWalletRequest,
220-
req.body,
221-
(_) => {
222-
// DON'T throw errors from decodeOrElse. It could leak sensitive information.
223-
throw new ApiResponseError('Invalid request body to unlock lightning wallet', 400);
224-
}
225-
);
226210

227211
const lndSignerClient = await LndSignerClient.create(walletId, req.config);
228212
// The passphrase at LND can only accommodate a base64 character set

modules/express/src/typedRoutes/api/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { PostAcceptShare } from './v1/acceptShare';
1212
import { PostSimpleCreate } from './v1/simpleCreate';
1313
import { PutPendingApproval } from './v1/pendingApproval';
1414
import { PostSignTransaction } from './v1/signTransaction';
15+
import { PostUnlockLightningWallet } from './v2/unlockWallet';
1516

1617
export const ExpressApi = apiSpec({
1718
'express.ping': {
@@ -44,6 +45,9 @@ export const ExpressApi = apiSpec({
4445
'express.v1.wallet.signTransaction': {
4546
post: PostSignTransaction,
4647
},
48+
'express.lightning.unlockWallet': {
49+
post: PostUnlockLightningWallet,
50+
},
4751
});
4852

4953
export type ExpressApi = typeof ExpressApi;
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import * as t from 'io-ts';
2+
import { httpRoute, httpRequest } from '@api-ts/io-ts-http';
3+
import { BitgoExpressError } from '../../schemas/error';
4+
5+
/**
6+
* Path parameters for unlocking a lightning wallet
7+
* @property {string} coin - A lightning coin name (e.g, lnbtc).
8+
* @property {string} id - The ID of the wallet.
9+
*/
10+
export const UnlockLightningWalletParams = {
11+
coin: t.string,
12+
id: t.string,
13+
};
14+
15+
/**
16+
* Request body for unlocking a lightning wallet
17+
* @property {string} passphrase - Passphrase to unlock the lightning wallet.
18+
*/
19+
export const UnlockLightningWalletBody = {
20+
passphrase: t.string,
21+
};
22+
23+
/**
24+
* Lightning - Unlock node
25+
*
26+
* This is only used for self-custody lightning. Unlock the Lightning Network Daemon (LND) node with the given wallet password.
27+
*
28+
* @operationId express.lightning.unlockWallet
29+
*/
30+
export const PostUnlockLightningWallet = httpRoute({
31+
method: 'POST',
32+
path: '/api/v2/{coin}/wallet/{id}/unlockwallet',
33+
request: httpRequest({
34+
params: UnlockLightningWalletParams,
35+
body: UnlockLightningWalletBody,
36+
}),
37+
response: {
38+
200: t.type({
39+
message: t.string, // currently returns { message: 'ok' }
40+
}),
41+
400: BitgoExpressError,
42+
},
43+
});

modules/express/test/unit/clientRoutes/lightning/lightningSignerRoutes.ts

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -173,15 +173,9 @@ describe('Lightning signer routes', () => {
173173

174174
const req = {
175175
bitgo: bitgo,
176-
body: apiData.unlockWalletRequestBody,
177-
params: {
178-
coin: 'tlnbtc',
179-
id: 'fakeid',
180-
},
181-
config: {
182-
lightningSignerFileSystemPath: 'lightningSignerFileSystemPath',
183-
},
184-
} as unknown as express.Request;
176+
config: { lightningSignerFileSystemPath: 'lightningSignerFileSystemPath' },
177+
decoded: { coin: 'tlnbtc', id: 'fakeid', passphrase: apiData.unlockWalletRequestBody.passphrase },
178+
} as any;
185179

186180
await handleUnlockLightningWallet(req);
187181

modules/express/test/unit/typedRoutes/decode.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { EncryptRequestBody } from '../../../src/typedRoutes/api/common/encrypt'
55
import { LoginRequest } from '../../../src/typedRoutes/api/common/login';
66
import { VerifyAddressBody } from '../../../src/typedRoutes/api/common/verifyAddress';
77
import { SimpleCreateRequestBody } from '../../../src/typedRoutes/api/v1/simpleCreate';
8+
import { UnlockLightningWalletBody, UnlockLightningWalletParams } from '../../../src/typedRoutes/api/v2/unlockWallet';
89

910
export function assertDecode<T>(codec: t.Type<T, unknown>, input: unknown): T {
1011
const result = codec.decode(input);
@@ -100,4 +101,12 @@ describe('io-ts decode tests', function () {
100101
passphrase: 'pass',
101102
});
102103
});
104+
it('express.lightning.unlockWallet', function () {
105+
// params require coin and id
106+
assertDecode(t.type(UnlockLightningWalletParams), { coin: 'tlnbtc', id: 'wallet123' });
107+
// missing passphrase
108+
assert.throws(() => assertDecode(t.type(UnlockLightningWalletBody), {}));
109+
// valid body
110+
assertDecode(t.type(UnlockLightningWalletBody), { passphrase: 'secret' });
111+
});
103112
});

0 commit comments

Comments
 (0)