Decode a serialized zone file into a zonefile structure that could contain subdomain info.
    Ignore duplicate subdomains.  The subdomain with the lower sequence number will be accepted.
    In the event of a tie, the *first* subdomain will be accepted

    Returns the list of subdomain operations, as Subdomain objects (optionally empty), in the order they appeared in the zone file
    Returns None if this zone file could not be decoded

def decode_zonefile_subdomains(domain, zonefile_txt, block_height, zonefile_index, txid):
    """
    Decode a serialized zone file into a zonefile structure that could contain subdomain info.
    Ignore duplicate subdomains.  The subdomain with the lower sequence number will be accepted.
    In the event of a tie, the *first* subdomain will be accepted

    Returns the list of subdomain operations, as Subdomain objects (optionally empty), in the order they appeared in the zone file
    Returns None if this zone file could not be decoded
    """
    zonefile_hash = get_zonefile_data_hash(zonefile_txt)

    try:
        # by default, it's a zonefile-formatted text file
        zonefile_defaultdict = blockstack_zones.parse_zone_file(zonefile_txt)
        zonefile_json = dict(zonefile_defaultdict)
        try:
            # zonefiles with subdomains have TXT records and URI records
            jsonschema.validate(zonefile_json, USER_ZONEFILE_SCHEMA)
        except Exception as e:
            if BLOCKSTACK_TEST:
                log.exception(e)
            
            log.debug("Failed to validate zone file {}".format(zonefile_hash))
            raise ValueError("Not a user zone file")

        assert zonefile_json['$origin'] == domain, 'Zonefile does not contain $ORIGIN == {} (but has {} instead)'.format(domain, zonefile_json['$origin'])
        
        resolver_url = None
        if 'uri' in zonefile_json:
            resolver_urls = [x['target'] for x in zonefile_json['uri'] if x['name'] == SUBDOMAIN_TXT_RR_RESOLVER]
            if len(resolver_urls) > 0:
                resolver_url = resolver_urls[0]

        subdomains = {}     # map fully-qualified name to subdomain record with lowest sequence number
        subdomain_pos = {}  # map fully-qualified name to position in zone file
        domain_zonefiles_missing = None   # list of zone files declared missing by this domain
        zonefile_offset = 0

        if "txt" in zonefile_json:
            for i, txt in enumerate(zonefile_json['txt']):
                if is_subdomain_missing_zonefiles_record(txt):
                    if domain_zonefiles_missing is not None:
                        raise ValueError("Invalid zone file: multiple RRs for {}".format(SUBDOMAIN_TXT_RR_MISSING))

                    try:
                        domain_zonefiles_missing = Subdomain.parse_subdomain_missing_zonefiles_record(txt)
                    except ParseError as pe:
                        if BLOCKSTACK_DEBUG:
                            log.exception(pe)

                        log.warn("Invalid missing-zonefiles vector at position {}".format(i))
                        continue

            if domain_zonefiles_missing is None:
                domain_zonefiles_missing = []

            for i, txt in enumerate(zonefile_json['txt']):
                if is_subdomain_record(txt):
                    try:
                        # force lowercase
                        txt['name'] = txt['name'].lower()
                        if txt['name'] in SUBDOMAIN_TXT_RR_RESERVED:
                            continue

                        subrec = Subdomain.parse_subdomain_record(domain, txt, block_height, zonefile_hash, zonefile_index, zonefile_offset, txid, domain_zonefiles_missing, resolver=resolver_url)
                        zonefile_offset += 1
                    except ParseError as pe:
                        if BLOCKSTACK_DEBUG:
                            log.exception(pe)

                        log.warn("Invalid subdomain record at position {}".format(i))
                        continue

                    if subrec.get_fqn() in subdomains:
                        if subrec.n < subdomains[subrec.get_fqn()].n:
                            # replace
                            subdomains[subrec.get_fqn()] = subrec
                            subdomain_pos[subrec.get_fqn()] = i

                        else:
                            log.warn("Ignoring subdomain record '{}' with higher sequence".format(subrec.get_fqn()))

                    else:
                        # new
                        subdomains[subrec.get_fqn()] = subrec
                        subdomain_pos[subrec.get_fqn()] = i
       
        subdomain_list = [subdomains[fqn] for fqn in subdomains]
        subdomain_list.sort(cmp=lambda subrec1, subrec2: -1 if subdomain_pos[subrec1.get_fqn()] < subdomain_pos[subrec2.get_fqn()] else 0 if subdomain_pos[subrec1.get_fqn()] == subdomain_pos[subrec2.get_fqn()] else 1)
        return subdomain_list

    except Exception as e:
        if BLOCKSTACK_TEST:
            log.exception(e)

        log.debug("Failed to parse zone file {}".format(zonefile_hash))
        return None