From c64762063e72feba4eb2add2ecad6a7263bd73ef Mon Sep 17 00:00:00 2001 From: snyk-bot Date: Tue, 14 Apr 2020 05:51:04 +0000 Subject: [PATCH 01/12] fix: upgrade react-native-gesture-handler from 1.5.6 to 1.6.1 Snyk has created this PR to upgrade react-native-gesture-handler from 1.5.6 to 1.6.1. See this package in NPM: https://www.npmjs.com/package/react-native-gesture-handler See this project in Snyk: https://app.snyk.io/org/bluewallet/project/4d0df22a-0152-410a-8584-6df0d0a596d4?utm_source=github&utm_medium=upgrade-pr --- package-lock.json | 14 +++----------- package.json | 2 +- 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7e18b504..8a97c162 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6526,13 +6526,6 @@ "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", "integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=" }, - "hammerjs": { - "version": "git+https://github.com/naver/hammer.js.git#54bc698b25edd6e1b76ca975ebaced5ce0467d51", - "from": "git+https://github.com/naver/hammer.js.git", - "requires": { - "@types/hammerjs": "^2.0.36" - } - }, "har-schema": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", @@ -11640,12 +11633,11 @@ } }, "react-native-gesture-handler": { - "version": "1.5.6", - "resolved": "https://registry.npmjs.org/react-native-gesture-handler/-/react-native-gesture-handler-1.5.6.tgz", - "integrity": "sha512-z2jLUkRiRc0PBAC9UcXYkqy3VUzBG0cYQAGMsDHsd90JgrzudHAFRJV9fvFm18wNauFTNnJievjZ0C3rI2ydhw==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/react-native-gesture-handler/-/react-native-gesture-handler-1.6.1.tgz", + "integrity": "sha512-gQgIKhDiYf754yzhhliagLuLupvGb6ZyBdzYzr7aus3Fyi87TLOw63ers+r4kGw0h26oAWTAdHd34JnF4NeL6Q==", "requires": { "@egjs/hammerjs": "^2.0.17", - "hammerjs": "git+https://github.com/naver/hammer.js.git", "hoist-non-react-statics": "^2.3.1", "invariant": "^2.2.4", "prop-types": "^15.7.2" diff --git a/package.json b/package.json index a359d320..094ffedd 100644 --- a/package.json +++ b/package.json @@ -105,7 +105,7 @@ "react-native-elements": "0.19.0", "react-native-flexi-radio-button": "0.2.2", "react-native-fs": "2.16.6", - "react-native-gesture-handler": "1.5.6", + "react-native-gesture-handler": "1.6.1", "react-native-handoff": "git+https://github.com/marcosrdz/react-native-handoff.git", "react-native-haptic-feedback": "1.9.0", "react-native-image-picker": "1.1.0", From 47caa6c04c5c5e9a94309a3421615ebebd24d194 Mon Sep 17 00:00:00 2001 From: Overtorment Date: Wed, 15 Apr 2020 18:04:46 +0100 Subject: [PATCH 02/12] REL: ver bump to v5.3.2 --- android/app/build.gradle | 2 +- ios/BlueWallet/Info.plist | 2 +- ios/BlueWalletWatch Extension/Info.plist | 2 +- ios/BlueWalletWatch/Info.plist | 2 +- ios/TodayExtension/Info.plist | 2 +- ios/fastlane/metadata/en-US/release_notes.txt | 25 ++++++++----------- package-lock.json | 2 +- package.json | 2 +- 8 files changed, 17 insertions(+), 22 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 9c125c2e..9cac5e70 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -140,7 +140,7 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion versionCode 1 - versionName "5.3.1" + versionName "5.3.2" multiDexEnabled true missingDimensionStrategy 'react-native-camera', 'general' testBuildType System.getProperty('testBuildType', 'debug') // This will later be used to control the test apk build type diff --git a/ios/BlueWallet/Info.plist b/ios/BlueWallet/Info.plist index 9b7b9ff5..ea536167 100644 --- a/ios/BlueWallet/Info.plist +++ b/ios/BlueWallet/Info.plist @@ -48,7 +48,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 5.3.1 + 5.3.2 CFBundleSignature ???? CFBundleURLTypes diff --git a/ios/BlueWalletWatch Extension/Info.plist b/ios/BlueWalletWatch Extension/Info.plist index ea9cfe02..9f3c6d93 100644 --- a/ios/BlueWalletWatch Extension/Info.plist +++ b/ios/BlueWalletWatch Extension/Info.plist @@ -17,7 +17,7 @@ CFBundlePackageType XPC! CFBundleShortVersionString - 5.3.1 + 5.3.2 CFBundleVersion 239 CLKComplicationPrincipalClass diff --git a/ios/BlueWalletWatch/Info.plist b/ios/BlueWalletWatch/Info.plist index 2531cce2..be606e9d 100644 --- a/ios/BlueWalletWatch/Info.plist +++ b/ios/BlueWalletWatch/Info.plist @@ -17,7 +17,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 5.3.1 + 5.3.2 CFBundleVersion 239 UISupportedInterfaceOrientations diff --git a/ios/TodayExtension/Info.plist b/ios/TodayExtension/Info.plist index bae8e83d..89a1306e 100644 --- a/ios/TodayExtension/Info.plist +++ b/ios/TodayExtension/Info.plist @@ -17,7 +17,7 @@ CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString - 5.3.1 + 5.3.2 CFBundleVersion 1 NSExtension diff --git a/ios/fastlane/metadata/en-US/release_notes.txt b/ios/fastlane/metadata/en-US/release_notes.txt index 01f61b40..af396051 100644 --- a/ios/fastlane/metadata/en-US/release_notes.txt +++ b/ios/fastlane/metadata/en-US/release_notes.txt @@ -1,3 +1,13 @@ +v5.3.2 +====== + +* ADD: Handoff Receive Address +* FIX: BIP38 import support (password-protected private key from paper wallet) +* FIX: iOS widget not working +* FIX: negative confirmation num for electrum personal server +* FIX: Keyboard avoiding +* FIX: allow LocalTrader for watch-only HD wallets + v5.3.0 ====== @@ -50,18 +60,3 @@ v5.0.0 * FIX: lnurl scan to receive is not returning the correct view (closes #828) * FIX: watch-only delete wallet doesnt have confirmation now * FIX: typo in spanish - -v4.9.4 -====== - -* REL: ver bump 4.9.4 -* FIX: Lint -* FIX: Listen to lnd invoice changes -* FIX: There's two refresh icons on the main view, when you pull-to-refresh -* FIX: Crash on scan in wallets import when closing the view -* FIX: Allow walet change on invoice creation -* FIX: Allow wallet text input to be empty for new wallet naming -* FIX: LN Invoice amount renders 0 -* FIX: Handle both chains -* FIX: deeplinking (safello etc) -* DEL: Remove alerts from main list diff --git a/package-lock.json b/package-lock.json index 8a97c162..92e3150b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "bluewallet", - "version": "5.3.1", + "version": "5.3.2", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 094ffedd..159a1a16 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "bluewallet", - "version": "5.3.1", + "version": "5.3.2", "license": "MIT", "devDependencies": { "@babel/core": "^7.6.2", From 4baa1f38a636cede92a1d3483c0dd05f720c5235 Mon Sep 17 00:00:00 2001 From: Overtorment Date: Fri, 17 Apr 2020 15:41:35 +0100 Subject: [PATCH 03/12] ADD: refill lightning wallet with bank card --- class/abstract-hd-wallet.js | 4 ++ class/lightning-custodian-wallet.js | 3 +- class/watch-only-wallet.js | 18 ++++- screen/wallets/buyBitcoin.js | 66 ++++++++++--------- screen/wallets/details.js | 3 +- screen/wallets/transactions.js | 16 ++++- .../hd-segwit-bech32-wallet.test.js | 3 + .../integration/hd-segwit-p2sh-wallet.test.js | 4 ++ 8 files changed, 81 insertions(+), 36 deletions(-) diff --git a/class/abstract-hd-wallet.js b/class/abstract-hd-wallet.js index 7cbd8f9a..0acf1437 100644 --- a/class/abstract-hd-wallet.js +++ b/class/abstract-hd-wallet.js @@ -23,6 +23,10 @@ export class AbstractHDWallet extends LegacyWallet { this.gap_limit = 20; } + getNextFreeAddressIndex() { + return this.next_free_address_index; + } + prepareForSerialization() { // deleting structures that cant be serialized delete this._node0; diff --git a/class/lightning-custodian-wallet.js b/class/lightning-custodian-wallet.js index c52335cd..8a701fa3 100644 --- a/class/lightning-custodian-wallet.js +++ b/class/lightning-custodian-wallet.js @@ -368,7 +368,8 @@ export class LightningCustodianWallet extends LegacyWallet { } async getAddressAsync() { - return this.fetchBtcAddress(); + await this.fetchBtcAddress(); + return this.getAddress(); } async allowOnchainAddress() { diff --git a/class/watch-only-wallet.js b/class/watch-only-wallet.js index 9e77af50..2dc4788b 100644 --- a/class/watch-only-wallet.js +++ b/class/watch-only-wallet.js @@ -35,7 +35,9 @@ export class WatchOnlyWallet extends LegacyWallet { } getAddress() { - return this.secret; + if (this.isAddressValid(this.secret)) return this.secret; // handling case when there is an XPUB there + if (this._hdWalletInstance) throw new Error('Should not be used in watch-only HD wallets'); + throw new Error('Not initialized'); } createTx(utxos, amount, fee, toAddress, memo) { @@ -119,6 +121,11 @@ export class WatchOnlyWallet extends LegacyWallet { throw new Error('Not initialized'); } + getNextFreeAddressIndex() { + if (this._hdWalletInstance) return this._hdWalletInstance.next_free_address_index; + throw new Error('Not initialized'); + } + async getChangeAddressAsync() { if (this._hdWalletInstance) return this._hdWalletInstance.getChangeAddressAsync(); throw new Error('Not initialized'); @@ -183,6 +190,15 @@ export class WatchOnlyWallet extends LegacyWallet { return this.secret.startsWith('xpub') || this.secret.startsWith('ypub') || this.secret.startsWith('zpub'); } + weOwnAddress(address) { + if (this.isHd()) { + if (this._hdWalletInstance) return this._hdWalletInstance.weOwnAddress(address); + throw new Error('Not initialized'); + } + + return this.getAddress() === address; + } + allowHodlHodlTrading() { return this.isHd(); } diff --git a/screen/wallets/buyBitcoin.js b/screen/wallets/buyBitcoin.js index 3b6e0b71..cb0ff6d4 100644 --- a/screen/wallets/buyBitcoin.js +++ b/screen/wallets/buyBitcoin.js @@ -2,8 +2,8 @@ import React, { Component } from 'react'; import { BlueNavigationStyle, BlueLoading } from '../../BlueComponents'; import PropTypes from 'prop-types'; import { WebView } from 'react-native-webview'; -/** @type {AppStorage} */ -let BlueApp = require('../../BlueApp'); +import { AppStorage, LightningCustodianWallet, WatchOnlyWallet } from '../../class'; +let BlueApp: AppStorage = require('../../BlueApp'); let loc = require('../../loc'); export default class BuyBitcoin extends Component { @@ -15,47 +15,54 @@ export default class BuyBitcoin extends Component { constructor(props) { super(props); - let address = props.navigation.state.params.address; - let secret = props.navigation.state.params.secret; + let wallet = props.navigation.state.params.wallet; this.state = { isLoading: true, - address: address, - secret: secret, - addressText: '', + wallet, + address: '', }; } async componentDidMount() { console.log('buyBitcoin - componentDidMount'); - /** @type {AbstractWallet} */ - let wallet; - let address = this.state.address; - for (let w of BlueApp.getWallets()) { - if ((address && w.getAddress() === this.state.address) || w.getSecret() === this.state.secret) { - // found our wallet - wallet = w; - } - } + /** @type {AbstractHDWallet|WatchOnlyWallet|LightningCustodianWallet} */ + let wallet = this.state.wallet; + + let address = ''; - if (wallet && wallet.getAddressAsync) { - setTimeout(async () => { - address = await wallet.getAddressAsync(); - BlueApp.saveToDisk(); // caching whatever getAddressAsync() generated internally - this.setState({ - address: address, - addressText: address, - isLoading: false, - }); - }, 1); - } else { + if (WatchOnlyWallet.type === wallet.type && !wallet.isHd()) { + // plain watchonly - just get the address + address = wallet.getAddress(); this.setState({ isLoading: false, address, - addressText: address, }); + return; } + + // otherwise, lets call widely-used getAddressAsync() + + try { + address = await Promise.race([wallet.getAddressAsync(), BlueApp.sleep(2000)]); + } catch (_) {} + + if (!address) { + // either sleep expired or getAddressAsync threw an exception + if (LightningCustodianWallet.type === wallet.type) { + // not much we can do, lets hope refill address was cached previously + address = wallet.getAddress() || ''; + } else { + // plain hd wallet (either HD or watchonly-wrapped). trying next free address + address = wallet._getExternalAddressByIndex(wallet.getNextFreeAddressIndex()); + } + } + + this.setState({ + isLoading: false, + address, + }); } render() { @@ -86,8 +93,7 @@ BuyBitcoin.propTypes = { goBack: PropTypes.func, state: PropTypes.shape({ params: PropTypes.shape({ - address: PropTypes.string, - secret: PropTypes.string, + wallet: PropTypes.object, safelloStateToken: PropTypes.string, }), }), diff --git a/screen/wallets/details.js b/screen/wallets/details.js index 83c77d47..08000265 100644 --- a/screen/wallets/details.js +++ b/screen/wallets/details.js @@ -253,8 +253,7 @@ export default class WalletDetails extends Component { }} onPress={() => this.props.navigation.navigate('BuyBitcoin', { - address: this.state.wallet.getAddress(), - secret: this.state.wallet.getSecret(), + wallet: this.state.wallet, }) } title={loc.wallets.details.buy_bitcoin} diff --git a/screen/wallets/transactions.js b/screen/wallets/transactions.js index 6406a680..2e2c2f07 100644 --- a/screen/wallets/transactions.js +++ b/screen/wallets/transactions.js @@ -288,6 +288,19 @@ export default class WalletTransactions extends Component { title={'Refill with External Wallet'} /> + { + this.setState({ isManageFundsModalVisible: false }, async () => { + this.props.navigation.navigate('BuyBitcoin', { + wallet: this.state.wallet, + }); + }); + }} + title={'Refill with bank card'} + /> + this.props.navigation.navigate('BuyBitcoin', { - address: this.state.wallet.getAddress(), - secret: this.state.wallet.getSecret(), + wallet: this.state.wallet, }) } > diff --git a/tests/integration/hd-segwit-bech32-wallet.test.js b/tests/integration/hd-segwit-bech32-wallet.test.js index fb53e17a..4e5a5b74 100644 --- a/tests/integration/hd-segwit-bech32-wallet.test.js +++ b/tests/integration/hd-segwit-bech32-wallet.test.js @@ -49,6 +49,7 @@ describe('Bech32 Segwit HD (BIP84)', () => { assert.strictEqual(await hd.getAddressAsync(), hd._getExternalAddressByIndex(2)); assert.strictEqual(await hd.getChangeAddressAsync(), hd._getInternalAddressByIndex(2)); assert.strictEqual(hd.next_free_address_index, 2); + assert.strictEqual(hd.getNextFreeAddressIndex(), 2); assert.strictEqual(hd.next_free_change_address_index, 2); // now fetch txs @@ -81,6 +82,7 @@ describe('Bech32 Segwit HD (BIP84)', () => { assert.strictEqual(await hd.getAddressAsync(), hd._getExternalAddressByIndex(2)); assert.strictEqual(await hd.getChangeAddressAsync(), hd._getInternalAddressByIndex(2)); assert.strictEqual(hd.next_free_address_index, 2); + assert.strictEqual(hd.getNextFreeAddressIndex(), 2); assert.strictEqual(hd.next_free_change_address_index, 2); }); @@ -153,6 +155,7 @@ describe('Bech32 Segwit HD (BIP84)', () => { assert.ok(hd.next_free_change_address_index > 0); assert.ok(hd.next_free_address_index > 0); + assert.ok(hd.getNextFreeAddressIndex() > 0); start = +new Date(); await hd.fetchTransactions(); diff --git a/tests/integration/hd-segwit-p2sh-wallet.test.js b/tests/integration/hd-segwit-p2sh-wallet.test.js index 614530c6..cc5fec0a 100644 --- a/tests/integration/hd-segwit-p2sh-wallet.test.js +++ b/tests/integration/hd-segwit-p2sh-wallet.test.js @@ -187,6 +187,7 @@ it('can create a Legacy HD (BIP44)', async function() { assert.ok(hd._lastTxFetch > 0); assert.strictEqual(hd.getTransactions().length, 4); assert.strictEqual(hd.next_free_address_index, 1); + assert.strictEqual(hd.getNextFreeAddressIndex(), 1); assert.strictEqual(hd.next_free_change_address_index, 1); for (let tx of hd.getTransactions()) { @@ -196,6 +197,7 @@ it('can create a Legacy HD (BIP44)', async function() { // checking that internal pointer and async address getter return the same address let freeAddress = await hd.getAddressAsync(); assert.strictEqual(hd._getExternalAddressByIndex(hd.next_free_address_index), freeAddress); + assert.strictEqual(hd._getExternalAddressByIndex(hd.getNextFreeAddressIndex()), freeAddress); }); it('Legacy HD (BIP44) can create TX', async () => { @@ -257,9 +259,11 @@ it('HD breadwallet works', async function() { assert.strictEqual(hdBread.getBalance(), 123456); assert.strictEqual(hdBread.next_free_address_index, 11); + assert.strictEqual(hdBread.getNextFreeAddressIndex(), 11); assert.strictEqual(hdBread.next_free_change_address_index, 118); // checking that internal pointer and async address getter return the same address let freeAddress = await hdBread.getAddressAsync(); assert.strictEqual(hdBread._getExternalAddressByIndex(hdBread.next_free_address_index), freeAddress); + assert.strictEqual(hdBread._getExternalAddressByIndex(hdBread.getNextFreeAddressIndex()), freeAddress); }); From bceacaca29034686e4ee26adbbc836ca6361ceaf Mon Sep 17 00:00:00 2001 From: Overtorment Date: Thu, 16 Apr 2020 15:40:23 +0100 Subject: [PATCH 04/12] ADD: support for Electrum Seed format, legacy (closes #954) --- class/abstract-hd-electrum-wallet.js | 15 +++++ class/abstract-wallet.js | 4 ++ class/hd-legacy-electrum-seed-p2pkh-wallet.js | 65 +++++++++++++++++++ class/index.js | 1 + class/walletImport.js | 9 +++ package-lock.json | 10 +++ package.json | 3 + .../hd-segwit-bech32-wallet.test.js | 15 +++++ ...-legacy-electrum-seed-p2pkh-wallet.test.js | 30 +++++++++ 9 files changed, 152 insertions(+) create mode 100644 class/hd-legacy-electrum-seed-p2pkh-wallet.js create mode 100644 tests/unit/hd-legacy-electrum-seed-p2pkh-wallet.test.js diff --git a/class/abstract-hd-electrum-wallet.js b/class/abstract-hd-electrum-wallet.js index 567b81c9..b9717b8f 100644 --- a/class/abstract-hd-electrum-wallet.js +++ b/class/abstract-hd-electrum-wallet.js @@ -12,6 +12,9 @@ const reverse = require('buffer-reverse'); const { RNRandomBytes } = NativeModules; +/** + * Electrum - means that it utilizes Electrum protocol for blockchain data + */ export class AbstractHDElectrumWallet extends AbstractHDWallet { static type = 'abstract'; static typeReadable = 'abstract'; @@ -986,4 +989,16 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet { if (broadcast.indexOf('successfully') !== -1) return true; return broadcast.length === 64; // this means return string is txid (precise length), so it was broadcasted ok } + + /** + * Probes zero address in external hierarchy for transactions, if there are any returns TRUE. + * Zero address is a pretty good indicator, since its a first one to fund the wallet. How can you use the wallet and + * not fund it first? + * + * @returns {Promise} + */ + async wasEverUsed() { + let txs = await BlueElectrum.getTransactionsByAddress(this._getExternalAddressByIndex(0)); + return txs.length > 0; + } } diff --git a/class/abstract-wallet.js b/class/abstract-wallet.js index f795a188..cf5e53e2 100644 --- a/class/abstract-wallet.js +++ b/class/abstract-wallet.js @@ -171,4 +171,8 @@ export class AbstractWallet { useWithHardwareWalletEnabled() { return false; } + + async wasEverUsed() { + throw new Error('Not implemented'); + } } diff --git a/class/hd-legacy-electrum-seed-p2pkh-wallet.js b/class/hd-legacy-electrum-seed-p2pkh-wallet.js new file mode 100644 index 00000000..789b15c7 --- /dev/null +++ b/class/hd-legacy-electrum-seed-p2pkh-wallet.js @@ -0,0 +1,65 @@ +import { HDLegacyP2PKHWallet } from './'; + +const bitcoin = require('bitcoinjs-lib'); +const mn = require('electrum-mnemonic'); + +/** + * ElectrumSeed means that instead of BIP39 seed format it works with the format invented by Electrum wallet. Otherwise + * its a regular HD wallet that has all the properties of parent class. + * + * @see https://electrum.readthedocs.io/en/latest/seedphrase.html + */ +export class HDLegacyElectrumSeedP2PKHWallet extends HDLegacyP2PKHWallet { + static type = 'HDlegacyElectrumSeedP2PKH'; + static typeReadable = 'HD Legacy Electrum (BIP32 P2PKH)'; + + validateMnemonic() { + try { + mn.mnemonicToSeedSync(this.secret); + return true; + } catch (_) { + return false; + } + } + + getXpub() { + if (this._xpub) { + return this._xpub; // cache hit + } + const root = bitcoin.bip32.fromSeed(mn.mnemonicToSeedSync(this.secret)); + this._xpub = root.toBase58(); + return this._xpub; + } + + _getInternalAddressByIndex(index) { + index = index * 1; // cast to int + if (this.internal_addresses_cache[index]) return this.internal_addresses_cache[index]; // cache hit + + const node = bitcoin.bip32.fromBase58(this.getXpub()); + const address = bitcoin.payments.p2pkh({ + pubkey: node.derive(1).derive(index).publicKey, + }).address; + + return (this.internal_addresses_cache[index] = address); + } + + _getExternalAddressByIndex(index) { + index = index * 1; // cast to int + if (this.external_addresses_cache[index]) return this.external_addresses_cache[index]; // cache hit + + const node = bitcoin.bip32.fromBase58(this.getXpub()); + const address = bitcoin.payments.p2pkh({ + pubkey: node.derive(0).derive(index).publicKey, + }).address; + + return (this.external_addresses_cache[index] = address); + } + + _getWIFByIndex(internal, index) { + const root = bitcoin.bip32.fromSeed(mn.mnemonicToSeedSync(this.secret)); + const path = `m/${internal ? 1 : 0}/${index}`; + const child = root.derivePath(path); + + return child.toWIF(); + } +} diff --git a/class/index.js b/class/index.js index 1ec5ece2..45793cd1 100644 --- a/class/index.js +++ b/class/index.js @@ -13,3 +13,4 @@ export * from './abstract-hd-wallet'; export * from './hd-segwit-bech32-wallet'; export * from './hd-segwit-bech32-transaction'; export * from './placeholder-wallet'; +export * from './hd-legacy-electrum-seed-p2pkh-wallet'; diff --git a/class/walletImport.js b/class/walletImport.js index f079b9ac..d2ddbea4 100644 --- a/class/walletImport.js +++ b/class/walletImport.js @@ -10,6 +10,7 @@ import { LightningCustodianWallet, PlaceholderWallet, SegwitBech32Wallet, + HDLegacyElectrumSeedP2PKHWallet, } from '../class'; import ReactNativeHapticFeedback from 'react-native-haptic-feedback'; const EV = require('../events'); @@ -97,6 +98,7 @@ export default class WalletImport { // 1. check if its HDSegwitP2SHWallet (BIP49) // 2. check if its HDLegacyP2PKHWallet (BIP44) // 3. check if its HDLegacyBreadwalletWallet (no BIP, just "m/0") + // 3.1 check HD Electrum legacy // 4. check if its Segwit WIF (P2SH) // 5. check if its Legacy WIF // 6. check if its address (watch-only wallet) @@ -202,6 +204,13 @@ export default class WalletImport { } } + let hdElectrumSeedLegacy = new HDLegacyElectrumSeedP2PKHWallet(); + hdElectrumSeedLegacy.setSecret(importText); + if (await hdElectrumSeedLegacy.wasEverUsed()) { + // not fetching txs or balances, fuck it, yolo, life is too short + return WalletImport._saveWallet(hdElectrumSeedLegacy); + } + let hd2 = new HDSegwitP2SHWallet(); hd2.setSecret(importText); if (hd2.validateMnemonic()) { diff --git a/package-lock.json b/package-lock.json index 92e3150b..3ff871b8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4460,6 +4460,16 @@ "version": "git+https://github.com/BlueWallet/rn-electrum-client.git#2a5bb11dd9a8d89f328049d9ed59bce49d88a15d", "from": "git+https://github.com/BlueWallet/rn-electrum-client.git#2a5bb11dd9a8d89f328049d9ed59bce49d88a15d" }, + "electrum-mnemonic": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/electrum-mnemonic/-/electrum-mnemonic-1.0.5.tgz", + "integrity": "sha512-Wq5vFTTZzHu6w6GSxSNdSgEouqK5x7nky7XGQOnIvS5gx2on7EHysAqB6sSGOhhD5MnoT8nn9LhZPSBrDWkllw==", + "requires": { + "create-hmac": "^1.1.7", + "pbkdf2": "^3.0.17", + "randombytes": "^2.1.0" + } + }, "elliptic": { "version": "6.5.2", "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.2.tgz", diff --git a/package.json b/package.json index 159a1a16..9c25f67c 100644 --- a/package.json +++ b/package.json @@ -45,6 +45,7 @@ "e2e:debug": "(test -f android/app/build/outputs/apk/debug/app-debug.apk && test -f android/app/build/outputs/apk/androidTest/debug/app-debug-androidTest.apk) || detox build -c android.emu.debug; detox test -c android.emu.debug", "lint": "./node_modules/.bin/eslint *.js screen/**/*.js screen/ class/ models/ loc/ tests/integration/ tests/e2e/ tests/unit/", "lint:fix": "./node_modules/.bin/eslint *.js screen/**/*.js screen/ class/ models/ loc/ tests/integration/ tests/e2e/ tests/unit/ --fix", + "lint:quickfix": "git status --porcelain | grep -v '\\.json' | grep '\\.js' --color=never | awk '{print $2}' | xargs ./node_modules/.bin/eslint --fix; exit 0", "unit": "node node_modules/jest/bin/jest.js tests/unit/*" }, "jest": { @@ -79,6 +80,7 @@ "dayjs": "1.8.23", "ecurve": "1.0.6", "electrum-client": "git+https://github.com/BlueWallet/rn-electrum-client.git#2a5bb11dd9a8d89f328049d9ed59bce49d88a15d", + "electrum-mnemonic": "1.0.5", "eslint-config-prettier": "6.10.0", "eslint-config-standard": "12.0.0", "eslint-config-standard-react": "7.0.2", @@ -90,6 +92,7 @@ "lottie-react-native": "3.1.1", "node-libs-react-native": "1.0.3", "path-browserify": "1.0.0", + "pbkdf2": "3.0.17", "prettier": "1.19.1", "process": "0.11.10", "prop-types": "15.7.2", diff --git a/tests/integration/hd-segwit-bech32-wallet.test.js b/tests/integration/hd-segwit-bech32-wallet.test.js index 4e5a5b74..b4d16414 100644 --- a/tests/integration/hd-segwit-bech32-wallet.test.js +++ b/tests/integration/hd-segwit-bech32-wallet.test.js @@ -235,4 +235,19 @@ describe('Bech32 Segwit HD (BIP84)', () => { assert.strictEqual(totalInput - totalOutput, fee); assert.strictEqual(outputs[outputs.length - 1].address, changeAddress); }); + + it('wasEverUsed() works', async () => { + if (!process.env.HD_MNEMONIC) { + console.error('process.env.HD_MNEMONIC not set, skipped'); + return; + } + + let hd = new HDSegwitBech32Wallet(); + hd.setSecret(process.env.HD_MNEMONIC); + assert.ok(await hd.wasEverUsed()); + + hd = new HDSegwitBech32Wallet(); + await hd.generate(); + assert.ok(!(await hd.wasEverUsed()), hd.getSecret()); + }); }); diff --git a/tests/unit/hd-legacy-electrum-seed-p2pkh-wallet.test.js b/tests/unit/hd-legacy-electrum-seed-p2pkh-wallet.test.js new file mode 100644 index 00000000..a2eadda9 --- /dev/null +++ b/tests/unit/hd-legacy-electrum-seed-p2pkh-wallet.test.js @@ -0,0 +1,30 @@ +/* global describe, it */ +import { HDLegacyElectrumSeedP2PKHWallet } from '../../class'; +let assert = require('assert'); + +describe('HDLegacyElectrumSeedP2PKHWallet', () => { + it('can import mnemonics and generate addresses and WIFs', async function() { + if (!process.env.HD_ELECTRUM_SEED_LEGACY) { + console.error('process.env.HD_ELECTRUM_SEED_LEGACY not set, skipped'); + return; + } + let hd = new HDLegacyElectrumSeedP2PKHWallet(); + hd.setSecret(process.env.HD_ELECTRUM_SEED_LEGACY); + assert.ok(hd.validateMnemonic()); + + let address = hd._getExternalAddressByIndex(0); + assert.strictEqual(address, '1Ca9ZVshGdKiiMEMNTG1bYqbifYMZMwV8'); + + address = hd._getInternalAddressByIndex(0); + assert.strictEqual(address, '1JygAvTQS9haAYgRfPSdHgmXd3syjB8Fnp'); + + let wif = hd._getExternalWIFByIndex(0); + assert.strictEqual(wif, 'KxGPz9dyib26p6bL2vQPvBPHBMA8iHVqEetg3x5XA4Rk1trZ11Kz'); + + wif = hd._getInternalWIFByIndex(0); + assert.strictEqual(wif, 'L52d26QmYGW8ctHo1omM5fZeJMgaonSkEWCGpnEekNvkVUoqTsNF'); + + hd.setSecret('bs'); + assert.ok(!hd.validateMnemonic()); + }); +}); From 4aad1b69154ed8b7e0c6af735d9b6be5f5abda77 Mon Sep 17 00:00:00 2001 From: Overtorment Date: Fri, 17 Apr 2020 16:23:18 +0100 Subject: [PATCH 05/12] REF: lint --- screen/lnd/scanLndInvoice.js | 2 +- screen/receive/details.js | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/screen/lnd/scanLndInvoice.js b/screen/lnd/scanLndInvoice.js index bf7e7ffb..f4815379 100644 --- a/screen/lnd/scanLndInvoice.js +++ b/screen/lnd/scanLndInvoice.js @@ -351,4 +351,4 @@ ScanLndInvoice.propTypes = { }), }), }), -}; \ No newline at end of file +}; diff --git a/screen/receive/details.js b/screen/receive/details.js index 94aa0ce6..5b4e3f26 100644 --- a/screen/receive/details.js +++ b/screen/receive/details.js @@ -26,7 +26,6 @@ import Handoff from 'react-native-handoff'; const BlueApp = require('../../BlueApp'); const loc = require('../../loc'); - export default class ReceiveDetails extends Component { static navigationOptions = ({ navigation }) => ({ ...BlueNavigationStyle(navigation, true), From c463faddbc198b12669d4d1fa02c6e0599ce1ded Mon Sep 17 00:00:00 2001 From: Overtorment Date: Fri, 17 Apr 2020 16:24:41 +0100 Subject: [PATCH 06/12] REL: ver bump to v5.3.3 --- android/app/build.gradle | 2 +- ios/BlueWallet/Info.plist | 2 +- ios/BlueWalletWatch Extension/Info.plist | 2 +- ios/BlueWalletWatch/Info.plist | 2 +- ios/TodayExtension/Info.plist | 2 +- package-lock.json | 2 +- package.json | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 9cac5e70..b99f90ea 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -140,7 +140,7 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion versionCode 1 - versionName "5.3.2" + versionName "5.3.3" multiDexEnabled true missingDimensionStrategy 'react-native-camera', 'general' testBuildType System.getProperty('testBuildType', 'debug') // This will later be used to control the test apk build type diff --git a/ios/BlueWallet/Info.plist b/ios/BlueWallet/Info.plist index ea536167..01a30aba 100644 --- a/ios/BlueWallet/Info.plist +++ b/ios/BlueWallet/Info.plist @@ -48,7 +48,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 5.3.2 + 5.3.3 CFBundleSignature ???? CFBundleURLTypes diff --git a/ios/BlueWalletWatch Extension/Info.plist b/ios/BlueWalletWatch Extension/Info.plist index 9f3c6d93..9e22bffe 100644 --- a/ios/BlueWalletWatch Extension/Info.plist +++ b/ios/BlueWalletWatch Extension/Info.plist @@ -17,7 +17,7 @@ CFBundlePackageType XPC! CFBundleShortVersionString - 5.3.2 + 5.3.3 CFBundleVersion 239 CLKComplicationPrincipalClass diff --git a/ios/BlueWalletWatch/Info.plist b/ios/BlueWalletWatch/Info.plist index be606e9d..b1e07734 100644 --- a/ios/BlueWalletWatch/Info.plist +++ b/ios/BlueWalletWatch/Info.plist @@ -17,7 +17,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 5.3.2 + 5.3.3 CFBundleVersion 239 UISupportedInterfaceOrientations diff --git a/ios/TodayExtension/Info.plist b/ios/TodayExtension/Info.plist index 89a1306e..8f37a5f8 100644 --- a/ios/TodayExtension/Info.plist +++ b/ios/TodayExtension/Info.plist @@ -17,7 +17,7 @@ CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString - 5.3.2 + 5.3.3 CFBundleVersion 1 NSExtension diff --git a/package-lock.json b/package-lock.json index 3ff871b8..fd0eaf05 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "bluewallet", - "version": "5.3.2", + "version": "5.3.3", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 9c25f67c..838f3c33 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "bluewallet", - "version": "5.3.2", + "version": "5.3.3", "license": "MIT", "devDependencies": { "@babel/core": "^7.6.2", From 78fdd045eb27cd0bb01e5eef8f7d20104728a1ce Mon Sep 17 00:00:00 2001 From: Overtorment Date: Fri, 17 Apr 2020 17:20:21 +0100 Subject: [PATCH 07/12] OPS: up dep electrum-mnemonic --- package-lock.json | 6 +++--- package.json | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index fd0eaf05..dff12690 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4461,9 +4461,9 @@ "from": "git+https://github.com/BlueWallet/rn-electrum-client.git#2a5bb11dd9a8d89f328049d9ed59bce49d88a15d" }, "electrum-mnemonic": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/electrum-mnemonic/-/electrum-mnemonic-1.0.5.tgz", - "integrity": "sha512-Wq5vFTTZzHu6w6GSxSNdSgEouqK5x7nky7XGQOnIvS5gx2on7EHysAqB6sSGOhhD5MnoT8nn9LhZPSBrDWkllw==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/electrum-mnemonic/-/electrum-mnemonic-1.0.7.tgz", + "integrity": "sha512-qi53UYOr+yxCBDVYzKm6a6yzomhHiZR43EuH2I3+teqizj2fapMDh0AyixB3id7ZnFCsiGnOxFqN4TzuzQy5dQ==", "requires": { "create-hmac": "^1.1.7", "pbkdf2": "^3.0.17", diff --git a/package.json b/package.json index 838f3c33..24feb843 100644 --- a/package.json +++ b/package.json @@ -80,7 +80,7 @@ "dayjs": "1.8.23", "ecurve": "1.0.6", "electrum-client": "git+https://github.com/BlueWallet/rn-electrum-client.git#2a5bb11dd9a8d89f328049d9ed59bce49d88a15d", - "electrum-mnemonic": "1.0.5", + "electrum-mnemonic": "1.0.7", "eslint-config-prettier": "6.10.0", "eslint-config-standard": "12.0.0", "eslint-config-standard-react": "7.0.2", From 68dc6e70ee0ee80185179a321fa21c72c73004d0 Mon Sep 17 00:00:00 2001 From: snyk-bot Date: Thu, 16 Apr 2020 05:51:00 +0000 Subject: [PATCH 08/12] fix: upgrade @react-native-community/async-storage from 1.7.1 to 1.8.1 Snyk has created this PR to upgrade @react-native-community/async-storage from 1.7.1 to 1.8.1. See this package in NPM: https://www.npmjs.com/package/@react-native-community/async-storage See this project in Snyk: https://app.snyk.io/org/bluewallet/project/4d0df22a-0152-410a-8584-6df0d0a596d4?utm_source=github&utm_medium=upgrade-pr --- package-lock.json | 6 +++--- package.json | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index dff12690..ff4de909 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1341,9 +1341,9 @@ } }, "@react-native-community/async-storage": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/@react-native-community/async-storage/-/async-storage-1.7.1.tgz", - "integrity": "sha512-/oX/x+EU4xNaqIaC/epVKzO8XghzImPA7l8cLz3USEFmtFiXFjBbTeeIFjjEm/u4/cv38Wi1xMEa10PHIWygRg==" + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/@react-native-community/async-storage/-/async-storage-1.8.1.tgz", + "integrity": "sha512-MA1fTp4SB7OOtDmNAwds6jIpiwwty1NIoFboWjEWkoyWW35zIuxlhHxD4joSy21aWEzUVwvv6JJ2hSsP/HTb7A==" }, "@react-native-community/blur": { "version": "3.4.1", diff --git a/package.json b/package.json index 24feb843..109d6808 100644 --- a/package.json +++ b/package.json @@ -59,7 +59,7 @@ }, "dependencies": { "@babel/preset-env": "7.8.7", - "@react-native-community/async-storage": "1.7.1", + "@react-native-community/async-storage": "1.8.1", "@react-native-community/blur": "3.4.1", "@react-native-community/slider": "2.0.8", "@react-native-community/viewpager": "3.3.0", From 68dc331eb3b8ab6e339281db9e0ece6c3bb575bd Mon Sep 17 00:00:00 2001 From: marcosrdz Date: Mon, 20 Apr 2020 00:03:36 -0400 Subject: [PATCH 09/12] REF: Receive Screen with React Hooks --- ios/Podfile.lock | 14 +- screen/receive/details.js | 345 +++++++++++++++++--------------------- 2 files changed, 165 insertions(+), 194 deletions(-) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index d5f1fd80..b24edf97 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -192,6 +192,8 @@ PODS: - React-jsinspector (0.61.5) - react-native-biometrics (2.0.0): - React + - react-native-blue-crypto (1.0.0): + - React - react-native-blur (0.8.0): - React - react-native-camera (3.21.0): @@ -255,7 +257,7 @@ PODS: - React - RemobileReactNativeQrcodeLocalImage (1.0.4): - React - - RNCAsyncStorage (1.7.1): + - RNCAsyncStorage (1.8.1): - React - RNDefaultPreference (1.4.1): - React @@ -263,7 +265,7 @@ PODS: - React - RNFS (2.16.6): - React - - RNGestureHandler (1.5.6): + - RNGestureHandler (1.6.1): - React - RNHandoff (0.0.3): - React @@ -315,6 +317,7 @@ DEPENDENCIES: - React-jsiexecutor (from `../node_modules/react-native/ReactCommon/jsiexecutor`) - React-jsinspector (from `../node_modules/react-native/ReactCommon/jsinspector`) - react-native-biometrics (from `../node_modules/react-native-biometrics`) + - react-native-blue-crypto (from `../node_modules/react-native-blue-crypto`) - "react-native-blur (from `../node_modules/@react-native-community/blur`)" - react-native-camera (from `../node_modules/react-native-camera`) - react-native-document-picker (from `../node_modules/react-native-document-picker`) @@ -398,6 +401,8 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native/ReactCommon/jsinspector" react-native-biometrics: :path: "../node_modules/react-native-biometrics" + react-native-blue-crypto: + :path: "../node_modules/react-native-blue-crypto" react-native-blur: :path: "../node_modules/@react-native-community/blur" react-native-camera: @@ -496,6 +501,7 @@ SPEC CHECKSUMS: React-jsiexecutor: d5525f9ed5f782fdbacb64b9b01a43a9323d2386 React-jsinspector: fa0ecc501688c3c4c34f28834a76302233e29dc0 react-native-biometrics: c892904948a32295b128f633bcc11eda020645c5 + react-native-blue-crypto: 23f1558ad3d38d7a2edb7e2f6ed1bc520ed93e56 react-native-blur: cad4d93b364f91e7b7931b3fa935455487e5c33c react-native-camera: a9749fa0c1162cdb2f56b93a5d97a72ebd56694f react-native-document-picker: e3516aff0dcf65ee0785d9bcf190eb10e2261154 @@ -517,11 +523,11 @@ SPEC CHECKSUMS: ReactCommon: 198c7c8d3591f975e5431bec1b0b3b581aa1c5dd ReactNativePrivacySnapshot: cc295e45dc22810e9ff2c93380d643de20a77015 RemobileReactNativeQrcodeLocalImage: 57aadc12896b148fb5e04bc7c6805f3565f5c3fa - RNCAsyncStorage: 8539fc80a0075fcc9c8e2dff84cd22dc5bf1dacf + RNCAsyncStorage: 00bdf63f7f1e0f11d3323533dba4f222e58bf092 RNDefaultPreference: 12d246dd2222e66dadcd76cc1250560663befc3a RNDeviceInfo: 12faae605ba42a1a5041c3c41a77834bc23f049d RNFS: 2bd9eb49dc82fa9676382f0585b992c424cd59df - RNGestureHandler: 911d3b110a7a233a34c4f800e7188a84b75319c6 + RNGestureHandler: 8f09cd560f8d533eb36da5a6c5a843af9f056b38 RNHandoff: d3b0754cca3a6bcd9b25f544f733f7f033ccf5fa RNQuickAction: 6d404a869dc872cde841ad3147416a670d13fa93 RNRate: d44a8bca6ee08f5d890ecccddaec2810955ffbb3 diff --git a/screen/receive/details.js b/screen/receive/details.js index 5b4e3f26..9be52613 100644 --- a/screen/receive/details.js +++ b/screen/receive/details.js @@ -1,6 +1,7 @@ -import React, { Component } from 'react'; +import React, { useEffect, useState, useCallback } from 'react'; import { View, InteractionManager, Platform, TextInput, KeyboardAvoidingView, Keyboard, StyleSheet, ScrollView } from 'react-native'; import QRCode from 'react-native-qrcode-svg'; +import { useNavigation, useNavigationParam } from 'react-navigation-hooks'; import bip21 from 'bip21'; import { BlueLoading, @@ -15,7 +16,6 @@ import { BlueSpacing20, BlueAlertWalletExportReminder, } from '../../BlueComponents'; -import PropTypes from 'prop-types'; import Privacy from '../../Privacy'; import Share from 'react-native-share'; import { Chain, BitcoinUnit } from '../../models/bitcoinUnits'; @@ -26,120 +26,113 @@ import Handoff from 'react-native-handoff'; const BlueApp = require('../../BlueApp'); const loc = require('../../loc'); -export default class ReceiveDetails extends Component { - static navigationOptions = ({ navigation }) => ({ - ...BlueNavigationStyle(navigation, true), - title: loc.receive.header, - headerLeft: null, - }); +const ReceiveDetails = () => { + const secret = useNavigationParam('secret'); + const [wallet, setWallet] = useState(); + const [isHandOffUseEnabled, setIsHandOffUseEnabled] = useState(false); + const [address, setAddress] = useState(''); + const [customLabel, setCustomLabel] = useState(); + const [customAmount, setCustomAmount] = useState(0); + const [bip21encoded, setBip21encoded] = useState(); + const [qrCodeSVG, setQrCodeSVG] = useState(); + const [isCustom, setIsCustom] = useState(false); + const [isCustomModalVisible, setIsCustomModalVisible] = useState(false); + const { navigate, goBack } = useNavigation(); - constructor(props) { - super(props); - let secret = props.navigation.state.params.secret || ''; - - this.state = { - secret: secret, - isHandOffUseEnabled: false, - address: '', - customLabel: '', - customAmount: 0, - bip21encoded: undefined, - isCustom: false, - isCustomModalVisible: false, - }; - } - - renderReceiveDetails = async () => { - this.wallet.setUserHasSavedExport(true); + const renderReceiveDetails = useCallback(async () => { + console.log('receive/details - componentDidMount'); + wallet.setUserHasSavedExport(true); await BlueApp.saveToDisk(); let address; - if (this.wallet.getAddressAsync) { - if (this.wallet.chain === Chain.ONCHAIN) { + if (wallet.getAddressAsync) { + if (wallet.chain === Chain.ONCHAIN) { try { - address = await Promise.race([this.wallet.getAddressAsync(), BlueApp.sleep(1000)]); + address = await Promise.race([wallet.getAddressAsync(), BlueApp.sleep(1000)]); } catch (_) {} if (!address) { // either sleep expired or getAddressAsync threw an exception console.warn('either sleep expired or getAddressAsync threw an exception'); - address = this.wallet._getExternalAddressByIndex(this.wallet.next_free_address_index); + address = wallet._getExternalAddressByIndex(wallet.next_free_address_index); } else { BlueApp.saveToDisk(); // caching whatever getAddressAsync() generated internally } - this.setState({ - address: address, - }); - } else if (this.wallet.chain === Chain.OFFCHAIN) { + setAddress(address); + } else if (wallet.chain === Chain.OFFCHAIN) { try { - await Promise.race([this.wallet.getAddressAsync(), BlueApp.sleep(1000)]); - address = this.wallet.getAddress(); + await Promise.race([wallet.getAddressAsync(), BlueApp.sleep(1000)]); + address = wallet.getAddress(); } catch (_) {} if (!address) { // either sleep expired or getAddressAsync threw an exception console.warn('either sleep expired or getAddressAsync threw an exception'); - address = this.wallet.getAddress(); + address = wallet.getAddress(); } else { BlueApp.saveToDisk(); // caching whatever getAddressAsync() generated internally } } - this.setState({ - address: address, - }); - } else if (this.wallet.getAddress) { - this.setState({ - address: this.wallet.getAddress(), - }); + setAddress(address); + } else if (wallet.getAddress) { + setAddress(wallet.getAddress()); } InteractionManager.runAfterInteractions(async () => { - const bip21encoded = bip21.encode(this.state.address); - this.setState({ bip21encoded }); + const bip21encoded = bip21.encode(address); + setBip21encoded(bip21encoded); }); - }; + }, [wallet]); - componentDidMount() { + useEffect(() => { Privacy.enableBlur(); - console.log('receive/details - componentDidMount'); - for (let w of BlueApp.getWallets()) { - if (w.getSecret() === this.state.secret) { - // found our wallet - this.wallet = w; - } - } - if (this.wallet) { - if (!this.wallet.getUserHasSavedExport()) { + setWallet(BlueApp.getWallets().find(w => w.getSecret() === secret)); + + if (wallet) { + if (!wallet.getUserHasSavedExport()) { BlueAlertWalletExportReminder({ - onSuccess: this.renderReceiveDetails, + onSuccess: renderReceiveDetails, onFailure: () => { - this.props.navigation.goBack(); - this.props.navigation.navigate('WalletExport', { - wallet: this.wallet, + goBack(); + navigate('WalletExport', { + wallet: wallet, }); }, }); } else { - this.renderReceiveDetails(); + renderReceiveDetails(); } } - HandoffSettings.isHandoffUseEnabled().then(value => this.setState({ isHandOffUseEnabled: value })); - } + HandoffSettings.isHandoffUseEnabled().then(setIsHandOffUseEnabled); + return () => Privacy.disableBlur(); + }, [goBack, navigate, renderReceiveDetails, secret, wallet]); - componentWillUnmount() { - Privacy.disableBlur(); - } + const dismissCustomAmountModal = () => { + Keyboard.dismiss(); + setIsCustomModalVisible(false); + }; - renderCustomAmountModal = () => { + const showCustomAmountModal = () => { + setIsCustomModalVisible(true); + }; + + const createCustomAmountAddress = () => { + setIsCustom(true); + setIsCustomModalVisible(false); + setBip21encoded(bip21.encode(address, { amount: customAmount, label: customLabel })); + }; + + const clearCustomAmount = () => { + setIsCustom(false); + setIsCustomModalVisible(false); + setCustomAmount(''); + setCustomLabel(''); + setBip21encoded(bip21.encode(address)); + }; + + const renderCustomAmountModal = () => { return ( - { - Keyboard.dismiss(); - this.setState({ isCustomModalVisible: false }); - }} - > + - this.setState({ customAmount: text })} /> + this.setState({ customLabel: text })} + onChangeText={setCustomLabel} placeholder={loc.receive.details.label} - value={this.state.customLabel || ''} + value={customLabel || ''} numberOfLines={1} style={{ flex: 1, marginHorizontal: 8, minHeight: 33 }} /> - { - this.setState({ - isCustom: true, - isCustomModalVisible: false, - bip21encoded: bip21.encode(this.state.address, { amount: this.state.customAmount, label: this.state.customLabel }), - }); - }} - /> + - { - this.setState({ - isCustom: false, - isCustomModalVisible: false, - customAmount: '', - customLabel: '', - bip21encoded: bip21.encode(this.state.addresss), - }); - }} - /> + @@ -197,87 +170,91 @@ export default class ReceiveDetails extends Component { ); }; - showCustomAmountModal = () => { - this.setState({ isCustomModalVisible: true }); + const handleShareButtonPressed = () => { + if (qrCodeSVG === undefined) { + Share.open({ message: bip21encoded }).catch(error => console.log(error)); + } else { + InteractionManager.runAfterInteractions(async () => { + qrCodeSVG.toDataURL(data => { + let shareImageBase64 = { + message: bip21encoded, + url: `data:image/png;base64,${data}`, + }; + Share.open(shareImageBase64).catch(error => console.log(error)); + }); + }); + } }; - render() { - return ( - - {this.state.isHandOffUseEnabled && this.state.address !== undefined && ( - - )} - - - {this.state.isCustom && ( - <> - - {this.state.customAmount} {BitcoinUnit.BTC} - - - {this.state.customLabel} - - - )} - {this.state.bip21encoded === undefined ? ( - - - - ) : ( - (this.qrCodeSVG = c)} - /> - )} - - - - - - { - if (this.qrCodeSVG === undefined) { - Share.open({ message: this.state.bip21encoded }).catch(error => console.log(error)); - } else { - InteractionManager.runAfterInteractions(async () => { - this.qrCodeSVG.toDataURL(data => { - let shareImageBase64 = { - message: this.state.bip21encoded, - url: `data:image/png;base64,${data}`, - }; - Share.open(shareImageBase64).catch(error => console.log(error)); - }); - }); - } - }} - title={loc.receive.details.share} - /> + return ( + + {isHandOffUseEnabled && address !== undefined && ( + + )} + + + {isCustom && ( + <> + + {customAmount} {BitcoinUnit.BTC} + + + {customLabel} + + + )} + {bip21encoded === undefined ? ( + + + ) : ( + + )} + + + + + + - {this.renderCustomAmountModal()} - - - ); - } -} + + {renderCustomAmountModal()} + + + ); +}; + +ReceiveDetails.navigationOptions = ({ navigation }) => ({ + ...BlueNavigationStyle(navigation, true), + title: loc.receive.header, + headerLeft: null, +}); + +export default ReceiveDetails; const styles = StyleSheet.create({ modalContent: { @@ -296,15 +273,3 @@ const styles = StyleSheet.create({ margin: 0, }, }); - -ReceiveDetails.propTypes = { - navigation: PropTypes.shape({ - goBack: PropTypes.func, - navigate: PropTypes.func, - state: PropTypes.shape({ - params: PropTypes.shape({ - secret: PropTypes.string, - }), - }), - }), -}; From 2305397547059afe9981dab340eb0583a3fbac29 Mon Sep 17 00:00:00 2001 From: snyk-bot Date: Sat, 18 Apr 2020 05:52:59 +0000 Subject: [PATCH 10/12] fix: upgrade @sentry/react-native from 1.3.1 to 1.3.5 Snyk has created this PR to upgrade @sentry/react-native from 1.3.1 to 1.3.5. See this package in NPM: https://www.npmjs.com/package/@sentry/react-native See this project in Snyk: https://app.snyk.io/org/bluewallet/project/4d0df22a-0152-410a-8584-6df0d0a596d4?utm_source=github&utm_medium=upgrade-pr --- package-lock.json | 114 +++++++++++++++++++++++++--------------------- package.json | 2 +- 2 files changed, 63 insertions(+), 53 deletions(-) diff --git a/package-lock.json b/package-lock.json index ff4de909..f4f003da 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1556,102 +1556,112 @@ "from": "git+https://github.com/BlueWallet/react-native-qrcode-local-image.git" }, "@sentry/browser": { - "version": "5.15.0", - "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-5.15.0.tgz", - "integrity": "sha512-9sgqWGaoT5jb3vk8sgQ1bz1LzhUf3oKoDMp/c6vX0reuA6Vz+/jwOC7a/FPWtQir2PwRJfbak2QOxw8W6Mwa3g==", + "version": "5.15.4", + "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-5.15.4.tgz", + "integrity": "sha512-l/auT1HtZM3KxjCGQHYO/K51ygnlcuOrM+7Ga8gUUbU9ZXDYw6jRi0+Af9aqXKmdDw1naNxr7OCSy6NBrLWVZw==", "requires": { - "@sentry/core": "5.15.0", - "@sentry/types": "5.15.0", - "@sentry/utils": "5.15.0", + "@sentry/core": "5.15.4", + "@sentry/types": "5.15.4", + "@sentry/utils": "5.15.4", "tslib": "^1.9.3" } }, "@sentry/cli": { - "version": "1.51.1", - "resolved": "https://registry.npmjs.org/@sentry/cli/-/cli-1.51.1.tgz", - "integrity": "sha512-JKYdoE5Pz8AQaupVQW3XOFTuff1UyLtxhcFzQPwQXiZEyXabo00dosX3YkqPKUwMv7LgxTOH23SJU8HwStAmFw==", + "version": "1.52.1", + "resolved": "https://registry.npmjs.org/@sentry/cli/-/cli-1.52.1.tgz", + "integrity": "sha512-XocAy3opa7bxWEbYQ9R/whbIb4BAX2YHXvfMoCwZRzLRy9cf85FYGQCMi8JA7wQd5PBmcxUh31AxcX7jAfMPCQ==", "requires": { "fs-copy-file-sync": "^1.1.1", "https-proxy-agent": "^4.0.0", - "mkdirp": "^0.5.1", + "mkdirp": "^0.5.4", "node-fetch": "^2.1.2", "progress": "2.0.0", "proxy-from-env": "^1.0.0" + }, + "dependencies": { + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "requires": { + "minimist": "^1.2.5" + } + } } }, "@sentry/core": { - "version": "5.15.0", - "resolved": "https://registry.npmjs.org/@sentry/core/-/core-5.15.0.tgz", - "integrity": "sha512-ujwHMwinPwuADoIBFjh1BiC6Li7RpEG3Mmo0MxOqKm7xKngkRUk5uH5e36roORnx+ngr/3NCe80QuvSqK7gQsw==", - "requires": { - "@sentry/hub": "5.15.0", - "@sentry/minimal": "5.15.0", - "@sentry/types": "5.15.0", - "@sentry/utils": "5.15.0", + "version": "5.15.4", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-5.15.4.tgz", + "integrity": "sha512-9KP4NM4SqfV5NixpvAymC7Nvp36Zj4dU2fowmxiq7OIbzTxGXDhwuN/t0Uh8xiqlkpkQqSECZ1OjSFXrBldetQ==", + "requires": { + "@sentry/hub": "5.15.4", + "@sentry/minimal": "5.15.4", + "@sentry/types": "5.15.4", + "@sentry/utils": "5.15.4", "tslib": "^1.9.3" } }, "@sentry/hub": { - "version": "5.15.0", - "resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-5.15.0.tgz", - "integrity": "sha512-wIDcaIuaYpg+Ma01NfFQTOnZLDCKSx2D06TTBqlo93WfMFNgyEgdMbU5Fk1PFZzjj2AMtzlc9DJzAfvt1hZx3w==", + "version": "5.15.4", + "resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-5.15.4.tgz", + "integrity": "sha512-1XJ1SVqadkbUT4zLS0TVIVl99si7oHizLmghR8LMFl5wOkGEgehHSoOydQkIAX2C7sJmaF5TZ47ORBHgkqclUg==", "requires": { - "@sentry/types": "5.15.0", - "@sentry/utils": "5.15.0", + "@sentry/types": "5.15.4", + "@sentry/utils": "5.15.4", "tslib": "^1.9.3" } }, "@sentry/integrations": { - "version": "5.15.0", - "resolved": "https://registry.npmjs.org/@sentry/integrations/-/integrations-5.15.0.tgz", - "integrity": "sha512-50OLHL26EMOCeyVlwLk9P7YocaUF02RYuCD3BPofTswgXSDe61Qy3ntT/RuOTiUV1sjmpc4FjDBJdtp/miFfOg==", + "version": "5.15.4", + "resolved": "https://registry.npmjs.org/@sentry/integrations/-/integrations-5.15.4.tgz", + "integrity": "sha512-GaEVQf4R+WBJvTOGptOHIFSylnH1JAvBQZ7c45jGIDBp+upqzeI67KD+HoM4sSNT2Y2i8DLTJCWibe34knz5Kw==", "requires": { - "@sentry/types": "5.15.0", - "@sentry/utils": "5.15.0", + "@sentry/types": "5.15.4", + "@sentry/utils": "5.15.4", "tslib": "^1.9.3" } }, "@sentry/minimal": { - "version": "5.15.0", - "resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-5.15.0.tgz", - "integrity": "sha512-VBkMfR6ahmuJrx4V51BNYd6XzGZ7GB8sfnBufMzqK6MsKe+g5oSyXeqHFd4oFC0co0YlFIw7IphF2JZLwVs0zA==", + "version": "5.15.4", + "resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-5.15.4.tgz", + "integrity": "sha512-GL4GZ3drS9ge+wmxkHBAMEwulaE7DMvAEfKQPDAjg2p3MfcCMhAYfuY4jJByAC9rg9OwBGGehz7UmhWMFjE0tw==", "requires": { - "@sentry/hub": "5.15.0", - "@sentry/types": "5.15.0", + "@sentry/hub": "5.15.4", + "@sentry/types": "5.15.4", "tslib": "^1.9.3" } }, "@sentry/react-native": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@sentry/react-native/-/react-native-1.3.1.tgz", - "integrity": "sha512-EsR3UW9hQJ7VHsS7nL5mKFa7iMQAHoelXiDJ94k9Y/aFm7pMA6Knpj4Gqo6rMqd2opGXLfO40b65VL8mJ4JDXw==", - "requires": { - "@sentry/browser": "^5.12.1", - "@sentry/core": "^5.12.0", - "@sentry/integrations": "^5.12.0", - "@sentry/types": "^5.12.0", - "@sentry/utils": "^5.12.0", + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@sentry/react-native/-/react-native-1.3.5.tgz", + "integrity": "sha512-shgh3Nq9f43CVvrFgb8wBr5sdOWX0T2j0GQ2h1+JR4YvB7vGjIq1xCEZWql4GeAiImnzg15JNnsLAsgOWxF84A==", + "requires": { + "@sentry/browser": "^5.15.2", + "@sentry/core": "^5.15.2", + "@sentry/integrations": "^5.15.2", + "@sentry/types": "^5.15.2", + "@sentry/utils": "^5.15.2", "@sentry/wizard": "^1.1.1" } }, "@sentry/types": { - "version": "5.15.0", - "resolved": "https://registry.npmjs.org/@sentry/types/-/types-5.15.0.tgz", - "integrity": "sha512-MC96wUAHhzRuH3xo4Qd+EXTOap8+d+SWbAdLBukScxuwhOSY/HNRh1TW17CuAu7s1oXa7xxO2ZCdyamSZinIiQ==" + "version": "5.15.4", + "resolved": "https://registry.npmjs.org/@sentry/types/-/types-5.15.4.tgz", + "integrity": "sha512-quPHPpeAuwID48HLPmqBiyXE3xEiZLZ5D3CEbU3c3YuvvAg8qmfOOTI6z4Z3Eedi7flvYpnx3n7N3dXIEz30Eg==" }, "@sentry/utils": { - "version": "5.15.0", - "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-5.15.0.tgz", - "integrity": "sha512-td+wSBdVUPO3mEPcEHZwJiVEQ0+wplJCHBvM1PHqwQd+miB2mQAaiSkzdAAHzUpTeqPBI3rzjWPn59WkCcVF5Q==", + "version": "5.15.4", + "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-5.15.4.tgz", + "integrity": "sha512-lO8SLBjrUDGADl0LOkd55R5oL510d/1SaI08/IBHZCxCUwI4TiYo5EPECq8mrj3XGfgCyq9osw33bymRlIDuSQ==", "requires": { - "@sentry/types": "5.15.0", + "@sentry/types": "5.15.4", "tslib": "^1.9.3" } }, "@sentry/wizard": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@sentry/wizard/-/wizard-1.1.1.tgz", - "integrity": "sha512-P07iMkndcT865SS4WMEBlzda4MXg1tOzPe8kg9p8vQlppWZ4ZyCgs5cDyTZk3b7HLffk+YPL4S116mfwzVdP7w==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@sentry/wizard/-/wizard-1.1.2.tgz", + "integrity": "sha512-z7Ck5uli91omT+xSGzOXA3XNj0IUFritzZ5Qjf/KcuSUZuyqLCH2olAR6pXl262tC6kBbWw/xb+AOgPsAQ7u/Q==", "requires": { "@sentry/cli": "^1.51.0", "chalk": "^2.4.1", diff --git a/package.json b/package.json index 109d6808..6b044681 100644 --- a/package.json +++ b/package.json @@ -64,7 +64,7 @@ "@react-native-community/slider": "2.0.8", "@react-native-community/viewpager": "3.3.0", "@remobile/react-native-qrcode-local-image": "git+https://github.com/BlueWallet/react-native-qrcode-local-image.git", - "@sentry/react-native": "1.3.1", + "@sentry/react-native": "1.3.5", "amplitude-js": "5.9.0", "bech32": "1.1.3", "bignumber.js": "9.0.0", From 2f34aaf3cc70f347493e64d9b858d867915be4b4 Mon Sep 17 00:00:00 2001 From: Overtorment Date: Mon, 20 Apr 2020 13:29:37 +0100 Subject: [PATCH 11/12] TST: ref detox --- tests/e2e/bluewallet.spec.js | 15 +++++++++++++++ tests/e2e/selftest.spec.js | 18 ------------------ 2 files changed, 15 insertions(+), 18 deletions(-) delete mode 100644 tests/e2e/selftest.spec.js diff --git a/tests/e2e/bluewallet.spec.js b/tests/e2e/bluewallet.spec.js index ccaa6db4..3d58558d 100644 --- a/tests/e2e/bluewallet.spec.js +++ b/tests/e2e/bluewallet.spec.js @@ -1,6 +1,21 @@ /* global it, describe, expect, element, by, waitFor, device */ describe('BlueWallet UI Tests', () => { + it('selftest passes', async () => { + await waitFor(element(by.id('WalletsList'))) + .toBeVisible() + .withTimeout(300 * 1000); + + // go to settings, press SelfTest and wait for OK + await element(by.id('SettingsButton')).tap(); + await element(by.id('AboutButton')).tap(); + await element(by.id('AboutScrollView')).swipe('up', 'fast', 1); // in case emu screen is small and it doesnt fit + await element(by.id('RunSelfTestButton')).tap(); + await waitFor(element(by.id('SelfTestOk'))) + .toBeVisible() + .withTimeout(300 * 1000); + }); + it('can encrypt storage, with plausible deniability', async () => { await yo('WalletsList'); diff --git a/tests/e2e/selftest.spec.js b/tests/e2e/selftest.spec.js deleted file mode 100644 index f1918551..00000000 --- a/tests/e2e/selftest.spec.js +++ /dev/null @@ -1,18 +0,0 @@ -/* global it, describe, element, by, waitFor */ - -describe('BlueWallet Selftest', () => { - it('passes', async () => { - await waitFor(element(by.id('WalletsList'))) - .toBeVisible() - .withTimeout(300 * 1000); - - // go to settings, press SelfTest and wait for OK - await element(by.id('SettingsButton')).tap(); - await element(by.id('AboutButton')).tap(); - await element(by.id('AboutScrollView')).swipe('up', 'fast', 1); // in case emu screen is small and it doesnt fit - await element(by.id('RunSelfTestButton')).tap(); - await waitFor(element(by.id('SelfTestOk'))) - .toBeVisible() - .withTimeout(300 * 1000); - }); -}); From 7c87fb55dca205eaa7ce6d838ed4c9bfa4ff65ca Mon Sep 17 00:00:00 2001 From: Overtorment Date: Mon, 20 Apr 2020 14:43:58 +0100 Subject: [PATCH 12/12] REF: new Buffer() deprecation warning fix (closes #995) --- blue_modules/bip38/scryptsy/lib/scrypt.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/blue_modules/bip38/scryptsy/lib/scrypt.js b/blue_modules/bip38/scryptsy/lib/scrypt.js index 42a167fd..5904e3de 100644 --- a/blue_modules/bip38/scryptsy/lib/scrypt.js +++ b/blue_modules/bip38/scryptsy/lib/scrypt.js @@ -10,13 +10,13 @@ async function scrypt (key, salt, N, r, p, dkLen, progressCallback) { if (N > MAX_VALUE / 128 / r) throw Error('Parameter N is too large') if (r > MAX_VALUE / 128 / p) throw Error('Parameter r is too large') - var XY = new Buffer(256 * r) - var V = new Buffer(128 * r * N) + var XY = Buffer.alloc(256 * r) + var V = Buffer.alloc(128 * r * N) // pseudo global var B32 = new Int32Array(16) // salsa20_8 var x = new Int32Array(16) // salsa20_8 - var _X = new Buffer(64) // blockmix_salsa8 + var _X = Buffer.alloc(64) // blockmix_salsa8 // pseudo global var B = pbkdf2.pbkdf2Sync(key, salt, 1, p * 128 * r, 'sha256')