-
Notifications
You must be signed in to change notification settings - Fork 994
EXPERIMENTAL: MPP send and receive support (lowlevel) #3309
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
Changes from all commits
178892b
ad4ed97
2a03434
c654478
04a46f0
c61227b
15fa972
3bc4636
94d3897
2e4416e
d94ae31
3c6e33a
555b217
73bf9e0
1839483
8cee375
84a2753
c6bbb41
cbfc84f
2b4ca09
207ae69
e6edb76
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,212 @@ | ||
| #include <common/memleak.h> | ||
| #include <common/timeout.h> | ||
| #include <lightningd/htlc_end.h> | ||
| #include <lightningd/htlc_set.h> | ||
| #include <lightningd/lightningd.h> | ||
| #include <lightningd/peer_htlcs.h> | ||
|
|
||
| #if EXPERIMENTAL_FEATURES | ||
| /* If an HTLC times out, we need to free entire set, since we could be processing | ||
| * it in invoice.c right now. */ | ||
| static void htlc_set_hin_destroyed(struct htlc_in *hin, | ||
| struct htlc_set *set) | ||
| { | ||
| for (size_t i = 0; i < tal_count(set->htlcs); i++) { | ||
| if (set->htlcs[i] == hin) { | ||
| /* Don't try to re-fail this HTLC! */ | ||
| tal_arr_remove(&set->htlcs, i); | ||
| /* Kind of the correct failure code. */ | ||
| htlc_set_fail(set, WIRE_MPP_TIMEOUT); | ||
| return; | ||
| } | ||
| } | ||
| abort(); | ||
| } | ||
|
|
||
| static void destroy_htlc_set(struct htlc_set *set, | ||
| struct htlc_set_map *map) | ||
| { | ||
| htlc_set_map_del(map, set); | ||
| } | ||
|
|
||
| /* BOLT-9441a66faad63edc8cd89860b22fbf24a86f0dcd #4: | ||
| * - MUST fail all HTLCs in the HTLC set after some reasonable | ||
| * timeout. | ||
| * - SHOULD use `mpp_timeout` for the failure message. | ||
| */ | ||
| static void timeout_htlc_set(struct htlc_set *set) | ||
| { | ||
| htlc_set_fail(set, WIRE_MPP_TIMEOUT); | ||
| } | ||
| #endif /* EXPERIMENTAL_FEATURES */ | ||
|
|
||
| void htlc_set_fail(struct htlc_set *set, enum onion_type failcode) | ||
| { | ||
| for (size_t i = 0; i < tal_count(set->htlcs); i++) { | ||
| #if EXPERIMENTAL_FEATURES | ||
| /* Don't remove from set */ | ||
| tal_del_destructor2(set->htlcs[i], htlc_set_hin_destroyed, set); | ||
| #endif | ||
| fail_htlc(set->htlcs[i], failcode); | ||
| } | ||
| tal_free(set); | ||
| } | ||
|
|
||
| void htlc_set_fulfill(struct htlc_set *set, const struct preimage *preimage) | ||
| { | ||
| for (size_t i = 0; i < tal_count(set->htlcs); i++) { | ||
| #if EXPERIMENTAL_FEATURES | ||
| /* Don't remove from set */ | ||
| tal_del_destructor2(set->htlcs[i], htlc_set_hin_destroyed, set); | ||
| #endif | ||
| fulfill_htlc(set->htlcs[i], preimage); | ||
| } | ||
| tal_free(set); | ||
| } | ||
|
|
||
| static struct htlc_set *new_htlc_set(struct lightningd *ld, | ||
| struct htlc_in *hin, | ||
| struct amount_msat total_msat) | ||
| { | ||
| struct htlc_set *set; | ||
|
|
||
| set = tal(ld, struct htlc_set); | ||
| set->total_msat = total_msat; | ||
| set->payment_hash = hin->payment_hash; | ||
| set->so_far = AMOUNT_MSAT(0); | ||
| set->htlcs = tal_arr(set, struct htlc_in *, 1); | ||
| set->htlcs[0] = hin; | ||
|
|
||
| #if EXPERIMENTAL_FEATURES | ||
| /* BOLT-9441a66faad63edc8cd89860b22fbf24a86f0dcd #4: | ||
| * - MUST fail all HTLCs in the HTLC set after some reasonable | ||
| * timeout. | ||
| * - SHOULD wait for at least 60 seconds after the initial | ||
| * HTLC. | ||
| */ | ||
| notleak(new_reltimer(ld->timers, set, time_from_sec(70), | ||
| timeout_htlc_set, set)); | ||
| htlc_set_map_add(&ld->htlc_sets, set); | ||
| tal_add_destructor2(set, destroy_htlc_set, &ld->htlc_sets); | ||
| #endif | ||
| return set; | ||
| } | ||
|
|
||
| void htlc_set_add(struct lightningd *ld, | ||
| struct htlc_in *hin, | ||
| struct amount_msat total_msat, | ||
|
cdecker marked this conversation as resolved.
|
||
| const struct secret *payment_secret) | ||
| { | ||
| struct htlc_set *set; | ||
| const struct invoice_details *details; | ||
|
|
||
| /* BOLT-9441a66faad63edc8cd89860b22fbf24a86f0dcd #4: | ||
| * The final node: | ||
| * - MUST fail the HTLC if dictated by Requirements under | ||
| * [Failure Messages](#failure-messages) | ||
| * - Note: "amount paid" specified there is the `total_msat` field. | ||
| */ | ||
| details = invoice_check_payment(tmpctx, ld, &hin->payment_hash, | ||
| total_msat, payment_secret); | ||
| if (!details) { | ||
| fail_htlc(hin, WIRE_INCORRECT_OR_UNKNOWN_PAYMENT_DETAILS); | ||
| return; | ||
| } | ||
|
|
||
| #if !EXPERIMENTAL_FEATURES | ||
| /* BOLT-9441a66faad63edc8cd89860b22fbf24a86f0dcd #4: | ||
| * - if it does not support `basic_mpp`: | ||
| * - MUST fail the HTLC if `total_msat` is not exactly equal to | ||
| * `amt_to_forward`. | ||
| */ | ||
| if (!amount_msat_eq(hin->msat, total_msat)) { | ||
| fail_htlc(hin, WIRE_FINAL_INCORRECT_HTLC_AMOUNT); | ||
| return; | ||
| } | ||
|
|
||
| /* We create a transient set which just has one entry. */ | ||
| set = new_htlc_set(ld, hin, total_msat); | ||
| #else | ||
| /* BOLT-9441a66faad63edc8cd89860b22fbf24a86f0dcd #4: | ||
| * - otherwise, if it supports `basic_mpp`: | ||
| * - MUST add it to the HTLC set corresponding to that `payment_hash`. | ||
| * - if the total `amount_msat` of this HTLC set equals `total_msat`: | ||
| * - SHOULD fulfill all HTLCs in the HTLC set | ||
| */ | ||
| set = htlc_set_map_get(&ld->htlc_sets, &hin->payment_hash); | ||
| if (!set) | ||
| set = new_htlc_set(ld, hin, total_msat); | ||
| else { | ||
| /* BOLT-0729433704dd11cc07a0535c09e5f64de7a5017b #4: | ||
| * | ||
| * if it supports `basic_mpp`: | ||
| * ... | ||
| * - otherwise, if the total `amount_msat` of this HTLC set is | ||
| * less than `total_msat`: | ||
| * ... | ||
| * - MUST require `payment_secret` for all HTLCs in the set. | ||
| */ | ||
| /* We check this now, since we want to fail with this as soon | ||
| * as possible, to avoid other probing attacks. */ | ||
| if (!payment_secret) { | ||
| fail_htlc(hin, WIRE_INCORRECT_OR_UNKNOWN_PAYMENT_DETAILS); | ||
| return; | ||
| } | ||
| tal_arr_expand(&set->htlcs, hin); | ||
| } | ||
|
|
||
| /* Remove from set should hin get destroyed somehow */ | ||
| tal_add_destructor2(hin, htlc_set_hin_destroyed, set); | ||
|
|
||
| /* BOLT-9441a66faad63edc8cd89860b22fbf24a86f0dcd #4: | ||
| * - SHOULD fail the entire HTLC set if `total_msat` is not | ||
| * the same for all HTLCs in the set. | ||
| */ | ||
| if (!amount_msat_eq(total_msat, set->total_msat)) { | ||
| log_unusual(ld->log, "Failing HTLC set %s:" | ||
| " total_msat %s new htlc total %s", | ||
| type_to_string(tmpctx, struct sha256, | ||
| &set->payment_hash), | ||
| type_to_string(tmpctx, struct amount_msat, | ||
| &set->total_msat), | ||
| type_to_string(tmpctx, struct amount_msat, | ||
| &total_msat)); | ||
| htlc_set_fail(set, WIRE_FINAL_INCORRECT_HTLC_AMOUNT); | ||
| return; | ||
| } | ||
| #endif /* EXPERIMENTAL_FEATURES */ | ||
|
|
||
| /* BOLT-9441a66faad63edc8cd89860b22fbf24a86f0dcd #4: | ||
| * - if the total `amount_msat` of this HTLC set equals `total_msat`: | ||
| * - SHOULD fulfill all HTLCs in the HTLC set | ||
| */ | ||
| if (!amount_msat_add(&set->so_far, set->so_far, hin->msat)) { | ||
| log_unusual(ld->log, "Failing HTLC set %s:" | ||
| " overflow adding %s+%s", | ||
| type_to_string(tmpctx, struct sha256, | ||
| &set->payment_hash), | ||
| type_to_string(tmpctx, struct amount_msat, | ||
| &set->so_far), | ||
| type_to_string(tmpctx, struct amount_msat, | ||
| &hin->msat)); | ||
| htlc_set_fail(set, WIRE_FINAL_INCORRECT_HTLC_AMOUNT); | ||
| return; | ||
| } | ||
|
|
||
| if (amount_msat_eq(set->so_far, total_msat)) { | ||
| invoice_try_pay(ld, set, details); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should this be
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm, no, it has to dispose of it. take() is for things where it may or may not take ownership. We've generally used naming to try to indicate which functions are a sink. Should we rename this to 'invoice_resolve_htlc_in'?
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That sounds like a good idea. I keep getting confused by I'm tempted to propose an |
||
| return; | ||
| } | ||
|
|
||
| /* BOLT-9441a66faad63edc8cd89860b22fbf24a86f0dcd #4: | ||
| * - otherwise, if the total `amount_msat` of this HTLC set is less than | ||
| * `total_msat`: | ||
| * - MUST NOT fulfill any HTLCs in the HTLC set | ||
| *... | ||
| * - MUST require `payment_secret` for all HTLCs in the set. */ | ||
| /* This catches the case of the first payment in a set. */ | ||
| if (!payment_secret) { | ||
| htlc_set_fail(set, WIRE_INCORRECT_OR_UNKNOWN_PAYMENT_DETAILS); | ||
| return; | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.