Merge remote-tracking branch 'origin/master' into buttonsMultisig

This commit is contained in:
Overtorment 2020-11-05 17:35:58 +00:00
commit ad1eaec4df
8 changed files with 118 additions and 40 deletions

View file

@ -12,6 +12,9 @@
"dont_allow": "Ne dovoli",
"yes": "Da",
"no": "Ne",
"save": "Shrani",
"seed": "Seme",
"wallet_key": "Ključ denarnice",
"invalid_animated_qr_code_fragment" : "Neveljaven del animirane QR kode, prosimo poskusite ponovno",
"file_saved": "Datoteka ({filePath}) je bila shranjena v mapo Prenosi."
},
@ -317,7 +320,7 @@
"transactions_count": "število transakcij"
},
"wallets": {
"add_bitcoin_explain": "Simple and powerful Bitcoin wallet",
"add_bitcoin_explain": "Preprosta in zmogljiva Bitcoin denarnica",
"add_bitcoin": "Bitcoin",
"add_create": "Ustvari",
"add_entropy_generated": "{gen} bajtov ustvarjene entropije",
@ -326,7 +329,7 @@
"add_import_wallet": "Uvozi denarnico",
"import_file": "Uvozi datoteko",
"add_lightning": "Lightning",
"add_lightning_explain": "For spending with instant transactions",
"add_lightning_explain": "Za hitre vsakodnevne transakcije",
"add_lndhub": "Povežite se s svojim LNDHub-om",
"add_lndhub_error": "Podan naslov vozlišča ni veljavno vozlišče LNDHub.",
"add_lndhub_placeholder": "naslov vašega vozlišča",
@ -391,10 +394,11 @@
"xpub_title": "XPUB denarnice"
},
"multisig": {
"multisig_vault": "Vault",
"multisig_vault_explain": "Best security for large amounts",
"multisig_vault": "Trezor",
"multisig_vault_explain": "Največja varnost za višje zneske",
"provide_signature": "Vnesite podpis",
"vault_key": "Ključ trezorja {number}",
"required_keys_out_of_total": "Zahtevani ključi od vseh",
"fee": "Omrežnina: {number}",
"fee_btc": "{number} BTC",
"confirm": "Potrditev",
@ -404,6 +408,41 @@
"scan_or_import_file": "Skenirajte ali uvozite datoteko",
"export_coordination_setup": "izvoz koordinacijskih nastavitev",
"cosign_this_transaction": "Sopodpis te transakcije?",
"co_sign_transaction": "Sopodpis QR-airgapped transakcije"
"lets_start": "Začnimo",
"create": "Ustvari",
"provide_key": "Vnesite ključ",
"native_segwit_title": "Najb. praksa",
"wrapped_segwit_title": "Najb. združljivost",
"legacy_title": "Zastarelo",
"co_sign_transaction": "Sopodpis QR-airgapped transakcije",
"what_is_vault": "Trezor je",
"what_is_vault_numberOfWallets": " {m}-od-{n} multisig ",
"what_is_vault_wallet": "denarnica",
"vault_advanced_customize": "Nastavitve trezorja...",
"needs": "Zahtevana sta",
"what_is_vault_description_number_of_vault_keys": " {m} ključa trezorja, ",
"what_is_vault_description_to_spend": "tretji pa\npredstavlja rezervo.",
"quorum": "{m} od {n} kvorum",
"quorum_header": "Kvorum",
"of": "od",
"wallet_type": "Tip denarnice",
"view_key": "prikaži",
"invalid_mnemonics": "Zdi se, da to mnemonično seme ni veljavno",
"invalid_cosigner": "Neveljavni podatki sopodpisnika",
"invalid_cosigner_format": "Nepravilen sopodpisnik: to ni sopodpisnik za {format} obliko",
"create_new_key": "Ustvari novega",
"scan_or_open_file": "Skenirajte ali odprite datoteko",
"i_have_mnemonics": "Za ta ključ imam seme...",
"please_write_down_mnemonics": "Prosimo, zapišite si seznam besed (mnemonično seme) na list papirja. Lahko si zapišete tudi pozneje.",
"i_wrote_it_down": "V redu, sem si zapisal",
"type_your_mnemonics": "Vnesite seme za uvoz obstoječega ključa trezorja",
"this_is_cosigners_xpub": "To je xpub sopodpisnika, pripravljen za uvoz v drugo denarnico. Varno ga lahko delite.",
"wallet_key_created": "Ključ trezorja je bil ustvarjen. Vzemite si trenutek, ter zapišite seznam besed (mnemonično seme) na list papirja.",
"are_you_sure_seed_will_be_lost": "Ali ste prepričani? Če nimate varnostne kopije, bo vaše mnemonično seme izgubljeno",
"forget_this_seed": "Pozabi to seme in uporabi xpub",
"invalid_fingerprint": "Prstni odtis (fingerprint) tega semena se ne ujema s sopodpisnikovim",
"view_edit_cosigners": "Prikaži/uredi sopodpisnike",
"this_cosigner_is_already_imported": "Ta sopodpisnik je že uvožen",
"view_edit_cosigners_title": "Urejanje sopodpisnikov"
}
}

