Beispiel #1
0
    def apply_computation(
            cls, state: StateAPI, message: MessageAPI,
            transaction_context: TransactionContextAPI) -> ComputationAPI:
        with cls(state, message, transaction_context) as computation:
            # Early exit on pre-compiles
            precompile = computation.precompiles.get(message.code_address,
                                                     NO_RESULT)
            if precompile is not NO_RESULT:
                precompile(computation)
                return computation

            show_debug2 = computation.logger.show_debug2

            opcode_lookup = computation.opcodes
            for opcode in computation.code:
                try:
                    opcode_fn = opcode_lookup[opcode]
                except KeyError:
                    opcode_fn = InvalidOpcode(opcode)

                if show_debug2:
                    computation.logger.debug2(
                        "OPCODE: 0x%x (%s) | pc: %s",
                        opcode,
                        opcode_fn.mnemonic,
                        max(0, computation.code.program_counter - 1),
                    )

                try:
                    opcode_fn(computation=computation)
                except Halt:
                    break
        return computation
Beispiel #2
0
    def apply_computation(cls,
                          state: BaseState,
                          message: Message,
                          transaction_context: BaseTransactionContext) -> 'BaseComputation':
        """
        Perform the computation that would be triggered by the VM message.
        """
        with cls(state, message, transaction_context) as computation:
            # Early exit on pre-compiles
            precompile = computation.precompiles.get(message.code_address, NO_RESULT)
            if precompile is not NO_RESULT:
                precompile(computation)
                return computation

            show_debug2 = computation.logger.show_debug2

            opcode_lookup = computation.opcodes
            for opcode in computation.code:
                try:
                    opcode_fn = opcode_lookup[opcode]
                except KeyError:
                    opcode_fn = InvalidOpcode(opcode)

                if show_debug2:
                    computation.logger.debug2(
                        "OPCODE: 0x%x (%s) | pc: %s",
                        opcode,
                        opcode_fn.mnemonic,
                        max(0, computation.code.pc - 1),
                    )

                try:
                    opcode_fn(computation=computation)
                except Halt:
                    break
        return computation
