refactor Rpc classes. add file index in DB

This commit is contained in:
Matias Alejo Garcia 2014-02-13 16:14:22 -03:00
parent 5cd43cbcaa
commit ab808e5c2b
6 changed files with 209 additions and 135 deletions

View File

@ -5,23 +5,21 @@ require('classtool');
function spec(b) {
var TIMESTAMP_PREFIX = 'bts-'; // b-ts-<ts> => <hash>
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 TIP = 'bti-'; // last block on the chain
var LAST_FILE_INDEX = 'file-'; // last processed file index
/**
* Module dependencies.
*/
var RpcClient = require('bitcore/RpcClient').class(),
util = require('bitcore/util/util'),
levelup = require('levelup'),
BitcoreBlock= require('bitcore/Block').class(),
var levelup = require('levelup'),
config = require('../config/config');
var db = b.db || levelup(config.leveldb + '/blocks');
var rpc = b.rpc || new RpcClient(config.bitcoind);
var db = b.db || levelup(config.leveldb + '/blocks');
var Rpc = b.rpc || require('./Rpc').class();
var BlockDb = function() {
};
@ -80,6 +78,24 @@ function spec(b) {
});
};
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;}
@ -138,13 +154,8 @@ function spec(b) {
BlockDb.prototype.fromHashWithInfo = function(hash, cb) {
var self = this;
rpc.getBlock(hash, function(err, info) {
// Not found?
if (err && err.code === -5) return cb();
if (err) return cb(err);
if (info.result.height)
info.result.reward = BitcoreBlock.getBlockValue(info.result.height) / util.COIN ;
Rpc.getBlock(hash, function(err, info) {
if (err || !info) return cb(err);
self.isMain(hash, function(err, val) {
if (err) return cb(err);
@ -182,12 +193,7 @@ function spec(b) {
};
BlockDb.prototype.blockIndex = function(height, cb) {
var rpc = new RpcClient(config.bitcoind);
rpc.getBlockHash(height, function(err, bh){
if (err) return cb(err);
cb(null, { blockHash: bh.result });
});
return Rpc.blockIndex(height,cb);
};
return BlockDb;

View File

@ -123,10 +123,9 @@ function spec() {
sockets.broadcastSyncInfo(self.info());
}
//TODO
// if (self.syncPercentage > 10) {
// process.exit(-1);
// }
// if (self.syncPercentage > 10) {
// process.exit(-1);
// }
};
HistoricSync.prototype.getPrevNextBlock = function(blockHash, blockEnd, scanOpts, cb) {
@ -277,19 +276,24 @@ function spec() {
}
self.sync.storeTipBlock(blockInfo, function(err) {
if (blockInfo && blockInfo.hash) {
self.syncedBlocks++;
} else
self.status = 'finished';
if (err) {
self.setError(util.format('ERROR: @%s: %s [count: syncedBlocks: %d]',
blockInfo ? blockInfo.hash : '-', err, self.syncedBlocks));
return cb(err);
}
return cb(err);
self.sync.bDb.setLastFileIndex(self.blockExtractor.currentFileIndex, function(err) {
if (err) return cb(err);
if (blockInfo && blockInfo.hash) {
self.syncedBlocks++;
} else
self.status = 'finished';
return cb(err);
});
});
});
};
@ -307,7 +311,7 @@ function spec() {
};
HistoricSync.prototype.getBlockCount = function(cb) {
HistoricSync.prototype.updateBlockCount = function(cb) {
var self = this;
if (self.blockChainHeight) return cb();
@ -333,8 +337,9 @@ function spec() {
}
return cb();
},
// We are not using getBestBlockHash, because is not available in all clients
function (cb) { return self.getBlockCount(cb); },
function (cb) {
return self.updateBlockCount(cb);
},
function(cb) {
if (!scanOpts.reverse) return cb();
self.rpc.getBlockHash(self.blockChainHeight, function(err, res) {
@ -372,8 +377,6 @@ function spec() {
self.setError(err);
return next(err, 0);
}
// SETUP Sync params
var start, end;
@ -432,42 +435,66 @@ function spec() {
});
};
// upto if we have genesis block?
HistoricSync.prototype.smartImport = function(scanOpts, next) {
var self = this;
self.sync.bDb.has(self.genesis, function(err, b) {
if (err) return next(err);
self.countNotOrphan(function(err, count) {
if (err) return next(err);
self.getBlockCount(function(err) {
if (err) return next(err);
if (!b || scanOpts.destroy || count < self.blockChainHeight * 0.8 ) {
if (!b)
p('Could not find Genesis block. Running FULL SYNC');
else
p('Less that 80% of current blockchain is stored. Running FULL SYNC',
parseInt(count/self.blockChainHeight*100));
if (config.bitcoind.dataDir) {
p('bitcoind dataDir configured...importing blocks from .dat files');
scanOpts.fromFiles = true;
self.blockExtractor = new BlockExtractor(config.bitcoind.dataDir, config.network);
}
else {
scanOpts.reverse = true;
}
}
else {
p('Genesis block found. Syncing upto old TIP.');
p('Got ' + count + ' out of ' + self.blockChainHeight + ' blocks');
scanOpts.reverse = true;
}
return self.importHistory(scanOpts, next);
var genesis, count;
async.series([
function(s_c) {
self.sync.bDb.has(self.genesis, function(err, b) {
genesis = b;
return s_c(err);
});
});
},
function(s_c) {
if (scanOpts.destroy) return s_c();
self.countNotOrphan(function(err, c) {
count = c;
return s_c(err);
});
},
function(s_c) {
if (!config.bitcoind.dataDir) return s_c();
self.sync.bDb.getLastFileIndex(function(err, idx) {
self.blockExtractor = new BlockExtractor(config.bitcoind.dataDir, config.network);
if (idx) self.blockExtractor.currentFileIndex = idx;
return s_c(err);
});
},
function(s_c) {
self.updateBlockCount(s_c);
}],
function(err) {
if (err) return next(err);
if (!genesis || scanOpts.destroy || count < self.blockChainHeight * 0.9 ) {
// Full sync.
if (!genesis)
p('Could not find Genesis block. Running FULL SYNC');
else
p('Less that 90% of current blockchain is stored. Running FULL SYNC',
parseInt(count/self.blockChainHeight*100));
if (config.bitcoind.dataDir) {
p('bitcoind dataDir configured...importing blocks from .dat files');
p('Starting from file: ' + self.blockExtractor.currentFileIndex);
scanOpts.fromFiles = true;
}
else {
scanOpts.reverse = true;
}
}
else {
p('Genesis block found. Syncing upto old TIP.');
p('Got ' + count + ' out of ' + self.blockChainHeight + ' blocks');
scanOpts.reverse = true;
}
return self.importHistory(scanOpts, next);
});
};

103
lib/Rpc.js Normal file
View File

@ -0,0 +1,103 @@
'use strict';
require('classtool');
function spec(b) {
var RpcClient = require('bitcore/RpcClient').class(),
BitcoreTransaction = require('bitcore/Transaction').class(),
BitcoreBlock = require('bitcore/Block').class(),
bitcoreUtil = require('bitcore/util/util'),
util = require('util'),
config = require('../config/config');
var bitcoreRpc = b.bitcoreRpc || new RpcClient(config.bitcoind);
function Rpc() {
}
Rpc._parseRpcResult = function(info) {
var b = new Buffer(info.hex,'hex');
var tx = new BitcoreTransaction();
tx.parse(b);
// Inputs
if (tx.isCoinBase()) {
info.isCoinBase = true;
}
var n =0;
info.vin.forEach(function(i) {
i.n = n++;
});
// Outputs
var valueOutSat = 0;
info.vout.forEach( function(o) {
valueOutSat += o.value * bitcoreUtil.COIN;
});
info.valueOut = parseInt(valueOutSat) / bitcoreUtil.COIN;
info.size = b.length;
return 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.getRpcInfo = function(txid, 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 = self._parseRpcResult(txInfo.result);
return cb(null,info);
});
};
Rpc.blockIndex = function(height, cb) {
var self = this;
bitcoreRpc.getBlockHash(height, function(err, bh){
if (err) return cb(self.errMsg(err));
cb(null, { blockHash: bh.result });
});
};
Rpc.getBlock = function(hash, cb) {
var self = this;
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) / util.COIN ;
return cb(err,info);
});
};
return Rpc;
}
module.defineClass(spec);

View File

@ -143,7 +143,7 @@ function spec() {
function(err) {
if (!err) self._handleBroadcast(b.hash, null, updatedAddrs);
if (err && err.toString().match(/WARN/) ) {
console.log(err);
//console.log(err);
err=null;
}
return cb(err);

View File

@ -25,7 +25,7 @@ function spec(b) {
/**
* Module dependencies.
*/
var TransactionRpc = require('./TransactionRpc').class(),
var Rpc = b.rpc || require('./Rpc').class(),
util = require('bitcore/util/util'),
levelup = require('levelup'),
async = require('async'),
@ -224,7 +224,7 @@ function spec(b) {
TransactionDb.prototype._getInfo = function(txid, next) {
var self = this;
TransactionRpc.getRpcInfo(txid, function(err, info) {
Rpc.getRpcInfo(txid, function(err, info) {
if (err) return next(err);
self._fillOutpoints(info, function() {
@ -573,7 +573,7 @@ function spec(b) {
// TODO: parse it from networks.genesisTX?
if (t === genesisTXID) return each_cb();
TransactionRpc.getRpcInfo(t, function(err, inInfo) {
Rpc.getRpcInfo(t, function(err, inInfo) {
if (!inInfo) return each_cb(err);
return self.add(inInfo, blockHash, each_cb);

View File

@ -1,62 +0,0 @@
'use strict';
require('classtool');
function spec(b) {
var RpcClient = require('bitcore/RpcClient').class(),
BitcoreTransaction = require('bitcore/Transaction').class(),
util = require('bitcore/util/util'),
config = require('../config/config');
var rpc = b.rpc || new RpcClient(config.bitcoind);
function TransactionRpc() {
}
TransactionRpc._parseRpcResult = function(info) {
var b = new Buffer(info.hex,'hex');
var tx = new BitcoreTransaction();
tx.parse(b);
// Inputs
if (tx.isCoinBase()) {
info.isCoinBase = true;
}
var n =0;
info.vin.forEach(function(i) {
i.n = n++;
});
// Outputs
var valueOutSat = 0;
info.vout.forEach( function(o) {
valueOutSat += o.value * util.COIN;
});
info.valueOut = parseInt(valueOutSat) / util.COIN;
info.size = b.length;
return info;
};
TransactionRpc.getRpcInfo = function(txid, cb) {
var Self = this;
rpc.getRawTransaction(txid, 1, function(err, txInfo) {
// Not found?
if (err && err.code === -5) return cb();
if (err) return cb(err);
var info = Self._parseRpcResult(txInfo.result);
return cb(null,info);
});
};
return TransactionRpc;
}
module.defineClass(spec);