High-level call to simple name verification:
    Given a trusted serial number, txid, or consensus_hash, use it as a trust root to verify that
    a previously-registered but untrusted name (@verify_name) exists and was processed
    at a given block (@verify_block_id)

    Basically, use the trust root to derive a "current" block ID and consensus hash, and
    use the untrusted (name, block_id) pair to derive an earlier untrusted block ID and
    consensus hash.  Then, use the snv_get_blockstack_ops_at() method to verify that the name
    existed at the given block ID.

    The Blockstack node is not trusted.  This algorithm prevents a malicious Blockstack node
    from getting the caller to falsely trust @verify_name and @verify_block_id by
    using SNV to confirm that:
    * the consensus hash at the trust root's block is consistent with @verify_name's
    corresponding NAMESPACE_PREORDER or NAME_PREORDER;
    * the consensus hash at @trusted_serial_number's block is consistent with @verify_name's
    consensus hash (from @verify_serial_number)

    The only way a Blockstack node working with a malicious Sybil can trick the caller is if
    both can create a parallel history of name operations such that the final consensus hash
    at @trusted_serial_number's block collides.  This is necessary, since the client uses
    the hash over a block's operations and prior consensus hashes to transitively trust
    prior consensus hashes--if the later consensus hash is assumed out-of-band to be valid,
    then the transitive closure of all prior consensus hashes will be assumed valid as well.
    This means that the only way to drive the valid consensus hash from a prior invalid
    consensus hash is to force a hash collision somewhere in the transitive closure, which is infeasible.

    NOTE: @trusted_txid is needed for isolating multiple operations in the same name within a single block.

    Return the list of nameops in the given verify_block_id that match.

