commit
05d8b7c76a
|
@ -5,7 +5,7 @@
|
|||
*/
|
||||
var common = require('./common'),
|
||||
async = require('async'),
|
||||
BlockDb = require('../../lib/BlockDb').class();
|
||||
BlockDb = require('../../lib/BlockDb');
|
||||
|
||||
var bdb = new BlockDb();
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ exports.show = function(req, res) {
|
|||
}
|
||||
else {
|
||||
var option = req.query.q;
|
||||
var statusObject = Status.new();
|
||||
var statusObject = new Status();
|
||||
|
||||
var returnJsonp = function (err) {
|
||||
if (err || ! statusObject)
|
||||
|
|
|
@ -7,12 +7,10 @@ var Address = require('../models/Address');
|
|||
var async = require('async');
|
||||
var common = require('./common');
|
||||
|
||||
var TransactionDb = require('../../lib/TransactionDb').class();
|
||||
var BlockDb = require('../../lib/BlockDb').class();
|
||||
var Rpc = require('../../lib/Rpc').class();
|
||||
var Rpc = require('../../lib/Rpc');
|
||||
|
||||
var tDb = new TransactionDb();
|
||||
var bdb = new BlockDb();
|
||||
var tDb = require('../../lib/TransactionDb').default();
|
||||
var bdb = require('../../lib/BlockDb').default();
|
||||
|
||||
exports.send = function(req, res) {
|
||||
Rpc.sendRawTransaction(req.body.rawtx, function(err, txid) {
|
||||
|
|
|
@ -1,194 +1,189 @@
|
|||
'use strict';
|
||||
|
||||
require('classtool');
|
||||
var imports = require('soop').imports();
|
||||
var async = require('async');
|
||||
var BitcoreAddress = require('bitcore/Address');
|
||||
var BitcoreTransaction = require('bitcore/Transaction');
|
||||
var BitcoreUtil = require('bitcore/util/util');
|
||||
var Parser = require('bitcore/util/BinaryParser');
|
||||
var Buffer = require('buffer').Buffer;
|
||||
var TransactionDb = imports.TransactionDb || require('../../lib/TransactionDb').default();
|
||||
var CONCURRENCY = 5;
|
||||
|
||||
function Address(addrStr) {
|
||||
this.balanceSat = 0;
|
||||
this.totalReceivedSat = 0;
|
||||
this.totalSentSat = 0;
|
||||
|
||||
this.unconfirmedBalanceSat = 0;
|
||||
|
||||
this.txApperances = 0;
|
||||
this.unconfirmedTxApperances= 0;
|
||||
|
||||
// TODO store only txids? +index? +all?
|
||||
this.transactions = [];
|
||||
|
||||
var a = new BitcoreAddress(addrStr);
|
||||
a.validate();
|
||||
this.addrStr = addrStr;
|
||||
|
||||
Object.defineProperty(this, 'totalSent', {
|
||||
get: function() {
|
||||
return parseFloat(this.totalSentSat) / parseFloat(BitcoreUtil.COIN);
|
||||
},
|
||||
set: function(i) {
|
||||
this.totalSentSat = i * BitcoreUtil.COIN;
|
||||
},
|
||||
enumerable: 1,
|
||||
});
|
||||
|
||||
Object.defineProperty(this, 'balance', {
|
||||
get: function() {
|
||||
return parseFloat(this.balanceSat) / parseFloat(BitcoreUtil.COIN);
|
||||
},
|
||||
set: function(i) {
|
||||
this.balance = i * BitcoreUtil.COIN;
|
||||
},
|
||||
enumerable: 1,
|
||||
});
|
||||
|
||||
Object.defineProperty(this, 'totalReceived', {
|
||||
get: function() {
|
||||
return parseFloat(this.totalReceivedSat) / parseFloat(BitcoreUtil.COIN);
|
||||
},
|
||||
set: function(i) {
|
||||
this.totalReceived = i * BitcoreUtil.COIN;
|
||||
},
|
||||
enumerable: 1,
|
||||
});
|
||||
|
||||
|
||||
function spec() {
|
||||
var async = require('async');
|
||||
var BitcoreAddress = require('bitcore/Address').class();
|
||||
var BitcoreUtil = require('bitcore/util/util');
|
||||
var TransactionDb = require('../../lib/TransactionDb').class();
|
||||
var BitcoreTransaction = require('bitcore/Transaction').class();
|
||||
var Parser = require('bitcore/util/BinaryParser').class();
|
||||
var Buffer = require('buffer').Buffer;
|
||||
var CONCURRENCY = 5;
|
||||
Object.defineProperty(this, 'unconfirmedBalance', {
|
||||
get: function() {
|
||||
return parseFloat(this.unconfirmedBalanceSat) / parseFloat(BitcoreUtil.COIN);
|
||||
},
|
||||
set: function(i) {
|
||||
this.unconfirmedBalanceSat = i * BitcoreUtil.COIN;
|
||||
},
|
||||
enumerable: 1,
|
||||
});
|
||||
|
||||
function Address(addrStr) {
|
||||
this.balanceSat = 0;
|
||||
this.totalReceivedSat = 0;
|
||||
this.totalSentSat = 0;
|
||||
}
|
||||
|
||||
this.unconfirmedBalanceSat = 0;
|
||||
Address.prototype._getScriptPubKey = function(hex,n) {
|
||||
// ScriptPubKey is not provided by bitcoind RPC, so we parse it from tx hex.
|
||||
|
||||
this.txApperances = 0;
|
||||
this.unconfirmedTxApperances= 0;
|
||||
var parser = new Parser(new Buffer(hex,'hex'));
|
||||
var tx = new BitcoreTransaction();
|
||||
tx.parse(parser);
|
||||
return (tx.outs[n].s.toString('hex'));
|
||||
};
|
||||
|
||||
// TODO store only txids? +index? +all?
|
||||
this.transactions = [];
|
||||
Address.prototype.getUtxo = function(next) {
|
||||
var self = this;
|
||||
if (!self.addrStr) return next();
|
||||
|
||||
var a = new BitcoreAddress(addrStr);
|
||||
a.validate();
|
||||
this.addrStr = addrStr;
|
||||
|
||||
Object.defineProperty(this, 'totalSent', {
|
||||
get: function() {
|
||||
return parseFloat(this.totalSentSat) / parseFloat(BitcoreUtil.COIN);
|
||||
},
|
||||
set: function(i) {
|
||||
this.totalSentSat = i * BitcoreUtil.COIN;
|
||||
},
|
||||
enumerable: 1,
|
||||
});
|
||||
var ret = [];
|
||||
var db = TransactionDb;
|
||||
|
||||
Object.defineProperty(this, 'balance', {
|
||||
get: function() {
|
||||
return parseFloat(this.balanceSat) / parseFloat(BitcoreUtil.COIN);
|
||||
},
|
||||
set: function(i) {
|
||||
this.balance = i * BitcoreUtil.COIN;
|
||||
},
|
||||
enumerable: 1,
|
||||
});
|
||||
db.fromAddr(self.addrStr, function(err,txOut){
|
||||
if (err) return next(err);
|
||||
|
||||
Object.defineProperty(this, 'totalReceived', {
|
||||
get: function() {
|
||||
return parseFloat(this.totalReceivedSat) / parseFloat(BitcoreUtil.COIN);
|
||||
},
|
||||
set: function(i) {
|
||||
this.totalReceived = i * BitcoreUtil.COIN;
|
||||
},
|
||||
enumerable: 1,
|
||||
});
|
||||
// Complete utxo info
|
||||
async.eachLimit(txOut,CONCURRENCY,function (txItem, a_c) {
|
||||
db.fromIdInfoSimple(txItem.txid, function(err, info) {
|
||||
|
||||
var scriptPubKey = self._getScriptPubKey(info.hex, txItem.index);
|
||||
|
||||
Object.defineProperty(this, 'unconfirmedBalance', {
|
||||
get: function() {
|
||||
return parseFloat(this.unconfirmedBalanceSat) / parseFloat(BitcoreUtil.COIN);
|
||||
},
|
||||
set: function(i) {
|
||||
this.unconfirmedBalanceSat = i * BitcoreUtil.COIN;
|
||||
},
|
||||
enumerable: 1,
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
Address.prototype._getScriptPubKey = function(hex,n) {
|
||||
// ScriptPubKey is not provided by bitcoind RPC, so we parse it from tx hex.
|
||||
|
||||
var parser = new Parser(new Buffer(hex,'hex'));
|
||||
var tx = new BitcoreTransaction();
|
||||
tx.parse(parser);
|
||||
return (tx.outs[n].s.toString('hex'));
|
||||
};
|
||||
|
||||
Address.prototype.getUtxo = function(next) {
|
||||
var self = this;
|
||||
if (!self.addrStr) return next();
|
||||
|
||||
var ret = [];
|
||||
var db = new TransactionDb();
|
||||
|
||||
db.fromAddr(self.addrStr, function(err,txOut){
|
||||
if (err) return next(err);
|
||||
|
||||
// Complete utxo info
|
||||
async.eachLimit(txOut,CONCURRENCY,function (txItem, a_c) {
|
||||
db.fromIdInfoSimple(txItem.txid, function(err, info) {
|
||||
|
||||
// we are filtering out even unconfirmed spents!
|
||||
// add || !txItem.spentIsConfirmed
|
||||
|
||||
if (!txItem.spentTxId && info && info.hex) {
|
||||
var scriptPubKey = self._getScriptPubKey(info.hex, txItem.index);
|
||||
ret.push({
|
||||
address: self.addrStr,
|
||||
txid: txItem.txid,
|
||||
vout: txItem.index,
|
||||
ts: txItem.ts,
|
||||
scriptPubKey: scriptPubKey,
|
||||
amount: txItem.value_sat / BitcoreUtil.COIN,
|
||||
confirmations: txItem.isConfirmed ? info.confirmations : 0,
|
||||
});
|
||||
}
|
||||
return a_c(err);
|
||||
});
|
||||
}, function(err) {
|
||||
return next(err,ret);
|
||||
// we are filtering out even unconfirmed spents!
|
||||
// add || !txItem.spentIsConfirmed
|
||||
if (!txItem.spentTxId) {
|
||||
ret.push({
|
||||
address: self.addrStr,
|
||||
txid: txItem.txid,
|
||||
vout: txItem.index,
|
||||
ts: txItem.ts,
|
||||
scriptPubKey: scriptPubKey,
|
||||
amount: txItem.value_sat / BitcoreUtil.COIN,
|
||||
confirmations: txItem.isConfirmed ? info.confirmations : 0,
|
||||
});
|
||||
}
|
||||
return a_c(err);
|
||||
});
|
||||
}, function(err) {
|
||||
return next(err,ret);
|
||||
});
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
Address.prototype.update = function(next) {
|
||||
var self = this;
|
||||
if (!self.addrStr) return next();
|
||||
Address.prototype.update = function(next) {
|
||||
var self = this;
|
||||
if (!self.addrStr) return next();
|
||||
|
||||
var txs = [];
|
||||
var db = new TransactionDb();
|
||||
async.series([
|
||||
function (cb) {
|
||||
var seen={};
|
||||
db.fromAddr(self.addrStr, function(err,txOut){
|
||||
if (err) return cb(err);
|
||||
txOut.forEach(function(txItem){
|
||||
var add=0, addSpend=0;
|
||||
var v = txItem.value_sat;
|
||||
var txs = [];
|
||||
var db = TransactionDb;
|
||||
async.series([
|
||||
function (cb) {
|
||||
var seen={};
|
||||
db.fromAddr(self.addrStr, function(err,txOut){
|
||||
if (err) return cb(err);
|
||||
txOut.forEach(function(txItem){
|
||||
var add=0, addSpend=0;
|
||||
var v = txItem.value_sat;
|
||||
|
||||
if ( !seen[txItem.txid] ) {
|
||||
txs.push({txid: txItem.txid, ts: txItem.ts});
|
||||
seen[txItem.txid]=1;
|
||||
add=1;
|
||||
if ( !seen[txItem.txid] ) {
|
||||
txs.push({txid: txItem.txid, ts: txItem.ts});
|
||||
seen[txItem.txid]=1;
|
||||
add=1;
|
||||
}
|
||||
|
||||
if (txItem.spentTxId && !seen[txItem.spentTxId] ) {
|
||||
txs.push({txid: txItem.spentTxId, ts: txItem.spentTs});
|
||||
seen[txItem.spentTxId]=1;
|
||||
addSpend=1;
|
||||
}
|
||||
|
||||
if (txItem.isConfirmed) {
|
||||
self.txApperances += add;
|
||||
self.totalReceivedSat += v;
|
||||
if (! txItem.spentTxId ) {
|
||||
//unspent
|
||||
self.balanceSat += v;
|
||||
}
|
||||
|
||||
if (txItem.spentTxId && !seen[txItem.spentTxId] ) {
|
||||
txs.push({txid: txItem.spentTxId, ts: txItem.spentTs});
|
||||
seen[txItem.spentTxId]=1;
|
||||
addSpend=1;
|
||||
}
|
||||
|
||||
if (txItem.isConfirmed) {
|
||||
self.txApperances += add;
|
||||
self.totalReceivedSat += v;
|
||||
if (! txItem.spentTxId ) {
|
||||
//unspent
|
||||
self.balanceSat += v;
|
||||
}
|
||||
else if(!txItem.spentIsConfirmed) {
|
||||
// unspent
|
||||
self.balanceSat += v;
|
||||
self.unconfirmedBalanceSat -= v;
|
||||
self.unconfirmedTxApperances += addSpend;
|
||||
}
|
||||
else {
|
||||
// spent
|
||||
self.totalSentSat += v;
|
||||
self.txApperances += addSpend;
|
||||
}
|
||||
else if(!txItem.spentIsConfirmed) {
|
||||
// unspent
|
||||
self.balanceSat += v;
|
||||
self.unconfirmedBalanceSat -= v;
|
||||
self.unconfirmedTxApperances += addSpend;
|
||||
}
|
||||
else {
|
||||
self.unconfirmedBalanceSat += v;
|
||||
self.unconfirmedTxApperances += add;
|
||||
// spent
|
||||
self.totalSentSat += v;
|
||||
self.txApperances += addSpend;
|
||||
}
|
||||
});
|
||||
return cb();
|
||||
}
|
||||
else {
|
||||
self.unconfirmedBalanceSat += v;
|
||||
self.unconfirmedTxApperances += add;
|
||||
}
|
||||
});
|
||||
},
|
||||
], function (err) {
|
||||
return cb();
|
||||
});
|
||||
},
|
||||
], function (err) {
|
||||
|
||||
// sort input and outputs togheter
|
||||
txs.sort(
|
||||
function compare(a,b) {
|
||||
if (a.ts < b.ts) return 1;
|
||||
if (a.ts > b.ts) return -1;
|
||||
return 0;
|
||||
});
|
||||
// sort input and outputs togheter
|
||||
txs.sort(
|
||||
function compare(a,b) {
|
||||
if (a.ts < b.ts) return 1;
|
||||
if (a.ts > b.ts) return -1;
|
||||
return 0;
|
||||
});
|
||||
|
||||
self.transactions = txs.map(function(i) { return i.txid; } );
|
||||
return next(err);
|
||||
});
|
||||
};
|
||||
self.transactions = txs.map(function(i) { return i.txid; } );
|
||||
return next(err);
|
||||
});
|
||||
};
|
||||
|
||||
return Address;
|
||||
}
|
||||
module.defineClass(spec);
|
||||
module.exports = require('soop')(Address);
|
||||
|
||||
|
|
|
@ -1,112 +1,105 @@
|
|||
'use strict';
|
||||
//var imports = require('soop').imports();
|
||||
|
||||
require('classtool');
|
||||
|
||||
function spec() {
|
||||
var async = require('async');
|
||||
var RpcClient = require('bitcore/RpcClient').class();
|
||||
var BlockDb = require('../../lib/BlockDb').class();
|
||||
var config = require('../../config/config');
|
||||
var rpc = new RpcClient(config.bitcoind);
|
||||
|
||||
function Status() {
|
||||
this.bDb = new BlockDb();
|
||||
}
|
||||
|
||||
Status.prototype.getInfo = function(next) {
|
||||
var that = this;
|
||||
async.series([
|
||||
function (cb) {
|
||||
rpc.getInfo(function(err, info){
|
||||
if (err) return cb(err);
|
||||
|
||||
that.info = info.result;
|
||||
return cb();
|
||||
});
|
||||
},
|
||||
], function (err) {
|
||||
return next(err);
|
||||
});
|
||||
};
|
||||
|
||||
Status.prototype.getDifficulty = function(next) {
|
||||
var that = this;
|
||||
async.series([
|
||||
function (cb) {
|
||||
rpc.getDifficulty(function(err, df){
|
||||
if (err) return cb(err);
|
||||
|
||||
that.difficulty = df.result;
|
||||
return cb();
|
||||
});
|
||||
}
|
||||
], function (err) {
|
||||
return next(err);
|
||||
});
|
||||
};
|
||||
|
||||
Status.prototype.getTxOutSetInfo = function(next) {
|
||||
var that = this;
|
||||
async.series([
|
||||
function (cb) {
|
||||
rpc.getTxOutSetInfo(function(err, txout){
|
||||
if (err) return cb(err);
|
||||
|
||||
that.txoutsetinfo = txout.result;
|
||||
return cb();
|
||||
});
|
||||
}
|
||||
], function (err) {
|
||||
return next(err);
|
||||
});
|
||||
};
|
||||
|
||||
Status.prototype.getBestBlockHash = function(next) {
|
||||
var that = this;
|
||||
async.series([
|
||||
function (cb) {
|
||||
rpc.getBestBlockHash(function(err, bbh){
|
||||
if (err) return cb(err);
|
||||
|
||||
that.bestblockhash = bbh.result;
|
||||
return cb();
|
||||
});
|
||||
},
|
||||
|
||||
], function (err) {
|
||||
return next(err);
|
||||
});
|
||||
};
|
||||
|
||||
Status.prototype.getLastBlockHash = function(next) {
|
||||
var that = this;
|
||||
that.bDb.getTip(function(err,tip) {
|
||||
that.syncTipHash = tip;
|
||||
async.waterfall(
|
||||
[
|
||||
function(callback){
|
||||
rpc.getBlockCount(function(err, bc){
|
||||
if (err) return callback(err);
|
||||
callback(null, bc.result);
|
||||
});
|
||||
},
|
||||
function(bc, callback){
|
||||
rpc.getBlockHash(bc, function(err, bh){
|
||||
if (err) return callback(err);
|
||||
callback(null, bh.result);
|
||||
});
|
||||
}
|
||||
],
|
||||
function (err, result) {
|
||||
that.lastblockhash = result;
|
||||
return next();
|
||||
}
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
return Status;
|
||||
var async = require('async');
|
||||
var RpcClient = require('bitcore/RpcClient');
|
||||
var config = require('../../config/config');
|
||||
var rpc = new RpcClient(config.bitcoind);
|
||||
|
||||
function Status() {
|
||||
this.bDb = require('../../lib/BlockDb').default();
|
||||
}
|
||||
module.defineClass(spec);
|
||||
|
||||
Status.prototype.getInfo = function(next) {
|
||||
var that = this;
|
||||
async.series([
|
||||
function (cb) {
|
||||
rpc.getInfo(function(err, info){
|
||||
if (err) return cb(err);
|
||||
|
||||
that.info = info.result;
|
||||
return cb();
|
||||
});
|
||||
},
|
||||
], function (err) {
|
||||
return next(err);
|
||||
});
|
||||
};
|
||||
|
||||
Status.prototype.getDifficulty = function(next) {
|
||||
var that = this;
|
||||
async.series([
|
||||
function (cb) {
|
||||
rpc.getDifficulty(function(err, df){
|
||||
if (err) return cb(err);
|
||||
|
||||
that.difficulty = df.result;
|
||||
return cb();
|
||||
});
|
||||
}
|
||||
], function (err) {
|
||||
return next(err);
|
||||
});
|
||||
};
|
||||
|
||||
Status.prototype.getTxOutSetInfo = function(next) {
|
||||
var that = this;
|
||||
async.series([
|
||||
function (cb) {
|
||||
rpc.getTxOutSetInfo(function(err, txout){
|
||||
if (err) return cb(err);
|
||||
|
||||
that.txoutsetinfo = txout.result;
|
||||
return cb();
|
||||
});
|
||||
}
|
||||
], function (err) {
|
||||
return next(err);
|
||||
});
|
||||
};
|
||||
|
||||
Status.prototype.getBestBlockHash = function(next) {
|
||||
var that = this;
|
||||
async.series([
|
||||
function (cb) {
|
||||
rpc.getBestBlockHash(function(err, bbh){
|
||||
if (err) return cb(err);
|
||||
|
||||
that.bestblockhash = bbh.result;
|
||||
return cb();
|
||||
});
|
||||
},
|
||||
|
||||
], function (err) {
|
||||
return next(err);
|
||||
});
|
||||
};
|
||||
|
||||
Status.prototype.getLastBlockHash = function(next) {
|
||||
var that = this;
|
||||
that.bDb.getTip(function(err,tip) {
|
||||
that.syncTipHash = tip;
|
||||
async.waterfall(
|
||||
[
|
||||
function(callback){
|
||||
rpc.getBlockCount(function(err, bc){
|
||||
if (err) return callback(err);
|
||||
callback(null, bc.result);
|
||||
});
|
||||
},
|
||||
function(bc, callback){
|
||||
rpc.getBlockHash(bc, function(err, bh){
|
||||
if (err) return callback(err);
|
||||
callback(null, bh.result);
|
||||
});
|
||||
}
|
||||
],
|
||||
function (err, result) {
|
||||
that.lastblockhash = result;
|
||||
return next();
|
||||
}
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = require('soop')(Status);
|
||||
|
|
|
@ -5,7 +5,7 @@ var util = require('util');
|
|||
|
||||
process.env.NODE_ENV = process.env.NODE_ENV || 'development';
|
||||
|
||||
var RpcClient = require('../node_modules/bitcore/RpcClient').class();
|
||||
var RpcClient = require('../node_modules/bitcore/RpcClient');
|
||||
|
||||
var config = require('../config/config');
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
'use strict';
|
||||
|
||||
var util = require('util');
|
||||
var T = require('../lib/TransactionDb').class();
|
||||
var T = require('../lib/TransactionDb');
|
||||
|
||||
process.env.NODE_ENV = process.env.NODE_ENV || 'development';
|
||||
|
||||
|
|
|
@ -41,7 +41,7 @@ first 10%
|
|||
=> sacando los contenidos adentro de getblock from file de => 4.5s!!
|
||||
|
||||
=> con base58 cpp => 21s
|
||||
=> toda la testnet => 17m
|
||||
=> toda la testnet => 17m !!
|
||||
|
||||
10% de blk2
|
||||
=> 50s con base58cpp
|
||||
|
@ -54,3 +54,7 @@ first 10%
|
|||
=> 15s comentando desde b.getStandardizedObject()
|
||||
=> 39s comentando dps b.getStandardizedObject()
|
||||
|
||||
|
||||
Mon Mar 10 11:59:25 ART 2014
|
||||
10% de blk 0 (testnet)
|
||||
=> 37s
|
||||
|
|
|
@ -9,8 +9,8 @@ process.env.NODE_ENV = process.env.NODE_ENV || 'development';
|
|||
*/
|
||||
var express = require('express'),
|
||||
fs = require('fs'),
|
||||
PeerSync = require('./lib/PeerSync').class(),
|
||||
HistoricSync = require('./lib/HistoricSync').class();
|
||||
PeerSync = require('./lib/PeerSync'),
|
||||
HistoricSync = require('./lib/HistoricSync');
|
||||
|
||||
//Initializing system variables
|
||||
var config = require('./config/config');
|
||||
|
|
403
lib/BlockDb.js
403
lib/BlockDb.js
|
@ -1,234 +1,223 @@
|
|||
'use strict';
|
||||
var imports = require('soop').imports();
|
||||
var ThisParent = imports.parent || require('events').EventEmitter;
|
||||
var TIMESTAMP_PREFIX = 'bts-'; // b-ts-<ts> => <hash>
|
||||
var PREV_PREFIX = 'bpr-'; // b-prev-<hash> => <prev_hash>
|
||||
var NEXT_PREFIX = 'bne-'; // b-next-<hash> => <next_hash>
|
||||
var MAIN_PREFIX = 'bma-'; // b-main-<hash> => 1/0
|
||||
var TIP = 'bti-'; // last block on the chain
|
||||
var LAST_FILE_INDEX = 'file-'; // last processed file index
|
||||
|
||||
require('classtool');
|
||||
var MAX_OPEN_FILES = 500;
|
||||
|
||||
|
||||
function spec(b) {
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
var levelup = require('levelup'),
|
||||
config = require('../config/config');
|
||||
var db = imports.db || levelup(config.leveldb + '/blocks',{maxOpenFiles: MAX_OPEN_FILES} );
|
||||
var Rpc = imports.rpc || require('./Rpc');
|
||||
var PoolMatch = imports.poolMatch || require('soop').load('./PoolMatch',config);
|
||||
|
||||
var superclass = b.superclass || require('events').EventEmitter;
|
||||
var TIMESTAMP_PREFIX = 'bts-'; // b-ts-<ts> => <hash>
|
||||
var PREV_PREFIX = 'bpr-'; // b-prev-<hash> => <prev_hash>
|
||||
var NEXT_PREFIX = 'bne-'; // b-next-<hash> => <next_hash>
|
||||
var MAIN_PREFIX = 'bma-'; // b-main-<hash> => 1/0
|
||||
var TIP = 'bti-'; // last block on the chain
|
||||
var LAST_FILE_INDEX = 'file-'; // last processed file index
|
||||
var tDb = require('./TransactionDb.js').default();
|
||||
|
||||
var MAX_OPEN_FILES = 500;
|
||||
var BlockDb = function() {
|
||||
BlockDb.super(this, arguments);
|
||||
this.poolMatch = new PoolMatch();
|
||||
};
|
||||
BlockDb.parent = ThisParent;
|
||||
|
||||
BlockDb.prototype.close = function(cb) {
|
||||
db.close(cb);
|
||||
};
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
var levelup = require('levelup'),
|
||||
config = require('../config/config');
|
||||
var db = b.db || levelup(config.leveldb + '/blocks',{maxOpenFiles: MAX_OPEN_FILES} );
|
||||
var Rpc = b.rpc || require('./Rpc').class();
|
||||
var PoolMatch = b.poolMatch || require('./PoolMatch').class(config);
|
||||
|
||||
var TransactionDb = require('./TransactionDb.js').class();
|
||||
|
||||
var BlockDb = function() {
|
||||
BlockDb.super(this, arguments);
|
||||
this.poolMatch = new PoolMatch();
|
||||
};
|
||||
|
||||
BlockDb.superclass = superclass;
|
||||
|
||||
BlockDb.prototype.close = function(cb) {
|
||||
db.close(cb);
|
||||
};
|
||||
|
||||
BlockDb.prototype.drop = function(cb) {
|
||||
var path = config.leveldb + '/blocks';
|
||||
db.close(function() {
|
||||
require('leveldown').destroy(path, function () {
|
||||
db = levelup(path,{maxOpenFiles: MAX_OPEN_FILES} );
|
||||
return cb();
|
||||
});
|
||||
BlockDb.prototype.drop = function(cb) {
|
||||
var path = config.leveldb + '/blocks';
|
||||
db.close(function() {
|
||||
require('leveldown').destroy(path, function () {
|
||||
db = levelup(path,{maxOpenFiles: MAX_OPEN_FILES} );
|
||||
return cb();
|
||||
});
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
// adds a block. Does not update Next pointer in
|
||||
// the block prev to the new block, nor TIP pointer
|
||||
//
|
||||
BlockDb.prototype.add = function(b, cb) {
|
||||
var self = this;
|
||||
var time_key = TIMESTAMP_PREFIX +
|
||||
( b.time || Math.round(new Date().getTime() / 1000) );
|
||||
// adds a block. Does not update Next pointer in
|
||||
// the block prev to the new block, nor TIP pointer
|
||||
//
|
||||
BlockDb.prototype.add = function(b, cb) {
|
||||
var self = this;
|
||||
var time_key = TIMESTAMP_PREFIX +
|
||||
( b.time || Math.round(new Date().getTime() / 1000) );
|
||||
|
||||
return db.batch()
|
||||
.put(time_key, b.hash)
|
||||
.put(MAIN_PREFIX + b.hash, 1)
|
||||
.put(PREV_PREFIX + b.hash, b.previousblockhash)
|
||||
.write(function(err){
|
||||
if (!err) {
|
||||
self.emit('new_block', {blockid: b.hash});
|
||||
}
|
||||
cb(err);
|
||||
});
|
||||
};
|
||||
|
||||
BlockDb.prototype.getTip = function(cb) {
|
||||
db.get(TIP, function(err, val) {
|
||||
return cb(err,val);
|
||||
});
|
||||
};
|
||||
|
||||
BlockDb.prototype.setTip = function(hash, cb) {
|
||||
db.put(TIP, hash, function(err) {
|
||||
return cb(err);
|
||||
});
|
||||
};
|
||||
|
||||
//mainly for testing
|
||||
BlockDb.prototype.setPrev = function(hash, prevHash, cb) {
|
||||
db.put(PREV_PREFIX + hash, prevHash, function(err) {
|
||||
return cb(err);
|
||||
});
|
||||
};
|
||||
|
||||
BlockDb.prototype.getPrev = function(hash, cb) {
|
||||
db.get(PREV_PREFIX + hash, function(err,val) {
|
||||
if (err && err.notFound) { err = null; val = null;}
|
||||
return cb(err,val);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
BlockDb.prototype.setLastFileIndex = function(idx, cb) {
|
||||
var self = this;
|
||||
if (this.lastFileIndexSaved === idx) return cb();
|
||||
|
||||
db.put(LAST_FILE_INDEX, idx, function(err) {
|
||||
self.lastFileIndexSaved = idx;
|
||||
return cb(err);
|
||||
});
|
||||
};
|
||||
|
||||
BlockDb.prototype.getLastFileIndex = function(cb) {
|
||||
db.get(LAST_FILE_INDEX, function(err,val) {
|
||||
if (err && err.notFound) { err = null; val = null;}
|
||||
return cb(err,val);
|
||||
});
|
||||
};
|
||||
|
||||
BlockDb.prototype.getNext = function(hash, cb) {
|
||||
db.get(NEXT_PREFIX + hash, function(err,val) {
|
||||
if (err && err.notFound) { err = null; val = null;}
|
||||
return cb(err,val);
|
||||
});
|
||||
};
|
||||
|
||||
BlockDb.prototype.isMain = function(hash, cb) {
|
||||
db.get(MAIN_PREFIX + hash, function(err, val) {
|
||||
if (err && err.notFound) { err = null; val = 0;}
|
||||
return cb(err,parseInt(val));
|
||||
});
|
||||
};
|
||||
|
||||
BlockDb.prototype.setMain = function(hash, isMain, cb) {
|
||||
if (!isMain) console.log('\tNew orphan: %s',hash);
|
||||
db.put(MAIN_PREFIX + hash, isMain?1:0, function(err) {
|
||||
return cb(err);
|
||||
});
|
||||
};
|
||||
BlockDb.prototype.setNext = function(hash, nextHash, cb) {
|
||||
db.put(NEXT_PREFIX + hash, nextHash, function(err) {
|
||||
return cb(err);
|
||||
});
|
||||
};
|
||||
|
||||
BlockDb.prototype.countConnected = function(cb) {
|
||||
var c = 0;
|
||||
console.log('Counting connected blocks. This could take some minutes');
|
||||
db.createReadStream({start: MAIN_PREFIX, end: MAIN_PREFIX + '~' })
|
||||
.on('data', function (data) {
|
||||
if (data.value !== 0) c++;
|
||||
})
|
||||
.on('error', function (err) {
|
||||
return cb(err);
|
||||
})
|
||||
.on('end', function () {
|
||||
return cb(null, c);
|
||||
});
|
||||
};
|
||||
|
||||
// .has() return true orphans also
|
||||
BlockDb.prototype.has = function(hash, cb) {
|
||||
var k = PREV_PREFIX + hash;
|
||||
db.get(k, function (err) {
|
||||
var ret = true;
|
||||
if (err && err.notFound) {
|
||||
err = null;
|
||||
ret = false;
|
||||
return db.batch()
|
||||
.put(time_key, b.hash)
|
||||
.put(MAIN_PREFIX + b.hash, 1)
|
||||
.put(PREV_PREFIX + b.hash, b.previousblockhash)
|
||||
.write(function(err){
|
||||
if (!err) {
|
||||
self.emit('new_block', {blockid: b.hash});
|
||||
}
|
||||
return cb(err, ret);
|
||||
cb(err);
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
BlockDb.prototype.getPoolInfo = function(tx, cb) {
|
||||
var tr = new TransactionDb();
|
||||
var self = this;
|
||||
|
||||
tr._getInfo(tx, function(e, a) {
|
||||
if (e) return cb(false);
|
||||
BlockDb.prototype.getTip = function(cb) {
|
||||
db.get(TIP, function(err, val) {
|
||||
return cb(err,val);
|
||||
});
|
||||
};
|
||||
|
||||
if (a.isCoinBase) {
|
||||
var coinbaseHexBuffer = new Buffer(a.vin[0].coinbase, 'hex');
|
||||
var aa = self.poolMatch.match(coinbaseHexBuffer);
|
||||
|
||||
return cb(aa);
|
||||
}
|
||||
BlockDb.prototype.setTip = function(hash, cb) {
|
||||
db.put(TIP, hash, function(err) {
|
||||
return cb(err);
|
||||
});
|
||||
};
|
||||
|
||||
//mainly for testing
|
||||
BlockDb.prototype.setPrev = function(hash, prevHash, cb) {
|
||||
db.put(PREV_PREFIX + hash, prevHash, function(err) {
|
||||
return cb(err);
|
||||
});
|
||||
};
|
||||
|
||||
BlockDb.prototype.getPrev = function(hash, cb) {
|
||||
db.get(PREV_PREFIX + hash, function(err,val) {
|
||||
if (err && err.notFound) { err = null; val = null;}
|
||||
return cb(err,val);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
BlockDb.prototype.setLastFileIndex = function(idx, cb) {
|
||||
var self = this;
|
||||
if (this.lastFileIndexSaved === idx) return cb();
|
||||
|
||||
db.put(LAST_FILE_INDEX, idx, function(err) {
|
||||
self.lastFileIndexSaved = idx;
|
||||
return cb(err);
|
||||
});
|
||||
};
|
||||
|
||||
BlockDb.prototype.getLastFileIndex = function(cb) {
|
||||
db.get(LAST_FILE_INDEX, function(err,val) {
|
||||
if (err && err.notFound) { err = null; val = null;}
|
||||
return cb(err,val);
|
||||
});
|
||||
};
|
||||
|
||||
BlockDb.prototype.getNext = function(hash, cb) {
|
||||
db.get(NEXT_PREFIX + hash, function(err,val) {
|
||||
if (err && err.notFound) { err = null; val = null;}
|
||||
return cb(err,val);
|
||||
});
|
||||
};
|
||||
|
||||
BlockDb.prototype.isMain = function(hash, cb) {
|
||||
db.get(MAIN_PREFIX + hash, function(err, val) {
|
||||
if (err && err.notFound) { err = null; val = 0;}
|
||||
return cb(err,parseInt(val));
|
||||
});
|
||||
};
|
||||
|
||||
BlockDb.prototype.setMain = function(hash, isMain, cb) {
|
||||
if (!isMain) console.log('\tNew orphan: %s',hash);
|
||||
db.put(MAIN_PREFIX + hash, isMain?1:0, function(err) {
|
||||
return cb(err);
|
||||
});
|
||||
};
|
||||
BlockDb.prototype.setNext = function(hash, nextHash, cb) {
|
||||
db.put(NEXT_PREFIX + hash, nextHash, function(err) {
|
||||
return cb(err);
|
||||
});
|
||||
};
|
||||
|
||||
BlockDb.prototype.countConnected = function(cb) {
|
||||
var c = 0;
|
||||
console.log('Counting connected blocks. This could take some minutes');
|
||||
db.createReadStream({start: MAIN_PREFIX, end: MAIN_PREFIX + '~' })
|
||||
.on('data', function (data) {
|
||||
if (data.value !== 0) c++;
|
||||
})
|
||||
.on('error', function (err) {
|
||||
return cb(err);
|
||||
})
|
||||
.on('end', function () {
|
||||
return cb(null, c);
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
BlockDb.prototype.fromHashWithInfo = function(hash, cb) {
|
||||
var self = this;
|
||||
// .has() return true orphans also
|
||||
BlockDb.prototype.has = function(hash, cb) {
|
||||
var k = PREV_PREFIX + hash;
|
||||
db.get(k, function (err) {
|
||||
var ret = true;
|
||||
if (err && err.notFound) {
|
||||
err = null;
|
||||
ret = false;
|
||||
}
|
||||
return cb(err, ret);
|
||||
});
|
||||
};
|
||||
|
||||
Rpc.getBlock(hash, function(err, info) {
|
||||
if (err || !info) return cb(err);
|
||||
BlockDb.prototype.getPoolInfo = function(tx, cb) {
|
||||
var self = this;
|
||||
|
||||
self.isMain(hash, function(err, val) {
|
||||
if (err) return cb(err);
|
||||
tDb._getInfo(tx, function(e, a) {
|
||||
if (e) return cb(false);
|
||||
|
||||
info.isMainChain = val ? true : false;
|
||||
if (a.isCoinBase) {
|
||||
var coinbaseHexBuffer = new Buffer(a.vin[0].coinbase, 'hex');
|
||||
var aa = self.poolMatch.match(coinbaseHexBuffer);
|
||||
|
||||
return cb(aa);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
return cb(null, {
|
||||
hash: hash,
|
||||
info: info,
|
||||
});
|
||||
BlockDb.prototype.fromHashWithInfo = function(hash, cb) {
|
||||
var self = this;
|
||||
|
||||
Rpc.getBlock(hash, function(err, info) {
|
||||
if (err || !info) return cb(err);
|
||||
|
||||
self.isMain(hash, function(err, val) {
|
||||
if (err) return cb(err);
|
||||
|
||||
info.isMainChain = val ? true : false;
|
||||
|
||||
return cb(null, {
|
||||
hash: hash,
|
||||
info: info,
|
||||
});
|
||||
});
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
BlockDb.prototype.getBlocksByDate = function(start_ts, end_ts, cb) {
|
||||
var list = [];
|
||||
db.createReadStream({
|
||||
start: TIMESTAMP_PREFIX + start_ts,
|
||||
end: TIMESTAMP_PREFIX + end_ts,
|
||||
fillCache: true
|
||||
})
|
||||
.on('data', function (data) {
|
||||
var k = data.key.split('-');
|
||||
list.push({
|
||||
ts: k[1],
|
||||
hash: data.value,
|
||||
});
|
||||
})
|
||||
.on('error', function (err) {
|
||||
return cb(err);
|
||||
})
|
||||
.on('end', function () {
|
||||
return cb(null, list.reverse());
|
||||
BlockDb.prototype.getBlocksByDate = function(start_ts, end_ts, cb) {
|
||||
var list = [];
|
||||
db.createReadStream({
|
||||
start: TIMESTAMP_PREFIX + start_ts,
|
||||
end: TIMESTAMP_PREFIX + end_ts,
|
||||
fillCache: true
|
||||
})
|
||||
.on('data', function (data) {
|
||||
var k = data.key.split('-');
|
||||
list.push({
|
||||
ts: k[1],
|
||||
hash: data.value,
|
||||
});
|
||||
};
|
||||
|
||||
BlockDb.prototype.blockIndex = function(height, cb) {
|
||||
return Rpc.blockIndex(height,cb);
|
||||
};
|
||||
|
||||
return BlockDb;
|
||||
}
|
||||
module.defineClass(spec);
|
||||
})
|
||||
.on('error', function (err) {
|
||||
return cb(err);
|
||||
})
|
||||
.on('end', function () {
|
||||
return cb(null, list.reverse());
|
||||
});
|
||||
};
|
||||
|
||||
BlockDb.prototype.blockIndex = function(height, cb) {
|
||||
return Rpc.blockIndex(height,cb);
|
||||
};
|
||||
|
||||
module.exports = require('soop')(BlockDb);
|
||||
|
|
|
@ -1,162 +1,155 @@
|
|||
'use strict';
|
||||
var Block = require('bitcore/Block'),
|
||||
networks = require('bitcore/networks'),
|
||||
Parser = require('bitcore/util/BinaryParser'),
|
||||
fs = require('fs'),
|
||||
Buffer = require('buffer').Buffer,
|
||||
glob = require('glob'),
|
||||
async = require('async');
|
||||
|
||||
require('classtool');
|
||||
function BlockExtractor(dataDir, network) {
|
||||
|
||||
function spec() {
|
||||
var self = this;
|
||||
var path = dataDir + '/blocks/blk*.dat';
|
||||
|
||||
var Block = require('bitcore/Block').class(),
|
||||
networks = require('bitcore/networks'),
|
||||
Parser = require('bitcore/util/BinaryParser').class(),
|
||||
fs = require('fs'),
|
||||
Buffer = require('buffer').Buffer,
|
||||
glob = require('glob'),
|
||||
async = require('async');
|
||||
self.dataDir = dataDir;
|
||||
self.files = glob.sync(path);
|
||||
self.nfiles = self.files.length;
|
||||
|
||||
function BlockExtractor(dataDir, network) {
|
||||
if (self.nfiles === 0)
|
||||
throw new Error('Could not find block files at: ' + path);
|
||||
|
||||
var self = this;
|
||||
var path = dataDir + '/blocks/blk*.dat';
|
||||
self.currentFileIndex = 0;
|
||||
self.isCurrentRead = false;
|
||||
self.currentBuffer = null;
|
||||
self.currentParser = null;
|
||||
self.network = network === 'testnet' ? networks.testnet: networks.livenet;
|
||||
self.magic = self.network.magic.toString('hex');
|
||||
}
|
||||
|
||||
self.dataDir = dataDir;
|
||||
self.files = glob.sync(path);
|
||||
self.nfiles = self.files.length;
|
||||
BlockExtractor.prototype.currentFile = function() {
|
||||
var self = this;
|
||||
|
||||
if (self.nfiles === 0)
|
||||
throw new Error('Could not find block files at: ' + path);
|
||||
return self.files[self.currentFileIndex];
|
||||
};
|
||||
|
||||
self.currentFileIndex = 0;
|
||||
self.isCurrentRead = false;
|
||||
self.currentBuffer = null;
|
||||
self.currentParser = null;
|
||||
self.network = network === 'testnet' ? networks.testnet: networks.livenet;
|
||||
self.magic = self.network.magic.toString('hex');
|
||||
|
||||
BlockExtractor.prototype.nextFile = function() {
|
||||
var self = this;
|
||||
|
||||
if (self.currentFileIndex < 0) return false;
|
||||
|
||||
var ret = true;
|
||||
|
||||
self.isCurrentRead = false;
|
||||
self.currentBuffer = null;
|
||||
self.currentParser = null;
|
||||
|
||||
if (self.currentFileIndex < self.nfiles - 1) {
|
||||
self.currentFileIndex++;
|
||||
}
|
||||
else {
|
||||
self.currentFileIndex=-1;
|
||||
ret = false;
|
||||
}
|
||||
return ret;
|
||||
};
|
||||
|
||||
BlockExtractor.prototype.currentFile = function() {
|
||||
var self = this;
|
||||
BlockExtractor.prototype.readCurrentFileSync = function() {
|
||||
var self = this;
|
||||
|
||||
return self.files[self.currentFileIndex];
|
||||
};
|
||||
if (self.currentFileIndex < 0 || self.isCurrentRead) return;
|
||||
|
||||
|
||||
BlockExtractor.prototype.nextFile = function() {
|
||||
var self = this;
|
||||
self.isCurrentRead = true;
|
||||
|
||||
if (self.currentFileIndex < 0) return false;
|
||||
|
||||
var ret = true;
|
||||
|
||||
self.isCurrentRead = false;
|
||||
self.currentBuffer = null;
|
||||
self.currentParser = null;
|
||||
|
||||
if (self.currentFileIndex < self.nfiles - 1) {
|
||||
self.currentFileIndex++;
|
||||
}
|
||||
else {
|
||||
self.currentFileIndex=-1;
|
||||
ret = false;
|
||||
}
|
||||
return ret;
|
||||
};
|
||||
|
||||
BlockExtractor.prototype.readCurrentFileSync = function() {
|
||||
var self = this;
|
||||
|
||||
if (self.currentFileIndex < 0 || self.isCurrentRead) return;
|
||||
var fname = self.currentFile();
|
||||
if (!fname) return;
|
||||
|
||||
|
||||
self.isCurrentRead = true;
|
||||
var stats = fs.statSync(fname);
|
||||
|
||||
var fname = self.currentFile();
|
||||
if (!fname) return;
|
||||
var size = stats.size;
|
||||
|
||||
console.log('Reading Blockfile %s [%d MB]',
|
||||
fname, parseInt(size/1024/1024));
|
||||
|
||||
var stats = fs.statSync(fname);
|
||||
var fd = fs.openSync(fname, 'r');
|
||||
|
||||
var size = stats.size;
|
||||
var buffer = new Buffer(size);
|
||||
|
||||
console.log('Reading Blockfile %s [%d MB]',
|
||||
fname, parseInt(size/1024/1024));
|
||||
fs.readSync(fd, buffer, 0, size, 0);
|
||||
|
||||
var fd = fs.openSync(fname, 'r');
|
||||
|
||||
var buffer = new Buffer(size);
|
||||
|
||||
fs.readSync(fd, buffer, 0, size, 0);
|
||||
|
||||
self.currentBuffer = buffer;
|
||||
self.currentParser = new Parser(buffer);
|
||||
};
|
||||
self.currentBuffer = buffer;
|
||||
self.currentParser = new Parser(buffer);
|
||||
};
|
||||
|
||||
|
||||
|
||||
BlockExtractor.prototype.getNextBlock = function(cb) {
|
||||
var self = this;
|
||||
BlockExtractor.prototype.getNextBlock = function(cb) {
|
||||
var self = this;
|
||||
|
||||
var b;
|
||||
var magic;
|
||||
async.series([
|
||||
function (a_cb) {
|
||||
|
||||
async.whilst(
|
||||
function() {
|
||||
return (!magic);
|
||||
},
|
||||
function(w_cb) {
|
||||
var b;
|
||||
var magic;
|
||||
async.series([
|
||||
function (a_cb) {
|
||||
|
||||
self.readCurrentFileSync();
|
||||
async.whilst(
|
||||
function() {
|
||||
return (!magic);
|
||||
},
|
||||
function(w_cb) {
|
||||
|
||||
if (self.currentFileIndex < 0) return cb();
|
||||
self.readCurrentFileSync();
|
||||
|
||||
if (self.currentFileIndex < 0) return cb();
|
||||
|
||||
|
||||
magic = self.currentParser ? self.currentParser.buffer(4).toString('hex')
|
||||
: null ;
|
||||
magic = self.currentParser ? self.currentParser.buffer(4).toString('hex')
|
||||
: null ;
|
||||
|
||||
if (!self.currentParser || self.currentParser.eof() || magic === '00000000') {
|
||||
magic = null;
|
||||
if (self.nextFile()) {
|
||||
console.log('Moving forward to file:' + self.currentFile() );
|
||||
return w_cb();
|
||||
}
|
||||
else {
|
||||
console.log('Finished all files');
|
||||
magic = null;
|
||||
return w_cb();
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!self.currentParser || self.currentParser.eof() || magic === '00000000') {
|
||||
magic = null;
|
||||
if (self.nextFile()) {
|
||||
console.log('Moving forward to file:' + self.currentFile() );
|
||||
return w_cb();
|
||||
}
|
||||
}, a_cb);
|
||||
},
|
||||
function (a_cb) {
|
||||
if (!magic) return a_cb();
|
||||
else {
|
||||
console.log('Finished all files');
|
||||
magic = null;
|
||||
return w_cb();
|
||||
}
|
||||
}
|
||||
else {
|
||||
return w_cb();
|
||||
}
|
||||
}, a_cb);
|
||||
},
|
||||
function (a_cb) {
|
||||
if (!magic) return a_cb();
|
||||
|
||||
if (magic !== self.magic) {
|
||||
var e = new Error('CRITICAL ERROR: Magic number mismatch: ' +
|
||||
magic + '!=' + self.magic);
|
||||
return a_cb(e);
|
||||
}
|
||||
if (magic !== self.magic) {
|
||||
var e = new Error('CRITICAL ERROR: Magic number mismatch: ' +
|
||||
magic + '!=' + self.magic);
|
||||
return a_cb(e);
|
||||
}
|
||||
|
||||
// spacer?
|
||||
self.currentParser.word32le();
|
||||
return a_cb();
|
||||
},
|
||||
function (a_cb) {
|
||||
if (!magic) return a_cb();
|
||||
// spacer?
|
||||
self.currentParser.word32le();
|
||||
return a_cb();
|
||||
},
|
||||
function (a_cb) {
|
||||
if (!magic) return a_cb();
|
||||
|
||||
b = new Block();
|
||||
b.parse(self.currentParser);
|
||||
b.getHash();
|
||||
return a_cb();
|
||||
},
|
||||
], function(err) {
|
||||
return cb(err,b);
|
||||
});
|
||||
};
|
||||
b = new Block();
|
||||
b.parse(self.currentParser);
|
||||
b.getHash();
|
||||
return a_cb();
|
||||
},
|
||||
], function(err) {
|
||||
return cb(err,b);
|
||||
});
|
||||
};
|
||||
|
||||
return BlockExtractor;
|
||||
}
|
||||
module.defineClass(spec);
|
||||
module.exports = require('soop')(BlockExtractor);
|
||||
|
||||
|
|
|
@ -1,446 +1,423 @@
|
|||
'use strict';
|
||||
|
||||
require('classtool');
|
||||
var imports = require('soop').imports();
|
||||
var util = require('util');
|
||||
var assert = require('assert');
|
||||
var async = require('async');
|
||||
var RpcClient = require('bitcore/RpcClient');
|
||||
var Script = require('bitcore/Script');
|
||||
var networks = require('bitcore/networks');
|
||||
var config = imports.config || require('../config/config');
|
||||
var Sync = require('./Sync');
|
||||
var sockets = require('../app/controllers/socket.js');
|
||||
var BlockExtractor = require('./BlockExtractor.js');
|
||||
var buffertools = require('buffertools');
|
||||
|
||||
// var bitcoreUtil = require('bitcore/util/util');
|
||||
// var Deserialize = require('bitcore/Deserialize');
|
||||
|
||||
|
||||
var BAD_GEN_ERROR = 'Bad genesis block. Network mismatch between Insight and bitcoind? Insight is configured for:';
|
||||
|
||||
function spec() {
|
||||
var util = require('util');
|
||||
var assert = require('assert');
|
||||
var RpcClient = require('bitcore/RpcClient').class();
|
||||
var Script = require('bitcore/Script').class();
|
||||
var networks = require('bitcore/networks');
|
||||
var async = require('async');
|
||||
var config = require('../config/config');
|
||||
var Sync = require('./Sync').class();
|
||||
var sockets = require('../app/controllers/socket.js');
|
||||
var BlockExtractor = require('./BlockExtractor.js').class();
|
||||
var buffertools = require('buffertools');
|
||||
|
||||
// var bitcoreUtil = require('bitcore/util/util');
|
||||
// var Deserialize = require('bitcore/Deserialize');
|
||||
var BAD_GEN_ERROR_DB = 'Bad genesis block. Network mismatch between Insight and levelDB? Insight is configured for:';
|
||||
function HistoricSync(opts) {
|
||||
opts = opts || {};
|
||||
|
||||
this.network = config.network === 'testnet' ? networks.testnet: networks.livenet;
|
||||
|
||||
var BAD_GEN_ERROR = 'Bad genesis block. Network mismatch between Insight and bitcoind? Insight is configured for:';
|
||||
var genesisHashReversed = new Buffer(32);
|
||||
this.network.genesisBlock.hash.copy(genesisHashReversed);
|
||||
buffertools.reverse(genesisHashReversed);
|
||||
this.genesis = genesisHashReversed.toString('hex');
|
||||
|
||||
var BAD_GEN_ERROR_DB = 'Bad genesis block. Network mismatch between Insight and levelDB? Insight is configured for:';
|
||||
function HistoricSync(opts) {
|
||||
opts = opts || {};
|
||||
this.rpc = new RpcClient(config.bitcoind);
|
||||
this.shouldBroadcast = opts.shouldBroadcastSync;
|
||||
this.sync = new Sync(opts);
|
||||
}
|
||||
|
||||
this.network = config.network === 'testnet' ? networks.testnet: networks.livenet;
|
||||
function p() {
|
||||
var args = [];
|
||||
Array.prototype.push.apply(args, arguments);
|
||||
|
||||
var genesisHashReversed = new Buffer(32);
|
||||
this.network.genesisBlock.hash.copy(genesisHashReversed);
|
||||
buffertools.reverse(genesisHashReversed);
|
||||
this.genesis = genesisHashReversed.toString('hex');
|
||||
args.unshift('[historic_sync]');
|
||||
/*jshint validthis:true */
|
||||
console.log.apply(this, args);
|
||||
}
|
||||
|
||||
this.rpc = new RpcClient(config.bitcoind);
|
||||
this.shouldBroadcast = opts.shouldBroadcastSync;
|
||||
this.sync = new Sync(opts);
|
||||
HistoricSync.prototype.showProgress = function() {
|
||||
var self = this;
|
||||
|
||||
if ( self.status ==='syncing' &&
|
||||
( self.syncedBlocks ) % self.step !== 1) return;
|
||||
|
||||
if (self.error) {
|
||||
p('ERROR: ' + self.error);
|
||||
}
|
||||
else {
|
||||
self.updatePercentage();
|
||||
p(util.format('status: [%d%%]', self.syncPercentage));
|
||||
}
|
||||
if (self.shouldBroadcast) {
|
||||
sockets.broadcastSyncInfo(self.info());
|
||||
}
|
||||
|
||||
function p() {
|
||||
var args = [];
|
||||
Array.prototype.push.apply(args, arguments);
|
||||
// if (self.syncPercentage > 10) {
|
||||
// process.exit(-1);
|
||||
// }
|
||||
};
|
||||
|
||||
args.unshift('[historic_sync]');
|
||||
/*jshint validthis:true */
|
||||
console.log.apply(this, args);
|
||||
}
|
||||
|
||||
HistoricSync.prototype.showProgress = function() {
|
||||
var self = this;
|
||||
HistoricSync.prototype.setError = function(err) {
|
||||
var self = this;
|
||||
self.error = err.message?err.message:err.toString();
|
||||
self.status='error';
|
||||
self.showProgress();
|
||||
return err;
|
||||
};
|
||||
|
||||
if ( self.status ==='syncing' &&
|
||||
( self.syncedBlocks ) % self.step !== 1) return;
|
||||
|
||||
if (self.error) {
|
||||
p('ERROR: ' + self.error);
|
||||
|
||||
HistoricSync.prototype.close = function() {
|
||||
this.sync.close();
|
||||
};
|
||||
|
||||
|
||||
HistoricSync.prototype.info = function() {
|
||||
this.updatePercentage();
|
||||
return {
|
||||
status: this.status,
|
||||
blockChainHeight: this.blockChainHeight,
|
||||
syncPercentage: this.syncPercentage,
|
||||
syncedBlocks: this.syncedBlocks,
|
||||
syncTipHash: this.sync.tip,
|
||||
error: this.error,
|
||||
type: this.type,
|
||||
startTs: this.startTs,
|
||||
endTs: this.endTs,
|
||||
};
|
||||
};
|
||||
|
||||
HistoricSync.prototype.updatePercentage = function() {
|
||||
var r = this.syncedBlocks / this.blockChainHeight;
|
||||
this.syncPercentage = parseFloat(100 * r).toFixed(3);
|
||||
if (this.syncPercentage > 100) this.syncPercentage = 100;
|
||||
};
|
||||
|
||||
HistoricSync.prototype.getBlockFromRPC = function(cb) {
|
||||
var self = this;
|
||||
|
||||
if (!self.currentRpcHash) return cb();
|
||||
|
||||
var blockInfo;
|
||||
self.rpc.getBlock(self.currentRpcHash, function(err, ret) {
|
||||
if (err) return cb(err);
|
||||
if (ret) {
|
||||
blockInfo = ret.result;
|
||||
// this is to match block retreived from file
|
||||
if (blockInfo.hash === self.genesis)
|
||||
blockInfo.previousblockhash =
|
||||
self.network.genesisBlock.prev_hash.toString('hex');
|
||||
|
||||
self.currentRpcHash = blockInfo.nextblockhash;
|
||||
}
|
||||
else {
|
||||
self.updatePercentage();
|
||||
p(util.format('status: [%d%%]', self.syncPercentage));
|
||||
}
|
||||
if (self.shouldBroadcast) {
|
||||
sockets.broadcastSyncInfo(self.info());
|
||||
blockInfo = null;
|
||||
}
|
||||
return cb(null, blockInfo);
|
||||
});
|
||||
};
|
||||
|
||||
// if (self.syncPercentage > 10) {
|
||||
// process.exit(-1);
|
||||
// }
|
||||
};
|
||||
HistoricSync.prototype.getBlockFromFile = function(cb) {
|
||||
var self = this;
|
||||
|
||||
var blockInfo;
|
||||
|
||||
//get Info
|
||||
self.blockExtractor.getNextBlock(function(err, b) {
|
||||
if (err || ! b) return cb(err);
|
||||
blockInfo = b.getStandardizedObject(b.txs, self.network);
|
||||
blockInfo.previousblockhash = blockInfo.prev_block;
|
||||
|
||||
var ti=0;
|
||||
// Get TX Address
|
||||
b.txs.forEach(function(t) {
|
||||
|
||||
|
||||
HistoricSync.prototype.setError = function(err) {
|
||||
var self = this;
|
||||
self.error = err.message?err.message:err.toString();
|
||||
self.status='error';
|
||||
self.showProgress();
|
||||
return err;
|
||||
};
|
||||
var objTx = blockInfo.tx[ti++];
|
||||
|
||||
//add time from block
|
||||
objTx.time = blockInfo.time;
|
||||
|
||||
var to=0;
|
||||
t.outs.forEach( function(o) {
|
||||
|
||||
|
||||
var s = new Script(o.s);
|
||||
var addrs = self.sync.txDb.getAddrStr(s);
|
||||
|
||||
HistoricSync.prototype.close = function() {
|
||||
this.sync.close();
|
||||
};
|
||||
|
||||
|
||||
HistoricSync.prototype.info = function() {
|
||||
this.updatePercentage();
|
||||
return {
|
||||
status: this.status,
|
||||
blockChainHeight: this.blockChainHeight,
|
||||
syncPercentage: this.syncPercentage,
|
||||
syncedBlocks: this.syncedBlocks,
|
||||
syncTipHash: this.sync.tip,
|
||||
error: this.error,
|
||||
type: this.type,
|
||||
startTs: this.startTs,
|
||||
endTs: this.endTs,
|
||||
};
|
||||
};
|
||||
|
||||
HistoricSync.prototype.updatePercentage = function() {
|
||||
var r = this.syncedBlocks / this.blockChainHeight;
|
||||
this.syncPercentage = parseFloat(100 * r).toFixed(3);
|
||||
if (this.syncPercentage > 100) this.syncPercentage = 100;
|
||||
};
|
||||
|
||||
HistoricSync.prototype.getBlockFromRPC = function(cb) {
|
||||
var self = this;
|
||||
|
||||
if (!self.currentRpcHash) return cb();
|
||||
|
||||
var blockInfo;
|
||||
self.rpc.getBlock(self.currentRpcHash, function(err, ret) {
|
||||
if (err) return cb(err);
|
||||
if (ret) {
|
||||
blockInfo = ret.result;
|
||||
// this is to match block retreived from file
|
||||
if (blockInfo.hash === self.genesis)
|
||||
blockInfo.previousblockhash =
|
||||
self.network.genesisBlock.prev_hash.toString('hex');
|
||||
|
||||
self.currentRpcHash = blockInfo.nextblockhash;
|
||||
}
|
||||
else {
|
||||
blockInfo = null;
|
||||
}
|
||||
return cb(null, blockInfo);
|
||||
});
|
||||
};
|
||||
|
||||
HistoricSync.prototype.getBlockFromFile = function(cb) {
|
||||
var self = this;
|
||||
|
||||
var blockInfo;
|
||||
|
||||
//get Info
|
||||
self.blockExtractor.getNextBlock(function(err, b) {
|
||||
if (err || ! b) return cb(err);
|
||||
blockInfo = b.getStandardizedObject(b.txs, self.network);
|
||||
blockInfo.previousblockhash = blockInfo.prev_block;
|
||||
|
||||
var ti=0;
|
||||
// Get TX Address
|
||||
b.txs.forEach(function(t) {
|
||||
|
||||
|
||||
var objTx = blockInfo.tx[ti++];
|
||||
|
||||
//add time from block
|
||||
objTx.time = blockInfo.time;
|
||||
|
||||
var to=0;
|
||||
t.outs.forEach( function(o) {
|
||||
|
||||
|
||||
var s = new Script(o.s);
|
||||
var addrs = self.sync.txDb.getAddrStr(s);
|
||||
|
||||
// support only for p2pubkey p2pubkeyhash and p2sh
|
||||
if (addrs.length === 1) {
|
||||
objTx.out[to].addrStr = addrs[0];
|
||||
}
|
||||
to++;
|
||||
});
|
||||
});
|
||||
self.sync.bDb.setLastFileIndex(self.blockExtractor.currentFileIndex, function(err) {
|
||||
return cb(err,blockInfo);
|
||||
// support only for p2pubkey p2pubkeyhash and p2sh
|
||||
if (addrs.length === 1) {
|
||||
objTx.out[to].addrStr = addrs[0];
|
||||
}
|
||||
to++;
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
HistoricSync.prototype.updateConnectedCountDB = function(cb) {
|
||||
var self = this;
|
||||
self.sync.bDb.countConnected(function(err, count) {
|
||||
self.connectedCountDB = count || 0;
|
||||
self.syncedBlocks = count || 0;
|
||||
return cb(err);
|
||||
self.sync.bDb.setLastFileIndex(self.blockExtractor.currentFileIndex, function(err) {
|
||||
return cb(err,blockInfo);
|
||||
});
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
HistoricSync.prototype.updateConnectedCountDB = function(cb) {
|
||||
var self = this;
|
||||
self.sync.bDb.countConnected(function(err, count) {
|
||||
self.connectedCountDB = count || 0;
|
||||
self.syncedBlocks = count || 0;
|
||||
return cb(err);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
HistoricSync.prototype.updateBlockChainHeight = function(cb) {
|
||||
var self = this;
|
||||
HistoricSync.prototype.updateBlockChainHeight = function(cb) {
|
||||
var self = this;
|
||||
|
||||
self.rpc.getBlockCount(function(err, res) {
|
||||
self.blockChainHeight = res.result;
|
||||
return cb(err);
|
||||
});
|
||||
};
|
||||
self.rpc.getBlockCount(function(err, res) {
|
||||
self.blockChainHeight = res.result;
|
||||
return cb(err);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
HistoricSync.prototype.checkNetworkSettings = function(next) {
|
||||
var self = this;
|
||||
HistoricSync.prototype.checkNetworkSettings = function(next) {
|
||||
var self = this;
|
||||
|
||||
self.hasGenesis = false;
|
||||
self.hasGenesis = false;
|
||||
|
||||
// check network config
|
||||
self.rpc.getBlockHash(0, function(err, res){
|
||||
// check network config
|
||||
self.rpc.getBlockHash(0, function(err, res){
|
||||
if (!err && ( res && res.result !== self.genesis)) {
|
||||
err = new Error(BAD_GEN_ERROR + config.network);
|
||||
}
|
||||
if (err) return next(err);
|
||||
self.sync.bDb.has(self.genesis, function(err, b) {
|
||||
if (!err && ( res && res.result !== self.genesis)) {
|
||||
err = new Error(BAD_GEN_ERROR + config.network);
|
||||
err = new Error(BAD_GEN_ERROR_DB + config.network);
|
||||
}
|
||||
if (err) return next(err);
|
||||
self.sync.bDb.has(self.genesis, function(err, b) {
|
||||
if (!err && ( res && res.result !== self.genesis)) {
|
||||
err = new Error(BAD_GEN_ERROR_DB + config.network);
|
||||
self.hasGenesis = b?true:false;
|
||||
return next(err);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
HistoricSync.prototype.updateStartBlock = function(next) {
|
||||
var self = this;
|
||||
|
||||
self.startBlock = self.genesis;
|
||||
|
||||
self.sync.bDb.getTip(function(err,tip) {
|
||||
if (!tip) return next();
|
||||
|
||||
var blockInfo;
|
||||
var oldtip;
|
||||
|
||||
//check that the tip is still on the mainchain
|
||||
async.doWhilst(
|
||||
function(cb) {
|
||||
self.sync.bDb.fromHashWithInfo(tip, function(err, bi) {
|
||||
blockInfo = bi ? bi.info : {};
|
||||
if (oldtip)
|
||||
self.sync.setBlockMain(oldtip, false, cb);
|
||||
else
|
||||
return cb();
|
||||
});
|
||||
},
|
||||
function(err) {
|
||||
if (err) return next(err);
|
||||
var ret = false;
|
||||
if ( self.blockChainHeight === blockInfo.height ||
|
||||
blockInfo.confirmations > 0) {
|
||||
ret = false;
|
||||
}
|
||||
self.hasGenesis = b?true:false;
|
||||
else {
|
||||
oldtip = tip;
|
||||
tip = blockInfo.previousblockhash;
|
||||
assert(tip);
|
||||
p('Previous TIP is now orphan. Back to:' + tip);
|
||||
ret = true;
|
||||
}
|
||||
return ret;
|
||||
},
|
||||
function(err) {
|
||||
self.startBlock = tip;
|
||||
p('Resuming sync from block:'+tip);
|
||||
return next(err);
|
||||
});
|
||||
});
|
||||
};
|
||||
}
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
HistoricSync.prototype.updateStartBlock = function(next) {
|
||||
var self = this;
|
||||
HistoricSync.prototype.prepareFileSync = function(opts, next) {
|
||||
var self = this;
|
||||
|
||||
self.startBlock = self.genesis;
|
||||
|
||||
self.sync.bDb.getTip(function(err,tip) {
|
||||
if (!tip) return next();
|
||||
|
||||
var blockInfo;
|
||||
var oldtip;
|
||||
|
||||
//check that the tip is still on the mainchain
|
||||
async.doWhilst(
|
||||
function(cb) {
|
||||
self.sync.bDb.fromHashWithInfo(tip, function(err, bi) {
|
||||
blockInfo = bi ? bi.info : {};
|
||||
if (oldtip)
|
||||
self.sync.setBlockMain(oldtip, false, cb);
|
||||
else
|
||||
return cb();
|
||||
});
|
||||
},
|
||||
function(err) {
|
||||
if (err) return next(err);
|
||||
var ret = false;
|
||||
if ( self.blockChainHeight === blockInfo.height || // Still the tip
|
||||
blockInfo.confirmations > 0) { // Or is confirmed
|
||||
ret = false;
|
||||
}
|
||||
else {
|
||||
oldtip = tip;
|
||||
tip = blockInfo.previousblockhash;
|
||||
p('Previous TIP is now orphan.');
|
||||
if (tip) {
|
||||
p('\tGoing back to:' + tip);
|
||||
ret = true;
|
||||
}
|
||||
else {
|
||||
p('\tNo able to recover last tip. A Total resync is needed.');
|
||||
tip = self.genesis;
|
||||
self.needResync = 1;
|
||||
ret = false;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
},
|
||||
function(err) {
|
||||
self.startBlock = tip;
|
||||
if (!self.needResync) {
|
||||
p('Resuming sync from block:'+tip);
|
||||
}
|
||||
return next(err);
|
||||
}
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
HistoricSync.prototype.prepareFileSync = function(opts, next) {
|
||||
var self = this;
|
||||
|
||||
if ( opts.forceRPC || !config.bitcoind.dataDir ||
|
||||
self.connectedCountDB > self.blockChainHeight * 0.9) return next();
|
||||
if ( opts.forceRPC || !config.bitcoind.dataDir ||
|
||||
self.connectedCountDB > self.blockChainHeight * 0.9) return next();
|
||||
|
||||
|
||||
try {
|
||||
self.blockExtractor = new BlockExtractor(config.bitcoind.dataDir, config.network);
|
||||
} catch (e) {
|
||||
p(e.message + '. Disabling file sync.');
|
||||
return next();
|
||||
}
|
||||
|
||||
self.getFn = self.getBlockFromFile;
|
||||
self.allowReorgs = true;
|
||||
self.sync.bDb.getLastFileIndex(function(err, idx) {
|
||||
if (opts.forceStartFile)
|
||||
self.blockExtractor.currentFileIndex = opts.forceStartFile;
|
||||
else if (idx) self.blockExtractor.currentFileIndex = idx;
|
||||
|
||||
var h = self.genesis;
|
||||
|
||||
p('Seeking file to:' + self.startBlock);
|
||||
//forward till startBlock
|
||||
async.whilst(
|
||||
function() {
|
||||
return h !== self.startBlock;
|
||||
},
|
||||
function (w_cb) {
|
||||
self.getBlockFromFile(function(err,b) {
|
||||
if (!b) return w_cb('Could not find block ' + self.startBlock);
|
||||
h=b.hash;
|
||||
setImmediate(function(){
|
||||
return w_cb(err);
|
||||
});
|
||||
});
|
||||
}, next);
|
||||
});
|
||||
};
|
||||
|
||||
//NOP
|
||||
HistoricSync.prototype.prepareRpcSync = function(opts, next) {
|
||||
var self = this;
|
||||
|
||||
if (self.blockExtractor) return next();
|
||||
self.getFn = self.getBlockFromRPC;
|
||||
self.currentRpcHash = self.startBlock;
|
||||
self.allowReorgs = false;
|
||||
try {
|
||||
self.blockExtractor = new BlockExtractor(config.bitcoind.dataDir, config.network);
|
||||
} catch (e) {
|
||||
p(e.message + '. Disabling file sync.');
|
||||
return next();
|
||||
};
|
||||
}
|
||||
|
||||
HistoricSync.prototype.showSyncStartMessage = function() {
|
||||
var self = this;
|
||||
self.getFn = self.getBlockFromFile;
|
||||
self.allowReorgs = true;
|
||||
self.sync.bDb.getLastFileIndex(function(err, idx) {
|
||||
if (opts.forceStartFile)
|
||||
self.blockExtractor.currentFileIndex = opts.forceStartFile;
|
||||
else if (idx) self.blockExtractor.currentFileIndex = idx;
|
||||
|
||||
p('Got ' + self.connectedCountDB +
|
||||
' blocks in current DB, out of ' + self.blockChainHeight + ' block at bitcoind');
|
||||
var h = self.genesis;
|
||||
|
||||
if (self.blockExtractor) {
|
||||
p('bitcoind dataDir configured...importing blocks from .dat files');
|
||||
p('First file index: ' + self.blockExtractor.currentFileIndex);
|
||||
}
|
||||
else {
|
||||
p('syncing from RPC (slow)');
|
||||
}
|
||||
|
||||
p('Starting from: ', self.startBlock);
|
||||
self.showProgress();
|
||||
};
|
||||
|
||||
|
||||
HistoricSync.prototype.setupSyncStatus = function() {
|
||||
var self = this;
|
||||
|
||||
var step = parseInt( (self.blockChainHeight - self.syncedBlocks) / 1000);
|
||||
if (step < 10) step = 10;
|
||||
|
||||
self.step = step;
|
||||
self.type = self.blockExtractor?'from .dat Files':'from RPC calls';
|
||||
self.status = 'syncing';
|
||||
self.startTs = Date.now();
|
||||
self.endTs = null;
|
||||
this.error = null;
|
||||
this.syncPercentage = 0;
|
||||
};
|
||||
|
||||
HistoricSync.prototype.prepareToSync = function(opts, next) {
|
||||
var self = this;
|
||||
|
||||
self.status = 'starting';
|
||||
async.series([
|
||||
function(s_c) {
|
||||
self.checkNetworkSettings(s_c);
|
||||
p('Seeking file to:' + self.startBlock);
|
||||
//forward till startBlock
|
||||
async.whilst(
|
||||
function() {
|
||||
return h !== self.startBlock;
|
||||
},
|
||||
function(s_c) {
|
||||
self.updateBlockChainHeight(s_c);
|
||||
},
|
||||
function(s_c) {
|
||||
self.updateStartBlock(s_c);
|
||||
},
|
||||
function(s_c) {
|
||||
if (self.needResync) {
|
||||
self.needResync=0;
|
||||
self.sync.destroy(s_c);
|
||||
}
|
||||
else s_c();
|
||||
},
|
||||
function(s_c) {
|
||||
self.updateConnectedCountDB(s_c);
|
||||
},
|
||||
function(s_c) {
|
||||
self.prepareFileSync(opts, s_c);
|
||||
},
|
||||
function(s_c) {
|
||||
self.prepareRpcSync(opts, s_c);
|
||||
},
|
||||
],
|
||||
function(err) {
|
||||
if (err) return(self.setError(err));
|
||||
function (w_cb) {
|
||||
self.getBlockFromFile(function(err,b) {
|
||||
if (!b) return w_cb('Could not find block ' + self.startBlock);
|
||||
h=b.hash;
|
||||
setImmediate(function(){
|
||||
return w_cb(err);
|
||||
});
|
||||
});
|
||||
}, next);
|
||||
});
|
||||
};
|
||||
|
||||
self.showSyncStartMessage();
|
||||
self.setupSyncStatus();
|
||||
return next();
|
||||
});
|
||||
};
|
||||
//NOP
|
||||
HistoricSync.prototype.prepareRpcSync = function(opts, next) {
|
||||
var self = this;
|
||||
|
||||
|
||||
HistoricSync.prototype.start = function(opts, next) {
|
||||
var self = this;
|
||||
if (self.blockExtractor) return next();
|
||||
self.getFn = self.getBlockFromRPC;
|
||||
self.currentRpcHash = self.startBlock;
|
||||
self.allowReorgs = false;
|
||||
return next();
|
||||
};
|
||||
|
||||
if (self.status==='starting' || self.status==='syncing') {
|
||||
p('## Wont start to sync while status is %s', self.status);
|
||||
return next();
|
||||
}
|
||||
HistoricSync.prototype.showSyncStartMessage = function() {
|
||||
var self = this;
|
||||
|
||||
self.prepareToSync(opts, function(err) {
|
||||
if (err) return next(self.setError(err));
|
||||
p('Got ' + self.connectedCountDB +
|
||||
' blocks in current DB, out of ' + self.blockChainHeight + ' block at bitcoind');
|
||||
|
||||
async.whilst(
|
||||
function() {
|
||||
self.showProgress();
|
||||
return self.status === 'syncing';
|
||||
},
|
||||
function (w_cb) {
|
||||
self.getFn(function(err,blockInfo) {
|
||||
if (err) return w_cb(self.setError(err));
|
||||
if (blockInfo && blockInfo.hash) {
|
||||
self.syncedBlocks++;
|
||||
self.sync.storeTipBlock(blockInfo, self.allowReorgs, function(err) {
|
||||
if (self.blockExtractor) {
|
||||
p('bitcoind dataDir configured...importing blocks from .dat files');
|
||||
p('First file index: ' + self.blockExtractor.currentFileIndex);
|
||||
}
|
||||
else {
|
||||
p('syncing from RPC (slow)');
|
||||
}
|
||||
|
||||
p('Starting from: ', self.startBlock);
|
||||
self.showProgress();
|
||||
};
|
||||
|
||||
|
||||
HistoricSync.prototype.setupSyncStatus = function() {
|
||||
var self = this;
|
||||
|
||||
var step = parseInt( (self.blockChainHeight - self.syncedBlocks) / 1000);
|
||||
if (step < 10) step = 10;
|
||||
|
||||
self.step = step;
|
||||
self.type = self.blockExtractor?'from .dat Files':'from RPC calls';
|
||||
self.status = 'syncing';
|
||||
self.startTs = Date.now();
|
||||
self.endTs = null;
|
||||
this.error = null;
|
||||
this.syncPercentage = 0;
|
||||
};
|
||||
|
||||
HistoricSync.prototype.prepareToSync = function(opts, next) {
|
||||
var self = this;
|
||||
|
||||
self.status = 'starting';
|
||||
async.series([
|
||||
function(s_c) {
|
||||
self.checkNetworkSettings(s_c);
|
||||
},
|
||||
function(s_c) {
|
||||
self.updateConnectedCountDB(s_c);
|
||||
},
|
||||
function(s_c) {
|
||||
self.updateBlockChainHeight(s_c);
|
||||
},
|
||||
function(s_c) {
|
||||
self.updateStartBlock(s_c);
|
||||
},
|
||||
function(s_c) {
|
||||
self.prepareFileSync(opts, s_c);
|
||||
},
|
||||
function(s_c) {
|
||||
self.prepareRpcSync(opts, s_c);
|
||||
},
|
||||
],
|
||||
function(err) {
|
||||
if (err) return(self.setError(err));
|
||||
|
||||
self.showSyncStartMessage();
|
||||
self.setupSyncStatus();
|
||||
return next();
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
HistoricSync.prototype.start = function(opts, next) {
|
||||
var self = this;
|
||||
|
||||
if (self.status==='starting' || self.status==='syncing') {
|
||||
p('## Wont start to sync while status is %s', self.status);
|
||||
return next();
|
||||
}
|
||||
|
||||
self.prepareToSync(opts, function(err) {
|
||||
if (err) return next(self.setError(err));
|
||||
|
||||
async.whilst(
|
||||
function() {
|
||||
self.showProgress();
|
||||
return self.status === 'syncing';
|
||||
},
|
||||
function (w_cb) {
|
||||
self.getFn(function(err,blockInfo) {
|
||||
if (err) return w_cb(self.setError(err));
|
||||
if (blockInfo && blockInfo.hash) {
|
||||
self.syncedBlocks++;
|
||||
self.sync.storeTipBlock(blockInfo, self.allowReorgs, function(err) {
|
||||
if (err) return w_cb(self.setError(err));
|
||||
|
||||
self.sync.bDb.setTip(blockInfo.hash, function(err) {
|
||||
if (err) return w_cb(self.setError(err));
|
||||
|
||||
self.sync.bDb.setTip(blockInfo.hash, function(err) {
|
||||
if (err) return w_cb(self.setError(err));
|
||||
|
||||
setImmediate(function(){
|
||||
return w_cb(err);
|
||||
});
|
||||
setImmediate(function(){
|
||||
return w_cb(err);
|
||||
});
|
||||
});
|
||||
}
|
||||
else {
|
||||
self.endTs = Date.now();
|
||||
self.status = 'finished';
|
||||
console.log('Done Syncing', self.info());
|
||||
return w_cb(err);
|
||||
}
|
||||
});
|
||||
}, next);
|
||||
});
|
||||
};
|
||||
return HistoricSync;
|
||||
}
|
||||
module.defineClass(spec);
|
||||
});
|
||||
}
|
||||
else {
|
||||
self.endTs = Date.now();
|
||||
self.status = 'finished';
|
||||
console.log('Done Syncing', self.info());
|
||||
return w_cb(err);
|
||||
}
|
||||
});
|
||||
}, next);
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = require('soop')(HistoricSync);
|
||||
|
|
240
lib/PeerSync.js
240
lib/PeerSync.js
|
@ -1,127 +1,121 @@
|
|||
'use strict';
|
||||
require('classtool');
|
||||
var fs = require('fs');
|
||||
var bitcoreUtil = require('bitcore/util/util');
|
||||
var Sync = require('./Sync');
|
||||
var Peer = require('bitcore/Peer');
|
||||
var config = require('../config/config');
|
||||
var networks = require('bitcore/networks');
|
||||
|
||||
function spec() {
|
||||
var fs = require('fs');
|
||||
var bitcoreUtil = require('bitcore/util/util');
|
||||
var Sync = require('./Sync').class();
|
||||
var Peer = require('bitcore/Peer').class();
|
||||
var config = require('../config/config');
|
||||
var networks = require('bitcore/networks');
|
||||
|
||||
var peerdb_fn = 'peerdb.json';
|
||||
|
||||
function PeerSync(opts) {
|
||||
this.connected = false;
|
||||
this.peerdb = undefined;
|
||||
this.allowReorgs = false;
|
||||
this.PeerManager = require('bitcore/PeerManager').createClass({
|
||||
network: (config.network === 'testnet' ? networks.testnet : networks.livenet)
|
||||
});
|
||||
this.peerman = new this.PeerManager();
|
||||
this.load_peers();
|
||||
this.sync = new Sync(opts);
|
||||
}
|
||||
|
||||
PeerSync.prototype.load_peers = function() {
|
||||
this.peerdb = [{
|
||||
ipv4: config.bitcoind.host,
|
||||
port: config.bitcoind.p2pPort
|
||||
}];
|
||||
|
||||
fs.writeFileSync(peerdb_fn, JSON.stringify(this.peerdb));
|
||||
};
|
||||
|
||||
PeerSync.prototype.info = function() {
|
||||
return {
|
||||
connected: this.connected,
|
||||
host: this.peerdb[0].ipv4,
|
||||
port: this.peerdb[0].port
|
||||
};
|
||||
};
|
||||
|
||||
PeerSync.prototype.handleInv = function(info) {
|
||||
var invs = info.message.invs;
|
||||
info.conn.sendGetData(invs);
|
||||
};
|
||||
|
||||
PeerSync.prototype.handleTx = function(info) {
|
||||
var tx = info.message.tx.getStandardizedObject();
|
||||
tx.outs = info.message.tx.outs;
|
||||
tx.ins = info.message.tx.ins;
|
||||
console.log('[p2p_sync] Handle tx: ' + tx.hash);
|
||||
tx.time = tx.time || Math.round(new Date().getTime() / 1000);
|
||||
|
||||
this.sync.storeTxs([tx], function(err) {
|
||||
if (err) {
|
||||
console.log('[p2p_sync] Error in handle TX: ' + JSON.stringify(err));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
PeerSync.prototype.handleBlock = function(info) {
|
||||
var self = this;
|
||||
var block = info.message.block;
|
||||
var blockHash = bitcoreUtil.formatHashFull(block.calcHash());
|
||||
|
||||
console.log('[p2p_sync] Handle block: %s (allowReorgs: %s)', blockHash, self.allowReorgs);
|
||||
|
||||
var tx_hashes = block.txs.map(function(tx) {
|
||||
return bitcoreUtil.formatHashFull(tx.hash);
|
||||
});
|
||||
|
||||
this.sync.storeTipBlock({
|
||||
'hash': blockHash,
|
||||
'tx': tx_hashes,
|
||||
'previousblockhash': bitcoreUtil.formatHashFull(block.prev_hash),
|
||||
}, self.allowReorgs, function(err) {
|
||||
if (err && err.message.match(/NEED_SYNC/) && self.historicSync) {
|
||||
console.log('[p2p_sync] Orphan block received. Triggering sync');
|
||||
self.historicSync.start({}, function(){
|
||||
console.log('[p2p_sync] Done resync.');
|
||||
});
|
||||
}
|
||||
else if (err) {
|
||||
console.log('[p2p_sync] Error in handle Block: ' + err);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
PeerSync.prototype.handle_connected = function(data) {
|
||||
var peerman = data.pm;
|
||||
var peers_n = peerman.peers.length;
|
||||
console.log('[p2p_sync] Connected to ' + peers_n + ' peer' + (peers_n !== 1 ? 's' : ''));
|
||||
};
|
||||
|
||||
PeerSync.prototype.run = function() {
|
||||
var self = this;
|
||||
|
||||
this.peerdb.forEach(function(datum) {
|
||||
var peer = new Peer(datum.ipv4, datum.port);
|
||||
self.peerman.addPeer(peer);
|
||||
});
|
||||
|
||||
this.peerman.on('connection', function(conn) {
|
||||
self.connected = true;
|
||||
conn.on('inv', self.handleInv.bind(self));
|
||||
conn.on('block', self.handleBlock.bind(self));
|
||||
conn.on('tx', self.handleTx.bind(self));
|
||||
});
|
||||
this.peerman.on('connect', self.handle_connected.bind(self));
|
||||
|
||||
this.peerman.on('netDisconnected', function() {
|
||||
self.connected = false;
|
||||
});
|
||||
|
||||
this.peerman.start();
|
||||
};
|
||||
|
||||
PeerSync.prototype.close = function() {
|
||||
this.sync.close();
|
||||
};
|
||||
|
||||
|
||||
return PeerSync;
|
||||
var peerdb_fn = 'peerdb.json';
|
||||
|
||||
function PeerSync(opts) {
|
||||
this.connected = false;
|
||||
this.peerdb = undefined;
|
||||
this.allowReorgs = false;
|
||||
this.PeerManager = require('soop').load('../node_modules/bitcore/PeerManager',{
|
||||
network: (config.network === 'testnet' ? networks.testnet : networks.livenet)
|
||||
});
|
||||
this.peerman = new this.PeerManager();
|
||||
this.load_peers();
|
||||
this.sync = new Sync(opts);
|
||||
}
|
||||
module.defineClass(spec);
|
||||
|
||||
PeerSync.prototype.load_peers = function() {
|
||||
this.peerdb = [{
|
||||
ipv4: config.bitcoind.host,
|
||||
port: config.bitcoind.p2pPort
|
||||
}];
|
||||
|
||||
fs.writeFileSync(peerdb_fn, JSON.stringify(this.peerdb));
|
||||
};
|
||||
|
||||
PeerSync.prototype.info = function() {
|
||||
return {
|
||||
connected: this.connected,
|
||||
host: this.peerdb[0].ipv4,
|
||||
port: this.peerdb[0].port
|
||||
};
|
||||
};
|
||||
|
||||
PeerSync.prototype.handleInv = function(info) {
|
||||
var invs = info.message.invs;
|
||||
info.conn.sendGetData(invs);
|
||||
};
|
||||
|
||||
PeerSync.prototype.handleTx = function(info) {
|
||||
var tx = info.message.tx.getStandardizedObject();
|
||||
tx.outs = info.message.tx.outs;
|
||||
tx.ins = info.message.tx.ins;
|
||||
console.log('[p2p_sync] Handle tx: ' + tx.hash);
|
||||
tx.time = tx.time || Math.round(new Date().getTime() / 1000);
|
||||
|
||||
this.sync.storeTxs([tx], function(err) {
|
||||
if (err) {
|
||||
console.log('[p2p_sync] Error in handle TX: ' + JSON.stringify(err));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
PeerSync.prototype.handleBlock = function(info) {
|
||||
var self = this;
|
||||
var block = info.message.block;
|
||||
var blockHash = bitcoreUtil.formatHashFull(block.calcHash());
|
||||
|
||||
console.log('[p2p_sync] Handle block: %s (allowReorgs: %s)', blockHash, self.allowReorgs);
|
||||
|
||||
var tx_hashes = block.txs.map(function(tx) {
|
||||
return bitcoreUtil.formatHashFull(tx.hash);
|
||||
});
|
||||
|
||||
this.sync.storeTipBlock({
|
||||
'hash': blockHash,
|
||||
'tx': tx_hashes,
|
||||
'previousblockhash': bitcoreUtil.formatHashFull(block.prev_hash),
|
||||
}, self.allowReorgs, function(err) {
|
||||
if (err && err.message.match(/NEED_SYNC/) && self.historicSync) {
|
||||
console.log('[p2p_sync] Orphan block received. Triggering sync');
|
||||
self.historicSync.start({}, function(){
|
||||
console.log('[p2p_sync] Done resync.');
|
||||
});
|
||||
}
|
||||
else if (err) {
|
||||
console.log('[p2p_sync] Error in handle Block: ' + err);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
PeerSync.prototype.handle_connected = function(data) {
|
||||
var peerman = data.pm;
|
||||
var peers_n = peerman.peers.length;
|
||||
console.log('[p2p_sync] Connected to ' + peers_n + ' peer' + (peers_n !== 1 ? 's' : ''));
|
||||
};
|
||||
|
||||
PeerSync.prototype.run = function() {
|
||||
var self = this;
|
||||
|
||||
this.peerdb.forEach(function(datum) {
|
||||
var peer = new Peer(datum.ipv4, datum.port);
|
||||
self.peerman.addPeer(peer);
|
||||
});
|
||||
|
||||
this.peerman.on('connection', function(conn) {
|
||||
self.connected = true;
|
||||
conn.on('inv', self.handleInv.bind(self));
|
||||
conn.on('block', self.handleBlock.bind(self));
|
||||
conn.on('tx', self.handleTx.bind(self));
|
||||
});
|
||||
this.peerman.on('connect', self.handle_connected.bind(self));
|
||||
|
||||
this.peerman.on('netDisconnected', function() {
|
||||
self.connected = false;
|
||||
});
|
||||
|
||||
this.peerman.start();
|
||||
};
|
||||
|
||||
PeerSync.prototype.close = function() {
|
||||
this.sync.close();
|
||||
};
|
||||
|
||||
|
||||
module.exports = require('soop')(PeerSync);
|
||||
|
|
|
@ -1,38 +1,32 @@
|
|||
'use strict';
|
||||
|
||||
require('classtool');
|
||||
var imports = require('soop').imports();
|
||||
var fs = require('fs');
|
||||
var buffertools = require('buffertools');
|
||||
var db = imports.db || JSON.parse( fs.readFileSync(imports.poolMatchFile || './poolMatchFile.json'));
|
||||
|
||||
function spec(b) {
|
||||
var PoolMatch = function() {
|
||||
var self = this;
|
||||
|
||||
var fs = require('fs');
|
||||
var buffertools = require('buffertools');
|
||||
var db = b.db || JSON.parse( fs.readFileSync(b.poolMatchFile || './poolMatchFile.json'));
|
||||
|
||||
var PoolMatch = function() {
|
||||
var self = this;
|
||||
|
||||
self.strings = {};
|
||||
db.forEach(function(pool) {
|
||||
pool.searchStrings.forEach(function(s) {
|
||||
self.strings[s] = {
|
||||
poolName: pool.poolName,
|
||||
url: pool.url
|
||||
};
|
||||
});
|
||||
self.strings = {};
|
||||
db.forEach(function(pool) {
|
||||
pool.searchStrings.forEach(function(s) {
|
||||
self.strings[s] = {
|
||||
poolName: pool.poolName,
|
||||
url: pool.url
|
||||
};
|
||||
});
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
PoolMatch.prototype.match = function(buffer) {
|
||||
var self = this;
|
||||
for(var k in self.strings) {
|
||||
if (buffertools.indexOf(buffer, k) >= 0) {
|
||||
return self.strings[k];
|
||||
}
|
||||
PoolMatch.prototype.match = function(buffer) {
|
||||
var self = this;
|
||||
for(var k in self.strings) {
|
||||
if (buffertools.indexOf(buffer, k) >= 0) {
|
||||
return self.strings[k];
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
return PoolMatch;
|
||||
}
|
||||
module.defineClass(spec);
|
||||
|
||||
module.exports = require('soop')(PoolMatch);
|
||||
|
|
182
lib/Rpc.js
182
lib/Rpc.js
|
@ -1,114 +1,110 @@
|
|||
'use strict';
|
||||
|
||||
require('classtool');
|
||||
var imports = require('soop').imports();
|
||||
|
||||
var RpcClient = require('bitcore/RpcClient'),
|
||||
BitcoreBlock = require('bitcore/Block'),
|
||||
bitcoreUtil = require('bitcore/util/util'),
|
||||
util = require('util'),
|
||||
config = require('../config/config');
|
||||
|
||||
var bitcoreRpc = imports.bitcoreRpc || new RpcClient(config.bitcoind);
|
||||
|
||||
function Rpc() {
|
||||
}
|
||||
|
||||
Rpc._parseTxResult = function(info) {
|
||||
var b = new Buffer(info.hex,'hex');
|
||||
|
||||
// remove fields we dont need, to speed and adapt the information
|
||||
delete info.hex;
|
||||
|
||||
// Inputs => add index + coinBase flag
|
||||
var n =0;
|
||||
info.vin.forEach(function(i) {
|
||||
i.n = n++;
|
||||
if (i.coinbase) info.isCoinBase = true;
|
||||
if (i.scriptSig) delete i.scriptSig.hex;
|
||||
});
|
||||
|
||||
// Outputs => add total
|
||||
var valueOutSat = 0;
|
||||
info.vout.forEach( function(o) {
|
||||
valueOutSat += o.value * bitcoreUtil.COIN;
|
||||
delete o.scriptPubKey.hex;
|
||||
});
|
||||
info.valueOut = parseInt(valueOutSat) / bitcoreUtil.COIN;
|
||||
info.size = b.length;
|
||||
|
||||
return info;
|
||||
};
|
||||
|
||||
|
||||
function spec(b) {
|
||||
var RpcClient = require('bitcore/RpcClient').class(),
|
||||
BitcoreBlock = require('bitcore/Block').class(),
|
||||
bitcoreUtil = require('bitcore/util/util'),
|
||||
util = require('util'),
|
||||
config = require('../config/config');
|
||||
Rpc.errMsg = function(err) {
|
||||
var e = err;
|
||||
e.message += util.format(' [Host: %s:%d User:%s Using password:%s]',
|
||||
bitcoreRpc.host,
|
||||
bitcoreRpc.port,
|
||||
bitcoreRpc.user,
|
||||
bitcoreRpc.pass?'yes':'no'
|
||||
);
|
||||
return e;
|
||||
};
|
||||
|
||||
var bitcoreRpc = b.bitcoreRpc || new RpcClient(config.bitcoind);
|
||||
Rpc.getTxInfo = function(txid, doNotParse, cb) {
|
||||
var self = this;
|
||||
|
||||
function Rpc() {
|
||||
if (typeof doNotParse === 'function') {
|
||||
cb = doNotParse;
|
||||
doNotParse = false;
|
||||
}
|
||||
|
||||
Rpc._parseTxResult = function(info) {
|
||||
var b = new Buffer(info.hex,'hex');
|
||||
bitcoreRpc.getRawTransaction(txid, 1, function(err, txInfo) {
|
||||
// Not found?
|
||||
if (err && err.code === -5) return cb();
|
||||
if (err) return cb(self.errMsg(err));
|
||||
|
||||
// remove fields we dont need, to speed and adapt the information
|
||||
delete info.hex;
|
||||
|
||||
// Inputs => add index + coinBase flag
|
||||
var n =0;
|
||||
info.vin.forEach(function(i) {
|
||||
i.n = n++;
|
||||
if (i.coinbase) info.isCoinBase = true;
|
||||
if (i.scriptSig) delete i.scriptSig.hex;
|
||||
});
|
||||
|
||||
// Outputs => add total
|
||||
var valueOutSat = 0;
|
||||
info.vout.forEach( function(o) {
|
||||
valueOutSat += o.value * bitcoreUtil.COIN;
|
||||
delete o.scriptPubKey.hex;
|
||||
});
|
||||
info.valueOut = parseInt(valueOutSat) / bitcoreUtil.COIN;
|
||||
info.size = b.length;
|
||||
|
||||
return info;
|
||||
};
|
||||
var info = doNotParse ? txInfo.result : self._parseTxResult(txInfo.result);
|
||||
return cb(null,info);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
Rpc.errMsg = function(err) {
|
||||
var e = err;
|
||||
e.message += util.format(' [Host: %s:%d User:%s Using password:%s]',
|
||||
bitcoreRpc.host,
|
||||
bitcoreRpc.port,
|
||||
bitcoreRpc.user,
|
||||
bitcoreRpc.pass?'yes':'no'
|
||||
);
|
||||
return e;
|
||||
};
|
||||
Rpc.blockIndex = function(height, cb) {
|
||||
var self = this;
|
||||
|
||||
Rpc.getTxInfo = function(txid, doNotParse, cb) {
|
||||
var self = this;
|
||||
bitcoreRpc.getBlockHash(height, function(err, bh){
|
||||
if (err) return cb(self.errMsg(err));
|
||||
cb(null, { blockHash: bh.result });
|
||||
});
|
||||
};
|
||||
|
||||
if (typeof doNotParse === 'function') {
|
||||
cb = doNotParse;
|
||||
doNotParse = false;
|
||||
}
|
||||
Rpc.getBlock = function(hash, cb) {
|
||||
var self = this;
|
||||
|
||||
bitcoreRpc.getRawTransaction(txid, 1, function(err, txInfo) {
|
||||
// Not found?
|
||||
if (err && err.code === -5) return cb();
|
||||
if (err) return cb(self.errMsg(err));
|
||||
|
||||
var info = doNotParse ? txInfo.result : self._parseTxResult(txInfo.result);
|
||||
return cb(null,info);
|
||||
});
|
||||
};
|
||||
bitcoreRpc.getBlock(hash, function(err,info) {
|
||||
// Not found?
|
||||
if (err && err.code === -5) return cb();
|
||||
if (err) return cb(self.errMsg(err));
|
||||
|
||||
|
||||
Rpc.blockIndex = function(height, cb) {
|
||||
var self = this;
|
||||
if (info.result.height)
|
||||
info.result.reward = BitcoreBlock.getBlockValue(info.result.height) / bitcoreUtil.COIN ;
|
||||
|
||||
bitcoreRpc.getBlockHash(height, function(err, bh){
|
||||
if (err) return cb(self.errMsg(err));
|
||||
cb(null, { blockHash: bh.result });
|
||||
});
|
||||
};
|
||||
return cb(err,info.result);
|
||||
});
|
||||
};
|
||||
|
||||
Rpc.getBlock = function(hash, cb) {
|
||||
var self = this;
|
||||
Rpc.sendRawTransaction = function(rawtx, cb) {
|
||||
var self = this;
|
||||
bitcoreRpc.sendRawTransaction(rawtx, function(err, txid) {
|
||||
if (err && err.code === -5) return cb(err); // transaction already in block chain
|
||||
if (err) return cb(self.errMsg(err));
|
||||
|
||||
bitcoreRpc.getBlock(hash, function(err,info) {
|
||||
// Not found?
|
||||
if (err && err.code === -5) return cb();
|
||||
if (err) return cb(self.errMsg(err));
|
||||
|
||||
|
||||
if (info.result.height)
|
||||
info.result.reward = BitcoreBlock.getBlockValue(info.result.height) / bitcoreUtil.COIN ;
|
||||
|
||||
return cb(err,info.result);
|
||||
});
|
||||
};
|
||||
|
||||
Rpc.sendRawTransaction = function(rawtx, cb) {
|
||||
var self = this;
|
||||
bitcoreRpc.sendRawTransaction(rawtx, function(err, txid) {
|
||||
if (err && err.code === -5) return cb(err); // transaction already in block chain
|
||||
if (err) return cb(self.errMsg(err));
|
||||
|
||||
return cb(err, txid.result);
|
||||
});
|
||||
};
|
||||
|
||||
return Rpc;
|
||||
}
|
||||
module.defineClass(spec);
|
||||
return cb(err, txid.result);
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = require('soop')(Rpc);
|
||||
|
||||
|
||||
|
|
523
lib/Sync.js
523
lib/Sync.js
|
@ -1,289 +1,282 @@
|
|||
'use strict';
|
||||
|
||||
require('classtool');
|
||||
var imports = require('soop').imports();
|
||||
var sockets = require('../app/controllers/socket.js');
|
||||
|
||||
var config = imports.config || require('../config/config');
|
||||
var networks = require('bitcore/networks');
|
||||
var async = require('async');
|
||||
|
||||
|
||||
function spec() {
|
||||
var sockets = require('../app/controllers/socket.js');
|
||||
var BlockDb = require('./BlockDb').class();
|
||||
function Sync(opts) {
|
||||
this.opts = opts || {};
|
||||
this.bDb = require('./BlockDb').default();
|
||||
this.txDb = require('./TransactionDb').default();
|
||||
this.txDb.on('tx_for_address', this.handleTxForAddress.bind(this));
|
||||
this.txDb.on('new_tx', this.handleNewTx.bind(this));
|
||||
this.bDb.on('new_block', this.handleNewBlock.bind(this));
|
||||
this.network = config.network === 'testnet' ? networks.testnet : networks.livenet;
|
||||
}
|
||||
|
||||
var TransactionDb = require('./TransactionDb').class();
|
||||
var config = require('../config/config');
|
||||
var networks = require('bitcore/networks');
|
||||
var async = require('async');
|
||||
Sync.prototype.close = function(cb) {
|
||||
var self = this;
|
||||
self.txDb.close(function() {
|
||||
self.bDb.close(cb);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
function Sync(opts) {
|
||||
this.opts = opts || {};
|
||||
this.bDb = new BlockDb(opts);
|
||||
this.txDb = new TransactionDb(opts);
|
||||
this.txDb.on('tx_for_address', this.handleTxForAddress.bind(this));
|
||||
this.txDb.on('new_tx', this.handleNewTx.bind(this));
|
||||
this.bDb.on('new_block', this.handleNewBlock.bind(this));
|
||||
this.network = config.network === 'testnet' ? networks.testnet : networks.livenet;
|
||||
Sync.prototype.destroy = function(next) {
|
||||
var self = this;
|
||||
async.series([
|
||||
|
||||
function(b) {
|
||||
self.bDb.drop(b);
|
||||
},
|
||||
function(b) {
|
||||
self.txDb.drop(b);
|
||||
},
|
||||
], next);
|
||||
};
|
||||
|
||||
/*
|
||||
* Arrives a NEW block, which is the new TIP
|
||||
*
|
||||
* Case 0) Simple case
|
||||
* A-B-C-D-E(TIP)-NEW
|
||||
*
|
||||
* Case 1)
|
||||
* A-B-C-D-E(TIP)
|
||||
* \
|
||||
* NEW
|
||||
*
|
||||
* 1) Declare D-E orphans (and possible invalidate TXs on them)
|
||||
*
|
||||
* Case 2)
|
||||
* A-B-C-D-E(TIP)
|
||||
* \
|
||||
* F-G-NEW
|
||||
* 1) Set F-G as connected (mark TXs as valid)
|
||||
* 2) Declare D-E orphans (and possible invalidate TXs on them)
|
||||
*
|
||||
*
|
||||
* Case 3)
|
||||
*
|
||||
* A-B-C-D-E(TIP) ... NEW
|
||||
*
|
||||
* NEW is ignored (if allowReorgs is false)
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
Sync.prototype.storeTipBlock = function(b, allowReorgs, cb) {
|
||||
|
||||
if (typeof allowReorgs === 'function') {
|
||||
cb = allowReorgs;
|
||||
allowReorgs = true;
|
||||
}
|
||||
if (!b) return cb();
|
||||
|
||||
Sync.prototype.close = function(cb) {
|
||||
var self = this;
|
||||
self.txDb.close(function() {
|
||||
self.bDb.close(cb);
|
||||
});
|
||||
};
|
||||
var self = this;
|
||||
var oldTip, oldNext, needReorg = false;
|
||||
var newPrev = b.previousblockhash;
|
||||
|
||||
async.series([
|
||||
|
||||
Sync.prototype.destroy = function(next) {
|
||||
var self = this;
|
||||
async.series([
|
||||
|
||||
function(b) {
|
||||
self.bDb.drop(b);
|
||||
},
|
||||
function(b) {
|
||||
self.txDb.drop(b);
|
||||
},
|
||||
], next);
|
||||
};
|
||||
|
||||
/*
|
||||
* Arrives a NEW block, which is the new TIP
|
||||
*
|
||||
* Case 0) Simple case
|
||||
* A-B-C-D-E(TIP)-NEW
|
||||
*
|
||||
* Case 1)
|
||||
* A-B-C-D-E(TIP)
|
||||
* \
|
||||
* NEW
|
||||
*
|
||||
* 1) Declare D-E orphans (and possible invalidate TXs on them)
|
||||
*
|
||||
* Case 2)
|
||||
* A-B-C-D-E(TIP)
|
||||
* \
|
||||
* F-G-NEW
|
||||
* 1) Set F-G as connected (mark TXs as valid)
|
||||
* 2) Declare D-E orphans (and possible invalidate TXs on them)
|
||||
*
|
||||
*
|
||||
* Case 3)
|
||||
*
|
||||
* A-B-C-D-E(TIP) ... NEW
|
||||
*
|
||||
* NEW is ignored (if allowReorgs is false)
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
Sync.prototype.storeTipBlock = function(b, allowReorgs, cb) {
|
||||
|
||||
if (typeof allowReorgs === 'function') {
|
||||
cb = allowReorgs;
|
||||
allowReorgs = true;
|
||||
}
|
||||
if (!b) return cb();
|
||||
|
||||
var self = this;
|
||||
var oldTip, oldNext, needReorg = false;
|
||||
var newPrev = b.previousblockhash;
|
||||
|
||||
async.series([
|
||||
|
||||
function(c) {
|
||||
self.bDb.has(b.hash, function(err, val) {
|
||||
return c(err ||
|
||||
(val ? new Error('WARN: Ignoring already existing block:' + b.hash) : null));
|
||||
});
|
||||
},
|
||||
function(c) {
|
||||
if (!allowReorgs) return c();
|
||||
self.bDb.has(newPrev, function(err, val) {
|
||||
if (!val && newPrev.match(/^0+$/)) return c();
|
||||
|
||||
return c(err ||
|
||||
(!val ? new Error('NEED_SYNC Ignoring block with non existing prev:' + b.hash) : null));
|
||||
});
|
||||
},
|
||||
function(c) {
|
||||
self.txDb.createFromBlock(b, function(err) {
|
||||
return c(err);
|
||||
});
|
||||
},
|
||||
function(c) {
|
||||
if (!allowReorgs) return c();
|
||||
self.bDb.getTip(function(err, val) {
|
||||
oldTip = val;
|
||||
if (oldTip && newPrev !== oldTip) needReorg = true;
|
||||
return c();
|
||||
});
|
||||
},
|
||||
function(c) {
|
||||
if (!needReorg) return c();
|
||||
self.bDb.getNext(newPrev, function(err, val) {
|
||||
if (err) return c(err);
|
||||
oldNext = val;
|
||||
return c();
|
||||
});
|
||||
},
|
||||
function(c) {
|
||||
self.bDb.add(b, c);
|
||||
},
|
||||
function(c) {
|
||||
if (!needReorg) return c();
|
||||
console.log('NEW TIP: %s NEED REORG (old tip: %s)', b.hash, oldTip);
|
||||
self.processReorg(oldTip, oldNext, newPrev, c);
|
||||
},
|
||||
function(c) {
|
||||
if (!allowReorgs) return c();
|
||||
self.bDb.setTip(b.hash, function(err) {
|
||||
return c(err);
|
||||
});
|
||||
},
|
||||
function(c) {
|
||||
self.bDb.setNext(newPrev, b.hash, function(err) {
|
||||
return c(err);
|
||||
});
|
||||
}
|
||||
|
||||
],
|
||||
function(err) {
|
||||
if (err && err.toString().match(/WARN/)) {
|
||||
err = null;
|
||||
}
|
||||
return cb(err);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
|
||||
Sync.prototype.processReorg = function(oldTip, oldNext, newPrev, cb) {
|
||||
var self = this;
|
||||
|
||||
var orphanizeFrom;
|
||||
|
||||
async.series([
|
||||
|
||||
function(c) {
|
||||
self.bDb.isMain(newPrev, function(err, val) {
|
||||
if (!val) return c();
|
||||
|
||||
console.log('# Reorg Case 1)');
|
||||
// case 1
|
||||
orphanizeFrom = oldNext;
|
||||
return c(err);
|
||||
});
|
||||
},
|
||||
function(c) {
|
||||
if (orphanizeFrom) return c();
|
||||
|
||||
console.log('# Reorg Case 2)');
|
||||
self.setBranchConnectedBackwards(newPrev, function(err, yHash, newYHashNext) {
|
||||
if (err) return c(err);
|
||||
self.bDb.getNext(yHash, function(err, yHashNext) {
|
||||
orphanizeFrom = yHashNext;
|
||||
self.bDb.setNext(yHash, newYHashNext, function(err) {
|
||||
return c(err);
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
function(c) {
|
||||
if (!orphanizeFrom) return c();
|
||||
self.setBranchOrphan(orphanizeFrom, function(err) {
|
||||
return c(err);
|
||||
});
|
||||
},
|
||||
],
|
||||
function(err) {
|
||||
return cb(err);
|
||||
});
|
||||
};
|
||||
|
||||
Sync.prototype.setBlockMain = function(hash, isMain, cb) {
|
||||
var self = this;
|
||||
self.bDb.setMain(hash, isMain, function(err) {
|
||||
if (err) return cb(err);
|
||||
return self.txDb.handleBlockChange(hash, isMain, cb);
|
||||
});
|
||||
};
|
||||
|
||||
Sync.prototype.setBranchOrphan = function(fromHash, cb) {
|
||||
var self = this,
|
||||
hashInterator = fromHash;
|
||||
|
||||
async.whilst(
|
||||
function() {
|
||||
return hashInterator;
|
||||
},
|
||||
function(c) {
|
||||
self.setBlockMain(hashInterator, false, function(err) {
|
||||
if (err) return cb(err);
|
||||
self.bDb.getNext(hashInterator, function(err, val) {
|
||||
hashInterator = val;
|
||||
return c(err);
|
||||
});
|
||||
self.bDb.has(b.hash, function(err, val) {
|
||||
return c(err ||
|
||||
(val ? new Error('WARN: Ignoring already existing block:' + b.hash) : null));
|
||||
});
|
||||
}, cb);
|
||||
};
|
||||
|
||||
Sync.prototype.setBranchConnectedBackwards = function(fromHash, cb) {
|
||||
var self = this,
|
||||
hashInterator = fromHash,
|
||||
lastHash = fromHash,
|
||||
isMain;
|
||||
|
||||
async.doWhilst(
|
||||
},
|
||||
function(c) {
|
||||
self.setBlockMain(hashInterator, true, function(err) {
|
||||
if (!allowReorgs) return c();
|
||||
self.bDb.has(newPrev, function(err, val) {
|
||||
if (!val && newPrev.match(/^0+$/)) return c();
|
||||
|
||||
return c(err ||
|
||||
(!val ? new Error('NEED_SYNC Ignoring block with non existing prev:' + b.hash) : null));
|
||||
});
|
||||
},
|
||||
function(c) {
|
||||
self.txDb.createFromBlock(b, function(err) {
|
||||
return c(err);
|
||||
});
|
||||
},
|
||||
function(c) {
|
||||
if (!allowReorgs) return c();
|
||||
self.bDb.getTip(function(err, val) {
|
||||
oldTip = val;
|
||||
if (oldTip && newPrev !== oldTip) needReorg = true;
|
||||
return c();
|
||||
});
|
||||
},
|
||||
function(c) {
|
||||
if (!needReorg) return c();
|
||||
self.bDb.getNext(newPrev, function(err, val) {
|
||||
if (err) return c(err);
|
||||
self.bDb.getPrev(hashInterator, function(err, val) {
|
||||
if (err) return c(err);
|
||||
lastHash = hashInterator;
|
||||
hashInterator = val;
|
||||
self.bDb.isMain(hashInterator, function(err, val) {
|
||||
isMain = val;
|
||||
return c();
|
||||
});
|
||||
});
|
||||
oldNext = val;
|
||||
return c();
|
||||
});
|
||||
},
|
||||
function() {
|
||||
return hashInterator && !isMain;
|
||||
function(c) {
|
||||
self.bDb.add(b, c);
|
||||
},
|
||||
function(err) {
|
||||
console.log('\tFound yBlock:', hashInterator);
|
||||
return cb(err, hashInterator, lastHash);
|
||||
function(c) {
|
||||
if (!needReorg) return c();
|
||||
console.log('NEW TIP: %s NEED REORG (old tip: %s)', b.hash, oldTip);
|
||||
self.processReorg(oldTip, oldNext, newPrev, c);
|
||||
},
|
||||
function(c) {
|
||||
if (!allowReorgs) return c();
|
||||
self.bDb.setTip(b.hash, function(err) {
|
||||
return c(err);
|
||||
});
|
||||
},
|
||||
function(c) {
|
||||
self.bDb.setNext(newPrev, b.hash, function(err) {
|
||||
return c(err);
|
||||
});
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
Sync.prototype.handleTxForAddress = function(data) {
|
||||
if (this.opts.shouldBroadcast) {
|
||||
sockets.broadcastAddressTx(data.address, data.txid);
|
||||
}
|
||||
};
|
||||
|
||||
Sync.prototype.handleNewTx = function(data) {
|
||||
if (this.opts.shouldBroadcast) {
|
||||
sockets.broadcastTx(data.tx);
|
||||
}
|
||||
};
|
||||
|
||||
Sync.prototype.handleNewBlock = function(data) {
|
||||
if (this.opts.shouldBroadcast) {
|
||||
sockets.broadcastBlock(data.blockid);
|
||||
}
|
||||
};
|
||||
|
||||
Sync.prototype.storeTxs = function(txs, cb) {
|
||||
var self = this;
|
||||
self.txDb.createFromArray(txs, null, function(err) {
|
||||
if (err) return cb(err);
|
||||
],
|
||||
function(err) {
|
||||
if (err && err.toString().match(/WARN/)) {
|
||||
err = null;
|
||||
}
|
||||
return cb(err);
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
return Sync;
|
||||
}
|
||||
module.defineClass(spec);
|
||||
|
||||
Sync.prototype.processReorg = function(oldTip, oldNext, newPrev, cb) {
|
||||
var self = this;
|
||||
|
||||
var orphanizeFrom;
|
||||
|
||||
async.series([
|
||||
|
||||
function(c) {
|
||||
self.bDb.isMain(newPrev, function(err, val) {
|
||||
if (!val) return c();
|
||||
|
||||
console.log('# Reorg Case 1)');
|
||||
// case 1
|
||||
orphanizeFrom = oldNext;
|
||||
return c(err);
|
||||
});
|
||||
},
|
||||
function(c) {
|
||||
if (orphanizeFrom) return c();
|
||||
|
||||
console.log('# Reorg Case 2)');
|
||||
self.setBranchConnectedBackwards(newPrev, function(err, yHash, newYHashNext) {
|
||||
if (err) return c(err);
|
||||
self.bDb.getNext(yHash, function(err, yHashNext) {
|
||||
orphanizeFrom = yHashNext;
|
||||
self.bDb.setNext(yHash, newYHashNext, function(err) {
|
||||
return c(err);
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
function(c) {
|
||||
if (!orphanizeFrom) return c();
|
||||
self.setBranchOrphan(orphanizeFrom, function(err) {
|
||||
return c(err);
|
||||
});
|
||||
},
|
||||
],
|
||||
function(err) {
|
||||
return cb(err);
|
||||
});
|
||||
};
|
||||
|
||||
Sync.prototype.setBlockMain = function(hash, isMain, cb) {
|
||||
var self = this;
|
||||
self.bDb.setMain(hash, isMain, function(err) {
|
||||
if (err) return cb(err);
|
||||
return self.txDb.handleBlockChange(hash, isMain, cb);
|
||||
});
|
||||
};
|
||||
|
||||
Sync.prototype.setBranchOrphan = function(fromHash, cb) {
|
||||
var self = this,
|
||||
hashInterator = fromHash;
|
||||
|
||||
async.whilst(
|
||||
function() {
|
||||
return hashInterator;
|
||||
},
|
||||
function(c) {
|
||||
self.setBlockMain(hashInterator, false, function(err) {
|
||||
if (err) return cb(err);
|
||||
self.bDb.getNext(hashInterator, function(err, val) {
|
||||
hashInterator = val;
|
||||
return c(err);
|
||||
});
|
||||
});
|
||||
}, cb);
|
||||
};
|
||||
|
||||
Sync.prototype.setBranchConnectedBackwards = function(fromHash, cb) {
|
||||
var self = this,
|
||||
hashInterator = fromHash,
|
||||
lastHash = fromHash,
|
||||
isMain;
|
||||
|
||||
async.doWhilst(
|
||||
function(c) {
|
||||
self.setBlockMain(hashInterator, true, function(err) {
|
||||
if (err) return c(err);
|
||||
self.bDb.getPrev(hashInterator, function(err, val) {
|
||||
if (err) return c(err);
|
||||
lastHash = hashInterator;
|
||||
hashInterator = val;
|
||||
self.bDb.isMain(hashInterator, function(err, val) {
|
||||
isMain = val;
|
||||
return c();
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
function() {
|
||||
return hashInterator && !isMain;
|
||||
},
|
||||
function(err) {
|
||||
console.log('\tFound yBlock:', hashInterator);
|
||||
return cb(err, hashInterator, lastHash);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
Sync.prototype.handleTxForAddress = function(data) {
|
||||
if (this.opts.shouldBroadcast) {
|
||||
sockets.broadcastAddressTx(data.address, data.txid);
|
||||
}
|
||||
};
|
||||
|
||||
Sync.prototype.handleNewTx = function(data) {
|
||||
if (this.opts.shouldBroadcast) {
|
||||
sockets.broadcastTx(data.tx);
|
||||
}
|
||||
};
|
||||
|
||||
Sync.prototype.handleNewBlock = function(data) {
|
||||
if (this.opts.shouldBroadcast) {
|
||||
sockets.broadcastBlock(data.blockid);
|
||||
}
|
||||
};
|
||||
|
||||
Sync.prototype.storeTxs = function(txs, cb) {
|
||||
var self = this;
|
||||
self.txDb.createFromArray(txs, null, function(err) {
|
||||
if (err) return cb(err);
|
||||
return cb(err);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
module.exports = require('soop')(Sync);
|
||||
|
|
1290
lib/TransactionDb.js
1290
lib/TransactionDb.js
File diff suppressed because it is too large
Load Diff
18
package.json
18
package.json
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "insight-bitcore-api",
|
||||
"description": "An open-source bitcoin blockchain API. The Insight API provides you with a convenient, powerful and simple way to query and broadcast data on the bitcoin network and build your own services with it.",
|
||||
"version": "0.1.3",
|
||||
"version": "0.1.4",
|
||||
"author": {
|
||||
"name": "Ryan X Charles",
|
||||
"email": "ryan@bitpay.com"
|
||||
|
@ -36,13 +36,11 @@
|
|||
"license": "MIT",
|
||||
"keywords": [
|
||||
"insight",
|
||||
"secret",
|
||||
"enigma",
|
||||
"riddle",
|
||||
"mystification",
|
||||
"puzzle",
|
||||
"conundrum",
|
||||
"api",
|
||||
"insight api",
|
||||
"blockchain",
|
||||
"bitcoin api",
|
||||
"blockchain api",
|
||||
"json",
|
||||
"bitcore"
|
||||
],
|
||||
"engines": {
|
||||
|
@ -52,12 +50,13 @@
|
|||
"start": "node node_modules/grunt-cli/bin/grunt"
|
||||
},
|
||||
"dependencies": {
|
||||
"bitcore": "~0.1.6",
|
||||
"base58-native": "0.1.2",
|
||||
"async": "*",
|
||||
"leveldown": "*",
|
||||
"levelup": "*",
|
||||
"glob": "*",
|
||||
"classtool": "*",
|
||||
"soop": "git://github.com/gasteve/node-soop.git",
|
||||
"commander": "*",
|
||||
"bignum": "*",
|
||||
"express": "~3.4.7",
|
||||
|
@ -67,7 +66,6 @@
|
|||
"moment": "~2.5.0",
|
||||
"sinon": "~1.7.3",
|
||||
"chai": "~1.8.1",
|
||||
"bitcore": "git://github.com/bitpay/bitcore.git",
|
||||
"bufferput": "git://github.com/bitpay/node-bufferput.git",
|
||||
"xmlhttprequest": "~1.6.0"
|
||||
},
|
||||
|
|
|
@ -8,7 +8,7 @@ process.env.NODE_ENV = process.env.NODE_ENV || 'development';
|
|||
var assert = require('assert'),
|
||||
fs = require('fs'),
|
||||
util = require('util'),
|
||||
TransactionDb = require('../../lib/TransactionDb').class();
|
||||
TransactionDb = require('../../lib/TransactionDb').default();
|
||||
|
||||
var txItemsValid = JSON.parse(fs.readFileSync('test/integration/txitems.json'));
|
||||
var txDb;
|
||||
|
@ -16,7 +16,7 @@ var txDb;
|
|||
describe('TransactionDb fromIdWithInfo', function(){
|
||||
|
||||
before(function(c) {
|
||||
txDb = new TransactionDb();
|
||||
txDb = TransactionDb;
|
||||
return c();
|
||||
});
|
||||
|
||||
|
@ -118,7 +118,7 @@ describe('TransactionDb fromIdWithInfo', function(){
|
|||
describe('TransactionDb Outs', function(){
|
||||
|
||||
before(function(c) {
|
||||
txDb = new TransactionDb();
|
||||
txDb = TransactionDb;
|
||||
return c();
|
||||
});
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ var
|
|||
util = require('util'),
|
||||
async = require('async'),
|
||||
config = require('../../config/config'),
|
||||
TransactionDb = require('../../lib/TransactionDb').class();
|
||||
TransactionDb = require('../../lib/TransactionDb').default();
|
||||
|
||||
var spentValid = JSON.parse(fs.readFileSync('test/integration/spent.json'));
|
||||
|
||||
|
@ -20,7 +20,7 @@ var txDb;
|
|||
describe('TransactionDb Expenses', function(){
|
||||
|
||||
before(function(c) {
|
||||
txDb = new TransactionDb();
|
||||
txDb = TransactionDb;
|
||||
|
||||
// lets spend!
|
||||
async.each(Object.keys(spentValid),
|
||||
|
|
|
@ -6,8 +6,8 @@ process.env.NODE_ENV = process.env.NODE_ENV || 'development';
|
|||
|
||||
var assert = require('assert'),
|
||||
fs = require('fs'),
|
||||
Address = require('../../app/models/Address').class(),
|
||||
TransactionDb = require('../../lib/TransactionDb').class(),
|
||||
Address = require('../../app/models/Address'),
|
||||
TransactionDb = require('../../lib/TransactionDb').default(),
|
||||
addrValid = JSON.parse(fs.readFileSync('test/integration/addr.json')),
|
||||
utxoValid = JSON.parse(fs.readFileSync('test/integration/utxo.json'));
|
||||
|
||||
|
@ -15,7 +15,7 @@ var txDb;
|
|||
describe('Address balances', function() {
|
||||
|
||||
before(function(c) {
|
||||
txDb = new TransactionDb();
|
||||
txDb = TransactionDb;
|
||||
return c();
|
||||
});
|
||||
|
||||
|
|
|
@ -10,14 +10,14 @@ var TESTING_BLOCK = '000000000185678d3d7ecc9962c96418174431f93fe20bf216d55652724
|
|||
var
|
||||
assert = require('assert'),
|
||||
// config = require('../../config/config'),
|
||||
BlockDb = require('../../lib/BlockDb').class();
|
||||
BlockDb = require('../../lib/BlockDb').default();
|
||||
|
||||
var bDb;
|
||||
|
||||
describe('BlockDb fromHashWithInfo', function() {
|
||||
|
||||
before(function(c) {
|
||||
bDb = new BlockDb();
|
||||
bDb = BlockDb;
|
||||
return c();
|
||||
});
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ process.env.NODE_ENV = process.env.NODE_ENV || 'development';
|
|||
|
||||
var assert = require('assert'),
|
||||
config = require('../../config/config'),
|
||||
BlockExtractor = require('../../lib/BlockExtractor').class(),
|
||||
BlockExtractor = require('../../lib/BlockExtractor'),
|
||||
networks = require('bitcore/networks'),
|
||||
util = require('bitcore/util/util');
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ var START_TS = 1;
|
|||
var END_TS = '1296688928~'; // 2/2/2011 23:23PM
|
||||
|
||||
var assert = require('assert'),
|
||||
BlockDb = require('../../lib/BlockDb').class();
|
||||
BlockDb = require('../../lib/BlockDb').default();
|
||||
|
||||
var bDb;
|
||||
|
||||
|
@ -17,7 +17,7 @@ describe('BlockDb getBlocksByDate', function(){
|
|||
|
||||
|
||||
before(function(c) {
|
||||
bDb = new BlockDb();
|
||||
bDb = BlockDb;
|
||||
return c();
|
||||
});
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
'use strict';
|
||||
|
||||
var BlockDb = require('../../lib/BlockDb').class();
|
||||
var BlockDb = require('../../lib/BlockDb').default();
|
||||
var height_needed = 180000;
|
||||
var bDb = new BlockDb();
|
||||
var bDb = BlockDb;
|
||||
|
||||
var expect = require('chai').expect;
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
process.env.NODE_ENV = process.env.NODE_ENV || 'development';
|
||||
|
||||
var assert = require('assert'),
|
||||
Status = require('../../app/models/Status').class();
|
||||
Status = require('../../app/models/Status');
|
||||
|
||||
describe('Status', function(){
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ var chai = require('chai'),
|
|||
expect = chai.expect,
|
||||
sinon = require('sinon');
|
||||
|
||||
var PeerSync = require('../../lib/PeerSync.js').class();
|
||||
var PeerSync = require('../../lib/PeerSync.js');
|
||||
describe('PeerSync', function() {
|
||||
var ps;
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
process.env.NODE_ENV = process.env.NODE_ENV || 'development';
|
||||
|
||||
var PeerSync = require('../lib/PeerSync').class();
|
||||
var PeerSync = require('../lib/PeerSync');
|
||||
|
||||
var PROGRAM_VERSION = '0.1';
|
||||
var program = require('commander');
|
||||
|
|
|
@ -7,7 +7,7 @@ process.env.NODE_ENV = process.env.NODE_ENV || 'development';
|
|||
|
||||
var SYNC_VERSION = '0.1';
|
||||
var program = require('commander');
|
||||
var HistoricSync = require('../lib/HistoricSync').class();
|
||||
var HistoricSync = require('../lib/HistoricSync');
|
||||
var async = require('async');
|
||||
|
||||
program
|
||||
|
|
Loading…
Reference in New Issue