diff --git a/lib/script/opcode.js b/lib/script/opcode.js index 144ba0c2..696a0f69 100644 --- a/lib/script/opcode.js +++ b/lib/script/opcode.js @@ -174,7 +174,7 @@ Opcode.prototype.fromRaw = function fromRaw(data) { */ Opcode.fromReader = function fromReader(br) { - return new Opcode().fromReader(br); + return new Opcode(0, null).fromReader(br); }; /** @@ -184,7 +184,7 @@ Opcode.fromReader = function fromReader(br) { */ Opcode.fromRaw = function fromRaw(data) { - return new Opcode().fromRaw(data); + return new Opcode(0, null).fromRaw(data); }; /** @@ -194,7 +194,7 @@ Opcode.fromRaw = function fromRaw(data) { */ Opcode.fromOp = function fromOp(op) { - return new Opcode(op); + return new Opcode(op, null); }; /** @@ -206,14 +206,14 @@ Opcode.fromOp = function fromOp(op) { Opcode.fromData = function fromData(data) { if (data.length === 0) - return new Opcode(opcodes.OP_0); + return Opcode.fromOp(opcodes.OP_0); if (data.length === 1) { if (data[0] >= 1 && data[0] <= 16) - return new Opcode(data[0] + 0x50); + return Opcode.fromOp(data[0] + 0x50); if (data[0] === 0x81) - return new Opcode(opcodes.OP_1NEGATE); + return Opcode.fromOp(opcodes.OP_1NEGATE); } return Opcode.fromPush(data); @@ -261,7 +261,7 @@ Opcode.fromNumber = function fromNumber(num) { Opcode.fromSmall = function fromSmall(num) { assert(util.isNumber(num) && num >= 0 && num <= 16); - return new Opcode(num === 0 ? 0 : num + 0x50); + return Opcode.fromOp(num === 0 ? 0 : num + 0x50); }; /** @@ -302,6 +302,32 @@ Opcode.from = function from(data) { assert(false, 'Bad data for opcode.'); }; +/** + * Instantiate a pushdata opcode from symbolic name. + * @example + * Opcode.fromSymbol('checksequenceverify') + * @param {String} name + * @returns {Opcode} + */ + +Opcode.fromSymbol = function fromSymbol(name) { + var op; + + assert(typeof name === 'string'); + assert(name.length > 0); + + if (!util.isUpperCase(name)) + name = name.toUpperCase(); + + if (!util.startsWith(name, 'OP_')) + name = 'OP_' + name; + + op = constants.opcodes[name]; + assert(op != null, 'Unknown opcode.'); + + return Opcode.fromOp(op); +}; + /** * Test whether an object an Opcode. * @param {Object} obj diff --git a/lib/script/script.js b/lib/script/script.js index 34f8d08d..f11558c9 100644 --- a/lib/script/script.js +++ b/lib/script/script.js @@ -83,7 +83,7 @@ Script.prototype.fromOptions = function fromOptions(options) { if (options.code) { if (!options.raw) - return this.fromArray(options.code); + return this.fromCode(options.code); assert(Array.isArray(options.code)); this.code = options.code; } @@ -124,18 +124,44 @@ Script.prototype.toArray = function toArray() { * Inject properties from an array of * of buffers and numbers. * @private + * @param {Array} code + * @returns {Script} */ Script.prototype.fromArray = function fromArray(code) { + var i, op; + assert(Array.isArray(code)); - this.code = Script.parseArray(code); + + if (code.length === 0) + return this; + + if (code[0] instanceof Opcode) + return this.fromCode(code); + + for (i = 0; i < code.length; i++) { + op = code[i]; + if (Buffer.isBuffer(op)) { + this.code.push(Opcode.fromData(op)); + continue; + } + if (typeof op === 'string') { + this.code.push(Opcode.fromSymbol(op)); + continue; + } + assert(typeof op === 'number'); + this.code.push(Opcode.fromOp(op)); + } + this.compile(); + return this; }; /** * Instantiate script from an array * of buffers and numbers. + * @param {Array} code * @returns {Script} */ @@ -143,6 +169,45 @@ Script.fromArray = function fromArray(code) { return new Script().fromArray(code); }; +/** + * Return an array of opcodes. + * @returns {Opcode[]} + */ + +Script.prototype.toCode = function toCode() { + return this.code.slice(); +}; + +/** + * Inject properties from an array of opcodes. + * @param {Opcode[]} code + * @private + */ + +Script.prototype.fromCode = function fromCode(code) { + assert(Array.isArray(code)); + + if (code.length === 0) + return this; + + assert(code[0] instanceof Opcode); + + this.code = code; + this.compile(); + + return this; +}; + +/** + * Instantiate script from an array of opcodes. + * @param {Opcode[]} code + * @returns {Script} + */ + +Script.fromCode = function fromCode(code) { + return new Script().fromCode(code); +}; + /** * Clone the script. * @returns {Script} Cloned script. @@ -2210,6 +2275,9 @@ Script.prototype.isWitnessMasthash = function isWitnessMasthash() { */ Script.prototype.isUnspendable = function isUnspendable() { + if (this.raw.length > constants.script.MAX_SIZE) + return true; + return this.raw.length > 0 && this.raw[0] === opcodes.OP_RETURN; }; @@ -3018,9 +3086,12 @@ Script.prototype.fromString = function fromString(code) { for (i = 0; i < code.length; i++) { op = code[i]; + symbol = op; - symbol = op.toUpperCase(); - if (symbol.indexOf('OP_') !== 0) + if (!util.isUpperCase(symbol)) + symbol = symbol.toUpperCase(); + + if (!util.startsWith(symbol, 'OP_')) symbol = 'OP_' + symbol; if (opcodes[symbol] == null) { @@ -3507,38 +3578,6 @@ Script.fromRaw = function fromRaw(data, enc) { return new Script().fromRaw(data); }; -/** - * Convert an array of Buffers and - * Numbers into an array of Opcodes. - * @param {Array} code - * @returns {Opcode[]} - */ - -Script.parseArray = function parseArray(code) { - var out = []; - var i, op; - - assert(Array.isArray(code)); - - if (code.length === 0) - return code; - - if (code[0] instanceof Opcode) - return code; - - for (i = 0; i < code.length; i++) { - op = code[i]; - if (Buffer.isBuffer(op)) { - out.push(Opcode.fromData(op)); - continue; - } - assert(typeof op === 'number'); - out.push(new Opcode(op)); - } - - return out; -}; - /** * Calculate the size (including * the opcode) of a pushdata. diff --git a/lib/utils/util.js b/lib/utils/util.js index 2109aa7e..9aaaee98 100644 --- a/lib/utils/util.js +++ b/lib/utils/util.js @@ -1034,6 +1034,35 @@ util.fastProp = function fastProp(obj) { ({ __proto__: obj }); }; +/** + * Quick test to see if a string is uppercase. + * @param {String} str + * @returns {Boolean} + */ + +util.isUpperCase = function isUpperCase(str) { + if (str.length === 0) + return false; + return (str.charCodeAt(0) & 32) === 0; +}; + +/** + * Test to see if a string starts with a prefix. + * @param {String} str + * @param {String} prefix + * @returns {Boolean} + */ + +util.startsWith = function startsWiths(str, prefix) { + return str.startsWith(prefix); +}; + +if (!''.startsWith) { + util.startsWith = function startsWith(str, prefix) { + return str.indexOf(prefix) === 0; + }; +} + /** * Promisify a function. * @param {Function} func