lightningd: Restore forwarding of legacy onions.

Partial revert of 43a833e405
"lightningd: remove support for legacy onion format."; we restore the
ability to decode legacy onions for forwarding, but not to generate them.
(We don't accept them properly since making payment_secret compulsory
anyway, so no real change there!)

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Changelog-Removed: Protocol: ... but we still forward legacy HTLC onions for now.
This commit is contained in:
Rusty Russell 2022-03-31 13:43:27 +10:30
parent 141d4ef675
commit 116a77f1be
10 changed files with 63 additions and 22 deletions

View file

@ -109,10 +109,11 @@ u8 *onion_final_hop(const tal_t *ctx,
return make_tlv_hop(ctx, tlv);
}
/* Returns true if valid, and fills in len. */
/* Returns true if valid, and fills in type. */
static bool pull_payload_length(const u8 **cursor,
size_t *max,
bool has_realm,
enum onion_payload_type *type,
size_t *len)
{
/* *len will incorporate bytes we read from cursor */
@ -127,6 +128,19 @@ static bool pull_payload_length(const u8 **cursor,
if (!cursor)
return false;
/* BOLT #4:
* - Legacy `hop_data` format, identified by a single `0x00` byte for
* length. In this case the `hop_payload_length` is defined to be 32
* bytes.
*/
if (has_realm && *len == 0) {
if (type)
*type = ONION_V0_PAYLOAD;
assert(*cursor - start == 1);
*len = 1 + 32;
return true;
}
/* BOLT #4:
* - `tlv_payload` format, identified by any length over `1`. In this
* case the `hop_payload_length` is equal to the numeric value of
@ -142,6 +156,8 @@ static bool pull_payload_length(const u8 **cursor,
return false;
}
if (type)
*type = ONION_TLV_PAYLOAD;
*len += (*cursor - start);
return true;
}
@ -150,10 +166,11 @@ static bool pull_payload_length(const u8 **cursor,
}
size_t onion_payload_length(const u8 *raw_payload, size_t len, bool has_realm,
bool *valid)
bool *valid,
enum onion_payload_type *type)
{
size_t max = len, payload_len;
*valid = pull_payload_length(&raw_payload, &max, has_realm, &payload_len);
*valid = pull_payload_length(&raw_payload, &max, has_realm, type, &payload_len);
/* If it's not valid, copy the entire thing. */
if (!*valid)
@ -210,12 +227,31 @@ struct onion_payload *onion_decode(const tal_t *ctx,
size_t max = tal_bytelen(cursor), len;
struct tlv_tlv_payload *tlv;
if (!pull_payload_length(&cursor, &max, true, &len)) {
if (!pull_payload_length(&cursor, &max, true, &p->type, &len)) {
*failtlvtype = 0;
*failtlvpos = tal_bytelen(rs->raw_payload);
goto fail_no_tlv;
}
/* Very limited legacy handling: forward only. */
if (p->type == ONION_V0_PAYLOAD && rs->nextcase == ONION_FORWARD) {
p->forward_channel = tal(p, struct short_channel_id);
fromwire_short_channel_id(&cursor, &max, p->forward_channel);
p->total_msat = NULL;
p->amt_to_forward = fromwire_amount_msat(&cursor, &max);
p->outgoing_cltv = fromwire_u32(&cursor, &max);
p->payment_secret = NULL;
p->blinding = NULL;
/* We can't handle blinding with a legacy payload */
if (blinding)
return tal_free(p);
/* If they somehow got an invalid onion this far, fail. */
if (!cursor)
return tal_free(p);
p->tlv = NULL;
return p;
}
/* We do this manually so we can accept extra types, and get
* error off and type. */
tlv = tlv_tlv_payload_new(p);

View file

@ -6,7 +6,14 @@
struct route_step;
enum onion_payload_type {
ONION_V0_PAYLOAD = 0,
ONION_TLV_PAYLOAD = 1,
};
struct onion_payload {
enum onion_payload_type type;
struct amount_msat amt_to_forward;
u32 outgoing_cltv;
struct amount_msat *total_msat;
@ -43,6 +50,7 @@ u8 *onion_final_hop(const tal_t *ctx,
* @len: length of @raw_payload in bytes.
* @has_realm: used for HTLCs, where first byte 0 is magical.
* @valid: set to true if it is valid, false otherwise.
* @type: if non-NULL, set to type of payload if *@valid is true.
*
* If @valid is set, there is room for the HMAC immediately following,
* as the return value is <= ROUTING_INFO_SIZE - HMAC_SIZE. Otherwise,
@ -50,7 +58,8 @@ u8 *onion_final_hop(const tal_t *ctx,
*/
size_t onion_payload_length(const u8 *raw_payload, size_t len,
bool has_realm,
bool *valid);
bool *valid,
enum onion_payload_type *type);
/**
* onion_decode: decode payload from a decrypted onion.

View file

@ -627,7 +627,7 @@ struct route_step *process_onionpacket(
payload_size = onion_payload_length(paddedheader,
tal_bytelen(msg->routinginfo),
has_realm,
&valid);
&valid, NULL);
/* Can't decode? Treat it as terminal. */
if (!valid) {

View file

@ -25,10 +25,6 @@ void towire_bigsize(u8 **pptr UNNEEDED, const bigsize_t val UNNEEDED)
/* Generated stub for towire_channel_id */
void towire_channel_id(u8 **pptr UNNEEDED, const struct channel_id *channel_id UNNEEDED)
{ fprintf(stderr, "towire_channel_id called!\n"); abort(); }
/* Generated stub for type_to_string_ */
const char *type_to_string_(const tal_t *ctx UNNEEDED, const char *typename UNNEEDED,
union printable_types u UNNEEDED)
{ fprintf(stderr, "type_to_string_ called!\n"); abort(); }
/* AUTOGENERATED MOCKS END */
/* Canned gossmap, taken from tests/test_gossip.py's

View file

@ -51,10 +51,6 @@ void towire_tlv(u8 **pptr UNNEEDED,
/* Generated stub for towire_wireaddr */
void towire_wireaddr(u8 **pptr UNNEEDED, const struct wireaddr *addr UNNEEDED)
{ fprintf(stderr, "towire_wireaddr called!\n"); abort(); }
/* Generated stub for type_to_string_ */
const char *type_to_string_(const tal_t *ctx UNNEEDED, const char *typename UNNEEDED,
union printable_types u UNNEEDED)
{ fprintf(stderr, "type_to_string_ called!\n"); abort(); }
/* AUTOGENERATED MOCKS END */
static void write_to_store(int store_fd, const u8 *msg)

View file

@ -44,10 +44,6 @@ void towire_tlv(u8 **pptr UNNEEDED,
/* Generated stub for towire_wireaddr */
void towire_wireaddr(u8 **pptr UNNEEDED, const struct wireaddr *addr UNNEEDED)
{ fprintf(stderr, "towire_wireaddr called!\n"); abort(); }
/* Generated stub for type_to_string_ */
const char *type_to_string_(const tal_t *ctx UNNEEDED, const char *typename UNNEEDED,
union printable_types u UNNEEDED)
{ fprintf(stderr, "type_to_string_ called!\n"); abort(); }
/* AUTOGENERATED MOCKS END */
static void write_to_store(int store_fd, const u8 *msg)

View file

@ -91,7 +91,8 @@ struct onionreply *new_onionreply(const tal_t *ctx UNNEEDED, const u8 *contents
/* Generated stub for onion_payload_length */
size_t onion_payload_length(const u8 *raw_payload UNNEEDED, size_t len UNNEEDED,
bool has_realm UNNEEDED,
bool *valid UNNEEDED)
bool *valid UNNEEDED,
enum onion_payload_type *type UNNEEDED)
{ fprintf(stderr, "onion_payload_length called!\n"); abort(); }
/* Generated stub for pubkey_from_node_id */
bool pubkey_from_node_id(struct pubkey *key UNNEEDED, const struct node_id *id UNNEEDED)

View file

@ -280,7 +280,7 @@ static void runtest(const char *filename)
errx(1, "Error serializing message.");
onion_payload_length(step->raw_payload,
tal_bytelen(step->raw_payload),
true, &valid);
true, &valid, NULL);
assert(valid);
printf(" Payload: %s\n", tal_hex(ctx, step->raw_payload));
printf(" Next onion: %s\n", tal_hex(ctx, serialized));

View file

@ -993,7 +993,15 @@ static void htlc_accepted_hook_serialize(struct htlc_accepted_hook_payload *p,
json_add_hex_talarr(s, "payload", rs->raw_payload);
if (p->payload) {
switch (p->payload->type) {
case ONION_V0_PAYLOAD:
json_add_string(s, "type", "legacy");
break;
case ONION_TLV_PAYLOAD:
json_add_string(s, "type", "tlv");
break;
}
if (p->payload->forward_channel)
json_add_short_channel_id(s, "short_channel_id",

View file

@ -5169,7 +5169,6 @@ def test_sendpay_grouping(node_factory, bitcoind):
assert([p['status'] for p in pays] == ['failed', 'failed', 'complete'])
@pytest.mark.xfail("needs lecacy onion support")
def test_legacyonion(node_factory, bitcoind):
# We have to replicate the topology we created onion with, exactly.
l1, l2, l3 = node_factory.line_graph(3, fundchannel=False)