pytest: test that opening a channel below our own minimum fails *gracefully*.
What changed, and why it matters
This commit adds a test that reveals a bug in how Core Lightning handles channel openings that are below a user's own configured minimum. Currently the node lets the user try to open the channel and only fails after the remote peer gets involved, which is impolite and could leak information or cause unnecessary peer disconnects. The test is marked as expected to fail (xfail), meaning the bug exists but is not yet fixed by this commit.
Treat this as a low-priority bug report rather than an active vulnerability. The maintainers should move the `min-capacity-sat` validation earlier in the `fundchannel` flow so the RPC fails locally without contacting the peer. Reviewers may want to confirm whether the peer-visible WIRE_ERROR could be used to fingerprint node policy or trigger unnecessary disconnects.
Security signals we found
Local policy enforcement bypassed until peer interaction occurs
Potential peer-visible error leakage before local validation
Test-only commit, no runtime fix applied
Expected-fail test documents a bug rather than patching it
Evidence from the diff
The new test test_opening_below_min_capacity_sat demonstrates that when min-capacity-sat is set to 2,000,000 sat and the user attempts to fund a channel below that threshold, Core Lightning does not reject the request locally before contacting the peer. Instead, it proceeds and only fails after the peer responds. The test also shows that even funding exactly 2,000,000 sat results in an effective capacity (funding minus reserves and anchors, capped by max_htlc_value_in_flight) of 1,955,125 sat, which is below the configured minimum. The test is decorated with @pytest.mark.xfail(strict=True), indicating the current behavior is known to be incorrect and the test documents the expected graceful failure. No production code is changed.
Changed components
tests/test_opening.pychannel funding/opening RPC pathmin-capacity-sat policy checkInspect captured patch +29 / −0
diff --git a/tests/test_opening.py b/tests/test_opening.py
index f91f1745..65fa44b5 100644
--- a/tests/test_opening.py
+++ b/tests/test_opening.py
@@ -2796,3 +2796,32 @@ def test_zeroconf_forget(node_factory, bitcoind, dopay: bool):
# It will forget the older one.
l2.daemon.wait_for_log(r"UNUSUAL {}-chan#1: Forgetting channel: It has been {} blocks without the funding transaction ".format(l1.info['id'], blocks + 1))
assert [c['peer_id'] for c in l2.rpc.listpeerchannels()["channels"]] == [l3.info['id']]
+
+
+@unittest.skipIf(TEST_NETWORK != 'regtest', 'elementsd gives different numbers')
+@pytest.mark.xfail(strict=True)
+def test_opening_below_min_capacity_sat(bitcoind, node_factory):
+ """OK, here's what happens:
+
+ The user configures min-capacity-sat=2,000,000.
+ They try to open a channel with 591,000 sat
+ We let them (for some reason), which kinda makes sense: it's their own rules
+ We then get upset when you accept!
+
+ The "capacity" here is the effective capacity of the channel, which is capped at funding - (reserves and 2 anchors), and at max_htlc_value_in_flight.
+ """
+ l1, l2 = node_factory.line_graph(2, fundchannel=False, opts=[{'min-capacity-sat': 2_000_000}, {}])
+
+ l1.fundwallet(3_000_000)
+
+ with pytest.raises(RpcError, match=r'which is below 2000000sat'):
+ l1.rpc.fundchannel(l2.info['id'], "591000sat")
+
+ l1.connect(l2)
+
+ # Even with the exact amount, the *capacity* is different.
+ with pytest.raises(RpcError, match=r'channel capacity is 1955125sat, which is below 2000000sat'):
+ l1.rpc.fundchannel(l2.info['id'], "2000000sat")
+
+ # But we shouldn't have bothered l2
+ assert not l2.daemon.is_in_log('peer_in WIRE_ERROR')
Why this scored 27/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.