From 774f08c91fe23380552e8b04b59b22e4d1b56387 Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Mon, 1 Dec 2014 22:05:26 -0800 Subject: [PATCH] use tiny to cache addresses and start where we left off. --- lib/bitcoind.js | 98 ++++++++++++++++++++++++++++++++++------------- package.json | 3 +- src/bitcoindjs.cc | 67 ++++++++++++++++++++++++++++---- 3 files changed, 134 insertions(+), 34 deletions(-) diff --git a/lib/bitcoind.js b/lib/bitcoind.js index e12728dc..9f1b1939 100644 --- a/lib/bitcoind.js +++ b/lib/bitcoind.js @@ -417,36 +417,78 @@ Bitcoin.prototype.getMiningInfo = function() { return bitcoindjs.getMiningInfo(); }; -Bitcoin._addrCache = {}; -Bitcoin._collectAddrGarbage = null; Bitcoin.prototype.getAddrTransactions = function(addr, callback) { - if (!Bitcoin._collectAddrGarbage) { - Bitcoin._collectAddrGarbage = setInterval(function() { - Bitcoin._addrCache = {}; - }, 20 * 60 * 1000); - Bitcoin._collectAddrGarbage.unref(); - } - var cached = Bitcoin._addrCache[addr]; - if (cached && Date.now() <= (cached.time + 10 * 60 * 1000)) { - setImmediate(function() { - return callback(null, cached.addr); + if (!bitcoin.db) { + var tiny = require('tiny').json; + bitcoin.db = tiny({ + file: process.env.HOME + '/.bitcoindjs.addr.db', + saveIndex: false, + initialCache: false }); - return; } - return bitcoindjs.getAddrTransactions(addr, function(err, addr) { - if (err) return callback(err); - addr = bitcoin.addr(addr); - if (addr.tx[0] && !addr.tx[0].vout[0]) { - return callback(null, bitcoin.addr({ - address: addr.address, - tx: [] - })); + return bitcoin.db.get(addr, function(err, records) { + var found = !err && records && records.length; + var last = found && records[records.length - 1]; + var limit = 15 * 60 * 1000; + if (!found || last.timestamp + limit < Date.now()) { + var options = { + 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] = { - addr: addr, - time: Date.now() - }; - return callback(null, addr); + var txs = []; + return utils.forEach(records, function(record, next) { + var block = record.block; + var txid = record.txid; + 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); }; +Bitcoin.prototype.getChainHeight = function() { + return bitcoindjs.getChainHeight(); +}; + Bitcoin.prototype.log = Bitcoin.prototype.info = function() { if (this.options.silent) return; diff --git a/package.json b/package.json index 5b006146..f6029e81 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,8 @@ ], "dependencies": { "nan": "1.3.0", - "mkdirp": "0.5.0" + "mkdirp": "0.5.0", + "tiny": "0.0.10" }, "devDependencies": { "mocha": "~1.16.2", diff --git a/src/bitcoindjs.cc b/src/bitcoindjs.cc index 0b1fabfc..8c809333 100644 --- a/src/bitcoindjs.cc +++ b/src/bitcoindjs.cc @@ -212,6 +212,7 @@ NAN_METHOD(GetGenerate); NAN_METHOD(GetMiningInfo); NAN_METHOD(GetAddrTransactions); NAN_METHOD(GetBestBlock); +NAN_METHOD(GetChainHeight); NAN_METHOD(GetBlockHex); NAN_METHOD(GetTxHex); @@ -473,6 +474,7 @@ struct async_addrtx_data { std::string err_msg; std::string addr; ctx_list *ctxs; + int64_t blockindex; Persistent callback; }; @@ -1966,24 +1968,46 @@ NAN_METHOD(GetAddrTransactions) { NanScope(); if (args.Length() < 2 - || !args[0]->IsString() + || (!args[0]->IsString() && !args[0]->IsObject()) || !args[1]->IsFunction()) { return NanThrowError( "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 options = Local::Cast(args[0]); + if (options->Get(NanNew("address"))->IsString()) { + String::Utf8Value s_(options->Get(NanNew("address"))->ToString()); + addr = std::string(*s_); + } + if (options->Get(NanNew("addr"))->IsString()) { + String::Utf8Value s_(options->Get(NanNew("addr"))->ToString()); + addr = std::string(*s_); + } + if (options->Get(NanNew("index"))->IsString()) { + blockindex = options->Get(NanNew("index"))->IntegerValue(); + } + if (options->Get(NanNew("blockindex"))->IsString()) { + blockindex = options->Get(NanNew("blockindex"))->IntegerValue(); + } + } + Local callback = Local::Cast(args[1]); Persistent cb; cb = Persistent::New(callback); - std::string addr = std::string(*addr_); - async_addrtx_data *data = new async_addrtx_data(); data->err_msg = std::string(""); data->addr = addr; data->ctxs = NULL; + data->blockindex = blockindex; data->callback = Persistent::New(callback); uv_work_t *req = new uv_work_t(); @@ -2002,6 +2026,11 @@ static void async_get_addrtx(uv_work_t *req) { async_addrtx_data* data = static_cast(req->data); + if (data->addr.empty()) { + data->err_msg = std::string("Invalid address."); + return; + } + CBitcoinAddress address = CBitcoinAddress(data->addr); if (!address.IsValid()) { data->err_msg = std::string("Invalid address."); @@ -2011,10 +2040,16 @@ async_get_addrtx(uv_work_t *req) { #if !USE_LDB_ADDR CScript expected = GetScriptForDestination(address.Get()); - // int64_t i = 0; + int64_t i = 0; + // Check the last 20,000 blocks - int64_t i = chainActive.Height() - 20000; - if (i < 0) i = 0; + // int64_t i = chainActive.Height() - 20000; + // if (i < 0) i = 0; + + if (data->blockindex != -1) { + i = data->blockindex; + } + int64_t height = chainActive.Height(); for (; i <= height; i++) { @@ -2152,6 +2187,23 @@ NAN_METHOD(GetBestBlock) { NanReturnValue(NanNew(hash.GetHex())); } +/** + * GetChainHeight() + * bitcoindjs.getChainHeight() + * Get miscellaneous information + */ + +NAN_METHOD(GetChainHeight) { + NanScope(); + + if (args.Length() > 0) { + return NanThrowError( + "Usage: bitcoindjs.getChainHeight()"); + } + + NanReturnValue(NanNew((int)chainActive.Height())->ToInt32()); +} + /** * GetBlockHex() * bitcoindjs.getBlockHex(callback) @@ -6179,6 +6231,7 @@ init(Handle target) { NODE_SET_METHOD(target, "getMiningInfo", GetMiningInfo); NODE_SET_METHOD(target, "getAddrTransactions", GetAddrTransactions); NODE_SET_METHOD(target, "getBestBlock", GetBestBlock); + NODE_SET_METHOD(target, "getChainHeight", GetChainHeight); NODE_SET_METHOD(target, "getBlockHex", GetBlockHex); NODE_SET_METHOD(target, "getTxHex", GetTxHex); NODE_SET_METHOD(target, "blockFromHex", BlockFromHex);