Skip to content

Commit

Permalink
chore(core): update default expiration for BTC invoices (#4638)
Browse files Browse the repository at this point in the history
* chore(core): update default expiration for BTC invoices

* test: fix expected expiration with default values
  • Loading branch information
dolcalmi authored Oct 28, 2024
1 parent cae4aa5 commit ee75e92
Show file tree
Hide file tree
Showing 9 changed files with 43 additions and 30 deletions.
6 changes: 3 additions & 3 deletions core/api/src/app/lightning/get-held-invoices.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ErrorLevel, WalletCurrency } from "@/domain/shared"
import { DEFAULT_EXPIRATIONS } from "@/domain/bitcoin/lightning/invoice-expiration"
import { INVOICE_EXPIRATIONS } from "@/domain/bitcoin/lightning/invoice-expiration"

import { LndService } from "@/services/lnd"
import { recordExceptionInCurrentSpan } from "@/services/tracing"
Expand All @@ -8,8 +8,8 @@ export const getHeldInvoicesCount = async (): Promise<number | ApplicationError>
const offChainService = LndService()
if (offChainService instanceof Error) return offChainService

const { delay } = DEFAULT_EXPIRATIONS[WalletCurrency.Btc]
const createdAfter = new Date(new Date().getTime() - delay * 2 * 1000)
const { max } = INVOICE_EXPIRATIONS[WalletCurrency.Btc]
const createdAfter = new Date(new Date().getTime() - max * 2 * 1000)

