From e4eb743cf95d23715d3044c229f9e13c16a7968f Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Fri, 28 Nov 2014 12:50:37 +1100 Subject: [PATCH] scripts: expose is* classifiers --- src/scripts.js | 27 +++-- test/fixtures/scripts.json | 133 +++++++++++---------- test/scripts.js | 236 ++++++++++++++++++++----------------- 3 files changed, 214 insertions(+), 182 deletions(-) diff --git a/src/scripts.js b/src/scripts.js index dfd167d..ac6d6e5 100644 --- a/src/scripts.js +++ b/src/scripts.js @@ -246,15 +246,26 @@ function nullDataOutput(data) { } module.exports = { - classifyInput: classifyInput, + isCanonicalPubKey: isCanonicalPubKey, + isCanonicalSignature: isCanonicalSignature, + isPubKeyHashInput: isPubKeyHashInput, + isPubKeyHashOutput: isPubKeyHashOutput, + isPubKeyInput: isPubKeyInput, + isPubKeyOutput: isPubKeyOutput, + isScriptHashInput: isScriptHashInput, + isScriptHashOutput: isScriptHashOutput, + isMultisigInput: isMultisigInput, + isMultisigOutput: isMultisigOutput, + isNullDataOutput: isNullDataOutput, classifyOutput: classifyOutput, - multisigInput: multisigInput, - multisigOutput: multisigOutput, - nullDataOutput: nullDataOutput, - pubKeyHashInput: pubKeyHashInput, - pubKeyHashOutput: pubKeyHashOutput, - pubKeyInput: pubKeyInput, + classifyInput: classifyInput, pubKeyOutput: pubKeyOutput, + pubKeyHashOutput: pubKeyHashOutput, + scriptHashOutput: scriptHashOutput, + multisigOutput: multisigOutput, + pubKeyInput: pubKeyInput, + pubKeyHashInput: pubKeyHashInput, scriptHashInput: scriptHashInput, - scriptHashOutput: scriptHashOutput + multisigInput: multisigInput, + nullDataOutput: nullDataOutput } diff --git a/test/fixtures/scripts.json b/test/fixtures/scripts.json index 86a7390..6ae7863 100644 --- a/test/fixtures/scripts.json +++ b/test/fixtures/scripts.json @@ -56,78 +56,71 @@ } ], "invalid": { - "classify": [ - { - "description": "multisig output : OP_CHECKMULTISIG not found", - "scriptPubKey": "OP_0 0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 02b80011a883a0fd621ad46dfc405df1e74bf075cbaf700fd4aebef6e96f848340 OP_2 OP_HASH160" - }, - { - "description": "multisig output : less than 4 chunks", - "scriptPubKey": "OP_0 0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 OP_HASH160" - }, - { - "description": "multisig output : m === 0", - "scriptPubKey": "OP_0 0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 02b80011a883a0fd621ad46dfc405df1e74bf075cbaf700fd4aebef6e96f848340 OP_2 OP_CHECKMULTISIG" - }, - { - "description": "multisig output : m < OP_1", - "scriptPubKey": "OP_1NEGATE 0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 02b80011a883a0fd621ad46dfc405df1e74bf075cbaf700fd4aebef6e96f848340 OP_2 OP_CHECKMULTISIG" - }, - { - "description": "multisig output : m > OP_16", - "scriptPubKey": "OP_NOP 0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 02b80011a883a0fd621ad46dfc405df1e74bf075cbaf700fd4aebef6e96f848340 OP_2 OP_CHECKMULTISIG" - }, - { - "description": "multisig output : n === 0", - "scriptPubKey": "OP_1 0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 02b80011a883a0fd621ad46dfc405df1e74bf075cbaf700fd4aebef6e96f848340 OP_0 OP_CHECKMULTISIG" - }, - { - "description": "multisig output : n < OP_1", - "scriptPubKey": "OP_1 0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 02b80011a883a0fd621ad46dfc405df1e74bf075cbaf700fd4aebef6e96f848340 OP_1NEGATE OP_CHECKMULTISIG" - }, - { - "description": "multisig output : n > OP_16", - "scriptPubKey": "OP_1 0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 02b80011a883a0fd621ad46dfc405df1e74bf075cbaf700fd4aebef6e96f848340 OP_NOP OP_CHECKMULTISIG" - }, - { - "description": "multisig output : n < m", - "scriptPubKey": "OP_2 0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 02b80011a883a0fd621ad46dfc405df1e74bf075cbaf700fd4aebef6e96f848340 OP_1 OP_CHECKMULTISIG" - }, - { - "description": "multisig output : n < len(pubKeys)", - "scriptPubKey": "OP_2 0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 02b80011a883a0fd621ad46dfc405df1e74bf075cbaf700fd4aebef6e96f848340 024289801366bcee6172b771cf5a7f13aaecd237a0b9a1ff9d769cabc2e6b70a34 OP_2 OP_CHECKMULTISIG" - }, - { - "description": "multisig output : non-canonical pubKey (bad length)", - "scriptPubKey": "OP_1 0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798ffff OP_1 OP_CHECKMULTISIG" - }, + "isPubKeyHashInput": [ { "description": "pubKeyHash input : extraneous data", "scriptSig": "304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801 02359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1 ffffffff" - }, + } + ], + "isScriptHashInput": [ { - "description": "scriptHash input : redeemScript not data", + "description": "redeemScript not data", "scriptSig": "OP_0 304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801 3045022100ef253c1faa39e65115872519e5f0a33bbecf430c0f35cf562beabbad4da24d8d02201742be8ee49812a73adea3007c9641ce6725c32cd44ddb8e3a3af460015d140501 OP_RESERVED" - }, + } + ], + "isPubKeyInput": [ { - "description": "pubKey input : non-canonical signature", + "description": "non-canonical signature", "scriptSig": "304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf7593ffffffffffffffff" } ], - "multisig": [ + "isMultisigOutput": [ { - "exception": "Not enough pubKeys provided", - "m": 4, - "pubKeys": [ - "02ea1297665dd733d444f31ec2581020004892cdaaf3dd6c0107c615afb839785f", - "02fab2dea1458990793f56f42e4a47dbf35a12a351f26fa5d7e0cc7447eaafa21f", - "036c6802ce7e8113723dd92cdb852e492ebb157a871ca532c3cb9ed08248ff0e19" - ], - "signatures": [ - "304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801" - ], - "scriptPubKey": true + "description": "OP_CHECKMULTISIG not found", + "scriptPubKey": "OP_0 0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 02b80011a883a0fd621ad46dfc405df1e74bf075cbaf700fd4aebef6e96f848340 OP_2 OP_HASH160" }, + { + "description": "less than 4 chunks", + "scriptPubKey": "OP_0 0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 OP_HASH160" + }, + { + "description": "m === 0", + "scriptPubKey": "OP_0 0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 02b80011a883a0fd621ad46dfc405df1e74bf075cbaf700fd4aebef6e96f848340 OP_2 OP_CHECKMULTISIG" + }, + { + "description": "m < OP_1", + "scriptPubKey": "OP_1NEGATE 0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 02b80011a883a0fd621ad46dfc405df1e74bf075cbaf700fd4aebef6e96f848340 OP_2 OP_CHECKMULTISIG" + }, + { + "description": "m > OP_16", + "scriptPubKey": "OP_NOP 0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 02b80011a883a0fd621ad46dfc405df1e74bf075cbaf700fd4aebef6e96f848340 OP_2 OP_CHECKMULTISIG" + }, + { + "description": "n === 0", + "scriptPubKey": "OP_1 0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 02b80011a883a0fd621ad46dfc405df1e74bf075cbaf700fd4aebef6e96f848340 OP_0 OP_CHECKMULTISIG" + }, + { + "description": "n < OP_1", + "scriptPubKey": "OP_1 0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 02b80011a883a0fd621ad46dfc405df1e74bf075cbaf700fd4aebef6e96f848340 OP_1NEGATE OP_CHECKMULTISIG" + }, + { + "description": "n > OP_16", + "scriptPubKey": "OP_1 0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 02b80011a883a0fd621ad46dfc405df1e74bf075cbaf700fd4aebef6e96f848340 OP_NOP OP_CHECKMULTISIG" + }, + { + "description": "n < m", + "scriptPubKey": "OP_2 0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 02b80011a883a0fd621ad46dfc405df1e74bf075cbaf700fd4aebef6e96f848340 OP_1 OP_CHECKMULTISIG" + }, + { + "description": "n < len(pubKeys)", + "scriptPubKey": "OP_2 0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 02b80011a883a0fd621ad46dfc405df1e74bf075cbaf700fd4aebef6e96f848340 024289801366bcee6172b771cf5a7f13aaecd237a0b9a1ff9d769cabc2e6b70a34 OP_2 OP_CHECKMULTISIG" + }, + { + "description": "non-canonical pubKey (bad length)", + "scriptPubKey": "OP_1 0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798ffff OP_1 OP_CHECKMULTISIG" + } + ], + "multisigInput": [ { "exception": "Not enough signatures provided", "pubKeys": [ @@ -136,8 +129,7 @@ ], "signatures": [ "304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801" - ], - "scriptPubKey": false + ] }, { "exception": "Too many signatures provided", @@ -149,8 +141,21 @@ "304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801", "3045022100ef253c1faa39e65115872519e5f0a33bbecf430c0f35cf562beabbad4da24d8d02201742be8ee49812a73adea3007c9641ce6725c32cd44ddb8e3a3af460015d140501", "3045022100ef253c1faa39e65115872519e5f0a33bbecf430c0f35cf562beabbad4da24d8d02201742be8ee49812a73adea3007c9641ce6725c32cd44ddb8e3a3af460015d140501" + ] + } + ], + "multisigOutput": [ + { + "exception": "Not enough pubKeys provided", + "m": 4, + "pubKeys": [ + "02ea1297665dd733d444f31ec2581020004892cdaaf3dd6c0107c615afb839785f", + "02fab2dea1458990793f56f42e4a47dbf35a12a351f26fa5d7e0cc7447eaafa21f", + "036c6802ce7e8113723dd92cdb852e492ebb157a871ca532c3cb9ed08248ff0e19" ], - "scriptPubKey": false + "signatures": [ + "304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801" + ] } ] } diff --git a/test/scripts.js b/test/scripts.js index 6a3c98b..41c3b30 100644 --- a/test/scripts.js +++ b/test/scripts.js @@ -1,7 +1,6 @@ var assert = require('assert') var scripts = require('../src/scripts') -var Address = require('../src/address') var ECPubKey = require('../src/ecpubkey') var Script = require('../src/script') @@ -19,17 +18,6 @@ describe('Scripts', function() { assert.equal(type, f.type) }) }) - - fixtures.invalid.classify.forEach(function(f) { - if (!f.scriptSig) return - - it('returns nonstandard for ' + f.description, function() { - var script = Script.fromASM(f.scriptSig) - var type = scripts.classifyInput(script) - - assert.equal(type, 'nonstandard') - }) - }) }) describe('classifyOutput', function() { @@ -43,159 +31,187 @@ describe('Scripts', function() { assert.equal(type, f.type) }) }) + }) - fixtures.invalid.classify.forEach(function(f) { - if (!f.scriptPubKey) return + ;['PubKey', 'PubKeyHash', 'ScriptHash', 'Multisig', 'NullData'].forEach(function(type) { + var inputFn = scripts['is' + type + 'Input'] + var outputFn= scripts['is' + type + 'Output'] - it('returns nonstandard for ' + f.description, function() { - var script = Script.fromASM(f.scriptPubKey) - var type = scripts.classifyOutput(script) + describe('is' + type + 'Input', function() { + fixtures.valid.forEach(function(f) { + var expected = type.toLowerCase() === f.type - assert.equal(type, 'nonstandard') + if (inputFn && f.scriptSig) { + it('returns ' + expected + ' for ' + f.scriptSig, function() { + var script = Script.fromASM(f.scriptSig) + + assert.equal(inputFn(script), expected) + }) + } + }) + }) + + describe('is' + type + 'Output', function() { + fixtures.valid.forEach(function(f) { + var expected = type.toLowerCase() === f.type + + if (outputFn && f.scriptPubKey) { + it('returns ' + expected + ' for ' + f.scriptPubKey, function() { + var script = Script.fromASM(f.scriptPubKey) + + assert.equal(outputFn(script), expected) + }) + } }) }) }) - describe('pubKey', function() { + describe('pubKeyInput', function() { fixtures.valid.forEach(function(f) { if (f.type !== 'pubkey') return - describe('input script', function() { - it('is generated correctly for ' + f.pubKey, function() { - var signature = new Buffer(f.signature, 'hex') + it('returns ' + f.scriptSig, function() { + var signature = new Buffer(f.signature, 'hex') - var scriptSig = scripts.pubKeyInput(signature) - assert.equal(scriptSig.toASM(), f.scriptSig) - }) - }) - - describe('output script', function() { - it('is generated correctly for ' + f.pubKey, function() { - var pubKey = ECPubKey.fromHex(f.pubKey) - - var scriptPubKey = scripts.pubKeyOutput(pubKey) - assert.equal(scriptPubKey.toASM(), f.scriptPubKey) - }) + var scriptSig = scripts.pubKeyInput(signature) + assert.equal(scriptSig.toASM(), f.scriptSig) }) }) }) - describe('pubKeyHash', function() { + describe('pubKeyOutput', function() { + fixtures.valid.forEach(function(f) { + if (f.type !== 'pubkey') return + + it('returns ' + f.scriptPubKey, function() { + var pubKey = ECPubKey.fromHex(f.pubKey) + + var scriptPubKey = scripts.pubKeyOutput(pubKey) + assert.equal(scriptPubKey.toASM(), f.scriptPubKey) + }) + }) + }) + + describe('pubKeyHashInput', function() { + fixtures.valid.forEach(function(f) { + if (f.type !== 'pubkeyhash') return + + var pubKey = ECPubKey.fromHex(f.pubKey) + + it('returns ' + f.scriptSig, function() { + var signature = new Buffer(f.signature, 'hex') + + var scriptSig = scripts.pubKeyHashInput(signature, pubKey) + assert.equal(scriptSig.toASM(), f.scriptSig) + }) + }) + }) + + describe('pubKeyHashOutput', function() { fixtures.valid.forEach(function(f) { if (f.type !== 'pubkeyhash') return var pubKey = ECPubKey.fromHex(f.pubKey) var address = pubKey.getAddress() - describe('input script', function() { - it('is generated correctly for ' + address, function() { - var signature = new Buffer(f.signature, 'hex') - - var scriptSig = scripts.pubKeyHashInput(signature, pubKey) - assert.equal(scriptSig.toASM(), f.scriptSig) - }) - }) - - describe('output script', function() { - it('is generated correctly for ' + address, function() { - var scriptPubKey = scripts.pubKeyHashOutput(address.hash) - assert.equal(scriptPubKey.toASM(), f.scriptPubKey) - }) + it('returns ' + f.scriptPubKey, function() { + var scriptPubKey = scripts.pubKeyHashOutput(address.hash) + assert.equal(scriptPubKey.toASM(), f.scriptPubKey) }) }) }) - describe('multisig', function() { + describe('multisigInput', function() { + fixtures.valid.forEach(function(f) { + if (f.type !== 'multisig') return + + it('returns ' + f.scriptSig, function() { + var signatures = f.signatures.map(function(signature) { + return new Buffer(signature, 'hex') + }) + + var scriptSig = scripts.multisigInput(signatures) + assert.equal(scriptSig.toASM(), f.scriptSig) + }) + }) + + fixtures.invalid.multisigInput.forEach(function(f) { + var pubKeys = f.pubKeys.map(ECPubKey.fromHex) + var scriptPubKey = scripts.multisigOutput(pubKeys.length, pubKeys) + + it('throws on ' + f.exception, function() { + var signatures = f.signatures.map(function(signature) { + return new Buffer(signature, 'hex') + }) + + assert.throws(function() { + scripts.multisigInput(signatures, scriptPubKey) + }, new RegExp(f.exception)) + }) + }) + }) + + describe('multisigOutput', function() { fixtures.valid.forEach(function(f) { if (f.type !== 'multisig') return var pubKeys = f.pubKeys.map(ECPubKey.fromHex) var scriptPubKey = scripts.multisigOutput(pubKeys.length, pubKeys) - describe('input script', function() { - it('is generated correctly for ' + f.scriptPubKey, function() { - var signatures = f.signatures.map(function(signature) { - return new Buffer(signature, 'hex') - }) - - var scriptSig = scripts.multisigInput(signatures) - assert.equal(scriptSig.toASM(), f.scriptSig) - }) - }) - - describe('output script', function() { - it('is generated correctly for ' + f.scriptPubKey, function() { - assert.equal(scriptPubKey.toASM(), f.scriptPubKey) - }) + it('returns ' + f.scriptPubKey, function() { + assert.equal(scriptPubKey.toASM(), f.scriptPubKey) }) }) - fixtures.invalid.multisig.forEach(function(f) { - var pubKeys = f.pubKeys.map(ECPubKey.fromHex) - var scriptPubKey = scripts.multisigOutput(pubKeys.length, pubKeys) + fixtures.invalid.multisigOutput.forEach(function(f) { + var pubKeys = f.pubKeys.map(function(p) { return new Buffer(p, 'hex') }) - if (f.scriptPubKey) { - describe('output script', function() { - it('throws on ' + f.exception, function() { - assert.throws(function() { - scripts.multisigOutput(f.m, pubKeys) - }, new RegExp(f.exception)) - }) - }) - } else { - describe('input script', function() { - it('throws on ' + f.exception, function() { - var signatures = f.signatures.map(function(signature) { - return new Buffer(signature, 'hex') - }) - - assert.throws(function() { - scripts.multisigInput(signatures, scriptPubKey) - }, new RegExp(f.exception)) - }) - }) - } + it('throws on ' + f.exception, function() { + assert.throws(function() { + scripts.multisigOutput(f.m, pubKeys) + }, new RegExp(f.exception)) + }) }) }) - describe('scripthash', function() { + describe('scriptHashInput', function() { fixtures.valid.forEach(function(f) { if (f.type !== 'scripthash') return var redeemScript = Script.fromASM(f.redeemScript) var redeemScriptSig = Script.fromASM(f.redeemScriptSig) - var address = Address.fromOutputScript(Script.fromASM(f.scriptPubKey)) + it('returns ' + f.scriptSig, function() { + var scriptSig = scripts.scriptHashInput(redeemScriptSig, redeemScript) - describe('input script', function() { - it('is generated correctly for ' + address, function() { - var scriptSig = scripts.scriptHashInput(redeemScriptSig, redeemScript) - - assert.equal(scriptSig.toASM(), f.scriptSig) - }) - }) - - describe('output script', function() { - it('is generated correctly for ' + address, function() { - var scriptPubKey = scripts.scriptHashOutput(redeemScript.getHash()) - - assert.equal(scriptPubKey.toASM(), f.scriptPubKey) - }) + assert.equal(scriptSig.toASM(), f.scriptSig) }) }) }) - describe('data', function() { + describe('scriptHashOutput', function() { + fixtures.valid.forEach(function(f) { + if (f.type !== 'scripthash') return + + var redeemScript = Script.fromASM(f.redeemScript) + + it('returns ' + f.scriptPubKey, function() { + var scriptPubKey = scripts.scriptHashOutput(redeemScript.getHash()) + + assert.equal(scriptPubKey.toASM(), f.scriptPubKey) + }) + }) + }) + + describe('nullDataOutput', function() { fixtures.valid.forEach(function(f) { if (f.type !== 'nulldata') return var data = new Buffer(f.data, 'hex') var scriptPubKey = scripts.nullDataOutput(data) - describe('output script', function() { - it('is generated correctly for ' + f.scriptPubKey, function() { - assert.equal(scriptPubKey.toASM(), f.scriptPubKey) - }) + it('returns ' + f.scriptPubKey, function() { + assert.equal(scriptPubKey.toASM(), f.scriptPubKey) }) }) })