예제 #1
0
def canonicalize_type(t, is_indexed=False):
    if isinstance(t, ByteArrayLike):
        # Check to see if maxlen is small enough for events
        byte_type = 'string' if isinstance(t, StringType) else 'bytes'
        if is_indexed:
            return f'{byte_type}{t.maxlen}'
        else:
            return f'{byte_type}'

    if isinstance(t, ListType):
        if not isinstance(t.subtype, (ListType, BaseType)):
            raise InvalidType(f"List of {t.subtype}s not allowed")
        return canonicalize_type(t.subtype) + f"[{t.count}]"

    if isinstance(t, TupleLike):
        return f"({','.join(canonicalize_type(x) for x in t.tuple_members())})"

    if not isinstance(t, BaseType):
        raise InvalidType(f"Cannot canonicalize non-base type: {t}")

    t = t.typ
    if t in ('int128', 'uint256', 'bool', 'address', 'bytes32'):
        return t
    elif t == 'decimal':
        return 'fixed168x10'

    raise InvalidType(f"Invalid or unsupported type: {repr(t)}")
예제 #2
0
def get_static_size_of_type(typ):
    if isinstance(typ, BaseType):
        return 1
    elif isinstance(typ, ByteArrayLike):
        return 1
    elif isinstance(typ, ListType):
        return get_size_of_type(typ.subtype) * typ.count
    elif isinstance(typ, MappingType):
        raise InvalidType(
            "Maps are not supported for function arguments or outputs.")
    elif isinstance(typ, TupleLike):
        return sum([get_size_of_type(v) for v in typ.tuple_members()])
    else:
        raise InvalidType(
            f"Can not get size of type, Unexpected type: {repr(typ)}")
예제 #3
0
def get_size_of_type(typ):
    if isinstance(typ, BaseType):
        return 1
    elif isinstance(typ, ByteArrayLike):
        # 1 word for offset (in static section), 1 word for length,
        # up to maxlen words for actual data.
        return ceil32(typ.maxlen) // 32 + 2
    elif isinstance(typ, ListType):
        return get_size_of_type(typ.subtype) * typ.count
    elif isinstance(typ, MappingType):
        raise InvalidType(
            "Maps are not supported for function arguments or outputs.")
    elif isinstance(typ, TupleLike):
        return sum([get_size_of_type(v) for v in typ.tuple_members()])
    else:
        raise InvalidType(
            f"Can not get size of type, Unexpected type: {repr(typ)}")
예제 #4
0
def has_dynamic_data(typ):
    if isinstance(typ, BaseType):
        return False
    elif isinstance(typ, ByteArrayLike):
        return True
    elif isinstance(typ, ListType):
        return has_dynamic_data(typ.subtype)
    elif isinstance(typ, TupleLike):
        return any([has_dynamic_data(v) for v in typ.tuple_members()])
    else:
        raise InvalidType(f"Unexpected type: {repr(typ)}")
예제 #5
0
def make_struct_type(name, location, members, custom_structs, constants):
    o = OrderedDict()

    for key, value in members:
        if not isinstance(key, sri_ast.Name):
            raise InvalidType(
                f"Invalid member variable for struct {key.id}, expected a name.",
                key,
            )
        check_valid_varname(
            key.id,
            custom_structs,
            constants,
            "Invalid member variable for struct",
        )
        o[key.id] = parse_type(
            value,
            location,
            custom_structs=custom_structs,
            constants=constants,
        )

    return StructType(o, name)
예제 #6
0
    def from_declaration(cls, code, global_ctx):
        name = code.target.id
        pos = 0

        check_valid_varname(name,
                            global_ctx._structs,
                            global_ctx._constants,
                            pos=code,
                            error_prefix="Event name invalid. ",
                            exc=EventDeclarationException)

        # Determine the arguments, expects something of the form def foo(arg1: num, arg2: num ...
        args = []
        indexed_list = []
        topics_count = 1
        if code.annotation.args:
            keys = code.annotation.args[0].keys
            values = code.annotation.args[0].values
            for i in range(len(keys)):
                typ = values[i]
                if not isinstance(keys[i], sri_ast.Name):
                    raise EventDeclarationException(
                        'Invalid key type, expected a valid name.',
                        keys[i],
                    )
                if not isinstance(
                        typ, (sri_ast.Name, sri_ast.Call, sri_ast.Subscript)):
                    raise EventDeclarationException(
                        'Invalid event argument type.', typ)
                if isinstance(typ, sri_ast.Call) and not isinstance(
                        typ.func, sri_ast.Name):
                    raise EventDeclarationException(
                        'Invalid event argument type', typ)
                arg = keys[i].id
                arg_item = keys[i]
                is_indexed = False

                # Check to see if argument is a topic
                if isinstance(typ, sri_ast.Call) and typ.func.id == 'indexed':
                    typ = values[i].args[0]
                    indexed_list.append(True)
                    topics_count += 1
                    is_indexed = True
                else:
                    indexed_list.append(False)
                if isinstance(typ, sri_ast.Subscript) and getattr(
                        typ.value, 'id', None
                ) == 'bytes' and typ.slice.value.n > 32 and is_indexed:  # noqa: E501
                    raise EventDeclarationException(
                        "Indexed arguments are limited to 32 bytes")
                if topics_count > 4:
                    raise EventDeclarationException(
                        f"Maximum of 3 topics {topics_count - 1} given",
                        arg,
                    )
                if not isinstance(arg, str):
                    raise VariableDeclarationException("Argument name invalid",
                                                       arg)
                if not typ:
                    raise InvalidType("Argument must have type", arg)
                check_valid_varname(
                    arg,
                    global_ctx._structs,
                    global_ctx._constants,
                    pos=arg_item,
                    error_prefix="Event argument name invalid or reserved.",
                )
                if arg in (x.name for x in args):
                    raise VariableDeclarationException(
                        "Duplicate function argument name: " + arg,
                        arg_item,
                    )
                # Can struct be logged?
                parsed_type = global_ctx.parse_type(typ, None)
                args.append(VariableRecord(arg, pos, parsed_type, False))
                if isinstance(parsed_type, ByteArrayType):
                    pos += ceil32(typ.slice.value.n)
                else:
                    pos += get_size_of_type(parsed_type) * 32
        sig = name + '(' + ','.join([
            canonicalize_type(arg.typ, indexed_list[pos])
            for pos, arg in enumerate(args)
        ]) + ')'  # noqa F812
        event_id = bytes_to_int(keccak256(bytes(sig, 'utf-8')))
        return cls(name, args, indexed_list, event_id, sig)
