From 3a18052f877c9eb213622b1972a5b6a2825868d8 Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Tue, 14 Jun 2016 18:13:51 -0700 Subject: [PATCH] script work. --- lib/bcoin/miner.js | 10 +- lib/bcoin/mtx.js | 160 +++++++++++++---------------- lib/bcoin/script.js | 242 ++++++++++++++++++++++++++++++++++---------- 3 files changed, 269 insertions(+), 143 deletions(-) diff --git a/lib/bcoin/miner.js b/lib/bcoin/miner.js index 4be398ed..c2aa07e2 100644 --- a/lib/bcoin/miner.js +++ b/lib/bcoin/miner.js @@ -373,7 +373,7 @@ function MinerBlock(options) { index: 0xffffffff }, coin: null, - script: new bcoin.script([ + script: bcoin.script.fromArray([ // Height (required in v2+ blocks) bcoin.script.array(this.height), // extraNonce - incremented when @@ -442,9 +442,10 @@ utils.inherits(MinerBlock, EventEmitter); */ MinerBlock.prototype.updateCommitment = function updateCommitment() { + var output = this.coinbase.outputs[1]; var hash = this.block.getCommitmentHash(); var commit = bcoin.script.createCommitment(hash, this.coinbaseFlags); - this.coinbase.outputs[1].script = commit; + output.script = commit; }; /** @@ -453,9 +454,10 @@ MinerBlock.prototype.updateCommitment = function updateCommitment() { MinerBlock.prototype.updateCoinbase = function updateCoinbase() { var input = this.coinbase.inputs[0]; - input.script.code[1] = bcoin.opcode.fromNumber(this.extraNonce); + var output = this.coinbase.outputs[0]; + input.script.set(1, this.extraNonce); input.script.compile(); - this.coinbase.outputs[0].value = this.block.getReward(this.network); + output.value = this.block.getReward(this.network); }; /** diff --git a/lib/bcoin/mtx.js b/lib/bcoin/mtx.js index 71a16123..e9da57cf 100644 --- a/lib/bcoin/mtx.js +++ b/lib/bcoin/mtx.js @@ -161,8 +161,7 @@ MTX.prototype.addInput = function addInput(options, index) { */ MTX.prototype.scriptInput = function scriptInput(index, addr) { - var input, prev, n, i, redeemScript, witnessScript, vector, dummy; - var inputScript, witnessItems; + var input, prev, n, i, vector, redeemScript, witnessScript; if (typeof index !== 'number') index = this.inputs.indexOf(index); @@ -174,13 +173,10 @@ MTX.prototype.scriptInput = function scriptInput(index, addr) { // We should have previous outputs by now. assert(input.coin, 'Coins are not available for scripting.'); - inputScript = input.script.toArray(); - witnessItems = input.witness.toArray(); - // Optimization: Don't bother with any below // calculation if the output is already templated. // Just say this is "our" output. - if (inputScript.length || witnessItems.length) + if (input.script.length !== 0 || input.witness.length !== 0) return true; // Optimization: test output against the @@ -197,11 +193,10 @@ MTX.prototype.scriptInput = function scriptInput(index, addr) { // with segwit: figuring out where the redeem script and witness // redeem scripts go. if (prev.isScripthash()) { - if (addr.program && utils.equal(prev.code[1].data, addr.programHash)) { + if (addr.program && utils.equal(prev.get(1), addr.programHash)) { // Witness program nested in regular P2SH. redeemScript = addr.program.encode(); - vector = witnessItems; - dummy = new Buffer([]); + vector = input.witness; if (addr.program.isWitnessScripthash()) { // P2WSH nested within pay-to-scripthash // (it had to be this complicated, didn't it?) @@ -213,117 +208,114 @@ MTX.prototype.scriptInput = function scriptInput(index, addr) { } else { assert(false, 'Unknown program.'); } - } else if (addr.script && utils.equal(prev.code[1].data, addr.scriptHash160)) { + } else if (addr.script && utils.equal(prev.get(1), addr.scriptHash160)) { // Regular P2SH. redeemScript = addr.script.encode(); - vector = inputScript; + vector = input.script; prev = addr.script; - dummy = opcodes.OP_0; } else { return false; } } else if (prev.isWitnessProgram()) { // Witness program. - vector = witnessItems; - dummy = new Buffer([]); + vector = input.witness; if (prev.isWitnessScripthash()) { // Bare P2WSH. - if (!addr.script || !utils.equal(prev.code[1].data, addr.scriptHash256)) + if (!addr.script || !utils.equal(prev.get(1), addr.scriptHash256)) return false; witnessScript = addr.script.encode(); prev = addr.script; } else if (prev.isWitnessPubkeyhash()) { // Bare P2WPKH. - if (!utils.equal(prev.code[1].data, addr.keyHash)) + if (!utils.equal(prev.get(1), addr.keyHash)) return false; - prev = Script.createPubkeyhash(prev.code[1].data); + prev = Script.createPubkeyhash(prev.get(1)); } else { // Bare... who knows? return false; } } else { // Wow, a normal output! Praise be to Jengus and Gord. - vector = inputScript; - dummy = opcodes.OP_0; + vector = input.script; } if (prev.isPubkey()) { // P2PK - if (!utils.equal(prev.code[0].data, addr.publicKey)) + if (!utils.equal(prev.get(1), addr.publicKey)) return false; // Already has a script template (at least) - if (vector.length) + if (vector.length !== 0) return true; - vector[0] = dummy; + vector.set(0, opcodes.OP_0); } else if (prev.isPubkeyhash()) { // P2PKH - if (!utils.equal(prev.code[2].data, addr.keyHash)) + if (!utils.equal(prev.get(2), addr.keyHash)) return false; // Already has a script template (at least) - if (vector.length) + if (vector.length !== 0) return true; - vector[0] = dummy; - vector[1] = addr.publicKey; + vector.set(0, opcodes.OP_0); + vector.set(1, addr.publicKey); } else if (prev.isMultisig()) { // Multisig if (prev.indexOf(addr.publicKey) === -1) return false; // Already has a script template (at least) - if (vector.length) + if (vector.length !== 0) return true; // Technically we should create m signature slots, // but we create n signature slots so we can order // the signatures properly. - vector[0] = dummy; + vector.set(0, opcodes.OP_0); // Grab `n` value (number of keys). - n = prev.getSmall(-2); + n = prev.getSmall(prev.length - 2); // Fill script with `n` signature slots. for (i = 0; i < n; i++) - vector[i + 1] = dummy; + vector.set(i + 1, opcodes.OP_0); } else { if (prev.indexOf(addr.publicKey) === -1) return false; // Already has a script template (at least) - if (vector.length) + if (vector.length !== 0) return true; // Likely a non-standard scripthash multisig // input. Determine n value by counting keys. // Also, only allow nonstandard types for // scripthash. - vector[0] = dummy; + vector.set(0, opcodes.OP_0); // Fill script with `n` signature slots. - for (i = 0; i < prev.code.length; i++) { - if (Script.isKey(prev.code[i].data)) - vector[i + 1] = dummy; + for (i = 0; i < prev.length; i++) { + if (Script.isKey(prev.get(i))) + vector.set(i + 1, opcodes.OP_0); } } // P2SH requires the redeem // script after signatures. if (redeemScript) - inputScript.push(redeemScript); + input.script.push(redeemScript); // P2WSH requires the witness // script after signatures. if (witnessScript) - witnessItems.push(witnessScript); + input.witness.push(witnessScript); - input.script = bcoin.script.fromArray(inputScript); - input.witness = bcoin.witness.fromArray(witnessItems); + input.script.compile(); + input.witness.compile(); return true; }; @@ -372,7 +364,7 @@ MTX.prototype.createSignature = function createSignature(index, prev, key, type, MTX.prototype.signInput = function signInput(index, addr, key, type) { var input, prev, signature, keyIndex, signatures, i; - var len, m, n, keys, vector, dummy, version, inputScript, witnessItems; + var len, m, n, keys, vector, version; if (typeof index !== 'number') index = this.inputs.indexOf(index); @@ -384,15 +376,11 @@ MTX.prototype.signInput = function signInput(index, addr, key, type) { // We should have previous outputs by now. assert(input.coin, 'Coins are not available for signing.'); - inputScript = input.script.toArray(); - witnessItems = input.witness.toArray(); - // Get the previous output's script prev = input.coin.script; - vector = inputScript; + vector = input.script; len = vector.length; - dummy = opcodes.OP_0; version = 0; // We need to grab the redeem script when @@ -410,15 +398,13 @@ MTX.prototype.signInput = function signInput(index, addr, key, type) { // pushes onto the stack). if (prev.isWitnessScripthash()) { prev = input.witness.getRedeem(); - vector = witnessItems; + vector = input.witness; len = vector.length - 1; - dummy = new Buffer([]); version = 1; } else if (prev.isWitnessPubkeyhash()) { - prev = Script.createPubkeyhash(prev.code[1].data); - vector = witnessItems; + prev = Script.createPubkeyhash(prev.get(1)); + vector = input.witness; len = vector.length; - dummy = new Buffer([]); version = 1; } @@ -428,17 +414,15 @@ MTX.prototype.signInput = function signInput(index, addr, key, type) { // P2PK if (prev.isPubkey()) { // Already signed. - if (Script.isSignature(vector[0])) + if (Script.isSignature(vector.get(0))) return true; // Make sure the pubkey is ours. - if (!utils.equal(addr.publicKey, prev.code[0].data)) + if (!utils.equal(addr.publicKey, prev.get(0))) return false; - vector[0] = signature; - - input.script = bcoin.script.fromArray(inputScript); - input.witness = bcoin.witness.fromArray(witnessItems); + vector.set(0, signature); + vector.compile(); return true; } @@ -446,17 +430,15 @@ MTX.prototype.signInput = function signInput(index, addr, key, type) { // P2PKH if (prev.isPubkeyhash()) { // Already signed. - if (Script.isSignature(vector[0])) + if (Script.isSignature(vector.get(0))) return true; // Make sure the pubkey hash is ours. - if (!utils.equal(addr.keyHash, prev.code[2].data)) + if (!utils.equal(addr.keyHash, prev.get(2))) return false; - vector[0] = signature; - - input.script = bcoin.script.fromArray(inputScript); - input.witness = bcoin.witness.fromArray(witnessItems); + vector.set(0, signature); + vector.compile(); return true; } @@ -465,13 +447,16 @@ MTX.prototype.signInput = function signInput(index, addr, key, type) { if (prev.isMultisig()) { // Grab the redeem script's keys to figure // out where our key should go. - keys = prev.toArray().slice(1, -2); + keys = []; + + for (i = 1; i < prev.length - 2; i++) + keys.push(prev.get(i)); // Grab `m` value (number of sigs required). m = prev.getSmall(0); // Grab `n` value (number of keys). - n = prev.getSmall(-2); + n = prev.getSmall(prev.length - 2); } else { // Only allow non-standard signing for // scripthash. @@ -480,9 +465,9 @@ MTX.prototype.signInput = function signInput(index, addr, key, type) { keys = []; - for (i = 0; i < prev.code.length; i++) { - if (Script.isKey(prev.code[i].data)) - keys.push(prev.code[i].data); + for (i = 0; i < prev.length; i++) { + if (Script.isKey(prev.get(i))) + keys.push(prev.get(i)); } // We don't know what m is, so @@ -498,7 +483,7 @@ MTX.prototype.signInput = function signInput(index, addr, key, type) { // Count the number of current signatures. signatures = 0; for (i = 1; i < len; i++) { - if (Script.isSignature(vector[i])) + if (Script.isSignature(vector.get(i))) signatures++; } @@ -511,7 +496,7 @@ MTX.prototype.signInput = function signInput(index, addr, key, type) { // or by `m`. Add some signature slots for // us to use. while (len - 1 < n) { - vector.splice(len, 0, dummy); + vector.insert(len, opcodes.OP_0); len++; } @@ -534,8 +519,8 @@ MTX.prototype.signInput = function signInput(index, addr, key, type) { // and increment the total number of // signatures. if (keyIndex < len && signatures < m) { - if (Script.isZero(vector[keyIndex])) { - vector[keyIndex] = signature; + if (vector.getSmall(keyIndex) === 0) { + vector.set(keyIndex, signature); signatures++; } } @@ -544,8 +529,8 @@ MTX.prototype.signInput = function signInput(index, addr, key, type) { if (signatures >= m) { // Remove empty slots left over. for (i = len - 1; i >= 1; i--) { - if (Script.isZero(vector[i])) { - vector.splice(i, 1); + if (vector.getSmall(i) === 0) { + vector.remove(i); len--; } } @@ -555,7 +540,7 @@ MTX.prototype.signInput = function signInput(index, addr, key, type) { // with implementations that potentially handle // signature slots differently. while (signatures > m) { - vector.splice(len - 1, 1); + vector.remove(len - 1); signatures--; len--; } @@ -565,8 +550,7 @@ MTX.prototype.signInput = function signInput(index, addr, key, type) { assert(len - 1 === m); } - input.script = bcoin.script.fromArray(inputScript); - input.witness = bcoin.witness.fromArray(witnessItems); + vector.compile(); return signatures === m; }; @@ -591,7 +575,7 @@ MTX.prototype.isSigned = function isSigned() { prev = input.coin.script; // Script length, needed for multisig - vector = input.script.toArray(); + vector = input.script; len = vector.length; // We need to grab the redeem script when @@ -606,19 +590,19 @@ MTX.prototype.isSigned = function isSigned() { // and potentially alter the length. if (prev.isWitnessScripthash()) { prev = input.witness.getRedeem(); - vector = input.witness.toArray(); + vector = input.witness; len = vector.length - 1; } else if (prev.isWitnessPubkeyhash()) { - prev = Script.createPubkeyhash(prev.code[1].data); - vector = input.witness.toArray(); + prev = Script.createPubkeyhash(prev.get(1)); + vector = input.witness; len = vector.length; } if (prev.isPubkey()) { - if (!Script.isSignature(vector[0])) + if (!Script.isSignature(vector.get(0))) return false; } else if (prev.isPubkeyhash()) { - if (!Script.isSignature(vector[0])) + if (!Script.isSignature(vector.get(0))) return false; } else if (prev.isMultisig()) { // Grab `m` value (number of required sigs). @@ -626,7 +610,7 @@ MTX.prototype.isSigned = function isSigned() { // Ensure all members are signatures. for (j = 1; j < len; j++) { - if (!Script.isSignature(vector[j])) + if (!Script.isSignature(vector.get(j))) return false; } @@ -839,7 +823,7 @@ MTX.prototype.maxSize = function maxSize(options, force) { // here since it will be ignored by // the isMultisig clause. // OP_PUSHDATA2 [redeem] - redeem = getRedeem(input.script, prev.code[1].data); + redeem = getRedeem(input.script, prev.get(1)); if (redeem) { prev = redeem; sz = prev.getSize(); @@ -870,7 +854,7 @@ MTX.prototype.maxSize = function maxSize(options, force) { hadWitness = true; if (prev.isWitnessScripthash()) { - redeem = getRedeem(input.witness, prev.code[1].data); + redeem = getRedeem(input.witness, prev.get(1)); if (redeem) { prev = redeem; sz = prev.getSize(); @@ -878,7 +862,7 @@ MTX.prototype.maxSize = function maxSize(options, force) { size += sz; } } else if (prev.isWitnessPubkeyhash()) { - prev = Script.createPubkeyhash(prev.code[1].data); + prev = Script.createPubkeyhash(prev.get(1)); } } @@ -930,8 +914,8 @@ MTX.prototype.maxSize = function maxSize(options, force) { size += 1; } else { // OP_PUSHDATA0 [signature] - for (j = 0; j < prev.code.length; j++) { - if (Script.isKey(prev.code[j].data)) + for (j = 0; j < prev.length; j++) { + if (Script.isKey(prev.get(j))) size += 1 + 73; } } diff --git a/lib/bcoin/script.js b/lib/bcoin/script.js index 02425511..c832f747 100644 --- a/lib/bcoin/script.js +++ b/lib/bcoin/script.js @@ -28,6 +28,7 @@ var ScriptError = bcoin.errors.ScriptError; * stack items or raw witness buffer. * @property {Buffer[]} items * @property {Script?} redeem + * @property {Number} length */ function Witness(items) { @@ -253,35 +254,98 @@ Witness.prototype.toRaw = function toRaw(enc) { return data; }; +/** + * Unshift an item onto the witness vector. + * @param {Number|String|Buffer|BN} data + * @returns {Number} + */ + Witness.prototype.unshift = function unshift(data) { return this.items.unshift(Witness.encodeItem(data)); }; +/** + * Push an item onto the witness vector. + * @param {Number|String|Buffer|BN} data + * @returns {Number} + */ + Witness.prototype.push = function push(data) { return this.items.push(Witness.encodeItem(data)); }; +/** + * Shift an item off the witness vector. + * @returns {Buffer} + */ + Witness.prototype.shift = function shift() { return this.items.shift(); }; +/** + * Shift an item off the witness vector. + * @returns {Buffer} + */ + Witness.prototype.pop = function push(data) { return this.items.pop(); }; +/** + * Remove an item from the witness vector. + * @param {Number} index + * @returns {Buffer} + */ + Witness.prototype.remove = function remove(i) { return this.items.splice(i, 1)[0]; }; +/** + * Insert an item into the witness vector. + * @param {Number} index + * @param {Number|String|Buffer|BN} data + */ + Witness.prototype.insert = function insert(i, data) { assert(i <= this.items.length, 'Index out of bounds.'); this.items.splice(i, 0, Witness.encodeItem(data))[0]; }; +/** + * Get an item from the witness vector. + * @param {Number} index + * @returns {Buffer} + */ + Witness.prototype.get = function get(i) { return this.items[i]; }; +/** + * Get a small int (0-16) from the witness vector. + * @param {Number} index + * @returns {Number} `-1` on non-existent. + */ + +Witness.prototype.getSmall = function getSmall(i) { + var item = this.items[i]; + if (!item || item.length > 1) + return -1; + if (item.length === 0) + return 0; + if (!(item[0] >= 1 && item[1] <= 16)) + return -1; + return item[0]; +}; + +/** + * Get a number from the witness vector. + * @param {Number} index + * @returns {BN} + */ + Witness.prototype.getNumber = function getNumber(i) { var item = this.items[i]; if (!item || item.length > 5) @@ -289,6 +353,12 @@ Witness.prototype.getNumber = function getNumber(i) { return Script.num(item, constants.flags.VERIFY_NONE, 5); }; +/** + * Get a string from the witness vector. + * @param {Number} index + * @returns {String} + */ + Witness.prototype.getString = function getString(i) { var item = this.items[i]; if (!item) @@ -296,35 +366,54 @@ Witness.prototype.getString = function getString(i) { return item.toString('utf8'); }; +/** + * Set an item in the witness vector. + * @param {Number} index + * @param {Number|String|Buffer|BN} data + */ + Witness.prototype.set = function set(i, data) { assert(i <= this.items.length, 'Index out of bounds.'); this.items[i] = Witness.encodeItem(data); }; Witness.prototype.__defineGetter__('length', function() { - return this.code.length; + return this.items.length; }); Witness.prototype.__defineSetter__('length', function(len) { - return this.code.length = len; + return this.items.length = len; }); +/** + * Encode a witness item. + * @param {Number|String|Buffer|BN} data + * @returns {Buffer} + */ + Witness.encodeItem = function encodeItem(data) { + if (data instanceof Opcode) + data = data.data || data.value; + if (typeof data === 'number') { if (data === opcodes.OP_1NEGATE) - data = -1; - else if (data === opcodes.OP_0) - data = 0; - else if (data >= opcodes.OP_1 && data <= opcodes.OP_16) - data -= 0x50; - else - throw new Error('Non-push opcode in witness.'); - return Script.array(data); + return STACK_NEGATE; + + if (data === opcodes.OP_0) + return STACK_FALSE; + + if (data >= opcodes.OP_1 && data <= opcodes.OP_16) + return new Buffer([data - 0x50]); + + throw new Error('Non-push opcode in witness.'); } + if (bn.isBN(data)) return Script.array(data); + if (typeof data === 'string') return new Buffer(data, 'utf8'); + return data; }; @@ -913,6 +1002,7 @@ Stack.isStack = function isStack(obj) { * @property {Array} code - Script code. * @property {Buffer?} raw - Serialized script. * @property {Script?} redeem - Redeem script. + * @property {Number} length */ function Script(raw) { @@ -2204,7 +2294,7 @@ Script.isCode = function isCode(raw) { Script.createPubkey = function createPubkey(key) { assert(key.length >= 33); - return new Script([key, opcodes.OP_CHECKSIG]); + return Script.fromArray([key, opcodes.OP_CHECKSIG]); }; /** @@ -2215,7 +2305,7 @@ Script.createPubkey = function createPubkey(key) { Script.createPubkeyhash = function createPubkeyhash(hash) { assert(hash.length === 20); - return new Script([ + return Script.fromArray([ opcodes.OP_DUP, opcodes.OP_HASH160, hash, @@ -2250,7 +2340,7 @@ Script.createMultisig = function createMultisig(keys, m, n) { code.push(n + 0x50); code.push(opcodes.OP_CHECKMULTISIG); - return new Script(code); + return Script.fromArray(code); }; /** @@ -2261,7 +2351,7 @@ Script.createMultisig = function createMultisig(keys, m, n) { Script.createScripthash = function createScripthash(hash) { assert(hash.length === 20); - return new Script([ + return Script.fromArray([ opcodes.OP_HASH160, hash, opcodes.OP_EQUAL @@ -2277,7 +2367,7 @@ Script.createScripthash = function createScripthash(hash) { Script.createNulldata = function createNulldata(flags) { assert(Buffer.isBuffer(flags)); assert(flags.length <= constants.script.MAX_OP_RETURN, 'Nulldata too large.'); - return new Script([ + return Script.fromArray([ opcodes.OP_RETURN, flags ]); @@ -2293,13 +2383,13 @@ Script.createNulldata = function createNulldata(flags) { Script.createWitnessProgram = function createWitnessProgram(version, data) { assert(typeof version === 'number' && version >= 0 && version <= 16); assert(data.length >= 2 && data.length <= 32); - return new Script([version === 0 ? 0 : version + 0x50, data]); + return Script.fromArray([version === 0 ? 0 : version + 0x50, data]); }; /** * Create a witness block commitment. * @param {Buffer} hash - * @params {String|Buffer} flags + * @param {String|Buffer} flags * @returns {Script} */ @@ -2316,7 +2406,7 @@ Script.createCommitment = function createCommitment(hash, flags) { p.writeU32BE(0xaa21a9ed); p.writeHash(hash); - return new Script([ + return Script.fromArray([ opcodes.OP_RETURN, p.render(), flags @@ -2390,8 +2480,8 @@ Script.prototype.isStandard = function isStandard() { var m, n; if (type === 'multisig') { - m = this.getSmall(0); - n = this.getSmall(-2); + m = Script.getSmall(this.raw[0]); + n = Script.getSmall(this.raw[this.raw.length - 2]); if (n < 1 || n > 3) return false; @@ -2947,7 +3037,7 @@ Script.getCoinbaseHeight = function getCoinbaseHeight(raw) { height = Script.getSmall(raw[0]); - if (height != null) + if (height !== -1) return height; if (raw[0] <= 0x06 && raw.length >= 1 + raw[0]) @@ -3013,14 +3103,31 @@ Script.prototype.test = function test(filter) { return false; }; +/** + * Unshift an item onto the `code` array. + * @param {Number|String|BN|Buffer} data + * @returns {Number} Length. + */ + Script.prototype.unshift = function unshift(data) { return this.code.unshift(Opcode.from(data)); }; +/** + * Push an item onto the `code` array. + * @param {Number|String|BN|Buffer} data + * @returns {Number} Length. + */ + Script.prototype.push = function push(data) { return this.code.push(Opcode.from(data)); }; +/** + * Shift an item off of the `code` array. + * @returns {Buffer|Number} + */ + Script.prototype.shift = function shift() { var op = this.code.shift(); if (!op) @@ -3028,6 +3135,11 @@ Script.prototype.shift = function shift() { return op.data || op.value; }; +/** + * Pop an item off of the `code` array. + * @returns {Buffer|Number} + */ + Script.prototype.pop = function push(data) { var op = this.code.pop(); if (!op) @@ -3035,6 +3147,12 @@ Script.prototype.pop = function push(data) { return op.data || op.value; }; +/** + * Remove an item from the `code` array. + * @param {Number} index + * @returns {Buffer|Number} + */ + Script.prototype.remove = function remove(i) { var op = this.code.splice(i, 1)[0]; if (!op) @@ -3042,11 +3160,23 @@ Script.prototype.remove = function remove(i) { return op.data || op.value; }; +/** + * Insert an item into the `code` array. + * @param {Number} index + * @param {Number|String|BN|Buffer} data + */ + 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]; }; +/** + * Get an item from the `code` array. + * @param {Number} index + * @returns {Buffer|Number} + */ + Script.prototype.get = function get(i) { var op = this.code[i]; if (!op) @@ -3054,13 +3184,44 @@ Script.prototype.get = function get(i) { return op.data || op.value; }; -Script.prototype.getNumber = function getNumber(i) { +/** + * Get a small integer from an opcode (OP_0-OP_16). + * @param {Number} index + * @returns {Number} + */ + +Script.prototype.getSmall = function getSmall(i) { var op = this.code[i]; + if (!op) + return -1; + return Script.getSmall(op.value); +}; + +/** + * Get a number from the `code` array (5-byte limit). + * @params {Number} index + * @returns {BN} + */ + +Script.prototype.getNumber = function getNumber(i) { + var small = this.getSmall(i); + var op = this.code[i]; + + if (small !== -1) + return new bn(small); + if (!op || !op.data || op.data.length > 5) return; + return Script.num(op.data, constants.flags.VERIFY_NONE, 5); }; +/** + * Get a string from the `code` array (utf8). + * @params {Number} index + * @returns {String} + */ + Script.prototype.getString = function getString(i) { var op = this.code[i]; if (!op || !op.data) @@ -3068,6 +3229,12 @@ Script.prototype.getString = function getString(i) { return op.data.toString('utf8'); }; +/** + * Set an item in the `code` array. + * @param {Number} index + * @param {Buffer|Number|String|BN} data + */ + Script.prototype.set = function set(i, data) { assert(i <= this.code.length, 'Index out of bounds.'); this.code[i] = Opcode.from(data); @@ -3123,20 +3290,6 @@ Script.isDummy = function isDummy(data) { return Buffer.isBuffer(data) && data.length === 0; }; -/** - * Test whether the data element is a null dummy or an OP_0. - * @private - * @param {Buffer?} data - * @returns {Boolean} - */ - -Script.isZero = function isZero(op) { - if (op === opcodes.OP_0) - return true; - - return Script.isDummy(op); -}; - /** * Test whether the data element is a valid key if VERIFY_STRICTENC is enabled. * @param {Buffer} key @@ -3698,19 +3851,6 @@ Script.fromString = function fromString(code) { return new Script(p.render()); }; -/** - * Get a small integer from an opcode (OP_0-OP_16). - * @param {Number} index - * @returns {Number} - */ - -Script.prototype.getSmall = function getSmall(i) { - if (i < 0) - i = this.code.length + i; - - return Script.getSmall(this.code[i].value); -}; - /** * Get a small integer from an opcode (OP_0-OP_16). * @param {Number} index @@ -3719,7 +3859,7 @@ Script.prototype.getSmall = function getSmall(i) { Script.getSmall = function getSmall(op) { if (typeof op !== 'number') - return null; + return -1; if (op === opcodes.OP_0) return 0; @@ -4339,7 +4479,7 @@ Opcode.fromString = function fromString(data, enc) { /** * Instantiate a pushdata opcode from anything. - * @params {String|Buffer|Number|BN|Opcode} data + * @param {String|Buffer|Number|BN|Opcode} data * @returns {Opcode} */