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

Restore legacy onion (damn you lnd!) #7352

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 77 additions & 6 deletions common/sphinx.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "config.h"
#include <assert.h>

#include <ccan/array_size/array_size.h>
#include <ccan/mem/mem.h>
#include <common/onion_decode.h>
#include <common/onionreply.h>
Expand Down Expand Up @@ -122,6 +123,33 @@ bool sphinx_add_hop_has_length(struct sphinx_path *path, const struct pubkey *pu
return true;
}

static u8 *make_v0_hop(const tal_t *ctx,
const struct short_channel_id *scid,
struct amount_msat forward, u32 outgoing_cltv)
{
const u8 padding[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
/* Prepend 0 byte for realm */
u8 *buf = tal_arrz(ctx, u8, 1);
towire_short_channel_id(&buf, *scid);
towire_amount_msat(&buf, forward);
towire_u32(&buf, outgoing_cltv);
towire(&buf, padding, ARRAY_SIZE(padding));
assert(tal_bytelen(buf) == 1 + 32);
return buf;
}

void sphinx_add_v0_hop(struct sphinx_path *path, const struct pubkey *pubkey,
const struct short_channel_id *scid,
struct amount_msat forward, u32 outgoing_cltv)
{
struct sphinx_hop sp;

sp.raw_payload = make_v0_hop(path, scid, forward, outgoing_cltv);
sp.pubkey = *pubkey;
tal_arr_expand(&path->hops, sp);
}

void sphinx_add_hop(struct sphinx_path *path, const struct pubkey *pubkey,
const u8 *payload TAKES)
{
Expand Down Expand Up @@ -639,12 +667,55 @@ struct route_step *process_onionpacket(

/* Any of these could fail, falling thru with cursor == NULL */
payload_size = fromwire_bigsize(&cursor, &max);
/* FIXME: raw_payload *includes* the length, which is redundant and
* means we can't just ust fromwire_tal_arrn. */
fromwire_pad(&cursor, &max, payload_size);
if (cursor != NULL)
step->raw_payload = tal_dup_arr(step, u8, paddedheader,
cursor - paddedheader, 0);

/* Legacy! 0 length payload means fixed 32 byte structure */
if (payload_size == 0 && max >= 32) {
struct tlv_payload *legacy = tlv_payload_new(tmpctx);
const u8 *legacy_cursor = cursor;
size_t legacy_max = 32;
u8 *onwire_tlv;

legacy->amt_to_forward = tal(legacy, u64);
legacy->outgoing_cltv_value = tal(legacy, u32);
legacy->short_channel_id = tal(legacy, struct short_channel_id);

/* BOLT-obsolete #4:
* ## Legacy `hop_data` payload format
*
* The `hop_data` format is identified by a single `0x00`-byte
* length, for backward compatibility. Its payload is defined
* as:
*
* 1. type: `hop_data` (for `realm` 0)
* 2. data:
* * [`short_channel_id`:`short_channel_id`]
* * [`u64`:`amt_to_forward`]
* * [`u32`:`outgoing_cltv_value`]
* * [`12*byte`:`padding`]
*/
*legacy->short_channel_id = fromwire_short_channel_id(&legacy_cursor, &legacy_max);
*legacy->amt_to_forward = fromwire_u64(&legacy_cursor, &legacy_max);
*legacy->outgoing_cltv_value = fromwire_u32(&legacy_cursor, &legacy_max);

/* Re-linearize it as a modern TLV! */
onwire_tlv = tal_arr(tmpctx, u8, 0);
towire_tlv_payload(&onwire_tlv, legacy);

/* Length, then tlv */
step->raw_payload = tal_arr(step, u8, 0);
towire_bigsize(&step->raw_payload, tal_bytelen(onwire_tlv));
towire_u8_array(&step->raw_payload, onwire_tlv, tal_bytelen(onwire_tlv));

payload_size = 32;
fromwire_pad(&cursor, &max, payload_size);
} else {
/* FIXME: raw_payload *includes* the length, which is redundant and
* means we can't just ust fromwire_tal_arrn. */
fromwire_pad(&cursor, &max, payload_size);
if (cursor != NULL)
step->raw_payload = tal_dup_arr(step, u8, paddedheader,
cursor - paddedheader, 0);
}
fromwire_hmac(&cursor, &max, &step->next->hmac);

/* BOLT #4:
Expand Down
7 changes: 7 additions & 0 deletions common/sphinx.h
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,13 @@ bool sphinx_add_hop_has_length(struct sphinx_path *path, const struct pubkey *pu
void sphinx_add_hop(struct sphinx_path *path, const struct pubkey *pubkey,
const u8 *payload TAKES);

/**
* Do not use, function is cursed.
*/
void sphinx_add_v0_hop(struct sphinx_path *path, const struct pubkey *pubkey,
const struct short_channel_id *scid,
struct amount_msat forward, u32 outgoing_cltv);

/**
* Compute the size of the serialized payloads.
*/
Expand Down
9 changes: 9 additions & 0 deletions common/test/run-sphinx-xor_cipher_stream.c
Original file line number Diff line number Diff line change
Expand Up @@ -108,9 +108,15 @@ void subkey_from_hmac(const char *prefix UNNEEDED,
const struct secret *base UNNEEDED,
struct secret *key UNNEEDED)
{ fprintf(stderr, "subkey_from_hmac called!\n"); abort(); }
/* Generated stub for tlv_payload_new */
struct tlv_payload *tlv_payload_new(const tal_t *ctx UNNEEDED)
{ fprintf(stderr, "tlv_payload_new called!\n"); abort(); }
/* Generated stub for towire */
void towire(u8 **pptr UNNEEDED, const void *data UNNEEDED, size_t len UNNEEDED)
{ fprintf(stderr, "towire called!\n"); abort(); }
/* Generated stub for towire_amount_msat */
void towire_amount_msat(u8 **pptr UNNEEDED, const struct amount_msat msat UNNEEDED)
{ fprintf(stderr, "towire_amount_msat called!\n"); abort(); }
/* Generated stub for towire_bigsize */
void towire_bigsize(u8 **pptr UNNEEDED, const bigsize_t val UNNEEDED)
{ fprintf(stderr, "towire_bigsize called!\n"); abort(); }
Expand All @@ -130,6 +136,9 @@ void towire_secp256k1_ecdsa_signature(u8 **pptr UNNEEDED,
/* Generated stub for towire_sha256 */
void towire_sha256(u8 **pptr UNNEEDED, const struct sha256 *sha256 UNNEEDED)
{ fprintf(stderr, "towire_sha256 called!\n"); abort(); }
/* Generated stub for towire_tlv_payload */
void towire_tlv_payload(u8 **pptr UNNEEDED, const struct tlv_payload *record UNNEEDED)
{ fprintf(stderr, "towire_tlv_payload called!\n"); abort(); }
/* Generated stub for towire_u16 */
void towire_u16(u8 **pptr UNNEEDED, u16 v UNNEEDED)
{ fprintf(stderr, "towire_u16 called!\n"); abort(); }
Expand Down
15 changes: 13 additions & 2 deletions lightningd/pay.c
Original file line number Diff line number Diff line change
Expand Up @@ -1150,7 +1150,8 @@ send_payment(struct lightningd *ld,
const char *description TAKES,
const struct sha256 *local_invreq_id,
const struct secret *payment_secret,
const u8 *payment_metadata)
const u8 *payment_metadata,
bool dev_legacy_hop)
{
unsigned int base_expiry;
struct onionpacket *packet;
Expand All @@ -1177,6 +1178,14 @@ send_payment(struct lightningd *ld,
ret = pubkey_from_node_id(&pubkey, &ids[i]);
assert(ret);

if (dev_legacy_hop && i == n_hops - 2) {
sphinx_add_v0_hop(path, &pubkey,
&route[i + 1].scid,
route[i + 1].amount,
base_expiry + route[i + 1].delay);
continue;
cdecker marked this conversation as resolved.
Show resolved Hide resolved
}

sphinx_add_hop_has_length(path, &pubkey,
take(onion_nonfinal_hop(NULL,
&route[i + 1].scid,
Expand Down Expand Up @@ -1506,6 +1515,7 @@ static struct command_result *json_sendpay(struct command *cmd,
struct secret *payment_secret;
struct sha256 *local_invreq_id;
u8 *payment_metadata;
bool *dev_legacy_hop;

if (!param_check(cmd, buffer, params,
p_req("route", param_route_hops, &route),
Expand All @@ -1520,6 +1530,7 @@ static struct command_result *json_sendpay(struct command *cmd,
p_opt("groupid", param_u64, &group),
p_opt("payment_metadata", param_bin_from_hex, &payment_metadata),
p_opt("description", param_string, &description),
p_opt_dev("dev_legacy_hop", param_bool, &dev_legacy_hop, false),
NULL))
return command_param_failed();

Expand Down Expand Up @@ -1582,7 +1593,7 @@ static struct command_result *json_sendpay(struct command *cmd,
final_amount,
msat ? *msat : final_amount,
label, invstring, description, local_invreq_id,
payment_secret, payment_metadata);
payment_secret, payment_metadata, *dev_legacy_hop);
}

static const struct json_command sendpay_command = {
Expand Down
28 changes: 28 additions & 0 deletions tests/test_pay.py
Original file line number Diff line number Diff line change
Expand Up @@ -5661,3 +5661,31 @@ def test_pay_while_opening_channel(node_factory, bitcoind, executor):
assert only_one(l1.rpc.listpeerchannels(l3.info['id'])['channels'])['state'] == 'OPENINGD'
inv = l2.rpc.invoice(10000, "inv", "inv")
l1.rpc.pay(inv['bolt11'])


def test_pay_legacy_forward(node_factory, bitcoind, executor):
"""We removed legacy in 22.11, and LND will still send them for
route hints! See
https://github.com/lightningnetwork/lnd/issues/8785

"""
l1, l2, l3 = node_factory.line_graph(3, fundamount=10**6, wait_for_announce=True)

inv = l3.rpc.invoice(1000, "inv", "inv")

chanid12 = only_one(l1.rpc.listpeerchannels(l2.info['id'])['channels'])['short_channel_id']
chanid23 = only_one(l2.rpc.listpeerchannels(l3.info['id'])['channels'])['short_channel_id']
route = [{'amount_msat': 1011,
'id': l2.info['id'],
'delay': 20,
'channel': chanid12},
{'amount_msat': 1000,
'id': l3.info['id'],
'delay': 10,
'channel': chanid23}]

l1.rpc.call("sendpay", payload={'route': route,
'payment_hash': inv['payment_hash'],
'payment_secret': inv['payment_secret'],
'dev_legacy_hop': True})
l1.rpc.waitsendpay(inv['payment_hash'])
5 changes: 5 additions & 0 deletions wallet/test/run-wallet.c
Original file line number Diff line number Diff line change
Expand Up @@ -932,6 +932,11 @@ u8 *serialize_onionpacket(
bool sphinx_add_hop_has_length(struct sphinx_path *path UNNEEDED, const struct pubkey *pubkey UNNEEDED,
const u8 *payload TAKES UNNEEDED)
{ fprintf(stderr, "sphinx_add_hop_has_length called!\n"); abort(); }
/* Generated stub for sphinx_add_v0_hop */
void sphinx_add_v0_hop(struct sphinx_path *path UNNEEDED, const struct pubkey *pubkey UNNEEDED,
const struct short_channel_id *scid UNNEEDED,
struct amount_msat forward UNNEEDED, u32 outgoing_cltv UNNEEDED)
{ fprintf(stderr, "sphinx_add_v0_hop called!\n"); abort(); }
/* Generated stub for sphinx_path_new */
struct sphinx_path *sphinx_path_new(const tal_t *ctx UNNEEDED,
const u8 *associated_data UNNEEDED)
Expand Down
Loading