diff --git a/phoenix-android/src/main/kotlin/fr/acinq/phoenix/android/payments/ScanDataView.kt b/phoenix-android/src/main/kotlin/fr/acinq/phoenix/android/payments/ScanDataView.kt
index b147f5c66..239f552b0 100644
--- a/phoenix-android/src/main/kotlin/fr/acinq/phoenix/android/payments/ScanDataView.kt
+++ b/phoenix-android/src/main/kotlin/fr/acinq/phoenix/android/payments/ScanDataView.kt
@@ -361,7 +361,8 @@ private fun ScanErrorView(
is Scan.BadRequestReason.InvalidLnurl -> stringResource(R.string.scan_error_lnurl_invalid)
is Scan.BadRequestReason.UnsupportedLnurl -> stringResource(R.string.scan_error_lnurl_unsupported)
is Scan.BadRequestReason.UnknownFormat -> stringResource(R.string.scan_error_invalid_generic)
- is Scan.BadRequestReason.InvalidBip353 -> "invalid bip-353 response: ${reason.path}"
+ is Scan.BadRequestReason.Bip353InvalidOffer -> stringResource(id = R.string.scan_error_bip353_invalid_offer)
+ is Scan.BadRequestReason.Bip353NoDNSSEC -> stringResource(id = R.string.scan_error_bip353_dnssec)
}
Dialog(
onDismiss = onErrorDialogDismiss,
diff --git a/phoenix-android/src/main/res/values-b+es+419/important_strings.xml b/phoenix-android/src/main/res/values-b+es+419/important_strings.xml
index bf0444cb7..e7b66ae86 100644
--- a/phoenix-android/src/main/res/values-b+es+419/important_strings.xml
+++ b/phoenix-android/src/main/res/values-b+es+419/important_strings.xml
@@ -166,6 +166,8 @@
Error al procesar este enlace LNURL. Comprueba que sea válido.
Este tipo de LNURL aún no es compatible.
Estos datos usan un formato desconocido por lo que no se pueden procesar.
+ Esta dirección utiliza una oferta Bolt12 no válida.
+ Esta dirección está alojada en un DNS no seguro. DNSSEC debe estar habilitado.
diff --git a/phoenix-android/src/main/res/values-cs/important_strings.xml b/phoenix-android/src/main/res/values-cs/important_strings.xml
index 5eb05b892..1e8058161 100644
--- a/phoenix-android/src/main/res/values-cs/important_strings.xml
+++ b/phoenix-android/src/main/res/values-cs/important_strings.xml
@@ -162,6 +162,8 @@
Tento LNURL odkaz se nepodařilo zpracovat. Ujistěte se, že je platný.
Tento typ LNURL zatím není podporován.
Tato data používají neznámý formát a nelze je zpracovat.
+ Tato adresa používá neplatnou nabídku Bolt12.
+ Tato adresa je hostována na nezabezpečeném DNS. DNSSEC musí být povolen.
diff --git a/phoenix-android/src/main/res/values-de/important_strings.xml b/phoenix-android/src/main/res/values-de/important_strings.xml
index 962006dcb..a1d1ec5c2 100644
--- a/phoenix-android/src/main/res/values-de/important_strings.xml
+++ b/phoenix-android/src/main/res/values-de/important_strings.xml
@@ -165,6 +165,8 @@
Dieser LNURL-Link konnte nicht verarbeitet werden. Stellen Sie sicher, dass er gültig ist.
Dieser LNURL-Typ wird noch nicht unterstützt.
Diese Daten haben ein unbekanntes Format und können nicht verarbeitet werden.
+ Diese Adresse verwendet ein ungültiges Bolt12-Angebot.
+ Diese Adresse wird über einen unsicheren DNS gehostet. DNSSEC muss aktiviert sein.
diff --git a/phoenix-android/src/main/res/values-es/important_strings.xml b/phoenix-android/src/main/res/values-es/important_strings.xml
index dc1a9d667..764a61234 100644
--- a/phoenix-android/src/main/res/values-es/important_strings.xml
+++ b/phoenix-android/src/main/res/values-es/important_strings.xml
@@ -169,6 +169,8 @@
No se ha podido procesar este enlace LNURL. Asegúrese de que es válido.
Este tipo de LNURL aún no se admite.
Estos datos utilizan un formato desconocido y no pueden ser procesados.
+ Esta dirección utiliza una oferta Bolt12 no válida.
+ Esta dirección está alojada en un DNS no seguro. DNSSEC debe estar habilitado.
diff --git a/phoenix-android/src/main/res/values-fr/important_strings.xml b/phoenix-android/src/main/res/values-fr/important_strings.xml
index 80d223975..b4540bbb7 100644
--- a/phoenix-android/src/main/res/values-fr/important_strings.xml
+++ b/phoenix-android/src/main/res/values-fr/important_strings.xml
@@ -169,6 +169,8 @@
Ce lien LNURL n\'a pas pu être traité. Assurez-vous qu\'il soit valide.
Ce type de lien LNURL n\'est pas supporté.
Ce contenu est mal formatté ou bien n\'est pas géré par Phoenix.
+ Cette adresse utilise une offre Bolt12 invalide.
+ Cette adresse est hébergée sur un DNS non sécurisé. DNSSEC doit être activé.
diff --git a/phoenix-android/src/main/res/values-pt-rBR/important_strings.xml b/phoenix-android/src/main/res/values-pt-rBR/important_strings.xml
index 977b784f4..27d04e913 100644
--- a/phoenix-android/src/main/res/values-pt-rBR/important_strings.xml
+++ b/phoenix-android/src/main/res/values-pt-rBR/important_strings.xml
@@ -167,6 +167,8 @@
Falha ao processar esse link LNURL. Verifique se ele é válido.
Este tipo de LNURL ainda não é suportado.
Esses dados usam um formato desconhecido e não podem ser processados.
+ Este endereço usa uma oferta Bolt12 inválida.
+ Este endereço está hospedado em um DNS não seguro. O DNSSEC deve estar ativado.
diff --git a/phoenix-android/src/main/res/values-sk/important_strings.xml b/phoenix-android/src/main/res/values-sk/important_strings.xml
index d30e4e85c..7c875ec76 100644
--- a/phoenix-android/src/main/res/values-sk/important_strings.xml
+++ b/phoenix-android/src/main/res/values-sk/important_strings.xml
@@ -168,6 +168,8 @@
Tento LNURL odkaz sa nepodarilo spracovať. Uistite sa, že je platný.
Tento typ LNURL zatiaľ nie je podporovaný.
Tieto údaje používajú neznámy formát a nemožno ich spracovať.
+ Táto adresa používa neplatnú ponuku Bolt12.
+ Táto adresa je umiestnená na nezabezpečenom DNS. DNSSEC musí byť povolený.
diff --git a/phoenix-android/src/main/res/values-vi/important_strings.xml b/phoenix-android/src/main/res/values-vi/important_strings.xml
index 9e19665b5..0e4f6a226 100644
--- a/phoenix-android/src/main/res/values-vi/important_strings.xml
+++ b/phoenix-android/src/main/res/values-vi/important_strings.xml
@@ -175,6 +175,8 @@
Không thể xử lý liên kết LNURL này. Hãy đảm bảo liên kết này có hiệu lực.
Loại LNURL này chưa được hỗ trợ.
Dữ liệu này sử dụng định dạng không xác định và không thể xử lý được.
+ Địa chỉ này sử dụng ưu đãi Bolt12 không hợp lệ.
+ Địa chỉ này được lưu trữ trên một DNS không an toàn. DNSSEC phải được bật.
diff --git a/phoenix-android/src/main/res/values/important_strings.xml b/phoenix-android/src/main/res/values/important_strings.xml
index ff84c087f..d9a443e67 100644
--- a/phoenix-android/src/main/res/values/important_strings.xml
+++ b/phoenix-android/src/main/res/values/important_strings.xml
@@ -171,6 +171,8 @@
Failed to process this LNURL link. Make sure it is valid.
This type of LNURL is not supported yet.
This data uses an unknown format and cannot be processed.
+ This address uses an invalid Bolt12 offer.
+ This address is hosted on an unsecure DNS. DNSSEC must be enabled.
diff --git a/phoenix-shared/src/commonMain/kotlin/fr.acinq.phoenix/controllers/payments/Scan.kt b/phoenix-shared/src/commonMain/kotlin/fr.acinq.phoenix/controllers/payments/Scan.kt
index 87f2ca0e1..3e9b5f58a 100644
--- a/phoenix-shared/src/commonMain/kotlin/fr.acinq.phoenix/controllers/payments/Scan.kt
+++ b/phoenix-shared/src/commonMain/kotlin/fr.acinq.phoenix/controllers/payments/Scan.kt
@@ -35,14 +35,15 @@ data class MaxFees(
object Scan {
- sealed class BadRequestReason {
+ sealed class BadRequestReason : Exception() {
object UnknownFormat : BadRequestReason()
object AlreadyPaidInvoice : BadRequestReason()
data class Expired(val timestampSeconds: Long, val expirySeconds: Long) : BadRequestReason()
data class ChainMismatch(val expected: Chain) : BadRequestReason()
data class ServiceError(val url: Url, val error: LnurlError.RemoteFailure) : BadRequestReason()
data class InvalidLnurl(val url: Url) : BadRequestReason()
- data class InvalidBip353(val path: String) : BadRequestReason()
+ data class Bip353InvalidOffer(val path: String) : BadRequestReason()
+ data class Bip353NoDNSSEC(val path: String) : BadRequestReason()
data class UnsupportedLnurl(val url: Url) : BadRequestReason()
}
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 ff1713d7c..6af01f33c 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
@@ -128,23 +128,31 @@ class AppScanController(
) {
val input = Parser.removeExcessInput(intent.request)
- Parser.readBolt11Invoice(input)?.let {
- processBolt11Invoice(it)
- } ?: Parser.readOffer(input)?.let {
- processOffer(it)
- } ?: readEmailLikeAddress(input)?.let {
- when (it) {
- is Either.Left -> processOffer(it.value)
- is Either.Right -> processLnurl(it.value)
+ try {
+ Parser.readBolt11Invoice(input)?.let {
+ processBolt11Invoice(it)
+ } ?: Parser.readOffer(input)?.let {
+ processOffer(it)
+ } ?: readEmailLikeAddress(input)?.let {
+ when (it) {
+ is Either.Left -> processOffer(it.value)
+ is Either.Right -> processLnurl(it.value)
+ }
+ } ?: readLnurl(input)?.let {
+ processLnurl(it)
+ } ?: readBitcoinAddress(input)?.let {
+ processBitcoinAddress(input, it)
+ } ?: readLNURLFallback(input)?.let {
+ processLnurl(it)
+ } ?: run {
+ model(Scan.Model.BadRequest(request = intent.request, reason = Scan.BadRequestReason.UnknownFormat))
+ }
+ } catch (e: Exception) {
+ if (e is Scan.BadRequestReason) {
+ model(Scan.Model.BadRequest(request = intent.request, reason = e))
+ } else {
+ model(Scan.Model.BadRequest(request = intent.request, reason = Scan.BadRequestReason.UnknownFormat))
}
- } ?: readLnurl(input)?.let {
- processLnurl(it)
- } ?: readBitcoinAddress(input)?.let {
- processBitcoinAddress(input, it)
- } ?: readLNURLFallback(input)?.let {
- processLnurl(it)
- } ?: run {
- model(Scan.Model.BadRequest(request = intent.request, reason = Scan.BadRequestReason.UnknownFormat))
}
}
@@ -564,45 +572,39 @@ class AppScanController(
model(Scan.Model.ResolvingBip353)
val dnsPath = "$username.user._bitcoin-payment.$domain."
- try {
- val json = DnsResolvers.getRandom().getTxtRecord(dnsPath)
- logger.debug { "dns resolved to ${json.toString().take(100)}" }
+ val json = DnsResolvers.getRandom().getTxtRecord(dnsPath)
+ logger.debug { "dns resolved to ${json.toString().take(100)}" }
- val status = json["Status"]?.jsonPrimitive?.intOrNull
- if (status == null || status > 0) throw RuntimeException("invalid status=$status")
+ val status = json["Status"]?.jsonPrimitive?.intOrNull
+ if (status == null || status > 0) return null
- val ad = json["AD"]?.jsonPrimitive?.booleanOrNull
- if (ad != true) {
- logger.info { "AD false, abort dns lookup" }
- return null
- }
+ val records = json["Answer"]?.jsonArray
+ if (records.isNullOrEmpty()) {
+ logger.debug { "no records for $dnsPath" }
+ return null
+ }
- val records = json["Answer"]?.jsonArray
- if (records.isNullOrEmpty()) {
- logger.debug { "no records for $dnsPath" }
- return null
- }
+ val matchingRecord = records.filterIsInstance().firstOrNull {
+ logger.debug { "inspecting record=$it" }
+ it["name"]?.jsonPrimitive?.content == dnsPath
+ } ?: return null
- val matchingRecord = records.filterIsInstance().firstOrNull {
- logger.debug { "inspecting record=$it" }
- it["name"]?.jsonPrimitive?.content == dnsPath
- } ?: return null
-
- val data = matchingRecord["data"]?.jsonPrimitive?.content ?: return null
- if (!data.startsWith("bitcoin:")) return null
- val offerString = data.substringAfter("lno=").substringBefore("?")
- if (offerString.isBlank()) return null
-
- return when (val offer = OfferTypes.Offer.decode(offerString)) {
- is Try.Success -> { offer.result }
- is Try.Failure -> {
- model(Scan.Model.BadRequest(request = dnsPath, reason = Scan.BadRequestReason.InvalidBip353(dnsPath)))
- null
- }
+ val ad = json["AD"]?.jsonPrimitive?.booleanOrNull
+ if (ad != true) {
+ logger.debug { "AD false, abort dns lookup" }
+ throw Scan.BadRequestReason.Bip353NoDNSSEC(dnsPath)
+ }
+
+ val data = matchingRecord["data"]?.jsonPrimitive?.content ?: return null
+ if (!data.startsWith("bitcoin:")) throw Scan.BadRequestReason.Bip353InvalidOffer(dnsPath)
+ val offerString = data.substringAfter("lno=").substringBefore("?")
+ if (offerString.isBlank()) throw Scan.BadRequestReason.Bip353InvalidOffer(dnsPath)
+
+ return when (val offer = OfferTypes.Offer.decode(offerString)) {
+ is Try.Success -> { offer.result }
+ is Try.Failure -> {
+ throw Scan.BadRequestReason.Bip353InvalidOffer(dnsPath)
}
- } catch (e: Exception) {
- logger.error(e) { "error when resolving offer on $dnsPath: ${e.message}" }
- return null
}
}