Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -1156,6 +1156,9 @@ ccan-rune-rune.o: $(CCANDIR)/ccan/rune/rune.c
ccan-rune-coding.o: $(CCANDIR)/ccan/rune/coding.c
@$(call VERBOSE, "cc $<", $(CC) $(CFLAGS) -c -o $@ $<)

canned-gossmap: devtools/gossmap-compress
DATE=`date +%Y-%m-%d` && devtools/gossmap-compress compress --output-node-map /tmp/gossip_store tests/data/gossip-store-$$DATE.compressed > tests/data/gossip-store-$$DATE-node-map && xz -9 tests/data/gossip-store-$$DATE-node-map && ls -l tests/data/gossip-store-$$DATE*

print-binary-sizes: $(ALL_PROGRAMS) $(ALL_TEST_PROGRAMS) $(BIN_PROGRAMS)
@echo User programs:
@size -t $(PKGLIBEXEC_PROGRAMS) $(filter-out tools/reckless,$(BIN_PROGRAMS)) $(PLUGINS)
Expand Down
Binary file removed tests/data/gossip-store-2024-09-22-node-map.xz
Binary file not shown.
Binary file removed tests/data/gossip-store-2024-09-22.compressed
Binary file not shown.
Binary file added tests/data/gossip-store-2026-02-03-node-map.xz
Binary file not shown.
Binary file added tests/data/gossip-store-2026-02-03.compressed
Binary file not shown.
9 changes: 5 additions & 4 deletions tests/plugins/channeld_fakenet.c
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,8 @@ static u64 channel_range(const struct info *info,
const struct short_channel_id_dir *scidd,
u64 min, u64 max)
{
return min + (siphash24(&info->seed, scidd, sizeof(scidd)) % max);
assert(max != min);
return min + (siphash24(&info->seed, scidd, sizeof(scidd)) % (max - min));
}

void ecdh(const struct pubkey *point, struct secret *ss)
Expand Down Expand Up @@ -839,9 +840,9 @@ static void forward_htlc(struct info *info,
dfwd->path_key = tal_steal(dfwd, next_path_key);
dfwd->expected = next;

/* Delay 0.1 - 1 seconds, but skewed lower */
msec_delay = channel_range(info, &scidd, 0, 900);
msec_delay = 100 + channel_range(info, &scidd, 0, msec_delay);
/* Delay 1 - 100 milliseconds, but skewed lower */
msec_delay = channel_range(info, &scidd, 1, 90);
msec_delay = 10 + channel_range(info, &scidd, 0, msec_delay);

status_debug("Delaying %u msec for %s",
msec_delay, fmt_short_channel_id_dir(tmpctx, &scidd));
Expand Down
83 changes: 30 additions & 53 deletions tests/test_askrene.py
Original file line number Diff line number Diff line change
Expand Up @@ -1431,17 +1431,27 @@ def test_min_htlc_after_excess(node_factory, bitcoind):
final_cltv=10)


# These were obviously having a bad day at the time of the snapshot:
canned_gossmap_badnodes = {
19: "We could not find a usable set of paths. The shortest path is 103x1x0->0x2134x0->0x333x988->19x333x16169, but 0x2134x0/0 exceeds htlc_maximum_msat ~1000448msat",
53: "We could not find a usable set of paths. The destination has disabled 177 of 177 channels, leaving capacity only 0msat of 4003677000msat.",
69: "We could not find a usable set of paths. The destination has disabled 151 of 151 channels, leaving capacity only 0msat of 9092303000msat.",
72: "We could not find a usable set of paths. The destination has disabled 146 of 146 channels, leaving capacity only 0msat of 1996000000msat.",
86: "We could not find a usable set of paths. The destination has disabled 131 of 131 channels, leaving capacity only 0msat of 162000000msat.",
}


@pytest.mark.slow_test
def test_real_data(node_factory, bitcoind):
# Route from Rusty's node to the top nodes
# From tests/data/gossip-store-2024-09-22-node-map.xz:
# Me: 3301:024b9a1fa8e006f1e3937f65f66c408e6da8e1ca728ea43222a7381df1cc449605:BLUEIRON
# So we make l2 node 3301.
# From tests/data/gossip-store-2026-02-03-node-map.xz:
# Me: 2134:024b9a1fa8e006f1e3937f65f66c408e6da8e1ca728ea43222a7381df1cc449605:BLUEIRON
# So we make l2 node 2134.
outfile = tempfile.NamedTemporaryFile(prefix='gossip-store-')
nodeids = subprocess.check_output(['devtools/gossmap-compress',
'decompress',
'--node-map=3301=033845802d25b4e074ccfd7cd8b339a41dc75bf9978a034800444b51d42b07799a',
'tests/data/gossip-store-2024-09-22.compressed',
'--node-map=2134=033845802d25b4e074ccfd7cd8b339a41dc75bf9978a034800444b51d42b07799a',
'tests/data/gossip-store-2026-02-03.compressed',
outfile.name]).decode('utf-8').splitlines()

