diff --git a/example/index.js b/example/index.js index 2bf11467..ca388753 100755 --- a/example/index.js +++ b/example/index.js @@ -33,11 +33,34 @@ bitcoind.on('open', function(status) { getBlocks(bitcoind); } + function hashCompare(obj) { + if (obj.txid) { + print('tx.txid: %s', obj.txid); + } else { + print('block.hash: %s', obj.hash); + } + if (obj.txid) { + print('tx.hex: %s', obj.hex || obj.toHex()); + } else { + print('block.hex: %s', obj.hex || obj.toHex()); + } + var jshash = obj._getHashJS('hex'); + var hash = obj.getHash('hex'); + print('jshash === hash: %s', jshash == hash); + print('jshash: %s', jshash); + print('hash: %s', hash); + var jshex = obj._toHexJS(); + var hex = obj.toHex(); + print('jshex === hex: %s', jshex == hex); + print('jshex: %s', jshex); + print('hex: %s', hex); + } + if (argv['on-block']) { bitcoind.on('block', function(block) { print('Found Block:'); print(block); - print(block._getHashJS() === block.getHash()); + hashCompare(block); }); } @@ -45,12 +68,12 @@ bitcoind.on('open', function(status) { bitcoind.on('tx', function(tx) { print('Found TX:'); print(tx); - print(tx._getHashJS() === tx.getHash()); + hashCompare(tx); }); bitcoind.on('mptx', function(mptx) { print('Found mempool TX:'); print(mptx); - print(mptx._getHashJS() === mptx.getHash()); + hashCompare(mptx); }); } diff --git a/lib/bitcoind.js b/lib/bitcoind.js index 9591ca30..38f31435 100644 --- a/lib/bitcoind.js +++ b/lib/bitcoind.js @@ -357,36 +357,49 @@ function Block(data) { var self = this; - this._blockFlag = _blockFlag; - Object.keys(data).forEach(function(key) { if (!self[key]) { self[key] = data[key]; } }); - this.vMerkleTree = data.merkletree || data.vMerkleTree; + this.tx = this.tx.map(function(tx) { + return bitcoin.tx(tx); + }); - this.toHex(); + // this.hex = this.toHex(); } +Object.defineProperty(Block.prototype, '_blockFlag', { + __proto__: null, + configurable: false, + enumerable: false, + writable: false, + value: _blockFlag +}); + Block.isBlock = function(block) { return block._blockFlag === _blockFlag; }; Block.prototype._getHashJS = function(enc) { - if (!this._hash) { - this._hash = utils.dsha256(this.rawHeader(), 'hex'); + if (!this.hashJS) { + this.hashJS = utils.dsha256(this.rawHeader(), 'hex'); } return enc === 'hex' - ? this._hash + ? this.hashJS : 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; + var data = bitcoindjs.getBlockHex(this); + if (!this.hash) { + this.hash = data.hash; + } + if (enc === 'hex') return this.hash; + var buf = new Buffer(data.hash, 'hex'); + var out = enc ? buf.toString(enc) : buf; + return out; }; Block.prototype.rawHeader = function() { @@ -406,21 +419,46 @@ Block.prototype.verify = function() { return this.verified = this.verified || bitcoindjs.verifyBlock(this); }; +Block.prototype.toHex = function() { + return Block.toHex(this); + return this.hex = this.hex || Block.toHex(this); +}; + +Block.toHex = function(block) { + var data = bitcoindjs.getBlockHex(block); + return data.hex; +}; + Block.prototype.toBinary = function() { return Block.toBinary(this); }; -Block.toBinary = function(block, type) { +Block.toBinary = function(block) { + var data = bitcoindjs.getBlockHex(block); + return new Buffer(data.hex, 'hex'); +}; + +Block.prototype._toHexJS = function() { + return Block._toHexJS(this); + return this.hexJS = this.hexJS || Block._toHexJS(this); +}; + +Block._toHexJS = function(block) { + return Block._toBinaryJS(block).toString('hex'); +}; + +Block.prototype._toBinaryJS = function() { + return Block._toBinaryJS(this); +}; + +Block._toBinaryJS = function(block) { var p = []; var off = 0; // version - off += utils.writeU32(p, block.nVersion || block.version, off); + off += utils.writeU32(p, block.version, off); // prev_block - if (!block.previousblockhash) { - block.previousblockhash = '0000000000000000000000000000000000000000000000000000000000000000'; - } utils.toArray(block.previousblockhash, 'hex').forEach(function(ch) { p[off++] = ch; }); @@ -441,47 +479,19 @@ Block.toBinary = function(block, type) { assert.equal(off, 80); - if (type === 'merkle') { - // txn_count - off += utils.writeU32(p, block.txn_count, off); - // hash count - off += utils.varint(p, block.hash_count, off); - // hashes - block.hashes.forEach(function(hash) { - utils.toArray(hash, 'hex').forEach(function(ch) { - p[off++] = ch; - }); + // txn_count + off += utils.varint(p, block.tx.length, off); + // txs + block.tx.forEach(function(tx) { + tx = bitcoin.tx(tx); + utils.toArray(tx._toHexJS(), 'hex').forEach(function(ch) { + p[off++] = ch; }); - // flag count - off += utils.varint(p, block.flags.length, off); - // flags - block.flags.forEach(function(flag) { - p[off++] = flag; - }); - } else { - // txn_count - off += utils.varint(p, block.tx.length, off); - // txs - block.tx.forEach(function(tx) { - tx = bitcoin.tx(tx); - tx.toHex(); - utils.toArray(tx.hex, 'hex').forEach(function(ch) { - p[off++] = ch; - }); - }); - } + }); return new Buffer(p); }; -Block.prototype.toHex = function() { - return this.hex = this.hex || Block.toHex(this); -}; - -Block.toHex = function(block) { - return Block.toBinary(block).toString('hex'); -}; - /** * Transaction */ @@ -499,34 +509,23 @@ function Transaction(data) { var self = this; - this._txFlag = _txFlag; - - this.nMinTxFee = data.nMinTxFee || data.minTxFee || 1000; - this.nMinRelayTxFee = data.nMinRelayTxFee || data.minRelayTxFee || 1000; - this.CURRENT_VERSION = 1; - this.nVersion = data.nVersion || data.version || this.CURRENT_VERSION; - this.vin = data.vin || []; - this.vout = data.vout || []; - this.nLockTime = data.nLockTime || data.locktime || 0; - Object.keys(data).forEach(function(key) { if (!self[key]) { self[key] = data[key]; } }); - if (this.isCoinbase()) { - this.vin[0].txid = Array(64 + 1).join('0'); - this.vin[0].vout = 0; - this.vin[0].scriptSig = { - asm: null, - hex: this.vin[0].coinbase - }; - } - - this.toHex(); + // this.hex = this.toHex(); } +Object.defineProperty(Transaction.prototype, '_txFlag', { + __proto__: null, + configurable: false, + enumerable: false, + writable: false, + value: _txFlag +}); + Transaction.isTransaction = Transaction.isTx = function(tx) { return tx._txFlag === _txFlag; @@ -564,18 +563,23 @@ Transaction.fill = function(tx, options) { }; Transaction.prototype._getHashJS = function(enc) { - if (!this._hash) { - this._hash = utils.dsha256(this.toBinary(), 'hex'); + if (!this.hashJS) { + this.hashJS = utils.dsha256(this._toBinaryJS(), 'hex'); } return enc === 'hex' - ? this._hash - : utils.dsha256(this.toBinary()); + ? this.hashJS + : utils.dsha256(this._toBinaryJS()); }; Transaction.prototype.getHash = function(enc) { - var hash = bitcoindjs.getTxHash(this); - var buf = new Buffer(hash, 'hex'); - return enc ? buf.toString(enc) : buf; + var data = bitcoindjs.getTxHex(this); + if (!this.hash) { + this.hash = data.hash; + } + if (enc === 'hex') return this.hash; + var buf = new Buffer(data.hash, 'hex'); + var out = enc ? buf.toString(enc) : buf; + return out; }; Transaction.prototype.isCoinbase = function() { @@ -583,40 +587,53 @@ Transaction.prototype.isCoinbase = function() { }; Transaction.prototype.toHex = function() { + return Transaction.toHex(this); return this.hex = this.hex || Transaction.toHex(this); }; Transaction.toHex = function(tx) { - return Transaction.toBinary(tx).toString('hex'); + var data = bitcoindjs.getTxHex(tx); + return data.hex; +}; + +Transaction.prototype.toBinary = function() { + return Transaction.toBinary(this); }; Transaction.toBinary = function(tx) { + var data = bitcoindjs.getTxHex(tx); + return new Buffer(data.hex, 'hex'); +}; + +Transaction.prototype._toHexJS = function() { + return Transaction._toHexJS(this); + return this.hexJS = this.hexJS || Transaction._toHexJS(this); +}; + +Transaction._toHexJS = function(tx) { + return Transaction._toBinaryJS(tx).toString('hex'); +}; + +Transaction.prototype._toBinaryJS = function() { + return Transaction._toBinaryJS(this); +}; + +Transaction._toBinaryJS = function(tx) { var p = []; - var off = utils.writeU32(p, tx.nVersion || tx.version, 0); + var off = utils.writeU32(p, tx.version, 0); off += utils.varint(p, tx.vin.length, off); for (var i = 0; i < tx.vin.length; i++) { var input = tx.vin[i]; - if (input.coinbase) { - off += utils.copy(new bn(Array(64 + 1).join('0'), 'hex').toArray(), p, off, true); - off += utils.writeU32(p, 0, off); + off += utils.copy(utils.toArray(input.txid, 'hex'), p, off, true); + off += utils.writeU32(p, input.vout, off); - var s = script.encode(new bn(input.coinbase, 'hex').toArray()); - off += utils.varint(p, s.length, off); - off += utils.copy(s, p, off, true); + var s = script.encode(utils.toArray(input.scriptSig.hex, 'hex')); + off += utils.varint(p, s.length, off); + off += utils.copy(s, p, off, true); - off += utils.writeU32(p, input.sequence, off); - } else { - off += utils.copy(new bn(input.txid, 'hex').toArray(), p, off, true); - off += utils.writeU32(p, input.vout, off); - - var s = script.encode(new bn(input.scriptSig.hex, 'hex').toArray()); - off += utils.varint(p, s.length, off); - off += utils.copy(s, p, off, true); - - off += utils.writeU32(p, input.sequence, off); - } + off += utils.writeU32(p, input.sequence, off); } off += utils.varint(p, tx.vout.length, off); @@ -631,19 +648,15 @@ Transaction.toBinary = function(tx) { p[off] = 0; } - var s = script.encode(new bn(output.scriptPubKey.hex, 'hex').toArray()); + var s = script.encode(utils.toArray(output.scriptPubKey.hex, 'hex')); off += utils.varint(p, s.length, off); off += utils.copy(s, p, off, true); } - off += utils.writeU32(p, tx.nLockTime || tx.locktime, off); + off += utils.writeU32(p, tx.locktime, off); return new Buffer(p); }; -Transaction.prototype.toBinary = function() { - return Transaction.toBinary(this); -}; - Transaction.broadcast = function(tx, options, callback) { if (typeof tx === 'string') { tx = { hex: tx }; @@ -665,9 +678,9 @@ Transaction.broadcast = function(tx, options, callback) { callback = utils.NOOP; } - if (!tx.hex) { + if (!bitcoin.isTx(tx)) { tx = bitcoin.tx(tx); - tx.toHex(); + // tx.hex = tx.toHex(); } return bitcoindjs.broadcastTx(tx, fee, own, function(err, hash, tx) { @@ -906,16 +919,16 @@ utils.varint = function(arr, value, off) { }; utils.ripesha = function(data, enc) { - return utils.ripemd160(utils.sha256(data, enc)); + return utils.ripemd160(utils.sha256(data), enc); }; utils.checksum = function(data, enc) { - var b = new Buffer(utils.toArray(utils.dsha256(data, enc)).slice(0, 4)); + var b = new Buffer(utils.toArray(utils.dsha256(data)).slice(0, 4)); return enc ? b.toString(enc) : b; }; utils.dsha256 = function(data, enc) { - return utils.sha256(utils.sha256(data, enc)); + return utils.sha256(utils.sha256(data), enc); }; utils._hash = function(algo, data, enc) { diff --git a/src/bitcoindjs.cc b/src/bitcoindjs.cc index 07289b93..32bed422 100644 --- a/src/bitcoindjs.cc +++ b/src/bitcoindjs.cc @@ -142,8 +142,8 @@ NAN_METHOD(BroadcastTx); NAN_METHOD(VerifyBlock); NAN_METHOD(VerifyTransaction); NAN_METHOD(FillTransaction); -NAN_METHOD(GetBlockHash); -NAN_METHOD(GetTxHash); +NAN_METHOD(GetBlockHex); +NAN_METHOD(GetTxHex); NAN_METHOD(WalletNewAddress); NAN_METHOD(WalletGetAccountAddress); @@ -1307,15 +1307,15 @@ NAN_METHOD(FillTransaction) { } /** - * GetBlockHash + * GetBlockHex */ -NAN_METHOD(GetBlockHash) { +NAN_METHOD(GetBlockHex) { NanScope(); if (args.Length() < 1 || !args[0]->IsObject()) { return NanThrowError( - "Usage: bitcoindjs.getBlockHash(block)"); + "Usage: bitcoindjs.getBlockHex(block)"); } Local jsblock = Local::Cast(args[0]); @@ -1323,21 +1323,28 @@ NAN_METHOD(GetBlockHash) { CBlock cblock; jsblock_to_cblock(jsblock, cblock); - Local hash = NanNew(cblock.GetHash().GetHex().c_str()); + Local data = NanNew(); - NanReturnValue(hash); + data->Set(NanNew("hash"), NanNew(cblock.GetHash().GetHex().c_str())); + + CDataStream ssBlock(SER_NETWORK, PROTOCOL_VERSION); + ssBlock << cblock; + std::string strHex = HexStr(ssBlock.begin(), ssBlock.end()); + data->Set(NanNew("hex"), NanNew(strHex)); + + NanReturnValue(data); } /** - * GetTxHash + * GetTxHex */ -NAN_METHOD(GetTxHash) { +NAN_METHOD(GetTxHex) { NanScope(); if (args.Length() < 1 || !args[0]->IsObject()) { return NanThrowError( - "Usage: bitcoindjs.getTxHash(tx)"); + "Usage: bitcoindjs.getTxHex(tx)"); } Local jstx = Local::Cast(args[0]); @@ -1345,9 +1352,16 @@ NAN_METHOD(GetTxHash) { CTransaction ctx; jstx_to_ctx(jstx, ctx); - Local hash = NanNew(ctx.GetHash().GetHex()); + Local data = NanNew(); - NanReturnValue(hash); + data->Set(NanNew("hash"), NanNew(ctx.GetHash().GetHex())); + + CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION); + ssTx << ctx; + std::string strHex = HexStr(ssTx.begin(), ssTx.end()); + data->Set(NanNew("hex"), NanNew(strHex)); + + NanReturnValue(data); } /** @@ -2479,21 +2493,31 @@ cblock_to_jsblock(const CBlock& cblock, const CBlockIndex* cblock_index, LocalSet(NanNew("bits"), NanNew(cblock.nBits)); jsblock->Set(NanNew("difficulty"), NanNew(GetDifficulty(cblock_index))); jsblock->Set(NanNew("chainwork"), NanNew(cblock_index->nChainWork.GetHex())); + if (cblock_index->pprev) { jsblock->Set(NanNew("previousblockhash"), NanNew(cblock_index->pprev->GetBlockHash().GetHex())); + } else { + // genesis + jsblock->Set(NanNew("previousblockhash"), + NanNew("0000000000000000000000000000000000000000000000000000000000000000")); } + CBlockIndex *pnext = chainActive.Next(cblock_index); if (pnext) { jsblock->Set(NanNew("nextblockhash"), NanNew(pnext->GetBlockHash().GetHex())); } + + CDataStream ssBlock(SER_NETWORK, PROTOCOL_VERSION); + ssBlock << cblock; + std::string strHex = HexStr(ssBlock.begin(), ssBlock.end()); + jsblock->Set(NanNew("hex"), NanNew(strHex)); } static inline void ctx_to_jstx(const CTransaction& ctx, uint256 block_hash, Local 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("mintxfee"), NanNew(ctx.nMinTxFee)); + jstx->Set(NanNew("minrelaytxfee"), NanNew(ctx.nMinRelayTxFee)); + jstx->Set(NanNew("current_version"), NanNew(ctx.CURRENT_VERSION)); jstx->Set(NanNew("txid"), NanNew(ctx.GetHash().GetHex())); jstx->Set(NanNew("version"), NanNew(ctx.nVersion)); @@ -2503,17 +2527,22 @@ ctx_to_jstx(const CTransaction& ctx, uint256 block_hash, Local jstx) { 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); } + + // 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); + // /else + in->Set(NanNew("sequence"), NanNew((boost::int64_t)txin.nSequence)); + vin->Set(vi, in); vi++; } @@ -2523,6 +2552,7 @@ ctx_to_jstx(const CTransaction& ctx, uint256 block_hash, Local jstx) { 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)); @@ -2530,15 +2560,12 @@ ctx_to_jstx(const CTransaction& ctx, uint256 block_hash, Local jstx) { { 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()))); - } + out->Set(NanNew("hex"), NanNew(HexStr(scriptPubKey.begin(), scriptPubKey.end()))); if (!ExtractDestinations(scriptPubKey, type, addresses, nRequired)) { out->Set(NanNew("type"), NanNew(GetTxnOutputType(type))); } else { @@ -2574,6 +2601,11 @@ ctx_to_jstx(const CTransaction& ctx, uint256 block_hash, Local jstx) { } } } + + CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION); + ssTx << ctx; + std::string strHex = HexStr(ssTx.begin(), ssTx.end()); + jstx->Set(NanNew("hex"), NanNew(strHex)); } static inline void @@ -2613,36 +2645,39 @@ jsblock_to_cblock(const Local jsblock, CBlock& cblock) { static inline void jstx_to_ctx(const Local jstx, CTransaction& ctx) { + ctx.nMinTxFee = jstx->Get(NanNew("mintxfee"))->IntegerValue(); + ctx.nMinRelayTxFee = jstx->Get(NanNew("minrelaytxfee"))->IntegerValue(); + // ctx.CURRENT_VERSION = jstx->Get(NanNew("current_version"))->IntegerValue(); + 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)); + 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(); + 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 (in->Get(NanNew("coinbase"))->IsString()) { + // String::AsciiValue shash__(in->Get(NanNew("coinbase"))->ToString()); + // shash_ = *shash__; + //} else { + Local script_obj = Local::Cast(in->Get(NanNew("scriptSig"))); + String::AsciiValue shash__(script_obj->Get(NanNew("hex"))->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); @@ -2655,8 +2690,8 @@ jstx_to_ctx(const Local jstx, CTransaction& ctx) { txout.nValue = (int64_t)out->Get(NanNew("value"))->IntegerValue(); - Local spk = Local::Cast(out->Get(NanNew("scriptPubKey"))); - String::AsciiValue phash__(spk->Get(NanNew("hex"))); + Local script_obj = Local::Cast(out->Get(NanNew("scriptPubKey"))); + String::AsciiValue phash__(script_obj->Get(NanNew("hex"))); std::string phash_ = *phash__; if (phash_[1] != 'x') phash_ = "0x" + phash_; uint256 phash(phash_); @@ -2666,6 +2701,8 @@ jstx_to_ctx(const Local jstx, CTransaction& ctx) { ctx.vout.push_back(txout); } + + ctx.nLockTime = jstx->Get(NanNew("locktime"))->IntegerValue(); } /** @@ -2690,8 +2727,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, "getBlockHex", GetBlockHex); + NODE_SET_METHOD(target, "getTxHex", GetTxHex); NODE_SET_METHOD(target, "walletNewAddress", WalletNewAddress); NODE_SET_METHOD(target, "walletGetAccountAddress", WalletGetAccountAddress);