예제 #1
0
def unwrap_location(orig):
    if orig.location == 'memory':
        return LLLnode.from_list(['mload', orig], typ=orig.typ)
    elif orig.location == 'storage':
        return LLLnode.from_list(['sload', orig], typ=orig.typ)
    else:
        return orig
예제 #2
0
def byte_array_to_num(
    arg,
    expr,
    out_type,
    offset=32,
):
    if arg.location == "memory":
        lengetter = LLLnode.from_list(['mload', '_sub'],
                                      typ=BaseType('int128'))
        first_el_getter = LLLnode.from_list(['mload', ['add', 32, '_sub']],
                                            typ=BaseType('int128'))
    elif arg.location == "storage":
        lengetter = LLLnode.from_list(['sload', ['sha3_32', '_sub']],
                                      typ=BaseType('int128'))
        first_el_getter = LLLnode.from_list(
            ['sload', ['add', 1, ['sha3_32', '_sub']]], typ=BaseType('int128'))
    if out_type == 'int128':
        result = [
            'clamp', ['mload', MemoryPositions.MINNUM],
            ['div', '_el1', ['exp', 256, ['sub', 32, '_len']]],
            ['mload', MemoryPositions.MAXNUM]
        ]
    elif out_type == 'uint256':
        result = ['div', '_el1', ['exp', 256, ['sub', offset, '_len']]]
    return LLLnode.from_list([
        'with', '_sub', arg,
        [
            'with', '_el1', first_el_getter,
            ['with', '_len', ['clamp', 0, lengetter, 32], result]
        ]
    ],
                             typ=BaseType(out_type),
                             annotation='bytearray to number (%s)' % out_type)
