bitcore-wallet-service/lib/blockchainexplorers/insight.js

232 lines
5.8 KiB
JavaScript
Raw Permalink Normal View History

'use strict';
var _ = require('lodash');
var $ = require('preconditions').singleton();
var log = require('npmlog');
log.debug = log.verbose;
var io = require('socket.io-client');
2016-02-16 19:15:43 -08:00
var requestList = require('./request-list');
2017-08-25 12:04:14 -07:00
var Common = require('../common');
var Constants = Common.Constants,
Defaults = Common.Defaults,
Utils = Common.Utils;
function Insight(opts) {
$.checkArgument(opts);
2017-08-25 12:04:14 -07:00
$.checkArgument(Utils.checkValueInCollection(opts.network, Constants.NETWORKS));
$.checkArgument(Utils.checkValueInCollection(opts.coin, Constants.COINS));
$.checkArgument(opts.url);
this.apiPrefix = opts.apiPrefix || '/api';
2017-08-25 12:04:14 -07:00
this.coin = opts.coin || Defaults.COIN;
this.network = opts.network || 'livenet';
2016-02-16 12:09:53 -08:00
this.hosts = opts.url;
2016-04-07 10:52:23 -07:00
this.userAgent = opts.userAgent || 'bws';
};
2015-09-07 20:57:59 -07:00
var _parseErr = function(err, res) {
if (err) {
2015-09-10 11:47:08 -07:00
log.warn('Insight error: ', err);
2015-09-07 20:57:59 -07:00
return "Insight Error";
}
log.warn("Insight " + res.request.href + " Returned Status: " + res.statusCode);
2015-09-07 20:57:59 -07:00
return "Error querying the blockchain";
};
2016-04-07 10:52:23 -07:00
Insight.prototype._doRequest = function(args, cb) {
var opts = {
hosts: this.hosts,
headers: {
'User-Agent': this.userAgent,
}
};
2017-11-01 07:43:47 -07:00
if (log.level == 'verbose') {
var s = JSON.stringify(args);
if ( s.length > 100 )
s= s.substr(0,100) + '...';
log.debug('', 'Insight Q: %s', s);
}
2016-04-07 10:52:23 -07:00
requestList(_.defaults(args, opts), cb);
};
Insight.prototype.getConnectionInfo = function() {
2017-08-25 12:04:14 -07:00
return 'Insight (' + this.coin + '/' + this.network + ') @ ' + this.hosts;
};
/**
* Retrieve a list of unspent outputs associated with an address or set of addresses
*/
Insight.prototype.getUtxos = function(addresses, cb) {
var url = this.url + this.apiPrefix + '/addrs/utxo';
var args = {
method: 'POST',
2016-02-16 12:09:53 -08:00
path: this.apiPrefix + '/addrs/utxo',
json: {
2017-09-03 07:16:02 -07:00
addrs: _.uniq([].concat(addresses)).join(',')
},
};
2017-11-01 07:43:47 -07:00
log.info('','Querying utxos: %s addrs', addresses.length);
2016-04-07 10:52:23 -07:00
this._doRequest(args, function(err, res, unspent) {
if (err || res.statusCode !== 200) return cb(_parseErr(err, res));
return cb(null, unspent);
});
};
/**
* Broadcast a transaction to the bitcoin network
*/
Insight.prototype.broadcast = function(rawTx, cb) {
var args = {
method: 'POST',
2016-02-16 12:09:53 -08:00
path: this.apiPrefix + '/tx/send',
json: {
rawtx: rawTx
},
};
2016-04-07 10:52:23 -07:00
this._doRequest(args, function(err, res, body) {
if (err || res.statusCode !== 200) return cb(_parseErr(err, res));
return cb(null, body ? body.txid : null);
});
};
Insight.prototype.getTransaction = function(txid, cb) {
var args = {
method: 'GET',
path: this.apiPrefix + '/tx/' + txid,
2015-10-26 15:30:57 -07:00
json: true,
};
2016-04-07 10:52:23 -07:00
this._doRequest(args, function(err, res, tx) {
if (res && res.statusCode == 404) return cb();
if (err || res.statusCode !== 200)
return cb(_parseErr(err, res));
return cb(null, tx);
});
};
Insight.prototype.getTransactions = function(addresses, from, to, cb) {
var qs = [];
2016-07-26 17:10:49 -07:00
var total;
if (_.isNumber(from)) qs.push('from=' + from);
if (_.isNumber(to)) qs.push('to=' + to);
2016-07-21 13:29:07 -07:00
// Trim output
qs.push('noAsm=1');
qs.push('noScriptSig=1');
qs.push('noSpent=1');
var args = {
method: 'POST',
2016-02-16 12:09:53 -08:00
path: this.apiPrefix + '/addrs/txs' + (qs.length > 0 ? '?' + qs.join('&') : ''),
json: {
2017-09-03 07:16:02 -07:00
addrs: _.uniq([].concat(addresses)).join(',')
},
timeout: 120000,
};
2017-11-01 07:43:47 -07:00
log.info('','Querying addresses: %s addrs', addresses.length);
2016-04-07 10:52:23 -07:00
this._doRequest(args, function(err, res, txs) {
if (err || res.statusCode !== 200) return cb(_parseErr(err, res));
2016-07-26 17:10:49 -07:00
if (_.isObject(txs)) {
if (txs.totalItems)
total = txs.totalItems;
if (txs.items)
2016-07-26 17:10:49 -07:00
txs = txs.items;
}
// 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)));
2016-07-26 17:10:49 -07:00
return cb(null, txs, total);
});
};
Insight.prototype.getAddressActivity = function(address, cb) {
var self = this;
var args = {
method: 'GET',
2016-02-16 12:09:53 -08:00
path: self.apiPrefix + '/addr/' + address,
json: true,
};
2016-04-07 10:52:23 -07:00
this._doRequest(args, function(err, res, result) {
if (res && res.statusCode == 404) return cb();
if (err || res.statusCode !== 200)
return cb(_parseErr(err, res));
var nbTxs = result.unconfirmedTxApperances + result.txApperances;
return cb(null, nbTxs > 0);
});
};
2015-07-15 18:42:05 -07:00
Insight.prototype.estimateFee = function(nbBlocks, cb) {
2016-02-16 12:09:53 -08:00
var path = this.apiPrefix + '/utils/estimatefee';
2015-07-15 18:42:05 -07:00
if (nbBlocks) {
2016-02-16 12:09:53 -08:00
path += '?nbBlocks=' + [].concat(nbBlocks).join(',');
2015-07-15 18:42:05 -07:00
}
var args = {
method: 'GET',
2016-02-16 12:09:53 -08:00
path: path,
2015-07-17 06:32:48 -07:00
json: true,
2015-07-15 18:42:05 -07:00
};
2016-04-07 10:52:23 -07:00
this._doRequest(args, function(err, res, body) {
if (err || res.statusCode !== 200) return cb(_parseErr(err, res));
2015-07-17 06:32:48 -07:00
return cb(null, body);
2015-07-15 18:42:05 -07:00
});
};
2016-07-29 12:24:03 -07:00
Insight.prototype.getBlockchainHeight = function(cb) {
var path = this.apiPrefix + '/sync';
var args = {
method: 'GET',
path: path,
json: true,
};
this._doRequest(args, function(err, res, body) {
if (err || res.statusCode !== 200) return cb(_parseErr(err, res));
return cb(null, body.blockChainHeight);
});
};
Insight.prototype.getTxidsInBlock = function(blockHash, cb) {
var self = this;
var args = {
method: 'GET',
2017-05-22 10:22:55 -07:00
path: this.apiPrefix + '/block/' + blockHash,
json: true,
};
this._doRequest(args, function(err, res, body) {
if (err || res.statusCode !== 200) return cb(_parseErr(err, res));
return cb(null, body.tx);
});
};
Insight.prototype.initSocket = function() {
2016-02-16 12:09:53 -08:00
// sockets always use the first server on the pull
2016-04-07 08:47:34 -07:00
var socket = io.connect(_.first([].concat(this.hosts)), {
'reconnection': true,
});
return socket;
};
module.exports = Insight;