Browse Source

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>
fee-tracking2
Rusty Russell 6 years ago
committed by Christian Decker
parent
commit
c8c3294a23
  1. 1
      CHANGELOG.md
  2. 11
      lightningd/channel.c
  3. 3
      lightningd/channel.h
  4. 260
      lightningd/invoice.c
  5. 3
      lightningd/jsonrpc.h
  6. 1
      lightningd/test/Makefile
  7. 632
      lightningd/test/run-invoice-select-inchan.c
  8. 48
      tests/test_invoices.py
  9. 4
      wallet/wallet.h

1
CHANGELOG.md

@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- JSON API: `listchannels` has two new fields: `message_flags` and `channel_flags`. This replaces `flags`. - JSON API: `listchannels` has two new fields: `message_flags` and `channel_flags`. This replaces `flags`.
- Bitcoind: more parallelism in requests, for very slow nodes. - Bitcoind: more parallelism in requests, for very slow nodes.
- Testing: fixed logging, cleaner interception of bitcoind, minor fixes. - Testing: fixed logging, cleaner interception of bitcoind, minor fixes.
- JSON API: `invoice` now adds route hint to invoices for incoming capacity (RouteBoost), and warns if insufficient capacity.
### Changed ### Changed

11
lightningd/channel.c

@ -270,6 +270,17 @@ struct channel *peer_active_channel(struct peer *peer)
return NULL; return NULL;
} }
struct channel *peer_normal_channel(struct peer *peer)
{
struct channel *channel;
list_for_each(&peer->channels, channel, list) {
if (channel->state == CHANNELD_NORMAL)
return channel;
}
return NULL;
}
struct channel *active_channel_by_id(struct lightningd *ld, struct channel *active_channel_by_id(struct lightningd *ld,
const struct pubkey *id, const struct pubkey *id,
struct uncommitted_channel **uc) struct uncommitted_channel **uc)

3
lightningd/channel.h

