lightningd: expose funding PSBT (if we have it) in JSON API.
What changed, and why it matters
This commit adds a new read-only field to two JSON-RPC API responses that exposes the funding PSBT (a Bitcoin transaction planning document) for a channel, if one is stored. It is an informational API enhancement and does not change how transactions are signed, validated, or broadcast. There is no indication in the commit that this fixes a security bug.
No security action required. Treat as a normal feature addition. Operators concerned about information leakage should review RPC access controls, since the PSBT may reveal channel funding details to any caller with listpeers/listclosedchannels access.
Security signals we found
No security-relevant code paths altered
Read-only exposure of existing internal state
No input parsing or authorization changes
No changelog or commit message security framing
Evidence from the diff
The patch extends listpeerchannels and listclosedchannels to include the existing in-memory/on-disk channel->funding_psbt value in JSON output. It updates generated schema files, static schema JSON, the C code that emits the JSON, and a Python test that introspects the SQL/JSON schema. The data was already held by lightningd; this change only makes it visible to RPC consumers. No parsing, signing, or wire logic is modified.
Changed components
lightningd JSON-RPC API (listpeerchannels, listclosedchannels)contrib/msggen/msggen/schema.jsondoc/schemas/listclosedchannels.jsondoc/schemas/listpeerchannels.jsonlightningd/closed_channel.clightningd/peer_control.ctests/test_plugin.pyInspect captured patch +36 / −0
diff --git a/contrib/msggen/msggen/schema.json b/contrib/msggen/msggen/schema.json
index f3e23cc..5c21622 100644
--- a/contrib/msggen/msggen/schema.json
+++ b/contrib/msggen/msggen/schema.json
@@ -18584,6 +18584,13 @@
"The 0-based output number of the funding transaction which opens the channel."
]
},
+ "funding_psbt": {
+ "type": "string",
+ "added": "v25.12",
+ "description": [
+ "The PSBT (may be non-final or unsigned) we should use to open the channel, if any"
+ ]
+ },
"leased": {
"type": "boolean",
"description": [
@@ -24327,6 +24334,13 @@
"description": [
"Amount we were paid by peer at open."
]
+ },
+ "psbt": {
+ "type": "string",
+ "added": "v25.12",
+ "description": [
+ "The PSBT (may be non-final or unsigned) we should use to open the channel, if any. This is initially from `fundchannel_complete`, but will be updated with if `sendpsbt` is called with an updated PSBT."
+ ]
}
}
},
diff --git a/doc/schemas/listclosedchannels.json b/doc/schemas/listclosedchannels.json
index becf033..d190195 100644
--- a/doc/schemas/listclosedchannels.json
+++ b/doc/schemas/listclosedchannels.json
@@ -188,6 +188,13 @@
"The 0-based output number of the funding transaction which opens the channel."
]
},
+ "funding_psbt": {
+ "type": "string",
+ "added": "v25.12",
+ "description": [
+ "The PSBT (may be non-final or unsigned) we should use to open the channel, if any"
+ ]
+ },
"leased": {
"type": "boolean",
"description": [
diff --git a/doc/schemas/listpeerchannels.json b/doc/schemas/listpeerchannels.json
index 16844c0..e3521e4 100644
--- a/doc/schemas/listpeerchannels.json
+++ b/doc/schemas/listpeerchannels.json
@@ -502,6 +502,13 @@
"description": [
"Amount we were paid by peer at open."
]
+ },
+ "psbt": {
+ "type": "string",
+ "added": "v25.12",
+ "description": [
+ "The PSBT (may be non-final or unsigned) we should use to open the channel, if any. This is initially from `fundchannel_complete`, but will be updated with if `sendpsbt` is called with an updated PSBT."
+ ]
}
}
},
diff --git a/lightningd/closed_channel.c b/lightningd/closed_channel.c
index 73b572f..2e18d40 100644
--- a/lightningd/closed_channel.c
+++ b/lightningd/closed_channel.c
@@ -59,6 +59,8 @@ static void json_add_closed_channel(struct json_stream *response,
} else if (!amount_msat_is_zero(channel->push))
json_add_amount_msat(response, "funding_pushed_msat",
channel->push);
+ if (channel->funding_psbt)
+ json_add_psbt(response, "funding_psbt", channel->funding_psbt);
json_add_amount_sat_msat(response, "total_msat", channel->funding_sats);
json_add_amount_msat(response, "final_to_us_msat", channel->our_msat);
diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c
index e14f597..e2b582e 100644
--- a/lightningd/peer_control.c
+++ b/lightningd/peer_control.c
@@ -1155,6 +1155,8 @@ static void NON_NULL_ARGS(1, 2, 4, 5) json_add_channel(struct command *cmd,
channel->push);
}
+ if (channel->funding_psbt)
+ json_add_psbt(response, "psbt", channel->funding_psbt);
json_object_end(response);
if (!amount_sat_to_msat(&funding_msat, channel->funding_sats)) {
diff --git a/tests/test_plugin.py b/tests/test_plugin.py
index 61cb6b5..2e5f926 100644
--- a/tests/test_plugin.py
+++ b/tests/test_plugin.py
@@ -3295,6 +3295,8 @@ def test_sql(node_factory, bitcoind):
'type': 'txid'},
{'name': 'funding_outnum',
'type': 'u32'},
+ {'name': 'funding_psbt',
+ 'type': 'string'},
{'name': 'leased',
'type': 'boolean'},
{'name': 'funding_fee_paid_msat',
@@ -3606,6 +3608,8 @@ def test_sql(node_factory, bitcoind):
'type': 'msat'},
{'name': 'funding_fee_rcvd_msat',
'type': 'msat'},
+ {'name': 'funding_psbt',
+ 'type': 'string'},
{'name': 'to_us_msat',
'type': 'msat'},
{'name': 'min_to_us_msat',
Why this scored 19/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.