mirror of
https://github.com/BlueWallet/BlueWallet.git
synced 2025-02-22 15:04:50 +01:00
FIX: scan Cobo vault signed transaction QR
This commit is contained in:
parent
bd77ad1dbd
commit
a6d45a25af
4 changed files with 54 additions and 21 deletions
|
@ -942,15 +942,24 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
|
|||
* Combines 2 PSBTs into final transaction from which you can
|
||||
* get HEX and broadcast
|
||||
*
|
||||
* @param base64one {string}
|
||||
* @param base64two {string}
|
||||
* @param base64one {string|Psbt}
|
||||
* @param base64two {string|Psbt}
|
||||
* @returns {Transaction}
|
||||
*/
|
||||
combinePsbt(base64one, base64two) {
|
||||
const final1 = bitcoin.Psbt.fromBase64(base64one);
|
||||
const final2 = bitcoin.Psbt.fromBase64(base64two);
|
||||
const final1 = typeof base64one === 'string' ? bitcoin.Psbt.fromBase64(base64one) : base64one;
|
||||
const final2 = typeof base64two === 'string' ? bitcoin.Psbt.fromBase64(base64two) : base64two;
|
||||
final1.combine(final2);
|
||||
return final1.finalizeAllInputs().extractTransaction();
|
||||
|
||||
let extractedTransaction;
|
||||
try {
|
||||
extractedTransaction = final1.finalizeAllInputs().extractTransaction();
|
||||
} catch (_) {
|
||||
// probably already finalized
|
||||
extractedTransaction = final1.extractTransaction();
|
||||
}
|
||||
|
||||
return extractedTransaction;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -698,7 +698,6 @@ export default class SendDetails extends Component {
|
|||
memo: this.state.memo,
|
||||
fromWallet: this.state.fromWallet,
|
||||
psbt: file,
|
||||
isFirstPSBTAlreadyBase64: true,
|
||||
});
|
||||
this.setState({ isLoading: false });
|
||||
return;
|
||||
|
|
|
@ -34,7 +34,6 @@ import ReactNativeHapticFeedback from 'react-native-haptic-feedback';
|
|||
import RNFS from 'react-native-fs';
|
||||
import DocumentPicker from 'react-native-document-picker';
|
||||
import { decodeUR, extractSingleWorkload } from 'bc-ur/dist';
|
||||
import { Psbt } from 'bitcoinjs-lib';
|
||||
import loc from '../../loc';
|
||||
import { BlueCurrentTheme } from '../../components/themes';
|
||||
const EV = require('../../blue_modules/events');
|
||||
|
@ -150,8 +149,8 @@ export default class PsbtWithHardwareWallet extends Component {
|
|||
if (this.state.animatedQRCodeData.length === total) {
|
||||
const payload = decodeUR(this.state.animatedQRCodeData.map(i => i.data));
|
||||
const psbtB64 = Buffer.from(payload, 'hex').toString('base64');
|
||||
const psbt = Psbt.fromBase64(psbtB64);
|
||||
this.setState({ txhex: psbt.extractTransaction().toHex() });
|
||||
const Tx = this._combinePSBT(psbtB64);
|
||||
this.setState({ txhex: Tx.toHex() });
|
||||
}
|
||||
},
|
||||
);
|
||||
|
@ -162,14 +161,13 @@ export default class PsbtWithHardwareWallet extends Component {
|
|||
};
|
||||
|
||||
_combinePSBT = receivedPSBT => {
|
||||
return this.state.fromWallet.combinePsbt(
|
||||
this.state.isFirstPSBTAlreadyBase64 ? this.state.psbt : this.state.psbt.toBase64(),
|
||||
receivedPSBT,
|
||||
);
|
||||
return this.state.fromWallet.combinePsbt(this.state.psbt, receivedPSBT);
|
||||
};
|
||||
|
||||
onBarScanned = ret => {
|
||||
if (ret && !ret.data) ret = { data: ret };
|
||||
if (ret.data.toUpperCase().startsWith('UR')) {
|
||||
this.props.navigation.dangerouslyGetParent().pop();
|
||||
return this._onReadUniformResource(ret.data);
|
||||
}
|
||||
if (ret.data.indexOf('+') === -1 && ret.data.indexOf('=') === -1 && ret.data.indexOf('=') === -1) {
|
||||
|
@ -193,7 +191,6 @@ export default class PsbtWithHardwareWallet extends Component {
|
|||
memo: props.route.params.memo,
|
||||
psbt: props.route.params.psbt,
|
||||
fromWallet: props.route.params.fromWallet,
|
||||
isFirstPSBTAlreadyBase64: props.route.params.isFirstPSBTAlreadyBase64,
|
||||
isSecondPSBTAlreadyBase64: false,
|
||||
deepLinkPSBT: undefined,
|
||||
txhex: props.route.params.txhex || undefined,
|
||||
|
@ -207,10 +204,7 @@ export default class PsbtWithHardwareWallet extends Component {
|
|||
const txhex = nextProps.route.params.txhex;
|
||||
if (deepLinkPSBT) {
|
||||
try {
|
||||
const Tx = prevState.fromWallet.combinePsbt(
|
||||
prevState.isFirstPSBTAlreadyBase64 ? prevState.psbt : prevState.psbt.toBase64(),
|
||||
deepLinkPSBT,
|
||||
);
|
||||
const Tx = prevState.fromWallet.combinePsbt(prevState.psbt, deepLinkPSBT);
|
||||
return {
|
||||
...prevState,
|
||||
txhex: Tx.toHex(),
|
||||
|
@ -293,7 +287,7 @@ export default class PsbtWithHardwareWallet extends Component {
|
|||
exportPSBT = async () => {
|
||||
if (Platform.OS === 'ios') {
|
||||
const filePath = RNFS.TemporaryDirectoryPath + `/${this.fileName}`;
|
||||
await RNFS.writeFile(filePath, this.state.isFirstPSBTAlreadyBase64 ? this.state.psbt : this.state.psbt.toBase64());
|
||||
await RNFS.writeFile(filePath, typeof this.state.psbt === 'string' ? this.state.psbt : this.state.psbt.toBase64());
|
||||
Share.open({
|
||||
url: 'file://' + filePath,
|
||||
})
|
||||
|
@ -313,7 +307,7 @@ export default class PsbtWithHardwareWallet extends Component {
|
|||
if (granted === PermissionsAndroid.RESULTS.GRANTED) {
|
||||
console.log('Storage Permission: Granted');
|
||||
const filePath = RNFS.DownloadDirectoryPath + `/${this.fileName}`;
|
||||
await RNFS.writeFile(filePath, this.state.isFirstPSBTAlreadyBase64 ? this.state.psbt : this.state.psbt.toBase64());
|
||||
await RNFS.writeFile(filePath, typeof this.state.psbt === 'string' ? this.state.psbt : this.state.psbt.toBase64());
|
||||
alert(loc.formatString(loc.send.txSaved, { filePath: this.fileName }));
|
||||
} else {
|
||||
console.log('Storage Permission: Denied');
|
||||
|
@ -424,7 +418,7 @@ export default class PsbtWithHardwareWallet extends Component {
|
|||
<BlueSpacing20 />
|
||||
<View style={styles.copyToClipboard}>
|
||||
<BlueCopyToClipboardButton
|
||||
stringToCopy={this.state.isFirstPSBTAlreadyBase64 ? this.state.psbt : this.state.psbt.toBase64()}
|
||||
stringToCopy={typeof this.state.psbt === 'string' ? this.state.psbt : this.state.psbt.toBase64()}
|
||||
displayText={loc.send.psbt_clipboard}
|
||||
/>
|
||||
</View>
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
/* global it, describe */
|
||||
import { WatchOnlyWallet } from '../../class';
|
||||
import { decodeUR } from 'bc-ur/dist';
|
||||
import { Psbt } from 'bitcoinjs-lib';
|
||||
const assert = require('assert');
|
||||
|
||||
describe('Watch only wallet', () => {
|
||||
|
@ -170,6 +172,11 @@ describe('Watch only wallet', () => {
|
|||
Tx.toHex(),
|
||||
'02000000000101805b8c2457c38afae8bcb5688fd20af8acf820d0b027507a3d41842a039e3b7f000000000000000080028813000000000000160014c0cebcd6c3d3ca8c75dc5ec62ebe55330ef910e2b739000000000000160014e73a921eeb94a4ad470c0cbdb69ebbea05bc1e0c0247304402203d1f736733539df3ea64989fc94c1d3367165bc3d9a829d20ac7c278f959d9aa022056e1affe4ffdb4ba786419b57dfeeaafa750ee8a69229881a699ad4644345e8101210358e1db384dc3e0a8aea11ee41ed993b4567b297401fbccdbe754002cb2714f1f00000000',
|
||||
);
|
||||
|
||||
// checking that combine can work with both base64 and pure Psbt objects
|
||||
const Tx2 = w.combinePsbt(Psbt.fromBase64(unsignedPsbt), Psbt.fromBase64(signedPsbt));
|
||||
|
||||
assert.strictEqual(Tx2.toHex(), Tx.toHex());
|
||||
});
|
||||
|
||||
it('ypub watch-only can generate addresses', async () => {
|
||||
|
@ -188,3 +195,27 @@ describe('Watch only wallet', () => {
|
|||
assert.ok(w.getAllExternalAddresses().includes(await w._getExternalAddressByIndex(0)));
|
||||
});
|
||||
});
|
||||
|
||||
describe('BC-UR', () => {
|
||||
it('can decodeUR() and then combine unfinalized signed PSBT', () => {
|
||||
const unsignedPayload = decodeUR([
|
||||
'UR:BYTES/TYQ4XURNVF607QGQWYPQQQQQQ9U63JU4AD5C93Y057WNRNTV24AE8QK4DDHVT04GHTKNQZCXYHNW5QGQQQQQPLHLLLLS9LRRQQQQQQQQQQTQQ9P9YMAAVV5GVUNKD49W4GDNJ4C9GJP7383QFCQQQQQQQQQPVQQ5CXKNG9PNTGMDRV0GNWNJZS23KGG3V0KXQQQQQQQQQYQ375XRQQQQQQQQQQTQQ98UXJHTKAHE83Q8W5VGHH2G93698VZLP6PZQCPXW47RAFD36W04SNHNTZK8CLCWHXDJJRRZ2EP998STFNRYWFQPC0CC3N8X87Z5QQQGQQQQQZQQQQQQSQQQQQQQQQQQQQQQYGPQY5M4J23F3Z9TK6HZTRDD6M89QX955DEH3HXGXAC6NJQMT3CHYTJHRZXVUCLC2SQQPQQQQQQGQQQQQZQQZQQQQQQQQQQQQQ3QYQK6E2MCA75ZCRMMWZYWXNQKGKNNJC7JUXPNWR5QPYQC3EYRM4NDQ5VGENNRLP2QQQYQQQQQPQQQQQQGQQQQQQQQZQQQQQQQ6GYX3G',
|
||||
]);
|
||||
const uPsbtB64 = Buffer.from(unsignedPayload, 'hex').toString('base64');
|
||||
|
||||
const payloadSignedButNotFinalized = decodeUR([
|
||||
'UR:BYTES/TR58QUMZWNLSZQR3QGQQQQQP0X5VH90TDXPVFRA8N5CU6MZ40WFC94TTDMZMA296A5CQKP39UM4QZQQQQQQ0ALLLLUP0CCCQQQQQQQQQZCQPGFFXL0TR9ZR8YANDFT42RVU4WP2YS05FUGZWQQQQQQQQQQTQQ9XP456PGV66XMGMR6YM5US5Z5DJZYTRA3SQQQQQQQPZQGPXW47RAFD36W04SNHNTZK8CLCWHXDJJRRZ2EP998STFNRYWFQPC068XPZQYGRH45ESDZ623KSNPTY2VJ37LWA2HTCCLGSDWPDDEPK48JAKNSVZTQPZQD0W5ND2M7D62YYQ74A85DRKM8ESQS2WSTZ5F4V2YNNGY9S7F0NSQYQQQQQ7VRJ5Z',
|
||||
]);
|
||||
const sPsbtB64 = Buffer.from(payloadSignedButNotFinalized, 'hex').toString('base64');
|
||||
|
||||
const w = new WatchOnlyWallet();
|
||||
w.setSecret('zpub6s2RJ9qAEBW8Abhojs6LyDzF7gttcDr6EsR3Umu2aptZBb45e734rGtt4KqsCMmNyR1EEzUU2ugdVYez2VywQvAbBjUSKn8ho4Zk2c5otkk');
|
||||
w.init();
|
||||
|
||||
const tx = w.combinePsbt(uPsbtB64, sPsbtB64);
|
||||
assert.strictEqual(
|
||||
tx.toHex(),
|
||||
'0200000000010179a8cb95eb6982c48fa79d31cd6c557b9382d56b6ec5bea8baed300b0625e6ea0100000000feffffff02fc630000000000001600142526fbd63288672766d4aeaa1b3957054483e89e204e000000000000160014c1ad3414335a36d1b1e89ba7214151b211163ec602473044022077ad33068b4a8da130ac8a64a3efbbaabaf18fa20d705adc86d53cbb69c18258022035eea4daadf9ba51080f57a7a3476d9f300414e82c544d58a24e682161e4be700121026757c3ea5b1d39f584ef358ac7c7f0eb99b290c625642529e0b4cc6472401c3f00000000',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Add table
Reference in a new issue