pytest: fix test_xpay_fake_channeld flake
What changed, and why it matters
This commit fixes a flaky automated test. It adds a developer-only switch to stop the xpay plugin from periodically calling another plugin (cln-askrene) during the test, because that background call was failing when the test deliberately replaced cln-askrene with a fake version. There is no security vulnerability here; it is purely a test reliability and log-noise reduction change.
No security action required. This is a test-only reliability fix.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The patch introduces a hidden dev flag dev-xpay-no-age in plugins/xpay/xpay.c that suppresses the start_aging_timer -> age_layer -> askrene-age RPC calls. The test test_xpay_fake_channeld then enables this flag so xpay does not attempt to age askrene data while the test swaps out cln-askrene for a fake channeld test plugin. The test also lowers log levels to reduce output size. The change is gated behind a plugin_option_dev flag and defaults to off in production.
Changed components
plugins/xpay/xpay.ctests/test_xpay.pyInspect captured patch +19 / −2
diff --git a/plugins/xpay/xpay.c b/plugins/xpay/xpay.c
index 16717ce3..4528a9e2 100644
--- a/plugins/xpay/xpay.c
+++ b/plugins/xpay/xpay.c
@@ -38,6 +38,8 @@ struct xpay {
bool take_over_pay;
/* Are we to wait for all parts to complete before returning? */
bool slow_mode;
+ /* Suppress calls to askrene-age */
+ bool dev_no_age;
};
static struct xpay *xpay_of(struct plugin *plugin)
@@ -2098,6 +2100,10 @@ static struct command_result *age_layer(struct command *timer_cmd, void *unused)
static void start_aging_timer(struct plugin *plugin)
{
+ struct xpay *xpay = xpay_of(plugin);
+
+ if (xpay->dev_no_age)
+ return;
notleak(global_timer(plugin, time_from_sec(60), age_layer, NULL));
}
@@ -2422,6 +2428,7 @@ int main(int argc, char *argv[])
xpay = tal(NULL, struct xpay);
xpay->take_over_pay = false;
xpay->slow_mode = false;
+ xpay->dev_no_age = false;
plugin_main(argv, init, take(xpay),
PLUGIN_RESTARTABLE, true, NULL,
commands, ARRAY_SIZE(commands),
@@ -2434,5 +2441,8 @@ int main(int argc, char *argv[])
plugin_option_dynamic("xpay-slow-mode", "bool",
"Wait until all parts have completed before returning success or failure",
bool_option, bool_jsonfmt, &xpay->slow_mode),
+ plugin_option_dev("dev-xpay-no-age", "flag",
+ "Don't call askrene-age",
+ flag_option, flag_jsonfmt, &xpay->dev_no_age),
NULL);
}
diff --git a/tests/test_xpay.py b/tests/test_xpay.py
index 1244e54c..71e6e8bc 100644
--- a/tests/test_xpay.py
+++ b/tests/test_xpay.py
@@ -227,14 +227,21 @@ def test_xpay_fake_channeld(node_factory, bitcoind, chainparams, slow_mode):
# l2 will warn l1 about its invalid gossip: ignore.
# We throttle l1's gossip to avoid massive log spam.
+ # Suppress debug and below because logs are huge
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},
- {'allow_bad_gossip': True}])
+ 'dev-throttle-gossip': None,
+ 'log-level': 'info',
+ # xpay gets upset if it's aging when we remove cln-askrene!
+ 'dev-xpay-no-age': None,
+ },
+ {'allow_bad_gossip': True,
+ 'log-level': 'info',
+ }])
# 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")
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.