From 0211712f5e98a0ce27a92b17854aae14738e9acb Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 14 Nov 2019 10:49:53 +1030 Subject: [PATCH] sphinx: separate nonfinal from final interface, add tlv option. For legacy, they were the same, but for TLV we care whether it's the final hop or not. Signed-off-by: Rusty Russell --- common/sphinx.c | 74 ++++++++++++++++++++++++++++++++++++++++ common/sphinx.h | 19 +++++++++++ common/test/run-sphinx.c | 6 ++++ lightningd/pay.c | 34 +++++++++++++----- 4 files changed, 124 insertions(+), 9 deletions(-) diff --git a/common/sphinx.c b/common/sphinx.c index be25d7374..264276048 100644 --- a/common/sphinx.c +++ b/common/sphinx.c @@ -149,6 +149,80 @@ void sphinx_add_v0_hop(struct sphinx_path *path, const struct pubkey *pubkey, sphinx_add_raw_hop(path, pubkey, 0, buf); } +static void sphinx_add_tlv_hop(struct sphinx_path *path, + const struct pubkey *pubkey, + const struct tlv_tlv_payload *tlv) +{ + u8 *tlvs = tal_arr(path, u8, 0); + towire_tlvs(&tlvs, tlvs_tlv_payload, TLVS_TLV_PAYLOAD_ARRAY_SIZE, tlv); + sphinx_add_raw_hop(path, pubkey, tal_bytelen(tlvs), tlvs); +} + +void sphinx_add_nonfinal_hop(struct sphinx_path *path, + const struct pubkey *pubkey, + bool use_tlv, + const struct short_channel_id *scid, + struct amount_msat forward, + u32 outgoing_cltv) +{ + if (use_tlv) { + struct tlv_tlv_payload *tlv = tlv_tlv_payload_new(tmpctx); + struct tlv_tlv_payload_amt_to_forward tlv_amt; + struct tlv_tlv_payload_outgoing_cltv_value tlv_cltv; + struct tlv_tlv_payload_short_channel_id tlv_scid; + + /* BOLT #4: + * + * The writer: + * - MUST include `amt_to_forward` and `outgoing_cltv_value` + * for every node. + * - MUST include `short_channel_id` for every non-final node. + */ + tlv_amt.amt_to_forward = forward.millisatoshis; /* Raw: TLV convert */ + tlv_cltv.outgoing_cltv_value = outgoing_cltv; + tlv_scid.short_channel_id = *scid; + tlv->amt_to_forward = &tlv_amt; + tlv->outgoing_cltv_value = &tlv_cltv; + tlv->short_channel_id = &tlv_scid; + + sphinx_add_tlv_hop(path, pubkey, tlv); + } else { + sphinx_add_v0_hop(path, pubkey, scid, forward, outgoing_cltv); + } +} + +void sphinx_add_final_hop(struct sphinx_path *path, + const struct pubkey *pubkey, + bool use_tlv, + struct amount_msat forward, + u32 outgoing_cltv) +{ + if (use_tlv) { + struct tlv_tlv_payload *tlv = tlv_tlv_payload_new(tmpctx); + struct tlv_tlv_payload_amt_to_forward tlv_amt; + struct tlv_tlv_payload_outgoing_cltv_value tlv_cltv; + + /* BOLT #4: + * + * The writer: + * - MUST include `amt_to_forward` and `outgoing_cltv_value` + * for every node. + *... + * - MUST NOT include `short_channel_id` for the final node. + */ + tlv_amt.amt_to_forward = forward.millisatoshis; /* Raw: TLV convert */ + tlv_cltv.outgoing_cltv_value = outgoing_cltv; + tlv->amt_to_forward = &tlv_amt; + tlv->outgoing_cltv_value = &tlv_cltv; + + sphinx_add_tlv_hop(path, pubkey, tlv); + } else { + static struct short_channel_id all_zero_scid; + sphinx_add_v0_hop(path, pubkey, &all_zero_scid, + forward, outgoing_cltv); + } +} + /* Small helper to append data to a buffer and update the position * into the buffer */ diff --git a/common/sphinx.h b/common/sphinx.h index 790aa3a2c..7f205c098 100644 --- a/common/sphinx.h +++ b/common/sphinx.h @@ -231,6 +231,25 @@ void sphinx_add_v0_hop(struct sphinx_path *path, const struct pubkey *pubkey, void sphinx_add_raw_hop(struct sphinx_path *path, const struct pubkey *pubkey, enum sphinx_payload_type type, const u8 *payload); +/** + * Add a non-final hop to the path. + */ +void sphinx_add_nonfinal_hop(struct sphinx_path *path, + const struct pubkey *pubkey, + bool use_tlv, + const struct short_channel_id *scid, + struct amount_msat forward, + u32 outgoing_cltv); + +/** + * Add a final hop to the path. + */ +void sphinx_add_final_hop(struct sphinx_path *path, + const struct pubkey *pubkey, + bool use_tlv, + struct amount_msat forward, + u32 outgoing_cltv); + /** * Helper to extract fields from ONION_END. */ diff --git a/common/test/run-sphinx.c b/common/test/run-sphinx.c index b6e5c4ede..41f9c44a0 100644 --- a/common/test/run-sphinx.c +++ b/common/test/run-sphinx.c @@ -99,6 +99,12 @@ void towire_sha256(u8 **pptr UNNEEDED, const struct sha256 *sha256 UNNEEDED) void towire_short_channel_id(u8 **pptr UNNEEDED, const struct short_channel_id *short_channel_id UNNEEDED) { fprintf(stderr, "towire_short_channel_id called!\n"); abort(); } +/* Generated stub for towire_tlvs */ +void towire_tlvs(u8 **pptr UNNEEDED, + const struct tlv_record_type types[] UNNEEDED, + size_t num_types UNNEEDED, + const void *record UNNEEDED) +{ fprintf(stderr, "towire_tlvs called!\n"); abort(); } /* Generated stub for towire_tu32 */ void towire_tu32(u8 **pptr UNNEEDED, u32 v UNNEEDED) { fprintf(stderr, "towire_tu32 called!\n"); abort(); } diff --git a/lightningd/pay.c b/lightningd/pay.c index 9aff1b78d..8a9811c54 100644 --- a/lightningd/pay.c +++ b/lightningd/pay.c @@ -615,6 +615,20 @@ static struct command_result *wait_payment(struct lightningd *ld, abort(); } +static bool should_use_tlv(enum route_hop_style style) +{ + switch (style) { + case ROUTE_HOP_TLV: +#if EXPERIMENTAL_FEATURES + return true; +#endif + /* Otherwise fall thru */ + case ROUTE_HOP_LEGACY: + return false; + } + abort(); +} + /* Returns command_result if cmd was resolved, NULL if not yet called. */ static struct command_result * send_payment(struct lightningd *ld, @@ -639,13 +653,11 @@ send_payment(struct lightningd *ld, struct routing_failure *fail; struct channel *channel; struct sphinx_path *path; - struct short_channel_id finalscid; struct pubkey pubkey; bool ret; /* Expiry for HTLCs is absolute. And add one to give some margin. */ base_expiry = get_block_height(ld->topology) + 1; - memset(&finalscid, 0, sizeof(struct short_channel_id)); path = sphinx_path_new(tmpctx, rhash->u.u8); /* Extract IDs for each hop: create_onionpacket wants array. */ @@ -656,19 +668,23 @@ send_payment(struct lightningd *ld, for (i = 0; i < n_hops - 1; i++) { ret = pubkey_from_node_id(&pubkey, &ids[i]); assert(ret); - sphinx_add_v0_hop(path, &pubkey, &route[i + 1].channel_id, - route[i + 1].amount, - base_expiry + route[i + 1].delay); + + sphinx_add_nonfinal_hop(path, &pubkey, + should_use_tlv(route[i].style), + &route[i + 1].channel_id, + route[i + 1].amount, + base_expiry + route[i + 1].delay); } /* And finally set the final hop to the special values in * BOLT04 */ - memset(&finalscid, 0, sizeof(struct short_channel_id)); ret = pubkey_from_node_id(&pubkey, &ids[i]); assert(ret); - sphinx_add_v0_hop(path, &pubkey, &finalscid, - route[i].amount, - base_expiry + route[i].delay); + + sphinx_add_final_hop(path, &pubkey, + should_use_tlv(route[i].style), + route[i].amount, + base_expiry + route[i].delay); /* Now, do we already have a payment? */ payment = wallet_payment_by_hash(tmpctx, ld->wallet, rhash);