void unspent_outputs::remove(const output_point& point)
{
    if (capacity_ == 0)
        return;

    const unspent_transaction key{ point };

    // Critical Section
    ///////////////////////////////////////////////////////////////////////////
    mutex_.lock_upgrade();

    // Find the unspent tx entry that may contain the output.
    auto tx = unspent_.left.find(key);

    if (tx == unspent_.left.end())
    {
        mutex_.unlock_upgrade();
        //---------------------------------------------------------------------
        return;
    }

    const auto outputs = tx->first.outputs();
    mutex_.unlock_upgrade_and_lock();
    //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

    // Erase the output if found at the specified index for the found tx.
    outputs->erase(point.index());

    // Erase the unspent transaction if it is now fully spent.
    if (outputs->empty())
        unspent_.left.erase(tx);

    mutex_.unlock();
    ///////////////////////////////////////////////////////////////////////////
}
bool unspent_outputs::get(output& out_output, size_t& out_height,
    bool& out_coinbase, const output_point& point, size_t fork_height) const
{
    if (capacity_ == 0)
        return false;

    ++queries_;
    const unspent_transaction key{ point };

    // Critical Section
    ///////////////////////////////////////////////////////////////////////////
    shared_lock lock(mutex_);

    // Find the unspent tx entry.
    const auto tx = unspent_.left.find(key);

    if (tx == unspent_.left.end())
        return false;

    // Find the output at the specified index for the found unspent tx.
    const auto outputs = tx->first.outputs();
    const auto output = outputs->find(point.index());

    if (output == outputs->end())
        return false;

    // Determine if the cached unspent tx is above specified fork_height.
    // Since the hash table does not allow duplicates there are no others.
    const auto& unspent = tx->first;
    const auto height = tx->first.height();

    if (height > fork_height)
        return false;

    ++hits_;
    out_height = height;
    out_coinbase = unspent.is_coinbase();
    out_output = output->second;
    return true;
    ///////////////////////////////////////////////////////////////////////////
}
// All responses are unspent, metadata should be defaulted by caller.
bool unspent_outputs::populate(const output_point& point,
    size_t fork_height) const
{
    if (disabled())
        return false;

    ++queries_;
    auto& prevout = point.metadata;
    const unspent_transaction key{ point };

    // Critical Section
    ///////////////////////////////////////////////////////////////////////////
    shared_lock lock(mutex_);

    // Find the unspent tx entry.
    const auto tx = unspent_.left.find(key);
    if (tx == unspent_.left.end())
        return false;

    // Find the output at the specified index for the found unspent tx.
    const auto& transaction = tx->first;
    const auto outputs = transaction.outputs();
    const auto output = outputs->find(point.index());
    if (output == outputs->end())
        return false;

    ++hits_;
    const auto height = transaction.height();

    // Populate the output metadata.
    prevout.spent = false;
    prevout.candidate = false;
    prevout.confirmed = transaction.is_confirmed() && height <= fork_height;
    prevout.coinbase = transaction.is_coinbase();
    prevout.height = height;
    prevout.median_time_past = transaction.median_time_past();
    prevout.cache = output->second;

    return true;
    ///////////////////////////////////////////////////////////////////////////
}