From cbfbd3284b9742915064d94c104a6c81834e8990 Mon Sep 17 00:00:00 2001 From: Hanwen Cheng Date: Tue, 27 Aug 2019 15:20:08 +0200 Subject: [PATCH] QR Code Scanner Fixes (#332) * fix: create enable scanner state * fix: add warnnings when address detected * fix: remove unhandled promise rejection warning * fix: remove useless code * fix: changes according to review --- src/screens/QrScanner.js | 53 ++++++++++++++++++++++--------- src/stores/ScannerStore.js | 8 ++--- src/util/decoders.js | 64 +++++++++++++++++++++++++------------- 3 files changed, 85 insertions(+), 40 deletions(-) diff --git a/src/screens/QrScanner.js b/src/screens/QrScanner.js index edea6da3b4..d7a02d46eb 100644 --- a/src/screens/QrScanner.js +++ b/src/screens/QrScanner.js @@ -23,10 +23,10 @@ import { RNCamera } from 'react-native-camera'; import { Subscribe } from 'unstated'; import colors from '../colors'; -import fonts from "../fonts"; +import fonts from '../fonts'; import AccountsStore from '../stores/AccountsStore'; import ScannerStore from '../stores/ScannerStore'; -import { isJsonString, rawDataToU8A } from '../util/decoders'; +import {isAddressString, isJsonString, rawDataToU8A} from '../util/decoders'; export default class Scanner extends React.PureComponent { static navigationOptions = { @@ -34,6 +34,24 @@ export default class Scanner extends React.PureComponent { headerBackTitle: 'Scanner' }; + constructor(props) { + super(props); + this.state = { enableScan: true }; + } + + showErrorMessage(scannerStore, title, message) { + this.setState({ enableScan: false }); + Alert.alert(title, message, [ + { + text: 'Try again', + onPress: () => { + scannerStore.cleanup(); + this.setState({ enableScan: true }); + } + } + ]); + } + render() { return ( @@ -43,27 +61,26 @@ export default class Scanner extends React.PureComponent { navigation={this.props.navigation} scannerStore={scannerStore} onBarCodeRead={async txRequestData => { - if (scannerStore.isBusy()) { + if (scannerStore.isBusy() || !this.state.enableScan) { return; } - if (isJsonString(txRequestData.data)) { // Ethereum Legacy + if(isAddressString(txRequestData.data)){ + return this.showErrorMessage(scannerStore, text.ADDRESS_ERROR_TITLE, text.ADDRESS_ERROR_MESSAGE); + } else if (isJsonString(txRequestData.data)) { + // Ethereum Legacy await scannerStore.setUnsigned(txRequestData.data); } else { try { const strippedData = rawDataToU8A(txRequestData.rawData); - await scannerStore.setParsedData(strippedData, accountsStore); + await scannerStore.setParsedData( + strippedData, + accountsStore + ); } catch (e) { - Alert.alert('Unable to parse transaction', e.message, [ - { - text: 'Try again', - onPress: () => { - scannerStore.cleanup(); - } - } - ]); + return this.showErrorMessage(scannerStore, text.PARSE_ERROR_TITLE, e.message); } - } + } if (await scannerStore.setData(accountsStore)) { if (scannerStore.getType() === 'transaction') { @@ -143,6 +160,12 @@ export class QrScannerView extends React.PureComponent { } } +const text = { + ADDRESS_ERROR_TITLE: 'Address detected', + ADDRESS_ERROR_MESSAGE: 'Please create a transaction using a software such as MyCrypto or Fether so that Parity Signer can sign it.', + PARSE_ERROR_TITLE: 'Unable to parse transaction' +}; + const styles = StyleSheet.create({ inactive: { backgroundColor: colors.bg, @@ -207,6 +230,6 @@ const styles = StyleSheet.create({ color: colors.bg_text, fontSize: 14, fontFamily: fonts.bold, - paddingBottom: 20, + paddingBottom: 20 } }); diff --git a/src/stores/ScannerStore.js b/src/stores/ScannerStore.js index 6d34b1f6f1..21967ec2a0 100644 --- a/src/stores/ScannerStore.js +++ b/src/stores/ScannerStore.js @@ -80,7 +80,7 @@ export default class ScannerStore extends Container { async setParsedData(strippedData, accountsStore) { const parsedData = await constructDataFromBytes(strippedData); - + if (parsedData.isMultipart) { this.setPartData(parseData.frame, parsedData.frameCount, parseData.partData, accountsStore); return; @@ -100,7 +100,7 @@ export default class ScannerStore extends Container { // we havne't filled all the frames yet if (Object.keys(this.state.multipartData.length) < frameCount) { const nextDataState = this.state.multipartData; - + nextDataState[frame] = partData; this.setState({ @@ -268,7 +268,7 @@ export default class ScannerStore extends Container { cleanup() { this.setState(defaultState); } - + getIsOversized() { return this.state.isOversized; } @@ -308,4 +308,4 @@ export default class ScannerStore extends Container { getErrorMsg() { return this.state.scanErrorMsg; } -} \ No newline at end of file +} diff --git a/src/util/decoders.js b/src/util/decoders.js index 14303056e2..870d6e39e8 100644 --- a/src/util/decoders.js +++ b/src/util/decoders.js @@ -17,7 +17,12 @@ // @flow import { GenericExtrinsicPayload } from '@polkadot/types'; -import { hexStripPrefix, hexToU8a, u8aToHex, u8aToString } from '@polkadot/util'; +import { + hexStripPrefix, + hexToU8a, + u8aToHex, + u8aToString +} from '@polkadot/util'; import { encodeAddress } from '@polkadot/util-crypto'; import { blake2s, keccak } from './native'; @@ -88,7 +93,7 @@ export function rawDataToU8A(rawData) { export async function constructDataFromBytes(bytes) { const frameInfo = hexStripPrefix(u8aToHex(bytes.slice(0, 5))); - const isMultipart = !!(parseInt(frameInfo.substr(0, 2), 16)); + const isMultipart = !!parseInt(frameInfo.substr(0, 2), 16); const frameCount = parseInt(frameInfo.substr(2, 4), 16); const currentFrame = parseInt(frameInfo.substr(6, 4), 16); const uosAfterFrames = hexStripPrefix(u8aToHex(bytes.slice(5))); @@ -116,7 +121,12 @@ export async function constructDataFromBytes(bytes) { // decode payload appropriately via UOS switch (zerothByte) { case '45': // Ethereum UOS payload - action = firstByte === '00' || firstByte === '01' ? 'signData' : firstByte === '01' ? 'signTransaction' : null; + action = + firstByte === '00' || firstByte === '01' + ? 'signData' + : firstByte === '01' + ? 'signTransaction' + : null; address = uosAfterFrames.substr(4, 44); data['action'] = action; @@ -142,12 +152,14 @@ export async function constructDataFromBytes(bytes) { data['data']['crypto'] = crypto; data['data']['account'] = ss58Encoded; - switch(secondByte) { + switch (secondByte) { case '00': data['action'] = 'signTransaction'; data['oversized'] = isOversized; data['isHash'] = isOversized; - data['data']['data'] = isOversized ? await blake2s(u8aToHex(rawPayload)) : new GenericExtrinsicPayload(rawPayload, { version: 3 }); + data['data']['data'] = isOversized + ? await blake2s(u8aToHex(rawPayload)) + : new GenericExtrinsicPayload(rawPayload, { version: 3 }); break; case '01': data['action'] = 'signTransaction'; @@ -159,13 +171,17 @@ export async function constructDataFromBytes(bytes) { data['action'] = 'signTransaction'; data['oversized'] = isOversized; data['isHash'] = isOversized; - data['data']['data'] = isOversized ? await blake2s(u8aToHex(rawPayload)) : new GenericExtrinsicPayload(rawPayload, { version: 3 }); + data['data']['data'] = isOversized + ? await blake2s(u8aToHex(rawPayload)) + : new GenericExtrinsicPayload(rawPayload, { version: 3 }); break; case '03': // Cold Signer should attempt to decode message to utf8 data['action'] = 'signData'; data['oversized'] = isOversized; data['isHash'] = isOversized; - data['data']['data'] = isOversized ? await blake2s(u8aToHex(rawPayload)) : u8aToString(rawPayload); + data['data']['data'] = isOversized + ? await blake2s(u8aToHex(rawPayload)) + : u8aToString(rawPayload); break; default: break; @@ -188,29 +204,35 @@ export function decodeToString(message: Uint8Array): string { } export function asciiToHex(message: string): string { - var result = []; - for (let i = 0; i < message.length; i++) { - var hex = Number(message.charCodeAt(i)).toString(16); - result.push(hex); + let result = []; + for (let i = 0; i < message.length; i++) { + const hex = Number(message.charCodeAt(i)).toString(16); + result.push(hex); } - return result.join(''); + return result.join(''); } export function hexToAscii(hexBytes: Uint8Array): string { - var hex = hexBytes.toString(); - var str = ''; - for (var n = 0; n < hex.length; n += 2) { - str += String.fromCharCode(parseInt(hex.substr(n, 2), 16)); - } + const hex = hexBytes.toString(); + let str = ''; + for (let n = 0; n < hex.length; n += 2) { + str += String.fromCharCode(parseInt(hex.substr(n, 2), 16)); + } - return str; + return str; } export function isJsonString(str) { try { - JSON.parse(str); + JSON.parse(str); } catch (e) { - return false; + return false; } return true; -} \ No newline at end of file +} + +export function isAddressString(str) { + return str.substr(0, 2) === '0x' || + str.substr(0, 9) === 'ethereum:' || + str.substr(0, 10) === 'substrate:' +}