diff --git a/ccan/README b/ccan/README index 3316cef10dfb..289e9cf259c9 100644 --- a/ccan/README +++ b/ccan/README @@ -1,3 +1,3 @@ CCAN imported from http://ccodearchive.net. -CCAN version: init-2504-g48b4ffc3 +CCAN version: init-2506-gec95c3c5 diff --git a/ccan/ccan/compiler/compiler.h b/ccan/ccan/compiler/compiler.h index 1bbb3b8b61c6..562b29ec71cc 100644 --- a/ccan/ccan/compiler/compiler.h +++ b/ccan/ccan/compiler/compiler.h @@ -271,6 +271,19 @@ #define NON_NULL_ARGS(...) #endif +#if HAVE_ATTRIBUTE_RETURNS_NONNULL +/** + * RETURNS_NONNULL - specify that this function cannot return NULL. + * + * Mainly an optimization opportunity, but can also suppress warnings. + * + * Example: + * RETURNS_NONNULL char *my_copy(char *buf); + */ +#define RETURNS_NONNULL __attribute__((__returns_nonnull__)) +#else +#define RETURNS_NONNULL +#endif #if HAVE_ATTRIBUTE_SENTINEL /** diff --git a/ccan/tools/configurator/configurator.c b/ccan/tools/configurator/configurator.c index 33651ef4df3b..9487e694ce23 100644 --- a/ccan/tools/configurator/configurator.c +++ b/ccan/tools/configurator/configurator.c @@ -142,6 +142,9 @@ static const struct test base_tests[] = { { "HAVE_ATTRIBUTE_NONNULL", "__attribute__((nonnull)) support", "DEFINES_FUNC", NULL, NULL, "static char *__attribute__((nonnull)) func(char *p) { return p; }" }, + { "HAVE_ATTRIBUTE_RETURNS_NONNULL", "__attribute__((returns_nonnull)) support", + "DEFINES_FUNC", NULL, NULL, + "static const char *__attribute__((returns_nonnull)) func(void) { return \"hi\"; }" }, { "HAVE_ATTRIBUTE_SENTINEL", "__attribute__((sentinel)) support", "DEFINES_FUNC", NULL, NULL, "static int __attribute__((sentinel)) func(int i, ...) { return i; }" }, diff --git a/common/json_command.h b/common/json_command.h index d090327ae3da..95ae8a192357 100644 --- a/common/json_command.h +++ b/common/json_command.h @@ -15,7 +15,7 @@ struct command_result; /* Caller supplied this: param assumes it can call it. */ struct command_result *command_fail(struct command *cmd, errcode_t code, const char *fmt, ...) - PRINTF_FMT(3, 4) WARN_UNUSED_RESULT; + PRINTF_FMT(3, 4) WARN_UNUSED_RESULT RETURNS_NONNULL; /* Convenient wrapper for "paramname: msg: invalid token '.*%s'" */ static inline struct command_result * diff --git a/common/param.c b/common/param.c index 3babe665e0c3..7d860225ab3f 100644 --- a/common/param.c +++ b/common/param.c @@ -298,9 +298,10 @@ const char *param_subcommand(struct command *cmd, const char *buffer, return subcmd; /* We really do ignore this. */ - if (command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "Unknown subcommand '%s'", subcmd)) - ; + struct command_result *ignore; + ignore = command_fail(cmd, JSONRPC2_INVALID_PARAMS, + "Unknown subcommand '%s'", subcmd); + assert(ignore); return NULL; } @@ -323,9 +324,10 @@ bool param(struct command *cmd, const char *buffer, } if (!param_add(¶ms, name, required, cbx, arg)) { /* We really do ignore this return! */ - if (command_fail(cmd, PARAM_DEV_ERROR, - "developer error: param_add %s", name)) - ; + struct command_result *ignore; + ignore = command_fail(cmd, PARAM_DEV_ERROR, + "developer error: param_add %s", name); + assert(ignore); va_end(ap); return false; } diff --git a/common/wireaddr.c b/common/wireaddr.c index b765157c385f..3a0a3784094f 100644 --- a/common/wireaddr.c +++ b/common/wireaddr.c @@ -691,6 +691,35 @@ struct addrinfo *wireaddr_to_addrinfo(const tal_t *ctx, abort(); } +struct wireaddr *fromwire_wireaddr_array(const tal_t *ctx, const u8 *ser) +{ + const u8 *cursor = ser; + size_t len = tal_count(ser); + struct wireaddr *wireaddrs = tal_arr(ctx, struct wireaddr, 0); + + while (cursor && len) { + struct wireaddr wireaddr; + + /* BOLT #7: + * + * The receiving node: + *... + * - SHOULD ignore the first `address descriptor` that does + * NOT match the types defined above. + */ + if (!fromwire_wireaddr(&cursor, &len, &wireaddr)) { + if (!cursor) + /* Parsing address failed */ + return tal_free(wireaddrs); + /* Unknown type, stop there. */ + break; + } + + tal_arr_expand(&wireaddrs, wireaddr); + } + return wireaddrs; +} + bool all_tor_addresses(const struct wireaddr_internal *wireaddr) { for (int i = 0; i < tal_count(wireaddr); i++) { diff --git a/common/wireaddr.h b/common/wireaddr.h index 26612a16e2fd..f45c2cc76e62 100644 --- a/common/wireaddr.h +++ b/common/wireaddr.h @@ -165,4 +165,7 @@ struct addrinfo *wireaddr_internal_to_addrinfo(const tal_t *ctx, bool all_tor_addresses(const struct wireaddr_internal *wireaddr); +/* Decode an array of serialized addresses from node_announcement */ +struct wireaddr *fromwire_wireaddr_array(const tal_t *ctx, const u8 *ser); + #endif /* LIGHTNING_COMMON_WIREADDR_H */ diff --git a/doc/lightning-invoice.7 b/doc/lightning-invoice.7 index 9e07b6ac17b8..d640a134ffcd 100644 --- a/doc/lightning-invoice.7 +++ b/doc/lightning-invoice.7 @@ -106,19 +106,16 @@ One of the following warnings may occur (on success): .RS .IP \[bu] -\fIwarning_offline\fR if no channel with a currently connected peer has - the incoming capacity to pay this invoice +\fIwarning_capacity\fR: even using all possible channels, there's not enough incoming capacity to pay this invoice\. .IP \[bu] -\fIwarning_capacity\fR if there is no channel that has sufficient - incoming capacity +\fIwarning_offline\fR: there would be enough incoming capacity, but some channels are offline, so there isn't\. .IP \[bu] -\fIwarning_deadends\fR if there is no channel that is not a dead-end +\fIwarning_deadends\fR: there would be enough incoming capacity, but some channels are dead-ends (no other public channels from those peers), so there isn't\. +.IP \[bu] +\fIwarning_private_unused\fR: there would be enough incoming capacity, but some channels are unannounced and \fIexposeprivatechannels\fR is \fIfalse\fR, so there isn't\. .IP \[bu] \fIwarning_mpp\fR if there is sufficient capacity, but not in a single channel, so the payer will have to use multi-part payments\. -.IP \[bu] -\fIwarning_mpp_capacity\fR if there is not sufficient capacity, even with all - channels\. .RE .SH AUTHOR @@ -134,4 +131,4 @@ Rusty Russell \fI is mainly responsible\. Main web site: \fIhttps://github.com/ElementsProject/lightning\fR -\" SHA256STAMP:dc375ee583188c669574a8ef203971df650ef221567dccb8df7a81ed5ee4990f +\" SHA256STAMP:d53ec67cd81a41c7218e282c3d7662933868b25190334e9322de8a90ab99d603 diff --git a/doc/lightning-invoice.7.md b/doc/lightning-invoice.7.md index 6f43a9b322eb..d48246c6855f 100644 --- a/doc/lightning-invoice.7.md +++ b/doc/lightning-invoice.7.md @@ -87,15 +87,12 @@ The following error codes may occur: - 902: None of the specified *exposeprivatechannels* were usable. One of the following warnings may occur (on success): -- *warning\_offline* if no channel with a currently connected peer has - the incoming capacity to pay this invoice -- *warning\_capacity* if there is no channel that has sufficient - incoming capacity -- *warning\_deadends* if there is no channel that is not a dead-end +- *warning_capacity*: even using all possible channels, there's not enough incoming capacity to pay this invoice. +- *warning_offline*: there would be enough incoming capacity, but some channels are offline, so there isn't. +- *warning_deadends*: there would be enough incoming capacity, but some channels are dead-ends (no other public channels from those peers), so there isn't. +- *warning_private_unused*: there would be enough incoming capacity, but some channels are unannounced and *exposeprivatechannels* is *false*, so there isn't. - *warning_mpp* if there is sufficient capacity, but not in a single channel, so the payer will have to use multi-part payments. -- *warning_mpp_capacity* if there is not sufficient capacity, even with all - channels. AUTHOR ------ diff --git a/gossipd/gossip_generation.c b/gossipd/gossip_generation.c index 1baddd1fca37..7defae9f434c 100644 --- a/gossipd/gossip_generation.c +++ b/gossipd/gossip_generation.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -414,6 +415,12 @@ void refresh_local_channel(struct daemon *daemon, { const struct half_chan *hc; struct local_cupdate *lc; + u8 *prev; + secp256k1_ecdsa_signature signature; + struct bitcoin_blkid chain_hash; + struct short_channel_id short_channel_id; + u32 timestamp; + u8 message_flags, channel_flags; hc = &local_chan->chan->half[local_chan->direction]; @@ -425,14 +432,32 @@ void refresh_local_channel(struct daemon *daemon, lc->daemon = daemon; lc->local_chan = local_chan; lc->even_if_identical = even_if_identical; - lc->disable = (hc->channel_flags & ROUTING_FLAGS_DISABLED) - || local_chan->local_disabled; - lc->cltv_expiry_delta = hc->delay; - lc->htlc_minimum = hc->htlc_minimum; - lc->htlc_maximum = hc->htlc_maximum; - lc->fee_base_msat = hc->base_fee; - lc->fee_proportional_millionths = hc->proportional_fee; + prev = cast_const(u8 *, + gossip_store_get(tmpctx, daemon->rstate->gs, + local_chan->chan->half[local_chan->direction] + .bcast.index)); + + /* If it's a private update, unwrap */ + fromwire_gossip_store_private_update(tmpctx, prev, &prev); + + if (!fromwire_channel_update_option_channel_htlc_max(prev, + &signature, &chain_hash, + &short_channel_id, ×tamp, + &message_flags, &channel_flags, + &lc->cltv_expiry_delta, + &lc->htlc_minimum, + &lc->fee_base_msat, + &lc->fee_proportional_millionths, + &lc->htlc_maximum)) { + status_broken("Could not decode local channel_update %s!", + tal_hex(tmpctx, prev)); + tal_free(lc); + return; + } + + lc->disable = (channel_flags & ROUTING_FLAGS_DISABLED) + || local_chan->local_disabled; update_local_channel(lc); } diff --git a/gossipd/gossipd.c b/gossipd/gossipd.c index 5619b5ef7b3d..d13ba5e4652c 100644 --- a/gossipd/gossipd.c +++ b/gossipd/gossipd.c @@ -193,7 +193,7 @@ static bool get_node_announcement(const tal_t *ctx, return false; } - *wireaddrs = read_addresses(ctx, addresses); + *wireaddrs = fromwire_wireaddr_array(ctx, addresses); tal_free(addresses); return true; } @@ -1025,7 +1025,7 @@ static void gossip_refresh_network(struct daemon *daemon) continue; } - if (!is_halfchan_enabled(hc)) { + if (is_chan_local_disabled(daemon->rstate, c)) { /* Only send keepalives for active connections */ continue; } @@ -1135,285 +1135,9 @@ static struct io_plan *gossip_init(struct io_conn *conn, daemon->connectd = daemon_conn_new(daemon, CONNECTD_FD, connectd_req, NULL, daemon); - return daemon_conn_read_next(conn, daemon->master); -} - -/*~ lightningd can ask for a route between nodes. */ -static struct io_plan *getroute_req(struct io_conn *conn, struct daemon *daemon, - const u8 *msg) -{ - struct node_id *source, destination; - struct amount_msat msat; - u32 final_cltv; - /* risk factor 12.345% -> riskfactor_millionths = 12345000 */ - u64 riskfactor_millionths; - u32 max_hops; - u8 *out; - struct route_hop **hops; - /* fuzz 12.345% -> fuzz_millionths = 12345000 */ - u64 fuzz_millionths; - struct exclude_entry **excluded; - - /* To choose between variations, we need to know how much we're - * sending (eliminates too-small channels, and also effects the fees - * we'll pay), how to trade off more locktime vs. more fees, and how - * much cltv we need a the final node to give exact values for each - * intermediate hop, as well as how much random fuzz to inject to - * avoid being too predictable. - * - * We also treat routing slightly differently if we're asking - * for a route from ourselves (the usual case): in that case, - * we don't have to consider fees on our own outgoing channels. - */ - if (!fromwire_gossipd_getroute_request( - msg, msg, &source, &destination, &msat, &riskfactor_millionths, - &final_cltv, &fuzz_millionths, &excluded, &max_hops)) - master_badmsg(WIRE_GOSSIPD_GETROUTE_REQUEST, msg); - - status_debug("Trying to find a route from %s to %s for %s", - source - ? type_to_string(tmpctx, struct node_id, source) : "(me)", - type_to_string(tmpctx, struct node_id, &destination), - type_to_string(tmpctx, struct amount_msat, &msat)); - - /* routing.c does all the hard work; can return NULL. */ - hops = get_route(tmpctx, daemon->rstate, source, &destination, msat, - riskfactor_millionths / 1000000.0, final_cltv, - fuzz_millionths / 1000000.0, pseudorand_u64(), - excluded, max_hops); - - out = towire_gossipd_getroute_reply(NULL, - cast_const2(const struct route_hop **, - hops)); - daemon_conn_send(daemon->master, take(out)); - return daemon_conn_read_next(conn, daemon->master); -} - -/*~ When someone asks lightningd to `listchannels`, gossipd does the work: - * marshalling the channel information for all channels into an array of - * gossip_getchannels_entry, which lightningd converts to JSON. Each channel - * is represented by two half_chan; one in each direction. - */ -static struct gossip_halfchannel_entry *hc_entry(const tal_t *ctx, - const struct chan *chan, - int idx) -{ - /* Our 'struct chan' contains two nodes: they are in pubkey_cmp order - * (ie. chan->nodes[0] is the lesser pubkey) and this is the same as - * the direction bit in `channel_update`s `channel_flags`. - * - * The halfchans are arranged so that half[0] src == nodes[0], and we - * use that here. */ - const struct half_chan *c = &chan->half[idx]; - struct gossip_halfchannel_entry *e; - - /* If we've never seen a channel_update for this direction... */ - if (!is_halfchan_defined(c)) - return NULL; - - e = tal(ctx, struct gossip_halfchannel_entry); - e->channel_flags = c->channel_flags; - e->message_flags = c->message_flags; - e->last_update_timestamp = c->bcast.timestamp; - e->base_fee_msat = c->base_fee; - e->fee_per_millionth = c->proportional_fee; - e->delay = c->delay; - e->min = c->htlc_minimum; - e->max = c->htlc_maximum; - - return e; -} - -/*~ We don't keep channel features in memory; they're rarely used. So we - * remember if it exists, and load it off disk when needed. */ -static u8 *get_channel_features(const tal_t *ctx, - struct gossip_store *gs, - const struct chan *chan) -{ - secp256k1_ecdsa_signature sig; - u8 *features; - struct bitcoin_blkid chain_hash; - struct short_channel_id short_channel_id; - struct node_id node_id; - struct pubkey bitcoin_key; - struct amount_sat sats; - u8 *ann; - - /* This is where we stash a flag to indicate it exists. */ - if (!chan->half[0].any_features) - return NULL; - - ann = cast_const(u8 *, gossip_store_get(tmpctx, gs, chan->bcast.index)); - - /* Could be a private_channel */ - fromwire_gossip_store_private_channel(tmpctx, ann, &sats, &ann); - - if (!fromwire_channel_announcement(ctx, ann, &sig, &sig, &sig, &sig, - &features, &chain_hash, - &short_channel_id, - &node_id, &node_id, - &bitcoin_key, &bitcoin_key)) - status_failed(STATUS_FAIL_INTERNAL_ERROR, - "bad channel_announcement / local_add_channel at %u: %s", - chan->bcast.index, tal_hex(tmpctx, ann)); - - return features; -} - -/*~ Marshal (possibly) both channel directions into entries. */ -static void append_channel(struct routing_state *rstate, - const struct gossip_getchannels_entry ***entries, - const struct chan *chan, - const struct node_id *srcfilter) -{ - struct gossip_getchannels_entry *e = tal(*entries, struct gossip_getchannels_entry); - - e->node[0] = chan->nodes[0]->id; - e->node[1] = chan->nodes[1]->id; - e->sat = chan->sat; - e->local_disabled = is_chan_local_disabled(rstate, chan); - e->public = is_chan_public(chan); - e->short_channel_id = chan->scid; - e->features = get_channel_features(e, rstate->gs, chan); - if (!srcfilter || node_id_eq(&e->node[0], srcfilter)) - e->e[0] = hc_entry(*entries, chan, 0); - else - e->e[0] = NULL; - if (!srcfilter || node_id_eq(&e->node[1], srcfilter)) - e->e[1] = hc_entry(*entries, chan, 1); - else - e->e[1] = NULL; - - /* We choose not to tell lightningd about channels with no updates, - * as they're unusable and can't be represented in the listchannels - * JSON output we use anyway. */ - if (e->e[0] || e->e[1]) - tal_arr_expand(entries, e); -} - -/*~ This is where lightningd asks for all channels we know about. */ -static struct io_plan *getchannels_req(struct io_conn *conn, - struct daemon *daemon, - const u8 *msg) -{ - u8 *out; - const struct gossip_getchannels_entry **entries; - struct chan *chan; - struct short_channel_id *scid, *prev; - struct node_id *source; - bool complete = true; - - /* Note: scid is marked optional in gossip_wire.csv */ - if (!fromwire_gossipd_getchannels_request(msg, msg, &scid, &source, - &prev)) - master_badmsg(WIRE_GOSSIPD_GETCHANNELS_REQUEST, msg); - - entries = tal_arr(tmpctx, const struct gossip_getchannels_entry *, 0); - /* They can ask about a particular channel by short_channel_id */ - if (scid) { - chan = get_channel(daemon->rstate, scid); - if (chan) - append_channel(daemon->rstate, &entries, chan, NULL); - } else if (source) { - struct node *s = get_node(daemon->rstate, source); - if (s) { - struct chan_map_iter i; - struct chan *c; - - for (c = first_chan(s, &i); c; c = next_chan(s, &i)) { - append_channel(daemon->rstate, - &entries, c, source); - } - } - } else { - u64 idx; - - /* For the more general case, we just iterate through every - * short channel id, starting with previous if any (there is - * no scid 0). */ - idx = prev ? prev->u64 : 0; - while ((chan = uintmap_after(&daemon->rstate->chanmap, &idx))) { - append_channel(daemon->rstate, &entries, chan, NULL); - /* Limit how many we do at once. */ - if (tal_count(entries) == 4096) { - complete = false; - break; - } - } - } - - out = towire_gossipd_getchannels_reply(NULL, complete, entries); - daemon_conn_send(daemon->master, take(out)); - return daemon_conn_read_next(conn, daemon->master); -} - -/*~ Similarly, lightningd asks us for all nodes when it gets `listnodes` */ -/* We keep pointers into n, assuming it won't change. */ -static void add_node_entry(const tal_t *ctx, - struct daemon *daemon, - const struct node *n, - struct gossip_getnodes_entry *e) -{ - e->nodeid = n->id; - if (get_node_announcement(ctx, daemon, n, - e->color, e->alias, - &e->features, - &e->addresses)) { - e->last_timestamp = n->bcast.timestamp; - } else { - /* Timestamp on wire is an unsigned 32 bit: we use a 64-bit - * signed, so -1 means "we never received a - * channel_update". */ - e->last_timestamp = -1; - } -} - -/* Simply routine when they ask for `listnodes` */ -static struct io_plan *getnodes(struct io_conn *conn, struct daemon *daemon, - const u8 *msg) -{ - u8 *out; - struct node *n; - const struct gossip_getnodes_entry **nodes; - struct gossip_getnodes_entry *node_arr; - struct node_id *id; - - if (!fromwire_gossipd_getnodes_request(tmpctx, msg, &id)) - master_badmsg(WIRE_GOSSIPD_GETNODES_REQUEST, msg); - - /* Format of reply is the same whether they ask for a specific node - * (0 or one responses) or all nodes (0 or more) */ - if (id) { - n = get_node(daemon->rstate, id); - if (n) { - node_arr = tal_arr(tmpctx, - struct gossip_getnodes_entry, - 1); - add_node_entry(node_arr, daemon, n, &node_arr[0]); - } else { - nodes = NULL; - node_arr = NULL; - } - } else { - struct node_map_iter it; - size_t i = 0; - node_arr = tal_arr(tmpctx, struct gossip_getnodes_entry, - node_map_count(daemon->rstate->nodes)); - n = node_map_first(daemon->rstate->nodes, &it); - while (n != NULL) { - add_node_entry(node_arr, daemon, n, &node_arr[i++]); - n = node_map_next(daemon->rstate->nodes, &it); - } - assert(i == node_map_count(daemon->rstate->nodes)); - } - - /* FIXME: towire wants array of pointers. */ - nodes = tal_arr(tmpctx, const struct gossip_getnodes_entry *, - tal_count(node_arr)); - for (size_t i = 0; i < tal_count(node_arr); i++) - nodes[i] = &node_arr[i]; - out = towire_gossipd_getnodes_reply(NULL, nodes); - daemon_conn_send(daemon->master, take(out)); + /* OK, we are ready. */ + daemon_conn_send(daemon->master, + take(towire_gossipd_init_reply(NULL))); return daemon_conn_read_next(conn, daemon->master); } @@ -1472,81 +1196,6 @@ static struct io_plan *ping_req(struct io_conn *conn, struct daemon *daemon, return daemon_conn_read_next(conn, daemon->master); } -/*~ If a node has no public channels (other than the one to us), it's not - * a very useful route to tell anyone about. */ -static bool node_has_public_channels(const struct node *peer, - const struct chan *exclude) -{ - struct chan_map_iter i; - struct chan *c; - - for (c = first_chan(peer, &i); c; c = next_chan(peer, &i)) { - if (c == exclude) - continue; - if (is_chan_public(c)) - return true; - } - return false; -} - -/*~ For routeboost, we offer payers a hint of what incoming channels might - * have capacity for their payment. To do this, lightningd asks for the - * information about all channels to this node; but gossipd doesn't know about - * current capacities, so lightningd selects which to use. */ -static struct io_plan *get_incoming_channels(struct io_conn *conn, - struct daemon *daemon, - const u8 *msg) -{ - struct node *node; - struct route_info *public = tal_arr(tmpctx, struct route_info, 0); - struct route_info *private = tal_arr(tmpctx, struct route_info, 0); - bool *priv_deadends = tal_arr(tmpctx, bool, 0); - bool *pub_deadends = tal_arr(tmpctx, bool, 0); - - if (!fromwire_gossipd_get_incoming_channels(msg)) - master_badmsg(WIRE_GOSSIPD_GET_INCOMING_CHANNELS, msg); - - node = get_node(daemon->rstate, &daemon->rstate->local_id); - if (node) { - struct chan_map_iter i; - struct chan *c; - - for (c = first_chan(node, &i); c; c = next_chan(node, &i)) { - const struct half_chan *hc; - struct route_info ri; - bool deadend; - - hc = &c->half[half_chan_to(node, c)]; - - if (!is_halfchan_enabled(hc)) - continue; - - ri.pubkey = other_node(node, c)->id; - ri.short_channel_id = c->scid; - ri.fee_base_msat = hc->base_fee; - ri.fee_proportional_millionths = hc->proportional_fee; - ri.cltv_expiry_delta = hc->delay; - - deadend = !node_has_public_channels(other_node(node, c), - c); - if (is_chan_public(c)) { - tal_arr_expand(&public, ri); - tal_arr_expand(&pub_deadends, deadend); - } else { - tal_arr_expand(&private, ri); - tal_arr_expand(&priv_deadends, deadend); - } - } - } - - msg = towire_gossipd_get_incoming_channels_reply(NULL, - public, pub_deadends, - private, priv_deadends); - daemon_conn_send(daemon->master, take(msg)); - - return daemon_conn_read_next(conn, daemon->master); -} - static struct io_plan *new_blockheight(struct io_conn *conn, struct daemon *daemon, const u8 *msg) @@ -1816,15 +1465,6 @@ static struct io_plan *recv_req(struct io_conn *conn, case WIRE_GOSSIPD_INIT: return gossip_init(conn, daemon, msg); - case WIRE_GOSSIPD_GETNODES_REQUEST: - return getnodes(conn, daemon, msg); - - case WIRE_GOSSIPD_GETROUTE_REQUEST: - return getroute_req(conn, daemon, msg); - - case WIRE_GOSSIPD_GETCHANNELS_REQUEST: - return getchannels_req(conn, daemon, msg); - case WIRE_GOSSIPD_GET_STRIPPED_CUPDATE: return get_stripped_cupdate(conn, daemon, msg); @@ -1840,9 +1480,6 @@ static struct io_plan *recv_req(struct io_conn *conn, case WIRE_GOSSIPD_PING: return ping_req(conn, daemon, msg); - case WIRE_GOSSIPD_GET_INCOMING_CHANNELS: - return get_incoming_channels(conn, daemon, msg); - case WIRE_GOSSIPD_NEW_BLOCKHEIGHT: return new_blockheight(conn, daemon, msg); @@ -1872,12 +1509,9 @@ static struct io_plan *recv_req(struct io_conn *conn, case WIRE_GOSSIPD_SEND_ONIONMSG: return onionmsg_req(conn, daemon, msg); /* We send these, we don't receive them */ - case WIRE_GOSSIPD_GETNODES_REPLY: - case WIRE_GOSSIPD_GETROUTE_REPLY: - case WIRE_GOSSIPD_GETCHANNELS_REPLY: case WIRE_GOSSIPD_PING_REPLY: + case WIRE_GOSSIPD_INIT_REPLY: case WIRE_GOSSIPD_GET_STRIPPED_CUPDATE_REPLY: - case WIRE_GOSSIPD_GET_INCOMING_CHANNELS_REPLY: case WIRE_GOSSIPD_GET_TXOUT: case WIRE_GOSSIPD_DEV_MEMLEAK_REPLY: case WIRE_GOSSIPD_DEV_COMPACT_STORE_REPLY: diff --git a/gossipd/gossipd_wire.csv b/gossipd/gossipd_wire.csv index f9b6e0f61939..2dbd2b2245d7 100644 --- a/gossipd/gossipd_wire.csv +++ b/gossipd/gossipd_wire.csv @@ -16,46 +16,12 @@ msgdata,gossipd_init,dev_gossip_time,?u32, msgdata,gossipd_init,dev_fast_gossip,bool, msgdata,gossipd_init,dev_fast_gossip_prune,bool, +msgtype,gossipd_init_reply,3100 + # In developer mode, we can mess with time. msgtype,gossipd_dev_set_time,3001 msgdata,gossipd_dev_set_time,dev_gossip_time,u32, -# Pass JSON-RPC getnodes call through -msgtype,gossipd_getnodes_request,3005 -msgdata,gossipd_getnodes_request,id,?node_id, - -#include -msgtype,gossipd_getnodes_reply,3105 -msgdata,gossipd_getnodes_reply,num_nodes,u32, -msgdata,gossipd_getnodes_reply,nodes,gossip_getnodes_entry,num_nodes - -# Pass JSON-RPC getroute call through -msgtype,gossipd_getroute_request,3006 -# Source defaults to "us", and means we don't consider first-hop channel fees -msgdata,gossipd_getroute_request,source,?node_id, -msgdata,gossipd_getroute_request,destination,node_id, -msgdata,gossipd_getroute_request,msatoshi,amount_msat, -msgdata,gossipd_getroute_request,riskfactor_millionths,u64, -msgdata,gossipd_getroute_request,final_cltv,u32, -msgdata,gossipd_getroute_request,fuzz_millionths,u64, -msgdata,gossipd_getroute_request,num_excluded,u16, -msgdata,gossipd_getroute_request,excluded,exclude_entry,num_excluded -msgdata,gossipd_getroute_request,max_hops,u32, - -msgtype,gossipd_getroute_reply,3106 -msgdata,gossipd_getroute_reply,num_hops,u16, -msgdata,gossipd_getroute_reply,hops,route_hop,num_hops - -msgtype,gossipd_getchannels_request,3007 -msgdata,gossipd_getchannels_request,short_channel_id,?short_channel_id, -msgdata,gossipd_getchannels_request,source,?node_id, -msgdata,gossipd_getchannels_request,prev,?short_channel_id, - -msgtype,gossipd_getchannels_reply,3107 -msgdata,gossipd_getchannels_reply,complete,bool, -msgdata,gossipd_getchannels_reply,num_channels,u32, -msgdata,gossipd_getchannels_reply,nodes,gossip_getchannels_entry,num_channels - # Ping/pong test. Waits for a reply if it expects one. msgtype,gossipd_ping,3008 msgdata,gossipd_ping,id,node_id, @@ -116,20 +82,6 @@ msgtype,gossipd_dev_compact_store,3034 msgtype,gossipd_dev_compact_store_reply,3134 msgdata,gossipd_dev_compact_store_reply,success,bool, -#include - -# master -> gossipd: get route_info for our incoming channels -msgtype,gossipd_get_incoming_channels,3025 - -# gossipd -> master: here they are. -msgtype,gossipd_get_incoming_channels_reply,3125 -msgdata,gossipd_get_incoming_channels_reply,num_public,u16, -msgdata,gossipd_get_incoming_channels_reply,public_route_info,route_info,num_public -msgdata,gossipd_get_incoming_channels_reply,public_deadends,bool,num_public -msgdata,gossipd_get_incoming_channels_reply,num_private,u16, -msgdata,gossipd_get_incoming_channels_reply,private_route_info,route_info,num_private -msgdata,gossipd_get_incoming_channels_reply,private_deadends,bool,num_private - # master -> gossipd: blockheight increased. msgtype,gossipd_new_blockheight,3026 msgdata,gossipd_new_blockheight,blockheight,u32, diff --git a/gossipd/gossipd_wiregen.c b/gossipd/gossipd_wiregen.c index 6e86ca01e0e8..b5f6a2b328e0 100644 --- a/gossipd/gossipd_wiregen.c +++ b/gossipd/gossipd_wiregen.c @@ -21,13 +21,8 @@ const char *gossipd_wire_name(int e) switch ((enum gossipd_wire)e) { case WIRE_GOSSIPD_INIT: return "WIRE_GOSSIPD_INIT"; + case WIRE_GOSSIPD_INIT_REPLY: return "WIRE_GOSSIPD_INIT_REPLY"; case WIRE_GOSSIPD_DEV_SET_TIME: return "WIRE_GOSSIPD_DEV_SET_TIME"; - case WIRE_GOSSIPD_GETNODES_REQUEST: return "WIRE_GOSSIPD_GETNODES_REQUEST"; - case WIRE_GOSSIPD_GETNODES_REPLY: return "WIRE_GOSSIPD_GETNODES_REPLY"; - case WIRE_GOSSIPD_GETROUTE_REQUEST: return "WIRE_GOSSIPD_GETROUTE_REQUEST"; - case WIRE_GOSSIPD_GETROUTE_REPLY: return "WIRE_GOSSIPD_GETROUTE_REPLY"; - case WIRE_GOSSIPD_GETCHANNELS_REQUEST: return "WIRE_GOSSIPD_GETCHANNELS_REQUEST"; - case WIRE_GOSSIPD_GETCHANNELS_REPLY: return "WIRE_GOSSIPD_GETCHANNELS_REPLY"; case WIRE_GOSSIPD_PING: return "WIRE_GOSSIPD_PING"; case WIRE_GOSSIPD_PING_REPLY: return "WIRE_GOSSIPD_PING_REPLY"; case WIRE_GOSSIPD_DEV_SET_MAX_SCIDS_ENCODE_SIZE: return "WIRE_GOSSIPD_DEV_SET_MAX_SCIDS_ENCODE_SIZE"; @@ -42,8 +37,6 @@ const char *gossipd_wire_name(int e) case WIRE_GOSSIPD_DEV_MEMLEAK_REPLY: return "WIRE_GOSSIPD_DEV_MEMLEAK_REPLY"; case WIRE_GOSSIPD_DEV_COMPACT_STORE: return "WIRE_GOSSIPD_DEV_COMPACT_STORE"; case WIRE_GOSSIPD_DEV_COMPACT_STORE_REPLY: return "WIRE_GOSSIPD_DEV_COMPACT_STORE_REPLY"; - case WIRE_GOSSIPD_GET_INCOMING_CHANNELS: return "WIRE_GOSSIPD_GET_INCOMING_CHANNELS"; - case WIRE_GOSSIPD_GET_INCOMING_CHANNELS_REPLY: return "WIRE_GOSSIPD_GET_INCOMING_CHANNELS_REPLY"; case WIRE_GOSSIPD_NEW_BLOCKHEIGHT: return "WIRE_GOSSIPD_NEW_BLOCKHEIGHT"; case WIRE_GOSSIPD_GOT_ONIONMSG_TO_US: return "WIRE_GOSSIPD_GOT_ONIONMSG_TO_US"; case WIRE_GOSSIPD_GOT_ONIONMSG_FORWARD: return "WIRE_GOSSIPD_GOT_ONIONMSG_FORWARD"; @@ -60,13 +53,8 @@ bool gossipd_wire_is_defined(u16 type) { switch ((enum gossipd_wire)type) { case WIRE_GOSSIPD_INIT:; + case WIRE_GOSSIPD_INIT_REPLY:; case WIRE_GOSSIPD_DEV_SET_TIME:; - case WIRE_GOSSIPD_GETNODES_REQUEST:; - case WIRE_GOSSIPD_GETNODES_REPLY:; - case WIRE_GOSSIPD_GETROUTE_REQUEST:; - case WIRE_GOSSIPD_GETROUTE_REPLY:; - case WIRE_GOSSIPD_GETCHANNELS_REQUEST:; - case WIRE_GOSSIPD_GETCHANNELS_REPLY:; case WIRE_GOSSIPD_PING:; case WIRE_GOSSIPD_PING_REPLY:; case WIRE_GOSSIPD_DEV_SET_MAX_SCIDS_ENCODE_SIZE:; @@ -81,8 +69,6 @@ bool gossipd_wire_is_defined(u16 type) case WIRE_GOSSIPD_DEV_MEMLEAK_REPLY:; case WIRE_GOSSIPD_DEV_COMPACT_STORE:; case WIRE_GOSSIPD_DEV_COMPACT_STORE_REPLY:; - case WIRE_GOSSIPD_GET_INCOMING_CHANNELS:; - case WIRE_GOSSIPD_GET_INCOMING_CHANNELS_REPLY:; case WIRE_GOSSIPD_NEW_BLOCKHEIGHT:; case WIRE_GOSSIPD_GOT_ONIONMSG_TO_US:; case WIRE_GOSSIPD_GOT_ONIONMSG_FORWARD:; @@ -155,261 +141,44 @@ bool fromwire_gossipd_init(const tal_t *ctx, const void *p, const struct chainpa return cursor != NULL; } -/* WIRE: GOSSIPD_DEV_SET_TIME */ -/* In developer mode */ -u8 *towire_gossipd_dev_set_time(const tal_t *ctx, u32 dev_gossip_time) +/* WIRE: GOSSIPD_INIT_REPLY */ +u8 *towire_gossipd_init_reply(const tal_t *ctx) { u8 *p = tal_arr(ctx, u8, 0); - towire_u16(&p, WIRE_GOSSIPD_DEV_SET_TIME); - towire_u32(&p, dev_gossip_time); + towire_u16(&p, WIRE_GOSSIPD_INIT_REPLY); return memcheck(p, tal_count(p)); } -bool fromwire_gossipd_dev_set_time(const void *p, u32 *dev_gossip_time) +bool fromwire_gossipd_init_reply(const void *p) { const u8 *cursor = p; size_t plen = tal_count(p); - if (fromwire_u16(&cursor, &plen) != WIRE_GOSSIPD_DEV_SET_TIME) + if (fromwire_u16(&cursor, &plen) != WIRE_GOSSIPD_INIT_REPLY) return false; - *dev_gossip_time = fromwire_u32(&cursor, &plen); return cursor != NULL; } -/* WIRE: GOSSIPD_GETNODES_REQUEST */ -/* Pass JSON-RPC getnodes call through */ -u8 *towire_gossipd_getnodes_request(const tal_t *ctx, const struct node_id *id) -{ - u8 *p = tal_arr(ctx, u8, 0); - - towire_u16(&p, WIRE_GOSSIPD_GETNODES_REQUEST); - if (!id) - towire_bool(&p, false); - else { - towire_bool(&p, true); - towire_node_id(&p, id); - } - - return memcheck(p, tal_count(p)); -} -bool fromwire_gossipd_getnodes_request(const tal_t *ctx, const void *p, struct node_id **id) -{ - const u8 *cursor = p; - size_t plen = tal_count(p); - - if (fromwire_u16(&cursor, &plen) != WIRE_GOSSIPD_GETNODES_REQUEST) - return false; - if (!fromwire_bool(&cursor, &plen)) - *id = NULL; - else { - *id = tal(ctx, struct node_id); - fromwire_node_id(&cursor, &plen, *id); - } - return cursor != NULL; -} - -/* WIRE: GOSSIPD_GETNODES_REPLY */ -u8 *towire_gossipd_getnodes_reply(const tal_t *ctx, const struct gossip_getnodes_entry **nodes) -{ - u32 num_nodes = tal_count(nodes); - u8 *p = tal_arr(ctx, u8, 0); - - towire_u16(&p, WIRE_GOSSIPD_GETNODES_REPLY); - towire_u32(&p, num_nodes); - for (size_t i = 0; i < num_nodes; i++) - towire_gossip_getnodes_entry(&p, nodes[i]); - - return memcheck(p, tal_count(p)); -} -bool fromwire_gossipd_getnodes_reply(const tal_t *ctx, const void *p, struct gossip_getnodes_entry ***nodes) -{ - u32 num_nodes; - - const u8 *cursor = p; - size_t plen = tal_count(p); - - if (fromwire_u16(&cursor, &plen) != WIRE_GOSSIPD_GETNODES_REPLY) - return false; - num_nodes = fromwire_u32(&cursor, &plen); - // 2nd case nodes - *nodes = num_nodes ? tal_arr(ctx, struct gossip_getnodes_entry *, num_nodes) : NULL; - for (size_t i = 0; i < num_nodes; i++) - (*nodes)[i] = fromwire_gossip_getnodes_entry(*nodes, &cursor, &plen); - return cursor != NULL; -} - -/* WIRE: GOSSIPD_GETROUTE_REQUEST */ -/* Pass JSON-RPC getroute call through */ -u8 *towire_gossipd_getroute_request(const tal_t *ctx, const struct node_id *source, const struct node_id *destination, struct amount_msat msatoshi, u64 riskfactor_millionths, u32 final_cltv, u64 fuzz_millionths, const struct exclude_entry **excluded, u32 max_hops) -{ - u16 num_excluded = tal_count(excluded); - u8 *p = tal_arr(ctx, u8, 0); - - towire_u16(&p, WIRE_GOSSIPD_GETROUTE_REQUEST); - /* Source defaults to "us" */ - if (!source) - towire_bool(&p, false); - else { - towire_bool(&p, true); - towire_node_id(&p, source); - } - towire_node_id(&p, destination); - towire_amount_msat(&p, msatoshi); - towire_u64(&p, riskfactor_millionths); - towire_u32(&p, final_cltv); - towire_u64(&p, fuzz_millionths); - towire_u16(&p, num_excluded); - for (size_t i = 0; i < num_excluded; i++) - towire_exclude_entry(&p, excluded[i]); - towire_u32(&p, max_hops); - - return memcheck(p, tal_count(p)); -} -bool fromwire_gossipd_getroute_request(const tal_t *ctx, const void *p, struct node_id **source, struct node_id *destination, struct amount_msat *msatoshi, u64 *riskfactor_millionths, u32 *final_cltv, u64 *fuzz_millionths, struct exclude_entry ***excluded, u32 *max_hops) -{ - u16 num_excluded; - - const u8 *cursor = p; - size_t plen = tal_count(p); - - if (fromwire_u16(&cursor, &plen) != WIRE_GOSSIPD_GETROUTE_REQUEST) - return false; - /* Source defaults to "us" */ - if (!fromwire_bool(&cursor, &plen)) - *source = NULL; - else { - *source = tal(ctx, struct node_id); - fromwire_node_id(&cursor, &plen, *source); - } - fromwire_node_id(&cursor, &plen, destination); - *msatoshi = fromwire_amount_msat(&cursor, &plen); - *riskfactor_millionths = fromwire_u64(&cursor, &plen); - *final_cltv = fromwire_u32(&cursor, &plen); - *fuzz_millionths = fromwire_u64(&cursor, &plen); - num_excluded = fromwire_u16(&cursor, &plen); - // 2nd case excluded - *excluded = num_excluded ? tal_arr(ctx, struct exclude_entry *, num_excluded) : NULL; - for (size_t i = 0; i < num_excluded; i++) - (*excluded)[i] = fromwire_exclude_entry(*excluded, &cursor, &plen); - *max_hops = fromwire_u32(&cursor, &plen); - return cursor != NULL; -} - -/* WIRE: GOSSIPD_GETROUTE_REPLY */ -u8 *towire_gossipd_getroute_reply(const tal_t *ctx, const struct route_hop **hops) -{ - u16 num_hops = tal_count(hops); - u8 *p = tal_arr(ctx, u8, 0); - - towire_u16(&p, WIRE_GOSSIPD_GETROUTE_REPLY); - towire_u16(&p, num_hops); - for (size_t i = 0; i < num_hops; i++) - towire_route_hop(&p, hops[i]); - - return memcheck(p, tal_count(p)); -} -bool fromwire_gossipd_getroute_reply(const tal_t *ctx, const void *p, struct route_hop ***hops) -{ - u16 num_hops; - - const u8 *cursor = p; - size_t plen = tal_count(p); - - if (fromwire_u16(&cursor, &plen) != WIRE_GOSSIPD_GETROUTE_REPLY) - return false; - num_hops = fromwire_u16(&cursor, &plen); - // 2nd case hops - *hops = num_hops ? tal_arr(ctx, struct route_hop *, num_hops) : NULL; - for (size_t i = 0; i < num_hops; i++) - (*hops)[i] = fromwire_route_hop(*hops, &cursor, &plen); - return cursor != NULL; -} - -/* WIRE: GOSSIPD_GETCHANNELS_REQUEST */ -u8 *towire_gossipd_getchannels_request(const tal_t *ctx, const struct short_channel_id *short_channel_id, const struct node_id *source, const struct short_channel_id *prev) -{ - u8 *p = tal_arr(ctx, u8, 0); - - towire_u16(&p, WIRE_GOSSIPD_GETCHANNELS_REQUEST); - if (!short_channel_id) - towire_bool(&p, false); - else { - towire_bool(&p, true); - towire_short_channel_id(&p, short_channel_id); - } - if (!source) - towire_bool(&p, false); - else { - towire_bool(&p, true); - towire_node_id(&p, source); - } - if (!prev) - towire_bool(&p, false); - else { - towire_bool(&p, true); - towire_short_channel_id(&p, prev); - } - - return memcheck(p, tal_count(p)); -} -bool fromwire_gossipd_getchannels_request(const tal_t *ctx, const void *p, struct short_channel_id **short_channel_id, struct node_id **source, struct short_channel_id **prev) -{ - const u8 *cursor = p; - size_t plen = tal_count(p); - - if (fromwire_u16(&cursor, &plen) != WIRE_GOSSIPD_GETCHANNELS_REQUEST) - return false; - if (!fromwire_bool(&cursor, &plen)) - *short_channel_id = NULL; - else { - *short_channel_id = tal(ctx, struct short_channel_id); - fromwire_short_channel_id(&cursor, &plen, *short_channel_id); - } - if (!fromwire_bool(&cursor, &plen)) - *source = NULL; - else { - *source = tal(ctx, struct node_id); - fromwire_node_id(&cursor, &plen, *source); - } - if (!fromwire_bool(&cursor, &plen)) - *prev = NULL; - else { - *prev = tal(ctx, struct short_channel_id); - fromwire_short_channel_id(&cursor, &plen, *prev); - } - return cursor != NULL; -} - -/* WIRE: GOSSIPD_GETCHANNELS_REPLY */ -u8 *towire_gossipd_getchannels_reply(const tal_t *ctx, bool complete, const struct gossip_getchannels_entry **nodes) +/* WIRE: GOSSIPD_DEV_SET_TIME */ +/* In developer mode */ +u8 *towire_gossipd_dev_set_time(const tal_t *ctx, u32 dev_gossip_time) { - u32 num_channels = tal_count(nodes); u8 *p = tal_arr(ctx, u8, 0); - towire_u16(&p, WIRE_GOSSIPD_GETCHANNELS_REPLY); - towire_bool(&p, complete); - towire_u32(&p, num_channels); - for (size_t i = 0; i < num_channels; i++) - towire_gossip_getchannels_entry(&p, nodes[i]); + towire_u16(&p, WIRE_GOSSIPD_DEV_SET_TIME); + towire_u32(&p, dev_gossip_time); return memcheck(p, tal_count(p)); } -bool fromwire_gossipd_getchannels_reply(const tal_t *ctx, const void *p, bool *complete, struct gossip_getchannels_entry ***nodes) +bool fromwire_gossipd_dev_set_time(const void *p, u32 *dev_gossip_time) { - u32 num_channels; - const u8 *cursor = p; size_t plen = tal_count(p); - if (fromwire_u16(&cursor, &plen) != WIRE_GOSSIPD_GETCHANNELS_REPLY) + if (fromwire_u16(&cursor, &plen) != WIRE_GOSSIPD_DEV_SET_TIME) return false; - *complete = fromwire_bool(&cursor, &plen); - num_channels = fromwire_u32(&cursor, &plen); - // 2nd case nodes - *nodes = num_channels ? tal_arr(ctx, struct gossip_getchannels_entry *, num_channels) : NULL; - for (size_t i = 0; i < num_channels; i++) - (*nodes)[i] = fromwire_gossip_getchannels_entry(*nodes, &cursor, &plen); + *dev_gossip_time = fromwire_u32(&cursor, &plen); return cursor != NULL; } @@ -742,79 +511,6 @@ bool fromwire_gossipd_dev_compact_store_reply(const void *p, bool *success) return cursor != NULL; } -/* WIRE: GOSSIPD_GET_INCOMING_CHANNELS */ -/* master -> gossipd: get route_info for our incoming channels */ -u8 *towire_gossipd_get_incoming_channels(const tal_t *ctx) -{ - u8 *p = tal_arr(ctx, u8, 0); - - towire_u16(&p, WIRE_GOSSIPD_GET_INCOMING_CHANNELS); - - return memcheck(p, tal_count(p)); -} -bool fromwire_gossipd_get_incoming_channels(const void *p) -{ - const u8 *cursor = p; - size_t plen = tal_count(p); - - if (fromwire_u16(&cursor, &plen) != WIRE_GOSSIPD_GET_INCOMING_CHANNELS) - return false; - return cursor != NULL; -} - -/* WIRE: GOSSIPD_GET_INCOMING_CHANNELS_REPLY */ -/* gossipd -> master: here they are. */ -u8 *towire_gossipd_get_incoming_channels_reply(const tal_t *ctx, const struct route_info *public_route_info, const bool *public_deadends, const struct route_info *private_route_info, const bool *private_deadends) -{ - u16 num_public = tal_count(public_deadends); - u16 num_private = tal_count(private_deadends); - u8 *p = tal_arr(ctx, u8, 0); - - towire_u16(&p, WIRE_GOSSIPD_GET_INCOMING_CHANNELS_REPLY); - towire_u16(&p, num_public); - for (size_t i = 0; i < num_public; i++) - towire_route_info(&p, public_route_info + i); - for (size_t i = 0; i < num_public; i++) - towire_bool(&p, public_deadends[i]); - towire_u16(&p, num_private); - for (size_t i = 0; i < num_private; i++) - towire_route_info(&p, private_route_info + i); - for (size_t i = 0; i < num_private; i++) - towire_bool(&p, private_deadends[i]); - - return memcheck(p, tal_count(p)); -} -bool fromwire_gossipd_get_incoming_channels_reply(const tal_t *ctx, const void *p, struct route_info **public_route_info, bool **public_deadends, struct route_info **private_route_info, bool **private_deadends) -{ - u16 num_public; - u16 num_private; - - const u8 *cursor = p; - size_t plen = tal_count(p); - - if (fromwire_u16(&cursor, &plen) != WIRE_GOSSIPD_GET_INCOMING_CHANNELS_REPLY) - return false; - num_public = fromwire_u16(&cursor, &plen); - // 2nd case public_route_info - *public_route_info = num_public ? tal_arr(ctx, struct route_info, num_public) : NULL; - for (size_t i = 0; i < num_public; i++) - fromwire_route_info(&cursor, &plen, *public_route_info + i); - // 2nd case public_deadends - *public_deadends = num_public ? tal_arr(ctx, bool, num_public) : NULL; - for (size_t i = 0; i < num_public; i++) - (*public_deadends)[i] = fromwire_bool(&cursor, &plen); - num_private = fromwire_u16(&cursor, &plen); - // 2nd case private_route_info - *private_route_info = num_private ? tal_arr(ctx, struct route_info, num_private) : NULL; - for (size_t i = 0; i < num_private; i++) - fromwire_route_info(&cursor, &plen, *private_route_info + i); - // 2nd case private_deadends - *private_deadends = num_private ? tal_arr(ctx, bool, num_private) : NULL; - for (size_t i = 0; i < num_private; i++) - (*private_deadends)[i] = fromwire_bool(&cursor, &plen); - return cursor != NULL; -} - /* WIRE: GOSSIPD_NEW_BLOCKHEIGHT */ /* master -> gossipd: blockheight increased. */ u8 *towire_gossipd_new_blockheight(const tal_t *ctx, u32 blockheight) @@ -1057,4 +753,4 @@ bool fromwire_gossipd_addgossip_reply(const tal_t *ctx, const void *p, wirestrin *err = fromwire_wirestring(ctx, &cursor, &plen); return cursor != NULL; } -// SHA256STAMP:a0d7494995d7f95fb7df295bab9d865e18670f15243116a0aaa9b9548534b922 +// SHA256STAMP:6f6810a7e9c5e3e7dc1dd716b6a8de019516f6e82e3664bdf760647b5a987883 diff --git a/gossipd/gossipd_wiregen.h b/gossipd/gossipd_wiregen.h index 0e989c517672..5e2c5f4a3cd4 100644 --- a/gossipd/gossipd_wiregen.h +++ b/gossipd/gossipd_wiregen.h @@ -11,22 +11,13 @@ #include #include #include -#include -#include enum gossipd_wire { /* Initialize the gossip daemon. */ WIRE_GOSSIPD_INIT = 3000, + WIRE_GOSSIPD_INIT_REPLY = 3100, /* In developer mode */ WIRE_GOSSIPD_DEV_SET_TIME = 3001, - /* Pass JSON-RPC getnodes call through */ - WIRE_GOSSIPD_GETNODES_REQUEST = 3005, - WIRE_GOSSIPD_GETNODES_REPLY = 3105, - /* Pass JSON-RPC getroute call through */ - WIRE_GOSSIPD_GETROUTE_REQUEST = 3006, - WIRE_GOSSIPD_GETROUTE_REPLY = 3106, - WIRE_GOSSIPD_GETCHANNELS_REQUEST = 3007, - WIRE_GOSSIPD_GETCHANNELS_REPLY = 3107, /* Ping/pong test. Waits for a reply if it expects one. */ WIRE_GOSSIPD_PING = 3008, WIRE_GOSSIPD_PING_REPLY = 3108, @@ -52,10 +43,6 @@ enum gossipd_wire { WIRE_GOSSIPD_DEV_COMPACT_STORE = 3034, /* gossipd -> master: ok */ WIRE_GOSSIPD_DEV_COMPACT_STORE_REPLY = 3134, - /* master -> gossipd: get route_info for our incoming channels */ - WIRE_GOSSIPD_GET_INCOMING_CHANNELS = 3025, - /* gossipd -> master: here they are. */ - WIRE_GOSSIPD_GET_INCOMING_CHANNELS_REPLY = 3125, /* master -> gossipd: blockheight increased. */ WIRE_GOSSIPD_NEW_BLOCKHEIGHT = 3026, /* Tell lightningd we got a onion message (for us */ @@ -86,37 +73,15 @@ bool gossipd_wire_is_defined(u16 type); u8 *towire_gossipd_init(const tal_t *ctx, const struct chainparams *chainparams, const struct feature_set *our_features, const struct node_id *id, const u8 rgb[3], const u8 alias[32], const struct wireaddr *announcable, u32 *dev_gossip_time, bool dev_fast_gossip, bool dev_fast_gossip_prune); bool fromwire_gossipd_init(const tal_t *ctx, const void *p, const struct chainparams **chainparams, struct feature_set **our_features, struct node_id *id, u8 rgb[3], u8 alias[32], struct wireaddr **announcable, u32 **dev_gossip_time, bool *dev_fast_gossip, bool *dev_fast_gossip_prune); +/* WIRE: GOSSIPD_INIT_REPLY */ +u8 *towire_gossipd_init_reply(const tal_t *ctx); +bool fromwire_gossipd_init_reply(const void *p); + /* WIRE: GOSSIPD_DEV_SET_TIME */ /* In developer mode */ u8 *towire_gossipd_dev_set_time(const tal_t *ctx, u32 dev_gossip_time); bool fromwire_gossipd_dev_set_time(const void *p, u32 *dev_gossip_time); -/* WIRE: GOSSIPD_GETNODES_REQUEST */ -/* Pass JSON-RPC getnodes call through */ -u8 *towire_gossipd_getnodes_request(const tal_t *ctx, const struct node_id *id); -bool fromwire_gossipd_getnodes_request(const tal_t *ctx, const void *p, struct node_id **id); - -/* WIRE: GOSSIPD_GETNODES_REPLY */ -u8 *towire_gossipd_getnodes_reply(const tal_t *ctx, const struct gossip_getnodes_entry **nodes); -bool fromwire_gossipd_getnodes_reply(const tal_t *ctx, const void *p, struct gossip_getnodes_entry ***nodes); - -/* WIRE: GOSSIPD_GETROUTE_REQUEST */ -/* Pass JSON-RPC getroute call through */ -u8 *towire_gossipd_getroute_request(const tal_t *ctx, const struct node_id *source, const struct node_id *destination, struct amount_msat msatoshi, u64 riskfactor_millionths, u32 final_cltv, u64 fuzz_millionths, const struct exclude_entry **excluded, u32 max_hops); -bool fromwire_gossipd_getroute_request(const tal_t *ctx, const void *p, struct node_id **source, struct node_id *destination, struct amount_msat *msatoshi, u64 *riskfactor_millionths, u32 *final_cltv, u64 *fuzz_millionths, struct exclude_entry ***excluded, u32 *max_hops); - -/* WIRE: GOSSIPD_GETROUTE_REPLY */ -u8 *towire_gossipd_getroute_reply(const tal_t *ctx, const struct route_hop **hops); -bool fromwire_gossipd_getroute_reply(const tal_t *ctx, const void *p, struct route_hop ***hops); - -/* WIRE: GOSSIPD_GETCHANNELS_REQUEST */ -u8 *towire_gossipd_getchannels_request(const tal_t *ctx, const struct short_channel_id *short_channel_id, const struct node_id *source, const struct short_channel_id *prev); -bool fromwire_gossipd_getchannels_request(const tal_t *ctx, const void *p, struct short_channel_id **short_channel_id, struct node_id **source, struct short_channel_id **prev); - -/* WIRE: GOSSIPD_GETCHANNELS_REPLY */ -u8 *towire_gossipd_getchannels_reply(const tal_t *ctx, bool complete, const struct gossip_getchannels_entry **nodes); -bool fromwire_gossipd_getchannels_reply(const tal_t *ctx, const void *p, bool *complete, struct gossip_getchannels_entry ***nodes); - /* WIRE: GOSSIPD_PING */ /* Ping/pong test. Waits for a reply if it expects one. */ u8 *towire_gossipd_ping(const tal_t *ctx, const struct node_id *id, u16 num_pong_bytes, u16 len); @@ -184,16 +149,6 @@ bool fromwire_gossipd_dev_compact_store(const void *p); u8 *towire_gossipd_dev_compact_store_reply(const tal_t *ctx, bool success); bool fromwire_gossipd_dev_compact_store_reply(const void *p, bool *success); -/* WIRE: GOSSIPD_GET_INCOMING_CHANNELS */ -/* master -> gossipd: get route_info for our incoming channels */ -u8 *towire_gossipd_get_incoming_channels(const tal_t *ctx); -bool fromwire_gossipd_get_incoming_channels(const void *p); - -/* WIRE: GOSSIPD_GET_INCOMING_CHANNELS_REPLY */ -/* gossipd -> master: here they are. */ -u8 *towire_gossipd_get_incoming_channels_reply(const tal_t *ctx, const struct route_info *public_route_info, const bool *public_deadends, const struct route_info *private_route_info, const bool *private_deadends); -bool fromwire_gossipd_get_incoming_channels_reply(const tal_t *ctx, const void *p, struct route_info **public_route_info, bool **public_deadends, struct route_info **private_route_info, bool **private_deadends); - /* WIRE: GOSSIPD_NEW_BLOCKHEIGHT */ /* master -> gossipd: blockheight increased. */ u8 *towire_gossipd_new_blockheight(const tal_t *ctx, u32 blockheight); @@ -225,4 +180,4 @@ bool fromwire_gossipd_addgossip_reply(const tal_t *ctx, const void *p, wirestrin #endif /* LIGHTNING_GOSSIPD_GOSSIPD_WIREGEN_H */ -// SHA256STAMP:a0d7494995d7f95fb7df295bab9d865e18670f15243116a0aaa9b9548534b922 +// SHA256STAMP:6f6810a7e9c5e3e7dc1dd716b6a8de019516f6e82e3664bdf760647b5a987883 diff --git a/gossipd/routing.c b/gossipd/routing.c index 321ce14ba1cf..fabb469d6b55 100644 --- a/gossipd/routing.c +++ b/gossipd/routing.c @@ -94,8 +94,6 @@ HTABLE_DEFINE_TYPE(struct pending_node_announce, pending_node_announce_keyof, struct unupdated_channel { /* The channel_announcement message */ const u8 *channel_announce; - /* The feature bitmap within it */ - const u8 *features; /* The short_channel_id */ struct short_channel_id scid; /* The ids of the nodes */ @@ -381,8 +379,6 @@ static struct node *new_node(struct routing_state *rstate, n->id = *id; memset(n->chans.arr, 0, sizeof(n->chans.arr)); broadcastable_init(&n->bcast); - /* We don't know, so assume legacy. */ - n->hop_style = ROUTE_HOP_LEGACY; n->tokens = TOKEN_MAX; node_map_add(rstate->nodes, n); tal_add_destructor2(n, destroy_node, rstate); @@ -522,10 +518,6 @@ static void init_half_chan(struct routing_state *rstate, { struct half_chan *c = &chan->half[channel_idx]; - /* Set the channel direction */ - c->channel_flags = channel_idx; - // TODO: wireup message_flags - c->message_flags = 0; broadcastable_init(&c->bcast); c->tokens = TOKEN_MAX; } @@ -574,8 +566,7 @@ struct chan *new_chan(struct routing_state *rstate, const struct short_channel_id *scid, const struct node_id *id1, const struct node_id *id2, - struct amount_sat satoshis, - const u8 *features) + struct amount_sat satoshis) { struct chan *chan = tal(rstate, struct chan); int n1idx = node_id_idx(id1, id2); @@ -610,8 +601,6 @@ struct chan *new_chan(struct routing_state *rstate, init_half_chan(rstate, chan, n1idx); init_half_chan(rstate, chan, !n1idx); - /* Stash hint here about whether we have features */ - chan->half[0].any_features = tal_bytelen(features) != 0; uintmap_add(&rstate->chanmap, scid->u64, chan); /* Initialize shadow structure if it's local */ @@ -619,755 +608,6 @@ struct chan *new_chan(struct routing_state *rstate, return chan; } -/* Too big to reach, but don't overflow if added. */ -#define INFINITE AMOUNT_MSAT(0x3FFFFFFFFFFFFFFFULL) - -/* We hack a multimap into a uintmap to implement a minheap by cost. - * This is relatively inefficient, containing an array for each cost - * value, assuming there aren't too many at same cost. - * - * We further optimize by never freeing or shrinking these entries, - * but delete by replacing with NULL. This means that we cache the - * lowest index which actually contains something, since others may - * contain empty arrays. */ -struct unvisited { - u64 min_index; - UINTMAP(struct node **) map; -}; - - -/* Risk of passing through this channel. - * - * There are two ways this function is used: - * - * 1. Normally, riskbias = 1. A tiny bias here in order to prefer - * shorter routes, all things equal. - * 2. Trying to find a shorter route, riskbias > 1. By adding an extra - * cost to every hop, we're trying to bias against overlength routes. - */ -static WARN_UNUSED_RESULT bool risk_add_fee(struct amount_msat *risk, - struct amount_msat msat, - u32 delay, double riskfactor, - u64 riskbias) -{ - struct amount_msat riskfee; - - if (!amount_msat_scale(&riskfee, msat, riskfactor * delay)) - return false; - if (!amount_msat_add(&riskfee, riskfee, amount_msat(riskbias))) - return false; - return amount_msat_add(risk, *risk, riskfee); -} - -/* Check that we can fit through this channel's indicated - * maximum_ and minimum_msat requirements. - */ -static bool hc_can_carry(const struct half_chan *hc, - struct amount_msat requiredcap) -{ - return amount_msat_greater_eq(hc->htlc_maximum, requiredcap) && - amount_msat_less_eq(hc->htlc_minimum, requiredcap); -} - -/* Theoretically, this could overflow. */ -static bool fuzz_fee(u64 *fee, - const struct short_channel_id *scid, - double fuzz, const struct siphash_seed *base_seed) -{ - u64 fuzzed_fee, h; - double fee_scale; - - if (fuzz == 0.0) - return true; - - h = siphash24(base_seed, scid, sizeof(*scid)); - - /* Scale fees for this channel */ - /* rand = (h / UINT64_MAX) random number between 0.0 -> 1.0 - * 2*fuzz*rand random number between 0.0 -> 2*fuzz - * 2*fuzz*rand - fuzz random number between -fuzz -> +fuzz - */ - fee_scale = 1.0 + (2.0 * fuzz * h / (double)UINT64_MAX) - fuzz; - fuzzed_fee = *fee * fee_scale; - if (fee_scale > 1.0 && fuzzed_fee < *fee) - return false; - *fee = fuzzed_fee; - return true; -} - -/* Can we carry this amount across the channel? If so, returns true and - * sets newtotal and newrisk */ -static bool can_reach(const struct half_chan *c, - const struct short_channel_id *scid, - bool no_charge, - struct amount_msat total, - struct amount_msat risk, - double riskfactor, - u64 riskbias, - double fuzz, const struct siphash_seed *base_seed, - struct amount_msat *newtotal, struct amount_msat *newrisk) -{ - /* FIXME: Bias against smaller channels. */ - struct amount_msat fee; - - if (!amount_msat_fee(&fee, total, c->base_fee, c->proportional_fee)) - return false; - - if (!fuzz_fee(&fee.millisatoshis, scid, fuzz, base_seed)) /* Raw: double manipulation */ - return false; - - if (no_charge) { - *newtotal = total; - - /* We still want to consider the "charge", since it's indicative - * of a bias (we discounted one channel for a reason), but we - * don't pay it. So we count it as additional risk. */ - if (!amount_msat_add(newrisk, risk, fee)) - return false; - } else { - *newrisk = risk; - - if (!amount_msat_add(newtotal, total, fee)) - return false; - } - - /* Skip a channel if it indicated that it won't route the - * requested amount. */ - if (!hc_can_carry(c, *newtotal)) - return false; - - if (!risk_add_fee(newrisk, *newtotal, c->delay, riskfactor, riskbias)) - return false; - - return true; -} - -/* Returns false on overflow (shouldn't happen!) */ -typedef bool WARN_UNUSED_RESULT costfn_t(struct amount_msat *, - struct amount_msat, - struct amount_msat); - -static WARN_UNUSED_RESULT bool -normal_cost_function(struct amount_msat *cost, - struct amount_msat total, struct amount_msat risk) -{ - if (amount_msat_add(cost, total, risk)) - return true; - - status_broken("Can't add cost of node %s + %s", - type_to_string(tmpctx, struct amount_msat, &total), - type_to_string(tmpctx, struct amount_msat, &risk)); - return false; -} - -static WARN_UNUSED_RESULT bool -shortest_cost_function(struct amount_msat *cost, - struct amount_msat total, struct amount_msat risk) -{ - /* We add 1, so cost is never 0, for our hacky uintmap-as-minheap. */ - if (amount_msat_add(cost, risk, AMOUNT_MSAT(1))) - return true; - - status_broken("Can't add 1 to risk of node %s", - type_to_string(tmpctx, struct amount_msat, &risk)); - return false; -} - -/* Does totala+riska add up to less than totalb+riskb? - * Saves sums if you want them. - */ -static bool costs_less(struct amount_msat totala, - struct amount_msat riska, - struct amount_msat *costa, - struct amount_msat totalb, - struct amount_msat riskb, - struct amount_msat *costb, - costfn_t *costfn) -{ - struct amount_msat suma, sumb; - - if (!costfn(&suma, totala, riska)) - return false; - if (!costfn(&sumb, totalb, riskb)) - return false; - - if (costa) - *costa = suma; - if (costb) - *costb = sumb; - return amount_msat_less(suma, sumb); -} - -/* Determine if the given half_chan is routable */ -static bool hc_is_routable(struct routing_state *rstate, - const struct chan *chan, int idx) -{ - return is_halfchan_enabled(&chan->half[idx]) - && !is_chan_local_disabled(rstate, chan); -} - -static void unvisited_add(struct unvisited *unvisited, struct amount_msat cost, - struct node **arr) -{ - u64 idx = cost.millisatoshis; /* Raw: uintmap needs u64 index */ - if (idx < unvisited->min_index) { - assert(idx); /* We don't allow sending 0 satoshis */ - unvisited->min_index = idx - 1; - } - uintmap_add(&unvisited->map, idx, arr); -} - -static struct node **unvisited_get(const struct unvisited *unvisited, - struct amount_msat cost) -{ - return uintmap_get(&unvisited->map, cost.millisatoshis); /* Raw: uintmap */ -} - -static struct node **unvisited_del(struct unvisited *unvisited, - struct amount_msat cost) -{ - return uintmap_del(&unvisited->map, cost.millisatoshis); /* Raw: uintmap */ -} - -static bool is_unvisited(const struct node *node, - const struct unvisited *unvisited, - costfn_t *costfn) -{ - struct node **arr; - struct amount_msat cost; - - /* If it's infinite, definitely unvisited */ - if (amount_msat_eq(node->dijkstra.total, INFINITE)) - return true; - - /* Shouldn't happen! */ - if (!costfn(&cost, node->dijkstra.total, node->dijkstra.risk)) - return false; - - arr = unvisited_get(unvisited, cost); - for (size_t i = 0; i < tal_count(arr); i++) { - if (arr[i] == node) - return true; - } - return false; -} - -static void unvisited_del_node(struct unvisited *unvisited, - struct amount_msat cost, - const struct node *node) -{ - struct node **arr; - - arr = unvisited_get(unvisited, cost); - for (size_t i = 0; i < tal_count(arr); i++) { - if (arr[i] == node) { - arr[i] = NULL; - return; - } - } - abort(); -} - -static void adjust_unvisited(struct node *node, - struct unvisited *unvisited, - struct amount_msat cost_before, - struct amount_msat total, - struct amount_msat risk, - struct amount_msat cost_after) -{ - struct node **arr; - - /* If it was in unvisited map, remove it. */ - if (!amount_msat_eq(node->dijkstra.total, INFINITE)) - unvisited_del_node(unvisited, cost_before, node); - - /* Update node */ - node->dijkstra.total = total; - node->dijkstra.risk = risk; - - SUPERVERBOSE("%s now cost %s", - type_to_string(tmpctx, struct node_id, &node->id), - type_to_string(tmpctx, struct amount_msat, &cost_after)); - - /* Update map of unvisited nodes */ - arr = unvisited_get(unvisited, cost_after); - if (arr) { - struct node **old_arr; - /* Try for empty slot */ - for (size_t i = 0; i < tal_count(arr); i++) { - if (arr[i] == NULL) { - arr[i] = node; - return; - } - } - /* Nope, expand */ - old_arr = arr; - tal_arr_expand(&arr, node); - if (arr == old_arr) - return; - - /* Realloc moved it; del and add again. */ - unvisited_del(unvisited, cost_after); - } else { - arr = tal_arr(unvisited, struct node *, 1); - arr[0] = node; - } - - unvisited_add(unvisited, cost_after, arr); -} - -static void remove_unvisited(struct node *node, struct unvisited *unvisited, - costfn_t *costfn) -{ - struct amount_msat cost; - - /* Shouldn't happen! */ - if (!costfn(&cost, node->dijkstra.total, node->dijkstra.risk)) - return; - - unvisited_del_node(unvisited, cost, node); -} - -static void update_unvisited_neighbors(struct routing_state *rstate, - struct node *cur, - const struct node *me, - double riskfactor, - u64 riskbias, - double fuzz, - const struct siphash_seed *base_seed, - struct unvisited *unvisited, - costfn_t *costfn) -{ - struct chan_map_iter i; - struct chan *chan; - - /* Consider all neighbors */ - for (chan = first_chan(cur, &i); chan; chan = next_chan(cur, &i)) { - struct amount_msat total, risk, cost_before, cost_after; - int idx = half_chan_to(cur, chan); - struct node *peer = chan->nodes[idx]; - - SUPERVERBOSE("CONSIDERING: %s -> %s (%s/%s)", - type_to_string(tmpctx, struct node_id, - &cur->id), - type_to_string(tmpctx, struct node_id, - &peer->id), - type_to_string(tmpctx, struct amount_msat, - &peer->dijkstra.total), - type_to_string(tmpctx, struct amount_msat, - &peer->dijkstra.risk)); - - if (!hc_is_routable(rstate, chan, idx)) { - SUPERVERBOSE("... not routable"); - continue; - } - - if (!is_unvisited(peer, unvisited, costfn)) { - SUPERVERBOSE("... already visited"); - continue; - } - - /* We're looking at channels *backwards*, so peer == me - * is the right test here for whether we don't charge fees. */ - if (!can_reach(&chan->half[idx], &chan->scid, peer == me, - cur->dijkstra.total, cur->dijkstra.risk, - riskfactor, riskbias, fuzz, base_seed, - &total, &risk)) { - SUPERVERBOSE("... can't reach"); - continue; - } - - /* This effectively adds it to the map if it was infinite */ - if (costs_less(total, risk, &cost_after, - peer->dijkstra.total, peer->dijkstra.risk, - &cost_before, - costfn)) { - SUPERVERBOSE("...%s can reach %s" - " total %s risk %s", - type_to_string(tmpctx, struct node_id, - &cur->id), - type_to_string(tmpctx, struct node_id, - &peer->id), - type_to_string(tmpctx, struct amount_msat, - &total), - type_to_string(tmpctx, struct amount_msat, - &risk)); - adjust_unvisited(peer, unvisited, - cost_before, total, risk, cost_after); - } - } -} - -static struct node *first_unvisited(struct unvisited *unvisited) -{ - struct node **arr; - - while ((arr = uintmap_after(&unvisited->map, &unvisited->min_index))) { - for (size_t i = 0; i < tal_count(arr); i++) { - if (arr[i]) { - unvisited->min_index--; - return arr[i]; - } - } - } - - return NULL; -} - -static void dijkstra(struct routing_state *rstate, - const struct node *dst, - const struct node *me, - double riskfactor, - u64 riskbias, - double fuzz, const struct siphash_seed *base_seed, - struct unvisited *unvisited, - costfn_t *costfn) -{ - struct node *cur; - - while ((cur = first_unvisited(unvisited)) != NULL) { - update_unvisited_neighbors(rstate, cur, me, - riskfactor, riskbias, - fuzz, base_seed, unvisited, costfn); - remove_unvisited(cur, unvisited, costfn); - if (cur == dst) - return; - } -} - -/* Note that we calculated route *backwards*, for fees. So "from" - * here has a high cost, "to" has a cost of exact amount sent. */ -static struct chan **build_route(const tal_t *ctx, - struct routing_state *rstate, - const struct node *from, - const struct node *to, - const struct node *me, - double riskfactor, - u64 riskbias, - double fuzz, - const struct siphash_seed *base_seed, - struct amount_msat *fee) -{ - const struct node *i; - struct chan **route, *chan; - - SUPERVERBOSE("Building route from %s (%s) -> %s (%s)", - type_to_string(tmpctx, struct node_id, &from->id), - type_to_string(tmpctx, struct amount_msat, - &from->dijkstra.total), - type_to_string(tmpctx, struct node_id, &to->id), - type_to_string(tmpctx, struct amount_msat, - &to->dijkstra.total)); - /* Never reached? */ - if (amount_msat_eq(from->dijkstra.total, INFINITE)) - return NULL; - - /* Walk to find which neighbors we used */ - route = tal_arr(ctx, struct chan *, 0); - for (i = from; i != to; i = other_node(i, chan)) { - struct chan_map_iter it; - - /* Consider all neighbors */ - for (chan = first_chan(i, &it); chan; chan = next_chan(i, &it)) { - struct node *peer = other_node(i, chan); - struct half_chan *hc = half_chan_from(i, chan); - struct amount_msat total, risk; - - SUPERVERBOSE("CONSIDER: %s -> %s (%s/%s)", - type_to_string(tmpctx, struct node_id, - &i->id), - type_to_string(tmpctx, struct node_id, - &peer->id), - type_to_string(tmpctx, struct amount_msat, - &peer->dijkstra.total), - type_to_string(tmpctx, struct amount_msat, - &peer->dijkstra.risk)); - - /* If traversing this wasn't possible, ignore */ - if (!hc_is_routable(rstate, chan, !half_chan_to(i, chan))) { - continue; - } - - if (!can_reach(hc, &chan->scid, i == me, - peer->dijkstra.total, peer->dijkstra.risk, - riskfactor, - riskbias, - fuzz, base_seed, - &total, &risk)) - continue; - - /* If this was the path we took, we're done (if there are - * two identical ones, it doesn't matter which) */ - if (amount_msat_eq(total, i->dijkstra.total) - && amount_msat_eq(risk, i->dijkstra.risk)) - break; - } - - if (!chan) { - status_broken("Could not find hop to %s", - type_to_string(tmpctx, struct node_id, - &i->id)); - return tal_free(route); - } - tal_arr_expand(&route, chan); - } - - /* We don't charge ourselves fees, so skip first hop */ - if (!amount_msat_sub(fee, - other_node(from, route[0])->dijkstra.total, - to->dijkstra.total)) { - status_broken("Could not subtract %s - %s for fee", - type_to_string(tmpctx, struct amount_msat, - &other_node(from, route[0]) - ->dijkstra.total), - type_to_string(tmpctx, struct amount_msat, - &to->dijkstra.total)); - return tal_free(route); - } - - return route; -} - -static struct unvisited *dijkstra_prepare(const tal_t *ctx, - struct routing_state *rstate, - struct node *src, - struct amount_msat msat, - costfn_t *costfn) -{ - struct node_map_iter it; - struct unvisited *unvisited; - struct node *n; - struct node **arr; - struct amount_msat cost; - - unvisited = tal(tmpctx, struct unvisited); - uintmap_init(&unvisited->map); - unvisited->min_index = UINT64_MAX; - - /* Reset all the information. */ - for (n = node_map_first(rstate->nodes, &it); - n; - n = node_map_next(rstate->nodes, &it)) { - if (n == src) - continue; - n->dijkstra.total = INFINITE; - n->dijkstra.risk = INFINITE; - } - - /* Mark start cost: place in unvisited map. */ - src->dijkstra.total = msat; - src->dijkstra.risk = AMOUNT_MSAT(0); - arr = tal_arr(unvisited, struct node *, 1); - arr[0] = src; - /* Adding 0 can never fail */ - if (!costfn(&cost, src->dijkstra.total, src->dijkstra.risk)) - abort(); - unvisited_add(unvisited, cost, arr); - - return unvisited; -} - -static void dijkstra_cleanup(struct unvisited *unvisited) -{ - struct node **arr; - u64 idx; - - /* uintmap uses malloc, so manual cleaning needed */ - while ((arr = uintmap_first(&unvisited->map, &idx)) != NULL) { - tal_free(arr); - uintmap_del(&unvisited->map, idx); - } - tal_free(unvisited); -} - -/* We need to start biassing against long routes. */ -static struct chan ** -find_shorter_route(const tal_t *ctx, struct routing_state *rstate, - struct node *src, struct node *dst, - const struct node *me, - struct amount_msat msat, - u32 max_hops, - double fuzz, const struct siphash_seed *base_seed, - struct chan **long_route, - struct amount_msat *fee) -{ - struct unvisited *unvisited; - struct chan **short_route = NULL; - struct amount_msat long_cost, short_cost, cost_diff; - u64 min_bias, max_bias; - double riskfactor; - - /* We traverse backwards, so dst has largest total */ - if (!amount_msat_sub(&long_cost, - dst->dijkstra.total, src->dijkstra.total)) - goto bad_total; - tal_free(long_route); - - /* FIXME: It's hard to juggle both the riskfactor and riskbias here, - * so we set our riskfactor to rougly equate to 1 millisatoshi - * per block delay, which is close enough to zero to not break - * this algorithm, but still provide some bias towards - * low-delay routes. */ - riskfactor = amount_msat_ratio(AMOUNT_MSAT(1), msat); - - /* First, figure out if a short route is even possible. - * We set the cost function to ignore total, riskbias 1 and riskfactor - * ~0 so risk simply operates as a simple hop counter. */ - unvisited = dijkstra_prepare(tmpctx, rstate, src, msat, - shortest_cost_function); - SUPERVERBOSE("Running shortest path from %s -> %s", - type_to_string(tmpctx, struct node_id, &dst->id), - type_to_string(tmpctx, struct node_id, &src->id)); - dijkstra(rstate, dst, NULL, riskfactor, 1, fuzz, base_seed, - unvisited, shortest_cost_function); - dijkstra_cleanup(unvisited); - - /* This will usually succeed, since we found a route before; however - * it's possible that it fails in corner cases. Consider that the reduced - * riskfactor may make us select a more fee-expensive route, which then - * makes us unable to complete the route due to htlc_max restrictions. */ - short_route = build_route(ctx, rstate, dst, src, me, riskfactor, 1, - fuzz, base_seed, fee); - if (!short_route) { - status_info("Could't find short enough route %s->%s", - type_to_string(tmpctx, struct node_id, &dst->id), - type_to_string(tmpctx, struct node_id, &src->id)); - goto out; - } - - if (!amount_msat_sub(&short_cost, - dst->dijkstra.total, src->dijkstra.total)) - goto bad_total; - - /* Still too long? Oh well. */ - if (tal_count(short_route) > max_hops) { - status_info("Minimal possible route %s->%s is %zu", - type_to_string(tmpctx, struct node_id, &dst->id), - type_to_string(tmpctx, struct node_id, &src->id), - tal_count(short_route)); - goto out; - } - - /* OK, so it's possible, just more expensive. */ - min_bias = 0; - - if (!amount_msat_sub(&cost_diff, short_cost, long_cost)) { - status_broken("Short cost %s < long cost %s?", - type_to_string(tmpctx, struct amount_msat, - &short_cost), - type_to_string(tmpctx, struct amount_msat, - &long_cost)); - goto out; - } - - /* This is a gross overestimate, but it works. */ - max_bias = cost_diff.millisatoshis; /* Raw: bias calc */ - - SUPERVERBOSE("maxbias %"PRIu64" gave rlen %zu", - max_bias, tal_count(short_route)); - - /* Now, binary search */ - while (min_bias < max_bias) { - struct chan **route; - struct amount_msat this_fee; - u64 riskbias = (min_bias + max_bias) / 2; - - unvisited = dijkstra_prepare(tmpctx, rstate, src, msat, - normal_cost_function); - dijkstra(rstate, dst, me, riskfactor, riskbias, fuzz, base_seed, - unvisited, normal_cost_function); - dijkstra_cleanup(unvisited); - - route = build_route(ctx, rstate, dst, src, me, - riskfactor, riskbias, - fuzz, base_seed, &this_fee); - - SUPERVERBOSE("riskbias %"PRIu64" rlen %zu", - riskbias, tal_count(route)); - /* Too long still? This is our new min_bias */ - if (tal_count(route) > max_hops) { - tal_free(route); - min_bias = riskbias + 1; - } else { - /* This route is acceptable. */ - tal_free(short_route); - short_route = route; - /* Save this fee in case we exit loop */ - *fee = this_fee; - max_bias = riskbias; - } - } - - return short_route; - -bad_total: - status_broken("dst total %s < src total %s?", - type_to_string(tmpctx, struct amount_msat, - &dst->dijkstra.total), - type_to_string(tmpctx, struct amount_msat, - &src->dijkstra.total)); -out: - tal_free(short_route); - return NULL; -} - -/* riskfactor is already scaled to per-block amount */ -static struct chan ** -find_route(const tal_t *ctx, struct routing_state *rstate, - const struct node_id *from, const struct node_id *to, - struct amount_msat msat, - double riskfactor, - double fuzz, const struct siphash_seed *base_seed, - u32 max_hops, - struct amount_msat *fee) -{ - struct node *src, *dst; - const struct node *me; - struct unvisited *unvisited; - struct chan **route; - - /* Note: we map backwards, since we know the amount of satoshi we want - * at the end, and need to derive how much we need to send. */ - src = get_node(rstate, to); - - /* If from is NULL, that's means it's us. */ - if (!from) - me = dst = get_node(rstate, &rstate->local_id); - else { - dst = get_node(rstate, from); - me = NULL; - } - - if (!src) { - status_info("find_route: cannot find %s", - type_to_string(tmpctx, struct node_id, to)); - return NULL; - } else if (!dst) { - status_info("find_route: cannot find source (%s)", - type_to_string(tmpctx, struct node_id, to)); - return NULL; - } else if (dst == src) { - status_info("find_route: this is %s, refusing to create empty route", - type_to_string(tmpctx, struct node_id, to)); - return NULL; - } - - unvisited = dijkstra_prepare(tmpctx, rstate, src, msat, - normal_cost_function); - dijkstra(rstate, dst, me, riskfactor, 1, fuzz, base_seed, - unvisited, normal_cost_function); - dijkstra_cleanup(unvisited); - - route = build_route(ctx, rstate, dst, src, me, riskfactor, 1, - fuzz, base_seed, fee); - if (tal_count(route) <= max_hops) - return route; - - /* This is the far more unlikely case */ - return find_shorter_route(ctx, rstate, src, dst, me, msat, - max_hops, fuzz, base_seed, route, fee); -} - /* Checks that key is valid, and signed this hash */ static bool check_signed_hash_nodeid(const struct sha256_double *hash, const secp256k1_ecdsa_signature *signature, @@ -1656,7 +896,6 @@ bool routing_add_channel_announcement(struct routing_state *rstate, uc = tal(rstate, struct unupdated_channel); uc->channel_announce = tal_dup_talarr(uc, u8, msg); - uc->features = tal_steal(uc, features); uc->added = gossip_time_now(rstate); uc->index = index; uc->sat = sat; @@ -1990,34 +1229,6 @@ static void update_pending(struct pending_cannouncement *pending, } } -static void set_connection_values(struct chan *chan, - int idx, - u32 base_fee, - u32 proportional_fee, - u32 delay, - u8 message_flags, - u8 channel_flags, - u32 timestamp, - struct amount_msat htlc_minimum, - struct amount_msat htlc_maximum) -{ - struct half_chan *c = &chan->half[idx]; - - c->delay = delay; - c->htlc_minimum = htlc_minimum; - c->htlc_maximum = htlc_maximum; - c->base_fee = base_fee; - c->proportional_fee = proportional_fee; - c->message_flags = message_flags; - c->channel_flags = channel_flags; - c->bcast.timestamp = timestamp; - assert((c->channel_flags & ROUTING_FLAGS_DIRECTION) == idx); - - SUPERVERBOSE("Channel %s/%d was updated.", - type_to_string(tmpctx, struct short_channel_id, &chan->scid), - idx); -} - bool routing_add_channel_update(struct routing_state *rstate, const u8 *update TAKES, u32 index, @@ -2106,7 +1317,7 @@ bool routing_add_channel_update(struct routing_state *rstate, if (uc) { assert(!chan); chan = new_chan(rstate, &short_channel_id, - &uc->id[0], &uc->id[1], sat, uc->features); + &uc->id[0], &uc->id[1], sat); } /* Discard older updates */ @@ -2155,16 +1366,7 @@ bool routing_add_channel_update(struct routing_state *rstate, } } - /* FIXME: https://github.com/lightningnetwork/lightning-rfc/pull/512 - * says we MUST NOT exceed 2^32-1, but c-lightning did, so just trim - * rather than rejecting. */ - if (amount_msat_greater(htlc_maximum, chainparams->max_payment)) - htlc_maximum = chainparams->max_payment; - - set_connection_values(chan, direction, fee_base_msat, - fee_proportional_millionths, expiry, - message_flags, channel_flags, - timestamp, htlc_minimum, htlc_maximum); + chan->half[direction].bcast.timestamp = timestamp; /* Safe even if was never added, but if it's a private channel it * would be a WIRE_GOSSIP_STORE_PRIVATE_UPDATE. */ @@ -2383,37 +1585,6 @@ u8 *handle_channel_update(struct routing_state *rstate, const u8 *update TAKES, return NULL; } -struct wireaddr *read_addresses(const tal_t *ctx, const u8 *ser) -{ - const u8 *cursor = ser; - size_t len = tal_count(ser); - struct wireaddr *wireaddrs = tal_arr(ctx, struct wireaddr, 0); - - while (cursor && len) { - struct wireaddr wireaddr; - - /* BOLT #7: - * - * The receiving node: - *... - * - SHOULD ignore the first `address descriptor` that does - * NOT match the types defined above. - */ - if (!fromwire_wireaddr(&cursor, &len, &wireaddr)) { - if (!cursor) - /* Parsing address failed */ - return tal_free(wireaddrs); - /* Unknown type, stop there. */ - status_debug("read_addresses: unknown address type %u", - cursor[0]); - break; - } - - tal_arr_expand(&wireaddrs, wireaddr); - } - return wireaddrs; -} - bool routing_add_node_announcement(struct routing_state *rstate, const u8 *msg TAKES, u32 index, @@ -2531,12 +1702,6 @@ bool routing_add_node_announcement(struct routing_state *rstate, && node->bcast.timestamp < time_now().ts.tv_sec) rstate->last_timestamp = node->bcast.timestamp; - if (feature_offered(features, OPT_VAR_ONION)) - node->hop_style = ROUTE_HOP_TLV; - else - /* Reset it in case they no longer offer the feature */ - node->hop_style = ROUTE_HOP_LEGACY; - if (index) node->bcast.index = index; else { @@ -2619,7 +1784,7 @@ u8 *handle_node_announcement(struct routing_state *rstate, const u8 *node_ann, return err; } - wireaddrs = read_addresses(tmpctx, addresses); + wireaddrs = fromwire_wireaddr_array(tmpctx, addresses); if (!wireaddrs) { /* BOLT #7: * @@ -2639,131 +1804,6 @@ u8 *handle_node_announcement(struct routing_state *rstate, const u8 *node_ann, return NULL; } -struct route_hop **get_route(const tal_t *ctx, struct routing_state *rstate, - const struct node_id *source, - const struct node_id *destination, - struct amount_msat msat, double riskfactor, - u32 final_cltv, - double fuzz, u64 seed, - struct exclude_entry **excluded, - u32 max_hops) -{ - struct chan **route; - struct amount_msat total_amount; - unsigned int total_delay; - struct amount_msat fee; - struct route_hop **hops; - struct node *n; - struct amount_msat *saved_capacity; - struct short_channel_id_dir *excluded_chan; - struct siphash_seed base_seed; - - saved_capacity = tal_arr(tmpctx, struct amount_msat, 0); - excluded_chan = tal_arr(tmpctx, struct short_channel_id_dir, 0); - - base_seed.u.u64[0] = base_seed.u.u64[1] = seed; - - if (amount_msat_eq(msat, AMOUNT_MSAT(0))) - return NULL; - - /* Temporarily set the capacity of the excluded channels and the incoming channels - * of excluded nodes to zero. */ - for (size_t i = 0; i < tal_count(excluded); i++) { - if (excluded[i]->type == EXCLUDE_CHANNEL) { - struct short_channel_id_dir *chan_id = &excluded[i]->u.chan_id; - struct chan *chan = get_channel(rstate, &chan_id->scid); - if (!chan) - continue; - tal_arr_expand(&saved_capacity, chan->half[chan_id->dir].htlc_maximum); - tal_arr_expand(&excluded_chan, *chan_id); - chan->half[chan_id->dir].htlc_maximum = AMOUNT_MSAT(0); - } else { - assert(excluded[i]->type == EXCLUDE_NODE); - - struct node *node = get_node(rstate, &excluded[i]->u.node_id); - if (!node) - continue; - - struct chan_map_iter i; - struct chan *chan; - for (chan = first_chan(node, &i); chan; chan = next_chan(node, &i)) { - int dir = half_chan_to(node, chan); - tal_arr_expand(&saved_capacity, chan->half[dir].htlc_maximum); - - struct short_channel_id_dir id; - id.scid = chan->scid; - id.dir = dir; - tal_arr_expand(&excluded_chan, id); - - chan->half[dir].htlc_maximum = AMOUNT_MSAT(0); - } - } - } - - route = find_route(ctx, rstate, source, destination, msat, - riskfactor / BLOCKS_PER_YEAR / 100, - fuzz, &base_seed, max_hops, &fee); - - /* Now restore the capacity. */ - /* Restoring is done in reverse order, in order to properly - * handle the case where a channel is indicated twice in - * our input. - * Entries in `saved_capacity` of that channel beyond the - * first entry will be 0, only the first entry of that - * channel will be the correct capacity. - * By restoring in reverse order we ensure we can restore - * the correct capacity. - */ - for (ssize_t i = tal_count(excluded_chan) - 1; i >= 0; i--) { - struct chan *chan = get_channel(rstate, &excluded_chan[i].scid); - if (!chan) - continue; - chan->half[excluded_chan[i].dir].htlc_maximum = saved_capacity[i]; - } - - if (!route) { - return NULL; - } - - /* Fees, delays need to be calculated backwards along route. */ - hops = tal_arr(ctx, struct route_hop *, tal_count(route)); - total_amount = msat; - total_delay = final_cltv; - - /* Start at destination node. */ - n = get_node(rstate, destination); - for (int i = tal_count(route) - 1; i >= 0; i--) { - const struct half_chan *c; - - int idx = half_chan_to(n, route[i]); - c = &route[i]->half[idx]; - hops[i] = tal(hops, struct route_hop); - hops[i]->scid = route[i]->scid; - hops[i]->node_id = n->id; - hops[i]->amount = total_amount; - hops[i]->delay = total_delay; - hops[i]->direction = idx; - hops[i]->style = n->hop_style; - hops[i]->blinding = NULL; - hops[i]->enctlv = NULL; - - /* Since we calculated this route, it should not overflow! */ - if (!amount_msat_add_fee(&total_amount, - c->base_fee, c->proportional_fee)) { - status_broken("Route overflow step %i: %s + %u/%u!?", - i, type_to_string(tmpctx, struct amount_msat, - &total_amount), - c->base_fee, c->proportional_fee); - return tal_free(hops); - } - total_delay += c->delay; - n = other_node(n, route[i]); - } - assert(node_id_eq(&n->id, source ? source : &rstate->local_id)); - - return hops; -} - void route_prune(struct routing_state *rstate) { u64 now = gossip_time_now(rstate).ts.tv_sec; @@ -2865,8 +1905,7 @@ bool routing_add_private_channel(struct routing_state *rstate, type_to_string(tmpctx, struct short_channel_id, &scid)); /* Create new (unannounced) channel */ - chan = new_chan(rstate, &scid, &node_id[0], &node_id[1], sat, - features); + chan = new_chan(rstate, &scid, &node_id[0], &node_id[1], sat); if (!index) index = gossip_store_add(rstate->gs, msg, 0, false, NULL); chan->bcast.index = index; diff --git a/gossipd/routing.h b/gossipd/routing.h index 367054108d51..a0ec756f09fe 100644 --- a/gossipd/routing.h +++ b/gossipd/routing.h @@ -20,34 +20,11 @@ struct peer; struct routing_state; struct half_chan { - /* millisatoshi. */ - u32 base_fee; - /* millionths */ - u32 proportional_fee; - - /* Delay for HTLC in blocks.*/ - u32 delay; - /* Timestamp and index into store file */ struct broadcastable bcast; - /* Flags as specified by the `channel_update`s, among other - * things indicated direction wrt the `channel_id` */ - u8 channel_flags; - - /* Flags as specified by the `channel_update`s, indicates - * optional fields. */ - u8 message_flags; - /* Token bucket */ u8 tokens; - - /* Feature cache for parent chan: squeezed in here where it would - * otherwise simply be padding. */ - u8 any_features; - - /* Minimum and maximum number of msatoshi in an HTLC */ - struct amount_msat htlc_minimum, htlc_maximum; }; struct chan { @@ -96,11 +73,6 @@ static inline bool is_halfchan_defined(const struct half_chan *hc) return hc->bcast.index != 0; } -static inline bool is_halfchan_enabled(const struct half_chan *hc) -{ - return is_halfchan_defined(hc) && !(hc->channel_flags & ROUTING_FLAGS_DISABLED); -} - /* Container for per-node channel pointers. Better cache performance * than uintmap, and we don't need ordering. */ static inline const struct short_channel_id *chan_map_scid(const struct chan *c) @@ -152,22 +124,11 @@ struct node { /* Token bucket */ u8 tokens; - /* route_hop_style */ - enum route_hop_style hop_style; - /* Channels connecting us to other nodes */ union { struct chan_map map; struct chan *arr[NUM_IMMEDIATE_CHANS+1]; } chans; - - /* Temporary data for routefinding. */ - struct { - /* Total to get to here from target. */ - struct amount_msat total; - /* Total risk premium of this route. */ - struct amount_msat risk; - } dijkstra; }; const struct node_id *node_map_keyof_node(const struct node *n); @@ -319,19 +280,6 @@ get_channel(const struct routing_state *rstate, return uintmap_get(&rstate->chanmap, scid->u64); } -enum exclude_entry_type { - EXCLUDE_CHANNEL = 1, - EXCLUDE_NODE = 2 -}; - -struct exclude_entry { - enum exclude_entry_type type; - union { - struct short_channel_id_dir chan_id; - struct node_id node_id; - } u; -}; - struct routing_state *new_routing_state(const tal_t *ctx, const struct node_id *local_id, struct list_head *peers, @@ -350,8 +298,7 @@ struct chan *new_chan(struct routing_state *rstate, const struct short_channel_id *scid, const struct node_id *id1, const struct node_id *id2, - struct amount_sat sat, - const u8 *features); + struct amount_sat sat); /* Handlers for incoming messages */ @@ -400,17 +347,6 @@ u8 *handle_node_announcement(struct routing_state *rstate, const u8 *node, struct node *get_node(struct routing_state *rstate, const struct node_id *id); -/* Compute a route to a destination, for a given amount and riskfactor. */ -struct route_hop **get_route(const tal_t *ctx, struct routing_state *rstate, - const struct node_id *source, - const struct node_id *destination, - const struct amount_msat msat, double riskfactor, - u32 final_cltv, - double fuzz, - u64 seed, - struct exclude_entry **excluded, - u32 max_hops); - void route_prune(struct routing_state *rstate); /** @@ -511,9 +447,6 @@ static inline void local_enable_chan(struct routing_state *rstate, local_chan->local_disabled = false; } -/* Helper to convert on-wire addresses format to wireaddrs array */ -struct wireaddr *read_addresses(const tal_t *ctx, const u8 *ser); - /* Remove channel from store: announcement and any updates. */ void remove_channel_from_store(struct routing_state *rstate, struct chan *chan); diff --git a/gossipd/test/run-bench-find_route.c b/gossipd/test/run-bench-find_route.c deleted file mode 100644 index 0772b7d2348f..000000000000 --- a/gossipd/test/run-bench-find_route.c +++ /dev/null @@ -1,279 +0,0 @@ -#include "config.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "../routing.c" -#include "../gossip_store.c" - -void status_fmt(enum log_level level UNUSED, - const struct node_id *node_id, - const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - vprintf(fmt, ap); - printf("\n"); - va_end(ap); -} - -/* AUTOGENERATED MOCKS START */ -/* Generated stub for cupdate_different */ -bool cupdate_different(struct gossip_store *gs UNNEEDED, - const struct half_chan *hc UNNEEDED, - const u8 *cupdate UNNEEDED) -{ fprintf(stderr, "cupdate_different called!\n"); abort(); } -/* Generated stub for fmt_wireaddr_without_port */ -char *fmt_wireaddr_without_port(const tal_t *ctx UNNEEDED, const struct wireaddr *a UNNEEDED) -{ fprintf(stderr, "fmt_wireaddr_without_port called!\n"); abort(); } -/* Generated stub for fromwire_wireaddr */ -bool fromwire_wireaddr(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct wireaddr *addr UNNEEDED) -{ fprintf(stderr, "fromwire_wireaddr called!\n"); abort(); } -/* Generated stub for json_add_member */ -void json_add_member(struct json_stream *js UNNEEDED, - const char *fieldname UNNEEDED, - bool quote UNNEEDED, - const char *fmt UNNEEDED, ...) -{ fprintf(stderr, "json_add_member called!\n"); abort(); } -/* Generated stub for json_member_direct */ -char *json_member_direct(struct json_stream *js UNNEEDED, - const char *fieldname UNNEEDED, size_t extra UNNEEDED) -{ fprintf(stderr, "json_member_direct called!\n"); abort(); } -/* Generated stub for json_object_end */ -void json_object_end(struct json_stream *js UNNEEDED) -{ fprintf(stderr, "json_object_end called!\n"); abort(); } -/* Generated stub for json_object_start */ -void json_object_start(struct json_stream *ks UNNEEDED, const char *fieldname UNNEEDED) -{ fprintf(stderr, "json_object_start called!\n"); abort(); } -/* Generated stub for memleak_add_helper_ */ -void memleak_add_helper_(const tal_t *p UNNEEDED, void (*cb)(struct htable *memtable UNNEEDED, - const tal_t *)){ } -/* Generated stub for nannounce_different */ -bool nannounce_different(struct gossip_store *gs UNNEEDED, - const struct node *node UNNEEDED, - const u8 *nannounce UNNEEDED) -{ fprintf(stderr, "nannounce_different called!\n"); abort(); } -/* Generated stub for notleak_ */ -void *notleak_(const void *ptr UNNEEDED, bool plus_children UNNEEDED) -{ fprintf(stderr, "notleak_ called!\n"); abort(); } -/* Generated stub for peer_supplied_good_gossip */ -void peer_supplied_good_gossip(struct peer *peer UNNEEDED, size_t amount UNNEEDED) -{ fprintf(stderr, "peer_supplied_good_gossip called!\n"); abort(); } -/* Generated stub for private_channel_announcement */ -const u8 *private_channel_announcement(const tal_t *ctx UNNEEDED, - const struct short_channel_id *scid UNNEEDED, - const struct node_id *local_node_id UNNEEDED, - const struct node_id *remote_node_id UNNEEDED, - const u8 *features UNNEEDED) -{ fprintf(stderr, "private_channel_announcement called!\n"); abort(); } -/* Generated stub for sanitize_error */ -char *sanitize_error(const tal_t *ctx UNNEEDED, const u8 *errmsg UNNEEDED, - struct channel_id *channel_id UNNEEDED) -{ fprintf(stderr, "sanitize_error called!\n"); abort(); } -/* Generated stub for status_failed */ -void status_failed(enum status_failreason code UNNEEDED, - const char *fmt UNNEEDED, ...) -{ fprintf(stderr, "status_failed called!\n"); abort(); } -/* Generated stub for towire_warningfmt */ -u8 *towire_warningfmt(const tal_t *ctx UNNEEDED, - const struct channel_id *channel UNNEEDED, - const char *fmt UNNEEDED, ...) -{ fprintf(stderr, "towire_warningfmt called!\n"); abort(); } -/* AUTOGENERATED MOCKS END */ - -#if DEVELOPER -/* Generated stub for memleak_remove_htable */ -void memleak_remove_htable(struct htable *memtable UNNEEDED, const struct htable *ht UNNEEDED) -{ fprintf(stderr, "memleak_remove_htable called!\n"); abort(); } -/* Generated stub for memleak_remove_intmap_ */ -void memleak_remove_intmap_(struct htable *memtable UNNEEDED, const struct intmap *m UNNEEDED) -{ fprintf(stderr, "memleak_remove_intmap_ called!\n"); abort(); } -#endif - -/* NOOP 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) -{ - return NULL; -} - -/* Updates existing route if required. */ -static void add_connection(struct routing_state *rstate, - const struct node_id *nodes, - u32 from, u32 to, - u32 base_fee, s32 proportional_fee, - u32 delay) -{ - struct short_channel_id scid; - struct half_chan *c; - struct chan *chan; - int idx = node_id_idx(&nodes[from], &nodes[to]); - - /* Encode src and dst in scid. */ - memcpy((char *)&scid + idx * sizeof(from), &from, sizeof(from)); - memcpy((char *)&scid + (!idx) * sizeof(to), &to, sizeof(to)); - - chan = get_channel(rstate, &scid); - if (!chan) { - chan = new_chan(rstate, &scid, &nodes[from], &nodes[to], - AMOUNT_SAT(1000000), NULL); - } - - c = &chan->half[idx]; - c->base_fee = base_fee; - c->proportional_fee = proportional_fee; - c->delay = delay; - c->channel_flags = node_id_idx(&nodes[from], &nodes[to]); - /* This must be non-zero, otherwise we consider it disabled! */ - c->bcast.index = 1; - c->htlc_maximum = AMOUNT_MSAT(-1ULL); - c->htlc_minimum = AMOUNT_MSAT(0); -} - -static struct node_id nodeid(size_t n) -{ - struct node_id id; - struct pubkey k; - struct secret s; - - memset(&s, 0xFF, sizeof(s)); - memcpy(&s, &n, sizeof(n)); - pubkey_from_secret(&s, &k); - node_id_from_pubkey(&id, &k); - return id; -} - -static void populate_random_node(struct routing_state *rstate, - const struct node_id *nodes, - u32 n) -{ - /* Create 2 random channels. */ - if (n < 1) - return; - - for (size_t i = 0; i < 2; i++) { - u32 randnode = pseudorand(n); - - add_connection(rstate, nodes, n, randnode, - pseudorand(1000), - pseudorand(1000), - pseudorand(144)); - add_connection(rstate, nodes, randnode, n, - pseudorand(1000), - pseudorand(1000), - pseudorand(144)); - } -} - -static void run(const char *name) -{ - int status; - - switch (fork()) { - case 0: - execlp(name, name, NULL); - exit(127); - case -1: - err(1, "forking %s", name); - default: - wait(&status); - if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) - errx(1, "%s failed", name); - } -} - -int main(int argc, char *argv[]) -{ - struct routing_state *rstate; - size_t num_nodes = 100, num_runs = 1; - struct timemono start, end; - size_t route_lengths[ROUTING_MAX_HOPS+1]; - struct node_id me; - struct node_id *nodes; - bool perfme = false; - const double riskfactor = 0.01 / BLOCKS_PER_YEAR / 10000; - struct siphash_seed base_seed; - - common_setup(argv[0]); - - me = nodeid(0); - rstate = new_routing_state(tmpctx, &me, NULL, NULL, NULL, - false, false); - opt_register_noarg("--perfme", opt_set_bool, &perfme, - "Run perfme-start and perfme-stop around benchmark"); - - opt_parse(&argc, argv, opt_log_stderr_exit); - - if (argc > 1) - num_nodes = atoi(argv[1]); - if (argc > 2) - num_runs = atoi(argv[2]); - if (argc > 3) - opt_usage_and_exit("[num_nodes [num_runs]]"); - - printf("Creating nodes...\n"); - nodes = tal_arr(rstate, struct node_id, num_nodes); - for (size_t i = 0; i < num_nodes; i++) - nodes[i] = nodeid(i); - - printf("Populating nodes...\n"); - memset(&base_seed, 0, sizeof(base_seed)); - for (size_t i = 0; i < num_nodes; i++) - populate_random_node(rstate, nodes, i); - - if (perfme) - run("perfme-start"); - - printf("Starting...\n"); - memset(route_lengths, 0, sizeof(route_lengths)); - start = time_mono(); - for (size_t i = 0; i < num_runs; i++) { - const struct node_id *from = &nodes[pseudorand(num_nodes)]; - const struct node_id *to = &nodes[pseudorand(num_nodes)]; - struct amount_msat fee; - struct chan **route; - size_t num_hops; - - route = find_route(tmpctx, rstate, from, to, - (struct amount_msat){pseudorand(100000)}, - riskfactor, - 0.75, &base_seed, - ROUTING_MAX_HOPS, - &fee); - num_hops = tal_count(route); - assert(num_hops < ARRAY_SIZE(route_lengths)); - route_lengths[num_hops]++; - tal_free(route); - } - end = time_mono(); - - if (perfme) - run("perfme-stop"); - - printf("%zu (%zu succeeded) routes in %zu nodes in %"PRIu64" msec (%"PRIu64" nanoseconds per route)\n", - num_runs, num_runs - route_lengths[0], num_nodes, - time_to_msec(timemono_between(end, start)), - time_to_nsec(time_divide(timemono_between(end, start), num_runs))); - for (size_t i = 0; i < ARRAY_SIZE(route_lengths); i++) - if (route_lengths[i]) - printf(" Length %zu: %zu\n", i, route_lengths[i]); - - common_shutdown(); - opt_free_table(); - return 0; -} diff --git a/gossipd/test/run-check_channel_announcement.c b/gossipd/test/run-check_channel_announcement.c index cc77a3414819..caaea2ca35d3 100644 --- a/gossipd/test/run-check_channel_announcement.c +++ b/gossipd/test/run-check_channel_announcement.c @@ -42,9 +42,9 @@ bool cupdate_different(struct gossip_store *gs UNNEEDED, /* Generated stub for fmt_wireaddr_without_port */ char *fmt_wireaddr_without_port(const tal_t *ctx UNNEEDED, const struct wireaddr *a UNNEEDED) { fprintf(stderr, "fmt_wireaddr_without_port called!\n"); abort(); } -/* Generated stub for fromwire_wireaddr */ -bool fromwire_wireaddr(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct wireaddr *addr UNNEEDED) -{ fprintf(stderr, "fromwire_wireaddr called!\n"); abort(); } +/* Generated stub for fromwire_wireaddr_array */ +struct wireaddr *fromwire_wireaddr_array(const tal_t *ctx UNNEEDED, const u8 *ser UNNEEDED) +{ fprintf(stderr, "fromwire_wireaddr_array called!\n"); abort(); } /* Generated stub for gossip_store_add */ u64 gossip_store_add(struct gossip_store *gs UNNEEDED, const u8 *gossip_msg UNNEEDED, u32 timestamp UNNEEDED, bool push UNNEEDED, const u8 *addendum UNNEEDED) diff --git a/gossipd/test/run-find_route-specific.c b/gossipd/test/run-find_route-specific.c deleted file mode 100644 index 64060092c2cb..000000000000 --- a/gossipd/test/run-find_route-specific.c +++ /dev/null @@ -1,256 +0,0 @@ -/* We can't seem to route the following: - * - * Expect route 03c173897878996287a8100469f954dd820fcd8941daed91c327f168f3329be0bf -> 0230ad0e74ea03976b28fda587bb75bdd357a1938af4424156a18265167f5e40ae -> 02ea622d5c8d6143f15ed3ce1d501dd0d3d09d3b1c83a44d0034949f8a9ab60f06 - * - * getchannels: - * {'channels': [{'active': True, 'short_id': '6990x2x1/1', 'fee_per_kw': 10, 'delay': 5, 'message_flags': 0, 'channel_flags': 1, 'destination': '0230ad0e74ea03976b28fda587bb75bdd357a1938af4424156a18265167f5e40ae', 'source': '02ea622d5c8d6143f15ed3ce1d501dd0d3d09d3b1c83a44d0034949f8a9ab60f06', 'last_update': 1504064344}, {'active': True, 'short_id': '6989x2x1/0', 'fee_per_kw': 10, 'delay': 5, 'message_flags': 0, 'channel_flags': 0, 'destination': '03c173897878996287a8100469f954dd820fcd8941daed91c327f168f3329be0bf', 'source': '0230ad0e74ea03976b28fda587bb75bdd357a1938af4424156a18265167f5e40ae', 'last_update': 1504064344}, {'active': True, 'short_id': '6990x2x1/0', 'fee_per_kw': 10, 'delay': 5, 'message_flags': 0, 'channel_flags': 0, 'destination': '02ea622d5c8d6143f15ed3ce1d501dd0d3d09d3b1c83a44d0034949f8a9ab60f06', 'source': '0230ad0e74ea03976b28fda587bb75bdd357a1938af4424156a18265167f5e40ae', 'last_update': 1504064344}, {'active': True, 'short_id': '6989x2x1/1', 'fee_per_kw': 10, 'delay': 5, 'message_flags': 0, 'channel_flags': 1, 'destination': '0230ad0e74ea03976b28fda587bb75bdd357a1938af4424156a18265167f5e40ae', 'source': '03c173897878996287a8100469f954dd820fcd8941daed91c327f168f3329be0bf', 'last_update': 1504064344}]} - */ -#include -#include -#include - -#include -#define status_fmt(level, node_id, fmt, ...) \ - do { (void)node_id; printf((fmt) ,##__VA_ARGS__); printf("\n"); } while(0) - -#include "../routing.c" -#include "../gossip_store.c" - -/* AUTOGENERATED MOCKS START */ -/* Generated stub for cupdate_different */ -bool cupdate_different(struct gossip_store *gs UNNEEDED, - const struct half_chan *hc UNNEEDED, - const u8 *cupdate UNNEEDED) -{ fprintf(stderr, "cupdate_different called!\n"); abort(); } -/* Generated stub for fmt_wireaddr_without_port */ -char *fmt_wireaddr_without_port(const tal_t *ctx UNNEEDED, const struct wireaddr *a UNNEEDED) -{ fprintf(stderr, "fmt_wireaddr_without_port called!\n"); abort(); } -/* Generated stub for fromwire_wireaddr */ -bool fromwire_wireaddr(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct wireaddr *addr UNNEEDED) -{ fprintf(stderr, "fromwire_wireaddr called!\n"); abort(); } -/* Generated stub for json_add_member */ -void json_add_member(struct json_stream *js UNNEEDED, - const char *fieldname UNNEEDED, - bool quote UNNEEDED, - const char *fmt UNNEEDED, ...) -{ fprintf(stderr, "json_add_member called!\n"); abort(); } -/* Generated stub for json_member_direct */ -char *json_member_direct(struct json_stream *js UNNEEDED, - const char *fieldname UNNEEDED, size_t extra UNNEEDED) -{ fprintf(stderr, "json_member_direct called!\n"); abort(); } -/* Generated stub for json_object_end */ -void json_object_end(struct json_stream *js UNNEEDED) -{ fprintf(stderr, "json_object_end called!\n"); abort(); } -/* Generated stub for json_object_start */ -void json_object_start(struct json_stream *ks UNNEEDED, const char *fieldname UNNEEDED) -{ fprintf(stderr, "json_object_start called!\n"); abort(); } -/* Generated stub for memleak_add_helper_ */ -void memleak_add_helper_(const tal_t *p UNNEEDED, void (*cb)(struct htable *memtable UNNEEDED, - const tal_t *)){ } -/* Generated stub for nannounce_different */ -bool nannounce_different(struct gossip_store *gs UNNEEDED, - const struct node *node UNNEEDED, - const u8 *nannounce UNNEEDED) -{ fprintf(stderr, "nannounce_different called!\n"); abort(); } -/* Generated stub for notleak_ */ -void *notleak_(const void *ptr UNNEEDED, bool plus_children UNNEEDED) -{ fprintf(stderr, "notleak_ called!\n"); abort(); } -/* Generated stub for peer_supplied_good_gossip */ -void peer_supplied_good_gossip(struct peer *peer UNNEEDED, size_t amount UNNEEDED) -{ fprintf(stderr, "peer_supplied_good_gossip called!\n"); abort(); } -/* Generated stub for private_channel_announcement */ -const u8 *private_channel_announcement(const tal_t *ctx UNNEEDED, - const struct short_channel_id *scid UNNEEDED, - const struct node_id *local_node_id UNNEEDED, - const struct node_id *remote_node_id UNNEEDED, - const u8 *features UNNEEDED) -{ fprintf(stderr, "private_channel_announcement called!\n"); abort(); } -/* Generated stub for sanitize_error */ -char *sanitize_error(const tal_t *ctx UNNEEDED, const u8 *errmsg UNNEEDED, - struct channel_id *channel_id UNNEEDED) -{ fprintf(stderr, "sanitize_error called!\n"); abort(); } -/* Generated stub for status_failed */ -void status_failed(enum status_failreason code UNNEEDED, - const char *fmt UNNEEDED, ...) -{ fprintf(stderr, "status_failed called!\n"); abort(); } -/* Generated stub for towire_warningfmt */ -u8 *towire_warningfmt(const tal_t *ctx UNNEEDED, - const struct channel_id *channel UNNEEDED, - const char *fmt UNNEEDED, ...) -{ fprintf(stderr, "towire_warningfmt called!\n"); abort(); } -/* AUTOGENERATED MOCKS END */ - -#if DEVELOPER -/* Generated stub for memleak_remove_htable */ -void memleak_remove_htable(struct htable *memtable UNNEEDED, const struct htable *ht UNNEEDED) -{ fprintf(stderr, "memleak_remove_htable called!\n"); abort(); } -/* Generated stub for memleak_remove_intmap_ */ -void memleak_remove_intmap_(struct htable *memtable UNNEEDED, const struct intmap *m UNNEEDED) -{ fprintf(stderr, "memleak_remove_intmap_ called!\n"); abort(); } -#endif - -/* NOOP 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) -{ - return NULL; -} - -const void *trc; - -static struct half_chan * -get_or_make_connection(struct routing_state *rstate, - const struct node_id *from_id, - const struct node_id *to_id, - const char *shortid, - struct amount_sat satoshis) -{ - struct short_channel_id scid; - struct chan *chan; - const int idx = node_id_idx(from_id, to_id); - - if (!short_channel_id_from_str(shortid, strlen(shortid), &scid)) - abort(); - chan = get_channel(rstate, &scid); - if (!chan) - chan = new_chan(rstate, &scid, from_id, to_id, satoshis, NULL); - - /* Make sure it's seen as initialized (index non-zero). */ - chan->half[idx].bcast.index = 1; - chan->half[idx].htlc_minimum = AMOUNT_MSAT(0); - if (!amount_sat_to_msat(&chan->half[idx].htlc_maximum, satoshis)) - abort(); - - return &chan->half[idx]; -} - -static bool channel_is_between(const struct chan *chan, - const struct node_id *a, const struct node_id *b) -{ - if (node_id_eq(&chan->nodes[0]->id, a) - && node_id_eq(&chan->nodes[1]->id, b)) - return true; - - if (node_id_eq(&chan->nodes[0]->id, b) - && node_id_eq(&chan->nodes[1]->id, a)) - return true; - - return false; -} - -int main(int argc, char *argv[]) -{ - struct half_chan *nc; - struct routing_state *rstate; - struct node_id a, b, c, d; - struct amount_msat fee; - struct chan **route; - const double riskfactor = 1.0 / BLOCKS_PER_YEAR / 10000; - - common_setup(argv[0]); - - node_id_from_hexstr("03c173897878996287a8100469f954dd820fcd8941daed91c327f168f3329be0bf", - strlen("03c173897878996287a8100469f954dd820fcd8941daed91c327f168f3329be0bf"), - &a); - node_id_from_hexstr("0230ad0e74ea03976b28fda587bb75bdd357a1938af4424156a18265167f5e40ae", - strlen("0230ad0e74ea03976b28fda587bb75bdd357a1938af4424156a18265167f5e40ae"), - &b); - node_id_from_hexstr("02ea622d5c8d6143f15ed3ce1d501dd0d3d09d3b1c83a44d0034949f8a9ab60f06", - strlen("02ea622d5c8d6143f15ed3ce1d501dd0d3d09d3b1c83a44d0034949f8a9ab60f06"), - &c); - node_id_from_hexstr("02cca6c5c966fcf61d121e3a70e03a1cd9eeeea024b26ea666ce974d43b242e636", - strlen("02cca6c5c966fcf61d121e3a70e03a1cd9eeeea024b26ea666ce974d43b242e636"), - &d); - - rstate = new_routing_state(tmpctx, &a, NULL, NULL, NULL, false, false); - - /* [{'active': True, 'short_id': '6990:2:1/1', 'fee_per_kw': 10, 'delay': 5, 'message_flags': 0, 'channel_flags': 1, 'destination': '0230ad0e74ea03976b28fda587bb75bdd357a1938af4424156a18265167f5e40ae', 'source': '02ea622d5c8d6143f15ed3ce1d501dd0d3d09d3b1c83a44d0034949f8a9ab60f06', 'last_update': 1504064344}, */ - - nc = get_or_make_connection(rstate, &c, &b, "6990x2x1", AMOUNT_SAT(1000)); - nc->base_fee = 0; - nc->proportional_fee = 10; - nc->delay = 5; - nc->channel_flags = 1; - nc->message_flags = 0; - nc->bcast.timestamp = 1504064344; - - /* {'active': True, 'short_id': '6989:2:1/0', 'fee_per_kw': 10, 'delay': 5, 'message_flags': 0, 'channel_flags': 0, 'destination': '03c173897878996287a8100469f954dd820fcd8941daed91c327f168f3329be0bf', 'source': '0230ad0e74ea03976b28fda587bb75bdd357a1938af4424156a18265167f5e40ae', 'last_update': 1504064344}, */ - nc = get_or_make_connection(rstate, &b, &a, "6989x2x1", AMOUNT_SAT(1000)); - nc->base_fee = 0; - nc->proportional_fee = 10; - nc->delay = 5; - nc->channel_flags = 0; - nc->message_flags = 0; - nc->bcast.timestamp = 1504064344; - - /* {'active': True, 'short_id': '6990:2:1/0', 'fee_per_kw': 10, 'delay': 5, 'message_flags': 0, 'channel_flags': 0, 'destination': '02ea622d5c8d6143f15ed3ce1d501dd0d3d09d3b1c83a44d0034949f8a9ab60f06', 'source': '0230ad0e74ea03976b28fda587bb75bdd357a1938af4424156a18265167f5e40ae', 'last_update': 1504064344}, */ - nc = get_or_make_connection(rstate, &b, &c, "6990x2x1", AMOUNT_SAT(1000)); - nc->base_fee = 0; - nc->proportional_fee = 10; - nc->delay = 5; - nc->channel_flags = 0; - nc->message_flags = 0; - nc->bcast.timestamp = 1504064344; - nc->htlc_minimum = AMOUNT_MSAT(100); - - /* {'active': True, 'short_id': '6989:2:1/1', 'fee_per_kw': 10, 'delay': 5, 'message_flags': 0, 'channel_flags': 1, 'destination': '0230ad0e74ea03976b28fda587bb75bdd357a1938af4424156a18265167f5e40ae', 'source': '03c173897878996287a8100469f954dd820fcd8941daed91c327f168f3329be0bf', 'last_update': 1504064344}]} */ - nc = get_or_make_connection(rstate, &a, &b, "6989x2x1", AMOUNT_SAT(1000)); - nc->base_fee = 0; - nc->proportional_fee = 10; - nc->delay = 5; - nc->channel_flags = 1; - nc->message_flags = 0; - nc->bcast.timestamp = 1504064344; - - route = find_route(tmpctx, rstate, &a, &c, AMOUNT_MSAT(100000), riskfactor, 0.0, NULL, - ROUTING_MAX_HOPS, &fee); - assert(route); - assert(tal_count(route) == 2); - assert(channel_is_between(route[0], &a, &b)); - assert(channel_is_between(route[1], &b, &c)); - - - /* We should not be able to find a route that exceeds our own capacity */ - route = find_route(tmpctx, rstate, &a, &c, AMOUNT_MSAT(1000001), riskfactor, 0.0, NULL, - ROUTING_MAX_HOPS, &fee); - assert(!route); - - /* Now test with a query that exceeds the channel capacity after adding - * some fees */ - route = find_route(tmpctx, rstate, &a, &c, AMOUNT_MSAT(999999), riskfactor, 0.0, NULL, - ROUTING_MAX_HOPS, &fee); - assert(!route); - - /* This should fail to return a route because it is smaller than these - * htlc_minimum_msat on the last channel. */ - route = find_route(tmpctx, rstate, &a, &c, AMOUNT_MSAT(1), riskfactor, 0.0, NULL, - ROUTING_MAX_HOPS, &fee); - assert(!route); - - /* {'active': True, 'short_id': '6990:2:1/0', 'fee_per_kw': 10, 'delay': 5, 'message_flags': 1, 'htlc_maximum_msat': 500000, 'htlc_minimum_msat': 100, 'channel_flags': 0, 'destination': '02cca6c5c966fcf61d121e3a70e03a1cd9eeeea024b26ea666ce974d43b242e636', 'source': '03c173897878996287a8100469f954dd820fcd8941daed91c327f168f3329be0bf', 'last_update': 1504064344}, */ - nc = get_or_make_connection(rstate, &a, &d, "6991x2x1", AMOUNT_SAT(1000)); - nc->base_fee = 0; - nc->proportional_fee = 0; - nc->delay = 5; - nc->channel_flags = 0; - nc->message_flags = 1; - nc->bcast.timestamp = 1504064344; - nc->htlc_minimum = AMOUNT_MSAT(100); - nc->htlc_maximum = AMOUNT_MSAT(500000); /* half capacity */ - - /* This should route correctly at the max_msat level */ - route = find_route(tmpctx, rstate, &a, &d, AMOUNT_MSAT(500000), riskfactor, 0.0, NULL, - ROUTING_MAX_HOPS, &fee); - assert(route); - - /* This should fail to return a route because it's larger than the - * htlc_maximum_msat on the last channel. */ - route = find_route(tmpctx, rstate, &a, &d, AMOUNT_MSAT(500001), riskfactor, 0.0, NULL, - ROUTING_MAX_HOPS, &fee); - assert(!route); - - common_shutdown(); - return 0; -} diff --git a/gossipd/test/run-find_route.c b/gossipd/test/run-find_route.c deleted file mode 100644 index 41400bf2cd05..000000000000 --- a/gossipd/test/run-find_route.c +++ /dev/null @@ -1,276 +0,0 @@ -#include "../routing.c" -#include "../gossip_store.c" -#include -#include -#include - -void status_fmt(enum log_level level UNUSED, - const struct node_id *node_id, - const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - vprintf(fmt, ap); - printf("\n"); - va_end(ap); -} - -/* AUTOGENERATED MOCKS START */ -/* Generated stub for cupdate_different */ -bool cupdate_different(struct gossip_store *gs UNNEEDED, - const struct half_chan *hc UNNEEDED, - const u8 *cupdate UNNEEDED) -{ fprintf(stderr, "cupdate_different called!\n"); abort(); } -/* Generated stub for fmt_wireaddr_without_port */ -char *fmt_wireaddr_without_port(const tal_t *ctx UNNEEDED, const struct wireaddr *a UNNEEDED) -{ fprintf(stderr, "fmt_wireaddr_without_port called!\n"); abort(); } -/* Generated stub for fromwire_wireaddr */ -bool fromwire_wireaddr(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct wireaddr *addr UNNEEDED) -{ fprintf(stderr, "fromwire_wireaddr called!\n"); abort(); } -/* Generated stub for json_add_member */ -void json_add_member(struct json_stream *js UNNEEDED, - const char *fieldname UNNEEDED, - bool quote UNNEEDED, - const char *fmt UNNEEDED, ...) -{ fprintf(stderr, "json_add_member called!\n"); abort(); } -/* Generated stub for json_member_direct */ -char *json_member_direct(struct json_stream *js UNNEEDED, - const char *fieldname UNNEEDED, size_t extra UNNEEDED) -{ fprintf(stderr, "json_member_direct called!\n"); abort(); } -/* Generated stub for json_object_end */ -void json_object_end(struct json_stream *js UNNEEDED) -{ fprintf(stderr, "json_object_end called!\n"); abort(); } -/* Generated stub for json_object_start */ -void json_object_start(struct json_stream *ks UNNEEDED, const char *fieldname UNNEEDED) -{ fprintf(stderr, "json_object_start called!\n"); abort(); } -/* Generated stub for memleak_add_helper_ */ -void memleak_add_helper_(const tal_t *p UNNEEDED, void (*cb)(struct htable *memtable UNNEEDED, - const tal_t *)){ } -/* Generated stub for nannounce_different */ -bool nannounce_different(struct gossip_store *gs UNNEEDED, - const struct node *node UNNEEDED, - const u8 *nannounce UNNEEDED) -{ fprintf(stderr, "nannounce_different called!\n"); abort(); } -/* Generated stub for notleak_ */ -void *notleak_(const void *ptr UNNEEDED, bool plus_children UNNEEDED) -{ fprintf(stderr, "notleak_ called!\n"); abort(); } -/* Generated stub for peer_supplied_good_gossip */ -void peer_supplied_good_gossip(struct peer *peer UNNEEDED, size_t amount UNNEEDED) -{ fprintf(stderr, "peer_supplied_good_gossip called!\n"); abort(); } -/* Generated stub for private_channel_announcement */ -const u8 *private_channel_announcement(const tal_t *ctx UNNEEDED, - const struct short_channel_id *scid UNNEEDED, - const struct node_id *local_node_id UNNEEDED, - const struct node_id *remote_node_id UNNEEDED, - const u8 *features UNNEEDED) -{ fprintf(stderr, "private_channel_announcement called!\n"); abort(); } -/* Generated stub for sanitize_error */ -char *sanitize_error(const tal_t *ctx UNNEEDED, const u8 *errmsg UNNEEDED, - struct channel_id *channel_id UNNEEDED) -{ fprintf(stderr, "sanitize_error called!\n"); abort(); } -/* Generated stub for status_failed */ -void status_failed(enum status_failreason code UNNEEDED, - const char *fmt UNNEEDED, ...) -{ fprintf(stderr, "status_failed called!\n"); abort(); } -/* Generated stub for towire_warningfmt */ -u8 *towire_warningfmt(const tal_t *ctx UNNEEDED, - const struct channel_id *channel UNNEEDED, - const char *fmt UNNEEDED, ...) -{ fprintf(stderr, "towire_warningfmt called!\n"); abort(); } -/* AUTOGENERATED MOCKS END */ - -#if DEVELOPER -/* Generated stub for memleak_remove_htable */ -void memleak_remove_htable(struct htable *memtable UNNEEDED, const struct htable *ht UNNEEDED) -{ fprintf(stderr, "memleak_remove_htable called!\n"); abort(); } -/* Generated stub for memleak_remove_intmap_ */ -void memleak_remove_intmap_(struct htable *memtable UNNEEDED, const struct intmap *m UNNEEDED) -{ fprintf(stderr, "memleak_remove_intmap_ called!\n"); abort(); } -#endif - -/* NOOP 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) -{ - return NULL; -} - -static void node_id_from_privkey(const struct privkey *p, struct node_id *id) -{ - struct pubkey k; - pubkey_from_privkey(p, &k); - node_id_from_pubkey(id, &k); -} - -/* Updates existing route if required. */ -static void add_connection(struct routing_state *rstate, - const struct node_id *from, - const struct node_id *to, - u32 base_fee, s32 proportional_fee, - u32 delay) -{ - struct short_channel_id scid; - struct half_chan *c; - struct chan *chan; - struct amount_sat satoshis = AMOUNT_SAT(100000); - - /* Make a unique scid. */ - memcpy(&scid, from, sizeof(scid) / 2); - memcpy((char *)&scid + sizeof(scid) / 2, to, sizeof(scid) / 2); - - chan = get_channel(rstate, &scid); - if (!chan) - chan = new_chan(rstate, &scid, from, to, satoshis, NULL); - - c = &chan->half[node_id_idx(from, to)]; - /* Make sure it's seen as initialized (index non-zero). */ - c->bcast.index = 1; - c->base_fee = base_fee; - c->proportional_fee = proportional_fee; - c->delay = delay; - c->channel_flags = node_id_idx(from, to); - c->htlc_minimum = AMOUNT_MSAT(0); - c->htlc_maximum = AMOUNT_MSAT(100000 * 1000); -} - -/* Returns chan connecting from and to: *idx set to refer - * to connection with src=from, dst=to */ -static struct chan *find_channel(struct routing_state *rstate UNUSED, - const struct node *from, - const struct node *to, - int *idx) -{ - struct chan_map_iter i; - struct chan *c; - - *idx = node_id_idx(&from->id, &to->id); - - for (c = first_chan(to, &i); c; c = next_chan(to, &i)) { - if (c->nodes[*idx] == from) - return c; - } - return NULL; -} - -static struct half_chan *get_connection(struct routing_state *rstate, - const struct node_id *from_id, - const struct node_id *to_id) -{ - int idx; - struct node *from, *to; - struct chan *c; - - from = get_node(rstate, from_id); - to = get_node(rstate, to_id); - if (!from || ! to) - return NULL; - - c = find_channel(rstate, from, to, &idx); - if (!c) - return NULL; - return &c->half[idx]; -} - -static bool channel_is_between(const struct chan *chan, - const struct node_id *a, const struct node_id *b) -{ - if (node_id_eq(&chan->nodes[0]->id, a) - && node_id_eq(&chan->nodes[1]->id, b)) - return true; - - if (node_id_eq(&chan->nodes[0]->id, b) - && node_id_eq(&chan->nodes[1]->id, a)) - return true; - - return false; -} - -int main(int argc, char *argv[]) -{ - struct routing_state *rstate; - struct node_id a, b, c, d; - struct privkey tmp; - struct amount_msat fee; - struct chan **route; - const double riskfactor = 1.0 / BLOCKS_PER_YEAR / 10000; - - common_setup(argv[0]); - - memset(&tmp, 'a', sizeof(tmp)); - node_id_from_privkey(&tmp, &a); - rstate = new_routing_state(tmpctx, &a, NULL, NULL, NULL, false, false); - - new_node(rstate, &a); - - memset(&tmp, 'b', sizeof(tmp)); - node_id_from_privkey(&tmp, &b); - new_node(rstate, &b); - - /* A<->B */ - add_connection(rstate, &a, &b, 1, 1, 1); - - route = find_route(tmpctx, rstate, &a, &b, AMOUNT_MSAT(1000), riskfactor, 0.0, NULL, - ROUTING_MAX_HOPS, &fee); - assert(route); - assert(tal_count(route) == 1); - assert(amount_msat_eq(fee, AMOUNT_MSAT(0))); - - /* A<->B<->C */ - memset(&tmp, 'c', sizeof(tmp)); - node_id_from_privkey(&tmp, &c); - new_node(rstate, &c); - - status_debug("A = %s", type_to_string(tmpctx, struct node_id, &a)); - status_debug("B = %s", type_to_string(tmpctx, struct node_id, &b)); - status_debug("C = %s", type_to_string(tmpctx, struct node_id, &c)); - add_connection(rstate, &b, &c, 1, 1, 1); - - route = find_route(tmpctx, rstate, &a, &c, AMOUNT_MSAT(1000), riskfactor, 0.0, NULL, - ROUTING_MAX_HOPS, &fee); - assert(route); - assert(tal_count(route) == 2); - assert(amount_msat_eq(fee, AMOUNT_MSAT(1))); - - /* A<->D<->C: Lower base, higher percentage. */ - memset(&tmp, 'd', sizeof(tmp)); - node_id_from_privkey(&tmp, &d); - new_node(rstate, &d); - status_debug("D = %s", type_to_string(tmpctx, struct node_id, &d)); - - add_connection(rstate, &a, &d, 0, 2, 1); - add_connection(rstate, &d, &c, 0, 2, 1); - - /* Will go via D for small amounts. */ - route = find_route(tmpctx, rstate, &a, &c, AMOUNT_MSAT(1000), riskfactor, 0.0, NULL, - ROUTING_MAX_HOPS, &fee); - assert(route); - assert(tal_count(route) == 2); - assert(channel_is_between(route[0], &a, &d)); - assert(channel_is_between(route[1], &d, &c)); - assert(amount_msat_eq(fee, AMOUNT_MSAT(0))); - - /* Will go via B for large amounts. */ - route = find_route(tmpctx, rstate, &a, &c, AMOUNT_MSAT(3000000), riskfactor, 0.0, NULL, - ROUTING_MAX_HOPS, &fee); - assert(route); - assert(tal_count(route) == 2); - assert(channel_is_between(route[0], &a, &b)); - assert(channel_is_between(route[1], &b, &c)); - assert(amount_msat_eq(fee, AMOUNT_MSAT(1 + 3))); - - /* Make B->C inactive, force it back via D */ - get_connection(rstate, &b, &c)->channel_flags |= ROUTING_FLAGS_DISABLED; - route = find_route(tmpctx, rstate, &a, &c, AMOUNT_MSAT(3000000), riskfactor, 0.0, NULL, - ROUTING_MAX_HOPS, &fee); - assert(route); - assert(tal_count(route) == 2); - assert(channel_is_between(route[0], &a, &d)); - assert(channel_is_between(route[1], &d, &c)); - assert(amount_msat_eq(fee, AMOUNT_MSAT(0 + 6))); - - common_shutdown(); - return 0; -} diff --git a/gossipd/test/run-overlong.c b/gossipd/test/run-overlong.c deleted file mode 100644 index 2a16642f7026..000000000000 --- a/gossipd/test/run-overlong.c +++ /dev/null @@ -1,196 +0,0 @@ -#include "../routing.c" -#include "../gossip_store.c" -#include -#include -#include - -void status_fmt(enum log_level level UNUSED, - const struct node_id *node_id, - const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - vprintf(fmt, ap); - printf("\n"); - va_end(ap); -} - -/* AUTOGENERATED MOCKS START */ -/* Generated stub for cupdate_different */ -bool cupdate_different(struct gossip_store *gs UNNEEDED, - const struct half_chan *hc UNNEEDED, - const u8 *cupdate UNNEEDED) -{ fprintf(stderr, "cupdate_different called!\n"); abort(); } -/* Generated stub for fmt_wireaddr_without_port */ -char *fmt_wireaddr_without_port(const tal_t *ctx UNNEEDED, const struct wireaddr *a UNNEEDED) -{ fprintf(stderr, "fmt_wireaddr_without_port called!\n"); abort(); } -/* Generated stub for fromwire_wireaddr */ -bool fromwire_wireaddr(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct wireaddr *addr UNNEEDED) -{ fprintf(stderr, "fromwire_wireaddr called!\n"); abort(); } -/* Generated stub for json_add_member */ -void json_add_member(struct json_stream *js UNNEEDED, - const char *fieldname UNNEEDED, - bool quote UNNEEDED, - const char *fmt UNNEEDED, ...) -{ fprintf(stderr, "json_add_member called!\n"); abort(); } -/* Generated stub for json_member_direct */ -char *json_member_direct(struct json_stream *js UNNEEDED, - const char *fieldname UNNEEDED, size_t extra UNNEEDED) -{ fprintf(stderr, "json_member_direct called!\n"); abort(); } -/* Generated stub for json_object_end */ -void json_object_end(struct json_stream *js UNNEEDED) -{ fprintf(stderr, "json_object_end called!\n"); abort(); } -/* Generated stub for json_object_start */ -void json_object_start(struct json_stream *ks UNNEEDED, const char *fieldname UNNEEDED) -{ fprintf(stderr, "json_object_start called!\n"); abort(); } -/* Generated stub for memleak_add_helper_ */ -void memleak_add_helper_(const tal_t *p UNNEEDED, void (*cb)(struct htable *memtable UNNEEDED, - const tal_t *)){ } -/* Generated stub for nannounce_different */ -bool nannounce_different(struct gossip_store *gs UNNEEDED, - const struct node *node UNNEEDED, - const u8 *nannounce UNNEEDED) -{ fprintf(stderr, "nannounce_different called!\n"); abort(); } -/* Generated stub for notleak_ */ -void *notleak_(const void *ptr UNNEEDED, bool plus_children UNNEEDED) -{ fprintf(stderr, "notleak_ called!\n"); abort(); } -/* Generated stub for peer_supplied_good_gossip */ -void peer_supplied_good_gossip(struct peer *peer UNNEEDED, size_t amount UNNEEDED) -{ fprintf(stderr, "peer_supplied_good_gossip called!\n"); abort(); } -/* Generated stub for private_channel_announcement */ -const u8 *private_channel_announcement(const tal_t *ctx UNNEEDED, - const struct short_channel_id *scid UNNEEDED, - const struct node_id *local_node_id UNNEEDED, - const struct node_id *remote_node_id UNNEEDED, - const u8 *features UNNEEDED) -{ fprintf(stderr, "private_channel_announcement called!\n"); abort(); } -/* Generated stub for sanitize_error */ -char *sanitize_error(const tal_t *ctx UNNEEDED, const u8 *errmsg UNNEEDED, - struct channel_id *channel_id UNNEEDED) -{ fprintf(stderr, "sanitize_error called!\n"); abort(); } -/* Generated stub for status_failed */ -void status_failed(enum status_failreason code UNNEEDED, - const char *fmt UNNEEDED, ...) -{ fprintf(stderr, "status_failed called!\n"); abort(); } -/* Generated stub for towire_warningfmt */ -u8 *towire_warningfmt(const tal_t *ctx UNNEEDED, - const struct channel_id *channel UNNEEDED, - const char *fmt UNNEEDED, ...) -{ fprintf(stderr, "towire_warningfmt called!\n"); abort(); } -/* AUTOGENERATED MOCKS END */ - -#if DEVELOPER -/* Generated stub for memleak_remove_htable */ -void memleak_remove_htable(struct htable *memtable UNNEEDED, const struct htable *ht UNNEEDED) -{ fprintf(stderr, "memleak_remove_htable called!\n"); abort(); } -/* Generated stub for memleak_remove_intmap_ */ -void memleak_remove_intmap_(struct htable *memtable UNNEEDED, const struct intmap *m UNNEEDED) -{ fprintf(stderr, "memleak_remove_intmap_ called!\n"); abort(); } -#endif - -/* NOOP 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) -{ - return NULL; -} - -static void node_id_from_privkey(const struct privkey *p, struct node_id *id) -{ - struct pubkey k; - pubkey_from_privkey(p, &k); - node_id_from_pubkey(id, &k); -} - -#define NUM_NODES (ROUTING_MAX_HOPS + 1) - -/* We create an arrangement of nodes, each node N connected to N+1 and - * to node 1. The cost for each N to N+1 route is 1, for N to 1 is - * 2^N. That means it's always cheapest to go the longer route */ -int main(int argc, char *argv[]) -{ - struct routing_state *rstate; - struct node_id ids[NUM_NODES]; - struct chan **route; - struct amount_msat last_fee; - - common_setup(argv[0]); - - for (size_t i = 0; i < NUM_NODES; i++) { - struct privkey tmp; - memset(&tmp, i+1, sizeof(tmp)); - node_id_from_privkey(&tmp, &ids[i]); - } - /* We are node 0 */ - rstate = new_routing_state(tmpctx, &ids[0], NULL, NULL, NULL, - false, false); - - for (size_t i = 0; i < NUM_NODES; i++) { - struct chan *chan; - struct half_chan *hc; - struct short_channel_id scid; - - new_node(rstate, &ids[i]); - - if (i == 0) - continue; - if (!mk_short_channel_id(&scid, i, i-1, 0)) - abort(); - chan = new_chan(rstate, &scid, &ids[i], &ids[i-1], - AMOUNT_SAT(1000000), NULL); - - hc = &chan->half[node_id_idx(&ids[i-1], &ids[i])]; - hc->bcast.index = 1; - hc->base_fee = 1; - hc->proportional_fee = 0; - hc->delay = 0; - hc->channel_flags = node_id_idx(&ids[i-1], &ids[i]); - hc->htlc_minimum = AMOUNT_MSAT(0); - hc->htlc_maximum = AMOUNT_MSAT(1000000 * 1000); - SUPERVERBOSE("Joining %s to %s, fee %u", - type_to_string(tmpctx, struct node_id, &ids[i-1]), - type_to_string(tmpctx, struct node_id, &ids[i]), - (int)hc->base_fee); - - if (i <= 2) - continue; - if (!mk_short_channel_id(&scid, i, 1, 0)) - abort(); - chan = new_chan(rstate, &scid, &ids[i], &ids[1], - AMOUNT_SAT(1000000), NULL); - hc = &chan->half[node_id_idx(&ids[1], &ids[i])]; - hc->bcast.index = 1; - hc->base_fee = 1 << i; - hc->proportional_fee = 0; - hc->delay = 0; - hc->channel_flags = node_id_idx(&ids[1], &ids[i]); - hc->htlc_minimum = AMOUNT_MSAT(0); - hc->htlc_maximum = AMOUNT_MSAT(1000000 * 1000); - SUPERVERBOSE("Joining %s to %s, fee %u", - type_to_string(tmpctx, struct node_id, &ids[1]), - type_to_string(tmpctx, struct node_id, &ids[i]), - (int)hc->base_fee); - } - - for (size_t i = ROUTING_MAX_HOPS; i > 1; i--) { - struct amount_msat fee; - SUPERVERBOSE("%s -> %s:", - type_to_string(tmpctx, struct node_id, &ids[0]), - type_to_string(tmpctx, struct node_id, &ids[NUM_NODES-1])); - - route = find_route(tmpctx, rstate, &ids[0], &ids[NUM_NODES-1], - AMOUNT_MSAT(1000), 0, 0.0, NULL, - i, &fee); - assert(route); - assert(tal_count(route) == i); - if (i != ROUTING_MAX_HOPS) - assert(amount_msat_greater(fee, last_fee)); - last_fee = fee; - } - - common_shutdown(); - return 0; -} diff --git a/gossipd/test/run-txout_failure.c b/gossipd/test/run-txout_failure.c index 044e8fed16ff..38cde87d4459 100644 --- a/gossipd/test/run-txout_failure.c +++ b/gossipd/test/run-txout_failure.c @@ -13,9 +13,9 @@ bool cupdate_different(struct gossip_store *gs UNNEEDED, /* Generated stub for fmt_wireaddr_without_port */ char *fmt_wireaddr_without_port(const tal_t *ctx UNNEEDED, const struct wireaddr *a UNNEEDED) { fprintf(stderr, "fmt_wireaddr_without_port called!\n"); abort(); } -/* Generated stub for fromwire_wireaddr */ -bool fromwire_wireaddr(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct wireaddr *addr UNNEEDED) -{ fprintf(stderr, "fromwire_wireaddr called!\n"); abort(); } +/* Generated stub for fromwire_wireaddr_array */ +struct wireaddr *fromwire_wireaddr_array(const tal_t *ctx UNNEEDED, const u8 *ser UNNEEDED) +{ fprintf(stderr, "fromwire_wireaddr_array called!\n"); abort(); } /* Generated stub for gossip_store_add */ u64 gossip_store_add(struct gossip_store *gs UNNEEDED, const u8 *gossip_msg UNNEEDED, u32 timestamp UNNEEDED, bool push UNNEEDED, const u8 *addendum UNNEEDED) diff --git a/lightningd/gossip_control.c b/lightningd/gossip_control.c index 38ceac648123..1b2fd6a70175 100644 --- a/lightningd/gossip_control.c +++ b/lightningd/gossip_control.c @@ -134,14 +134,10 @@ static unsigned gossip_msg(struct subd *gossip, const u8 *msg, const int *fds) switch (t) { /* These are messages we send, not them. */ case WIRE_GOSSIPD_INIT: - case WIRE_GOSSIPD_GETNODES_REQUEST: - case WIRE_GOSSIPD_GETROUTE_REQUEST: - case WIRE_GOSSIPD_GETCHANNELS_REQUEST: case WIRE_GOSSIPD_PING: case WIRE_GOSSIPD_GET_STRIPPED_CUPDATE: case WIRE_GOSSIPD_GET_TXOUT_REPLY: case WIRE_GOSSIPD_OUTPOINT_SPENT: - case WIRE_GOSSIPD_GET_INCOMING_CHANNELS: case WIRE_GOSSIPD_DEV_SET_MAX_SCIDS_ENCODE_SIZE: case WIRE_GOSSIPD_DEV_SUPPRESS: case WIRE_GOSSIPD_LOCAL_CHANNEL_CLOSE: @@ -152,10 +148,7 @@ static unsigned gossip_msg(struct subd *gossip, const u8 *msg, const int *fds) case WIRE_GOSSIPD_SEND_ONIONMSG: case WIRE_GOSSIPD_ADDGOSSIP: /* This is a reply, so never gets through to here. */ - case WIRE_GOSSIPD_GETNODES_REPLY: - case WIRE_GOSSIPD_GETROUTE_REPLY: - case WIRE_GOSSIPD_GETCHANNELS_REPLY: - case WIRE_GOSSIPD_GET_INCOMING_CHANNELS_REPLY: + case WIRE_GOSSIPD_INIT_REPLY: case WIRE_GOSSIPD_DEV_MEMLEAK_REPLY: case WIRE_GOSSIPD_DEV_COMPACT_STORE_REPLY: case WIRE_GOSSIPD_GET_STRIPPED_CUPDATE_REPLY: @@ -195,6 +188,16 @@ static void gossip_topology_synced(struct chain_topology *topo, void *unused) gossip_notify_new_block(topo->ld, get_block_height(topo)); } +/* We make sure gossipd is started before plugins (which may want gossip_map) */ +static void gossipd_init_done(struct subd *gossipd, + const u8 *msg, + const int *fds, + void *unused) +{ + /* Break out of loop, so we can begin */ + io_break(gossipd); +} + /* Create the `gossipd` subdaemon and send the initialization * message */ void gossip_init(struct lightningd *ld, int connectd_fd) @@ -215,7 +218,7 @@ void gossip_init(struct lightningd *ld, int connectd_fd) gossip_topology_synced, NULL); msg = towire_gossipd_init( - tmpctx, + NULL, chainparams, ld->our_features, &ld->id, @@ -225,7 +228,12 @@ void gossip_init(struct lightningd *ld, int connectd_fd) IFDEV(ld->dev_gossip_time ? &ld->dev_gossip_time: NULL, NULL), IFDEV(ld->dev_fast_gossip, false), IFDEV(ld->dev_fast_gossip_prune, false)); - subd_send_msg(ld->gossip, msg); + + subd_req(ld->gossip, ld->gossip, take(msg), -1, 0, + gossipd_init_done, NULL); + + /* Wait for gossipd_init_reply */ + io_loop(NULL, NULL); } void gossipd_notify_spend(struct lightningd *ld, @@ -235,347 +243,6 @@ void gossipd_notify_spend(struct lightningd *ld, subd_send_msg(ld->gossip, msg); } -static void json_getnodes_reply(struct subd *gossip UNUSED, const u8 *reply, - const int *fds UNUSED, - struct command *cmd) -{ - struct gossip_getnodes_entry **nodes; - struct json_stream *response; - size_t i, j; - - if (!fromwire_gossipd_getnodes_reply(reply, reply, &nodes)) { - was_pending(command_fail(cmd, LIGHTNINGD, - "Malformed gossip_getnodes response")); - return; - } - - response = json_stream_success(cmd); - json_array_start(response, "nodes"); - - for (i = 0; i < tal_count(nodes); i++) { - struct json_escape *esc; - - json_object_start(response, NULL); - json_add_node_id(response, "nodeid", &nodes[i]->nodeid); - if (nodes[i]->last_timestamp < 0) { - json_object_end(response); - continue; - } - esc = json_escape(NULL, - take(tal_strndup(NULL, - (const char *)nodes[i]->alias, - ARRAY_SIZE(nodes[i]->alias)))); - json_add_escaped_string(response, "alias", take(esc)); - json_add_hex(response, "color", - nodes[i]->color, ARRAY_SIZE(nodes[i]->color)); - json_add_u64(response, "last_timestamp", - nodes[i]->last_timestamp); - json_add_hex_talarr(response, "features", nodes[i]->features); - json_array_start(response, "addresses"); - for (j=0; jaddresses); j++) { - json_add_address(response, NULL, &nodes[i]->addresses[j]); - } - json_array_end(response); - json_object_end(response); - } - json_array_end(response); - was_pending(command_success(cmd, response)); -} - -static struct command_result *json_listnodes(struct command *cmd, - const char *buffer, - const jsmntok_t *obj UNNEEDED, - const jsmntok_t *params) -{ - u8 *req; - struct node_id *id; - - if (!param(cmd, buffer, params, - p_opt("id", param_node_id, &id), - NULL)) - return command_param_failed(); - - req = towire_gossipd_getnodes_request(cmd, id); - subd_req(cmd, cmd->ld->gossip, req, -1, 0, json_getnodes_reply, cmd); - return command_still_pending(cmd); -} - -static const struct json_command listnodes_command = { - "listnodes", - "network", - json_listnodes, - "Show node {id} (or all, if no {id}), in our local network view" -}; -AUTODATA(json_command, &listnodes_command); - -static void json_add_route_hop_style(struct json_stream *response, - const char *fieldname, - enum route_hop_style style) -{ - switch (style) { - case ROUTE_HOP_LEGACY: - json_add_string(response, fieldname, "legacy"); - return; - case ROUTE_HOP_TLV: - json_add_string(response, fieldname, "tlv"); - return; - } - fatal("Unknown route_hop_style %u", style); -} - -/* Output a route hop */ -static void json_add_route_hop(struct json_stream *r, char const *n, - const struct route_hop *h) -{ - /* Imitate what getroute/sendpay use */ - json_object_start(r, n); - json_add_node_id(r, "id", &h->node_id); - json_add_short_channel_id(r, "channel", &h->scid); - json_add_num(r, "direction", h->direction); - json_add_amount_msat_compat(r, h->amount, "msatoshi", "amount_msat"); - json_add_num(r, "delay", h->delay); - json_add_route_hop_style(r, "style", h->style); - json_object_end(r); -} - -/* Output a route */ -static void json_add_route(struct json_stream *r, char const *n, - struct route_hop **hops, size_t hops_len) -{ - size_t i; - json_array_start(r, n); - for (i = 0; i < hops_len; ++i) { - json_add_route_hop(r, NULL, hops[i]); - } - json_array_end(r); -} - -static void json_getroute_reply(struct subd *gossip UNUSED, const u8 *reply, const int *fds UNUSED, - struct command *cmd) -{ - struct json_stream *response; - struct route_hop **hops; - - fromwire_gossipd_getroute_reply(reply, reply, &hops); - - if (tal_count(hops) == 0) { - was_pending(command_fail(cmd, PAY_ROUTE_NOT_FOUND, - "Could not find a route")); - return; - } - - response = json_stream_success(cmd); - json_add_route(response, "route", hops, tal_count(hops)); - was_pending(command_success(cmd, response)); -} - -static struct command_result *json_getroute(struct command *cmd, - const char *buffer, - const jsmntok_t *obj UNNEEDED, - const jsmntok_t *params) -{ - struct lightningd *ld = cmd->ld; - struct node_id *destination; - struct node_id *source; - const jsmntok_t *excludetok; - struct amount_msat *msat; - u32 *cltv; - /* risk factor 12.345% -> riskfactor_millionths = 12345000 */ - u64 *riskfactor_millionths; - const struct exclude_entry **excluded; - u32 *max_hops; - - /* Higher fuzz means that some high-fee paths can be discounted - * for an even larger value, increasing the scope for route - * randomization (the higher-fee paths become more likely to - * be selected) at the cost of increasing the probability of - * selecting the higher-fee paths. */ - u64 *fuzz_millionths; /* fuzz 12.345% -> fuzz_millionths = 12345000 */ - - if (!param( - cmd, buffer, params, p_req("id", param_node_id, &destination), - p_req("msatoshi", param_msat, &msat), - p_req("riskfactor", param_millionths, &riskfactor_millionths), - p_opt_def("cltv", param_number, &cltv, 9), - p_opt("fromid", param_node_id, &source), - p_opt_def("fuzzpercent", param_millionths, &fuzz_millionths, - 5000000), - p_opt("exclude", param_array, &excludetok), - p_opt_def("maxhops", param_number, &max_hops, ROUTING_MAX_HOPS), - NULL)) - return command_param_failed(); - - /* Convert from percentage */ - *fuzz_millionths /= 100; - - if (excludetok) { - const jsmntok_t *t; - size_t i; - - excluded = tal_arr(cmd, const struct exclude_entry *, 0); - - json_for_each_arr(i, t, excludetok) { - struct exclude_entry *entry = tal(excluded, struct exclude_entry); - struct short_channel_id_dir *chan_id = tal(tmpctx, struct short_channel_id_dir); - if (!short_channel_id_dir_from_str(buffer + t->start, - t->end - t->start, - chan_id)) { - struct node_id *node_id = tal(tmpctx, struct node_id); - - if (!json_to_node_id(buffer, t, node_id)) - return command_fail_badparam(cmd, "exclude", - buffer, t, - "should be short_channel_id or node_id"); - - entry->type = EXCLUDE_NODE; - entry->u.node_id = *node_id; - } else { - entry->type = EXCLUDE_CHANNEL; - entry->u.chan_id = *chan_id; - } - - tal_arr_expand(&excluded, entry); - } - } else { - excluded = NULL; - } - - u8 *req = towire_gossipd_getroute_request( - cmd, source, destination, *msat, *riskfactor_millionths, *cltv, - *fuzz_millionths, excluded, *max_hops); - subd_req(ld->gossip, ld->gossip, req, -1, 0, json_getroute_reply, cmd); - return command_still_pending(cmd); -} - -static const struct json_command getroute_command = { - "getroute", - "channels", - json_getroute, - "Show route to {id} for {msatoshi}, using {riskfactor} and optional {cltv} (default 9). " - "If specified search from {fromid} otherwise use this node as source. " - "Randomize the route with up to {fuzzpercent} (default 5.0). " - "{exclude} an array of short-channel-id/direction (e.g. [ '564334x877x1/0', '564195x1292x0/1' ]) " - "or node-id from consideration. " - "Set the {maxhops} the route can take (default 20)." -}; -AUTODATA(json_command, &getroute_command); - -static void json_add_halfchan(struct json_stream *response, - const struct gossip_getchannels_entry *e, - int idx) -{ - const struct gossip_halfchannel_entry *he = e->e[idx]; - if (!he) - return; - - json_object_start(response, NULL); - json_add_node_id(response, "source", &e->node[idx]); - json_add_node_id(response, "destination", &e->node[!idx]); - json_add_short_channel_id(response, "short_channel_id", - &e->short_channel_id); - json_add_bool(response, "public", e->public); - json_add_amount_sat_compat(response, e->sat, - "satoshis", "amount_msat"); - json_add_num(response, "message_flags", he->message_flags); - json_add_num(response, "channel_flags", he->channel_flags); - json_add_bool(response, "active", - !(he->channel_flags & ROUTING_FLAGS_DISABLED) - && !e->local_disabled); - json_add_num(response, "last_update", he->last_update_timestamp); - json_add_num(response, "base_fee_millisatoshi", he->base_fee_msat); - json_add_num(response, "fee_per_millionth", he->fee_per_millionth); - json_add_num(response, "delay", he->delay); - json_add_amount_msat_only(response, "htlc_minimum_msat", he->min); - json_add_amount_msat_only(response, "htlc_maximum_msat", he->max); - json_add_hex_talarr(response, "features", e->features); - json_object_end(response); -} - -struct listchannels_info { - struct command *cmd; - struct json_stream *response; - struct short_channel_id *id; - struct node_id *source; -}; - -/* Called upon receiving a getchannels_reply from `gossipd` */ -static void json_listchannels_reply(struct subd *gossip UNUSED, const u8 *reply, - const int *fds UNUSED, - struct listchannels_info *linfo) -{ - size_t i; - struct gossip_getchannels_entry **entries; - bool complete; - - if (!fromwire_gossipd_getchannels_reply(reply, reply, - &complete, &entries)) { - /* Shouldn't happen: just end json stream. */ - log_broken(linfo->cmd->ld->log, "Invalid reply from gossipd"); - was_pending(command_raw_complete(linfo->cmd, linfo->response)); - return; - } - - for (i = 0; i < tal_count(entries); i++) { - json_add_halfchan(linfo->response, entries[i], 0); - json_add_halfchan(linfo->response, entries[i], 1); - } - - /* More coming? Ask from this point on.. */ - if (!complete) { - u8 *req; - assert(tal_count(entries) != 0); - req = towire_gossipd_getchannels_request(linfo->cmd, - linfo->id, - linfo->source, - &entries[i-1] - ->short_channel_id); - subd_req(linfo->cmd->ld->gossip, linfo->cmd->ld->gossip, - req, -1, 0, json_listchannels_reply, linfo); - } else { - json_array_end(linfo->response); - was_pending(command_success(linfo->cmd, linfo->response)); - } -} - -static struct command_result *json_listchannels(struct command *cmd, - const char *buffer, - const jsmntok_t *obj UNNEEDED, - const jsmntok_t *params) -{ - u8 *req; - struct listchannels_info *linfo = tal(cmd, struct listchannels_info); - - linfo->cmd = cmd; - if (!param(cmd, buffer, params, - p_opt("short_channel_id", param_short_channel_id, &linfo->id), - p_opt("source", param_node_id, &linfo->source), - NULL)) - return command_param_failed(); - - if (linfo->id && linfo->source) - return command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "Cannot specify both source and short_channel_id"); - - /* Start JSON response, then we stream. */ - linfo->response = json_stream_success(cmd); - json_array_start(linfo->response, "channels"); - - req = towire_gossipd_getchannels_request(cmd, linfo->id, linfo->source, - NULL); - subd_req(cmd->ld->gossip, cmd->ld->gossip, - req, -1, 0, json_listchannels_reply, linfo); - - return command_still_pending(cmd); -} - -static const struct json_command listchannels_command = { - "listchannels", - "channels", - json_listchannels, - "Show channel {short_channel_id} or {source} (or all known channels, if not specified)" -}; -AUTODATA(json_command, &listchannels_command); - /* Called upon receiving a addgossip_reply from `gossipd` */ static void json_addgossip_reply(struct subd *gossip UNUSED, const u8 *reply, const int *fds UNUSED, diff --git a/lightningd/gossip_msg.c b/lightningd/gossip_msg.c index bc827c938b91..47eb5bb93e2b 100644 --- a/lightningd/gossip_msg.c +++ b/lightningd/gossip_msg.c @@ -194,32 +194,3 @@ void towire_gossip_getchannels_entry(u8 **pptr, } else towire_bool(pptr, false); } - -struct exclude_entry *fromwire_exclude_entry(const tal_t *ctx, - const u8 **pptr, size_t *max) -{ - struct exclude_entry *entry = tal(ctx, struct exclude_entry); - entry->type = fromwire_u8(pptr, max); - switch (entry->type) { - case EXCLUDE_CHANNEL: - fromwire_short_channel_id_dir(pptr, max, &entry->u.chan_id); - return entry; - case EXCLUDE_NODE: - fromwire_node_id(pptr, max, &entry->u.node_id); - return entry; - default: - return fromwire_fail(pptr, max); - } -} - -void towire_exclude_entry(u8 **pptr, const struct exclude_entry *entry) -{ - assert(entry->type == EXCLUDE_CHANNEL || - entry->type == EXCLUDE_NODE); - - towire_u8(pptr, entry->type); - if (entry->type == EXCLUDE_CHANNEL) - towire_short_channel_id_dir(pptr, &entry->u.chan_id); - else - towire_node_id(pptr, &entry->u.node_id); -} diff --git a/lightningd/gossip_msg.h b/lightningd/gossip_msg.h index 5f405619212a..fe1f48eca82d 100644 --- a/lightningd/gossip_msg.h +++ b/lightningd/gossip_msg.h @@ -54,9 +54,4 @@ fromwire_gossip_getchannels_entry(const tal_t *ctx, void towire_gossip_getchannels_entry( u8 **pptr, const struct gossip_getchannels_entry *entry); -struct exclude_entry * -fromwire_exclude_entry(const tal_t *ctx, - const u8 **pptr, size_t *max); -void towire_exclude_entry(u8 **pptr, const struct exclude_entry *entry); - #endif /* LIGHTNING_LIGHTNINGD_GOSSIP_MSG_H */ diff --git a/lightningd/invoice.c b/lightningd/invoice.c index 56b42a10e39c..bf7c2ec4ca13 100644 --- a/lightningd/invoice.c +++ b/lightningd/invoice.c @@ -603,8 +603,7 @@ 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) + *candidates) { /* The total amount we have gathered for incoming channels. */ struct amount_msat gathered; @@ -637,9 +636,6 @@ static struct route_info **select_inchan_mpp(const tal_t *ctx, candidates[i].c->rr_number = ld->rr_counter++; } - /* Check if we gathered enough. */ - *warning_mpp_capacity = amount_msat_less(gathered, amount_needed); - return routehints; } @@ -657,87 +653,169 @@ struct invoice_info { struct chanhints *chanhints; }; -static void gossipd_incoming_channels_reply(struct subd *gossipd, - const u8 *msg, - const int *fs, - struct invoice_info *info) +/* Add routehints based on listincoming results: NULL means success. */ +static struct command_result * +add_routehints(struct invoice_info *info, + const char *buffer, + const jsmntok_t *toks, + bool *warning_mpp, + bool *warning_capacity, + bool *warning_deadends, + bool *warning_offline, + bool *warning_private_unused) { - 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; + struct amount_msat avail_capacity, deadend_capacity, offline_capacity, + private_capacity; + struct routehint_candidate *candidates; + struct amount_msat total, needed; - candidates = routehint_candidates(tmpctx, info->cmd->ld, msg, - chanhints ? chanhints->expose_all_private : false, + /* Dev code can force routes. */ + if (tal_count(info->b11->routes) != 0) { + *warning_mpp = *warning_capacity = *warning_deadends + = *warning_offline = *warning_private_unused + = false; + return NULL; + } + + candidates = routehint_candidates(tmpctx, info->cmd->ld, + buffer, toks, + chanhints ? &chanhints->expose_all_private : NULL, chanhints ? chanhints->hints : NULL, &node_unpublished, - &deadends, - &offline_amt); + &avail_capacity, + &private_capacity, + &deadend_capacity, + &offline_capacity); /* 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; + return command_fail(info->cmd, + INVOICE_HINTS_GAVE_NO_ROUTES, + "None of those hints were suitable local channels"); } + 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) { - 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); + info->b11->routes = select_inchan_mpp(info->b11, + info->cmd->ld, + needed, + candidates); + *warning_mpp = (tal_count(info->b11->routes) > 1); + } else { + *warning_mpp = false; + } + + log_debug(info->cmd->ld->log, "needed = %s, avail_capacity = %s, private_capacity = %s, offline_capacity = %s, deadend_capacity = %s", + type_to_string(tmpctx, struct amount_msat, &needed), + type_to_string(tmpctx, struct amount_msat, &avail_capacity), + type_to_string(tmpctx, struct amount_msat, &private_capacity), + type_to_string(tmpctx, struct amount_msat, &offline_capacity), + type_to_string(tmpctx, struct amount_msat, &deadend_capacity)); + + if (!amount_msat_add(&total, avail_capacity, offline_capacity) + || !amount_msat_add(&total, total, deadend_capacity) + || !amount_msat_add(&total, total, private_capacity)) + fatal("Cannot add %s + %s + %s + %s", + type_to_string(tmpctx, struct amount_msat, + &avail_capacity), + type_to_string(tmpctx, struct amount_msat, + &offline_capacity), + type_to_string(tmpctx, struct amount_msat, + &deadend_capacity), + type_to_string(tmpctx, struct amount_msat, + &private_capacity)); + + /* If we literally didn't have capacity at all, warn. */ + *warning_capacity = amount_msat_greater_eq(needed, total); + + /* We only warn about these if we didn't have capacity and + * they would have helped. */ + *warning_offline = false; + *warning_deadends = false; + *warning_private_unused = false; + if (amount_msat_greater(needed, avail_capacity)) { + struct amount_msat tot; + + /* We didn't get enough: would offline have helped? */ + if (!amount_msat_add(&tot, avail_capacity, offline_capacity)) + abort(); + if (amount_msat_greater_eq(tot, needed)) { + *warning_offline = true; + goto done; + } + + /* Hmm, what about deadends? */ + if (!amount_msat_add(&tot, tot, deadend_capacity)) + abort(); + if (amount_msat_greater_eq(tot, needed)) { + *warning_deadends = true; + goto done; + } + + /* What about private channels? */ + if (!amount_msat_add(&tot, tot, private_capacity)) + abort(); + if (amount_msat_greater_eq(tot, needed)) { + *warning_private_unused = true; + goto done; } } +done: + return NULL; +} + +static struct command_result * +invoice_complete(struct invoice_info *info, + bool warning_no_listincoming, + bool warning_mpp, + bool warning_capacity, + bool warning_deadends, + bool warning_offline, + bool warning_private_unused) +{ + struct json_stream *response; + struct invoice invoice; + char *b11enc; + const struct invoice_details *details; + struct wallet *wallet = info->cmd->ld->wallet; + 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; + return command_fail(info->cmd, + INVOICE_PREIMAGE_ALREADY_EXISTS, + "preimage already used"); } if (!wallet_invoice_create(wallet, @@ -751,10 +829,9 @@ static void gossipd_incoming_channels_reply(struct subd *gossipd, &info->payment_preimage, &info->b11->payment_hash, NULL)) { - was_pending(command_fail(info->cmd, INVOICE_LABEL_ALREADY_EXISTS, - "Duplicate label '%s'", - info->label->s)); - return; + return command_fail(info->cmd, INVOICE_LABEL_ALREADY_EXISTS, + "Duplicate label '%s'", + info->label->s); } /* Get details */ @@ -768,41 +845,60 @@ static void gossipd_incoming_channels_reply(struct subd *gossipd, 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_no_listincoming) + json_add_string(response, "warning_listincoming", + "No listincoming command available, cannot add routehints to invoice"); 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."); + if (warning_capacity) + json_add_string(response, "warning_capacity", + "Insufficient incoming channel capacity to pay invoice"); + + if (warning_deadends) + json_add_string(response, "warning_deadends", + "Insufficient incoming capacity, once dead-end peers were excluded"); + + if (warning_offline) + json_add_string(response, "warning_offline", + "Insufficient incoming capacity, once offline peers were excluded"); - was_pending(command_success(info->cmd, response)); + if (warning_private_unused) + json_add_string(response, "warning_private_unused", + "Insufficient incoming capacity, once private channels were excluded (try exposeprivatechannels=true?)"); + + return command_success(info->cmd, response); +} + +/* Return from "listincoming". */ +static void listincoming_done(const char *buffer, + const jsmntok_t *toks, + const jsmntok_t *idtok UNUSED, + struct invoice_info *info) +{ + struct lightningd *ld = info->cmd->ld; + struct command_result *ret; + bool warning_mpp, warning_capacity, warning_deadends, warning_offline, warning_private_unused; + + ret = add_routehints(info, buffer, toks, + &warning_mpp, + &warning_capacity, + &warning_deadends, + &warning_offline, + &warning_private_unused); + if (ret) + return; + + /* We're actually outside a db transaction here: spooky! */ + db_begin_transaction(ld->wallet->db); + invoice_complete(info, + false, + warning_mpp, + warning_capacity, + warning_deadends, + warning_offline, + warning_private_unused); + db_commit_transaction(ld->wallet->db); } #if DEVELOPER @@ -940,12 +1036,11 @@ static struct command_result *param_chanhints(struct command *cmd, /* 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); + (*chanhints)->hints = NULL; return NULL; } - (*chanhints)->expose_all_private = false; + (*chanhints)->expose_all_private = true; /* Could be a single short_channel_id or an array */ if (tok->type == JSMN_ARRAY) { size_t i; @@ -999,6 +1094,8 @@ static struct command_result *json_invoice(struct command *cmd, struct secret payment_secret; struct preimage *preimage; u32 *cltv; + struct jsonrpc_request *req; + struct plugin *plugin; #if DEVELOPER const jsmntok_t *routes; #endif @@ -1094,11 +1191,21 @@ static struct command_result *json_invoice(struct command *cmd, 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); + req = jsonrpc_request_start(info, "listincoming", + cmd->ld->log, + NULL, listincoming_done, + info); + jsonrpc_request_end(req); + + plugin = find_plugin_for_command(cmd->ld, "listincoming"); + if (plugin) { + plugin_request_send(plugin, req); + return command_still_pending(cmd); + } - return command_still_pending(cmd); + /* We can't generate routehints without listincoming. */ + return invoice_complete(info, true, + false, false, false, false, false); } static const struct json_command invoice_command = { diff --git a/lightningd/offer.c b/lightningd/offer.c index 4cb99370740b..d6a8f6a27665 100644 --- a/lightningd/offer.c +++ b/lightningd/offer.c @@ -236,7 +236,7 @@ static struct command_result *prev_payment(struct command *cmd, bool prev_paid = false; assert(!invreq->payer_info); - payments = wallet_payment_list(cmd, cmd->ld->wallet, NULL); + payments = wallet_payment_list(cmd, cmd->ld->wallet, NULL, NULL); for (size_t i = 0; i < tal_count(payments); i++) { const struct tlv_invoice *inv; diff --git a/lightningd/pay.c b/lightningd/pay.c index 778f955efb4a..e69ca6ec2aa8 100644 --- a/lightningd/pay.c +++ b/lightningd/pay.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -868,7 +869,7 @@ send_payment_core(struct lightningd *ld, bool have_complete = false; /* Now, do we already have one or more payments? */ - payments = wallet_payment_list(tmpctx, ld->wallet, rhash); + payments = wallet_payment_list(tmpctx, ld->wallet, rhash, NULL); for (size_t i = 0; i < tal_count(payments); i++) { log_debug(ld->log, "Payment %zu/%zu: %s %s", i, tal_count(payments), @@ -1524,12 +1525,13 @@ static struct command_result *json_listsendpays(struct command *cmd, const struct wallet_payment **payments; struct json_stream *response; struct sha256 *rhash; - const char *invstring; + const char *invstring, *status_str; if (!param(cmd, buffer, params, /* FIXME: parameter should be invstring now */ p_opt("bolt11", param_string, &invstring), p_opt("payment_hash", param_sha256, &rhash), + p_opt("status", param_string, &status_str), NULL)) return command_param_failed(); @@ -1561,7 +1563,14 @@ static struct command_result *json_listsendpays(struct command *cmd, } } - payments = wallet_payment_list(cmd, cmd->ld->wallet, rhash); + if (status_str) { + enum wallet_payment_status status; + + if (!string_to_payment_status(status_str, &status)) + return command_fail(cmd, JSONRPC2_INVALID_PARAMS, "Unrecognized status: %s", status_str); + payments = wallet_payment_list(cmd, cmd->ld->wallet, rhash, &status); + } else + payments = wallet_payment_list(cmd, cmd->ld->wallet, rhash, NULL); response = json_stream_success(cmd); @@ -1614,7 +1623,7 @@ static struct command_result *json_delpay(struct command *cmd, payment_status_to_string(status)); } - payments = wallet_payment_list(cmd, cmd->ld->wallet, payment_hash); + payments = wallet_payment_list(cmd, cmd->ld->wallet, payment_hash, NULL); if (tal_count(payments) == 0) return command_fail(cmd, PAY_NO_SUCH_PAYMENT, "Unknown payment with payment_hash: %s", diff --git a/lightningd/routehint.c b/lightningd/routehint.c index 284dedcae112..d87d0b73dd63 100644 --- a/lightningd/routehint.c +++ b/lightningd/routehint.c @@ -1,26 +1,12 @@ #include +#include #include #include +#include #include #include #include -static void append_routes(struct route_info **dst, const struct route_info *src) -{ - size_t n = tal_count(*dst); - - tal_resize(dst, n + tal_count(src)); - memcpy(*dst + n, src, tal_count(src) * sizeof(*src)); -} - -static void append_bools(bool **dst, const bool *src) -{ - size_t n = tal_count(*dst); - - tal_resize(dst, n + tal_count(src)); - memcpy(*dst + n, src, tal_count(src) * sizeof(*src)); -} - static bool scid_in_arr(const struct short_channel_id *scidarr, const struct short_channel_id *scid) { @@ -34,100 +20,204 @@ static bool scid_in_arr(const struct short_channel_id *scidarr, struct routehint_candidate * routehint_candidates(const tal_t *ctx, struct lightningd *ld, - const u8 *incoming_channels_reply, - bool expose_all_private, + const char *buf, + const jsmntok_t *toks, + const bool *expose_all_private, const struct short_channel_id *hints, bool *none_public, - bool *deadends, - struct amount_msat *amount_offline) + struct amount_msat *avail_capacity, + struct amount_msat *private_capacity, + struct amount_msat *deadend_capacity, + struct amount_msat *offline_capacity) { - struct routehint_candidate *candidates; - struct route_info *inchans, *private; - bool *inchan_deadends, *private_deadends; - - if (!fromwire_gossipd_get_incoming_channels_reply(tmpctx, - incoming_channels_reply, - &inchans, - &inchan_deadends, - &private, - &private_deadends)) - fatal("Gossip gave bad GOSSIPD_GET_INCOMING_CHANNELS_REPLY %s", - tal_hex(tmpctx, incoming_channels_reply)); - - *none_public = (tal_count(inchans) == 0) && (tal_count(private) > 0); - *deadends = false; - - /* fromwire explicitly makes empty arrays into NULL */ - if (!inchans) { - inchans = tal_arr(tmpctx, struct route_info, 0); - inchan_deadends = tal_arr(tmpctx, bool, 0); - } - - if (expose_all_private) { - append_routes(&inchans, private); - append_bools(&inchan_deadends, private_deadends); - } else if (hints) { - /* Start by considering all channels as candidates */ - append_routes(&inchans, private); - append_bools(&inchan_deadends, private_deadends); - - /* Consider only hints they gave */ - for (size_t i = 0; i < tal_count(inchans); i++) { - if (!scid_in_arr(hints, - &inchans[i].short_channel_id)) { - tal_arr_remove(&inchans, i); - tal_arr_remove(&inchan_deadends, i); - i--; - } else - /* If they specify directly, we don't - * care if it's a deadend */ - inchan_deadends[i] = false; - } - } else { - assert(!hints); - /* By default, only consider private channels if there are - * no public channels *at all* */ - if (tal_count(inchans) == 0) { - append_routes(&inchans, private); - append_bools(&inchan_deadends, private_deadends); - } - } + struct routehint_candidate *candidates, *privcandidates; + const jsmntok_t *t, *arr; + size_t i; + + log_debug(ld->log, "routehint: %.*s", + json_tok_full_len(toks), + json_tok_full(buf, toks)); + + /* We get the full JSON, including result. */ + t = json_get_member(buf, toks, "result"); + if (!t) + fatal("Missing result from listincoming: %.*s", + json_tok_full_len(toks), + json_tok_full(buf, toks)); + arr = json_get_member(buf, t, "incoming"); candidates = tal_arr(ctx, struct routehint_candidate, 0); - *amount_offline = AMOUNT_MSAT(0); - - for (size_t i = 0; i < tal_count(inchans); i++) { - struct peer *peer; + privcandidates = tal_arr(tmpctx, struct routehint_candidate, 0); + *none_public = true; + *deadend_capacity = AMOUNT_MSAT(0); + *offline_capacity = AMOUNT_MSAT(0); + *avail_capacity = AMOUNT_MSAT(0); + *private_capacity = AMOUNT_MSAT(0); + + /* We combine the JSON output which knows the peers' details, + * with our internal information */ + json_for_each_arr(i, t, arr) { + struct amount_msat capacity; + const char *err; struct routehint_candidate candidate; + struct amount_msat fee_base; + struct route_info *r; + struct peer *peer; + bool is_public; + + r = tal(tmpctx, struct route_info); + + err = json_scan(tmpctx, buf, t, + "{id:%" + ",short_channel_id:%" + ",fee_base_msat:%" + ",fee_proportional_millionths:%" + ",cltv_expiry_delta:%" + ",incoming_capacity_msat:%" + "}", + JSON_SCAN(json_to_node_id, &r->pubkey), + JSON_SCAN(json_to_short_channel_id, + &r->short_channel_id), + JSON_SCAN(json_to_msat, &fee_base), + JSON_SCAN(json_to_u32, + &r->fee_proportional_millionths), + JSON_SCAN(json_to_u16, &r->cltv_expiry_delta), + JSON_SCAN(json_to_msat, &capacity)); + + if (err) { + fatal("Invalid return from listincoming (%s): %.*s", + err, + json_tok_full_len(toks), + json_tok_full(buf, toks)); + } /* Do we know about this peer? */ - peer = peer_by_id(ld, &inchans[i].pubkey); - if (!peer) + peer = peer_by_id(ld, &r->pubkey); + if (!peer) { + log_debug(ld->log, "%s: unknown peer", + type_to_string(tmpctx, + struct short_channel_id, + &r->short_channel_id)); continue; + } /* Does it have a channel in state CHANNELD_NORMAL */ candidate.c = peer_normal_channel(peer); - if (!candidate.c) + if (!candidate.c) { + log_debug(ld->log, "%s: abnormal channel", + type_to_string(tmpctx, + struct short_channel_id, + &r->short_channel_id)); continue; + } + + candidate.capacity = channel_amount_receivable(candidate.c); - /* Is it a dead-end? */ - if (inchan_deadends[i]) { - *deadends = true; + /* Now we can tell if it's public. If so (even if it's otherwise + * unusable), we *don't* expose private channels! */ + is_public = (candidate.c->channel_flags + & CHANNEL_FLAGS_ANNOUNCE_CHANNEL); + + if (is_public) + *none_public = false; + + /* If they explicitly say to expose all private ones, consider + * it public. */ + if (expose_all_private != NULL && *expose_all_private) + is_public = true; + + r->fee_base_msat = fee_base.millisatoshis; /* Raw: route_info */ + /* Could wrap: if so ignore */ + if (!amount_msat_eq(amount_msat(r->fee_base_msat), fee_base)) { + log_debug(ld->log, + "Peer charging insane fee %.*s; ignoring", + json_tok_full_len(t), + json_tok_full(buf, t)); continue; } - candidate.capacity = channel_amount_receivable(candidate.c); + /* Consider only hints they gave */ + if (hints) { + log_debug(ld->log, "We have hints!"); + if (!scid_in_arr(hints, &r->short_channel_id)) { + log_debug(ld->log, "scid %s not in hints", + type_to_string(tmpctx, + struct short_channel_id, + &r->short_channel_id)); + continue; + } + /* If they give us a hint, we use even if capacity 0 */ + } else if (amount_msat_eq(capacity, AMOUNT_MSAT(0))) { + log_debug(ld->log, "%s: deadend", + type_to_string(tmpctx, + struct short_channel_id, + &r->short_channel_id)); + if (!amount_msat_add(deadend_capacity, + *deadend_capacity, + candidate.capacity)) + fatal("Overflow summing deadend capacity!"); + continue; + } /* Is it offline? */ if (candidate.c->owner == NULL) { - if (!amount_msat_add(amount_offline, - *amount_offline, + log_debug(ld->log, "%s: offline", + type_to_string(tmpctx, + struct short_channel_id, + &r->short_channel_id)); + if (!amount_msat_add(offline_capacity, + *offline_capacity, candidate.capacity)) fatal("Overflow summing offline capacity!"); continue; } - candidate.r = &inchans[i]; - tal_arr_expand(&candidates, candidate); + + /* OK, finish it and append to one of the arrays. */ + if (is_public) { + log_debug(ld->log, "%s: added to public", + type_to_string(tmpctx, + struct short_channel_id, + &r->short_channel_id)); + candidate.r = tal_steal(candidates, r); + tal_arr_expand(&candidates, candidate); + if (!amount_msat_add(avail_capacity, + *avail_capacity, + candidate.capacity)) { + fatal("Overflow summing pub capacities %s + %s", + type_to_string(tmpctx, struct amount_msat, + avail_capacity), + type_to_string(tmpctx, struct amount_msat, + &candidate.capacity)); + } + } else { + log_debug(ld->log, "%s: added to private", + type_to_string(tmpctx, + struct short_channel_id, + &r->short_channel_id)); + candidate.r = tal_steal(privcandidates, r); + tal_arr_expand(&privcandidates, candidate); + if (!amount_msat_add(private_capacity, + *private_capacity, + candidate.capacity)) { + fatal("Overflow summing priv capacities %s + %s", + type_to_string(tmpctx, struct amount_msat, + private_capacity), + type_to_string(tmpctx, struct amount_msat, + &candidate.capacity)); + } + } + } + + /* By default, only consider private channels if there are + * no public channels *at all* */ + if (expose_all_private == NULL + && tal_count(candidates) == 0 && *none_public) { + log_debug(ld->log, "No publics: using private channels"); + tal_free(candidates); + candidates = tal_steal(ctx, privcandidates); + *avail_capacity = *private_capacity; + /* This reflects *unused* private capacity. */ + *private_capacity = AMOUNT_MSAT(0); } return candidates; diff --git a/lightningd/routehint.h b/lightningd/routehint.h index 8eb8476ec99f..27eadca688e2 100644 --- a/lightningd/routehint.h +++ b/lightningd/routehint.h @@ -20,21 +20,26 @@ struct routehint_candidate { * routehint_candidates - get possible incoming channels for routehinting. * @ctx: tal context to allocate return off * @ld: lightningd - * @incoming_channels_reply: reply from gossipd get_incoming_channels - * @expose_all_private: consider private channels too (otherwise iff no public) + * @buf, @toks: output of listincoming command + * @expose_all_private: trinary. NULL=iff no public, true=always, false=never. * @hints: only consider these channels (if !expose_all_private). * @none_public: set to true if we used private channels because none were public. - * @deadends: set to true if we found a dead-end channel. - * @amount_offline: amount we didn't consider due to offline channels. + * @avail_capacity: total capacity of usable channels. + * @private_capacity: total capacity of unused private channels. + * @deadend_capacity: total capacity of "deadend" channels. + * @offline_capacity: total capacity of offline channels. */ struct routehint_candidate * routehint_candidates(const tal_t *ctx, struct lightningd *ld, - const u8 *incoming_channels_reply, - bool expose_all_private, + const char *buf, + const jsmntok_t *toks, + const bool *expose_all_private, const struct short_channel_id *hints, bool *none_public, - bool *deadends, - struct amount_msat *amount_offline); + struct amount_msat *avail_capacity, + struct amount_msat *private_capacity, + struct amount_msat *deadend_capacity, + struct amount_msat *offline_capacity); #endif /* LIGHTNING_LIGHTNINGD_ROUTEHINT_H */ diff --git a/lightningd/signmessage.c b/lightningd/signmessage.c index 92d3c91f9f5b..ae9047208ba0 100644 --- a/lightningd/signmessage.c +++ b/lightningd/signmessage.c @@ -129,24 +129,22 @@ struct command_and_node { struct node_id id; }; -/* Gossipd tells us if it's a known node by returning details. */ -static void getnode_reply(struct subd *gossip UNUSED, const u8 *reply, - const int *fds UNUSED, - struct command_and_node *can) +/* topology tells us if it's a known node by returning details. */ +static void listnodes_done(const char *buffer, + const jsmntok_t *toks, + const jsmntok_t *idtok UNUSED, + struct command_and_node *can) { - struct gossip_getnodes_entry **nodes; struct json_stream *response; + const jsmntok_t *t; - if (!fromwire_gossipd_getnodes_reply(reply, reply, &nodes)) { - log_broken(can->cmd->ld->log, - "Malformed gossip_getnodes response %s", - tal_hex(tmpctx, reply)); - nodes = NULL; - } + t = json_get_member(buffer, toks, "result"); + if (t) + t = json_get_member(buffer, t, "nodes"); response = json_stream_success(can->cmd); json_add_node_id(response, "pubkey", &can->id); - json_add_bool(response, "verified", tal_count(nodes) > 0); + json_add_bool(response, "verified", t && t->size == 1); was_pending(command_success(can->cmd, response)); } @@ -207,19 +205,31 @@ static struct command_result *json_checkmessage(struct command *cmd, * make two (different) signed messages with the same recovered key * unless you know the secret key */ if (!pubkey) { - u8 *req; + struct jsonrpc_request *req; + struct plugin *plugin; struct command_and_node *can = tal(cmd, struct command_and_node); node_id_from_pubkey(&can->id, &reckey); can->cmd = cmd; - req = towire_gossipd_getnodes_request(cmd, &can->id); - subd_req(cmd, cmd->ld->gossip, req, -1, 0, getnode_reply, can); - return command_still_pending(cmd); + req = jsonrpc_request_start(cmd, "listnodes", + cmd->ld->log, + NULL, listnodes_done, + can); + json_add_node_id(req->stream, "id", &can->id); + jsonrpc_request_end(req); + + /* Only works if we have listnodes! */ + plugin = find_plugin_for_command(cmd->ld, "listnodes"); + if (plugin) { + plugin_request_send(plugin, req); + return command_still_pending(cmd); + } } response = json_stream_success(cmd); json_add_pubkey(response, "pubkey", &reckey); - json_add_bool(response, "verified", pubkey_eq(pubkey, &reckey)); + json_add_bool(response, "verified", + pubkey && pubkey_eq(pubkey, &reckey)); return command_success(cmd, response); } diff --git a/lightningd/test/run-invoice-select-inchan.c b/lightningd/test/run-invoice-select-inchan.c index 8a4fd854e092..34b7e7e56b7d 100644 --- a/lightningd/test/run-invoice-select-inchan.c +++ b/lightningd/test/run-invoice-select-inchan.c @@ -160,6 +160,12 @@ void connect_succeeded(struct lightningd *ld UNNEEDED, const struct peer *peer U bool incoming UNNEEDED, const struct wireaddr_internal *addr UNNEEDED) { fprintf(stderr, "connect_succeeded called!\n"); abort(); } +/* Generated stub for db_begin_transaction_ */ +void db_begin_transaction_(struct db *db UNNEEDED, const char *location UNNEEDED) +{ fprintf(stderr, "db_begin_transaction_ called!\n"); abort(); } +/* Generated stub for db_commit_transaction */ +void db_commit_transaction(struct db *db UNNEEDED) +{ fprintf(stderr, "db_commit_transaction 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) @@ -191,6 +197,10 @@ bool feature_negotiated(const struct feature_set *our_features UNNEEDED, /* Generated stub for feature_offered */ bool feature_offered(const u8 *features UNNEEDED, size_t f UNNEEDED) { fprintf(stderr, "feature_offered called!\n"); abort(); } +/* Generated stub for find_plugin_for_command */ +struct plugin *find_plugin_for_command(struct lightningd *ld UNNEEDED, + const char *cmd_name UNNEEDED) +{ fprintf(stderr, "find_plugin_for_command called!\n"); abort(); } /* Generated stub for fixup_htlcs_out */ void fixup_htlcs_out(struct lightningd *ld UNNEEDED) { fprintf(stderr, "fixup_htlcs_out called!\n"); abort(); } @@ -207,9 +217,6 @@ bool fromwire_channeld_dev_memleak_reply(const void *p UNNEEDED, bool *leak UNNE /* Generated stub for fromwire_connectd_peer_connected */ bool fromwire_connectd_peer_connected(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct node_id *id UNNEEDED, struct wireaddr_internal *addr UNNEEDED, bool *incoming UNNEEDED, struct per_peer_state **pps UNNEEDED, u8 **features UNNEEDED) { fprintf(stderr, "fromwire_connectd_peer_connected called!\n"); abort(); } -/* Generated stub for fromwire_gossipd_get_incoming_channels_reply */ -bool fromwire_gossipd_get_incoming_channels_reply(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct route_info **public_route_info UNNEEDED, bool **public_deadends UNNEEDED, struct route_info **private_route_info UNNEEDED, bool **private_deadends UNNEEDED) -{ fprintf(stderr, "fromwire_gossipd_get_incoming_channels_reply called!\n"); abort(); } /* Generated stub for fromwire_hsmd_sign_bolt12_reply */ bool fromwire_hsmd_sign_bolt12_reply(const void *p UNNEEDED, struct bip340sig *sig UNNEEDED) { fprintf(stderr, "fromwire_hsmd_sign_bolt12_reply called!\n"); abort(); } @@ -389,6 +396,10 @@ enum address_parse_result json_to_address_scriptpubkey(const tal_t *ctx UNNEEDED const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, const u8 **scriptpubkey UNNEEDED) { fprintf(stderr, "json_to_address_scriptpubkey called!\n"); abort(); } +/* Generated stub for json_to_msat */ +bool json_to_msat(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, + struct amount_msat *msat UNNEEDED) +{ fprintf(stderr, "json_to_msat called!\n"); abort(); } /* Generated stub for json_to_node_id */ bool json_to_node_id(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, struct node_id *id UNNEEDED) @@ -405,6 +416,21 @@ bool json_to_short_channel_id(const char *buffer UNNEEDED, const jsmntok_t *tok 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 jsonrpc_request_end */ +void jsonrpc_request_end(struct jsonrpc_request *request UNNEEDED) +{ fprintf(stderr, "jsonrpc_request_end called!\n"); abort(); } +/* Generated stub for jsonrpc_request_start_ */ +struct jsonrpc_request *jsonrpc_request_start_( + const tal_t *ctx UNNEEDED, const char *method UNNEEDED, struct log *log UNNEEDED, + void (*notify_cb)(const char *buffer UNNEEDED, + const jsmntok_t *idtok UNNEEDED, + const jsmntok_t *methodtok UNNEEDED, + const jsmntok_t *paramtoks UNNEEDED, + void *) UNNEEDED, + void (*response_cb)(const char *buffer UNNEEDED, const jsmntok_t *toks UNNEEDED, + const jsmntok_t *idtok UNNEEDED, void *) UNNEEDED, + void *response_cb_arg UNNEEDED) +{ fprintf(stderr, "jsonrpc_request_start_ called!\n"); abort(); } /* Generated stub for kill_uncommitted_channel */ void kill_uncommitted_channel(struct uncommitted_channel *uc UNNEEDED, const char *why UNNEEDED) @@ -601,6 +627,10 @@ void per_peer_state_set_fds(struct per_peer_state *pps UNNEEDED, bool plugin_hook_call_(struct lightningd *ld UNNEEDED, const struct plugin_hook *hook UNNEEDED, tal_t *cb_arg STEALS UNNEEDED) { fprintf(stderr, "plugin_hook_call_ called!\n"); abort(); } +/* Generated stub for plugin_request_send */ +void plugin_request_send(struct plugin *plugin UNNEEDED, + struct jsonrpc_request *req TAKES UNNEEDED) +{ fprintf(stderr, "plugin_request_send called!\n"); abort(); } /* Generated stub for subd_req_ */ void subd_req_(const tal_t *ctx UNNEEDED, struct subd *sd UNNEEDED, @@ -647,9 +677,6 @@ 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_gossipd_get_incoming_channels */ -u8 *towire_gossipd_get_incoming_channels(const tal_t *ctx UNNEEDED) -{ fprintf(stderr, "towire_gossipd_get_incoming_channels called!\n"); abort(); } /* Generated stub for towire_hsmd_sign_bolt12 */ u8 *towire_hsmd_sign_bolt12(const tal_t *ctx UNNEEDED, const wirestring *messagename UNNEEDED, const wirestring *fieldname UNNEEDED, const struct sha256 *merkleroot UNNEEDED, const u8 *publictweak UNNEEDED) { fprintf(stderr, "towire_hsmd_sign_bolt12 called!\n"); abort(); } diff --git a/plugins/Makefile b/plugins/Makefile index 410a2a7be9e6..c13808bbc7b8 100644 --- a/plugins/Makefile +++ b/plugins/Makefile @@ -4,6 +4,9 @@ PLUGIN_PAY_OBJS := $(PLUGIN_PAY_SRC:.c=.o) PLUGIN_AUTOCLEAN_SRC := plugins/autoclean.c PLUGIN_AUTOCLEAN_OBJS := $(PLUGIN_AUTOCLEAN_SRC:.c=.o) +PLUGIN_TOPOLOGY_SRC := plugins/topology.c +PLUGIN_TOPOLOGY_OBJS := $(PLUGIN_TOPOLOGY_SRC:.c=.o) + PLUGIN_TXPREPARE_SRC := plugins/txprepare.c PLUGIN_TXPREPARE_OBJS := $(PLUGIN_TXPREPARE_SRC:.c=.o) @@ -55,6 +58,7 @@ PLUGIN_ALL_SRC := \ $(PLUGIN_BCLI_SRC) \ $(PLUGIN_FETCHINVOICE_SRC) \ $(PLUGIN_FUNDER_SRC) \ + $(PLUGIN_TOPOLOGY_SRC) \ $(PLUGIN_KEYSEND_SRC) \ $(PLUGIN_TXPREPARE_SRC) \ $(PLUGIN_LIB_SRC) \ @@ -76,6 +80,7 @@ PLUGINS := \ plugins/bcli \ plugins/fetchinvoice \ plugins/funder \ + plugins/topology \ plugins/keysend \ plugins/offers \ plugins/pay \ @@ -135,6 +140,10 @@ $(PLUGIN_PAY_OBJS): $(PLUGIN_PAY_LIB_HEADER) plugins/autoclean: bitcoin/chainparams.o $(PLUGIN_AUTOCLEAN_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_COMMON_OBJS) $(JSMN_OBJS) $(CCAN_OBJS) +# Topology wants to decode node_announcement, and peer_wiregen which +# pulls in some of bitcoin/. +plugins/topology: common/route.o common/dijkstra.o common/gossmap.o common/fp16.o bitcoin/chainparams.o wire/peer$(EXP)_wiregen.o bitcoin/block.o bitcoin/preimage.o $(PLUGIN_TOPOLOGY_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_COMMON_OBJS) $(JSMN_OBJS) $(CCAN_OBJS) + plugins/txprepare: bitcoin/chainparams.o $(PLUGIN_TXPREPARE_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_COMMON_OBJS) $(JSMN_OBJS) $(CCAN_OBJS) plugins/bcli: bitcoin/chainparams.o $(PLUGIN_BCLI_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_COMMON_OBJS) $(JSMN_OBJS) $(CCAN_OBJS) diff --git a/plugins/pay.c b/plugins/pay.c index 95d09ba84bf3..80c237280e26 100644 --- a/plugins/pay.c +++ b/plugins/pay.c @@ -1905,7 +1905,7 @@ static struct command_result *json_listpays(struct command *cmd, const char *buf, const jsmntok_t *params) { - const char *invstring; + const char *invstring, *status; struct sha256 *payment_hash; struct out_req *req; @@ -1914,6 +1914,7 @@ static struct command_result *json_listpays(struct command *cmd, /* FIXME: parameter should be invstring now */ p_opt("bolt11", param_string, &invstring), p_opt("payment_hash", param_sha256, &payment_hash), + p_opt("status", param_string, &status), NULL)) return command_param_failed(); @@ -1926,6 +1927,9 @@ static struct command_result *json_listpays(struct command *cmd, if (payment_hash) json_add_sha256(req->js, "payment_hash", payment_hash); + if(status) + json_add_string(req->js, "status", status); + return send_outreq(cmd->plugin, req); } diff --git a/plugins/topology.c b/plugins/topology.c new file mode 100644 index 000000000000..2fbe1db76bd3 --- /dev/null +++ b/plugins/topology.c @@ -0,0 +1,721 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Access via get_gossmap() */ +static struct gossmap *global_gossmap; +static struct node_id local_id; +static struct plugin *plugin; + +/* We load this on demand, since we can start before gossipd. */ +static struct gossmap *get_gossmap(void) +{ + gossmap_refresh(global_gossmap); + return global_gossmap; +} + +/* Convenience global since route_score_fuzz doesn't take args. 0 to 1. */ +static double fuzz; + +enum exclude_entry_type { + EXCLUDE_CHANNEL = 1, + EXCLUDE_NODE = 2 +}; + +struct exclude_entry { + enum exclude_entry_type type; + union { + struct short_channel_id_dir chan_id; + struct node_id node_id; + } u; +}; + +/* Prioritize costs over distance, but with fuzz. Cost must be + * the same when the same channel queried, so we base it on that. */ +static u64 route_score_fuzz(u32 distance, + struct amount_msat cost, + struct amount_msat risk, + const struct gossmap_chan *c) +{ + u64 costs = cost.millisatoshis + risk.millisatoshis; /* Raw: score */ + /* Use the literal pointer, since it's stable. */ + u64 h = siphash24(siphash_seed(), &c, sizeof(c)); + + /* Use distance as the tiebreaker */ + costs += distance; + + /* h / (UINT64_MAX / 2.0) is between 0 and 2. */ + costs *= (h / (double)(UINT64_MAX / 2) - 1) * fuzz; + + return costs; +} + +static bool can_carry(const struct gossmap *map, + const struct gossmap_chan *c, + int dir, + struct amount_msat amount, + const struct exclude_entry **excludes) +{ + struct node_id dstid; + + /* First do generic check */ + if (!route_can_carry(map, c, dir, amount, NULL)) { + plugin_log(plugin, LOG_DBG, "cannot carry %s across %p", + type_to_string(tmpctx, struct amount_msat, &amount), + c); + return false; + } + + /* Now check exclusions. Premature optimization: */ + if (!tal_count(excludes)) { + plugin_log(plugin, LOG_DBG, "CAN carry %s across %p", + type_to_string(tmpctx, struct amount_msat, &amount), + c); + return true; + } + + gossmap_node_get_id(map, gossmap_nth_node(map, c, !dir), &dstid); + for (size_t i = 0; i < tal_count(excludes); i++) { + struct short_channel_id scid; + + switch (excludes[i]->type) { + case EXCLUDE_CHANNEL: + scid = gossmap_chan_scid(map, c); + if (short_channel_id_eq(&excludes[i]->u.chan_id.scid, &scid) + && dir == excludes[i]->u.chan_id.dir) + return false; + continue; + + case EXCLUDE_NODE: + if (node_id_eq(&dstid, &excludes[i]->u.node_id)) + return false; + continue; + } + /* No other cases should be possible! */ + plugin_err(plugin, "Invalid type %i in exclusion[%zu]", + excludes[i]->type, i); + } + return true; +} + +static void json_add_route_hop_style(struct json_stream *response, + const char *fieldname, + enum route_hop_style style) +{ + switch (style) { + case ROUTE_HOP_LEGACY: + json_add_string(response, fieldname, "legacy"); + return; + case ROUTE_HOP_TLV: + json_add_string(response, fieldname, "tlv"); + return; + } + abort(); +} + +/* Output a route hop */ +static void json_add_route_hop(struct json_stream *js, + const char *fieldname, + const struct route_hop *r) +{ + /* Imitate what getroute/sendpay use */ + json_object_start(js, fieldname); + json_add_node_id(js, "id", &r->node_id); + json_add_short_channel_id(js, "channel", &r->scid); + json_add_num(js, "direction", r->direction); + json_add_amount_msat_compat(js, r->amount, "msatoshi", "amount_msat"); + json_add_num(js, "delay", r->delay); + json_add_route_hop_style(js, "style", r->style); + json_object_end(js); +} + +static struct command_result *json_getroute(struct command *cmd, + const char *buffer, + const jsmntok_t *params) +{ + struct node_id *destination; + struct node_id *source; + const jsmntok_t *excludetok; + struct amount_msat *msat; + u32 *cltv; + /* risk factor 12.345% -> riskfactor_millionths = 12345000 */ + u64 *riskfactor_millionths, *fuzz_millionths; + const struct exclude_entry **excluded; + u32 *max_hops; + const struct dijkstra *dij; + struct route_hop *route; + struct gossmap_node *src, *dst; + struct json_stream *js; + struct gossmap *gossmap; + + if (!param(cmd, buffer, params, + p_req("id", param_node_id, &destination), + p_req("msatoshi", param_msat, &msat), + p_req("riskfactor", param_millionths, &riskfactor_millionths), + p_opt_def("cltv", param_number, &cltv, 9), + p_opt_def("fromid", param_node_id, &source, local_id), + p_opt_def("fuzzpercent", param_millionths, &fuzz_millionths, + 5000000), + p_opt("exclude", param_array, &excludetok), + p_opt_def("maxhops", param_number, &max_hops, ROUTING_MAX_HOPS), + NULL)) + return command_param_failed(); + + /* Convert from percentage */ + fuzz = *fuzz_millionths / 100.0 / 1000000.0; + if (fuzz > 1.0) + return command_fail_badparam(cmd, "fuzzpercent", + buffer, params, + "should be <= 100"); + + if (excludetok) { + const jsmntok_t *t; + size_t i; + + excluded = tal_arr(cmd, const struct exclude_entry *, 0); + + json_for_each_arr(i, t, excludetok) { + struct exclude_entry *entry = tal(excluded, struct exclude_entry); + struct short_channel_id_dir *chan_id = tal(tmpctx, struct short_channel_id_dir); + if (!short_channel_id_dir_from_str(buffer + t->start, + t->end - t->start, + chan_id)) { + struct node_id *node_id = tal(tmpctx, struct node_id); + + if (!json_to_node_id(buffer, t, node_id)) + return command_fail_badparam(cmd, "exclude", + buffer, t, + "should be short_channel_id or node_id"); + + entry->type = EXCLUDE_NODE; + entry->u.node_id = *node_id; + } else { + entry->type = EXCLUDE_CHANNEL; + entry->u.chan_id = *chan_id; + } + + tal_arr_expand(&excluded, entry); + } + } else { + excluded = NULL; + } + + gossmap = get_gossmap(); + src = gossmap_find_node(gossmap, source); + if (!src) + return command_fail(cmd, JSONRPC2_INVALID_PARAMS, + "%s: unknown source node_id (no public channels?)", + type_to_string(tmpctx, struct node_id, source)); + + dst = gossmap_find_node(gossmap, destination); + if (!dst) + return command_fail(cmd, JSONRPC2_INVALID_PARAMS, + "%s: unknown destination node_id (no public channels?)", + type_to_string(tmpctx, struct node_id, destination)); + + fuzz = 0; + dij = dijkstra(tmpctx, gossmap, dst, *msat, + *riskfactor_millionths / 1000000.0, + can_carry, route_score_fuzz, excluded); + route = route_from_dijkstra(dij, gossmap, dij, src, *msat, *cltv); + if (!route) + return command_fail(cmd, PAY_ROUTE_NOT_FOUND, "Could not find a route"); + + /* If it's too far, fall back to using shortest path. */ + if (tal_count(route) > *max_hops) { + plugin_notify_message(cmd, LOG_INFORM, "Cheapest route %zu hops: seeking shorter (no fuzz)", + tal_count(route)); + dij = dijkstra(tmpctx, gossmap, dst, *msat, + *riskfactor_millionths / 1000000.0, + can_carry, route_score_shorter, excluded); + route = route_from_dijkstra(dij, gossmap, dij, src, *msat, *cltv); + if (tal_count(route) > *max_hops) + return command_fail(cmd, PAY_ROUTE_NOT_FOUND, "Shortest route was %zu", + tal_count(route)); + } + + js = jsonrpc_stream_success(cmd); + json_array_start(js, "route"); + for (size_t i = 0; i < tal_count(route); i++) { + json_add_route_hop(js, NULL, &route[i]); + } + json_array_end(js); + + return command_finished(cmd, js); +} + +static const struct node_id *node_id_keyof(const struct node_id *id) +{ + return id; +} + +static size_t node_id_hash(const struct node_id *id) +{ + return siphash24(siphash_seed(), id->k, sizeof(id->k)); +} + + +HTABLE_DEFINE_TYPE(struct node_id, node_id_keyof, node_id_hash, node_id_eq, + node_map); + +/* To avoid multiple fetches, we represent directions as a bitmap + * so we can do two at once. */ +static void json_add_halfchan(struct json_stream *response, + struct gossmap *gossmap, + const struct node_map *connected, + const struct gossmap_chan *c, + int dirbits) +{ + struct short_channel_id scid; + struct node_id node_id[2]; + const u8 *chanfeatures; + struct amount_sat capacity; + bool local_disable; + + /* These are channel (not per-direction) properties */ + chanfeatures = gossmap_chan_get_features(tmpctx, gossmap, c); + scid = gossmap_chan_scid(gossmap, c); + for (size_t i = 0; i < 2; i++) + gossmap_node_get_id(gossmap, gossmap_nth_node(gossmap, c, i), + &node_id[i]); + + /* This can theoretically happen on partial write races. */ + if (!gossmap_chan_get_capacity(gossmap, c, &capacity)) + capacity = AMOUNT_SAT(0); + + /* Local channels are not "active" unless peer is connected. */ + if (node_id_eq(&node_id[0], &local_id)) + local_disable = !node_map_get(connected, &node_id[1]); + else if (node_id_eq(&node_id[1], &local_id)) + local_disable = !node_map_get(connected, &node_id[0]); + else + local_disable = false; + + for (int dir = 0; dir < 2; dir++) { + u32 timestamp; + u8 message_flags, channel_flags; + u32 fee_base_msat, fee_proportional_millionths; + struct amount_msat htlc_minimum_msat, htlc_maximum_msat; + + if (!((1 << dir) & dirbits)) + continue; + + if (!gossmap_chan_set(c, dir)) + continue; + + json_object_start(response, NULL); + json_add_node_id(response, "source", &node_id[dir]); + json_add_node_id(response, "destination", &node_id[!dir]); + json_add_short_channel_id(response, "short_channel_id", &scid); + json_add_bool(response, "public", !c->private); + + gossmap_chan_get_update_details(gossmap, c, dir, + ×tamp, + &message_flags, + &channel_flags, + &fee_base_msat, + &fee_proportional_millionths, + &htlc_minimum_msat, + &htlc_maximum_msat); + + json_add_amount_sat_compat(response, capacity, + "satoshis", "amount_msat"); + json_add_num(response, "message_flags", message_flags); + json_add_num(response, "channel_flags", channel_flags); + + json_add_bool(response, "active", + c->half[dir].enabled && !local_disable); + json_add_num(response, "last_update", timestamp); + json_add_num(response, "base_fee_millisatoshi", fee_base_msat); + json_add_num(response, "fee_per_millionth", + fee_proportional_millionths); + json_add_num(response, "delay", c->half[dir].delay); + json_add_amount_msat_only(response, "htlc_minimum_msat", + htlc_minimum_msat); + + /* We used to always print this, but that's weird */ + if (deprecated_apis && !(message_flags & 1)) { + if (!amount_sat_to_msat(&htlc_maximum_msat, capacity)) + plugin_err(plugin, + "Channel with impossible capacity %s", + type_to_string(tmpctx, + struct amount_sat, + &capacity)); + message_flags = 1; + } + + if (message_flags & 1) + json_add_amount_msat_only(response, "htlc_maximum_msat", + htlc_maximum_msat); + json_add_hex_talarr(response, "features", chanfeatures); + json_object_end(response); + } +} + +struct listchannels_opts { + struct node_id *source; + struct short_channel_id *scid; +}; + +/* We record which local channels are valid; we could record which are + * invalid, but our testsuite has some weirdness where it has local + * channels in the store it knows nothing about. */ +static struct node_map *local_connected(const tal_t *ctx, + const char *buf, + const jsmntok_t *result) +{ + size_t i; + const jsmntok_t *t, *peers = json_get_member(buf, result, "peers"); + struct node_map *connected = tal(ctx, struct node_map); + + node_map_init(connected); + + json_for_each_arr(i, t, peers) { + const jsmntok_t *chans, *c; + struct node_id id; + bool is_connected, normal_chan; + const char *err; + size_t j; + + err = json_scan(tmpctx, buf, t, + "{id:%,connected:%}", + JSON_SCAN(json_to_node_id, &id), + JSON_SCAN(json_to_bool, &is_connected)); + if (err) + plugin_err(plugin, "Bad listpeers response (%s): %.*s", + err, + json_tok_full_len(result), + json_tok_full(buf, result)); + + if (!is_connected) + continue; + + /* Must also have a channel in CHANNELD_NORMAL */ + normal_chan = false; + chans = json_get_member(buf, t, "channels"); + json_for_each_arr(j, c, chans) { + if (json_tok_streq(buf, + json_get_member(buf, c, "state"), + "CHANNELD_NORMAL")) + normal_chan = true; + } + + if (normal_chan) + node_map_add(connected, + tal_dup(connected, struct node_id, &id)); + } + + return connected; +} + +/* We want to combine local knowledge to we know which are actually inactive! */ +static struct command_result *listpeers_done(struct command *cmd, + const char *buf, + const jsmntok_t *result, + struct listchannels_opts *opts) +{ + struct node_map *connected; + struct gossmap_chan *c; + struct json_stream *js; + struct gossmap *gossmap = get_gossmap(); + + connected = local_connected(opts, buf, result); + + js = jsonrpc_stream_success(cmd); + json_array_start(js, "channels"); + if (opts->scid) { + c = gossmap_find_chan(gossmap, opts->scid); + if (c) + json_add_halfchan(js, gossmap, connected, c, 3); + } else if (opts->source) { + struct gossmap_node *src; + + src = gossmap_find_node(gossmap, opts->source); + if (src) { + for (size_t i = 0; i < src->num_chans; i++) { + int dir; + c = gossmap_nth_chan(gossmap, src, i, &dir); + json_add_halfchan(js, gossmap, connected, + c, 1 << dir); + } + } + } else { + for (c = gossmap_first_chan(gossmap); + c; + c = gossmap_next_chan(gossmap, c)) { + json_add_halfchan(js, gossmap, connected, c, 3); + } + } + + json_array_end(js); + + return command_finished(cmd, js); +} + +static struct command_result *json_listchannels(struct command *cmd, + const char *buffer, + const jsmntok_t *params) +{ + struct listchannels_opts *opts = tal(cmd, struct listchannels_opts); + struct out_req *req; + + if (!param(cmd, buffer, params, + p_opt("short_channel_id", param_short_channel_id, + &opts->scid), + p_opt("source", param_node_id, &opts->source), + NULL)) + return command_param_failed(); + + if (opts->scid && opts->source) + return command_fail(cmd, JSONRPC2_INVALID_PARAMS, + "Cannot specify both source and short_channel_id"); + req = jsonrpc_request_start(cmd->plugin, cmd, "listpeers", + listpeers_done, forward_error, opts); + return send_outreq(cmd->plugin, req); +} + +static void json_add_node(struct json_stream *js, + const struct gossmap *gossmap, + const struct gossmap_node *n) +{ + struct node_id node_id; + u8 *nannounce; + + json_object_start(js, NULL); + gossmap_node_get_id(gossmap, n, &node_id); + json_add_node_id(js, "nodeid", &node_id); + nannounce = gossmap_node_get_announce(tmpctx, gossmap, n); + if (nannounce) { + secp256k1_ecdsa_signature signature; + u8 *features; + u32 timestamp; + u8 rgb_color[3], alias[32]; + u8 *addresses; + struct node_id nid; + struct wireaddr *addrs; + struct json_escape *esc; + + if (!fromwire_node_announcement(nannounce, nannounce, + &signature, + &features, + ×tamp, + &nid, + rgb_color, + alias, + &addresses)) { + plugin_log(plugin, LOG_BROKEN, + "Cannot parse stored node_announcement" + " for %s at %u: %s", + type_to_string(tmpctx, struct node_id, + &node_id), + n->nann_off, + tal_hex(tmpctx, nannounce)); + goto out; + } + + esc = json_escape(NULL, + take(tal_strndup(NULL, + (const char *)alias, + ARRAY_SIZE(alias)))); + json_add_escaped_string(js, "alias", take(esc)); + json_add_hex(js, "color", rgb_color, ARRAY_SIZE(rgb_color)); + json_add_u64(js, "last_timestamp", timestamp); + json_add_hex_talarr(js, "features", features); + + json_array_start(js, "addresses"); + addrs = fromwire_wireaddr_array(nannounce, addresses); + for (size_t i = 0; i < tal_count(addrs); i++) + json_add_address(js, NULL, &addrs[i]); + json_array_end(js); + } +out: + json_object_end(js); +} + +static struct command_result *json_listnodes(struct command *cmd, + const char *buffer, + const jsmntok_t *params) +{ + struct node_id *id; + struct json_stream *js; + struct gossmap *gossmap; + + if (!param(cmd, buffer, params, + p_opt("id", param_node_id, &id), + NULL)) + return command_param_failed(); + + gossmap = get_gossmap(); + js = jsonrpc_stream_success(cmd); + json_array_start(js, "nodes"); + if (id) { + struct gossmap_node *n = gossmap_find_node(gossmap, id); + if (n) + json_add_node(js, gossmap, n); + } else { + for (struct gossmap_node *n = gossmap_first_node(gossmap); + n; + n = gossmap_next_node(gossmap, n)) { + json_add_node(js, gossmap, n); + } + } + json_array_end(js); + + return command_finished(cmd, js); +} + +/* What is capacity of peer attached to chan #n? */ +static struct amount_sat peer_capacity(const struct gossmap *gossmap, + const struct gossmap_node *me, + const struct gossmap_node *peer, + const struct gossmap_chan *ourchan) +{ + struct amount_sat capacity = AMOUNT_SAT(0); + + for (size_t i = 0; i < peer->num_chans; i++) { + int dir; + struct gossmap_chan *c; + c = gossmap_nth_chan(gossmap, peer, i, &dir); + if (c == ourchan) + continue; + if (!c->half[!dir].enabled) + continue; + if (!amount_sat_add(&capacity, capacity, + amount_sat(fp16_to_u64(c->half[dir] + .htlc_max)))) + continue; + } + return capacity; +} + +static struct command_result *json_listincoming(struct command *cmd, + const char *buffer, + const jsmntok_t *params) +{ + struct json_stream *js; + struct gossmap_node *me; + struct gossmap *gossmap; + + if (!param(cmd, buffer, params, NULL)) + return command_param_failed(); + + gossmap = get_gossmap(); + + js = jsonrpc_stream_success(cmd); + json_array_start(js, "incoming"); + me = gossmap_find_node(gossmap, &local_id); + if (!me) + goto done; + + for (size_t i = 0; i < me->num_chans; i++) { + struct node_id peer_id; + int dir; + struct gossmap_chan *ourchan; + struct gossmap_node *peer; + struct short_channel_id scid; + + ourchan = gossmap_nth_chan(gossmap, me, i, &dir); + /* If its half is disabled, ignore. */ + if (!ourchan->half[!dir].enabled) + continue; + + peer = gossmap_nth_node(gossmap, ourchan, !dir); + scid = gossmap_chan_scid(gossmap, ourchan); + + json_object_start(js, NULL); + gossmap_node_get_id(gossmap, peer, &peer_id); + json_add_node_id(js, "id", &peer_id); + json_add_short_channel_id(js, "short_channel_id", &scid); + json_add_amount_msat_only(js, "fee_base_msat", + amount_msat(ourchan->half[!dir] + .base_fee)); + json_add_u32(js, "fee_proportional_millionths", + ourchan->half[!dir].proportional_fee); + json_add_u32(js, "cltv_expiry_delta", ourchan->half[!dir].delay); + json_add_amount_sat_only(js, "incoming_capacity_msat", + peer_capacity(gossmap, + me, peer, ourchan)); + json_object_end(js); + } +done: + json_array_end(js); + + return command_finished(cmd, js); +} + +static const char *init(struct plugin *p, + const char *buf UNUSED, const jsmntok_t *config UNUSED) +{ + plugin = p; + rpc_scan(p, "getinfo", + take(json_out_obj(NULL, NULL, NULL)), + "{id:%}", JSON_SCAN(json_to_node_id, &local_id)); + + global_gossmap = notleak_with_children(gossmap_load(NULL, + GOSSIP_STORE_FILENAME)); + if (!global_gossmap) + plugin_err(plugin, "Could not load gossmap %s: %s", + GOSSIP_STORE_FILENAME, strerror(errno)); + + return NULL; +} + +static const struct plugin_command commands[] = { + { + "getroute", + "channels", + "Primitive route command", + "Show route to {id} for {msatoshi}, using {riskfactor} and optional {cltv} (default 9). " + "If specified search from {fromid} otherwise use this node as source. " + "Randomize the route with up to {fuzzpercent} (default 5.0). " + "{exclude} an array of short-channel-id/direction (e.g. [ '564334x877x1/0', '564195x1292x0/1' ]) " + "or node-id from consideration. " + "Set the {maxhops} the route can take (default 20).", + json_getroute, + }, + { + "listchannels", + "channels", + "List all known channels in the network", + "Show channel {short_channel_id} or {source} (or all known channels, if not specified)", + json_listchannels, + }, + { + "listnodes", + "network", + "List all known nodes in the network", + "Show node {id} (or all known nods, if not specified)", + json_listnodes, + }, + { + "listincoming", + "network", + "List the channels incoming from our direct peers", + "Used by invoice code to select peers for routehints", + json_listincoming, + }, +}; + +int main(int argc, char *argv[]) +{ + setup_locale(); + plugin_main(argv, init, PLUGIN_STATIC, true, NULL, commands, ARRAY_SIZE(commands), + NULL, 0, NULL, 0, NULL, 0, NULL); +} diff --git a/tests/test_invoices.py b/tests/test_invoices.py index 995f3c00f00b..33e91ae9bc76 100644 --- a/tests/test_invoices.py +++ b/tests/test_invoices.py @@ -160,9 +160,11 @@ def test_invoice_routeboost(node_factory, bitcoind): # Make invoice and pay it inv = l2.rpc.invoice(msatoshi=123456, label="inv1", description="?") # Check routeboost. + assert 'warning_private_unused' not in inv assert 'warning_capacity' not in inv assert 'warning_offline' not in inv assert 'warning_deadends' not in inv + assert 'warning_mpp' 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'] @@ -178,18 +180,22 @@ def test_invoice_routeboost(node_factory, bitcoind): # Due to reserve & fees, l1 doesn't have capacity to pay this. inv = l2.rpc.invoice(msatoshi=2 * (10**8) - 123456, label="inv2", description="?") # Check warning - assert 'warning_capacity' in inv or 'warning_mpp_capacity' in inv + assert 'warning_capacity' in inv + assert 'warning_private_unused' not in inv assert 'warning_offline' not in inv assert 'warning_deadends' not in inv + assert 'warning_mpp' not in inv l1.rpc.disconnect(l2.info['id'], True) 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_private_unused' not in inv assert 'warning_capacity' not in inv assert 'warning_deadends' not in inv assert 'warning_offline' in inv + assert 'warning_mpp' not in inv # Close l0, l2 will not use l1 at all. l0.rpc.close(l1.info['id']) @@ -201,8 +207,10 @@ def test_invoice_routeboost(node_factory, bitcoind): inv = l2.rpc.invoice(123456, label="inv4", description="?") # Check warning. assert 'warning_deadends' in inv + assert 'warning_private_unused' not in inv assert 'warning_capacity' not in inv assert 'warning_offline' not in inv + assert 'warning_mpp' not in inv @pytest.mark.developer("gossip without DEVELOPER=1 is slow") @@ -226,9 +234,11 @@ def test_invoice_routeboost_private(node_factory, bitcoind): # Since there's only one route, it will reluctantly hint that even # though it's private inv = l2.rpc.invoice(msatoshi=123456, label="inv0", description="?") + assert 'warning_private_unused' not in inv assert 'warning_capacity' not in inv assert 'warning_offline' not in inv assert 'warning_deadends' not in inv + assert 'warning_mpp' 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'] @@ -239,16 +249,20 @@ def test_invoice_routeboost_private(node_factory, bitcoind): # If we explicitly say not to, it won't expose. inv = l2.rpc.invoice(msatoshi=123456, label="inv1", description="?", exposeprivatechannels=False) - assert 'warning_capacity' in inv or 'warning_mpp_capacity' in inv + assert 'warning_private_unused' in inv + assert 'warning_capacity' not in inv assert 'warning_offline' not in inv assert 'warning_deadends' not in inv + assert 'warning_mpp' not in inv assert 'routes' not in l1.rpc.decodepay(inv['bolt11']) # If we ask for it, we get it. inv = l2.rpc.invoice(msatoshi=123456, label="inv1a", description="?", exposeprivatechannels=scid) + assert 'warning_private_unused' not in inv assert 'warning_capacity' not in inv assert 'warning_offline' not in inv assert 'warning_deadends' not in inv + assert 'warning_mpp' 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'] @@ -259,9 +273,11 @@ def test_invoice_routeboost_private(node_factory, bitcoind): # Similarly if we ask for an array. inv = l2.rpc.invoice(msatoshi=123456, label="inv1b", description="?", exposeprivatechannels=[scid]) + assert 'warning_private_unused' not in inv assert 'warning_capacity' not in inv assert 'warning_offline' not in inv assert 'warning_deadends' not in inv + assert 'warning_mpp' 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'] @@ -280,15 +296,20 @@ def test_invoice_routeboost_private(node_factory, bitcoind): wait_for(lambda: [c['public'] for c in l2.rpc.listchannels(scid2)['channels']] == [True, True]) inv = l2.rpc.invoice(msatoshi=10**7, label="inv2", description="?") + print(inv) assert 'warning_deadends' in inv + assert 'warning_private_unused' not in inv assert 'warning_capacity' not in inv assert 'warning_offline' not in inv + assert 'warning_mpp' not in inv # Unless we tell it to include it. inv = l2.rpc.invoice(msatoshi=10**7, label="inv3", description="?", exposeprivatechannels=True) + assert 'warning_private_unused' not in inv assert 'warning_capacity' not in inv assert 'warning_offline' not in inv assert 'warning_deadends' not in inv + assert 'warning_mpp' 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'] @@ -298,9 +319,11 @@ def test_invoice_routeboost_private(node_factory, bitcoind): assert r['cltv_expiry_delta'] == 6 inv = l2.rpc.invoice(msatoshi=10**7, label="inv4", description="?", exposeprivatechannels=scid) + assert 'warning_private_unused' not in inv assert 'warning_capacity' not in inv assert 'warning_offline' not in inv assert 'warning_deadends' not in inv + assert 'warning_mpp' 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'] @@ -311,15 +334,19 @@ def test_invoice_routeboost_private(node_factory, bitcoind): # Ask it explicitly to use a channel it can't (insufficient capacity) inv = l2.rpc.invoice(msatoshi=(10**5) * 1000 + 1, label="inv5", description="?", exposeprivatechannels=scid2) + assert 'warning_private_unused' not in inv assert 'warning_deadends' not in inv - assert 'warning_capacity' in inv or 'warning_mpp_capacity' in inv + assert 'warning_capacity' in inv assert 'warning_offline' not in inv + assert 'warning_mpp' not in inv # Give it two options and it will pick one with suff capacity. inv = l2.rpc.invoice(msatoshi=(10**5) * 1000 + 1, label="inv6", description="?", exposeprivatechannels=[scid2, scid]) + assert 'warning_private_unused' not in inv assert 'warning_capacity' not in inv assert 'warning_offline' not in inv assert 'warning_deadends' not in inv + assert 'warning_mpp' 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'] @@ -335,9 +362,11 @@ def test_invoice_routeboost_private(node_factory, bitcoind): wait_for(lambda: l2.rpc.listchannels(scid_dummy)['channels'] == []) inv = l2.rpc.invoice(msatoshi=123456, label="inv7", description="?", exposeprivatechannels=scid) + assert 'warning_private_unused' not in inv assert 'warning_capacity' not in inv assert 'warning_offline' not in inv assert 'warning_deadends' not in inv + assert 'warning_mpp' 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'] diff --git a/tests/test_pay.py b/tests/test_pay.py index c701c38d4110..1f77fa8767fc 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -4304,3 +4304,26 @@ def test_pay_low_max_htlcs(node_factory): l1.daemon.wait_for_log( r'Number of pre-split HTLCs \([0-9]+\) exceeds our HTLC budget \([0-9]+\), skipping pre-splitter' ) + + +def test_listpays_with_filter_by_status(node_factory, bitcoind): + """ + This test check if the filtering by status of the command listpays + has some mistakes. + """ + + # Create the line graph l2 -> l1 with a channel of 10 ** 5 sat! + l2, l1 = node_factory.line_graph(2, fundamount=10**5, wait_for_announce=True) + + inv = l1.rpc.invoice(10 ** 5, 'inv', 'inv') + l2.rpc.pay(inv['bolt11']) + + wait_for(lambda: l2.rpc.listpays(inv['bolt11'])['pays'][0]['status'] == 'complete') + + # test if the node is still ready + payments = l2.rpc.call("listpays", {"status": 'failed'}) + + assert len(payments['pays']) == 0 + + payments = l2.rpc.listpays() + assert len(l2.rpc.listpays()['pays']) == 1 diff --git a/tools/mockup.sh b/tools/mockup.sh index 9686b14cffdc..f1e8ac047125 100755 --- a/tools/mockup.sh +++ b/tools/mockup.sh @@ -61,5 +61,5 @@ for SYMBOL; do echo "/* Generated stub for $SYMBOL */" - tail -n "+${LINE}" < "$FILE" | head -n "$NUM" | sed 's/^extern *//' | sed 's/PRINTF_FMT([^)]*)//' | sed 's/NON_NULL_ARGS([^)]*)//' | sed 's/NO_NULL_ARGS//g' | sed 's/NORETURN//g' | sed 's/LAST_ARG_NULL//g' | sed 's/WARN_UNUSED_RESULT//g' | sed 's/,/ UNNEEDED,/g' | sed 's/\([a-z0-9A-Z*_]* [a-z0-9A-Z*_]*\));/\1 UNNEEDED);/' | sed "s/;\$/$STUB/" | sed 's/[[:space:]]*$//' + tail -n "+${LINE}" < "$FILE" | head -n "$NUM" | sed 's/^extern *//' | sed 's/PRINTF_FMT([^)]*)//' | sed 's/NON_NULL_ARGS([^)]*)//' | sed 's/NO_NULL_ARGS//g' | sed 's/NORETURN//g' | sed 's/RETURNS_NONNULL//g' | sed 's/LAST_ARG_NULL//g' | sed 's/WARN_UNUSED_RESULT//g' | sed 's/,/ UNNEEDED,/g' | sed 's/\([a-z0-9A-Z*_]* [a-z0-9A-Z*_]*\));/\1 UNNEEDED);/' | sed "s/;\$/$STUB/" | sed 's/[[:space:]]*$//' done diff --git a/wallet/db_postgres_sqlgen.c b/wallet/db_postgres_sqlgen.c index e85d5929d387..daa9923ae67c 100644 --- a/wallet/db_postgres_sqlgen.c +++ b/wallet/db_postgres_sqlgen.c @@ -1616,6 +1616,12 @@ struct db_query db_postgres_queries[] = { .placeholders = 1, .readonly = true, }, + { + .name = "SELECT id, status, destination, msatoshi, payment_hash, timestamp, payment_preimage, path_secrets, route_nodes, route_channels, msatoshi_sent, description, bolt11, failonionreply, total_msat, partid, local_offer_id FROM payments WHERE status = ? ORDER BY id;", + .query = "SELECT id, status, destination, msatoshi, payment_hash, timestamp, payment_preimage, path_secrets, route_nodes, route_channels, msatoshi_sent, description, bolt11, failonionreply, total_msat, partid, local_offer_id FROM payments WHERE status = $1 ORDER BY id;", + .placeholders = 1, + .readonly = true, + }, { .name = "SELECT id, status, destination, msatoshi, payment_hash, timestamp, payment_preimage, path_secrets, route_nodes, route_channels, msatoshi_sent, description, bolt11, failonionreply, total_msat, partid, local_offer_id FROM payments ORDER BY id;", .query = "SELECT id, status, destination, msatoshi, payment_hash, timestamp, payment_preimage, path_secrets, route_nodes, route_channels, msatoshi_sent, description, bolt11, failonionreply, total_msat, partid, local_offer_id FROM payments ORDER BY id;", @@ -1918,10 +1924,10 @@ struct db_query db_postgres_queries[] = { }, }; -#define DB_POSTGRES_QUERY_COUNT 318 +#define DB_POSTGRES_QUERY_COUNT 319 #endif /* HAVE_POSTGRES */ #endif /* LIGHTNINGD_WALLET_GEN_DB_POSTGRES */ -// SHA256STAMP:dbbcb7d784e7b3d6c7b27c2ff976dcc39335fdc26fbf095b65116488007799f7 +// SHA256STAMP:f7ebcea1bd78308227ed94ab39f4015337f73acc84fd5a7c4ad35c0be0624852 diff --git a/wallet/db_sqlite3_sqlgen.c b/wallet/db_sqlite3_sqlgen.c index 5b6eb8fa06d6..40ef14e3bdca 100644 --- a/wallet/db_sqlite3_sqlgen.c +++ b/wallet/db_sqlite3_sqlgen.c @@ -1616,6 +1616,12 @@ struct db_query db_sqlite3_queries[] = { .placeholders = 1, .readonly = true, }, + { + .name = "SELECT id, status, destination, msatoshi, payment_hash, timestamp, payment_preimage, path_secrets, route_nodes, route_channels, msatoshi_sent, description, bolt11, failonionreply, total_msat, partid, local_offer_id FROM payments WHERE status = ? ORDER BY id;", + .query = "SELECT id, status, destination, msatoshi, payment_hash, timestamp, payment_preimage, path_secrets, route_nodes, route_channels, msatoshi_sent, description, bolt11, failonionreply, total_msat, partid, local_offer_id FROM payments WHERE status = ? ORDER BY id;", + .placeholders = 1, + .readonly = true, + }, { .name = "SELECT id, status, destination, msatoshi, payment_hash, timestamp, payment_preimage, path_secrets, route_nodes, route_channels, msatoshi_sent, description, bolt11, failonionreply, total_msat, partid, local_offer_id FROM payments ORDER BY id;", .query = "SELECT id, status, destination, msatoshi, payment_hash, timestamp, payment_preimage, path_secrets, route_nodes, route_channels, msatoshi_sent, description, bolt11, failonionreply, total_msat, partid, local_offer_id FROM payments ORDER BY id;", @@ -1918,10 +1924,10 @@ struct db_query db_sqlite3_queries[] = { }, }; -#define DB_SQLITE3_QUERY_COUNT 318 +#define DB_SQLITE3_QUERY_COUNT 319 #endif /* HAVE_SQLITE3 */ #endif /* LIGHTNINGD_WALLET_GEN_DB_SQLITE3 */ -// SHA256STAMP:dbbcb7d784e7b3d6c7b27c2ff976dcc39335fdc26fbf095b65116488007799f7 +// SHA256STAMP:f7ebcea1bd78308227ed94ab39f4015337f73acc84fd5a7c4ad35c0be0624852 diff --git a/wallet/statements_gettextgen.po b/wallet/statements_gettextgen.po index 030daf2061c8..8ec71958c963 100644 --- a/wallet/statements_gettextgen.po +++ b/wallet/statements_gettextgen.po @@ -1066,191 +1066,195 @@ msgstr "" msgid "UPDATE payments SET failonionreply=? , faildestperm=? , failindex=? , failcode=? , failnode=? , failchannel=? , failupdate=? , faildetail=? , faildirection=? WHERE payment_hash=? AND partid=?;" msgstr "" -#: wallet/wallet.c:3165 +#: wallet/wallet.c:3166 msgid "SELECT id, status, destination, msatoshi, payment_hash, timestamp, payment_preimage, path_secrets, route_nodes, route_channels, msatoshi_sent, description, bolt11, failonionreply, total_msat, partid, local_offer_id FROM payments WHERE payment_hash = ? ORDER BY id;" msgstr "" -#: wallet/wallet.c:3188 +#: wallet/wallet.c:3192 +msgid "SELECT id, status, destination, msatoshi, payment_hash, timestamp, payment_preimage, path_secrets, route_nodes, route_channels, msatoshi_sent, description, bolt11, failonionreply, total_msat, partid, local_offer_id FROM payments WHERE status = ? ORDER BY id;" +msgstr "" + +#: wallet/wallet.c:3215 msgid "SELECT id, status, destination, msatoshi, payment_hash, timestamp, payment_preimage, path_secrets, route_nodes, route_channels, msatoshi_sent, description, bolt11, failonionreply, total_msat, partid, local_offer_id FROM payments ORDER BY id;" msgstr "" -#: wallet/wallet.c:3239 +#: wallet/wallet.c:3266 msgid "SELECT id, status, destination, msatoshi, payment_hash, timestamp, payment_preimage, path_secrets, route_nodes, route_channels, msatoshi_sent, description, bolt11, failonionreply, total_msat, partid, local_offer_id FROM payments WHERE local_offer_id = ?;" msgstr "" -#: wallet/wallet.c:3284 +#: wallet/wallet.c:3311 msgid "DELETE FROM htlc_sigs WHERE channelid = ?" msgstr "" -#: wallet/wallet.c:3291 +#: wallet/wallet.c:3318 msgid "INSERT INTO htlc_sigs (channelid, signature) VALUES (?, ?)" msgstr "" -#: wallet/wallet.c:3303 +#: wallet/wallet.c:3330 msgid "SELECT blobval FROM vars WHERE name='genesis_hash'" msgstr "" -#: wallet/wallet.c:3327 +#: wallet/wallet.c:3354 msgid "INSERT INTO vars (name, blobval) VALUES ('genesis_hash', ?);" msgstr "" -#: wallet/wallet.c:3345 +#: wallet/wallet.c:3372 msgid "SELECT txid, outnum FROM utxoset WHERE spendheight < ?" msgstr "" -#: wallet/wallet.c:3357 +#: wallet/wallet.c:3384 msgid "DELETE FROM utxoset WHERE spendheight < ?" msgstr "" -#: wallet/wallet.c:3365 wallet/wallet.c:3479 +#: wallet/wallet.c:3392 wallet/wallet.c:3506 msgid "INSERT INTO blocks (height, hash, prev_hash) VALUES (?, ?, ?);" msgstr "" -#: wallet/wallet.c:3384 +#: wallet/wallet.c:3411 msgid "DELETE FROM blocks WHERE hash = ?" msgstr "" -#: wallet/wallet.c:3390 +#: wallet/wallet.c:3417 msgid "SELECT * FROM blocks WHERE height >= ?;" msgstr "" -#: wallet/wallet.c:3399 +#: wallet/wallet.c:3426 msgid "DELETE FROM blocks WHERE height > ?" msgstr "" -#: wallet/wallet.c:3411 +#: wallet/wallet.c:3438 msgid "UPDATE outputs SET spend_height = ?, status = ? WHERE prev_out_tx = ? AND prev_out_index = ?" msgstr "" -#: wallet/wallet.c:3429 +#: wallet/wallet.c:3456 msgid "UPDATE utxoset SET spendheight = ? WHERE txid = ? AND outnum = ?" msgstr "" -#: wallet/wallet.c:3452 wallet/wallet.c:3490 +#: wallet/wallet.c:3479 wallet/wallet.c:3517 msgid "INSERT INTO utxoset ( txid, outnum, blockheight, spendheight, txindex, scriptpubkey, satoshis) VALUES(?, ?, ?, ?, ?, ?, ?);" msgstr "" -#: wallet/wallet.c:3516 +#: wallet/wallet.c:3543 msgid "SELECT height FROM blocks WHERE height = ?" msgstr "" -#: wallet/wallet.c:3529 +#: wallet/wallet.c:3556 msgid "SELECT txid, spendheight, scriptpubkey, satoshis FROM utxoset WHERE blockheight = ? AND txindex = ? AND outnum = ? AND spendheight IS NULL" msgstr "" -#: wallet/wallet.c:3571 +#: wallet/wallet.c:3598 msgid "SELECT blockheight, txindex, outnum FROM utxoset WHERE spendheight = ?" msgstr "" -#: wallet/wallet.c:3602 wallet/wallet.c:3762 +#: wallet/wallet.c:3629 wallet/wallet.c:3789 msgid "SELECT blockheight FROM transactions WHERE id=?" msgstr "" -#: wallet/wallet.c:3612 +#: wallet/wallet.c:3639 msgid "INSERT INTO transactions ( id, blockheight, txindex, rawtx) VALUES (?, ?, ?, ?);" msgstr "" -#: wallet/wallet.c:3633 +#: wallet/wallet.c:3660 msgid "UPDATE transactions SET blockheight = ?, txindex = ? WHERE id = ?" msgstr "" -#: wallet/wallet.c:3650 +#: wallet/wallet.c:3677 msgid "INSERT INTO transaction_annotations (txid, idx, location, type, channel) VALUES (?, ?, ?, ?, ?) ON CONFLICT(txid,idx) DO NOTHING;" msgstr "" -#: wallet/wallet.c:3682 +#: wallet/wallet.c:3709 msgid "SELECT type, channel_id FROM transactions WHERE id=?" msgstr "" -#: wallet/wallet.c:3698 +#: wallet/wallet.c:3725 msgid "UPDATE transactions SET type = ?, channel_id = ? WHERE id = ?" msgstr "" -#: wallet/wallet.c:3717 +#: wallet/wallet.c:3744 msgid "SELECT type FROM transactions WHERE id=?" msgstr "" -#: wallet/wallet.c:3740 +#: wallet/wallet.c:3767 msgid "SELECT rawtx FROM transactions WHERE id=?" msgstr "" -#: wallet/wallet.c:3786 +#: wallet/wallet.c:3813 msgid "SELECT blockheight, txindex FROM transactions WHERE id=?" msgstr "" -#: wallet/wallet.c:3814 +#: wallet/wallet.c:3841 msgid "SELECT id FROM transactions WHERE blockheight=?" msgstr "" -#: wallet/wallet.c:3833 +#: wallet/wallet.c:3860 msgid "INSERT INTO channeltxs ( channel_id, type, transaction_id, input_num, blockheight) VALUES (?, ?, ?, ?, ?);" msgstr "" -#: wallet/wallet.c:3857 +#: wallet/wallet.c:3884 msgid "SELECT DISTINCT(channel_id) FROM channeltxs WHERE type = ?;" msgstr "" -#: wallet/wallet.c:3878 +#: wallet/wallet.c:3905 msgid "SELECT c.type, c.blockheight, t.rawtx, c.input_num, c.blockheight - t.blockheight + 1 AS depth, t.id as txid FROM channeltxs c JOIN transactions t ON t.id = c.transaction_id WHERE c.channel_id = ? ORDER BY c.id ASC;" msgstr "" -#: wallet/wallet.c:3923 +#: wallet/wallet.c:3950 msgid "UPDATE forwarded_payments SET in_msatoshi=?, out_msatoshi=?, state=?, resolved_time=?, failcode=? WHERE in_htlc_id=?" msgstr "" -#: wallet/wallet.c:3981 +#: wallet/wallet.c:4008 msgid "INSERT INTO forwarded_payments ( in_htlc_id, out_htlc_id, in_channel_scid, out_channel_scid, in_msatoshi, out_msatoshi, state, received_time, resolved_time, failcode) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?);" msgstr "" -#: wallet/wallet.c:4040 +#: wallet/wallet.c:4067 msgid "SELECT CAST(COALESCE(SUM(in_msatoshi - out_msatoshi), 0) AS BIGINT)FROM forwarded_payments WHERE state = ?;" msgstr "" -#: wallet/wallet.c:4089 +#: wallet/wallet.c:4116 msgid "SELECT f.state, in_msatoshi, out_msatoshi, hin.payment_hash as payment_hash, in_channel_scid, out_channel_scid, f.received_time, f.resolved_time, f.failcode FROM forwarded_payments f LEFT JOIN channel_htlcs hin ON (f.in_htlc_id = hin.id) WHERE (1 = ? OR f.state = ?) AND (1 = ? OR f.in_channel_scid = ?) AND (1 = ? OR f.out_channel_scid = ?)" msgstr "" -#: wallet/wallet.c:4211 +#: wallet/wallet.c:4238 msgid "SELECT t.id, t.rawtx, t.blockheight, t.txindex, t.type as txtype, c2.short_channel_id as txchan, a.location, a.idx as ann_idx, a.type as annotation_type, c.short_channel_id FROM transactions t LEFT JOIN transaction_annotations a ON (a.txid = t.id) LEFT JOIN channels c ON (a.channel = c.id) LEFT JOIN channels c2 ON (t.channel_id = c2.id) ORDER BY t.blockheight, t.txindex ASC" msgstr "" -#: wallet/wallet.c:4305 +#: wallet/wallet.c:4332 msgid "INSERT INTO penalty_bases ( channel_id, commitnum, txid, outnum, amount) VALUES (?, ?, ?, ?, ?);" msgstr "" -#: wallet/wallet.c:4330 +#: wallet/wallet.c:4357 msgid "SELECT commitnum, txid, outnum, amount FROM penalty_bases WHERE channel_id = ?" msgstr "" -#: wallet/wallet.c:4354 +#: wallet/wallet.c:4381 msgid "DELETE FROM penalty_bases WHERE channel_id = ? AND commitnum = ?" msgstr "" -#: wallet/wallet.c:4372 +#: wallet/wallet.c:4399 msgid "SELECT 1 FROM offers WHERE offer_id = ?;" msgstr "" -#: wallet/wallet.c:4385 +#: wallet/wallet.c:4412 msgid "INSERT INTO offers ( offer_id, bolt12, label, status) VALUES (?, ?, ?, ?);" msgstr "" -#: wallet/wallet.c:4412 +#: wallet/wallet.c:4439 msgid "SELECT bolt12, label, status FROM offers WHERE offer_id = ?;" msgstr "" -#: wallet/wallet.c:4440 +#: wallet/wallet.c:4467 msgid "SELECT offer_id FROM offers;" msgstr "" -#: wallet/wallet.c:4466 +#: wallet/wallet.c:4493 msgid "UPDATE offers SET status=? WHERE offer_id = ?;" msgstr "" -#: wallet/wallet.c:4477 +#: wallet/wallet.c:4504 msgid "UPDATE invoices SET state=? WHERE state=? AND local_offer_id = ?;" msgstr "" -#: wallet/wallet.c:4505 +#: wallet/wallet.c:4532 msgid "SELECT status FROM offers WHERE offer_id = ?;" msgstr "" @@ -1269,4 +1273,4 @@ msgstr "" #: wallet/test/run-wallet.c:1653 msgid "INSERT INTO channels (id) VALUES (1);" msgstr "" -# SHA256STAMP:e3c8d5cac8615668f0c9f37ebf6edff3b18833bafdf9643c2203b2a4ab654b7c +# SHA256STAMP:a9e9f879b603c01558d2303ef4d982a49384e8e50047612095d79753e0722caa diff --git a/wallet/wallet.c b/wallet/wallet.c index d53652fece4c..d413c29453dd 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -3152,7 +3152,8 @@ void wallet_payment_set_failinfo(struct wallet *wallet, const struct wallet_payment ** wallet_payment_list(const tal_t *ctx, struct wallet *wallet, - const struct sha256 *payment_hash) + const struct sha256 *payment_hash, + enum wallet_payment_status *status) { const struct wallet_payment **payments; struct db_stmt *stmt; @@ -3184,6 +3185,32 @@ wallet_payment_list(const tal_t *ctx, " WHERE payment_hash = ?" " ORDER BY id;")); db_bind_sha256(stmt, 0, payment_hash); + } else if (status) { + // TODO(vincenzopalazzo): Missing the filter options are both not null + // A possible solution is divided the string in two part (pre-where and post-where) and + // use a small if else to set the remain string, with this method we can have small code. + stmt = db_prepare_v2(wallet->db, SQL("SELECT" + " id" + ", status" + ", destination" + ", msatoshi" + ", payment_hash" + ", timestamp" + ", payment_preimage" + ", path_secrets" + ", route_nodes" + ", route_channels" + ", msatoshi_sent" + ", description" + ", bolt11" + ", failonionreply" + ", total_msat" + ", partid" + ", local_offer_id" + " FROM payments" + " WHERE status = ?" + " ORDER BY id;")); + db_bind_int(stmt, 0, wallet_payment_status_in_db(*status)); } else { stmt = db_prepare_v2(wallet->db, SQL("SELECT" " id" diff --git a/wallet/wallet.h b/wallet/wallet.h index d8b399385477..acf688ee34cd 100644 --- a/wallet/wallet.h +++ b/wallet/wallet.h @@ -1114,10 +1114,12 @@ void wallet_payment_set_failinfo(struct wallet *wallet, * wallet_payment_list - Retrieve a list of payments * * payment_hash: optional filter for only this payment hash. + * status: option filtering for all the payment with the specified status. */ const struct wallet_payment **wallet_payment_list(const tal_t *ctx, struct wallet *wallet, - const struct sha256 *payment_hash); + const struct sha256 *payment_hash, + enum wallet_payment_status *status); /** * wallet_payments_by_offer - Retrieve a list of payments for this local_offer_id @@ -1280,7 +1282,7 @@ struct channeltx *wallet_channeltxs_get(struct wallet *w, const tal_t *ctx, */ void wallet_forwarded_payment_add(struct wallet *w, const struct htlc_in *in, const struct short_channel_id *scid_out, - const struct htlc_out *out, + const struct htlc_out *out, enum forward_status state, enum onion_wire failcode);