Args:
        An ordered collection of block generators which have been consumed to
        the point where they are all at the same block height and the tails of
        the chains from that block height (in the same order).

    Returns:
        A ForkGraph
        A list of indexes of the chains that had communication problems.

def build_fork_graph(chains, tails):
    """
    Args:
        An ordered collection of block generators which have been consumed to
        the point where they are all at the same block height and the tails of
        the chains from that block height (in the same order).

    Returns:
        A ForkGraph
        A list of indexes of the chains that had communication problems.
    """
    graph = ForkGraph()
    bad_chains = []

    # Add tails to the graph first
    for i, tail in tails.items():
        for block in reversed(tail):
            graph.add_block(i, block)

    # If we are already at the common ancestor, stop
    if _compare_across(
        [tail[0] for tail in tails.values()], key=lambda block: block.ident
    ):
        return graph, bad_chains

    # Chains should now all be at the same height, so we can walk back
    # to common ancestor
    while True:
        heads = []
        for i, chain in chains.items():
            if i not in bad_chains:
                try:
                    head = next(chain)
                except StopIteration:
                    bad_chains.append(i)
                heads.append((i, head))

        for i, block in heads:
            graph.add_block(i, block)
        if _compare_across(heads, key=lambda head: head[1].ident):
            break

    prune_unreporting_peers(graph, bad_chains)

    return graph, bad_chains