Guarantee insertion order into the tree using global output ID

- Leaves enter the tree in the block they unlock, in the order
they appear in the chain
This commit is contained in:
j-berman 2024-07-25 12:37:41 -07:00
parent 306488b690
commit 634e12e9ad
13 changed files with 152 additions and 109 deletions

View File

@ -179,7 +179,7 @@ void BlockchainDB::pop_block()
pop_block(blk, txs);
}
void BlockchainDB::add_transaction(const crypto::hash& blk_hash, const std::pair<transaction, blobdata_ref>& txp, const crypto::hash* tx_hash_ptr, const crypto::hash* tx_prunable_hash_ptr)
std::vector<uint64_t> BlockchainDB::add_transaction(const crypto::hash& blk_hash, const std::pair<transaction, blobdata_ref>& txp, const crypto::hash* tx_hash_ptr, const crypto::hash* tx_prunable_hash_ptr)
{
const transaction &tx = txp.first;
@ -223,7 +223,7 @@ void BlockchainDB::add_transaction(const crypto::hash& blk_hash, const std::pair
uint64_t tx_id = add_transaction_data(blk_hash, txp, tx_hash, tx_prunable_hash);
std::vector<uint64_t> amount_output_indices(tx.vout.size());
std::vector<output_indexes_t> output_indices(tx.vout.size());
// iterate tx.vout using indices instead of C++11 foreach syntax because
// we need the index
@ -231,21 +231,35 @@ void BlockchainDB::add_transaction(const crypto::hash& blk_hash, const std::pair
{
// miner v2 txes have their coinbase output in one single out to save space,
// and we store them as rct outputs with an identity mask
// note: tx_outs_to_leaf_tuples in curve_trees.cpp mirrors this logic
if (miner_tx && tx.version == 2)
{
cryptonote::tx_out vout = tx.vout[i];
rct::key commitment = rct::zeroCommit(vout.amount);
vout.amount = 0;
amount_output_indices[i] = add_output(tx_hash, vout, i, tx.unlock_time,
output_indices[i] = add_output(tx_hash, vout, i, tx.unlock_time,
&commitment);
}
else
{
amount_output_indices[i] = add_output(tx_hash, tx.vout[i], i, tx.unlock_time,
output_indices[i] = add_output(tx_hash, tx.vout[i], i, tx.unlock_time,
tx.version > 1 ? &tx.rct_signatures.outPk[i].mask : NULL);
}
}
std::vector<uint64_t> amount_output_indices;
std::vector<uint64_t> output_ids;
amount_output_indices.reserve(output_indices.size());
output_ids.reserve(output_indices.size());
for (const auto &o_idx : output_indices)
{
amount_output_indices.push_back(o_idx.amount_index);
output_ids.push_back(o_idx.output_id);
}
add_tx_amount_output_indices(tx_id, amount_output_indices);
return output_ids;
}
uint64_t BlockchainDB::add_block( const std::pair<block, blobdata>& blck
@ -273,9 +287,12 @@ uint64_t BlockchainDB::add_block( const std::pair<block, blobdata>& blck
time1 = epee::misc_utils::get_tick_count();
std::vector<std::vector<uint64_t>> output_ids;
output_ids.reserve(1 + txs.size());
uint64_t num_rct_outs = 0;
blobdata miner_bd = tx_to_blob(blk.miner_tx);
add_transaction(blk_hash, std::make_pair(blk.miner_tx, blobdata_ref(miner_bd)));
output_ids.push_back(add_transaction(blk_hash, std::make_pair(blk.miner_tx, blobdata_ref(miner_bd))));
if (blk.miner_tx.version == 2)
num_rct_outs += blk.miner_tx.vout.size();
int tx_i = 0;
@ -283,7 +300,7 @@ uint64_t BlockchainDB::add_block( const std::pair<block, blobdata>& blck
for (const std::pair<transaction, blobdata>& tx : txs)
{
tx_hash = blk.tx_hashes[tx_i];
add_transaction(blk_hash, tx, &tx_hash);
output_ids.push_back(add_transaction(blk_hash, tx, &tx_hash));
for (const auto &vout: tx.first.vout)
{
if (vout.amount == 0)
@ -297,20 +314,22 @@ uint64_t BlockchainDB::add_block( const std::pair<block, blobdata>& blck
// When adding a block, we also need to add all the leaf tuples included in
// the block to a table keeping track of locked leaf tuples. Once those leaf
// tuples unlock, we use them to grow the tree.
std::multimap<uint64_t, fcmp::curve_trees::CurveTreesV1::LeafTuple> leaf_tuples_by_unlock_height;
std::multimap<uint64_t, fcmp::curve_trees::CurveTreesV1::LeafTupleContext> leaf_tuples_by_unlock_height;
// Get miner tx's leaf tuples
fcmp::curve_trees::curve_trees_v1.tx_outs_to_leaf_tuples(
blk.miner_tx,
output_ids[0],
prev_height,
true/*miner_tx*/,
leaf_tuples_by_unlock_height);
// Get all other txs' leaf tuples
for (const auto &txp : txs)
for (std::size_t i = 0; i < txs.size(); ++i)
{
fcmp::curve_trees::curve_trees_v1.tx_outs_to_leaf_tuples(
txp.first,
txs[i].first,
output_ids[i+1],
prev_height,
false/*miner_tx*/,
leaf_tuples_by_unlock_height);

View File

@ -188,6 +188,14 @@ struct txpool_tx_meta_t
}
};
/**
* @brief a struct containing output indexes for convenience
*/
struct output_indexes_t
{
uint64_t amount_index;
uint64_t output_id;
};
#define DBF_SAFE 1
#define DBF_FAST 2
@ -408,7 +416,7 @@ private:
, const uint64_t& coins_generated
, uint64_t num_rct_outs
, const crypto::hash& blk_hash
, const std::multimap<uint64_t, fcmp::curve_trees::CurveTreesV1::LeafTuple>& leaf_tuples_by_unlock_height
, const std::multimap<uint64_t, fcmp::curve_trees::CurveTreesV1::LeafTupleContext>& leaf_tuples_by_unlock_height
) = 0;
/**
@ -473,8 +481,9 @@ private:
* future, this tracking (of the number, at least) should be moved to
* this class, as it is necessary and the same among all BlockchainDB.
*
* It returns an amount output index, which is the index of the output
* for its specified amount.
* It returns the output indexes, which contains an amount output index (the
* index of the output for its specified amount) and output id (the global
* index of the output among all outputs of any amount).
*
* This data should be stored in such a manner that the only thing needed to
* reverse the process is the tx_out.
@ -487,9 +496,9 @@ private:
* @param local_index index of the output in its transaction
* @param unlock_time unlock time/height of the output
* @param commitment the rct commitment to the output amount
* @return amount output index
* @return output indexes
*/
virtual uint64_t add_output(const crypto::hash& tx_hash, const tx_out& tx_output, const uint64_t& local_index, const uint64_t unlock_time, const rct::key *commitment) = 0;
virtual output_indexes_t add_output(const crypto::hash& tx_hash, const tx_out& tx_output, const uint64_t& local_index, const uint64_t unlock_time, const rct::key *commitment) = 0;
/**
* @brief store amount output indices for a tx's outputs
@ -570,8 +579,10 @@ protected:
* @param tx the transaction to add
* @param tx_hash_ptr the hash of the transaction, if already calculated
* @param tx_prunable_hash_ptr the hash of the prunable part of the transaction, if already calculated
*
* @return the global output ids of all outputs inserted
*/
void add_transaction(const crypto::hash& blk_hash, const std::pair<transaction, blobdata_ref>& tx, const crypto::hash* tx_hash_ptr = NULL, const crypto::hash* tx_prunable_hash_ptr = NULL);
std::vector<uint64_t> add_transaction(const crypto::hash& blk_hash, const std::pair<transaction, blobdata_ref>& tx, const crypto::hash* tx_hash_ptr = NULL, const crypto::hash* tx_prunable_hash_ptr = NULL);
mutable uint64_t time_tx_exists = 0; //!< a performance metric
uint64_t time_commit1 = 0; //!< a performance metric
@ -1396,17 +1407,6 @@ public:
*/
virtual uint64_t get_num_outputs(const uint64_t& amount) const = 0;
// returns the total number of global outputs
/**
* @brief fetches the total number of global outputs
*
* The subclass should return a count of all outputs,
* or zero if there are none.
* *
* @return the number of global outputs
*/
virtual uint64_t get_num_global_outputs() const = 0;
/**
* @brief return index of the first element (should be hidden, but isn't)
*
@ -1780,7 +1780,7 @@ public:
// TODO: description and make private
virtual void grow_tree(const fcmp::curve_trees::CurveTreesV1 &curve_trees,
const std::vector<fcmp::curve_trees::CurveTreesV1::LeafTuple> &new_leaves) = 0;
const std::vector<fcmp::curve_trees::CurveTreesV1::LeafTupleContext> &new_leaves) = 0;
virtual void trim_tree(const fcmp::curve_trees::CurveTreesV1 &curve_trees, const uint64_t trim_n_leaf_tuples) = 0;

View File

@ -216,7 +216,7 @@ namespace
*
* spent_keys input hash -
*
* locked_leaves block ID [{leaf tuple}...]
* locked_leaves block ID [{output ID, leaf tuple}...]
* leaves leaf_idx {leaf tuple}
* layers layer_idx [{child_chunk_idx, child_chunk_hash}...]
*
@ -817,7 +817,7 @@ estim:
}
void BlockchainLMDB::add_block(const block& blk, size_t block_weight, uint64_t long_term_block_weight, const difficulty_type& cumulative_difficulty, const uint64_t& coins_generated,
uint64_t num_rct_outs, const crypto::hash& blk_hash, const std::multimap<uint64_t, fcmp::curve_trees::CurveTreesV1::LeafTuple> &leaf_tuples_by_unlock_height)
uint64_t num_rct_outs, const crypto::hash& blk_hash, const std::multimap<uint64_t, fcmp::curve_trees::CurveTreesV1::LeafTupleContext> &leaf_tuples_by_unlock_height)
{
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
check_open();
@ -848,10 +848,9 @@ 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
const auto unlocked_leaf_tuples = this->get_locked_leaf_tuples_at_height(m_height);
// TODO: double check consistent order for inserting outputs into the tree
this->grow_tree(fcmp::curve_trees::curve_trees_v1, unlocked_leaf_tuples);
// TODO: remove unlocked_leaf_tuples from the locked outputs table
// TODO: remove locked from the locked outputs table
int result = 0;
@ -1119,7 +1118,7 @@ void BlockchainLMDB::remove_transaction_data(const crypto::hash& tx_hash, const
throw1(DB_ERROR("Failed to add removal of tx index to db transaction"));
}
uint64_t BlockchainLMDB::add_output(const crypto::hash& tx_hash,
output_indexes_t BlockchainLMDB::add_output(const crypto::hash& tx_hash,
const tx_out& tx_output,
const uint64_t& local_index,
const uint64_t unlock_time,
@ -1183,7 +1182,10 @@ uint64_t BlockchainLMDB::add_output(const crypto::hash& tx_hash,
if ((result = mdb_cursor_put(m_cur_output_amounts, &val_amount, &data, MDB_APPENDDUP)))
throw0(DB_ERROR(lmdb_error("Failed to add output pubkey to db transaction: ", result).c_str()));
return ok.amount_index;
return output_indexes_t{
.amount_index = ok.amount_index,
.output_id = ok.output_id
};
}
void BlockchainLMDB::add_tx_amount_output_indices(const uint64_t tx_id,
@ -1362,12 +1364,11 @@ void BlockchainLMDB::remove_spent_key(const crypto::key_image& k_image)
}
void BlockchainLMDB::grow_tree(const fcmp::curve_trees::CurveTreesV1 &curve_trees,
const std::vector<fcmp::curve_trees::CurveTreesV1::LeafTuple> &new_leaves)
const std::vector<fcmp::curve_trees::CurveTreesV1::LeafTupleContext> &new_leaves)
{
if (new_leaves.empty())
return;
// TODO: block_wtxn_start like pop_block, then call BlockchainDB::grow_tree
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
check_open();
mdb_txn_cursors *m_cursors = &m_wcursors;
@ -1970,6 +1971,8 @@ bool BlockchainLMDB::audit_tree(const fcmp::curve_trees::CurveTreesV1 &curve_tre
const uint64_t actual_n_leaf_tuples = this->get_num_leaf_tuples();
CHECK_AND_ASSERT_MES(actual_n_leaf_tuples == expected_n_leaf_tuples, false, "unexpected num leaf tuples");
MDEBUG("Auditing tree with " << actual_n_leaf_tuples << " leaf tuples");
if (actual_n_leaf_tuples == 0)
{
// Make sure layers table is also empty
@ -2203,7 +2206,7 @@ bool BlockchainLMDB::audit_layer(const C_CHILD &c_child,
chunk_width);
}
std::vector<fcmp::curve_trees::CurveTreesV1::LeafTuple> BlockchainLMDB::get_locked_leaf_tuples_at_height(
std::vector<fcmp::curve_trees::CurveTreesV1::LeafTupleContext> BlockchainLMDB::get_locked_leaf_tuples_at_height(
const uint64_t height)
{
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
@ -2216,7 +2219,7 @@ std::vector<fcmp::curve_trees::CurveTreesV1::LeafTuple> BlockchainLMDB::get_lock
MDB_val v_tuple;
// Get all the locked outputs at that height
std::vector<fcmp::curve_trees::CurveTreesV1::LeafTuple> leaf_tuples;
std::vector<fcmp::curve_trees::CurveTreesV1::LeafTupleContext> leaf_tuples;
// TODO: double check this gets all leaf tuples when it does multiple iters
MDB_cursor_op op = MDB_SET;
@ -2233,8 +2236,8 @@ std::vector<fcmp::curve_trees::CurveTreesV1::LeafTuple> BlockchainLMDB::get_lock
if (h != height)
throw0(DB_ERROR(("Height " + std::to_string(h) + " not the expected" + std::to_string(height)).c_str()));
const auto range_begin = ((const fcmp::curve_trees::CurveTreesV1::LeafTuple*)v_tuple.mv_data);
const auto range_end = range_begin + v_tuple.mv_size / sizeof(fcmp::curve_trees::CurveTreesV1::LeafTuple);
const auto range_begin = ((const fcmp::curve_trees::CurveTreesV1::LeafTupleContext*)v_tuple.mv_data);
const auto range_end = range_begin + v_tuple.mv_size / sizeof(fcmp::curve_trees::CurveTreesV1::LeafTupleContext);
auto it = range_begin;
@ -4399,27 +4402,6 @@ uint64_t BlockchainLMDB::get_num_outputs(const uint64_t& amount) const
return num_elems;
}
uint64_t BlockchainLMDB::get_num_global_outputs() const
{
LOG_PRINT_L3("BlockchainLMDB:: " << __func__);
check_open();
TXN_PREFIX_RDONLY();
RCURSOR(output_amounts);
MDB_stat db_stats;
int result = mdb_stat(m_txn, m_output_amounts, &db_stats);
uint64_t count = 0;
if (result != MDB_NOTFOUND)
{
if (result)
throw0(DB_ERROR(lmdb_error("Failed to query m_output_amounts: ", result).c_str()));
count = db_stats.ms_entries;
}
TXN_POSTFIX_RDONLY();
return count;
}
output_data_t BlockchainLMDB::get_output_key(const uint64_t& amount, const uint64_t& index, bool include_commitmemt) const
{
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
@ -6688,6 +6670,9 @@ void BlockchainLMDB::migrate_5_6()
// ... Could also require outputs be inserted all-or-nothing first, and then can pick up where left off for the tree
// if any of leaves, layers, or block_infn tables exist, then locked_leaves migration should be complete
// TODO: I can keep track of the contiguous output_id inserted in a separate table used strictly for this migration
// On next run, read all outputs until we reach the highest contiguous output_id, then continue from there
do
{
// 1. Set up locked outputs table
@ -6711,7 +6696,7 @@ void BlockchainLMDB::migrate_5_6()
MDB_cursor_op op = MDB_FIRST;
const uint64_t n_outputs = this->get_num_global_outputs();
const uint64_t n_outputs = this->num_outputs();
i = 0;
while (1)
@ -6763,23 +6748,25 @@ void BlockchainLMDB::migrate_5_6()
uint64_t amount = *(const uint64_t*)k.mv_data;
output_data_t output_data;
fcmp::curve_trees::CurveTreesV1::LeafTupleContext tuple_context;
if (amount == 0)
{
const outkey *okp = (const outkey *)v.mv_data;
output_data = okp->data;
tuple_context.output_id = okp->output_id;
}
else
{
const pre_rct_outkey *okp = (const pre_rct_outkey *)v.mv_data;
memcpy(&output_data, &okp->data, sizeof(pre_rct_output_data_t));
output_data.commitment = rct::zeroCommit(amount);
tuple_context.output_id = okp->output_id;
}
// Convert the output into a leaf tuple
fcmp::curve_trees::CurveTreesV1::LeafTuple leaf_tuple;
try
{
leaf_tuple = fcmp::curve_trees::curve_trees_v1.output_to_leaf_tuple(
tuple_context.leaf_tuple = fcmp::curve_trees::curve_trees_v1.output_to_leaf_tuple(
output_data.pubkey,
rct::rct2pk(output_data.commitment));
}
@ -6792,9 +6779,9 @@ void BlockchainLMDB::migrate_5_6()
// Get the block in which the output will unlock
const uint64_t unlock_height = cryptonote::get_unlock_height(output_data.unlock_time, output_data.height);
// Now add the leaf tuple to the locked outputs table
// Now add the leaf tuple to the locked leaves table
MDB_val_set(k_height, unlock_height);
MDB_val_set(v_tuple, leaf_tuple);
MDB_val_set(v_tuple, tuple_context);
// MDB_NODUPDATA because no benefit to having duplicate outputs in the tree, only 1 can be spent
// Can't use MDB_APPENDDUP because outputs aren't inserted in order sorted by unlock height

View File

@ -278,7 +278,6 @@ public:
virtual uint64_t get_tx_block_height(const crypto::hash& h) const;
virtual uint64_t get_num_outputs(const uint64_t& amount) const;
virtual uint64_t get_num_global_outputs() const;
virtual output_data_t get_output_key(const uint64_t& amount, const uint64_t& index, bool include_commitmemt) const;
virtual void get_output_key(const epee::span<const uint64_t> &amounts, const std::vector<uint64_t> &offsets, std::vector<output_data_t> &outputs, bool allow_partial = false) const;
@ -370,7 +369,7 @@ public:
// make private
virtual void grow_tree(const fcmp::curve_trees::CurveTreesV1 &curve_trees,
const std::vector<fcmp::curve_trees::CurveTreesV1::LeafTuple> &new_leaves);
const std::vector<fcmp::curve_trees::CurveTreesV1::LeafTupleContext> &new_leaves);
virtual void trim_tree(const fcmp::curve_trees::CurveTreesV1 &curve_trees, const uint64_t trim_n_leaf_tuples);
@ -391,7 +390,7 @@ private:
, const uint64_t& coins_generated
, uint64_t num_rct_outs
, const crypto::hash& block_hash
, const std::multimap<uint64_t, fcmp::curve_trees::CurveTreesV1::LeafTuple>& leaf_tuples_by_unlock_height
, const std::multimap<uint64_t, fcmp::curve_trees::CurveTreesV1::LeafTupleContext>& leaf_tuples_by_unlock_height
);
virtual void remove_block();
@ -400,7 +399,7 @@ private:
virtual void remove_transaction_data(const crypto::hash& tx_hash, const transaction& tx);
virtual uint64_t add_output(const crypto::hash& tx_hash,
virtual output_indexes_t add_output(const crypto::hash& tx_hash,
const tx_out& tx_output,
const uint64_t& local_index,
const uint64_t unlock_time,
@ -451,7 +450,7 @@ private:
const uint64_t child_chunk_idx,
const uint64_t chunk_width) const;
std::vector<fcmp::curve_trees::CurveTreesV1::LeafTuple> get_locked_leaf_tuples_at_height(const uint64_t height);
std::vector<fcmp::curve_trees::CurveTreesV1::LeafTupleContext> get_locked_leaf_tuples_at_height(const uint64_t height);
uint64_t num_outputs() const;
@ -547,6 +546,8 @@ private:
mdb_txn_cursors m_wcursors;
mutable boost::thread_specific_ptr<mdb_threadinfo> m_tinfo;
// TODO: m_curve_trees
#if defined(__arm__)
// force a value so it can compile with 32-bit ARM
constexpr static uint64_t DEFAULT_MAPSIZE = 1LL << 31;

View File

@ -100,7 +100,6 @@ public:
virtual std::vector<cryptonote::transaction> get_tx_list(const std::vector<crypto::hash>& hlist) const override { return std::vector<cryptonote::transaction>(); }
virtual uint64_t get_tx_block_height(const crypto::hash& h) const override { return 0; }
virtual uint64_t get_num_outputs(const uint64_t& amount) const override { return 1; }
virtual uint64_t get_num_global_outputs() const override { return 1; }
virtual uint64_t get_indexing_base() const override { return 0; }
virtual cryptonote::output_data_t get_output_key(const uint64_t& amount, const uint64_t& index, bool include_commitmemt) const override { return cryptonote::output_data_t(); }
virtual cryptonote::tx_out_index get_output_tx_and_index_from_global(const uint64_t& index) const override { return cryptonote::tx_out_index(); }
@ -113,12 +112,12 @@ public:
virtual void remove_block() override { }
virtual uint64_t add_transaction_data(const crypto::hash& blk_hash, const std::pair<cryptonote::transaction, cryptonote::blobdata_ref>& tx, const crypto::hash& tx_hash, const crypto::hash& tx_prunable_hash) override {return 0;}
virtual void remove_transaction_data(const crypto::hash& tx_hash, const cryptonote::transaction& tx) override {}
virtual uint64_t add_output(const crypto::hash& tx_hash, const cryptonote::tx_out& tx_output, const uint64_t& local_index, const uint64_t unlock_time, const rct::key *commitment) override {return 0;}
virtual output_indexes_t add_output(const crypto::hash& tx_hash, const cryptonote::tx_out& tx_output, const uint64_t& local_index, const uint64_t unlock_time, const rct::key *commitment) override {return {0, 0};}
virtual void add_tx_amount_output_indices(const uint64_t tx_index, const std::vector<uint64_t>& amount_output_indices) override {}
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(const fcmp::curve_trees::CurveTreesV1 &curve_trees,
const std::vector<fcmp::curve_trees::CurveTreesV1::LeafTuple> &new_leaves) override {};
const std::vector<fcmp::curve_trees::CurveTreesV1::LeafTupleContext> &new_leaves) override {};
virtual void trim_tree(const fcmp::curve_trees::CurveTreesV1 &curve_trees, const uint64_t trim_n_leaf_tuples) override {};
virtual bool audit_tree(const fcmp::curve_trees::CurveTreesV1 &curve_trees, const uint64_t expected_n_leaf_tuples) const override { return false; };
@ -149,7 +148,7 @@ public:
, const uint64_t& coins_generated
, uint64_t num_rct_outs
, const crypto::hash& blk_hash
, const std::multimap<uint64_t, fcmp::curve_trees::CurveTreesV1::LeafTuple>& leaf_tuples_by_unlock_height
, const std::multimap<uint64_t, fcmp::curve_trees::CurveTreesV1::LeafTupleContext>& leaf_tuples_by_unlock_height
) override { }
virtual cryptonote::block get_block_from_height(const uint64_t& height) const override { return cryptonote::block(); }
virtual void set_hard_fork_version(uint64_t height, uint8_t version) override {}

View File

@ -1673,8 +1673,10 @@ namespace cryptonote
{
const auto seconds_since_unlock = hf_v15_time - unlock_time;
const auto blocks_since_unlock = seconds_since_unlock / DIFFICULTY_TARGET_V2;
CHECK_AND_ASSERT_THROW_MES(hf_v15_height > blocks_since_unlock, "unexpected blocks since unlock");
unlock_height = hf_v15_height - blocks_since_unlock;
unlock_height = hf_v15_height >= blocks_since_unlock
? (hf_v15_height - blocks_since_unlock)
: default_unlock_height;
}
else
{

View File

@ -630,25 +630,27 @@ CurveTrees<Helios, Selene>::LeafTuple CurveTrees<Helios, Selene>::output_to_leaf
const crypto::public_key &output_pubkey,
const crypto::public_key &commitment) const
{
CHECK_AND_ASSERT_THROW_MES(crypto::check_key(output_pubkey), "invalid output pub key");
if (!crypto::check_key(output_pubkey))
throw std::runtime_error("invalid output pub key");
const auto clear_torsion = [](const crypto::public_key &key)
const auto clear_torsion = [](const crypto::public_key &key, const std::string &s)
{
// TODO: don't need to decompress and recompress points, can be optimized
rct::key torsion_cleared_key = rct::scalarmultKey(rct::pk2rct(key), rct::INV_EIGHT);
torsion_cleared_key = rct::scalarmult8(torsion_cleared_key);
CHECK_AND_ASSERT_THROW_MES(torsion_cleared_key != rct::I, "cannot equal identity");
if (torsion_cleared_key == rct::I)
throw std::runtime_error(s + " cannot equal identity");
return torsion_cleared_key;
};
// Torsion clear the output pub key and commitment
const rct::key rct_O = clear_torsion(output_pubkey);
const rct::key rct_C = clear_torsion(commitment);
const rct::key rct_O = clear_torsion(output_pubkey, "output pub key");
const rct::key rct_C = clear_torsion(commitment, "commitment");
const crypto::public_key O = rct::rct2pk(rct_O);
const crypto::public_key C = rct::rct2pk(rct_C);
const crypto::public_key &O = rct::rct2pk(rct_O);
const crypto::public_key &C = rct::rct2pk(rct_C);
crypto::ec_point I;
crypto::derive_key_image_generator(O, I);
@ -679,12 +681,15 @@ std::vector<typename C2::Scalar> CurveTrees<C1, C2>::flatten_leaves(const std::v
//----------------------------------------------------------------------------------------------------------------------
template <>
void CurveTrees<Helios, Selene>::tx_outs_to_leaf_tuples(const cryptonote::transaction &tx,
const std::vector<uint64_t> &output_ids,
const uint64_t tx_height,
const bool miner_tx,
std::multimap<uint64_t, CurveTrees<Helios, Selene>::LeafTuple> &leaf_tuples_by_unlock_height_inout) const
std::multimap<uint64_t, CurveTrees<Helios, Selene>::LeafTupleContext> &leaf_tuples_by_unlock_height_inout) const
{
const uint64_t unlock_height = cryptonote::get_unlock_height(tx.unlock_time, tx_height);
CHECK_AND_ASSERT_THROW_MES(tx.vout.size() == output_ids.size(), "unexpected size of output ids");
for (std::size_t i = 0; i < tx.vout.size(); ++i)
{
const auto &out = tx.vout[i];
@ -693,7 +698,13 @@ void CurveTrees<Helios, Selene>::tx_outs_to_leaf_tuples(const cryptonote::transa
if (!cryptonote::get_output_public_key(out, output_public_key))
throw std::runtime_error("Could not get an output public key from a tx output.");
const rct::key commitment = (miner_tx || tx.version < 2)
static_assert(CURRENT_TRANSACTION_VERSION == 2, "This section of code was written with 2 tx versions in mind. "
"Revisit this section and update for the new tx version.");
if (!miner_tx && tx.version == 2)
CHECK_AND_ASSERT_THROW_MES(tx.rct_signatures.outPk.size() > i, "unexpected size of outPk");
const rct::key commitment = (miner_tx || tx.version != 2)
? rct::zeroCommit(out.amount)
: tx.rct_signatures.outPk[i].mask;
@ -704,7 +715,12 @@ void CurveTrees<Helios, Selene>::tx_outs_to_leaf_tuples(const cryptonote::transa
output_public_key,
rct::rct2pk(commitment));
leaf_tuples_by_unlock_height_inout.emplace(unlock_height, std::move(leaf_tuple));
auto tuple_context = CurveTrees<Helios, Selene>::LeafTupleContext{
.output_id = output_ids[i],
.leaf_tuple = std::move(leaf_tuple),
};
leaf_tuples_by_unlock_height_inout.emplace(unlock_height, std::move(tuple_context));
}
catch (...)
{ /*continue*/ };
@ -715,7 +731,7 @@ template<typename C1, typename C2>
typename CurveTrees<C1, C2>::TreeExtension CurveTrees<C1, C2>::get_tree_extension(
const uint64_t old_n_leaf_tuples,
const LastHashes &existing_last_hashes,
const std::vector<LeafTuple> &new_leaf_tuples) const
const std::vector<LeafTupleContext> &new_leaf_tuples) const
{
TreeExtension tree_extension;
@ -730,15 +746,21 @@ typename CurveTrees<C1, C2>::TreeExtension CurveTrees<C1, C2>::get_tree_extensio
tree_extension.leaves.start_leaf_tuple_idx = grow_layer_instructions.old_total_children / LEAF_TUPLE_SIZE;
// Copy the leaves
// Sort the leaves by order they appear in the chain
// TODO: don't copy here
std::vector<LeafTupleContext> sorted_leaf_tuples = new_leaf_tuples;
const auto sort_fn = [](const LeafTupleContext &a, const LeafTupleContext &b) { return a.output_id < b.output_id; };
std::sort(sorted_leaf_tuples.begin(), sorted_leaf_tuples.end(), sort_fn);
// Copy the sorted leaves into the tree extension struct
// TODO: don't copy here
tree_extension.leaves.tuples.reserve(new_leaf_tuples.size());
for (const auto &leaf : new_leaf_tuples)
for (const auto &leaf : sorted_leaf_tuples)
{
tree_extension.leaves.tuples.emplace_back(LeafTuple{
.O_x = leaf.O_x,
.I_x = leaf.I_x,
.C_x = leaf.C_x
.O_x = leaf.leaf_tuple.O_x,
.I_x = leaf.leaf_tuple.I_x,
.C_x = leaf.leaf_tuple.C_x
});
}
@ -751,7 +773,7 @@ typename CurveTrees<C1, C2>::TreeExtension CurveTrees<C1, C2>::get_tree_extensio
grow_layer_instructions.need_old_last_parent ? &existing_last_hashes.c2_last_hashes[0] : nullptr,
grow_layer_instructions.start_offset,
grow_layer_instructions.next_parent_start_index,
this->flatten_leaves(new_leaf_tuples),
this->flatten_leaves(tree_extension.leaves.tuples),
m_leaf_layer_chunk_width
);

View File

@ -164,6 +164,14 @@ public:
static const uint64_t LEAF_TUPLE_SIZE = 3;
static_assert(sizeof(LeafTuple) == (sizeof(typename C2::Scalar) * LEAF_TUPLE_SIZE), "unexpected LeafTuple size");
// Contextual wrapper for leaf tuple
struct LeafTupleContext final
{
// Global output ID useful to order the leaf tuple for insertion into the tree
uint64_t output_id;
LeafTuple leaf_tuple;
};
// Contiguous leaves in the tree, starting a specified start_idx in the leaf layer
struct Leaves final
{
@ -221,15 +229,16 @@ public:
// Convert cryptonote tx outs to leaf tuples, grouped by the leaf tuple unlock height
void tx_outs_to_leaf_tuples(const cryptonote::transaction &tx,
const std::vector<uint64_t> &output_ids,
const uint64_t tx_height,
const bool miner_tx,
std::multimap<uint64_t, LeafTuple> &leaf_tuples_by_unlock_height_inout) const;
std::multimap<uint64_t, LeafTupleContext> &leaf_tuples_by_unlock_height_inout) const;
// Take in the existing number of leaf tuples and the existing last hashes of each layer in the tree, as well as new
// leaves to add to the tree, and return a tree extension struct that can be used to extend a tree
TreeExtension get_tree_extension(const uint64_t old_n_leaf_tuples,
const LastHashes &existing_last_hashes,
const std::vector<LeafTuple> &new_leaf_tuples) const;
const std::vector<LeafTupleContext> &new_leaf_tuples) const;
// Get instructions useful for trimming all existing layers in the tree
std::vector<TrimLayerInstructions> get_trim_instructions(

View File

@ -65,7 +65,7 @@ public:
, const uint64_t& coins_generated
, uint64_t num_rct_outs
, const crypto::hash& blk_hash
, const std::multimap<uint64_t, fcmp::curve_trees::CurveTreesV1::LeafTuple>& leaf_tuples_by_unlock_height
, const std::multimap<uint64_t, fcmp::curve_trees::CurveTreesV1::LeafTupleContext>& leaf_tuples_by_unlock_height
) override {
blocks.push_back({block_weight, long_term_block_weight});
}

View File

@ -88,7 +88,7 @@ namespace
, const uint64_t& coins_generated
, uint64_t num_rct_outs
, const crypto::hash& blk_hash
, const std::multimap<uint64_t, fcmp::curve_trees::CurveTreesV1::LeafTuple>& leaf_tuples_by_unlock_height
, const std::multimap<uint64_t, fcmp::curve_trees::CurveTreesV1::LeafTupleContext>& leaf_tuples_by_unlock_height
) override
{
blocks.push_back({blk, blk_hash});

View File

@ -730,13 +730,14 @@ void CurveTreesGlobalTree::log_tree()
//----------------------------------------------------------------------------------------------------------------------
// Test helpers
//----------------------------------------------------------------------------------------------------------------------
static const std::vector<CurveTreesV1::LeafTuple> generate_random_leaves(const CurveTreesV1 &curve_trees,
const std::size_t num_leaves)
static const std::vector<fcmp::curve_trees::CurveTreesV1::LeafTupleContext> generate_random_leaves(const CurveTreesV1 &curve_trees,
const std::size_t old_n_leaf_tuples,
const std::size_t new_n_leaf_tuples)
{
std::vector<CurveTreesV1::LeafTuple> tuples;
tuples.reserve(num_leaves);
std::vector<CurveTreesV1::LeafTupleContext> tuples;
tuples.reserve(new_n_leaf_tuples);
for (std::size_t i = 0; i < num_leaves; ++i)
for (std::size_t i = 0; i < new_n_leaf_tuples; ++i)
{
// Generate random output tuple
crypto::secret_key o,c;
@ -746,7 +747,10 @@ static const std::vector<CurveTreesV1::LeafTuple> generate_random_leaves(const C
auto leaf_tuple = curve_trees.output_to_leaf_tuple(O, C);
tuples.emplace_back(std::move(leaf_tuple));
tuples.emplace_back(fcmp::curve_trees::CurveTreesV1::LeafTupleContext{
.output_id = old_n_leaf_tuples + i,
.leaf_tuple = std::move(leaf_tuple),
});
}
return tuples;
@ -775,7 +779,7 @@ static bool grow_tree(CurveTreesV1 &curve_trees,
// - 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(old_n_leaf_tuples,
last_hashes,
generate_random_leaves(curve_trees, new_n_leaf_tuples));
generate_random_leaves(curve_trees, old_n_leaf_tuples, new_n_leaf_tuples));
global_tree.log_tree_extension(tree_extension);
@ -852,14 +856,14 @@ static bool grow_tree_db(const std::size_t init_leaves,
LOG_PRINT_L1("Adding " << init_leaves << " leaves to db, then extending by " << ext_leaves << " leaves");
test_db.m_db->grow_tree(curve_trees, generate_random_leaves(curve_trees, init_leaves));
test_db.m_db->grow_tree(curve_trees, generate_random_leaves(curve_trees, 0, init_leaves));
CHECK_AND_ASSERT_MES(test_db.m_db->audit_tree(curve_trees, init_leaves), false,
"failed to add initial leaves to db");
MDEBUG("Successfully added initial " << init_leaves << " leaves to db, extending by "
<< ext_leaves << " leaves");
test_db.m_db->grow_tree(curve_trees, generate_random_leaves(curve_trees, ext_leaves));
test_db.m_db->grow_tree(curve_trees, generate_random_leaves(curve_trees, init_leaves, ext_leaves));
CHECK_AND_ASSERT_MES(test_db.m_db->audit_tree(curve_trees, init_leaves + ext_leaves), false,
"failed to extend tree in db");
@ -881,7 +885,7 @@ static bool trim_tree_db(const std::size_t init_leaves,
LOG_PRINT_L1("Adding " << init_leaves << " leaves to db, then trimming by " << trim_leaves << " leaves");
test_db.m_db->grow_tree(curve_trees, generate_random_leaves(curve_trees, init_leaves));
test_db.m_db->grow_tree(curve_trees, generate_random_leaves(curve_trees, 0, init_leaves));
CHECK_AND_ASSERT_MES(test_db.m_db->audit_tree(curve_trees, init_leaves), false,
"failed to add initial leaves to db");

View File

@ -54,7 +54,7 @@ public:
, const uint64_t& coins_generated
, uint64_t num_rct_outs
, const crypto::hash& blk_hash
, const std::multimap<uint64_t, fcmp::curve_trees::CurveTreesV1::LeafTuple>& leaf_tuples_by_unlock_height
, const std::multimap<uint64_t, fcmp::curve_trees::CurveTreesV1::LeafTupleContext>& leaf_tuples_by_unlock_height
) override {
blocks.push_back(blk);
}

View File

@ -58,7 +58,7 @@ public:
, const uint64_t& coins_generated
, uint64_t num_rct_outs
, const crypto::hash& blk_hash
, const std::multimap<uint64_t, fcmp::curve_trees::CurveTreesV1::LeafTuple>& leaf_tuples_by_unlock_height
, const std::multimap<uint64_t, fcmp::curve_trees::CurveTreesV1::LeafTupleContext>& leaf_tuples_by_unlock_height
) override {
blocks.push_back({block_weight, long_term_block_weight});
}