From 60f1513759526bdcad75793dd6c32c8c6c8f21d0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 19 May 2026 19:09:35 +0000 Subject: [PATCH 1/8] Add coverage tests for serialization and tree semantics Agent-Logs-Url: https://github.com/microsoft/merklecpp/sessions/cd6a239e-ca77-480e-b515-6c171a869977 Co-authored-by: achamayou <4016369+achamayou@users.noreply.github.com> --- merklecpp.h | 14 ++- test/CMakeLists.txt | 1 + test/coverage.cpp | 289 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 302 insertions(+), 2 deletions(-) create mode 100644 test/coverage.cpp diff --git a/merklecpp.h b/merklecpp.h index 7bb1de1..f614f89 100644 --- a/merklecpp.h +++ b/merklecpp.h @@ -497,7 +497,10 @@ namespace merkle /// @brief Equality operator for paths bool operator==(const PathT& other) const { - if (_leaf != other._leaf || elements.size() != other.elements.size()) + if ( + _leaf != other._leaf || _leaf_index != other._leaf_index || + _max_index != other._max_index || + elements.size() != other.elements.size()) { return false; } @@ -1338,7 +1341,7 @@ namespace merkle << to << std::endl;); if ( - (from < min_index() || max_index() < from) || + empty() || (from < min_index() || max_index() < from) || (to < min_index() || max_index() < to) || from > to) { throw std::runtime_error("invalid leaf indices"); @@ -1577,6 +1580,13 @@ namespace merkle /// @return The number of bytes required to serialise the tree segment size_t serialised_size(size_t from, size_t to) { + if ( + empty() || (from < min_index() || max_index() < from) || + (to < min_index() || max_index() < to) || from > to) + { + throw std::runtime_error("invalid leaf indices"); + } + size_t num_extras = 0; walk_to(from, false, [&num_extras](Node*&, bool go_right) { if (go_right) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 230e095..e90a87c 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -28,6 +28,7 @@ add_merklecpp_test(past_paths past_paths.cpp) add_merklecpp_test(serialisation serialisation.cpp) add_merklecpp_test(partial_serialisation partial_serialisation.cpp) add_merklecpp_test(serialise_to_file serialise_to_file.cpp) +add_merklecpp_test(coverage coverage.cpp) if(TARGET evercrypt.host) add_merklecpp_test(compare_evercrypt compare_evercrypt.cpp) diff --git a/test/coverage.cpp b/test/coverage.cpp new file mode 100644 index 0000000..67cca51 --- /dev/null +++ b/test/coverage.cpp @@ -0,0 +1,289 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include "util.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace +{ + void require(bool condition, const std::string& message) + { + if (!condition) + { + throw std::runtime_error(message); + } + } + + template + void require_throws(F&& f, const std::string& message) + { + try + { + f(); + } + catch (const std::exception&) + { + return; + } + throw std::runtime_error(message); + } + + merkle::Hash hash_with_byte(uint8_t byte) + { + merkle::Hash h; + h.bytes[0] = byte; + h.bytes[31] = byte; + return h; + } + + merkle::Path single_element_path( + const merkle::Hash& leaf, + size_t leaf_index, + size_t max_index, + const merkle::Hash& sibling, + merkle::Path::Direction direction) + { + merkle::Path::Element e; + e.hash = sibling; + e.direction = direction; + + std::list elements; + elements.push_back(e); + return merkle::Path(leaf, leaf_index, std::move(elements), max_index); + } + + merkle::Tree make_tree(size_t num_leaves) + { + merkle::Tree tree; + auto hashes = make_hashes(num_leaves); + for (auto& hash : hashes) + { + tree.insert(hash); + } + return tree; + } + + void test_serialisation_helpers() + { + const uint32_t n = 0x01020304U; + const uint32_t converted = merkle::convert_endianness(n); + uint8_t bytes[sizeof(converted)] = {}; + std::memcpy(bytes, &converted, sizeof(converted)); + require(bytes[0] == 0x01, "convert_endianness byte 0 mismatch"); + require(bytes[1] == 0x02, "convert_endianness byte 1 mismatch"); + require(bytes[2] == 0x03, "convert_endianness byte 2 mismatch"); + require(bytes[3] == 0x04, "convert_endianness byte 3 mismatch"); + require( + merkle::convert_endianness(converted) == n, + "convert_endianness should round-trip"); + + std::vector buffer = {0xAA}; + merkle::serialise_uint64_t(0x0102030405060708ULL, buffer); + require(buffer.size() == 9, "serialise_uint64_t should append 8 bytes"); + for (size_t i = 1; i < buffer.size(); i++) + { + require( + buffer[i] == static_cast(i), + "serialise_uint64_t should write big-endian bytes"); + } + + size_t position = 1; + const auto value = merkle::deserialise_uint64_t(buffer, position); + require(value == 0x0102030405060708ULL, "deserialise_uint64_t mismatch"); + require(position == buffer.size(), "deserialise_uint64_t position mismatch"); + + std::vector short_buffer(7, 0); + size_t short_position = 0; + require_throws( + [&] { merkle::deserialise_uint64_t(short_buffer, short_position); }, + "deserialise_uint64_t should reject short buffers"); + } + + void test_path_metadata_and_equality() + { + merkle::Tree tree; + const auto hashes = make_hashes(4); + for (auto& hash : hashes) + { + tree.insert(hash); + } + + const auto root = tree.root(); + const auto path = tree.path(2); + require(path->leaf() == hashes[2], "path leaf mismatch"); + require(path->leaf_index() == 2, "path leaf_index mismatch"); + require(path->max_index() == 3, "path max_index mismatch"); + require(path->verify(root), "path should verify root"); + + const std::string path_string = path->to_string(1); + require(path_string.find("(L)") != std::string::npos, "path missing left"); + require(path_string.find("(R)") != std::string::npos, "path missing right"); + + std::vector serialised_path = {0xFF}; + path->serialise(serialised_path); + + size_t position = 1; + const merkle::Path from_position(serialised_path, position); + require(from_position == *path, "position constructor should round-trip"); + require( + position == serialised_path.size(), + "position constructor should advance position"); + + std::vector exact_path( + serialised_path.begin() + 1, serialised_path.end()); + const merkle::Path from_exact_buffer(exact_path); + require(from_exact_buffer == *path, "buffer constructor should round-trip"); + + const auto leaf = hash_with_byte(0x10); + const auto sibling = hash_with_byte(0x20); + const auto same = single_element_path( + leaf, 4, 9, sibling, merkle::Path::Direction::PATH_LEFT); + const auto different_leaf_index = single_element_path( + leaf, 5, 9, sibling, merkle::Path::Direction::PATH_LEFT); + const auto different_max_index = single_element_path( + leaf, 4, 10, sibling, merkle::Path::Direction::PATH_LEFT); + const auto different_direction = single_element_path( + leaf, 4, 9, sibling, merkle::Path::Direction::PATH_RIGHT); + + require(same == same, "path should equal itself"); + require( + !(same == different_leaf_index), + "path equality should include leaf index"); + require( + !(same == different_max_index), "path equality should include max index"); + require( + same != different_direction, + "path inequality should include element direction"); + } + + void test_tree_partial_serialisation_bounds() + { + merkle::Tree empty_tree; + std::vector buffer = {0xAA}; + require_throws( + [&] { empty_tree.serialise(0, 0, buffer); }, + "empty tree partial serialise should throw"); + require(buffer.size() == 1, "failed partial serialise should not append"); + require_throws( + [&] { empty_tree.serialised_size(0, 0); }, + "empty tree partial serialised_size should throw"); + + auto tree = make_tree(6); + tree.root(); + tree.flush_to(2); + require(tree.min_index() == 2, "flush_to should update min_index"); + require(tree.max_index() == 5, "unexpected max_index after flush"); + + buffer.clear(); + tree.serialise(2, 5, buffer); + require( + tree.serialised_size(2, 5) == buffer.size(), + "partial serialised_size should match serialise size"); + + require_throws( + [&] { tree.serialise(1, 3, buffer); }, + "partial serialise should reject flushed-from index"); + require_throws( + [&] { tree.serialised_size(1, 3); }, + "partial serialised_size should reject flushed-from index"); + require_throws( + [&] { tree.serialise(2, 6, buffer); }, + "partial serialise should reject too-large to index"); + require_throws( + [&] { tree.serialised_size(2, 6); }, + "partial serialised_size should reject too-large to index"); + require_throws( + [&] { tree.serialise(4, 3, buffer); }, + "partial serialise should reject reversed range"); + require_throws( + [&] { tree.serialised_size(4, 3); }, + "partial serialised_size should reject reversed range"); + + tree.retract_to(4); + require(tree.min_index() == 2, "retract_to should preserve min_index"); + require(tree.max_index() == 4, "retract_to should update max_index"); + + buffer.clear(); + tree.serialise(2, 4, buffer); + require( + tree.serialised_size(2, 4) == buffer.size(), + "partial serialised_size should match after retract"); + require_throws( + [&] { tree.serialise(2, 5, buffer); }, + "partial serialise should reject retracted to index"); + require_throws( + [&] { tree.serialised_size(2, 5); }, + "partial serialised_size should reject retracted to index"); + } + + void test_tree_assignment_and_moves() + { + auto source = make_tree(5); + source.root(); + source.flush_to(1); + const merkle::Hash expected_root = source.root(); + const auto expected_min = source.min_index(); + const auto expected_max = source.max_index(); + + merkle::Tree moved(std::move(source)); + require(moved.root() == expected_root, "move constructor root mismatch"); + require(moved.min_index() == expected_min, "move constructor min mismatch"); + require(moved.max_index() == expected_max, "move constructor max mismatch"); + require(moved.leaf(expected_min) == hash_with_byte(1), "moved leaf mismatch"); + + auto assign_source = make_tree(4); + const merkle::Hash assign_root = assign_source.root(); + merkle::Tree move_assigned(hash_with_byte(0xAA)); + move_assigned = std::move(assign_source); + require(move_assigned.root() == assign_root, "move assignment root mismatch"); + require(move_assigned.min_index() == 0, "move assignment min mismatch"); + require(move_assigned.max_index() == 3, "move assignment max mismatch"); + + auto self_assigned = make_tree(3); + self_assigned.root(); + self_assigned.flush_to(1); + const merkle::Hash self_root = self_assigned.root(); + self_assigned = self_assigned; + require(self_assigned.root() == self_root, "self assignment root mismatch"); + require(self_assigned.min_index() == 1, "self assignment min mismatch"); + require(self_assigned.max_index() == 2, "self assignment max mismatch"); + + self_assigned = std::move(self_assigned); + require( + self_assigned.root() == self_root, + "self move assignment root mismatch"); + require(self_assigned.min_index() == 1, "self move assignment min mismatch"); + require(self_assigned.max_index() == 2, "self move assignment max mismatch"); + } +} + +int main() +{ + try + { + test_serialisation_helpers(); + test_path_metadata_and_equality(); + test_tree_partial_serialisation_bounds(); + test_tree_assignment_and_moves(); + } + catch (const std::exception& ex) + { + std::cout << "Error: " << ex.what() << '\n'; + return 1; + } + catch (...) + { + std::cout << "Error" << '\n'; + return 1; + } + + return 0; +} From e36939bc83cba70cbffc82fa9f2226c5ce5e6c6c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 19 May 2026 19:10:50 +0000 Subject: [PATCH 2/8] Fix coverage test build issues Agent-Logs-Url: https://github.com/microsoft/merklecpp/sessions/cd6a239e-ca77-480e-b515-6c171a869977 Co-authored-by: achamayou <4016369+achamayou@users.noreply.github.com> --- merklecpp.h | 2 +- test/coverage.cpp | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/merklecpp.h b/merklecpp.h index f614f89..94c61b8 100644 --- a/merklecpp.h +++ b/merklecpp.h @@ -519,7 +519,7 @@ namespace merkle } /// @brief Inequality operator for paths - bool operator!=(const PathT& other) + bool operator!=(const PathT& other) const { return !this->operator==(other); } diff --git a/test/coverage.cpp b/test/coverage.cpp index 67cca51..1bee839 100644 --- a/test/coverage.cpp +++ b/test/coverage.cpp @@ -226,6 +226,7 @@ namespace void test_tree_assignment_and_moves() { + const auto source_hashes = make_hashes(5); auto source = make_tree(5); source.root(); source.flush_to(1); @@ -237,7 +238,9 @@ namespace require(moved.root() == expected_root, "move constructor root mismatch"); require(moved.min_index() == expected_min, "move constructor min mismatch"); require(moved.max_index() == expected_max, "move constructor max mismatch"); - require(moved.leaf(expected_min) == hash_with_byte(1), "moved leaf mismatch"); + require( + moved.leaf(expected_min) == source_hashes[expected_min], + "moved leaf mismatch"); auto assign_source = make_tree(4); const merkle::Hash assign_root = assign_source.root(); From 97c78f0801222b719549869b264890aa48e620e1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 19 May 2026 19:12:02 +0000 Subject: [PATCH 3/8] Format coverage test Agent-Logs-Url: https://github.com/microsoft/merklecpp/sessions/cd6a239e-ca77-480e-b515-6c171a869977 Co-authored-by: achamayou <4016369+achamayou@users.noreply.github.com> --- test/coverage.cpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/test/coverage.cpp b/test/coverage.cpp index 1bee839..b08c306 100644 --- a/test/coverage.cpp +++ b/test/coverage.cpp @@ -98,7 +98,8 @@ namespace size_t position = 1; const auto value = merkle::deserialise_uint64_t(buffer, position); require(value == 0x0102030405060708ULL, "deserialise_uint64_t mismatch"); - require(position == buffer.size(), "deserialise_uint64_t position mismatch"); + require( + position == buffer.size(), "deserialise_uint64_t position mismatch"); std::vector short_buffer(7, 0); size_t short_position = 0; @@ -246,7 +247,8 @@ namespace const merkle::Hash assign_root = assign_source.root(); merkle::Tree move_assigned(hash_with_byte(0xAA)); move_assigned = std::move(assign_source); - require(move_assigned.root() == assign_root, "move assignment root mismatch"); + require( + move_assigned.root() == assign_root, "move assignment root mismatch"); require(move_assigned.min_index() == 0, "move assignment min mismatch"); require(move_assigned.max_index() == 3, "move assignment max mismatch"); @@ -261,10 +263,11 @@ namespace self_assigned = std::move(self_assigned); require( - self_assigned.root() == self_root, - "self move assignment root mismatch"); - require(self_assigned.min_index() == 1, "self move assignment min mismatch"); - require(self_assigned.max_index() == 2, "self move assignment max mismatch"); + self_assigned.root() == self_root, "self move assignment root mismatch"); + require( + self_assigned.min_index() == 1, "self move assignment min mismatch"); + require( + self_assigned.max_index() == 2, "self move assignment max mismatch"); } } From 842776c6003b6f48de12c0f6435798e4f66f5a54 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 19 May 2026 19:14:42 +0000 Subject: [PATCH 4/8] Deduplicate partial range validation Agent-Logs-Url: https://github.com/microsoft/merklecpp/sessions/cd6a239e-ca77-480e-b515-6c171a869977 Co-authored-by: achamayou <4016369+achamayou@users.noreply.github.com> --- merklecpp.h | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/merklecpp.h b/merklecpp.h index 94c61b8..6f76f40 100644 --- a/merklecpp.h +++ b/merklecpp.h @@ -1340,12 +1340,7 @@ namespace merkle MERKLECPP_TRACE(MERKLECPP_TOUT << "> serialise from " << from << " to " << to << std::endl;); - if ( - empty() || (from < min_index() || max_index() < from) || - (to < min_index() || max_index() < to) || from > to) - { - throw std::runtime_error("invalid leaf indices"); - } + validate_partial_range(from, to); serialise_uint64_t(to - from + 1, bytes); serialise_uint64_t(from, bytes); @@ -1580,12 +1575,7 @@ namespace merkle /// @return The number of bytes required to serialise the tree segment size_t serialised_size(size_t from, size_t to) { - if ( - empty() || (from < min_index() || max_index() < from) || - (to < min_index() || max_index() < to) || from > to) - { - throw std::runtime_error("invalid leaf indices"); - } + validate_partial_range(from, to); size_t num_extras = 0; walk_to(from, false, [&num_extras](Node*&, bool go_right) { @@ -1699,6 +1689,16 @@ namespace merkle } protected: + void validate_partial_range(size_t from, size_t to) const + { + if ( + empty() || (from < min_index() || max_index() < from) || + (to < min_index() || max_index() < to) || from > to) + { + throw std::runtime_error("invalid leaf indices"); + } + } + /// @brief Vector of leaf nodes current in the tree std::vector leaf_nodes; From 7744b93d22394f736d72b813201c35134e419870 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 19 May 2026 19:16:43 +0000 Subject: [PATCH 5/8] Clarify partial range validation Agent-Logs-Url: https://github.com/microsoft/merklecpp/sessions/cd6a239e-ca77-480e-b515-6c171a869977 Co-authored-by: achamayou <4016369+achamayou@users.noreply.github.com> --- merklecpp.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/merklecpp.h b/merklecpp.h index 6f76f40..d4e27c4 100644 --- a/merklecpp.h +++ b/merklecpp.h @@ -1691,9 +1691,10 @@ namespace merkle protected: void validate_partial_range(size_t from, size_t to) const { - if ( - empty() || (from < min_index() || max_index() < from) || - (to < min_index() || max_index() < to) || from > to) + const bool from_out_of_range = from < min_index() || max_index() < from; + const bool to_out_of_range = to < min_index() || max_index() < to; + const bool reversed_range = from > to; + if (empty() || from_out_of_range || to_out_of_range || reversed_range) { throw std::runtime_error("invalid leaf indices"); } From df1e73acb8eb9d16571e1d25455e1476755afc99 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 19 May 2026 19:18:30 +0000 Subject: [PATCH 6/8] Document self-assignment coverage Agent-Logs-Url: https://github.com/microsoft/merklecpp/sessions/cd6a239e-ca77-480e-b515-6c171a869977 Co-authored-by: achamayou <4016369+achamayou@users.noreply.github.com> --- test/coverage.cpp | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/test/coverage.cpp b/test/coverage.cpp index b08c306..69d574d 100644 --- a/test/coverage.cpp +++ b/test/coverage.cpp @@ -256,18 +256,12 @@ namespace self_assigned.root(); self_assigned.flush_to(1); const merkle::Hash self_root = self_assigned.root(); + + // Intentional self-assignment coverage for TreeT::operator=. self_assigned = self_assigned; require(self_assigned.root() == self_root, "self assignment root mismatch"); require(self_assigned.min_index() == 1, "self assignment min mismatch"); require(self_assigned.max_index() == 2, "self assignment max mismatch"); - - self_assigned = std::move(self_assigned); - require( - self_assigned.root() == self_root, "self move assignment root mismatch"); - require( - self_assigned.min_index() == 1, "self move assignment min mismatch"); - require( - self_assigned.max_index() == 2, "self move assignment max mismatch"); } } From 05797c0b11bd4c111c31ada06c4e33bd7cb40c04 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 20 May 2026 07:30:30 +0000 Subject: [PATCH 7/8] Fix CI compiler warnings and TreeT assignment safety Agent-Logs-Url: https://github.com/microsoft/merklecpp/sessions/fe3cc83e-0fd8-4966-880a-f5bbe2208438 Co-authored-by: achamayou <4016369+achamayou@users.noreply.github.com> --- merklecpp.h | 11 ++++++++++- test/coverage.cpp | 16 +++++++++------- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/merklecpp.h b/merklecpp.h index d4e27c4..3198efc 100644 --- a/merklecpp.h +++ b/merklecpp.h @@ -730,7 +730,10 @@ namespace merkle insertion_stack(std::move(other.insertion_stack)), hashing_stack(std::move(other.hashing_stack)), walk_stack(std::move(other.walk_stack)) - {} + { + other._root = nullptr; + other.num_flushed = 0; + } /// @brief Deserialises a tree /// @param bytes Byte buffer containing a serialised tree @@ -944,6 +947,10 @@ namespace merkle /// @return The tree Tree& operator=(const Tree& other) { + if (this == &other) + { + return *this; + } leaf_nodes.clear(); for (auto n : uninserted_leaf_nodes) { @@ -953,6 +960,8 @@ namespace merkle insertion_stack.clear(); hashing_stack.clear(); walk_stack.clear(); + delete (_root); + _root = nullptr; size_t to_skip = (other.num_flushed % 2 == 0) ? 0 : 1; _root = Node::copy_node( diff --git a/test/coverage.cpp b/test/coverage.cpp index 69d574d..ee91dc9 100644 --- a/test/coverage.cpp +++ b/test/coverage.cpp @@ -27,7 +27,7 @@ namespace { try { - f(); + std::forward(f)(); } catch (const std::exception&) { @@ -57,14 +57,14 @@ namespace std::list elements; elements.push_back(e); - return merkle::Path(leaf, leaf_index, std::move(elements), max_index); + return {leaf, leaf_index, std::move(elements), max_index}; } merkle::Tree make_tree(size_t num_leaves) { merkle::Tree tree; auto hashes = make_hashes(num_leaves); - for (auto& hash : hashes) + for (const auto& hash : hashes) { tree.insert(hash); } @@ -112,7 +112,7 @@ namespace { merkle::Tree tree; const auto hashes = make_hashes(4); - for (auto& hash : hashes) + for (const auto& hash : hashes) { tree.insert(hash); } @@ -138,7 +138,7 @@ namespace position == serialised_path.size(), "position constructor should advance position"); - std::vector exact_path( + const std::vector exact_path( serialised_path.begin() + 1, serialised_path.end()); const merkle::Path from_exact_buffer(exact_path); require(from_exact_buffer == *path, "buffer constructor should round-trip"); @@ -153,8 +153,10 @@ namespace leaf, 4, 10, sibling, merkle::Path::Direction::PATH_LEFT); const auto different_direction = single_element_path( leaf, 4, 9, sibling, merkle::Path::Direction::PATH_RIGHT); + const auto equivalent = single_element_path( + leaf, 4, 9, sibling, merkle::Path::Direction::PATH_LEFT); - require(same == same, "path should equal itself"); + require(same == equivalent, "path should equal equivalent path"); require( !(same == different_leaf_index), "path equality should include leaf index"); @@ -246,7 +248,7 @@ namespace auto assign_source = make_tree(4); const merkle::Hash assign_root = assign_source.root(); merkle::Tree move_assigned(hash_with_byte(0xAA)); - move_assigned = std::move(assign_source); + move_assigned = assign_source; require( move_assigned.root() == assign_root, "move assignment root mismatch"); require(move_assigned.min_index() == 0, "move assignment min mismatch"); From 3f1edbb12d70a1bd1733f62dfcb9cc497d41fc25 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 20 May 2026 07:33:27 +0000 Subject: [PATCH 8/8] Add safe TreeT move assignment and finalize coverage fixes Agent-Logs-Url: https://github.com/microsoft/merklecpp/sessions/fe3cc83e-0fd8-4966-880a-f5bbe2208438 Co-authored-by: achamayou <4016369+achamayou@users.noreply.github.com> --- merklecpp.h | 45 +++++++++++++++++++++++++++++++++++++++++++++ test/coverage.cpp | 4 +++- 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/merklecpp.h b/merklecpp.h index 3198efc..5cc87ac 100644 --- a/merklecpp.h +++ b/merklecpp.h @@ -731,8 +731,13 @@ namespace merkle hashing_stack(std::move(other.hashing_stack)), walk_stack(std::move(other.walk_stack)) { + other.leaf_nodes.clear(); + other.uninserted_leaf_nodes.clear(); other._root = nullptr; other.num_flushed = 0; + other.insertion_stack.clear(); + other.hashing_stack.clear(); + other.walk_stack.clear(); } /// @brief Deserialises a tree @@ -980,6 +985,46 @@ namespace merkle return *this; } + /// @brief Assigns a tree by move + /// @param other The tree to assign + /// @return The tree + Tree& operator=(Tree&& other) noexcept + { + if (this == &other) + { + return *this; + } + + leaf_nodes.clear(); + for (auto n : uninserted_leaf_nodes) + { + delete (n); + } + uninserted_leaf_nodes.clear(); + insertion_stack.clear(); + hashing_stack.clear(); + walk_stack.clear(); + delete (_root); + _root = nullptr; + + leaf_nodes = std::move(other.leaf_nodes); + uninserted_leaf_nodes = std::move(other.uninserted_leaf_nodes); + _root = other._root; + num_flushed = other.num_flushed; + insertion_stack = std::move(other.insertion_stack); + hashing_stack = std::move(other.hashing_stack); + walk_stack = std::move(other.walk_stack); + + other.leaf_nodes.clear(); + other.uninserted_leaf_nodes.clear(); + other._root = nullptr; + other.num_flushed = 0; + other.insertion_stack.clear(); + other.hashing_stack.clear(); + other.walk_stack.clear(); + return *this; + } + /// @brief Extracts the root hash of the tree /// @return The root hash const Hash& root() diff --git a/test/coverage.cpp b/test/coverage.cpp index ee91dc9..d431451 100644 --- a/test/coverage.cpp +++ b/test/coverage.cpp @@ -155,7 +155,9 @@ namespace leaf, 4, 9, sibling, merkle::Path::Direction::PATH_RIGHT); const auto equivalent = single_element_path( leaf, 4, 9, sibling, merkle::Path::Direction::PATH_LEFT); + const auto& same_ref = same; + require(same == same_ref, "path should equal itself"); require(same == equivalent, "path should equal equivalent path"); require( !(same == different_leaf_index), @@ -248,7 +250,7 @@ namespace auto assign_source = make_tree(4); const merkle::Hash assign_root = assign_source.root(); merkle::Tree move_assigned(hash_with_byte(0xAA)); - move_assigned = assign_source; + move_assigned = std::move(assign_source); require( move_assigned.root() == assign_root, "move assignment root mismatch"); require(move_assigned.min_index() == 0, "move assignment min mismatch");