Skip to content

Commit eda6fcb

Browse files
angelixcdecker
authored andcommitted
feat(gl-sdk): NodeState snapshot, hex identifiers, list_payments fixes
NodeState - Unified node_state() snapshot aggregating get_info, list_peer_channels, and list_funds into a single record with balances, capacity, utxos, and connected-channel-peer set. - Reserved UTXOs (in-flight PSBTs) excluded from on-chain balance. FundOutput now carries `reserved` from listfunds.outputs[].reserved. - Closing-channel double-count fixed. Onchain-state channels only contribute to pending_onchain_balance_msat when we initiated the close and our payout is still timelocked (DELAYED_OUTPUT_TO_US); otherwise the payout is already a wallet UTXO and would be counted twice. PeerChannel exposes `closer` (ChannelSide) and `status` to support this gate. - ChannelState::from_i32 unknown fallback now maps to a new ChannelState::Unknown variant rather than Onchain (which silently counted unmapped states as closing). Unknown is treated as neither open nor closing by balance math. gl-sdk-cli output formatter handles the new variant too. - Immature outputs (confirmed but timelocked, e.g. coinbase maturation) surface as immature_onchain_balance_msat instead of being silently dropped. OutputStatus match is exhaustive. - The three underlying RPCs run concurrently via tokio::join!. NodeState aggregate fields: - total_onchain_msat (confirmed + unconfirmed + immature) - total_balance_msat (everything the user owns) - spendable_balance_msat (send-screen gate) - max_chan_reserve_msat (protocol reserve locked across channels) - utxos: Vec<FundOutput> for coin-control UIs Other NodeState changes: - channels_balance_msat / max_payable_msat docstrings explicitly name their roles: home-screen display vs send-button gate. - num_active/pending/inactive_channels docstrings clarify which balance fields each count contributes to. - connected_peers renamed to connected_channel_peers (only lists peers we have a channel with); dedup via HashSet. - fees_collected_msat removed from NodeState (still on GetInfoResponse); it's a routing-node concern. Hex identifier surface - Every identifier (pubkey, payment hash, txid, preimage, funding txid, channel id) on public structs is now a lowercase hex String instead of Vec<u8>. Affected: NodeState, PeerChannel, FundOutput, FundChannel, GetInfoResponse, Peer, Invoice, Pay, Payment, SendResponse, OnchainSendResponse (txid only; raw tx stays bytes), InvoicePaidEvent, ParsedInvoice. - Kept as Vec<u8>: Credentials::load/save bytes, raw on-chain tx bytes, Peer.features bitfield, DeveloperCert/Signer constructor arguments. list_payments fixes - Unpaid (open) and expired invoices are dropped from list_payments. Only Paid invoices appear as Payment rows on the received side; list_invoices() still surfaces the full invoice list for callers that want to inspect open invoices directly. - Payment.destination is documented as always-None for PaymentType::Received: Lightning's privacy model does not reveal the sender's pubkey to the recipient, and the only pubkey derivable from a paid invoice is the payee (our own node), which is uninteresting per-row. PaymentStatus::Pending stays on the public type — it remains valid for in-flight sent payments. NAPI mirrors synced (Buffer→String everywhere the underlying field became hex), gl-sdk-cli output formatters simplified, NodeExtensions defaults adjusted, and Python + Kotlin tests updated to match.
1 parent f45a39a commit eda6fcb

10 files changed

Lines changed: 1428 additions & 267 deletions

File tree

libs/gl-sdk-android/lib/src/androidInstrumentedTest/kotlin/com/blockstream/glsdk/NodeOperationsTest.kt

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ package com.blockstream.glsdk
22

33
import android.system.Os
44
import androidx.test.ext.junit.runners.AndroidJUnit4
5+
import org.junit.Assert.assertEquals
6+
import org.junit.Assert.assertTrue
57
import org.junit.Before
68
import org.junit.Test
79
import org.junit.runner.RunWith
@@ -41,4 +43,22 @@ class NodeOperationsTest {
4143
println("Lightning Invoice: ${invoice.toString()}")
4244
}
4345
}
46+
47+
@Test
48+
fun test_node_state_returns_valid_snapshot() {
49+
val config = Config()
50+
val node = registerOrRecover(mnemonic = testMnemonic, inviteCode = null, config = config)
51+
node.use { n ->
52+
val state = n.nodeState()
53+
assertTrue(state.id.isNotEmpty())
54+
assertTrue(state.blockHeight > 0u)
55+
assertEquals("bitcoin", state.network)
56+
assertTrue(state.version.isNotEmpty())
57+
assertEquals(0uL, state.channelsBalanceMsat)
58+
assertEquals(0uL, state.maxPayableMsat)
59+
assertEquals(0uL, state.totalChannelCapacityMsat)
60+
assertEquals(0uL, state.onchainBalanceMsat)
61+
assertEquals(0uL, state.totalInboundLiquidityMsat)
62+
}
63+
}
4464
}

libs/gl-sdk-android/lib/src/commonMain/kotlin/com/blockstream/glsdk/NodeExtensions.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ public fun Node.listPayments(
3030
filters: List<PaymentTypeFilter>? = null,
3131
fromTimestamp: ULong? = null,
3232
toTimestamp: ULong? = null,
33-
includeFailures: Boolean? = null,
33+
includeFailures: Boolean = false,
3434
offset: UInt? = null,
3535
limit: UInt? = null,
3636
): List<Payment> = listPayments(ListPaymentsRequest(filters, fromTimestamp, toTimestamp, includeFailures, offset, limit))

