Skip to content

Commit

Permalink
feat: ios list all channel monitors
Browse files Browse the repository at this point in the history
  • Loading branch information
Jasonvdb committed Mar 21, 2024
1 parent 94b0741 commit c4ac2c1
Show file tree
Hide file tree
Showing 6 changed files with 152 additions and 52 deletions.
17 changes: 17 additions & 0 deletions example/Dev.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -587,6 +587,23 @@ const Dev = (): ReactElement => {
}}
/>

<Button
title={'List channel monitors'}
onPress={async (): Promise<void> => {
const monitorsRes = await ldk.listChannelMonitors(true);
if (monitorsRes.isErr()) {
return setMessage(monitorsRes.error.message);
}

let msg = `Channel Monitors (${monitorsRes.value.length}): \n`;
monitorsRes.value.forEach((monitor) => {
msg += `\n\n${JSON.stringify(monitor)}`;
});

setMessage(msg);
}}
/>

<Button
title={'Show version'}
onPress={async (): Promise<void> => {
Expand Down
113 changes: 62 additions & 51 deletions lib/ios/Helpers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,17 @@ extension LightningDevKit.RouteHop {
}
}

extension ChannelMonitor {
func asJson(channelId: String) -> [String: Any?] {
return [
"channel_id": channelId,
"funding_txo": Data(getFundingTxo().1).hexEncodedString(),
"counterparty_node_id": Data(getCounterpartyNodeId() ?? []).hexEncodedString(),
"claimable_balances": getClaimableBalances().map({ $0.asJson })
]
}
}

extension Data {
struct HexEncodingOptions: OptionSet {
let rawValue: Int
Expand Down Expand Up @@ -402,63 +413,63 @@ extension UserConfig {
}
}

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

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])
}
result.append(balance.asJson)
}

return result
Expand Down
3 changes: 3 additions & 0 deletions lib/ios/Ldk.m
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,9 @@ @interface RCT_EXTERN_MODULE(Ldk, NSObject)
reject:(RCTPromiseRejectBlock)reject)
RCT_EXTERN_METHOD(listChannelFiles:(RCTPromiseResolveBlock)resolve
reject:(RCTPromiseRejectBlock)reject)
RCT_EXTERN_METHOD(listChannelMonitors:(BOOL *)ignoreOpenChannels
resolve:(RCTPromiseResolveBlock)resolve
reject:(RCTPromiseRejectBlock)reject)
RCT_EXTERN_METHOD(networkGraphListNodeIds:(RCTPromiseResolveBlock)resolve
reject:(RCTPromiseRejectBlock)reject)
RCT_EXTERN_METHOD(networkGraphListChannels:(RCTPromiseResolveBlock)resolve
Expand Down
45 changes: 44 additions & 1 deletion lib/ios/Ldk.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1014,6 +1014,49 @@ class Ldk: NSObject {
return resolve(try! FileManager.default.contentsOfDirectory(at: channelStoragePath, includingPropertiesForKeys: nil).map { $0.lastPathComponent })
}

@objc
func listChannelMonitors(_ ignoreOpenChannels: Bool, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
guard let channelManager else {
return handleReject(reject, .init_channel_manager)
}

guard let keysManager else {
return handleReject(reject, .init_keys_manager)
}

guard let channelStoragePath = Ldk.channelStoragePath else {
return handleReject(reject, .init_storage_path)
}

let excludeChannelIds = ignoreOpenChannels ? channelManager.listChannels().map { Data($0.getChannelId() ?? []).hexEncodedString() }.filter { $0 != "" } : []

let channelFiles = try! FileManager.default.contentsOfDirectory(at: channelStoragePath, includingPropertiesForKeys: nil)

var result: [[String: Any?]] = []
for channelFile in channelFiles {
let channelId = channelFile.lastPathComponent.replacingOccurrences(of: ".bin", with: "")

guard !excludeChannelIds.contains(channelId) else {
continue
}

let channelMonitorResult = Bindings.readThirtyTwoBytesChannelMonitor(
ser: [UInt8](try! Data(contentsOf: channelFile.standardizedFileURL)),
argA: keysManager.inner.asEntropySource(),
argB: keysManager.signerProvider
)

guard let (channelId, channelMonitor) = channelMonitorResult.getValue() else {
LdkEventEmitter.shared.send(withEvent: .native_log, body: "Loading channel error. No channel value.")
continue
}

result.append(channelMonitor.asJson(channelId: Data(channelId).hexEncodedString()))
}

return resolve(result)
}

@objc
func networkGraphListNodeIds(_ resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
guard let networkGraph = networkGraph?.readOnly() else {
Expand Down Expand Up @@ -1203,7 +1246,7 @@ class Ldk: NSObject {

@objc
func spendRecoveredForceCloseOutputs(_ transaction: NSString, confirmationHeight: NSInteger, changeDestinationScript: NSString, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
//TODO check which ones are not open channels and try spend them again

guard let channelStoragePath = Ldk.channelStoragePath, let keysManager, let channelManager else {
return handleReject(reject, .init_storage_path)
}
Expand Down
19 changes: 19 additions & 0 deletions lib/src/ldk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import {
TDownloadScorer,
TInitKeysManager,
TSpendRecoveredForceCloseOutputsReq,
TChannelMonitor,
} from './utils/types';
import { extractPaymentRequest } from './utils/helpers';

Expand Down Expand Up @@ -957,6 +958,24 @@ class LDK {
}
}

/**
* Lists all channel monitors from storage
* @param ignoreOpenChannels
* @returns {Promise<Ok<Ok<string[]> | Err<string[]>> | Err<unknown>>}
*/
async listChannelMonitors(
ignoreOpenChannels: boolean,
): Promise<Result<TChannelMonitor[]>> {
try {
const res = await NativeLDK.listChannelMonitors(ignoreOpenChannels);
this.writeDebugToLog('listChannelMonitors', `Monitors: ${res.length}`);
return ok(res);
} catch (e) {
this.writeErrorToLog('listChannelMonitors', e);
return err(e);
}
}

/**
* Fetches list of all node IDs in network graph
* https://docs.rs/lightning/latest/lightning/routing/gossip/struct.ReadOnlyNetworkGraph.html#method.nodes
Expand Down
7 changes: 7 additions & 0 deletions lib/src/utils/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,13 @@ export type TChannel = {
confirmations: number;
};

export type TChannelMonitor = {
channel_id: string;
funding_txo: string;
counterparty_node_id: string;
claimable_balances: [TClaimableBalance];
};

export type TNetworkGraphChannelInfo = {
shortChannelId: string;
capacity_sats?: number;
Expand Down

0 comments on commit c4ac2c1

Please sign in to comment.