diff --git a/example/index.js b/example/index.js index fcc0cca8..2bf11467 100755 --- a/example/index.js +++ b/example/index.js @@ -37,6 +37,7 @@ bitcoind.on('open', function(status) { bitcoind.on('block', function(block) { print('Found Block:'); print(block); + print(block._getHashJS() === block.getHash()); }); } @@ -44,10 +45,12 @@ bitcoind.on('open', function(status) { bitcoind.on('tx', function(tx) { print('Found TX:'); print(tx); + print(tx._getHashJS() === tx.getHash()); }); bitcoind.on('mptx', function(mptx) { print('Found mempool TX:'); print(mptx); + print(mptx._getHashJS() === mptx.getHash()); }); } diff --git a/lib/bitcoind.js b/lib/bitcoind.js index cfe25aef..9591ca30 100644 --- a/lib/bitcoind.js +++ b/lib/bitcoind.js @@ -374,8 +374,7 @@ Block.isBlock = function(block) { return block._blockFlag === _blockFlag; }; -// NOTE: Could just call tx.GetHash().ToString() in C++ -Block.prototype.getHash = function(enc) { +Block.prototype._getHashJS = function(enc) { if (!this._hash) { this._hash = utils.dsha256(this.rawHeader(), 'hex'); } @@ -384,6 +383,12 @@ Block.prototype.getHash = function(enc) { : utils.dsha256(this.rawHeader()); }; +Block.prototype.getHash = function(enc) { + var hash = bitcoindjs.getBlockHash(this); + var buf = new Buffer(hash, 'hex'); + return enc ? buf.toString(enc) : buf; +}; + Block.prototype.rawHeader = function() { var res = new Array(80); @@ -558,8 +563,7 @@ Transaction.fill = function(tx, options) { return isTx ? true : tx; }; -// NOTE: Could just call tx.GetHash().ToString() in C++ -Transaction.prototype.getHash = function(enc) { +Transaction.prototype._getHashJS = function(enc) { if (!this._hash) { this._hash = utils.dsha256(this.toBinary(), 'hex'); } @@ -568,6 +572,12 @@ Transaction.prototype.getHash = function(enc) { : utils.dsha256(this.toBinary()); }; +Transaction.prototype.getHash = function(enc) { + var hash = bitcoindjs.getTxHash(this); + var buf = new Buffer(hash, 'hex'); + return enc ? buf.toString(enc) : buf; +}; + Transaction.prototype.isCoinbase = function() { return this.vin.length === 1 && this.vin[0].coinbase; }; diff --git a/src/bitcoindjs.cc b/src/bitcoindjs.cc index eca8e5eb..07289b93 100644 --- a/src/bitcoindjs.cc +++ b/src/bitcoindjs.cc @@ -142,6 +142,8 @@ NAN_METHOD(BroadcastTx); NAN_METHOD(VerifyBlock); NAN_METHOD(VerifyTransaction); NAN_METHOD(FillTransaction); +NAN_METHOD(GetBlockHash); +NAN_METHOD(GetTxHash); NAN_METHOD(WalletNewAddress); NAN_METHOD(WalletGetAccountAddress); @@ -284,7 +286,7 @@ struct async_tx_data { std::string err_msg; std::string txHash; std::string blockHash; - CTransaction result_tx; + CTransaction ctx; Persistent callback; }; @@ -321,7 +323,8 @@ struct async_poll_mempool_data { struct async_broadcast_tx_data { std::string err_msg; - std::string tx_hex; + Persistent jstx; + CTransaction ctx; std::string tx_hash; bool override_fees; bool own_only; @@ -777,7 +780,7 @@ async_get_tx(uv_work_t *req) { CTransaction ctx; if (GetTransaction(hash, ctx, block_hash, true)) { - data->result_tx = ctx; + data->ctx = ctx; } else { data->err_msg = std::string("get_tx(): failed."); } @@ -790,7 +793,7 @@ async_get_tx_after(uv_work_t *req) { std::string txHash = data->txHash; std::string blockHash = data->blockHash; - CTransaction ctx = data->result_tx; + CTransaction ctx = data->ctx; uint256 hash(txHash); uint256 block_hash(blockHash); @@ -805,12 +808,7 @@ async_get_tx_after(uv_work_t *req) { node::FatalException(try_catch); } } else { - CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION); - ssTx << ctx; - std::string strHex = HexStr(ssTx.begin(), ssTx.end()); - Local jstx = NanNew(); - jstx->Set(NanNew("hex"), NanNew(strHex)); ctx_to_jstx(ctx, block_hash, jstx); const unsigned argc = 2; @@ -1079,16 +1077,18 @@ NAN_METHOD(BroadcastTx) { Local jstx = Local::Cast(args[0]); Local callback = Local::Cast(args[3]); - String::Utf8Value tx_hex_(jstx->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); + data->jstx = Persistent::New(jstx); + + CTransaction ctx; + jstx_to_ctx(jstx, ctx); + data->ctx = ctx; + uv_work_t *req = new uv_work_t(); req->data = data; @@ -1105,9 +1105,6 @@ 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 ctx; - bool fOverrideFees = false; bool fOwnOnly = false; @@ -1119,14 +1116,7 @@ async_broadcast_tx(uv_work_t *req) { fOwnOnly = true; } - // jstx_to_ctx(jstx, ctx); - - try { - ssData >> ctx; - } catch (std::exception &e) { - data->err_msg = std::string("TX decode failed"); - return; - } + CTransaction ctx = data->ctx; uint256 hashTx = ctx.GetHash(); @@ -1173,18 +1163,11 @@ async_broadcast_tx_after(uv_work_t *req) { node::FatalException(try_catch); } } else { - // jstx_to_ctx(jstx, ctx); - CDataStream ssData(ParseHex(data->tx_hex), SER_NETWORK, PROTOCOL_VERSION); - CTransaction ctx; - ssData >> ctx; - Local jstx = NanNew(); - ctx_to_jstx(ctx, 0, jstx); - const unsigned argc = 3; Local argv[argc] = { Local::New(Null()), Local::New(NanNew(data->tx_hash)), - Local::New(jstx) + Local::New(data->jstx) }; TryCatch try_catch; data->callback->Call(Context::GetCurrent()->Global(), argc, argv); @@ -1216,10 +1199,8 @@ NAN_METHOD(VerifyBlock) { String::Utf8Value block_hex_(jsblock->Get(NanNew("hex"))->ToString()); std::string block_hex = std::string(*block_hex_); - // jsblock_to_cblock(jsblock, cblock); CBlock cblock; - CDataStream ssData(ParseHex(block_hex), SER_NETWORK, PROTOCOL_VERSION); - ssData >> cblock; + jsblock_to_cblock(jsblock, cblock); CValidationState state; bool valid = CheckBlock(cblock, state); @@ -1244,10 +1225,8 @@ NAN_METHOD(VerifyTransaction) { String::Utf8Value tx_hex_(jstx->Get(NanNew("hex"))->ToString()); std::string tx_hex = std::string(*tx_hex_); - // jstx_to_ctx(jstx, ctx); CTransaction ctx; - CDataStream ssData(ParseHex(tx_hex), SER_NETWORK, PROTOCOL_VERSION); - ssData >> ctx; + jstx_to_ctx(jstx, ctx); CValidationState state; bool valid = CheckTransaction(ctx, state); @@ -1272,10 +1251,8 @@ NAN_METHOD(FillTransaction) { String::Utf8Value tx_hex_(jstx->Get(NanNew("hex"))->ToString()); std::string tx_hex = std::string(*tx_hex_); - // jstx_to_ctx(jstx, ctx); CTransaction ctx; - CDataStream ssData(ParseHex(tx_hex), SER_NETWORK, PROTOCOL_VERSION); - ssData >> ctx; + jstx_to_ctx(jstx, ctx); // Get total value of outputs // Get the scriptPubKey of the first output (presumably our destination) @@ -1329,6 +1306,50 @@ NAN_METHOD(FillTransaction) { NanReturnValue(new_jstx); } +/** + * GetBlockHash + */ + +NAN_METHOD(GetBlockHash) { + NanScope(); + + if (args.Length() < 1 || !args[0]->IsObject()) { + return NanThrowError( + "Usage: bitcoindjs.getBlockHash(block)"); + } + + Local jsblock = Local::Cast(args[0]); + + CBlock cblock; + jsblock_to_cblock(jsblock, cblock); + + Local hash = NanNew(cblock.GetHash().GetHex().c_str()); + + NanReturnValue(hash); +} + +/** + * GetTxHash + */ + +NAN_METHOD(GetTxHash) { + NanScope(); + + if (args.Length() < 1 || !args[0]->IsObject()) { + return NanThrowError( + "Usage: bitcoindjs.getTxHash(tx)"); + } + + Local jstx = Local::Cast(args[0]); + + CTransaction ctx; + jstx_to_ctx(jstx, ctx); + + Local hash = NanNew(ctx.GetHash().GetHex()); + + NanReturnValue(hash); +} + /** * Wallet */ @@ -2446,99 +2467,8 @@ cblock_to_jsblock(const CBlock& cblock, const CBlockIndex* cblock_index, Local jstx = NanNew(); - - // const uint256 block_hash = cblock.GetHash(); - // ctx_to_jstx(ctx, block_hash, jstx); - - CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION); - ssTx << ctx; - std::string strHex = HexStr(ssTx.begin(), ssTx.end()); - jstx->Set(NanNew("hex"), NanNew(strHex)); - - jstx->Set(NanNew("txid"), NanNew(ctx.GetHash().GetHex())); - jstx->Set(NanNew("hash"), NanNew(ctx.GetHash().GetHex())); - jstx->Set(NanNew("version"), NanNew(ctx.nVersion)); - jstx->Set(NanNew("locktime"), NanNew(ctx.nLockTime)); - - Local vin = NanNew(); - int vi = 0; - BOOST_FOREACH(const CTxIn& txin, ctx.vin) { - Local in = NanNew(); - if (ctx.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++; - } - jstx->Set(NanNew("vin"), vin); - - Local vout = NanNew(); - for (unsigned int vo = 0; vo < ctx.vout.size(); vo++) { - const CTxOut& txout = ctx.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); - } - jstx->Set(NanNew("vout"), vout); - - { - const uint256 block_hash = cblock.GetHash(); - if (block_hash != 0) { - jstx->Set(NanNew("blockhash"), NanNew(block_hash.GetHex())); - map::iterator mi = mapBlockIndex.find(block_hash); - if (mi != mapBlockIndex.end() && (*mi).second) { - CBlockIndex* cblock_index = (*mi).second; - if (chainActive.Contains(cblock_index)) { - jstx->Set(NanNew("confirmations"), - NanNew(1 + chainActive.Height() - cblock_index->nHeight)); - jstx->Set(NanNew("time"), NanNew((boost::int64_t)cblock_index->nTime)); - jstx->Set(NanNew("blocktime"), NanNew((boost::int64_t)cblock_index->nTime)); - } else { - jstx->Set(NanNew("confirmations"), NanNew(0)); - } - } - } - } - + const uint256 block_hash = cblock.GetHash(); + ctx_to_jstx(ctx, block_hash, jstx); txs->Set(ti, jstx); ti++; } @@ -2676,62 +2606,7 @@ jsblock_to_cblock(const Local jsblock, CBlock& cblock) { for (unsigned int ti = 0; ti < txs->Length(); ti++) { Local jstx = Local::Cast(txs->Get(ti)); CTransaction ctx; - - // jstx_to_ctx(jstx, ctx); - - ctx.nVersion = jstx->Get(NanNew("version"))->IntegerValue(); - ctx.nLockTime = jstx->Get(NanNew("locktime"))->IntegerValue(); - - Local vin = Local::Cast(jstx->Get(NanNew("vin"))); - for (unsigned int vi = 0; vi < vin->Length(); vi++) { - CTxIn txin; - Local in = Local::Cast(vin->Get(vi)); - - std::string shash_; - if (in->Get(NanNew("coinbase"))->IsString()) { - String::AsciiValue shash__(in->Get(NanNew("coinbase"))->ToString()); - shash_ = *shash__; - } else { - String::AsciiValue shash__(in->Get(NanNew("scriptSig"))->ToString()); - shash_ = *shash__; - } - if (shash_[1] != 'x') shash_ = "0x" + shash_; - uint256 shash(shash_); - CScript scriptSig(shash); - - txin.scriptSig = scriptSig; - - String::AsciiValue phash__(in->Get(NanNew("txid"))->ToString()); - std::string phash_ = *phash__; - if (phash_[1] != 'x') phash_ = "0x" + phash_; - uint256 phash(phash_); - - txin.prevout.hash = phash; - txin.prevout.n = (boost::int64_t)in->Get(NanNew("vout"))->IntegerValue(); - txin.nSequence = (boost::int64_t)in->Get(NanNew("sequence"))->IntegerValue(); - - ctx.vin.push_back(txin); - } - - Local vout = Local::Cast(jstx->Get(NanNew("vout"))); - for (unsigned int vo = 0; vo < vout->Length(); vo++) { - CTxOut txout; - Local out = Local::Cast(vout->Get(vo)); - - txout.nValue = (int64_t)out->Get(NanNew("value"))->IntegerValue(); - - Local spk = Local::Cast(out->Get(NanNew("scriptPubKey"))); - String::AsciiValue phash__(spk->Get(NanNew("hex"))); - std::string phash_ = *phash__; - if (phash_[1] != 'x') phash_ = "0x" + phash_; - uint256 phash(phash_); - CScript scriptPubKey(phash); - - txout.scriptPubKey = scriptPubKey; - - ctx.vout.push_back(txout); - } - + jstx_to_ctx(jstx, ctx); cblock.vtx.push_back(ctx); } } @@ -2815,6 +2690,8 @@ init(Handle target) { NODE_SET_METHOD(target, "verifyBlock", VerifyBlock); NODE_SET_METHOD(target, "verifyTransaction", VerifyTransaction); NODE_SET_METHOD(target, "fillTransaction", FillTransaction); + NODE_SET_METHOD(target, "getBlockHash", GetBlockHash); + NODE_SET_METHOD(target, "getTxHash", GetTxHash); NODE_SET_METHOD(target, "walletNewAddress", WalletNewAddress); NODE_SET_METHOD(target, "walletGetAccountAddress", WalletGetAccountAddress);