From f2d5dcabe0d18e2d44b4fb6226af7f21086224d8 Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Mon, 22 Sep 2014 13:21:42 -0700 Subject: [PATCH] make GetTx asynchronous. --- src/bitcoindjs.cc | 246 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 235 insertions(+), 11 deletions(-) diff --git a/src/bitcoindjs.cc b/src/bitcoindjs.cc index a8658cce..f61e2b00 100644 --- a/src/bitcoindjs.cc +++ b/src/bitcoindjs.cc @@ -118,6 +118,10 @@ extern std::string strWalletFile; extern CWallet *pwalletMain; #endif +/** + * Node and Templates + */ + #include #include @@ -177,22 +181,20 @@ async_get_block(uv_work_t *req); static void async_get_block_after(uv_work_t *req); +static void +async_get_tx(uv_work_t *req); + +static void +async_get_tx_after(uv_work_t *req); + extern "C" void init(Handle); -static volatile bool shutdownComplete = false; - /** - * async_block_data + * Private Variables */ -struct async_block_data { - std::string hash; - std::string err_msg; - CBlock result_block; - CBlockIndex* result_blockindex; - Persistent callback; -}; +static volatile bool shutdownComplete = false; /** * async_node_data @@ -211,13 +213,37 @@ struct async_node_data { */ struct async_log_data { + char *err_msg; int **out_pipe; int **log_pipe; - char *err_msg; char *result; Persistent callback; }; +/** + * async_block_data + */ + +struct async_block_data { + std::string err_msg; + std::string hash; + CBlock result_block; + CBlockIndex* result_blockindex; + Persistent callback; +}; + +/** + * async_tx_data + */ + +struct async_tx_data { + std::string err_msg; + std::string txHash; + std::string blockHash; + CTransaction result_tx; + Persistent callback; +}; + /** * StartBitcoind * bitcoind.start(callback) @@ -877,6 +903,9 @@ async_get_block_after(uv_work_t *req) { * bitcoind.getTx(hash, callback) */ +// Synchronous: + +#if 0 NAN_METHOD(GetTx) { NanScope(); @@ -1030,6 +1059,201 @@ NAN_METHOD(GetTx) { NanReturnValue(Undefined()); } } +#endif + +NAN_METHOD(GetTx) { + NanScope(); + + if (args.Length() < 2 + || !args[0]->IsString() + || !args[1]->IsString() + || !args[2]->IsFunction()) { + return NanThrowError( + "Usage: bitcoindjs.getTx(txHash, [blockHash], callback)"); + } + + String::Utf8Value txHash_(args[0]->ToString()); + String::Utf8Value blockHash_(args[1]->ToString()); + Local callback = Local::Cast(args[2]); + + Persistent cb; + cb = Persistent::New(callback); + + std::string txHash = std::string(*txHash_); + std::string blockHash = std::string(*blockHash_); + + if (blockHash.empty()) { + blockHash = std::string("0x0000000000000000000000000000000000000000000000000000000000000000"); + } + + if (txHash[1] != 'x') { + txHash = "0x" + txHash; + } + + if (blockHash[1] != 'x') { + blockHash = "0x" + blockHash; + } + + async_tx_data *data = new async_tx_data(); + data->err_msg = std::string(""); + data->txHash = txHash; + data->blockHash = blockHash; + 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_tx, + (uv_after_work_cb)async_get_tx_after); + + assert(status == 0); + + NanReturnValue(Undefined()); +} + +static void +async_get_tx(uv_work_t *req) { + async_tx_data* data = static_cast(req->data); + + uint256 hash(data->txHash); + uint256 hashBlock(data->blockHash); + CTransaction tx; + + if (GetTransaction(hash, tx, hashBlock, hashBlock == 0 ? true : false)) { + data->result_tx = tx; + } else { + data->err_msg = std::string("get_block(): failed."); + } +} + +static void +async_get_tx_after(uv_work_t *req) { + NanScope(); + async_tx_data* data = static_cast(req->data); + + std::string txHash = data->txHash; + std::string blockHash = data->blockHash; + CTransaction tx = data->result_tx; + + uint256 hash(txHash); + uint256 hashBlock(blockHash); + + 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 { + CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION); + ssTx << tx; + string strHex = HexStr(ssTx.begin(), ssTx.end()); + + Local entry = NanNew(); + entry->Set(NanNew("hex"), NanNew(strHex)); + entry->Set(NanNew("txid"), NanNew(tx.GetHash().GetHex())); + entry->Set(NanNew("version"), NanNew(tx.nVersion)); + entry->Set(NanNew("locktime"), NanNew(tx.nLockTime)); + + Local vin = NanNew(); + int vi = 0; + BOOST_FOREACH(const CTxIn& txin, tx.vin) { + Local in = NanNew(); + if (tx.IsCoinBase()) { + in->Set(NanNew("coinbase"), NanNew(HexStr(txin.scriptSig.begin(), txin.scriptSig.end()))); + } else { + in->Set(NanNew("txid"), NanNew(txin.prevout.hash.GetHex())); + in->Set(NanNew("vout"), NanNew((boost::int64_t)txin.prevout.n)); + Local o = NanNew(); + o->Set(NanNew("asm"), NanNew(txin.scriptSig.ToString())); + o->Set(NanNew("hex"), NanNew(HexStr(txin.scriptSig.begin(), txin.scriptSig.end()))); + in->Set(NanNew("scriptSig"), o); + } + in->Set(NanNew("sequence"), NanNew((boost::int64_t)txin.nSequence)); + vin->Set(vi, in); + vi++; + } + entry->Set(NanNew("vin"), vin); + + Local vout = NanNew(); + for (unsigned int vo = 0; vo < tx.vout.size(); vo++) { + const CTxOut& txout = tx.vout[vo]; + Local out = NanNew(); + //out->Set(NanNew("value"), NanNew(ValueFromAmount(txout.nValue))); + out->Set(NanNew("value"), NanNew(txout.nValue)); + out->Set(NanNew("n"), NanNew((boost::int64_t)vo)); + + // ScriptPubKeyToJSON(txout.scriptPubKey, o, true); + Local o = NanNew(); + { + const CScript& scriptPubKey = txout.scriptPubKey; + Local out = o; + bool fIncludeHex = true; + // --- + txnouttype type; + vector addresses; + int nRequired; + out->Set(NanNew("asm"), NanNew(scriptPubKey.ToString())); + if (fIncludeHex) { + out->Set(NanNew("hex"), NanNew(HexStr(scriptPubKey.begin(), scriptPubKey.end()))); + } + if (!ExtractDestinations(scriptPubKey, type, addresses, nRequired)) { + out->Set(NanNew("type"), NanNew(GetTxnOutputType(type))); + } else { + out->Set(NanNew("reqSigs"), NanNew(nRequired)); + out->Set(NanNew("type"), NanNew(GetTxnOutputType(type))); + Local a = NanNew(); + int ai = 0; + BOOST_FOREACH(const CTxDestination& addr, addresses) { + a->Set(ai, NanNew(CBitcoinAddress(addr).ToString())); + ai++; + } + out->Set(NanNew("addresses"), a); + } + } + out->Set(NanNew("scriptPubKey"), o); + + vout->Set(vo, out); + } + entry->Set(NanNew("vout"), vout); + + if (hashBlock != 0) { + entry->Set(NanNew("blockhash"), NanNew(hashBlock.GetHex())); + map::iterator mi = mapBlockIndex.find(hashBlock); + if (mi != mapBlockIndex.end() && (*mi).second) { + CBlockIndex* pindex = (*mi).second; + if (chainActive.Contains(pindex)) { + entry->Set(NanNew("confirmations"), + NanNew(1 + chainActive.Height() - pindex->nHeight)); + entry->Set(NanNew("time"), NanNew((boost::int64_t)pindex->nTime)); + entry->Set(NanNew("blocktime"), NanNew((boost::int64_t)pindex->nTime)); + } else { + entry->Set(NanNew("confirmations"), NanNew(0)); + } + } + } + + const unsigned argc = 2; + Local argv[argc] = { + Local::New(Null()), + Local::New(entry) + }; + 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