Browse Source

plugin: Parse response for htlc_accepted hook

Signed-off-by: Christian Decker <decker.christian@gmail.com>
htlc_accepted_hook
Christian Decker 6 years ago
committed by Rusty Russell
parent
commit
2b81e02a2e
  1. 2
      contrib/plugins/helloworld.py
  2. 138
      lightningd/peer_htlcs.c
  3. 17
      wallet/test/run-wallet.c

2
contrib/plugins/helloworld.py

@ -38,7 +38,7 @@ def on_disconnect(plugin, id):
def on_htlc_accepted(onion, htlc, plugin): def on_htlc_accepted(onion, htlc, plugin):
plugin.log('on_htlc_accepted called') plugin.log('on_htlc_accepted called')
time.sleep(20) time.sleep(20)
return None return {'result': 'continue'}
plugin.add_option('greeting', 'Hello', 'The greeting I should use.') plugin.add_option('greeting', 'Hello', 'The greeting I should use.')

138
lightningd/peer_htlcs.c

@ -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);
} }

17
wallet/test/run-wallet.c

@ -299,9 +299,15 @@ void json_object_end(struct json_stream *js UNNEEDED)
/* Generated stub for json_object_start */ /* Generated stub for json_object_start */
void json_object_start(struct json_stream *ks UNNEEDED, const char *fieldname UNNEEDED) void json_object_start(struct json_stream *ks UNNEEDED, const char *fieldname UNNEEDED)
{ fprintf(stderr, "json_object_start called!\n"); abort(); } { fprintf(stderr, "json_object_start called!\n"); abort(); }
/* Generated stub for json_strdup */
char *json_strdup(const tal_t *ctx UNNEEDED, const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED)
{ fprintf(stderr, "json_strdup called!\n"); abort(); }
/* Generated stub for json_stream_success */ /* Generated stub for json_stream_success */
struct json_stream *json_stream_success(struct command *cmd UNNEEDED) struct json_stream *json_stream_success(struct command *cmd UNNEEDED)
{ fprintf(stderr, "json_stream_success called!\n"); abort(); } { fprintf(stderr, "json_stream_success called!\n"); abort(); }
/* Generated stub for json_tok_bin_from_hex */
u8 *json_tok_bin_from_hex(const tal_t *ctx UNNEEDED, const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED)
{ fprintf(stderr, "json_tok_bin_from_hex called!\n"); abort(); }
/* Generated stub for json_tok_channel_id */ /* Generated stub for json_tok_channel_id */
bool json_tok_channel_id(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, bool json_tok_channel_id(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED,
struct channel_id *cid UNNEEDED) struct channel_id *cid UNNEEDED)
@ -319,6 +325,17 @@ bool json_tok_streq(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED,
bool json_to_node_id(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, bool json_to_node_id(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED,
struct node_id *id UNNEEDED) struct node_id *id UNNEEDED)
{ fprintf(stderr, "json_to_node_id called!\n"); abort(); } { fprintf(stderr, "json_to_node_id called!\n"); abort(); }
/* Generated stub for json_to_number */
bool json_to_number(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED,
unsigned int *num UNNEEDED)
{ fprintf(stderr, "json_to_number called!\n"); abort(); }
/* Generated stub for json_to_preimage */
bool json_to_preimage(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, struct preimage *preimage UNNEEDED)
{ fprintf(stderr, "json_to_preimage called!\n"); abort(); }
/* Generated stub for json_to_pubkey */
bool json_to_pubkey(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED,
struct pubkey *pubkey UNNEEDED)
{ fprintf(stderr, "json_to_pubkey called!\n"); abort(); }
/* Generated stub for json_to_short_channel_id */ /* Generated stub for json_to_short_channel_id */
bool json_to_short_channel_id(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, bool json_to_short_channel_id(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED,
struct short_channel_id *scid UNNEEDED, struct short_channel_id *scid UNNEEDED,

Loading…
Cancel
Save