mirror of
https://github.com/ElementsProject/lightning.git
synced 2025-02-21 14:24:09 +01:00
libplugin/pay: allow shortcut for self-pay.
This is the simplest solution, not the best, but there's significant risk in try to remove the "we have a path" assumption in the code pay code. Includes removing a `tal_steal` which was incorrect: the buffer has the same lifetime as the plugin, so if we steal it then things get messy when we free the struct payment. Signed-off-by: Rusty Russell <rusty@rustcorp.com.au> Changelog-Added: Plugins: `pay` will now pay your own invoices if you try.
This commit is contained in:
parent
18760db66d
commit
620135335d
6 changed files with 116 additions and 38 deletions
|
@ -119,7 +119,7 @@ The following error codes may occur:
|
|||
- 201: Already paid with this *hash* using different amount or
|
||||
destination.
|
||||
- 203: Permanent failure at destination. The *data* field of the error
|
||||
will be routing failure object.
|
||||
will be routing failure object (except for self-payment, which currently returns the error directly from lightning-sendpay(7)).
|
||||
- 205: Unable to find a route.
|
||||
- 206: Route too expensive. Either the fee or the needed total
|
||||
locktime for the route exceeds your *maxfeepercent* or *maxdelay*
|
||||
|
|
|
@ -1156,9 +1156,9 @@ send_payment_core(struct lightningd *ld,
|
|||
first_hop->amount,
|
||||
total_msat,
|
||||
NULL,
|
||||
take(path_secrets),
|
||||
take(route_nodes),
|
||||
take(route_channels),
|
||||
path_secrets,
|
||||
route_nodes,
|
||||
route_channels,
|
||||
invstring,
|
||||
label,
|
||||
description,
|
||||
|
|
|
@ -1933,6 +1933,45 @@ static void payment_notify_failure(struct payment *p, const char *error_message)
|
|||
plugin_notification_end(p->plugin, n);
|
||||
}
|
||||
|
||||
/* Code shared by selfpay fast-path: populate JSON output for successful
|
||||
* payment, and send pay_success notification. */
|
||||
void json_add_payment_success(struct json_stream *js,
|
||||
struct payment *p,
|
||||
const struct preimage *preimage,
|
||||
const struct payment_tree_result *result)
|
||||
{
|
||||
struct json_stream *n;
|
||||
struct payment *root = payment_root(p);
|
||||
|
||||
json_add_node_id(js, "destination", p->destination);
|
||||
json_add_sha256(js, "payment_hash", p->payment_hash);
|
||||
json_add_timeabs(js, "created_at", p->start_time);
|
||||
if (result)
|
||||
json_add_num(js, "parts", result->attempts);
|
||||
else
|
||||
json_add_num(js, "parts", 1);
|
||||
|
||||
json_add_amount_msat(js, "amount_msat", p->amount);
|
||||
if (result)
|
||||
json_add_amount_msat(js, "amount_sent_msat", result->sent);
|
||||
else
|
||||
json_add_amount_msat(js, "amount_sent_msat", p->amount);
|
||||
|
||||
if (result && result->leafstates != PAYMENT_STEP_SUCCESS)
|
||||
json_add_string(js, "warning_partial_completion",
|
||||
"Some parts of the payment are not yet "
|
||||
"completed, but we have the confirmation "
|
||||
"from the recipient.");
|
||||
json_add_preimage(js, "payment_preimage", preimage);
|
||||
json_add_string(js, "status", "complete");
|
||||
|
||||
n = plugin_notification_start(p->plugin, "pay_success");
|
||||
json_add_sha256(n, "payment_hash", p->payment_hash);
|
||||
if (root->invstring != NULL)
|
||||
json_add_string(n, "bolt11", root->invstring);
|
||||
plugin_notification_end(p->plugin, n);
|
||||
}
|
||||
|
||||
/* This function is called whenever a payment ends up in a final state, or all
|
||||
* leafs in the subtree rooted in the payment are all in a final state. It is
|
||||
* called only once, and it is guaranteed to be called in post-order
|
||||
|
@ -1943,8 +1982,6 @@ static void payment_finished(struct payment *p)
|
|||
struct json_stream *ret;
|
||||
struct command *cmd = p->cmd;
|
||||
const char *msg;
|
||||
struct json_stream *n;
|
||||
struct payment *root = payment_root(p);
|
||||
|
||||
/* Either none of the leaf attempts succeeded yet, or we have a
|
||||
* preimage. */
|
||||
|
@ -1969,30 +2006,8 @@ static void payment_finished(struct payment *p)
|
|||
p->on_payment_success(p);
|
||||
|
||||
ret = jsonrpc_stream_success(cmd);
|
||||
json_add_node_id(ret, "destination", p->destination);
|
||||
json_add_sha256(ret, "payment_hash", p->payment_hash);
|
||||
json_add_timeabs(ret, "created_at", p->start_time);
|
||||
json_add_num(ret, "parts", result.attempts);
|
||||
|
||||
json_add_amount_msat(ret, "amount_msat", p->amount);
|
||||
json_add_amount_msat(ret, "amount_sent_msat",
|
||||
result.sent);
|
||||
|
||||
if (result.leafstates != PAYMENT_STEP_SUCCESS)
|
||||
json_add_string(
|
||||
ret, "warning_partial_completion",
|
||||
"Some parts of the payment are not yet "
|
||||
"completed, but we have the confirmation "
|
||||
"from the recipient.");
|
||||
json_add_preimage(ret, "payment_preimage", result.preimage);
|
||||
|
||||
json_add_string(ret, "status", "complete");
|
||||
|
||||
n = plugin_notification_start(p->plugin, "pay_success");
|
||||
json_add_sha256(n, "payment_hash", p->payment_hash);
|
||||
if (root->invstring != NULL)
|
||||
json_add_string(n, "bolt11", root->invstring);
|
||||
plugin_notification_end(p->plugin, n);
|
||||
json_add_payment_success(ret, p, result.preimage,
|
||||
&result);
|
||||
|
||||
if (command_finished(cmd, ret)) {/* Ignore result. */}
|
||||
p->cmd = NULL;
|
||||
|
|
|
@ -484,6 +484,12 @@ void payment_abort(struct payment *p, const char *fmt, ...) PRINTF_FMT(2,3);
|
|||
struct payment *payment_root(struct payment *p);
|
||||
struct payment_tree_result payment_collect_result(struct payment *p);
|
||||
|
||||
/* Add fields for successful payment: result can be NULL for selfpay */
|
||||
void json_add_payment_success(struct json_stream *js,
|
||||
struct payment *p,
|
||||
const struct preimage *preimage,
|
||||
const struct payment_tree_result *result);
|
||||
|
||||
/* For special effects, like inspecting your own routes. */
|
||||
struct gossmap *get_gossmap(struct plugin *plugin);
|
||||
|
||||
|
|
|
@ -799,6 +799,55 @@ static void on_payment_failure(struct payment *payment)
|
|||
}
|
||||
}
|
||||
|
||||
static struct command_result *selfpay_success(struct command *cmd,
|
||||
const char *buf,
|
||||
const jsmntok_t *result,
|
||||
struct payment *p)
|
||||
{
|
||||
struct json_stream *ret = jsonrpc_stream_success(cmd);
|
||||
struct preimage preimage;
|
||||
const char *err;
|
||||
|
||||
err = json_scan(tmpctx, buf, result,
|
||||
"{payment_preimage:%}",
|
||||
JSON_SCAN(json_to_preimage, &preimage));
|
||||
if (err)
|
||||
plugin_err(p->plugin,
|
||||
"selfpay didn't have payment_preimage? %.*s",
|
||||
json_tok_full_len(result),
|
||||
json_tok_full(buf, result));
|
||||
json_add_payment_success(ret, p, &preimage, NULL);
|
||||
return command_finished(cmd, ret);
|
||||
}
|
||||
|
||||
static struct command_result *selfpay(struct command *cmd, struct payment *p)
|
||||
{
|
||||
struct out_req *req;
|
||||
|
||||
/* This "struct payment" simply gets freed once command is done. */
|
||||
tal_steal(cmd, p);
|
||||
|
||||
req = jsonrpc_request_start(cmd->plugin, cmd, "sendpay",
|
||||
selfpay_success,
|
||||
forward_error, p);
|
||||
/* Empty route means "to-self" */
|
||||
json_array_start(req->js, "route");
|
||||
json_array_end(req->js);
|
||||
json_add_sha256(req->js, "payment_hash", p->payment_hash);
|
||||
if (p->label)
|
||||
json_add_string(req->js, "label", p->label);
|
||||
json_add_amount_msat(req->js, "amount_msat", p->amount);
|
||||
json_add_string(req->js, "bolt11", p->invstring);
|
||||
if (p->payment_secret)
|
||||
json_add_secret(req->js, "payment_secret", p->payment_secret);
|
||||
json_add_u64(req->js, "groupid", p->groupid);
|
||||
if (p->payment_metadata)
|
||||
json_add_hex_talarr(req->js, "payment_metadata", p->payment_metadata);
|
||||
if (p->description)
|
||||
json_add_string(req->js, "description", p->description);
|
||||
return send_outreq(cmd->plugin, req);
|
||||
}
|
||||
|
||||
/* We are interested in any prior attempts to pay this payment_hash /
|
||||
* invoice so we can set the `groupid` correctly and ensure we don't
|
||||
* already have a pending payment running. We also collect the summary
|
||||
|
@ -916,6 +965,11 @@ payment_listsendpays_previous(struct command *cmd, const char *buf,
|
|||
p->groupid = last_group + 1;
|
||||
p->on_payment_success = on_payment_success;
|
||||
p->on_payment_failure = on_payment_failure;
|
||||
|
||||
/* Bypass everything if we're doing (synchronous) self-pay */
|
||||
if (node_id_eq(&my_id, p->destination))
|
||||
return selfpay(cmd, p);
|
||||
|
||||
payment_start(p);
|
||||
return command_still_pending(cmd);
|
||||
}
|
||||
|
@ -1166,13 +1220,8 @@ static struct command_result *json_pay(struct command *cmd,
|
|||
"This payment blinded path fee overflows!");
|
||||
}
|
||||
|
||||
if (node_id_eq(&my_id, p->destination))
|
||||
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
||||
"This payment is destined for ourselves. "
|
||||
"Self-payments are not supported");
|
||||
|
||||
p->local_id = &my_id;
|
||||
p->json_buffer = tal_steal(p, buf);
|
||||
p->json_buffer = buf;
|
||||
p->json_toks = params;
|
||||
p->why = "Initial attempt";
|
||||
p->constraints.cltv_budget = *maxdelay;
|
||||
|
|
|
@ -4830,9 +4830,17 @@ def test_self_pay(node_factory):
|
|||
l1, l2 = node_factory.line_graph(2, wait_for_announce=True)
|
||||
|
||||
inv = l1.rpc.invoice(10000, 'test', 'test')['bolt11']
|
||||
l1.rpc.pay(inv)
|
||||
|
||||
with pytest.raises(RpcError):
|
||||
l1.rpc.pay(inv)
|
||||
# We can pay twice, no problem!
|
||||
l1.rpc.pay(inv)
|
||||
|
||||
inv2 = l1.rpc.invoice(10000, 'test2', 'test2')['bolt11']
|
||||
l1.rpc.delinvoice('test2', 'unpaid')
|
||||
|
||||
with pytest.raises(RpcError, match=r'Unknown invoice') as excinfo:
|
||||
l1.rpc.pay(inv2)
|
||||
assert excinfo.value.error['code'] == 203
|
||||
|
||||
|
||||
@unittest.skipIf(TEST_NETWORK != 'regtest', "Canned invoice is network specific")
|
||||
|
|
Loading…
Add table
Reference in a new issue