Пример #1
0
def base_type_conversion(orig, frm, to, pos=None):
    orig = unwrap_location(orig)
    if not isinstance(frm,
                      (BaseType, NullType)) or not isinstance(to, BaseType):
        raise TypeMismatchException(
            "Base type conversion from or to non-base type: %r %r" % (frm, to),
            pos)
    elif is_base_type(frm, to.typ) and are_units_compatible(frm, to):
        return LLLnode(orig.value,
                       orig.args,
                       typ=to,
                       add_gas_estimate=orig.add_gas_estimate)
    elif is_base_type(frm, 'num') and is_base_type(
            to, 'decimal') and are_units_compatible(frm, to):
        return LLLnode.from_list(['mul', orig, DECIMAL_DIVISOR],
                                 typ=BaseType('decimal', to.unit,
                                              to.positional))
    elif is_base_type(frm, 'num256') and is_base_type(
            to, 'num') and are_units_compatible(frm, to):
        return LLLnode.from_list(
            ['uclample', orig, ['mload', MemoryPositions.MAXNUM]],
            typ=BaseType("num"))
    elif isinstance(frm, NullType):
        if to.typ not in ('num', 'bool', 'num256', 'address', 'bytes32',
                          'decimal'):
            raise TypeMismatchException(
                "Cannot convert null-type object to type %r" % to, pos)
        return LLLnode.from_list(0, typ=to)
    else:
        raise TypeMismatchException(
            "Typecasting from base type %r to %r unavailable" % (frm, to), pos)
Пример #2
0
 def attribute(self):
     # x.balance: balance of address x
     if self.expr.attr == 'balance':
         addr = Expr.parse_value_expr(self.expr.value, self.context)
         if not is_base_type(addr.typ, 'address'):
             raise TypeMismatchException("Type mismatch: balance keyword expects an address as input", self.expr)
         return LLLnode.from_list(['balance', addr], typ=BaseType('num', {'wei': 1}), 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 not is_base_type(addr.typ, 'address'):
             raise TypeMismatchException("Type mismatch: codesize keyword expects an address as input", self.expr)
         if self.expr.attr == 'codesize':
             output_type = 'num'
         else:
             output_type = 'bool'
         return LLLnode.from_list(['extcodesize', addr], typ=BaseType(output_type), location=None, pos=getpos(self.expr))
     # self.x: global attribute
     elif isinstance(self.expr.value, ast.Name) and self.expr.value.id == "self":
         if self.expr.attr not in self.context.globals:
             raise VariableDeclarationException("Persistent variable undeclared: " + self.expr.attr, self.expr)
         var = self.context.globals[self.expr.attr]
         return LLLnode.from_list(var.pos, typ=var.typ, location='storage', pos=getpos(self.expr), annotation='self.' + self.expr.attr)
     # Reserved keywords
     elif isinstance(self.expr.value, ast.Name) and self.expr.value.id in ("msg", "block", "tx"):
         key = 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.value":
             if not self.context.is_payable:
                 raise NonPayableViolationException("Cannot use msg.value in a non-payable function", self.expr)
             return LLLnode.from_list(['callvalue'], typ=BaseType('num', {'wei': 1}), pos=getpos(self.expr))
         elif key == "msg.gas":
             return LLLnode.from_list(['gas'], typ='num', pos=getpos(self.expr))
         elif key == "block.difficulty":
             return LLLnode.from_list(['difficulty'], typ='num', pos=getpos(self.expr))
         elif key == "block.timestamp":
             return LLLnode.from_list(['timestamp'], typ=BaseType('num', {'sec': 1}, True), 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='num', 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))
         else:
             raise Exception("Unsupported keyword: " + key)
     # Other variables
     else:
         sub = Expr.parse_variable_location(self.expr.value, self.context)
         if not isinstance(sub.typ, StructType):
             raise TypeMismatchException("Type mismatch: member variable access not expected", self.expr.value)
         attrs = sorted(sub.typ.members.keys())
         if self.expr.attr not in attrs:
             raise TypeMismatchException("Member %s not found. Only the following available: %s" % (self.expr.attr, " ".join(attrs)), self.expr)
         return add_variable_offset(sub, self.expr.attr)
