Example #1
0
    def parse_Subscript(self):
        sub = Expr(self.expr.value, self.context).lll_node
        if sub.value == "multi":
            # force literal to memory, e.g.
            # MY_LIST: constant(decimal[6])
            # ...
            # return MY_LIST[ix]
            sub = ensure_in_memory(sub, self.context, pos=getpos(self.expr))

        if isinstance(sub.typ, MappingType):
            # TODO sanity check we are in a self.my_map[i] situation
            index = Expr.parse_value_expr(self.expr.slice.value, self.context)
            if isinstance(index.typ, ByteArrayLike):
                # we have to hash the key to get a storage location
                assert len(index.args) == 1
                index = keccak256_helper(self.expr.slice.value, index.args[0],
                                         self.context)

        elif isinstance(sub.typ, ArrayLike):
            index = Expr.parse_value_expr(self.expr.slice.value, self.context)

        elif isinstance(sub.typ, TupleType):
            index = self.expr.slice.value.n
            # note: this check should also happen in get_element_ptr
            if not 0 <= index < len(sub.typ.members):
                return
        else:
            return

        lll_node = get_element_ptr(sub, index, pos=getpos(self.expr))
        lll_node.mutable = sub.mutable
        return lll_node
Example #2
0
def _register_function_args(context: Context, sig: FunctionSignature) -> List[LLLnode]:
    pos = None

    ret = []

    # the type of the calldata
    base_args_t = TupleType([arg.typ for arg in sig.base_args])

    # tuple with the abi_encoded args
    if sig.is_init_func:
        base_args_ofst = LLLnode(0, location="data", typ=base_args_t, encoding=Encoding.ABI)
    else:
        base_args_ofst = LLLnode(4, location="calldata", typ=base_args_t, encoding=Encoding.ABI)

    for i, arg in enumerate(sig.base_args):

        arg_lll = get_element_ptr(base_args_ofst, i, pos=pos)

        if _should_decode(arg.typ):
            # allocate a memory slot for it and copy
            p = context.new_variable(arg.name, arg.typ, is_mutable=False)
            dst = LLLnode(p, typ=arg.typ, location="memory")
            ret.append(make_setter(dst, arg_lll, pos=pos))
        else:
            # leave it in place
            context.vars[arg.name] = VariableRecord(
                name=arg.name,
                pos=arg_lll,
                typ=arg.typ,
                mutable=False,
                location=arg_lll.location,
                encoding=Encoding.ABI,
            )

    return ret
Example #3
0
    def handler_for(calldata_kwargs, default_kwargs):
        calldata_args = sig.base_args + calldata_kwargs
        # create a fake type so that get_element_ptr works
        calldata_args_t = TupleType(list(arg.typ for arg in calldata_args))

        abi_sig = sig.abi_signature_for_kwargs(calldata_kwargs)
        method_id = _annotated_method_id(abi_sig)

        calldata_kwargs_ofst = IRnode(
            4, location=CALLDATA, typ=calldata_args_t, encoding=Encoding.ABI
        )

        # a sequence of statements to strictify kwargs into memory
        ret = ["seq"]

        # ensure calldata is at least of minimum length
        args_abi_t = calldata_args_t.abi_type
        calldata_min_size = args_abi_t.min_size() + 4
        if args_abi_t.is_dynamic():
            ret.append(["assert", ["ge", "calldatasize", calldata_min_size]])
        else:
            # stricter for static data
            ret.append(["assert", ["eq", "calldatasize", calldata_min_size]])

        # TODO optimize make_setter by using
        # TupleType(list(arg.typ for arg in calldata_kwargs + default_kwargs))
        # (must ensure memory area is contiguous)

        n_base_args = len(sig.base_args)

        for i, arg_meta in enumerate(calldata_kwargs):
            k = n_base_args + i

            dst = context.lookup_var(arg_meta.name).pos

            lhs = IRnode(dst, location=MEMORY, typ=arg_meta.typ)

            rhs = get_element_ptr(calldata_kwargs_ofst, k, array_bounds_check=False)

            copy_arg = make_setter(lhs, rhs)
            copy_arg.source_pos = getpos(arg_meta.ast_source)
            ret.append(copy_arg)

        for x in default_kwargs:
            dst = context.lookup_var(x.name).pos
            lhs = IRnode(dst, location=MEMORY, typ=x.typ)
            lhs.source_pos = getpos(x.ast_source)
            kw_ast_val = sig.default_values[x.name]  # e.g. `3` in x: int = 3
            rhs = Expr(kw_ast_val, context).ir_node

            copy_arg = make_setter(lhs, rhs)
            copy_arg.source_pos = getpos(x.ast_source)
            ret.append(copy_arg)

        ret.append(["goto", sig.external_function_base_entry_label])

        ret = ["if", ["eq", "_calldata_method_id", method_id], ret]
        return ret
