diff --git a/lib/bitcoind.js b/lib/bitcoind.js index 28526616..d2c82508 100644 --- a/lib/bitcoind.js +++ b/lib/bitcoind.js @@ -486,6 +486,10 @@ Bitcoin.prototype.getBlockByTx = function(txid, callback) { return bitcoindjs.getBlockByTx(txid, callback); }; +Bitcoin.prototype.getBlockByTime = function(options, callback) { + return bitcoindjs.getBlockByTime(options, callback); +}; + Bitcoin.prototype.log = Bitcoin.prototype.info = function() { if (this.options.silent) return; diff --git a/src/bitcoindjs.cc b/src/bitcoindjs.cc index b3d8039f..0eefeb86 100644 --- a/src/bitcoindjs.cc +++ b/src/bitcoindjs.cc @@ -214,6 +214,7 @@ NAN_METHOD(GetAddrTransactions); NAN_METHOD(GetBestBlock); NAN_METHOD(GetChainHeight); NAN_METHOD(GetBlockByTx); +NAN_METHOD(GetBlockByTime); NAN_METHOD(GetBlockHex); NAN_METHOD(GetTxHex); @@ -351,6 +352,12 @@ async_block_tx(uv_work_t *req); static void async_block_tx_after(uv_work_t *req); +static void +async_block_time(uv_work_t *req); + +static void +async_block_time_after(uv_work_t *req); + static inline void cblock_to_jsblock(const CBlock& cblock, CBlockIndex* cblock_index, Local jsblock, bool is_new); @@ -464,6 +471,19 @@ struct async_block_tx_data { Persistent callback; }; +/** + * async_block_time_data + */ + +struct async_block_time_data { + std::string err_msg; + uint32_t gte; + uint32_t lte; + CBlock cblock; + CBlockIndex* cblock_index; + Persistent callback; +}; + /** * async_addrtx_data */ @@ -2221,6 +2241,110 @@ async_block_tx_after(uv_work_t *req) { delete req; } +/** + * GetBlockByTime() + * bitcoindjs.getBlockByTime() + * Get block by tx hash (requires -txindex or it's very slow) + */ + +NAN_METHOD(GetBlockByTime) { + NanScope(); + + if (args.Length() < 2 + || !args[0]->IsString() + || !args[1]->IsFunction()) { + return NanThrowError( + "Usage: bitcoindjs.getBlockByTime(options, callback)"); + } + + async_block_time_data *data = new async_block_time_data(); + + uv_work_t *req = new uv_work_t(); + req->data = data; + + Local options = Local::Cast(args[0]); + if (options->Get(NanNew("gte"))->IsNumber()) { + data->gte = options->Get(NanNew("gte"))->ToUint32(); + } + if (options->Get(NanNew("lte"))->IsNumber()) { + data->lte = options->Get(NanNew("lte"))->ToUint32(); + } + data->err_msg = std::string(""); + + Local callback = Local::Cast(args[1]); + data->callback = Persistent::New(callback); + + int status = uv_queue_work(uv_default_loop(), + req, async_block_time, + (uv_after_work_cb)async_block_time_after); + + assert(status == 0); + + NanReturnValue(Undefined()); +} + +static void +async_block_time(uv_work_t *req) { + async_block_time_data* data = static_cast(req->data); + CBlock cblock; + CBlockIndex *cblock_index; + int64_t i = 0; + // XXX Slow: figure out how to ballpark the height based on gte and lte. + int64_t height = chainActive.Height(); + for (; i <= height; i++) { + CBlockIndex* pblockindex = chainActive[i]; + CBlock cblock; + if (ReadBlockFromDisk(cblock, pblockindex)) { + uint32_t blocktime = cblock.GetBlockTime(); + if (blocktime >= data->gte && blocktime <= data->lte) { + data->cblock = cblock; + data->cblock_index = pblockindex; + return; + } + } + } + data->err_msg = std::string("Block not found."); +} + +static void +async_block_time_after(uv_work_t *req) { + NanScope(); + async_block_time_data* data = static_cast(req->data); + + if (data->err_msg != "") { + Local err = Exception::Error(NanNew(data->err_msg)); + const unsigned argc = 1; + Local argv[argc] = { err }; + TryCatch try_catch; + data->callback->Call(Context::GetCurrent()->Global(), argc, argv); + if (try_catch.HasCaught()) { + node::FatalException(try_catch); + } + } else { + const CBlock& cblock = data->cblock; + CBlockIndex* cblock_index = data->cblock_index; + + Local jsblock = NanNew(); + cblock_to_jsblock(cblock, cblock_index, jsblock, false); + + const unsigned argc = 2; + Local argv[argc] = { + Local::New(Null()), + Local::New(jsblock) + }; + TryCatch try_catch; + data->callback->Call(Context::GetCurrent()->Global(), argc, argv); + if (try_catch.HasCaught()) { + node::FatalException(try_catch); + } + } + + data->callback.Dispose(); + + delete data; + delete req; +} + /** * GetBlockHex() * bitcoindjs.getBlockHex(callback) @@ -6312,6 +6436,7 @@ init(Handle target) { NODE_SET_METHOD(target, "getBestBlock", GetBestBlock); NODE_SET_METHOD(target, "getChainHeight", GetChainHeight); NODE_SET_METHOD(target, "getBlockByTx", GetBlockByTx); + NODE_SET_METHOD(target, "getBlockByTime", GetBlockByTime); NODE_SET_METHOD(target, "getBlockHex", GetBlockHex); NODE_SET_METHOD(target, "getTxHex", GetTxHex); NODE_SET_METHOD(target, "blockFromHex", BlockFromHex);