Merge pull request #1151 from maraoz/address/script

refactor Address<->Script relation
This commit is contained in:
Esteban Ordano 2015-03-19 09:12:20 -03:00
commit a67fa91ca9
6 changed files with 245 additions and 117 deletions

View File

@ -2,10 +2,13 @@
var _ = require('lodash');
var $ = require('./util/preconditions');
var errors = require('./errors');
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');
/**
* Instantiate an address from an address String or Buffer, a public key or script hash Buffer,
@ -91,8 +94,6 @@ function Address(data, network, type) {
* @returns {Object} An "info" object with "type", "network", and "hashBuffer"
*/
Address.prototype._classifyArguments = function(data, network, type) {
var PublicKey = require('./publickey');
var Script = require('./script');
/* jshint maxcomplexity: 10 */
// transform and validate input data
if ((data instanceof Buffer || data instanceof Uint8Array) && data.length === 20) {
@ -122,7 +123,7 @@ Address.PayToScriptHash = 'scripthash';
* @returns {Object} An object with keys: hashBuffer
* @private
*/
Address._transformHash = function(hash){
Address._transformHash = function(hash) {
var info = {};
if (!(hash instanceof Buffer) && !(hash instanceof Uint8Array)) {
throw new TypeError('Address supplied is not a buffer.');
@ -159,7 +160,7 @@ Address._transformObject = function(data) {
* @returns {Object} An object with keys: network and type
* @private
*/
Address._classifyFromVersion = function(buffer){
Address._classifyFromVersion = function(buffer) {
var version = {};
version.network = Networks.get(buffer[0]);
switch (buffer[0]) { // the version byte
@ -191,7 +192,7 @@ Address._classifyFromVersion = function(buffer){
* @returns {Object} An object with keys: hashBuffer, network and type
* @private
*/
Address._transformBuffer = function(buffer, network, type){
Address._transformBuffer = function(buffer, network, type) {
/* jshint maxcomplexity: 9 */
var info = {};
if (!(buffer instanceof Buffer) && !(buffer instanceof Uint8Array)) {
@ -208,7 +209,7 @@ Address._transformBuffer = function(buffer, network, type){
throw new TypeError('Address has mismatched network type.');
}
if (!bufferVersion.type || ( type && type !== bufferVersion.type )) {
if (!bufferVersion.type || (type && type !== bufferVersion.type)) {
throw new TypeError('Address has mismatched type.');
}
@ -225,8 +226,7 @@ Address._transformBuffer = function(buffer, network, type){
* @returns {Object} An object with keys: hashBuffer, type
* @private
*/
Address._transformPublicKey = function(pubkey){
var PublicKey = require('./publickey');
Address._transformPublicKey = function(pubkey) {
var info = {};
if (!(pubkey instanceof PublicKey)) {
throw new TypeError('Address must be an instance of PublicKey.');
@ -243,23 +243,12 @@ Address._transformPublicKey = function(pubkey){
* @returns {Object} An object with keys: hashBuffer, type
* @private
*/
Address._transformScript = function(script, network){
var Script = require('./script');
var info = {};
if (!(script instanceof Script)) {
throw new TypeError('Address must be an instance of Script.');
Address._transformScript = function(script, network) {
$.checkArgument(script instanceof Script, 'script must be a Script instance');
var info = script.getAddressInfo(network);
if (!info) {
throw new errors.Script.CantDeriveAddress(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 = Networks.get(network) || Networks.defaultNetwork;
return info;
};
@ -276,9 +265,8 @@ Address._transformScript = function(script, network){
* @return {Address}
*/
Address.createMultisig = function(publicKeys, threshold, network) {
var Script = require('./script');
network = network || publicKeys[0].network;
return new Address(Script.buildMultisigOut(publicKeys, threshold), network || Networks.defaultNetwork);
network = network || publicKeys[0].network || Networks.defaultNetwork;
return Address.payingTo(Script.buildMultisigOut(publicKeys, threshold), network);
};
/**
@ -290,9 +278,9 @@ Address.createMultisig = function(publicKeys, threshold, network) {
* @returns {Object} An object with keys: hashBuffer, network and type
* @private
*/
Address._transformString = function(data, network, type){
if( typeof(data) !== 'string' ) {
throw new TypeError('Address supplied is not a string.');
Address._transformString = function(data, network, type) {
if (typeof(data) !== 'string') {
throw new TypeError('data parameter supplied is not a string.');
}
var addressBuffer = Base58Check.decode(data);
var info = Address._transformBuffer(addressBuffer, network, type);
@ -306,7 +294,7 @@ Address._transformString = function(data, network, type){
* @param {String|Network} network - either a Network instance, 'livenet', or 'testnet'
* @returns {Address} A new valid and frozen instance of an Address
*/
Address.fromPublicKey = function(data, network){
Address.fromPublicKey = function(data, network) {
var info = Address._transformPublicKey(data);
network = network || Networks.defaultNetwork;
return new Address(info.hashBuffer, network, info.type);
@ -332,18 +320,42 @@ Address.fromPublicKeyHash = function(hash, network) {
* @returns {Address} A new valid and frozen instance of an Address
*/
Address.fromScriptHash = function(hash, network) {
$.checkArgument(hash, 'hash parameter is required');
var info = Address._transformHash(hash);
return new Address(info.hashBuffer, network, Address.PayToScriptHash);
};
/**
* Instantiate an address from a Script
* Builds a p2sh address paying to script. This will hash the script and
* use that to create the address.
* If you want to extract an address associated with a script instead,
* see {{Address#fromScript}}
*
* @param {Script} script - An instance of Script
* @param {String|Network} network - either a Network instance, 'livenet', or 'testnet'
* @returns {Address} A new valid and frozen instance of an Address
*/
Address.payingTo = function(script, network) {
$.checkArgument(script, 'script is required');
$.checkArgument(script instanceof Script, 'script must be instance of Script');
return Address.fromScriptHash(Hash.sha256ripemd160(script.toBuffer()), network);
};
/**
* Extract address from a Script. The script must be of one
* of the following types: p2pkh input, p2pkh output, p2sh input
* or p2sh output.
* This will analyze the script and extract address information from it.
* If you want to transform any script to a p2sh Address paying
* to that script's hash instead, use {{Address#payingTo}}
*
* @param {Script} script - An instance of Script
* @param {String|Network} network - either a Network instance, 'livenet', or 'testnet'
* @returns {Address} A new valid and frozen instance of an Address
*/
Address.fromScript = function(script, network) {
$.checkArgument(script instanceof Script, 'script must be a Script instance');
var info = Address._transformScript(script, network);
return new Address(info.hashBuffer, network, info.type);
};
@ -494,7 +506,7 @@ Address.prototype.toString = function() {
* @returns {string} Bitcoin address
*/
Address.prototype.inspect = function() {
return '<Address: ' + this.toString() + ', type: '+this.type+', network: '+this.network+'>';
return '<Address: ' + this.toString() + ', type: ' + this.type + ', network: ' + this.network + '>';
};
module.exports = Address;

View File

@ -103,6 +103,12 @@ module.exports = [{
errors: [{
name: 'UnrecognizedAddress',
message: 'Expected argument {0} to be an address'
}, {
name: 'CantDeriveAddress',
message: 'Can\'t derive address associated with script {0}, needs to be p2pkh in, p2pkh out, p2sh in, or p2sh out.'
}, {
name: 'InvalidBuffer',
message: 'Invalid script buffer: can\'t parse valid script from given buffer {0}'
}]
}, {
name: 'HDPrivateKey',

View File

@ -1,6 +1,5 @@
'use strict';
var Address = require('./address');
var BN = require('./crypto/bn');
var Point = require('./crypto/point');
var Hash = require('./crypto/hash');
@ -404,6 +403,7 @@ PublicKey.prototype._getID = function _getID() {
* @returns {Address} An address generated from the public key
*/
PublicKey.prototype.toAddress = function(network) {
var Address = require('./address');
return Address.fromPublicKey(this, network || this.network);
};

View File

@ -1,7 +1,6 @@
'use strict';
var Address = require('../address');
var BufferReader = require('../encoding/bufferreader');
var BufferWriter = require('../encoding/bufferwriter');
var Hash = require('../crypto/hash');
@ -30,6 +29,7 @@ var Script = function Script(from) {
if (!(this instanceof Script)) {
return new Script(from);
}
var Address = require('../address');
this.chunks = [];
@ -57,44 +57,51 @@ Script.fromBuffer = function(buffer) {
var br = new BufferReader(buffer);
while (!br.finished()) {
var opcodenum = br.readUInt8();
try {
var opcodenum = br.readUInt8();
var len, buf;
if (opcodenum > 0 && opcodenum < Opcode.OP_PUSHDATA1) {
len = opcodenum;
script.chunks.push({
buf: br.read(len),
len: len,
opcodenum: opcodenum
});
} else if (opcodenum === Opcode.OP_PUSHDATA1) {
len = br.readUInt8();
buf = br.read(len);
script.chunks.push({
buf: buf,
len: len,
opcodenum: opcodenum
});
} else if (opcodenum === Opcode.OP_PUSHDATA2) {
len = br.readUInt16LE();
buf = br.read(len);
script.chunks.push({
buf: buf,
len: len,
opcodenum: opcodenum
});
} else if (opcodenum === Opcode.OP_PUSHDATA4) {
len = br.readUInt32LE();
buf = br.read(len);
script.chunks.push({
buf: buf,
len: len,
opcodenum: opcodenum
});
} else {
script.chunks.push({
opcodenum: opcodenum
});
var len, buf;
if (opcodenum > 0 && opcodenum < Opcode.OP_PUSHDATA1) {
len = opcodenum;
script.chunks.push({
buf: br.read(len),
len: len,
opcodenum: opcodenum
});
} else if (opcodenum === Opcode.OP_PUSHDATA1) {
len = br.readUInt8();
buf = br.read(len);
script.chunks.push({
buf: buf,
len: len,
opcodenum: opcodenum
});
} else if (opcodenum === Opcode.OP_PUSHDATA2) {
len = br.readUInt16LE();
buf = br.read(len);
script.chunks.push({
buf: buf,
len: len,
opcodenum: opcodenum
});
} else if (opcodenum === Opcode.OP_PUSHDATA4) {
len = br.readUInt32LE();
buf = br.read(len);
script.chunks.push({
buf: buf,
len: len,
opcodenum: opcodenum
});
} else {
script.chunks.push({
opcodenum: opcodenum
});
}
} catch (e) {
if (e instanceof RangeError) {
throw new errors.Script.InvalidBuffer(buffer);
}
throw e;
}
}
@ -283,15 +290,21 @@ Script.prototype.isScriptHashIn = function() {
if (this.chunks.length === 0) {
return false;
}
var chunk = this.chunks[this.chunks.length - 1];
if (!chunk) {
var redeemChunk = this.chunks[this.chunks.length - 1];
var redeemBuf = redeemChunk.buf;
if (!redeemBuf) {
return false;
}
var scriptBuf = chunk.buf;
if (!scriptBuf) {
return false;
var redeemScript;
try {
redeemScript = Script.fromBuffer(redeemBuf);
} catch (e) {
if (e instanceof errors.Script.InvalidBuffer) {
return false;
}
throw e;
}
var redeemScript = new Script(scriptBuf);
var type = redeemScript.classify();
return type !== Script.types.UNKNOWN;
};
@ -433,14 +446,12 @@ Script.prototype.equals = function(script) {
}
var i;
for (i = 0; i < this.chunks.length; i++) {
if (BufferUtil.isBuffer(this.chunks[i]) && !BufferUtil.isBuffer(script.chunks[i])) {
return false;
} else if (this.chunks[i] instanceof Opcode && !(script.chunks[i] instanceof Opcode)) {
if (BufferUtil.isBuffer(this.chunks[i].buf) && !BufferUtil.isBuffer(script.chunks[i].buf)) {
return false;
}
if (BufferUtil.isBuffer(this.chunks[i]) && !BufferUtil.equals(this.chunks[i], script.chunks[i])) {
if (BufferUtil.isBuffer(this.chunks[i].buf) && !BufferUtil.equals(this.chunks[i].buf, script.chunks[i].buf)) {
return false;
} else if (this.chunks[i].num !== script.chunks[i].num) {
} else if (this.chunks[i].opcodenum !== script.chunks[i].opcodenum) {
return false;
}
}
@ -546,7 +557,7 @@ Script.prototype.removeCodeseparators = function() {
*/
Script.buildMultisigOut = function(publicKeys, threshold, opts) {
$.checkArgument(threshold <= publicKeys.length,
'Number of required signatures must be less than or equal to the number of public keys');
'Number of required signatures must be less than or equal to the number of public keys');
opts = opts || {};
var script = new Script();
script.add(Opcode.smallInt(threshold));
@ -598,6 +609,7 @@ 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) {
@ -650,6 +662,7 @@ 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();
@ -657,7 +670,7 @@ Script.buildScriptHashOut = function(script) {
.add(script instanceof Address ? script.hashBuffer : Hash.sha256ripemd160(script.toBuffer()))
.add(Opcode.OP_EQUAL);
s._network = script._network || script.network;
s._network = script._network || script.network;
return s;
};
@ -699,9 +712,10 @@ Script.prototype.toScriptHashOut = function() {
};
/**
* @return {Script} a script built from the address
* @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);
@ -713,14 +727,44 @@ Script.fromAddress = function(address) {
/**
* @param {Network=} network
* @return {Address} the associated address for this script
* @return {Address|boolean} the associated address information object
* for this script if any, or false
*/
Script.prototype.getAddressInfo = function() {
var Address = require('../address');
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()) {
// hash the publickey found in the scriptSig
info.hashBuffer = Hash.sha256ripemd160(this.chunks[1].buf);
info.type = Address.PayToPublicKeyHash;
} else if (this.isScriptHashIn()) {
// hash the redeemscript found at the end of the scriptSig
info.hashBuffer = Hash.sha256ripemd160(this.chunks[this.chunks.length - 1].buf);
info.type = Address.PayToScriptHash;
} else {
return false;
}
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;
if (this.isPublicKeyHashOut() || this.isScriptHashOut()) {
return new Address(this, network);
var info = this.getAddressInfo();
if (!info) {
return false;
}
throw new Error('The script type needs to be PayToPublicKeyHash or PayToScriptHash');
info.network = Networks.get(network) || Networks.defaultNetwork;
return new Address(info);
};
/**

View File

@ -28,7 +28,7 @@ describe('Address', function() {
});
it('should throw an error because of bad network param', function() {
(function(){
(function() {
return new Address(PKHLivenet[0], 'main', 'pubkeyhash');
}).should.throw('Second argument must be "livenet" or "testnet".');
});
@ -40,7 +40,7 @@ describe('Address', function() {
});
describe('bitcoind compliance', function() {
validbase58.map(function(d){
validbase58.map(function(d) {
if (!d[2].isPrivkey) {
it('should describe address ' + d[0] + ' as valid', function() {
var type;
@ -57,8 +57,8 @@ describe('Address', function() {
});
}
});
invalidbase58.map(function(d){
it('should describe input ' + d[0].slice(0,10) + '... as invalid', function() {
invalidbase58.map(function(d) {
it('should describe input ' + d[0].slice(0, 10) + '... as invalid', function() {
expect(function() {
return new Address(d[0]);
}).to.throw(Error);
@ -121,12 +121,12 @@ describe('Address', function() {
should.exist(error);
});
it('isValid returns true on a valid address', function(){
it('isValid returns true on a valid address', function() {
var valid = Address.isValid('37BahqRsFrAd3qLiNNwLNV3AWMRD7itxTo', 'livenet');
valid.should.equal(true);
});
it('isValid returns false on network mismatch', function(){
it('isValid returns false on network mismatch', function() {
var valid = Address.isValid('37BahqRsFrAd3qLiNNwLNV3AWMRD7itxTo', 'testnet');
valid.should.equal(false);
});
@ -280,13 +280,13 @@ describe('Address', function() {
it('should error because of incorrect type for script transform', function() {
(function() {
return Address._transformScript(new Buffer(20));
}).should.throw('Address must be an instance of Script.');
}).should.throw('Invalid Argument: script must be a Script instance');
});
it('should error because of incorrect type for string transform', function() {
(function() {
return Address._transformString(new Buffer(20));
}).should.throw('Address supplied is not a string.');
}).should.throw('data parameter supplied is not a string.');
});
it('should make an address from a pubkey hash buffer', function() {
@ -328,7 +328,7 @@ describe('Address', function() {
it('should make this address from an uncompressed pubkey', function() {
var pubkey = new PublicKey('0485e9737a74c30a873f74df05124f2aa6f53042c2fc0a130d6cbd7d16b944b00' +
'4833fef26c8be4c4823754869ff4e46755b85d851077771c220e2610496a29d98');
'4833fef26c8be4c4823754869ff4e46755b85d851077771c220e2610496a29d98');
var a = Address.fromPublicKey(pubkey, 'livenet');
a.toString().should.equal('16JXnhxjJUhxfyx4y6H4sFcxrgt8kQ8ewX');
var b = new Address(pubkey, 'livenet', 'pubkeyhash');
@ -336,23 +336,28 @@ describe('Address', function() {
});
describe('from a script', function() {
it('should make this address from a script', function() {
var s = Script.fromString('OP_CHECKMULTISIG');
it('should fail to build address from a non p2sh,p2pkh script', function() {
var s = new Script('OP_CHECKMULTISIG');
(function() {
return new Address(s);
}).should.throw('needs to be p2pkh in, p2pkh out, p2sh in, or p2sh out');
});
it('should make this address from a p2pkh output script', function() {
var s = new Script('OP_DUP OP_HASH160 20 ' +
'0xc8e11b0eb0d2ad5362d894f048908341fa61b6e1 OP_EQUALVERIFY OP_CHECKSIG');
var buf = s.toBuffer();
var a = Address.fromScript(s, 'livenet');
a.toString().should.equal('3BYmEwgV2vANrmfRymr1mFnHXgLjD6gAWm');
a.toString().should.equal('1KK9oz4bFH8c1t6LmighHaoSEGx3P3FEmc');
var b = new Address(s, 'livenet');
b.toString().should.equal('3BYmEwgV2vANrmfRymr1mFnHXgLjD6gAWm');
var c = Address.fromScriptHash(bitcore.crypto.Hash.sha256ripemd160(buf), 'livenet');
c.toString().should.equal('3BYmEwgV2vANrmfRymr1mFnHXgLjD6gAWm');
b.toString().should.equal('1KK9oz4bFH8c1t6LmighHaoSEGx3P3FEmc');
});
it('should make this address from other script', function() {
var s = Script.fromString('OP_CHECKSIG OP_HASH160');
it('should make this address from a p2sh input script', function() {
var s = Script.fromString('OP_HASH160 20 0xa6ed4af315271e657ee307828f54a4365fa5d20f OP_EQUAL');
var a = Address.fromScript(s, 'livenet');
a.toString().should.equal('347iRqVwks5r493N1rsLN4k9J7Ljg488W7');
a.toString().should.equal('3GueMn6ruWVfQTN4XKBGEbCbGLwRSUhfnS');
var b = new Address(s, 'livenet');
b.toString().should.equal('347iRqVwks5r493N1rsLN4k9J7Ljg488W7');
b.toString().should.equal('3GueMn6ruWVfQTN4XKBGEbCbGLwRSUhfnS');
});
it('returns the same address if the script is a pay to public key hash out', function() {
@ -487,6 +492,8 @@ describe('Address', function() {
it('can create an address from a set of public keys', function() {
var address = Address.createMultisig(publics, 2, Networks.livenet);
address.toString().should.equal('3FtqPRirhPvrf7mVUSkygyZ5UuoAYrTW3y');
address = new Address(publics, 2, Networks.livenet);
address.toString().should.equal('3FtqPRirhPvrf7mVUSkygyZ5UuoAYrTW3y');
});
it('works on testnet also', function() {
@ -502,7 +509,7 @@ describe('Address', function() {
it('fails if invalid array is provided', function() {
expect(function() {
return Address.createMultisig([],3,'testnet');
return Address.createMultisig([], 3, 'testnet');
}).to.throw('Number of required signatures must be less than or equal to the number of public keys');
});
});

View File

@ -11,6 +11,8 @@ var Opcode = bitcore.Opcode;
var PublicKey = bitcore.PublicKey;
var Address = bitcore.Address;
var script_valid = require('../data/bitcoind/script_valid');
describe('Script', function() {
it('should make a new script', function() {
@ -308,6 +310,16 @@ describe('Script', function() {
it('should identify this known non-scripthashin', function() {
Script('20 0000000000000000000000000000000000000000 OP_CHECKSIG').isScriptHashIn().should.equal(false);
});
it('should identify this problematic non-scripthashin scripts', function() {
var s = new Script('71 0x3044022017053dad84aa06213749df50a03330cfd24d6' +
'b8e7ddbb6de66c03697b78a752a022053bc0faca8b4049fb3944a05fcf7c93b2861' +
'734d39a89b73108f605f70f5ed3401 33 0x0225386e988b84248dc9c30f784b06e' +
'02fdec57bbdbd443768eb5744a75ce44a4c');
var s2 = new Script('OP_RETURN 32 0x19fdb20634911b6459e6086658b3a6ad2dc6576bd6826c73ee86a5f9aec14ed9');
s.isScriptHashIn().should.equal(false);
s2.isScriptHashIn().should.equal(false);
});
});
describe('#isScripthashOut', function() {
@ -641,18 +653,17 @@ describe('Script', function() {
it('priorize the network argument', function() {
var script = new Script(liveAddress);
script.toAddress(Networks.testnet).toString().should.equal(testAddress.toString());
var s = new Script('OP_DUP OP_HASH160 20 0x06c06f6d931d7bfba2b5bd5ad0d19a8f257af3e3 OP_EQUALVERIFY OP_CHECKSIG');
script.toAddress(Networks.testnet).network.should.equal(Networks.testnet);
});
it('use the inherited network', function() {
var script = new Script(liveAddress);
script.toAddress().toString().should.equal(liveAddress.toString());
var script = new Script(testAddress);
script = new Script(testAddress);
script.toAddress().toString().should.equal(testAddress.toString());
});
it('uses default network', function() {
var script = new Script('OP_DUP OP_HASH160 20 0x06c06f6d931d7bfba2b5bd5ad0d19a8f257af3e3 OP_EQUALVERIFY OP_CHECKSIG');
var script = new Script('OP_DUP OP_HASH160 20 ' +
'0x06c06f6d931d7bfba2b5bd5ad0d19a8f257af3e3 OP_EQUALVERIFY OP_CHECKSIG');
script.toAddress().network.should.equal(Networks.defaultNetwork);
});
it('for a P2PKH address', function() {
@ -668,9 +679,57 @@ describe('Script', function() {
script.toAddress().toString().should.equal(stringAddress);
});
it('fails if content is not recognized', function() {
expect(function() {
return Script().toAddress(Networks.livenet);
}).to.throw();
Script().toAddress(Networks.livenet).should.equal(false);
});
it('works for p2pkh output', function() {
// taken from tx 7e519caca256423320b92e3e17be5701f87afecbdb3f53af598032bfd8d164f5
var script = new Script('OP_DUP OP_HASH160 20 ' +
'0xc8e11b0eb0d2ad5362d894f048908341fa61b6e1 OP_EQUALVERIFY OP_CHECKSIG');
script.toAddress().toString().should.equal('1KK9oz4bFH8c1t6LmighHaoSEGx3P3FEmc');
});
it('works for p2pkh input', function() {
// taken from tx 7e519caca256423320b92e3e17be5701f87afecbdb3f53af598032bfd8d164f5
var script = new Script('72 0x3045022100eff96230ca0f55b1e8c7a63e014f48611ff1af40875ecd33dee9062d7a6f5e2002206320405b5f6992c756e03e66b21a05a812b60996464ac6af815c2638b930dd7a01 65 0x04150defa035a2c7d826d7d5fc8ab2154bd1bb832f1a5c8ecb338f436362ad232e428b57db44677c5a8bd42c5ed9e2d7e04e742c59bee1b40080cfd57dec64b23a');
script.toAddress().toString().should.equal('1KK9oz4bFH8c1t6LmighHaoSEGx3P3FEmc');
// taken from tx 7f8f95752a59d715dae9e0008a42e7968d2736741591bbfc6685f6e1649c21ed
var s2 = new Script('71 0x3044022017053dad84aa06213749df50a03330cfd24d6b8e7ddbb6de66c03697b78a752a022053bc0faca8b4049fb3944a05fcf7c93b2861734d39a89b73108f605f70f5ed3401 33 0x0225386e988b84248dc9c30f784b06e02fdec57bbdbd443768eb5744a75ce44a4c');
s2.toAddress().toString().should.equal('17VArX6GRE6i6MVscBUZoXwi6NhnHa68B7');
});
it('works for p2sh output', function() {
// taken from tx fe1f764299dc7f3b5a8fae912050df2b633bf99554c68bf1c456edb9c2b63585
var script = new Script('OP_HASH160 20 0x99d29051af0c29adcb9040034752bba7dde33e35 OP_EQUAL');
script.toAddress().toString().should.equal('3FiMZ7stbfH2WG5JQ7CiuzrFo7CEnGUcAP');
});
it('works for p2sh input', function() {
// taken from tx fe1f764299dc7f3b5a8fae912050df2b633bf99554c68bf1c456edb9c2b63585
var script = new Script('OP_FALSE 72 0x3045022100e824fbe979fac5834d0062dd5a4e82a898e00ac454bd254cd708ad28530816f202206251ff0fa4dd70c0524c690d4e4deb2bd167297e7bbdf6743b4a8050d681555001 37 0x512102ff3ae0aaa4679ea156d5581dbe6695cc0c311df0aa42af76670d0debbd8f672951ae');
script.toAddress().toString().should.equal('3GYicPxCvsKvbJmZNBBeWkC3cLuGFhtrQi');
});
// no address scripts
it('works for OP_RETURN script', function() {
var script = new Script('OP_RETURN 20 0x99d29051af0c29adcb9040034752bba7dde33e35');
script.toAddress().should.equal(false);
});
});
describe('equals', function() {
it('returns true for same script', function() {
Script('OP_TRUE').equals(Script('OP_TRUE')).should.equal(true);
});
it('returns false for different chunks sizes', function() {
Script('OP_TRUE').equals(Script('OP_TRUE OP_TRUE')).should.equal(false);
});
it('returns false for different opcodes', function() {
Script('OP_TRUE OP_TRUE').equals(Script('OP_TRUE OP_FALSE')).should.equal(false);
});
it('returns false for different data', function() {
Script().add(new Buffer('a')).equals(Script('OP_TRUE')).should.equal(false);
});
it('returns false for different data', function() {
Script().add(new Buffer('a')).equals(Script().add(new Buffer('b'))).should.equal(false);
});
});