Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 8 additions & 7 deletions include/bitcoin/node/chasers/chaser_validate.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,12 +76,18 @@ class BCN_API chaser_validate
size_t height, bool bypass, bool batched=false,
bool faulted=false, bool capturing=false) NOEXCEPT;
virtual void notify_block(const code& ec, size_t height,
const header_link& link, bool bypass) NOEXCEPT;
const header_link& link, bool bypass, bool startup=false) NOEXCEPT;

/// Batching.
virtual bool is_residual() NOEXCEPT;
virtual bool is_mature(bool residual) NOEXCEPT;
virtual code start_batch() NOEXCEPT;
virtual void process_batch(bool residual) NOEXCEPT;
virtual void push_batch(const header_link& link, size_t height) NOEXCEPT;
virtual void process_batch(bool residual) NOEXCEPT;
virtual code do_process_batch(bool startup) NOEXCEPT;
virtual bool mark_valids(bool startup) NOEXCEPT;
virtual bool mark_invalids(const header_links& invalids,
bool startup) NOEXCEPT;

// Override base class strand because it sits on the network thread pool.
network::asio::strand& strand() NOEXCEPT override;
Expand All @@ -94,11 +100,6 @@ class BCN_API chaser_validate
using threshold = system::chain::threshold;
using missed = signatures::miss;

/// Batching helpers.
bool is_maximum() NOEXCEPT;
bool process_valids(bool residual) NOEXCEPT;
bool process_invalids(const header_links& invalids) NOEXCEPT;

// Capture handlers.
void do_log(const system::chain::script& missed) NOEXCEPT;
void do_fire(missed miss, size_t count) NOEXCEPT;
Expand Down
1 change: 1 addition & 0 deletions src/chasers/chaser_confirm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,7 @@ void chaser_confirm::organize(header_states& fork, const header_links& popped,
}
// error::unassociated
// error::unknown_state
// error::block_prevalid
// error::block_unconfirmable
default:
{
Expand Down
16 changes: 10 additions & 6 deletions src/chasers/chaser_validate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,10 @@ code chaser_validate::start() NOEXCEPT
if (!node_settings().headers_first)
return error::success;

set_position(archive().get_fork());
if (const auto ec = start_batch())
return ec;
return fault(ec);

set_position(archive().get_fork());
SUBSCRIBE_CHASE(handle_chase, _1, _2, _3);
return error::success;
}
Expand Down Expand Up @@ -192,6 +192,10 @@ void chaser_validate::do_bumped(height_t height) NOEXCEPT
complete_block(error::success, link, height, true);
break;
}
case database::error::block_prevalid:
{
break;
}
case database::error::block_unconfirmable:
{
return;
Expand Down Expand Up @@ -279,28 +283,28 @@ void chaser_validate::complete_block(const code& ec, const header_link& link,
const auto current = !capturing && !bypass;

// Drain batch when recent (current, or maximum reached without backlog).
if (current || is_maximum())
if (current || is_residual())
{
POST(process_batch, true);
}
}

void chaser_validate::notify_block(const code& ec, size_t height,
const header_link& link, bool bypass) NOEXCEPT
const header_link& link, bool bypass, bool startup) NOEXCEPT
{
// Not stranded when complete_block is called from validate_block.

if (ec)
{
// INVALID BLOCK (not a fault but discontinue)
notify(ec, chase::unvalid, link);
if (!startup) notify(ec, chase::unvalid, link);
fire(events::block_unconfirmable, height);
LOGR("Invalid block [" << height << "] " << ec.message());
return;
}

// VALID BLOCK
notify(ec, chase::valid, possible_wide_cast<height_t>(height));
if (!startup) notify(ec, chase::valid, possible_wide_cast<height_t>(height));
fire(events::block_validated, height);
LOGV("Block validated: " << height << (bypass ? " (bypass)" : ""));
}
Expand Down
136 changes: 76 additions & 60 deletions src/chasers/chaser_validate_batch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,24 +38,52 @@ BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT)
// ----------------------------------------------------------------------------
// protected

// Cannot know if archived batch is faulted, despite being otherwise full, as
// faulted is a non-persistent state. So we must purge batches at start.
bool chaser_validate::is_residual() NOEXCEPT
{
// Verify residuals when recent.
return maximum_posted_.load() &&
is_zero(batch_backlog_.load()) &&
is_zero(validate_backlog_.load());
}

bool chaser_validate::is_mature(bool residual) NOEXCEPT
{
const auto& query = archive();
const auto ecdsa = query.ecdsa_records();
const auto schnorr = query.schnorr_records();

// Verify non-residuals when mature.
return residual || (ecdsa >= batch_target_) || (schnorr >= batch_target_);
}

// If there was a non-empty batch at startup, process it for invalids and set
// their states normally, then scan from fork point (position()) to association
// gap for all prevalids. Iterate over these setting their states to valid.
code chaser_validate::start_batch() NOEXCEPT
{
auto& query = archive();
return (batch_enabled_ &&
(!query.purge_ecdsa_signatures() ||
!query.purge_schnorr_signatures())) ?
error::batch1 : error::success;
BC_ASSERT(batched_.empty());
if (!batch_enabled_)
return {};

const auto& query = archive();
if (is_zero(query.ecdsa_records()) && is_zero(query.schnorr_records()))
return {};

// Accumulate all prevalid block links above the fork point.
batched_ = query.get_prevalids(position());
return do_process_batch(true);
}

// batched_ is redundant with the combined set of ecdsa/schnorr unfailed block
// identifiers stored in the two batch tables, so just a sort optimization.
void chaser_validate::push_batch(const header_link& link, size_t height) NOEXCEPT
void chaser_validate::push_batch(const header_link& link,
size_t height) NOEXCEPT
{
BC_ASSERT(stranded());

if (closed()) return;
if (closed())
return;

batched_.push_back(link);
--batch_backlog_;

Expand All @@ -65,18 +93,19 @@ void chaser_validate::push_batch(const header_link& link, size_t height) NOEXCEP
// Process both tables when one hits target, allowing batched_ clearance
// and therefore forward confirmation progress. Drain batch if no backlogs
// and maximum hash been posted.
process_batch(is_maximum());
const auto residual = is_residual();
process_batch(residual);

if (residual)
batched_.shrink_to_fit();
}

void chaser_validate::process_batch(bool residual) NOEXCEPT
{
BC_ASSERT(stranded());

// Test outside of lock to prevent reader contention for nearly all calls.
auto& query = archive();
if (closed() || (!residual &&
(query.ecdsa_records() < batch_target_) &&
(query.schnorr_records() < batch_target_)))
if (closed() || !is_mature(residual))
return;

// Unique lock prevents batch table updates during evaluation, allowing the
Expand All @@ -86,84 +115,72 @@ void chaser_validate::process_batch(bool residual) NOEXCEPT
const std::unique_lock lock{ mutex_ };

// Must retest inside the lock as table updates are running concurrently.
if (closed()) return;
const auto ecdsa = query.ecdsa_records();
const auto schnorr = query.schnorr_records();
if (!residual && (ecdsa < batch_target_) && (schnorr < batch_target_))
if (closed() || !is_mature(residual))
return;

log_captures();
if (const auto ec = do_process_batch(false))
fault(ec);
// ========================================================================
}

// set_block_unconfirmable(ecdsa)
// ------------------------------------------------------------------------
code chaser_validate::do_process_batch(bool startup) NOEXCEPT
{
if (!startup)
log_captures();

auto& query = archive();

const auto ecdsa = query.ecdsa_records();
if (is_nonzero(ecdsa))
{
header_links invalids{};
const auto start = network::logger::now();
if (!query.verify_ecdsa_signatures(stopping_, invalids))
{
LOGN("Batch verify ecdsa canceled (" << ecdsa << ").");
return;
return network::error::operation_canceled;
}

const auto end = network::logger::now();
const auto elapsed = duration_cast<seconds>(end - start).count();
fire(events::ecdsa_secs, elapsed);
LOGN(log_rate("Batch verify rate ecdsa.... ", ecdsa, elapsed));

if (!process_invalids(invalids) || !query.purge_ecdsa_signatures())
if (!startup)
{
fault(error::batch2);
return;
LOGN(log_rate("Batch verify rate ecdsa.... ", ecdsa, elapsed));
}
}

// set_block_unconfirmable(schnorr)
// ------------------------------------------------------------------------
if (!mark_invalids(invalids, startup) ||
!query.purge_ecdsa_signatures())
return error::batch2;
}

