flosight-api/app/controllers/addresses.js
2015-07-17 18:35:44 -03:00

285 lines
6.5 KiB
JavaScript

'use strict';
/**
* Module dependencies.
*/
var _ = require('lodash');
var Address = require('../models/Address');
var common = require('./common');
var async = require('async');
var MAX_BATCH_SIZE = 100;
var RPC_CONCURRENCY = 5;
var tDb = require('../../lib/TransactionDb').default();
var checkSync = function(req, res) {
if (req.historicSync) {
var i = req.historicSync.info()
if (i.status !== 'finished') {
common.notReady(req, res, i.syncPercentage);
return false;
}
}
return true;
};
var getAddr = function(req, res, next) {
var a;
try {
var addr = req.param('addr');
a = new Address(addr);
} catch (e) {
common.handleErrors({
message: 'Invalid address:' + e.message,
code: 1
}, res, next);
return null;
}
return a;
};
var getAddrs = function(req, res, next) {
var as = [];
try {
var addrStrs = req.param('addrs');
var s = addrStrs.split(',');
if (s.length === 0) return as;
for (var i = 0; i < s.length; i++) {
var a = new Address(s[i]);
as.push(a);
}
} catch (e) {
common.handleErrors({
message: 'Invalid addrs param:' + e.message,
code: 1
}, res, next);
return null;
}
return as;
};
exports.show = function(req, res, next) {
if (!checkSync(req, res)) return;
var a = getAddr(req, res, next);
if (a) {
a.update(function(err) {
if (err) {
return common.handleErrors(err, res);
} else {
return res.jsonp(a.getObj());
}
}, {
txLimit: req.query.noTxList ? 0 : -1,
ignoreCache: req.param('noCache')
});
}
};
exports.utxo = function(req, res, next) {
if (!checkSync(req, res)) return;
var a = getAddr(req, res, next);
if (a) {
a.update(function(err) {
if (err)
return common.handleErrors(err, res);
else {
return res.jsonp(a.unspent);
}
}, {
onlyUnspent: 1,
ignoreCache: req.param('noCache')
});
}
};
exports.multiutxo = function(req, res, next) {
if (!checkSync(req, res)) return;
var as = getAddrs(req, res, next);
if (as) {
var utxos = [];
async.eachLimit(as, RPC_CONCURRENCY, function(a, callback) {
a.update(function(err) {
if (err) callback(err);
utxos = utxos.concat(a.unspent);
callback();
}, {
onlyUnspent: 1,
ignoreCache: req.param('noCache')
});
}, function(err) { // finished callback
if (err) return common.handleErrors(err, res);
res.jsonp(utxos);
});
}
};
exports.multitxs = function(req, res, next) {
if (!checkSync(req, res)) return;
function processTxs(txs, from, to, cb) {
txs = _.uniq(_.flatten(txs), 'txid');
var nbTxs = txs.length;
if (_.isUndefined(from) && _.isUndefined(to)) {
from = 0;
to = MAX_BATCH_SIZE;
}
if (!_.isUndefined(from) && _.isUndefined(to))
to = from + MAX_BATCH_SIZE;
if (!_.isUndefined(from) && !_.isUndefined(to) && to - from > MAX_BATCH_SIZE)
to = from + MAX_BATCH_SIZE;
if (from < 0) from = 0;
if (to < 0) to = 0;
if (from > nbTxs) from = nbTxs;
if (to > nbTxs) to = nbTxs;
txs.sort(function(a, b) {
var b = (b.firstSeenTs || b.ts)+ b.txid;
var a = (a.firstSeenTs || a.ts)+ a.txid;
if (a > b) return -1;
if (a < b) return 1;
return 0;
});
txs = txs.slice(from, to);
var txIndex = {};
_.each(txs, function(tx) {
txIndex[tx.txid] = tx;
});
async.eachLimit(txs, RPC_CONCURRENCY, function(tx2, callback) {
tDb.fromIdWithInfo(tx2.txid, function(err, tx) {
if (err) {
console.log(err);
return common.handleErrors(err, res);
}
if (tx && tx.info) {
if (tx2.firstSeenTs)
tx.info.firstSeenTs = tx2.firstSeenTs;
txIndex[tx.txid].info = tx.info;
} else {
// TX no longer available
txIndex[tx2.txid].info = {
txid: tx2.txid,
possibleDoubleSpend: true,
firstSeenTs: tx2.firstSeenTs,
};
}
callback();
});
}, function(err) {
if (err) return cb(err);
// It could be that a txid is stored at an address but it is
// no longer at bitcoind (for example a double spend)
var transactions = _.compact(_.pluck(txs, 'info'));
transactions = {
totalItems: nbTxs,
from: +from,
to: +to,
items: transactions,
};
return cb(null, transactions);
});
};
var from = req.param('from');
var to = req.param('to');
var as = getAddrs(req, res, next);
if (as) {
var txs = [];
async.eachLimit(as, RPC_CONCURRENCY, function(a, callback) {
a.update(function(err) {
if (err) callback(err);
txs.push(a.transactions);
callback();
}, {
ignoreCache: req.param('noCache'),
includeTxInfo: true,
});
}, function(err) { // finished callback
if (err) return common.handleErrors(err, res);
processTxs(txs, from, to, function(err, transactions) {
if (err) return common.handleErrors(err, res);
res.jsonp(transactions);
});
});
}
};
exports.balance = function(req, res, next) {
if (!checkSync(req, res)) return;
var a = getAddr(req, res, next);
if (a)
a.update(function(err) {
if (err) {
return common.handleErrors(err, res);
} else {
return res.jsonp(a.balanceSat);
}
}, {
ignoreCache: req.param('noCache')
});
};
exports.totalReceived = function(req, res, next) {
if (!checkSync(req, res)) return;
var a = getAddr(req, res, next);
if (a)
a.update(function(err) {
if (err) {
return common.handleErrors(err, res);
} else {
return res.jsonp(a.totalReceivedSat);
}
}, {
ignoreCache: req.param('noCache')
});
};
exports.totalSent = function(req, res, next) {
if (!checkSync(req, res)) return;
var a = getAddr(req, res, next);
if (a)
a.update(function(err) {
if (err) {
return common.handleErrors(err, res);
} else {
return res.jsonp(a.totalSentSat);
}
}, {
ignoreCache: req.param('noCache')
});
};
exports.unconfirmedBalance = function(req, res, next) {
if (!checkSync(req, res)) return;
var a = getAddr(req, res, next);
if (a)
a.update(function(err) {
if (err) {
return common.handleErrors(err, res);
} else {
return res.jsonp(a.unconfirmedBalanceSat);
}
}, {
ignoreCache: req.param('noCache')
});
};