Example #1
0
 def __init__(self, vyper_name: str, viper_name: str, vyper_type: VyperType, viper_ast: ViperAST,
              pos=None, info=None, is_local=True):
     self.name = vyper_name
     self.mangled_name = viper_name
     self.type = vyper_type
     self.viper_ast = viper_ast
     self.pos = pos
     self.info = info
     self.is_local = is_local
     self._type_translator = TypeTranslator(viper_ast)
Example #2
0
class TranslatedVar:

    def __init__(self, vyper_name: str, viper_name: str, vyper_type: VyperType, viper_ast: ViperAST,
                 pos=None, info=None, is_local=True):
        self.name = vyper_name
        self.mangled_name = viper_name
        self.type = vyper_type
        self.viper_ast = viper_ast
        self.pos = pos
        self.info = info
        self.is_local = is_local
        self._type_translator = TypeTranslator(viper_ast)

    def var_decl(self, ctx: Context, pos=None, info=None) -> VarDecl:
        pos = pos or self.pos
        info = info or self.info
        vtype = self._type_translator.translate(self.type, ctx, is_local=self.is_local)
        return self.viper_ast.LocalVarDecl(self.mangled_name, vtype, pos, info)

    def local_var(self, ctx: Context, pos=None, info=None) -> Var:
        pos = pos or self.pos
        info = info or self.info
        vtype = self._type_translator.translate(self.type, ctx, is_local=self.is_local)
        return self.viper_ast.LocalVar(self.mangled_name, vtype, pos, info)
Example #3
0
    def __init__(self, viper_ast: ViperAST):
        self.viper_ast = viper_ast

        self.type_translator = TypeTranslator(viper_ast)
Example #4
0
class ModelTranslator(CommonTranslator):
    def __init__(self, viper_ast: ViperAST):
        self.viper_ast = viper_ast

        self.type_translator = TypeTranslator(viper_ast)

    def save_variables(self,
                       res: List[Stmt],
                       ctx: Context,
                       pos=None) -> Optional[ModelTransformation]:
        # Viper only gives a model for variables, therefore we save all important expressions
        # in variables and provide a mapping back to the Vyper expression. Also, we give a value
        # transformation to change, e.g., 12 to 0.0000000012 for decimals.
        if not ctx.options.create_model:
            return [], None

        self_var = ctx.self_var.local_var(ctx)
        old_self_var = ctx.old_self_var.local_var(ctx)
        transform = {}
        type_map = {}

        def add_model_var(name, var_type, rhs, components):
            new_var_name = ctx.new_local_var_name(
                mangled.model_var_name(*components))
            vtype = self.type_translator.translate(var_type, ctx)
            new_var = self.viper_ast.LocalVarDecl(new_var_name, vtype, pos)
            ctx.new_local_vars.append(new_var)
            transform[new_var_name] = name
            type_map[new_var_name] = var_type
            if hasattr(rhs, 'isSubtype') and rhs.isSubtype(
                    helpers.wrapped_int_type(self.viper_ast)):
                rhs = helpers.w_unwrap(self.viper_ast, rhs)
            res.append(
                self.viper_ast.LocalVarAssign(new_var.localVar(), rhs, pos))

        def add_struct_members(struct, struct_type, components, wrapped=None):
            for member, member_type in struct_type.member_types.items():
                new_components = components + [member]
                mtype = self.type_translator.translate(member_type, ctx)
                get = helpers.struct_get(self.viper_ast, struct, member, mtype,
                                         struct_type, pos)
                if isinstance(member_type, StructType):
                    add_struct_members(get, member_type, new_components,
                                       wrapped)
                else:
                    if member == mangled.SELFDESTRUCT_FIELD:
                        name = f'{names.SELFDESTRUCT}()'
                    elif member == mangled.SENT_FIELD:
                        name = f'{names.SENT}()'
                    elif member == mangled.RECEIVED_FIELD:
                        name = f'{names.RECEIVED}()'
                    else:
                        name = '.'.join(new_components)

                    if wrapped:
                        name = wrapped(name)

                    add_model_var(name, member_type, get, new_components)

        add_struct_members(self_var, ctx.program.type, [names.SELF])
        add_struct_members(old_self_var, ctx.program.type, [names.SELF],
                           lambda n: f'{names.OLD}({n})')
        if ctx.function.analysis.uses_issued:
            issued_self_var = ctx.pre_self_var.local_var(ctx)
            add_struct_members(issued_self_var, ctx.program.type, [names.SELF],
                               lambda n: f'{names.ISSUED}({n})')

        for var in chain(ctx.args.values(), ctx.locals.values()):
            if isinstance(var.type, StructType):
                add_struct_members(var.local_var(ctx), var.type, [var.name])
            else:
                transform[var.mangled_name] = var.name
                type_map[var.mangled_name] = var.type

        if ctx.success_var is not None:
            transform[ctx.success_var.mangled_name] = f'{names.SUCCESS}()'
            type_map[ctx.success_var.mangled_name] = ctx.success_var.type
        if ctx.result_var is not None:
            transform[ctx.result_var.mangled_name] = f'{names.RESULT}()'
            type_map[ctx.result_var.mangled_name] = ctx.result_var.type

        transform[mangled.OUT_OF_GAS] = f'{names.OUT_OF_GAS}()'
        type_map[mangled.OUT_OF_GAS] = types.VYPER_BOOL
        transform[mangled.OVERFLOW] = f'{names.OVERFLOW}()'
        type_map[mangled.OVERFLOW] = types.VYPER_BOOL

        return (transform, type_map)
