Support berlin fork (#1428)

* Support berlin fork

* Add review fixes

Co-authored-by: JoranHonig <JoranHonig@users.noreply.github.com>
pull/1436/head
Nikhil Parasaram 4 years ago committed by GitHub
parent a257df3074
commit 3ef1065b15
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      mythril/laser/ethereum/instruction_data.py
  2. 39
      mythril/laser/ethereum/instructions.py
  3. 4
      mythril/laser/ethereum/state/machine_state.py
  4. 1
      mythril/laser/ethereum/svm.py
  5. 3
      mythril/support/opcodes.py
  6. 89
      tests/instructions/berlin_fork_opcodes_test.py

@ -181,6 +181,9 @@ OPCODES = {
"SUICIDE": {GAS: (5000, 30000), STACK: (1, 0)},
"ASSERT_FAIL": {GAS: (0, 0), STACK: (0, 0)},
"INVALID": {GAS: (0, 0), STACK: (0, 0)},
"BEGINSUB": {GAS: (2, 2), STACK: (0, 0)},
"JUMPSUB": {GAS: (10, 10), STACK: (1, 0)},
"RETURNSUB": {GAS: (5, 5), STACK: (0, 0)},
} # type: Dict[str, Dict[str, Tuple[int, int]]]

@ -257,7 +257,6 @@ class Instruction:
raise NotImplementedError
self._execute_pre_hooks(global_state)
result = instruction_mutator(global_state)
self._execute_post_hooks(global_state)
@ -1618,6 +1617,42 @@ class Instruction:
log.debug("Pruned unreachable states.")
return states
@StateTransition()
def beginsub_(self, global_state: GlobalState) -> List[GlobalState]:
"""
This opcode depicts the start of the subroutine
"""
raise OutOfGasException("Encountered BEGINSUB")
@StateTransition()
def jumpsub_(self, global_state: GlobalState) -> List[GlobalState]:
"""
Jump to the subroutine
"""
disassembly = global_state.environment.code
try:
location = util.get_concrete_int(global_state.mstate.stack.pop())
except TypeError:
raise VmException("Encountered symbolic JUMPSUB location")
index = util.get_instruction_index(disassembly.instruction_list, location)
instr = disassembly.instruction_list[index]
if instr["opcode"] != "BEGINSUB":
raise VmException(
"Encountered invalid JUMPSUB location :{}".format(instr["address"])
)
global_state.mstate.subroutine_stack.append(global_state.mstate.pc + 1)
global_state.mstate.pc = location
return [global_state]
@StateTransition(increment_pc=False)
def returnsub_(self, global_state: GlobalState) -> List[GlobalState]:
"""
Returns control to the caller of the subroutine
"""
global_state.mstate.pc = global_state.mstate.subroutine_stack.pop()
return [global_state]
@StateTransition()
def pc_(self, global_state: GlobalState) -> List[GlobalState]:
"""
@ -1629,7 +1664,7 @@ class Instruction:
program_counter = global_state.environment.code.instruction_list[index][
"address"
]
global_state.mstate.stack.append(program_counter)
global_state.mstate.stack.append(symbol_factory.BitVecVal(program_counter, 256))
return [global_state]

@ -89,6 +89,7 @@ class MachineState:
gas_limit: int,
pc=0,
stack=None,
subroutine_stack=None,
memory: Optional[Memory] = None,
constraints=None,
depth=0,
@ -110,6 +111,7 @@ class MachineState:
"""
self._pc = pc
self.stack = MachineStack(stack)
self.subroutine_stack = MachineStack(subroutine_stack)
self.memory = memory or Memory()
self.gas_limit = gas_limit
self.min_gas_used = min_gas_used # lower gas usage bound
@ -216,6 +218,7 @@ class MachineState:
memory=copy(self.memory),
depth=self.depth,
prev_pc=self.prev_pc,
subroutine_stack=copy(self.subroutine_stack),
)
def __str__(self):
@ -255,6 +258,7 @@ class MachineState:
return dict(
pc=self._pc,
stack=self.stack,
subroutine_stack=self.subroutine_stack,
memory=self.memory,
memsize=self.memory_size,
gas=self.gas_limit,

@ -313,7 +313,6 @@ class LaserEVM:
hook(global_state)
instructions = global_state.environment.code.instruction_list
try:
op_code = instructions[global_state.mstate.pc]["opcode"]
except IndexError:

@ -65,6 +65,9 @@ opcodes = {
0x59: ("MSIZE", 0, 1, 2),
0x5A: ("GAS", 0, 1, 2),
0x5B: ("JUMPDEST", 0, 0, 1),
0x5C: ("BEGINSUB", 0, 0, 2),
0x5D: ("RETURNSUB", 0, 0, 5),
0x5E: ("JUMPSUB", 1, 0, 10),
0xA0: ("LOG0", 2, 0, 375),
0xA1: ("LOG1", 3, 0, 750),
0xA2: ("LOG2", 4, 0, 1125),

@ -0,0 +1,89 @@
import pytest
from mythril.laser.ethereum.evm_exceptions import VmException, OutOfGasException
from mythril.disassembler.disassembly import Disassembly
from mythril.laser.ethereum.state.environment import Environment
from mythril.laser.ethereum.state.world_state import WorldState
from mythril.laser.ethereum.state.account import Account
from mythril.laser.ethereum.state.machine_state import MachineState
from mythril.laser.ethereum.state.global_state import GlobalState
from mythril.laser.ethereum.state.world_state import WorldState
from mythril.laser.ethereum.instructions import Instruction
from mythril.laser.ethereum.transaction.transaction_models import MessageCallTransaction
from mythril.laser.smt import symbol_factory, simplify
def get_state():
world_state = WorldState()
account = world_state.create_account(balance=10, address=101)
account.code = Disassembly("0x60045e005c5d")
environment = Environment(account, None, None, None, None, None)
state = GlobalState(world_state, environment, None, MachineState(gas_limit=8000000))
state.transaction_stack.append(
(MessageCallTransaction(world_state=WorldState(), gas_limit=8000000), None)
)
return state
BVV = symbol_factory.BitVecVal
BV = symbol_factory.BitVecSym
test_data = (([BVV(5, 256)], [], 2, -1, ()),)
def test_jumpsub_success():
# Arrange
state = get_state()
state.mstate.pc = 2
state.mstate.stack = [BVV(4, 256)]
state.mstate.subroutine_stack = []
instruction = Instruction("jumpsub", dynamic_loader=None)
# Act
new_state = instruction.evaluate(state)[0]
# Assert
assert new_state.mstate.pc == 5
assert new_state.mstate.stack == []
assert new_state.mstate.subroutine_stack == [3]
def test_jumpsub_fail():
# Arrange
state = get_state()
state.mstate.pc = 2
state.mstate.stack = [BVV(5, 256)]
state.mstate.subroutine_stack = []
instruction = Instruction("jumpsub", dynamic_loader=None)
# Act + Assert
with pytest.raises(VmException):
instruction.evaluate(state)[0]
def test_beginsub():
# Arrange
state = get_state()
state.mstate.pc = 3
state.mstate.stack = []
state.mstate.subroutine_stack = []
instruction = Instruction("beginsub", dynamic_loader=None)
# Act + Assert
with pytest.raises(OutOfGasException):
instruction.evaluate(state)[0]
def test_returnsub():
# Arrange
state = get_state()
state.mstate.pc = 5
state.mstate.stack = []
state.mstate.subroutine_stack = [3]
instruction = Instruction("returnsub", dynamic_loader=None)
# Act
new_state = instruction.evaluate(state)[0]
# Assert
assert new_state.mstate.pc == 3
Loading…
Cancel
Save