Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Offers: bolt12 updated for latest draft. #5676

Merged
merged 32 commits into from
Nov 9, 2022

Conversation

rustyrussell
Copy link
Contributor

In some places this is a minimal implementation, and it's still experimental, but it Works For Me!

First three patches make sure we track the latest route blinding spec.

The offers spec changed so much that I'm afraid "rewrite" is more appropriate, particularly bolt12: import the latest spec, update to fit. which is pretty hard to split further. Sorry.

@rustyrussell rustyrussell added this to the v22.11 milestone Oct 26, 2022
Copy link
Member

@cdecker cdecker left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got up to f7e350e, after the dreaded monster commit, but then my eyes glazed over.

One thing I noticed is the splitting of the onion code, and I was wondering if it has any actual impact on compilation time, or binary size in the end, that'd warrant the big code move.

common/onion.c Show resolved Hide resolved
plugins/offers.c Outdated Show resolved Hide resolved
@rustyrussell
Copy link
Contributor Author

Got up to f7e350e, after the dreaded monster commit, but then my eyes glazed over.

One thing I noticed is the splitting of the onion code, and I was wondering if it has any actual impact on compilation time, or binary size in the end, that'd warrant the big code move.

I split it because I ended up dragging in too much, IIRC. OTOH the blinding code was developed in pieces, but it may want further rationalization.

The onion code has multiple files which I find confusing (sphinx, onion, all the blinded* parts) which may want a cleanup now we have removed legacy onion support.

@rustyrussell
Copy link
Contributor Author

Updated rust build, and fixed the missing API update noted by @cdecker.

@cdecker
Copy link
Member

cdecker commented Oct 30, 2022

Gotcha, I was thinking there might be a performance reason to do so that I was unaware, but I understand the desire of a cleaner separation, given the onion and sphinx code is about the earliest code I wrote, and didn't have a good idea on how to structure things yet 😋

Copy link
Member

@cdecker cdecker left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Squashed fixups, and eyes glazed over, but looks ok.

ACK cb83675

lightningd/offer.c Show resolved Hide resolved
@cdecker
Copy link
Member

cdecker commented Nov 1, 2022

There are some missing schemas for the new RPC methods, that would have helped interpret the meaning of the new RPC methods too. Worth following up with a cleanup PR.

@rustyrussell
Copy link
Contributor Author

rustyrussell commented Nov 2, 2022

Trivial rebase on master (weird CI issues?) and remove final commit (enhances cppcheck, but now we need more annotations...)

@rustyrussell rustyrussell force-pushed the guilt/offers-updates branch 2 times, most recently from 4049649 to 30efcf2 Compare November 2, 2022 04:40
@cdecker cdecker force-pushed the guilt/offers-updates branch 2 times, most recently from c078eea to 0f329ff Compare November 2, 2022 11:04
@cdecker
Copy link
Member

cdecker commented Nov 2, 2022

Rebased, merging as soon as CI is happy.

ACK 0f329ff

cdecker
cdecker previously requested changes Nov 2, 2022
Copy link
Member

@cdecker cdecker left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems I was celebrating prematurely. There seems to be a use of uninitialized memory that reliably kills the htlc_accepted hook:

Valgrind error file: valgrind-errors.68291
==68291== Conditional jump or move depends on uninitialised value(s)
==68291==    at 0x179003: htlc_accepted_hook_serialize (peer_htlcs.c:1080)
==68291==    by 0x18714C: plugin_hook_call_next (plugin_hook.c:242)
==68291==    by 0x187333: plugin_hook_call_ (plugin_hook.c:279)
==68291==    by 0x1795B8: plugin_hook_call_htlc_accepted (peer_htlcs.c:1195)
==68291==    by 0x179E92: peer_accepted_htlc (peer_htlcs.c:1407)
==68291==    by 0x17D26B: peer_got_revoke (peer_htlcs.c:2412)
==68291==    by 0x1307F9: channel_msg (channel_control.c:535)
==68291==    by 0x18AF2C: sd_msg_read (subd.c:557)
==68291==    by 0x2A600A: next_plan (io.c:59)
==68291==    by 0x2A6C12: do_plan (io.c:407)
==68291==    by 0x2A6C54: io_ready (io.c:417)
==68291==    by 0x2A8F47: io_loop (poll.c:453)
==68291== 
{
   <insert_a_suppression_name_here>
   Memcheck:Cond
   fun:htlc_accepted_hook_serialize
   fun:plugin_hook_call_next
   fun:plugin_hook_call_
   fun:plugin_hook_call_htlc_accepted
   fun:peer_accepted_htlc
   fun:peer_got_revoke
   fun:channel_msg
   fun:sd_msg_read
   fun:next_plan
   fun:do_plan
   fun:io_ready
   fun:io_loop
}

