better handling of different BCH addresses formats
This commit is contained in:
parent
58447bbceb
commit
8f35e78b57
|
@ -5,11 +5,11 @@ var Bitcore_ = {
|
||||||
|
|
||||||
var _ = require('lodash');
|
var _ = require('lodash');
|
||||||
|
|
||||||
function AddressTranslator() {
|
function BCHAddressTranslator() {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
AddressTranslator.getAddressCoin = function(address) {
|
BCHAddressTranslator.getAddressCoin = function(address) {
|
||||||
try {
|
try {
|
||||||
new Bitcore_['btc'].Address(address);
|
new Bitcore_['btc'].Address(address);
|
||||||
return 'legacy';
|
return 'legacy';
|
||||||
|
@ -26,7 +26,7 @@ AddressTranslator.getAddressCoin = function(address) {
|
||||||
|
|
||||||
|
|
||||||
// Supports 3 formats: legacy (1xxx, mxxxx); Copay: (Cxxx, Hxxx), Cashaddr(qxxx);
|
// Supports 3 formats: legacy (1xxx, mxxxx); Copay: (Cxxx, Hxxx), Cashaddr(qxxx);
|
||||||
AddressTranslator.translate = function(addresses, to, from) {
|
BCHAddressTranslator.translate = function(addresses, to, from) {
|
||||||
var wasArray = true;
|
var wasArray = true;
|
||||||
if (!_.isArray(addresses)) {
|
if (!_.isArray(addresses)) {
|
||||||
wasArray = false;
|
wasArray = false;
|
||||||
|
@ -34,7 +34,7 @@ AddressTranslator.translate = function(addresses, to, from) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
from = from || AddressTranslator.getAddressCoin(addresses[0]);
|
from = from || BCHAddressTranslator.getAddressCoin(addresses[0]);
|
||||||
if (from == to) return addresses;
|
if (from == to) return addresses;
|
||||||
var ret = _.map(addresses, function(x) {
|
var ret = _.map(addresses, function(x) {
|
||||||
|
|
||||||
|
@ -57,15 +57,5 @@ AddressTranslator.translate = function(addresses, to, from) {
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
AddressTranslator.translateInput = function(addresses) {
|
|
||||||
return this.translate(addresses, 'btc', 'bch');
|
|
||||||
}
|
|
||||||
|
|
||||||
AddressTranslator.translateOutput = function(addresses) {
|
module.exports = BCHAddressTranslator;
|
||||||
return this.translate(addresses, 'bch', 'btc');
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
module.exports = AddressTranslator;
|
|
|
@ -37,6 +37,14 @@ function BlockChainExplorer(opts) {
|
||||||
|
|
||||||
var url = opts.url || PROVIDERS[provider][coin][network];
|
var url = opts.url || PROVIDERS[provider][coin][network];
|
||||||
|
|
||||||
|
|
||||||
|
if (coin != 'bch' && opts.addressFormat)
|
||||||
|
throw new Error('addressFormat only supported for bch');
|
||||||
|
|
||||||
|
if (coin == 'bch' && !opts.addressFormat)
|
||||||
|
opts.addressFormat = 'cashaddr';
|
||||||
|
|
||||||
|
|
||||||
switch (provider) {
|
switch (provider) {
|
||||||
case 'insight':
|
case 'insight':
|
||||||
return new Insight({
|
return new Insight({
|
||||||
|
@ -45,7 +53,7 @@ function BlockChainExplorer(opts) {
|
||||||
url: url,
|
url: url,
|
||||||
apiPrefix: opts.apiPrefix,
|
apiPrefix: opts.apiPrefix,
|
||||||
userAgent: opts.userAgent,
|
userAgent: opts.userAgent,
|
||||||
translateAddresses: opts.translateAddresses,
|
addressFormat: opts.addressFormat,
|
||||||
});
|
});
|
||||||
default:
|
default:
|
||||||
throw new Error('Provider ' + provider + ' not supported.');
|
throw new Error('Provider ' + provider + ' not supported.');
|
||||||
|
|
|
@ -24,7 +24,11 @@ function Insight(opts) {
|
||||||
this.network = opts.network || 'livenet';
|
this.network = opts.network || 'livenet';
|
||||||
this.hosts = opts.url;
|
this.hosts = opts.url;
|
||||||
this.userAgent = opts.userAgent || 'bws';
|
this.userAgent = opts.userAgent || 'bws';
|
||||||
this.shouldTranslateAddresses = _.isUndefined(opts.translateAddresses) ? this.coin == 'bch' : opts.translateAddresses;
|
|
||||||
|
if (opts.addressFormat) {
|
||||||
|
$.checkArgument(Constants.ADDRESS_FORMATS.includes(opts.addressFormat), 'Unkown addr format:' + opts.addressFormat);
|
||||||
|
this.addressFormat = opts.addressFormat != 'copay' ? opts.addressFormat : null;
|
||||||
|
}
|
||||||
|
|
||||||
this.requestQueue = async.queue(this._doRequest.bind(this), Defaults.INSIGHT_REQUEST_POOL_SIZE);
|
this.requestQueue = async.queue(this._doRequest.bind(this), Defaults.INSIGHT_REQUEST_POOL_SIZE);
|
||||||
|
|
||||||
|
@ -42,39 +46,33 @@ var _parseErr = function(err, res) {
|
||||||
|
|
||||||
// Translate Request Address query
|
// Translate Request Address query
|
||||||
Insight.prototype.translateQueryAddresses = function(addresses) {
|
Insight.prototype.translateQueryAddresses = function(addresses) {
|
||||||
|
if (!this.addressFormat) return addresses;
|
||||||
console.log('[insight.js.45]', this.shouldTranslateAddresses); //TODO
|
return AddressTranslator.translate(addresses, this.addressFormat, 'copay');
|
||||||
if (!this.shouldTranslateAddresses) return addresses;
|
|
||||||
|
|
||||||
// It is called 'translatedInput' from Cxxx to qXXXX because the
|
|
||||||
// module was created for insight-api
|
|
||||||
return AddressTranslator.translateInput(addresses);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// Translate Result Address
|
// Translate Result Address
|
||||||
Insight.prototype.translateResultAddresses = function(addresses) {
|
Insight.prototype.translateResultAddresses = function(addresses) {
|
||||||
if (!this.shouldTranslateAddresses) return addresses;
|
if (!this.addressFormat) return addresses;
|
||||||
|
|
||||||
// It is called 'translateOutput' from qXXX to Cxxx because the
|
return AddressTranslator.translate(addresses, 'copay', this.addressFormat);
|
||||||
// module was created for insight-api
|
|
||||||
return AddressTranslator.translateOutput(addresses);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
Insight.prototype.translateTx = function(tx) {
|
Insight.prototype.translateTx = function(tx) {
|
||||||
if (!this.shouldTranslateAddresses) return tx;
|
var self = this;
|
||||||
|
if (!this.addressFormat) return tx;
|
||||||
|
|
||||||
_.each(tx.vin, function(x){
|
_.each(tx.vin, function(x){
|
||||||
if (x.addr) {
|
if (x.addr) {
|
||||||
x.addr = AddressTranslator.translateOutput(x.addr);
|
x.addr = self.translateResultAddresses(x.addr);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
_.each(tx.vout, function(x){
|
_.each(tx.vout, function(x){
|
||||||
if (x.scriptPubKey && x.scriptPubKey.addresses) {
|
if (x.scriptPubKey && x.scriptPubKey.addresses) {
|
||||||
x.scriptPubKey.addresses = AddressTranslator.translateOutput(x.scriptPubKey.addresses);
|
x.scriptPubKey.addresses = self.translateResultAddresses(x.scriptPubKey.addresses);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -105,7 +103,6 @@ Insight.prototype.getConnectionInfo = function() {
|
||||||
* Retrieve a list of unspent outputs associated with an address or set of addresses
|
* Retrieve a list of unspent outputs associated with an address or set of addresses
|
||||||
*/
|
*/
|
||||||
Insight.prototype.getUtxos = function(addresses, cb) {
|
Insight.prototype.getUtxos = function(addresses, cb) {
|
||||||
console.log('[insight.js.105:addresses:]',addresses); //TODO
|
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
var url = this.url + this.apiPrefix + '/addrs/utxo';
|
var url = this.url + this.apiPrefix + '/addrs/utxo';
|
||||||
|
@ -120,7 +117,8 @@ console.log('[insight.js.105:addresses:]',addresses); //TODO
|
||||||
this.requestQueue.push(args, function(err, res, unspent) {
|
this.requestQueue.push(args, function(err, res, unspent) {
|
||||||
if (err || res.statusCode !== 200) return cb(_parseErr(err, res));
|
if (err || res.statusCode !== 200) return cb(_parseErr(err, res));
|
||||||
|
|
||||||
if (self.shouldTranslateAddresses) {
|
if (self.addressFormat) {
|
||||||
|
|
||||||
_.each(unspent, function(x) {
|
_.each(unspent, function(x) {
|
||||||
x.address = self.translateResultAddresses(x.address);
|
x.address = self.translateResultAddresses(x.address);
|
||||||
});
|
});
|
||||||
|
@ -204,7 +202,7 @@ Insight.prototype.getTransactions = function(addresses, from, to, cb) {
|
||||||
// NOTE: Whenever Insight breaks communication with bitcoind, it returns invalid data but no error code.
|
// NOTE: Whenever Insight breaks communication with bitcoind, it returns invalid data but no error code.
|
||||||
if (!_.isArray(txs) || (txs.length != _.compact(txs).length)) return cb(new Error('Could not retrieve transactions from blockchain. Request was:' + JSON.stringify(args)));
|
if (!_.isArray(txs) || (txs.length != _.compact(txs).length)) return cb(new Error('Could not retrieve transactions from blockchain. Request was:' + JSON.stringify(args)));
|
||||||
|
|
||||||
if (self.shouldTranslateAddresses) {
|
if (self.addressFormat) {
|
||||||
|
|
||||||
_.each(txs, function(tx){
|
_.each(txs, function(tx){
|
||||||
self.translateTx(tx);
|
self.translateTx(tx);
|
||||||
|
|
|
@ -12,6 +12,8 @@ Constants.NETWORKS = {
|
||||||
TESTNET: 'testnet',
|
TESTNET: 'testnet',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Constants.ADDRESS_FORMATS = ['copay', 'cashaddr', 'legacy'];
|
||||||
|
|
||||||
Constants.SCRIPT_TYPES = {
|
Constants.SCRIPT_TYPES = {
|
||||||
P2SH: 'P2SH',
|
P2SH: 'P2SH',
|
||||||
P2PKH: 'P2PKH',
|
P2PKH: 'P2PKH',
|
||||||
|
|
|
@ -1189,6 +1189,12 @@ WalletService.prototype._getUtxosForCurrentWallet = function(opts, cb) {
|
||||||
if (!opts.coin) return next();
|
if (!opts.coin) return next();
|
||||||
|
|
||||||
coin = opts.coin;
|
coin = opts.coin;
|
||||||
|
if (coin != 'bch') return next();
|
||||||
|
|
||||||
|
if (Utils.getAddressCoin(addressStrs[0]) == 'bch')
|
||||||
|
return next();
|
||||||
|
|
||||||
|
// because some old BCH walelts could have legacy addresses?
|
||||||
addressStrs = _.map(addressStrs, function(a) {
|
addressStrs = _.map(addressStrs, function(a) {
|
||||||
return Utils.translateAddress(a, coin);
|
return Utils.translateAddress(a, coin);
|
||||||
});
|
});
|
||||||
|
@ -1245,6 +1251,10 @@ WalletService.prototype._getUtxosForCurrentWallet = function(opts, cb) {
|
||||||
// Needed for the clients to sign UTXOs
|
// Needed for the clients to sign UTXOs
|
||||||
var addressToPath = _.indexBy(allAddresses, 'address');
|
var addressToPath = _.indexBy(allAddresses, 'address');
|
||||||
_.each(allUtxos, function(utxo) {
|
_.each(allUtxos, function(utxo) {
|
||||||
|
if (!addressToPath[utxo.address]) {
|
||||||
|
log.warn('Ignored UTXO!: ' + utxo.address);
|
||||||
|
return;
|
||||||
|
}
|
||||||
utxo.path = addressToPath[utxo.address].path;
|
utxo.path = addressToPath[utxo.address].path;
|
||||||
utxo.publicKeys = addressToPath[utxo.address].publicKeys;
|
utxo.publicKeys = addressToPath[utxo.address].publicKeys;
|
||||||
});
|
});
|
||||||
|
@ -3183,8 +3193,10 @@ WalletService.prototype.getTxHistory = function(opts, cb) {
|
||||||
|
|
||||||
bc.getTransactions(addressStrs, from, to, function(err, rawTxs, total) {
|
bc.getTransactions(addressStrs, from, to, function(err, rawTxs, total) {
|
||||||
if (err) return next(err);
|
if (err) return next(err);
|
||||||
|
console.log('[server.js.3197: RAWtxs:]', JSON.stringify(rawTxs, null, 4)); //TODO
|
||||||
|
|
||||||
txs = self._normalizeTxHistory(rawTxs);
|
txs = self._normalizeTxHistory(rawTxs);
|
||||||
|
console.log('[server.js.3197:txs:]', JSON.stringify(txs, null, 4)); //TODO
|
||||||
totalItems = total;
|
totalItems = total;
|
||||||
return next();
|
return next();
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,87 +0,0 @@
|
||||||
|
|
||||||
var _ = require('lodash');
|
|
||||||
var chai = require('chai');
|
|
||||||
var sinon = require('sinon');
|
|
||||||
var assert = require('assert');
|
|
||||||
var should = chai.should;
|
|
||||||
|
|
||||||
var AddressTranslator = require('../lib/addresstranslator');
|
|
||||||
|
|
||||||
describe('#AddressTranslator', function() {
|
|
||||||
|
|
||||||
it('should translate address from legacy to cashaddr', function() {
|
|
||||||
var res = AddressTranslator.translate('1LqBGSKuX5yYUonjxT5qGfpUsXKYYWeabA', 'cashaddr');
|
|
||||||
assert( res == 'qrvcdmgpk73zyfd8pmdl9wnuld36zh9n4gms8s0u59');
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
it('should translate address from copay to cashaddr', function() {
|
|
||||||
var res = AddressTranslator.translate('HBf8isgS8EXG1r3X6GP89FmooUmiJ42wHS', 'cashaddr');
|
|
||||||
assert( res == 'pqu9c0xe7g0ngz9hzpky64nva9790m64esxxjmcv2k');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should translate address from cashaddr to copay', function() {
|
|
||||||
var res = AddressTranslator.translate('pqu9c0xe7g0ngz9hzpky64nva9790m64esxxjmcv2k', 'copay');
|
|
||||||
assert( res == 'HBf8isgS8EXG1r3X6GP89FmooUmiJ42wHS');
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
it('should keep addresses if not translation needed from cashaddr to copay', function() {
|
|
||||||
var res = AddressTranslator.translate('pqu9c0xe7g0ngz9hzpky64nva9790m64esxxjmcv2k', 'cashaddr');
|
|
||||||
assert( res == 'pqu9c0xe7g0ngz9hzpky64nva9790m64esxxjmcv2k');
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
it('should translate address from legacy to cashaddr (testnet)', function() {
|
|
||||||
var res = AddressTranslator.translate('mnE5ERQ3MKrgAVSLQtBifyjAPE8t6aRfLL', 'cashaddr');
|
|
||||||
assert( res == 'qpye0xdyrukzs548zjag5vas8s8f956g8svs329zm4');
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
it('should translate address from cashaddr to legacy (testnet)', function() {
|
|
||||||
var res = AddressTranslator.translate('qpye0xdyrukzs548zjag5vas8s8f956g8svs329zm4', 'legacy');
|
|
||||||
assert( res == 'mnE5ERQ3MKrgAVSLQtBifyjAPE8t6aRfLL');
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
it.skip('should translate address from bch to btc', function() {
|
|
||||||
var res = AddressTranslator.translateInput('HBf8isgS8EXG1r3X6GP89FmooUmiJ42wHS');
|
|
||||||
assert(res=='36q2G5FMGvJbPgAVEaiyAsFGmpkhPKwk2r');
|
|
||||||
});
|
|
||||||
|
|
||||||
it.skip('should keep the address if there is nothing to do (bch)', function() {
|
|
||||||
var res = AddressTranslator.translate('CcJ4qUfyQ8x5NwhAeCQkrBSWVeXxXghcNz', 'bch');
|
|
||||||
assert(res=='qrvcdmgpk73zyfd8pmdl9wnuld36zh9n4gms8s0u59');
|
|
||||||
});
|
|
||||||
it.skip('should keep the address if there is nothing to do (btc)', function() {
|
|
||||||
var res = AddressTranslator.translate('1LqBGSKuX5yYUonjxT5qGfpUsXKYYWeabA', 'btc');
|
|
||||||
assert(res=='1LqBGSKuX5yYUonjxT5qGfpUsXKYYWeabA');
|
|
||||||
});
|
|
||||||
it.skip('should support 3 params NOK', function() {
|
|
||||||
|
|
||||||
var a;
|
|
||||||
try {
|
|
||||||
var res = AddressTranslator.translate('1LqBGSKuX5yYUonjxT5qGfpUsXKYYWeabA', 'btc', 'bch');
|
|
||||||
} catch (e) {
|
|
||||||
a=e.toString();
|
|
||||||
assert(a.match(/Address has mismatched network type/));
|
|
||||||
};
|
|
||||||
});
|
|
||||||
it.skip('should support 3 params OK', function() {
|
|
||||||
var res = AddressTranslator.translate('HBf8isgS8EXG1r3X6GP89FmooUmiJ42wHS', 'btc', 'bch');
|
|
||||||
assert(res=='36q2G5FMGvJbPgAVEaiyAsFGmpkhPKwk2r');
|
|
||||||
});
|
|
||||||
|
|
||||||
it.skip('should work with arrays also', function() {
|
|
||||||
var res = AddressTranslator.translateOutput(['1LqBGSKuX5yYUonjxT5qGfpUsXKYYWeabA', '37YHiaQnMjy73GS1UpiE8p2Ju6MyrrDw3J', '1DuPdCpGzVX73kBYaAbu5XDNDgE2Lza5Ed']);
|
|
||||||
assert(res[0] == 'qrvcdmgpk73zyfd8pmdl9wnuld36zh9n4gms8s0u59');
|
|
||||||
assert(res[1] == 'ppqz5v08kssnuupe0ckqtw4ss3qt460fcqugqzq2me');
|
|
||||||
assert(res[2] == 'qzxc5pnsfs8pmgfprhzc4l4vzf3zxz8p85nc6kfh8l');
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue