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
19 changes: 8 additions & 11 deletions include/bitcoin/database/impl/query/consensus/consensus_states.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ code CLASS::get_block_state(const header_link& link) const NOEXCEPT
TEMPLATE
inline bool CLASS::is_validated(const header_link& link) const NOEXCEPT
{
// Validated and not invalid (checkpoint/milestone shows false).
// Validated and not invalid (bypassed shows false unless indexed).
const auto ec = get_header_state(link);
return ec == error::block_valid || ec == error::block_confirmable;
}
Expand All @@ -135,22 +135,20 @@ bool CLASS::is_block_validated(code& ec, const header_link& link,
{
if (height <= checkpoint || is_milestone(link))
{
if (is_associated(link))
{
ec = error::bypassed;
return true;
}
else
if (!is_associated(link))
{
ec = error::unassociated;
return false;
}

// A bypassed block requiring silent indexing is validated to index it.
ec = error::bypassed;
return !is_silent(link, height) || is_validated(link);
}
else
{
ec = get_header_state(link);
return ec == error::block_valid
|| ec == error::block_confirmable;
return ec == error::block_valid || ec == error::block_confirmable;
}
}

Expand Down Expand Up @@ -185,8 +183,7 @@ code CLASS::get_pooled(pooled_tx& out, const tx_link& link,
{
return pooled_tx::prevout
{
prevout::output_tx_fk(merged),
prevout::coinbase(merged)
prevout::output_tx_fk(merged), prevout::coinbase(merged)
};
});

Expand Down
9 changes: 9 additions & 0 deletions include/bitcoin/database/impl/query/extent.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,15 @@ size_t CLASS::silent_start_height() const NOEXCEPT
return store_.get_envelope().silent_start_height;
}

// A block is indexed from the start height once taproot is active.
TEMPLATE
bool CLASS::is_silent(const header_link& link, size_t height) const NOEXCEPT
{
context ctx{};
return height >= silent_start_height() && get_context(ctx, link) &&
ctx.is_enabled(system::chain::flags::bip341_rule);
}

} // namespace database
} // namespace libbitcoin

Expand Down
51 changes: 49 additions & 2 deletions include/bitcoin/database/impl/store/store_open.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,54 @@ void CLASS::store_envelope(code& ec) NOEXCEPT
ec = error::create_table;
}

// public
TEMPLATE
code CLASS::read_schema(system::config::version& schema) NOEXCEPT
{
if (!transactor_mutex_.try_lock())
return error::transactor_lock;

auto ec = envelope_head_.open();
if (!ec) ec = envelope_head_.load();
if (!ec && !get_schema(schema)) ec = error::verify_table;

// unload and close errors override ec.
if (const auto unloaded = envelope_head_.unload()) ec = unloaded;
if (const auto closed = envelope_head_.close()) ec = closed;

transactor_mutex_.unlock();
return ec;
}

// The schema is deserialized before it is validated, so the record holds it
// when envelope deserialization fails.
TEMPLATE
bool CLASS::get_schema(system::config::version& schema) const NOEXCEPT
{
if (is_zero(envelope.head_size()))
return false;

table::envelope::record record{};
const auto valid = envelope.get(zero, record);
schema = record.envelope.schema;
return valid || (schema != database::envelope::compiled);
}

// A created store has no envelope until it is populated.
TEMPLATE
code CLASS::load_schema() NOEXCEPT
{
if (is_zero(envelope.head_size()))
return error::success;

system::config::version schema{};
if (!get_schema(schema))
return error::verify_table;

return (schema == database::envelope::compiled) ? error::success :
error::schema_version;
}

TEMPLATE
code CLASS::load_envelope() NOEXCEPT
{
Expand All @@ -135,8 +183,7 @@ code CLASS::load_envelope() NOEXCEPT
if (!is_zero(envelope.head_size()))
{
if (!envelope.get(zero, record))
return record.envelope.schema == envelope_.schema ?
error::verify_table : error::schema_version;
return error::verify_table;

envelope_ = record.envelope;
}
Expand Down
26 changes: 15 additions & 11 deletions include/bitcoin/database/impl/store/store_open_load.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,21 @@ code CLASS::open_load(const event_handler& handler) NOEXCEPT
}
};

