mirror of
https://github.com/aljazceru/lightning.git
synced 2025-12-19 15:14:23 +01:00
keysend: Change the plugin to add an invoice and then continue
So far we were relying on `lightningd` to create an ad-hoc invoice when telling it to `resolve` with a given preimage. We now switch to having the plugin create the invoice, remove the mandatory `keysend_preimage` field (which would upset `lightningd` otherwise), and then return the modified payload with the instructions to `continue` instead of resolving. This ties back in with the existing payment/invoice handling code. Invoices are created only if we don't have a label clash (unlikely since we have the nano-time in the label), or the `payment_hash` was already used for another invoice (at which point `lightningd` will automatically reject the payment and we're a bit poorer for it, but meh :-)
This commit is contained in:
committed by
Rusty Russell
parent
63441075b5
commit
de0fd1f274
@@ -1,3 +1,4 @@
|
|||||||
|
#include <bitcoin/preimage.h>
|
||||||
#include <ccan/array_size/array_size.h>
|
#include <ccan/array_size/array_size.h>
|
||||||
#include <plugins/libplugin.h>
|
#include <plugins/libplugin.h>
|
||||||
#include <wire/gen_onion_wire.h>
|
#include <wire/gen_onion_wire.h>
|
||||||
@@ -13,25 +14,57 @@ static void init(struct plugin *p, const char *buf UNUSED,
|
|||||||
static const struct plugin_command commands[] = {
|
static const struct plugin_command commands[] = {
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct command_result *htlc_accepted_continue(struct command *cmd)
|
static struct command_result *
|
||||||
|
htlc_accepted_continue(struct command *cmd, struct tlv_tlv_payload *payload)
|
||||||
{
|
{
|
||||||
struct json_stream *response;
|
struct json_stream *response;
|
||||||
|
u8 *binpayload, *rawpayload;
|
||||||
response = jsonrpc_stream_success(cmd);
|
response = jsonrpc_stream_success(cmd);
|
||||||
|
|
||||||
json_add_string(response, "result", "continue");
|
json_add_string(response, "result", "continue");
|
||||||
|
if (payload) {
|
||||||
|
binpayload = tal_arr(cmd, u8, 0);
|
||||||
|
towire_tlvstream_raw(&binpayload, payload->fields);
|
||||||
|
rawpayload = tal_arr(cmd, u8, 0);
|
||||||
|
towire_bigsize(&rawpayload, tal_bytelen(binpayload));
|
||||||
|
towire(&rawpayload, binpayload, tal_bytelen(binpayload));
|
||||||
|
json_add_string(response, "payload", tal_hex(cmd, rawpayload));
|
||||||
|
}
|
||||||
return command_finished(cmd, response);
|
return command_finished(cmd, response);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct command_result *htlc_accepted_resolve(struct command *cmd,
|
/* Struct wrapping the information we extract from an incoming keysend
|
||||||
char *hexpreimage)
|
* payment */
|
||||||
|
struct keysend_in {
|
||||||
|
struct sha256 payment_hash;
|
||||||
|
struct preimage payment_preimage;
|
||||||
|
char *label;
|
||||||
|
struct tlv_tlv_payload *payload;
|
||||||
|
struct tlv_field *preimage_field;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct command_result *
|
||||||
|
htlc_accepted_invoice_created(struct command *cmd, const char *buf UNUSED,
|
||||||
|
const jsmntok_t *result UNUSED,
|
||||||
|
struct keysend_in *ki)
|
||||||
{
|
{
|
||||||
struct json_stream *response;
|
int preimage_field_idx = ki->preimage_field - ki->payload->fields;
|
||||||
response = jsonrpc_stream_success(cmd);
|
|
||||||
json_add_string(response, "result", "resolve");
|
/* Remove the preimage field so `lightningd` knows how to handle
|
||||||
json_add_string(response, "payment_key", hexpreimage);
|
* this. */
|
||||||
return command_finished(cmd, response);
|
tal_arr_remove(&ki->payload->fields, preimage_field_idx);
|
||||||
|
|
||||||
|
/* Finally we can resolve the payment with the preimage. */
|
||||||
|
plugin_log(cmd->plugin, LOG_INFORM,
|
||||||
|
"Resolving incoming HTLC with preimage for payment_hash %s "
|
||||||
|
"provided in the onion payload.",
|
||||||
|
tal_hexstr(tmpctx, &ki->payment_hash, sizeof(struct sha256)));
|
||||||
|
return htlc_accepted_continue(cmd, ki->payload);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct command_result *htlc_accepted_call(struct command *cmd, const char *buf,
|
static struct command_result *htlc_accepted_call(struct command *cmd,
|
||||||
|
const char *buf,
|
||||||
const jsmntok_t *params)
|
const jsmntok_t *params)
|
||||||
{
|
{
|
||||||
const jsmntok_t *payloadt = json_delve(buf, params, ".onion.payload");
|
const jsmntok_t *payloadt = json_delve(buf, params, ".onion.payload");
|
||||||
@@ -41,13 +74,15 @@ static struct command_result *htlc_accepted_call(struct command *cmd, const char
|
|||||||
struct tlv_tlv_payload *payload;
|
struct tlv_tlv_payload *payload;
|
||||||
struct tlv_field *preimage_field = NULL;
|
struct tlv_field *preimage_field = NULL;
|
||||||
char *hexpreimage, *hexpaymenthash;
|
char *hexpreimage, *hexpaymenthash;
|
||||||
struct sha256 payment_hash;
|
|
||||||
bigsize_t s;
|
bigsize_t s;
|
||||||
bool unknown_even_type = false;
|
bool unknown_even_type = false;
|
||||||
struct tlv_field *field;
|
struct tlv_field *field;
|
||||||
|
struct keysend_in *ki;
|
||||||
|
struct out_req *req;
|
||||||
|
struct timeabs now = time_now();
|
||||||
|
|
||||||
if (!payloadt)
|
if (!payloadt)
|
||||||
return htlc_accepted_continue(cmd);
|
return htlc_accepted_continue(cmd, NULL);
|
||||||
|
|
||||||
rawpayload = json_tok_bin_from_hex(cmd, buf, payloadt);
|
rawpayload = json_tok_bin_from_hex(cmd, buf, payloadt);
|
||||||
max = tal_bytelen(rawpayload);
|
max = tal_bytelen(rawpayload);
|
||||||
@@ -55,9 +90,14 @@ static struct command_result *htlc_accepted_call(struct command *cmd, const char
|
|||||||
|
|
||||||
s = fromwire_varint(&rawpayload, &max);
|
s = fromwire_varint(&rawpayload, &max);
|
||||||
if (s != max) {
|
if (s != max) {
|
||||||
return htlc_accepted_continue(cmd);
|
return htlc_accepted_continue(cmd, NULL);
|
||||||
|
}
|
||||||
|
if (!fromwire_tlv_payload(&rawpayload, &max, payload)) {
|
||||||
|
plugin_log(
|
||||||
|
cmd->plugin, LOG_UNUSUAL, "Malformed TLV payload %.*s",
|
||||||
|
payloadt->end - payloadt->start, buf + payloadt->start);
|
||||||
|
return htlc_accepted_continue(cmd, NULL);
|
||||||
}
|
}
|
||||||
fromwire_tlv_payload(&rawpayload, &max, payload);
|
|
||||||
|
|
||||||
/* Try looking for the field that contains the preimage */
|
/* Try looking for the field that contains the preimage */
|
||||||
for (int i=0; i<tal_count(payload->fields); i++) {
|
for (int i=0; i<tal_count(payload->fields); i++) {
|
||||||
@@ -74,7 +114,7 @@ static struct command_result *htlc_accepted_call(struct command *cmd, const char
|
|||||||
/* If we don't have a preimage field then this is not a keysend, let
|
/* If we don't have a preimage field then this is not a keysend, let
|
||||||
* someone else take care of it. */
|
* someone else take care of it. */
|
||||||
if (preimage_field == NULL)
|
if (preimage_field == NULL)
|
||||||
return htlc_accepted_continue(cmd);
|
return htlc_accepted_continue(cmd, NULL);
|
||||||
|
|
||||||
if (unknown_even_type) {
|
if (unknown_even_type) {
|
||||||
plugin_log(cmd->plugin, LOG_UNUSUAL,
|
plugin_log(cmd->plugin, LOG_UNUSUAL,
|
||||||
@@ -82,7 +122,7 @@ static struct command_result *htlc_accepted_call(struct command *cmd, const char
|
|||||||
", can't safely accept the keysend. Deferring to "
|
", can't safely accept the keysend. Deferring to "
|
||||||
"other plugins.",
|
"other plugins.",
|
||||||
preimage_field->numtype);
|
preimage_field->numtype);
|
||||||
return htlc_accepted_continue(cmd);
|
return htlc_accepted_continue(cmd, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If the preimage is not 32 bytes long then we can't accept the
|
/* If the preimage is not 32 bytes long then we can't accept the
|
||||||
@@ -92,15 +132,21 @@ static struct command_result *htlc_accepted_call(struct command *cmd, const char
|
|||||||
"Sender specified a preimage that is %zu bytes long, "
|
"Sender specified a preimage that is %zu bytes long, "
|
||||||
"we expected 32 bytes. Ignoring this HTLC.",
|
"we expected 32 bytes. Ignoring this HTLC.",
|
||||||
preimage_field->length);
|
preimage_field->length);
|
||||||
return htlc_accepted_continue(cmd);
|
return htlc_accepted_continue(cmd, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ki = tal(cmd, struct keysend_in);
|
||||||
|
memcpy(&ki->payment_preimage, preimage_field->value, 32);
|
||||||
|
ki->label = tal_fmt(ki, "keysend-%lu.%09lu", now.ts.tv_sec, now.ts.tv_nsec);
|
||||||
|
ki->payload = tal_steal(ki, payload);
|
||||||
|
ki->preimage_field = preimage_field;
|
||||||
|
|
||||||
hexpreimage = tal_hex(cmd, preimage_field->value);
|
hexpreimage = tal_hex(cmd, preimage_field->value);
|
||||||
|
|
||||||
/* If the preimage doesn't hash to the payment_hash we must continue,
|
/* If the preimage doesn't hash to the payment_hash we must continue,
|
||||||
* maybe someone else knows how to handle these. */
|
* maybe someone else knows how to handle these. */
|
||||||
sha256(&payment_hash, preimage_field->value, preimage_field->length);
|
sha256(&ki->payment_hash, preimage_field->value, preimage_field->length);
|
||||||
hexpaymenthash = tal_hexstr(cmd, &payment_hash, sizeof(payment_hash));
|
hexpaymenthash = tal_hexstr(cmd, &ki->payment_hash, sizeof(ki->payment_hash));
|
||||||
if (!json_tok_streq(buf, payment_hash_tok, hexpaymenthash)) {
|
if (!json_tok_streq(buf, payment_hash_tok, hexpaymenthash)) {
|
||||||
plugin_log(
|
plugin_log(
|
||||||
cmd->plugin, LOG_UNUSUAL,
|
cmd->plugin, LOG_UNUSUAL,
|
||||||
@@ -109,15 +155,29 @@ static struct command_result *htlc_accepted_call(struct command *cmd, const char
|
|||||||
hexpreimage, hexpaymenthash,
|
hexpreimage, hexpaymenthash,
|
||||||
payment_hash_tok->end - payment_hash_tok->start,
|
payment_hash_tok->end - payment_hash_tok->start,
|
||||||
buf + payment_hash_tok->start);
|
buf + payment_hash_tok->start);
|
||||||
return htlc_accepted_continue(cmd);
|
tal_free(ki);
|
||||||
|
return htlc_accepted_continue(cmd, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Finally we can resolve the payment with the preimage. */
|
/* Now we can call `invoice` RPC to backfill an invoce matching this
|
||||||
plugin_log(cmd->plugin, LOG_INFORM,
|
* spontaneous payment, thus leaving us with an accounting
|
||||||
"Resolving incoming HTLC with preimage for payment_hash %s "
|
* trace. Creating the invoice is best effort: it may fail if the
|
||||||
"provided in the onion payload.",
|
* `payment_hash` is already attached to an existing invoice, and the
|
||||||
hexpaymenthash);
|
* label could collide (unlikely since we use the nanosecond time). If
|
||||||
return htlc_accepted_resolve(cmd, hexpreimage);
|
* the call to `invoice` fails we will just continue, and `lightningd`
|
||||||
|
* will be nice and reject the payment. */
|
||||||
|
req = jsonrpc_request_start(cmd->plugin, cmd, "invoice",
|
||||||
|
&htlc_accepted_invoice_created,
|
||||||
|
&htlc_accepted_invoice_created,
|
||||||
|
ki);
|
||||||
|
|
||||||
|
plugin_log(cmd->plugin, LOG_INFORM, "Inserting a new invoice for keysend with payment_hash %s", hexpaymenthash);
|
||||||
|
json_add_string(req->js, "msatoshi", "any");
|
||||||
|
json_add_string(req->js, "label", ki->label);
|
||||||
|
json_add_string(req->js, "description", "Spontaneous incoming payment through keysend");
|
||||||
|
json_add_preimage(req->js, "preimage", &ki->payment_preimage);
|
||||||
|
|
||||||
|
return send_outreq(cmd->plugin, req);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct plugin_hook hooks[] = {
|
static const struct plugin_hook hooks[] = {
|
||||||
|
|||||||
Reference in New Issue
Block a user