From a4ab20abba5a766984111697b650e4310b6b8923 Mon Sep 17 00:00:00 2001 From: Brendon Duncan Date: Tue, 28 Aug 2018 14:17:43 +1200 Subject: [PATCH 01/19] Tests for BIP72. --- src/js/services/bitcoin-uri.service.js | 13 ++++++-- src/js/services/bitcoin-uri.service.spec.js | 36 +++++++++++++++++++++ 2 files changed, 46 insertions(+), 3 deletions(-) diff --git a/src/js/services/bitcoin-uri.service.js b/src/js/services/bitcoin-uri.service.js index 40dc48b9f..a7566812d 100644 --- a/src/js/services/bitcoin-uri.service.js +++ b/src/js/services/bitcoin-uri.service.js @@ -1,5 +1,7 @@ 'use strict'; +// https://en.bitcoin.it/wiki/BIP_0072 + (function(){ angular @@ -17,9 +19,7 @@ return service; - - - function isValidCashAddr(address, network) { + function generateTestData() { var privateKey = new bch.PrivateKey('testnet'); var address1 = privateKey.toAddress(); console.log('legacy pub:', address1.toString()); @@ -27,7 +27,13 @@ //console.log('generated:', addrss.cashaddr); //bch.Address.fromString(address1, 'testnet'); console.log('generated:', address1.toString('cashaddr')); + + } + + + function isValidCashAddr(address, network) { + var isValid = false; var prefix = network === 'testnet' ? 'bchtest:' : 'bitcoincash:'; @@ -219,6 +225,7 @@ parsed.address = cashAddr; parsed.coin = 'bch'; // TODO: Get legacy address + } else if (cashAddrRe.test(address)) { var cashAddr = 'bitcoincash:' + addressLowerCase; diff --git a/src/js/services/bitcoin-uri.service.spec.js b/src/js/services/bitcoin-uri.service.spec.js index 5cbfdb215..8ac2608a3 100644 --- a/src/js/services/bitcoin-uri.service.spec.js +++ b/src/js/services/bitcoin-uri.service.spec.js @@ -11,6 +11,42 @@ fdescribe('bitcoinUriService', function() { }); }); + + + it('Bitcoin Cash BIP72', function() { + + var parsed = bitcoinUriService.parse('bitcoincash:?r=https://bitpay.com/i/SmHdie5dvBnG5kouZzEPzu'); + + expect(parsed.isValid).toBe(true); + expect(parsed.address).toBeUndefined() + expect(parsed.coin).toBe('bch'); + expect(parsed.legacyAddress).toBeUndefined(); + expect(parsed.testnet).toBeUndefined(); + expect(parsed.url).toBe('https://bitpay.com/i/SmHdie5dvBnG5kouZzEPzu'); + }); + + it('Bitcoin BIP72', function() { + + var parsed = bitcoinUriService.parse('bitcoin:?r=https://bitpay.com/i/CwzbKP3k3JNgXJBfuoerDr'); + + expect(parsed.isValid).toBe(true); + expect(parsed.address).toBeUndefined() + expect(parsed.coin).toBe('btc'); + expect(parsed.legacyAddress).toBeUndefined(); + expect(parsed.testnet).toBeUndefined(); + expect(parsed.url).toBe('https://bitpay.com/i/CwzbKP3k3JNgXJBfuoerDr'); + }); + + 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.testnet).toBe(true); + }); it('Bitcoin testnet address', function() { From 4d525c85d77d881177cc5a433a95d8d9eee1c194 Mon Sep 17 00:00:00 2001 From: Brendon Duncan Date: Tue, 28 Aug 2018 17:00:52 +1200 Subject: [PATCH 02/19] CashAddr on testnet and Bitpay format on mainnet. --- src/js/services/bitcoin-uri.service.js | 219 ++++++++++++++------ src/js/services/bitcoin-uri.service.spec.js | 123 +++++++++-- 2 files changed, 256 insertions(+), 86 deletions(-) diff --git a/src/js/services/bitcoin-uri.service.js b/src/js/services/bitcoin-uri.service.js index a7566812d..f6f91ddce 100644 --- a/src/js/services/bitcoin-uri.service.js +++ b/src/js/services/bitcoin-uri.service.js @@ -1,6 +1,7 @@ 'use strict'; -// https://en.bitcoin.it/wiki/BIP_0072 +// https://github.com/bitcoin/bips/blob/master/bip-0021.mediawiki +// https://github.com/bitcoin/bips/blob/master/bip-0072.mediawiki (function(){ @@ -30,9 +31,91 @@ } + function bitpayAddrOnMainnet(address) { + var Address = bch.Address; + var BitpayFormat = Address.BitpayFormat; + + var mainnet = bch.Networks.mainnet; + + var result = null; + if (address[0] == 'C') { + try { + 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) {}; + + } + return result; + } + + 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; + } function isValidCashAddr(address, network) { + var a = address.replace('bitcoincash:', ''); + var result = {}; + if (a[0] == '1') { + result = Address.fromString(a, 'livenet', 'pubkeyhash'); + } else if (a[0] == '3') { + result = Address.fromString(a, 'livenet', 'scripthash'); + } else if (a[0] == 'C') { + result = Address.fromString(a, 'livenet', 'pubkeyhash', BitpayFormat); + } else if (a[0] == 'H') { + result = Address.fromString(a, 'livenet', 'scripthash', BitpayFormat); + } else if (a[0] == 'q') { + result = Address.fromString(address, 'livenet', 'pubkeyhash', CashAddrFormat); + } else if (a[0] == 'p') { + result = Address.fromString(address, 'livenet', 'scripthash', CashAddrFormat); + } else { + return null; + } var isValid = false; @@ -144,55 +227,61 @@ var address = questionMarkSplit[1]; var params = questionMarkSplit[2]; - var paramsSplit = params.split('&'); - var others; - var req; - paramsSplit.forEach(function onParam(param){ - var valueSplit = param.split('='); - if (valueSplit.length !== 2) { - return parsed; - } + if (params.length > 0) { + var paramsSplit = params.split('&'); + var others; + var req; + 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; + } - var key = valueSplit[0]; - var value = valueSplit[1]; - switch(key) { - case 'amount': - if (parseFloat(value)) { - parsed.amount = value; - } else { - return parsed; - } - break; + var key = valueSplit[0]; + var value = valueSplit[1]; + var decodedValue = decodeURIComponent(value); + switch(key) { + case 'amount': + 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; - break; + case 'label': + parsed.label = decodedValue; + break; - case 'message': - parsed.message = value; - break; + case 'message': + 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; - } else { - return parsed; - } - 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 (decodedValue.startsWith('https://')) { + parsed.url = decodedValue; + } else { + return parsed; + } + break; - default: - if (key.startsWith('req-')) { - req = req || {}; - req[key] = value; - } else { - others = others || {}; - others[key] = value; - } - } + default: + if (key.startsWith('req-')) { + req = req || {}; + req[key] = decodedValue; + } else { + others = others || {}; + others[key] = decodedValue; + } + } - }); + }; + } parsed.others = others; parsed.req = req; @@ -207,43 +296,43 @@ //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 bitpayAddrMainnet = bitpayAddrOnMainnet(address); + var cashAddrTestnet = cashAddrOnTestnet(addressLowerCase); + var cashAddrMainnet = cashAddrOnMainnet(addressLowerCase); - if (bitcore.Address.isValid(address, 'livenet')) { + if (parsed.testnet && cashAddrTestnet) { + parsed.address = addressLowerCase; + parsed.coin = 'bch'; + parsed.legacyAddress = cashAddrTestnet.toString(); + + } else if (cashAddrMainnet) { + parsed.address = addressLowerCase; + parsed.coin = 'bch'; + parsed.legacyAddress = cashAddrMainnet.toString(); + parsed.testnet = false; + + } else if (bitcore.Address.isValid(address, 'livenet') && parsed.coin !== 'bch') { parsed.address = address; parsed.legacyAddress = address; parsed.testnet = false; - } else if (bitcore.Address.isValid(address, 'testnet')) { + } else if (bitcore.Address.isValid(address, 'testnet') && parsed.coin !== 'bch') { parsed.address = address; parsed.legacyAddress = address; parsed.testnet = 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.address = address; 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.legacyAddress = bitpayAddrMainnet.toString(); parsed.testnet = false; - - } + } } - // TODO: Check for a private key here too + // TODO: Check for a private key here too, including WIF format, etc. // If has no address, must have Url. diff --git a/src/js/services/bitcoin-uri.service.spec.js b/src/js/services/bitcoin-uri.service.spec.js index 8ac2608a3..d72ce8b20 100644 --- a/src/js/services/bitcoin-uri.service.spec.js +++ b/src/js/services/bitcoin-uri.service.spec.js @@ -12,6 +12,17 @@ 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.address).toBeUndefined() + expect(parsed.coin).toBe('btc'); + expect(parsed.legacyAddress).toBeUndefined(); + expect(parsed.testnet).toBeUndefined(); + expect(parsed.url).toBe('https://bitpay.com/i/CwzbKP3k3JNgXJBfuoerDr'); + }); it('Bitcoin Cash BIP72', function() { @@ -25,38 +36,93 @@ fdescribe('bitcoinUriService', function() { expect(parsed.url).toBe('https://bitpay.com/i/SmHdie5dvBnG5kouZzEPzu'); }); - it('Bitcoin BIP72', function() { + it('Bitcoin Cash prefix with legacy address', function() { - var parsed = bitcoinUriService.parse('bitcoin:?r=https://bitpay.com/i/CwzbKP3k3JNgXJBfuoerDr'); + var parsed = bitcoinUriService.parse('bitcoincash:1G9FA9fFnHfTYxvmXeAbBD9FwzPAVMbd3j'); + + expect(parsed.isValid).toBe(false); + }); + + 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.address).toBeUndefined() + expect(parsed.address).toBe('qr8v2vqnzntykakht43rqmxq8cdjzjp795fc3vsjgc'); + expect(parsed.coin).toBe('bch'); + expect(parsed.legacyAddress).toBe('1KrJRNApaAKRvHL5kDtL69nwmAJ31apAnu'); + expect(parsed.others.mystery).toBe('Melton probang'); + expect(parsed.others.unknown).toBe('something'); + 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.testnet).toBe(true); + }); + + 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.testnet).toBe(true); + }); + + it('Bitcoin uri', function() { + + var parsed = bitcoinUriService.parse('bitcoin:15yCdKWVKRvfXMJpPYZBqMhiGKwjKzZdLN'); + + expect(parsed.isValid).toBe(true); + expect(parsed.address).toBe('15yCdKWVKRvfXMJpPYZBqMhiGKwjKzZdLN'); expect(parsed.coin).toBe('btc'); - expect(parsed.legacyAddress).toBeUndefined(); - expect(parsed.testnet).toBeUndefined(); - expect(parsed.url).toBe('https://bitpay.com/i/CwzbKP3k3JNgXJBfuoerDr'); + expect(parsed.legacyAddress).toBe('15yCdKWVKRvfXMJpPYZBqMhiGKwjKzZdLN'); + expect(parsed.testnet).toBe(false); }); - it('Bitcoin testnet address', function() { + it('Bitcoin uri with encoded label', function() { - var parsed = bitcoinUriService.parse('mtWcoToWhbtPoCby5fvs8xdBujT5GGenD4'); + var parsed = bitcoinUriService.parse('bitcoin:1MxudKDEBWZ1yjizUSf6htacenNtb3DWbT?label=Mr.%20Smith'); expect(parsed.isValid).toBe(true); - expect(parsed.address).toBe('mtWcoToWhbtPoCby5fvs8xdBujT5GGenD4'); - expect(parsed.coin).toBeUndefined(); - expect(parsed.legacyAddress).toBe('mtWcoToWhbtPoCby5fvs8xdBujT5GGenD4'); - expect(parsed.testnet).toBe(true); + expect(parsed.address).toBe('1MxudKDEBWZ1yjizUSf6htacenNtb3DWbT'); + expect(parsed.coin).toBe('btc'); + expect(parsed.label).toBe('Mr. Smith'); + expect(parsed.legacyAddress).toBe('1MxudKDEBWZ1yjizUSf6htacenNtb3DWbT'); + expect(parsed.testnet).toBe(false); }); - it('Bitcoin testnet address', function() { + it('Bitcoin uri with params', function() { - var parsed = bitcoinUriService.parse('mtWcoToWhbtPoCby5fvs8xdBujT5GGenD4'); + var parsed = bitcoinUriService.parse('bitcoin:12nCRhMDfxVnuF3uYMXv2fNxBohNmacfWu?amount=20.3&label=Luke-Jr&message=Donation%20for%20project%20xyz'); expect(parsed.isValid).toBe(true); - expect(parsed.address).toBe('mtWcoToWhbtPoCby5fvs8xdBujT5GGenD4'); - expect(parsed.coin).toBeUndefined(); - expect(parsed.legacyAddress).toBe('mtWcoToWhbtPoCby5fvs8xdBujT5GGenD4'); - expect(parsed.testnet).toBe(true); + expect(parsed.amount).toBe('20.3'); + expect(parsed.address).toBe('12nCRhMDfxVnuF3uYMXv2fNxBohNmacfWu'); + expect(parsed.coin).toBe('btc'); + expect(parsed.label).toBe('Luke-Jr'); + expect(parsed.legacyAddress).toBe('12nCRhMDfxVnuF3uYMXv2fNxBohNmacfWu'); + expect(parsed.message).toBe('Donation for project xyz'); + expect(parsed.testnet).toBe(false); }); it('legacy address', function() { @@ -75,7 +141,7 @@ fdescribe('bitcoinUriService', function() { var parsed = bitcoinUriService.parse('bchtest:qpcz6pmurq9ctg5848trzz9zmuuygj4q5qam7ph3gt'); expect(parsed.isValid).toBe(true); - expect(parsed.address).toBe('bchtest:qpcz6pmurq9ctg5848trzz9zmuuygj4q5qam7ph3gt'); + expect(parsed.address).toBe('qpcz6pmurq9ctg5848trzz9zmuuygj4q5qam7ph3gt'); expect(parsed.coin).toBe('bch'); expect(parsed.legacyAddress).toBe('mqk5vE278ytt6LUZqd97wi8c3FHsSYREX4'); expect(parsed.testnet).toBe(true); @@ -84,9 +150,10 @@ fdescribe('bitcoinUriService', function() { it('cashAddr with prefix', function() { var parsed = bitcoinUriService.parse('bitcoincash:qrq9p82a247lecv08ldk5p5h6ahtnjzpqcnh8yhq92'); + console.log('parsed:', JSON.stringify(parsed)); expect(parsed.isValid).toBe(true); - expect(parsed.address).toBe('bitcoincash:qrq9p82a247lecv08ldk5p5h6ahtnjzpqcnh8yhq92'); + expect(parsed.address).toBe('qrq9p82a247lecv08ldk5p5h6ahtnjzpqcnh8yhq92'); expect(parsed.coin).toBe('bch'); expect(parsed.legacyAddress).toBe('1JXsK3HSFqoMnwh4Mevf5bTgqPcgNWX7ic'); expect(parsed.testnet).toBe(false); @@ -97,12 +164,26 @@ fdescribe('bitcoinUriService', function() { var parsed = bitcoinUriService.parse('qqen2y3l28dpk0dzsag8w027ds96u7z4pc0uxtl0nq'); expect(parsed.isValid).toBe(true); - expect(parsed.address).toBe('bitcoincash:qqen2y3l28dpk0dzsag8w027ds96u7z4pc0uxtl0nq'); + expect(parsed.address).toBe('qqen2y3l28dpk0dzsag8w027ds96u7z4pc0uxtl0nq'); expect(parsed.coin).toBe('bch'); expect(parsed.legacyAddress).toBe('15fm3EwqgBYcxkndALBfforueps5yWKReJ'); expect(parsed.testnet).toBe(false); }); + + it('Bitpay without prefix', function() { + + var parsed = bitcoinUriService.parse('CJoRov8TirekvajiimQpb5Hk95evA7H2Yz'); + + expect(parsed.isValid).toBe(true); + expect(parsed.address).toBe('CJoRov8TirekvajiimQpb5Hk95evA7H2Yz'); + expect(parsed.coin).toBe('bch'); + expect(parsed.legacyAddress).toBe('13LYEsnPqogE2SqJ325u1ZfiWxSWEo6uyo'); + expect(parsed.testnet).toBe(false); + }); + + + // 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'); From e8005c9ea6c7b63741c1a62d7eca1fff0cd51ad3 Mon Sep 17 00:00:00 2001 From: Brendon Duncan Date: Tue, 28 Aug 2018 17:10:15 +1200 Subject: [PATCH 03/19] Legacy addresses with bitcoincash: prefix. --- src/js/services/bitcoin-uri.service.js | 4 ++-- src/js/services/bitcoin-uri.service.spec.js | 21 ++++++++++++++++++--- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/src/js/services/bitcoin-uri.service.js b/src/js/services/bitcoin-uri.service.js index f6f91ddce..d8498ec53 100644 --- a/src/js/services/bitcoin-uri.service.js +++ b/src/js/services/bitcoin-uri.service.js @@ -311,12 +311,12 @@ parsed.legacyAddress = cashAddrMainnet.toString(); parsed.testnet = false; - } else if (bitcore.Address.isValid(address, 'livenet') && parsed.coin !== 'bch') { + } else if (bitcore.Address.isValid(address, 'livenet')) { parsed.address = address; parsed.legacyAddress = address; parsed.testnet = false; - } else if (bitcore.Address.isValid(address, 'testnet') && parsed.coin !== 'bch') { + } else if (bitcore.Address.isValid(address, 'testnet')) { parsed.address = address; parsed.legacyAddress = address; parsed.testnet = true; diff --git a/src/js/services/bitcoin-uri.service.spec.js b/src/js/services/bitcoin-uri.service.spec.js index d72ce8b20..b956cccc6 100644 --- a/src/js/services/bitcoin-uri.service.spec.js +++ b/src/js/services/bitcoin-uri.service.spec.js @@ -17,7 +17,7 @@ fdescribe('bitcoinUriService', function() { var parsed = bitcoinUriService.parse('bitcoin:?r=https://bitpay.com/i/CwzbKP3k3JNgXJBfuoerDr'); expect(parsed.isValid).toBe(true); - expect(parsed.address).toBeUndefined() + expect(parsed.address).toBeUndefined(); expect(parsed.coin).toBe('btc'); expect(parsed.legacyAddress).toBeUndefined(); expect(parsed.testnet).toBeUndefined(); @@ -29,7 +29,7 @@ fdescribe('bitcoinUriService', function() { var parsed = bitcoinUriService.parse('bitcoincash:?r=https://bitpay.com/i/SmHdie5dvBnG5kouZzEPzu'); expect(parsed.isValid).toBe(true); - expect(parsed.address).toBeUndefined() + expect(parsed.address).toBeUndefined(); expect(parsed.coin).toBe('bch'); expect(parsed.legacyAddress).toBeUndefined(); expect(parsed.testnet).toBeUndefined(); @@ -40,7 +40,22 @@ fdescribe('bitcoinUriService', function() { var parsed = bitcoinUriService.parse('bitcoincash:1G9FA9fFnHfTYxvmXeAbBD9FwzPAVMbd3j'); - expect(parsed.isValid).toBe(false); + expect(parsed.isValid).toBe(true); + expect(parsed.address).toBe('1G9FA9fFnHfTYxvmXeAbBD9FwzPAVMbd3j'); + expect(parsed.coin).toBe('bch'); + expect(parsed.legacyAddress).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.address).toBe('mkDQrKfSFD441JxrD1iPBsJFExgkvrPGQn'); + expect(parsed.coin).toBe('bch'); + expect(parsed.legacyAddress).toBe('mkDQrKfSFD441JxrD1iPBsJFExgkvrPGQn'); + expect(parsed.testnet).toBe(true); }); it('Bitcoin Cash uri with extended params', function() { From c2cca3c08024af9f7075e772e09418bd81c7d059 Mon Sep 17 00:00:00 2001 From: Brendon Duncan Date: Tue, 28 Aug 2018 17:18:05 +1200 Subject: [PATCH 04/19] Bitcoin cash uppercase. --- src/js/services/bitcoin-uri.service.js | 45 +-------------------- src/js/services/bitcoin-uri.service.spec.js | 12 +++++- 2 files changed, 12 insertions(+), 45 deletions(-) diff --git a/src/js/services/bitcoin-uri.service.js b/src/js/services/bitcoin-uri.service.js index d8498ec53..dcb3fc5a8 100644 --- a/src/js/services/bitcoin-uri.service.js +++ b/src/js/services/bitcoin-uri.service.js @@ -96,49 +96,6 @@ } return result; } - - function isValidCashAddr(address, network) { - - var a = address.replace('bitcoincash:', ''); - var result = {}; - if (a[0] == '1') { - result = Address.fromString(a, 'livenet', 'pubkeyhash'); - } else if (a[0] == '3') { - result = Address.fromString(a, 'livenet', 'scripthash'); - } else if (a[0] == 'C') { - result = Address.fromString(a, 'livenet', 'pubkeyhash', BitpayFormat); - } else if (a[0] == 'H') { - result = Address.fromString(a, 'livenet', 'scripthash', BitpayFormat); - } else if (a[0] == 'q') { - result = Address.fromString(address, 'livenet', 'pubkeyhash', CashAddrFormat); - } else if (a[0] == 'p') { - result = Address.fromString(address, 'livenet', 'scripthash', CashAddrFormat); - } else { - return null; - } - - var isValid = false; - - var prefix = network === 'testnet' ? 'bchtest:' : 'bitcoincash:'; - - 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; - } - } - } - } catch (e) { - // Nop - Must not be a valid cashAddr. - $log.error('Error validating address.', e); - } - console.log(address,'isValidCashAddr:', isValid); - return isValid; - } /* @@ -170,7 +127,7 @@ // Need to do testnet, and copay too */ - // bitcoincash:?r=https://bitpay.com/i/GLRoZMZxaWBqLqpoXexzoD + function parse(uri) { var parsed = { isValid: false diff --git a/src/js/services/bitcoin-uri.service.spec.js b/src/js/services/bitcoin-uri.service.spec.js index b956cccc6..c2a2571c4 100644 --- a/src/js/services/bitcoin-uri.service.spec.js +++ b/src/js/services/bitcoin-uri.service.spec.js @@ -162,10 +162,20 @@ fdescribe('bitcoinUriService', function() { expect(parsed.testnet).toBe(true); }); + it('cashAddr uppercase', function() { + + var parsed = bitcoinUriService.parse('BITCOINCASH:QZZG9NMC5VX8GAP6XFATX3TWNSDN2YRMCSSULSMY44'); + + expect(parsed.isValid).toBe(true); + expect(parsed.address).toBe('qzzg9nmc5vx8gap6xfatx3twnsdn2yrmcssulsmy44'); + expect(parsed.coin).toBe('bch'); + expect(parsed.legacyAddress).toBe('1D5euC1yUbHiNpXreQrUUYt7LNevD3cviR'); + expect(parsed.testnet).toBe(false); + }); + it('cashAddr with prefix', function() { var parsed = bitcoinUriService.parse('bitcoincash:qrq9p82a247lecv08ldk5p5h6ahtnjzpqcnh8yhq92'); - console.log('parsed:', JSON.stringify(parsed)); expect(parsed.isValid).toBe(true); expect(parsed.address).toBe('qrq9p82a247lecv08ldk5p5h6ahtnjzpqcnh8yhq92'); From 61a29cf7ea3d6edb622bc6db3a3f2057d71e41fb Mon Sep 17 00:00:00 2001 From: Brendon Duncan Date: Tue, 28 Aug 2018 18:42:03 +1200 Subject: [PATCH 05/19] Copay invitation and wifPrivateKey. --- src/js/services/bitcoin-uri.service.js | 44 +++++++++++++++------ src/js/services/bitcoin-uri.service.spec.js | 39 +++++++++++++----- 2 files changed, 62 insertions(+), 21 deletions(-) diff --git a/src/js/services/bitcoin-uri.service.js b/src/js/services/bitcoin-uri.service.js index dcb3fc5a8..48464cb27 100644 --- a/src/js/services/bitcoin-uri.service.js +++ b/src/js/services/bitcoin-uri.service.js @@ -108,6 +108,7 @@ address: '', amount: '', coin: '', + copayInvitation: '', isValid: false, label: '', legacyAddress: '', @@ -120,7 +121,8 @@ "req-param1": "" }, testnet: false, - url: '' + url: '', + wifPrivateKey: '' } @@ -244,18 +246,22 @@ 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 cashAddrRe = /^((?:q|p)[a-z0-9]{41})|((?:Q|P)[A-Z0-9]{41})$/; + var copayRe = /^[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 privateKeyUncompressedRe = /^5[1-9A-HJ-NP-Za-km-z]{50}$/; + var privateKeyUncompressedTestnetRe = /^9[1-9A-HJ-NP-Za-km-z]{50}$/; + var privateKeyCompressedRe = /^[KL][1-9A-HJ-NP-Za-km-z]{51}$/; + var privateKeyCompressedTestnetRe = /^[c][1-9A-HJ-NP-Za-km-z]{51}$/; + var bitpayAddrMainnet = bitpayAddrOnMainnet(address); var cashAddrTestnet = cashAddrOnTestnet(addressLowerCase); var cashAddrMainnet = cashAddrOnMainnet(addressLowerCase); + var privateKey = ''; if (parsed.testnet && cashAddrTestnet) { parsed.address = addressLowerCase; @@ -282,18 +288,32 @@ parsed.address = address; parsed.coin = 'bch'; parsed.legacyAddress = bitpayAddrMainnet.toString(); - parsed.testnet = false; + parsed.testnet = false; + + } else if (copayRe.test(address) ) { + parsed.copayInvitation = address; + + } else if (privateKeyUncompressedRe.test(address) || privateKeyCompressedRe.test(address)) { + privateKey = address; + try { + new bitcore.PrivateKey(privateKey, 'livenet'); + parsed.wifPrivateKey = privateKey; + parsed.testnet = false; + } catch (e) {} + + } else if (privateKeyUncompressedTestnetRe.test(address) || privateKeyCompressedTestnetRe.test(address)) { + privateKey = address; + try { + new bitcore.PrivateKey(privateKey, 'testnet'); + parsed.wifPrivateKey = privateKey; + parsed.testnet = true; + } catch (e) {} } } - - - // TODO: Check for a private key here too, including WIF format, etc. - - // If has no address, must have Url. - parsed.isValid = !!(parsed.address || parsed.url); + parsed.isValid = !!(parsed.address || parsed.url || parsed.copayInvitation || parsed.wifPrivateKey); return parsed; } diff --git a/src/js/services/bitcoin-uri.service.spec.js b/src/js/services/bitcoin-uri.service.spec.js index c2a2571c4..c37d708d6 100644 --- a/src/js/services/bitcoin-uri.service.spec.js +++ b/src/js/services/bitcoin-uri.service.spec.js @@ -140,6 +140,17 @@ fdescribe('bitcoinUriService', function() { expect(parsed.testnet).toBe(false); }); + it('Bitpay without prefix', function() { + + var parsed = bitcoinUriService.parse('CJoRov8TirekvajiimQpb5Hk95evA7H2Yz'); + + expect(parsed.isValid).toBe(true); + expect(parsed.address).toBe('CJoRov8TirekvajiimQpb5Hk95evA7H2Yz'); + expect(parsed.coin).toBe('bch'); + expect(parsed.legacyAddress).toBe('13LYEsnPqogE2SqJ325u1ZfiWxSWEo6uyo'); + expect(parsed.testnet).toBe(false); + }); + it('legacy address', function() { var parsed = bitcoinUriService.parse('1JXeGEu7bNEAYu6URT6dU6g1Ys6ffSAWYW'); @@ -195,20 +206,14 @@ fdescribe('bitcoinUriService', function() { expect(parsed.testnet).toBe(false); }); + it('copay invitation', function() { - it('Bitpay without prefix', function() { - - var parsed = bitcoinUriService.parse('CJoRov8TirekvajiimQpb5Hk95evA7H2Yz'); + var parsed = bitcoinUriService.parse('PD5B7rEEj72st9d5nFszyuKxJP6FAGS7idVC2SMqiMxUcWVd8JifZDJw1UgjUctxefUFE3Sz6qLbch'); expect(parsed.isValid).toBe(true); - expect(parsed.address).toBe('CJoRov8TirekvajiimQpb5Hk95evA7H2Yz'); - expect(parsed.coin).toBe('bch'); - expect(parsed.legacyAddress).toBe('13LYEsnPqogE2SqJ325u1ZfiWxSWEo6uyo'); - expect(parsed.testnet).toBe(false); + 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'); @@ -234,4 +239,20 @@ fdescribe('bitcoinUriService', function() { var parsed = bitcoinUriService.parse('bchreg:555555555555555555555555555555555555555555555udxmlmrz'); expect(parsed.isValid).toBe(false); }); + + it('private key compressed mainnet', function() { + + var parsed = bitcoinUriService.parse('5HueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTJ'); + + expect(parsed.isValid).toBe(true); + expect(parsed.wifPrivateKey).toBe('5HueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTJ'); + }); + + it('private key compressed mainnet with wrong checksum', function() { + + var parsed = bitcoinUriService.parse('5HueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTu'); + + expect(parsed.isValid).toBe(false); + }); + }); \ No newline at end of file From a9f2794d11628abc4baaeb8edb93ab306cbeca05 Mon Sep 17 00:00:00 2001 From: Brendon Duncan Date: Tue, 28 Aug 2018 20:56:10 +1200 Subject: [PATCH 06/19] Testing uncompressed private keys on mainnet. --- src/js/services/bitcoin-uri.service.spec.js | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/js/services/bitcoin-uri.service.spec.js b/src/js/services/bitcoin-uri.service.spec.js index c37d708d6..e5c73bbc8 100644 --- a/src/js/services/bitcoin-uri.service.spec.js +++ b/src/js/services/bitcoin-uri.service.spec.js @@ -255,4 +255,21 @@ fdescribe('bitcoinUriService', function() { expect(parsed.isValid).toBe(false); }); + it('private key uncompressed mainnet', function() { + + var parsed = bitcoinUriService.parse('L18V3rAhCKEioPnJ4BHLCCsaYa8eSNFrMjNQ2EdwgeAdmBSnTMwx'); + + expect(parsed.isValid).toBe(true); + expect(parsed.wifPrivateKey).toBe('L18V3rAhCKEioPnJ4BHLCCsaYa8eSNFrMjNQ2EdwgeAdmBSnTMwx'); + }); + + it('private key uncompressed mainnet with wrong checksum', function() { + + var parsed = bitcoinUriService.parse('L18V3rAhCKEioPnJ4BHLCCsaYa8eSNFrMjNQ2EdwgeAdmBSnTTwx'); + + expect(parsed.isValid).toBe(false); + }); + + // TODO: Tests for private key variations. (testnet) + }); \ No newline at end of file From b95798d271f067c61b4137ec715f3d88a882bb43 Mon Sep 17 00:00:00 2001 From: Brendon Duncan Date: Wed, 29 Aug 2018 08:49:18 +1200 Subject: [PATCH 07/19] Tests for private keys on testnet. --- src/js/services/bitcoin-uri.service.spec.js | 42 ++++++++++++++++++--- 1 file changed, 37 insertions(+), 5 deletions(-) diff --git a/src/js/services/bitcoin-uri.service.spec.js b/src/js/services/bitcoin-uri.service.spec.js index e5c73bbc8..463cd802e 100644 --- a/src/js/services/bitcoin-uri.service.spec.js +++ b/src/js/services/bitcoin-uri.service.spec.js @@ -240,36 +240,68 @@ fdescribe('bitcoinUriService', function() { expect(parsed.isValid).toBe(false); }); - it('private key compressed mainnet', function() { + it('private key for compressed pubkey mainnet', function() { var parsed = bitcoinUriService.parse('5HueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTJ'); expect(parsed.isValid).toBe(true); expect(parsed.wifPrivateKey).toBe('5HueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTJ'); + expect(parsed.testnet).toBe(false); }); - it('private key compressed mainnet with wrong checksum', function() { + it('private key for compressed pubkey mainnet with wrong checksum', function() { var parsed = bitcoinUriService.parse('5HueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTu'); expect(parsed.isValid).toBe(false); }); - it('private key uncompressed mainnet', function() { + it('private key for compressed pubkey testnet', function() { + + var parsed = bitcoinUriService.parse('cNJFgo1driFnPcBdBX8BrJrpxchBWXwXCvNH5SoSkdcF6JXXwHMm'); + + expect(parsed.isValid).toBe(true); + expect(parsed.wifPrivateKey).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.wifPrivateKey).toBe('L18V3rAhCKEioPnJ4BHLCCsaYa8eSNFrMjNQ2EdwgeAdmBSnTMwx'); + expect(parsed.testnet).toBe(false); }); - it('private key uncompressed mainnet with wrong checksum', function() { + it('private key for uncompressed pubkey mainnet with wrong checksum', function() { var parsed = bitcoinUriService.parse('L18V3rAhCKEioPnJ4BHLCCsaYa8eSNFrMjNQ2EdwgeAdmBSnTTwx'); expect(parsed.isValid).toBe(false); }); - // TODO: Tests for private key variations. (testnet) + it('private key for uncompressed pubkey testnet', function() { + + var parsed = bitcoinUriService.parse('92Pg46rUhgTT7romnV7iGW6W1gbGdeezqdbJCzShkCsYNzyyNcc'); + + expect(parsed.isValid).toBe(true); + expect(parsed.wifPrivateKey).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); + }); }); \ No newline at end of file From 6c85cffb2067fe2b51138a4180f4ce1f912f5b93 Mon Sep 17 00:00:00 2001 From: Brendon Duncan Date: Wed, 29 Aug 2018 09:11:14 +1200 Subject: [PATCH 08/19] Now handles encrypted private key. --- src/js/services/bitcoin-uri.service.js | 37 ++++++++++++++------- src/js/services/bitcoin-uri.service.spec.js | 8 +++++ 2 files changed, 33 insertions(+), 12 deletions(-) diff --git a/src/js/services/bitcoin-uri.service.js b/src/js/services/bitcoin-uri.service.js index 48464cb27..28e1421e3 100644 --- a/src/js/services/bitcoin-uri.service.js +++ b/src/js/services/bitcoin-uri.service.js @@ -109,6 +109,7 @@ amount: '', coin: '', copayInvitation: '', + encryptedPrivateKey: '', isValid: false, label: '', legacyAddress: '', @@ -250,13 +251,14 @@ var addressLowerCase = address.toLowerCase(); // 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 copayRe = /^[0-9A-HJ-NP-Za-km-z]{70,80}$/; + 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 privateKeyUncompressedRe = /^5[1-9A-HJ-NP-Za-km-z]{50}$/; - var privateKeyUncompressedTestnetRe = /^9[1-9A-HJ-NP-Za-km-z]{50}$/; - var privateKeyCompressedRe = /^[KL][1-9A-HJ-NP-Za-km-z]{51}$/; - var privateKeyCompressedTestnetRe = /^[c][1-9A-HJ-NP-Za-km-z]{51}$/; + 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}$/; var bitpayAddrMainnet = bitpayAddrOnMainnet(address); var cashAddrTestnet = cashAddrOnTestnet(addressLowerCase); @@ -267,54 +269,65 @@ parsed.address = addressLowerCase; parsed.coin = 'bch'; parsed.legacyAddress = cashAddrTestnet.toString(); + parsed.isValid = true; } else if (cashAddrMainnet) { parsed.address = addressLowerCase; parsed.coin = 'bch'; parsed.legacyAddress = cashAddrMainnet.toString(); - parsed.testnet = false; + parsed.testnet = false; + parsed.isValid = true; } else if (bitcore.Address.isValid(address, 'livenet')) { parsed.address = address; parsed.legacyAddress = address; parsed.testnet = false; + parsed.isValid = true; } else if (bitcore.Address.isValid(address, 'testnet')) { parsed.address = address; parsed.legacyAddress = address; parsed.testnet = true; + parsed.isValid = true; } else if (bitpayAddrMainnet) { parsed.address = address; parsed.coin = 'bch'; parsed.legacyAddress = bitpayAddrMainnet.toString(); parsed.testnet = false; + parsed.isValid = true; - } else if (copayRe.test(address) ) { + } else if (copayInvitationRe.test(address) ) { parsed.copayInvitation = address; + parsed.isValid = true; - } else if (privateKeyUncompressedRe.test(address) || privateKeyCompressedRe.test(address)) { + } else if (privateKeyForUncompressedPublicKeyRe.test(address) || privateKeyForCompressedPublicKeyRe.test(address)) { privateKey = address; try { new bitcore.PrivateKey(privateKey, 'livenet'); parsed.wifPrivateKey = privateKey; parsed.testnet = false; + parsed.isValid = true; } catch (e) {} - } else if (privateKeyUncompressedTestnetRe.test(address) || privateKeyCompressedTestnetRe.test(address)) { + } else if (privateKeyForUncompressedPublicKeyTestnetRe.test(address) || privateKeyForCompressedPublicKeyTestnetRe.test(address)) { privateKey = address; try { new bitcore.PrivateKey(privateKey, 'testnet'); parsed.wifPrivateKey = privateKey; parsed.testnet = true; + parsed.isValid = true; } catch (e) {} + + } else if (privateKeyEncryptedRe.test(address)) { + parsed.encryptedPrivateKey = address; + parsed.isValid = true; } + } else { + parsed.isValid = !!parsed.url; // BIP72 } - // If has no address, must have Url. - parsed.isValid = !!(parsed.address || parsed.url || parsed.copayInvitation || parsed.wifPrivateKey); - return parsed; } diff --git a/src/js/services/bitcoin-uri.service.spec.js b/src/js/services/bitcoin-uri.service.spec.js index 463cd802e..bd2c6f2fb 100644 --- a/src/js/services/bitcoin-uri.service.spec.js +++ b/src/js/services/bitcoin-uri.service.spec.js @@ -240,6 +240,14 @@ fdescribe('bitcoinUriService', function() { expect(parsed.isValid).toBe(false); }); + it('private key encrypted with BIP38', function() { + + var parsed = bitcoinUriService.parse('6PRN5nEDmX842gsBzJryPu8Tw5kcsaQq1GPLcjVQPcEStvbFAtz11JX9pX'); + + expect(parsed.isValid).toBe(true); + expect(parsed.encryptedPrivateKey).toBe('6PRN5nEDmX842gsBzJryPu8Tw5kcsaQq1GPLcjVQPcEStvbFAtz11JX9pX'); + }); + it('private key for compressed pubkey mainnet', function() { var parsed = bitcoinUriService.parse('5HueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTJ'); From 9bed4239da665e447ac2f0e2c04a1aad8cb12b22 Mon Sep 17 00:00:00 2001 From: Brendon Duncan Date: Wed, 29 Aug 2018 09:54:04 +1200 Subject: [PATCH 09/19] Grouped the output. --- src/js/services/bitcoin-uri.service.js | 56 +++++++++------- src/js/services/bitcoin-uri.service.spec.js | 72 ++++++++++----------- 2 files changed, 69 insertions(+), 59 deletions(-) diff --git a/src/js/services/bitcoin-uri.service.js b/src/js/services/bitcoin-uri.service.js index 28e1421e3..fd5c5d70f 100644 --- a/src/js/services/bitcoin-uri.service.js +++ b/src/js/services/bitcoin-uri.service.js @@ -105,26 +105,29 @@ returns: { - address: '', amount: '', coin: '', copayInvitation: '', - encryptedPrivateKey: '', isValid: false, label: '', - legacyAddress: '', message: '', other: { somethingIDontUnderstand: 'Its value' }, + privateKey: { + encrypted: '', + wif: '' + }'', + publicAddress: { + asReceived: '', + legacy: '', + }, req: { - "req-param0": "", - "req-param1": "" + "req-param0": '', + "req-param1": '' }, testnet: false, - url: '', - wifPrivateKey: '' - + url: '' // For BIP70 } // Need to do testnet, and copay too @@ -249,8 +252,6 @@ if (address) { var addressLowerCase = address.toLowerCase(); - // 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}$/; @@ -268,32 +269,43 @@ if (parsed.testnet && cashAddrTestnet) { parsed.address = addressLowerCase; parsed.coin = 'bch'; - parsed.legacyAddress = cashAddrTestnet.toString(); + parsed.publicAddress = { + asReceived: addressLowerCase, + legacy: cashAddrTestnet.toString() + }; parsed.isValid = true; } else if (cashAddrMainnet) { - parsed.address = addressLowerCase; parsed.coin = 'bch'; - parsed.legacyAddress = cashAddrMainnet.toString(); + parsed.publicAddress = { + asReceived: addressLowerCase, + legacy: cashAddrMainnet.toString() + }; parsed.testnet = false; parsed.isValid = true; } else if (bitcore.Address.isValid(address, 'livenet')) { - parsed.address = address; - parsed.legacyAddress = address; + parsed.publicAddress = { + asReceived: address, + legacy: address + }; parsed.testnet = false; parsed.isValid = true; } else if (bitcore.Address.isValid(address, 'testnet')) { - parsed.address = address; - parsed.legacyAddress = address; + parsed.publicAddress = { + asReceived: address, + legacy: address + }; parsed.testnet = true; parsed.isValid = true; } else if (bitpayAddrMainnet) { - parsed.address = address; parsed.coin = 'bch'; - parsed.legacyAddress = bitpayAddrMainnet.toString(); + parsed.publicAddress = { + asReceived: address, + legacy: bitpayAddrMainnet.toString() + }; parsed.testnet = false; parsed.isValid = true; @@ -305,7 +317,7 @@ privateKey = address; try { new bitcore.PrivateKey(privateKey, 'livenet'); - parsed.wifPrivateKey = privateKey; + parsed.privateKey = { wif: privateKey }; parsed.testnet = false; parsed.isValid = true; } catch (e) {} @@ -314,13 +326,13 @@ privateKey = address; try { new bitcore.PrivateKey(privateKey, 'testnet'); - parsed.wifPrivateKey = privateKey; + parsed.privateKey = { wif: privateKey }; parsed.testnet = true; parsed.isValid = true; } catch (e) {} } else if (privateKeyEncryptedRe.test(address)) { - parsed.encryptedPrivateKey = address; + parsed.privateKey = { encrypted: address }; parsed.isValid = true; } diff --git a/src/js/services/bitcoin-uri.service.spec.js b/src/js/services/bitcoin-uri.service.spec.js index bd2c6f2fb..da2d25876 100644 --- a/src/js/services/bitcoin-uri.service.spec.js +++ b/src/js/services/bitcoin-uri.service.spec.js @@ -17,10 +17,9 @@ fdescribe('bitcoinUriService', function() { var parsed = bitcoinUriService.parse('bitcoin:?r=https://bitpay.com/i/CwzbKP3k3JNgXJBfuoerDr'); expect(parsed.isValid).toBe(true); - expect(parsed.address).toBeUndefined(); expect(parsed.coin).toBe('btc'); - expect(parsed.legacyAddress).toBeUndefined(); expect(parsed.testnet).toBeUndefined(); + expect(parsed.publicAddress).toBeUndefined(); expect(parsed.url).toBe('https://bitpay.com/i/CwzbKP3k3JNgXJBfuoerDr'); }); @@ -29,9 +28,8 @@ fdescribe('bitcoinUriService', function() { var parsed = bitcoinUriService.parse('bitcoincash:?r=https://bitpay.com/i/SmHdie5dvBnG5kouZzEPzu'); expect(parsed.isValid).toBe(true); - expect(parsed.address).toBeUndefined(); expect(parsed.coin).toBe('bch'); - expect(parsed.legacyAddress).toBeUndefined(); + expect(parsed.publicAddress).toBeUndefined(); expect(parsed.testnet).toBeUndefined(); expect(parsed.url).toBe('https://bitpay.com/i/SmHdie5dvBnG5kouZzEPzu'); }); @@ -41,9 +39,9 @@ fdescribe('bitcoinUriService', function() { var parsed = bitcoinUriService.parse('bitcoincash:1G9FA9fFnHfTYxvmXeAbBD9FwzPAVMbd3j'); expect(parsed.isValid).toBe(true); - expect(parsed.address).toBe('1G9FA9fFnHfTYxvmXeAbBD9FwzPAVMbd3j'); expect(parsed.coin).toBe('bch'); - expect(parsed.legacyAddress).toBe('1G9FA9fFnHfTYxvmXeAbBD9FwzPAVMbd3j'); + expect(parsed.publicAddress.asReceived).toBe('1G9FA9fFnHfTYxvmXeAbBD9FwzPAVMbd3j'); + expect(parsed.publicAddress.legacy).toBe('1G9FA9fFnHfTYxvmXeAbBD9FwzPAVMbd3j'); expect(parsed.testnet).toBe(false); }); @@ -52,9 +50,9 @@ fdescribe('bitcoinUriService', function() { var parsed = bitcoinUriService.parse('bitcoincash:mkDQrKfSFD441JxrD1iPBsJFExgkvrPGQn'); expect(parsed.isValid).toBe(true); - expect(parsed.address).toBe('mkDQrKfSFD441JxrD1iPBsJFExgkvrPGQn'); expect(parsed.coin).toBe('bch'); - expect(parsed.legacyAddress).toBe('mkDQrKfSFD441JxrD1iPBsJFExgkvrPGQn'); + expect(parsed.publicAddress.asReceived).toBe('mkDQrKfSFD441JxrD1iPBsJFExgkvrPGQn'); + expect(parsed.publicAddress.legacy).toBe('mkDQrKfSFD441JxrD1iPBsJFExgkvrPGQn'); expect(parsed.testnet).toBe(true); }); @@ -63,11 +61,11 @@ fdescribe('bitcoinUriService', 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.address).toBe('qr8v2vqnzntykakht43rqmxq8cdjzjp795fc3vsjgc'); expect(parsed.coin).toBe('bch'); - expect(parsed.legacyAddress).toBe('1KrJRNApaAKRvHL5kDtL69nwmAJ31apAnu'); expect(parsed.others.mystery).toBe('Melton probang'); expect(parsed.others.unknown).toBe('something'); + expect(parsed.publicAddress.asReceived).toBe('qr8v2vqnzntykakht43rqmxq8cdjzjp795fc3vsjgc'); + expect(parsed.publicAddress.legacy).toBe('1KrJRNApaAKRvHL5kDtL69nwmAJ31apAnu'); expect(parsed.req['req-beta']).toBe('Ni san'); expect(parsed.req['req-one']).toBe('ichi'); expect(parsed.testnet).toBe(false); @@ -86,9 +84,9 @@ fdescribe('bitcoinUriService', 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.asReceived).toBe('mtWcoToWhbtPoCby5fvs8xdBujT5GGenD4'); + expect(parsed.publicAddress.legacy).toBe('mtWcoToWhbtPoCby5fvs8xdBujT5GGenD4'); expect(parsed.testnet).toBe(true); }); @@ -97,9 +95,9 @@ fdescribe('bitcoinUriService', 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.asReceived).toBe('mtWcoToWhbtPoCby5fvs8xdBujT5GGenD4'); + expect(parsed.publicAddress.legacy).toBe('mtWcoToWhbtPoCby5fvs8xdBujT5GGenD4'); expect(parsed.testnet).toBe(true); }); @@ -108,9 +106,9 @@ fdescribe('bitcoinUriService', function() { var parsed = bitcoinUriService.parse('bitcoin:15yCdKWVKRvfXMJpPYZBqMhiGKwjKzZdLN'); expect(parsed.isValid).toBe(true); - expect(parsed.address).toBe('15yCdKWVKRvfXMJpPYZBqMhiGKwjKzZdLN'); expect(parsed.coin).toBe('btc'); - expect(parsed.legacyAddress).toBe('15yCdKWVKRvfXMJpPYZBqMhiGKwjKzZdLN'); + expect(parsed.publicAddress.asReceived).toBe('15yCdKWVKRvfXMJpPYZBqMhiGKwjKzZdLN'); + expect(parsed.publicAddress.legacy).toBe('15yCdKWVKRvfXMJpPYZBqMhiGKwjKzZdLN'); expect(parsed.testnet).toBe(false); }); @@ -119,10 +117,10 @@ fdescribe('bitcoinUriService', function() { var parsed = bitcoinUriService.parse('bitcoin:1MxudKDEBWZ1yjizUSf6htacenNtb3DWbT?label=Mr.%20Smith'); expect(parsed.isValid).toBe(true); - expect(parsed.address).toBe('1MxudKDEBWZ1yjizUSf6htacenNtb3DWbT'); expect(parsed.coin).toBe('btc'); expect(parsed.label).toBe('Mr. Smith'); - expect(parsed.legacyAddress).toBe('1MxudKDEBWZ1yjizUSf6htacenNtb3DWbT'); + expect(parsed.publicAddress.asReceived).toBe('1MxudKDEBWZ1yjizUSf6htacenNtb3DWbT'); + expect(parsed.publicAddress.legacy).toBe('1MxudKDEBWZ1yjizUSf6htacenNtb3DWbT'); expect(parsed.testnet).toBe(false); }); @@ -132,10 +130,10 @@ fdescribe('bitcoinUriService', function() { expect(parsed.isValid).toBe(true); expect(parsed.amount).toBe('20.3'); - expect(parsed.address).toBe('12nCRhMDfxVnuF3uYMXv2fNxBohNmacfWu'); expect(parsed.coin).toBe('btc'); expect(parsed.label).toBe('Luke-Jr'); - expect(parsed.legacyAddress).toBe('12nCRhMDfxVnuF3uYMXv2fNxBohNmacfWu'); + expect(parsed.publicAddress.asReceived).toBe('12nCRhMDfxVnuF3uYMXv2fNxBohNmacfWu'); + expect(parsed.publicAddress.legacy).toBe('12nCRhMDfxVnuF3uYMXv2fNxBohNmacfWu'); expect(parsed.message).toBe('Donation for project xyz'); expect(parsed.testnet).toBe(false); }); @@ -145,9 +143,9 @@ fdescribe('bitcoinUriService', function() { var parsed = bitcoinUriService.parse('CJoRov8TirekvajiimQpb5Hk95evA7H2Yz'); expect(parsed.isValid).toBe(true); - expect(parsed.address).toBe('CJoRov8TirekvajiimQpb5Hk95evA7H2Yz'); expect(parsed.coin).toBe('bch'); - expect(parsed.legacyAddress).toBe('13LYEsnPqogE2SqJ325u1ZfiWxSWEo6uyo'); + expect(parsed.publicAddress.asReceived).toBe('CJoRov8TirekvajiimQpb5Hk95evA7H2Yz'); + expect(parsed.publicAddress.legacy).toBe('13LYEsnPqogE2SqJ325u1ZfiWxSWEo6uyo'); expect(parsed.testnet).toBe(false); }); @@ -156,9 +154,9 @@ fdescribe('bitcoinUriService', 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.asReceived).toBe('1JXeGEu7bNEAYu6URT6dU6g1Ys6ffSAWYW'); + expect(parsed.publicAddress.legacy).toBe('1JXeGEu7bNEAYu6URT6dU6g1Ys6ffSAWYW'); expect(parsed.testnet).toBe(false); }); @@ -167,9 +165,9 @@ fdescribe('bitcoinUriService', function() { var parsed = bitcoinUriService.parse('bchtest:qpcz6pmurq9ctg5848trzz9zmuuygj4q5qam7ph3gt'); expect(parsed.isValid).toBe(true); - expect(parsed.address).toBe('qpcz6pmurq9ctg5848trzz9zmuuygj4q5qam7ph3gt'); expect(parsed.coin).toBe('bch'); - expect(parsed.legacyAddress).toBe('mqk5vE278ytt6LUZqd97wi8c3FHsSYREX4'); + expect(parsed.publicAddress.asReceived).toBe('qpcz6pmurq9ctg5848trzz9zmuuygj4q5qam7ph3gt'); + expect(parsed.publicAddress.legacy).toBe('mqk5vE278ytt6LUZqd97wi8c3FHsSYREX4'); expect(parsed.testnet).toBe(true); }); @@ -178,9 +176,9 @@ fdescribe('bitcoinUriService', function() { var parsed = bitcoinUriService.parse('BITCOINCASH:QZZG9NMC5VX8GAP6XFATX3TWNSDN2YRMCSSULSMY44'); expect(parsed.isValid).toBe(true); - expect(parsed.address).toBe('qzzg9nmc5vx8gap6xfatx3twnsdn2yrmcssulsmy44'); expect(parsed.coin).toBe('bch'); - expect(parsed.legacyAddress).toBe('1D5euC1yUbHiNpXreQrUUYt7LNevD3cviR'); + expect(parsed.publicAddress.asReceived).toBe('qzzg9nmc5vx8gap6xfatx3twnsdn2yrmcssulsmy44'); + expect(parsed.publicAddress.legacy).toBe('1D5euC1yUbHiNpXreQrUUYt7LNevD3cviR'); expect(parsed.testnet).toBe(false); }); @@ -189,9 +187,9 @@ fdescribe('bitcoinUriService', function() { var parsed = bitcoinUriService.parse('bitcoincash:qrq9p82a247lecv08ldk5p5h6ahtnjzpqcnh8yhq92'); expect(parsed.isValid).toBe(true); - expect(parsed.address).toBe('qrq9p82a247lecv08ldk5p5h6ahtnjzpqcnh8yhq92'); expect(parsed.coin).toBe('bch'); - expect(parsed.legacyAddress).toBe('1JXsK3HSFqoMnwh4Mevf5bTgqPcgNWX7ic'); + expect(parsed.publicAddress.asReceived).toBe('qrq9p82a247lecv08ldk5p5h6ahtnjzpqcnh8yhq92'); + expect(parsed.publicAddress.legacy).toBe('1JXsK3HSFqoMnwh4Mevf5bTgqPcgNWX7ic'); expect(parsed.testnet).toBe(false); }); @@ -200,9 +198,9 @@ fdescribe('bitcoinUriService', function() { var parsed = bitcoinUriService.parse('qqen2y3l28dpk0dzsag8w027ds96u7z4pc0uxtl0nq'); expect(parsed.isValid).toBe(true); - expect(parsed.address).toBe('qqen2y3l28dpk0dzsag8w027ds96u7z4pc0uxtl0nq'); expect(parsed.coin).toBe('bch'); - expect(parsed.legacyAddress).toBe('15fm3EwqgBYcxkndALBfforueps5yWKReJ'); + expect(parsed.publicAddress.asReceived).toBe('qqen2y3l28dpk0dzsag8w027ds96u7z4pc0uxtl0nq'); + expect(parsed.publicAddress.legacy).toBe('15fm3EwqgBYcxkndALBfforueps5yWKReJ'); expect(parsed.testnet).toBe(false); }); @@ -245,7 +243,7 @@ fdescribe('bitcoinUriService', function() { var parsed = bitcoinUriService.parse('6PRN5nEDmX842gsBzJryPu8Tw5kcsaQq1GPLcjVQPcEStvbFAtz11JX9pX'); expect(parsed.isValid).toBe(true); - expect(parsed.encryptedPrivateKey).toBe('6PRN5nEDmX842gsBzJryPu8Tw5kcsaQq1GPLcjVQPcEStvbFAtz11JX9pX'); + expect(parsed.privateKey.encrypted).toBe('6PRN5nEDmX842gsBzJryPu8Tw5kcsaQq1GPLcjVQPcEStvbFAtz11JX9pX'); }); it('private key for compressed pubkey mainnet', function() { @@ -253,7 +251,7 @@ fdescribe('bitcoinUriService', function() { var parsed = bitcoinUriService.parse('5HueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTJ'); expect(parsed.isValid).toBe(true); - expect(parsed.wifPrivateKey).toBe('5HueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTJ'); + expect(parsed.privateKey.wif).toBe('5HueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTJ'); expect(parsed.testnet).toBe(false); }); @@ -269,7 +267,7 @@ fdescribe('bitcoinUriService', function() { var parsed = bitcoinUriService.parse('cNJFgo1driFnPcBdBX8BrJrpxchBWXwXCvNH5SoSkdcF6JXXwHMm'); expect(parsed.isValid).toBe(true); - expect(parsed.wifPrivateKey).toBe('cNJFgo1driFnPcBdBX8BrJrpxchBWXwXCvNH5SoSkdcF6JXXwHMm'); + expect(parsed.privateKey.wif).toBe('cNJFgo1driFnPcBdBX8BrJrpxchBWXwXCvNH5SoSkdcF6JXXwHMm'); expect(parsed.testnet).toBe(true); }); @@ -285,7 +283,7 @@ fdescribe('bitcoinUriService', function() { var parsed = bitcoinUriService.parse('L18V3rAhCKEioPnJ4BHLCCsaYa8eSNFrMjNQ2EdwgeAdmBSnTMwx'); expect(parsed.isValid).toBe(true); - expect(parsed.wifPrivateKey).toBe('L18V3rAhCKEioPnJ4BHLCCsaYa8eSNFrMjNQ2EdwgeAdmBSnTMwx'); + expect(parsed.privateKey.wif).toBe('L18V3rAhCKEioPnJ4BHLCCsaYa8eSNFrMjNQ2EdwgeAdmBSnTMwx'); expect(parsed.testnet).toBe(false); }); @@ -301,7 +299,7 @@ fdescribe('bitcoinUriService', function() { var parsed = bitcoinUriService.parse('92Pg46rUhgTT7romnV7iGW6W1gbGdeezqdbJCzShkCsYNzyyNcc'); expect(parsed.isValid).toBe(true); - expect(parsed.wifPrivateKey).toBe('92Pg46rUhgTT7romnV7iGW6W1gbGdeezqdbJCzShkCsYNzyyNcc'); + expect(parsed.privateKey.wif).toBe('92Pg46rUhgTT7romnV7iGW6W1gbGdeezqdbJCzShkCsYNzyyNcc'); expect(parsed.testnet).toBe(true); }); From 3b74ab7d8cded0d8b0782f00c7742405dc286ac2 Mon Sep 17 00:00:00 2001 From: Brendon Duncan Date: Wed, 29 Aug 2018 09:59:47 +1200 Subject: [PATCH 10/19] Tolerating extra dash and slashes. --- src/js/services/bitcoin-uri.service.spec.js | 33 +++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/js/services/bitcoin-uri.service.spec.js b/src/js/services/bitcoin-uri.service.spec.js index da2d25876..c41424e75 100644 --- a/src/js/services/bitcoin-uri.service.spec.js +++ b/src/js/services/bitcoin-uri.service.spec.js @@ -138,6 +138,17 @@ fdescribe('bitcoinUriService', function() { 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.asReceived).toBe('18PCPhgZJjLxe9g3Q1BXLpL5aVut1fW3aX'); + expect(parsed.publicAddress.legacy).toBe('18PCPhgZJjLxe9g3Q1BXLpL5aVut1fW3aX'); + expect(parsed.testnet).toBe(false); + }); + it('Bitpay without prefix', function() { var parsed = bitcoinUriService.parse('CJoRov8TirekvajiimQpb5Hk95evA7H2Yz'); @@ -182,6 +193,17 @@ fdescribe('bitcoinUriService', function() { 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.asReceived).toBe('qpshfu3dk5s3e7zdcgdcun6xgxtra6uyxs7g580js0'); + expect(parsed.publicAddress.legacy).toBe('19tJeAD7JwkarE6hgviqHKqUYVgtPAfMbb'); + expect(parsed.testnet).toBe(false); + }); + it('cashAddr with prefix', function() { var parsed = bitcoinUriService.parse('bitcoincash:qrq9p82a247lecv08ldk5p5h6ahtnjzpqcnh8yhq92'); @@ -193,6 +215,17 @@ fdescribe('bitcoinUriService', function() { 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.asReceived).toBe('qpj966w8utue75lqqq3rlgh20zkz3rmydqpq8syv9c'); + expect(parsed.publicAddress.legacy).toBe('1A9gUeVVKcJtbbHfAPjUHmLSWJrD5YEc7k'); + expect(parsed.testnet).toBe(false); + }); + it('cashAddr without prefix', function() { var parsed = bitcoinUriService.parse('qqen2y3l28dpk0dzsag8w027ds96u7z4pc0uxtl0nq'); From 217b02504a07a4c20aa5de989886e4fb5ea1410a Mon Sep 17 00:00:00 2001 From: Brendon Duncan Date: Wed, 29 Aug 2018 10:04:02 +1200 Subject: [PATCH 11/19] Added tests for single slash. Removed some unused code. --- src/js/services/bitcoin-uri.service.js | 13 ------------ src/js/services/bitcoin-uri.service.spec.js | 22 +++++++++++++++++++++ 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/src/js/services/bitcoin-uri.service.js b/src/js/services/bitcoin-uri.service.js index fd5c5d70f..6b0be7191 100644 --- a/src/js/services/bitcoin-uri.service.js +++ b/src/js/services/bitcoin-uri.service.js @@ -12,24 +12,12 @@ 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 }; return service; - - function generateTestData() { - 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')); - - } function bitpayAddrOnMainnet(address) { var Address = bch.Address; @@ -52,7 +40,6 @@ return result; } - function cashAddrOnMainnet(address) { var Address = bch.Address; var CashAddrFormat = Address.CashAddrFormat; diff --git a/src/js/services/bitcoin-uri.service.spec.js b/src/js/services/bitcoin-uri.service.spec.js index c41424e75..3f75635cc 100644 --- a/src/js/services/bitcoin-uri.service.spec.js +++ b/src/js/services/bitcoin-uri.service.spec.js @@ -138,6 +138,17 @@ fdescribe('bitcoinUriService', function() { 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.asReceived).toBe('1GhpYmbRaf73AZRxDwAGr6653iZBGzdgeA'); + expect(parsed.publicAddress.legacy).toBe('1GhpYmbRaf73AZRxDwAGr6653iZBGzdgeA'); + expect(parsed.testnet).toBe(false); + }); + it('Bitcoin uri with slashes', function() { var parsed = bitcoinUriService.parse('bitcoin://18PCPhgZJjLxe9g3Q1BXLpL5aVut1fW3aX'); @@ -215,6 +226,17 @@ fdescribe('bitcoinUriService', function() { 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.asReceived).toBe('qzdectfmuw0xxztfx7mh045830dqcshj85hr44l35a'); + expect(parsed.publicAddress.legacy).toBe('1FBnq5gZhzTvvcJBjA7C2P3bKQZCiJaG1x'); + expect(parsed.testnet).toBe(false); + }); + it('cashAddr with slashes', function() { var parsed = bitcoinUriService.parse('bitcoincash://qpj966w8utue75lqqq3rlgh20zkz3rmydqpq8syv9c'); From d30a076f4b22b92b9c350cc842ebf1a33d82a8ef Mon Sep 17 00:00:00 2001 From: Brendon Duncan Date: Wed, 29 Aug 2018 10:05:23 +1200 Subject: [PATCH 12/19] Changed argument name to data, because it may not be a URI. --- src/js/services/bitcoin-uri.service.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/js/services/bitcoin-uri.service.js b/src/js/services/bitcoin-uri.service.js index 6b0be7191..e05267fb5 100644 --- a/src/js/services/bitcoin-uri.service.js +++ b/src/js/services/bitcoin-uri.service.js @@ -121,13 +121,13 @@ */ - function parse(uri) { + function parse(data) { var parsed = { isValid: false }; // Identify prefix - var trimmed = uri.trim(); + var trimmed = data.trim(); var colonSplit = /^([\w-]*):?(.*)$/.exec(trimmed); if (!colonSplit) { return parsed; From 32aa02a8c21175361cc1cd1a17c7e19364332516 Mon Sep 17 00:00:00 2001 From: Brendon Duncan Date: Wed, 29 Aug 2018 10:15:03 +1200 Subject: [PATCH 13/19] Fails gracefully when not passed a string. --- src/js/services/bitcoin-uri.service.js | 4 ++++ src/js/services/bitcoin-uri.service.spec.js | 7 +++++++ 2 files changed, 11 insertions(+) diff --git a/src/js/services/bitcoin-uri.service.js b/src/js/services/bitcoin-uri.service.js index e05267fb5..e8cac76b4 100644 --- a/src/js/services/bitcoin-uri.service.js +++ b/src/js/services/bitcoin-uri.service.js @@ -126,6 +126,10 @@ isValid: false }; + if (typeof data !== 'string') { + return parsed; + } + // Identify prefix var trimmed = data.trim(); var colonSplit = /^([\w-]*):?(.*)$/.exec(trimmed); diff --git a/src/js/services/bitcoin-uri.service.spec.js b/src/js/services/bitcoin-uri.service.spec.js index 3f75635cc..be9738ba0 100644 --- a/src/js/services/bitcoin-uri.service.spec.js +++ b/src/js/services/bitcoin-uri.service.spec.js @@ -293,6 +293,13 @@ fdescribe('bitcoinUriService', function() { 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'); From e6d438abf98f49cdff6da595df8f6d1dd90b2a1f Mon Sep 17 00:00:00 2001 From: Brendon Duncan Date: Wed, 29 Aug 2018 11:18:09 +1200 Subject: [PATCH 14/19] Test for a bare URL being invalid. --- src/js/services/bitcoin-uri.service.spec.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/js/services/bitcoin-uri.service.spec.js b/src/js/services/bitcoin-uri.service.spec.js index be9738ba0..f01ae087b 100644 --- a/src/js/services/bitcoin-uri.service.spec.js +++ b/src/js/services/bitcoin-uri.service.spec.js @@ -372,4 +372,11 @@ fdescribe('bitcoinUriService', function() { expect(parsed.isValid).toBe(false); }); + it('URL only', function() { + + var parsed = bitcoinUriService.parse('https://www.google.com'); + + expect(parsed.isValid).toBe(false); + }); + }); \ No newline at end of file From 3a3a525133b3d9ce37ce464b483243f4781a20d1 Mon Sep 17 00:00:00 2001 From: Brendon Duncan Date: Wed, 29 Aug 2018 11:38:20 +1200 Subject: [PATCH 15/19] Bugfix for displaying payment amount. --- src/js/controllers/walletSelectorController.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/js/controllers/walletSelectorController.js b/src/js/controllers/walletSelectorController.js index 777871e44..6a5b96cbf 100644 --- a/src/js/controllers/walletSelectorController.js +++ b/src/js/controllers/walletSelectorController.js @@ -99,6 +99,7 @@ angular.module('copayApp.controllers').controller('walletSelectorController', fu $scope.requestAmountSecondary = fiatAmount; $scope.requestCurrencySecondary = fiatCurrrency; } + $scope.$apply(); } }); } From fcfb039673d3c1aacbe6f6a0c185808ec6ddc737 Mon Sep 17 00:00:00 2001 From: Brendon Duncan Date: Wed, 29 Aug 2018 15:21:43 +1200 Subject: [PATCH 16/19] parse() now only returns the address in the format that was present in the data, to make it easier to find out about the data. --- src/js/services/bitcoin-uri.service.js | 11 ++--- src/js/services/bitcoin-uri.service.spec.js | 47 ++++----------------- 2 files changed, 12 insertions(+), 46 deletions(-) diff --git a/src/js/services/bitcoin-uri.service.js b/src/js/services/bitcoin-uri.service.js index e8cac76b4..4505a6eb4 100644 --- a/src/js/services/bitcoin-uri.service.js +++ b/src/js/services/bitcoin-uri.service.js @@ -261,23 +261,20 @@ parsed.address = addressLowerCase; parsed.coin = 'bch'; parsed.publicAddress = { - asReceived: addressLowerCase, - legacy: cashAddrTestnet.toString() + cashAddr: addressLowerCase }; parsed.isValid = true; } else if (cashAddrMainnet) { parsed.coin = 'bch'; parsed.publicAddress = { - asReceived: addressLowerCase, - legacy: cashAddrMainnet.toString() + cashAddr: addressLowerCase }; parsed.testnet = false; parsed.isValid = true; } else if (bitcore.Address.isValid(address, 'livenet')) { parsed.publicAddress = { - asReceived: address, legacy: address }; parsed.testnet = false; @@ -285,7 +282,6 @@ } else if (bitcore.Address.isValid(address, 'testnet')) { parsed.publicAddress = { - asReceived: address, legacy: address }; parsed.testnet = true; @@ -294,8 +290,7 @@ } else if (bitpayAddrMainnet) { parsed.coin = 'bch'; parsed.publicAddress = { - asReceived: address, - legacy: bitpayAddrMainnet.toString() + bitpay: address }; parsed.testnet = false; parsed.isValid = true; diff --git a/src/js/services/bitcoin-uri.service.spec.js b/src/js/services/bitcoin-uri.service.spec.js index f01ae087b..0e4a2ba31 100644 --- a/src/js/services/bitcoin-uri.service.spec.js +++ b/src/js/services/bitcoin-uri.service.spec.js @@ -40,7 +40,6 @@ fdescribe('bitcoinUriService', function() { expect(parsed.isValid).toBe(true); expect(parsed.coin).toBe('bch'); - expect(parsed.publicAddress.asReceived).toBe('1G9FA9fFnHfTYxvmXeAbBD9FwzPAVMbd3j'); expect(parsed.publicAddress.legacy).toBe('1G9FA9fFnHfTYxvmXeAbBD9FwzPAVMbd3j'); expect(parsed.testnet).toBe(false); }); @@ -51,7 +50,6 @@ fdescribe('bitcoinUriService', function() { expect(parsed.isValid).toBe(true); expect(parsed.coin).toBe('bch'); - expect(parsed.publicAddress.asReceived).toBe('mkDQrKfSFD441JxrD1iPBsJFExgkvrPGQn'); expect(parsed.publicAddress.legacy).toBe('mkDQrKfSFD441JxrD1iPBsJFExgkvrPGQn'); expect(parsed.testnet).toBe(true); }); @@ -64,8 +62,7 @@ fdescribe('bitcoinUriService', function() { expect(parsed.coin).toBe('bch'); expect(parsed.others.mystery).toBe('Melton probang'); expect(parsed.others.unknown).toBe('something'); - expect(parsed.publicAddress.asReceived).toBe('qr8v2vqnzntykakht43rqmxq8cdjzjp795fc3vsjgc'); - expect(parsed.publicAddress.legacy).toBe('1KrJRNApaAKRvHL5kDtL69nwmAJ31apAnu'); + 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); @@ -85,18 +82,6 @@ fdescribe('bitcoinUriService', function() { expect(parsed.isValid).toBe(true); expect(parsed.coin).toBeUndefined(); - expect(parsed.publicAddress.asReceived).toBe('mtWcoToWhbtPoCby5fvs8xdBujT5GGenD4'); - expect(parsed.publicAddress.legacy).toBe('mtWcoToWhbtPoCby5fvs8xdBujT5GGenD4'); - expect(parsed.testnet).toBe(true); - }); - - it('Bitcoin testnet address', function() { - - var parsed = bitcoinUriService.parse('mtWcoToWhbtPoCby5fvs8xdBujT5GGenD4'); - - expect(parsed.isValid).toBe(true); - expect(parsed.coin).toBeUndefined(); - expect(parsed.publicAddress.asReceived).toBe('mtWcoToWhbtPoCby5fvs8xdBujT5GGenD4'); expect(parsed.publicAddress.legacy).toBe('mtWcoToWhbtPoCby5fvs8xdBujT5GGenD4'); expect(parsed.testnet).toBe(true); }); @@ -107,7 +92,6 @@ fdescribe('bitcoinUriService', function() { expect(parsed.isValid).toBe(true); expect(parsed.coin).toBe('btc'); - expect(parsed.publicAddress.asReceived).toBe('15yCdKWVKRvfXMJpPYZBqMhiGKwjKzZdLN'); expect(parsed.publicAddress.legacy).toBe('15yCdKWVKRvfXMJpPYZBqMhiGKwjKzZdLN'); expect(parsed.testnet).toBe(false); }); @@ -119,7 +103,6 @@ fdescribe('bitcoinUriService', function() { expect(parsed.isValid).toBe(true); expect(parsed.coin).toBe('btc'); expect(parsed.label).toBe('Mr. Smith'); - expect(parsed.publicAddress.asReceived).toBe('1MxudKDEBWZ1yjizUSf6htacenNtb3DWbT'); expect(parsed.publicAddress.legacy).toBe('1MxudKDEBWZ1yjizUSf6htacenNtb3DWbT'); expect(parsed.testnet).toBe(false); }); @@ -132,7 +115,6 @@ fdescribe('bitcoinUriService', function() { expect(parsed.amount).toBe('20.3'); expect(parsed.coin).toBe('btc'); expect(parsed.label).toBe('Luke-Jr'); - expect(parsed.publicAddress.asReceived).toBe('12nCRhMDfxVnuF3uYMXv2fNxBohNmacfWu'); expect(parsed.publicAddress.legacy).toBe('12nCRhMDfxVnuF3uYMXv2fNxBohNmacfWu'); expect(parsed.message).toBe('Donation for project xyz'); expect(parsed.testnet).toBe(false); @@ -144,7 +126,6 @@ fdescribe('bitcoinUriService', function() { expect(parsed.isValid).toBe(true); expect(parsed.coin).toBe('btc'); - expect(parsed.publicAddress.asReceived).toBe('1GhpYmbRaf73AZRxDwAGr6653iZBGzdgeA'); expect(parsed.publicAddress.legacy).toBe('1GhpYmbRaf73AZRxDwAGr6653iZBGzdgeA'); expect(parsed.testnet).toBe(false); }); @@ -155,7 +136,6 @@ fdescribe('bitcoinUriService', function() { expect(parsed.isValid).toBe(true); expect(parsed.coin).toBe('btc'); - expect(parsed.publicAddress.asReceived).toBe('18PCPhgZJjLxe9g3Q1BXLpL5aVut1fW3aX'); expect(parsed.publicAddress.legacy).toBe('18PCPhgZJjLxe9g3Q1BXLpL5aVut1fW3aX'); expect(parsed.testnet).toBe(false); }); @@ -166,8 +146,7 @@ fdescribe('bitcoinUriService', function() { expect(parsed.isValid).toBe(true); expect(parsed.coin).toBe('bch'); - expect(parsed.publicAddress.asReceived).toBe('CJoRov8TirekvajiimQpb5Hk95evA7H2Yz'); - expect(parsed.publicAddress.legacy).toBe('13LYEsnPqogE2SqJ325u1ZfiWxSWEo6uyo'); + expect(parsed.publicAddress.bitpay).toBe('CJoRov8TirekvajiimQpb5Hk95evA7H2Yz'); expect(parsed.testnet).toBe(false); }); @@ -177,7 +156,6 @@ fdescribe('bitcoinUriService', function() { expect(parsed.isValid).toBe(true); expect(parsed.coin).toBeUndefined(); - expect(parsed.publicAddress.asReceived).toBe('1JXeGEu7bNEAYu6URT6dU6g1Ys6ffSAWYW'); expect(parsed.publicAddress.legacy).toBe('1JXeGEu7bNEAYu6URT6dU6g1Ys6ffSAWYW'); expect(parsed.testnet).toBe(false); }); @@ -188,8 +166,7 @@ fdescribe('bitcoinUriService', function() { expect(parsed.isValid).toBe(true); expect(parsed.coin).toBe('bch'); - expect(parsed.publicAddress.asReceived).toBe('qpcz6pmurq9ctg5848trzz9zmuuygj4q5qam7ph3gt'); - expect(parsed.publicAddress.legacy).toBe('mqk5vE278ytt6LUZqd97wi8c3FHsSYREX4'); + expect(parsed.publicAddress.cashAddr).toBe('qpcz6pmurq9ctg5848trzz9zmuuygj4q5qam7ph3gt'); expect(parsed.testnet).toBe(true); }); @@ -199,8 +176,7 @@ fdescribe('bitcoinUriService', function() { expect(parsed.isValid).toBe(true); expect(parsed.coin).toBe('bch'); - expect(parsed.publicAddress.asReceived).toBe('qzzg9nmc5vx8gap6xfatx3twnsdn2yrmcssulsmy44'); - expect(parsed.publicAddress.legacy).toBe('1D5euC1yUbHiNpXreQrUUYt7LNevD3cviR'); + expect(parsed.publicAddress.cashAddr).toBe('qzzg9nmc5vx8gap6xfatx3twnsdn2yrmcssulsmy44'); expect(parsed.testnet).toBe(false); }); @@ -210,8 +186,7 @@ fdescribe('bitcoinUriService', function() { expect(parsed.isValid).toBe(true); expect(parsed.coin).toBe('bch'); - expect(parsed.publicAddress.asReceived).toBe('qpshfu3dk5s3e7zdcgdcun6xgxtra6uyxs7g580js0'); - expect(parsed.publicAddress.legacy).toBe('19tJeAD7JwkarE6hgviqHKqUYVgtPAfMbb'); + expect(parsed.publicAddress.cashAddr).toBe('qpshfu3dk5s3e7zdcgdcun6xgxtra6uyxs7g580js0'); expect(parsed.testnet).toBe(false); }); @@ -221,8 +196,7 @@ fdescribe('bitcoinUriService', function() { expect(parsed.isValid).toBe(true); expect(parsed.coin).toBe('bch'); - expect(parsed.publicAddress.asReceived).toBe('qrq9p82a247lecv08ldk5p5h6ahtnjzpqcnh8yhq92'); - expect(parsed.publicAddress.legacy).toBe('1JXsK3HSFqoMnwh4Mevf5bTgqPcgNWX7ic'); + expect(parsed.publicAddress.cashAddr).toBe('qrq9p82a247lecv08ldk5p5h6ahtnjzpqcnh8yhq92'); expect(parsed.testnet).toBe(false); }); @@ -232,8 +206,7 @@ fdescribe('bitcoinUriService', function() { expect(parsed.isValid).toBe(true); expect(parsed.coin).toBe('bch'); - expect(parsed.publicAddress.asReceived).toBe('qzdectfmuw0xxztfx7mh045830dqcshj85hr44l35a'); - expect(parsed.publicAddress.legacy).toBe('1FBnq5gZhzTvvcJBjA7C2P3bKQZCiJaG1x'); + expect(parsed.publicAddress.cashAddr).toBe('qzdectfmuw0xxztfx7mh045830dqcshj85hr44l35a'); expect(parsed.testnet).toBe(false); }); @@ -243,8 +216,7 @@ fdescribe('bitcoinUriService', function() { expect(parsed.isValid).toBe(true); expect(parsed.coin).toBe('bch'); - expect(parsed.publicAddress.asReceived).toBe('qpj966w8utue75lqqq3rlgh20zkz3rmydqpq8syv9c'); - expect(parsed.publicAddress.legacy).toBe('1A9gUeVVKcJtbbHfAPjUHmLSWJrD5YEc7k'); + expect(parsed.publicAddress.cashAddr).toBe('qpj966w8utue75lqqq3rlgh20zkz3rmydqpq8syv9c'); expect(parsed.testnet).toBe(false); }); @@ -254,8 +226,7 @@ fdescribe('bitcoinUriService', function() { expect(parsed.isValid).toBe(true); expect(parsed.coin).toBe('bch'); - expect(parsed.publicAddress.asReceived).toBe('qqen2y3l28dpk0dzsag8w027ds96u7z4pc0uxtl0nq'); - expect(parsed.publicAddress.legacy).toBe('15fm3EwqgBYcxkndALBfforueps5yWKReJ'); + expect(parsed.publicAddress.cashAddr).toBe('qqen2y3l28dpk0dzsag8w027ds96u7z4pc0uxtl0nq'); expect(parsed.testnet).toBe(false); }); From 5738bab13e9f1a945e7733d9062a29519f68a99a Mon Sep 17 00:00:00 2001 From: Brendon Duncan Date: Wed, 29 Aug 2018 15:41:13 +1200 Subject: [PATCH 17/19] Updated comments. --- src/js/services/bitcoin-uri.service.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/js/services/bitcoin-uri.service.js b/src/js/services/bitcoin-uri.service.js index 4505a6eb4..0dd57dbf2 100644 --- a/src/js/services/bitcoin-uri.service.js +++ b/src/js/services/bitcoin-uri.service.js @@ -106,7 +106,8 @@ wif: '' }'', publicAddress: { - asReceived: '', + bitpay: '', + cashAddr: '', legacy: '', }, req: { @@ -117,7 +118,8 @@ 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. */ From 89f4328f8e0e98d0dcd04d1b300a5050a9383562 Mon Sep 17 00:00:00 2001 From: Brendon Duncan Date: Wed, 29 Aug 2018 15:42:43 +1200 Subject: [PATCH 18/19] BIP70 in IncomingDataService uses new BitcoinUriService. --- src/js/services/incomingData.js | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/js/services/incomingData.js b/src/js/services/incomingData.js index 0bf708d8a..f01959815 100644 --- a/src/js/services/incomingData.js +++ b/src/js/services/incomingData.js @@ -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); } }); } From 8a1e3f22973624e561417eacaa64ec9ca0d95b13 Mon Sep 17 00:00:00 2001 From: Brendon Duncan Date: Wed, 29 Aug 2018 15:55:49 +1200 Subject: [PATCH 19/19] Bitcoin Cash URI in incoming data now more permissive with the prefix. --- src/js/services/incomingData.js | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/js/services/incomingData.js b/src/js/services/incomingData.js index f01959815..c7677ed0a 100644 --- a/src/js/services/incomingData.js +++ b/src/js/services/incomingData.js @@ -145,6 +145,7 @@ angular.module('copayApp.services').factory('incomingData', function(bitcoinUriS data = sanitizeUri(data); + var addr = ''; // Bitcoin URL if (bitcore.URI.isValid(data)) { var coin = 'btc'; @@ -167,18 +168,18 @@ angular.module('copayApp.services').factory('incomingData', function(bitcoinUriS } 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); @@ -402,7 +403,7 @@ angular.module('copayApp.services').factory('incomingData', function(bitcoinUriS function handlePayPro(payProData, coin) { - console.log(payProData); + console.log('payProData', payProData); var toAddr = payProData.toAddress; var amount = payProData.amount; @@ -457,9 +458,6 @@ angular.module('copayApp.services').factory('incomingData', function(bitcoinUriS 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,