const auto load = [&handler](code& ec, auto& file, table_t table) NOEXCEPT
{
if (!ec)
{
handler(event_t::load_file, table);
ec = file.load();
}
};

// The schema version governs all other files, so it precedes them.
open(ec, envelope_head_, table_t::envelope_head);
load(ec, envelope_head_, table_t::envelope_head);
if (!ec)
ec = load_schema();

open(ec, header_head_, table_t::header_head);
open(ec, header_body_, table_t::header_body);
open(ec, input_head_, table_t::input_head);
Expand Down Expand Up @@ -68,7 +83,6 @@ code CLASS::open_load(const event_handler& handler) NOEXCEPT
open(ec, schnorr1_body_, table_t::schnorr1_body);
open(ec, silent_head_, table_t::silent_head);
open(ec, silent_body_, table_t::silent_body);
open(ec, envelope_head_, table_t::envelope_head);
open(ec, duplicate_head_, table_t::duplicate_head);
open(ec, duplicate_body_, table_t::duplicate_body);
open(ec, prevalid0_head_, table_t::prevalid0_head);
Expand All @@ -89,15 +103,6 @@ code CLASS::open_load(const event_handler& handler) NOEXCEPT
open(ec, filter_tx_head_, table_t::filter_tx_head);
open(ec, filter_tx_body_, table_t::filter_tx_body);

const auto load = [&handler](code& ec, auto& file, table_t table) NOEXCEPT
{
if (!ec)
{
handler(event_t::load_file, table);
ec = file.load();
}
};

load(ec, header_head_, table_t::header_head);
load(ec, header_body_, table_t::header_body);
load(ec, input_head_, table_t::input_head);
Expand Down Expand Up @@ -128,7 +133,6 @@ code CLASS::open_load(const event_handler& handler) NOEXCEPT
load(ec, schnorr1_body_, table_t::schnorr1_body);
load(ec, silent_head_, table_t::silent_head);
load(ec, silent_body_, table_t::silent_body);
load(ec, envelope_head_, table_t::envelope_head);
load(ec, duplicate_head_, table_t::duplicate_head);
load(ec, duplicate_body_, table_t::duplicate_body);
load(ec, prevalid0_head_, table_t::prevalid0_head);
Expand Down
1 change: 1 addition & 0 deletions include/bitcoin/database/query.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,7 @@ class query
bool filter_enabled() const NOEXCEPT;
bool silent_enabled() const NOEXCEPT;
size_t silent_start_height() const NOEXCEPT;
bool is_silent(const header_link& link, size_t height) const NOEXCEPT;
size_t interval_span() const NOEXCEPT;

/// Initialization (natural-keyed).
Expand Down
5 changes: 5 additions & 0 deletions include/bitcoin/database/store.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,9 @@ class store
/// Settings envelope stored at store creation.
const database::envelope& get_envelope() const NOEXCEPT;

/// Read the stored schema version without opening the store.
code read_schema(system::config::version& schema) NOEXCEPT;

/// Determine if the store is non-empty/initialized.
bool is_dirty() const NOEXCEPT;
void set_dirty() NOEXCEPT;
Expand Down Expand Up @@ -128,6 +131,8 @@ class store
/// Method helpers.
code create_load(const event_handler& handler) NOEXCEPT;
code open_load(const event_handler& handler) NOEXCEPT;
bool get_schema(system::config::version& schema) const NOEXCEPT;
code load_schema() NOEXCEPT;
code load_envelope() NOEXCEPT;
void store_envelope(code& ec) NOEXCEPT;
code unload_close(const event_handler& handler) NOEXCEPT;
Expand Down
5 changes: 5 additions & 0 deletions test/mocks/map_store.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,11 @@ class map_store
//// return silent_body_.compressed.file();
////}

inline const path& envelope_head_file() const NOEXCEPT
{
return envelope_head_.file();
}

