From a4fcd348d46573a1d6c098ecac2ff47a7ac0e48c Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Fri, 19 Sep 2014 13:53:55 -0700 Subject: [PATCH] make bitcoind.getBlock asynchronous. --- example/index.js | 7 ++- lib/bitcoind.js | 7 ++- src/bitcoindjs.cc | 151 ++++++++++++++++++++++++++++++++++------------ 3 files changed, 123 insertions(+), 42 deletions(-) diff --git a/example/index.js b/example/index.js index acf286f8..35ae8008 100755 --- a/example/index.js +++ b/example/index.js @@ -10,8 +10,11 @@ bitcoind.start(function(err) { }); bitcoind.on('open', function(status) { setTimeout(function() { - var block = bitcoind.getBlock(0); - console.log(block); + var genesis = '0x000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f'; + bitcoind.getBlock(genesis, function(err, block) { + if (err) return console.log(err.message); + console.log(block); + }); }, 1000); console.log('bitcoind: status="%s"', status); }); diff --git a/lib/bitcoind.js b/lib/bitcoind.js index 5533b395..f88066af 100644 --- a/lib/bitcoind.js +++ b/lib/bitcoind.js @@ -119,8 +119,11 @@ Bitcoin.prototype.start = function(callback) { } }; -Bitcoin.prototype.getBlock = function(hash) { - return JSON.parse(bitcoindjs.getBlock(hash)); +Bitcoin.prototype.getBlock = function(hash, callback) { + return bitcoindjs.getBlock(hash, function(err, block) { + if (err) return callback(err) + return callback(null, JSON.parse(block)); + }); }; Bitcoin.prototype.log = diff --git a/src/bitcoindjs.cc b/src/bitcoindjs.cc index e8a21ce6..938108d1 100644 --- a/src/bitcoindjs.cc +++ b/src/bitcoindjs.cc @@ -140,6 +140,10 @@ using namespace node; using namespace v8; NAN_METHOD(StartBitcoind); +NAN_METHOD(IsStopping); +NAN_METHOD(IsStopped); +NAN_METHOD(StopBitcoind); +NAN_METHOD(GetBlock); static void async_start_node_work(uv_work_t *req); @@ -173,11 +177,28 @@ static void async_parse_logs_after(uv_work_t *req); #endif +static void +async_get_block(uv_work_t *req); + +static void +async_get_block_after(uv_work_t *req); + extern "C" void init(Handle); static volatile bool shutdownComplete = false; +/** + * async_block_data + */ + +struct async_block_data { + std::string hash; + std::string err_msg; + std::string result; + Persistent callback; +}; + /** * async_node_data * Where the uv async request data resides. @@ -344,44 +365,6 @@ NAN_METHOD(IsStopped) { NanReturnValue(NanNew(shutdownComplete)); } -/** - * GetBlock(height) - * bitcoind.getBlock(height) - */ - -NAN_METHOD(GetBlock) { - NanScope(); - std::string strHash = "0x000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"; - uint256 hash(strHash); - CBlock block; - CBlockIndex* pblockindex = mapBlockIndex[hash]; - ReadBlockFromDisk(block, pblockindex); - json_spirit::Object result = blockToJSON(block, pblockindex); - json_spirit::Object rpc_result = JSONRPCReplyObj(result, json_spirit::Value::null, 0); - std::string out = json_spirit::write_string(json_spirit::Value(rpc_result), false) + "\n"; - // https://www.google.com/search?q=json.parse%20in%20v8%20c%2B%2B - // https://v8.googlecode.com/svn/trunk/src/json-parser.h - // NanReturnValue(NanNew(v8::ParseJsonValue(NanNew(out)))); - NanReturnValue(NanNew(out)); -#if 0 - int nHeight = 0; - CBlockIndex *pindex = chainActive[nHeight]; - CBlock block; - if (ReadBlockFromDisk(block, pindex)) { - // const uint256 txhash = 0; - // uint256 hashBlock = pindex->GetBlockHash(); - // BOOST_FOREACH(const CTransaction &tx, block.vtx) { - // if (tx.GetHash() == txhash) { - // return true; - // } - // } - blockToJSON(block); - NanReturnValue(NanNew(block.ToString())); - } - NanReturnValue(NanNew(false)); -#endif -} - /** * start_node(void) * start_node_thread(void) @@ -684,6 +667,98 @@ async_stop_node_after(uv_work_t *req) { delete req; } +/** + * GetBlock(height) + * bitcoind.getBlock(height) + */ + +NAN_METHOD(GetBlock) { + NanScope(); + + if (args.Length() < 2 + || !args[0]->IsString() + || !args[1]->IsFunction()) { + return NanThrowError( + "Usage: bitcoindjs.getBlock(hash, callback)"); + } + + String::Utf8Value hash(args[0]->ToString()); + Local callback = Local::Cast(args[1]); + + std::string hashp = std::string(*hash); + //char *hashc = (char *)hashp.c_str(); + + async_block_data *data = new async_block_data(); + data->err_msg = std::string(""); + data->result = std::string(""); + data->hash = hashp; + data->callback = Persistent::New(callback); + + uv_work_t *req = new uv_work_t(); + req->data = data; + + int status = uv_queue_work(uv_default_loop(), + req, async_get_block, + (uv_after_work_cb)async_get_block_after); + + assert(status == 0); + + NanReturnValue(Undefined()); +} + +static void +async_get_block(uv_work_t *req) { + async_block_data* data = static_cast(req->data); + std::string strHash = data->hash; + if (strHash[1] != 'x') { + strHash = "0x" + strHash; + } + uint256 hash(strHash); + CBlock block; + CBlockIndex* pblockindex = mapBlockIndex[hash]; + if (ReadBlockFromDisk(block, pblockindex)) { + json_spirit::Object result = blockToJSON(block, pblockindex); + json_spirit::Object rpc_result = JSONRPCReplyObj(result, json_spirit::Value::null, 0); + std::string out = json_spirit::write_string(json_spirit::Value(rpc_result), false) + "\n"; + data->result = out; + } else { + data->err_msg = std::string("get_block(): failed."); + } +} + +static void +async_get_block_after(uv_work_t *req) { + NanScope(); + async_block_data* data = static_cast(req->data); + + if (!data->err_msg.empty()) { + Local err = Exception::Error(String::New(data->err_msg.c_str())); + 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 unsigned argc = 2; + Local argv[argc] = { + Local::New(Null()), + Local::New(NanNew(data->result)) + }; + 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; +} + /** * Init */