const invoices = await Promise.all(
offChainService.listActivePubkeys().map(async (pubkey) => {
Expand Down
8 changes: 4 additions & 4 deletions core/api/src/app/wallets/add-invoice-for-wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import { AccountValidator } from "@/domain/accounts"
import { checkedToLedgerExternalId } from "@/domain/ledger"
import { checkedToWalletId } from "@/domain/wallets"
import { RateLimitConfig } from "@/domain/rate-limit"
import { checkedToMinutes } from "@/domain/primitives"
import { checkedToMinutes, secondsToMinutes } from "@/domain/primitives"
import { RateLimiterExceededError } from "@/domain/rate-limit/errors"
import { DEFAULT_EXPIRATIONS } from "@/domain/bitcoin/lightning/invoice-expiration"
import { INVOICE_EXPIRATIONS } from "@/domain/bitcoin/lightning/invoice-expiration"
import { WalletInvoiceBuilder } from "@/domain/wallet-invoices/wallet-invoice-builder"
import { checkedToBtcPaymentAmount, checkedToUsdPaymentAmount } from "@/domain/shared"

Expand All @@ -19,8 +19,8 @@ import {
WalletsRepository,
} from "@/services/mongoose"

const defaultBtcExpiration = DEFAULT_EXPIRATIONS["BTC"].delayMinutes
const defaultUsdExpiration = DEFAULT_EXPIRATIONS["USD"].delayMinutes
const defaultBtcExpiration = secondsToMinutes(INVOICE_EXPIRATIONS["BTC"].defaultValue)
const defaultUsdExpiration = secondsToMinutes(INVOICE_EXPIRATIONS["USD"].defaultValue)

const addInvoiceForSelf = async ({
walletId,
Expand Down
1 change: 1 addition & 0 deletions core/api/src/domain/bitcoin/lightning/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export { decodeInvoice } from "./ln-invoice"
export {
invoiceExpirationForCurrency,
defaultTimeToExpiryInSeconds,
INVOICE_EXPIRATIONS,
} from "./invoice-expiration"
export * from "./errors"

Expand Down
19 changes: 9 additions & 10 deletions core/api/src/domain/bitcoin/lightning/invoice-expiration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,14 @@ import { toSeconds } from "@/domain/primitives"

const SECS_PER_MIN = toSeconds(60)
const SECS_PER_5_MINS = toSeconds(60 * 5)
const SECS_PER_DAY = toSeconds(60 * 60 * 24)
const SECS_PER_HOUR = toSeconds(60 * 60)
const SECS_PER_DAY = toSeconds(SECS_PER_HOUR * 24)

export const defaultTimeToExpiryInSeconds = SECS_PER_5_MINS

export const DEFAULT_EXPIRATIONS = {
BTC: { delay: SECS_PER_DAY, delayMinutes: (SECS_PER_DAY / SECS_PER_MIN) as Minutes },
USD: {
delay: defaultTimeToExpiryInSeconds,
delayMinutes: (defaultTimeToExpiryInSeconds / SECS_PER_MIN) as Minutes,
},
export const INVOICE_EXPIRATIONS = {
BTC: { min: SECS_PER_MIN, max: SECS_PER_DAY, defaultValue: SECS_PER_HOUR },
USD: { min: SECS_PER_MIN, max: SECS_PER_5_MINS, defaultValue: SECS_PER_5_MINS },
}

export const invoiceExpirationForCurrency = (
Expand All @@ -20,9 +18,10 @@ export const invoiceExpirationForCurrency = (
delay?: Seconds,
): InvoiceExpiration => {
let expirationDelay = delay || toSeconds(0)
const { delay: defaultDelay } = DEFAULT_EXPIRATIONS[currency]
if (expirationDelay < SECS_PER_MIN || expirationDelay > defaultDelay) {
expirationDelay = defaultDelay
const { min, max, defaultValue } = INVOICE_EXPIRATIONS[currency]
const isValidExpiration = expirationDelay >= min && expirationDelay <= max
if (!isValidExpiration) {
expirationDelay = defaultValue
}

const expirationTimestamp = now.getTime() + expirationDelay * 1000
Expand Down
6 changes: 6 additions & 0 deletions core/api/src/domain/primitives/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
import { InvalidMinutesError, InvalidPaginatedQueryArgsError } from "@/domain/errors"

export const toSeconds = (seconds: number): Seconds => {
return seconds as Seconds
}

const SECS_PER_MIN = toSeconds(60)
export const secondsToMinutes = (seconds: number): Minutes => {
return (seconds / SECS_PER_MIN) as Minutes
}

export const toDays = (days: number): Days => {
return days as Days
}
Expand Down
6 changes: 3 additions & 3 deletions core/api/src/servers/trigger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ import { TxDecoder } from "@/domain/bitcoin/onchain"
import { CacheKeys } from "@/domain/cache"
import { CouldNotFindWalletFromOnChainAddressError } from "@/domain/errors"
import { checkedToDisplayCurrency } from "@/domain/fiat"
import { DEFAULT_EXPIRATIONS } from "@/domain/bitcoin/lightning/invoice-expiration"
import { INVOICE_EXPIRATIONS } from "@/domain/bitcoin/lightning/invoice-expiration"
import { ErrorLevel, paymentAmountFromNumber, WalletCurrency } from "@/domain/shared"

import { BriaSubscriber } from "@/services/bria"
Expand Down Expand Up @@ -284,8 +284,8 @@ const setupListenersForExistingHodlInvoices = async ({
const lndService = LndService()
if (lndService instanceof Error) return lndService

const { delay } = DEFAULT_EXPIRATIONS[WalletCurrency.Btc]
const createdAfter = new Date(new Date().getTime() - delay * 2 * 1000)
const { max } = INVOICE_EXPIRATIONS[WalletCurrency.Btc]
const createdAfter = new Date(new Date().getTime() - max * 2 * 1000)

const invoices = lndService.listInvoices({ pubkey, createdAfter })
if (invoices instanceof Error) return invoices
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { updatePendingInvoice } from "@/app/wallets/update-single-pending-invoic

import { toMilliSatsFromNumber, toSats } from "@/domain/bitcoin"
import { decodeInvoice, getSecretAndPaymentHash } from "@/domain/bitcoin/lightning"
import { DEFAULT_EXPIRATIONS } from "@/domain/bitcoin/lightning/invoice-expiration"
import { INVOICE_EXPIRATIONS } from "@/domain/bitcoin/lightning/invoice-expiration"
import { LedgerTransactionType } from "@/domain/ledger"
import { WalletCurrency } from "@/domain/shared"
import * as DisplayAmountsConverterImpl from "@/domain/fiat"
Expand Down Expand Up @@ -84,7 +84,7 @@ describe("update pending invoices", () => {
)
if (persisted instanceof Error) throw persisted

const usdDelayMs = DEFAULT_EXPIRATIONS.USD.delay * 1000
const usdDelayMs = INVOICE_EXPIRATIONS.USD.max * 1000
const timeBuffer = 1000 // buffer for any time library discrepancies
const pastCreatedAt = new Date(Date.now() - (usdDelayMs + timeBuffer))
await WalletInvoice.findOneAndUpdate(
Expand Down Expand Up @@ -143,7 +143,7 @@ describe("update pending invoices", () => {
)
if (persisted instanceof Error) throw persisted

const btcDelayMs = DEFAULT_EXPIRATIONS.BTC.delay * 1000
const btcDelayMs = INVOICE_EXPIRATIONS.BTC.max * 1000
const timeBuffer = 1000 // buffer for any time library discrepancies
const pastCreatedAt = new Date(Date.now() - (btcDelayMs + timeBuffer))
await WalletInvoice.findOneAndUpdate(
Expand Down Expand Up @@ -171,7 +171,7 @@ describe("update pending invoices", () => {
it("should be idempotent", async () => {
const invoiceAmount = toSats(1)
const { paymentHash } = getSecretAndPaymentHash()
const btcDelayMs = DEFAULT_EXPIRATIONS.BTC.delay * 1000
const btcDelayMs = INVOICE_EXPIRATIONS.BTC.max * 1000
const timeBuffer = 1000 // buffer for any time library discrepancies
const pastCreatedAt = new Date(Date.now() - (btcDelayMs + timeBuffer))

Expand Down Expand Up @@ -255,7 +255,7 @@ describe("update pending invoices", () => {
it("records transaction with ln-receive metadata on ln receive", async () => {
const invoiceAmount = toSats(1)
const { paymentHash } = getSecretAndPaymentHash()
const btcDelayMs = DEFAULT_EXPIRATIONS.BTC.delay * 1000
const btcDelayMs = INVOICE_EXPIRATIONS.BTC.max * 1000
const timeBuffer = 1000 // buffer for any time library discrepancies
const pastCreatedAt = new Date(Date.now() - (btcDelayMs + timeBuffer))

Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
import { SECS_PER_10_MINS, SECS_PER_DAY } from "@/config"

import {
invoiceExpirationForCurrency,
INVOICE_EXPIRATIONS,
} from "@/domain/bitcoin/lightning"
import { toSeconds } from "@/domain/primitives"
import { WalletCurrency } from "@/domain/shared"
import { invoiceExpirationForCurrency } from "@/domain/bitcoin/lightning"

describe("invoiceExpirationForCurrency", () => {
const BTC = WalletCurrency.Btc
const USD = WalletCurrency.Usd
const now = new Date("2000-01-01T00:00:00Z")

it("should return expiration for BTC currency with default delay", () => {
const expectedExpiration = new Date("2000-01-02T00:00:00.000Z")
const expectedExpiration = new Date(
now.getTime() + INVOICE_EXPIRATIONS.BTC.defaultValue * 1000,
)
let expiresAt = invoiceExpirationForCurrency(BTC, now)
expect(expiresAt).toEqual(expectedExpiration)

Expand All @@ -28,7 +33,9 @@ describe("invoiceExpirationForCurrency", () => {
})

it("should return expiration for USD currency with default delay", () => {
const expectedExpiration = new Date("2000-01-01T00:05:00.000Z")
const expectedExpiration = new Date(
now.getTime() + INVOICE_EXPIRATIONS.USD.defaultValue * 1000,
)
let expiresAt = invoiceExpirationForCurrency(USD, now)
expect(expiresAt).toEqual(expectedExpiration)

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { DEFAULT_EXPIRATIONS } from "@/domain/bitcoin/lightning/invoice-expiration"
import { INVOICE_EXPIRATIONS } from "@/domain/bitcoin/lightning/invoice-expiration"
import { CouldNotFindWalletInvoiceError, RepositoryError } from "@/domain/errors"
import { WalletCurrency } from "@/domain/shared"
import { WalletInvoiceChecker } from "@/domain/wallet-invoices"
Expand Down Expand Up @@ -36,7 +36,7 @@ describe("WalletInvoiceChecker", () => {
})

it("returns true for expired usd invoice", () => {
const usdDelayMs = DEFAULT_EXPIRATIONS.USD.delay * 1000
const usdDelayMs = INVOICE_EXPIRATIONS.USD.max * 1000
const timeBuffer = 1000 // buffer for any time library discrepancies
const pastCreatedAt = new Date(Date.now() - (usdDelayMs + timeBuffer))
expect(
Expand Down

0 comments on commit ee75e92

Please sign in to comment.