From 20a026aefe18acbe28dd3fb545eda4cb0ef2cd2f Mon Sep 17 00:00:00 2001 From: Daniel Cousens Date: Tue, 12 Jul 2016 01:37:59 +1000 Subject: [PATCH] scripts/tests: add witness* scripts --- src/script.js | 46 ++++++++++++++++++++++- test/fixtures/script.json | 26 +++++++++++++ test/script.js | 78 +++++++++++++++++++++++++++++++++++++-- 3 files changed, 146 insertions(+), 4 deletions(-) diff --git a/src/script.js b/src/script.js index 917a8e5..8264737 100644 --- a/src/script.js +++ b/src/script.js @@ -206,6 +206,20 @@ function isScriptHashOutput (script) { buffer[22] === OPS.OP_EQUAL } +function isWitnessPubKeyHashOutput (script) { + var buffer = compile(script) + + return buffer.length === 22 && + buffer[0] === OPS.OP_0 +} + +function isWitnessScriptHashOutput (script) { + var buffer = compile(script) + + return buffer.length === 34 && + buffer[0] === OPS.OP_0 +} + // allowIncomplete is to account for combining signatures // See https://github.com/bitcoin/bitcoin/blob/f425050546644a36b0b8e0eb2f6934a3e0f6f80f/src/script/sign.cpp#L195-L197 function isMultisigInput (script, allowIncomplete) { @@ -253,7 +267,11 @@ function isNullDataOutput (script) { function classifyOutput (script) { var chunks = decompile(script) - if (isPubKeyHashOutput(chunks)) { + if (isWitnessPubKeyHashOutput(chunks)) { + return 'witnesspubkeyhash' + } else if (isWitnessScriptHashOutput(chunks)) { + return 'witnessscripthash' + } else if (isPubKeyHashOutput(chunks)) { return 'pubkeyhash' } else if (isScriptHashOutput(chunks)) { return 'scripthash' @@ -319,6 +337,20 @@ function multisigOutput (m, pubKeys) { )) } +// OP_0 {pubKeyHash} +function witnessPubKeyHashOutput (pubKeyHash) { + typeforce(types.Hash160bit, pubKeyHash) + + return compile([OPS.OP_0, pubKeyHash]) +} + +// OP_0 {scriptHash} +function witnessScriptHashOutput (scriptHash) { + typeforce(types.Hash256bit, scriptHash) + + return compile([OPS.OP_0, scriptHash]) +} + // {signature} function pubKeyInput (signature) { typeforce(types.Buffer, signature) @@ -344,6 +376,11 @@ function scriptHashInput (scriptSig, scriptPubKey) { )) } +// {serialized scriptPubKey script} +function witnessScriptHashInput (scriptSig, scriptPubKey) { + return scriptHashInput(scriptSig, scriptPubKey) +} + // OP_0 [signatures ...] function multisigInput (signatures, scriptPubKey) { if (scriptPubKey) { @@ -383,14 +420,21 @@ module.exports = { isPubKeyOutput: isPubKeyOutput, isScriptHashInput: isScriptHashInput, isScriptHashOutput: isScriptHashOutput, + isWitnessPubKeyHashOutput: isWitnessPubKeyHashOutput, + isWitnessScriptHashOutput: isWitnessScriptHashOutput, isMultisigInput: isMultisigInput, isMultisigOutput: isMultisigOutput, isNullDataOutput: isNullDataOutput, + classifyOutput: classifyOutput, classifyInput: classifyInput, pubKeyOutput: pubKeyOutput, pubKeyHashOutput: pubKeyHashOutput, scriptHashOutput: scriptHashOutput, + witnessPubKeyHashOutput: witnessPubKeyHashOutput, + witnessScriptHashInput: witnessScriptHashInput, + witnessScriptHashOutput: witnessScriptHashOutput, + multisigOutput: multisigOutput, pubKeyInput: pubKeyInput, pubKeyHashInput: pubKeyHashInput, diff --git a/test/fixtures/script.json b/test/fixtures/script.json index 797186f..a7649ed 100644 --- a/test/fixtures/script.json +++ b/test/fixtures/script.json @@ -59,6 +59,20 @@ "scriptSigHex": "0047304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801483045022100ef253c1faa39e65115872519e5f0a33bbecf430c0f35cf562beabbad4da24d8d02201742be8ee49812a73adea3007c9641ce6725c32cd44ddb8e3a3af460015d14050147522102359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1210395a9d84d47d524548f79f435758c01faec5da2b7e551d3b8c995b7e06326ae4a52ae", "scriptPubKeyHex": "a914722ff0bc2c3f47b35c20df646c395594da24e90e87" }, + { + "type": "witnesspubkeyhash", + "pubKey": "02359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1", + "scriptPubKey": "OP_0 aa4d7985c57e011a8b3dd8e0e5a73aaef41629c5", + "scriptPubKeyHex": "0014aa4d7985c57e011a8b3dd8e0e5a73aaef41629c5" + }, + { + "type": "witnessscripthash", + "witnessScriptPubKey": "OP_2 02359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1 0395a9d84d47d524548f79f435758c01faec5da2b7e551d3b8c995b7e06326ae4a OP_2 OP_CHECKMULTISIG", + "witnessScriptSig": "OP_0 304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801 3045022100ef253c1faa39e65115872519e5f0a33bbecf430c0f35cf562beabbad4da24d8d02201742be8ee49812a73adea3007c9641ce6725c32cd44ddb8e3a3af460015d140501", + "scriptPubKey": "OP_0 32447752937d355ca2defddcd1f6b4fc53d182f8901cebbcff42f5e381bf0b80", + "scriptPubKeyHex": "002032447752937d355ca2defddcd1f6b4fc53d182f8901cebbcff42f5e381bf0b80", + "witness": "OP_0 304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801 3045022100ef253c1faa39e65115872519e5f0a33bbecf430c0f35cf562beabbad4da24d8d02201742be8ee49812a73adea3007c9641ce6725c32cd44ddb8e3a3af460015d140501 522102359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1210395a9d84d47d524548f79f435758c01faec5da2b7e551d3b8c995b7e06326ae4a52ae" + }, { "type": "nulldata", "data": "06deadbeef03f895a2ad89fb6d696497af486cb7c644a27aa568c7a18dd06113401115185474", @@ -319,6 +333,18 @@ "exception": "Expected 160-bit Buffer, got 24-bit Buffer", "hash": "ffffff" } + ], + "witnessPubKeyHashOutput": [ + { + "exception": "Expected 160-bit Buffer, got 24-bit Buffer", + "hash": "ffffff" + } + ], + "witnessScriptHashOutput": [ + { + "exception": "Expected 256-bit Buffer, got 24-bit Buffer", + "hash": "ffffff" + } ] } } diff --git a/test/script.js b/test/script.js index ccb009f..e1598cd 100644 --- a/test/script.js +++ b/test/script.js @@ -35,7 +35,7 @@ describe('script', function () { describe('compile', function () { fixtures.valid.forEach(function (f) { if (f.scriptSig) { - it('compiles ' + f.scriptSig, function () { + it('(' + f.type + ') compiles ' + f.scriptSig, function () { var scriptSig = bscript.fromASM(f.scriptSig) assert.strictEqual(bscript.compile(scriptSig).toString('hex'), f.scriptSigHex) @@ -43,7 +43,7 @@ describe('script', function () { } if (f.scriptPubKey) { - it('compiles ' + f.scriptPubKey, function () { + it('(' + f.type + ') compiles ' + f.scriptPubKey, function () { var scriptPubKey = bscript.fromASM(f.scriptPubKey) assert.strictEqual(bscript.compile(scriptPubKey).toString('hex'), f.scriptPubKeyHex) @@ -118,7 +118,15 @@ describe('script', function () { }) }) - ;['PubKey', 'PubKeyHash', 'ScriptHash', 'Multisig', 'NullData'].forEach(function (type) { + ;[ + 'PubKey', + 'PubKeyHash', + 'ScriptHash', + 'WitnessPubKeyHash', + 'WitnessScriptHash', + 'Multisig', + 'NullData' + ].forEach(function (type) { var inputFnName = 'is' + type + 'Input' var outputFnName = 'is' + type + 'Output' @@ -356,6 +364,70 @@ describe('script', function () { }) }) + describe('witnessPubKeyHashOutput', function () { + fixtures.valid.forEach(function (f) { + if (f.type !== 'witnesspubkeyhash') return + if (!f.scriptPubKey) return + + var pubKey = new Buffer(f.pubKey, 'hex') + var pubKeyHash = bcrypto.hash160(pubKey) + + it('returns ' + f.scriptPubKey, function () { + var scriptPubKey = bscript.witnessPubKeyHashOutput(pubKeyHash) + assert.strictEqual(bscript.toASM(scriptPubKey), f.scriptPubKey) + }) + }) + + fixtures.invalid.witnessPubKeyHashOutput.forEach(function (f) { + var hash = new Buffer(f.hash, 'hex') + + it('throws on ' + f.exception, function () { + assert.throws(function () { + bscript.witnessPubKeyHashOutput(hash) + }, new RegExp(f.exception)) + }) + }) + }) + + describe('witnessScriptHashInput', function () { + fixtures.valid.forEach(function (f) { + if (f.type !== 'witnessscripthash') return + + var witnessScript = bscript.fromASM(f.witnessScriptPubKey) + var witnessScriptSig = bscript.fromASM(f.witnessScriptSig) + + it('returns ' + f.witness, function () { + var witness = bscript.witnessScriptHashInput(witnessScriptSig, witnessScript) + + assert.strictEqual(bscript.toASM(witness), f.witness) + }) + }) + }) + + describe('witnessScriptHashOutput', function () { + fixtures.valid.forEach(function (f) { + if (f.type !== 'witnessscripthash') return + if (!f.scriptPubKey) return + + it('returns ' + f.scriptPubKey, function () { + var witnessScriptPubKey = bscript.fromASM(f.witnessScriptPubKey) + var scriptPubKey = bscript.witnessScriptHashOutput(bcrypto.hash256(witnessScriptPubKey)) + + assert.strictEqual(bscript.toASM(scriptPubKey), f.scriptPubKey) + }) + }) + + fixtures.invalid.witnessScriptHashOutput.forEach(function (f) { + var hash = new Buffer(f.hash, 'hex') + + it('throws on ' + f.exception, function () { + assert.throws(function () { + bscript.witnessScriptHashOutput(hash) + }, new RegExp(f.exception)) + }) + }) + }) + describe('nullDataOutput', function () { fixtures.valid.forEach(function (f) { if (f.type !== 'nulldata') return