From d688222769364202f4f827bd4d3f87f025cdb0ec Mon Sep 17 00:00:00 2001 From: Esteban Ordano Date: Tue, 30 Dec 2014 14:47:06 -0300 Subject: [PATCH 1/4] Add toAddress(network) functionality to scripts --- lib/address.js | 12 +++++++++-- lib/script/script.js | 16 +++++++++++++++ test/address.js | 47 +++++++++++++++++++++++++++---------------- test/script/script.js | 35 +++++++++++++++++++++++++++++--- 4 files changed, 88 insertions(+), 22 deletions(-) diff --git a/lib/address.js b/lib/address.js index f9177ee..abc0aca 100644 --- a/lib/address.js +++ b/lib/address.js @@ -234,9 +234,17 @@ Address._transformScript = function(script, network){ if (!script.constructor || (script.constructor.name && script.constructor.name !== 'Script')) { throw new TypeError('Address must be an instance of Script.'); } + if (script.isScriptHashOut()) { + info.hashBuffer = script.getData(); + info.type = Address.PayToScriptHash; + } else if (script.isPublicKeyHashOut()) { + info.hashBuffer = script.getData(); + info.type = Address.PayToPublicKeyHash; + } else { + info.hashBuffer = Hash.sha256ripemd160(script.toBuffer()); + info.type = Address.PayToScriptHash; + } info.network = network || Networks.defaultNetwork; - info.hashBuffer = Hash.sha256ripemd160(script.toBuffer()); - info.type = Address.PayToScriptHash; return info; }; diff --git a/lib/script/script.js b/lib/script/script.js index 8e557ef..aadd2db 100644 --- a/lib/script/script.js +++ b/lib/script/script.js @@ -8,6 +8,7 @@ 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'); @@ -700,6 +701,21 @@ Script.fromAddress = function(address) { throw new errors.Script.UnrecognizedAddress(address); }; +/** + * @return Address the associated address for this script + */ +Script.prototype.toAddress = function(network) { + network = Networks.get(network); + $.checkArgument(network, 'Must provide a network'); + if (this.isPublicKeyHashOut()) { + return new Address(this.getData(), network, Address.PayToPublicKeyHash); + } + if (this.isScriptHashOut()) { + return new Address(this.getData(), network, Address.PayToScriptHash); + } + throw new Error('The script type needs to be PayToPublicKeyHash or PayToScriptHash'); +}; + /** * Analagous to bitcoind's FindAndDelete. Find and delete equivalent chunks, * typically used with push data chunks. Note that this will find and delete diff --git a/test/address.js b/test/address.js index 0e8a1fb..b75c7ae 100644 --- a/test/address.js +++ b/test/address.js @@ -320,23 +320,36 @@ describe('Address', function() { b.toString().should.equal('16JXnhxjJUhxfyx4y6H4sFcxrgt8kQ8ewX'); }); - it('should make this address from a script', function() { - var s = Script.fromString('OP_CHECKMULTISIG'); - var buf = s.toBuffer(); - var a = Address.fromScript(s); - a.toString().should.equal('3BYmEwgV2vANrmfRymr1mFnHXgLjD6gAWm'); - var b = new Address(s); - b.toString().should.equal('3BYmEwgV2vANrmfRymr1mFnHXgLjD6gAWm'); - var c = Address.fromScriptHash(bitcore.crypto.Hash.sha256ripemd160(buf)); - c.toString().should.equal('3BYmEwgV2vANrmfRymr1mFnHXgLjD6gAWm'); - }); - - it('should make this address from other script', function() { - var s = Script.fromString('OP_CHECKSIG OP_HASH160'); - var a = Address.fromScript(s); - a.toString().should.equal('347iRqVwks5r493N1rsLN4k9J7Ljg488W7'); - var b = new Address(s); - b.toString().should.equal('347iRqVwks5r493N1rsLN4k9J7Ljg488W7'); + describe('from a script', function() { + it('should make this address from a script', function() { + var s = Script.fromString('OP_CHECKMULTISIG'); + var buf = s.toBuffer(); + var a = Address.fromScript(s); + a.toString().should.equal('3BYmEwgV2vANrmfRymr1mFnHXgLjD6gAWm'); + var b = new Address(s); + b.toString().should.equal('3BYmEwgV2vANrmfRymr1mFnHXgLjD6gAWm'); + var c = Address.fromScriptHash(bitcore.crypto.Hash.sha256ripemd160(buf)); + c.toString().should.equal('3BYmEwgV2vANrmfRymr1mFnHXgLjD6gAWm'); + }); + + it('should make this address from other script', function() { + var s = Script.fromString('OP_CHECKSIG OP_HASH160'); + var a = Address.fromScript(s); + a.toString().should.equal('347iRqVwks5r493N1rsLN4k9J7Ljg488W7'); + var b = new Address(s); + b.toString().should.equal('347iRqVwks5r493N1rsLN4k9J7Ljg488W7'); + }); + + it('returns the same address if the script is a pay to public key hash out', function() { + var address = '16JXnhxjJUhxfyx4y6H4sFcxrgt8kQ8ewX'; + var script = Script.buildPublicKeyHashOut(new Address(address)); + Address(script, Networks.livenet).toString().should.equal(address); + }); + it('returns the same address if the script is a pay to script hash out', function() { + var address = '3BYmEwgV2vANrmfRymr1mFnHXgLjD6gAWm'; + var script = Script.buildScriptHashOut(new Address(address)); + Address(script, Networks.livenet).toString().should.equal(address); + }); }); it('should derive from this known address string livenet', function() { diff --git a/test/script/script.js b/test/script/script.js index 7fc66d0..5ea7176 100644 --- a/test/script/script.js +++ b/test/script/script.js @@ -6,6 +6,7 @@ var bitcore = require('../..'); var BufferUtil = bitcore.util.buffer; var Script = bitcore.Script; +var Networks = bitcore.Networks; var Opcode = bitcore.Opcode; var PublicKey = bitcore.PublicKey; var Address = bitcore.Address; @@ -584,9 +585,37 @@ describe('Script', function() { expect(BufferUtil.equal(Script('OP_RETURN 1 0xFF').getData(), new Buffer([255]))).to.be.true(); }); it('fails if content is not recognized', function() { - expect(function() { - return Script('1 0xFF').getData(); - }).to.throw(); + var failed = false; + try { + Script('1 0xFF').getData(); + } catch (e) { + failed = true; + } + failed.should.equal(true); + }); + }); + + describe('toAddress', function() { + it('for a P2PKH address', function() { + var stringAddress = '1NaTVwXDDUJaXDQajoa9MqHhz4uTxtgK14'; + var address = new Address(stringAddress); + var script = new Script(address); + script.toAddress(Networks.livenet).toString().should.equal(stringAddress); + }); + it('for a P2SH address', function() { + var stringAddress = '3GhtMmAbWrUf6Y8vDxn9ETB14R6V7Br3mt'; + var address = new Address(stringAddress); + var script = new Script(address); + script.toAddress(Networks.livenet).toString().should.equal(stringAddress); + }); + it('fails if content is not recognized', function() { + var failed = false; + try { + Script().toAddress(); + } catch (e) { + failed = true; + } + failed.should.equal(true); }); }); From c1a1571535f988c22cd204f370915c7ab76241ad Mon Sep 17 00:00:00 2001 From: Esteban Ordano Date: Tue, 30 Dec 2014 15:27:05 -0300 Subject: [PATCH 2/4] Fix JSDoc for script --- lib/script/script.js | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/lib/script/script.js b/lib/script/script.js index aadd2db..55b43c1 100644 --- a/lib/script/script.js +++ b/lib/script/script.js @@ -214,7 +214,7 @@ Script.prototype.inspect = function() { // script classification methods /** - * @returns true if this is a pay to pubkey hash output script + * @returns {boolean} if this is a pay to pubkey hash output script */ Script.prototype.isPublicKeyHashOut = function() { return !!(this.chunks.length === 5 && @@ -226,7 +226,7 @@ Script.prototype.isPublicKeyHashOut = function() { }; /** - * @returns true if this is a pay to public key hash input script + * @returns {boolean} if this is a pay to public key hash input script */ Script.prototype.isPublicKeyHashIn = function() { return this.chunks.length === 2 && @@ -242,7 +242,7 @@ Script.prototype.getPublicKeyHash = function() { }; /** - * @returns true if this is a public key output script + * @returns {boolean} if this is a public key output script */ Script.prototype.isPublicKeyOut = function() { return this.chunks.length === 2 && @@ -252,7 +252,7 @@ Script.prototype.isPublicKeyOut = function() { }; /** - * @returns true if this is a pay to public key input script + * @returns {boolean} if this is a pay to public key input script */ Script.prototype.isPublicKeyIn = function() { return this.chunks.length === 1 && @@ -262,7 +262,7 @@ Script.prototype.isPublicKeyIn = function() { /** - * @returns true if this is a p2sh output script + * @returns {boolean} if this is a p2sh output script */ Script.prototype.isScriptHashOut = function() { var buf = this.toBuffer(); @@ -273,7 +273,7 @@ Script.prototype.isScriptHashOut = function() { }; /** - * @returns true if this is a p2sh input script + * @returns {boolean} if this is a p2sh input script * Note that these are frequently indistinguishable from pubkeyhashin */ Script.prototype.isScriptHashIn = function() { @@ -294,7 +294,7 @@ Script.prototype.isScriptHashIn = function() { }; /** - * @returns true if this is a mutlsig output script + * @returns {boolean} if this is a mutlsig output script */ Script.prototype.isMultisigOut = function() { return (this.chunks.length > 3 && @@ -308,7 +308,7 @@ Script.prototype.isMultisigOut = function() { /** - * @returns true if this is a multisig input script + * @returns {boolean} if this is a multisig input script */ Script.prototype.isMultisigIn = function() { return this.chunks.length >= 2 && @@ -321,7 +321,7 @@ Script.prototype.isMultisigIn = function() { }; /** - * @returns true if this is an OP_RETURN data script + * @returns {boolean} if this is an OP_RETURN data script */ Script.prototype.isDataOut = function() { return this.chunks.length >= 1 && @@ -350,7 +350,7 @@ Script.prototype.getData = function() { }; /** - * @returns true if the script is only composed of data pushing + * @returns {boolean} if the script is only composed of data pushing * opcodes or small int opcodes (OP_0, OP_1, ..., OP_16) */ Script.prototype.isPushOnly = function() { @@ -398,7 +398,7 @@ Script.prototype.classify = function() { /** - * @returns true if script is one of the known types + * @returns {boolean} if script is one of the known types */ Script.prototype.isStandard = function() { // TODO: Add BIP62 compliance @@ -531,7 +531,7 @@ Script.prototype.removeCodeseparators = function() { // high level script builder methods /** - * @returns a new Multisig output script for given public keys, + * @returns {Script} a new Multisig output script for given public keys, * requiring m of those public keys to spend * @param {PublicKey[]} publicKeys - list of all public keys controlling the output * @param {number} threshold - amount of required signatures to spend the output @@ -569,7 +569,7 @@ Script.buildMultisigOut = function(publicKeys, threshold, opts) { * @param {boolean=} opts.noSorting don't sort the given public keys before creating the script (false by default) * @param {Script=} opts.cachedMultisig don't recalculate the redeemScript * - * @returns Script + * @returns {Script} */ Script.buildP2SHMultisigIn = function(pubkeys, threshold, signatures, opts) { $.checkArgument(_.isArray(pubkeys)); @@ -586,7 +586,7 @@ Script.buildP2SHMultisigIn = function(pubkeys, threshold, signatures, opts) { }; /** - * @returns a new pay to public key hash output for the given + * @returns {Script} a new pay to public key hash output for the given * address or public key * @param {(Address|PublicKey)} to - destination address or public key */ @@ -608,7 +608,7 @@ Script.buildPublicKeyHashOut = function(to) { }; /** - * @returns a new pay to public key output for the given + * @returns {Script} a new pay to public key output for the given * public key */ Script.buildPublicKeyOut = function(pubkey) { @@ -620,7 +620,7 @@ Script.buildPublicKeyOut = function(pubkey) { }; /** - * @returns a new OP_RETURN script with data + * @returns {Script} a new OP_RETURN script with data * @param {(string|Buffer)} to - the data to embed in the output */ Script.buildDataOut = function(data) { @@ -639,7 +639,7 @@ Script.buildDataOut = function(data) { /** * @param {Script|Address} script - the redeemScript for the new p2sh output. * It can also be a p2sh address - * @returns Script new pay to script hash script for given script + * @returns {Script} new pay to script hash script for given script */ Script.buildScriptHashOut = function(script) { $.checkArgument(script instanceof Script || @@ -675,21 +675,21 @@ Script.buildPublicKeyHashIn = function(publicKey, signature, sigtype) { }; /** - * @returns Script an empty script + * @returns {Script} an empty script */ Script.empty = function() { return new Script(); }; /** - * @returns Script a new pay to script hash script that pays to this script + * @returns {Script} a new pay to script hash script that pays to this script */ Script.prototype.toScriptHashOut = function() { return Script.buildScriptHashOut(this); }; /** - * @return Script a script built from the address + * @return {Script} a script built from the address */ Script.fromAddress = function(address) { address = Address(address); @@ -702,7 +702,7 @@ Script.fromAddress = function(address) { }; /** - * @return Address the associated address for this script + * @return {Address} the associated address for this script */ Script.prototype.toAddress = function(network) { network = Networks.get(network); @@ -741,8 +741,8 @@ Script.prototype.findAndDelete = function(script) { }; /** - * @returns true if the chunk {i} is the smallest way to push that particular data. * Comes from bitcoind's script interpreter CheckMinimalPush function + * @returns {boolean} if the chunk {i} is the smallest way to push that particular data. */ Script.prototype.checkMinimalPush = function(i) { var chunk = this.chunks[i]; From ffca4cfb6f2afdfff09ec89259e058cdf434f53c Mon Sep 17 00:00:00 2001 From: Esteban Ordano Date: Tue, 30 Dec 2014 15:33:52 -0300 Subject: [PATCH 3/4] Fixes Script test case for coverage and style --- lib/script/script.js | 6 +++--- test/script/script.js | 20 ++++++-------------- 2 files changed, 9 insertions(+), 17 deletions(-) diff --git a/lib/script/script.js b/lib/script/script.js index 55b43c1..f72bad1 100644 --- a/lib/script/script.js +++ b/lib/script/script.js @@ -709,11 +709,11 @@ Script.prototype.toAddress = function(network) { $.checkArgument(network, 'Must provide a network'); if (this.isPublicKeyHashOut()) { return new Address(this.getData(), network, Address.PayToPublicKeyHash); - } - if (this.isScriptHashOut()) { + } else if (this.isScriptHashOut()) { return new Address(this.getData(), network, Address.PayToScriptHash); + } else { + throw new Error('The script type needs to be PayToPublicKeyHash or PayToScriptHash'); } - throw new Error('The script type needs to be PayToPublicKeyHash or PayToScriptHash'); }; /** diff --git a/test/script/script.js b/test/script/script.js index 5ea7176..48dbfb9 100644 --- a/test/script/script.js +++ b/test/script/script.js @@ -585,13 +585,9 @@ describe('Script', function() { expect(BufferUtil.equal(Script('OP_RETURN 1 0xFF').getData(), new Buffer([255]))).to.be.true(); }); it('fails if content is not recognized', function() { - var failed = false; - try { - Script('1 0xFF').getData(); - } catch (e) { - failed = true; - } - failed.should.equal(true); + expect(function() { + return Script('1 0xFF').getData(); + }).to.throw(); }); }); @@ -609,13 +605,9 @@ describe('Script', function() { script.toAddress(Networks.livenet).toString().should.equal(stringAddress); }); it('fails if content is not recognized', function() { - var failed = false; - try { - Script().toAddress(); - } catch (e) { - failed = true; - } - failed.should.equal(true); + expect(function() { + return Script().toAddress(Networks.livenet); + }).to.throw(); }); }); From f74c7f3929f7055bc559ae9253788d110febb815 Mon Sep 17 00:00:00 2001 From: Esteban Ordano Date: Tue, 30 Dec 2014 16:16:34 -0300 Subject: [PATCH 4/4] Simplify script to address method --- lib/script/script.js | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/lib/script/script.js b/lib/script/script.js index f72bad1..f7820cb 100644 --- a/lib/script/script.js +++ b/lib/script/script.js @@ -707,13 +707,10 @@ Script.fromAddress = function(address) { Script.prototype.toAddress = function(network) { network = Networks.get(network); $.checkArgument(network, 'Must provide a network'); - if (this.isPublicKeyHashOut()) { - return new Address(this.getData(), network, Address.PayToPublicKeyHash); - } else if (this.isScriptHashOut()) { - return new Address(this.getData(), network, Address.PayToScriptHash); - } else { - throw new Error('The script type needs to be PayToPublicKeyHash or PayToScriptHash'); + if (this.isPublicKeyHashOut() || this.isScriptHashOut()) { + return new Address(this, network); } + throw new Error('The script type needs to be PayToPublicKeyHash or PayToScriptHash'); }; /**