@ -178,6 +178,9 @@ void channel_set_state(struct channel *channel,
/* Find a channel which is not onchain, if any */ /* Find a channel which is not onchain, if any */
struct channel *peer_active_channel(struct peer *peer); struct channel *peer_active_channel(struct peer *peer);
/* Find a channel which is in state CHANNELD_NORMAL, if any */
struct channel *peer_normal_channel(struct peer *peer);
/* Get active channel for peer, optionally any uncommitted_channel. */ /* Get active channel for peer, optionally any uncommitted_channel. */
struct channel *active_channel_by_id(struct lightningd *ld, struct channel *active_channel_by_id(struct lightningd *ld,
const struct pubkey *id, const struct pubkey *id,

260
lightningd/invoice.c

@ -11,15 +11,20 @@
#include <common/bech32.h> #include <common/bech32.h>
#include <common/bolt11.h> #include <common/bolt11.h>
#include <common/json_escaped.h> #include <common/json_escaped.h>
#include <common/pseudorand.h>
#include <common/utils.h> #include <common/utils.h>
#include <errno.h> #include <errno.h>
#include <gossipd/gen_gossip_wire.h>
#include <hsmd/gen_hsm_wire.h> #include <hsmd/gen_hsm_wire.h>
#include <inttypes.h> #include <inttypes.h>
#include <lightningd/channel.h>
#include <lightningd/hsm_control.h> #include <lightningd/hsm_control.h>
#include <lightningd/jsonrpc_errors.h> #include <lightningd/jsonrpc_errors.h>
#include <lightningd/log.h> #include <lightningd/log.h>
#include <lightningd/options.h> #include <lightningd/options.h>
#include <lightningd/param.h> #include <lightningd/param.h>
#include <lightningd/peer_control.h>
#include <lightningd/subd.h>
#include <sodium/randombytes.h> #include <sodium/randombytes.h>
#include <wire/wire_sync.h> #include <wire/wire_sync.h>
@ -126,27 +131,179 @@ static bool parse_fallback(struct command *cmd,
return true; return true;
} }
static void json_invoice(struct command *cmd, /* BOLT11 struct wants an array of arrays (can provide multiple routes) */
const char *buffer, const jsmntok_t *params) 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)
{ {
struct json_result *response = new_json_result(info->cmd);
struct route_info *inchans;
bool any_offline;
struct invoice invoice; struct invoice invoice;
char *b11enc;
const struct invoice_details *details; const struct invoice_details *details;
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);
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);
}
static void json_invoice(struct command *cmd,
const char *buffer, const jsmntok_t *params)
{
const jsmntok_t *fallbacks; const jsmntok_t *fallbacks;
const jsmntok_t *preimagetok; const jsmntok_t *preimagetok;
u64 *msatoshi_val; u64 *msatoshi_val;
struct json_escaped *label_val; struct invoice_info *info;
const char *desc_val; const char *desc_val;
struct json_result *response = new_json_result(cmd);
struct wallet *wallet = cmd->ld->wallet;
struct bolt11 *b11;
char *b11enc;
const u8 **fallback_scripts = NULL; const u8 **fallback_scripts = NULL;
u64 *expiry; u64 *expiry;
bool result; struct sha256 rhash;
info = tal(cmd, struct invoice_info);
info->cmd = cmd;
if (!param(cmd, buffer, params, if (!param(cmd, buffer, params,
p_req("msatoshi", json_tok_msat, &msatoshi_val), p_req("msatoshi", json_tok_msat, &msatoshi_val),
p_req("label", json_tok_label, &label_val), p_req("label", json_tok_label, &info->label),
p_req("description", json_tok_escaped_string, &desc_val), p_req("description", json_tok_escaped_string, &desc_val),
p_opt_def("expiry", json_tok_u64, &expiry, 3600), p_opt_def("expiry", json_tok_u64, &expiry, 3600),
p_opt("fallbacks", json_tok_array, &fallbacks), p_opt("fallbacks", json_tok_array, &fallbacks),
@ -154,14 +311,9 @@ static void json_invoice(struct command *cmd,
NULL)) NULL))
return; return;
if (wallet_invoice_find_by_label(wallet, &invoice, label_val)) { if (strlen(info->label->s) > INVOICE_MAX_LABEL_LEN) {
command_fail(cmd, INVOICE_LABEL_ALREADY_EXISTS,
"Duplicate label '%s'", label_val->s);
return;
}
if (strlen(label_val->s) > INVOICE_MAX_LABEL_LEN) {
command_fail(cmd, JSONRPC2_INVALID_PARAMS, command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"Label '%s' over %u bytes", label_val->s, "Label '%s' over %u bytes", info->label->s,
INVOICE_MAX_LABEL_LEN); INVOICE_MAX_LABEL_LEN);
return; return;
} }
@ -187,78 +339,42 @@ static void json_invoice(struct command *cmd,
} }
} }
struct preimage r;
struct sha256 rhash;
if (preimagetok) { if (preimagetok) {
/* Get secret preimage from user. */ /* Get secret preimage from user. */
if (!hex_decode(buffer + preimagetok->start, if (!hex_decode(buffer + preimagetok->start,
preimagetok->end - preimagetok->start, preimagetok->end - preimagetok->start,
r.r, sizeof(r.r))) { &info->payment_preimage,
sizeof(info->payment_preimage))) {
command_fail(cmd, JSONRPC2_INVALID_PARAMS, command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"preimage must be 64 hex digits"); "preimage must be 64 hex digits");
return; return;
} }
} else } else
/* Generate random secret preimage. */ /* Generate random secret preimage. */
randombytes_buf(r.r, sizeof(r.r)); randombytes_buf(&info->payment_preimage,
sizeof(info->payment_preimage));
/* Generate preimage hash. */ /* Generate preimage hash. */
sha256(&rhash, r.r, sizeof(r.r)); sha256(&rhash, &info->payment_preimage, sizeof(info->payment_preimage));
/* Check duplicate preimage if explicitly specified.
* We do not check when it is randomly generated, since
* the probability of that matching is very low.
*/
if (preimagetok &&
wallet_invoice_find_by_rhash(cmd->ld->wallet, &invoice, &rhash)) {
command_fail(cmd, INVOICE_PREIMAGE_ALREADY_EXISTS,
"preimage already used");
return;
}
/* Construct bolt11 string. */ /* Construct bolt11 string. */
b11 = new_bolt11(cmd, msatoshi_val); info->b11 = new_bolt11(info, msatoshi_val);
b11->chain = get_chainparams(cmd->ld); info->b11->chain = get_chainparams(cmd->ld);
b11->timestamp = time_now().ts.tv_sec; info->b11->timestamp = time_now().ts.tv_sec;
b11->payment_hash = rhash; info->b11->payment_hash = rhash;
b11->receiver_id = cmd->ld->id; info->b11->receiver_id = cmd->ld->id;
b11->min_final_cltv_expiry = cmd->ld->config.cltv_final; info->b11->min_final_cltv_expiry = cmd->ld->config.cltv_final;
b11->expiry = *expiry; info->b11->expiry = *expiry;
b11->description = tal_steal(b11, desc_val); info->b11->description = tal_steal(info->b11, desc_val);
b11->description_hash = NULL; info->b11->description_hash = NULL;
if (fallback_scripts)
b11->fallbacks = tal_steal(b11, fallback_scripts);
/* FIXME: add private routes if necessary! */ if (fallback_scripts)
b11enc = bolt11_encode(cmd, b11, false, hsm_sign_b11, cmd->ld); info->b11->fallbacks = tal_steal(info->b11, fallback_scripts);
result = wallet_invoice_create(cmd->ld->wallet,
&invoice,
take(msatoshi_val),
take(label_val),
*expiry,
b11enc,
b11->description,
&r,
&rhash);
if (!result) {
command_fail(cmd, LIGHTNINGD,
"Failed to create invoice on database");
return;
}
/* Get details */
details = wallet_invoice_details(cmd, cmd->ld->wallet, invoice);
json_object_start(response, NULL); subd_req(cmd, cmd->ld->gossip,
json_add_hex(response, "payment_hash", details->rhash.u.u8, take(towire_gossip_get_incoming_channels(NULL)),
sizeof(details->rhash)); -1, 0, gossipd_incoming_channels_reply, info);
json_add_u64(response, "expires_at", details->expiry_time);
json_add_string(response, "bolt11", details->bolt11);
json_object_end(response);
command_success(cmd, response); command_still_pending(cmd);
} }
static const struct json_command invoice_command = { static const struct json_command invoice_command = {

3
lightningd/jsonrpc.h

@ -102,8 +102,7 @@ enum address_parse_result {
/* Return result of address parsing and fills in *scriptpubkey /* Return result of address parsing and fills in *scriptpubkey
* allocated off ctx if ADDRESS_PARSE_SUCCESS * allocated off ctx if ADDRESS_PARSE_SUCCESS
*/ */
enum address_parse_result enum address_parse_result json_tok_address_scriptpubkey(const tal_t *ctx,
json_tok_address_scriptpubkey(const tal_t *ctx,
const struct chainparams *chainparams, const struct chainparams *chainparams,
const char *buffer, const char *buffer,
const jsmntok_t *tok, const u8 **scriptpubkey); const jsmntok_t *tok, const u8 **scriptpubkey);

1
lightningd/test/Makefile

@ -8,6 +8,7 @@ ALL_TEST_PROGRAMS += $(LIGHTNINGD_TEST_PROGRAMS)
ALL_OBJS += $(LIGHTNINGD_TEST_OBJS) ALL_OBJS += $(LIGHTNINGD_TEST_OBJS)
LIGHTNINGD_TEST_COMMON_OBJS := \ LIGHTNINGD_TEST_COMMON_OBJS := \
common/bech32.o \
common/daemon_conn.o \ common/daemon_conn.o \
common/htlc_state.o \ common/htlc_state.o \
common/pseudorand.o \ common/pseudorand.o \

632
lightningd/test/run-invoice-select-inchan.c

@ -0,0 +1,632 @@
#include "../channel.c"
#include "../invoice.c"
#include "../peer_control.c"
#include <ccan/alignof/alignof.h>
bool deprecated_apis = false;
/* AUTOGENERATED MOCKS START */
/* Generated stub for bip32_pubkey */
bool bip32_pubkey(const struct ext_key *bip32_base UNNEEDED,
struct pubkey *pubkey UNNEEDED, u32 index UNNEEDED)
{ fprintf(stderr, "bip32_pubkey called!\n"); abort(); }
/* Generated stub for bitcoind_gettxout */
void bitcoind_gettxout(struct bitcoind *bitcoind UNNEEDED,
const struct bitcoin_txid *txid UNNEEDED, const u32 outnum UNNEEDED,
void (*cb)(struct bitcoind *bitcoind UNNEEDED,
const struct bitcoin_tx_output *txout UNNEEDED,
void *arg) UNNEEDED,
void *arg UNNEEDED)
{ fprintf(stderr, "bitcoind_gettxout called!\n"); abort(); }
/* Generated stub for bolt11_decode */
struct bolt11 *bolt11_decode(const tal_t *ctx UNNEEDED, const char *str UNNEEDED,
const char *description UNNEEDED, char **fail UNNEEDED)
{ fprintf(stderr, "bolt11_decode called!\n"); abort(); }
/* Generated stub for bolt11_encode_ */
char *bolt11_encode_(const tal_t *ctx UNNEEDED,
const struct bolt11 *b11 UNNEEDED, bool n_field UNNEEDED,
bool (*sign)(const u5 *u5bytes UNNEEDED,
const u8 *hrpu8 UNNEEDED,
secp256k1_ecdsa_recoverable_signature *rsig UNNEEDED,
void *arg) UNNEEDED,
void *arg UNNEEDED)
{ fprintf(stderr, "bolt11_encode_ called!\n"); abort(); }
/* Generated stub for broadcast_tx */
void broadcast_tx(struct chain_topology *topo UNNEEDED,
struct channel *channel UNNEEDED, const struct bitcoin_tx *tx UNNEEDED,
void (*failed)(struct channel *channel UNNEEDED,
int exitstatus UNNEEDED,
const char *err))
{ fprintf(stderr, "broadcast_tx called!\n"); abort(); }
/* Generated stub for channel_tell_funding_locked */
bool channel_tell_funding_locked(struct lightningd *ld UNNEEDED,
struct channel *channel UNNEEDED,
const struct bitcoin_txid *txid UNNEEDED,
u32 depth UNNEEDED)
{ fprintf(stderr, "channel_tell_funding_locked called!\n"); abort(); }
/* Generated stub for command_fail */
void command_fail(struct command *cmd UNNEEDED, int code UNNEEDED,
const char *fmt UNNEEDED, ...)
{ fprintf(stderr, "command_fail called!\n"); abort(); }
/* Generated stub for command_fail_detailed */
void command_fail_detailed(struct command *cmd UNNEEDED,
int code UNNEEDED,
const struct json_result *data UNNEEDED,
const char *fmt UNNEEDED, ...)
{ fprintf(stderr, "command_fail_detailed called!\n"); abort(); }
/* Generated stub for command_still_pending */
void command_still_pending(struct command *cmd UNNEEDED)
{ fprintf(stderr, "command_still_pending called!\n"); abort(); }
/* Generated stub for command_success */
void command_success(struct command *cmd UNNEEDED, struct json_result *response UNNEEDED)
{ fprintf(stderr, "command_success called!\n"); abort(); }
/* Generated stub for connect_succeeded */
void connect_succeeded(struct lightningd *ld UNNEEDED, const struct pubkey *id UNNEEDED)
{ fprintf(stderr, "connect_succeeded called!\n"); abort(); }
/* Generated stub for delay_then_reconnect */
void delay_then_reconnect(struct channel *channel UNNEEDED, u32 seconds_delay UNNEEDED,
const struct wireaddr_internal *addrhint TAKES UNNEEDED)
{ fprintf(stderr, "delay_then_reconnect called!\n"); abort(); }
/* Generated stub for fatal */
void fatal(const char *fmt UNNEEDED, ...)
{ fprintf(stderr, "fatal called!\n"); abort(); }
/* Generated stub for fromwire_connect_peer_connected */
bool fromwire_connect_peer_connected(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct pubkey *id UNNEEDED, struct wireaddr_internal *addr UNNEEDED, struct crypto_state *crypto_state UNNEEDED, u8 **gfeatures UNNEEDED, u8 **lfeatures UNNEEDED)
{ fprintf(stderr, "fromwire_connect_peer_connected called!\n"); abort(); }
/* Generated stub for fromwire_gossip_get_incoming_channels_reply */
bool fromwire_gossip_get_incoming_channels_reply(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct route_info **route_info UNNEEDED)
{ fprintf(stderr, "fromwire_gossip_get_incoming_channels_reply called!\n"); abort(); }
/* Generated stub for fromwire_hsm_get_channel_basepoints_reply */
bool fromwire_hsm_get_channel_basepoints_reply(const void *p UNNEEDED, struct basepoints *basepoints UNNEEDED, struct pubkey *funding_pubkey UNNEEDED)
{ fprintf(stderr, "fromwire_hsm_get_channel_basepoints_reply called!\n"); abort(); }
/* Generated stub for fromwire_hsm_sign_commitment_tx_reply */
bool fromwire_hsm_sign_commitment_tx_reply(const void *p UNNEEDED, secp256k1_ecdsa_signature *sig UNNEEDED)
{ fprintf(stderr, "fromwire_hsm_sign_commitment_tx_reply called!\n"); abort(); }
/* Generated stub for fromwire_hsm_sign_invoice_reply */
bool fromwire_hsm_sign_invoice_reply(const void *p UNNEEDED, secp256k1_ecdsa_recoverable_signature *sig UNNEEDED)
{ fprintf(stderr, "fromwire_hsm_sign_invoice_reply called!\n"); abort(); }
/* Generated stub for get_chainparams */
const struct chainparams *get_chainparams(const struct lightningd *ld UNNEEDED)
{ fprintf(stderr, "get_chainparams called!\n"); abort(); }
/* Generated stub for get_log_level */
enum log_level get_log_level(struct log_book *lr UNNEEDED)
{ fprintf(stderr, "get_log_level called!\n"); abort(); }
/* Generated stub for htlcs_reconnect */
void htlcs_reconnect(struct lightningd *ld UNNEEDED,
struct htlc_in_map *htlcs_in UNNEEDED,
struct htlc_out_map *htlcs_out UNNEEDED)
{ fprintf(stderr, "htlcs_reconnect called!\n"); abort(); }
/* Generated stub for json_add_bool */
void json_add_bool(struct json_result *result UNNEEDED, const char *fieldname UNNEEDED,
bool value UNNEEDED)
{ fprintf(stderr, "json_add_bool called!\n"); abort(); }
/* Generated stub for json_add_escaped_string */
void json_add_escaped_string(struct json_result *result UNNEEDED,
const char *fieldname UNNEEDED,
const struct json_escaped *esc TAKES UNNEEDED)
{ fprintf(stderr, "json_add_escaped_string called!\n"); abort(); }
/* Generated stub for json_add_hex */
void json_add_hex(struct json_result *result UNNEEDED, const char *fieldname UNNEEDED,
const void *data UNNEEDED, size_t len UNNEEDED)
{ fprintf(stderr, "json_add_hex called!\n"); abort(); }
/* Generated stub for json_add_hex_talarr */
void json_add_hex_talarr(struct json_result *result UNNEEDED,
const char *fieldname UNNEEDED,
const tal_t *data UNNEEDED)
{ fprintf(stderr, "json_add_hex_talarr called!\n"); abort(); }
/* Generated stub for json_add_log */
void json_add_log(struct json_result *result UNNEEDED,
const struct log_book *lr UNNEEDED, enum log_level minlevel UNNEEDED)
{ fprintf(stderr, "json_add_log called!\n"); abort(); }
/* Generated stub for json_add_num */
void json_add_num(struct json_result *result UNNEEDED, const char *fieldname UNNEEDED,
unsigned int value UNNEEDED)
{ fprintf(stderr, "json_add_num called!\n"); abort(); }
/* Generated stub for json_add_pubkey */
void json_add_pubkey(struct json_result *response UNNEEDED,
const char *fieldname UNNEEDED,
const struct pubkey *key UNNEEDED)
{ fprintf(stderr, "json_add_pubkey called!\n"); abort(); }
/* Generated stub for json_add_short_channel_id */
void json_add_short_channel_id(struct json_result *response UNNEEDED,
const char *fieldname UNNEEDED,
const struct short_channel_id *id UNNEEDED)
{ fprintf(stderr, "json_add_short_channel_id called!\n"); abort(); }
/* Generated stub for json_add_string */
void json_add_string(struct json_result *result UNNEEDED, const char *fieldname UNNEEDED, const char *value UNNEEDED)
{ fprintf(stderr, "json_add_string called!\n"); abort(); }
/* Generated stub for json_add_txid */
void json_add_txid(struct json_result *result UNNEEDED, const char *fieldname UNNEEDED,
const struct bitcoin_txid *txid UNNEEDED)
{ fprintf(stderr, "json_add_txid called!\n"); abort(); }
/* Generated stub for json_add_u64 */
void json_add_u64(struct json_result *result UNNEEDED, const char *fieldname UNNEEDED,
uint64_t value UNNEEDED)
{ fprintf(stderr, "json_add_u64 called!\n"); abort(); }
/* Generated stub for json_add_uncommitted_channel */
void json_add_uncommitted_channel(struct json_result *response UNNEEDED,
const struct uncommitted_channel *uc UNNEEDED)
{ fprintf(stderr, "json_add_uncommitted_channel called!\n"); abort(); }
/* Generated stub for json_array_end */
void json_array_end(struct json_result *ptr UNNEEDED)
{ fprintf(stderr, "json_array_end called!\n"); abort(); }
/* Generated stub for json_array_start */
void json_array_start(struct json_result *ptr UNNEEDED, const char *fieldname UNNEEDED)
{ fprintf(stderr, "json_array_start called!\n"); abort(); }
/* Generated stub for json_escape */
struct json_escaped *json_escape(const tal_t *ctx UNNEEDED, const char *str TAKES UNNEEDED)
{ fprintf(stderr, "json_escape called!\n"); abort(); }
/* Generated stub for json_next */
const jsmntok_t *json_next(const jsmntok_t *tok UNNEEDED)
{ fprintf(stderr, "json_next called!\n"); abort(); }
/* Generated stub for json_object_end */
void json_object_end(struct json_result *ptr UNNEEDED)
{ fprintf(stderr, "json_object_end called!\n"); abort(); }
/* Generated stub for json_object_start */
void json_object_start(struct json_result *ptr UNNEEDED, const char *fieldname UNNEEDED)
{ fprintf(stderr, "json_object_start called!\n"); abort(); }
/* Generated stub for json_tok_address_scriptpubkey */
enum address_parse_result json_tok_address_scriptpubkey(const tal_t *ctx UNNEEDED,
const struct chainparams *chainparams UNNEEDED,
const char *buffer UNNEEDED,
const jsmntok_t *tok UNNEEDED, const u8 **scriptpubkey UNNEEDED)
{ fprintf(stderr, "json_tok_address_scriptpubkey called!\n"); abort(); }
/* Generated stub for json_tok_array */
bool json_tok_array(struct command *cmd UNNEEDED, const char *name UNNEEDED,
const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED,
const jsmntok_t **arr UNNEEDED)
{ fprintf(stderr, "json_tok_array called!\n"); abort(); }
/* Generated stub for json_tok_bool */
bool json_tok_bool(struct command *cmd UNNEEDED, const char *name UNNEEDED,
const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED,
bool **b UNNEEDED)
{ fprintf(stderr, "json_tok_bool called!\n"); abort(); }
/* Generated stub for json_tok_channel_id */
bool json_tok_channel_id(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED,
struct channel_id *cid UNNEEDED)
{ fprintf(stderr, "json_tok_channel_id called!\n"); abort(); }
/* Generated stub for json_tok_escaped_string */
bool json_tok_escaped_string(struct command *cmd UNNEEDED, const char *name UNNEEDED,
const char * buffer UNNEEDED, const jsmntok_t *tok UNNEEDED,
const char **str UNNEEDED)
{ fprintf(stderr, "json_tok_escaped_string called!\n"); abort(); }
/* Generated stub for json_tok_label */
bool json_tok_label(struct command *cmd UNNEEDED, const char *name UNNEEDED,
const char * buffer UNNEEDED, const jsmntok_t *tok UNNEEDED,
struct json_escaped **label UNNEEDED)
{ fprintf(stderr, "json_tok_label called!\n"); abort(); }
/* Generated stub for json_tok_loglevel */
bool json_tok_loglevel(struct command *cmd UNNEEDED, const char *name UNNEEDED,
const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED,
enum log_level **level UNNEEDED)
{ fprintf(stderr, "json_tok_loglevel called!\n"); abort(); }
/* Generated stub for json_tok_msat */
bool json_tok_msat(struct command *cmd UNNEEDED, const char *name UNNEEDED,
const char *buffer UNNEEDED, const jsmntok_t * tok UNNEEDED,
u64 **msatoshi_val UNNEEDED)
{ fprintf(stderr, "json_tok_msat called!\n"); abort(); }
/* Generated stub for json_tok_number */
bool json_tok_number(struct command *cmd UNNEEDED, const char *name UNNEEDED,
const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED,
unsigned int **num UNNEEDED)
{ fprintf(stderr, "json_tok_number called!\n"); abort(); }
/* Generated stub for json_tok_pubkey */
bool json_tok_pubkey(struct command *cmd UNNEEDED, const char *name UNNEEDED,
const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED,
struct pubkey **pubkey UNNEEDED)
{ fprintf(stderr, "json_tok_pubkey called!\n"); abort(); }
/* Generated stub for json_tok_short_channel_id */
bool json_tok_short_channel_id(struct command *cmd UNNEEDED, const char *name UNNEEDED,
const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED,
struct short_channel_id **scid UNNEEDED)
{ fprintf(stderr, "json_tok_short_channel_id called!\n"); abort(); }
/* Generated stub for json_tok_string */
bool json_tok_string(struct command *cmd UNNEEDED, const char *name UNNEEDED,
const char * buffer UNNEEDED, const jsmntok_t *tok UNNEEDED,
const char **str UNNEEDED)
{ fprintf(stderr, "json_tok_string called!\n"); abort(); }
/* Generated stub for json_tok_tok */
bool json_tok_tok(struct command *cmd UNNEEDED, const char *name UNNEEDED,
const char *buffer UNNEEDED, const jsmntok_t * tok UNNEEDED,
const jsmntok_t **out UNNEEDED)
{ fprintf(stderr, "json_tok_tok called!\n"); abort(); }
/* Generated stub for json_tok_u64 */
bool json_tok_u64(struct command *cmd UNNEEDED, const char *name UNNEEDED,
const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED,
uint64_t **num UNNEEDED)
{ fprintf(stderr, "json_tok_u64 called!\n"); abort(); }
/* Generated stub for json_to_pubkey */
bool json_to_pubkey(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED,
struct pubkey *pubkey UNNEEDED)
{ fprintf(stderr, "json_to_pubkey called!\n"); abort(); }
/* Generated stub for json_to_short_channel_id */
bool json_to_short_channel_id(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED,
struct short_channel_id *scid UNNEEDED)
{ fprintf(stderr, "json_to_short_channel_id called!\n"); abort(); }
/* Generated stub for kill_uncommitted_channel */
void kill_uncommitted_channel(struct uncommitted_channel *uc UNNEEDED,
const char *why UNNEEDED)
{ fprintf(stderr, "kill_uncommitted_channel called!\n"); abort(); }
/* Generated stub for log_ */
void log_(struct log *log UNNEEDED, enum log_level level UNNEEDED, const char *fmt UNNEEDED, ...)
{ fprintf(stderr, "log_ called!\n"); abort(); }
/* Generated stub for log_add */
void log_add(struct log *log UNNEEDED, const char *fmt UNNEEDED, ...)
{ fprintf(stderr, "log_add called!\n"); abort(); }
/* Generated stub for log_io */
void log_io(struct log *log UNNEEDED, enum log_level dir UNNEEDED, const char *comment UNNEEDED,
const void *data UNNEEDED, size_t len UNNEEDED)
{ fprintf(stderr, "log_io called!\n"); abort(); }
/* Generated stub for new_bolt11 */
struct bolt11 *new_bolt11(const tal_t *ctx UNNEEDED, u64 *msatoshi UNNEEDED)
{ fprintf(stderr, "new_bolt11 called!\n"); abort(); }
/* Generated stub for new_json_result */
struct json_result *new_json_result(const tal_t *ctx UNNEEDED)
{ fprintf(stderr, "new_json_result called!\n"); abort(); }
/* Generated stub for new_log */
struct log *new_log(const tal_t *ctx UNNEEDED, struct log_book *record UNNEEDED, const char *fmt UNNEEDED, ...)
{ fprintf(stderr, "new_log called!\n"); abort(); }
/* Generated stub for new_log_book */
struct log_book *new_log_book(size_t max_mem UNNEEDED,
enum log_level printlevel UNNEEDED)
{ fprintf(stderr, "new_log_book called!\n"); abort(); }
/* Generated stub for new_reltimer_ */
struct oneshot *new_reltimer_(struct timers *timers UNNEEDED,
const tal_t *ctx UNNEEDED,
struct timerel expire UNNEEDED,
void (*cb)(void *) UNNEEDED, void *arg UNNEEDED)
{ fprintf(stderr, "new_reltimer_ called!\n"); abort(); }
/* Generated stub for null_response */
struct json_result *null_response(const tal_t *ctx UNNEEDED)
{ fprintf(stderr, "null_response called!\n"); abort(); }
/* Generated stub for onchaind_funding_spent */
enum watch_result onchaind_funding_spent(struct channel *channel UNNEEDED,
const struct bitcoin_tx *tx UNNEEDED,
u32 blockheight UNNEEDED)
{ fprintf(stderr, "onchaind_funding_spent called!\n"); abort(); }
/* Generated stub for opening_peer_no_active_channels */
void opening_peer_no_active_channels(struct peer *peer UNNEEDED)
{ fprintf(stderr, "opening_peer_no_active_channels called!\n"); abort(); }
/* Generated stub for param */
bool param(struct command *cmd UNNEEDED, const char *buffer UNNEEDED,
const jsmntok_t params[] UNNEEDED, ...)
{ fprintf(stderr, "param called!\n"); abort(); }
/* Generated stub for peer_start_channeld */
void peer_start_channeld(struct channel *channel UNNEEDED,
const struct crypto_state *cs UNNEEDED,
int peer_fd UNNEEDED, int gossip_fd UNNEEDED,
const u8 *funding_signed UNNEEDED,
bool reconnected UNNEEDED)
{ fprintf(stderr, "peer_start_channeld called!\n"); abort(); }
/* Generated stub for peer_start_closingd */
void peer_start_closingd(struct channel *channel UNNEEDED,
const struct crypto_state *cs UNNEEDED,
int peer_fd UNNEEDED, int gossip_fd UNNEEDED,
bool reconnected UNNEEDED,
const u8 *channel_reestablish UNNEEDED)
{ fprintf(stderr, "peer_start_closingd called!\n"); abort(); }
/* Generated stub for peer_start_openingd */
void peer_start_openingd(struct peer *peer UNNEEDED,
const struct crypto_state *cs UNNEEDED,
int peer_fd UNNEEDED, int gossip_fd UNNEEDED,
const u8 *msg UNNEEDED)
{ fprintf(stderr, "peer_start_openingd called!\n"); abort(); }
/* Generated stub for set_log_outfn_ */
void set_log_outfn_(struct log_book *lr UNNEEDED,
void (*print)(const char *prefix UNNEEDED,
enum log_level level UNNEEDED,
bool continued UNNEEDED,
const struct timeabs *time UNNEEDED,
const char *str UNNEEDED,
const u8 *io UNNEEDED,
void *arg) UNNEEDED,
void *arg UNNEEDED)
{ fprintf(stderr, "set_log_outfn_ called!\n"); abort(); }
/* Generated stub for subd_release_channel */
void subd_release_channel(struct subd *owner UNNEEDED, void *channel UNNEEDED)
{ fprintf(stderr, "subd_release_channel called!\n"); abort(); }
/* Generated stub for subd_req_ */
void subd_req_(const tal_t *ctx UNNEEDED,
struct subd *sd UNNEEDED,
const u8 *msg_out UNNEEDED,
int fd_out UNNEEDED, size_t num_fds_in UNNEEDED,
void (*replycb)(struct subd * UNNEEDED, const u8 * UNNEEDED, const int * UNNEEDED, void *) UNNEEDED,
void *replycb_data UNNEEDED)
{ fprintf(stderr, "subd_req_ called!\n"); abort(); }
/* Generated stub for subd_send_msg */
void subd_send_msg(struct subd *sd UNNEEDED, const u8 *msg_out UNNEEDED)
{ fprintf(stderr, "subd_send_msg called!\n"); abort(); }
/* Generated stub for towire_channel_dev_reenable_commit */
u8 *towire_channel_dev_reenable_commit(const tal_t *ctx UNNEEDED)
{ fprintf(stderr, "towire_channel_dev_reenable_commit called!\n"); abort(); }
/* Generated stub for towire_channel_send_shutdown */
u8 *towire_channel_send_shutdown(const tal_t *ctx UNNEEDED)
{ fprintf(stderr, "towire_channel_send_shutdown called!\n"); abort(); }
/* Generated stub for towire_connectctl_connect_to_peer */
u8 *towire_connectctl_connect_to_peer(const tal_t *ctx UNNEEDED, const struct pubkey *id UNNEEDED, u32 seconds_waited UNNEEDED, const struct wireaddr_internal *addrhint UNNEEDED)
{ fprintf(stderr, "towire_connectctl_connect_to_peer called!\n"); abort(); }
/* Generated stub for towire_connectctl_peer_disconnected */
u8 *towire_connectctl_peer_disconnected(const tal_t *ctx UNNEEDED, const struct pubkey *id UNNEEDED)
{ fprintf(stderr, "towire_connectctl_peer_disconnected called!\n"); abort(); }
/* Generated stub for towire_errorfmt */
u8 *towire_errorfmt(const tal_t *ctx UNNEEDED,
const struct channel_id *channel UNNEEDED,
const char *fmt UNNEEDED, ...)
{ fprintf(stderr, "towire_errorfmt called!\n"); abort(); }
/* Generated stub for towire_gossip_get_incoming_channels */
u8 *towire_gossip_get_incoming_channels(const tal_t *ctx UNNEEDED)
{ fprintf(stderr, "towire_gossip_get_incoming_channels called!\n"); abort(); }
/* Generated stub for towire_hsm_get_channel_basepoints */
u8 *towire_hsm_get_channel_basepoints(const tal_t *ctx UNNEEDED, const struct pubkey *peerid UNNEEDED, u64 dbid UNNEEDED)
{ fprintf(stderr, "towire_hsm_get_channel_basepoints called!\n"); abort(); }
/* Generated stub for towire_hsm_sign_commitment_tx */
u8 *towire_hsm_sign_commitment_tx(const tal_t *ctx UNNEEDED, const struct pubkey *peer_id UNNEEDED, u64 channel_dbid UNNEEDED, const struct bitcoin_tx *tx UNNEEDED, const struct pubkey *remote_funding_key UNNEEDED, u64 funding_amount UNNEEDED)
{ fprintf(stderr, "towire_hsm_sign_commitment_tx called!\n"); abort(); }
/* Generated stub for towire_hsm_sign_invoice */
u8 *towire_hsm_sign_invoice(const tal_t *ctx UNNEEDED, const u8 *u5bytes UNNEEDED, const u8 *hrp UNNEEDED)
{ fprintf(stderr, "towire_hsm_sign_invoice called!\n"); abort(); }
/* Generated stub for txfilter_add_scriptpubkey */
void txfilter_add_scriptpubkey(struct txfilter *filter UNNEEDED, const u8 *script TAKES UNNEEDED)
{ fprintf(stderr, "txfilter_add_scriptpubkey called!\n"); abort(); }
/* Generated stub for wallet_channel_delete */
void wallet_channel_delete(struct wallet *w UNNEEDED, u64 wallet_id UNNEEDED)
{ fprintf(stderr, "wallet_channel_delete called!\n"); abort(); }
/* Generated stub for wallet_channel_save */
void wallet_channel_save(struct wallet *w UNNEEDED, struct channel *chan UNNEEDED)
{ fprintf(stderr, "wallet_channel_save called!\n"); abort(); }
/* Generated stub for wallet_channels_load_active */
bool wallet_channels_load_active(const tal_t *ctx UNNEEDED, struct wallet *w UNNEEDED)
{ fprintf(stderr, "wallet_channels_load_active called!\n"); abort(); }
/* Generated stub for wallet_channel_stats_load */
void wallet_channel_stats_load(struct wallet *w UNNEEDED, u64 cdbid UNNEEDED, struct channel_stats *stats UNNEEDED)
{ fprintf(stderr, "wallet_channel_stats_load called!\n"); abort(); }
/* Generated stub for wallet_channeltxs_add */
void wallet_channeltxs_add(struct wallet *w UNNEEDED, struct channel *chan UNNEEDED,
const int type UNNEEDED, const struct bitcoin_txid *txid UNNEEDED,
const u32 input_num UNNEEDED, const u32 blockheight UNNEEDED)
{ fprintf(stderr, "wallet_channeltxs_add called!\n"); abort(); }
/* Generated stub for wallet_htlcs_load_for_channel */
bool wallet_htlcs_load_for_channel(struct wallet *wallet UNNEEDED,
struct channel *chan UNNEEDED,
struct htlc_in_map *htlcs_in UNNEEDED,
struct htlc_out_map *htlcs_out UNNEEDED)
{ fprintf(stderr, "wallet_htlcs_load_for_channel called!\n"); abort(); }
/* Generated stub for wallet_invoice_autoclean */
void wallet_invoice_autoclean(struct wallet * wallet UNNEEDED,
u64 cycle_seconds UNNEEDED,
u64 expired_by UNNEEDED)
{ fprintf(stderr, "wallet_invoice_autoclean called!\n"); abort(); }
/* Generated stub for wallet_invoice_create */
bool wallet_invoice_create(struct wallet *wallet UNNEEDED,
struct invoice *pinvoice UNNEEDED,
u64 *msatoshi TAKES UNNEEDED,
const struct json_escaped *label TAKES UNNEEDED,
u64 expiry UNNEEDED,
const char *b11enc UNNEEDED,
const char *description UNNEEDED,
const struct preimage *r UNNEEDED,
const struct sha256 *rhash UNNEEDED)
{ fprintf(stderr, "wallet_invoice_create called!\n"); abort(); }
/* Generated stub for wallet_invoice_delete */
bool wallet_invoice_delete(struct wallet *wallet UNNEEDED,
struct invoice invoice UNNEEDED)
{ fprintf(stderr, "wallet_invoice_delete called!\n"); abort(); }
/* Generated stub for wallet_invoice_delete_expired */
void wallet_invoice_delete_expired(struct wallet *wallet UNNEEDED,
u64 max_expiry_time UNNEEDED)
{ fprintf(stderr, "wallet_invoice_delete_expired called!\n"); abort(); }
/* Generated stub for wallet_invoice_details */
const struct invoice_details *wallet_invoice_details(const tal_t *ctx UNNEEDED,
struct wallet *wallet UNNEEDED,
struct invoice invoice UNNEEDED)
{ fprintf(stderr, "wallet_invoice_details called!\n"); abort(); }
/* Generated stub for wallet_invoice_find_by_label */
bool wallet_invoice_find_by_label(struct wallet *wallet UNNEEDED,
struct invoice *pinvoice UNNEEDED,
const struct json_escaped *label UNNEEDED)
{ fprintf(stderr, "wallet_invoice_find_by_label called!\n"); abort(); }
/* Generated stub for wallet_invoice_find_by_rhash */
bool wallet_invoice_find_by_rhash(struct wallet *wallet UNNEEDED,
struct invoice *pinvoice UNNEEDED,
const struct sha256 *rhash UNNEEDED)
{ fprintf(stderr, "wallet_invoice_find_by_rhash called!\n"); abort(); }
/* Generated stub for wallet_invoice_iterate */
bool wallet_invoice_iterate(struct wallet *wallet UNNEEDED,
struct invoice_iterator *it UNNEEDED)
{ fprintf(stderr, "wallet_invoice_iterate called!\n"); abort(); }
/* Generated stub for wallet_invoice_iterator_deref */
const struct invoice_details *wallet_invoice_iterator_deref(const tal_t *ctx UNNEEDED,
struct wallet *wallet UNNEEDED,
const struct invoice_iterator *it UNNEEDED)
{ fprintf(stderr, "wallet_invoice_iterator_deref called!\n"); abort(); }
/* Generated stub for wallet_invoice_waitany */
void wallet_invoice_waitany(const tal_t *ctx UNNEEDED,
struct wallet *wallet UNNEEDED,
u64 lastpay_index UNNEEDED,
void (*cb)(const struct invoice * UNNEEDED, void*) UNNEEDED,
void *cbarg UNNEEDED)
{ fprintf(stderr, "wallet_invoice_waitany called!\n"); abort(); }
/* Generated stub for wallet_invoice_waitone */
void wallet_invoice_waitone(const tal_t *ctx UNNEEDED,
struct wallet *wallet UNNEEDED,
struct invoice invoice UNNEEDED,
void (*cb)(const struct invoice * UNNEEDED, void*) UNNEEDED,
void *cbarg UNNEEDED)
{ fprintf(stderr, "wallet_invoice_waitone called!\n"); abort(); }
/* Generated stub for wallet_peer_delete */
void wallet_peer_delete(struct wallet *w UNNEEDED, u64 peer_dbid UNNEEDED)
{ fprintf(stderr, "wallet_peer_delete called!\n"); abort(); }
/* Generated stub for wallet_transaction_locate */
struct txlocator *wallet_transaction_locate(const tal_t *ctx UNNEEDED, struct wallet *w UNNEEDED,
const struct bitcoin_txid *txid UNNEEDED)
{ fprintf(stderr, "wallet_transaction_locate called!\n"); abort(); }
/* Generated stub for watch_txid */
struct txwatch *watch_txid(const tal_t *ctx UNNEEDED,
struct chain_topology *topo UNNEEDED,
struct channel *channel UNNEEDED,
const struct bitcoin_txid *txid UNNEEDED,
enum watch_result (*cb)(struct lightningd *ld UNNEEDED,
struct channel *channel UNNEEDED,
const struct bitcoin_txid * UNNEEDED,
unsigned int depth))
{ fprintf(stderr, "watch_txid called!\n"); abort(); }
/* Generated stub for watch_txo */
struct txowatch *watch_txo(const tal_t *ctx UNNEEDED,
struct chain_topology *topo UNNEEDED,
struct channel *channel UNNEEDED,
const struct bitcoin_txid *txid UNNEEDED,
unsigned int output UNNEEDED,
enum watch_result (*cb)(struct channel *channel UNNEEDED,
const struct bitcoin_tx *tx UNNEEDED,
size_t input_num UNNEEDED,
const struct block *block))
{ fprintf(stderr, "watch_txo called!\n"); abort(); }
/* AUTOGENERATED MOCKS END */
#if DEVELOPER
/* Generated stub for dev_disconnect_permanent */
bool dev_disconnect_permanent(struct lightningd *ld UNNEEDED)
{ fprintf(stderr, "dev_disconnect_permanent called!\n"); abort(); }
#endif
static void add_inchan(struct route_info **inchans, int n)
{
struct route_info *r = tal_arr_expand(inchans);
memset(&r->pubkey, n, sizeof(r->pubkey));
memset(&r->short_channel_id, n, sizeof(r->short_channel_id));
r->fee_base_msat = r->fee_proportional_millionths = r->cltv_expiry_delta
= n;
}
static void add_peer(struct lightningd *ld, int n, enum channel_state state,
bool connected)
{
struct peer *peer = tal(ld, struct peer);
struct channel *c = tal(peer, struct channel);
memset(&peer->id, n, sizeof(peer->id));
list_head_init(&peer->channels);
list_add_tail(&ld->peers, &peer->list);
c->state = state;
c->owner = connected ? (void *)peer : NULL;
/* Channel has incoming capacity n*1000 - 1 millisatoshi */
c->funding_satoshi = n+1;
c->our_msatoshi = 1;
c->our_config.channel_reserve_satoshis = 1;
list_add_tail(&peer->channels, &c->list);
}
/* There *is* padding in this structure, at the end. */
STRUCTEQ_DEF(route_info, ALIGNOF(struct route_info) - sizeof(u16),
pubkey,
short_channel_id,
fee_base_msat,
fee_proportional_millionths,
cltv_expiry_delta);
int main(void)
{
struct lightningd *ld;
bool any_offline;
struct route_info *inchans;
struct route_info **ret;
size_t n;
setup_locale();
secp256k1_ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY
| SECP256K1_CONTEXT_SIGN);
setup_tmpctx();
ld = tal(tmpctx, struct lightningd);
list_head_init(&ld->peers);
inchans = tal_arr(tmpctx, struct route_info, 0);
/* Nothing to choose from -> NULL result. */
assert(select_inchan(tmpctx, ld, 0, inchans, &any_offline) == NULL);
assert(any_offline == false);
/* inchan but no peer -> NULL result. */
add_inchan(&inchans, 0);
assert(select_inchan(tmpctx, ld, 0, inchans, &any_offline) == NULL);
assert(any_offline == false);
/* connected peer but no inchan -> NULL result. */
add_peer(ld, 1, CHANNELD_NORMAL, false);
assert(select_inchan(tmpctx, ld, 0, inchans, &any_offline) == NULL);
assert(any_offline == false);
/* inchan but peer awaiting lockin -> NULL result. */
add_peer(ld, 0, CHANNELD_AWAITING_LOCKIN, true);
assert(select_inchan(tmpctx, ld, 0, inchans, &any_offline) == NULL);
assert(any_offline == false);
/* inchan but peer not connected -> NULL result. */
add_inchan(&inchans, 1);
assert(select_inchan(tmpctx, ld, 0, inchans, &any_offline) == NULL);
assert(any_offline == true);
/* Finally, a correct peer! */
add_inchan(&inchans, 2);
add_peer(ld, 2, CHANNELD_NORMAL, true);
ret = select_inchan(tmpctx, ld, 0, inchans, &any_offline);
assert(tal_count(ret) == 1);
assert(tal_count(ret[0]) == 1);
assert(any_offline == true);
assert(route_info_eq(ret[0], &inchans[2]));
/* Not if we ask for too much! Reserve is 1 satoshi */
ret = select_inchan(tmpctx, ld, 1999, inchans, &any_offline);
assert(tal_count(ret) == 1);
assert(tal_count(ret[0]) == 1);
assert(any_offline == false); /* Other candidate insufficient funds. */
assert(route_info_eq(ret[0], &inchans[2]));
ret = select_inchan(tmpctx, ld, 2000, inchans, &any_offline);
assert(ret == NULL);
assert(any_offline == false); /* Other candidate insufficient funds. */
/* Add another candidate, with twice as much excess. */
add_inchan(&inchans, 3);
add_peer(ld, 3, CHANNELD_NORMAL, true);
for (size_t i = n = 0; i < 1000; i++) {
ret = select_inchan(tmpctx, ld, 1000, inchans, &any_offline);
assert(tal_count(ret) == 1);
assert(tal_count(ret[0]) == 1);
assert(any_offline == false); /* Other candidate insufficient funds. */
assert(route_info_eq(ret[0], &inchans[2])
|| route_info_eq(ret[0], &inchans[3]));
n += route_info_eq(ret[0], &inchans[2]);
}
/* Handwave over probability of this happening! Within 20% */
assert(n > 333 - 66 && n < 333 + 66);
printf("Number of selections with 999 excess: %zu\n"
"Number of selections with 1999 excess: %zu\n",
n, 1000 - n);
for (size_t i = n = 0; i < 1000; i++) {
ret = select_inchan(tmpctx, ld, 1499, inchans, &any_offline);
assert(tal_count(ret) == 1);
assert(tal_count(ret[0]) == 1);
assert(any_offline == false); /* Other candidate insufficient funds. */
assert(route_info_eq(ret[0], &inchans[2])
|| route_info_eq(ret[0], &inchans[3]));
n += route_info_eq(ret[0], &inchans[2]);
}
assert(n > 250 - 50 && n < 250 + 50);
printf("Number of selections with 500 excess: %zu\n"
"Number of selections with 1500 excess: %zu\n",
n, 1000 - n);
/* No memory leaks please */
secp256k1_context_destroy(secp256k1_ctx);
tal_free(tmpctx);
/* FIXME: Do BOLT comparison! */
return 0;
}

48
tests/test_invoices.py

@ -1,6 +1,6 @@
from fixtures import * # noqa: F401,F403 from fixtures import * # noqa: F401,F403
from lightning import RpcError from lightning import RpcError
from utils import only_one, DEVELOPER from utils import only_one, DEVELOPER, wait_for, wait_channel_quiescent
import pytest import pytest
@ -29,6 +29,9 @@ def test_invoice(node_factory):
assert b11['fallbacks'][0]['type'] == 'P2WPKH' assert b11['fallbacks'][0]['type'] == 'P2WPKH'
assert b11['fallbacks'][1]['addr'] == addr2 assert b11['fallbacks'][1]['addr'] == addr2
assert b11['fallbacks'][1]['type'] == 'P2SH' assert b11['fallbacks'][1]['type'] == 'P2SH'
# There's no incoming channel, so no routeboost
assert 'routes' not in b11
assert 'warning_capacity' in inv
# Check pay_index is null # Check pay_index is null
outputs = l1.db_query('SELECT pay_index IS NULL AS q FROM invoices WHERE label="label";') outputs = l1.db_query('SELECT pay_index IS NULL AS q FROM invoices WHERE label="label";')
@ -45,6 +48,9 @@ def test_invoice(node_factory):
# to match the "lnbcrt1" above with the '1' digit as the # to match the "lnbcrt1" above with the '1' digit as the
# separator, and not for example "lnbcrt1m1....". # separator, and not for example "lnbcrt1m1....".
assert b11.count('1') == 1 assert b11.count('1') == 1
# There's no incoming channel, so no routeboost
assert 'routes' not in b11
assert 'warning_capacity' in inv
def test_invoice_weirdstring(node_factory): def test_invoice_weirdstring(node_factory):
@ -111,6 +117,46 @@ def test_invoice_preimage(node_factory):
l2.rpc.invoice(123456, 'inv2', '?', preimage=invoice_preimage) l2.rpc.invoice(123456, 'inv2', '?', preimage=invoice_preimage)
def test_invoice_routeboost(node_factory):
"""Test routeboost 'r' hint in bolt11 invoice.
"""
l1, l2 = node_factory.line_graph(2, announce=True, fundamount=10**4)
# Make sure we've seen channel_update from peer.
wait_for(lambda: [a['active'] for a in l2.rpc.listchannels()['channels']] == [True, True])
# Make invoice and pay it
inv = l2.rpc.invoice(msatoshi=123456, label="inv1", description="?")
# Check routeboost.
assert 'warning_capacity' not in inv
assert 'warning_offline' not in inv
# Route array has single route with single element.
r = only_one(only_one(l1.rpc.decodepay(inv['bolt11'])['routes']))
assert r['pubkey'] == l1.info['id']
assert r['short_channel_id'] == l1.rpc.listchannels()['channels'][0]['short_channel_id']
assert r['fee_base_msat'] == 1
assert r['fee_proportional_millionths'] == 10
assert r['cltv_expiry_delta'] == 6
# Pay it (and make sure it's fully resolved before we take l1 offline!)
l1.rpc.pay(inv['bolt11'])
wait_channel_quiescent(l1, l2)
# Due to reserve, l1 doesn't have capacity to pay this.
inv = l2.rpc.invoice(msatoshi=10**7 - 123456, label="inv2", description="?")
# Check warning
assert 'warning_capacity' in inv
assert 'warning_offline' not in inv
l1.stop()
wait_for(lambda: not only_one(l2.rpc.listpeers(l1.info['id'])['peers'])['connected'])
inv = l2.rpc.invoice(123456, label="inv3", description="?")
# Check warning.
assert 'warning_capacity' not in inv
assert 'warning_offline' in inv
def test_invoice_expiry(node_factory, executor): def test_invoice_expiry(node_factory, executor):
l1, l2 = node_factory.line_graph(2, fundchannel=True) l1, l2 = node_factory.line_graph(2, fundchannel=True)

4
wallet/wallet.h

@ -683,8 +683,8 @@ bool wallet_invoice_iterate(struct wallet *wallet,
* @iterator - the iterator object to use. * @iterator - the iterator object to use.
* @return pointer to the invoice details allocated off of `ctx`. * @return pointer to the invoice details allocated off of `ctx`.
*/ */
const struct invoice_details * const struct invoice_details *wallet_invoice_iterator_deref(const tal_t *ctx,
wallet_invoice_iterator_deref(const tal_t *ctx, struct wallet *wallet, struct wallet *wallet,
const struct invoice_iterator *it); const struct invoice_iterator *it);
/** /**

Loading…
Cancel
Save