Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: dump useful node info to logs on startup and each sync #174

Merged
merged 2 commits into from
Oct 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions example/Dev.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -698,6 +698,19 @@ const Dev = (): ReactElement => {
setMessage(res.value);
}}
/>

<Button
title={'Node state'}
onPress={async (): Promise<void> => {
const res = await ldk.nodeStateDump();
if (res.isErr()) {
setMessage(res.error.message);
return;
}

setMessage(res.value);
}}
/>
</View>
</ScrollView>

Expand Down
50 changes: 50 additions & 0 deletions lib/android/src/main/java/com/reactnativeldk/Helpers.kt
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,56 @@ fun UserConfig.mergeWithMap(map: ReadableMap): UserConfig {
return this
}

fun ChainMonitor.getClaimableBalancesAsJson(ignoredChannels: Array<ChannelDetails>): WritableArray {
val result = Arguments.createArray()

get_claimable_balances(ignoredChannels).iterator().forEach { balance ->
val map = Arguments.createMap()
//Defaults if all castings for balance fail
map.putInt("amount_satoshis", 0)
map.putString("type", "Unknown")

(balance as? Balance.ClaimableAwaitingConfirmations)?.let { claimableAwaitingConfirmations ->
map.putInt("amount_satoshis", claimableAwaitingConfirmations.amount_satoshis.toInt())
map.putInt("confirmation_height", claimableAwaitingConfirmations.confirmation_height)
map.putString("type", "ClaimableAwaitingConfirmations")
}

(balance as? Balance.ClaimableOnChannelClose)?.let { claimableOnChannelClose ->
map.putInt("amount_satoshis", claimableOnChannelClose.amount_satoshis.toInt())
map.putString("type", "ClaimableOnChannelClose")
}

(balance as? Balance.ContentiousClaimable)?.let { contentiousClaimable ->
map.putInt("amount_satoshis", contentiousClaimable.amount_satoshis.toInt())
map.putInt("timeout_height", contentiousClaimable.timeout_height)
map.putString("type", "ContentiousClaimable")
}

(balance as? Balance.CounterpartyRevokedOutputClaimable)?.let { counterpartyRevokedOutputClaimable ->
map.putInt("amount_satoshis", counterpartyRevokedOutputClaimable.amount_satoshis.toInt())
map.putString("type", "CounterpartyRevokedOutputClaimable")
}

(balance as? Balance.MaybePreimageClaimableHTLC)?.let { maybePreimageClaimableHTLC ->
map.putInt("amount_satoshis", maybePreimageClaimableHTLC.amount_satoshis.toInt())
map.putInt("expiry_height", maybePreimageClaimableHTLC.expiry_height)
map.putString("type", "MaybePreimageClaimableHTLC")
}

(balance as? Balance.MaybeTimeoutClaimableHTLC)?.let { maybeTimeoutClaimableHTLC ->
map.putInt("amount_satoshis", maybeTimeoutClaimableHTLC.amount_satoshis.toInt())
map.putInt("claimable_height", maybeTimeoutClaimableHTLC.claimable_height)
map.putString("type", "MaybeTimeoutClaimableHTLC")
}

result.pushMap(map)
}

return result
}


