From ff563c203cb4a2f4969233e2c714b5cd6b987aec Mon Sep 17 00:00:00 2001 From: pm47 Date: Tue, 14 Jan 2025 17:05:34 +0100 Subject: [PATCH] add a succeeded_at timestamp to payments This allows separating between a _completed_ payment and a _successful_ payment. Incoming payments are always successful when they are completed. Outgoing payments may be completed, but unsuccessful. For example a Lightning outgoing payment is completed when all attempts have been exhausted but not route was found. --- .../acinq/lightning/channel/ChannelAction.kt | 2 +- .../acinq/lightning/channel/states/Channel.kt | 2 +- .../fr/acinq/lightning/db/PaymentsDb.kt | 22 ++++++++++++++----- .../payment/v1/Deserialization.kt | 12 +++++----- .../serialization/payment/v1/Serialization.kt | 10 ++++----- .../fr/acinq/lightning/channel/TestsHelper.kt | 5 +++-- .../channel/states/ClosingTestsCommon.kt | 2 +- 7 files changed, 34 insertions(+), 21 deletions(-) diff --git a/modules/core/src/commonMain/kotlin/fr/acinq/lightning/channel/ChannelAction.kt b/modules/core/src/commonMain/kotlin/fr/acinq/lightning/channel/ChannelAction.kt index 2a4494e8d..2098ece37 100644 --- a/modules/core/src/commonMain/kotlin/fr/acinq/lightning/channel/ChannelAction.kt +++ b/modules/core/src/commonMain/kotlin/fr/acinq/lightning/channel/ChannelAction.kt @@ -6,7 +6,7 @@ import fr.acinq.lightning.MilliSatoshi import fr.acinq.lightning.NodeEvents import fr.acinq.lightning.blockchain.Watch import fr.acinq.lightning.channel.states.PersistedChannelState -import fr.acinq.lightning.db.ChannelClosingType +import fr.acinq.lightning.db.ChannelCloseOutgoingPayment.ChannelClosingType import fr.acinq.lightning.transactions.Transactions import fr.acinq.lightning.utils.UUID import fr.acinq.lightning.wire.* diff --git a/modules/core/src/commonMain/kotlin/fr/acinq/lightning/channel/states/Channel.kt b/modules/core/src/commonMain/kotlin/fr/acinq/lightning/channel/states/Channel.kt index 705edeea1..a7630aa19 100644 --- a/modules/core/src/commonMain/kotlin/fr/acinq/lightning/channel/states/Channel.kt +++ b/modules/core/src/commonMain/kotlin/fr/acinq/lightning/channel/states/Channel.kt @@ -15,7 +15,7 @@ import fr.acinq.lightning.channel.Helpers.Closing.claimRemoteCommitTxOutputs import fr.acinq.lightning.channel.Helpers.Closing.claimRevokedRemoteCommitTxOutputs import fr.acinq.lightning.channel.Helpers.Closing.getRemotePerCommitmentSecret import fr.acinq.lightning.crypto.KeyManager -import fr.acinq.lightning.db.ChannelClosingType +import fr.acinq.lightning.db.ChannelCloseOutgoingPayment.ChannelClosingType import fr.acinq.lightning.logging.* import fr.acinq.lightning.serialization.channel.Encryption.from import fr.acinq.lightning.transactions.Transactions.TransactionWithInputInfo.ClosingTx diff --git a/modules/core/src/commonMain/kotlin/fr/acinq/lightning/db/PaymentsDb.kt b/modules/core/src/commonMain/kotlin/fr/acinq/lightning/db/PaymentsDb.kt index aa28121b2..408b1fed4 100644 --- a/modules/core/src/commonMain/kotlin/fr/acinq/lightning/db/PaymentsDb.kt +++ b/modules/core/src/commonMain/kotlin/fr/acinq/lightning/db/PaymentsDb.kt @@ -91,9 +91,17 @@ sealed class WalletPayment { /** Absolute time in milliseconds since UNIX epoch when the payment was created. */ abstract val createdAt: Long - /** Absolute time in milliseconds since UNIX epoch when the payment was completed. May be null. */ + /** + * Absolute time in milliseconds since UNIX epoch when the payment was completed. + * A completed payment is not necessarily successful. For example a Lightning + * outgoing payment will be considered completed after all payment attempts + * have been exhausted. + */ abstract val completedAt: Long? + /** Absolute time in milliseconds since UNIX epoch when the payment succeeded. */ + abstract val succeededAt: Long? + /** Fees applied to complete this payment. */ abstract val fees: MilliSatoshi @@ -109,6 +117,8 @@ sealed class IncomingPayment : WalletPayment() { /** Amount received for this part after applying the fees. This is the final amount we can use. */ abstract val amountReceived: MilliSatoshi override val amount: MilliSatoshi get() = amountReceived + /** A completed incoming payment is always successful. */ + override val succeededAt: Long? get() = completedAt } /** @@ -321,6 +331,8 @@ data class LightningOutgoingPayment( override val completedAt: Long? = (status as? Status.Completed)?.completedAt + override val succeededAt: Long? = (status as? Status.Succeeded)?.completedAt + /** This is the total fees that have been paid to make the payment work. It includes the LN routing fees, the fee for the swap-out service, the mining fees for closing a channel. */ override val fees: MilliSatoshi = when (status) { is Status.Pending -> 0.msat @@ -460,6 +472,7 @@ sealed class OnChainOutgoingPayment : OutgoingPayment() { abstract val confirmedAt: Long? abstract val lockedAt: Long? override val completedAt: Long? get() = lockedAt + override val succeededAt: Long? get() = lockedAt /** Helper method to facilitate updating child classes */ fun setLocked(lockedAt: Long): OnChainOutgoingPayment = @@ -551,10 +564,6 @@ data class InboundLiquidityOutgoingPayment( } } -enum class ChannelClosingType { - Mutual, Local, Remote, Revoked, Other; -} - data class ChannelCloseOutgoingPayment( override val id: UUID, val recipientAmount: Satoshi, @@ -572,6 +581,9 @@ data class ChannelCloseOutgoingPayment( override val lockedAt: Long?, val closingType: ChannelClosingType ) : OnChainOutgoingPayment() { + enum class ChannelClosingType { + Mutual, Local, Remote, Revoked, Other; + } override val amount: MilliSatoshi = (recipientAmount + miningFees).toMilliSatoshi() override val fees: MilliSatoshi = miningFees.toMilliSatoshi() } \ No newline at end of file diff --git a/modules/core/src/commonMain/kotlin/fr/acinq/lightning/serialization/payment/v1/Deserialization.kt b/modules/core/src/commonMain/kotlin/fr/acinq/lightning/serialization/payment/v1/Deserialization.kt index fc4db8d02..db3564e38 100644 --- a/modules/core/src/commonMain/kotlin/fr/acinq/lightning/serialization/payment/v1/Deserialization.kt +++ b/modules/core/src/commonMain/kotlin/fr/acinq/lightning/serialization/payment/v1/Deserialization.kt @@ -303,12 +303,12 @@ object Deserialization { confirmedAt = readNullable { readNumber() }, lockedAt = readNullable { readNumber() }, closingType = when (val discriminator = read()) { - 0x00 -> ChannelClosingType.Mutual - 0x01 -> ChannelClosingType.Local - 0x02 -> ChannelClosingType.Remote - 0x03 -> ChannelClosingType.Revoked - 0x04 -> ChannelClosingType.Other - else -> error("unknown discriminator $discriminator for class ${ChannelClosingType::class}") + 0x00 -> ChannelCloseOutgoingPayment.ChannelClosingType.Mutual + 0x01 -> ChannelCloseOutgoingPayment.ChannelClosingType.Local + 0x02 -> ChannelCloseOutgoingPayment.ChannelClosingType.Remote + 0x03 -> ChannelCloseOutgoingPayment.ChannelClosingType.Revoked + 0x04 -> ChannelCloseOutgoingPayment.ChannelClosingType.Other + else -> error("unknown discriminator $discriminator for class ${ChannelCloseOutgoingPayment.ChannelClosingType::class}") } ) } \ No newline at end of file diff --git a/modules/core/src/commonMain/kotlin/fr/acinq/lightning/serialization/payment/v1/Serialization.kt b/modules/core/src/commonMain/kotlin/fr/acinq/lightning/serialization/payment/v1/Serialization.kt index 3faacfbeb..06dbc7fd6 100644 --- a/modules/core/src/commonMain/kotlin/fr/acinq/lightning/serialization/payment/v1/Serialization.kt +++ b/modules/core/src/commonMain/kotlin/fr/acinq/lightning/serialization/payment/v1/Serialization.kt @@ -354,11 +354,11 @@ object Serialization { writeNullable(confirmedAt) { writeNumber(it) } writeNullable(lockedAt) { writeNumber(it) } when (closingType) { - ChannelClosingType.Mutual -> write(0x00) - ChannelClosingType.Local -> write(0x01) - ChannelClosingType.Remote -> write(0x02) - ChannelClosingType.Revoked -> write(0x03) - ChannelClosingType.Other -> write(0x04) + ChannelCloseOutgoingPayment.ChannelClosingType.Mutual -> write(0x00) + ChannelCloseOutgoingPayment.ChannelClosingType.Local -> write(0x01) + ChannelCloseOutgoingPayment.ChannelClosingType.Remote -> write(0x02) + ChannelCloseOutgoingPayment.ChannelClosingType.Revoked -> write(0x03) + ChannelCloseOutgoingPayment.ChannelClosingType.Other -> write(0x04) } } } \ No newline at end of file diff --git a/modules/core/src/commonTest/kotlin/fr/acinq/lightning/channel/TestsHelper.kt b/modules/core/src/commonTest/kotlin/fr/acinq/lightning/channel/TestsHelper.kt index 649e0baf0..c798e3aae 100644 --- a/modules/core/src/commonTest/kotlin/fr/acinq/lightning/channel/TestsHelper.kt +++ b/modules/core/src/commonTest/kotlin/fr/acinq/lightning/channel/TestsHelper.kt @@ -9,7 +9,8 @@ import fr.acinq.lightning.blockchain.fee.FeeratePerKw import fr.acinq.lightning.blockchain.fee.OnChainFeerates import fr.acinq.lightning.channel.states.* import fr.acinq.lightning.crypto.KeyManager -import fr.acinq.lightning.db.ChannelClosingType +import fr.acinq.lightning.db.ChannelCloseOutgoingPayment +import fr.acinq.lightning.db.ChannelCloseOutgoingPayment.ChannelClosingType import fr.acinq.lightning.json.JsonSerializers import fr.acinq.lightning.logging.MDCLogger import fr.acinq.lightning.logging.mdc @@ -303,7 +304,7 @@ object TestsHelper { actions1.has() actions1.find().also { assertEquals(commitTx.txid, it.txId) - assertEquals(ChannelClosingType.Local, it.closingType) + assertEquals(ChannelCloseOutgoingPayment.ChannelClosingType.Local, it.closingType) } val localCommitPublished = s1.state.localCommitPublished diff --git a/modules/core/src/commonTest/kotlin/fr/acinq/lightning/channel/states/ClosingTestsCommon.kt b/modules/core/src/commonTest/kotlin/fr/acinq/lightning/channel/states/ClosingTestsCommon.kt index 08c3e527d..a2ac0fb54 100644 --- a/modules/core/src/commonTest/kotlin/fr/acinq/lightning/channel/states/ClosingTestsCommon.kt +++ b/modules/core/src/commonTest/kotlin/fr/acinq/lightning/channel/states/ClosingTestsCommon.kt @@ -23,7 +23,7 @@ import fr.acinq.lightning.channel.TestsHelper.mutualCloseBob import fr.acinq.lightning.channel.TestsHelper.reachNormal import fr.acinq.lightning.channel.TestsHelper.remoteClose import fr.acinq.lightning.channel.TestsHelper.useAlternativeCommitSig -import fr.acinq.lightning.db.ChannelClosingType +import fr.acinq.lightning.db.ChannelCloseOutgoingPayment.ChannelClosingType import fr.acinq.lightning.tests.TestConstants import fr.acinq.lightning.tests.utils.LightningTestSuite import fr.acinq.lightning.transactions.Scripts