From 83b897ff474af67bcf644f50d247b62c2cd0416d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Corn=C3=A9=20Plooy?= Date: Thu, 14 Jun 2018 17:30:16 +0200 Subject: [PATCH 1/7] sphinx: pubkeys can be const in create_onionpacket --- common/sphinx.c | 4 ++-- common/sphinx.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/common/sphinx.c b/common/sphinx.c index 7bbc4fa4a0b4..f4902d74b3c5 100644 --- a/common/sphinx.c +++ b/common/sphinx.c @@ -247,7 +247,7 @@ static void generate_key_set(const u8 secret[SHARED_SECRET_SIZE], static struct hop_params *generate_hop_params( const tal_t *ctx, const u8 *sessionkey, - struct pubkey path[]) + const struct pubkey path[]) { int i, j, num_hops = tal_count(path); secp256k1_pubkey temp; @@ -337,7 +337,7 @@ static void deserialize_hop_data(struct hop_data *data, const u8 *src) struct onionpacket *create_onionpacket( const tal_t *ctx, - struct pubkey *path, + const struct pubkey *path, struct hop_data hops_data[], const u8 *sessionkey, const u8 *assocdata, diff --git a/common/sphinx.h b/common/sphinx.h index 03fdd9350acf..7e679bdd0805 100644 --- a/common/sphinx.h +++ b/common/sphinx.h @@ -94,7 +94,7 @@ struct route_step { */ struct onionpacket *create_onionpacket( const tal_t * ctx, - struct pubkey path[], + const struct pubkey path[], struct hop_data hops_data[], const u8 * sessionkey, const u8 *assocdata, From 47b3ae6c8db0c44cdee55e6b306aba1083e015d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Corn=C3=A9=20Plooy?= Date: Wed, 13 Jun 2018 17:38:35 +0200 Subject: [PATCH 2/7] sphinx: refactor the way how onion packet serialization is done This way, the per_hop data stays serialized for longer, so it can be passed in its original (but decrypted) form to a custom transaction router. --- common/sphinx.c | 44 ++++++++++++++++++++++++++++++++-------- common/sphinx.h | 36 +++++++++++++++++++++++++++----- devtools/onion.c | 13 ++++++++---- lightningd/pay.c | 21 +++++++++++++------ lightningd/peer_htlcs.c | 23 +++++++++++++++------ wallet/test/run-wallet.c | 8 ++++++++ 6 files changed, 116 insertions(+), 29 deletions(-) diff --git a/common/sphinx.c b/common/sphinx.c index f4902d74b3c5..492dda7d0eba 100644 --- a/common/sphinx.c +++ b/common/sphinx.c @@ -314,10 +314,7 @@ static void serialize_hop_data(tal_t *ctx, u8 *dst, const struct hop_data *data) { u8 *buf = tal_arr(ctx, u8, 0); towire_u8(&buf, data->realm); - towire_short_channel_id(&buf, &data->channel_id); - towire_u64(&buf, data->amt_forward); - towire_u32(&buf, data->outgoing_cltv); - towire_pad(&buf, 12); + towire(&buf, data->per_hop_data, PAYLOAD_SIZE); towire(&buf, data->hmac, SECURITY_PARAMETER); memcpy(dst, buf, tal_count(buf)); tal_free(buf); @@ -328,10 +325,7 @@ static void deserialize_hop_data(struct hop_data *data, const u8 *src) const u8 *cursor = src; size_t max = HOP_DATA_SIZE; data->realm = fromwire_u8(&cursor, &max); - fromwire_short_channel_id(&cursor, &max, &data->channel_id); - data->amt_forward = fromwire_u64(&cursor, &max); - data->outgoing_cltv = fromwire_u32(&cursor, &max); - fromwire_pad(&cursor, &max, 12); + fromwire(&cursor, &max, &data->per_hop_data, PAYLOAD_SIZE); fromwire(&cursor, &max, &data->hmac, SECURITY_PARAMETER); } @@ -453,6 +447,40 @@ struct route_step *process_onionpacket( return step; } +void serialize_per_hop_data( + const struct short_channel_id *channel_id, + u64 amt_forward, + u32 outgoing_cltv, + u8 *per_hop_data + ) +{ + u8 *buf = tal_arr(tmpctx, u8, 0); + + towire_short_channel_id(&buf, channel_id); + towire_u64(&buf, amt_forward); + towire_u32(&buf, outgoing_cltv); + towire_pad(&buf, 12); + + memcpy(per_hop_data, buf, tal_bytelen(buf)); + tal_free(buf); +} + +void deserialize_per_hop_data( + const u8 *per_hop_data, + struct short_channel_id *channel_id, + u64 *amt_forward, + u32 *outgoing_cltv + ) +{ + const u8 *cursor = per_hop_data; + size_t max = PAYLOAD_SIZE; + + fromwire_short_channel_id(&cursor, &max, channel_id); + *amt_forward = fromwire_u64(&cursor, &max); + *outgoing_cltv = fromwire_u32(&cursor, &max); + fromwire_pad(&cursor, &max, 12); +} + u8 *create_onionreply(const tal_t *ctx, const struct secret *shared_secret, const u8 *failure_msg) { diff --git a/common/sphinx.h b/common/sphinx.h index 7e679bdd0805..c7d89c64c4ea 100644 --- a/common/sphinx.h +++ b/common/sphinx.h @@ -65,10 +65,7 @@ enum route_next_case { */ struct hop_data { u8 realm; - struct short_channel_id channel_id; - u64 amt_forward; - u32 outgoing_cltv; - /* Padding omitted, will be zeroed */ + u8 per_hop_data[PAYLOAD_SIZE]; u8 hmac[SECURITY_PARAMETER]; }; @@ -121,7 +118,6 @@ bool onion_shared_secret( * @ctx: tal context to allocate from * @packet: incoming packet being processed * @shared_secret: the result of onion_shared_secret. - * @hoppayload: the per-hop payload destined for the processing node. * @assocdata: associated data to commit to in HMACs * @assocdatalen: length of the assocdata */ @@ -133,6 +129,36 @@ struct route_step *process_onionpacket( const size_t assocdatalen ); +/** + * serialize_per_hop_data - Serialize the per_hop data (for realm 0) + * + * @channel_id: the short channel ID + * @amt_forward: the amount to forward + * @outgoing_cltv: the CLTV value of the outgoing HTLC + * @per_hop_data: the serialized data buffer + */ +void serialize_per_hop_data( + const struct short_channel_id *channel_id, + u64 amt_forward, + u32 outgoing_cltv, + u8 *per_hop_data + ); + +/** + * deserialize_per_hop_data - Deserialize the per_hop data (for realm 0) + * + * @per_hop_data: the serialized data buffer + * @channel_id: the short channel ID + * @amt_forward: the amount to forward + * @outgoing_cltv: the CLTV value of the outgoing HTLC + */ +void deserialize_per_hop_data( + const u8 *per_hop_data, + struct short_channel_id *channel_id, + u64 *amt_forward, + u32 *outgoing_cltv + ); + /** * serialize_onionpacket - Serialize an onionpacket to a buffer. * diff --git a/devtools/onion.c b/devtools/onion.c index 1237a11d0722..c7dd06de5c86 100644 --- a/devtools/onion.c +++ b/devtools/onion.c @@ -34,12 +34,17 @@ static void do_generate(int argc, char **argv) } for (int i = 0; i < num_hops; i++) { + struct short_channel_id channel_id; memset(&hops_data[i], 0, sizeof(hops_data[i])); hops_data[i].realm = i; - memset(&hops_data[i].channel_id, i, - sizeof(hops_data[i].channel_id)); - hops_data[i].amt_forward = i; - hops_data[i].outgoing_cltv = i; + memset(&channel_id, i, + sizeof(channel_id)); + serialize_per_hop_data( + &channel_id, + i, + i, + hops_data[i].per_hop_data + ); fprintf(stderr, "Hopdata %d: %s\n", i, tal_hexstr(NULL, &hops_data[i], sizeof(hops_data[i]))); } diff --git a/lightningd/pay.c b/lightningd/pay.c index 84d052ad7824..faa885e76bb0 100644 --- a/lightningd/pay.c +++ b/lightningd/pay.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include @@ -691,17 +692,25 @@ send_payment(const tal_t *ctx, /* Copy hop_data[n] from route[n+1] (ie. where it goes next) */ for (i = 0; i < n_hops - 1; i++) { hop_data[i].realm = 0; - hop_data[i].channel_id = route[i+1].channel_id; - hop_data[i].amt_forward = route[i+1].amount; - hop_data[i].outgoing_cltv = base_expiry + route[i+1].delay; + serialize_per_hop_data( + &route[i+1].channel_id, + route[i+1].amount, + base_expiry + route[i+1].delay, + hop_data[i].per_hop_data + ); } /* And finally set the final hop to the special values in * BOLT04 */ hop_data[i].realm = 0; - hop_data[i].outgoing_cltv = base_expiry + route[i].delay; - memset(&hop_data[i].channel_id, 0, sizeof(struct short_channel_id)); - hop_data[i].amt_forward = route[i].amount; + struct short_channel_id channel_id; + memset(&channel_id, 0, sizeof(channel_id)); + serialize_per_hop_data( + &channel_id, + route[i].amount, + base_expiry + route[i].delay, + hop_data[i].per_hop_data + ); /* Now, do we already have a payment? */ payment = wallet_payment_by_hash(tmpctx, ld->wallet, rhash); diff --git a/lightningd/peer_htlcs.c b/lightningd/peer_htlcs.c index db7652feb27d..8e946adcaa64 100644 --- a/lightningd/peer_htlcs.c +++ b/lightningd/peer_htlcs.c @@ -681,13 +681,23 @@ static bool peer_accepted_htlc(struct channel *channel, goto out; } + struct short_channel_id channel_id; + u64 amt_forward; + u32 outgoing_cltv; + deserialize_per_hop_data( + rs->hop_data.per_hop_data, + &channel_id, + &amt_forward, + &outgoing_cltv + ); + if (rs->nextcase == ONION_FORWARD) { struct gossip_resolve *gr = tal(ld, struct gossip_resolve); gr->next_onion = serialize_onionpacket(gr, rs->next); - gr->next_channel = rs->hop_data.channel_id; - gr->amt_to_forward = rs->hop_data.amt_forward; - gr->outgoing_cltv_value = rs->hop_data.outgoing_cltv; + gr->next_channel = channel_id; + gr->amt_to_forward = amt_forward; + gr->outgoing_cltv_value = outgoing_cltv; gr->hin = hin; req = towire_gossip_resolve_channel_request(tmpctx, @@ -697,10 +707,11 @@ static bool peer_accepted_htlc(struct channel *channel, &gr->next_channel)); subd_req(hin, ld->gossip, req, -1, 0, channel_resolve_reply, gr); - } else + } else { handle_localpay(hin, hin->cltv_expiry, &hin->payment_hash, - rs->hop_data.amt_forward, - rs->hop_data.outgoing_cltv); + amt_forward, + outgoing_cltv); + } *failcode = 0; out: diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index 8d460e28d3f4..27f36d3b5326 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -67,6 +67,14 @@ void connect_succeeded(struct lightningd *ld UNNEEDED, const struct pubkey *id U void delay_then_reconnect(struct channel *channel UNNEEDED, u32 seconds_delay UNNEEDED, const struct wireaddr_internal *addrhint TAKES UNNEEDED) { fprintf(stderr, "delay_then_reconnect called!\n"); abort(); } +/* Generated stub for deserialize_per_hop_data */ +void deserialize_per_hop_data( + const u8 *per_hop_data UNNEEDED, + struct short_channel_id *channel_id UNNEEDED, + u64 *amt_forward UNNEEDED, + u32 *outgoing_cltv + ) +{ fprintf(stderr, "deserialize_per_hop_data called!\n"); abort(); } /* Generated stub for fatal */ void fatal(const char *fmt UNNEEDED, ...) { fprintf(stderr, "fatal called!\n"); abort(); } From 2161a6faa7000250ea9b5cbad8665c53594f4d7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Corn=C3=A9=20Plooy?= Date: Thu, 14 Jun 2018 18:10:37 +0200 Subject: [PATCH 3/7] pay: separate generating the route data from the rest of send_payment This way, we can later use the rest of the function with other route data. --- lightningd/pay.c | 139 ++++++++++++++++++++++++++++++----------------- 1 file changed, 89 insertions(+), 50 deletions(-) diff --git a/lightningd/pay.c b/lightningd/pay.c index faa885e76bb0..e47b2c1f07ae 100644 --- a/lightningd/pay.c +++ b/lightningd/pay.c @@ -32,6 +32,15 @@ struct sendpay_command { void *cbarg; }; +/* data consumed by send_payment_custom_route_data */ +struct payment_route_data +{ + struct hop_data *hop_data; + struct pubkey *ids; + struct short_channel_id *channels; + unsigned int base_expiry; +}; + static void destroy_sendpay_command(struct sendpay_command *pc) { list_del(&pc->list); @@ -655,63 +664,75 @@ bool wait_payment(const tal_t *cxt, return cb_not_called; } -/* Returns false if cb was called, true if cb not yet called. */ -bool -send_payment(const tal_t *ctx, - struct lightningd* ld, - const struct sha256 *rhash, - const struct route_hop *route, - u64 msatoshi, - const char *description TAKES, - void (*cb)(const struct sendpay_result *, void*), - void *cbarg) +static void route_to_payment_route_data( + struct lightningd *ld, + const struct route_hop *route, + struct payment_route_data *payment_route_data) { - const u8 *onion; - u8 sessionkey[32]; - unsigned int base_expiry; - struct onionpacket *packet; - struct secret *path_secrets; - enum onion_type failcode; size_t i, n_hops = tal_count(route); - struct hop_data *hop_data = tal_arr(tmpctx, struct hop_data, n_hops); - struct pubkey *ids = tal_arr(tmpctx, struct pubkey, n_hops); - struct wallet_payment *payment = NULL; - struct htlc_out *hout; - struct short_channel_id *channels; - struct routing_failure *fail; - struct channel *channel; - struct sendpay_result *result; + payment_route_data->hop_data = tal_arr(tmpctx, struct hop_data, n_hops); + payment_route_data->ids = tal_arr(tmpctx, struct pubkey, n_hops); + payment_route_data->channels = tal_arr(tmpctx, struct short_channel_id, n_hops); /* Expiry for HTLCs is absolute. And add one to give some margin. */ - base_expiry = get_block_height(ld->topology) + 1; + payment_route_data->base_expiry = get_block_height(ld->topology) + 1; /* Extract IDs for each hop: create_onionpacket wants array. */ for (i = 0; i < n_hops; i++) - ids[i] = route[i].nodeid; + payment_route_data->ids[i] = route[i].nodeid; - /* Copy hop_data[n] from route[n+1] (ie. where it goes next) */ + /* Copy hd[n] from route[n+1] (ie. where it goes next) */ for (i = 0; i < n_hops - 1; i++) { - hop_data[i].realm = 0; + payment_route_data->hop_data[i].realm = 0; serialize_per_hop_data( &route[i+1].channel_id, route[i+1].amount, - base_expiry + route[i+1].delay, - hop_data[i].per_hop_data + payment_route_data->base_expiry + route[i+1].delay, + payment_route_data->hop_data[i].per_hop_data ); } /* And finally set the final hop to the special values in * BOLT04 */ - hop_data[i].realm = 0; + payment_route_data->hop_data[i].realm = 0; struct short_channel_id channel_id; memset(&channel_id, 0, sizeof(channel_id)); serialize_per_hop_data( &channel_id, route[i].amount, - base_expiry + route[i].delay, - hop_data[i].per_hop_data + payment_route_data->base_expiry + route[i].delay, + payment_route_data->hop_data[i].per_hop_data ); + /* Copy channels used along the route. */ + for (i = 0; i < n_hops; ++i) + payment_route_data->channels[i] = route[i].channel_id; +} + +/* Returns false if cb was called, true if cb not yet called. */ +static bool +send_payment_custom_route_data(const tal_t *ctx, + struct lightningd* ld, + const struct sha256 *rhash, + struct payment_route_data route_data, + const struct route_hop *first_hop, + u64 msatoshi, + const char *description TAKES, + void (*cb)(const struct sendpay_result *, void*), + void *cbarg) +{ + const u8 *onion; + u8 sessionkey[32]; + struct onionpacket *packet; + struct secret *path_secrets; + enum onion_type failcode; + size_t n_hops = tal_count(route_data.hop_data); + struct wallet_payment *payment = NULL; + struct htlc_out *hout; + struct routing_failure *fail; + struct channel *channel; + struct sendpay_result *result; + /* Now, do we already have a payment? */ payment = wallet_payment_by_hash(tmpctx, ld->wallet, rhash); if (payment) { @@ -739,7 +760,7 @@ send_payment(const tal_t *ctx, cb(result, cbarg); return false; } - if (!pubkey_eq(&payment->destination, &ids[n_hops-1])) { + if (!pubkey_eq(&payment->destination, &route_data.ids[n_hops-1])) { char *msg = tal_fmt(tmpctx, "Already succeeded to %s", type_to_string(tmpctx, @@ -760,12 +781,12 @@ send_payment(const tal_t *ctx, log_add(ld->log, "... retrying"); } - channel = active_channel_by_id(ld, &ids[0], NULL); + channel = active_channel_by_id(ld, &route_data.ids[0], NULL); if (!channel) { /* Report routing failure to gossipd */ fail = immediate_routing_failure(ctx, ld, WIRE_UNKNOWN_NEXT_PEER, - &route[0].channel_id); + &first_hop->channel_id); report_routing_failure(ld->log, ld->gossip, fail); /* Report routing failure to caller */ @@ -779,21 +800,21 @@ send_payment(const tal_t *ctx, randombytes_buf(&sessionkey, sizeof(sessionkey)); /* Onion will carry us from first peer onwards. */ - packet = create_onionpacket(tmpctx, ids, hop_data, sessionkey, rhash->u.u8, + packet = create_onionpacket(tmpctx, route_data.ids, route_data.hop_data, sessionkey, rhash->u.u8, sizeof(struct sha256), &path_secrets); onion = serialize_onionpacket(tmpctx, packet); log_info(ld->log, "Sending %"PRIu64" over %zu hops to deliver %"PRIu64"", - route[0].amount, n_hops, msatoshi); + first_hop->amount, n_hops, msatoshi); - failcode = send_htlc_out(channel, route[0].amount, - base_expiry + route[0].delay, + failcode = send_htlc_out(channel, first_hop->amount, + route_data.base_expiry + first_hop->delay, rhash, onion, NULL, &hout); if (failcode) { /* Report routing failure to gossipd */ fail = immediate_routing_failure(ctx, ld, failcode, - &route[0].channel_id); + &first_hop->channel_id); report_routing_failure(ld->log, ld->gossip, fail); /* Report routing failure to caller */ @@ -803,11 +824,6 @@ send_payment(const tal_t *ctx, return false; } - /* Copy channels used along the route. */ - channels = tal_arr(tmpctx, struct short_channel_id, n_hops); - for (i = 0; i < n_hops; ++i) - channels[i] = route[i].channel_id; - /* If we're retrying, delete all trace of previous one. We delete * outgoing HTLC, too, otherwise it gets reported to onchaind as * a possibility, and we end up in handle_missing_htlc_output-> @@ -822,15 +838,15 @@ send_payment(const tal_t *ctx, payment = tal(hout, struct wallet_payment); payment->id = 0; payment->payment_hash = *rhash; - payment->destination = ids[n_hops - 1]; + payment->destination = route_data.ids[n_hops - 1]; payment->status = PAYMENT_PENDING; payment->msatoshi = msatoshi; - payment->msatoshi_sent = route[0].amount; + payment->msatoshi_sent = first_hop->amount; payment->timestamp = time_now().ts.tv_sec; payment->payment_preimage = NULL; payment->path_secrets = tal_steal(payment, path_secrets); - payment->route_nodes = tal_steal(payment, ids); - payment->route_channels = tal_steal(payment, channels); + payment->route_nodes = tal_steal(payment, route_data.ids); + payment->route_channels = tal_steal(payment, route_data.channels); if (description != NULL) payment->description = tal_strdup(payment, description); else @@ -844,6 +860,29 @@ send_payment(const tal_t *ctx, return true; } +/* Returns false if cb was called, true if cb not yet called. */ +bool send_payment(const tal_t *ctx, + struct lightningd* ld, + const struct sha256 *rhash, + const struct route_hop *route, + u64 msatoshi, + const char *description TAKES, + void (*cb)(const struct sendpay_result *, void*), + void *cbarg) +{ + struct payment_route_data payment_route_data; + route_to_payment_route_data(ld, route, &payment_route_data); + return send_payment_custom_route_data(ctx, + ld, + rhash, + payment_route_data, + route, + msatoshi, + description, + cb, + cbarg); +} + /*----------------------------------------------------------------------------- JSON-RPC sendpay interface -----------------------------------------------------------------------------*/ From e09f61eec09fa1124d25b3d58eb6d5194af462a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Corn=C3=A9=20Plooy?= Date: Mon, 18 Jun 2018 16:05:29 +0200 Subject: [PATCH 4/7] pay: in json_sendpay, use send_payment_custom_route_data --- lightningd/pay.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lightningd/pay.c b/lightningd/pay.c index e47b2c1f07ae..3b3021b21572 100644 --- a/lightningd/pay.c +++ b/lightningd/pay.c @@ -998,6 +998,7 @@ static void json_sendpay(struct command *cmd, size_t n_hops; struct sha256 *rhash; struct route_hop *route; + struct payment_route_data payment_route_data; u64 *msatoshi; const char *description; @@ -1036,6 +1037,8 @@ static void json_sendpay(struct command *cmd, n_hops++; } + route_to_payment_route_data(cmd->ld, route, &payment_route_data); + if (n_hops == 0) { command_fail(cmd, JSONRPC2_INVALID_PARAMS, "Empty route"); return; @@ -1056,7 +1059,8 @@ static void json_sendpay(struct command *cmd, } } - if (send_payment(cmd, cmd->ld, rhash, route, + if (send_payment_custom_route_data(cmd, cmd->ld, rhash, + payment_route_data, route, msatoshi ? *msatoshi : route[n_hops-1].amount, description, &json_sendpay_on_resolve, cmd)) From 525c2c0c61cffb61f443f468285957145cb36ab1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Corn=C3=A9=20Plooy?= Date: Tue, 19 Jun 2018 09:55:39 +0200 Subject: [PATCH 5/7] pay: add realm and data elements to sendpay route input This allows the caller to specify non-default realm and per-hop data in the route. --- lightningd/pay.c | 56 +++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 53 insertions(+), 3 deletions(-) diff --git a/lightningd/pay.c b/lightningd/pay.c index 3b3021b21572..ad04e2040737 100644 --- a/lightningd/pay.c +++ b/lightningd/pay.c @@ -17,6 +17,7 @@ #include #include #include +#include /*----------------------------------------------------------------------------- Internal sendpay interface @@ -995,7 +996,7 @@ static void json_sendpay(struct command *cmd, { const jsmntok_t *routetok; const jsmntok_t *t, *end; - size_t n_hops; + size_t i, n_hops; struct sha256 *rhash; struct route_hop *route; struct payment_route_data payment_route_data; @@ -1014,17 +1015,22 @@ static void json_sendpay(struct command *cmd, n_hops = 0; route = tal_arr(cmd, struct route_hop, n_hops); + /* First pass through the route input */ for (t = routetok + 1; t < end; t = json_next(t)) { u64 *amount; struct pubkey *id; struct short_channel_id *channel; unsigned *delay; + unsigned *realm; + const char *data; if (!param(cmd, buffer, t, p_req("msatoshi", json_tok_u64, &amount), p_req("id", json_tok_pubkey, &id), p_req("delay", json_tok_number, &delay), p_req("channel", json_tok_short_channel_id, &channel), + p_opt("realm", json_tok_number, &realm), + p_opt("data", json_tok_string, &data), NULL)) return; @@ -1037,13 +1043,57 @@ static void json_sendpay(struct command *cmd, n_hops++; } - route_to_payment_route_data(cmd->ld, route, &payment_route_data); - if (n_hops == 0) { command_fail(cmd, JSONRPC2_INVALID_PARAMS, "Empty route"); return; } + /* Default (realm = 0) realm / hop_data, and other route data */ + route_to_payment_route_data(cmd->ld, route, &payment_route_data); + + /* Second pass through the route input, to override realm / hop_data */ + for (i=0, t = routetok + 1; t < end; i++, t = json_next(t)) { + u64 *amount; + struct pubkey *id; + struct short_channel_id *channel; + unsigned *delay; + unsigned *realm; + const char *data; + + //FIXME: ugly repetition of what was already done in the first pass. + if (!param(cmd, buffer, t, + p_req("msatoshi", json_tok_u64, &amount), + p_req("id", json_tok_pubkey, &id), + p_req("delay", json_tok_number, &delay), + p_req("channel", json_tok_short_channel_id, &channel), + p_opt("realm", json_tok_number, &realm), + p_opt("data", json_tok_string, &data), + NULL)) + return; + + if (realm) { + if (*realm > 0xff) { + command_fail(cmd, JSONRPC2_INVALID_PARAMS, + "Route %zu invalid realm", + i); + return; + } + payment_route_data.hop_data[i].realm = *realm; + } + + if (data) { + if (!hex_decode(data, + strlen(data), + payment_route_data.hop_data[i].per_hop_data, + sizeof(payment_route_data.hop_data[i].per_hop_data))) { + command_fail(cmd, JSONRPC2_INVALID_PARAMS, + "Route %zu invalid data", + i); + return; + } + } + } + /* The given msatoshi is the actual payment that the payee is * requesting. The final hop amount is what we actually give, which can * be from the msatoshi to twice msatoshi. */ From 035602f0b0dad06344399e53877a0607e7c70226 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Corn=C3=A9=20Plooy?= Date: Tue, 19 Jun 2018 14:27:49 +0200 Subject: [PATCH 6/7] sphinx: don't zero the realm value The realm value is provided by the calling code (which will zero it where needed). --- common/sphinx.c | 1 - common/sphinx.h | 5 ++--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/common/sphinx.c b/common/sphinx.c index 492dda7d0eba..6ab4749a0d36 100644 --- a/common/sphinx.c +++ b/common/sphinx.c @@ -362,7 +362,6 @@ struct onionpacket *create_onionpacket( for (i = num_hops - 1; i >= 0; i--) { memcpy(hops_data[i].hmac, nexthmac, SECURITY_PARAMETER); - hops_data[i].realm = 0; generate_key_set(params[i].secret, &keys); generate_cipher_stream(stream, keys.rho, ROUTING_INFO_SIZE); diff --git a/common/sphinx.h b/common/sphinx.h index c7d89c64c4ea..70ed5086faf1 100644 --- a/common/sphinx.h +++ b/common/sphinx.h @@ -81,9 +81,8 @@ struct route_step { * * @ctx: tal context to allocate from * @path: public keys of nodes along the path. - * @hoppayloads: payloads destined for individual hosts (limited to - * HOP_PAYLOAD_SIZE bytes) - * @num_hops: path length in nodes + * @hops_data: data destined for individual hosts. + * hops_data[..].hmac will be filled in by this function. * @sessionkey: 32 byte random session key to derive secrets from * @assocdata: associated data to commit to in HMACs * @assocdatalen: length of the assocdata From 544cbb062ede0709172f5065ba3ae8bc4a22d567 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Corn=C3=A9=20Plooy?= Date: Tue, 19 Jun 2018 14:31:47 +0200 Subject: [PATCH 7/7] test_pay.py: test for rejection of unknown realm value --- tests/test_pay.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/test_pay.py b/tests/test_pay.py index d5ea5022363c..88b8c695de2a 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -777,6 +777,13 @@ def test_forward(node_factory, bitcoind): with pytest.raises(RpcError): l1.rpc.waitsendpay(rhash) + # Unknown realm + route = copy.deepcopy(baseroute) + route[0]['realm'] = 255 + l1.rpc.sendpay(route, rhash) + with pytest.raises(ValueError): + l1.rpc.waitsendpay(rhash) + # Delay too short (we always add one internally anyway, so subtract 2 here). route = copy.deepcopy(baseroute) route[0]['delay'] = 8