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
11 changes: 11 additions & 0 deletions include/bitcoin/node/chase.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,10 @@ enum class chase
/// Issued by 'confirm' and handled by 'protocol_header/block_out/estimator'.
block,

/// The store has been pruned (once, following the first current block).
/// Issued by 'snapshot' and handled by 'estimate'.
pruned,

/// The confirmed chain is no longer current.
/// Issued by 'confirm' and handled by 'protocol_transaction_out'.
stale,
Expand Down Expand Up @@ -325,6 +329,12 @@ struct block
header_t link;
};

struct pruned
{
static constexpr chase id{ chase::pruned };
header_t link;
};

struct stale
{
static constexpr chase id{ chase::stale };
Expand Down Expand Up @@ -388,6 +398,7 @@ using event_value = std::variant
chases::confirmable,
chases::unconfirmable,
chases::block,
chases::pruned,
chases::stale,
chases::organized,
chases::reorganized,
Expand Down
4 changes: 3 additions & 1 deletion include/bitcoin/node/chasers/chaser_estimate.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ class BCN_API chaser_estimate
protected:
virtual bool handle_chase(const code& ec, event_value value) NOEXCEPT;

virtual void do_pruned(header_t link) NOEXCEPT;
virtual void do_initialize(header_t link) NOEXCEPT;
virtual void do_organized(header_t link) NOEXCEPT;
virtual void do_reorganized(header_t link) NOEXCEPT;
Expand All @@ -67,8 +68,9 @@ class BCN_API chaser_estimate
std::atomic_bool stopping_{};
std::atomic_bool initialized_{};

// This is protected by strand.
// These are protected by strand.
estimator::ptr estimator_{};
bool pruned_{};
};

} // namespace node
Expand Down
5 changes: 3 additions & 2 deletions include/bitcoin/node/chasers/chaser_validate.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,9 @@ class BCN_API chaser_validate
bool stranded() const NOEXCEPT override;