def snv_lookup(verify_name, verify_block_id,
               trusted_serial_number_or_txid_or_consensus_hash, hostport=None, proxy=None, trusted_txid=None):

    """
    High-level call to simple name verification:
    Given a trusted serial number, txid, or consensus_hash, use it as a trust root to verify that
    a previously-registered but untrusted name (@verify_name) exists and was processed
    at a given block (@verify_block_id)

    Basically, use the trust root to derive a "current" block ID and consensus hash, and
    use the untrusted (name, block_id) pair to derive an earlier untrusted block ID and
    consensus hash.  Then, use the snv_get_blockstack_ops_at() method to verify that the name
    existed at the given block ID.

    The Blockstack node is not trusted.  This algorithm prevents a malicious Blockstack node
    from getting the caller to falsely trust @verify_name and @verify_block_id by
    using SNV to confirm that:
    * the consensus hash at the trust root's block is consistent with @verify_name's
    corresponding NAMESPACE_PREORDER or NAME_PREORDER;
    * the consensus hash at @trusted_serial_number's block is consistent with @verify_name's
    consensus hash (from @verify_serial_number)

    The only way a Blockstack node working with a malicious Sybil can trick the caller is if
    both can create a parallel history of name operations such that the final consensus hash
    at @trusted_serial_number's block collides.  This is necessary, since the client uses
    the hash over a block's operations and prior consensus hashes to transitively trust
    prior consensus hashes--if the later consensus hash is assumed out-of-band to be valid,
    then the transitive closure of all prior consensus hashes will be assumed valid as well.
    This means that the only way to drive the valid consensus hash from a prior invalid
    consensus hash is to force a hash collision somewhere in the transitive closure, which is infeasible.

    NOTE: @trusted_txid is needed for isolating multiple operations in the same name within a single block.

    Return the list of nameops in the given verify_block_id that match.
    """

    proxy = get_default_proxy(hostport) if proxy is None else proxy

    trusted_serial_number_or_txid_or_consensus_hash = str(trusted_serial_number_or_txid_or_consensus_hash)

    bitcoind_proxy = get_bitcoind_client()
    trusted_serial_number = None
    trusted_tx_index = None
    trusted_consensus_hash = None
    trusted_block_id = None

    # what did we get?
    hash_len_64 = len(trusted_serial_number_or_txid_or_consensus_hash) == 64
    hash_len_32 = len(trusted_serial_number_or_txid_or_consensus_hash) == 32
    hash_parts_2 = len(trusted_serial_number_or_txid_or_consensus_hash.split('-')) == 2
    hash_is_hex = is_hex(trusted_serial_number_or_txid_or_consensus_hash)

    if hash_len_64 and hash_is_hex:
        # txid: convert to trusted block ID and consensus hash
        trusted_txid = trusted_serial_number_or_txid_or_consensus_hash
        trusted_block_hash, trusted_block_data, trusted_tx = txid_to_block_data(trusted_txid, bitcoind_proxy)
        if trusted_block_hash is None or trusted_block_data is None or trusted_tx is None:
            return {'error': 'Unable to look up given transaction ID'}

        # must have a consensus hash
        # TOOD: Check why return values are ignored
        op, payload = parse_tx_op_return(trusted_tx)
        trusted_consensus_hash = get_consensus_hash_from_tx(trusted_tx)
        if trusted_consensus_hash is None:
            return {'error': 'Tx does not refer to a consensus-bearing transaction'}

        # find the block for this consensus hash (it's not the same as the serial number's block ID,
        # but that's okay--if the consensus hash in this tx is inauthentic, it will be unreachable
        # from the other consensus hash [short of a SHA256 collision])
        trusted_block_id = get_block_from_consensus(trusted_consensus_hash, proxy=proxy)

    elif hash_len_32 and hash_is_hex:
        # consensus hash
        trusted_consensus_hash = trusted_serial_number_or_txid_or_consensus_hash
        trusted_block_id = get_block_from_consensus(trusted_consensus_hash, proxy=proxy)
        if isinstance(trusted_block_id, dict) and 'error' in trusted_block_id:
            # got error back
            return trusted_block_id
        
    elif hash_parts_2:
        # must be a serial number
        parts = trusted_serial_number_or_txid_or_consensus_hash.split('-')
        try:
            trusted_block_id = int(parts[0])
            trusted_tx_index = int(parts[1])
        except:
            log.error('Malformed serial number "{}"'.format(trusted_serial_number_or_txid_or_consensus_hash))
            return {'error': 'Did not receive a valid serial number'}

        trusted_tx = serial_number_to_tx(trusted_serial_number_or_txid_or_consensus_hash, bitcoind_proxy)
        if trusted_tx is None:
            return {'error': 'Unable to convert given serial number into transaction'}

        # tx must have a consensus hash
        # TOOD: Check why return values are ignored
        op, payload = parse_tx_op_return(trusted_tx)
        trusted_consensus_hash = get_consensus_hash_from_tx(trusted_tx)
        if trusted_consensus_hash is None:
            return {'error': 'Tx does not refer to a consensus-bearing transaction'}

        # find the block for this consensus hash (it's not the same as the serial number's block ID,
        # but that's okay--if the consensus hash in this tx is inauthentic, it will be unreachable
        # from the other consensus hash [short of a SHA256 collision])
        trusted_block_id = get_block_from_consensus(trusted_consensus_hash, proxy=proxy)
        if isinstance(trusted_block_id, dict) and 'error' in trusted_block_id:
            # got error back
            return trusted_block_id
    else:
        msg = 'Did not receive a valid txid, consensus hash, or serial number ({})'
        return {'error': msg.format(trusted_serial_number_or_txid_or_consensus_hash)}

    if trusted_block_id < verify_block_id:
        msg = 'Trusted block/consensus hash came before the untrusted block/consensus hash'
        return {'error': msg}

    # go verify the name
    verify_consensus_hash = get_consensus_at(verify_block_id, proxy=proxy)
    historic_namerecs = snv_name_verify(
        verify_name, trusted_block_id, trusted_consensus_hash,
        verify_block_id, verify_consensus_hash,
        trusted_txid=trusted_txid, trusted_txindex=trusted_tx_index
    )

    if 'error' in historic_namerecs:
        return historic_namerecs

    return historic_namerecs['nameops']