예제 #7
0
    def from_definition(cls,
                        code,
                        sigs=None,
                        custom_structs=None,
                        contract_def=False,
                        constants=None,
                        constant_override=False):
        if not custom_structs:
            custom_structs = {}

        name = code.name
        mem_pos = 0

        valid_name, msg = is_varname_valid(name, custom_structs, constants)
        if not valid_name and (not name.lower() in FUNCTION_WHITELIST):
            raise FunctionDeclarationException("Function name invalid. " + msg,
                                               code)

        # Validate default values.
        for default_value in getattr(code.args, 'defaults', []):
            validate_default_values(default_value)

        # Determine the arguments, expects something of the form def foo(arg1:
        # int128, arg2: int128 ...
        args = []
        for arg in code.args.args:
            # Each arg needs a type specified.
            typ = arg.annotation
            if not typ:
                raise InvalidType("Argument must have type", arg)
            # Validate arg name.
            check_valid_varname(
                arg.arg,
                custom_structs,
                constants,
                arg,
                "Argument name invalid or reserved. ",
                FunctionDeclarationException,
            )
            # Check for duplicate arg name.
            if arg.arg in (x.name for x in args):
                raise FunctionDeclarationException(
                    "Duplicate function argument name: " + arg.arg,
                    arg,
                )
            parsed_type = parse_type(
                typ,
                None,
                sigs,
                custom_structs=custom_structs,
                constants=constants,
            )
            args.append(
                VariableRecord(
                    arg.arg,
                    mem_pos,
                    parsed_type,
                    False,
                    defined_at=getpos(arg),
                ))

            if isinstance(parsed_type, ByteArrayLike):
                mem_pos += 32
            else:
                mem_pos += get_size_of_type(parsed_type) * 32

        const = constant_override
        payable = False
        private = False
        public = False
        nonreentrant_key = ''

        # Update function properties from decorators
        for dec in code.decorator_list:
            if isinstance(dec, sri_ast.Name) and dec.id == "constant":
                const = True
            elif isinstance(dec, sri_ast.Name) and dec.id == "payable":
                payable = True
            elif isinstance(dec, sri_ast.Name) and dec.id == "private":
                private = True
            elif isinstance(dec, sri_ast.Name) and dec.id == "public":
                public = True
            elif isinstance(dec,
                            sri_ast.Call) and dec.func.id == "nonreentrant":
                if nonreentrant_key:
                    raise StructureException(
                        "Only one @nonreentrant decorator allowed per function",
                        dec)
                if dec.args and len(dec.args) == 1 and isinstance(
                        dec.args[0],
                        sri_ast.Str) and dec.args[0].s:  # noqa: E501
                    nonreentrant_key = dec.args[0].s
                else:
                    raise StructureException(
                        "@nonreentrant decorator requires a non-empty string to use as a key.",
                        dec)
            else:
                raise StructureException("Bad decorator", dec)

        if public and private:
            raise StructureException(
                f"Cannot use public and private decorators on the same function: {name}"
            )
        if payable and const:
            raise StructureException(
                f"Function {name} cannot be both constant and payable.")
        if payable and private:
            raise StructureException(
                f"Function {name} cannot be both private and payable.")
        if (not public and not private) and not contract_def:
            raise StructureException(
                "Function visibility must be declared (@public or @private)",
                code,
            )
        if const and nonreentrant_key:
            raise StructureException(
                "@nonreentrant makes no sense on a @constant function.", code)

        # Determine the return type and whether or not it's constant. Expects something
        # of the form:
        # def foo(): ...
        # def foo() -> int128: ...
        # If there is no return type, ie. it's of the form def foo(): ...
        # and NOT def foo() -> type: ..., then it's null
        if not code.returns:
            output_type = None
        elif isinstance(code.returns,
                        (sri_ast.Name, sri_ast.Compare, sri_ast.Subscript,
                         sri_ast.Call, sri_ast.Tuple)):
            output_type = parse_type(
                code.returns,
                None,
                sigs,
                custom_structs=custom_structs,
                constants=constants,
            )
        else:
            raise InvalidType(
                f"Output type invalid or unsupported: {parse_type(code.returns, None)}",
                code.returns,
            )
        # Output type must be canonicalizable
        if output_type is not None:
            assert isinstance(output_type,
                              TupleType) or canonicalize_type(output_type)
        # Get the canonical function signature
        sig = cls.get_full_sig(name, code.args.args, sigs, custom_structs,
                               constants)

        # Take the first 4 bytes of the hash of the sig to get the method ID
        method_id = fourbytes_to_int(keccak256(bytes(sig, 'utf-8'))[:4])
        return cls(name, args, output_type, const, payable, private,
                   nonreentrant_key, sig, method_id, code)