private:
using atomic_counter = std::atomic<size_t>;
using atomic_height = std::atomic<size_t>;
using atomic_counter = std::atomic<size_t>;
using bank_heights = std::array<atomic_height, two>;
struct counters
{
atomic_counter ecdsa_{};
Expand Down Expand Up @@ -175,7 +176,7 @@ class BCN_API chaser_validate
std::atomic_bool silent_draining_{};
std::atomic_bool silent_bank_{};
std::array<atomic_counter, two> silent_writers_{};
std::array<atomic_height, two> silent_heights_{ max_size_t, max_size_t };
bank_heights silent_heights_{ max_size_t, max_size_t };
counters counters_{};
stopper stopping_{};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ class BCN_API protocol_transaction_in_106
virtual type_id get_data_type() const NOEXCEPT;

/// The hash by which the peer identifies the tx.
virtual system::hash_digest identifier(
virtual system::hash_digest tx_identifier(
const system::chain::transaction& tx) const NOEXCEPT;

/// The tx identified by the hash is archived.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ class BCN_API protocol_transaction_in_70016
/// Transactions are announced, requested and identified by witness hash.
type_id inventory_type() const NOEXCEPT override;
type_id get_data_type() const NOEXCEPT override;
system::hash_digest identifier(
system::hash_digest tx_identifier(
const system::chain::transaction& tx) const NOEXCEPT override;
bool is_archived(const system::hash_digest& hash) const NOEXCEPT override;
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,8 @@ class BCN_API protocol_transaction_out_106
virtual type_id inventory_type() const NOEXCEPT;

/// The hash by which the tx is announced to the peer.
virtual system::hash_digest identifier(transaction_t link) const NOEXCEPT;
virtual system::hash_digest tx_identifier(
transaction_t link) const NOEXCEPT;

/// The tx of a tx inventory item, terminal if not archived.
virtual database::tx_link to_transaction(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ class BCN_API protocol_transaction_out_70016
protected:
/// Transactions are announced, requested and served by witness hash.
type_id inventory_type() const NOEXCEPT override;
system::hash_digest identifier(transaction_t link) const NOEXCEPT override;
system::hash_digest tx_identifier(
transaction_t link) const NOEXCEPT override;
database::tx_link to_transaction(
const inventory_item& item) const NOEXCEPT override;
};
Expand Down
24 changes: 19 additions & 5 deletions src/chasers/chaser_estimate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -119,10 +119,17 @@ bool chaser_estimate::handle_chase(const code&, event_value value) NOEXCEPT

switch (to_chase(value))
{
// chase::block is only sent when current. This is captured as a cheap
// way to test currency for initialization. Once initialized it is not
// used again. chase::organized is used instead, to ensure that there
// are no push() gaps due to falling out of currency.
// Initialization follows the prune (which rewrites the pool) and
// retries on each current block until the chain reaches the horizon.
case chase::pruned:
{
if (!initialized())
{
POST(do_pruned, to_payload<chase::pruned>(value).link);
}

break;
}
case chase::block:
{
if (!initialized())
Expand Down Expand Up @@ -175,11 +182,18 @@ bool chaser_estimate::handle_chase(const code&, event_value value) NOEXCEPT
return true;
}

void chaser_estimate::do_pruned(header_t link) NOEXCEPT
{
BC_ASSERT(stranded());
pruned_ = true;
do_initialize(link);
}

void chaser_estimate::do_initialize(header_t) NOEXCEPT
{
BC_ASSERT(stranded());

if (initialized())
if (!pruned_ || initialized())
return;

// Preempt initialize fault when horizon exceeds chain length.
Expand Down
3 changes: 2 additions & 1 deletion src/chasers/chaser_snapshot.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ bool chaser_snapshot::handle_chase(const code&, event_value value) NOEXCEPT
// events
// ----------------------------------------------------------------------------

void chaser_snapshot::do_prune(header_t) NOEXCEPT
void chaser_snapshot::do_prune(header_t link) NOEXCEPT
{
BC_ASSERT(stranded());
if (pruned_.load(std::memory_order_relaxed) ||
Expand All @@ -163,6 +163,7 @@ void chaser_snapshot::do_prune(header_t) NOEXCEPT
{
// The resumption is observable, so the prune is marked before it.
pruned_.store(true, std::memory_order_relaxed);
notify(error::success, chases::pruned{ link });

// Could become full before prune start (and it could still succeed).
if (running && !archive().is_full())
Expand Down
19 changes: 18 additions & 1 deletion src/chasers/chaser_transaction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -310,15 +310,32 @@ database::tx_link chaser_transaction::to_stored(
code chaser_transaction::fill(database::tx_link& out,
const chain::transaction& tx, const chain::context& ctx) NOEXCEPT
{
auto& query = archive();
out = to_stored(tx);
if (!out.is_terminal())
{
if (tx.is_coinbase() || !query.is_pooling() ||
query.is_pooled(out) || query.is_confirmed_tx(out))
return error::success;

if (const auto ec = validate(tx, ctx))
return ec;

if (!query.set_pooled(out, tx, ctx))
return fault(error::transaction2);

if (ctx.height >= query.silent_start_height() &&
ctx.is_enabled(flags::bip341_rule) && !query.set_silent(out, tx))
return fault(error::transaction6);

notify(error::success, chases::transaction{ out });
return error::success;
}

if (const auto ec = validate(tx, ctx))
return ec;

bool pooled{};
auto& query = archive();

// Disk full may leave txs partly archived, resolves by resubmit.
if (const auto ec = query.set_code(out, pooled, tx))
Expand Down
6 changes: 3 additions & 3 deletions src/protocols/protocol_transaction_in_106.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ type_id protocol_transaction_in_106::get_data_type() const NOEXCEPT
return tx_type_;
}

hash_digest protocol_transaction_in_106::identifier(
hash_digest protocol_transaction_in_106::tx_identifier(
const chain::transaction& tx) const NOEXCEPT
{
return tx.get_hash(false);
Expand Down Expand Up @@ -161,9 +161,9 @@ bool protocol_transaction_in_106::handle_receive_transaction(const code& ec,
return false;

const auto& tx = message->transaction_ptr;
if (!erase_requested(identifier(*tx)))
if (!erase_requested(tx_identifier(*tx)))
{
LOGR("Unrequested tx [" << encode_hash(identifier(*tx))
LOGR("Unrequested tx [" << encode_hash(tx_identifier(*tx))
<< "] from [" << opposite() << "].");
stop(network::error::protocol_violation);
return false;
Expand Down
2 changes: 1 addition & 1 deletion src/protocols/protocol_transaction_in_70016.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ type_id protocol_transaction_in_70016::get_data_type() const NOEXCEPT
return type_id::wtxid;
}

hash_digest protocol_transaction_in_70016::identifier(
hash_digest protocol_transaction_in_70016::tx_identifier(
const chain::transaction& tx) const NOEXCEPT
{
return tx.get_hash(true);
Expand Down
4 changes: 2 additions & 2 deletions src/protocols/protocol_transaction_out_106.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ type_id protocol_transaction_out_106::inventory_type() const NOEXCEPT
return type_id::transaction;
}

hash_digest protocol_transaction_out_106::identifier(
hash_digest protocol_transaction_out_106::tx_identifier(
transaction_t link) const NOEXCEPT
{
return archive().get_tx_key(link);
Expand Down Expand Up @@ -115,7 +115,7 @@ bool protocol_transaction_out_106::do_announce(transaction_t link) NOEXCEPT
if (stopped())
return false;

return announce(identifier(link));
return announce(tx_identifier(link));
}

bool protocol_transaction_out_106::announce(const hash_digest& hash) NOEXCEPT
Expand Down
2 changes: 1 addition & 1 deletion src/protocols/protocol_transaction_out_60002.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ void protocol_transaction_out_60002::send_memory_pool(const code& ec,
if (is_filtered(link))
continue;

if (const auto hash = identifier(link); hash != null_hash)
if (const auto hash = tx_identifier(link); hash != null_hash)
items.emplace_back(inventory_type(), hash);
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/protocols/protocol_transaction_out_70016.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ type_id protocol_transaction_out_70016::inventory_type() const NOEXCEPT
return type_id::wtxid;
}

hash_digest protocol_transaction_out_70016::identifier(
hash_digest protocol_transaction_out_70016::tx_identifier(
transaction_t link) const NOEXCEPT
{
return archive().get_wtxid(link);
Expand Down
18 changes: 9 additions & 9 deletions test/chasers/chaser_estimate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,10 +80,10 @@ struct chaser_estimate_setup_fixture
return value;
}

void notify_block(const hash_digest& hash)
void notify_pruned(const hash_digest& hash)
{
const auto link = query_.to_header(hash);
node_.notify(node::error::success, chases::block{ link.value });
node_.notify(node::error::success, chases::pruned{ link.value });
}

void notify_organized(const hash_digest& hash)
Expand Down Expand Up @@ -184,14 +184,14 @@ BOOST_FIXTURE_TEST_CASE(chaser_estimate__estimate__not_initialized__estimate_pre

BOOST_FIXTURE_TEST_CASE(chaser_estimate__estimate__horizon_exceeds_chain__estimate_premature, chaser_estimate_excess_setup_fixture)
{
notify_block(p2p_compact_setup_fixture::block1().hash());
notify_pruned(p2p_compact_setup_fixture::block1().hash());
const auto result = estimate(1, estimator::mode::basic);
BOOST_REQUIRE_EQUAL(result.first, node::error::estimate_premature);
}

BOOST_FIXTURE_TEST_CASE(chaser_estimate__estimate__initialized_no_fees__estimate_false, chaser_estimate_enabled_setup_fixture)
{
notify_block(p2p_compact_setup_fixture::block1().hash());
notify_pruned(p2p_compact_setup_fixture::block1().hash());
const auto result = initialized(1, estimator::mode::basic);
BOOST_REQUIRE_EQUAL(result.first, node::error::estimate_false);
BOOST_REQUIRE_EQUAL(result.second, estimator::estimate_failed);
Expand All @@ -201,10 +201,10 @@ BOOST_FIXTURE_TEST_CASE(chaser_estimate__estimate__initialized_no_fees__estimate
////{
//// const auto& block1 = p2p_compact_setup_fixture::block1();
//// const auto& block2 = p2p_compact_setup_fixture::block2();
//// notify_block(block1.hash());
//// notify_pruned(block1.hash());
//// BOOST_REQUIRE_EQUAL(initialized(1, estimator::mode::basic).first, node::error::estimate_false);

//// notify_block(block1.hash());
//// notify_pruned(block1.hash());
//// notify_organized(block1.hash());
//// BOOST_REQUIRE(confirm2());
//// notify_organized(block2.hash());
Expand All @@ -222,7 +222,7 @@ BOOST_FIXTURE_TEST_CASE(chaser_estimate__top_height__not_initialized__zero, chas

////BOOST_FIXTURE_TEST_CASE(chaser_estimate__organized__invalid_link__suspended, chaser_estimate_enabled_setup_fixture)
////{
//// notify_block(p2p_compact_setup_fixture::block1().hash());
//// notify_pruned(p2p_compact_setup_fixture::block1().hash());
//// BOOST_REQUIRE_EQUAL(initialized(1, estimator::mode::basic).first, node::error::estimate_false);

//// node_.notify(node::error::success, chases::organized{ node::header_t{ 42 } });
Expand All @@ -231,7 +231,7 @@ BOOST_FIXTURE_TEST_CASE(chaser_estimate__top_height__not_initialized__zero, chas

////BOOST_FIXTURE_TEST_CASE(chaser_estimate__organized__unconfirmed_above_top__suspended, chaser_estimate_enabled_setup_fixture)
////{
//// notify_block(p2p_compact_setup_fixture::block1().hash());
//// notify_pruned(p2p_compact_setup_fixture::block1().hash());
//// BOOST_REQUIRE_EQUAL(initialized(1, estimator::mode::basic).first, node::error::estimate_false);

//// BOOST_REQUIRE(store_header2());
Expand All @@ -241,7 +241,7 @@ BOOST_FIXTURE_TEST_CASE(chaser_estimate__top_height__not_initialized__zero, chas

////BOOST_FIXTURE_TEST_CASE(chaser_estimate__reorganized__invalid_link__suspended, chaser_estimate_enabled_setup_fixture)
////{
//// notify_block(p2p_compact_setup_fixture::block1().hash());
//// notify_pruned(p2p_compact_setup_fixture::block1().hash());
//// BOOST_REQUIRE_EQUAL(initialized(1, estimator::mode::basic).first, node::error::estimate_false);

//// node_.notify(node::error::success, chases::reorganized{ node::header_t{ 42 } });
Expand Down
16 changes: 16 additions & 0 deletions test/chasers/chaser_transaction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -521,6 +521,22 @@ BOOST_FIXTURE_TEST_CASE(chaser_transaction__submit_compact__invalid_tx_link__int
BOOST_REQUIRE(!query_.is_associated(link));
}

BOOST_FIXTURE_TEST_CASE(chaser_transaction__submit_compact__stored_unpooled_tx__pooled, chaser_transaction_header_setup_fixture)
{
const auto link = query_.to_header(p2p_compact_setup_fixture::block1().hash());
const auto tx = spend(parent_value);
BOOST_REQUIRE(query_.set(*tx));

const auto stored = query_.to_tx(tx->hash(false));
BOOST_REQUIRE(!query_.is_pooled(stored));

const auto result = submit_compact(package({ tx }), { database::tx_link{ 0 }, database::tx_link::terminal }, link);
BOOST_REQUIRE_EQUAL(result.first, node::error::success);
BOOST_REQUIRE(query_.is_associated(link));
BOOST_REQUIRE(query_.is_pooled(stored));
BOOST_REQUIRE_EQUAL(query_.to_tx(tx->hash(false)), stored);
}

BOOST_AUTO_TEST_SUITE_END()

BOOST_AUTO_TEST_SUITE(protocol_transaction_tests)
Expand Down
11 changes: 11 additions & 0 deletions test/full_node.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,17 @@ BOOST_AUTO_TEST_CASE(full_node__notify__snap__resumed)
BOOST_REQUIRE_EQUAL(future.get(), error::success);
}

BOOST_AUTO_TEST_CASE(full_node__notify__block__pruned)
{
BOOST_REQUIRE(initialize());
BOOST_REQUIRE(!start());
BOOST_REQUIRE(!run());
auto pruned = subscribe(chase::pruned);
node_.notify(error::success, chases::block{ 0 });
BOOST_REQUIRE(ready(pruned));
BOOST_REQUIRE_EQUAL(pruned.get(), error::success);
}

BOOST_AUTO_TEST_CASE(full_node__notify__block_after_prune__not_suspended)
{
BOOST_REQUIRE(initialize());
Expand Down
3 changes: 2 additions & 1 deletion test/functional/p2p_compact.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,8 @@ BOOST_AUTO_TEST_CASE(functional_p2p_compact__compact_block__replayed_while_organ
BOOST_REQUIRE(!received(get_compact_transactions::command, pong::command));
}

BOOST_AUTO_TEST_CASE(functional_p2p_compact__compact_block__replayed_while_submitting__not_requested)
// Block 2 remains an unassociated candidate, so the chain never coalesces.
BOOST_FIXTURE_TEST_CASE(functional_p2p_compact__compact_block__replayed_while_submitting__not_requested, p2p_compact_candidate_setup_fixture)
{
BOOST_REQUIRE(handshake(full_node));

Expand Down
Loading