Bitcoin full node and query server

Dec 6, 2024

View in context in the libbitcoin Slack #general mirror.

evoskuil22:02
The ad-hoc query that produces this is here.
void executor::read_test(bool dump) const
{
    constexpr auto start_tx = 1'000'000'000_u32;
    constexpr auto target_count = 100_size;

    // Set ensures unique addresses.
    std::set<hash_digest> keys{};
    auto tx = start_tx;

    logger(format("Getting first [%1%] output address hashes.") % target_count);

    auto start = fine_clock::now();
    while (!cancel_ && keys.size() < target_count)
    {
        const auto outputs = query_.get_outputs(tx++);
        if (outputs->empty())
            return;

        for (const auto& put: *outputs)
        {
            keys.emplace(put->script().hash());
            if (cancel_ || keys.size() == target_count)
                break;
        }
    }

    auto span = duration_cast<milliseconds>(fine_clock::now() - start);
    logger(format("Got first [%1%] unique addresses above tx [%2%] in [%3%] ms.") %
        keys.size() % start_tx % span.count());

    struct out
    {
        hash_digest address;

        uint32_t bk_fk;
        uint32_t bk_height;
        hash_digest bk_hash;

        uint32_t tx_fk;
        size_t tx_position;
        hash_digest tx_hash;

        uint32_t sp_tx_fk;
        hash_digest sp_tx_hash;

        uint64_t input_fk;
        chain::input::cptr input{};

        uint64_t output_fk;
        chain::output::cptr output{};
    };

    std_vector<out> outs{};
    outs.reserve(target_count);
    using namespace database;

    start = fine_clock::now();
    for (auto& key: keys)
    {
        size_t found{};
        auto address_it = store_.address.it(key);
        if (cancel_ || address_it.self().is_terminal())
            return;

        do
        {
            table::address::record address{};
            if (cancel_ || !store_.address.get(address_it.self(), address))
                return;

            const auto out_fk = address.output_fk;
            table::output::get_parent output{};
            if (!store_.output.get(out_fk, output))
                return;

            const auto tx_fk = output.parent_fk;
            const auto block_fk = query_.to_block(tx_fk);

            table::header::get_height header{};
            if (!store_.header.get(block_fk, header))
                return;

            table::txs::get_position txs{ {}, tx_fk };
            if (!store_.txs.get(query_.to_txs(block_fk), txs))
                return;

            spend_link sp_fk{};
            input_link in_fk{};
            tx_link sp_tx_fk{};

            // Get first spender only (may or may not be confirmed).
            const auto spenders = query_.to_spenders(out_fk);
            if (!spenders.empty())
            {
                sp_fk = spenders.front();
                table::spend::record spend{};
                if (!store_.spend.get(sp_fk, spend))
                    return;

                in_fk = spend.input_fk;
                sp_tx_fk = spend.parent_fk;
            }

            ++found;
            outs.push_back(out
            {
                key,

                block_fk,
                header.height,
                query_.get_header_key(block_fk),

                tx_fk,
                txs.position,
                query_.get_tx_key(tx_fk),

                sp_tx_fk,
                query_.get_tx_key(sp_tx_fk),

                in_fk,
                query_.get_input(sp_fk),

                out_fk,
                query_.get_output(out_fk)
            });
        }
        while (address_it.advance());

        logger(format("Fetched [%1%] unique payments to address [%2%].") %
            found% encode_hash(key));
    }

    span = duration_cast<milliseconds>(fine_clock::now() - start);
    logger(format("Got all [%1%] payments to [%2%] addresses in [%3%] ms.") %
        outs.size() % keys.size() % span.count());

    if (!dump)
        return;

    // Write it all...
    logger(
        "output_script_hash, "
    
        "ouput_bk_fk, "
        "ouput_bk_height, "
        "ouput_bk_hash, "

        "ouput_tx_fk, "
        "ouput_tx_position, "
        "ouput_tx_hash, "

        "input_tx_fk, "
        "input_tx_hash, "

        "output_fk, "
        "output_script, "

        "input_fk, "
        "input_script"
    );

    for (const auto& row: outs)
    {
        if (cancel_) break;

        const auto output = !row.output ? "{error}" :
            row.output->script().to_string(chain::flags::all_rules);

        const auto input = !row.input ? "{unspent}" :
            row.input->script().to_string(chain::flags::all_rules);
    
        logger(format("%1%, %2%, %3%, %4%, %5%, %6%, %7%, %8%, %9%, %10%, %11%, %12%, %13%") %
            encode_hash(row.address) %

            row.bk_fk %
            row.bk_height %
            encode_hash(row.bk_hash) %

            row.tx_fk %
            row.tx_position %
            encode_hash(row.tx_hash) %

            row.sp_tx_fk %
            encode_hash(row.sp_tx_hash) %

            row.output_fk %
            output %

            row.input_fk %
            input);
    }
}