commit
f405f72e74
|
@ -37,13 +37,13 @@ exports.show = function(req, res) {
|
||||||
* Show block by Height
|
* Show block by Height
|
||||||
*/
|
*/
|
||||||
exports.blockindex = function(req, res, next, height) {
|
exports.blockindex = function(req, res, next, height) {
|
||||||
Block.fromHeight(height, function(err, hash) {
|
Block.blockIndex(height, function(err, hashStr) {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.log(err);
|
console.log(err);
|
||||||
res.status(400).send('Bad Request'); // TODO
|
res.status(400).send('Bad Request'); // TODO
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
res.jsonp(hash);
|
res.jsonp(hashStr);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
/**
|
/**
|
||||||
* Module dependencies.
|
* Module dependencies.
|
||||||
*/
|
*/
|
||||||
var Transaction = require('../models/Transaction');
|
var Transaction = require('../models/Transaction').class();
|
||||||
var Block = require('../models/Block');
|
var Block = require('../models/Block');
|
||||||
var Address = require('../models/Address');
|
var Address = require('../models/Address');
|
||||||
var async = require('async');
|
var async = require('async');
|
||||||
|
@ -138,32 +138,8 @@ exports.list = function(req, res, next) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Transaction
|
res.jsonp({
|
||||||
.find()
|
txs: [],
|
||||||
.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
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -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}).sort({ts:-1}).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(),
|
||||||
Transaction = require('./Transaction'),
|
TransactionOut = require('./TransactionOut'),
|
||||||
config = require('../../config/config')
|
config = require('../../config/config')
|
||||||
;
|
;
|
||||||
|
|
||||||
|
@ -20,19 +20,51 @@ var BlockSchema = new Schema({
|
||||||
// For now we keep this as short as possible
|
// For now we keep this as short as possible
|
||||||
// More fields will be propably added as we move
|
// More fields will be propably added as we move
|
||||||
// forward with the UX
|
// forward with the UX
|
||||||
hash: {
|
_id: {
|
||||||
type: String,
|
type: Buffer,
|
||||||
index: true,
|
index: true,
|
||||||
unique: true,
|
unique: true,
|
||||||
|
required: true,
|
||||||
},
|
},
|
||||||
time: Number,
|
time: Number,
|
||||||
nextBlockHash: String,
|
nextBlockHash: Buffer,
|
||||||
isOrphan: Boolean,
|
isOrphan: Boolean,
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
BlockSchema.virtual('hash').get(function () {
|
||||||
* Validations
|
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) {
|
BlockSchema.path('title').validate(function(title) {
|
||||||
|
@ -45,61 +77,56 @@ 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.hash = block.hash;
|
newBlock.hashStr = block.hash;
|
||||||
newBlock.nextBlockHash = block.nextBlockHash;
|
newBlock.nextBlockHashStr = block.nextBlockHash;
|
||||||
|
|
||||||
|
TransactionOut.createFromArray(block.tx, function(err, inserted_txs, update_addrs) {
|
||||||
Transaction.createFromArray(block.tx, newBlock.time, function(err, inserted_txs) {
|
|
||||||
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);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
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 rpc = new RpcClient(config.bitcoind);
|
||||||
var hash = {};
|
var hashStr = {};
|
||||||
rpc.getBlockHash(height, function(err, bh){
|
rpc.getBlockHash(height, function(err, bh){
|
||||||
if (err) return cb(err);
|
if (err) return cb(err);
|
||||||
hash.blockHash = bh.result;
|
hashStr.blockHash = bh.result;
|
||||||
cb(null, hash);
|
cb(null, hashStr);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
BlockSchema.statics.fromHash = function(hash, cb) {
|
BlockSchema.statics.fromHash = function(hashStr, cb) {
|
||||||
|
var hash = new Buffer(hashStr, 'hex');
|
||||||
|
|
||||||
this.findOne({
|
this.findOne({
|
||||||
hash: hash,
|
_id: hash,
|
||||||
}).exec(cb);
|
}).exec(cb);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
BlockSchema.statics.fromHashWithInfo = function(hash, cb) {
|
BlockSchema.statics.fromHashWithInfo = function(hashStr, cb) {
|
||||||
var That = this;
|
var That = this;
|
||||||
|
|
||||||
this.fromHash(hash, function(err, block) {
|
That.fromHash(hashStr, function(err, block) {
|
||||||
if (err) return cb(err);
|
if (err) return cb(err);
|
||||||
|
|
||||||
if (!block) {
|
if (!block) {
|
||||||
// No in mongo...but maybe in bitcoind... lets query it
|
// No in mongo...but maybe in bitcoind... lets query it
|
||||||
block = new That();
|
block = new That();
|
||||||
|
|
||||||
block.hash = hash;
|
block.hashStr = hashStr;
|
||||||
block.getInfo(function(err, blockInfo) {
|
block.getInfo(function(err, blockInfo) {
|
||||||
if (err) return cb(err);
|
if (err) return cb(err);
|
||||||
if (!blockInfo) return cb();
|
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?
|
// TODO: Can we store the rpc instance in the Block object?
|
||||||
BlockSchema.methods.getInfo = function (next) {
|
BlockSchema.methods.getInfo = function (next) {
|
||||||
|
|
||||||
var that = this;
|
var self = this;
|
||||||
var rpc = new RpcClient(config.bitcoind);
|
var rpc = new RpcClient(config.bitcoind);
|
||||||
|
|
||||||
rpc.getBlock(this.hash, function(err, blockInfo) {
|
rpc.getBlock(self.hashStr, function(err, blockInfo) {
|
||||||
// Not found?
|
// Not found?
|
||||||
if (err && err.code === -5) return next();
|
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?
|
* 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 ;
|
return next(null, self.info);
|
||||||
|
|
||||||
//console.log("THAT", that);
|
|
||||||
return next(null, that.info);
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,361 +1,86 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
/**
|
require('classtool');
|
||||||
* 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);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
TransactionSchema.statics.fromId = function(txid, cb) {
|
function spec() {
|
||||||
this.findOne({
|
|
||||||
txid: txid,
|
|
||||||
}).exec(cb);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
var util = require('bitcore/util/util'),
|
||||||
|
TransactionRpc = require('../../lib/TransactionRpc').class(),
|
||||||
|
TransactionOut = require('./TransactionOut'),
|
||||||
|
async = require('async');
|
||||||
|
|
||||||
TransactionSchema.statics.fromIdWithInfo = function(txid, cb) {
|
var CONCURRENCY = 20;
|
||||||
var That = this;
|
|
||||||
|
|
||||||
this.fromId(txid, function(err, tx) {
|
function Transaction() {
|
||||||
if (err) return cb(err);
|
this.txid = null;
|
||||||
|
}
|
||||||
|
|
||||||
if (!tx) {
|
Transaction.fromIdWithInfo = function (txid,cb) {
|
||||||
// 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');
|
|
||||||
var tx = new Transaction();
|
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 (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 {
|
else {
|
||||||
tx.ins.forEach(function(i) {
|
info.incompleteInputs = 1;
|
||||||
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++;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
return cb();
|
||||||
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 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
|
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);
|
||||||
|
|
||||||
|
|
|
@ -62,7 +62,7 @@ function spec() {
|
||||||
if (this.verbose) {
|
if (this.verbose) {
|
||||||
console.log('[p2p_sync] Handle tx: ' + tx.hash);
|
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) {
|
if (err) {
|
||||||
console.log('[PeerSync.js.71:err:]',err); //TODO
|
console.log('[PeerSync.js.71:err:]',err); //TODO
|
||||||
console.log('[p2p_sync] Error in handle TX: ' + JSON.stringify(err));
|
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 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 Transaction = require('../app/models/Transaction');
|
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,49 +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.transactions.drop(b);},
|
function(b) { try {self.db.collections.transactionitems.drop(b);} catch (e) { return b(); } },
|
||||||
function(b) { return self.db.collections.transactionitems.drop(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.storeTxs = function(txs, inTime, cb) {
|
Sync.prototype._handleBroadcast = function(block, inserted_txs, updated_addrs) {
|
||||||
var self = this;
|
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 (inserted_txs && self.opts.broadcast_txs) {
|
||||||
if (!err && 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);
|
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++;
|
||||||
|
|
|
@ -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
|
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,
|
|
@ -1,4 +1,5 @@
|
||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
|
'use strict';
|
||||||
|
|
||||||
process.env.NODE_ENV = process.env.NODE_ENV || 'development';
|
process.env.NODE_ENV = process.env.NODE_ENV || 'development';
|
||||||
|
|
||||||
|
@ -28,21 +29,46 @@ describe('Block fromHashWithInfo', function(){
|
||||||
|
|
||||||
|
|
||||||
it('should poll block\'s info from mongoose', function(done) {
|
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);
|
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();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should poll block\'s info from bitcoind', function(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);
|
if (err) done(err);
|
||||||
assert.equal(b2.info.hash, TESTING_BLOCK);
|
assert.equal(b2.info.hash, TESTING_BLOCK);
|
||||||
assert.equal(b2.info.chainwork, '000000000000000000000000000000000000000000000000001b6dc969ffe847');
|
assert.equal(b2.info.chainwork, '000000000000000000000000000000000000000000000000001b6dc969ffe847');
|
||||||
done();
|
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
|
#!/usr/bin/env node
|
||||||
|
'use strict';
|
||||||
|
|
||||||
process.env.NODE_ENV = process.env.NODE_ENV || 'development';
|
process.env.NODE_ENV = process.env.NODE_ENV || 'development';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
var
|
var mongoose= require('mongoose'),
|
||||||
mongoose= require('mongoose'),
|
|
||||||
assert = require('assert'),
|
assert = require('assert'),
|
||||||
config = require('../../config/config'),
|
config = require('../../config/config'),
|
||||||
Transaction = require('../../app/models/Transaction'),
|
Transaction = require('../../app/models/Transaction').class();
|
||||||
TransactionItem = require('../../app/models/TransactionItem'),
|
|
||||||
fs = require('fs'),
|
|
||||||
util = require('util');
|
|
||||||
|
|
||||||
|
|
||||||
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('Transaction', function(){
|
describe('Transaction', function(){
|
||||||
|
@ -28,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();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -103,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();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -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",
|
"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