diff --git a/src/js/services/bitcoin-uri.service.js b/src/js/services/bitcoin-uri.service.js index ebd74fc25..40dc48b9f 100644 --- a/src/js/services/bitcoin-uri.service.js +++ b/src/js/services/bitcoin-uri.service.js @@ -6,8 +6,11 @@ .module('bitcoincom.services') .factory('bitcoinUriService', bitcoinUriService); - function bitcoinUriService(bitcoinCashJsService, bwcService) { + 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 }; @@ -16,6 +19,39 @@ + 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:'; + + 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; + } + + /* For parsing: BIP21 @@ -67,6 +103,13 @@ } else if (/^(?:bitcoincash)|(?:bitcoin-cash)$/.test(preColonLower)) { parsed.coin = 'bch'; + parsed.test = false; + addressAndParams = colonSplit[2]; + console.log('Is bch'); + + } else if (/^(?:bchtest)$/.test(preColonLower)) { + parsed.coin = 'bch'; + parsed.testnet = true; addressAndParams = colonSplit[2]; console.log('Is bch'); @@ -125,7 +168,7 @@ case 'r': // Could use a more comprehesive regex to test URL validity, but then how would we know - // which part of the validatiion it failed? + // which part of the validation it failed? if (value.startsWith('https://')) { parsed.url = value; } else { @@ -147,26 +190,38 @@ 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 legacyRe = /^[13][a-km-zA-HJ-NP-Z1-9]{25,34}$/; - var legacyTestnetRe = /^[mn][a-km-zA-HJ-NP-Z1-9]{25,34}$/; + + //var legacyRe = /^[13][a-km-zA-HJ-NP-Z1-9]{25,34}$/; + //var legacyTestnetRe = /^[mn][a-km-zA-HJ-NP-Z1-9]{25,34}$/; - if (legacyRe.test(address) && bitcore.Address.isValid(address, 'livenet')) { + if (bitcore.Address.isValid(address, 'livenet')) { parsed.address = address; parsed.legacyAddress = address; parsed.testnet = false; - } else if (legacyTestnetRe.test(address) && bitcore.Address.isValid(address, 'testnet')) { + } else if (bitcore.Address.isValid(address, 'testnet')) { 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; + parsed.coin = 'bch'; + // TODO: Get legacy address + } else if (cashAddrRe.test(address)) { - var cashAddr = 'bitcoincash:' + address.toLowerCase(); + var cashAddr = 'bitcoincash:' + addressLowerCase; parsed.address = cashAddr; parsed.coin = 'bch'; @@ -175,13 +230,14 @@ parsed.testnet = false; - } // TODO: Check for private key + } + + } - // TODO: identify different types of addresses // TODO: Check for a private key here too - } + // If has no address, must have Url. parsed.isValid = !!(parsed.address || parsed.url); diff --git a/src/js/services/bitcoin-uri.service.spec.js b/src/js/services/bitcoin-uri.service.spec.js index a2a0cc894..5cbfdb215 100644 --- a/src/js/services/bitcoin-uri.service.spec.js +++ b/src/js/services/bitcoin-uri.service.spec.js @@ -10,9 +10,6 @@ fdescribe('bitcoinUriService', function() { bitcoinUriService = $injector.get('bitcoinUriService'); }); }); - - - it('Bitcoin testnet address', function() { @@ -37,6 +34,17 @@ fdescribe('bitcoinUriService', function() { expect(parsed.testnet).toBe(false); }); + it('cashAddr testnet with prefix', 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.testnet).toBe(true); + }); + it('cashAddr with prefix', function() { var parsed = bitcoinUriService.parse('bitcoincash:qrq9p82a247lecv08ldk5p5h6ahtnjzpqcnh8yhq92'); @@ -58,4 +66,30 @@ fdescribe('bitcoinUriService', function() { expect(parsed.legacyAddress).toBe('15fm3EwqgBYcxkndALBfforueps5yWKReJ'); 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'); + expect(parsed.isValid).toBe(false); + }); + + it('invalid cashAddr style 2', function() { + var parsed = bitcoinUriService.parse('p:gpf8m4h7'); + expect(parsed.isValid).toBe(false); + }); + + it('invalid cashAddr style 3', function() { + var parsed = bitcoinUriService.parse('bitcoincash:qpzry9x8gf2tvdw0s3jn54khce6mua7lcw20ayyn'); + expect(parsed.isValid).toBe(false); + }); + + it('invalid cashAddr style 4', function() { + var parsed = bitcoinUriService.parse('bchtest:testnetaddress4d6njnut'); + expect(parsed.isValid).toBe(false); + }); + + it('invalid cashAddr style 5', function() { + var parsed = bitcoinUriService.parse('bchreg:555555555555555555555555555555555555555555555udxmlmrz'); + expect(parsed.isValid).toBe(false); + }); }); \ No newline at end of file