-
Notifications
You must be signed in to change notification settings - Fork 906
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
Onion TLV support (EXPERIMENTAL_FEATURE) #3167
Changes from all commits
9882aae
5df7cbd
ce3203b
4d25b9a
ecb67e5
af029eb
992b9b1
78a8a20
c22b748
ed0b575
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -134,9 +134,10 @@ void sphinx_add_raw_hop(struct sphinx_path *path, const struct pubkey *pubkey, | |
assert(sphinx_path_payloads_size(path) <= ROUTING_INFO_SIZE); | ||
} | ||
|
||
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) | ||
static 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) | ||
{ | ||
const u8 padding[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; | ||
|
@@ -149,6 +150,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 | ||
*/ | ||
|
@@ -426,7 +501,7 @@ static struct hop_params *generate_hop_params( | |
return params; | ||
} | ||
|
||
static void deserialize_hop_data(struct hop_data *data, const u8 *src) | ||
static void deserialize_hop_data(struct hop_data_legacy *data, const u8 *src) | ||
{ | ||
const u8 *cursor = src; | ||
size_t max = FRAME_SIZE; | ||
|
@@ -477,16 +552,31 @@ static void sphinx_parse_payload(struct route_step *step, const u8 *src) | |
} | ||
#endif | ||
|
||
/* Legacy hop_data support */ | ||
/* BOLT #4: | ||
* | ||
* The `length` field determines both the length and the format of the | ||
* `hop_payload` field; the following formats are defined: | ||
* | ||
* - 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. | ||
* | ||
* - `tlv_payload` format, identified by any length over `1`. In this | ||
* case the `hop_payload_length` is equal to the numeric value of | ||
* `length`. | ||
*/ | ||
if (src[0] == 0x00) { | ||
vsize = 1; | ||
raw_size = 32; | ||
hop_size = FRAME_SIZE; | ||
step->type = SPHINX_V0_PAYLOAD; | ||
} else { | ||
} else if (src[0] > 1) { | ||
vsize = bigsize_get(src, 3, &raw_size); | ||
hop_size = raw_size + vsize + HMAC_SIZE; | ||
step->type = SPHINX_TLV_PAYLOAD; | ||
} else { | ||
step->type = SPHINX_INVALID_PAYLOAD; | ||
return; | ||
} | ||
|
||
/* Copy common pieces over */ | ||
|
@@ -497,6 +587,18 @@ static void sphinx_parse_payload(struct route_step *step, const u8 *src) | |
* later. */ | ||
if (step->type == SPHINX_V0_PAYLOAD) | ||
deserialize_hop_data(&step->payload.v0, src); | ||
else if (step->type == SPHINX_TLV_PAYLOAD) { | ||
const u8 *tlv = step->raw_payload; | ||
size_t max = tal_bytelen(tlv); | ||
step->payload.tlv = tlv_tlv_payload_new(step); | ||
if (!fromwire_tlvs(&tlv, &max, tlvs_tlv_payload, | ||
TLVS_TLV_PAYLOAD_ARRAY_SIZE, | ||
step->payload.tlv)) { | ||
/* FIXME: record offset of violation for error! */ | ||
step->type = SPHINX_INVALID_PAYLOAD; | ||
return; | ||
} | ||
} | ||
} | ||
|
||
struct onionpacket *create_onionpacket( | ||
|
@@ -781,3 +883,74 @@ struct onionreply *unwrap_onionreply(const tal_t *ctx, | |
return oreply; | ||
|
||
} | ||
|
||
/** | ||
* Helper to extract fields from ONION_END. | ||
*/ | ||
bool route_step_decode_end(const struct route_step *rs, | ||
struct amount_msat *amt_forward, | ||
u32 *outgoing_cltv) | ||
{ | ||
assert(rs->nextcase == ONION_END); | ||
|
||
switch (rs->type) { | ||
case SPHINX_V0_PAYLOAD: | ||
*amt_forward = rs->payload.v0.amt_forward; | ||
*outgoing_cltv = rs->payload.v0.outgoing_cltv; | ||
return true; | ||
case SPHINX_TLV_PAYLOAD: | ||
if (!rs->payload.tlv->amt_to_forward) | ||
return false; | ||
if (!rs->payload.tlv->outgoing_cltv_value) | ||
rustyrussell marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return false; | ||
amt_forward->millisatoshis /* Raw: tu64 -> millisatoshis */ | ||
= rs->payload.tlv->amt_to_forward->amt_to_forward; | ||
*outgoing_cltv = rs->payload.tlv->outgoing_cltv_value->outgoing_cltv_value; | ||
return true; | ||
case SPHINX_INVALID_PAYLOAD: | ||
return false; | ||
|
||
/* This should probably be removed, as it's just for testing */ | ||
case SPHINX_RAW_PAYLOAD: | ||
abort(); | ||
} | ||
abort(); | ||
Comment on lines
+913
to
+917
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, if you want I can clean it up since it also touches the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The onion tool actually uses this to make an arbitrary raw payload, so we'll leave it for a bit longer. |
||
} | ||
|
||
/** | ||
* Helper to extract fields from ONION_FORWARD. | ||
*/ | ||
bool route_step_decode_forward(const struct route_step *rs, | ||
struct amount_msat *amt_forward, | ||
u32 *outgoing_cltv, | ||
struct short_channel_id *scid) | ||
{ | ||
assert(rs->nextcase == ONION_FORWARD); | ||
|
||
switch (rs->type) { | ||
case SPHINX_V0_PAYLOAD: | ||
*amt_forward = rs->payload.v0.amt_forward; | ||
*outgoing_cltv = rs->payload.v0.outgoing_cltv; | ||
*scid = rs->payload.v0.channel_id; | ||
return true; | ||
case SPHINX_TLV_PAYLOAD: | ||
if (!rs->payload.tlv->amt_to_forward) | ||
return false; | ||
amt_forward->millisatoshis /* Raw: tu64 -> millisatoshis */ | ||
= rs->payload.tlv->amt_to_forward->amt_to_forward; | ||
if (!rs->payload.tlv->outgoing_cltv_value) | ||
return false; | ||
*outgoing_cltv = rs->payload.tlv->outgoing_cltv_value->outgoing_cltv_value; | ||
if (!rs->payload.tlv->short_channel_id) | ||
return false; | ||
*scid = rs->payload.tlv->short_channel_id->short_channel_id; | ||
return true; | ||
case SPHINX_INVALID_PAYLOAD: | ||
return false; | ||
|
||
/* This should probably be removed, as it's just for testing */ | ||
case SPHINX_RAW_PAYLOAD: | ||
abort(); | ||
} | ||
abort(); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -61,7 +61,7 @@ struct sphinx_path; | |
* * [`u32`:`outgoing_cltv_value`] | ||
* * [`12*byte`:`padding`] | ||
*/ | ||
struct hop_data { | ||
struct hop_data_legacy { | ||
u8 realm; | ||
struct short_channel_id channel_id; | ||
struct amount_msat amt_forward; | ||
|
@@ -80,7 +80,8 @@ struct route_step { | |
struct onionpacket *next; | ||
enum sphinx_payload_type type; | ||
union { | ||
struct hop_data v0; | ||
struct hop_data_legacy v0; | ||
struct tlv_tlv_payload *tlv; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why not make this an instance of the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Our marshaling/unmarshaling really wants a pointer... There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hm, ok, I should read the TLV implementation more carefully :-) |
||
} payload; | ||
u8 *raw_payload; | ||
}; | ||
|
@@ -218,16 +219,43 @@ struct sphinx_path *sphinx_path_new_with_key(const tal_t *ctx, | |
const u8 *associated_data, | ||
const struct secret *session_key); | ||
|
||
/** | ||
* Add a V0 (Realm 0) single frame hop to the path. | ||
*/ | ||
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); | ||
/** | ||
* Add a raw payload hop to the path. | ||
*/ | ||
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. | ||
*/ | ||
bool route_step_decode_end(const struct route_step *rs, | ||
struct amount_msat *amt_forward, | ||
u32 *outgoing_cltv); | ||
|
||
/** | ||
* Helper to extract fields from ONION_FORWARD. | ||
*/ | ||
bool route_step_decode_forward(const struct route_step *rs, | ||
struct amount_msat *amt_forward, | ||
u32 *outgoing_cltv, | ||
struct short_channel_id *scid); | ||
#endif /* LIGHTNING_COMMON_SPHINX_H */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch, the
src[0] == 1
escape hatch got lost somewhere before.