Skip to content

Commit 6204d8d

Browse files
committed
Fix merkle branch/proof queries.
1 parent f3e2832 commit 6204d8d

3 files changed

Lines changed: 347 additions & 240 deletions

File tree

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

Lines changed: 50 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -77,10 +77,14 @@ CLASS::hash_option CLASS::get_confirmed_interval(size_t height) const NOEXCEPT
7777
TEMPLATE
7878
void CLASS::merge_merkle(hashes& to, hashes&& from, size_t first) NOEXCEPT
7979
{
80+
// from is either even or has one additional element of reserved space.
81+
if (!is_one(from.size()) && is_odd(from.size()))
82+
from.push_back(from.back());
83+
8084
using namespace system;
8185
for (const auto& row: block::merkle_branch(first, from.size()))
8286
{
83-
BC_ASSERT(row.sibling * add1(row.width) <= from.size());
87+
BC_ASSERT(add1(row.sibling) * row.width <= from.size());
8488
const auto it = std::next(from.begin(), row.sibling * row.width);
8589
const auto mover = std::make_move_iterator(it);
8690
to.push_back(merkle_root({ mover, std::next(mover, row.width) }));
@@ -108,15 +112,52 @@ code CLASS::get_merkle_proof(hashes& proof, hashes roots, size_t target,
108112
return error::success;
109113
}
110114

115+
// static/private
116+
TEMPLATE
117+
hash_digest CLASS::merkle_subroot(hashes&& tree, size_t span) NOEXCEPT
118+
{
119+
using namespace system;
120+
121+
// Tree cannot be empty or exceed span (span is power of two).
122+
if (tree.empty() || tree.size() > span)
123+
return {};
124+
125+
// zero depth implies single tree element, which is the root.
126+
const auto depth = ceilinged_log2(span);
127+
if (is_zero(depth))
128+
return tree.front();
129+
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.
132+
if (is_odd(tree.size()))
133+
tree.push_back(tree.back());
134+
135+
// Log2 of the evened breadth gives the elevation by merkle_root.
136+
const auto partial = ceilinged_log2(tree.size());
137+
138+
// Partial cannot exceed depth, since tree.size() <= span (a power of 2).
139+
if (is_subtract_overflow(depth, partial))
140+
return {};
141+
142+
// Elevate hashes to partial level.
143+
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)
147+
hash = sha256::double_hash(hash, hash);
148+
149+
return hash;
150+
}
151+
111152
// protected
112153
TEMPLATE
113-
code CLASS::get_merkle_tree(hashes& tree, size_t waypoint) const NOEXCEPT
154+
code CLASS::get_merkle_subroots(hashes& roots, size_t waypoint) const NOEXCEPT
114155
{
115156
const auto span = interval_span();
116157
BC_ASSERT(!is_zero(span));
117158

118159
const auto range = add1(waypoint);
119-
tree.reserve(system::ceilinged_divide(range, span));
160+
roots.reserve(system::ceilinged_divide(range, span));
120161
for (size_t first{}; first < range; first += span)
121162
{
122163
const auto last = std::min(sub1(first + span), waypoint);
@@ -126,13 +167,13 @@ code CLASS::get_merkle_tree(hashes& tree, size_t waypoint) const NOEXCEPT
126167
{
127168
auto interval = get_confirmed_interval(last);
128169
if (!interval.has_value()) return error::merkle_interval;
129-
tree.push_back(std::move(interval.value()));
170+
roots.push_back(std::move(interval.value()));
130171
}
131172
else
132173
{
133-
auto confirmed = get_confirmed_hashes(first, size);
134-
if (confirmed.empty()) return error::merkle_hashes;
135-
tree.push_back(system::merkle_root(std::move(confirmed)));
174+
auto partial = get_confirmed_hashes(first, size);
175+
if (partial.empty()) return error::merkle_hashes;
176+
roots.push_back(merkle_subroot(std::move(partial), span));
136177
}
137178
}
138179

@@ -150,7 +191,7 @@ code CLASS::get_merkle_root_and_proof(hash_digest& root, hashes& proof,
150191
return error::not_found;
151192

152193
hashes tree{};
153-
if (const auto ec = get_merkle_tree(tree, waypoint))
194+
if (const auto ec = get_merkle_subroots(tree, waypoint))
154195
return ec;
155196

156197
proof.clear();
@@ -165,7 +206,7 @@ TEMPLATE
165206
hash_digest CLASS::get_merkle_root(size_t height) const NOEXCEPT
166207
{
167208
hashes tree{};
168-
if (const auto ec = get_merkle_tree(tree, height))
209+
if (const auto ec = get_merkle_subroots(tree, height))
169210
return {};
170211

171212
return system::merkle_root(std::move(tree));

include/bitcoin/database/query.hpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -767,6 +767,8 @@ class query
767767
/// merkle
768768
/// -----------------------------------------------------------------------
769769

770+
// merkle related utilities
771+
static hash_digest merkle_subroot(hashes&& tree, size_t span) NOEXCEPT;
770772
static void merge_merkle(hashes& branch, hashes&& hashes,
771773
size_t first) NOEXCEPT;
772774

@@ -776,7 +778,7 @@ class query
776778

777779
hash_option get_confirmed_interval(size_t height) const NOEXCEPT;
778780
hash_option create_interval(header_link link, size_t height) const NOEXCEPT;
779-
code get_merkle_tree(hashes& roots, size_t waypoint) const NOEXCEPT;
781+
code get_merkle_subroots(hashes& roots, size_t waypoint) const NOEXCEPT;
780782
code get_merkle_proof(hashes& proof, hashes roots, size_t target,
781783
size_t waypoint) const NOEXCEPT;
782784

0 commit comments

Comments
 (0)