예제 #3
0
def make_byte_array_copier(destination, source, pos=None):
    if not isinstance(source.typ, (ByteArrayType, NullType)):
        raise TypeMismatchException(
            "Can only set a byte array to another byte array", pos)
    if isinstance(
            source.typ,
            ByteArrayType) and source.typ.maxlen > destination.typ.maxlen:
        raise TypeMismatchException(
            "Cannot cast from greater max-length %d to shorter max-length %d" %
            (source.typ.maxlen, destination.typ.maxlen))
    # Special case: memory to memory
    if source.location == "memory" and destination.location == "memory":
        gas_calculation = GAS_IDENTITY + GAS_IDENTITYWORD * (
            ceil32(source.typ.maxlen) // 32)
        o = LLLnode.from_list([
            'with', '_source', source,
            [
                'with', '_sz', ['add', 32, ['mload', '_source']],
                [
                    'assert',
                    [
                        'call', ['add', 18, ['div', '_sz', 10]], 4, 0,
                        '_source', '_sz', destination, '_sz'
                    ]
                ]
            ]
        ],
                              typ=None,
                              add_gas_estimate=gas_calculation,
                              annotation='Memory copy')
        return o

    pos_node = LLLnode.from_list('_pos',
                                 typ=source.typ,
                                 location=source.location)
    # Get the length
    if isinstance(source.typ, NullType):
        length = 1
    elif source.location == "memory":
        length = ['add', ['mload', '_pos'], 32]
    elif source.location == "storage":
        length = ['add', ['sload', '_pos'], 32]
        pos_node = LLLnode.from_list(['sha3_32', pos_node],
                                     typ=source.typ,
                                     location=source.location)
    else:
        raise Exception("Unsupported location:" + source.location)
    if destination.location == "storage":
        destination = LLLnode.from_list(['sha3_32', destination],
                                        typ=destination.typ,
                                        location=destination.location)
    # Maximum theoretical length
    max_length = 32 if isinstance(source.typ,
                                  NullType) else source.typ.maxlen + 32
    return LLLnode.from_list([
        'with', '_pos', 0 if isinstance(source.typ, NullType) else source,
        make_byte_slice_copier(
            destination, pos_node, length, max_length, pos=pos)
    ],
                             typ=None)
예제 #4
0
    def build_in_comparator(self):
        from ophydia.parser.parser import make_setter
        left = Expr(self.expr.left, self.context).lll_node
        right = Expr(self.expr.comparators[0], self.context).lll_node

        if left.typ.typ != right.typ.subtype.typ:
            raise TypeMismatchException("%s cannot be in a list of %s" % (left.typ.typ, right.typ.subtype.typ))
        result_placeholder = self.context.new_placeholder(BaseType('bool'))
        setter = []

        # Load nth item from list in memory.
        if right.value == 'multi':
            # Copy literal to memory to be compared.
            tmp_list = LLLnode.from_list(
                obj=self.context.new_placeholder(ListType(right.typ.subtype, right.typ.count)),
                typ=ListType(right.typ.subtype, right.typ.count),
                location='memory'
            )
            setter = make_setter(tmp_list, right, 'memory', pos=getpos(self.expr))
            load_i_from_list = ['mload', ['add', tmp_list, ['mul', 32, ['mload', MemoryPositions.FREE_LOOP_INDEX]]]]
        elif right.location == "storage":
            load_i_from_list = ['sload', ['add', ['sha3_32', right], ['mload', MemoryPositions.FREE_LOOP_INDEX]]]
        else:
            load_i_from_list = ['mload', ['add', right, ['mul', 32, ['mload', MemoryPositions.FREE_LOOP_INDEX]]]]

        # Condition repeat loop has to break on.
        break_loop_condition = [
            'if',
            ['eq', unwrap_location(left), load_i_from_list],
            ['seq',
                ['mstore', '_result', 1],  # store true.
                'break']
        ]

        # Repeat loop to loop-compare each item in the list.
        for_loop_sequence = [
            ['mstore', result_placeholder, 0],
            ['with', '_result', result_placeholder,
                ['repeat', MemoryPositions.FREE_LOOP_INDEX, 0, right.typ.count, break_loop_condition]],
            ['mload', result_placeholder]
        ]

        # Save list to memory, so one can iterate over it,
        # used when literal was created with tmp_list.
        if setter:
            compare_sequence = ['seq', setter] + for_loop_sequence
        else:
            compare_sequence = ['seq'] + for_loop_sequence

        # Compare the result of the repeat loop to 1, to know if a match was found.
        o = LLLnode.from_list([
            'eq', 1,
            compare_sequence],
            typ='bool',
            annotation="in comporator"
        )

        return o
예제 #5
0
 def constants(self):
     if self.expr.value is True:
         return LLLnode.from_list(1, typ=BaseType('bool', is_literal=True), pos=getpos(self.expr))
     elif self.expr.value is False:
         return LLLnode.from_list(0, typ=BaseType('bool', is_literal=True), pos=getpos(self.expr))
     elif self.expr.value is None:
         return LLLnode.from_list(None, typ=NullType(), pos=getpos(self.expr))
     else:
         raise Exception("Unknown name constant: %r" % self.expr.value.value)
예제 #6
0
    def variables(self):
        builtin_constants = {
            'ZERO_ADDRESS': LLLnode.from_list([0], typ=BaseType('address', None, is_literal=True), pos=getpos(self.expr)),
            'MAX_INT128': LLLnode.from_list(['mload', MemoryPositions.MAXNUM], typ=BaseType('int128', None, is_literal=True), pos=getpos(self.expr)),
            'MIN_INT128': LLLnode.from_list(['mload', MemoryPositions.MINNUM], typ=BaseType('int128', None, is_literal=True), pos=getpos(self.expr)),
            'MAX_DECIMAL': LLLnode.from_list(['mload', MemoryPositions.MAXDECIMAL], typ=BaseType('decimal', None, is_literal=True), pos=getpos(self.expr)),
            'MIN_DECIMAL': LLLnode.from_list(['mload', MemoryPositions.MINDECIMAL], typ=BaseType('decimal', None, is_literal=True), pos=getpos(self.expr)),
            'MAX_UINT256': LLLnode.from_list([2**256 - 1], typ=BaseType('uint256', None, is_literal=True), pos=getpos(self.expr)),
        }

        if self.expr.id == 'self':
            return LLLnode.from_list(['address'], typ='address', pos=getpos(self.expr))
        elif self.expr.id in self.context.vars:
            var = self.context.vars[self.expr.id]
            return LLLnode.from_list(var.pos, typ=var.typ, location='memory', pos=getpos(self.expr), annotation=self.expr.id, mutable=var.mutable)
        elif self.expr.id in builtin_constants:
            return builtin_constants[self.expr.id]
        elif self.expr.id in self.context.constants:
            # check if value is compatible with
            const = self.context.constants[self.expr.id]
            if isinstance(const, ast.AnnAssign):  # Handle ByteArrays.
                expr = Expr(const.value, self.context).lll_node
                return expr
            # Other types are already unwrapped, no need
            return self.context.constants[self.expr.id]
        else:
            raise VariableDeclarationException("Undeclared variable: " + self.expr.id, self.expr)
예제 #7
0
def call_self_public(stmt_expr, context, sig):
    # self.* style call to a public function.
    method_name, expr_args, sig = call_lookup_specs(stmt_expr, context)
    add_gas = sig.gas  # gas of call
    inargs, inargsize, _ = pack_arguments(sig,
                                          expr_args,
                                          context,
                                          pos=getpos(stmt_expr))
    output_placeholder, returner, output_size = call_make_placeholder(
        stmt_expr, context, sig)
    assert_call = [
        'assert',
        [
            'call', ['gas'], ['address'], 0, inargs, inargsize,
            output_placeholder, output_size
        ]
    ]
    if output_size > 0:
        assert_call = ['seq', assert_call, returner]
    o = LLLnode.from_list(assert_call,
                          typ=sig.output_type,
                          location='memory',
                          pos=getpos(stmt_expr),
                          add_gas_estimate=add_gas,
                          annotation='Internal Call: %s' % method_name)
    o.gas += sig.gas
    return o
예제 #8
0
파일: parser.py 프로젝트: DadiaoMoe/ophydia
def get_sig_statements(sig, pos):
    method_id_node = LLLnode.from_list(sig.method_id,
                                       pos=pos,
                                       annotation='%s' % sig.sig)

    if sig.private:
        sig_compare = 0
        private_label = LLLnode.from_list(
            ['label', 'priv_{}'.format(sig.method_id)],
            pos=pos,
            annotation='%s' % sig.sig)
    else:
        sig_compare = ['eq', ['mload', 0], method_id_node]
        private_label = ['pass']

    return sig_compare, private_label
예제 #9
0
 def tuple_literals(self):
     if not len(self.expr.elts):
         raise StructureException("Tuple must have elements", self.expr)
     o = []
     for elt in self.expr.elts:
         o.append(Expr(elt, self.context).lll_node)
     return LLLnode.from_list(["multi"] + o, typ=TupleType(o), pos=getpos(self.expr))
예제 #10
0
 def arithmetic_get_reference(self, item):
     item_lll = Expr.parse_value_expr(item, self.context)
     if isinstance(item, ast.Call):
         # We only want to perform call statements once.
         placeholder = self.context.new_placeholder(item_lll.typ)
         pre_alloc = ['mstore', placeholder, item_lll]
         return pre_alloc, LLLnode.from_list(['mload', placeholder], location='memory', typ=item_lll.typ)
     else:
         return None, item_lll
예제 #11
0
 def string(self):
     bytez, bytez_length = string_to_bytes(self.expr.s)
     placeholder = self.context.new_placeholder(ByteArrayType(bytez_length))
     seq = []
     seq.append(['mstore', placeholder, bytez_length])
     for i in range(0, len(bytez), 32):
         seq.append(['mstore', ['add', placeholder, i + 32], bytes_to_int((bytez + b'\x00' * 31)[i: i + 32])])
     return LLLnode.from_list(['seq'] + seq + [placeholder],
         typ=ByteArrayType(bytez_length), location='memory', pos=getpos(self.expr), annotation='Create ByteArray: %s' % bytez)
예제 #12
0
파일: parser.py 프로젝트: DadiaoMoe/ophydia
def parse_body(code, context):
    if not isinstance(code, list):
        return parse_stmt(code, context)
    o = []
    for stmt in code:
        lll = parse_stmt(stmt, context)
        o.append(lll)
    return LLLnode.from_list(['seq'] + o,
                             pos=getpos(code[0]) if code else None)
예제 #13
0
def base_type_conversion(orig, frm, to, pos):
    orig = unwrap_location(orig)
    if getattr(frm, 'is_literal', False) and frm.typ in (
            'int128',
            'uint256') and not SizeLimits.in_bounds(frm.typ, orig.value):
        raise InvalidLiteralException(
            "Number out of range: " + str(orig.value), pos)
    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, 'int128') 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 isinstance(frm, NullType):
        if to.typ not in ('int128', 'bool', 'uint256', 'address', 'bytes32',
                          'decimal'):
            # This is only to future proof the use of  base_type_conversion.
            raise TypeMismatchException(
                "Cannot convert null-type object to type %r" % to,
                pos)  # pragma: no cover
        return LLLnode.from_list(0, typ=to)
    elif isinstance(to, ContractType) and frm.typ == 'address':
        return LLLnode(orig.value,
                       orig.args,
                       typ=to,
                       add_gas_estimate=orig.add_gas_estimate)
    # Integer literal conversion.
    elif (frm.typ, to.typ, frm.is_literal) == ('int128', 'uint256', True):
        return LLLnode(orig.value,
                       orig.args,
                       typ=to,
                       add_gas_estimate=orig.add_gas_estimate)
    else:
        raise TypeMismatchException(
            "Typecasting from base type %r to %r unavailable" % (frm, to), pos)
예제 #14
0
 def struct_literals(self):
     o = {}
     members = {}
     for key, value in zip(self.expr.keys, self.expr.values):
         if not isinstance(key, ast.Name) or not is_varname_valid(key.id, self.context.custom_units):
             raise TypeMismatchException("Invalid member variable for struct: %r" % vars(key).get('id', key), key)
         if key.id in o:
             raise TypeMismatchException("Member variable duplicated: " + key.id, key)
         o[key.id] = Expr(value, self.context).lll_node
         members[key.id] = o[key.id].typ
     return LLLnode.from_list(["multi"] + [o[key] for key in sorted(list(o.keys()))], typ=StructType(members), pos=getpos(self.expr))
예제 #15
0
    def unary_operations(self):
        operand = Expr.parse_value_expr(self.expr.operand, self.context)
        if isinstance(self.expr.op, ast.Not):
            if isinstance(operand.typ, BaseType) and operand.typ.typ == 'bool':
                return LLLnode.from_list(["iszero", operand], typ='bool', pos=getpos(self.expr))
            else:
                raise TypeMismatchException("Only bool is supported for not operation, %r supplied." % operand.typ, self.expr)
        elif isinstance(self.expr.op, ast.USub):
            if not is_numeric_type(operand.typ):
                raise TypeMismatchException("Unsupported type for negation: %r" % operand.typ, operand)

            if operand.typ.is_literal and 'int' in operand.typ.typ:
                num = ast.Num(0 - operand.value)
                num.source_code = self.expr.source_code
                num.lineno = self.expr.lineno
                num.col_offset = self.expr.col_offset
                return Expr.parse_value_expr(num, self.context)

            return LLLnode.from_list(["sub", 0, operand], typ=operand.typ, pos=getpos(self.expr))
        else:
            raise StructureException("Only the 'not' unary operator is supported")
예제 #16
0
파일: parser.py 프로젝트: DadiaoMoe/ophydia
def external_contract_call(node,
                           context,
                           contract_name,
                           contract_address,
                           pos,
                           value=None,
                           gas=None):
    if value is None:
        value = 0
    if gas is None:
        gas = 'gas'
    if contract_name not in context.sigs:
        raise VariableDeclarationException("Contract not declared yet: %s" %
                                           contract_name)
    method_name = node.func.attr
    if method_name not in context.sigs[contract_name]:
        raise FunctionDeclarationException(
            "Function not declared yet: %s (reminder: "
            "function must be declared in the correct contract)" % method_name,
            pos)
    sig = context.sigs[contract_name][method_name]
    inargs, inargsize, _ = pack_arguments(
        sig, [parse_expr(arg, context) for arg in node.args], context, pos=pos)
    output_placeholder, output_size, returner = get_external_contract_call_output(
        sig, context)
    sub = [
        'seq', ['assert', ['extcodesize', contract_address]],
        ['assert', ['ne', 'address', contract_address]]
    ]
    if context.is_constant or sig.const:
        sub.append([
            'assert',
            [
                'staticcall', gas, contract_address, inargs, inargsize,
                output_placeholder, output_size
            ]
        ])
    else:
        sub.append([
            'assert',
            [
                'call', gas, contract_address, value, inargs, inargsize,
                output_placeholder, output_size
            ]
        ])
    sub.extend(returner)
    o = LLLnode.from_list(sub,
                          typ=sig.output_type,
                          location='memory',
                          pos=getpos(node))
    return o
예제 #17
0
    def call(self):
        from ophydia.parser.parser import (
            external_contract_call
        )
        from ophydia.functions import (
            dispatch_table,
        )

        if isinstance(self.expr.func, ast.Name):
            function_name = self.expr.func.id
            if function_name in dispatch_table:
                return dispatch_table[function_name](self.expr, self.context)
            else:
                err_msg = "Not a top-level function: {}".format(function_name)
                if function_name in [x.split('(')[0] for x, _ in self.context.sigs['self'].items()]:
                    err_msg += ". Did you mean self.{}?".format(function_name)
                raise StructureException(err_msg, self.expr)
        elif isinstance(self.expr.func, ast.Attribute) and isinstance(self.expr.func.value, ast.Name) and self.expr.func.value.id == "self":
            return self_call.make_call(self.expr, self.context)
        elif isinstance(self.expr.func, ast.Attribute) and isinstance(self.expr.func.value, ast.Call):
            contract_name = self.expr.func.value.func.id
            contract_address = Expr.parse_value_expr(self.expr.func.value.args[0], self.context)
            value, gas = self._get_external_contract_keywords()
            return external_contract_call(self.expr, self.context, contract_name, contract_address, pos=getpos(self.expr), value=value, gas=gas)
        elif isinstance(self.expr.func.value, ast.Attribute) and self.expr.func.value.attr in self.context.sigs:
            contract_name = self.expr.func.value.attr
            var = self.context.globals[self.expr.func.value.attr]
            contract_address = unwrap_location(LLLnode.from_list(var.pos, typ=var.typ, location='storage', pos=getpos(self.expr), annotation='self.' + self.expr.func.value.attr))
            value, gas = self._get_external_contract_keywords()
            return external_contract_call(self.expr, self.context, contract_name, contract_address, pos=getpos(self.expr), value=value, gas=gas)
        elif isinstance(self.expr.func.value, ast.Attribute) and self.expr.func.value.attr in self.context.globals:
            contract_name = self.context.globals[self.expr.func.value.attr].typ.unit
            var = self.context.globals[self.expr.func.value.attr]
            contract_address = unwrap_location(LLLnode.from_list(var.pos, typ=var.typ, location='storage', pos=getpos(self.expr), annotation='self.' + self.expr.func.value.attr))
            value, gas = self._get_external_contract_keywords()
            return external_contract_call(self.expr, self.context, contract_name, contract_address, pos=getpos(self.expr), value=value, gas=gas)
        else:
            raise StructureException("Unsupported operator: %r" % ast.dump(self.expr), self.expr)
예제 #18
0
 def list_literals(self):
     if not len(self.expr.elts):
         raise StructureException("List must have elements", self.expr)
     o = []
     out_type = None
     for elt in self.expr.elts:
         o.append(Expr(elt, self.context).lll_node)
         if not out_type:
             out_type = o[-1].typ
         previous_type = o[-1].typ.subtype.typ if hasattr(o[-1].typ, 'subtype') else o[-1].typ
         current_type = out_type.subtype.typ if hasattr(out_type, 'subtype') else out_type
         if len(o) > 1 and previous_type != current_type:
             raise TypeMismatchException("Lists may only contain one type", self.expr)
     return LLLnode.from_list(["multi"] + o, typ=ListType(out_type, len(o)), pos=getpos(self.expr))
예제 #19
0
파일: parser.py 프로젝트: DadiaoMoe/ophydia
def parse_tree_to_lll(code, origcode, runtime_only=False):
    global_ctx = GlobalContext.get_global_context(code)
    _names_def = [_def.name for _def in global_ctx._defs]
    # Checks for duplicate function names
    if len(set(_names_def)) < len(_names_def):
        raise FunctionDeclarationException(
            "Duplicate function name: %s" %
            [name for name in _names_def if _names_def.count(name) > 1][0])
    # TODO: remove events
    # _names_events = [_event.target.id for _event in global_ctx._events]
    # # Checks for duplicate event names
    # if len(set(_names_events)) < len(_names_events):
    #     raise EventDeclarationException("Duplicate event name: %s" % [name for name in _names_events if _names_events.count(name) > 1][0])
    # Initialization function
    initfunc = [_def for _def in global_ctx._defs if is_initializer(_def)]
    # Default function
    defaultfunc = [_def for _def in global_ctx._defs if is_default_func(_def)]
    # Regular functions
    otherfuncs = [
        _def for _def in global_ctx._defs
        if not is_initializer(_def) and not is_default_func(_def)
    ]
    sigs = {}
    external_contracts = {}
    # Create the main statement
    o = ['seq']
    # TODO: remove events
    # if global_ctx._events:
    #     sigs = parse_events(sigs, global_ctx._events, global_ctx._custom_units)
    if global_ctx._contracts:
        external_contracts = parse_external_contracts(external_contracts,
                                                      global_ctx._contracts)
    # If there is an init func...
    if initfunc:
        o.append(['seq', initializer_lll])
        # TODO: breakpoint
        o.append(
            parse_func(initfunc[0], {
                **{
                    'self': sigs
                },
                **external_contracts
            }, origcode, global_ctx))
    # If there are regular functions...
    if otherfuncs or defaultfunc:
        o = parse_other_functions(o, otherfuncs, sigs, external_contracts,
                                  origcode, global_ctx, defaultfunc,
                                  runtime_only)
    return LLLnode.from_list(o, typ=None)
예제 #20
0
파일: parser.py 프로젝트: DadiaoMoe/ophydia
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, 'int128'):
        return LLLnode.from_list([
            'clamp', ['mload', MemoryPositions.MINNUM], data_decl,
            ['mload', MemoryPositions.MAXNUM]
        ],
                                 typ=typ,
                                 annotation='checking int128 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')
