Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(core): update default expiration for BTC invoices #4638

Merged
merged 2 commits into from
Oct 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading