commit
f405f72e74
|
@ -37,13 +37,13 @@ exports.show = function(req, res) {
|
|||
* Show block by Height
|
||||
*/
|
||||
exports.blockindex = function(req, res, next, height) {
|
||||
Block.fromHeight(height, function(err, hash) {
|
||||
Block.blockIndex(height, function(err, hashStr) {
|
||||
if (err) {
|
||||
console.log(err);
|
||||
res.status(400).send('Bad Request'); // TODO
|
||||
}
|
||||
else {
|
||||
res.jsonp(hash);
|
||||
res.jsonp(hashStr);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
var Transaction = require('../models/Transaction');
|
||||
var Transaction = require('../models/Transaction').class();
|
||||
var Block = require('../models/Block');
|
||||
var Address = require('../models/Address');
|
||||
var async = require('async');
|
||||
|
@ -138,32 +138,8 @@ exports.list = function(req, res, next) {
|
|||
});
|
||||
}
|
||||
else {
|
||||
Transaction
|
||||
.find()
|
||||
.limit(limit)
|
||||
.sort('-time')
|
||||
.exec(function(err, txs) {
|
||||
if (err) {
|
||||
res.status(500).send(err);
|
||||
} else {
|
||||
var txids = [];
|
||||
for(var i=0;i<txs.length;i++) {
|
||||
txids.push(txs[i].txid);
|
||||
}
|
||||
|
||||
async.mapSeries(txids, getTransaction, function(err, alltxs) {
|
||||
if (err) {
|
||||
console.log(err);
|
||||
res.status(404).send('TX not found');
|
||||
}
|
||||
|
||||
res.jsonp({
|
||||
txs: alltxs,
|
||||
length: alltxs.length
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
res.jsonp({
|
||||
txs: [],
|
||||
});
|
||||
}
|
||||
|
||||
};
|
||||
|
|
|
@ -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}).sort({ts:-1}).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);
|
||||
});
|
||||
|
|
|
@ -8,7 +8,7 @@ var mongoose = require('mongoose'),
|
|||
RpcClient = require('bitcore/RpcClient').class(),
|
||||
util = require('bitcore/util/util'),
|
||||
BitcoreBlock= require('bitcore/Block').class(),
|
||||
Transaction = require('./Transaction'),
|
||||
TransactionOut = require('./TransactionOut'),
|
||||
config = require('../../config/config')
|
||||
;
|
||||
|
||||
|
@ -20,19 +20,51 @@ var BlockSchema = new Schema({
|
|||
// For now we keep this as short as possible
|
||||
// More fields will be propably added as we move
|
||||
// forward with the UX
|
||||
hash: {
|
||||
type: String,
|
||||
_id: {
|
||||
type: Buffer,
|
||||
index: true,
|
||||
unique: true,
|
||||
required: true,
|
||||
},
|
||||
time: Number,
|
||||
nextBlockHash: String,
|
||||
nextBlockHash: Buffer,
|
||||
isOrphan: Boolean,
|
||||
});
|
||||
|
||||
/**
|
||||
* Validations
|
||||
*/
|
||||
BlockSchema.virtual('hash').get(function () {
|
||||
return this._id;
|
||||
});
|
||||
|
||||
|
||||
BlockSchema.virtual('hash').set(function (hash) {
|
||||
this._id = hash;
|
||||
});
|
||||
|
||||
|
||||
BlockSchema.virtual('hashStr').get(function () {
|
||||
return this._id.toString('hex');
|
||||
});
|
||||
|
||||
|
||||
BlockSchema.virtual('hashStr').set(function (hashStr) {
|
||||
if (hashStr)
|
||||
this._id = new Buffer(hashStr,'hex');
|
||||
else
|
||||
this._id = null;
|
||||
});
|
||||
|
||||
|
||||
|
||||
BlockSchema.virtual('nextBlockHashStr').get(function () {
|
||||
return this.nextBlockHash.toString('hex');
|
||||
});
|
||||
|
||||
BlockSchema.virtual('nextBlockHashStr').set(function (hashStr) {
|
||||
if (hashStr)
|
||||
this.nextBlockHash = new Buffer(hashStr,'hex');
|
||||
else
|
||||
this.nextBlockHash = null;
|
||||
});
|
||||
|
||||
/*
|
||||
BlockSchema.path('title').validate(function(title) {
|
||||
|
@ -45,61 +77,56 @@ 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.hash = block.hash;
|
||||
newBlock.nextBlockHash = block.nextBlockHash;
|
||||
newBlock.hashStr = block.hash;
|
||||
newBlock.nextBlockHashStr = block.nextBlockHash;
|
||||
|
||||
|
||||
Transaction.createFromArray(block.tx, newBlock.time, 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);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
BlockSchema.statics.load = function(id, cb) {
|
||||
this.findOne({
|
||||
_id: id
|
||||
}).exec(cb);
|
||||
};
|
||||
|
||||
BlockSchema.statics.fromHeight = function(height, cb) {
|
||||
BlockSchema.statics.blockIndex = function(height, cb) {
|
||||
var rpc = new RpcClient(config.bitcoind);
|
||||
var hash = {};
|
||||
var hashStr = {};
|
||||
rpc.getBlockHash(height, function(err, bh){
|
||||
if (err) return cb(err);
|
||||
hash.blockHash = bh.result;
|
||||
cb(null, hash);
|
||||
hashStr.blockHash = bh.result;
|
||||
cb(null, hashStr);
|
||||
});
|
||||
};
|
||||
|
||||
BlockSchema.statics.fromHash = function(hash, cb) {
|
||||
BlockSchema.statics.fromHash = function(hashStr, cb) {
|
||||
var hash = new Buffer(hashStr, 'hex');
|
||||
|
||||
this.findOne({
|
||||
hash: hash,
|
||||
_id: hash,
|
||||
}).exec(cb);
|
||||
};
|
||||
|
||||
|
||||
BlockSchema.statics.fromHashWithInfo = function(hash, cb) {
|
||||
BlockSchema.statics.fromHashWithInfo = function(hashStr, cb) {
|
||||
var That = this;
|
||||
|
||||
this.fromHash(hash, function(err, block) {
|
||||
That.fromHash(hashStr, function(err, block) {
|
||||
if (err) return cb(err);
|
||||
|
||||
if (!block) {
|
||||
// No in mongo...but maybe in bitcoind... lets query it
|
||||
block = new That();
|
||||
|
||||
block.hash = hash;
|
||||
block.hashStr = hashStr;
|
||||
block.getInfo(function(err, blockInfo) {
|
||||
if (err) return cb(err);
|
||||
if (!blockInfo) return cb();
|
||||
|
@ -120,10 +147,10 @@ BlockSchema.statics.fromHashWithInfo = function(hash, cb) {
|
|||
// TODO: Can we store the rpc instance in the Block object?
|
||||
BlockSchema.methods.getInfo = function (next) {
|
||||
|
||||
var that = this;
|
||||
var self = this;
|
||||
var rpc = new RpcClient(config.bitcoind);
|
||||
|
||||
rpc.getBlock(this.hash, function(err, blockInfo) {
|
||||
rpc.getBlock(self.hashStr, function(err, blockInfo) {
|
||||
// Not found?
|
||||
if (err && err.code === -5) return next();
|
||||
|
||||
|
@ -134,12 +161,10 @@ BlockSchema.methods.getInfo = function (next) {
|
|||
* Any other way to lazy load a property in a mongoose object?
|
||||
*/
|
||||
|
||||
that.info = blockInfo.result;
|
||||
self.info = blockInfo.result;
|
||||
self.info.reward = BitcoreBlock.getBlockValue(self.info.height) / util.COIN ;
|
||||
|
||||
that.info.reward = BitcoreBlock.getBlockValue(that.info.height) / util.COIN ;
|
||||
|
||||
//console.log("THAT", that);
|
||||
return next(null, that.info);
|
||||
return next(null, self.info);
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
@ -1,361 +1,86 @@
|
|||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var mongoose = require('mongoose'),
|
||||
Schema = mongoose.Schema,
|
||||
async = require('async'),
|
||||
RpcClient = require('bitcore/RpcClient').class(),
|
||||
Transaction = 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 = 5;
|
||||
|
||||
// TODO: use bitcore networks module
|
||||
var genesisTXID = '4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b';
|
||||
|
||||
/**
|
||||
*/
|
||||
var TransactionSchema = new Schema({
|
||||
// For now we keep this as short as possible
|
||||
// More fields will be propably added as we move
|
||||
// forward with the UX
|
||||
txid: {
|
||||
type: String,
|
||||
index: true,
|
||||
unique: true,
|
||||
},
|
||||
/* TODO?
|
||||
orphaned: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
*/
|
||||
time: Number,
|
||||
});
|
||||
|
||||
/**
|
||||
* Statics
|
||||
*/
|
||||
|
||||
TransactionSchema.statics.load = function(id, cb) {
|
||||
this.findOne({
|
||||
_id: id
|
||||
}).exec(cb);
|
||||
};
|
||||
require('classtool');
|
||||
|
||||
|
||||
TransactionSchema.statics.fromId = function(txid, cb) {
|
||||
this.findOne({
|
||||
txid: txid,
|
||||
}).exec(cb);
|
||||
};
|
||||
function spec() {
|
||||
|
||||
var util = require('bitcore/util/util'),
|
||||
TransactionRpc = require('../../lib/TransactionRpc').class(),
|
||||
TransactionOut = require('./TransactionOut'),
|
||||
async = require('async');
|
||||
|
||||
var CONCURRENCY = 20;
|
||||
|
||||
TransactionSchema.statics.fromIdWithInfo = function(txid, cb) {
|
||||
var That = this;
|
||||
function Transaction() {
|
||||
this.txid = null;
|
||||
}
|
||||
|
||||
this.fromId(txid, function(err, tx) {
|
||||
if (err) return cb(err);
|
||||
|
||||
if (!tx) {
|
||||
// No in mongo...but maybe in bitcoind... lets query it
|
||||
tx = new That();
|
||||
|
||||
tx.txid = txid;
|
||||
tx.fillInfo(function(err, txInfo) {
|
||||
|
||||
if (err) return cb(err);
|
||||
if (!txInfo) return cb();
|
||||
|
||||
tx.save(function(err) {
|
||||
return cb(err,tx);
|
||||
});
|
||||
});
|
||||
}
|
||||
else {
|
||||
tx.fillInfo(function(err) {
|
||||
return cb(err,tx);
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
TransactionSchema.statics.createFromArray = function(txs, time, next) {
|
||||
var that = this;
|
||||
if (!txs) return next();
|
||||
var mongo_txs = [];
|
||||
|
||||
async.forEachLimit(txs, CONCURRENCY, function(txid, cb) {
|
||||
|
||||
that.explodeTransactionItems( txid, time, function(err, addrs) {
|
||||
if (err) return next(err);
|
||||
if (addrs) {
|
||||
async.each(addrs, function(addr){
|
||||
sockets.broadcast_address_tx(addr, {'txid': txid});
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
that.create({txid: txid, time: time}, function(err, new_tx) {
|
||||
if (err && ! err.toString().match(/E11000/)) return cb(err);
|
||||
|
||||
if (new_tx) {
|
||||
mongo_txs.push(new_tx);
|
||||
}
|
||||
|
||||
return cb();
|
||||
});
|
||||
});
|
||||
},
|
||||
function(err) {
|
||||
return next(err, mongo_txs);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
TransactionSchema.statics.explodeTransactionItems = function(txid, time, cb) {
|
||||
var addrs = [];
|
||||
|
||||
// Is it from genesis block? (testnet==livenet)
|
||||
// TODO: parse it from networks.genesisTX
|
||||
if (txid === genesisTXID) return cb();
|
||||
|
||||
this.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) {
|
||||
|
||||
TransactionItem.create({
|
||||
txid : txid,
|
||||
value_sat : -1 * i.valueSat,
|
||||
addr : i.addr,
|
||||
index : i.n,
|
||||
ts : time,
|
||||
}, 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 && !err.message.match(/E11000/) ) console.log (err);
|
||||
async.forEachLimit(info.vout, CONCURRENCY, function(o, next_out) {
|
||||
|
||||
/*
|
||||
* TODO Support multisigs
|
||||
*/
|
||||
if (o.value && o.scriptPubKey && o.scriptPubKey.addresses && o.scriptPubKey.addresses[0]) {
|
||||
TransactionItem.create({
|
||||
txid : txid,
|
||||
value_sat : o.valueSat,
|
||||
addr : o.scriptPubKey.addresses[0], // TODO: only address 0?
|
||||
index : o.n,
|
||||
ts : time,
|
||||
}, 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 && ! err.toString().match(/E11000/)) return cb(err);
|
||||
return cb(null, addrs);
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
|
||||
TransactionSchema.statics.getOutpoints = function (tx, next) {
|
||||
|
||||
if (tx.isCoinBase()) return next();
|
||||
|
||||
var rpc = new RpcClient(config.bitcoind);
|
||||
var network = ( config.network === 'testnet') ? networks.testnet : networks.livenet ;
|
||||
|
||||
async.forEachLimit(tx.ins, CONCURRENCY, function(i, cb) {
|
||||
|
||||
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 Transaction();
|
||||
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);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
TransactionSchema.statics.queryInfo = function(txid, cb) {
|
||||
var that = 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');
|
||||
Transaction.fromIdWithInfo = function (txid,cb) {
|
||||
var tx = new Transaction();
|
||||
tx.parse(b);
|
||||
tx.txid = txid;
|
||||
|
||||
that.getOutpoints(tx, function(err) {
|
||||
tx._fillInfo(function(err) {
|
||||
if (err) return cb(err);
|
||||
if (! tx.info ) return cb();
|
||||
|
||||
// Copy TX relevant values to .info
|
||||
return cb(err,tx);
|
||||
});
|
||||
};
|
||||
|
||||
var c = 0;
|
||||
var valueIn = bignum(0);
|
||||
var valueOut = bignum(0);
|
||||
|
||||
if ( tx.isCoinBase() ) {
|
||||
info.isCoinBase = true;
|
||||
|
||||
Transaction.prototype._fillInfo = function(next) {
|
||||
var self = this;
|
||||
|
||||
TransactionRpc.getRpcInfo(self.txid, function(err, info) {
|
||||
if (err) return next(err);
|
||||
Transaction._fillOutpoints(info, function() {
|
||||
self.info = info;
|
||||
return next();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
|
||||
Transaction._fillOutpoints = function(info, cb) {
|
||||
|
||||
if (!info || info.isCoinBase) return cb();
|
||||
|
||||
var valueIn = 0;
|
||||
var incompleteInputs = 0;
|
||||
async.eachLimit(info.vin, CONCURRENCY, function(i, c_in) {
|
||||
TransactionOut.fromTxIdN(i.txid, i.vout, function(err, out) {
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
i.addr = out.addr;
|
||||
i.valueSat = out.value_sat;
|
||||
i.value = out.value_sat / util.COIN;
|
||||
|
||||
valueIn += i.valueSat;
|
||||
return c_in();
|
||||
});
|
||||
},
|
||||
function () {
|
||||
if (! incompleteInputs ) {
|
||||
info.valueIn = valueIn / util.COIN;
|
||||
info.fees = (valueIn - parseInt(info.valueOut * util.COIN)) / util.COIN ;
|
||||
}
|
||||
else {
|
||||
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++;
|
||||
});
|
||||
info.incompleteInputs = 1;
|
||||
}
|
||||
|
||||
c=0;
|
||||
tx.outs.forEach( function(i) {
|
||||
var n = util.valueToBigInt(i.v).toNumber();
|
||||
valueOut = valueOut.add(n);
|
||||
|
||||
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);
|
||||
return cb();
|
||||
});
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
return Transaction;
|
||||
}
|
||||
module.defineClass(spec);
|
||||
|
||||
|
||||
|
||||
TransactionSchema.methods.fillInfo = function(next) {
|
||||
var that = this;
|
||||
|
||||
mongoose.model('Transaction', TransactionSchema).queryInfo(that.txid, function(err, info) {
|
||||
if (err) return next(err);
|
||||
|
||||
that.info = info;
|
||||
if (! that.info) {
|
||||
return next();
|
||||
}
|
||||
else {
|
||||
that.info.time = that.time;
|
||||
return next();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
|
||||
module.exports = mongoose.model('Transaction', TransactionSchema);
|
||||
|
|
|
@ -1,54 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
var mongoose = require('mongoose'),
|
||||
Schema = mongoose.Schema;
|
||||
|
||||
var TransactionItemSchema = new Schema({
|
||||
txid: String,
|
||||
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({txid: 1, index: 1, value_sat: 1}, {unique: true, dropDups: true});
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
}));
|
||||
});
|
||||
};
|
||||
|
||||
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
|
||||
|
||||
# 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
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -62,7 +62,7 @@ function spec() {
|
|||
if (this.verbose) {
|
||||
console.log('[p2p_sync] Handle tx: ' + tx.hash);
|
||||
}
|
||||
this.sync.storeTxs([tx.hash], null, function(err) {
|
||||
this.sync.storeTxs([tx.hash], function(err) {
|
||||
if (err) {
|
||||
console.log('[PeerSync.js.71:err:]',err); //TODO
|
||||
console.log('[p2p_sync] Error in handle TX: ' + JSON.stringify(err));
|
||||
|
|
58
lib/Sync.js
58
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 Transaction = require('../app/models/Transaction');
|
||||
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,49 +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.transactions.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.storeTxs = function(txs, inTime, cb) {
|
||||
Sync.prototype._handleBroadcast = function(block, inserted_txs, updated_addrs) {
|
||||
var self = this;
|
||||
|
||||
var time = inTime ? inTime : Math.round(new Date().getTime() / 1000);
|
||||
if (block && self.opts.broadcast_blocks) {
|
||||
sockets.broadcast_block(block);
|
||||
}
|
||||
|
||||
Transaction.createFromArray(txs, time, function(err, inserted_txs) {
|
||||
if (!err && inserted_txs && self.opts.broadcast_txs) {
|
||||
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});
|
||||
|
||||
inserted_txs.forEach(function(tx) {
|
||||
sockets.broadcast_tx(tx);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
Sync.prototype.storeTxs = function(txs, cb) {
|
||||
var self = this;
|
||||
|
||||
TransactionOut.createFromArray(txs, function(err, inserted_txs, updated_addrs) {
|
||||
if (err) return cb(err);
|
||||
|
||||
self._handleBroadcast(null, inserted_txs, updated_addrs);
|
||||
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 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++;
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
#!/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'),
|
||||
config = require('../../config/config'),
|
||||
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('TransactionOut', function(){
|
||||
|
||||
before(function(done) {
|
||||
mongoose.connect(config.db);
|
||||
done();
|
||||
});
|
||||
|
||||
after(function(done) {
|
||||
mongoose.connection.close();
|
||||
done();
|
||||
});
|
||||
|
||||
txItemsValid.forEach( function(v) {
|
||||
if (v.disabled) return;
|
||||
it('test a exploding tx ' + v.txid, function(done) {
|
||||
|
||||
// Remove first
|
||||
TransactionOut.removeFromTxId(v.txid, function(err) {
|
||||
TransactionOut._explodeTransactionOuts(v.txid, function(err, tx) {
|
||||
if (err) done(err);
|
||||
|
||||
TransactionOut
|
||||
.fromTxId( v.txid, function(err, readItems) {
|
||||
|
||||
var unmatch={};
|
||||
|
||||
v.items.forEach(function(validItem){
|
||||
unmatch[validItem.addr] =1;
|
||||
});
|
||||
v.items.forEach(function(validItem){
|
||||
var readItem = readItems.shift();
|
||||
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();
|
||||
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
|
@ -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
|
||||
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) {
|
||||
|
|
@ -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,
|
|
@ -1,11 +1,12 @@
|
|||
#!/usr/bin/env node
|
||||
'use strict';
|
||||
|
||||
process.env.NODE_ENV = process.env.NODE_ENV || 'development';
|
||||
|
||||
|
||||
var TESTING_BLOCK = '000000000185678d3d7ecc9962c96418174431f93fe20bf216d5565272423f74';
|
||||
|
||||
var
|
||||
var
|
||||
mongoose= require('mongoose'),
|
||||
assert = require('assert'),
|
||||
config = require('../../config/config'),
|
||||
|
@ -28,21 +29,46 @@ describe('Block fromHashWithInfo', function(){
|
|||
|
||||
|
||||
it('should poll block\'s info from mongoose', function(done) {
|
||||
var block2 = Block.fromHashWithInfo(TESTING_BLOCK, function(err, b2) {
|
||||
Block.fromHashWithInfo(TESTING_BLOCK, function(err, b2) {
|
||||
if (err) done(err);
|
||||
|
||||
assert.equal(b2.hash, TESTING_BLOCK);
|
||||
|
||||
var h = new Buffer(TESTING_BLOCK,'hex');
|
||||
assert(b2.hashStr === TESTING_BLOCK);
|
||||
assert.equal(b2.hashStr, TESTING_BLOCK);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should poll block\'s info from bitcoind', function(done) {
|
||||
var block2 = Block.fromHashWithInfo(TESTING_BLOCK, function(err, b2) {
|
||||
Block.fromHashWithInfo(TESTING_BLOCK, function(err, b2) {
|
||||
if (err) done(err);
|
||||
assert.equal(b2.info.hash, TESTING_BLOCK);
|
||||
assert.equal(b2.info.chainwork, '000000000000000000000000000000000000000000000000001b6dc969ffe847');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('hash Virtuals SET', function(done) {
|
||||
var b = new Block();
|
||||
b.hashStr = 'a1a2';
|
||||
assert.equal(b.hash.toString('hex'),'a1a2');
|
||||
b.nextBlockHashStr = 'a1a3';
|
||||
assert.equal(b.nextBlockHash.toString('hex'),'a1a3');
|
||||
done();
|
||||
});
|
||||
|
||||
|
||||
it('hash Virtuals GET', function(done) {
|
||||
var b = new Block();
|
||||
b.hash = new Buffer('a1a2','hex');
|
||||
assert.equal(b.hashStr,'a1a2');
|
||||
|
||||
|
||||
b.nextBlockHash = new Buffer('b2b1','hex');
|
||||
assert.equal(b.nextBlockHashStr,'b2b1');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
|
@ -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
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,20 +1,16 @@
|
|||
#!/usr/bin/env node
|
||||
'use strict';
|
||||
|
||||
process.env.NODE_ENV = process.env.NODE_ENV || 'development';
|
||||
|
||||
|
||||
|
||||
var
|
||||
mongoose= require('mongoose'),
|
||||
var mongoose= require('mongoose'),
|
||||
assert = require('assert'),
|
||||
config = require('../../config/config'),
|
||||
Transaction = require('../../app/models/Transaction'),
|
||||
TransactionItem = require('../../app/models/TransactionItem'),
|
||||
fs = require('fs'),
|
||||
util = require('util');
|
||||
Transaction = require('../../app/models/Transaction').class();
|
||||
|
||||
|
||||
var txItemsValid = JSON.parse(fs.readFileSync('test/model/txitems.json'));
|
||||
mongoose.connection.on('error', function(err) { console.log(err); });
|
||||
|
||||
describe('Transaction', function(){
|
||||
|
@ -28,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();
|
||||
});
|
||||
});
|
||||
|
@ -103,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();
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -122,43 +116,5 @@ describe('Transaction', function(){
|
|||
});
|
||||
});
|
||||
|
||||
|
||||
txItemsValid.forEach( function(v) {
|
||||
if (v.disabled) return;
|
||||
it('test a exploding TX ' + v.txid, function(done) {
|
||||
|
||||
// Remove first
|
||||
TransactionItem.remove({txid: v.txid}, function(err) {
|
||||
|
||||
var now = Math.round(new Date().getTime() / 1000);
|
||||
Transaction.explodeTransactionItems(v.txid, now, function(err, tx) {
|
||||
if (err) done(err);
|
||||
|
||||
TransactionItem
|
||||
.fromTxId( v.txid, function(err, readItems) {
|
||||
|
||||
var unmatch={};
|
||||
|
||||
v.items.forEach(function(validItem){
|
||||
unmatch[validItem.addr] =1;
|
||||
});
|
||||
v.items.forEach(function(validItem){
|
||||
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\tUnmatchs:' + Object.keys(unmatch) + "\n\n" +valid + '\nvs.\n' + readItems);
|
||||
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",
|
||||
"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,
|
|
@ -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