diff --git a/example/index.js b/example/index.js index 35ae8008..29ed2dd6 100755 --- a/example/index.js +++ b/example/index.js @@ -2,6 +2,7 @@ process.title = 'bitcoind.js'; +var util = require('util'); var bitcoind = require('../')(); bitcoind.start(function(err) { @@ -11,11 +12,19 @@ bitcoind.start(function(err) { bitcoind.on('open', function(status) { setTimeout(function() { var genesis = '0x000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f'; - bitcoind.getBlock(genesis, function(err, block) { + return bitcoind.getBlock(genesis, function(err, block) { if (err) return console.log(err.message); - console.log(block); + print(block); }); }, 1000); console.log('bitcoind: status="%s"', status); }); }); + +function inspect(obj) { + return util.inspect(obj, null, 20, true); +} + +function print(obj) { + return process.stdout.write(inspect(obj) + '\n'); +} diff --git a/lib/bitcoind.js b/lib/bitcoind.js index f88066af..bed25b12 100644 --- a/lib/bitcoind.js +++ b/lib/bitcoind.js @@ -120,6 +120,7 @@ Bitcoin.prototype.start = function(callback) { }; Bitcoin.prototype.getBlock = function(hash, callback) { + return bitcoindjs.getBlock(hash, callback); return bitcoindjs.getBlock(hash, function(err, block) { if (err) return callback(err) return callback(null, JSON.parse(block)); diff --git a/src/bitcoindjs.cc b/src/bitcoindjs.cc index 938108d1..4d421c0e 100644 --- a/src/bitcoindjs.cc +++ b/src/bitcoindjs.cc @@ -81,7 +81,8 @@ // using namespace json_spirit; -extern json_spirit::Object blockToJSON(const CBlock& block, const CBlockIndex* blockindex); +extern json_spirit::Object +blockToJSON(const CBlock& block, const CBlockIndex* blockindex); /** * Bitcoin Globals @@ -183,6 +184,9 @@ async_get_block(uv_work_t *req); static void async_get_block_after(uv_work_t *req); +Local +block_to_obj(const CBlock& block, const CBlockIndex* blockindex); + extern "C" void init(Handle); @@ -196,6 +200,9 @@ struct async_block_data { std::string hash; std::string err_msg; std::string result; + Local result_obj; + CBlock result_block; + CBlockIndex* result_blockindex; Persistent callback; }; @@ -269,7 +276,7 @@ NAN_METHOD(StartBitcoind) { // Run bitcoind's StartNode() on a separate thread. // - async_node_data* data_start_node = new async_node_data(); + 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); @@ -297,7 +304,7 @@ NAN_METHOD(StartBitcoind) { static void async_start_node_work(uv_work_t *req) { - async_node_data* node_data = static_cast(req->data); + async_node_data *node_data = static_cast(req->data); start_node(); node_data->result = (char *)strdup("start_node(): bitcoind opened."); } @@ -310,7 +317,7 @@ async_start_node_work(uv_work_t *req) { static void async_start_node_after(uv_work_t *req) { NanScope(); - async_node_data* node_data = static_cast(req->data); + 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)); @@ -389,7 +396,7 @@ start_node(void) { static void start_node_thread(void) { boost::thread_group threadGroup; - boost::thread* detectShutdownThread = NULL; + boost::thread *detectShutdownThread = NULL; const int argc = 0; const char *argv[argc + 1] = { @@ -545,7 +552,7 @@ parse_logs(int **out_pipe, int **log_pipe) { static void async_parse_logs(uv_work_t *req) { - async_log_data* log_data = static_cast(req->data); + async_log_data *log_data = static_cast(req->data); parse_logs(log_data->out_pipe, log_data->log_pipe); log_data->err_msg = (char *)strdup("parse_logs(): failed."); } @@ -553,7 +560,7 @@ async_parse_logs(uv_work_t *req) { static void async_parse_logs_after(uv_work_t *req) { NanScope(); - async_log_data* log_data = static_cast(req->data); + async_log_data *log_data = static_cast(req->data); if (log_data->err_msg != NULL) { Local err = Exception::Error(String::New(log_data->err_msg)); @@ -595,7 +602,7 @@ NAN_METHOD(StopBitcoind) { // Run bitcoind's StartShutdown() on a separate thread. // - async_node_data* data_stop_node = new async_node_data(); + 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); @@ -619,7 +626,7 @@ NAN_METHOD(StopBitcoind) { static void async_stop_node_work(uv_work_t *req) { - async_node_data* node_data = static_cast(req->data); + async_node_data *node_data = static_cast(req->data); StartShutdown(); node_data->result = (char *)strdup("stop_node(): bitcoind shutdown."); } @@ -668,8 +675,8 @@ async_stop_node_after(uv_work_t *req) { } /** - * GetBlock(height) - * bitcoind.getBlock(height) + * GetBlock(hash, callback) + * bitcoind.getBlock(hash, callback) */ NAN_METHOD(GetBlock) { @@ -717,10 +724,16 @@ async_get_block(uv_work_t *req) { CBlock block; CBlockIndex* pblockindex = mapBlockIndex[hash]; if (ReadBlockFromDisk(block, pblockindex)) { +#if 0 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; +#endif + //Local out = block_to_obj(block, pblockindex); + //data->result_obj = out; + data->result_block = block; + data->result_blockindex = pblockindex; } else { data->err_msg = std::string("get_block(): failed."); } @@ -741,10 +754,133 @@ async_get_block_after(uv_work_t *req) { node::FatalException(try_catch); } } else { + const CBlock& block = data->result_block; + const CBlockIndex* blockindex = data->result_blockindex; + + Local obj = NanNew(); + 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 i = 0; + BOOST_FOREACH(const CTransaction& tx, block.vtx) { + Local entry = NanNew(); + 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 e = 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(e, in); + } + entry->Set(NanNew("vin"), vin); + + Local vout = NanNew(); + for (unsigned int j = 0; j < tx.vout.size(); j++) { + const CTxOut& txout = tx.vout[j]; + 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)j)); + + // 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 k = 0; + BOOST_FOREACH(const CTxDestination& addr, addresses) { + a->Set(k, NanNew(CBitcoinAddress(addr).ToString())); + k++; + } + out->Set(NanNew("addresses"), a); + } + } + out->Set(NanNew("scriptPubKey"), o); + + vout->Set(j, out); + } + entry->Set(NanNew("vout"), vout); + + // TxToJSON(tx, hashBlock, result); + { + 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(i, entry); + i++; + } + 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())); + } + const unsigned argc = 2; Local argv[argc] = { Local::New(Null()), - Local::New(NanNew(data->result)) + //Local::New(NanNew("")) + //Local::New(NanNew(data->result)) + //Local::New(data->result_obj) + Local::New(obj) }; TryCatch try_catch; data->callback->Call(Context::GetCurrent()->Global(), argc, argv); @@ -759,6 +895,44 @@ async_get_block_after(uv_work_t *req) { delete req; } +Local +block_to_obj(const CBlock& block, const CBlockIndex* blockindex) { + Local obj = NanNew(); + 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))); + return obj; + +#if 0 + Object result; + result.push_back(Pair("hash", block.GetHash().GetHex())); + CMerkleTx txGen(block.vtx[0]); + txGen.SetMerkleBranch(&block); + result.push_back(Pair("confirmations", (int)txGen.GetDepthInMainChain())); + result.push_back(Pair("size", (int)::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION))); + result.push_back(Pair("height", blockindex->nHeight)); + result.push_back(Pair("version", block.nVersion)); + result.push_back(Pair("merkleroot", block.hashMerkleRoot.GetHex())); + Array txs; + BOOST_FOREACH(const CTransaction&tx, block.vtx) + txs.push_back(tx.GetHash().GetHex()); + result.push_back(Pair("tx", txs)); + result.push_back(Pair("time", (boost::int64_t)block.GetBlockTime())); + result.push_back(Pair("nonce", (boost::uint64_t)block.nNonce)); + result.push_back(Pair("bits", HexBits(block.nBits))); + result.push_back(Pair("difficulty", GetDifficulty(blockindex))); + result.push_back(Pair("chainwork", blockindex->nChainWork.GetHex())); + if (blockindex->pprev) + result.push_back(Pair("previousblockhash", blockindex->pprev->GetBlockHash().GetHex())); + CBlockIndex *pnext = chainActive.Next(blockindex); + if (pnext) + result.push_back(Pair("nextblockhash", pnext->GetBlockHash().GetHex())); + return result; +#endif +} + /** * Init */