From 8893131e088abba97b49dc91c1f2f12a93d6c688 Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Thu, 8 Jun 2017 19:47:38 -0700 Subject: [PATCH] script: move static methods off of script constructor. --- lib/primitives/tx.js | 13 +- lib/script/common.js | 102 ++++++++--- lib/script/opcode.js | 30 ++++ lib/script/script.js | 411 ++++++++++++++----------------------------- 4 files changed, 241 insertions(+), 315 deletions(-) diff --git a/lib/primitives/tx.js b/lib/primitives/tx.js index 9a3ae0b3..d1251cd0 100644 --- a/lib/primitives/tx.js +++ b/lib/primitives/tx.js @@ -1300,6 +1300,9 @@ TX.prototype.getSigopsCost = function getSigopsCost(view, flags) { if (flags & Script.flags.VERIFY_P2SH) cost += this.getScripthashSigops(view) * scale; + if ((flags & Script.flags.VERIFY_WITNESS) === 0) + return cost; + for (i = 0; i < this.inputs.length; i++) { input = this.inputs[i]; coin = view.getOutput(input); @@ -1307,11 +1310,7 @@ TX.prototype.getSigopsCost = function getSigopsCost(view, flags) { if (!coin) continue; - cost += Script.getWitnessSigops( - input.script, - coin.script, - input.witness, - flags); + cost += coin.script.getWitnessSigops(input.script, input.witness); } return cost; @@ -1326,10 +1325,6 @@ TX.prototype.getSigopsCost = function getSigopsCost(view, flags) { TX.prototype.getSigops = function getSigops(view, flags) { var scale = consensus.WITNESS_SCALE_FACTOR; - - if (flags == null) - flags = Script.flags.STANDARD_VERIFY_FLAGS; - return (this.getSigopsCost(view, flags) + scale - 1) / scale | 0; }; diff --git a/lib/script/common.js b/lib/script/common.js index cbfd7e88..85752cd0 100644 --- a/lib/script/common.js +++ b/lib/script/common.js @@ -14,6 +14,7 @@ var assert = require('assert'); var BN = require('bn.js'); var util = require('../utils/util'); +var ec = require('../crypto/ec'); /** * Script opcodes. @@ -298,7 +299,7 @@ exports.typesByVal = util.revMap(exports.types); * @const {Buffer} */ -exports.STACK_FALSE = Buffer.alloc(0); +exports.STACK_FALSE = Buffer.from([]); /** * True stack return value. @@ -314,6 +315,59 @@ exports.STACK_TRUE = Buffer.from([0x01]); exports.STACK_NEGATE = Buffer.from([0x81]); +/** + * Test a signature to see whether it contains a valid sighash type. + * @param {Buffer} sig + * @returns {Boolean} + */ + +exports.isHashType = function isHashType(sig) { + var type; + + assert(Buffer.isBuffer(sig)); + + if (sig.length === 0) + return false; + + type = sig[sig.length - 1] & ~exports.hashType.ANYONECANPAY; + + if (!(type >= exports.hashType.ALL && type <= exports.hashType.SINGLE)) + return false; + + return true; +}; + +/** + * Test a signature to see whether it contains a low S value. + * @param {Buffer} sig + * @returns {Boolean} + */ + +exports.isLowDER = function isLowDER(sig) { + if (!exports.isSignatureEncoding(sig)) + return false; + + return ec.isLowS(sig.slice(0, -1)); +}; + +/** + * Get a small integer from an opcode (OP_0-OP_16). + * @param {Number} index + * @returns {Number} + */ + +exports.getSmall = function getSmall(op) { + assert(typeof op === 'number'); + + if (op === exports.opcodes.OP_0) + return 0; + + if (op >= exports.opcodes.OP_1 && op <= exports.opcodes.OP_16) + return op - 0x50; + + return -1; +}; + /** * Test whether the data element is a ripemd160 hash. * @param {Buffer?} hash @@ -346,34 +400,6 @@ exports.isSignature = function isSignature(sig) { return Buffer.isBuffer(sig) && sig.length >= 9 && sig.length <= 73; }; -/** - * Test whether the data element is a null dummy (a zero-length array). - * @param {Buffer?} data - * @returns {Boolean} - */ - -exports.isDummy = function isDummy(data) { - return Buffer.isBuffer(data) && data.length === 0; -}; - -/** - * Test whether the data element is a compressed key. - * @param {Buffer} key - * @returns {Boolean} - */ - -exports.isCompressedEncoding = function isCompressedEncoding(key) { - assert(Buffer.isBuffer(key)); - - if (key.length !== 33) - return false; - - if (key[0] !== 0x02 && key[0] !== 0x03) - return false; - - return true; -}; - /** * Test whether the data element is a valid key. * @param {Buffer} key @@ -399,6 +425,24 @@ exports.isKeyEncoding = function isKeyEncoding(key) { return true; }; +/** + * Test whether the data element is a compressed key. + * @param {Buffer} key + * @returns {Boolean} + */ + +exports.isCompressedEncoding = function isCompressedEncoding(key) { + assert(Buffer.isBuffer(key)); + + if (key.length !== 33) + return false; + + if (key[0] !== 0x02 && key[0] !== 0x03) + return false; + + return true; +}; + /** * Test a signature to see if it abides by BIP66. * @see https://github.com/bitcoin/bips/blob/master/bip-0066.mediawiki diff --git a/lib/script/opcode.js b/lib/script/opcode.js index 14e7f0d6..e26cfca1 100644 --- a/lib/script/opcode.js +++ b/lib/script/opcode.js @@ -34,6 +34,36 @@ function Opcode(value, data) { this.data = data || null; } +/** + * Check to see if a pushdata abides by minimaldata. + * @returns {Boolean} + */ + +Opcode.prototype.isMinimal = function isMinimal() { + if (!this.data) + return true; + + if (this.data.length === 0) + return this.value === opcodes.OP_0; + + if (this.data.length === 1 && this.data[0] >= 1 && this.data[0] <= 16) + return false; + + if (this.data.length === 1 && this.data[0] === 0x81) + return false; + + if (this.data.length <= 75) + return this.value === this.data.length; + + if (this.data.length <= 255) + return this.value === opcodes.OP_PUSHDATA1; + + if (this.data.length <= 65535) + return this.value === opcodes.OP_PUSHDATA2; + + return true; +}; + /** * Encode the opcode to a buffer writer. * @param {BufferWriter} bw diff --git a/lib/script/script.js b/lib/script/script.js index 42cea82a..850f7c7d 100644 --- a/lib/script/script.js +++ b/lib/script/script.js @@ -507,6 +507,7 @@ Script.prototype.execute = function execute(stack, flags, tx, index, value, vers var alt = []; var state = []; var negate = 0; + var minimal = false; var op, code, data; var val, v1, v2, v3; var num, n1, n2, n3; @@ -518,6 +519,12 @@ Script.prototype.execute = function execute(stack, flags, tx, index, value, vers if (flags == null) flags = Script.flags.STANDARD_VERIFY_FLAGS; + if (version == null) + version = 0; + + if (flags & common.flags.VERIFY_MINIMALDATA) + minimal = true; + if (this.getSize() > consensus.MAX_SCRIPT_SIZE) throw new ScriptError('SCRIPT_SIZE'); @@ -536,7 +543,7 @@ Script.prototype.execute = function execute(stack, flags, tx, index, value, vers // Note that minimaldata is not checked // on unexecuted branches of code. if (negate === 0) { - if (!Script.isMinimal(data, op, flags)) + if (minimal && !code.isMinimal()) throw new ScriptError('MINIMALDATA', op, ip); stack.push(data); } @@ -1180,8 +1187,8 @@ Script.prototype.execute = function execute(stack, flags, tx, index, value, vers if (version === 0) subscript.removeData(sig); - Script.validateSignature(sig, flags); - Script.validateKey(key, flags, version); + validateSignature(sig, flags); + validateKey(key, flags, version); if (sig.length > 0) { type = sig[sig.length - 1]; @@ -1259,8 +1266,8 @@ Script.prototype.execute = function execute(stack, flags, tx, index, value, vers sig = stack.top(-isig); key = stack.top(-ikey); - Script.validateSignature(sig, flags); - Script.validateKey(key, flags, version); + validateSignature(sig, flags); + validateKey(key, flags, version); if (sig.length > 0) { type = sig[sig.length - 1]; @@ -1293,7 +1300,7 @@ Script.prototype.execute = function execute(stack, flags, tx, index, value, vers throw new ScriptError('INVALID_STACK_OPERATION', op, ip); if (flags & Script.flags.VERIFY_NULLDUMMY) { - if (!Script.isDummy(stack.top(-1))) + if (stack.top(-1).length !== 0) throw new ScriptError('SIG_NULLDUMMY', op, ip); } @@ -1526,7 +1533,7 @@ Script.prototype.removeData = function removeData(data) { if (!op.data) continue; - if (!Script.isMinimal(op.data, op.value)) + if (!op.isMinimal()) continue; if (util.equal(op.data, data)) @@ -1571,57 +1578,16 @@ Script.prototype.indexOf = function indexOf(data) { }; /** - * Check to see if a pushdata Buffer abides by minimaldata. - * @param {Buffer} data - * @param {Number} opcode - * @param {Number?} flags - * @returns {Boolean} - */ - -Script.isMinimal = function isMinimal(data, opcode, flags) { - if (flags == null) - flags = Script.flags.STANDARD_VERIFY_FLAGS; - - if (!(flags & Script.flags.VERIFY_MINIMALDATA)) - return true; - - if (!data) - return true; - - if (data.length === 0) - return opcode === opcodes.OP_0; - - if (data.length === 1 && data[0] >= 1 && data[0] <= 16) - return false; - - if (data.length === 1 && data[0] === 0x81) - return false; - - if (data.length <= 75) - return opcode === data.length; - - if (data.length <= 255) - return opcode === opcodes.OP_PUSHDATA1; - - if (data.length <= 65535) - return opcode === opcodes.OP_PUSHDATA2; - - return true; -}; - -/** - * Test a buffer to see if it is valid + * Test a script to see if it is valid * script code (no non-existent opcodes). - * @param {Buffer} raw * @returns {Boolean} */ -Script.isCode = function isCode(raw) { - var script = Script.fromRaw(raw); +Script.prototype.isCode = function isCode() { var i, op; - for (i = 0; i < script.code.length; i++) { - op = script.code[i]; + for (i = 0; i < this.code.length; i++) { + op = this.code[i]; if (op.data) continue; @@ -2093,7 +2059,7 @@ Script.prototype.isPubkey = function isPubkey(minimal) { } return this.code.length === 2 - && Script.isKey(this.code[0].data) + && common.isKey(this.code[0].data) && this.code[1].value === opcodes.OP_CHECKSIG; }; @@ -2116,7 +2082,7 @@ Script.prototype.isPubkeyhash = function isPubkeyhash(minimal) { return this.code.length === 5 && this.code[0].value === opcodes.OP_DUP && this.code[1].value === opcodes.OP_HASH160 - && Script.isHash(this.code[2].data) + && common.isHash(this.code[2].data) && this.code[3].value === opcodes.OP_EQUALVERIFY && this.code[4].value === opcodes.OP_CHECKSIG; }; @@ -2136,12 +2102,12 @@ Script.prototype.isMultisig = function isMultisig(minimal) { if (this.raw[this.raw.length - 1] !== opcodes.OP_CHECKMULTISIG) return false; - n = Script.getSmall(this.raw[this.raw.length - 2]); + n = common.getSmall(this.raw[this.raw.length - 2]); if (n < 1) return false; - m = Script.getSmall(this.raw[0]); + m = common.getSmall(this.raw[0]); if (!(m >= 1 && m <= n)) return false; @@ -2152,11 +2118,11 @@ Script.prototype.isMultisig = function isMultisig(minimal) { for (i = 1; i < n + 1; i++) { op = this.code[i]; - if (!Script.isKey(op.data)) + if (!common.isKey(op.data)) return false; if (minimal) { - if (!Script.isMinimal(op.data, op.value)) + if (!op.isMinimal()) return false; } } @@ -2202,7 +2168,7 @@ Script.prototype.isNulldata = function isNulldata(minimal) { return false; if (this.raw.length === 2) - return Script.getSmall(this.raw[1]) !== -1; + return common.getSmall(this.raw[1]) !== -1; if (this.raw[1] >= 0x01 && this.raw[1] <= 0x4b) return this.raw[1] + 2 === this.raw.length; @@ -2287,7 +2253,7 @@ Script.prototype.toProgram = function toProgram() { if (!this.isProgram()) return; - version = Script.getSmall(this.raw[0]); + version = common.getSmall(this.raw[0]); data = this.raw.slice(2); return new Program(version, data); @@ -2412,7 +2378,7 @@ Script.prototype.isPubkeyInput = function isPubkeyInput() { if (this.raw[0] > opcodes.OP_PUSHDATA4) return false; - return this.code.length === 1 && Script.isSignature(this.code[0].data); + return this.code.length === 1 && common.isSignature(this.code[0].data); }; /** @@ -2432,8 +2398,8 @@ Script.prototype.isPubkeyhashInput = function isPubkeyhashInput() { return false; return this.code.length === 2 - && Script.isSignature(this.code[0].data) - && Script.isKey(this.code[1].data); + && common.isSignature(this.code[0].data) + && common.isKey(this.code[1].data); }; /** @@ -2463,7 +2429,7 @@ Script.prototype.isMultisigInput = function isMultisigInput() { return false; for (i = 1; i < this.code.length; i++) { - if (!Script.isSignature(this.code[i].data)) + if (!common.isSignature(this.code[i].data)) return false; } @@ -2477,7 +2443,7 @@ Script.prototype.isMultisigInput = function isMultisigInput() { */ Script.prototype.isScripthashInput = function isScripthashInput() { - var op; + var op, redeem; if (this.raw.length < 2) return false; @@ -2499,16 +2465,18 @@ Script.prototype.isScripthashInput = function isScripthashInput() { // key, and we ensure that it is at least // a script that does not use undefined // opcodes. - if (Script.isDummy(op.data)) + if (op.data.length === 0) return false; - if (Script.isSignatureEncoding(op.data)) + if (common.isSignatureEncoding(op.data)) return false; - if (Script.isKeyEncoding(op.data)) + if (common.isKeyEncoding(op.data)) return false; - if (!Script.isCode(op.data)) + redeem = Script.fromRaw(op.data); + + if (!redeem.isCode()) return false; return true; @@ -2537,7 +2505,7 @@ Script.getCoinbaseHeight = function getCoinbaseHeight(raw) { return -1; // Small ints are allowed. - height = Script.getSmall(raw[0]); + height = common.getSmall(raw[0]); if (height !== -1) return height; @@ -2623,25 +2591,29 @@ Script.prototype.push = function push(data) { /** * Shift an item off of the `code` array. - * @returns {Buffer|Number} + * @returns {Buffer} */ Script.prototype.shift = function shift() { var op = this.code.shift(); + if (!op) - return; + return null; + return op.data || op.value; }; /** * Pop an item off of the `code` array. - * @returns {Buffer|Number} + * @returns {Buffer} */ Script.prototype.pop = function push(data) { var op = this.code.pop(); + if (!op) - return; + return null; + return op.data || op.value; }; @@ -2653,8 +2625,10 @@ Script.prototype.pop = function push(data) { Script.prototype.remove = function remove(i) { var op = this.code.splice(i, 1)[0]; + if (!op) - return; + return null; + return op.data || op.value; }; @@ -2666,19 +2640,21 @@ Script.prototype.remove = function remove(i) { Script.prototype.insert = function insert(i, data) { assert(i <= this.code.length, 'Index out of bounds.'); - this.code.splice(i, 0, Opcode.from(data))[0]; + this.code.splice(i, 0, Opcode.from(data)); }; /** * Get an item from the `code` array. * @param {Number} index - * @returns {Buffer|Number} + * @returns {Buffer} */ Script.prototype.get = function get(i) { var op = this.code[i]; + if (!op) - return; + return null; + return op.data || op.value; }; @@ -2690,9 +2666,11 @@ Script.prototype.get = function get(i) { Script.prototype.getSmall = function getSmall(i) { var op = this.code[i]; + if (!op) return -1; - return Script.getSmall(op.value); + + return common.getSmall(op.value); }; /** @@ -2709,7 +2687,7 @@ Script.prototype.getNumber = function getNumber(i) { return new BN(small); if (!op || !op.data || op.data.length > 5) - return; + return null; return Script.num(op.data, Script.flags.VERIFY_NONE, 5); }; @@ -2722,8 +2700,10 @@ Script.prototype.getNumber = function getNumber(i) { Script.prototype.getString = function getString(i) { var op = this.code[i]; + if (!op || !op.data) - return; + return null; + return op.data.toString('utf8'); }; @@ -2746,16 +2726,6 @@ Script.prototype.set = function set(i, data) { this.code[i] = Opcode.from(data); }; -/** - * Test whether the data element is a ripemd160 hash. - * @param {Buffer?} hash - * @returns {Boolean} - */ - -Script.isHash = function isHash(hash) { - return common.isHash(hash); -}; - /** * Test whether the data element is a public key. Note that * this does not verify the format of the key, only the length. @@ -2778,153 +2748,6 @@ Script.isSignature = function isSignature(sig) { return common.isSignature(sig); }; -/** - * Test whether the data element is a null dummy (a zero-length array). - * @param {Buffer?} data - * @returns {Boolean} - */ - -Script.isDummy = function isDummy(data) { - return common.isDummy(data); -}; - -/** - * Test whether the data element is a valid key if VERIFY_STRICTENC is enabled. - * @param {Buffer} key - * @param {VerifyFlags?} flags - * @returns {Boolean} - * @throws {ScriptError} - */ - -Script.validateKey = function validateKey(key, flags, version) { - if (flags == null) - flags = Script.flags.STANDARD_VERIFY_FLAGS; - - assert(Buffer.isBuffer(key)); - - if (flags & Script.flags.VERIFY_STRICTENC) { - if (!Script.isKeyEncoding(key)) - throw new ScriptError('PUBKEYTYPE'); - } - - if (version === 1) { - if (flags & Script.flags.VERIFY_WITNESS_PUBKEYTYPE) { - if (!Script.isCompressedEncoding(key)) - throw new ScriptError('WITNESS_PUBKEYTYPE'); - } - } - - return true; -}; - -/** - * Test whether the data element is a compressed key. - * @param {Buffer} key - * @returns {Boolean} - */ - -Script.isCompressedEncoding = function isCompressedEncoding(key) { - return common.isCompressedEncoding(key); -}; - -/** - * Test whether the data element is a valid key. - * @param {Buffer} key - * @returns {Boolean} - */ - -Script.isKeyEncoding = function isKeyEncoding(key) { - return common.isKeyEncoding(key); -}; - -/** - * Test whether the data element is a valid signature based - * on the encoding, S value, and sighash type. Requires - * VERIFY_DERSIG|VERIFY_LOW_S|VERIFY_STRICTENC, VERIFY_LOW_S - * and VERIFY_STRING_ENC to be enabled respectively. Note that - * this will allow zero-length signatures. - * @param {Buffer} sig - * @param {VerifyFlags?} flags - * @returns {Boolean} - * @throws {ScriptError} - */ - -Script.validateSignature = function validateSignature(sig, flags) { - if (flags == null) - flags = Script.flags.STANDARD_VERIFY_FLAGS; - - assert(Buffer.isBuffer(sig)); - - // Allow empty sigs - if (sig.length === 0) - return true; - - if ((flags & Script.flags.VERIFY_DERSIG) - || (flags & Script.flags.VERIFY_LOW_S) - || (flags & Script.flags.VERIFY_STRICTENC)) { - if (!Script.isSignatureEncoding(sig)) - throw new ScriptError('SIG_DER'); - } - - if (flags & Script.flags.VERIFY_LOW_S) { - if (!Script.isLowDER(sig)) - throw new ScriptError('SIG_HIGH_S'); - } - - if (flags & Script.flags.VERIFY_STRICTENC) { - if (!Script.isHashType(sig)) - throw new ScriptError('SIG_HASHTYPE'); - } - - return true; -}; - -/** - * Test a signature to see if it abides by BIP66. - * @see https://github.com/bitcoin/bips/blob/master/bip-0066.mediawiki - * @param {Buffer} sig - * @returns {Boolean} - */ - -Script.isSignatureEncoding = function isSignatureEncoding(sig) { - return common.isSignatureEncoding(sig); -}; - -/** - * Test a signature to see whether it contains a valid sighash type. - * @param {Buffer} sig - * @returns {Boolean} - */ - -Script.isHashType = function isHashType(sig) { - var type; - - assert(Buffer.isBuffer(sig)); - - if (sig.length === 0) - return false; - - type = sig[sig.length - 1] & ~Script.hashType.ANYONECANPAY; - - if (!(type >= Script.hashType.ALL && type <= Script.hashType.SINGLE)) - return false; - - return true; -}; - -/** - * Test a signature to see whether it contains a low S value. - * @param {Buffer} sig - * @returns {Boolean} - */ - -Script.isLowDER = function isLowDER(sig) { - if (!Script.isSignatureEncoding(sig)) - return false; - - return ec.isLowS(sig.slice(0, -1)); -}; - /** * Test the script to see if it contains only push ops. * Push ops are: OP_1NEGATE, OP_0-OP_16 and all PUSHDATAs. @@ -3031,16 +2854,12 @@ Script.prototype.getScripthashSigops = function getScripthashSigops(input) { * Count the sigops for a program. * @param {Program} program * @param {Witness} witness - * @param {VerifyFlags} flags * @returns {Number} sigop count */ -Script.witnessSigops = function witnessSigops(program, witness, flags) { +Script.witnessSigops = function witnessSigops(program, witness) { var redeem; - if (flags == null) - flags = Script.flags.STANDARD_VERIFY_FLAGS; - if (program.version === 0) { if (program.data.length === 20) return 1; @@ -3057,25 +2876,15 @@ Script.witnessSigops = function witnessSigops(program, witness, flags) { /** * Count the sigops in a script, taking into account witness programs. * @param {Script} input - * @param {Script} output * @param {Witness} witness - * @param {VerifyFlags} flags * @returns {Number} sigop count */ -Script.getWitnessSigops = function getWitnessSigops(input, output, witness, flags) { +Script.prototype.getWitnessSigops = function getWitnessSigops(input, witness) { var redeem; - if (flags == null) - flags = Script.flags.STANDARD_VERIFY_FLAGS; - - if ((flags & Script.flags.VERIFY_WITNESS) === 0) - return 0; - - assert((flags & Script.flags.VERIFY_P2SH) !== 0); - - if (output.isProgram()) - return Script.witnessSigops(output.toProgram(), witness, flags); + if (this.isProgram()) + return Script.witnessSigops(this.toProgram(), witness); // This is a unique situation in terms of consensus // rules. We can just grab the redeem script without @@ -3084,10 +2893,10 @@ Script.getWitnessSigops = function getWitnessSigops(input, output, witness, flag // which checks for parse errors and will return // false if one is found. Even the bitcoind code // does not check the return value of GetOp. - if (output.isScripthash() && input.isPushOnly()) { + if (this.isScripthash() && input.isPushOnly()) { redeem = input.getRedeem(); if (redeem && redeem.isProgram()) - return Script.witnessSigops(redeem.toProgram(), witness, flags); + return Script.witnessSigops(redeem.toProgram(), witness); } return 0; @@ -3164,25 +2973,6 @@ Script.fromString = function fromString(code) { return new Script().fromString(code); }; -/** - * Get a small integer from an opcode (OP_0-OP_16). - * @param {Number} index - * @returns {Number} - */ - -Script.getSmall = function getSmall(op) { - if (typeof op !== 'number') - return -1; - - if (op === opcodes.OP_0) - return 0; - - if (op >= opcodes.OP_1 && op <= opcodes.OP_16) - return op - 0x50; - - return -1; -}; - /** * Verify an input and output script, and a witness if present. * @param {Script} input @@ -3633,6 +3423,73 @@ function sortKeys(keys) { }); } +/** + * Test whether the data element is a valid key if VERIFY_STRICTENC is enabled. + * @param {Buffer} key + * @param {VerifyFlags?} flags + * @returns {Boolean} + * @throws {ScriptError} + */ + +function validateKey(key, flags, version) { + assert(Buffer.isBuffer(key)); + assert(typeof flags === 'number'); + + if (flags & Script.flags.VERIFY_STRICTENC) { + if (!common.isKeyEncoding(key)) + throw new ScriptError('PUBKEYTYPE'); + } + + if (version === 1) { + if (flags & Script.flags.VERIFY_WITNESS_PUBKEYTYPE) { + if (!common.isCompressedEncoding(key)) + throw new ScriptError('WITNESS_PUBKEYTYPE'); + } + } + + return true; +} + +/** + * Test whether the data element is a valid signature based + * on the encoding, S value, and sighash type. Requires + * VERIFY_DERSIG|VERIFY_LOW_S|VERIFY_STRICTENC, VERIFY_LOW_S + * and VERIFY_STRING_ENC to be enabled respectively. Note that + * this will allow zero-length signatures. + * @param {Buffer} sig + * @param {VerifyFlags?} flags + * @returns {Boolean} + * @throws {ScriptError} + */ + +function validateSignature(sig, flags) { + assert(Buffer.isBuffer(sig)); + assert(typeof flags === 'number'); + + // Allow empty sigs + if (sig.length === 0) + return true; + + if ((flags & Script.flags.VERIFY_DERSIG) + || (flags & Script.flags.VERIFY_LOW_S) + || (flags & Script.flags.VERIFY_STRICTENC)) { + if (!common.isSignatureEncoding(sig)) + throw new ScriptError('SIG_DER'); + } + + if (flags & Script.flags.VERIFY_LOW_S) { + if (!common.isLowDER(sig)) + throw new ScriptError('SIG_HIGH_S'); + } + + if (flags & Script.flags.VERIFY_STRICTENC) { + if (!common.isHashType(sig)) + throw new ScriptError('SIG_HASHTYPE'); + } + + return true; +} + /* * Expose */