2014-02-03 18:30:46 -08:00
|
|
|
'use strict';
|
|
|
|
|
|
|
|
require('classtool');
|
|
|
|
|
|
|
|
|
2014-02-05 08:02:51 -08:00
|
|
|
function spec(b) {
|
2014-02-03 18:30:46 -08:00
|
|
|
|
2014-02-04 14:55:58 -08:00
|
|
|
var TIMESTAMP_ROOT = 'b-ts-'; // b-ts-<ts> => <hash>
|
|
|
|
var PREV_ROOT = 'b-prev-'; // b-prev-<hash> => <prev_hash> (0 if orphan)
|
2014-02-03 18:30:46 -08:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Module dependencies.
|
|
|
|
*/
|
|
|
|
var RpcClient = require('bitcore/RpcClient').class(),
|
|
|
|
util = require('bitcore/util/util'),
|
|
|
|
levelup = require('levelup'),
|
|
|
|
BitcoreBlock= require('bitcore/Block').class(),
|
2014-02-04 08:06:05 -08:00
|
|
|
config = require('../config/config');
|
2014-02-05 08:02:51 -08:00
|
|
|
var db = b.db || levelup(config.leveldb + '/blocks');
|
2014-02-03 18:30:46 -08:00
|
|
|
|
2014-02-05 08:02:51 -08:00
|
|
|
|
|
|
|
var BlockDb = function() {
|
2014-02-05 06:23:41 -08:00
|
|
|
};
|
|
|
|
|
|
|
|
BlockDb.prototype.close = function(cb) {
|
2014-02-05 08:02:51 -08:00
|
|
|
db.close(cb);
|
2014-02-03 18:30:46 -08:00
|
|
|
};
|
|
|
|
|
|
|
|
BlockDb.prototype.drop = function(cb) {
|
|
|
|
var path = config.leveldb + '/blocks';
|
2014-02-05 08:02:51 -08:00
|
|
|
db.close(function() {
|
2014-02-04 08:06:05 -08:00
|
|
|
require('leveldown').destroy(path, function () {
|
2014-02-05 08:02:51 -08:00
|
|
|
db = levelup(path);
|
2014-02-04 08:06:05 -08:00
|
|
|
return cb();
|
|
|
|
});
|
2014-02-03 18:30:46 -08:00
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
BlockDb.prototype.add = function(b, cb) {
|
|
|
|
if (!b.hash) return cb(new Error('no Hash at Block.save'));
|
|
|
|
|
|
|
|
|
|
|
|
var time_key = TIMESTAMP_ROOT +
|
2014-02-05 07:31:38 -08:00
|
|
|
( b.time || Math.round(new Date().getTime() / 1000) );
|
2014-02-03 18:30:46 -08:00
|
|
|
|
2014-02-05 08:02:51 -08:00
|
|
|
db.batch()
|
2014-02-03 18:30:46 -08:00
|
|
|
.put(time_key, b.hash)
|
2014-02-04 18:50:18 -08:00
|
|
|
.put(PREV_ROOT + b.hash, b.previousblockhash)
|
2014-02-03 18:30:46 -08:00
|
|
|
.write(cb);
|
|
|
|
};
|
|
|
|
|
2014-02-03 22:22:58 -08:00
|
|
|
|
2014-02-04 14:55:58 -08:00
|
|
|
|
2014-02-03 22:22:58 -08:00
|
|
|
BlockDb.prototype.setOrphan = function(hash, cb) {
|
|
|
|
var k = PREV_ROOT + hash;
|
2014-02-04 08:06:05 -08:00
|
|
|
|
2014-02-05 08:02:51 -08:00
|
|
|
db.get(k, function (err,oldPrevHash) {
|
2014-02-03 22:22:58 -08:00
|
|
|
if (err || !oldPrevHash) return cb(err);
|
2014-02-05 08:02:51 -08:00
|
|
|
db.put(PREV_ROOT + hash, 0, function() {
|
2014-02-03 22:22:58 -08:00
|
|
|
return cb(err, oldPrevHash);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
// We keep the block in TIMESTAMP_ROOT
|
|
|
|
};
|
|
|
|
|
2014-02-04 08:06:05 -08:00
|
|
|
//mainly for testing
|
|
|
|
BlockDb.prototype.setPrev = function(hash, prevHash, cb) {
|
2014-02-05 08:02:51 -08:00
|
|
|
db.put(PREV_ROOT + hash, prevHash, function(err) {
|
2014-02-04 08:06:05 -08:00
|
|
|
return cb(err);
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
//mainly for testing
|
|
|
|
BlockDb.prototype.getPrev = function(hash, cb) {
|
2014-02-05 08:02:51 -08:00
|
|
|
db.get(PREV_ROOT + hash, function(err,val) {
|
2014-02-04 08:06:05 -08:00
|
|
|
return cb(err,val);
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
BlockDb.prototype.countNotOrphan = function(cb) {
|
2014-02-03 18:30:46 -08:00
|
|
|
var c = 0;
|
2014-02-05 08:02:51 -08:00
|
|
|
console.log('Counting connected blocks. This could take some minutes');
|
|
|
|
db.createReadStream({start: PREV_ROOT, end: PREV_ROOT + '~' })
|
2014-02-03 18:30:46 -08:00
|
|
|
.on('data', function (data) {
|
2014-02-03 22:22:58 -08:00
|
|
|
if (data.value !== 0) c++;
|
2014-02-03 18:30:46 -08:00
|
|
|
})
|
|
|
|
.on('error', function (err) {
|
|
|
|
return cb(err);
|
|
|
|
})
|
|
|
|
.on('end', function () {
|
2014-02-04 18:50:18 -08:00
|
|
|
return cb(null, c);
|
2014-02-03 18:30:46 -08:00
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
BlockDb.prototype.has = function(hash, cb) {
|
2014-02-03 22:22:58 -08:00
|
|
|
var k = PREV_ROOT + hash;
|
2014-02-05 08:02:51 -08:00
|
|
|
db.get(k, function (err,val) {
|
2014-02-03 18:30:46 -08:00
|
|
|
var ret;
|
|
|
|
if (err && err.notFound) {
|
|
|
|
err = null;
|
|
|
|
ret = false;
|
|
|
|
}
|
|
|
|
if (typeof val !== 'undefined') {
|
|
|
|
ret = true;
|
|
|
|
}
|
|
|
|
return cb(err, ret);
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
BlockDb.prototype.fromHashWithInfo = function(hash, cb) {
|
|
|
|
var rpc = new RpcClient(config.bitcoind);
|
|
|
|
|
|
|
|
rpc.getBlock(hash, function(err, info) {
|
|
|
|
// Not found?
|
|
|
|
if (err && err.code === -5) return cb();
|
|
|
|
if (err) return cb(err);
|
|
|
|
|
|
|
|
info.reward = BitcoreBlock.getBlockValue(info.height) / util.COIN ;
|
|
|
|
|
|
|
|
return cb(null, {
|
|
|
|
hash: hash,
|
|
|
|
info: info.result,
|
|
|
|
});
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2014-02-05 09:17:19 -08:00
|
|
|
BlockDb.prototype.getBlocksByDate = function(start_ts, end_ts, limit, cb) {
|
2014-02-04 12:22:51 -08:00
|
|
|
var list = [];
|
2014-02-05 08:02:51 -08:00
|
|
|
db.createReadStream({
|
2014-02-04 12:22:51 -08:00
|
|
|
start: TIMESTAMP_ROOT + start_ts,
|
2014-02-05 09:17:19 -08:00
|
|
|
end: TIMESTAMP_ROOT + end_ts,
|
|
|
|
limit: limit
|
2014-02-05 08:02:51 -08:00
|
|
|
})
|
2014-02-04 12:22:51 -08:00
|
|
|
.on('data', function (data) {
|
|
|
|
list.push({
|
|
|
|
ts: data.key.replace(TIMESTAMP_ROOT, ''),
|
|
|
|
hash: data.value,
|
|
|
|
});
|
|
|
|
})
|
|
|
|
.on('error', function (err) {
|
|
|
|
return cb(err);
|
|
|
|
})
|
|
|
|
.on('end', function () {
|
|
|
|
return cb(null, list);
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2014-02-03 18:30:46 -08:00
|
|
|
BlockDb.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 });
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2014-02-05 05:16:40 -08:00
|
|
|
return BlockDb;
|
2014-02-03 18:30:46 -08:00
|
|
|
}
|
|
|
|
module.defineClass(spec);
|
|
|
|
|
|
|
|
|