core-lightning/plugins/test/run-route-overlong.c
Rusty Russell a3c4908f4a lightningd: don't explicitly tell connectd to disconnect, have it do it on sending error/warning.
Connectd already does this when we *receive* an error or warning, but
now do it on send.  This causes some slight behavior change: we don't
disconnect when we close a channel, for example (our behaviour here
has been inconsistent across versions, depending on the code).

When connectd is told to disconnect, it now does so immediately, and
doesn't wait for subds to drain etc.  That simplifies the manual
disconnect case, which now cleans up as it would from any other
disconnection when connectd says it's disconnected.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2022-07-18 20:50:04 -05:00

408 lines
17 KiB
C

#include "config.h"
#include "../libplugin-pay.c"
#include <bitcoin/chainparams.h>
#include <common/gossip_store.h>
#include <common/setup.h>
#include <common/utils.h>
#include <stdio.h>
#include <unistd.h>
/* AUTOGENERATED MOCKS START */
/* Generated stub for command_finished */
struct command_result *command_finished(struct command *cmd UNNEEDED, struct json_stream *response UNNEEDED)
{ fprintf(stderr, "command_finished called!\n"); abort(); }
/* Generated stub for command_still_pending */
struct command_result *command_still_pending(struct command *cmd UNNEEDED)
{ fprintf(stderr, "command_still_pending called!\n"); abort(); }
/* Generated stub for deprecated_apis */
bool deprecated_apis;
/* 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 fromwire_bigsize */
bigsize_t fromwire_bigsize(const u8 **cursor UNNEEDED, size_t *max UNNEEDED)
{ fprintf(stderr, "fromwire_bigsize called!\n"); abort(); }
/* Generated stub for fromwire_channel_id */
bool fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED,
struct channel_id *channel_id UNNEEDED)
{ fprintf(stderr, "fromwire_channel_id called!\n"); abort(); }
/* Generated stub for json_add_amount_msat_compat */
void json_add_amount_msat_compat(struct json_stream *result UNNEEDED,
struct amount_msat msat UNNEEDED,
const char *rawfieldname UNNEEDED,
const char *msatfieldname)
{ fprintf(stderr, "json_add_amount_msat_compat called!\n"); abort(); }
/* Generated stub for json_add_amount_msat_only */
void json_add_amount_msat_only(struct json_stream *result UNNEEDED,
const char *msatfieldname UNNEEDED,
struct amount_msat msat)
{ fprintf(stderr, "json_add_amount_msat_only called!\n"); abort(); }
/* Generated stub for json_add_hex_talarr */
void json_add_hex_talarr(struct json_stream *result UNNEEDED,
const char *fieldname UNNEEDED,
const tal_t *data UNNEEDED)
{ fprintf(stderr, "json_add_hex_talarr called!\n"); abort(); }
/* Generated stub for json_add_invstring */
void json_add_invstring(struct json_stream *result UNNEEDED, const char *invstring UNNEEDED)
{ fprintf(stderr, "json_add_invstring called!\n"); abort(); }
/* Generated stub for json_add_node_id */
void json_add_node_id(struct json_stream *response UNNEEDED,
const char *fieldname UNNEEDED,
const struct node_id *id UNNEEDED)
{ fprintf(stderr, "json_add_node_id called!\n"); abort(); }
/* Generated stub for json_add_num */
void json_add_num(struct json_stream *result UNNEEDED, const char *fieldname UNNEEDED,
unsigned int value UNNEEDED)
{ fprintf(stderr, "json_add_num called!\n"); abort(); }
/* Generated stub for json_add_preimage */
void json_add_preimage(struct json_stream *result UNNEEDED, const char *fieldname UNNEEDED,
const struct preimage *preimage UNNEEDED)
{ fprintf(stderr, "json_add_preimage called!\n"); abort(); }
/* Generated stub for json_add_secret */
void json_add_secret(struct json_stream *response UNNEEDED,
const char *fieldname UNNEEDED,
const struct secret *secret UNNEEDED)
{ fprintf(stderr, "json_add_secret called!\n"); abort(); }
/* Generated stub for json_add_sha256 */
void json_add_sha256(struct json_stream *result UNNEEDED, const char *fieldname UNNEEDED,
const struct sha256 *hash UNNEEDED)
{ fprintf(stderr, "json_add_sha256 called!\n"); abort(); }
/* Generated stub for json_add_short_channel_id */
void json_add_short_channel_id(struct json_stream *response UNNEEDED,
const char *fieldname UNNEEDED,
const struct short_channel_id *id UNNEEDED)
{ fprintf(stderr, "json_add_short_channel_id called!\n"); abort(); }
/* Generated stub for json_add_string */
void json_add_string(struct json_stream *js UNNEEDED,
const char *fieldname UNNEEDED,
const char *str TAKES UNNEEDED)
{ fprintf(stderr, "json_add_string called!\n"); abort(); }
/* Generated stub for json_add_timeabs */
void json_add_timeabs(struct json_stream *result UNNEEDED, const char *fieldname UNNEEDED,
struct timeabs t UNNEEDED)
{ fprintf(stderr, "json_add_timeabs called!\n"); abort(); }
/* Generated stub for json_add_u32 */
void json_add_u32(struct json_stream *result UNNEEDED, const char *fieldname UNNEEDED,
uint32_t value UNNEEDED)
{ fprintf(stderr, "json_add_u32 called!\n"); abort(); }
/* Generated stub for json_add_u64 */
void json_add_u64(struct json_stream *result UNNEEDED, const char *fieldname UNNEEDED,
uint64_t value UNNEEDED)
{ fprintf(stderr, "json_add_u64 called!\n"); abort(); }
/* Generated stub for json_array_end */
void json_array_end(struct json_stream *js UNNEEDED)
{ fprintf(stderr, "json_array_end called!\n"); abort(); }
/* Generated stub for json_array_start */
void json_array_start(struct json_stream *js UNNEEDED, const char *fieldname UNNEEDED)
{ fprintf(stderr, "json_array_start called!\n"); abort(); }
/* Generated stub for json_get_member */
const jsmntok_t *json_get_member(const char *buffer UNNEEDED, const jsmntok_t tok[] UNNEEDED,
const char *label UNNEEDED)
{ fprintf(stderr, "json_get_member called!\n"); abort(); }
/* Generated stub for json_next */
const jsmntok_t *json_next(const jsmntok_t *tok UNNEEDED)
{ fprintf(stderr, "json_next called!\n"); abort(); }
/* Generated stub for json_object_end */
void json_object_end(struct json_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 json_strdup */
char *json_strdup(const tal_t *ctx UNNEEDED, const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED)
{ fprintf(stderr, "json_strdup called!\n"); abort(); }
/* Generated stub for json_to_bool */
bool json_to_bool(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, bool *b UNNEEDED)
{ fprintf(stderr, "json_to_bool called!\n"); abort(); }
/* Generated stub for json_to_createonion_response */
struct createonion_response *json_to_createonion_response(const tal_t *ctx UNNEEDED,
const char *buffer UNNEEDED,
const jsmntok_t *toks UNNEEDED)
{ fprintf(stderr, "json_to_createonion_response called!\n"); abort(); }
/* Generated stub for json_to_int */
bool json_to_int(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, int *num UNNEEDED)
{ fprintf(stderr, "json_to_int called!\n"); abort(); }
/* Generated stub for json_to_listpeers_result */
struct listpeers_result *json_to_listpeers_result(const tal_t *ctx UNNEEDED,
const char *buffer UNNEEDED,
const jsmntok_t *tok UNNEEDED)
{ fprintf(stderr, "json_to_listpeers_result 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)
{ fprintf(stderr, "json_to_node_id called!\n"); abort(); }
/* Generated stub for json_to_number */
bool json_to_number(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED,
unsigned int *num UNNEEDED)
{ fprintf(stderr, "json_to_number called!\n"); abort(); }
/* Generated stub for json_to_preimage */
bool json_to_preimage(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, struct preimage *preimage UNNEEDED)
{ fprintf(stderr, "json_to_preimage called!\n"); abort(); }
/* Generated stub for json_to_sat */
bool json_to_sat(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED,
struct amount_sat *sat UNNEEDED)
{ fprintf(stderr, "json_to_sat called!\n"); abort(); }
/* Generated stub for json_to_short_channel_id */
bool json_to_short_channel_id(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED,
struct short_channel_id *scid UNNEEDED)
{ fprintf(stderr, "json_to_short_channel_id called!\n"); abort(); }
/* Generated stub for json_to_u16 */
bool json_to_u16(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED,
uint16_t *num UNNEEDED)
{ fprintf(stderr, "json_to_u16 called!\n"); abort(); }
/* Generated stub for json_to_u32 */
bool json_to_u32(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, u32 *num UNNEEDED)
{ fprintf(stderr, "json_to_u32 called!\n"); abort(); }
/* Generated stub for json_to_u64 */
bool json_to_u64(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, u64 *num UNNEEDED)
{ fprintf(stderr, "json_to_u64 called!\n"); abort(); }
/* Generated stub for json_tok_bin_from_hex */
u8 *json_tok_bin_from_hex(const tal_t *ctx UNNEEDED, const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED)
{ fprintf(stderr, "json_tok_bin_from_hex called!\n"); abort(); }
/* Generated stub for json_tok_full */
const char *json_tok_full(const char *buffer UNNEEDED, const jsmntok_t *t UNNEEDED)
{ fprintf(stderr, "json_tok_full called!\n"); abort(); }
/* Generated stub for json_tok_full_len */
int json_tok_full_len(const jsmntok_t *t UNNEEDED)
{ fprintf(stderr, "json_tok_full_len called!\n"); abort(); }
/* Generated stub for json_tok_streq */
bool json_tok_streq(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, const char *str UNNEEDED)
{ fprintf(stderr, "json_tok_streq called!\n"); abort(); }
/* Generated stub for jsonrpc_request_start_ */
struct out_req *jsonrpc_request_start_(struct plugin *plugin UNNEEDED,
struct command *cmd UNNEEDED,
const char *method UNNEEDED,
struct command_result *(*cb)(struct command *command UNNEEDED,
const char *buf UNNEEDED,
const jsmntok_t *result UNNEEDED,
void *arg) UNNEEDED,
struct command_result *(*errcb)(struct command *command UNNEEDED,
const char *buf UNNEEDED,
const jsmntok_t *result UNNEEDED,
void *arg) UNNEEDED,
void *arg UNNEEDED)
{ fprintf(stderr, "jsonrpc_request_start_ called!\n"); abort(); }
/* Generated stub for jsonrpc_stream_fail */
struct json_stream *jsonrpc_stream_fail(struct command *cmd UNNEEDED,
int code UNNEEDED,
const char *err UNNEEDED)
{ fprintf(stderr, "jsonrpc_stream_fail called!\n"); abort(); }
/* Generated stub for jsonrpc_stream_success */
struct json_stream *jsonrpc_stream_success(struct command *cmd UNNEEDED)
{ fprintf(stderr, "jsonrpc_stream_success called!\n"); abort(); }
/* Generated stub for notleak_ */
void *notleak_(void *ptr UNNEEDED, bool plus_children UNNEEDED)
{ fprintf(stderr, "notleak_ called!\n"); abort(); }
/* Generated stub for plugin_err */
void plugin_err(struct plugin *p UNNEEDED, const char *fmt UNNEEDED, ...)
{ fprintf(stderr, "plugin_err called!\n"); abort(); }
/* Generated stub for plugin_log */
void plugin_log(struct plugin *p UNNEEDED, enum log_level l UNNEEDED, const char *fmt UNNEEDED, ...)
{ fprintf(stderr, "plugin_log called!\n"); abort(); }
/* Generated stub for plugin_notification_end */
void plugin_notification_end(struct plugin *plugin UNNEEDED,
struct json_stream *stream TAKES UNNEEDED)
{ fprintf(stderr, "plugin_notification_end called!\n"); abort(); }
/* Generated stub for plugin_notification_start */
struct json_stream *plugin_notification_start(struct plugin *plugins UNNEEDED,
const char *method UNNEEDED)
{ fprintf(stderr, "plugin_notification_start called!\n"); abort(); }
/* Generated stub for random_select */
bool random_select(double weight UNNEEDED, double *tot_weight UNNEEDED)
{ fprintf(stderr, "random_select called!\n"); abort(); }
/* Generated stub for send_outreq */
struct command_result *send_outreq(struct plugin *plugin UNNEEDED,
const struct out_req *req UNNEEDED)
{ fprintf(stderr, "send_outreq called!\n"); abort(); }
/* Generated stub for towire_bigsize */
void towire_bigsize(u8 **pptr UNNEEDED, const bigsize_t val UNNEEDED)
{ fprintf(stderr, "towire_bigsize called!\n"); abort(); }
/* Generated stub for towire_channel_id */
void towire_channel_id(u8 **pptr UNNEEDED, const struct channel_id *channel_id UNNEEDED)
{ fprintf(stderr, "towire_channel_id called!\n"); abort(); }
/* AUTOGENERATED MOCKS END */
#ifndef SUPERVERBOSE
#define SUPERVERBOSE(...)
#endif
static void write_to_store(int store_fd, const u8 *msg)
{
struct gossip_hdr hdr;
hdr.len = cpu_to_be32(tal_count(msg));
/* We don't actually check these! */
hdr.crc = 0;
hdr.timestamp = 0;
assert(write(store_fd, &hdr, sizeof(hdr)) == sizeof(hdr));
assert(write(store_fd, msg, tal_count(msg)) == tal_count(msg));
}
static void update_connection(int store_fd,
const struct node_id *from,
const struct node_id *to,
const struct short_channel_id *scid,
struct amount_msat min,
struct amount_msat max,
u32 base_fee, s32 proportional_fee,
u32 delay,
bool disable)
{
secp256k1_ecdsa_signature dummy_sig;
u8 *msg;
/* So valgrind doesn't complain */
memset(&dummy_sig, 0, sizeof(dummy_sig));
msg = towire_channel_update_option_channel_htlc_max(tmpctx,
&dummy_sig,
&chainparams->genesis_blockhash,
scid, 0,
ROUTING_OPT_HTLC_MAX_MSAT,
node_id_idx(from, to)
+ (disable ? ROUTING_FLAGS_DISABLED : 0),
delay,
min,
base_fee,
proportional_fee,
max);
write_to_store(store_fd, msg);
}
static void add_connection(int store_fd,
const struct node_id *from,
const struct node_id *to,
const struct short_channel_id *scid,
struct amount_msat min,
struct amount_msat max,
u32 base_fee, s32 proportional_fee,
u32 delay)
{
secp256k1_ecdsa_signature dummy_sig;
struct secret not_a_secret;
struct pubkey dummy_key;
u8 *msg;
const struct node_id *ids[2];
/* So valgrind doesn't complain */
memset(&dummy_sig, 0, sizeof(dummy_sig));
memset(&not_a_secret, 1, sizeof(not_a_secret));
pubkey_from_secret(&not_a_secret, &dummy_key);
if (node_id_cmp(from, to) > 0) {
ids[0] = to;
ids[1] = from;
} else {
ids[0] = from;
ids[1] = to;
}
msg = towire_channel_announcement(tmpctx, &dummy_sig, &dummy_sig,
&dummy_sig, &dummy_sig,
/* features */ NULL,
&chainparams->genesis_blockhash,
scid,
ids[0], ids[1],
&dummy_key, &dummy_key);
write_to_store(store_fd, msg);
update_connection(store_fd, from, to, scid, min, max,
base_fee, proportional_fee,
delay, false);
}
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 node_id ids[NUM_NODES];
int store_fd;
struct payment *p;
struct payment_modifier **mods;
char gossip_version = GOSSIP_STORE_VERSION;
char *gossipfilename;
common_setup(argv[0]);
chainparams = chainparams_for_network("regtest");
store_fd = tmpdir_mkstemp(tmpctx, "run-route-overlong.XXXXXX", &gossipfilename);
assert(write(store_fd, &gossip_version, sizeof(gossip_version))
== sizeof(gossip_version));
global_gossmap = gossmap_load(tmpctx, gossipfilename, NULL);
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]);
}
mods = tal_arrz(tmpctx, struct payment_modifier *, 1);
p = payment_new(mods, tal(tmpctx, struct command), NULL, mods);
for (size_t i = 1; i < NUM_NODES; i++) {
struct short_channel_id scid;
if (!mk_short_channel_id(&scid, i, i-1, 0))
abort();
add_connection(store_fd, &ids[i-1], &ids[i], &scid,
AMOUNT_MSAT(0),
AMOUNT_MSAT(1000000 * 1000),
0, 0, 0);
SUPERVERBOSE("Joining %s to %s, fee %u\n",
type_to_string(tmpctx, struct node_id, &ids[i-1]),
type_to_string(tmpctx, struct node_id, &ids[i]),
0);
if (i <= 2)
continue;
if (!mk_short_channel_id(&scid, i, 1, 0))
abort();
add_connection(store_fd, &ids[1], &ids[i], &scid,
AMOUNT_MSAT(0),
AMOUNT_MSAT(1000000 * 1000),
1 << i, 0, 0);
SUPERVERBOSE("Joining %s to %s, fee %u\n",
type_to_string(tmpctx, struct node_id, &ids[1]),
type_to_string(tmpctx, struct node_id, &ids[i]),
1 << i);
}
assert(gossmap_refresh(global_gossmap, NULL));
for (size_t i = ROUTING_MAX_HOPS; i > 2; i--) {
struct gossmap_node *dst, *src;
struct route_hop *r;
const char *errmsg;
SUPERVERBOSE("%s -> %s:\n",
type_to_string(tmpctx, struct node_id, &ids[0]),
type_to_string(tmpctx, struct node_id, &ids[NUM_NODES-1]));
src = gossmap_find_node(global_gossmap, &ids[0]);
dst = gossmap_find_node(global_gossmap, &ids[NUM_NODES-1]);
r = route(tmpctx, global_gossmap, src, dst, AMOUNT_MSAT(1000), 0, 0.0,
i - 1, p, &errmsg);
assert(r);
/* FIXME: We naively fall back on shortest, rather
* than biassing! */
assert(tal_count(r) == 2);
}
common_shutdown();
return 0;
}