diff --git a/lib/script.js b/lib/script.js index e3be402..6dac25d 100644 --- a/lib/script.js +++ b/lib/script.js @@ -170,6 +170,67 @@ Script.prototype.toString = function() { return str.substr(0, str.length - 1); }; + + +// script classification methods + +/** + * @returns true if this is a pay to pubkey hash output script + */ +Script.prototype.isPublicKeyHashOut = function() { + return this.chunks[0] === Opcode('OP_DUP').toNumber() && + this.chunks[1] === Opcode('OP_HASH160').toNumber() && + this.chunks[2].buf && + this.chunks[3] === Opcode('OP_EQUALVERIFY').toNumber() && + this.chunks[4] === Opcode('OP_CHECKSIG').toNumber(); +}; + +/** + * @returns true if this is a pay to public key hash input script + */ +Script.prototype.isPublicKeyHashIn = function() { + return !!(this.chunks.length === 2 && + this.chunks[0].buf && + this.chunks[1].buf); +}; + +/** + * @returns true if this is a p2sh output script + */ +Script.prototype.isScriptHashOut = function() { + return this.chunks.length === 3 && + this.chunks[0] === Opcode('OP_HASH160').toNumber() && + this.chunks[1].buf && + this.chunks[1].buf.length === 20 && + this.chunks[2] === Opcode('OP_EQUAL').toNumber(); +}; + +/** + * @returns true if this is a p2sh input script + * Note that these are frequently indistinguishable from pubkeyhashin + */ +Script.prototype.isScriptHashIn = function() { + return this.chunks.every(function(chunk) { + return Buffer.isBuffer(chunk.buf); + }); +}; + +/** + * @returns true if this is a mutlsig output script + */ +Script.prototype.isMultisigOut = function() { + return (this.chunks.length > 3 && + Opcode.isSmallIntOp(this.chunks[0]) && + this.chunks.slice(1, this.chunks.length - 2).every(function(obj) { + return obj.buf && Buffer.isBuffer(obj.buf); + }) && + Opcode.isSmallIntOp(this.chunks[this.chunks.length - 2]) && + this.chunks[this.chunks.length - 1] === Opcode.map.OP_CHECKMULTISIG); +}; + +/** + * @returns true if this is an OP_RETURN data script + */ Script.prototype.isOpReturn = function() { return (this.chunks[0] === Opcode('OP_RETURN').toNumber() && (this.chunks.length === 1 || @@ -179,34 +240,8 @@ Script.prototype.isOpReturn = function() { this.chunks[1].length === this.chunks.len))); }; -Script.prototype.isPublicKeyHashOut = function() { - return this.chunks[0] === Opcode('OP_DUP').toNumber() && - this.chunks[1] === Opcode('OP_HASH160').toNumber() && - this.chunks[2].buf && - this.chunks[3] === Opcode('OP_EQUALVERIFY').toNumber() && - this.chunks[4] === Opcode('OP_CHECKSIG').toNumber(); -}; -Script.prototype.isPublicKeyHashIn = function() { - return !!(this.chunks.length === 2 && - this.chunks[0].buf && - this.chunks[1].buf); -}; - -Script.prototype.isScriptHashOut = function() { - return this.chunks.length === 3 && - this.chunks[0] === Opcode('OP_HASH160').toNumber() && - this.chunks[1].buf && - this.chunks[1].buf.length === 20 && - this.chunks[2] === Opcode('OP_EQUAL').toNumber(); -}; - -//note that these are frequently indistinguishable from pubkeyhashin -Script.prototype.isScriptHashIn = function() { - return this.chunks.every(function(chunk) { - return Buffer.isBuffer(chunk.buf); - }); -}; +// Script construction methods /** * Adds a script element at the start of the script. diff --git a/package.json b/package.json index 6567381..5ae6407 100644 --- a/package.json +++ b/package.json @@ -96,8 +96,11 @@ "mocha": "~2.0.1", "run-sequence": "^1.0.2", "karma": "^0.12.28", + "karma-firefox-launcher": "^0.1.3", "karma-mocha": "^0.1.9", - "karma-firefox-launcher": "^0.1.3" + "lodash": "^2.4.1", + "mocha": "~2.0.1", + "run-sequence": "^1.0.2" }, "license": "MIT" } diff --git a/test/script.js b/test/script.js index 1a3fedf..2be11f1 100644 --- a/test/script.js +++ b/test/script.js @@ -251,6 +251,28 @@ describe('Script', function() { }); + describe('#isMultisigOut', function() { + it('should classify known multisig out 1 as multisig out', function() { + Script('OP_2 21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 OP_2 OP_CHECKMULTISIG').isMultisigOut().should.equal(true); + }); + it('should classify known multisig out 2 as multisig out', function() { + Script('OP_1 21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 OP_2 OP_CHECKMULTISIG').isMultisigOut().should.equal(true); + }); + it('should classify known multisig out 3 as multisig out', function() { + Script('OP_2 21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 OP_3 OP_CHECKMULTISIG').isMultisigOut().should.equal(true); + }); + + it('should classify non-multisig out 1 as non-multisig out', function() { + Script('OP_2 21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 OP_2 OP_CHECKMULTISIG OP_EQUAL').isMultisigOut().should.equal(false); + }); + it('should classify non-multisig out 2 as non-multisig out', function() { + Script('OP_2').isMultisigOut().should.equal(false); + }); + it('should classify non-multisig out 3 as non-multisig out', function() { + Script('OP_2 OP_2 21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 OP_2 OP_CHECKMULTISIG OP_EQUAL').isMultisigOut().should.equal(false); + }); + }); + describe('#add and #prepend', function() { it('should add these ops', function() {