mirror of
https://github.com/ElementsProject/lightning.git
synced 2024-11-19 09:54:16 +01:00
common: add routing test using real data which crashes.
The amount is set not to crash by default, but run "common/test/run-route-infloop 8388607" and you'll see a crash. Sorry about the 7MB blob, but this testing was quite revealing and I consider it worth adding. Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
parent
0a7e6211df
commit
fdfffdc232
@ -38,7 +38,7 @@ common/test/run-json: \
|
|||||||
wire/peer_wiregen.o \
|
wire/peer_wiregen.o \
|
||||||
wire/towire.o
|
wire/towire.o
|
||||||
|
|
||||||
common/test/run-route common/test/run-route-specific: \
|
common/test/run-route common/test/run-route-specific common/test/run-route-infloop: \
|
||||||
common/amount.o \
|
common/amount.o \
|
||||||
common/dijkstra.o \
|
common/dijkstra.o \
|
||||||
common/fp16.o \
|
common/fp16.o \
|
||||||
|
161
common/test/run-route-infloop.c
Normal file
161
common/test/run-route-infloop.c
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
/* Test based on bug report from Ken Sedgewick, where routing would crash.
|
||||||
|
* I took his gossip_store (this version is compacted) and brute forced routes from
|
||||||
|
* his node with different amounts until I reproduced it */
|
||||||
|
#include "config.h"
|
||||||
|
#include <assert.h>
|
||||||
|
#include <common/channel_type.h>
|
||||||
|
#include <common/dijkstra.h>
|
||||||
|
#include <common/gossmap.h>
|
||||||
|
#include <common/gossip_store.h>
|
||||||
|
#include <common/route.h>
|
||||||
|
#include <common/setup.h>
|
||||||
|
#include <common/type_to_string.h>
|
||||||
|
#include <common/utils.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <wire/peer_wiregen.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
/* AUTOGENERATED MOCKS START */
|
||||||
|
/* 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 fromwire_tlv */
|
||||||
|
bool fromwire_tlv(const u8 **cursor UNNEEDED, size_t *max UNNEEDED,
|
||||||
|
const struct tlv_record_type *types UNNEEDED, size_t num_types UNNEEDED,
|
||||||
|
void *record UNNEEDED, struct tlv_field **fields UNNEEDED,
|
||||||
|
const u64 *extra_types UNNEEDED, size_t *err_off UNNEEDED, u64 *err_type UNNEEDED)
|
||||||
|
{ fprintf(stderr, "fromwire_tlv 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(); }
|
||||||
|
/* Generated stub for towire_tlv */
|
||||||
|
void towire_tlv(u8 **pptr UNNEEDED,
|
||||||
|
const struct tlv_record_type *types UNNEEDED, size_t num_types UNNEEDED,
|
||||||
|
const void *record UNNEEDED)
|
||||||
|
{ fprintf(stderr, "towire_tlv called!\n"); abort(); }
|
||||||
|
/* AUTOGENERATED MOCKS END */
|
||||||
|
|
||||||
|
/* Node id 03942f5fe67645fdce4584e7f159c1f0a396b05fbc15f0fb7d6e83c553037b1c73 */
|
||||||
|
static struct gossmap *gossmap;
|
||||||
|
|
||||||
|
static u64 capacity_bias(const struct gossmap *map,
|
||||||
|
const struct gossmap_chan *c,
|
||||||
|
int dir,
|
||||||
|
struct amount_msat amount)
|
||||||
|
{
|
||||||
|
struct amount_sat capacity;
|
||||||
|
u64 amtmsat = amount.millisatoshis; /* Raw: lengthy math */
|
||||||
|
double capmsat;
|
||||||
|
|
||||||
|
/* Can fail in theory if gossmap changed underneath. */
|
||||||
|
if (!gossmap_chan_get_capacity(map, c, &capacity))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
capmsat = (double)capacity.satoshis * 1000; /* Raw: lengthy math */
|
||||||
|
return -log((capmsat + 1 - amtmsat) / (capmsat + 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
static u64 route_score(u32 distance,
|
||||||
|
struct amount_msat cost,
|
||||||
|
struct amount_msat risk,
|
||||||
|
int dir,
|
||||||
|
const struct gossmap_chan *c)
|
||||||
|
{
|
||||||
|
u64 cmsat = cost.millisatoshis; /* Raw: lengthy math */
|
||||||
|
u64 rmsat = risk.millisatoshis; /* Raw: lengthy math */
|
||||||
|
u64 bias = capacity_bias(gossmap, c, dir, cost);
|
||||||
|
|
||||||
|
/* Smoothed harmonic mean to avoid division by 0 */
|
||||||
|
u64 costs = (cmsat * rmsat * bias) / (cmsat + rmsat + bias + 1);
|
||||||
|
|
||||||
|
if (costs > 0xFFFFFFFF)
|
||||||
|
costs = 0xFFFFFFFF;
|
||||||
|
return costs;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
const double riskfactor = 10;
|
||||||
|
const u32 final_delay = 159;
|
||||||
|
struct node_id my_id;
|
||||||
|
const struct gossmap_node **nodes, *me;
|
||||||
|
u64 amt;
|
||||||
|
|
||||||
|
common_setup(argv[0]);
|
||||||
|
|
||||||
|
/* 8388607 crashes, use half that so we don't break CI! */
|
||||||
|
if (!argv[1])
|
||||||
|
amt = 8388607 / 2;
|
||||||
|
else
|
||||||
|
amt = atol(argv[1]);
|
||||||
|
gossmap = gossmap_load(tmpctx, "tests/data/routing_gossip_store", NULL);
|
||||||
|
|
||||||
|
nodes = tal_arr(tmpctx, const struct gossmap_node *, 0);
|
||||||
|
for (struct gossmap_node *n = gossmap_first_node(gossmap);
|
||||||
|
n;
|
||||||
|
n = gossmap_next_node(gossmap, n)) {
|
||||||
|
tal_arr_expand(&nodes, n);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(node_id_from_hexstr("03942f5fe67645fdce4584e7f159c1f0a396b05fbc15f0fb7d6e83c553037b1c73",
|
||||||
|
strlen("03942f5fe67645fdce4584e7f159c1f0a396b05fbc15f0fb7d6e83c553037b1c73"),
|
||||||
|
&my_id));
|
||||||
|
me = gossmap_find_node(gossmap, &my_id);
|
||||||
|
assert(me);
|
||||||
|
|
||||||
|
printf("Destination node, success, probability, hops, fees, cltv, scid...\n");
|
||||||
|
for (size_t i = 0; i < tal_count(nodes); i++) {
|
||||||
|
const struct dijkstra *dij;
|
||||||
|
struct route_hop *r;
|
||||||
|
struct node_id them;
|
||||||
|
|
||||||
|
if (nodes[i] == me)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
dij = dijkstra(tmpctx, gossmap, nodes[i], amount_msat(amt), riskfactor,
|
||||||
|
route_can_carry, route_score, NULL);
|
||||||
|
r = route_from_dijkstra(tmpctx, gossmap, dij, me, amount_msat(amt), final_delay);
|
||||||
|
|
||||||
|
gossmap_node_get_id(gossmap, nodes[i], &them);
|
||||||
|
|
||||||
|
printf("%s,", node_id_to_hexstr(tmpctx, &them));
|
||||||
|
if (!r) {
|
||||||
|
printf("0,0.0,");
|
||||||
|
} else {
|
||||||
|
double probability = 1;
|
||||||
|
for (size_t j = 0; j < tal_count(r); j++) {
|
||||||
|
struct amount_sat capacity_sat;
|
||||||
|
u64 cap_msat;
|
||||||
|
struct gossmap_chan *c = gossmap_find_chan(gossmap, &r[j].scid);
|
||||||
|
assert(c);
|
||||||
|
assert(gossmap_chan_get_capacity(gossmap, c, &capacity_sat));
|
||||||
|
|
||||||
|
cap_msat = capacity_sat.satoshis * 1000;
|
||||||
|
/* Assume linear distribution, implying probability depends on
|
||||||
|
* amount we would leave in channel */
|
||||||
|
assert(cap_msat >= r[0].amount.millisatoshis);
|
||||||
|
probability *= (double)(cap_msat - r[0].amount.millisatoshis) / cap_msat;
|
||||||
|
}
|
||||||
|
printf("1,%f,%zu,%"PRIu64",%u",
|
||||||
|
probability,
|
||||||
|
tal_count(r),
|
||||||
|
r[0].amount.millisatoshis - amt,
|
||||||
|
r[0].delay - final_delay);
|
||||||
|
for (size_t j = 0; j < tal_count(r); j++)
|
||||||
|
printf(",%s/%u", short_channel_id_to_str(tmpctx, &r[j].scid), r[j].direction);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
common_shutdown();
|
||||||
|
return 0;
|
||||||
|
}
|
BIN
tests/data/routing_gossip_store
Normal file
BIN
tests/data/routing_gossip_store
Normal file
Binary file not shown.
@ -1,6 +1,6 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
if git --no-pager grep -nHiE 'l[ightn]{6}g|l[ightn]{8}g|ilghtning|lgihtning|lihgtning|ligthning|lighnting|lightinng|lightnnig|lightnign' -- . ':!tools/check-spelling.sh' | grep -vE "highlighting|LightningGrpc"; then
|
if git --no-pager grep -nHiE 'l[ightn]{6}g|l[ightn]{8}g|ilghtning|lgihtning|lihgtning|ligthning|lighnting|lightinng|lightnnig|lightnign' -- . ':!tools/check-spelling.sh' ':!tests/data/routing_gossip_store' | grep -vE "highlighting|LightningGrpc"; then
|
||||||
echo "Identified a likely misspelling of the word \"lightning\" (see above). Please fix."
|
echo "Identified a likely misspelling of the word \"lightning\" (see above). Please fix."
|
||||||
echo "Is this warning incorrect? Please teach tools/check-spelling.sh about the exciting new word."
|
echo "Is this warning incorrect? Please teach tools/check-spelling.sh about the exciting new word."
|
||||||
exit 1
|
exit 1
|
||||||
|
Loading…
Reference in New Issue
Block a user