Browse Source

lightningd: handle tlv-style payloads.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Changelog-changed: JSON API: `htlc_accepted` hook has `type` (currently `legacy` or `tlv`) and other fields directly inside `onion`.
Changelog-deprecated: JSON API: `htlc_accepted` hook `per_hop_v0` object deprecated, as is `short_channel_id` for the final hop.
travis-debug
Rusty Russell 5 years ago
committed by Christian Decker
parent
commit
2a2259083a
  1. 4
      common/sphinx.c
  2. 26
      doc/PLUGINS.md
  3. 72
      lightningd/peer_htlcs.c
  4. 4
      tests/test_plugin.py

4
common/sphinx.c

@ -826,10 +826,10 @@ bool route_step_decode_end(const struct route_step *rs,
case SPHINX_TLV_PAYLOAD: case SPHINX_TLV_PAYLOAD:
if (!rs->payload.tlv->amt_to_forward) if (!rs->payload.tlv->amt_to_forward)
return false; return false;
amt_forward->millisatoshis /* Raw: tu64 -> millisatoshis */
= rs->payload.tlv->amt_to_forward->amt_to_forward;
if (!rs->payload.tlv->outgoing_cltv_value) if (!rs->payload.tlv->outgoing_cltv_value)
return false; 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; *outgoing_cltv = rs->payload.tlv->outgoing_cltv_value->outgoing_cltv_value;
return true; return true;
case SPHINX_INVALID_PAYLOAD: case SPHINX_INVALID_PAYLOAD:

26
doc/PLUGINS.md

@ -588,11 +588,10 @@ The payload of the hook call has the following format:
{ {
"onion": { "onion": {
"payload": "", "payload": "",
"per_hop_v0": { "type": "legacy",
"realm": "00", "short_channel_id": "1x2x3",
"short_channel_id": "1x2x3", "forward_amount": "42msat",
"forward_amount": "42msat", "outgoing_cltv_value": 500014
"outgoing_cltv_value": 500014
} }
}, },
"next_onion": "[1365bytes of serialized onion]", "next_onion": "[1365bytes of serialized onion]",
@ -606,21 +605,16 @@ The payload of the hook call has the following format:
} }
``` ```
The `per_hop_v0` will only be present if the per hop payload has format `0x00` For detailed information about each field please refer to [BOLT 04 of the specification][bolt4], the following is just a brief summary:
as defined by the specification. If not present an object representing the
type-length-vale (TLV) payload will be added (pending specification). For detailed information about each field please refer to [BOLT 04 of the specification][bolt4], the following is just a brief summary:
- `onion.payload` contains the unparsed payload that was sent to us from the - `onion.payload` contains the unparsed payload that was sent to us from the
sender of the payment. sender of the payment.
- `onion.per_hop_v0`: - `onion.type` is `legacy` for realm 0 payments, `tlv` for realm > 1.
- `realm` will always be `00` since that value determines that we are using - `short_channel_id` determines the channel that the sender is hinting
the `per_hop_v0` format. should be used next. Not present if we're the final destination.
- `short_channel_id` determines the channel that the sender is hinting - `forward_amount` is the amount we should be forwarding to the next hop,
should be used next (set to `0x0x0` if we are the recipient of the
payment).
- `forward_amount` is the amount we should be forwarding to the next hop,
and should match the incoming funds in case we are the recipient. and should match the incoming funds in case we are the recipient.
- `outgoing_cltv_value` determines what the CLTV value for the HTLC that we - `outgoing_cltv_value` determines what the CLTV value for the HTLC that we
forward to the next hop should be. forward to the next hop should be.
- `next_onion` is the fully processed onion that we should be sending to the - `next_onion` is the fully processed onion that we should be sending to the
next hop as part of the outgoing HTLC. Processed in this case means that we next hop as part of the outgoing HTLC. Processed in this case means that we

72
lightningd/peer_htlcs.c

