Skip to content

Commit

Permalink
Revert "feat: hodl invoice (on WalletInvoiceBuilder) (#1395)" (#1483)
Browse files Browse the repository at this point in the history
This reverts commit 91dd5f1.
  • Loading branch information
vindard authored Jul 26, 2022
1 parent 91dd5f1 commit bbaec22
Show file tree
Hide file tree
Showing 25 changed files with 201 additions and 418 deletions.
16 changes: 12 additions & 4 deletions src/app/wallets/get-balance-for-wallet.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,25 @@
import { LedgerService } from "@services/ledger"
import { updatePendingPaymentsByWalletId } from "@app/payments"

import { updatePendingInvoicesByWalletId } from "./update-pending-invoices"

export const getBalanceForWallet = async ({
walletId,
logger,
}: {
walletId: WalletId
logger: Logger
}): Promise<CurrencyBaseAmount | ApplicationError> => {
const updatePaymentsResult = await updatePendingPaymentsByWalletId({
walletId,
logger,
})
const [, updatePaymentsResult] = await Promise.all([
updatePendingInvoicesByWalletId({
walletId,
logger,
}),
updatePendingPaymentsByWalletId({
walletId,
logger,
}),
])
if (updatePaymentsResult instanceof Error) return updatePaymentsResult

return LedgerService().getWalletBalance(walletId)
Expand Down
146 changes: 53 additions & 93 deletions src/app/wallets/update-pending-invoices.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,36 +12,57 @@ import {
WalletsRepository,
} from "@services/mongoose"
import { NotificationsService } from "@services/notifications"
import { elapsedSinceTimestamp, runInParallel } from "@utils"
import { runInParallel } from "@utils"
import { WalletInvoiceReceiver } from "@domain/wallet-invoices/wallet-invoice-receiver"
import * as LedgerFacade from "@services/ledger/facade"
import { usdFromBtcMidPriceFn } from "@app/shared"

export const declineHeldInvoices = async (logger: Logger): Promise<void> => {
export const updatePendingInvoices = async (logger: Logger): Promise<void> => {
const invoicesRepo = WalletInvoicesRepository()

const pendingInvoices = invoicesRepo.yieldPending()
const walletIdsWithPendingInvoices = invoicesRepo.listWalletIdsWithPendingInvoices()

if (pendingInvoices instanceof Error) {
if (walletIdsWithPendingInvoices instanceof Error) {
logger.error(
{ error: pendingInvoices },
{ error: walletIdsWithPendingInvoices },
"finish updating pending invoices with error",
)
return
}

await runInParallel({
iterator: pendingInvoices,
iterator: walletIdsWithPendingInvoices,
logger,
processor: async (walletInvoice: WalletInvoice, index: number) => {
logger.trace("updating pending invoices %s in worker %d", index)
await declineHeldInvoice({ walletInvoice, logger })
processor: async (walletId: WalletId, index: number) => {
logger.trace(
"updating pending invoices for wallet %s in worker %d",
walletId,
index,
)
await updatePendingInvoicesByWalletId({ walletId, logger })
},
})

logger.info("finish updating pending invoices")
}

export const updatePendingInvoicesByWalletId = async ({
walletId,
logger,
}: {
walletId: WalletId
logger: Logger
}) => {
const invoicesRepo = WalletInvoicesRepository()

const invoices = invoicesRepo.findPendingByWalletId(walletId)
if (invoices instanceof Error) return invoices

for await (const walletInvoice of invoices) {
await updatePendingInvoice({ walletInvoice, logger })
}
}

export const updatePendingInvoiceByPaymentHash = async ({
paymentHash,
logger,
Expand Down Expand Up @@ -73,43 +94,46 @@ const updatePendingInvoice = async ({

const walletInvoicesRepo = WalletInvoicesRepository()

const { pubkey, paymentHash, secret, recipientWalletDescriptor } = walletInvoice

const pendingInvoiceLogger = logger.child({
hash: paymentHash,
walletId: recipientWalletDescriptor.id,
topic: "payment",
protocol: "lightning",
transactionType: "receipt",
onUs: false,
})
const { pubkey, paymentHash, recipientWalletDescriptor } = walletInvoice

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")
logger.error(
{ walletInvoice, error: isDeleted },
"impossible to delete WalletInvoice entry",
)
return isDeleted
}
return false
}
if (lnInvoiceLookup instanceof Error) return lnInvoiceLookup

if (!lnInvoiceLookup.isSettled) {
logger.debug({ invoice: lnInvoiceLookup }, "invoice has not been paid")
return false
}

const {
lnInvoice: { description },
roundedDownReceived,
} = lnInvoiceLookup

const pendingInvoiceLogger = logger.child({
hash: paymentHash,
walletId: recipientWalletDescriptor.id,
topic: "payment",
protocol: "lightning",
transactionType: "receipt",
onUs: false,
})

if (walletInvoice.paid) {
pendingInvoiceLogger.info("invoice has already been processed")
return true
}

if (!lnInvoiceLookup.isHeld) {
pendingInvoiceLogger.info("invoice has not been paid yet")
return false
}

const receivedBtc = paymentAmountFromNumber({
amount: roundedDownReceived,
currency: WalletCurrency.Btc,
Expand All @@ -133,25 +157,22 @@ const updatePendingInvoice = async ({
return false
}
if (invoiceToUpdate instanceof Error) return invoiceToUpdate
if (walletInvoice.paid) {
if (invoiceToUpdate.paid) {
pendingInvoiceLogger.info("invoice has already been processed")
return true
}

const displayCurrencyPerSat = await getCurrentPrice()
if (displayCurrencyPerSat instanceof Error) return displayCurrencyPerSat

const invoiceSettled = await lndService.settleInvoice({ pubkey, secret })
if (invoiceSettled instanceof Error) return invoiceSettled

const invoicePaid = await walletInvoicesRepo.markAsPaid(paymentHash)
if (invoicePaid instanceof Error) return invoicePaid

// TODO: this should be a in a mongodb transaction session with the ledger transaction below
// markAsPaid could be done after the transaction, but we should in that case not only look
// for walletInvoicesRepo, but also in the ledger to make sure in case the process crash in this
// loop that an eventual consistency doesn't lead to a double credit

const invoicePaid = await walletInvoicesRepo.markAsPaid(paymentHash)
if (invoicePaid instanceof Error) return invoicePaid

const metadata = LedgerFacade.LnReceiveLedgerMetadata({
paymentHash,
fee: walletInvoiceReceiver.btcBankFee,
Expand Down Expand Up @@ -217,64 +238,3 @@ const updatePendingInvoice = async ({
return true
})
}

const declineHeldInvoice = async ({
walletInvoice,
logger,
}: {
walletInvoice: WalletInvoice
logger: Logger
}): Promise<boolean | ApplicationError> => {
const lndService = LndService()
if (lndService instanceof Error) return lndService

const walletInvoicesRepo = WalletInvoicesRepository()

const { pubkey, paymentHash } = walletInvoice

const lnInvoiceLookup = await lndService.lookupInvoice({ pubkey, paymentHash })

const pendingInvoiceLogger = logger.child({
hash: paymentHash,
lnInvoiceLookup,
walletInvoice,
topic: "payment",
protocol: "lightning",
transactionType: "receipt",
onUs: false,
})

if (lnInvoiceLookup instanceof InvoiceNotFoundError) {
const isDeleted = await walletInvoicesRepo.deleteByPaymentHash(paymentHash)
if (isDeleted instanceof Error) {
pendingInvoiceLogger.error("impossible to delete WalletInvoice entry")
return isDeleted
}
return false
}
if (lnInvoiceLookup instanceof Error) return lnInvoiceLookup

if (!lnInvoiceLookup.isHeld) {
pendingInvoiceLogger.info({ lnInvoiceLookup }, "invoice has not been paid yet")
return false
}

let heldForMsg = ""
if (lnInvoiceLookup.heldAt) {
heldForMsg = `for ${elapsedSinceTimestamp(lnInvoiceLookup.heldAt)}s `
}
pendingInvoiceLogger.error(
{ lnInvoiceLookup },
`invoice has been held ${heldForMsg}and is now been cancelled`,
)

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")
}

return true
}
4 changes: 0 additions & 4 deletions src/domain/bitcoin/lightning/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,6 @@ export class CouldNotDecodeReturnedPaymentRequest extends LightningServiceError
export class UnknownLightningServiceError extends LightningServiceError {
level = ErrorLevel.Critical
}
export class SecretDoesNotMatchAnyExistingHodlInvoiceError extends LightningServiceError {
level = ErrorLevel.Critical
}

export class InvoiceNotFoundError extends LightningServiceError {}
export class LnPaymentPendingError extends LightningServiceError {}
export class LnAlreadyPaidError extends LightningServiceError {}
Expand Down
13 changes: 0 additions & 13 deletions src/domain/bitcoin/lightning/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import { createHash, randomBytes } from "crypto"

import { InvalidPubKeyError } from "@domain/errors"

export { decodeInvoice } from "./ln-invoice"
Expand Down Expand Up @@ -29,14 +27,3 @@ export const checkedToPubkey = (pubkey: string): Pubkey | InvalidPubKeyError =>
}
return new InvalidPubKeyError("Pubkey conversion error")
}

export const sha256 = (buffer: Buffer) =>
createHash("sha256").update(buffer).digest("hex")
const randomSecret = () => randomBytes(32)

export const getSecretAndPaymentHash = () => {
const secret = randomSecret()
const paymentHash = sha256(secret) as PaymentHash

return { secret: secret.toString("hex") as SecretPreImage, paymentHash }
}
12 changes: 0 additions & 12 deletions src/domain/bitcoin/lightning/index.types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,6 @@ type LnInvoiceLookup = {
readonly createdAt: Date
readonly confirmedAt: Date | undefined
readonly isSettled: boolean
readonly isHeld: boolean
readonly heldAt: Date | undefined
readonly roundedDownReceived: Satoshis
readonly milliSatsReceived: MilliSatoshis
readonly secretPreImage: SecretPreImage
Expand Down Expand Up @@ -111,15 +109,13 @@ type LnInvoice = {
}

type RegisterInvoiceArgs = {
paymentHash: PaymentHash
description: string
descriptionHash?: string
sats: Satoshis
expiresAt: InvoiceExpiration
}

type NewRegisterInvoiceArgs = {
paymentHash: PaymentHash
description: string
descriptionHash?: string
btcPaymentAmount: BtcPaymentAmount
Expand Down Expand Up @@ -223,14 +219,6 @@ interface ILightningService {
pubkey?: Pubkey
}): Promise<true | LightningServiceError>

settleInvoice({
pubkey,
secret,
}: {
pubkey: Pubkey
secret: SecretPreImage
}): Promise<true | LightningServiceError>

cancelInvoice({
pubkey,
paymentHash,
Expand Down
5 changes: 0 additions & 5 deletions src/domain/wallet-invoices/errors.ts

This file was deleted.

7 changes: 5 additions & 2 deletions src/domain/wallet-invoices/index.types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,6 @@ type WIBWithAmount = {

type WalletInvoice = {
paymentHash: PaymentHash
secret: SecretPreImage
selfGenerated: boolean
pubkey: Pubkey
usdAmount?: UsdPaymentAmount
Expand Down Expand Up @@ -109,7 +108,11 @@ interface IWalletInvoicesRepository {
paymentHash: PaymentHash,
) => Promise<WalletInvoice | RepositoryError>

yieldPending: () => AsyncGenerator<WalletInvoice> | RepositoryError
findPendingByWalletId: (
walletId: WalletId,
) => AsyncGenerator<WalletInvoice> | RepositoryError

listWalletIdsWithPendingInvoices: () => AsyncGenerator<WalletId> | RepositoryError

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

Expand Down
Loading

0 comments on commit bbaec22

Please sign in to comment.