Skip to content

Commit

Permalink
chore: prevent WalletInvoice deletion
Browse files Browse the repository at this point in the history
  • Loading branch information
UncleSamtoshi committed Nov 15, 2023
1 parent 7700265 commit 4c1e047
Show file tree
Hide file tree
Showing 16 changed files with 142 additions and 153 deletions.
37 changes: 26 additions & 11 deletions core/api/src/app/wallets/update-pending-invoices.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,10 +116,11 @@ export const declineHeldInvoice = wrapAsyncToRunInSpan({
})

if (lnInvoiceLookup instanceof InvoiceNotFoundError) {
const isDeleted = await walletInvoicesRepo.deleteByPaymentHash(paymentHash)
if (isDeleted instanceof Error) {
pendingInvoiceLogger.error("impossible to delete WalletInvoice entry")
return isDeleted
const processingCompletedInvoice =
await walletInvoicesRepo.markAsProcessingCompleted(paymentHash)
if (processingCompletedInvoice instanceof Error) {
pendingInvoiceLogger.error("Unable to mark invoice as processingCompleted")
return processingCompletedInvoice
}
return false
}
Expand Down Expand Up @@ -148,9 +149,10 @@ export const declineHeldInvoice = wrapAsyncToRunInSpan({
const invoiceSettled = await lndService.cancelInvoice({ pubkey, paymentHash })
if (invoiceSettled instanceof Error) return invoiceSettled

const isDeleted = await walletInvoicesRepo.deleteByPaymentHash(paymentHash)
if (isDeleted instanceof Error) {
pendingInvoiceLogger.error("impossible to delete WalletInvoice entry")
const processingCompletedInvoice =
await walletInvoicesRepo.markAsProcessingCompleted(paymentHash)
if (processingCompletedInvoice instanceof Error) {
pendingInvoiceLogger.error("Unable to mark invoice as processingCompleted")
}

return true
Expand Down Expand Up @@ -211,10 +213,11 @@ const updatePendingInvoiceBeforeFinally = async ({

const lnInvoiceLookup = await lndService.lookupInvoice({ pubkey, paymentHash })
if (lnInvoiceLookup instanceof InvoiceNotFoundError) {
const isDeleted = await walletInvoicesRepo.deleteByPaymentHash(paymentHash)
if (isDeleted instanceof Error) {
pendingInvoiceLogger.error("impossible to delete WalletInvoice entry")
return isDeleted
const processingCompletedInvoice =
await walletInvoicesRepo.markAsProcessingCompleted(paymentHash)
if (processingCompletedInvoice instanceof Error) {
pendingInvoiceLogger.error("Unable to mark invoice as processingCompleted")
return processingCompletedInvoice
}
return false
}
Expand All @@ -230,6 +233,17 @@ const updatePendingInvoiceBeforeFinally = async ({
return true
}

if (lnInvoiceLookup.isCanceled) {
pendingInvoiceLogger.info("invoice has been canceled")
const processingCompletedInvoice =
await walletInvoicesRepo.markAsProcessingCompleted(paymentHash)
if (processingCompletedInvoice instanceof Error) {
pendingInvoiceLogger.error("Unable to mark invoice as processingCompleted")
return processingCompletedInvoice
}
return false
}

if (!lnInvoiceLookup.isHeld && !lnInvoiceLookup.isSettled) {
pendingInvoiceLogger.info("invoice has not been paid yet")
return false
Expand Down Expand Up @@ -302,6 +316,7 @@ const updatePendingInvoiceBeforeFinally = async ({
"invoices.finalRecipient": JSON.stringify(recipientWalletDescriptor),
})

// Is this crossing the lock boundary?
if (!lnInvoiceLookup.isSettled) {
const invoiceSettled = await lndService.settleInvoice({ pubkey, secret })
if (invoiceSettled instanceof Error) return invoiceSettled
Expand Down
34 changes: 17 additions & 17 deletions core/api/src/debug/migrate-ln-payments-trackpaymentsv2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,23 +102,23 @@ const migrateLnPayment = async (
payment.status === PaymentStatus.Pending
? partialLnPayment
: payment.status === PaymentStatus.Failed
? {
...partialLnPayment,
status: PaymentStatus.Failed,
confirmedDetails: undefined,
attempts: undefined,
isCompleteRecord: true,
}
: {
...partialLnPayment,
createdAt: payment.createdAt,
status: payment.status,
milliSatsAmount: payment.milliSatsAmount,
roundedUpAmount: payment.roundedUpAmount,
confirmedDetails: payment.confirmedDetails,
attempts: payment.attempts, // will be null, only comes from getPayments
isCompleteRecord: true,
}
? {
...partialLnPayment,
status: PaymentStatus.Failed,
confirmedDetails: undefined,
attempts: undefined,
isCompleteRecord: true,
}
: {
...partialLnPayment,
createdAt: payment.createdAt,
status: payment.status,
milliSatsAmount: payment.milliSatsAmount,
roundedUpAmount: payment.roundedUpAmount,
confirmedDetails: payment.confirmedDetails,
attempts: payment.attempts, // will be null, only comes from getPayments
isCompleteRecord: true,
}

const updatedPaymentLookup = await LnPaymentsRepository().persistNew(newLnPayment)
if (updatedPaymentLookup instanceof Error) {
Expand Down
8 changes: 4 additions & 4 deletions core/api/src/domain/payments/payment-flow-builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ export const LightningPaymentFlowBuilder = <S extends WalletCurrency>(
destination === undefined
? SettlementMethod.IntraLedger
: config.localNodeIds.includes(destination)
? SettlementMethod.IntraLedger
: SettlementMethod.Lightning
? SettlementMethod.IntraLedger
: SettlementMethod.Lightning
return {
settlementMethod,
btcProtocolAndBankFee:
Expand Down Expand Up @@ -533,8 +533,8 @@ const LPFBWithConversion = <S extends WalletCurrency, R extends WalletCurrency>(
const hash = state.paymentHash
? { paymentHash: state.paymentHash }
: state.intraLedgerHash
? { intraLedgerHash: state.intraLedgerHash }
: new InvalidLightningPaymentFlowStateError()
? { intraLedgerHash: state.intraLedgerHash }
: new InvalidLightningPaymentFlowStateError()
if (hash instanceof Error) return hash

return PaymentFlow({
Expand Down
8 changes: 4 additions & 4 deletions core/api/src/domain/shared/error-parsers-unknown.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ export const parseUnknownDomainErrorFromUnknown = (error: unknown): DomainError
error instanceof Error
? error
: typeof error === "string"
? new UnknownDomainError(error)
: error instanceof Object
? new UnknownDomainError(JSON.stringify(error))
: new UnknownDomainError("Unknown error")
? new UnknownDomainError(error)
: error instanceof Object
? new UnknownDomainError(JSON.stringify(error))
: new UnknownDomainError("Unknown error")
return err
}
16 changes: 8 additions & 8 deletions core/api/src/domain/shared/error-parsers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ export const parseErrorMessageFromUnknown = (error: unknown): string => {
error instanceof Error
? error.message
: typeof error === "string"
? error
: error instanceof Object
? JSON.stringify(error)
: "Unknown error"
? error
: error instanceof Object
? JSON.stringify(error)
: "Unknown error"
return errMsg
}

Expand All @@ -15,9 +15,9 @@ export const parseErrorFromUnknown = (error: unknown): Error => {
error instanceof Error
? error
: typeof error === "string"
? new Error(error)
: error instanceof Object
? new Error(JSON.stringify(error))
: new Error("Unknown error")
? new Error(error)
: error instanceof Object
? new Error(JSON.stringify(error))
: new Error("Unknown error")
return err
}
7 changes: 4 additions & 3 deletions core/api/src/domain/wallet-invoices/index.types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ type WalletInvoiceWithOptionalLnInvoice = {
recipientWalletDescriptor: PartialWalletDescriptor<WalletCurrency>
paid: boolean
createdAt: Date
processingCompleted?: boolean
lnInvoice?: LnInvoice // LnInvoice is optional because some older invoices don't have it
}

Expand Down Expand Up @@ -197,7 +198,7 @@ interface IWalletInvoicesRepository {

yieldPending: () => AsyncGenerator<WalletInvoiceWithOptionalLnInvoice> | RepositoryError

deleteByPaymentHash: (paymentHash: PaymentHash) => Promise<boolean | RepositoryError>

deleteUnpaidOlderThan: (before: Date) => Promise<number | RepositoryError>
markAsProcessingCompleted: (
paymentHash: PaymentHash,
) => Promise<WalletInvoiceWithOptionalLnInvoice | RepositoryError>
}
4 changes: 2 additions & 2 deletions core/api/src/graphql/admin/root/query/lightning-payment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ const LightningPaymentQuery = GT.Field({
const paymentRequest = !(lightningPayment instanceof Error)
? lightningPayment.paymentRequest
: "paymentRequest" in lightningPaymentFromLnd
? lightningPaymentFromLnd.paymentRequest
: undefined
? lightningPaymentFromLnd.paymentRequest
: undefined

return {
...lightningPaymentFromLnd,
Expand Down
6 changes: 0 additions & 6 deletions core/api/src/servers/cron.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import {
} from "@/services/tracing"
import {
deleteExpiredLightningPaymentFlows,
deleteExpiredWalletInvoice,
deleteFailedPaymentsAttemptAllLnds,
updateEscrows,
updateRoutingRevenues,
Expand Down Expand Up @@ -40,10 +39,6 @@ const updateLegacyOnChainReceipt = async () => {
if (txNumber instanceof Error) throw txNumber
}

const deleteExpiredInvoices = async () => {
await deleteExpiredWalletInvoice()
}

const deleteExpiredPaymentFlows = async () => {
await deleteExpiredLightningPaymentFlows()
}
Expand Down Expand Up @@ -85,7 +80,6 @@ const main = async () => {
...(cronConfig.rebalanceEnabled ? [rebalance] : []),
...(cronConfig.swapEnabled ? [swapOutJob] : []),
deleteExpiredPaymentFlows,
deleteExpiredInvoices,
deleteLndPaymentsBefore2Months,
deleteFailedPaymentsAttemptAllLnds,
]
Expand Down
20 changes: 1 addition & 19 deletions core/api/src/services/lnd/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,29 +35,11 @@ import {
updateLndEscrow,
} from "@/services/ledger/admin-legacy"
import { baseLogger } from "@/services/logger"
import { PaymentFlowStateRepository, WalletInvoicesRepository } from "@/services/mongoose"
import { PaymentFlowStateRepository } from "@/services/mongoose"
import { DbMetadata } from "@/services/mongoose/schema"

import { timestampDaysAgo } from "@/utils"

export const deleteExpiredWalletInvoice = async (): Promise<number> => {
const walletInvoicesRepo = WalletInvoicesRepository()

// this should be longer than the invoice validity time
const delta = 90 // days

const date = new Date()
date.setDate(date.getDate() - delta)

const result = await walletInvoicesRepo.deleteUnpaidOlderThan(date)
if (result instanceof Error) {
baseLogger.error({ error: result }, "error deleting expired invoices")
return 0
}

return result
}

export const deleteFailedPaymentsAttemptAllLnds = async () => {
const lnds = offchainLnds
for (const { lnd, socket } of lnds) {
Expand Down
28 changes: 14 additions & 14 deletions core/api/src/services/mongoose/payment-flow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ export const PaymentFlowStateRepository = (
const hash = paymentHash
? { paymentHash }
: intraLedgerHash
? { intraLedgerHash }
: new BadInputsForFindError(JSON.stringify(args))
? { intraLedgerHash }
: new BadInputsForFindError(JSON.stringify(args))
if (hash instanceof Error) return hash

try {
Expand Down Expand Up @@ -192,10 +192,10 @@ const paymentFlowFromRaw = <S extends WalletCurrency, R extends WalletCurrency>(
const hash = paymentHash
? { paymentHash: paymentHash as PaymentHash }
: intraLedgerHash
? { intraLedgerHash: intraLedgerHash as IntraLedgerHash }
: new InvalidLightningPaymentFlowStateError(
"Missing valid 'paymentHash' or 'intraLedgerHash'",
)
? { intraLedgerHash: intraLedgerHash as IntraLedgerHash }
: new InvalidLightningPaymentFlowStateError(
"Missing valid 'paymentHash' or 'intraLedgerHash'",
)
if (hash instanceof Error) return hash

const btcPaymentAmount = paymentAmountFromNumber({
Expand Down Expand Up @@ -262,10 +262,10 @@ const rawFromPaymentFlow = <S extends WalletCurrency, R extends WalletCurrency>(
const hash = paymentHash
? { paymentHash }
: intraLedgerHash
? { intraLedgerHash }
: new InvalidLightningPaymentFlowStateError(
"Missing valid 'paymentHash' or 'intraLedgerHash'",
)
? { intraLedgerHash }
: new InvalidLightningPaymentFlowStateError(
"Missing valid 'paymentHash' or 'intraLedgerHash'",
)
if (hash instanceof Error) return hash

return {
Expand Down Expand Up @@ -307,10 +307,10 @@ const rawIndexFromPaymentFlowIndex = (
const hash = paymentHash
? { paymentHash }
: intraLedgerHash
? { intraLedgerHash }
: new InvalidLightningPaymentFlowStateError(
"Missing valid 'paymentHash' or 'intraLedgerHash'",
)
? { intraLedgerHash }
: new InvalidLightningPaymentFlowStateError(
"Missing valid 'paymentHash' or 'intraLedgerHash'",
)
if (hash instanceof Error) return hash

return {
Expand Down
5 changes: 5 additions & 0 deletions core/api/src/services/mongoose/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,11 @@ const walletInvoiceSchema = new Schema<WalletInvoiceRecord>({
default: false,
},

processingCompleted: {
type: Boolean,
default: false,
},

paymentRequest: {
type: String,
},
Expand Down
1 change: 1 addition & 0 deletions core/api/src/services/mongoose/schema.types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ interface WalletInvoiceRecord {
currency: string
timestamp: Date
selfGenerated: boolean
processingCompleted?: boolean
pubkey: string
paid: boolean
paymentRequest?: string // optional because we historically did not store it
Expand Down
Loading

0 comments on commit 4c1e047

Please sign in to comment.