All Downloads are FREE. Search and download functionalities are using the official Maven repository.

commonMain.fr.acinq.lightning.payment.OutgoingPaymentFailure.kt Maven / Gradle / Ivy

There is a newer version: 1.8.4
Show newest version
package fr.acinq.lightning.payment

import fr.acinq.lightning.channel.ChannelException
import fr.acinq.lightning.db.LightningOutgoingPayment
import fr.acinq.lightning.utils.Either
import fr.acinq.lightning.utils.currentTimestampMillis
import fr.acinq.lightning.wire.*

/** A fatal failure that stops payment attempts. */
sealed class FinalFailure {

    /** Use this function when no payment attempts have been made (e.g. when a precondition failed). */
    fun toPaymentFailure(): OutgoingPaymentFailure = OutgoingPaymentFailure(this, listOf())

    // @formatter:off
    object AlreadyPaid : FinalFailure() { override fun toString(): String = "this invoice has already been paid" }
    object InvalidPaymentAmount : FinalFailure() { override fun toString(): String = "payment amount must be positive" }
    object FeaturesNotSupported : FinalFailure() { override fun toString(): String = "payment request features not supported" }
    object InvalidPaymentId : FinalFailure() { override fun toString(): String = "payment ID must be unique" }
    object NoAvailableChannels : FinalFailure() { override fun toString(): String = "no channels available to send payment" }
    object InsufficientBalance : FinalFailure() { override fun toString(): String = "not enough funds in wallet to afford payment" }
    object NoRouteToRecipient : FinalFailure() { override fun toString(): String = "unable to route payment to recipient" }
    object RecipientUnreachable : FinalFailure() { override fun toString(): String = "the recipient was offline or did not have enough liquidity to receive the payment" }
    object RetryExhausted: FinalFailure() { override fun toString(): String = "payment attempts exhausted without success" }
    object WalletRestarted: FinalFailure() { override fun toString(): String = "wallet restarted while a payment was ongoing" }
    object UnknownError : FinalFailure() { override fun toString(): String = "an unknown error occurred" }
    // @formatter:on
}

data class OutgoingPaymentFailure(val reason: FinalFailure, val failures: List) {
    constructor(reason: FinalFailure, failures: List>, completedAt: Long = currentTimestampMillis()) : this(reason, failures.map { convertFailure(it, completedAt) })

    /**
     * A detailed summary of the all internal errors.
     * This is targeted at users with technical knowledge of the lightning protocol.
     */
    fun details(): String = failures.foldIndexed("") { index, msg, problem -> msg + "${index + 1}: ${problem.details}\n" }

    companion object {
        fun convertFailure(failure: Either, completedAt: Long = currentTimestampMillis()): LightningOutgoingPayment.Part.Status.Failed = when (failure) {
            is Either.Left -> LightningOutgoingPayment.Part.Status.Failed(null, failure.value.details(), completedAt)
            is Either.Right -> LightningOutgoingPayment.Part.Status.Failed(failure.value.code, failure.value.message, completedAt)
        }

        fun isRouteError(failure: LightningOutgoingPayment.Part.Status.Failed) = when (failure.remoteFailureCode) {
            UnknownNextPeer.code -> true
            ChannelDisabled.code -> true
            TemporaryChannelFailure.code -> true
            PermanentChannelFailure.code -> true
            TemporaryNodeFailure.code -> true
            PermanentNodeFailure.code -> true
            else -> false
        }

        fun isRejectedByRecipient(failure: LightningOutgoingPayment.Part.Status.Failed) = when (failure.remoteFailureCode) {
            IncorrectOrUnknownPaymentDetails.code -> true
            else -> false
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy