2018-02-20 09:30:36 +00:00
|
|
|
/* @flow */
|
2018-07-30 10:52:13 +00:00
|
|
|
|
2018-04-23 10:20:15 +00:00
|
|
|
import HDKey from 'hdkey';
|
|
|
|
|
2018-08-14 13:11:52 +00:00
|
|
|
import * as DISCOVERY from 'actions/constants/discovery';
|
|
|
|
import * as ACCOUNT from 'actions/constants/account';
|
|
|
|
import * as CONNECT from 'actions/constants/TrezorConnect';
|
|
|
|
import * as WALLET from 'actions/constants/wallet';
|
2018-02-20 09:30:36 +00:00
|
|
|
|
2018-08-14 12:56:47 +00:00
|
|
|
import type { Action, TrezorDevice } from 'flowtype';
|
2018-07-30 10:52:13 +00:00
|
|
|
import type {
|
2018-04-23 10:20:15 +00:00
|
|
|
DiscoveryStartAction,
|
|
|
|
DiscoveryWaitingAction,
|
2018-07-30 10:52:13 +00:00
|
|
|
DiscoveryCompleteAction,
|
2018-08-14 13:11:52 +00:00
|
|
|
} from 'actions/DiscoveryActions';
|
2018-04-23 10:20:15 +00:00
|
|
|
|
2018-09-12 11:25:32 +00:00
|
|
|
import type { Account } from './AccountsReducer';
|
2018-04-23 10:20:15 +00:00
|
|
|
|
2018-02-20 09:30:36 +00:00
|
|
|
export type Discovery = {
|
2019-03-04 12:33:02 +00:00
|
|
|
network: string,
|
|
|
|
basePath: Array<number>,
|
|
|
|
deviceState: string,
|
|
|
|
accountIndex: number,
|
|
|
|
interrupted: boolean,
|
|
|
|
completed: boolean,
|
|
|
|
waitingForDevice: boolean,
|
|
|
|
waitingForBlockchain: boolean,
|
|
|
|
fwNotSupported: boolean,
|
|
|
|
fwOutdated: boolean,
|
|
|
|
|
|
|
|
publicKey: string, // used in ethereum only
|
|
|
|
chainCode: string, // used in ethereum only
|
|
|
|
hdKey: HDKey, // used in ethereum only
|
2018-11-23 11:58:27 +00:00
|
|
|
};
|
2018-02-20 09:30:36 +00:00
|
|
|
|
2018-04-23 10:20:15 +00:00
|
|
|
export type State = Array<Discovery>;
|
|
|
|
const initialState: State = [];
|
2018-11-26 09:17:20 +00:00
|
|
|
const defaultDiscovery: Discovery = {
|
|
|
|
network: '',
|
|
|
|
deviceState: '',
|
|
|
|
basePath: [],
|
|
|
|
accountIndex: 0,
|
|
|
|
interrupted: false,
|
|
|
|
completed: false,
|
|
|
|
waitingForDevice: false,
|
|
|
|
waitingForBlockchain: false,
|
2018-11-26 18:36:11 +00:00
|
|
|
fwNotSupported: false,
|
|
|
|
fwOutdated: false,
|
2018-11-26 09:17:20 +00:00
|
|
|
|
|
|
|
publicKey: '',
|
|
|
|
chainCode: '',
|
|
|
|
hdKey: null,
|
|
|
|
};
|
2018-02-20 09:30:36 +00:00
|
|
|
|
2019-03-04 12:33:02 +00:00
|
|
|
const findIndex = (state: State, network: string, deviceState: string): number =>
|
|
|
|
state.findIndex(d => d.network === network && d.deviceState === deviceState);
|
2018-03-29 11:38:09 +00:00
|
|
|
|
2018-04-23 10:20:15 +00:00
|
|
|
const start = (state: State, action: DiscoveryStartAction): State => {
|
|
|
|
const deviceState: string = action.device.state || '0';
|
2018-02-20 09:30:36 +00:00
|
|
|
const instance: Discovery = {
|
2018-11-26 09:17:20 +00:00
|
|
|
...defaultDiscovery,
|
2018-11-23 11:58:27 +00:00
|
|
|
network: action.network.shortcut,
|
2018-04-23 10:20:15 +00:00
|
|
|
deviceState,
|
2018-07-30 10:52:13 +00:00
|
|
|
};
|
2018-02-20 09:30:36 +00:00
|
|
|
|
2018-11-26 09:17:20 +00:00
|
|
|
if (action.networkType === 'ethereum') {
|
|
|
|
const hdKey = new HDKey();
|
|
|
|
hdKey.publicKey = Buffer.from(action.publicKey, 'hex');
|
|
|
|
hdKey.chainCode = Buffer.from(action.chainCode, 'hex');
|
|
|
|
|
|
|
|
instance.hdKey = hdKey;
|
|
|
|
instance.publicKey = action.publicKey;
|
|
|
|
instance.chainCode = action.chainCode;
|
2018-12-03 19:22:49 +00:00
|
|
|
|
|
|
|
instance.basePath = action.basePath;
|
2018-11-26 09:17:20 +00:00
|
|
|
}
|
|
|
|
|
2018-07-30 10:52:13 +00:00
|
|
|
const newState: State = [...state];
|
2018-11-23 11:58:27 +00:00
|
|
|
const index: number = findIndex(state, action.network.shortcut, deviceState);
|
2018-02-20 09:30:36 +00:00
|
|
|
if (index >= 0) {
|
|
|
|
newState[index] = instance;
|
|
|
|
} else {
|
|
|
|
newState.push(instance);
|
|
|
|
}
|
|
|
|
return newState;
|
2018-07-30 10:52:13 +00:00
|
|
|
};
|
2018-02-20 09:30:36 +00:00
|
|
|
|
2018-04-23 10:20:15 +00:00
|
|
|
const complete = (state: State, action: DiscoveryCompleteAction): State => {
|
|
|
|
const index: number = findIndex(state, action.network, action.device.state || '0');
|
2018-07-30 10:52:13 +00:00
|
|
|
const newState: State = [...state];
|
2018-08-08 13:16:42 +00:00
|
|
|
newState[index] = { ...newState[index], completed: true };
|
2018-02-20 09:30:36 +00:00
|
|
|
return newState;
|
2018-07-30 10:52:13 +00:00
|
|
|
};
|
2018-02-20 09:30:36 +00:00
|
|
|
|
2018-09-12 11:25:32 +00:00
|
|
|
const accountCreate = (state: State, account: Account): State => {
|
|
|
|
const index: number = findIndex(state, account.network, account.deviceState);
|
2018-07-30 10:52:13 +00:00
|
|
|
const newState: State = [...state];
|
2019-04-08 16:30:11 +00:00
|
|
|
// do not increment index when adding imported account
|
|
|
|
// imported accounts should not interfere with the index used in discovery proccess.
|
|
|
|
if (!account.imported) {
|
|
|
|
newState[index].accountIndex++;
|
|
|
|
}
|
2018-02-20 09:30:36 +00:00
|
|
|
return newState;
|
2018-07-30 10:52:13 +00:00
|
|
|
};
|
2018-02-20 09:30:36 +00:00
|
|
|
|
2019-03-04 12:33:02 +00:00
|
|
|
const forgetDiscovery = (state: State, device: TrezorDevice): State =>
|
|
|
|
state.filter(d => d.deviceState !== device.state);
|
2018-02-20 09:30:36 +00:00
|
|
|
|
2018-05-28 17:42:03 +00:00
|
|
|
const clear = (state: State, devices: Array<TrezorDevice>): State => {
|
2018-07-30 10:52:13 +00:00
|
|
|
let newState: State = [...state];
|
2019-03-04 12:33:02 +00:00
|
|
|
devices.forEach(d => {
|
2018-05-28 17:42:03 +00:00
|
|
|
newState = forgetDiscovery(newState, d);
|
|
|
|
});
|
|
|
|
return newState;
|
2018-07-30 10:52:13 +00:00
|
|
|
};
|
2018-05-28 17:42:03 +00:00
|
|
|
|
2018-10-04 08:49:12 +00:00
|
|
|
const stop = (state: State, device: TrezorDevice): State => {
|
|
|
|
const affectedProcesses = state.filter(d => d.deviceState === device.state && !d.completed);
|
|
|
|
const otherProcesses = state.filter(d => affectedProcesses.indexOf(d) === -1);
|
|
|
|
const changedProcesses = affectedProcesses.map(d => ({
|
|
|
|
...d,
|
|
|
|
interrupted: true,
|
|
|
|
waitingForDevice: false,
|
|
|
|
waitingForBlockchain: false,
|
|
|
|
}));
|
|
|
|
|
|
|
|
return otherProcesses.concat(changedProcesses);
|
2018-07-30 10:52:13 +00:00
|
|
|
};
|
2018-02-20 09:30:36 +00:00
|
|
|
|
2018-05-07 11:25:46 +00:00
|
|
|
const waitingForDevice = (state: State, action: DiscoveryWaitingAction): State => {
|
2018-04-23 10:20:15 +00:00
|
|
|
const deviceState: string = action.device.state || '0';
|
2018-02-20 09:30:36 +00:00
|
|
|
const instance: Discovery = {
|
2018-11-26 09:17:20 +00:00
|
|
|
...defaultDiscovery,
|
2018-03-29 11:38:09 +00:00
|
|
|
network: action.network,
|
2018-04-23 10:20:15 +00:00
|
|
|
deviceState,
|
2018-05-07 11:25:46 +00:00
|
|
|
waitingForDevice: true,
|
2018-07-30 10:52:13 +00:00
|
|
|
};
|
2018-05-07 11:25:46 +00:00
|
|
|
|
|
|
|
const index: number = findIndex(state, action.network, deviceState);
|
2018-07-30 10:52:13 +00:00
|
|
|
const newState: State = [...state];
|
2018-05-07 11:25:46 +00:00
|
|
|
if (index >= 0) {
|
|
|
|
newState[index] = instance;
|
|
|
|
} else {
|
|
|
|
newState.push(instance);
|
|
|
|
}
|
|
|
|
|
|
|
|
return newState;
|
2018-07-30 10:52:13 +00:00
|
|
|
};
|
2018-05-07 11:25:46 +00:00
|
|
|
|
2018-09-13 12:40:41 +00:00
|
|
|
const waitingForBlockchain = (state: State, action: DiscoveryWaitingAction): State => {
|
2018-05-07 11:25:46 +00:00
|
|
|
const deviceState: string = action.device.state || '0';
|
|
|
|
const instance: Discovery = {
|
2018-11-26 09:17:20 +00:00
|
|
|
...defaultDiscovery,
|
2018-05-07 11:25:46 +00:00
|
|
|
network: action.network,
|
|
|
|
deviceState,
|
2018-09-13 12:40:41 +00:00
|
|
|
waitingForBlockchain: true,
|
2018-07-30 10:52:13 +00:00
|
|
|
};
|
2018-02-20 09:30:36 +00:00
|
|
|
|
2018-04-23 10:20:15 +00:00
|
|
|
const index: number = findIndex(state, action.network, deviceState);
|
2018-07-30 10:52:13 +00:00
|
|
|
const newState: State = [...state];
|
2018-02-20 09:30:36 +00:00
|
|
|
if (index >= 0) {
|
|
|
|
newState[index] = instance;
|
|
|
|
} else {
|
|
|
|
newState.push(instance);
|
|
|
|
}
|
|
|
|
|
|
|
|
return newState;
|
2018-07-30 10:52:13 +00:00
|
|
|
};
|
2018-02-20 09:30:36 +00:00
|
|
|
|
2018-11-26 14:20:30 +00:00
|
|
|
const notSupported = (state: State, action: DiscoveryWaitingAction): State => {
|
2019-03-04 12:33:02 +00:00
|
|
|
const affectedProcesses = state.filter(
|
|
|
|
d => d.deviceState === action.device.state && d.network === action.network
|
|
|
|
);
|
2018-11-26 18:36:11 +00:00
|
|
|
const otherProcesses = state.filter(d => affectedProcesses.indexOf(d) === -1);
|
2018-11-26 14:20:30 +00:00
|
|
|
|
2018-11-26 18:36:11 +00:00
|
|
|
const changedProcesses = affectedProcesses.map(d => ({
|
|
|
|
...d,
|
|
|
|
fwOutdated: action.type === DISCOVERY.FIRMWARE_OUTDATED,
|
|
|
|
fwNotSupported: action.type === DISCOVERY.FIRMWARE_NOT_SUPPORTED,
|
|
|
|
}));
|
2018-11-26 14:20:30 +00:00
|
|
|
|
2018-11-26 18:36:11 +00:00
|
|
|
return otherProcesses.concat(changedProcesses);
|
2018-11-26 14:20:30 +00:00
|
|
|
};
|
|
|
|
|
2018-04-23 10:20:15 +00:00
|
|
|
export default function discovery(state: State = initialState, action: Action): State {
|
2018-02-20 09:30:36 +00:00
|
|
|
switch (action.type) {
|
2018-07-30 10:52:13 +00:00
|
|
|
case DISCOVERY.START:
|
2018-02-20 09:30:36 +00:00
|
|
|
return start(state, action);
|
2018-07-30 10:52:13 +00:00
|
|
|
case ACCOUNT.CREATE:
|
2018-09-12 11:25:32 +00:00
|
|
|
return accountCreate(state, action.payload);
|
2018-07-30 10:52:13 +00:00
|
|
|
case DISCOVERY.STOP:
|
2018-10-04 08:49:12 +00:00
|
|
|
return stop(state, action.device);
|
2018-07-30 10:52:13 +00:00
|
|
|
case DISCOVERY.COMPLETE:
|
2018-02-20 09:30:36 +00:00
|
|
|
return complete(state, action);
|
2018-07-30 10:52:13 +00:00
|
|
|
case DISCOVERY.WAITING_FOR_DEVICE:
|
2018-05-07 11:25:46 +00:00
|
|
|
return waitingForDevice(state, action);
|
2018-09-13 12:40:41 +00:00
|
|
|
case DISCOVERY.WAITING_FOR_BLOCKCHAIN:
|
|
|
|
return waitingForBlockchain(state, action);
|
2018-11-26 18:36:11 +00:00
|
|
|
case DISCOVERY.FIRMWARE_NOT_SUPPORTED:
|
|
|
|
return notSupported(state, action);
|
|
|
|
case DISCOVERY.FIRMWARE_OUTDATED:
|
2018-11-26 14:20:30 +00:00
|
|
|
return notSupported(state, action);
|
2018-07-30 10:52:13 +00:00
|
|
|
case DISCOVERY.FROM_STORAGE:
|
2019-03-04 12:33:02 +00:00
|
|
|
return action.payload.map(d => {
|
2018-12-19 17:16:28 +00:00
|
|
|
if (d.publicKey.length < 1) return d;
|
|
|
|
// recreate ethereum discovery HDKey
|
|
|
|
// deprecated: will be removed after switching to blockbook
|
2018-07-30 10:52:13 +00:00
|
|
|
const hdKey: HDKey = new HDKey();
|
2018-10-04 08:49:12 +00:00
|
|
|
hdKey.publicKey = Buffer.from(d.publicKey, 'hex');
|
|
|
|
hdKey.chainCode = Buffer.from(d.chainCode, 'hex');
|
2018-02-20 09:30:36 +00:00
|
|
|
return {
|
|
|
|
...d,
|
2018-05-07 14:02:10 +00:00
|
|
|
hdKey,
|
2018-02-20 09:30:36 +00:00
|
|
|
interrupted: false,
|
2018-05-07 11:25:46 +00:00
|
|
|
waitingForDevice: false,
|
2018-09-13 12:40:41 +00:00
|
|
|
waitingForBlockchain: false,
|
2018-07-30 10:52:13 +00:00
|
|
|
};
|
|
|
|
});
|
|
|
|
case CONNECT.FORGET:
|
|
|
|
case CONNECT.FORGET_SINGLE:
|
2018-10-08 09:59:10 +00:00
|
|
|
case CONNECT.FORGET_SILENT:
|
|
|
|
case CONNECT.RECEIVE_WALLET_TYPE:
|
2018-04-23 10:20:15 +00:00
|
|
|
return forgetDiscovery(state, action.device);
|
2018-07-30 10:52:13 +00:00
|
|
|
case WALLET.CLEAR_UNAVAILABLE_DEVICE_DATA:
|
2018-05-28 17:42:03 +00:00
|
|
|
return clear(state, action.devices);
|
2018-02-20 09:30:36 +00:00
|
|
|
|
|
|
|
default:
|
|
|
|
return state;
|
|
|
|
}
|
2019-03-04 12:33:02 +00:00
|
|
|
}
|