예제 #8
0
 def __init__(self, keytype, valuetype):
     if not isinstance(keytype, (BaseType, ByteArrayLike)):
         raise InvalidType("Dictionary keys must be a base type")
     self.keytype = keytype
     self.valuetype = valuetype
예제 #9
0
def parse_type(item, location, sigs=None, custom_structs=None, constants=None):
    # Base and custom types, e.g. num
    if isinstance(item, sri_ast.Name):
        if item.id in BASE_TYPES:
            return BaseType(item.id)
        elif (custom_structs is not None) and (item.id in custom_structs):
            return make_struct_type(
                item.id,
                location,
                custom_structs[item.id],
                custom_structs,
                constants,
            )
        else:
            raise InvalidType("Invalid base type: " + item.id, item)
    # Units, e.g. num (1/sec) or contracts
    elif isinstance(item, sri_ast.Call) and isinstance(item.func,
                                                       sri_ast.Name):
        # Mapping type.
        if item.func.id == 'map':
            if location == 'memory':
                raise InvalidType(
                    "No mappings allowed for in-memory types, only fixed-size arrays",
                    item,
                )
            if len(item.args) != 2:
                raise InvalidType(
                    "Mapping requires 2 valid positional arguments.",
                    item,
                )
            keytype = parse_type(
                item.args[0],
                None,
                custom_structs=custom_structs,
                constants=constants,
            )
            if not isinstance(keytype, (BaseType, ByteArrayLike)):
                raise InvalidType(
                    "Mapping keys must be base or bytes/string types", item)
            return MappingType(
                keytype,
                parse_type(
                    item.args[1],
                    location,
                    custom_structs=custom_structs,
                    constants=constants,
                ),
            )
        # Contract_types
        if item.func.id == 'address':
            if sigs and item.args[0].id in sigs:
                return ContractType(item.args[0].id)
        # Struct types
        if (custom_structs is not None) and (item.func.id in custom_structs):
            return make_struct_type(
                item.id,
                location,
                custom_structs[item.id],
                custom_structs,
                constants,
            )
        raise InvalidType("Units are no longer supported", item)
    # Subscripts
    elif isinstance(item, sri_ast.Subscript):
        # Fixed size lists or bytearrays, e.g. num[100]
        is_constant_val = constants.ast_is_constant(item.slice.value)
        if isinstance(item.slice.value, sri_ast.Int) or is_constant_val:
            n_val = (constants.get_constant(item.slice.value.id,
                                            context=None).value
                     if is_constant_val else item.slice.value.n)
            if not isinstance(n_val, int) or n_val <= 0:
                raise InvalidType(
                    "Arrays / ByteArrays must have a positive integral number of elements",
                    item.slice.value,
                )
            # ByteArray
            if getattr(item.value, 'id', None) == 'bytes':
                return ByteArrayType(n_val)
            elif getattr(item.value, 'id', None) == 'string':
                return StringType(n_val)
            # List
            else:
                return ListType(
                    parse_type(
                        item.value,
                        location,
                        custom_structs=custom_structs,
                        constants=constants,
                    ), n_val)
        # Mappings, e.g. num[address]
        else:
            warnings.warn(
                "Mapping definitions using subscript have deprecated (see VIP564). "
                "Use map(type1, type2) instead.", DeprecationWarning)
            raise InvalidType('Unknown list type.', item)

    # Dicts, used to represent mappings, e.g. {uint: uint}. Key must be a base type
    elif isinstance(item, sri_ast.Dict):
        warnings.warn(
            "Anonymous structs have been removed in"
            " favor of named structs, see VIP300", DeprecationWarning)
        raise InvalidType("Invalid type", item)
    elif isinstance(item, sri_ast.Tuple):
        members = [
            parse_type(x,
                       location,
                       custom_structs=custom_structs,
                       constants=constants) for x in item.elts
        ]
        return TupleType(members)
    else:
        raise InvalidType("Invalid type", item)