Skip to content

Commit

Permalink
fix: gets peer message when manual open channel fails
Browse files Browse the repository at this point in the history
  • Loading branch information
Jasonvdb committed Sep 20, 2024
1 parent ebb1440 commit 98e7381
Show file tree
Hide file tree
Showing 7 changed files with 108 additions and 38 deletions.
38 changes: 35 additions & 3 deletions lib/android/src/main/java/com/reactnativeldk/LdkModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import org.ldk.structs.Result_Bolt11InvoiceParseOrSemanticErrorZ.Result_Bolt11In
import org.ldk.structs.Result_Bolt11InvoiceSignOrCreationErrorZ.Result_Bolt11InvoiceSignOrCreationErrorZ_OK
import org.ldk.structs.Result_C2Tuple_ThirtyTwoBytesChannelMonitorZDecodeErrorZ.Result_C2Tuple_ThirtyTwoBytesChannelMonitorZDecodeErrorZ_OK
import org.ldk.structs.Result_C3Tuple_ThirtyTwoBytesRecipientOnionFieldsRouteParametersZNoneZ.Result_C3Tuple_ThirtyTwoBytesRecipientOnionFieldsRouteParametersZNoneZ_OK
import org.ldk.structs.Result_ChannelIdAPIErrorZ.Result_ChannelIdAPIErrorZ_OK
import org.ldk.structs.Result_NoneAPIErrorZ.Result_NoneAPIErrorZ_OK
import org.ldk.structs.Result_NoneRetryableSendFailureZ.Result_NoneRetryableSendFailureZ_Err
import org.ldk.structs.Result_PublicKeyNoneZ.Result_PublicKeyNoneZ_OK
import org.ldk.structs.Result_StrSecp256k1ErrorZ.Result_StrSecp256k1ErrorZ_OK
Expand Down Expand Up @@ -772,6 +774,8 @@ class LdkModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMod
channelManager ?: return handleReject(promise, LdkErrors.init_channel_manager)
keysManager ?: return handleReject(promise, LdkErrors.init_keys_manager)

println("Creating channel with counterPartyNodeId: $counterPartyNodeId, channelValueSats: $channelValueSats, pushSats: $pushSats")

val theirNetworkKey = counterPartyNodeId.hexa()
val channelValueSatoshis = channelValueSats.toLong()
val pushMsat = pushSats.toLong() * 1000
Expand All @@ -790,11 +794,39 @@ class LdkModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMod
UserConfig.with_default()
)

if (!res.is_ok) {
return handleReject(promise, LdkErrors.start_create_channel_fail)
if (res.is_ok) {
return promise.resolve((res as Result_ChannelIdAPIErrorZ_OK).res._a.hexEncodedString())
}

if ((res as Result_ChannelIdAPIErrorZ.Result_ChannelIdAPIErrorZ_Err).err is APIError.APIMisuseError) {
return handleReject(promise, LdkErrors.start_create_channel_fail, Error((res.err as APIError.APIMisuseError).err))
}

if (res.err is APIError.ChannelUnavailable) {
return handleReject(promise, LdkErrors.start_create_channel_fail, Error((res.err as APIError.ChannelUnavailable).err))
}

if (res.err is APIError.FeeRateTooHigh) {
return handleReject(promise, LdkErrors.start_create_channel_fail, Error((res.err as APIError.FeeRateTooHigh).err))
}

if (res.err is APIError.InvalidRoute) {
return handleReject(promise, LdkErrors.start_create_channel_fail, Error((res.err as APIError.InvalidRoute).err))
}

if (res.err is APIError.IncompatibleShutdownScript) {
return handleReject(promise, LdkErrors.start_create_channel_fail, Error("IncompatibleShutdownScript"))
}

if (res.err is APIError.MonitorUpdateInProgress) {
return handleReject(promise, LdkErrors.start_create_channel_fail, Error("MonitorUpdateInProgress"))
}

if (res.err is APIError.ChannelUnavailable) {
return handleReject(promise, LdkErrors.start_create_channel_fail, Error((res.err as APIError.ChannelUnavailable).err))
}

handleResolve(promise, LdkCallbackResponses.start_create_channel_fail)
return handleReject(promise, LdkErrors.start_create_channel_fail)
}

@ReactMethod
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,14 @@ class LdkChannelManagerPersister: ChannelManagerConstructor.EventHandler {
}
body.putString("reason", reasonString)

if (channelClosed.reason is ClosureReason.CounterpartyForceClosed) {
(channelClosed.reason as ClosureReason.CounterpartyForceClosed).peer_msg?.let {
body.putString("peer_message", it.to_str())
}
}

println("Channel closed: ${channelClosed.channel_id._a.hexEncodedString()}")

