Bitcoin full node and query server

Jun 24, 2026

View in context in the libbitcoin Slack #general mirror.

josie15:35
up and running, might need to do some param tuning but so far seems to be chugging through. this is 1gbps , but im requesting a 10 gbps link for this box
josie10:43
i am glad i chased it down, tho, because i do think i have something. i kept looking at the code and something didn't sit right. so here is what i believe can happen: imagine block a has a bad sig. when validating `a` the bad signature row is written into the batch table under the batch mutex. But a’s block id is not added to batched_ at that moment, rather it is posted later to the strand via push_batch(). before push_batch() runs, block b finishes and triggers batch verification. verifier sees a’s bad signature row, correctly marks a unconfirmable, and purges the table. but because a’s id is not yet in batched_, the invalid link cleanup cannot mark a’s entry as terminal. now a’s delayed push_batch() finally runs and appends a to batched_. On the next batch, process_valids() treats every non-terminal link in batched_ as valid, so a can be marked valid even though it was already marked unconfirmable for an invalid signature. relevant code paths (not using line links because i also have some local changes):
// chaser_validate_parallel.cpp
{
    std::shared_lock lock{ mutex_, std::defer_lock };
    if (capturing) lock.lock();

    if ((ec = block.connect(ctx, capture)))
        return ec;

    batched = capture.batched;
    faulted = capture.faulted;
}
^ signature rows are written during block.connect(), but the block link is not added to batched_ here.
// chaser_validate.cpp
if (batched)
{
    ++batch_backlog_;
    POST(push_batch, link, height);
    return;
}
^ link posted later
// chaser_validate_batch.cpp
batched_.push_back(link);
process_batch(is_maximum());
^ link can arrive after another batch processes rows
// chaser_validate_batch.cpp
for (const auto& link: invalids)
{
    if (!query.get_height(height, link) ||
        !query.set_block_unconfirmable(link))
        return false;      
    notify_block(system::error::invalid_signature, height, link, false);
}

std::ranges::replace_if(batched_, [&](const auto& link) NOEXCEPT
{
    return contains(invalids, link);
}, header_link::terminal);
^ this only removes invalid links that are already present in batched_
// chaser_validate_batch.cpp
for (const auto& link: batched_)
{
    if (link == header_link::terminal)
        continue;

    if (!query.get_height(height, link) ||
        !query.set_block_valid(link))
        return false;

    notify_block(system::error::success, height, link, false);
}
^ and lastly later marks every non terminal batched_ link valid, even if that link arrived late and the block had already been marked unconfirmable.
(edited)