From 85aad21ede0fe840dafabafeb645e3d3c2ac2c33 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Fri, 26 Jan 2018 15:02:23 +0000 Subject: [PATCH 1/6] Add SHL/SHR/SAR opcodes --- lib/opcodes.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/opcodes.js b/lib/opcodes.js index f277001e13..11dbc31d9f 100644 --- a/lib/opcodes.js +++ b/lib/opcodes.js @@ -26,6 +26,9 @@ const codes = { 0x18: ['XOR', 3, 2, 1, false], 0x19: ['NOT', 3, 1, 1, false], 0x1a: ['BYTE', 3, 2, 1, false], + 0x1b: ['SHL', 3, 2, 1, false], + 0x1c: ['SHR', 3, 2, 1, false], + 0x1d: ['SAR', 3, 2, 1, false], // 0x20 range - crypto 0x20: ['SHA3', 30, 2, 1, false], From ed6b9dc419d6f5e72b118e6b0f5ddddf9828e679 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Fri, 26 Jan 2018 15:02:30 +0000 Subject: [PATCH 2/6] Implement SHL and SHR --- lib/opFns.js | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/lib/opFns.js b/lib/opFns.js index 8d0c95fc67..5a93cb6514 100644 --- a/lib/opFns.js +++ b/lib/opFns.js @@ -157,6 +157,29 @@ module.exports = { return new BN(word.shrn((31 - pos.toNumber()) * 8).andln(0xff)) }, + SHL: function (a, b, runState) { + if (a.gten(256)) { + return new BN(0) + } + return b.shln(a.toNumber()).iand(utils.MAX_INTEGER) + }, + SHR: function (a, b, runState) { + if (a.gten(256)) { + return new BN(0) + } + return b.shrn(a.toNumber()) + }, + SAR: function (a, b, runState) { + b = b.fromTwos(256) + if (a.gten(256)) { + if (b.ltn(0)) { + return new BN(-1).toTwos(256) + } else { + return new BN(0) + } + } + // FIXME: implement + }, // 0x20 range - crypto SHA3: function (offset, length, runState) { var data = memLoad(runState, offset, length) From 6218bc60ee09b9518cb641386581acd6f5aeee3d Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Fri, 20 Jul 2018 11:29:08 +0100 Subject: [PATCH 3/6] Check if constantinople hard fork is active in the SHL/SHR/SAR implementation --- lib/opFns.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/opFns.js b/lib/opFns.js index 5a93cb6514..741a8fe18a 100644 --- a/lib/opFns.js +++ b/lib/opFns.js @@ -158,18 +158,27 @@ module.exports = { return new BN(word.shrn((31 - pos.toNumber()) * 8).andln(0xff)) }, SHL: function (a, b, runState) { + if (!runState._common.gteHardfork('constantinople')) { + trap(ERROR.INVALID_OPCODE) + } if (a.gten(256)) { return new BN(0) } return b.shln(a.toNumber()).iand(utils.MAX_INTEGER) }, SHR: function (a, b, runState) { + if (!runState._common.gteHardfork('constantinople')) { + trap(ERROR.INVALID_OPCODE) + } if (a.gten(256)) { return new BN(0) } return b.shrn(a.toNumber()) }, SAR: function (a, b, runState) { + if (!runState._common.gteHardfork('constantinople')) { + trap(ERROR.INVALID_OPCODE) + } b = b.fromTwos(256) if (a.gten(256)) { if (b.ltn(0)) { From 4c64288a4ee61a3bcc55125701e3f9020b7c65f3 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Mon, 23 Jul 2018 20:36:39 +0100 Subject: [PATCH 4/6] Implement SAR --- lib/opFns.js | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/lib/opFns.js b/lib/opFns.js index 741a8fe18a..59edf98936 100644 --- a/lib/opFns.js +++ b/lib/opFns.js @@ -179,15 +179,21 @@ module.exports = { if (!runState._common.gteHardfork('constantinople')) { trap(ERROR.INVALID_OPCODE) } - b = b.fromTwos(256) if (a.gten(256)) { - if (b.ltn(0)) { + if (b.fromTwos(256).ltn(0)) { return new BN(-1).toTwos(256) } else { return new BN(0) } } - // FIXME: implement + const c = b.shrn(a.toNumber()) + if (b.testn(255)) { + const shiftedOutWidth = 255 - a.toNumber() + const mask = utils.MAX_INTEGER.shrn(shiftedOutWidth).shln(shiftedOutWidth) + return c.ior(mask) + } else { + return c + } }, // 0x20 range - crypto SHA3: function (offset, length, runState) { From b90ba6e927efce8e02f9d5acf2e74b72d102f12b Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Mon, 23 Jul 2018 20:38:30 +0100 Subject: [PATCH 5/6] Always properly throw VMError --- lib/runCode.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/runCode.js b/lib/runCode.js index 7dace81894..921342af1d 100644 --- a/lib/runCode.js +++ b/lib/runCode.js @@ -213,14 +213,14 @@ module.exports = function (opts, cb) { if (result !== undefined) { if (retNum !== 1) { // opcode post-stack mismatch - return cb(VmError(ERROR.INTERNAL_ERROR)) + return cb(new VmError(ERROR.INTERNAL_ERROR)) } runState.stack.push(result) } else { if (!opInfo.async && retNum !== 0) { // opcode post-stack mismatch - return cb(VmError(ERROR.INTERNAL_ERROR)) + return cb(new VmError(ERROR.INTERNAL_ERROR)) } } From d843cd269704c6cfbc0d4dfcc4b3a6b82a3b867c Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Mon, 23 Jul 2018 20:50:53 +0100 Subject: [PATCH 6/6] Optimise SAR to not use from/toTwos conversion --- lib/opFns.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/opFns.js b/lib/opFns.js index 59edf98936..611e8c6368 100644 --- a/lib/opFns.js +++ b/lib/opFns.js @@ -179,15 +179,16 @@ module.exports = { if (!runState._common.gteHardfork('constantinople')) { trap(ERROR.INVALID_OPCODE) } + const isSigned = b.testn(255) if (a.gten(256)) { - if (b.fromTwos(256).ltn(0)) { - return new BN(-1).toTwos(256) + if (isSigned) { + return new BN(utils.MAX_INTEGER) } else { return new BN(0) } } const c = b.shrn(a.toNumber()) - if (b.testn(255)) { + if (isSigned) { const shiftedOutWidth = 255 - a.toNumber() const mask = utils.MAX_INTEGER.shrn(shiftedOutWidth).shln(shiftedOutWidth) return c.ior(mask)