Skip to content

feat: add signature in onchain withdraw lightning #6610

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 11 additions & 9 deletions modules/abstract-lightning/src/codecs/api/withdraw.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,22 @@
import * as t from 'io-ts';
import { LightningOnchainRecipient } from '@bitgo/public-types';
import { LightningOnchainRequest, optionalString } from '@bitgo/public-types';
import { PendingApprovalData, TxRequestState } from '@bitgo/sdk-core';
import { BigIntFromString } from 'io-ts-types';

export const WithdrawStatusDelivered = 'delivered';
export const WithdrawStatusFailed = 'failed';

export const WithdrawStatus = t.union([t.literal(WithdrawStatusDelivered), t.literal(WithdrawStatusFailed)]);

export const LightningOnchainWithdrawParams = t.type({
recipients: t.array(LightningOnchainRecipient),
satsPerVbyte: BigIntFromString,
// todo:(current) add passphrase
// passphrase: t.string,
});

export const LightningOnchainWithdrawParams = t.intersection([
LightningOnchainRequest,
t.type({
passphrase: t.string,
}),
t.partial({
sequenceId: optionalString,
comment: optionalString,
}),
]);
export type LightningOnchainWithdrawParams = t.TypeOf<typeof LightningOnchainWithdrawParams>;

export const LndCreateWithdrawResponse = t.intersection(
Expand Down
20 changes: 18 additions & 2 deletions modules/abstract-lightning/src/wallet/lightning.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import {
ListPaymentsResponse,
LndCreateWithdrawResponse,
} from '../codecs';
import { LightningPaymentIntent, LightningPaymentRequest } from '@bitgo/public-types';
import { LightningPaymentIntent, LightningPaymentRequest, LightningOnchainRequest } from '@bitgo/public-types';

export type PayInvoiceResponse = {
/**
Expand Down Expand Up @@ -167,6 +167,9 @@ export interface ILightningWallet {
* @param {LightningOnchainWithdrawParams} params - Withdraw parameters
* @param {LightningOnchainRecipient[]} params.recipients - The recipients to pay
* @param {bigint} params.satsPerVbyte - Value for sats per virtual byte
* @param {string} params.passphrase - The wallet passphrase
* @param {string} [params.sequenceId] - Optional sequence ID for the respective withdraw transfer
* @param {string} [params.comment] - Optional comment for the respective withdraw transfer
* @returns {Promise<LightningOnchainWithdrawResponse>} Withdraw result containing transaction request details and status
*/
withdrawOnchain(params: LightningOnchainWithdrawParams): Promise<LightningOnchainWithdrawResponse>;
Expand Down Expand Up @@ -336,12 +339,25 @@ export class LightningWallet implements ILightningWallet {
const reqId = new RequestTracer();
this.wallet.bitgo.setRequestTracer(reqId);

const { userAuthKey } = await getLightningAuthKeychains(this.wallet);
const userAuthKeyEncryptedPrv = userAuthKey.encryptedPrv;
if (!userAuthKeyEncryptedPrv) {
throw new Error(`user auth key is missing encrypted private key`);
}
const signature = createMessageSignature(
t.exact(LightningOnchainRequest).encode(params),
this.wallet.bitgo.decrypt({ password: params.passphrase, input: userAuthKeyEncryptedPrv })
);

const paymentIntent: { intent: LightningPaymentIntent } = {
intent: {
onchainRequest: {
comment: params.comment,
sequenceId: params.sequenceId,
signedRequest: {
recipients: params.recipients,
satsPerVbyte: params.satsPerVbyte,
},
signature,
intentType: 'payment',
},
};
Expand Down
2 changes: 2 additions & 0 deletions modules/bitgo/test/v2/unit/lightning/lightningWallets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -825,6 +825,7 @@ describe('Lightning wallets', function () {
},
],
satsPerVbyte: 15n,
passphrase: 'password123',
};

const txRequestResponse = {
Expand Down Expand Up @@ -973,6 +974,7 @@ describe('Lightning wallets', function () {
},
],
satsPerVbyte: 15n,
passphrase: 'password123',
};

const txRequestResponse = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ describe('Lightning Withdraw Routes', () => {
},
],
satsPerVbyte: '15',
passphrase: 'password123',
};

const expectedResponse: LightningOnchainWithdrawResponse = {
Expand Down Expand Up @@ -84,6 +85,7 @@ describe('Lightning Withdraw Routes', () => {
// we decode the amountMsat string to bigint, it should be in bigint format when passed to payInvoice
should(firstArg).have.property('recipients', decodedRecipients);
should(firstArg).have.property('satsPerVbyte', BigInt(inputParams.satsPerVbyte));
should(firstArg).have.property('passphrase', BigInt(inputParams.satsPerVbyte));
});

it('should throw an error if the satsPerVbyte is missing in the request params', async () => {
Expand All @@ -94,6 +96,7 @@ describe('Lightning Withdraw Routes', () => {
address: 'bcrt1qjq48cqk2u80hewdcndf539m8nnnvt845nl68x7',
},
],
passphrase: 'password123',
};

const req = mockRequestObject({
Expand All @@ -110,6 +113,29 @@ describe('Lightning Withdraw Routes', () => {
it('should throw an error if the recipients is missing in the request params', async () => {
const inputParams = {
satsPerVbyte: '15',
passphrase: 'password123',
};

const req = mockRequestObject({
params: { id: 'testWalletId', coin },
body: inputParams,
});
req.bitgo = bitgo;

await should(handleLightningWithdraw(req)).be.rejectedWith(
'Invalid request body for withdrawing on chain lightning balance'
);
});

it('should throw an error if passphrase is missing in the request params', async () => {
const inputParams = {
satsPerVbyte: '15',
recipients: [
{
amountSat: '500000',
address: 'bcrt1qjq48cqk2u80hewdcndf539m8nnnvt845nl68x7',
},
],
};

const req = mockRequestObject({
Expand Down
Loading