Resolve a name to its profile.
    This is a multi-step process:
    1. get the name record
    2. get the zone file
    3. parse the zone file to get its URLs (if it's not well-formed, then abort)
    4. fetch and authenticate the JWT at each URL (abort if there are none)
    5. extract the profile JSON and return that, along with the zone file and public key

    Return {'profile': ..., 'zonefile': ..., 'public_key': ...['name_rec': ...]} on success
    Return {'error': ...} on error

def resolve_profile(name, include_expired=False, include_name_record=False, hostport=None, proxy=None):
    """
    Resolve a name to its profile.
    This is a multi-step process:
    1. get the name record
    2. get the zone file
    3. parse the zone file to get its URLs (if it's not well-formed, then abort)
    4. fetch and authenticate the JWT at each URL (abort if there are none)
    5. extract the profile JSON and return that, along with the zone file and public key

    Return {'profile': ..., 'zonefile': ..., 'public_key': ...['name_rec': ...]} on success
    Return {'error': ...} on error
    """
    assert hostport or proxy, 'Need hostport or proxy'
    
    name_rec = get_name_record(name, include_history=False, include_expired=include_expired, include_grace=False, proxy=proxy, hostport=hostport)
    if 'error' in name_rec:
        log.error("Failed to get name record for {}: {}".format(name, name_rec['error']))
        return {'error': 'Failed to get name record: {}'.format(name_rec['error']), 'http_status': name_rec.get('http_status', 500)}
   
    if 'grace_period' in name_rec and name_rec['grace_period']:
        log.error("Name {} is in the grace period".format(name))
        return {'error': 'Name {} is not yet expired, but is in the renewal grace period.'.format(name), 'http_status': name_rec.get('http_status', 404)}
        
    if 'value_hash' not in name_rec:
        log.error("Name record for {} has no zone file hash".format(name))
        return {'error': 'No zone file hash in name record for {}'.format(name), 'http_status': 404}

    zonefile_hash = name_rec['value_hash']
    zonefile_res = get_zonefiles(hostport, [zonefile_hash], proxy=proxy)
    if 'error' in zonefile_res:
        log.error("Failed to get zone file for {} for name {}: {}".format(zonefile_hash, name, zonefile_res['error']))
        return {'error': 'Failed to get zone file for {}'.format(name), 'http_status': 404}

    zonefile_txt = zonefile_res['zonefiles'][zonefile_hash]
    log.debug("Got {}-byte zone file {}".format(len(zonefile_txt), zonefile_hash))

    try:
        zonefile_data = blockstack_zones.parse_zone_file(zonefile_txt)
        zonefile_data = dict(zonefile_data)
        assert 'uri' in zonefile_data
        if len(zonefile_data['uri']) == 0:
            return {'error': 'No URI records in zone file {} for {}'.format(zonefile_hash, name), 'http_status': 404}

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

        return {'error': 'Failed to parse zone file {} for {}'.format(zonefile_hash, name), 'http_status': 404}

    urls = [uri['target'] for uri in zonefile_data['uri']]
    for url in urls:
        jwt = get_JWT(url, address=str(name_rec['address']))
        if not jwt:
            continue

        if 'claim' not in jwt['payload']:
            # not something we produced
            log.warning("No 'claim' field in payload for {}".format(url))
            continue

        # success!
        profile_data = jwt['payload']['claim']
        public_key = str(jwt['payload']['issuer']['publicKey'])

        # return public key that matches address 
        pubkeys = [virtualchain.ecdsalib.ecdsa_public_key(keylib.key_formatting.decompress(public_key)),
                   virtualchain.ecdsalib.ecdsa_public_key(keylib.key_formatting.compress(public_key))]

        if name_rec['address'] == pubkeys[0].address():
            public_key = pubkeys[0].to_hex()
        else:
            public_key = pubkeys[1].to_hex()

        ret = {
            'profile': profile_data,
            'zonefile': zonefile_txt,
            'public_key': public_key,
        }

        if include_name_record:
            ret['name_record'] = name_rec

        return ret

    log.error("No zone file URLs resolved to a JWT with the public key whose address is {}".format(name_rec['address']))
    return {'error': 'No profile found for this name', 'http_status': 404}