Reliably update inputs. Self-healing. Audit Mongo blockheights

This commit is contained in:
tenthirtyone 2017-08-24 14:28:21 -04:00
parent a3941f75ef
commit 2ccaf2fce0
6 changed files with 69 additions and 39 deletions

View File

@ -9,11 +9,16 @@ logger.log('debug',
db.connect(config.mongodb.uri, config.mongodb.options); db.connect(config.mongodb.uri, config.mongodb.options);
db.connection.once('open', () => {
if (config.start_node) Bcoin.start();
Api.listen(config.api.port, () => { db.connection.once('open', () => {
logger.log('debug', // DB Audit returns best height to node
'listening on port 3000'); 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');
});
}); });
}); });

View File

@ -2,7 +2,6 @@ const Block = require('../../models/block.js');
const logger = require('../logger'); const logger = require('../logger');
const config = require('../../config'); const config = require('../../config');
const block = new Block();
let bestBlockHeight = 0; let bestBlockHeight = 0;
@ -20,27 +19,48 @@ function bestHeight(height) {
} }
function getRawBlock(hash, cb) { function getRawBlock(hash, cb) {
return block.getRawBlock(hash, cb); return Block.getRawBlock(hash, cb);
} }
function byHeight(height, cb) { function byHeight(height, cb) {
return block.byHeight(height, cb); return Block.byHeight(height, cb);
} }
function getTopBlocks(cb) { function getTopBlocks(cb) {
return block.last(cb); return Block.last(cb);
} }
function getByHash(hash, cb) { function getByHash(hash, cb) {
return block.byHash(hash, cb); return Block.byHash(hash, cb);
} }
function getLastBlock(cb) { function getLastBlock(cb) {
return block.last(cb) return Block.last(cb)
.limit(1); .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 = { module.exports = {
findMissingBlocks,
getRawBlock, getRawBlock,
getTopBlocks, getTopBlocks,
getLastBlock, getLastBlock,

View File

@ -2,44 +2,45 @@ const Transactions = require('../../models/transaction.js');
const config = require('../../config'); const config = require('../../config');
const logger = require('../logger'); const logger = require('../logger');
const Txs = new Transactions();
const MAX_PAGE_TXS = config.api.max_page_txs; const MAX_PAGE_TXS = config.api.max_page_txs;
function getEmptyInputs(cb) { function getEmptyInputs(cb) {
return Txs.getEmptyInputs(cb); return Transactions.getEmptyInputs(cb);
} }
function getTopTransactions(cb) { function getTopTransactions(cb) {
return Txs.last(cb); return Transactions.last(cb);
} }
function getTxById(txid, cb) { function getTxById(txid, cb) {
return Txs.byId(txid, cb); return Transactions.byId(txid, cb);
} }
function getTxByBlock(blockHash, page, limit, cb) { function getTxByBlock(blockHash, page, limit, cb) {
return Txs.byBlockHash(blockHash, cb) return Transactions.byBlockHash(blockHash, cb)
.skip(limit * page); .skip(limit * page);
} }
function getTxByAddress(address, page, limit, cb) { function getTxByAddress(address, page, limit, cb) {
return Txs.byAddress(address, cb) return Transactions.byAddress(address, cb)
.limit(limit) .limit(limit)
.skip(limit * page); .skip(limit * page);
} }
function getTxCountByBlock(blockHash, cb) { function getTxCountByBlock(blockHash, cb) {
return Txs.countByBlock(blockHash, cb); return Transactions.countByBlock(blockHash, cb);
} }
function getTxCountByAddress(address, cb) { function getTxCountByAddress(address, cb) {
return Txs.countByAddress(address, cb); return Transactions.countByAddress(address, cb);
} }
function updateInput(txid, inputid, value, address) { 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() { function auditInputs() {
getEmptyInputs( getEmptyInputs(
(err, txs) => { (err, txs) => {
@ -48,9 +49,6 @@ function auditInputs() {
`No Empty Inputs found: ${err.err}`); `No Empty Inputs found: ${err.err}`);
} }
// For each tx with unmarked inputs // For each tx with unmarked inputs
logger.log('debug',
`Found ${txs.length} txs with inputs to update`);
return txs.forEach((inputTx) => { return txs.forEach((inputTx) => {
inputTx.inputs.forEach((input) => { inputTx.inputs.forEach((input) => {
const txHash = input.prevout.hash; const txHash = input.prevout.hash;

View File

@ -9,11 +9,12 @@ const db = require('../../lib/db');
const node = new FullNode(config.bcoin); const node = new FullNode(config.bcoin);
let doneSyncing = false; let doneSyncing = false;
function start() { function start(bestBlockHeight) {
node.open() node.open()
.then(() => { .then(() => {
node.connect() node.connect()
.then(() => { .then(() => {
node.reset(bestBlockHeight);
node.startSync(); node.startSync();
}); });
}); });

View File

@ -31,26 +31,26 @@ const BlockSchema = new Schema({
BlockSchema.index({ hash: 1 }); BlockSchema.index({ hash: 1 });
BlockSchema.index({ height: 1 }); BlockSchema.index({ height: 1 });
BlockSchema.methods.byHeight = function blockByHeight(height, cb) { BlockSchema.statics.byHeight = function blockByHeight(height, cb) {
return this.model('Block').findOne( return this.model('Block').findOne(
{ height }, { height },
cb); cb);
}; };
BlockSchema.methods.byHash = function byHash(hash, cb) { BlockSchema.statics.byHash = function byHash(hash, cb) {
return this.model('Block').findOne( return this.model('Block').findOne(
{ hash }, { hash },
cb); cb);
}; };
BlockSchema.methods.getRawBlock = function getRawBlock(hash, cb) { BlockSchema.statics.getRawBlock = function getRawBlock(hash, cb) {
return this.model('Block').findOne( return this.model('Block').findOne(
{ hash }, { hash },
{ rawBlock: 1 }, { rawBlock: 1 },
cb); cb);
}; };
BlockSchema.methods.last = function lastBlocks(cb) { BlockSchema.statics.last = function lastBlocks(cb) {
return this.model('Block').find( return this.model('Block').find(
{}, {},
cb) cb)
@ -58,4 +58,12 @@ BlockSchema.methods.last = function lastBlocks(cb) {
.sort({ height: -1 }); .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); module.exports = mongoose.model('Block', BlockSchema);

View File

@ -32,24 +32,24 @@ TransactionSchema.index({ 'outputs.address': 1 });
TransactionSchema.index({ 'inputs.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( return this.model('Transaction').findOne(
{ hash: txid }, { hash: txid },
cb); cb);
}; };
TransactionSchema.methods.byHash = function txByHash(hash, cb) { TransactionSchema.statics.byHash = function txByHash(hash, cb) {
return this.byId(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( return this.model('Transaction').find(
{ block: hash }, { block: hash },
cb) cb)
.limit(MAX_TXS); .limit(MAX_TXS);
}; };
TransactionSchema.methods.byAddress = function txByAddress(address, cb) { TransactionSchema.statics.byAddress = function txByAddress(address, cb) {
return this.model('Transaction').find( return this.model('Transaction').find(
{ {
$or: [ $or: [
@ -60,13 +60,13 @@ TransactionSchema.methods.byAddress = function txByAddress(address, cb) {
.limit(MAX_TXS); .limit(MAX_TXS);
}; };
TransactionSchema.methods.countByBlock = function txByAddress(hash, cb) { TransactionSchema.statics.countByBlock = function txByAddress(hash, cb) {
return this.model('Transaction').count( return this.model('Transaction').count(
{ block: hash }, { block: hash },
cb); cb);
}; };
TransactionSchema.methods.countByAddress = function txByAddress(address, cb) { TransactionSchema.statics.countByAddress = function txByAddress(address, cb) {
return this.model('Transaction').count( return this.model('Transaction').count(
{ {
$or: [ $or: [
@ -76,7 +76,7 @@ TransactionSchema.methods.countByAddress = function txByAddress(address, cb) {
cb); cb);
}; };
TransactionSchema.methods.last = function lastTx(cb) { TransactionSchema.statics.last = function lastTx(cb) {
return this.model('Transaction').find( return this.model('Transaction').find(
{}, {},
cb) cb)
@ -84,7 +84,7 @@ TransactionSchema.methods.last = function lastTx(cb) {
.sort({ height: -1 }); .sort({ height: -1 });
}; };
TransactionSchema.methods.getEmptyInputs = function getEmptyInputs(cb) { TransactionSchema.statics.getEmptyInputs = function getEmptyInputs(cb) {
return this.model('Transaction').find({ return this.model('Transaction').find({
'inputs.prevout.hash': { $ne: '0000000000000000000000000000000000000000000000000000000000000000' }, 'inputs.prevout.hash': { $ne: '0000000000000000000000000000000000000000000000000000000000000000' },
'inputs.value': 0, 'inputs.value': 0,
@ -92,9 +92,7 @@ TransactionSchema.methods.getEmptyInputs = function getEmptyInputs(cb) {
cb); cb);
}; };
TransactionSchema.methods.updateInput = function updateInput(txid, inputid, value, address) { TransactionSchema.statics.updateInput = function updateInput(txid, inputid, value, address) {
logger.log('debug',
`${txid} ${address}value is ${value}`);
return this.model('Transaction').findOneAndUpdate( return this.model('Transaction').findOneAndUpdate(
{ _id: txid, 'inputs._id': inputid }, { _id: txid, 'inputs._id': inputid },
{ {