inline const path& duplicate_head_file() const NOEXCEPT
{
return duplicate_head_.file();
Expand Down
34 changes: 34 additions & 0 deletions test/query/consensus/consensus_forks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,40 @@ BOOST_AUTO_TEST_CASE(query_consensus__get_validated_fork__filters_disabled__bypa
BOOST_REQUIRE(fork.back().ec == error::bypassed);
}

BOOST_AUTO_TEST_CASE(query_consensus__get_validated_fork__bypassed_silent_pending__stops_at_first_unvalidated)
{
settings settings{};
settings.path = TEST_DIRECTORY;
settings.filter_tx.buckets = 0;
settings.initialize(system::settings{ system::chain::selection::regtest }, false, 2);
test::chunk_store store{ settings };
test::query_accessor query{ store };
BOOST_REQUIRE(!store.create(test::events_handler));
BOOST_REQUIRE(query.initialize(test::genesis));
BOOST_REQUIRE(!query.filter_enabled());
BOOST_REQUIRE_EQUAL(query.silent_start_height(), 2u);
BOOST_REQUIRE(query.set(test::block1, database::context{ system::chain::flags::bip341_rule, 1, 0 }, {}, false, false));
BOOST_REQUIRE(query.set(test::block2, database::context{ system::chain::flags::bip341_rule, 2, 0 }, {}, false, false));

const auto link1 = query.to_header(test::block1_hash);
const auto link2 = query.to_header(test::block2_hash);
BOOST_REQUIRE(query.push_candidate(link1));
BOOST_REQUIRE(query.push_candidate(link2));

size_t fork_point{};
auto fork = query.get_validated_fork(fork_point, 2);
BOOST_REQUIRE_EQUAL(fork_point, 0u);
BOOST_REQUIRE_EQUAL(fork.size(), 1u);
BOOST_REQUIRE(fork.front().link == link1);
BOOST_REQUIRE(fork.front().ec == error::bypassed);

BOOST_REQUIRE(query.set_block_valid(link2));
fork = query.get_validated_fork(fork_point, 2);
BOOST_REQUIRE_EQUAL(fork.size(), 2u);
BOOST_REQUIRE(fork.back().link == link2);
BOOST_REQUIRE(fork.back().ec == error::bypassed);
}

BOOST_AUTO_TEST_CASE(query_consensus__get_validated_fork__milestone_filter_pending__empty)
{
settings settings{};
Expand Down
49 changes: 49 additions & 0 deletions test/store/store_open.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -97,4 +97,53 @@ BOOST_AUTO_TEST_CASE(store__open__created__success)
BOOST_REQUIRE(!instance.close(test::events));
}

BOOST_AUTO_TEST_CASE(store__open__schema_mismatch__schema_version)
{
settings configuration{};
configuration.path = TEST_DIRECTORY;
test::map_store instance{ configuration };
BOOST_REQUIRE(!instance.create(test::events));
BOOST_REQUIRE(!instance.close(test::events));
BOOST_REQUIRE(test::create(instance.envelope_head_file(), std::string(16, '')));
BOOST_REQUIRE_EQUAL(instance.open(test::events), error::schema_version);
}

// read_schema
// ----------------------------------------------------------------------------

BOOST_AUTO_TEST_CASE(store__read_schema__uncreated__failure)
{
settings configuration{};
configuration.path = TEST_DIRECTORY;
system::config::version schema{};
test::map_store instance{ configuration };
BOOST_REQUIRE(instance.read_schema(schema));
}

BOOST_AUTO_TEST_CASE(store__read_schema__created__compiled)
{
settings configuration{};
configuration.path = TEST_DIRECTORY;
system::config::version schema{};
test::map_store instance{ configuration };
BOOST_REQUIRE(!instance.create(test::events));
BOOST_REQUIRE(!instance.close(test::events));
BOOST_REQUIRE(!instance.read_schema(schema));
BOOST_REQUIRE(schema == envelope::compiled);
}

BOOST_AUTO_TEST_CASE(store__read_schema__schema_mismatch__stored)
{
settings configuration{};
configuration.path = TEST_DIRECTORY;
system::config::version schema{};
const system::config::version expected{ 0x01010101, 0x01010101, 0x01010101, 0x01010101 };
test::map_store instance{ configuration };
BOOST_REQUIRE(!instance.create(test::events));
BOOST_REQUIRE(!instance.close(test::events));
BOOST_REQUIRE(test::create(instance.envelope_head_file(), std::string(16, '')));
BOOST_REQUIRE(!instance.read_schema(schema));
BOOST_REQUIRE(schema == expected);
}

BOOST_AUTO_TEST_SUITE_END()
Loading