Skip to content

Commit eaa1092

Browse files
authored
Merge pull request #727 from evoskuil/master
Fix get_merkle_subroots, get_merkle_root, merge_merkle queries.
2 parents 20ee666 + d4078b4 commit eaa1092

4 files changed

Lines changed: 421 additions & 65 deletions

File tree

include/bitcoin/database/impl/query/height.ipp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,7 @@ TEMPLATE
225225
hashes CLASS::get_confirmed_hashes(size_t first, size_t count) const NOEXCEPT
226226
{
227227
using namespace system;
228-
const auto size = is_odd(count) && count > one ? add1(count) : count;
228+
const auto size = count + to_int<size_t>(!is_one(count) && is_odd(count));
229229
if (is_zero(count) ||
230230
is_add_overflow(count, one) ||
231231
is_add_overflow(first, size))

include/bitcoin/database/impl/query/merkle.ipp

Lines changed: 45 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#define LIBBITCOIN_DATABASE_QUERY_MERKLE_IPP
2121

2222
#include <algorithm>
23+
#include <iterator>
2324
#include <ranges>
2425
#include <utility>
2526
#include <bitcoin/database/define.hpp>
@@ -45,15 +46,15 @@ CLASS::hash_option CLASS::create_interval(header_link link,
4546
return {};
4647

4748
// Generate the leaf nodes for the span.
48-
hashes leaves(span);
49-
for (auto& leaf: std::views::reverse(leaves))
49+
hashes leafs(span);
50+
for (auto& leaf: std::views::reverse(leafs))
5051
{
5152
leaf = get_header_key(link);
5253
link = to_parent(link);
5354
}
5455

5556
// Generate the merkle root of the interval ending on link header.
56-
return system::merkle_root(std::move(leaves));
57+
return system::merkle_root(std::move(leafs));
5758
}
5859

5960
// protected
@@ -77,17 +78,18 @@ CLASS::hash_option CLASS::get_confirmed_interval(size_t height) const NOEXCEPT
7778
TEMPLATE
7879
void CLASS::merge_merkle(hashes& to, hashes&& from, size_t first) NOEXCEPT
7980
{
80-
// from is either even or has one additional element of reserved space.
81+
// From is either even or has one additional element of reserved space.
8182
if (!is_one(from.size()) && is_odd(from.size()))
8283
from.push_back(from.back());
8384

84-
using namespace system;
8585
for (const auto& row: block::merkle_branch(first, from.size()))
8686
{
87-
BC_ASSERT(add1(row.sibling) * row.width <= from.size());
88-
const auto it = std::next(from.begin(), row.sibling * row.width);
89-
const auto mover = std::make_move_iterator(it);
90-
to.push_back(merkle_root({ mover, std::next(mover, row.width) }));
87+
if (const auto start = row.sibling * row.width; start < from.size())
88+
{
89+
const auto count = std::min(row.width, from.size() - start);
90+
auto it = std::make_move_iterator(std::next(from.begin(), start));
91+
to.push_back(partial_subroot({ it, std::next(it, count) }, row.width));
92+
}
9193
}
9294
}
9395

@@ -114,36 +116,32 @@ code CLASS::get_merkle_proof(hashes& proof, hashes roots, size_t target,
114116

115117
// static/private
116118
TEMPLATE
117-
hash_digest CLASS::merkle_subroot(hashes&& tree, size_t span) NOEXCEPT
119+
hash_digest CLASS::partial_subroot(hashes&& tree, size_t span) NOEXCEPT
118120
{
119-
using namespace system;
120-
121-
// Tree cannot be empty or exceed span (span is power of two).
121+
// Tree cannot be empty or exceed span (a power of 2).
122122
if (tree.empty() || tree.size() > span)
123123
return {};
124124

125-
// zero depth implies single tree element, which is the root.
125+
// Zero depth implies single tree element, which is the root.
126+
using namespace system;
126127
const auto depth = ceilinged_log2(span);
127128
if (is_zero(depth))
128129
return tree.front();
129130

130-
// Merkle root treats a single hash as top/complete, but for a non-zero
131-
// depth subtree, any odd leaf requires duplication including a single.
131+
// merkle_root() treats a single hash as top/complete, but for a partial
132+
// depth subtree, an odd leaf (including a single) requires duplication.
132133
if (is_odd(tree.size()))
133134
tree.push_back(tree.back());
134135

135136
// Log2 of the evened breadth gives the elevation by merkle_root.
136-
const auto partial = ceilinged_log2(tree.size());
137-
138137
// Partial cannot exceed depth, since tree.size() <= span (a power of 2).
138+
auto partial = ceilinged_log2(tree.size());
139139
if (is_subtract_overflow(depth, partial))
140140
return {};
141141

142-
// Elevate hashes to partial level.
142+
// Elevate hashes to partial level, and then from partial to depth.
143143
auto hash = merkle_root(std::move(tree));
144-
145-
// Elevate hashes from partial to depth level.
146-
for (size_t level{}; level < (depth - partial); ++level)
144+
for (; partial < depth; ++partial)
147145
hash = sha256::double_hash(hash, hash);
148146

149147
return hash;
@@ -156,9 +154,16 @@ code CLASS::get_merkle_subroots(hashes& roots, size_t waypoint) const NOEXCEPT
156154
const auto span = interval_span();
157155
BC_ASSERT(!is_zero(span));
158156

159-
const auto range = add1(waypoint);
160-
roots.reserve(system::ceilinged_divide(range, span));
161-
for (size_t first{}; first < range; first += span)
157+
using namespace system;
158+
const auto leafs = add1(waypoint);
159+
const auto limit = ceilinged_divide(leafs, span);
160+
const auto count = limit + to_int<size_t>(!is_one(limit) && is_odd(limit));
161+
162+
// Roots is even-size-except-one-reserved for merkle root push.
163+
roots.reserve(count);
164+
165+
// Either all subroots elevated to same level, or there is a single root.
166+
for (size_t first{}; first < leafs; first += span)
162167
{
163168
const auto last = std::min(sub1(first + span), waypoint);
164169
const auto size = add1(last - first);
@@ -169,11 +174,18 @@ code CLASS::get_merkle_subroots(hashes& roots, size_t waypoint) const NOEXCEPT
169174
if (!interval.has_value()) return error::merkle_interval;
170175
roots.push_back(std::move(interval.value()));
171176
}
177+
else if (is_zero(first))
178+
{
179+
// Single hash, is the complete merkle root.
180+
auto complete = get_confirmed_hashes(zero, size);
181+
roots.push_back(merkle_root(std::move(complete)));
182+
}
172183
else
173184
{
185+
// Roots is even-size-except-one-reserved for merkle root push.
174186
auto partial = get_confirmed_hashes(first, size);
175187
if (partial.empty()) return error::merkle_hashes;
176-
roots.push_back(merkle_subroot(std::move(partial), span));
188+
roots.push_back(partial_subroot(std::move(partial), span));
177189
}
178190
}
179191

@@ -190,26 +202,26 @@ code CLASS::get_merkle_root_and_proof(hash_digest& root, hashes& proof,
190202
if (waypoint > get_top_confirmed())
191203
return error::not_found;
192204

193-
hashes tree{};
194-
if (const auto ec = get_merkle_subroots(tree, waypoint))
205+
hashes roots{};
206+
if (const auto ec = get_merkle_subroots(roots, waypoint))
195207
return ec;
196208

197209
proof.clear();
198-
if (const auto ec = get_merkle_proof(proof, tree, target, waypoint))
210+
if (const auto ec = get_merkle_proof(proof, roots, target, waypoint))
199211
return ec;
200212

201-
root = system::merkle_root(std::move(tree));
213+
root = system::merkle_root(std::move(roots));
202214
return {};
203215
}
204216

205217
TEMPLATE
206218
hash_digest CLASS::get_merkle_root(size_t height) const NOEXCEPT
207219
{
208-
hashes tree{};
209-
if (const auto ec = get_merkle_subroots(tree, height))
220+
hashes roots{};
221+
if (const auto ec = get_merkle_subroots(roots, height))
210222
return {};
211223

212-
return system::merkle_root(std::move(tree));
224+
return system::merkle_root(std::move(roots));
213225
}
214226

215227
} // namespace database

include/bitcoin/database/query.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -768,8 +768,8 @@ class query
768768
/// -----------------------------------------------------------------------
769769

770770
// merkle related utilities
771-
static hash_digest merkle_subroot(hashes&& tree, size_t span) NOEXCEPT;
772-
static void merge_merkle(hashes& branch, hashes&& hashes,
771+
static hash_digest partial_subroot(hashes&& tree, size_t span) NOEXCEPT;
772+
static void merge_merkle(hashes& to, hashes&& from,
773773
size_t first) NOEXCEPT;
774774

775775
// merkle related configuration

0 commit comments

Comments
 (0)