# This is in msat, but is also the size of channel we create.
Expand All @@ -1457,34 +1467,21 @@ def test_real_data(node_factory, bitcoind):
'askrene-timeout': TIMEOUT},
{'allow_warning': True}])

# These were obviously having a bad day at the time of the snapshot:
badnodes = {
# 62:03dbe3fedd4f6e7f7020c69e6d01453d5a69f9faa1382901cf3028f1e997ef2814:BTC_👽👽👽👽👽👽
62: " marked disabled by gossip message",
# This one has 151 channels, with peers each only connected to it. An island!
# 76:0298906458987af756e2a43b208c03499c4d2bde630d4868dda0ea6a184f87c62a:0298906458987af756e2
76: "There is no connection between source and destination at all",
# 80:02d246c519845e7b23b02684d64ca23b750958e0307f9519849ee2535e3637999a:SLIMYRAGE-
80: " marked disabled by gossip message",
# 97:034a5fdb2df3ce1bfd2c2aca205ce9cfeef1a5f4af21b0b5e81c453080c30d7683:🚶LightningTransact
97: r"We could not find a usable set of paths\. The shortest path is 103x1x0->0x3301x1646->0x1281x2323->97x1281x33241, but 97x1281x33241/1 isn't big enough to carry 100000000msat\.",
}

# CI, it's slow.
if SLOW_MACHINE:
limit = 25
expected = (6, 25, 1568821, 144649, 91)
expected = (9, 24, 1935647, 219066, 89)
else:
limit = 100
expected = (9, 96, 6565466, 668476, 90)
expected = (11, 95, 8026484, 925406, 89)

fees = {}
for n in range(0, limit):
# 0.5% is the norm
MAX_FEE = AMOUNT // 200

if n in badnodes:
with pytest.raises(RpcError, match=badnodes[n]):
if n in canned_gossmap_badnodes:
with pytest.raises(RpcError, match=canned_gossmap_badnodes[n]):
l1.rpc.getroutes(source=l1.info['id'],
destination=nodeids[n],
amount_msat=AMOUNT,
Expand Down Expand Up @@ -1552,14 +1549,14 @@ def test_real_data(node_factory, bitcoind):
@pytest.mark.slow_test
def test_real_biases(node_factory, bitcoind):
# Route from Rusty's node to the top 100.
# From tests/data/gossip-store-2024-09-22-node-map.xz:
# Me: 3301:024b9a1fa8e006f1e3937f65f66c408e6da8e1ca728ea43222a7381df1cc449605:BLUEIRON
# So we make l2 node 3301.
# From tests/data/gossip-store-2026-02-03-node-map.xz:
# Me: 2134:024b9a1fa8e006f1e3937f65f66c408e6da8e1ca728ea43222a7381df1cc449605:BLUEIRON
# So we make l2 node 2134.
outfile = tempfile.NamedTemporaryFile(prefix='gossip-store-')
nodeids = subprocess.check_output(['devtools/gossmap-compress',
'decompress',
'--node-map=3301=033845802d25b4e074ccfd7cd8b339a41dc75bf9978a034800444b51d42b07799a',
'tests/data/gossip-store-2024-09-22.compressed',
'--node-map=2134=033845802d25b4e074ccfd7cd8b339a41dc75bf9978a034800444b51d42b07799a',
'tests/data/gossip-store-2026-02-03.compressed',
outfile.name]).decode('utf-8').splitlines()

# This is in msat, but is also the size of channel we create.
Expand All @@ -1573,26 +1570,13 @@ def test_real_biases(node_factory, bitcoind):
'dev-throttle-gossip': None},
{'allow_warning': True}])

