Browse Source

all methods working with coin

feat/estimateFee-limit
Ivan Socolsky 7 years ago
parent
commit
06b63e0311
No known key found for this signature in database GPG Key ID: FAECE6A05FAA4F56
  1. 30
      config.js
  2. 20
      lib/blockchainexplorer.js
  3. 10
      lib/blockchainexplorers/insight.js
  4. 25
      lib/blockchainmonitor.js
  5. 14
      lib/model/txproposal.js
  6. 4
      lib/model/txproposal_legacy.js
  7. 6
      lib/model/wallet.js
  8. 143
      lib/server.js
  9. 6
      test/integration/bcmonitor.js
  10. 1
      test/integration/server.js
  11. 11
      test/model/txproposal.js
  12. 2
      test/storage.js

30
config.js

@ -38,16 +38,28 @@ var config = {
},
},
blockchainExplorerOpts: {
livenet: {
provider: 'insight',
url: 'https://insight.bitpay.com:443',
btc: {
livenet: {
provider: 'insight',
url: 'https://insight.bitpay.com:443',
},
testnet: {
provider: 'insight',
url: 'https://test-insight.bitpay.com:443',
// url: 'http://localhost:3001',
// Multiple servers (in priority order)
// url: ['http://a.b.c', 'https://test-insight.bitpay.com:443'],
},
},
testnet: {
provider: 'insight',
url: 'https://test-insight.bitpay.com:443',
// url: 'http://localhost:3001',
// Multiple servers (in priority order)
// url: ['http://a.b.c', 'https://test-insight.bitpay.com:443'],
bch: {
livenet: {
provider: 'insight',
url: 'https://cashexplorer.bitcoin.com/api/',
},
testnet: {
provider: 'insight',
url: '',
},
},
},
pushNotificationsOpts: {

20
lib/blockchainexplorer.js

@ -6,11 +6,20 @@ var log = require('npmlog');
log.debug = log.verbose;
var Insight = require('./blockchainexplorers/insight');
var Common = require('./common');
var Constants = Common.Constants,
Defaults = Common.Defaults,
Utils = Common.Utils;
var PROVIDERS = {
'insight': {
'livenet': 'https://insight.bitpay.com:443',
'testnet': 'https://test-insight.bitpay.com:443',
'btc': {
'livenet': 'https://insight.bitpay.com:443',
'testnet': 'https://test-insight.bitpay.com:443',
},
'bch': {
'livenet': 'https://insight.bitpay.com:443',
},
},
};
@ -18,16 +27,19 @@ function BlockChainExplorer(opts) {
$.checkArgument(opts);
var provider = opts.provider || 'insight';
var coin = opts.coin || Defaults.COIN;
var network = opts.network || 'livenet';
$.checkState(PROVIDERS[provider], 'Provider ' + provider + ' not supported');
$.checkState(_.contains(_.keys(PROVIDERS[provider]), network), 'Network ' + network + ' not supported by this provider');
$.checkState(_.contains(_.keys(PROVIDERS[provider]), coin), 'Coin ' + coin + ' not supported by this provider');
$.checkState(_.contains(_.keys(PROVIDERS[provider][coin]), network), 'Network ' + network + ' not supported by this provider for coin ' + coin);
var url = opts.url || PROVIDERS[provider][network];
var url = opts.url || PROVIDERS[provider][coin][network];
switch (provider) {
case 'insight':
return new Insight({
coin: coin,
network: network,
url: url,
apiPrefix: opts.apiPrefix,

10
lib/blockchainexplorers/insight.js

@ -6,13 +6,19 @@ var log = require('npmlog');
log.debug = log.verbose;
var io = require('socket.io-client');
var requestList = require('./request-list');
var Common = require('../common');
var Constants = Common.Constants,
Defaults = Common.Defaults,
Utils = Common.Utils;
function Insight(opts) {
$.checkArgument(opts);
$.checkArgument(_.contains(['livenet', 'testnet'], opts.network));
$.checkArgument(Utils.checkValueInCollection(opts.network, Constants.NETWORKS));
$.checkArgument(Utils.checkValueInCollection(opts.coin, Constants.COINS));
$.checkArgument(opts.url);
this.apiPrefix = opts.apiPrefix || '/api';
this.coin = opts.coin || Defaults.COIN;
this.network = opts.network || 'livenet';
this.hosts = opts.url;
this.userAgent = opts.userAgent || 'bws';
@ -39,7 +45,7 @@ Insight.prototype._doRequest = function(args, cb) {
};
Insight.prototype.getConnectionInfo = function() {
return 'Insight (' + this.network + ') @ ' + this.hosts;
return 'Insight (' + this.coin + '/' + this.network + ') @ ' + this.hosts;
};
/**

25
lib/blockchainmonitor.js

@ -30,21 +30,22 @@ BlockchainMonitor.prototype.start = function(opts, cb) {
_.map(_.values(Constants.NETWORKS), function(network) {
var explorer;
if (opts.blockchainExplorers) {
explorer = opts.blockchainExplorers[network];
explorer = opts.blockchainExplorers['btc'][network];
} else {
var config = {}
if (opts.blockchainExplorerOpts && opts.blockchainExplorerOpts[network]) {
config = opts.blockchainExplorerOpts[network];
if (opts.blockchainExplorerOpts && opts.blockchainExplorerOpts['btc'] && opts.blockchainExplorerOpts['btc'][network]) {
config = opts.blockchainExplorerOpts['btc'][network];
}
var explorer = new BlockchainExplorer({
provider: config.provider,
coin: 'btc',
network: network,
url: config.url,
userAgent: WalletService.getServiceVersion(),
});
}
$.checkState(explorer);
self._initExplorer(network, explorer);
self._initExplorer('btc', network, explorer);
self.explorers[network] = explorer;
});
done();
@ -74,7 +75,7 @@ BlockchainMonitor.prototype.start = function(opts, cb) {
});
};
BlockchainMonitor.prototype._initExplorer = function(network, explorer) {
BlockchainMonitor.prototype._initExplorer = function(coin, network, explorer) {
var self = this;
var socket = explorer.initSocket();
@ -87,7 +88,7 @@ BlockchainMonitor.prototype._initExplorer = function(network, explorer) {
log.error('Error connecting to ' + explorer.getConnectionInfo());
});
socket.on('tx', _.bind(self._handleIncomingTx, self));
socket.on('block', _.bind(self._handleNewBlock, self, network));
socket.on('block', _.bind(self._handleNewBlock, self, coin, network));
};
BlockchainMonitor.prototype._handleThirdPartyBroadcasts = function(data, processIt) {
@ -208,7 +209,7 @@ BlockchainMonitor.prototype._handleIncomingTx = function(data) {
this._handleIncomingPayments(data);
};
BlockchainMonitor.prototype._notifyNewBlock = function(network, hash) {
BlockchainMonitor.prototype._notifyNewBlock = function(coin, network, hash) {
var self = this;
log.info('New ' + network + ' block: ' + hash);
@ -217,6 +218,7 @@ BlockchainMonitor.prototype._notifyNewBlock = function(network, hash) {
walletId: network, // use network name as wallet id for global notifications
data: {
hash: hash,
coin: coin,
network: network,
},
});
@ -228,7 +230,7 @@ BlockchainMonitor.prototype._notifyNewBlock = function(network, hash) {
});
};
BlockchainMonitor.prototype._handleTxConfirmations = function(network, hash) {
BlockchainMonitor.prototype._handleTxConfirmations = function(coin, network, hash) {
var self = this;
function processTriggeredSubs(subs, cb) {
@ -244,6 +246,7 @@ BlockchainMonitor.prototype._handleTxConfirmations = function(network, hash) {
creatorId: sub.copayerId,
data: {
txid: sub.txid,
coin: coin,
network: network,
// TODO: amount
},
@ -280,9 +283,9 @@ BlockchainMonitor.prototype._handleTxConfirmations = function(network, hash) {
});
};
BlockchainMonitor.prototype._handleNewBlock = function(network, hash) {
this._notifyNewBlock(network, hash);
this._handleTxConfirmations(network, hash);
BlockchainMonitor.prototype._handleNewBlock = function(coin, network, hash) {
this._notifyNewBlock(coin, network, hash);
this._handleTxConfirmations(coin, network, hash);
};
BlockchainMonitor.prototype._storeAndBroadcastNotification = function(notification, cb) {

14
lib/model/txproposal.js

@ -22,6 +22,9 @@ function TxProposal() {};
TxProposal.create = function(opts) {
opts = opts || {};
$.checkArgument(Utils.checkValueInCollection(opts.coin, Constants.COINS));
$.checkArgument(Utils.checkValueInCollection(opts.network, Constants.NETWORKS));
var x = new TxProposal();
x.version = 3;
@ -31,6 +34,8 @@ TxProposal.create = function(opts) {
x.id = opts.id || Uuid.v4();
x.walletId = opts.walletId;
x.creatorId = opts.creatorId;
x.coin = opts.coin;
x.network = opts.network;
x.message = opts.message;
x.payProUrl = opts.payProUrl;
x.changeAddress = opts.changeAddress;
@ -57,10 +62,6 @@ TxProposal.create = function(opts) {
x.customData = opts.customData;
x.amount = x.getTotalAmount();
try {
x.network = opts.network || Bitcore.Address(x.outputs[0].toAddress).toObject().network;
} catch (ex) {}
$.checkState(Utils.checkValueInCollection(x.network, Constants.NETWORKS));
x.setInputs(opts.inputs);
x.fee = opts.fee;
@ -80,6 +81,7 @@ TxProposal.fromObj = function(obj) {
x.id = obj.id;
x.walletId = obj.walletId;
x.creatorId = obj.creatorId;
x.coin = obj.coin || Defaults.COIN;
x.network = obj.network;
x.outputs = obj.outputs;
x.amount = obj.amount;
@ -221,10 +223,6 @@ TxProposal.prototype.getBitcoreTx = function() {
return t;
};
TxProposal.prototype.getNetworkName = function() {
return this.network;
};
TxProposal.prototype.getRawTx = function() {
var t = this.getBitcoreTx();

4
lib/model/txproposal_legacy.js

@ -93,10 +93,6 @@ TxProposal.prototype.getBitcoreTx = function() {
throwUnsupportedError();
};
TxProposal.prototype.getNetworkName = function() {
return Bitcore.Address(this.changeAddress.address).toObject().network;
};
TxProposal.prototype.getRawTx = function() {
throwUnsupportedError();
};

6
lib/model/wallet.js

@ -23,8 +23,8 @@ Wallet.create = function(opts) {
$.shouldBeNumber(opts.m);
$.shouldBeNumber(opts.n);
$.checkArgument(Utils.checkValueInCollection(opts.network, Constants.NETWORKS));
$.checkArgument(Utils.checkValueInCollection(opts.coin, Constants.COINS));
$.checkArgument(Utils.checkValueInCollection(opts.network, Constants.NETWORKS));
x.version = '1.0.0';
x.createdOn = Math.floor(Date.now() / 1000);
@ -141,10 +141,6 @@ Wallet.prototype.getCopayer = function(copayerId) {
});
};
Wallet.prototype.getNetworkName = function() {
return this.network;
};
Wallet.prototype.isComplete = function() {
return this.status == 'complete';
};

143
lib/server.js

@ -12,6 +12,10 @@ var EmailValidator = require('email-validator');
var Stringify = require('json-stable-stringify');
var Bitcore = require('bitcore-lib');
var Bitcore_ = {
btc: Bitcore,
bch: require('bitcore-lib-cash')
};
var Common = require('./common');
var Utils = Common.Utils;
@ -158,7 +162,7 @@ WalletService.handleIncomingNotification = function(notification, cb) {
if (!notification || notification.type != 'NewBlock') return cb();
WalletService._clearBlockchainHeightCache(notification.data.network);
WalletService._clearBlockchainHeightCache(notification.data.coin, notification.data.network);
return cb();
};
@ -441,10 +445,19 @@ WalletService.prototype.getWalletFromIdentifier = function(opts, cb) {
}
// Is identifier a txid form an incomming tx?
async.detectSeries(_.values(Constants.NETWORKS), function(network, nextNetwork) {
var bc = self._getBlockchainExplorer(network);
var coinNetworkPairs = [];
_.each(_.values(Constants.COINS), function(coin) {
_.each(_.values(Constants.NETWORKS), function(network) {
coinNetworkPairs.push({
coin: coin,
network: network
});
});
});
async.detectSeries(coinNetworkPairs, function(coinNetwork, nextCoinNetwork) {
var bc = self._getBlockchainExplorer(coinNetwork.coin, coinNetwork.network);
bc.getTransaction(opts.identifier, function(err, tx) {
if (err || !tx) return nextNetwork(err, false);
if (err || !tx) return nextCoinNetwork(err, false);
var outputs = _.first(self._normalizeTxHistory(tx)).outputs;
var toAddresses = _.pluck(outputs, 'address');
async.detect(toAddresses, function(addressStr, nextAddress) {
@ -454,7 +467,7 @@ WalletService.prototype.getWalletFromIdentifier = function(opts, cb) {
nextAddress(null, true);
});
}, function(err) {
nextNetwork(err, !!walletId);
nextCoinNetwork(err, !!walletId);
});
});
}, function(err) {
@ -915,7 +928,7 @@ WalletService.prototype._canCreateAddress = function(ignoreMaxGap, cb) {
hasActivity: true
})) return cb(null, true);
var bc = self._getBlockchainExplorer(latestAddresses[0].network);
var bc = self._getBlockchainExplorer(latestAddresses[0].coin, latestAddresses[0].network);
var activityFound = false;
var i = latestAddresses.length;
async.whilst(function() {
@ -1035,27 +1048,28 @@ WalletService.prototype.verifyMessageSignature = function(opts, cb) {
};
WalletService.prototype._getBlockchainExplorer = function(network) {
WalletService.prototype._getBlockchainExplorer = function(coin, network) {
var opts = {};
if (this.blockchainExplorer) return this.blockchainExplorer;
if (this.blockchainExplorerOpts && this.blockchainExplorerOpts[network]) {
opts = this.blockchainExplorerOpts[network];
if (this.blockchainExplorerOpts && this.blockchainExplorerOpts[coin] && this.blockchainExplorerOpts[coin][network]) {
opts = this.blockchainExplorerOpts[coin][network];
}
// TODO: provider should be configurable
opts.provider = 'insight';
opts.coin = coin;
opts.network = network;
opts.userAgent = WalletService.getServiceVersion();
return new BlockchainExplorer(opts);
};
WalletService.prototype._getUtxos = function(addresses, cb) {
WalletService.prototype._getUtxos = function(coin, addresses, cb) {
var self = this;
if (addresses.length == 0) return cb(null, []);
var networkName = Bitcore.Address(addresses[0]).toObject().network;
var bc = self._getBlockchainExplorer(networkName);
var bc = self._getBlockchainExplorer(coin, networkName);
bc.getUtxos(addresses, function(err, utxos) {
if (err) return cb(err);
@ -1079,10 +1093,16 @@ WalletService.prototype._getUtxosForCurrentWallet = function(addresses, cb) {
return utxo.txid + '|' + utxo.vout
};
var allAddresses, allUtxos, utxoIndex;
var coin, allAddresses, allUtxos, utxoIndex;
async.series([
function(next) {
self.getWallet({}, function(err, wallet) {
coin = wallet.coin;
return next();
});
},
function(next) {
if (_.isArray(addresses)) {
allAddresses = addresses;
@ -1097,7 +1117,7 @@ WalletService.prototype._getUtxosForCurrentWallet = function(addresses, cb) {
if (allAddresses.length == 0) return cb(null, []);
var addressStrs = _.pluck(allAddresses, 'address');
self._getUtxos(addressStrs, function(err, utxos) {
self._getUtxos(coin, addressStrs, function(err, utxos) {
if (err) return next(err);
if (utxos.length == 0) return cb(null, []);
@ -1159,6 +1179,7 @@ WalletService.prototype._getUtxosForCurrentWallet = function(addresses, cb) {
/**
* Returns list of UTXOs
* @param {Object} opts
* @param {String} [opts.coin='btc'] (optional)
* @param {Array} opts.addresses (optional) - List of addresses from where to fetch UTXOs.
* @returns {Array} utxos - List of UTXOs.
*/
@ -1167,10 +1188,14 @@ WalletService.prototype.getUtxos = function(opts, cb) {
opts = opts || {};
opts.coin = opts.coin || Defaults.COIN;
if (!Utils.checkValueInCollection(opts.coin, Constants.COINS))
return cb(new ClientError('Invalid coin'));
if (_.isUndefined(opts.addresses)) {
self._getUtxosForCurrentWallet(null, cb);
} else {
self._getUtxos(opts.addresses, cb);
self._getUtxos(opts.coin, opts.addresses, cb);
}
};
@ -1392,6 +1417,7 @@ WalletService.prototype.getSendMaxInfo = function(opts, cb) {
var txp = Model.TxProposal.create({
walletId: self.walletId,
coin: wallet.coin,
network: wallet.network,
walletM: wallet.m,
walletN: wallet.n,
@ -1442,10 +1468,10 @@ WalletService.prototype.getSendMaxInfo = function(opts, cb) {
});
};
WalletService.prototype._sampleFeeLevels = function(network, points, cb) {
WalletService.prototype._sampleFeeLevels = function(coin, network, points, cb) {
var self = this;
var bc = self._getBlockchainExplorer(network);
var bc = self._getBlockchainExplorer(coin, network);
bc.estimateFee(points, function(err, result) {
if (err) {
log.error('Error estimating fee', err);
@ -1473,6 +1499,7 @@ WalletService.prototype._sampleFeeLevels = function(network, points, cb) {
/**
* Returns fee levels for the current state of the network.
* @param {Object} opts
* @param {string} [opts.coin = 'btc'] - The coin to estimate fee levels from.
* @param {string} [opts.network = 'livenet'] - The Bitcoin network to estimate fee levels from.
* @returns {Object} feeLevels - A list of fee levels & associated amount per kB in satoshi.
*/
@ -1509,11 +1536,15 @@ WalletService.prototype.getFeeLevels = function(opts, cb) {
return result;
};
var network = opts.network || 'livenet';
if (network != 'livenet' && network != 'testnet')
opts.coin = opts.coin || Defaults.COIN;
if (!Utils.checkValueInCollection(opts.coin, Constants.COINS))
return cb(new ClientError('Invalid coin'));
opts.network = opts.network || 'livenet';
if (!Utils.checkValueInCollection(opts.network, Constants.NETWORKS))
return cb(new ClientError('Invalid network'));
self._sampleFeeLevels(network, samplePoints(), function(err, feeSamples) {
self._sampleFeeLevels(opts.coin, opts.network, samplePoints(), function(err, feeSamples) {
var values = _.map(Defaults.FEE_LEVELS, function(level) {
var result = {
level: level.name,
@ -1565,10 +1596,10 @@ WalletService.prototype._checkTx = function(txp) {
return ex;
}
if (bitcoreError instanceof Bitcore.errors.Transaction.FeeError)
if (bitcoreError instanceof Bitcore_[txp.coin].errors.Transaction.FeeError)
return Errors.INSUFFICIENT_FUNDS_FOR_FEE;
if (bitcoreError instanceof Bitcore.errors.Transaction.DustOutputs)
if (bitcoreError instanceof Bitcore_[txp.coin].errors.Transaction.DustOutputs)
return Errors.DUST_AMOUNT;
return bitcoreError;
};
@ -1697,7 +1728,7 @@ WalletService.prototype._selectTxInputs = function(txp, utxosToExclude, cb) {
var changeAmount = Math.round(total - txpAmount - fee);
log.debug('Tx change: ', Utils.formatAmountInBtc(changeAmount));
var dustThreshold = Math.max(Defaults.MIN_OUTPUT_AMOUNT, Bitcore.Transaction.DUST_AMOUNT);
var dustThreshold = Math.max(Defaults.MIN_OUTPUT_AMOUNT, Bitcore_[txp.coin].Transaction.DUST_AMOUNT);
if (changeAmount > 0 && changeAmount <= dustThreshold) {
log.debug('Change below dust threshold (' + Utils.formatAmountInBtc(dustThreshold) + '). Incrementing fee to remove change.');
// Remove dust change by incrementing fee
@ -1850,7 +1881,7 @@ WalletService.prototype._canCreateTx = function(cb) {
};
WalletService.prototype._validateOutputs = function(opts, wallet, cb) {
var dustThreshold = Math.max(Defaults.MIN_OUTPUT_AMOUNT, Bitcore.Transaction.DUST_AMOUNT);
var dustThreshold = Math.max(Defaults.MIN_OUTPUT_AMOUNT, Bitcore_[wallet.coin].Transaction.DUST_AMOUNT);
if (_.isEmpty(opts.outputs)) return new ClientError('No outputs were specified');
@ -1868,7 +1899,7 @@ WalletService.prototype._validateOutputs = function(opts, wallet, cb) {
} catch (ex) {
return Errors.INVALID_ADDRESS;
}
if (toAddress.network != wallet.getNetworkName()) {
if (toAddress.network != wallet.network) {
return Errors.INCORRECT_ADDRESS_NETWORK;
}
@ -1957,6 +1988,7 @@ WalletService.prototype._getFeePerKb = function(wallet, opts, cb) {
if (_.isNumber(opts.feePerKb)) return cb(null, opts.feePerKb);
self.getFeeLevels({
coin: wallet.coin,
network: wallet.network
}, function(err, levels) {
if (err) return cb(err);
@ -2066,6 +2098,8 @@ WalletService.prototype.createTx = function(opts, cb) {
id: opts.txProposalId,
walletId: self.walletId,
creatorId: self.copayerId,
coin: wallet.coin,
network: wallet.network,
outputs: opts.outputs,
message: opts.message,
changeAddress: changeAddress,
@ -2330,8 +2364,8 @@ WalletService.prototype.removePendingTx = function(opts, cb) {
});
};
WalletService.prototype._broadcastRawTx = function(network, raw, cb) {
var bc = this._getBlockchainExplorer(network);
WalletService.prototype._broadcastRawTx = function(coin, network, raw, cb) {
var bc = this._getBlockchainExplorer(coin, network);
bc.broadcast(raw, function(err, txid) {
if (err) return cb(err);
return cb(null, txid);
@ -2341,6 +2375,7 @@ WalletService.prototype._broadcastRawTx = function(network, raw, cb) {
/**
* Broadcast a raw transaction.
* @param {Object} opts
* @param {string} [opts.coin = 'btc'] - The coin for this transaction.
* @param {string} [opts.network = 'livenet'] - The Bitcoin network for this transaction.
* @param {string} opts.rawTx - Raw tx data.
*/
@ -2349,17 +2384,21 @@ WalletService.prototype.broadcastRawTx = function(opts, cb) {
if (!checkRequired(opts, ['network', 'rawTx'], cb)) return;
var network = opts.network || 'livenet';
if (network != 'livenet' && network != 'testnet')
opts.coin = opts.coin || Defaults.COIN;
if (!Utils.checkValueInCollection(opts.coin, Constants.COINS))
return cb(new ClientError('Invalid coin'));
opts.network = opts.network || 'livenet';
if (!Utils.checkValueInCollection(opts.network, Constants.NETWORKS))
return cb(new ClientError('Invalid network'));
self._broadcastRawTx(network, opts.rawTx, cb);
self._broadcastRawTx(opts.coin, opts.network, opts.rawTx, cb);
};
WalletService.prototype._checkTxInBlockchain = function(txp, cb) {
if (!txp.txid) return cb();
var bc = this._getBlockchainExplorer(txp.getNetworkName());
var bc = this._getBlockchainExplorer(txp.coin, txp.network);
bc.getTransaction(txp.txid, function(err, tx) {
if (err) return cb(err);
return cb(null, !!tx);
@ -2487,7 +2526,7 @@ WalletService.prototype.broadcastTx = function(opts, cb) {
} catch (ex) {
return cb(ex);
}
self._broadcastRawTx(txp.getNetworkName(), raw, function(err, txid) {
self._broadcastRawTx(wallet.coin, wallet.network, raw, function(err, txid) {
if (err) {
var broadcastErr = err;
// Check if tx already in blockchain
@ -2693,29 +2732,35 @@ WalletService._cachedBlockheight;
WalletService._initBlockchainHeightCache = function() {
if (WalletService._cachedBlockheight) return;
WalletService._cachedBlockheight = {
livenet: {},
testnet: {}
btc: {
livenet: {},
testnet: {}
},
bch: {
livenet: {},
testnet: {}
},
};
};
WalletService._clearBlockchainHeightCache = function(network) {
WalletService._clearBlockchainHeightCache = function(coin, network) {
WalletService._initBlockchainHeightCache();
if (!Utils.checkValueInCollection(network, Constants.NETWORKS)) {
log.error('Incorrect network in new block: ' + network);
log.error('Incorrect network in new block: ' + coin + '/' + network);
return;
}
WalletService._cachedBlockheight[network].current = null;
WalletService._cachedBlockheight[coin][network].current = null;
};
WalletService.prototype._getBlockchainHeight = function(network, cb) {
WalletService.prototype._getBlockchainHeight = function(coin, network, cb) {
var self = this;
var now = Date.now();
WalletService._initBlockchainHeightCache();
var cache = WalletService._cachedBlockheight[network];
var cache = WalletService._cachedBlockheight[coin][network];
function fetchFromBlockchain(cb) {
var bc = self._getBlockchainExplorer(network);
var bc = self._getBlockchainExplorer(coin, network);
bc.getBlockchainHeight(function(err, height) {
if (!err && height > 0) {
cache.current = height;
@ -2883,10 +2928,9 @@ WalletService.prototype.getTxHistory = function(opts, cb) {
});
};
function getNormalizedTxs(addresses, from, to, cb) {
function getNormalizedTxs(wallet, addresses, from, to, cb) {
var txs, fromCache, totalItems;
var useCache = addresses.length >= Defaults.HISTORY_CACHE_ADDRESS_THRESOLD;
var network = Bitcore.Address(addresses[0].address).toObject().network;
async.series([
@ -2907,7 +2951,7 @@ WalletService.prototype.getTxHistory = function(opts, cb) {
if (txs) return next();
var addressStrs = _.pluck(addresses, 'address');
var bc = self._getBlockchainExplorer(network);
var bc = self._getBlockchainExplorer(wallet.coin, wallet.network);
bc.getTransactions(addressStrs, from, to, function(err, rawTxs, total) {
if (err) return next(err);
@ -2934,7 +2978,7 @@ WalletService.prototype.getTxHistory = function(opts, cb) {
if (!txs) return next();
// Fix tx confirmations for cached txs
self._getBlockchainHeight(network, function(err, height) {
self._getBlockchainHeight(wallet.coin, wallet.network, function(err, height) {
if (err || !height) return next(err);
_.each(txs, function(tx) {
if (tx.blockheight >= 0) {
@ -2961,6 +3005,7 @@ WalletService.prototype.getTxHistory = function(opts, cb) {
if (_.isEmpty(unconfirmed)) return cb();
self.getFeeLevels({
coin: wallet.coin,
network: wallet.network
}, function(err, levels) {
if (err) {
@ -2997,7 +3042,7 @@ WalletService.prototype.getTxHistory = function(opts, cb) {
async.waterfall([
function(next) {
getNormalizedTxs(addresses, from, to, next);
getNormalizedTxs(wallet, addresses, from, to, next);
},
function(txs, next) {
// Fetch all proposals in [t - 7 days, t + 1 day]
@ -3056,12 +3101,12 @@ WalletService.prototype.scan = function(opts, cb) {
opts = opts || {};
function checkActivity(address, network, cb) {
var bc = self._getBlockchainExplorer(network);
function checkActivity(wallet, address, cb) {
var bc = self._getBlockchainExplorer(wallet.coin, wallet.network);
bc.getAddressActivity(address, cb);
};
function scanBranch(derivator, cb) {
function scanBranch(wallet, derivator, cb) {
var inactiveCounter = 0;
var allAddresses = [];
var gap = Defaults.SCAN_ADDRESS_GAP;
@ -3070,7 +3115,7 @@ WalletService.prototype.scan = function(opts, cb) {
return inactiveCounter < gap;
}, function(next) {
var address = derivator.derive();
checkActivity(address.address, address.network, function(err, activity) {
checkActivity(wallet, address.address, function(err, activity) {
if (err) return next(err);
allAddresses.push(address);
@ -3114,7 +3159,7 @@ WalletService.prototype.scan = function(opts, cb) {
});
async.eachSeries(derivators, function(derivator, next) {
scanBranch(derivator, function(err, addresses) {
scanBranch(wallet, derivator, function(err, addresses) {
if (err) return next(err);
self.storage.storeAddressAndWallet(wallet, addresses, next);
});

6
test/integration/bcmonitor.js

@ -49,8 +49,10 @@ describe('Blockchain monitor', function() {
messageBroker: server.messageBroker,
storage: storage,
blockchainExplorers: {
'testnet': blockchainExplorer,
'livenet': blockchainExplorer
'btc': {
'testnet': blockchainExplorer,
'livenet': blockchainExplorer
}
},
}, function(err) {
should.not.exist(err);

1
test/integration/server.js

@ -6636,6 +6636,7 @@ describe('Wallet service', function() {
blockchainExplorer.getBlockchainHeight = sinon.stub().callsArgWith(0, null, 2000);
server._notify('NewBlock', {
coin: 'btc',
network: 'livenet',
hash: 'dummy hash',
}, {

11
test/model/txproposal.js

@ -24,6 +24,11 @@ describe('TxProposal', function() {
should.exist(txp);
txp.amount.should.equal(aTXP().amount);
});
it('should default to BTC coin', function() {
var txp = TxProposal.fromObj(aTXP());
should.exist(txp);
txp.coin.should.equal('btc');
});
});
describe('#getBitcoreTx', function() {
@ -108,8 +113,10 @@ var theXPub = 'xpub661MyMwAqRbcFLRkhYzK8eQdoywNHJVsJCMQNDoMks5bZymuMcyDgYfnVQYq2
var theSignatures = ['304402201d210f731fa8cb8473ce49554382ad5d950c963d48b173a0591f13ed8cee10ce022027b30dc3a55c46b1f977a72491d338fc14b6d13a7b1a7c5a35950d8543c1ced6'];
var theRawTx = '0100000001ab069f7073be9b491bb1ad4233a45d2e383082ccc7206df905662d6d8499e66e08000000910047304402201d210f731fa8cb8473ce49554382ad5d950c963d48b173a0591f13ed8cee10ce022027b30dc3a55c46b1f977a72491d338fc14b6d13a7b1a7c5a35950d8543c1ced6014752210319008ffe1b3e208f5ebed8f46495c056763f87b07930a7027a92ee477fb0cb0f2103b5f035af8be40d0db5abb306b7754949ab39032cf99ad177691753b37d10130152aeffffffff0380969800000000001976a91451224bca38efcaa31d5340917c3f3f713b8b20e488ac002d3101000000001976a91451224bca38efcaa31d5340917c3f3f713b8b20e488ac70f62b040000000017a914778192003f0e9e1d865c082179cc3dae5464b03d8700000000';
var aTxpOpts = function(type) {
var aTxpOpts = function() {
var opts = {
coin: 'btc',
network: 'livenet',
message: 'some message'
};
opts.outputs = [{
@ -125,7 +132,7 @@ var aTxpOpts = function(type) {
return opts;
};
var aTXP = function(type) {
var aTXP = function() {
var txp = {
"version": 3,
"createdOn": 1423146231,

2
test/storage.js

@ -150,6 +150,8 @@ describe('Storage', function() {
proposals = _.map(_.range(4), function(i) {
var tx = Model.TxProposal.create({
walletId: '123',
coin: 'btc',
network: 'livenet',
outputs: [{
toAddress: '18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7',
amount: i + 100,

Loading…
Cancel
Save