Browse Source

openchannel: make openchannel hook chainable

This will make the `openchannel_hook` chainable. Logic is as follows:
 - The first plugin that rejects terminates the chain
 - If more than one plugin uses the `close_to` parameter, take the first
   value and log_unusual for the others.

Changelog-Added: openchannel_hook is now chainable
bump-pyln-proto
Michael Schmoock 4 years ago
committed by Rusty Russell
parent
commit
a3e18a6aba
  1. 143
      lightningd/opening_control.c

143
lightningd/opening_control.c

@ -765,6 +765,8 @@ struct openchannel_hook_payload {
u16 max_accepted_htlcs;
u8 channel_flags;
u8 *shutdown_scriptpubkey;
const u8 *our_upfront_shutdown_script;
char *errmsg;
};
static void
@ -803,13 +805,12 @@ static void openchannel_payload_remove_openingd(struct subd *openingd,
payload->openingd = NULL;
}
static void openchannel_hook_cb(struct openchannel_hook_payload *payload STEALS,
const char *buffer,
const jsmntok_t *toks)
static void
openchannel_hook_final(struct openchannel_hook_payload *payload STEALS)
{
struct subd *openingd = payload->openingd;
const u8 *our_upfront_shutdown_script;
const char *errmsg = NULL;
const u8 *our_upfront_shutdown_script = payload->our_upfront_shutdown_script;
const char *errmsg = payload->errmsg;
/* We want to free this, whatever happens. */
tal_steal(tmpctx, payload);
@ -820,65 +821,87 @@ static void openchannel_hook_cb(struct openchannel_hook_payload *payload STEALS,
tal_del_destructor2(openingd, openchannel_payload_remove_openingd, payload);
/* If we had a hook, check what it says */
if (buffer) {
const jsmntok_t *t = json_get_member(buffer, toks, "result");
if (!t)
fatal("Plugin returned an invalid response to the"
" openchannel hook: %.*s",
toks[0].end - toks[0].start,
buffer + toks[0].start);
if (json_tok_streq(buffer, t, "reject")) {
t = json_get_member(buffer, toks, "error_message");
if (t)
errmsg = json_strdup(tmpctx, buffer, t);
else
errmsg = "";
log_debug(openingd->ld->log,
"openchannel_hook_cb says '%s'",
errmsg);
our_upfront_shutdown_script = NULL;
} else if (!json_tok_streq(buffer, t, "continue"))
fatal("Plugin returned an invalid result for the "
"openchannel hook: %.*s",
t->end - t->start, buffer + t->start);
/* Check for a 'close_to' address passed back */
if (!errmsg) {
t = json_get_member(buffer, toks, "close_to");
if (t) {
switch (json_to_address_scriptpubkey(tmpctx, chainparams,
buffer, t,
&our_upfront_shutdown_script)) {
case ADDRESS_PARSE_UNRECOGNIZED:
fatal("Plugin returned an invalid response to the"
" openchannel.close_to hook: %.*s",
t->end - t->start, buffer + t->start);
case ADDRESS_PARSE_WRONG_NETWORK:
fatal("Plugin returned invalid response to the"
" openchannel.close_to hook: address %s is"
" not on network %s",
tal_hex(NULL, our_upfront_shutdown_script),
chainparams->network_name);
case ADDRESS_PARSE_SUCCESS:
errmsg = NULL;
}
} else
our_upfront_shutdown_script = NULL;
}
} else
our_upfront_shutdown_script = NULL;
subd_send_msg(openingd,
take(towire_openingd_got_offer_reply(NULL, errmsg,
our_upfront_shutdown_script)));
}
REGISTER_SINGLE_PLUGIN_HOOK(openchannel,
openchannel_hook_cb,
openchannel_hook_serialize,
struct openchannel_hook_payload *);
static bool
openchannel_hook_deserialize(struct openchannel_hook_payload *payload,
const char *buffer,
const jsmntok_t *toks)
{
struct subd *openingd = payload->openingd;
/* already rejected by prior plugin hook in the chain */
if (payload->errmsg != NULL)
return true;
if (!toks || !buffer)
return true;
const jsmntok_t *t_result = json_get_member(buffer, toks, "result");
const jsmntok_t *t_errmsg = json_get_member(buffer, toks, "error_message");
const jsmntok_t *t_closeto = json_get_member(buffer, toks, "close_to");
if (!t_result)
fatal("Plugin returned an invalid response to the"
" openchannel hook: %.*s",
toks[0].end - toks[0].start, buffer + toks[0].start);
/* reject */
if (json_tok_streq(buffer, t_result, "reject")) {
payload->errmsg = "";
if (t_errmsg)
payload->errmsg = json_strdup(payload, buffer, t_errmsg);
log_debug(openingd->ld->log,
"openchannel_hook rejects and says '%s'",
payload->errmsg);
if (t_closeto)
fatal("Plugin rejected openchannel but also set close_to");
openchannel_hook_final(payload);
return false;
} else if (!json_tok_streq(buffer, t_result, "continue")) {
fatal("Plugin returned an invalid result for the "
"openchannel hook: %.*s",
t_result->end - t_result->start, buffer + t_result->start);
}
/* Check for a valid 'close_to' address passed back */
if (t_closeto) {
/* First plugin can set close_to. Log others. */
if (payload->our_upfront_shutdown_script != NULL) {
log_unusual(openingd->ld->log,
"openchannel_hook close_to address was"
" already set by other plugin. Ignoring!");
return true;
}
switch (json_to_address_scriptpubkey(tmpctx, chainparams,
buffer, t_closeto,
&payload->our_upfront_shutdown_script)) {
case ADDRESS_PARSE_UNRECOGNIZED:
fatal("Plugin returned an invalid response to"
" the openchannel.close_to hook: %.*s",
t_closeto->end - t_closeto->start,
buffer + t_closeto->start);
case ADDRESS_PARSE_WRONG_NETWORK:
fatal("Plugin returned invalid response to the"
" openchannel.close_to hook: address %s is"
" not on network %s",
tal_hex(NULL, payload->our_upfront_shutdown_script),
chainparams->network_name);
case ADDRESS_PARSE_SUCCESS:
break;
}
}
return true;
}
REGISTER_PLUGIN_HOOK(openchannel,
openchannel_hook_deserialize,
openchannel_hook_final,
openchannel_hook_serialize,
struct openchannel_hook_payload *);
static void opening_got_offer(struct subd *openingd,
const u8 *msg,
@ -896,6 +919,8 @@ static void opening_got_offer(struct subd *openingd,
payload = tal(openingd, struct openchannel_hook_payload);
payload->openingd = openingd;
payload->our_upfront_shutdown_script = NULL;
payload->errmsg = NULL;
if (!fromwire_openingd_got_offer(payload, msg,
&payload->funding_satoshis,
&payload->push_msat,

Loading…
Cancel
Save