mirror of
https://github.com/BlueWallet/BlueWallet.git
synced 2025-02-22 15:04:50 +01:00
ADD: support for Cobo Vault hardware wallet
This commit is contained in:
parent
1d89bff1f3
commit
82fd28ce12
20 changed files with 901 additions and 23 deletions
|
@ -34,6 +34,8 @@ import { BlurView } from '@react-native-community/blur';
|
|||
import showPopupMenu from 'react-native-popup-menu-android';
|
||||
import NetworkTransactionFees, { NetworkTransactionFeeType } from './models/networkTransactionFees';
|
||||
import Biometric from './class/biometrics';
|
||||
import { encodeUR } from 'bc-ur/dist';
|
||||
import QRCode from 'react-native-qrcode-svg';
|
||||
const loc = require('./loc/');
|
||||
/** @type {AppStorage} */
|
||||
const BlueApp = require('./BlueApp');
|
||||
|
@ -2529,3 +2531,151 @@ export function BlueBigCheckmark({ style }) {
|
|||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
export class DynamicQRCode extends Component {
|
||||
constructor() {
|
||||
super();
|
||||
const qrCodeHeight = height > width ? width - 40 : width / 3;
|
||||
const qrCodeMaxHeight = 370;
|
||||
this.state = {
|
||||
index: 0,
|
||||
total: 0,
|
||||
qrCodeHeight: Math.min(qrCodeHeight, qrCodeMaxHeight),
|
||||
intervalHandler: null,
|
||||
};
|
||||
}
|
||||
|
||||
fragments = [];
|
||||
|
||||
componentDidMount() {
|
||||
const { value, capacity = 800 } = this.props;
|
||||
this.fragments = encodeUR(value, capacity);
|
||||
this.setState(
|
||||
{
|
||||
total: this.fragments.length,
|
||||
},
|
||||
() => {
|
||||
this.startAutoMove();
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
moveToNextFragment = () => {
|
||||
const { index, total } = this.state;
|
||||
if (index === total - 1) {
|
||||
this.setState({
|
||||
index: 0,
|
||||
});
|
||||
} else {
|
||||
this.setState(state => ({
|
||||
index: state.index + 1,
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
startAutoMove = () => {
|
||||
if (!this.state.intervalHandler)
|
||||
this.setState(() => ({
|
||||
intervalHandler: setInterval(this.moveToNextFragment, 500),
|
||||
}));
|
||||
};
|
||||
|
||||
stopAutoMove = () => {
|
||||
clearInterval(this.state.intervalHandler);
|
||||
this.setState(() => ({
|
||||
intervalHandler: null,
|
||||
}));
|
||||
};
|
||||
|
||||
moveToPreviousFragment = () => {
|
||||
const { index, total } = this.state;
|
||||
if (index > 0) {
|
||||
this.setState(state => ({
|
||||
index: state.index - 1,
|
||||
}));
|
||||
} else {
|
||||
this.setState(state => ({
|
||||
index: total - 1,
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const currentFragment = this.fragments[this.state.index];
|
||||
return currentFragment ? (
|
||||
<View style={animatedQRCodeStyle.container}>
|
||||
<BlueSpacing20 />
|
||||
<View style={[animatedQRCodeStyle.qrcodeContainer, { height: this.state.qrCodeHeight }]}>
|
||||
<QRCode
|
||||
value={currentFragment.toUpperCase()}
|
||||
size={this.state.qrCodeHeight}
|
||||
color={BlueApp.settings.foregroundColor}
|
||||
logoBackgroundColor={BlueApp.settings.brandingColor}
|
||||
ecl="L"
|
||||
/>
|
||||
</View>
|
||||
<BlueSpacing20 />
|
||||
<View>
|
||||
<Text style={animatedQRCodeStyle.text}>
|
||||
{this.state.index + 1} of {this.state.total}
|
||||
</Text>
|
||||
</View>
|
||||
<BlueSpacing20 />
|
||||
<View style={animatedQRCodeStyle.controller}>
|
||||
<TouchableOpacity
|
||||
style={[animatedQRCodeStyle.button, { width: '25%', alignItems: 'flex-start' }]}
|
||||
onPress={this.moveToPreviousFragment}
|
||||
>
|
||||
<Text style={animatedQRCodeStyle.text}>Previous</Text>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity
|
||||
style={[animatedQRCodeStyle.button, { width: '50%' }]}
|
||||
onPress={this.state.intervalHandler ? this.stopAutoMove : this.startAutoMove}
|
||||
>
|
||||
<Text style={animatedQRCodeStyle.text}>{this.state.intervalHandler ? 'Stop' : 'Start'}</Text>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity
|
||||
style={[animatedQRCodeStyle.button, { width: '25%', alignItems: 'flex-end' }]}
|
||||
onPress={this.moveToNextFragment}
|
||||
>
|
||||
<Text style={animatedQRCodeStyle.text}>Next</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
) : (
|
||||
<View>
|
||||
<Text>Initialing</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const animatedQRCodeStyle = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
},
|
||||
qrcodeContainer: {
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
controller: {
|
||||
width: '90%',
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
backgroundColor: '#ccddf9',
|
||||
borderRadius: 25,
|
||||
height: 45,
|
||||
paddingHorizontal: 18,
|
||||
},
|
||||
button: {
|
||||
alignItems: 'center',
|
||||
height: 45,
|
||||
justifyContent: 'center',
|
||||
},
|
||||
text: {
|
||||
fontSize: 16,
|
||||
},
|
||||
});
|
||||
|
|
36
blue_modules/bc-bech32/Readme.md
Normal file
36
blue_modules/bc-bech32/Readme.md
Normal file
|
@ -0,0 +1,36 @@
|
|||
# bc-bech32
|
||||
this library is for implementing [BlockChain Commons bc-32](https://github.com/BlockchainCommons/Research/blob/master/papers/bcr-0004-bc32.md)
|
||||
|
||||
an encoding
|
||||
|
||||
## Installation
|
||||
|
||||
```
|
||||
yarn add bc-bech32
|
||||
```
|
||||
|
||||
## Test
|
||||
|
||||
```
|
||||
yarn test
|
||||
```
|
||||
|
||||
## Build
|
||||
|
||||
```
|
||||
yarn build
|
||||
```
|
||||
|
||||
## Sample
|
||||
|
||||
```
|
||||
import { encodeBc32Data, decodeBc32Data, encodeSegwitAddress, decodeSegwitAddress } from '../src';
|
||||
const data = encodeBc32Data('48656c6c6f20776f726c64');
|
||||
console.log(data) // fpjkcmr0ypmk7unvvsh4ra4j
|
||||
|
||||
```
|
||||
|
||||
|
||||
note: for using the node vesion should be upper than 10.16
|
||||
|
||||
this library is inspire on the [bech32](https://github.com/sipa/bech32/tree/master/ref/javascript). Thanks for the good library.
|
136
blue_modules/bc-bech32/dist/bech32.js
vendored
Normal file
136
blue_modules/bc-bech32/dist/bech32.js
vendored
Normal file
|
@ -0,0 +1,136 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
var index_1 = {};
|
||||
var Bech32Version;
|
||||
(function (Bech32Version) {
|
||||
Bech32Version[Bech32Version["Origin"] = 1] = "Origin";
|
||||
Bech32Version[Bech32Version["bis"] = 2] = "bis";
|
||||
})(Bech32Version = Bech32Version || (Bech32Version = {}));
|
||||
index_1.Bech32Version=Bech32Version;
|
||||
var CHARSET = 'qpzry9x8gf2tvdw0s3jn54khce6mua7l';
|
||||
var GENERATOR = [0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3];
|
||||
function polymod(values) {
|
||||
var chk = 1;
|
||||
for (var p = 0; p < values.length; ++p) {
|
||||
var top_1 = chk >> 25;
|
||||
chk = ((chk & 0x1ffffff) << 5) ^ values[p];
|
||||
for (var i = 0; i < 6; ++i) {
|
||||
if ((top_1 >> i) & 1) {
|
||||
chk ^= GENERATOR[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
return chk;
|
||||
}
|
||||
function hrpExpand(hrp) {
|
||||
var ret = [];
|
||||
var p;
|
||||
for (p = 0; p < hrp.length; ++p) {
|
||||
ret.push(hrp.charCodeAt(p) >> 5);
|
||||
}
|
||||
ret.push(0);
|
||||
for (p = 0; p < hrp.length; ++p) {
|
||||
ret.push(hrp.charCodeAt(p) & 31);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
function verifyChecksum(hrp, data, version) {
|
||||
var header;
|
||||
if (hrp) {
|
||||
header = hrpExpand(hrp);
|
||||
}
|
||||
else {
|
||||
header = [0];
|
||||
}
|
||||
var check = version === index_1.Bech32Version.Origin ? 1 : 0x3fffffff;
|
||||
return polymod(header.concat(data)) === check;
|
||||
}
|
||||
function createChecksum(hrp, data, bech32Version) {
|
||||
var values;
|
||||
if (hrp) {
|
||||
values = hrpExpand(hrp).concat(data).concat([0, 0, 0, 0, 0, 0]);
|
||||
}
|
||||
else {
|
||||
values = [0].concat(data).concat([0, 0, 0, 0, 0, 0]);
|
||||
}
|
||||
var chk = bech32Version === index_1.Bech32Version.Origin ? 1 : 0x3fffffff;
|
||||
var mod = polymod(values) ^ chk;
|
||||
var ret = [];
|
||||
for (var p = 0; p < 6; ++p) {
|
||||
ret.push((mod >> (5 * (5 - p))) & 31);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
var encode = function (hrp, data, version) {
|
||||
var combined = data.concat(createChecksum(hrp, data, version));
|
||||
var ret;
|
||||
if (hrp) {
|
||||
ret = hrp + '1';
|
||||
}
|
||||
else {
|
||||
ret = '';
|
||||
}
|
||||
for (var p = 0; p < combined.length; ++p) {
|
||||
ret += CHARSET.charAt(combined[p]);
|
||||
}
|
||||
return ret;
|
||||
};
|
||||
var decodeBc32 = function (bechString) {
|
||||
var data = [];
|
||||
for (var p = 0; p < bechString.length; ++p) {
|
||||
var d = CHARSET.indexOf(bechString.charAt(p));
|
||||
if (d === -1) {
|
||||
return null;
|
||||
}
|
||||
data.push(d);
|
||||
}
|
||||
if (!verifyChecksum(null, data, index_1.Bech32Version.bis)) {
|
||||
return null;
|
||||
}
|
||||
return { hrp: null, data: data.slice(0, data.length - 6) };
|
||||
};
|
||||
var decode = function (bechString) {
|
||||
var p;
|
||||
var hasLower = false;
|
||||
var hasUpper = false;
|
||||
for (p = 0; p < bechString.length; ++p) {
|
||||
if (bechString.charCodeAt(p) < 33 || bechString.charCodeAt(p) > 126) {
|
||||
return null;
|
||||
}
|
||||
if (bechString.charCodeAt(p) >= 97 && bechString.charCodeAt(p) <= 122) {
|
||||
hasLower = true;
|
||||
}
|
||||
if (bechString.charCodeAt(p) >= 65 && bechString.charCodeAt(p) <= 90) {
|
||||
hasUpper = true;
|
||||
}
|
||||
}
|
||||
if (hasLower && hasUpper) {
|
||||
return null;
|
||||
}
|
||||
bechString = bechString.toLowerCase();
|
||||
var pos = bechString.lastIndexOf('1');
|
||||
if (pos === -1) {
|
||||
return decodeBc32(bechString);
|
||||
}
|
||||
if (pos < 1 || pos + 7 > bechString.length || bechString.length > 90) {
|
||||
return null;
|
||||
}
|
||||
var hrp = bechString.substring(0, pos);
|
||||
var data = [];
|
||||
for (p = pos + 1; p < bechString.length; ++p) {
|
||||
var d = CHARSET.indexOf(bechString.charAt(p));
|
||||
if (d === -1) {
|
||||
return null;
|
||||
}
|
||||
data.push(d);
|
||||
}
|
||||
if (!verifyChecksum(hrp, data, index_1.Bech32Version.Origin)) {
|
||||
return null;
|
||||
}
|
||||
return { hrp: hrp, data: data.slice(0, data.length - 6) };
|
||||
};
|
||||
exports.default = {
|
||||
encode: encode,
|
||||
decode: decode,
|
||||
};
|
||||
//# sourceMappingURL=bech32.js.map
|
75
blue_modules/bc-bech32/dist/index.js
vendored
Normal file
75
blue_modules/bc-bech32/dist/index.js
vendored
Normal file
|
@ -0,0 +1,75 @@
|
|||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.decodeBc32Data = exports.encodeBc32Data = exports.encodeSegwitAddress = exports.decodeSegwitAddress = exports.Bech32Version = void 0;
|
||||
var bech32_1 = __importDefault(require("./bech32"));
|
||||
var Bech32Version;
|
||||
(function (Bech32Version) {
|
||||
Bech32Version[Bech32Version["Origin"] = 1] = "Origin";
|
||||
Bech32Version[Bech32Version["bis"] = 2] = "bis";
|
||||
})(Bech32Version = exports.Bech32Version || (exports.Bech32Version = {}));
|
||||
var convertBits = function (data, fromBits, toBits, pad) {
|
||||
var acc = 0;
|
||||
var bits = 0;
|
||||
var ret = [];
|
||||
var maxv = (1 << toBits) - 1;
|
||||
for (var p = 0; p < data.length; ++p) {
|
||||
var value = data[p];
|
||||
if (value < 0 || value >> fromBits !== 0) {
|
||||
return null;
|
||||
}
|
||||
acc = (acc << fromBits) | value;
|
||||
bits += fromBits;
|
||||
while (bits >= toBits) {
|
||||
bits -= toBits;
|
||||
ret.push((acc >> bits) & maxv);
|
||||
}
|
||||
}
|
||||
if (pad) {
|
||||
if (bits > 0) {
|
||||
ret.push((acc << (toBits - bits)) & maxv);
|
||||
}
|
||||
}
|
||||
else if (bits >= fromBits || (acc << (toBits - bits)) & maxv) {
|
||||
return null;
|
||||
}
|
||||
return ret;
|
||||
};
|
||||
exports.decodeSegwitAddress = function (hrp, addr) {
|
||||
var dec = bech32_1.default.decode(addr);
|
||||
if (dec === null || dec.hrp !== hrp || dec.data.length < 1 || dec.data[0] > 16) {
|
||||
return null;
|
||||
}
|
||||
var res = convertBits(Uint8Array.from(dec.data.slice(1)), 5, 8, false);
|
||||
if (res === null || res.length < 2 || res.length > 40) {
|
||||
return null;
|
||||
}
|
||||
if (dec.data[0] === 0 && res.length !== 20 && res.length !== 32) {
|
||||
return null;
|
||||
}
|
||||
return { version: dec.data[0], program: res };
|
||||
};
|
||||
exports.encodeSegwitAddress = function (hrp, version, program) {
|
||||
var ret = bech32_1.default.encode(hrp, [version].concat(convertBits(program, 8, 5, true)), Bech32Version.Origin);
|
||||
if (exports.decodeSegwitAddress(hrp, ret) === null) {
|
||||
return null;
|
||||
}
|
||||
return ret;
|
||||
};
|
||||
exports.encodeBc32Data = function (hex) {
|
||||
var data = Buffer.from(hex, 'hex');
|
||||
return bech32_1.default.encode(null, convertBits(data, 8, 5, true), Bech32Version.bis);
|
||||
};
|
||||
exports.decodeBc32Data = function (data) {
|
||||
var result = bech32_1.default.decode(data);
|
||||
if (result) {
|
||||
var res = convertBits(Buffer.from(result.data), 5, 8, false);
|
||||
return Buffer.from(res).toString('hex');
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
//# sourceMappingURL=index.js.map
|
64
blue_modules/bc-bech32/package.json
Normal file
64
blue_modules/bc-bech32/package.json
Normal file
|
@ -0,0 +1,64 @@
|
|||
{
|
||||
"_args": [
|
||||
[
|
||||
"bc-bech32@1.0.2",
|
||||
"/home/user/BlueWallet"
|
||||
]
|
||||
],
|
||||
"_from": "bc-bech32@1.0.2",
|
||||
"_id": "bc-bech32@1.0.2",
|
||||
"_inBundle": false,
|
||||
"_integrity": "sha512-lwAn5R4LUhcnyrZgNx3YdDPr5+nseM4kARANcv8i0YOMtnPJRTF7B7TZzS3DYgC6tff/aR2W/3jGoY/SJMs6MA==",
|
||||
"_location": "/bc-bech32",
|
||||
"_phantomChildren": {},
|
||||
"_requested": {
|
||||
"type": "version",
|
||||
"registry": true,
|
||||
"raw": "bc-bech32@1.0.2",
|
||||
"name": "bc-bech32",
|
||||
"escapedName": "bc-bech32",
|
||||
"rawSpec": "1.0.2",
|
||||
"saveSpec": null,
|
||||
"fetchSpec": "1.0.2"
|
||||
},
|
||||
"_requiredBy": [
|
||||
"/bc-ur"
|
||||
],
|
||||
"_resolved": "https://registry.npmjs.org/bc-bech32/-/bc-bech32-1.0.2.tgz",
|
||||
"_spec": "1.0.2",
|
||||
"_where": "/home/user/BlueWallet",
|
||||
"description": "this library is for implementing [BlockChain Commons bc-32](https://github.com/BlockchainCommons/Research/blob/master/papers/bcr-0004-bc32.md)",
|
||||
"files": [
|
||||
"src",
|
||||
"dist"
|
||||
],
|
||||
"gitHead": "981882405f62b1268edcbda018662a369431bb62",
|
||||
"license": "MIT",
|
||||
"main": "dist/index.js",
|
||||
"name": "bc-bech32",
|
||||
"scripts": {
|
||||
"build": "npm run clean && npx tsc -p tsconfig.json -outDir ./dist",
|
||||
"clean": "rm -rf ./dist"
|
||||
},
|
||||
"version": "1.0.2",
|
||||
"react-native": {
|
||||
"path": "path-browserify",
|
||||
"fs": "react-native-level-fs",
|
||||
"_stream_transform": "readable-stream/transform",
|
||||
"_stream_readable": "readable-stream/readable",
|
||||
"_stream_writable": "readable-stream/writable",
|
||||
"_stream_duplex": "readable-stream/duplex",
|
||||
"_stream_passthrough": "readable-stream/passthrough",
|
||||
"stream": "stream-browserify"
|
||||
},
|
||||
"browser": {
|
||||
"path": "path-browserify",
|
||||
"fs": "react-native-level-fs",
|
||||
"_stream_transform": "readable-stream/transform",
|
||||
"_stream_readable": "readable-stream/readable",
|
||||
"_stream_writable": "readable-stream/writable",
|
||||
"_stream_duplex": "readable-stream/duplex",
|
||||
"_stream_passthrough": "readable-stream/passthrough",
|
||||
"stream": "stream-browserify"
|
||||
}
|
||||
}
|
22
blue_modules/bc-ur/README.md
Normal file
22
blue_modules/bc-ur/README.md
Normal file
|
@ -0,0 +1,22 @@
|
|||
# bc-ur
|
||||
This library is an implementation of [bc-ur](https://github.com/BlockchainCommons/Research/blob/master/papers/bcr-0005-ur.md).
|
||||
|
||||
Still in development, please do not use it in production;
|
||||
|
||||
## Install
|
||||
|
||||
```
|
||||
yarn add bc-ur
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```
|
||||
const {encodeUR, decodeUR} from 'bc-ur';
|
||||
|
||||
const hexstr = '7e4a61385e2550981b4b5633ab178eb077a30505fbd53f107ec1081e7cf0ca3c0dc0bfea5b8bfb5e6ffc91afd104c3aa756210b5dbc5118fd12c87ee04269815ba6a9968a0d0d3b7a9b631382a36bc70ab626d5670b4b48ff843f4d9a15631aa67c7aaf0ac6ce7e3bff03b2c9643e3375e47493c4e0f8635329d66fdec41b10ce74dcbf25fc15d829e7830c325643a98561f441b40a02e8353493e6afc16192fe99d90d8ca65539af77ddeaccc8943a37563a9ba83675bd5d4da7c60c9a172cf6940cbf0ec8fe04175a629932e3512c5d2aaea3cca3246f40a21ffdc33c3987dc7b880351230eb3759fe3c7dc7b2d3a20a95996ff0b7a0dba834f96beb64c14e3426fb051a936ba41569ab99c0066a6d9c0777a49e49e6cbad24d722a4c7da112432679264b9adc0a8cff9dd1fe0ee9ee2747f6a68537c389a7303a1af23c534ee6392bc17b04cf0fbce7689e66b673a440c04a9454005b0c76664639113458eb7d0902eff04d11138ce2a8ee16a9cd7c8926514efa9bd83ae7a4c139835f0fe0f68c628e0645c8524c30dfc314e825a7aa13224d98e2f7a9d12183a999bb1f28549c99a9072d99c05c24e0c84848c4fc147a094ab7b69e9cbea86952fccf15500fbb234ffe6ee6e6ded515c8016cb017ba36fb931ef276cec4ed22c1aed1495d2df3b3ce66c03f5b9ffa8434bf0e8fb149de94e050b3da178df1f76c00a366cb2801fabdf1a1e90cd3cd45ecb7a930a40b151455f76b726d552f31c21324992da257ff8bde2923dfd5d0d6b87233fae215ffacbecd96249099e7e3427d533db56cdb09c7475b4ce3314e33f43953a7370866cc11d85f00b71b15510b46c4b4fa490c660ddfeda0ceb1b8265995f7071c155ad1b57465fdc0fa81a73f9f19ac4872029d5844c1838f732e803043673e26cbc5b51297a324ff00a2d2d4222bad556b93d27c8e376e3ff8f9d37b3073410708ebb3d4dd7473d27212310b71a3c33a5c8f87f44824640e7f8970f4eda9364195c87a91172b8f085f1773641dde1ce21938746234055bc971ce2325f814e3eec60f781dd4faf52afd5be4a6b38656f7e9739f724cb7ccd4e4d01e802add3dc7b83191f894b3ee0ed752ee514d5ec55';
|
||||
|
||||
const fragments = encodeUR(hexstr, 500);
|
||||
|
||||
const payload = decodeUR(fragments);
|
||||
```
|
115
blue_modules/bc-ur/dist/decodeUR.js
vendored
Normal file
115
blue_modules/bc-ur/dist/decodeUR.js
vendored
Normal file
|
@ -0,0 +1,115 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.extractSingleWorkload = exports.decodeUR = void 0;
|
||||
var utils_1 = require("./utils");
|
||||
var miniCbor_1 = require("./miniCbor");
|
||||
var bc_bech32_1 = require("bc-bech32");
|
||||
var checkAndGetSequence = function (sequence) {
|
||||
var pieces = sequence.toUpperCase().split('OF');
|
||||
if (pieces.length !== 2)
|
||||
throw new Error("invalid sequence: " + sequence);
|
||||
var index = pieces[0], total = pieces[1];
|
||||
return [+index, +total];
|
||||
};
|
||||
var checkDigest = function (digest, payload) {
|
||||
if (bc_bech32_1.decodeBc32Data(digest) !== utils_1.sha256Hash(Buffer.from(bc_bech32_1.decodeBc32Data(payload), 'hex')).toString('hex')) {
|
||||
throw new Error("invalid digest: \n digest:" + digest + " \n payload:" + payload);
|
||||
}
|
||||
};
|
||||
var checkURHeader = function (UR, type) {
|
||||
if (type === void 0) { type = 'bytes'; }
|
||||
if (UR.toUpperCase() !== ("ur:" + type).toUpperCase())
|
||||
throw new Error("invalid UR header: " + UR);
|
||||
};
|
||||
var dealWithSingleWorkload = function (workload, type) {
|
||||
if (type === void 0) { type = 'bytes'; }
|
||||
var pieces = workload.split('/');
|
||||
switch (pieces.length) {
|
||||
case 2: {
|
||||
//UR:Type/[Fragment]
|
||||
checkURHeader(pieces[0], type);
|
||||
return pieces[1];
|
||||
}
|
||||
case 3: {
|
||||
//UR:Type/[Digest]/[Fragment] when Sequencing is omitted, Digest MAY be omitted;
|
||||
//should check digest
|
||||
checkURHeader(pieces[0], type);
|
||||
var digest = pieces[1];
|
||||
var fragment = pieces[2];
|
||||
checkDigest(digest, fragment);
|
||||
return fragment;
|
||||
}
|
||||
case 4: {
|
||||
//UR:Type/[Sequencing]/[Digest]/[Fragment]
|
||||
//should check sequencing and digest
|
||||
checkURHeader(pieces[0], type);
|
||||
checkAndGetSequence(pieces[1]);
|
||||
var digest = pieces[2];
|
||||
var fragment = pieces[3];
|
||||
checkDigest(digest, fragment);
|
||||
return fragment;
|
||||
}
|
||||
default:
|
||||
throw new Error("invalid workload pieces length: expect 2 / 3 / 4 bug got " + pieces.length);
|
||||
}
|
||||
};
|
||||
var dealWithMultipleWorkloads = function (workloads, type) {
|
||||
if (type === void 0) { type = 'bytes'; }
|
||||
var length = workloads.length;
|
||||
var fragments = new Array(length).fill('');
|
||||
var digest = '';
|
||||
workloads.forEach(function (workload) {
|
||||
var pieces = workload.split('/');
|
||||
checkURHeader(pieces[0], type);
|
||||
var _a = checkAndGetSequence(pieces[1]), index = _a[0], total = _a[1];
|
||||
if (total !== length)
|
||||
throw new Error("invalid workload: " + workload + ", total " + total + " not equal workloads length " + length);
|
||||
if (digest && digest !== pieces[2])
|
||||
throw new Error("invalid workload: " + workload + ", checksum changed " + digest + ", " + pieces[2]);
|
||||
digest = pieces[2];
|
||||
if (fragments[index - 1])
|
||||
throw new Error("invalid workload: " + workload + ", index " + index + " has already been set");
|
||||
fragments[index - 1] = pieces[3];
|
||||
});
|
||||
var payload = fragments.join('');
|
||||
checkDigest(digest, payload);
|
||||
return payload;
|
||||
};
|
||||
var getBC32Payload = function (workloads, type) {
|
||||
if (type === void 0) { type = 'bytes'; }
|
||||
try {
|
||||
var length_1 = workloads.length;
|
||||
if (length_1 === 1) {
|
||||
return dealWithSingleWorkload(workloads[0], type);
|
||||
}
|
||||
else {
|
||||
return dealWithMultipleWorkloads(workloads, type);
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
throw new Error("invalid workloads: " + workloads + "\n " + e);
|
||||
}
|
||||
};
|
||||
exports.decodeUR = function (workloads, type) {
|
||||
if (type === void 0) { type = 'bytes'; }
|
||||
var bc32Payload = getBC32Payload(workloads, type);
|
||||
var cborPayload = bc_bech32_1.decodeBc32Data(bc32Payload);
|
||||
return miniCbor_1.decodeSimpleCBOR(cborPayload);
|
||||
};
|
||||
exports.extractSingleWorkload = function (workload) {
|
||||
var pieces = workload.toUpperCase().split('/');
|
||||
switch (pieces.length) {
|
||||
case 2: //UR:Type/[Fragment]
|
||||
case 3: {
|
||||
//UR:Type/[Digest]/[Fragment] when Sequencing is omitted, Digest MAY be omitted;
|
||||
return [1, 1];
|
||||
}
|
||||
case 4: {
|
||||
//UR:Type/[Sequencing]/[Digest]/[Fragment]
|
||||
return checkAndGetSequence(pieces[1]);
|
||||
}
|
||||
default:
|
||||
throw new Error("invalid workload pieces length: expect 2 / 3 / 4 bug got " + pieces.length);
|
||||
}
|
||||
};
|
||||
//# sourceMappingURL=decodeUR.js.map
|
37
blue_modules/bc-ur/dist/encodeUR.js
vendored
Normal file
37
blue_modules/bc-ur/dist/encodeUR.js
vendored
Normal file
|
@ -0,0 +1,37 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.encodeUR = void 0;
|
||||
var miniCbor_1 = require("./miniCbor");
|
||||
var bc_bech32_1 = require("bc-bech32");
|
||||
var utils_1 = require("./utils");
|
||||
var composeUR = function (payload, type) {
|
||||
if (type === void 0) { type = 'bytes'; }
|
||||
return "ur:" + type + "/" + payload;
|
||||
};
|
||||
var composeDigest = function (payload, digest) {
|
||||
return digest + "/" + payload;
|
||||
};
|
||||
var composeSequencing = function (payload, index, total) {
|
||||
return index + 1 + "of" + total + "/" + payload;
|
||||
};
|
||||
var composeHeadersToFragments = function (fragments, digest, type) {
|
||||
if (type === void 0) { type = 'bytes'; }
|
||||
if (fragments.length === 1) {
|
||||
return [composeUR(fragments[0])];
|
||||
}
|
||||
else {
|
||||
return fragments.map(function (f, index) {
|
||||
return utils_1.compose3(function (payload) { return composeUR(payload, type); }, function (payload) { return composeSequencing(payload, index, fragments.length); }, function (payload) { return composeDigest(payload, digest); })(f);
|
||||
});
|
||||
}
|
||||
};
|
||||
exports.encodeUR = function (payload, fragmentCapacity) {
|
||||
if (fragmentCapacity === void 0) { fragmentCapacity = 200; }
|
||||
var cborPayload = miniCbor_1.encodeSimpleCBOR(payload);
|
||||
var bc32Payload = bc_bech32_1.encodeBc32Data(cborPayload);
|
||||
var digest = utils_1.sha256Hash(Buffer.from(cborPayload, 'hex')).toString('hex');
|
||||
var bc32Digest = bc_bech32_1.encodeBc32Data(digest);
|
||||
var fragments = bc32Payload.match(new RegExp('.{1,' + fragmentCapacity + '}', 'g'));
|
||||
return composeHeadersToFragments(fragments, bc32Digest, 'bytes');
|
||||
};
|
||||
//# sourceMappingURL=encodeUR.js.map
|
8
blue_modules/bc-ur/dist/index.js
vendored
Normal file
8
blue_modules/bc-ur/dist/index.js
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
var encodeUR_1 = require("./encodeUR");
|
||||
Object.defineProperty(exports, "encodeUR", { enumerable: true, get: function () { return encodeUR_1.encodeUR; } });
|
||||
var decodeUR_1 = require("./decodeUR");
|
||||
Object.defineProperty(exports, "decodeUR", { enumerable: true, get: function () { return decodeUR_1.decodeUR; } });
|
||||
Object.defineProperty(exports, "extractSingleWorkload", { enumerable: true, get: function () { return decodeUR_1.extractSingleWorkload; } });
|
||||
//# sourceMappingURL=index.js.map
|
62
blue_modules/bc-ur/dist/miniCbor.js
vendored
Normal file
62
blue_modules/bc-ur/dist/miniCbor.js
vendored
Normal file
|
@ -0,0 +1,62 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.decodeSimpleCBOR = exports.encodeSimpleCBOR = exports.composeHeader = void 0;
|
||||
/*
|
||||
this an simple cbor implementation which is just using
|
||||
on BCR-05
|
||||
*/
|
||||
exports.composeHeader = function (length) {
|
||||
var header;
|
||||
if (length > 0 && length <= 23) {
|
||||
header = Buffer.from([0x40 + length]);
|
||||
}
|
||||
if (length >= 24 && length <= 255) {
|
||||
var headerLength = Buffer.alloc(1);
|
||||
headerLength.writeUInt8(length);
|
||||
header = Buffer.concat([Buffer.from([0x58]), headerLength]);
|
||||
}
|
||||
if (length >= 256 && length <= 65535) {
|
||||
var headerLength = Buffer.alloc(2);
|
||||
headerLength.writeUInt16BE(length);
|
||||
header = Buffer.concat([Buffer.from([0x59]), headerLength]);
|
||||
}
|
||||
if (length >= 65536 && length <= Math.pow(2, 32) - 1) {
|
||||
var headerLength = Buffer.alloc(4);
|
||||
headerLength.writeUInt32BE(length);
|
||||
header = Buffer.concat([Buffer.from([0x60]), headerLength]);
|
||||
}
|
||||
return header;
|
||||
};
|
||||
exports.encodeSimpleCBOR = function (data) {
|
||||
var bufferData = Buffer.from(data, 'hex');
|
||||
if (bufferData.length <= 0 || bufferData.length >= Math.pow(2, 32)) {
|
||||
throw new Error('data is too large');
|
||||
}
|
||||
var header = exports.composeHeader(bufferData.length);
|
||||
var endcoded = Buffer.concat([header, bufferData]);
|
||||
return endcoded.toString('hex');
|
||||
};
|
||||
exports.decodeSimpleCBOR = function (data) {
|
||||
var dataBuffer = Buffer.from(data, 'hex');
|
||||
if (dataBuffer.length <= 0) {
|
||||
throw new Error('input data is not valid');
|
||||
}
|
||||
var header = dataBuffer[0];
|
||||
if (header < 0x58) {
|
||||
var dataLength = header - 0x40;
|
||||
return dataBuffer.slice(1, 1 + dataLength).toString('hex');
|
||||
}
|
||||
if (header == 0x58) {
|
||||
var dataLength = dataBuffer.slice(1, 2).readUInt8();
|
||||
return dataBuffer.slice(2, 2 + dataLength).toString('hex');
|
||||
}
|
||||
if (header == 0x59) {
|
||||
var dataLength = dataBuffer.slice(1, 3).readUInt16BE();
|
||||
return dataBuffer.slice(3, 3 + dataLength).toString('hex');
|
||||
}
|
||||
if (header == 0x60) {
|
||||
var dataLength = dataBuffer.slice(1, 5).readUInt32BE();
|
||||
return dataBuffer.slice(5, 5 + dataLength).toString('hex');
|
||||
}
|
||||
};
|
||||
//# sourceMappingURL=miniCbor.js.map
|
11
blue_modules/bc-ur/dist/utils.js
vendored
Normal file
11
blue_modules/bc-ur/dist/utils.js
vendored
Normal file
|
@ -0,0 +1,11 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.compose3 = exports.sha256Hash = void 0;
|
||||
var bitcoinjs_lib_1 = require("bitcoinjs-lib");
|
||||
exports.sha256Hash = function (data) {
|
||||
return bitcoinjs_lib_1.crypto.sha256(data);
|
||||
};
|
||||
exports.compose3 = function (f, g, h) { return function (x) {
|
||||
return f(g(h(x)));
|
||||
}; };
|
||||
//# sourceMappingURL=utils.js.map
|
81
blue_modules/bc-ur/package.json
Normal file
81
blue_modules/bc-ur/package.json
Normal file
|
@ -0,0 +1,81 @@
|
|||
{
|
||||
"_args": [
|
||||
[
|
||||
"bc-ur@0.1.6",
|
||||
"/home/user/BlueWallet"
|
||||
]
|
||||
],
|
||||
"_from": "bc-ur@0.1.6",
|
||||
"_id": "bc-ur@0.1.6",
|
||||
"_inBundle": false,
|
||||
"_integrity": "sha512-k5jZLNgiCMQH5d/4lwsa6DJjH12vzdTEr9qVH1y9UPzJW32Ga1u8iC0KDAqtYnkvh8NR4DW8Fco6D2hphHZLzg==",
|
||||
"_location": "/bc-ur",
|
||||
"_phantomChildren": {},
|
||||
"_requested": {
|
||||
"type": "version",
|
||||
"registry": true,
|
||||
"raw": "bc-ur@0.1.6",
|
||||
"name": "bc-ur",
|
||||
"escapedName": "bc-ur",
|
||||
"rawSpec": "0.1.6",
|
||||
"saveSpec": null,
|
||||
"fetchSpec": "0.1.6"
|
||||
},
|
||||
"_requiredBy": [
|
||||
"/"
|
||||
],
|
||||
"_resolved": "https://registry.npmjs.org/bc-ur/-/bc-ur-0.1.6.tgz",
|
||||
"_spec": "0.1.6",
|
||||
"_where": "/home/user/BlueWallet",
|
||||
"author": {
|
||||
"name": "aaronisme",
|
||||
"email": "aarondongchen@gmail.com"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/CoboVault/cobo-vault-blockchain-base/issues"
|
||||
},
|
||||
"description": "BlockChain Commons Uniform Resources",
|
||||
"directories": {
|
||||
"lib": "src",
|
||||
"test": "__tests__"
|
||||
},
|
||||
"files": [
|
||||
"src",
|
||||
"dist"
|
||||
],
|
||||
"gitHead": "2b6ffe86287ae1a58def5d8e485abc413fe1558f",
|
||||
"homepage": "https://github.com/CoboVault/cobo-vault-blockchain-base#readme",
|
||||
"license": "MIT",
|
||||
"main": "dist/index.js",
|
||||
"name": "bc-ur",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/CoboVault/cobo-vault-blockchain-base.git"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "npm run clean && npx tsc -p tsconfig.json -outDir ./dist",
|
||||
"clean": "rm -rf ./dist",
|
||||
"test": "echo \"Error: run tests from root\" && exit 1"
|
||||
},
|
||||
"version": "0.1.6",
|
||||
"react-native": {
|
||||
"path": "path-browserify",
|
||||
"fs": "react-native-level-fs",
|
||||
"_stream_transform": "readable-stream/transform",
|
||||
"_stream_readable": "readable-stream/readable",
|
||||
"_stream_writable": "readable-stream/writable",
|
||||
"_stream_duplex": "readable-stream/duplex",
|
||||
"_stream_passthrough": "readable-stream/passthrough",
|
||||
"stream": "stream-browserify"
|
||||
},
|
||||
"browser": {
|
||||
"path": "path-browserify",
|
||||
"fs": "react-native-level-fs",
|
||||
"_stream_transform": "readable-stream/transform",
|
||||
"_stream_readable": "readable-stream/readable",
|
||||
"_stream_writable": "readable-stream/writable",
|
||||
"_stream_duplex": "readable-stream/duplex",
|
||||
"_stream_passthrough": "readable-stream/passthrough",
|
||||
"stream": "stream-browserify"
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
/* global alert */
|
||||
import {
|
||||
AppStorage,
|
||||
SegwitP2SHWallet,
|
||||
LegacyWallet,
|
||||
WatchOnlyWallet,
|
||||
|
@ -16,8 +17,7 @@ import {
|
|||
import ReactNativeHapticFeedback from 'react-native-haptic-feedback';
|
||||
const EV = require('../events');
|
||||
const A = require('../analytics');
|
||||
/** @type {AppStorage} */
|
||||
const BlueApp = require('../BlueApp');
|
||||
const BlueApp: AppStorage = require('../BlueApp');
|
||||
const loc = require('../loc');
|
||||
const bip38 = require('../blue_modules/bip38');
|
||||
const wif = require('wif');
|
||||
|
@ -38,8 +38,9 @@ export default class WalletImport {
|
|||
alert('This wallet has been previously imported.');
|
||||
WalletImport.removePlaceholderWallet();
|
||||
} else {
|
||||
const emptyWalletLabel = new LegacyWallet().getLabel();
|
||||
ReactNativeHapticFeedback.trigger('notificationSuccess', { ignoreAndroidSystemSettings: false });
|
||||
w.setLabel(loc.wallets.import.imported + ' ' + w.typeReadable);
|
||||
if (w.getLabel() === emptyWalletLabel) w.setLabel(loc.wallets.import.imported + ' ' + w.typeReadable);
|
||||
w.setUserHasSavedExport(true);
|
||||
if (additionalProperties) {
|
||||
for (const [key, value] of Object.entries(additionalProperties)) {
|
||||
|
|
|
@ -153,6 +153,13 @@ export class AbstractWallet {
|
|||
this.secret = parsedSecret.keystore.xpub;
|
||||
this.masterFingerprint = masterFingerprint;
|
||||
}
|
||||
// It is a Cobo Vault Hardware Wallet
|
||||
if (parsedSecret && parsedSecret.ExtPubKey && parsedSecret.MasterFingerprint) {
|
||||
this.secret = parsedSecret.ExtPubKey;
|
||||
const mfp = Buffer.from(parsedSecret.MasterFingerprint, 'hex').reverse().toString('hex');
|
||||
this.masterFingerprint = parseInt(mfp, 16);
|
||||
this.setLabel('Cobo Vault ' + parsedSecret.MasterFingerprint);
|
||||
}
|
||||
} catch (_) {}
|
||||
return this;
|
||||
}
|
||||
|
|
|
@ -372,9 +372,9 @@ PODS:
|
|||
- React
|
||||
- RNWatch (0.5.0):
|
||||
- React
|
||||
- Sentry (5.1.4):
|
||||
- Sentry/Core (= 5.1.4)
|
||||
- Sentry/Core (5.1.4)
|
||||
- Sentry (5.1.5):
|
||||
- Sentry/Core (= 5.1.5)
|
||||
- Sentry/Core (5.1.5)
|
||||
- swift_qrcodejs (1.1.2)
|
||||
- ToolTipMenu (5.2.0):
|
||||
- React
|
||||
|
@ -682,7 +682,7 @@ SPEC CHECKSUMS:
|
|||
RNSVG: ce9d996113475209013317e48b05c21ee988d42e
|
||||
RNVectorIcons: 0bb4def82230be1333ddaeee9fcba45f0b288ed4
|
||||
RNWatch: 4de37878bbf071847e438b089c01d4ad8d4eddae
|
||||
Sentry: 74f2ad93a20a5567a1badaedf5b3f60b142ad082
|
||||
Sentry: e73af5462d1e7978c571aa2fdb861053938d7efd
|
||||
swift_qrcodejs: 4d024fc98b0778b804ec6a5c810880fd092aec9d
|
||||
ToolTipMenu: 4d89d95ddffd7539230bdbe02ee51bbde362e37e
|
||||
Yoga: 3ebccbdd559724312790e7742142d062476b698e
|
||||
|
|
|
@ -101,7 +101,6 @@ dayjs.extend(relativeTime);
|
|||
dayjs.locale(lang.split('_')[0]);
|
||||
}
|
||||
} else {
|
||||
console.log(RNLocalize.getLocales());
|
||||
const locales = RNLocalize.getLocales();
|
||||
if (Object.keys(AvailableLanguages).some(language => language === locales[0])) {
|
||||
strings.saveLanguage(locales[0].languageCode);
|
||||
|
|
8
package-lock.json
generated
8
package-lock.json
generated
|
@ -5901,6 +5901,14 @@
|
|||
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz",
|
||||
"integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g=="
|
||||
},
|
||||
"bc-bech32": {
|
||||
"version": "file:blue_modules/bc-bech32",
|
||||
"integrity": "sha512-lwAn5R4LUhcnyrZgNx3YdDPr5+nseM4kARANcv8i0YOMtnPJRTF7B7TZzS3DYgC6tff/aR2W/3jGoY/SJMs6MA=="
|
||||
},
|
||||
"bc-ur": {
|
||||
"version": "file:blue_modules/bc-ur",
|
||||
"integrity": "sha512-k5jZLNgiCMQH5d/4lwsa6DJjH12vzdTEr9qVH1y9UPzJW32Ga1u8iC0KDAqtYnkvh8NR4DW8Fco6D2hphHZLzg=="
|
||||
},
|
||||
"bcrypt-pbkdf": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
|
||||
|
|
|
@ -70,6 +70,8 @@
|
|||
"@sentry/react-native": "1.4.5",
|
||||
"amplitude-js": "5.11.0",
|
||||
"assert": "1.5.0",
|
||||
"bc-ur": "file:blue_modules/bc-ur",
|
||||
"bc-bech32": "file:blue_modules/bc-bech32",
|
||||
"bech32": "1.1.3",
|
||||
"bignumber.js": "9.0.0",
|
||||
"bip21": "2.0.2",
|
||||
|
|
|
@ -14,7 +14,6 @@ import {
|
|||
PermissionsAndroid,
|
||||
StyleSheet,
|
||||
} from 'react-native';
|
||||
import QRCode from 'react-native-qrcode-svg';
|
||||
import Clipboard from '@react-native-community/clipboard';
|
||||
import {
|
||||
BlueButton,
|
||||
|
@ -25,6 +24,7 @@ import {
|
|||
BlueSpacing20,
|
||||
BlueCopyToClipboardButton,
|
||||
BlueBigCheckmark,
|
||||
DynamicQRCode,
|
||||
} from '../../BlueComponents';
|
||||
import PropTypes from 'prop-types';
|
||||
import Share from 'react-native-share';
|
||||
|
@ -32,6 +32,8 @@ import ReactNativeHapticFeedback from 'react-native-haptic-feedback';
|
|||
import { RNCamera } from 'react-native-camera';
|
||||
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';
|
||||
const loc = require('../../loc');
|
||||
const EV = require('../../events');
|
||||
const BlueElectrum = require('../../BlueElectrum');
|
||||
|
@ -122,9 +124,63 @@ export default class PsbtWithHardwareWallet extends Component {
|
|||
|
||||
cameraRef = null;
|
||||
|
||||
_onReadUniformResource = ur => {
|
||||
try {
|
||||
const [index, total] = extractSingleWorkload(ur);
|
||||
const { animatedQRCodeData } = this.state;
|
||||
if (animatedQRCodeData.length > 0) {
|
||||
const currentTotal = animatedQRCodeData[0].total;
|
||||
if (total !== currentTotal) {
|
||||
alert('invalid animated QRCode');
|
||||
this.setState({ renderScanner: false });
|
||||
}
|
||||
}
|
||||
if (!animatedQRCodeData.find(i => i.index === index)) {
|
||||
this.setState(
|
||||
state => ({
|
||||
animatedQRCodeData: [
|
||||
...state.animatedQRCodeData,
|
||||
{
|
||||
index,
|
||||
total,
|
||||
data: ur,
|
||||
},
|
||||
],
|
||||
}),
|
||||
() => {
|
||||
if (this.state.animatedQRCodeData.length === total) {
|
||||
this.setState(
|
||||
{
|
||||
renderScanner: false,
|
||||
},
|
||||
() => {
|
||||
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() });
|
||||
},
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
} catch (Err) {
|
||||
alert('invalid animated QRCode fragment, please try again');
|
||||
}
|
||||
};
|
||||
|
||||
_combinePSBT = receivedPSBT => {
|
||||
return this.state.fromWallet.combinePsbt(
|
||||
this.state.isFirstPSBTAlreadyBase64 ? this.state.psbt : this.state.psbt.toBase64(),
|
||||
receivedPSBT,
|
||||
);
|
||||
};
|
||||
|
||||
onBarCodeRead = ret => {
|
||||
if (RNCamera.Constants.CameraStatus === RNCamera.Constants.CameraStatus.READY) this.cameraRef.pausePreview();
|
||||
|
||||
if (ret.data.toUpperCase().startsWith('UR')) {
|
||||
return this._onReadUniformResource(ret.data);
|
||||
}
|
||||
if (ret.data.indexOf('+') === -1 && ret.data.indexOf('=') === -1 && ret.data.indexOf('=') === -1) {
|
||||
// this looks like NOT base64, so maybe its transaction's hex
|
||||
this.setState({ renderScanner: false, txhex: ret.data });
|
||||
|
@ -133,10 +189,7 @@ export default class PsbtWithHardwareWallet extends Component {
|
|||
|
||||
this.setState({ renderScanner: false }, () => {
|
||||
try {
|
||||
const Tx = this.state.fromWallet.combinePsbt(
|
||||
this.state.isFirstPSBTAlreadyBase64 ? this.state.psbt : this.state.psbt.toBase64(),
|
||||
ret.data,
|
||||
);
|
||||
const Tx = this._combinePSBT(ret.data);
|
||||
this.setState({ txhex: Tx.toHex() });
|
||||
} catch (Err) {
|
||||
alert(Err);
|
||||
|
@ -157,6 +210,7 @@ export default class PsbtWithHardwareWallet extends Component {
|
|||
isSecondPSBTAlreadyBase64: false,
|
||||
deepLinkPSBT: undefined,
|
||||
txhex: props.route.params.txhex || undefined,
|
||||
animatedQRCodeData: [],
|
||||
};
|
||||
this.fileName = `${Date.now()}.psbt`;
|
||||
}
|
||||
|
@ -245,7 +299,7 @@ export default class PsbtWithHardwareWallet extends Component {
|
|||
<SafeBlueArea style={styles.root}>
|
||||
<BlueBigCheckmark style={styles.blueBigCheckmark} />
|
||||
<BlueCard>
|
||||
<BlueButton onPress={this.props.navigation.dangerouslyGetParent().pop} title={loc.send.success.done} />
|
||||
<BlueButton onPress={() => this.props.navigation.dangerouslyGetParent().pop()} title={loc.send.success.done} />
|
||||
</BlueCard>
|
||||
</SafeBlueArea>
|
||||
);
|
||||
|
@ -343,13 +397,7 @@ export default class PsbtWithHardwareWallet extends Component {
|
|||
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()}
|
||||
size={this.state.qrCodeHeight}
|
||||
color={BlueApp.settings.foregroundColor}
|
||||
logoBackgroundColor={BlueApp.settings.brandingColor}
|
||||
ecl="L"
|
||||
/>
|
||||
<DynamicQRCode value={this.state.psbt.toHex()} capacity={200} />
|
||||
<BlueSpacing20 />
|
||||
<BlueButton
|
||||
icon={{
|
||||
|
@ -357,7 +405,7 @@ export default class PsbtWithHardwareWallet extends Component {
|
|||
type: 'font-awesome',
|
||||
color: BlueApp.settings.buttonTextColor,
|
||||
}}
|
||||
onPress={() => this.setState({ renderScanner: true })}
|
||||
onPress={() => this.setState({ renderScanner: true, animatedQRCodeData: [] })}
|
||||
title="Scan Signed Transaction"
|
||||
/>
|
||||
<BlueSpacing20 />
|
||||
|
|
|
@ -103,6 +103,22 @@ describe('Watch only wallet', () => {
|
|||
);
|
||||
});
|
||||
|
||||
it('can import cobo vault JSON skeleton wallet', async () => {
|
||||
const skeleton =
|
||||
'{"ExtPubKey":"zpub6rcabYFcdr41zyUNRWRyHYs2Sm86E5XV8RjjRzTFYsiCngteeZnkwaF2xuhjmM6kpHjuNpFW42BMhzPmFwXt48e1FhddMB7xidZzN4SF24K","MasterFingerprint":"5271c071","CoboVaultFirmwareVersion":"1.2.4(BTC-Only)"}';
|
||||
const w = new WatchOnlyWallet();
|
||||
w.setSecret(skeleton);
|
||||
w.init();
|
||||
assert.ok(w.valid());
|
||||
assert.strictEqual(
|
||||
w.getSecret(),
|
||||
'zpub6rcabYFcdr41zyUNRWRyHYs2Sm86E5XV8RjjRzTFYsiCngteeZnkwaF2xuhjmM6kpHjuNpFW42BMhzPmFwXt48e1FhddMB7xidZzN4SF24K',
|
||||
);
|
||||
assert.strictEqual(w.getMasterFingerprint(), 1908437330);
|
||||
assert.strictEqual(w.getMasterFingerprintHex(), '5271c071');
|
||||
assert.strictEqual(w.getLabel(), 'Cobo Vault 5271c071');
|
||||
});
|
||||
|
||||
it('can combine signed PSBT and prepare it for broadcast', async () => {
|
||||
const w = new WatchOnlyWallet();
|
||||
w.setSecret('zpub6rjLjQVqVnj7crz9E4QWj4WgczmEseJq22u2B6k2HZr6NE2PQx3ZYg8BnbjN9kCfHymSeMd2EpwpM5iiz5Nrb3TzvddxW2RMcE3VXdVaXHk');
|
||||
|
|
Loading…
Add table
Reference in a new issue