Skip to content

Commit 10d8d14

Browse files
committed
feat: migrate handleCreateSignerMacaroon
TICKET: WP-5445
1 parent e39dbdb commit 10d8d14

File tree

7 files changed

+744
-83
lines changed

7 files changed

+744
-83
lines changed

modules/express/src/clientRoutes.ts

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1583,11 +1583,6 @@ export function setupAPIRoutes(app: express.Application, config: Config): void {
15831583
router.post('express.decrypt', [prepareBitGo(config), typedPromiseWrapper(handleDecrypt)]);
15841584
router.post('express.encrypt', [prepareBitGo(config), typedPromiseWrapper(handleEncrypt)]);
15851585
router.post('express.verifyaddress', [prepareBitGo(config), typedPromiseWrapper(handleVerifyAddress)]);
1586-
router.post('express.lightning.initWallet', [prepareBitGo(config), typedPromiseWrapper(handleInitLightningWallet)]);
1587-
router.post('express.lightning.unlockWallet', [
1588-
prepareBitGo(config),
1589-
typedPromiseWrapper(handleUnlockLightningWallet),
1590-
]);
15911586
router.post('express.calculateminerfeeinfo', [
15921587
prepareBitGo(config),
15931588
typedPromiseWrapper(handleCalculateMinerFeeInfo),
@@ -1610,7 +1605,6 @@ export function setupAPIRoutes(app: express.Application, config: Config): void {
16101605
);
16111606

16121607
router.post('express.v1.wallet.signTransaction', [prepareBitGo(config), typedPromiseWrapper(handleSignTransaction)]);
1613-
router.get('express.lightning.getState', [prepareBitGo(config), typedPromiseWrapper(handleGetLightningWalletState)]);
16141608

16151609
app.post('/api/v1/wallet/:id/simpleshare', parseBody, prepareBitGo(config), promiseWrapper(handleShareWallet));
16161610
router.post('express.v1.wallet.acceptShare', [prepareBitGo(config), typedPromiseWrapper(handleAcceptShare)]);
@@ -1757,10 +1751,16 @@ export function setupSigningRoutes(app: express.Application, config: Config): vo
17571751
}
17581752

17591753
export function setupLightningSignerNodeRoutes(app: express.Application, config: Config): void {
1760-
app.post(
1761-
'/api/v2/:coin/wallet/:id/signermacaroon',
1762-
parseBody,
1754+
const router = createExpressRouter();
1755+
app.use(router);
1756+
router.post('express.lightning.initWallet', [prepareBitGo(config), typedPromiseWrapper(handleInitLightningWallet)]);
1757+
router.post('express.lightning.signerMacaroon', [
17631758
prepareBitGo(config),
1764-
promiseWrapper(handleCreateSignerMacaroon)
1765-
);
1759+
typedPromiseWrapper(handleCreateSignerMacaroon),
1760+
]);
1761+
router.post('express.lightning.unlockWallet', [
1762+
prepareBitGo(config),
1763+
typedPromiseWrapper(handleUnlockLightningWallet),
1764+
]);
1765+
router.get('express.lightning.getState', [prepareBitGo(config), typedPromiseWrapper(handleGetLightningWalletState)]);
17661766
}

modules/express/src/lightning/lightningSignerRoutes.ts

Lines changed: 6 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
import { isIP } from 'net';
2-
import * as express from 'express';
3-
import { decodeOrElse } from '@bitgo/sdk-core';
42
import {
53
getUtxolibNetwork,
64
signerMacaroonPermissions,
@@ -13,11 +11,11 @@ import {
1311
} from '@bitgo/abstract-lightning';
1412
import * as utxolib from '@bitgo/utxo-lib';
1513
import { Buffer } from 'buffer';
16-
import { ExpressApiRouteRequest } from '../typedRoutes/api';
1714

18-
import { CreateSignerMacaroonRequest, GetWalletStateResponse } from './codecs';
15+
import { GetWalletStateResponse } from './codecs';
1916
import { LndSignerClient } from './lndSignerClient';
2017
import { ApiResponseError } from '../errors';
18+
import { ExpressApiRouteRequest } from '../typedRoutes/api';
2119

2220
type Decrypt = (params: { input: string; password: string }) => string;
2321

@@ -106,28 +104,19 @@ export async function handleInitLightningWallet(
106104
/**
107105
* Handle the request to create a signer macaroon from remote signer LND for a wallet.
108106
*/
109-
export async function handleCreateSignerMacaroon(req: express.Request): Promise<unknown> {
107+
export async function handleCreateSignerMacaroon(
108+
req: ExpressApiRouteRequest<'express.lightning.signerMacaroon', 'post'>
109+
): Promise<unknown> {
110110
const bitgo = req.bitgo;
111-
const coinName = req.params.coin;
111+
const { coin: coinName, walletId, passphrase, addIpCaveatToMacaroon } = req.decoded;
112112
if (!isLightningCoinName(coinName)) {
113113
throw new ApiResponseError(`Invalid coin to create signer macaroon: ${coinName}. Must be a lightning coin.`, 400);
114114
}
115115
const coin = bitgo.coin(coinName);
116-
const walletId = req.params.id;
117116
if (typeof walletId !== 'string') {
118117
throw new ApiResponseError(`Invalid wallet id: ${walletId}`, 400);
119118
}
120119

121-
const { passphrase, addIpCaveatToMacaroon } = decodeOrElse(
122-
CreateSignerMacaroonRequest.name,
123-
CreateSignerMacaroonRequest,
124-
req.body,
125-
(_) => {
126-
// DON'T throw errors from decodeOrElse. It could leak sensitive information.
127-
throw new ApiResponseError('Invalid request body to create signer macaroon', 400);
128-
}
129-
);
130-
131120
const wallet = await coin.wallets().get({ id: walletId, includeBalance: false });
132121
if (wallet.subType() !== 'lightningSelfCustody') {
133122
throw new ApiResponseError(`not a self custodial lighting wallet ${walletId}`, 400);

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import { PutFanoutUnspents } from './v1/fanoutUnspents';
2828
import { PostOfcSignPayload } from './v2/ofcSignPayload';
2929
import { PostWalletRecoverToken } from './v2/walletRecoverToken';
3030
import { PostGenerateWallet } from './v2/generateWallet';
31+
import { PostSignerMacaroon } from './v2/signerMacaroon';
3132
import { PostCoinSignTx } from './v2/coinSignTx';
3233
import { PostWalletSignTx } from './v2/walletSignTx';
3334
import { PostWalletTxSignTSS } from './v2/walletTxSignTSS';
@@ -298,6 +299,9 @@ export const ExpressWalletManagementApiSpec = apiSpec({
298299
'express.wallet.generate': {
299300
post: PostGenerateWallet,
300301
},
302+
'express.lightning.signerMacaroon': {
303+
post: PostSignerMacaroon,
304+
},
301305
});
302306

303307
export const ExpressV2CanonicalAddressApiSpec = apiSpec({
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import * as t from 'io-ts';
2+
import { httpRoute, httpRequest, optional } from '@api-ts/io-ts-http';
3+
import { BitgoExpressError } from '../../schemas/error';
4+
5+
/**
6+
* Path parameters for creating a signer macaroon
7+
* @property {string} coin - A lightning coin name (e.g, lnbtc).
8+
* @property {string} walletId - The ID of the wallet.
9+
*/
10+
export const SignerMacaroonParams = {
11+
/** A lightning coin name (e.g, lnbtc). */
12+
coin: t.string,
13+
/** The ID of the wallet. */
14+
walletId: t.string,
15+
} as const;
16+
17+
/**
18+
* Request body for creating a signer macaroon
19+
* @property {string} passphrase - Passphrase to decrypt the admin macaroon of the signer node.
20+
* @property {boolean} addIpCaveatToMacaroon - If true, adds an IP caveat to the generated signer macaroon.
21+
*/
22+
export const SignerMacaroonBody = {
23+
/** Passphrase to decrypt the admin macaroon of the signer node. */
24+
passphrase: t.string,
25+
/** If true, adds an IP caveat to the generated signer macaroon. */
26+
addIpCaveatToMacaroon: optional(t.boolean),
27+
} as const;
28+
29+
/**
30+
* Response
31+
* - 200: Returns the updated wallet. On success, the wallet's `coinSpecific` includes the generated signer macaroon (derived from the signer node admin macaroon), optionally with an IP caveat.
32+
* - 400: BitGo Express error payload when macaroon creation cannot proceed (e.g., invalid coin, wallet not self‑custody lightning, missing encrypted signer admin macaroon, or external IP not set when an IP caveat is requested).
33+
*
34+
* See platform spec: POST /api/v2/{coin}/wallet/{walletId}/signermacaroon
35+
*/
36+
export const SignerMacaroonResponse = {
37+
/** The updated wallet with the generated signer macaroon. */
38+
200: t.UnknownRecord,
39+
/** BitGo Express error payload. */
40+
400: BitgoExpressError,
41+
} as const;
42+
43+
/**
44+
* Lightning - Create signer macaroon
45+
*
46+
* This is only used for self-custody lightning.
47+
* Create the signer macaroon for the watch-only Lightning Network Daemon (LND) node.
48+
* This macaroon derives from the signer node admin macaroon and is used by the watch-only node to request signatures from the signer node for operational tasks.
49+
* Returns the updated wallet with the encrypted signer macaroon in the `coinSpecific` response field.
50+
*
51+
* @operationId express.lightning.signerMacaroon
52+
* @tag express
53+
*/
54+
export const PostSignerMacaroon = httpRoute({
55+
method: 'POST',
56+
path: '/api/v2/{coin}/wallet/{walletId}/signermacaroon',
57+
request: httpRequest({
58+
params: SignerMacaroonParams,
59+
body: SignerMacaroonBody,
60+
}),
61+
response: SignerMacaroonResponse,
62+
});

0 commit comments

Comments
 (0)