From 62b6a407a6f258c7d36afc251ba83daa9f46e230 Mon Sep 17 00:00:00 2001 From: Daniel Cousens Date: Tue, 24 Jun 2014 17:30:37 +1000 Subject: [PATCH 01/11] scripts: break tests down from type structure --- test/fixtures/scripts.json | 97 ++++++++++++++++++-------------------- test/scripts.js | 16 +++++-- 2 files changed, 59 insertions(+), 54 deletions(-) diff --git a/test/fixtures/scripts.json b/test/fixtures/scripts.json index 58eb32e..f9c5f28 100644 --- a/test/fixtures/scripts.json +++ b/test/fixtures/scripts.json @@ -1,54 +1,51 @@ { - "valid": { - "pubKey": [ - { - "pubKey": "02359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1", - "signature": "304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801", - "scriptPubKey": "2102359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1ac", - "scriptSig": "47304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801" - } - ], - "pubKeyHash": [ - { - "pubKey": "02359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1", - "signature": "304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801", - "scriptPubKey": "76a914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac", - "scriptSig": "47304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca28012102359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1" - } - ], - "multisig": [ - { - "pubKeys": [ - "02359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1", - "0395a9d84d47d524548f79f435758c01faec5da2b7e551d3b8c995b7e06326ae4a" - ], - "signatures": [ - "304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801", - "3045022100ef253c1faa39e65115872519e5f0a33bbecf430c0f35cf562beabbad4da24d8d02201742be8ee49812a73adea3007c9641ce6725c32cd44ddb8e3a3af460015d140501" - ], - "scriptPubKey": "522102359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1210395a9d84d47d524548f79f435758c01faec5da2b7e551d3b8c995b7e06326ae4a52ae", - "scriptSig": "0047304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801483045022100ef253c1faa39e65115872519e5f0a33bbecf430c0f35cf562beabbad4da24d8d02201742be8ee49812a73adea3007c9641ce6725c32cd44ddb8e3a3af460015d140501" - }, - { - "pubKeys": [ - "02ea1297665dd733d444f31ec2581020004892cdaaf3dd6c0107c615afb839785f", - "02fab2dea1458990793f56f42e4a47dbf35a12a351f26fa5d7e0cc7447eaafa21f", - "036c6802ce7e8113723dd92cdb852e492ebb157a871ca532c3cb9ed08248ff0e19" - ], - "signatures": [], - "scriptPubKey": "532102ea1297665dd733d444f31ec2581020004892cdaaf3dd6c0107c615afb839785f2102fab2dea1458990793f56f42e4a47dbf35a12a351f26fa5d7e0cc7447eaafa21f21036c6802ce7e8113723dd92cdb852e492ebb157a871ca532c3cb9ed08248ff0e1953ae", - "scriptSig": "" - } - ], - "scripthash": [ - { - "redeemScript": "522102359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1210395a9d84d47d524548f79f435758c01faec5da2b7e551d3b8c995b7e06326ae4a52ae", - "redeemScriptSig": "0047304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801483045022100ef253c1faa39e65115872519e5f0a33bbecf430c0f35cf562beabbad4da24d8d02201742be8ee49812a73adea3007c9641ce6725c32cd44ddb8e3a3af460015d140501", - "scriptSig": "0047304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801483045022100ef253c1faa39e65115872519e5f0a33bbecf430c0f35cf562beabbad4da24d8d02201742be8ee49812a73adea3007c9641ce6725c32cd44ddb8e3a3af460015d14050147522102359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1210395a9d84d47d524548f79f435758c01faec5da2b7e551d3b8c995b7e06326ae4a52ae", - "scriptPubKey": "a914722ff0bc2c3f47b35c20df646c395594da24e90e87" - } - ] - }, + "valid": [ + { + "type": "pubkey", + "pubKey": "02359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1", + "signature": "304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801", + "scriptPubKey": "2102359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1ac", + "scriptSig": "47304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801" + }, + { + "type": "pubkeyhash", + "pubKey": "02359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1", + "signature": "304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801", + "scriptPubKey": "76a914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac", + "scriptSig": "47304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca28012102359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1" + }, + { + "type": "multisig", + "pubKeys": [ + "02359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1", + "0395a9d84d47d524548f79f435758c01faec5da2b7e551d3b8c995b7e06326ae4a" + ], + "signatures": [ + "304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801", + "3045022100ef253c1faa39e65115872519e5f0a33bbecf430c0f35cf562beabbad4da24d8d02201742be8ee49812a73adea3007c9641ce6725c32cd44ddb8e3a3af460015d140501" + ], + "scriptPubKey": "522102359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1210395a9d84d47d524548f79f435758c01faec5da2b7e551d3b8c995b7e06326ae4a52ae", + "scriptSig": "0047304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801483045022100ef253c1faa39e65115872519e5f0a33bbecf430c0f35cf562beabbad4da24d8d02201742be8ee49812a73adea3007c9641ce6725c32cd44ddb8e3a3af460015d140501" + }, + { + "type": "multisig", + "pubKeys": [ + "02ea1297665dd733d444f31ec2581020004892cdaaf3dd6c0107c615afb839785f", + "02fab2dea1458990793f56f42e4a47dbf35a12a351f26fa5d7e0cc7447eaafa21f", + "036c6802ce7e8113723dd92cdb852e492ebb157a871ca532c3cb9ed08248ff0e19" + ], + "signatures": [], + "scriptPubKey": "532102ea1297665dd733d444f31ec2581020004892cdaaf3dd6c0107c615afb839785f2102fab2dea1458990793f56f42e4a47dbf35a12a351f26fa5d7e0cc7447eaafa21f21036c6802ce7e8113723dd92cdb852e492ebb157a871ca532c3cb9ed08248ff0e1953ae", + "scriptSig": "" + }, + { + "type": "scripthash", + "redeemScript": "522102359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1210395a9d84d47d524548f79f435758c01faec5da2b7e551d3b8c995b7e06326ae4a52ae", + "redeemScriptSig": "0047304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801483045022100ef253c1faa39e65115872519e5f0a33bbecf430c0f35cf562beabbad4da24d8d02201742be8ee49812a73adea3007c9641ce6725c32cd44ddb8e3a3af460015d140501", + "scriptSig": "0047304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801483045022100ef253c1faa39e65115872519e5f0a33bbecf430c0f35cf562beabbad4da24d8d02201742be8ee49812a73adea3007c9641ce6725c32cd44ddb8e3a3af460015d14050147522102359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1210395a9d84d47d524548f79f435758c01faec5da2b7e551d3b8c995b7e06326ae4a52ae", + "scriptPubKey": "a914722ff0bc2c3f47b35c20df646c395594da24e90e87" + } + ], "invalid": { "multisig": [ { diff --git a/test/scripts.js b/test/scripts.js index 8aed551..31ec045 100644 --- a/test/scripts.js +++ b/test/scripts.js @@ -36,7 +36,9 @@ describe('Scripts', function() { }) describe('pubKey', function() { - fixtures2.valid.pubKey.forEach(function(f) { + fixtures2.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') @@ -58,7 +60,9 @@ describe('Scripts', function() { }) describe('pubKeyHash', function() { - fixtures2.valid.pubKeyHash.forEach(function(f) { + fixtures2.valid.forEach(function(f) { + if (f.type !== 'pubkeyhash') return + var pubKey = ECPubKey.fromHex(f.pubKey) var address = pubKey.getAddress() @@ -81,7 +85,9 @@ describe('Scripts', function() { }) describe('multisig', function() { - fixtures2.valid.multisig.forEach(function(f) { + fixtures2.valid.forEach(function(f) { + if (f.type !== 'multisig') return + var pubKeys = f.pubKeys.map(ECPubKey.fromHex) var scriptPubKey = scripts.multisigOutput(pubKeys.length, pubKeys) @@ -135,7 +141,9 @@ describe('Scripts', function() { }) describe('scripthash', function() { - fixtures2.valid.scripthash.forEach(function(f) { + fixtures2.valid.forEach(function(f) { + if (f.type !== 'scripthash') return + var redeemScript = Script.fromHex(f.redeemScript) var redeemScriptSig = Script.fromHex(f.redeemScriptSig) From 576fbbfff5e2d74d6ee287adfcff147f581ef17d Mon Sep 17 00:00:00 2001 From: Daniel Cousens Date: Tue, 24 Jun 2014 17:31:55 +1000 Subject: [PATCH 02/11] scripts: switch to failing to classification tests --- test/scripts.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/test/scripts.js b/test/scripts.js index 31ec045..d6bc4b4 100644 --- a/test/scripts.js +++ b/test/scripts.js @@ -10,11 +10,11 @@ var fixtures2 = require('./fixtures/scripts.json') describe('Scripts', function() { describe('classifyInput', function() { - fixtures.valid.forEach(function(f) { - if (f.scriptPubKey) return + fixtures2.valid.forEach(function(f) { + if (!f.scriptSig) return - it('supports ' + f.type, function() { - var script = Script.fromHex(f.hex) + it('classifies ' + f.scriptSig + ' as ' + f.type, function() { + var script = Script.fromHex(f.scriptSig) var type = scripts.classifyInput(script) assert.equal(type, f.type) @@ -23,11 +23,11 @@ describe('Scripts', function() { }) describe('classifyOutput', function() { - fixtures.valid.forEach(function(f) { + fixtures2.valid.forEach(function(f) { if (!f.scriptPubKey) return - it('supports ' + f.type, function() { - var script = Script.fromHex(f.hex) + it('classifies ' + f.scriptPubKey + ' as ' + f.type, function() { + var script = Script.fromHex(f.scriptPubKey) var type = scripts.classifyOutput(script) assert.equal(type, f.type) From daa2cb7daabc0b8a2368c02021de512d76ebe259 Mon Sep 17 00:00:00 2001 From: Daniel Cousens Date: Tue, 24 Jun 2014 17:32:23 +1000 Subject: [PATCH 03/11] scripts: fix classification logic --- src/scripts.js | 166 ++++++++++++++++++++++++++++++++++++------------- 1 file changed, 122 insertions(+), 44 deletions(-) diff --git a/src/scripts.js b/src/scripts.js index fc5d1e1..09098c9 100644 --- a/src/scripts.js +++ b/src/scripts.js @@ -1,87 +1,165 @@ var assert = require('assert') var opcodes = require('./opcodes') + +// FIXME: use ECPubKey, currently the circular dependency breaks everything. +// +// Solutions: +// * Remove ECPubKey.getAddress +// - Minimal change, but likely unpopular +// * Move all script related functionality out of Address +// - Means a lot of changes to Transaction/Wallet +// * Ignore it (existing solution) +// * Some form of hackery with commonjs +// +var ecurve = require('ecurve') +var curve = ecurve.getCurveByName('secp256k1') + +var ECSignature = require('./ecsignature') var Script = require('./script') function classifyOutput(script) { assert(script instanceof Script, 'Expected Script, got ', script) - if (isPubkeyhash.call(script)) { + if (isPubKeyHashOutput.call(script)) { return 'pubkeyhash' - } else if (isPubkey.call(script)) { - return 'pubkey' - } else if (isScripthash.call(script)) { + } else if (isScriptHashOutput.call(script)) { return 'scripthash' - } else if (isMultisig.call(script)) { + } else if (isMultisigOutput.call(script)) { return 'multisig' - } else if (isNulldata.call(script)) { + } else if (isPubKeyOutput.call(script)) { + return 'pubkey' + } else if (isNulldataOutput.call(script)) { return 'nulldata' } else { return 'nonstandard' } } -function classifyInput(script) { +function classifyInput(script, checkScriptHash) { assert(script instanceof Script, 'Expected Script, got ', script) + if (checkScriptHash === undefined) checkScriptHash = true - if (script.chunks.length == 1 && Buffer.isBuffer(script.chunks[0])) { - return 'pubkey' - } else if (script.chunks.length == 2 && Buffer.isBuffer(script.chunks[0]) && Buffer.isBuffer(script.chunks[1])) { + if (isPubKeyHashInput.call(script)) { return 'pubkeyhash' - } else if (script.chunks[0] == opcodes.OP_0 && script.chunks.slice(1).reduce(function(t, chunk, i) { - return t && Buffer.isBuffer(chunk) && (chunk[0] == 48 || i == script.chunks.length - 1) - }, true)) { + } else if (checkScriptHash && isScriptHashInput.call(script)) { + return 'scripthash' + } else if (isMultisigInput.call(script)) { return 'multisig' + } else if (isPubKeyInput.call(script)) { + return 'pubkey' } else { return 'nonstandard' } } -function isPubkeyhash() { - return this.chunks.length == 5 && - this.chunks[0] == opcodes.OP_DUP && - this.chunks[1] == opcodes.OP_HASH160 && - Buffer.isBuffer(this.chunks[2]) && - this.chunks[2].length === 20 && - this.chunks[3] == opcodes.OP_EQUALVERIFY && - this.chunks[4] == opcodes.OP_CHECKSIG +function isCanonicalPubKey(buffer) { + if (!Buffer.isBuffer(buffer)) return false + + try { + // FIXME: boo + ecurve.Point.decodeFrom(curve, buffer) + } catch (e) { + if (!(e.message.match(/Invalid sequence (length|tag)/))) throw e + + return false + } + + return true } -function isPubkey() { +function isCanonicalSignature(buffer) { + if (!Buffer.isBuffer(buffer)) return false + + try { + ECSignature.parseScriptSignature(buffer) + } catch(e) { + if (!(e.message.match(/Not a DER sequence|Invalid sequence length|Expected a DER integer|R length is zero|S length is zero|R value excessively padded|S value excessively padded|R value is negative|S value is negative|Invalid hashType/))) throw e + + return false + } + + return true +} + +function isPubKeyHashInput() { return this.chunks.length === 2 && - Buffer.isBuffer(this.chunks[0]) && + isCanonicalSignature(this.chunks[0]) && + isCanonicalPubKey(this.chunks[1]) +} + +function isPubKeyHashOutput() { + return this.chunks.length === 5 && + this.chunks[0] === opcodes.OP_DUP && + this.chunks[1] === opcodes.OP_HASH160 && + Buffer.isBuffer(this.chunks[2]) && + this.chunks[2].length === 20 && + this.chunks[3] === opcodes.OP_EQUALVERIFY && + this.chunks[4] === opcodes.OP_CHECKSIG +} + +function isPubKeyInput() { + return this.chunks.length === 1 && + isCanonicalSignature(this.chunks[0]) +} + +function isPubKeyOutput() { + return this.chunks.length === 2 && + isCanonicalPubKey(this.chunks[0]) && this.chunks[1] === opcodes.OP_CHECKSIG } -function isScripthash() { - return this.chunks[this.chunks.length - 1] == opcodes.OP_EQUAL && - this.chunks[0] == opcodes.OP_HASH160 && +function isScriptHashInput() { + if (this.chunks.length < 2) return false + var lastChunk = this.chunks[this.chunks.length - 1] + + if (!Buffer.isBuffer(lastChunk)) return false + + var scriptSig = Script.fromChunks(this.chunks.slice(0, -1)) + var scriptPubKey = Script.fromBuffer(lastChunk) + + return classifyInput(scriptSig, false) === classifyOutput(scriptPubKey) +} + +function isScriptHashOutput() { + return this.chunks.length === 3 && + this.chunks[0] === opcodes.OP_HASH160 && Buffer.isBuffer(this.chunks[1]) && this.chunks[1].length === 20 && - this.chunks.length == 3 + this.chunks[2] === opcodes.OP_EQUAL } -function isMultisig() { - return this.chunks.length > 3 && - // m is a smallint - isSmallIntOp(this.chunks[0]) && - // n is a smallint - isSmallIntOp(this.chunks[this.chunks.length - 2]) && - // n greater or equal to m - this.chunks[0] <= this.chunks[this.chunks.length - 2] && - // n cannot be 0 - this.chunks[this.chunks.length - 2] !== opcodes.OP_0 && - // n is the size of chunk length minus 3 (m, n, OP_CHECKMULTISIG) - this.chunks.length - 3 === this.chunks[this.chunks.length - 2] - opcodes.OP_RESERVED && - // last chunk is OP_CHECKMULTISIG - this.chunks[this.chunks.length - 1] == opcodes.OP_CHECKMULTISIG +function isMultisigInput() { + return this.chunks[0] === opcodes.OP_0 && + this.chunks.slice(1).every(isCanonicalSignature) } -function isNulldata() { +function isMultisigOutput() { + if (this.chunks < 4) return false + if (this.chunks[this.chunks.length - 1] !== opcodes.OP_CHECKMULTISIG) return false + + var mS = this.chunks[0] + if (!isSmallIntOp(mS)) return false + + var nS = this.chunks[this.chunks.length - 2] + if (!isSmallIntOp(nS)) return false + + var m = mS - (opcodes.OP_1 - 1) + var n = nS - (opcodes.OP_1 - 1) + if (n < m) return false + if (n === 0) return false + if (m > (this.chunks.length - 3)) return false + + return this.chunks.slice(1, -2).every(isCanonicalPubKey) +} + +function isNulldataOutput() { return this.chunks[0] === opcodes.OP_RETURN } function isSmallIntOp(opcode) { - return ((opcode == opcodes.OP_0) || ((opcode >= opcodes.OP_1) && (opcode <= opcodes.OP_16))) + if (Buffer.isBuffer(opcode)) return false + + return ((opcode === opcodes.OP_0) || ((opcode >= opcodes.OP_1) && (opcode <= opcodes.OP_16))) } // Standard Script Templates @@ -160,7 +238,7 @@ function scriptHashInput(scriptSig, scriptPubKey) { // OP_0 [signatures ...] function multisigInput(signatures, scriptPubKey) { if (scriptPubKey) { - assert(isMultisig.call(scriptPubKey)) + assert(isMultisigOutput.call(scriptPubKey)) var m = scriptPubKey.chunks[0] var k = m - (opcodes.OP_1 - 1) From c637cb4be75c01f4d041c62fc4db8e4cfa57b3ed Mon Sep 17 00:00:00 2001 From: Daniel Cousens Date: Tue, 24 Jun 2014 17:41:08 +1000 Subject: [PATCH 04/11] tests: remove unused import --- test/scripts.js | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/test/scripts.js b/test/scripts.js index d6bc4b4..13a2b2f 100644 --- a/test/scripts.js +++ b/test/scripts.js @@ -5,12 +5,11 @@ var Address = require('../src/address') var ECPubKey = require('../src/ecpubkey') var Script = require('../src/script') -var fixtures = require('./fixtures/script.json') -var fixtures2 = require('./fixtures/scripts.json') +var fixtures = require('./fixtures/scripts.json') describe('Scripts', function() { describe('classifyInput', function() { - fixtures2.valid.forEach(function(f) { + fixtures.valid.forEach(function(f) { if (!f.scriptSig) return it('classifies ' + f.scriptSig + ' as ' + f.type, function() { @@ -23,7 +22,7 @@ describe('Scripts', function() { }) describe('classifyOutput', function() { - fixtures2.valid.forEach(function(f) { + fixtures.valid.forEach(function(f) { if (!f.scriptPubKey) return it('classifies ' + f.scriptPubKey + ' as ' + f.type, function() { @@ -36,7 +35,7 @@ describe('Scripts', function() { }) describe('pubKey', function() { - fixtures2.valid.forEach(function(f) { + fixtures.valid.forEach(function(f) { if (f.type !== 'pubkey') return describe('input script', function() { @@ -60,7 +59,7 @@ describe('Scripts', function() { }) describe('pubKeyHash', function() { - fixtures2.valid.forEach(function(f) { + fixtures.valid.forEach(function(f) { if (f.type !== 'pubkeyhash') return var pubKey = ECPubKey.fromHex(f.pubKey) @@ -85,7 +84,7 @@ describe('Scripts', function() { }) describe('multisig', function() { - fixtures2.valid.forEach(function(f) { + fixtures.valid.forEach(function(f) { if (f.type !== 'multisig') return var pubKeys = f.pubKeys.map(ECPubKey.fromHex) @@ -112,7 +111,7 @@ describe('Scripts', function() { }) }) - fixtures2.invalid.multisig.forEach(function(f) { + fixtures.invalid.multisig.forEach(function(f) { var pubKeys = f.pubKeys.map(ECPubKey.fromHex) var scriptPubKey = scripts.multisigOutput(pubKeys.length, pubKeys) @@ -141,7 +140,7 @@ describe('Scripts', function() { }) describe('scripthash', function() { - fixtures2.valid.forEach(function(f) { + fixtures.valid.forEach(function(f) { if (f.type !== 'scripthash') return var redeemScript = Script.fromHex(f.redeemScript) From 8b1e2c5e1ec0e41f54a38057bbe06069f2ccbf8a Mon Sep 17 00:00:00 2001 From: Daniel Cousens Date: Tue, 24 Jun 2014 17:47:16 +1000 Subject: [PATCH 05/11] scripts: recursive scriptHash no longer problematic --- src/scripts.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/scripts.js b/src/scripts.js index 09098c9..a442c47 100644 --- a/src/scripts.js +++ b/src/scripts.js @@ -35,13 +35,12 @@ function classifyOutput(script) { } } -function classifyInput(script, checkScriptHash) { +function classifyInput(script) { assert(script instanceof Script, 'Expected Script, got ', script) - if (checkScriptHash === undefined) checkScriptHash = true if (isPubKeyHashInput.call(script)) { return 'pubkeyhash' - } else if (checkScriptHash && isScriptHashInput.call(script)) { + } else if (isScriptHashInput.call(script)) { return 'scripthash' } else if (isMultisigInput.call(script)) { return 'multisig' From 924ecfb998bcc45591bf56b485ec59ca55970424 Mon Sep 17 00:00:00 2001 From: Daniel Cousens Date: Wed, 25 Jun 2014 15:20:28 +1000 Subject: [PATCH 06/11] scripts: add missing test data --- test/fixtures/scripts.json | 16 ++++++++++------ test/scripts.js | 19 ++++++++----------- 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/test/fixtures/scripts.json b/test/fixtures/scripts.json index f9c5f28..4e5c997 100644 --- a/test/fixtures/scripts.json +++ b/test/fixtures/scripts.json @@ -30,13 +30,17 @@ { "type": "multisig", "pubKeys": [ - "02ea1297665dd733d444f31ec2581020004892cdaaf3dd6c0107c615afb839785f", - "02fab2dea1458990793f56f42e4a47dbf35a12a351f26fa5d7e0cc7447eaafa21f", - "036c6802ce7e8113723dd92cdb852e492ebb157a871ca532c3cb9ed08248ff0e19" + "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", + "02b80011a883a0fd621ad46dfc405df1e74bf075cbaf700fd4aebef6e96f848340", + "024289801366bcee6172b771cf5a7f13aaecd237a0b9a1ff9d769cabc2e6b70a34" ], - "signatures": [], - "scriptPubKey": "532102ea1297665dd733d444f31ec2581020004892cdaaf3dd6c0107c615afb839785f2102fab2dea1458990793f56f42e4a47dbf35a12a351f26fa5d7e0cc7447eaafa21f21036c6802ce7e8113723dd92cdb852e492ebb157a871ca532c3cb9ed08248ff0e1953ae", - "scriptSig": "" + "signatures": [ + "3045022100fe324541215798b2df68cbd44039615e23c506d4ec1a05572064392a98196b82022068c849fa6699206da2fc6d7848efc1d3804a5816d6293615fe34c1a7f34e1c2f01", + "3044022001ab168e80b863fdec694350b587339bb72a37108ac3c989849251444d13ebba02201811272023e3c1038478eb972a82d3ad431bfc2408e88e4da990f1a7ecbb263901", + "3045022100aaeb7204c17eee2f2c4ff1c9f8b39b79e75e7fbf33e92cc67ac51be8f15b75f90220659eee314a4943a6384d2b154fa5821ef7a084814d7ee2c6f9f7f0ffb53be34b01" + ], + "scriptPubKey": "53210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f817982102b80011a883a0fd621ad46dfc405df1e74bf075cbaf700fd4aebef6e96f84834021024289801366bcee6172b771cf5a7f13aaecd237a0b9a1ff9d769cabc2e6b70a3453ae", + "scriptSig": "00483045022100fe324541215798b2df68cbd44039615e23c506d4ec1a05572064392a98196b82022068c849fa6699206da2fc6d7848efc1d3804a5816d6293615fe34c1a7f34e1c2f01473044022001ab168e80b863fdec694350b587339bb72a37108ac3c989849251444d13ebba02201811272023e3c1038478eb972a82d3ad431bfc2408e88e4da990f1a7ecbb263901483045022100aaeb7204c17eee2f2c4ff1c9f8b39b79e75e7fbf33e92cc67ac51be8f15b75f90220659eee314a4943a6384d2b154fa5821ef7a084814d7ee2c6f9f7f0ffb53be34b01" }, { "type": "scripthash", diff --git a/test/scripts.js b/test/scripts.js index 13a2b2f..139ec65 100644 --- a/test/scripts.js +++ b/test/scripts.js @@ -90,19 +90,16 @@ describe('Scripts', function() { var pubKeys = f.pubKeys.map(ECPubKey.fromHex) var scriptPubKey = scripts.multisigOutput(pubKeys.length, pubKeys) - // FIXME: some missing test data for now - if (f.scriptSig) { - describe('input script', function() { - it('is generated correctly for ' + scriptPubKey.toHex(), function() { - var signatures = f.signatures.map(function(signature) { - return new Buffer(signature, 'hex') - }) - - var scriptSig = scripts.multisigInput(signatures) - assert.equal(scriptSig.toHex(), f.scriptSig) + describe('input script', function() { + it('is generated correctly for ' + scriptPubKey.toHex(), function() { + var signatures = f.signatures.map(function(signature) { + return new Buffer(signature, 'hex') }) + + var scriptSig = scripts.multisigInput(signatures) + assert.equal(scriptSig.toHex(), f.scriptSig) }) - } + }) describe('output script', function() { it('is generated correctly for ' + scriptPubKey.toHex(), function() { From 7e85515e375cf9fa5bd84148934f54b2c3c830c0 Mon Sep 17 00:00:00 2001 From: Daniel Cousens Date: Wed, 25 Jun 2014 15:42:16 +1000 Subject: [PATCH 07/11] opcodes: prefer OP_0 --- src/opcodes.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/opcodes.js b/src/opcodes.js index 5db4049..974c8d0 100644 --- a/src/opcodes.js +++ b/src/opcodes.js @@ -1,7 +1,7 @@ module.exports = { // push value - OP_0 : 0, OP_FALSE : 0, + OP_0 : 0, OP_PUSHDATA1 : 76, OP_PUSHDATA2 : 77, OP_PUSHDATA4 : 78, From 13d41f67e9c55e1b32eb299442fbecafa7e62c20 Mon Sep 17 00:00:00 2001 From: Daniel Cousens Date: Wed, 25 Jun 2014 15:44:15 +1000 Subject: [PATCH 08/11] Script: adds toASM/fromASM --- src/script.js | 32 ++++++++++++++++++++++++++++++++ test/fixtures/script.json | 22 +++++++++++----------- test/fixtures/scripts.json | 24 ++++++++++++------------ test/script.js | 8 ++++++++ test/scripts.js | 30 +++++++++++++++--------------- 5 files changed, 78 insertions(+), 38 deletions(-) diff --git a/src/script.js b/src/script.js index 700b7ff..1556598 100644 --- a/src/script.js +++ b/src/script.js @@ -12,6 +12,21 @@ function Script(buffer, chunks) { } // Import operations +Script.fromASM = function(asm) { + var strChunks = asm.split(' ') + + var chunks = strChunks.map(function(strChunk) { + if (strChunk in opcodes) { + return opcodes[strChunk] + + } else { + return new Buffer(strChunk, 'hex') + } + }) + + return Script.fromChunks(chunks) +} + Script.fromBuffer = function(buffer) { var chunks = [] @@ -90,6 +105,23 @@ Script.prototype.without = function(needle) { } // Export operations +var reverseOps = [] +for (var op in opcodes) { + var code = opcodes[op] + reverseOps[code] = op +} + +Script.prototype.toASM = function() { + return this.chunks.map(function(chunk) { + if (Buffer.isBuffer(chunk)) { + return chunk.toString('hex') + + } else { + return reverseOps[chunk] + } + }).join(' ') +} + Script.prototype.toBuffer = function() { return this.buffer } diff --git a/test/fixtures/script.json b/test/fixtures/script.json index a393444..e06f9f0 100644 --- a/test/fixtures/script.json +++ b/test/fixtures/script.json @@ -6,7 +6,7 @@ "type": "pubkey", "hash": "26e645ab170255f2a0a82d29e48f35b14ae7c826", "pubKey": "031f1e68f82112b373f0fe980b3a89d212d2b5c01fb51eb25acb8b4c4b4299ce95", - "asm": "33 031f1e68f82112b373f0fe980b3a89d212d2b5c01fb51eb25acb8b4c4b4299ce95 OP_CHECKSIG", + "asm": "031f1e68f82112b373f0fe980b3a89d212d2b5c01fb51eb25acb8b4c4b4299ce95 OP_CHECKSIG", "scriptPubKey": true }, { @@ -14,7 +14,7 @@ "hex": "a914e8c300c87986efa84c37c0519929019ef86eb5b487", "type": "scripthash", "hash": "0ba47b56a573bab4b430ad6ed3ec79270e04b066", - "asm": "OP_HASH160 20 e8c300c87986efa84c37c0519929019ef86eb5b4 OP_EQUAL", + "asm": "OP_HASH160 e8c300c87986efa84c37c0519929019ef86eb5b4 OP_EQUAL", "scriptPubKey": true }, { @@ -22,7 +22,7 @@ "hex": "76a9145a3acbc7bbcc97c5ff16f5909c9d7d3fadb293a888ac", "type": "pubkeyhash", "hash": "a5313f33d5c7b81674b35f7f3febc3522ef234db", - "asm": "OP_DUP OP_HASH160 20 5a3acbc7bbcc97c5ff16f5909c9d7d3fadb293a8 OP_EQUALVERIFY OP_CHECKSIG", + "asm": "OP_DUP OP_HASH160 5a3acbc7bbcc97c5ff16f5909c9d7d3fadb293a8 OP_EQUALVERIFY OP_CHECKSIG", "scriptPubKey": true }, { @@ -30,7 +30,7 @@ "hex": "48304502206becda98cecf7a545d1a640221438ff8912d9b505ede67e0138485111099f696022100ccd616072501310acba10feb97cecc918e21c8e92760cd35144efec7622938f30141040cd2d2ce17a1e9b2b3b2cb294d40eecf305a25b7e7bfdafae6bb2639f4ee399b3637706c3d377ec4ab781355add443ae864b134c5e523001c442186ea60f0eb8", "type": "pubkeyhash", "hash": "b9bac2a5c5c29bb27c382d41fa3d179c646c78fd", - "asm": "72 304502206becda98cecf7a545d1a640221438ff8912d9b505ede67e0138485111099f696022100ccd616072501310acba10feb97cecc918e21c8e92760cd35144efec7622938f301 65 040cd2d2ce17a1e9b2b3b2cb294d40eecf305a25b7e7bfdafae6bb2639f4ee399b3637706c3d377ec4ab781355add443ae864b134c5e523001c442186ea60f0eb8", + "asm": "304502206becda98cecf7a545d1a640221438ff8912d9b505ede67e0138485111099f696022100ccd616072501310acba10feb97cecc918e21c8e92760cd35144efec7622938f301 040cd2d2ce17a1e9b2b3b2cb294d40eecf305a25b7e7bfdafae6bb2639f4ee399b3637706c3d377ec4ab781355add443ae864b134c5e523001c442186ea60f0eb8", "scriptPubKey": false }, { @@ -39,7 +39,7 @@ "type": "pubkey", "hash": "44d9982c3e79452e02ef5816976a0e20a0ec1cba", "signature": "304502206becda98cecf7a545d1a640221438ff8912d9b505ede67e0138485111099f696022100ccd616072501310acba10feb97cecc918e21c8e92760cd35144efec7622938f301", - "asm": "72 304502206becda98cecf7a545d1a640221438ff8912d9b505ede67e0138485111099f696022100ccd616072501310acba10feb97cecc918e21c8e92760cd35144efec7622938f301", + "asm": "304502206becda98cecf7a545d1a640221438ff8912d9b505ede67e0138485111099f696022100ccd616072501310acba10feb97cecc918e21c8e92760cd35144efec7622938f301", "scriptPubKey": false }, { @@ -47,7 +47,7 @@ "hex": "5121032487c2a32f7c8d57d2a93906a6457afd00697925b0e6e145d89af6d3bca330162102308673d16987eaa010e540901cc6fe3695e758c19f46ce604e174dac315e685a52ae", "type": "multisig", "hash": "f1c98f0b74ecabcf78ae20dfa224bb6666051fbe", - "asm": "OP_TRUE 33 032487c2a32f7c8d57d2a93906a6457afd00697925b0e6e145d89af6d3bca33016 33 02308673d16987eaa010e540901cc6fe3695e758c19f46ce604e174dac315e685a OP_2 OP_CHECKMULTISIG", + "asm": "OP_TRUE 032487c2a32f7c8d57d2a93906a6457afd00697925b0e6e145d89af6d3bca33016 02308673d16987eaa010e540901cc6fe3695e758c19f46ce604e174dac315e685a OP_2 OP_CHECKMULTISIG", "scriptPubKey": true }, { @@ -55,15 +55,15 @@ "hex": "0047304402202b29881db1b4cc128442d955e906d41c77365ed9a8392b584be12d980b236459022009da4bc60d09280aa26f4f981bfbed94eb7263d92920961e48a7f3f0991895b101483045022100871708a7597c1dbebff2a5527a56a1f2b49d73e35cd825a07285f5f29f5766d8022003bd7ac25334e9a6d6020cc8ba1be67a8c70dca8e7063ea0547d79c45b9bc12601", "type": "multisig", "hash": "b1ef3ae2c77b356eff81049aad7dfd2eeb34c6f5", - "asm": "00 47 304402202b29881db1b4cc128442d955e906d41c77365ed9a8392b584be12d980b236459022009da4bc60d09280aa26f4f981bfbed94eb7263d92920961e48a7f3f0991895b101 48 3045022100871708a7597c1dbebff2a5527a56a1f2b49d73e35cd825a07285f5f29f5766d8022003bd7ac25334e9a6d6020cc8ba1be67a8c70dca8e7063ea0547d79c45b9bc12601", + "asm": "OP_0 304402202b29881db1b4cc128442d955e906d41c77365ed9a8392b584be12d980b236459022009da4bc60d09280aa26f4f981bfbed94eb7263d92920961e48a7f3f0991895b101 3045022100871708a7597c1dbebff2a5527a56a1f2b49d73e35cd825a07285f5f29f5766d8022003bd7ac25334e9a6d6020cc8ba1be67a8c70dca8e7063ea0547d79c45b9bc12601", "scriptPubKey": false }, { "description": "OP_RETURN script", - "hex":"6a2606deadbeef03f895a2ad89fb6d696497af486cb7c644a27aa568c7a18dd06113401115185474", + "hex": "6a2606deadbeef03f895a2ad89fb6d696497af486cb7c644a27aa568c7a18dd06113401115185474", "type": "nulldata", "hash": "ec88f016655477663455fe6a8e83508c348ea145", - "asm": "OP_RETURN 38 06deadbeef03f895a2ad89fb6d696497af486cb7c644a27aa568c7a18dd06113401115185474", + "asm": "OP_RETURN 06deadbeef03f895a2ad89fb6d696497af486cb7c644a27aa568c7a18dd06113401115185474", "scriptPubKey": true }, { @@ -71,7 +71,7 @@ "hex": "aa206fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d619000000000087", "type": "nonstandard", "hash": "3823382e70d1930989813d3459988e0d7c2861d8", - "asm": "OP_HASH256 32 6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000 OP_EQUAL", + "asm": "OP_HASH256 6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000 OP_EQUAL", "scriptPubKey": true }, { @@ -79,7 +79,7 @@ "hex": "000000ae", "type": "nonstandard", "hash": "62ede8963f9387544935f168745262f703dab1fb", - "asm": "0 0 0 OP_CHECKMULTISIG", + "asm": "OP_0 OP_0 OP_0 OP_CHECKMULTISIG", "scriptPubKey": true } ] diff --git a/test/fixtures/scripts.json b/test/fixtures/scripts.json index 4e5c997..3728e6e 100644 --- a/test/fixtures/scripts.json +++ b/test/fixtures/scripts.json @@ -4,15 +4,15 @@ "type": "pubkey", "pubKey": "02359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1", "signature": "304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801", - "scriptPubKey": "2102359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1ac", - "scriptSig": "47304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801" + "scriptPubKey": "02359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1 OP_CHECKSIG", + "scriptSig": "304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801" }, { "type": "pubkeyhash", "pubKey": "02359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1", "signature": "304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801", - "scriptPubKey": "76a914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac", - "scriptSig": "47304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca28012102359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1" + "scriptPubKey": "OP_DUP OP_HASH160 aa4d7985c57e011a8b3dd8e0e5a73aaef41629c5 OP_EQUALVERIFY OP_CHECKSIG", + "scriptSig": "304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801 02359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1" }, { "type": "multisig", @@ -24,8 +24,8 @@ "304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801", "3045022100ef253c1faa39e65115872519e5f0a33bbecf430c0f35cf562beabbad4da24d8d02201742be8ee49812a73adea3007c9641ce6725c32cd44ddb8e3a3af460015d140501" ], - "scriptPubKey": "522102359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1210395a9d84d47d524548f79f435758c01faec5da2b7e551d3b8c995b7e06326ae4a52ae", - "scriptSig": "0047304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801483045022100ef253c1faa39e65115872519e5f0a33bbecf430c0f35cf562beabbad4da24d8d02201742be8ee49812a73adea3007c9641ce6725c32cd44ddb8e3a3af460015d140501" + "scriptPubKey": "OP_2 02359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1 0395a9d84d47d524548f79f435758c01faec5da2b7e551d3b8c995b7e06326ae4a OP_2 OP_CHECKMULTISIG", + "scriptSig": "OP_0 304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801 3045022100ef253c1faa39e65115872519e5f0a33bbecf430c0f35cf562beabbad4da24d8d02201742be8ee49812a73adea3007c9641ce6725c32cd44ddb8e3a3af460015d140501" }, { "type": "multisig", @@ -39,15 +39,15 @@ "3044022001ab168e80b863fdec694350b587339bb72a37108ac3c989849251444d13ebba02201811272023e3c1038478eb972a82d3ad431bfc2408e88e4da990f1a7ecbb263901", "3045022100aaeb7204c17eee2f2c4ff1c9f8b39b79e75e7fbf33e92cc67ac51be8f15b75f90220659eee314a4943a6384d2b154fa5821ef7a084814d7ee2c6f9f7f0ffb53be34b01" ], - "scriptPubKey": "53210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f817982102b80011a883a0fd621ad46dfc405df1e74bf075cbaf700fd4aebef6e96f84834021024289801366bcee6172b771cf5a7f13aaecd237a0b9a1ff9d769cabc2e6b70a3453ae", - "scriptSig": "00483045022100fe324541215798b2df68cbd44039615e23c506d4ec1a05572064392a98196b82022068c849fa6699206da2fc6d7848efc1d3804a5816d6293615fe34c1a7f34e1c2f01473044022001ab168e80b863fdec694350b587339bb72a37108ac3c989849251444d13ebba02201811272023e3c1038478eb972a82d3ad431bfc2408e88e4da990f1a7ecbb263901483045022100aaeb7204c17eee2f2c4ff1c9f8b39b79e75e7fbf33e92cc67ac51be8f15b75f90220659eee314a4943a6384d2b154fa5821ef7a084814d7ee2c6f9f7f0ffb53be34b01" + "scriptPubKey": "OP_3 0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 02b80011a883a0fd621ad46dfc405df1e74bf075cbaf700fd4aebef6e96f848340 024289801366bcee6172b771cf5a7f13aaecd237a0b9a1ff9d769cabc2e6b70a34 OP_3 OP_CHECKMULTISIG", + "scriptSig": "OP_0 3045022100fe324541215798b2df68cbd44039615e23c506d4ec1a05572064392a98196b82022068c849fa6699206da2fc6d7848efc1d3804a5816d6293615fe34c1a7f34e1c2f01 3044022001ab168e80b863fdec694350b587339bb72a37108ac3c989849251444d13ebba02201811272023e3c1038478eb972a82d3ad431bfc2408e88e4da990f1a7ecbb263901 3045022100aaeb7204c17eee2f2c4ff1c9f8b39b79e75e7fbf33e92cc67ac51be8f15b75f90220659eee314a4943a6384d2b154fa5821ef7a084814d7ee2c6f9f7f0ffb53be34b01" }, { "type": "scripthash", - "redeemScript": "522102359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1210395a9d84d47d524548f79f435758c01faec5da2b7e551d3b8c995b7e06326ae4a52ae", - "redeemScriptSig": "0047304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801483045022100ef253c1faa39e65115872519e5f0a33bbecf430c0f35cf562beabbad4da24d8d02201742be8ee49812a73adea3007c9641ce6725c32cd44ddb8e3a3af460015d140501", - "scriptSig": "0047304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801483045022100ef253c1faa39e65115872519e5f0a33bbecf430c0f35cf562beabbad4da24d8d02201742be8ee49812a73adea3007c9641ce6725c32cd44ddb8e3a3af460015d14050147522102359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1210395a9d84d47d524548f79f435758c01faec5da2b7e551d3b8c995b7e06326ae4a52ae", - "scriptPubKey": "a914722ff0bc2c3f47b35c20df646c395594da24e90e87" + "redeemScript": "OP_2 02359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1 0395a9d84d47d524548f79f435758c01faec5da2b7e551d3b8c995b7e06326ae4a OP_2 OP_CHECKMULTISIG", + "redeemScriptSig": "OP_0 304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801 3045022100ef253c1faa39e65115872519e5f0a33bbecf430c0f35cf562beabbad4da24d8d02201742be8ee49812a73adea3007c9641ce6725c32cd44ddb8e3a3af460015d140501", + "scriptSig": "OP_0 304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801 3045022100ef253c1faa39e65115872519e5f0a33bbecf430c0f35cf562beabbad4da24d8d02201742be8ee49812a73adea3007c9641ce6725c32cd44ddb8e3a3af460015d140501 522102359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1210395a9d84d47d524548f79f435758c01faec5da2b7e551d3b8c995b7e06326ae4a52ae", + "scriptPubKey": "OP_HASH160 722ff0bc2c3f47b35c20df646c395594da24e90e OP_EQUAL" } ], "invalid": { diff --git a/test/script.js b/test/script.js index bb9b9b9..d8d080f 100644 --- a/test/script.js +++ b/test/script.js @@ -21,6 +21,14 @@ describe('Script', function() { }) }) + describe('fromASM/toASM', function() { + fixtures.valid.forEach(function(f) { + it('decodes/encodes ' + f.description, function() { + assert.equal(Script.fromASM(f.asm).toASM(), f.asm) + }) + }) + }) + describe('fromHex/toHex', function() { fixtures.valid.forEach(function(f) { it('decodes/encodes ' + f.description, function() { diff --git a/test/scripts.js b/test/scripts.js index 139ec65..c8cdae5 100644 --- a/test/scripts.js +++ b/test/scripts.js @@ -13,7 +13,7 @@ describe('Scripts', function() { if (!f.scriptSig) return it('classifies ' + f.scriptSig + ' as ' + f.type, function() { - var script = Script.fromHex(f.scriptSig) + var script = Script.fromASM(f.scriptSig) var type = scripts.classifyInput(script) assert.equal(type, f.type) @@ -26,7 +26,7 @@ describe('Scripts', function() { if (!f.scriptPubKey) return it('classifies ' + f.scriptPubKey + ' as ' + f.type, function() { - var script = Script.fromHex(f.scriptPubKey) + var script = Script.fromASM(f.scriptPubKey) var type = scripts.classifyOutput(script) assert.equal(type, f.type) @@ -43,7 +43,7 @@ describe('Scripts', function() { var signature = new Buffer(f.signature, 'hex') var scriptSig = scripts.pubKeyInput(signature) - assert.equal(scriptSig.toHex(), f.scriptSig) + assert.equal(scriptSig.toASM(), f.scriptSig) }) }) @@ -52,7 +52,7 @@ describe('Scripts', function() { var pubKey = ECPubKey.fromHex(f.pubKey) var scriptPubKey = scripts.pubKeyOutput(pubKey) - assert.equal(scriptPubKey.toHex(), f.scriptPubKey) + assert.equal(scriptPubKey.toASM(), f.scriptPubKey) }) }) }) @@ -70,14 +70,14 @@ describe('Scripts', function() { var signature = new Buffer(f.signature, 'hex') var scriptSig = scripts.pubKeyHashInput(signature, pubKey) - assert.equal(scriptSig.toHex(), f.scriptSig) + 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.toHex(), f.scriptPubKey) + assert.equal(scriptPubKey.toASM(), f.scriptPubKey) }) }) }) @@ -91,19 +91,19 @@ describe('Scripts', function() { var scriptPubKey = scripts.multisigOutput(pubKeys.length, pubKeys) describe('input script', function() { - it('is generated correctly for ' + scriptPubKey.toHex(), function() { + it('is generated correctly for ' + scriptPubKey.toASM(), function() { var signatures = f.signatures.map(function(signature) { return new Buffer(signature, 'hex') }) var scriptSig = scripts.multisigInput(signatures) - assert.equal(scriptSig.toHex(), f.scriptSig) + assert.equal(scriptSig.toASM(), f.scriptSig) }) }) describe('output script', function() { - it('is generated correctly for ' + scriptPubKey.toHex(), function() { - assert.equal(scriptPubKey.toHex(), f.scriptPubKey) + it('is generated correctly for ' + scriptPubKey.toASM(), function() { + assert.equal(scriptPubKey.toASM(), f.scriptPubKey) }) }) }) @@ -140,16 +140,16 @@ describe('Scripts', function() { fixtures.valid.forEach(function(f) { if (f.type !== 'scripthash') return - var redeemScript = Script.fromHex(f.redeemScript) - var redeemScriptSig = Script.fromHex(f.redeemScriptSig) + var redeemScript = Script.fromASM(f.redeemScript) + var redeemScriptSig = Script.fromASM(f.redeemScriptSig) - var address = Address.fromOutputScript(Script.fromHex(f.scriptPubKey)) + var address = Address.fromOutputScript(Script.fromASM(f.scriptPubKey)) describe('input script', function() { it('is generated correctly for ' + address, function() { var scriptSig = scripts.scriptHashInput(redeemScriptSig, redeemScript) - assert.equal(scriptSig.toHex(), f.scriptSig) + assert.equal(scriptSig.toASM(), f.scriptSig) }) }) @@ -157,7 +157,7 @@ describe('Scripts', function() { it('is generated correctly for ' + address, function() { var scriptPubKey = scripts.scriptHashOutput(redeemScript.getHash()) - assert.equal(scriptPubKey.toHex(), f.scriptPubKey) + assert.equal(scriptPubKey.toASM(), f.scriptPubKey) }) }) }) From 5c53178c3cad8f53468a325f4daec03e33ec4d99 Mon Sep 17 00:00:00 2001 From: Daniel Cousens Date: Wed, 25 Jun 2014 15:47:46 +1000 Subject: [PATCH 09/11] tests: avoid pointless toASM in description --- test/scripts.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/scripts.js b/test/scripts.js index c8cdae5..7376206 100644 --- a/test/scripts.js +++ b/test/scripts.js @@ -91,7 +91,7 @@ describe('Scripts', function() { var scriptPubKey = scripts.multisigOutput(pubKeys.length, pubKeys) describe('input script', function() { - it('is generated correctly for ' + scriptPubKey.toASM(), function() { + it('is generated correctly for ' + f.scriptPubKey, function() { var signatures = f.signatures.map(function(signature) { return new Buffer(signature, 'hex') }) @@ -102,7 +102,7 @@ describe('Scripts', function() { }) describe('output script', function() { - it('is generated correctly for ' + scriptPubKey.toASM(), function() { + it('is generated correctly for ' + f.scriptPubKey, function() { assert.equal(scriptPubKey.toASM(), f.scriptPubKey) }) }) From 759bba5c2125a13686b9d4b026276df5a25656b6 Mon Sep 17 00:00:00 2001 From: Daniel Cousens Date: Thu, 26 Jun 2014 16:22:29 +1000 Subject: [PATCH 10/11] scripts: add falsy classifications and fix multisig --- src/scripts.js | 29 ++++++++++++++--------------- test/fixtures/scripts.json | 26 ++++++++++++++++++++++++++ test/scripts.js | 9 +++++++++ 3 files changed, 49 insertions(+), 15 deletions(-) diff --git a/src/scripts.js b/src/scripts.js index a442c47..ee317bb 100644 --- a/src/scripts.js +++ b/src/scripts.js @@ -136,31 +136,30 @@ function isMultisigOutput() { if (this.chunks < 4) return false if (this.chunks[this.chunks.length - 1] !== opcodes.OP_CHECKMULTISIG) return false - var mS = this.chunks[0] - if (!isSmallIntOp(mS)) return false + var mOp = this.chunks[0] + if (mOp === opcodes.OP_0) return false + if (mOp < opcodes.OP_1) return false + if (mOp > opcodes.OP_16) return false - var nS = this.chunks[this.chunks.length - 2] - if (!isSmallIntOp(nS)) return false + var nOp = this.chunks[this.chunks.length - 2] + if (nOp === opcodes.OP_0) return false + if (nOp < opcodes.OP_1) return false + if (nOp > opcodes.OP_16) return false - var m = mS - (opcodes.OP_1 - 1) - var n = nS - (opcodes.OP_1 - 1) + var m = mOp - (opcodes.OP_1 - 1) + var n = nOp - (opcodes.OP_1 - 1) if (n < m) return false - if (n === 0) return false - if (m > (this.chunks.length - 3)) return false - return this.chunks.slice(1, -2).every(isCanonicalPubKey) + var pubKeys = this.chunks.slice(1, -2) + if (n < pubKeys.length) return false + + return pubKeys.every(isCanonicalPubKey) } function isNulldataOutput() { return this.chunks[0] === opcodes.OP_RETURN } -function isSmallIntOp(opcode) { - if (Buffer.isBuffer(opcode)) return false - - return ((opcode === opcodes.OP_0) || ((opcode >= opcodes.OP_1) && (opcode <= opcodes.OP_16))) -} - // Standard Script Templates // {pubKey} OP_CHECKSIG function pubKeyOutput(pubKey) { diff --git a/test/fixtures/scripts.json b/test/fixtures/scripts.json index 3728e6e..666ea59 100644 --- a/test/fixtures/scripts.json +++ b/test/fixtures/scripts.json @@ -51,6 +51,32 @@ } ], "invalid": { + "classify": [ + { + "description": "multisig output : m > n", + "scriptPubKey": "OP_2 0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 02b80011a883a0fd621ad46dfc405df1e74bf075cbaf700fd4aebef6e96f848340 OP_1 OP_CHECKMULTISIG" + }, + { + "description": "multisig output : n === 0", + "scriptPubKey": "OP_0 0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 02b80011a883a0fd621ad46dfc405df1e74bf075cbaf700fd4aebef6e96f848340 OP_0 OP_CHECKMULTISIG" + }, + { + "description": "multisig output : not (m <= len(pubKeys) <= n)", + "scriptPubKey": "OP_2 0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 02b80011a883a0fd621ad46dfc405df1e74bf075cbaf700fd4aebef6e96f848340 024289801366bcee6172b771cf5a7f13aaecd237a0b9a1ff9d769cabc2e6b70a34 OP_2 OP_CHECKMULTISIG" + }, + { + "description": "multisig output : m not a small int", + "scriptPubKey": "OP_HASH160 024289801366bcee6172b771cf5a7f13aaecd237a0b9a1ff9d769cabc2e6b70a34 OP_1 OP_CHECKMULTISIG" + }, + { + "description": "multisig output : n not a small int", + "scriptPubKey": "OP_1 024289801366bcee6172b771cf5a7f13aaecd237a0b9a1ff9d769cabc2e6b70a34 OP_HASH160 OP_CHECKMULTISIG" + }, + { + "description": "multisig output : non-canonical pubKey (bad length)", + "scriptPubKey": "OP_1 0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798ffff OP_1 OP_CHECKMULTISIG" + } + ], "multisig": [ { "exception": "Not enough pubKeys provided", diff --git a/test/scripts.js b/test/scripts.js index 7376206..b50ec3a 100644 --- a/test/scripts.js +++ b/test/scripts.js @@ -32,6 +32,15 @@ describe('Scripts', function() { assert.equal(type, f.type) }) }) + + fixtures.invalid.classify.forEach(function(f) { + it('returns nonstandard for ' + f.description, function() { + var script = Script.fromASM(f.scriptPubKey) + var type = scripts.classifyOutput(script) + + assert.equal(type, 'nonstandard') + }) + }) }) describe('pubKey', function() { From 886bdee947d71f0620d9ef07401b5d0e854ea37b Mon Sep 17 00:00:00 2001 From: Daniel Cousens Date: Mon, 30 Jun 2014 01:06:43 +1000 Subject: [PATCH 11/11] scripts: remove unused 2nd argument in function call --- src/scripts.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/scripts.js b/src/scripts.js index ee317bb..661285a 100644 --- a/src/scripts.js +++ b/src/scripts.js @@ -116,7 +116,7 @@ function isScriptHashInput() { var scriptSig = Script.fromChunks(this.chunks.slice(0, -1)) var scriptPubKey = Script.fromBuffer(lastChunk) - return classifyInput(scriptSig, false) === classifyOutput(scriptPubKey) + return classifyInput(scriptSig) === classifyOutput(scriptPubKey) } function isScriptHashOutput() {