CurveTreesUnitTest -> CurveTreesGlobalTree class

This commit is contained in:
j-berman 2024-05-27 16:03:58 -07:00
parent 17b1f421c0
commit c7c6c6afff
3 changed files with 65 additions and 71 deletions

View file

@ -34,8 +34,6 @@
#include <vector> #include <vector>
// forward declarations
class CurveTreesUnitTest;
namespace fcmp namespace fcmp
@ -52,7 +50,7 @@ typename C::Point get_new_parent(const C &curve, const typename C::Chunk &new_ch
template<typename C> template<typename C>
struct LayerExtension final struct LayerExtension final
{ {
std::size_t start_idx; std::size_t start_idx{0};
std::vector<typename C::Point> hashes; std::vector<typename C::Point> hashes;
}; };
@ -86,7 +84,6 @@ struct LastChunkData final
template<typename C1, typename C2> template<typename C1, typename C2>
class CurveTrees class CurveTrees
{ {
friend class ::CurveTreesUnitTest;
public: public:
CurveTrees(const C1 &c1, const C2 &c2, const std::size_t c1_width, const std::size_t c2_width): CurveTrees(const C1 &c1, const C2 &c2, const std::size_t c1_width, const std::size_t c2_width):
m_c1{c1}, m_c1{c1},
@ -105,11 +102,11 @@ public:
struct LeafTuple final struct LeafTuple final
{ {
// Output ed25519 point x-coordinate // Output ed25519 point x-coordinate
typename C2::Scalar O_x; const typename C2::Scalar O_x;
// Key image generator x-coordinate // Key image generator x-coordinate
typename C2::Scalar I_x; const typename C2::Scalar I_x;
// Commitment x-coordinate // Commitment x-coordinate
typename C2::Scalar C_x; const typename C2::Scalar C_x;
}; };
static const std::size_t LEAF_TUPLE_SIZE = 3; static const std::size_t LEAF_TUPLE_SIZE = 3;
static_assert(sizeof(LeafTuple) == (sizeof(typename C2::Scalar) * LEAF_TUPLE_SIZE), "unexpected LeafTuple size"); static_assert(sizeof(LeafTuple) == (sizeof(typename C2::Scalar) * LEAF_TUPLE_SIZE), "unexpected LeafTuple size");

View file

@ -35,7 +35,7 @@
//---------------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------------
// CurveTreesUnitTest helpers // CurveTreesGlobalTree helpers
//---------------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------------
template<typename C> template<typename C>
static fcmp::curve_trees::LastChunkData<C> get_last_child_layer_chunk(const bool update_last_parent, static fcmp::curve_trees::LastChunkData<C> get_last_child_layer_chunk(const bool update_last_parent,
@ -61,7 +61,7 @@ static fcmp::curve_trees::LastChunkData<C> get_last_child_layer_chunk(const bool
//---------------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------------
template<typename C> template<typename C>
static bool validate_layer(const C &curve, static bool validate_layer(const C &curve,
const CurveTreesUnitTest::Layer<C> &parents, const CurveTreesGlobalTree::Layer<C> &parents,
const std::vector<typename C::Scalar> &child_scalars, const std::vector<typename C::Scalar> &child_scalars,
const std::size_t max_chunk_size) const std::size_t max_chunk_size)
{ {
@ -98,13 +98,13 @@ static bool validate_layer(const C &curve,
} }
//---------------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------------
// CurveTreesUnitTest implementations // CurveTreesGlobalTree implementations
//---------------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------------
CurveTreesV1::LastChunks CurveTreesUnitTest::get_last_chunks(const CurveTreesUnitTest::Tree &tree) CurveTreesV1::LastChunks CurveTreesGlobalTree::get_last_chunks()
{ {
const auto &leaves = tree.leaves; const auto &leaves = m_tree.leaves;
const auto &c1_layers = tree.c1_layers; const auto &c1_layers = m_tree.c1_layers;
const auto &c2_layers = tree.c2_layers; const auto &c2_layers = m_tree.c2_layers;
// We started with c2 and then alternated, so c2 is the same size or 1 higher than c1 // We started with c2 and then alternated, so c2 is the same size or 1 higher than c1
CHECK_AND_ASSERT_THROW_MES(c2_layers.size() == c1_layers.size() || c2_layers.size() == (c1_layers.size() + 1), CHECK_AND_ASSERT_THROW_MES(c2_layers.size() == c1_layers.size() || c2_layers.size() == (c1_layers.size() + 1),
@ -201,18 +201,17 @@ CurveTreesV1::LastChunks CurveTreesUnitTest::get_last_chunks(const CurveTreesUni
return last_chunks; return last_chunks;
} }
//---------------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------------
void CurveTreesUnitTest::extend_tree(const CurveTreesV1::TreeExtension &tree_extension, void CurveTreesGlobalTree::extend_tree(const CurveTreesV1::TreeExtension &tree_extension)
CurveTreesUnitTest::Tree &tree_inout)
{ {
// Add the leaves // Add the leaves
const std::size_t init_num_leaves = tree_inout.leaves.size() * m_curve_trees.LEAF_TUPLE_SIZE; const std::size_t init_num_leaves = m_tree.leaves.size() * m_curve_trees.LEAF_TUPLE_SIZE;
CHECK_AND_ASSERT_THROW_MES(init_num_leaves == tree_extension.leaves.start_idx, CHECK_AND_ASSERT_THROW_MES(init_num_leaves == tree_extension.leaves.start_idx,
"unexpected leaf start idx"); "unexpected leaf start idx");
tree_inout.leaves.reserve(tree_inout.leaves.size() + tree_extension.leaves.tuples.size()); m_tree.leaves.reserve(m_tree.leaves.size() + tree_extension.leaves.tuples.size());
for (const auto &leaf : tree_extension.leaves.tuples) for (const auto &leaf : tree_extension.leaves.tuples)
{ {
tree_inout.leaves.emplace_back(CurveTreesV1::LeafTuple{ m_tree.leaves.emplace_back(CurveTreesV1::LeafTuple{
.O_x = leaf.O_x, .O_x = leaf.O_x,
.I_x = leaf.I_x, .I_x = leaf.I_x,
.C_x = leaf.C_x .C_x = leaf.C_x
@ -237,11 +236,11 @@ void CurveTreesUnitTest::extend_tree(const CurveTreesV1::TreeExtension &tree_ext
CHECK_AND_ASSERT_THROW_MES(!c2_ext.hashes.empty(), "empty c2 layer extension"); CHECK_AND_ASSERT_THROW_MES(!c2_ext.hashes.empty(), "empty c2 layer extension");
CHECK_AND_ASSERT_THROW_MES(c2_idx <= tree_inout.c2_layers.size(), "missing c2 layer"); CHECK_AND_ASSERT_THROW_MES(c2_idx <= m_tree.c2_layers.size(), "missing c2 layer");
if (tree_inout.c2_layers.size() == c2_idx) if (m_tree.c2_layers.size() == c2_idx)
tree_inout.c2_layers.emplace_back(Layer<Selene>{}); m_tree.c2_layers.emplace_back(Layer<Selene>{});
auto &c2_inout = tree_inout.c2_layers[c2_idx]; auto &c2_inout = m_tree.c2_layers[c2_idx];
const bool started_after_tip = (c2_inout.size() == c2_ext.start_idx); const bool started_after_tip = (c2_inout.size() == c2_ext.start_idx);
const bool started_at_tip = (c2_inout.size() == (c2_ext.start_idx + 1)); const bool started_at_tip = (c2_inout.size() == (c2_ext.start_idx + 1));
@ -263,11 +262,11 @@ void CurveTreesUnitTest::extend_tree(const CurveTreesV1::TreeExtension &tree_ext
CHECK_AND_ASSERT_THROW_MES(!c1_ext.hashes.empty(), "empty c1 layer extension"); CHECK_AND_ASSERT_THROW_MES(!c1_ext.hashes.empty(), "empty c1 layer extension");
CHECK_AND_ASSERT_THROW_MES(c1_idx <= tree_inout.c1_layers.size(), "missing c1 layer"); CHECK_AND_ASSERT_THROW_MES(c1_idx <= m_tree.c1_layers.size(), "missing c1 layer");
if (tree_inout.c1_layers.size() == c1_idx) if (m_tree.c1_layers.size() == c1_idx)
tree_inout.c1_layers.emplace_back(Layer<Helios>{}); m_tree.c1_layers.emplace_back(Layer<Helios>{});
auto &c1_inout = tree_inout.c1_layers[c1_idx]; auto &c1_inout = m_tree.c1_layers[c1_idx];
const bool started_after_tip = (c1_inout.size() == c1_ext.start_idx); const bool started_after_tip = (c1_inout.size() == c1_ext.start_idx);
const bool started_at_tip = (c1_inout.size() == (c1_ext.start_idx + 1)); const bool started_at_tip = (c1_inout.size() == (c1_ext.start_idx + 1));
@ -287,11 +286,11 @@ void CurveTreesUnitTest::extend_tree(const CurveTreesV1::TreeExtension &tree_ext
} }
} }
//---------------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------------
bool CurveTreesUnitTest::validate_tree(const CurveTreesUnitTest::Tree &tree) bool CurveTreesGlobalTree::audit_tree()
{ {
const auto &leaves = tree.leaves; const auto &leaves = m_tree.leaves;
const auto &c1_layers = tree.c1_layers; const auto &c1_layers = m_tree.c1_layers;
const auto &c2_layers = tree.c2_layers; const auto &c2_layers = m_tree.c2_layers;
CHECK_AND_ASSERT_MES(!leaves.empty(), false, "must have at least 1 leaf in tree"); CHECK_AND_ASSERT_MES(!leaves.empty(), false, "must have at least 1 leaf in tree");
CHECK_AND_ASSERT_MES(!c2_layers.empty(), false, "must have at least 1 c2 layer in tree"); CHECK_AND_ASSERT_MES(!c2_layers.empty(), false, "must have at least 1 c2 layer in tree");
@ -380,7 +379,7 @@ bool CurveTreesUnitTest::validate_tree(const CurveTreesUnitTest::Tree &tree)
//---------------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------------
// Logging helpers // Logging helpers
//---------------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------------
void CurveTreesUnitTest::log_last_chunks(const CurveTreesV1::LastChunks &last_chunks) void CurveTreesGlobalTree::log_last_chunks(const CurveTreesV1::LastChunks &last_chunks)
{ {
const auto &c1_last_chunks = last_chunks.c1_last_chunks; const auto &c1_last_chunks = last_chunks.c1_last_chunks;
const auto &c2_last_chunks = last_chunks.c2_last_chunks; const auto &c2_last_chunks = last_chunks.c2_last_chunks;
@ -424,7 +423,7 @@ void CurveTreesUnitTest::log_last_chunks(const CurveTreesV1::LastChunks &last_ch
} }
} }
//---------------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------------
void CurveTreesUnitTest::log_tree_extension(const CurveTreesV1::TreeExtension &tree_extension) void CurveTreesGlobalTree::log_tree_extension(const CurveTreesV1::TreeExtension &tree_extension)
{ {
const auto &c1_extensions = tree_extension.c1_layer_extensions; const auto &c1_extensions = tree_extension.c1_layer_extensions;
const auto &c2_extensions = tree_extension.c2_layer_extensions; const auto &c2_extensions = tree_extension.c2_layer_extensions;
@ -481,14 +480,14 @@ void CurveTreesUnitTest::log_tree_extension(const CurveTreesV1::TreeExtension &t
} }
} }
//---------------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------------
void CurveTreesUnitTest::log_tree(const CurveTreesUnitTest::Tree &tree) void CurveTreesGlobalTree::log_tree()
{ {
LOG_PRINT_L1("Tree has " << tree.leaves.size() << " leaves, " MDEBUG("Tree has " << m_tree.leaves.size() << " leaves, "
<< tree.c1_layers.size() << " helios layers, " << tree.c2_layers.size() << " selene layers"); << m_tree.c1_layers.size() << " helios layers, " << m_tree.c2_layers.size() << " selene layers");
for (std::size_t i = 0; i < tree.leaves.size(); ++i) for (std::size_t i = 0; i < m_tree.leaves.size(); ++i)
{ {
const auto &leaf = tree.leaves[i]; const auto &leaf = m_tree.leaves[i];
const auto O_x = m_curve_trees.m_c2.to_string(leaf.O_x); const auto O_x = m_curve_trees.m_c2.to_string(leaf.O_x);
const auto I_x = m_curve_trees.m_c2.to_string(leaf.I_x); const auto I_x = m_curve_trees.m_c2.to_string(leaf.I_x);
@ -500,13 +499,13 @@ void CurveTreesUnitTest::log_tree(const CurveTreesUnitTest::Tree &tree)
bool use_c2 = true; bool use_c2 = true;
std::size_t c1_idx = 0; std::size_t c1_idx = 0;
std::size_t c2_idx = 0; std::size_t c2_idx = 0;
for (std::size_t i = 0; i < (tree.c1_layers.size() + tree.c2_layers.size()); ++i) for (std::size_t i = 0; i < (m_tree.c1_layers.size() + m_tree.c2_layers.size()); ++i)
{ {
if (use_c2) if (use_c2)
{ {
CHECK_AND_ASSERT_THROW_MES(c2_idx < tree.c2_layers.size(), "unexpected c2 layer"); CHECK_AND_ASSERT_THROW_MES(c2_idx < m_tree.c2_layers.size(), "unexpected c2 layer");
const CurveTreesUnitTest::Layer<Selene> &c2_layer = tree.c2_layers[c2_idx]; const CurveTreesGlobalTree::Layer<Selene> &c2_layer = m_tree.c2_layers[c2_idx];
MDEBUG("Selene layer size: " << c2_layer.size() << " , tree layer: " << i); MDEBUG("Selene layer size: " << c2_layer.size() << " , tree layer: " << i);
for (std::size_t j = 0; j < c2_layer.size(); ++j) for (std::size_t j = 0; j < c2_layer.size(); ++j)
@ -516,9 +515,9 @@ void CurveTreesUnitTest::log_tree(const CurveTreesUnitTest::Tree &tree)
} }
else else
{ {
CHECK_AND_ASSERT_THROW_MES(c1_idx < tree.c1_layers.size(), "unexpected c1 layer"); CHECK_AND_ASSERT_THROW_MES(c1_idx < m_tree.c1_layers.size(), "unexpected c1 layer");
const CurveTreesUnitTest::Layer<Helios> &c1_layer = tree.c1_layers[c1_idx]; const CurveTreesGlobalTree::Layer<Helios> &c1_layer = m_tree.c1_layers[c1_idx];
MDEBUG("Helios layer size: " << c1_layer.size() << " , tree layer: " << i); MDEBUG("Helios layer size: " << c1_layer.size() << " , tree layer: " << i);
for (std::size_t j = 0; j < c1_layer.size(); ++j) for (std::size_t j = 0; j < c1_layer.size(); ++j)
@ -557,47 +556,45 @@ const std::vector<CurveTreesV1::LeafTuple> generate_random_leaves(const CurveTre
} }
//---------------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------------
static bool grow_tree(CurveTreesV1 &curve_trees, static bool grow_tree(CurveTreesV1 &curve_trees,
CurveTreesUnitTest &curve_trees_accessor, CurveTreesGlobalTree &global_tree,
const std::size_t num_leaves, const std::size_t num_leaves)
CurveTreesUnitTest::Tree &tree_inout)
{ {
// Get the last chunk from each layer in the tree; empty if tree is empty // Get the last chunk from each layer in the tree; empty if tree is empty
const auto last_chunks = curve_trees_accessor.get_last_chunks(tree_inout); const auto last_chunks = global_tree.get_last_chunks();
curve_trees_accessor.log_last_chunks(last_chunks); global_tree.log_last_chunks(last_chunks);
// Get a tree extension object to the existing tree using randomly generated leaves // Get a tree extension object to the existing tree using randomly generated leaves
// - The tree extension includes all elements we'll need to add to the existing tree when adding the new leaves // - The tree extension includes all elements we'll need to add to the existing tree when adding the new leaves
const auto tree_extension = curve_trees.get_tree_extension(last_chunks, const auto tree_extension = curve_trees.get_tree_extension(last_chunks,
generate_random_leaves(curve_trees, num_leaves)); generate_random_leaves(curve_trees, num_leaves));
curve_trees_accessor.log_tree_extension(tree_extension); global_tree.log_tree_extension(tree_extension);
// Use the tree extension to extend the existing tree // Use the tree extension to extend the existing tree
curve_trees_accessor.extend_tree(tree_extension, tree_inout); global_tree.extend_tree(tree_extension);
curve_trees_accessor.log_tree(tree_inout); global_tree.log_tree();
// Validate tree structure and all hashes // Validate tree structure and all hashes
return curve_trees_accessor.validate_tree(tree_inout); return global_tree.audit_tree();
} }
//---------------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------------
static bool grow_tree_in_memory(const std::size_t init_leaves, static bool grow_tree_in_memory(const std::size_t init_leaves,
const std::size_t ext_leaves, const std::size_t ext_leaves,
CurveTreesV1 &curve_trees, CurveTreesV1 &curve_trees)
CurveTreesUnitTest &curve_trees_accessor)
{ {
LOG_PRINT_L1("Adding " << init_leaves << " leaves to tree, then extending by " << ext_leaves << " leaves"); LOG_PRINT_L1("Adding " << init_leaves << " leaves to tree in memory, then extending by "
<< ext_leaves << " leaves");
CurveTreesUnitTest::Tree global_tree; CurveTreesGlobalTree global_tree(curve_trees);
// Initialize global tree with `init_leaves` // Initialize global tree with `init_leaves`
MDEBUG("Adding " << init_leaves << " leaves to tree"); MDEBUG("Adding " << init_leaves << " leaves to tree");
bool res = grow_tree(curve_trees, bool res = grow_tree(curve_trees,
curve_trees_accessor, global_tree,
init_leaves, init_leaves);
global_tree);
CHECK_AND_ASSERT_MES(res, false, "failed to add inital leaves to tree in memory"); CHECK_AND_ASSERT_MES(res, false, "failed to add inital leaves to tree in memory");
@ -607,9 +604,8 @@ static bool grow_tree_in_memory(const std::size_t init_leaves,
MDEBUG("Extending tree by " << ext_leaves << " leaves"); MDEBUG("Extending tree by " << ext_leaves << " leaves");
res = grow_tree(curve_trees, res = grow_tree(curve_trees,
curve_trees_accessor, global_tree,
ext_leaves, ext_leaves);
global_tree);
CHECK_AND_ASSERT_MES(res, false, "failed to extend tree in memory"); CHECK_AND_ASSERT_MES(res, false, "failed to extend tree in memory");
@ -661,7 +657,6 @@ TEST(curve_trees, grow_tree)
HELIOS_CHUNK_WIDTH, HELIOS_CHUNK_WIDTH,
SELENE_CHUNK_WIDTH); SELENE_CHUNK_WIDTH);
CurveTreesUnitTest curve_trees_accessor{curve_trees};
unit_test::BlockchainLMDBTest test_db; unit_test::BlockchainLMDBTest test_db;
CHECK_AND_ASSERT_THROW_MES(HELIOS_CHUNK_WIDTH > 1, "helios width must be > 1"); CHECK_AND_ASSERT_THROW_MES(HELIOS_CHUNK_WIDTH > 1, "helios width must be > 1");
@ -699,7 +694,7 @@ TEST(curve_trees, grow_tree)
if (ext_leaves > 1 && init_leaves == NEED_3_LAYERS) if (ext_leaves > 1 && init_leaves == NEED_3_LAYERS)
continue; continue;
ASSERT_TRUE(grow_tree_in_memory(init_leaves, ext_leaves, curve_trees, curve_trees_accessor)); ASSERT_TRUE(grow_tree_in_memory(init_leaves, ext_leaves, curve_trees));
ASSERT_TRUE(grow_tree_db(init_leaves, ext_leaves, curve_trees, test_db)); ASSERT_TRUE(grow_tree_db(init_leaves, ext_leaves, curve_trees, test_db));
} }
} }

View file

@ -43,11 +43,12 @@ const std::vector<CurveTreesV1::LeafTuple> generate_random_leaves(const CurveTre
const std::size_t HELIOS_CHUNK_WIDTH = 38; const std::size_t HELIOS_CHUNK_WIDTH = 38;
const std::size_t SELENE_CHUNK_WIDTH = 18; const std::size_t SELENE_CHUNK_WIDTH = 18;
// Helper class that can access the private members of the CurveTrees class // Helper class to read/write a global tree in memory. It's only used in testing because normally the tree isn't kept
class CurveTreesUnitTest // in memory (it's stored in the db)
class CurveTreesGlobalTree
{ {
public: public:
CurveTreesUnitTest(CurveTreesV1 &curve_trees): m_curve_trees(curve_trees) {}; CurveTreesGlobalTree(CurveTreesV1 &curve_trees): m_curve_trees(curve_trees) {};
//member structs //member structs
public: public:
@ -65,20 +66,21 @@ public:
//public member functions //public member functions
public: public:
// Read the in-memory tree and get data from last chunks from each layer // Read the in-memory tree and get data from last chunks from each layer
CurveTreesV1::LastChunks get_last_chunks(const Tree &tree); CurveTreesV1::LastChunks get_last_chunks();
// Use the tree extension to extend the in-memory tree // Use the tree extension to extend the in-memory tree
void extend_tree(const CurveTreesV1::TreeExtension &tree_extension, Tree &tree_inout); void extend_tree(const CurveTreesV1::TreeExtension &tree_extension);
// Validate the in-memory tree by re-hashing every layer, starting from root and working down to leaf layer // Validate the in-memory tree by re-hashing every layer, starting from root and working down to leaf layer
bool validate_tree(const Tree &tree); bool audit_tree();
// logging helpers // logging helpers
void log_last_chunks(const CurveTreesV1::LastChunks &last_chunks); void log_last_chunks(const CurveTreesV1::LastChunks &last_chunks);
void log_tree_extension(const CurveTreesV1::TreeExtension &tree_extension); void log_tree_extension(const CurveTreesV1::TreeExtension &tree_extension);
void log_tree(const CurveTreesUnitTest::Tree &tree); void log_tree();
private: private:
CurveTreesV1 &m_curve_trees; CurveTreesV1 &m_curve_trees;
Tree m_tree = Tree{};
}; };