From 3cd3c4f6572bacbb5f19f0115d17f356f51dde44 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Wed, 22 Jul 2015 16:11:31 +0200 Subject: [PATCH 1/3] Added batch query support, plus a bit more information to some queries --- app/controllers/blocks.js | 63 ++++++++++++++++++++++++++++----- app/controllers/common.js | 20 +++++++++++ app/controllers/transactions.js | 26 +++++++------- config/routes.js | 12 +++++-- lib/BlockDb.js | 1 + 5 files changed, 98 insertions(+), 24 deletions(-) diff --git a/app/controllers/blocks.js b/app/controllers/blocks.js index 24788120a..0b94ba233 100644 --- a/app/controllers/blocks.js +++ b/app/controllers/blocks.js @@ -4,6 +4,7 @@ * Module dependencies. */ var common = require('./common'); +var multi = common.multi; var async = require('async'); var bdb = require('../../lib/BlockDb').default(); var tdb = require('../../lib/TransactionDb').default(); @@ -11,19 +12,34 @@ var tdb = require('../../lib/TransactionDb').default(); /** * Find block by hash ... */ -exports.block = function(req, res, next, hash) { +exports.block = multi(function(hash, cb) { bdb.fromHashWithInfo(hash, function(err, block) { if (err || !block) return common.handleErrors(err, res, next); else { tdb.getPoolInfo(block.info.tx[0], function(info) { block.info.poolInfo = info; - req.block = block.info; - return next(); + cb(null, block.info); }); } }); -}; +}, 'block'); + + +/** + * Find block header by hash ... + */ +exports.blockHeader = multi(function(hash, cb) { + bdb.fromHashWithInfo(hash, function(err, block) { + if (err || !block) + return common.handleErrors(err, res, next); + else { + delete block.info.tx; + cb(null, block.info); + } + }); +}, 'block'); + /** @@ -35,19 +51,50 @@ exports.show = function(req, res) { } }; +/** + * Show block hash + */ +exports.showBlockHash = function(req, res) { + if (req.blockHash) { + res.jsonp(req.blockHash); + } +}; + /** * Show block by Height */ -exports.blockindex = function(req, res, next, height) { +exports.blockIndex = multi(function(height, cb) { bdb.blockIndex(height, function(err, hashStr) { if (err) { console.log(err); - res.status(400).send('Bad Request'); // TODO + cb('Bad Request'); // TODO } else { - res.jsonp(hashStr); + cb(null, hashStr); } }); -}; +}, 'blockHash'); + + +/** + * Show block header by Height + */ +exports.blockHeaderByIndex = multi(function(height, cb) { + bdb.blockIndex(height, function(err, hashStr) { + if (err) { + console.log(err); + cb('Bad Request'); + } else { + bdb.fromHashWithInfo(hashStr.blockHash, function(err, block) { + if (err || !block) + cb(err); + else { + delete block.info.tx; + cb(null, block.info); + } + }); + } + }); +}, 'block'); var getBlock = function(blockhash, cb) { bdb.fromHashWithInfo(blockhash, function(err, block) { diff --git a/app/controllers/common.js b/app/controllers/common.js index d7fb9af67..c74c56804 100644 --- a/app/controllers/common.js +++ b/app/controllers/common.js @@ -1,5 +1,7 @@ 'use strict'; +var async = require('async'); + exports.notReady = function (err, res, p) { res.status(503).send('Server not yet ready. Sync Percentage:' + p); }; @@ -17,3 +19,21 @@ exports.handleErrors = function (err, res) { res.status(404).send('Not found'); } }; + +exports.multi = function(f, outkey) { + return function(req, res, next, inputdata) { + var inputs; + if (inputdata.indexOf(',') >= 0) { + inputs = inputdata.split(','); + } + else inputs = [inputdata]; + async.mapSeries(inputs, f, function(err, results) { + if (err) + return exports.handleErrors(err, res); + req[outkey] = results; + if (req[outkey].length == 1) + req[outkey] = req[outkey][0] + return next(); + }); + }; +} diff --git a/app/controllers/transactions.js b/app/controllers/transactions.js index bb1621918..e5b1b326f 100644 --- a/app/controllers/transactions.js +++ b/app/controllers/transactions.js @@ -6,6 +6,7 @@ var Address = require('../models/Address'); var async = require('async'); var common = require('./common'); +var multi = common.multi; var util = require('util'); var Rpc = require('../../lib/Rpc'); @@ -54,22 +55,19 @@ exports.rawTransaction = function (req, res, next, txid) { /** * Find transaction by hash ... */ -exports.transaction = function(req, res, next, txid) { - - tDb.fromIdWithInfo(txid, function(err, tx) { - if (err || ! tx) - return common.handleErrors(err, res); - - bdb.fillVinConfirmations(tx.info, function(err) { - if (err) - return common.handleErrors(err, res); +exports.transaction = multi(function(txid, cb) { + tDb.fromIdWithInfo(txid, function(err, tx) { + if (err || ! tx) + return cb(err); + + bdb.fillVinConfirmations(tx.info, function(err) { + if (err) + return cb(err); + return cb(null, tx.info); + }); - req.transaction = tx.info; - return next(); }); - - }); -}; +}, 'transaction'); /** diff --git a/config/routes.js b/config/routes.js index d4ba9521f..2caceca35 100644 --- a/config/routes.js +++ b/config/routes.js @@ -17,8 +17,14 @@ module.exports = function(app) { app.get(apiPrefix + '/block/:blockHash', blocks.show); app.param('blockHash', blocks.block); - app.get(apiPrefix + '/block-index/:height', blocks.blockindex); - app.param('height', blocks.blockindex); + app.get(apiPrefix + '/blockheader/:blockHeaderHash', blocks.show); + app.param('blockHeaderHash', blocks.blockHeader); + + app.get(apiPrefix + '/block-index/:height', blocks.showBlockHash); + app.param('height', blocks.blockIndex); + + app.get(apiPrefix + '/blockheader-by-index/:headerHeight', blocks.show); + app.param('headerHeight', blocks.blockHeaderByIndex); // Transaction routes var transactions = require('../app/controllers/transactions'); @@ -26,6 +32,8 @@ module.exports = function(app) { app.param('txid', transactions.transaction); app.get(apiPrefix + '/txs', transactions.list); app.post(apiPrefix + '/tx/send', transactions.send); + app.get(apiPrefix + '/multitx/:txids', transactions.show); + app.param('txids', transactions.transaction); // Raw Routes app.get(apiPrefix + '/rawtx/:txid', transactions.showRaw); diff --git a/lib/BlockDb.js b/lib/BlockDb.js index 41c5b86ea..1c50eb461 100644 --- a/lib/BlockDb.js +++ b/lib/BlockDb.js @@ -382,6 +382,7 @@ BlockDb.prototype._fillConfirmationsOneVin = function(o, chainHeight, cb) { o.confirmations = chainHeight - height + 1; } o.unconfirmedInput = ! o.isConfirmed; + o.confirmedIn = height; return cb(); }); }; From 3d9e6726b074590beb6187d1ef815e58d781f0b7 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Thu, 23 Jul 2015 20:18:26 +0200 Subject: [PATCH 2/3] Some fixes and additions to transaction data --- app/controllers/transactions.js | 9 ++++----- config/routes.js | 2 ++ lib/BlockDb.js | 1 + lib/TransactionDb.js | 23 ++++++++++++++++------- 4 files changed, 23 insertions(+), 12 deletions(-) diff --git a/app/controllers/transactions.js b/app/controllers/transactions.js index e5b1b326f..d1ff6482a 100644 --- a/app/controllers/transactions.js +++ b/app/controllers/transactions.js @@ -41,16 +41,15 @@ exports.send = function(req, res) { }); }; -exports.rawTransaction = function (req, res, next, txid) { +exports.rawTransaction = multi(function(txid, cb) { bitcoreRpc.getRawTransaction(txid, function (err, transaction) { if (err || !transaction) - return common.handleErrors(err, res); + return cb(err); else { - req.rawTransaction = { 'rawtx': transaction.result }; - return next(); + return cb(null, transaction.result); } }); -}; +}, 'rawTransaction'); /** * Find transaction by hash ... diff --git a/config/routes.js b/config/routes.js index 2caceca35..5d152d67e 100644 --- a/config/routes.js +++ b/config/routes.js @@ -38,6 +38,8 @@ module.exports = function(app) { // Raw Routes app.get(apiPrefix + '/rawtx/:txid', transactions.showRaw); app.param('txid', transactions.rawTransaction); + app.get(apiPrefix + '/rawmultitx/:txids', transactions.showRaw); + app.param('txids', transactions.rawTransaction); // Address routes var addresses = require('../app/controllers/addresses'); diff --git a/lib/BlockDb.js b/lib/BlockDb.js index 1c50eb461..c899b8468 100644 --- a/lib/BlockDb.js +++ b/lib/BlockDb.js @@ -427,6 +427,7 @@ BlockDb.prototype.fillVinConfirmations = function(tx, cb) { if (!vin) return cb(); async.eachLimit(vin, CONCURRENCY, function(v, e_c) { + tx.confirmedIn = height - tx.confirmations + 1; self._fillConfirmationsOneVin(v, height, e_c); }, cb); }); diff --git a/lib/TransactionDb.js b/lib/TransactionDb.js index aa791b849..cd61d713f 100644 --- a/lib/TransactionDb.js +++ b/lib/TransactionDb.js @@ -179,10 +179,15 @@ TransactionDb.prototype._fillSpent = function(info, cb) { }); }; +var valToSat = function(x) { return parseInt(x.replace('.', '')) } TransactionDb.prototype._fillOutpoints = function(txInfo, cb) { var self = this; + for (var i = 0; i < txInfo.vout.length; i++) { + txInfo.vout[i].valueSat = valToSat(txInfo.vout[i].value); + } + if (!txInfo || txInfo.isCoinBase) return cb(); var valueIn = 0; @@ -227,22 +232,26 @@ TransactionDb.prototype._fillOutpoints = function(txInfo, cb) { }); }, function() { - if (!incompleteInputs) { - txInfo.valueIn = valueIn / util.COIN; - txInfo.fees = (valueIn - (txInfo.valueOut * util.COIN)).toFixed(0) / util.COIN; - } else { - txInfo.incompleteInputs = 1; - } - return cb(); + if (!incompleteInputs) { + txInfo.valueIn = valueIn / util.COIN; + txInfo.fees = (valueIn - (txInfo.valueOut * util.COIN)).toFixed(0) / util.COIN; + } else { + txInfo.incompleteInputs = 1; + } + return cb(); }); }; + TransactionDb.prototype._getInfo = function(txid, next) { var self = this; Rpc.getTxInfo(txid, function(err, txInfo) { if (err) return next(err); self._fillOutpoints(txInfo, function() { + for (var i = 0; i < txInfo.vout.length; i++) { + if (!txInfo.vout[i].valueSat) throw("Boo no valuesat: "+JSON.stringify(txInfo.vout[i])+" "+i+" "+txInfo.txid) + } self._fillSpent(txInfo, function() { return next(null, txInfo); }); From 74ea80be3261215a071578601572030aac8d6f76 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Fri, 24 Jul 2015 08:58:29 +0200 Subject: [PATCH 3/3] A few more fixes --- app/controllers/blocks.js | 6 +++--- app/controllers/common.js | 3 ++- lib/TransactionDb.js | 7 +++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/app/controllers/blocks.js b/app/controllers/blocks.js index 0b94ba233..f73b21c83 100644 --- a/app/controllers/blocks.js +++ b/app/controllers/blocks.js @@ -67,7 +67,7 @@ exports.blockIndex = multi(function(height, cb) { bdb.blockIndex(height, function(err, hashStr) { if (err) { console.log(err); - cb('Bad Request'); // TODO + return cb(err); // TODO } else { cb(null, hashStr); } @@ -82,11 +82,11 @@ exports.blockHeaderByIndex = multi(function(height, cb) { bdb.blockIndex(height, function(err, hashStr) { if (err) { console.log(err); - cb('Bad Request'); + return cb(err); } else { bdb.fromHashWithInfo(hashStr.blockHash, function(err, block) { if (err || !block) - cb(err); + return cb(err); else { delete block.info.tx; cb(null, block.info); diff --git a/app/controllers/common.js b/app/controllers/common.js index c74c56804..666d50b3e 100644 --- a/app/controllers/common.js +++ b/app/controllers/common.js @@ -28,8 +28,9 @@ exports.multi = function(f, outkey) { } else inputs = [inputdata]; async.mapSeries(inputs, f, function(err, results) { - if (err) + if (err) { return exports.handleErrors(err, res); + } req[outkey] = results; if (req[outkey].length == 1) req[outkey] = req[outkey][0] diff --git a/lib/TransactionDb.js b/lib/TransactionDb.js index cd61d713f..b2d0136b6 100644 --- a/lib/TransactionDb.js +++ b/lib/TransactionDb.js @@ -184,11 +184,13 @@ var valToSat = function(x) { return parseInt(x.replace('.', '')) } TransactionDb.prototype._fillOutpoints = function(txInfo, cb) { var self = this; + if (!txInfo) return cb(); + for (var i = 0; i < txInfo.vout.length; i++) { txInfo.vout[i].valueSat = valToSat(txInfo.vout[i].value); } - if (!txInfo || txInfo.isCoinBase) return cb(); + if (txInfo.isCoinBase) return cb(); var valueIn = 0; var incompleteInputs = 0; @@ -249,9 +251,6 @@ TransactionDb.prototype._getInfo = function(txid, next) { Rpc.getTxInfo(txid, function(err, txInfo) { if (err) return next(err); self._fillOutpoints(txInfo, function() { - for (var i = 0; i < txInfo.vout.length; i++) { - if (!txInfo.vout[i].valueSat) throw("Boo no valuesat: "+JSON.stringify(txInfo.vout[i])+" "+i+" "+txInfo.txid) - } self._fillSpent(txInfo, function() { return next(null, txInfo); });