Example #4
0
    def _parse_For_list(self):
        with self.context.range_scope():
            iter_list = Expr(self.stmt.iter, self.context).lll_node

        # override with type inferred at typechecking time
        # TODO investigate why stmt.target.type != stmt.iter.type.subtype
        target_type = new_type_to_old_type(self.stmt.target._metadata["type"])
        iter_list.typ.subtype = target_type

        # user-supplied name for loop variable
        varname = self.stmt.target.id
        loop_var = LLLnode.from_list(
            self.context.new_variable(varname, target_type),
            typ=target_type,
            location="memory",
        )

        i = LLLnode.from_list(self.context.fresh_varname("for_list_ix"),
                              typ="uint256")

        self.context.forvars[varname] = True

        ret = ["seq"]

        # list literal, force it to memory first
        if isinstance(self.stmt.iter, vy_ast.List):
            tmp_list = LLLnode.from_list(
                self.context.new_internal_variable(iter_list.typ),
                typ=iter_list.typ,
                location="memory",
            )
            ret.append(make_setter(tmp_list, iter_list, pos=getpos(self.stmt)))
            iter_list = tmp_list

        # set up the loop variable
        loop_var_ast = getpos(self.stmt.target)
        e = get_element_ptr(iter_list,
                            i,
                            array_bounds_check=False,
                            pos=loop_var_ast)
        body = [
            "seq",
            make_setter(loop_var, e, pos=loop_var_ast),
            parse_body(self.stmt.body, self.context),
        ]

        repeat_bound = iter_list.typ.count
        if isinstance(iter_list.typ, DArrayType):
            array_len = get_dyn_array_count(iter_list)
        else:
            array_len = repeat_bound

        ret.append(["repeat", i, 0, array_len, repeat_bound, body])

        del self.context.forvars[varname]
        return LLLnode.from_list(ret, pos=getpos(self.stmt))
Example #5
0
def _encode_dyn_array_helper(dst, ir_node, context):
    # if it's a literal, first serialize to memory as we
    # don't have a compile-time abi encoder
    # TODO handle this upstream somewhere
    if ir_node.value == "multi":
        buf = context.new_internal_variable(dst.typ)
        buf = IRnode.from_list(buf, typ=dst.typ, location=MEMORY)
        _bufsz = dst.typ.abi_type.size_bound()
        return [
            "seq",
            make_setter(buf, ir_node),
            [
                "set", "dyn_ofst",
                abi_encode(dst, buf, context, _bufsz, returns_len=True)
            ],
        ]

    subtyp = ir_node.typ.subtype
    child_abi_t = subtyp.abi_type

    ret = ["seq"]

    len_ = get_dyn_array_count(ir_node)
    with len_.cache_when_complex("len") as (b, len_):
        # set the length word
        ret.append(STORE(dst, len_))

        # prepare the loop
        t = BaseType("uint256")
        i = IRnode.from_list(context.fresh_varname("ix"), typ=t)

        # offset of the i'th element in ir_node
        child_location = get_element_ptr(ir_node, i, array_bounds_check=False)

        # offset of the i'th element in dst
        dst = add_ofst(dst, 32)  # jump past length word
        static_elem_size = child_abi_t.embedded_static_size()
        static_ofst = ["mul", i, static_elem_size]
        loop_body = _encode_child_helper(dst, child_location, static_ofst,
                                         "dyn_child_ofst", context)
        loop = ["repeat", i, 0, len_, ir_node.typ.count, loop_body]

        x = ["seq", loop, "dyn_child_ofst"]
        start_dyn_ofst = ["mul", len_, static_elem_size]
        run_children = ["with", "dyn_child_ofst", start_dyn_ofst, x]
        new_dyn_ofst = ["add", "dyn_ofst", run_children]
        # size of dynarray is size of encoded children + size of the length word
        # TODO optimize by adding 32 to the initial value of dyn_ofst
        new_dyn_ofst = ["add", 32, new_dyn_ofst]
        ret.append(["set", "dyn_ofst", new_dyn_ofst])

        return b.resolve(ret)
