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..f7820cb 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'); @@ -213,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 && @@ -225,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 && @@ -241,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 && @@ -251,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 && @@ -261,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(); @@ -272,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() { @@ -293,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 && @@ -307,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 && @@ -320,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 && @@ -349,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() { @@ -397,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 @@ -530,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 @@ -568,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)); @@ -585,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 */ @@ -607,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) { @@ -619,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) { @@ -638,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 || @@ -674,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); @@ -700,6 +701,18 @@ 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() || this.isScriptHashOut()) { + return new Address(this, network); + } + 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 @@ -725,8 +738,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]; 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..48dbfb9 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; @@ -590,4 +591,24 @@ describe('Script', function() { }); }); + 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() { + expect(function() { + return Script().toAddress(Networks.livenet); + }).to.throw(); + }); + }); + });