From 1d8a86838d40b957b5cdd7c35dabbb65c05e61f6 Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Wed, 20 Apr 2016 09:17:58 -0700 Subject: [PATCH] new way of handling script parsing. --- lib/bcoin/script.js | 228 +++++++++++--------------------------------- 1 file changed, 58 insertions(+), 170 deletions(-) diff --git a/lib/bcoin/script.js b/lib/bcoin/script.js index b533088e..7de5034a 100644 --- a/lib/bcoin/script.js +++ b/lib/bcoin/script.js @@ -1047,8 +1047,6 @@ Script.prototype.execute = function execute(stack, flags, tx, index, version) { op = this.code[ip]; if (Buffer.isBuffer(op)) { - if (!Script.checkPush(op)) - throw new ScriptError('BAD_OPCODE', op, ip); if (op.length > constants.script.MAX_PUSH) throw new ScriptError('PUSH_SIZE', op, ip); // Note that minimaldata is not checked @@ -1981,7 +1979,7 @@ Script.array = function(value) { Script.prototype.removeData = function removeData(data) { var total = 0; - var pushdata = data.pushdata; + var op = data.op; var sig, raw, i, a, b; if (!this.raw) @@ -1989,10 +1987,10 @@ Script.prototype.removeData = function removeData(data) { // We need to reserialize // the signature as minimaldata. - delete data.pushdata; + delete data.op; sig = new Script([data]).encode(); - if (pushdata) - utils.hidden(data, 'pushdata', pushdata); + if (op) + utils.hidden(data, 'op', op); raw = this.encode(); @@ -2064,38 +2062,6 @@ Script.prototype.indexOf = function indexOf(data) { return utils.indexOf(this.code, data); }; -/** - * Test whether an op is a buffer, also - * check for buffer underflows. - * @param {Buffer?} value - * @returns {Boolean} - */ - -Script.isPush = function isPush(value) { - return Buffer.isBuffer(value) && Script.checkPush(value); -}; - -/** - * Perform some range checking on the pushdatas - * (exactly what GetOp2 does). Note that this - * _must_ be done during execution, not parsing. - * @see GetOp2 - * @param {Buffer} value - Pushdata op from script code - * (must be from a deserialized script). - * @returns {Boolean} - */ - -Script.checkPush = function checkPush(value) { - var pushdata = value.pushdata; - - if (!pushdata) - return true; - - // The pushdata size can never - // be greater than the buffer. - return pushdata.size === value.length; -}; - /** * Check to see if a pushdata Buffer abides by minimaldata. * @param {Buffer} value - Pushdata op from script code @@ -2105,15 +2071,13 @@ Script.checkPush = function checkPush(value) { */ Script.checkMinimal = function checkMinimal(value, flags) { - var pushdata = value.pushdata; - if (flags == null) flags = constants.flags.STANDARD_VERIFY_FLAGS; if (!(flags & constants.flags.VERIFY_MINIMALDATA)) return true; - if (!pushdata) + if (value.op == null) return true; if (value.length === 1 && value[0] >= 1 && value[0] <= 16) @@ -2123,13 +2087,13 @@ Script.checkMinimal = function checkMinimal(value, flags) { return false; if (value.length <= 75) - return pushdata.opcode == null && pushdata.size === value.length; + return value.op === value.length; if (value.length <= 255) - return pushdata.opcode === opcodes.OP_PUSHDATA1; + return value.op === opcodes.OP_PUSHDATA1; if (value.length <= 65535) - return pushdata.opcode === opcodes.OP_PUSHDATA2; + return value.op === opcodes.OP_PUSHDATA2; return true; }; @@ -2155,7 +2119,7 @@ Script.isCode = function isCode(buf) { op = code[i]; if (Buffer.isBuffer(op)) continue; - if (op >= opcodes.OP_PUSHDATA1 && op <= opcodes.OP_PUSHDATA4) + if (Script.isBadPush(op)) return false; if (constants.opcodesByVal[op] == null) return false; @@ -2312,7 +2276,7 @@ Script.prototype.getRedeem = function getRedeem() { Script.getRedeem = function getRedeem(code) { var redeem = code[code.length - 1]; - if (!Script.isPush(redeem)) + if (!Buffer.isBuffer(redeem)) return; return new Script(redeem); @@ -2662,14 +2626,11 @@ Script.prototype.isNulldata = function isNulldata() { for (i = 1; i < this.code.length; i++) { op = this.code[i]; - if (Buffer.isBuffer(op)) { - if (!Script.checkPush(op)) - return false; + if (Buffer.isBuffer(op)) continue; - } if (op > opcodes.OP_16) return false; - if (op >= opcodes.OP_PUSHDATA1 && op <= opcodes.OP_PUSHDATA4) + if (Script.isBadPush(op)) return false; } @@ -2699,7 +2660,7 @@ Script.prototype.isCommitment = function isCommitment() { } return this.code.length >= 2 && this.code[0] === opcodes.OP_RETURN - && Script.isPush(this.code[1]) + && Buffer.isBuffer(this.code[1]) && this.code[1].length === 36 && utils.readU32BE(this.code[1], 0) === 0xaa21a9ed; }; @@ -2746,7 +2707,7 @@ Script.prototype.isWitnessProgram = function isWitnessProgram() { if (typeof this.code[0] !== 'number') return false; - if (!Script.isPush(this.code[1])) + if (!Buffer.isBuffer(this.code[1])) return false; return (this.code[0] === opcodes.OP_0 @@ -3044,7 +3005,7 @@ Script.isScripthashInput = function isScripthashInput(code, isWitness) { // Last data element should be an array // for the redeem script. - if (!Script.isPush(raw)) + if (!Buffer.isBuffer(raw)) return false; // Testing for scripthash inputs requires @@ -3101,9 +3062,6 @@ Script.getCoinbaseHeight = function getCoinbaseHeight(code) { if (!Buffer.isBuffer(code[0])) return -1; - if (!Script.checkPush(code[0])) - return -1; - if (!Script.checkMinimal(code[0])) return -1; @@ -3152,7 +3110,7 @@ Script.prototype.getCoinbaseData = function getCoinbaseData() { */ Script.isHash = function isHash(hash) { - return Script.isPush(hash) && hash.length === 20; + return Buffer.isBuffer(hash) && hash.length === 20; }; /** @@ -3163,7 +3121,7 @@ Script.isHash = function isHash(hash) { */ Script.isKey = function isKey(key) { - return Script.isPush(key) && key.length >= 33 && key.length <= 65; + return Buffer.isBuffer(key) && key.length >= 33 && key.length <= 65; }; /** @@ -3174,7 +3132,7 @@ Script.isKey = function isKey(key) { */ Script.isSignature = function isSignature(sig) { - return Script.isPush(sig) && sig.length >= 9 && sig.length <= 73; + return Buffer.isBuffer(sig) && sig.length >= 9 && sig.length <= 73; }; /** @@ -3184,7 +3142,7 @@ Script.isSignature = function isSignature(sig) { */ Script.isDummy = function isDummy(data) { - return Script.isPush(data) && data.length === 0; + return Buffer.isBuffer(data) && data.length === 0; }; /** @@ -3213,7 +3171,7 @@ Script.validateKey = function validateKey(key, flags) { if (flags == null) flags = constants.flags.STANDARD_VERIFY_FLAGS; - if (!Script.isPush(key)) + if (!Buffer.isBuffer(key)) throw new ScriptError('BAD_OPCODE'); if (flags & constants.flags.VERIFY_STRICTENC) { @@ -3231,7 +3189,7 @@ Script.validateKey = function validateKey(key, flags) { */ Script.isKeyEncoding = function isKeyEncoding(key) { - if (!Script.isPush(key)) + if (!Buffer.isBuffer(key)) return false; if (key.length < 33) @@ -3266,7 +3224,7 @@ Script.validateSignature = function validateSignature(sig, flags) { if (flags == null) flags = constants.flags.STANDARD_VERIFY_FLAGS; - if (!Script.isPush(sig)) + if (!Buffer.isBuffer(sig)) throw new ScriptError('BAD_OPCODE'); // Allow empty sigs @@ -3303,7 +3261,7 @@ Script.validateSignature = function validateSignature(sig, flags) { Script.isSignatureEncoding = function isSignatureEncoding(sig) { var lenR, lenS; - if (!Script.isPush(sig)) + if (!Buffer.isBuffer(sig)) return false; // Format: 0x30 [total-length] 0x02 [R-length] [R] 0x02 [S-length] [S] [sighash] @@ -3394,7 +3352,7 @@ Script.isSignatureEncoding = function isSignatureEncoding(sig) { Script.isHashType = function isHashType(sig) { var type; - if (!Script.isPush(sig)) + if (!Buffer.isBuffer(sig)) return false; if (sig.length === 0) @@ -3416,7 +3374,7 @@ Script.isHashType = function isHashType(sig) { Script.isLowDER = function isLowDER(sig) { if (!sig.s) { - if (!Script.isPush(sig)) + if (!Buffer.isBuffer(sig)) return false; if (!Script.isSignatureEncoding(sig)) @@ -3485,14 +3443,11 @@ Script.prototype.isPushOnly = function isPushOnly() { var i, op; for (i = 0; i < this.code.length; i++) { op = this.code[i]; - if (Buffer.isBuffer(op)) { - if (!Script.checkPush(op)) - return false; + if (Buffer.isBuffer(op)) continue; - } if (op > opcodes.OP_16) return false; - if (op >= opcodes.OP_PUSHDATA1 && op <= opcodes.OP_PUSHDATA4) + if (Script.isBadPush(op)) return false; } return true; @@ -3513,13 +3468,10 @@ Script.prototype.getSigops = function getSigops(accurate) { for (i = 0; i < this.code.length; i++) { op = this.code[i]; - if (Script.isPush(op)) + if (Buffer.isBuffer(op)) continue; - if (op >= opcodes.OP_PUSHDATA1 && op <= opcodes.OP_PUSHDATA4) - return 0; - - if (constants.opcodesByVal[op] == null) + if (Script.isBadPush(op)) return 0; if (op === opcodes.OP_CHECKSIG || op === opcodes.OP_CHECKSIGVERIFY) { @@ -4085,72 +4037,14 @@ Script.fromRaw = function fromRaw(data, enc) { * @returns {Array} Script code. */ -Script.isPushOp = function isPushOp(op) { - if (Buffer.isBuffer(op)) { - if (!Script.checkPush(op)) - return false; +Script.isBadPush = function isBadPush(op) { + if (Buffer.isBuffer(op)) + return false; + if (op >= 0x01 && op <= 0x4b) return true; - } - if (op > opcodes.OP_16) - return false; if (op >= opcodes.OP_PUSHDATA1 && op <= opcodes.OP_PUSHDATA4) - return false; - return true; -}; - -Script.decode = function decode(buf) { - var code = []; - var off = 0; - var op, size; - - assert(Buffer.isBuffer(buf)); - - while (off < buf.length) { - op = buf[off++]; - if (op >= 0x01 && op <= 0x4b) { - code.push(buf.slice(off, off + op)); - off += op; - // if (off > buf.length) { - utils.hidden(code[code.length - 1], 'pushdata', { - opcode: null, - size: op - }); - } else if (op === opcodes.OP_PUSHDATA1) { - size = buf[off]; - off += 1; - code.push(buf.slice(off, off + size)); - off += size; - // if (size <= 0x4b || off > buf.length) { - utils.hidden(code[code.length - 1], 'pushdata', { - opcode: op, - size: size - }); - } else if (op === opcodes.OP_PUSHDATA2) { - size = utils.readU16(buf, off); - off += 2; - code.push(buf.slice(off, off + size)); - off += size; - // if (size <= 0xff || off > buf.length) { - utils.hidden(code[code.length - 1], 'pushdata', { - opcode: op, - size: size - }); - } else if (op === opcodes.OP_PUSHDATA4) { - size = utils.readU32(buf, off); - off += 4; - code.push(buf.slice(off, off + size)); - off += size; - // if (size <= 0xffff || off > buf.length) { - utils.hidden(code[code.length - 1], 'pushdata', { - opcode: op, - size: size - }); - } else { - code.push(op); - } - } - - return code; + return true; + return false; }; Script.decode = function decode(buf) { @@ -4164,11 +4058,14 @@ Script.decode = function decode(buf) { while (p.left()) { op = p.readU8(); if (op >= 0x01 && op <= 0x4b) { - data = p.readBytes(Math.min(p.left(), op)); - utils.hidden(data, 'pushdata', { - opcode: null, - size: op - }); + if (p.left() < op) { + code.push(op); + while (p.left()) + code.push(p.readU8()); + continue; + } + data = p.readBytes(op); + utils.hidden(data, 'op', op); code.push(data); } else if (op === opcodes.OP_PUSHDATA1) { if (p.left() < 1) { @@ -4185,11 +4082,8 @@ Script.decode = function decode(buf) { code.push(p.readU8()); continue; } - data = p.readBytes(Math.min(p.left(), size)); - utils.hidden(data, 'pushdata', { - opcode: op, - size: size - }); + data = p.readBytes(size); + utils.hidden(data, 'op', op); code.push(data); } else if (op === opcodes.OP_PUSHDATA2) { if (p.left() < 2) { @@ -4207,11 +4101,8 @@ Script.decode = function decode(buf) { code.push(p.readU8()); continue; } - data = p.readBytes(Math.min(p.left(), size)); - utils.hidden(data, 'pushdata', { - opcode: op, - size: size - }); + data = p.readBytes(size); + utils.hidden(data, 'op', op); code.push(data); } else if (op === opcodes.OP_PUSHDATA4) { if (p.left() < 4) { @@ -4231,11 +4122,8 @@ Script.decode = function decode(buf) { code.push(p.readU8()); continue; } - data = p.readBytes(Math.min(p.left(), size)); - utils.hidden(data, 'pushdata', { - opcode: op, - size: size - }); + data = p.readBytes(size); + utils.hidden(data, 'op', op); code.push(data); } else { code.push(op); @@ -4271,21 +4159,21 @@ Script.encode = function encode(code, writer) { if (Buffer.isBuffer(op)) { // Check for nonstandard pushdatas that // may have been decoded from before. - if (op.pushdata) { - if (op.pushdata.opcode === null) { - p.writeU8(op.pushdata.size); + if (op.op != null) { + if (op.op <= 0x4b) { + p.writeU8(op.length); p.writeBytes(op); - } else if (op.pushdata.opcode === opcodes.OP_PUSHDATA1) { + } else if (op.op === opcodes.OP_PUSHDATA1) { p.writeU8(opcodes.OP_PUSHDATA1); - p.writeU8(op.pushdata.size); + p.writeU8(op.length); p.writeBytes(op); - } else if (op.pushdata.opcode === opcodes.OP_PUSHDATA2) { + } else if (op.op === opcodes.OP_PUSHDATA2) { p.writeU8(opcodes.OP_PUSHDATA2); - p.writeU16(op.pushdata.size); + p.writeU16(op.length); p.writeBytes(op); - } else if (op.pushdata.opcode === opcodes.OP_PUSHDATA4) { + } else if (op.op === opcodes.OP_PUSHDATA4) { p.writeU8(opcodes.OP_PUSHDATA4); - p.writeU32(op.pushdata.size); + p.writeU32(op.length); p.writeBytes(op); } else { assert(false, 'Bad pushdata op.');