Example #6
0
def _deconstruct_complex_type(ir_node):
    ir_t = ir_node.typ
    assert isinstance(ir_t, (TupleLike, SArrayType))

    if isinstance(ir_t, TupleLike):
        ks = ir_t.tuple_keys()
    else:
        ks = [IRnode.from_list(i, "uint256") for i in range(ir_t.count)]

    ret = []
    for k in ks:
        ret.append(get_element_ptr(ir_node, k, array_bounds_check=False))
    return ret
Example #7
0
def _unpack_returndata(buf, contract_sig, skip_contract_check, context, pos):
    return_t = contract_sig.return_type
    if return_t is None:
        return ["pass"], 0, 0

    return_t = calculate_type_for_external_return(return_t)
    # if the abi signature has a different type than
    # the vyper type, we need to wrap and unwrap the type
    # so that the ABI decoding works correctly
    should_unwrap_abi_tuple = return_t != contract_sig.return_type

    abi_return_t = return_t.abi_type

    min_return_size = abi_return_t.min_size()
    max_return_size = abi_return_t.size_bound()
    assert 0 < min_return_size <= max_return_size

    ret_ofst = buf
    ret_len = max_return_size

    # revert when returndatasize is not in bounds
    ret = []
    # runtime: min_return_size <= returndatasize
    # TODO move the -1 optimization to LLL optimizer
    if not skip_contract_check:
        ret += [["assert", ["gt", "returndatasize", min_return_size - 1]]]

    # add as the last LLLnode a pointer to the return data structure

    # the return type has been wrapped by the calling contract;
    # unwrap it so downstream code isn't confused.
    # basically this expands to buf+32 if the return type has been wrapped
    # in a tuple AND its ABI type is dynamic.
    # in most cases, this simply will evaluate to ret.
    # in the special case where the return type has been wrapped
    # in a tuple AND its ABI type is dynamic, it expands to buf+32.
    buf = LLLnode(buf,
                  typ=return_t,
                  encoding=_returndata_encoding(contract_sig),
                  location="memory")

    if should_unwrap_abi_tuple:
        buf = get_element_ptr(buf, 0, pos=None, array_bounds_check=False)

    ret += [buf]

    return ret, ret_ofst, ret_len
Example #8
0
    def handler_for(calldata_kwargs, default_kwargs):
        calldata_args = sig.base_args + calldata_kwargs
        # create a fake type so that get_element_ptr works
        calldata_args_t = TupleType(list(arg.typ for arg in calldata_args))

        abi_sig = sig.abi_signature_for_kwargs(calldata_kwargs)
        method_id = _annotated_method_id(abi_sig)

        calldata_kwargs_ofst = LLLnode(4,
                                       location="calldata",
                                       typ=calldata_args_t,
                                       encoding=Encoding.ABI)

        # a sequence of statements to strictify kwargs into memory
        ret = ["seq"]

        # TODO optimize make_setter by using
        # TupleType(list(arg.typ for arg in calldata_kwargs + default_kwargs))
        # (must ensure memory area is contiguous)

        n_base_args = len(sig.base_args)

        for i, arg_meta in enumerate(calldata_kwargs):
            k = n_base_args + i

            dst = context.lookup_var(arg_meta.name).pos

            lhs = LLLnode(dst, location="memory", typ=arg_meta.typ)
            rhs = get_element_ptr(calldata_kwargs_ofst,
                                  k,
                                  pos=None,
                                  array_bounds_check=False)
            ret.append(make_setter(lhs, rhs, pos))

        for x in default_kwargs:
            dst = context.lookup_var(x.name).pos
            lhs = LLLnode(dst, location="memory", typ=x.typ)
            kw_ast_val = sig.default_values[x.name]  # e.g. `3` in x: int = 3
            rhs = Expr(kw_ast_val, context).lll_node
            ret.append(make_setter(lhs, rhs, pos))

        ret.append(["goto", sig.external_function_base_entry_label])

        ret = ["if", ["eq", "_calldata_method_id", method_id], ret]
        return ret