예제 #21
0
    def boolean_operations(self):
        if len(self.expr.values) != 2:
            raise StructureException("Expected two arguments for a bool op", self.expr)
        if self.context.in_assignment and (isinstance(self.expr.values[0], ast.Call) or isinstance(self.expr.values[1], ast.Call)):
            raise StructureException("Boolean operations with calls may not be performed on assignment", 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))
예제 #22
0
    def number(self):
        orignum = get_original_if_0_prefixed(self.expr, self.context)

        if orignum is None and isinstance(self.expr.n, int):
            # Literal (mostly likely) becomes int128
            if SizeLimits.in_bounds('int128', self.expr.n) or self.expr.n < 0:
                return LLLnode.from_list(self.expr.n, typ=BaseType('int128', unit=None, is_literal=True), pos=getpos(self.expr))
            # Literal is large enough (mostly likely) becomes uint256.
            else:
                return LLLnode.from_list(self.expr.n, typ=BaseType('uint256', unit=None, is_literal=True), pos=getpos(self.expr))

        elif isinstance(self.expr.n, float):
            numstring, num, den = get_number_as_fraction(self.expr, self.context)
            # if not SizeLimits.in_bounds('decimal', num // den):
            # if not SizeLimits.MINDECIMAL * den <= num <= SizeLimits.MAXDECIMAL * den:
            if not (SizeLimits.MINNUM * den < num < SizeLimits.MAXNUM * den):
                raise InvalidLiteralException("Number out of range: " + numstring, self.expr)
            if DECIMAL_DIVISOR % den:
                raise InvalidLiteralException("Too many decimal places: " + numstring, self.expr)
            return LLLnode.from_list(num * DECIMAL_DIVISOR // den, typ=BaseType('decimal', unit=None), pos=getpos(self.expr))
        # Binary literal.
        elif orignum[:2] == '0b':
            str_val = orignum[2:]
            total_bits = len(orignum[2:])
            total_bits = total_bits if total_bits % 8 == 0 else total_bits + 8 - (total_bits % 8)  # ceil8 to get byte length.
            if len(orignum[2:]) != total_bits:  # Support only full formed bit definitions.
                raise InvalidLiteralException("Bit notation requires a multiple of 8 bits / 1 byte. {} bit(s) are missing.".format(total_bits - len(orignum[2:])), self.expr)
            byte_len = int(total_bits / 8)
            placeholder = self.context.new_placeholder(ByteArrayType(byte_len))
            seq = []
            seq.append(['mstore', placeholder, byte_len])
            for i in range(0, total_bits, 256):
                section = str_val[i:i + 256]
                int_val = int(section, 2) << (256 - len(section))  # bytes are right padded.
                seq.append(
                    ['mstore', ['add', placeholder, i + 32], int_val])
            return LLLnode.from_list(['seq'] + seq + [placeholder],
                typ=ByteArrayType(byte_len), location='memory', pos=getpos(self.expr), annotation='Create ByteArray (Binary literal): %s' % str_val)
        elif len(orignum) == 42:
            if checksum_encode(orignum) != orignum:
                raise InvalidLiteralException("Address checksum mismatch. If you are sure this is the "
                                              "right address, the correct checksummed form is: " +
                                              checksum_encode(orignum), self.expr)
            return LLLnode.from_list(self.expr.n, typ=BaseType('address', is_literal=True), pos=getpos(self.expr))
        elif len(orignum) == 66:
            return LLLnode.from_list(self.expr.n, typ=BaseType('bytes32', is_literal=True), pos=getpos(self.expr))
        else:
            raise InvalidLiteralException("Cannot read 0x value with length %d. Expecting 42 (address incl 0x) or 66 (bytes32 incl 0x)"
                                          % len(orignum), self.expr)
예제 #23
0
파일: parser.py 프로젝트: DadiaoMoe/ophydia
def parse_other_functions(o, otherfuncs, sigs, external_contracts, origcode,
                          global_ctx, default_function, runtime_only):
    sub = ['seq', initializer_lll]
    add_gas = initializer_lll.gas
    for _def in otherfuncs:
        sub.append(
            parse_func(_def, {
                **{
                    'self': sigs
                },
                **external_contracts
            }, origcode, global_ctx))  # noqa E999
        sub[-1].total_gas += add_gas
        add_gas += 30
        for sig in generate_default_arg_sigs(_def, external_contracts,
                                             global_ctx._custom_units):
            sig.gas = sub[-1].total_gas
            sigs[sig.sig] = sig

    # Add fallback function
    if default_function:
        default_func = parse_func(default_function[0], {
            **{
                'self': sigs
            },
            **external_contracts
        }, origcode, global_ctx)
        sub.append(default_func)
    else:
        sub.append(
            LLLnode.from_list(['revert', 0, 0],
                              typ=None,
                              annotation='Default function'))
    if runtime_only:
        return sub
    else:
        o.append(['return', 0, ['lll', sub, 0]])
        return o
예제 #24
0
파일: parser.py 프로젝트: DadiaoMoe/ophydia
def pack_args_by_32(holder,
                    maxlen,
                    arg,
                    typ,
                    context,
                    placeholder,
                    dynamic_offset_counter=None,
                    datamem_start=None,
                    zero_pad_i=None,
                    pos=None):
    """
    Copy necessary variables to pre-allocated memory section.

    :param holder: Complete holder for all args
    :param maxlen: Total length in bytes of the full arg section (static + dynamic).
    :param arg: Current arg to pack
    :param context: Context of arg
    :param placeholder: Static placeholder for static argument part.
    :param dynamic_offset_counter: position counter stored in static args.
    :param dynamic_placeholder: pointer to current position in memory to write dynamic values to.
    :param datamem_start: position where the whole datemem section starts.
    """

    if isinstance(typ, BaseType):
        value = parse_expr(arg, context)
        value = base_type_conversion(value, value.typ, typ, pos)
        holder.append(
            LLLnode.from_list(['mstore', placeholder, value],
                              typ=typ,
                              location='memory'))
    elif isinstance(typ, ByteArrayType):
        bytez = b''

        source_expr = Expr(arg, context)
        if isinstance(arg, ast.Str):
            if len(arg.s) > typ.maxlen:
                raise TypeMismatchException(
                    "Data input bytes are to big: %r %r" % (len(arg.s), typ),
                    pos)
            for c in arg.s:
                if ord(c) >= 256:
                    raise InvalidLiteralException(
                        "Cannot insert special character %r into byte array" %
                        c, pos)
                bytez += bytes([ord(c)])

            holder.append(source_expr.lll_node)

        # Set static offset, in arg slot.
        holder.append(
            LLLnode.from_list(
                ['mstore', placeholder, ['mload', dynamic_offset_counter]]))
        # Get the biginning to write the ByteArray to.
        dest_placeholder = LLLnode.from_list(
            ['add', datamem_start, ['mload', dynamic_offset_counter]],
            typ=typ,
            location='memory',
            annotation="pack_args_by_32:dest_placeholder")
        copier = make_byte_array_copier(dest_placeholder,
                                        source_expr.lll_node,
                                        pos=pos)
        holder.append(copier)
        # Add zero padding.
        new_maxlen = ceil32(source_expr.lll_node.typ.maxlen)

        holder.append([
            'with',
            '_bytearray_loc',
            dest_placeholder,
            [
                'seq',
                [
                    'repeat',
                    zero_pad_i,
                    ['mload', '_bytearray_loc'],
                    new_maxlen,
                    [
                        'seq',
                        [
                            'if', ['ge', ['mload', zero_pad_i], new_maxlen],
                            'break'
                        ],  # stay within allocated bounds
                        [
                            'mstore8',
                            [
                                'add', ['add', '_bytearray_loc', 32],
                                ['mload', zero_pad_i]
                            ], 0
                        ]
                    ]
                ]
            ]
        ])
        # Increment offset counter.
        increment_counter = LLLnode.from_list([
            'mstore', dynamic_offset_counter,
            [
                'add',
                [
                    'add', ['mload', dynamic_offset_counter],
                    ['ceil32', ['mload', dest_placeholder]]
                ], 32
            ]
        ])
        holder.append(increment_counter)
    elif isinstance(typ, ListType):
        maxlen += (typ.count - 1) * 32
        typ = typ.subtype

        def check_list_type_match(provided):  # Check list types match.
            if provided != typ:
                raise TypeMismatchException(
                    "Log list type '%s' does not match provided, expected '%s'"
                    % (provided, typ))

        # List from storage
        if isinstance(arg, ast.Attribute) and arg.value.id == 'self':
            stor_list = context.globals[arg.attr]
            check_list_type_match(stor_list.typ.subtype)
            size = stor_list.typ.count
            for offset in range(0, size):
                arg2 = LLLnode.from_list([
                    'sload',
                    ['add', ['sha3_32', Expr(arg, context).lll_node], offset]
                ],
                                         typ=typ)
                p_holder = context.new_placeholder(
                    BaseType(32)) if offset > 0 else placeholder
                holder, maxlen = pack_args_by_32(holder,
                                                 maxlen,
                                                 arg2,
                                                 typ,
                                                 context,
                                                 p_holder,
                                                 pos=pos)
        # List from variable.
        elif isinstance(arg, ast.Name):
            size = context.vars[arg.id].size
            pos = context.vars[arg.id].pos
            check_list_type_match(context.vars[arg.id].typ.subtype)
            for i in range(0, size):
                offset = 32 * i
                arg2 = LLLnode.from_list(pos + offset,
                                         typ=typ,
                                         location='memory')
                p_holder = context.new_placeholder(
                    BaseType(32)) if i > 0 else placeholder
                holder, maxlen = pack_args_by_32(holder,
                                                 maxlen,
                                                 arg2,
                                                 typ,
                                                 context,
                                                 p_holder,
                                                 pos=pos)
        # is list literal.
        else:
            holder, maxlen = pack_args_by_32(holder,
                                             maxlen,
                                             arg.elts[0],
                                             typ,
                                             context,
                                             placeholder,
                                             pos=pos)
            for j, arg2 in enumerate(arg.elts[1:]):
                holder, maxlen = pack_args_by_32(holder,
                                                 maxlen,
                                                 arg2,
                                                 typ,
                                                 context,
                                                 context.new_placeholder(
                                                     BaseType(32)),
                                                 pos=pos)

    return holder, maxlen
예제 #25
0
def make_setter(left, right, location, pos):
    # Basic types
    if isinstance(left.typ, BaseType):
        right = base_type_conversion(right, right.typ, left.typ, pos)
        if location == 'storage':
            return LLLnode.from_list(['sstore', left, right], typ=None)
        elif location == 'memory':
            return LLLnode.from_list(['mstore', left, right], typ=None)
    # Byte arrays
    elif isinstance(left.typ, ByteArrayType):
        return make_byte_array_copier(left, right, pos)
    # Can't copy mappings
    elif isinstance(left.typ, MappingType):
        raise TypeMismatchException(
            "Cannot copy mappings; can only copy individual elements", pos)
    # Arrays
    elif isinstance(left.typ, ListType):
        # Cannot do something like [a, b, c] = [1, 2, 3]
        if left.value == "multi":
            raise Exception("Target of set statement must be a single item")
        if not isinstance(right.typ, (ListType, NullType)):
            raise TypeMismatchException(
                "Setter type mismatch: left side is array, right side is %r" %
                right.typ, pos)
        left_token = LLLnode.from_list('_L',
                                       typ=left.typ,
                                       location=left.location)
        if left.location == "storage":
            left = LLLnode.from_list(['sha3_32', left],
                                     typ=left.typ,
                                     location="storage_prehashed")
            left_token.location = "storage_prehashed"
        # Type checks
        if not isinstance(right.typ, NullType):
            if not isinstance(right.typ, ListType):
                raise TypeMismatchException(
                    "Left side is array, right side is not", pos)
            if left.typ.count != right.typ.count:
                raise TypeMismatchException("Mismatched number of elements",
                                            pos)
        # If the right side is a literal
        if right.value == "multi":
            if len(right.args) != left.typ.count:
                raise TypeMismatchException("Mismatched number of elements",
                                            pos)
            subs = []
            for i in range(left.typ.count):
                subs.append(
                    make_setter(add_variable_offset(left_token,
                                                    LLLnode.from_list(
                                                        i, typ='int128'),
                                                    pos=pos),
                                right.args[i],
                                location,
                                pos=pos))
            return LLLnode.from_list(['with', '_L', left, ['seq'] + subs],
                                     typ=None)
        # If the right side is a null
        elif isinstance(right.typ, NullType):
            subs = []
            for i in range(left.typ.count):
                subs.append(
                    make_setter(add_variable_offset(left_token,
                                                    LLLnode.from_list(
                                                        i, typ='int128'),
                                                    pos=pos),
                                LLLnode.from_list(None, typ=NullType()),
                                location,
                                pos=pos))
            return LLLnode.from_list(['with', '_L', left, ['seq'] + subs],
                                     typ=None)
        # If the right side is a variable
        else:
            right_token = LLLnode.from_list('_R',
                                            typ=right.typ,
                                            location=right.location)
            subs = []
            for i in range(left.typ.count):
                subs.append(
                    make_setter(
                        add_variable_offset(left_token,
                                            LLLnode.from_list(i, typ='int128'),
                                            pos=pos),
                        add_variable_offset(right_token,
                                            LLLnode.from_list(i, typ='int128'),
                                            pos=pos),
                        location,
                        pos=pos))
            return LLLnode.from_list(
                ['with', '_L', left, ['with', '_R', right, ['seq'] + subs]],
                typ=None)
    # Structs
    elif isinstance(left.typ, (StructType, TupleType)):
        if left.value == "multi" and isinstance(left.typ, StructType):
            raise Exception("Target of set statement must be a single item")
        if not isinstance(right.typ, NullType):
            if not isinstance(right.typ, left.typ.__class__):
                raise TypeMismatchException(
                    "Setter type mismatch: left side is %r, right side is %r" %
                    (left.typ, right.typ), pos)
            if isinstance(left.typ, StructType):
                for k in left.typ.members:
                    if k not in right.typ.members:
                        raise TypeMismatchException(
                            "Keys don't match for structs, missing %s" % k,
                            pos)
                for k in right.typ.members:
                    if k not in left.typ.members:
                        raise TypeMismatchException(
                            "Keys don't match for structs, extra %s" % k, pos)
            else:
                if len(left.typ.members) != len(right.typ.members):
                    raise TypeMismatchException(
                        "Tuple lengths don't match, %d vs %d" %
                        (len(left.typ.members), len(right.typ.members)), pos)
        left_token = LLLnode.from_list('_L',
                                       typ=left.typ,
                                       location=left.location)
        if left.location == "storage":
            left = LLLnode.from_list(['sha3_32', left],
                                     typ=left.typ,
                                     location="storage_prehashed")
            left_token.location = "storage_prehashed"
        if isinstance(left.typ, StructType):
            keyz = sorted(list(left.typ.members.keys()))
        else:
            keyz = list(range(len(left.typ.members)))
        # If the right side is a literal
        if right.value == "multi":
            if len(right.args) != len(keyz):
                raise TypeMismatchException("Mismatched number of elements",
                                            pos)
            subs = []
            for i, typ in enumerate(keyz):
                subs.append(
                    make_setter(add_variable_offset(left_token, typ, pos=pos),
                                right.args[i],
                                location,
                                pos=pos))
            return LLLnode.from_list(['with', '_L', left, ['seq'] + subs],
                                     typ=None)
        # If the right side is a null
        elif isinstance(right.typ, NullType):
            subs = []
            for typ in keyz:
                subs.append(
                    make_setter(add_variable_offset(left_token, typ, pos=pos),
                                LLLnode.from_list(None, typ=NullType()),
                                location,
                                pos=pos))
            return LLLnode.from_list(['with', '_L', left, ['seq'] + subs],
                                     typ=None)
        # If tuple assign.
        elif isinstance(left.typ, TupleType) and isinstance(
                right.typ, TupleType):
            right_token = LLLnode.from_list('_R',
                                            typ=right.typ,
                                            location="memory")
            subs = []
            static_offset_counter = 0
            for idx, (left_arg,
                      right_arg) in enumerate(zip(left.args,
                                                  right.typ.members)):
                # if left_arg.typ.typ != right_arg.typ:
                #     raise TypeMismatchException("Tuple assignment mismatch position %d, expected '%s'" % (idx, right.typ), pos)
                if isinstance(right_arg, ByteArrayType):
                    offset = LLLnode.from_list([
                        'add', '_R',
                        ['mload', ['add', '_R', static_offset_counter]]
                    ],
                                               typ=ByteArrayType(
                                                   right_arg.maxlen),
                                               location='memory',
                                               pos=pos)
                    static_offset_counter += 32
                else:
                    offset = LLLnode.from_list(
                        ['mload', ['add', '_R', static_offset_counter]],
                        typ=right_arg.typ,
                        pos=pos)
                    static_offset_counter += get_size_of_type(right_arg) * 32
                subs.append(
                    make_setter(left_arg, offset, location="memory", pos=pos))
            return LLLnode.from_list(['with', '_R', right, ['seq'] + subs],
                                     typ=None,
                                     annotation='Tuple assignment')
        # If the right side is a variable
        else:
            subs = []
            right_token = LLLnode.from_list('_R',
                                            typ=right.typ,
                                            location=right.location)
            for typ in keyz:
                subs.append(
                    make_setter(add_variable_offset(left_token, typ, pos=pos),
                                add_variable_offset(right_token, typ, pos=pos),
                                location,
                                pos=pos))
            return LLLnode.from_list(
                ['with', '_L', left, ['with', '_R', right, ['seq'] + subs]],
                typ=None)
    else:
        raise Exception("Invalid type for setters")
예제 #26
0
def pack_arguments(signature, args, context, pos, return_placeholder=True):
    placeholder_typ = ByteArrayType(
        maxlen=sum([get_size_of_type(arg.typ)
                    for arg in signature.args]) * 32 + 32)
    placeholder = context.new_placeholder(placeholder_typ)
    setters = [['mstore', placeholder, signature.method_id]]
    needpos = False
    staticarray_offset = 0
    expected_arg_count = len(signature.args)
    actual_arg_count = len(args)
    if actual_arg_count != expected_arg_count:
        raise StructureException(
            "Wrong number of args for: %s (%s args, expected %s)" %
            (signature.name, actual_arg_count, expected_arg_count))

    for i, (arg,
            typ) in enumerate(zip(args, [arg.typ for arg in signature.args])):
        if isinstance(typ, BaseType):
            setters.append(
                make_setter(LLLnode.from_list(placeholder +
                                              staticarray_offset + 32 + i * 32,
                                              typ=typ),
                            arg,
                            'memory',
                            pos=pos))
        elif isinstance(typ, ByteArrayType):
            setters.append([
                'mstore', placeholder + staticarray_offset + 32 + i * 32,
                '_poz'
            ])
            arg_copy = LLLnode.from_list('_s',
                                         typ=arg.typ,
                                         location=arg.location)
            target = LLLnode.from_list(['add', placeholder + 32, '_poz'],
                                       typ=typ,
                                       location='memory')
            setters.append([
                'with', '_s', arg,
                [
                    'seq',
                    make_byte_array_copier(target, arg_copy, pos),
                    [
                        'set', '_poz',
                        [
                            'add', 32,
                            ['ceil32', ['add', '_poz',
                                        get_length(arg_copy)]]
                        ]
                    ]
                ]
            ])
            needpos = True
        elif isinstance(typ, ListType):
            target = LLLnode.from_list(
                [placeholder + 32 + staticarray_offset + i * 32],
                typ=typ,
                location='memory')
            setters.append(make_setter(target, arg, 'memory', pos=pos))
            staticarray_offset += 32 * (typ.count - 1)
        else:
            raise TypeMismatchException("Cannot pack argument of type %r" %
                                        typ)

    # For private call usage, doesn't use a returner.
    returner = [[placeholder + 28]] if return_placeholder else []
    if needpos:
        return (LLLnode.from_list([
            'with', '_poz',
            len(args) * 32 + staticarray_offset, ['seq'] + setters + returner
        ],
                                  typ=placeholder_typ,
                                  location='memory'),
                placeholder_typ.maxlen - 28, placeholder + 32)
    else:
        return (LLLnode.from_list(['seq'] + setters + returner,
                                  typ=placeholder_typ,
                                  location='memory'),
                placeholder_typ.maxlen - 28, placeholder + 32)
예제 #27
0
파일: parser.py 프로젝트: DadiaoMoe/ophydia
def parse_func(code, sigs, origcode, global_ctx, _vars=None):
    if _vars is None:
        _vars = {}
    sig = FunctionSignature.from_definition(
        code, sigs=sigs, custom_units=global_ctx._custom_units)
    # Get base args for function.
    total_default_args = len(code.args.defaults)
    base_args = sig.args[:
                         -total_default_args] if total_default_args > 0 else sig.args
    default_args = code.args.args[-total_default_args:]
    default_values = dict(
        zip([arg.arg for arg in default_args], code.args.defaults))
    # __init__ function may not have defaults.
    if sig.name == '__init__' and total_default_args > 0:
        raise FunctionDeclarationException(
            "__init__ function may not have default parameters.")
    # Check for duplicate variables with globals
    for arg in sig.args:
        if arg.name in global_ctx._globals:
            raise FunctionDeclarationException(
                "Variable name duplicated between function arguments and globals: "
                + arg.name)

    # Create a local (per function) context.
    context = Context(vars=_vars,
                      global_ctx=global_ctx,
                      sigs=sigs,
                      return_type=sig.output_type,
                      is_constant=sig.const,
                      is_payable=sig.payable,
                      origcode=origcode,
                      is_private=sig.private,
                      method_id=sig.method_id)

    # Copy calldata to memory for fixed-size arguments
    max_copy_size = sum([
        32 if isinstance(arg.typ, ByteArrayType) else
        get_size_of_type(arg.typ) * 32 for arg in sig.args
    ])
    base_copy_size = sum([
        32 if isinstance(arg.typ, ByteArrayType) else
        get_size_of_type(arg.typ) * 32 for arg in base_args
    ])
    context.next_mem += max_copy_size

    clampers = []

    # Create callback_ptr, this stores a destination in the bytecode for a private
    # function to jump to after a function has executed.
    _post_callback_ptr = "{}_{}_post_callback_ptr".format(
        sig.name, sig.method_id)
    if sig.private:
        context.callback_ptr = context.new_placeholder(typ=BaseType('uint256'))
        clampers.append(
            LLLnode.from_list(['mstore', context.callback_ptr, 'pass'],
                              annotation='pop callback pointer'))
        if total_default_args > 0:
            clampers.append(['label', _post_callback_ptr])

    # private functions without return types need to jump back to
    # the calling function, as there is no return statement to handle the
    # jump.
    stop_func = [['stop']]
    if sig.output_type is None and sig.private:
        stop_func = [['jump', ['mload', context.callback_ptr]]]

    if not len(base_args):
        copier = 'pass'
    elif sig.name == '__init__':
        copier = [
            'codecopy', MemoryPositions.RESERVED_MEMORY, '~codelen',
            base_copy_size
        ]
    else:
        copier = get_arg_copier(sig=sig,
                                total_size=base_copy_size,
                                memory_dest=MemoryPositions.RESERVED_MEMORY)
    clampers.append(copier)

    # Add asserts for payable and internal
    # private never gets payable check.
    if not sig.payable and not sig.private:
        clampers.append(['assert', ['iszero', 'callvalue']])

    # Fill variable positions
    for i, arg in enumerate(sig.args):
        if i < len(base_args) and not sig.private:
            clampers.append(
                make_clamper(arg.pos, context.next_mem, arg.typ,
                             sig.name == '__init__'))
        if isinstance(arg.typ, ByteArrayType):
            context.vars[arg.name] = VariableRecord(arg.name, context.next_mem,
                                                    arg.typ, False)
            context.next_mem += 32 * get_size_of_type(arg.typ)
        else:
            context.vars[arg.name] = VariableRecord(
                arg.name, MemoryPositions.RESERVED_MEMORY + arg.pos, arg.typ,
                False)

    # Private function copiers. No clamping for private functions.
    dyn_variable_names = [
        a.name for a in base_args if isinstance(a.typ, ByteArrayType)
    ]
    if sig.private and dyn_variable_names:
        i_placeholder = context.new_placeholder(typ=BaseType('uint256'))
        unpackers = []
        for idx, var_name in enumerate(dyn_variable_names):
            var = context.vars[var_name]
            ident = "_load_args_%d_dynarg%d" % (sig.method_id, idx)
            o = make_unpacker(ident=ident,
                              i_placeholder=i_placeholder,
                              begin_pos=var.pos)
            unpackers.append(o)

        if not unpackers:
            unpackers = ['pass']

        clampers.append(
            LLLnode.from_list(
                ['seq_unchecked'] + unpackers + [
                    0
                ],  # [0] to complete full overarching 'seq' statement, see private_label.
                typ=None,
                annotation='dynamic unpacker',
                pos=getpos(code)))

    # Create "clampers" (input well-formedness checkers)
    # Return function body
    if sig.name == '__init__':
        o = LLLnode.from_list(['seq'] + clampers +
                              [parse_body(code.body, context)],
                              pos=getpos(code))
    elif is_default_func(sig):
        if len(sig.args) > 0:
            raise FunctionDeclarationException(
                'Default function may not receive any arguments.', code)
        if sig.private:
            raise FunctionDeclarationException(
                'Default function may only be public.', code)
        o = LLLnode.from_list(['seq'] + clampers +
                              [parse_body(code.body, context)],
                              pos=getpos(code))
    else:

        if total_default_args > 0:  # Function with default parameters.
            function_routine = "{}_{}".format(sig.name, sig.method_id)
            default_sigs = generate_default_arg_sigs(code, sigs,
                                                     global_ctx._custom_units)
            sig_chain = ['seq']

            for default_sig in default_sigs:
                sig_compare, private_label = get_sig_statements(
                    default_sig, getpos(code))

                # Populate unset default variables
                populate_arg_count = len(sig.args) - len(default_sig.args)
                set_defaults = []
                if populate_arg_count > 0:
                    current_sig_arg_names = {x.name for x in default_sig.args}
                    missing_arg_names = [
                        arg.arg for arg in default_args
                        if arg.arg not in current_sig_arg_names
                    ]
                    for arg_name in missing_arg_names:
                        value = Expr(default_values[arg_name],
                                     context).lll_node
                        var = context.vars[arg_name]
                        left = LLLnode.from_list(var.pos,
                                                 typ=var.typ,
                                                 location='memory',
                                                 pos=getpos(code),
                                                 mutable=var.mutable)
                        set_defaults.append(
                            make_setter(left,
                                        value,
                                        'memory',
                                        pos=getpos(code)))

                current_sig_arg_names = {x.name for x in default_sig.args}
                base_arg_names = {arg.name for arg in base_args}
                if sig.private:
                    # Load all variables in default section, if private,
                    # because the stack is a linear pipe.
                    copier_arg_count = len(default_sig.args)
                    copier_arg_names = current_sig_arg_names
                else:
                    copier_arg_count = len(default_sig.args) - len(base_args)
                    copier_arg_names = current_sig_arg_names - base_arg_names
                # Order copier_arg_names, this is very important.
                copier_arg_names = [
                    x.name for x in default_sig.args
                    if x.name in copier_arg_names
                ]

                # Variables to be populated from calldata/stack.
                default_copiers = []
                if copier_arg_count > 0:
                    # Get map of variables in calldata, with thier offsets
                    offset = 4
                    calldata_offset_map = {}
                    for arg in default_sig.args:
                        calldata_offset_map[arg.name] = offset
                        offset += 32 if isinstance(
                            arg.typ,
                            ByteArrayType) else get_size_of_type(arg.typ) * 32
                    # Copy set default parameters from calldata
                    dynamics = []
                    for arg_name in copier_arg_names:
                        var = context.vars[arg_name]
                        calldata_offset = calldata_offset_map[arg_name]
                        if sig.private:
                            _offset = calldata_offset
                            if isinstance(var.typ, ByteArrayType):
                                _size = 32
                                dynamics.append(var.pos)
                            else:
                                _size = var.size * 32
                            default_copiers.append(
                                get_arg_copier(sig=sig,
                                               memory_dest=var.pos,
                                               total_size=_size,
                                               offset=_offset))
                        else:
                            # Add clampers.
                            default_copiers.append(
                                make_clamper(calldata_offset - 4, var.pos,
                                             var.typ))
                            # Add copying code.
                            if isinstance(var.typ, ByteArrayType):
                                _offset = [
                                    'add', 4,
                                    ['calldataload', calldata_offset]
                                ]
                            else:
                                _offset = calldata_offset
                            default_copiers.append(
                                get_arg_copier(sig=sig,
                                               memory_dest=var.pos,
                                               total_size=var.size * 32,
                                               offset=_offset))

                    # Unpack byte array if necessary.
                    if dynamics:
                        i_placeholder = context.new_placeholder(
                            typ=BaseType('uint256'))
                        for idx, var_pos in enumerate(dynamics):
                            ident = 'unpack_default_sig_dyn_%d_arg%d' % (
                                default_sig.method_id, idx)
                            default_copiers.append(
                                make_unpacker(ident=ident,
                                              i_placeholder=i_placeholder,
                                              begin_pos=var_pos))
                    default_copiers.append(0)  # for over arching seq, POP

                sig_chain.append([
                    'if', sig_compare,
                    [
                        'seq', private_label,
                        LLLnode.from_list(
                            ['mstore', context.callback_ptr, 'pass'],
                            annotation='pop callback pointer',
                            pos=getpos(code)) if sig.private else ['pass'],
                        ['seq'] + set_defaults if set_defaults else ['pass'],
                        ['seq_unchecked'] +
                        default_copiers if default_copiers else ['pass'],
                        [
                            'goto', _post_callback_ptr
                            if sig.private else function_routine
                        ]
                    ]
                ])

            # With private functions all variable loading occurs in the default
            # function sub routine.
            if sig.private:
                _clampers = [['label', _post_callback_ptr]]
            else:
                _clampers = clampers

            # Function with default parameters.
            o = LLLnode.from_list(
                [
                    'seq',
                    sig_chain,
                    [
                        'if',
                        0,  # can only be jumped into
                        [
                            'seq', ['label', function_routine]
                            if not sig.private else ['pass'],
                            ['seq'] + _clampers +
                            [parse_body(c, context)
                             for c in code.body] + stop_func
                        ]
                    ]
                ],
                typ=None,
                pos=getpos(code))

        else:
            # Function without default parameters.
            sig_compare, private_label = get_sig_statements(sig, getpos(code))
            o = LLLnode.from_list([
                'if', sig_compare, ['seq'] + [private_label] + clampers +
                [parse_body(c, context) for c in code.body] + stop_func
            ],
                                  typ=None,
                                  pos=getpos(code))

    # Check for at leasts one return statement if necessary.
    if context.return_type and context.function_return_count == 0:
        raise FunctionDeclarationException(
            "Missing return statement in function '%s' " % sig.name, code)

    o.context = context
    o.total_gas = o.gas + calc_mem_gas(o.context.next_mem)
    o.func_name = sig.name
    return o
예제 #28
0
def add_variable_offset(parent, key, pos):
    typ, location = parent.typ, parent.location
    if isinstance(typ, (StructType, TupleType)):
        if isinstance(typ, StructType):
            if not isinstance(key, str):
                raise TypeMismatchException(
                    "Expecting a member variable access; cannot access element %r"
                    % key, pos)
            if key not in typ.members:
                raise TypeMismatchException(
                    "Object does not have member variable %s" % key, pos)
            subtype = typ.members[key]
            attrs = sorted(typ.members.keys())

            if key not in attrs:
                raise TypeMismatchException(
                    "Member %s not found. Only the following available: %s" %
                    (key, " ".join(attrs)), pos)
            index = attrs.index(key)
            annotation = key
        else:
            if not isinstance(key, int):
                raise TypeMismatchException(
                    "Expecting a static index; cannot access element %r" % key,
                    pos)
            attrs = list(range(len(typ.members)))
            index = key
            annotation = None
        if location == 'storage':
            return LLLnode.from_list([
                'add', ['sha3_32', parent],
                LLLnode.from_list(index, annotation=annotation)
            ],
                                     typ=subtype,
                                     location='storage')
        elif location == 'storage_prehashed':
            return LLLnode.from_list([
                'add', parent,
                LLLnode.from_list(index, annotation=annotation)
            ],
                                     typ=subtype,
                                     location='storage')
        elif location == 'memory':
            offset = 0
            for i in range(index):
                offset += 32 * get_size_of_type(typ.members[attrs[i]])
            return LLLnode.from_list(['add', offset, parent],
                                     typ=typ.members[key],
                                     location='memory',
                                     annotation=annotation)
        else:
            raise TypeMismatchException(
                "Not expecting a member variable access")

    elif isinstance(typ, MappingType):

        if isinstance(key.typ, ByteArrayType):
            if not isinstance(typ.keytype, ByteArrayType) or (
                    typ.keytype.maxlen < key.typ.maxlen):
                raise TypeMismatchException(
                    'Mapping keys of bytes cannot be cast, use exact same bytes type of: %s'
                    % str(typ.keytype), pos)
            subtype = typ.valuetype
            if len(key.args[0].args) >= 3:  # handle bytes literal.
                sub = LLLnode.from_list([
                    'seq', key,
                    [
                        'sha3', ['add', key.args[0].args[-1], 32],
                        ['mload', key.args[0].args[-1]]
                    ]
                ])
            else:
                sub = LLLnode.from_list([
                    'sha3', ['add', key.args[0].value, 32],
                    ['mload', key.args[0].value]
                ])
        else:
            subtype = typ.valuetype
            sub = base_type_conversion(key, key.typ, typ.keytype, pos=pos)

        if location == 'storage':
            return LLLnode.from_list(['sha3_64', parent, sub],
                                     typ=subtype,
                                     location='storage')
        elif location == 'memory':
            raise TypeMismatchException(
                "Can only have fixed-side arrays in memory, not mappings", pos)

    elif isinstance(typ, ListType):

        subtype = typ.subtype
        sub = [
            'uclamplt',
            base_type_conversion(key, key.typ, BaseType('int128'), pos=pos),
            typ.count
        ]

        if location == 'storage':
            return LLLnode.from_list(['add', ['sha3_32', parent], sub],
                                     typ=subtype,
                                     location='storage')
        elif location == 'storage_prehashed':
            return LLLnode.from_list(['add', parent, sub],
                                     typ=subtype,
                                     location='storage')
        elif location == 'memory':
            offset = 32 * get_size_of_type(subtype)
            return LLLnode.from_list(['add', ['mul', offset, sub], parent],
                                     typ=subtype,
                                     location='memory')
        else:
            raise TypeMismatchException("Not expecting an array access ", pos)
    else:
        raise TypeMismatchException(
            "Cannot access the child of a constant variable! %r" % typ, pos)
예제 #29
0
def get_length(arg):
    if arg.location == "memory":
        return LLLnode.from_list(['mload', arg], typ=BaseType('int128'))
    elif arg.location == "storage":
        return LLLnode.from_list(['sload', ['sha3_32', arg]],
                                 typ=BaseType('int128'))
예제 #30
0
def make_byte_slice_copier(destination, source, length, max_length, pos=None):
    # Special case: memory to memory
    if source.location == "memory" and destination.location == "memory":
        return LLLnode.from_list([
            'with', '_l', max_length,
            [
                'pop',
                [
                    'call', 18 + max_length // 10, 4, 0, source, '_l',
                    destination, '_l'
                ]
            ]
        ],
                                 typ=None,
                                 annotation='copy byte slice dest: %s' %
                                 str(destination))
    # Copy over data
    if isinstance(source.typ, NullType):
        loader = 0
    elif source.location == "memory":
        loader = [
            'mload',
            [
                'add', '_pos',
                ['mul', 32, ['mload', MemoryPositions.FREE_LOOP_INDEX]]
            ]
        ]
    elif source.location == "storage":
        loader = [
            'sload',
            ['add', '_pos', ['mload', MemoryPositions.FREE_LOOP_INDEX]]
        ]
    else:
        raise Exception("Unsupported location:" + source.location)
    # Where to paste it?
    if destination.location == "memory":
        setter = [
            'mstore',
            [
                'add', '_opos',
                ['mul', 32, ['mload', MemoryPositions.FREE_LOOP_INDEX]]
            ], loader
        ]
    elif destination.location == "storage":
        setter = [
            'sstore',
            ['add', '_opos', ['mload', MemoryPositions.FREE_LOOP_INDEX]],
            loader
        ]
    else:
        raise Exception("Unsupported location:" + destination.location)
    # Check to see if we hit the length
    checker = [
        'if',
        [
            'gt', ['mul', 32, ['mload', MemoryPositions.FREE_LOOP_INDEX]],
            '_actual_len'
        ], 'break'
    ]
    # Make a loop to do the copying
    o = [
        'with', '_pos', source,
        [
            'with', '_opos', destination,
            [
                'with', '_actual_len', length,
                [
                    'repeat', MemoryPositions.FREE_LOOP_INDEX, 0,
                    (max_length + 31) // 32, ['seq', checker, setter]
                ]
            ]
        ]
    ]
    return LLLnode.from_list(o,
                             typ=None,
                             annotation='copy byte slice src: %s dst: %s' %
                             (source, destination),
                             pos=pos)