Merge pull request #310 from Bitcoin-com/wallet/task/500

Upgrade bitcoin-uri.service.js
This commit is contained in:
Jean-Baptiste Dominguez 2018-08-29 14:27:23 +09:00 committed by GitHub
commit b35ee316a8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 496 additions and 152 deletions

View file

@ -99,6 +99,7 @@ angular.module('copayApp.controllers').controller('walletSelectorController', fu
$scope.requestAmountSecondary = fiatAmount;
$scope.requestCurrencySecondary = fiatCurrrency;
}
$scope.$apply();
}
});
}

View file

@ -1,5 +1,8 @@
'use strict';
// https://github.com/bitcoin/bips/blob/master/bip-0021.mediawiki
// https://github.com/bitcoin/bips/blob/master/bip-0072.mediawiki
(function(){
angular
@ -9,7 +12,6 @@
function bitcoinUriService(bitcoinCashJsService, bwcService, $log) {
var bch = bitcoinCashJsService.getBitcoinCashJs();
var bitcore = bwcService.getBitcore();
var cashAddrRe = /^((?:q|p)[a-z0-9]{41})|((?:Q|P)[A-Z0-9]{41})$/;
var service = {
parse: parse
@ -17,38 +19,69 @@
return service;
function bitpayAddrOnMainnet(address) {
var Address = bch.Address;
var BitpayFormat = Address.BitpayFormat;
var mainnet = bch.Networks.mainnet;
function isValidCashAddr(address, network) {
var privateKey = new bch.PrivateKey('testnet');
var address1 = privateKey.toAddress();
console.log('legacy pub:', address1.toString());
//var addrss = bitcoinCashJsService.readAddress(address1);
//console.log('generated:', addrss.cashaddr);
//bch.Address.fromString(address1, 'testnet');
console.log('generated:', address1.toString('cashaddr'));
var isValid = false;
var prefix = network === 'testnet' ? 'bchtest:' : 'bitcoincash:';
var result = null;
if (address[0] == 'C') {
try {
if (cashAddrRe.test(address)) {
// bitcoinCashJs.Address.isValid() assumes legacy address for string data, so does not work with cashaddr.
var bchAddresses = bitcoinCashJsService.readAddress(address.toLowerCase());
if (bchAddresses) {
var legacyAddress = bchAddresses.legacy;
if (bch.Address.isValid(legacyAddress, network)) {
isValid = true;
result = Address.fromString(address, mainnet, 'pubkeyhash', BitpayFormat);
} catch (e) {};
} else if (address[0] == 'H') {
try {
result = Address.fromString(address, mainnet, 'scripthash', BitpayFormat);
} catch (e) {};
}
return result;
}
function cashAddrOnMainnet(address) {
var Address = bch.Address;
var CashAddrFormat = Address.CashAddrFormat;
var mainnet = bch.Networks.mainnet;
var prefixed = 'bitcoincash:' + address;
var result = null;
if (address[0] == 'q') {
try {
result = Address.fromString(prefixed, mainnet, 'pubkeyhash', CashAddrFormat);
} catch (e) {};
} else if (address[0] == 'p') {
try {
result = Address.fromString(prefixed, mainnet, 'scripthash', CashAddrFormat);
} catch (e) {};
}
} catch (e) {
// Nop - Must not be a valid cashAddr.
$log.error('Error validating address.', e);
return result;
}
console.log(address,'isValidCashAddr:', isValid);
return isValid;
function cashAddrOnTestnet(address) {
var Address = bch.Address;
var CashAddrFormat = Address.CashAddrFormat;
var testnet = bch.Networks.testnet;
var prefixed = 'bchtest:' + address;
var result = null;
if (address[0] == 'q') {
try {
result = Address.fromString(prefixed, testnet, 'pubkeyhash', CashAddrFormat);
} catch (e) {};
} else if (address[0] == 'p') {
try {
result = Address.fromString(prefixed, testnet, 'scripthash', CashAddrFormat);
} catch (e) {};
}
return result;
}
@ -59,36 +92,48 @@
returns:
{
address: '',
amount: '',
coin: '',
copayInvitation: '',
isValid: false,
label: '',
legacyAddress: '',
message: '',
other: {
somethingIDontUnderstand: 'Its value'
},
privateKey: {
encrypted: '',
wif: ''
}'',
publicAddress: {
bitpay: '',
cashAddr: '',
legacy: '',
},
req: {
"req-param0": "",
"req-param1": ""
"req-param0": '',
"req-param1": ''
},
testnet: false,
url: ''
url: '' // For BIP70
}
// Need to do testnet, and copay too
Only fields that are present in the data are defined in the returned object. Both privateKey and publicAddress only have 1 field defined, if they exist at all.
The exception to this is the coin property, which is determined from other data, such as the prefix or address type.
*/
// bitcoincash:?r=https://bitpay.com/i/GLRoZMZxaWBqLqpoXexzoD
function parse(uri) {
function parse(data) {
var parsed = {
isValid: false
};
if (typeof data !== 'string') {
return parsed;
}
// Identify prefix
var trimmed = uri.trim();
var trimmed = data.trim();
var colonSplit = /^([\w-]*):?(.*)$/.exec(trimmed);
if (!colonSplit) {
return parsed;
@ -138,10 +183,13 @@
var address = questionMarkSplit[1];
var params = questionMarkSplit[2];
if (params.length > 0) {
var paramsSplit = params.split('&');
var others;
var req;
paramsSplit.forEach(function onParam(param){
var paramCount = paramsSplit.length;
for(var i = 0; i < paramCount; i++) {
var param = paramsSplit[i];
var valueSplit = param.split('=');
if (valueSplit.length !== 2) {
return parsed;
@ -149,28 +197,30 @@
var key = valueSplit[0];
var value = valueSplit[1];
var decodedValue = decodeURIComponent(value);
switch(key) {
case 'amount':
if (parseFloat(value)) {
parsed.amount = value;
var amount = parseFloat(decodedValue);
if (amount) { // Checking for NaN, or no numbers at all etc.
parsed.amount = decodedValue;
} else {
return parsed;
}
break;
case 'label':
parsed.label = value;
parsed.label = decodedValue;
break;
case 'message':
parsed.message = value;
parsed.message = decodedValue;
break;
case 'r':
// Could use a more comprehesive regex to test URL validity, but then how would we know
// which part of the validation it failed?
if (value.startsWith('https://')) {
parsed.url = value;
if (decodedValue.startsWith('https://')) {
parsed.url = decodedValue;
} else {
return parsed;
}
@ -179,69 +229,105 @@
default:
if (key.startsWith('req-')) {
req = req || {};
req[key] = value;
req[key] = decodedValue;
} else {
others = others || {};
others[key] = value;
others[key] = decodedValue;
}
}
});
};
}
parsed.others = others;
parsed.req = req;
// Need to do bitpay format as well? Probably
if (address) {
var addressLowerCase = address.toLowerCase();
var bch = bitcoinCashJsService.getBitcoinCashJs();
// Just a rough validation to exclude half-pasted addresses, or things obviously not bitcoin addresses
var cashAddrRe = /^((?:q|p)[a-z0-9]{41})|((?:Q|P)[A-Z0-9]{41})$/;
var copayInvitationRe = /^[0-9A-HJ-NP-Za-km-z]{70,80}$/;
//var legacyRe = /^[13][a-km-zA-HJ-NP-Z1-9]{25,34}$/;
//var legacyTestnetRe = /^[mn][a-km-zA-HJ-NP-Z1-9]{25,34}$/;
var privateKeyEncryptedRe = /^6P[1-9A-HJ-NP-Za-km-z]{56}$/;
var privateKeyForUncompressedPublicKeyRe = /^5[1-9A-HJ-NP-Za-km-z]{50}$/;
var privateKeyForUncompressedPublicKeyTestnetRe = /^9[1-9A-HJ-NP-Za-km-z]{50}$/;
var privateKeyForCompressedPublicKeyRe = /^[KL][1-9A-HJ-NP-Za-km-z]{51}$/;
var privateKeyForCompressedPublicKeyTestnetRe = /^[c][1-9A-HJ-NP-Za-km-z]{51}$/;
if (bitcore.Address.isValid(address, 'livenet')) {
parsed.address = address;
parsed.legacyAddress = address;
var bitpayAddrMainnet = bitpayAddrOnMainnet(address);
var cashAddrTestnet = cashAddrOnTestnet(addressLowerCase);
var cashAddrMainnet = cashAddrOnMainnet(addressLowerCase);
var privateKey = '';
if (parsed.testnet && cashAddrTestnet) {
parsed.address = addressLowerCase;
parsed.coin = 'bch';
parsed.publicAddress = {
cashAddr: addressLowerCase
};
parsed.isValid = true;
} else if (cashAddrMainnet) {
parsed.coin = 'bch';
parsed.publicAddress = {
cashAddr: addressLowerCase
};
parsed.testnet = false;
parsed.isValid = true;
} else if (bitcore.Address.isValid(address, 'livenet')) {
parsed.publicAddress = {
legacy: address
};
parsed.testnet = false;
parsed.isValid = true;
} else if (bitcore.Address.isValid(address, 'testnet')) {
parsed.address = address;
parsed.legacyAddress = address;
parsed.publicAddress = {
legacy: address
};
parsed.testnet = true;
parsed.isValid = true;
// bitcoinCaashJs.Address.isValid() assumes legacy address for string data, so does not work with cashaddr.
// } else if (isValidCashAddr(addressLowerCase, 'livenet')) {
} else if (cashAddrRe.test(address) && parsed.testnet) {
var cashAddr = 'bchtest:' + addressLowerCase;
parsed.address = cashAddr;
} else if (bitpayAddrMainnet) {
parsed.coin = 'bch';
// TODO: Get legacy address
} else if (cashAddrRe.test(address)) {
var cashAddr = 'bitcoincash:' + addressLowerCase;
parsed.address = cashAddr;
parsed.coin = 'bch';
var bchAddresses = bitcoinCashJsService.readAddress(cashAddr);
parsed.legacyAddress = bchAddresses['legacy'];
parsed.publicAddress = {
bitpay: address
};
parsed.testnet = false;
parsed.isValid = true;
} else if (copayInvitationRe.test(address) ) {
parsed.copayInvitation = address;
parsed.isValid = true;
} else if (privateKeyForUncompressedPublicKeyRe.test(address) || privateKeyForCompressedPublicKeyRe.test(address)) {
privateKey = address;
try {
new bitcore.PrivateKey(privateKey, 'livenet');
parsed.privateKey = { wif: privateKey };
parsed.testnet = false;
parsed.isValid = true;
} catch (e) {}
} else if (privateKeyForUncompressedPublicKeyTestnetRe.test(address) || privateKeyForCompressedPublicKeyTestnetRe.test(address)) {
privateKey = address;
try {
new bitcore.PrivateKey(privateKey, 'testnet');
parsed.privateKey = { wif: privateKey };
parsed.testnet = true;
parsed.isValid = true;
} catch (e) {}
} else if (privateKeyEncryptedRe.test(address)) {
parsed.privateKey = { encrypted: address };
parsed.isValid = true;
}
} else {
parsed.isValid = !!parsed.url; // BIP72
}
// TODO: Check for a private key here too
// If has no address, must have Url.
parsed.isValid = !!(parsed.address || parsed.url);
return parsed;
}

View file

@ -12,25 +12,151 @@ fdescribe('bitcoinUriService', function() {
});
it('Bitcoin BIP72', function() {
var parsed = bitcoinUriService.parse('bitcoin:?r=https://bitpay.com/i/CwzbKP3k3JNgXJBfuoerDr');
expect(parsed.isValid).toBe(true);
expect(parsed.coin).toBe('btc');
expect(parsed.testnet).toBeUndefined();
expect(parsed.publicAddress).toBeUndefined();
expect(parsed.url).toBe('https://bitpay.com/i/CwzbKP3k3JNgXJBfuoerDr');
});
it('Bitcoin Cash BIP72', function() {
var parsed = bitcoinUriService.parse('bitcoincash:?r=https://bitpay.com/i/SmHdie5dvBnG5kouZzEPzu');
expect(parsed.isValid).toBe(true);
expect(parsed.coin).toBe('bch');
expect(parsed.publicAddress).toBeUndefined();
expect(parsed.testnet).toBeUndefined();
expect(parsed.url).toBe('https://bitpay.com/i/SmHdie5dvBnG5kouZzEPzu');
});
it('Bitcoin Cash prefix with legacy address', function() {
var parsed = bitcoinUriService.parse('bitcoincash:1G9FA9fFnHfTYxvmXeAbBD9FwzPAVMbd3j');
expect(parsed.isValid).toBe(true);
expect(parsed.coin).toBe('bch');
expect(parsed.publicAddress.legacy).toBe('1G9FA9fFnHfTYxvmXeAbBD9FwzPAVMbd3j');
expect(parsed.testnet).toBe(false);
});
it('Bitcoin Cash prefix with legacy address on testnet', function() {
var parsed = bitcoinUriService.parse('bitcoincash:mkDQrKfSFD441JxrD1iPBsJFExgkvrPGQn');
expect(parsed.isValid).toBe(true);
expect(parsed.coin).toBe('bch');
expect(parsed.publicAddress.legacy).toBe('mkDQrKfSFD441JxrD1iPBsJFExgkvrPGQn');
expect(parsed.testnet).toBe(true);
});
it('Bitcoin Cash uri with extended params', function() {
var parsed = bitcoinUriService.parse('bitcoincash:qr8v2vqnzntykakht43rqmxq8cdjzjp795fc3vsjgc?unknown=something&mystery=Melton%20probang&req-one=ichi&req-beta=Ni%20san');
expect(parsed.isValid).toBe(true);
expect(parsed.coin).toBe('bch');
expect(parsed.others.mystery).toBe('Melton probang');
expect(parsed.others.unknown).toBe('something');
expect(parsed.publicAddress.cashAddr).toBe('qr8v2vqnzntykakht43rqmxq8cdjzjp795fc3vsjgc');
expect(parsed.req['req-beta']).toBe('Ni san');
expect(parsed.req['req-one']).toBe('ichi');
expect(parsed.testnet).toBe(false);
});
it('Bitcoin Cash uri with invalid amount', function() {
var parsed = bitcoinUriService.parse('bitcoincash:qq0knhwj4d5zy3kdph24w6etq58vwzua6sm7lhcmuk?amount=three');
expect(parsed.isValid).toBe(false);
});
it('Bitcoin testnet address', function() {
var parsed = bitcoinUriService.parse('mtWcoToWhbtPoCby5fvs8xdBujT5GGenD4');
expect(parsed.isValid).toBe(true);
expect(parsed.address).toBe('mtWcoToWhbtPoCby5fvs8xdBujT5GGenD4');
expect(parsed.coin).toBeUndefined();
expect(parsed.legacyAddress).toBe('mtWcoToWhbtPoCby5fvs8xdBujT5GGenD4');
expect(parsed.publicAddress.legacy).toBe('mtWcoToWhbtPoCby5fvs8xdBujT5GGenD4');
expect(parsed.testnet).toBe(true);
});
it('Bitcoin uri', function() {
var parsed = bitcoinUriService.parse('bitcoin:15yCdKWVKRvfXMJpPYZBqMhiGKwjKzZdLN');
expect(parsed.isValid).toBe(true);
expect(parsed.coin).toBe('btc');
expect(parsed.publicAddress.legacy).toBe('15yCdKWVKRvfXMJpPYZBqMhiGKwjKzZdLN');
expect(parsed.testnet).toBe(false);
});
it('Bitcoin uri with encoded label', function() {
var parsed = bitcoinUriService.parse('bitcoin:1MxudKDEBWZ1yjizUSf6htacenNtb3DWbT?label=Mr.%20Smith');
expect(parsed.isValid).toBe(true);
expect(parsed.coin).toBe('btc');
expect(parsed.label).toBe('Mr. Smith');
expect(parsed.publicAddress.legacy).toBe('1MxudKDEBWZ1yjizUSf6htacenNtb3DWbT');
expect(parsed.testnet).toBe(false);
});
it('Bitcoin uri with params', function() {
var parsed = bitcoinUriService.parse('bitcoin:12nCRhMDfxVnuF3uYMXv2fNxBohNmacfWu?amount=20.3&label=Luke-Jr&message=Donation%20for%20project%20xyz');
expect(parsed.isValid).toBe(true);
expect(parsed.amount).toBe('20.3');
expect(parsed.coin).toBe('btc');
expect(parsed.label).toBe('Luke-Jr');
expect(parsed.publicAddress.legacy).toBe('12nCRhMDfxVnuF3uYMXv2fNxBohNmacfWu');
expect(parsed.message).toBe('Donation for project xyz');
expect(parsed.testnet).toBe(false);
});
it('Bitcoin uri with slash', function() {
var parsed = bitcoinUriService.parse('bitcoin:/1GhpYmbRaf73AZRxDwAGr6653iZBGzdgeA');
expect(parsed.isValid).toBe(true);
expect(parsed.coin).toBe('btc');
expect(parsed.publicAddress.legacy).toBe('1GhpYmbRaf73AZRxDwAGr6653iZBGzdgeA');
expect(parsed.testnet).toBe(false);
});
it('Bitcoin uri with slashes', function() {
var parsed = bitcoinUriService.parse('bitcoin://18PCPhgZJjLxe9g3Q1BXLpL5aVut1fW3aX');
expect(parsed.isValid).toBe(true);
expect(parsed.coin).toBe('btc');
expect(parsed.publicAddress.legacy).toBe('18PCPhgZJjLxe9g3Q1BXLpL5aVut1fW3aX');
expect(parsed.testnet).toBe(false);
});
it('Bitpay without prefix', function() {
var parsed = bitcoinUriService.parse('CJoRov8TirekvajiimQpb5Hk95evA7H2Yz');
expect(parsed.isValid).toBe(true);
expect(parsed.coin).toBe('bch');
expect(parsed.publicAddress.bitpay).toBe('CJoRov8TirekvajiimQpb5Hk95evA7H2Yz');
expect(parsed.testnet).toBe(false);
});
it('legacy address', function() {
var parsed = bitcoinUriService.parse('1JXeGEu7bNEAYu6URT6dU6g1Ys6ffSAWYW');
expect(parsed.isValid).toBe(true);
expect(parsed.address).toBe('1JXeGEu7bNEAYu6URT6dU6g1Ys6ffSAWYW');
expect(parsed.coin).toBeUndefined();
expect(parsed.legacyAddress).toBe('1JXeGEu7bNEAYu6URT6dU6g1Ys6ffSAWYW');
expect(parsed.publicAddress.legacy).toBe('1JXeGEu7bNEAYu6URT6dU6g1Ys6ffSAWYW');
expect(parsed.testnet).toBe(false);
});
@ -39,20 +165,58 @@ fdescribe('bitcoinUriService', function() {
var parsed = bitcoinUriService.parse('bchtest:qpcz6pmurq9ctg5848trzz9zmuuygj4q5qam7ph3gt');
expect(parsed.isValid).toBe(true);
expect(parsed.address).toBe('bchtest:qpcz6pmurq9ctg5848trzz9zmuuygj4q5qam7ph3gt');
expect(parsed.coin).toBe('bch');
expect(parsed.legacyAddress).toBe('mqk5vE278ytt6LUZqd97wi8c3FHsSYREX4');
expect(parsed.publicAddress.cashAddr).toBe('qpcz6pmurq9ctg5848trzz9zmuuygj4q5qam7ph3gt');
expect(parsed.testnet).toBe(true);
});
it('cashAddr uppercase', function() {
var parsed = bitcoinUriService.parse('BITCOINCASH:QZZG9NMC5VX8GAP6XFATX3TWNSDN2YRMCSSULSMY44');
expect(parsed.isValid).toBe(true);
expect(parsed.coin).toBe('bch');
expect(parsed.publicAddress.cashAddr).toBe('qzzg9nmc5vx8gap6xfatx3twnsdn2yrmcssulsmy44');
expect(parsed.testnet).toBe(false);
});
it('cashAddr with dash', function() {
var parsed = bitcoinUriService.parse('bitcoin-cash:qpshfu3dk5s3e7zdcgdcun6xgxtra6uyxs7g580js0');
expect(parsed.isValid).toBe(true);
expect(parsed.coin).toBe('bch');
expect(parsed.publicAddress.cashAddr).toBe('qpshfu3dk5s3e7zdcgdcun6xgxtra6uyxs7g580js0');
expect(parsed.testnet).toBe(false);
});
it('cashAddr with prefix', function() {
var parsed = bitcoinUriService.parse('bitcoincash:qrq9p82a247lecv08ldk5p5h6ahtnjzpqcnh8yhq92');
expect(parsed.isValid).toBe(true);
expect(parsed.address).toBe('bitcoincash:qrq9p82a247lecv08ldk5p5h6ahtnjzpqcnh8yhq92');
expect(parsed.coin).toBe('bch');
expect(parsed.legacyAddress).toBe('1JXsK3HSFqoMnwh4Mevf5bTgqPcgNWX7ic');
expect(parsed.publicAddress.cashAddr).toBe('qrq9p82a247lecv08ldk5p5h6ahtnjzpqcnh8yhq92');
expect(parsed.testnet).toBe(false);
});
it('cashAddr with slash', function() {
var parsed = bitcoinUriService.parse('bitcoincash:/qzdectfmuw0xxztfx7mh045830dqcshj85hr44l35a');
expect(parsed.isValid).toBe(true);
expect(parsed.coin).toBe('bch');
expect(parsed.publicAddress.cashAddr).toBe('qzdectfmuw0xxztfx7mh045830dqcshj85hr44l35a');
expect(parsed.testnet).toBe(false);
});
it('cashAddr with slashes', function() {
var parsed = bitcoinUriService.parse('bitcoincash://qpj966w8utue75lqqq3rlgh20zkz3rmydqpq8syv9c');
expect(parsed.isValid).toBe(true);
expect(parsed.coin).toBe('bch');
expect(parsed.publicAddress.cashAddr).toBe('qpj966w8utue75lqqq3rlgh20zkz3rmydqpq8syv9c');
expect(parsed.testnet).toBe(false);
});
@ -61,12 +225,19 @@ fdescribe('bitcoinUriService', function() {
var parsed = bitcoinUriService.parse('qqen2y3l28dpk0dzsag8w027ds96u7z4pc0uxtl0nq');
expect(parsed.isValid).toBe(true);
expect(parsed.address).toBe('bitcoincash:qqen2y3l28dpk0dzsag8w027ds96u7z4pc0uxtl0nq');
expect(parsed.coin).toBe('bch');
expect(parsed.legacyAddress).toBe('15fm3EwqgBYcxkndALBfforueps5yWKReJ');
expect(parsed.publicAddress.cashAddr).toBe('qqen2y3l28dpk0dzsag8w027ds96u7z4pc0uxtl0nq');
expect(parsed.testnet).toBe(false);
});
it('copay invitation', function() {
var parsed = bitcoinUriService.parse('PD5B7rEEj72st9d5nFszyuKxJP6FAGS7idVC2SMqiMxUcWVd8JifZDJw1UgjUctxefUFE3Sz6qLbch');
expect(parsed.isValid).toBe(true);
expect(parsed.copayInvitation).toBe('PD5B7rEEj72st9d5nFszyuKxJP6FAGS7idVC2SMqiMxUcWVd8JifZDJw1UgjUctxefUFE3Sz6qLbch');
});
// Invalid addresses from https://github.com/bitcoincashorg/bitcoincash.org/blob/master/spec/cashaddr.md
it('invalid cashAddr style 1', function() {
var parsed = bitcoinUriService.parse('prefix:x64nx6hz');
@ -92,4 +263,91 @@ fdescribe('bitcoinUriService', function() {
var parsed = bitcoinUriService.parse('bchreg:555555555555555555555555555555555555555555555udxmlmrz');
expect(parsed.isValid).toBe(false);
});
it('non-string', function() {
var parsed = bitcoinUriService.parse([1, 2, 3, 4]);
expect(parsed.isValid).toBe(false);
});
it('private key encrypted with BIP38', function() {
var parsed = bitcoinUriService.parse('6PRN5nEDmX842gsBzJryPu8Tw5kcsaQq1GPLcjVQPcEStvbFAtz11JX9pX');
expect(parsed.isValid).toBe(true);
expect(parsed.privateKey.encrypted).toBe('6PRN5nEDmX842gsBzJryPu8Tw5kcsaQq1GPLcjVQPcEStvbFAtz11JX9pX');
});
it('private key for compressed pubkey mainnet', function() {
var parsed = bitcoinUriService.parse('5HueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTJ');
expect(parsed.isValid).toBe(true);
expect(parsed.privateKey.wif).toBe('5HueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTJ');
expect(parsed.testnet).toBe(false);
});
it('private key for compressed pubkey mainnet with wrong checksum', function() {
var parsed = bitcoinUriService.parse('5HueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTu');
expect(parsed.isValid).toBe(false);
});
it('private key for compressed pubkey testnet', function() {
var parsed = bitcoinUriService.parse('cNJFgo1driFnPcBdBX8BrJrpxchBWXwXCvNH5SoSkdcF6JXXwHMm');
expect(parsed.isValid).toBe(true);
expect(parsed.privateKey.wif).toBe('cNJFgo1driFnPcBdBX8BrJrpxchBWXwXCvNH5SoSkdcF6JXXwHMm');
expect(parsed.testnet).toBe(true);
});
it('private key for compressed pubkey testnet with wrong checksum', function() {
var parsed = bitcoinUriService.parse('cNJFgo1driFnPcBdBX8BrJrpxchBWXwXCvNH5SoSkdcF6JXXwHMM');
expect(parsed.isValid).toBe(false);
});
it('private key for uncompressed pubkey mainnet', function() {
var parsed = bitcoinUriService.parse('L18V3rAhCKEioPnJ4BHLCCsaYa8eSNFrMjNQ2EdwgeAdmBSnTMwx');
expect(parsed.isValid).toBe(true);
expect(parsed.privateKey.wif).toBe('L18V3rAhCKEioPnJ4BHLCCsaYa8eSNFrMjNQ2EdwgeAdmBSnTMwx');
expect(parsed.testnet).toBe(false);
});
it('private key for uncompressed pubkey mainnet with wrong checksum', function() {
var parsed = bitcoinUriService.parse('L18V3rAhCKEioPnJ4BHLCCsaYa8eSNFrMjNQ2EdwgeAdmBSnTTwx');
expect(parsed.isValid).toBe(false);
});
it('private key for uncompressed pubkey testnet', function() {
var parsed = bitcoinUriService.parse('92Pg46rUhgTT7romnV7iGW6W1gbGdeezqdbJCzShkCsYNzyyNcc');
expect(parsed.isValid).toBe(true);
expect(parsed.privateKey.wif).toBe('92Pg46rUhgTT7romnV7iGW6W1gbGdeezqdbJCzShkCsYNzyyNcc');
expect(parsed.testnet).toBe(true);
});
it('private key for uncompressed pubkey testnet with wrong checksum', function() {
var parsed = bitcoinUriService.parse('92Pg46rUhgTT7romnV7iGW6W1gbGdeezqdbJCzShkCsYNzyyNcC');
expect(parsed.isValid).toBe(false);
});
it('URL only', function() {
var parsed = bitcoinUriService.parse('https://www.google.com');
expect(parsed.isValid).toBe(false);
});
});

View file

@ -1,6 +1,6 @@
'use strict';
angular.module('copayApp.services').factory('incomingData', function($log, $state, $timeout, $ionicHistory, bitcore, bitcoreCash, $rootScope, payproService, scannerService, sendFlowService, appConfigService, popupService, gettextCatalog, bitcoinCashJsService) {
angular.module('copayApp.services').factory('incomingData', function(bitcoinUriService, $log, $state, $timeout, $ionicHistory, bitcore, bitcoreCash, $rootScope, payproService, scannerService, sendFlowService, appConfigService, popupService, gettextCatalog, bitcoinCashJsService) {
var root = {};
@ -11,6 +11,7 @@ angular.module('copayApp.services').factory('incomingData', function($log, $stat
root.redir = function(data, serviceId, serviceData) {
var originalAddress = null;
var noPrefixInAddress = 0;
var allParsed = bitcoinUriService.parse(data);
if (data.toLowerCase().indexOf('bitcoin') < 0) {
noPrefixInAddress = 1;
@ -114,10 +115,10 @@ angular.module('copayApp.services').factory('incomingData', function($log, $stat
}, 100);
}
// data extensions for Payment Protocol with non-backwards-compatible request
if ((/^bitcoin(cash)?:\?r=[\w+]/).exec(data)) {
var coin = data.indexOf('bitcoincash') >= 0 ? 'bch' : 'btc';
data = decodeURIComponent(data.replace(/bitcoin(cash)?:\?r=/, ''));
if (coin == 'bch') {
if (allParsed.isValid && allParsed.coin && allParsed.url) {
var coin = allParsed.coin;
data = allParsed.url;
if (allParsed.coin == 'bch') {
payproService.getPayProDetailsViaHttp(data, function onGetPayProDetailsViaHttp(err, details) {
if (err) {
var message = err.toString();
@ -127,15 +128,15 @@ angular.module('copayApp.services').factory('incomingData', function($log, $stat
}
popupService.showAlert(gettextCatalog.getString('Error'), message)
} else {
handlePayPro(details, coin);
handlePayPro(details, allParsed.coin);
}
});
} else {
payproService.getPayProDetails(data, coin, function onGetPayProDetails(err, details) {
payproService.getPayProDetails(data, allParsed.coin, function onGetPayProDetails(err, details) {
if (err) {
popupService.showAlert(gettextCatalog.getString('Error'), err);
} else {
handlePayPro(details, coin);
handlePayPro(details, allParsed.coin);
}
});
}
@ -144,6 +145,7 @@ angular.module('copayApp.services').factory('incomingData', function($log, $stat
data = sanitizeUri(data);
var addr = '';
// Bitcoin URL
if (bitcore.URI.isValid(data)) {
var coin = 'btc';
@ -166,18 +168,18 @@ angular.module('copayApp.services').factory('incomingData', function($log, $stat
}
return true;
// Cash URI
} else if (bitcoreCash.URI.isValid(data)) {
} else if (allParsed.isValid && allParsed.publicAddress && allParsed.publicAddress.cashAddr) {
var coin = 'bch';
var parsed = new bitcoreCash.URI(data);
var addr = parsed.address ? parsed.address.toString() : '';
var prefix = allParsed.testnet ? 'bchtest:' : 'bitcoincash:';
addr = bitcoinCashJsService.readAddress(prefix + allParsed.publicAddress.cashAddr).legacy;
var message = parsed.message;
var amount = parsed.amount ? parsed.amount : '';
// paypro not yet supported on cash
if (parsed.r) {
payproService.getPayProDetails(parsed.r, coin, function(err, details) {
if (allParsed.url) {
payproService.getPayProDetails(allParsed.url, coin, function(err, details) {
if (err) {
if (addr && amount)
goSend(addr, amount, message, coin, serviceId, serviceData);
@ -401,7 +403,7 @@ angular.module('copayApp.services').factory('incomingData', function($log, $stat
function handlePayPro(payProData, coin) {
console.log(payProData);
console.log('payProData', payProData);
var toAddr = payProData.toAddress;
var amount = payProData.amount;
@ -456,9 +458,6 @@ angular.module('copayApp.services').factory('incomingData', function($log, $stat
stateParams.requiredFeeRate = thirdPartyData.requiredFeeRate * 1024;
}
// This does not make sense, thirdPartyData gets added by stateParams below
//sendFlowService.pushState(thirdPartyData);
scannerService.pausePreview();
$state.go('tabs.send', {}, {
'reload': true,