It's used by common/gossip_store.c, which is used by many things other than gossipd. This file belongs in common. Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
308 lines
10 KiB
C
308 lines
10 KiB
C
#include "config.h"
|
|
#include "../libplugin-pay.c"
|
|
#include <bitcoin/chainparams.h>
|
|
#include <ccan/crc32c/crc32c.h>
|
|
#include <common/gossip_store.h>
|
|
#include <common/gossip_store_wiregen.h>
|
|
#include <common/setup.h>
|
|
#include <common/utils.h>
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
|
|
/* AUTOGENERATED MOCKS START */
|
|
/* Generated stub for aux_command */
|
|
struct command *aux_command(const struct command *cmd)
|
|
|
|
{ fprintf(stderr, "aux_command called!\n"); abort(); }
|
|
/* Generated stub for command_check_only */
|
|
bool command_check_only(const struct command *cmd UNNEEDED)
|
|
{ fprintf(stderr, "command_check_only called!\n"); abort(); }
|
|
/* Generated stub for command_dev_apis */
|
|
bool command_dev_apis(const struct command *cmd UNNEEDED)
|
|
{ fprintf(stderr, "command_dev_apis called!\n"); abort(); }
|
|
/* Generated stub for command_fail */
|
|
struct command_result *command_fail(struct command *cmd UNNEEDED, enum jsonrpc_errcode code UNNEEDED,
|
|
const char *fmt UNNEEDED, ...)
|
|
|
|
{ fprintf(stderr, "command_fail called!\n"); abort(); }
|
|
/* Generated stub for command_filter_ptr */
|
|
struct json_filter **command_filter_ptr(struct command *cmd UNNEEDED)
|
|
{ fprintf(stderr, "command_filter_ptr called!\n"); abort(); }
|
|
/* Generated stub for command_finished */
|
|
struct command_result *command_finished(struct command *cmd UNNEEDED, struct json_stream *response)
|
|
|
|
{ fprintf(stderr, "command_finished called!\n"); abort(); }
|
|
/* Generated stub for command_log */
|
|
void command_log(struct command *cmd UNNEEDED, enum log_level level UNNEEDED,
|
|
const char *fmt UNNEEDED, ...)
|
|
|
|
{ fprintf(stderr, "command_log called!\n"); abort(); }
|
|
/* Generated stub for command_still_pending */
|
|
struct command_result *command_still_pending(struct command *cmd)
|
|
|
|
{ fprintf(stderr, "command_still_pending 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_listpeers_channels */
|
|
struct listpeers_channel **json_to_listpeers_channels(const tal_t *ctx UNNEEDED,
|
|
const char *buffer UNNEEDED,
|
|
const jsmntok_t *tok UNNEEDED)
|
|
{ fprintf(stderr, "json_to_listpeers_channels called!\n"); abort(); }
|
|
/* Generated stub for jsonrpc_request_start_ */
|
|
struct out_req *jsonrpc_request_start_(struct command *cmd UNNEEDED,
|
|
const char *method UNNEEDED,
|
|
const char *id_prefix UNNEEDED,
|
|
const char *filter UNNEEDED,
|
|
struct command_result *(*cb)(struct command *command UNNEEDED,
|
|
const char *methodname UNNEEDED,
|
|
const char *buf UNNEEDED,
|
|
const jsmntok_t *result UNNEEDED,
|
|
void *arg) UNNEEDED,
|
|
struct command_result *(*errcb)(struct command *command UNNEEDED,
|
|
const char *methodname UNNEEDED,
|
|
const char *buf UNNEEDED,
|
|
const jsmntok_t *result UNNEEDED,
|
|
void *arg) UNNEEDED,
|
|
void *arg)
|
|
|
|
{ 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)
|
|
|
|
{ fprintf(stderr, "jsonrpc_stream_fail called!\n"); abort(); }
|
|
/* Generated stub for jsonrpc_stream_success */
|
|
struct json_stream *jsonrpc_stream_success(struct command *cmd)
|
|
|
|
{ fprintf(stderr, "jsonrpc_stream_success called!\n"); abort(); }
|
|
/* Generated stub for notification_deprecated_out_ok */
|
|
bool notification_deprecated_out_ok(struct plugin *plugin UNNEEDED,
|
|
const char *method UNNEEDED,
|
|
const char *fieldname UNNEEDED,
|
|
const char *depr_start UNNEEDED,
|
|
const char *depr_end UNNEEDED)
|
|
{ fprintf(stderr, "notification_deprecated_out_ok 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_gossmap_logcb */
|
|
void plugin_gossmap_logcb(struct plugin *plugin UNNEEDED,
|
|
enum log_level level UNNEEDED,
|
|
const char *fmt UNNEEDED,
|
|
...)
|
|
{ fprintf(stderr, "plugin_gossmap_logcb 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_obs */
|
|
void plugin_notification_end_obs(struct plugin *plugin UNNEEDED,
|
|
struct json_stream *stream TAKES UNNEEDED)
|
|
{ fprintf(stderr, "plugin_notification_end_obs called!\n"); abort(); }
|
|
/* Generated stub for plugin_notification_start_obs */
|
|
struct json_stream *plugin_notification_start_obs(const tal_t *ctx UNNEEDED,
|
|
const char *method UNNEEDED)
|
|
{ fprintf(stderr, "plugin_notification_start_obs called!\n"); abort(); }
|
|
/* Generated stub for plugin_notify_message */
|
|
void plugin_notify_message(struct command *cmd UNNEEDED,
|
|
enum log_level level UNNEEDED,
|
|
const char *fmt UNNEEDED, ...)
|
|
|
|
{ fprintf(stderr, "plugin_notify_message called!\n"); abort(); }
|
|
/* Generated stub for send_outreq */
|
|
struct command_result *send_outreq(const struct out_req *req UNNEEDED)
|
|
{ fprintf(stderr, "send_outreq 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.flags = cpu_to_be16(GOSSIP_STORE_COMPLETED_BIT);
|
|
hdr.len = cpu_to_be16(tal_count(msg));
|
|
hdr.timestamp = 0;
|
|
hdr.crc = cpu_to_be32(crc32c(be32_to_cpu(hdr.timestamp), msg, tal_count(msg)));
|
|
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,
|
|
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 flags = node_id_idx(from, to);
|
|
u8 *msg;
|
|
|
|
if (disable)
|
|
flags |= ROUTING_FLAGS_DISABLED;
|
|
|
|
/* So valgrind doesn't complain */
|
|
memset(&dummy_sig, 0, sizeof(dummy_sig));
|
|
|
|
msg = towire_channel_update(tmpctx,
|
|
&dummy_sig,
|
|
&chainparams->genesis_blockhash,
|
|
scid, 0,
|
|
ROUTING_OPT_HTLC_MAX_MSAT,
|
|
flags,
|
|
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,
|
|
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(¬_a_secret, 1, sizeof(not_a_secret));
|
|
pubkey_from_secret(¬_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);
|
|
tal_free(msg);
|
|
|
|
/* Also needs a hint as to the funding size. */
|
|
struct amount_sat capacity = AMOUNT_SAT(100000000);
|
|
msg = towire_gossip_store_channel_amount(tmpctx, capacity);
|
|
write_to_store(store_fd, msg);
|
|
tal_free(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 = 10;
|
|
char *gossipfilename;
|
|
struct channel_hint_set *hints = channel_hint_set_new(tmpctx);
|
|
|
|
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, 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, hints, 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",
|
|
fmt_node_id(tmpctx, &ids[i-1]),
|
|
fmt_node_id(tmpctx, &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",
|
|
fmt_node_id(tmpctx, &ids[1]),
|
|
fmt_node_id(tmpctx, &ids[i]),
|
|
1 << i);
|
|
}
|
|
|
|
assert(gossmap_refresh(global_gossmap));
|
|
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",
|
|
fmt_node_id(tmpctx, &ids[0]),
|
|
fmt_node_id(tmpctx, &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);
|
|
}
|
|
|
|
tal_free(hints);
|
|
common_shutdown();
|
|
return 0;
|
|
}
|