Пример #3
0
def make_clamper(datapos, mempos, typ, is_init=False):
    if not is_init:
        data_decl = ['calldataload', ['add', 4, datapos]]
        copier = lambda pos, sz: ['calldatacopy', mempos, ['add', 4, pos], sz]
    else:
        data_decl = ['codeload', ['add', '~codelen', datapos]]
        copier = lambda pos, sz: [
            'codecopy', mempos, ['add', '~codelen', pos], sz
        ]
    # Numbers: make sure they're in range
    if is_base_type(typ, 'num'):
        return LLLnode.from_list([
            'clamp', ['mload', MemoryPositions.MINNUM], data_decl,
            ['mload', MemoryPositions.MAXNUM]
        ],
                                 typ=typ,
                                 annotation='checking num input')
    # Booleans: make sure they're zero or one
    elif is_base_type(typ, 'bool'):
        return LLLnode.from_list(['uclamplt', data_decl, 2],
                                 typ=typ,
                                 annotation='checking bool input')
    # Addresses: make sure they're in range
    elif is_base_type(typ, 'address'):
        return LLLnode.from_list(
            ['uclamplt', data_decl, ['mload', MemoryPositions.ADDRSIZE]],
            typ=typ,
            annotation='checking address input')
    # Bytes: make sure they have the right size
    elif isinstance(typ, ByteArrayType):
        return LLLnode.from_list([
            'seq',
            copier(data_decl, 32 + typ.maxlen),
            [
                'assert',
                ['le', ['calldataload', ['add', 4, data_decl]], typ.maxlen]
            ]
        ],
                                 typ=None,
                                 annotation='checking bytearray input')
    # Lists: recurse
    elif isinstance(typ, ListType):
        o = []
        for i in range(typ.count):
            offset = get_size_of_type(typ.subtype) * 32 * i
            o.append(
                make_clamper(datapos + offset, mempos + offset, typ.subtype,
                             is_init))
        return LLLnode.from_list(['seq'] + o,
                                 typ=None,
                                 annotation='checking list input')
    # Otherwise don't make any checks
    else:
        return LLLnode.from_list('pass')
Пример #4
0
 def boolean_operations(self):
     if len(self.expr.values) != 2:
         raise StructureException("Expected two arguments for a bool op", self.expr)
     left = Expr.parse_value_expr(self.expr.values[0], self.context)
     right = Expr.parse_value_expr(self.expr.values[1], self.context)
     if not is_base_type(left.typ, 'bool') or not is_base_type(right.typ, 'bool'):
         raise TypeMismatchException("Boolean operations can only be between booleans!", self.expr)
     if isinstance(self.expr.op, ast.And):
         op = 'and'
     elif isinstance(self.expr.op, ast.Or):
         op = 'or'
     else:
         raise Exception("Unsupported bool op: " + self.expr.op)
     return LLLnode.from_list([op, left, right], typ='bool', pos=getpos(self.expr))
