From 0009e1335a94cfc047d2c4ca53d7c9d2017bca62 Mon Sep 17 00:00:00 2001 From: j-berman Date: Tue, 11 Aug 2026 11:01:06 -0700 Subject: [PATCH] cryptonote_core: tx verif util to collect transp amt commitments With FCMP++, we'll need the transparent amount commitments for all txs in a block in 3 distinct places: 1) Adding v2 coinbase outputs to the db. 2) Adding any transparent amount outputs to the curve tree. 3) Validating amount commitments for torsion (may not be strictly necessary for this). Since we do those actions at separate call sites, rather than re-calculate transparent amount commitments at every call site, we collect them one time and avoid repeating the calculation. --- src/cryptonote_core/tx_verification_utils.cpp | 62 +++++++++++++++++++ src/cryptonote_core/tx_verification_utils.h | 29 +++++++++ 2 files changed, 91 insertions(+) diff --git a/src/cryptonote_core/tx_verification_utils.cpp b/src/cryptonote_core/tx_verification_utils.cpp index 652965a7178..a6985273ec2 100644 --- a/src/cryptonote_core/tx_verification_utils.cpp +++ b/src/cryptonote_core/tx_verification_utils.cpp @@ -42,6 +42,28 @@ using namespace cryptonote; +static void collect_transparent_amount_commitments_static( + const std::vector> &tx_refs, + std::unordered_map &transparent_amount_commitments_inout) +{ + // Note: we do not clear transparent_amount_commitments_inout because it may be a rolling cache + + for (const std::reference_wrapper &tx_ref : tx_refs) + { + const transaction &tx = tx_ref.get(); + + // We only need commitments for transparent amounts, which are tx version 1 || coinbase txs + if (tx.version > 1 && !cryptonote::is_coinbase(tx)) + continue; + for (const auto &tx_out : tx.vout) + { + const uint64_t amount = tx_out.amount; + if (transparent_amount_commitments_inout.find(amount) == transparent_amount_commitments_inout.end()) + transparent_amount_commitments_inout[amount] = rct::zeroCommitVartime(amount); + } + } +} + // Do RCT expansion, then do post-expansion sanity checks, then do full non-semantics verification. static bool expand_tx_and_ver_rct_non_sem(transaction& tx, const rct::ctkeyM& mix_ring) { @@ -315,6 +337,46 @@ static bool ver_non_input_consensus_templated(TxForwardIt tx_begin, TxForwardIt namespace cryptonote { +std::vector> collect_transparent_amount_commitments( + const transaction &miner_tx, + const std::vector> &tx_pairs, + std::unordered_map &transparent_amount_commitments_inout) +{ + std::vector> tx_refs; + tx_refs.reserve(1 + tx_pairs.size()); + tx_refs.push_back(std::cref(miner_tx)); + for (const auto &tx : tx_pairs) + tx_refs.push_back(std::cref(tx.first)); + collect_transparent_amount_commitments_static(tx_refs, transparent_amount_commitments_inout); + return tx_refs; +} + +std::vector> collect_transparent_amount_commitments( + const transaction &miner_tx, + const std::vector &txs, + std::unordered_map &transparent_amount_commitments_inout) +{ + std::vector> tx_refs; + tx_refs.reserve(1 + txs.size()); + tx_refs.push_back(std::cref(miner_tx)); + for (const auto &tx : txs) + tx_refs.push_back(std::cref(tx)); + collect_transparent_amount_commitments_static(tx_refs, transparent_amount_commitments_inout); + return tx_refs; +} + +std::vector> collect_transparent_amount_commitments( + const std::unordered_map> &txs_by_txid, + std::unordered_map &transparent_amount_commitments_inout) +{ + std::vector> tx_refs; + tx_refs.reserve(txs_by_txid.size()); + for (const auto &tx_pair : txs_by_txid) + tx_refs.push_back(std::cref(tx_pair.second.first)); + collect_transparent_amount_commitments_static(tx_refs, transparent_amount_commitments_inout); + return tx_refs; +} + uint64_t get_transaction_weight_limit(const uint8_t hf_version) { // from v8, limit a tx to 50% of the minimum block weight diff --git a/src/cryptonote_core/tx_verification_utils.h b/src/cryptonote_core/tx_verification_utils.h index 9af705a6b79..4522436f9d0 100644 --- a/src/cryptonote_core/tx_verification_utils.h +++ b/src/cryptonote_core/tx_verification_utils.h @@ -28,6 +28,9 @@ #pragma once +#include +#include + #include "cryptonote_basic/blobdatatype.h" #include "cryptonote_basic/cryptonote_basic.h" #include "cryptonote_basic/verification_context.h" @@ -35,6 +38,32 @@ namespace cryptonote { +/** + * @brief Calculate all commitments for any transparent amounts passed in + * + * The container passed in may have some commitments already calculated. + * This function won't erase from it, it'll just add to the collection + * if the commitment hasn't been calculated yet. + * + * @param miner_tx the miner tx included in a block + * @param tx_pairs the txs included in a block + * @param transparent_amount_commitments_inout the collection passed in and returned by ref + * @return all passed in txs reference wrappers + */ +std::vector> collect_transparent_amount_commitments( + const transaction &miner_tx, + const std::vector> &tx_pairs, + std::unordered_map &transparent_amount_commitments_inout); + +std::vector> collect_transparent_amount_commitments( + const transaction &miner_tx, + const std::vector &txs, + std::unordered_map &transparent_amount_commitments_inout); + +std::vector> collect_transparent_amount_commitments( + const std::unordered_map> &txs_by_txid, + std::unordered_map &transparent_amount_commitments_inout); + /** * @brief Get the maximum transaction weight for a given hardfork *