new way of handling script parsing.

This commit is contained in:
Christopher Jeffrey 2016-04-20 09:17:58 -07:00
parent 91acf2a607
commit 1d8a86838d
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD

View File

@ -1047,8 +1047,6 @@ Script.prototype.execute = function execute(stack, flags, tx, index, version) {
op = this.code[ip]; op = this.code[ip];
if (Buffer.isBuffer(op)) { if (Buffer.isBuffer(op)) {
if (!Script.checkPush(op))
throw new ScriptError('BAD_OPCODE', op, ip);
if (op.length > constants.script.MAX_PUSH) if (op.length > constants.script.MAX_PUSH)
throw new ScriptError('PUSH_SIZE', op, ip); throw new ScriptError('PUSH_SIZE', op, ip);
// Note that minimaldata is not checked // Note that minimaldata is not checked
@ -1981,7 +1979,7 @@ Script.array = function(value) {
Script.prototype.removeData = function removeData(data) { Script.prototype.removeData = function removeData(data) {
var total = 0; var total = 0;
var pushdata = data.pushdata; var op = data.op;
var sig, raw, i, a, b; var sig, raw, i, a, b;
if (!this.raw) if (!this.raw)
@ -1989,10 +1987,10 @@ Script.prototype.removeData = function removeData(data) {
// We need to reserialize // We need to reserialize
// the signature as minimaldata. // the signature as minimaldata.
delete data.pushdata; delete data.op;
sig = new Script([data]).encode(); sig = new Script([data]).encode();
if (pushdata) if (op)
utils.hidden(data, 'pushdata', pushdata); utils.hidden(data, 'op', op);
raw = this.encode(); raw = this.encode();
@ -2064,38 +2062,6 @@ Script.prototype.indexOf = function indexOf(data) {
return utils.indexOf(this.code, 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. * Check to see if a pushdata Buffer abides by minimaldata.
* @param {Buffer} value - Pushdata op from script code * @param {Buffer} value - Pushdata op from script code
@ -2105,15 +2071,13 @@ Script.checkPush = function checkPush(value) {
*/ */
Script.checkMinimal = function checkMinimal(value, flags) { Script.checkMinimal = function checkMinimal(value, flags) {
var pushdata = value.pushdata;
if (flags == null) if (flags == null)
flags = constants.flags.STANDARD_VERIFY_FLAGS; flags = constants.flags.STANDARD_VERIFY_FLAGS;
if (!(flags & constants.flags.VERIFY_MINIMALDATA)) if (!(flags & constants.flags.VERIFY_MINIMALDATA))
return true; return true;
if (!pushdata) if (value.op == null)
return true; return true;
if (value.length === 1 && value[0] >= 1 && value[0] <= 16) if (value.length === 1 && value[0] >= 1 && value[0] <= 16)
@ -2123,13 +2087,13 @@ Script.checkMinimal = function checkMinimal(value, flags) {
return false; return false;
if (value.length <= 75) if (value.length <= 75)
return pushdata.opcode == null && pushdata.size === value.length; return value.op === value.length;
if (value.length <= 255) if (value.length <= 255)
return pushdata.opcode === opcodes.OP_PUSHDATA1; return value.op === opcodes.OP_PUSHDATA1;
if (value.length <= 65535) if (value.length <= 65535)
return pushdata.opcode === opcodes.OP_PUSHDATA2; return value.op === opcodes.OP_PUSHDATA2;
return true; return true;
}; };
@ -2155,7 +2119,7 @@ Script.isCode = function isCode(buf) {
op = code[i]; op = code[i];
if (Buffer.isBuffer(op)) if (Buffer.isBuffer(op))
continue; continue;
if (op >= opcodes.OP_PUSHDATA1 && op <= opcodes.OP_PUSHDATA4) if (Script.isBadPush(op))
return false; return false;
if (constants.opcodesByVal[op] == null) if (constants.opcodesByVal[op] == null)
return false; return false;
@ -2312,7 +2276,7 @@ Script.prototype.getRedeem = function getRedeem() {
Script.getRedeem = function getRedeem(code) { Script.getRedeem = function getRedeem(code) {
var redeem = code[code.length - 1]; var redeem = code[code.length - 1];
if (!Script.isPush(redeem)) if (!Buffer.isBuffer(redeem))
return; return;
return new Script(redeem); return new Script(redeem);
@ -2662,14 +2626,11 @@ Script.prototype.isNulldata = function isNulldata() {
for (i = 1; i < this.code.length; i++) { for (i = 1; i < this.code.length; i++) {
op = this.code[i]; op = this.code[i];
if (Buffer.isBuffer(op)) { if (Buffer.isBuffer(op))
if (!Script.checkPush(op))
return false;
continue; continue;
}
if (op > opcodes.OP_16) if (op > opcodes.OP_16)
return false; return false;
if (op >= opcodes.OP_PUSHDATA1 && op <= opcodes.OP_PUSHDATA4) if (Script.isBadPush(op))
return false; return false;
} }
@ -2699,7 +2660,7 @@ Script.prototype.isCommitment = function isCommitment() {
} }
return this.code.length >= 2 return this.code.length >= 2
&& this.code[0] === opcodes.OP_RETURN && this.code[0] === opcodes.OP_RETURN
&& Script.isPush(this.code[1]) && Buffer.isBuffer(this.code[1])
&& this.code[1].length === 36 && this.code[1].length === 36
&& utils.readU32BE(this.code[1], 0) === 0xaa21a9ed; && utils.readU32BE(this.code[1], 0) === 0xaa21a9ed;
}; };
@ -2746,7 +2707,7 @@ Script.prototype.isWitnessProgram = function isWitnessProgram() {
if (typeof this.code[0] !== 'number') if (typeof this.code[0] !== 'number')
return false; return false;
if (!Script.isPush(this.code[1])) if (!Buffer.isBuffer(this.code[1]))
return false; return false;
return (this.code[0] === opcodes.OP_0 return (this.code[0] === opcodes.OP_0
@ -3044,7 +3005,7 @@ Script.isScripthashInput = function isScripthashInput(code, isWitness) {
// Last data element should be an array // Last data element should be an array
// for the redeem script. // for the redeem script.
if (!Script.isPush(raw)) if (!Buffer.isBuffer(raw))
return false; return false;
// Testing for scripthash inputs requires // Testing for scripthash inputs requires
@ -3101,9 +3062,6 @@ Script.getCoinbaseHeight = function getCoinbaseHeight(code) {
if (!Buffer.isBuffer(code[0])) if (!Buffer.isBuffer(code[0]))
return -1; return -1;
if (!Script.checkPush(code[0]))
return -1;
if (!Script.checkMinimal(code[0])) if (!Script.checkMinimal(code[0]))
return -1; return -1;
@ -3152,7 +3110,7 @@ Script.prototype.getCoinbaseData = function getCoinbaseData() {
*/ */
Script.isHash = function isHash(hash) { 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) { 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) { 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) { 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) if (flags == null)
flags = constants.flags.STANDARD_VERIFY_FLAGS; flags = constants.flags.STANDARD_VERIFY_FLAGS;
if (!Script.isPush(key)) if (!Buffer.isBuffer(key))
throw new ScriptError('BAD_OPCODE'); throw new ScriptError('BAD_OPCODE');
if (flags & constants.flags.VERIFY_STRICTENC) { if (flags & constants.flags.VERIFY_STRICTENC) {
@ -3231,7 +3189,7 @@ Script.validateKey = function validateKey(key, flags) {
*/ */
Script.isKeyEncoding = function isKeyEncoding(key) { Script.isKeyEncoding = function isKeyEncoding(key) {
if (!Script.isPush(key)) if (!Buffer.isBuffer(key))
return false; return false;
if (key.length < 33) if (key.length < 33)
@ -3266,7 +3224,7 @@ Script.validateSignature = function validateSignature(sig, flags) {
if (flags == null) if (flags == null)
flags = constants.flags.STANDARD_VERIFY_FLAGS; flags = constants.flags.STANDARD_VERIFY_FLAGS;
if (!Script.isPush(sig)) if (!Buffer.isBuffer(sig))
throw new ScriptError('BAD_OPCODE'); throw new ScriptError('BAD_OPCODE');
// Allow empty sigs // Allow empty sigs
@ -3303,7 +3261,7 @@ Script.validateSignature = function validateSignature(sig, flags) {
Script.isSignatureEncoding = function isSignatureEncoding(sig) { Script.isSignatureEncoding = function isSignatureEncoding(sig) {
var lenR, lenS; var lenR, lenS;
if (!Script.isPush(sig)) if (!Buffer.isBuffer(sig))
return false; return false;
// Format: 0x30 [total-length] 0x02 [R-length] [R] 0x02 [S-length] [S] [sighash] // 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) { Script.isHashType = function isHashType(sig) {
var type; var type;
if (!Script.isPush(sig)) if (!Buffer.isBuffer(sig))
return false; return false;
if (sig.length === 0) if (sig.length === 0)
@ -3416,7 +3374,7 @@ Script.isHashType = function isHashType(sig) {
Script.isLowDER = function isLowDER(sig) { Script.isLowDER = function isLowDER(sig) {
if (!sig.s) { if (!sig.s) {
if (!Script.isPush(sig)) if (!Buffer.isBuffer(sig))
return false; return false;
if (!Script.isSignatureEncoding(sig)) if (!Script.isSignatureEncoding(sig))
@ -3485,14 +3443,11 @@ Script.prototype.isPushOnly = function isPushOnly() {
var i, op; var i, op;
for (i = 0; i < this.code.length; i++) { for (i = 0; i < this.code.length; i++) {
op = this.code[i]; op = this.code[i];
if (Buffer.isBuffer(op)) { if (Buffer.isBuffer(op))
if (!Script.checkPush(op))
return false;
continue; continue;
}
if (op > opcodes.OP_16) if (op > opcodes.OP_16)
return false; return false;
if (op >= opcodes.OP_PUSHDATA1 && op <= opcodes.OP_PUSHDATA4) if (Script.isBadPush(op))
return false; return false;
} }
return true; return true;
@ -3513,13 +3468,10 @@ Script.prototype.getSigops = function getSigops(accurate) {
for (i = 0; i < this.code.length; i++) { for (i = 0; i < this.code.length; i++) {
op = this.code[i]; op = this.code[i];
if (Script.isPush(op)) if (Buffer.isBuffer(op))
continue; continue;
if (op >= opcodes.OP_PUSHDATA1 && op <= opcodes.OP_PUSHDATA4) if (Script.isBadPush(op))
return 0;
if (constants.opcodesByVal[op] == null)
return 0; return 0;
if (op === opcodes.OP_CHECKSIG || op === opcodes.OP_CHECKSIGVERIFY) { if (op === opcodes.OP_CHECKSIG || op === opcodes.OP_CHECKSIGVERIFY) {
@ -4085,72 +4037,14 @@ Script.fromRaw = function fromRaw(data, enc) {
* @returns {Array} Script code. * @returns {Array} Script code.
*/ */
Script.isPushOp = function isPushOp(op) { Script.isBadPush = function isBadPush(op) {
if (Buffer.isBuffer(op)) { if (Buffer.isBuffer(op))
if (!Script.checkPush(op)) return false;
return false; if (op >= 0x01 && op <= 0x4b)
return true; return true;
}
if (op > opcodes.OP_16)
return false;
if (op >= opcodes.OP_PUSHDATA1 && op <= opcodes.OP_PUSHDATA4) if (op >= opcodes.OP_PUSHDATA1 && op <= opcodes.OP_PUSHDATA4)
return false; return true;
return true; return false;
};
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;
}; };
Script.decode = function decode(buf) { Script.decode = function decode(buf) {
@ -4164,11 +4058,14 @@ Script.decode = function decode(buf) {
while (p.left()) { while (p.left()) {
op = p.readU8(); op = p.readU8();
if (op >= 0x01 && op <= 0x4b) { if (op >= 0x01 && op <= 0x4b) {
data = p.readBytes(Math.min(p.left(), op)); if (p.left() < op) {
utils.hidden(data, 'pushdata', { code.push(op);
opcode: null, while (p.left())
size: op code.push(p.readU8());
}); continue;
}
data = p.readBytes(op);
utils.hidden(data, 'op', op);
code.push(data); code.push(data);
} else if (op === opcodes.OP_PUSHDATA1) { } else if (op === opcodes.OP_PUSHDATA1) {
if (p.left() < 1) { if (p.left() < 1) {
@ -4185,11 +4082,8 @@ Script.decode = function decode(buf) {
code.push(p.readU8()); code.push(p.readU8());
continue; continue;
} }
data = p.readBytes(Math.min(p.left(), size)); data = p.readBytes(size);
utils.hidden(data, 'pushdata', { utils.hidden(data, 'op', op);
opcode: op,
size: size
});
code.push(data); code.push(data);
} else if (op === opcodes.OP_PUSHDATA2) { } else if (op === opcodes.OP_PUSHDATA2) {
if (p.left() < 2) { if (p.left() < 2) {
@ -4207,11 +4101,8 @@ Script.decode = function decode(buf) {
code.push(p.readU8()); code.push(p.readU8());
continue; continue;
} }
data = p.readBytes(Math.min(p.left(), size)); data = p.readBytes(size);
utils.hidden(data, 'pushdata', { utils.hidden(data, 'op', op);
opcode: op,
size: size
});
code.push(data); code.push(data);
} else if (op === opcodes.OP_PUSHDATA4) { } else if (op === opcodes.OP_PUSHDATA4) {
if (p.left() < 4) { if (p.left() < 4) {
@ -4231,11 +4122,8 @@ Script.decode = function decode(buf) {
code.push(p.readU8()); code.push(p.readU8());
continue; continue;
} }
data = p.readBytes(Math.min(p.left(), size)); data = p.readBytes(size);
utils.hidden(data, 'pushdata', { utils.hidden(data, 'op', op);
opcode: op,
size: size
});
code.push(data); code.push(data);
} else { } else {
code.push(op); code.push(op);
@ -4271,21 +4159,21 @@ Script.encode = function encode(code, writer) {
if (Buffer.isBuffer(op)) { if (Buffer.isBuffer(op)) {
// Check for nonstandard pushdatas that // Check for nonstandard pushdatas that
// may have been decoded from before. // may have been decoded from before.
if (op.pushdata) { if (op.op != null) {
if (op.pushdata.opcode === null) { if (op.op <= 0x4b) {
p.writeU8(op.pushdata.size); p.writeU8(op.length);
p.writeBytes(op); 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(opcodes.OP_PUSHDATA1);
p.writeU8(op.pushdata.size); p.writeU8(op.length);
p.writeBytes(op); p.writeBytes(op);
} else if (op.pushdata.opcode === opcodes.OP_PUSHDATA2) { } else if (op.op === opcodes.OP_PUSHDATA2) {
p.writeU8(opcodes.OP_PUSHDATA2); p.writeU8(opcodes.OP_PUSHDATA2);
p.writeU16(op.pushdata.size); p.writeU16(op.length);
p.writeBytes(op); p.writeBytes(op);
} else if (op.pushdata.opcode === opcodes.OP_PUSHDATA4) { } else if (op.op === opcodes.OP_PUSHDATA4) {
p.writeU8(opcodes.OP_PUSHDATA4); p.writeU8(opcodes.OP_PUSHDATA4);
p.writeU32(op.pushdata.size); p.writeU32(op.length);
p.writeBytes(op); p.writeBytes(op);
} else { } else {
assert(false, 'Bad pushdata op.'); assert(false, 'Bad pushdata op.');