From df7671c5c98064a7f1673ba64d4545c6b9c7882c Mon Sep 17 00:00:00 2001 From: hatim boufnichel Date: Wed, 6 Mar 2024 20:36:20 +0100 Subject: [PATCH] fix double tx, add pending --- src/services/lnd/lnd.ts | 3 +-- src/services/main/index.ts | 24 ++++++++++++++++-------- src/services/main/paymentManager.ts | 8 ++++---- src/services/storage/paymentStorage.ts | 2 +- 4 files changed, 22 insertions(+), 15 deletions(-) diff --git a/src/services/lnd/lnd.ts b/src/services/lnd/lnd.ts index ee5a3352..f8d5c616 100644 --- a/src/services/lnd/lnd.ts +++ b/src/services/lnd/lnd.ts @@ -161,11 +161,10 @@ export default class { startHeight: this.latestKnownBlockHeigh, }, { abort: this.abortController.signal }) stream.responses.onMessage(tx => { - if (tx.blockHeight > this.latestKnownBlockHeigh) { this.latestKnownBlockHeigh = tx.blockHeight } - if (tx.numConfirmations > 0) { + if (tx.numConfirmations === 0) { // only process pending transactions, confirmed transaction are processed by the newBlock CB tx.outputDetails.forEach(output => { if (output.isOurAddress) { this.log("received chan TX", Number(output.amount), "sats") diff --git a/src/services/main/index.ts b/src/services/main/index.ts index 9d166e33..61a8d924 100644 --- a/src/services/main/index.ts +++ b/src/services/main/index.ts @@ -87,7 +87,7 @@ export default class { try { const balanceEvents = await this.paymentManager.GetLndBalance() await this.metricsManager.NewBlockCb(height, balanceEvents) - confirmed = await this.paymentManager.CheckPendingTransactions(height) + confirmed = await this.paymentManager.CheckNewlyConfirmedTxs(height) } catch (err: any) { log("failed to check transactions after new block", err.message || err) return @@ -96,12 +96,18 @@ export default class { if (c.type === 'outgoing') { await this.storage.paymentStorage.UpdateUserTransactionPayment(c.tx.serial_id, { confs: c.confs }) } else { - const { user_address: userAddress, paid_amount: amount, service_fee: serviceFee, serial_id: serialId } = c.tx - await this.storage.paymentStorage.UpdateAddressReceivingTransaction(serialId, { confs: c.confs }) - await this.storage.userStorage.IncrementUserBalance(userAddress.user.user_id, amount - serviceFee) - const operationId = `${Types.UserOperationType.INCOMING_TX}-${userAddress.serial_id}` - const op = { amount, paidAtUnix: Date.now() / 1000, inbound: true, type: Types.UserOperationType.INCOMING_TX, identifier: userAddress.address, operationId, network_fee: 0, service_fee: serviceFee, confirmed: true, tx_hash: c.tx.tx_hash, internal: c.tx.internal } - this.sendOperationToNostr(userAddress.linkedApplication!, userAddress.user.user_id, op) + this.storage.StartTransaction(async tx => { + const { user_address: userAddress, paid_amount: amount, service_fee: serviceFee, serial_id: serialId } = c.tx + const updateResult = await this.storage.paymentStorage.UpdateAddressReceivingTransaction(serialId, { confs: c.confs }, tx) + if (!updateResult.affected) { + throw new Error("unable to flag chain transaction as paid") + } + await this.storage.userStorage.IncrementUserBalance(userAddress.user.user_id, amount - serviceFee, tx) + const operationId = `${Types.UserOperationType.INCOMING_TX}-${userAddress.serial_id}` + const op = { amount, paidAtUnix: Date.now() / 1000, inbound: true, type: Types.UserOperationType.INCOMING_TX, identifier: userAddress.address, operationId, network_fee: 0, service_fee: serviceFee, confirmed: true, tx_hash: c.tx.tx_hash, internal: c.tx.internal } + this.sendOperationToNostr(userAddress.linkedApplication!, userAddress.user.user_id, op) + }) + } })) } @@ -125,7 +131,9 @@ export default class { try { // This call will fail if the transaction is already registered const addedTx = await this.storage.paymentStorage.AddAddressReceivingTransaction(userAddress, txOutput.hash, txOutput.index, amount, fee, internal, blockHeight, tx) - await this.storage.userStorage.IncrementUserBalance(userAddress.user.user_id, addedTx.paid_amount - fee, tx) + if (internal) { + await this.storage.userStorage.IncrementUserBalance(userAddress.user.user_id, addedTx.paid_amount - fee, tx) + } const operationId = `${Types.UserOperationType.INCOMING_TX}-${addedTx.serial_id}` const op = { amount, paidAtUnix: Date.now() / 1000, inbound: true, type: Types.UserOperationType.INCOMING_TX, identifier: userAddress.address, operationId, network_fee: 0, service_fee: fee, confirmed: internal, tx_hash: txOutput.hash, internal: false } this.sendOperationToNostr(userAddress.linkedApplication, userAddress.user.user_id, op) diff --git a/src/services/main/paymentManager.ts b/src/services/main/paymentManager.ts index 3bc32c3e..7448c9c3 100644 --- a/src/services/main/paymentManager.ts +++ b/src/services/main/paymentManager.ts @@ -495,7 +495,7 @@ export default class { return sentAmount } - async CheckPendingTransactions(height: number) { + async CheckNewlyConfirmedTxs(height: number) { const pending = await this.storage.paymentStorage.GetPendingTransactions() let lowestHeight = height const map: Record = {} @@ -507,7 +507,7 @@ export default class { pending.incoming.forEach(t => checkTx({ type: "incoming", tx: t })) pending.outgoing.forEach(t => checkTx({ type: "outgoing", tx: t })) const { transactions } = await this.lnd.GetTransactions(lowestHeight) - const resolved = await Promise.all(transactions.map(async tx => { + const newlyConfirmedTxs = transactions.map(tx => { const { txHash, numConfirmations: confs, amount: amt } = tx const t = map[txHash] if (!t || confs === 0) { @@ -516,8 +516,8 @@ export default class { if (confs > 2 || (amt <= confInTwo && confs > 1) || (amt <= confInOne && confs > 0)) { return { ...t, confs } } - })) - return resolved.filter(t => t !== undefined) as (PendingTx & { confs: number })[] + }) + return newlyConfirmedTxs.filter(t => t !== undefined) as (PendingTx & { confs: number })[] } async GetLndBalance() { diff --git a/src/services/storage/paymentStorage.ts b/src/services/storage/paymentStorage.ts index 07893213..c8005ec2 100644 --- a/src/services/storage/paymentStorage.ts +++ b/src/services/storage/paymentStorage.ts @@ -191,7 +191,7 @@ export default class { } async UpdateAddressReceivingTransaction(serialId: number, update: Partial, entityManager = this.DB) { - await entityManager.getRepository(AddressReceivingTransaction).update(serialId, update) + return entityManager.getRepository(AddressReceivingTransaction).update(serialId, update) } async UpdateUserTransactionPayment(serialId: number, update: Partial, entityManager = this.DB) { await entityManager.getRepository(UserTransactionPayment).update(serialId, update)