From da496dcbc78fcb8675a13ac74daafd7820bbae93 Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Tue, 4 Nov 2014 16:08:31 -0800 Subject: [PATCH] getBlockHeight. getAddrTransactions. --- lib/bitcoind.js | 11 ++ src/bitcoindjs.cc | 268 +++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 250 insertions(+), 29 deletions(-) diff --git a/lib/bitcoind.js b/lib/bitcoind.js index fb4919d3..0d0d4721 100644 --- a/lib/bitcoind.js +++ b/lib/bitcoind.js @@ -330,6 +330,13 @@ Bitcoin.prototype.getBlock = function(blockHash, callback) { }); }; +Bitcoin.prototype.getBlockHeight = function(height, callback) { + return bitcoindjs.getBlock(+height, function(err, block) { + if (err) return callback(err); + return callback(null, bitcoin.block(block)); + }); +}; + Bitcoin.prototype.getTx = function(txHash, blockHash, callback) { if (!callback) { callback = blockHash; @@ -369,6 +376,10 @@ Bitcoin.prototype.getMiningInfo = function() { return bitcoindjs.getMiningInfo(); }; +Bitcoin.prototype.getAddrTransactions = function(addr, callback) { + return bitcoindjs.getAddrTransactions(addr, callback); +}; + Bitcoin.prototype.log = Bitcoin.prototype.info = function() { if (this.options.silent) return; diff --git a/src/bitcoindjs.cc b/src/bitcoindjs.cc index 7009efba..e2494b45 100644 --- a/src/bitcoindjs.cc +++ b/src/bitcoindjs.cc @@ -238,6 +238,7 @@ NAN_METHOD(GetProgress); NAN_METHOD(SetGenerate); NAN_METHOD(GetGenerate); NAN_METHOD(GetMiningInfo); +NAN_METHOD(GetAddrTransactions); NAN_METHOD(GetBlockHex); NAN_METHOD(GetTxHex); @@ -319,6 +320,12 @@ async_get_tx(uv_work_t *req); static void async_get_tx_after(uv_work_t *req); +static void +async_get_addrtx(uv_work_t *req); + +static void +async_get_addrtx_after(uv_work_t *req); + static void async_broadcast_tx(uv_work_t *req); @@ -426,18 +433,6 @@ struct async_node_data { Persistent callback; }; -/** - * async_hook_data - * Hook into bitcoind packet reception. - */ - -struct async_hook_data { - std::string err_msg; - std::string result; - CNode result_pfrom; - Persistent callback; -}; - /** * async_block_data */ @@ -445,8 +440,9 @@ struct async_hook_data { struct async_block_data { std::string err_msg; std::string hash; - CBlock result_block; - CBlockIndex* result_blockindex; + int64_t height; + CBlock cblock; + CBlockIndex* cblock_index; Persistent callback; }; @@ -462,6 +458,22 @@ struct async_tx_data { Persistent callback; }; +/** + * async_addrtx_data + */ + +typedef struct _ctx_list { + CTransaction ctx; + struct _ctx_list *next; +} ctx_list; + +struct async_addrtx_data { + std::string err_msg; + std::string addr; + ctx_list *ctxs; + Persistent callback; +}; + /** * async_broadcast_tx_data */ @@ -901,7 +913,7 @@ NAN_METHOD(IsStopped) { /** * GetBlock() - * bitcoind.getBlock(blockHash, callback) + * bitcoind.getBlock([blockHash,blockHeight], callback) * Read any block from disk asynchronously. */ @@ -909,20 +921,29 @@ NAN_METHOD(GetBlock) { NanScope(); if (args.Length() < 2 - || !args[0]->IsString() + || (!args[0]->IsString() || !args[0]->IsNumber()) || !args[1]->IsFunction()) { return NanThrowError( - "Usage: bitcoindjs.getBlock(blockHash, callback)"); + "Usage: bitcoindjs.getBlock([blockHash,blockHeight], callback)"); + } + + async_block_data *data = new async_block_data(); + + if (args[0]->IsNumber()) { + int64_t height = args[0]->IntegerValue(); + data->err_msg = std::string(""); + data->hash = std::string(""); + data->height = height; + } else { + String::Utf8Value hash_(args[0]->ToString()); + std::string hash = std::string(*hash_); + data->err_msg = std::string(""); + data->hash = hash; + data->height = -1; } - 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(); @@ -940,13 +961,27 @@ NAN_METHOD(GetBlock) { static void async_get_block(uv_work_t *req) { async_block_data* data = static_cast(req->data); + + if (data->height != -1) { + CBlockIndex* pblockindex = chainActive[data->height]; + CBlock cblock; + if (ReadBlockFromDisk(cblock, pblockindex)) { + data->cblock = cblock; + data->cblock_index = pblockindex; + } else { + data->err_msg = std::string("get_block(): failed."); + } + return; + } + std::string strHash = data->hash; uint256 hash(strHash); CBlock cblock; CBlockIndex* pblockindex = mapBlockIndex[hash]; + if (ReadBlockFromDisk(cblock, pblockindex)) { - data->result_block = cblock; - data->result_blockindex = pblockindex; + data->cblock = cblock; + data->cblock_index = pblockindex; } else { data->err_msg = std::string("get_block(): failed."); } @@ -967,8 +1002,8 @@ async_get_block_after(uv_work_t *req) { node::FatalException(try_catch); } } else { - const CBlock& cblock = data->result_block; - CBlockIndex* cblock_index = data->result_blockindex; + const CBlock& cblock = data->cblock; + CBlockIndex* cblock_index = data->cblock_index; Local jsblock = NanNew(); cblock_to_jsblock(cblock, cblock_index, jsblock, false); @@ -1613,8 +1648,8 @@ async_get_progress_after(uv_work_t *req) { node::FatalException(try_catch); } } else { - const CBlock& cblock = data->result_block; - CBlockIndex* cblock_index = data->result_blockindex; + const CBlock& cblock = data->cblock; + CBlockIndex* cblock_index = data->cblock_index; Local jsblock = NanNew(); cblock_to_jsblock(cblock, cblock_index, jsblock, false); @@ -1800,6 +1835,180 @@ NAN_METHOD(GetMiningInfo) { NanReturnValue(obj); } +/** + * GetAddrTransactions() + * bitcoind.getAddrTransactions(addr, callback) + * Read any transaction from disk asynchronously. + */ + +NAN_METHOD(GetAddrTransactions) { + NanScope(); + + if (args.Length() < 2 + || !args[0]->IsString() + || !args[1]->IsFunction()) { + return NanThrowError( + "Usage: bitcoindjs.getAddrTransactions(addr, callback)"); + } + + String::Utf8Value addr_(args[0]->ToString()); + Local callback = Local::Cast(args[2]); + + Persistent cb; + cb = Persistent::New(callback); + + std::string addr = std::string(*addr_); + + async_addrtx_data *data = new async_addrtx_data(); + data->err_msg = std::string(""); + data->addr = addr; + 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_addrtx, + (uv_after_work_cb)async_get_addrtx_after); + + assert(status == 0); + + NanReturnValue(Undefined()); +} + +static void +async_get_addrtx(uv_work_t *req) { + async_addrtx_data* data = static_cast(req->data); + + CBitcoinAddress address = CBitcoinAddress(data->addr); + if (!address.IsValid()) { + data->err_msg = std::string("bad addr"); + return; + } + + CScript expected = GetScriptForDestination(address.Get()); + + int64_t i = 0; + int64_t height = chainActive.Height(); + + for (; i <= height; i++) { + CBlockIndex* pblockindex = chainActive[i]; + CBlock cblock; + if (ReadBlockFromDisk(cblock, pblockindex)) { + BOOST_FOREACH(const CTransaction& ctx, cblock.vtx) { + // vin + BOOST_FOREACH(const CTxIn& txin, ctx.vin) { + // prev_txid + // std::string prev_txid = txin.prevout.hash.GetHex(); + + // asm + // std::string input_asm = txin.scriptSig.ToString(); + + // hex + // std::string input_hex = HexStr(txin.scriptSig.begin(), txin.scriptSig.end()); + + // format: + // { sig, pubkey } + // pseudocode: + // pubkey = input_asm.split().get(1); + // addr = base58(dsha(pubkey)); + + if (txin.scriptSig == expected) { + ctx_list *item = new ctx_list(); + item->ctx = ctx; + if (data->ctxs == NULL) { + data->ctxs = item; + } else { + data->ctxs->next = item; + data->ctxs = item; + } + goto done; + } + } + + // vout + for (unsigned int vo = 0; vo < ctx.vout.size(); vo++) { + const CTxOut& txout = ctx.vout[vo]; + const CScript& scriptPubKey = txout.scriptPubKey; + txnouttype type; + vector addresses; + int nRequired; + if (ExtractDestinations(scriptPubKey, type, addresses, nRequired)) { + BOOST_FOREACH(const CTxDestination& addr, addresses) { + std::string str_addr = CBitcoinAddress(addr).ToString(); + if (data->addr == str_addr) { + ctx_list *item = new ctx_list(); + item->ctx = ctx; + if (data->ctxs == NULL) { + data->ctxs = item; + } else { + data->ctxs->next = item; + data->ctxs = item; + } + goto done; + } + } + } + } + } + +done: + continue; + } else { + data->err_msg = std::string("get_addrtx(): failed."); + break; + } + } + return; +} + +static void +async_get_addrtx_after(uv_work_t *req) { + NanScope(); + async_addrtx_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 result = NanNew(); + Local tx = NanNew(); + int i = 0; + ctx_list *next; + for (ctx_list *item = data->ctxs; item; item = next) { + Local jstx = NanNew(); + ctx_to_jstx(item->ctx, 0, jstx); + tx->Set(i, jstx); + i++; + next = item->next; + delete item; + } + result->Set(NanNew("address"), NanNew(data->addr)); + result->Set(NanNew("tx"), tx); + Local argv[argc] = { + Local::New(Null()), + Local::New(result) + }; + TryCatch try_catch; + data->callback->Call(Context::GetCurrent()->Global(), argc, argv); + if (try_catch.HasCaught()) { + node::FatalException(try_catch); + } + } + + data->callback.Dispose(); + + delete data; + delete req; +} + /** * GetBlockHex() * bitcoindjs.getBlockHex(callback) @@ -5234,6 +5443,7 @@ init(Handle target) { NODE_SET_METHOD(target, "setGenerate", SetGenerate); NODE_SET_METHOD(target, "getGenerate", GetGenerate); NODE_SET_METHOD(target, "getMiningInfo", GetMiningInfo); + NODE_SET_METHOD(target, "getAddrTransactions", GetAddrTransactions); NODE_SET_METHOD(target, "getBlockHex", GetBlockHex); NODE_SET_METHOD(target, "getTxHex", GetTxHex); NODE_SET_METHOD(target, "blockFromHex", BlockFromHex);