From 6e3011d6abeb0faec2217590103cc0ef5fd0b18b Mon Sep 17 00:00:00 2001
From: Dominique Padiou <5765435+dpad85@users.noreply.github.com>
Date: Tue, 30 Apr 2024 18:58:21 +0200
Subject: [PATCH] Handle new database types for offer payments
---
buildSrc/src/main/kotlin/Versions.kt | 2 +-
.../details/PaymentDetailsTechnicalView.kt | 66 +++++++++++++++----
.../android/utils/LegacyMigrationHelper.kt | 2 +-
.../acinq/phoenix/android/utils/extensions.kt | 3 +-
.../src/main/res/values/strings.xml | 5 ++
phoenix-shared/build.gradle.kts | 4 ++
.../controllers/payments/ScanController.kt | 6 +-
.../db/payments/IncomingOriginType.kt | 15 +++++
.../db/payments/OutgoingDetailsType.kt | 36 ++++++----
.../fr.acinq.phoenix/utils/CsvWriter.kt | 5 +-
10 files changed, 112 insertions(+), 32 deletions(-)
diff --git a/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/src/main/kotlin/Versions.kt
index 3cf028a18..039c6e3e3 100644
--- a/buildSrc/src/main/kotlin/Versions.kt
+++ b/buildSrc/src/main/kotlin/Versions.kt
@@ -1,5 +1,5 @@
object Versions {
- const val lightningKmp = "1.6.1"
+ const val lightningKmp = "1.6.2-SNAPSHOT"
const val secp256k1 = "0.14.0"
const val torMobile = "0.2.0"
diff --git a/phoenix-android/src/main/kotlin/fr/acinq/phoenix/android/payments/details/PaymentDetailsTechnicalView.kt b/phoenix-android/src/main/kotlin/fr/acinq/phoenix/android/payments/details/PaymentDetailsTechnicalView.kt
index 65f56d99f..b75917266 100644
--- a/phoenix-android/src/main/kotlin/fr/acinq/phoenix/android/payments/details/PaymentDetailsTechnicalView.kt
+++ b/phoenix-android/src/main/kotlin/fr/acinq/phoenix/android/payments/details/PaymentDetailsTechnicalView.kt
@@ -28,10 +28,12 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import fr.acinq.bitcoin.ByteVector32
+import fr.acinq.bitcoin.PrivateKey
import fr.acinq.lightning.MilliSatoshi
import fr.acinq.lightning.db.*
import fr.acinq.lightning.payment.Bolt11Invoice
import fr.acinq.lightning.payment.Bolt12Invoice
+import fr.acinq.lightning.payment.OfferPaymentMetadata
import fr.acinq.lightning.utils.currentTimestampMillis
import fr.acinq.lightning.utils.msat
import fr.acinq.lightning.utils.sum
@@ -133,7 +135,7 @@ private fun HeaderForOutgoing(
is LightningOutgoingPayment -> when (payment.details) {
is LightningOutgoingPayment.Details.Normal -> stringResource(R.string.paymentdetails_normal_outgoing)
is LightningOutgoingPayment.Details.SwapOut -> stringResource(R.string.paymentdetails_swapout)
- is LightningOutgoingPayment.Details.KeySend -> stringResource(R.string.paymentdetails_keysend)
+ is LightningOutgoingPayment.Details.Blinded -> stringResource(id = R.string.paymentdetails_offer_outgoing)
}
is SpliceCpfpOutgoingPayment -> stringResource(id = R.string.paymentdetails_splice_cpfp_outgoing)
is InboundLiquidityOutgoingPayment -> stringResource(id = R.string.paymentdetails_inbound_liquidity)
@@ -175,6 +177,7 @@ private fun HeaderForIncoming(
is IncomingPayment.Origin.KeySend -> stringResource(R.string.paymentdetails_keysend)
is IncomingPayment.Origin.SwapIn -> stringResource(R.string.paymentdetails_swapin)
is IncomingPayment.Origin.OnChain -> stringResource(R.string.paymentdetails_swapin)
+ is IncomingPayment.Origin.Offer -> stringResource(id = R.string.paymentdetails_offer_incoming)
}
)
}
@@ -300,19 +303,14 @@ private fun DetailsForLightningOutgoingPayment(
// -- details of the payment
when (details) {
is LightningOutgoingPayment.Details.Normal -> {
- when (val paymentRequest = details.paymentRequest) {
- is Bolt11Invoice -> InvoiceSection(invoice = paymentRequest)
- is Bolt12Invoice -> {
- // TODO
- }
- }
+ Bolt11InvoiceSection(invoice = details.paymentRequest)
}
is LightningOutgoingPayment.Details.SwapOut -> {
TechnicalRowSelectable(label = stringResource(id = R.string.paymentdetails_bitcoin_address_label), value = details.address)
TechnicalRowSelectable(label = stringResource(id = R.string.paymentdetails_payment_hash_label), value = details.paymentHash.toHex())
}
- is LightningOutgoingPayment.Details.KeySend -> {
- TechnicalRowSelectable(label = stringResource(id = R.string.paymentdetails_payment_hash_label), value = details.paymentHash.toHex())
+ is LightningOutgoingPayment.Details.Blinded -> {
+ Bolt12InvoiceSection(invoice = details.paymentRequest, payerKey = details.payerKey)
}
}
@@ -408,7 +406,7 @@ private fun DetailsForIncoming(
// -- details about the origin of the payment
when (val origin = payment.origin) {
is IncomingPayment.Origin.Invoice -> {
- InvoiceSection(invoice = origin.paymentRequest)
+ Bolt11InvoiceSection(invoice = origin.paymentRequest)
TechnicalRowSelectable(label = stringResource(id = R.string.paymentdetails_preimage_label), value = payment.preimage.toHex())
}
is IncomingPayment.Origin.SwapIn -> {
@@ -428,6 +426,9 @@ private fun DetailsForIncoming(
}
}
}
+ is IncomingPayment.Origin.Offer -> {
+ Bolt12MetadataSection(metadata = origin.metadata)
+ }
}
}
@@ -507,7 +508,7 @@ private fun LightningPart(
}
@Composable
-private fun InvoiceSection(
+private fun Bolt11InvoiceSection(
invoice: Bolt11Invoice
) {
val requestedAmount = invoice.amount
@@ -530,6 +531,49 @@ private fun InvoiceSection(
TechnicalRowSelectable(label = stringResource(id = R.string.paymentdetails_payment_request_label), value = invoice.write())
}
+@Composable
+private fun Bolt12InvoiceSection(
+ invoice: Bolt12Invoice,
+ payerKey: PrivateKey,
+) {
+ val requestedAmount = invoice.amount
+ if (requestedAmount != null) {
+ TechnicalRowAmount(
+ label = stringResource(id = R.string.paymentdetails_invoice_requested_label),
+ amount = requestedAmount,
+ rateThen = null
+ )
+ }
+
+ val description = invoice.description?.takeIf { it.isNotBlank() }
+ if (description != null) {
+ TechnicalRow(label = stringResource(id = R.string.paymentdetails_payment_request_description_label)) {
+ Text(text = description)
+ }
+ }
+
+ TechnicalRowSelectable(label = stringResource(id = R.string.paymentdetails_payerkey_label), value = payerKey.toHex())
+ TechnicalRowSelectable(label = stringResource(id = R.string.paymentdetails_payment_hash_label), value = invoice.paymentHash.toHex())
+ TechnicalRowSelectable(label = stringResource(id = R.string.paymentdetails_payment_request_label), value = invoice.write())
+}
+
+@Composable
+private fun Bolt12MetadataSection(
+ metadata: OfferPaymentMetadata
+) {
+ TechnicalRowAmount(
+ label = stringResource(id = R.string.paymentdetails_invoice_requested_label),
+ amount = metadata.amount,
+ rateThen = null
+ )
+ TechnicalRowSelectable(label = stringResource(id = R.string.paymentdetails_payment_hash_label), value = metadata.paymentHash.toHex())
+ TechnicalRowSelectable(label = stringResource(id = R.string.paymentdetails_preimage_label), value = metadata.preimage.toHex())
+ TechnicalRowSelectable(label = stringResource(id = R.string.paymentdetails_offerid_label), value = metadata.offerId.toHex())
+ if (metadata is OfferPaymentMetadata.V1) {
+ TechnicalRowSelectable(label = stringResource(id = R.string.paymentdetails_payerkey_label), value = metadata.payerKey.toHex())
+ }
+}
+
// ============== utility components for this view
@Composable
diff --git a/phoenix-android/src/main/kotlin/fr/acinq/phoenix/android/utils/LegacyMigrationHelper.kt b/phoenix-android/src/main/kotlin/fr/acinq/phoenix/android/utils/LegacyMigrationHelper.kt
index 63228d378..c3d1a7673 100644
--- a/phoenix-android/src/main/kotlin/fr/acinq/phoenix/android/utils/LegacyMigrationHelper.kt
+++ b/phoenix-android/src/main/kotlin/fr/acinq/phoenix/android/utils/LegacyMigrationHelper.kt
@@ -388,7 +388,7 @@ object LegacyMigrationHelper {
} else if (paymentRequest != null) {
LightningOutgoingPayment.Details.Normal(paymentRequest)
} else {
- LightningOutgoingPayment.Details.KeySend(preimage = Lightning.randomBytes32().sha256())
+ throw RuntimeException("unhandled outgoing payment details")
}
val parts = listOfParts.filter { it.paymentType() == PaymentType.Standard() }.map { part ->
diff --git a/phoenix-android/src/main/kotlin/fr/acinq/phoenix/android/utils/extensions.kt b/phoenix-android/src/main/kotlin/fr/acinq/phoenix/android/utils/extensions.kt
index 2edd6a44b..31374e913 100644
--- a/phoenix-android/src/main/kotlin/fr/acinq/phoenix/android/utils/extensions.kt
+++ b/phoenix-android/src/main/kotlin/fr/acinq/phoenix/android/utils/extensions.kt
@@ -123,13 +123,14 @@ fun Connection.CLOSED.isBadCertificate() = this.reason?.cause is CertificateExce
fun WalletPayment.smartDescription(context: Context): String? = when (this) {
is LightningOutgoingPayment -> when (val details = this.details) {
is LightningOutgoingPayment.Details.Normal -> details.paymentRequest.desc
- is LightningOutgoingPayment.Details.KeySend -> context.getString(R.string.paymentdetails_desc_keysend)
is LightningOutgoingPayment.Details.SwapOut -> context.getString(R.string.paymentdetails_desc_swapout, details.address)
+ is LightningOutgoingPayment.Details.Blinded -> details.paymentRequest.description
}
is IncomingPayment -> when (val origin = this.origin) {
is IncomingPayment.Origin.Invoice -> origin.paymentRequest.description
is IncomingPayment.Origin.KeySend -> context.getString(R.string.paymentdetails_desc_keysend)
is IncomingPayment.Origin.SwapIn, is IncomingPayment.Origin.OnChain -> context.getString(R.string.paymentdetails_desc_swapin)
+ is IncomingPayment.Origin.Offer -> context.getString(R.string.paymentdetails_desc_offer_incoming, origin.metadata.offerId.toHex())
}
is SpliceOutgoingPayment -> context.getString(R.string.paymentdetails_desc_splice_out)
is ChannelCloseOutgoingPayment -> context.getString(R.string.paymentdetails_desc_closing_channel)
diff --git a/phoenix-android/src/main/res/values/strings.xml b/phoenix-android/src/main/res/values/strings.xml
index e8e77d5ea..b0fa8a28d 100644
--- a/phoenix-android/src/main/res/values/strings.xml
+++ b/phoenix-android/src/main/res/values/strings.xml
@@ -368,6 +368,7 @@
Swap-out to %1$s
On-chain deposit
Payment to %1$s
+ Payment to %1$s
Add a custom description to this payment
Description
@@ -388,6 +389,8 @@
Swap-in Bitcoin deposit
Swap-out to Bitcoin address
Keysend (spontaneous payment)
+ Offer outgoing Lightning payment
+ Offer incoming Lightning payment
Deposit address
Bitcoin address
@@ -410,6 +413,8 @@
Payment Hash
Invoice
Preimage
+ Payer key
+ Offer ID
Payment status
Successful
diff --git a/phoenix-shared/build.gradle.kts b/phoenix-shared/build.gradle.kts
index 06ffaef00..18c5ad6bb 100644
--- a/phoenix-shared/build.gradle.kts
+++ b/phoenix-shared/build.gradle.kts
@@ -181,6 +181,10 @@ if (includeAndroid) {
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
+ lint {
+ disable.add("Deprecation")
+ }
+
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
diff --git a/phoenix-shared/src/commonMain/kotlin/fr.acinq.phoenix/controllers/payments/ScanController.kt b/phoenix-shared/src/commonMain/kotlin/fr.acinq.phoenix/controllers/payments/ScanController.kt
index b1ad04cf5..9fa498507 100644
--- a/phoenix-shared/src/commonMain/kotlin/fr.acinq.phoenix/controllers/payments/ScanController.kt
+++ b/phoenix-shared/src/commonMain/kotlin/fr.acinq.phoenix/controllers/payments/ScanController.kt
@@ -21,6 +21,7 @@ import fr.acinq.bitcoin.Chain
import fr.acinq.bitcoin.utils.Either
import fr.acinq.lightning.*
import fr.acinq.lightning.db.LightningOutgoingPayment
+import fr.acinq.lightning.io.PayInvoice
import fr.acinq.lightning.io.SendPayment
import fr.acinq.lightning.logging.LoggerFactory
import fr.acinq.lightning.utils.*
@@ -222,11 +223,10 @@ class AppScanController(
}
peer.send(
- SendPayment(
+ PayInvoice(
paymentId = paymentId,
amount = amountToSend,
- recipient = invoice.nodeId,
- paymentRequest = invoice,
+ paymentDetails = LightningOutgoingPayment.Details.Normal(paymentRequest = invoice),
trampolineFeesOverride = listOf(trampolineFees)
)
)
diff --git a/phoenix-shared/src/commonMain/kotlin/fr.acinq.phoenix/db/payments/IncomingOriginType.kt b/phoenix-shared/src/commonMain/kotlin/fr.acinq.phoenix/db/payments/IncomingOriginType.kt
index b4c6321e8..925a93669 100644
--- a/phoenix-shared/src/commonMain/kotlin/fr.acinq.phoenix/db/payments/IncomingOriginType.kt
+++ b/phoenix-shared/src/commonMain/kotlin/fr.acinq.phoenix/db/payments/IncomingOriginType.kt
@@ -17,17 +17,21 @@
@file:UseSerializers(
OutpointSerializer::class,
ByteVector32Serializer::class,
+ ByteVectorSerializer::class,
)
package fr.acinq.phoenix.db.payments
+import fr.acinq.bitcoin.ByteVector
import fr.acinq.bitcoin.ByteVector32
import fr.acinq.bitcoin.OutPoint
import fr.acinq.bitcoin.TxId
import fr.acinq.lightning.db.IncomingPayment
import fr.acinq.lightning.payment.Bolt11Invoice
+import fr.acinq.lightning.payment.OfferPaymentMetadata
import fr.acinq.phoenix.db.payments.DbTypesHelper.decodeBlob
import fr.acinq.phoenix.db.serializers.v1.ByteVector32Serializer
+import fr.acinq.phoenix.db.serializers.v1.ByteVectorSerializer
import fr.acinq.phoenix.db.serializers.v1.OutpointSerializer
import io.ktor.utils.io.charsets.*
import io.ktor.utils.io.core.*
@@ -40,6 +44,7 @@ enum class IncomingOriginTypeVersion {
INVOICE_V0,
SWAPIN_V0,
ONCHAIN_V0,
+ OFFER_V0,
}
sealed class IncomingOriginData {
@@ -65,6 +70,11 @@ sealed class IncomingOriginData {
data class V0(@Serializable val txId: ByteVector32, val outpoints: List<@Serializable OutPoint>) : SwapIn()
}
+ sealed class Offer : IncomingOriginData() {
+ @Serializable
+ data class V0(@Serializable val encodedMetadata: ByteVector) : Offer()
+ }
+
companion object {
fun deserialize(typeVersion: IncomingOriginTypeVersion, blob: ByteArray): IncomingPayment.Origin = decodeBlob(blob) { json, format ->
when (typeVersion) {
@@ -74,6 +84,9 @@ sealed class IncomingOriginData {
IncomingOriginTypeVersion.ONCHAIN_V0 -> format.decodeFromString(json).let {
IncomingPayment.Origin.OnChain(TxId(it.txId), it.outpoints.toSet())
}
+ IncomingOriginTypeVersion.OFFER_V0 -> format.decodeFromString(json).let {
+ IncomingPayment.Origin.Offer(metadata = OfferPaymentMetadata.decode(it.encodedMetadata))
+ }
}
}
}
@@ -88,4 +101,6 @@ fun IncomingPayment.Origin.mapToDb(): Pair
Json.encodeToString(IncomingOriginData.SwapIn.V0(address)).toByteArray(Charsets.UTF_8)
is IncomingPayment.Origin.OnChain -> IncomingOriginTypeVersion.ONCHAIN_V0 to
Json.encodeToString(IncomingOriginData.OnChain.V0(txId.value, localInputs.toList())).toByteArray(Charsets.UTF_8)
+ is IncomingPayment.Origin.Offer -> IncomingOriginTypeVersion.OFFER_V0 to
+ Json.encodeToString(IncomingOriginData.Offer.V0(metadata.encode())).toByteArray(Charsets.UTF_8)
}
diff --git a/phoenix-shared/src/commonMain/kotlin/fr.acinq.phoenix/db/payments/OutgoingDetailsType.kt b/phoenix-shared/src/commonMain/kotlin/fr.acinq.phoenix/db/payments/OutgoingDetailsType.kt
index 49be4761e..e3534a847 100644
--- a/phoenix-shared/src/commonMain/kotlin/fr.acinq.phoenix/db/payments/OutgoingDetailsType.kt
+++ b/phoenix-shared/src/commonMain/kotlin/fr.acinq.phoenix/db/payments/OutgoingDetailsType.kt
@@ -22,25 +22,27 @@
package fr.acinq.phoenix.db.payments
import fr.acinq.bitcoin.ByteVector32
+import fr.acinq.bitcoin.PrivateKey
import fr.acinq.bitcoin.Satoshi
import fr.acinq.lightning.db.LightningOutgoingPayment
import fr.acinq.lightning.payment.Bolt11Invoice
+import fr.acinq.lightning.payment.Bolt12Invoice
import fr.acinq.phoenix.db.serializers.v1.ByteVector32Serializer
import fr.acinq.phoenix.db.serializers.v1.SatoshiSerializer
import io.ktor.utils.io.charsets.*
import io.ktor.utils.io.core.*
import kotlinx.serialization.Serializable
import kotlinx.serialization.UseSerializers
+import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
-
enum class OutgoingDetailsTypeVersion {
NORMAL_V0,
- KEYSEND_V0,
SWAPOUT_V0,
@Deprecated("channel close are now stored in their own table")
CLOSING_V0,
+ BLINDED_V0,
}
sealed class OutgoingDetailsData {
@@ -50,20 +52,19 @@ sealed class OutgoingDetailsData {
data class V0(val paymentRequest: String) : Normal()
}
- sealed class KeySend : OutgoingDetailsData() {
+ sealed class SwapOut : OutgoingDetailsData() {
@Serializable
- data class V0(@Serializable val preimage: ByteVector32) : KeySend()
+ data class V0(val address: String, val paymentRequest: String, @Serializable val swapOutFee: Satoshi) : SwapOut()
}
- sealed class SwapOut : OutgoingDetailsData() {
+ sealed class Blinded : OutgoingDetailsData() {
@Serializable
- data class V0(val address: String, val paymentRequest: String, @Serializable val swapOutFee: Satoshi) : SwapOut()
+ data class V0(val paymentRequest: String, val payerKey: String) : Blinded()
}
@Deprecated("channel close are now stored in their own table")
sealed class Closing : OutgoingDetailsData() {
@Serializable
- @Suppress("DEPRECATION")
data class V0(
@Serializable val channelId: ByteVector32,
val closingAddress: String,
@@ -73,18 +74,25 @@ sealed class OutgoingDetailsData {
companion object {
/** Deserialize the details of an outgoing payment. Return null if the details is for a legacy channel closing payment (see [deserializeLegacyClosingDetails]). */
- @Suppress("DEPRECATION")
fun deserialize(typeVersion: OutgoingDetailsTypeVersion, blob: ByteArray): LightningOutgoingPayment.Details? = DbTypesHelper.decodeBlob(blob) { json, format ->
when (typeVersion) {
- OutgoingDetailsTypeVersion.NORMAL_V0 -> format.decodeFromString(json).let { LightningOutgoingPayment.Details.Normal(Bolt11Invoice.read(it.paymentRequest).get()) }
- OutgoingDetailsTypeVersion.KEYSEND_V0 -> format.decodeFromString(json).let { LightningOutgoingPayment.Details.KeySend(it.preimage) }
- OutgoingDetailsTypeVersion.SWAPOUT_V0 -> format.decodeFromString(json).let { LightningOutgoingPayment.Details.SwapOut(it.address, Bolt11Invoice.read(it.paymentRequest).get(), it.swapOutFee) }
+ OutgoingDetailsTypeVersion.NORMAL_V0 -> format.decodeFromString(json).let {
+ LightningOutgoingPayment.Details.Normal(Bolt11Invoice.read(it.paymentRequest).get())
+ }
+ OutgoingDetailsTypeVersion.SWAPOUT_V0 -> format.decodeFromString(json).let {
+ LightningOutgoingPayment.Details.SwapOut(it.address, Bolt11Invoice.read(it.paymentRequest).get(), it.swapOutFee)
+ }
OutgoingDetailsTypeVersion.CLOSING_V0 -> null
+ OutgoingDetailsTypeVersion.BLINDED_V0 -> format.decodeFromString(json).let {
+ LightningOutgoingPayment.Details.Blinded(
+ paymentRequest = Bolt12Invoice.fromString(it.paymentRequest).get(),
+ payerKey = PrivateKey.fromHex(it.payerKey),
+ )
+ }
}
}
/** Returns the channel closing details from a blob, for backward-compatibility purposes. */
- @Suppress("DEPRECATION")
fun deserializeLegacyClosingDetails(blob: ByteArray): Closing.V0 = DbTypesHelper.decodeBlob(blob) { json, format ->
format.decodeFromString(json)
}
@@ -94,8 +102,8 @@ sealed class OutgoingDetailsData {
fun LightningOutgoingPayment.Details.mapToDb(): Pair = when (this) {
is LightningOutgoingPayment.Details.Normal -> OutgoingDetailsTypeVersion.NORMAL_V0 to
Json.encodeToString(OutgoingDetailsData.Normal.V0(paymentRequest.write())).toByteArray(Charsets.UTF_8)
- is LightningOutgoingPayment.Details.KeySend -> OutgoingDetailsTypeVersion.KEYSEND_V0 to
- Json.encodeToString(OutgoingDetailsData.KeySend.V0(preimage)).toByteArray(Charsets.UTF_8)
is LightningOutgoingPayment.Details.SwapOut -> OutgoingDetailsTypeVersion.SWAPOUT_V0 to
Json.encodeToString(OutgoingDetailsData.SwapOut.V0(address, paymentRequest.write(), swapOutFee)).toByteArray(Charsets.UTF_8)
+ is LightningOutgoingPayment.Details.Blinded -> OutgoingDetailsTypeVersion.BLINDED_V0 to
+ Json.encodeToString(OutgoingDetailsData.Blinded.V0(paymentRequest.write(), payerKey.toHex())).toByteArray(Charsets.UTF_8)
}
diff --git a/phoenix-shared/src/commonMain/kotlin/fr.acinq.phoenix/utils/CsvWriter.kt b/phoenix-shared/src/commonMain/kotlin/fr.acinq.phoenix/utils/CsvWriter.kt
index a2943c4f9..7a88ff8c4 100644
--- a/phoenix-shared/src/commonMain/kotlin/fr.acinq.phoenix/utils/CsvWriter.kt
+++ b/phoenix-shared/src/commonMain/kotlin/fr.acinq.phoenix/utils/CsvWriter.kt
@@ -128,11 +128,14 @@ class CsvWriter {
is IncomingPayment.Origin.OnChain -> {
"Swap-in with inputs: ${origin.localInputs.map { it.txid.toString() } }"
}
+ is IncomingPayment.Origin.Offer -> {
+ "Incoming offer ${origin.metadata.offerId}"
+ }
}
is LightningOutgoingPayment -> when (val details = payment.details) {
is LightningOutgoingPayment.Details.Normal -> "Outgoing LN payment to ${details.paymentRequest.nodeId.toHex()}"
- is LightningOutgoingPayment.Details.KeySend -> "Outgoing LN payment (keysend)"
is LightningOutgoingPayment.Details.SwapOut -> "Swap-out to ${details.address}"
+ is LightningOutgoingPayment.Details.Blinded -> "Offer to ${details.payerKey.publicKey()}"
}
is SpliceOutgoingPayment -> "Outgoing splice to ${payment.address}"
is ChannelCloseOutgoingPayment -> "Channel closing to ${payment.address}"