Пример #5
0
    def parse_return(self):
        from .parser import (make_setter)
        if self.context.return_type is None:
            if self.stmt.value:
                raise TypeMismatchException("Not expecting to return a value",
                                            self.stmt)
            return LLLnode.from_list(['return', 0, 0],
                                     typ=None,
                                     pos=getpos(self.stmt))
        if not self.stmt.value:
            raise TypeMismatchException("Expecting to return a value",
                                        self.stmt)
        sub = Expr(self.stmt.value, self.context).lll_node
        self.context.increment_return_counter()
        # Returning a value (most common case)
        if isinstance(sub.typ, BaseType):
            if not isinstance(self.context.return_type, BaseType):
                raise TypeMismatchException(
                    "Trying to return base type %r, output expecting %r" %
                    (sub.typ, self.context.return_type), self.stmt.value)
            sub = unwrap_location(sub)
            if not are_units_compatible(sub.typ, self.context.return_type):
                raise TypeMismatchException(
                    "Return type units mismatch %r %r" %
                    (sub.typ, self.context.return_type), self.stmt.value)
            elif is_base_type(sub.typ, self.context.return_type.typ) or \
                    (is_base_type(sub.typ, 'num') and is_base_type(self.context.return_type, 'signed256')):
                return LLLnode.from_list(
                    ['seq', ['mstore', 0, sub], ['return', 0, 32]],
                    typ=None,
                    pos=getpos(self.stmt))
            else:
                raise TypeMismatchException(
                    "Unsupported type conversion: %r to %r" %
                    (sub.typ, self.context.return_type), self.stmt.value)
        # Returning a byte array
        elif isinstance(sub.typ, ByteArrayType):
            if not isinstance(self.context.return_type, ByteArrayType):
                raise TypeMismatchException(
                    "Trying to return base type %r, output expecting %r" %
                    (sub.typ, self.context.return_type), self.stmt.value)
            if sub.typ.maxlen > self.context.return_type.maxlen:
                raise TypeMismatchException(
                    "Cannot cast from greater max-length %d to shorter max-length %d"
                    % (sub.typ.maxlen, self.context.return_type.maxlen),
                    self.stmt.value)
            # Returning something already in memory
            if sub.location == 'memory':
                return LLLnode.from_list([
                    'with', '_loc', sub,
                    [
                        'seq', ['mstore', ['sub', '_loc', 32], 32],
                        [
                            'return', ['sub', '_loc', 32],
                            ['ceil32', ['add', ['mload', '_loc'], 64]]
                        ]
                    ]
                ],
                                         typ=None,
                                         pos=getpos(self.stmt))
            # Copying from storage
            elif sub.location == 'storage':
                # Instantiate a byte array at some index
                fake_byte_array = LLLnode(self.context.get_next_mem() + 32,
                                          typ=sub.typ,
                                          location='memory',
                                          pos=getpos(self.stmt))
                o = [
                    'seq',
                    # Copy the data to this byte array
                    make_byte_array_copier(fake_byte_array, sub),
                    # Store the number 32 before it for ABI formatting purposes
                    ['mstore', self.context.get_next_mem(), 32],
                    # Return it
                    [
                        'return',
                        self.context.get_next_mem(),
                        [
                            'add',
                            [
                                'ceil32',
                                ['mload',
                                 self.context.get_next_mem() + 32]
                            ], 64
                        ]
                    ]
                ]
                return LLLnode.from_list(o, typ=None, pos=getpos(self.stmt))
            else:
                raise Exception("Invalid location: %s" % sub.location)

        elif isinstance(sub.typ, ListType):
            sub_base_type = re.split(r'\(|\[', str(sub.typ.subtype))[0]
            ret_base_type = re.split(r'\(|\[',
                                     str(self.context.return_type.subtype))[0]
            if sub_base_type != ret_base_type and sub.value != 'multi':
                raise TypeMismatchException(
                    "List return type %r does not match specified return type, expecting %r"
                    % (sub_base_type, ret_base_type), self.stmt)
            if sub.location == "memory" and sub.value != "multi":
                return LLLnode.from_list([
                    'return', sub,
                    get_size_of_type(self.context.return_type) * 32
                ],
                                         typ=None,
                                         pos=getpos(self.stmt))
            else:
                new_sub = LLLnode.from_list(self.context.new_placeholder(
                    self.context.return_type),
                                            typ=self.context.return_type,
                                            location='memory')
                setter = make_setter(new_sub,
                                     sub,
                                     'memory',
                                     pos=getpos(self.stmt))
                return LLLnode.from_list([
                    'seq', setter,
                    [
                        'return', new_sub,
                        get_size_of_type(self.context.return_type) * 32
                    ]
                ],
                                         typ=None,
                                         pos=getpos(self.stmt))

        # Returning a tuple.
        elif isinstance(sub.typ, TupleType):
            if len(self.context.return_type.members) != len(sub.typ.members):
                raise StructureException("Tuple lengths don't match!")
            subs = []
            dynamic_offset_counter = LLLnode(
                self.context.get_next_mem(),
                typ=None,
                annotation="dynamic_offset_counter"
            )  # dynamic offset position counter.
            new_sub = LLLnode.from_list(self.context.get_next_mem() + 32,
                                        typ=self.context.return_type,
                                        location='memory',
                                        annotation='new_sub')
            keyz = list(range(len(sub.typ.members)))
            dynamic_offset_start = 32 * len(
                sub.args)  # The static list of args end.
            left_token = LLLnode.from_list('_loc',
                                           typ=new_sub.typ,
                                           location="memory")

            def get_dynamic_offset_value():
                # Get value of dynamic offset counter.
                return ['mload', dynamic_offset_counter]

            def increment_dynamic_offset(dynamic_spot):
                # Increment dyanmic offset counter in memory.
                return [
                    'mstore', dynamic_offset_counter,
                    [
                        'add',
                        ['add', ['ceil32', ['mload', dynamic_spot]], 32],
                        ['mload', dynamic_offset_counter]
                    ]
                ]

            for i, typ in enumerate(keyz):
                arg = sub.args[i]
                variable_offset = LLLnode.from_list(
                    ['add', 32 * i, left_token],
                    typ=arg.typ,
                    annotation='variable_offset')
                if isinstance(arg.typ, ByteArrayType):
                    # Store offset pointer value.
                    subs.append([
                        'mstore', variable_offset,
                        get_dynamic_offset_value()
                    ])

                    # Store dynamic data, from offset pointer onwards.
                    dynamic_spot = LLLnode.from_list(
                        ['add', left_token,
                         get_dynamic_offset_value()],
                        location="memory",
                        typ=arg.typ,
                        annotation='dynamic_spot')
                    subs.append(
                        make_setter(dynamic_spot,
                                    arg,
                                    location="memory",
                                    pos=getpos(self.stmt)))
                    subs.append(increment_dynamic_offset(dynamic_spot))

                elif isinstance(arg.typ, BaseType):
                    subs.append(
                        make_setter(variable_offset,
                                    arg,
                                    "memory",
                                    pos=getpos(self.stmt)))
                else:
                    raise Exception("Can't return type %s as part of tuple",
                                    type(arg.typ))

            setter = LLLnode.from_list([
                'seq', [
                    'mstore', dynamic_offset_counter, dynamic_offset_start
                ], ['with', '_loc', new_sub, ['seq'] + subs]
            ],
                                       typ=None)

            return LLLnode.from_list([
                'seq', setter, ['return', new_sub,
                                get_dynamic_offset_value()]
            ],
                                     typ=None,
                                     pos=getpos(self.stmt))
        else:
            raise TypeMismatchException("Can only return base type!",
                                        self.stmt)
