Skip to content

BOLT12 fixes - #2

Closed
m0wer wants to merge 45 commits into
f321x:bolt12_2from
m0wer:bolt12_2_fixes
Closed

m0wer wants to merge 45 commits into
f321x:bolt12_2from
m0wer:bolt12_2_fixes

Conversation

@m0wer

@m0wer m0wer commented May 27, 2026

Copy link
Copy Markdown

Moved the two fixes that still apply from accumulator#3 The others had been addressed already!

accumulator and others added 30 commits May 22, 2026 15:09
…ation and schnorr-sign over tlvs,

implicit en/decode utf8 fields, schnorr signature verification.

Co-Authored-By: f321x <f@f321x.com>
Validate points to be valid ECPubkeys in lnmsg._read_primitive_field.
There are failing bolt12 test vectors that contain offers with invalid
points.
Separates the payinfo creation from get_blinded_paths_to_me into a separate
function.
Handle missing remote channel_update when creating payinfo
for blinded path by skipping the affected channel.
Ignore channel amount constraints when doing pathfinding for an
onion message. Onion messages don't need to move funds so pathfinding
shouldn't penalize channels based on fake payment amounts.
Filter channels/peers in `get_blinded_paths_to_me` depending on
the context (onion message or blinded payment path). Raise
according exceptions when no channel/peer is available.
move decryption of recipient_data to process_onion_packet,
add handling of blinding in peer msgs,
handle properties in ProcessedOnionPacket in case of path blinding,
move repeated next blinding key derivation to func lnonion.next_blinding_from_shared_secret()

Co-Authored-By: f321x <f@f321x.com>
…EATURES

... and lnutil.LN_FEATURES_IMPLEMENTED
when we have tried all blinded paths and for none of them we could find a route
and we have a network address available for the destination (either node_id
or blinded path introduction point).
Adds a separate method to `Peer` specifically for sending
onion messages.

This allows to 'enqueue' a (onion) message send before the peer
is initialized without having to externalize waiting for init from `Peer`.
This way we can establish a peer connection in `OnionMessageManager` and
immediately fire an onion message on the `Peer`.
Using the Queue also prevents the timing/retry logic in `OnionMessageManager` from
breaking down if a single peer (of multiple available ones) takes a long time
to send for whatever reason.

Also creates a simpler API for callers (they don't have to pass
a message_name and packet len).
Only catch UserFacingException. The scope of Exception is way too large,
this caught all kinds of exceptions from LNWallet etc.
It is not useful to show the user an error popup with e.g. "KeyError".
Introduce dataclasses for blinded path related structures.
This allows safer and cleaner handling.

- BlindedPathHop
- BlindedPath
- BlindedPayInfo
- BlindedPathInfo
Introduce typed dataclass hierarchy for BOLT12 objects:
- BOLT12Offer, BOLT12InvoiceRequest, BOLT12Invoice
- validation in __post_init__() methods
- helper functions
- unittests (test_bolt12.py)

Co-Authored-By: Sander van Grieken <sander@outrightsolutions.nl>
Adds unittest to check if we handle malformed bolt12 strings according
to the specification using the format-string-test.json test vector.
https://github.com/lightning/bolts/blob/5f31faa0b6e2cdbe32171d79464305f90bda9585/bolt12/format-string-test.json
Tests BOLT12Offer validation against the test vector json from the
bolts repository.
Co-Authored-By: f321x <f@f321x.com>
Implements methods in LNWallet to create and send invoice_requests,
handles incoming invoices. Supports both offerless and offer based
invreqs
Add unittests for the invoice_request creation and invoice handling.
Store BOLT12 invoices as bech32-encoded strings (lni...) in the
lightning_invoice field of the persisted Invoice class.
Decode on demand and cache instance via cached_property.

The `Invoice` can act as sort of abstraction over bolt11 and bolt12
invoices for the GUI.

Co-Authored-By: Sander van Grieken <sander@outrightsolutions.nl>
Add unnittests for bolt12 payment identifier.
Implement `calc_hops_data_for_blinded_payment`, which takes
a route to the introduction point of a blinded payment as well as
the blinded path and assembles the hop payloads for the whole payment
(unblinded path + blinded path).

Co-Authored-By: f321x <f@f321x.com>
Introduce RoutingInfo class to make the lightning payment
flow (LNWallet.pay_invoice and following) less specific to bolt11
but act on generic `RoutingInfo`.
Adapt the LNWallet payment flow to handle blinded/bolt12 payments.
Paying via Trampoline is added in a future commit.

Co-Authored-By: Sander van Grieken <sander@outrightsolutions.nl>
ecdsa and others added 9 commits May 22, 2026 15:18
Add method to substract fees required for blinded path from a
PaymentFeeBudget to calculate the remaining budget for the unblinded
path.
Eclair interprets the invoice_features tlv for legacy trampoline
payments as bytes, potentially this u64 conversion is outdated?
This changes the invoice_features tlv to bytes type.

See eclair source (grep "invoiceFeatures").
https://github.com/ACINQ/eclair/blob/a4d66adce2ec180c94532d1d0014f86f6e74f607/eclair-core/src/main/scala/fr/acinq/eclair/wire/protocol/PaymentOnion.scala#L638
Update the trampoline payment logic in LNWallet to handle payments
to/through blinded paths.
This allows for bolt 12 trampoline payments.

Also stops including a trampoline onion layer for the recipient of
a legacy trampoline payment. A non-trampoline aware (legacy) receiver
cannot read this onion and it is not required for Eclair compatibility
anymore.
Co-Authored-By: f321x <f@f321x.com>
Co-Authored-By: f321x <f@f321x.com>
The LRU cache for processed onions keyed entries on
onion_hash + payment_hash + is_trampoline only. With route blinding a
receiver may advertise multiple blinded paths for the same payment;
two incoming HTLCs sharing onion_hash and payment_hash but using
different path_key bytes would otherwise be served the same cached
result. Including path_key in the key removes the ambiguity.

Also drops a commented-out debug line from the bolt12 regtest case.
UpdateAddHtlc.to_json now produces a 6-tuple (with the trailing
'path_key' field) instead of the previous 5-tuple. The new code reads
either shape, but older electrum versions call from_tuple(*tuple)
positionally and would raise TypeError on a 6-tuple entry.

Bumping FINAL_SEED_VERSION ensures older clients refuse to open wallets
that may now contain blinded HTLC entries, rather than failing
mid-deserialization with an unclear error.
@f321x
f321x force-pushed the bolt12_2 branch 5 times, most recently from 82edaab to 9195022 Compare June 3, 2026 10:10
@f321x
f321x force-pushed the bolt12_2 branch 7 times, most recently from c8b41e3 to d41111b Compare June 9, 2026 09:55
@f321x
f321x force-pushed the bolt12_2 branch 3 times, most recently from cc45ddc to 5419870 Compare June 25, 2026 12:35
@f321x
f321x force-pushed the bolt12_2 branch 3 times, most recently from 2801209 to 06700f9 Compare July 3, 2026 16:02
@f321x

f321x commented Jul 6, 2026

Copy link
Copy Markdown
Owner

Many things have changed in https://github.com/f321x/electrum-fork/tree/bolt12_2

@f321x f321x closed this Jul 6, 2026
@f321x

f321x commented Jul 6, 2026

Copy link
Copy Markdown
Owner

doing both now in the branch

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants