What changed, and why it matters
This commit only adds a new test case to Eclair's invoice parsing tests. It checks that a specific Bitcoin Lightning invoice with a non-standard 'high-S' signature can still be decoded and that the sender's public key is recovered correctly. There is no code change to the actual invoice handling logic.
No action required; this is a regression test addition. If reviewing related production code, verify that signature verification in Bolt11Invoice.fromString() does not enforce low-S and correctly uses secp256k1 public-key recovery semantics.
Security signals we found
Test-only change: no production code modified
Relates to BOLT 11 signature malleability / high-S acceptance
References upstream BOLT specification change (bolts PR 1284)
Evidence from the diff
The diff adds one test in Bolt11InvoiceSpec.scala verifying that Bolt11Invoice.fromString() successfully parses a mainnet BOLT 11 invoice whose signature uses a high-S value and recovers the expected nodeId. The commit message states Eclair already accepts both high-S and low-S signatures during public key recovery, consistent with secp256k1 behavior and the proposed BOLT update lightning/bolts#1284. No production code is modified.
Changed components
eclair-core/src/test/scala/fr/acinq/eclair/payment/Bolt11InvoiceSpec.scalaInspect captured patch +8 / −0
diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/payment/Bolt11InvoiceSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/payment/Bolt11InvoiceSpec.scala
index dad1d24..dc185b4 100644
--- a/eclair-core/src/test/scala/fr/acinq/eclair/payment/Bolt11InvoiceSpec.scala
+++ b/eclair-core/src/test/scala/fr/acinq/eclair/payment/Bolt11InvoiceSpec.scala
@@ -396,6 +396,14 @@ class Bolt11InvoiceSpec extends AnyFunSuite {
assert(invoice.sign(priv).toString == ref)
}
+ test("On mainnet, public-key recovery with high-S signature") {
+ val ref = "lnbc1pvjluezsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygspp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpl2pkx2ctnv5sxxmmwwd5kgetjypeh2ursdae8g6twvus8g6rfwvs8qun0dfjkxaq9qrsgq357wnc5r2ueh7ck6q93dj32dlqnls087fxdwk8qakdyafkq3yap2r09nt4ndd0unm3z9u5t48y6ucv4r5sg7lk98c77ctvjczkspk5qprc90gx"
+ val Success(invoice) = Bolt11Invoice.fromString(ref)
+ assert(invoice.prefix == "lnbc")
+ assert(invoice.amount_opt.isEmpty)
+ assert(invoice.nodeId == PublicKey(hex"02d0139ce7427d6dfffd26a326c18be754ef1e64672b42694ba5b23ef6e6e7803d"))
+ }
+
test("reject invalid invoices") {
val refs = Seq(
// Bech32 checksum is invalid.
Why this scored 12/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.