Browse Source

FIX: crash on send from watch-only wallet (closes #1023)

master
Overtorment 5 years ago
parent
commit
c157b108ef
  1. 12
      screen/send/details.js
  2. 4
      screen/send/psbtWithHardwareWallet.js
  3. 4
      screen/wallets/transactions.js
  4. 63
      tests/e2e/bluewallet.spec.js

12
screen/send/details.js

@ -67,10 +67,6 @@ export default class SendDetails extends Component {
this.keyboardDidShowListener = Keyboard.addListener('keyboardDidShow', this._keyboardDidShow);
this.keyboardDidHideListener = Keyboard.addListener('keyboardDidHide', this._keyboardDidHide);
let fromAddress;
if (props.navigation.state.params) fromAddress = props.navigation.state.params.fromAddress;
let fromSecret;
if (props.navigation.state.params) fromSecret = props.navigation.state.params.fromSecret;
/** @type {LegacyWallet} */
let fromWallet = null;
if (props.navigation.state.params) fromWallet = props.navigation.state.params.fromWallet;
@ -83,8 +79,6 @@ export default class SendDetails extends Component {
} else {
if (!fromWallet && wallets.length > 0) {
fromWallet = wallets[0];
fromAddress = fromWallet.getAddress();
fromSecret = fromWallet.getSecret();
}
this.state = {
isLoading: false,
@ -93,9 +87,7 @@ export default class SendDetails extends Component {
isAdvancedTransactionOptionsVisible: false,
isTransactionReplaceable: fromWallet.type === HDSegwitBech32Wallet.type,
recipientsScrollIndex: 0,
fromAddress,
fromWallet,
fromSecret,
addresses: [],
memo: '',
networkTransactionFees: new NetworkTransactionFee(1, 1, 1),
@ -466,7 +458,7 @@ export default class SendDetails extends Component {
onWalletSelect = wallet => {
const changeWallet = () => {
this.setState({ fromAddress: wallet.getAddress(), fromSecret: wallet.getSecret(), fromWallet: wallet }, () => {
this.setState({ fromWallet: wallet }, () => {
this.renderNavigationHeader();
this.props.navigation.pop();
});
@ -1012,9 +1004,7 @@ SendDetails.propTypes = {
params: PropTypes.shape({
amount: PropTypes.number,
address: PropTypes.string,
fromAddress: PropTypes.string,
satoshiPerByte: PropTypes.string,
fromSecret: PropTypes.fromSecret,
fromWallet: PropTypes.fromWallet,
memo: PropTypes.string,
uri: PropTypes.string,

4
screen/send/psbtWithHardwareWallet.js

@ -293,7 +293,9 @@ export default class PsbtWithHardwareWallet extends Component {
<ScrollView centerContent contentContainerStyle={{ flexGrow: 1, justifyContent: 'space-between' }}>
<View style={{ flexDirection: 'row', justifyContent: 'center', paddingTop: 16, paddingBottom: 16 }}>
<BlueCard>
<BlueText>This is partially signed bitcoin transaction (PSBT). Please finish signing it with your hardware wallet.</BlueText>
<BlueText testID={'TextHelperForPSBT'}>
This is partially signed bitcoin transaction (PSBT). Please finish signing it with your hardware wallet.
</BlueText>
<BlueSpacing20 />
<QRCode
value={this.state.isFirstPSBTAlreadyBase64 ? this.state.psbt : this.state.psbt.toBase64()}

4
screen/wallets/transactions.js

@ -428,7 +428,6 @@ export default class WalletTransactions extends Component {
}
this.props.navigation.navigate('SendDetails', {
memo: loc.lnd.refill_lnd_balance,
fromSecret: wallet.getSecret(),
address: toAddress,
fromWallet: wallet,
});
@ -446,8 +445,6 @@ export default class WalletTransactions extends Component {
navigateToSendScreen = () => {
this.props.navigation.navigate('SendDetails', {
fromAddress: this.state.wallet.getAddress(),
fromSecret: this.state.wallet.getSecret(),
fromWallet: this.state.wallet,
});
};
@ -470,6 +467,7 @@ export default class WalletTransactions extends Component {
this.setState({ isLoading: false });
this.props.navigation.navigate(this.state.wallet.chain === Chain.ONCHAIN ? 'SendDetails' : 'ScanLndInvoice', {
fromSecret: this.state.wallet.getSecret(),
// ScanLndInvoice actrually uses `fromSecret` so keeping it for now
uri: ret.data ? ret.data : ret,
fromWallet: this.state.wallet,
});

63
tests/e2e/bluewallet.spec.js

@ -309,30 +309,14 @@ describe('BlueWallet UI Tests', () => {
console.error('process.env.HD_MNEMONIC_BIP84 not set, skipped');
return;
}
await yo('WalletsList');
// going to Import Wallet screen and importing mnemonic for existing BIP84 wallet with real balance
await element(by.id('CreateAWallet')).tap();
await element(by.id('ImportWallet')).tap();
await element(by.id('MnemonicInput')).typeText(process.env.HD_MNEMONIC_BIP84);
try {
await element(by.id('DoImport')).tap();
} catch (_) {}
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
await element(by.text('Imported HD SegWit (BIP84 Bech32 Native)')).tap();
// label might change in the future; see HDSegwitBech32Wallet.typeReadable
expect(element(by.id('WalletBalance'))).toHaveText('0.00105526 BTC');
await helperImportWallet(process.env.HD_MNEMONIC_BIP84, 'Imported HD SegWit (BIP84 Bech32 Native)', '0.00105526 BTC');
// lets create real transaction:
await element(by.id('SendButton')).tap();
await element(by.id('AddressInput')).typeText('bc1q063ctu6jhe5k4v8ka99qac8rcm2tzjjnuktyrl');
await element(by.id('BitcoinAmountInput')).typeText('0.0005\n');
await sleep(5000);
if (process.env.TRAVIS) await sleep(5000);
try {
await element(by.id('CreateTransactionButton')).tap();
} catch (_) {}
@ -370,11 +354,31 @@ describe('BlueWallet UI Tests', () => {
}
let transaction = bitcoin.Transaction.fromHex(txhex);
assert.strictEqual(transaction.ins.length, 2);
assert.ok(transaction.ins.length === 1 || transaction.ins.length === 2); // depending on current fees gona use either 1 or 2 inputs
assert.strictEqual(transaction.outs.length, 2);
assert.strictEqual(bitcoin.address.fromOutputScript(transaction.outs[0].script), 'bc1q063ctu6jhe5k4v8ka99qac8rcm2tzjjnuktyrl'); // to address
assert.strictEqual(transaction.outs[0].value, 50000);
});
it('can import zpub as watch-only and create PSBT', async () => {
await helperImportWallet(
'zpub6r7jhKKm7BAVx3b3nSnuadY1WnshZYkhK8gKFoRLwK9rF3Mzv28BrGcCGA3ugGtawi1WLb2vyjQAX9ZTDGU5gNk2bLdTc3iEXr6tzR1ipNP',
'Imported Watch-only',
'0.002 BTC',
);
await element(by.id('SendButton')).tap();
await element(by.text('OK')).tap();
await element(by.id('AddressInput')).typeText('bc1q063ctu6jhe5k4v8ka99qac8rcm2tzjjnuktyrl');
await element(by.id('BitcoinAmountInput')).typeText('0.0005\n');
if (process.env.TRAVIS) await sleep(5000);
try {
await element(by.id('CreateTransactionButton')).tap();
} catch (_) {}
await yo('TextHelperForPSBT');
});
});
async function sleep(ms) {
@ -410,3 +414,24 @@ async function helperCreateWallet(walletName) {
await expect(element(by.id('WalletsList'))).toBeVisible();
await expect(element(by.id(walletName || 'cr34t3d'))).toBeVisible();
}
async function helperImportWallet(importText, expectedWalletLabel, expectedBalance) {
await yo('WalletsList');
// going to Import Wallet screen and importing mnemonic
await element(by.id('CreateAWallet')).tap();
await element(by.id('ImportWallet')).tap();
await element(by.id('MnemonicInput')).typeText(importText);
try {
await element(by.id('DoImport')).tap();
} catch (_) {}
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
await element(by.text(expectedWalletLabel)).tap();
// label might change in the future
expect(element(by.id('WalletBalance'))).toHaveText(expectedBalance);
}

Loading…
Cancel
Save