PHP WebShell
Текущая директория: /opt/BitGoJS/modules/abstract-lightning/src/wallet
Просмотр файла: lightning.ts
import * as sdkcore from '@bitgo/sdk-core';
import {
PendingApprovalData,
PendingApprovals,
RequestTracer,
RequestType,
TxRequest,
commonTssMethods,
TxRequestState,
decodeOrElse,
} from '@bitgo/sdk-core';
import * as t from 'io-ts';
import { createMessageSignature, unwrapLightningCoinSpecific } from '../lightning';
import {
CreateInvoiceBody,
Invoice,
InvoiceInfo,
InvoiceQuery,
LightningAuthKeychain,
LightningKeychain,
LndCreatePaymentResponse,
SubmitPaymentParams,
Transaction,
TransactionQuery,
PaymentInfo,
PaymentQuery,
LightningOnchainWithdrawParams,
LightningOnchainWithdrawResponse,
} from '../codecs';
import { LightningPaymentIntent, LightningPaymentRequest } from '@bitgo/public-types';
export type PayInvoiceResponse = {
/**
* Unique identifier for the payment request submitted to BitGo.
*/
txRequestId: string;
/**
* Status of the payment request submission to BitGo.
* - `'delivered'`: Successfully received by BitGo, but may or may not have been sent to the Lightning Network yet.
* - For the actual payment status, refer to `paymentStatus` and track `transfer`.
*/
txRequestState: TxRequestState;
/**
* Pending approval details, if applicable.
* - If present, the payment has not been initiated yet.
*/
pendingApproval?: PendingApprovalData;
/**
* Current snapshot of payment status (if available).
* - **`'in_flight'`**: Payment is in progress.
* - **`'settled'`**: Payment was successfully completed.
* - **`'failed'`**: Payment failed.
* This field is absent if approval is required before processing.
*/
paymentStatus?: LndCreatePaymentResponse;
/**
* Latest transfer details for this payment request (if available).
* - Provides the current state of the transfer.
* - To track the final payment status, monitor `transfer` asynchronously.
* This field is absent if approval is required before processing.
*/
transfer?: any;
};
/**
* Get the lightning keychain for the given wallet.
*/
export async function getLightningKeychain(wallet: sdkcore.IWallet): Promise<LightningKeychain> {
const coin = wallet.baseCoin;
if (coin.getFamily() !== 'lnbtc') {
throw new Error(`Invalid coin to get lightning wallet key: ${coin.getFamily()}`);
}
const keyIds = wallet.keyIds();
if (keyIds.length !== 1) {
throw new Error(`Invalid number of key in lightning wallet: ${keyIds.length}`);
}
const keychain = await coin.keychains().get({ id: keyIds[0] });
return sdkcore.decodeOrElse(LightningKeychain.name, LightningKeychain, keychain, (_) => {
throw new Error(`Invalid user key`);
});
}
/**
* Get the lightning auth keychains for the given wallet.
*/
export async function getLightningAuthKeychains(wallet: sdkcore.IWallet): Promise<{
userAuthKey: LightningAuthKeychain;
nodeAuthKey: LightningAuthKeychain;
}> {
const coin = wallet.baseCoin;
if (coin.getFamily() !== 'lnbtc') {
throw new Error(`Invalid coin to get lightning wallet auth keys: ${coin.getFamily()}`);
}
const authKeyIds = wallet.coinSpecific()?.keys;
if (authKeyIds?.length !== 2) {
throw new Error(`Invalid number of auth keys in lightning wallet: ${authKeyIds?.length}`);
}
const keychains = await Promise.all(authKeyIds.map((id) => coin.keychains().get({ id })));
const authKeychains = keychains.map((keychain) => {
return sdkcore.decodeOrElse(LightningAuthKeychain.name, LightningAuthKeychain, keychain, (_) => {
// DON'T throw errors from decodeOrElse. It could leak sensitive information.
throw new Error(`Invalid lightning auth key: ${keychain?.id}`);
});
});
const [userAuthKey, nodeAuthKey] = (['userAuth', 'nodeAuth'] as const).map((purpose) => {
const keychain = authKeychains.find(
(k) => unwrapLightningCoinSpecific(k.coinSpecific, coin.getChain()).purpose === purpose
);
if (!keychain) {
throw new Error(`Missing ${purpose} key`);
}
return keychain;
});
return { userAuthKey, nodeAuthKey };
}
export interface ILightningWallet {
/**
* Creates a lightning invoice
* @param {object} params Invoice parameters
* @param {bigint} params.valueMsat The value of the invoice in millisatoshis
* @param {string} [params.memo] A memo or description for the invoice
* @param {number} [params.expiry] The expiry time of the invoice in seconds
* @returns {Promise<Invoice>} A promise that resolves to the created invoice
*/
createInvoice(params: CreateInvoiceBody): Promise<Invoice>;
/**
* Get invoice details by payment hash
* @param {string} paymentHash - Payment hash to lookup
* @returns {Promise<InvoiceInfo>} Invoice details
* @throws {InvalidPaymentHash} When payment hash is not valid
*/
getInvoice(paymentHash: string): Promise<InvoiceInfo>;
/**
* Lists current lightning invoices
* @param {InvoiceQuery} params Query parameters for filtering invoices
* @param {string} [params.status] The status of the invoice (open, settled, canceled)
* @param {bigint} [params.limit] The maximum number of invoices to return
* @param {Date} [params.startDate] The start date for the query
* @param {Date} [params.endDate] The end date for the query
* @returns {Promise<InvoiceInfo[]>} List of invoices
*/
listInvoices(params: InvoiceQuery): Promise<InvoiceInfo[]>;
/**
* Pay a lightning invoice
* @param {SubmitPaymentParams} params - Payment parameters
* @param {string} params.invoice - The invoice to pay
* @param {string} params.amountMsat - The amount to pay in millisatoshis
* @param {string} params.passphrase - The wallet passphrase
* @param {string} [params.sequenceId] - Optional sequence ID for the respective payment transfer
* @param {string} [params.comment] - Optional comment for the respective payment transfer
* @returns {Promise<PayInvoiceResponse>} Payment result containing transaction request details and payment status
*/
payInvoice(params: SubmitPaymentParams): Promise<PayInvoiceResponse>;
/**
* On chain withdrawal
* @param {LightningOnchainWithdrawParams} params - Withdraw parameters
* @param {LightningOnchainRecipient[]} params.recipients - The recipients to pay
* @param {bigint} params.satsPerVbyte - Value for sats per virtual byte
* @returns {Promise<LightningOnchainWithdrawResponse>} Withdraw result containing transaction request details and status
*/
withdrawOnchain(params: LightningOnchainWithdrawParams): Promise<LightningOnchainWithdrawResponse>;
/**
* Get payment details by payment hash
* @param {string} paymentHash - Payment hash to lookup
* @returns {Promise<PaymentInfo>} Payment details
* @throws {InvalidPaymentHash} When payment hash is not valid
*/
getPayment(paymentHash: string): Promise<PaymentInfo>;
/**
* List payments for a wallet with optional filtering
* @param {PaymentQuery} params Query parameters for filtering payments
* @param {string} [params.status] The status of the payment
* @param {bigint} [params.limit] The maximum number of payments to return
* @param {Date} [params.startDate] The start date for the query
* @param {Date} [params.endDate] The end date for the query
* @returns {Promise<PaymentInfo[]>} List of payments
*/
listPayments(params: PaymentQuery): Promise<PaymentInfo[]>;
/**
* Get transaction details by ID
* @param {string} txId - Transaction ID to lookup
* @returns {Promise<Transaction>} Transaction details
* @throws {InvalidTxId} When transaction ID is not valid
*/
getTransaction(txId: string): Promise<Transaction>;
/**
* List transactions for a wallet with optional filtering
* @param {TransactionQuery} params Query parameters for filtering transactions
* @param {bigint} [params.limit] The maximum number of transactions to return
* @param {Date} [params.startDate] The start date for the query
* @param {Date} [params.endDate] The end date for the query
* @returns {Promise<Transaction[]>} List of transactions
*/
listTransactions(params: TransactionQuery): Promise<Transaction[]>;
}
export class LightningWallet implements ILightningWallet {
public wallet: sdkcore.IWallet;
constructor(wallet: sdkcore.IWallet) {
const coin = wallet.baseCoin;
if (coin.getFamily() !== 'lnbtc') {
throw new Error(`Invalid coin for lightning wallet: ${coin.getFamily()}`);
}
this.wallet = wallet;
}
async createInvoice(params: CreateInvoiceBody): Promise<Invoice> {
const createInvoiceResponse = await this.wallet.bitgo
.post(this.wallet.bitgo.url(`/wallet/${this.wallet.id()}/lightning/invoice`, 2))
.send(t.exact(CreateInvoiceBody).encode(params))
.result();
return sdkcore.decodeOrElse(Invoice.name, Invoice, createInvoiceResponse, (error) => {
// DON'T throw errors from decodeOrElse. It could leak sensitive information.
throw new Error(`Invalid create invoice response ${error}`);
});
}
async getInvoice(paymentHash: string): Promise<InvoiceInfo> {
const response = await this.wallet.bitgo
.get(this.wallet.bitgo.url(`/wallet/${this.wallet.id()}/lightning/invoice/${paymentHash}`, 2))
.result();
return decodeOrElse(InvoiceInfo.name, InvoiceInfo, response, (error) => {
throw new Error(`Invalid get invoice response ${error}`);
});
}
async listInvoices(params: InvoiceQuery): Promise<InvoiceInfo[]> {
const returnCodec = t.array(InvoiceInfo);
const createInvoiceResponse = await this.wallet.bitgo
.get(this.wallet.bitgo.url(`/wallet/${this.wallet.id()}/lightning/invoice`, 2))
.query(InvoiceQuery.encode(params))
.result();
return sdkcore.decodeOrElse(returnCodec.name, returnCodec, createInvoiceResponse, (error) => {
throw new Error(`Invalid list invoices response ${error}`);
});
}
async payInvoice(params: SubmitPaymentParams): Promise<PayInvoiceResponse> {
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(LightningPaymentRequest).encode(params),
this.wallet.bitgo.decrypt({ password: params.passphrase, input: userAuthKeyEncryptedPrv })
);
const paymentIntent: { intent: LightningPaymentIntent } = {
intent: {
comment: params.comment,
sequenceId: params.sequenceId,
intentType: 'payment',
signedRequest: {
invoice: params.invoice,
amountMsat: params.amountMsat,
feeLimitMsat: params.feeLimitMsat,
feeLimitRatio: params.feeLimitRatio,
},
signature,
},
};
const transactionRequestCreate = (await this.wallet.bitgo
.post(this.wallet.bitgo.url('/wallet/' + this.wallet.id() + '/txrequests', 2))
.send(t.type({ intent: LightningPaymentIntent }).encode(paymentIntent))
.result()) as TxRequest;
if (transactionRequestCreate.state === 'pendingApproval') {
const pendingApprovals = new PendingApprovals(this.wallet.bitgo, this.wallet.baseCoin);
const pendingApproval = await pendingApprovals.get({ id: transactionRequestCreate.pendingApprovalId });
return {
pendingApproval: pendingApproval.toJSON(),
txRequestId: transactionRequestCreate.txRequestId,
txRequestState: transactionRequestCreate.state,
};
}
const transactionRequestSend = await commonTssMethods.sendTxRequest(
this.wallet.bitgo,
this.wallet.id(),
transactionRequestCreate.txRequestId,
RequestType.tx,
reqId
);
const coinSpecific = transactionRequestSend.transactions?.[0]?.unsignedTx?.coinSpecific;
let transfer;
if (coinSpecific && coinSpecific.paymentHash && typeof coinSpecific.paymentHash === 'string') {
transfer = await this.wallet.getTransfer({ id: coinSpecific.paymentHash });
}
return {
txRequestId: transactionRequestCreate.txRequestId,
txRequestState: transactionRequestSend.state,
paymentStatus: coinSpecific
? t.exact(LndCreatePaymentResponse).encode(coinSpecific as LndCreatePaymentResponse)
: undefined,
transfer,
};
}
async withdrawOnchain(params: LightningOnchainWithdrawParams): Promise<LightningOnchainWithdrawResponse> {
const reqId = new RequestTracer();
this.wallet.bitgo.setRequestTracer(reqId);
const paymentIntent: { intent: LightningPaymentIntent } = {
intent: {
onchainRequest: {
recipients: params.recipients,
satsPerVbyte: params.satsPerVbyte,
},
intentType: 'payment',
},
};
const transactionRequestCreate = (await this.wallet.bitgo
.post(this.wallet.bitgo.url('/wallet/' + this.wallet.id() + '/txrequests', 2))
.send(t.type({ intent: LightningPaymentIntent }).encode(paymentIntent))
.result()) as TxRequest;
if (transactionRequestCreate.state === 'pendingApproval') {
const pendingApprovals = new PendingApprovals(this.wallet.bitgo, this.wallet.baseCoin);
const pendingApproval = await pendingApprovals.get({ id: transactionRequestCreate.pendingApprovalId });
return {
pendingApproval: pendingApproval.toJSON(),
txRequestId: transactionRequestCreate.txRequestId,
txRequestState: transactionRequestCreate.state,
};
}
const transactionRequestSend = await commonTssMethods.sendTxRequest(
this.wallet.bitgo,
this.wallet.id(),
transactionRequestCreate.txRequestId,
RequestType.tx,
reqId
);
return {
txRequestId: transactionRequestCreate.txRequestId,
txRequestState: transactionRequestSend.state,
};
}
async getPayment(paymentHash: string): Promise<PaymentInfo> {
const response = await this.wallet.bitgo
.get(this.wallet.bitgo.url(`/wallet/${this.wallet.id()}/lightning/payment/${paymentHash}`, 2))
.result();
return decodeOrElse(PaymentInfo.name, PaymentInfo, response, (error) => {
throw new Error(`Invalid payment response: ${error}`);
});
}
async listPayments(params: PaymentQuery): Promise<PaymentInfo[]> {
const response = await this.wallet.bitgo
.get(this.wallet.bitgo.url(`/wallet/${this.wallet.id()}/lightning/payment`, 2))
.query(PaymentQuery.encode(params))
.result();
return decodeOrElse(t.array(PaymentInfo).name, t.array(PaymentInfo), response, (error) => {
throw new Error(`Invalid payment list response: ${error}`);
});
}
async getTransaction(txId: string): Promise<Transaction> {
const response = await this.wallet.bitgo
.get(this.wallet.bitgo.url(`/wallet/${this.wallet.id()}/lightning/transaction/${txId}`, 2))
.result();
return decodeOrElse(Transaction.name, Transaction, response, (error) => {
throw new Error(`Invalid transaction response: ${error}`);
});
}
async listTransactions(params: TransactionQuery): Promise<Transaction[]> {
const response = await this.wallet.bitgo
.get(this.wallet.bitgo.url(`/wallet/${this.wallet.id()}/lightning/transaction`, 2))
.query(TransactionQuery.encode(params))
.result();
return decodeOrElse(t.array(Transaction).name, t.array(Transaction), response, (error) => {
throw new Error(`Invalid transaction list response: ${error}`);
});
}
}
Выполнить команду
Для локальной разработки. Не используйте в интернете!