@@ -218,7 +218,34 @@ class PaymentRequestSpec extends FunSuite {
218
218
}
219
219
220
220
test(" On mainnet, please send $30 for coffee beans to the same peer, which supports features 9, 15 and 99, using secret 0x1111111111111111111111111111111111111111111111111111111111111111" ) {
221
- val ref = " lnbc25m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5vdhkven9v5sxyetpdeessp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygs9q5sqqqqqqqqqqqqqqqpqsq67gye39hfg3zd8rgc80k32tvy9xk2xunwm5lzexnvpx6fd77en8qaq424dxgt56cag2dpt359k3ssyhetktkpqh24jqnjyw6uqd08sgptq44qu"
221
+ val refs = Seq (
222
+ " lnbc25m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5vdhkven9v5sxyetpdeessp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygs9q5sqqqqqqqqqqqqqqqpqsq67gye39hfg3zd8rgc80k32tvy9xk2xunwm5lzexnvpx6fd77en8qaq424dxgt56cag2dpt359k3ssyhetktkpqh24jqnjyw6uqd08sgptq44qu" ,
223
+ // All upper-case
224
+ " lnbc25m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5vdhkven9v5sxyetpdeessp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygs9q5sqqqqqqqqqqqqqqqpqsq67gye39hfg3zd8rgc80k32tvy9xk2xunwm5lzexnvpx6fd77en8qaq424dxgt56cag2dpt359k3ssyhetktkpqh24jqnjyw6uqd08sgptq44qu" .toUpperCase,
225
+ // With ignored fields
226
+ " lnbc25m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5vdhkven9v5sxyetpdeessp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygs9q5sqqqqqqqqqqqqqqqpqsq2qrqqqfppnqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqppnqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqpp4qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqhpnqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqhp4qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqspnqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqsp4qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqnp5qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqnpkqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq2jxxfsnucm4jf4zwtznpaxphce606fvhvje5x7d4gw7n73994hgs7nteqvenq8a4ml8aqtchv5d9pf7l558889hp4yyrqv6a7zpq9fgpskqhza"
227
+ )
228
+
229
+ for (ref <- refs) {
230
+ val pr = PaymentRequest .read(ref)
231
+ assert(pr.prefix === " lnbc" )
232
+ assert(pr.amount === Some (2500000000L msat))
233
+ assert(pr.paymentHash.bytes === hex " 0001020304050607080900010203040506070809000102030405060708090102 " )
234
+ assert(pr.paymentSecret === Some (ByteVector32 (hex " 1111111111111111111111111111111111111111111111111111111111111111 " )))
235
+ assert(pr.timestamp === 1496314658L )
236
+ assert(pr.nodeId === PublicKey (hex " 03e7156ae33b0a208d0744199163177e909e80176e55d97a2f221ede0f934dd9ad " ))
237
+ assert(pr.description === Left (" coffee beans" ))
238
+ assert(pr.features.bitmask === bin " 1000000000000000000000000000000000000000000000000000000000000000000000000000000000001000001000000000 " )
239
+ assert(! pr.features.allowMultiPart)
240
+ assert(! pr.features.requirePaymentSecret)
241
+ assert(! pr.features.allowTrampoline)
242
+ assert(pr.features.supported)
243
+ assert(PaymentRequest .write(pr.sign(priv)) === ref.toLowerCase)
244
+ }
245
+ }
246
+
247
+ test(" On mainnet, please send $30 for coffee beans to the same peer, which supports features 9, 15, 99 and 100, using secret 0x1111111111111111111111111111111111111111111111111111111111111111" ) {
248
+ val ref = " lnbc25m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5vdhkven9v5sxyetpdeessp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygs9q4psqqqqqqqqqqqqqqqpqsqq40wa3khl49yue3zsgm26jrepqr2eghqlx86rttutve3ugd05em86nsefzh4pfurpd9ek9w2vp95zxqnfe2u7ckudyahsa52q66tgzcp6t2dyk"
222
249
val pr = PaymentRequest .read(ref)
223
250
assert(pr.prefix === " lnbc" )
224
251
assert(pr.amount === Some (2500000000L msat))
@@ -228,33 +255,51 @@ class PaymentRequestSpec extends FunSuite {
228
255
assert(pr.nodeId === PublicKey (hex " 03e7156ae33b0a208d0744199163177e909e80176e55d97a2f221ede0f934dd9ad " ))
229
256
assert(pr.description === Left (" coffee beans" ))
230
257
assert(pr.fallbackAddress().isEmpty)
231
- assert(pr.features.bitmask === bin " 1000000000000000000000000000000000000000000000000000000000000000000000000000000000001000001000000000 " )
258
+ assert(pr.features.bitmask === bin " 000011000000000000000000000000000000000000000000000000000000000000000000000000000000000001000001000000000 " )
232
259
assert(! pr.features.allowMultiPart)
233
260
assert(! pr.features.requirePaymentSecret)
234
261
assert(! pr.features.allowTrampoline)
235
- assert(pr.features.supported)
262
+ assert(! pr.features.supported)
236
263
assert(PaymentRequest .write(pr.sign(priv)) === ref)
237
264
}
238
265
239
- test(" On mainnet, please send $30 for coffee beans to the same peer, which supports features 9, 15, 99 and 100, using secret 0x1111111111111111111111111111111111111111111111111111111111111111 " ) {
240
- val ref = " lnbc25m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5vdhkven9v5sxyetpdeessp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygs9q4psqqqqqqqqqqqqqqqpqsqq40wa3khl49yue3zsgm26jrepqr2eghqlx86rttutve3ugd05em86nsefzh4pfurpd9ek9w2vp95zxqnfe2u7ckudyahsa52q66tgzcp6t2dyk "
266
+ test(" On mainnet, please send 0.00967878534 BTC for a list of items within one week, amount in pico-BTC " ) {
267
+ val ref = " lnbc9678785340p1pwmna7lpp5gc3xfm08u9qy06djf8dfflhugl6p7lgza6dsjxq454gxhj9t7a0sd8dgfkx7cmtwd68yetpd5s9xar0wfjn5gpc8qhrsdfq24f5ggrxdaezqsnvda3kkum5wfjkzmfqf3jkgem9wgsyuctwdus9xgrcyqcjcgpzgfskx6eqf9hzqnteypzxz7fzypfhg6trddjhygrcyqezcgpzfysywmm5ypxxjemgw3hxjmn8yptk7untd9hxwg3q2d6xjcmtv4ezq7pqxgsxzmnyyqcjqmt0wfjjq6t5v4khxxqyjw5qcqp2rzjq0gxwkzc8w6323m55m4jyxcjwmy7stt9hwkwe2qxmy8zpsgg7jcuwz87fcqqeuqqqyqqqqlgqqqqn3qq9qn07ytgrxxzad9hc4xt3mawjjt8znfv8xzscs7007v9gh9j569lencxa8xeujzkxs0uamak9aln6ez02uunw6rd2ht2sqe4hz8thcdagpleym0j "
241
268
val pr = PaymentRequest .read(ref)
242
269
assert(pr.prefix === " lnbc" )
243
- assert(pr.amount === Some (2500000000L msat))
244
- assert(pr.paymentHash.bytes === hex " 0001020304050607080900010203040506070809000102030405060708090102 " )
245
- assert(pr.paymentSecret === Some (ByteVector32 (hex " 1111111111111111111111111111111111111111111111111111111111111111 " )))
246
- assert(pr.timestamp === 1496314658L )
270
+ assert(pr.amount === Some (967878534 msat))
271
+ assert(pr.paymentHash.bytes === hex " 462264ede7e14047e9b249da94fefc47f41f7d02ee9b091815a5506bc8abf75f " )
272
+ assert(pr.timestamp === 1572468703L )
247
273
assert(pr.nodeId === PublicKey (hex " 03e7156ae33b0a208d0744199163177e909e80176e55d97a2f221ede0f934dd9ad " ))
248
- assert(pr.description === Left (" coffee beans " ))
274
+ assert(pr.description === Left (" Blockstream Store: 88.85 USD for Blockstream Ledger Nano S x 1, \" Back In My Day \" Sticker x 2, \" I Got Lightning Working \" Sticker x 2 and 1 more items " ))
249
275
assert(pr.fallbackAddress().isEmpty)
250
- assert(pr.features.bitmask === bin " 000011000000000000000000000000000000000000000000000000000000000000000000000000000000000001000001000000000 " )
251
- assert(! pr.features.allowMultiPart)
252
- assert(! pr.features.requirePaymentSecret)
253
- assert(! pr.features.allowTrampoline)
254
- assert(! pr.features.supported)
276
+ assert(pr.expiry === Some (604800L ))
277
+ assert(pr.minFinalCltvExpiryDelta === Some (CltvExpiryDelta (10 )))
278
+ assert(pr.routingInfo === Seq (Seq (ExtraHop (PublicKey (hex " 03d06758583bb5154774a6eb221b1276c9e82d65bbaceca806d90e20c108f4b1c7 " ), ShortChannelId (" 589390x3312x1" ), 1000 msat, 2500 , CltvExpiryDelta (40 )))))
279
+ assert(pr.features.supported)
255
280
assert(PaymentRequest .write(pr.sign(priv)) === ref)
256
281
}
257
282
283
+ test(" reject invalid invoices" ) {
284
+ val refs = Seq (
285
+ // Bech32 checksum is invalid.
286
+ " lnbc2500u1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpquwpc4curk03c9wlrswe78q4eyqc7d8d0xqzpuyk0sg5g70me25alkluzd2x62aysf2pyy8edtjeevuv4p2d5p76r4zkmneet7uvyakky2zr4cusd45tftc9c5fh0nnqpnl2jfll544esqchsrnt" ,
287
+ // Malformed bech32 string (no 1).
288
+ " pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpquwpc4curk03c9wlrswe78q4eyqc7d8d0xqzpuyk0sg5g70me25alkluzd2x62aysf2pyy8edtjeevuv4p2d5p76r4zkmneet7uvyakky2zr4cusd45tftc9c5fh0nnqpnl2jfll544esqchsrny" ,
289
+ // Malformed bech32 string (mixed case).
290
+ " LNBC2500u1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpquwpc4curk03c9wlrswe78q4eyqc7d8d0xqzpuyk0sg5g70me25alkluzd2x62aysf2pyy8edtjeevuv4p2d5p76r4zkmneet7uvyakky2zr4cusd45tftc9c5fh0nnqpnl2jfll544esqchsrny" ,
291
+ // Signature is not recoverable.
292
+ " lnbc2500u1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4jsxqzpuaxtrnwngzn3kdzw5hydlzf03qdgm2hdq27cqv3agm2awhz5se903vruatfhq77w3ls4evs3ch9zw97j25emudupq63nyw24cg27h2rspk28uwq" ,
293
+ // String is too short.
294
+ " lnbc1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpl2pkx2ctnv5sxxmmwwd5kgetjypeh2ursdae8g6na6hlh" ,
295
+ // Invalid multiplier.
296
+ " lnbc2500x1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4jsxqzpujr6jxr9gq9pv6g46y7d20jfkegkg4gljz2ea2a3m9lmvvr95tq2s0kvu70u3axgelz3kyvtp2ywwt0y8hkx2869zq5dll9nelr83zzqqpgl2zg"
297
+ )
298
+ for (ref <- refs) {
299
+ assertThrows[Exception ](PaymentRequest .read(ref))
300
+ }
301
+ }
302
+
258
303
test(" correctly serialize/deserialize variable-length tagged fields" ) {
259
304
val number = 123456
260
305
@@ -295,7 +340,8 @@ class PaymentRequestSpec extends FunSuite {
295
340
296
341
test(" ignore hash tags with invalid length" ) {
297
342
// Bolt11: A reader: MUST skip over p, h, s or n fields that do NOT have data_lengths of 52, 52, 52 or 53, respectively.
298
- def bits (i : Int ) = BitVector .fill(i * 5 )(false )
343
+ def bits (i : Int ) = BitVector .fill(i * 5 )(high = false )
344
+
299
345
val inputs = Map (
300
346
" ppnqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq" -> InvalidTag1 (bits(51 )),
301
347
" pp4qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq" -> InvalidTag1 (bits(53 )),
0 commit comments