From 4b2219c71ff7355894f3ee7d099a2e19d97a3512 Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Mon, 9 May 2016 15:13:07 -0700 Subject: [PATCH] more accurate FindAndDelete. --- lib/bcoin/script.js | 91 +++++++++++++++++++++++++- test/script-test.js | 153 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 241 insertions(+), 3 deletions(-) diff --git a/lib/bcoin/script.js b/lib/bcoin/script.js index 4492a823..a70e65d0 100644 --- a/lib/bcoin/script.js +++ b/lib/bcoin/script.js @@ -1549,7 +1549,7 @@ Script.prototype.execute = function execute(stack, flags, tx, index, version) { subscript = this.getSubscript(lastSep); if (version === 0) - subscript.removeData(sig); + subscript.removeData(Script.encodePush(sig)); Script.validateSignature(sig, flags); Script.validateKey(key, flags); @@ -1611,7 +1611,7 @@ Script.prototype.execute = function execute(stack, flags, tx, index, version) { for (j = 0; j < m; j++) { sig = stack.top(-isig - j); if (version === 0) - subscript.removeData(sig); + subscript.removeData(Script.encodePush(sig)); } res = true; @@ -1971,7 +1971,7 @@ Script.array = function(value) { * @returns {Number} Total. */ -Script.prototype.removeData = function removeData(data) { +Script.prototype.removeDataFast = function removeData(data) { var index = []; var i, op; @@ -2009,6 +2009,91 @@ Script.prototype.removeData = function removeData(data) { return index.length; }; +Script.encodePush = function encodePush(data) { + var p = new BufferWriter(); + if (data.length <= 0x4b) { + p.writeU8(data.length); + p.writeBytes(data); + } else if (data.length <= 0xff) { + p.writeU8(opcodes.OP_PUSHDATA1); + p.writeU8(data.length); + p.writeBytes(data); + } else if (data.length <= 0xffff) { + p.writeU8(opcodes.OP_PUSHDATA2); + p.writeU16(data.length); + p.writeBytes(data); + } else if (data.length <= 0xffffffff) { + p.writeU8(opcodes.OP_PUSHDATA4); + p.writeU32(data.length); + p.writeBytes(data); + } else { + assert(false, 'Bad pushdata op.'); + } + return p.render(); +}; + +Script.prototype.removeData = function removeData(data) { + var total = 0; + var p, raw, a, b, op, size; + + if (data.length === 0) + return total; + + // Compare on the byte level. + raw = this.encode(); + + p = new BufferReader(raw, true); + + for (;;) { + while (p.left() >= data.length && utils.icmp(raw, data, p.offset) === 0) { + a = raw.slice(0, p.offset); + b = raw.slice(p.offset + data.length); + raw = Buffer.concat([a, b]); + p.data = raw; + total++; + } + + if (p.left() === 0) + break; + + op = p.readU8(); + if (op >= 0x01 && op <= 0x4b) { + if (p.left() < op) + break; + p.seek(op); + } else if (op === opcodes.OP_PUSHDATA1) { + if (p.left() < 1) + break; + size = p.readU8(); + if (p.left() < size) + break; + p.seek(size); + } else if (op === opcodes.OP_PUSHDATA2) { + if (p.left() < 2) + break; + size = p.readU16(); + if (p.left() < size) + break; + p.seek(size); + } else if (op === opcodes.OP_PUSHDATA4) { + if (p.left() < 4) + break; + size = p.readU32(); + if (p.left() < size) + break; + p.seek(size); + } + } + + if (total > 0) { + this.code = Script.decode(raw); + if (this.raw) + this.raw = raw; + } + + return total; +}; + /** * Find a data element in a script. * @param {Buffer} data - Data element to match against. diff --git a/test/script-test.js b/test/script-test.js index ff0aac8e..424b9184 100644 --- a/test/script-test.js +++ b/test/script-test.js @@ -258,6 +258,159 @@ describe('Script', function() { assert(success(s2.execute(stack), stack)); }); + it('should execute FindAndDelete correctly', function() { + var s, d, expect; + + function del(s) { + s.mutable = true; + delete s.raw; + return s; + } + + s = bcoin.script.fromString('OP_1 OP_2'); + del(s); + d = new bcoin.script(); + expect = s.clone(); + assert.equal(s.removeData(d.encode()), 0); + assert.deepEqual(s.encode(), expect.encode()); + + s = bcoin.script.fromString('OP_1 OP_2 OP_3'); + del(s); + d = bcoin.script.fromString('OP_2'); + del(d); + expect = bcoin.script.fromString('OP_1 OP_3'); + del(expect); + assert.equal(s.removeData(d.encode()), 1); + assert.deepEqual(s.encode(), expect.encode()); + + s = bcoin.script.fromString('OP_3 OP_1 OP_3 OP_3 OP_4 OP_3'); + del(s); + d = bcoin.script.fromString('OP_3'); + del(d); + expect = bcoin.script.fromString('OP_1 OP_4'); + del(expect); + assert.equal(s.removeData(d.encode()), 4); + assert.deepEqual(s.encode(), expect.encode()); + + s = bcoin.script.fromRaw('0302ff03', 'hex'); + del(s); + d = bcoin.script.fromRaw('0302ff03', 'hex'); + del(d); + expect = new bcoin.script(); + del(expect); + assert.equal(s.removeData(d.encode()), 1); + assert.deepEqual(s.encode(), expect.encode()); + + s = bcoin.script.fromRaw('0302ff030302ff03', 'hex'); + del(s); + d = bcoin.script.fromRaw('0302ff03', 'hex'); + del(d); + expect = new bcoin.script(); + del(expect); + assert.equal(s.removeData(d.encode()), 2); + assert.deepEqual(s.encode(), expect.encode()); + + s = bcoin.script.fromRaw('0302ff030302ff03', 'hex'); + del(s); + d = bcoin.script.fromRaw('02', 'hex'); + del(d); + expect = s.clone(); + del(expect); + //assert.equal(s.removeData(d.encode()), 0); + s.removeData(d.encode()); + assert.deepEqual(s.encode(), expect.encode()); + + s = bcoin.script.fromRaw('0302ff030302ff03', 'hex'); + del(s); + d = bcoin.script.fromRaw('ff', 'hex'); + del(d); + expect = s.clone(); + del(expect); + assert.equal(s.removeData(d.encode()), 0); + assert.deepEqual(s.encode(), expect.encode()); + + s = bcoin.script.fromRaw('0302ff030302ff03', 'hex'); + del(s); + d = bcoin.script.fromRaw('03', 'hex'); + del(d); + expect = new bcoin.script([new Buffer([0xff, 0x03]), new Buffer([0xff, 0x03])]); + del(expect); + assert.equal(s.removeData(d.encode()), 2); + assert.deepEqual(s.encode(), expect.encode()); + + s = bcoin.script.fromRaw('02feed5169', 'hex'); + del(s); + d = bcoin.script.fromRaw('feed51', 'hex'); + del(d); + expect = s.clone(); + del(expect); + assert.equal(s.removeData(d.encode()), 0); + assert.deepEqual(s.encode(), expect.encode()); + + s = bcoin.script.fromRaw('02feed5169', 'hex'); + del(s); + d = bcoin.script.fromRaw('02feed51', 'hex'); + del(d); + expect = bcoin.script.fromRaw('69', 'hex'); + del(expect); + assert.equal(s.removeData(d.encode()), 1); + assert.deepEqual(s.encode(), expect.encode()); + + s = bcoin.script.fromRaw('516902feed5169', 'hex'); + del(s); + d = bcoin.script.fromRaw('feed51', 'hex'); + del(d); + expect = s.clone(); + del(expect); + assert.equal(s.removeData(d.encode()), 0); + assert.deepEqual(s.encode(), expect.encode()); + + s = bcoin.script.fromRaw('516902feed5169', 'hex'); + del(s); + d = bcoin.script.fromRaw('02feed51', 'hex'); + del(d); + expect = bcoin.script.fromRaw('516969', 'hex'); + del(expect); + assert.equal(s.removeData(d.encode()), 1); + assert.deepEqual(s.encode(), expect.encode()); + + s = bcoin.script.fromString('OP_0 OP_0 OP_1 OP_1'); + del(s); + d = bcoin.script.fromString('OP_0 OP_1'); + del(d); + expect = bcoin.script.fromString('OP_0 OP_1'); + del(expect); + assert.equal(s.removeData(d.encode()), 1); + assert.deepEqual(s.encode(), expect.encode()); + + s = bcoin.script.fromString('OP_0 OP_0 OP_1 OP_0 OP_1 OP_1'); + del(s); + d = bcoin.script.fromString('OP_0 OP_1'); + del(d); + expect = bcoin.script.fromString('OP_0 OP_1'); + del(expect); + assert.equal(s.removeData(d.encode()), 2); + assert.deepEqual(s.encode(), expect.encode()); + + s = bcoin.script.fromRaw('0003feed', 'hex'); + del(s); + d = bcoin.script.fromRaw('03feed', 'hex'); + del(d); + expect = bcoin.script.fromRaw('00', 'hex'); + del(expect); + assert.equal(s.removeData(d.encode()), 1); + assert.deepEqual(s.encode(), expect.encode()); + + s = bcoin.script.fromRaw('0003feed', 'hex'); + del(s); + d = bcoin.script.fromRaw('00', 'hex'); + del(d); + expect = bcoin.script.fromRaw('03feed', 'hex'); + del(expect); + assert.equal(s.removeData(d.encode()), 1); + assert.deepEqual(s.encode(), expect.encode()); + }); + scripts.forEach(function(data) { // ["Format is: [[wit...]?, scriptSig, scriptPubKey, flags, expected_scripterror, ... comments]"], var witness = Array.isArray(data[0]) ? data.shift() : null;