From 5ae6a4e02c0fd845d7840be104969c5020e3819d Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Mon, 13 Jun 2016 22:39:05 -0700 Subject: [PATCH] always compile scripts. --- lib/bcoin/coins.js | 4 +- lib/bcoin/script.js | 380 +++++++++++--------------------------------- lib/bcoin/tx.js | 4 +- 3 files changed, 92 insertions(+), 296 deletions(-) diff --git a/lib/bcoin/coins.js b/lib/bcoin/coins.js index b2ea9f0b..bd6af664 100644 --- a/lib/bcoin/coins.js +++ b/lib/bcoin/coins.js @@ -156,10 +156,10 @@ Coins.prototype.toRaw = function toRaw(writer) { // Saves up to 7 bytes. if (output.script.isPubkeyhash()) { prefix = 1; - hash = output.script.raw.slice(3, 23); + hash = output.script.code[2].data; } else if (output.script.isScripthash()) { prefix = 2; - hash = output.script.raw.slice(2, 22); + hash = output.script.code[1].data; } p.writeU8(prefix); diff --git a/lib/bcoin/script.js b/lib/bcoin/script.js index b15ca701..bf597f09 100644 --- a/lib/bcoin/script.js +++ b/lib/bcoin/script.js @@ -812,40 +812,36 @@ Stack.isStack = function isStack(obj) { * @property {Script?} redeem - Redeem script. */ -function Script(code) { - if (code instanceof Script) - return code; +function Script(raw) { + if (raw instanceof Script) + return raw; if (!(this instanceof Script)) - return new Script(code); + return new Script(raw); - if (!code) - code = STACK_FALSE; + if (!raw) + raw = STACK_FALSE; - if (code.raw) - code = code.raw; + if (raw.raw) + raw = raw.raw; - this.raw = code; + this.raw = null; this.code = null; - this._code = null; this.redeem = null; - if (Array.isArray(code)) - this.raw = Script.encodeArray(code); + if (Array.isArray(raw)) { + raw = Script.parseArray(raw); + this.raw = Script.encode(raw); + this.code = raw; + } else { + this.raw = raw; + this.code = Script.decode(raw); + } assert(Buffer.isBuffer(this.raw)); + assert(Array.isArray(this.code)); } -Script.prototype.__defineGetter__('code', function() { - if (!this._code) - this._code = Script.decode(this.raw); - return this._code; -}); - -Script.prototype.__defineSetter__('code', function(code) { - return this._code = code; -}); - Script.prototype.toArray = function toArray() { var code = []; var i, op; @@ -859,22 +855,9 @@ Script.prototype.toArray = function toArray() { }; Script.fromArray = function fromArray(code) { - var raw = Script.encodeArray(code); - return new Script(raw); + return new Script(code); }; -Script.prototype.toOps = function toOps() { - return this.code.slice(); -}; - -Script.fromOps = function fromOps(code) { - var raw = Script.encode(code); - var script = new Script(raw); - script.code = code; - return script; -}; - - /** * Clone the script. * @returns {Script} Cloned script. @@ -960,7 +943,7 @@ Script.prototype.getSubscript = function getSubscript(lastSep) { code.push(op); } - return Script.fromOps(code); + return new Script(code); }; /** @@ -986,7 +969,7 @@ Script.prototype.removeSeparators = function removeSeparators() { if (code.length === this.code.length) return this.clone(); - return Script.fromOps(code); + return new Script(code); }; /** @@ -2107,7 +2090,7 @@ Script.prototype.concat = function concat(scripts) { */ Script.createPubkey = function createPubkey(key) { - return Script.fromArray([key, opcodes.OP_CHECKSIG]); + return new Script([key, opcodes.OP_CHECKSIG]); }; /** @@ -2117,7 +2100,7 @@ Script.createPubkey = function createPubkey(key) { */ Script.createPubkeyhash = function createPubkeyhash(hash) { - return Script.fromArray([ + return new Script([ opcodes.OP_DUP, opcodes.OP_HASH160, hash, @@ -2152,7 +2135,7 @@ Script.createMultisig = function createMultisig(keys, m, n) { code.push(n + 0x50); code.push(opcodes.OP_CHECKMULTISIG); - return Script.fromArray(code); + return new Script(code); }; /** @@ -2163,7 +2146,7 @@ Script.createMultisig = function createMultisig(keys, m, n) { Script.createScripthash = function createScripthash(hash) { assert(hash.length === 20); - return Script.fromArray([ + return new Script([ opcodes.OP_HASH160, hash, opcodes.OP_EQUAL @@ -2179,7 +2162,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 Script.fromArray([ + return new Script([ opcodes.OP_RETURN, flags ]); @@ -2196,7 +2179,7 @@ Script.createWitnessProgram = function createWitnessProgram(version, data) { assert(typeof version === 'number' && version >= 0 && version <= 16); assert(Buffer.isBuffer(data)); assert(data.length >= 2 && data.length <= 32); - return Script.fromArray([version === 0 ? 0 : version + 0x50, data]); + return new Script([version === 0 ? 0 : version + 0x50, data]); }; /** @@ -2219,7 +2202,7 @@ Script.createCommitment = function createCommitment(hash, flags) { p.writeU32BE(0xaa21a9ed); p.writeHash(hash); - return Script.fromArray([ + return new Script([ opcodes.OP_RETURN, p.render(), flags @@ -3838,9 +3821,42 @@ Script.fromRaw = function fromRaw(data, enc) { function Opcode(value, data) { this.value = value; - this.data = data; + this.data = data || null; } +Opcode.fromData = function fromData(data) { + if (data.length === 0) + return new Opcode(opcodes.OP_0); + + if (data.length <= 0x4b) { + if (data.length === 1) { + if (data[0] >= 1 && data[0] <= 16) + return new Opcode(data[0] + 0x50); + + if (data[0] === 0x81) + return new Opcode(opcodes.OP_1NEGATE); + } + return new Opcode(data.length, data); + } + + if (data.length <= 0xff) + return new Opcode(opcodes.OP_PUSHDATA1, data); + + if (data.length <= 0xffff) + return new Opcode(opcodes.OP_PUSHDATA2, data); + + if (data.length <= 0xffffffff) + return new Opcode(opcodes.OP_PUSHDATA4, data); + + assert(false, 'Bad pushdata size.'); +}; + +Opcode.fromOp = function fromOp(op) { + return new Opcode(op); +}; + +Script.opcode = Opcode; + Script.decode = function decode(raw) { var p = new BufferReader(raw, true); var code = []; @@ -3948,54 +3964,47 @@ Script.encode = function encode(code, writer) { return p; }; -Script.encodeArray = function encodeArray(code, writer) { - var p = new BufferWriter(writer); +Script.parseArray = function parseArray(code, writer) { + var out = []; var i, op; assert(Array.isArray(code)); + if (code[0] instanceof Opcode) + return code; + for (i = 0; i < code.length; i++) { op = code[i]; if (Buffer.isBuffer(op)) { if (op.length === 0) { - p.writeU8(opcodes.OP_0); + out.push(new Opcode(opcodes.OP_0)); } else if (op.length <= 0x4b) { if (op.length === 1) { if (op[0] >= 1 && op[0] <= 16) { - p.writeU8(op[0] + 0x50); + out.push(new Opcode(op[0] + 0x50)); continue; } else if (op[0] === 0x81) { - p.writeU8(opcodes.OP_1NEGATE); + out.push(new Opcode(opcodes.OP_1NEGATE)); continue; } } - p.writeU8(op.length); - p.writeBytes(op); + out.push(new Opcode(op.length, op)); } else if (op.length <= 0xff) { - p.writeU8(opcodes.OP_PUSHDATA1); - p.writeU8(op.length); - p.writeBytes(op); + out.push(new Opcode(opcodes.OP_PUSHDATA1, op)); } else if (op.length <= 0xffff) { - p.writeU8(opcodes.OP_PUSHDATA2); - p.writeU16(op.length); - p.writeBytes(op); + out.push(new Opcode(opcodes.OP_PUSHDATA2, op)); } else if (op.length <= 0xffffffff) { - p.writeU8(opcodes.OP_PUSHDATA4); - p.writeU32(op.length); - p.writeBytes(op); + out.push(new Opcode(opcodes.OP_PUSHDATA4, op)); } else { assert(false, 'Bad pushdata op.'); } continue; } assert(typeof op === 'number'); - p.writeU8(op); + out.push(new Opcode(op)); } - if (!writer) - p = p.render(); - - return p; + return out; }; /** @@ -4029,10 +4038,8 @@ Script.encodePush = function encodePush(data) { return p.render(); }; -function ScriptWriter(script) { - this.writer = new BufferWriter(); - if (script) - this.writer.writeBytes(script.data); +function ScriptWriter() { + this.ops = []; } ScriptWriter.prototype.writeString = function writeString(str, enc) { @@ -4049,17 +4056,17 @@ ScriptWriter.prototype.writeData = function writeData(data, op) { return this.writePush(data, op); if (data.length === 0) { - p.writeU8(opcodes.OP_0); + this.ops.push(new Opcode(opcodes.OP_0)); return; } if (data.length === 1) { if (data[0] >= 1 && data[0] <= 16) { - p.writeU8(data[0] + 0x50); + this.ops.push(new Opcode(data[0] + 0x50)); return; } if (data[0] === 0x81) { - p.writeU8(opcodes.OP_1NEGATE); + this.ops.push(new Opcode(opcodes.OP_1NEGATE)); return; } } @@ -4071,73 +4078,39 @@ ScriptWriter.prototype.writePush = function writePush(data, op) { var p = this.writer; if (op != null) { - if (op >= 0x01 && op <= 0x4b) { - p.writeU8(op); - p.writeBytes(data); - return; - } - if (op === opcodes.OP_PUSHDATA1) { - p.writeU8(op); - p.writeU8(data.length); - p.writeBytes(data); - return; - } - if (op === opcodes.OP_PUSHDATA2) { - p.writeU8(op); - p.writeU16(data.length); - p.writeBytes(data); - return; - } - if (op === opcodes.OP_PUSHDATA4) { - p.writeU8(op); - p.writeU32(data.length); - p.writeBytes(data); - return; - } - assert('Bad opcode.'); + this.ops.push(new Opcode(op, data)); + return; } if (data.length <= 0x4b) { - p.writeU8(data.length); - p.writeBytes(data); + this.ops.push(new Opcode(data.length, data)); return; } if (data.length <= 0xff) { - p.writeU8(opcodes.OP_PUSHDATA1); - p.writeU8(data.length); - p.writeBytes(data); + this.ops.push(new Opcode(opcodes.OP_PUSHDATA1, data)); return; } if (data.length <= 0xffff) { - p.writeU8(opcodes.OP_PUSHDATA2); - p.writeU16(data.length); - p.writeBytes(data); + this.ops.push(new Opcode(opcodes.OP_PUSHDATA2, data)); return; } if (data.length <= 0xffffffff) { - p.writeU8(opcodes.OP_PUSHDATA4); - p.writeU32(data.length); - p.writeBytes(data); + this.ops.push(new Opcode(opcodes.OP_PUSHDATA4, data)); return; } throw new Error('Pushdata too large.'); }; - -ScriptWriter.prototype.writeBytes = function writeBytes(data) { - this.writer.writeBytes(data); -}; - ScriptWriter.prototype.writeScript = function writeScript(script) { this.writeData(script.raw); }; ScriptWriter.prototype.writeOp = function writeOp(opcode) { - this.writer.writeU8(opcode); + this.ops.push(new Opcode(opcode)); }; ScriptWriter.prototype.writeNumber = function writeNumber(num) { @@ -4165,182 +4138,7 @@ ScriptWriter.prototype.writeNumber = function writeNumber(num) { }; ScriptWriter.prototype.toScript = function toScript() { - return new Script(this.writer.render()); -}; - -function ScriptReader(script) { - this.raw = script.raw || script; - this.opcode = -1; - this.offset = 0; - this.ip = 0; - this.data = null; -} - -ScriptReader.prototype.isMinimal = function isMinimal(flags) { - return Script.checkMinimal(this.data, this.opcode, flags); -}; - -ScriptReader.prototype.isInvalid = function isInvalid() { - return this.opcode === -1; -}; - -ScriptReader.prototype.reset = function reset() { - this.offset = 0; - this.ip = 0; - this.opcode = -1; - this.data = null; -}; - -ScriptReader.prototype.readString = function readString(enc) { - var str = this.readData(); - if (!str) - return; - return str.toString(enc); -}; - -ScriptReader.prototype.readScript = function readScript() { - var script = this.readData(); - if (!script) - return; - return new Script(script); -}; - -ScriptReader.prototype.readNumber = function readNumber(limit) { - this.next(); - - if (this.opcode === opcodes.OP_1NEGATE) - return -1; - - if (this.opcode === opcodes.OP_0) - return 0; - - if (this.opcode >= opcodes.OP_1 && this.opcode <= opcodes.OP_16) - return this.opcode - 0x50; - - if (this.data) - return Script.num(this.data, 0, limit); - - return null; -}; - -ScriptReader.prototype.readSmall = function readSmall() { - var op = this.readOp(); - - if (op === opcodes.OP_0) - return 0; - - if (op >= opcodes.OP_1 && op <= opcodes.OP_16) - return op - 0x50; - - return -1; -}; - -ScriptReader.prototype.readOp = function readOp() { - this.next(true); - return this.opcode; -}; - -ScriptReader.prototype.readData = function readData() { - this.next(); - return this.data; -}; - -ScriptReader.prototype.seek = function seek() { - return this.next(true); -}; - -ScriptReader.prototype.peek = function peek() { - if (this.left() === 0) - return -1; - - return this.raw[this.offset]; -}; - -ScriptReader.prototype.left = function left() { - return this.raw.length - this.offset; -}; - -ScriptReader.prototype.next = function next(seek) { - var p = this.reader; - var op, data, size; - - this.ip = this.offset; - this.opcode = -1; - this.data = null; - - if (this.left() === 0) - return false; - - op = this.raw[this.offset++]; - - if (op >= 0x01 && op <= 0x4b) { - if (this.left() < op) { - this.offset = this.raw.length; - return true; - } - if (!seek) - this.data = this.raw.slice(this.offset, this.offset + op); - this.offset += op; - this.opcode = op; - return true; - } - - if (op === opcodes.OP_PUSHDATA1) { - if (this.left() < 1) { - this.offset = this.raw.length; - return true; - } - size = this.raw[this.offset++]; - if (this.left() < size) { - this.offset = this.raw.length; - return true; - } - if (!seek) - this.data = this.raw.slice(this.offset, this.offset + size); - this.offset += size; - this.opcode = op; - return true; - } - - if (op === opcodes.OP_PUSHDATA2) { - if (this.left() < 2) { - this.offset = this.raw.length; - return true; - } - size = this.raw.readUInt16LE(this.offset, true); - this.offset += 2; - if (this.left() < size) { - this.offset = this.raw.length; - return true; - } - if (!seek) - this.data = this.raw.slice(this.offset, this.offset + size); - this.offset += size; - this.opcode = op; - return true; - } - - if (op === opcodes.OP_PUSHDATA4) { - if (this.left() < 4) { - this.offset = this.raw.length; - return true; - } - size = this.raw.readUInt32LE(this.offset, true); - this.offset += 4; - if (this.left() < size) { - this.offset = this.raw.length; - return true; - } - if (!seek) - this.data = this.raw.slice(this.offset, this.offset + size); - this.offset += size; - this.opcode = op; - return true; - } - - this.opcode = op; - - return true; + return new Script(this.ops); }; /** diff --git a/lib/bcoin/tx.js b/lib/bcoin/tx.js index 73c216d3..018b4ade 100644 --- a/lib/bcoin/tx.js +++ b/lib/bcoin/tx.js @@ -1039,10 +1039,8 @@ TX.prototype.getLegacySigops = function getLegacySigops() { for (i = 0; i < this.inputs.length; i++) total += this.inputs[i].script.getSigops(false); - for (i = 0; i < this.outputs.length; i++) { + for (i = 0; i < this.outputs.length; i++) total += this.outputs[i].script.getSigops(false); - this.outputs[i].script.code = null; - } return total; };