예제 #1
0
 def _check_no_ghost_function(self):
     if self.ghost_functions:
         cond = "Ghost function declaration"
         node = first(self.ghost_functions.values()).node
     elif self.ghost_function_implementations:
         cond = "Ghost function definition"
         node = first(self.ghost_function_implementations.values()).node
     else:
         return
     raise InvalidProgramException(
         node, 'invalid.ghost', f'{cond} only allowed after "#@ interface"')
예제 #2
0
def _check_ghost_implements(program: VyperProgram):
    def check(cond, node, ghost_name, interface_name):
        if not cond:
            raise InvalidProgramException(
                node, 'ghost.not.implemented',
                f'The ghost function "{ghost_name}" from the interface "{interface_name}" '
                f'has not been implemented correctly.')

    ghost_function_implementations = dict(
        program.ghost_function_implementations)

    for itype in program.implements:
        interface = program.interfaces[itype.name]
        for ghost in interface.own_ghost_functions.values():
            implementation = ghost_function_implementations.pop(
                ghost.name, None)
            check(implementation is not None, program.node, ghost.name,
                  itype.name)
            check(implementation.name == ghost.name, implementation.node,
                  ghost.name, itype.name)
            check(
                len(implementation.args) == len(ghost.args),
                implementation.node, ghost.name, itype.name)
            check(implementation.type == ghost.type, implementation.node,
                  ghost.name, itype.name)

    if len(ghost_function_implementations) > 0:
        raise InvalidProgramException(
            first(ghost_function_implementations.values()).node,
            'invalid.ghost.implemented',
            f'This contract implements some ghost functions that have no declaration in '
            f'any of the implemented interfaces.\n'
            f'(Ghost functions without declaration: {list(ghost_function_implementations)})'
        )
예제 #3
0
 def is_canditate(f: VyperFunction) -> bool:
     if not f.is_public():
         return False
     elif len(f.args) == 1:
         return first(f.args.values()).type == types.VYPER_WEI_VALUE
     else:
         return not f.args
예제 #4
0
    def compute(self, program: VyperProgram):
        send_functions = []

        for function in program.functions.values():
            self.visit(function.node, function, send_functions)

        def is_canditate(f: VyperFunction) -> bool:
            if not f.is_public():
                return False
            elif len(f.args) == 1:
                return first(f.args.values()).type == types.VYPER_WEI_VALUE
            else:
                return not f.args

        def val(f: VyperFunction):
            name = f.name.lower()
            if name == names.WITHDRAW:
                return 0
            elif names.WITHDRAW in name:
                return 1
            else:
                return 3

        candidates = sorted((f for f in send_functions if is_canditate(f)),
                            key=val)
        program.analysis.accessible_function = first(candidates)
예제 #5
0
def _check_ghost_functions(program: VyperProgram):
    if not isinstance(program, VyperInterface):
        node = first(program.node.stmts) or program.node
        for implemented_ghost in program.ghost_function_implementations.values(
        ):
            if program.ghost_functions.get(implemented_ghost.name) is None:
                raise InvalidProgramException(
                    implemented_ghost.node, 'missing.ghost',
                    f'This contract is implementing an unknown ghost function. '
                    f'None of the interfaces, this contract implements, declares a ghost '
                    f'function "{implemented_ghost.name}".')

        for interface in program.interfaces.values():
            for ghost_function_list in interface.ghost_functions.values():
                for ghost_function in ghost_function_list:
                    imported_ghost_functions = [
                        ghost_func
                        for ghost_func in program.ghost_functions.get(
                            ghost_function.name, [])
                        if ghost_func.file == ghost_function.file
                    ]
                    if not imported_ghost_functions:
                        prefix_length = len(
                            os.path.commonprefix(
                                [ghost_function.file, program.file]))
                        raise InvalidProgramException(
                            node, 'missing.ghost',
                            f'The interface "{interface.name}" '
                            f'needs a ghost function "{ghost_function.name}" from '
                            f'".{os.path.sep}{ghost_function.file[prefix_length:]}" but it '
                            f'was not imported for this contract.')
                    imported_ghost_functions = [
                        ghost_func for ghost_func in
                        program.ghost_functions.get(ghost_function.name)
                        if ghost_func.interface == ghost_function.interface
                    ]
                    for imported_ghost_function in imported_ghost_functions:
                        if ghost_function.file != imported_ghost_function.file:
                            prefix_length = len(
                                os.path.commonprefix([
                                    ghost_function.file,
                                    imported_ghost_function.file
                                ]))
                            ghost_function_file = ghost_function.file[
                                prefix_length:]
                            imported_ghost_function_file = imported_ghost_function.file[
                                prefix_length:]
                            raise InvalidProgramException(
                                node, 'duplicate.ghost',
                                f'There are two versions of the ghost function '
                                f'"{ghost_function.name}" defined in an interface '
                                f'"{ghost_function.interface}" one from '
                                f'[...]"{imported_ghost_function_file}" the other from '
                                f'[...]"{ghost_function_file}".')
