Skip to content

Commit 7f8c28c

Browse files
angelixcdecker
authored andcommitted
feat(gl-sdk): add generate_diagnostic_data for support dumps
Returns a pretty-printed JSON envelope { timestamp, node, sdk } where the node section serializes getinfo/listpeerchannels/listfunds and the sdk section carries version + node_state. Failed sub-calls are embedded as { "error": "..." } instead of failing the dump. Adds serde derive on the response types so each section is real nested JSON, queryable with jq. Payment and invoice history are intentionally excluded to avoid leaking preimages, payment hashes, bolt11 strings, and labels into support dumps.
1 parent eda6fcb commit 7f8c28c

5 files changed

Lines changed: 209 additions & 18 deletions

File tree

Cargo.lock

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

libs/gl-sdk/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ gl-client = { version = "0.4.0", path = "../gl-client" }
1717
hex = "0.4"
1818
lightning-invoice = "0.33"
1919
once_cell = "1.21.3"
20+
serde = { version = "1", features = ["derive"] }
21+
serde_json = "1"
2022
thiserror = "2.0.17"
2123
tokio = { version = "1", features = ["sync"] }
2224
tonic.workspace = true

libs/gl-sdk/glsdk/glsdk.py

Lines changed: 77 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -474,6 +474,8 @@ def _uniffi_check_api_checksums(lib):
474474
raise InternalError("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
475475
if lib.uniffi_glsdk_checksum_method_config_with_network() != 35643:
476476
raise InternalError("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
477+
if lib.uniffi_glsdk_checksum_method_credentials_node_id() != 16312:
478+
raise InternalError("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
477479
if lib.uniffi_glsdk_checksum_method_credentials_save() != 26677:
478480
raise InternalError("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
479481
if lib.uniffi_glsdk_checksum_method_handle_stop() != 36432:
@@ -482,6 +484,8 @@ def _uniffi_check_api_checksums(lib):
482484
raise InternalError("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
483485
if lib.uniffi_glsdk_checksum_method_node_disconnect() != 43626:
484486
raise InternalError("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
487+
if lib.uniffi_glsdk_checksum_method_node_generate_diagnostic_data() != 41140:
488+
raise InternalError("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
485489
if lib.uniffi_glsdk_checksum_method_node_get_info() != 39460:
486490
raise InternalError("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
487491
if lib.uniffi_glsdk_checksum_method_node_list_funds() != 21692:
@@ -685,6 +689,11 @@ class _UniffiForeignFutureStructVoid(ctypes.Structure):
685689
ctypes.POINTER(_UniffiRustCallStatus),
686690
)
687691
_UniffiLib.uniffi_glsdk_fn_constructor_credentials_load.restype = ctypes.c_void_p
692+
_UniffiLib.uniffi_glsdk_fn_method_credentials_node_id.argtypes = (
693+
ctypes.c_void_p,
694+
ctypes.POINTER(_UniffiRustCallStatus),
695+
)
696+
_UniffiLib.uniffi_glsdk_fn_method_credentials_node_id.restype = _UniffiRustBuffer
688697
_UniffiLib.uniffi_glsdk_fn_method_credentials_save.argtypes = (
689698
ctypes.c_void_p,
690699
ctypes.POINTER(_UniffiRustCallStatus),
@@ -746,6 +755,11 @@ class _UniffiForeignFutureStructVoid(ctypes.Structure):
746755
ctypes.POINTER(_UniffiRustCallStatus),
747756
)
748757
_UniffiLib.uniffi_glsdk_fn_method_node_disconnect.restype = None
758+
_UniffiLib.uniffi_glsdk_fn_method_node_generate_diagnostic_data.argtypes = (
759+
ctypes.c_void_p,
760+
ctypes.POINTER(_UniffiRustCallStatus),
761+
)
762+
_UniffiLib.uniffi_glsdk_fn_method_node_generate_diagnostic_data.restype = _UniffiRustBuffer
749763
_UniffiLib.uniffi_glsdk_fn_method_node_get_info.argtypes = (
750764
ctypes.c_void_p,
751765
ctypes.POINTER(_UniffiRustCallStatus),
@@ -1243,6 +1257,9 @@ class _UniffiForeignFutureStructVoid(ctypes.Structure):
12431257
_UniffiLib.uniffi_glsdk_checksum_method_config_with_network.argtypes = (
12441258
)
12451259
_UniffiLib.uniffi_glsdk_checksum_method_config_with_network.restype = ctypes.c_uint16
1260+
_UniffiLib.uniffi_glsdk_checksum_method_credentials_node_id.argtypes = (
1261+
)
1262+
_UniffiLib.uniffi_glsdk_checksum_method_credentials_node_id.restype = ctypes.c_uint16
12461263
_UniffiLib.uniffi_glsdk_checksum_method_credentials_save.argtypes = (
12471264
)
12481265
_UniffiLib.uniffi_glsdk_checksum_method_credentials_save.restype = ctypes.c_uint16
@@ -1255,6 +1272,9 @@ class _UniffiForeignFutureStructVoid(ctypes.Structure):
12551272
_UniffiLib.uniffi_glsdk_checksum_method_node_disconnect.argtypes = (
12561273
)
12571274
_UniffiLib.uniffi_glsdk_checksum_method_node_disconnect.restype = ctypes.c_uint16
1275+
_UniffiLib.uniffi_glsdk_checksum_method_node_generate_diagnostic_data.argtypes = (
1276+
)
1277+
_UniffiLib.uniffi_glsdk_checksum_method_node_generate_diagnostic_data.restype = ctypes.c_uint16
12581278
_UniffiLib.uniffi_glsdk_checksum_method_node_get_info.argtypes = (
12591279
)
12601280
_UniffiLib.uniffi_glsdk_checksum_method_node_get_info.restype = ctypes.c_uint16
@@ -2884,12 +2904,23 @@ class Payment:
28842904
bolt11: "typing.Optional[str]"
28852905
preimage: "typing.Optional[str]"
28862906
"""
2887-
Payment preimage as lowercase hex (64 chars), when the payment is known.
2907+
Payment preimage as lowercase hex (64 chars), when known.
28882908
"""
28892909

28902910
destination: "typing.Optional[str]"
28912911
"""
2892-
Counterparty node pubkey as lowercase hex (66 chars), when known.
2912+
Pubkey of the counterparty in the payment, as lowercase hex
2913+
(66 chars).
2914+
2915+
For `PaymentType::Sent`: the recipient node we paid (when CLN
2916+
reports it).
2917+
2918+
For `PaymentType::Received`: always `None`. Lightning's privacy
2919+
model does not reveal the sender's pubkey to the recipient — the
2920+
HTLC arrives via one of our channel peers, but that peer is
2921+
usually just a router, not the original payer. The only pubkey
2922+
derivable from a paid invoice is the *payee* (i.e. our own
2923+
node), which is uninteresting to display per-row.
28932924
"""
28942925

28952926
def __init__(self, *, id: "str", payment_type: "PaymentType", payment_time: "int", amount_msat: "int", fee_msat: "int", status: "PaymentStatus", description: "typing.Optional[str]", bolt11: "typing.Optional[str]", preimage: "typing.Optional[str]", destination: "typing.Optional[str]"):
@@ -4844,6 +4875,8 @@ class CredentialsProtocol(typing.Protocol):
48444875
background.
48454876
"""
48464877

4878+
def node_id(self, ):
4879+
raise NotImplementedError
48474880
def save(self, ):
48484881
raise NotImplementedError
48494882
# Credentials is a Rust-only trait - it's a wrapper around a Rust implementation.
@@ -4889,6 +4922,15 @@ def load(cls, raw: "bytes"):
48894922

48904923

48914924

4925+
def node_id(self, ) -> "bytes":
4926+
return _UniffiConverterBytes.lift(
4927+
_uniffi_rust_call_with_error(_UniffiConverterTypeError,_UniffiLib.uniffi_glsdk_fn_method_credentials_node_id,self._uniffi_clone_pointer(),)
4928+
)
4929+
4930+
4931+
4932+
4933+
48924934
def save(self, ) -> "bytes":
48934935
return _UniffiConverterBytes.lift(
48944936
_uniffi_rust_call_with_error(_UniffiConverterTypeError,_UniffiLib.uniffi_glsdk_fn_method_credentials_save,self._uniffi_clone_pointer(),)
@@ -5111,6 +5153,19 @@ def disconnect(self, ):
51115153
Safe to call multiple times.
51125154
"""
51135155

5156+
raise NotImplementedError
5157+
def generate_diagnostic_data(self, ):
5158+
"""
5159+
Collect a diagnostic snapshot of the node and SDK state.
5160+
5161+
Returns a pretty-printed JSON string with shape:
5162+
`{ "timestamp": <unix-secs>, "node": { ... }, "sdk": { "version": ..., "node_state": ... } }`.
5163+
The `node` object contains one entry per CLN RPC (`getinfo`,
5164+
`listpeerchannels`, `listfunds`, `listpays`, `listinvoices`); each
5165+
value is the serialized response, or `{ "error": "..." }` if that
5166+
RPC failed. Intended for support tickets.
5167+
"""
5168+
51145169
raise NotImplementedError
51155170
def get_info(self, ):
51165171
"""
@@ -5306,6 +5361,26 @@ def disconnect(self, ) -> None:
53065361

53075362

53085363

5364+
def generate_diagnostic_data(self, ) -> "str":
5365+
"""
5366+
Collect a diagnostic snapshot of the node and SDK state.
5367+
5368+
Returns a pretty-printed JSON string with shape:
5369+
`{ "timestamp": <unix-secs>, "node": { ... }, "sdk": { "version": ..., "node_state": ... } }`.
5370+
The `node` object contains one entry per CLN RPC (`getinfo`,
5371+
`listpeerchannels`, `listfunds`, `listpays`, `listinvoices`); each
5372+
value is the serialized response, or `{ "error": "..." }` if that
5373+
RPC failed. Intended for support tickets.
5374+
"""
5375+
5376+
return _UniffiConverterString.lift(
5377+
_uniffi_rust_call_with_error(_UniffiConverterTypeError,_UniffiLib.uniffi_glsdk_fn_method_node_generate_diagnostic_data,self._uniffi_clone_pointer(),)
5378+
)
5379+
5380+
5381+
5382+
5383+
53095384
def get_info(self, ) -> "GetInfoResponse":
53105385
"""
53115386
Get information about the node.

libs/gl-sdk/src/node.rs

Lines changed: 81 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -584,6 +584,70 @@ impl Node {
584584
inner: Mutex::new(stream),
585585
}))
586586
}
587+
588+
/// Collect a diagnostic snapshot of the node and SDK state.
589+
///
590+
/// Returns a pretty-printed JSON string with shape:
591+
/// `{ "timestamp": <unix-secs>, "node": { ... }, "sdk": { "version": ..., "node_state": ... } }`.
592+
/// The `node` object contains one entry per CLN RPC (`getinfo`,
593+
/// `listpeerchannels`, `listfunds`); each value is the serialized
594+
/// response, or `{ "error": "..." }` if that RPC failed. Payment and
595+
/// invoice history are deliberately excluded to avoid leaking
596+
/// preimages, payment hashes, bolt11 strings, and labels into support
597+
/// dumps. Intended for support tickets.
598+
pub fn generate_diagnostic_data(&self) -> Result<String, Error> {
599+
self.check_connected()?;
600+
601+
let timestamp = std::time::SystemTime::now()
602+
.duration_since(std::time::UNIX_EPOCH)
603+
.map(|d| d.as_secs())
604+
.unwrap_or(0);
605+
606+
let getinfo = render_section(self.get_info());
607+
let listpeerchannels = render_section(self.list_peer_channels());
608+
let listfunds = render_section(self.list_funds());
609+
let node_state = render_section(self.node_state());
610+
611+
build_diagnostic_json(
612+
timestamp,
613+
env!("CARGO_PKG_VERSION"),
614+
getinfo,
615+
listpeerchannels,
616+
listfunds,
617+
node_state,
618+
)
619+
}
620+
}
621+
622+
fn render_section<T: serde::Serialize>(result: Result<T, Error>) -> serde_json::Value {
623+
match result {
624+
Ok(v) => serde_json::to_value(&v)
625+
.unwrap_or_else(|e| serde_json::json!({ "error": e.to_string() })),
626+
Err(e) => serde_json::json!({ "error": e.to_string() }),
627+
}
628+
}
629+
630+
fn build_diagnostic_json(
631+
timestamp: u64,
632+
sdk_version: &str,
633+
getinfo: serde_json::Value,
634+
listpeerchannels: serde_json::Value,
635+
listfunds: serde_json::Value,
636+
node_state: serde_json::Value,
637+
) -> Result<String, Error> {
638+
let envelope = serde_json::json!({
639+
"timestamp": timestamp,
640+
"node": {
641+
"getinfo": getinfo,
642+
"listpeerchannels": listpeerchannels,
643+
"listfunds": listfunds,
644+
},
645+
"sdk": {
646+
"version": sdk_version,
647+
"node_state": node_state,
648+
}
649+
});
650+
serde_json::to_string_pretty(&envelope).map_err(|e| Error::Other(e.to_string()))
587651
}
588652

589653
// Not exported through uniffi
@@ -713,7 +777,7 @@ pub struct ReceiveResponse {
713777
pub opening_fee_msat: u64,
714778
}
715779

716-
#[derive(uniffi::Enum, Clone)]
780+
#[derive(uniffi::Enum, Clone, serde::Serialize)]
717781
pub enum PayStatus {
718782
COMPLETE = 0,
719783
PENDING = 1,
@@ -746,7 +810,7 @@ impl From<i32> for PayStatus {
746810
// ============================================================
747811

748812
#[allow(unused)]
749-
#[derive(Clone, uniffi::Record)]
813+
#[derive(Clone, serde::Serialize, uniffi::Record)]
750814
pub struct GetInfoResponse {
751815
/// Node public key as lowercase hex (66 chars).
752816
pub id: String,
@@ -831,13 +895,13 @@ impl From<clnpb::ListpeersPeers> for Peer {
831895
// ============================================================
832896

833897
#[allow(unused)]
834-
#[derive(Clone, uniffi::Record)]
898+
#[derive(Clone, serde::Serialize, uniffi::Record)]
835899
pub struct ListPeerChannelsResponse {
836900
pub channels: Vec<PeerChannel>,
837901
}
838902

839903
#[allow(unused)]
840-
#[derive(Clone, uniffi::Record)]
904+
#[derive(Clone, serde::Serialize, uniffi::Record)]
841905
pub struct PeerChannel {
842906
/// Peer node public key as lowercase hex (66 chars).
843907
pub peer_id: String,
@@ -863,7 +927,7 @@ pub struct PeerChannel {
863927
}
864928

865929
/// Which side of a channel performed a given action (e.g. initiated close).
866-
#[derive(Clone, uniffi::Enum)]
930+
#[derive(Clone, serde::Serialize, uniffi::Enum)]
867931
pub enum ChannelSide {
868932
Local,
869933
Remote,
@@ -879,7 +943,7 @@ impl ChannelSide {
879943
}
880944
}
881945

882-
#[derive(Clone, uniffi::Enum)]
946+
#[derive(Clone, serde::Serialize, uniffi::Enum)]
883947
pub enum ChannelState {
884948
Openingd,
885949
ChanneldAwaitingLockin,
@@ -992,14 +1056,14 @@ impl From<clnpb::ListpeerchannelsChannels> for PeerChannel {
9921056
// ============================================================
9931057

9941058
#[allow(unused)]
995-
#[derive(Clone, uniffi::Record)]
1059+
#[derive(Clone, serde::Serialize, uniffi::Record)]
9961060
pub struct ListFundsResponse {
9971061
pub outputs: Vec<FundOutput>,
9981062
pub channels: Vec<FundChannel>,
9991063
}
10001064

10011065
#[allow(unused)]
1002-
#[derive(Clone, uniffi::Record)]
1066+
#[derive(Clone, serde::Serialize, uniffi::Record)]
10031067
pub struct FundOutput {
10041068
/// Transaction id as lowercase hex (64 chars).
10051069
pub txid: String,
@@ -1015,7 +1079,7 @@ pub struct FundOutput {
10151079
pub reserved: bool,
10161080
}
10171081

1018-
#[derive(Clone, uniffi::Enum)]
1082+
#[derive(Clone, serde::Serialize, uniffi::Enum)]
10191083
pub enum OutputStatus {
10201084
Unconfirmed,
10211085
Confirmed,
@@ -1036,7 +1100,7 @@ impl OutputStatus {
10361100
}
10371101

10381102
#[allow(unused)]
1039-
#[derive(Clone, uniffi::Record)]
1103+
#[derive(Clone, serde::Serialize, uniffi::Record)]
10401104
pub struct FundChannel {
10411105
/// Peer node public key as lowercase hex (66 chars).
10421106
pub peer_id: String,
@@ -1117,7 +1181,7 @@ impl ListIndex {
11171181
// ListInvoices response types
11181182
// ============================================================
11191183

1120-
#[derive(Clone, uniffi::Enum)]
1184+
#[derive(Clone, serde::Serialize, uniffi::Enum)]
11211185
pub enum InvoiceStatus {
11221186
UNPAID,
11231187
PAID,
@@ -1135,7 +1199,7 @@ impl From<i32> for InvoiceStatus {
11351199
}
11361200
}
11371201

1138-
#[derive(Clone, uniffi::Record)]
1202+
#[derive(Clone, serde::Serialize, uniffi::Record)]
11391203
pub struct Invoice {
11401204
pub label: String,
11411205
pub description: String,
@@ -1180,7 +1244,7 @@ impl From<clnpb::ListinvoicesInvoices> for Invoice {
11801244
}
11811245
}
11821246

1183-
#[derive(Clone, uniffi::Record)]
1247+
#[derive(Clone, serde::Serialize, uniffi::Record)]
11841248
pub struct ListInvoicesResponse {
11851249
pub invoices: Vec<Invoice>,
11861250
}
@@ -1197,7 +1261,7 @@ impl From<clnpb::ListinvoicesResponse> for ListInvoicesResponse {
11971261
// ListPays response types
11981262
// ============================================================
11991263

1200-
#[derive(Clone, uniffi::Record)]
1264+
#[derive(Clone, serde::Serialize, uniffi::Record)]
12011265
pub struct Pay {
12021266
/// Payment hash as lowercase hex (64 chars).
12031267
pub payment_hash: String,
@@ -1243,7 +1307,7 @@ impl From<clnpb::ListpaysPays> for Pay {
12431307
}
12441308
}
12451309

1246-
#[derive(Clone, uniffi::Record)]
1310+
#[derive(Clone, serde::Serialize, uniffi::Record)]
12471311
pub struct ListPaysResponse {
12481312
pub pays: Vec<Pay>,
12491313
}
@@ -1394,7 +1458,7 @@ impl From<clnpb::ListpaysPays> for Payment {
13941458
/// connectivity. Returned by `node_state()`.
13951459
///
13961460
/// All amounts are in millisatoshis (1 sat = 1000 msat).
1397-
#[derive(Clone, uniffi::Record)]
1461+
#[derive(Clone, serde::Serialize, uniffi::Record)]
13981462
pub struct NodeState {
13991463
/// The node's public key as a lowercase hex string (66 chars).
14001464
pub id: String,
@@ -1574,3 +1638,4 @@ impl From<glpb::NodeEvent> for NodeEvent {
15741638
}
15751639
}
15761640
}
1641+

0 commit comments

Comments
 (0)