This refers to the check here that checks if p->payload->forward_node_id is not NULL, which it is, and the non-valgrind nodes then crash one line below when we try to encode the value as a JSON pubkey.

Most likely it is just a missing initialization of the forward_node_id in the hook payload creation, but I'll let @rustyrussell do that since he has more context here.

@rustyrussell
Copy link
Contributor Author

Seems I was celebrating prematurely. There seems to be a use of uninitialized memory that reliably kills the htlc_accepted hook:

Valgrind error file: valgrind-errors.68291
==68291== Conditional jump or move depends on uninitialised value(s)
==68291==    at 0x179003: htlc_accepted_hook_serialize (peer_htlcs.c:1080)
==68291==    by 0x18714C: plugin_hook_call_next (plugin_hook.c:242)
==68291==    by 0x187333: plugin_hook_call_ (plugin_hook.c:279)
==68291==    by 0x1795B8: plugin_hook_call_htlc_accepted (peer_htlcs.c:1195)
==68291==    by 0x179E92: peer_accepted_htlc (peer_htlcs.c:1407)
==68291==    by 0x17D26B: peer_got_revoke (peer_htlcs.c:2412)
==68291==    by 0x1307F9: channel_msg (channel_control.c:535)
==68291==    by 0x18AF2C: sd_msg_read (subd.c:557)
==68291==    by 0x2A600A: next_plan (io.c:59)
==68291==    by 0x2A6C12: do_plan (io.c:407)
==68291==    by 0x2A6C54: io_ready (io.c:417)
==68291==    by 0x2A8F47: io_loop (poll.c:453)
==68291== 
{
   <insert_a_suppression_name_here>
   Memcheck:Cond
   fun:htlc_accepted_hook_serialize
   fun:plugin_hook_call_next
   fun:plugin_hook_call_
   fun:plugin_hook_call_htlc_accepted
   fun:peer_accepted_htlc
   fun:peer_got_revoke
   fun:channel_msg
   fun:sd_msg_read
   fun:next_plan
   fun:do_plan
   fun:io_ready
   fun:io_loop
}

This refers to the check here that checks if p->payload->forward_node_id is not NULL, which it is, and the non-valgrind nodes then crash one line below when we try to encode the value as a JSON pubkey.

Most likely it is just a missing initialization of the forward_node_id in the hook payload creation, but I'll let @rustyrussell do that since he has more context here.

Fixed, thanks!

@rustyrussell
Copy link
Contributor Author

Fixed another uninitialized variable in debug logging too.

@cdecker
Copy link
Member

cdecker commented Nov 8, 2022

Thanks for the fix. It now just has one failure where the error does not match the expectation:

        with pytest.raises(RpcError, match="INCORRECT_OR_UNKNOWN_PAYMENT_DETAILS.*'erring_node': '{}'".format(l3.info['id'])):
>           l1.rpc.pay(inv2['invoice'])

Fails with this error instead:

