From 5a89cf17d268478b82fa35953175bfde1d80a00e Mon Sep 17 00:00:00 2001 From: tenthirtyone Date: Thu, 17 Aug 2017 11:25:58 -0400 Subject: [PATCH] All routes replaced. Stable but no tx inputs working. --- server/lib/api/address.js | 15 ++++- server/lib/api/transaction.js | 100 +++++++++++++------------------ server/lib/db/transactions.js | 88 +++++++++++++++++++++++++-- server/lib/node/index.js | 4 ++ server/lib/parser/transaction.js | 23 ++----- server/public/img/leveldb.png | Bin 12009 -> 12077 bytes 6 files changed, 148 insertions(+), 82 deletions(-) diff --git a/server/lib/api/address.js b/server/lib/api/address.js index 722adb2..d9c93c2 100644 --- a/server/lib/api/address.js +++ b/server/lib/api/address.js @@ -1,6 +1,7 @@ const logger = require('../logger'); const request = require('request'); const config = require('../../config'); +const db = require('../db'); const API_URL = `http://${config.bcoin_http}:${config.bcoin['http-port']}`; const TTL = config.api.request_ttl; @@ -8,8 +9,18 @@ const TTL = config.api.request_ttl; module.exports = function AddressAPI(router) { router.get('/addr/:addr', (req, res) => { const addr = req.params.addr || ''; - logger.log('debug', - 'Warning: Requesting data from Bcoin by address, may take some time'); + /* + db.txs.getTxByAddress(addr, 0, 999999999, (error, txs) => { + if (error) { + logger.log('err', + `getTxByBlock ${error}`); + return res.status(404).send(); + } + + console.log(txs.count()); + + }); +*/ // Get Bcoin data return request(`${API_URL}/tx/address/${addr}`, { timeout: TTL }, diff --git a/server/lib/api/transaction.js b/server/lib/api/transaction.js index 1c92b25..26cf635 100644 --- a/server/lib/api/transaction.js +++ b/server/lib/api/transaction.js @@ -19,9 +19,9 @@ module.exports = function transactionAPI(router) { if (err) { logger.log('err', `getTxById: ${err}`); - return res.status(400).send(); + return res.status(404).send(); } - console.log(transaction); + const tx = transaction; return res.send({ txid: tx.hash, @@ -31,6 +31,7 @@ module.exports = function transactionAPI(router) { locktime: tx.locktime, blockhash: tx.block, fees: tx.fee / 1e8, + size: tx.size, confirmations: (height - tx.height) + 1, valueOut: tx.outputs.reduce((sum, output) => sum + output.value, 0) / 1e8, vin: tx.inputs.map(input => ({ @@ -59,43 +60,32 @@ module.exports = function transactionAPI(router) { // get txs for blockhash, start with best height to calc confirmations if (req.query.block) { const height = db.blocks.bestHeight(); - // Get Bcoin data - return request(`${API_URL}/block/${req.query.block}`, - { timeout: TTL }, - (error, localRes, block) => { + + db.txs.getTxCountByBlock(req.query.block, (err, count) => { + if (err) { + logger.log('err', + `getTxByBlock ${err}`); + return res.status(404).send(); + } + const totalPages = Math.ceil(count / MAX_TXS); + + return db.txs.getTxByBlock(req.query.block, pageNum, MAX_TXS, (error, txs) => { if (error) { - logger.log('error', - `${error}`); + logger.log('err', + `getTxByBlock ${error}`); return res.status(404).send(); } - // Catch JSON errors - try { - block = JSON.parse(block); - } catch (e) { - logger.log('error', - `${e}`); - return res.status(404).send(); - } - - if (block.error) { - logger.log('error', - `${'No tx results'}`); - return res.status(404).send(); - } - // Setup UI JSON - const totalPages = Math.ceil(block.txs.length / MAX_TXS); - block.txs = block.txs.slice(rangeStart, rangeEnd); - return res.send({ pagesTotal: totalPages, - txs: block.txs.map(tx => ({ + txs: txs.map(tx => ({ txid: tx.hash, fees: tx.fee / 1e8, - confirmations: (height - block.height) + 1, + size: tx.size, + confirmations: (height - tx.height) + 1, valueOut: tx.outputs.reduce((sum, output) => sum + output.value, 0) / 1e8, vin: tx.inputs.map(input => ({ - addr: input.coin ? input.coin.address : '', - value: input.coin ? input.coin.value / 1e8 : 0, + addr: input.address, + value: input.value / 1e8, })), vout: tx.outputs.map(output => ({ scriptPubKey: { @@ -107,47 +97,37 @@ module.exports = function transactionAPI(router) { })), }); }); + }); } else if (req.query.address) { // Get txs by address, start with best height to calc confirmations const height = db.blocks.bestHeight(); const addr = req.query.address || ''; - logger.log('debug', - 'Warning: Requesting data from Bcoin by address, may take some time'); + db.txs.getTxCountByAddress(req.query.address, (err, count) => { + if (err) { + logger.log('err', + `getTxByBlock ${err}`); + return res.status(404).send(); + } + const totalPages = Math.ceil(count / MAX_TXS); - return request(`${API_URL}/tx/address/${addr}`, - { timeout: TTL }, - (error, localRes, txs) => { + return db.txs.getTxByAddress(req.query.address, pageNum, MAX_TXS, (error, txs) => { if (error) { - logger.log('error', - `${error}`); + logger.log('err', + `getTxByBlock ${error}`); return res.status(404).send(); } - // Catch JSON errors - try { - txs = JSON.parse(txs); - } catch (e) { - logger.log('error', - `${e}`); - return res.status(404).send(); - } - // Bcoin returns error as part of data object - if (txs.error) { - logger.log('error', - `${'No tx results'}`); - return res.status(404).send(); - } - // Setup UI JSON return res.send({ - pagesTotal: 1, + pagesTotal: totalPages, txs: txs.map(tx => ({ txid: tx.hash, fees: tx.fee / 1e8, - confirmations: (height - tx.height) + 1, + size: tx.size, + confirmations: (height - tx.height) + 1, valueOut: tx.outputs.reduce((sum, output) => sum + output.value, 0) / 1e8, vin: tx.inputs.map(input => ({ - addr: input.coin ? input.coin.address : '', - value: input.coin ? input.coin.value / 1e8 : 0, + addr: input.address, + value: input.value / 1e8, })), vout: tx.outputs.map(output => ({ scriptPubKey: { @@ -159,9 +139,11 @@ module.exports = function transactionAPI(router) { })), }); }); + }); + } else { + // Get last n txs + return res.status(404).send({ error: 'Block hash or address expected' }); } - // Get last n txs - return res.status(404).send({ error: 'Block hash or address expected' }); }); router.get('/rawtx/:txid', (req, res) => { @@ -178,7 +160,7 @@ module.exports = function transactionAPI(router) { if (err) { logger.log('error', `${err}`); - res.status(400).send(err); + res.status(404).send(err); return; } diff --git a/server/lib/db/transactions.js b/server/lib/db/transactions.js index 0bbfbd7..c41c9ff 100644 --- a/server/lib/db/transactions.js +++ b/server/lib/db/transactions.js @@ -2,9 +2,11 @@ const Transactions = require('../../models/transaction.js'); const logger = require('../logger'); const config = require('../../config'); +// Will be replaced with a more sophisticated api soon + const MAX_TXS = config.api.max_txs; -function getTransactions(params, options, limit, cb) { +function getTransactions(params, options, limit, skip, cb) { // Do not return mongo ids const defaultOptions = { _id: 0 }; // Copy over mongo options @@ -36,12 +38,13 @@ function getTransactions(params, options, limit, cb) { } return cb(null, txs); }) - .sort({ height: -1 }) + .sort({ height: 1 }) + .skip() .limit(limit); } -function getTransaction(params, options, limit, cb) { - getTransactions(params, options, limit, (err, tx) => { +function getTransaction(params, options, limit, skip, cb) { + getTransactions(params, options, limit, skip, (err, tx) => { if (err) { logger.log('error', `getTransaction: ${err.err}`); @@ -59,6 +62,7 @@ function getTxById(txid, cb) { { hash: txid }, {}, 1, + 0, (err, transaction) => { if (err) { logger.log('err', @@ -69,6 +73,78 @@ function getTxById(txid, cb) { }); } +function getTxByBlock(blockHash, page, limit, cb) { + getTransactions( + { block: blockHash }, + {}, + limit, + page * limit, + (err, tx) => { + if (err) { + logger.log('error', + `getTxByBlock: ${err.err}`); + return cb(err); + } + if (!tx.length > 0) { + return cb({ err: 'Tx not found' }); + } + return cb(null, tx); + }); +} + +function getTxByAddress(address, page, limit, cb) { + getTransactions( + { + $or: [ + { 'inputs.address': address }, + { 'outputs.address': address }], + }, + {}, + limit, + page * limit, + (err, tx) => { + if (err) { + logger.log('error', + `getTxByAddress: ${err.err}`); + return cb(err); + } + if (!tx.length > 0) { + return cb({ err: 'Tx not found' }); + } + return cb(null, tx); + }); +} + +function getTxCountByBlock(blockHash, cb) { + Transactions.count( + { block: blockHash }, + (err, count) => { + if (err) { + logger.log('err', + `getTxCountByBlock ${err}`); + return cb(err); + } + return cb(null, count); + }); +} + +function getTxCountByAddress(address, cb) { + Transactions.count( + { $or: [ + { 'inputs.address': address }, + { 'outputs.address': address }], + }, + (err, count) => { + if (err) { + logger.log('err', + `getTxCountByAddress ${err}`); + return cb(err); + } + return cb(null, count); + }); +} + + function updateInput(txid, inputid, value, address) { Transactions.findOneAndUpdate( { _id: txid, 'inputs._id': inputid }, @@ -91,5 +167,9 @@ module.exports = { getTransaction, getTransactions, getTxById, + getTxByBlock, + getTxCountByBlock, + getTxByAddress, + getTxCountByAddress, updateInput, }; diff --git a/server/lib/node/index.js b/server/lib/node/index.js index c7a2415..2e53167 100644 --- a/server/lib/node/index.js +++ b/server/lib/node/index.js @@ -24,6 +24,10 @@ function start() { db.blocks.bestHeight(entry.height); }); + node.chain.on('full', (block) => { + + }); + node.on('error', (err) => { logger.log('error', `${err}`); diff --git a/server/lib/parser/transaction.js b/server/lib/parser/transaction.js index 993e825..4283d81 100644 --- a/server/lib/parser/transaction.js +++ b/server/lib/parser/transaction.js @@ -6,6 +6,12 @@ const util = require('../../lib/util'); const logger = require('../logger'); const db = require('../db'); +// Bleh, Bcoin pulls in blocks 20 at a time +// Crappy delay for now otherwise async saves +// could miss a tx if an input refs a block within +// the last 20 that hasn't saved. +// Aggregate stuff will replace all of this. + function parse(entry, txs) { txs.forEach((tx) => { const txJSON = tx.toJSON(); @@ -52,23 +58,6 @@ function parse(entry, txs) { if (err) { logger.log('error', err.message); } - - t.inputs.forEach((input) => { - const txid = input.prevout.hash; - const idx = input.prevout.index; - const addr = input.address; - if (txid !== '0000000000000000000000000000000000000000000000000000000000000000') { - db.txs.getTxById(txid, (err, tx) => { - if (err) { - logger.log('err', - `Tx Parser inputs.ForEach: ${err}`); - return; - } - - db.txs.updateInput(t._id, input._id, tx.outputs[idx].value, tx.outputs[idx].address); - }); - } - }); }); }); } diff --git a/server/public/img/leveldb.png b/server/public/img/leveldb.png index da13a671aaabef5febe267d4d10fa663fbbab21a..b4a177783a10958445412037a502b87023eed70c 100644 GIT binary patch literal 12077 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 oOf!h%@6PfbDF|I^_*KF`54E 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