mirror of
https://github.com/monero-project/monero.git
synced 2025-01-07 11:27:53 -05:00
fcmp++: trim tree when removing a block
- trim_tree now re-adds trimmed outputs back to the locked outputs table. remove_output then deletes from the locked output table. - Since outputs added to the tree in a specific block may have originated from distinct younger blocks (thanks to distinct unlock times), we need to store the 8 byte output_id in the leaves table as well, so that in the event of a reorg, upon removing outputs from the tree we can add them back to the locked outputs table in the correct order.
This commit is contained in:
parent
41b1985f63
commit
16ff6a9e68
@ -1785,7 +1785,7 @@ public:
|
||||
// TODO: description and make private
|
||||
virtual void grow_tree(std::vector<fcmp_pp::curve_trees::OutputContext> &&new_outputs) = 0;
|
||||
|
||||
virtual void trim_tree(const uint64_t trim_n_leaf_tuples) = 0;
|
||||
virtual void trim_tree(const uint64_t trim_n_leaf_tuples, const uint64_t trim_block_id) = 0;
|
||||
|
||||
// TODO: description
|
||||
virtual bool audit_tree(const uint64_t expected_n_leaf_tuples) const = 0;
|
||||
|
@ -200,7 +200,7 @@ namespace
|
||||
* spent_keys input hash -
|
||||
*
|
||||
* locked_outputs block ID [{output ID, output pubkey, commitment}...]
|
||||
* leaves leaf_idx {output pubkey, commitment}
|
||||
* leaves leaf_idx {output ID, output pubkey, commitment}
|
||||
* layers layer_idx [{child_chunk_idx, child_chunk_hash}...]
|
||||
*
|
||||
* txpool_meta txn hash txn metadata
|
||||
@ -828,8 +828,7 @@ void BlockchainLMDB::add_block(const block& blk, size_t block_weight, uint64_t l
|
||||
|
||||
// Grow the tree with outputs that unlock at this block height
|
||||
auto unlocked_outputs = this->get_outs_at_unlock_block_id(m_height);
|
||||
if (!unlocked_outputs.empty())
|
||||
this->grow_tree(std::move(unlocked_outputs));
|
||||
this->grow_tree(std::move(unlocked_outputs));
|
||||
|
||||
// Now that we've used the unlocked leaves to grow the tree, we can delete them from the locked outputs table
|
||||
this->del_locked_outs_at_block_id(m_height);
|
||||
@ -935,6 +934,15 @@ void BlockchainLMDB::remove_block()
|
||||
|
||||
if ((result = mdb_cursor_del(m_cur_block_info, 0)))
|
||||
throw1(DB_ERROR(lmdb_error("Failed to add removal of block info to db transaction: ", result).c_str()));
|
||||
|
||||
// Get n_leaf_tuples from the new tip so we can trim the curve trees tree to the new tip
|
||||
const uint64_t new_n_leaf_tuples = get_top_block_n_leaf_tuples();
|
||||
const uint64_t old_n_leaf_tuples = bi->bi_n_leaf_tuples;
|
||||
|
||||
if (new_n_leaf_tuples > old_n_leaf_tuples)
|
||||
throw1(DB_ERROR("Unexpected: more leaf tuples are in prev block, tree is expected to only grow"));
|
||||
|
||||
this->trim_tree(old_n_leaf_tuples - new_n_leaf_tuples, bi->bi_height/*trim_block_id*/);
|
||||
}
|
||||
|
||||
uint64_t BlockchainLMDB::add_transaction_data(const crypto::hash& blk_hash, const std::pair<transaction, blobdata_ref>& txp, const crypto::hash& tx_hash, const crypto::hash& tx_prunable_hash)
|
||||
@ -1244,6 +1252,32 @@ void BlockchainLMDB::remove_output(const uint64_t amount, const uint64_t& out_in
|
||||
{
|
||||
throw1(DB_ERROR(lmdb_error("Error adding removal of output tx to db transaction", result).c_str()));
|
||||
}
|
||||
|
||||
// Remove output from locked outputs table. We expect the output to be in the
|
||||
// locked outputs table because remove_output is called when removing the
|
||||
// top block from the chain, and all outputs from the top block are expected
|
||||
// to be locked until they are at least 10 blocks old.
|
||||
CURSOR(locked_outputs);
|
||||
|
||||
const uint64_t unlock_block = cryptonote::get_unlock_block_index(ok->data.unlock_time, ok->data.height);
|
||||
|
||||
MDB_val_set(k_block_id, unlock_block);
|
||||
MDB_val_set(v_output, ok->output_id);
|
||||
|
||||
result = mdb_cursor_get(m_cur_locked_outputs, &k_block_id, &v_output, MDB_GET_BOTH);
|
||||
if (result == MDB_NOTFOUND)
|
||||
{
|
||||
throw0(DB_ERROR("Unexpected: output not found in m_cur_locked_outputs"));
|
||||
}
|
||||
else if (result)
|
||||
{
|
||||
throw1(DB_ERROR(lmdb_error("Error adding removal of locked output to db transaction", result).c_str()));
|
||||
}
|
||||
|
||||
result = mdb_cursor_del(m_cur_locked_outputs, 0);
|
||||
if (result)
|
||||
throw0(DB_ERROR(lmdb_error(std::string("Error deleting locked output index ").append(boost::lexical_cast<std::string>(out_index).append(": ")).c_str(), result).c_str()));
|
||||
|
||||
result = mdb_cursor_del(m_cur_output_txs, 0);
|
||||
if (result)
|
||||
throw0(DB_ERROR(lmdb_error(std::string("Error deleting output index ").append(boost::lexical_cast<std::string>(out_index).append(": ")).c_str(), result).c_str()));
|
||||
@ -1475,7 +1509,7 @@ void BlockchainLMDB::grow_layer(const std::unique_ptr<C> &curve,
|
||||
}
|
||||
}
|
||||
|
||||
void BlockchainLMDB::trim_tree(const uint64_t trim_n_leaf_tuples)
|
||||
void BlockchainLMDB::trim_tree(const uint64_t trim_n_leaf_tuples, const uint64_t trim_block_id)
|
||||
{
|
||||
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
|
||||
if (trim_n_leaf_tuples == 0)
|
||||
@ -1485,6 +1519,7 @@ void BlockchainLMDB::trim_tree(const uint64_t trim_n_leaf_tuples)
|
||||
mdb_txn_cursors *m_cursors = &m_wcursors;
|
||||
|
||||
CURSOR(leaves)
|
||||
CURSOR(locked_outputs)
|
||||
CURSOR(layers)
|
||||
|
||||
const uint64_t old_n_leaf_tuples = this->get_num_leaf_tuples();
|
||||
@ -1511,11 +1546,12 @@ void BlockchainLMDB::trim_tree(const uint64_t trim_n_leaf_tuples)
|
||||
|
||||
// Trim the leaves
|
||||
// TODO: trim_leaves
|
||||
MDB_val_set(k_block_id, trim_block_id);
|
||||
for (uint64_t i = 0; i < trim_n_leaf_tuples; ++i)
|
||||
{
|
||||
uint64_t last_leaf_tuple_idx = (old_n_leaf_tuples - 1 - i);
|
||||
uint64_t leaf_tuple_idx = (old_n_leaf_tuples - trim_n_leaf_tuples + i);
|
||||
|
||||
MDB_val_copy<uint64_t> k(last_leaf_tuple_idx);
|
||||
MDB_val_copy<uint64_t> k(leaf_tuple_idx);
|
||||
MDB_val v;
|
||||
int result = mdb_cursor_get(m_cur_leaves, &k, &v, MDB_SET);
|
||||
if (result == MDB_NOTFOUND)
|
||||
@ -1523,11 +1559,20 @@ void BlockchainLMDB::trim_tree(const uint64_t trim_n_leaf_tuples)
|
||||
if (result != MDB_SUCCESS)
|
||||
throw0(DB_ERROR(lmdb_error("Failed to get leaf: ", result).c_str()));
|
||||
|
||||
// Re-add the output to the locked output table in order. The output should
|
||||
// be in the outputs tables.
|
||||
const auto o = *(fcmp_pp::curve_trees::OutputContext *)v.mv_data;
|
||||
MDB_val_set(v_output, o);
|
||||
result = mdb_cursor_put(m_cur_locked_outputs, &k_block_id, &v_output, MDB_APPENDDUP);
|
||||
if (result != MDB_SUCCESS)
|
||||
throw0(DB_ERROR(lmdb_error("Failed to re-add locked output: ", result).c_str()));
|
||||
|
||||
// Delete the leaf
|
||||
result = mdb_cursor_del(m_cur_leaves, 0);
|
||||
if (result != MDB_SUCCESS)
|
||||
throw0(DB_ERROR(lmdb_error("Error removing leaf: ", result).c_str()));
|
||||
|
||||
MDEBUG("Successfully removed leaf at last_leaf_tuple_idx: " << last_leaf_tuple_idx);
|
||||
MDEBUG("Successfully removed leaf at leaf_tuple_idx: " << leaf_tuple_idx);
|
||||
}
|
||||
|
||||
// Trim the layers
|
||||
@ -1599,6 +1644,7 @@ void BlockchainLMDB::trim_layer(const std::unique_ptr<C> &curve,
|
||||
|
||||
CURSOR(layers)
|
||||
|
||||
MDEBUG("Trimming layer " << layer_idx);
|
||||
MDB_val_copy<uint64_t> k(layer_idx);
|
||||
|
||||
// Get the number of existing elements in the layer
|
||||
@ -1685,6 +1731,32 @@ uint64_t BlockchainLMDB::get_num_leaf_tuples() const
|
||||
return n_leaf_tuples;
|
||||
}
|
||||
|
||||
uint64_t BlockchainLMDB::get_top_block_n_leaf_tuples() const
|
||||
{
|
||||
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
|
||||
check_open();
|
||||
|
||||
TXN_PREFIX_RDONLY();
|
||||
RCURSOR(block_info);
|
||||
|
||||
// if no blocks, return 0
|
||||
uint64_t m_height = height();
|
||||
if (m_height == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
MDB_val_copy<uint64_t> k(m_height - 1);
|
||||
MDB_val h = k;
|
||||
int result = 0;
|
||||
if ((result = mdb_cursor_get(m_cur_block_info, (MDB_val *)&zerokval, &h, MDB_GET_BOTH)))
|
||||
throw1(BLOCK_DNE(lmdb_error("Failed to get top block: ", result).c_str()));
|
||||
|
||||
const uint64_t n_leaf_tuples = ((mdb_block_info *)h.mv_data)->bi_n_leaf_tuples;
|
||||
TXN_POSTFIX_RDONLY();
|
||||
return n_leaf_tuples;
|
||||
}
|
||||
|
||||
std::array<uint8_t, 32UL> BlockchainLMDB::get_tree_root() const
|
||||
{
|
||||
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
|
||||
@ -1811,10 +1883,10 @@ fcmp_pp::curve_trees::CurveTreesV1::LastChunkChildrenToTrim BlockchainLMDB::get_
|
||||
if (result != MDB_SUCCESS)
|
||||
throw0(DB_ERROR(lmdb_error("Failed to get leaf: ", result).c_str()));
|
||||
|
||||
const auto output_pair = *(fcmp_pp::curve_trees::OutputPair *)v.mv_data;
|
||||
const auto o = *(fcmp_pp::curve_trees::OutputContext *)v.mv_data;
|
||||
|
||||
// TODO: parallelize calls to this function
|
||||
auto leaf = m_curve_trees->leaf_tuple(output_pair);
|
||||
auto leaf = m_curve_trees->leaf_tuple(o.output_pair);
|
||||
|
||||
leaves_to_trim.emplace_back(std::move(leaf.O_x));
|
||||
leaves_to_trim.emplace_back(std::move(leaf.I_x));
|
||||
@ -1987,8 +2059,8 @@ bool BlockchainLMDB::audit_tree(const uint64_t expected_n_leaf_tuples) const
|
||||
if (result != MDB_SUCCESS)
|
||||
throw0(DB_ERROR(lmdb_error("Failed to add leaf: ", result).c_str()));
|
||||
|
||||
const auto output_pair = *(fcmp_pp::curve_trees::OutputPair *)v.mv_data;
|
||||
auto leaf = m_curve_trees->leaf_tuple(output_pair);
|
||||
const auto o = *(fcmp_pp::curve_trees::OutputContext *)v.mv_data;
|
||||
auto leaf = m_curve_trees->leaf_tuple(o.output_pair);
|
||||
|
||||
leaf_tuples_chunk.emplace_back(std::move(leaf));
|
||||
|
||||
|
@ -370,7 +370,7 @@ public:
|
||||
// make private
|
||||
virtual void grow_tree(std::vector<fcmp_pp::curve_trees::OutputContext> &&new_outputs);
|
||||
|
||||
virtual void trim_tree(const uint64_t trim_n_leaf_tuples);
|
||||
virtual void trim_tree(const uint64_t trim_n_leaf_tuples, const uint64_t trim_block_id);
|
||||
|
||||
virtual bool audit_tree(const uint64_t expected_n_leaf_tuples) const;
|
||||
|
||||
@ -431,6 +431,8 @@ private:
|
||||
|
||||
virtual uint64_t get_num_leaf_tuples() const;
|
||||
|
||||
uint64_t get_top_block_n_leaf_tuples() const;
|
||||
|
||||
virtual std::array<uint8_t, 32UL> get_tree_root() const;
|
||||
|
||||
fcmp_pp::curve_trees::CurveTreesV1::LastHashes get_tree_last_hashes() const;
|
||||
|
@ -117,7 +117,7 @@ public:
|
||||
virtual void add_spent_key(const crypto::key_image& k_image) override {}
|
||||
virtual void remove_spent_key(const crypto::key_image& k_image) override {}
|
||||
virtual void grow_tree(std::vector<fcmp_pp::curve_trees::OutputContext> &&new_outputs) override {};
|
||||
virtual void trim_tree(const uint64_t trim_n_leaf_tuples) override {};
|
||||
virtual void trim_tree(const uint64_t trim_n_leaf_tuples, const uint64_t trim_block_id) override {};
|
||||
virtual bool audit_tree(const uint64_t expected_n_leaf_tuples) const override { return false; };
|
||||
virtual std::array<uint8_t, 32UL> get_tree_root() const override { return {}; };
|
||||
virtual uint64_t get_num_leaf_tuples() const override { return 0; };
|
||||
|
@ -740,7 +740,7 @@ typename CurveTrees<C1, C2>::TreeExtension CurveTrees<C1, C2>::get_tree_extensio
|
||||
flattened_leaves.emplace_back(std::move(leaf.C_x));
|
||||
|
||||
// We can derive {O.x,I.x,C.x} from output pairs, so we store just the output pair in the db to save 32 bytes
|
||||
tree_extension.leaves.tuples.emplace_back(std::move(o.output_pair));
|
||||
tree_extension.leaves.tuples.emplace_back(std::move(o));
|
||||
}
|
||||
|
||||
if (flattened_leaves.empty())
|
||||
|
@ -197,9 +197,9 @@ public:
|
||||
struct Leaves final
|
||||
{
|
||||
// Starting leaf tuple index in the leaf layer
|
||||
uint64_t start_leaf_tuple_idx{0};
|
||||
uint64_t start_leaf_tuple_idx{0};
|
||||
// Contiguous leaves in a tree that start at the start_idx
|
||||
std::vector<OutputPair> tuples;
|
||||
std::vector<OutputContext> tuples;
|
||||
};
|
||||
|
||||
// A struct useful to extend an existing tree
|
||||
|
@ -139,4 +139,4 @@ CResult hash_trim_selene(SelenePoint existing_hash,
|
||||
SeleneScalar child_to_grow_back);
|
||||
|
||||
} // extern "C"
|
||||
}//namespace fcmp_pp
|
||||
}//namespace fcmp_pp_rust
|
||||
|
@ -169,9 +169,9 @@ void CurveTreesGlobalTree::extend_tree(const CurveTreesV1::TreeExtension &tree_e
|
||||
"unexpected leaf start idx");
|
||||
|
||||
m_tree.leaves.reserve(m_tree.leaves.size() + tree_extension.leaves.tuples.size());
|
||||
for (const auto &output_pair : tree_extension.leaves.tuples)
|
||||
for (const auto &o : tree_extension.leaves.tuples)
|
||||
{
|
||||
auto leaf = m_curve_trees.leaf_tuple(output_pair);
|
||||
auto leaf = m_curve_trees.leaf_tuple(o.output_pair);
|
||||
|
||||
m_tree.leaves.emplace_back(CurveTreesV1::LeafTuple{
|
||||
.O_x = std::move(leaf.O_x),
|
||||
@ -641,14 +641,14 @@ void CurveTreesGlobalTree::log_tree_extension(const CurveTreesV1::TreeExtension
|
||||
MDEBUG("Leaf start idx: " << tree_extension.leaves.start_leaf_tuple_idx);
|
||||
for (std::size_t i = 0; i < tree_extension.leaves.tuples.size(); ++i)
|
||||
{
|
||||
const auto &output_pair = tree_extension.leaves.tuples[i];
|
||||
const auto &output_pair = tree_extension.leaves.tuples[i].output_pair;
|
||||
const auto leaf = m_curve_trees.leaf_tuple(output_pair);
|
||||
|
||||
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 C_x = m_curve_trees.m_c2->to_string(leaf.C_x);
|
||||
|
||||
MDEBUG("Leaf tuple idx " << (tree_extension.leaves.start_leaf_tuple_idx)
|
||||
MDEBUG("Leaf tuple idx " << (tree_extension.leaves.start_leaf_tuple_idx + (i * CurveTreesV1::LEAF_TUPLE_SIZE))
|
||||
<< " : { O_x: " << O_x << " , I_x: " << I_x << " , C_x: " << C_x << " }");
|
||||
}
|
||||
|
||||
@ -926,7 +926,8 @@ static bool trim_tree_db(const std::size_t init_leaves,
|
||||
MDEBUG("Successfully added initial " << init_leaves << " leaves to db, trimming by "
|
||||
<< trim_leaves << " leaves");
|
||||
|
||||
test_db.m_db->trim_tree(trim_leaves);
|
||||
// Can use 0 from trim_block_id since it's unused in tests
|
||||
test_db.m_db->trim_tree(trim_leaves, 0);
|
||||
CHECK_AND_ASSERT_MES(test_db.m_db->audit_tree(init_leaves - trim_leaves), false,
|
||||
"failed to trim tree in db");
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user