Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle new outgoing payment failures #563

Merged
merged 6 commits into from
May 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,12 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import fr.acinq.bitcoin.ByteVector32
import fr.acinq.bitcoin.TxId
import fr.acinq.bitcoin.utils.Either
import fr.acinq.lightning.blockchain.electrum.ElectrumConnectionStatus
import fr.acinq.lightning.blockchain.electrum.getConfirmations
import fr.acinq.lightning.db.*
import fr.acinq.lightning.payment.FinalFailure
import fr.acinq.lightning.payment.OutgoingPaymentFailure
import fr.acinq.lightning.utils.msat
import fr.acinq.lightning.utils.sum
import fr.acinq.lightning.wire.LiquidityAds
Expand Down Expand Up @@ -119,8 +121,8 @@ fun PaymentDetailsSplashView(
}

if (payment is LightningOutgoingPayment) {
(payment.status as? LightningOutgoingPayment.Status.Completed.Failed)?.let {
PaymentErrorView(status = it)
(payment.status as? LightningOutgoingPayment.Status.Completed.Failed)?.let { status ->
PaymentErrorView(status = status, failedParts = payment.parts.map { it.status }.filterIsInstance<LightningOutgoingPayment.Part.Status.Failed>())
}
}

Expand Down Expand Up @@ -594,8 +596,9 @@ private fun InboundLiquidityLeaseDetails(lease: LiquidityAds.Lease) {
}

@Composable
private fun PaymentErrorView(status: LightningOutgoingPayment.Status.Completed.Failed) {
translatePaymentError(failure = status.reason)?.let {
private fun PaymentErrorView(status: LightningOutgoingPayment.Status.Completed.Failed, failedParts: List<LightningOutgoingPayment.Part.Status.Failed>) {
val failure = remember(status, failedParts) { OutgoingPaymentFailure(status.reason, failedParts) }
translatePaymentError(failure).let {
Spacer(modifier = Modifier.height(8.dp))
SplashLabelRow(label = stringResource(id = R.string.paymentdetails_error_label)) {
Text(text = it)
Expand Down Expand Up @@ -668,9 +671,7 @@ private fun ConfirmationView(

suspend fun getConfirmations(): Int {
val confirmations = electrumClient.getConfirmations(txId)
// log.debug { "retrieved confirmations=$confirmations from electrum for tx=$txId" }
return confirmations ?: run {
// log.debug { "retrying getConfirmations from electrum in 5 sec" }
delay(5_000)
getConfirmations()
}
Expand Down Expand Up @@ -749,20 +750,41 @@ private fun BumpTransactionDialog(
}

@Composable
private fun translatePaymentError(failure: FinalFailure): String? {
return when (failure) {
FinalFailure.RetryExhausted -> stringResource(id = R.string.outgoing_finalfailure_noroutefound)
FinalFailure.NoRouteToRecipient -> stringResource(id = R.string.outgoing_finalfailure_noroutefound)
FinalFailure.RecipientUnreachable -> stringResource(id = R.string.outgoing_finalfailure_noroutefound)

FinalFailure.AlreadyPaid -> stringResource(id = R.string.outgoing_finalfailure_alreadypaid)

FinalFailure.NoAvailableChannels,
FinalFailure.InsufficientBalance,
FinalFailure.FeaturesNotSupported,
FinalFailure.InvalidPaymentAmount,
FinalFailure.InvalidPaymentId,
FinalFailure.UnknownError,
FinalFailure.WalletRestarted -> null
private fun translatePaymentError(paymentFailure: OutgoingPaymentFailure): String {
return when (val result = paymentFailure.explain()) {
is Either.Left -> {
when (val partFailure = result.value) {
is LightningOutgoingPayment.Part.Status.Failure.Uninterpretable -> partFailure.message
LightningOutgoingPayment.Part.Status.Failure.ChannelIsClosing -> stringResource(id = R.string.outgoing_failuremessage_channel_closing)
LightningOutgoingPayment.Part.Status.Failure.ChannelIsSplicing -> stringResource(id = R.string.outgoing_failuremessage_channel_splicing)
LightningOutgoingPayment.Part.Status.Failure.NotEnoughFees -> stringResource(id = R.string.outgoing_failuremessage_not_enough_fee)
LightningOutgoingPayment.Part.Status.Failure.NotEnoughFunds -> stringResource(id = R.string.outgoing_failuremessage_not_enough_balance)
LightningOutgoingPayment.Part.Status.Failure.PaymentAmountTooBig -> stringResource(id = R.string.outgoing_failuremessage_too_big)
LightningOutgoingPayment.Part.Status.Failure.PaymentAmountTooSmall -> stringResource(id = R.string.outgoing_failuremessage_too_small)
LightningOutgoingPayment.Part.Status.Failure.PaymentExpiryTooBig -> stringResource(id = R.string.outgoing_failuremessage_expiry_too_big)
LightningOutgoingPayment.Part.Status.Failure.RecipientRejectedPayment -> stringResource(id = R.string.outgoing_failuremessage_rejected_by_recipient)
LightningOutgoingPayment.Part.Status.Failure.RecipientIsOffline -> stringResource(id = R.string.outgoing_failuremessage_recipient_offline)
LightningOutgoingPayment.Part.Status.Failure.RecipientLiquidityIssue -> stringResource(id = R.string.outgoing_failuremessage_not_enough_liquidity)
LightningOutgoingPayment.Part.Status.Failure.TemporaryRemoteFailure -> stringResource(id = R.string.outgoing_failuremessage_temporary_failure)
LightningOutgoingPayment.Part.Status.Failure.TooManyPendingPayments -> stringResource(id = R.string.outgoing_failuremessage_too_many_pending)
}
}
is Either.Right -> {
when (result.value) {
FinalFailure.InvalidPaymentId -> stringResource(id = R.string.outgoing_failuremessage_invalid_id)
FinalFailure.AlreadyPaid -> stringResource(id = R.string.outgoing_failuremessage_alreadypaid)
FinalFailure.ChannelClosing -> stringResource(id = R.string.outgoing_failuremessage_channel_closing)
FinalFailure.ChannelNotConnected -> stringResource(id = R.string.outgoing_failuremessage_not_connected)
FinalFailure.ChannelOpening -> stringResource(id = R.string.outgoing_failuremessage_channel_opening)
FinalFailure.FeaturesNotSupported -> stringResource(id = R.string.outgoing_failuremessage_unsupported_features)
FinalFailure.InsufficientBalance -> stringResource(id = R.string.outgoing_failuremessage_not_enough_balance)
FinalFailure.InvalidPaymentAmount -> stringResource(id = R.string.outgoing_failuremessage_invalid_amount)
FinalFailure.NoAvailableChannels -> stringResource(id = R.string.outgoing_failuremessage_no_available_channels)
FinalFailure.RecipientUnreachable -> stringResource(id = R.string.outgoing_failuremessage_noroutefound)
FinalFailure.RetryExhausted -> stringResource(id = R.string.outgoing_failuremessage_noroutefound)
FinalFailure.UnknownError -> stringResource(id = R.string.outgoing_failuremessage_unknown)
FinalFailure.WalletRestarted -> stringResource(id = R.string.outgoing_failuremessage_restarted)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -417,8 +417,9 @@ object LegacyMigrationHelper {
amount = part.amount().toLong().msat + 0.msat, // must include the fee!!!
route = listOf(),
status = LightningOutgoingPayment.Part.Status.Failed(
remoteFailureCode = null,
details = JavaConverters.asJavaCollectionConverter(partStatus.failures()).asJavaCollection().toList().lastOrNull()?.failureMessage() ?: "error details unavailable",
failure = LightningOutgoingPayment.Part.Status.Failure.Uninterpretable(
message = JavaConverters.asJavaCollectionConverter(partStatus.failures()).asJavaCollection().toList().lastOrNull()?.failureMessage() ?: "error details unavailable",
),
completedAt = partStatus.completedAt()
),
createdAt = part.createdAt()
Expand Down Expand Up @@ -463,11 +464,11 @@ object LegacyMigrationHelper {
val lastFailure = JavaConverters.asJavaCollectionConverter((it.last().status() as OutgoingPaymentStatus.Failed).failures()).asJavaCollection().toList().lastOrNull()
when {
lastFailure == null -> FinalFailure.UnknownError
lastFailure.failureMessage().contains(TrampolineFeeInsufficient.message(), ignoreCase = true) -> FinalFailure.NoRouteToRecipient
lastFailure.failureMessage().contains(TrampolineFeeInsufficient.message(), ignoreCase = true) -> FinalFailure.RecipientUnreachable
lastFailure.failureMessage().contains(TemporaryNodeFailure.message(), ignoreCase = true)
|| lastFailure.failureMessage().contains(UnknownNextPeer.message(), ignoreCase = true)
|| lastFailure.failureMessage().contains("is currently unavailable", ignoreCase = true) -> FinalFailure.RecipientUnreachable
lastFailure.failureMessage().contains("incorrect payment details or unknown payment hash", ignoreCase = true) -> FinalFailure.NoRouteToRecipient
lastFailure.failureMessage().contains("incorrect payment details or unknown payment hash", ignoreCase = true) -> FinalFailure.RecipientUnreachable
else -> FinalFailure.UnknownError
} to (it.last().status() as OutgoingPaymentStatus.Failed).completedAt()
}
Expand Down
26 changes: 24 additions & 2 deletions phoenix-android/src/main/res/values-b+es+419/important_strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -357,8 +357,30 @@

<!-- Lightning payment errors -->

<string name="outgoing_finalfailure_alreadypaid">Esta factura ya ha sido pagada.</string>
<string name="outgoing_finalfailure_noroutefound">El destinatario no está localizable o no tiene suficiente liquidez entrante.</string>
<string name="outgoing_failuremessage_channel_closing">Los canales se están cerrando.</string>
<string name="outgoing_failuremessage_channel_splicing">Los canales ya están procesando un splice. Inténtelo de nuevo más tarde.</string>
<string name="outgoing_failuremessage_not_enough_fee">La tarifa es insuficiente.</string>
<string name="outgoing_failuremessage_not_enough_balance">Este pago supera su saldo.</string>
<string name="outgoing_failuremessage_too_big">El importe del pago es demasiado grande - intente dividirlo en varias partes.</string>
<string name="outgoing_failuremessage_too_small">El importe del pago es demasiado pequeño.</string>
<string name="outgoing_failuremessage_expiry_too_big">El vencimiento de este pago es demasiado lejano.</string>
<string name="outgoing_failuremessage_rejected_by_recipient">El pago ha sido rechazado por el destinatario. Es posible que esta factura en concreto ya se haya pagado.</string>
<string name="outgoing_failuremessage_recipient_offline">El destinatario no está conectado.</string>
<string name="outgoing_failuremessage_not_enough_liquidity">El pago no se ha podido retransmitir al destinatario (probablemente liquidez entrante insuficiente).</string>
<string name="outgoing_failuremessage_temporary_failure">Se ha producido un error en un nodo de la ruta de pago. El pago puede tener éxito si lo intentas de nuevo.</string>
<string name="outgoing_failuremessage_too_many_pending">Tienes demasiados pagos pendientes. Vuelva a intentarlo una vez que se hayan liquidado.</string>

<string name="outgoing_failuremessage_invalid_id">El ID del pago no es válido. Inténtelo de nuevo.</string>
<string name="outgoing_failuremessage_not_connected">Tu canal aún no está conectado. Espere a tener una conexión estable e inténtelo de nuevo.</string>
<string name="outgoing_failuremessage_channel_opening">Tu canal aún está en proceso de apertura. Espere e inténtelo de nuevo.</string>
<string name="outgoing_failuremessage_unsupported_features">Esta factura utiliza funciones no compatibles. Asegúrate de que estás en la última versión de Phoenix.</string>
<string name="outgoing_failuremessage_invalid_amount">El importe del pago no es válido.</string>
<string name="outgoing_failuremessage_no_available_channels">El pago no se ha podido enviar a través de sus canales existentes.</string>
<string name="outgoing_failuremessage_unknown">Se ha producido un error desconocido y el pago ha fallado.</string>
<string name="outgoing_failuremessage_restarted">La cartera se ha reiniciado mientras se procesaba el pago.</string>

<string name="outgoing_failuremessage_alreadypaid">Esta factura ya ha sido pagada.</string>
<string name="outgoing_failuremessage_noroutefound">El destinatario no está localizable o no tiene suficiente liquidez entrante.</string>

<!-- low feerate disclaimer -->

Expand Down
26 changes: 24 additions & 2 deletions phoenix-android/src/main/res/values-cs/important_strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -368,8 +368,30 @@

<!-- Lightning payment errors -->

<string name="outgoing_finalfailure_alreadypaid">Tato platba již byla uhrazena.</string>
<string name="outgoing_finalfailure_noroutefound">Příjemce není dosažitelný nebo nemá dostatečnou příchozí likviditu.</string>
<string name="outgoing_failuremessage_channel_closing">Kanály se uzavírají.</string>
<string name="outgoing_failuremessage_channel_splicing">Kanály již zpracovávají splicing. Zkuste to později.</string>
<string name="outgoing_failuremessage_not_enough_fee">Poplatek je nedostatečný.</string>
<string name="outgoing_failuremessage_not_enough_balance">Tato platba přesahuje váš zůstatek.</string>
<string name="outgoing_failuremessage_too_big">Suma platby je příliš velká - zkuste ji rozdělit na více částí.</string>
<string name="outgoing_failuremessage_too_small">Suma platby je příliš malá.</string>
<string name="outgoing_failuremessage_expiry_too_big">Ukončení platnosti této platby je příliš daleko v budoucnosti.</string>
<string name="outgoing_failuremessage_rejected_by_recipient">Příjemce platbu odmítl. Tato konkrétní faktura již mohla být uhrazena.</string>
<string name="outgoing_failuremessage_recipient_offline">Příjemce je offline.</string>
<string name="outgoing_failuremessage_not_enough_liquidity">Platbu nebylo možné předat příjemci (pravděpodobně nedostatečná příchozí likvidita).</string>
<string name="outgoing_failuremessage_temporary_failure">Na uzlu v platební trase došlo k chybě. Platba může být úspěšná, pokud se o ni pokusíte znovu.</string>
<string name="outgoing_failuremessage_too_many_pending">Máte příliš mnoho čekajících plateb. Zkuste to znovu, jakmile budou vypořádány.</string>

<string name="outgoing_failuremessage_invalid_id">Identifikátor platby není platný. Zkuste to znovu.</string>
<string name="outgoing_failuremessage_not_connected">Váš kanál ještě není připojen. Počkejte na stabilní připojení a zkuste to znovu.</string>
<string name="outgoing_failuremessage_channel_opening">Váš kanál je stále v procesu otevírání. Počkejte a zkuste to znovu.</string>
<string name="outgoing_failuremessage_unsupported_features">Tato faktura používá nepodporované funkce. Ujistěte se, že používáte nejnovější verzi systému Phoenix.</string>
<string name="outgoing_failuremessage_invalid_amount">Částka platby je neplatná.</string>
<string name="outgoing_failuremessage_no_available_channels">Platba nemohla být odeslána prostřednictvím vašich stávajících kanálů.</string>
<string name="outgoing_failuremessage_unknown">Došlo k neznámé chybě a platba se nezdařila.</string>
<string name="outgoing_failuremessage_restarted">Peněženka byla restartována během zpracování platby.</string>

<string name="outgoing_failuremessage_alreadypaid">Tato platba již byla uhrazena.</string>
<string name="outgoing_failuremessage_noroutefound">Příjemce není dosažitelný nebo nemá dostatečnou příchozí likviditu.</string>

<!-- low feerate disclaimer -->

Expand Down
26 changes: 24 additions & 2 deletions phoenix-android/src/main/res/values-de/important_strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -366,8 +366,30 @@

<!-- Lightning payment errors -->

<string name="outgoing_finalfailure_alreadypaid">Diese Zahlung wurde bereits getätigt.</string>
<string name="outgoing_finalfailure_noroutefound">Der Empfänger ist nicht erreichbar oder hat nicht genügend Liquidität im Eingang.</string>
<string name="outgoing_failuremessage_channel_closing">Kanäle werden geschlossen.</string>
<string name="outgoing_failuremessage_channel_splicing">Kanäle verarbeiten bereits einen Splice. Versuchen Sie es später noch einmal.</string>
<string name="outgoing_failuremessage_not_enough_fee">Gebühr ist nicht ausreichend.</string>
<string name="outgoing_failuremessage_not_enough_balance">Diese Zahlung übersteigt Ihr Guthaben.</string>
<string name="outgoing_failuremessage_too_big">Der Zahlungsbetrag ist zu groß - versuchen Sie, ihn in mehrere Teile aufzuteilen.</string>
<string name="outgoing_failuremessage_too_small">Der Zahlungsbetrag ist zu klein.</string>
<string name="outgoing_failuremessage_expiry_too_big">Der Verfall dieser Zahlung liegt zu weit in der Zukunft.</string>
<string name="outgoing_failuremessage_rejected_by_recipient">Die Zahlung wurde vom Empfänger abgelehnt. Möglicherweise wurde diese Rechnung bereits bezahlt.</string>
<string name="outgoing_failuremessage_recipient_offline">Der Empfänger ist offline.</string>
<string name="outgoing_failuremessage_not_enough_liquidity">Die Zahlung konnte nicht an den Empfänger weitergeleitet werden (wahrscheinlich unzureichende eingehende Liquidität).</string>
<string name="outgoing_failuremessage_temporary_failure">Ein Fehler ist an einem Knoten in der Zahlungsroute aufgetreten. Die Zahlung kann erfolgreich sein, wenn Sie es erneut versuchen.</string>
<string name="outgoing_failuremessage_too_many_pending">Sie haben zu viele ausstehende Zahlungen. Versuchen Sie es erneut, sobald sie abgewickelt sind.</string>

<string name="outgoing_failuremessage_alreadypaid">Diese Zahlung wurde bereits getätigt.</string>
<string name="outgoing_failuremessage_noroutefound">Der Empfänger ist nicht erreichbar oder hat nicht genügend Liquidität im Eingang.</string>

<string name="outgoing_failuremessage_invalid_id">Die ID der Zahlung ist nicht gültig. Versuchen Sie es erneut.</string>
<string name="outgoing_failuremessage_not_connected">Ihr Kanal ist noch nicht verbunden. Warte auf eine stabile Verbindung und versuche es erneut.</string>
<string name="outgoing_failuremessage_channel_opening">Ihr Kanal wird noch geöffnet. Warten Sie und versuchen Sie es erneut.</string>
<string name="outgoing_failuremessage_unsupported_features">Diese Rechnung verwendet nicht unterstützte Funktionen. Stellen Sie sicher, dass Sie die neueste Phoenix-Version verwenden.</string>
<string name="outgoing_failuremessage_invalid_amount">Der Zahlungsbetrag ist ungültig.</string>
<string name="outgoing_failuremessage_no_available_channels">Die Zahlung konnte nicht über Ihre vorhandenen Kanäle gesendet werden.</string>
<string name="outgoing_failuremessage_unknown">Ein unbekannter Fehler ist aufgetreten und die Zahlung ist fehlgeschlagen.</string>
<string name="outgoing_failuremessage_restarted">Die Brieftasche wurde neu gestartet, während die Zahlung bearbeitet wurde.</string>

<!-- low feerate disclaimer -->

Expand Down
Loading