use tiny to cache addresses and start where we left off.

This commit is contained in:
Christopher Jeffrey 2014-12-01 22:05:26 -08:00
parent 144cb40195
commit 774f08c91f
3 changed files with 134 additions and 34 deletions

View File

@ -417,36 +417,78 @@ Bitcoin.prototype.getMiningInfo = function() {
return bitcoindjs.getMiningInfo(); return bitcoindjs.getMiningInfo();
}; };
Bitcoin._addrCache = {};
Bitcoin._collectAddrGarbage = null;
Bitcoin.prototype.getAddrTransactions = function(addr, callback) { Bitcoin.prototype.getAddrTransactions = function(addr, callback) {
if (!Bitcoin._collectAddrGarbage) { if (!bitcoin.db) {
Bitcoin._collectAddrGarbage = setInterval(function() { var tiny = require('tiny').json;
Bitcoin._addrCache = {}; bitcoin.db = tiny({
}, 20 * 60 * 1000); file: process.env.HOME + '/.bitcoindjs.addr.db',
Bitcoin._collectAddrGarbage.unref(); saveIndex: false,
} initialCache: false
var cached = Bitcoin._addrCache[addr];
if (cached && Date.now() <= (cached.time + 10 * 60 * 1000)) {
setImmediate(function() {
return callback(null, cached.addr);
}); });
return;
} }
return bitcoindjs.getAddrTransactions(addr, function(err, addr) { return bitcoin.db.get(addr, function(err, records) {
if (err) return callback(err); var found = !err && records && records.length;
addr = bitcoin.addr(addr); var last = found && records[records.length - 1];
if (addr.tx[0] && !addr.tx[0].vout[0]) { var limit = 15 * 60 * 1000;
return callback(null, bitcoin.addr({ if (!found || last.timestamp + limit < Date.now()) {
address: addr.address, var options = {
tx: [] address: addr,
})); blockindex: (records || []).reduce(function(out, record) {
return record.blockindex > out
? record.blockindex
: out;
}, -1)
};
return bitcoindjs.getAddrTransactions(options, function(err, addr) {
if (err) return callback(err);
addr = bitcoin.addr(addr);
if (addr.tx[0] && !addr.tx[0].vout[0]) {
return bitcoin.db.set(addr, [{
txid: null,
blockhash: null,
blockindex: null,
timestamp: Date.now()
}], function() {
return callback(null, bitcoin.addr({
address: addr.address,
tx: []
}));
});
}
var set = [];
if (records && records.length) {
set = records;
}
addr.tx.forEach(function(tx) {
set.push({
txid: tx.txid,
blockhash: tx.blockhash,
blockindex: tx.blockindex,
timestamp: Date.now()
});
});
return bitcoin.db.set(addr, set, function() {
return callback(null, addr);
});
});
} }
Bitcoin._addrCache[addr.address] = { var txs = [];
addr: addr, return utils.forEach(records, function(record, next) {
time: Date.now() var block = record.block;
}; var txid = record.txid;
return callback(null, addr); var index = record.blockindex;
if (txid == null) return next();
return bitcoin.getTransaction(txid, block, function(err, tx) {
if (err) return next();
txs.push(tx);
return next();
});
}, function() {
return callback(null, bitcoin.addr({
address: addr,
tx: txs
}));
});
}); });
}; };
@ -455,6 +497,10 @@ Bitcoin.prototype.getBestBlock = function(callback) {
return bitcoindjs.getBlock(hash, callback); return bitcoindjs.getBlock(hash, callback);
}; };
Bitcoin.prototype.getChainHeight = function() {
return bitcoindjs.getChainHeight();
};
Bitcoin.prototype.log = Bitcoin.prototype.log =
Bitcoin.prototype.info = function() { Bitcoin.prototype.info = function() {
if (this.options.silent) return; if (this.options.silent) return;

View File

@ -18,7 +18,8 @@
], ],
"dependencies": { "dependencies": {
"nan": "1.3.0", "nan": "1.3.0",
"mkdirp": "0.5.0" "mkdirp": "0.5.0",
"tiny": "0.0.10"
}, },
"devDependencies": { "devDependencies": {
"mocha": "~1.16.2", "mocha": "~1.16.2",

View File

@ -212,6 +212,7 @@ NAN_METHOD(GetGenerate);
NAN_METHOD(GetMiningInfo); NAN_METHOD(GetMiningInfo);
NAN_METHOD(GetAddrTransactions); NAN_METHOD(GetAddrTransactions);
NAN_METHOD(GetBestBlock); NAN_METHOD(GetBestBlock);
NAN_METHOD(GetChainHeight);
NAN_METHOD(GetBlockHex); NAN_METHOD(GetBlockHex);
NAN_METHOD(GetTxHex); NAN_METHOD(GetTxHex);
@ -473,6 +474,7 @@ struct async_addrtx_data {
std::string err_msg; std::string err_msg;
std::string addr; std::string addr;
ctx_list *ctxs; ctx_list *ctxs;
int64_t blockindex;
Persistent<Function> callback; Persistent<Function> callback;
}; };
@ -1966,24 +1968,46 @@ NAN_METHOD(GetAddrTransactions) {
NanScope(); NanScope();
if (args.Length() < 2 if (args.Length() < 2
|| !args[0]->IsString() || (!args[0]->IsString() && !args[0]->IsObject())
|| !args[1]->IsFunction()) { || !args[1]->IsFunction()) {
return NanThrowError( return NanThrowError(
"Usage: bitcoindjs.getAddrTransactions(addr, callback)"); "Usage: bitcoindjs.getAddrTransactions(addr, callback)");
} }
String::Utf8Value addr_(args[0]->ToString()); std::string addr = "";
int64_t blockindex = -1;
if (args[0]->IsString()) {
String::Utf8Value addr_(args[0]->ToString());
addr = std::string(*addr_);
} else if (args[0]->IsObject()) {
Local<Object> options = Local<Object>::Cast(args[0]);
if (options->Get(NanNew<String>("address"))->IsString()) {
String::Utf8Value s_(options->Get(NanNew<String>("address"))->ToString());
addr = std::string(*s_);
}
if (options->Get(NanNew<String>("addr"))->IsString()) {
String::Utf8Value s_(options->Get(NanNew<String>("addr"))->ToString());
addr = std::string(*s_);
}
if (options->Get(NanNew<String>("index"))->IsString()) {
blockindex = options->Get(NanNew<String>("index"))->IntegerValue();
}
if (options->Get(NanNew<String>("blockindex"))->IsString()) {
blockindex = options->Get(NanNew<String>("blockindex"))->IntegerValue();
}
}
Local<Function> callback = Local<Function>::Cast(args[1]); Local<Function> callback = Local<Function>::Cast(args[1]);
Persistent<Function> cb; Persistent<Function> cb;
cb = Persistent<Function>::New(callback); cb = Persistent<Function>::New(callback);
std::string addr = std::string(*addr_);
async_addrtx_data *data = new async_addrtx_data(); async_addrtx_data *data = new async_addrtx_data();
data->err_msg = std::string(""); data->err_msg = std::string("");
data->addr = addr; data->addr = addr;
data->ctxs = NULL; data->ctxs = NULL;
data->blockindex = blockindex;
data->callback = Persistent<Function>::New(callback); data->callback = Persistent<Function>::New(callback);
uv_work_t *req = new uv_work_t(); uv_work_t *req = new uv_work_t();
@ -2002,6 +2026,11 @@ static void
async_get_addrtx(uv_work_t *req) { async_get_addrtx(uv_work_t *req) {
async_addrtx_data* data = static_cast<async_addrtx_data*>(req->data); async_addrtx_data* data = static_cast<async_addrtx_data*>(req->data);
if (data->addr.empty()) {
data->err_msg = std::string("Invalid address.");
return;
}
CBitcoinAddress address = CBitcoinAddress(data->addr); CBitcoinAddress address = CBitcoinAddress(data->addr);
if (!address.IsValid()) { if (!address.IsValid()) {
data->err_msg = std::string("Invalid address."); data->err_msg = std::string("Invalid address.");
@ -2011,10 +2040,16 @@ async_get_addrtx(uv_work_t *req) {
#if !USE_LDB_ADDR #if !USE_LDB_ADDR
CScript expected = GetScriptForDestination(address.Get()); CScript expected = GetScriptForDestination(address.Get());
// int64_t i = 0; int64_t i = 0;
// Check the last 20,000 blocks // Check the last 20,000 blocks
int64_t i = chainActive.Height() - 20000; // int64_t i = chainActive.Height() - 20000;
if (i < 0) i = 0; // if (i < 0) i = 0;
if (data->blockindex != -1) {
i = data->blockindex;
}
int64_t height = chainActive.Height(); int64_t height = chainActive.Height();
for (; i <= height; i++) { for (; i <= height; i++) {
@ -2152,6 +2187,23 @@ NAN_METHOD(GetBestBlock) {
NanReturnValue(NanNew<String>(hash.GetHex())); NanReturnValue(NanNew<String>(hash.GetHex()));
} }
/**
* GetChainHeight()
* bitcoindjs.getChainHeight()
* Get miscellaneous information
*/
NAN_METHOD(GetChainHeight) {
NanScope();
if (args.Length() > 0) {
return NanThrowError(
"Usage: bitcoindjs.getChainHeight()");
}
NanReturnValue(NanNew<Number>((int)chainActive.Height())->ToInt32());
}
/** /**
* GetBlockHex() * GetBlockHex()
* bitcoindjs.getBlockHex(callback) * bitcoindjs.getBlockHex(callback)
@ -6179,6 +6231,7 @@ init(Handle<Object> target) {
NODE_SET_METHOD(target, "getMiningInfo", GetMiningInfo); NODE_SET_METHOD(target, "getMiningInfo", GetMiningInfo);
NODE_SET_METHOD(target, "getAddrTransactions", GetAddrTransactions); NODE_SET_METHOD(target, "getAddrTransactions", GetAddrTransactions);
NODE_SET_METHOD(target, "getBestBlock", GetBestBlock); NODE_SET_METHOD(target, "getBestBlock", GetBestBlock);
NODE_SET_METHOD(target, "getChainHeight", GetChainHeight);
NODE_SET_METHOD(target, "getBlockHex", GetBlockHex); NODE_SET_METHOD(target, "getBlockHex", GetBlockHex);
NODE_SET_METHOD(target, "getTxHex", GetTxHex); NODE_SET_METHOD(target, "getTxHex", GetTxHex);
NODE_SET_METHOD(target, "blockFromHex", BlockFromHex); NODE_SET_METHOD(target, "blockFromHex", BlockFromHex);