@ -636,6 +636,10 @@ struct htlc_accepted_hook_payload {
struct htlc_in *hin; struct htlc_in *hin;
struct channel *channel; struct channel *channel;
struct lightningd *ld; struct lightningd *ld;
struct amount_msat amt_to_forward;
u32 outgoing_cltv_value;
/* NULL if this is node is final */
struct short_channel_id *short_channel_id;
u8 *next_onion; u8 *next_onion;
}; };
@ -726,14 +730,24 @@ static void htlc_accepted_hook_serialize(struct htlc_accepted_hook_payload *p,
json_add_hex_talarr (s, "payload", rs->raw_payload); json_add_hex_talarr (s, "payload", rs->raw_payload);
if (rs->type == SPHINX_V0_PAYLOAD) { if (rs->type == SPHINX_V0_PAYLOAD) {
json_object_start(s, "per_hop_v0"); if (deprecated_apis) {
json_add_string(s, "realm", "00"); json_object_start(s, "per_hop_v0");
json_add_short_channel_id(s, "short_channel_id", &rs->payload.v0.channel_id); json_add_string(s, "realm", "00");
json_add_amount_msat_only(s, "forward_amount", rs->payload.v0.amt_forward); json_add_short_channel_id(s, "short_channel_id", &rs->payload.v0.channel_id);
json_add_u64(s, "outgoing_cltv_value", rs->payload.v0.outgoing_cltv); json_add_amount_msat_only(s, "forward_amount", rs->payload.v0.amt_forward);
json_object_end(s); json_add_u64(s, "outgoing_cltv_value", rs->payload.v0.outgoing_cltv);
json_object_end(s);
}
json_add_string(s, "type", "legacy");
} else if (rs->type == SPHINX_TLV_PAYLOAD) {
json_add_string(s, "type", "tlv");
} }
if (p->short_channel_id)
json_add_short_channel_id(s, "short_channel_id",
p->short_channel_id);
json_add_amount_msat_only(s, "forward_amount", p->amt_to_forward);
json_add_u32(s, "outgoing_cltv_value", p->outgoing_cltv_value);
json_add_hex_talarr(s, "next_onion", p->next_onion); json_add_hex_talarr(s, "next_onion", p->next_onion);
json_add_secret(s, "shared_secret", hin->shared_secret); json_add_secret(s, "shared_secret", hin->shared_secret);
json_object_end(s); json_object_end(s);
@ -762,19 +776,17 @@ htlc_accepted_hook_callback(struct htlc_accepted_hook_payload *request,
enum htlc_accepted_result result; enum htlc_accepted_result result;
enum onion_type failure_code; enum onion_type failure_code;
u8 *channel_update; u8 *channel_update;
struct hop_data_legacy *hop_data;
result = htlc_accepted_hook_deserialize(buffer, toks, &payment_preimage, &failure_code, &channel_update); result = htlc_accepted_hook_deserialize(buffer, toks, &payment_preimage, &failure_code, &channel_update);
hop_data = &rs->payload.v0;
switch (result) { switch (result) {
case htlc_accepted_continue: case htlc_accepted_continue:
if (rs->nextcase == ONION_FORWARD) { if (rs->nextcase == ONION_FORWARD) {
struct gossip_resolve *gr = tal(ld, struct gossip_resolve); struct gossip_resolve *gr = tal(ld, struct gossip_resolve);
gr->next_onion = serialize_onionpacket(gr, rs->next); gr->next_onion = serialize_onionpacket(gr, rs->next);
gr->next_channel = hop_data->channel_id; gr->next_channel = *request->short_channel_id;
gr->amt_to_forward = hop_data->amt_forward; gr->amt_to_forward = request->amt_to_forward;
gr->outgoing_cltv_value = hop_data->outgoing_cltv; gr->outgoing_cltv_value = request->outgoing_cltv_value;
gr->hin = hin; gr->hin = hin;
req = towire_gossip_get_channel_peer(tmpctx, &gr->next_channel); req = towire_gossip_get_channel_peer(tmpctx, &gr->next_channel);
@ -785,13 +797,19 @@ htlc_accepted_hook_callback(struct htlc_accepted_hook_payload *request,
channel_resolve_reply, gr); channel_resolve_reply, gr);
} else } else
handle_localpay(hin, hin->cltv_expiry, &hin->payment_hash, handle_localpay(hin, hin->cltv_expiry, &hin->payment_hash,
hop_data->amt_forward, request->amt_to_forward,
hop_data->outgoing_cltv); request->outgoing_cltv_value);
break; break;
case htlc_accepted_fail: case htlc_accepted_fail:
log_debug(channel->log, log_debug(channel->log,
"Failing incoming HTLC as instructed by plugin hook"); "Failing incoming HTLC as instructed by plugin hook");
fail_in_htlc(hin, failure_code, NULL, &hop_data->channel_id); if ((failure_code & UPDATE) && rs->nextcase == ONION_END) {
log_broken(channel->log,
"htlc_acccepted hook: Can't return failure %u on last hop!",
failure_code);
failure_code = WIRE_TEMPORARY_NODE_FAILURE;
}
fail_in_htlc(hin, failure_code, NULL, request->short_channel_id);
break; break;
case htlc_accepted_resolve: case htlc_accepted_resolve:
fulfill_htlc(hin, &payment_preimage); fulfill_htlc(hin, &payment_preimage);
@ -897,16 +915,32 @@ static bool peer_accepted_htlc(struct channel *channel, u64 id,
} }
/* Unknown realm isn't a bad onion, it's a normal failure. */ /* Unknown realm isn't a bad onion, it's a normal failure. */
/* FIXME: if we want hooks to handle foreign realms we should if (rs->type == SPHINX_INVALID_PAYLOAD) {
* move this check to the hook callback. */
if (rs->type != SPHINX_V0_PAYLOAD) {
*failcode = WIRE_INVALID_REALM; *failcode = WIRE_INVALID_REALM;
goto out; goto out;
} }
/* It's time to package up all the information and call the
* hook so plugins can interject if they want */
hook_payload = tal(hin, struct htlc_accepted_hook_payload); hook_payload = tal(hin, struct htlc_accepted_hook_payload);
if (rs->nextcase == ONION_END) {
if (!route_step_decode_end(rs, &hook_payload->amt_to_forward,
&hook_payload->outgoing_cltv_value)) {
*failcode = WIRE_INVALID_ONION_PAYLOAD;
goto out;
}
hook_payload->short_channel_id = NULL;
} else {
hook_payload->short_channel_id
= tal(hook_payload, struct short_channel_id);
if (!route_step_decode_forward(rs,
&hook_payload->amt_to_forward,
&hook_payload->outgoing_cltv_value,
hook_payload->short_channel_id)) {
*failcode = WIRE_INVALID_ONION_PAYLOAD;
goto out;
}
}
hook_payload->route_step = tal_steal(hook_payload, rs); hook_payload->route_step = tal_steal(hook_payload, rs);
hook_payload->ld = ld; hook_payload->ld = ld;
hook_payload->hin = hin; hook_payload->hin = hin;

4
tests/test_plugin.py

@ -537,8 +537,8 @@ def test_htlc_accepted_hook_forward_restart(node_factory, executor):
assert re.match(r'^00006700000.000100000000000003e8000000..000000000000000000000000$', onion['payload']) assert re.match(r'^00006700000.000100000000000003e8000000..000000000000000000000000$', onion['payload'])
assert len(onion['payload']) == 64 assert len(onion['payload']) == 64
assert len(onion['shared_secret']) == 64 assert len(onion['shared_secret']) == 64
assert onion['per_hop_v0']['realm'] == "00" assert onion['type'] == 'legacy'
assert onion['per_hop_v0']['forward_amount'] == '1000msat' assert onion['forward_amount'] == '1000msat'
assert len(onion['next_onion']) == 2 * (1300 + 32 + 33 + 1) assert len(onion['next_onion']) == 2 * (1300 + 32 + 33 + 1)
f1.result() f1.result()

Loading…
Cancel
Save