return LdkEventEmitter.send(EventTypes.channel_manager_channel_closed, body)
}

Expand Down
68 changes: 36 additions & 32 deletions lib/ios/Classes/LdkChannelManagerPersister.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import Foundation
import LightningDevKit

class LdkChannelManagerPersister: Persister, ExtendedChannelManagerPersister {
//Custom function to manage any unlikely missing info from the event object
// Custom function to manage any unlikely missing info from the event object
func handleEventError(_ event: Event) {
LdkEventEmitter.shared.send(
withEvent: .native_log,
Expand All @@ -25,14 +25,14 @@ class LdkChannelManagerPersister: Persister, ExtendedChannelManagerPersister {
guard let fundingGeneration = event.getValueAsFundingGenerationReady() else {
return handleEventError(event)
}

LdkEventEmitter.shared.send(
withEvent: .channel_manager_funding_generation_ready,
body: [
"temp_channel_id": Data(fundingGeneration.getTemporaryChannelId().getA() ?? []).hexEncodedString(),
"output_script": Data(fundingGeneration.getOutputScript()).hexEncodedString(),
"user_channel_id": Data(fundingGeneration.getUserChannelId()).hexEncodedString(),
"value_satoshis": fundingGeneration.getChannelValueSatoshis(),
"value_satoshis": fundingGeneration.getChannelValueSatoshis()
]
)
return
Expand Down Expand Up @@ -60,7 +60,7 @@ class LdkChannelManagerPersister: Persister, ExtendedChannelManagerPersister {
body: body
)

//Save to disk for TX history
// Save to disk for TX history
persistPaymentClaimed(body)
return
case .PaymentSent:
Expand All @@ -82,11 +82,11 @@ class LdkChannelManagerPersister: Persister, ExtendedChannelManagerPersister {
body: body
)

//Save to disk for tx history
// Save to disk for tx history
persistPaymentSent(body)
return
case .OpenChannelRequest:
//Use if we ever manually accept inbound channels. Setting in initConfig.
// Use if we ever manually accept inbound channels. Setting in initConfig.
guard let openChannelRequest = event.getValueAsOpenChannelRequest() else {
return handleEventError(event)
}
Expand All @@ -100,7 +100,7 @@ class LdkChannelManagerPersister: Persister, ExtendedChannelManagerPersister {
"requires_zero_conf": openChannelRequest.getChannelType().requiresZeroConf(),
"supports_zero_conf": openChannelRequest.getChannelType().supportsZeroConf(),
"requires_anchors_zero_fee_htlc_tx": openChannelRequest.getChannelType().requiresAnchorsZeroFeeHtlcTx()
] as [String : Any]
] as [String: Any]
)
return
case .PaymentPathSuccessful:
Expand All @@ -116,8 +116,8 @@ class LdkChannelManagerPersister: Persister, ExtendedChannelManagerPersister {
body: [
"payment_id": paymentId,
"payment_hash": paymentHash,
"path_hops": paymentPathSuccessful.getPath().getHops().map { $0.asJson },
] as [String : Any]
"path_hops": paymentPathSuccessful.getPath().getHops().map { $0.asJson }
] as [String: Any]
)
return
case .PaymentPathFailed:
Expand All @@ -136,15 +136,15 @@ class LdkChannelManagerPersister: Persister, ExtendedChannelManagerPersister {
"payment_failed_permanently": paymentPathFailed.getPaymentFailedPermanently(),
"short_channel_id": String(paymentPathFailed.getShortChannelId() ?? 0),
"path_hops": paymentPathFailed.getPath().getHops().map { $0.asJson }
] as [String : Any]
] as [String: Any]
)

persistPaymentSent(
[
"payment_id": paymentId,
"payment_hash": paymentHash,
"unix_timestamp": Int(Date().timeIntervalSince1970),
"state": paymentPathFailed.getPaymentFailedPermanently() ? "failed" : "pending"
"state": paymentPathFailed.getPaymentFailedPermanently() ? "failed" : "pending"
]
)
return
Expand All @@ -160,23 +160,23 @@ class LdkChannelManagerPersister: Persister, ExtendedChannelManagerPersister {
withEvent: .channel_manager_payment_failed,
body: [
"payment_id": paymentId,
"payment_hash": paymentHash,
"payment_hash": paymentHash
]
)

//MARK: Mark as failed
// MARK: Mark as failed

