diff --git a/core/api/src/app/payments/send-lightning.ts b/core/api/src/app/payments/send-lightning.ts index 7cbc6172e7..e6d6878745 100644 --- a/core/api/src/app/payments/send-lightning.ts +++ b/core/api/src/app/payments/send-lightning.ts @@ -12,10 +12,11 @@ import { AccountValidator } from "@/domain/accounts" import { decodeInvoice, defaultTimeToExpiryInSeconds, - LnAlreadyPaidError, - LnPaymentPendingError, PaymentSendStatus, + LnPaymentAttemptResult, + LnPaymentAttemptResultType, } from "@/domain/bitcoin/lightning" +import { ResourceExpiredLockServiceError } from "@/domain/lock" import { AlreadyPaidError, CouldNotFindLightningPaymentFlowError } from "@/domain/errors" import { DisplayAmountsConverter } from "@/domain/fiat" import { @@ -74,8 +75,6 @@ import { validateIsUsdWallet, } from "@/app/wallets" -import { ResourceExpiredLockServiceError } from "@/domain/lock" - const dealer = DealerPriceService() const paymentFlowRepo = PaymentFlowStateRepository(defaultTimeToExpiryInSeconds) @@ -916,7 +915,7 @@ const lockedPaymentViaLnSteps = async ({ const { journalId } = journal // Execute payment - let payResult: PayInvoiceResult | LightningServiceError + let payResult: LnPaymentAttemptResult if (rawRoute) { payResult = await lndService.payInvoiceViaRoutes({ paymentHash, @@ -937,28 +936,44 @@ const lockedPaymentViaLnSteps = async ({ payResult = maxFeeCheck instanceof Error - ? maxFeeCheck + ? LnPaymentAttemptResult.err(maxFeeCheck) : await lndService.payInvoiceViaPaymentDetails({ ...maxFeeCheckArgs, decodedInvoice, }) } - if (!(payResult instanceof LnAlreadyPaidError)) { + if (!(payResult.type === LnPaymentAttemptResultType.AlreadyPaid)) { await LnPaymentsRepository().persistNew({ paymentHash: decodedInvoice.paymentHash, paymentRequest: decodedInvoice.paymentRequest, - sentFromPubkey: outgoingNodePubkey, + sentFromPubkey: + payResult.type === LnPaymentAttemptResultType.Error + ? outgoingNodePubkey + : payResult.result.sentFromPubkey, }) - if (!(payResult instanceof Error)) + if (payResult.type === LnPaymentAttemptResultType.Ok) await LedgerFacade.updateMetadataByHash({ hash: paymentHash, - revealedPreImage: payResult.revealedPreImage, + revealedPreImage: payResult.result.revealedPreImage, }) } - if (payResult instanceof LnPaymentPendingError) { + if ( + outgoingNodePubkey === undefined && + !(payResult.type === LnPaymentAttemptResultType.Error) + ) { + const updatedPubkey = await LedgerFacade.updatePubkeyByHash({ + paymentHash, + pubkey: payResult.result.sentFromPubkey, + }) + if (updatedPubkey instanceof Error) { + recordExceptionInCurrentSpan({ error: updatedPubkey }) + } + } + + if (payResult.type === LnPaymentAttemptResultType.Pending) { paymentFlow.paymentSentAndPending = true const updateResult = await paymentFlowRepo.updateLightningPaymentFlow(paymentFlow) if (updateResult instanceof Error) { @@ -977,7 +992,10 @@ const lockedPaymentViaLnSteps = async ({ } // Settle and record reversion entries - if (payResult instanceof Error) { + if ( + payResult.type === LnPaymentAttemptResultType.Error || + payResult.type === LnPaymentAttemptResultType.AlreadyPaid + ) { const settled = await LedgerFacade.settlePendingLnSend(paymentHash) if (settled instanceof Error) return LnSendAttemptResult.err(settled) @@ -995,31 +1013,24 @@ const lockedPaymentViaLnSteps = async ({ if (updateJournalTxnsState instanceof Error) { return LnSendAttemptResult.err(updateJournalTxnsState) } - return payResult instanceof LnAlreadyPaidError + + return payResult.type === LnPaymentAttemptResultType.AlreadyPaid ? LnSendAttemptResult.alreadyPaid(journalId) - : LnSendAttemptResult.errWithJournal({ journalId, error: payResult }) + : LnSendAttemptResult.errWithJournal({ journalId, error: payResult.error }) } // Settle and conditionally record reimbursement entries const settled = await LedgerFacade.settlePendingLnSend(paymentHash) if (settled instanceof Error) return LnSendAttemptResult.err(settled) - const updatedPubkey = await LedgerFacade.updatePubkeyByHash({ - paymentHash, - pubkey: payResult.sentFromPubkey, - }) - if (updatedPubkey instanceof Error) { - recordExceptionInCurrentSpan({ error: updatedPubkey }) - } - if (!rawRoute) { const reimbursed = await reimburseFee({ paymentFlow, senderDisplayAmount: toDisplayBaseAmount(displayAmount), senderDisplayCurrency, journalId, - actualFee: payResult.roundedUpFee, - revealedPreImage: payResult.revealedPreImage, + actualFee: payResult.result.roundedUpFee, + revealedPreImage: payResult.result.revealedPreImage, }) if (reimbursed instanceof Error) return LnSendAttemptResult.err(reimbursed) } diff --git a/core/api/src/app/payments/update-pending-payments.ts b/core/api/src/app/payments/update-pending-payments.ts index 45cc95eb5f..4f2d4e4015 100644 --- a/core/api/src/app/payments/update-pending-payments.ts +++ b/core/api/src/app/payments/update-pending-payments.ts @@ -329,14 +329,6 @@ const lockedPendingPaymentSteps = async ({ paymentLogger.error({ error: settled }, "no transaction to update") return settled } - const updatedPubkey = await LedgerFacade.updatePubkeyByHash({ - paymentHash, - pubkey: lnPaymentLookup.sentFromPubkey, - }) - if (updatedPubkey instanceof Error) { - paymentLogger.error({ error: updatedPubkey }, "no transaction to update") - return updatedPubkey - } let roundedUpFee: Satoshis = toSats(0) let satsAmount: Satoshis | undefined = undefined diff --git a/core/api/src/domain/bitcoin/lightning/index.ts b/core/api/src/domain/bitcoin/lightning/index.ts index 7c435381a3..7df2d983af 100644 --- a/core/api/src/domain/bitcoin/lightning/index.ts +++ b/core/api/src/domain/bitcoin/lightning/index.ts @@ -7,6 +7,7 @@ export { invoiceExpirationForCurrency, defaultTimeToExpiryInSeconds, } from "./invoice-expiration" +export * from "./ln-payment-result" export * from "./errors" export const PaymentStatus = { diff --git a/core/api/src/services/lnd/index.ts b/core/api/src/services/lnd/index.ts index 4fefbaec2d..48af91052e 100644 --- a/core/api/src/services/lnd/index.ts +++ b/core/api/src/services/lnd/index.ts @@ -76,6 +76,8 @@ import { decodeInvoice, InvalidInvoiceAmountError, InvoiceAlreadySettledError, + LnPaymentAttemptResult, + LnPaymentAttemptResultType, } from "@/domain/bitcoin/lightning" import { CacheKeys } from "@/domain/cache" import { LnFees } from "@/domain/payments" @@ -87,10 +89,6 @@ import { LocalCacheService } from "@/services/cache" import { wrapAsyncFunctionsToRunInSpan } from "@/services/tracing" import { timeoutWithCancel } from "@/utils" -import { - LnPaymentAttemptResult, - LnPaymentAttemptResultType, -} from "@/domain/bitcoin/lightning/ln-payment-result" const TIMEOUT_PAYMENT = NETWORK !== "regtest" ? 45000 : 3000 diff --git a/core/api/test/integration/app/wallets/send-lightning.spec.ts b/core/api/test/integration/app/wallets/send-lightning.spec.ts index 1b4c0bc593..3b294f44cd 100644 --- a/core/api/test/integration/app/wallets/send-lightning.spec.ts +++ b/core/api/test/integration/app/wallets/send-lightning.spec.ts @@ -8,6 +8,7 @@ import { MaxFeeTooLargeForRoutelessPaymentError, PaymentSendStatus, decodeInvoice, + LnPaymentAttemptResultType, } from "@/domain/bitcoin/lightning" import { UsdDisplayCurrency, toCents } from "@/domain/fiat" import { LnPaymentRequestNonZeroAmountRequiredError } from "@/domain/payments" @@ -430,9 +431,12 @@ describe("initiated via lightning", () => { defaultPubkey: (): Pubkey => DEFAULT_PUBKEY, listAllPubkeys: () => [], payInvoiceViaPaymentDetails: () => ({ - roundedUpFee: toSats(0), - revealedPreImage: "revealedPreImage" as RevealedPreImage, - sentFromPubkey: DEFAULT_PUBKEY, + type: LnPaymentAttemptResultType.Ok, + result: { + roundedUpFee: toSats(0), + revealedPreImage: "revealedPreImage" as RevealedPreImage, + sentFromPubkey: DEFAULT_PUBKEY, + }, }), }) @@ -549,9 +553,12 @@ describe("initiated via lightning", () => { defaultPubkey: (): Pubkey => DEFAULT_PUBKEY, listAllPubkeys: () => [], payInvoiceViaPaymentDetails: () => ({ - roundedUpFee: toSats(0), - revealedPreImage: "revealedPreImage" as RevealedPreImage, - sentFromPubkey: DEFAULT_PUBKEY, + type: LnPaymentAttemptResultType.Ok, + result: { + roundedUpFee: toSats(0), + revealedPreImage: "revealedPreImage" as RevealedPreImage, + sentFromPubkey: DEFAULT_PUBKEY, + }, }), }) diff --git a/core/api/test/integration/services/lnd-service.spec.ts b/core/api/test/integration/services/lnd-service.spec.ts index 103c3ef1f7..18d47248d4 100644 --- a/core/api/test/integration/services/lnd-service.spec.ts +++ b/core/api/test/integration/services/lnd-service.spec.ts @@ -20,6 +20,7 @@ import { PaymentStatus, RouteNotFoundError, decodeInvoice, + LnPaymentAttemptResultType, } from "@/domain/bitcoin/lightning" import { LnFees } from "@/domain/payments" @@ -42,7 +43,6 @@ import { waitFor, } from "test/helpers" import { BitcoindWalletClient } from "test/helpers/bitcoind" -import { LnPaymentAttemptResultType } from "@/domain/bitcoin/lightning/ln-payment-result" const amountInvoice = toSats(1000) const btcPaymentAmount = { amount: BigInt(amountInvoice), currency: WalletCurrency.Btc }