new DB schema. test updated
This commit is contained in:
parent
7607651fd7
commit
6f2ffeff04
|
@ -5,7 +5,7 @@ require('classtool');
|
||||||
|
|
||||||
function spec() {
|
function spec() {
|
||||||
var async = require('async');
|
var async = require('async');
|
||||||
var TransactionItem = require('./TransactionItem');
|
var TransactionOut = require('./TransactionOut');
|
||||||
var BitcoreAddress = require('bitcore/Address').class();
|
var BitcoreAddress = require('bitcore/Address').class();
|
||||||
var BitcoreUtil = require('bitcore/util/util');
|
var BitcoreUtil = require('bitcore/util/util');
|
||||||
|
|
||||||
|
@ -55,30 +55,44 @@ function spec() {
|
||||||
}
|
}
|
||||||
|
|
||||||
Address.prototype.update = function(next) {
|
Address.prototype.update = function(next) {
|
||||||
var that = this;
|
var self = this;
|
||||||
async.series([
|
async.series([
|
||||||
// TODO TXout!
|
/* function (cb) {
|
||||||
//T
|
TransactionIn.find({addr:self.addrStr}).exec(function(err,txIn){
|
||||||
function (cb) {
|
|
||||||
TransactionItem.find({addr:that.addrStr}).exec(function(err,txItems){
|
|
||||||
if (err) return cb(err);
|
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) ) );
|
self.balanceSat += txItem.value_sat;
|
||||||
that.txApperances +=1;
|
self.totalReceivedSat += txItem.value_sat;
|
||||||
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);
|
|
||||||
});
|
});
|
||||||
return cb();
|
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) {
|
], function (err) {
|
||||||
return next(err);
|
return next(err);
|
||||||
});
|
});
|
||||||
|
|
|
@ -8,7 +8,7 @@ var mongoose = require('mongoose'),
|
||||||
RpcClient = require('bitcore/RpcClient').class(),
|
RpcClient = require('bitcore/RpcClient').class(),
|
||||||
util = require('bitcore/util/util'),
|
util = require('bitcore/util/util'),
|
||||||
BitcoreBlock= require('bitcore/Block').class(),
|
BitcoreBlock= require('bitcore/Block').class(),
|
||||||
TransactionItem = require('./TransactionItem'),
|
TransactionOut = require('./TransactionOut'),
|
||||||
config = require('../../config/config')
|
config = require('../../config/config')
|
||||||
;
|
;
|
||||||
|
|
||||||
|
@ -41,8 +41,6 @@ BlockSchema.virtual('hash').set(function (hash) {
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
BlockSchema.virtual('hashStr').get(function () {
|
BlockSchema.virtual('hashStr').get(function () {
|
||||||
return this._id.toString('hex');
|
return this._id.toString('hex');
|
||||||
});
|
});
|
||||||
|
@ -79,22 +77,21 @@ BlockSchema.path('title').validate(function(title) {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
BlockSchema.statics.customCreate = function(block, cb) {
|
BlockSchema.statics.customCreate = function(block, cb) {
|
||||||
|
var Self= this;
|
||||||
var That= this;
|
|
||||||
|
|
||||||
var BlockSchema = mongoose.model('Block', BlockSchema);
|
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.time = block.time ? block.time : Math.round(new Date().getTime() / 1000);
|
||||||
newBlock.hashStr = block.hash;
|
newBlock.hashStr = block.hash;
|
||||||
newBlock.nextBlockHashStr = block.nextBlockHash;
|
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);
|
if (err) return cb(err);
|
||||||
|
|
||||||
newBlock.save(function(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) {
|
BlockSchema.statics.fromHash = function(hashStr, cb) {
|
||||||
|
|
||||||
var hash = new Buffer(hashStr, 'hex');
|
var hash = new Buffer(hashStr, 'hex');
|
||||||
|
|
||||||
this.findOne({
|
this.findOne({
|
||||||
hash: hash,
|
_id: hash,
|
||||||
}).exec(cb);
|
}).exec(cb);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -5,31 +5,22 @@ require('classtool');
|
||||||
|
|
||||||
function spec() {
|
function spec() {
|
||||||
|
|
||||||
var async = require('async'),
|
var util = require('bitcore/util/util'),
|
||||||
RpcClient = require('bitcore/RpcClient').class(),
|
TransactionRpc = require('../../lib/TransactionRpc').class(),
|
||||||
BitcoreTransaction = require('bitcore/Transaction').class(),
|
TransactionOut = require('./TransactionOut'),
|
||||||
Address = require('bitcore/Address').class(),
|
async = require('async');
|
||||||
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 CONCURRENCY = 20;
|
||||||
|
|
||||||
function Transaction() {
|
function Transaction() {
|
||||||
this.txid = null;
|
this.txid = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Transaction.fromIdWithInfo = function (txid,cb) {
|
Transaction.fromIdWithInfo = function (txid,cb) {
|
||||||
var tx = new Transaction();
|
var tx = new Transaction();
|
||||||
tx.txid = txid;
|
tx.txid = txid;
|
||||||
|
|
||||||
tx.fillInfo(function(err) {
|
tx._fillInfo(function(err) {
|
||||||
if (err) return cb(err);
|
if (err) return cb(err);
|
||||||
if (! tx.info ) return cb();
|
if (! tx.info ) return cb();
|
||||||
|
|
||||||
|
@ -39,157 +30,52 @@ function spec() {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Transaction.prototype.fillInfo = function(next) {
|
Transaction.prototype._fillInfo = function(next) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
Transaction.queryInfo(self.txid, function(err, info) {
|
TransactionRpc.getRpcInfo(self.txid, function(err, info) {
|
||||||
if (err) return next(err);
|
if (err) return next(err);
|
||||||
|
Transaction._fillOutpoints(info, function() {
|
||||||
self.info = info;
|
self.info = info;
|
||||||
return next();
|
return next();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
Transaction.getOutpoints = function (tx, next) {
|
|
||||||
|
|
||||||
if (tx.isCoinBase()) return next();
|
Transaction._fillOutpoints = function(info, cb) {
|
||||||
|
|
||||||
var rpc = new RpcClient(config.bitcoind);
|
if (!info || info.isCoinBase) return cb();
|
||||||
var network = ( config.network === 'testnet') ? networks.testnet : networks.livenet ;
|
|
||||||
|
|
||||||
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();
|
if (err || !out || ! out.addr) {
|
||||||
var outIndex = i.getOutpointIndex();
|
console.log('Could not get TXouts in %s,%d from %s ', i.txid, i.vout, info.txid);
|
||||||
var outHashBase64 = outHash.reverse().toString('hex');
|
incompleteInputs = 1;
|
||||||
|
return c_in(); // error not scaled
|
||||||
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++;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
c=0;
|
i.addr = out.addr;
|
||||||
tx.outs.forEach( function(i) {
|
i.valueSat = out.value_sat;
|
||||||
var n = util.valueToBigInt(i.v).toNumber();
|
i.value = out.value_sat / util.COIN;
|
||||||
valueOut = valueOut.add(n);
|
|
||||||
|
|
||||||
info.vout[c].valueSat = n;
|
valueIn += i.valueSat;
|
||||||
c++;
|
return c_in();
|
||||||
});
|
|
||||||
|
|
||||||
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);
|
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
function () {
|
||||||
|
if (! incompleteInputs ) {
|
||||||
|
info.valueIn = valueIn / util.COIN;
|
||||||
|
info.fees = (valueIn - parseInt(info.valueOut * util.COIN)) / util.COIN ;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
info.incompleteInputs = 1;
|
||||||
|
}
|
||||||
|
return cb();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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);
|
|
|
@ -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);
|
|
@ -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();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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} ));
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -4,8 +4,11 @@ server=1
|
||||||
txindex=1
|
txindex=1
|
||||||
|
|
||||||
# Allow connections outsite localhost?
|
# 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
|
rpcport=18332
|
||||||
testnet=3
|
testnet=3
|
||||||
|
|
|
@ -63,7 +63,6 @@ if (!config.disableHistoricSync) {
|
||||||
historicSync.init({
|
historicSync.init({
|
||||||
skipDbConnection: true,
|
skipDbConnection: true,
|
||||||
shouldBroadcast: true,
|
shouldBroadcast: true,
|
||||||
progressStep: 2,
|
|
||||||
networkName: config.network
|
networkName: config.network
|
||||||
}, function(err) {
|
}, function(err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
|
@ -88,7 +87,8 @@ if (!config.disableP2pSync) {
|
||||||
ps.init({
|
ps.init({
|
||||||
skipDbConnection: true,
|
skipDbConnection: true,
|
||||||
broadcast_txs: true,
|
broadcast_txs: true,
|
||||||
broadcast_blocks: true
|
broadcast_address_tx: true,
|
||||||
|
broadcast_blocks: true,
|
||||||
}, function() {
|
}, function() {
|
||||||
ps.run();
|
ps.run();
|
||||||
});
|
});
|
||||||
|
|
|
@ -102,7 +102,7 @@ function spec() {
|
||||||
p('ERROR: ' + self.error);
|
p('ERROR: ' + self.error);
|
||||||
}
|
}
|
||||||
else {
|
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;
|
if (self.syncPercentage > 100) self.syncPercentage = 100;
|
||||||
|
|
||||||
p(util.format('status: [%d%%] skipped: %d', self.syncPercentage, self.skippedBlocks));
|
p(util.format('status: [%d%%] skipped: %d', self.syncPercentage, self.skippedBlocks));
|
||||||
|
@ -125,10 +125,7 @@ function spec() {
|
||||||
async.series([
|
async.series([
|
||||||
// Already got it?
|
// Already got it?
|
||||||
function(c) {
|
function(c) {
|
||||||
Block.findOne({
|
Block.fromHash(blockHash, function(err, block) {
|
||||||
hash: blockHash
|
|
||||||
},
|
|
||||||
function(err, block) {
|
|
||||||
if (err) {
|
if (err) {
|
||||||
p(err);
|
p(err);
|
||||||
return c(err);
|
return c(err);
|
||||||
|
@ -238,8 +235,6 @@ function spec() {
|
||||||
},
|
},
|
||||||
// We are not using getBestBlockHash, because is not available in all clients
|
// We are not using getBestBlockHash, because is not available in all clients
|
||||||
function(cb) {
|
function(cb) {
|
||||||
if (!scanOpts.reverse) return cb();
|
|
||||||
|
|
||||||
self.rpc.getBlockCount(function(err, res) {
|
self.rpc.getBlockCount(function(err, res) {
|
||||||
if (err) return cb(err);
|
if (err) return cb(err);
|
||||||
self.blockChainHeight = res.result;
|
self.blockChainHeight = res.result;
|
||||||
|
@ -304,6 +299,7 @@ function spec() {
|
||||||
|
|
||||||
|
|
||||||
if (!self.step) {
|
if (!self.step) {
|
||||||
|
|
||||||
var step = parseInt( (self.blockChainHeight - self.syncedBlocks) / 1000);
|
var step = parseInt( (self.blockChainHeight - self.syncedBlocks) / 1000);
|
||||||
|
|
||||||
if (self.opts.progressStep) {
|
if (self.opts.progressStep) {
|
||||||
|
@ -328,10 +324,7 @@ function spec() {
|
||||||
HistoricSync.prototype.smartImport = function(next) {
|
HistoricSync.prototype.smartImport = function(next) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
Block.findOne({
|
Block.fromHash(self.genesis, function(err, b) {
|
||||||
hash: self.genesis
|
|
||||||
},
|
|
||||||
function(err, b) {
|
|
||||||
|
|
||||||
if (err) return next(err);
|
if (err) return next(err);
|
||||||
|
|
||||||
|
|
59
lib/Sync.js
59
lib/Sync.js
|
@ -7,13 +7,12 @@ function spec() {
|
||||||
var mongoose = require('mongoose');
|
var mongoose = require('mongoose');
|
||||||
var config = require('../config/config');
|
var config = require('../config/config');
|
||||||
var Block = require('../app/models/Block');
|
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 sockets = require('../app/controllers/socket.js');
|
||||||
var async = require('async');
|
var async = require('async');
|
||||||
|
|
||||||
|
|
||||||
function Sync() {
|
function Sync() {
|
||||||
this.tx_count = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Sync.prototype.init = function(opts, cb) {
|
Sync.prototype.init = function(opts, cb) {
|
||||||
|
@ -61,46 +60,56 @@ function spec() {
|
||||||
Sync.prototype.destroy = function(next) {
|
Sync.prototype.destroy = function(next) {
|
||||||
var self = this;
|
var self = this;
|
||||||
async.series([
|
async.series([
|
||||||
function(b) { return self.db.collections.blocks.drop(b);},
|
function(b) { try {self.db.collections.blocks.drop(b);} catch (e) { return b(); } },
|
||||||
function(b) { return self.db.collections.transactionitems.drop(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);
|
], next);
|
||||||
};
|
};
|
||||||
|
|
||||||
Sync.prototype.storeBlock = function(block, cb) {
|
Sync.prototype.storeBlock = function(block, cb) {
|
||||||
var self = this;
|
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 (err) return cb(err);
|
||||||
|
|
||||||
if (block && self.opts.broadcast_blocks) {
|
self._handleBroadcast(block, inserted_txs, updated_addrs);
|
||||||
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;
|
|
||||||
|
|
||||||
return cb();
|
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) {
|
Sync.prototype.storeTxs = function(txs, cb) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
TransactionItem.createFromArray(txs, function(err, inserted_txs) {
|
TransactionOut.createFromArray(txs, function(err, inserted_txs, updated_addrs) {
|
||||||
if (!err && inserted_txs && self.opts.broadcast_txs) {
|
if (err) return cb(err);
|
||||||
|
|
||||||
inserted_txs.forEach(function(tx) {
|
|
||||||
sockets.broadcast_tx(tx);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
self._handleBroadcast(null, inserted_txs, updated_addrs);
|
||||||
return cb(err);
|
return cb(err);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@ function($scope, $rootScope, $routeParams, $location, Global, Transaction, Trans
|
||||||
|
|
||||||
var pageNum = 0;
|
var pageNum = 0;
|
||||||
var pagesTotal = 1;
|
var pagesTotal = 1;
|
||||||
|
var COIN = 100000000;
|
||||||
|
|
||||||
var _aggregateItems = function(items) {
|
var _aggregateItems = function(items) {
|
||||||
if (!items) return [];
|
if (!items) return [];
|
||||||
|
@ -55,8 +56,8 @@ function($scope, $rootScope, $routeParams, $location, Global, Transaction, Trans
|
||||||
tmp[addr].items = [];
|
tmp[addr].items = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
tmp[addr].valueSat += items[i].valueSat;
|
tmp[addr].valueSat += items[i].value * COIN;
|
||||||
tmp[addr].value = tmp[addr].valueSat / 100000000;
|
tmp[addr].value = items[i].value;
|
||||||
tmp[addr].items.push(items[i]);
|
tmp[addr].items.push(items[i]);
|
||||||
tmp[addr].notAddr = notAddr;
|
tmp[addr].notAddr = notAddr;
|
||||||
tmp[addr].count++;
|
tmp[addr].count++;
|
||||||
|
|
|
@ -10,15 +10,13 @@ var mongoose = require('mongoose'),
|
||||||
fs = require('fs'),
|
fs = require('fs'),
|
||||||
util = require('util'),
|
util = require('util'),
|
||||||
config = require('../../config/config'),
|
config = require('../../config/config'),
|
||||||
Transaction = require('../../app/models/Transaction').class(),
|
TransactionOut = require('../../app/models/TransactionOut');
|
||||||
TransactionItem = require('../../app/models/TransactionItem');
|
|
||||||
|
|
||||||
var txItemsValid = JSON.parse(fs.readFileSync('test/model/txitems.json'));
|
var txItemsValid = JSON.parse(fs.readFileSync('test/model/txitems.json'));
|
||||||
|
|
||||||
|
|
||||||
mongoose.connection.on('error', function(err) { console.log(err); });
|
mongoose.connection.on('error', function(err) { console.log(err); });
|
||||||
|
|
||||||
describe('TransactionItem', function(){
|
describe('TransactionOut', function(){
|
||||||
|
|
||||||
before(function(done) {
|
before(function(done) {
|
||||||
mongoose.connect(config.db);
|
mongoose.connect(config.db);
|
||||||
|
@ -32,15 +30,14 @@ describe('TransactionItem', function(){
|
||||||
|
|
||||||
txItemsValid.forEach( function(v) {
|
txItemsValid.forEach( function(v) {
|
||||||
if (v.disabled) return;
|
if (v.disabled) return;
|
||||||
it('test a exploding TX ' + v.txid, function(done) {
|
it('test a exploding tx ' + v.txid, function(done) {
|
||||||
|
|
||||||
// Remove first
|
// Remove first
|
||||||
TransactionItem.remove({txid: v.txid}, function(err) {
|
TransactionOut.removeFromTxId(v.txid, function(err) {
|
||||||
|
TransactionOut._explodeTransactionOuts(v.txid, function(err, tx) {
|
||||||
TransactionItem.explodeTransactionItems(v.txid, function(err, tx) {
|
|
||||||
if (err) done(err);
|
if (err) done(err);
|
||||||
|
|
||||||
TransactionItem
|
TransactionOut
|
||||||
.fromTxId( v.txid, function(err, readItems) {
|
.fromTxId( v.txid, function(err, readItems) {
|
||||||
|
|
||||||
var unmatch={};
|
var unmatch={};
|
||||||
|
@ -53,17 +50,21 @@ describe('TransactionItem', function(){
|
||||||
assert.equal(readItem.addr,validItem.addr);
|
assert.equal(readItem.addr,validItem.addr);
|
||||||
assert.equal(readItem.value_sat,validItem.value_sat);
|
assert.equal(readItem.value_sat,validItem.value_sat);
|
||||||
assert.equal(readItem.index,validItem.index);
|
assert.equal(readItem.index,validItem.index);
|
||||||
|
assert.equal(readItem.spendIndex, null);
|
||||||
|
assert.equal(readItem.spendTxIdBuf, null);
|
||||||
delete unmatch[validItem.addr];
|
delete unmatch[validItem.addr];
|
||||||
});
|
});
|
||||||
|
|
||||||
var valid = util.inspect(v.items, { depth: null });
|
var valid = util.inspect(v.items, { depth: null });
|
||||||
assert(!Object.keys(unmatch).length,'\n\tUnmatchs:' + 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();
|
done();
|
||||||
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -5,9 +5,9 @@ process.env.NODE_ENV = process.env.NODE_ENV || 'development';
|
||||||
var
|
var
|
||||||
assert = require('assert'),
|
assert = require('assert'),
|
||||||
fs = require('fs'),
|
fs = require('fs'),
|
||||||
|
mongoose= require('mongoose'),
|
||||||
config = require('../../config/config'),
|
config = require('../../config/config'),
|
||||||
Address = require('../../app/models/Address').class();
|
Address = require('../../app/models/Address').class();
|
||||||
mongoose= require('mongoose'),
|
|
||||||
addrValid = JSON.parse(fs.readFileSync('test/model/addr.json'));
|
addrValid = JSON.parse(fs.readFileSync('test/model/addr.json'));
|
||||||
|
|
||||||
describe('Address balances', function(){
|
describe('Address balances', function(){
|
||||||
|
@ -27,7 +27,7 @@ describe('Address balances', function(){
|
||||||
console.log(v.addr + " => disabled in JSON");
|
console.log(v.addr + " => disabled in JSON");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
it('Info for:' + v.addr, function(done) {
|
it('Info for: ' + v.addr, function(done) {
|
||||||
this.timeout(5000);
|
this.timeout(5000);
|
||||||
|
|
||||||
var a = new Address(v.addr);
|
var a = new Address(v.addr);
|
||||||
|
@ -37,10 +37,12 @@ describe('Address balances', function(){
|
||||||
assert.equal(v.addr, a.addrStr);
|
assert.equal(v.addr, a.addrStr);
|
||||||
console.log("TX count:" + a.transactions.length);
|
console.log("TX count:" + a.transactions.length);
|
||||||
|
|
||||||
if (v.balance) assert.equal(v.balance, a.balance);
|
if (v.balance) assert.equal(v.balance, a.balance, 'balance: ' + a.balance);
|
||||||
if (v.totalReceived) assert.equal(v.totalReceived, a.totalReceived);
|
if (v.totalReceived) assert.equal(v.totalReceived, a.totalReceived, 'received: ' + a.totalReceived );
|
||||||
if (v.totalSent) assert.equal(v.totalSent, a.totalSent);
|
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) {
|
if (v.transactions) {
|
||||||
|
|
|
@ -1,58 +1,68 @@
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"addr": "mgqvRGJMwR9JU5VhJ3x9uX9MTkzTsmmDgQ",
|
"addr": "mgqvRGJMwR9JU5VhJ3x9uX9MTkzTsmmDgQ",
|
||||||
"balance": 43.1
|
"balance": 43.1,
|
||||||
|
"txApperances": 19
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"addr": "mp3Rzxx9s1A21SY3sjJ3CQoa2Xjph7e5eS",
|
"addr": "mp3Rzxx9s1A21SY3sjJ3CQoa2Xjph7e5eS",
|
||||||
"balance": 0,
|
"balance": 0,
|
||||||
"totalReceived": 50,
|
"totalReceived": 50,
|
||||||
"totalSent": 50.0
|
"totalSent": 50.0,
|
||||||
|
"txApperances": 2
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"addr": "muyg1K5WsHkfMVCkUXU2y7Xp5ZD6RGzCeH",
|
"addr": "muyg1K5WsHkfMVCkUXU2y7Xp5ZD6RGzCeH",
|
||||||
"balance": 0.38571339,
|
"balance": 0.38571339,
|
||||||
"totalReceived": 0.38571339,
|
"totalReceived": 0.38571339,
|
||||||
"totalSent": 0
|
"totalSent": 0,
|
||||||
|
"txApperances": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"addr": "mhPEfAmeKVwT7arwMYbhwnL2TfwuWbP4r4",
|
"addr": "mhPEfAmeKVwT7arwMYbhwnL2TfwuWbP4r4",
|
||||||
"balance": 1065,
|
"balance": 1065,
|
||||||
"totalReceived": 1069,
|
"totalReceived": 1069,
|
||||||
"totalSent": 4
|
"totalSent": 4,
|
||||||
|
"txApperances": 13
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"addr": "n47CfqnKWdNwqY1UWxTmNJAqYutFxdH3zY",
|
"addr": "n47CfqnKWdNwqY1UWxTmNJAqYutFxdH3zY",
|
||||||
"balance": 0,
|
"balance": 0,
|
||||||
"totalReceived":26.4245,
|
"totalReceived":26.4245,
|
||||||
"totalSent": 26.4245
|
"totalSent": 26.4245,
|
||||||
|
"txApperances": 4
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"addr": "mzSyyXgofoBxpr6gYcU3cV345G8hJpixRd",
|
"addr": "mzSyyXgofoBxpr6gYcU3cV345G8hJpixRd",
|
||||||
"balance": 0,
|
"balance": 0,
|
||||||
"totalReceived":3.9775,
|
"totalReceived":3.9775,
|
||||||
"totalSent": 3.9775
|
"totalSent": 3.9775,
|
||||||
|
"txApperances": 2
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"addr": "mzW2hdZN2um7WBvTDerdahKqRgj3md9C29",
|
"addr": "mzW2hdZN2um7WBvTDerdahKqRgj3md9C29",
|
||||||
"balance": 1036.76206223,
|
"txApperances": 2034,
|
||||||
"totalReceived": 1036.76206223,
|
"balance": 1049.69744099,
|
||||||
|
"totalReceived": 1049.69744099,
|
||||||
"totalSent": 0
|
"totalSent": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"addr": "mjRmkmYzvZN3cA3aBKJgYJ65epn3WCG84H",
|
"addr": "mjRmkmYzvZN3cA3aBKJgYJ65epn3WCG84H",
|
||||||
|
"txApperances": 13343,
|
||||||
"balance": 46413.0,
|
"balance": 46413.0,
|
||||||
"totalReceived": 357130.17644359,
|
"totalReceived": 357130.17644359,
|
||||||
"totalSent": 310717.17644359
|
"totalSent": 310717.17644359
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"addr": "mgKY35SXqxFpcKK3Dq9mW9919N7wYXvcFM",
|
"addr": "mgKY35SXqxFpcKK3Dq9mW9919N7wYXvcFM",
|
||||||
|
"txApperances": 1,
|
||||||
"balance": 0.01979459,
|
"balance": 0.01979459,
|
||||||
"totalReceived": 0.01979459,
|
"totalReceived": 0.01979459,
|
||||||
"totalSent": 0,
|
"totalSent": 0,
|
||||||
"transactions": [ "91800d80bb4c69b238c9bfd94eb5155ab821e6b25cae5c79903d12853bbb4ed5" ]
|
"transactions": [ "91800d80bb4c69b238c9bfd94eb5155ab821e6b25cae5c79903d12853bbb4ed5" ]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"disabled":1,
|
||||||
"addr": "mmvP3mTe53qxHdPqXEvdu8WdC7GfQ2vmx5",
|
"addr": "mmvP3mTe53qxHdPqXEvdu8WdC7GfQ2vmx5",
|
||||||
"balance": 10580.50027254,
|
"balance": 10580.50027254,
|
||||||
"totalReceived": 12157.65075053,
|
"totalReceived": 12157.65075053,
|
|
@ -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
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -8,8 +8,7 @@ process.env.NODE_ENV = process.env.NODE_ENV || 'development';
|
||||||
var mongoose= require('mongoose'),
|
var mongoose= require('mongoose'),
|
||||||
assert = require('assert'),
|
assert = require('assert'),
|
||||||
config = require('../../config/config'),
|
config = require('../../config/config'),
|
||||||
Transaction = require('../../app/models/Transaction').class(),
|
Transaction = require('../../app/models/Transaction').class();
|
||||||
TransactionItem = require('../../app/models/TransactionItem');
|
|
||||||
|
|
||||||
|
|
||||||
mongoose.connection.on('error', function(err) { console.log(err); });
|
mongoose.connection.on('error', function(err) { console.log(err); });
|
||||||
|
@ -25,17 +24,17 @@ describe('Transaction', function(){
|
||||||
mongoose.connection.close();
|
mongoose.connection.close();
|
||||||
done();
|
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) {
|
Transaction.fromIdWithInfo(txid, function(err, tx) {
|
||||||
if (err) done(err);
|
if (err) done(err);
|
||||||
assert.equal(tx.txid, txid);
|
assert.equal(tx.txid, txid);
|
||||||
assert(!tx.info.isCoinBase);
|
assert(!tx.info.isCoinBase);
|
||||||
|
|
||||||
for(var i=0; i<20; i++)
|
for(var i=0; i<20; i++)
|
||||||
assert(parseFloat(tx.info.vin[i].value) === parseFloat(50));
|
assert(parseFloat(tx.info.vin[i].value) === parseFloat(50), 'input '+i);
|
||||||
assert(tx.info.vin[0].addr === 'msGKGCy2i8wbKS5Fo1LbWUTJnf1GoFFG59');
|
assert(tx.info.vin[0].addr === 'msGKGCy2i8wbKS5Fo1LbWUTJnf1GoFFG59', 'addr 0');
|
||||||
assert(tx.info.vin[1].addr === 'mfye7oHsdrHbydtj4coPXCasKad2eYSv5P');
|
assert(tx.info.vin[1].addr === 'mfye7oHsdrHbydtj4coPXCasKad2eYSv5P', 'addr 1');
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -100,12 +99,10 @@ describe('Transaction', function(){
|
||||||
|
|
||||||
var txid2 = '64496d005faee77ac5a18866f50af6b8dd1f60107d6795df34c402747af98608';
|
var txid2 = '64496d005faee77ac5a18866f50af6b8dd1f60107d6795df34c402747af98608';
|
||||||
it('create TX on the fly ' + txid2, function(done) {
|
it('create TX on the fly ' + txid2, function(done) {
|
||||||
TransactionItem.remove({txid: txid2}, function(err) {
|
Transaction.fromIdWithInfo(txid2, function(err, tx) {
|
||||||
Transaction.fromIdWithInfo(txid2, function(err, tx) {
|
if (err) return done(err);
|
||||||
if (err) return done(err);
|
assert.equal(tx.info.txid, txid2);
|
||||||
assert.equal(tx.info.txid, txid2);
|
done();
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -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();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -6,11 +6,6 @@
|
||||||
{
|
{
|
||||||
"txid": "21798ddc9664ac0ef618f52b151dda82dafaf2e26d2bbef6cdaf55a6957ca237",
|
"txid": "21798ddc9664ac0ef618f52b151dda82dafaf2e26d2bbef6cdaf55a6957ca237",
|
||||||
"items": [
|
"items": [
|
||||||
{
|
|
||||||
"addr": "mwcFwXv2Yquy4vJA4nnNLAbHVjrPdC8Q1Z",
|
|
||||||
"value_sat": -166224000,
|
|
||||||
"index": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"addr": "mzjLe62faUqCSjkwQkwPAL5nYyR8K132fA",
|
"addr": "mzjLe62faUqCSjkwQkwPAL5nYyR8K132fA",
|
||||||
"value_sat": 134574000,
|
"value_sat": 134574000,
|
||||||
|
@ -26,11 +21,6 @@
|
||||||
{
|
{
|
||||||
"txid": "b633a6249d4a2bc123e7f8a151cae2d4afd17aa94840009f8697270c7818ceee",
|
"txid": "b633a6249d4a2bc123e7f8a151cae2d4afd17aa94840009f8697270c7818ceee",
|
||||||
"items": [
|
"items": [
|
||||||
{
|
|
||||||
"addr": "mzjLe62faUqCSjkwQkwPAL5nYyR8K132fA",
|
|
||||||
"value_sat": -40790667,
|
|
||||||
"index": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"addr": "mhfQJUSissP6nLM5pz6DxHfctukrrLct2T",
|
"addr": "mhfQJUSissP6nLM5pz6DxHfctukrrLct2T",
|
||||||
"value_sat": 19300000,
|
"value_sat": 19300000,
|
||||||
|
@ -46,16 +36,6 @@
|
||||||
{
|
{
|
||||||
"txid": "ca2f42e44455b8a84434de139efea1fe2c7d71414a8939e0a20f518849085c3b",
|
"txid": "ca2f42e44455b8a84434de139efea1fe2c7d71414a8939e0a20f518849085c3b",
|
||||||
"items": [
|
"items": [
|
||||||
{
|
|
||||||
"addr": "mzeiUi4opeheWYveXqp8ebqHyVwYGA2s3x",
|
|
||||||
"value_sat": -1225871,
|
|
||||||
"index": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"addr": "mtMLijHAbG8CsgBbQGajsqav9p9wKUYad5",
|
|
||||||
"value_sat": -1201823,
|
|
||||||
"index": 1
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"addr": "mhqyL1nDQDo1WLH9qH8sjRjx2WwrnmAaXE",
|
"addr": "mhqyL1nDQDo1WLH9qH8sjRjx2WwrnmAaXE",
|
||||||
"value_sat": 1327746,
|
"value_sat": 1327746,
|
|
@ -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);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue