Skip to content

Commit

Permalink
add a succeeded_at timestamp to payments
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
pm47 committed Jan 14, 2025
1 parent ba75224 commit ff563c2
Show file tree
Hide file tree
Showing 7 changed files with 34 additions and 21 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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.*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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
}

/**
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 =
Expand Down Expand Up @@ -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,
Expand All @@ -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()
}
Original file line number Diff line number Diff line change
Expand Up @@ -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}")
}
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -303,7 +304,7 @@ object TestsHelper {
actions1.has<ChannelAction.Storage.StoreState>()
actions1.find<ChannelAction.Storage.StoreOutgoingPayment.ViaClose>().also {
assertEquals(commitTx.txid, it.txId)
assertEquals(ChannelClosingType.Local, it.closingType)
assertEquals(ChannelCloseOutgoingPayment.ChannelClosingType.Local, it.closingType)
}

val localCommitPublished = s1.state.localCommitPublished
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit ff563c2

Please sign in to comment.