Skip to content
This repository was archived by the owner on Feb 9, 2026. It is now read-only.

Commit 4d7e15c

Browse files
authored
Merge pull request #94 from synonymdev/graph-debugging
Additional logging and graph debugging
2 parents 686f70f + 627e4e5 commit 4d7e15c

12 files changed

Lines changed: 362 additions & 111 deletions

File tree

example/App.tsx

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -424,16 +424,14 @@ const App = (): ReactElement => {
424424
return setMessage(decode.error.message);
425425
}
426426

427-
const { recover_payee_pub_key, amount_satoshis } = decode.value;
427+
const { amount_satoshis, description } = decode.value;
428428

429429
const ownAmountSats = 1000;
430430
Alert.alert(
431431
amount_satoshis
432432
? `Pay ${amount_satoshis ?? 0}`
433433
: 'Zero sat invoice found',
434-
amount_satoshis
435-
? `To pubkey: ${recover_payee_pub_key}`
436-
: `Send ${ownAmountSats} sats (Our chosen amount) to send over?`,
434+
description,
437435
[
438436
{
439437
text: 'Cancel',
@@ -461,26 +459,33 @@ const App = (): ReactElement => {
461459
/>
462460

463461
<Button
464-
title={'Get network graph'}
462+
title={'Get network graph nodes'}
465463
onPress={async (): Promise<void> => {
466-
const nodesRes = await ldk.completeGraphNodes();
464+
const nodesRes = await ldk.networkGraphListNodeIds();
467465
if (nodesRes.isErr()) {
468466
return setMessage(nodesRes.error.message);
469467
}
470468

471-
const channelRes = await ldk.completeGraphChannels();
472-
if (channelRes.isErr()) {
473-
return setMessage(channelRes.error.message);
469+
let msg = `Nodes (${nodesRes.value.length}): \n`;
470+
471+
const nodes = await ldk.networkGraphNodes(nodesRes.value);
472+
if (nodes.isOk()) {
473+
nodes.value.forEach((node) => {
474+
const {
475+
id,
476+
shortChannelIds,
477+
lowest_inbound_channel_fees_base_sat,
478+
announcement_info_last_update,
479+
} = node;
480+
const time = new Date(announcement_info_last_update);
481+
482+
msg += `\n\n${id}\nChannels: ${shortChannelIds.length}\n`;
483+
msg += `Lowest inbound fee: ${lowest_inbound_channel_fees_base_sat} sat\n`;
484+
msg += `Last announcement: ${time}`;
485+
});
474486
}
475487

476-
const nodes = `Nodes:\n\n${nodesRes.value.map(
477-
(node) => `\n${JSON.stringify(node)}`,
478-
)}`;
479-
const channels = `Channels:\n\n${channelRes.value.map(
480-
(channel) => `\n${JSON.stringify(channel)}`,
481-
)}`;
482-
483-
setMessage(`${nodes}\n${channels}`);
488+
setMessage(`${msg}`);
484489
}}
485490
/>
486491

lib/android/src/main/java/com/reactnativeldk/Helpers.kt

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ import java.net.URL
1010
import java.nio.channels.Channels
1111

1212
fun handleResolve(promise: Promise, res: LdkCallbackResponses) {
13-
LdkEventEmitter.send(EventTypes.native_log, "Success: ${res}")
13+
if (res != LdkCallbackResponses.log_write_success) {
14+
LdkEventEmitter.send(EventTypes.native_log, "Success: ${res}")
15+
}
1416
promise.resolve(res.toString());
1517
}
1618

@@ -144,8 +146,10 @@ val NodeInfo.asJson: WritableMap
144146

145147
val shortChannelIds = Arguments.createArray()
146148
_channels.iterator().forEach { shortChannelIds.pushString(it.toString()) }
147-
result.putArray("channels", shortChannelIds)
148-
149+
result.putArray("shortChannelIds", shortChannelIds)
150+
result.putInt("lowest_inbound_channel_fees_base_sat", (_lowest_inbound_channel_fees?._base_msat ?: 0) / 1000)
151+
result.putInt("lowest_inbound_channel_fees_proportional_millionths", _lowest_inbound_channel_fees?._proportional_millionths ?: 0)
152+
result.putDouble("announcement_info_last_update", (_announcement_info?._last_update ?: 0).toDouble() * 1000)
149153
return result
150154
}
151155

lib/android/src/main/java/com/reactnativeldk/LdkModule.kt

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ enum class LdkCallbackResponses {
9292
fees_updated,
9393
log_level_updated,
9494
log_path_updated,
95+
log_write_success,
9596
chain_monitor_init_success,
9697
keys_manager_init_success,
9798
channel_manager_init_success,
@@ -200,6 +201,13 @@ class LdkModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMod
200201
handleResolve(promise, LdkCallbackResponses.log_path_updated)
201202
}
202203

204+
@ReactMethod
205+
fun writeToLogFile(line: String, promise: Promise) {
206+
LogFile.write(line)
207+
208+
handleResolve(promise, LdkCallbackResponses.log_write_success)
209+
}
210+
203211
@ReactMethod
204212
fun initChainMonitor(promise: Promise) {
205213
if (chainMonitor !== null) {
@@ -794,7 +802,7 @@ class LdkModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMod
794802
}
795803

796804
@ReactMethod
797-
fun networkGraphListNodes(promise: Promise) {
805+
fun networkGraphListNodeIds(promise: Promise) {
798806
val graph = networkGraph?.read_only() ?: return handleReject(promise, LdkErrors.init_network_graph)
799807

800808
val total = graph.list_nodes().count()
@@ -809,11 +817,24 @@ class LdkModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMod
809817
}
810818

811819
@ReactMethod
812-
fun networkGraphNode(nodeId: String, promise: Promise) {
820+
fun networkGraphNodes(nodeIds: ReadableArray, promise: Promise) {
813821
val graph = networkGraph?.read_only() ?: return handleReject(promise, LdkErrors.init_network_graph)
814822

815-
val id = NodeId.from_pubkey(nodeId.hexa())
816-
promise.resolve(graph.node(id)?.asJson)
823+
val graphNodes = graph.list_nodes().map { it.as_slice().hexEncodedString() }
824+
825+
//Filter out nodes we don't know about as querying unknown nodes will cause a crash
826+
val includedList: List<String> = nodeIds.toArrayList().map { it as String }.filter { graphNodes.contains(it) }
827+
828+
val list = Arguments.createArray()
829+
includedList.forEach {
830+
val node = graph.node(NodeId.from_pubkey(it.hexa()))?.asJson
831+
if (node != null) {
832+
node.putString("id", it)
833+
list.pushMap(node)
834+
}
835+
}
836+
837+
promise.resolve(list)
817838
}
818839

819840
@ReactMethod

lib/android/src/main/java/com/reactnativeldk/classes/LdkLogger.kt

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,23 @@ import java.io.File
99
class LdkLogger {
1010
var activeLevels = HashMap<Int, Boolean>()
1111
var logger = Logger.new_impl{ record: Record ->
12-
if (activeLevels[record._level.ordinal] == true) {
12+
val level = record._level.ordinal
13+
if (activeLevels[level] == true) {
1314
LdkEventEmitter.send(EventTypes.ldk_log, record._args)
14-
LogFile.write(record._args)
15+
16+
var levelStr = "LEVEL $level"
17+
when (level) {
18+
0 -> levelStr = "GOSSIP"
19+
1 -> levelStr = "TRACE"
20+
2 -> levelStr = "DEBUG"
21+
3 -> levelStr = "INFO"
22+
4 -> levelStr = "WARN"
23+
5 -> levelStr = "ERROR"
24+
}
25+
26+
LogFile.write( "$levelStr (LDK): ${record._args}")
27+
} else {
28+
println("Skipping log level $level")
1529
}
1630
}
1731

@@ -25,15 +39,18 @@ object LogFile {
2539
private var logFile: File? = null
2640

2741
fun setFilePath(logFile: File) {
28-
if (!logFile!!.isFile) {
29-
logFile!!.createNewFile()
42+
if (!logFile.isFile) {
43+
logFile.createNewFile()
3044
}
45+
46+
this.logFile = logFile
3147
}
3248

3349
fun write(str: String) {
3450
if (logFile == null) {
3551
return
3652
}
53+
3754
logFile!!.appendText("${str}\n")
3855
}
3956
}

lib/ios/Classes/LdkLogger.swift

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,32 @@ class LdkLogger: Logger {
2121
//Only when the JS code has set the log level to active
2222
if activeLevels[level] == true {
2323
LdkEventEmitter.shared.send(withEvent: .ldk_log, body: record.get_args())
24-
Logfile.log.write(record.get_args())
24+
25+
var levelStr = ""
26+
switch level {
27+
case 0:
28+
levelStr = "GOSSIP"
29+
break
30+
case 1:
31+
levelStr = "TRACE"
32+
break
33+
case 2:
34+
levelStr = "DEBUG"
35+
break
36+
case 3:
37+
levelStr = "INFO"
38+
break
39+
case 4:
40+
levelStr = "WARN"
41+
break
42+
case 5:
43+
levelStr = "ERROR"
44+
break
45+
default:
46+
levelStr = "LEVEL \(level)"
47+
}
48+
49+
Logfile.log.write("\(levelStr) (LDK): \(record.get_args())")
2550
}
2651
}
2752

lib/ios/Helpers.swift

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ import Foundation
99
import LightningDevKit
1010

1111
func handleResolve(_ resolve: RCTPromiseResolveBlock, _ res: LdkCallbackResponses) {
12-
LdkEventEmitter.shared.send(withEvent: .native_log, body: "Success: \(res.rawValue)")
12+
if res != .log_write_success {
13+
LdkEventEmitter.shared.send(withEvent: .native_log, body: "Success: \(res.rawValue)")
14+
}
1315
resolve(res.rawValue)
1416
}
1517

@@ -116,10 +118,12 @@ extension ChannelInfo {
116118

117119
//Nodes in our network graph
118120
extension NodeInfo {
119-
var asJson: Any {
121+
var asJson: [String: Any] {
120122
return [
121123
"shortChannelIds": get_channels().map({ String($0) }),
122-
//TODO gathering other details results in EXC_BAD_ACCESS. Test with next version of LDK.
124+
"lowest_inbound_channel_fees_base_sat": get_lowest_inbound_channel_fees().get_base_msat() / 1000,
125+
"lowest_inbound_channel_fees_proportional_millionths": get_lowest_inbound_channel_fees().get_proportional_millionths(),
126+
"announcement_info_last_update": Int(get_announcement_info().get_last_update()) * 1000
123127
]
124128
}
125129
}

lib/ios/Ldk.m

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ @interface RCT_EXTERN_MODULE(Ldk, NSObject)
1010
RCT_EXTERN_METHOD(setLogFilePath:(NSString *)path
1111
resolve:(RCTPromiseResolveBlock)resolve
1212
reject:(RCTPromiseRejectBlock)reject)
13+
RCT_EXTERN_METHOD(writeToLogFile:(NSString *)line
14+
resolve:(RCTPromiseResolveBlock)resolve
15+
reject:(RCTPromiseRejectBlock)reject)
1316
RCT_EXTERN_METHOD(initChainMonitor:(RCTPromiseResolveBlock)resolve
1417
reject:(RCTPromiseRejectBlock)reject)
1518
RCT_EXTERN_METHOD(initKeysManager:(NSString *)seed
@@ -86,14 +89,14 @@ @interface RCT_EXTERN_MODULE(Ldk, NSObject)
8689
reject:(RCTPromiseRejectBlock)reject)
8790
RCT_EXTERN_METHOD(listChannelFiles:(RCTPromiseResolveBlock)resolve
8891
reject:(RCTPromiseRejectBlock)reject)
89-
RCT_EXTERN_METHOD(networkGraphListNodes:(RCTPromiseResolveBlock)resolve
92+
RCT_EXTERN_METHOD(networkGraphListNodeIds:(RCTPromiseResolveBlock)resolve
9093
reject:(RCTPromiseRejectBlock)reject)
9194
RCT_EXTERN_METHOD(networkGraphListChannels:(RCTPromiseResolveBlock)resolve
9295
reject:(RCTPromiseRejectBlock)reject)
9396
RCT_EXTERN_METHOD(networkGraphChannel:(NSString *)shortChannelId
9497
resolve:(RCTPromiseResolveBlock)resolve
9598
reject:(RCTPromiseRejectBlock)reject)
96-
RCT_EXTERN_METHOD(networkGraphNode:(NSString *)nodeId
99+
RCT_EXTERN_METHOD(networkGraphNodes:(NSArray *)nodeIds
97100
resolve:(RCTPromiseResolveBlock)resolve
98101
reject:(RCTPromiseRejectBlock)reject)
99102
RCT_EXTERN_METHOD(claimableBalances:(BOOL *)ignoreOpenChannels

lib/ios/Ldk.swift

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ enum LdkCallbackResponses: String {
7171
case fees_updated = "fees_updated"
7272
case log_level_updated = "log_level_updated"
7373
case log_path_updated = "log_path_updated"
74+
case log_write_success = "log_write_success"
7475
case chain_monitor_init_success = "chain_monitor_init_success"
7576
case keys_manager_init_success = "keys_manager_init_success"
7677
case channel_manager_init_success = "channel_manager_init_success"
@@ -174,6 +175,12 @@ class Ldk: NSObject {
174175
return handleResolve(resolve, .log_path_updated)
175176
}
176177

178+
@objc
179+
func writeToLogFile(_ line: NSString, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
180+
Logfile.log.write(String(line))
181+
return handleResolve(resolve, .log_write_success)
182+
}
183+
177184
@objc
178185
func initChainMonitor(_ resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
179186
guard chainMonitor == nil else {
@@ -250,13 +257,14 @@ class Ldk: NSObject {
250257
if read.isOk() {
251258
networkGraph = read.getValue()
252259
LdkEventEmitter.shared.send(withEvent: .native_log, body: "Loaded network graph from file")
253-
254260
}
255261
} catch {
256262
networkGraph = NetworkGraph(genesis_hash: String(genesisHash).hexaBytes, logger: logger)
257263
LdkEventEmitter.shared.send(withEvent: .native_log, body: "Failed to load cached network graph from disk. Will sync from scratch. \(error.localizedDescription)")
258264
}
259265

266+
print("rapidGossipSyncUrl: \(rapidGossipSyncUrl)")
267+
260268
//Download url passed, enable rapid gossip sync
261269
if rapidGossipSyncUrl != "" {
262270
do {
@@ -377,6 +385,8 @@ class Ldk: NSObject {
377385
channelMonitorsSerialized.append([UInt8](try! Data(contentsOf: channelFile.standardizedFileURL)))
378386
}
379387

388+
print("enableP2PGossip \(enableP2PGossip)")
389+
380390
do {
381391
//Only restore a node if we have existing channel monitors to restore. Else we lose our UserConfig settings when restoring.
382392
if let channelManagerSerialized = storedChannelManager, channelMonitorsSerialized.count > 0 {
@@ -430,6 +440,10 @@ class Ldk: NSObject {
430440
peerHandler = channelManagerConstructor!.getTCPPeerHandler()
431441
invoicePayer = channelManagerConstructor!.payer
432442

443+
if enableP2PGossip {
444+
self.networkGraph = channelManagerConstructor!.net_graph
445+
}
446+
433447
return handleResolve(resolve, .channel_manager_init_success)
434448
}
435449

@@ -819,7 +833,7 @@ class Ldk: NSObject {
819833
}
820834

821835
@objc
822-
func networkGraphListNodes(_ resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
836+
func networkGraphListNodeIds(_ resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
823837
guard let networkGraph = networkGraph?.read_only() else {
824838
return handleReject(reject, .init_network_graph)
825839
}
@@ -833,20 +847,31 @@ class Ldk: NSObject {
833847
}
834848

835849
@objc
836-
func networkGraphNode(_ nodeId: NSString, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
850+
func networkGraphNodes(_ nodeIds: NSArray, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
837851
guard let networkGraph = networkGraph?.read_only() else {
838852
return handleReject(reject, .init_network_graph)
839853
}
854+
855+
let graphNodes = networkGraph.list_nodes().map { Data($0.as_slice()).hexEncodedString() }
856+
857+
//Filter out nodes we don't know about as querying unknown nodes will cause a crash
858+
let includedList = nodeIds.map({ $0 as! String }).filter { id in
859+
return graphNodes.contains { id == $0 }
860+
}
840861

841-
return resolve(networkGraph.node(node_id: NodeId(pubkey: String(nodeId).hexaBytes)).asJson)
862+
return resolve(includedList.map({ id in
863+
var info = networkGraph.node(node_id: NodeId(pubkey: id.hexaBytes)).asJson
864+
info["id"] = id
865+
return info
866+
}))
842867
}
843868

844869
@objc
845870
func networkGraphListChannels(_ resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
846871
guard let networkGraph = networkGraph?.read_only() else {
847872
return handleReject(reject, .init_network_graph)
848873
}
849-
874+
850875
let total = networkGraph.list_channels().count
851876
if total > 100 {
852877
return handleReject(reject, .data_too_large_for_rn, nil, "Too many channels to return (\(total))")
@@ -861,7 +886,14 @@ class Ldk: NSObject {
861886
return handleReject(reject, .init_network_graph)
862887
}
863888

864-
return resolve(networkGraph.channel(short_channel_id: UInt64(shortChannelId as String)!).asJson)
889+
let channelId = String(shortChannelId)
890+
891+
let channels = networkGraph.list_channels()
892+
if !channels.contains{ channelId == String($0) } {
893+
return handleReject(reject, .init_network_graph)
894+
}
895+
896+
return resolve(networkGraph.channel(short_channel_id: UInt64(channelId)!).asJson)
865897
}
866898

867899
@objc

0 commit comments

Comments
 (0)