libs/gl-sdk-cli/src/output.rs

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,9 @@ pub struct GetInfoOutput {
2727
impl From<glsdk::GetInfoResponse> for GetInfoOutput {
2828
fn from(r: glsdk::GetInfoResponse) -> Self {
2929
Self {
30-
id: hex::encode(&r.id),
30+
id: r.id,
3131
alias: r.alias,
32-
color: hex::encode(&r.color),
32+
color: r.color,
3333
num_peers: r.num_peers,
3434
num_pending_channels: r.num_pending_channels,
3535
num_active_channels: r.num_active_channels,
@@ -73,7 +73,7 @@ impl From<glsdk::ListPeersResponse> for ListPeersOutput {
7373
impl From<glsdk::Peer> for PeerOutput {
7474
fn from(p: glsdk::Peer) -> Self {
7575
Self {
76-
id: hex::encode(&p.id),
76+
id: p.id,
7777
connected: p.connected,
7878
num_channels: p.num_channels,
7979
netaddr: p.netaddr,
@@ -130,18 +130,19 @@ fn channel_state_str(s: &glsdk::ChannelState) -> &'static str {
130130
glsdk::ChannelState::DualopendAwaitingLockin => "DUALOPEND_AWAITING_LOCKIN",
131131
glsdk::ChannelState::DualopendOpenCommitted => "DUALOPEND_OPEN_COMMITTED",
132132
glsdk::ChannelState::DualopendOpenCommitReady => "DUALOPEND_OPEN_COMMIT_READY",
133+
glsdk::ChannelState::Unknown => "UNKNOWN",
133134
}
134135
}
135136

136137
impl From<glsdk::PeerChannel> for PeerChannelOutput {
137138
fn from(c: glsdk::PeerChannel) -> Self {
138139
Self {
139-
peer_id: hex::encode(&c.peer_id),
140+
peer_id: c.peer_id,
140141
peer_connected: c.peer_connected,
141142
state: channel_state_str(&c.state).to_string(),
142143
short_channel_id: c.short_channel_id,
143-
channel_id: c.channel_id.map(|v| hex::encode(&v)),
144-
funding_txid: c.funding_txid.map(|v| hex::encode(&v)),
144+
channel_id: c.channel_id,
145+
funding_txid: c.funding_txid,
145146
funding_outnum: c.funding_outnum,
146147
to_us_msat: c.to_us_msat,
147148
total_msat: c.total_msat,
@@ -205,7 +206,7 @@ impl From<glsdk::ListFundsResponse> for ListFundsOutput {
205206
impl From<glsdk::FundOutput> for FundOutputOutput {
206207
fn from(o: glsdk::FundOutput) -> Self {
207208
Self {
208-
txid: hex::encode(&o.txid),
209+
txid: o.txid,
209210
output: o.output,
210211
amount_msat: o.amount_msat,
211212
status: output_status_str(&o.status).to_string(),
@@ -218,15 +219,15 @@ impl From<glsdk::FundOutput> for FundOutputOutput {
218219
impl From<glsdk::FundChannel> for FundChannelOutput {
219220
fn from(c: glsdk::FundChannel) -> Self {
220221
Self {
221-
peer_id: hex::encode(&c.peer_id),
222+
peer_id: c.peer_id,
222223
our_amount_msat: c.our_amount_msat,
223224
amount_msat: c.amount_msat,
224-
funding_txid: hex::encode(&c.funding_txid),
225+
funding_txid: c.funding_txid,
225226
funding_output: c.funding_output,
226227
connected: c.connected,
227228
state: channel_state_str(&c.state).to_string(),
228229
short_channel_id: c.short_channel_id,
229-
channel_id: c.channel_id.map(|v| hex::encode(&v)),
230+
channel_id: c.channel_id,
230231
}
231232
}
232233
}
@@ -267,7 +268,7 @@ impl From<glsdk::SendResponse> for SendOutput {
267268
fn from(r: glsdk::SendResponse) -> Self {
268269
Self {
269270
status: pay_status_str(&r.status).to_string(),
270-
preimage: hex::encode(&r.preimage),
271+
preimage: r.preimage,
271272
amount_msat: r.amount_msat,
272273
amount_sent_msat: r.amount_sent_msat,
273274
parts: r.parts,
@@ -301,7 +302,7 @@ impl From<glsdk::OnchainSendResponse> for OnchainSendOutput {
301302
fn from(r: glsdk::OnchainSendResponse) -> Self {
302303
Self {
303304
tx: hex::encode(&r.tx),
304-
txid: hex::encode(&r.txid),
305+
txid: r.txid,
305306
psbt: r.psbt,
306307
}
307308
}
@@ -330,9 +331,9 @@ impl From<glsdk::NodeEvent> for NodeEventOutput {
330331
fn from(e: glsdk::NodeEvent) -> Self {
331332
match e {
332333
glsdk::NodeEvent::InvoicePaid { details } => NodeEventOutput::InvoicePaid {
333-
payment_hash: hex::encode(&details.payment_hash),
334+
payment_hash: details.payment_hash,
334335
bolt11: details.bolt11,
335-
preimage: hex::encode(&details.preimage),
336+
preimage: details.preimage,
336337
label: details.label,
337338
amount_msat: details.amount_msat,
338339
},

libs/gl-sdk-napi/package-lock.json

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

0 commit comments

Comments
 (0)