# These were obviously having a bad day at the time of the snapshot:
badnodes = {
# 62:03dbe3fedd4f6e7f7020c69e6d01453d5a69f9faa1382901cf3028f1e997ef2814:BTC_👽👽👽👽👽👽
62: " marked disabled by gossip message",
# This one has 151 channels, with peers each only connected to it. An island!
# 76:0298906458987af756e2a43b208c03499c4d2bde630d4868dda0ea6a184f87c62a:0298906458987af756e2
76: "There is no connection between source and destination at all",
# 80:02d246c519845e7b23b02684d64ca23b750958e0307f9519849ee2535e3637999a:SLIMYRAGE-
80: " marked disabled by gossip message",
# 97:034a5fdb2df3ce1bfd2c2aca205ce9cfeef1a5f4af21b0b5e81c453080c30d7683:🚶LightningTransact
97: r"We could not find a usable set of paths\. The shortest path is 103x1x0->0x3301x1646->0x1281x2323->97x1281x33241, but 97x1281x33241/1 isn't big enough to carry 100000000msat\.",
}

# CI, it's slow.
if SLOW_MACHINE:
limit = 25
expected = ({1: 6, 2: 6, 4: 7, 8: 12, 16: 14, 32: 19, 64: 25, 100: 25}, 0)
expected = ({1: 6, 2: 7, 4: 12, 8: 13, 16: 18, 32: 23, 64: 24, 100: 24}, 0)
else:
limit = 100
expected = ({1: 22, 2: 25, 4: 36, 8: 53, 16: 69, 32: 80, 64: 96, 100: 96}, 0)
expected = ({1: 26, 2: 33, 4: 48, 8: 57, 16: 77, 32: 90, 64: 95, 100: 95}, 0)

l1.rpc.askrene_create_layer('biases')
num_changed = {}
Expand All @@ -1604,7 +1588,7 @@ def test_real_biases(node_factory, bitcoind):
# 0.5% is the norm
MAX_FEE = AMOUNT // 200

if n in badnodes:
if n in canned_gossmap_badnodes:
continue

