lightningdevkit / rust-lightning

A highly modular Bitcoin Lightning library written in Rust. It's rust-lightning, not Rusty's Lightning!
Other
1.14k stars 357 forks source link

Cleanup Bech32 logic, isolate or make away with bech32 dependency #3195

Open optout21 opened 1 month ago

optout21 commented 1 month ago

Bech32 logic is used for Lightning invoice decoding & encoding, and the bech32 crate is used for that. This analysis is triggered by the upgrade from bech32 v0.9.1 to bech32 v0.11.0, during which the library API has changed significantly. The upgrade is being handled by PR #3181 ( #3176 ) .

Here is an analysis of needed/used functionalities, and recommendations:

Conclusion:

Options for location of bech32 logic:

tcharding commented 1 month ago

I had a bit of a play with both the PRs aimed at closing this issue, unless I'm missing something I don't see any reason to keep your custom u5 type. Of course you could elect to not use bech32 at all but I think that would be seen by us (the authors of bech32 as a failure by us). It looks to me like you should be able to do everything you want with the new crate after https://github.com/rust-bitcoin/rust-bech32/pull/189 and https://github.com/rust-bitcoin/rust-bech32/pull/190 are merged and released.

Admittedly the iterator api code is a bit gnarly to read, I'm happy to help if you get stuck.

apoelstra commented 1 month ago

We can add these if they make derives more convenient (I think Default makes sense, at least as much as it makes sense for every std numeric type, though Ord does not, hence us using ArbitraryOrd). But I'm curious why you need an ordering for elements of an unordered field?

For that matter I'm curious where you're directly using field elements at all.

apoelstra commented 1 month ago

Reading through the codebase it looks like you have a ton of custom checksummed-data-parsing code that should be able to be removed entirely and replaced with calls into the new API, and you shouldn't ever need to deal with FEs.

apoelstra commented 1 month ago

Ah, ok, because LN invoices have fields which are 5-bit aligned you probably do want to work with Fes, at least during parsing and serialization. But I don't think you ever need to store them at rest.

apoelstra commented 1 month ago

@optout21 so, after having worked on this for a few days I've nearly converted the serialization logic to the new bech32 library. I think this issue is really nontrivial. Essentially, the LN invoice format is a collection of fields which are ad-hoc aligned on 5-bit and 8-bit boundaries, and in the old bech32 API (which provided no assistance with conversions) you were expected to just "produce a slice of u5s somehow".

This results in the following issues:

So if you naively try to translate the old logic to the new one by just replacing types, you'll basically take an existing huge mess and kinda smear it around. What you need to do is (a) experiment a bit with the new iterator-based API (which probably needs much more extensive doccomments; I hadn't considered something as elaborate as the LN invoice format when I wrote that), and (b) identify all the existing places where bech32 logic is re-implemented inline in rust-lighting, and (c) rewrite all the serialization logic.

As I said, I'm nearly finish this on the serialization side. I haven't looked at deserialization yet. Will try to open a PR today or tomorrow, though I'm a bit behind on other stuff since the recent rust-bitcoin summit.

optout21 commented 1 month ago

But I'm curious why you need an ordering for elements of an unordered field?

Ordering would have been needed for the tags in a bech32-encoded lightning invoice, each tag has a 32-bit tag value (usually denoted by their Bech32 char representation), and they are kept in a map, they are ordered, etc.

BTW, here the 32-bit elements are just that: 32-bit numbers, the field properties are not used.

optout21 commented 1 month ago

So if you naively try to translate the old logic to the new one by just replacing types, you'll basically take an existing huge mess and kinda smear it around.

Thanks for looking into this, @apoelstra , you're right. I've started looking into this as a simple library dependency upgrade, just replacing types, but it turned out to be more... :D After digging deeper, I've changed course, towards minimizing/eliminating rust-bech32 usage. The focus of the library and the needs in rust-lightning are a bit different (e.g. no need for field properties, but need for lightning invoice parsing, which is not in the scope of the rust-bitcoin/rust-bech32 projects). As I see that now there is no open issue/PR on rust-bech32 from me.

apoelstra commented 1 month ago

lightning-invoices have a bech32 checksum on them. This requires field properties and is exactly what rust-bech32 is designed for. As far as I can tell, every other use of u5s in the library is an API mistake that comes from storing and manipulating partially-parsed data.

optout21 commented 1 month ago

Update: Default support has been added (https://github.com/rust-bitcoin/rust-bech32/pull/184), and Ord can be worked-around (needed only by UnknownSemantics).

apoelstra commented 1 month ago

FYI I have a branch where I've been working on this https://github.com/apoelstra/rust-lightning/tree/2024-08--new-bech32

Though it is a bit of a mess; it needs https://github.com/lightningdevkit/rust-lightning/pull/3234