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>
// forward declarations
class CurveTreesUnitTest;
namespace fcmp
@ -52,7 +50,7 @@ typename C::Point get_new_parent(const C &curve, const typename C::Chunk &new_ch
template<typename C>
struct LayerExtension final
{
std::size_t start_idx;
std::size_t start_idx{0};
std::vector<typename C::Point> hashes;
};
@ -86,7 +84,6 @@ struct LastChunkData final
template<typename C1, typename C2>
class CurveTrees
{
friend class ::CurveTreesUnitTest;
public:
CurveTrees(const C1 &c1, const C2 &c2, const std::size_t c1_width, const std::size_t c2_width):
m_c1{c1},
@ -105,11 +102,11 @@ public:
struct LeafTuple final
{
// Output ed25519 point x-coordinate
typename C2::Scalar O_x;
const typename C2::Scalar O_x;
// Key image generator x-coordinate
typename C2::Scalar I_x;
const typename C2::Scalar I_x;
// Commitment x-coordinate
typename C2::Scalar C_x;
const typename C2::Scalar C_x;
};
static const std::size_t LEAF_TUPLE_SIZE = 3;
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>
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>
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::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 &c1_layers = tree.c1_layers;
const auto &c2_layers = tree.c2_layers;
const auto &leaves = m_tree.leaves;
const auto &c1_layers = m_tree.c1_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
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;
}
//----------------------------------------------------------------------------------------------------------------------
void CurveTreesUnitTest::extend_tree(const CurveTreesV1::TreeExtension &tree_extension,
CurveTreesUnitTest::Tree &tree_inout)
void CurveTreesGlobalTree::extend_tree(const CurveTreesV1::TreeExtension &tree_extension)
{
// 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,
"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)
{
tree_inout.leaves.emplace_back(CurveTreesV1::LeafTuple{
m_tree.leaves.emplace_back(CurveTreesV1::LeafTuple{
.O_x = leaf.O_x,
.I_x = leaf.I_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_idx <= tree_inout.c2_layers.size(), "missing c2 layer");
if (tree_inout.c2_layers.size() == c2_idx)
tree_inout.c2_layers.emplace_back(Layer<Selene>{});
CHECK_AND_ASSERT_THROW_MES(c2_idx <= m_tree.c2_layers.size(), "missing c2 layer");
if (m_tree.c2_layers.size() == c2_idx)
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_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_idx <= tree_inout.c1_layers.size(), "missing c1 layer");
if (tree_inout.c1_layers.size() == c1_idx)
tree_inout.c1_layers.emplace_back(Layer<Helios>{});
CHECK_AND_ASSERT_THROW_MES(c1_idx <= m_tree.c1_layers.size(), "missing c1 layer");
if (m_tree.c1_layers.size() == c1_idx)
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_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 &c1_layers = tree.c1_layers;
const auto &c2_layers = tree.c2_layers;
const auto &leaves = m_tree.leaves;
const auto &c1_layers = m_tree.c1_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(!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
//----------------------------------------------------------------------------------------------------------------------
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 &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 &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, "
<< tree.c1_layers.size() << " helios layers, " << tree.c2_layers.size() << " selene layers");
MDEBUG("Tree has " << m_tree.leaves.size() << " leaves, "
<< 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 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;
std::size_t c1_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)
{
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);
for (std::size_t j = 0; j < c2_layer.size(); ++j)
@ -516,9 +515,9 @@ void CurveTreesUnitTest::log_tree(const CurveTreesUnitTest::Tree &tree)
}
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);
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,
CurveTreesUnitTest &curve_trees_accessor,
const std::size_t num_leaves,
CurveTreesUnitTest::Tree &tree_inout)
CurveTreesGlobalTree &global_tree,
const std::size_t num_leaves)
{
// 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
// - 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,
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
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
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,
const std::size_t ext_leaves,
CurveTreesV1 &curve_trees,
CurveTreesUnitTest &curve_trees_accessor)
CurveTreesV1 &curve_trees)
{
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`
MDEBUG("Adding " << init_leaves << " leaves to tree");
bool res = grow_tree(curve_trees,
curve_trees_accessor,
init_leaves,
global_tree);
global_tree,
init_leaves);
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");
res = grow_tree(curve_trees,
curve_trees_accessor,
ext_leaves,
global_tree);
global_tree,
ext_leaves);
CHECK_AND_ASSERT_MES(res, false, "failed to extend tree in memory");
@ -661,7 +657,6 @@ TEST(curve_trees, grow_tree)
HELIOS_CHUNK_WIDTH,
SELENE_CHUNK_WIDTH);
CurveTreesUnitTest curve_trees_accessor{curve_trees};
unit_test::BlockchainLMDBTest test_db;
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)
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));
}
}

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 SELENE_CHUNK_WIDTH = 18;
// Helper class that can access the private members of the CurveTrees class
class CurveTreesUnitTest
// Helper class to read/write a global tree in memory. It's only used in testing because normally the tree isn't kept
// in memory (it's stored in the db)
class CurveTreesGlobalTree
{
public:
CurveTreesUnitTest(CurveTreesV1 &curve_trees): m_curve_trees(curve_trees) {};
CurveTreesGlobalTree(CurveTreesV1 &curve_trees): m_curve_trees(curve_trees) {};
//member structs
public:
@ -65,20 +66,21 @@ public:
//public member functions
public:
// 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
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
bool validate_tree(const Tree &tree);
bool audit_tree();
// logging helpers
void log_last_chunks(const CurveTreesV1::LastChunks &last_chunks);
void log_tree_extension(const CurveTreesV1::TreeExtension &tree_extension);
void log_tree(const CurveTreesUnitTest::Tree &tree);
void log_tree();
private:
CurveTreesV1 &m_curve_trees;
Tree m_tree = Tree{};
};