core-lightning/lightningd/invoice.c
Rusty Russell 2be1f3fe1b lightningd: extract routehint selection code.
We're going to want this for bolt13 formation as well.

As a result of reworking the logic into "candidate selection" then
"route hint selection", we need to change the way round-robin works.
We use a simple incrementing index now.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2020-09-08 19:47:11 +09:30

1428 lines
45 KiB
C

#include "invoice.h"
#include <bitcoin/address.h>
#include <bitcoin/base58.h>
#include <bitcoin/script.h>
#include <ccan/array_size/array_size.h>
#include <ccan/asort/asort.h>
#include <ccan/json_escape/json_escape.h>
#include <ccan/str/hex/hex.h>
#include <ccan/tal/str/str.h>
#include <common/amount.h>
#include <common/bech32.h>
#include <common/bolt11.h>
#include <common/configdir.h>
#include <common/features.h>
#include <common/json_command.h>
#include <common/json_helpers.h>
#include <common/jsonrpc_errors.h>
#include <common/overflows.h>
#include <common/param.h>
#include <common/random_select.h>
#include <common/timeout.h>
#include <common/utils.h>
#include <errno.h>
#include <gossipd/gossipd_wiregen.h>
#include <hsmd/hsmd_wiregen.h>
#include <inttypes.h>
#include <lightningd/channel.h>
#include <lightningd/hsm_control.h>
#include <lightningd/json.h>
#include <lightningd/jsonrpc.h>
#include <lightningd/lightningd.h>
#include <lightningd/log.h>
#include <lightningd/notification.h>
#include <lightningd/options.h>
#include <lightningd/peer_control.h>
#include <lightningd/peer_htlcs.h>
#include <lightningd/plugin_hook.h>
#include <lightningd/routehint.h>
#include <lightningd/subd.h>
#include <sodium/randombytes.h>
#include <wire/wire_sync.h>
static const char *invoice_status_str(const struct invoice_details *inv)
{
if (inv->state == PAID)
return "paid";
if (inv->state == EXPIRED)
return "expired";
return "unpaid";
}
static void json_add_invoice(struct json_stream *response,
const struct invoice_details *inv)
{
json_add_escaped_string(response, "label", inv->label);
json_add_string(response, "bolt11", inv->bolt11);
json_add_sha256(response, "payment_hash", &inv->rhash);
if (inv->msat)
json_add_amount_msat_compat(response, *inv->msat,
"msatoshi", "amount_msat");
json_add_string(response, "status", invoice_status_str(inv));
if (inv->state == PAID) {
json_add_u64(response, "pay_index", inv->pay_index);
json_add_amount_msat_compat(response, inv->received,
"msatoshi_received",
"amount_received_msat");
json_add_u64(response, "paid_at", inv->paid_timestamp);
json_add_preimage(response, "payment_preimage", &inv->r);
}
if (inv->description)
json_add_string(response, "description", inv->description);
json_add_u64(response, "expires_at", inv->expiry_time);
}
static struct command_result *tell_waiter(struct command *cmd,
const struct invoice *inv)
{
struct json_stream *response;
const struct invoice_details *details;
details = wallet_invoice_details(cmd, cmd->ld->wallet, *inv);
if (details->state == PAID) {
response = json_stream_success(cmd);
json_add_invoice(response, details);
return command_success(cmd, response);
} else {
response = json_stream_fail(cmd, INVOICE_EXPIRED_DURING_WAIT,
"invoice expired during wait");
json_add_invoice(response, details);
json_object_end(response);
return command_failed(cmd, response);
}
}
static void tell_waiter_deleted(struct command *cmd)
{
was_pending(command_fail(cmd, LIGHTNINGD,
"Invoice deleted during wait"));
}
static void wait_on_invoice(const struct invoice *invoice, void *cmd)
{
if (invoice)
tell_waiter((struct command *) cmd, invoice);
else
tell_waiter_deleted((struct command *) cmd);
}
static void wait_timed_out(struct command *cmd)
{
was_pending(command_fail(cmd, INVOICE_WAIT_TIMED_OUT,
"Timed out while waiting "
"for invoice to be paid"));
}
/* We derive invoice secret using 1-way function from payment_preimage
* (just a different one from the payment_hash!) */
static void invoice_secret(const struct preimage *payment_preimage,
struct secret *payment_secret)
{
struct preimage modified;
struct sha256 secret;
modified = *payment_preimage;
modified.r[0] ^= 1;
sha256(&secret, modified.r,
ARRAY_SIZE(modified.r) * sizeof(*modified.r));
BUILD_ASSERT(sizeof(secret.u.u8) == sizeof(payment_secret->data));
memcpy(payment_secret->data, secret.u.u8, sizeof(secret.u.u8));
}
struct invoice_payment_hook_payload {
struct lightningd *ld;
/* Set to NULL if it is deleted while waiting for plugin */
struct htlc_set *set;
/* What invoice it's trying to pay. */
const struct json_escape *label;
/* Amount it's offering. */
struct amount_msat msat;
/* Preimage we'll give it if succeeds. */
struct preimage preimage;
/* FIXME: Include raw payload! */
};
static void
invoice_payment_serialize(struct invoice_payment_hook_payload *payload,
struct json_stream *stream)
{
json_object_start(stream, "payment");
json_add_escaped_string(stream, "label", payload->label);
json_add_preimage(stream, "preimage", &payload->preimage);
json_add_string(stream, "msat",
type_to_string(tmpctx, struct amount_msat,
&payload->msat));
json_object_end(stream); /* .payment */
}
/* Set times out or HTLC deleted? Remove set ptr from payload so we
* know to ignore plugin return */
static void invoice_payload_remove_set(struct htlc_set *set,
struct invoice_payment_hook_payload *payload)
{
assert(payload->set == set);
payload->set = NULL;
}
static const u8 *hook_gives_failmsg(const tal_t *ctx,
struct lightningd *ld,
const struct htlc_in *hin,
const char *buffer,
const jsmntok_t *toks)
{
const jsmntok_t *resulttok;
const jsmntok_t *t;
unsigned int val;
/* No plugin registered on hook at all? */
if (!buffer)
return NULL;
resulttok = json_get_member(buffer, toks, "result");
if (resulttok) {
if (json_tok_streq(buffer, resulttok, "continue")) {
return NULL;
} else if (json_tok_streq(buffer, resulttok, "reject")) {
return failmsg_incorrect_or_unknown(ctx, ld, hin);
} else
fatal("Invalid invoice_payment hook result: %.*s",
toks[0].end - toks[0].start, buffer);
}
t = json_get_member(buffer, toks, "failure_message");
if (t) {
const u8 *failmsg = json_tok_bin_from_hex(ctx, buffer, t);
if (!failmsg)
fatal("Invalid invoice_payment_hook failure_message: %.*s",
toks[0].end - toks[1].start, buffer);
return failmsg;
}
if (!deprecated_apis)
return NULL;
t = json_get_member(buffer, toks, "failure_code");
if (!t) {
static bool warned = false;
if (!warned) {
warned = true;
log_unusual(ld->log,
"Plugin did not return object with "
"'result' or 'failure_message' fields. "
"This is now deprecated and you should "
"return {'result': 'continue' } or "
"{'result': 'reject'} or "
"{'failure_message'... instead.");
}
return failmsg_incorrect_or_unknown(ctx, ld, hin);
}
if (!json_to_number(buffer, t, &val))
fatal("Invalid invoice_payment_hook failure_code: %.*s",
toks[0].end - toks[1].start, buffer);
if (val == WIRE_TEMPORARY_NODE_FAILURE)
return towire_temporary_node_failure(ctx);
if (val != WIRE_INCORRECT_OR_UNKNOWN_PAYMENT_DETAILS)
log_broken(hin->key.channel->log,
"invoice_payment hook returned failcode %u,"
" changing to incorrect_or_unknown_payment_details",
val);
return failmsg_incorrect_or_unknown(ctx, ld, hin);
}
static void
invoice_payment_hook_cb(struct invoice_payment_hook_payload *payload STEALS,
const char *buffer,
const jsmntok_t *toks)
{
struct lightningd *ld = payload->ld;
struct invoice invoice;
const u8 *failmsg;
/* We notify here to benefit from the payload and because the hook callback is
* called even if the hook is not registered. */
notify_invoice_payment(ld, payload->msat, payload->preimage, payload->label);
tal_del_destructor2(payload->set, invoice_payload_remove_set, payload);
/* We want to free this, whatever happens. */
tal_steal(tmpctx, payload);
/* If peer dies or something, this can happen. */
if (!payload->set) {
log_debug(ld->log, "invoice '%s' paying htlc_in has gone!",
payload->label->s);
return;
}
/* If invoice gets paid meanwhile (plugin responds out-of-order?) then
* we can also fail */
if (!wallet_invoice_find_by_label(ld->wallet, &invoice, payload->label)) {
htlc_set_fail(payload->set, take(failmsg_incorrect_or_unknown(
NULL, ld, payload->set->htlcs[0])));
return;
}
/* Did we have a hook result? */
failmsg = hook_gives_failmsg(NULL, ld,
payload->set->htlcs[0], buffer, toks);
if (failmsg) {
htlc_set_fail(payload->set, take(failmsg));
return;
}
log_info(ld->log, "Resolved invoice '%s' with amount %s in %zu htlcs",
payload->label->s,
type_to_string(tmpctx, struct amount_msat, &payload->msat),
tal_count(payload->set->htlcs));
wallet_invoice_resolve(ld->wallet, invoice, payload->msat);
htlc_set_fulfill(payload->set, &payload->preimage);
}
REGISTER_SINGLE_PLUGIN_HOOK(invoice_payment,
invoice_payment_hook_cb,
invoice_payment_serialize,
struct invoice_payment_hook_payload *);
const struct invoice_details *
invoice_check_payment(const tal_t *ctx,
struct lightningd *ld,
const struct sha256 *payment_hash,
const struct amount_msat msat,
const struct secret *payment_secret)
{
struct invoice invoice;
const struct invoice_details *details;
/* BOLT #4:
* - if the payment hash has already been paid:
* - MAY treat the payment hash as unknown.
* - MAY succeed in accepting the HTLC.
*...
* - if the payment hash is unknown:
* - MUST fail the HTLC.
* - MUST return an `incorrect_or_unknown_payment_details` error.
*/
if (!wallet_invoice_find_unpaid(ld->wallet, &invoice, payment_hash)) {
log_debug(ld->log, "Unknown paid invoice %s",
type_to_string(tmpctx, struct sha256, payment_hash));
if (wallet_invoice_find_by_rhash(ld->wallet, &invoice, payment_hash)) {
log_debug(ld->log, "ALREADY paid invoice %s",
type_to_string(tmpctx, struct sha256, payment_hash));
}
return NULL;
}
details = wallet_invoice_details(ctx, ld->wallet, invoice);
/* BOLT #4:
* - if the `payment_secret` doesn't match the expected value for that
* `payment_hash`, or the `payment_secret` is required and is not
* present:
* - MUST fail the HTLC.
*/
if (feature_is_set(details->features, COMPULSORY_FEATURE(OPT_VAR_ONION))
&& !payment_secret) {
log_debug(ld->log, "Attept to pay %s without secret",
type_to_string(tmpctx, struct sha256, &details->rhash));
return tal_free(details);
}
if (payment_secret) {
struct secret expected;
invoice_secret(&details->r, &expected);
if (!secret_eq_consttime(payment_secret, &expected)) {
log_debug(ld->log, "Attept to pay %s with wrong secret",
type_to_string(tmpctx, struct sha256,
&details->rhash));
return tal_free(details);
}
}
/* BOLT #4:
*
* An _intermediate hop_ MUST NOT, but the _final node_:
*...
* - if the amount paid is less than the amount expected:
* - MUST fail the HTLC.
*/
if (details->msat != NULL) {
struct amount_msat twice;
if (amount_msat_less(msat, *details->msat)) {
log_debug(ld->log, "Attept to pay %s with amount %s < %s",
type_to_string(tmpctx, struct sha256,
&details->rhash),
type_to_string(tmpctx, struct amount_msat, &msat),
type_to_string(tmpctx, struct amount_msat, details->msat));
return tal_free(details);
}
if (amount_msat_add(&twice, *details->msat, *details->msat)
&& amount_msat_greater(msat, twice)) {
log_debug(ld->log, "Attept to pay %s with amount %s > %s",
type_to_string(tmpctx, struct sha256,
&details->rhash),
type_to_string(tmpctx, struct amount_msat, details->msat),
type_to_string(tmpctx, struct amount_msat, &twice));
/* BOLT #4:
*
* - if the amount paid is more than twice the amount
* expected:
* - SHOULD fail the HTLC.
*/
return tal_free(details);
}
}
return details;
}
void invoice_try_pay(struct lightningd *ld,
struct htlc_set *set,
const struct invoice_details *details)
{
struct invoice_payment_hook_payload *payload;
payload = tal(NULL, struct invoice_payment_hook_payload);
payload->ld = ld;
payload->label = tal_steal(payload, details->label);
payload->msat = set->so_far;
payload->preimage = details->r;
payload->set = set;
tal_add_destructor2(set, invoice_payload_remove_set, payload);
plugin_hook_call_invoice_payment(ld, payload);
}
static bool hsm_sign_b11(const u5 *u5bytes,
const u8 *hrpu8,
secp256k1_ecdsa_recoverable_signature *rsig,
struct lightningd *ld)
{
u8 *msg = towire_hsmd_sign_invoice(NULL, u5bytes, hrpu8);
if (!wire_sync_write(ld->hsm_fd, take(msg)))
fatal("Could not write to HSM: %s", strerror(errno));
msg = wire_sync_read(tmpctx, ld->hsm_fd);
if (!fromwire_hsmd_sign_invoice_reply(msg, rsig))
fatal("HSM gave bad sign_invoice_reply %s",
tal_hex(msg, msg));
return true;
}
static struct command_result *parse_fallback(struct command *cmd,
const char *buffer,
const jsmntok_t *fallback,
const u8 **fallback_script)
{
enum address_parse_result fallback_parse;
fallback_parse
= json_to_address_scriptpubkey(cmd,
chainparams,
buffer, fallback,
fallback_script);
if (fallback_parse == ADDRESS_PARSE_UNRECOGNIZED) {
return command_fail(cmd, LIGHTNINGD,
"Fallback address not valid");
} else if (fallback_parse == ADDRESS_PARSE_WRONG_NETWORK) {
return command_fail(cmd, LIGHTNINGD,
"Fallback address does not match our network %s",
chainparams->network_name);
}
return NULL;
}
/*
* From array of incoming channels [inchan], find suitable ones for
* a payment-to-us of [amount_needed], using criteria:
* 1. Channel's peer is known, in state CHANNELD_NORMAL and is online.
* 2. Channel's peer capacity to pay us is sufficient.
*
* Then use weighted reservoir sampling, which makes probing channel balances
* harder, to choose one channel from the set of suitable channels. It favors
* channels that have less balance on our side as fraction of their capacity.
*/
static struct route_info **select_inchan(const tal_t *ctx,
struct lightningd *ld,
struct amount_msat amount_needed,
const struct routehint_candidate
*candidates)
{
/* BOLT11 struct wants an array of arrays (can provide multiple routes) */
struct route_info **r = NULL;
double total_weight = 0.0;
/* Collect suitable channels and assign each a weight. */
for (size_t i = 0; i < tal_count(candidates); i++) {
struct amount_msat excess, capacity;
struct amount_sat cumulative_reserve;
double excess_frac;
/* Does the peer have sufficient balance to pay us,
* even after having taken into account their reserve? */
if (!amount_msat_sub(&excess, candidates[i].capacity,
amount_needed))
continue;
/* Channel balance as seen by our node:
|<----------------- capacity ----------------->|
. .
. |<------------------ their_msat -------------------->|
. | . |
. |<----- capacity_to_pay_us ----->|<- their_reserve ->|
. | | |
. |<- amount_needed --><- excess ->| |
. | | |
|-------|-------------|--------------------------------|-------------------|
0 ^ ^ ^ funding
our_reserve our_msat */
/* Find capacity and calculate its excess fraction */
if (!amount_sat_add(&cumulative_reserve,
candidates[i].c->our_config.channel_reserve,
candidates[i].c->channel_info.their_config.channel_reserve)
|| !amount_sat_to_msat(&capacity, candidates[i].c->funding)
|| !amount_msat_sub_sat(&capacity, capacity, cumulative_reserve)) {
log_broken(ld->log, "Channel %s capacity overflow!",
type_to_string(tmpctx, struct short_channel_id, candidates[i].c->scid));
continue;
}
/* We don't want a 0 probability if 0 excess; it might be the
* only one! So bump it by 1 msat */
if (!amount_msat_add(&excess, excess, AMOUNT_MSAT(1))) {
log_broken(ld->log, "Channel %s excess overflow!",
type_to_string(tmpctx,
struct short_channel_id,
candidates[i].c->scid));
continue;
}
excess_frac = amount_msat_ratio(excess, capacity);
if (random_select(excess_frac, &total_weight)) {
tal_free(r);
r = tal_arr(ctx, struct route_info *, 1);
r[0] = tal_dup(r, struct route_info, candidates[i].r);
}
}
return r;
}
static int cmp_rr_number(const struct routehint_candidate *a,
const struct routehint_candidate *b,
void *unused)
{
/* They're unique, so can't be equal */
if (a->c->rr_number > b->c->rr_number)
return 1;
assert(a->c->rr_number < b->c->rr_number);
return -1;
}
/** select_inchan_mpp
*
* @brief fallback in case select_inchan cannot find a *single*
* channel capable of accepting the payment as a whole.
* Also the main routehint-selector if we are completely unpublished
* (i.e. all our channels are unpublished), since if we are completely
* unpublished then the payer cannot fall back to just directly routing
* to us.
*/
static struct route_info **select_inchan_mpp(const tal_t *ctx,
struct lightningd *ld,
struct amount_msat amount_needed,
struct routehint_candidate
*candidates,
bool *warning_mpp_capacity)
{
/* The total amount we have gathered for incoming channels. */
struct amount_msat gathered;
/* Routehint array. */
struct route_info **routehints;
gathered = AMOUNT_MSAT(0);
routehints = tal_arr(ctx, struct route_info *, 0);
/* Sort by rr_number, so we get fresh channels. */
asort(candidates, tal_count(candidates), cmp_rr_number, NULL);
for (size_t i = 0; i < tal_count(candidates); i++) {
if (amount_msat_greater_eq(gathered, amount_needed))
break;
/* Add to current routehints set. */
if (!amount_msat_add(&gathered, gathered, candidates[i].capacity)) {
log_broken(ld->log,
"Gathered channel capacity overflow: "
"%s + %s",
type_to_string(tmpctx, struct amount_msat, &gathered),
type_to_string(tmpctx, struct amount_msat,
&candidates[i].capacity));
continue;
}
tal_arr_expand(&routehints,
tal_dup(routehints, struct route_info,
candidates[i].r));
/* Put to the back of the round-robin list */
candidates[i].c->rr_number = ld->rr_counter++;
}
/* Check if we gathered enough. */
*warning_mpp_capacity = amount_msat_less(gathered, amount_needed);
return routehints;
}
/* Encapsulating struct while we wait for gossipd to give us incoming channels */
struct chanhints {
bool expose_all_private;
struct short_channel_id *hints;
};
struct invoice_info {
struct command *cmd;
struct preimage payment_preimage;
struct bolt11 *b11;
struct json_escape *label;
struct chanhints *chanhints;
};
static void gossipd_incoming_channels_reply(struct subd *gossipd,
const u8 *msg,
const int *fs,
struct invoice_info *info)
{
struct json_stream *response;
struct invoice invoice;
char *b11enc;
const struct invoice_details *details;
struct wallet *wallet = info->cmd->ld->wallet;
const struct chanhints *chanhints = info->chanhints;
struct routehint_candidate *candidates;
struct amount_msat offline_amt;
bool warning_mpp = false;
bool warning_mpp_capacity = false;
bool deadends;
bool node_unpublished;
candidates = routehint_candidates(tmpctx, info->cmd->ld, msg,
chanhints ? chanhints->expose_all_private : false,
chanhints ? chanhints->hints : NULL,
&node_unpublished,
&deadends,
&offline_amt);
/* If they told us to use scids and we couldn't, fail. */
if (tal_count(candidates) == 0
&& chanhints && tal_count(chanhints->hints) != 0) {
was_pending(command_fail(info->cmd,
INVOICE_HINTS_GAVE_NO_ROUTES,
"None of those hints were suitable local channels"));
return;
}
if (tal_count(info->b11->routes) == 0) {
struct amount_msat needed;
needed = info->b11->msat ? *info->b11->msat : AMOUNT_MSAT(1);
/* If we are not completely unpublished, try with reservoir
* sampling first.
*
* Why do we not do this if we are completely unpublished?
* Because it is possible that multiple invoices will, by
* chance, select the same channel as routehint.
* This single channel might not be able to accept all the
* incoming payments on all the invoices generated.
* If we were published, that is fine because the payer can
* fall back to just attempting to route directly.
* But if we were unpublished, the only way for the payer to
* reach us would be via the routehints we provide, so we
* should make an effort to avoid overlapping incoming
* channels, which is done by select_inchan_mpp.
*/
if (!node_unpublished)
info->b11->routes = select_inchan(info->b11,
info->cmd->ld,
needed,
candidates);
/* If we are completely unpublished, or if the above reservoir
* sampling fails, select channels by round-robin. */
if (tal_count(info->b11->routes) == 0) {
info->b11->routes = select_inchan_mpp(info->b11,
info->cmd->ld,
needed,
candidates,
&warning_mpp_capacity);
warning_mpp = (tal_count(info->b11->routes) > 1);
}
}
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)) {
was_pending(command_fail(info->cmd,
INVOICE_PREIMAGE_ALREADY_EXISTS,
"preimage already used"));
return;
}
if (!wallet_invoice_create(wallet,
&invoice,
info->b11->msat,
info->label,
info->b11->expiry,
b11enc,
info->b11->description,
info->b11->features,
&info->payment_preimage,
&info->b11->payment_hash)) {
was_pending(command_fail(info->cmd, INVOICE_LABEL_ALREADY_EXISTS,
"Duplicate label '%s'",
info->label->s));
return;
}
/* Get details */
details = wallet_invoice_details(info, wallet, invoice);
response = json_stream_success(info->cmd);
json_add_sha256(response, "payment_hash", &details->rhash);
json_add_u64(response, "expires_at", details->expiry_time);
json_add_string(response, "bolt11", details->bolt11);
notify_invoice_creation(info->cmd->ld, info->b11->msat,
info->payment_preimage, info->label);
/* 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 %s%s",
info->b11->msat
? type_to_string(tmpctx, struct amount_msat,
info->b11->msat)
: "0",
amount_msat_greater(offline_amt, AMOUNT_MSAT(0))
? " (among currently connected peers)" : "");
if (amount_msat_greater(offline_amt, AMOUNT_MSAT(0))) {
json_add_string(response, "warning_offline",
"No channel with a peer that is currently connected"
" has sufficient incoming capacity");
} else if (deadends) {
json_add_string(response, "warning_deadends",
"No channel with a peer that is not a dead end");
} else if (tal_count(candidates) == 0) {
json_add_string(response, "warning_capacity",
"No channels");
} else {
json_add_string(response, "warning_capacity",
"No channel with a peer that has sufficient incoming capacity");
}
}
if (warning_mpp)
json_add_string(response, "warning_mpp",
"The invoice might only be payable by MPP-capable payers.");
if (warning_mpp_capacity)
json_add_string(response, "warning_mpp_capacity",
"The total incoming capacity is still insufficient even if the payer had MPP capability.");
was_pending(command_success(info->cmd, response));
}
#if DEVELOPER
/* Since this is a dev-only option, we will crash if dev-routes is not
* an array-of-arrays-of-correct-items. */
static struct route_info *unpack_route(const tal_t *ctx,
const char *buffer,
const jsmntok_t *routetok)
{
const jsmntok_t *t;
size_t i;
struct route_info *route = tal_arr(ctx, struct route_info, routetok->size);
json_for_each_arr(i, t, routetok) {
const jsmntok_t *pubkey, *fee_base, *fee_prop, *scid, *cltv;
struct route_info *r = &route[i];
u32 cltv_u32;
pubkey = json_get_member(buffer, t, "id");
scid = json_get_member(buffer, t, "short_channel_id");
fee_base = json_get_member(buffer, t, "fee_base_msat");
fee_prop = json_get_member(buffer, t,
"fee_proportional_millionths");
cltv = json_get_member(buffer, t, "cltv_expiry_delta");
if (!json_to_node_id(buffer, pubkey, &r->pubkey)
|| !json_to_short_channel_id(buffer, scid,
&r->short_channel_id)
|| !json_to_number(buffer, fee_base, &r->fee_base_msat)
|| !json_to_number(buffer, fee_prop,
&r->fee_proportional_millionths)
|| !json_to_number(buffer, cltv, &cltv_u32))
abort();
/* We don't have a json_to_u16 */
r->cltv_expiry_delta = cltv_u32;
}
return route;
}
static struct route_info **unpack_routes(const tal_t *ctx,
const char *buffer,
const jsmntok_t *routestok)
{
struct route_info **routes;
const jsmntok_t *t;
size_t i;
if (!routestok)
return NULL;
routes = tal_arr(ctx, struct route_info *, routestok->size);
json_for_each_arr(i, t, routestok)
routes[i] = unpack_route(routes, buffer, t);
return routes;
}
#endif /* DEVELOPER */
static struct command_result *param_positive_msat_or_any(struct command *cmd,
const char *name,
const char *buffer,
const jsmntok_t *tok,
struct amount_msat **msat)
{
if (json_tok_streq(buffer, tok, "any")) {
*msat = NULL;
return NULL;
}
*msat = tal(cmd, struct amount_msat);
if (parse_amount_msat(*msat, buffer + tok->start, tok->end - tok->start)
&& !amount_msat_eq(**msat, AMOUNT_MSAT(0)))
return NULL;
return command_fail_badparam(cmd, name, buffer, tok,
"should be positive msat or 'any'");
}
/* Parse time with optional suffix, return seconds */
static struct command_result *param_time(struct command *cmd, const char *name,
const char *buffer,
const jsmntok_t *tok,
uint64_t **secs)
{
/* We need to manipulate this, so make copy */
jsmntok_t timetok = *tok;
u64 mul;
char s;
struct {
char suffix;
u64 mul;
} suffixes[] = {
{ 's', 1 },
{ 'm', 60 },
{ 'h', 60*60 },
{ 'd', 24*60*60 },
{ 'w', 7*24*60*60 } };
mul = 1;
if (timetok.end == timetok.start)
s = '\0';
else
s = buffer[timetok.end - 1];
for (size_t i = 0; i < ARRAY_SIZE(suffixes); i++) {
if (s == suffixes[i].suffix) {
mul = suffixes[i].mul;
timetok.end--;
break;
}
}
*secs = tal(cmd, uint64_t);
if (json_to_u64(buffer, &timetok, *secs)) {
if (mul_overflows_u64(**secs, mul)) {
return command_fail_badparam(cmd, name, buffer, tok,
"value too large");
}
**secs *= mul;
return NULL;
}
return command_fail_badparam(cmd, name, buffer, tok,
"should be a number with optional {s,m,h,d,w} suffix");
}
static struct command_result *param_chanhints(struct command *cmd,
const char *name,
const char *buffer,
const jsmntok_t *tok,
struct chanhints **chanhints)
{
bool boolhint;
*chanhints = tal(cmd, struct chanhints);
/* Could be simply "true" or "false" */
if (json_to_bool(buffer, tok, &boolhint)) {
(*chanhints)->expose_all_private = boolhint;
(*chanhints)->hints
= tal_arr(*chanhints, struct short_channel_id, 0);
return NULL;
}
(*chanhints)->expose_all_private = false;
/* Could be a single short_channel_id or an array */
if (tok->type == JSMN_ARRAY) {
size_t i;
const jsmntok_t *t;
(*chanhints)->hints
= tal_arr(*chanhints, struct short_channel_id,
tok->size);
json_for_each_arr(i, t, tok) {
if (!json_to_short_channel_id(buffer, t,
&(*chanhints)->hints[i])) {
return command_fail_badparam(cmd, name, buffer, t,
"should be a short channel id");
}
}
return NULL;
}
/* Otherwise should be a short_channel_id */
return param_short_channel_id(cmd, name, buffer, tok,
&(*chanhints)->hints);
}
static struct command_result *json_invoice(struct command *cmd,
const char *buffer,
const jsmntok_t *obj UNNEEDED,
const jsmntok_t *params)
{
const jsmntok_t *fallbacks;
const jsmntok_t *preimagetok;
struct amount_msat *msatoshi_val;
struct invoice_info *info;
const char *desc_val;
const u8 **fallback_scripts = NULL;
u64 *expiry;
struct sha256 rhash;
struct secret payment_secret;
#if DEVELOPER
const jsmntok_t *routes;
#endif
info = tal(cmd, struct invoice_info);
info->cmd = cmd;
if (!param(cmd, buffer, params,
p_req("msatoshi", param_positive_msat_or_any, &msatoshi_val),
p_req("label", param_label, &info->label),
p_req("description", param_escaped_string, &desc_val),
p_opt_def("expiry", param_time, &expiry, 3600*24*7),
p_opt("fallbacks", param_array, &fallbacks),
p_opt("preimage", param_tok, &preimagetok),
p_opt("exposeprivatechannels", param_chanhints,
&info->chanhints),
#if DEVELOPER
p_opt("dev-routes", param_array, &routes),
#endif
NULL))
return command_param_failed();
if (strlen(info->label->s) > INVOICE_MAX_LABEL_LEN) {
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"Label '%s' over %u bytes", info->label->s,
INVOICE_MAX_LABEL_LEN);
}
if (strlen(desc_val) >= BOLT11_FIELD_BYTE_LIMIT) {
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));
}
if (msatoshi_val
&& amount_msat_greater(*msatoshi_val, chainparams->max_payment)) {
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"msatoshi cannot exceed %s",
type_to_string(tmpctx, struct amount_msat,
&chainparams->max_payment));
}
if (fallbacks) {
size_t i;
const jsmntok_t *t;
fallback_scripts = tal_arr(cmd, const u8 *, fallbacks->size);
json_for_each_arr(i, t, fallbacks) {
struct command_result *r;
r = parse_fallback(cmd, buffer, t, &fallback_scripts[i]);
if (r)
return r;
}
}
if (preimagetok) {
/* Get secret preimage from user. */
if (!hex_decode(buffer + preimagetok->start,
preimagetok->end - preimagetok->start,
&info->payment_preimage,
sizeof(info->payment_preimage))) {
return command_fail_badparam(cmd, "preimage",
buffer, preimagetok,
"should be 64 hex digits");
}
} else
/* Generate random secret preimage. */
randombytes_buf(&info->payment_preimage,
sizeof(info->payment_preimage));
/* Generate preimage hash. */
sha256(&rhash, &info->payment_preimage, sizeof(info->payment_preimage));
/* Generate payment secret. */
invoice_secret(&info->payment_preimage, &payment_secret);
info->b11 = new_bolt11(info, msatoshi_val);
info->b11->chain = chainparams;
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;
info->b11->payment_secret = tal_dup(info->b11, struct secret,
&payment_secret);
info->b11->features = tal_dup_talarr(info->b11, u8,
cmd->ld->our_features
->bits[BOLT11_FEATURE]);
#if DEVELOPER
info->b11->routes = unpack_routes(info->b11, buffer, routes);
#else
info->b11->routes = NULL;
#endif
if (fallback_scripts)
info->b11->fallbacks = tal_steal(info->b11, fallback_scripts);
subd_req(cmd, cmd->ld->gossip,
take(towire_gossipd_get_incoming_channels(NULL)),
-1, 0, gossipd_incoming_channels_reply, info);
return command_still_pending(cmd);
}
static const struct json_command invoice_command = {
"invoice",
"payment",
json_invoice,
"Create an invoice for {msatoshi} with {label} "
"and {description} with optional {expiry} seconds "
"(default 1 week), optional {fallbacks} address list"
"(default empty list) and optional {preimage} "
"(default autogenerated)"};
AUTODATA(json_command, &invoice_command);
static void json_add_invoices(struct json_stream *response,
struct wallet *wallet,
const struct json_escape *label)
{
struct invoice_iterator it;
const struct invoice_details *details;
/* 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)) {
details = wallet_invoice_details(response, wallet, invoice);
json_object_start(response, NULL);
json_add_invoice(response, details);
json_object_end(response);
}
return;
}
memset(&it, 0, sizeof(it));
while (wallet_invoice_iterate(wallet, &it)) {
details = wallet_invoice_iterator_deref(response, wallet, &it);
json_object_start(response, NULL);
json_add_invoice(response, details);
json_object_end(response);
}
}
static struct command_result *json_listinvoices(struct command *cmd,
const char *buffer,
const jsmntok_t *obj UNNEEDED,
const jsmntok_t *params)
{
struct json_escape *label;
struct json_stream *response;
struct wallet *wallet = cmd->ld->wallet;
if (!param(cmd, buffer, params,
p_opt("label", param_label, &label),
NULL))
return command_param_failed();
response = json_stream_success(cmd);
json_array_start(response, "invoices");
json_add_invoices(response, wallet, label);
json_array_end(response);
return command_success(cmd, response);
}
static const struct json_command listinvoices_command = {
"listinvoices",
"payment",
json_listinvoices,
"Show invoice {label} (or all, if no {label})"
};
AUTODATA(json_command, &listinvoices_command);
static struct command_result *json_delinvoice(struct command *cmd,
const char *buffer,
const jsmntok_t *obj UNNEEDED,
const jsmntok_t *params)
{
struct invoice i;
const struct invoice_details *details;
struct json_stream *response;
const char *status, *actual_status;
struct json_escape *label;
struct wallet *wallet = cmd->ld->wallet;
if (!param(cmd, buffer, params,
p_req("label", param_label, &label),
p_req("status", param_string, &status),
NULL))
return command_param_failed();
if (!wallet_invoice_find_by_label(wallet, &i, label)) {
return command_fail(cmd, INVOICE_NOT_FOUND, "Unknown invoice");
}
details = wallet_invoice_details(cmd, cmd->ld->wallet, i);
/* This is time-sensitive, so only call once; otherwise error msg
* might not make sense if it changed! */
actual_status = invoice_status_str(details);
if (!streq(actual_status, status)) {
struct json_stream *js;
js = json_stream_fail(cmd, INVOICE_STATUS_UNEXPECTED,
tal_fmt(tmpctx,
"Invoice status is %s not %s",
actual_status, status));
json_add_string(js, "current_status", actual_status);
json_add_string(js, "expected_status", status);
json_object_end(js);
return command_failed(cmd, js);
}
if (!wallet_invoice_delete(wallet, i)) {
log_broken(cmd->ld->log,
"Error attempting to remove invoice %"PRIu64,
i.id);
/* FIXME: allocate a generic DATABASE_ERROR code. */
return command_fail(cmd, LIGHTNINGD, "Database error");
}
response = json_stream_success(cmd);
json_add_invoice(response, details);
return command_success(cmd, response);
}
static const struct json_command delinvoice_command = {
"delinvoice",
"payment",
json_delinvoice,
"Delete unpaid invoice {label} with {status}",
};
AUTODATA(json_command, &delinvoice_command);
static struct command_result *json_delexpiredinvoice(struct command *cmd,
const char *buffer,
const jsmntok_t *obj UNNEEDED,
const jsmntok_t *params)
{
u64 *maxexpirytime;
if (!param(cmd, buffer, params,
p_opt_def("maxexpirytime", param_u64, &maxexpirytime,
time_now().ts.tv_sec),
NULL))
return command_param_failed();
wallet_invoice_delete_expired(cmd->ld->wallet, *maxexpirytime);
return command_success(cmd, json_stream_success(cmd));
}
static const struct json_command delexpiredinvoice_command = {
"delexpiredinvoice",
"payment",
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);
static struct command_result *json_waitanyinvoice(struct command *cmd,
const char *buffer,
const jsmntok_t *obj UNNEEDED,
const jsmntok_t *params)
{
u64 *pay_index;
u64 *timeout;
struct wallet *wallet = cmd->ld->wallet;
if (!param(cmd, buffer, params,
p_opt_def("lastpay_index", param_u64, &pay_index, 0),
p_opt("timeout", &param_u64, &timeout),
NULL))
return command_param_failed();
/*~ We allocate the timeout and the wallet-waitanyinvoice
* in the cmd context, so whichever one manages to complete
* the command first (and destroy the cmd context)
* auto-cancels the other, is not tal amazing?
*/
if (timeout)
(void) new_reltimer(cmd->ld->timers, cmd,
time_from_sec(*timeout),
&wait_timed_out, cmd);
/* Set command as pending. We do not know if
* wallet_invoice_waitany will return immediately
* or not, so indicating pending is safest. */
fixme_ignore(command_still_pending(cmd));
/* Find next paid invoice. */
wallet_invoice_waitany(cmd, wallet, *pay_index,
&wait_on_invoice, (void*) cmd);
return command_its_complicated("wallet_invoice_waitany might complete"
" immediately, but we also call it as a"
" callback so plumbing through the return"
" is non-trivial.");
}
static const struct json_command waitanyinvoice_command = {
"waitanyinvoice",
"payment",
json_waitanyinvoice,
"Wait for the next invoice to be paid, after {lastpay_index} (if supplied). "
"If {timeout} seconds is reached while waiting, fail with an error."
};
AUTODATA(json_command, &waitanyinvoice_command);
/* 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.
*/
static struct command_result *json_waitinvoice(struct command *cmd,
const char *buffer,
const jsmntok_t *obj UNNEEDED,
const jsmntok_t *params)
{
struct invoice i;
const struct invoice_details *details;
struct wallet *wallet = cmd->ld->wallet;
struct json_escape *label;
if (!param(cmd, buffer, params,
p_req("label", param_label, &label),
NULL))
return command_param_failed();
if (!wallet_invoice_find_by_label(wallet, &i, label)) {
return command_fail(cmd, LIGHTNINGD, "Label not found");
}
details = wallet_invoice_details(cmd, cmd->ld->wallet, i);
/* If paid or expired return immediately */
if (details->state == PAID || details->state == EXPIRED) {
return tell_waiter(cmd, &i);
} else {
/* There is an unpaid one matching, let's wait... */
fixme_ignore(command_still_pending(cmd));
wallet_invoice_waitone(cmd, wallet, i,
&wait_on_invoice, (void *) cmd);
return command_its_complicated("wallet_invoice_waitone might"
" complete immediately");
}
}
static const struct json_command waitinvoice_command = {
"waitinvoice",
"payment",
json_waitinvoice,
"Wait for an incoming payment matching the invoice with {label}, or if the invoice expires"
};
AUTODATA(json_command, &waitinvoice_command);
static void json_add_fallback(struct json_stream *response,
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, &pkh));
} else if (is_p2sh(fallback, &sh)) {
json_add_string(response, "type", "P2SH");
json_add_string(response, "addr",
p2sh_to_base58(tmpctx, chain, &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);
}
json_add_hex_talarr(response, "hex", fallback);
json_object_end(response);
}
static struct command_result *json_decodepay(struct command *cmd,
const char *buffer,
const jsmntok_t *obj UNNEEDED,
const jsmntok_t *params)
{
struct bolt11 *b11;
struct json_stream *response;
const char *str, *desc;
char *fail;
if (!param(cmd, buffer, params,
p_req("bolt11", param_string, &str),
p_opt("description", param_string, &desc),
NULL))
return command_param_failed();
b11 = bolt11_decode(cmd, str, cmd->ld->our_features, desc, &fail);
if (!b11) {
return command_fail(cmd, LIGHTNINGD, "Invalid bolt11: %s", fail);
}
response = json_stream_success(cmd);
json_add_string(response, "currency", b11->chain->bip173_name);
json_add_u64(response, "created_at", b11->timestamp);
json_add_u64(response, "expiry", b11->expiry);
json_add_node_id(response, "payee", &b11->receiver_id);
if (b11->msat)
json_add_amount_msat_compat(response, *b11->msat,
"msatoshi", "amount_msat");
if (b11->description) {
struct json_escape *esc = json_escape(NULL, b11->description);
json_add_escaped_string(response, "description", take(esc));
}
if (b11->description_hash)
json_add_sha256(response, "description_hash",
b11->description_hash);
json_add_num(response, "min_final_cltv_expiry",
b11->min_final_cltv_expiry);
if (b11->payment_secret)
json_add_secret(response, "payment_secret",
b11->payment_secret);
if (b11->features)
json_add_hex_talarr(response, "features", b11->features);
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);
}
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_node_id(response, "pubkey",
&b11->routes[i][n].pubkey);
json_add_short_channel_id(response,
"short_channel_id",
&b11->routes[i][n]
.short_channel_id);
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);
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) {
char *data = tal_arr(cmd, char, tal_count(extra->data)+1);
size_t i;
for (i = 0; i < tal_count(extra->data); i++)
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_sha256(response, "payment_hash", &b11->payment_hash);
json_add_string(response, "signature",
type_to_string(cmd, secp256k1_ecdsa_signature,
&b11->sig));
return command_success(cmd, response);
}
static const struct json_command decodepay_command = {
"decodepay",
"payment",
json_decodepay,
"Decode {bolt11}, using {description} if necessary"
};
AUTODATA(json_command, &decodepay_command);