예제 #6
0
    def check(self, program: VyperProgram):
        if program.resources and not program.config.has_option(names.CONFIG_ALLOCATION):
            msg = "Resources require allocation config option."
            raise InvalidProgramException(first(program.node.stmts) or program.node, 'alloc.not.alloc', msg)

        seen_functions = set()
        for implements in program.real_implements:
            interface = program.interfaces.get(implements.name)
            if interface is not None:
                function_names = set(function.name for function in interface.functions.values())
            else:
                contract = program.contracts[implements.name]
                function_names = set(contract.type.function_types)
            _assert(not seen_functions & function_names, program.node, 'invalid.implemented.interfaces',
                    f'Implemented interfaces should not have a function that shares the name with another function of '
                    f'another implemented interface.\n'
                    f'(Conflicting functions: {seen_functions & function_names})')
            seen_functions.update(function_names)

        for function in program.functions.values():
            self.visit(function.node, _Context.CODE, program, function)
            if function.is_pure():
                self._function_pure_checker.check_function(function, program)

            for postcondition in function.postconditions:
                self.visit(postcondition, _Context.POSTCONDITION, program, function)

            if function.preconditions:
                _assert(not function.is_public(), function.preconditions[0],
                        'invalid.preconditions', 'Public functions are not allowed to have preconditions.')
            for precondition in function.preconditions:
                self.visit(precondition, _Context.PRECONDITION, program, function)

            if function.checks:
                _assert(not program.is_interface(), function.checks[0],
                        'invalid.checks', 'No checks are allowed in interfaces.')
            for check in function.checks:
                self.visit(check, _Context.CHECK, program, function)

            if function.performs:
                _assert(function.name != names.INIT, function.performs[0],
                        'invalid.performs', '__init__ does not require and must not have performs clauses.')
                _assert(function.is_public(), function.performs[0],
                        'invalid.performs', 'Private functions are not allowed to have performs clauses.')
            for performs in function.performs:
                self._visit_performs(performs, program, function)

        for lemma in program.lemmas.values():
            for default_val in lemma.defaults.values():
                _assert(default_val is None, lemma.node, 'invalid.lemma')
            _assert(not lemma.postconditions, first(lemma.postconditions), 'invalid.lemma',
                    'No postconditions are allowed for lemmas.')
            _assert(not lemma.checks, first(lemma.checks), 'invalid.lemma',
                    'No checks are allowed for lemmas.')
            _assert(not lemma.performs, first(lemma.performs), 'invalid.lemma')

            self.visit(lemma.node, _Context.LEMMA, program, lemma)
            for stmt in lemma.node.body:
                _assert(isinstance(stmt, ast.ExprStmt), stmt, 'invalid.lemma',
                        'All steps of the lemma should be expressions')

            for precondition in lemma.preconditions:
                self.visit(precondition, _Context.LEMMA, program, lemma)

        for invariant in program.invariants:
            self.visit(invariant, _Context.INVARIANT, program, None)

        if program.general_checks:
            _assert(not program.is_interface(), program.general_checks[0],
                    'invalid.checks', 'No checks are allowed in interfaces.')
        for check in program.general_checks:
            self.visit(check, _Context.CHECK, program, None)

        for postcondition in program.general_postconditions:
            self.visit(postcondition, _Context.POSTCONDITION, program, None)

        if program.transitive_postconditions:
            _assert(not program.is_interface(), program.transitive_postconditions[0],
                    'invalid.transitive.postconditions', 'No transitive postconditions are allowed in interfaces')
        for postcondition in program.transitive_postconditions:
            self.visit(postcondition, _Context.TRANSITIVE_POSTCONDITION, program, None)

        for ghost_function in program.ghost_function_implementations.values():
            self.visit(ghost_function.node, _Context.GHOST_FUNCTION, program, None)

        if isinstance(program, VyperInterface):
            for caller_private in program.caller_private:
                self._visited_caller_spec = False
                self._num_visited_conditional = 0
                self.visit(caller_private, _Context.CALLER_PRIVATE, program, None)
                _assert(self._visited_caller_spec, caller_private, 'invalid.caller.private',
                        'A caller private expression must contain "caller()"')
                _assert(self._num_visited_conditional <= 1, caller_private, 'invalid.caller.private',
                        'A caller private expression can only contain at most one "conditional(...)"')
