From 6f2ffeff047e594e947bd08c091d73614475b8bf Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Wed, 29 Jan 2014 15:10:36 -0300 Subject: [PATCH] new DB schema. test updated --- app/models/Address.js | 50 ++-- app/models/Block.js | 16 +- app/models/Transaction.js | 188 +++------------ app/models/TransactionItem.js | 198 ---------------- app/models/TransactionOut.js | 215 ++++++++++++++++++ dev-util/explode_tx.js | 41 ++++ {util => dev-util}/find_ref.sh | 0 dev-util/get_block.js | 31 +++ dev-util/get_outs.js | 41 ++++ dev-util/get_outs_addr.js | 39 ++++ dev-util/get_tx.js | 35 +++ etc/bitcoind/bitcoin-testnet.conf | 7 +- insight.js | 4 +- lib/HistoricSync.js | 15 +- lib/Sync.js | 59 +++-- lib/TransactionRpc.js | 69 ++++++ public/js/controllers/transactions.js | 5 +- .../01-transactionouts.js} | 21 +- test/integration/02-transactionouts.js | 68 ++++++ test/{model => integration}/addr.js | 12 +- test/{model => integration}/addr.json | 26 ++- test/{model => integration}/block.js | 0 test/integration/spent.json | 32 +++ test/{model => integration}/status.js | 0 test/{model => integration}/transaction.js | 23 +- test/integration/transactionouts-expenses.js | 67 ++++++ test/{model => integration}/txitems.json | 20 -- util/get_block.js | 25 -- 28 files changed, 807 insertions(+), 500 deletions(-) delete mode 100644 app/models/TransactionItem.js create mode 100644 app/models/TransactionOut.js create mode 100755 dev-util/explode_tx.js rename {util => dev-util}/find_ref.sh (100%) create mode 100755 dev-util/get_block.js create mode 100755 dev-util/get_outs.js create mode 100755 dev-util/get_outs_addr.js create mode 100755 dev-util/get_tx.js create mode 100644 lib/TransactionRpc.js rename test/{model/transactionitems.js => integration/01-transactionouts.js} (78%) create mode 100644 test/integration/02-transactionouts.js rename test/{model => integration}/addr.js (78%) rename test/{model => integration}/addr.json (90%) rename test/{model => integration}/block.js (100%) create mode 100644 test/integration/spent.json rename test/{model => integration}/status.js (100%) rename test/{model => integration}/transaction.js (87%) create mode 100644 test/integration/transactionouts-expenses.js rename test/{model => integration}/txitems.json (69%) delete mode 100755 util/get_block.js diff --git a/app/models/Address.js b/app/models/Address.js index 09f86f9d..fd0f8d0f 100644 --- a/app/models/Address.js +++ b/app/models/Address.js @@ -5,7 +5,7 @@ require('classtool'); function spec() { var async = require('async'); - var TransactionItem = require('./TransactionItem'); + var TransactionOut = require('./TransactionOut'); var BitcoreAddress = require('bitcore/Address').class(); var BitcoreUtil = require('bitcore/util/util'); @@ -55,30 +55,44 @@ function spec() { } Address.prototype.update = function(next) { - var that = this; + var self = this; async.series([ - // TODO TXout! - //T - function (cb) { - TransactionItem.find({addr:that.addrStr}).exec(function(err,txItems){ +/* function (cb) { + TransactionIn.find({addr:self.addrStr}).exec(function(err,txIn){ if (err) return cb(err); - txItems.forEach(function(txItem){ + txIn.forEach(function(txItem){ -// console.log(txItem.txid + ':' + txItem.ts+ ' : ' + (txItem.value_sat/parseFloat(BitcoreUtil.COIN) ) ); - that.txApperances +=1; - that.balanceSat += txItem.value_sat; - - that.transactions.push(txItem.txid); - - if (txItem.value_sat > 0) - that.totalReceivedSat += txItem.value_sat; - else - that.totalSentSat += Math.abs(txItem.value_sat); + self.balanceSat += txItem.value_sat; + self.totalReceivedSat += txItem.value_sat; }); return cb(); }); - } + }, +*/ + function (cb) { + TransactionOut.find({addr:self.addrStr}).exec(function(err,txOut){ + if (err) return cb(err); + + txOut.forEach(function(txItem){ + + self.totalReceivedSat += txItem.value_sat; + self.transactions.push(txItem.txid); + if (! txItem.spendTxIdBuf) { + // unspent + self.balanceSat += txItem.value_sat; + self.txApperances +=1; + } + else { + // spent + self.totalSentSat += txItem.value_sat; + self.transactions.push(txItem.spendTxid); + self.txApperances +=2; + } + }); + return cb(); + }); + }, ], function (err) { return next(err); }); diff --git a/app/models/Block.js b/app/models/Block.js index 19152ec1..030131dc 100644 --- a/app/models/Block.js +++ b/app/models/Block.js @@ -8,7 +8,7 @@ var mongoose = require('mongoose'), RpcClient = require('bitcore/RpcClient').class(), util = require('bitcore/util/util'), BitcoreBlock= require('bitcore/Block').class(), - TransactionItem = require('./TransactionItem'), + TransactionOut = require('./TransactionOut'), config = require('../../config/config') ; @@ -41,8 +41,6 @@ BlockSchema.virtual('hash').set(function (hash) { }); - - BlockSchema.virtual('hashStr').get(function () { return this._id.toString('hex'); }); @@ -79,22 +77,21 @@ BlockSchema.path('title').validate(function(title) { */ BlockSchema.statics.customCreate = function(block, cb) { - - var That= this; + var Self= this; var BlockSchema = mongoose.model('Block', BlockSchema); - var newBlock = new That(); + var newBlock = new Self(); newBlock.time = block.time ? block.time : Math.round(new Date().getTime() / 1000); newBlock.hashStr = block.hash; newBlock.nextBlockHashStr = block.nextBlockHash; - TransactionItem.createFromArray(block.tx, function(err, inserted_txs) { + TransactionOut.createFromArray(block.tx, function(err, inserted_txs, update_addrs) { if (err) return cb(err); newBlock.save(function(err) { - return cb(err, newBlock, inserted_txs); + return cb(err, newBlock, inserted_txs, update_addrs); }); }); }; @@ -111,11 +108,10 @@ BlockSchema.statics.blockIndex = function(height, cb) { }; BlockSchema.statics.fromHash = function(hashStr, cb) { - var hash = new Buffer(hashStr, 'hex'); this.findOne({ - hash: hash, + _id: hash, }).exec(cb); }; diff --git a/app/models/Transaction.js b/app/models/Transaction.js index 245978aa..58a39e82 100644 --- a/app/models/Transaction.js +++ b/app/models/Transaction.js @@ -5,31 +5,22 @@ require('classtool'); function spec() { - var async = require('async'), - RpcClient = require('bitcore/RpcClient').class(), - BitcoreTransaction = require('bitcore/Transaction').class(), - Address = require('bitcore/Address').class(), - BitcoreBlock = require('bitcore/Block').class(), - networks = require('bitcore/networks'), - util = require('bitcore/util/util'), - bignum = require('bignum'), - config = require('../../config/config'), - sockets = require('../controllers/socket.js'), - TransactionItem = require('./TransactionItem'); - - var CONCURRENCY = 15; + var util = require('bitcore/util/util'), + TransactionRpc = require('../../lib/TransactionRpc').class(), + TransactionOut = require('./TransactionOut'), + 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) { + tx._fillInfo(function(err) { if (err) return cb(err); if (! tx.info ) return cb(); @@ -39,157 +30,52 @@ function spec() { - Transaction.prototype.fillInfo = function(next) { + Transaction.prototype._fillInfo = function(next) { var self = this; - Transaction.queryInfo(self.txid, function(err, info) { + TransactionRpc.getRpcInfo(self.txid, function(err, info) { if (err) return next(err); - - self.info = info; - return next(); + Transaction._fillOutpoints(info, function() { + self.info = info; + return next(); + }); }); }; - Transaction.getOutpoints = function (tx, next) { - if (tx.isCoinBase()) return next(); + Transaction._fillOutpoints = function(info, cb) { - var rpc = new RpcClient(config.bitcoind); - var network = ( config.network === 'testnet') ? networks.testnet : networks.livenet ; + if (!info || info.isCoinBase) return cb(); - async.forEachLimit(tx.ins, CONCURRENCY, function(i, cb) { + var valueIn = 0; + var incompleteInputs = 0; + async.eachLimit(info.vin, CONCURRENCY, function(i, c_in) { + TransactionOut.fromTxIdN(i.txid, i.vout, function(err, out) { - var outHash = i.getOutpointHash(); - var outIndex = i.getOutpointIndex(); - var outHashBase64 = outHash.reverse().toString('hex'); - - var c=0; - rpc.getRawTransaction(outHashBase64, function(err, txdata) { - var txin = new BitcoreTransaction(); - if (err || ! txdata.result) return cb( new Error('Input TX '+outHashBase64+' not found')); - - var b = new Buffer(txdata.result,'hex'); - txin.parse(b); - - /* - *We have to parse it anyways. It will have outputs even it is a coinbase tx - if ( txin.isCoinBase() ) { - return cb(); - } - */ - - txin.outs.forEach( function(j) { - // console.log( c + ': ' + util.formatValue(j.v) ); - if (c === outIndex) { - i.value = j.v; - - // This is used for pay-to-pubkey transaction in which - // the pubkey is not provided on the input - var scriptPubKey = j.getScript(); - var hash = scriptPubKey.simpleOutHash(); - if (hash) { - var addr = new Address(network.addressPubkey, hash); - i.addrFromOutput = addr.toString(); - } - } - c++; - }); - return cb(); - }); - }, - function(err) { - return next(err); - } - ); - }; - - - Transaction.queryInfo = function(txid, cb) { - var self = this; - var network = ( config.network === 'testnet') ? networks.testnet : networks.livenet ; - var rpc = new RpcClient(config.bitcoind); - - rpc.getRawTransaction(txid, 1, function(err, txInfo) { - - // Not found? - if (err && err.code === -5) return cb(); - - if (err) return cb(err); - - var info = txInfo.result; - - // Transaction parsing - var b = new Buffer(txInfo.result.hex,'hex'); - var tx = new BitcoreTransaction(); - tx.parse(b); - - self.getOutpoints(tx, function(err) { - if (err) return cb(err); - - // Copy TX relevant values to .info - - var c = 0; - var valueIn = bignum(0); - var valueOut = bignum(0); - - if ( tx.isCoinBase() ) { - info.isCoinBase = true; - } - else { - tx.ins.forEach(function(i) { - if (i.value) { - info.vin[c].value = parseFloat(util.formatValue(i.value)); - var n = util.valueToBigInt(i.value).toNumber(); - info.vin[c].valueSat = n; - valueIn = valueIn.add( n ); - - var scriptSig = i.getScript(); - var pubKey = scriptSig.simpleInPubKey(); - - // We check for pubKey in case a broken / strange TX. - if (pubKey) { - var pubKeyHash = util.sha256ripe160(pubKey); - var addr = new Address(network.addressPubkey, pubKeyHash); - var addrStr = addr.toString(); - info.vin[c].addr = addrStr; - } - else { - if (i.addrFromOutput) - info.vin[c].addr = i.addrFromOutput; - } - } - else { - console.log('TX could not be parsed: %s,%d' ,txInfo.result.txid, c); - } - c++; - }); + if (err || !out || ! out.addr) { + 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 } - c=0; - tx.outs.forEach( function(i) { - var n = util.valueToBigInt(i.v).toNumber(); - valueOut = valueOut.add(n); + i.addr = out.addr; + i.valueSat = out.value_sat; + i.value = out.value_sat / util.COIN; - info.vout[c].valueSat = n; - c++; - }); - - info.valueOut = valueOut / util.COIN; - - if ( !tx.isCoinBase() ) { - info.valueIn = valueIn / util.COIN; - info.fees = (valueIn - valueOut) / util.COIN; - } - else { - var reward = BitcoreBlock.getBlockValue(info.height) / util.COIN; - info.vin[0].reward = reward; - info.valueIn = reward; - } - - info.size = b.length; - return cb(null, info); + 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(); }); }; diff --git a/app/models/TransactionItem.js b/app/models/TransactionItem.js deleted file mode 100644 index 147f311f..00000000 --- a/app/models/TransactionItem.js +++ /dev/null @@ -1,198 +0,0 @@ -'use strict'; - -/** - * Module dependencies. - */ -var mongoose = require('mongoose'), - async = require('async'), - Transaction = require('./Transaction').class(), - sockets = require('../controllers/socket.js'), - Schema = mongoose.Schema; - -var CONCURRENCY = 15; -// TODO: use bitcore networks module -var genesisTXID = '4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b'; - -var TransactionItemSchema = new Schema({ - txidBuf: Buffer, - index: Number, - addr: { - type: String, - index: true, - }, - // OJO: mongoose doesnt accept camelcase for field names - // <0 is Input >0 is Output - value_sat: Number, - ts: Number, -}); - - -// Compound index -TransactionItemSchema.index({txidBuf: 1, index: 1, value_sat: 1}, {unique: true, dropDups: true}); - -TransactionItemSchema.virtual('txid').get(function () { - return this.txidBuf.toString('hex'); -}); - -TransactionItemSchema.virtual('txid').set(function (txidStr) { - if (txidStr) - this.txidBuf = new Buffer(txidStr,'hex'); - else - this.txidBuf = null; -}); - - - - - - - -TransactionItemSchema.statics.load = function(id, cb) { - this.findOne({ - _id: id - }).exec(cb); -}; - - -TransactionItemSchema.statics.fromTxId = function(txid, cb) { - this.find({ - txid: txid, - }).exec(function (err,items) { - - // sort by 1) value sign 2) index - return cb(err,items.sort(function(a,b){ - var sa= a.value_sat < 0 ? -1 : 1; - var sb= b.value_sat < 0 ? -1 : 1; - - if (sa !== sb) { - return sa-sb; - } - else { - return a.index - b.index; - } - })); - }); -}; - - -TransactionItemSchema.statics.explodeTransactionItems = function(txid, cb) { - - var Self = this; - var addrs = []; - var is_new = true; - - // Is it from genesis block? (testnet==livenet) - // TODO: parse it from networks.genesisTX - if (txid === genesisTXID) return cb(); - - Transaction.queryInfo(txid, function(err, info) { - if (err || !info) return cb(err); - - var index = 0; - info.vin.forEach( function(i){ - i.n = index++; - }); - - async.forEachLimit(info.vin, CONCURRENCY, function(i, next_in) { - if (i.addr && i.value) { - - Self.create({ - txid : txid, - value_sat : -1 * i.valueSat, - addr : i.addr, - index : i.n, - }, next_in); - if (addrs.indexOf(i.addr) === -1) { - addrs.push(i.addr); - } - } - else { - if ( !i.coinbase ) { - console.log ('WARN in TX: %s: could not parse INPUT %d', txid, i.n); - } - return next_in(); - } - }, - function (err) { - - if (err) { - if (err.message.match(/E11000/)) { - is_new = false; - } - else { - console.log('ERR at TX %s: %s', txid, err); - return cb(err); - } - } - - // Parse Outputs - async.forEachLimit(info.vout, CONCURRENCY, function(o, next_out) { - - /* - * TODO Support multisigs - */ - if (o.value && o.scriptPubKey && o.scriptPubKey.addresses && o.scriptPubKey.addresses[0]) { - Self.create({ - txid : txid, - value_sat : o.valueSat, - addr : o.scriptPubKey.addresses[0], // TODO: only address 0? - index : o.n, - }, next_out); - if (addrs.indexOf(o.scriptPubKey.addresses[0]) === -1) { - addrs.push(o.scriptPubKey.addresses[0]); - } - } - else { - console.log ('WARN in TX: %s could not parse OUTPUT %d', txid, o.n); - return next_out(); - } - }, - function (err) { - if (err) { - if (err.message.match(/E11000/)) { - is_new = false; - } - else { - console.log('ERR at TX %s: %s', txid, err); - return cb(err); - } - } - return cb(null, addrs, is_new); - }); - }); - }); -}; - - - - -TransactionItemSchema.statics.createFromArray = function(txs, next) { - - var Self = this; - if (!txs) return next(); - - var inserted_txs = []; - - async.forEachLimit(txs, CONCURRENCY, function(txid, cb, was_new) { - - Self.explodeTransactionItems( txid, function(err, addrs) { - if (err) return next(err); - if (addrs) { - async.each(addrs, function(addr){ - sockets.broadcast_address_tx(addr, {'txid': txid}); - }); - } - if (was_new) { - inserted_txs.push(txid); - } - - return cb(); - }); - }, - function(err) { - return next(err, inserted_txs); - }); -}; - - -module.exports = mongoose.model('TransactionItem', TransactionItemSchema); diff --git a/app/models/TransactionOut.js b/app/models/TransactionOut.js new file mode 100644 index 00000000..247ffc22 --- /dev/null +++ b/app/models/TransactionOut.js @@ -0,0 +1,215 @@ +'use strict'; + +/** + * Module dependencies. + */ +var mongoose = require('mongoose'), + async = require('async'), + util = require('bitcore/util/util'), + TransactionRpc = require('../../lib/TransactionRpc').class(), + Schema = mongoose.Schema; + +var CONCURRENCY = 15; +// TODO: use bitcore networks module +var genesisTXID = '4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b'; + +var TransactionOutSchema = new Schema({ + txidBuf: { + type: Buffer, + index: true, + }, + index: Number, + addr: { + type: String, + index: true, + }, + value_sat: Number, + + spendTxIdBuf: Buffer, + spendIndex: Number, +}); + + +// Compound index +TransactionOutSchema.index({txidBuf: 1, index: 1}, {unique: true, sparse: true}); +TransactionOutSchema.index({spendTxIdBuf: 1, spendIndex: 1}, {unique: true, sparse: true}); + +TransactionOutSchema.virtual('txid').get(function () { + return this.txidBuf.toString('hex'); +}); + + +TransactionOutSchema.virtual('spendTxid').get(function () { + if (!this.spendTxIdBuf) return (null); + return this.spendTxIdBuf.toString('hex'); +}); + + +TransactionOutSchema.virtual('txid').set(function (txidStr) { + if (txidStr) + this.txidBuf = new Buffer(txidStr,'hex'); + else + this.txidBuf = null; +}); + +TransactionOutSchema.statics.fromTxId = function(txid, cb) { + var txidBuf = new Buffer(txid, 'hex'); + + this.find({ + txidBuf: txidBuf, + }).exec(function (err,items) { + + // sort by index + return cb(err,items.sort(function(a,b){ + return a.index - b.index; + })); + }); +}; + +TransactionOutSchema.statics.fromTxIdOne = function(txid, cb) { + var txidBuf = new Buffer(txid, 'hex'); + + this.find({ + txidBuf: txidBuf, + }).exec(function (err,item) { + return cb(err, item[0]); + }); +}; + + +TransactionOutSchema.statics.fromTxIdN = function(txid, n, cb) { + var txidBuf = new Buffer(txid, 'hex'); + this.findOne({ + txidBuf: txidBuf, index: n + }).exec(cb); +}; + +TransactionOutSchema.statics.removeFromTxId = function(txid, cb) { + var txidBuf = new Buffer(txid, 'hex'); + this.remove({ txidBuf: txidBuf }).exec(cb); +}; + + + +TransactionOutSchema.statics._explodeTransactionOuts = function(txid, cb) { + + var Self = this; + var addrs = []; + var is_new = true; + + // Is it from genesis block? (testnet==livenet) + // TODO: parse it from networks.genesisTX + if (txid === genesisTXID) return cb(); + + TransactionRpc.getRpcInfo(txid, function(err, info) { + + if (err || !info) return cb(err); + + var bTxId = new Buffer(txid,'hex'); + + async.series([ + // Input Outputs (mark them as spended) + function(p_c) { + if (info.isCoinBase) return p_c(); + async.forEachLimit(info.vin, CONCURRENCY, + function(i, next_out) { + var b = new Buffer(i.txid,'hex'); + var data = { + txidBuf: b, + index: i.vout, + + spendTxIdBuf: bTxId, + spendIndex: i.n, + }; + Self.update({txidBuf: b, index: i.vout}, data, {upsert: true}, next_out); + }, + function (err) { + if (err) { + if (!err.message.match(/E11000/)) { + console.log('ERR at TX %s: %s', txid, err); + return cb(err); + } + } + return p_c(); + }); + }, + // Parse Outputs + function(p_c) { + async.forEachLimit(info.vout, CONCURRENCY, + function(o, next_out) { + if (o.value && o.scriptPubKey && + o.scriptPubKey.addresses && + o.scriptPubKey.addresses[0] && + ! o.scriptPubKey.addresses[1] // TODO : not supported + ){ + + // This is only to broadcast + if (addrs.indexOf(o.scriptPubKey.addresses[0]) === -1) { + addrs.push(o.scriptPubKey.addresses[0]); + } + + var data = { + txidBuf: bTxId, + index : o.n, + + value_sat : o.value * util.COIN, + addr : o.scriptPubKey.addresses[0], + }; + Self.update({txidBuf: bTxId, index: o.n}, data, {upsert: true}, next_out); + } + else { + console.log ('WARN in TX: %s could not parse OUTPUT %d', txid, o.n); + return next_out(); + } + }, + function (err) { + if (err) { + if (err.message.match(/E11000/)) { + is_new = false; + } + else { + console.log('ERR at TX %s: %s', txid, err); + return cb(err); + } + } + return p_c(); + }); + }], function(err) { + return cb(null, addrs, is_new); + }); + }); +}; + + +TransactionOutSchema.statics.createFromArray = function(txs, next) { + + var Self = this; + if (!txs) return next(); + + var inserted_txs = []; + var updated_addrs = {}; + + async.forEachLimit(txs, CONCURRENCY, function(txid, cb, was_new) { + + Self._explodeTransactionOuts( txid, function(err, addrs) { + + if (err) return next(err); + + if (was_new) { + inserted_txs.push(txid); + addrs.each(function(a) { + if ( !updated_addrs[a]) updated_addrs[a] = []; + updated_addrs[a].push(txid); + }); + } + + return cb(); + }); + }, + function(err) { + return next(err, inserted_txs, updated_addrs); + }); +}; + + +module.exports = mongoose.model('TransactionOut', TransactionOutSchema); diff --git a/dev-util/explode_tx.js b/dev-util/explode_tx.js new file mode 100755 index 00000000..814b33b6 --- /dev/null +++ b/dev-util/explode_tx.js @@ -0,0 +1,41 @@ +#!/usr/bin/env node +'use strict'; + +var util = require('util'); +var mongoose= require('mongoose'), + config = require('../config/config'); + +process.env.NODE_ENV = process.env.NODE_ENV || 'development'; + +var T = require('../app/models/TransactionOut'); + + +// var hash = process.argv[2] || '0000000000b6288775bbd326bedf324ca8717a15191da58391535408205aada4'; +var hash = process.argv[2] || '6749762ae220c10705556799dcec9bb6a54a7b881eb4b961323a3363b00db518'; + + + + +mongoose.connect(config.db); + +mongoose.connection.on('error', function(err) { console.log(err); }); + + +mongoose.connection.on('open', function() { + + var b = new Buffer(hash,'hex'); + + T.createFromArray([hash], function(err, ret) { + + console.log('Err:'); + console.log(err); + + + console.log('Ret:'); + console.log(util.inspect(ret,{depth:null})); + mongoose.connection.close(); + }); +}); + + + diff --git a/util/find_ref.sh b/dev-util/find_ref.sh similarity index 100% rename from util/find_ref.sh rename to dev-util/find_ref.sh diff --git a/dev-util/get_block.js b/dev-util/get_block.js new file mode 100755 index 00000000..23c5f457 --- /dev/null +++ b/dev-util/get_block.js @@ -0,0 +1,31 @@ +#!/usr/bin/env node +'use strict'; + +var util = require('util'); + +process.env.NODE_ENV = process.env.NODE_ENV || 'development'; + +var RpcClient = require('../node_modules/bitcore/RpcClient').class(); + +var config = require('../config/config'); + + +// var hash = process.argv[2] || '0000000000b6288775bbd326bedf324ca8717a15191da58391535408205aada4'; +var hash = process.argv[2] || 'f6c2901f39fd07f2f2e503183d76f73ecc1aee9ac9216fde58e867bc29ce674e'; + +hash = 'e2253359458db3e732c82a43fc62f56979ff59928f25a2df34dfa443e9a41160'; + +var rpc = new RpcClient(config.bitcoind); + +rpc.getRawTransaction( hash, 1, function(err, ret) { + + console.log('Err:'); + console.log(err); + + + console.log('Ret:'); + console.log(util.inspect(ret, { depth: 10} )); +}); + + + diff --git a/dev-util/get_outs.js b/dev-util/get_outs.js new file mode 100755 index 00000000..d93be4bd --- /dev/null +++ b/dev-util/get_outs.js @@ -0,0 +1,41 @@ +#!/usr/bin/env node +'use strict'; + +var util = require('util'); +var mongoose= require('mongoose'), + config = require('../config/config'); + +process.env.NODE_ENV = process.env.NODE_ENV || 'development'; + +var T = require('../app/models/TransactionOut'); + + +// var hash = process.argv[2] || '0000000000b6288775bbd326bedf324ca8717a15191da58391535408205aada4'; +var hash = process.argv[2] || 'e2253359458db3e732c82a43fc62f56979ff59928f25a2df34dfa443e9a41160'; + + + + +mongoose.connect(config.db); + +mongoose.connection.on('error', function(err) { console.log(err); }); + + +mongoose.connection.on('open', function() { + + var b = new Buffer(hash,'hex'); + + T.find({txidBuf: b}, function(err, ret) { + + console.log('Err:'); + console.log(err); + + + console.log('Ret:'); + console.log(util.inspect(ret,{depth:null})); + mongoose.connection.close(); + }); +}); + + + diff --git a/dev-util/get_outs_addr.js b/dev-util/get_outs_addr.js new file mode 100755 index 00000000..b9935d56 --- /dev/null +++ b/dev-util/get_outs_addr.js @@ -0,0 +1,39 @@ +#!/usr/bin/env node +'use strict'; + +var util = require('util'); +var mongoose= require('mongoose'), + config = require('../config/config'); + +process.env.NODE_ENV = process.env.NODE_ENV || 'development'; + +var T = require('../app/models/TransactionOut'); + + +// var hash = process.argv[2] || '0000000000b6288775bbd326bedf324ca8717a15191da58391535408205aada4'; +var hash = process.argv[2] || 'mp3Rzxx9s1A21SY3sjJ3CQoa2Xjph7e5eS'; + + + + +mongoose.connect(config.db); + +mongoose.connection.on('error', function(err) { console.log(err); }); + + +mongoose.connection.on('open', function() { + + T.find({addr: hash}, function(err, ret) { + + console.log('Err:'); + console.log(err); + + + console.log('Ret:'); + console.log(util.inspect(ret,{depth:null})); + mongoose.connection.close(); + }); +}); + + + diff --git a/dev-util/get_tx.js b/dev-util/get_tx.js new file mode 100755 index 00000000..a937c7f0 --- /dev/null +++ b/dev-util/get_tx.js @@ -0,0 +1,35 @@ +#!/usr/bin/env node +'use strict'; + +var util = require('util'); +var T = require('../app/models/Transaction').class(); +var mongoose= require('mongoose'), + config = require('../config/config'); + +process.env.NODE_ENV = process.env.NODE_ENV || 'development'; + + +// var hash = process.argv[2] || '0000000000b6288775bbd326bedf324ca8717a15191da58391535408205aada4'; +var hash = process.argv[2] || 'e2253359458db3e732c82a43fc62f56979ff59928f25a2df34dfa443e9a41160'; + + + + +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('Ret:'); + console.log(util.inspect(ret,{depth:null})); + mongoose.connection.close(); + }); +}); + + + diff --git a/etc/bitcoind/bitcoin-testnet.conf b/etc/bitcoind/bitcoin-testnet.conf index d1b7fee8..01572d2b 100644 --- a/etc/bitcoind/bitcoin-testnet.conf +++ b/etc/bitcoind/bitcoin-testnet.conf @@ -4,8 +4,11 @@ server=1 txindex=1 # Allow connections outsite localhost? -rpcallowip=192.168.1.* -rpcallowip='192.168.1.*' +#rpcallowip=192.168.1.* +#rpcallowip='192.168.1.*' +#rpcallowip=127.0.0.1 + +rpcallowip=* rpcport=18332 testnet=3 diff --git a/insight.js b/insight.js index 64027d03..117f32b8 100644 --- a/insight.js +++ b/insight.js @@ -63,7 +63,6 @@ if (!config.disableHistoricSync) { historicSync.init({ skipDbConnection: true, shouldBroadcast: true, - progressStep: 2, networkName: config.network }, function(err) { if (err) { @@ -88,7 +87,8 @@ if (!config.disableP2pSync) { ps.init({ skipDbConnection: true, broadcast_txs: true, - broadcast_blocks: true + broadcast_address_tx: true, + broadcast_blocks: true, }, function() { ps.run(); }); diff --git a/lib/HistoricSync.js b/lib/HistoricSync.js index 47101cc0..55065c72 100644 --- a/lib/HistoricSync.js +++ b/lib/HistoricSync.js @@ -102,7 +102,7 @@ function spec() { p('ERROR: ' + self.error); } else { - self.syncPercentage = parseFloat(100 * self.syncedBlocks / self.blockChainHeight).toFixed(3); + self.syncPercentage = parseFloat(100 * (self.syncedBlocks + self.skippedBlocks) / self.blockChainHeight).toFixed(3); if (self.syncPercentage > 100) self.syncPercentage = 100; p(util.format('status: [%d%%] skipped: %d', self.syncPercentage, self.skippedBlocks)); @@ -125,10 +125,7 @@ function spec() { async.series([ // Already got it? function(c) { - Block.findOne({ - hash: blockHash - }, - function(err, block) { + Block.fromHash(blockHash, function(err, block) { if (err) { p(err); return c(err); @@ -238,8 +235,6 @@ function spec() { }, // We are not using getBestBlockHash, because is not available in all clients function(cb) { - if (!scanOpts.reverse) return cb(); - self.rpc.getBlockCount(function(err, res) { if (err) return cb(err); self.blockChainHeight = res.result; @@ -304,6 +299,7 @@ function spec() { if (!self.step) { + var step = parseInt( (self.blockChainHeight - self.syncedBlocks) / 1000); if (self.opts.progressStep) { @@ -328,10 +324,7 @@ function spec() { HistoricSync.prototype.smartImport = function(next) { var self = this; - Block.findOne({ - hash: self.genesis - }, - function(err, b) { + Block.fromHash(self.genesis, function(err, b) { if (err) return next(err); diff --git a/lib/Sync.js b/lib/Sync.js index cada4827..ea4794b0 100644 --- a/lib/Sync.js +++ b/lib/Sync.js @@ -7,13 +7,12 @@ function spec() { var mongoose = require('mongoose'); var config = require('../config/config'); var Block = require('../app/models/Block'); - var TransactionItem = require('../app/models/TransactionItem'); + var TransactionOut = require('../app/models/TransactionOut'); var sockets = require('../app/controllers/socket.js'); var async = require('async'); function Sync() { - this.tx_count = 0; } Sync.prototype.init = function(opts, cb) { @@ -61,46 +60,56 @@ function spec() { Sync.prototype.destroy = function(next) { var self = this; async.series([ - function(b) { return self.db.collections.blocks.drop(b);}, - function(b) { return self.db.collections.transactionitems.drop(b);}, + function(b) { try {self.db.collections.blocks.drop(b);} catch (e) { return b(); } }, + function(b) { try {self.db.collections.transactionitems.drop(b);} catch (e) { return b(); } }, + function(b) { try {self.db.collections.transactionouts.drop(b);} catch (e) { return b(); } }, ], next); }; - Sync.prototype.storeBlock = function(block, cb) { var self = this; - Block.customCreate(block, function(err, block, inserted_txs){ + Block.customCreate(block, function(err, block, inserted_txs, updated_addrs){ if (err) return cb(err); - if (block && self.opts.broadcast_blocks) { - sockets.broadcast_block(block); - } - - if (inserted_txs && self.opts.broadcast_txs) { - inserted_txs.forEach(function(tx) { - sockets.broadcast_tx(tx); - }); - } - - if (inserted_txs) - self.tx_count += inserted_txs.length; + self._handleBroadcast(block, inserted_txs, updated_addrs); return cb(); }); }; + Sync.prototype._handleBroadcast = function(block, inserted_txs, updated_addrs) { + var self = this; + + if (block && self.opts.broadcast_blocks) { + sockets.broadcast_block(block); + } + + if (inserted_txs && self.opts.broadcast_txs) { + inserted_txs.forEach(function(tx) { + sockets.broadcast_tx(tx); + }); + } + + if (updated_addrs && self.opts.broadcast_addresses) { + updated_addrs.forEach(function(addr, txs){ + txs.forEach(function(addr, t){ + sockets.broadcast_address_tx(addr, {'txid': t}); + + }); + }); + } + }; + + + Sync.prototype.storeTxs = function(txs, cb) { var self = this; - TransactionItem.createFromArray(txs, function(err, inserted_txs) { - if (!err && inserted_txs && self.opts.broadcast_txs) { - - inserted_txs.forEach(function(tx) { - sockets.broadcast_tx(tx); - }); - } + TransactionOut.createFromArray(txs, function(err, inserted_txs, updated_addrs) { + if (err) return cb(err); + self._handleBroadcast(null, inserted_txs, updated_addrs); return cb(err); }); }; diff --git a/lib/TransactionRpc.js b/lib/TransactionRpc.js new file mode 100644 index 00000000..e162ae2e --- /dev/null +++ b/lib/TransactionRpc.js @@ -0,0 +1,69 @@ +'use strict'; + +require('classtool'); + + +function spec() { + var RpcClient = require('bitcore/RpcClient').class(), +// networks = require('bitcore/network'), + BitcoreTransaction = require('bitcore/Transaction').class(), + BitcoreBlock = require('bitcore/Block').class(), + util = require('bitcore/util/util'), + config = require('../config/config'); + + function TransactionRpc() { + this.dummy = null; + } + + TransactionRpc._parseRpcResult = function(info) { + var b = new Buffer(info.hex,'hex'); + var tx = new BitcoreTransaction(); + tx.parse(b); + + // Inputs + if (tx.isCoinBase()) { + info.isCoinBase = true; + + var reward = BitcoreBlock.getBlockValue(info.height) / util.COIN; + info.vin[0].reward = reward; + info.valueIn = reward; + } + + var n =0; + info.vin.forEach(function(i) { + i.n = n++; + }); + + // Outputs + var valueOut = 0; + info.vout.forEach( function(o) { + valueOut += o.value * util.COIN; + }); + info.valueOut = valueOut / util.COIN; + info.size = b.length; + + return info; + }; + + TransactionRpc.getRpcInfo = function(txid, cb) { + var Self = this; + + var rpc = new RpcClient(config.bitcoind); + + rpc.getRawTransaction(txid, 1, function(err, txInfo) { + + // Not found? + if (err && err.code === -5) return cb(); + if (err) return cb(err); + + var info = Self._parseRpcResult(txInfo.result); + + return cb(null,info); + }); + }; + + return TransactionRpc; +} +module.defineClass(spec); + + diff --git a/public/js/controllers/transactions.js b/public/js/controllers/transactions.js index 0fcbbcba..00e064dc 100644 --- a/public/js/controllers/transactions.js +++ b/public/js/controllers/transactions.js @@ -8,6 +8,7 @@ function($scope, $rootScope, $routeParams, $location, Global, Transaction, Trans var pageNum = 0; var pagesTotal = 1; + var COIN = 100000000; var _aggregateItems = function(items) { if (!items) return []; @@ -55,8 +56,8 @@ function($scope, $rootScope, $routeParams, $location, Global, Transaction, Trans tmp[addr].items = []; } - tmp[addr].valueSat += items[i].valueSat; - tmp[addr].value = tmp[addr].valueSat / 100000000; + tmp[addr].valueSat += items[i].value * COIN; + tmp[addr].value = items[i].value; tmp[addr].items.push(items[i]); tmp[addr].notAddr = notAddr; tmp[addr].count++; diff --git a/test/model/transactionitems.js b/test/integration/01-transactionouts.js similarity index 78% rename from test/model/transactionitems.js rename to test/integration/01-transactionouts.js index 146476a7..57a7f2d4 100644 --- a/test/model/transactionitems.js +++ b/test/integration/01-transactionouts.js @@ -10,15 +10,13 @@ var mongoose = require('mongoose'), fs = require('fs'), util = require('util'), config = require('../../config/config'), - Transaction = require('../../app/models/Transaction').class(), - TransactionItem = require('../../app/models/TransactionItem'); + TransactionOut = require('../../app/models/TransactionOut'); var txItemsValid = JSON.parse(fs.readFileSync('test/model/txitems.json')); - mongoose.connection.on('error', function(err) { console.log(err); }); -describe('TransactionItem', function(){ +describe('TransactionOut', function(){ before(function(done) { mongoose.connect(config.db); @@ -32,15 +30,14 @@ describe('TransactionItem', function(){ txItemsValid.forEach( function(v) { if (v.disabled) return; - it('test a exploding TX ' + v.txid, function(done) { + it('test a exploding tx ' + v.txid, function(done) { // Remove first - TransactionItem.remove({txid: v.txid}, function(err) { - - TransactionItem.explodeTransactionItems(v.txid, function(err, tx) { + TransactionOut.removeFromTxId(v.txid, function(err) { + TransactionOut._explodeTransactionOuts(v.txid, function(err, tx) { if (err) done(err); - TransactionItem + TransactionOut .fromTxId( v.txid, function(err, readItems) { var unmatch={}; @@ -53,17 +50,21 @@ describe('TransactionItem', function(){ assert.equal(readItem.addr,validItem.addr); assert.equal(readItem.value_sat,validItem.value_sat); assert.equal(readItem.index,validItem.index); + assert.equal(readItem.spendIndex, null); + assert.equal(readItem.spendTxIdBuf, null); delete unmatch[validItem.addr]; }); var valid = util.inspect(v.items, { depth: null }); assert(!Object.keys(unmatch).length,'\n\tUnmatchs:' + Object.keys(unmatch) + "\n\n" +valid + '\nvs.\n' + readItems); done(); + }); }); }); }); }); - }); + + diff --git a/test/integration/02-transactionouts.js b/test/integration/02-transactionouts.js new file mode 100644 index 00000000..8097f41c --- /dev/null +++ b/test/integration/02-transactionouts.js @@ -0,0 +1,68 @@ +#!/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) { + console.log('Done spending ', txid); //TODO + 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/model/addr.js b/test/integration/addr.js similarity index 78% rename from test/model/addr.js rename to test/integration/addr.js index dfeea716..362bcac6 100644 --- a/test/model/addr.js +++ b/test/integration/addr.js @@ -5,9 +5,9 @@ process.env.NODE_ENV = process.env.NODE_ENV || 'development'; var assert = require('assert'), fs = require('fs'), + mongoose= require('mongoose'), config = require('../../config/config'), Address = require('../../app/models/Address').class(); - mongoose= require('mongoose'), addrValid = JSON.parse(fs.readFileSync('test/model/addr.json')); describe('Address balances', function(){ @@ -27,7 +27,7 @@ describe('Address balances', function(){ console.log(v.addr + " => disabled in JSON"); } else { - it('Info for:' + v.addr, function(done) { + it('Info for: ' + v.addr, function(done) { this.timeout(5000); var a = new Address(v.addr); @@ -37,10 +37,12 @@ describe('Address balances', function(){ assert.equal(v.addr, a.addrStr); console.log("TX count:" + a.transactions.length); - if (v.balance) assert.equal(v.balance, a.balance); - if (v.totalReceived) assert.equal(v.totalReceived, a.totalReceived); - if (v.totalSent) assert.equal(v.totalSent, a.totalSent); + if (v.balance) assert.equal(v.balance, a.balance, 'balance: ' + a.balance); + if (v.totalReceived) assert.equal(v.totalReceived, a.totalReceived, 'received: ' + a.totalReceived ); + if (v.totalSent) assert.equal(v.totalSent, a.totalSent, 'send: ' + a.totalSent); + if (v.txApperances) + assert.equal(v.txApperances, a.txApperances, 'txApperances: ' + a.txApperances ); if (v.transactions) { diff --git a/test/model/addr.json b/test/integration/addr.json similarity index 90% rename from test/model/addr.json rename to test/integration/addr.json index 76a22dff..1812aaba 100644 --- a/test/model/addr.json +++ b/test/integration/addr.json @@ -1,58 +1,68 @@ [ { "addr": "mgqvRGJMwR9JU5VhJ3x9uX9MTkzTsmmDgQ", - "balance": 43.1 + "balance": 43.1, + "txApperances": 19 }, { "addr": "mp3Rzxx9s1A21SY3sjJ3CQoa2Xjph7e5eS", "balance": 0, "totalReceived": 50, - "totalSent": 50.0 + "totalSent": 50.0, + "txApperances": 2 }, { "addr": "muyg1K5WsHkfMVCkUXU2y7Xp5ZD6RGzCeH", "balance": 0.38571339, "totalReceived": 0.38571339, - "totalSent": 0 + "totalSent": 0, + "txApperances": 1 }, { "addr": "mhPEfAmeKVwT7arwMYbhwnL2TfwuWbP4r4", "balance": 1065, "totalReceived": 1069, - "totalSent": 4 + "totalSent": 4, + "txApperances": 13 }, { "addr": "n47CfqnKWdNwqY1UWxTmNJAqYutFxdH3zY", "balance": 0, "totalReceived":26.4245, - "totalSent": 26.4245 + "totalSent": 26.4245, + "txApperances": 4 }, { "addr": "mzSyyXgofoBxpr6gYcU3cV345G8hJpixRd", "balance": 0, "totalReceived":3.9775, - "totalSent": 3.9775 + "totalSent": 3.9775, + "txApperances": 2 }, { "addr": "mzW2hdZN2um7WBvTDerdahKqRgj3md9C29", - "balance": 1036.76206223, - "totalReceived": 1036.76206223, + "txApperances": 2034, + "balance": 1049.69744099, + "totalReceived": 1049.69744099, "totalSent": 0 }, { "addr": "mjRmkmYzvZN3cA3aBKJgYJ65epn3WCG84H", + "txApperances": 13343, "balance": 46413.0, "totalReceived": 357130.17644359, "totalSent": 310717.17644359 }, { "addr": "mgKY35SXqxFpcKK3Dq9mW9919N7wYXvcFM", + "txApperances": 1, "balance": 0.01979459, "totalReceived": 0.01979459, "totalSent": 0, "transactions": [ "91800d80bb4c69b238c9bfd94eb5155ab821e6b25cae5c79903d12853bbb4ed5" ] }, { + "disabled":1, "addr": "mmvP3mTe53qxHdPqXEvdu8WdC7GfQ2vmx5", "balance": 10580.50027254, "totalReceived": 12157.65075053, diff --git a/test/model/block.js b/test/integration/block.js similarity index 100% rename from test/model/block.js rename to test/integration/block.js diff --git a/test/integration/spent.json b/test/integration/spent.json new file mode 100644 index 00000000..351661be --- /dev/null +++ b/test/integration/spent.json @@ -0,0 +1,32 @@ +{ + "21798ddc9664ac0ef618f52b151dda82dafaf2e26d2bbef6cdaf55a6957ca237": [ + { + "txid": "bcd8da8ee847da377f8aaca92502c05e5f914c6a2452753146013b0e642a25a0", + "n": 0 + }, + { + "txid": "deb7bddc67e936ae49b97a97885d29e60afc6f6784f6d871f2904614a67250f5", + "n": 0 + } + ], + "b633a6249d4a2bc123e7f8a151cae2d4afd17aa94840009f8697270c7818ceee": [ + { + "txid": "c0c46d6be0183f52c88afe2d649800ecdaa7594ee390c77bafbd06322e6c823d", + "n": 11 + }, + { + "txid": "d60e980419c5a8abd629fdea5032d561678b62e23b3fdba62b42f410c5a29560", + "n": 1 + } + ], + "ca2f42e44455b8a84434de139efea1fe2c7d71414a8939e0a20f518849085c3b": [ + { + "txid": "aa21822f1a69bc54e5a4ab60b25c09503702a821379fd2dfbb696b8ada4ce5b9", + "n": 0 + }, + { + "txid": "a33bd24a47ab6f23758ed09e05716f809614f2e280e5a05a317ec6d839e81225", + "n": 1 + } + ] +} diff --git a/test/model/status.js b/test/integration/status.js similarity index 100% rename from test/model/status.js rename to test/integration/status.js diff --git a/test/model/transaction.js b/test/integration/transaction.js similarity index 87% rename from test/model/transaction.js rename to test/integration/transaction.js index 489182d1..d5a87c06 100644 --- a/test/model/transaction.js +++ b/test/integration/transaction.js @@ -8,8 +8,7 @@ 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(), - TransactionItem = require('../../app/models/TransactionItem'); + Transaction = require('../../app/models/Transaction').class(); mongoose.connection.on('error', function(err) { console.log(err); }); @@ -25,17 +24,17 @@ describe('Transaction', function(){ mongoose.connection.close(); done(); }); - it('should pool tx\'s object from mongoose', function(done) { - var txid = '7e621eeb02874ab039a8566fd36f4591e65eca65313875221842c53de6907d6c'; + 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)); - assert(tx.info.vin[0].addr === 'msGKGCy2i8wbKS5Fo1LbWUTJnf1GoFFG59'); - assert(tx.info.vin[1].addr === 'mfye7oHsdrHbydtj4coPXCasKad2eYSv5P'); + 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(); }); }); @@ -100,12 +99,10 @@ describe('Transaction', function(){ var txid2 = '64496d005faee77ac5a18866f50af6b8dd1f60107d6795df34c402747af98608'; it('create TX on the fly ' + txid2, function(done) { - TransactionItem.remove({txid: txid2}, function(err) { - Transaction.fromIdWithInfo(txid2, function(err, tx) { - if (err) return done(err); - assert.equal(tx.info.txid, txid2); - done(); - }); + Transaction.fromIdWithInfo(txid2, function(err, tx) { + if (err) return done(err); + assert.equal(tx.info.txid, txid2); + done(); }); }); diff --git a/test/integration/transactionouts-expenses.js b/test/integration/transactionouts-expenses.js new file mode 100644 index 00000000..b8b04beb --- /dev/null +++ b/test/integration/transactionouts-expenses.js @@ -0,0 +1,67 @@ +#!/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/model/txitems.json b/test/integration/txitems.json similarity index 69% rename from test/model/txitems.json rename to test/integration/txitems.json index 9e92a17c..72ff9699 100644 --- a/test/model/txitems.json +++ b/test/integration/txitems.json @@ -6,11 +6,6 @@ { "txid": "21798ddc9664ac0ef618f52b151dda82dafaf2e26d2bbef6cdaf55a6957ca237", "items": [ - { - "addr": "mwcFwXv2Yquy4vJA4nnNLAbHVjrPdC8Q1Z", - "value_sat": -166224000, - "index": 0 - }, { "addr": "mzjLe62faUqCSjkwQkwPAL5nYyR8K132fA", "value_sat": 134574000, @@ -26,11 +21,6 @@ { "txid": "b633a6249d4a2bc123e7f8a151cae2d4afd17aa94840009f8697270c7818ceee", "items": [ - { - "addr": "mzjLe62faUqCSjkwQkwPAL5nYyR8K132fA", - "value_sat": -40790667, - "index": 0 - }, { "addr": "mhfQJUSissP6nLM5pz6DxHfctukrrLct2T", "value_sat": 19300000, @@ -46,16 +36,6 @@ { "txid": "ca2f42e44455b8a84434de139efea1fe2c7d71414a8939e0a20f518849085c3b", "items": [ - { - "addr": "mzeiUi4opeheWYveXqp8ebqHyVwYGA2s3x", - "value_sat": -1225871, - "index": 0 - }, - { - "addr": "mtMLijHAbG8CsgBbQGajsqav9p9wKUYad5", - "value_sat": -1201823, - "index": 1 - }, { "addr": "mhqyL1nDQDo1WLH9qH8sjRjx2WwrnmAaXE", "value_sat": 1327746, diff --git a/util/get_block.js b/util/get_block.js deleted file mode 100755 index 0bc5955e..00000000 --- a/util/get_block.js +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env node -process.env.NODE_ENV = process.env.NODE_ENV || 'development'; - -var RpcClient = require('../node_modules/bitcore/RpcClient').class(); - -var config = require('../config/config'); - - -var block_hash = process.argv[2] || '0000000000b6288775bbd326bedf324ca8717a15191da58391535408205aada4'; - - -var rpc = new RpcClient(config.bitcoind); - -var block = rpc.getBestBlockHash( function(err, block) { - - console.log("Err:"); - console.log(err); - - - console.log("Block info:"); - console.log(block); -}); - - -