Skip to content

Commit

Permalink
Merge pull request #236 from synonymdev/duplicate-receive-fix
Browse files Browse the repository at this point in the history
Duplicate payment receive fix
  • Loading branch information
ovitrif authored May 9, 2024
2 parents a3b90be + 5e032b1 commit 442b6c9
Show file tree
Hide file tree
Showing 8 changed files with 88 additions and 7 deletions.
11 changes: 11 additions & 0 deletions lib/android/src/main/java/com/reactnativeldk/LdkModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ enum class LdkCallbackResponses {
tx_set_unconfirmed,
process_pending_htlc_forwards_success,
claim_funds_success,
fail_htlc_backwards_success,
ldk_stop,
ldk_restart,
accept_channel_success,
Expand Down Expand Up @@ -506,6 +507,7 @@ class LdkModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMod

handleResolve(promise, LdkCallbackResponses.channel_manager_init_success)
}

@ReactMethod
fun restart(promise: Promise) {
if (channelManagerConstructor == null) {
Expand Down Expand Up @@ -875,6 +877,15 @@ class LdkModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMod
return handleResolve(promise, LdkCallbackResponses.claim_funds_success)
}

@ReactMethod
fun failHtlcBackwards(paymentHash: String, promise: Promise) {
channelManager ?: return handleReject(promise, LdkErrors.init_channel_manager)

channelManager!!.fail_htlc_backwards(paymentHash.hexa())

return handleResolve(promise, LdkCallbackResponses.fail_htlc_backwards_success)
}

//MARK: Fetch methods

@ReactMethod
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,11 @@ class LdkChannelManagerPersister: ChannelManagerConstructor.EventHandler {
val existingPayment = existingPayments.getJSONObject(i)
//Replace entry if payment hash exists (Confirmed payment replacing pending)
if (existingPayment.getString("payment_hash") == payment.getString("payment_hash")) {
//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.getString("state") == "successful") {
return
}

payments[i] = mergeObj(existingPayment, payment.toHashMap())
paymentReplaced = true
continue
Expand Down
5 changes: 5 additions & 0 deletions lib/ios/Classes/LdkChannelManagerPersister.swift
Original file line number Diff line number Diff line change
Expand Up @@ -412,6 +412,11 @@ class LdkChannelManagerPersister: Persister, ExtendedChannelManagerPersister {
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.
if existingPayment["state"] as? String == "successful" {
return
}

payments[index] = mergeObj(payments[index], payment) //Merges update into orginal entry
paymentReplaced = true
}
Expand Down
3 changes: 3 additions & 0 deletions lib/ios/Ldk.m
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,9 @@ @interface RCT_EXTERN_MODULE(Ldk, NSObject)
RCT_EXTERN_METHOD(claimFunds:(NSString *)paymentPreimage
resolve:(RCTPromiseResolveBlock)resolve
reject:(RCTPromiseRejectBlock)reject)
RCT_EXTERN_METHOD(failHtlcBackwards:(NSString *)paymentHash
resolve:(RCTPromiseResolveBlock)resolve
reject:(RCTPromiseRejectBlock)reject)

//MARK: Misc methods
RCT_EXTERN_METHOD(writeToFile:(NSString *)fileName
Expand Down
20 changes: 17 additions & 3 deletions lib/ios/Ldk.swift
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ enum LdkCallbackResponses: String {
case tx_set_unconfirmed = "tx_set_unconfirmed"
case process_pending_htlc_forwards_success = "process_pending_htlc_forwards_success"
case claim_funds_success = "claim_funds_success"
case fail_htlc_backwards_success = "fail_htlc_backwards_success"
case ldk_stop = "ldk_stop"
case ldk_restart = "ldk_restart"
case accept_channel_success = "accept_channel_success"
Expand Down Expand Up @@ -285,6 +286,9 @@ class Ldk: NSObject {

let networkGraphStoragePath = accountStoragePath.appendingPathComponent(LdkFileNames.network_graph.rawValue).standardizedFileURL

print("rapidGossipSyncUrl: \(rapidGossipSyncUrl)")
print("accountStoragePath: \(accountStoragePath)")

do {
let read = NetworkGraph.read(ser: [UInt8](try Data(contentsOf: networkGraphStoragePath)), arg: logger)
if read.isOk() {
Expand All @@ -305,9 +309,6 @@ class Ldk: NSObject {
return handleResolve(resolve, .network_graph_init_success)
}

print("rapidGossipSyncUrl: \(rapidGossipSyncUrl)")
print("accountStoragePath: \(accountStoragePath)")

//Download url passed, enable rapid gossip sync
do {
let rapidGossipSyncStoragePath = accountStoragePath.appendingPathComponent("rapid_gossip_sync")
Expand Down Expand Up @@ -953,6 +954,19 @@ class Ldk: NSObject {
return handleResolve(resolve, .claim_funds_success)
}

@objc
func failHtlcBackwards(_ paymentHash: NSString, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
guard let channelManager = channelManager else {
return handleReject(reject, .init_channel_manager)
}

LdkEventEmitter.shared.send(withEvent: .native_log, body: "Rejecting payment with failHtlcBackwards")

channelManager.failHtlcBackwards(paymentHash: String(paymentHash).hexaBytes)

return handleResolve(resolve, .fail_htlc_backwards_success)
}

//MARK: Fetch methods
@objc
func version(_ resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
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.137",
"version": "0.0.138",
"description": "React Native wrapper for LDK",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
Expand Down
16 changes: 16 additions & 0 deletions lib/src/ldk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -629,6 +629,22 @@ class LDK {
}
}

/**
* https://docs.rs/lightning/latest/lightning/ln/channelmanager/struct.ChannelManager.html#method.fail_htlc_backwards
* @returns {Promise<Err<unknown> | Ok<Ok<string> | Err<string>>>}
* @param paymentHash
*/
async failHtlcBackwards(paymentHash: string): Promise<Result<string>> {
try {
const res = await NativeLDK.failHtlcBackwards(paymentHash);
this.writeDebugToLog('failHtlcBackwards');
return ok(res);
} catch (e) {
this.writeErrorToLog('failHtlcBackwards', e);
return err(e);
}
}

/**
* Pays a bolt11 payment request and returns paymentId
* @param paymentRequest
Expand Down
33 changes: 30 additions & 3 deletions lib/src/lightning-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1815,12 +1815,39 @@ class LightningManager {
); //TODO
}

private onChannelManagerPaymentClaimable(res: TChannelManagerClaim): void {
private async onChannelManagerPaymentClaimable(
res: TChannelManagerClaim,
): Promise<void> {
const claimedPayments = await this.getLdkPaymentsClaimed();

const existingClaimedPayment = claimedPayments.find(
(p) => p.payment_hash === res.payment_hash,
);

console.error(
`Existing claimed payments: ${JSON.stringify(existingClaimedPayment)}`,
);

if (
existingClaimedPayment &&
existingClaimedPayment.state === 'successful'
) {
ldk
.writeToLogFile(
'error',
`Failing payment as it was already claimed: ${res.payment_hash}`,
)
.catch(console.error);

await ldk.failHtlcBackwards(res.payment_hash);
return;
}

if (res.spontaneous_payment_preimage) {
//https://docs.rs/lightning/latest/lightning/util/events/enum.PaymentPurpose.html#variant.SpontaneousPayment
ldk.claimFunds(res.spontaneous_payment_preimage).catch(console.error);
await ldk.claimFunds(res.spontaneous_payment_preimage);
} else {
ldk.claimFunds(res.payment_preimage).catch(console.error);
await ldk.claimFunds(res.payment_preimage);
}
}

Expand Down

0 comments on commit 442b6c9

Please sign in to comment.