View file

@ -70,6 +70,7 @@ const styles = StyleSheet.create({
position: 'absolute',
},
backdoorInputWrapper: { position: 'absolute', left: '5%', top: '0%', width: '90%', height: '70%', backgroundColor: 'white' },
progressWrapper: { position: 'absolute', right: '0%', top: '0%', backgroundColor: 'white' },
backdoorInput: {
height: '50%',
marginTop: 5,
@ -95,6 +96,8 @@ const ScanQRCode = () => {
const isFocused = useIsFocused();
const [cameraStatus, setCameraStatus] = useState(RNCamera.Constants.CameraStatus.PENDING_AUTHORIZATION);
const [backdoorPressed, setBackdoorPressed] = useState(0);
const [urTotal, setUrTotal] = useState(0);
const [urHave, setUrHave] = useState(0);
const [backdoorText, setBackdoorText] = useState('');
const [backdoorVisible, setBackdoorVisible] = useState(false);
const [animatedQRCodeData, setAnimatedQRCodeData] = useState({});
@ -111,6 +114,8 @@ const ScanQRCode = () => {
try {
const [index, total] = extractSingleWorkload(ur);
animatedQRCodeData[index + 'of' + total] = ur;
setUrTotal(total);
setUrHave(Object.values(animatedQRCodeData).length);
if (Object.values(animatedQRCodeData).length === total) {
const payload = decodeUR(Object.values(animatedQRCodeData));
// lets look inside that data
@ -264,6 +269,14 @@ const ScanQRCode = () => {
<Icon name="file-import" type="material-community" color="#ffffff" />
</TouchableOpacity>
)}
{urTotal > 0 && (
<View style={styles.progressWrapper} testID="UrProgressBar">
<BlueTextHooks>
{urHave} / {urTotal}
</BlueTextHooks>
</View>
)}
{backdoorVisible && (
<View style={styles.backdoorInputWrapper}>
<BlueTextHooks>Provide QR code contents manually:</BlueTextHooks>
@ -291,6 +304,8 @@ const ScanQRCode = () => {
// this might be a json string (for convenience - in case there are "\n" in there)
} catch (_) {
data = backdoorText;
} finally {
setBackdoorText('');
}
if (data) onBarCodeRead({ data });

View file

@ -9,16 +9,6 @@ import { BitcoinUnit } from '../../models/bitcoinUnits';
import PropTypes from 'prop-types';
import ReactNativeHapticFeedback from 'react-native-haptic-feedback';
import Biometric from '../../class/biometrics';
import {
HDLegacyElectrumSeedP2PKHWallet,
HDLegacyP2PKHWallet,
HDSegwitBech32Wallet,
HDSegwitP2SHWallet,
HDLegacyBreadwalletWallet,
LegacyWallet,
SegwitP2SHWallet,
SegwitBech32Wallet,
} from '../../class';
import loc, { formatBalance, formatBalanceWithoutSuffix } from '../../loc';
import { BlueCurrentTheme } from '../../components/themes';
import Notifications from '../../blue_modules/notifications';
@ -89,21 +79,7 @@ export default class Confirm extends Component {
}
}
// wallets that support new createTransaction() instead of deprecated createTx()
if (
[
HDSegwitBech32Wallet.type,
HDSegwitP2SHWallet.type,
HDLegacyP2PKHWallet.type,
HDLegacyBreadwalletWallet.type,
HDLegacyElectrumSeedP2PKHWallet.type,
LegacyWallet.type,
SegwitP2SHWallet.type,
SegwitBech32Wallet.type,
].includes(this.state.fromWallet.type)
) {
amount = formatBalanceWithoutSuffix(amount, BitcoinUnit.BTC, false);
}
this.context.fetchAndSaveWalletTransactions(this.state.fromWallet.getID());
this.props.navigation.navigate('Success', {

View file

@ -42,7 +42,7 @@ const WalletsAddMultisig = () => {
color: colors.alternativeTextColor,
},
selectedItem: {
backgroundColor: colors.buttonDisabledTextColor,
backgroundColor: colors.elevated,
},
deSelectedItem: {
backgroundColor: 'transparent',
@ -146,21 +146,21 @@ const WalletsAddMultisig = () => {
onPress={setFormatP2wsh}
title={`${loc.multisig.native_segwit_title} (${MultisigHDWallet.FORMAT_P2WSH})`}
checkmark={isP2wsh()}
containerStyle={[styles.borderRadius6, isP2wsh() ? stylesHook.selectedItem : stylesHook.deSelectedItem]}
containerStyle={[styles.borderRadius6, styles.item, isP2wsh() ? stylesHook.selectedItem : stylesHook.deSelectedItem]}
/>
<BlueListItem
bottomDivider={false}
onPress={setFormatP2shP2wsh}
title={`${loc.multisig.wrapped_segwit_title} (${MultisigHDWallet.FORMAT_P2SH_P2WSH})`}
checkmark={isP2shP2wsh()}
containerStyle={[styles.borderRadius6, isP2shP2wsh() ? stylesHook.selectedItem : stylesHook.deSelectedItem]}
containerStyle={[styles.borderRadius6, styles.item, isP2shP2wsh() ? stylesHook.selectedItem : stylesHook.deSelectedItem]}
/>
<BlueListItem
bottomDivider={false}
onPress={setFormatP2sh}
title={`${loc.multisig.legacy_title} (${MultisigHDWallet.FORMAT_P2SH})`}
checkmark={isP2sh()}
containerStyle={[styles.borderRadius6, isP2sh() ? stylesHook.selectedItem : stylesHook.deSelectedItem]}
containerStyle={[styles.borderRadius6, styles.item, isP2sh() ? stylesHook.selectedItem : stylesHook.deSelectedItem]}
/>
</View>
</KeyboardAvoidingView>
@ -239,6 +239,9 @@ const styles = StyleSheet.create({
justifyContent: 'flex-end',
margin: 0,
},
item: {
paddingHorizontal: 0,
},
descriptionContainer: {
alignContent: 'center',
justifyContent: 'center',

View file

@ -1,5 +1,5 @@
/* global alert */
import React, { useRef, useState } from 'react';
import React, { useContext, useRef, useState } from 'react';
import {
ActivityIndicator,
FlatList,
@ -32,7 +32,6 @@ import Modal from 'react-native-modal';
import { getSystemName } from 'react-native-device-info';
import ImagePicker from 'react-native-image-picker';
import ScanQRCode from '../send/ScanQRCode';
import WalletImport from '../../class/wallet-import';
import QRCode from 'react-native-qrcode-svg';
import { SquareButton } from '../../components/SquareButton';
import { SafeAreaView } from 'react-native-safe-area-context';
@ -43,13 +42,17 @@ import MultipleStepsListItem, {
import Clipboard from '@react-native-community/clipboard';
import showPopupMenu from 'react-native-popup-menu-android';
import ToolTip from 'react-native-tooltip';
import ReactNativeHapticFeedback from 'react-native-haptic-feedback';
import { BlueStorageContext } from '../../blue_modules/storage-context';
const A = require('../../blue_modules/analytics');
const fs = require('../../blue_modules/fs');
const isDesktop = getSystemName() === 'Mac OS X';
const LocalQRCode = require('@remobile/react-native-qrcode-local-image');
const staticCache = {};
const WalletsAddMultisigStep2 = () => {
const { addWallet, saveToDisk, setNewWalletAdded } = useContext(BlueStorageContext);
const { colors } = useTheme();
const navigation = useNavigation();
@ -125,7 +128,7 @@ const WalletsAddMultisigStep2 = () => {
},
});
const onCreate = () => {
const onCreate = async () => {
setIsLoading(true);
const w = new MultisigHDWallet();
w.setM(m);
@ -149,7 +152,14 @@ const WalletsAddMultisigStep2 = () => {
w.addCosigner(cc[0], cc[1], cc[2]);
}
w.setLabel('Multisig Vault');
WalletImport._saveWallet(w);
await w.fetchBalance();
addWallet(w);
await saveToDisk();
setNewWalletAdded(true);
A(A.ENUM.CREATED_WALLET);
ReactNativeHapticFeedback.trigger('notificationSuccess', { ignoreAndroidSystemSettings: false });
navigation.dangerouslyGetParent().pop();
};

View file

@ -234,7 +234,7 @@ const WalletsImport = () => {
onPress={importButtonPressed}
/>
<BlueSpacing20 />
<BlueButtonLink title={loc.wallets.import_scan_qr} onPress={importScan} />
<BlueButtonLink title={loc.wallets.import_scan_qr} onPress={importScan} testID="ScanImport" />
</>
</View>
{Platform.select({

View file

@ -551,6 +551,41 @@ describe('BlueWallet UI Tests', () => {
expect(element(by.id('TransactionValue'))).toHaveText('0.0001');
expect(element(by.id('TransactionAddress'))).toHaveText('BC1QH6TF004TY7Z7UN2V5NTU4MKF630545GVHS45U7');
});
it('can import multisig setup from UR (ver1) QRs 2 frames', async () => {
await yo('WalletsList');
await element(by.id('WalletsList')).swipe('left', 'fast', 1); // in case emu screen is small and it doesnt fit
// going to Import Wallet screen and importing mnemonic
await element(by.id('CreateAWallet')).tap();
await element(by.id('ImportWallet')).tap();
await element(by.id('ScanImport')).tap();
const urs = [
'UR:BYTES/1OF2/J8RX04F2WJ9SSY577U30R55ELM4LUCJCXJVJTD60SYV9A286Q0AQH7QXL6/TYQMJGEQGFK82E2HV9KXCET5YPXH2MR5D9EKJEEQWDJHGATSYPNXJMR9PG3JQARGD9EJQENFD3JJQCM0DE6XZ6TWWVSX7MNV0YS8QATZD35KXGRTV4UHXGRPDEJZQ6TNYPEKZEN9YP6X7Z3RYPJXJUM5WF5KYAT5V5SXZMT0DENJQCM0WD5KWMN9WFES5GC2FESK6EF6YPXH2MR5D9EKJEEQ2ESH2MR5PFGX7MRFVDUN5GPJYPHKVGPJPFZX2UNFWESHG6T0DCAZQMF0XSUZWTESYUHNQFE0XGNS53N0WFKKZAP6YPGRY46NFQ9Q53PNXAZ5Z3PC8QAZQKNSW43RWDRFDFCXV6Z92F9YU6NGGD94S5NNWP2XGNZ22C6K2M69D4F4YKNYFPC5GANS',
'UR:BYTES/2OF2/J8RX04F2WJ9SSY577U30R55ELM4LUCJCXJVJTD60SYV9A286Q0AQH7QXL6/8944VARY2EZHJ62CDVMHQKRC2F3XVKN629M8X3ZXWPNYGJZ9FPT8G4NS0Q6YG73EG3R42468DCE9S6E40FRN2AF5X4G4GNTNT9FNYAN2DA5YU5G2PGCNVWZYGSMRQVE6YPD8QATZXU6K6S298PZK57TC2DAX772SD4RKUEP4G5MY672YXAQ5C36WDEJ8YA2HWC6NY7RS0F5K6KJ3FD6KKAMKG4N9S4ZGW9K5SWRWVF3XXDNRVDGR2APJV9XNXMTHWVEHQJ6E2DHYKUZTF4XHJARYVF8Y2KJX24UYK7N6W3V5VNFC2PHQ5ZSJDYL5T',
];
await waitFor(element(by.id('UrProgressBar'))).toBeNotVisible();
for (const ur of urs) {
// tapping 10 times invisible button is a backdoor:
for (let c = 0; c <= 10; c++) {
await element(by.id('ScanQrBackdoorButton')).tap();
}
await element(by.id('scanQrBackdoorInput')).replaceText(ur);
await element(by.id('scanQrBackdoorOkButton')).tap();
await waitFor(element(by.id('UrProgressBar'))).toBeVisible();
}
if (process.env.TRAVIS) await sleep(60000);
await sup('OK', 3 * 61000); // waiting for wallet import
await element(by.text('OK')).tap();
// ok, wallet imported
// lets go inside wallet
const expectedWalletLabel = 'Multisig Vault';
await element(by.text(expectedWalletLabel)).tap();
});
});
async function sleep(ms) {

View file

@ -46,6 +46,6 @@ describe('multisig-hd-wallet', () => {
await w.fetchBalance();
await w.fetchTransactions();
assert.strictEqual(w.getTransactions().length, 5);
assert.ok(w.getTransactions().length >= 6);
});
});