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
4 changes: 2 additions & 2 deletions include/bitcoin/node/chasers/chaser.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,14 +59,14 @@ class BCN_API chaser
template <class Derived, typename Method, typename... Args>
auto bind(Method&& method, Args&&... args) NOEXCEPT
{
return BIND_THIS(method, args);
return BIND_TO(method, args);
}

/// Post a method to channel strand (use POST).
template <class Derived, typename Method, typename... Args>
auto post(Method&& method, Args&&... args) NOEXCEPT
{
return boost::asio::post(strand(), BIND_THIS(method, args));
return boost::asio::post(strand(), BIND_TO(method, args));
}

/// Methods.
Expand Down
27 changes: 25 additions & 2 deletions include/bitcoin/node/chasers/chaser_validate.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ class BCN_API chaser_validate
inline auto parallel(Method&& method, Args&&... args) NOEXCEPT
{
return boost::asio::post(validation_threadpool_.service(),
BIND_THIS(method, args));
BIND_TO(method, args));
}

typedef network::race_unity<const code&, const database::tx_link&> race;
Expand Down Expand Up @@ -78,8 +78,31 @@ class BCN_API chaser_validate
bool stranded() const NOEXCEPT override;

private:
system::chain::signatures get_capture(
using atomic_counter = std::atomic<size_t>;
using atomic_counter_ptr = std::shared_ptr<atomic_counter>;
using signatures = system::chain::signatures;
using threshold_group = signatures::threshold_group;
using missed = signatures::miss;

signatures get_capture(const database::header_link& link) NOEXCEPT;

// Handlers.
void do_log(const system::chain::script& missed) NOEXCEPT;
void do_fire(missed miss, size_t count) NOEXCEPT;
bool do_ecdsa(const system::hash_digest& digest,
const system::ec_compressed& point, const system::ec_signature& sign,
const database::header_link& link) NOEXCEPT;
bool do_schnorr(const system::hash_digest& digest,
const system::ec_xonly& point, const system::ec_signature& sign,
const database::header_link& link) NOEXCEPT;
bool do_multisig(const system::hash_digest& digest,
const system::ec_compresseds& points,
const system::ec_signatures& signs, const database::header_link& link,
const atomic_counter_ptr& id) NOEXCEPT;
bool do_threshold(const threshold_group& group,
const database::header_link& link,
const atomic_counter_ptr& id) NOEXCEPT;

void log_capture(const std::string_view& name,
size_t captured, size_t missed) const NOEXCEPT;
void log_captures() const NOEXCEPT;
Expand Down
4 changes: 4 additions & 0 deletions include/bitcoin/node/impl/chasers/chaser_organize.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,10 @@ bool CLASS::handle_chase(const code&, chase event_, event_value value) NOEXCEPT
case chase::unvalid:
case chase::unconfirmable:
{
// !mark_unconfirmable allows node to stall, preserving log.
if (!node_settings().mark_unconfirmable)
break;

// Roll back the candidate chain to confirmed top (via fork point).
BC_ASSERT(std::holds_alternative<header_t>(value));
POST(do_disorganize, std::get<header_t>(value));
Expand Down
1 change: 1 addition & 0 deletions include/bitcoin/node/settings.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ class BCN_API settings
bool memory_priority;
bool allow_overlapped;
bool batch_signatures;
bool mark_unconfirmable;
bool defer_validation;
bool defer_confirmation;
float allowed_deviation;
Expand Down
5 changes: 4 additions & 1 deletion src/chasers/chaser_confirm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,10 @@ bool chaser_confirm::confirm_block(const header_link& link, size_t height,

if (const auto ec = query.block_confirmable(link))
{
if (!query.set_block_unconfirmable(link))
// !mark_unconfirmable allows node to stall, preserving log.
// Will continue to validate this block and fail to confirm here.
if (node_settings().mark_unconfirmable &&
!query.set_block_unconfirmable(link))
{
fault(error::confirm9);
return false;
Expand Down
159 changes: 104 additions & 55 deletions src/chasers/chaser_validate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,9 @@ void chaser_validate::do_bumped(height_t height) NOEXCEPT
void chaser_validate::post_block(const header_link& link,
bool bypass) NOEXCEPT
{
BC_ASSERT(stranded());
// may be called by do_bumped (stranded) or complete_block (not stranded).
///BC_ASSERT(stranded());

backlog_.fetch_add(one, std::memory_order_relaxed);
PARALLEL(validate_block, link, bypass);
}
Expand Down Expand Up @@ -232,13 +234,17 @@ void chaser_validate::validate_block(const header_link& link,
}
else if ((ec = populate(bypass, *block, ctx)))
{
if (!query.set_block_unconfirmable(link))
// !mark_unconfirmable allows node to stall, preserving log.
if (node_settings().mark_unconfirmable &&
!query.set_block_unconfirmable(link))
ec = error::validate4;
}
else if ((ec = validate(bypass, *block, link, ctx)))
{
if (!query.set_block_unconfirmable(link))
ec = error::validate5;
// !mark_unconfirmable allows node to stall, preserving log.
if (node_settings().mark_unconfirmable &&
!query.set_block_unconfirmable(link))
ec = error::validate5;
}

complete_block(ec, link, ctx.height, bypass);
Expand Down Expand Up @@ -329,6 +335,17 @@ void chaser_validate::complete_block(const code& ec, const header_link& link,
return;
}

if (ec == system::error::block_capture)
{
// At least one unrecoverable (threshold) capture failed during
// script validations, and there was no other failure. This is only
// caused by a store fault - possibly a disk full condition. In the
// case of disk full the node will pause, otherwise it will halt.
// Assume disk full here, requiring a repost for block validation.
post_block(link, bypass);
return;
}

// INVALID BLOCK (not a fault)
notify(ec, chase::unvalid, link);
fire(events::block_unconfirmable, height);
Expand Down Expand Up @@ -387,66 +404,27 @@ chain::signatures chaser_validate::get_capture(
return {};

// Group identifier for block, incremented for each multisig/threshold.
const auto id = to_shared<std::atomic<size_t>>();
const auto id = to_shared<atomic_counter>();

using namespace chain;
return signatures
{
// Default struct is disabled.
.enabled = true,

// Enable for a game of whack-a-mole.
.log = [&](const script& /* LOG_ONLY(missed) */) NOEXCEPT
{
////LOGA("Sigop @ " << ctx.height << " -> "
//// << missed.to_string(chain::flags::all_rules));
},
.log = BIND_THIS(do_log, _1),

// Update counters for missed capture.
.fire = [&](signatures::miss miss, size_t count) NOEXCEPT
{
switch (miss)
{
case signatures::miss::ecdsa:
missed_ecdsa_ += count;
break;
case signatures::miss::multisig:
missed_multisig_ += count;
break;
case signatures::miss::schnorr:
missed_schnorr_ += count;
break;
default:;
}
},
.fire = BIND_THIS(do_fire, _1, _2),

// opcode::checksig/verify
.ecdsa = [&](const hash_digest& digest, const ec_compressed& point,
const ec_signature& sign) NOEXCEPT
{
++ecdsa_;
auto& query = archive();
return query.set_signature(digest, point, sign, link);
},
.ecdsa = BIND_THIS(do_ecdsa, _1, _2, _3, link),

// opcode::checksigadd | opcode::checksig/verify
.schnorr = [&](const hash_digest& digest, const ec_xonly& point,
const ec_signature& sign) NOEXCEPT
{
++schnorr_;
auto& query = archive();
return query.set_signature(digest, point, sign, link);
},
.schnorr = BIND_THIS(do_schnorr, _1, _2, _3, link),

// opcode::checkmultisig/verify
.multisig = [&, id](const hash_digest& digest,
const ec_compresseds& points, const ec_signatures& signs) NOEXCEPT
{
BC_ASSERT(points.size() == signs.size());
auto& query = archive();
multisig_ += points.size();
return query.set_signatures(digest, points, signs, (*id)++, link);
},
.multisig = BIND_THIS(do_multisig, _1, _2, _3, link, id),

// opcode::within
// opcode::numequal/verify
Expand All @@ -456,15 +434,86 @@ chain::signatures chaser_validate::get_capture(
// opcode::lessthanorequal
// opcode::greaterthanorequal
// opcode::checksig (m of m)
.threshold = [&, id](const signatures::threshold_group& group) NOEXCEPT
{
threshold_ += group.entries.size();
auto& query = archive();
return query.set_signatures(group, (*id)++, link);
}
.threshold = BIND_THIS(do_threshold, _1, link, id)
};
}

// Enable for a game of whack-a-mole.
void chaser_validate::do_log(
const chain::script& /* LOG_ONLY(missed) */) NOEXCEPT
{
////LOGA("Sigop @ " << ctx.height << " -> "
//// << missed.to_string(chain::flags::all_rules));
}

void chaser_validate::do_fire(missed miss, size_t count) NOEXCEPT
{
switch (miss)
{
case missed::ecdsa:
missed_ecdsa_ += count;
break;
case missed::multisig:
missed_multisig_ += count;
break;
case missed::schnorr:
missed_schnorr_ += count;
break;
default:;
}
}

bool chaser_validate::do_ecdsa(const hash_digest& digest,
const ec_compressed& point, const ec_signature& sign,
const header_link& link) NOEXCEPT
{
++ecdsa_;
const auto set = archive().set_signature(digest, point, sign, link);
if (!set) fault(system::error::block_capture);
return set;
}

bool chaser_validate::do_schnorr(const hash_digest& digest,
const ec_xonly& point, const ec_signature& sign,
const header_link& link) NOEXCEPT
{
++schnorr_;
const auto set = archive().set_signature(digest, point, sign, link);
if (!set) fault(system::error::block_capture);
return set;
}

BC_PUSH_WARNING(SMART_PTR_NOT_NEEDED)
BC_PUSH_WARNING(NO_VALUE_OR_CONST_REF_SHARED_PTR)

bool chaser_validate::do_multisig(const hash_digest& digest,
const ec_compresseds& points, const ec_signatures& signs,
const header_link& link, const atomic_counter_ptr& id) NOEXCEPT
{
BC_ASSERT(points.size() == signs.size());

multisig_ += points.size();
const auto set = archive().set_signatures(digest, points, signs, (*id)++,
link);
if (!set) fault(system::error::block_capture);
return set;
}

bool chaser_validate::do_threshold(const threshold_group& group,
const header_link& link, const atomic_counter_ptr& id) NOEXCEPT
{
threshold_ += group.entries.size();
const auto set = archive().set_signatures(group, (*id)++, link);
if (!set) fault(system::error::block_capture);

// False here sets signatures.fault, causing block.connect(2) to
// return error::block_capture, causing block validation resubmit.
return set;
}

BC_POP_WARNING()
BC_POP_WARNING()

void chaser_validate::log_capture(const std::string_view& name,
size_t captured, size_t missed) const NOEXCEPT
{
Expand Down
4 changes: 3 additions & 1 deletion src/protocols/protocol_block_in_31800.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,9 @@ bool protocol_block_in_31800::handle_receive_block(const code& ec,
return false;
}

if (!query.set_block_unconfirmable(link))
// !mark_unconfirmable allows node to stall, preserving log.
if (node_settings().mark_unconfirmable &&
!query.set_block_unconfirmable(link))
{
stop(fault(error::protocol1));
return false;
Expand Down
1 change: 1 addition & 0 deletions src/settings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ settings::settings() NOEXCEPT
thread_priority{ true },
allow_overlapped{ true },
batch_signatures{ true },
mark_unconfirmable{ true },
defer_validation{ false },
defer_confirmation{ false },
minimum_fee_rate{ 0.0 },
Expand Down
1 change: 1 addition & 0 deletions test/settings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ BOOST_AUTO_TEST_CASE(settings__node__default_context__expected)
BOOST_REQUIRE_EQUAL(node.thread_priority, true);
BOOST_REQUIRE_EQUAL(node.allow_overlapped, true);
BOOST_REQUIRE_EQUAL(node.batch_signatures, true);
BOOST_REQUIRE_EQUAL(node.mark_unconfirmable, true);
BOOST_REQUIRE_EQUAL(node.defer_validation, false);
BOOST_REQUIRE_EQUAL(node.defer_confirmation, false);
BOOST_REQUIRE_EQUAL(node.minimum_fee_rate, 0.0);
Expand Down
Loading