mirror of
https://github.com/BlueWallet/BlueWallet.git
synced 2025-02-24 15:36:59 +01:00
Merge pull request #2164 from BlueWallet/fix-close-camera-scan
FIX: camera is not closing after QR with PSBT (UR) scanned from hw wa…
This commit is contained in:
commit
4e35391ecc
4 changed files with 125 additions and 9 deletions
|
@ -70,7 +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' },
|
||||
progressWrapper: { position: 'absolute', right: '50%', top: '50%', backgroundColor: 'rgba(255, 255, 255, 0.1)' },
|
||||
backdoorInput: {
|
||||
height: '50%',
|
||||
marginTop: 5,
|
||||
|
@ -321,7 +321,7 @@ const ScanQRCode = () => {
|
|||
// tapping it 10 times fires prompt dialog asking for a string thats gona be passed to onBarCodeRead.
|
||||
// this allows to mock and test QR scanning in e2e tests
|
||||
setBackdoorPressed(backdoorPressed + 1);
|
||||
if (backdoorPressed < 10) return;
|
||||
if (backdoorPressed < 5) return;
|
||||
setBackdoorPressed(0);
|
||||
setBackdoorVisible(true);
|
||||
}}
|
||||
|
|
|
@ -818,6 +818,50 @@ export default class SendDetails extends Component {
|
|||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* same as `importTransaction`, but opens camera instead.
|
||||
*
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
importQrTransaction = async () => {
|
||||
if (this.state.fromWallet.type !== WatchOnlyWallet.type) {
|
||||
alert('Error: importing transaction in non-watchonly wallet (this should never happen)');
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState({ isAdvancedTransactionOptionsVisible: false });
|
||||
this.props.navigation.navigate('ScanQRCodeRoot', {
|
||||
screen: 'ScanQRCode',
|
||||
params: {
|
||||
onBarScanned: this.importQrTransactionOnBarScanned,
|
||||
showFileImportButton: false,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
importQrTransactionOnBarScanned = async ret => {
|
||||
this.props.navigation.dangerouslyGetParent().pop();
|
||||
if (!ret.data) ret = { data: ret };
|
||||
if (ret.data.toUpperCase().startsWith('UR')) {
|
||||
alert('BC-UR not decoded. This should never happen');
|
||||
} else if (ret.data.indexOf('+') === -1 && ret.data.indexOf('=') === -1 && ret.data.indexOf('=') === -1) {
|
||||
// this looks like NOT base64, so maybe its transaction's hex
|
||||
// we dont support it in this flow
|
||||
} else {
|
||||
// psbt base64?
|
||||
|
||||
// we construct PSBT object and pass to next screen
|
||||
// so user can do smth with it:
|
||||
const psbt = bitcoin.Psbt.fromBase64(ret.data);
|
||||
this.props.navigation.navigate('PsbtWithHardwareWallet', {
|
||||
memo: this.state.memo,
|
||||
fromWallet: this.state.fromWallet,
|
||||
psbt,
|
||||
});
|
||||
this.setState({ isLoading: false, isAdvancedTransactionOptionsVisible: false });
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* watch-only wallets with enabled HW wallet support have different flow. we have to show PSBT to user as QR code
|
||||
* so he can scan it and sign it. then we have to scan it back from user (via camera and QR code), and ask
|
||||
|
@ -1033,6 +1077,17 @@ export default class SendDetails extends Component {
|
|||
onPress={this.importTransaction}
|
||||
/>
|
||||
)}
|
||||
{this.state.fromWallet.type === WatchOnlyWallet.type &&
|
||||
this.state.fromWallet.isHd() &&
|
||||
this.state.fromWallet.getSecret().startsWith('zpub') && (
|
||||
<BlueListItem
|
||||
testID="ImportQrTransactionButton"
|
||||
title={loc.send.details_adv_import + ' (QR)'}
|
||||
hideChevron
|
||||
component={TouchableOpacity}
|
||||
onPress={this.importQrTransaction}
|
||||
/>
|
||||
)}
|
||||
{this.state.fromWallet.type === MultisigHDWallet.type && (
|
||||
<BlueListItem
|
||||
title={loc.send.details_adv_import}
|
||||
|
|
|
@ -341,6 +341,8 @@ export default class PsbtWithHardwareWallet extends Component {
|
|||
this.props.navigation.navigate('ScanQRCodeRoot', {
|
||||
screen: 'ScanQRCode',
|
||||
params: {
|
||||
launchedBy: this.props.route.name,
|
||||
showFileImportButton: false,
|
||||
onBarScanned: this.onBarScanned,
|
||||
},
|
||||
});
|
||||
|
|
|
@ -403,7 +403,7 @@ describe('BlueWallet UI Tests', () => {
|
|||
await element(by.id('BlueAddressInputScanQrButton')).tap();
|
||||
|
||||
// tapping 10 times invisible button is a backdoor:
|
||||
for (let c = 0; c <= 10; c++) {
|
||||
for (let c = 0; c <= 5; c++) {
|
||||
await element(by.id('ScanQrBackdoorButton')).tap();
|
||||
await sleep(1000);
|
||||
}
|
||||
|
@ -471,7 +471,7 @@ describe('BlueWallet UI Tests', () => {
|
|||
process.env.TRAVIS && require('fs').writeFileSync(lockFile, '1');
|
||||
});
|
||||
|
||||
it('can import zpub as watch-only and create PSBT', async () => {
|
||||
it('can import zpub as watch-only and create PSBT, and scan txhex back', async () => {
|
||||
const lockFile = '/tmp/travislock.' + hashIt(jasmine.currentTest.fullName);
|
||||
if (process.env.TRAVIS) {
|
||||
if (require('fs').existsSync(lockFile))
|
||||
|
@ -509,7 +509,7 @@ describe('BlueWallet UI Tests', () => {
|
|||
await element(by.id('PsbtTxScanButton')).tap(); // opening camera
|
||||
|
||||
// tapping 10 times invisible button is a backdoor:
|
||||
for (let c = 0; c <= 10; c++) {
|
||||
for (let c = 0; c <= 5; c++) {
|
||||
await element(by.id('ScanQrBackdoorButton')).tap();
|
||||
await sleep(1000);
|
||||
}
|
||||
|
@ -518,9 +518,68 @@ describe('BlueWallet UI Tests', () => {
|
|||
'020000000001011628f58e8e81bfcfff1b106bb8968e342fb86f09aa810ed2939e43d5127c51040200000000000000000227e42d000000000017a914c679a827d57a9b8b539515dbafb4e573d2bcc6ca87df15cf02000000002200209705cdfcbc459a220e7f39ffe547a31335505c2357f452ae12a22b9ae36ea59d04004730440220626c5205a6f49d1dd1577c85c0af4c5fc70f41de61f891d71a5cf57af09110d4022045bcb1e7d4e93e1a9baf6ae1ad0b4087c9e9f73ec366e97576912377d9f6904301473044022044aea98e8983f09cb0639f08d34526bb7e3ed47d208b7bf714fb29a1b5f9535a02200baa510b94cf434775b4aa2184682f2fb33f15e5e76f79aa0885e7ee12bdc8f70169522102e67ce679d617d674d68eea95ecb166c67b4b5520105c4745adf37ce8a40b92dc21029ff54b8bf26dbddd7bd4336593d2ff17519d5374989f36a6f5f8239675ff79a421039000ee2853c6db4bd956e80b1ecfb8711bf3e0a9a8886d15450c29458b60473153ae00000000';
|
||||
await element(by.id('scanQrBackdoorInput')).replaceText(randomTxHex);
|
||||
await element(by.id('scanQrBackdoorOkButton')).tap();
|
||||
await expect(element(by.id('ScanQrBackdoorButton'))).toBeNotVisible();
|
||||
await yo('PsbtWithHardwareWalletBroadcastTransactionButton');
|
||||
|
||||
// TODO: same but with real signed PSBT QR for this specific transaction
|
||||
process.env.TRAVIS && require('fs').writeFileSync(lockFile, '1');
|
||||
});
|
||||
|
||||
/**
|
||||
* test plan:
|
||||
* 1. import wallet
|
||||
* 2. wallet -> send -> import transaction (scan QR)
|
||||
* 3. provide unsigned psbt from coldcard (UR)
|
||||
* 4. on psbtWithHardwareWallet, tap scanQr
|
||||
* 5. provide fully signed psbt (UR)
|
||||
* 6. verify that we can see broadcast button and camera backdorr button is NOT visible
|
||||
*/
|
||||
it('can import zpub as watch-only, import psbt, and then scan signed psbt', async () => {
|
||||
const lockFile = '/tmp/travislock.' + hashIt(jasmine.currentTest.fullName);
|
||||
if (process.env.TRAVIS) {
|
||||
if (require('fs').existsSync(lockFile))
|
||||
return console.warn('skipping', JSON.stringify(jasmine.currentTest.fullName), 'as it previously passed on Travis');
|
||||
}
|
||||
await helperImportWallet(
|
||||
'zpub6rDWXE4wbwefeCrHWehXJheXnti5F9PbpamDUeB5eFbqaY89x3jq86JADBuXpnJnSvRVwqkaTnyMaZERUg4BpxD9V4tSZfKeYh1ozPdL1xK',
|
||||
'Imported Watch-only',
|
||||
'0.00030666 BTC',
|
||||
);
|
||||
|
||||
await element(by.id('SendButton')).tap();
|
||||
await element(by.text('OK')).tap();
|
||||
|
||||
await element(by.id('advancedOptionsMenuButton')).tap();
|
||||
await element(by.id('ImportQrTransactionButton')).tap(); // opens camera
|
||||
|
||||
const unsignedPsbt =
|
||||
'ur:bytes/tzahqumzwnlszqzjqgqqqqqp6uu247pvcz6zld9p77ghlnl753q8fgygggzv9ugjxsmggyy5gqcqqqqqqqq0llllluqepssqqqqqqqqqzcqpfkxmzh6ud2yrvcl37uyy9yswr2z4mx276qqqqqqqqqgpragvxqqqqqqqqqqkqq2tgxjzwa0000egemyzygsv92j2zdwvg5ejypszwe3qctjvrwul6t2ts7yhk8e5takxwzey2z70kdnykwd43jsptrzps95d6cp4gqqqsqqqqqyqqqqqpqqqqqqqqpqqqqqqqqq0vr0lj';
|
||||
const signedPsbt =
|
||||
'ur:bytes/tyqjuurnvf607qgq2gpqqqqqq8tn32hc9nqtgta558mezl70l6jyqa9q3ppqfsh3zg6rdpqsj3qrqqqqqqqqpllllllsryxzqqqqqqqqqqtqq9xcmv2lt34gsdnr78msss5jpcdg2hvetmgqqqqqqqqpqy04pscqqqqqqqqqzcqpfdq6gfm4aaal9r8vsg3zps42fgf4e3znxgszqfmxyrpwfsdmnlfdfwrcj7clx30kcecty3gte7ekvjeekkx2q9vvgjpsg5pzzqxjc9xv3rlhu2n6u87pm94agwcmvcywwsx9k0jpvwyng8crytgrkcpzqae6amp5xy03x2lsklv5zgnmeht0grzns27tmsjtsg2j0ne2969kqyqsxpqpqqqqqgsxqfmxyrpwfsdmnlfdfwrcj7clx30kcecty3gte7ekvjeekkx2q9vvgxqk3htqx4qqqzqqqqqqsqqqqqyqqqqqqqqyqqqqqqqqear8ke';
|
||||
|
||||
// tapping 10 times invisible button is a backdoor:
|
||||
for (let c = 0; c <= 5; c++) {
|
||||
await element(by.id('ScanQrBackdoorButton')).tap();
|
||||
await sleep(1000);
|
||||
}
|
||||
|
||||
await element(by.id('scanQrBackdoorInput')).replaceText(unsignedPsbt);
|
||||
await element(by.id('scanQrBackdoorOkButton')).tap();
|
||||
|
||||
// now lets test scanning back QR with UR PSBT. this should lead straight to broadcast dialog
|
||||
|
||||
await element(by.id('PsbtWithHardwareScrollView')).swipe('up', 'fast', 1); // in case emu screen is small and it doesnt fit
|
||||
await element(by.id('PsbtTxScanButton')).tap(); // opening camera
|
||||
|
||||
// tapping 10 times invisible button is a backdoor:
|
||||
for (let c = 0; c <= 5; c++) {
|
||||
await element(by.id('ScanQrBackdoorButton')).tap();
|
||||
await sleep(1000);
|
||||
}
|
||||
|
||||
await element(by.id('scanQrBackdoorInput')).replaceText(signedPsbt);
|
||||
await element(by.id('scanQrBackdoorOkButton')).tap();
|
||||
await expect(element(by.id('ScanQrBackdoorButton'))).toBeNotVisible();
|
||||
await yo('PsbtWithHardwareWalletBroadcastTransactionButton');
|
||||
|
||||
process.env.TRAVIS && require('fs').writeFileSync(lockFile, '1');
|
||||
});
|
||||
|
@ -577,7 +636,7 @@ describe('BlueWallet UI Tests', () => {
|
|||
|
||||
for (const ur of urs) {
|
||||
// tapping 10 times invisible button is a backdoor:
|
||||
for (let c = 0; c <= 10; c++) {
|
||||
for (let c = 0; c <= 5; c++) {
|
||||
await element(by.id('ScanQrBackdoorButton')).tap();
|
||||
}
|
||||
await element(by.id('scanQrBackdoorInput')).replaceText(ur);
|
||||
|
@ -627,7 +686,7 @@ describe('BlueWallet UI Tests', () => {
|
|||
|
||||
for (const ur of ursSignedByColdcard) {
|
||||
// tapping 10 times invisible button is a backdoor:
|
||||
for (let c = 0; c <= 10; c++) {
|
||||
for (let c = 0; c <= 5; c++) {
|
||||
await element(by.id('ScanQrBackdoorButton')).tap();
|
||||
}
|
||||
await element(by.id('scanQrBackdoorInput')).replaceText(ur);
|
||||
|
@ -648,7 +707,7 @@ describe('BlueWallet UI Tests', () => {
|
|||
|
||||
for (const ur of urSignedByColdcardAndCobo) {
|
||||
// tapping 10 times invisible button is a backdoor:
|
||||
for (let c = 0; c <= 10; c++) {
|
||||
for (let c = 0; c <= 5; c++) {
|
||||
await element(by.id('ScanQrBackdoorButton')).tap();
|
||||
}
|
||||
await element(by.id('scanQrBackdoorInput')).replaceText(ur);
|
||||
|
|
Loading…
Add table
Reference in a new issue