Example #9
0
def _register_function_args(context: Context,
                            sig: FunctionSignature) -> List[IRnode]:
    ret = []

    # the type of the calldata
    base_args_t = TupleType([arg.typ for arg in sig.base_args])

    # tuple with the abi_encoded args
    if sig.is_init_func:
        base_args_ofst = IRnode(0,
                                location=DATA,
                                typ=base_args_t,
                                encoding=Encoding.ABI)
    else:
        base_args_ofst = IRnode(4,
                                location=CALLDATA,
                                typ=base_args_t,
                                encoding=Encoding.ABI)

    for i, arg in enumerate(sig.base_args):

        arg_ir = get_element_ptr(base_args_ofst, i)

        if _should_decode(arg.typ):
            # allocate a memory slot for it and copy
            p = context.new_variable(arg.name, arg.typ, is_mutable=False)
            dst = IRnode(p, typ=arg.typ, location=MEMORY)

            copy_arg = make_setter(dst, arg_ir)
            copy_arg.source_pos = getpos(arg.ast_source)
            ret.append(copy_arg)
        else:
            # leave it in place
            context.vars[arg.name] = VariableRecord(
                name=arg.name,
                pos=arg_ir,
                typ=arg.typ,
                mutable=False,
                location=arg_ir.location,
                encoding=Encoding.ABI,
            )

    return ret
Example #10
0
    def build_in_comparator(self):
        left = Expr(self.expr.left, self.context).lll_node
        right = Expr(self.expr.right, self.context).lll_node

        # temporary kludge to block #2637 bug
        # TODO actually fix the bug
        if not isinstance(left.typ, BaseType):
            raise TypeMismatch(
                "`in` not allowed for arrays of non-base types, tracked in issue #2637",
                self.expr)

        if isinstance(self.expr.op, vy_ast.In):
            found, not_found = 1, 0
        elif isinstance(self.expr.op, vy_ast.NotIn):
            found, not_found = 0, 1
        else:
            return  # pragma: notest

        i = LLLnode.from_list(self.context.fresh_varname("in_ix"),
                              typ="uint256")

        found_ptr = self.context.new_internal_variable(BaseType("bool"))

        ret = ["seq"]

        left = unwrap_location(left)
        with left.cache_when_complex("needle") as (
                b1, left), right.cache_when_complex("haystack") as (b2, right):
            if right.value == "multi":
                # Copy literal to memory to be compared.
                tmp_list = LLLnode.from_list(
                    self.context.new_internal_variable(right.typ),
                    typ=right.typ,
                    location="memory",
                )
                ret.append(make_setter(tmp_list, right, pos=getpos(self.expr)))

                right = tmp_list

            # location of i'th item from list
            pos = getpos(self.expr)
            ith_element_ptr = get_element_ptr(right,
                                              i,
                                              array_bounds_check=False,
                                              pos=pos)
            ith_element = unwrap_location(ith_element_ptr)

            if isinstance(right.typ, SArrayType):
                len_ = right.typ.count
            else:
                len_ = get_dyn_array_count(right)

            # Condition repeat loop has to break on.
            # TODO maybe put result on the stack
            loop_body = [
                "if",
                ["eq", left, ith_element],
                ["seq", ["mstore", found_ptr, found], "break"],  # store true.
            ]
            loop = ["repeat", i, 0, len_, right.typ.count, loop_body]

            ret.append([
                "seq",
                ["mstore", found_ptr, not_found],
                loop,
                ["mload", found_ptr],
            ])

            return LLLnode.from_list(b1.resolve(b2.resolve(ret)), typ="bool")