const auto schnorr = query.schnorr_records();
if (is_nonzero(schnorr))
{
header_links invalids{};
const auto start = network::logger::now();
if (!query.verify_schnorr_signatures(stopping_, invalids))
{
LOGN("Batch verify schnorr canceled (" << schnorr << ").");
return;
return network::error::operation_canceled;
}

const auto end = network::logger::now();
const auto elapsed = duration_cast<seconds>(end - start).count();
fire(events::schnorr_secs, elapsed);
LOGN(log_rate("Batch verify rate schnorr.. ", schnorr, elapsed));

if (!process_invalids(invalids) || !query.purge_schnorr_signatures())
if (!startup)
{
fault(error::batch3);
return;
LOGN(log_rate("Batch verify rate schnorr.. ", schnorr, elapsed));
}
}

// set_block_valid(batched_ excluding ecdsa/schnorr failures)
// ------------------------------------------------------------------------

if (!process_valids(residual))
{
fault(error::batch4);
return;
if (!mark_invalids(invalids, startup) ||
!query.purge_schnorr_signatures())
return error::batch3;
}
// ========================================================================
}

// Batching helpers.
// ----------------------------------------------------------------------------
// private

bool chaser_validate::is_maximum() NOEXCEPT
{
return maximum_posted_.load() &&
is_zero(batch_backlog_.load()) &&
is_zero(validate_backlog_.load());
return mark_valids(startup) ? error::success : error::batch4;
}

// Invalids might not be included in batched, as link push is a race.
Expand All @@ -174,7 +191,8 @@ bool chaser_validate::is_maximum() NOEXCEPT
// to match here, this block id will subsequently land in batched_ and would
// then be reported as valid, overriding the unconfirmable block state set
// below, so invalids_ are cached for process lifetime.
bool chaser_validate::process_invalids(const header_links& invalids) NOEXCEPT
bool chaser_validate::mark_invalids(const header_links& invalids,
bool startup) NOEXCEPT
{
BC_ASSERT(stranded());

Expand All @@ -187,7 +205,8 @@ bool chaser_validate::process_invalids(const header_links& invalids) NOEXCEPT
return false;

invalids_.push_back(link);
notify_block(system::error::invalid_signature, height, link, false);
const auto ec = system::error::invalid_signature;
notify_block(ec, height, link, false, startup);
}

// Set all invalid links in batched_ to terminal (to be skipped).
Expand All @@ -204,7 +223,7 @@ bool chaser_validate::process_invalids(const header_links& invalids) NOEXCEPT

// Set all batched blocks that aren't invalid to valid.
// May be ancestors of invalid, in which case they are also unconfirmable.
bool chaser_validate::process_valids(bool residual) NOEXCEPT
bool chaser_validate::mark_valids(bool startup) NOEXCEPT
{
BC_ASSERT(stranded());

Expand All @@ -226,13 +245,10 @@ bool chaser_validate::process_valids(bool residual) NOEXCEPT
return;
}

notify_block(system::error::success, height, link, false);
notify_block(system::error::success, height, link, false, startup);
});

batched_.clear();
if (residual)
batched_.shrink_to_fit();

return !fault.load();
}

Expand Down
5 changes: 5 additions & 0 deletions src/chasers/chaser_validate_parallel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,11 @@ void chaser_validate::validate_block(const header_link& link,
if (!query.set_block_unconfirmable(link))
ec = error::validate5;
}
else if (batched && !faulted)
{
if (!query.set_block_prevalid(link))
ec = error::batch1;
}

--validate_backlog_;
complete_block(ec, link, ctx.height, bypass, batched, faulted, capturing);
Expand Down
Loading