From f5f2b6e897c31aadacd9ffead216c4524cbaf68b Mon Sep 17 00:00:00 2001 From: myxmaster Date: Fri, 20 Dec 2024 19:27:07 +0100 Subject: [PATCH 1/2] improve error messages for connection failures and failed LNDHub authentication --- locales/en.json | 2 ++ stores/SettingsStore.ts | 18 +++++++++++++----- stores/TransactionsStore.ts | 2 +- utils/ErrorUtils.test.ts | 20 ++++++++++++++++---- utils/ErrorUtils.ts | 20 ++++++++++++++------ 5 files changed, 46 insertions(+), 16 deletions(-) diff --git a/locales/en.json b/locales/en.json index 2561ce3ce..6c53d961d 100644 --- a/locales/en.json +++ b/locales/en.json @@ -1080,12 +1080,14 @@ "stores.SettingsStore.lndhubSuccess": "Successfully created LNDHub account. Record the username and password somewhere so you can restore your funds if something happens to your device. Then hit Save Wallet Config to continue.", "stores.SettingsStore.lndhubError": "Error creating LNDHub account. Please check the host and try again.", "stores.SettingsStore.lndhubLoginError": "Failed to log in to LNDHub server", + "stores.SettingsStore.lndhubConnectError": "Unable to connect to LNDHub server. Please verify the host address is correct and the service is running.", "stores.SettingsStore.lncConnectError": "Failed to connect the LNC client to the proxy server", "stores.LSPStore.error": "LSP error", "stores.LSPStore.connectionError": "Could not connect to LSP. Please check your LSP settings or try again later.", "stores.LightningAddressStore.preimageNotFound": "Pre-image not found on your device. Did you recently change devices?", "error.connectionRefused": "Host unreachable. Try restarting your node or its Tor process.", "error.hostUnreachable": "Host unreachable. Try restarting your node or its Tor process.", + "error.nodeConnectError": "Unable to connect to node. Please verify the host and port are correct and the service is running.", "error.torBootstrap": "Error starting up Tor on your phone. Try restarting Zeus. If the problem persists consider using the Orbot app to connect to Tor, or using an alternative connection method like Lightning Node Connect or Tailscale.", "error.sendingPayment": "Error sending payment", "error.failureReasonTimeout": "There are more routes to try, but the payment timeout was exceeded.", diff --git a/stores/SettingsStore.ts b/stores/SettingsStore.ts index e66d23ba9..f1dfb5642 100644 --- a/stores/SettingsStore.ts +++ b/stores/SettingsStore.ts @@ -1767,13 +1767,21 @@ export default class SettingsStore { this.refreshToken = data.refresh_token; resolve(data); }) - .catch(() => { - // handle error + .catch((error: any) => { this.loading = false; this.error = true; - this.errorMsg = localeString( - 'stores.SettingsStore.lndhubLoginError' - ); + if ( + typeof error.message === 'string' && + error.message.includes('"bad auth"') + ) { + this.errorMsg = localeString( + 'stores.SettingsStore.lndhubLoginError' + ); + } else { + this.errorMsg = localeString( + 'stores.SettingsStore.lndhubConnectError' + ); + } resolve(); }); }); diff --git a/stores/TransactionsStore.ts b/stores/TransactionsStore.ts index 5072dafc0..1cac11e5b 100644 --- a/stores/TransactionsStore.ts +++ b/stores/TransactionsStore.ts @@ -566,7 +566,7 @@ export default class TransactionsStore { const isKeysend = result?.htlcs?.[0]?.route?.hops?.[0]?.custom_records?.[ keySendPreimageType - ]; + ] != null; // TODO add message for in-flight transactions if ( diff --git a/utils/ErrorUtils.test.ts b/utils/ErrorUtils.test.ts index f669454a7..7c8a97b3e 100644 --- a/utils/ErrorUtils.test.ts +++ b/utils/ErrorUtils.test.ts @@ -1,10 +1,7 @@ import { errorToUserFriendly } from './ErrorUtils'; jest.mock('./LocaleUtils', () => ({ - localeString: (key: string) => { - const EN = require('../locales/en.json'); - return EN[key]; - } + localeString: (key: string) => require('../locales/en.json')[key] })); describe('ErrorUtils', () => { @@ -98,6 +95,21 @@ describe('ErrorUtils', () => { ); }); + it('Handles partial error message matches', () => { + expect( + errorToUserFriendly( + Object.assign(new Error(), { + message: + 'Error: Failed to connect to /can-be-any-host:8082', + name: 'test' + }), + false + ) + ).toEqual( + 'Unable to connect to node. Please verify the host and port are correct and the service is running.' + ); + }); + it('Returns normal error message for unhandled errorContext', () => { expect( errorToUserFriendly( diff --git a/utils/ErrorUtils.ts b/utils/ErrorUtils.ts index a9982216f..de44e0f6d 100644 --- a/utils/ErrorUtils.ts +++ b/utils/ErrorUtils.ts @@ -5,6 +5,8 @@ const userFriendlyErrors: any = { 'error.torBootstrap', 'Error: called `Result::unwrap()` on an `Err` value: BootStrapError("Timeout waiting for boostrap")': 'error.torBootstrap', + 'Error: Failed to connect to': 'error.nodeConnectError', + 'Error: Unable to resolve host': 'error.nodeConnectError', FAILURE_REASON_TIMEOUT: 'error.failureReasonTimeout', FAILURE_REASON_NO_ROUTE: 'error.failureReasonNoRoute', FAILURE_REASON_ERROR: 'error.failureReasonError', @@ -48,13 +50,19 @@ const errorToUserFriendly = ( errorMsg = errorMsg.charAt(0).toUpperCase() + errorMsg.slice(1); } + const matchingPattern = Object.keys(userFriendlyErrors).find((pattern) => + errorMsg.includes(pattern) + ); + + let localeKey = matchingPattern + ? userFriendlyErrors[matchingPattern] + : null; + if (localize) { const localeString = require('./LocaleUtils').localeString; - let baseError = - localeString(userFriendlyErrors[errorMsg])?.replace( - 'Zeus', - 'ZEUS' - ) || errorMsg; + let baseError = localeKey + ? localeString(localeKey)?.replace('Zeus', 'ZEUS') + : errorMsg; if ( errorContext?.includes('Keysend') && @@ -69,7 +77,7 @@ const errorToUserFriendly = ( return baseError; } else { const EN = require('../locales/en.json'); - return EN[userFriendlyErrors[errorMsg]] || errorMsg; + return localeKey ? EN[localeKey] : errorMsg; } }; From baf2718fa0370e805a247f7ce0d630c0ea73f1c7 Mon Sep 17 00:00:00 2001 From: myxmaster Date: Mon, 23 Dec 2024 19:44:42 +0100 Subject: [PATCH 2/2] remove obsolete parameter localize --- stores/TransactionsStore.ts | 1 - utils/ErrorUtils.test.ts | 39 +++++++++++++------------------------ utils/ErrorUtils.ts | 37 +++++++++++++---------------------- 3 files changed, 27 insertions(+), 50 deletions(-) diff --git a/stores/TransactionsStore.ts b/stores/TransactionsStore.ts index 1cac11e5b..cbc58066b 100644 --- a/stores/TransactionsStore.ts +++ b/stores/TransactionsStore.ts @@ -590,7 +590,6 @@ export default class TransactionsStore { ) : errorToUserFriendly( result.failure_reason, - true, isKeysend ? ['Keysend'] : undefined )) || errorToUserFriendly(result.payment_error); } diff --git a/utils/ErrorUtils.test.ts b/utils/ErrorUtils.test.ts index 7c8a97b3e..abbe0bdd6 100644 --- a/utils/ErrorUtils.test.ts +++ b/utils/ErrorUtils.test.ts @@ -16,8 +16,7 @@ describe('ErrorUtils', () => { "details": [] }`, name: 'test' - }), - false + }) ) ).toEqual('transaction output is dust'); expect( @@ -29,8 +28,7 @@ describe('ErrorUtils', () => { "details": [] }`, name: 'test' - }), - false + }) ) ).toEqual( 'proto: (line 1:126): invalid value for uint64 type: 0.1' @@ -47,8 +45,7 @@ describe('ErrorUtils', () => { } `, name: 'test' - }), - false + }) ) ).toEqual('invoice is already paid'); expect( @@ -63,8 +60,7 @@ describe('ErrorUtils', () => { } `, name: 'test' - }), - false + }) ) ).toEqual( 'Host unreachable. Try restarting your node or its Tor process.' @@ -75,11 +71,10 @@ describe('ErrorUtils', () => { message: 'Error: called `Result::unwrap()` on an `Err` value: BootStrapError("Timeout waiting for bootstrap")', name: 'test' - }), - false + }) ) ).toEqual( - 'Error starting up Tor on your phone. Try restarting Zeus. If the problem persists consider using the Orbot app to connect to Tor, or using an alternative connection method like Lightning Node Connect or Tailscale.' + 'Error starting up Tor on your phone. Try restarting ZEUS. If the problem persists consider using the Orbot app to connect to Tor, or using an alternative connection method like Lightning Node Connect or Tailscale.' ); expect( errorToUserFriendly( @@ -87,11 +82,10 @@ describe('ErrorUtils', () => { message: 'Error: called `Result::unwrap()` on an `Err` value: BootStrapError("Timeout waiting for boostrap")', name: 'test' - }), - false + }) ) ).toEqual( - 'Error starting up Tor on your phone. Try restarting Zeus. If the problem persists consider using the Orbot app to connect to Tor, or using an alternative connection method like Lightning Node Connect or Tailscale.' + 'Error starting up Tor on your phone. Try restarting ZEUS. If the problem persists consider using the Orbot app to connect to Tor, or using an alternative connection method like Lightning Node Connect or Tailscale.' ); }); @@ -102,8 +96,7 @@ describe('ErrorUtils', () => { message: 'Error: Failed to connect to /can-be-any-host:8082', name: 'test' - }), - false + }) ) ).toEqual( 'Unable to connect to node. Please verify the host and port are correct and the service is running.' @@ -117,7 +110,6 @@ describe('ErrorUtils', () => { message: 'FAILURE_REASON_INCORRECT_PAYMENT_DETAILS', name: 'test' }), - true, ['UnhandledContext'] ) ).toEqual( @@ -132,7 +124,6 @@ describe('ErrorUtils', () => { message: 'FAILURE_REASON_INCORRECT_PAYMENT_DETAILS', name: 'test' }), - true, ['Keysend'] ) ).toEqual( @@ -146,23 +137,21 @@ describe('ErrorUtils', () => { Object.assign(new Error(), { message: 'Random message', name: 'test' - }), - false + }) ) ).toEqual('Random message'); }); it('Return string if error is sent as a string', () => { - expect( - errorToUserFriendly(new Error('Payment timed out'), false) - ).toEqual('Payment timed out'); + expect(errorToUserFriendly(new Error('Payment timed out'))).toEqual( + 'Payment timed out' + ); }); it('Handles PascalCased LSP error messages', () => { expect( errorToUserFriendly( - new Error('ChannelExpiryBlocksTooHighInCreateOrderRequest'), - false + new Error('ChannelExpiryBlocksTooHighInCreateOrderRequest') ) ).toEqual('Channel expiry blocks too high in create order request'); }); diff --git a/utils/ErrorUtils.ts b/utils/ErrorUtils.ts index de44e0f6d..8935b1cea 100644 --- a/utils/ErrorUtils.ts +++ b/utils/ErrorUtils.ts @@ -19,11 +19,7 @@ const userFriendlyErrors: any = { const pascalCase = /^[A-Z](([a-z0-9]+[A-Z]?)*)$/; -const errorToUserFriendly = ( - error: Error, - localize = true, - errorContext?: string[] -) => { +const errorToUserFriendly = (error: Error, errorContext?: string[]) => { let errorMessage: string = error?.message; let errorObject: any; @@ -58,27 +54,20 @@ const errorToUserFriendly = ( ? userFriendlyErrors[matchingPattern] : null; - if (localize) { - const localeString = require('./LocaleUtils').localeString; - let baseError = localeKey - ? localeString(localeKey)?.replace('Zeus', 'ZEUS') - : errorMsg; + const localeString = require('./LocaleUtils').localeString; + let baseError = localeKey + ? localeString(localeKey)?.replace('Zeus', 'ZEUS') + : errorMsg; - if ( - errorContext?.includes('Keysend') && - errorMsg === 'FAILURE_REASON_INCORRECT_PAYMENT_DETAILS' - ) { - baseError += - ' ' + - localeString( - 'error.failureReasonIncorrectPaymentDetailsKeysend' - ); - } - return baseError; - } else { - const EN = require('../locales/en.json'); - return localeKey ? EN[localeKey] : errorMsg; + if ( + errorContext?.includes('Keysend') && + errorMsg === 'FAILURE_REASON_INCORRECT_PAYMENT_DETAILS' + ) { + baseError += + ' ' + + localeString('error.failureReasonIncorrectPaymentDetailsKeysend'); } + return baseError; }; export { errorToUserFriendly };