From c197f7f7b67f2965b4b5a051225f010630e85e24 Mon Sep 17 00:00:00 2001 From: Szymon Lesisz Date: Wed, 8 Aug 2018 11:42:12 +0200 Subject: [PATCH] flowtype fixes corresponding changes in trezor-connect --- .flowconfig | 3 - src/flowtype/index.js | 41 ++++-- src/js/actions/DiscoveryActions.js | 1 - src/js/actions/SendFormActions.js | 17 +-- src/js/actions/TrezorConnectActions.js | 14 +-- src/js/actions/WalletActions.js | 3 +- src/js/components/modal/DuplicateDevice.js | 3 +- .../wallet/aside/DeviceSelection.js | 23 ++-- src/js/reducers/DevicesReducer.js | 118 ++++++++++++------ src/js/reducers/ModalReducer.js | 2 +- src/js/reducers/utils/index.js | 2 +- 11 files changed, 137 insertions(+), 90 deletions(-) diff --git a/.flowconfig b/.flowconfig index ab10939a..59ad41d9 100644 --- a/.flowconfig +++ b/.flowconfig @@ -1,5 +1,4 @@ [include] -.*/node_modules/trezor-connect [ignore] .*/node_modules/rc-util/.* @@ -21,8 +20,6 @@ ./src/flowtype/npm/web3.js ./src/flowtype/css.js -./node_modules/trezor-connect/flowtype/ - [options] diff --git a/src/flowtype/index.js b/src/flowtype/index.js index a66b4135..74d33214 100644 --- a/src/flowtype/index.js +++ b/src/flowtype/index.js @@ -1,6 +1,5 @@ /* @flow */ - import type { Store as ReduxStore, ReduxDispatch, @@ -35,6 +34,8 @@ import type { FiatRateAction } from '~/js/services/CoinmarketcapService'; // thi import type { Device, Features, + DeviceStatus, + DeviceFirmwareStatus, DeviceMessageType, TransportMessageType, UiMessageType, @@ -42,22 +43,42 @@ import type { import type { RouterAction, LocationState } from 'react-router-redux'; -export type TrezorDevice = { +export type AcquiredDevice = $Exact<{ + +type: 'acquired', + path: string, + +label: string, + +features: Features, + +firmware: DeviceFirmwareStatus, + status: DeviceStatus, + state: ?string, + remember: boolean; // device should be remembered connected: boolean; // device is connected available: boolean; // device cannot be used because of features.passphrase_protection is different then expected - path: string; - label: string; - state: ?string; instance?: number; instanceLabel: string; instanceName: ?string; - features?: Features; - unacquired?: boolean; - isUsedElsewhere?: boolean; - featuresNeedsReload?: boolean; ts: number; -} + +}>; + +export type UnknownDevice = $Exact<{ + +type: 'unacquired' | 'unreadable', + path: string, + +label: string, + +features: null, + state: ?string, + + remember: boolean; // device should be remembered + connected: boolean; // device is connected + available: boolean; // device cannot be used because of features.passphrase_protection is different then expected + instance?: number; + instanceLabel: string; + instanceName: ?string; + ts: number; +}> + +export type TrezorDevice = AcquiredDevice | UnknownDevice; export type RouterLocationState = LocationState; diff --git a/src/js/actions/DiscoveryActions.js b/src/js/actions/DiscoveryActions.js index 57c857c3..e7e76445 100644 --- a/src/js/actions/DiscoveryActions.js +++ b/src/js/actions/DiscoveryActions.js @@ -191,7 +191,6 @@ const discoverAccount = (device: TrezorDevice, discoveryProcess: Discovery): Asy const ethAddress: string = EthereumjsUtil.toChecksumAddress(publicAddress); const network = discoveryProcess.network; - // TODO: check if address was created before // verify address with TREZOR diff --git a/src/js/actions/SendFormActions.js b/src/js/actions/SendFormActions.js index e1e6a5e7..b1711e6e 100644 --- a/src/js/actions/SendFormActions.js +++ b/src/js/actions/SendFormActions.js @@ -836,14 +836,8 @@ export const onSend = (): AsyncAction => async (dispatch: Dispatch, getState: Ge state: selected.state, }, useEmptyPassphrase: !selected.instance, - path: txData.address_n, - nonce: strip(txData.nonce), - gasPrice: strip(txData.gasPrice), - gasLimit: strip(txData.gasLimit), - to: strip(txData.to), - value: strip(txData.value), - data: strip(txData.data), - chainId: txData.chainId, + path: address_n, + transaction: txData }); if (!signedTransaction || !signedTransaction.success) { @@ -860,10 +854,9 @@ export const onSend = (): AsyncAction => async (dispatch: Dispatch, getState: Ge return; } - txData.r = `0x${signedTransaction.payload.r}`; - txData.s = `0x${signedTransaction.payload.s}`; - txData.v = w3.toHex(signedTransaction.payload.v); - + txData.r = signedTransaction.payload.r; + txData.s = signedTransaction.payload.s; + txData.v = signedTransaction.payload.v; try { const tx = new EthereumjsTx(txData); diff --git a/src/js/actions/TrezorConnectActions.js b/src/js/actions/TrezorConnectActions.js index 72278921..f9f56709 100644 --- a/src/js/actions/TrezorConnectActions.js +++ b/src/js/actions/TrezorConnectActions.js @@ -16,7 +16,6 @@ import { resolveAfter } from '../utils/promiseUtils'; import type { Device, - ResponseMessage, DeviceMessage, UiMessage, TransportMessage, @@ -115,9 +114,10 @@ export const init = (): AsyncAction => async (dispatch: Dispatch, getState: GetS try { await TrezorConnect.init({ transportReconnect: true, - // connectSrc: 'https://localhost:8088/', - connectSrc: 'https://sisyfos.trezor.io/', - debug: false, + connectSrc: typeof window.LOCAL === 'string' ? window.LOCAL : 'https://connect.trezor.io/5/', + // connectSrc: 'https://connect.trezor.io/5/', + // connectSrc: 'https://sisyfos.trezor.io/', + debug: true, popup: false, webusb: true, pendingTransportEvent: (getState().devices.length < 1), @@ -156,7 +156,7 @@ export const postInit = (): ThunkAction => (dispatch: Dispatch, getState: GetSta } if (devices.length > 0) { - const unacquired: ?TrezorDevice = devices.find(d => d.unacquired); + const unacquired: ?TrezorDevice = devices.find(d => d.features); if (unacquired) { dispatch(onSelectDevice(unacquired)); } else { @@ -246,7 +246,7 @@ export const switchToFirstAvailableDevice = (): AsyncAction => async (dispatch: // 1. First Unacquired // 2. First connected // 3. Saved with latest timestamp - const unacquired = devices.find(d => d.unacquired); + const unacquired = devices.find(d => !d.features); if (unacquired) { dispatch(initConnectedDevice(unacquired)); } else { @@ -316,7 +316,7 @@ export const deviceDisconnect = (device: Device): AsyncAction => async (dispatch dispatch(DiscoveryActions.stop(selected)); } - const instances = getState().devices.filter(d => d.features && d.state && !d.remember && d.features.device_id === device.features.device_id); + const instances = getState().devices.filter(d => d.features && device.features && d.state && !d.remember && d.features.device_id === device.features.device_id); if (instances.length > 0) { dispatch({ type: CONNECT.REMEMBER_REQUEST, diff --git a/src/js/actions/WalletActions.js b/src/js/actions/WalletActions.js index 763bb8db..65b4a7d3 100644 --- a/src/js/actions/WalletActions.js +++ b/src/js/actions/WalletActions.js @@ -75,7 +75,8 @@ export const toggleDeviceDropdown = (opened: boolean): WalletAction => ({ export const clearUnavailableDevicesData = (prevState: State, device: Device): ThunkAction => (dispatch: Dispatch, getState: GetState): void => { if (!device.features) return; - const affectedDevices = prevState.devices.filter(d => d.features + const affectedDevices = prevState.devices.filter(d => + d.features && device.features && d.features.device_id === device.features.device_id && d.features.passphrase_protection !== device.features.passphrase_protection); diff --git a/src/js/components/modal/DuplicateDevice.js b/src/js/components/modal/DuplicateDevice.js index ba0a9ccb..c047b9fe 100644 --- a/src/js/components/modal/DuplicateDevice.js +++ b/src/js/components/modal/DuplicateDevice.js @@ -67,7 +67,8 @@ export default class DuplicateDevice extends Component { submit() { if (!this.props.modal.opened) return; - this.props.modalActions.onDuplicateDevice({ ...this.props.modal.device, instanceName: this.state.instanceName, instance: this.state.instance }); + const extended: Object = { instanceName: this.state.instanceName, instance: this.state.instance }; + this.props.modalActions.onDuplicateDevice({ ...this.props.modal.device, ...extended }); } render() { diff --git a/src/js/components/wallet/aside/DeviceSelection.js b/src/js/components/wallet/aside/DeviceSelection.js index 443e37dc..4b3e06c1 100644 --- a/src/js/components/wallet/aside/DeviceSelection.js +++ b/src/js/components/wallet/aside/DeviceSelection.js @@ -26,16 +26,17 @@ export const DeviceSelect = (props: Props) => { css += ' unavailable'; deviceStatus = 'Unavailable'; } else { - if (selected.unacquired) { + if (selected.type === 'acquired') { + if (selected.status === 'occupied') { + css += ' used-elsewhere'; + deviceStatus = 'Used in other window'; + } else if (selected.status === 'used') { + css += ' reload-features'; + } + } else if (selected.type === 'unacquired') { css += ' unacquired'; deviceStatus = 'Used in other window'; } - if (selected.isUsedElsewhere) { - css += ' used-elsewhere'; - deviceStatus = 'Used in other window'; - } else if (selected.featuresNeedsReload) { - css += ' reload-features'; - } } if (selected.features && selected.features.major_version > 1) { @@ -147,14 +148,12 @@ export class DeviceDropdown extends Component { if (selected.features) { const deviceMenuItems: Array = []; - if (selected.isUsedElsewhere) { - deviceMenuItems.push({ type: 'reload', label: 'Renew session' }); - } else if (selected.featuresNeedsReload) { + if (selected.status !== 'available') { deviceMenuItems.push({ type: 'reload', label: 'Renew session' }); } deviceMenuItems.push({ type: 'settings', label: 'Device settings' }); - if (selected.features && selected.features.passphrase_protection && selected.connected && selected.available) { + if (selected.features.passphrase_protection && selected.connected && selected.available) { deviceMenuItems.push({ type: 'clone', label: 'Clone device' }); } //if (selected.remember) { @@ -177,7 +176,7 @@ export class DeviceDropdown extends Component { let deviceStatus: string = 'Connected'; let css: string = 'device item'; - if (dev.unacquired || dev.isUsedElsewhere) { + if (!dev.features || (dev.features && dev.status === 'occupied')) { deviceStatus = 'Used in other window'; css += ' unacquired'; } else if (!dev.connected) { diff --git a/src/js/reducers/DevicesReducer.js b/src/js/reducers/DevicesReducer.js index 7bc64155..b62e9c86 100644 --- a/src/js/reducers/DevicesReducer.js +++ b/src/js/reducers/DevicesReducer.js @@ -29,10 +29,7 @@ const mergeDevices = (current: TrezorDevice, upcoming: Device | TrezorDevice): T } } - const dev: TrezorDevice = { - // ...current, - ...upcoming, - // make sure that instance specific variables will not be overridden + const extended = { connected: typeof upcoming.connected === 'boolean' ? upcoming.connected : current.connected, available: typeof upcoming.available === 'boolean' ? upcoming.available : current.available, remember: typeof upcoming.remember === 'boolean' ? upcoming.remember : current.remember, @@ -41,16 +38,26 @@ const mergeDevices = (current: TrezorDevice, upcoming: Device | TrezorDevice): T instanceName: typeof upcoming.instanceName === 'string' ? upcoming.instanceName : current.instanceName, state: current.state, ts: typeof upcoming.ts === 'number' ? upcoming.ts : current.ts, - }; - // corner-case: trying to merge unacquired device with acquired - // make sure that sensitive fields will not be changed and device will remain acquired - if (upcoming.unacquired && current.state) { - dev.unacquired = false; - dev.features = current.features; - dev.label = current.label; } - return dev; + if (upcoming.type === 'acquired') { + return { ...upcoming, ...extended }; + } else if (upcoming.type === 'unacquired' && current.features && current.state) { + // corner-case: trying to merge unacquired device with acquired + // make sure that sensitive fields will not be changed and device will remain acquired + return { + type: 'acquired', + path: upcoming.path, + ...current, + ...extended + } + } else { + return { + ...upcoming, + features: null, + ...extended + } + } }; const addDevice = (state: State, device: Device): State => { @@ -65,14 +72,15 @@ const addDevice = (state: State, device: Device): State => { } otherDevices = state.filter(d => affectedDevices.indexOf(d) === -1); } else { - affectedDevices = state.filter(d => d.features && d.features.device_id === device.features.device_id); + affectedDevices = state.filter(d => + d.features && + device.features && + d.features.device_id === device.features.device_id); const unacquiredDevices = state.filter(d => d.path.length > 0 && d.path === device.path); otherDevices = state.filter(d => affectedDevices.indexOf(d) < 0 && unacquiredDevices.indexOf(d) < 0); } - const newDevice: TrezorDevice = { - ...device, - acquiring: false, + const extended = { remember: false, connected: true, available: true, @@ -82,6 +90,17 @@ const addDevice = (state: State, device: Device): State => { instanceLabel: device.label, instanceName: null, ts: new Date().getTime(), + } + + + const newDevice: TrezorDevice = device.type === 'acquired' ? { + ...device, + // acquiring: false, + ...extended + } : { + ...device, + features: null, + ...extended }; if (affectedDevices.length > 0) { @@ -112,7 +131,12 @@ const addDevice = (state: State, device: Device): State => { // changedDevices.push(newDevice); // } - const changedDevices: Array = affectedDevices.filter(d => d.features && d.features.passphrase_protection === device.features.passphrase_protection).map(d => mergeDevices(d, { ...device, connected: true, available: true })); + const changedDevices: Array = affectedDevices.filter(d => + d.features && device.features && + d.features.passphrase_protection === device.features.passphrase_protection).map(d => { + const extended: Object = { connected: true, available: true } + return mergeDevices(d, { ...device, ...extended }) + }); if (changedDevices.length !== affectedDevices.length) { changedDevices.push(newDevice); } @@ -144,20 +168,21 @@ const duplicate = (state: State, device: TrezorDevice): State => { return newState; }; -const changeDevice = (state: State, device: Device): State => { +const changeDevice = (state: State, device: Device | TrezorDevice, extended: Object): State => { // change only acquired devices if (!device.features) return state; // find devices with the same device_id and passphrase_protection settings // or devices with the same path (TODO: should be that way?) - const affectedDevices: Array = state.filter(d => (d.features && d.features.device_id === device.features.device_id && d.features.passphrase_protection === device.features.passphrase_protection) + const affectedDevices: Array = state.filter(d => + (d.features && device.features && d.features.device_id === device.features.device_id && d.features.passphrase_protection === device.features.passphrase_protection) || (d.features && d.path.length > 0 && d.path === device.path)); const otherDevices: Array = state.filter(d => affectedDevices.indexOf(d) === -1); if (affectedDevices.length > 0) { // merge incoming device with State - const changedDevices = affectedDevices.map(d => mergeDevices(d, device)); + const changedDevices = affectedDevices.map(d => mergeDevices(d, { ...device, ...extended })); return otherDevices.concat(changedDevices); } @@ -177,15 +202,22 @@ const authDevice = (state: State, device: TrezorDevice, deviceState: string): St // Transform JSON form local storage into State -const devicesFromStorage = (devices: Array): State => devices.map((d: TrezorDevice) => ({ - ...d, - connected: false, - available: false, - path: '', - acquiring: false, - featuresNeedsReload: false, - isUsedElsewhere: false, -})); +const devicesFromStorage = (devices: Array): State => devices.map((device: TrezorDevice) => { + const extended = { + connected: false, + available: false, + path: '', + } + + return device.type === 'acquired' ? { + ...device, + ...extended + } : { + ...device, + features: null, + ...extended + }; +}); // Remove all device reference from State const forgetDevice = (state: State, device: TrezorDevice): State => state.filter(d => d.remember || (d.features && device.features && d.features.device_id !== device.features.device_id) || (!d.features && d.path !== device.path)); @@ -204,15 +236,16 @@ const disconnectDevice = (state: State, device: Device): State => { const otherDevices: State = state.filter(d => affectedDevices.indexOf(d) === -1); if (affectedDevices.length > 0) { - const acquiredDevices = affectedDevices.filter(d => !d.unacquired && d.state); - return otherDevices.concat(acquiredDevices.map((d) => { - d.connected = false; - d.available = false; - d.isUsedElsewhere = false; - d.featuresNeedsReload = false; - d.path = ''; + const acquiredDevices = affectedDevices.filter(d => d.features && d.state).map(d => { + if (d.type === 'acquired') { + d.connected = false; + d.available = false; + d.status = 'available'; + d.path = ''; + } return d; - })); + }); + return otherDevices.concat(acquiredDevices); } return state; @@ -221,7 +254,8 @@ const disconnectDevice = (state: State, device: Device): State => { const onSelectedDevice = (state: State, device: ?TrezorDevice): State => { if (device) { const otherDevices: Array = state.filter(d => d !== device); - return otherDevices.concat([{ ...device, ts: new Date().getTime() }]); + device.ts = new Date().getTime(); + return otherDevices.concat([ device ]); } return state; }; @@ -238,7 +272,8 @@ export default function devices(state: State = initialState, action: Action): St return authDevice(state, action.device, action.state); case CONNECT.REMEMBER: - return changeDevice(state, { ...action.device, path: '', remember: true }); + // return changeDevice(state, { ...action.device, path: '', remember: true }); + return changeDevice(state, action.device, { path: '', remember: true }); case CONNECT.FORGET: return forgetDevice(state, action.device); @@ -250,11 +285,12 @@ export default function devices(state: State = initialState, action: Action): St return addDevice(state, action.device); case DEVICE.CHANGED: - return changeDevice(state, { ...action.device, connected: true, available: true }); + // return changeDevice(state, { ...action.device, connected: true, available: true }); + return changeDevice(state, action.device, { connected: true, available: true }); // TODO: check if available will propagate to unavailable case DEVICE.DISCONNECT: - case DEVICE.DISCONNECT_UNACQUIRED: + // case DEVICE.DISCONNECT_UNACQUIRED: return disconnectDevice(state, action.device); case WALLET.SET_SELECTED_DEVICE: diff --git a/src/js/reducers/ModalReducer.js b/src/js/reducers/ModalReducer.js index e545d561..ae68258b 100644 --- a/src/js/reducers/ModalReducer.js +++ b/src/js/reducers/ModalReducer.js @@ -54,7 +54,7 @@ export default function modal(state: State = initialState, action: Action): Stat }; case DEVICE.CHANGED: - if (state.opened && action.device.path === state.device.path && action.device.isUsedElsewhere) { + if (state.opened && action.device.path === state.device.path && action.device.status === 'occupied') { return initialState; } return state; diff --git a/src/js/reducers/utils/index.js b/src/js/reducers/utils/index.js index 26fb3761..17068816 100644 --- a/src/js/reducers/utils/index.js +++ b/src/js/reducers/utils/index.js @@ -30,7 +30,7 @@ export const getSelectedDevice = (state: State): ?TrezorDevice => { const instance: ?number = locationState.deviceInstance ? parseInt(locationState.deviceInstance) : undefined; return state.devices.find((d) => { - if (d.unacquired && d.path === locationState.device) { + if (!d.features && d.path === locationState.device) { return true; } if (d.features && d.features.bootloader_mode && d.path === locationState.device) { return true;