diff --git a/app/controllers/blocks.js b/app/controllers/blocks.js index d9c9f89..3a38d8d 100644 --- a/app/controllers/blocks.js +++ b/app/controllers/blocks.js @@ -13,10 +13,10 @@ var mongoose = require('mongoose'), * Find block by hash ... */ exports.block = function(req, res, next, hash) { - Block.fromHash(hash, function(err, block) { + Block.fromHashWithInfo(hash, function(err, block) { if (err) return next(err); if (!block) return next(new Error('Failed to load block ' + hash)); - req.block = block; + req.block = block.info; next(); }); }; diff --git a/app/controllers/transactions.js b/app/controllers/transactions.js index 8bf4635..6a4cffd 100644 --- a/app/controllers/transactions.js +++ b/app/controllers/transactions.js @@ -15,10 +15,10 @@ var Transaction = require('../models/Transaction'); * Find block by hash ... */ exports.transaction = function(req, res, next, txid) { - Transaction.fromID(txid, function(err, tx) { + Transaction.fromIdWithInfo(txid, function(err, tx) { if (err) return next(err); if (!tx) return next(new Error('Failed to load TX ' + txid)); - req.transaction = tx; + req.transaction = tx.info; next(); }); }; diff --git a/app/models/Block.js b/app/models/Block.js index ee68b94..a226c34 100644 --- a/app/models/Block.js +++ b/app/models/Block.js @@ -4,68 +4,27 @@ * Module dependencies. */ var mongoose = require('mongoose'), - Schema = mongoose.Schema; - -var async = require('async'); -var Transaction = require('./Transaction'); + Schema = mongoose.Schema, + RpcClient = require('bitcore/RpcClient').class(), + config = require('../../config/config') + ; /** * Block Schema */ var BlockSchema = new Schema({ + + // For now we keep this as short as possible + // More fields will be propably added as we move + // forward with the UX hash: { type: String, index: true, unique: true, }, - size: Number, - height: Number, - confirmations: Number, - version: Number, - merkleroot: String, - tx: [ String ], - time: Date, - nonce: Number, - bits: String, - difficulty: Number, - chainwork: String, - previousblockhash: { - type: String, - index: true, - unique: true, - }, - nextblockhash: { - type: String, - index: true, - unique: true, - }, + time: Number, }); -BlockSchema.methods.explodeTransactions = function(next) { - - // console.log('exploding %s', this.hash, typeof this.tx); - - async.forEach( this.tx, - function(tx, callback) { - // console.log('procesing TX %s', tx); - Transaction.create({ txid: tx }, function(err) { - if (err && ! err.toString().match(/E11000/)) { - return callback(); - } - if (err) { - - return callback(err); - } - return callback(); - - }); - }, - function(err) { - if (err) return next(err); - return next(); - } - ); -}; /** * Validations @@ -94,4 +53,38 @@ BlockSchema.statics.fromHash = function(hash, cb) { }).exec(cb); }; + +BlockSchema.statics.fromHashWithInfo = function(hash, cb) { + this.fromHash(hash, function(err, block) { + if (err) return cb(err); + + block.getInfo(function(err) { return cb(err,block); } ); + }); +}; + + + +// TODO: Can we store the rpc instance in the Block object? +BlockSchema.methods.getInfo = function (next) { + + var that = this; + var rpc = new RpcClient(config.bitcoind); + + rpc.getBlock(this.hash, function(err, blockInfo) { + if (err) return next(err); + + /* + * Not sure this is the right way to do it. + * Any other way to lazy load a property in a mongoose object? + */ + + that.info = blockInfo.result; + + //console.log("THAT", that); + return next(null, that.info); + }); +}; + + + module.exports = mongoose.model('Block', BlockSchema); diff --git a/app/models/Transaction.js b/app/models/Transaction.js index 566a480..fb8cd86 100644 --- a/app/models/Transaction.js +++ b/app/models/Transaction.js @@ -4,35 +4,23 @@ * Module dependencies. */ var mongoose = require('mongoose'), - Schema = mongoose.Schema; - + Schema = mongoose.Schema, + async = require('async'), + RpcClient = require('bitcore/RpcClient').class(), + config = require('../../config/config'); + /** */ var TransactionSchema = new Schema({ + // For now we keep this as short as possible + // More fields will be propably added as we move + // forward with the UX txid: { type: String, index: true, unique: true, }, - version: Number, - locktime: Number, - vin: { - type: Array, - default: [], - }, - vout: { - type: Array, - default: [], - }, - blockhash: { - type: String, - index: true, - default: null, - }, - confirmations: Number, - time: Number, - blocktime: Number, }); /** @@ -46,19 +34,67 @@ TransactionSchema.statics.load = function(id, cb) { }; -TransactionSchema.statics.fromID = function(txid, cb) { +TransactionSchema.statics.fromId = function(txid, cb) { this.findOne({ txid: txid, }).exec(cb); }; -/* - * virtual - */ +TransactionSchema.statics.fromIdWithInfo = function(txid, cb) { + this.fromId(txid, function(err, tx) { + if (err) return cb(err); + + tx.getInfo(function(err) { return cb(err,tx); } ); + }); +}; + +TransactionSchema.statics.createFromArray = function(txs, next) { + + var that = this; + + if (!txs) return next(); + +// console.log('exploding ', txs); + + async.forEach( txs, + function(tx, callback) { + // console.log('procesing TX %s', tx); + that.create({ txid: tx }, function(err) { + if (err && ! err.toString().match(/E11000/)) { + return callback(); + } + if (err) { + + return callback(err); + } + return callback(); + + }); + }, + function(err) { + if (err) return next(err); + return next(); + } + ); +}; + + + +TransactionSchema.methods.getInfo = function (next) { + + var that = this; + var rpc = new RpcClient(config.bitcoind); + + rpc.getRawTransaction(this.txid, 1, function(err, txInfo) { + if (err) return next(err); + that.info = txInfo.result; + + //console.log("THAT", that); + return next(null, that.info); + }); +}; + + -// ugly? new object every call? -TransactionSchema.virtual('date').get(function () { - return new Date(this.time); -}); module.exports = mongoose.model('Transaction', TransactionSchema); diff --git a/Sync.js b/lib/Sync.js similarity index 87% rename from Sync.js rename to lib/Sync.js index ae7d23e..6fb58cd 100644 --- a/Sync.js +++ b/lib/Sync.js @@ -1,5 +1,10 @@ require('classtool'); + +/* We dont sync any contents from TXs, only their IDs are stored */ + +var isSyncTxEnabled = 0; + function spec(b) { var mongoose = require('mongoose'); var util = require('util'); @@ -8,9 +13,9 @@ function spec(b) { var networks = require('bitcore/networks'); var async = require('async'); - var config = require('./config/config'); - var Block = require('./app/models/Block'); - var Transaction=require('./app/models/Transaction'); + var config = require('../config/config'); + var Block = require('../app/models/Block'); + var Transaction=require('../app/models/Transaction'); function Sync(config) { this.network = config.networkName == 'testnet' ? networks.testnet : networks.livenet; @@ -46,7 +51,7 @@ function spec(b) { } if (inBlock) { - inBlock.explodeTransactions(function (err) { + Transaction.createFromArray( blockInfo.result.tx,function (err) { return that.getNextBlock(blockInfo.result.nextblockhash, cb); }); } @@ -66,7 +71,7 @@ function spec(b) { return this.getNextBlock(genesisHash, cb); - Block.findOne({}, {}, { sort: { 'confirmations' : 1 } }, function(err, block) { + Block.findOne({}, {}, { sort: { 'time' : -1 } }, function(err, block) { if (err) return cb(err); var nextHash = @@ -82,6 +87,8 @@ function spec(b) { } + // This is not currently used. Transactions are represented by txid only + // in mongodb Sync.prototype.syncTXs = function (reindex, cb) { var that = this; @@ -145,7 +152,7 @@ function spec(b) { mongoose.connect(config.db); var db = mongoose.connection; - this.rpc = new RpcClient(config.bitcoind); + this.rpc = new RpcClient(config.bitcoind); var that = this; @@ -157,14 +164,14 @@ function spec(b) { function(cb){ if (opts.destroy) { console.log("Deleting Blocks..."); - return Block.remove().exec(cb); + return db.collections['blocks'].drop(cb); } return cb(); }, function(cb){ if (opts.destroy) { console.log("Deleting TXs..."); - return Transaction.remove().exec(cb); + return db.collections['transactions'].drop(cb); } return cb(); }, @@ -186,7 +193,7 @@ function spec(b) { } }, function(cb) { - if (! opts.skip_txs) { + if ( isSyncTxEnabled && ! opts.skip_txs) { that.syncTXs(opts.reindex, function(err) { if (err) { return cb(err); diff --git a/test/mocha.opts b/test/mocha.opts index 74590dc..a9caeb4 100644 --- a/test/mocha.opts +++ b/test/mocha.opts @@ -1,4 +1,5 @@ --require should -R spec --ui bdd +--recursive diff --git a/test/model/block.js b/test/model/block.js new file mode 100644 index 0000000..6a2b5f0 --- /dev/null +++ b/test/model/block.js @@ -0,0 +1,47 @@ +#!/usr/bin/env node + +process.env.NODE_ENV = process.env.NODE_ENV || 'development'; + + +var TESTING_BLOCK = '0000000000b6288775bbd326bedf324ca8717a15191da58391535408205aada4'; + +var + mongoose= require('mongoose'), + assert = require('assert'), + config = require('../../config/config'), + Block = require('../../app/models/Block'); + + +mongoose.connection.on('error', function(err) { console.log(err); }); + +describe('Block getInfo', function(){ + + before(function(done) { + mongoose.connect(config.db); + done(); + }); + + after(function(done) { + mongoose.connection.close(); + done(); + }); + + it('should poll block\'s info from mongoose', function(done) { + var block2 = Block.fromHashWithInfo(TESTING_BLOCK, function(err, b2) { + if (err) done(err); + + assert.equal(b2.hash, TESTING_BLOCK); + done(); + }); + }); + + it('should poll block\'s info from bitcoind', function(done) { + var block2 = Block.fromHashWithInfo(TESTING_BLOCK, function(err, b2) { + if (err) done(err); + assert.equal(b2.info.hash, TESTING_BLOCK); + assert.equal(b2.info.chainwork, '00000000000000000000000000000000000000000000000000446af21d50acd3'); + done(); + }); + }); +}); + diff --git a/test/model/transaction.js b/test/model/transaction.js new file mode 100644 index 0000000..bef066f --- /dev/null +++ b/test/model/transaction.js @@ -0,0 +1,46 @@ +#!/usr/bin/env node + +process.env.NODE_ENV = process.env.NODE_ENV || 'development'; + + +var TESTING_TX = '9f4648538a8fd773029139f7e67cee51586bced78d7ff0388d10cb71096f2289'; + +var + mongoose= require('mongoose'), + assert = require('assert'), + config = require('../../config/config'), + Transaction = require('../../app/models/Transaction'); + + +mongoose.connection.on('error', function(err) { console.log(err); }); + +describe('Transaction getInfo', function(){ + + before(function(done) { + mongoose.connect(config.db); + done(); + }); + + after(function(done) { + mongoose.connection.close(); + done(); + }); + + it('should pool tx\'s object from mongoose', function(done) { + Transaction.fromIdWithInfo(TESTING_TX, function(err, tx) { + if (err) done(err); + assert.equal(tx.txid, TESTING_TX); + done(); + }); + }); + + it('should pool tx\'s info from bitcoind', function(done) { + Transaction.fromIdWithInfo(TESTING_TX, function(err, tx) { + if (err) done(err); + assert.equal(tx.info.txid, TESTING_TX); + assert.equal(tx.info.blockhash, '000000007af2a08af7ce4934167dc2afd7a2e6bfd31472332db02a6f38cb7b4d'); + done(); + }); + }); +}); + diff --git a/test/test.js b/test/test.js deleted file mode 100644 index bab25b6..0000000 --- a/test/test.js +++ /dev/null @@ -1,9 +0,0 @@ -var assert = require("assert") -describe('Array', function(){ - describe('#indexOf()', function(){ - it('should return -1 when the value is not present', function(){ - assert.equal(-1, [1,2,3].indexOf(5)); - assert.equal(-1, [1,2,3].indexOf(0)); - }) - }) -}) diff --git a/util/sync.js b/util/sync.js index 22d1a53..c13562a 100755 --- a/util/sync.js +++ b/util/sync.js @@ -6,7 +6,7 @@ require('buffertools').extend(); var SYNC_VERSION = '0.1'; var program = require('commander'); -var Sync = require('../Sync').class(); +var Sync = require('../lib/Sync').class(); program .version(SYNC_VERSION)