diff --git a/app/models/Address.js b/app/models/Address.js index e6717e73..9c5651b9 100644 --- a/app/models/Address.js +++ b/app/models/Address.js @@ -9,8 +9,8 @@ function spec() { var BitcoreUtil = require('bitcore/util/util'); var TransactionDb = require('../../lib/TransactionDb').class(); - function Address(addrStr) { - this.tdb = new TransactionDb(); + function Address(addrStr, txDb) { + this.txDb = txDb || new TransactionDb(); this.balanceSat = 0; this.totalReceivedSat = 0; this.totalSentSat = 0; @@ -58,35 +58,24 @@ function spec() { Address.prototype.update = function(next) { var self = this; async.series([ -/* function (cb) { - TransactionIn.find({addr:self.addrStr}).exec(function(err,txIn){ - if (err) return cb(err); - - txIn.forEach(function(txItem){ - - self.balanceSat += txItem.value_sat; - self.totalReceivedSat += txItem.value_sat; - }); - return cb(); - }); - }, -*/ function (cb) { - self.tdb.fromAddr(self.addrStr, function(err,txOut){ + self.txDb.fromAddr(self.addrStr, function(err,txOut){ if (err) return cb(err); txOut.forEach(function(txItem){ - self.totalReceivedSat += txItem.value_sat; + var v = parseInt(txItem.value_sat); + + self.totalReceivedSat += v; self.transactions.push(txItem.txid); if (! txItem.spendTxId) { // unspent - self.balanceSat += txItem.value_sat; + self.balanceSat += v; self.txApperances +=1; } else { // spent - self.totalSentSat += txItem.value_sat; + self.totalSentSat += v; self.transactions.push(txItem.spendTxid); self.txApperances +=2; } diff --git a/app/models/Transaction.js b/app/models/Transaction.js deleted file mode 100644 index b752cfdf..00000000 --- a/app/models/Transaction.js +++ /dev/null @@ -1,82 +0,0 @@ -'use strict'; - -require('classtool'); - - -function spec() { - - var util = require('bitcore/util/util'), - TransactionRpc = require('../../lib/TransactionRpc').class(), - TransactionOut = require('../../lib/TransactionDb'), - async = require('async'); - - var CONCURRENCY = 20; - - function Transaction() { - this.txid = null; - } - - Transaction.fromIdWithInfo = function (txid,cb) { - var tx = new Transaction(); - tx.txid = txid; - - tx._fillInfo(function(err) { - if (err) return cb(err); - if (! tx.info ) return cb(); - - return cb(err,tx); - }); - }; - - Transaction.prototype._fillInfo = function(next) { - var self = this; - - TransactionRpc.getRpcInfo(self.txid, function(err, info) { - if (err) return next(err); - Transaction._fillOutpoints(info, function() { - self.info = info; - return next(); - }); - }); - }; - - Transaction._fillOutpoints = function(info, cb) { - - if (!info || info.isCoinBase) return cb(); - - var valueIn = 0; - var incompleteInputs = 0; - async.eachLimit(info.vin, CONCURRENCY, function(i, c_in) { - TransactionOut.fromTxIdN(i.txid, i.vout, function(err, addr, valueSat) { - - - if (err || !addr || !valueSat ) { - console.log('Could not get TXouts in %s,%d from %s ', i.txid, i.vout, info.txid); - incompleteInputs = 1; - return c_in(); // error not scaled - } - i.addr = addr; - i.valueSat = valueSat; - i.value = valueSat / util.COIN; - - valueIn += i.valueSat; - return c_in(); - }); - }, - function () { - if (! incompleteInputs ) { - info.valueIn = valueIn / util.COIN; - info.fees = (valueIn - parseInt(info.valueOut * util.COIN)) / util.COIN ; - } - else { - info.incompleteInputs = 1; - } - return cb(); - }); - }; - - return Transaction; -} -module.defineClass(spec); - - diff --git a/dev-util/get_tx.js b/dev-util/get_tx.js index a937c7f0..c30f0f60 100755 --- a/dev-util/get_tx.js +++ b/dev-util/get_tx.js @@ -2,9 +2,7 @@ 'use strict'; var util = require('util'); -var T = require('../app/models/Transaction').class(); -var mongoose= require('mongoose'), - config = require('../config/config'); +var T = require('../lib/TransactionDb').class(); process.env.NODE_ENV = process.env.NODE_ENV || 'development'; @@ -14,21 +12,15 @@ var hash = process.argv[2] || 'e2253359458db3e732c82a43fc62f56979ff59928f25a2df3 +var t = new T(); +t.fromIdWithInfo(hash, function(err, ret) { -mongoose.connect(config.db); -mongoose.connection.on('error', function(err) { console.log('error!', err); }); -mongoose.connection.on('open', function() { - - T.fromIdWithInfo(hash, function(err, ret) { - - console.log('Err:'); - console.log(err); + console.log('Err:'); + console.log(err); - console.log('Ret:'); - console.log(util.inspect(ret,{depth:null})); - mongoose.connection.close(); - }); + console.log('Ret:'); + console.log(util.inspect(ret,{depth:null})); }); diff --git a/dev-util/sync-level.js b/dev-util/sync-level.js new file mode 100644 index 00000000..2f2f95b9 --- /dev/null +++ b/dev-util/sync-level.js @@ -0,0 +1,17 @@ +#!/usr/bin/env node +'use strict'; + +var Sync = require('../lib/Sync').class(); + + +var s = new Sync(); + + +s.setOrphan( + '0000000000c2b1e8dab92a72741289e5ef0d4f375fd1b26f729da2ba979c028a', + '000000000228f9d02654459e09998c7557afa9082784c11226853f5feb805df9', + function (err) { + console.log('[sync-level.js.15]',err); //TODO + }); + + diff --git a/dev-util/tx-level.js b/dev-util/tx-level.js new file mode 100755 index 00000000..a90cc2ef --- /dev/null +++ b/dev-util/tx-level.js @@ -0,0 +1,19 @@ +#!/usr/bin/env node + +var + config = require('../config/config'), + levelup = require('levelup'); + + +db = levelup(config.leveldb + '/txs'); + +var s = 'txouts-addr-mgqvRGJMwR9JU5VhJ3x9uX9MTkzTsmmDgQ'; +db.createReadStream({start: s, end: s+'~'}) + .on('data', function (data) { +console.log('[block-level.js.11:data:]',data); //TODO + if (data==false) c++; + }) + .on('end', function () { + }); + + diff --git a/lib/BlockDb.js b/lib/BlockDb.js index ca37c705..81c57ebf 100644 --- a/lib/BlockDb.js +++ b/lib/BlockDb.js @@ -5,8 +5,8 @@ require('classtool'); function spec() { - var TIMESTAMP_ROOT = 'b-ts-'; - var PREV_ROOT = 'b-prev-'; // b-prev- => (0 if orphan) + var TIMESTAMP_ROOT = 'b-ts-'; // b-ts- => + var PREV_ROOT = 'b-prev-'; // b-prev- => (0 if orphan) /** @@ -44,11 +44,12 @@ function spec() { self.db.batch() .put(time_key, b.hash) - .put(PREV_ROOT + b.hash, b.prev_block) + .put(PREV_ROOT + b.hash, b.previousblockhash) .write(cb); }; + BlockDb.prototype.setOrphan = function(hash, cb) { var self = this; @@ -80,9 +81,9 @@ function spec() { BlockDb.prototype.countNotOrphan = function(cb) { - var self = this; var c = 0; - this.db.createReadStream({start: PREV_ROOT}) + console.log('Counting connected blocks. This could take some minutes'); + this.db.createReadStream({start: PREV_ROOT, end: PREV_ROOT + '~' }) .on('data', function (data) { if (data.value !== 0) c++; }) @@ -90,7 +91,7 @@ function spec() { return cb(err); }) .on('end', function () { - return cb(null); + return cb(null, c); }); }; diff --git a/lib/BlockExtractor.js b/lib/BlockExtractor.js index 857ed71b..92c6740e 100644 --- a/lib/BlockExtractor.js +++ b/lib/BlockExtractor.js @@ -113,7 +113,7 @@ function spec() { magic = self.currentParser ? self.currentParser.buffer(4).toString('hex') : null ; - if (!self.currentParser || self.currentParser.eof()) { + if (!self.currentParser || self.currentParser.eof() || magic === '00000000') { magic = null; if (self.nextFile()) { console.log('Moving forward to file:' + self.currentFile() ); @@ -121,7 +121,8 @@ function spec() { } else { console.log('Finished all files'); - return cb(); + magic = null; + return w_cb(); } } else { @@ -130,6 +131,8 @@ function spec() { }, a_cb); }, function (a_cb) { + if (!magic) return a_cb(); + if (magic !== self.magic) { var e = new Error('CRITICAL ERROR: Magic number mismatch: ' + magic + '!=' + self.magic); @@ -141,6 +144,8 @@ function spec() { return a_cb(); }, function (a_cb) { + if (!magic) return a_cb(); + b = new Block(); b.parse(self.currentParser); b.getHash(); diff --git a/lib/HistoricSync.js b/lib/HistoricSync.js index b84ca9e4..b0b541d9 100644 --- a/lib/HistoricSync.js +++ b/lib/HistoricSync.js @@ -9,7 +9,6 @@ function spec() { var RpcClient = require('bitcore/RpcClient').class(); var bitutil = require('bitcore/util/util'); var Address = require('bitcore/Address').class(); - var Deserialize = require('bitcore/Deserialize'); var Script = require('bitcore/Script').class(); var networks = require('bitcore/networks'); var async = require('async'); @@ -17,6 +16,7 @@ function spec() { var Sync = require('./Sync').class(); var sockets = require('../app/controllers/socket.js'); var BlockExtractor = require('./BlockExtractor.js').class(); + //var Deserialize = require('bitcore/Deserialize'); var BAD_GEN_ERROR = 'Bad genesis block. Network mismatch between Insight and bitcoind? Insight is configured for:'; @@ -37,6 +37,7 @@ function spec() { this.syncedBlocks = 0; this.skippedBlocks = 0; this.orphanBlocks = 0; + this.type =''; } function p() { @@ -97,6 +98,7 @@ function spec() { syncedBlocks: this.syncedBlocks, orphanBlocks: this.orphanBlocks, error: this.error, + type: this.type, }; }; @@ -115,13 +117,6 @@ function spec() { if (self.opts.shouldBroadcast) { sockets.broadcastSyncInfo(self.info()); } - -//TODO - /* -if (self.syncPercentage>10) { - console.log(self.info()); - process.exit(1); -}*/ }; HistoricSync.prototype.getPrevNextBlock = function(blockHash, blockEnd, scanOpts, cb) { @@ -136,7 +131,7 @@ if (self.syncPercentage>10) { async.series([ // Already got it? function(c) { - self.sync.hasBlock(blockHash, function(err, ret) { + self.sync.blockDb.has(blockHash, function(err, ret) { if (err) { p(err); return c(err); @@ -168,11 +163,7 @@ if (self.syncPercentage>10) { if (existed) return c(); self.sync.storeBlock(blockInfo, function(err) { - - existed = err && err.toString().match(/E11000/); - - if (err && ! existed) return c(err); - return c(); + return c(err); }); }, /* TODO: Should Start to sync backwards? (this is for partial syncs) @@ -247,9 +238,7 @@ if (self.syncPercentage>10) { addrStrs = [ addr.toString() ]; break; case Script.TX_MULTISIG: - var addrs = []; var chunks = s.capture(); - chunks.forEach(function(chunk) { var a = new Address(self.network.addressPubkey, bitutil.sha256ripe160(chunk)); addrStrs.push(a.toString()); @@ -262,15 +251,10 @@ if (self.syncPercentage>10) { return addrStrs; }; -var kk=0; - HistoricSync.prototype.getBlockFromFile = function(scanOpts, cb) { var self = this; - - var nextHash; var blockInfo; var existed; - async.series([ //show some (inacurate) status function(c) { @@ -287,12 +271,20 @@ var kk=0; if (err || ! b) return c(err); blockInfo = b.getStandardizedObject(b.txs, self.network); - blockInfo.curWork = Deserialize.intFromCompact(b.bits); + // blockInfo.curWork = Deserialize.intFromCompact(b.bits); + // We keep the RPC field names + blockInfo.previousblockhash = blockInfo.prev_block; var ti=0; // Get TX Address b.txs.forEach(function(t) { + + var objTx = blockInfo.tx[ti++]; + + //add time from block + objTx.time = blockInfo.time; + var to=0; t.outs.forEach( function(o) { @@ -311,31 +303,40 @@ var kk=0; return c(); }); }, - //store it + //check prev function(c) { - if (self.prevHash && blockInfo.prev_block !== self.prevHash) { + if (blockInfo && self.prevHash && blockInfo.previousblockhash !== self.prevHash) { - console.log('Orphans found: @%s', blockInfo.hash); - - console.log('From: %s To: %s', self.prevHash, blockInfo.prev_block); -process.exit(1); - self.sync.setOrphan(self.prevHash, blockInfo.prev_block, c); + console.log('Hole found @%s', blockInfo.hash); + console.log('From: %s To: %s', self.prevHash, blockInfo.previousblockhash); + self.sync.checkOrphan(self.prevHash, blockInfo.previousblockhash, c); } else return c(); }, + //check it + function(c) { + if (!blockInfo) return c(); + + self.sync.blockDb.has(blockInfo.hash, function(err, had) { + existed = had; + return c(err); + }); + }, //store it function(c) { - self.sync.storeBlock(blockInfo, function(err) { - existed = err && err.toString().match(/E11000/); + if (!blockInfo || existed) return c(); - if (err && ! existed) return c(err); - return c(); + self.sync.storeBlock(blockInfo, function(err) { + return c(err); }); }, function(c) { if (blockInfo && blockInfo.hash) { self.prevHash = blockInfo.hash; - self.syncedBlocks++; + if (existed) + self.skippedBlocks++; + else + self.syncedBlocks++; } else self.status = 'finished'; @@ -343,13 +344,27 @@ process.exit(1); }, ], function(err) { if (err) { - self.setError(util.format('ERROR: @%s: %s [count: syncedBlocks: %d]', blockInfo.hash, err, self.syncedBlocks)); + self.setError(util.format('ERROR: @%s: %s [count: syncedBlocks: %d]', blockInfo ? blockInfo.hash : '-', err, self.syncedBlocks)); } return cb(err); }); }; + HistoricSync.prototype.countNotOrphan = function(cb) { + var self = this; + + if (self.notOrphanCount) return cb(null, self.notOrphanCount); + + + self.sync.blockDb.countNotOrphan(function(err, count) { + if (err) return cb(err); + self.notOrphanCount = count; + return cb(null, self.notOrphanCount); + }); + }; + + HistoricSync.prototype.getBlockCount = function(cb) { var self = this; @@ -390,7 +405,7 @@ process.exit(1); function(cb) { if (scanOpts.upToExisting) { // should be isOrphan = true or null to be more accurate. - self.sync.countNotOrphan(function(err, count) { + self.countNotOrphan(function(err, count) { if (err) return cb(err); self.syncedBlocks = count || 0; @@ -437,10 +452,8 @@ process.exit(1); p(' scanOpts: ', JSON.stringify(scanOpts)); if (scanOpts.fromFiles) { - - var height = 0; - self.status = 'syncing'; + self.type = 'from .dat Files'; async.whilst(function() { return self.status === 'syncing'; }, function (w_cb) { @@ -454,6 +467,7 @@ process.exit(1); }); } else { + self.type = 'from RPC calls'; self.getPrevNextBlock(start, end, scanOpts, function(err) { return next(err); }); @@ -466,19 +480,19 @@ process.exit(1); HistoricSync.prototype.smartImport = function(scanOpts, next) { var self = this; - self.sync.hasBlock(self.genesis, function(err, b) { + self.sync.blockDb.has(self.genesis, function(err, b) { if (err) return next(err); - self.sync.countNotOrphan(function(err, count) { + self.countNotOrphan(function(err, count) { if (err) return next(err); self.getBlockCount(function(err) { if (err) return next(err); - if (!b || scanOpts.destroy || self.blockChainHeight > count * 0.7 ) { + if (!b || scanOpts.destroy || count < self.blockChainHeight * 0.8 ) { if (!b) p('Could not find Genesis block. Running FULL SYNC'); else - p('Less that 70% of current blockchain is stored. Running FULL SYNC', + p('Less that 80% of current blockchain is stored. Running FULL SYNC', parseInt(count/self.blockChainHeight*100)); if (config.bitcoind.dataDir) { @@ -492,6 +506,7 @@ process.exit(1); } else { p('Genesis block found. Syncing upto known blocks.'); + p('Got %d out of %d blocks', count, self.blockChainHeight); scanOpts.reverse = true; scanOpts.upToExisting = true; } diff --git a/lib/Sync.js b/lib/Sync.js index f2aa9661..eec0362c 100644 --- a/lib/Sync.js +++ b/lib/Sync.js @@ -34,24 +34,12 @@ function spec() { Sync.prototype.destroy = function(next) { var self = this; async.series([ - function(b) { try {self.blockDb.drop(b);} catch (e) { return b(); } }, - function(b) { try {self.TransactionDb.drop(b);} catch (e) { return b(); } }, + function(b) { self.blockDb.drop(b); }, + function(b) { self.txDb.drop(b); }, ], next); }; - Sync.prototype.hasBlock = function(hash, cb) { - var self = this; - - return self.blockDb.has(hash, cb); - }; - - Sync.prototype.countNotOrphan = function(hash, cb) { - var self = this; - return self.blockDb.countNotOrphan(hash, cb); - }; - - Sync.prototype.storeBlock = function(block, cb) { var self = this; @@ -66,23 +54,41 @@ function spec() { }); }; - Sync.prototype.setOrphan = function(fromBlock, toBlock, c) { + Sync.prototype.checkOrphan = function(fromBlock, toBlock, c) { var self = this; var hash = fromBlock; + var co = 0; + var limit = 10; + var cont = 1; + async.whilst( function () { - return hash && hash !== toBlock; + if (++co > limit) { +console.log('[Sync.js.109] WARN: Reach reog depth limit'); //TODO + } + return cont && hash && hash !== toBlock && co < limit; }, function (w_c) { - self.txDb.setOrphan(hash, function(err, insertedTxs, updateAddrs) { - if (err) return w_c(err); - self.blockDb.setOrphan(hash, function(err, prevHash){ + //check with RPC if the block is mainchain + self.blockDb.fromHashWithInfo(hash, function (err, info) { - hash = prevHash; - return w_c(err); - }); + if (!info) { + console.log('[Sync.js.107:hash:ORPHAN]',hash); //TODO + self.txDb.setOrphan(hash, function(err) { + if (err) return w_c(err); + self.blockDb.setOrphan(hash, function(err, prevHash){ + hash = prevHash; + return w_c(err); + }); + }); + } + else { + console.log('[Sync.js.107:hash:NOT ORPHAN]',hash); //TODO + cont = 0; + return w_c(); + } }); }, function (err) { diff --git a/lib/TransactionDb.js b/lib/TransactionDb.js index f3ab69b8..e5a8753d 100644 --- a/lib/TransactionDb.js +++ b/lib/TransactionDb.js @@ -5,8 +5,8 @@ require('classtool'); function spec() { - // blockHash -> txid mapping (to orphanize )/ - var ROOT = 'tx-b-'; //tx-b- => txid + // blockHash -> txid mapping (to reorgs) + var ROOT = 'tx-b-'; //tx-b-- => 1 // to show tx outs var OUTS_ROOT = 'txouts-'; //txouts-- => [addr, btc_sat] @@ -26,7 +26,8 @@ function spec() { util = require('bitcore/util/util'), levelup = require('levelup'), async = require('async'), - config = require('../config/config'); + config = require('../config/config'), + assert = require('assert'); var TransactionDb = function() { this.db = levelup(config.leveldb + '/txs'); @@ -91,12 +92,16 @@ function spec() { .on('data', function (data) { var k = data.key.split('-'); var v = data.value.split(':'); + var set=0; for(var i=0; i= 0) { - toRm.push(data.key); - console.log('To Remove Found', data.key); //TODO - } - }) - .on('end', function() { - return s_c(); - }); - }, - function(s_c) { - async.each(toRm, function(k,e_c) { - self.db.del(k,e_c); - }, s_c); + .pipe( + self.db.createWriteStream({type:'del'}) + ).on('close', c); }], function(err) { cb(err); @@ -291,7 +340,7 @@ console.log('[TransactionDb.js.165]', ret); //TODO // } var addr = o.scriptPubKey.addresses[0]; - var sat = o.value * util.COIN; + var sat = Math.round(o.value * util.COIN); self.db.batch() .put( OUTS_ROOT + tx.txid + '-' + o.n, addr + ':' + sat) .put( ADDR_ROOT + addr + '-' + ts + '-' + tx.txid + @@ -343,7 +392,7 @@ console.log('[TransactionDb.js.165]', ret); //TODO self.add(inInfo, function(err) { if (err || !blockHash) return each_cb(err); - self.db.put(ROOT + blockHash, t, function(err) { + self.db.put(ROOT + t + '-' + blockHash, 1, function(err) { return each_cb(err); }); }); @@ -353,7 +402,7 @@ console.log('[TransactionDb.js.165]', ret); //TODO self.add(t, function(err) { if (err) return each_cb(err); - self.db.put(ROOT + blockHash, t.txid, function(err) { + self.db.put(ROOT + t.txid + '-' + blockHash, 1, function(err) { return each_cb(err); }); }); @@ -368,7 +417,7 @@ console.log('[TransactionDb.js.165]', ret); //TODO // txs can be a [hashes] or [txObjects] TransactionDb.prototype.createFromBlock = function(b, next) { var self = this; - if (!b.tx) return next(); + if (!b || !b.tx) return next(); return self.createFromArray(b.tx, b.hash, next); }; diff --git a/test/integration/01-transactionouts.js b/test/integration/01-transactionouts.js index b9e5ef5c..c59de6b7 100644 --- a/test/integration/01-transactionouts.js +++ b/test/integration/01-transactionouts.js @@ -5,18 +5,116 @@ process.env.NODE_ENV = process.env.NODE_ENV || 'development'; -var - assert = require('assert'), +var assert = require('assert'), fs = require('fs'), util = require('util'), - config = require('../../config/config'), TransactionDb = require('../../lib/TransactionDb').class(); var txItemsValid = JSON.parse(fs.readFileSync('test/integration/txitems.json')); +var txDb; -describe('TransactionDb', function(){ +describe('TransactionDb fromIdWithInfo', function(){ - var tdb = new TransactionDb(); + before(function(c) { + txDb = new TransactionDb(); + return c(); + }); + + var txid = '7e621eeb02874ab039a8566fd36f4591e65eca65313875221842c53de6907d6c'; + it('tx info ' + txid, function(done) { + txDb.fromIdWithInfo(txid, function(err, tx) { + + if (err) done(err); + assert.equal(tx.txid, txid); + assert(!tx.info.isCoinBase); + + for(var i=0; i<20; i++) + assert(parseFloat(tx.info.vin[i].value) === parseFloat(50), 'input '+i); + assert(tx.info.vin[0].addr === 'msGKGCy2i8wbKS5Fo1LbWUTJnf1GoFFG59', 'addr 0'); + assert(tx.info.vin[1].addr === 'mfye7oHsdrHbydtj4coPXCasKad2eYSv5P', 'addr 1'); + done(); + }); + }); + + it('tx info', function(done) { + var txid = '21798ddc9664ac0ef618f52b151dda82dafaf2e26d2bbef6cdaf55a6957ca237'; + txDb.fromIdWithInfo(txid, function(err, tx) { + if (err) done(err); + assert.equal(tx.txid, txid); + assert(!tx.info.isCoinBase); + done(); + }); + }); + + it('should pool tx\'s info from bitcoind', function(done) { + var txid = '21798ddc9664ac0ef618f52b151dda82dafaf2e26d2bbef6cdaf55a6957ca237'; + txDb.fromIdWithInfo(txid, function(err, tx) { + if (err) done(err); + assert.equal(tx.info.txid, txid); + assert.equal(tx.info.blockhash, '000000000185678d3d7ecc9962c96418174431f93fe20bf216d5565272423f74'); + assert.equal(tx.info.valueOut, 1.66174); + assert.equal(tx.info.fees, 0.0005 ); + assert.equal(tx.info.size, 226 ); + assert(!tx.info.isCoinBase); + done(); + }); + }); + + var txid1 = '2a104bab1782e9b6445583296d4a0ecc8af304e4769ceb64b890e8219c562399'; + it('test a coinbase TX ' + txid1, function(done) { + txDb.fromIdWithInfo(txid1, function(err, tx) { + if (err) done(err); + assert(tx.info.isCoinBase); + assert.equal(tx.info.txid, txid1); + assert(!tx.info.feeds); + done(); + }); + }); + var txid22 = '666'; + it('test invalid TX ' + txid22, function(done) { + txDb.fromIdWithInfo(txid22, function(err, tx) { + if (err && err.message.match(/must.be.hexadecimal/)) { + return done(); + } + else { + return done(err); + } + }); + }); + + var txid23 = '21798ddc9664ac0ef618f52b151dda82dafaf2e26d2bbef6cdaf55a6957ca227'; + it('test unexisting TX ' + txid23, function(done) { + + txDb.fromIdWithInfo(txid23, function(err, tx) { + assert(!err); + assert(!tx); + return done(); + }); + }); + + + + var txid2 = '64496d005faee77ac5a18866f50af6b8dd1f60107d6795df34c402747af98608'; + it('create TX on the fly ' + txid2, function(done) { + txDb.fromIdWithInfo(txid2, function(err, tx) { + if (err) return done(err); + assert.equal(tx.info.txid, txid2); + done(); + }); + }); + + txid2 = '64496d005faee77ac5a18866f50af6b8dd1f60107d6795df34c402747af98608'; + it('test a broken TX ' + txid2, function(done) { + txDb.fromIdWithInfo(txid2, function(err, tx) { + if (err) return done(err); + assert.equal(tx.info.txid, txid2); + assert.equal(tx.info.vin[0].addr, 'n1JagbRWBDi6VMvG7HfZmXX74dB9eiHJzU'); + done(); + }); + }); +}); + +describe('TransactionDb Outs', function(){ txItemsValid.forEach( function(v) { if (v.disabled) return; @@ -24,16 +122,16 @@ describe('TransactionDb', function(){ this.timeout(60000); // Remove first - tdb.removeFromTxId(v.txid, v.toRm, function() { + txDb.removeFromTxId(v.txid, function() { - tdb.fromTxId( v.txid, function(err, readItems) { + txDb.fromTxId( v.txid, function(err, readItems) { assert.equal(readItems.length,0); var unmatch=[]; - tdb.createFromArray([v.txid], null, function(err) { + txDb.createFromArray([v.txid], null, function(err) { if (err) return done(err); - tdb.fromTxId( v.txid, function(err, readItems) { + txDb.fromTxId( v.txid, function(err, readItems) { v.items.forEach(function(validItem){ unmatch[validItem.addr] =1; @@ -64,4 +162,3 @@ describe('TransactionDb', function(){ }); - diff --git a/test/integration/addr.js b/test/integration/addr.js index 12396147..3f31bc6c 100644 --- a/test/integration/addr.js +++ b/test/integration/addr.js @@ -1,16 +1,23 @@ #!/usr/bin/env node +'use strict'; process.env.NODE_ENV = process.env.NODE_ENV || 'development'; -var - assert = require('assert'), +var assert = require('assert'), fs = require('fs'), - config = require('../../config/config'), - Address = require('../../app/models/Address').class(); + Address = require('../../app/models/Address').class(), + TransactionDb = require('../../lib/TransactionDb').class(), addrValid = JSON.parse(fs.readFileSync('test/integration/addr.json')); + var txDb; describe('Address balances', function(){ + before(function(c) { + txDb = new TransactionDb(); + return c(); + }); + + addrValid.forEach( function(v) { if (v.disabled) { console.log(v.addr + " => disabled in JSON"); @@ -19,7 +26,7 @@ describe('Address balances', function(){ it('Info for: ' + v.addr, function(done) { this.timeout(5000); - var a = new Address(v.addr); + var a = new Address(v.addr, txDb); a.update(function(err) { if (err) done(err); diff --git a/test/integration/addr.json b/test/integration/addr.json index fd174e6d..efd513e1 100644 --- a/test/integration/addr.json +++ b/test/integration/addr.json @@ -1,12 +1,6 @@ [ + { -"disabled":1, - "addr": "mgqvRGJMwR9JU5VhJ3x9uX9MTkzTsmmDgQ", - "balance": 43.1, - "txApperances": 19 - }, - { -"disabled":1, "addr": "mp3Rzxx9s1A21SY3sjJ3CQoa2Xjph7e5eS", "balance": 0, "totalReceived": 50, @@ -14,7 +8,6 @@ "txApperances": 2 }, { -"disabled":1, "addr": "muyg1K5WsHkfMVCkUXU2y7Xp5ZD6RGzCeH", "balance": 0.38571339, "totalReceived": 0.38571339, @@ -29,7 +22,6 @@ "txApperances": 13 }, { -"disabled":1, "addr": "n47CfqnKWdNwqY1UWxTmNJAqYutFxdH3zY", "balance": 0, "totalReceived":26.4245, @@ -37,7 +29,6 @@ "txApperances": 4 }, { -"disabled":1, "addr": "mzSyyXgofoBxpr6gYcU3cV345G8hJpixRd", "balance": 0, "totalReceived":3.9775, @@ -45,7 +36,11 @@ "txApperances": 2 }, { -"disabled":1, + "addr": "mgqvRGJMwR9JU5VhJ3x9uX9MTkzTsmmDgQ", + "balance": 43.1, + "txApperances": 19 + }, + { "addr": "mzW2hdZN2um7WBvTDerdahKqRgj3md9C29", "txApperances": 2034, "balance": 1049.69744099, @@ -53,7 +48,6 @@ "totalSent": 0 }, { -"disabled":1, "addr": "mjRmkmYzvZN3cA3aBKJgYJ65epn3WCG84H", "txApperances": 13343, "balance": 46413.0, @@ -61,7 +55,6 @@ "totalSent": 310717.17644359 }, { -"disabled":1, "addr": "mgKY35SXqxFpcKK3Dq9mW9919N7wYXvcFM", "txApperances": 1, "balance": 0.01979459, @@ -70,7 +63,6 @@ "transactions": [ "91800d80bb4c69b238c9bfd94eb5155ab821e6b25cae5c79903d12853bbb4ed5" ] }, { - "disabled":1, "addr": "mmvP3mTe53qxHdPqXEvdu8WdC7GfQ2vmx5", "balance": 10580.50027254, "totalReceived": 12157.65075053, diff --git a/test/integration/sync.js b/test/integration/sync.js index f0e25891..be6a078e 100644 --- a/test/integration/sync.js +++ b/test/integration/sync.js @@ -42,8 +42,25 @@ var test = function(s,cb) { +var testNo = function(s,cb) { + async.each([2,3,4], function(i,c) { + s.blockDb.getPrev(b[i], function(err, p) { + assert.equal(p,b[i-1]); + return c(); + }); + }, function() { + s.blockDb.getPrev(b[1], function(err, p) { + assert.equal(p,b[0]); + return cb(); + }); + }); +}; + + + + var s; -describe('Sync setOrphan', function(){ +describe('Sync checkOrphan', function(){ before(function(done) { s = new Sync(); @@ -54,15 +71,15 @@ describe('Sync setOrphan', function(){ fix(s,done); }); - it('setOrphan', function(done) { + it('checkOrphan', function(done) { this.timeout(100000); s.blockDb.has(b[0], function(err, has) { assert(has); s.blockDb.has(b[1], function(err, has) { assert(has); - s.setOrphan(b[4],b[1], function() { - test(s,done); + s.checkOrphan(b[4],b[1], function() { + testNo(s,done); }); }); }); diff --git a/test/integration/transaction.js b/test/integration/transaction.js deleted file mode 100644 index d5a87c06..00000000 --- a/test/integration/transaction.js +++ /dev/null @@ -1,120 +0,0 @@ -#!/usr/bin/env node -'use strict'; - -process.env.NODE_ENV = process.env.NODE_ENV || 'development'; - - - -var mongoose= require('mongoose'), - assert = require('assert'), - config = require('../../config/config'), - Transaction = require('../../app/models/Transaction').class(); - - -mongoose.connection.on('error', function(err) { console.log(err); }); - -describe('Transaction', function(){ - - before(function(done) { - mongoose.connect(config.db); - done(); - }); - - after(function(done) { - mongoose.connection.close(); - done(); - }); - var txid = '7e621eeb02874ab039a8566fd36f4591e65eca65313875221842c53de6907d6c'; - it('txid ' + txid, function(done) { - Transaction.fromIdWithInfo(txid, function(err, tx) { - if (err) done(err); - assert.equal(tx.txid, txid); - assert(!tx.info.isCoinBase); - - for(var i=0; i<20; i++) - assert(parseFloat(tx.info.vin[i].value) === parseFloat(50), 'input '+i); - assert(tx.info.vin[0].addr === 'msGKGCy2i8wbKS5Fo1LbWUTJnf1GoFFG59', 'addr 0'); - assert(tx.info.vin[1].addr === 'mfye7oHsdrHbydtj4coPXCasKad2eYSv5P', 'addr 1'); - done(); - }); - }); - - it('should pool tx\'s object from mongoose', function(done) { - var txid = '21798ddc9664ac0ef618f52b151dda82dafaf2e26d2bbef6cdaf55a6957ca237'; - Transaction.fromIdWithInfo(txid, function(err, tx) { - if (err) done(err); - assert.equal(tx.txid, txid); - assert(!tx.info.isCoinBase); - done(); - }); - }); - - it('should pool tx\'s info from bitcoind', function(done) { - var txid = '21798ddc9664ac0ef618f52b151dda82dafaf2e26d2bbef6cdaf55a6957ca237'; - Transaction.fromIdWithInfo(txid, function(err, tx) { - if (err) done(err); - assert.equal(tx.info.txid, txid); - assert.equal(tx.info.blockhash, '000000000185678d3d7ecc9962c96418174431f93fe20bf216d5565272423f74'); - assert.equal(tx.info.valueOut, 1.66174); - assert.equal(tx.info.fees, 0.0005 ); - assert.equal(tx.info.size, 226 ); - assert(!tx.info.isCoinBase); - done(); - }); - }); - - var txid1 = '2a104bab1782e9b6445583296d4a0ecc8af304e4769ceb64b890e8219c562399'; - it('test a coinbase TX ' + txid1, function(done) { - Transaction.fromIdWithInfo(txid1, function(err, tx) { - if (err) done(err); - assert(tx.info.isCoinBase); - assert.equal(tx.info.txid, txid1); - assert(!tx.info.feeds); - done(); - }); - }); - var txid22 = '666'; - it('test invalid TX ' + txid22, function(done) { - Transaction.fromIdWithInfo(txid22, function(err, tx) { - if (err && err.message.match(/must.be.hexadecimal/)) { - return done(); - } - else { - return done(err); - } - }); - }); - - var txid23 = '21798ddc9664ac0ef618f52b151dda82dafaf2e26d2bbef6cdaf55a6957ca227'; - it('test unexisting TX ' + txid23, function(done) { - - Transaction.fromIdWithInfo(txid23, function(err, tx) { - assert(!err); - assert(!tx); - return done(); - }); - }); - - - - var txid2 = '64496d005faee77ac5a18866f50af6b8dd1f60107d6795df34c402747af98608'; - it('create TX on the fly ' + txid2, function(done) { - Transaction.fromIdWithInfo(txid2, function(err, tx) { - if (err) return done(err); - assert.equal(tx.info.txid, txid2); - done(); - }); - }); - - var txid2 = '64496d005faee77ac5a18866f50af6b8dd1f60107d6795df34c402747af98608'; - it('test a broken TX ' + txid2, function(done) { - Transaction.fromIdWithInfo(txid2, function(err, tx) { - if (err) return done(err); - assert.equal(tx.info.txid, txid2); - assert.equal(tx.info.vin[0].addr, 'n1JagbRWBDi6VMvG7HfZmXX74dB9eiHJzU'); - done(); - }); - }); - -}); - diff --git a/test/integration/transactionouts-expenses.js b/test/integration/transactionouts-expenses.js deleted file mode 100644 index b8b04beb..00000000 --- a/test/integration/transactionouts-expenses.js +++ /dev/null @@ -1,67 +0,0 @@ -#!/usr/bin/env node -'use strict'; - -process.env.NODE_ENV = process.env.NODE_ENV || 'development'; - - - -var mongoose = require('mongoose'), - assert = require('assert'), - fs = require('fs'), - util = require('util'), - async = require('async'), - config = require('../../config/config'), - TransactionOut = require('../../app/models/TransactionOut'); - -var spentValid = JSON.parse(fs.readFileSync('test/model/spent.json')); - -mongoose.connection.on('error', function(err) { console.log(err); }); - - - -describe('TransactionOut Expenses', function(){ - - before(function(done) { - mongoose.connect(config.db); - - // lets spend! - async.each(Object.keys(spentValid), - function(txid,c_out) { - async.each(spentValid[txid], - function(i,c_in) { - TransactionOut._explodeTransactionOuts(i.txid, function(err) { - return c_in(); - }); - }, - function(err) { - return c_out(); - } - ); - }, - function(err) { - console.log('[transactionouts.js.88]'); //TODO - return done(); - } - ); - }); - - after(function(done) { - mongoose.connection.close(); - done(); - }); - - Object.keys(spentValid).forEach( function(txid) { - it('test result of spending tx ' + txid, function(done) { - var s = spentValid[txid]; - var c=0; - TransactionOut.fromTxId( txid, function(err, readItems) { - s.forEach( function(v) { - assert.equal(readItems[c].spendTxIdBuf.toString('hex'),v.txid); - assert.equal(readItems[c].spendIndex,v.n); - c++; - }); - done(); - }); - }); - }); -}); diff --git a/test/integration/txitems.json b/test/integration/txitems.json index eae6b116..bee3b43f 100644 --- a/test/integration/txitems.json +++ b/test/integration/txitems.json @@ -6,7 +6,8 @@ { "txid": "21798ddc9664ac0ef618f52b151dda82dafaf2e26d2bbef6cdaf55a6957ca237", "toRm": [ - "txouts-spend-86a03cac7d87f596008c6d5a8d3fd8b88842932ea6f0337673eda16f6b472f7f-0" + "txouts-spend-86a03cac7d87f596008c6d5a8d3fd8b88842932ea6f0337673eda16f6b472f7f-0", + "txouts-spend-bcd8da8ee847da377f8aaca92502c05e5f914c6a2452753146013b0e642a25a0-0" ], "items": [ {