Example #5
0
 def __init__(self, viper_ast: ViperAST):
     super().__init__(viper_ast)
     self.type_translator = TypeTranslator(self.viper_ast)
Example #6
0
class StateTranslator(CommonTranslator):
    def __init__(self, viper_ast: ViperAST):
        super().__init__(viper_ast)
        self.type_translator = TypeTranslator(self.viper_ast)

    @property
    def expression_translator(self):
        from twovyper.translation.specification import ExpressionTranslator
        return ExpressionTranslator(self.viper_ast)

    def state(self, name_transformation: Callable[[str], str], ctx: Context):
        def self_var(name):
            return TranslatedVar(names.SELF, name, ctx.self_type,
                                 self.viper_ast)

        def contract_var(name):
            contracts_type = helpers.contracts_type()
            return TranslatedVar(mangled.CONTRACTS, name, contracts_type,
                                 self.viper_ast)

        def allocated_var(name):
            allocated_type = helpers.allocated_type()
            return TranslatedVar(mangled.ALLOCATED, name, allocated_type,
                                 self.viper_ast)

        def offered_var(name):
            offered_type = helpers.offered_type()
            return TranslatedVar(mangled.OFFERED, name, offered_type,
                                 self.viper_ast)

        def trusted_var(name):
            trusted_type = helpers.trusted_type()
            return TranslatedVar(mangled.TRUSTED, name, trusted_type,
                                 self.viper_ast)

        s = {
            names.SELF: self_var(name_transformation(mangled.SELF)),
            mangled.CONTRACTS:
            contract_var(name_transformation(mangled.CONTRACTS))
        }

        if ctx.program.config.has_option(names.CONFIG_ALLOCATION):
            s[mangled.ALLOCATED] = allocated_var(
                name_transformation(mangled.ALLOCATED))
            s[mangled.OFFERED] = offered_var(
                name_transformation(mangled.OFFERED))
            s[mangled.TRUSTED] = trusted_var(
                name_transformation(mangled.TRUSTED))

        return s

    @staticmethod
    def _is_self(state_var: str) -> bool:
        return state_var == mangled.SELF

    @staticmethod
    def _is_non_contracts(state_var: str) -> bool:
        return state_var != mangled.CONTRACTS

    @staticmethod
    def is_allocation(state_var: str) -> bool:
        return (state_var == mangled.ALLOCATED or state_var == mangled.TRUSTED
                or state_var == mangled.OFFERED)

    def initialize_state(self, state: State, res: List[Stmt], ctx: Context):
        """
        Initializes the state belonging to the current contract, namely self and allocated,
        to its default value.
        """
        for var in state.values():
            if self._is_non_contracts(var.name):
                default = self.type_translator.default_value(
                    None, var.type, res, ctx)
                assign = self.viper_ast.LocalVarAssign(var.local_var(ctx),
                                                       default)
                res.append(assign)

    def copy_state(self,
                   from_state: State,
                   to_state: State,
                   res: List[Stmt],
                   ctx: Context,
                   pos=None,
                   unless=None):
        copies = []
        for name in set(from_state) & set(to_state):
            if unless and unless(name):
                continue
            to_var = to_state[name].local_var(ctx)
            from_var = from_state[name].local_var(ctx)
            copies.append(self.viper_ast.LocalVarAssign(to_var, from_var, pos))

        self.seqn_with_info(copies, "Copy state", res)

    def assume_type_assumptions_for_state(self, state_dict: State, name: str,
                                          res, ctx):
        stmts = []
        for state_var in state_dict.values():
            type_assumptions = self.type_translator.type_assumptions(
                state_var.local_var(ctx), state_var.type, ctx)
            stmts.extend(
                self.viper_ast.Inhale(type_assumption)
                for type_assumption in type_assumptions)

        return self.seqn_with_info(stmts, f"{name} state assumptions", res)

    def havoc_state_except_self(self,
                                state: State,
                                res: List[Stmt],
                                ctx: Context,
                                pos=None):
        """
        Havocs all contract state except self and self-allocated.
        """

        with ctx.inline_scope(None):

            def inlined_pre_state(name: str) -> str:
                return ctx.inline_prefix + mangled.pre_state_var_name(name)

            old_state_for_performs = self.state(inlined_pre_state, ctx)

        for val in old_state_for_performs.values():
            ctx.new_local_vars.append(val.var_decl(ctx, pos))

        self.copy_state(ctx.current_state,
                        old_state_for_performs,
                        res,
                        ctx,
                        unless=lambda n: not self.is_allocation(n))
        self.copy_state(ctx.current_old_state,
                        old_state_for_performs,
                        res,
                        ctx,
                        unless=self.is_allocation)
        self.havoc_state(state, res, ctx, pos, unless=self._is_self)
        with ctx.state_scope(ctx.current_state, old_state_for_performs):
            self.expression_translator.assume_own_resources_stayed_constant(
                res, ctx, pos)

    def havoc_state(self,
                    state: State,
                    res: List[Stmt],
                    ctx: Context,
                    pos=None,
                    unless=None):
        havocs = []
        for var in state.values():
            if unless and unless(var.name):
                continue
            havoc_name = ctx.new_local_var_name('havoc')
            havoc_var = self.viper_ast.LocalVarDecl(havoc_name,
                                                    var.var_decl(ctx).typ(),
                                                    pos)
            ctx.new_local_vars.append(havoc_var)
            havocs.append(
                self.viper_ast.LocalVarAssign(var.local_var(ctx),
                                              havoc_var.localVar(), pos))

        self.seqn_with_info(havocs, "Havoc state", res)

    def havoc_old_and_current_state(self,
                                    specification_translator,
                                    res,
                                    ctx,
                                    pos=None):
        # Havoc states
        self.havoc_state(ctx.current_state, res, ctx, pos)
        self.havoc_state(ctx.current_old_state, res, ctx, pos)
        self.assume_type_assumptions_for_state(ctx.current_state, "Present",
                                               res, ctx)
        self.assume_type_assumptions_for_state(ctx.current_old_state, "Old",
                                               res, ctx)
        assume_invs = []
        # Assume Invariants for old state
        with ctx.state_scope(ctx.current_old_state, ctx.current_old_state):
            for inv in ctx.unchecked_invariants():
                assume_invs.append(self.viper_ast.Inhale(inv))

            # Assume implemented interface invariants
            for interface_type in ctx.program.implements:
                interface = ctx.program.interfaces[interface_type.name]
                with ctx.program_scope(interface):
                    for inv in ctx.current_program.invariants:
                        cond = specification_translator.translate_invariant(
                            inv, assume_invs, ctx, True)
                        inv_pos = self.to_position(inv, ctx,
                                                   rules.INHALE_INVARIANT_FAIL)
                        assume_invs.append(self.viper_ast.Inhale(
                            cond, inv_pos))

            # Assume own invariants
            for inv in ctx.current_program.invariants:
                cond = specification_translator.translate_invariant(
                    inv, assume_invs, ctx, True)
                inv_pos = self.to_position(inv, ctx,
                                           rules.INHALE_INVARIANT_FAIL)
                assume_invs.append(self.viper_ast.Inhale(cond, inv_pos))
        # Assume Invariants for current state
        with ctx.state_scope(ctx.current_state, ctx.current_state):
            for inv in ctx.unchecked_invariants():
                assume_invs.append(self.viper_ast.Inhale(inv))
        self.seqn_with_info(assume_invs, "Assume invariants", res)

    def check_first_public_state(self,
                                 res: List[Stmt],
                                 ctx: Context,
                                 set_false: bool,
                                 pos=None,
                                 info=None):
        stmts = []
        for name in ctx.current_state:
            if self._is_non_contracts(name):
                current_var = ctx.current_state[name].local_var(ctx)
                old_var = ctx.current_old_state[name].local_var(ctx)
                assign = self.viper_ast.LocalVarAssign(old_var, current_var)
                stmts.append(assign)

        first_public_state = helpers.first_public_state_var(
            self.viper_ast, pos).localVar()

        if set_false:
            false = self.viper_ast.FalseLit(pos)
            var_assign = self.viper_ast.LocalVarAssign(first_public_state,
                                                       false, pos)
            stmts.append(var_assign)

        res.extend(
            helpers.flattened_conditional(self.viper_ast, first_public_state,
                                          stmts, [], pos))
