From e0d87353215dc58451b2700b94f90df6755d4216 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 29 May 2024 09:38:55 +0930 Subject: [PATCH] common: translate legacy onion payloads. We do this by literally creating the modern-style TLV, and pretending we found it in the onion. This isolates us from messing with any callers, who don't even know. Co-programmed-with: Alex Myers Signed-off-by: Rusty Russell Fixes: https://github.com/ElementsProject/lightning/issues/7347 Changelog-Fixed: Protocol: forward legacy non-TLV onions which we removed in 22.11 and spec itself in Feb 2022. Still sent by LND nodes who haven't seen our node_announcement. --- common/sphinx.c | 55 +++++++++++++++++++--- common/test/run-sphinx-xor_cipher_stream.c | 6 +++ tests/test_pay.py | 1 - 3 files changed, 55 insertions(+), 7 deletions(-) diff --git a/common/sphinx.c b/common/sphinx.c index 5c7f99f89903..815e85d3481b 100644 --- a/common/sphinx.c +++ b/common/sphinx.c @@ -667,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: diff --git a/common/test/run-sphinx-xor_cipher_stream.c b/common/test/run-sphinx-xor_cipher_stream.c index 0e33ace54de4..b5a30ef72699 100644 --- a/common/test/run-sphinx-xor_cipher_stream.c +++ b/common/test/run-sphinx-xor_cipher_stream.c @@ -108,6 +108,9 @@ 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(); } @@ -133,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(); } diff --git a/tests/test_pay.py b/tests/test_pay.py index ab06c4a53b0f..558ed83b03db 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -5663,7 +5663,6 @@ def test_pay_while_opening_channel(node_factory, bitcoind, executor): l1.rpc.pay(inv['bolt11']) -@pytest.mark.xfail(strict=True) 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