diff --git a/lib/bcoin/script.js b/lib/bcoin/script.js index 34bfd813..1ae9f766 100644 --- a/lib/bcoin/script.js +++ b/lib/bcoin/script.js @@ -2103,20 +2103,20 @@ Script.checkMinimal = function checkMinimal(value, flags) { /** * Test a buffer to see if it is valid script code (no non-existent opcodes). - * @param {Buffer} buf + * @param {Buffer} raw * @returns {Boolean} */ -Script.isCode = function isCode(buf) { +Script.isCode = function isCode(raw) { var i, op, code; - if (!buf) + if (!raw) return false; - if (!Buffer.isBuffer(buf)) + if (!Buffer.isBuffer(raw)) return false; - code = Script.decode(buf); + code = Script.decode(raw); for (i = 0; i < code.length; i++) { op = code[i]; @@ -3475,7 +3475,7 @@ Script.prototype.getSigops = function getSigops(accurate) { continue; if (Script.isBadPush(op)) - return 0; + break; if (op === opcodes.OP_CHECKSIG || op === opcodes.OP_CHECKSIGVERIFY) { total++; @@ -4019,34 +4019,45 @@ Script.fromRaw = function fromRaw(data, enc) { * Decode a serialized script into script code. * Note that the serialized script must _not_ * include the varint size before it. Note that - * this will apply hidden `pushdata` properties + * this will apply hidden `opcode` properties * to each Buffer if the buffer was created from - * a non-standard pushdata. + * a non-minimal pushdata. * - * This function does not do bounds checking - * on buffers because some jackass could do a - * direct push of 30 bytes with only 20 bytes - * after it. That script would be perfectly - * fine _until_ it is executed. There are - * output scripts on the blockchain that can - * never be redeemed due to this, but they are - * in valid blocks, therefore we cannot fail - * parsing them. + * BCoin parses scripts "differently" because it + * parses them _before they're executed_. This + * lends itself to some interesting edge cases. * + * If bitcoind comes across a bad push, it + * will return an invalid opcode. The problem + * is bitcoind parses scripts _as_ they are + * executing, which can be slow for us because + * now every function that needs to test the + * script needs to parse the raw data. It's + * also impossible to read a script + * _backwards_ making testing for things like + * multisig outputs even more difficult. + * + * If this function comes accross a bad push + * in its parsing, it simply will _not + * consider the pushdata to be a pushdata_ + * but just another opcode in the code array + * (all of the data after the pushdata op + * will also be considered opcodes rather + * than data). + * Also note that this function uses reference * Buffer slices. Larger buffer slices should * _never_ be passed in here. - * @param {Buffer} buf - Serialized script. + * @param {Buffer} raw - Serialized script. * @returns {Array} Script code. */ -Script.decode = function decode(buf) { +Script.decode = function decode(raw) { + var p = new BufferReader(raw, true); var code = []; - var p = new BufferReader(buf, true); - var off = 0; - var op, size; + var op, size, data; - assert(Buffer.isBuffer(buf)); + assert(Buffer.isBuffer(raw)); while (p.left()) { op = p.readU8(); @@ -4225,26 +4236,10 @@ Script.encode = function encode(code, writer) { * not enough size bytes after a PUSHDATA, * or not enough data after the size. * - * If bitcoind comes across a bad push, it - * will return an invalid opcode. The problem - * is bitcoind parses scripts _as_ they are - * executing, which can be slow for us because - * now every function that needs to test the - * script needs to parse the raw data. It's - * also impossible to read a script - * _backwards_ making testing for things like - * multisig outputs even more difficult. - * - * If BCoin comes accross a bad push in its - * initial parsing, it simply will _not - * consider the pushdata to be a pushdata_ - * but just another opcode in the code array - * (all of the data after the pushdata op - * will also be considered opcodes rather - * than data). This function checks to see - * if an op is a direct push, or PUSHDATA1 - * to PUSHDATA4 -- these opcodes cannot - * exist in the code array of valid parsed + * This function checks to see if an op + * is a direct push, or PUSHDATA1 to + * PUSHDATA4 -- these opcodes cannot exist + * in the code array of valid parsed * scripts. * @param {Number|Buffer} op * @returns {Boolean} diff --git a/lib/bcoin/utils.js b/lib/bcoin/utils.js index 6a6d31ef..f01b7d9c 100644 --- a/lib/bcoin/utils.js +++ b/lib/bcoin/utils.js @@ -1042,23 +1042,6 @@ utils.merge = function merge(target) { return target; }; -/** - * Set a non-enumerable property. - * @param {Object} obj - * @param {String} prop - Property name. - * @param value - */ - -utils.hidden = function hidden(obj, prop, value) { - Object.defineProperty(obj, prop, { - value: value, - enumerable: false, - configurable: true, - writable: true - }); - return obj; -}; - /** * Sort public keys lexicographically. * @param {Buffer[]} keys diff --git a/test/script-test.js b/test/script-test.js index b9e80dd0..c4d974cf 100644 --- a/test/script-test.js +++ b/test/script-test.js @@ -147,6 +147,8 @@ describe('Script', function() { 'OP_1 OP_DUP OP_PUSHDATA1' ); assert(utils.equals(s.raw, new Buffer('51764c', 'hex'))); + delete s.raw; + assert(utils.equals(s.encode(), new Buffer('51764c', 'hex'))); try { s.execute(stack); } catch (e) { @@ -158,6 +160,8 @@ describe('Script', function() { 'OP_1 OP_DUP OP_PUSHDATA2 0x01' ); assert(utils.equals(s.raw, new Buffer('51764d01', 'hex'))); + delete s.raw; + assert(utils.equals(s.encode(), new Buffer('51764d01', 'hex'))); err = null; try { s.execute(stack); @@ -170,6 +174,36 @@ describe('Script', function() { 'OP_1 OP_DUP OP_PUSHDATA4 0x0001' ); assert(utils.equals(s.raw, new Buffer('51764e0001', 'hex'))); + delete s.raw; + assert(utils.equals(s.encode(), new Buffer('51764e0001', 'hex'))); + err = null; + try { + s.execute(stack); + } catch (e) { + err = e; + } + assert(err); + assert(err.code === 'BAD_OPCODE'); + var s = bcoin.script.fromTestString( + 'OP_1 OP_DUP OP_PUSHDATA1 0x02 0x01' + ); + assert(utils.equals(s.raw, new Buffer('51764c0201', 'hex'))); + delete s.raw; + assert(utils.equals(s.encode(), new Buffer('51764c0201', 'hex'))); + err = null; + try { + s.execute(stack); + } catch (e) { + err = e; + } + assert(err); + assert(err.code === 'BAD_OPCODE'); + var s = bcoin.script.fromTestString( + 'OP_1 OP_DUP OP_PUSHDATA2 0x0200 0x01' + ); + assert(utils.equals(s.raw, new Buffer('51764d020001', 'hex'))); + delete s.raw; + assert(utils.equals(s.encode(), new Buffer('51764d020001', 'hex'))); err = null; try { s.execute(stack);