pytest: test for signing a signed PSBT.
What changed, and why it matters
This commit adds a test showing that Core Lightning crashes when asked to sign a PSBT (a Bitcoin transaction format) that has already been signed. The test is marked as expected to fail for now. It is a test-only change, not a fix, so the crash itself remains in the codebase. The crash appears to be triggered through a normal RPC call by an authenticated user.
Treat this as a bug report with security-relevant availability impact. Investigate the crash in the `signpsbt` implementation, add defensive handling for already-signed PSBTs, and remove the `xfail` marker once fixed. Consider whether the crash is exploitable by unauthenticated or low-privilege RPC callers.
Security signals we found
Crash on re-signing already-signed PSBT via RPC
Test-only commit documenting a known failure
Potential denial-of-service vector through `signpsbt` RPC
No input validation or idempotency handling for already-signed PSBTs
Evidence from the diff
The commit adds test_sign_signed_psbt in tests/test_wallet.py. It creates a PSBT, signs it once, then calls signpsbt again on the already-signed PSBT. On non-Liquid regtest networks the test expects an RpcError (noting it should ideally be a no-op), while on Liquid regtest it asserts the call returns the same PSBT unchanged. The commit message explicitly says ‘we crash’, indicating the current behavior is a crash. The test is decorated with @pytest.mark.xfail(strict=True), documenting a known failure rather than fixing it.
Changed components
tests/test_wallet.pyRPC command signpsbtPSBT signing logic in Core Lightning walletInspect captured patch +17 / −0
diff --git a/tests/test_wallet.py b/tests/test_wallet.py
index 17cfb92..c34c8ae 100644
--- a/tests/test_wallet.py
+++ b/tests/test_wallet.py
@@ -948,6 +948,23 @@ def test_sign_external_psbt(node_factory, bitcoind, chainparams):
l1.rpc.signpsbt(psbt)
+@pytest.mark.xfail(strict=True)
+def test_sign_signed_psbt(node_factory, bitcoind, chainparams):
+ l1 = node_factory.get_node()
+ l1.fundwallet(10**6)
+
+ psbt = l1.rpc.txprepare([{l1.rpc.newaddr('bech32')['bech32']: 10000}])['psbt']
+ signed_psbt = l1.rpc.signpsbt(psbt)['signed_psbt']
+
+ if TEST_NETWORK != 'liquid-regtest':
+ # FIXME: ideally this would succeed, as a noop. But it shouldn't crash
+ with pytest.raises(RpcError):
+ l1.rpc.signpsbt(signed_psbt)['signed_psbt']
+ else:
+ # Non-taproot works fine.
+ assert l1.rpc.signpsbt(signed_psbt)['signed_psbt'] == signed_psbt
+
+
def test_psbt_version(node_factory, bitcoind, chainparams):
sats_amount = 10**8
Why this scored 43/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.