/** * bitcoind.js * Copyright (c) 2014, BitPay (MIT License) * * bitcoindjs.cc: * A bitcoind node.js binding. */ #include "nan.h" #include "bitcoindjs.h" /** * Bitcoin headers */ #if defined(HAVE_CONFIG_H) #include "bitcoin-config.h" #endif #include "core.h" #include "addrman.h" #include "checkpoints.h" #include "crypter.h" #include "main.h" // #include "random.h" // #include "timedata.h" #ifdef ENABLE_WALLET #include "db.h" #include "wallet.h" #include "walletdb.h" #endif // #include "walletdb.h" #include "alert.h" #include "checkqueue.h" // #include "db.h" #include "miner.h" #include "rpcclient.h" #include "tinyformat.h" // #include "wallet.h" #include "allocators.h" #include "clientversion.h" #include "hash.h" #include "mruset.h" #include "rpcprotocol.h" #include "txdb.h" #include "base58.h" #include "coincontrol.h" #include "init.h" #include "netbase.h" #include "rpcserver.h" #include "txmempool.h" #include "bloom.h" #include "coins.h" #include "key.h" #include "net.h" #include "script.h" #include "ui_interface.h" // #include "chainparamsbase.h" #include "compat.h" #include "keystore.h" #include "noui.h" #include "serialize.h" #include "uint256.h" #include "chainparams.h" #include "core.h" #include "leveldbwrapper.h" // #include "pow.h" #include "sync.h" #include "util.h" // #include "chainparamsseeds.h" // #include "core_io.h" #include "limitedmap.h" #include "protocol.h" #include "threadsafety.h" #include "version.h" /** * Bitcoin Globals * Relevant: * ~/bitcoin/src/init.cpp * ~/bitcoin/src/bitcoind.cpp * ~/bitcoin/src/main.h */ #include #include #include #include #include #include using namespace std; using namespace boost; extern void DetectShutdownThread(boost::thread_group*); /** * Node and Templates */ #include #include #include #include #include #include #include #include #include using namespace node; using namespace v8; NAN_METHOD(StartBitcoind); NAN_METHOD(IsStopping); NAN_METHOD(IsStopped); NAN_METHOD(StopBitcoind); NAN_METHOD(GetBlock); NAN_METHOD(GetTx); NAN_METHOD(PollBlocks); static void async_start_node_work(uv_work_t *req); static void async_start_node_after(uv_work_t *req); static void async_stop_node_work(uv_work_t *req); static void async_stop_node_after(uv_work_t *req); static int start_node(void); static void start_node_thread(void); static void 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); static void async_poll_blocks(uv_work_t *req); static void async_poll_blocks_after(uv_work_t *req); static void ctx_to_js(const CTransaction& tx, uint256 hashBlock, Local entry); static void cblock_to_js(const CBlock& block, const CBlockIndex* blockindex, Local obj); extern "C" void init(Handle); /** * Private Variables */ static volatile bool shutdownComplete = false; /** * async_node_data * Where the uv async request data resides. */ struct async_node_data { char *err_msg; char *result; Persistent callback; }; /** * async_log_data * Where the uv async request data resides. */ struct async_log_data { char *err_msg; int **out_pipe; int **log_pipe; 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; }; /** * async_poll_blocks_data */ struct async_poll_blocks_data { std::string err_msg; int poll_saved_height; int poll_top_height; Persistent result_array; Persistent callback; }; /** * StartBitcoind * bitcoind.start(callback) */ NAN_METHOD(StartBitcoind) { NanScope(); if (args.Length() < 1 || !args[0]->IsFunction()) { return NanThrowError( "Usage: bitcoind.start(callback)"); } Local callback = Local::Cast(args[0]); // // Run bitcoind's StartNode() on a separate thread. // async_node_data *data_start_node = new async_node_data(); data_start_node->err_msg = NULL; data_start_node->result = NULL; data_start_node->callback = Persistent::New(callback); uv_work_t *req_start_node = new uv_work_t(); req_start_node->data = data_start_node; int status_start_node = uv_queue_work(uv_default_loop(), req_start_node, async_start_node_work, (uv_after_work_cb)async_start_node_after); assert(status_start_node == 0); NanReturnValue(NanNew(-1)); } /** * async_start_node_work() * Call start_node() and start all our boost threads. */ static void async_start_node_work(uv_work_t *req) { async_node_data *node_data = static_cast(req->data); start_node(); node_data->result = (char *)strdup("start_node(): bitcoind opened."); } /** * async_start_node_after() * Execute our callback. */ static void async_start_node_after(uv_work_t *req) { NanScope(); async_node_data *node_data = static_cast(req->data); if (node_data->err_msg != NULL) { Local err = Exception::Error(String::New(node_data->err_msg)); free(node_data->err_msg); const unsigned argc = 1; Local argv[argc] = { err }; TryCatch try_catch; node_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(String::New(node_data->result)) }; TryCatch try_catch; node_data->callback->Call(Context::GetCurrent()->Global(), argc, argv); if (try_catch.HasCaught()) { node::FatalException(try_catch); } } // node_data->callback.Dispose(); if (node_data->result != NULL) { free(node_data->result); } delete node_data; delete req; } /** * IsStopping() * bitcoind.stopping() */ NAN_METHOD(IsStopping) { NanScope(); NanReturnValue(NanNew(ShutdownRequested())); } /** * IsStopped() * bitcoind.stopped() */ NAN_METHOD(IsStopped) { NanScope(); NanReturnValue(NanNew(shutdownComplete)); } /** * start_node(void) * start_node_thread(void) * A reimplementation of AppInit2 minus * the logging and argument parsing. */ static int start_node(void) { noui_connect(); (boost::thread *)new boost::thread(boost::bind(&start_node_thread)); // horrible fix for a race condition sleep(2); signal(SIGINT, SIG_DFL); signal(SIGHUP, SIG_DFL); return 0; } static void start_node_thread(void) { boost::thread_group threadGroup; boost::thread *detectShutdownThread = NULL; const int argc = 0; const char *argv[argc + 1] = { //"-server", NULL }; ParseParameters(argc, argv); ReadConfigFile(mapArgs, mapMultiArgs); if (!SelectParamsFromCommandLine()) { return; } // CreatePidFile(GetPidFile(), getpid()); detectShutdownThread = new boost::thread( boost::bind(&DetectShutdownThread, &threadGroup)); int fRet = AppInit2(threadGroup); if (!fRet) { if (detectShutdownThread) detectShutdownThread->interrupt(); threadGroup.interrupt_all(); } if (detectShutdownThread) { detectShutdownThread->join(); delete detectShutdownThread; detectShutdownThread = NULL; } Shutdown(); shutdownComplete = true; } /** * StopBitcoind * bitcoind.stop(callback) */ NAN_METHOD(StopBitcoind) { NanScope(); if (args.Length() < 1 || !args[0]->IsFunction()) { return NanThrowError( "Usage: bitcoind.stop(callback)"); } Local callback = Local::Cast(args[0]); // // Run bitcoind's StartShutdown() on a separate thread. // async_node_data *data_stop_node = new async_node_data(); data_stop_node->err_msg = NULL; data_stop_node->result = NULL; data_stop_node->callback = Persistent::New(callback); uv_work_t *req_stop_node = new uv_work_t(); req_stop_node->data = data_stop_node; int status_stop_node = uv_queue_work(uv_default_loop(), req_stop_node, async_stop_node_work, (uv_after_work_cb)async_stop_node_after); assert(status_stop_node == 0); NanReturnValue(Undefined()); } /** * async_stop_node_work() * Call StartShutdown() to join the boost threads, which will call Shutdown(). */ static void async_stop_node_work(uv_work_t *req) { async_node_data *node_data = static_cast(req->data); StartShutdown(); node_data->result = (char *)strdup("stop_node(): bitcoind shutdown."); } /** * async_stop_node_after() * Execute our callback. */ static void async_stop_node_after(uv_work_t *req) { NanScope(); async_node_data* node_data = static_cast(req->data); if (node_data->err_msg != NULL) { Local err = Exception::Error(String::New(node_data->err_msg)); free(node_data->err_msg); const unsigned argc = 1; Local argv[argc] = { err }; TryCatch try_catch; node_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(String::New(node_data->result)) }; TryCatch try_catch; node_data->callback->Call(Context::GetCurrent()->Global(), argc, argv); if (try_catch.HasCaught()) { node::FatalException(try_catch); } } node_data->callback.Dispose(); if (node_data->result != NULL) { free(node_data->result); } delete node_data; delete req; } /** * GetBlock(hash, callback) * bitcoind.getBlock(hash, callback) */ NAN_METHOD(GetBlock) { NanScope(); if (args.Length() < 2 || !args[0]->IsString() || !args[1]->IsFunction()) { return NanThrowError( "Usage: bitcoindjs.getBlock(blockHash, callback)"); } String::Utf8Value hash(args[0]->ToString()); Local callback = Local::Cast(args[1]); std::string hashp = std::string(*hash); async_block_data *data = new async_block_data(); data->err_msg = 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)) { data->result_block = block; data->result_blockindex = pblockindex; } 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 CBlock& block = data->result_block; const CBlockIndex* blockindex = data->result_blockindex; Local obj = NanNew(); cblock_to_js(block, blockindex, obj); const unsigned argc = 2; Local argv[argc] = { Local::New(Null()), Local::New(obj) }; 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; } /** * GetTx(hash, callback) * bitcoind.getTx(hash, callback) */ 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_tx(): 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)); ctx_to_js(tx, hashBlock, entry); 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; } /** * PollBlocks(callback) * bitcoind.pollBlocks(callback) */ NAN_METHOD(PollBlocks) { NanScope(); if (args.Length() < 1 || !args[0]->IsFunction()) { return NanThrowError( "Usage: bitcoindjs.pollBlocks(callback)"); } Local callback = Local::Cast(args[0]); async_poll_blocks_data *data = new async_poll_blocks_data(); data->poll_saved_height = -1; data->poll_top_height = -1; data->err_msg = std::string(""); 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_poll_blocks, (uv_after_work_cb)async_poll_blocks_after); assert(status == 0); NanReturnValue(Undefined()); } static void async_poll_blocks(uv_work_t *req) { async_poll_blocks_data* data = static_cast(req->data); data->poll_saved_height = data->poll_top_height; while (chainActive.Tip()) { int cur_height = chainActive.Height(); if (cur_height != data->poll_top_height) { data->poll_top_height = cur_height; break; } else { // 100 milliseconds useconds_t usec = 100 * 1000; usleep(usec); } } } static void async_poll_blocks_after(uv_work_t *req) { NanScope(); async_poll_blocks_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 blocks = NanNew(); for (int i = data->poll_saved_height, j = 0; i < data->poll_top_height; i++) { if (i == -1) continue; CBlockIndex *pindex = chainActive[i]; if (pindex != NULL) { CBlock block; if (ReadBlockFromDisk(block, pindex)) { Local obj = NanNew(); cblock_to_js(block, pindex, obj); blocks->Set(j, obj); j++; } } } Local argv[argc] = { Local::New(Null()), Local::New(blocks) }; 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; } /** * Conversions */ static void cblock_to_js(const CBlock& block, const CBlockIndex* blockindex, Local obj) { obj->Set(NanNew("hash"), NanNew(block.GetHash().GetHex().c_str())); CMerkleTx txGen(block.vtx[0]); txGen.SetMerkleBranch(&block); obj->Set(NanNew("confirmations"), NanNew((int)txGen.GetDepthInMainChain())); obj->Set(NanNew("size"), NanNew((int)::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION))); obj->Set(NanNew("height"), NanNew(blockindex->nHeight)); obj->Set(NanNew("version"), NanNew(block.nVersion)); obj->Set(NanNew("merkleroot"), NanNew(block.hashMerkleRoot.GetHex())); Local txs = NanNew(); int ti = 0; BOOST_FOREACH(const CTransaction& tx, block.vtx) { Local entry = NanNew(); CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION); ssTx << tx; string strHex = HexStr(ssTx.begin(), ssTx.end()); 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(txout.nValue)); out->Set(NanNew("n"), NanNew((boost::int64_t)vo)); 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); { const uint256 hashBlock = block.GetHash(); 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)); } } } } txs->Set(ti, entry); ti++; } obj->Set(NanNew("tx"), txs); obj->Set(NanNew("time"), NanNew((boost::int64_t)block.GetBlockTime())); obj->Set(NanNew("nonce"), NanNew((boost::uint64_t)block.nNonce)); obj->Set(NanNew("bits"), NanNew(block.nBits)); obj->Set(NanNew("difficulty"), NanNew(GetDifficulty(blockindex))); obj->Set(NanNew("chainwork"), NanNew(blockindex->nChainWork.GetHex())); if (blockindex->pprev) { obj->Set(NanNew("previousblockhash"), NanNew(blockindex->pprev->GetBlockHash().GetHex())); } CBlockIndex *pnext = chainActive.Next(blockindex); if (pnext) { obj->Set(NanNew("nextblockhash"), NanNew(pnext->GetBlockHash().GetHex())); } } static void ctx_to_js(const CTransaction& tx, uint256 hashBlock, Local entry) { // 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(txout.nValue)); out->Set(NanNew("n"), NanNew((boost::int64_t)vo)); 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)); } } } } /** * Init */ extern "C" void init(Handle target) { NanScope(); NODE_SET_METHOD(target, "start", StartBitcoind); NODE_SET_METHOD(target, "stop", StopBitcoind); NODE_SET_METHOD(target, "stopping", IsStopping); NODE_SET_METHOD(target, "stopped", IsStopped); NODE_SET_METHOD(target, "getBlock", GetBlock); NODE_SET_METHOD(target, "getTx", GetTx); NODE_SET_METHOD(target, "pollBlocks", PollBlocks); } NODE_MODULE(bitcoindjs, init)