move invoice creation outside of interactive tx
This commit is contained in:
		
							parent
							
								
									6e8d7ef1b8
								
							
						
					
					
						commit
						0aa5ba4955
					
				| @ -130,9 +130,11 @@ async function performOptimisticAction (actionType, args, context) { | ||||
|   const { models } = context | ||||
|   const action = paidActions[actionType] | ||||
| 
 | ||||
|   context.optimistic = true | ||||
|   context.lndInvoice = await createLndInvoice(actionType, args, context) | ||||
| 
 | ||||
|   return await models.$transaction(async tx => { | ||||
|     context.tx = tx | ||||
|     context.optimistic = true | ||||
| 
 | ||||
|     const invoice = await createDbInvoice(actionType, args, context) | ||||
| 
 | ||||
| @ -160,15 +162,20 @@ async function performPessimisticAction (actionType, args, context) { | ||||
|       const invoice = await verifyPayment(context) | ||||
|       args.invoiceId = invoice.id | ||||
| 
 | ||||
|       // make sure to perform before settling so we don't race with worker to onPaid
 | ||||
|       const result = await action.perform(args, context) | ||||
| 
 | ||||
|       // XXX this might cause the interactive tx to time out
 | ||||
|       await settleHodlInvoice({ secret: invoice.preimage, lnd }) | ||||
| 
 | ||||
|       return { | ||||
|         result: await action.perform(args, context), | ||||
|         result, | ||||
|         paymentMethod: 'PESSIMISTIC' | ||||
|       } | ||||
|     }, { isolationLevel: Prisma.TransactionIsolationLevel.ReadCommitted }) | ||||
|   } else { | ||||
|     // just create the invoice and complete action when it's paid
 | ||||
|     context.lndInvoice = await createLndInvoice(actionType, args, context) | ||||
|     return { | ||||
|       invoice: await createDbInvoice(actionType, args, context), | ||||
|       paymentMethod: 'PESSIMISTIC' | ||||
| @ -201,13 +208,18 @@ export async function retryPaidAction (actionType, args, context) { | ||||
|     throw new Error(`retryPaidAction - missing invoiceId ${actionType}`) | ||||
|   } | ||||
| 
 | ||||
|   context.optimistic = true | ||||
|   context.user = await models.user.findUnique({ where: { id: me.id } }) | ||||
| 
 | ||||
|   const { msatsRequested } = await models.invoice.findUnique({ where: { id: invoiceId, actionState: 'FAILED' } }) | ||||
|   context.cost = BigInt(msatsRequested) | ||||
|   context.lndInvoice = await createLndInvoice(actionType, args, context) | ||||
| 
 | ||||
|   return await models.$transaction(async tx => { | ||||
|     context.tx = tx | ||||
|     context.optimistic = true | ||||
| 
 | ||||
|     // update the old invoice to RETRYING, so that it's not confused with FAILED
 | ||||
|     const { msatsRequested, actionId } = await tx.invoice.update({ | ||||
|     const { actionId } = await tx.invoice.update({ | ||||
|       where: { | ||||
|         id: invoiceId, | ||||
|         actionState: 'FAILED' | ||||
| @ -217,7 +229,6 @@ export async function retryPaidAction (actionType, args, context) { | ||||
|       } | ||||
|     }) | ||||
| 
 | ||||
|     context.cost = BigInt(msatsRequested) | ||||
|     context.actionId = actionId | ||||
| 
 | ||||
|     // create a new invoice
 | ||||
| @ -234,13 +245,14 @@ export async function retryPaidAction (actionType, args, context) { | ||||
| const OPTIMISTIC_INVOICE_EXPIRE = { minutes: 10 } | ||||
| const PESSIMISTIC_INVOICE_EXPIRE = { minutes: 10 } | ||||
| 
 | ||||
| async function createDbInvoice (actionType, args, context) { | ||||
|   const { user, models, tx, lnd, cost, optimistic, actionId } = context | ||||
| // we seperate the invoice creation into two functions because
 | ||||
| // because if lnd is slow, it'll timeout the interactive tx
 | ||||
| async function createLndInvoice (actionType, args, context) { | ||||
|   const { user, lnd, cost, optimistic } = context | ||||
|   const action = paidActions[actionType] | ||||
|   const db = tx ?? models | ||||
|   const [createLNDInvoice, expirePivot, actionState] = optimistic | ||||
|     ? [createInvoice, OPTIMISTIC_INVOICE_EXPIRE, 'PENDING'] | ||||
|     : [createHodlInvoice, PESSIMISTIC_INVOICE_EXPIRE, 'PENDING_HELD'] | ||||
|   const [createLNDInvoice, expirePivot] = optimistic | ||||
|     ? [createInvoice, OPTIMISTIC_INVOICE_EXPIRE] | ||||
|     : [createHodlInvoice, PESSIMISTIC_INVOICE_EXPIRE] | ||||
| 
 | ||||
|   if (cost < 1000n) { | ||||
|     // sanity check
 | ||||
| @ -248,19 +260,33 @@ async function createDbInvoice (actionType, args, context) { | ||||
|   } | ||||
| 
 | ||||
|   const expiresAt = datePivot(new Date(), expirePivot) | ||||
|   const lndInv = await createLNDInvoice({ | ||||
|   return await createLNDInvoice({ | ||||
|     description: user?.hideInvoiceDesc ? undefined : await action.describe(args, context), | ||||
|     lnd, | ||||
|     mtokens: String(cost), | ||||
|     expires_at: expiresAt | ||||
|   }) | ||||
| } | ||||
| 
 | ||||
| async function createDbInvoice (actionType, args, context) { | ||||
|   const { user, models, tx, lndInvoice, cost, optimistic, actionId } = context | ||||
|   const db = tx ?? models | ||||
|   const [expirePivot, actionState] = optimistic | ||||
|     ? [OPTIMISTIC_INVOICE_EXPIRE, 'PENDING'] | ||||
|     : [PESSIMISTIC_INVOICE_EXPIRE, 'PENDING_HELD'] | ||||
| 
 | ||||
|   if (cost < 1000n) { | ||||
|     // sanity check
 | ||||
|     throw new Error('The cost of the action must be at least 1 sat') | ||||
|   } | ||||
| 
 | ||||
|   const expiresAt = datePivot(new Date(), expirePivot) | ||||
|   const invoice = await db.invoice.create({ | ||||
|     data: { | ||||
|       hash: lndInv.id, | ||||
|       hash: lndInvoice.id, | ||||
|       msatsRequested: cost, | ||||
|       preimage: optimistic ? undefined : lndInv.secret, | ||||
|       bolt11: lndInv.request, | ||||
|       preimage: optimistic ? undefined : lndInvoice.secret, | ||||
|       bolt11: lndInvoice.request, | ||||
|       userId: user?.id || USER_ID.anon, | ||||
|       actionType, | ||||
|       actionState, | ||||
| @ -273,7 +299,7 @@ async function createDbInvoice (actionType, args, context) { | ||||
|   await db.$executeRaw` | ||||
|       INSERT INTO pgboss.job (name, data, retrylimit, retrybackoff, startafter, expirein, priority) | ||||
|       VALUES ('checkInvoice', | ||||
|         jsonb_build_object('hash', ${lndInv.id}::TEXT), 21, true, | ||||
|         jsonb_build_object('hash', ${lndInvoice.id}::TEXT), 21, true, | ||||
|           ${expiresAt}::TIMESTAMP WITH TIME ZONE, | ||||
|           ${expiresAt}::TIMESTAMP WITH TIME ZONE - now() + interval '10m', 100)` | ||||
| 
 | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user