diff --git a/benchmark/script.js b/benchmark/script.js index 33229dc..7442d1a 100644 --- a/benchmark/script.js +++ b/benchmark/script.js @@ -24,6 +24,12 @@ async.series([ scripts.push(input.script); } } + for (var k = 0; k < tx.outputs.length; k++) { + var output = tx.outputs[k]; + if (output.script) { + scripts.push(output.script); + } + } } function isPublicKeyHashIn() { diff --git a/lib/address.js b/lib/address.js index f49acd7..4905ed1 100644 --- a/lib/address.js +++ b/lib/address.js @@ -7,7 +7,6 @@ var Base58Check = require('./encoding/base58check'); var Networks = require('./networks'); var Hash = require('./crypto/hash'); var JSUtil = require('./util/js'); -var Script = require('./script'); var PublicKey = require('./publickey'); /** @@ -505,3 +504,5 @@ Address.prototype.inspect = function() { }; module.exports = Address; + +var Script = require('./script'); diff --git a/lib/crypto/hash.js b/lib/crypto/hash.js index 0661a46..f6a625a 100644 --- a/lib/crypto/hash.js +++ b/lib/crypto/hash.js @@ -29,10 +29,19 @@ Hash.sha256sha256 = function(buf) { Hash.ripemd160 = function(buf) { $.checkArgument(BufferUtil.isBuffer(buf)); - var hash = (new hashjs.ripemd160()).update(buf).digest(); - return new Buffer(hash); + return crypto.createHash('ripemd160').update(buf).digest(); }; +// Node.js crypto ripemd160 hashes are not supported in a browser +// We'll replace with a (slower) version that does. +if (global.window) { + Hash.ripemd160 = function(buf) { + $.checkArgument(BufferUtil.isBuffer(buf)); + var hash = (new hashjs.ripemd160()).update(buf).digest(); + return new Buffer(hash); + }; +} + Hash.sha256ripemd160 = function(buf) { $.checkArgument(BufferUtil.isBuffer(buf)); return Hash.ripemd160(Hash.sha256(buf)); diff --git a/lib/script/script.js b/lib/script/script.js index 21fe7fb..5bcdce6 100644 --- a/lib/script/script.js +++ b/lib/script/script.js @@ -1,6 +1,6 @@ 'use strict'; - +var Address = require('../address'); var BufferReader = require('../encoding/bufferreader'); var BufferWriter = require('../encoding/bufferwriter'); var Hash = require('../crypto/hash'); @@ -8,7 +8,6 @@ var Opcode = require('../opcode'); var PublicKey = require('../publickey'); var Signature = require('../crypto/signature'); var Networks = require('../networks'); - var $ = require('../util/preconditions'); var _ = require('lodash'); var errors = require('../errors'); @@ -29,8 +28,6 @@ var Script = function Script(from) { if (!(this instanceof Script)) { return new Script(from); } - var Address = require('../address'); - this.chunks = []; if (BufferUtil.isBuffer(from)) { @@ -639,7 +636,6 @@ Script.buildP2SHMultisigIn = function(pubkeys, threshold, signatures, opts) { * @param {(Address|PublicKey)} to - destination address or public key */ Script.buildPublicKeyHashOut = function(to) { - var Address = require('../address'); $.checkArgument(!_.isUndefined(to)); $.checkArgument(to instanceof PublicKey || to instanceof Address || _.isString(to)); if (to instanceof PublicKey) { @@ -692,7 +688,6 @@ Script.buildDataOut = function(data) { * @returns {Script} new pay to script hash script for given script */ Script.buildScriptHashOut = function(script) { - var Address = require('../address'); $.checkArgument(script instanceof Script || (script instanceof Address && script.isPayToScriptHash())); var s = new Script(); @@ -745,7 +740,6 @@ Script.prototype.toScriptHashOut = function() { * @return {Script} an output script built from the address */ Script.fromAddress = function(address) { - var Address = require('../address'); address = Address(address); if (address.isPayToScriptHash()) { return Script.buildScriptHashOut(address); @@ -756,12 +750,29 @@ Script.fromAddress = function(address) { }; /** - * @param {Network=} network - * @return {Address|boolean} the associated address information object - * for this script if any, or false + * Will return the associated address information object + * @return {Address|boolean} */ -Script.prototype.getAddressInfo = function() { - var Address = require('../address'); +Script.prototype.getAddressInfo = function(opts) { + if (this._isInput) { + return this._getInputAddressInfo(); + } else if (this._isOutput) { + return this._getOutputAddressInfo(); + } else { + var info = this._getOutputAddressInfo(); + if (!info) { + return this._getInputAddressInfo(); + } + return info; + } +}; + +/** + * Will return the associated output scriptPubKey address information object + * @return {Address|boolean} + * @private + */ +Script.prototype._getOutputAddressInfo = function() { var info = {}; if (this.isScriptHashOut()) { info.hashBuffer = this.getData(); @@ -769,7 +780,20 @@ Script.prototype.getAddressInfo = function() { } else if (this.isPublicKeyHashOut()) { info.hashBuffer = this.getData(); info.type = Address.PayToPublicKeyHash; - } else if (this.isPublicKeyHashIn()) { + } else { + return false; + } + return info; +}; + +/** + * Will return the associated input scriptSig address information object + * @return {Address|boolean} + * @private + */ +Script.prototype._getInputAddressInfo = function() { + var info = {}; + if (this.isPublicKeyHashIn()) { // hash the publickey found in the scriptSig info.hashBuffer = Hash.sha256ripemd160(this.chunks[1].buf); info.type = Address.PayToPublicKeyHash; @@ -782,18 +806,17 @@ Script.prototype.getAddressInfo = function() { } return info; }; + /** * @param {Network=} network * @return {Address|boolean} the associated address for this script if possible, or false */ Script.prototype.toAddress = function(network) { - var Address = require('../address'); - network = Networks.get(network) || this._network || Networks.defaultNetwork; var info = this.getAddressInfo(); if (!info) { return false; } - info.network = Networks.get(network) || Networks.defaultNetwork; + info.network = Networks.get(network) || this._network || Networks.defaultNetwork; return new Address(info); }; diff --git a/lib/transaction/input/input.js b/lib/transaction/input/input.js index afa169e..d810890 100644 --- a/lib/transaction/input/input.js +++ b/lib/transaction/input/input.js @@ -34,6 +34,7 @@ Object.defineProperty(Input.prototype, 'script', { } if (!this._script) { this._script = new Script(this._scriptBuffer); + this._script._isInput = true; } return this._script; } @@ -116,6 +117,7 @@ Input.prototype.setScript = function(script) { this._script = null; if (script instanceof Script) { this._script = script; + this._script._isInput = true; this._scriptBuffer = script.toBuffer(); } else if (JSUtil.isHexa(script)) { // hex string script @@ -123,6 +125,7 @@ Input.prototype.setScript = function(script) { } else if (_.isString(script)) { // human readable string script this._script = new Script(script); + this._script._isInput = true; this._scriptBuffer = this._script.toBuffer(); } else if (BufferUtil.isBuffer(script)) { // buffer script diff --git a/lib/transaction/output.js b/lib/transaction/output.js index 684b7d2..62af6b4 100644 --- a/lib/transaction/output.js +++ b/lib/transaction/output.js @@ -113,6 +113,7 @@ Output.prototype.setScriptFromBuffer = function(buffer) { this._scriptBuffer = buffer; try { this._script = Script.fromBuffer(this._scriptBuffer); + this._script._isOutput = true; } catch(e) { if (e instanceof errors.Script.InvalidBuffer) { this._script = null; @@ -126,9 +127,11 @@ Output.prototype.setScript = function(script) { if (script instanceof Script) { this._scriptBuffer = script.toBuffer(); this._script = script; + this._script._isOutput = true; } else if (_.isString(script)) { this._script = Script.fromString(script); this._scriptBuffer = this._script.toBuffer(); + this._script._isOutput = true; } else if (bufferUtil.isBuffer(script)) { this.setScriptFromBuffer(script); } else { diff --git a/test/transaction/transaction.js b/test/transaction/transaction.js index b828ec5..8980969 100644 --- a/test/transaction/transaction.js +++ b/test/transaction/transaction.js @@ -202,7 +202,9 @@ describe('Transaction', function() { transaction.outputs[1].satoshis.should.equal(40000); transaction.outputs[1].script.toString() .should.equal(Script.fromAddress(changeAddress).toString()); - transaction.getChangeOutput().script.should.deep.equal(Script.fromAddress(changeAddress)); + var actual = transaction.getChangeOutput().script.toString(); + var expected = Script.fromAddress(changeAddress).toString(); + actual.should.equal(expected); }); it('accepts a P2SH address for change', function() { var transaction = new Transaction()