>           raise RpcError(method, payload, resp['error'])
E           pyln.client.lightning.RpcError: RPC call failed: method: pay, payload: {'bolt11': 'lni1qqgf42den6cwzacntjjz66uvn7tkjq3qqc3xu3s3rg94nj40zfsy866mhu5vxne6tcej5878k2mneuvgjy8ssqgppg8hx6twvakx2tt4wdjjqar9wd6pvggrt543ryklhgf5uy89gzr46dnwhj9ux5744fmxhqxqjzeecwja3pw4qgqxyfhyvyg6pdvu4tcjvpp7kkal9rp57wj7xv4pl3ajku70rzy3pavzzq3gfvfwydjehktxec99tanr7tvvgt9g8gjcyxuggevt3nvus9p43jsfsq6a9vge9ha6zd8ppe2qsawnvm4u30p484d2we4cpsyskwwr5hvgt5pw5drhhvv8dgrzk6ee99nlnvddejuagzs5jaez6x3tqvjh4c8r0lqpqd5l2v3fz9pmzd733zrj3phe5vakljw5awn8g6zm0wv02ulre4gujqpjzgrcaqpg8w0e3xxahn35w6n2m7p6u3zuumxyhvlzp7a58gvcys9gpa7fg90x9vgucqjcee6yspfs9u8l5gwqqqqqqqqqqqqqqqzsqqqqqqqqqqqqr5jt9hav2gqqqqqq5szxx6wdj75zqjz9pdj24nt2ecjl776nyrxxpqq6y8ay3lk33j7duxmff2vz2fax4gqsrvppqdwjkyvjm7apxnssu4qgwhfkd67ghs6n6k48v6uqczgt88p6tky9muzq9qr62gcx0wwn55q86s594u9zd6x07snpkj2zwfv85gg2q7m5e050vmxewmp4k6qt6rtrhun2kphkdwzla6wgycc5uspqwtaeh98qf9s'}, error: {'code': 203, 'message': 'failed: WIRE_INVALID_ONION_BLINDING (reply from remote)', 'id': 6, 'failcode': 49176, 'failcodename': 'WIRE_INVALID_ONION_BLINDING', 'bolt12': 'lni1qqgf42den6cwzacntjjz66uvn7tkjq3qqc3xu3s3rg94nj40zfsy866mhu5vxne6tcej5878k2mneuvgjy8ssqgppg8hx6twvakx2tt4wdjjqar9wd6pvggrt543ryklhgf5uy89gzr46dnwhj9ux5744fmxhqxqjzeecwja3pw4qgqxyfhyvyg6pdvu4tcjvpp7kkal9rp57wj7xv4pl3ajku70rzy3pavzzq3gfvfwydjehktxec99tanr7tvvgt9g8gjcyxuggevt3nvus9p43jsfsq6a9vge9ha6zd8ppe2qsawnvm4u30p484d2we4cpsyskwwr5hvgt5pw5drhhvv8dgrzk6ee99nlnvddejuagzs5jaez6x3tqvjh4c8r0lqpqd5l2v3fz9pmzd733zrj3phe5vakljw5awn8g6zm0wv02ulre4gujqpjzgrcaqpg8w0e3xxahn35w6n2m7p6u3zuumxyhvlzp7a58gvcys9gpa7fg90x9vgucqjcee6yspfs9u8l5gwqqqqqqqqqqqqqqqzsqqqqqqqqqqqqr5jt9hav2gqqqqqq5szxx6wdj75zqjz9pdj24nt2ecjl776nyrxxpqq6y8ay3lk33j7duxmff2vz2fax4gqsrvppqdwjkyvjm7apxnssu4qgwhfkd67ghs6n6k48v6uqczgt88p6tky9muzq9qr62gcx0wwn55q86s594u9zd6x07snpkj2zwfv85gg2q7m5e050vmxewmp4k6qt6rtrhun2kphkdwzla6wgycc5uspqwtaeh98qf9s', 'raw_message': 'c0185fbe64cb00087d99ba487ce474cb3efaa18ad05412c0ffe004607df0a4ddc051', 'created_at': 1667878297, 'destination': '035d2b1192dfba134e10e540875d366ebc8bc353d5aa766b80c090b39c3a5d885d', 'payment_hash': '48450b64aacd6ace25ff7b5320cc60801a21fa48fed18cbcde1b694a982527a6', 'status': 'failed', 'amount_msat': 1msat, 'amount_sent_msat': 0msat, 'erring_index': 2, 'erring_node': '035d2b1192dfba134e10e540875d366ebc8bc353d5aa766b80c090b39c3a5d885d'}

So we expect an unknown payment, but get WIRE_INVALID_ONION_BLINDING. @rustyrussell can you confirm that the expected error has changed and we should be expecting the invalid onion blinding instead?

@rustyrussell
Copy link
Contributor Author

rustyrussell commented Nov 9, 2022

Fixed test properly in correct place in series, which lead me to find a place where we'd broken it in another way for one patch.

Now there's a "disable checks" patch in there, which is reverted two patches later. But better than breaking bisect...

@cdecker
Copy link
Member

cdecker commented Nov 9, 2022

Thanks @rustyrussell, git range-diff is here.

ACK 83cfc5f

This makes us match eed2ab0c30ad7f93e3b2641ca9d7ade32f3d121d
("Use `invalid_onion_blinding` everywhere").

1. Numerous typographical changes.
2. Make sure we *always* return WIRE_INVALID_ONION_BLINDING if
   we're in a blinded path.
3. Handle p->total_msat correctly (MPP payments).
4. Reorganize blinding handling just like spec order.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This current spec is not strict enough: we might complain that the
next peer is not connected, for example, which leaks information.

So return WIRE_INVALID_ONION_BLINDING even if we're the first hop
on the path, to be safe.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
We had a scheme where lightningd itself would put a per-node secret in
the blinded path, then we'd tell the caller when it was used.  Then it
simply checks the alias to determine if the correct path was used.

But this doesn't work when we start to offer multiple blinded paths.
So go for a far simpler scheme, where the secret is generated (and
stored) by the caller, and hand it back to them.

