Esempio n. 1
0
 def _reverted_trace(self, trace: Sequence) -> None:
     self._modified_state = False
     # get events from trace
     self._events = _decode_trace(trace)
     if self._revert_msg is not None:
         return
     # get revert message
     step = next(i for i in trace if i["op"] in ("REVERT", "INVALID"))
     if step["op"] == "REVERT" and int(step["stack"][-2], 16):
         # get returned error string from stack
         data = _get_memory(step, -1)[4:]
         self._revert_msg = decode_abi(["string"], data)[0]
         return
     # check for dev revert string using program counter
     self._revert_msg = build._get_dev_revert(step["pc"])
     if self._revert_msg is not None:
         return
     # if none is found, expand the trace and get it from the pcMap
     self._expand_trace()
     try:
         pc_map = _find_contract(step["address"])._build["pcMap"]
         # if this is the function selector revert, check for a jump
         if "first_revert" in pc_map[step["pc"]]:
             i = trace.index(step) - 4
             if trace[i]["pc"] != step["pc"] - 4:
                 step = trace[i]
         self._revert_msg = pc_map[step["pc"]]["dev"]
     except KeyError:
         self._revert_msg = "invalid opcode" if step[
             "op"] == "INVALID" else ""
Esempio n. 2
0
    def _reverted_trace(self, trace: Sequence) -> None:
        self._modified_state = False
        # get events from trace
        self._events = _decode_trace(
            trace, str(self.receiver or self.contract_address))
        if self.contract_address:
            step = next((i for i in trace if i["op"] == "CODECOPY"), None)
            if step is not None and int(step["stack"][-3], 16) > 24577:
                self._revert_msg = "exceeds EIP-170 size limit"
        if self._revert_msg is not None:
            return

        # iterate over revert instructions in reverse to find revert message
        for step in (i for i in trace[::-1]
                     if i["op"] in ("REVERT", "INVALID")):
            if step["op"] == "REVERT" and int(step["stack"][-2], 16):
                # get returned error string from stack
                data = _get_memory(step, -1)[4:]
                self._revert_msg = decode_abi(["string"], data)[0]
                return
            if self.contract_address:
                self._revert_msg = "invalid opcode" if step[
                    "op"] == "INVALID" else ""
                return
            # check for dev revert string using program counter
            self._revert_msg = build._get_dev_revert(step["pc"])
            if self._revert_msg is not None:
                return
            # if none is found, expand the trace and get it from the pcMap
            self._expand_trace()
            try:
                pc_map = state._find_contract(step["address"])._build["pcMap"]
                # if this is the function selector revert, check for a jump
                if "first_revert" in pc_map[step["pc"]]:
                    i = trace.index(step) - 4
                    if trace[i]["pc"] != step["pc"] - 4:
                        step = trace[i]
                self._revert_msg = pc_map[step["pc"]]["dev"]
                return
            except (KeyError, AttributeError, TypeError):
                pass

        step = next(i for i in trace[::-1] if i["op"] in ("REVERT", "INVALID"))
        self._revert_msg = "invalid opcode" if step["op"] == "INVALID" else ""
Esempio n. 3
0
    def __init__(
        self,
        txid: Union[str, bytes],
        sender: Any = None,
        silent: bool = False,
        name: str = "",
        revert_data: Optional[Tuple] = None,
    ) -> None:
        """Instantiates a new TransactionReceipt object.

        Args:
            txid: hexstring transaction ID
            sender: sender as a hex string or Account object
            silent: toggles console verbosity
            name: contract function being called
            revert_data: (revert string, program counter, revert type)
        """
        if isinstance(txid, bytes):
            txid = txid.hex()
        if not silent:
            print(
                f"{color['key']}Transaction sent{color}: {color['value']}{txid}{color}"
            )
        history._add_tx(self)

        self._raw_trace = None
        self._trace = None
        self._events = None
        self._return_value = None
        self._revert_msg = None
        self._modified_state = None
        self._confirmed = threading.Event()

        self.sender = sender
        self.status = -1
        self.txid = txid
        self.fn_name = name

        if name and "." in name:
            self.contract_name, self.fn_name = name.split(".", maxsplit=1)

        # avoid querying the trace to get the revert string if possible
        revert_msg, self._revert_pc, revert_type = revert_data or (None, None,
                                                                   None)
        if revert_msg:
            # revert message was returned
            self._revert_msg = revert_msg
        elif revert_type in ("revert", "invalid opcode"):
            # check for dev revert string as a comment
            self._revert_msg = build._get_dev_revert(self._revert_pc)
        else:
            self._revert_msg = revert_type

        # threaded to allow impatient users to ctrl-c to stop waiting in the console
        confirm_thread = threading.Thread(target=self._await_confirmation,
                                          args=(silent, ),
                                          daemon=True)
        confirm_thread.start()
        try:
            confirm_thread.join()
            if ARGV["cli"] == "console":
                return
            # if coverage evaluation is active, evaluate the trace
            if ARGV["coverage"] and not coverage._check_cached(
                    self.coverage_hash) and self.trace:
                self._expand_trace()
            if not self.status:
                if self._revert_msg is None:
                    # no revert message and unable to check dev string - have to get trace
                    self._expand_trace()
                # raise from a new function to reduce pytest traceback length
                _raise(
                    self._revert_msg or "",
                    self._traceback_string()
                    if ARGV["revert"] else self._error_string(1),
                )
        except KeyboardInterrupt:
            if ARGV["cli"] != "console":
                raise