|
@ -1,3 +1,4 @@ |
|
|
|
|
|
#include <bitcoin/preimage.h> |
|
|
#include <bitcoin/tx.h> |
|
|
#include <bitcoin/tx.h> |
|
|
#include <ccan/build_assert/build_assert.h> |
|
|
#include <ccan/build_assert/build_assert.h> |
|
|
#include <ccan/cast/cast.h> |
|
|
#include <ccan/cast/cast.h> |
|
@ -624,10 +625,22 @@ struct htlc_accepted_hook_payload { |
|
|
u8 *next_onion; |
|
|
u8 *next_onion; |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
/* The possible return value types that a plugin may return for the
|
|
|
|
|
|
* `htlc_accepted` hook. */ |
|
|
|
|
|
enum htlc_accepted_result { |
|
|
|
|
|
htlc_accepted_continue, |
|
|
|
|
|
htlc_accepted_fail, |
|
|
|
|
|
htlc_accepted_resolve, |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
/**
|
|
|
/**
|
|
|
* Response type from the plugin |
|
|
* Response type from the plugin |
|
|
*/ |
|
|
*/ |
|
|
struct htlc_accepted_hook_response { |
|
|
struct htlc_accepted_hook_response { |
|
|
|
|
|
enum htlc_accepted_result result; |
|
|
|
|
|
struct preimage payment_key; |
|
|
|
|
|
enum onion_type failure_code; |
|
|
|
|
|
u8 *channel_update; |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
/**
|
|
|
/**
|
|
@ -637,7 +650,64 @@ static struct htlc_accepted_hook_response * |
|
|
htlc_accepted_hook_deserialize(const tal_t *ctx, const char *buffer, |
|
|
htlc_accepted_hook_deserialize(const tal_t *ctx, const char *buffer, |
|
|
const jsmntok_t *toks) |
|
|
const jsmntok_t *toks) |
|
|
{ |
|
|
{ |
|
|
return NULL; |
|
|
struct htlc_accepted_hook_response *response; |
|
|
|
|
|
const jsmntok_t *resulttok, *failcodetok, *paykeytok, *chanupdtok; |
|
|
|
|
|
|
|
|
|
|
|
if (!toks || !buffer) |
|
|
|
|
|
return NULL; |
|
|
|
|
|
|
|
|
|
|
|
resulttok = json_get_member(buffer, toks, "result"); |
|
|
|
|
|
|
|
|
|
|
|
/* If the result is "continue" we can just return NULL since
|
|
|
|
|
|
* this is the default behavior for this hook anyway */ |
|
|
|
|
|
if (!resulttok) { |
|
|
|
|
|
fatal("Plugin return value does not contain 'result' key %s", |
|
|
|
|
|
json_strdup(tmpctx, buffer, toks)); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (json_tok_streq(buffer, resulttok, "continue")) { |
|
|
|
|
|
return NULL; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
response = tal(ctx, struct htlc_accepted_hook_response); |
|
|
|
|
|
if (json_tok_streq(buffer, resulttok, "fail")) { |
|
|
|
|
|
response->result = htlc_accepted_fail; |
|
|
|
|
|
failcodetok = json_get_member(buffer, toks, "failure_code"); |
|
|
|
|
|
chanupdtok = json_get_member(buffer, toks, "channel_update"); |
|
|
|
|
|
if (failcodetok && !json_to_number(buffer, failcodetok, |
|
|
|
|
|
&response->failure_code)) |
|
|
|
|
|
fatal("Plugin provided a non-numeric failcode " |
|
|
|
|
|
"in response to an htlc_accepted hook"); |
|
|
|
|
|
|
|
|
|
|
|
if (!failcodetok) |
|
|
|
|
|
response->failure_code = WIRE_TEMPORARY_NODE_FAILURE; |
|
|
|
|
|
|
|
|
|
|
|
if (chanupdtok) |
|
|
|
|
|
response->channel_update = |
|
|
|
|
|
json_tok_bin_from_hex(response, buffer, chanupdtok); |
|
|
|
|
|
else |
|
|
|
|
|
response->channel_update = NULL; |
|
|
|
|
|
|
|
|
|
|
|
} else if (json_tok_streq(buffer, resulttok, "resolve")) { |
|
|
|
|
|
response->result = htlc_accepted_resolve; |
|
|
|
|
|
paykeytok = json_get_member(buffer, toks, "payment_key"); |
|
|
|
|
|
if (!paykeytok) |
|
|
|
|
|
fatal( |
|
|
|
|
|
"Plugin did not specify a 'payment_key' in return " |
|
|
|
|
|
"value to the htlc_accepted hook: %s", |
|
|
|
|
|
json_strdup(tmpctx, buffer, resulttok)); |
|
|
|
|
|
|
|
|
|
|
|
if (!json_to_preimage(buffer, paykeytok, |
|
|
|
|
|
&response->payment_key)) |
|
|
|
|
|
fatal("Plugin specified an invalid 'payment_key': %s", |
|
|
|
|
|
json_tok_full(buffer, resulttok)); |
|
|
|
|
|
} else { |
|
|
|
|
|
fatal("Plugin responded with an unknown result to the " |
|
|
|
|
|
"htlc_accepted hook: %s", |
|
|
|
|
|
json_strdup(tmpctx, buffer, resulttok)); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return response; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
static void htlc_accepted_hook_serialize(struct htlc_accepted_hook_payload *p, |
|
|
static void htlc_accepted_hook_serialize(struct htlc_accepted_hook_payload *p, |
|
@ -679,29 +749,53 @@ htlc_accepted_hook_callback(struct htlc_accepted_hook_payload *request, |
|
|
struct channel *channel = request->channel; |
|
|
struct channel *channel = request->channel; |
|
|
struct lightningd *ld = request->ld; |
|
|
struct lightningd *ld = request->ld; |
|
|
u8 *req; |
|
|
u8 *req; |
|
|
|
|
|
enum htlc_accepted_result result; |
|
|
|
|
|
struct htlc_accepted_hook_response *response; |
|
|
|
|
|
response = htlc_accepted_hook_deserialize(request, buffer, toks); |
|
|
|
|
|
|
|
|
/* TODO(cdecker) Assign to *response once we actually use it */ |
|
|
if (response) |
|
|
htlc_accepted_hook_deserialize(request, buffer, toks); |
|
|
result = response->result; |
|
|
|
|
|
else |
|
|
if (rs->nextcase == ONION_FORWARD) { |
|
|
result = htlc_accepted_continue; |
|
|
struct gossip_resolve *gr = tal(ld, struct gossip_resolve); |
|
|
|
|
|
|
|
|
switch (result) { |
|
|
gr->next_onion = tal_steal(gr, request->next_onion); |
|
|
case htlc_accepted_continue: |
|
|
gr->next_channel = rs->hop_data.channel_id; |
|
|
if (rs->nextcase == ONION_FORWARD) { |
|
|
gr->amt_to_forward = rs->hop_data.amt_forward; |
|
|
struct gossip_resolve *gr = |
|
|
gr->outgoing_cltv_value = rs->hop_data.outgoing_cltv; |
|
|
tal(ld, struct gossip_resolve); |
|
|
gr->hin = hin; |
|
|
|
|
|
|
|
|
gr->next_onion = tal_steal(gr, request->next_onion); |
|
|
|
|
|
serialize_onionpacket(gr, rs->next); |
|
|
|
|
|
gr->next_channel = rs->hop_data.channel_id; |
|
|
|
|
|
gr->amt_to_forward = rs->hop_data.amt_forward; |
|
|
|
|
|
gr->outgoing_cltv_value = rs->hop_data.outgoing_cltv; |
|
|
|
|
|
gr->hin = hin; |
|
|
|
|
|
|
|
|
|
|
|
req = towire_gossip_get_channel_peer(tmpctx, |
|
|
|
|
|
&gr->next_channel); |
|
|
|
|
|
log_debug( |
|
|
|
|
|
channel->log, "Asking gossip to resolve channel %s", |
|
|
|
|
|
type_to_string(tmpctx, struct short_channel_id, |
|
|
|
|
|
&gr->next_channel)); |
|
|
|
|
|
subd_req(hin, ld->gossip, req, -1, 0, |
|
|
|
|
|
channel_resolve_reply, gr); |
|
|
|
|
|
} else |
|
|
|
|
|
handle_localpay(hin, hin->cltv_expiry, |
|
|
|
|
|
&hin->payment_hash, |
|
|
|
|
|
rs->hop_data.amt_forward, |
|
|
|
|
|
rs->hop_data.outgoing_cltv); |
|
|
|
|
|
|
|
|
|
|
|
break; |
|
|
|
|
|
case htlc_accepted_fail: |
|
|
|
|
|
log_debug(channel->log, |
|
|
|
|
|
"Failing incoming HTLC as instructed by plugin hook"); |
|
|
|
|
|
fail_in_htlc(hin, response->failure_code, NULL, NULL); |
|
|
|
|
|
break; |
|
|
|
|
|
case htlc_accepted_resolve: |
|
|
|
|
|
fulfill_htlc(hin, &response->payment_key); |
|
|
|
|
|
break; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
req = towire_gossip_get_channel_peer(tmpctx, &gr->next_channel); |
|
|
|
|
|
log_debug(channel->log, "Asking gossip to resolve channel %s", |
|
|
|
|
|
type_to_string(tmpctx, struct short_channel_id, |
|
|
|
|
|
&gr->next_channel)); |
|
|
|
|
|
subd_req(hin, ld->gossip, req, -1, 0, |
|
|
|
|
|
channel_resolve_reply, gr); |
|
|
|
|
|
} else |
|
|
|
|
|
handle_localpay(hin, hin->cltv_expiry, &hin->payment_hash, |
|
|
|
|
|
rs->hop_data.amt_forward, |
|
|
|
|
|
rs->hop_data.outgoing_cltv); |
|
|
|
|
|
tal_free(request); |
|
|
tal_free(request); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|