diff --git a/app/models/Address.js b/app/models/Address.js index 23d7089..ef8f319 100644 --- a/app/models/Address.js +++ b/app/models/Address.js @@ -14,9 +14,9 @@ function spec() { function Address(addrStr,cb) { this.addrStr = addrStr; - this.balance = null; - this.totalReceived = null; - this.totalSent = null; + this.balanceSat = 0; + this.totalReceivedSat = 0; + this.totalSentSat = 0; this.txApperances = 0; // TODO store only txids? +index? +all? @@ -35,16 +35,16 @@ function spec() { txItems.forEach(function(txItem){ +console.log(txItem.txid + ' : ' + txItem.value_sat); that.txApperances +=1; - // TESTING - that.balance += txItem.value + 0.1; + that.balanceSat += txItem.value_sat; that.transactions.push(txItem.txid); - if (txItem.value > 0) - that.totalSent += txItem.value; + if (txItem.value_sat > 0) + that.totalSentSat += txItem.value_sat; else - that.totalReceived += Math.abs(txItem.value); + that.totalReceivedSat += Math.abs(txItem.value_sat); }); return cb(); }) diff --git a/app/models/Transaction.js b/app/models/Transaction.js index f60653f..7858b35 100644 --- a/app/models/Transaction.js +++ b/app/models/Transaction.js @@ -58,19 +58,36 @@ TransactionSchema.statics.fromId = function(txid, cb) { TransactionSchema.statics.fromIdWithInfo = function(txid, cb) { + var that = this; - // TODO Should we go to mongoDB first? Now, no extra information is stored at mongo. this.fromId(txid, function(err, tx) { if (err) return cb(err); - if (!tx) { return cb(new Error('TX not found')); } + if (!tx) { + + return cb(new Error('TX not found')); + + // No in mongo...but maybe in bitcoind... lets query it +/* var tx = new that(); + + tx.txid = txid; + + tx.queryInfo(function(err, txInfo) { + + if (!txInfo) return cb(new Error('TX not found')); + + tx.save(function(err) { +console.log('asdadsads'); + return cb(err,tx); + }); + }); +*/ } tx.queryInfo(function(err) { return cb(err,tx); } ); }); }; - TransactionSchema.statics.createFromArray = function(txs, next) { var that = this; if (!txs) return next(); @@ -97,23 +114,23 @@ TransactionSchema.statics.explodeTransactionItems = function(txid, cb) { if (err || !t) return cb(err); var index=0; + t.info.vin.forEach(function(i){ i.n = index++}); + async.each(t.info.vin, function(i, next_in) { - - /* - * TODO Support multisigs??? - */ - if (i.addr && i.value) { + +//console.log("Creating IN %s %d", i.addr, i.valueSat); TransactionItem.create({ txid : t.txid, - value : -1 * i.value, + value_sat : -1 * i.valueSat, addr : i.addr, - index : index++, + index : i.n, + ts : t.info.time, }, next_in); } else { if ( !i.coinbase ) { - console.log ("TX: %s seems to be multisig IN. Skipping... ", t.txid); + console.log ("TX: %s,%d could not parse INPUT", t.txid, i.n); } return next_in(); } @@ -129,15 +146,17 @@ TransactionSchema.statics.explodeTransactionItems = function(txid, cb) { && o.scriptPubKey.addresses && o.scriptPubKey.addresses[0] ) { +//console.log("Creating OUT %s %d", o.scriptPubKey.addresses[0], o.valueSat); TransactionItem.create({ txid : t.txid, - value : o.value, + value_sat : o.valueSat, addr : o.scriptPubKey.addresses[0], index : o.n, + ts : t.info.time, }, next_out); } else { - console.log ("TX: %s,%d seems to be multisig OUT. Skipping... ", t.txid, o.n); + console.log ("TX: %s,%d could not parse OUTPUT. Skipping... ", t.txid, o.n); return next_out(); } }, @@ -240,6 +259,7 @@ TransactionSchema.methods.queryInfo = function (next) { if (i.value) { that.info.vin[c].value = util.formatValue(i.value); var n = util.valueToBigInt(i.value).toNumber(); + that.info.vin[c].valueSat = n; valueIn = valueIn.add( n ); var scriptSig = i.getScript(); @@ -264,9 +284,13 @@ TransactionSchema.methods.queryInfo = function (next) { }); } + var c = 0; tx.outs.forEach( function(i) { var n = util.valueToBigInt(i.v).toNumber(); valueOut = valueOut.add(n); + + that.info.vout[c].valueSat = n; + c++; }); that.info.valueOut = valueOut / util.COIN; diff --git a/app/models/TransactionItem.js b/app/models/TransactionItem.js index 8f5ca97..bb7c4b9 100644 --- a/app/models/TransactionItem.js +++ b/app/models/TransactionItem.js @@ -13,13 +13,15 @@ var TransactionItemSchema = new Schema({ type: String, index: true, }, + // OJO: mongoose doesnt accept camelcase for field names // <0 is Input >0 is Output - value: Number, + value_sat: Number, + ts: Number, }); // Compound index -TransactionItemSchema.index({txid: 1, index: 1, value: 1}, {unique: true, dropDups: true}); +TransactionItemSchema.index({txid: 1, index: 1, value_sat: 1}, {unique: true, dropDups: true}); TransactionItemSchema.statics.load = function(id, cb) { @@ -29,10 +31,24 @@ TransactionItemSchema.statics.load = function(id, cb) { }; -TransactionItemSchema.statics.fromAddr = function(addr, cb) { +TransactionItemSchema.statics.fromTxId = function(txid, cb) { this.find({ - addr: addr, - }).exec(cb); + 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; + } + })); + }); }; module.exports = mongoose.model('TransactionItem', TransactionItemSchema); diff --git a/config/env/development.js b/config/env/development.js index f01f9fb..479553e 100755 --- a/config/env/development.js +++ b/config/env/development.js @@ -6,9 +6,9 @@ module.exports = { name: "Mystery - Development" }, bitcoind: { - user: 'mystery', - pass: 'real_mystery', - protocol: 'http', + protocol: process.env.BITCOIND_USER || 'http', + user: process.env.BITCOIND_USER || 'mystery', + pass: process.env.BITCOIND_PASS || 'real_mystery', host: process.env.BITCOIND_HOST || '127.0.0.1', port: process.env.BITCOIND_PORT || '18332', }, diff --git a/test/model/addr.js b/test/model/addr.js index 0b683a5..b999200 100644 --- a/test/model/addr.js +++ b/test/model/addr.js @@ -8,9 +8,7 @@ var config = require('../../config/config'), Address = require('../../app/models/Address'); mongoose= require('mongoose'), - config = require('../../config/config'); - -var addrValid = JSON.parse(fs.readFileSync('test/model/addr.json')); + addrValid = JSON.parse(fs.readFileSync('test/model/addr.json')); describe('Address update', function(){ diff --git a/test/model/addr.json b/test/model/addr.json index c82aa7d..716a62b 100644 --- a/test/model/addr.json +++ b/test/model/addr.json @@ -1,15 +1,20 @@ [ { +"disabled":1, "addr": "mjRmkmYzvZN3cA3aBKJgYJ65epn3WCG84H" }, { +"disabled":1, "addr": "mp3Rzxx9s1A21SY3sjJ3CQoa2Xjph7e5eS", "balance": 0, "totalReceived": 50, "totalSent": 50.0 } , - + { + "addr": "mgqvRGJMwR9JU5VhJ3x9uX9MTkzTsmmDgQ", + "balance": 43.1 + }, { "addr": "mzW2hdZN2um7WBvTDerdahKqRgj3md9C29", "balance": 910.39522682, diff --git a/test/model/transaction.js b/test/model/transaction.js index 03c4a4d..d42c172 100644 --- a/test/model/transaction.js +++ b/test/model/transaction.js @@ -28,9 +28,6 @@ describe('Transaction', function(){ mongoose.connection.close(); done(); }); - - - it('should pool tx\'s object from mongoose', function(done) { var txid = '7e621eeb02874ab039a8566fd36f4591e65eca65313875221842c53de6907d6c'; Transaction.fromIdWithInfo(txid, function(err, tx) { @@ -80,23 +77,42 @@ describe('Transaction', function(){ done(); }); }); + var txid22 = '666'; + it('test unexisting TX ' + txid22, function(done) { + Transaction.fromIdWithInfo(txid22, function(err, tx) { + if (err && err.toString().match(/TX.not.found/)) { + return done(); + } + else { + return done(err); + } + }); + }); + 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(); + }); + }); + }); var txid2 = '64496d005faee77ac5a18866f50af6b8dd1f60107d6795df34c402747af98608'; it('test a broken TX ' + txid2, function(done) { Transaction.fromIdWithInfo(txid2, function(err, tx) { - if (err) done(err); + if (err) return done(err); assert.equal(tx.info.txid, txid2); assert.equal(tx.info.vin[0].addr, 'n1JagbRWBDi6VMvG7HfZmXX74dB9eiHJzU'); - - // TODO output -> multisig! - // https://www.biteasy.com/testnet/transactions/64496d005faee77ac5a18866f50af6b8dd1f60107d6795df34c402747af98608 done(); }); }); txItemsValid.forEach( function(v) { + if (v.disabled) return; it('test a exploding TX ' + v.txid, function(done) { // Remove first @@ -105,7 +121,8 @@ describe('Transaction', function(){ Transaction.explodeTransactionItems(v.txid, function(err, tx) { if (err) done(err); - TransactionItem.find({txid: v.txid}).sort({ index:1 }).exec(function(err, readItems) { + TransactionItem + .fromTxId( v.txid, function(err, readItems) { var unmatch={}; @@ -113,20 +130,22 @@ describe('Transaction', function(){ unmatch[validItem.addr] =1; }); v.items.forEach(function(validItem){ - readItems.forEach(function(readItem){ - if ( readItem.addr === validItem.addr && - parseInt(readItem.index) == parseInt(validItem.index) && - parseFloat(readItem.value) == parseFloat(validItem.value) ) - delete unmatch[validItem.addr]; - }); + var readItem = readItems.shift(); + assert.equal(readItem.addr,validItem.addr); + assert.equal(readItem.value_sat,validItem.value_sat); + assert.equal(readItem.index,validItem.index); + delete unmatch[validItem.addr]; }); + var valid = util.inspect(v.items, { depth: null }); - assert(!Object.keys(unmatch).length, '\n\tmatched:' + Object.keys(unmatch) + "\n\n" +valid + '\nvs.\n' + readItems); + assert(!Object.keys(unmatch).length, + '\n\tUnmatchs:' + Object.keys(unmatch) + "\n\n" +valid + '\nvs.\n' + readItems); done(); }); }); }); }); }); + }); diff --git a/test/model/txitems.json b/test/model/txitems.json index 3441bb6..9e92a17 100644 --- a/test/model/txitems.json +++ b/test/model/txitems.json @@ -1,20 +1,24 @@ [ + { + "disabled": 1, + "txid": "75c5ffe6dc2eb0f6bd011a08c041ef115380ccd637d859b379506a0dca4c26fc" + }, { "txid": "21798ddc9664ac0ef618f52b151dda82dafaf2e26d2bbef6cdaf55a6957ca237", "items": [ { "addr": "mwcFwXv2Yquy4vJA4nnNLAbHVjrPdC8Q1Z", - "value": -1.66224, + "value_sat": -166224000, "index": 0 }, { "addr": "mzjLe62faUqCSjkwQkwPAL5nYyR8K132fA", - "value": 1.34574, + "value_sat": 134574000, "index": 0 }, { "addr": "n28wb1cRGxPtfmsenYKFfsvnZ6kRapx3jF", - "value": 0.316, + "value_sat": 31600000, "index": 1 } ] @@ -24,17 +28,17 @@ "items": [ { "addr": "mzjLe62faUqCSjkwQkwPAL5nYyR8K132fA", - "value": -0.40790667, + "value_sat": -40790667, "index": 0 }, { "addr": "mhfQJUSissP6nLM5pz6DxHfctukrrLct2T", - "value": 0.193, + "value_sat": 19300000, "index": 0 }, { "addr": "mzcDhbL877ES3MGftWnc3EuTSXs3WXDDML", - "value": 0.21440667, + "value_sat": 21440667, "index": 1 } ] @@ -44,22 +48,22 @@ "items": [ { "addr": "mzeiUi4opeheWYveXqp8ebqHyVwYGA2s3x", - "value": -0.01225871, + "value_sat": -1225871, "index": 0 }, { "addr": "mtMLijHAbG8CsgBbQGajsqav9p9wKUYad5", - "value": -0.01201823, + "value_sat": -1201823, "index": 1 }, { "addr": "mhqyL1nDQDo1WLH9qH8sjRjx2WwrnmAaXE", - "value": 0.01327746, + "value_sat": 1327746, "index": 0 }, { "addr": "mkGrySSnxcqRbtPCisApj3zXCQVmUUWbf1", - "value": 0.01049948, + "value_sat": 1049948, "index": 1 } ]