Beispiel #3
0
    def init_debug_session_signal_cb(self, code: CodeStreamAPI,
                                     opcode_lookup: Dict[int, OpcodeAPI],
                                     message: MessageAPI):
        """
        :param code: Must be a deepcopy() version of the real code object.
        :param opcode_lookup:
        :param message:
        :return:
        """
        logger.info("Init_debug signal received")

        # self.history.init = (code, opcode_lookup, message)
        # self.change_chains: [ChangeChain] = []

        self._clear_table_widget(TableWidgetEnum.OPCODES)
        self._clear_table_widget(TableWidgetEnum.STACK)
        self._clear_table_widget(TableWidgetEnum.MEMORY)

        # reset the program counter, it might not be 0 and we need the whole thing.
        code.pc = 0

        # this variable will be used to skip rounds of the iteration.
        # e.g. we read in the opcode push1 -> we will skip the next round (of drawing mnemonic)
        # e.g. we read push10 -> we skip 10 rounds
        skip = 0
        for opcode in code:
            c = self.ui.opcodes_table_widget.rowCount()
            self.ui.opcodes_table_widget.insertRow(c)
            try:
                opcode_fn = opcode_lookup[opcode]
            except KeyError:
                opcode_fn = InvalidOpcode(opcode)

            o = QTableWidgetItem()
            o.setText(hex2(opcode))
            self.ui.opcodes_table_widget.setItem(c, 0, o)

            if skip > 0:
                skip = skip - 1
                continue

            m = QTableWidgetItem()
            g = QTableWidgetItem()
            m.setText(opcode_fn.mnemonic)
            g.setText(str(opcode_fn.gas_cost))
            g.setTextAlignment(130)
            self.ui.opcodes_table_widget.setItem(c, 1, m)
            self.ui.opcodes_table_widget.setItem(c, 2, g)
            if 0x60 <= opcode <= 0x7f:
                skip = opcode - 0x5f

        self.ui.origin_label.setText("origin: 0x" + MASTER_ADDRESS.hex())
        self.ui.origin_label.setToolTip("origin: 0x" + MASTER_ADDRESS.hex())
        self.ui.from_label.setText("from: 0x" + message.sender.hex())
        self.ui.from_label.setToolTip("from: 0x" + message.sender.hex())
        self.ui.to_label.setText("to: 0x" + message.to.hex())
        self.ui.to_label.setToolTip("to: 0x" + message.to.hex())
        self.ui.value_label.setText("value: " + str(message.value))
        self.ui.value_label.setToolTip("value: " + str(message.value))
        self.ui.gas_limit_label.setText("gas limit: " + str(message.gas))
        self.ui.gas_limit_label.setToolTip("gas limit: " + str(message.gas))
        if message.is_create:
            self.ui.data_label.setText("data: 0x" + message.code.hex()[:8] +
                                       "...")
            self.ui.data_label.setToolTip("<FONT COLOR=white>0x" +
                                          message.code.hex() + "</FONT>")
        else:
            self.ui.data_label.setText("data: 0x" + message.data.hex()[:8] +
                                       "...")
            self.ui.data_label.setToolTip("<FONT COLOR=white>0x" +
                                          message.data.hex() + "</FONT>")
        self.ui.call_depth_label.setText("call depth: " + str(message.depth))
        self.ui.call_depth_label.setToolTip("call depth: " +
                                            str(message.depth))

        self._refresh_storage(message.storage_address)

        # I think the processEvents function does also process signals in the background. This isn't really documented
        # anywhere besides at some places in the docs where signals are also called events. The reason why I am
        # thinking that is that when this line is reached, the pre_computation_cb callback is called immediately
        # afterwards, which should not be and is only the case if there already is a signal in the queue waiting to be
        # handled and the processEvents function handles all the signals before updating the gui (which
        # is what we actually want).
        # To circumvent this, we need to put a lock in the worker thread that locks immediately after
        # firing the init_debug signal and will only release after processEvents has been called. That way the worker
        # thread will not add another signal to the queue before the current events (namely updating the gui which
        # is happening in this function) are processed.

        qApp.processEvents()
        self.init_lock.release()
Beispiel #4
0
 def get_opcode_fn(self, opcode: int) -> Opcode:
     try:
         return self.opcodes[opcode]
     except KeyError:
         return InvalidOpcode(opcode)
Beispiel #5
0
    def apply_computation(cls,
                          state: StateAPI,
                          message: MessageAPI,
                          transaction_context: TransactionContextAPI) -> ComputationAPI:
        """
        Perform the computation that would be triggered by the VM message.
        """
        cls.parse_kwargs()
        with cls(state, message, transaction_context) as computation:
            # Early exit on pre-compiles
            precompile = computation.precompiles.get(message.code_address, NO_RESULT)
            if precompile is not NO_RESULT:
                precompile(computation)
                return computation

            opcode_lookup = computation.opcodes

            if cls.debug_mode:
                cls.init_debug_session.emit(
                    deepcopy(computation.code), computation.opcodes, message
                )
                cls.init_lock.acquire(True)

            for opcode in computation.code:
                try:
                    opcode_fn = opcode_lookup[opcode]
                except KeyError:
                    opcode_fn = InvalidOpcode(opcode)

                try:
                    if cls.abort:
                        logger.warning("Abort has been received")
                        raise Halt

                    if cls.debug_mode == MODE_DEBUG:
                        cls.step_semaphore.acquire(1)
                        cls.before_computation(computation, opcode, opcode_fn)
                    elif cls.debug_mode == MODE_DEBUG_AUTO:
                        sleep(cls.step_duration)
                        cls.before_computation(computation, opcode, opcode_fn)
                    # Since we cannot really read out every storage position that has been filled, we need
                    # to keep track of every storage slot that gets filled from the beginning of a contracts
                    # creation. This has to be done regardless of whether the user has activated debug mode or not,
                    # since else we might never get to see a storage variable ever again.
                    slot = ""
                    value = ""
                    if opcode == SSTORE:
                        # first param is key second param is value
                        arr = get_stack_content(computation._stack.values, 2)
                        slot = arr[0]
                        value = arr[1]
                    opcode_fn(computation=computation)
                    if cls.returned:
                        cls.init_debug_session.emit(deepcopy(computation.code), computation.opcodes, message)
                        cls.init_lock.acquire(True)
                        cls.returned = False
                    if opcode == REVERT:
                        logger.info("Revert has been processed")
                        cls.abort = True
                        raise Halt
                        pass
                    elif opcode == SSTORE:
                        cls.set_storage.emit(message.storage_address, slot, value)
                        if cls.debug_mode:
                            cls.storage_lock.acquire(True)
                    if cls.debug_mode == MODE_DEBUG:
                        cls.step_semaphore.acquire(1)
                        cls.after_computation(computation, cls.last_consumed_gas_amount)
                    elif cls.debug_mode == MODE_DEBUG_AUTO:
                        sleep(cls.step_duration)
                        cls.after_computation(computation, cls.last_consumed_gas_amount)
                except Halt:
                    # if current opcode is RETURN, opcode_fn() will throw exception before next line is reached
                    # this is to ensure that the UI gets updated in the last iteration as well
                    if cls.debug_mode and not cls.abort:
                        cls.returned = True
                        cls.after_computation(computation, cls.last_consumed_gas_amount)
                    elif cls.abort:
                        cls.abort_transaction.emit()
                    break
        return computation
