diff --git a/core/api/src/app/lightning/get-held-invoices.ts b/core/api/src/app/lightning/get-held-invoices.ts index ed83492a2f..f39c90d518 100644 --- a/core/api/src/app/lightning/get-held-invoices.ts +++ b/core/api/src/app/lightning/get-held-invoices.ts @@ -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" @@ -8,8 +8,8 @@ export const getHeldInvoicesCount = async (): Promise 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) => { diff --git a/core/api/src/app/wallets/add-invoice-for-wallet.ts b/core/api/src/app/wallets/add-invoice-for-wallet.ts index 78a41d0cc4..5ccafec267 100644 --- a/core/api/src/app/wallets/add-invoice-for-wallet.ts +++ b/core/api/src/app/wallets/add-invoice-for-wallet.ts @@ -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" @@ -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, diff --git a/core/api/src/domain/bitcoin/lightning/index.ts b/core/api/src/domain/bitcoin/lightning/index.ts index 7c435381a3..a24ef7752e 100644 --- a/core/api/src/domain/bitcoin/lightning/index.ts +++ b/core/api/src/domain/bitcoin/lightning/index.ts @@ -6,6 +6,7 @@ export { decodeInvoice } from "./ln-invoice" export { invoiceExpirationForCurrency, defaultTimeToExpiryInSeconds, + INVOICE_EXPIRATIONS, } from "./invoice-expiration" export * from "./errors" diff --git a/core/api/src/domain/bitcoin/lightning/invoice-expiration.ts b/core/api/src/domain/bitcoin/lightning/invoice-expiration.ts index d9de90a9e0..be805fd19b 100644 --- a/core/api/src/domain/bitcoin/lightning/invoice-expiration.ts +++ b/core/api/src/domain/bitcoin/lightning/invoice-expiration.ts @@ -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 = ( @@ -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 diff --git a/core/api/src/domain/primitives/index.ts b/core/api/src/domain/primitives/index.ts index 1918aceb14..21c3b9dbbe 100644 --- a/core/api/src/domain/primitives/index.ts +++ b/core/api/src/domain/primitives/index.ts @@ -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 } diff --git a/core/api/src/servers/trigger.ts b/core/api/src/servers/trigger.ts index 16352f3245..76fac9ff82 100644 --- a/core/api/src/servers/trigger.ts +++ b/core/api/src/servers/trigger.ts @@ -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" @@ -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 diff --git a/core/api/test/integration/app/wallets/update-pending-invoices.spec.ts b/core/api/test/integration/app/wallets/update-pending-invoices.spec.ts index 0f29fba01a..2109ecad5f 100644 --- a/core/api/test/integration/app/wallets/update-pending-invoices.spec.ts +++ b/core/api/test/integration/app/wallets/update-pending-invoices.spec.ts @@ -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" @@ -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( @@ -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( @@ -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)) @@ -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)) diff --git a/core/api/test/unit/domain/bitcoin/lightning/invoice-expiration.spec.ts b/core/api/test/unit/domain/bitcoin/lightning/invoice-expiration.spec.ts index 88b9a95842..9804f27c51 100644 --- a/core/api/test/unit/domain/bitcoin/lightning/invoice-expiration.spec.ts +++ b/core/api/test/unit/domain/bitcoin/lightning/invoice-expiration.spec.ts @@ -1,8 +1,11 @@ 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 @@ -10,7 +13,9 @@ describe("invoiceExpirationForCurrency", () => { 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) @@ -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) diff --git a/core/api/test/unit/domain/wallet-invoices/wallet-invoice-checker.spec.ts b/core/api/test/unit/domain/wallet-invoices/wallet-invoice-checker.spec.ts index ee1e85ab1c..a2d92e6753 100644 --- a/core/api/test/unit/domain/wallet-invoices/wallet-invoice-checker.spec.ts +++ b/core/api/test/unit/domain/wallet-invoices/wallet-invoice-checker.spec.ts @@ -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" @@ -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(