Skip to content

Commit d557b44

Browse files
committed
Merge tag 'v0.1.2' into 2024-12-0.1-bindings
v0.1.2 - Apr 02, 2025 - "Foolishly Edgy Cases" API Updates =========== * `lightning-invoice` is now re-exported as `lightning::bolt11_invoice` (lightningdevkit#3671). Performance Improvements ======================== * `rapid-gossip-sync` graph parsing is substantially faster, resolving a regression in 0.1 (lightningdevkit#3581). * `NetworkGraph` loading is now substantially faster and does fewer allocations, resulting in a 20% further improvement in `rapid-gossip-sync` loading when initializing from scratch (lightningdevkit#3581). * `ChannelMonitor`s for closed channels are no longer always re-persisted immediately after startup, reducing on-startup I/O burden (lightningdevkit#3619). Bug Fixes ========= * BOLT 11 invoices longer than 1023 bytes long (and up to 7089 bytes) now properly parse (lightningdevkit#3665). * In some cases, when using synchronous persistence with higher latency than the latency to communicate with peers, when receiving an MPP payment with multiple parts received over the same channel, a channel could hang and not make progress, eventually leading to a force-closure due to timed-out HTLCs. This has now been fixed (lightningdevkit#3680). * Some rare cases with multi-hop BOLT 11 route hints or multiple redundant blinded paths could have led to the router creating invalid `Route`s were fixed (lightningdevkit#3586). * Corrected the decay logic in `ProbabilisticScorer`'s historical buckets model. Note that by default historical buckets are only decayed if no new datapoints have been added for a channel for two weeks (lightningdevkit#3562). * `{Channel,Onion}MessageHandler::peer_disconnected` will now be called if a different message handler refused connection by returning an `Err` from its `peer_connected` method (lightningdevkit#3580). * If the counterparty broadcasts a revoked state with pending HTLCs, those will now be claimed with other outputs which we consider to not be vulnerable to pinning attacks if they are not yet claimable by our counterparty, potentially reducing our exposure to pinning attacks (lightningdevkit#3564).
2 parents 54a945b + 9069cc3 commit d557b44

31 files changed

+1306
-848
lines changed

.github/workflows/semver.yml

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
name: SemVer checks
2+
on:
3+
push:
4+
branches-ignore:
5+
- master
6+
pull_request:
7+
branches-ignore:
8+
- master
9+
10+
jobs:
11+
semver-checks:
12+
runs-on: ubuntu-latest
13+
steps:
14+
- name: Checkout source code
15+
uses: actions/checkout@v4
16+
- name: Install Rust stable toolchain
17+
run: |
18+
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile=minimal --default-toolchain stable
19+
rustup override set stable
20+
- name: Check SemVer with default features
21+
uses: obi1kenobi/cargo-semver-checks-action@v2
22+
with:
23+
feature-group: default-features
24+
- name: Check SemVer *without* default features
25+
uses: obi1kenobi/cargo-semver-checks-action@v2
26+
with:
27+
feature-group: only-explicit-features
28+
- name: Check lightning-background-processor SemVer
29+
uses: obi1kenobi/cargo-semver-checks-action@v2
30+
with:
31+
package: lightning-background-processor
32+
feature-group: only-explicit-features
33+
features: futures
34+
- name: Check lightning-block-sync SemVer
35+
uses: obi1kenobi/cargo-semver-checks-action@v2
36+
with:
37+
package: lightning-block-sync
38+
feature-group: only-explicit-features
39+
features: rpc-client,rest-client
40+
- name: Check lightning-transaction-sync electrum SemVer
41+
uses: obi1kenobi/cargo-semver-checks-action@v2
42+
with:
43+
manifest-path: lightning-transaction-sync/Cargo.toml
44+
feature-group: only-explicit-features
45+
features: electrum
46+
- name: Check lightning-transaction-sync esplora-blocking SemVer
47+
uses: obi1kenobi/cargo-semver-checks-action@v2
48+
with:
49+
manifest-path: lightning-transaction-sync/Cargo.toml
50+
feature-group: only-explicit-features
51+
features: esplora-blocking
52+
- name: Check lightning-transaction-sync esplora-async SemVer
53+
uses: obi1kenobi/cargo-semver-checks-action@v2
54+
with:
55+
manifest-path: lightning-transaction-sync/Cargo.toml
56+
feature-group: only-explicit-features
57+
features: esplora-async

CHANGELOG.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,40 @@
1+
# 0.1.2 - Apr 02, 2025 - "Foolishly Edgy Cases"
2+
3+
## API Updates
4+
* `lightning-invoice` is now re-exported as `lightning::bolt11_invoice`
5+
(#3671).
6+
7+
## Performance Improvements
8+
* `rapid-gossip-sync` graph parsing is substantially faster, resolving a
9+
regression in 0.1 (#3581).
10+
* `NetworkGraph` loading is now substantially faster and does fewer
11+
allocations, resulting in a 20% further improvement in `rapid-gossip-sync`
12+
loading when initializing from scratch (#3581).
13+
* `ChannelMonitor`s for closed channels are no longer always re-persisted
14+
immediately after startup, reducing on-startup I/O burden (#3619).
15+
16+
## Bug Fixes
17+
* BOLT 11 invoices longer than 1023 bytes long (and up to 7089 bytes) now
18+
properly parse (#3665).
19+
* In some cases, when using synchronous persistence with higher latency than
20+
the latency to communicate with peers, when receiving an MPP payment with
21+
multiple parts received over the same channel, a channel could hang and not
22+
make progress, eventually leading to a force-closure due to timed-out HTLCs.
23+
This has now been fixed (#3680).
24+
* Some rare cases with multi-hop BOLT 11 route hints or multiple redundant
25+
blinded paths could have led to the router creating invalid `Route`s were
26+
fixed (#3586).
27+
* Corrected the decay logic in `ProbabilisticScorer`'s historical buckets
28+
model. Note that by default historical buckets are only decayed if no new
29+
datapoints have been added for a channel for two weeks (#3562).
30+
* `{Channel,Onion}MessageHandler::peer_disconnected` will now be called if a
31+
different message handler refused connection by returning an `Err` from its
32+
`peer_connected` method (#3580).
33+
* If the counterparty broadcasts a revoked state with pending HTLCs, those
34+
will now be claimed with other outputs which we consider to not be
35+
vulnerable to pinning attacks if they are not yet claimable by our
36+
counterparty, potentially reducing our exposure to pinning attacks (#3564).
37+
138
# 0.1.1 - Jan 28, 2025 - "Onchain Matters"
239

340
## API Updates

lightning-background-processor/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2371,8 +2371,8 @@ mod tests {
23712371
42,
23722372
53,
23732373
features,
2374-
$nodes[0].node.get_our_node_id(),
2375-
$nodes[1].node.get_our_node_id(),
2374+
$nodes[0].node.get_our_node_id().into(),
2375+
$nodes[1].node.get_our_node_id().into(),
23762376
)
23772377
.expect("Failed to update channel from partial announcement");
23782378
let original_graph_description = $nodes[0].network_graph.to_string();

lightning-invoice/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[package]
22
name = "lightning-invoice"
33
description = "Data structures to parse and serialize BOLT11 lightning invoices"
4-
version = "0.33.1"
4+
version = "0.33.2"
55
authors = ["Sebastian Geisler <sgeisler@wh2.tu-dresden.de>"]
66
documentation = "https://docs.rs/lightning-invoice/"
77
license = "MIT OR Apache-2.0"

lightning-invoice/src/de.rs

Lines changed: 243 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,10 @@ use core::str::FromStr;
99
use std::error;
1010

1111
use bech32::primitives::decode::{CheckedHrpstring, CheckedHrpstringError};
12-
use bech32::{Bech32, Fe32, Fe32IterExt};
12+
use bech32::{Fe32, Fe32IterExt};
1313

1414
use crate::prelude::*;
15+
use crate::Bolt11Bech32;
1516
use bitcoin::hashes::sha256;
1617
use bitcoin::hashes::Hash;
1718
use bitcoin::{PubkeyHash, ScriptHash, WitnessVersion};
@@ -377,7 +378,7 @@ impl FromStr for SignedRawBolt11Invoice {
377378
type Err = Bolt11ParseError;
378379

379380
fn from_str(s: &str) -> Result<Self, Self::Err> {
380-
let parsed = CheckedHrpstring::new::<Bech32>(s)?;
381+
let parsed = CheckedHrpstring::new::<Bolt11Bech32>(s)?;
381382
let hrp = parsed.hrp();
382383
// Access original non-packed 32 byte values (as Fe32s)
383384
// Note: the type argument is needed due to the API peculiarities, but it's not used
@@ -1175,4 +1176,244 @@ mod test {
11751176
)
11761177
)
11771178
}
1179+
1180+
// Test some long invoice test vectors successfully roundtrip. Generated
1181+
// from Lexe proptest: <https://github.com/lexe-app/lexe-public/blob/4bc7018307e5221e1e1ee8b17ce366338fb11a16/common/src/ln/invoice.rs#L183>.
1182+
#[test]
1183+
fn test_deser_long_test_vectors() {
1184+
use crate::Bolt11Invoice;
1185+
1186+
#[track_caller]
1187+
fn parse_ok(invoice_str: &str) {
1188+
let invoice = Bolt11Invoice::from_str(invoice_str).unwrap();
1189+
let invoice_str2 = invoice.to_string();
1190+
if invoice_str != invoice_str2 {
1191+
panic!(
1192+
"Invoice does not roundtrip: invoice_str != invoice_str2\n\
1193+
invoice_str: {invoice_str}\n\
1194+
invoice_str2: {invoice_str2}\n\
1195+
\n\
1196+
{invoice:?}"
1197+
);
1198+
}
1199+
}
1200+
1201+
// 1024 B shrunk invoice just above previous limit of 1023 B from Lexe proptest
1202+
parse_ok(
1203+
"lnbc10000000000000000010p1qqqqqqqdtuxpqkzq8sjzqgps4pvyczqq8sjzqgpuysszq0pyyqsrp2zs0sjz\
1204+
qgps4pxrcfpqyqc2slpyyqsqsv9gwz59s5zqpqyps5rc9qsrs2pqxz5ysyzcfqgysyzs0sjzqgqq8sjzqgps4p\
1205+
xqqzps4pqpssqgzpxps5ruysszqrps4pg8p2zgpsc2snpuysszqzqsgqvys0pyyqsrcfpqyqvycv9gfqqrcfpq\
1206+
yq7zggpq8q5zqyruysszqwpgyqxpsjqsgq7zggpqps7zggpq8sjzqgqgqq7zggpqpq7zggpq8q5zqqpuysszq0\
1207+
pyyqsqs0pyyqspsnqgzpqpqlpyyqsqszpuysszqyzvzpvysrqq8sjzqgqvrp7zggpqpqxpsspp5mf45hs3cgph\
1208+
h0074r5qmr74y82r26ac4pzdg4nd9mdmsvz6ffqpssp5vr4yra4pcv74h9hk3d0233nqu4gktpuykjamrafrdp\
1209+
uedqugzh3q9q2sqqqqqysgqcqrpqqxq8pqqqqqqnp4qgvcxpme2q5lng36j9gruwlrtk2f86s3c5xmk87yhvyu\
1210+
wdeh025q5r9yqwnqegv9hj9nzkhyxaeyq92wcrnqp36pyrc2qzrvswj5g96ey2dn6qqqqqqqqqqqqqqqqqqqqq\
1211+
qqqqqqqqp9a5vs0t4z56p64xyma8s84yvdx7uhqj0gvrr424fea2wpztq2fwqqqqqqqqqqqqqqqqqqqqqqqqqq\
1212+
qqqqmy9qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq\
1213+
qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqpcnsxc32du9n7amlypuhclzqrt6lkegq\
1214+
0v3r7nczjv9tv30z7phq80r3dm7pvgykl7gwuenmem93h5xwdwac6ngsmzqc34khrg3qjgsq6qk6lc"
1215+
);
1216+
// 1517 B mainnet invoice from Lexe proptest
1217+
parse_ok(
1218+
"lnbc8735500635020489010p1av5kfs8deupvyk4u5ynj03hmalhhhml0fxc2jlrv9z4lg6s4hnhkz69malhhe\
1219+
t3x9yqpsxru4a3kwar2qtu2q2ughx367q600s5x7c7tln4k0fu78skxqevaqm8sayhuur377zgf3uf94n57xzh\
1220+
dw99u42hwc089djn5xj723w7zageflsnzdmyte89tecf2ac7xhg4y3u9f4xpuv2hwxjlsarp0e24fu8tme6rgv\
1221+
0tqj08z9f4u30rw59k8emhtvs7wye0xfw6x5q5tju2p208rvtkunzwtwghtp22tlnh62gxwhfkxp4cnz7ts3rx\
1222+
vlzszhv9y00h77lpdvcjyhjtmalh5dn5e8n5w8cqle0vunzduu4nza9y0734qhxday9hzywl0aa0vhzy0qmphc\
1223+
64d4hduj08dv2krpgqtc2v83gptk34reelxyc7wsgnze890c6nrv6p0cmepatc269eayzjjkqk30n52rfl5dg7\
1224+
wztl96f7wc2tzx34q909xuajnyt4u4lnk87lwal7z0etdz5tmece0v3u796jfp68nccn05ty54ncfelts3v8g0\
1225+
sn6v6hsu87zat4r03368ersu87252dd0nswymxzc2pyxl8yy844hspuyj47w0px4u4leefq568sk0rr9th4ql9\
1226+
f9ykawrczkz5hp22nstg3lrlsa6u2q2ull3kzce2sh0h77sjv0zszhzy4hfh6u0pwux5l3gpthsn72mfu47sw9\
1227+
zw3hzk7srznp27z0etdp0725me00sn72mgkf0fteehruk0lg6swh34z52puaekzmjlmalhhe6m8ug7z3c8g8zh\
1228+
jjspp5zj0sm85g5ufng9w7s6p4ucdk80tyvz64sg54v0cy4vgnr37f78sqsp5l6azu2hv6we30er90jrslqpvd\
1229+
trnrphhesca2wg5q83k52rsu2cq9q2sqqqqqysgqcqr8h2np4qw0ha2k282hm8jh5rcfq0hsp2zhddtlc5vs23\
1230+
uphyv0lv3k8sqsfgfp4qyrk86tx5xg2aa7et4cdzhnvl5s4nd33ugytt7gamk9tugn9yransr9yq08gpwsn8t2\
1231+
tq4ducjfhrcz707av0ss20urjh8vldrpmehqxa0stkesvuq82txyqzfhej7qccswy7k5wvcppk63c6zpjytfda\
1232+
ccadacjtn52lpe6s85rjfqlxzp6frq33xshaz2nr9xjkhd3jj8qg39nmfzvpgmayakqmy9rseakwgcudug7hs4\
1233+
5wh430ywh7qhj3khczh8gle4cn93ymgfwa7rrvcw9lywyyz58k4p40a3nu9svthaf0qeg8f2ay4tw9p48p70qm\
1234+
ayu3ejl2q8pj9e2l22h7775tl44hs6ke4sdfgcr6aj8wra4r2v9sj6xa5chd5ctpfg8chtrer3kkp0e6af88lk\
1235+
rfxcklf2hyslv2hr0xl5lwrm5y5uttxn4ndfz8789znf78nspa3xy68"
1236+
);
1237+
// 1804 B regtest invoice from Lexe proptest
1238+
parse_ok(
1239+
"lnbcrt17124979001314909880p1y6lkcwgd76tfnxksfk2atyy4tzw4nyg6jrx3282s2ygvcxyj64gevhxsjk\
1240+
2ymhzv3e0p5h5u3kfey92jt9ge44gsfnwycxynm2g3unw3ntt9qh25texe98jcfhxvcxuezxw9tngwrndpy9s4\
1241+
p4x9eyze2tfe9rxm68tp5yj5jfduen2nny8prhsm6edegn2stww4n4gwp4vfjkvdthd43524n9fa8h262vwesk\
1242+
g66nw3vnyafn29zhsvfeg9mxummtfp35uumzfqmhy3jwgdh55mt5xpvhgmjn25uku5e5g939wmmnvdfygnrdgd\
1243+
h56uzcx4a92vfhgdcky3z9gfnrsvp4f4f55j68vak9yufhvdm8x5zrgc6955jvf429zumv89nh2a35wae5yntg\
1244+
v985jumpxehyv7t92pjrwufs89yh23f5ddy5s568wgchve3cg9ek5nzewgcrzjz0dftxg3nvf4hngje52ac4zm\
1245+
esxpvk6sfef4hkuetvd4vk6n29wftrw5rvg4yy2vjjwyexc5mnvfd8xknndpqkkenx0q642j35298hwve3dyc5\
1246+
25jrd3295sm9v9jrqup3wpykg7zd239ns7jgtqu95jz0deaxksjh2fu56n6n2f5x6mm8wa89qjfef385sam2x9\
1247+
mxcs20gfpnq460d3axzknnf3e4sw2kvf25wjjxddpyg52dw4vx7nn2w9cyu5t8vfnyxjtpg33kssjp24ch536p\
1248+
d938snmtx345x6r4x93kvv2tff855um3tfekxjted4kxys2kve5hvu6g89z4ynmjgfhnw7tv892rymejgvey77\
1249+
rcfqe9xjr92d85636fvajxyajndfa92k2nxycx5jtjx4zxsm2y2dyn2up50f5ku3nrfdk4g5npxehkzjjv8y69\
1250+
gveev4z56denddaxy7tfwe8xx42zgf6kzmnxxpk826ze2s6xk6jrwearw6ejvd8rsvj2fpg525jtd5pp5j2tlt\
1251+
28m4kakjr84w6ce4fd8e7awy6ncyswcyut760rdnem30ptssp5p5u3xgxxtr6aev8y2w9m30wcw3kyn7fgm8wm\
1252+
f8qw8wzrqt34zcvq9q2sqqqqqysgqcqypmw9xq8lllllllnp4qt36twam2ca08m3s7vnhre3c0j89589wyw4vd\
1253+
k7fln0lryxzkdcrur28qwqq3hnyt84vsasuldd2786eysdf4dyuggwsmvw2atftf7spkmpa9dd3efq5tenpqm2\
1254+
v7vcz2a4s0s7jnqpjn0srysnstnw5y5z9taxn0ue37aqgufxcdsj6f8a2m4pm9udppdzc4shsdqzzx0u0rm4xl\
1255+
js0dqz3c5zqyvglda7nsqvqfztmlyup7vyuadzav4zyuqwx90ev6nmk53nkhkt0sev9e745wxqtdvrqzgqkaka\
1256+
zen7e2qmsdauk665g3llg5qtl79t3xulrhjnducehdn72gpmkjvtth7kh6ejpl9dv0qcsxv2jvzzvg0hzdmk3y\
1257+
jsmydqksdk3h78kc63qnr265h8vyeslqexszppfm7y287t3gxvhw0ulg2wp0rsw3tevz03z50kpy77zdz9snxm\
1258+
kkwxd76xvj4qvj2f89rrnuvdvzw947ay0kydc077pkec2jet9qwp2tud98s24u65uz07eaxk5jk3e4nggn2caa\
1259+
ek2p5pkrc6mm6mxjm2ezpdu8p5jstg6tgvnttgac3ygt5ys04t4udujzlshpl7e4f3ff03xe6v24cp6aq4wa"
1260+
);
1261+
// 1870 B testnet invoice from Lexe proptest
1262+
parse_ok(
1263+
"lntb5826417333454665580p1c5rwh5edlhf33hvkj5vav5z3t02a5hxvj3vfv5kuny2f3yzj6zwf9hx3nn2fk\
1264+
9gepc2a3ywvj6dax5v3jy2d5nxmp3gaxhycjkv38hx4z4d4vyznrp2p24xa6t2pg4w4rrxfens6tcxdhxvvfhx\
1265+
a8xvvpkgat8xnpe2p44juz9g43hyur00989gvfhwd2kj72wfum4g4mgx5m5cs2rg9d9vnn6xe89ydnnvfpyy52\
1266+
s2dxx2er4x4xxwstdd5cxwdrjw3nkxnnv2uexxnrxw4t56sjswfn52s2xv4t8xmjtwpn8xm6sfeh4q526dyu8x\
1267+
3r9gceyw6fhd934qjttvdk57az5w368zdrhwfjxxu35xcmrsmmpd4g8wwtev4tkzutdd32k56mxveuy6c6v2em\
1268+
yv7zkfp39zjpjgd8hx7n4xph5kceswf6xxmnyfcuxca20fp24z7ncvfhyu5jf2exhw36nwf68s7rh2a6yzjf4d\
1269+
gukcenfxpchqsjn2pt5x334tf98wsm6dvcrvvfcwapxvk2cdvmk2npcfe68zue3w4f9xc6s2fvrw6nrg3fkskt\
1270+
e2ftxyc20ffckcd692964sdzjwdp4yvrfdfm9q72pxp3kwat5f4j9xee5da8rss60w92857tgwych55f5w3n8z\
1271+
mzexpy4jwredejrqm6txf3nxm64ffh8x460dp9yjazhw4yx6dm5xerysnn5wa455k3h2d89ss2fd9axwjp3f4r\
1272+
9qdmfd4fx6stx2eg9sezrv369w7nvvfvhj4nnwaz5z3ny8qcxcdnvwd64jc2nx9uy2e2gxdrnx6r3w9ykxatxx\
1273+
g6kk6rv2ekr2emwx5ehy362d3x82dzvddfxs5rcg4vn27npf564qdtg2anycc6523jnwe3e0p65unrpvccrs5m\
1274+
2fuexgmnj23ay5e34v4xk5jnrwpg4xemfwqe5vjjjw9qk76zsd9yrzu6xdpv5v5ntdejxg6jtv3kx65t6gdhrg\
1275+
vj3fe34sj2vv3h5kegpp57hjf5kv6clw97y2e063yuz0psrz9a6l49v836dflum00rh8qtn8qsp5gd29qycuze\
1276+
08xls8l32zjaaf2uqv78v97lg9ss0c699huw980h2q9q2sqqqqqysgqcqr8ulnp4q26hcfwr7qxz7lwwlr2kjc\
1277+
rws7m2u5j36mm0kxa45uxy6zvsqt2zzfppjdkrm2rlgadt9dq3d6jkv4r2cugmf2kamr28qwuleyzzyyly8a6t\
1278+
u70eldahx7hzxx5x9gms7vjjr577ps8n4qyds5nern39j0v7czkch2letnt46895jupxgehf208xgxz8d6j8gu\
1279+
3h2qqtsk9nr9nuquhkqjxw40h2ucpldrawmktxzxdgtkt9a3p95g98nywved8s8laj2a0c98rq5zzdnzddz6nd\
1280+
w0lvr6u0av9m7859844cgz9vpeq05gw79zqae2s7jzeq66wydyueqtp56qc67g7krv6lj5aahxtmq4y208q5qy\
1281+
z38cnwl9ma6m5f4nhzqaj0tjxpfrk4nr5arv9d20lvxvddvffhzygmyuvwd959uhdcgcgjejchqt2qncuwpqqk\
1282+
5vws7dflw8x6esrfwhz7h3jwmhevf445k76nme926sr8drsdveqg7l7t7lnjvhaludqnwk4l2pmevkjf9pla92\
1283+
4p77v76r7x8jzyy7h59hmk0lgzfsk6c8dpj37hssj7jt4q7jzvy8hq25l3pag37axxanjqnq56c47gpgy6frsy\
1284+
c0str9w2aahz4h6t7axaka4cwvhwg49r6qgj8kwz2mt6vcje25l9ekvmgq5spqtn"
1285+
);
1286+
}
1287+
1288+
// Generate a valid invoice of `MAX_LENGTH` bytes and ensure that it roundtrips.
1289+
#[test]
1290+
fn test_serde_long_invoice() {
1291+
use crate::TaggedField::*;
1292+
use crate::{
1293+
Bolt11Invoice, Bolt11InvoiceFeatures, Bolt11InvoiceSignature, Currency,
1294+
PositiveTimestamp, RawBolt11Invoice, RawDataPart, RawHrp, RawTaggedField, Sha256,
1295+
SignedRawBolt11Invoice,
1296+
};
1297+
use bitcoin::secp256k1::ecdsa::{RecoverableSignature, RecoveryId};
1298+
use bitcoin::secp256k1::PublicKey;
1299+
use lightning_types::routing::{RouteHint, RouteHintHop, RoutingFees};
1300+
1301+
// Generate an `UnknownSemantics` field with a given length.
1302+
fn unknown_semantics_field(len: usize) -> Vec<Fe32> {
1303+
assert!(len <= 1023);
1304+
let mut field = Vec::with_capacity(len + 3);
1305+
// Big-endian encoded length prefix
1306+
field.push(Fe32::Q);
1307+
field.push(Fe32::try_from((len >> 5) as u8).unwrap());
1308+
field.push(Fe32::try_from((len & 0x1f) as u8).unwrap());
1309+
// Data
1310+
field.extend(std::iter::repeat(Fe32::P).take(len));
1311+
field
1312+
}
1313+
1314+
// Invoice fields
1315+
let payment_hash = sha256::Hash::from_str(
1316+
"0001020304050607080900010203040506070809000102030405060708090102",
1317+
)
1318+
.unwrap();
1319+
let description = std::iter::repeat("A").take(639).collect::<String>();
1320+
let fallback_addr = crate::Fallback::SegWitProgram {
1321+
version: bitcoin::WitnessVersion::V0,
1322+
program: vec![0; 32],
1323+
};
1324+
let payee_pk = PublicKey::from_slice(&[
1325+
0x03, 0x24, 0x65, 0x3e, 0xac, 0x43, 0x44, 0x88, 0x00, 0x2c, 0xc0, 0x6b, 0xbf, 0xb7,
1326+
0xf1, 0x0f, 0xe1, 0x89, 0x91, 0xe3, 0x5f, 0x9f, 0xe4, 0x30, 0x2d, 0xbe, 0xa6, 0xd2,
1327+
0x35, 0x3d, 0xc0, 0xab, 0x1c,
1328+
])
1329+
.unwrap();
1330+
let route_hints = std::iter::repeat(RouteHintHop {
1331+
src_node_id: payee_pk,
1332+
short_channel_id: 0x0102030405060708,
1333+
fees: RoutingFees { base_msat: 1, proportional_millionths: 20 },
1334+
cltv_expiry_delta: 3,
1335+
htlc_minimum_msat: None,
1336+
htlc_maximum_msat: None,
1337+
})
1338+
.take(12)
1339+
.collect::<Vec<_>>();
1340+
1341+
// Build raw invoice
1342+
let raw_invoice = RawBolt11Invoice {
1343+
hrp: RawHrp {
1344+
currency: Currency::Bitcoin,
1345+
raw_amount: Some(10000000000000000010),
1346+
si_prefix: Some(crate::SiPrefix::Pico),
1347+
},
1348+
data: RawDataPart {
1349+
timestamp: PositiveTimestamp::from_unix_timestamp(1496314658).unwrap(),
1350+
tagged_fields: vec![
1351+
PaymentHash(Sha256(payment_hash)).into(),
1352+
Description(crate::Description::new(description).unwrap()).into(),
1353+
PayeePubKey(crate::PayeePubKey(payee_pk)).into(),
1354+
ExpiryTime(crate::ExpiryTime(std::time::Duration::from_secs(u64::MAX))).into(),
1355+
MinFinalCltvExpiryDelta(crate::MinFinalCltvExpiryDelta(u64::MAX)).into(),
1356+
Fallback(fallback_addr).into(),
1357+
PrivateRoute(crate::PrivateRoute(RouteHint(route_hints))).into(),
1358+
PaymentSecret(crate::PaymentSecret([17; 32])).into(),
1359+
PaymentMetadata(vec![0x69; 639]).into(),
1360+
Features(Bolt11InvoiceFeatures::from_le_bytes(vec![0xaa; 639])).into(),
1361+
// This invoice is 4458 B w/o unknown semantics fields.
1362+
// Need to add some non-standard fields to reach 7089 B limit.
1363+
RawTaggedField::UnknownSemantics(unknown_semantics_field(1023)),
1364+
RawTaggedField::UnknownSemantics(unknown_semantics_field(1023)),
1365+
RawTaggedField::UnknownSemantics(unknown_semantics_field(576)),
1366+
],
1367+
},
1368+
};
1369+
1370+
// Build signed invoice
1371+
let hash = [
1372+
0x75, 0x99, 0xe1, 0x51, 0x7f, 0xa1, 0x0e, 0xb5, 0xc0, 0x79, 0xb4, 0x6e, 0x8e, 0x62,
1373+
0x0c, 0x4f, 0xb0, 0x72, 0x71, 0xd2, 0x81, 0xa1, 0x92, 0x65, 0x9c, 0x90, 0x89, 0x69,
1374+
0xe1, 0xf3, 0xd6, 0x59,
1375+
];
1376+
let signature = &[
1377+
0x6c, 0xbe, 0xbe, 0xfe, 0xd3, 0xfb, 0x07, 0x68, 0xb5, 0x79, 0x98, 0x82, 0x29, 0xab,
1378+
0x0e, 0xcc, 0x8d, 0x3a, 0x81, 0xee, 0xee, 0x07, 0xb3, 0x5d, 0x64, 0xca, 0xb4, 0x12,
1379+
0x33, 0x99, 0x33, 0x2a, 0x31, 0xc2, 0x2c, 0x2b, 0x62, 0x96, 0x4e, 0x37, 0xd7, 0x96,
1380+
0x50, 0x5e, 0xdb, 0xe9, 0xa9, 0x5b, 0x0b, 0x3b, 0x87, 0x22, 0x89, 0xed, 0x95, 0xf1,
1381+
0xf1, 0xdf, 0x2d, 0xb6, 0xbd, 0xf5, 0x0a, 0x20,
1382+
];
1383+
let signature = Bolt11InvoiceSignature(
1384+
RecoverableSignature::from_compact(signature, RecoveryId::from_i32(1).unwrap())
1385+
.unwrap(),
1386+
);
1387+
let signed_invoice = SignedRawBolt11Invoice { raw_invoice, hash, signature };
1388+
1389+
// Ensure serialized invoice roundtrips
1390+
let invoice = Bolt11Invoice::from_signed(signed_invoice).unwrap();
1391+
let invoice_str = invoice.to_string();
1392+
assert_eq!(invoice_str.len(), crate::MAX_LENGTH);
1393+
assert_eq!(invoice, Bolt11Invoice::from_str(&invoice_str).unwrap());
1394+
}
1395+
1396+
// Test that invoices above the maximum length fail to parse with the expected error.
1397+
#[test]
1398+
fn test_deser_too_long_fails() {
1399+
use crate::{Bolt11Invoice, ParseOrSemanticError, MAX_LENGTH};
1400+
use bech32::primitives::decode::{CheckedHrpstringError, ChecksumError};
1401+
1402+
fn parse_is_code_length_err(s: &str) -> bool {
1403+
// Need matches! b/c ChecksumError::CodeLength(_) is marked non-exhaustive
1404+
matches!(
1405+
Bolt11Invoice::from_str(s),
1406+
Err(ParseOrSemanticError::ParseError(Bolt11ParseError::Bech32Error(
1407+
CheckedHrpstringError::Checksum(ChecksumError::CodeLength(_))
1408+
))),
1409+
)
1410+
}
1411+
1412+
let mut too_long = String::from("lnbc1");
1413+
too_long.push_str(
1414+
String::from_utf8(vec![b'x'; (MAX_LENGTH + 1) - too_long.len()]).unwrap().as_str(),
1415+
);
1416+
assert!(parse_is_code_length_err(&too_long));
1417+
assert!(!parse_is_code_length_err(&too_long[..too_long.len() - 1]));
1418+
}
11781419
}

0 commit comments

Comments
 (0)