From d9fa39a2ccf6291b4d72f5035266504bd63d2223 Mon Sep 17 00:00:00 2001 From: Daniel Cousens Date: Wed, 2 Nov 2016 12:30:37 +1100 Subject: [PATCH] scripts: extract all scripts to encode/decode/check style --- src/address.js | 8 +- src/script.js | 445 +++---------------- src/templates/multisig/index.js | 4 + src/templates/multisig/input.js | 54 +++ src/templates/multisig/output.js | 63 +++ src/templates/nulldata.js | 34 ++ src/templates/pubkey/index.js | 4 + src/templates/pubkey/input.js | 31 ++ src/templates/pubkey/output.js | 32 ++ src/templates/pubkeyhash/index.js | 4 + src/templates/pubkeyhash/input.js | 39 ++ src/templates/pubkeyhash/output.js | 41 ++ src/templates/scripthash/index.js | 4 + src/templates/scripthash/input.js | 48 +++ src/templates/scripthash/output.js | 33 ++ src/templates/witnesspubkeyhash/index.js | 3 + src/templates/witnesspubkeyhash/output.js | 32 ++ src/templates/witnessscripthash/index.js | 3 + src/templates/witnessscripthash/output.js | 32 ++ src/transaction_builder.js | 16 +- test/fixtures/script.json | 492 +++++++++++----------- test/script.js | 139 +++--- test/transaction_builder.js | 2 +- 23 files changed, 843 insertions(+), 720 deletions(-) create mode 100644 src/templates/multisig/index.js create mode 100644 src/templates/multisig/input.js create mode 100644 src/templates/multisig/output.js create mode 100644 src/templates/nulldata.js create mode 100644 src/templates/pubkey/index.js create mode 100644 src/templates/pubkey/input.js create mode 100644 src/templates/pubkey/output.js create mode 100644 src/templates/pubkeyhash/index.js create mode 100644 src/templates/pubkeyhash/input.js create mode 100644 src/templates/pubkeyhash/output.js create mode 100644 src/templates/scripthash/index.js create mode 100644 src/templates/scripthash/input.js create mode 100644 src/templates/scripthash/output.js create mode 100644 src/templates/witnesspubkeyhash/index.js create mode 100644 src/templates/witnesspubkeyhash/output.js create mode 100644 src/templates/witnessscripthash/index.js create mode 100644 src/templates/witnessscripthash/output.js diff --git a/src/address.js b/src/address.js index 656ec37..cb7248f 100644 --- a/src/address.js +++ b/src/address.js @@ -28,8 +28,8 @@ function toBase58Check (hash, version) { function fromOutputScript (scriptPubKey, network) { network = network || networks.bitcoin - if (bscript.isPubKeyHashOutput(scriptPubKey)) return toBase58Check(bscript.compile(scriptPubKey).slice(3, 23), network.pubKeyHash) - if (bscript.isScriptHashOutput(scriptPubKey)) return toBase58Check(bscript.compile(scriptPubKey).slice(2, 22), network.scriptHash) + if (bscript.pubKeyHash.output.check(scriptPubKey)) return toBase58Check(bscript.compile(scriptPubKey).slice(3, 23), network.pubKeyHash) + if (bscript.scriptHash.output.check(scriptPubKey)) return toBase58Check(bscript.compile(scriptPubKey).slice(2, 22), network.scriptHash) throw new Error(bscript.toASM(scriptPubKey) + ' has no matching Address') } @@ -38,8 +38,8 @@ function toOutputScript (address, network) { network = network || networks.bitcoin var decode = fromBase58Check(address) - if (decode.version === network.pubKeyHash) return bscript.pubKeyHashOutput(decode.hash) - if (decode.version === network.scriptHash) return bscript.scriptHashOutput(decode.hash) + if (decode.version === network.pubKeyHash) return bscript.pubKeyHash.output.encode(decode.hash) + if (decode.version === network.scriptHash) return bscript.scriptHash.output.encode(decode.hash) throw new Error(address + ' has no matching Script') } diff --git a/src/script.js b/src/script.js index 052175e..59d58df 100644 --- a/src/script.js +++ b/src/script.js @@ -12,16 +12,6 @@ var REVERSE_OPS = (function () { return result })() -var LIST_DECODE_NAMES = [ - decodePubKeyOutput, - decodePubKeyHashOutput, - decodeMultisigOutput, - decodeNullDataOutput, - decodeScriptHashOutput, - decodeWitnessPubKeyHashOutput, - decodeWitnessScriptHashOutput -] - var OP_INT_BASE = OPS.OP_RESERVED // OP_1 - 1 function compile (chunks) { @@ -169,410 +159,71 @@ function isCanonicalSignature (buffer) { return bip66.check(buffer.slice(0, -1)) } -function isPubKeyHashInput (script) { - var chunks = decompile(script) - - return chunks.length === 2 && - isCanonicalSignature(chunks[0]) && - isCanonicalPubKey(chunks[1]) -} - -function decodePubKeyHashOutput (buffer, chunks) { - if (buffer.length !== 25) { - throw new Error('pub-key-hash output script is 25 bytes') - } - if (buffer[0] !== OPS.OP_DUP) { - throw new Error('PubKeyHash script missing OP_DUP') - } - if (buffer[1] !== OPS.OP_HASH160) { - throw new Error('PubKeyHash script missing OP_HASH160') - } - if (buffer[2] !== 0x14) { - throw new Error('Incorrect opcode for pubkeyhash') - } - if (buffer[23] !== OPS.OP_EQUALVERIFY) { - throw new Error('PubKeyHash script missing OP_EQUALVERIFY') - } - if (buffer[24] !== OPS.OP_CHECKSIG) { - throw new Error('PubKeyHash script missing OP_CHECKSIG') - } - - return { - type: 'pubkeyhash', - pubKeyHash: chunks[2] - } -} - -function isPubKeyHashOutput (script) { - return determinesTypeOrNonstandard([decodePubKeyHashOutput], script) === 'pubkeyhash' -} - -function isPubKeyInput (script) { - var chunks = decompile(script) - - return chunks.length === 1 && - isCanonicalSignature(chunks[0]) -} - -function decodePubKeyOutput (script, chunks) { - if (script[0] !== 0x21 && script[0] !== 0x41) { - throw new Error('Bad (or non-minimal) public key length for pub') - } - if (!isCanonicalPubKey(chunks[0])) { - throw new Error('pub-key output does not have a canonical public key') - } - if (chunks[1] !== OPS.OP_CHECKSIG) { - throw new Error('pub-key output missing OP_CHECKSIG operator') - } - if (chunks.length !== 2) { - throw new Error('pub-key output has two elements') - } - return { - type: 'pubkey', - publicKey: chunks[0] - } -} - -function isPubKeyOutput (script) { - return determinesTypeOrNonstandard([decodePubKeyOutput], script) === 'pubkey' -} - -function isScriptHashInput (script, allowIncomplete) { - var chunks = decompile(script) - if (chunks.length < 2) return false - - var lastChunk = chunks[chunks.length - 1] - if (!Buffer.isBuffer(lastChunk)) return false - - var scriptSigChunks = chunks.slice(0, -1) - var redeemScriptChunks = decompile(lastChunk) - - // is redeemScript a valid script? - if (redeemScriptChunks.length === 0) return false - - return classifyInput(scriptSigChunks, allowIncomplete) === classifyOutput(lastChunk) -} - -function decodeScriptHashOutput (chunks) { - if (chunks[0] !== OPS.OP_HASH160) { - throw new Error() - } - if (chunks[1] !== 0x14) { - throw new Error() - } - if (chunks[22] !== OPS.OP_EQUAL) { - throw new Error() - } - return { - type: 'scripthash', - scriptHash: chunks[1] - } -} - -function isScriptHashOutput (script) { - return determinesTypeOrNonstandard([decodeScriptHashOutput], script) === 'scripthash' -} - -function decodeWitnessPubKeyHashOutput (script, chunks) { - if (script.length !== 22) { - throw new Error('P2WPKH script should be 22 bytes') - } - if (script[0] !== OPS.OP_0) { - throw new Error('Missing v0 prefix for witness keyhash') - } - if (script[1] !== 0x14) { - throw new Error('Witness keyhash length marker is wrong') - } - - return { - type: 'witnesspubkeyhash', - witnessKeyHash: chunks[2] - } -} +module.exports = { + compile: compile, + decompile: decompile, + fromASM: fromASM, + toASM: toASM, + number: require('./script_number'), -function isWitnessPubKeyHashOutput (script) { - return determinesTypeOrNonstandard([decodeWitnessPubKeyHashOutput], script) === 'witnesspubkeyhash' + isCanonicalPubKey: isCanonicalPubKey, + isCanonicalSignature: isCanonicalSignature, + isDefinedHashType: isDefinedHashType } -function decodeWitnessScriptHashOutput (script, chunks) { - if (script.length !== 34) { - throw new Error('P2WSH script should be 34 bytes') - } - if (script[0] !== OPS.OP_0) { - throw new Error('Missing v0 prefix for witness script hash') - } - if (script[1] !== 0x20) { - throw new Error('Witness program length marker is wrong') - } +// - return { - type: 'witnessscripthash', - witnessScriptHash: chunks[2] - } -} +var multisig = require('./templates/multisig') +var nullData = require('./templates/nulldata') +var pubKey = require('./templates/pubkey') +var pubKeyHash = require('./templates/pubkeyhash') +var scriptHash = require('./templates/scripthash') +var witnessPubKeyHash = require('./templates/witnesspubkeyhash') +var witnessScriptHash = require('./templates/witnessscripthash') -function isWitnessScriptHashOutput (script) { - return determinesTypeOrNonstandard([decodeWitnessScriptHashOutput], script) === 'witnessscripthash' -} +function classifyOutput (script) { + if (witnessPubKeyHash.output.check(script)) return 'witnesspubkeyhash' + if (witnessScriptHash.output.check(script)) return 'witnessscripthash' + if (pubKeyHash.output.check(script)) return 'pubkeyhash' + if (scriptHash.output.check(script)) return 'scripthash' -// 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) { + // XXX: optimization, below functions .decompile before use var chunks = decompile(script) - if (chunks.length < 2) return false - if (chunks[0] !== OPS.OP_0) return false - - if (allowIncomplete) { - return chunks.slice(1).every(function (chunk) { - return chunk === OPS.OP_0 || isCanonicalSignature(chunk) - }) - } - - return chunks.slice(1).every(isCanonicalSignature) -} - -function decodeMultisigOutput (scriptPubKey, chunks) { - if (chunks.length < 4) { - throw new Error('Multisig script should contain at least 4 elements') - } - if (chunks[chunks.length - 1] !== OPS.OP_CHECKMULTISIG) { - throw new Error('Final opcode should be OP_CHECKMULTISIG') - } - if (!types.Number(chunks[0])) { - throw new Error('First element must be a number-push opcode') - } - if (!types.Number(chunks[chunks.length - 2])) { - throw new Error('Second-last element must be a number-push opcode') - } - var m = chunks[0] - OP_INT_BASE - var n = chunks[chunks.length - 2] - OP_INT_BASE + if (multisig.output.check(chunks)) return 'multisig' + if (pubKey.output.check(chunks)) return 'pubkey' + if (nullData.output.check(chunks)) return 'nulldata' - if (m <= 0) { - throw new Error('Number of signatures required must be greater than zero') - } - if (n > 16) { - throw new Error('Number of public keys must be less than 17') - } - if (m > n) { - throw new Error('Number of signatures cannot exceed the number of public keys') - } - if (n !== chunks.length - 3) { - throw new Error('n does not match the number of public keys') - } - - var keys = chunks.slice(1, -2) - if (!keys.every(isCanonicalPubKey)) { - throw new Error('Non-canonical pubic key found') - } - - return { - type: 'multisig', - nRequiredSigs: m, - nPublicKeys: n, - publicKeyBuffers: keys - } -} - -function isMultisigOutput (script) { - return determinesTypeOrNonstandard([decodeMultisigOutput], script) === 'multisig' -} - -function isNullDataOutput (script) { - return determinesTypeOrNonstandard([decodeNullDataOutput], script) === 'nulldata' -} - -function decodeNullDataOutput (script, chunks) { - if (script[0] !== OPS.OP_RETURN) { - throw new Error('Missing OP_RETURN at start of script') - } - - return { - type: 'nulldata' - } -} - -function determinesTypeOrNonstandard (functions, script) { - if (!types.Array(functions)) { - throw new Error('Must provide an array of functions to determinesTypeOrNonstandard') - } - if (!types.Buffer(script)) { - throw new Error('Must provide a script to determinesTypeOrNonstandard') - } - var decoded - var decompiled = decompile(script) - var type = 'nonstandard' - for (var i = 0; i < functions.length && type === 'nonstandard'; i++) { - try { - decoded = functions[i](script, decompiled) - type = decoded.type - } catch (e) { - - } - } - - return type -} - -function classifyOutput (script) { - return determinesTypeOrNonstandard(LIST_DECODE_NAMES, script) + return 'nonstandard' } function classifyInput (script, allowIncomplete) { + // XXX: optimization, below functions .decompile before use var chunks = decompile(script) - if (isPubKeyHashInput(chunks)) { - return 'pubkeyhash' - } else if (isMultisigInput(chunks, allowIncomplete)) { - return 'multisig' - } else if (isScriptHashInput(chunks, allowIncomplete)) { - return 'scripthash' - } else if (isPubKeyInput(chunks)) { - return 'pubkey' - } + if (pubKeyHash.input.check(chunks)) return 'pubkeyhash' + if (scriptHash.input.check(chunks, allowIncomplete)) return 'scripthash' + if (multisig.input.check(chunks, allowIncomplete)) return 'multisig' + if (pubKey.input.check(chunks)) return 'pubkey' return 'nonstandard' } -// Standard Script Templates -// {pubKey} OP_CHECKSIG -function pubKeyOutput (pubKey) { - return compile([pubKey, OPS.OP_CHECKSIG]) -} - -// OP_DUP OP_HASH160 {pubKeyHash} OP_EQUALVERIFY OP_CHECKSIG -function pubKeyHashOutput (pubKeyHash) { - typeforce(types.Hash160bit, pubKeyHash) - - return compile([OPS.OP_DUP, OPS.OP_HASH160, pubKeyHash, OPS.OP_EQUALVERIFY, OPS.OP_CHECKSIG]) -} - -// OP_HASH160 {scriptHash} OP_EQUAL -function scriptHashOutput (scriptHash) { - typeforce(types.Hash160bit, scriptHash) - - return compile([OPS.OP_HASH160, scriptHash, OPS.OP_EQUAL]) -} - -// m [pubKeys ...] n OP_CHECKMULTISIG -function multisigOutput (m, pubKeys) { - typeforce(types.tuple(types.Number, [types.Buffer]), arguments) - - var n = pubKeys.length - if (n < m) throw new Error('Not enough pubKeys provided') - - return compile([].concat( - OP_INT_BASE + m, - pubKeys, - OP_INT_BASE + n, - OPS.OP_CHECKMULTISIG - )) -} - -// 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) - - return compile([signature]) -} - -// {signature} {pubKey} -function pubKeyHashInput (signature, pubKey) { - typeforce(types.tuple(types.Buffer, types.Buffer), arguments) - - return compile([signature, pubKey]) -} - -// {serialized scriptPubKey script} -function scriptHashInput (scriptSig, scriptPubKey) { - var scriptSigChunks = decompile(scriptSig) - var serializedScriptPubKey = compile(scriptPubKey) - - return compile([].concat( - scriptSigChunks, - serializedScriptPubKey - )) -} - -// {serialized scriptPubKey script} -function witnessScriptHashInput (scriptSig, scriptPubKey) { - return scriptHashInput(scriptSig, scriptPubKey) -} - -// OP_0 [signatures ...] -function multisigInput (signatures, scriptPubKey) { - if (scriptPubKey) { - var scriptData = decodeMultisigOutput(scriptPubKey, decompile(scriptPubKey)) - if (signatures.length < scriptData.nRequiredSigs) throw new Error('Not enough signatures provided') - if (signatures.length > scriptData.nPublicKeys) throw new Error('Too many signatures provided') - } - - return compile([].concat(OPS.OP_0, signatures)) -} +function classifyWitness (script, allowIncomplete) { + // XXX: optimization, below functions .decompile before use + var chunks = decompile(script) -function nullDataOutput (data) { - return compile([OPS.OP_RETURN, data]) + if (pubKeyHash.input.check(chunks)) return 'witnesspubkeyhash' + if (scriptHash.input.check(chunks)) return 'witnessscripthash' + return 'nonstandard' } -module.exports = { - compile: compile, - decompile: decompile, - fromASM: fromASM, - toASM: toASM, - - number: require('./script_number'), - - isCanonicalPubKey: isCanonicalPubKey, - isCanonicalSignature: isCanonicalSignature, - isDefinedHashType: isDefinedHashType, - - decodePubKeyOutput: decodePubKeyOutput, - decodePubKeyHashOutput: decodePubKeyHashOutput, - decodeMultisigOutput: decodeMultisigOutput, - decodeScriptHashOutput: decodeScriptHashOutput, - decodeNullDataOutput: decodeNullDataOutput, - decodeWitnessPubKeyHashOutput: decodeWitnessPubKeyHashOutput, - decodeWitnessScriptHashOutput: decodeWitnessScriptHashOutput, - - isPubKeyOutput: isPubKeyOutput, - isPubKeyHashOutput: isPubKeyHashOutput, - isMultisigOutput: isMultisigOutput, - isScriptHashOutput: isScriptHashOutput, - isNullDataOutput: isNullDataOutput, - isWitnessPubKeyHashOutput: isWitnessPubKeyHashOutput, - isWitnessScriptHashOutput: isWitnessScriptHashOutput, - classifyOutput: classifyOutput, - - isPubKeyInput: isPubKeyInput, - isPubKeyHashInput: isPubKeyHashInput, - isMultisigInput: isMultisigInput, - isScriptHashInput: isScriptHashInput, - classifyInput: classifyInput, - - pubKeyOutput: pubKeyOutput, - pubKeyHashOutput: pubKeyHashOutput, - multisigOutput: multisigOutput, - scriptHashOutput: scriptHashOutput, - nullDataOutput: nullDataOutput, - witnessPubKeyHashOutput: witnessPubKeyHashOutput, - witnessScriptHashOutput: witnessScriptHashOutput, - - pubKeyInput: pubKeyInput, - pubKeyHashInput: pubKeyHashInput, - multisigInput: multisigInput, - scriptHashInput: scriptHashInput, - witnessScriptHashInput: witnessScriptHashInput -} +module.exports.classifyInput = classifyInput +module.exports.classifyOutput = classifyOutput +module.exports.classifyWitness = classifyWitness +module.exports.multisig = multisig +module.exports.nullData = nullData +module.exports.pubKey = pubKey +module.exports.pubKeyHash = pubKeyHash +module.exports.scriptHash = scriptHash +module.exports.witnessPubKeyHash = witnessPubKeyHash +module.exports.witnessScriptHash = witnessScriptHash diff --git a/src/templates/multisig/index.js b/src/templates/multisig/index.js new file mode 100644 index 0000000..46863d6 --- /dev/null +++ b/src/templates/multisig/index.js @@ -0,0 +1,4 @@ +module.exports = { + input: require('./input'), + output: require('./output') +} diff --git a/src/templates/multisig/input.js b/src/templates/multisig/input.js new file mode 100644 index 0000000..1bc00f2 --- /dev/null +++ b/src/templates/multisig/input.js @@ -0,0 +1,54 @@ +// OP_0 [signatures ...] + +var bscript = require('../../script') +var typeforce = require('typeforce') +var OPS = require('../../opcodes.json') + +function partialSignature (value) { + return value === OPS.OP_0 || bscript.isCanonicalSignature(value) +} + +function check (script, allowIncomplete) { + var chunks = bscript.decompile(script) + if (chunks.length < 2) return false + if (chunks[0] !== OPS.OP_0) return false + + if (allowIncomplete) { + return chunks.slice(1).every(partialSignature) + } + + return chunks.slice(1).every(bscript.isCanonicalSignature) +} + +function encode (signatures, scriptPubKey) { + typeforce([partialSignature], signatures) + + if (scriptPubKey) { + var scriptData = bscript.multisig.output.decode(scriptPubKey) + + if (signatures.length < scriptData.m) { + throw new TypeError('Not enough signatures provided') + } + + if (signatures.length > scriptData.pubKeys.length) { + throw new TypeError('Too many signatures provided') + } + } + + return bscript.compile([].concat(OPS.OP_0, signatures)) +} + +function decode (buffer, allowIncomplete) { + var chunks = bscript.decompile(buffer) + typeforce(check, chunks, allowIncomplete) + + return { + signatures: chunks.slice(1) + } +} + +module.exports = { + check: check, + decode: decode, + encode: encode +} diff --git a/src/templates/multisig/output.js b/src/templates/multisig/output.js new file mode 100644 index 0000000..087530b --- /dev/null +++ b/src/templates/multisig/output.js @@ -0,0 +1,63 @@ +// m [pubKeys ...] n OP_CHECKMULTISIG + +var bscript = require('../../script') +var types = require('../../types') +var typeforce = require('typeforce') +var OPS = require('../../opcodes.json') +var OP_INT_BASE = OPS.OP_RESERVED // OP_1 - 1 + +function check (script, allowIncomplete) { + var chunks = bscript.decompile(script) + + if (chunks.length < 4) return false + if (chunks[chunks.length - 1] !== OPS.OP_CHECKMULTISIG) return false + if (!types.Number(chunks[0])) return false + if (!types.Number(chunks[chunks.length - 2])) return false + var m = chunks[0] - OP_INT_BASE + var n = chunks[chunks.length - 2] - OP_INT_BASE + + if (m <= 0) return false + if (n > 16) return false + if (m > n) return false + if (n !== chunks.length - 3) return false + if (allowIncomplete) return true + + var keys = chunks.slice(1, -2) + return keys.every(bscript.isCanonicalPubKey) +} + +function encode (m, pubKeys) { + typeforce({ + m: types.Number, + pubKeys: [bscript.isCanonicalPubKey] + }, { + m: m, + pubKeys: pubKeys + }) + + var n = pubKeys.length + if (n < m) throw new TypeError('Not enough pubKeys provided') + + return bscript.compile([].concat( + OP_INT_BASE + m, + pubKeys, + OP_INT_BASE + n, + OPS.OP_CHECKMULTISIG + )) +} + +function decode (buffer, allowIncomplete) { + var chunks = bscript.decompile(buffer) + typeforce(check, chunks, allowIncomplete) + + return { + m: chunks[0] - OP_INT_BASE, + pubKeys: chunks.slice(1, -2) + } +} + +module.exports = { + check: check, + decode: decode, + encode: encode +} diff --git a/src/templates/nulldata.js b/src/templates/nulldata.js new file mode 100644 index 0000000..e996107 --- /dev/null +++ b/src/templates/nulldata.js @@ -0,0 +1,34 @@ +// OP_RETURN {data} + +var bscript = require('../script') +var types = require('../types') +var typeforce = require('typeforce') +var OPS = require('../opcodes.json') + +function check (script) { + var buffer = bscript.compile(script) + + return buffer.length > 1 && + buffer[0] === OPS.OP_RETURN +} + +function encode (data) { + typeforce(types.Buffer, data) + + return bscript.compile([OPS.OP_RETURN, data]) +} + +function decode (buffer) { + typeforce(check, buffer) + + return buffer.slice(1) +} + +module.exports = { + output: { + check: check, + decode: decode, + encode: encode + } +} + diff --git a/src/templates/pubkey/index.js b/src/templates/pubkey/index.js new file mode 100644 index 0000000..46863d6 --- /dev/null +++ b/src/templates/pubkey/index.js @@ -0,0 +1,4 @@ +module.exports = { + input: require('./input'), + output: require('./output') +} diff --git a/src/templates/pubkey/input.js b/src/templates/pubkey/input.js new file mode 100644 index 0000000..82a2dfe --- /dev/null +++ b/src/templates/pubkey/input.js @@ -0,0 +1,31 @@ +// {signature} + +var bscript = require('../../script') +var types = require('../../types') +var typeforce = require('typeforce') + +function check (script) { + var chunks = bscript.decompile(script) + + return chunks.length === 1 && + bscript.isCanonicalSignature(chunks[0]) +} + +function encode (signature) { + typeforce(types.Buffer, signature) + + return bscript.compile([signature]) +} + +function decode (buffer) { + var chunks = bscript.decompile(buffer) + typeforce(check, chunks) + + return chunks[0] +} + +module.exports = { + check: check, + decode: decode, + encode: encode +} diff --git a/src/templates/pubkey/output.js b/src/templates/pubkey/output.js new file mode 100644 index 0000000..1e6f961 --- /dev/null +++ b/src/templates/pubkey/output.js @@ -0,0 +1,32 @@ +// {pubKey} OP_CHECKSIG + +var bscript = require('../../script') +var typeforce = require('typeforce') +var OPS = require('../../opcodes.json') + +function check (script) { + var chunks = bscript.decompile(script) + + return chunks.length === 2 && + bscript.isCanonicalPubKey(chunks[0]) && + chunks[1] === OPS.OP_CHECKSIG +} + +function encode (pubKey) { + typeforce(bscript.isCanonicalPubKey, pubKey) + + return bscript.compile([pubKey, OPS.OP_CHECKSIG]) +} + +function decode (buffer) { + var chunks = bscript.decompile(buffer) + typeforce(check, chunks) + + return chunks[0] +} + +module.exports = { + check: check, + decode: decode, + encode: encode +} diff --git a/src/templates/pubkeyhash/index.js b/src/templates/pubkeyhash/index.js new file mode 100644 index 0000000..46863d6 --- /dev/null +++ b/src/templates/pubkeyhash/index.js @@ -0,0 +1,4 @@ +module.exports = { + input: require('./input'), + output: require('./output') +} diff --git a/src/templates/pubkeyhash/input.js b/src/templates/pubkeyhash/input.js new file mode 100644 index 0000000..8a55ab7 --- /dev/null +++ b/src/templates/pubkeyhash/input.js @@ -0,0 +1,39 @@ +// {pubKey} OP_CHECKSIG + +var bscript = require('../../script') +var types = require('../../types') +var typeforce = require('typeforce') + +function check (script) { + var chunks = bscript.decompile(script) + + return chunks.length === 2 && + bscript.isCanonicalSignature(chunks[0]) && + bscript.isCanonicalPubKey(chunks[1]) +} + +function encode (signature, pubKey) { + typeforce({ + signature: types.Buffer, pubKey: types.Buffer + }, { + signature: signature, pubKey: pubKey + }) + + return bscript.compile([signature, pubKey]) +} + +function decode (buffer) { + var chunks = bscript.decompile(buffer) + typeforce(check, chunks) + + return { + signature: chunks[0], + pubKey: chunks[1] + } +} + +module.exports = { + check: check, + decode: decode, + encode: encode +} diff --git a/src/templates/pubkeyhash/output.js b/src/templates/pubkeyhash/output.js new file mode 100644 index 0000000..a7d87b9 --- /dev/null +++ b/src/templates/pubkeyhash/output.js @@ -0,0 +1,41 @@ +// OP_DUP OP_HASH160 {pubKeyHash} OP_EQUALVERIFY OP_CHECKSIG + +var bscript = require('../../script') +var types = require('../../types') +var typeforce = require('typeforce') +var OPS = require('../../opcodes.json') + +function check (script) { + var buffer = bscript.compile(script) + + return buffer.length === 25 && + buffer[0] === OPS.OP_DUP && + buffer[1] === OPS.OP_HASH160 && + buffer[2] === 0x14 && + buffer[23] === OPS.OP_EQUALVERIFY && + buffer[24] === OPS.OP_CHECKSIG +} + +function encode (pubKeyHash) { + typeforce(types.Hash160bit, pubKeyHash) + + return bscript.compile([ + OPS.OP_DUP, + OPS.OP_HASH160, + pubKeyHash, + OPS.OP_EQUALVERIFY, + OPS.OP_CHECKSIG + ]) +} + +function decode (buffer) { + typeforce(check, buffer) + + return buffer.slice(3, 23) +} + +module.exports = { + check: check, + decode: decode, + encode: encode +} diff --git a/src/templates/scripthash/index.js b/src/templates/scripthash/index.js new file mode 100644 index 0000000..46863d6 --- /dev/null +++ b/src/templates/scripthash/index.js @@ -0,0 +1,4 @@ +module.exports = { + input: require('./input'), + output: require('./output') +} diff --git a/src/templates/scripthash/input.js b/src/templates/scripthash/input.js new file mode 100644 index 0000000..569b2a9 --- /dev/null +++ b/src/templates/scripthash/input.js @@ -0,0 +1,48 @@ +// {serialized scriptPubKey script} + +var bscript = require('../../script') +var typeforce = require('typeforce') + +function check (script, allowIncomplete) { + var chunks = bscript.decompile(script) + if (chunks.length < 2) return false + + var lastChunk = chunks[chunks.length - 1] + if (!Buffer.isBuffer(lastChunk)) return false + + var scriptSigChunks = chunks.slice(0, -1) + var redeemScriptChunks = bscript.decompile(lastChunk) + + // is redeemScript a valid script? + if (redeemScriptChunks.length === 0) return false + + var inputType = bscript.classifyInput(scriptSigChunks, allowIncomplete) + var outputType = bscript.classifyOutput(redeemScriptChunks) + return inputType === outputType +} + +function encode (scriptSignature, scriptPubKey) { + var scriptSigChunks = bscript.decompile(scriptSignature) + var serializedScriptPubKey = bscript.compile(scriptPubKey) + + return bscript.compile([].concat( + scriptSigChunks, + serializedScriptPubKey + )) +} + +function decode (buffer) { + var chunks = bscript.decompile(buffer) + typeforce(check, chunks) + + return { + scriptSignature: chunks.slice(0, -1), + scriptPubKey: chunks[chunks.length - 1] + } +} + +module.exports = { + check: check, + decode: decode, + encode: encode +} diff --git a/src/templates/scripthash/output.js b/src/templates/scripthash/output.js new file mode 100644 index 0000000..d73aa8b --- /dev/null +++ b/src/templates/scripthash/output.js @@ -0,0 +1,33 @@ +// OP_HASH160 {scriptHash} OP_EQUAL + +var bscript = require('../../script') +var types = require('../../types') +var typeforce = require('typeforce') +var OPS = require('../../opcodes.json') + +function check (script) { + var buffer = bscript.compile(script) + + return buffer.length === 23 && + buffer[0] === OPS.OP_HASH160 && + buffer[1] === 0x14 && + buffer[22] === OPS.OP_EQUAL +} + +function encode (scriptHash) { + typeforce(types.Hash160bit, scriptHash) + + return bscript.compile([OPS.OP_HASH160, scriptHash, OPS.OP_EQUAL]) +} + +function decode (buffer) { + typeforce(check, buffer) + + return buffer.slice(2, 22) +} + +module.exports = { + check: check, + decode: decode, + encode: encode +} diff --git a/src/templates/witnesspubkeyhash/index.js b/src/templates/witnesspubkeyhash/index.js new file mode 100644 index 0000000..d459038 --- /dev/null +++ b/src/templates/witnesspubkeyhash/index.js @@ -0,0 +1,3 @@ +module.exports = { + output: require('./output') +} diff --git a/src/templates/witnesspubkeyhash/output.js b/src/templates/witnesspubkeyhash/output.js new file mode 100644 index 0000000..fdf2ae2 --- /dev/null +++ b/src/templates/witnesspubkeyhash/output.js @@ -0,0 +1,32 @@ +// OP_0 {pubKeyHash} + +var bscript = require('../../script') +var types = require('../../types') +var typeforce = require('typeforce') +var OPS = require('../../opcodes.json') + +function check (script) { + var buffer = bscript.compile(script) + + return buffer.length === 22 && + buffer[0] === OPS.OP_0 && + buffer[1] === 0x14 +} + +function encode (pubKeyHash) { + typeforce(types.Hash160bit, pubKeyHash) + + return bscript.compile([OPS.OP_0, pubKeyHash]) +} + +function decode (buffer) { + typeforce(check, buffer) + + return buffer.slice(2) +} + +module.exports = { + check: check, + decode: decode, + encode: encode +} diff --git a/src/templates/witnessscripthash/index.js b/src/templates/witnessscripthash/index.js new file mode 100644 index 0000000..d459038 --- /dev/null +++ b/src/templates/witnessscripthash/index.js @@ -0,0 +1,3 @@ +module.exports = { + output: require('./output') +} diff --git a/src/templates/witnessscripthash/output.js b/src/templates/witnessscripthash/output.js new file mode 100644 index 0000000..a8f92a0 --- /dev/null +++ b/src/templates/witnessscripthash/output.js @@ -0,0 +1,32 @@ +// OP_0 {scriptHash} + +var bscript = require('../../script') +var types = require('../../types') +var typeforce = require('typeforce') +var OPS = require('../../opcodes.json') + +function check (script) { + var buffer = bscript.compile(script) + + return buffer.length === 34 && + buffer[0] === OPS.OP_0 && + buffer[1] === 0x20 +} + +function encode (scriptHash) { + typeforce(types.Hash256bit, scriptHash) + + return bscript.compile([OPS.OP_0, scriptHash]) +} + +function decode (buffer) { + typeforce(check, buffer) + + return buffer.slice(2) +} + +module.exports = { + check: check, + decode: decode, + encode: encode +} diff --git a/src/transaction_builder.js b/src/transaction_builder.js index 402917f..bf3ccd1 100644 --- a/src/transaction_builder.js +++ b/src/transaction_builder.js @@ -29,7 +29,7 @@ function expandInput (scriptSig, redeemScript) { var result = expandInput(redeemScriptSig, redeemScript) result.redeemScript = redeemScript result.redeemScriptType = result.prevOutType - result.prevOutScript = bscript.scriptHashOutput(bcrypto.hash160(redeemScript)) + result.prevOutScript = bscript.scriptHash.output.encode(bcrypto.hash160(redeemScript)) result.prevOutType = 'scripthash' return result @@ -39,7 +39,7 @@ function expandInput (scriptSig, redeemScript) { signatures = scriptSigChunks.slice(0, 1) if (redeemScript) break - prevOutScript = bscript.pubKeyHashOutput(bcrypto.hash160(pubKeys[0])) + prevOutScript = bscript.pubKeyHash.output.encode(bcrypto.hash160(pubKeys[0])) break case 'pubkey': @@ -161,7 +161,7 @@ function prepareInput (input, kpPubKey, redeemScript) { input.signatures = expanded.signatures input.redeemScript = redeemScript input.redeemScriptType = expanded.scriptType - input.prevOutScript = input.prevOutScript || bscript.scriptHashOutput(redeemScriptHash) + input.prevOutScript = input.prevOutScript || bscript.scriptHash.output.encode(redeemScriptHash) input.prevOutType = 'scripthash' // maybe we have some prevOut knowledge @@ -178,7 +178,7 @@ function prepareInput (input, kpPubKey, redeemScript) { // no prior knowledge, assume pubKeyHash } else { - input.prevOutScript = bscript.pubKeyHashOutput(bcrypto.hash160(kpPubKey)) + input.prevOutScript = bscript.pubKeyHash.output.encode(bcrypto.hash160(kpPubKey)) input.prevOutType = 'pubkeyhash' input.pubKeys = [kpPubKey] input.signatures = [undefined] @@ -195,9 +195,9 @@ function buildInput (input, allowIncomplete) { case 'pubkey': if (signatures.length < 1 || !signatures[0]) throw new Error('Not enough signatures provided') if (scriptType === 'pubkeyhash') { - scriptSig = bscript.pubKeyHashInput(signatures[0], input.pubKeys[0]) + scriptSig = bscript.pubKeyHash.input.encode(signatures[0], input.pubKeys[0]) } else { - scriptSig = bscript.pubKeyInput(signatures[0]) + scriptSig = bscript.pubKey.input.encode(signatures[0]) } break @@ -213,7 +213,7 @@ function buildInput (input, allowIncomplete) { signatures = signatures.filter(function (x) { return x !== ops.OP_0 }) } - scriptSig = bscript.multisigInput(signatures, allowIncomplete ? undefined : input.redeemScript) + scriptSig = bscript.multisig.input.encode(signatures, allowIncomplete ? undefined : input.redeemScript) break default: return @@ -221,7 +221,7 @@ function buildInput (input, allowIncomplete) { // wrap as scriptHash if necessary if (input.prevOutType === 'scripthash') { - scriptSig = bscript.scriptHashInput(scriptSig, input.redeemScript) + scriptSig = bscript.scriptHash.input.encode(scriptSig, input.redeemScript) } return scriptSig diff --git a/test/fixtures/script.json b/test/fixtures/script.json index 86fc8f8..7e41e96 100644 --- a/test/fixtures/script.json +++ b/test/fixtures/script.json @@ -188,249 +188,253 @@ "hex": "4effffffff01" } ], - "isPubKeyHashInput": [ - { - "description": "pubKeyHash input : extraneous data", - "scriptSig": "304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801 02359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1 ffffffff" - } - ], - "isScriptHashInput": [ - { - "description": "redeemScript not data", - "scriptSig": "OP_0 304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801 3045022100ef253c1faa39e65115872519e5f0a33bbecf430c0f35cf562beabbad4da24d8d02201742be8ee49812a73adea3007c9641ce6725c32cd44ddb8e3a3af460015d140501 OP_RESERVED" - }, - { - "description": "redeemScript is a signature, therefore not a valid script", - "scriptSig": "OP_0 3045022100e12b17b3a4c80c401a1687487bd2bafee9e5f1f8f1ffc6180ce186672ad7b43a02205e316d1e5e71822f5ef301b694e578fa9c94af4f5f098c952c833f4691307f4e01" - } - ], - "isPubKeyInput": [ - { - "description": "non-canonical signature (too short)", - "scriptSig": "304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf7593" - }, - { - "description": "non-canonical signature (too long)", - "scriptSig": "304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca28ffffffff01" - }, - { - "description": "non-canonical signature (invalid hashType)", - "scriptSig": "304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca28ff" - } - ], - "isPubKeyOutput": [ - { - "description": "non-canonical pubkey (too short)", - "scriptPubKey": "02359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce OP_CHECKSIG" - }, - { - "description": "non-canonical pubkey (too long)", - "scriptPubKey": "02359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1ffffff OP_CHECKSIG" - }, - { - "description": "last operator is wrong for pubkey-output", - "scriptPubKeyHex": "21027a71801ab59336de37785c50005b6abd8ea859eecce1edbe8e81afa74ee5c752ae" - }, - { - "description": "missing OP_CHECKSIG", - "scriptPubKeyHex": "21027a71801ab59336de37785c50005b6abd8ea859eecce1edbe8e81afa74ee5c752" - }, - { - "description": "non-canonical pubkey (bad prefix)", - "scriptPubKey": "427a71801ab59336de37785c50005b6abd8ea859eecce1edbe8e81afa74ee5c752 OP_CHECKSIG" - }, - { - "description": "has extra opcode at the end isPubKeyOutput", - "scriptPubKey": "027a71801ab59336de37785c50005b6abd8ea859eecce1edbe8e81afa74ee5c752 OP_CHECKSIG OP_0" - } - ], - "isPubKeyHashOutput": [ - { - "description": "non-minimal encoded isPubKeyHashOutput (non BIP62 compliant)", - "scriptPubKeyHex": "76a94c14aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac" - }, - { - "description": "bad OP_DUP isPubKeyHashOutput", - "scriptPubKeyHex": "aca914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac" - }, - { - "description": "bad OP_HASH160 isPubKeyHashOutput", - "scriptPubKeyHex": "76ac14aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac" - }, - { - "description": "bad OP_EQUALVERIFY isPubKeyHashOutput", - "scriptPubKeyHex": "76a914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c5acac" - }, - { - "description": "bad OP_CHECKSIG isPubKeyHashOutput", - "scriptPubKeyHex": "76a914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c58888" - }, - { - "description": "bad length isPubKeyHashOutput", - "scriptPubKeyHex": "76a920aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac" - }, - { - "description": "has something at the end isPubKeyHashOutput", - "scriptPubKeyHex": "76a920aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac00" - } - ], - "isScriptHashOutput": [ - { - "description": "non-minimal encoded isScriptHashOutput (non BIP62 compliant)", - "scriptPubKeyHex": "a94c14c286a1af0947f58d1ad787385b1c2c4a976f9e7187" - }, - { - "description": "wrong OP_HASH160 opcode", - "scriptPubKeyHex": "ac4c14c286a1af0947f58d1ad787385b1c2c4a976f9e7187" - }, - { - "description": "wrong length marker", - "scriptPubKeyHex": "a916c286a1af0947f58d1ad787385b1c2c4a976f9e7187" - }, - { - "description": "wrong OP_EQUAL opcode", - "scriptPubKeyHex": "a914c286a1af0947f58d1ad787385b1c2c4a976f9e7188" - } - ], - "isWitnessPubKeyHashOutput": [ - { - "description": "wrong version", - "scriptPubKeyHex": "51149090909090909090909090909090909090909090" - }, - { - "description": "wrong length marker", - "scriptPubKeyHex": "00209090909090909090909090909090909090909090" - } - ], - "isWitnessScriptHashOutput": [ - { - "description": "wrong version", - "scriptPubKeyHex": "51209090909090909090909090909090909090909090909090909090909090909090" - }, - { - "description": "wrong length marker", - "scriptPubKeyHex": "00219090909090909090909090909090909090909090909090909090909090909090" - } - ], - "isMultisigOutput": [ - { - "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": "n > len(pubKeys)", - "scriptPubKey": "OP_1 024289801366bcee6172b771cf5a7f13aaecd237a0b9a1ff9d769cabc2e6b70a34 OP_2 OP_CHECKMULTISIG" - }, - { - "description": "m is data", - "scriptPubKey": "ffff 024289801366bcee6172b771cf5a7f13aaecd237a0b9a1ff9d769cabc2e6b70a34 OP_1 OP_CHECKMULTISIG" - }, - { - "description": "n is data", - "scriptPubKey": "OP_1 024289801366bcee6172b771cf5a7f13aaecd237a0b9a1ff9d769cabc2e6b70a34 ffff OP_CHECKMULTISIG" - }, - { - "description": "non-canonical pubKey (bad length)", - "scriptPubKey": "OP_1 0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798ffff OP_1 OP_CHECKMULTISIG" - } - ], - "multisigInput": [ - { - "description": "Not enough signatures provided", - "type": "multisig", - "scriptPubKey": "OP_2 0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 02b80011a883a0fd621ad46dfc405df1e74bf075cbaf700fd4aebef6e96f848340 OP_2 OP_CHECKMULTISIG", - "signatures": [] - }, - { - "exception": "Not enough signatures provided", - "scriptPubKey": "OP_2 02359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1 0395a9d84d47d524548f79f435758c01faec5da2b7e551d3b8c995b7e06326ae4a OP_2 OP_CHECKMULTISIG", - "signatures": [ - "304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801" - ] - }, - { - "exception": "Too many signatures provided", - "scriptPubKey": "OP_2 02359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1 0395a9d84d47d524548f79f435758c01faec5da2b7e551d3b8c995b7e06326ae4a OP_2 OP_CHECKMULTISIG", - "signatures": [ - "304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801", - "3045022100ef253c1faa39e65115872519e5f0a33bbecf430c0f35cf562beabbad4da24d8d02201742be8ee49812a73adea3007c9641ce6725c32cd44ddb8e3a3af460015d140501", - "3045022100ef253c1faa39e65115872519e5f0a33bbecf430c0f35cf562beabbad4da24d8d02201742be8ee49812a73adea3007c9641ce6725c32cd44ddb8e3a3af460015d140501" - ] - } - ], - "multisigOutput": [ - { - "exception": "Not enough pubKeys provided", - "m": 4, - "pubKeys": [ - "02ea1297665dd733d444f31ec2581020004892cdaaf3dd6c0107c615afb839785f", - "02fab2dea1458990793f56f42e4a47dbf35a12a351f26fa5d7e0cc7447eaafa21f", - "036c6802ce7e8113723dd92cdb852e492ebb157a871ca532c3cb9ed08248ff0e19" - ], - "signatures": [ - "304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801" - ] - } - ], - "pubKeyHashOutput": [ - { - "exception": "Expected Buffer\\(Length: 20\\), got Buffer\\(Length: 2\\)", - "hash": "ffff" - } - ], - "scriptHashOutput": [ - { - "exception": "Expected Buffer\\(Length: 20\\), got Buffer\\(Length: 3\\)", - "hash": "ffffff" - } - ], - "witnessPubKeyHashOutput": [ - { - "exception": "Expected Buffer\\(Length: 20\\), got Buffer\\(Length: 3\\)", - "hash": "ffffff" - } - ], - "witnessScriptHashOutput": [ - { - "exception": "Expected Buffer\\(Length: 32\\), got Buffer\\(Length: 3\\)", - "hash": "ffffff" - } - ] + "pubKey": { + "inputs": [ + { + "description": "non-canonical signature (too short)", + "scriptSig": "304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf7593" + }, + { + "description": "non-canonical signature (too long)", + "scriptSig": "304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca28ffffffff01" + }, + { + "description": "non-canonical signature (invalid hashType)", + "scriptSig": "304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca28ff" + } + ], + "outputs": [ + { + "description": "non-canonical pubkey (too short)", + "scriptPubKey": "02359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce OP_CHECKSIG" + }, + { + "description": "non-canonical pubkey (too long)", + "scriptPubKey": "02359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1ffffff OP_CHECKSIG" + }, + { + "description": "last operator is wrong for pubkey-output", + "scriptPubKeyHex": "21027a71801ab59336de37785c50005b6abd8ea859eecce1edbe8e81afa74ee5c752ae" + }, + { + "description": "missing OP_CHECKSIG", + "scriptPubKeyHex": "21027a71801ab59336de37785c50005b6abd8ea859eecce1edbe8e81afa74ee5c752" + }, + { + "description": "non-canonical pubkey (bad prefix)", + "scriptPubKey": "427a71801ab59336de37785c50005b6abd8ea859eecce1edbe8e81afa74ee5c752 OP_CHECKSIG" + }, + { + "description": "has extra opcode at the end isPubKeyOutput", + "scriptPubKey": "027a71801ab59336de37785c50005b6abd8ea859eecce1edbe8e81afa74ee5c752 OP_CHECKSIG OP_0" + } + ] + }, + "pubKeyHash": { + "inputs": [ + { + "description": "pubKeyHash input : extraneous data", + "scriptSig": "304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801 02359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1 ffffffff" + } + ], + "outputs": [ + { + "description": "non-minimal encoded isPubKeyHashOutput (non BIP62 compliant)", + "scriptPubKeyHex": "76a94c14aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac" + }, + { + "description": "bad OP_DUP isPubKeyHashOutput", + "scriptPubKeyHex": "aca914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac" + }, + { + "description": "bad OP_HASH160 isPubKeyHashOutput", + "scriptPubKeyHex": "76ac14aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac" + }, + { + "description": "bad OP_EQUALVERIFY isPubKeyHashOutput", + "scriptPubKeyHex": "76a914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c5acac" + }, + { + "description": "bad OP_CHECKSIG isPubKeyHashOutput", + "scriptPubKeyHex": "76a914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c58888" + }, + { + "description": "bad length isPubKeyHashOutput", + "scriptPubKeyHex": "76a920aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac" + }, + { + "description": "has something at the end isPubKeyHashOutput", + "scriptPubKeyHex": "76a920aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac00" + }, + { + "exception": "Expected Buffer\\(Length: 20\\), got Buffer\\(Length: 2\\)", + "hash": "ffff" + } + ] + }, + "scriptHash": { + "inputs": [ + { + "description": "redeemScript not data", + "scriptSig": "OP_0 304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801 3045022100ef253c1faa39e65115872519e5f0a33bbecf430c0f35cf562beabbad4da24d8d02201742be8ee49812a73adea3007c9641ce6725c32cd44ddb8e3a3af460015d140501 OP_RESERVED" + }, + { + "description": "redeemScript is a signature, therefore not a valid script", + "scriptSig": "OP_0 3045022100e12b17b3a4c80c401a1687487bd2bafee9e5f1f8f1ffc6180ce186672ad7b43a02205e316d1e5e71822f5ef301b694e578fa9c94af4f5f098c952c833f4691307f4e01" + } + ], + "outputs": [ + { + "description": "non-minimal encoded isScriptHashOutput (non BIP62 compliant)", + "scriptPubKeyHex": "a94c14c286a1af0947f58d1ad787385b1c2c4a976f9e7187" + }, + { + "description": "wrong OP_HASH160 opcode", + "scriptPubKeyHex": "ac4c14c286a1af0947f58d1ad787385b1c2c4a976f9e7187" + }, + { + "description": "wrong length marker", + "scriptPubKeyHex": "a916c286a1af0947f58d1ad787385b1c2c4a976f9e7187" + }, + { + "description": "wrong OP_EQUAL opcode", + "scriptPubKeyHex": "a914c286a1af0947f58d1ad787385b1c2c4a976f9e7188" + }, + { + "exception": "Expected Buffer\\(Length: 20\\), got Buffer\\(Length: 3\\)", + "hash": "ffffff" + } + ] + }, + "multisig": { + "inputs": [ + { + "description": "Not enough signatures provided", + "type": "multisig", + "scriptPubKey": "OP_2 0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 02b80011a883a0fd621ad46dfc405df1e74bf075cbaf700fd4aebef6e96f848340 OP_2 OP_CHECKMULTISIG", + "signatures": [] + }, + { + "exception": "Not enough signatures provided", + "scriptPubKey": "OP_2 02359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1 0395a9d84d47d524548f79f435758c01faec5da2b7e551d3b8c995b7e06326ae4a OP_2 OP_CHECKMULTISIG", + "signatures": [ + "304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801" + ] + }, + { + "exception": "Too many signatures provided", + "scriptPubKey": "OP_2 02359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1 0395a9d84d47d524548f79f435758c01faec5da2b7e551d3b8c995b7e06326ae4a OP_2 OP_CHECKMULTISIG", + "signatures": [ + "304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801", + "3045022100ef253c1faa39e65115872519e5f0a33bbecf430c0f35cf562beabbad4da24d8d02201742be8ee49812a73adea3007c9641ce6725c32cd44ddb8e3a3af460015d140501", + "3045022100ef253c1faa39e65115872519e5f0a33bbecf430c0f35cf562beabbad4da24d8d02201742be8ee49812a73adea3007c9641ce6725c32cd44ddb8e3a3af460015d140501" + ] + } + ], + "outputs": [ + { + "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": "n > len(pubKeys)", + "scriptPubKey": "OP_1 024289801366bcee6172b771cf5a7f13aaecd237a0b9a1ff9d769cabc2e6b70a34 OP_2 OP_CHECKMULTISIG" + }, + { + "description": "m is data", + "scriptPubKey": "ffff 024289801366bcee6172b771cf5a7f13aaecd237a0b9a1ff9d769cabc2e6b70a34 OP_1 OP_CHECKMULTISIG" + }, + { + "description": "n is data", + "scriptPubKey": "OP_1 024289801366bcee6172b771cf5a7f13aaecd237a0b9a1ff9d769cabc2e6b70a34 ffff OP_CHECKMULTISIG" + }, + { + "description": "non-canonical pubKey (bad length)", + "scriptPubKey": "OP_1 0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798ffff OP_1 OP_CHECKMULTISIG" + }, + { + "exception": "Not enough pubKeys provided", + "m": 4, + "pubKeys": [ + "02ea1297665dd733d444f31ec2581020004892cdaaf3dd6c0107c615afb839785f", + "02fab2dea1458990793f56f42e4a47dbf35a12a351f26fa5d7e0cc7447eaafa21f", + "036c6802ce7e8113723dd92cdb852e492ebb157a871ca532c3cb9ed08248ff0e19" + ], + "signatures": [ + "304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801" + ] + } + ] + }, + "witnessPubKeyHash": { + "inputs": [], + "outputs": [ + { + "description": "wrong version", + "scriptPubKeyHex": "51149090909090909090909090909090909090909090" + }, + { + "description": "wrong length marker", + "scriptPubKeyHex": "00209090909090909090909090909090909090909090" + }, + { + "exception": "Expected Buffer\\(Length: 20\\), got Buffer\\(Length: 3\\)", + "hash": "ffffff" + } + ] + }, + "witnessScriptHash": { + "inputs": [], + "outputs": [ + { + "description": "wrong version", + "scriptPubKeyHex": "51209090909090909090909090909090909090909090909090909090909090909090" + }, + { + "description": "wrong length marker", + "scriptPubKeyHex": "00219090909090909090909090909090909090909090909090909090909090909090" + }, + { + "exception": "Expected Buffer\\(Length: 32\\), got Buffer\\(Length: 3\\)", + "hash": "ffffff" + } + ] + } } } diff --git a/test/script.js b/test/script.js index 0a2bf5b..b595f65 100644 --- a/test/script.js +++ b/test/script.js @@ -146,44 +146,43 @@ describe('script', function () { }) ;[ - 'PubKey', - 'PubKeyHash', - 'ScriptHash', - 'WitnessPubKeyHash', - 'WitnessScriptHash', - 'Multisig', - 'NullData' - ].forEach(function (type) { - var inputFnName = 'is' + type + 'Input' - var outputFnName = 'is' + type + 'Output' - - var inputFn = bscript[inputFnName] - var outputFn = bscript[outputFnName] - - describe('is' + type + 'Input', function () { + 'pubKey', + 'pubKeyHash', + 'scriptHash', + 'witnessPubKeyHash', + 'witnessScriptHash', + 'multisig', + 'nullData' + ].forEach(function (name) { + var inputType = bscript[name].input + var outputType = bscript[name].output + + describe(name + '.input.check', function () { fixtures.valid.forEach(function (f) { - var expected = type.toLowerCase() === f.type + var expected = name.toLowerCase() === f.type - if (inputFn && f.scriptSig) { + if (inputType && f.scriptSig) { var scriptSig = bscript.fromASM(f.scriptSig) it('returns ' + expected + ' for ' + f.scriptSig, function () { - assert.strictEqual(inputFn(scriptSig), expected) + assert.strictEqual(inputType.check(scriptSig), expected) }) if (f.typeIncomplete) { - var expectedIncomplete = type.toLowerCase() === f.typeIncomplete + var expectedIncomplete = name.toLowerCase() === f.typeIncomplete it('returns ' + expected + ' for ' + f.scriptSig, function () { - assert.strictEqual(inputFn(scriptSig, true), expectedIncomplete) + assert.strictEqual(inputType.check(scriptSig, true), expectedIncomplete) }) } } }) - if (!(inputFnName in fixtures.invalid)) return + if (!(fixtures.invalid[name])) return + + fixtures.invalid[name].inputs.forEach(function (f) { + if (!f.scriptSig && !f.scriptSigHex) return - fixtures.invalid[inputFnName].forEach(function (f) { it('returns false for ' + f.description + ' (' + (f.scriptSig || f.scriptSigHex) + ')', function () { var scriptSig @@ -193,27 +192,29 @@ describe('script', function () { scriptSig = new Buffer(f.scriptSigHex, 'hex') } - assert.strictEqual(inputFn(scriptSig), false) + assert.strictEqual(inputType.check(scriptSig), false) }) }) }) - describe('is' + type + 'Output', function () { + describe(name + '.output.check', function () { fixtures.valid.forEach(function (f) { - var expected = type.toLowerCase() === f.type + var expected = name.toLowerCase() === f.type - if (outputFn && f.scriptPubKey) { + if (outputType && f.scriptPubKey) { it('returns ' + expected + ' for ' + f.scriptPubKey, function () { var scriptPubKey = bscript.fromASM(f.scriptPubKey) - assert.strictEqual(outputFn(scriptPubKey), expected) + assert.strictEqual(outputType.check(scriptPubKey), expected) }) } }) - if (!(outputFnName in fixtures.invalid)) return + if (!(fixtures.invalid[name])) return + + fixtures.invalid[name].outputs.forEach(function (f) { + if (!f.scriptPubKey && !f.scriptPubKeyHex) return - fixtures.invalid[outputFnName].forEach(function (f) { it('returns false for ' + f.description + ' (' + (f.scriptPubKey || f.scriptPubKeyHex) + ')', function () { var scriptPubKey @@ -223,39 +224,39 @@ describe('script', function () { scriptPubKey = new Buffer(f.scriptPubKeyHex, 'hex') } - assert.strictEqual(outputFn(scriptPubKey), false) + assert.strictEqual(outputType.check(scriptPubKey), false) }) }) }) }) - describe('pubKeyInput', function () { + describe('pubKey.input.encode', function () { fixtures.valid.forEach(function (f) { if (f.type !== 'pubkey') return it('returns ' + f.scriptSig, function () { var signature = new Buffer(f.signature, 'hex') - var scriptSig = bscript.pubKeyInput(signature) + var scriptSig = bscript.pubKey.input.encode(signature) assert.strictEqual(bscript.toASM(scriptSig), f.scriptSig) }) }) }) - describe('pubKeyOutput', function () { + describe('pubKey.output.encode', function () { fixtures.valid.forEach(function (f) { if (f.type !== 'pubkey') return it('returns ' + f.scriptPubKey, function () { var pubKey = new Buffer(f.pubKey, 'hex') - var scriptPubKey = bscript.pubKeyOutput(pubKey) + var scriptPubKey = bscript.pubKey.output.encode(pubKey) assert.strictEqual(bscript.toASM(scriptPubKey), f.scriptPubKey) }) }) }) - describe('pubKeyHashInput', function () { + describe('pubKeyHash.input.encode', function () { fixtures.valid.forEach(function (f) { if (f.type !== 'pubkeyhash') return @@ -264,13 +265,13 @@ describe('script', function () { it('returns ' + f.scriptSig, function () { var signature = new Buffer(f.signature, 'hex') - var scriptSig = bscript.pubKeyHashInput(signature, pubKey) + var scriptSig = bscript.pubKeyHash.input.encode(signature, pubKey) assert.strictEqual(bscript.toASM(scriptSig), f.scriptSig) }) }) }) - describe('pubKeyHashOutput', function () { + describe('pubKeyHash.output.encode', function () { fixtures.valid.forEach(function (f) { if (f.type !== 'pubkeyhash') return @@ -278,23 +279,24 @@ describe('script', function () { var pubKeyHash = bcrypto.hash160(pubKey) it('returns ' + f.scriptPubKey, function () { - var scriptPubKey = bscript.pubKeyHashOutput(pubKeyHash) + var scriptPubKey = bscript.pubKeyHash.output.encode(pubKeyHash) assert.strictEqual(bscript.toASM(scriptPubKey), f.scriptPubKey) }) }) - fixtures.invalid.pubKeyHashOutput.forEach(function (f) { + fixtures.invalid.pubKeyHash.outputs.forEach(function (f) { + if (!f.hash) return var hash = new Buffer(f.hash, 'hex') it('throws on ' + f.exception, function () { assert.throws(function () { - bscript.pubKeyHashOutput(hash) + bscript.pubKeyHash.output.encode(hash) }, new RegExp(f.exception)) }) }) }) - describe('multisigInput', function () { + describe('multisig.input.encode', function () { fixtures.valid.forEach(function (f) { if (f.type !== 'multisig') return @@ -303,12 +305,13 @@ describe('script', function () { return signature ? new Buffer(signature, 'hex') : ops.OP_0 }) - var scriptSig = bscript.multisigInput(signatures) + var scriptSig = bscript.multisig.input.encode(signatures) assert.strictEqual(bscript.toASM(scriptSig), f.scriptSig) }) }) - fixtures.invalid.multisigInput.forEach(function (f) { + fixtures.invalid.multisig.inputs.forEach(function (f) { + if (!f.scriptPubKey) return var scriptPubKey = bscript.fromASM(f.scriptPubKey) it('throws on ' + f.exception, function () { @@ -317,38 +320,39 @@ describe('script', function () { }) assert.throws(function () { - bscript.multisigInput(signatures, scriptPubKey) + bscript.multisig.input.encode(signatures, scriptPubKey) }, new RegExp(f.exception)) }) }) }) - describe('multisigOutput', function () { + describe('multisig.output.encode', function () { fixtures.valid.forEach(function (f) { if (f.type !== 'multisig') return var pubKeys = f.pubKeys.map(function (p) { return new Buffer(p, 'hex') }) - var scriptPubKey = bscript.multisigOutput(pubKeys.length, pubKeys) + var scriptPubKey = bscript.multisig.output.encode(pubKeys.length, pubKeys) it('returns ' + f.scriptPubKey, function () { assert.strictEqual(bscript.toASM(scriptPubKey), f.scriptPubKey) }) }) - fixtures.invalid.multisigOutput.forEach(function (f) { + fixtures.invalid.multisig.outputs.forEach(function (f) { + if (!f.pubKeys) return var pubKeys = f.pubKeys.map(function (p) { return new Buffer(p, 'hex') }) it('throws on ' + f.exception, function () { assert.throws(function () { - bscript.multisigOutput(f.m, pubKeys) + bscript.multisig.output.encode(f.m, pubKeys) }, new RegExp(f.exception)) }) }) }) - describe('scriptHashInput', function () { + describe('scriptHash.input.encode', function () { fixtures.valid.forEach(function (f) { if (f.type !== 'scripthash') return @@ -356,7 +360,7 @@ describe('script', function () { var redeemScriptSig = bscript.fromASM(f.redeemScriptSig) it('returns ' + f.scriptSig, function () { - var scriptSig = bscript.scriptHashInput(redeemScriptSig, redeemScript) + var scriptSig = bscript.scriptHash.input.encode(redeemScriptSig, redeemScript) if (f.scriptSig) { assert.strictEqual(bscript.toASM(scriptSig), f.scriptSig) @@ -367,31 +371,32 @@ describe('script', function () { }) }) - describe('scriptHashOutput', function () { + describe('scriptHash.output.encode', function () { fixtures.valid.forEach(function (f) { if (f.type !== 'scripthash') return if (!f.scriptPubKey) return it('returns ' + f.scriptPubKey, function () { var redeemScript = bscript.fromASM(f.redeemScript) - var scriptPubKey = bscript.scriptHashOutput(bcrypto.hash160(redeemScript)) + var scriptPubKey = bscript.scriptHash.output.encode(bcrypto.hash160(redeemScript)) assert.strictEqual(bscript.toASM(scriptPubKey), f.scriptPubKey) }) }) - fixtures.invalid.scriptHashOutput.forEach(function (f) { + fixtures.invalid.scriptHash.outputs.forEach(function (f) { + if (!f.hash) return var hash = new Buffer(f.hash, 'hex') it('throws on ' + f.exception, function () { assert.throws(function () { - bscript.scriptHashOutput(hash) + bscript.scriptHash.output.encode(hash) }, new RegExp(f.exception)) }) }) }) - describe('witnessPubKeyHashOutput', function () { + describe('witnessPubKeyHash.output.encode', function () { fixtures.valid.forEach(function (f) { if (f.type !== 'witnesspubkeyhash') return if (!f.scriptPubKey) return @@ -400,23 +405,24 @@ describe('script', function () { var pubKeyHash = bcrypto.hash160(pubKey) it('returns ' + f.scriptPubKey, function () { - var scriptPubKey = bscript.witnessPubKeyHashOutput(pubKeyHash) + var scriptPubKey = bscript.witnessPubKeyHash.output.encode(pubKeyHash) assert.strictEqual(bscript.toASM(scriptPubKey), f.scriptPubKey) }) }) - fixtures.invalid.witnessPubKeyHashOutput.forEach(function (f) { + fixtures.invalid.witnessPubKeyHash.outputs.forEach(function (f) { + if (!f.hash) return var hash = new Buffer(f.hash, 'hex') it('throws on ' + f.exception, function () { assert.throws(function () { - bscript.witnessPubKeyHashOutput(hash) + bscript.witnessPubKeyHash.output.encode(hash) }, new RegExp(f.exception)) }) }) }) - describe('witnessScriptHashInput', function () { + describe('witnessScriptHash.inputs.encode', function () { fixtures.valid.forEach(function (f) { if (f.type !== 'witnessscripthash') return @@ -424,43 +430,44 @@ describe('script', function () { var witnessScriptSig = bscript.fromASM(f.witnessScriptSig) it('returns ' + f.witness, function () { - var witness = bscript.witnessScriptHashInput(witnessScriptSig, witnessScript) + var witness = bscript.witnessScriptHash.input.encode(witnessScriptSig, witnessScript) assert.strictEqual(bscript.toASM(witness), f.witness) }) }) }) - describe('witnessScriptHashOutput', function () { + describe('witnessScriptHash.outputs.encode', 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)) + var scriptPubKey = bscript.witnessScriptHash.output.encode(bcrypto.hash256(witnessScriptPubKey)) assert.strictEqual(bscript.toASM(scriptPubKey), f.scriptPubKey) }) }) - fixtures.invalid.witnessScriptHashOutput.forEach(function (f) { + fixtures.invalid.witnessScriptHash.outputs.forEach(function (f) { + if (!f.hash) return var hash = new Buffer(f.hash, 'hex') it('throws on ' + f.exception, function () { assert.throws(function () { - bscript.witnessScriptHashOutput(hash) + bscript.witnessScriptHash.output.encode(hash) }, new RegExp(f.exception)) }) }) }) - describe('nullDataOutput', function () { + describe('nullData.output.encode', function () { fixtures.valid.forEach(function (f) { if (f.type !== 'nulldata') return var data = new Buffer(f.data, 'hex') - var scriptPubKey = bscript.nullDataOutput(data) + var scriptPubKey = bscript.nullData.output.encode(data) it('returns ' + f.scriptPubKey, function () { assert.strictEqual(bscript.toASM(scriptPubKey), f.scriptPubKey) diff --git a/test/transaction_builder.js b/test/transaction_builder.js index de4dc27..97c52ca 100644 --- a/test/transaction_builder.js +++ b/test/transaction_builder.js @@ -357,7 +357,7 @@ describe('TransactionBuilder', function () { var signatures = bscript.decompile(scriptSig).slice(1, -1).filter(function (x) { return x !== ops.OP_0 }) // rebuild/replace the scriptSig without them - var replacement = bscript.scriptHashInput(bscript.multisigInput(signatures), redeemScript) + var replacement = bscript.scriptHash.input.encode(bscript.multisig.input.encode(signatures), redeemScript) assert.strictEqual(bscript.toASM(replacement), sign.scriptSigFiltered) tx.ins[i].script = replacement