CashAddr on testnet and Bitpay format on mainnet.
This commit is contained in:
parent
a4ab20abba
commit
4d525c85d7
2 changed files with 249 additions and 79 deletions
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue