diff --git a/lib/common/utils.js b/lib/common/utils.js index 2d4d807..cd4413f 100644 --- a/lib/common/utils.js +++ b/lib/common/utils.js @@ -7,6 +7,13 @@ var encoding = bitcore.encoding; var secp256k1 = require('secp256k1'); var Utils = {}; +var Bitcore = require('bitcore-lib'); +var Bitcore_ = { + btc: Bitcore, + bch: require('bitcore-lib-cash') +}; + + Utils.getMissingFields = function(obj, args) { args = [].concat(args); @@ -186,4 +193,30 @@ Utils.checkValueInCollection = function(value, collection) { return _.contains(_.values(collection), value); }; + +Utils.getAddressCoin = function(address) { + try { + new Bitcore_['btc'].Address(address); + return 'btc'; + } catch (e) { + try { + new Bitcore_['bch'].Address(address); + return 'bch'; + } catch (e) { + return; + } + } +}; + +Utils.translateAddress = function(address, coin) { + var origCoin = Utils.getAddressCoin(address); + var origAddress = new Bitcore_[origCoin].Address(address); + var origObj = origAddress.toObject(); + + var result = Bitcore_[coin].Address.fromObject(origObj) + return result.toString(); +}; + + + module.exports = Utils; diff --git a/lib/server.js b/lib/server.js index d675439..d5a3578 100644 --- a/lib/server.js +++ b/lib/server.js @@ -1107,20 +1107,16 @@ WalletService.prototype._getUtxosForCurrentWallet = function(opts, cb) { return utxo.txid + '|' + utxo.vout }; - var coin, allAddresses, allUtxos, utxoIndex; + var coin, allAddresses, allUtxos, utxoIndex, addressStrs; async.series([ - function(next) { - if (opts.coin) { - coin = opts.coin; - next(); - } else { - self.getWallet({}, function(err, wallet) { - coin = wallet.coin; - return next(); - }); - } + self.getWallet({}, function(err, wallet) { + if (err) return next(err); + + coin = wallet.coin; + return next(); + }); }, function(next) { if (_.isArray(opts.addresses)) { @@ -1129,13 +1125,23 @@ WalletService.prototype._getUtxosForCurrentWallet = function(opts, cb) { } self.storage.fetchAddresses(self.walletId, function(err, addresses) { allAddresses = addresses; + if (allAddresses.length == 0) return cb(null, []); + return next(); }); }, function(next) { - if (allAddresses.length == 0) return cb(null, []); + addressStrs = _.pluck(allAddresses, 'address'); + if (!opts.coin) return next(); + + coin = opts.coin; + addressStrs = _.map(addressStrs, function(a) { + return Utils.translateAddress(a, coin); + }); + next(); + }, + function(next) { - var addressStrs = _.pluck(allAddresses, 'address'); self._getUtxos(coin, addressStrs, function(err, utxos) { if (err) return next(err); diff --git a/package.json b/package.json index c4cea08..c8d1d4b 100644 --- a/package.json +++ b/package.json @@ -71,14 +71,18 @@ "coveralls": "./node_modules/.bin/istanbul cover ./node_modules/mocha/bin/_mocha --report lcovonly -- -R spec && cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js && rm -rf ./coverage" }, "bitcoreNode": "./bitcorenode", - "contributors": [{ - "name": "Braydon Fuller", - "email": "braydon@bitpay.com" - }, { - "name": "Ivan Socolsky", - "email": "ivan@bitpay.com" - }, { - "name": "Matias Alejo Garcia", - "email": "ematiu@gmail.com" - }] + "contributors": [ + { + "name": "Braydon Fuller", + "email": "braydon@bitpay.com" + }, + { + "name": "Ivan Socolsky", + "email": "ivan@bitpay.com" + }, + { + "name": "Matias Alejo Garcia", + "email": "ematiu@gmail.com" + } + ] } diff --git a/test/integration/server.js b/test/integration/server.js index 4058608..92ea2c4 100644 --- a/test/integration/server.js +++ b/test/integration/server.js @@ -1222,6 +1222,7 @@ describe('Wallet service', function() { address.network.should.equal('livenet'); address.address.should.equal('36q2G5FMGvJbPgAVEaiyAsFGmpkhPKwk2r'); address.isChange.should.be.false; + address.coin.should.equal('btc'); address.path.should.equal('m/0/0'); address.type.should.equal('P2SH'); server.getNotifications({}, function(err, notifications) { @@ -1271,6 +1272,76 @@ describe('Wallet service', function() { }); }); + + describe('shared wallets (BIP44/BCH)', function() { + beforeEach(function(done) { + helpers.createAndJoinWallet(2, 2, { + coin: 'bch' + }, function(s, w) { + server = s; + wallet = w; + done(); + }); + }); + + it('should create address', function(done) { + server.createAddress({}, function(err, address) { + should.not.exist(err); + should.exist(address); + address.walletId.should.equal(wallet.id); + address.network.should.equal('livenet'); + address.address.should.equal('HBf8isgS8EXG1r3X6GP89FmooUmiJ42wHS'); + address.isChange.should.be.false; + address.path.should.equal('m/0/0'); + address.type.should.equal('P2SH'); + address.coin.should.equal('bch'); + server.getNotifications({}, function(err, notifications) { + should.not.exist(err); + var notif = _.find(notifications, { + type: 'NewAddress' + }); + should.exist(notif); + notif.data.address.should.equal(address.address); + done(); + }); + }); + }); + + it('should create many addresses on simultaneous requests', function(done) { + var N = 5; + async.mapSeries(_.range(N), function(i, cb) { + server.createAddress({}, cb); + }, function(err, addresses) { + addresses.length.should.equal(N); + _.each(_.range(N), function(i) { + addresses[i].path.should.equal('m/0/' + i); + }); + // No two identical addresses + _.uniq(_.pluck(addresses, 'address')).length.should.equal(N); + done(); + }); + }); + + it('should not create address if unable to store it', function(done) { + sinon.stub(server.storage, 'storeAddressAndWallet').yields('dummy error'); + server.createAddress({}, function(err, address) { + should.exist(err); + should.not.exist(address); + + server.getMainAddresses({}, function(err, addresses) { + addresses.length.should.equal(0); + + server.storage.storeAddressAndWallet.restore(); + server.createAddress({}, function(err, address) { + should.not.exist(err); + should.exist(address); + done(); + }); + }); + }); + }); + }); + describe('1-of-1 (BIP44 & P2PKH)', function() { beforeEach(function(done) { helpers.createAndJoinWallet(1, 1, function(s, w) { @@ -7672,7 +7743,7 @@ describe('Wallet service', function() { address.walletId.should.equal(wallet.bch.id); address.coin.should.equal('bch'); address.network.should.equal('livenet'); - address.address.should.equal('1L3z9LPd861FWQhf3vDn89Fnc9dkdBo2CG'); + address.address.should.equal('CbWsiNjh18ynQYc5jfYhhespEGrAaW8YUq'); server.btc.getMainAddresses({}, function(err, addresses) { should.not.exist(err); addresses.length.should.equal(1); @@ -7684,7 +7755,7 @@ describe('Wallet service', function() { addresses.length.should.equal(1); addresses[0].coin.should.equal('bch'); addresses[0].walletId.should.equal(wallet.bch.id); - addresses[0].address.should.equal('1L3z9LPd861FWQhf3vDn89Fnc9dkdBo2CG'); + addresses[0].address.should.equal('CbWsiNjh18ynQYc5jfYhhespEGrAaW8YUq'); done(); }); }); diff --git a/test/utils.js b/test/utils.js index f945aa9..33e3aa0 100644 --- a/test/utils.js +++ b/test/utils.js @@ -131,4 +131,42 @@ describe('Utils', function() { }); }); }); + + describe('#getAddressCoin', function() { + it('should identify btc as coin for 1LqBGSKuX5yYUonjxT5qGfpUsXKYYWeabA', function() { + Utils.getAddressCoin('1LqBGSKuX5yYUonjxT5qGfpUsXKYYWeabA').should.equal('btc'); + }); + it('should identify bch as coin for CcJ4qUfyQ8x5NwhAeCQkrBSWVeXxXghcNz', function() { + Utils.getAddressCoin('CcJ4qUfyQ8x5NwhAeCQkrBSWVeXxXghcNz').should.equal('bch'); + }); + it('should return null for 1L', function() { + should.not.exist(Utils.getAddressCoin('1L')); + }); + }); + + + describe('#translateAddress', function() { + it('should translate address from btc to bch', function() { + var res = Utils.translateAddress('1LqBGSKuX5yYUonjxT5qGfpUsXKYYWeabA', 'bch'); + res.should.equal('CcJ4qUfyQ8x5NwhAeCQkrBSWVeXxXghcNz'); + }); + it('should translate address from bch to btc', function() { + var res = Utils.translateAddress('HBf8isgS8EXG1r3X6GP89FmooUmiJ42wHS', 'btc'); + res.should.equal('36q2G5FMGvJbPgAVEaiyAsFGmpkhPKwk2r'); + }); + + it('should keep the address if there is nothing to do (bch)', function() { + var res = Utils.translateAddress('CcJ4qUfyQ8x5NwhAeCQkrBSWVeXxXghcNz', 'bch'); + res.should.equal('CcJ4qUfyQ8x5NwhAeCQkrBSWVeXxXghcNz'); + }); + it('should keep the address if there is nothing to do (btc)', function() { + var res = Utils.translateAddress('1LqBGSKuX5yYUonjxT5qGfpUsXKYYWeabA', 'btc'); + should.exist(res); + res.should.equal('1LqBGSKuX5yYUonjxT5qGfpUsXKYYWeabA'); + }); + + + + }); + });