/// Helper for returning real network and currency as a tuple from a string
fun getNetwork(chain: String): Pair<Network, Currency> {
return when (chain) {
Expand Down
110 changes: 66 additions & 44 deletions lib/android/src/main/java/com/reactnativeldk/LdkModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import java.io.File
import java.net.InetSocketAddress
import java.nio.file.Files
import java.nio.file.Paths
import java.text.SimpleDateFormat
import java.util.*


Expand Down Expand Up @@ -962,52 +963,9 @@ class LdkModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMod
channelManager.list_channels() else
arrayOf<ChannelDetails>()

val result = Arguments.createArray()

chainMonitor.get_claimable_balances(ignoredChannels).iterator().forEach { balance ->
val map = Arguments.createMap()
//Defaults if all castings for balance fail
map.putInt("amount_satoshis", 0)
map.putString("type", "Unknown")

(balance as? Balance.ClaimableAwaitingConfirmations)?.let { claimableAwaitingConfirmations ->
map.putInt("amount_satoshis", claimableAwaitingConfirmations.amount_satoshis.toInt())
map.putInt("confirmation_height", claimableAwaitingConfirmations.confirmation_height)
map.putString("type", "ClaimableAwaitingConfirmations")
}

(balance as? Balance.ClaimableOnChannelClose)?.let { claimableOnChannelClose ->
map.putInt("amount_satoshis", claimableOnChannelClose.amount_satoshis.toInt())
map.putString("type", "ClaimableOnChannelClose")
}

(balance as? Balance.ContentiousClaimable)?.let { contentiousClaimable ->
map.putInt("amount_satoshis", contentiousClaimable.amount_satoshis.toInt())
map.putInt("timeout_height", contentiousClaimable.timeout_height)
map.putString("type", "ContentiousClaimable")
}

(balance as? Balance.CounterpartyRevokedOutputClaimable)?.let { counterpartyRevokedOutputClaimable ->
map.putInt("amount_satoshis", counterpartyRevokedOutputClaimable.amount_satoshis.toInt())
map.putString("type", "CounterpartyRevokedOutputClaimable")
}

(balance as? Balance.MaybePreimageClaimableHTLC)?.let { maybePreimageClaimableHTLC ->
map.putInt("amount_satoshis", maybePreimageClaimableHTLC.amount_satoshis.toInt())
map.putInt("expiry_height", maybePreimageClaimableHTLC.expiry_height)
map.putString("type", "MaybePreimageClaimableHTLC")
}

(balance as? Balance.MaybeTimeoutClaimableHTLC)?.let { maybeTimeoutClaimableHTLC ->
map.putInt("amount_satoshis", maybeTimeoutClaimableHTLC.amount_satoshis.toInt())
map.putInt("claimable_height", maybeTimeoutClaimableHTLC.claimable_height)
map.putString("type", "MaybeTimeoutClaimableHTLC")
}

result.pushMap(map)
}

promise.resolve(result)
promise.resolve(chainMonitor.getClaimableBalancesAsJson(ignoredChannels))
}

//MARK: Misc methods
Expand Down Expand Up @@ -1122,6 +1080,70 @@ class LdkModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMod
promise.resolve((res as Result_StringErrorZ_OK).res)
}

@ReactMethod
fun nodeStateDump(promise: Promise) {
val logDump: MutableList<String> = mutableListOf()

keysManager?.as_NodeSigner()?.get_node_id(Recipient.LDKRecipient_Node)?.let { pubKeyRes ->
if (pubKeyRes.is_ok) {
logDump.add("NodeID: ${(pubKeyRes as Result_PublicKeyNoneZ_OK).res.hexEncodedString()}")
}
}

channelManager?.list_channels()?.forEach { channel ->
logDump.add("Open channel:")

channel._funding_txo?._txid?.let { txId ->
logDump.add("Funding txid: ${txId.hexEncodedString()}")
}

logDump.add("Ready: ${if (channel._is_channel_ready) "YES" else "NO"}")
logDump.add("Usable: ${if (channel._is_usable) "YES" else "NO"}")
logDump.add("Balance: ${channel._balance_msat / 1000} sats")
}

chainMonitor?.getClaimableBalancesAsJson(arrayOf())?.let { claimableBalances ->
logDump.add("All claimable balances:\n $claimableBalances")
} ?: run {
logDump.add("Claimable balances unavailable. Chain monitor not set yet")
}

networkGraph?._last_rapid_gossip_sync_timestamp?.let { res ->
val syncTimestamp = if (res is Option_u32Z.Some) (res as Option_u32Z.Some).some.toLong() else 0
if (syncTimestamp == 0L) {
logDump.add("Last rapid gossip sync time: NEVER")
} else {
val date = Date(syncTimestamp * 1000)
val dateFormatter = SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
logDump.add("Last rapid gossip sync time: ${dateFormatter.format(date)}")
}
} ?: run {
logDump.add("RGS last sync time unavailable.")
}

peerManager?._peer_node_ids?.let { peers ->
if (peers.isNotEmpty()) {
peers.forEach { peer ->
logDump.add("Connected peer: ${peer._a.hexEncodedString()}")
}
} else {
logDump.add("No connected peers")
}
} ?: run {
logDump.add("Connected peers unavailable. Peer manager not set.")
}

logDump.add("Storage: ${accountStoragePath}")

logDump.add("BackupClient setup: ${if (BackupClient.requiresSetup) "NO" else "YES"}")
logDump.add("Skip remote backups: ${if (BackupClient.skipRemoteBackup) "YES" else "NO"}")

val logString = "********NODE STATE********\n" + logDump.joinToString("\n") + "\n****************"
LogFile.write(logString)

promise.resolve(logString)
}

//Backup methods
@ReactMethod
fun backupSetup(seed: String, network: String, server: String, serverPubKey: String, promise: Promise) {
Expand Down
63 changes: 63 additions & 0 deletions lib/ios/Helpers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,69 @@ extension UserConfig {
}
}

extension ChainMonitor {
func getClaimableBalancesAsJson(ignoredChannels: [Bindings.ChannelDetails]) -> [[String: Any]] {
var result: [[String: Any]] = []

let claimableBalances = self.getClaimableBalances(ignoredChannels: ignoredChannels)
for balance in claimableBalances {
switch balance.getValueType() {
case .ClaimableAwaitingConfirmations:
let b = balance.getValueAsClaimableAwaitingConfirmations()!
result.append([
"amount_satoshis": b.getAmountSatoshis(),
"confirmation_height": b.getConfirmationHeight(),
"type": "ClaimableAwaitingConfirmations"
] as [String : Any])
break
case .ClaimableOnChannelClose:
let b = balance.getValueAsClaimableOnChannelClose()!
result.append([
"amount_satoshis": b.getAmountSatoshis(),
"type": "ClaimableOnChannelClose",
] as [String : Any])
break
case .ContentiousClaimable:
let b = balance.getValueAsContentiousClaimable()!
result.append([
"amount_satoshis": b.getAmountSatoshis(),
"timeout_height": b.getTimeoutHeight(),
"type": "ContentiousClaimable"
] as [String : Any])
break
case .CounterpartyRevokedOutputClaimable:
let b = balance.getValueAsCounterpartyRevokedOutputClaimable()!
result.append([
"amount_satoshis": b.getAmountSatoshis(),
"type": "CounterpartyRevokedOutputClaimable"
] as [String : Any])
break
case .MaybePreimageClaimableHTLC:
let b = balance.getValueAsMaybePreimageClaimableHtlc()!
result.append([
"amount_satoshis": b.getAmountSatoshis(),
"expiry_height": b.getExpiryHeight(),
"type": "MaybePreimageClaimableHTLC"
] as [String : Any])
break
case .MaybeTimeoutClaimableHTLC:
let b = balance.getValueAsMaybeTimeoutClaimableHtlc()!
result.append([
"amount_satoshis": b.getAmountSatoshis(),
"claimable_height": b.getClaimableHeight(),
"type": "MaybeTimeoutClaimableHTLC"
] as [String : Any])
break
default:
LdkEventEmitter.shared.send(withEvent: .native_log, body: "Unknown balance type type in claimableBalances() \(balance.getValueType())")
result.append(["amount_satoshis": 0, "type": "Unknown"] as [String : Any])
}
}

return result
}
}

func handlePaymentSendFailure(_ reject: RCTPromiseRejectBlock, error: Bindings.PaymentSendFailure) {
switch error.getValueType() {
case .AllFailedResendSafe:
Expand Down
2 changes: 2 additions & 0 deletions lib/ios/Ldk.m
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,8 @@ @interface RCT_EXTERN_MODULE(Ldk, NSObject)
RCT_EXTERN_METHOD(nodeSign:(NSString *)message
resolve:(RCTPromiseResolveBlock)resolve
reject:(RCTPromiseRejectBlock)reject)
RCT_EXTERN_METHOD(nodeStateDump:(RCTPromiseResolveBlock)resolve
reject:(RCTPromiseRejectBlock)reject)

//MARK: Backup methods
RCT_EXTERN_METHOD(backupSetup:(NSString *)seed
Expand Down
Loading