<SYSTEM_TASK:>
Solve the following problem using Python, implementing the functions described below, one line at a time
<END_TASK>
<USER_TASK:>
Description:
 def is_invalid(self):
 """Check if this RefPkg is invalid. Valid means that it contains a properly named manifest, and each of the files described in the manifest exists and has the proper MD5 hashsum. If the Refpkg is valid, is_invalid returns False. Otherwise it returns a nonempty string describing the error. """

        # Manifest file contains the proper keys
        for k in ['metadata', 'files', 'md5']:
            if not(k in self.contents):
                return "Manifest file missing key %s" % k
            if not(isinstance(self.contents[k], dict)):
                return "Key %s in manifest did not refer to a dictionary" % k

        if not('rollback' in self.contents):
            return "Manifest file missing key rollback"
        if not(isinstance(self.contents['rollback'], dict)) and self.contents[
                "rollback"] is not None:
            return ("Key rollback in manifest did not refer to a "
                    "dictionary or None, found %s") % str(self.contents['rollback'])

        if not('rollforward' in self.contents):
            return "Manifest file missing key rollforward"

        if self.contents['rollforward'] is not None:
            if not(isinstance(self.contents['rollforward'], list)):
                return "Key rollforward was not a list, found %s" % str(
                    self.contents['rollforward'])
            elif len(self.contents['rollforward']) != 2:
                return "Key rollforward had wrong length, found %d" % \
                    len(self.contents['rollforward'])
            elif not is_string(self.contents['rollforward'][0]):
                print(type(self.contents['rollforward'][0]))
                return "Key rollforward's first entry was not a string, found %s" % \
                    str(self.contents['rollforward'][0])
            elif not(isinstance(self.contents['rollforward'][1], dict)):
                return "Key rollforward's second entry was not a dict, found %s" % \
                    str(self.contents['rollforward'][1])

        if not("log" in self.contents):
            return "Manifest file missing key 'log'"

        if not(isinstance(self.contents['log'], list)):
            return "Key 'log' in manifest did not refer to a list"

        # MD5 keys and filenames are in one to one correspondence
        if self.contents['files'].keys() != self.contents[
                'md5'].keys():
            return ("Files and MD5 sums in manifest do not "
                    "match (files: %s, MD5 sums: %s)") % \
                (list(self.contents['files'].keys()),
                 list(self.contents['md5'].keys()))

        # All files in the manifest exist and match the MD5 sums
        for key, filename in self.contents['files'].items():
            # we don't need to explicitly check for existence;
            # calculate_resource_md5 will open the file for us.
            expected_md5 = self.resource_md5(key)
            found_md5 = self.calculate_resource_md5(key)
            if found_md5 != expected_md5:
                return ("File %s referred to by key %s did "
                        "not match its MD5 sum (found: %s, expected %s)") % \
                    (filename, key, found_md5, expected_md5)
        return False