예제 #7
0
def _check_resources(program: VyperProgram):
    if not isinstance(program, VyperInterface):
        node = first(program.node.stmts) or program.node
        for interface in program.interfaces.values():
            for resource_name, resources_list in interface.resources.items():
                for resource in resources_list:
                    if resource.file is None:
                        if program.resources.get(resource_name) is None:
                            if not program.config.has_option(
                                    names.CONFIG_ALLOCATION):
                                raise InvalidProgramException(
                                    node, 'alloc.not.alloc',
                                    f'The interface "{interface.name}" uses the '
                                    f'allocation config option. Therefore, this contract '
                                    f'also has to enable this config option.')
                            raise InvalidProgramException(
                                node, 'missing.resource',
                                f'The interface "{interface.name}" '
                                f'needs a default resource "{resource_name}" '
                                f'that is not present in this contract.')
                        continue
                    imported_resources = [
                        r for r in program.resources.get(resource_name, [])
                        if r.file == resource.file
                    ]
                    if not imported_resources:
                        prefix_length = len(
                            os.path.commonprefix([resource.file,
                                                  program.file]))
                        raise InvalidProgramException(
                            node, 'missing.resource',
                            f'The interface "{interface.name}" '
                            f'needs a resource "{resource_name}" from '
                            f'".{os.path.sep}{resource.file[prefix_length:]}" but it '
                            f'was not imported for this contract.')
                    imported_resources = [
                        r for r in program.resources.get(resource_name)
                        if r.interface == resource.interface
                    ]
                    for imported_resource in imported_resources:
                        if resource.file != imported_resource.file:
                            prefix_length = len(
                                os.path.commonprefix(
                                    [resource.file, imported_resource.file]))
                            resource_file = resource.file[prefix_length:]
                            imported_resource_file = imported_resource.file[
                                prefix_length:]
                            raise InvalidProgramException(
                                node, 'duplicate.resource',
                                f'There are two versions of the resource '
                                f'"{resource_name}" defined in an interface '
                                f'"{imported_resource.interface}" one from '
                                f'[...]"{imported_resource_file}" the other from '
                                f'[...]"{resource_file}".')

        for interface_type in program.implements:
            interface = program.interfaces[interface_type.name]
            for resource_name, resource in program.declared_resources.items():
                if resource_name in interface.own_resources:
                    raise InvalidProgramException(
                        resource.node, 'duplicate.resource',
                        f'A contract cannot redeclare a resource it already imports. '
                        f'The resource "{resource_name}" got already declared in the '
                        f'interface {interface.name}.')
예제 #8
0
 def _check_no_lemmas(self):
     if self.lemmas:
         raise InvalidProgramException(
             first(self.lemmas.values()).node, 'invalid.lemma',
             'Lemmas are not allowed in interfaces')
예제 #9
0
    def build(self, node) -> VyperProgram:
        self.visit(node)
        # No trailing local specs allowed
        self._check_no_local_spec()

        self.config = self.config or Config([])

        if self.config.has_option(names.CONFIG_ALLOCATION):
            # Add wei underlying resource
            underlying_wei_type = ResourceType(names.UNDERLYING_WEI, {})
            underlying_wei_resource = Resource(underlying_wei_type, None, None)
            self.resources[names.UNDERLYING_WEI] = underlying_wei_resource
            # Create a fake node for the underlying wei resource
            fake_node = ast.Name(names.UNDERLYING_WEI)
            copy_pos_from(node, fake_node)
            fake_node.is_ghost_code = True
            if not self.config.has_option(names.CONFIG_NO_DERIVED_WEI):
                # Add wei derived resource
                wei_type = DerivedResourceType(names.WEI, {},
                                               underlying_wei_type)
                wei_resource = Resource(wei_type, None, None, fake_node)
                self.resources[names.WEI] = wei_resource

        if self.is_interface:
            interface_type = InterfaceType(self.name)
            if self.parse_level <= 2:
                return VyperInterface(node, self.path, self.name, self.config,
                                      self.functions, self.interfaces,
                                      self.resources,
                                      self.local_state_invariants,
                                      self.inter_contract_invariants,
                                      self.general_postconditions,
                                      self.transitive_postconditions,
                                      self.general_checks, self.caller_private,
                                      self.ghost_functions, interface_type)
            else:
                return VyperInterface(node,
                                      self.path,
                                      self.name,
                                      self.config, {}, {},
                                      self.resources, [], [], [], [], [], [],
                                      self.ghost_functions,
                                      interface_type,
                                      is_stub=True)
        else:
            if self.caller_private:
                node = first(self.caller_private)
                raise InvalidProgramException(
                    node, 'invalid.caller.private',
                    'Caller private is only allowed in interfaces')
            # Create the self-type
            self_type = SelfType(self.field_types)
            self_struct = VyperStruct(names.SELF, self_type, None)

            return VyperProgram(
                node, self.path, self.config, self_struct, self.functions,
                self.interfaces, self.structs, self.contracts, self.events,
                self.resources, self.local_state_invariants,
                self.inter_contract_invariants, self.general_postconditions,
                self.transitive_postconditions, self.general_checks,
                self.lemmas, self.implements,
                self.implements + self.additional_implements,
                self.ghost_function_implementations)