Example #11
0
 def parse_Attribute(self):
     # x.balance: balance of address x
     if self.expr.attr == "balance":
         addr = Expr.parse_value_expr(self.expr.value, self.context)
         if is_base_type(addr.typ, "address"):
             if (isinstance(self.expr.value, vy_ast.Name)
                     and self.expr.value.id == "self"
                     and version_check(begin="istanbul")):
                 seq = ["selfbalance"]
             else:
                 seq = ["balance", addr]
             return LLLnode.from_list(
                 seq,
                 typ=BaseType("uint256"),
                 location=None,
                 pos=getpos(self.expr),
             )
     # x.codesize: codesize of address x
     elif self.expr.attr == "codesize" or self.expr.attr == "is_contract":
         addr = Expr.parse_value_expr(self.expr.value, self.context)
         if is_base_type(addr.typ, "address"):
             if self.expr.attr == "codesize":
                 if self.expr.value.id == "self":
                     eval_code = ["codesize"]
                 else:
                     eval_code = ["extcodesize", addr]
                 output_type = "uint256"
             else:
                 eval_code = ["gt", ["extcodesize", addr], 0]
                 output_type = "bool"
             return LLLnode.from_list(
                 eval_code,
                 typ=BaseType(output_type),
                 location=None,
                 pos=getpos(self.expr),
             )
     # x.codehash: keccak of address x
     elif self.expr.attr == "codehash":
         addr = Expr.parse_value_expr(self.expr.value, self.context)
         if not version_check(begin="constantinople"):
             raise EvmVersionException(
                 "address.codehash is unavailable prior to constantinople ruleset",
                 self.expr)
         if is_base_type(addr.typ, "address"):
             return LLLnode.from_list(
                 ["extcodehash", addr],
                 typ=BaseType("bytes32"),
                 location=None,
                 pos=getpos(self.expr),
             )
     # x.code: codecopy/extcodecopy of address x
     elif self.expr.attr == "code":
         addr = Expr.parse_value_expr(self.expr.value, self.context)
         if is_base_type(addr.typ, "address"):
             # These adhoc nodes will be replaced with a valid node in `Slice.build_LLL`
             if addr.value == "address":  # for `self.code`
                 return LLLnode.from_list(["~selfcode"],
                                          typ=ByteArrayType(0))
             return LLLnode.from_list(["~extcode", addr],
                                      typ=ByteArrayType(0))
     # self.x: global attribute
     elif isinstance(self.expr.value,
                     vy_ast.Name) and self.expr.value.id == "self":
         type_ = self.expr._metadata["type"]
         var = self.context.globals[self.expr.attr]
         return LLLnode.from_list(
             type_.position.position,
             typ=var.typ,
             location="storage",
             pos=getpos(self.expr),
             annotation="self." + self.expr.attr,
         )
     # Reserved keywords
     elif (isinstance(self.expr.value, vy_ast.Name)
           and self.expr.value.id in ENVIRONMENT_VARIABLES):
         key = f"{self.expr.value.id}.{self.expr.attr}"
         if key == "msg.sender":
             return LLLnode.from_list(["caller"],
                                      typ="address",
                                      pos=getpos(self.expr))
         elif key == "msg.data":
             # This adhoc node will be replaced with a valid node in `Slice/Len.build_LLL`
             return LLLnode.from_list(["~calldata"], typ=ByteArrayType(0))
         elif key == "msg.value" and self.context.is_payable:
             return LLLnode.from_list(
                 ["callvalue"],
                 typ=BaseType("uint256"),
                 pos=getpos(self.expr),
             )
         elif key == "msg.gas":
             return LLLnode.from_list(
                 ["gas"],
                 typ="uint256",
                 pos=getpos(self.expr),
             )
         elif key == "block.difficulty":
             return LLLnode.from_list(
                 ["difficulty"],
                 typ="uint256",
                 pos=getpos(self.expr),
             )
         elif key == "block.timestamp":
             return LLLnode.from_list(
                 ["timestamp"],
                 typ=BaseType("uint256"),
                 pos=getpos(self.expr),
             )
         elif key == "block.coinbase":
             return LLLnode.from_list(["coinbase"],
                                      typ="address",
                                      pos=getpos(self.expr))
         elif key == "block.number":
             return LLLnode.from_list(["number"],
                                      typ="uint256",
                                      pos=getpos(self.expr))
         elif key == "block.gaslimit":
             return LLLnode.from_list(["gaslimit"],
                                      typ="uint256",
                                      pos=getpos(self.expr))
         elif key == "block.basefee":
             return LLLnode.from_list(["basefee"],
                                      typ="uint256",
                                      pos=getpos(self.expr))
         elif key == "block.prevhash":
             return LLLnode.from_list(
                 ["blockhash", ["sub", "number", 1]],
                 typ="bytes32",
                 pos=getpos(self.expr),
             )
         elif key == "tx.origin":
             return LLLnode.from_list(["origin"],
                                      typ="address",
                                      pos=getpos(self.expr))
         elif key == "tx.gasprice":
             return LLLnode.from_list(["gasprice"],
                                      typ="uint256",
                                      pos=getpos(self.expr))
         elif key == "chain.id":
             if not version_check(begin="istanbul"):
                 raise EvmVersionException(
                     "chain.id is unavailable prior to istanbul ruleset",
                     self.expr)
             return LLLnode.from_list(["chainid"],
                                      typ="uint256",
                                      pos=getpos(self.expr))
     # Other variables
     else:
         sub = Expr(self.expr.value, self.context).lll_node
         # contract type
         if isinstance(sub.typ, InterfaceType):
             return sub
         if isinstance(sub.typ,
                       StructType) and self.expr.attr in sub.typ.members:
             return get_element_ptr(sub,
                                    self.expr.attr,
                                    pos=getpos(self.expr))