route = l1.rpc.getroutes(source=l1.info['id'],
Expand Down Expand Up @@ -1638,13 +1622,6 @@ def amount_through_chan(chan, routes):
amount_after = amount_through_chan(chan, route2['routes'])
if amount_after < amount_before:
num_changed[bias] += 1
else:
# We bias -4 against 83x88x31908/0 going to node 83, and this is violated.
# Both routes contain three paths, all via 83x88x31908/0.
# The first amounts 49490584, 1018832, 49490584,
# The second amounts 25254708, 25254708, 49490584,
# Due to fees and rounding, we actually spend 1msat more on the second case!
assert (n, bias, chan) == (83, 4, '83x88x31908/0')

# Undo bias
l1.rpc.askrene_bias_channel(layer='biases', short_channel_id_dir=chan, bias=0)
Expand Down Expand Up @@ -1677,8 +1654,8 @@ def test_askrene_fake_channeld(node_factory, bitcoind):
outfile = tempfile.NamedTemporaryFile(prefix='gossip-store-')
nodeids = subprocess.check_output(['devtools/gossmap-compress',
'decompress',
'--node-map=3301=033845802d25b4e074ccfd7cd8b339a41dc75bf9978a034800444b51d42b07799a',
'tests/data/gossip-store-2024-09-22.compressed',
'--node-map=2134=033845802d25b4e074ccfd7cd8b339a41dc75bf9978a034800444b51d42b07799a',
'tests/data/gossip-store-2026-02-03.compressed',
outfile.name]).decode('utf-8').splitlines()
AMOUNT = 100_000_000

Expand All @@ -1705,7 +1682,7 @@ def test_askrene_fake_channeld(node_factory, bitcoind):

l1.rpc.askrene_create_layer('test_askrene_fake_channeld')
for n in range(0, 100):
if n in (62, 76, 80, 97):
if n in canned_gossmap_badnodes:
continue

print(f"PAYING Node #{n}")
Expand Down
67 changes: 10 additions & 57 deletions tests/test_xpay.py
Original file line number Diff line number Diff line change
Expand Up @@ -213,17 +213,22 @@ def test_xpay_selfpay(node_factory):
l1.rpc.xpay(b12)


# These were obviously having a bad day at the time of the snapshot:
canned_gossmap_badnodes = [19, 53, 69, 72, 86]


@pytest.mark.slow_test
@unittest.skipIf(TEST_NETWORK != 'regtest', '29-way split for node 17 is too dusty on elements')
@pytest.mark.parametrize("slow_mode", [False, True])
def test_xpay_fake_channeld(node_factory, bitcoind, chainparams, slow_mode):
outfile = tempfile.NamedTemporaryFile(prefix='gossip-store-')
nodeids = subprocess.check_output(['devtools/gossmap-compress',
'decompress',
'--node-map=3301=033845802d25b4e074ccfd7cd8b339a41dc75bf9978a034800444b51d42b07799a',
'tests/data/gossip-store-2024-09-22.compressed',
'--node-map=2134=033845802d25b4e074ccfd7cd8b339a41dc75bf9978a034800444b51d42b07799a',
'tests/data/gossip-store-2026-02-03.compressed',
outfile.name]).decode('utf-8').splitlines()
AMOUNT = 100_000_000
# 100,000sat gave no failures at all, which is not very interesting!
AMOUNT = 500_000_000

# l2 will warn l1 about its invalid gossip: ignore.
# We throttle l1's gossip to avoid massive log spam.
Expand Down Expand Up @@ -254,7 +259,7 @@ def test_xpay_fake_channeld(node_factory, bitcoind, chainparams, slow_mode):
l1.rpc.setconfig('xpay-slow-mode', slow_mode)
failed_parts = []
for n in range(0, 100):
if n in (62, 76, 80, 97):
if n in canned_gossmap_badnodes:
continue

print(f"PAYING Node #{n}")
Expand Down Expand Up @@ -289,7 +294,7 @@ def test_xpay_fake_channeld(node_factory, bitcoind, chainparams, slow_mode):

failed_parts_retry = []
for n in range(0, 100):
if n in (62, 76, 80, 97):
if n in canned_gossmap_badnodes:
continue

print(f"PAYING Node #{n}")
Expand Down Expand Up @@ -537,58 +542,6 @@ def test_xpay_preapprove(node_factory):
l1.rpc.xpay(inv)


@unittest.skipIf(TEST_NETWORK != 'regtest', 'too dusty on elements')
@pytest.mark.slow_test
def test_xpay_maxfee(node_factory, bitcoind, chainparams):
"""Test which shows that we don't excees maxfee"""
outfile = tempfile.NamedTemporaryFile(prefix='gossip-store-')
subprocess.check_output(['devtools/gossmap-compress',
'decompress',
'--node-map=3301=033845802d25b4e074ccfd7cd8b339a41dc75bf9978a034800444b51d42b07799a',
'tests/data/gossip-store-2024-09-22.compressed',
outfile.name]).decode('utf-8').splitlines()
AMOUNT = 100_000_000

# l2 will warn l1 about its invalid gossip: ignore.
# We throttle l1's gossip to avoid massive log spam.
l1, l2 = node_factory.line_graph(2,
# This is in sats, so 1000x amount we send.
fundamount=AMOUNT,
opts=[{'gossip_store_file': outfile.name,
'subdaemon': 'channeld:../tests/plugins/channeld_fakenet',
'allow_warning': True,
'dev-throttle-gossip': None,
# This can be more than 10 seconds under CI!
'askrene-timeout': 60},
{'allow_bad_gossip': True}])

# l1 needs to know l2's shaseed for the channel so it can make revocations
hsmfile = os.path.join(l2.daemon.lightning_dir, TEST_NETWORK, "hsm_secret")
# Needs peer node id and channel dbid (1, it's the first channel), prints out:
# "shaseed: xxxxxxx\n"
shaseed = subprocess.check_output(["tools/lightning-hsmtool", "dumpcommitments", l1.info['id'], "1", "0", hsmfile]).decode('utf-8').strip().partition(": ")[2]
l1.rpc.dev_peer_shachain(l2.info['id'], shaseed)

# This one triggers the bug!
n = 59
maxfee = 57966
preimage_hex = bytes([n + 100]).hex() + '00' * 31
hash_hex = sha256(bytes.fromhex(preimage_hex)).hexdigest()
inv = subprocess.check_output(["devtools/bolt11-cli",
"encode",
n.to_bytes(length=8, byteorder=sys.byteorder).hex() + '01' * 24,
f"currency={chainparams['bip173_prefix']}",
f"p={hash_hex}",
"9=020000", # option_basic_mpp
f"s={'00' * 32}",
f"d=Paying node {n} with maxfee",
f"amount={AMOUNT}msat"]).decode('utf-8').strip()

ret = l1.rpc.xpay(invstring=inv, maxfee=maxfee)
fee = ret['amount_sent_msat'] - ret['amount_msat']
assert fee <= maxfee


def test_xpay_maxdelay(node_factory):
l1, l2 = node_factory.line_graph(2, wait_for_announce=True)

Expand Down
Loading