From 2ccaf2fce0f17a0975646224e68fd2945dbe7d8d Mon Sep 17 00:00:00 2001 From: tenthirtyone Date: Thu, 24 Aug 2017 14:28:21 -0400 Subject: [PATCH] Reliably update inputs. Self-healing. Audit Mongo blockheights --- server/index.js | 15 ++++++++++----- server/lib/db/blocks.js | 32 ++++++++++++++++++++++++++------ server/lib/db/transactions.js | 22 ++++++++++------------ server/lib/node/index.js | 3 ++- server/models/block.js | 16 ++++++++++++---- server/models/transaction.js | 20 +++++++++----------- 6 files changed, 69 insertions(+), 39 deletions(-) diff --git a/server/index.js b/server/index.js index 6cf93ad..00ec06c 100644 --- a/server/index.js +++ b/server/index.js @@ -9,11 +9,16 @@ logger.log('debug', db.connect(config.mongodb.uri, config.mongodb.options); -db.connection.once('open', () => { - if (config.start_node) Bcoin.start(); - Api.listen(config.api.port, () => { - logger.log('debug', - 'listening on port 3000'); +db.connection.once('open', () => { + // DB Audit returns best height to node + db.blocks.findMissingBlocks((err, lastBestHeight) => { + // Pass height to node to start Sync + if (config.start_node) Bcoin.start(lastBestHeight); + + Api.listen(config.api.port, () => { + logger.log('debug', + 'listening on port 3000'); + }); }); }); diff --git a/server/lib/db/blocks.js b/server/lib/db/blocks.js index e4536bd..547319b 100644 --- a/server/lib/db/blocks.js +++ b/server/lib/db/blocks.js @@ -2,7 +2,6 @@ const Block = require('../../models/block.js'); const logger = require('../logger'); const config = require('../../config'); -const block = new Block(); let bestBlockHeight = 0; @@ -20,27 +19,48 @@ function bestHeight(height) { } function getRawBlock(hash, cb) { - return block.getRawBlock(hash, cb); + return Block.getRawBlock(hash, cb); } function byHeight(height, cb) { - return block.byHeight(height, cb); + return Block.byHeight(height, cb); } function getTopBlocks(cb) { - return block.last(cb); + return Block.last(cb); } function getByHash(hash, cb) { - return block.byHash(hash, cb); + return Block.byHash(hash, cb); } function getLastBlock(cb) { - return block.last(cb) + return Block.last(cb) .limit(1); } +// Returns the missing block if it exists. Otherwise, return tip. +function findMissingBlocks(cb) { + logger.log('debug', + 'Verifying Mongo Blockchain'); + return Block.getHeights((err, blocks) => { + if (err) { + return cb(err); + } + // Blocks are in ascending order + let lastGoodHeight = 0; + blocks.forEach((block) => { + if (lastGoodHeight !== block.height - 1) { + return lastGoodHeight; + } + lastGoodHeight = block.height; + }); + return lastGoodHeight; + }); +} + module.exports = { + findMissingBlocks, getRawBlock, getTopBlocks, getLastBlock, diff --git a/server/lib/db/transactions.js b/server/lib/db/transactions.js index 98d7f0c..6f84645 100644 --- a/server/lib/db/transactions.js +++ b/server/lib/db/transactions.js @@ -2,44 +2,45 @@ const Transactions = require('../../models/transaction.js'); const config = require('../../config'); const logger = require('../logger'); -const Txs = new Transactions(); + const MAX_PAGE_TXS = config.api.max_page_txs; function getEmptyInputs(cb) { - return Txs.getEmptyInputs(cb); + return Transactions.getEmptyInputs(cb); } function getTopTransactions(cb) { - return Txs.last(cb); + return Transactions.last(cb); } function getTxById(txid, cb) { - return Txs.byId(txid, cb); + return Transactions.byId(txid, cb); } function getTxByBlock(blockHash, page, limit, cb) { - return Txs.byBlockHash(blockHash, cb) + return Transactions.byBlockHash(blockHash, cb) .skip(limit * page); } function getTxByAddress(address, page, limit, cb) { - return Txs.byAddress(address, cb) + return Transactions.byAddress(address, cb) .limit(limit) .skip(limit * page); } function getTxCountByBlock(blockHash, cb) { - return Txs.countByBlock(blockHash, cb); + return Transactions.countByBlock(blockHash, cb); } function getTxCountByAddress(address, cb) { - return Txs.countByAddress(address, cb); + return Transactions.countByAddress(address, cb); } function updateInput(txid, inputid, value, address) { - return Txs.updateInput(txid, inputid, value, address); + return Transactions.updateInput(txid, inputid, value, address); } +// Updates empty inputs with prevout addr & value function auditInputs() { getEmptyInputs( (err, txs) => { @@ -48,9 +49,6 @@ function auditInputs() { `No Empty Inputs found: ${err.err}`); } // For each tx with unmarked inputs - logger.log('debug', - `Found ${txs.length} txs with inputs to update`); - return txs.forEach((inputTx) => { inputTx.inputs.forEach((input) => { const txHash = input.prevout.hash; diff --git a/server/lib/node/index.js b/server/lib/node/index.js index 8bb864e..ee4171a 100644 --- a/server/lib/node/index.js +++ b/server/lib/node/index.js @@ -9,11 +9,12 @@ const db = require('../../lib/db'); const node = new FullNode(config.bcoin); let doneSyncing = false; -function start() { +function start(bestBlockHeight) { node.open() .then(() => { node.connect() .then(() => { + node.reset(bestBlockHeight); node.startSync(); }); }); diff --git a/server/models/block.js b/server/models/block.js index 9c18bc7..445e119 100644 --- a/server/models/block.js +++ b/server/models/block.js @@ -31,26 +31,26 @@ const BlockSchema = new Schema({ BlockSchema.index({ hash: 1 }); BlockSchema.index({ height: 1 }); -BlockSchema.methods.byHeight = function blockByHeight(height, cb) { +BlockSchema.statics.byHeight = function blockByHeight(height, cb) { return this.model('Block').findOne( { height }, cb); }; -BlockSchema.methods.byHash = function byHash(hash, cb) { +BlockSchema.statics.byHash = function byHash(hash, cb) { return this.model('Block').findOne( { hash }, cb); }; -BlockSchema.methods.getRawBlock = function getRawBlock(hash, cb) { +BlockSchema.statics.getRawBlock = function getRawBlock(hash, cb) { return this.model('Block').findOne( { hash }, { rawBlock: 1 }, cb); }; -BlockSchema.methods.last = function lastBlocks(cb) { +BlockSchema.statics.last = function lastBlocks(cb) { return this.model('Block').find( {}, cb) @@ -58,4 +58,12 @@ BlockSchema.methods.last = function lastBlocks(cb) { .sort({ height: -1 }); }; +BlockSchema.statics.getHeights = function findMissing(cb) { + return this.model('Block').find( + {}, + { height: 1 }, + cb) + .sort({ height: 1 }); +}; + module.exports = mongoose.model('Block', BlockSchema); diff --git a/server/models/transaction.js b/server/models/transaction.js index 4c7140c..2376357 100644 --- a/server/models/transaction.js +++ b/server/models/transaction.js @@ -32,24 +32,24 @@ TransactionSchema.index({ 'outputs.address': 1 }); TransactionSchema.index({ 'inputs.address': 1 }); -TransactionSchema.methods.byId = function txById(txid, cb) { +TransactionSchema.statics.byId = function txById(txid, cb) { return this.model('Transaction').findOne( { hash: txid }, cb); }; -TransactionSchema.methods.byHash = function txByHash(hash, cb) { +TransactionSchema.statics.byHash = function txByHash(hash, cb) { return this.byId(hash, cb); }; -TransactionSchema.methods.byBlockHash = function txByBlockHash(hash, cb) { +TransactionSchema.statics.byBlockHash = function txByBlockHash(hash, cb) { return this.model('Transaction').find( { block: hash }, cb) .limit(MAX_TXS); }; -TransactionSchema.methods.byAddress = function txByAddress(address, cb) { +TransactionSchema.statics.byAddress = function txByAddress(address, cb) { return this.model('Transaction').find( { $or: [ @@ -60,13 +60,13 @@ TransactionSchema.methods.byAddress = function txByAddress(address, cb) { .limit(MAX_TXS); }; -TransactionSchema.methods.countByBlock = function txByAddress(hash, cb) { +TransactionSchema.statics.countByBlock = function txByAddress(hash, cb) { return this.model('Transaction').count( { block: hash }, cb); }; -TransactionSchema.methods.countByAddress = function txByAddress(address, cb) { +TransactionSchema.statics.countByAddress = function txByAddress(address, cb) { return this.model('Transaction').count( { $or: [ @@ -76,7 +76,7 @@ TransactionSchema.methods.countByAddress = function txByAddress(address, cb) { cb); }; -TransactionSchema.methods.last = function lastTx(cb) { +TransactionSchema.statics.last = function lastTx(cb) { return this.model('Transaction').find( {}, cb) @@ -84,7 +84,7 @@ TransactionSchema.methods.last = function lastTx(cb) { .sort({ height: -1 }); }; -TransactionSchema.methods.getEmptyInputs = function getEmptyInputs(cb) { +TransactionSchema.statics.getEmptyInputs = function getEmptyInputs(cb) { return this.model('Transaction').find({ 'inputs.prevout.hash': { $ne: '0000000000000000000000000000000000000000000000000000000000000000' }, 'inputs.value': 0, @@ -92,9 +92,7 @@ TransactionSchema.methods.getEmptyInputs = function getEmptyInputs(cb) { cb); }; -TransactionSchema.methods.updateInput = function updateInput(txid, inputid, value, address) { - logger.log('debug', - `${txid} ${address}value is ${value}`); +TransactionSchema.statics.updateInput = function updateInput(txid, inputid, value, address) { return this.model('Transaction').findOneAndUpdate( { _id: txid, 'inputs._id': inputid }, {