/** * 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 "rpcwallet.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 #include using namespace std; using namespace boost; extern void DetectShutdownThread(boost::thread_group*); extern int nScriptCheckThreads; extern bool fDaemon; extern std::map mapArgs; #ifdef ENABLE_WALLET extern std::string strWalletFile; extern CWallet *pwalletMain; #endif extern int64_t nTransactionFee; /** * Node and Templates */ #include #include #include #include #include #include #include #include #include using namespace node; using namespace v8; Handle bitcoindjs_obj; NAN_METHOD(StartBitcoind); NAN_METHOD(IsStopping); NAN_METHOD(IsStopped); NAN_METHOD(StopBitcoind); NAN_METHOD(GetBlock); NAN_METHOD(GetTx); NAN_METHOD(PollBlocks); NAN_METHOD(PollMempool); NAN_METHOD(BroadcastTx); NAN_METHOD(VerifyBlock); NAN_METHOD(VerifyTransaction); NAN_METHOD(FillTransaction); NAN_METHOD(WalletNewAddress); NAN_METHOD(WalletGetAccountAddress); NAN_METHOD(WalletSetAccount); NAN_METHOD(WalletGetAccount); NAN_METHOD(WalletSendTo); NAN_METHOD(WalletSignMessage); NAN_METHOD(WalletVerifyMessage); NAN_METHOD(WalletGetBalance); NAN_METHOD(WalletGetUnconfirmedBalance); NAN_METHOD(WalletSendFrom); NAN_METHOD(WalletListTransactions); NAN_METHOD(WalletListAccounts); NAN_METHOD(WalletGetTransaction); NAN_METHOD(WalletBackup); NAN_METHOD(WalletPassphrase); NAN_METHOD(WalletPassphraseChange); NAN_METHOD(WalletLock); NAN_METHOD(WalletEncrypt); NAN_METHOD(WalletSetTxFee); 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 async_poll_mempool(uv_work_t *req); static void async_poll_mempool_after(uv_work_t *req); static void async_broadcast_tx(uv_work_t *req); static void async_broadcast_tx_after(uv_work_t *req); static void async_wallet_sendto(uv_work_t *req); static void async_wallet_sendto_after(uv_work_t *req); static void async_wallet_sendfrom(uv_work_t *req); static void async_wallet_sendfrom_after(uv_work_t *req); static inline void ctx_to_jstx(const CTransaction& tx, uint256 hashBlock, Local entry); static inline void cblock_to_jsblock(const CBlock& block, const CBlockIndex* blockindex, Local obj); #if 0 static inline void jsblock_to_cblock(Local jsblock, CBlock& cblock); static inline void jstx_to_ctx(Local jstx, CTransaction& ctx); #endif static inline void hexblock_to_cblock(std::string block_hex, CBlock& cblock); static inline void hextx_to_ctx(std::string tx_hex, CTransaction& ctx); 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 { std::string err_msg; std::string 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; }; /** * async_poll_mempool_data */ struct async_poll_mempool_data { std::string err_msg; int poll_saved_height; int poll_top_height; Persistent result_array; Persistent callback; }; /** * async_broadcast_tx_data */ struct async_broadcast_tx_data { std::string err_msg; std::string tx_hex; std::string tx_hash; bool override_fees; bool own_only; Persistent callback; }; /** * async_wallet_sendto_data */ struct async_wallet_sendto_data { std::string err_msg; std::string tx_hash; std::string address; int64_t nAmount; CWalletTx wtx; Persistent callback; }; /** * async_wallet_sendfrom_data */ struct async_wallet_sendfrom_data { std::string err_msg; std::string tx_hash; std::string address; int64_t nAmount; int nMinDepth; CWalletTx wtx; 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 = new async_node_data(); data->err_msg = std::string(""); data->result = 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_start_node_work, (uv_after_work_cb)async_start_node_after); assert(status == 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 *data = static_cast(req->data); start_node(); data->result = std::string("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 *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(String::New(data->result.c_str())) }; 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; } /** * 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)); // wait for wallet to be instantiated // this also avoids a race condition with signals not being set up while (!pwalletMain) { useconds_t usec = 100 * 1000; usleep(usec); } // drop the bitcoind signal handlers - we want our own signal(SIGINT, SIG_DFL); signal(SIGHUP, SIG_DFL); signal(SIGQUIT, 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 = new async_node_data(); data->err_msg = std::string(""); data->result = 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_stop_node_work, (uv_after_work_cb)async_stop_node_after); assert(status == 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 *data = static_cast(req->data); StartShutdown(); data->result = std::string("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* 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(String::New(data->result.c_str())) }; 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; } /** * GetBlock * bitcoind.getBlock(blockHash, 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_jsblock(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 * bitcoind.getTx(txHash, [blockHash], callback) */ NAN_METHOD(GetTx) { NanScope(); if (args.Length() < 3 || !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, true)) { 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; std::string strHex = HexStr(ssTx.begin(), ssTx.end()); Local entry = NanNew(); entry->Set(NanNew("hex"), NanNew(strHex)); ctx_to_jstx(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 * 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_jsblock(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; } /** * PollMempool * bitcoind.pollMempool(callback) */ NAN_METHOD(PollMempool) { NanScope(); if (args.Length() < 1 || !args[0]->IsFunction()) { return NanThrowError( "Usage: bitcoindjs.pollMempool(callback)"); } Local callback = Local::Cast(args[0]); async_poll_mempool_data *data = new async_poll_mempool_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_mempool, (uv_after_work_cb)async_poll_mempool_after); assert(status == 0); NanReturnValue(Undefined()); } static void async_poll_mempool(uv_work_t *req) { // async_poll_blocks_data* data = static_cast(req->data); // Nothing really async to do here. It's all in memory. Placeholder for now. useconds_t usec = 20 * 1000; usleep(usec); } static void async_poll_mempool_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 { int ti = 0; Local txs = NanNew(); { std::map::const_iterator it = mempool.mapTx.begin(); for (; it != mempool.mapTx.end(); it++) { const CTransaction& tx = it->second.GetTx(); Local entry = NanNew(); ctx_to_jstx(tx, 0, entry); txs->Set(ti, entry); ti++; } } { std::map::const_iterator it = mempool.mapNextTx.begin(); for (; it != mempool.mapNextTx.end(); it++) { const CTransaction tx = *it->second.ptx; Local entry = NanNew(); ctx_to_jstx(tx, 0, entry); txs->Set(ti, entry); ti++; } } const unsigned argc = 2; Local argv[argc] = { Local::New(Null()), Local::New(txs) }; 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; } /** * BroadcastTx * bitcoind.broadcastTx(tx, override_fees, own_only, callback) */ NAN_METHOD(BroadcastTx) { NanScope(); if (args.Length() < 4 || !args[0]->IsObject() || !args[1]->IsBoolean() || !args[2]->IsBoolean() || !args[3]->IsFunction()) { return NanThrowError( "Usage: bitcoindjs.broadcastTx(tx, override_fees, own_only, callback)"); } Local js_tx = Local::Cast(args[0]); Local callback = Local::Cast(args[3]); String::Utf8Value tx_hex_(js_tx->Get(NanNew("hex"))->ToString()); std::string tx_hex = std::string(*tx_hex_); async_broadcast_tx_data *data = new async_broadcast_tx_data(); data->tx_hex = tx_hex; data->override_fees = args[1]->ToBoolean()->IsTrue(); data->own_only = args[2]->ToBoolean()->IsTrue(); 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_broadcast_tx, (uv_after_work_cb)async_broadcast_tx_after); assert(status == 0); NanReturnValue(Undefined()); } static void async_broadcast_tx(uv_work_t *req) { async_broadcast_tx_data* data = static_cast(req->data); CDataStream ssData(ParseHex(data->tx_hex), SER_NETWORK, PROTOCOL_VERSION); CTransaction tx; bool fOverrideFees = false; bool fOwnOnly = false; if (data->override_fees) { fOverrideFees = true; } if (data->own_only) { fOwnOnly = true; } // jstx_to_ctx(jstx, ctx); try { ssData >> tx; } catch (std::exception &e) { data->err_msg = std::string("TX decode failed"); return; } uint256 hashTx = tx.GetHash(); bool fHave = false; CCoinsViewCache &view = *pcoinsTip; CCoins existingCoins; if (fOwnOnly) { fHave = view.GetCoins(hashTx, existingCoins); if (!fHave) { CValidationState state; if (!AcceptToMemoryPool(mempool, state, tx, false, NULL, !fOverrideFees)) { data->err_msg = std::string("TX rejected"); return; } } } if (fHave) { if (existingCoins.nHeight < 1000000000) { data->err_msg = std::string("transaction already in block chain"); return; } } else { SyncWithWallets(hashTx, tx, NULL); } RelayTransaction(tx, hashTx); data->tx_hash = hashTx.GetHex(); } static void async_broadcast_tx_after(uv_work_t *req) { NanScope(); async_broadcast_tx_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 { // jstx_to_ctx(jstx, ctx); CDataStream ssData(ParseHex(data->tx_hex), SER_NETWORK, PROTOCOL_VERSION); CTransaction tx; ssData >> tx; Local entry = NanNew(); ctx_to_jstx(tx, 0, entry); const unsigned argc = 3; Local argv[argc] = { Local::New(Null()), Local::New(NanNew(data->tx_hash)), 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; } /** * VerifyBlock */ NAN_METHOD(VerifyBlock) { NanScope(); if (args.Length() < 1 || !args[0]->IsObject()) { return NanThrowError( "Usage: bitcoindjs.verifyBlock(block)"); } Local js_block = Local::Cast(args[0]); String::Utf8Value block_hex_(js_block->Get(NanNew("hex"))->ToString()); std::string block_hex = std::string(*block_hex_); // jsblock_to_cblock(jsblock, cblock); CBlock block; CDataStream ssData(ParseHex(block_hex), SER_NETWORK, PROTOCOL_VERSION); ssData >> block; CValidationState state; bool valid = CheckBlock(block, state); NanReturnValue(NanNew(valid)); } /** * VerifyTransaction */ NAN_METHOD(VerifyTransaction) { NanScope(); if (args.Length() < 1 || !args[0]->IsObject()) { return NanThrowError( "Usage: bitcoindjs.verifyTransaction(tx)"); } Local js_tx = Local::Cast(args[0]); String::Utf8Value tx_hex_(js_tx->Get(NanNew("hex"))->ToString()); std::string tx_hex = std::string(*tx_hex_); // jstx_to_ctx(jstx, ctx); CTransaction tx; CDataStream ssData(ParseHex(tx_hex), SER_NETWORK, PROTOCOL_VERSION); ssData >> tx; CValidationState state; bool valid = CheckTransaction(tx, state); std::string reason; bool standard = IsStandardTx(tx, reason); NanReturnValue(NanNew(valid && standard)); } NAN_METHOD(FillTransaction) { NanScope(); if (args.Length() < 2 || !args[0]->IsObject() || !args[1]->IsObject()) { return NanThrowError( "Usage: bitcoindjs.fillTransaction(tx, options)"); } Local js_tx = Local::Cast(args[0]); // Local options = Local::Cast(args[1]); String::Utf8Value tx_hex_(js_tx->Get(NanNew("hex"))->ToString()); std::string tx_hex = std::string(*tx_hex_); // jstx_to_ctx(jstx, ctx); CTransaction tx; CDataStream ssData(ParseHex(tx_hex), SER_NETWORK, PROTOCOL_VERSION); ssData >> tx; // Get total value of outputs // Get the scriptPubKey of the first output (presumably our destination) int64_t nValue = 0; for (unsigned int vo = 0; vo < tx.vout.size(); vo++) { const CTxOut& txout = tx.vout[vo]; int64_t value = txout.nValue; const CScript& scriptPubKey = txout.scriptPubKey; nValue += value; } if (nValue <= 0) return NanThrowError("Invalid amount"); if (nValue + nTransactionFee > pwalletMain->GetBalance()) return NanThrowError("Insufficient funds"); int64_t nFeeRet = nTransactionFee; if (pwalletMain->IsLocked()) { return NanThrowError("Error: Wallet locked, unable to create transaction!"); } CCoinControl* coinControl = new CCoinControl(); int64_t nTotalValue = nValue + nFeeRet; set > setCoins; int64_t nValueIn = 0; if (!pwalletMain->SelectCoins(nTotalValue, setCoins, nValueIn, coinControl)) { return NanThrowError("Insufficient funds"); } // Fill inputs if they aren't already filled tx.vin.clear(); BOOST_FOREACH(const PAIRTYPE(const CWalletTx*,unsigned int)& coin, setCoins) { tx.vin.push_back(CTxIn(coin.first->GetHash(), coin.second)); } // Sign everything int nIn = 0; BOOST_FOREACH(const PAIRTYPE(const CWalletTx*,unsigned int)& coin, setCoins) { if (!SignSignature(*pwalletMain, *coin.first, tx, nIn++)) { return NanThrowError("Signing transaction failed"); } } // Turn our CTransaction into a javascript Transaction Local entry = NanNew(); ctx_to_jstx(tx, 0, entry); NanReturnValue(entry); } /** * Wallet */ NAN_METHOD(WalletNewAddress) { NanScope(); if (args.Length() < 1 || !args[0]->IsObject()) { return NanThrowError( "Usage: bitcoindjs.walletNewAddress(options)"); } // Parse the account first so we don't generate a key if there's an error Local options = Local::Cast(args[0]); String::Utf8Value name_(options->Get(NanNew("name"))->ToString()); std::string strAccount = std::string(*name_); if (!pwalletMain->IsLocked()) { pwalletMain->TopUpKeyPool(); } // Generate a new key that is added to wallet CPubKey newKey; if (!pwalletMain->GetKeyFromPool(newKey)) { // return NanThrowError("Keypool ran out, please call keypoolrefill first"); // EnsureWalletIsUnlocked(); if (pwalletMain->IsLocked()) { return NanThrowError("Please enter the wallet passphrase with walletpassphrase first."); } pwalletMain->TopUpKeyPool(100); if (pwalletMain->GetKeyPoolSize() < 100) { return NanThrowError("Error refreshing keypool."); } } CKeyID keyID = newKey.GetID(); pwalletMain->SetAddressBook(keyID, strAccount, "receive"); NanReturnValue(NanNew(CBitcoinAddress(keyID).ToString())); } NAN_METHOD(WalletGetAccountAddress) { NanScope(); if (args.Length() < 1 || !args[0]->IsObject()) { return NanThrowError( "Usage: bitcoindjs.walletGetAccountAddress(options)"); } // Parse the account first so we don't generate a key if there's an error Local options = Local::Cast(args[0]); String::Utf8Value name_(options->Get(NanNew("name"))->ToString()); std::string strAccount = std::string(*name_); NanReturnValue(Undefined()); } NAN_METHOD(WalletSetAccount) { NanScope(); if (args.Length() < 1 || !args[0]->IsObject()) { return NanThrowError( "Usage: bitcoindjs.walletSetAccount(options)"); } // Parse the account first so we don't generate a key if there's an error Local options = Local::Cast(args[0]); String::Utf8Value name_(options->Get(NanNew("name"))->ToString()); std::string strAccount = std::string(*name_); NanReturnValue(Undefined()); } NAN_METHOD(WalletGetAccount) { NanScope(); if (args.Length() < 1 || !args[0]->IsObject()) { return NanThrowError( "Usage: bitcoindjs.walletGetAccount(options)"); } // Parse the account first so we don't generate a key if there's an error Local options = Local::Cast(args[0]); String::Utf8Value name_(options->Get(NanNew("name"))->ToString()); std::string strAccount = std::string(*name_); NanReturnValue(Undefined()); } NAN_METHOD(WalletSendTo) { NanScope(); if (args.Length() < 1 || !args[0]->IsObject()) { return NanThrowError( "Usage: bitcoindjs.walletSendTo(options)"); } Local options = Local::Cast(args[0]); async_wallet_sendto_data *data = new async_wallet_sendto_data(); String::Utf8Value addr_(options->Get(NanNew("address"))->ToString()); std::string addr = std::string(*addr_); data->address = addr; // Amount int64_t nAmount = options->Get(NanNew("amount"))->IntegerValue(); data->nAmount = nAmount; // Wallet comments CWalletTx wtx; if (options->Get(NanNew("comment"))->IsString()) { String::Utf8Value comment_(options->Get(NanNew("comment"))->ToString()); std::string comment = std::string(*comment_); wtx.mapValue["comment"] = comment; } if (options->Get(NanNew("to"))->IsString()) { String::Utf8Value to_(options->Get(NanNew("to"))->ToString()); std::string to = std::string(*to_); wtx.mapValue["to"] = to; } data->wtx = wtx; uv_work_t *req = new uv_work_t(); req->data = data; int status = uv_queue_work(uv_default_loop(), req, async_wallet_sendto, (uv_after_work_cb)async_wallet_sendto_after); assert(status == 0); NanReturnValue(Undefined()); } static void async_wallet_sendto(uv_work_t *req) { async_wallet_sendto_data* data = static_cast(req->data); CBitcoinAddress address(data->address); if (!address.IsValid()) { data->err_msg = std::string("Invalid Bitcoin address"); return; } // Amount int64_t nAmount = data->nAmount; // Wallet Transaction CWalletTx wtx = data->wtx; // EnsureWalletIsUnlocked(); if (pwalletMain->IsLocked()) { data->err_msg = std::string("Please enter the wallet passphrase with walletpassphrase first."); return; } std::string strError = pwalletMain->SendMoneyToDestination(address.Get(), nAmount, wtx); if (strError != "") { data->err_msg = strError; return; } data->tx_hash = wtx.GetHash().GetHex(); } static void async_wallet_sendto_after(uv_work_t *req) { NanScope(); async_wallet_sendto_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->tx_hash)) }; 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; } NAN_METHOD(WalletSignMessage) { NanScope(); if (args.Length() < 1 || !args[0]->IsObject()) { return NanThrowError( "Usage: bitcoindjs.walletSignMessage(options)"); } // Parse the account first so we don't generate a key if there's an error Local options = Local::Cast(args[0]); String::Utf8Value name_(options->Get(NanNew("name"))->ToString()); std::string strAccount = std::string(*name_); NanReturnValue(Undefined()); } NAN_METHOD(WalletVerifyMessage) { NanScope(); if (args.Length() < 1 || !args[0]->IsObject()) { return NanThrowError( "Usage: bitcoindjs.walletVerifyMessage(options)"); } // Parse the account first so we don't generate a key if there's an error Local options = Local::Cast(args[0]); String::Utf8Value name_(options->Get(NanNew("name"))->ToString()); std::string strAccount = std::string(*name_); NanReturnValue(Undefined()); } NAN_METHOD(WalletGetBalance) { NanScope(); if (args.Length() < 1 || !args[0]->IsObject()) { return NanThrowError( "Usage: bitcoindjs.walletGetBalance(options)"); } // Parse the account first so we don't generate a key if there's an error Local options = Local::Cast(args[0]); String::Utf8Value name_(options->Get(NanNew("name"))->ToString()); std::string strAccount = std::string(*name_); NanReturnValue(Undefined()); } NAN_METHOD(WalletGetUnconfirmedBalance) { NanScope(); if (args.Length() < 1 || !args[0]->IsObject()) { return NanThrowError( "Usage: bitcoindjs.walletGetUnconfirmedBalance(options)"); } // Parse the account first so we don't generate a key if there's an error Local options = Local::Cast(args[0]); String::Utf8Value name_(options->Get(NanNew("name"))->ToString()); std::string strAccount = std::string(*name_); NanReturnValue(Undefined()); } NAN_METHOD(WalletSendFrom) { NanScope(); if (args.Length() < 1 || !args[0]->IsObject()) { return NanThrowError( "Usage: bitcoindjs.walletSendFrom(options)"); } Local options = Local::Cast(args[0]); async_wallet_sendfrom_data *data = new async_wallet_sendfrom_data(); String::Utf8Value addr_(options->Get(NanNew("address"))->ToString()); std::string addr = std::string(*addr_); data->address = addr; String::Utf8Value from_(options->Get(NanNew("from"))->ToString()); std::string from = std::string(*from_); std::string strAccount = from; int64_t nAmount = options->Get(NanNew("amount"))->IntegerValue(); data->nAmount = nAmount; int nMinDepth = 1; if (options->Get(NanNew("minDepth"))->IsNumber()) { nMinDepth = options->Get(NanNew("minDepth"))->IntegerValue(); } data->nMinDepth = nMinDepth; CWalletTx wtx; wtx.strFromAccount = strAccount; if (options->Get(NanNew("comment"))->IsString()) { String::Utf8Value comment_(options->Get(NanNew("comment"))->ToString()); std::string comment = std::string(*comment_); wtx.mapValue["comment"] = comment; } if (options->Get(NanNew("to"))->IsString()) { String::Utf8Value to_(options->Get(NanNew("to"))->ToString()); std::string to = std::string(*to_); wtx.mapValue["to"] = to; } data->wtx = wtx; uv_work_t *req = new uv_work_t(); req->data = data; int status = uv_queue_work(uv_default_loop(), req, async_wallet_sendfrom, (uv_after_work_cb)async_wallet_sendfrom_after); assert(status == 0); NanReturnValue(Undefined()); } static void async_wallet_sendfrom(uv_work_t *req) { async_wallet_sendfrom_data* data = static_cast(req->data); CBitcoinAddress address(data->address); if (!address.IsValid()) { data->err_msg = std::string("Invalid Bitcoin address"); return; } int64_t nAmount = data->nAmount; int nMinDepth = data->nMinDepth; CWalletTx wtx = data->wtx; std::string strAccount = data->wtx.strFromAccount; // EnsureWalletIsUnlocked(); if (pwalletMain->IsLocked()) { data->err_msg = std::string("Please enter the wallet passphrase with walletpassphrase first."); return; } // Check funds int64_t nBalance = GetAccountBalance(strAccount, nMinDepth); if (nAmount > nBalance) { data->err_msg = std::string("Account has insufficient funds"); return; } // Send std::string strError = pwalletMain->SendMoneyToDestination(address.Get(), nAmount, wtx); if (strError != "") { data->err_msg = strError; return; } data->tx_hash = wtx.GetHash().GetHex(); } static void async_wallet_sendfrom_after(uv_work_t *req) { NanScope(); async_wallet_sendfrom_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->tx_hash)) }; 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; } NAN_METHOD(WalletListTransactions) { NanScope(); if (args.Length() < 1 || !args[0]->IsObject()) { return NanThrowError( "Usage: bitcoindjs.walletListTransactions(options)"); } // Parse the account first so we don't generate a key if there's an error Local options = Local::Cast(args[0]); String::Utf8Value name_(options->Get(NanNew("name"))->ToString()); std::string strAccount = std::string(*name_); NanReturnValue(Undefined()); } NAN_METHOD(WalletListAccounts) { NanScope(); if (args.Length() < 1 || !args[0]->IsObject()) { return NanThrowError( "Usage: bitcoindjs.walletListAccounts(options)"); } Local options = Local::Cast(args[0]); int nMinDepth = 1; if (options->Get(NanNew("minDepth"))->IsNumber()) { nMinDepth = options->Get(NanNew("minDepth"))->IntegerValue(); } map mapAccountBalances; BOOST_FOREACH(const PAIRTYPE(CTxDestination, CAddressBookData)& entry, pwalletMain->mapAddressBook) { if (IsMine(*pwalletMain, entry.first)) { // This address belongs to me mapAccountBalances[entry.second.name] = 0; } } for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) { const CWalletTx& wtx = (*it).second; int64_t nFee; std::string strSentAccount; list > listReceived; list > listSent; int nDepth = wtx.GetDepthInMainChain(); if (wtx.GetBlocksToMaturity() > 0 || nDepth < 0) { continue; } wtx.GetAmounts(listReceived, listSent, nFee, strSentAccount); mapAccountBalances[strSentAccount] -= nFee; BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64_t)& s, listSent) { mapAccountBalances[strSentAccount] -= s.second; } if (nDepth >= nMinDepth) { BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64_t)& r, listReceived) { if (pwalletMain->mapAddressBook.count(r.first)) { mapAccountBalances[pwalletMain->mapAddressBook[r.first].name] += r.second; } else { mapAccountBalances[""] += r.second; } } } } list acentries; CWalletDB(pwalletMain->strWalletFile).ListAccountCreditDebit("*", acentries); BOOST_FOREACH(const CAccountingEntry& entry, acentries) { mapAccountBalances[entry.strAccount] += entry.nCreditDebit; } Local obj = NanNew(); BOOST_FOREACH(const PAIRTYPE(string, int64_t)& accountBalance, mapAccountBalances) { Local entry = NanNew(); entry->Set(NanNew("balance"), NanNew(accountBalance.second)); Local addr = NanNew(); int i = 0; BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, CAddressBookData)& item, pwalletMain->mapAddressBook) { const CBitcoinAddress& address = item.first; const std::string& strName = item.second.name; if (strName == accountBalance.first) { Local a = NanNew(); a->Set(NanNew("address"), NanNew(address.ToString())); CKeyID keyID; if (!address.GetKeyID(keyID)) { return NanThrowError("Address does not refer to a key"); } CKey vchSecret; if (!pwalletMain->GetKey(keyID, vchSecret)) { return NanThrowError("Private key for address is not known"); } std::string priv = CBitcoinSecret(vchSecret).ToString(); a->Set(NanNew("privkeycompressed"), NanNew(vchSecret.IsCompressed())); a->Set(NanNew("privkey"), NanNew(priv)); CPubKey vchPubKey; pwalletMain->GetPubKey(keyID, vchPubKey); a->Set(NanNew("pubkeycompressed"), NanNew(vchPubKey.IsCompressed())); a->Set(NanNew("pubkey"), NanNew(HexStr(vchPubKey))); addr->Set(i, a); i++; } } entry->Set(NanNew("addresses"), addr); obj->Set(NanNew(accountBalance.first), entry); } NanReturnValue(obj); } NAN_METHOD(WalletGetTransaction) { NanScope(); if (args.Length() < 1 || !args[0]->IsObject()) { return NanThrowError( "Usage: bitcoindjs.walletGetTransaction(options)"); } // Parse the account first so we don't generate a key if there's an error Local options = Local::Cast(args[0]); String::Utf8Value name_(options->Get(NanNew("name"))->ToString()); std::string strAccount = std::string(*name_); NanReturnValue(Undefined()); } NAN_METHOD(WalletBackup) { NanScope(); if (args.Length() < 1 || !args[0]->IsObject()) { return NanThrowError( "Usage: bitcoindjs.walletBackup(options)"); } // Parse the account first so we don't generate a key if there's an error Local options = Local::Cast(args[0]); String::Utf8Value name_(options->Get(NanNew("name"))->ToString()); std::string strAccount = std::string(*name_); NanReturnValue(Undefined()); } NAN_METHOD(WalletPassphrase) { NanScope(); if (args.Length() < 1 || !args[0]->IsObject()) { return NanThrowError( "Usage: bitcoindjs.walletPassphrase(options)"); } // Parse the account first so we don't generate a key if there's an error Local options = Local::Cast(args[0]); String::Utf8Value name_(options->Get(NanNew("name"))->ToString()); std::string strAccount = std::string(*name_); NanReturnValue(Undefined()); } NAN_METHOD(WalletPassphraseChange) { NanScope(); if (args.Length() < 1 || !args[0]->IsObject()) { return NanThrowError( "Usage: bitcoindjs.walletPassphraseChange(options)"); } // Parse the account first so we don't generate a key if there's an error Local options = Local::Cast(args[0]); String::Utf8Value name_(options->Get(NanNew("name"))->ToString()); std::string strAccount = std::string(*name_); NanReturnValue(Undefined()); } NAN_METHOD(WalletLock) { NanScope(); if (args.Length() < 1 || !args[0]->IsObject()) { return NanThrowError( "Usage: bitcoindjs.walletLock(options)"); } // Parse the account first so we don't generate a key if there's an error Local options = Local::Cast(args[0]); String::Utf8Value name_(options->Get(NanNew("name"))->ToString()); std::string strAccount = std::string(*name_); NanReturnValue(Undefined()); } NAN_METHOD(WalletEncrypt) { NanScope(); if (args.Length() < 1 || !args[0]->IsObject()) { return NanThrowError( "Usage: bitcoindjs.walletEncrypt(options)"); } // Parse the account first so we don't generate a key if there's an error Local options = Local::Cast(args[0]); String::Utf8Value name_(options->Get(NanNew("name"))->ToString()); std::string strAccount = std::string(*name_); NanReturnValue(Undefined()); } NAN_METHOD(WalletSetTxFee) { NanScope(); if (args.Length() < 1 || !args[0]->IsObject()) { return NanThrowError( "Usage: bitcoindjs.walletSetTxFee(options)"); } // Parse the account first so we don't generate a key if there's an error Local options = Local::Cast(args[0]); String::Utf8Value name_(options->Get(NanNew("name"))->ToString()); std::string strAccount = std::string(*name_); NanReturnValue(Undefined()); } /** * Conversions */ static inline void cblock_to_jsblock(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())); // Build merkle tree if (block.vMerkleTree.empty()) { block.BuildMerkleTree(); } Local merkle = NanNew(); int mi = 0; BOOST_FOREACH(uint256& hash, block.vMerkleTree) { merkle->Set(mi, NanNew(hash.ToString())); mi++; } obj->Set(NanNew("merkletree"), merkle); Local txs = NanNew(); int ti = 0; BOOST_FOREACH(const CTransaction& tx, block.vtx) { Local entry = NanNew(); CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION); ssTx << tx; std::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 inline void ctx_to_jstx(const CTransaction& tx, uint256 hashBlock, Local entry) { CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION); ssTx << tx; std::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); 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)); } } } } #if 0 static inline void jsblock_to_cblock(Local jsblock, CBlock& cblock) { const unsigned argc = 1; Local argv[argc] = { Local::New(jsblock) }; //Local object = Local::Cast(Context::GetCurrent()->Global()->Get(NanNew("bitcoindjs"))); //Local object = Context::GetCurrent()->Global()->Get(NanNew("bitcoindjs")); //Local toHex = Local::Cast(object->Get(NanNew("txToHex"))); //Local toHex = Local::Cast(bitcoindjs_obj->Get(NanNew("blockToHex"))); Local toHex = bitcoindjs_obj->Get(NanNew("blockToHex")).As(); Local block_hex__ = toHex->Call(Context::GetCurrent()->Global(), argc, argv); String::Utf8Value block_hex_(block_hex__->ToString()); std::string block_hex = std::string(*block_hex_); CDataStream ssData(ParseHex(block_hex), SER_NETWORK, PROTOCOL_VERSION); try { ssData >> cblock; } catch (std::exception &e) { NanThrowError("Block decode failed"); } } static inline void jstx_to_ctx(Local jstx, CTransaction& ctx) { const unsigned argc = 1; Local argv[argc] = { Local::New(jstx) }; //Local object = Local::Cast(Context::GetCurrent()->Global()->Get(NanNew("bitcoindjs"))); //Local object = Context::GetCurrent()->Global()->Get(NanNew("bitcoindjs")); //Local toHex = Local::Cast(object->Get(NanNew("txToHex"))); //Local toHex = Local::Cast(bitcoindjs_obj->Get(NanNew("txToHex"))); Local toHex = bitcoindjs_obj->Get(NanNew("txToHex")).As(); Local tx_hex__ = toHex->Call(Context::GetCurrent()->Global(), argc, argv); String::Utf8Value tx_hex_(tx_hex__->ToString()); std::string tx_hex = std::string(*tx_hex_); CDataStream ssData(ParseHex(tx_hex), SER_NETWORK, PROTOCOL_VERSION); try { ssData >> ctx; } catch (std::exception &e) { NanThrowError("TX decode failed"); } } #endif static inline void hexblock_to_cblock(std::string block_hex, CBlock& cblock) { CDataStream ssData(ParseHex(block_hex), SER_NETWORK, PROTOCOL_VERSION); try { ssData >> cblock; } catch (std::exception &e) { NanThrowError("Block decode failed"); } } static inline void hextx_to_ctx(std::string tx_hex, CTransaction& ctx) { CDataStream ssData(ParseHex(tx_hex), SER_NETWORK, PROTOCOL_VERSION); try { ssData >> ctx; } catch (std::exception &e) { NanThrowError("TX decode failed"); } } #if 0 static inline void jsblock_to_cblock(const Local obj, CBlock& block, CBlockIndex* blockindex) { obj->Get(NanNew("confirmations"))->IntegerValue(); obj->Get(NanNew("size"))->IntegerValue(); obj->Get(NanNew("height"))->IntegerValue(); obj->Get(NanNew("version"))->IntegerValue(); obj->Get(NanNew("merkleroot"))->IntegerValue(); Local txs = obj->Get("tx"); for (int ti = 0; ti < txs.Length(); ti++) { Local entry = txs->Get(ti); CTransaction tx; entry->Get(NanNew("hex")); entry->Get(NanNew("txid")); entry->Get(NanNew("version")); entry->Get(NanNew("locktime")); Local vin = entry->Get("vin"); for (int vi = 0; vi < vin.Length(); vi++) { CTxIn txin; Local in = vin->Get(vi); if (in->Get(NanNew("coinbase"))->IsString()) { in->Get(NanNew("coinbase")); } else { in->Get(NanNew("txid"), NanNew(txin.prevout.hash.GetHex())); in->Get(NanNew("vout"), NanNew((boost::int64_t)txin.prevout.n)); Local o = in->Get(NanNew("scriptSig"), o); o->Get(NanNew("asm")); o->Get(NanNew("hex")); } in->Get(NanNew("sequence")); } Local vout = entry->Get("vout"); for (unsigned int vo = 0; vo < vout.Length(); vo++) { const CTxOut txout; Local out = vout->Get(vo); out->Get(NanNew("value")); out->Get(NanNew("n")); Local o = out->Get(NanNew("scriptPubKey")); { CScript scriptPubKey; Local out = o; bool fIncludeHex = true; txnouttype type; vector addresses; int nRequired; out->Get(NanNew("asm")); if (fIncludeHex) { out->Set(NanNew("hex")); } if (!ExtractDestinations(scriptPubKey, type, addresses, nRequired)) { out->Set(NanNew("type")); } else { out->Set(NanNew("reqSigs")); out->Set(NanNew("type")); Local a = out->Get("addresses"); for (int ai = 0; ai < a.Length(); ai++) { CTxDestination addr; a->Get(ai); } } } } if (entry->Get(NanNew("blockhash"))->IsString()) { const uint256 hashBlock; entry->Get(NanNew("blockhash")); if (entry->Get(NanNew("time"))->IsNumber()) { entry->Get(NanNew("confirmations")); entry->Get(NanNew("time")); entry->Get(NanNew("blocktime")); } else { entry->Get(NanNew("confirmations")); } } } obj->Get(NanNew("time")); obj->Get(NanNew("nonce")); obj->Get(NanNew("bits")); obj->Get(NanNew("difficulty")); obj->Get(NanNew("chainwork")); if (obj->Get(NanNew("previousblockhash"))->IsString()) { obj->Get(NanNew("previousblockhash")); } if (obj->Get(NanNew("nextblockhash"))->IsString()) { CBlockIndex pnext; obj->Get(NanNew("nextblockhash")); } } static inline void jstx_to_ctx(const Local entry, CTransaction& tx, uint256 hashBlock) { entry->Get(NanNew("hex"), NanNew(strHex)); entry->Get(NanNew("txid"), NanNew(tx.GetHash().GetHex())); entry->Get(NanNew("version"), NanNew(tx.nVersion)); entry->Get(NanNew("locktime"), NanNew(tx.nLockTime)); Local vin = entry->Get("vin"); for (int vi = 0; vi < vin.Length(); vi++) { const CTxIn txin; Local in = vin->Get(vi); if (in->Get(NanNew("coinbase")->IsString()) { in->Get(NanNew("coinbase")); } else { in->Get(NanNew("txid"), NanNew(txin.prevout.hash.GetHex())); in->Get(NanNew("vout"), NanNew((boost::int64_t)txin.prevout.n)); Local o = in->Get(NanNew("scriptSig")); o->Get(NanNew("asm"), NanNew(txin.scriptSig.ToString())); o->Get(NanNew("hex"), NanNew(HexStr(txin.scriptSig.begin(), txin.scriptSig.end()))); } in->Get(NanNew("sequence"), NanNew((boost::int64_t)txin.nSequence)); } Local vout = entry->Get("vout"); for (unsigned int vo = 0; vo < vout.Length(); vo++) { CTxOut txout; Local out = vout->Get(vo); out->Get(NanNew("value")); out->Get(NanNew("n")); Local o = out->Get(NanNew("scriptPubKey")); { CScript scriptPubKey; Local out = o; out->Get(NanNew("asm")); if (out->Get(NanNew("hex"))->IsString()) { out->Get(NanNew("hex")); } if (out->Get(NanNew("type"))->IsString()) { out->Get(NanNew("type")); } else { out->Get(NanNew("reqSigs")); out->Get(NanNew("type")); Local a = out->Get("addresses"); for (int ai = 0; ai < a.Length(); ai++) { CTxDestination addr; a->get(ai); } } } } if (entry->Get(NanNew("blockhash"))->IsString()) { entry->Get(NanNew("blockhash")); CBlockIndex pindex; if (entry->Get("time")->IsNumber()) { entry->Get(NanNew("confirmations")); entry->Get(NanNew("time")); entry->Get(NanNew("blocktime")); } else { entry->Get(NanNew("confirmations")); } } } #endif /** * Init */ extern "C" void init(Handle target) { NanScope(); bitcoindjs_obj = target; 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_SET_METHOD(target, "pollMempool", PollMempool); NODE_SET_METHOD(target, "broadcastTx", BroadcastTx); NODE_SET_METHOD(target, "verifyBlock", VerifyBlock); NODE_SET_METHOD(target, "verifyTransaction", VerifyTransaction); NODE_SET_METHOD(target, "fillTransaction", FillTransaction); NODE_SET_METHOD(target, "walletNewAddress", WalletNewAddress); NODE_SET_METHOD(target, "walletGetAccountAddress", WalletGetAccountAddress); NODE_SET_METHOD(target, "walletSetAccount", WalletSetAccount); NODE_SET_METHOD(target, "walletGetAccount", WalletGetAccount); NODE_SET_METHOD(target, "walletSendTo", WalletSendTo); NODE_SET_METHOD(target, "walletSignMessage", WalletSignMessage); NODE_SET_METHOD(target, "walletVerifyMessage", WalletVerifyMessage); NODE_SET_METHOD(target, "walletGetBalance", WalletGetBalance); NODE_SET_METHOD(target, "walletGetUnconfirmedBalance", WalletGetUnconfirmedBalance); NODE_SET_METHOD(target, "walletSendFrom", WalletSendFrom); NODE_SET_METHOD(target, "walletListTransactions", WalletListTransactions); NODE_SET_METHOD(target, "walletListAccounts", WalletListAccounts); NODE_SET_METHOD(target, "walletGetTransaction", WalletGetTransaction); NODE_SET_METHOD(target, "walletBackup", WalletBackup); NODE_SET_METHOD(target, "walletPassphrase", WalletPassphrase); NODE_SET_METHOD(target, "walletPassphraseChange", WalletPassphraseChange); NODE_SET_METHOD(target, "walletLock", WalletLock); NODE_SET_METHOD(target, "walletEncrypt", WalletEncrypt); NODE_SET_METHOD(target, "walletSetTxFee", WalletSetTxFee); } NODE_MODULE(bitcoindjs, init)