persistPaymentSent(
[
"payment_id": paymentId,
"payment_hash": paymentHash,
"unix_timestamp": Int(Date().timeIntervalSince1970),
"state": "failed"
"state": "failed"
]
)
return
case .PaymentForwarded:
//Unused on mobile
// Unused on mobile
return
case .PendingHTLCsForwardable:
guard let pendingHtlcsForwardable = event.getValueAsPendingHtlcsForwardable() else {
Expand All @@ -186,7 +186,7 @@ class LdkChannelManagerPersister: Persister, ExtendedChannelManagerPersister {
LdkEventEmitter.shared.send(
withEvent: .channel_manager_pending_htlcs_forwardable,
body: [
"time_forwardable": pendingHtlcsForwardable.getTimeForwardable(),
"time_forwardable": pendingHtlcsForwardable.getTimeForwardable()
]
)
return
Expand All @@ -198,7 +198,7 @@ class LdkChannelManagerPersister: Persister, ExtendedChannelManagerPersister {
LdkEventEmitter.shared.send(
withEvent: .channel_manager_spendable_outputs,
body: [
"outputsSerialized": spendableOutputs.getOutputs().map { Data($0.write()).hexEncodedString() },
"outputsSerialized": spendableOutputs.getOutputs().map { Data($0.write()).hexEncodedString() }
]
)
return
Expand All @@ -208,13 +208,16 @@ class LdkChannelManagerPersister: Persister, ExtendedChannelManagerPersister {
}

let reasonString: String
var peerMessage: String? = nil

switch channelClosed.getReason().getValueType() {
case .CommitmentTxConfirmed:
reasonString = "CommitmentTxConfirmed"
case .CounterpartyCoopClosedUnfundedChannel:
reasonString = "CounterpartyCoopClosedUnfundedChannel"
case .CounterpartyForceClosed:
reasonString = "CounterpartyForceClosed"
peerMessage = channelClosed.getReason().getValueAsCounterpartyForceClosed()?.getPeerMsg().getA()
case .DisconnectedPeer:
reasonString = "DisconnectedPeer"
case .FundingBatchClosure:
Expand Down Expand Up @@ -244,17 +247,18 @@ class LdkChannelManagerPersister: Persister, ExtendedChannelManagerPersister {
body: [
"user_channel_id": Data(channelClosed.getUserChannelId()).hexEncodedString(),
"channel_id": Data(channelClosed.getChannelId().getA() ?? []).hexEncodedString(),
"reason": reasonString
"reason": reasonString,
"peer_message": peerMessage
]
)

return
case .DiscardFunding:
guard let discardFunding = event.getValueAsDiscardFunding() else {
return handleEventError(event)
}

//Wallet should probably "lock" the UTXOs spent in funding transactions until the funding transaction either confirms, or this event is generated.
// Wallet should probably "lock" the UTXOs spent in funding transactions until the funding transaction either confirms, or this event is generated.
LdkEventEmitter.shared.send(
withEvent: .channel_manager_discard_funding,
body: [
Expand Down Expand Up @@ -287,7 +291,7 @@ class LdkChannelManagerPersister: Persister, ExtendedChannelManagerPersister {
body: body
)

//Save to disk for TX history
// Save to disk for TX history
persistPaymentClaimed(body)
return
case .ChannelReady:
Expand All @@ -300,7 +304,7 @@ class LdkChannelManagerPersister: Persister, ExtendedChannelManagerPersister {
guard let bumpTransaction = event.getValueAsBumpTransaction() else {
return handleEventError(event)
}

LdkEventEmitter.shared.send(withEvent: .native_log, body: "BumpTransaction request")

if let channelClose = bumpTransaction.getValueAsChannelClose() {
Expand All @@ -309,11 +313,11 @@ class LdkChannelManagerPersister: Persister, ExtendedChannelManagerPersister {
"commitment_tx_fee": channelClose.getCommitmentTxFeeSatoshis(),
"pending_htlcs_count": channelClose.getPendingHtlcs().count
]

LdkEventEmitter.shared.send(withEvent: .lsp_log, body: body)
return
}

LdkEventEmitter.shared.send(withEvent: .native_log, body: "BumpTransaction event not handled")
return
case .ProbeFailed:
Expand Down Expand Up @@ -413,23 +417,23 @@ class LdkChannelManagerPersister: Persister, ExtendedChannelManagerPersister {
}
}

//Replace entry if payment hash exists (Confirmed payment replacing pending)
// Replace entry if payment hash exists (Confirmed payment replacing pending)
var paymentReplaced = false
for (index, existingPayment) in payments.enumerated() {
if let existingPaymentHash = existingPayment["payment_hash"] as? String, let newPaymentHash = payment["payment_hash"] as? String {
if existingPaymentHash == newPaymentHash {
//Don't replace a successful payment. If a 2nd wallet tries to pay an invoice the first successful one should not be overwritten.
// Don't replace a successful payment. If a 2nd wallet tries to pay an invoice the first successful one should not be overwritten.
if existingPayment["state"] as? String == "successful" {
return
}
payments[index] = mergeObj(payments[index], payment) //Merges update into orginal entry

payments[index] = mergeObj(payments[index], payment) // Merges update into orginal entry
paymentReplaced = true
}
}
}

//No existing payment found, append as new payment
// No existing payment found, append as new payment
if !paymentReplaced {
payments.append(payment)
}
Expand Down Expand Up @@ -472,18 +476,18 @@ class LdkChannelManagerPersister: Persister, ExtendedChannelManagerPersister {
}
}

//Replace entry if payment hash exists (Confirmed payment replacing pending)
// Replace entry if payment hash exists (Confirmed payment replacing pending)
var paymentReplaced = false
for (index, existingPayment) in payments.enumerated() {
if let existingPaymentId = existingPayment["payment_id"] as? String, let newPaymentId = payment["payment_id"] as? String {
if existingPaymentId == newPaymentId {
payments[index] = mergeObj(payments[index], payment) //Merges update into orginal entry
payments[index] = mergeObj(payments[index], payment) // Merges update into orginal entry
paymentReplaced = true
}
}
}

//No existing payment found, append as new payment
// No existing payment found, append as new payment
if !paymentReplaced {
payments.append(payment)
}
Expand Down
19 changes: 19 additions & 0 deletions lib/ios/Ldk.swift
Original file line number Diff line number Diff line change
Expand Up @@ -937,6 +937,25 @@ class Ldk: NSObject {
return resolve(Data(res.getValue()?.getA() ?? []).hexEncodedString())
}

if let error = res.getError() {
switch error.getValueType() {
case .APIMisuseError:
return handleReject(reject, .start_create_channel_fail, nil, error.getValueAsApiMisuseError()?.getErr())
case .ChannelUnavailable:
return handleReject(reject, .start_create_channel_fail, nil, error.getValueAsChannelUnavailable()?.getErr())
case .FeeRateTooHigh:
return handleReject(reject, .start_create_channel_fail, nil, error.getValueAsFeeRateTooHigh()?.getErr())
case .InvalidRoute:
return handleReject(reject, .start_create_channel_fail, nil, error.getValueAsInvalidRoute()?.getErr())
case .IncompatibleShutdownScript:
return handleReject(reject, .start_create_channel_fail, nil, "IncompatibleShutdownScript")
case .MonitorUpdateInProgress:
return handleReject(reject, .start_create_channel_fail, nil, "MonitorUpdateInProgress")
@unknown default:
handleReject(reject, .start_create_channel_fail, "Unhandled error")
}
}

handleReject(reject, .start_create_channel_fail)
}

Expand Down
2 changes: 1 addition & 1 deletion lib/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@synonymdev/react-native-ldk",
"title": "React Native LDK",
"version": "0.0.151",
"version": "0.0.152",
"description": "React Native wrapper for LDK",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
Expand Down
10 changes: 8 additions & 2 deletions lib/src/lightning-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2449,7 +2449,7 @@ class LightningManager {
return new Promise((resolve, reject) => {

Check warning on line 2449 in lib/src/lightning-manager.ts

View workflow job for this annotation

GitHub Actions / Run lint check

'reject' is defined but never used. Allowed unused args must match /^_/u

Check warning on line 2449 in lib/src/lightning-manager.ts

View workflow job for this annotation

GitHub Actions / Run lint check

'reject' is defined but never used. Allowed unused args must match /^_/u

Check warning on line 2449 in lib/src/lightning-manager.ts

View workflow job for this annotation

GitHub Actions / e2e-ios

'reject' is defined but never used. Allowed unused args must match /^_/u

Check warning on line 2449 in lib/src/lightning-manager.ts

View workflow job for this annotation

GitHub Actions / e2e-android

'reject' is defined but never used. Allowed unused args must match /^_/u
// Channel funding ready event should be instant but if it fails and we don't get the event, we should reject.
const timeout = setTimeout(() => {
reject(err(new Error('Event not triggered within 5 seconds')));
resolve(err(new Error('Event not triggered within 5 seconds')));
}, 5000);

// Listen for the event for the channel funding details
Expand All @@ -2465,8 +2465,14 @@ class LightningManager {
EEventTypes.channel_manager_channel_closed,
(eventRes: TChannelManagerChannelClosed) => {
if (eventRes.channel_id === res.value) {

clearTimeout(timeout);
reject(err('Channel closed before funding'));
if (eventRes.peer_message) {
resolve(err(eventRes.peer_message));
return;
}

resolve(err(`Channel open failed: ${eventRes.reason}`));
}
},
);
Expand Down
1 change: 1 addition & 0 deletions lib/src/utils/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ export type TChannelManagerChannelClosed = {
user_channel_id: string;
channel_id: string;
reason: string;
peer_message?: string;
};

export type TChannelManagerDiscardFunding = {
Expand Down

0 comments on commit 98e7381

Please sign in to comment.