monero/tests/unit_tests/curve_trees.h

98 lines
4.2 KiB
C
Raw Normal View History

// Copyright (c) 2014, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#pragma once
#include "fcmp_pp/curve_trees.h"
#include "fcmp_pp/tower_cycle.h"
using Helios = fcmp_pp::curve_trees::Helios;
using Selene = fcmp_pp::curve_trees::Selene;
using CurveTreesV1 = fcmp_pp::curve_trees::CurveTreesV1;
// 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:
CurveTreesGlobalTree(CurveTreesV1 &curve_trees): m_curve_trees(curve_trees) {};
//member structs
public:
template<typename C>
using Layer = std::vector<typename C::Point>;
// A complete tree, useful for testing (don't want to keep the whole tree in memory during normal operation)
struct Tree final
{
std::vector<CurveTreesV1::LeafTuple> leaves;
std::vector<Layer<Helios>> c1_layers;
std::vector<Layer<Selene>> c2_layers;
};
//public member functions
public:
Fix grow_tree, restructure it, and clean the approach The initial impl didn't capture the following edge case: - Tree has 3 (or more) layers + 1 leaf layeri - Leaf layer last chunk IS full - Layer 0 last chunk is NOT full - Layer 1 last chunk is NOT full - Layer 2 last chunk IS NOT full In this case, when updating layer 1, we need to use layer 0's old last hash to update layer 1's old last hash. Same for Layer 2. The solution is to use logic that checks the *prev* layer when updating a layer to determine if the old last hash from the prev layer is needed. This commit restructures the grow_tree impl to account for this and simplifies the approach as follows: 1. Read the tree to get num leaf tuples + last hashes in each layer 2. Get the tree extension using the above values + new leaf tuples 2a. Prior to updating the leaf layer, call the function get_update_leaf_layer_metadata. This function uses existing totals in the leaf layer, the new total of leaf tuples, and tree params to calculate how the layer after the leaf layer should be updated. 2b. For each subsequent layer, call the function get_update_layer_metadata. This function uses the existing totals in the *prev* layer, the new total of children in the *prev* layer, and tree params to calculate how the layer should be updated. 3. Grow the tree using the tree extension. This approach isolates update logic and actual hashing into neat structured functions, rather than mix the two. This makes the code easier to follow without needing to keep so much in your head at one time.
2024-06-28 14:00:10 -04:00
// Read the in-memory tree and get the number of leaf tuples
std::size_t get_num_leaf_tuples() const;
// Read the in-memory tree and get the last hashes from each layer in the tree
CurveTreesV1::LastHashes get_last_hashes() const;
// Use the tree extension to extend the in-memory tree
void extend_tree(const CurveTreesV1::TreeExtension &tree_extension);
// Use the tree reduction to reduce the in-memory tree
void reduce_tree(const CurveTreesV1::TreeReduction &tree_reduction);
// Trim the provided number of leaf tuples from the tree
void trim_tree(const std::size_t trim_n_leaf_tuples);
2024-06-07 01:48:01 -04:00
// Validate the in-memory tree by re-hashing every layer, starting from root and working down to leaf layer
bool audit_tree(const std::size_t expected_n_leaf_tuples);
// logging helpers
Fix grow_tree, restructure it, and clean the approach The initial impl didn't capture the following edge case: - Tree has 3 (or more) layers + 1 leaf layeri - Leaf layer last chunk IS full - Layer 0 last chunk is NOT full - Layer 1 last chunk is NOT full - Layer 2 last chunk IS NOT full In this case, when updating layer 1, we need to use layer 0's old last hash to update layer 1's old last hash. Same for Layer 2. The solution is to use logic that checks the *prev* layer when updating a layer to determine if the old last hash from the prev layer is needed. This commit restructures the grow_tree impl to account for this and simplifies the approach as follows: 1. Read the tree to get num leaf tuples + last hashes in each layer 2. Get the tree extension using the above values + new leaf tuples 2a. Prior to updating the leaf layer, call the function get_update_leaf_layer_metadata. This function uses existing totals in the leaf layer, the new total of leaf tuples, and tree params to calculate how the layer after the leaf layer should be updated. 2b. For each subsequent layer, call the function get_update_layer_metadata. This function uses the existing totals in the *prev* layer, the new total of children in the *prev* layer, and tree params to calculate how the layer should be updated. 3. Grow the tree using the tree extension. This approach isolates update logic and actual hashing into neat structured functions, rather than mix the two. This makes the code easier to follow without needing to keep so much in your head at one time.
2024-06-28 14:00:10 -04:00
void log_last_hashes(const CurveTreesV1::LastHashes &last_hashes);
void log_tree_extension(const CurveTreesV1::TreeExtension &tree_extension);
void log_tree();
// Read the in-memory tree and get data from what will be the last chunks after trimming the tree to the provided
// number of leaves
// - This function is useful to collect all tree data necessary to perform the actual trim operation
// - This function can return elems from each last chunk that will need to be trimmed
CurveTreesV1::LastHashes get_last_hashes_to_trim(
const std::vector<fcmp_pp::curve_trees::TrimLayerInstructions> &trim_instructions) const;
CurveTreesV1::LastChunkChildrenToTrim get_all_last_chunk_children_to_trim(
const std::vector<fcmp_pp::curve_trees::TrimLayerInstructions> &trim_instructions);
private:
CurveTreesV1 &m_curve_trees;
Tree m_tree = Tree{};
};
2024-05-24 02:56:23 -04:00