Merge pull request #13 from matiu/feature/use-soop

Feature/use soop
This commit is contained in:
Mario Colque 2014-03-10 16:42:47 -02:00
commit 05d8b7c76a
29 changed files with 2109 additions and 2189 deletions

View File

@ -5,7 +5,7 @@
*/
var common = require('./common'),
async = require('async'),
BlockDb = require('../../lib/BlockDb').class();
BlockDb = require('../../lib/BlockDb');
var bdb = new BlockDb();

View File

@ -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)

View File

@ -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) {

View File

@ -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);

View File

@ -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);

View File

@ -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');

View File

@ -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';

View File

@ -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

View File

@ -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');

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

File diff suppressed because it is too large Load Diff

View File

@ -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"
},

View File

@ -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();
});

View File

@ -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),

View File

@ -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();
});

View File

@ -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();
});

View File

@ -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');

View File

@ -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();
});

View File

@ -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;

View File

@ -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(){

View File

@ -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;

View File

@ -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');

View File

@ -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