From 6013bf075ce06ced808c4f5b8ec2d18e7387bb70 Mon Sep 17 00:00:00 2001 From: Daniel Cousens Date: Wed, 27 Sep 2017 08:30:20 +1000 Subject: [PATCH 1/2] tests: add passing and failing tests for witness*.input.encode/decode --- test/fixtures/templates.json | 22 ++++++++++++--- test/templates.js | 55 ++++++++++++++++++++++++++++++++++-- 2 files changed, 71 insertions(+), 6 deletions(-) diff --git a/test/fixtures/templates.json b/test/fixtures/templates.json index 2623684fd..0cba6c6b8 100644 --- a/test/fixtures/templates.json +++ b/test/fixtures/templates.json @@ -86,16 +86,30 @@ { "type": "witnesspubkeyhash", "pubKey": "02359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1", + "signature": "304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801", "output": "OP_0 aa4d7985c57e011a8b3dd8e0e5a73aaef41629c5", - "outputHex": "0014aa4d7985c57e011a8b3dd8e0e5a73aaef41629c5" + "outputHex": "0014aa4d7985c57e011a8b3dd8e0e5a73aaef41629c5", + "inputStack": [ + "304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801", + "02359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1" + ] }, { "type": "witnessscripthash", - "witnessScriptPubKey": "OP_2 02359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1 0395a9d84d47d524548f79f435758c01faec5da2b7e551d3b8c995b7e06326ae4a OP_2 OP_CHECKMULTISIG", - "witnessScriptSig": "OP_0 304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801 3045022100ef253c1faa39e65115872519e5f0a33bbecf430c0f35cf562beabbad4da24d8d02201742be8ee49812a73adea3007c9641ce6725c32cd44ddb8e3a3af460015d140501", + "witnessScript": "OP_2 02359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1 0395a9d84d47d524548f79f435758c01faec5da2b7e551d3b8c995b7e06326ae4a OP_2 OP_CHECKMULTISIG", + "witnessData": [ + "", + "304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801", + "3045022100ef253c1faa39e65115872519e5f0a33bbecf430c0f35cf562beabbad4da24d8d02201742be8ee49812a73adea3007c9641ce6725c32cd44ddb8e3a3af460015d140501" + ], "output": "OP_0 32447752937d355ca2defddcd1f6b4fc53d182f8901cebbcff42f5e381bf0b80", "outputHex": "002032447752937d355ca2defddcd1f6b4fc53d182f8901cebbcff42f5e381bf0b80", - "witness": "OP_0 304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801 3045022100ef253c1faa39e65115872519e5f0a33bbecf430c0f35cf562beabbad4da24d8d02201742be8ee49812a73adea3007c9641ce6725c32cd44ddb8e3a3af460015d140501 522102359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1210395a9d84d47d524548f79f435758c01faec5da2b7e551d3b8c995b7e06326ae4a52ae" + "inputStack": [ + "", + "304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801", + "3045022100ef253c1faa39e65115872519e5f0a33bbecf430c0f35cf562beabbad4da24d8d02201742be8ee49812a73adea3007c9641ce6725c32cd44ddb8e3a3af460015d140501", + "522102359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1210395a9d84d47d524548f79f435758c01faec5da2b7e551d3b8c995b7e06326ae4a52ae" + ] }, { "type": "nulldata", diff --git a/test/templates.js b/test/templates.js index c3fc44e21..104b32696 100644 --- a/test/templates.js +++ b/test/templates.js @@ -8,6 +8,9 @@ var ops = require('bitcoin-ops') var fixtures = require('./fixtures/templates.json') +function fromHex (x) { return Buffer.from(x, 'hex') } +function toHex (x) { return x.toString('hex') } + describe('script-templates', function () { describe('classifyInput', function () { fixtures.valid.forEach(function (f) { @@ -296,8 +299,8 @@ describe('script-templates', function () { fixtures.valid.forEach(function (f) { if (f.type !== 'scripthash') return - var redeemScript = bscript.fromASM(f.redeemScript) var redeemScriptSig = bscript.fromASM(f.redeemScriptSig) + var redeemScript = bscript.fromASM(f.redeemScript) var input = btemplates.scriptHash.input.encode(redeemScriptSig, redeemScript) it('encodes to ' + f.output, function () { @@ -347,6 +350,31 @@ describe('script-templates', function () { }) }) + describe('witnessPubKeyHash.input', function () { + fixtures.valid.forEach(function (f) { + if (f.type !== 'pubkeyhash' && f.type !== 'witnesspubkeyhash') return + if (!f.inputStack) return + + var pubKey = Buffer.from(f.pubKey, 'hex') + var signature = Buffer.from(f.signature, 'hex') + + it('encodes to ' + f.input, function () { + var inputStack = btemplates.witnessPubKeyHash.input.encodeStack(signature, pubKey) + + assert.deepEqual(inputStack.map(toHex), f.inputStack) + }) + + it('decodes to original arguments', function () { + var fInputStack = f.inputStack.map(fromHex) + + assert.deepEqual(btemplates.witnessPubKeyHash.input.decodeStack(fInputStack), { + signature: signature, + pubKey: pubKey + }) + }) + }) + }) + describe('witnessPubKeyHash.output', function () { fixtures.valid.forEach(function (f) { if (f.type !== 'witnesspubkeyhash') return @@ -377,12 +405,35 @@ describe('script-templates', function () { }) }) + describe('witnessScriptHash.input', function () { + fixtures.valid.forEach(function (f) { + if (f.type !== 'witnessscripthash') return + if (!f.inputStack || !f.witnessData) return + + var witnessData = f.witnessData.map(fromHex) + var witnessScript = bscript.fromASM(f.witnessScript || f.redeemScript) + + it('encodes to ' + f.input, function () { + var inputStack = btemplates.witnessScriptHash.input.encodeStack(witnessData, witnessScript) + + assert.deepEqual(inputStack.map(toHex), f.inputStack) + }) + + it('decodes to original arguments', function () { + var result = btemplates.witnessScriptHash.input.decodeStack(f.inputStack.map(fromHex)) + + assert.deepEqual(result.witnessData.map(toHex), f.witnessData) + assert.strictEqual(bscript.toASM(result.witnessScript), f.witnessScript) + }) + }) + }) + describe('witnessScriptHash.output', function () { fixtures.valid.forEach(function (f) { if (f.type !== 'witnessscripthash') return if (!f.output) return - var witnessScriptPubKey = bscript.fromASM(f.witnessScriptPubKey) + var witnessScriptPubKey = bscript.fromASM(f.witnessScript) var scriptHash = bcrypto.hash256(witnessScriptPubKey) var output = btemplates.witnessScriptHash.output.encode(scriptHash) From 8c217a7ba7e1d615175a1a1af9a15a6c284c2593 Mon Sep 17 00:00:00 2001 From: Daniel Cousens Date: Wed, 27 Sep 2017 08:30:31 +1000 Subject: [PATCH 2/2] witnessScriptHash: fixed implementation --- src/templates/witnessscripthash/input.js | 64 ++++++++++++++++++++++-- 1 file changed, 59 insertions(+), 5 deletions(-) diff --git a/src/templates/witnessscripthash/input.js b/src/templates/witnessscripthash/input.js index 02b654544..bc2e90935 100644 --- a/src/templates/witnessscripthash/input.js +++ b/src/templates/witnessscripthash/input.js @@ -1,9 +1,63 @@ -// {signature} {pubKey} +// {serialized scriptPubKey script} -var p2sh = require('../scripthash/input') +var bscript = require('../../script') +var types = require('../../types') +var typeforce = require('typeforce') + +var p2ms = require('../multisig/') +var p2pk = require('../pubkey/') +var p2pkh = require('../pubkeyhash/') + +function check (chunks, allowIncomplete) { + typeforce(types.Array, chunks) + if (chunks.length < 1) return false + + var witnessScript = chunks[chunks.length - 1] + if (!Buffer.isBuffer(witnessScript)) return false + + var witnessScriptChunks = bscript.decompile(witnessScript) + + // is witnessScript a valid script? + if (witnessScriptChunks.length === 0) return false + + var witnessRawScriptSig = bscript.compile(chunks.slice(0, -1)) + + // match types + if (p2pkh.input.check(witnessRawScriptSig) && + p2pkh.output.check(witnessScriptChunks)) return true + + if (p2ms.input.check(witnessRawScriptSig, allowIncomplete) && + p2ms.output.check(witnessScriptChunks)) return true + + if (p2pk.input.check(witnessRawScriptSig) && + p2pk.output.check(witnessScriptChunks)) return true + + return false +} +check.toJSON = function () { return 'witnessScriptHash input' } + +function encodeStack (witnessData, witnessScript) { + typeforce({ + witnessData: [types.Buffer], + witnessScript: types.Buffer + }, { + witnessData: witnessData, + witnessScript: witnessScript + }) + + return [].concat(witnessData, witnessScript) +} + +function decodeStack (chunks) { + typeforce(check, chunks) + return { + witnessData: chunks.slice(0, -1), + witnessScript: chunks[chunks.length - 1] + } +} module.exports = { - check: p2sh.check, - decodeStack: p2sh.decodeStack, - encodeStack: p2sh.encodeStack + check: check, + decodeStack: decodeStack, + encodeStack: encodeStack }