2016-09-06 09:17:49 +02:00
|
|
|
#include "invoice.h"
|
2018-03-16 04:45:08 +01:00
|
|
|
#include "json.h"
|
2016-09-06 09:17:49 +02:00
|
|
|
#include "jsonrpc.h"
|
|
|
|
#include "lightningd.h"
|
2017-11-22 01:25:39 +01:00
|
|
|
#include <bitcoin/address.h>
|
|
|
|
#include <bitcoin/base58.h>
|
|
|
|
#include <bitcoin/script.h>
|
2016-09-06 09:17:49 +02:00
|
|
|
#include <ccan/str/hex/hex.h>
|
|
|
|
#include <ccan/tal/str/str.h>
|
2017-11-22 01:25:39 +01:00
|
|
|
#include <common/bech32.h>
|
|
|
|
#include <common/bolt11.h>
|
2018-12-08 01:39:28 +01:00
|
|
|
#include <common/json_command.h>
|
|
|
|
#include <common/json_escaped.h>
|
|
|
|
#include <common/jsonrpc_errors.h>
|
|
|
|
#include <common/param.h>
|
json-invoice: add routeboost, warnings.
We split json_invoice(), as it now needs to round-trip to the gossipd,
and uniqueness checks need to happen *after* gossipd replies to avoid
a race.
For every candidate channel gossipd gives us, we check that it's in
state NORMAL (not shutting down, not still waiting for lockin), that
it's connected, and that it has capacity. We then choose one with
probability weighted by excess capacity, so larger channels are more
likely.
As a side effect of this, we can tell if an invoice is unpayble (no
channels have sufficient incoming capacity) or difficuly (no *online*
channels have sufficient capacity), so we add those warnings.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2018-09-27 07:29:17 +02:00
|
|
|
#include <common/pseudorand.h>
|
2017-08-28 18:02:01 +02:00
|
|
|
#include <common/utils.h>
|
2017-11-22 01:25:39 +01:00
|
|
|
#include <errno.h>
|
json-invoice: add routeboost, warnings.
We split json_invoice(), as it now needs to round-trip to the gossipd,
and uniqueness checks need to happen *after* gossipd replies to avoid
a race.
For every candidate channel gossipd gives us, we check that it's in
state NORMAL (not shutting down, not still waiting for lockin), that
it's connected, and that it has capacity. We then choose one with
probability weighted by excess capacity, so larger channels are more
likely.
As a side effect of this, we can tell if an invoice is unpayble (no
channels have sufficient incoming capacity) or difficuly (no *online*
channels have sufficient capacity), so we add those warnings.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2018-09-27 07:29:17 +02:00
|
|
|
#include <gossipd/gen_gossip_wire.h>
|
2018-09-20 05:06:42 +02:00
|
|
|
#include <hsmd/gen_hsm_wire.h>
|
2017-10-05 23:29:56 +02:00
|
|
|
#include <inttypes.h>
|
json-invoice: add routeboost, warnings.
We split json_invoice(), as it now needs to round-trip to the gossipd,
and uniqueness checks need to happen *after* gossipd replies to avoid
a race.
For every candidate channel gossipd gives us, we check that it's in
state NORMAL (not shutting down, not still waiting for lockin), that
it's connected, and that it has capacity. We then choose one with
probability weighted by excess capacity, so larger channels are more
likely.
As a side effect of this, we can tell if an invoice is unpayble (no
channels have sufficient incoming capacity) or difficuly (no *online*
channels have sufficient capacity), so we add those warnings.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2018-09-27 07:29:17 +02:00
|
|
|
#include <lightningd/channel.h>
|
2017-11-22 01:25:39 +01:00
|
|
|
#include <lightningd/hsm_control.h>
|
2017-10-05 23:29:56 +02:00
|
|
|
#include <lightningd/log.h>
|
2018-01-17 04:04:29 +01:00
|
|
|
#include <lightningd/options.h>
|
json-invoice: add routeboost, warnings.
We split json_invoice(), as it now needs to round-trip to the gossipd,
and uniqueness checks need to happen *after* gossipd replies to avoid
a race.
For every candidate channel gossipd gives us, we check that it's in
state NORMAL (not shutting down, not still waiting for lockin), that
it's connected, and that it has capacity. We then choose one with
probability weighted by excess capacity, so larger channels are more
likely.
As a side effect of this, we can tell if an invoice is unpayble (no
channels have sufficient incoming capacity) or difficuly (no *online*
channels have sufficient capacity), so we add those warnings.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2018-09-27 07:29:17 +02:00
|
|
|
#include <lightningd/peer_control.h>
|
|
|
|
#include <lightningd/subd.h>
|
2016-09-06 09:17:49 +02:00
|
|
|
#include <sodium/randombytes.h>
|
2017-11-22 01:25:39 +01:00
|
|
|
#include <wire/wire_sync.h>
|
2016-09-06 09:17:49 +02:00
|
|
|
|
2018-02-20 02:07:30 +01:00
|
|
|
static const char *invoice_status_str(const struct invoice_details *inv)
|
2018-01-17 04:04:29 +01:00
|
|
|
{
|
|
|
|
if (inv->state == PAID)
|
|
|
|
return "paid";
|
2018-01-23 02:02:10 +01:00
|
|
|
if (inv->state == EXPIRED)
|
2018-01-17 04:04:29 +01:00
|
|
|
return "expired";
|
|
|
|
return "unpaid";
|
|
|
|
}
|
|
|
|
|
2018-10-19 03:17:49 +02:00
|
|
|
static void json_add_invoice(struct json_stream *response,
|
2018-07-29 04:08:29 +02:00
|
|
|
const struct invoice_details *inv)
|
2018-01-13 12:14:13 +01:00
|
|
|
{
|
|
|
|
json_object_start(response, NULL);
|
2018-03-26 02:08:16 +02:00
|
|
|
json_add_escaped_string(response, "label", inv->label);
|
2018-02-28 17:26:58 +01:00
|
|
|
json_add_string(response, "bolt11", inv->bolt11);
|
2018-01-13 12:17:34 +01:00
|
|
|
json_add_hex(response, "payment_hash", &inv->rhash, sizeof(inv->rhash));
|
2018-01-13 12:14:13 +01:00
|
|
|
if (inv->msatoshi)
|
|
|
|
json_add_u64(response, "msatoshi", *inv->msatoshi);
|
2018-07-29 04:08:29 +02:00
|
|
|
json_add_string(response, "status", invoice_status_str(inv));
|
2018-01-13 12:19:33 +01:00
|
|
|
if (inv->state == PAID) {
|
2018-01-13 12:14:13 +01:00
|
|
|
json_add_u64(response, "pay_index", inv->pay_index);
|
2018-01-13 12:19:33 +01:00
|
|
|
json_add_u64(response, "msatoshi_received",
|
|
|
|
inv->msatoshi_received);
|
2018-01-19 10:53:24 +01:00
|
|
|
json_add_u64(response, "paid_at", inv->paid_timestamp);
|
2018-01-13 12:19:33 +01:00
|
|
|
}
|
2018-07-24 20:56:21 +02:00
|
|
|
|
|
|
|
if (inv->description)
|
|
|
|
json_add_string(response, "description", inv->description);
|
|
|
|
|
2018-01-19 10:53:24 +01:00
|
|
|
json_add_u64(response, "expires_at", inv->expiry_time);
|
|
|
|
|
2018-01-13 12:14:13 +01:00
|
|
|
json_object_end(response);
|
|
|
|
}
|
|
|
|
|
2018-12-16 05:52:06 +01:00
|
|
|
static struct command_result *tell_waiter(struct command *cmd,
|
|
|
|
const struct invoice *inv)
|
2016-09-06 09:17:49 +02:00
|
|
|
{
|
2018-10-19 03:17:49 +02:00
|
|
|
struct json_stream *response;
|
2018-07-27 12:57:02 +02:00
|
|
|
const struct invoice_details *details;
|
2016-09-06 09:17:49 +02:00
|
|
|
|
2018-07-27 12:57:02 +02:00
|
|
|
details = wallet_invoice_details(cmd, cmd->ld->wallet, *inv);
|
2018-10-19 03:17:48 +02:00
|
|
|
if (details->state == PAID) {
|
|
|
|
response = json_stream_success(cmd);
|
|
|
|
json_add_invoice(response, details);
|
2018-12-16 05:52:06 +01:00
|
|
|
return command_success(cmd, response);
|
2018-10-19 03:17:48 +02:00
|
|
|
} else {
|
2018-05-24 23:40:18 +02:00
|
|
|
/* FIXME: -2 should be a constant in jsonrpc_errors.h. */
|
2018-10-19 03:17:48 +02:00
|
|
|
response = json_stream_fail(cmd, -2,
|
|
|
|
"invoice expired during wait");
|
|
|
|
json_add_invoice(response, details);
|
2018-12-16 05:52:06 +01:00
|
|
|
return command_failed(cmd, response);
|
2018-05-24 23:40:18 +02:00
|
|
|
}
|
2016-09-06 09:17:49 +02:00
|
|
|
}
|
2018-10-19 03:17:48 +02:00
|
|
|
|
2018-01-14 15:15:30 +01:00
|
|
|
static void tell_waiter_deleted(struct command *cmd)
|
2017-12-27 13:55:22 +01:00
|
|
|
{
|
2018-05-24 23:40:18 +02:00
|
|
|
command_fail(cmd, LIGHTNINGD, "Invoice deleted during wait");
|
2017-12-27 13:55:22 +01:00
|
|
|
}
|
2018-01-14 15:15:30 +01:00
|
|
|
static void wait_on_invoice(const struct invoice *invoice, void *cmd)
|
2016-09-06 09:17:49 +02:00
|
|
|
{
|
2018-01-14 15:15:30 +01:00
|
|
|
if (invoice)
|
|
|
|
tell_waiter((struct command *) cmd, invoice);
|
|
|
|
else
|
|
|
|
tell_waiter_deleted((struct command *) cmd);
|
2016-09-06 09:17:49 +02:00
|
|
|
}
|
2016-11-11 00:02:04 +01:00
|
|
|
|
2017-11-22 01:25:39 +01:00
|
|
|
static bool hsm_sign_b11(const u5 *u5bytes,
|
|
|
|
const u8 *hrpu8,
|
|
|
|
secp256k1_ecdsa_recoverable_signature *rsig,
|
|
|
|
struct lightningd *ld)
|
|
|
|
{
|
2018-07-09 13:17:59 +02:00
|
|
|
u8 *msg = towire_hsm_sign_invoice(NULL, u5bytes, hrpu8);
|
2017-11-22 01:25:39 +01:00
|
|
|
|
|
|
|
if (!wire_sync_write(ld->hsm_fd, take(msg)))
|
|
|
|
fatal("Could not write to HSM: %s", strerror(errno));
|
|
|
|
|
2018-07-09 13:17:59 +02:00
|
|
|
msg = wire_sync_read(tmpctx, ld->hsm_fd);
|
2018-02-20 21:59:09 +01:00
|
|
|
if (!fromwire_hsm_sign_invoice_reply(msg, rsig))
|
2017-11-22 01:25:39 +01:00
|
|
|
fatal("HSM gave bad sign_invoice_reply %s",
|
|
|
|
tal_hex(msg, msg));
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-12-16 05:52:06 +01:00
|
|
|
static struct command_result *parse_fallback(struct command *cmd,
|
|
|
|
const char *buffer,
|
|
|
|
const jsmntok_t *fallback,
|
|
|
|
const u8 **fallback_script)
|
2018-04-05 07:13:51 +02:00
|
|
|
|
|
|
|
{
|
|
|
|
enum address_parse_result fallback_parse;
|
|
|
|
|
|
|
|
fallback_parse
|
|
|
|
= json_tok_address_scriptpubkey(cmd,
|
|
|
|
get_chainparams(cmd->ld),
|
|
|
|
buffer, fallback,
|
|
|
|
fallback_script);
|
|
|
|
if (fallback_parse == ADDRESS_PARSE_UNRECOGNIZED) {
|
2018-12-16 05:52:06 +01:00
|
|
|
return command_fail(cmd, LIGHTNINGD,
|
|
|
|
"Fallback address not valid");
|
2018-04-05 07:13:51 +02:00
|
|
|
} else if (fallback_parse == ADDRESS_PARSE_WRONG_NETWORK) {
|
2018-12-16 05:52:06 +01:00
|
|
|
return command_fail(cmd, LIGHTNINGD,
|
|
|
|
"Fallback address does not match our network %s",
|
|
|
|
get_chainparams(cmd->ld)->network_name);
|
2018-04-05 07:13:51 +02:00
|
|
|
}
|
2018-12-16 05:52:06 +01:00
|
|
|
return NULL;
|
2018-04-05 07:13:51 +02:00
|
|
|
}
|
|
|
|
|
json-invoice: add routeboost, warnings.
We split json_invoice(), as it now needs to round-trip to the gossipd,
and uniqueness checks need to happen *after* gossipd replies to avoid
a race.
For every candidate channel gossipd gives us, we check that it's in
state NORMAL (not shutting down, not still waiting for lockin), that
it's connected, and that it has capacity. We then choose one with
probability weighted by excess capacity, so larger channels are more
likely.
As a side effect of this, we can tell if an invoice is unpayble (no
channels have sufficient incoming capacity) or difficuly (no *online*
channels have sufficient capacity), so we add those warnings.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2018-09-27 07:29:17 +02:00
|
|
|
/* BOLT11 struct wants an array of arrays (can provide multiple routes) */
|
|
|
|
static struct route_info **select_inchan(const tal_t *ctx,
|
|
|
|
struct lightningd *ld,
|
|
|
|
u64 capacity_needed,
|
|
|
|
const struct route_info *inchans,
|
|
|
|
bool *any_offline)
|
|
|
|
{
|
|
|
|
const struct route_info *r = NULL;
|
|
|
|
struct route_info **ret;
|
|
|
|
|
|
|
|
*any_offline = false;
|
|
|
|
|
|
|
|
/* Weighted reservoir sampling.
|
|
|
|
* Based on https://en.wikipedia.org/wiki/Reservoir_sampling
|
|
|
|
* Algorithm A-Chao
|
|
|
|
*/
|
|
|
|
u64 wsum = 0;
|
|
|
|
for (size_t i = 0; i < tal_count(inchans); i++) {
|
|
|
|
struct peer *peer;
|
|
|
|
struct channel *c;
|
|
|
|
u64 msatoshi_avail;
|
|
|
|
|
|
|
|
/* Do we know about this peer? */
|
|
|
|
peer = peer_by_id(ld, &inchans[i].pubkey);
|
|
|
|
if (!peer)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* Does it have a channel in state CHANNELD_NORMAL */
|
|
|
|
c = peer_normal_channel(peer);
|
|
|
|
if (!c)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* Does it have sufficient capacity. */
|
|
|
|
msatoshi_avail = c->funding_satoshi * 1000 - c->our_msatoshi;
|
|
|
|
|
|
|
|
/* Even after reserve taken into account */
|
|
|
|
if (c->our_config.channel_reserve_satoshis * 1000
|
|
|
|
> msatoshi_avail)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
msatoshi_avail -= c->our_config.channel_reserve_satoshis * 1000;
|
|
|
|
if (msatoshi_avail < capacity_needed)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* Is it offline? */
|
|
|
|
if (c->owner == NULL) {
|
|
|
|
*any_offline = true;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Avoid divide-by-zero corner case. */
|
|
|
|
wsum += (msatoshi_avail - capacity_needed + 1);
|
|
|
|
if (pseudorand(1ULL << 32)
|
|
|
|
<= ((msatoshi_avail - capacity_needed + 1) << 32) / wsum)
|
|
|
|
r = &inchans[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!r)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
ret = tal_arr(ctx, struct route_info *, 1);
|
|
|
|
ret[0] = tal_dup(ret, struct route_info, r);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Encapsulating struct while we wait for gossipd to give us incoming channels */
|
|
|
|
struct invoice_info {
|
|
|
|
struct command *cmd;
|
|
|
|
struct preimage payment_preimage;
|
|
|
|
struct bolt11 *b11;
|
|
|
|
struct json_escaped *label;
|
|
|
|
};
|
|
|
|
|
|
|
|
static void gossipd_incoming_channels_reply(struct subd *gossipd,
|
|
|
|
const u8 *msg,
|
|
|
|
const int *fs,
|
|
|
|
struct invoice_info *info)
|
2016-09-06 09:17:49 +02:00
|
|
|
{
|
2018-10-19 03:17:49 +02:00
|
|
|
struct json_stream *response;
|
json-invoice: add routeboost, warnings.
We split json_invoice(), as it now needs to round-trip to the gossipd,
and uniqueness checks need to happen *after* gossipd replies to avoid
a race.
For every candidate channel gossipd gives us, we check that it's in
state NORMAL (not shutting down, not still waiting for lockin), that
it's connected, and that it has capacity. We then choose one with
probability weighted by excess capacity, so larger channels are more
likely.
As a side effect of this, we can tell if an invoice is unpayble (no
channels have sufficient incoming capacity) or difficuly (no *online*
channels have sufficient capacity), so we add those warnings.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2018-09-27 07:29:17 +02:00
|
|
|
struct route_info *inchans;
|
|
|
|
bool any_offline;
|
2018-02-23 02:04:47 +01:00
|
|
|
struct invoice invoice;
|
json-invoice: add routeboost, warnings.
We split json_invoice(), as it now needs to round-trip to the gossipd,
and uniqueness checks need to happen *after* gossipd replies to avoid
a race.
For every candidate channel gossipd gives us, we check that it's in
state NORMAL (not shutting down, not still waiting for lockin), that
it's connected, and that it has capacity. We then choose one with
probability weighted by excess capacity, so larger channels are more
likely.
As a side effect of this, we can tell if an invoice is unpayble (no
channels have sufficient incoming capacity) or difficuly (no *online*
channels have sufficient capacity), so we add those warnings.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2018-09-27 07:29:17 +02:00
|
|
|
char *b11enc;
|
2018-07-27 12:57:02 +02:00
|
|
|
const struct invoice_details *details;
|
json-invoice: add routeboost, warnings.
We split json_invoice(), as it now needs to round-trip to the gossipd,
and uniqueness checks need to happen *after* gossipd replies to avoid
a race.
For every candidate channel gossipd gives us, we check that it's in
state NORMAL (not shutting down, not still waiting for lockin), that
it's connected, and that it has capacity. We then choose one with
probability weighted by excess capacity, so larger channels are more
likely.
As a side effect of this, we can tell if an invoice is unpayble (no
channels have sufficient incoming capacity) or difficuly (no *online*
channels have sufficient capacity), so we add those warnings.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2018-09-27 07:29:17 +02:00
|
|
|
struct wallet *wallet = info->cmd->ld->wallet;
|
|
|
|
|
|
|
|
if (!fromwire_gossip_get_incoming_channels_reply(tmpctx, msg, &inchans))
|
|
|
|
fatal("Gossip gave bad GOSSIP_GET_INCOMING_CHANNELS_REPLY %s",
|
|
|
|
tal_hex(msg, msg));
|
|
|
|
|
|
|
|
info->b11->routes
|
|
|
|
= select_inchan(info->b11,
|
|
|
|
info->cmd->ld,
|
|
|
|
info->b11->msatoshi ? *info->b11->msatoshi : 1,
|
|
|
|
inchans,
|
|
|
|
&any_offline);
|
|
|
|
|
|
|
|
/* FIXME: add private routes if necessary! */
|
|
|
|
b11enc = bolt11_encode(info, info->b11, false,
|
|
|
|
hsm_sign_b11, info->cmd->ld);
|
|
|
|
|
|
|
|
/* Check duplicate preimage (unlikely unless they specified it!) */
|
|
|
|
if (wallet_invoice_find_by_rhash(wallet,
|
|
|
|
&invoice, &info->b11->payment_hash)) {
|
|
|
|
command_fail(info->cmd, INVOICE_PREIMAGE_ALREADY_EXISTS,
|
|
|
|
"preimage already used");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!wallet_invoice_create(wallet,
|
|
|
|
&invoice,
|
|
|
|
info->b11->msatoshi,
|
|
|
|
info->label,
|
|
|
|
info->b11->expiry,
|
|
|
|
b11enc,
|
|
|
|
info->b11->description,
|
|
|
|
&info->payment_preimage,
|
|
|
|
&info->b11->payment_hash)) {
|
|
|
|
command_fail(info->cmd, INVOICE_LABEL_ALREADY_EXISTS,
|
|
|
|
"Duplicate label '%s'", info->label->s);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Get details */
|
|
|
|
details = wallet_invoice_details(info, wallet, invoice);
|
|
|
|
|
2018-10-19 03:17:48 +02:00
|
|
|
response = json_stream_success(info->cmd);
|
json-invoice: add routeboost, warnings.
We split json_invoice(), as it now needs to round-trip to the gossipd,
and uniqueness checks need to happen *after* gossipd replies to avoid
a race.
For every candidate channel gossipd gives us, we check that it's in
state NORMAL (not shutting down, not still waiting for lockin), that
it's connected, and that it has capacity. We then choose one with
probability weighted by excess capacity, so larger channels are more
likely.
As a side effect of this, we can tell if an invoice is unpayble (no
channels have sufficient incoming capacity) or difficuly (no *online*
channels have sufficient capacity), so we add those warnings.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2018-09-27 07:29:17 +02:00
|
|
|
json_object_start(response, NULL);
|
|
|
|
json_add_hex(response, "payment_hash", details->rhash.u.u8,
|
|
|
|
sizeof(details->rhash));
|
|
|
|
json_add_u64(response, "expires_at", details->expiry_time);
|
|
|
|
json_add_string(response, "bolt11", details->bolt11);
|
|
|
|
|
|
|
|
/* Warn if there's not sufficient incoming capacity. */
|
|
|
|
if (tal_count(info->b11->routes) == 0) {
|
|
|
|
log_unusual(info->cmd->ld->log,
|
|
|
|
"invoice: insufficient incoming capacity for %"PRIu64
|
|
|
|
" msatoshis%s",
|
|
|
|
info->b11->msatoshi ? *info->b11->msatoshi : 0,
|
|
|
|
any_offline
|
|
|
|
? " (among currently connected peers)" : "");
|
|
|
|
|
|
|
|
if (any_offline)
|
|
|
|
json_add_string(response, "warning_offline",
|
|
|
|
"No peers with sufficient"
|
|
|
|
" incoming capacity are connected");
|
|
|
|
else
|
|
|
|
json_add_string(response, "warning_capacity",
|
|
|
|
"No channels have sufficient"
|
|
|
|
" incoming capacity");
|
|
|
|
}
|
|
|
|
json_object_end(response);
|
|
|
|
|
|
|
|
command_success(info->cmd, response);
|
|
|
|
}
|
|
|
|
|
2018-12-16 05:52:06 +01:00
|
|
|
static struct command_result *json_invoice(struct command *cmd,
|
|
|
|
const char *buffer,
|
|
|
|
const jsmntok_t *obj UNNEEDED,
|
|
|
|
const jsmntok_t *params)
|
json-invoice: add routeboost, warnings.
We split json_invoice(), as it now needs to round-trip to the gossipd,
and uniqueness checks need to happen *after* gossipd replies to avoid
a race.
For every candidate channel gossipd gives us, we check that it's in
state NORMAL (not shutting down, not still waiting for lockin), that
it's connected, and that it has capacity. We then choose one with
probability weighted by excess capacity, so larger channels are more
likely.
As a side effect of this, we can tell if an invoice is unpayble (no
channels have sufficient incoming capacity) or difficuly (no *online*
channels have sufficient capacity), so we add those warnings.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2018-09-27 07:29:17 +02:00
|
|
|
{
|
2018-08-29 22:44:04 +02:00
|
|
|
const jsmntok_t *fallbacks;
|
2018-07-16 22:48:38 +02:00
|
|
|
const jsmntok_t *preimagetok;
|
2018-01-14 15:15:30 +01:00
|
|
|
u64 *msatoshi_val;
|
json-invoice: add routeboost, warnings.
We split json_invoice(), as it now needs to round-trip to the gossipd,
and uniqueness checks need to happen *after* gossipd replies to avoid
a race.
For every candidate channel gossipd gives us, we check that it's in
state NORMAL (not shutting down, not still waiting for lockin), that
it's connected, and that it has capacity. We then choose one with
probability weighted by excess capacity, so larger channels are more
likely.
As a side effect of this, we can tell if an invoice is unpayble (no
channels have sufficient incoming capacity) or difficuly (no *online*
channels have sufficient capacity), so we add those warnings.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2018-09-27 07:29:17 +02:00
|
|
|
struct invoice_info *info;
|
2018-02-17 12:48:22 +01:00
|
|
|
const char *desc_val;
|
2018-04-05 07:13:51 +02:00
|
|
|
const u8 **fallback_scripts = NULL;
|
2018-08-13 18:16:41 +02:00
|
|
|
u64 *expiry;
|
json-invoice: add routeboost, warnings.
We split json_invoice(), as it now needs to round-trip to the gossipd,
and uniqueness checks need to happen *after* gossipd replies to avoid
a race.
For every candidate channel gossipd gives us, we check that it's in
state NORMAL (not shutting down, not still waiting for lockin), that
it's connected, and that it has capacity. We then choose one with
probability weighted by excess capacity, so larger channels are more
likely.
As a side effect of this, we can tell if an invoice is unpayble (no
channels have sufficient incoming capacity) or difficuly (no *online*
channels have sufficient capacity), so we add those warnings.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2018-09-27 07:29:17 +02:00
|
|
|
struct sha256 rhash;
|
|
|
|
|
|
|
|
info = tal(cmd, struct invoice_info);
|
|
|
|
info->cmd = cmd;
|
2016-09-06 09:17:49 +02:00
|
|
|
|
2018-07-16 22:48:38 +02:00
|
|
|
if (!param(cmd, buffer, params,
|
2018-12-16 05:50:06 +01:00
|
|
|
p_req("msatoshi", param_msat, &msatoshi_val),
|
|
|
|
p_req("label", param_label, &info->label),
|
|
|
|
p_req("description", param_escaped_string, &desc_val),
|
|
|
|
p_opt_def("expiry", param_u64, &expiry, 3600),
|
|
|
|
p_opt("fallbacks", param_array, &fallbacks),
|
|
|
|
p_opt("preimage", param_tok, &preimagetok),
|
2018-07-16 22:48:38 +02:00
|
|
|
NULL))
|
2018-12-16 05:52:06 +01:00
|
|
|
return command_param_failed();
|
2016-09-06 09:17:49 +02:00
|
|
|
|
json-invoice: add routeboost, warnings.
We split json_invoice(), as it now needs to round-trip to the gossipd,
and uniqueness checks need to happen *after* gossipd replies to avoid
a race.
For every candidate channel gossipd gives us, we check that it's in
state NORMAL (not shutting down, not still waiting for lockin), that
it's connected, and that it has capacity. We then choose one with
probability weighted by excess capacity, so larger channels are more
likely.
As a side effect of this, we can tell if an invoice is unpayble (no
channels have sufficient incoming capacity) or difficuly (no *online*
channels have sufficient capacity), so we add those warnings.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2018-09-27 07:29:17 +02:00
|
|
|
if (strlen(info->label->s) > INVOICE_MAX_LABEL_LEN) {
|
2018-12-16 05:52:06 +01:00
|
|
|
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
|
|
|
"Label '%s' over %u bytes", info->label->s,
|
|
|
|
INVOICE_MAX_LABEL_LEN);
|
2016-09-06 09:17:49 +02:00
|
|
|
}
|
2018-03-26 02:08:47 +02:00
|
|
|
|
|
|
|
if (strlen(desc_val) >= BOLT11_FIELD_BYTE_LIMIT) {
|
2018-12-16 05:52:06 +01:00
|
|
|
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
|
|
|
"Descriptions greater than %d bytes "
|
|
|
|
"not yet supported "
|
|
|
|
"(description length %zu)",
|
|
|
|
BOLT11_FIELD_BYTE_LIMIT,
|
|
|
|
strlen(desc_val));
|
2018-02-17 12:48:22 +01:00
|
|
|
}
|
2017-11-21 07:26:15 +01:00
|
|
|
|
2018-04-05 07:13:51 +02:00
|
|
|
if (fallbacks) {
|
|
|
|
const jsmntok_t *i, *end = json_next(fallbacks);
|
|
|
|
|
2018-09-27 02:19:24 +02:00
|
|
|
fallback_scripts = tal_arr(cmd, const u8 *, 0);
|
2018-04-05 07:13:51 +02:00
|
|
|
for (i = fallbacks + 1; i < end; i = json_next(i)) {
|
2018-12-16 05:52:06 +01:00
|
|
|
struct command_result *r;
|
|
|
|
r = parse_fallback(cmd, buffer, i,
|
|
|
|
tal_arr_expand(&fallback_scripts));
|
|
|
|
if (r)
|
|
|
|
return r;
|
2018-04-05 07:13:51 +02:00
|
|
|
}
|
2018-02-15 00:45:04 +01:00
|
|
|
}
|
|
|
|
|
2018-04-22 15:42:23 +02:00
|
|
|
if (preimagetok) {
|
|
|
|
/* Get secret preimage from user. */
|
|
|
|
if (!hex_decode(buffer + preimagetok->start,
|
|
|
|
preimagetok->end - preimagetok->start,
|
json-invoice: add routeboost, warnings.
We split json_invoice(), as it now needs to round-trip to the gossipd,
and uniqueness checks need to happen *after* gossipd replies to avoid
a race.
For every candidate channel gossipd gives us, we check that it's in
state NORMAL (not shutting down, not still waiting for lockin), that
it's connected, and that it has capacity. We then choose one with
probability weighted by excess capacity, so larger channels are more
likely.
As a side effect of this, we can tell if an invoice is unpayble (no
channels have sufficient incoming capacity) or difficuly (no *online*
channels have sufficient capacity), so we add those warnings.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2018-09-27 07:29:17 +02:00
|
|
|
&info->payment_preimage,
|
|
|
|
sizeof(info->payment_preimage))) {
|
2018-12-16 05:52:06 +01:00
|
|
|
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
|
|
|
"preimage must be 64 hex digits");
|
2018-04-22 15:42:23 +02:00
|
|
|
}
|
|
|
|
} else
|
|
|
|
/* Generate random secret preimage. */
|
json-invoice: add routeboost, warnings.
We split json_invoice(), as it now needs to round-trip to the gossipd,
and uniqueness checks need to happen *after* gossipd replies to avoid
a race.
For every candidate channel gossipd gives us, we check that it's in
state NORMAL (not shutting down, not still waiting for lockin), that
it's connected, and that it has capacity. We then choose one with
probability weighted by excess capacity, so larger channels are more
likely.
As a side effect of this, we can tell if an invoice is unpayble (no
channels have sufficient incoming capacity) or difficuly (no *online*
channels have sufficient capacity), so we add those warnings.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2018-09-27 07:29:17 +02:00
|
|
|
randombytes_buf(&info->payment_preimage,
|
|
|
|
sizeof(info->payment_preimage));
|
2018-04-22 15:42:23 +02:00
|
|
|
/* Generate preimage hash. */
|
json-invoice: add routeboost, warnings.
We split json_invoice(), as it now needs to round-trip to the gossipd,
and uniqueness checks need to happen *after* gossipd replies to avoid
a race.
For every candidate channel gossipd gives us, we check that it's in
state NORMAL (not shutting down, not still waiting for lockin), that
it's connected, and that it has capacity. We then choose one with
probability weighted by excess capacity, so larger channels are more
likely.
As a side effect of this, we can tell if an invoice is unpayble (no
channels have sufficient incoming capacity) or difficuly (no *online*
channels have sufficient capacity), so we add those warnings.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2018-09-27 07:29:17 +02:00
|
|
|
sha256(&rhash, &info->payment_preimage, sizeof(info->payment_preimage));
|
2018-04-25 01:04:33 +02:00
|
|
|
|
2017-10-26 05:06:19 +02:00
|
|
|
/* Construct bolt11 string. */
|
json-invoice: add routeboost, warnings.
We split json_invoice(), as it now needs to round-trip to the gossipd,
and uniqueness checks need to happen *after* gossipd replies to avoid
a race.
For every candidate channel gossipd gives us, we check that it's in
state NORMAL (not shutting down, not still waiting for lockin), that
it's connected, and that it has capacity. We then choose one with
probability weighted by excess capacity, so larger channels are more
likely.
As a side effect of this, we can tell if an invoice is unpayble (no
channels have sufficient incoming capacity) or difficuly (no *online*
channels have sufficient capacity), so we add those warnings.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2018-09-27 07:29:17 +02:00
|
|
|
info->b11 = new_bolt11(info, msatoshi_val);
|
|
|
|
info->b11->chain = get_chainparams(cmd->ld);
|
|
|
|
info->b11->timestamp = time_now().ts.tv_sec;
|
|
|
|
info->b11->payment_hash = rhash;
|
|
|
|
info->b11->receiver_id = cmd->ld->id;
|
|
|
|
info->b11->min_final_cltv_expiry = cmd->ld->config.cltv_final;
|
|
|
|
info->b11->expiry = *expiry;
|
|
|
|
info->b11->description = tal_steal(info->b11, desc_val);
|
|
|
|
info->b11->description_hash = NULL;
|
2018-02-28 17:26:58 +01:00
|
|
|
|
json-invoice: add routeboost, warnings.
We split json_invoice(), as it now needs to round-trip to the gossipd,
and uniqueness checks need to happen *after* gossipd replies to avoid
a race.
For every candidate channel gossipd gives us, we check that it's in
state NORMAL (not shutting down, not still waiting for lockin), that
it's connected, and that it has capacity. We then choose one with
probability weighted by excess capacity, so larger channels are more
likely.
As a side effect of this, we can tell if an invoice is unpayble (no
channels have sufficient incoming capacity) or difficuly (no *online*
channels have sufficient capacity), so we add those warnings.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2018-09-27 07:29:17 +02:00
|
|
|
if (fallback_scripts)
|
|
|
|
info->b11->fallbacks = tal_steal(info->b11, fallback_scripts);
|
2018-02-28 17:26:58 +01:00
|
|
|
|
json-invoice: add routeboost, warnings.
We split json_invoice(), as it now needs to round-trip to the gossipd,
and uniqueness checks need to happen *after* gossipd replies to avoid
a race.
For every candidate channel gossipd gives us, we check that it's in
state NORMAL (not shutting down, not still waiting for lockin), that
it's connected, and that it has capacity. We then choose one with
probability weighted by excess capacity, so larger channels are more
likely.
As a side effect of this, we can tell if an invoice is unpayble (no
channels have sufficient incoming capacity) or difficuly (no *online*
channels have sufficient capacity), so we add those warnings.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2018-09-27 07:29:17 +02:00
|
|
|
subd_req(cmd, cmd->ld->gossip,
|
|
|
|
take(towire_gossip_get_incoming_channels(NULL)),
|
|
|
|
-1, 0, gossipd_incoming_channels_reply, info);
|
2016-09-06 09:17:49 +02:00
|
|
|
|
2018-12-16 05:52:06 +01:00
|
|
|
return command_still_pending(cmd);
|
2016-09-06 09:17:49 +02:00
|
|
|
}
|
|
|
|
|
2017-01-04 04:38:15 +01:00
|
|
|
static const struct json_command invoice_command = {
|
2018-07-27 12:57:02 +02:00
|
|
|
"invoice", json_invoice, "Create an invoice for {msatoshi} with {label} "
|
|
|
|
"and {description} with optional {expiry} seconds "
|
2018-09-25 07:21:00 +02:00
|
|
|
"(default 1 hour), optional {fallbacks} address list"
|
|
|
|
"(default empty list) and optional {preimage} "
|
2018-07-27 12:57:02 +02:00
|
|
|
"(default autogenerated)"};
|
2017-01-04 04:38:15 +01:00
|
|
|
AUTODATA(json_command, &invoice_command);
|
2016-09-06 09:17:49 +02:00
|
|
|
|
2018-10-19 03:17:49 +02:00
|
|
|
static void json_add_invoices(struct json_stream *response,
|
2018-01-14 15:15:30 +01:00
|
|
|
struct wallet *wallet,
|
2018-07-29 04:08:29 +02:00
|
|
|
const struct json_escaped *label)
|
2016-09-06 09:17:49 +02:00
|
|
|
{
|
2018-02-24 13:11:14 +01:00
|
|
|
struct invoice_iterator it;
|
2018-07-27 12:57:02 +02:00
|
|
|
const struct invoice_details *details;
|
2016-09-06 09:17:49 +02:00
|
|
|
|
2018-06-14 06:50:47 +02:00
|
|
|
/* Don't iterate entire db if we're just after one. */
|
|
|
|
if (label) {
|
|
|
|
struct invoice invoice;
|
|
|
|
if (wallet_invoice_find_by_label(wallet, &invoice, label)) {
|
2018-07-27 12:57:02 +02:00
|
|
|
details = wallet_invoice_details(response, wallet, invoice);
|
|
|
|
json_add_invoice(response, details);
|
2018-06-14 06:50:47 +02:00
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-02-24 13:11:14 +01:00
|
|
|
memset(&it, 0, sizeof(it));
|
|
|
|
while (wallet_invoice_iterate(wallet, &it)) {
|
2018-07-27 12:57:02 +02:00
|
|
|
details = wallet_invoice_iterator_deref(response, wallet, &it);
|
|
|
|
json_add_invoice(response, details);
|
2016-09-06 09:17:49 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-16 05:52:06 +01:00
|
|
|
static struct command_result *json_listinvoices(struct command *cmd,
|
|
|
|
const char *buffer,
|
|
|
|
const jsmntok_t *obj UNNEEDED,
|
|
|
|
const jsmntok_t *params)
|
2016-09-06 09:17:49 +02:00
|
|
|
{
|
2018-03-26 02:08:16 +02:00
|
|
|
struct json_escaped *label;
|
2018-10-19 03:17:49 +02:00
|
|
|
struct json_stream *response;
|
2018-01-14 15:15:30 +01:00
|
|
|
struct wallet *wallet = cmd->ld->wallet;
|
2018-07-16 22:48:38 +02:00
|
|
|
if (!param(cmd, buffer, params,
|
2018-12-16 05:50:06 +01:00
|
|
|
p_opt("label", param_label, &label),
|
2018-07-16 22:48:38 +02:00
|
|
|
NULL))
|
2018-12-16 05:52:06 +01:00
|
|
|
return command_param_failed();
|
2018-10-19 03:17:48 +02:00
|
|
|
response = json_stream_success(cmd);
|
2018-07-29 04:08:29 +02:00
|
|
|
json_object_start(response, NULL);
|
|
|
|
json_array_start(response, "invoices");
|
|
|
|
json_add_invoices(response, wallet, label);
|
2016-09-06 09:17:49 +02:00
|
|
|
json_array_end(response);
|
2018-07-29 04:08:29 +02:00
|
|
|
json_object_end(response);
|
2018-12-16 05:52:06 +01:00
|
|
|
return command_success(cmd, response);
|
2016-09-06 09:17:49 +02:00
|
|
|
}
|
|
|
|
|
2018-01-16 21:29:32 +01:00
|
|
|
static const struct json_command listinvoices_command = {
|
2018-01-16 21:28:46 +01:00
|
|
|
"listinvoices",
|
|
|
|
json_listinvoices,
|
2018-01-22 09:55:07 +01:00
|
|
|
"Show invoice {label} (or all, if no {label})"
|
2018-01-16 21:28:46 +01:00
|
|
|
};
|
|
|
|
AUTODATA(json_command, &listinvoices_command);
|
|
|
|
|
2018-12-16 05:52:06 +01:00
|
|
|
static struct command_result *json_delinvoice(struct command *cmd,
|
|
|
|
const char *buffer,
|
|
|
|
const jsmntok_t *obj UNNEEDED,
|
|
|
|
const jsmntok_t *params)
|
2016-09-06 09:17:49 +02:00
|
|
|
{
|
2018-02-23 02:04:47 +01:00
|
|
|
struct invoice i;
|
2018-07-27 12:57:02 +02:00
|
|
|
const struct invoice_details *details;
|
2018-10-19 03:17:49 +02:00
|
|
|
struct json_stream *response;
|
2018-03-26 02:08:16 +02:00
|
|
|
const char *status, *actual_status;
|
|
|
|
struct json_escaped *label;
|
2018-01-14 15:15:30 +01:00
|
|
|
struct wallet *wallet = cmd->ld->wallet;
|
2016-09-06 09:17:49 +02:00
|
|
|
|
2018-07-16 22:48:38 +02:00
|
|
|
if (!param(cmd, buffer, params,
|
2018-12-16 05:50:06 +01:00
|
|
|
p_req("label", param_label, &label),
|
|
|
|
p_req("status", param_string, &status),
|
2018-07-16 22:48:38 +02:00
|
|
|
NULL))
|
2018-12-16 05:52:06 +01:00
|
|
|
return command_param_failed();
|
2016-09-06 09:17:49 +02:00
|
|
|
|
2018-02-23 02:04:47 +01:00
|
|
|
if (!wallet_invoice_find_by_label(wallet, &i, label)) {
|
2018-12-16 05:52:06 +01:00
|
|
|
return command_fail(cmd, LIGHTNINGD, "Unknown invoice");
|
2016-09-06 09:17:49 +02:00
|
|
|
}
|
2018-07-27 12:57:02 +02:00
|
|
|
|
|
|
|
details = wallet_invoice_details(cmd, cmd->ld->wallet, i);
|
2017-10-05 23:29:56 +02:00
|
|
|
|
2018-01-18 04:57:16 +01:00
|
|
|
/* This is time-sensitive, so only call once; otherwise error msg
|
|
|
|
* might not make sense if it changed! */
|
2018-07-27 12:57:02 +02:00
|
|
|
actual_status = invoice_status_str(details);
|
2018-01-18 04:57:16 +01:00
|
|
|
if (!streq(actual_status, status)) {
|
2018-12-16 05:52:06 +01:00
|
|
|
return command_fail(cmd, LIGHTNINGD,
|
|
|
|
"Invoice status is %s not %s",
|
|
|
|
actual_status, status);
|
2018-01-18 04:57:16 +01:00
|
|
|
}
|
|
|
|
|
2018-01-18 04:55:43 +01:00
|
|
|
if (!wallet_invoice_delete(wallet, i)) {
|
|
|
|
log_broken(cmd->ld->log,
|
|
|
|
"Error attempting to remove invoice %"PRIu64,
|
2018-02-23 02:04:47 +01:00
|
|
|
i.id);
|
2018-12-16 05:52:06 +01:00
|
|
|
return command_fail(cmd, LIGHTNINGD, "Database error");
|
2017-12-27 12:51:58 +01:00
|
|
|
}
|
|
|
|
|
2018-10-19 03:17:48 +02:00
|
|
|
response = json_stream_success(cmd);
|
|
|
|
json_add_invoice(response, details);
|
2018-12-16 05:52:06 +01:00
|
|
|
return command_success(cmd, response);
|
2016-09-06 09:17:49 +02:00
|
|
|
}
|
|
|
|
|
2017-01-04 04:38:15 +01:00
|
|
|
static const struct json_command delinvoice_command = {
|
2016-09-06 09:17:49 +02:00
|
|
|
"delinvoice",
|
|
|
|
json_delinvoice,
|
2018-01-18 04:57:16 +01:00
|
|
|
"Delete unpaid invoice {label} with {status}",
|
2016-09-06 09:17:49 +02:00
|
|
|
};
|
2017-01-04 04:38:15 +01:00
|
|
|
AUTODATA(json_command, &delinvoice_command);
|
2016-09-06 09:17:49 +02:00
|
|
|
|
2018-12-16 05:52:06 +01:00
|
|
|
static struct command_result *json_delexpiredinvoice(struct command *cmd,
|
|
|
|
const char *buffer,
|
|
|
|
const jsmntok_t *obj UNNEEDED,
|
|
|
|
const jsmntok_t *params)
|
2018-02-26 13:37:53 +01:00
|
|
|
{
|
2018-08-13 18:16:41 +02:00
|
|
|
u64 *maxexpirytime;
|
2018-02-26 13:37:53 +01:00
|
|
|
|
2018-07-16 22:48:38 +02:00
|
|
|
if (!param(cmd, buffer, params,
|
2018-12-16 05:50:06 +01:00
|
|
|
p_opt_def("maxexpirytime", param_u64, &maxexpirytime,
|
2018-08-13 18:16:41 +02:00
|
|
|
time_now().ts.tv_sec),
|
2018-07-16 22:48:38 +02:00
|
|
|
NULL))
|
2018-12-16 05:52:06 +01:00
|
|
|
return command_param_failed();
|
2018-02-26 13:37:53 +01:00
|
|
|
|
2018-08-13 18:16:41 +02:00
|
|
|
wallet_invoice_delete_expired(cmd->ld->wallet, *maxexpirytime);
|
2018-02-26 13:37:53 +01:00
|
|
|
|
2018-12-16 05:52:06 +01:00
|
|
|
return command_success(cmd, null_response(cmd));
|
2018-02-26 13:37:53 +01:00
|
|
|
}
|
|
|
|
static const struct json_command delexpiredinvoice_command = {
|
|
|
|
"delexpiredinvoice",
|
|
|
|
json_delexpiredinvoice,
|
|
|
|
"Delete all expired invoices that expired as of given {maxexpirytime} (a UNIX epoch time), or all expired invoices if not specified"
|
|
|
|
};
|
|
|
|
AUTODATA(json_command, &delexpiredinvoice_command);
|
|
|
|
|
2018-12-16 05:52:06 +01:00
|
|
|
static struct command_result *json_autocleaninvoice(struct command *cmd,
|
|
|
|
const char *buffer,
|
|
|
|
const jsmntok_t *obj UNNEEDED,
|
|
|
|
const jsmntok_t *params)
|
2018-03-17 16:12:37 +01:00
|
|
|
{
|
2018-08-13 18:16:41 +02:00
|
|
|
u64 *cycle;
|
|
|
|
u64 *exby;
|
2018-03-17 16:12:37 +01:00
|
|
|
|
2018-07-16 22:48:38 +02:00
|
|
|
if (!param(cmd, buffer, params,
|
2018-12-16 05:50:06 +01:00
|
|
|
p_opt_def("cycle_seconds", param_u64, &cycle, 3600),
|
|
|
|
p_opt_def("expired_by", param_u64, &exby, 86400),
|
2018-07-16 22:48:38 +02:00
|
|
|
NULL))
|
2018-12-16 05:52:06 +01:00
|
|
|
return command_param_failed();
|
2018-03-17 16:12:37 +01:00
|
|
|
|
2018-08-13 18:16:41 +02:00
|
|
|
wallet_invoice_autoclean(cmd->ld->wallet, *cycle, *exby);
|
2018-03-17 16:12:37 +01:00
|
|
|
|
2018-12-16 05:52:06 +01:00
|
|
|
return command_success(cmd, null_response(cmd));
|
2018-03-17 16:12:37 +01:00
|
|
|
}
|
|
|
|
static const struct json_command autocleaninvoice_command = {
|
|
|
|
"autocleaninvoice",
|
|
|
|
json_autocleaninvoice,
|
|
|
|
"Set up autoclean of expired invoices. "
|
|
|
|
"Perform cleanup every {cycle_seconds} (default 3600), or disable autoclean if 0. "
|
|
|
|
"Clean up expired invoices that have expired for {expired_by} seconds (default 86400). "
|
|
|
|
};
|
|
|
|
AUTODATA(json_command, &autocleaninvoice_command);
|
|
|
|
|
2018-12-16 05:52:06 +01:00
|
|
|
static struct command_result *json_waitanyinvoice(struct command *cmd,
|
|
|
|
const char *buffer,
|
|
|
|
const jsmntok_t *obj UNNEEDED,
|
|
|
|
const jsmntok_t *params)
|
2016-09-06 09:17:49 +02:00
|
|
|
{
|
2018-08-13 18:16:41 +02:00
|
|
|
u64 *pay_index;
|
2017-12-26 14:47:27 +01:00
|
|
|
struct wallet *wallet = cmd->ld->wallet;
|
2016-09-06 09:17:49 +02:00
|
|
|
|
2018-07-16 22:48:38 +02:00
|
|
|
if (!param(cmd, buffer, params,
|
2018-12-16 05:50:06 +01:00
|
|
|
p_opt_def("lastpay_index", param_u64, &pay_index, 0),
|
2018-07-16 22:48:38 +02:00
|
|
|
NULL))
|
2018-12-16 05:52:06 +01:00
|
|
|
return command_param_failed();
|
2017-12-26 14:47:27 +01:00
|
|
|
|
2018-01-14 15:15:30 +01:00
|
|
|
/* Set command as pending. We do not know if
|
|
|
|
* wallet_invoice_waitany will return immediately
|
|
|
|
* or not, so indicating pending is safest. */
|
2017-12-15 11:15:54 +01:00
|
|
|
command_still_pending(cmd);
|
2018-01-14 15:15:30 +01:00
|
|
|
|
|
|
|
/* Find next paid invoice. */
|
2018-08-13 18:16:41 +02:00
|
|
|
wallet_invoice_waitany(cmd, wallet, *pay_index,
|
2018-01-14 15:15:30 +01:00
|
|
|
&wait_on_invoice, (void*) cmd);
|
2018-12-16 05:52:06 +01:00
|
|
|
|
|
|
|
return command_its_complicated();
|
2016-09-06 09:17:49 +02:00
|
|
|
}
|
|
|
|
|
2017-01-17 22:09:09 +01:00
|
|
|
static const struct json_command waitanyinvoice_command = {
|
|
|
|
"waitanyinvoice",
|
|
|
|
json_waitanyinvoice,
|
2018-01-22 09:55:07 +01:00
|
|
|
"Wait for the next invoice to be paid, after {lastpay_index} (if supplied)"
|
2016-09-06 09:17:49 +02:00
|
|
|
};
|
2017-01-17 22:09:09 +01:00
|
|
|
AUTODATA(json_command, &waitanyinvoice_command);
|
2017-01-10 12:18:40 +01:00
|
|
|
|
|
|
|
|
|
|
|
/* Wait for an incoming payment matching the `label` in the JSON
|
|
|
|
* command. This will either return immediately if the payment has
|
|
|
|
* already been received or it may add the `cmd` to the list of
|
|
|
|
* waiters, if the payment is still pending.
|
|
|
|
*/
|
2018-12-16 05:52:06 +01:00
|
|
|
static struct command_result *json_waitinvoice(struct command *cmd,
|
|
|
|
const char *buffer,
|
|
|
|
const jsmntok_t *obj UNNEEDED,
|
|
|
|
const jsmntok_t *params)
|
2017-01-10 12:18:40 +01:00
|
|
|
{
|
2018-02-23 02:04:47 +01:00
|
|
|
struct invoice i;
|
2018-07-27 12:57:02 +02:00
|
|
|
const struct invoice_details *details;
|
2018-01-14 15:15:30 +01:00
|
|
|
struct wallet *wallet = cmd->ld->wallet;
|
2018-03-26 02:08:16 +02:00
|
|
|
struct json_escaped *label;
|
2017-01-10 12:18:40 +01:00
|
|
|
|
2018-07-16 22:48:38 +02:00
|
|
|
if (!param(cmd, buffer, params,
|
2018-12-16 05:50:06 +01:00
|
|
|
p_req("label", param_label, &label),
|
2018-07-16 22:48:38 +02:00
|
|
|
NULL))
|
2018-12-16 05:52:06 +01:00
|
|
|
return command_param_failed();
|
2017-01-10 12:18:40 +01:00
|
|
|
|
2018-02-23 02:04:47 +01:00
|
|
|
if (!wallet_invoice_find_by_label(wallet, &i, label)) {
|
2018-12-16 05:52:06 +01:00
|
|
|
return command_fail(cmd, LIGHTNINGD, "Label not found");
|
2018-02-20 02:07:30 +01:00
|
|
|
}
|
2018-07-27 12:57:02 +02:00
|
|
|
details = wallet_invoice_details(cmd, cmd->ld->wallet, i);
|
2018-02-20 02:07:30 +01:00
|
|
|
|
2018-02-23 02:04:47 +01:00
|
|
|
/* If paid or expired return immediately */
|
2018-07-27 12:57:02 +02:00
|
|
|
if (details->state == PAID || details->state == EXPIRED) {
|
2018-12-16 05:52:06 +01:00
|
|
|
return tell_waiter(cmd, &i);
|
2017-10-03 18:31:07 +02:00
|
|
|
} else {
|
|
|
|
/* There is an unpaid one matching, let's wait... */
|
2017-12-15 11:15:54 +01:00
|
|
|
command_still_pending(cmd);
|
2018-01-14 15:15:30 +01:00
|
|
|
wallet_invoice_waitone(cmd, wallet, i,
|
|
|
|
&wait_on_invoice, (void *) cmd);
|
2018-12-16 05:52:06 +01:00
|
|
|
return command_its_complicated();
|
2017-01-10 12:18:40 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-17 22:09:09 +01:00
|
|
|
static const struct json_command waitinvoice_command = {
|
|
|
|
"waitinvoice",
|
|
|
|
json_waitinvoice,
|
2018-01-23 02:02:10 +01:00
|
|
|
"Wait for an incoming payment matching the invoice with {label}, or if the invoice expires"
|
2017-01-10 12:18:40 +01:00
|
|
|
};
|
2017-01-17 22:09:09 +01:00
|
|
|
AUTODATA(json_command, &waitinvoice_command);
|
2017-11-22 01:25:39 +01:00
|
|
|
|
2018-10-19 03:17:49 +02:00
|
|
|
static void json_add_fallback(struct json_stream *response,
|
2018-04-05 07:13:51 +02:00
|
|
|
const char *fieldname,
|
|
|
|
const u8 *fallback,
|
|
|
|
const struct chainparams *chain)
|
|
|
|
{
|
|
|
|
struct bitcoin_address pkh;
|
|
|
|
struct ripemd160 sh;
|
|
|
|
struct sha256 wsh;
|
|
|
|
|
|
|
|
json_object_start(response, fieldname);
|
|
|
|
if (is_p2pkh(fallback, &pkh)) {
|
|
|
|
json_add_string(response, "type", "P2PKH");
|
|
|
|
json_add_string(response, "addr",
|
|
|
|
bitcoin_to_base58(tmpctx, chain->testnet, &pkh));
|
|
|
|
} else if (is_p2sh(fallback, &sh)) {
|
|
|
|
json_add_string(response, "type", "P2SH");
|
|
|
|
json_add_string(response, "addr",
|
|
|
|
p2sh_to_base58(tmpctx, chain->testnet, &sh));
|
|
|
|
} else if (is_p2wpkh(fallback, &pkh)) {
|
|
|
|
char out[73 + strlen(chain->bip173_name)];
|
|
|
|
json_add_string(response, "type", "P2WPKH");
|
|
|
|
if (segwit_addr_encode(out, chain->bip173_name, 0,
|
|
|
|
(const u8 *)&pkh, sizeof(pkh)))
|
|
|
|
json_add_string(response, "addr", out);
|
|
|
|
} else if (is_p2wsh(fallback, &wsh)) {
|
|
|
|
char out[73 + strlen(chain->bip173_name)];
|
|
|
|
json_add_string(response, "type", "P2WSH");
|
|
|
|
if (segwit_addr_encode(out, chain->bip173_name, 0,
|
|
|
|
(const u8 *)&wsh, sizeof(wsh)))
|
|
|
|
json_add_string(response, "addr", out);
|
|
|
|
}
|
2018-07-28 07:53:33 +02:00
|
|
|
json_add_hex_talarr(response, "hex", fallback);
|
2018-04-05 07:13:51 +02:00
|
|
|
json_object_end(response);
|
|
|
|
}
|
|
|
|
|
2018-12-16 05:52:06 +01:00
|
|
|
static struct command_result *json_decodepay(struct command *cmd,
|
|
|
|
const char *buffer,
|
|
|
|
const jsmntok_t *obj UNNEEDED,
|
|
|
|
const jsmntok_t *params)
|
2017-11-22 01:25:39 +01:00
|
|
|
{
|
|
|
|
struct bolt11 *b11;
|
2018-10-19 03:17:49 +02:00
|
|
|
struct json_stream *response;
|
2018-10-19 03:17:48 +02:00
|
|
|
const char *str, *desc;
|
|
|
|
char *fail;
|
2017-11-22 01:25:39 +01:00
|
|
|
|
2018-07-16 22:48:38 +02:00
|
|
|
if (!param(cmd, buffer, params,
|
2018-12-16 05:50:06 +01:00
|
|
|
p_req("bolt11", param_string, &str),
|
|
|
|
p_opt("description", param_string, &desc),
|
2018-07-16 22:48:38 +02:00
|
|
|
NULL))
|
2018-12-16 05:52:06 +01:00
|
|
|
return command_param_failed();
|
2017-11-22 01:25:39 +01:00
|
|
|
|
|
|
|
b11 = bolt11_decode(cmd, str, desc, &fail);
|
|
|
|
|
|
|
|
if (!b11) {
|
2018-12-16 05:52:06 +01:00
|
|
|
return command_fail(cmd, LIGHTNINGD, "Invalid bolt11: %s", fail);
|
2017-11-22 01:25:39 +01:00
|
|
|
}
|
|
|
|
|
2018-10-19 03:17:48 +02:00
|
|
|
response = json_stream_success(cmd);
|
2017-11-22 01:25:39 +01:00
|
|
|
json_object_start(response, NULL);
|
|
|
|
|
|
|
|
json_add_string(response, "currency", b11->chain->bip173_name);
|
2018-01-19 10:53:24 +01:00
|
|
|
json_add_u64(response, "created_at", b11->timestamp);
|
2017-11-22 01:25:39 +01:00
|
|
|
json_add_u64(response, "expiry", b11->expiry);
|
|
|
|
json_add_pubkey(response, "payee", &b11->receiver_id);
|
|
|
|
if (b11->msatoshi)
|
|
|
|
json_add_u64(response, "msatoshi", *b11->msatoshi);
|
2018-03-26 02:08:47 +02:00
|
|
|
if (b11->description) {
|
|
|
|
struct json_escaped *esc = json_escape(NULL, b11->description);
|
|
|
|
json_add_escaped_string(response, "description", take(esc));
|
|
|
|
}
|
2017-11-22 01:25:39 +01:00
|
|
|
if (b11->description_hash)
|
|
|
|
json_add_hex(response, "description_hash",
|
|
|
|
b11->description_hash,
|
|
|
|
sizeof(*b11->description_hash));
|
2018-01-16 20:44:32 +01:00
|
|
|
json_add_num(response, "min_final_cltv_expiry",
|
|
|
|
b11->min_final_cltv_expiry);
|
2018-04-05 07:13:51 +02:00
|
|
|
if (tal_count(b11->fallbacks)) {
|
|
|
|
json_array_start(response, "fallbacks");
|
|
|
|
for (size_t i = 0; i < tal_count(b11->fallbacks); i++)
|
|
|
|
json_add_fallback(response, NULL,
|
|
|
|
b11->fallbacks[i], b11->chain);
|
|
|
|
json_array_end(response);
|
2017-11-22 01:25:39 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (tal_count(b11->routes)) {
|
|
|
|
size_t i, n;
|
|
|
|
|
|
|
|
json_array_start(response, "routes");
|
|
|
|
for (i = 0; i < tal_count(b11->routes); i++) {
|
|
|
|
json_array_start(response, NULL);
|
|
|
|
for (n = 0; n < tal_count(b11->routes[i]); n++) {
|
|
|
|
json_object_start(response, NULL);
|
|
|
|
json_add_pubkey(response, "pubkey",
|
|
|
|
&b11->routes[i][n].pubkey);
|
|
|
|
json_add_short_channel_id(response,
|
|
|
|
"short_channel_id",
|
|
|
|
&b11->routes[i][n]
|
|
|
|
.short_channel_id);
|
2017-12-12 01:34:07 +01:00
|
|
|
json_add_u64(response, "fee_base_msat",
|
|
|
|
b11->routes[i][n].fee_base_msat);
|
|
|
|
json_add_u64(response, "fee_proportional_millionths",
|
|
|
|
b11->routes[i][n].fee_proportional_millionths);
|
2017-11-22 01:25:39 +01:00
|
|
|
json_add_num(response, "cltv_expiry_delta",
|
|
|
|
b11->routes[i][n]
|
|
|
|
.cltv_expiry_delta);
|
|
|
|
json_object_end(response);
|
|
|
|
}
|
|
|
|
json_array_end(response);
|
|
|
|
}
|
|
|
|
json_array_end(response);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!list_empty(&b11->extra_fields)) {
|
|
|
|
struct bolt11_field *extra;
|
|
|
|
|
|
|
|
json_array_start(response, "extra");
|
|
|
|
list_for_each(&b11->extra_fields, extra, list) {
|
2018-07-28 08:00:16 +02:00
|
|
|
char *data = tal_arr(cmd, char, tal_count(extra->data)+1);
|
2017-11-22 01:25:39 +01:00
|
|
|
size_t i;
|
|
|
|
|
2018-07-28 08:00:16 +02:00
|
|
|
for (i = 0; i < tal_count(extra->data); i++)
|
2017-11-22 01:25:39 +01:00
|
|
|
data[i] = bech32_charset[extra->data[i]];
|
|
|
|
data[i] = '\0';
|
|
|
|
json_object_start(response, NULL);
|
|
|
|
json_add_string(response, "tag",
|
|
|
|
tal_fmt(data, "%c", extra->tag));
|
|
|
|
json_add_string(response, "data", data);
|
|
|
|
tal_free(data);
|
|
|
|
json_object_end(response);
|
|
|
|
}
|
|
|
|
json_array_end(response);
|
|
|
|
}
|
|
|
|
|
|
|
|
json_add_hex(response, "payment_hash",
|
|
|
|
&b11->payment_hash, sizeof(b11->payment_hash));
|
|
|
|
|
|
|
|
json_add_string(response, "signature",
|
|
|
|
type_to_string(cmd, secp256k1_ecdsa_signature,
|
|
|
|
&b11->sig));
|
|
|
|
json_object_end(response);
|
2018-12-16 05:52:06 +01:00
|
|
|
return command_success(cmd, response);
|
2017-11-22 01:25:39 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static const struct json_command decodepay_command = {
|
|
|
|
"decodepay",
|
|
|
|
json_decodepay,
|
2018-01-22 09:55:07 +01:00
|
|
|
"Decode {bolt11}, using {description} if necessary"
|
2017-11-22 01:25:39 +01:00
|
|
|
};
|
|
|
|
AUTODATA(json_command, &decodepay_command);
|