Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Connection error handling improvements #2654

Merged
merged 2 commits into from
Dec 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -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.",
Expand Down
18 changes: 13 additions & 5 deletions stores/SettingsStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
});
});
Expand Down
3 changes: 1 addition & 2 deletions stores/TransactionsStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand All @@ -590,7 +590,6 @@ export default class TransactionsStore {
)
: errorToUserFriendly(
result.failure_reason,
true,
isKeysend ? ['Keysend'] : undefined
)) || errorToUserFriendly(result.payment_error);
}
Expand Down
55 changes: 28 additions & 27 deletions utils/ErrorUtils.test.ts
Original file line number Diff line number Diff line change
@@ -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', () => {
Expand All @@ -19,8 +16,7 @@ describe('ErrorUtils', () => {
"details": []
}`,
name: 'test'
}),
false
})
)
).toEqual('transaction output is dust');
expect(
Expand All @@ -32,8 +28,7 @@ describe('ErrorUtils', () => {
"details": []
}`,
name: 'test'
}),
false
})
)
).toEqual(
'proto: (line 1:126): invalid value for uint64 type: 0.1'
Expand All @@ -50,8 +45,7 @@ describe('ErrorUtils', () => {
}
`,
name: 'test'
}),
false
})
)
).toEqual('invoice is already paid');
expect(
Expand All @@ -66,8 +60,7 @@ describe('ErrorUtils', () => {
}
`,
name: 'test'
}),
false
})
)
).toEqual(
'Host unreachable. Try restarting your node or its Tor process.'
Expand All @@ -78,23 +71,35 @@ 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(
Object.assign(new Error(), {
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.'
);
});

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'
})
)
).toEqual(
'Unable to connect to node. Please verify the host and port are correct and the service is running.'
);
});

Expand All @@ -105,7 +110,6 @@ describe('ErrorUtils', () => {
message: 'FAILURE_REASON_INCORRECT_PAYMENT_DETAILS',
name: 'test'
}),
true,
['UnhandledContext']
)
).toEqual(
Expand All @@ -120,7 +124,6 @@ describe('ErrorUtils', () => {
message: 'FAILURE_REASON_INCORRECT_PAYMENT_DETAILS',
name: 'test'
}),
true,
['Keysend']
)
).toEqual(
Expand All @@ -134,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');
});
Expand Down
49 changes: 23 additions & 26 deletions utils/ErrorUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand All @@ -17,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;

Expand All @@ -48,29 +46,28 @@ const errorToUserFriendly = (
errorMsg = errorMsg.charAt(0).toUpperCase() + errorMsg.slice(1);
}

if (localize) {
const localeString = require('./LocaleUtils').localeString;
let baseError =
localeString(userFriendlyErrors[errorMsg])?.replace(
'Zeus',
'ZEUS'
) || errorMsg;
const matchingPattern = Object.keys(userFriendlyErrors).find((pattern) =>
errorMsg.includes(pattern)
);

if (
errorContext?.includes('Keysend') &&
errorMsg === 'FAILURE_REASON_INCORRECT_PAYMENT_DETAILS'
) {
baseError +=
' ' +
localeString(
'error.failureReasonIncorrectPaymentDetailsKeysend'
);
}
return baseError;
} else {
const EN = require('../locales/en.json');
return EN[userFriendlyErrors[errorMsg]] || errorMsg;
let localeKey = matchingPattern
? userFriendlyErrors[matchingPattern]
: null;

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;
};

export { errorToUserFriendly };
Loading