We keep the split "with secret" or "without secret" API, since I'm
sure callers who don't care about the secret won't check that it
doesn't exist!  And without that, someone can use a blinded path for a
different message and get a response which may reveal the node.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Instead of doing command_fail() in the else, do it immediately then
unindent the normal path.

No code changes.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
We're going to mess with it in the next patch...

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
…e presented.

The "path" is just a message to ourselves.  This meets the minimal
requirement for bolt12 invoices: that there be a blinded path (at
least so we can use the path_id inside in place of "payment_secret").

We expose the method to make this path_id to a common routine: offers
will need this for generating more sophisticated paths.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
And check it in invoice.c, insead of a hack where we compare against invhash.
Restore checking, too.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This is needed for offers to generate blinded paths.

No documentation changes since listincoming is an undocumented
internal hack interface which topology presents for production
of routehints.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
We need this to create the payment_constraints for an invoice blinded path.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
It's just to a direct peer, and we only create one, but this is
enough to test, and make payments to non-public nodes work.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
The new spec removes the offer_id, in favor of mirroring all the
fields.  So we need a way of generating a convenient identifier to
identify the offer, and this works.

We also want to extract parts of streams elsewhere, so expose that.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
…eqs.

This is an important part of the coming spec: we mirror all fields,
known and unknown.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
The spec only specifies the mpp bit for invoices, but in
general they are separate spaces.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This is in preparation for the spec update where the signature field
does not even exist.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This has radically changed in the spec, so remove it now, and we'll
reintroduce / rewrite it.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
I know this is an unforgivably large diff, but the spec has changed so
much that most of this amounts to a rewrite.

Some points:
* We no longer have "offer_id" fields, we generate that locally, as all
  offer fields are mirrored into invoice_request and then invoice.
* Because of that mirroring, field names all have explicit offer/invreq/invoice
  prefixes.
* The `refund_for` fields have been removed from spec: will re-add locally later.
* quantity_min was removed, max == 0 now mean "must specify a quantity".
* I have put recurrence fields back in locally.

This brings us to 655df03d8729c0918bdacac99eb13fdb0ee93345 ("BOLT 12:
add explicit invoice_node_id.")

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This changed, by popular demands.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
We no longer have to refer back to the offer for which we're making
the invoice_request, or to the invoice_request we made for an invoice,
as they are all mirrored (and we check!).

It's clearer to simply look at the object directly.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
…nstead of towire/fromwire hack.

I forgot this existed!

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This gives us a unique identifier, by which we can match an invoice to
their invoice_request.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
We no longer use offers for "I want to send you money", but we'll use
invoice_requests directly.  Create a new table for them, and
associated functions.

The "localofferid" for "pay" and "sendpay" is now "localinvreqid".
This is an experimental-only option, so document the change under
experimental only.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Changelog-EXPERIMENTAL: JSON-RPC: `pay` and `sendpay` `localofferid` is now `localinvreqid`.
…invoicerequests/disableinvoicerequest

This is how we put new invoice_requests into the db; this will be used
by a new "invoicerequest" command which replaces "offerout".

The API	is now the same as the offers api.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
No code changes.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
And make errors gcc-style, so emacs can jump through the automatically.

```
In devtools/reduce-includes.sh line 21:
	    echo -n "-$LINE"
                 ^-- SC3037 (warning): In POSIX sh, echo flags are undefined.


In devtools/reduce-includes.sh line 25:
	    echo -n "."
                 ^-- SC3037 (warning): In POSIX sh, echo flags are undefined.


In tools/rel.sh line 6:
prefix=$(printf '%s\n' "${from#$common}" | sed 's@[^/][^/]*@..@g')
                               ^-----^ SC2295 (info): Expansions inside ${..} need to be quoted separately, otherwise they match as patterns.

Did you mean: 
prefix=$(printf '%s\n' "${from#"$common"}" | sed 's@[^/][^/]*@..@g')


In tools/rel.sh line 7:
printf '%s\n' "$prefix/${to#$common}"
                            ^-----^ SC2295 (info): Expansions inside ${..} need to be quoted separately, otherwise they match as patterns.

Did you mean: 
printf '%s\n' "$prefix/${to#"$common"}"

For more information:
  https://www.shellcheck.net/wiki/SC3037 -- In POSIX sh, echo flags are undef...
  https://www.shellcheck.net/wiki/SC2295 -- Expansions inside ${..} need to b...
make: *** [Makefile:553: check-shellcheck] Error 123
```

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
@cdecker
Copy link
Member

cdecker commented Nov 9, 2022

Rebased on top of the other merged PRs.

ACK 6c25666

@cdecker cdecker merged commit 2e4a58e into ElementsProject:master Nov 9, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants