mirror of
https://github.com/trezor/trezor-wallet
synced 2024-11-14 04:19:09 +00:00
Merge pull request #615 from trezor/fix/backend-reconnection
Fix/backend reconnection
This commit is contained in:
commit
9844d2cc21
@ -128,8 +128,8 @@
|
||||
]
|
||||
},
|
||||
"explorer": {
|
||||
"tx": "https://sisyfos.trezor.io/ripple-testnet-explorer/tx/",
|
||||
"address": "https://sisyfos.trezor.io/ripple-testnet-explorer/address/"
|
||||
"tx": "https://test.bithomp.com/explorer/",
|
||||
"address": "https://test.bithomp.com/explorer/"
|
||||
},
|
||||
"hasSignVerify": false
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
/* @flow */
|
||||
|
||||
import * as BLOCKCHAIN from 'actions/constants/blockchain';
|
||||
import * as DiscoveryActions from 'actions/DiscoveryActions';
|
||||
import * as EthereumBlockchainActions from 'actions/ethereum/BlockchainActions';
|
||||
import * as RippleBlockchainActions from 'actions/ripple/BlockchainActions';
|
||||
import { resolveAfter } from 'utils/promiseUtils';
|
||||
|
||||
import type { Dispatch, GetState, PromiseAction, BlockchainFeeLevel } from 'flowtype';
|
||||
import type { BlockchainBlock, BlockchainNotification, BlockchainError } from 'trezor-connect';
|
||||
@ -20,10 +20,6 @@ export type BlockchainAction =
|
||||
| {
|
||||
type: typeof BLOCKCHAIN.START_SUBSCRIBE,
|
||||
shortcut: string,
|
||||
}
|
||||
| {
|
||||
type: typeof BLOCKCHAIN.FAIL_SUBSCRIBE,
|
||||
shortcut: string,
|
||||
};
|
||||
|
||||
// Conditionally subscribe to blockchain backend
|
||||
@ -153,23 +149,13 @@ const autoReconnect = (shortcut: string): PromiseAction<void> => async (
|
||||
dispatch: Dispatch,
|
||||
getState: GetState
|
||||
): Promise<void> => {
|
||||
const MAX_ATTEMPTS = 4;
|
||||
let blockchain = getState().blockchain.find(b => b.shortcut === shortcut);
|
||||
// try to automatically reconnect and wait after each attemp (5s * #attempt) untill max number of attemps is reached
|
||||
for (let i = 0; i < MAX_ATTEMPTS; i++) {
|
||||
const waitTime = 5000 * (i + 1); /// 5s * #attempt
|
||||
if (!blockchain || blockchain.connected) {
|
||||
break;
|
||||
}
|
||||
if (!blockchain || blockchain.reconnectionAttempts >= 5) return;
|
||||
|
||||
blockchain = getState().blockchain.find(b => b.shortcut === shortcut);
|
||||
await resolveAfter(5000 * (blockchain.reconnectionAttempts + 1));
|
||||
|
||||
// reconnect with 7s timeout
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await dispatch(DiscoveryActions.reconnect(shortcut, 7000));
|
||||
blockchain = getState().blockchain.find(b => b.shortcut === shortcut);
|
||||
if (!blockchain || blockchain.connected || blockchain.connecting) return;
|
||||
|
||||
// wait before next try
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await new Promise(resolve => setTimeout(resolve, waitTime));
|
||||
}
|
||||
await dispatch(subscribe(shortcut));
|
||||
};
|
||||
|
@ -2,7 +2,6 @@
|
||||
import React from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import TrezorConnect, { UI } from 'trezor-connect';
|
||||
import * as BLOCKCHAIN_ACTION from 'actions/constants/blockchain';
|
||||
import * as DISCOVERY from 'actions/constants/discovery';
|
||||
import * as ACCOUNT from 'actions/constants/account';
|
||||
import * as NOTIFICATION from 'actions/constants/notification';
|
||||
@ -10,7 +9,6 @@ import * as NOTIFICATION from 'actions/constants/notification';
|
||||
import type {
|
||||
ThunkAction,
|
||||
AsyncAction,
|
||||
PromiseAction,
|
||||
PayloadAction,
|
||||
GetState,
|
||||
Dispatch,
|
||||
@ -331,30 +329,7 @@ const finish = (device: TrezorDevice, discoveryProcess: Discovery): AsyncAction
|
||||
});
|
||||
};
|
||||
|
||||
export const reconnect = (network: string, timeout: number = 30): PromiseAction<void> => async (
|
||||
dispatch: Dispatch
|
||||
): Promise<void> => {
|
||||
// Runs two promises.
|
||||
// First promise is a subscribe action which will never resolve in case of completely lost connection to the backend
|
||||
// That's why there is a second promise that rejects after the specified timeout.
|
||||
return Promise.race([
|
||||
dispatch(BlockchainActions.subscribe(network)),
|
||||
new Promise((_, reject) => setTimeout(() => reject(new Error('timeout')), timeout)),
|
||||
])
|
||||
.catch(() => {
|
||||
// catch error from first promises that rejects (most likely timeout)
|
||||
dispatch({
|
||||
type: BLOCKCHAIN_ACTION.FAIL_SUBSCRIBE,
|
||||
shortcut: network,
|
||||
});
|
||||
})
|
||||
.then(() => {
|
||||
// dispatch restore when subscribe promise resolves
|
||||
dispatch(restore());
|
||||
});
|
||||
};
|
||||
|
||||
// Called after DEVICE.CONNECT ('trezor-connect') or CONNECT.AUTH_DEVICE actions in WalletService
|
||||
// Called after DEVICE.CONNECT ('trezor-connect') or CONNECT.AUTH_DEVICE or BLOCKCHAIN.CONNECT actions in WalletService
|
||||
// OR after BlockchainSubscribe in this.reconnect
|
||||
export const restore = (): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
|
||||
// check if current url has "network" parameter
|
||||
|
@ -160,7 +160,9 @@ export const init = (): AsyncAction => async (
|
||||
if (buildUtils.isDev()) {
|
||||
// eslint-disable-next-line
|
||||
window.__TREZOR_CONNECT_SRC =
|
||||
typeof LOCAL === 'string' ? LOCAL : 'https://sisyfos.sldev.cz/connect/'; // eslint-disable-line no-underscore-dangle
|
||||
typeof LOCAL === 'string'
|
||||
? LOCAL
|
||||
: 'https://connect.corp.sldev.cz/fix/v7-ripple-lib-error/'; // eslint-disable-line no-underscore-dangle
|
||||
// window.__TREZOR_CONNECT_SRC = typeof LOCAL === 'string' ? LOCAL : 'https://localhost:8088/'; // eslint-disable-line no-underscore-dangle
|
||||
window.TrezorConnect = TrezorConnect;
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
/* @flow */
|
||||
|
||||
export const START_SUBSCRIBE: 'blockchain__start_subscribe' = 'blockchain__start_subscribe';
|
||||
export const FAIL_SUBSCRIBE: 'blockchain__fail_subscribe' = 'blockchain__fail_subscribe';
|
||||
export const READY: 'blockchain__ready' = 'blockchain__ready';
|
||||
export const UPDATE_FEE: 'blockchain__update_fee' = 'blockchain__update_fee';
|
||||
|
@ -161,31 +161,47 @@ export const onNotification = (
|
||||
});
|
||||
}
|
||||
|
||||
const updatedAccount = await TrezorConnect.rippleGetAccountInfo({
|
||||
account: {
|
||||
descriptor: account.descriptor,
|
||||
from: account.block,
|
||||
history: false,
|
||||
},
|
||||
coin: account.network,
|
||||
// In case of tx sent between two Trezor accounts there is a possibility that only 1 notification will be received
|
||||
// therefore we need to find target account and update data for it as well
|
||||
const accountsToUpdate = [account];
|
||||
const targetAddress =
|
||||
notification.type === 'send'
|
||||
? notification.outputs[0].addresses[0]
|
||||
: notification.inputs[0].addresses[0];
|
||||
|
||||
const targetAccount = getState().accounts.find(a => a.descriptor === targetAddress);
|
||||
if (targetAccount) {
|
||||
accountsToUpdate.push(targetAccount);
|
||||
}
|
||||
|
||||
accountsToUpdate.forEach(async a => {
|
||||
const response = await TrezorConnect.rippleGetAccountInfo({
|
||||
account: {
|
||||
descriptor: a.descriptor,
|
||||
from: a.block,
|
||||
history: false,
|
||||
},
|
||||
coin: a.network,
|
||||
});
|
||||
|
||||
if (response.success) {
|
||||
const updatedAccount = response.payload;
|
||||
const empty = updatedAccount.sequence <= 0 && updatedAccount.balance === '0';
|
||||
dispatch(
|
||||
AccountsActions.update({
|
||||
networkType: 'ripple',
|
||||
...a,
|
||||
balance: toDecimalAmount(updatedAccount.balance, network.decimals),
|
||||
availableBalance: toDecimalAmount(
|
||||
updatedAccount.availableBalance,
|
||||
network.decimals
|
||||
),
|
||||
block: updatedAccount.block,
|
||||
sequence: updatedAccount.sequence,
|
||||
reserve: toDecimalAmount(updatedAccount.reserve, network.decimals),
|
||||
empty,
|
||||
})
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
if (!updatedAccount.success) return;
|
||||
|
||||
const empty = updatedAccount.payload.sequence <= 0 && updatedAccount.payload.balance === '0';
|
||||
dispatch(
|
||||
AccountsActions.update({
|
||||
networkType: 'ripple',
|
||||
...account,
|
||||
balance: toDecimalAmount(updatedAccount.payload.balance, network.decimals),
|
||||
availableBalance: toDecimalAmount(
|
||||
updatedAccount.payload.availableBalance,
|
||||
network.decimals
|
||||
),
|
||||
block: updatedAccount.payload.block,
|
||||
sequence: updatedAccount.payload.sequence,
|
||||
reserve: '0',
|
||||
empty,
|
||||
})
|
||||
);
|
||||
};
|
||||
|
@ -14,17 +14,21 @@ export default (props: Props) => {
|
||||
if (notification.type === 'backend') {
|
||||
// special case: backend is down
|
||||
// TODO: this is a different component with "auto resolve" button
|
||||
const inProgress = blockchain && blockchain.connecting;
|
||||
const status = inProgress
|
||||
? l10nMessages.TR_RECONNECTING
|
||||
: l10nMessages.TR_CONNECT_TO_BACKEND;
|
||||
return (
|
||||
<Notification
|
||||
variant="error"
|
||||
title={notification.title}
|
||||
message={notification.message}
|
||||
isActionInProgress={blockchain && blockchain.connecting}
|
||||
isActionInProgress={inProgress}
|
||||
actions={[
|
||||
{
|
||||
label: props.intl.formatMessage(l10nMessages.TR_CONNECT_TO_BACKEND),
|
||||
label: props.intl.formatMessage(status),
|
||||
callback: async () => {
|
||||
await props.blockchainReconnect(network.shortcut);
|
||||
if (!inProgress) props.blockchainReconnect(network.shortcut);
|
||||
},
|
||||
},
|
||||
]}
|
||||
|
@ -7,6 +7,10 @@ const definedMessages: Messages = defineMessages({
|
||||
id: 'TR_CONNECT_TO_BACKEND',
|
||||
defaultMessage: 'Connect',
|
||||
},
|
||||
TR_RECONNECTING: {
|
||||
id: 'TR_RECONNECTING',
|
||||
defaultMessage: 'Reconnecting...',
|
||||
},
|
||||
});
|
||||
|
||||
export default definedMessages;
|
||||
|
@ -7,7 +7,7 @@ import type { IntlShape } from 'react-intl';
|
||||
|
||||
import type { State, Dispatch } from 'flowtype';
|
||||
|
||||
import { reconnect } from 'actions/DiscoveryActions';
|
||||
import { subscribe } from 'actions/BlockchainActions';
|
||||
import * as NotificationActions from 'actions/NotificationActions';
|
||||
|
||||
import StaticNotifications from './components/Static';
|
||||
@ -29,7 +29,7 @@ export type StateProps = {|
|
||||
|
||||
export type DispatchProps = {|
|
||||
close: typeof NotificationActions.close,
|
||||
blockchainReconnect: typeof reconnect,
|
||||
blockchainReconnect: typeof subscribe,
|
||||
|};
|
||||
|
||||
export type Props = {| ...OwnProps, ...StateProps, ...DispatchProps |};
|
||||
@ -52,7 +52,7 @@ const mapStateToProps = (state: State): StateProps => ({
|
||||
|
||||
const mapDispatchToProps = (dispatch: Dispatch): DispatchProps => ({
|
||||
close: bindActionCreators(NotificationActions.close, dispatch),
|
||||
blockchainReconnect: bindActionCreators(reconnect, dispatch),
|
||||
blockchainReconnect: bindActionCreators(subscribe, dispatch),
|
||||
});
|
||||
|
||||
export default injectIntl<OwnProps>(
|
||||
|
@ -17,6 +17,7 @@ export type BlockchainNetwork = {
|
||||
feeLevels: Array<BlockchainFeeLevel>,
|
||||
connected: boolean,
|
||||
connecting: boolean,
|
||||
reconnectionAttempts: number,
|
||||
block: number,
|
||||
};
|
||||
|
||||
@ -41,30 +42,7 @@ const onStartSubscribe = (state: State, shortcut: string): State => {
|
||||
shortcut,
|
||||
connected: false,
|
||||
connecting: true,
|
||||
block: 0,
|
||||
feeTimestamp: 0,
|
||||
feeLevels: [],
|
||||
},
|
||||
]);
|
||||
};
|
||||
|
||||
const onFailSubscribe = (state: State, shortcut: string): State => {
|
||||
const network = state.find(b => b.shortcut === shortcut);
|
||||
if (network) {
|
||||
const others = state.filter(b => b !== network);
|
||||
return others.concat([
|
||||
{
|
||||
...network,
|
||||
connecting: false,
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
return state.concat([
|
||||
{
|
||||
shortcut,
|
||||
connected: false,
|
||||
connecting: false,
|
||||
reconnectionAttempts: 0,
|
||||
block: 0,
|
||||
feeTimestamp: 0,
|
||||
feeLevels: [],
|
||||
@ -84,6 +62,7 @@ const onConnect = (state: State, action: BlockchainConnect): State => {
|
||||
block: info.block,
|
||||
connected: true,
|
||||
connecting: false,
|
||||
reconnectionAttempts: 0,
|
||||
},
|
||||
]);
|
||||
}
|
||||
@ -93,6 +72,7 @@ const onConnect = (state: State, action: BlockchainConnect): State => {
|
||||
shortcut,
|
||||
connected: true,
|
||||
connecting: false,
|
||||
reconnectionAttempts: 0,
|
||||
block: info.block,
|
||||
feeTimestamp: 0,
|
||||
feeLevels: [],
|
||||
@ -110,6 +90,7 @@ const onError = (state: State, action: BlockchainError): State => {
|
||||
...network,
|
||||
connected: false,
|
||||
connecting: false,
|
||||
reconnectionAttempts: network.reconnectionAttempts + 1,
|
||||
},
|
||||
]);
|
||||
}
|
||||
@ -119,6 +100,7 @@ const onError = (state: State, action: BlockchainError): State => {
|
||||
shortcut,
|
||||
connected: false,
|
||||
connecting: false,
|
||||
reconnectionAttempts: 0,
|
||||
block: 0,
|
||||
feeTimestamp: 0,
|
||||
feeLevels: [],
|
||||
@ -160,8 +142,6 @@ export default (state: State = initialState, action: Action): State => {
|
||||
switch (action.type) {
|
||||
case BLOCKCHAIN_ACTION.START_SUBSCRIBE:
|
||||
return onStartSubscribe(state, action.shortcut);
|
||||
case BLOCKCHAIN_ACTION.FAIL_SUBSCRIBE:
|
||||
return onFailSubscribe(state, action.shortcut);
|
||||
case BLOCKCHAIN_EVENT.CONNECT:
|
||||
return onConnect(state, action);
|
||||
case BLOCKCHAIN_EVENT.ERROR:
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* @flow */
|
||||
import { DEVICE } from 'trezor-connect';
|
||||
import { DEVICE, BLOCKCHAIN } from 'trezor-connect';
|
||||
import { LOCATION_CHANGE } from 'connected-react-router';
|
||||
import * as WALLET from 'actions/constants/wallet';
|
||||
import * as CONNECT from 'actions/constants/TrezorConnect';
|
||||
@ -132,6 +132,12 @@ const WalletService: Middleware = (api: MiddlewareAPI) => (next: MiddlewareDispa
|
||||
api.dispatch(DiscoveryActions.stop());
|
||||
}
|
||||
|
||||
// try to restore discovery on BLOCKCHAIN.CONNECT event
|
||||
// edge case when backend throws error during discovery
|
||||
if (action.type === BLOCKCHAIN.CONNECT) {
|
||||
api.dispatch(DiscoveryActions.restore());
|
||||
}
|
||||
|
||||
return action;
|
||||
};
|
||||
|
||||
|
@ -489,14 +489,13 @@ const AccountSend = (props: Props) => {
|
||||
</AdvancedForm>
|
||||
)}
|
||||
|
||||
{props.selectedAccount.pending.length > 0 ||
|
||||
(account.imported && (
|
||||
<PendingTransactions
|
||||
pending={props.selectedAccount.pending}
|
||||
tokens={props.selectedAccount.tokens}
|
||||
network={network}
|
||||
/>
|
||||
))}
|
||||
{props.selectedAccount.pending.length > 0 && (
|
||||
<PendingTransactions
|
||||
pending={props.selectedAccount.pending}
|
||||
tokens={props.selectedAccount.tokens}
|
||||
network={network}
|
||||
/>
|
||||
)}
|
||||
</Content>
|
||||
);
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user