Пример #6
0
 def parse_return(self):
     from .parser import (parse_expr, make_setter)
     if self.context.return_type is None:
         if self.stmt.value:
             raise TypeMismatchException("Not expecting to return a value",
                                         self.stmt)
         return LLLnode.from_list(['return', 0, 0],
                                  typ=None,
                                  pos=getpos(self.stmt))
     if not self.stmt.value:
         raise TypeMismatchException("Expecting to return a value",
                                     self.stmt)
     sub = parse_expr(self.stmt.value, self.context)
     # Returning a value (most common case)
     if isinstance(sub.typ, BaseType):
         if not isinstance(self.context.return_type, BaseType):
             raise TypeMismatchException(
                 "Trying to return base type %r, output expecting %r" %
                 (sub.typ, self.context.return_type), self.stmt.value)
         sub = unwrap_location(sub)
         if not are_units_compatible(sub.typ, self.context.return_type):
             raise TypeMismatchException(
                 "Return type units mismatch %r %r" %
                 (sub.typ, self.context.return_type), self.stmt.value)
         elif is_base_type(sub.typ, self.context.return_type.typ) or \
                 (is_base_type(sub.typ, 'num') and is_base_type(self.context.return_type, 'signed256')):
             return LLLnode.from_list(
                 ['seq', ['mstore', 0, sub], ['return', 0, 32]],
                 typ=None,
                 pos=getpos(self.stmt))
         else:
             raise TypeMismatchException(
                 "Unsupported type conversion: %r to %r" %
                 (sub.typ, self.context.return_type), self.stmt.value)
     # Returning a byte array
     elif isinstance(sub.typ, ByteArrayType):
         if not isinstance(self.context.return_type, ByteArrayType):
             raise TypeMismatchException(
                 "Trying to return base type %r, output expecting %r" %
                 (sub.typ, self.context.return_type), self.stmt.value)
         if sub.typ.maxlen > self.context.return_type.maxlen:
             raise TypeMismatchException(
                 "Cannot cast from greater max-length %d to shorter max-length %d"
                 % (sub.typ.maxlen, self.context.return_type.maxlen),
                 self.stmt.value)
         # Returning something already in memory
         if sub.location == 'memory':
             return LLLnode.from_list([
                 'with', '_loc', sub,
                 [
                     'seq', ['mstore', ['sub', '_loc', 32], 32],
                     [
                         'return', ['sub', '_loc', 32],
                         ['ceil32', ['add', ['mload', '_loc'], 64]]
                     ]
                 ]
             ],
                                      typ=None,
                                      pos=getpos(self.stmt))
         # Copying from storage
         elif sub.location == 'storage':
             # Instantiate a byte array at some index
             fake_byte_array = LLLnode(self.context.get_next_mem() + 32,
                                       typ=sub.typ,
                                       location='memory',
                                       pos=getpos(self.stmt))
             o = [
                 'seq',
                 # Copy the data to this byte array
                 make_byte_array_copier(fake_byte_array, sub),
                 # Store the number 32 before it for ABI formatting purposes
                 ['mstore', self.context.get_next_mem(), 32],
                 # Return it
                 [
                     'return',
                     self.context.get_next_mem(),
                     [
                         'add',
                         [
                             'ceil32',
                             ['mload',
                              self.context.get_next_mem() + 32]
                         ], 64
                     ]
                 ]
             ]
             return LLLnode.from_list(o, typ=None, pos=getpos(self.stmt))
         else:
             raise Exception("Invalid location: %s" % sub.location)
     # Returning a list
     elif isinstance(sub.typ, ListType):
         if sub.location == "memory" and sub.value != "multi":
             return LLLnode.from_list([
                 'return', sub,
                 get_size_of_type(self.context.return_type) * 32
             ],
                                      typ=None,
                                      pos=getpos(self.stmt))
         else:
             new_sub = LLLnode.from_list(self.context.new_placeholder(
                 self.context.return_type),
                                         typ=self.context.return_type,
                                         location='memory')
             setter = make_setter(new_sub, sub, 'memory')
             return LLLnode.from_list([
                 'seq', setter,
                 [
                     'return', new_sub,
                     get_size_of_type(self.context.return_type) * 32
                 ]
             ],
                                      typ=None,
                                      pos=getpos(self.stmt))
     else:
         raise TypeMismatchException("Can only return base type!",
                                     self.stmt)