paid action limits (#1323)

This commit is contained in:
Keyan 2024-08-21 14:45:51 -05:00 committed by GitHub
parent 67d71ef0c8
commit df62cfb28c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 50 additions and 5 deletions

View File

@ -1,6 +1,6 @@
import { createHodlInvoice, createInvoice, parsePaymentRequest } from 'ln-service'
import { datePivot } from '@/lib/time'
import { USER_ID } from '@/lib/constants'
import { PAID_ACTION_TERMINAL_STATES, USER_ID } from '@/lib/constants'
import { createHmac } from '../resolvers/wallet'
import { Prisma } from '@prisma/client'
import * as ITEM_CREATE from './itemCreate'
@ -215,13 +215,30 @@ export async function retryPaidAction (actionType, args, context) {
}
const INVOICE_EXPIRE_SECS = 600
const MAX_PENDING_PAID_ACTIONS_PER_USER = 100
export async function createLightningInvoice (actionType, args, context) {
// if the action has an invoiceable peer, we'll create a peer invoice
// wrap it, and return the wrapped invoice
const { cost, models, lnd } = context
const { cost, models, lnd, me } = context
const userId = await paidActions[actionType]?.invoiceablePeer?.(args, context)
// count pending invoices and bail if we're over the limit
const pendingInvoices = await models.invoice.count({
where: {
userId: me?.id ?? USER_ID.anon,
actionState: {
// not in a terminal state. Note: null isn't counted by prisma
notIn: PAID_ACTION_TERMINAL_STATES
}
}
})
console.log('pending paid actions', pendingInvoices)
if (pendingInvoices >= MAX_PENDING_PAID_ACTIONS_PER_USER) {
throw new Error('You have too many pending paid actions, cancel some or wait for them to expire')
}
if (userId) {
try {
const description = await paidActions[actionType].describe(args, context)

View File

@ -3,6 +3,7 @@
export const DEFAULT_SUBS = ['bitcoin', 'nostr', 'tech', 'meta', 'jobs']
export const DEFAULT_SUBS_NO_JOBS = DEFAULT_SUBS.filter(s => s !== 'jobs')
export const PAID_ACTION_TERMINAL_STATES = ['FAILED', 'PAID', 'RETRYING']
export const NOFOLLOW_LIMIT = 1000
export const UNKNOWN_LINK_REL = 'noreferrer nofollow noopener'
export const BOOST_MULT = 5000

View File

@ -7,9 +7,11 @@ import { addWalletLog } from '@/api/resolvers/wallet'
import walletDefs from 'wallets/server'
import { parsePaymentRequest } from 'ln-service'
import { toPositiveNumber } from '@/lib/validate'
import { PAID_ACTION_TERMINAL_STATES } from '@/lib/constants'
export default [lnd, cln, lnAddr, lnbits, nwc]
const MAX_PENDING_INVOICES_PER_WALLET = 25
export async function createInvoice (userId, { msats, description, descriptionHash, expiry = 360 }, { models }) {
// get the wallets in order of priority
const wallets = await models.wallet.findMany({
@ -45,6 +47,31 @@ export async function createInvoice (userId, { msats, description, descriptionHa
throw new Error(`no ${walletType} wallet found`)
}
// check for pending withdrawals
const pendingWithdrawals = await models.withdrawl.count({
where: {
walletId: walletFull.id,
status: null
}
})
// and pending forwards
const pendingForwards = await models.invoiceForward.count({
where: {
walletId: walletFull.id,
invoice: {
actionState: {
notIn: PAID_ACTION_TERMINAL_STATES
}
}
}
})
console.log('pending invoices', pendingWithdrawals + pendingForwards)
if (pendingWithdrawals + pendingForwards >= MAX_PENDING_INVOICES_PER_WALLET) {
throw new Error('wallet has too many pending invoices')
}
const invoice = await createInvoice({
msats,
description: wallet.user.hideInvoiceDesc ? undefined : description,

View File

@ -1,6 +1,6 @@
import { getPaymentFailureStatus, hodlInvoiceCltvDetails } from '@/api/lnd'
import { paidActions } from '@/api/paidAction'
import { LND_PATHFINDING_TIMEOUT_MS } from '@/lib/constants'
import { LND_PATHFINDING_TIMEOUT_MS, PAID_ACTION_TERMINAL_STATES } from '@/lib/constants'
import { datePivot } from '@/lib/time'
import { toPositiveNumber } from '@/lib/validate'
import { Prisma } from '@prisma/client'
@ -21,7 +21,7 @@ async function transitionInvoice (jobName, { invoiceId, fromState, toState, tran
const currentDbInvoice = await models.invoice.findUnique({ where: { id: invoiceId } })
console.log('invoice is in state', currentDbInvoice.actionState)
if (['FAILED', 'PAID', 'RETRYING'].includes(currentDbInvoice.actionState)) {
if (PAID_ACTION_TERMINAL_STATES.includes(currentDbInvoice.actionState)) {
console.log('invoice is already in a terminal state, skipping transition')
return
}