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)
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)
def __init__(self, viper_ast: ViperAST): self.viper_ast = viper_ast self.type_translator = TypeTranslator(viper_ast)
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)
def __init__(self, viper_ast: ViperAST): super().__init__(viper_ast) self.type_translator = TypeTranslator(self.viper_ast)
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))
def __init__(self, viper_ast: ViperAST): super().__init__(viper_ast) self.specification_translator = SpecificationTranslator(viper_ast) self.type_translator = TypeTranslator(viper_ast)
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)