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/database/impl/query/archive_read.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,17 @@ bool CLASS::get_tx_position(size_t& out, const tx_link& link) const NOEXCEPT
return true;
}

TEMPLATE
tx_link CLASS::get_position_tx(const header_link& link,
size_t position) const NOEXCEPT
{
table::txs::get_at_position txs{ {}, position };
if (!store_.txs.at(to_txs(link), txs))
return {};

return txs.tx_fk;
}

// Sizes.
// ----------------------------------------------------------------------------

Expand Down
33 changes: 32 additions & 1 deletion include/bitcoin/database/impl/query/merkle.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,37 @@ namespace database {
// merkle
// ----------------------------------------------------------------------------

// static/protected
TEMPLATE
CLASS::positions CLASS::merkle_branch(size_t leaf, size_t leaves,
bool compress) NOEXCEPT
{
using namespace system;
BC_ASSERT(leaves <= power2(sub1(bits<size_t>)));
BC_ASSERT(is_even(leaves) || is_one(leaves));

positions branch{};
if (is_zero(leaves) || leaf >= leaves)
return branch;

// Upper bound, actual count may be less given compression.
branch.reserve(ceilinged_log2(leaves));

for (auto width = one, current = leaves; current > one;)
{
const auto sibling = bit_xor(leaf, one);
if (!compress || sibling < current)
branch.emplace_back(sibling, width);

++current;
shift_left_into(width);
shift_right_into(leaf);
shift_right_into(current);
}

return branch;
}

// protected
TEMPLATE
CLASS::hash_option CLASS::create_interval(header_link link,
Expand Down Expand Up @@ -87,7 +118,7 @@ void CLASS::merge_merkle(hashes& path, hashes&& leaves, size_t first,
++size;
}

for (const auto& row: block::merkle_branch(first, size + lift))
for (const auto& row: merkle_branch(first, size + lift))
{
hashes subroot{};
if (const auto leaf = row.sibling * row.width; leaf < size)
Expand Down
8 changes: 7 additions & 1 deletion include/bitcoin/database/query.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -344,8 +344,10 @@ class query
inline hash_digest get_point_hash(const point_link& link) const NOEXCEPT;

/// False position implies not confirmed (or fault).
bool get_tx_position(size_t& out, const tx_link& link) const NOEXCEPT;
bool get_tx_height(size_t& out, const tx_link& link) const NOEXCEPT;
bool get_tx_position(size_t& out, const tx_link& link) const NOEXCEPT;
tx_link get_position_tx(const header_link& link,
size_t position) const NOEXCEPT;

/// Sizes.
bool get_tx_size(size_t& out, const tx_link& link,
Expand Down Expand Up @@ -766,11 +768,15 @@ class query

/// merkle
/// -----------------------------------------------------------------------
struct position { size_t sibling; size_t width; };
using positions = std::vector<position>;

// merkle related utilities
static hash_digest partial_subroot(hashes&& tree, size_t span) NOEXCEPT;
static void merge_merkle(hashes& path, hashes&& leaves, size_t first,
size_t lift) NOEXCEPT;
static positions merkle_branch(size_t leaf, size_t leaves,
bool compress=false) NOEXCEPT;

// merkle related configuration
size_t interval_depth() const NOEXCEPT;
Expand Down
31 changes: 31 additions & 0 deletions include/bitcoin/database/tables/archives/txs.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,37 @@ struct txs
size_t position{};
};

struct get_at_position
: public schema::txs
{
inline link count() const NOEXCEPT
{
BC_ASSERT(false);
return {};
}

inline bool from_data(reader& source) NOEXCEPT
{
// tx sizes
source.skip_bytes(skip_sizes);

// tx fks
const auto number = source.read_little_endian<ct::integer, ct::size>();
if (position < number)
{
source.skip_bytes(position * tx::size);
tx_fk = source.read_little_endian<tx::integer, tx::size>();
return source;
}

source.invalidate();
return source;
}

const size_t position{};
tx::integer tx_fk{};
};

struct get_coinbase
: public schema::txs
{
Expand Down
28 changes: 28 additions & 0 deletions test/query/archive_read.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,12 @@ BOOST_AUTO_TEST_CASE(query_archive_read__get_tx_position__confirmed__expected)
BOOST_REQUIRE(query.get_tx_position(out, 4));
BOOST_REQUIRE_EQUAL(out, 0u);
BOOST_REQUIRE(!query.get_tx_position(out, 5));

BOOST_REQUIRE_EQUAL(query.get_position_tx(0, 0), 0u);
BOOST_REQUIRE_EQUAL(query.get_position_tx(1, 0), 1u);
BOOST_REQUIRE_EQUAL(query.get_position_tx(2, 0), 2u);
BOOST_REQUIRE_EQUAL(query.get_position_tx(2, 1), 3u);
BOOST_REQUIRE_EQUAL(query.get_position_tx(3, 0), 4u);
}

BOOST_AUTO_TEST_CASE(query_archive_read__get_tx_position__always__expected)
Expand Down Expand Up @@ -474,6 +480,28 @@ BOOST_AUTO_TEST_CASE(query_archive_read__get_tx_position__always__expected)
BOOST_REQUIRE(!query.get_tx_position(out, 5));
}


BOOST_AUTO_TEST_CASE(query_archive_read__get_position_tx__always__expected)
{
settings settings{};
settings.path = TEST_DIRECTORY;
test::chunk_store store{ settings };
test::query_accessor query{ store };
BOOST_REQUIRE(!store.create(events_handler));
BOOST_REQUIRE(query.initialize(test::genesis));
BOOST_REQUIRE(query.set(test::block1a, context{ 0, 1, 0 }, false, false));
BOOST_REQUIRE(query.set(test::block2a, context{ 0, 2, 0 }, false, false));
BOOST_REQUIRE(query.set(test::block3a, context{ 0, 3, 0 }, false, false));

BOOST_REQUIRE_EQUAL(query.get_position_tx(0, 0), 0u);
BOOST_REQUIRE_EQUAL(query.get_position_tx(1, 0), 1u);
BOOST_REQUIRE_EQUAL(query.get_position_tx(2, 0), 2u);
BOOST_REQUIRE_EQUAL(query.get_position_tx(2, 1), 3u);
BOOST_REQUIRE_EQUAL(query.get_position_tx(3, 0), 4u);
BOOST_REQUIRE(query.get_position_tx(3, 1).is_terminal());
BOOST_REQUIRE(query.get_position_tx({}, 0).is_terminal());
}

BOOST_AUTO_TEST_CASE(query_archive_read__get_tx_sizes__coinbase__204)
{
settings settings{};
Expand Down
144 changes: 144 additions & 0 deletions test/query/merkle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,9 @@ class merkle_accessor
{
public:
using base = test::query_accessor;
using positions = base::positions;
using base::base;
using base::merkle_branch;
using base::interval_span;
using base::create_interval;
using base::get_confirmed_interval;
Expand All @@ -69,6 +71,148 @@ class merkle_accessor
using base::get_merkle_root_and_proof;
};

// merkle_branch

BOOST_AUTO_TEST_CASE(query_merkle___merkle_branch__leaf_zero__empty)
{
BOOST_REQUIRE(merkle_accessor::merkle_branch(0, 0, true).empty());
BOOST_REQUIRE(merkle_accessor::merkle_branch(0, 0, false).empty());
}

BOOST_AUTO_TEST_CASE(query_merkle___merkle_branch__one__zero)
{
auto branch = merkle_accessor::merkle_branch(1, 2, true);
BOOST_REQUIRE_EQUAL(branch.size(), 1u);
BOOST_REQUIRE_EQUAL(branch[0].sibling, 0u);
BOOST_REQUIRE_EQUAL(branch[0].width, 1u);

branch = merkle_accessor::merkle_branch(1, 2, false);
BOOST_REQUIRE_EQUAL(branch.size(), 1u);
BOOST_REQUIRE_EQUAL(branch[0].sibling, 0u);
BOOST_REQUIRE_EQUAL(branch[0].width, 1u);
}

BOOST_AUTO_TEST_CASE(query_merkle___merkle_branch__three__two_and_zero)
{
auto branch = merkle_accessor::merkle_branch(3, 4, true);
BOOST_REQUIRE_EQUAL(branch.size(), 2u);
BOOST_REQUIRE_EQUAL(branch[0].sibling, 2u);
BOOST_REQUIRE_EQUAL(branch[0].width, 1u);
BOOST_REQUIRE_EQUAL(branch[1].sibling, 0u);
BOOST_REQUIRE_EQUAL(branch[1].width, 2u);

branch = merkle_accessor::merkle_branch(3, 4, false);
BOOST_REQUIRE_EQUAL(branch.size(), 2u);
BOOST_REQUIRE_EQUAL(branch[0].sibling, 2u);
BOOST_REQUIRE_EQUAL(branch[0].width, 1u);
BOOST_REQUIRE_EQUAL(branch[1].sibling, 0u);
BOOST_REQUIRE_EQUAL(branch[1].width, 2u);
}

BOOST_AUTO_TEST_CASE(query_merkle___merkle_branch__seven__six_four_and_zero)
{
auto branch = merkle_accessor::merkle_branch(7, 8, true);
BOOST_REQUIRE_EQUAL(branch.size(), 3u);
BOOST_REQUIRE_EQUAL(branch[0].sibling, 6u);
BOOST_REQUIRE_EQUAL(branch[0].width, 1u);
BOOST_REQUIRE_EQUAL(branch[1].sibling, 2u);
BOOST_REQUIRE_EQUAL(branch[1].width, 2u);
BOOST_REQUIRE_EQUAL(branch[2].sibling, 0u);
BOOST_REQUIRE_EQUAL(branch[2].width, 4u);

branch = merkle_accessor::merkle_branch(7, 8, false);
BOOST_REQUIRE_EQUAL(branch.size(), 3u);
BOOST_REQUIRE_EQUAL(branch[0].sibling, 6u);
BOOST_REQUIRE_EQUAL(branch[0].width, 1u);
BOOST_REQUIRE_EQUAL(branch[1].sibling, 2u);
BOOST_REQUIRE_EQUAL(branch[1].width, 2u);
BOOST_REQUIRE_EQUAL(branch[2].sibling, 0u);
BOOST_REQUIRE_EQUAL(branch[2].width, 4u);
}

BOOST_AUTO_TEST_CASE(block__merkle_branch__medium_power_of_two__expected)
{
const merkle_accessor::positions expected{ { 14, 1 }, { 6, 2 }, { 2, 4 }, { 0, 8 } };

auto branch = merkle_accessor::merkle_branch(15, 16, true);
BOOST_REQUIRE_EQUAL(branch.size(), 4u);
BOOST_REQUIRE_EQUAL(branch[0].sibling, 14u);
BOOST_REQUIRE_EQUAL(branch[0].width, 1u);
BOOST_REQUIRE_EQUAL(branch[1].sibling, 6u);
BOOST_REQUIRE_EQUAL(branch[1].width, 2u);
BOOST_REQUIRE_EQUAL(branch[2].sibling, 2u);
BOOST_REQUIRE_EQUAL(branch[2].width, 4u);
BOOST_REQUIRE_EQUAL(branch[3].sibling, 0u);
BOOST_REQUIRE_EQUAL(branch[3].width, 8u);

branch = merkle_accessor::merkle_branch(15, 16, false);
BOOST_REQUIRE_EQUAL(branch.size(), 4u);
BOOST_REQUIRE_EQUAL(branch[0].sibling, 14u);
BOOST_REQUIRE_EQUAL(branch[0].width, 1u);
BOOST_REQUIRE_EQUAL(branch[1].sibling, 6u);
BOOST_REQUIRE_EQUAL(branch[1].width, 2u);
BOOST_REQUIRE_EQUAL(branch[2].sibling, 2u);
BOOST_REQUIRE_EQUAL(branch[2].width, 4u);
BOOST_REQUIRE_EQUAL(branch[3].sibling, 0u);
BOOST_REQUIRE_EQUAL(branch[3].width, 8u);
}

BOOST_AUTO_TEST_CASE(block__merkle_branch__power_of_two_minus_one__expected)
{
constexpr auto leaf = 1023u;
constexpr auto size = system::ceilinged_log2(add1(leaf));
auto branch = merkle_accessor::merkle_branch(leaf, add1(leaf), true);
BOOST_REQUIRE_EQUAL(branch.size(), size);
BOOST_REQUIRE_EQUAL(branch.front().sibling, 1022u);
BOOST_REQUIRE_EQUAL(branch.front().width, 1u);
BOOST_REQUIRE_EQUAL(branch.back().sibling, 0u);
BOOST_REQUIRE_EQUAL(branch.back().width, system::power2(sub1(size)));

branch = merkle_accessor::merkle_branch(leaf, add1(leaf), false);
BOOST_REQUIRE_EQUAL(branch.size(), size);
BOOST_REQUIRE_EQUAL(branch.front().sibling, 1022u);
BOOST_REQUIRE_EQUAL(branch.front().width, 1u);
BOOST_REQUIRE_EQUAL(branch.back().sibling, 0u);
BOOST_REQUIRE_EQUAL(branch.back().width, system::power2(sub1(size)));
}

BOOST_AUTO_TEST_CASE(block__merkle_branch__odd_large_leaf_with_duplication__expected)
{
constexpr auto leaf = 2047u;
constexpr auto size = system::ceilinged_log2(add1(leaf));
auto branch = merkle_accessor::merkle_branch(leaf, add1(leaf), true);
BOOST_REQUIRE_EQUAL(branch.size(), size);
BOOST_REQUIRE_EQUAL(branch.front().sibling, 2046u);
BOOST_REQUIRE_EQUAL(branch.front().width, 1u);
BOOST_REQUIRE_EQUAL(branch.back().sibling, 0u);
BOOST_REQUIRE_EQUAL(branch.back().width, system::power2(sub1(size)));

branch = merkle_accessor::merkle_branch(leaf, add1(leaf), false);
BOOST_REQUIRE_EQUAL(branch.size(), size);
BOOST_REQUIRE_EQUAL(branch.front().sibling, 2046u);
BOOST_REQUIRE_EQUAL(branch.front().width, 1u);
BOOST_REQUIRE_EQUAL(branch.back().sibling, 0u);
BOOST_REQUIRE_EQUAL(branch.back().width, system::power2(sub1(size)));
}

BOOST_AUTO_TEST_CASE(block__merkle_branch__maximum_non_overflow__expected)
{
constexpr auto maximum = sub1(system::power2(sub1(bits<size_t>)));
auto branch = merkle_accessor::merkle_branch(maximum, add1(maximum), true);
BOOST_REQUIRE_EQUAL(branch.size(), sub1(bits<size_t>));
BOOST_REQUIRE_EQUAL(branch.front().sibling, sub1(maximum));
BOOST_REQUIRE_EQUAL(branch.front().width, 1u);
BOOST_REQUIRE_EQUAL(branch.back().sibling, 0u);
BOOST_REQUIRE_EQUAL(branch.back().width, system::power2(sub1(sub1(bits<size_t>))));

branch = merkle_accessor::merkle_branch(maximum, add1(maximum), false);
BOOST_REQUIRE_EQUAL(branch.size(), sub1(bits<size_t>));
BOOST_REQUIRE_EQUAL(branch.front().sibling, sub1(maximum));
BOOST_REQUIRE_EQUAL(branch.front().width, 1u);
BOOST_REQUIRE_EQUAL(branch.back().sibling, 0u);
BOOST_REQUIRE_EQUAL(branch.back().width, system::power2(sub1(sub1(bits<size_t>))));
}

// interval_span

BOOST_AUTO_TEST_CASE(query_merkle__interval_span__uninitialized__max_size_t)
Expand Down
Loading