2024-05-21 17:22:54 -04:00
|
|
|
// 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/curve_trees.h"
|
|
|
|
#include "fcmp/tower_cycle.h"
|
|
|
|
|
2024-05-23 13:48:08 -04:00
|
|
|
using Helios = fcmp::curve_trees::Helios;
|
|
|
|
using Selene = fcmp::curve_trees::Selene;
|
|
|
|
using CurveTreesV1 = fcmp::curve_trees::CurveTreesV1;
|
2024-05-21 17:22:54 -04:00
|
|
|
|
2024-05-27 17:54:35 -04:00
|
|
|
const std::vector<CurveTreesV1::LeafTuple> generate_random_leaves(const CurveTreesV1 &curve_trees,
|
|
|
|
const std::size_t num_leaves);
|
|
|
|
|
|
|
|
// https://github.com/kayabaNerve/fcmp-plus-plus/blob
|
|
|
|
// /b2742e86f3d18155fd34dd1ed69cb8f79b900fce/crypto/fcmps/src/tests.rs#L81-L82
|
|
|
|
const std::size_t HELIOS_CHUNK_WIDTH = 38;
|
|
|
|
const std::size_t SELENE_CHUNK_WIDTH = 18;
|
|
|
|
|
2024-05-27 19:03:58 -04:00
|
|
|
// 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
|
2024-05-21 17:22:54 -04:00
|
|
|
{
|
|
|
|
public:
|
2024-05-27 19:03:58 -04:00
|
|
|
CurveTreesGlobalTree(CurveTreesV1 &curve_trees): m_curve_trees(curve_trees) {};
|
2024-05-21 17:22:54 -04:00
|
|
|
|
|
|
|
//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;
|
2024-05-21 17:22:54 -04:00
|
|
|
|
|
|
|
// Use the tree extension to extend the in-memory tree
|
2024-05-27 19:03:58 -04:00
|
|
|
void extend_tree(const CurveTreesV1::TreeExtension &tree_extension);
|
2024-05-21 17:22:54 -04:00
|
|
|
|
2024-06-07 01:48:01 -04:00
|
|
|
// Trim tree to the provided number of leaves
|
|
|
|
void trim_tree(const std::size_t new_num_leaves);
|
|
|
|
|
2024-05-21 17:22:54 -04:00
|
|
|
// Validate the in-memory tree by re-hashing every layer, starting from root and working down to leaf layer
|
2024-05-27 19:03:58 -04:00
|
|
|
bool audit_tree();
|
2024-05-21 17:22:54 -04:00
|
|
|
|
2024-05-22 04:31:27 -04:00
|
|
|
// 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);
|
2024-05-22 04:31:27 -04:00
|
|
|
void log_tree_extension(const CurveTreesV1::TreeExtension &tree_extension);
|
2024-05-27 19:03:58 -04:00
|
|
|
void log_tree();
|
2024-05-22 04:31:27 -04:00
|
|
|
|
2024-05-21 17:22:54 -04:00
|
|
|
private:
|
|
|
|
CurveTreesV1 &m_curve_trees;
|
2024-05-27 19:03:58 -04:00
|
|
|
Tree m_tree = Tree{};
|
2024-05-21 17:22:54 -04:00
|
|
|
};
|
2024-05-24 02:56:23 -04:00
|
|
|
|