Beispiel #6
0
def fuzz_apply_computation(cls, state, message, transaction_context):
    cls = cls.__class__
    with cls(state, message, transaction_context) as computation:

        # Early exit on pre-compiles
        from eth.vm.computation import NO_RESULT
        precompile = computation.precompiles.get(message.code_address,
                                                 NO_RESULT)
        if precompile is not NO_RESULT:
            precompile(computation)
            return computation

        opcode_lookup = computation.opcodes
        computation.trace = list()
        previous_stack = []
        previous_call_address = None
        memory = None

        for opcode in computation.code:
            try:
                opcode_fn = opcode_lookup[opcode]
            except KeyError:
                from eth.vm.logic.invalid import InvalidOpcode
                opcode_fn = InvalidOpcode(opcode)

            from eth.exceptions import Halt
            from copy import deepcopy

            previous_pc = computation.code.pc
            previous_gas = computation.get_gas_remaining()

            try:
                if opcode == 0x42:  # TIMESTAMP
                    fuzz_timestamp_opcode_fn(computation=computation)
                elif opcode == 0x43:  # NUMBER
                    fuzz_blocknumber_opcode_fn(computation=computation)
                elif opcode == 0x31:  # BALANCE
                    fuzz_balance_opcode_fn(computation=computation,
                                           opcode_fn=opcode_fn)
                elif opcode == 0xf1:  # CALL
                    previous_call_address = fuzz_call_opcode_fn(
                        computation=computation, opcode_fn=opcode_fn)
                elif opcode == 0x3b:  # EXTCODESIZE
                    fuzz_extcodesize_opcode_fn(computation=computation,
                                               opcode_fn=opcode_fn)
                elif opcode == 0x3d:  # RETURNDATASIZE
                    fuzz_returndatasize_opcode_fn(previous_call_address,
                                                  computation=computation,
                                                  opcode_fn=opcode_fn)
                elif opcode == 0x20:  # SHA3
                    start_position, size = computation.stack_pop_ints(2)
                    memory = computation.memory_read_bytes(
                        start_position, size)
                    computation.stack_push_int(size)
                    computation.stack_push_int(start_position)
                    opcode_fn(computation=computation)
                else:
                    opcode_fn(computation=computation)
            except Halt:
                break
            finally:
                computation.trace.append({
                    "pc":
                    max(0, previous_pc - 1),
                    "op":
                    opcode_fn.mnemonic,
                    "depth":
                    computation.msg.depth + 1,
                    "error":
                    deepcopy(computation._error),
                    "stack":
                    previous_stack,
                    "memory":
                    memory,
                    "gas":
                    computation.get_gas_remaining(),
                    "gas_used_by_opcode":
                    previous_gas - computation.get_gas_remaining()
                })
                previous_stack = list(computation._stack.values)
    return computation