From 6517dd8918e0639bee30f1d4cee59eb42d8792e2 Mon Sep 17 00:00:00 2001 From: George Hotz Date: Tue, 11 Aug 2020 10:37:57 -0400 Subject: [PATCH] halve gas used by safetychecker --- .../optimistic-ethereum/ovm/SafetyChecker.sol | 116 +++++++++--------- 1 file changed, 58 insertions(+), 58 deletions(-) diff --git a/packages/contracts/contracts/optimistic-ethereum/ovm/SafetyChecker.sol b/packages/contracts/contracts/optimistic-ethereum/ovm/SafetyChecker.sol index b3180c7119398..e49be180b520e 100644 --- a/packages/contracts/contracts/optimistic-ethereum/ovm/SafetyChecker.sol +++ b/packages/contracts/contracts/optimistic-ethereum/ovm/SafetyChecker.sol @@ -65,8 +65,7 @@ contract SafetyChecker is ContractResolver { { bool seenJUMP = false; bool insideUnreachableCode = false; - uint256[] memory ops = new uint256[](_bytecode.length); - uint256 opIndex = 0; + uint256 ops = 0; for (uint256 pc = 0; pc < _bytecode.length; pc++) { // current opcode: 0x00...0xff uint256 op = uint8(_bytecode[pc]); @@ -85,72 +84,73 @@ contract SafetyChecker is ContractResolver { } } else { // check that opcode is whitelisted (using the whitelist bit mask) - uint256 opBit = 2 ** op; + uint256 opBit = 1 << op; if (opcodeWhitelistMask & opBit != opBit) { // encountered a non-whitelisted opcode! return false; } // append this opcode to a list of ops - ops[opIndex] = op; - // JUMPI - if (op == 0x57) { - // We can now reach all JUMPDESTs - seenJUMP = true; - // JUMP - } else if (op == 0x56) { - // We can now reach all JUMPDESTs - seenJUMP = true; - // we are now inside unreachable code until we hit a JUMPDEST! - insideUnreachableCode = true; - // STOP or REVERT or INVALID or RETURN (see safety checker docs in wiki for more info) - } else if (op == 0x00 || op == 0xfd || op == 0xfe || op == 0xf3) { - // If we can't jump to JUMPDESTs, then all remaining bytecode is unreachable - if (!seenJUMP) { - return true; - } - // We are now inside unreachable code until we hit a JUMPDEST! - insideUnreachableCode = true; - // CALL - } else if (op == 0xf1) { - // Minimum 4 total ops: - // 1. PUSH1 value - // 2. PUSH20 execution manager address - // 3. PUSH or DUP gas - // 4. CALL - - if (opIndex < 3) { - return false; - } - uint256 gasOp = ops[opIndex - 1]; - uint256 addressOp = ops[opIndex - 2]; - uint256 valueOp = ops[opIndex - 3]; - if ( - gasOp < 0x60 || // PUSHes are 0x60...0x7f - gasOp > 0x8f || // DUPs are 0x80...0x8f - addressOp != 0x73 || // address must be set with a PUSH20 - valueOp != 0x60 // value must be set with a PUSH1 - ) { - return false; - } else { - uint256 pushedBytes; - // gas is set with a PUSH## - if (gasOp >= 0x60 && gasOp <= 0x7f) { - pushedBytes = gasOp - 0x5f; + ops <<= 8; + ops |= op; + // [0x57, 0x56, 0x00, 0xfd, 0xfe, 0xf3, 0xf1] all have handlers + if (opBit & 0x600a00000000000000000000000000000000000000c000000000000000000001 != 0) { + // JUMPI + if (op == 0x57) { + // We can now reach all JUMPDESTs + seenJUMP = true; + // JUMP + } else if (op == 0x56) { + // We can now reach all JUMPDESTs + seenJUMP = true; + // we are now inside unreachable code until we hit a JUMPDEST! + insideUnreachableCode = true; + // STOP or REVERT or INVALID or RETURN (see safety checker docs in wiki for more info) + } else if (op == 0x00 || op == 0xfd || op == 0xfe || op == 0xf3) { + // If we can't jump to JUMPDESTs, then all remaining bytecode is unreachable + if (!seenJUMP) { + return true; } - - // 23 is from 1 + PUSH20 + 20 bytes of address + PUSH or DUP gas - byte callValue = _bytecode[pc - (23 + pushedBytes)]; - - // 21 is from 1 + 19 bytes of address + PUSH or DUP gas - address callAddress = toAddress(_bytecode, (pc - (21 + pushedBytes))); - - // CALL is made to the execution manager with msg.value of 0 ETH - if (callAddress != address(resolveExecutionManager()) || callValue != 0 ) { + // We are now inside unreachable code until we hit a JUMPDEST! + insideUnreachableCode = true; + // CALL + } else if (op == 0xf1) { + // Minimum 4 total ops: + // 1. PUSH1 value + // 2. PUSH20 execution manager address + // 3. PUSH or DUP gas + // 4. CALL + + // if opIndex < 3, there would be 0s here, so we don't need the check + uint256 gasOp = (ops >> 8) & 0xFF; + uint256 addressOp = (ops >> 16) & 0xFF; + uint256 valueOp = (ops >> 24) & 0xFF; + if ( + gasOp < 0x60 || // PUSHes are 0x60...0x7f + gasOp > 0x8f || // DUPs are 0x80...0x8f + addressOp != 0x73 || // address must be set with a PUSH20 + valueOp != 0x60 // value must be set with a PUSH1 + ) { return false; + } else { + uint256 pushedBytes; + // gas is set with a PUSH## + if (gasOp >= 0x60 && gasOp <= 0x7f) { + pushedBytes = gasOp - 0x5f; + } + + // 23 is from 1 + PUSH20 + 20 bytes of address + PUSH or DUP gas + byte callValue = _bytecode[pc - (23 + pushedBytes)]; + + // 21 is from 1 + 19 bytes of address + PUSH or DUP gas + address callAddress = toAddress(_bytecode, (pc - (21 + pushedBytes))); + + // CALL is made to the execution manager with msg.value of 0 ETH + if (callAddress != address(resolveExecutionManager()) || callValue != 0 ) { + return false; + } } } } - opIndex++; } } return true;