diff --git a/server/lib/api/transaction.js b/server/lib/api/transaction.js index d07a398..c834902 100644 --- a/server/lib/api/transaction.js +++ b/server/lib/api/transaction.js @@ -11,60 +11,53 @@ module.exports = function transactionAPI(router) { // Txs by txid router.get('/tx/:txid', (req, res) => { // Get max block height for calculating confirmations - db.blocks.getBestHeight( - (err, blockHeight) => { - if (err) { - logger.log('err', err); + const height = db.blocks.bestHeight(); + // Bcoin transaction data + return request(`${API_URL}/tx/${req.params.txid}`, + { timeout: TTL }, + (error, localRes, tx) => { + if (error) { + logger.log('error', + `${error}`); + return res.status(404).send(); + } + // Catch JSON errors + try { + tx = JSON.parse(tx); + } catch (e) { + logger.log('error', + `${e}`); + return res.status(404).send(); + } + if (!tx || !tx.hash) { + logger.log('error', + 'No results found'); return res.status(404).send(); } - const height = blockHeight; - // Bcoin transaction data - return request(`${API_URL}/tx/${req.params.txid}`, - { timeout: TTL }, - (error, localRes, tx) => { - if (error) { - logger.log('error', - `${error}`); - return res.status(404).send(); - } - // Catch JSON errors - try { - tx = JSON.parse(tx); - } catch (e) { - logger.log('error', - `${e}`); - return res.status(404).send(); - } - if (!tx || !tx.hash) { - logger.log('error', - 'No results found'); - return res.status(404).send(); - } - // Return UI JSON - return res.send({ - txid: tx.hash, - version: tx.version, - time: tx.ps, - blocktime: tx.ps, - locktime: tx.locktime, - blockhash: tx.block, - fees: tx.fee / 1e8, - 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, - })), - vout: tx.outputs.map(output => ({ - scriptPubKey: { - addresses: [output.address], - }, - value: output.value / 1e8, - })), - isCoinBase: tx.inputs[0].prevout.hash === '0000000000000000000000000000000000000000000000000000000000000000', - }); - }); + // Return UI JSON + return res.send({ + txid: tx.hash, + version: tx.version, + time: tx.ps, + blocktime: tx.ps, + locktime: tx.locktime, + blockhash: tx.block, + fees: tx.fee / 1e8, + 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, + })), + vout: tx.outputs.map(output => ({ + scriptPubKey: { + addresses: [output.address], + }, + value: output.value / 1e8, + })), + isCoinBase: tx.inputs[0].prevout.hash === '0000000000000000000000000000000000000000000000000000000000000000', + }); }); }); @@ -78,126 +71,111 @@ module.exports = function transactionAPI(router) { const rangeEnd = rangeStart + MAX_TXS; // get txs for blockhash, start with best height to calc confirmations if (req.query.block) { - db.blocks.getBestHeight( - (err, blockHeight) => { - if (err) { - logger.log('err', err); + + const height = db.blocks.bestHeight(); + // Get Bcoin data + return request(`${API_URL}/block/${req.query.block}`, + { timeout: TTL }, + (error, localRes, block) => { + if (error) { + logger.log('error', + `${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(); } - const height = blockHeight; - // Get Bcoin data - return request(`${API_URL}/block/${req.query.block}`, - { timeout: TTL }, - (error, localRes, block) => { - if (error) { - logger.log('error', - `${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); + 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 => ({ - txid: tx.hash, - fees: tx.fee / 1e8, - confirmations: (height - block.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, - })), - vout: tx.outputs.map(output => ({ - scriptPubKey: { - addresses: [output.address], - }, - value: output.value / 1e8, - })), - isCoinBase: tx.inputs[0].prevout.hash === '0000000000000000000000000000000000000000000000000000000000000000', - })), - }); - }); + return res.send({ + pagesTotal: totalPages, + txs: block.txs.map(tx => ({ + txid: tx.hash, + fees: tx.fee / 1e8, + confirmations: (height - block.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, + })), + vout: tx.outputs.map(output => ({ + scriptPubKey: { + addresses: [output.address], + }, + value: output.value / 1e8, + })), + isCoinBase: tx.inputs[0].prevout.hash === '0000000000000000000000000000000000000000000000000000000000000000', + })), + }); }); } else if (req.query.address) { // Get txs by address, start with best height to calc confirmations - db.blocks.getBestHeight( - (err, blockHeight) => { - if (err) { - logger.log('err', err); + const height = db.blocks.bestHeight(); + const addr = req.query.address || ''; + + logger.log('debug', + 'Warning: Requesting data from Bcoin by address, may take some time'); + + return request(`${API_URL}/tx/address/${addr}`, + { timeout: TTL }, + (error, localRes, txs) => { + if (error) { + logger.log('error', + `${error}`); return res.status(404).send(); } - - const height = blockHeight; - const addr = req.query.address || ''; - - logger.log('debug', - 'Warning: Requesting data from Bcoin by address, may take some time'); - - return request(`${API_URL}/tx/address/${addr}`, - { timeout: TTL }, - (error, localRes, txs) => { - if (error) { - logger.log('error', - `${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, - txs: txs.map(tx => ({ - txid: tx.hash, - fees: tx.fee / 1e8, - 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, - })), - vout: tx.outputs.map(output => ({ - scriptPubKey: { - addresses: [output.address], - }, - value: output.value / 1e8, - })), - isCoinBase: tx.inputs[0].prevout.hash === '0000000000000000000000000000000000000000000000000000000000000000', - })), - }); - }); + // 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, + txs: txs.map(tx => ({ + txid: tx.hash, + fees: tx.fee / 1e8, + 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, + })), + vout: tx.outputs.map(output => ({ + scriptPubKey: { + addresses: [output.address], + }, + value: output.value / 1e8, + })), + isCoinBase: tx.inputs[0].prevout.hash === '0000000000000000000000000000000000000000000000000000000000000000', + })), + }); }); - } 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) => { diff --git a/server/lib/db/blocks.js b/server/lib/db/blocks.js index deb101f..5659d34 100644 --- a/server/lib/db/blocks.js +++ b/server/lib/db/blocks.js @@ -4,6 +4,8 @@ const config = require('../../config'); const MAX_BLOCKS = config.api.max_blocks; // ~ 12 hours +let bestBlockHeight = 0; + function getBlocks(params, options, limit, cb) { // Do not return mongo ids const defaultOptions = { _id: 0 }; @@ -54,20 +56,31 @@ function getBlock(params, options, limit, cb) { return cb(null, blocks[0]); }); } -// Highest known height -function getBestHeight(cb) { +// Highest known height in mongo +function getBestHeight() { getBlock({}, {}, 1, (err, block) => { if (err) { logger.log('error', - `getBlock: ${err.err}`); - return cb(err, null); + `getBestHeight: ${err.err}`); + return; } - return cb(null, block.height); + bestBlockHeight = block.height; }); } +// 1e9 limit = ~2M years from now +// Mostly for sync to set height +function bestHeight(height) { + if (Number.isInteger(height) && + height > 0 && + height < 1 * 1e9) { + bestBlockHeight = height; + return bestBlockHeight; + } + return bestBlockHeight; +} module.exports = { getBlock, getBlocks, - getBestHeight, + bestHeight, }; diff --git a/server/lib/db/transactions.js b/server/lib/db/transactions.js index 905d578..bfd90c3 100644 --- a/server/lib/db/transactions.js +++ b/server/lib/db/transactions.js @@ -2,6 +2,9 @@ const Transactions = require('../../models/transaction.js'); const logger = require('../logger'); const config = require('../../config'); +// For now, blocks handles these calls. +// These will be replaced with more advanced mongo + const MAX_TXS = config.api.max_txs; function getTransactions(params, options, limit, cb) { @@ -26,7 +29,6 @@ function getTransactions(params, options, limit, cb) { params, defaultOptions, (err, txs) => { - console.log(txs) if (err) { logger.log('error', `getTransactions: ${err}`); diff --git a/server/lib/node/index.js b/server/lib/node/index.js index 266f5b1..bea48c0 100644 --- a/server/lib/node/index.js +++ b/server/lib/node/index.js @@ -3,6 +3,7 @@ const logger = require('../../lib/logger'); const BlockParser = require('../parser').Block; const config = require('../../config'); const socket = require('../../lib/api/socket'); +const db = require('../../lib/db'); const node = new FullNode(config.bcoin); @@ -18,6 +19,7 @@ function start() { node.chain.on('connect', (entry, block) => { BlockParser.parse(entry, block); socket.processBlock(entry, block); + db.blocks.bestHeight(entry.height); }); node.on('error', (err) => {