Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optimizations for script.toAddress() #1296

Merged
merged 5 commits into from
Jul 9, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions benchmark/script.js
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down
3 changes: 2 additions & 1 deletion lib/address.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');

/**
Expand Down Expand Up @@ -505,3 +504,5 @@ Address.prototype.inspect = function() {
};

module.exports = Address;

var Script = require('./script');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why this change?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Resolves the circular dependency between address.js and script.js without needing to have several address.js require statements in functions in script.js that slowed each function down each time it was invoked. Using a late definition here is faster.

13 changes: 11 additions & 2 deletions lib/crypto/hash.js
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down
55 changes: 39 additions & 16 deletions lib/script/script.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
'use strict';


var Address = require('../address');
var BufferReader = require('../encoding/bufferreader');
var BufferWriter = require('../encoding/bufferwriter');
var Hash = require('../crypto/hash');
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');
Expand All @@ -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)) {
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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);
Expand All @@ -756,20 +750,50 @@ 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(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.getAddressInfo = function() {
var Address = require('../address');
Script.prototype._getOutputAddressInfo = function() {
var info = {};
if (this.isScriptHashOut()) {
info.hashBuffer = this.getData();
info.type = Address.PayToScriptHash;
} 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;
Expand All @@ -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);
};

Expand Down
3 changes: 3 additions & 0 deletions lib/transaction/input/input.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down Expand Up @@ -116,13 +117,15 @@ 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
this._scriptBuffer = new buffer.Buffer(script, 'hex');
} 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
Expand Down
3 changes: 3 additions & 0 deletions lib/transaction/output.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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 {
Expand Down
4 changes: 3 additions & 1 deletion test/transaction/transaction.js
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down