cln-grpc: fix txids encoding in close command
What changed, and why it matters
This commit fixes how transaction IDs (txids) are encoded when the 'close' command response is sent over the gRPC interface in Core Lightning. Previously, the txids were likely passed through as plain strings instead of being decoded from hex into raw bytes, which could cause the gRPC layer to produce malformed or unexpected data for clients. The fix also updates the code generator so future txid fields are handled correctly in both directions.
Review other gRPC response fields for similar type mismatches, consider replacing unwrap() with proper error handling for hex decoding, and regenerate cln-grpc conversion code to ensure the generator fix is reflected across all txid usages.
Security signals we found
Data serialization mismatch in gRPC response field
Use of unwrap() on hex decode (panic on malformed input)
Generated code fix in msggen to prevent recurrence for txid fields
Evidence from the diff
The patch changes cln-grpc/src/convert.rs so that CloseResponse.txids are converted via hex::decode(i).unwrap() rather than i.into(), matching the existing handling of txs. It also updates contrib/msggen/msggen/gen/grpc/convert.py and unconvert.py to treat the ‘txid’ type with explicit hex decode/encode rules. This ensures txid fields are transmitted as raw bytes over gRPC instead of remaining as hex strings, correcting a serialization mismatch in the close command response.
Changed components
cln-grpc/src/convert.rscontrib/msggen/msggen/gen/grpc/convert.pycontrib/msggen/msggen/gen/grpc/unconvert.pyCloseResponse gRPC messageInspect captured patch +3 / −1
diff --git a/cln-grpc/src/convert.rs b/cln-grpc/src/convert.rs
index fa3d4fd0..3a357b7d 100644
--- a/cln-grpc/src/convert.rs
+++ b/cln-grpc/src/convert.rs
@@ -454,7 +454,7 @@ impl From<responses::CloseResponse> for pb::CloseResponse {
fn from(c: responses::CloseResponse) -> Self {
Self {
// Field: Close.txids[]
- txids: c.txids.map(|arr| arr.into_iter().map(|i| i.into()).collect()).unwrap_or(vec![]), // Rule #3
+ txids: c.txids.map(|arr| arr.into_iter().map(|i| hex::decode(i).unwrap()).collect()).unwrap_or(vec![]), // Rule #3
// Field: Close.txs[]
txs: c.txs.map(|arr| arr.into_iter().map(|i| hex::decode(i).unwrap()).collect()).unwrap_or(vec![]), // Rule #3
item_type: c.item_type as i32,
diff --git a/contrib/msggen/msggen/gen/grpc/convert.py b/contrib/msggen/msggen/gen/grpc/convert.py
index 3adc7978..a65c447d 100644
--- a/contrib/msggen/msggen/gen/grpc/convert.py
+++ b/contrib/msggen/msggen/gen/grpc/convert.py
@@ -71,6 +71,7 @@ class GrpcConverterGenerator(IGenerator):
"short_channel_id": f"i.to_string()",
"short_channel_id_dir": f"i.to_string()",
"pubkey": f"i.serialize().to_vec()",
+ "txid": f"hex::decode(i).unwrap()",
}.get(typ, f"i.into()")
self.write(f"// Field: {f.path}\n", numindent=3)
diff --git a/contrib/msggen/msggen/gen/grpc/unconvert.py b/contrib/msggen/msggen/gen/grpc/unconvert.py
index 16a7591d..73984f75 100644
--- a/contrib/msggen/msggen/gen/grpc/unconvert.py
+++ b/contrib/msggen/msggen/gen/grpc/unconvert.py
@@ -56,6 +56,7 @@ class GrpcUnconverterGenerator(GrpcConverterGenerator):
"short_channel_id": f"cln_rpc::primitives::ShortChannelId::from_str(&s).unwrap()",
"short_channel_id_dir": f"cln_rpc::primitives::ShortChannelIdDir::from_str(&s).unwrap()",
"pubkey": f"PublicKey::from_slice(&s).unwrap()",
+ "txid": f"hex::encode(s)",
}.get(typ, f"s.into()")
# TODO fix properly
Why this scored 24/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.