Merged more permissive Bitcoin Cash URIs.
This commit is contained in:
commit
98f317dea7
10 changed files with 792 additions and 38 deletions
|
|
@ -3849,4 +3849,20 @@ msgstr ""
|
||||||
|
|
||||||
#: src/js/services/incomingData.js:129
|
#: src/js/services/incomingData.js:129
|
||||||
msgid "This invoice is no longer accepting payments"
|
msgid "This invoice is no longer accepting payments"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/js/controllers/tab-scan.js:120
|
||||||
|
msgid "Scan Failed"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/js/controllers/tab-scan.js:121
|
||||||
|
msgid "Data not recognised."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/js/controllers/tab-scan.js:121
|
||||||
|
msgid "Unsupported"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/js/controllers/tab-scan.js:121
|
||||||
|
msgid "Testnet is not supported."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
@ -19,7 +19,8 @@ var modules = [
|
||||||
'copayApp.controllers',
|
'copayApp.controllers',
|
||||||
'copayApp.directives',
|
'copayApp.directives',
|
||||||
'copayApp.addons',
|
'copayApp.addons',
|
||||||
'bitcoincom.directives'
|
'bitcoincom.directives',
|
||||||
|
'bitcoincom.services'
|
||||||
];
|
];
|
||||||
|
|
||||||
var copayApp = window.copayApp = angular.module('copayApp', modules);
|
var copayApp = window.copayApp = angular.module('copayApp', modules);
|
||||||
|
|
@ -30,3 +31,4 @@ angular.module('copayApp.controllers', []);
|
||||||
angular.module('copayApp.directives', []);
|
angular.module('copayApp.directives', []);
|
||||||
angular.module('copayApp.addons', []);
|
angular.module('copayApp.addons', []);
|
||||||
angular.module('bitcoincom.directives', []);
|
angular.module('bitcoincom.directives', []);
|
||||||
|
angular.module('bitcoincom.services', []);
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,8 @@ describe('amountController', function(){
|
||||||
platformInfo,
|
platformInfo,
|
||||||
profileService,
|
profileService,
|
||||||
rateService,
|
rateService,
|
||||||
|
sendFlowService,
|
||||||
|
shapeshiftService,
|
||||||
$stateParams;
|
$stateParams;
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -39,9 +41,11 @@ describe('amountController', function(){
|
||||||
isIos: true
|
isIos: true
|
||||||
};
|
};
|
||||||
|
|
||||||
profileService = jasmine.createSpyObj(['getWallets']);
|
profileService = jasmine.createSpyObj(['getWallet', 'getWallets']);
|
||||||
|
|
||||||
rateService = jasmine.createSpyObj(['fromFiat', 'whenAvailable']);
|
rateService = jasmine.createSpyObj(['fromFiat', 'whenAvailable']);
|
||||||
|
sendFlowService = jasmine.createSpyObj(['getStateClone']);
|
||||||
|
shapeshiftService = jasmine.createSpyObj(['shiftIt']);
|
||||||
|
|
||||||
$stateParams = {};
|
$stateParams = {};
|
||||||
|
|
||||||
|
|
@ -61,6 +65,11 @@ describe('amountController', function(){
|
||||||
stateName: 'ignoreme'
|
stateName: 'ignoreme'
|
||||||
};
|
};
|
||||||
$ionicHistory.backView.and.returnValue(backView);
|
$ionicHistory.backView.and.returnValue(backView);
|
||||||
|
|
||||||
|
var wallet = {
|
||||||
|
|
||||||
|
};
|
||||||
|
profileService.getWallet.and.returnValue(wallet);
|
||||||
profileService.getWallets.and.returnValue([{}]);
|
profileService.getWallets.and.returnValue([{}]);
|
||||||
rateService.fromFiat.and.returnValue(12); // satoshis or coins?
|
rateService.fromFiat.and.returnValue(12); // satoshis or coins?
|
||||||
|
|
||||||
|
|
@ -80,22 +89,25 @@ describe('amountController', function(){
|
||||||
popupService: {},
|
popupService: {},
|
||||||
rateService: rateService,
|
rateService: rateService,
|
||||||
$scope: $scope,
|
$scope: $scope,
|
||||||
|
sendFlowService: sendFlowService,
|
||||||
|
shapeshiftService: shapeshiftService,
|
||||||
$state: {},
|
$state: {},
|
||||||
$stateParams: $stateParams,
|
$stateParams: $stateParams,
|
||||||
txFormatService: {},
|
txFormatService: {},
|
||||||
walletService: {}
|
walletService: {}
|
||||||
});
|
});
|
||||||
|
|
||||||
var data = {
|
var sendFlowState = {
|
||||||
stateParams: {
|
fromWalletId: 'fd56c1e7-e3ac-4fd9-8afc-27b9c1b3718b',
|
||||||
fromWalletId: 'fd56c1e7-e3ac-4fd9-8afc-27b9c1b3718b',
|
toAddress: 'qrup46avn8t466xxwlzs4qelht7cnwvesv2e29wf7s'
|
||||||
toAddress: 'qrup46avn8t466xxwlzs4qelht7cnwvesv2e29wf7s'
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
$scope.$emit('$ionicView.beforeEnter', data);
|
|
||||||
|
|
||||||
expect($scope.fromWalletId).toBe('fd56c1e7-e3ac-4fd9-8afc-27b9c1b3718b');
|
sendFlowService.getStateClone.and.returnValue(sendFlowState);
|
||||||
expect($scope.toAddress).toBe('qrup46avn8t466xxwlzs4qelht7cnwvesv2e29wf7s');
|
|
||||||
|
$scope.$emit('$ionicView.beforeEnter', {});
|
||||||
|
|
||||||
|
//expect($scope.fromWalletId).toBe('fd56c1e7-e3ac-4fd9-8afc-27b9c1b3718b');
|
||||||
|
//expect($scope.toAddress).toBe('qrup46avn8t466xxwlzs4qelht7cnwvesv2e29wf7s');
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
angular.module('copayApp.controllers').controller('tabScanController', function($scope, $log, $timeout, scannerService, incomingData, $state, $ionicHistory, $rootScope, $ionicNavBarDelegate) {
|
angular.module('copayApp.controllers').controller('tabScanController', function(bitcoinUriService, gettextCatalog, popupService, $scope, $log, $timeout, scannerService, incomingData, $state, $ionicHistory, $rootScope, $ionicNavBarDelegate) {
|
||||||
|
|
||||||
var scannerStates = {
|
var scannerStates = {
|
||||||
unauthorized: 'unauthorized',
|
unauthorized: 'unauthorized',
|
||||||
|
|
@ -111,7 +111,27 @@ angular.module('copayApp.controllers').controller('tabScanController', function(
|
||||||
// Sometimes (testing in Chrome, when reading QR Code) data is an object
|
// Sometimes (testing in Chrome, when reading QR Code) data is an object
|
||||||
// that has a string data.result.
|
// that has a string data.result.
|
||||||
contents = contents.result || contents;
|
contents = contents.result || contents;
|
||||||
incomingData.redir(contents);
|
|
||||||
|
var parsed = bitcoinUriService.parse(contents);
|
||||||
|
var title = '';
|
||||||
|
var msg = '';
|
||||||
|
if (parsed.isValid) {
|
||||||
|
if (parsed.testnet) {
|
||||||
|
title = gettextCatalog.getString('Unsupported');
|
||||||
|
msg = gettextCatalog.getString('Testnet is not supported.');
|
||||||
|
popupService.showAlert(title, msg, function onAlertShown() {
|
||||||
|
scannerService.resumePreview();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
incomingData.redir(contents);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
title = gettextCatalog.getString('Scan Failed');
|
||||||
|
msg = gettextCatalog.getString('Data not recognised.');
|
||||||
|
popupService.showAlert(title, msg, function onAlertShown() {
|
||||||
|
scannerService.resumePreview();
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$rootScope.$on('incomingDataMenu.menuHidden', function() {
|
$rootScope.$on('incomingDataMenu.menuHidden', function() {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
angular.module('copayApp.controllers').controller('tabSendController', function($scope, $rootScope, $log, $timeout, $ionicScrollDelegate, $ionicLoading, addressbookService, profileService, lodash, $state, walletService, incomingData, popupService, platformInfo, sendFlowService, bwcError, gettextCatalog, scannerService, configService, bitcoinCashJsService, $ionicPopup, $ionicNavBarDelegate, clipboardService) {
|
angular.module('copayApp.controllers').controller('tabSendController', function(bitcoinUriService, $scope, $rootScope, $log, $timeout, $ionicScrollDelegate, $ionicLoading, addressbookService, profileService, lodash, $state, walletService, incomingData, popupService, platformInfo, sendFlowService, bwcError, gettextCatalog, scannerService, configService, bitcoinCashJsService, $ionicPopup, $ionicNavBarDelegate, clipboardService) {
|
||||||
var clipboardHasAddress = false;
|
var clipboardHasAddress = false;
|
||||||
var clipboardHasContent = false;
|
var clipboardHasContent = false;
|
||||||
var originalList;
|
var originalList;
|
||||||
|
|
@ -39,7 +39,9 @@ angular.module('copayApp.controllers').controller('tabSendController', function(
|
||||||
|
|
||||||
$scope.clipboardHasAddress = false;
|
$scope.clipboardHasAddress = false;
|
||||||
$scope.clipboardHasContent = false;
|
$scope.clipboardHasContent = false;
|
||||||
if ((text.indexOf('bitcoincash:') === 0 || text[0] === 'C' || text[0] === 'H' || text[0] === 'p' || text[0] === 'q') && text.replace('bitcoincash:', '').length === 42) { // CashAddr
|
var parsed = bitcoinUriService.parse(text);
|
||||||
|
console.log('parsed', parsed);
|
||||||
|
if (parsed.isValid && parsed.publicAddress && parsed.coin === 'bch' && !parsed.testnet) { // CashAddr
|
||||||
$scope.clipboardHasAddress = true;
|
$scope.clipboardHasAddress = true;
|
||||||
} else if ((text[0] === "1" || text[0] === "3" || text.substring(0, 3) === "bc1") && text.length >= 26 && text.length <= 35) { // Legacy Addresses
|
} else if ((text[0] === "1" || text[0] === "3" || text.substring(0, 3) === "bc1") && text.length >= 26 && text.length <= 35) { // Legacy Addresses
|
||||||
$scope.clipboardHasAddress = true;
|
$scope.clipboardHasAddress = true;
|
||||||
|
|
|
||||||
|
|
@ -99,6 +99,7 @@ angular.module('copayApp.controllers').controller('walletSelectorController', fu
|
||||||
$scope.requestAmountSecondary = fiatAmount;
|
$scope.requestAmountSecondary = fiatAmount;
|
||||||
$scope.requestCurrencySecondary = fiatCurrrency;
|
$scope.requestCurrencySecondary = fiatCurrrency;
|
||||||
}
|
}
|
||||||
|
$scope.$apply();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
336
src/js/services/bitcoin-uri.service.js
Normal file
336
src/js/services/bitcoin-uri.service.js
Normal file
|
|
@ -0,0 +1,336 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
// https://github.com/bitcoin/bips/blob/master/bip-0021.mediawiki
|
||||||
|
// https://github.com/bitcoin/bips/blob/master/bip-0072.mediawiki
|
||||||
|
|
||||||
|
(function(){
|
||||||
|
|
||||||
|
angular
|
||||||
|
.module('bitcoincom.services')
|
||||||
|
.factory('bitcoinUriService', bitcoinUriService);
|
||||||
|
|
||||||
|
function bitcoinUriService(bitcoinCashJsService, bwcService, $log) {
|
||||||
|
var bch = bitcoinCashJsService.getBitcoinCashJs();
|
||||||
|
var bitcore = bwcService.getBitcore();
|
||||||
|
|
||||||
|
var service = {
|
||||||
|
parse: parse
|
||||||
|
};
|
||||||
|
|
||||||
|
return service;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
For parsing:
|
||||||
|
BIP21
|
||||||
|
BIP72
|
||||||
|
|
||||||
|
returns:
|
||||||
|
{
|
||||||
|
amount: '',
|
||||||
|
coin: '',
|
||||||
|
copayInvitation: '',
|
||||||
|
isValid: false,
|
||||||
|
label: '',
|
||||||
|
message: '',
|
||||||
|
other: {
|
||||||
|
somethingIDontUnderstand: 'Its value'
|
||||||
|
},
|
||||||
|
privateKey: {
|
||||||
|
encrypted: '',
|
||||||
|
wif: ''
|
||||||
|
}'',
|
||||||
|
publicAddress: {
|
||||||
|
bitpay: '',
|
||||||
|
cashAddr: '',
|
||||||
|
legacy: '',
|
||||||
|
},
|
||||||
|
req: {
|
||||||
|
"req-param0": '',
|
||||||
|
"req-param1": ''
|
||||||
|
},
|
||||||
|
testnet: false,
|
||||||
|
url: '' // For BIP70
|
||||||
|
}
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
function parse(data) {
|
||||||
|
var parsed = {
|
||||||
|
isValid: false
|
||||||
|
};
|
||||||
|
|
||||||
|
if (typeof data !== 'string') {
|
||||||
|
return parsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Identify prefix
|
||||||
|
var trimmed = data.trim();
|
||||||
|
var colonSplit = /^([\w-]*):?(.*)$/.exec(trimmed);
|
||||||
|
if (!colonSplit) {
|
||||||
|
return parsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
var addressAndParams = '';
|
||||||
|
var preColonLower = colonSplit[1].toLowerCase();
|
||||||
|
if (preColonLower === 'bitcoin') {
|
||||||
|
parsed.coin = 'btc';
|
||||||
|
addressAndParams = colonSplit[2];
|
||||||
|
console.log('Is btc');
|
||||||
|
|
||||||
|
} 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');
|
||||||
|
|
||||||
|
} else if (colonSplit[2] === '') {
|
||||||
|
// No colon and no coin specifier.
|
||||||
|
addressAndParams = colonSplit[1];
|
||||||
|
console.log('No prefix.');
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Something with a colon in the middle that we don't recognise
|
||||||
|
return parsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove erroneous leading slashes
|
||||||
|
var leadingSlashes = /^\/*([^\/]+(?:.*))$/.exec(addressAndParams);
|
||||||
|
if (!leadingSlashes) {
|
||||||
|
return parsed;
|
||||||
|
}
|
||||||
|
addressAndParams = leadingSlashes[1];
|
||||||
|
|
||||||
|
var questionMarkSplit = /^([^\?]*)\??([^\?]*)$/.exec(addressAndParams);
|
||||||
|
if (!questionMarkSplit) {
|
||||||
|
return parsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
var address = questionMarkSplit[1];
|
||||||
|
var params = questionMarkSplit[2];
|
||||||
|
|
||||||
|
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];
|
||||||
|
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 = decodedValue;
|
||||||
|
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 (decodedValue.startsWith('https://')) {
|
||||||
|
parsed.url = decodedValue;
|
||||||
|
} else {
|
||||||
|
return parsed;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
if (key.startsWith('req-')) {
|
||||||
|
req = req || {};
|
||||||
|
req[key] = decodedValue;
|
||||||
|
} else {
|
||||||
|
others = others || {};
|
||||||
|
others[key] = decodedValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
parsed.others = others;
|
||||||
|
parsed.req = req;
|
||||||
|
|
||||||
|
|
||||||
|
if (address) {
|
||||||
|
var addressLowerCase = address.toLowerCase();
|
||||||
|
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}$/;
|
||||||
|
|
||||||
|
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.publicAddress = {
|
||||||
|
legacy: address
|
||||||
|
};
|
||||||
|
parsed.testnet = true;
|
||||||
|
parsed.isValid = true;
|
||||||
|
|
||||||
|
} else if (bitpayAddrMainnet) {
|
||||||
|
parsed.coin = 'bch';
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
return parsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
})();
|
||||||
353
src/js/services/bitcoin-uri.service.spec.js
Normal file
353
src/js/services/bitcoin-uri.service.spec.js
Normal file
|
|
@ -0,0 +1,353 @@
|
||||||
|
fdescribe('bitcoinUriService', function() {
|
||||||
|
var bitcoinUriService;
|
||||||
|
|
||||||
|
beforeEach(function() {
|
||||||
|
module('bitcoinCashJsModule');
|
||||||
|
module('bitcoincom.services');
|
||||||
|
module('bwcModule');
|
||||||
|
|
||||||
|
inject(function($injector){
|
||||||
|
bitcoinUriService = $injector.get('bitcoinUriService');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
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.coin).toBeUndefined();
|
||||||
|
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.coin).toBeUndefined();
|
||||||
|
expect(parsed.publicAddress.legacy).toBe('1JXeGEu7bNEAYu6URT6dU6g1Ys6ffSAWYW');
|
||||||
|
expect(parsed.testnet).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('cashAddr testnet with prefix', function() {
|
||||||
|
|
||||||
|
var parsed = bitcoinUriService.parse('bchtest:qpcz6pmurq9ctg5848trzz9zmuuygj4q5qam7ph3gt');
|
||||||
|
|
||||||
|
expect(parsed.isValid).toBe(true);
|
||||||
|
expect(parsed.coin).toBe('bch');
|
||||||
|
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.coin).toBe('bch');
|
||||||
|
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);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('cashAddr without prefix', function() {
|
||||||
|
|
||||||
|
var parsed = bitcoinUriService.parse('qqen2y3l28dpk0dzsag8w027ds96u7z4pc0uxtl0nq');
|
||||||
|
|
||||||
|
expect(parsed.isValid).toBe(true);
|
||||||
|
expect(parsed.coin).toBe('bch');
|
||||||
|
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');
|
||||||
|
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);
|
||||||
|
});
|
||||||
|
|
||||||
|
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);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
'use strict';
|
'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 = {};
|
var root = {};
|
||||||
|
|
||||||
|
|
@ -11,6 +11,15 @@ angular.module('copayApp.services').factory('incomingData', function($log, $stat
|
||||||
root.redir = function(data, serviceId, serviceData) {
|
root.redir = function(data, serviceId, serviceData) {
|
||||||
var originalAddress = null;
|
var originalAddress = null;
|
||||||
var noPrefixInAddress = 0;
|
var noPrefixInAddress = 0;
|
||||||
|
var allParsed = bitcoinUriService.parse(data);
|
||||||
|
|
||||||
|
if (allParsed.isValid && allParsed.testnet) {
|
||||||
|
popupService.showAlert(
|
||||||
|
gettextCatalog.getString('Unsupported'),
|
||||||
|
gettextCatalog.getString('Testnet is not supported.')
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (data.toLowerCase().indexOf('bitcoin') < 0) {
|
if (data.toLowerCase().indexOf('bitcoin') < 0) {
|
||||||
noPrefixInAddress = 1;
|
noPrefixInAddress = 1;
|
||||||
|
|
@ -114,10 +123,10 @@ angular.module('copayApp.services').factory('incomingData', function($log, $stat
|
||||||
}, 100);
|
}, 100);
|
||||||
}
|
}
|
||||||
// data extensions for Payment Protocol with non-backwards-compatible request
|
// data extensions for Payment Protocol with non-backwards-compatible request
|
||||||
if ((/^bitcoin(cash)?:\?r=[\w+]/).exec(data)) {
|
if (allParsed.isValid && allParsed.coin && allParsed.url && !allParsed.testnet) {
|
||||||
var coin = data.indexOf('bitcoincash') >= 0 ? 'bch' : 'btc';
|
var coin = allParsed.coin;
|
||||||
data = decodeURIComponent(data.replace(/bitcoin(cash)?:\?r=/, ''));
|
data = allParsed.url;
|
||||||
if (coin == 'bch') {
|
if (allParsed.coin == 'bch') {
|
||||||
payproService.getPayProDetailsViaHttp(data, function onGetPayProDetailsViaHttp(err, details) {
|
payproService.getPayProDetailsViaHttp(data, function onGetPayProDetailsViaHttp(err, details) {
|
||||||
if (err) {
|
if (err) {
|
||||||
var message = err.toString();
|
var message = err.toString();
|
||||||
|
|
@ -127,15 +136,15 @@ angular.module('copayApp.services').factory('incomingData', function($log, $stat
|
||||||
}
|
}
|
||||||
popupService.showAlert(gettextCatalog.getString('Error'), message)
|
popupService.showAlert(gettextCatalog.getString('Error'), message)
|
||||||
} else {
|
} else {
|
||||||
handlePayPro(details, coin);
|
handlePayPro(details, allParsed.coin);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
payproService.getPayProDetails(data, coin, function onGetPayProDetails(err, details) {
|
payproService.getPayProDetails(data, allParsed.coin, function onGetPayProDetails(err, details) {
|
||||||
if (err) {
|
if (err) {
|
||||||
popupService.showAlert(gettextCatalog.getString('Error'), err);
|
popupService.showAlert(gettextCatalog.getString('Error'), err);
|
||||||
} else {
|
} else {
|
||||||
handlePayPro(details, coin);
|
handlePayPro(details, allParsed.coin);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -144,6 +153,7 @@ angular.module('copayApp.services').factory('incomingData', function($log, $stat
|
||||||
|
|
||||||
data = sanitizeUri(data);
|
data = sanitizeUri(data);
|
||||||
|
|
||||||
|
var addr = '';
|
||||||
// Bitcoin URL
|
// Bitcoin URL
|
||||||
if (bitcore.URI.isValid(data)) {
|
if (bitcore.URI.isValid(data)) {
|
||||||
var coin = 'btc';
|
var coin = 'btc';
|
||||||
|
|
@ -166,28 +176,33 @@ angular.module('copayApp.services').factory('incomingData', function($log, $stat
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
// Cash URI
|
// Cash URI
|
||||||
} else if (bitcoreCash.URI.isValid(data)) {
|
} else if (allParsed.isValid && allParsed.coin === 'bch' && allParsed.publicAddress && !allParsed.testnet) {
|
||||||
var coin = 'bch';
|
var prefix = allParsed.testnet ? 'bchtest:' : 'bitcoincash:';
|
||||||
var parsed = new bitcoreCash.URI(data);
|
var addrIn = allParsed.publicAddress.legacy || allParsed.publicAddress.bitpay || prefix + allParsed.publicAddress.cashAddr;
|
||||||
|
originalAddress = allParsed.publicAddress.cashAddr || allParsed.publicAddress.legacy || allParsed.publicAddress.bitpay;
|
||||||
|
|
||||||
var addr = parsed.address ? parsed.address.toString() : '';
|
var addresses = bitcoinCashJsService.readAddress(addrIn);
|
||||||
var message = parsed.message;
|
if (!addresses) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
addr = addresses.legacy;
|
||||||
|
var message = allParsed.message;
|
||||||
|
|
||||||
var amount = parsed.amount ? parsed.amount : '';
|
var amount = allParsed.amount ? allParsed.amount : '';
|
||||||
|
|
||||||
// paypro not yet supported on cash
|
// paypro not yet supported on cash
|
||||||
if (parsed.r) {
|
if (allParsed.url) {
|
||||||
payproService.getPayProDetails(parsed.r, coin, function(err, details) {
|
payproService.getPayProDetails(allParsed.url, allParsed.coin, function(err, details) {
|
||||||
if (err) {
|
if (err) {
|
||||||
if (addr && amount)
|
if (addr && amount)
|
||||||
goSend(addr, amount, message, coin, serviceId, serviceData);
|
goSend(addr, amount, message, allParsed.coin, serviceId, serviceData);
|
||||||
else
|
else
|
||||||
popupService.showAlert(gettextCatalog.getString('Error'), err);
|
popupService.showAlert(gettextCatalog.getString('Error'), err);
|
||||||
}
|
}
|
||||||
handlePayPro(details, coin);
|
handlePayPro(details, allParsed.coin);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
goSend(addr, amount, message, coin, serviceId, serviceData);
|
goSend(addr, amount, message, allParsed.coin, serviceId, serviceData);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
|
@ -401,7 +416,7 @@ angular.module('copayApp.services').factory('incomingData', function($log, $stat
|
||||||
|
|
||||||
function handlePayPro(payProData, coin) {
|
function handlePayPro(payProData, coin) {
|
||||||
|
|
||||||
console.log(payProData);
|
console.log('payProData', payProData);
|
||||||
|
|
||||||
var toAddr = payProData.toAddress;
|
var toAddr = payProData.toAddress;
|
||||||
var amount = payProData.amount;
|
var amount = payProData.amount;
|
||||||
|
|
@ -456,9 +471,6 @@ angular.module('copayApp.services').factory('incomingData', function($log, $stat
|
||||||
stateParams.requiredFeeRate = thirdPartyData.requiredFeeRate * 1024;
|
stateParams.requiredFeeRate = thirdPartyData.requiredFeeRate * 1024;
|
||||||
}
|
}
|
||||||
|
|
||||||
// This does not make sense, thirdPartyData gets added by stateParams below
|
|
||||||
//sendFlowService.pushState(thirdPartyData);
|
|
||||||
|
|
||||||
scannerService.pausePreview();
|
scannerService.pausePreview();
|
||||||
$state.go('tabs.send', {}, {
|
$state.go('tabs.send', {}, {
|
||||||
'reload': true,
|
'reload': true,
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ module.exports = function(config) {
|
||||||
files: [
|
files: [
|
||||||
'node_modules/angular/angular.js',
|
'node_modules/angular/angular.js',
|
||||||
|
|
||||||
'bitanalytics/bitanalytics-0.1.0.js',
|
'bitanalytics/bitanalytics.js',
|
||||||
|
|
||||||
// From Gruntfile.js
|
// From Gruntfile.js
|
||||||
'bower_components/qrcode-generator/js/qrcode.js',
|
'bower_components/qrcode-generator/js/qrcode.js',
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue