diff --git a/lib/android/src/main/java/com/reactnativeldk/LdkModule.kt b/lib/android/src/main/java/com/reactnativeldk/LdkModule.kt index 588a4e4e..eca731f3 100644 --- a/lib/android/src/main/java/com/reactnativeldk/LdkModule.kt +++ b/lib/android/src/main/java/com/reactnativeldk/LdkModule.kt @@ -1228,7 +1228,7 @@ class LdkModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMod } @ReactMethod - fun spendRecoveredForceCloseOutputs(transaction: String, confirmationHeight: Double, changeDestinationScript: String, promise: Promise) { + fun spendRecoveredForceCloseOutputs(transaction: String, confirmationHeight: Double, changeDestinationScript: String, useInner: Boolean, promise: Promise) { if (channelStoragePath == "") { return handleReject(promise, LdkErrors.init_storage_path) } @@ -1270,13 +1270,23 @@ class LdkModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMod continue } - val res = keysManager!!.spend_spendable_outputs( - descriptors, - emptyArray(), - changeDestinationScript.hexa(), - feeEstimator.onChainSweep, - Option_u32Z.none() - ) + val res = if (useInner) { + keysManager!!.inner.spend_spendable_outputs( + descriptors, + emptyArray(), + changeDestinationScript.hexa(), + feeEstimator.onChainSweep, + Option_u32Z.none() + ) + } else { + keysManager!!.spend_spendable_outputs( + descriptors, + emptyArray(), + changeDestinationScript.hexa(), + feeEstimator.onChainSweep, + Option_u32Z.none() + ) + } if (res.is_ok) { txs.pushHexString((res as Result_TransactionNoneZ.Result_TransactionNoneZ_OK).res) diff --git a/lib/ios/Ldk.m b/lib/ios/Ldk.m index 34314708..fcc64328 100644 --- a/lib/ios/Ldk.m +++ b/lib/ios/Ldk.m @@ -176,6 +176,7 @@ @interface RCT_EXTERN_MODULE(Ldk, NSObject) RCT_EXTERN_METHOD(spendRecoveredForceCloseOutputs:(NSString *)transaction confirmationHeight:(NSInteger *)confirmationHeight changeDestinationScript:(NSString *)changeDestinationScript + useInner:(BOOL *)useInner resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) RCT_EXTERN_METHOD(nodeSign:(NSString *)message diff --git a/lib/ios/Ldk.swift b/lib/ios/Ldk.swift index 6c75a60e..f9453dff 100644 --- a/lib/ios/Ldk.swift +++ b/lib/ios/Ldk.swift @@ -507,7 +507,7 @@ class Ldk: NSObject { currentBlockchainHeight = blockHeight addForegroundObserver() startDroppedPeerTimer() - + return handleResolve(resolve, .channel_manager_init_success) } @@ -659,7 +659,7 @@ class Ldk: NSObject { guard let self else { return } LdkEventEmitter.shared.send(withEvent: .native_log, body: "Starting timer to check for dropped peers") - + droppedPeerTimer = Timer.scheduledTimer( timeInterval: 5.0, target: self, @@ -1385,7 +1385,7 @@ class Ldk: NSObject { } @objc - func spendRecoveredForceCloseOutputs(_ transaction: NSString, confirmationHeight: NSInteger, changeDestinationScript: NSString, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { + func spendRecoveredForceCloseOutputs(_ transaction: NSString, confirmationHeight: NSInteger, changeDestinationScript: NSString, useInner: Bool, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { guard let channelStoragePath = Ldk.channelStoragePath, let keysManager, let channelManager else { return handleReject(reject, .init_storage_path) @@ -1428,7 +1428,14 @@ class Ldk: NSObject { continue } - let res = keysManager.spendSpendableOutputs( + let res = useInner ? keysManager.inner.spendSpendableOutputs( + descriptors: descriptors, + outputs: [], + changeDestinationScript: String(changeDestinationScript).hexaBytes, + feerateSatPer1000Weight: feeEstimator.getEstSatPer1000Weight(confirmationTarget: .OnChainSweep), + locktime: nil) + : + keysManager.spendSpendableOutputs( descriptors: descriptors, outputs: [], changeDestinationScript: String(changeDestinationScript).hexaBytes, diff --git a/lib/src/ldk.ts b/lib/src/ldk.ts index ab6dae17..4d294357 100644 --- a/lib/src/ldk.ts +++ b/lib/src/ldk.ts @@ -1249,12 +1249,14 @@ class LDK { transaction, confirmationHeight, changeDestinationScript, + useInner, }: TSpendRecoveredForceCloseOutputsReq): Promise> { try { const res = await NativeLDK.spendRecoveredForceCloseOutputs( transaction, confirmationHeight, changeDestinationScript, + useInner, ); this.writeDebugToLog('spendRecoveredForceCloseOutputs', res); return ok(res); diff --git a/lib/src/lightning-manager.ts b/lib/src/lightning-manager.ts index aa86db5b..a977a221 100644 --- a/lib/src/lightning-manager.ts +++ b/lib/src/lightning-manager.ts @@ -1637,14 +1637,26 @@ class LightningManager { * Returns previously broadcasted transactions saved in storgare. * @returns {Promise} */ - async getLdkBroadcastedTxs(): Promise { + async getLdkBroadcastedTxs( + includeConfirmed: boolean = false, + ): Promise { + let txs: TLdkBroadcastedTransactions = []; const res = await ldk.readFromFile({ fileName: ELdkFiles.broadcasted_transactions, }); if (res.isOk()) { - return parseData(res.value.content, []); + txs = parseData(res.value.content, []); } - return []; + + if (includeConfirmed) { + const confirmedRes = await ldk.readFromFile({ + fileName: ELdkFiles.confirmed_broadcasted_transactions, + }); + if (confirmedRes.isOk()) { + txs = txs.concat(parseData(confirmedRes.value.content, [])); + } + } + return txs; } async cleanupBroadcastedTxs(): Promise { @@ -1766,33 +1778,41 @@ class LightningManager { return err('Unable to retrieve change_destination_script.'); } - const txs = await this.getLdkBroadcastedTxs(); + const txs = await this.getLdkBroadcastedTxs(true); if (!txs.length) { return ok('No outputs to reconstruct as no cached transactions found.'); } let txsToBroadcast = 0; - for (const hexTx of txs) { - const tx = bitcoin.Transaction.fromHex(hexTx); - const txData = await this.getTransactionData(tx.getId()); - const txsRes = await ldk.spendRecoveredForceCloseOutputs({ - transaction: hexTx, - confirmationHeight: txData?.height ?? 0, - changeDestinationScript, - }); + const processTxs = async (useInner: boolean): Promise => { + for (const hexTx of txs) { + const tx = bitcoin.Transaction.fromHex(hexTx); + const txData = await this.getTransactionData(tx.getId()); - if (txsRes.isErr()) { - await ldk.writeToLogFile('error', txsRes.error.message); - console.error(txsRes.error.message); - continue; - } + const txsRes = await ldk.spendRecoveredForceCloseOutputs({ + transaction: hexTx, + confirmationHeight: txData?.height ?? 0, + changeDestinationScript, + useInner, + }); + + if (txsRes.isErr()) { + await ldk.writeToLogFile('error', txsRes.error.message); + console.error(txsRes.error.message); + continue; + } - for (const createdTx of txsRes.value) { - txsToBroadcast++; - await this.broadcastTransaction(createdTx); + for (const createdTx of txsRes.value) { + txsToBroadcast++; + await this.broadcastTransaction(createdTx); + } } - } + }; + + //Process first using the inner (ldk keychain) and then using the custom keychain + await processTxs(true); + await processTxs(false); return ok(`Attempting to reconstruct ${txsToBroadcast} transactions.`); } diff --git a/lib/src/utils/types.ts b/lib/src/utils/types.ts index d9be84aa..8cb617b1 100644 --- a/lib/src/utils/types.ts +++ b/lib/src/utils/types.ts @@ -150,7 +150,7 @@ export type TChannel = { balance_sat: number; counterparty_node_id: string; funding_txid?: string; - funding_output_index? : number; + funding_output_index?: number; channel_type?: string; user_channel_id: string; confirmations_required?: number; @@ -613,6 +613,7 @@ export type TSpendRecoveredForceCloseOutputsReq = { transaction: string; confirmationHeight: number; changeDestinationScript: string; + useInner: boolean; }; export type TBackupServerDetails = {