Example #7
0
 def __init__(self, viper_ast: ViperAST):
     super().__init__(viper_ast)
     self.specification_translator = SpecificationTranslator(viper_ast)
     self.type_translator = TypeTranslator(viper_ast)
Example #8
0
class LemmaTranslator(CommonTranslator):
    def __init__(self, viper_ast: ViperAST):
        super().__init__(viper_ast)
        self.specification_translator = SpecificationTranslator(viper_ast)
        self.type_translator = TypeTranslator(viper_ast)

    def translate(self, function: VyperFunction,
                  ctx: Context) -> List[Function]:
        with ctx.function_scope():
            with ctx.lemma_scope():
                pos = self.to_position(function.node, ctx)

                ctx.function = function

                args = {
                    name: self._translate_non_local_var(var, ctx)
                    for name, var in function.args.items()
                }

                ctx.present_state = {}
                ctx.old_state = {}
                ctx.pre_state = {}
                ctx.issued_state = {}
                ctx.current_state = {}
                ctx.current_old_state = {}

                ctx.args = args.copy()
                ctx.locals = {}

                ctx.success_var = None
                ctx.return_label = None
                ctx.revert_label = None
                ctx.result_var = None

                preconditions = []
                # Type assumptions for arguments
                for var in function.args.values():
                    local_var = args[var.name].local_var(ctx)
                    assumptions = self.type_translator.type_assumptions(
                        local_var, var.type, ctx)
                    preconditions.extend(assumptions)

                # Explicit preconditions
                for precondition in function.preconditions:
                    stmts = []
                    preconditions.append(
                        self.specification_translator.
                        translate_pre_or_postcondition(precondition, stmts,
                                                       ctx))
                    assert not stmts

                # If we assume uninterpreted function and assert the interpreted ones, the lemma has no body
                body = None if function.is_interpreted(
                ) else self.viper_ast.TrueLit()

                postconditions = []
                interpreted_postconditions = []
                if function.is_interpreted():
                    # Without a body, we must ensure that "result == true"
                    viper_result = self.viper_ast.Result(
                        self.viper_ast.Bool, pos)
                    postconditions.append(
                        self.viper_ast.EqCmp(viper_result,
                                             self.viper_ast.TrueLit(), pos))
                for idx, stmt in enumerate(function.node.body):
                    assert isinstance(stmt, ast.ExprStmt)
                    expr = stmt.value
                    stmts = []
                    post = self.specification_translator.translate(
                        expr, stmts, ctx)
                    post_pos = self.to_position(stmt,
                                                ctx,
                                                rules=rules.LEMMA_FAIL)
                    viper_result = self.viper_ast.Result(
                        self.viper_ast.Bool, post_pos)
                    postconditions.append(
                        self.viper_ast.EqCmp(viper_result, post, post_pos))
                    if function.is_interpreted():
                        # If the function is interpreted, generate also an interpreted version of the lemma step
                        with ctx.interpreted_scope():
                            interpreted_post = self.specification_translator.translate(
                                expr, stmts, ctx)
                            interpreted_postconditions.append(
                                self.viper_ast.EqCmp(viper_result,
                                                     interpreted_post,
                                                     post_pos))
                    assert not stmts

                args_list = [arg.var_decl(ctx) for arg in args.values()]
                viper_name = mangled.lemma_name(function.name)
                viper_functions = [
                    self.viper_ast.Function(viper_name, args_list,
                                            self.viper_ast.Bool, preconditions,
                                            postconditions, body, pos)
                ]
                if function.is_interpreted():
                    # If we have interpreted postconditions, generate a second function.
                    # This second function has always a body, the same arguments and the same preconditions, but uses
                    # interpreted mul, div and mod instead of the uninterpreted $Int-functions in the postconditions.
                    viper_functions.append(
                        self.viper_ast.Function("interpreted$" + viper_name,
                                                args_list, self.viper_ast.Bool,
                                                preconditions,
                                                interpreted_postconditions,
                                                self.viper_ast.TrueLit(), pos))
        return viper_functions

    def _translate_non_local_var(self, var: VyperVar, ctx: Context):
        pos = self.to_position(var.node, ctx)
        name = mangled.local_var_name(ctx.inline_prefix, var.name)
        return TranslatedVar(var.name,
                             name,
                             var.type,
                             self.viper_ast,
                             pos,
                             is_local=False)