From d9ed5cbd4f311c62da7f8bb598ae98a883a7ec1a Mon Sep 17 00:00:00 2001 From: Corey Phillips Date: Thu, 21 Sep 2023 10:30:27 -0400 Subject: [PATCH] fix(lightning): Attempt Restart In syncLdk Attempts to restart LDK if any error or timeout occurs in the syncLdk method. Bumps version to 0.0.110. --- example/ios/Podfile.lock | 4 +- lib/package.json | 2 +- lib/src/lightning-manager.ts | 106 ++++++++++++++++++++++++++++++----- 3 files changed, 94 insertions(+), 18 deletions(-) diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index 995dafc0..cfabba74 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -302,7 +302,7 @@ PODS: - React-jsinspector (0.70.6) - React-logger (0.70.6): - glog - - react-native-ldk (0.0.109): + - react-native-ldk (0.0.110): - React - react-native-randombytes (3.6.1): - React-Core @@ -593,7 +593,7 @@ SPEC CHECKSUMS: React-jsiexecutor: b4a65947391c658450151275aa406f2b8263178f React-jsinspector: 60769e5a0a6d4b32294a2456077f59d0266f9a8b React-logger: 1623c216abaa88974afce404dc8f479406bbc3a0 - react-native-ldk: 3c1cd457a2372ef3eda9fbe144f4cdf6bc1fd6c3 + react-native-ldk: a69156c3ff99495ef8fb518b4c873418608fc7c1 react-native-randombytes: 421f1c7d48c0af8dbcd471b0324393ebf8fe7846 react-native-tcp-socket: c1b7297619616b4c9caae6889bcb0aba78086989 React-perflogger: 8c79399b0500a30ee8152d0f9f11beae7fc36595 diff --git a/lib/package.json b/lib/package.json index b20db50e..13e41360 100644 --- a/lib/package.json +++ b/lib/package.json @@ -1,7 +1,7 @@ { "name": "@synonymdev/react-native-ldk", "title": "React Native LDK", - "version": "0.0.109", + "version": "0.0.110", "description": "React Native wrapper for LDK", "main": "./dist/index.js", "types": "./dist/index.d.ts", diff --git a/lib/src/lightning-manager.ts b/lib/src/lightning-manager.ts index 9dffc129..ec572a6e 100644 --- a/lib/src/lightning-manager.ts +++ b/lib/src/lightning-manager.ts @@ -474,20 +474,46 @@ class LightningManager { /** * Fetches current best block and sends to LDK to update both channelManager and chainMonitor. * Also watches transactions and outputs for confirmed and unconfirmed transactions and updates LDK. + * @param {number} [timeout] Timeout to set for each async function in this method. Potential overall timeout may be greater. + * @param {boolean} [attemptRestart] Will attempt to restart LDK if an error or timeout occurs. * @returns {Promise>} */ - async syncLdk(): Promise> { + async syncLdk({ + timeout = 15000, + attemptRestart = true, + }: { + timeout?: number; + attemptRestart?: boolean; + } = {}): Promise> { + // Check that the getBestBlock method has been provided. if (!this.getBestBlock) { return err('No getBestBlock method provided.'); } - const bestBlock = await this.getBestBlock(); - const height = bestBlock?.height; + const bestBlock = await promiseTimeout( + timeout, + this.getBestBlock(), + ); + if (!bestBlock?.height) { + if (attemptRestart) { + return await this.attemptRestart(); + } else { + return err('Unable to get best block in syncLdk method.'); + } + } + const height = bestBlock.height; //Don't update unnecessarily if (this.currentBlock.hash !== bestBlock?.hash) { - const syncToTip = await ldk.syncToTip(bestBlock); + const syncToTip = await promiseTimeout>( + timeout, + ldk.syncToTip(bestBlock), + ); if (syncToTip.isErr()) { - return syncToTip; + if (attemptRestart) { + return await this.attemptRestart(); + } else { + return syncToTip; + } } this.currentBlock = bestBlock; @@ -496,29 +522,76 @@ class LightningManager { let channels: TChannel[] = []; if (this.watchTxs.length > 0) { // Get fresh array of channels. - const listChannelsResponse = await ldk.listChannels(); + const listChannelsResponse = await promiseTimeout>( + timeout, + ldk.listChannels(), + ); if (listChannelsResponse.isOk()) { channels = listChannelsResponse.value; } } // Iterate over watch transactions/outputs and set whether they are confirmed or unconfirmed. - await this.checkWatchTxs(this.watchTxs, channels, bestBlock); - await this.checkWatchOutputs(this.watchOutputs); - await this.checkUnconfirmedTransactions(); + const r1 = await promiseTimeout>( + timeout, + this.checkWatchTxs(this.watchTxs, channels, bestBlock), + ); + if (r1.isErr()) { + if (attemptRestart) { + return await this.attemptRestart(); + } else { + return r1; + } + } + const r2 = await promiseTimeout>( + timeout, + this.checkWatchOutputs(this.watchOutputs), + ); + if (r2.isErr()) { + if (attemptRestart) { + return await this.attemptRestart(); + } else { + return r2; + } + } + const r3 = await promiseTimeout>( + timeout, + this.checkUnconfirmedTransactions(), + ); + if (r3.isErr()) { + if (attemptRestart) { + return await this.attemptRestart(); + } else { + return r3; + } + } return ok(`Synced to block ${height}`); } + /** + * Attempts to Restart and Sync LDK + * @param {number} [timeout] + * @returns {Promise>} + */ + attemptRestart = async (timeout = 15000): Promise> => { + const r = await promiseTimeout>(timeout, ldk.restart()); + if (r.isErr()) { + return r; + } + return await this.syncLdk({ + attemptRestart: false, + }); + }; + checkWatchTxs = async ( watchTxs: TRegisterTxEvent[], channels: TChannel[], bestBlock: THeader, - ): Promise => { + ): Promise> => { const height = bestBlock?.height; if (!height) { - console.log('No height provided'); - return; + return err('No height provided'); } await Promise.all( watchTxs.map(async (watchTxData) => { @@ -533,7 +606,7 @@ class LightningManager { //Watch TX was never confirmed so there's no need to unconfirm it. return; } - if (!txData.transaction) { + if (!txData?.transaction) { console.log( 'Unable to retrieve transaction data from the getTransactionData method.', ); @@ -564,11 +637,12 @@ class LightningManager { } }), ); + return ok('Watch transactions checked'); }; checkWatchOutputs = async ( watchOutputs: TRegisterOutputEvent[], - ): Promise => { + ): Promise> => { await Promise.all( watchOutputs.map(async ({ index, script_pubkey }) => { const transactions = await this.getScriptPubKeyHistory(script_pubkey); @@ -630,6 +704,7 @@ class LightningManager { ); }), ); + return ok('Watch outputs checked'); }; /** @@ -1127,7 +1202,7 @@ class LightningManager { } }; - checkUnconfirmedTransactions = async (): Promise => { + checkUnconfirmedTransactions = async (): Promise> => { let needsToSync = false; let newUnconfirmedTxs: TLdkUnconfirmedTransactions = []; await Promise.all( @@ -1171,6 +1246,7 @@ class LightningManager { if (needsToSync) { await this.syncLdk(); } + return ok('Unconfirmed transactions checked'); }; /**