From 9e9aa1beeaf7b77bf706b30ff145b21d2e732aab Mon Sep 17 00:00:00 2001 From: jeffro256 Date: Sun, 31 Mar 2024 21:59:43 -0500 Subject: [PATCH] Legacy Indexing Overhaul This PR removes "universal"-style indexing for legacy CLSAG rings, and replaces it with a reference set scheme that uses (amount, index in amount) indexing pairs to reference on-chain enotes. This is the same method that Cryptonote txs use, and is how the current Monero Core LMDB database is referenced. Doing things this way means that the database will not have to be re-indexed, saving at a very minimum 1.6 GB (100M on-chain enotes * (16 bytes for extra table keys)) of storage space, and an expensive database migration involving moving all existing enote data to a new table. We change the MockLedgerContext to support this indexing scheme. In practice, serialized txs under this method shouldn't take up much more space than pre-PR if compressed clever-ly, and assuming most ring members will RingCT enotes. We also add LegacyEnoteOriginContext for contextualized enote records so we can better keep tracked of scanned legacy enotes under the legacy indexing scheme. Co-authored-by: SNeedlewoods --- src/seraphis_core/legacy_decoy_selector.h | 7 +- .../legacy_decoy_selector_flat.cpp | 56 +++--- .../legacy_decoy_selector_flat.h | 24 ++- src/seraphis_core/legacy_enote_types.cpp | 15 ++ src/seraphis_core/legacy_enote_types.h | 6 +- src/seraphis_core/legacy_enote_utils.cpp | 5 + src/seraphis_core/legacy_enote_utils.h | 7 + src/seraphis_core/legacy_output_index.h | 99 ++++++++++ .../enote_finding_context_legacy.cpp | 4 +- src/seraphis_impl/seraphis_serialization.h | 146 +++++++++++++- .../contextual_enote_record_types.cpp | 83 ++++++-- .../contextual_enote_record_types.h | 44 ++++- .../contextual_enote_record_utils.cpp | 46 ++++- .../contextual_enote_record_utils.h | 11 +- src/seraphis_main/enote_finding_context.h | 2 +- .../scan_balance_recovery_utils.cpp | 88 +++++---- .../scan_balance_recovery_utils.h | 4 +- src/seraphis_main/scan_misc_utils.cpp | 12 +- src/seraphis_main/tx_builder_types_legacy.h | 4 +- .../tx_builder_types_multisig.cpp | 2 +- src/seraphis_main/tx_builder_types_multisig.h | 4 +- .../tx_builders_legacy_inputs.cpp | 16 +- src/seraphis_main/tx_builders_legacy_inputs.h | 6 +- src/seraphis_main/tx_builders_mixed.cpp | 34 ++-- src/seraphis_main/tx_builders_multisig.cpp | 42 ++--- src/seraphis_main/tx_builders_multisig.h | 4 +- .../tx_component_types_legacy.cpp | 18 +- src/seraphis_main/tx_component_types_legacy.h | 29 ++- src/seraphis_main/tx_validation_context.h | 3 +- src/seraphis_main/tx_validators.cpp | 26 ++- src/seraphis_main/txtype_squashed_v1.cpp | 6 +- src/seraphis_mocks/mock_ledger_context.cpp | 178 ++++++++++++------ src/seraphis_mocks/mock_ledger_context.h | 27 ++- src/seraphis_mocks/mock_send_receive.cpp | 4 +- .../mock_tx_builders_legacy_inputs.cpp | 31 +-- .../mock_tx_builders_legacy_inputs.h | 9 +- .../scan_context_async_mock.cpp | 37 ++-- .../tx_validation_context_mock.h | 2 +- tests/unit_tests/seraphis_enote_scanning.cpp | 52 +++++ tests/unit_tests/seraphis_multisig.cpp | 4 +- 40 files changed, 896 insertions(+), 301 deletions(-) create mode 100644 src/seraphis_core/legacy_output_index.h diff --git a/src/seraphis_core/legacy_decoy_selector.h b/src/seraphis_core/legacy_decoy_selector.h index 0ec6f28fe7..1663d901a4 100644 --- a/src/seraphis_core/legacy_decoy_selector.h +++ b/src/seraphis_core/legacy_decoy_selector.h @@ -31,12 +31,13 @@ #pragma once //local headers +#include "legacy_output_index.h" //third party headers //standard headers #include -#include +#include //forward declarations @@ -60,9 +61,9 @@ class LegacyDecoySelector //member functions /// request a set of ring members as on-chain enote indices - virtual void get_ring_members(const std::uint64_t real_ring_member_index, + virtual void get_ring_members(const legacy_output_index_t real_ring_member_index, const std::uint64_t num_ring_members, - std::vector &ring_members_out, + std::set &ring_members_out, std::uint64_t &real_ring_member_index_in_ref_set_out) const = 0; }; diff --git a/src/seraphis_core/legacy_decoy_selector_flat.cpp b/src/seraphis_core/legacy_decoy_selector_flat.cpp index 0c5b1ec83b..6a6a4d0015 100644 --- a/src/seraphis_core/legacy_decoy_selector_flat.cpp +++ b/src/seraphis_core/legacy_decoy_selector_flat.cpp @@ -45,50 +45,52 @@ namespace sp { //------------------------------------------------------------------------------------------------------------------- -LegacyDecoySelectorFlat::LegacyDecoySelectorFlat(const std::uint64_t min_index, const std::uint64_t max_index) : - m_min_index{min_index}, - m_max_index{max_index} +LegacyDecoySelectorFlat::LegacyDecoySelectorFlat(index_bounds_by_amount_t index_bounds_by_amount) : + m_index_bounds_by_amount(std::move(index_bounds_by_amount)) { // checks - CHECK_AND_ASSERT_THROW_MES(m_max_index >= m_min_index, "legacy decoy selector (flat): invalid element range."); + for (const auto &p : m_index_bounds_by_amount) + { + const std::uint64_t min_ind{p.second.first}; + const std::uint64_t max_ind{p.second.second}; + CHECK_AND_ASSERT_THROW_MES(max_ind >= min_ind, "legacy decoy selector (flat): min > max index."); + } } //------------------------------------------------------------------------------------------------------------------- -void LegacyDecoySelectorFlat::get_ring_members(const std::uint64_t real_ring_member_index, +void LegacyDecoySelectorFlat::get_ring_members(const legacy_output_index_t real_ring_member_index, const std::uint64_t num_ring_members, - std::vector &ring_members_out, + std::set &ring_members_out, std::uint64_t &real_ring_member_index_in_ref_set_out) const { - CHECK_AND_ASSERT_THROW_MES(real_ring_member_index >= m_min_index, + const rct::xmr_amount amount{real_ring_member_index.ledger_indexing_amount}; + const std::uint64_t min_index{this->get_min_index(amount)}; + const std::uint64_t max_index{this->get_max_index(amount)}; + + CHECK_AND_ASSERT_THROW_MES(real_ring_member_index.index >= min_index, "legacy decoy selector (flat): real ring member index below available index range."); - CHECK_AND_ASSERT_THROW_MES(real_ring_member_index <= m_max_index, + CHECK_AND_ASSERT_THROW_MES(real_ring_member_index.index <= max_index, "legacy decoy selector (flat): real ring member index above available index range."); - CHECK_AND_ASSERT_THROW_MES(num_ring_members <= m_max_index - m_min_index + 1, + CHECK_AND_ASSERT_THROW_MES(num_ring_members <= max_index - min_index + 1, "legacy decoy selector (flat): insufficient available legacy enotes to have unique ring members."); // fill in ring members ring_members_out.clear(); - ring_members_out.reserve(num_ring_members); - ring_members_out.emplace_back(real_ring_member_index); + ring_members_out.insert(real_ring_member_index); while (ring_members_out.size() < num_ring_members) { - // select a new ring member from indices in the specified range that aren't used yet (only unique ring members - // are allowed) - std::uint64_t new_ring_member; - do { new_ring_member = crypto::rand_range(m_min_index, m_max_index); } - while (std::find(ring_members_out.begin(), ring_members_out.end(), new_ring_member) != ring_members_out.end()); - - ring_members_out.emplace_back(new_ring_member); + // select a new ring member from indices in the specified range with uniform distribution + const std::uint64_t new_ring_member_index{crypto::rand_range(min_index, max_index)}; + // add to set (only unique values will remain) + ring_members_out.insert({amount, new_ring_member_index}); } - // sort reference set - std::sort(ring_members_out.begin(), ring_members_out.end()); - // find location in reference set where the real reference sits // note: the reference set does not contain duplicates, so we don't have to handle the case of multiple real references + // note2: `ring_members_out` is a `std::set`, which contains ordered keys, so the index selected will be correct. real_ring_member_index_in_ref_set_out = 0; - for (const std::uint64_t reference : ring_members_out) + for (const legacy_output_index_t reference : ring_members_out) { if (reference == real_ring_member_index) return; @@ -97,4 +99,14 @@ void LegacyDecoySelectorFlat::get_ring_members(const std::uint64_t real_ring_mem } } //------------------------------------------------------------------------------------------------------------------- +std::uint64_t LegacyDecoySelectorFlat::get_min_index(const rct::xmr_amount amount) const +{ + return m_index_bounds_by_amount.at(amount).first; +} +//------------------------------------------------------------------------------------------------------------------- +std::uint64_t LegacyDecoySelectorFlat::get_max_index(const rct::xmr_amount amount) const +{ + return m_index_bounds_by_amount.at(amount).second; +} +//------------------------------------------------------------------------------------------------------------------- } //namespace sp diff --git a/src/seraphis_core/legacy_decoy_selector_flat.h b/src/seraphis_core/legacy_decoy_selector_flat.h index 0447d653ec..d70589d756 100644 --- a/src/seraphis_core/legacy_decoy_selector_flat.h +++ b/src/seraphis_core/legacy_decoy_selector_flat.h @@ -36,8 +36,8 @@ //third party headers //standard headers -#include -#include +#include +#include //forward declarations @@ -47,29 +47,37 @@ namespace sp //// // LegacyDecoySelectorFlat -// - get a set of unique legacy ring members, selected from a flat distribution across the range of available enotes +// - get a set of unique legacy ring members, selected from a flat distribution across the range of available +// enotes with the same ledger indexing amount /// class LegacyDecoySelectorFlat final : public LegacyDecoySelector { public: +//member types + /// [ ledger amount : {min index, max index} ] + using index_bounds_by_amount_t = std::map>; + //constructors /// default constructor: disabled /// normal constructor - LegacyDecoySelectorFlat(const std::uint64_t min_index, const std::uint64_t max_index); + LegacyDecoySelectorFlat(index_bounds_by_amount_t index_bounds_by_amount); //destructor: default //member functions /// request a set of ring members from range [min_index, max_index] - void get_ring_members(const std::uint64_t real_ring_member_index, + void get_ring_members(const legacy_output_index_t real_ring_member_index, const std::uint64_t num_ring_members, - std::vector &ring_members_out, + std::set &ring_members_out, std::uint64_t &real_ring_member_index_in_ref_set_out) const override; //member variables private: - std::uint64_t m_min_index; - std::uint64_t m_max_index; + index_bounds_by_amount_t m_index_bounds_by_amount; + +//member functions (private) + std::uint64_t get_min_index(const rct::xmr_amount amount) const; + std::uint64_t get_max_index(const rct::xmr_amount amount) const; }; } //namespace sp diff --git a/src/seraphis_core/legacy_enote_types.cpp b/src/seraphis_core/legacy_enote_types.cpp index 521d507cc3..dd67deb6f4 100644 --- a/src/seraphis_core/legacy_enote_types.cpp +++ b/src/seraphis_core/legacy_enote_types.cpp @@ -76,6 +76,21 @@ rct::key amount_commitment_ref(const LegacyEnoteVariant &variant) return variant.visit(visitor()); } //------------------------------------------------------------------------------------------------------------------- +rct::xmr_amount cleartext_amount_ref(const LegacyEnoteVariant &variant) +{ + struct visitor final : public tools::variant_static_visitor + { + using variant_static_visitor::operator(); //for blank overload + rct::xmr_amount operator()(const LegacyEnoteV1 &enote) const { return enote.amount; } + rct::xmr_amount operator()(const LegacyEnoteV2 &enote) const { return 0; } + rct::xmr_amount operator()(const LegacyEnoteV3 &enote) const { return 0; } + rct::xmr_amount operator()(const LegacyEnoteV4 &enote) const { return enote.amount; } + rct::xmr_amount operator()(const LegacyEnoteV5 &enote) const { return 0; } + }; + + return variant.visit(visitor()); +} +//------------------------------------------------------------------------------------------------------------------- LegacyEnoteV1 gen_legacy_enote_v1() { LegacyEnoteV1 temp; diff --git a/src/seraphis_core/legacy_enote_types.h b/src/seraphis_core/legacy_enote_types.h index afffdfc5e1..d1ce864ce6 100644 --- a/src/seraphis_core/legacy_enote_types.h +++ b/src/seraphis_core/legacy_enote_types.h @@ -47,7 +47,7 @@ namespace sp { //// -// LegacyEnoteV1 +// LegacyEnoteV1 (all pre-RingCT enotes, then post-RingCT pre-viewtag coinbase) // - onetime address // - cleartext amount /// @@ -104,7 +104,7 @@ struct LegacyEnoteV3 final inline std::size_t legacy_enote_v3_size_bytes() { return 2*32 + 8; } //// -// LegacyEnoteV4 +// LegacyEnoteV4 (post-viewtag coinbase, also post-viewtag v1 unmixable dust txs) // - onetime address // - cleartext amount // - view tag @@ -151,10 +151,12 @@ inline std::size_t legacy_enote_v5_size_bytes() { return 2*32 + 8 + sizeof(crypt // onetime_address_ref(): get the enote's onetime address // amount_commitment_ref(): get the enote's amount commitment (this is a copy because V1 enotes need to // compute the commitment) +// cleartext_amount_ref(): get the enote's cleartext amount if applicable, or 0 otherwise /// using LegacyEnoteVariant = tools::variant; const rct::key& onetime_address_ref(const LegacyEnoteVariant &variant); rct::key amount_commitment_ref(const LegacyEnoteVariant &variant); +rct::xmr_amount cleartext_amount_ref(const LegacyEnoteVariant &variant); /** * brief: gen_legacy_enote_v1() - generate a legacy v1 enote (all random) diff --git a/src/seraphis_core/legacy_enote_utils.cpp b/src/seraphis_core/legacy_enote_utils.cpp index 34c3686b21..fbad026981 100644 --- a/src/seraphis_core/legacy_enote_utils.cpp +++ b/src/seraphis_core/legacy_enote_utils.cpp @@ -58,6 +58,11 @@ void get_legacy_enote_identifier(const rct::key &onetime_address, const rct::xmr sp_hash_to_32(transcript.data(), transcript.size(), identifier_out.bytes); } //------------------------------------------------------------------------------------------------------------------- +rct::xmr_amount get_legacy_ledger_indexing_amount(const LegacyEnoteVariant &enote, const bool is_rct) +{ + return is_rct ? 0 : cleartext_amount_ref(enote); +} +//------------------------------------------------------------------------------------------------------------------- void make_legacy_enote_v1(const rct::key &destination_spendkey, const rct::key &destination_viewkey, const rct::xmr_amount amount, diff --git a/src/seraphis_core/legacy_enote_utils.h b/src/seraphis_core/legacy_enote_utils.h index 29a63c8b28..b4943b95eb 100644 --- a/src/seraphis_core/legacy_enote_utils.h +++ b/src/seraphis_core/legacy_enote_utils.h @@ -59,6 +59,13 @@ namespace sp * outparam: identifier_out - H_32(Ko, a) */ void get_legacy_enote_identifier(const rct::key &onetime_address, const rct::xmr_amount amount, rct::key &identifier_out); +/** + * brief: get_legacy_ledger_indexing_amount - get the "amount" key used for indexing this enote in the ledger + * param: enote - + * param: is_rct - true if this is a RingCT enote + * return: the enote's cleartext amount if pre-RingCT or 0 if RingCT +*/ +rct::xmr_amount get_legacy_ledger_indexing_amount(const LegacyEnoteVariant &enote, const bool is_rct); /** * brief: make_legacy_enote_v1 - make a v1 legacy enote sending to an address or subaddress * param: destination_spendkey - [address: K^s = k^s G] [subaddress: K^{s,i} = (Hn(k^v, i) + k^s) G] diff --git a/src/seraphis_core/legacy_output_index.h b/src/seraphis_core/legacy_output_index.h new file mode 100644 index 0000000000..129c71fd77 --- /dev/null +++ b/src/seraphis_core/legacy_output_index.h @@ -0,0 +1,99 @@ +// Copyright (c) 2022, 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. + +// Convenience type for indexing legacy enotes in (ledger amount, index in amount) form. + +#pragma once + +//local headers + +//third party headers + +//standard headers +#include // std::uint64_t +#include // std::hash + +//forward declarations +namespace rct { typedef uint64_t xmr_amount; } + +namespace sp +{ +//// +// legacy_output_index_t +// - used to index legacy enotes the same way cryptonote inputs do: (ledger amount, index in amount) +// +struct legacy_output_index_t +{ + /// the public ledger amount used to index the enote, 0 for everything post RingCT (even coinbase) + rct::xmr_amount ledger_indexing_amount; + /// the nth position of this enote in the chain for the given amount + std::uint64_t index; +}; + +inline bool operator==(const legacy_output_index_t &a, const legacy_output_index_t &b) +{ + return a.ledger_indexing_amount == b.ledger_indexing_amount && a.index == b.index; +} + +inline bool operator!=(const legacy_output_index_t &a, const legacy_output_index_t &b) +{ + return !(a == b); +} + +/// the only purpose of this total ordering is consistency, it can't tell which came first on-chain +inline bool operator<(const legacy_output_index_t &a, const legacy_output_index_t &b) +{ + if (a.ledger_indexing_amount < b.ledger_indexing_amount) return true; + else if (a.ledger_indexing_amount > b.ledger_indexing_amount) return false; + else if (a.index < b.index) return true; + else return false; +} + +inline bool operator>(const legacy_output_index_t &a, const legacy_output_index_t &b) +{ + if (a.ledger_indexing_amount > b.ledger_indexing_amount) return true; + else if (a.ledger_indexing_amount < b.ledger_indexing_amount) return false; + else if (a.index > b.index) return true; + else return false; +} +} // namespace sp + +namespace std +{ +template <> +struct hash +{ + size_t operator()(const sp::legacy_output_index_t x) const + { + const std::size_t h1{hash{}(x.ledger_indexing_amount)}; + const std::size_t h2{hash{}(x.index)}; + return h2 ^ (h1 + 0x9e3779b9 + (h2 << 6) + (h2 >> 2)); // see boost::hash_combine + } +}; +} // namespace std + diff --git a/src/seraphis_impl/enote_finding_context_legacy.cpp b/src/seraphis_impl/enote_finding_context_legacy.cpp index fe3bc880f7..163c01700b 100644 --- a/src/seraphis_impl/enote_finding_context_legacy.cpp +++ b/src/seraphis_impl/enote_finding_context_legacy.cpp @@ -63,7 +63,7 @@ void EnoteFindingContextLegacySimple::view_scan_chunk(const LegacyUnscannedChunk blk.block_index, blk.block_timestamp, tx.transaction_id, - tx.total_enotes_before_tx, + tx.legacy_output_index_per_enote, tx.unlock_time, tx.tx_memo, tx.enotes, @@ -138,7 +138,7 @@ void EnoteFindingContextLegacyMultithreaded::view_scan_chunk( blk.block_index, blk.block_timestamp, tx.transaction_id, - tx.total_enotes_before_tx, + tx.legacy_output_index_per_enote, tx.unlock_time, tx.tx_memo, tx.enotes, diff --git a/src/seraphis_impl/seraphis_serialization.h b/src/seraphis_impl/seraphis_serialization.h index 02a8d7901f..c1e39ddbac 100644 --- a/src/seraphis_impl/seraphis_serialization.h +++ b/src/seraphis_impl/seraphis_serialization.h @@ -160,8 +160,8 @@ END_SERIALIZE() BEGIN_SERIALIZE_OBJECT_FN(GrootleProof) FIELD_F(A) FIELD_F(B) - FIELD_F(f) // @TODO: sizeless f serialization - FIELD_F(X) // @TODO: sizeless X serialization + FIELD_F(f) /// @TODO: sizeless f serialization + FIELD_F(X) /// @TODO: sizeless X serialization FIELD_F(zA) FIELD_F(z) END_SERIALIZE() @@ -193,14 +193,148 @@ BEGIN_SERIALIZE_OBJECT_FN(SpBalanceProofV1, const size_t implied_lr_size = SIZE_ FIELD_F(remainder_blinding_factor) END_SERIALIZE() //-------------------------------------------------------------------------------------------------- +BEGIN_SERIALIZE_OBJECT_FN(LegacyReferenceSetV2, const size_t implied_ring_size = SIZE_MAX) + // if writing and we don't match the passed implied ring size, then fail + if (W && implied_ring_size != SIZE_MAX && v.indices.size() != implied_ring_size) + return false; + + // check that the passed implied ring size is not bigger than the number of remaining bytes + if constexpr (!W) + { + if (implied_ring_size != SIZE_MAX && ar.remaining_bytes() < implied_ring_size) + return false; + } + + // if we don't have an implied size, serialize the actual size + size_t actual_ring_size{v.indices.size()}; + if (implied_ring_size == SIZE_MAX) + VARINT_FIELD_N("ring_size", actual_ring_size) + else + actual_ring_size = implied_ring_size; + + // if the ring size is 0, we can stop here + if (actual_ring_size == 0) + return ar.good(); + + // start compacted indices data array + ar.tag("indices_compressed"); + ar.begin_array(); + + // if storing, construct the number of indices per ledger indexing amount + std::vector> index_quantities_by_amount; + if constexpr (W) + { + index_quantities_by_amount.reserve(actual_ring_size); + index_quantities_by_amount.push_back({v.indices.begin()->ledger_indexing_amount, 0}); + for (const legacy_output_index_t i : v.indices) + { + if (i.ledger_indexing_amount != index_quantities_by_amount.back().first) + index_quantities_by_amount.push_back({i.ledger_indexing_amount, 0}); + ++index_quantities_by_amount.back().second; + } + } + + // serialize the number of unique ledger indexing amounts + size_t num_unique_amounts{index_quantities_by_amount.size() - 1}; + ar.serialize_varint(num_unique_amounts); + ++num_unique_amounts; + + // sanity check num_unique_amounts + if (num_unique_amounts == 0 || num_unique_amounts > actual_ring_size) + return false; + + // for each unique indexing amount... + rct::xmr_amount current_amount{0}; + size_t remaining_indices{actual_ring_size}; + auto writer_index_it{v.indices.begin()}; + for (size_t nth_amount{0}; nth_amount < num_unique_amounts; ++nth_amount) + { + // serialize ledger amount offset (-1 in the data if not the first amount) + rct::xmr_amount amount_offset; + if constexpr (W) + { + amount_offset = index_quantities_by_amount[nth_amount].first - current_amount; + if (nth_amount) + --amount_offset; + } + ar.delimit_array(); + ar.serialize_varint(amount_offset); + if (nth_amount) + ++amount_offset; + + // accumulate the ledger amount, checking for overflow + if (amount_offset > MONEY_SUPPLY - current_amount) + return false; + current_amount += amount_offset; + + // serialize number of indices for this amount (-1 in the data), unless this is the last + // amount in the list, we can imply the number as the number of indices not already serialized + size_t num_indices_for_this_amount; + if (nth_amount == num_unique_amounts - 1) + { + num_indices_for_this_amount = remaining_indices; + } + else // not last amount + { + if constexpr (W) + num_indices_for_this_amount = index_quantities_by_amount[nth_amount].second - 1; + ar.delimit_array(); + ar.serialize_varint(num_indices_for_this_amount); + ++num_indices_for_this_amount; + } + + // sanity check number of indices + if (num_indices_for_this_amount > remaining_indices) + return false; + + // serialize the indices as a list of cumulative offsets (-1 in the data if not first index) + size_t current_index{0}; + for (size_t nth_index{0}; nth_index < num_indices_for_this_amount; ++nth_index) + { + // serialize index offset + size_t index_offset; + if constexpr (W) + { + index_offset = writer_index_it->index; + if (nth_index) + { + index_offset -= current_index + 1; + } + } + ar.delimit_array(); + ar.serialize_varint(index_offset); + if (nth_index) + ++index_offset; + + // update iterators and check for index overflow + --remaining_indices; + if constexpr (W) + ++writer_index_it; + if (index_offset > SIZE_MAX - current_index) + return false; + current_index += index_offset; + + // if loading, insert legacy output index into the set + if constexpr (!W) + v.indices.insert({current_amount, current_index}); + } + } + + // check that we loaded the right number of unique legacy indices + if (v.indices.size() != actual_ring_size) + return false; + + // end compacted indices data array + ar.end_array(); +END_SERIALIZE() +//-------------------------------------------------------------------------------------------------- BEGIN_SERIALIZE_OBJECT_FN(LegacyRingSignatureV4, const size_t implied_ring_size = SIZE_MAX) FIELD_F(clsag_proof, implied_ring_size) - // @TODO: accumlate/decumulate CLSAG ref set offsets - VEC_FIELD_OPT_EXACT_F(reference_set, implied_ring_size) + FIELD_F(reference_set, implied_ring_size) END_SERIALIZE() //-------------------------------------------------------------------------------------------------- BEGIN_SERIALIZE_OBJECT_FN(SpImageProofV1) - // @TODO: sizeless f, X serialization + /// @TODO: sizeless f, X serialization FIELD_F(composition_proof) END_SERIALIZE() //-------------------------------------------------------------------------------------------------- @@ -239,7 +373,7 @@ BEGIN_SERIALIZE_OBJECT_FN(SpTxSquashedV1) FIELD_F(balance_proof, implied_bpp_lr_size) size_t clsag_ring_size = v.legacy_ring_signatures.size() ? - v.legacy_ring_signatures[0].reference_set.size() : 0; + v.legacy_ring_signatures[0].reference_set.indices.size() : 0; VARINT_FIELD(clsag_ring_size) VEC_FIELD_EXACT_F(legacy_ring_signatures, num_legacy_inputs, clsag_ring_size) diff --git a/src/seraphis_main/contextual_enote_record_types.cpp b/src/seraphis_main/contextual_enote_record_types.cpp index 7de583b18a..190e96af90 100644 --- a/src/seraphis_main/contextual_enote_record_types.cpp +++ b/src/seraphis_main/contextual_enote_record_types.cpp @@ -86,41 +86,52 @@ rct::xmr_amount amount_ref(const SpContextualEnoteRecordV1 &record) return record.record.amount; } //------------------------------------------------------------------------------------------------------------------- -const SpEnoteOriginContextV1& origin_context_ref(const ContextualBasicRecordVariant &variant) +std::uint64_t block_index_ref(const ContextualBasicRecordVariant &variant) { - struct visitor final : public tools::variant_static_visitor + struct visitor final : public tools::variant_static_visitor { - using variant_static_visitor::operator(); //for blank overload - const SpEnoteOriginContextV1& operator()(const LegacyContextualBasicEnoteRecordV1 &record) const - { return record.origin_context; } - const SpEnoteOriginContextV1& operator()(const SpContextualBasicEnoteRecordV1 &record) const - { return record.origin_context; } + std::uint64_t operator()(const LegacyContextualBasicEnoteRecordV1 &record) const + { return record.origin_context.block_index; } + std::uint64_t operator()(const SpContextualBasicEnoteRecordV1 &record) const + { return record.origin_context.block_index; } }; return variant.visit(visitor()); } //------------------------------------------------------------------------------------------------------------------- -rct::xmr_amount amount_ref(const ContextualRecordVariant &variant) +const rct::key& transaction_id_ref(const ContextualBasicRecordVariant &variant) { - struct visitor final : public tools::variant_static_visitor + struct visitor final : public tools::variant_static_visitor { - using variant_static_visitor::operator(); //for blank overload - rct::xmr_amount operator()(const LegacyContextualEnoteRecordV1 &record) const { return amount_ref(record); } - rct::xmr_amount operator()(const SpContextualEnoteRecordV1 &record) const { return amount_ref(record); } + const rct::key& operator()(const LegacyContextualBasicEnoteRecordV1 &record) const + { return record.origin_context.transaction_id; } + const rct::key& operator()(const SpContextualBasicEnoteRecordV1 &record) const + { return record.origin_context.transaction_id; } + }; + + return variant.visit(visitor()); +} +//------------------------------------------------------------------------------------------------------------------- +SpEnoteOriginStatus origin_status_ref(const ContextualBasicRecordVariant &variant) +{ + struct visitor final : public tools::variant_static_visitor + { + SpEnoteOriginStatus operator()(const LegacyContextualBasicEnoteRecordV1 &record) const + { return record.origin_context.origin_status; } + SpEnoteOriginStatus operator()(const SpContextualBasicEnoteRecordV1 &record) const + { return record.origin_context.origin_status; } }; return variant.visit(visitor()); } //------------------------------------------------------------------------------------------------------------------- -const SpEnoteOriginContextV1& origin_context_ref(const ContextualRecordVariant &variant) +rct::xmr_amount amount_ref(const ContextualRecordVariant &variant) { - struct visitor final : public tools::variant_static_visitor + struct visitor final : public tools::variant_static_visitor { using variant_static_visitor::operator(); //for blank overload - const SpEnoteOriginContextV1& operator()(const LegacyContextualEnoteRecordV1 &record) const - { return record.origin_context; } - const SpEnoteOriginContextV1& operator()(const SpContextualEnoteRecordV1 &record) const - { return record.origin_context; } + rct::xmr_amount operator()(const LegacyContextualEnoteRecordV1 &record) const { return amount_ref(record); } + rct::xmr_amount operator()(const SpContextualEnoteRecordV1 &record) const { return amount_ref(record); } }; return variant.visit(visitor()); @@ -140,6 +151,42 @@ const SpEnoteSpentContextV1& spent_context_ref(const ContextualRecordVariant &va return variant.visit(visitor()); } //------------------------------------------------------------------------------------------------------------------- +bool is_older_than(const LegacyEnoteOriginContext &context, const LegacyEnoteOriginContext &other_context) +{ + // 1. origin status (higher statuses are assumed to be 'older') + if (context.origin_status > other_context.origin_status) + return true; + if (context.origin_status < other_context.origin_status) + return false; + + // 2. block index + if (context.block_index < other_context.block_index) + return true; + if (context.block_index > other_context.block_index) + return false; + + // note: don't assess the tx output index + + // 3. assert that the ledger indexing amounts are the same (else comparing age is undecidable) + CHECK_AND_ASSERT_THROW_MES(context.legacy_enote_ledger_index.ledger_indexing_amount + == other_context.legacy_enote_ledger_index.ledger_indexing_amount, + "is_older_than: legacy enote origin contexts have different ledger indexing amounts"); + + // 4. enote ledger index + if (context.legacy_enote_ledger_index < other_context.legacy_enote_ledger_index) + return true; + if (context.legacy_enote_ledger_index > other_context.legacy_enote_ledger_index) + return false; + + // 5. block timestamp + if (context.block_timestamp < other_context.block_timestamp) + return true; + if (context.block_timestamp > other_context.block_timestamp) + return false; + + return false; +} +//------------------------------------------------------------------------------------------------------------------- bool is_older_than(const SpEnoteOriginContextV1 &context, const SpEnoteOriginContextV1 &other_context) { // 1. origin status (higher statuses are assumed to be 'older') diff --git a/src/seraphis_main/contextual_enote_record_types.h b/src/seraphis_main/contextual_enote_record_types.h index 47a12a7393..f392d6441c 100644 --- a/src/seraphis_main/contextual_enote_record_types.h +++ b/src/seraphis_main/contextual_enote_record_types.h @@ -37,6 +37,7 @@ #include "ringct/rctOps.h" #include "ringct/rctTypes.h" #include "seraphis_core/jamtis_support_types.h" +#include "seraphis_core/legacy_output_index.h" #include "seraphis_core/sp_core_types.h" #include "seraphis_core/tx_extra.h" #include "tx_component_types.h" @@ -86,9 +87,32 @@ enum class SpEnoteSpentStatus : unsigned char SPENT_ONCHAIN }; +//// +// LegacyEnoteOriginContext +// - info related to the transaction where a legacy enote was found +// - note that an enote may originate off-chain in a partial tx where the tx id is unknown +/// +struct LegacyEnoteOriginContext final +{ + /// block index of tx (-1 if index is unknown) + std::uint64_t block_index{static_cast(-1)}; + /// timestamp of tx's block (-1 if timestamp is unknown) + std::uint64_t block_timestamp{static_cast(-1)}; + /// tx id of the tx (0 if tx is unknown) + rct::key transaction_id{rct::zero()}; + /// index of the enote in the tx's output set (-1 if index is unknown) + std::uint64_t enote_tx_index{static_cast(-1)}; + /// ledger index of the enote (-1 for indexing amount if index is unknown) + legacy_output_index_t legacy_enote_ledger_index{static_cast(-1), 0}; + /// origin status (off-chain by default) + SpEnoteOriginStatus origin_status{SpEnoteOriginStatus::OFFCHAIN}; + + /// associated memo field (none by default) + TxExtra memo{}; +}; //// // SpEnoteOriginContextV1 -// - info related to the transaction where an enote was found +// - info related to the transaction where a Seraphis enote was found // - note that an enote may originate off-chain in a partial tx where the tx id is unknown /// struct SpEnoteOriginContextV1 final @@ -140,7 +164,7 @@ struct LegacyContextualBasicEnoteRecordV1 final /// basic info about the enote LegacyBasicEnoteRecord record; /// info about where the enote was found - SpEnoteOriginContextV1 origin_context; + LegacyEnoteOriginContext origin_context; }; //// @@ -153,7 +177,7 @@ struct LegacyContextualIntermediateEnoteRecordV1 final /// intermediate info about the enote LegacyIntermediateEnoteRecord record; /// info about where the enote was found - SpEnoteOriginContextV1 origin_context; + LegacyEnoteOriginContext origin_context; }; /// get the record's onetime address @@ -170,7 +194,7 @@ struct LegacyContextualEnoteRecordV1 final /// info about the enote LegacyEnoteRecord record; /// info about where the enote was found - SpEnoteOriginContextV1 origin_context; + LegacyEnoteOriginContext origin_context; /// info about where the enote was spent SpEnoteSpentContextV1 spent_context; }; @@ -241,22 +265,24 @@ rct::xmr_amount amount_ref(const SpContextualEnoteRecordV1 &record); // ContextualBasicRecordVariant // - variant of all contextual basic enote record types // -// origin_context_ref(): get the record's origin context +// block_index_ref(): get the record's block index +// transaction_id_ref(): get the record's transaction id +// origin_status_ref(): get the record's origin status /// using ContextualBasicRecordVariant = tools::variant; -const SpEnoteOriginContextV1& origin_context_ref(const ContextualBasicRecordVariant &variant); +std::uint64_t block_index_ref(const ContextualBasicRecordVariant &variant); +const rct::key& transaction_id_ref(const ContextualBasicRecordVariant &variant); +SpEnoteOriginStatus origin_status_ref(const ContextualBasicRecordVariant &variant); //// // ContextualRecordVariant // - variant of all contextual full enote record types // // amount_ref(): get the record's amount -// origin_context_ref(): get the record's origin context // spent_context_ref(): get the record's spent context /// using ContextualRecordVariant = tools::variant; rct::xmr_amount amount_ref(const ContextualRecordVariant &variant); -const SpEnoteOriginContextV1& origin_context_ref(const ContextualRecordVariant &variant); const SpEnoteSpentContextV1& spent_context_ref(const ContextualRecordVariant &variant); //// @@ -278,6 +304,8 @@ struct SpContextualKeyImageSetV1 final //////////////////////////////////////////////////////////////////////////////////////////////////////////// /// check if a context is older than another (returns false if apparently the same age, or younger) +/// throws if undecidable (i.e. legacy origin contexts have same block, different ledger indexing amount) +bool is_older_than(const LegacyEnoteOriginContext &context, const LegacyEnoteOriginContext &other_context); bool is_older_than(const SpEnoteOriginContextV1 &context, const SpEnoteOriginContextV1 &other_context); bool is_older_than(const SpEnoteSpentContextV1 &context, const SpEnoteSpentContextV1 &other_context); /// check if records have onetime address equivalence diff --git a/src/seraphis_main/contextual_enote_record_utils.cpp b/src/seraphis_main/contextual_enote_record_utils.cpp index b2be59dc0b..e4e5f0d544 100644 --- a/src/seraphis_main/contextual_enote_record_utils.cpp +++ b/src/seraphis_main/contextual_enote_record_utils.cpp @@ -191,7 +191,7 @@ boost::multiprecision::uint128_t total_amount(const std::vector &contextual_records, - std::unordered_map &enote_ledger_mappings_out) + std::unordered_map &enote_ledger_mappings_out) { enote_ledger_mappings_out.clear(); enote_ledger_mappings_out.reserve(contextual_records.size()); @@ -204,7 +204,7 @@ bool try_get_membership_proof_real_reference_mappings(const std::vector &contextual_records, - std::unordered_map &enote_ledger_mappings_out); + std::unordered_map &enote_ledger_mappings_out); bool try_get_membership_proof_real_reference_mappings(const std::vector &contextual_records, std::unordered_map &enote_ledger_mappings_out); /** @@ -122,7 +123,11 @@ bool try_get_membership_proof_real_reference_mappings(const std::vector legacy_output_index_per_enote; std::vector enotes; std::vector legacy_key_images; }; diff --git a/src/seraphis_main/scan_balance_recovery_utils.cpp b/src/seraphis_main/scan_balance_recovery_utils.cpp index 46b6e19d22..460335d008 100644 --- a/src/seraphis_main/scan_balance_recovery_utils.cpp +++ b/src/seraphis_main/scan_balance_recovery_utils.cpp @@ -72,8 +72,8 @@ static bool try_view_scan_legacy_enote_v1(const rct::key &legacy_base_spend_pubk const std::uint64_t block_index, const std::uint64_t block_timestamp, const rct::key &transaction_id, - const std::uint64_t total_enotes_before_tx, - const std::uint64_t enote_index, + const sp::legacy_output_index_t &legacy_ledger_enote_index, + const std::uint64_t enote_tx_index, const std::uint64_t unlock_time, const TxExtra &tx_memo, const LegacyEnoteVariant &legacy_enote, @@ -88,7 +88,7 @@ static bool try_view_scan_legacy_enote_v1(const rct::key &legacy_base_spend_pubk { if (!try_get_legacy_basic_enote_record(legacy_enote, rct::pk2rct(legacy_enote_ephemeral_pubkey), - enote_index, + enote_tx_index, unlock_time, DH_derivation, legacy_base_spend_pubkey, @@ -100,14 +100,14 @@ static bool try_view_scan_legacy_enote_v1(const rct::key &legacy_base_spend_pubk // 2. set the origin context contextual_record_out.origin_context = - SpEnoteOriginContextV1{ - .block_index = block_index, - .block_timestamp = block_timestamp, - .transaction_id = transaction_id, - .enote_tx_index = enote_index, - .enote_ledger_index = total_enotes_before_tx + enote_index, - .origin_status = origin_status, - .memo = tx_memo + LegacyEnoteOriginContext{ + .block_index = block_index, + .block_timestamp = block_timestamp, + .transaction_id = transaction_id, + .enote_tx_index = enote_tx_index, + .legacy_enote_ledger_index = legacy_ledger_enote_index, + .origin_status = origin_status, + .memo = tx_memo }; return true; @@ -115,7 +115,7 @@ static bool try_view_scan_legacy_enote_v1(const rct::key &legacy_base_spend_pubk //------------------------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------------------------- static void update_with_new_intermediate_record_legacy(const LegacyIntermediateEnoteRecord &new_enote_record, - const SpEnoteOriginContextV1 &new_record_origin_context, + const LegacyEnoteOriginContext &new_record_origin_context, std::unordered_map &found_enote_records_inout) { // 1. add new intermediate legacy record to found enotes (or refresh if already there) @@ -133,7 +133,7 @@ static void update_with_new_intermediate_record_legacy(const LegacyIntermediateE //------------------------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------------------------- static void update_with_new_record_legacy(const LegacyEnoteRecord &new_enote_record, - const SpEnoteOriginContextV1 &new_record_origin_context, + const LegacyEnoteOriginContext &new_record_origin_context, const std::list &chunk_contextual_key_images, std::unordered_map &found_enote_records_inout, std::unordered_map &found_spent_key_images_inout) @@ -308,18 +308,19 @@ static std::unordered_set process_chunk_sp_selfsend_pass( for (const ContextualBasicRecordVariant &contextual_basic_record : chunk_basic_records_per_tx.at(tx_with_spent_enotes)) { - if (!contextual_basic_record.is_type()) + const auto * const sp_contextual_basic_record{ + contextual_basic_record.try_unwrap() + }; + if (sp_contextual_basic_record == nullptr) continue; try { // a. check if the enote is owned by attempting to convert it to a full enote record (selfsend conversion) if (!try_get_enote_record_v1_selfsend( - contextual_basic_record.unwrap().record.enote, - contextual_basic_record.unwrap().record - .enote_ephemeral_pubkey, - contextual_basic_record.unwrap().record - .input_context, + sp_contextual_basic_record->record.enote, + sp_contextual_basic_record->record.enote_ephemeral_pubkey, + sp_contextual_basic_record->record.input_context, jamtis_spend_pubkey, k_view_balance, xk_find_received, @@ -332,7 +333,7 @@ static std::unordered_set process_chunk_sp_selfsend_pass( // - this will also check if the enote was spent in this chunk, and update 'txs_have_spent_enotes' // accordingly update_with_new_record_sp(new_enote_record, - origin_context_ref(contextual_basic_record), + sp_contextual_basic_record->origin_context, chunk_contextual_key_images, found_enote_records_inout, found_spent_sp_key_images_inout, @@ -343,7 +344,7 @@ static std::unordered_set process_chunk_sp_selfsend_pass( // txs with selfsend outputs, but during seraphis scanning it isn't guaranteed that we will be able // to check if legacy key images attached to selfsend owned enotes are associated with owned legacy // enotes; therefore we cache those legacy key images so they can be handled outside this scan process - collect_legacy_key_images_from_tx(origin_context_ref(contextual_basic_record).transaction_id, + collect_legacy_key_images_from_tx(sp_contextual_basic_record->origin_context.transaction_id, chunk_contextual_key_images, legacy_key_images_in_sp_selfspends_inout); } catch (...) {} @@ -384,7 +385,7 @@ bool try_find_legacy_enotes_in_tx(const rct::key &legacy_base_spend_pubkey, const std::uint64_t block_index, const std::uint64_t block_timestamp, const rct::key &transaction_id, - const std::uint64_t total_enotes_before_tx, + const std::vector &legacy_output_index_per_enote, const std::uint64_t unlock_time, const TxExtra &tx_memo, const std::vector &enotes_in_tx, @@ -394,6 +395,9 @@ bool try_find_legacy_enotes_in_tx(const rct::key &legacy_base_spend_pubkey, { basic_records_in_tx_out.clear(); + if (legacy_output_index_per_enote.size() != enotes_in_tx.size()) + return false; + // 1. extract enote ephemeral pubkeys from the memo rct::key_keyV_variant legacy_main_enote_ephemeral_pubkeys = rct::key{}; std::vector legacy_additional_enote_ephemeral_pubkeys; @@ -423,7 +427,7 @@ bool try_find_legacy_enotes_in_tx(const rct::key &legacy_base_spend_pubkey, block_index, block_timestamp, transaction_id, - total_enotes_before_tx, + legacy_output_index_per_enote[enote_index], enote_index, unlock_time, tx_memo, @@ -485,7 +489,7 @@ bool try_find_legacy_enotes_in_tx(const rct::key &legacy_base_spend_pubkey, block_index, block_timestamp, transaction_id, - total_enotes_before_tx, + legacy_output_index_per_enote[enote_index], enote_index, unlock_time, tx_memo, @@ -651,14 +655,17 @@ void process_chunk_intermediate_legacy(const rct::key &legacy_base_spend_pubkey, { for (const ContextualBasicRecordVariant &contextual_basic_record : tx_basic_records.second) { - if (!contextual_basic_record.is_type()) + const auto * const legacy_contextual_basic_record{ + contextual_basic_record.try_unwrap() + }; + if (legacy_contextual_basic_record == nullptr) continue; try { // a. check if we own the enote by attempting to convert it to an intermediate enote record if (!try_get_legacy_intermediate_enote_record( - contextual_basic_record.unwrap().record, + legacy_contextual_basic_record->record, legacy_base_spend_pubkey, legacy_view_privkey, hwdev, @@ -667,7 +674,7 @@ void process_chunk_intermediate_legacy(const rct::key &legacy_base_spend_pubkey, // b. we found an owned enote, so handle it update_with_new_intermediate_record_legacy(new_enote_record, - origin_context_ref(contextual_basic_record), + legacy_contextual_basic_record->origin_context, found_enote_records_out); } catch (...) {} } @@ -716,14 +723,17 @@ void process_chunk_full_legacy(const rct::key &legacy_base_spend_pubkey, { for (const ContextualBasicRecordVariant &contextual_basic_record : tx_basic_records.second) { - if (!contextual_basic_record.is_type()) + const auto * const legacy_contextual_basic_record{ + contextual_basic_record.try_unwrap() + }; + if (legacy_contextual_basic_record == nullptr) continue; try { // a. check if we own the enote by attempting to convert it to a full enote record if (!try_get_legacy_enote_record( - contextual_basic_record.unwrap().record, + legacy_contextual_basic_record->record, legacy_base_spend_pubkey, legacy_spend_privkey, legacy_view_privkey, @@ -733,7 +743,7 @@ void process_chunk_full_legacy(const rct::key &legacy_base_spend_pubkey, // b. we found an owned enote, so handle it update_with_new_record_legacy(new_enote_record, - origin_context_ref(contextual_basic_record), + legacy_contextual_basic_record->origin_context, chunk_contextual_key_images, found_enote_records_out, found_spent_key_images_out); @@ -759,14 +769,17 @@ void process_chunk_intermediate_sp(const rct::key &jamtis_spend_pubkey, { for (const ContextualBasicRecordVariant &contextual_basic_record : tx_basic_records.second) { - if (!contextual_basic_record.is_type()) + const auto * const sp_contextual_basic_record{ + contextual_basic_record.try_unwrap() + }; + if (sp_contextual_basic_record == nullptr) continue; try { // a. check if we own the enote by attempting to convert it to an intermediate enote record if (!try_get_intermediate_enote_record_v1( - contextual_basic_record.unwrap().record, + sp_contextual_basic_record->record, jamtis_spend_pubkey, xk_unlock_amounts, xk_find_received, @@ -777,7 +790,7 @@ void process_chunk_intermediate_sp(const rct::key &jamtis_spend_pubkey, // b. we found an owned enote, so handle it update_with_new_intermediate_record_sp(new_enote_record, - origin_context_ref(contextual_basic_record), + sp_contextual_basic_record->origin_context, found_enote_records_out); } catch (...) {} } @@ -848,14 +861,17 @@ void process_chunk_full_sp(const rct::key &jamtis_spend_pubkey, { for (const ContextualBasicRecordVariant &contextual_basic_record : tx_basic_records.second) { - if (!contextual_basic_record.is_type()) + const auto * const sp_contextual_basic_record{ + contextual_basic_record.try_unwrap() + }; + if (sp_contextual_basic_record == nullptr) continue; try { // a. check if we own the enote by attempting to convert it to a full enote record if (!try_get_enote_record_v1_plain( - contextual_basic_record.unwrap().record, + sp_contextual_basic_record->record, jamtis_spend_pubkey, k_view_balance, xk_unlock_amounts, @@ -869,7 +885,7 @@ void process_chunk_full_sp(const rct::key &jamtis_spend_pubkey, // - this will also check if the enote was spent in this chunk, and update 'txs_have_spent_enotes' // accordingly update_with_new_record_sp(new_enote_record, - origin_context_ref(contextual_basic_record), + sp_contextual_basic_record->origin_context, chunk_contextual_key_images, found_enote_records_out, found_spent_sp_key_images_out, diff --git a/src/seraphis_main/scan_balance_recovery_utils.h b/src/seraphis_main/scan_balance_recovery_utils.h index 709b1ae957..eb38ec842c 100644 --- a/src/seraphis_main/scan_balance_recovery_utils.h +++ b/src/seraphis_main/scan_balance_recovery_utils.h @@ -67,7 +67,7 @@ namespace scanning * param: block_index - * param: block_timestamp - * param: transaction_id - -* param: total_enotes_before_tx - number of legacy enotes ordered before this tx (set to '0' if tx is non-ledger) +* param: legacy_output_index_per_enote - legacy output indices for each enote in the tx * param: unlock_time - * param: tx_memo - * param: enotes_in_tx - @@ -81,7 +81,7 @@ bool try_find_legacy_enotes_in_tx(const rct::key &legacy_base_spend_pubkey, const std::uint64_t block_index, const std::uint64_t block_timestamp, const rct::key &transaction_id, - const std::uint64_t total_enotes_before_tx, + const std::vector &legacy_output_index_per_enote, const std::uint64_t unlock_time, const TxExtra &tx_memo, const std::vector &enotes_in_tx, diff --git a/src/seraphis_main/scan_misc_utils.cpp b/src/seraphis_main/scan_misc_utils.cpp index 29c8b800b6..eea50614c2 100644 --- a/src/seraphis_main/scan_misc_utils.cpp +++ b/src/seraphis_main/scan_misc_utils.cpp @@ -75,19 +75,19 @@ void check_chunk_data_semantics(const ChunkData &chunk_data, { for (const ContextualBasicRecordVariant &contextual_basic_record : tx_basic_records.second) { - CHECK_AND_ASSERT_THROW_MES(origin_context_ref(contextual_basic_record).origin_status == + CHECK_AND_ASSERT_THROW_MES(origin_status_ref(contextual_basic_record) == expected_origin_status, "scan chunk data semantics check: contextual basic record doesn't have expected origin status."); - CHECK_AND_ASSERT_THROW_MES(origin_context_ref(contextual_basic_record).transaction_id == + CHECK_AND_ASSERT_THROW_MES(transaction_id_ref(contextual_basic_record) == tx_basic_records.first, "scan chunk data semantics check: contextual basic record doesn't have origin tx id matching mapped id."); - CHECK_AND_ASSERT_THROW_MES(origin_context_ref(contextual_basic_record).block_index == - origin_context_ref(*tx_basic_records.second.begin()).block_index, + CHECK_AND_ASSERT_THROW_MES(block_index_ref(contextual_basic_record) == + block_index_ref(*tx_basic_records.second.begin()), "scan chunk data semantics check: contextual record tx index doesn't match other records in tx."); CHECK_AND_ASSERT_THROW_MES( - origin_context_ref(contextual_basic_record).block_index >= allowed_lowest_index && - origin_context_ref(contextual_basic_record).block_index <= allowed_highest_index, + block_index_ref(contextual_basic_record) >= allowed_lowest_index && + block_index_ref(contextual_basic_record) <= allowed_highest_index, "scan chunk data semantics check: contextual record block index is out of the expected range."); } } diff --git a/src/seraphis_main/tx_builder_types_legacy.h b/src/seraphis_main/tx_builder_types_legacy.h index ba666bac2e..50f0cc41f2 100644 --- a/src/seraphis_main/tx_builder_types_legacy.h +++ b/src/seraphis_main/tx_builder_types_legacy.h @@ -80,10 +80,10 @@ struct LegacyRingSignaturePrepV1 final /// tx proposal prefix (message to sign in the proof) rct::key tx_proposal_prefix; /// ledger indices of legacy enotes to be referenced by the proof - std::vector reference_set; + LegacyReferenceSetV2 reference_set; /// the referenced enotes ({Ko, C}((legacy)) representation) rct::ctkeyV referenced_enotes; - /// the index of the real enote being referenced within the reference set + /// the index of the real enote being referenced within the referenced enotes vector std::uint64_t real_reference_index; /// enote image of the real reference (useful for sorting) LegacyEnoteImageV2 reference_image; diff --git a/src/seraphis_main/tx_builder_types_multisig.cpp b/src/seraphis_main/tx_builder_types_multisig.cpp index 70cd3e5de6..fb9ff4c969 100644 --- a/src/seraphis_main/tx_builder_types_multisig.cpp +++ b/src/seraphis_main/tx_builder_types_multisig.cpp @@ -216,7 +216,7 @@ bool matches_with(const LegacyMultisigInputProposalV1 &multisig_input_proposal, return false; // references line up 1:1 - if (multisig_input_proposal.reference_set.size() != proof_proposal.ring_members.size()) + if (multisig_input_proposal.reference_set.indices.size() != proof_proposal.ring_members.size()) return false; return true; diff --git a/src/seraphis_main/tx_builder_types_multisig.h b/src/seraphis_main/tx_builder_types_multisig.h index e093ced8be..ccb7b1b1b3 100644 --- a/src/seraphis_main/tx_builder_types_multisig.h +++ b/src/seraphis_main/tx_builder_types_multisig.h @@ -70,7 +70,7 @@ namespace sp struct LegacyMultisigRingSignaturePrepV1 final { /// ledger indices of legacy enotes referenced by the proof - std::vector reference_set; + LegacyReferenceSetV2 reference_set; /// the referenced enotes ({Ko, C}((legacy)) representation) rct::ctkeyV referenced_enotes; /// the index of the real enote being referenced within the reference set @@ -100,7 +100,7 @@ struct LegacyMultisigInputProposalV1 final crypto::secret_key commitment_mask; /// cached legacy enote indices for a legacy ring signature (should include a reference to this input proposal's enote) - std::vector reference_set; + LegacyReferenceSetV2 reference_set; }; //// diff --git a/src/seraphis_main/tx_builders_legacy_inputs.cpp b/src/seraphis_main/tx_builders_legacy_inputs.cpp index 8efdd214c2..6bebcc2498 100644 --- a/src/seraphis_main/tx_builders_legacy_inputs.cpp +++ b/src/seraphis_main/tx_builders_legacy_inputs.cpp @@ -155,22 +155,22 @@ void make_v1_legacy_input_proposal_v1(const LegacyEnoteRecord &enote_record, } //------------------------------------------------------------------------------------------------------------------- void make_tx_legacy_ring_signature_message_v1(const rct::key &tx_proposal_message, - const std::vector &reference_set_indices, + const LegacyReferenceSetV2 &reference_set, rct::key &message_out) { // m = H_32(tx proposal message, {reference set indices}) SpFSTranscript transcript{ config::HASH_KEY_LEGACY_RING_SIGNATURES_MESSAGE_V1, - 32 + reference_set_indices.size() * 8 + 32 + legacy_ref_set_v2_size_bytes(reference_set) }; transcript.append("tx_proposal_message", tx_proposal_message); - transcript.append("reference_set_indices", reference_set_indices); + transcript.append("reference_set", reference_set); sp_hash_to_32(transcript.data(), transcript.size(), message_out.bytes); } //------------------------------------------------------------------------------------------------------------------- void make_v4_legacy_ring_signature(const rct::key &message, - std::vector reference_set, + LegacyReferenceSetV2 reference_set, const rct::ctkeyV &referenced_enotes, const std::uint64_t real_reference_index, const rct::key &masked_commitment, @@ -185,9 +185,7 @@ void make_v4_legacy_ring_signature(const rct::key &message, /// checks // 1. reference sets - CHECK_AND_ASSERT_THROW_MES(tools::is_sorted_and_unique(reference_set), - "make v4 legacy ring signature: reference set indices are not sorted and unique."); - CHECK_AND_ASSERT_THROW_MES(reference_set.size() == referenced_enotes.size(), + CHECK_AND_ASSERT_THROW_MES(reference_set.indices.size() == referenced_enotes.size(), "make v4 legacy ring signature: reference set indices don't match referenced enotes."); CHECK_AND_ASSERT_THROW_MES(real_reference_index < referenced_enotes.size(), "make v4 legacy ring signature: real reference index is outside range of referenced enotes."); @@ -310,9 +308,7 @@ void check_v1_legacy_input_semantics_v1(const LegacyInputV1 &input) "legacy input semantics (v1): could not reproduce masked commitment (pseudo-output commitment)."); // 2. ring signature reference indices are sorted and unique and match with the cached reference enotes - CHECK_AND_ASSERT_THROW_MES(tools::is_sorted_and_unique(input.ring_signature.reference_set), - "legacy input semantics (v1): reference set indices are not sorted and unique."); - CHECK_AND_ASSERT_THROW_MES(input.ring_signature.reference_set.size() == input.ring_members.size(), + CHECK_AND_ASSERT_THROW_MES(input.ring_signature.reference_set.indices.size() == input.ring_members.size(), "legacy input semantics (v1): reference set indices don't match referenced enotes."); // 3. ring signature message diff --git a/src/seraphis_main/tx_builders_legacy_inputs.h b/src/seraphis_main/tx_builders_legacy_inputs.h index 06cf215536..ec77cfb3f6 100644 --- a/src/seraphis_main/tx_builders_legacy_inputs.h +++ b/src/seraphis_main/tx_builders_legacy_inputs.h @@ -87,10 +87,10 @@ void make_v1_legacy_input_proposal_v1(const LegacyEnoteRecord &enote_record, * outparam: message_out - the message to sign in a legacy ring signature */ void make_tx_legacy_ring_signature_message_v1(const rct::key &tx_proposal_message, - const std::vector &reference_set_indices, + const LegacyReferenceSetV2 &reference_set_indices, rct::key &message_out); /** -* brief: make_v4_legacy_ring_signature - make a legacy v3 ring signature +* brief: make_v4_legacy_ring_signature - make a legacy v4 ring signature * param: message - * param: reference_set - * param: referenced_enotes - @@ -103,7 +103,7 @@ void make_tx_legacy_ring_signature_message_v1(const rct::key &tx_proposal_messag * outparam: ring_signature_out - */ void make_v4_legacy_ring_signature(const rct::key &message, - std::vector reference_set, + LegacyReferenceSetV2 reference_set, const rct::ctkeyV &referenced_enotes, const std::uint64_t real_reference_index, const rct::key &masked_commitment, diff --git a/src/seraphis_main/tx_builders_mixed.cpp b/src/seraphis_main/tx_builders_mixed.cpp index b4d6d5c9bd..7b0139fb4c 100644 --- a/src/seraphis_main/tx_builders_mixed.cpp +++ b/src/seraphis_main/tx_builders_mixed.cpp @@ -81,7 +81,7 @@ class TxValidationContextSimple final : public TxValidationContext public: //constructors TxValidationContextSimple(const SemanticConfigSpRefSetV1 &sp_ref_set_config, - const std::unordered_map &legacy_reference_set_proof_elements, + const std::unordered_map &legacy_reference_set_proof_elements, const std::unordered_map &sp_reference_set_proof_elements) : m_sp_ref_set_config{sp_ref_set_config}, m_legacy_reference_set_proof_elements{legacy_reference_set_proof_elements}, @@ -115,19 +115,19 @@ class TxValidationContextSimple final : public TxValidationContext } /** * brief: get_reference_set_proof_elements_v1 - gets legacy {KI, C} pairs stored in the validation context - * param: indices - + * - note: should only return elements that are valid to reference in a tx (e.g. locked elements are invalid) + * param: indices - (output amount, global output index) reference pairs to existing on-chain legacy enotes * outparam: proof_elements_out - {KI, C} */ - void get_reference_set_proof_elements_v1(const std::vector &indices, + virtual void get_reference_set_proof_elements_v1(const std::set &indices, rct::ctkeyV &proof_elements_out) const override { proof_elements_out.clear(); proof_elements_out.reserve(indices.size()); - - for (const std::uint64_t index : indices) + for (const legacy_output_index_t &index : indices) { - if (m_legacy_reference_set_proof_elements.find(index) != m_legacy_reference_set_proof_elements.end()) - proof_elements_out.emplace_back(m_legacy_reference_set_proof_elements.at(index)); + if (m_legacy_reference_set_proof_elements.count(index)) + proof_elements_out.push_back(m_legacy_reference_set_proof_elements.at(index)); else proof_elements_out.emplace_back(rct::ctkey{}); } @@ -155,7 +155,7 @@ class TxValidationContextSimple final : public TxValidationContext //member variables private: const SemanticConfigSpRefSetV1 m_sp_ref_set_config; - const std::unordered_map &m_legacy_reference_set_proof_elements; + const std::unordered_map &m_legacy_reference_set_proof_elements; const std::unordered_map &m_sp_reference_set_proof_elements; }; @@ -503,7 +503,7 @@ static void check_tx_proposal_semantics_output_proposals_v1(const std::vector &legacy_ring_signatures, const std::vector &legacy_ring_signature_rings, - std::unordered_map &legacy_reference_set_proof_elements_out) + std::unordered_map &legacy_reference_set_proof_elements_out) { // map legacy ring members onto their on-chain legacy enote indices CHECK_AND_ASSERT_THROW_MES(legacy_ring_signatures.size() == legacy_ring_signature_rings.size(), @@ -512,15 +512,17 @@ static void collect_legacy_ring_signature_ring_members(const std::vector legacy_reference_set_proof_elements; + std::unordered_map legacy_reference_set_proof_elements; collect_legacy_ring_signature_ring_members(partial_tx.legacy_ring_signatures, partial_tx.legacy_ring_signature_rings, diff --git a/src/seraphis_main/tx_builders_multisig.cpp b/src/seraphis_main/tx_builders_multisig.cpp index e81f9a399f..3c464350bc 100644 --- a/src/seraphis_main/tx_builders_multisig.cpp +++ b/src/seraphis_main/tx_builders_multisig.cpp @@ -285,20 +285,27 @@ static void replace_legacy_input_proposal_destinations_for_tx_simulation_v1( { for (LegacyRingSignaturePrepV1 &prep_to_repair : legacy_ring_signature_preps_out) { - // a. see if the reference prep's real reference is a decoy in this prep's reference set - auto ref_set_it = - std::find(prep_to_repair.reference_set.begin(), - prep_to_repair.reference_set.end(), - reference_prep.reference_set.at(reference_prep.real_reference_index)); - - // b. if not, skip it - if (ref_set_it == prep_to_repair.reference_set.end()) + // a. sanity check real_reference_index + CHECK_AND_ASSERT_THROW_MES( + reference_prep.real_reference_index < reference_prep.reference_set.indices.size(), + "real reference index is too large for given reference set prep"); + + // b. get legacy output index at "real reference index" into reference set indices + const legacy_output_index_t real_reference_index_global{ + *std::next(reference_prep.reference_set.indices.cbegin(), reference_prep.real_reference_index) + }; + + // c. see if the reference prep's real reference is a decoy in this prep's reference set, if not skip it + const auto ref_set_it = prep_to_repair.reference_set.indices.find(real_reference_index_global); + + // d. if not, skip it + if (ref_set_it == prep_to_repair.reference_set.indices.cend()) continue; - // c. otherwise, update the decoy's onetime address + // e. otherwise, update the decoy's onetime address prep_to_repair .referenced_enotes - .at(std::distance(prep_to_repair.reference_set.begin(), ref_set_it)) + .at(std::distance(prep_to_repair.reference_set.indices.begin(), ref_set_it)) .dest = reference_prep .referenced_enotes @@ -510,7 +517,7 @@ static void collect_sp_composition_proof_privkeys_for_multisig(const std::vector //------------------------------------------------------------------------------------------------------------------- static bool try_make_v1_legacy_input_v1(const rct::key &tx_proposal_prefix, const LegacyInputProposalV1 &input_proposal, - std::vector reference_set, + LegacyReferenceSetV2 reference_set, rct::ctkeyV referenced_enotes, const rct::key &masked_commitment, const std::vector &input_proof_partial_sigs, @@ -626,7 +633,7 @@ static bool try_make_legacy_inputs_for_multisig_v1(const rct::key &tx_proposal_p // - map ring signature messages to onetime addresses // - map legacy reference sets to onetime addresses std::unordered_map legacy_proof_contexts; //[ proof key : proof message ] - std::unordered_map> mapped_reference_sets; + std::unordered_map mapped_reference_sets; rct::key message_temp; for (const LegacyMultisigInputProposalV1 &legacy_multisig_input_proposal : legacy_multisig_input_proposals) @@ -768,13 +775,6 @@ void check_v1_legacy_multisig_input_proposal_semantics_v1(const LegacyMultisigIn "semantics check legacy multisig input proposal v1: bad address mask (zero)."); CHECK_AND_ASSERT_THROW_MES(sc_check(to_bytes(multisig_input_proposal.commitment_mask)) == 0, "semantics check legacy multisig input proposal v1: bad address mask (not canonical)."); - CHECK_AND_ASSERT_THROW_MES(std::find(multisig_input_proposal.reference_set.begin(), - multisig_input_proposal.reference_set.end(), - multisig_input_proposal.tx_output_index) != - multisig_input_proposal.reference_set.end(), - "semantics check legacy multisig input proposal v1: referenced enote index is not in the reference set."); - CHECK_AND_ASSERT_THROW_MES(tools::is_sorted_and_unique(multisig_input_proposal.reference_set), - "semantics check legacy multisig input proposal v1: reference set indices are not sorted and unique."); } //------------------------------------------------------------------------------------------------------------------- void make_v1_legacy_multisig_input_proposal_v1(const LegacyEnoteVariant &enote, @@ -783,7 +783,7 @@ void make_v1_legacy_multisig_input_proposal_v1(const LegacyEnoteVariant &enote, const std::uint64_t tx_output_index, const std::uint64_t unlock_time, const crypto::secret_key &commitment_mask, - std::vector reference_set, + LegacyReferenceSetV2 reference_set, LegacyMultisigInputProposalV1 &proposal_out) { // add components @@ -798,7 +798,7 @@ void make_v1_legacy_multisig_input_proposal_v1(const LegacyEnoteVariant &enote, //------------------------------------------------------------------------------------------------------------------- void make_v1_legacy_multisig_input_proposal_v1(const LegacyEnoteRecord &enote_record, const crypto::secret_key &commitment_mask, - std::vector reference_set, + LegacyReferenceSetV2 reference_set, LegacyMultisigInputProposalV1 &proposal_out) { make_v1_legacy_multisig_input_proposal_v1(enote_record.enote, diff --git a/src/seraphis_main/tx_builders_multisig.h b/src/seraphis_main/tx_builders_multisig.h index d6e9f76314..41487daafc 100644 --- a/src/seraphis_main/tx_builders_multisig.h +++ b/src/seraphis_main/tx_builders_multisig.h @@ -100,11 +100,11 @@ void make_v1_legacy_multisig_input_proposal_v1(const LegacyEnoteVariant &enote, const std::uint64_t tx_output_index, const std::uint64_t unlock_time, const crypto::secret_key &commitment_mask, - std::vector reference_set, + LegacyReferenceSetV2 reference_set, LegacyMultisigInputProposalV1 &proposal_out); void make_v1_legacy_multisig_input_proposal_v1(const LegacyEnoteRecord &enote_record, const crypto::secret_key &commitment_mask, - std::vector reference_set, + LegacyReferenceSetV2 reference_set, LegacyMultisigInputProposalV1 &proposal_out); /** * brief: check_v1_sp_multisig_input_proposal_semantics_v1 - check semantics of a seraphis multisig input proposal diff --git a/src/seraphis_main/tx_component_types_legacy.cpp b/src/seraphis_main/tx_component_types_legacy.cpp index 70c7504efc..48c057baf3 100644 --- a/src/seraphis_main/tx_component_types_legacy.cpp +++ b/src/seraphis_main/tx_component_types_legacy.cpp @@ -69,6 +69,18 @@ void append_to_transcript(const LegacyEnoteImageV2 &container, SpTranscriptBuild transcript_inout.append("KI", container.key_image); } //------------------------------------------------------------------------------------------------------------------- +void append_to_transcript(const LegacyReferenceSetV2 &container, SpTranscriptBuilder &transcript_inout) +{ + std::vector refset_data; + refset_data.reserve(2 * container.indices.size()); + for (const legacy_output_index_t &i : container.indices) + { + refset_data.push_back(i.ledger_indexing_amount); + refset_data.push_back(i.index); + } + transcript_inout.append("indices", refset_data); +} +//------------------------------------------------------------------------------------------------------------------- void append_to_transcript(const LegacyRingSignatureV4 &container, SpTranscriptBuilder &transcript_inout) { transcript_inout.append("clsag_proof", container.clsag_proof); @@ -77,15 +89,15 @@ void append_to_transcript(const LegacyRingSignatureV4 &container, SpTranscriptBu //------------------------------------------------------------------------------------------------------------------- std::size_t legacy_ring_signature_v4_size_bytes(const std::size_t num_ring_members) { - return clsag_size_bytes(num_ring_members) + num_ring_members * 8; + return clsag_size_bytes(num_ring_members) + num_ring_members * 16; } //------------------------------------------------------------------------------------------------------------------- std::size_t legacy_ring_signature_v4_size_bytes(const LegacyRingSignatureV4 &ring_signature) { - CHECK_AND_ASSERT_THROW_MES(ring_signature.clsag_proof.s.size() == ring_signature.reference_set.size(), + CHECK_AND_ASSERT_THROW_MES(ring_signature.clsag_proof.s.size() == ring_signature.reference_set.indices.size(), "legacy ring signature v4 size: clsag proof doesn't match reference set size."); - return legacy_ring_signature_v4_size_bytes(ring_signature.reference_set.size()); + return legacy_ring_signature_v4_size_bytes(ring_signature.reference_set.indices.size()); } //------------------------------------------------------------------------------------------------------------------- bool compare_KI(const LegacyEnoteImageV2 &a, const LegacyEnoteImageV2 &b) diff --git a/src/seraphis_main/tx_component_types_legacy.h b/src/seraphis_main/tx_component_types_legacy.h index c91c8b8fbb..942744b5ca 100644 --- a/src/seraphis_main/tx_component_types_legacy.h +++ b/src/seraphis_main/tx_component_types_legacy.h @@ -33,6 +33,7 @@ //local headers #include "crypto/crypto.h" #include "ringct/rctTypes.h" +#include "seraphis_core/legacy_output_index.h" //third party headers #include @@ -88,6 +89,28 @@ void append_to_transcript(const LegacyEnoteImageV2 &container, SpTranscriptBuild /// get the size in bytes inline std::size_t legacy_enote_image_v2_size_bytes() { return 32 + 32; } +//// +// LegacyReferenceSetV1: not used in seraphis +// - ledger indexing amount +// - list of global output indices for given amount +/// + +//// +// LegacyReferenceSetV2: +// - sorted list of (ledger indexing amount, global index) pairs +/// +struct LegacyReferenceSetV2 +{ + std::set indices; +}; +inline const boost::string_ref container_name(const LegacyReferenceSetV2&) { return "LegacyReferenceSetV2"; } +void append_to_transcript(const LegacyReferenceSetV2 &container, SpTranscriptBuilder &transcript_inout); + +/// get the size in bytes +inline std::size_t legacy_ref_set_v2_size_bytes(const std::size_t num_indices) { return 16 * num_indices; } +inline std::size_t legacy_ref_set_v2_size_bytes(const LegacyReferenceSetV2 &legacy_ref_set) +{ return legacy_ref_set_v2_size_bytes(legacy_ref_set.indices.size()); } + //// // LegacyRingSignatureV1: not used in seraphis // - Cryptonote ring signature (using LegacyEnoteImageV1) @@ -105,14 +128,14 @@ inline std::size_t legacy_enote_image_v2_size_bytes() { return 32 + 32; } //// // LegacyRingSignatureV4 -// - CLSAG (using LegacyEnoteImageV2) +// - CLSAG (using LegacyEnoteImageV2 and mixed ledger amount reference set [Seraphis-only]) /// struct LegacyRingSignatureV4 final { /// a clsag proof LegacyClsagProof clsag_proof; - /// on-chain indices of the proof's ring members - std::vector reference_set; + /// sorted on-chain indices of the proof's ring members in (amount, index) form + LegacyReferenceSetV2 reference_set; }; inline const boost::string_ref container_name(const LegacyRingSignatureV4&) { return "LegacyRingSignatureV4"; } void append_to_transcript(const LegacyRingSignatureV4 &container, SpTranscriptBuilder &transcript_inout); diff --git a/src/seraphis_main/tx_validation_context.h b/src/seraphis_main/tx_validation_context.h index c18e6c07d0..d017138392 100644 --- a/src/seraphis_main/tx_validation_context.h +++ b/src/seraphis_main/tx_validation_context.h @@ -33,6 +33,7 @@ //local headers #include "crypto/crypto.h" #include "ringct/rctTypes.h" +#include "seraphis_core/legacy_output_index.h" #include "txtype_base.h" #include "tx_validators.h" @@ -83,7 +84,7 @@ class TxValidationContext * param: indices - * outparam: proof_elements_out - {KI, C} */ - virtual void get_reference_set_proof_elements_v1(const std::vector &indices, + virtual void get_reference_set_proof_elements_v1(const std::set &indices, rct::ctkeyV &proof_elements_out) const = 0; /** * brief: get_reference_set_proof_elements_v2 - gets seraphis squashed enotes stored in the validation context diff --git a/src/seraphis_main/tx_validators.cpp b/src/seraphis_main/tx_validators.cpp index 33be1b65b2..6482e69a14 100644 --- a/src/seraphis_main/tx_validators.cpp +++ b/src/seraphis_main/tx_validators.cpp @@ -170,13 +170,14 @@ bool validate_sp_semantics_legacy_reference_sets_v1(const SemanticConfigLegacyRe // check ring size in each ring signature for (const LegacyRingSignatureV4 &legacy_ring_signature : legacy_ring_signatures) { + const std::size_t ring_size{legacy_ring_signature.reference_set.indices.size()}; + // reference set - if (legacy_ring_signature.reference_set.size() < config.ring_size_min || - legacy_ring_signature.reference_set.size() > config.ring_size_max) + if (ring_size < config.ring_size_min || ring_size > config.ring_size_max) return false; // CLSAG signature size - if (legacy_ring_signature.reference_set.size() != legacy_ring_signature.clsag_proof.s.size()) + if (ring_size != legacy_ring_signature.clsag_proof.s.size()) return false; } @@ -312,12 +313,17 @@ bool validate_sp_semantics_layout_v1(const std::vector &l const std::vector &enote_ephemeral_pubkeys, const TxExtra &tx_extra) { - // legacy reference sets should be sorted (ascending) without duplicates - for (const LegacyRingSignatureV4 &legacy_ring_signature : legacy_ring_signatures) - { - if (!tools::is_sorted_and_unique(legacy_ring_signature.reference_set)) - return false; - } + using legacy_ring_sig_element_t = + std::remove_cv_t::value_type>; + using legacy_indices_t = + decltype(decltype(legacy_ring_sig_element_t::reference_set)::indices); + + // legacy ring signature reference sets should be sorted, which is done automatically by having + // them in a std::set + static_assert(std::is_same_v, + "legacy ring sigs list element type is not LegacyRingSignatureV4"); + static_assert(std::is_same_v>, + "legacy ring sig ref set indices is not a sorted set"); // seraphis membership proof binned reference set bins should be sorted (ascending) // note: duplicate bin locations are allowed @@ -444,7 +450,7 @@ bool validate_sp_legacy_input_proofs_v1(const std::vector // collect CLSAG ring members ring_members_temp.clear(); tx_validation_context.get_reference_set_proof_elements_v1( - legacy_ring_signatures[legacy_input_index].reference_set, + legacy_ring_signatures[legacy_input_index].reference_set.indices, ring_members_temp); // make legacy proof message diff --git a/src/seraphis_main/txtype_squashed_v1.cpp b/src/seraphis_main/txtype_squashed_v1.cpp index 3129fc5303..d72e709f81 100644 --- a/src/seraphis_main/txtype_squashed_v1.cpp +++ b/src/seraphis_main/txtype_squashed_v1.cpp @@ -160,7 +160,7 @@ std::size_t sp_tx_squashed_v1_size_bytes(const SpTxSquashedV1 &tx) { const std::size_t legacy_ring_size{ tx.legacy_ring_signatures.size() - ? tx.legacy_ring_signatures[0].reference_set.size() + ? tx.legacy_ring_signatures[0].reference_set.indices.size() : 0 }; @@ -202,7 +202,7 @@ std::size_t sp_tx_squashed_v1_weight(const SpTxSquashedV1 &tx) { const std::size_t legacy_ring_size{ tx.legacy_ring_signatures.size() - ? tx.legacy_ring_signatures[0].reference_set.size() + ? tx.legacy_ring_signatures[0].reference_set.indices.size() : 0 }; @@ -738,7 +738,7 @@ bool try_get_tx_contextual_validation_id(const SpTxSquashedV1 &tx, { // get the legacy ring members tx_validation_context.get_reference_set_proof_elements_v1( - legacy_ring_signature.reference_set, + legacy_ring_signature.reference_set.indices, tools::add_element(legacy_ring_members)); } diff --git a/src/seraphis_mocks/mock_ledger_context.cpp b/src/seraphis_mocks/mock_ledger_context.cpp index 79def24229..d5f8df8dac 100644 --- a/src/seraphis_mocks/mock_ledger_context.cpp +++ b/src/seraphis_mocks/mock_ledger_context.cpp @@ -32,6 +32,7 @@ #include "mock_ledger_context.h" //local headers +#include "common/container_helpers.h" #include "crypto/crypto.h" #include "crypto/x25519.h" #include "cryptonote_basic/subaddress_index.h" @@ -40,7 +41,7 @@ #include "misc_log_ex.h" #include "ringct/rctTypes.h" #include "seraphis_core/jamtis_enote_utils.h" -#include "seraphis_core/legacy_enote_types.h" +#include "seraphis_core/legacy_enote_utils.h" #include "seraphis_core/sp_core_enote_utils.h" #include "seraphis_crypto/sp_crypto_utils.h" #include "seraphis_main/scan_balance_recovery_utils.h" @@ -124,18 +125,21 @@ bool MockLedgerContext::seraphis_key_image_exists_onchain(const crypto::key_imag return m_sp_key_images.find(key_image) != m_sp_key_images.end(); } //------------------------------------------------------------------------------------------------------------------- -void MockLedgerContext::get_reference_set_proof_elements_v1(const std::vector &indices, +void MockLedgerContext::get_reference_set_proof_elements_v1(const std::set &indices, rct::ctkeyV &proof_elements_out) const { // get legacy enotes: {KI, C} proof_elements_out.clear(); proof_elements_out.reserve(indices.size()); - for (const std::uint64_t index : indices) + for (const legacy_output_index_t index : indices) { - CHECK_AND_ASSERT_THROW_MES(index < m_legacy_enote_references.size(), - "Tried to get legacy enote that doesn't exist."); - proof_elements_out.emplace_back(m_legacy_enote_references.at(index)); + CHECK_AND_ASSERT_THROW_MES(m_legacy_enote_references.count(index.ledger_indexing_amount), + "Tried to get legacy enote for amount that doesn't exist in the ledger."); + const auto &amount_outputs = m_legacy_enote_references.at(index.ledger_indexing_amount); + CHECK_AND_ASSERT_THROW_MES(index.index < amount_outputs.size(), + "Tried to get legacy enote for too high index."); + proof_elements_out.emplace_back(amount_outputs[index.index]); } } //------------------------------------------------------------------------------------------------------------------- @@ -153,9 +157,12 @@ void MockLedgerContext::get_reference_set_proof_elements_v2(const std::vector::max(); } //------------------------------------------------------------------------------------------------------------------- std::uint64_t MockLedgerContext::max_sp_enote_index() const @@ -189,6 +196,7 @@ void MockLedgerContext::clear_unconfirmed_cache() } //------------------------------------------------------------------------------------------------------------------- std::uint64_t MockLedgerContext::add_legacy_coinbase(const rct::key &tx_id, + const bool is_rct, const std::uint64_t unlock_time, TxExtra memo, std::vector legacy_key_images_for_block, @@ -201,15 +209,27 @@ std::uint64_t MockLedgerContext::add_legacy_coinbase(const rct::key &tx_id, "mock tx ledger (adding legacy coinbase tx): chain index is above last block that can have a legacy coinbase " "tx."); - // b. accumulated output count is consistent - const std::uint64_t accumulated_output_count = - m_accumulated_legacy_output_counts.size() - ? (m_accumulated_legacy_output_counts.rbegin())->second //last block's accumulated legacy output count - : 0; + // b. accumulated output counts is consistent + static const std::map empty_map{}; + const std::map &last_block_legacy_output_counts_per_amount{ + m_accumulated_legacy_output_counts.empty() ? + empty_map : m_accumulated_legacy_output_counts.crbegin()->second + }; - CHECK_AND_ASSERT_THROW_MES(accumulated_output_count == m_legacy_enote_references.size(), - "mock tx ledger (adding legacy coinbase tx): inconsistent number of accumulated outputs (bug)."); + CHECK_AND_ASSERT_THROW_MES(last_block_legacy_output_counts_per_amount.size() == m_legacy_enote_references.size(), + "mock tx ledger (adding legacy coinbase tx): inconsistent number unique output amounts from last block (bug)."); + for (const auto &amount_and_output_count : last_block_legacy_output_counts_per_amount) + { + CHECK_AND_ASSERT_THROW_MES(m_legacy_enote_references.count(amount_and_output_count.first), + "mock tx ledger (adding legacy coinbase tx): amount key not present in legacy enote references (bug)."); + + const std::uint64_t accumulated_output_count{amount_and_output_count.second}; + const std::uint64_t reference_set_count{m_legacy_enote_references.at(amount_and_output_count.first).size()}; + + CHECK_AND_ASSERT_THROW_MES(accumulated_output_count == reference_set_count, + "mock tx ledger (adding legacy coinbase tx): inconsistent number of accumulated outputs (bug)."); + } /// update state const std::uint64_t new_index{this->top_block_index() + 1}; @@ -222,25 +242,34 @@ std::uint64_t MockLedgerContext::add_legacy_coinbase(const rct::key &tx_id, // 2. add tx outputs - // a. initialize with current total legacy output count - std::uint64_t total_legacy_output_count{m_legacy_enote_references.size()}; + // a. initialize the amount -> total legacy output count map for next block + std::map next_block_legacy_output_counts_per_amount{ + last_block_legacy_output_counts_per_amount + }; - // b. insert all legacy enotes to the reference set + // b. insert all legacy enotes to the reference set and update output counts for next block for (const LegacyEnoteVariant &enote : output_enotes) { - m_legacy_enote_references[total_legacy_output_count] = {onetime_address_ref(enote), amount_commitment_ref(enote)}; + const rct::xmr_amount enote_amount{get_legacy_ledger_indexing_amount(enote, is_rct)}; + m_legacy_enote_references[enote_amount].push_back({onetime_address_ref(enote), amount_commitment_ref(enote)}); - ++total_legacy_output_count; + next_block_legacy_output_counts_per_amount[enote_amount] = m_legacy_enote_references[enote_amount].size(); } // c. add this block's accumulated output count - m_accumulated_legacy_output_counts[new_index] = total_legacy_output_count; + m_accumulated_legacy_output_counts[new_index] = std::move(next_block_legacy_output_counts_per_amount); if (new_index >= m_first_seraphis_allowed_block) m_accumulated_sp_output_counts[new_index] = m_sp_squashed_enotes.size(); // d. add this block's tx output contents - m_blocks_of_legacy_tx_output_contents[new_index][tx_id] = {unlock_time, std::move(memo), std::move(output_enotes)}; + m_blocks_of_legacy_tx_output_contents[new_index] = {{ + tx_id, + is_rct, + unlock_time, + std::move(memo), + std::move(output_enotes) + }}; if (new_index >= m_first_seraphis_allowed_block) m_blocks_of_sp_tx_output_contents[new_index]; @@ -448,7 +477,10 @@ std::uint64_t MockLedgerContext::commit_unconfirmed_txs_v1(const rct::key &coinb m_accumulated_sp_output_counts[new_index] = total_sp_output_count; if (new_index < m_first_seraphis_only_block) - m_accumulated_legacy_output_counts[new_index] = m_legacy_enote_references.size(); + { + m_accumulated_legacy_output_counts[new_index] = m_accumulated_legacy_output_counts.empty() ? + std::map() : m_accumulated_legacy_output_counts.crbegin()->second; + } // d. steal the unconfirmed cache's tx output contents m_blocks_of_sp_tx_output_contents[new_index] = std::move(m_unconfirmed_tx_output_contents); @@ -528,16 +560,28 @@ std::uint64_t MockLedgerContext::pop_chain_at_index(const std::uint64_t pop_inde CHECK_AND_ASSERT_THROW_MES(m_accumulated_legacy_output_counts.find(pop_index - 1) != m_accumulated_legacy_output_counts.end(), "mock ledger context (popping chain): accumulated legacy output counts has a hole (bug)."); - } - // remove all outputs starting in the pop_index block - const std::uint64_t first_output_to_remove = - pop_index > 0 - ? m_accumulated_legacy_output_counts[pop_index - 1] - : 0; + const std::map &accumulated_legacy_output_counts_top_new{ + m_accumulated_legacy_output_counts.at(pop_index - 1) + }; + + for (auto &amount_and_enotes : m_legacy_enote_references) + { + // remove all outputs starting in the pop_index block + const std::uint64_t first_output_to_remove = + accumulated_legacy_output_counts_top_new.count(amount_and_enotes.first) ? + accumulated_legacy_output_counts_top_new.at(amount_and_enotes.first) : 0; + + CHECK_AND_ASSERT_THROW_MES(first_output_to_remove <= amount_and_enotes.second.size(), + "mock ledger context (popping chain): accumulated legacy output count too high (bug)."); - m_legacy_enote_references.erase(m_legacy_enote_references.find(first_output_to_remove), - m_legacy_enote_references.end()); + amount_and_enotes.second.resize(first_output_to_remove); + } + } + else + { + m_legacy_enote_references.clear(); + } } // 3. remove squashed enotes @@ -731,29 +775,19 @@ void MockLedgerContext::get_onchain_chunk_legacy(const std::uint64_t chunk_start m_blocks_of_tx_key_images.end(), "onchain chunk legacy-view scanning (mock ledger context): end of chunk not known in key images map (bug)."); - // a. initialize output count to the total number of legacy enotes in the ledger before the first block to scan - std::uint64_t total_output_count_before_tx{0}; + // a. setup getters for the count of the total number of legacy enotes in the ledger before the each tx + std::map accumulated_legacy_output_counts_this_chunk; if (chunk_context_out.start_index > 0) { - CHECK_AND_ASSERT_THROW_MES(m_accumulated_legacy_output_counts.find(chunk_context_out.start_index - 1) != - m_accumulated_legacy_output_counts.end(), - "onchain chunk legacy-view scanning (mock ledger context): output counts missing a block (bug)."); - - total_output_count_before_tx = m_accumulated_legacy_output_counts.at(chunk_context_out.start_index - 1); - } - - // b. optimization: reserve size in the chunk map - // - use output counts as a proxy for the number of txs in the chunk range - if (chunk_context_out.block_ids.size() > 0) - { - CHECK_AND_ASSERT_THROW_MES(m_accumulated_legacy_output_counts.find(chunk_end_index - 1) != + const auto previous_accumulated_legacy_output_counts_it{ + m_accumulated_legacy_output_counts.find(chunk_context_out.start_index - 1) + }; + CHECK_AND_ASSERT_THROW_MES(previous_accumulated_legacy_output_counts_it != m_accumulated_legacy_output_counts.end(), "onchain chunk legacy-view scanning (mock ledger context): output counts missing a block (bug)."); - chunk_data_out.basic_records_per_tx.reserve( - (m_accumulated_legacy_output_counts.at(chunk_end_index - 1) - total_output_count_before_tx) / 2 - ); + accumulated_legacy_output_counts_this_chunk = previous_accumulated_legacy_output_counts_it->second; } // c. legacy-view scan each block in the range @@ -770,7 +804,26 @@ void MockLedgerContext::get_onchain_chunk_legacy(const std::uint64_t chunk_start for (const auto &tx_with_output_contents : block_of_tx_output_contents.second) { - const rct::key &tx_id{sortable2rct(tx_with_output_contents.first)}; + const rct::key &tx_id{sortable2rct(std::get(tx_with_output_contents))}; + const bool is_rct{std::get(tx_with_output_contents)}; + const std::vector &enotes{ + std::get>(tx_with_output_contents) + }; + + // calculate the legacy ledger output indices for each enote in this tx + std::vector legacy_output_index_per_enote; + for (const LegacyEnoteVariant &enote : enotes) + { + const rct::xmr_amount legacy_ledger_indexing_amount{ + get_legacy_ledger_indexing_amount(enote, is_rct) + }; + // notice we increment the index for this enote's amount in chunk + const std::uint64_t this_enote_amount_index{ + accumulated_legacy_output_counts_this_chunk[legacy_ledger_indexing_amount]++ + }; + legacy_output_index_per_enote.push_back({legacy_ledger_indexing_amount, + this_enote_amount_index}); + } // legacy view-scan the tx if in scan mode if (legacy_scan_mode == LegacyScanMode::SCAN) @@ -781,10 +834,10 @@ void MockLedgerContext::get_onchain_chunk_legacy(const std::uint64_t chunk_start block_of_tx_output_contents.first, std::get(m_block_infos.at(block_of_tx_output_contents.first)), tx_id, - total_output_count_before_tx, - std::get(tx_with_output_contents.second), - std::get(tx_with_output_contents.second), - std::get>(tx_with_output_contents.second), + legacy_output_index_per_enote, + std::get(tx_with_output_contents), + std::get(tx_with_output_contents), + enotes, SpEnoteOriginStatus::ONCHAIN, hw::get_device("default"), collected_records)) @@ -796,7 +849,7 @@ void MockLedgerContext::get_onchain_chunk_legacy(const std::uint64_t chunk_start } // always add an entry for this tx in the basic records map (since we save key images for every tx) - chunk_data_out.basic_records_per_tx[sortable2rct(tx_with_output_contents.first)]; + chunk_data_out.basic_records_per_tx[tx_id]; // collect key images from the tx (always do this for legacy txs) // - optimization not implemented here: only key images of rings which include a received @@ -804,27 +857,30 @@ void MockLedgerContext::get_onchain_chunk_legacy(const std::uint64_t chunk_start // include all key images CHECK_AND_ASSERT_THROW_MES( m_blocks_of_tx_key_images - .at(block_of_tx_output_contents.first).find(tx_with_output_contents.first) != + .at(block_of_tx_output_contents.first).find(tx_id) != m_blocks_of_tx_key_images .at(block_of_tx_output_contents.first).end(), "onchain chunk legacy-view scanning (mock ledger context): key image map missing tx (bug)."); if (scanning::try_collect_key_images_from_tx(block_of_tx_output_contents.first, std::get(m_block_infos.at(block_of_tx_output_contents.first)), - sortable2rct(tx_with_output_contents.first), + tx_id, std::get<0>(m_blocks_of_tx_key_images .at(block_of_tx_output_contents.first) - .at(tx_with_output_contents.first)), + .at(tx_id)), std::get<1>(m_blocks_of_tx_key_images .at(block_of_tx_output_contents.first) - .at(tx_with_output_contents.first)), + .at(tx_id)), SpEnoteSpentStatus::SPENT_ONCHAIN, collected_key_images)) chunk_data_out.contextual_key_images.emplace_back(std::move(collected_key_images)); - // add this tx's number of outputs to the total output count - total_output_count_before_tx += - std::get>(tx_with_output_contents.second).size(); + // add this tx's number of outputs to the chunk's total output count per amount + for (const LegacyEnoteVariant &enote : enotes) + { + const rct::xmr_amount enote_amount{get_legacy_ledger_indexing_amount(enote, is_rct)}; + accumulated_legacy_output_counts_this_chunk[enote_amount]++; + } } } ); diff --git a/src/seraphis_mocks/mock_ledger_context.h b/src/seraphis_mocks/mock_ledger_context.h index d579194cb4..dec43a2946 100644 --- a/src/seraphis_mocks/mock_ledger_context.h +++ b/src/seraphis_mocks/mock_ledger_context.h @@ -42,6 +42,7 @@ #include "ringct/rctOps.h" #include "ringct/rctTypes.h" #include "seraphis_core/legacy_enote_types.h" +#include "seraphis_core/legacy_output_index.h" #include "seraphis_crypto/sp_crypto_utils.h" #include "seraphis_main/scan_core_types.h" #include "seraphis_main/tx_component_types.h" @@ -106,7 +107,7 @@ class MockLedgerContext final * param: indices - * outparam: proof_elements_out - {KI, C} */ - void get_reference_set_proof_elements_v1(const std::vector &indices, + void get_reference_set_proof_elements_v1(const std::set &indices, rct::ctkeyV &proof_elements_out) const; /** * brief: get_reference_set_proof_elements_v2 - get seraphis squashed enotes stored in the ledger @@ -117,9 +118,10 @@ class MockLedgerContext final rct::keyV &proof_elements_out) const; /** * brief: max_legacy_enote_index - highest index of a legacy enote in the ledger + * param: ledger_indexing_amount - * return: highest legacy enote index (defaults to std::uint64_t::max if no enotes) */ - std::uint64_t max_legacy_enote_index() const; + std::uint64_t max_legacy_enote_index(const rct::xmr_amount ledger_indexing_amount) const; /** * brief: max_sp_enote_index - highest index of a seraphis enote in the ledger * return: highest seraphis enote index (defaults to std::uint64_t::max if no enotes) @@ -127,9 +129,11 @@ class MockLedgerContext final std::uint64_t max_sp_enote_index() const; /** * brief: num_legacy_enotes - number of legacy enotes in the ledger + * param: ledger_indexing_amount - * return: number of legacy enotes in the ledger */ - std::uint64_t num_legacy_enotes() const { return max_legacy_enote_index() + 1; } + std::uint64_t num_legacy_enotes(const rct::xmr_amount ledger_indexing_amount) const + { return this->max_legacy_enote_index(ledger_indexing_amount) + 1; } /** * brief: num_sp_enotes - number of seraphis enotes in the ledger * return: number of seraphis enotes in the ledger @@ -147,6 +151,7 @@ class MockLedgerContext final /** * brief: add_legacy_coinbase - make a block with a mock legacy coinbase tx (containing legacy key images) * param: tx_id - + * param: is_rct - * param: unlock_time - * param: memo - * param: legacy_key_images_for_block - @@ -154,6 +159,7 @@ class MockLedgerContext final * return: block index of newly added block */ std::uint64_t add_legacy_coinbase(const rct::key &tx_id, + const bool is_rct, const std::uint64_t unlock_time, TxExtra memo, std::vector legacy_key_images_for_block, @@ -305,14 +311,14 @@ class MockLedgerContext final > > > m_blocks_of_tx_key_images; - /// legacy enote references {KI, C} (mapped to output index) - std::map m_legacy_enote_references; + /// legacy enote references {KI, C} (mapped by indexable amount, then output index) + std::map> m_legacy_enote_references; /// seraphis squashed enotes (mapped to output index) std::map m_sp_squashed_enotes; /// map of accumulated output counts (legacy) std::map< - std::uint64_t, // block index - std::uint64_t // total number of legacy enotes including those in this block + std::uint64_t, // block index + std::map // total number of legacy enotes including those in this block, per amount > m_accumulated_legacy_output_counts; /// map of accumulated output counts (seraphis) std::map< @@ -322,9 +328,10 @@ class MockLedgerContext final /// map of legacy tx outputs std::map< std::uint64_t, // block index - std::map< - sortable_key, // tx id - std::tuple< // tx output contents + std::vector< // ordered list of txs + std::tuple< // tx contents + sortable_key, // txid + bool, // is_rct std::uint64_t, // unlock time TxExtra, // tx memo std::vector // output enotes diff --git a/src/seraphis_mocks/mock_send_receive.cpp b/src/seraphis_mocks/mock_send_receive.cpp index b626c51607..b2636107da 100644 --- a/src/seraphis_mocks/mock_send_receive.cpp +++ b/src/seraphis_mocks/mock_send_receive.cpp @@ -129,7 +129,7 @@ void send_legacy_coinbase_amounts_to_user(const std::vector &co "send legacy coinbase amounts to user: appending enote ephemeral pubkeys to tx extra failed."); // 3. commit coinbase enotes as new block - ledger_context_inout.add_legacy_coinbase(rct::pkGen(), 0, std::move(tx_extra), {}, std::move(coinbase_enotes)); + ledger_context_inout.add_legacy_coinbase(rct::pkGen(), true, 0, std::move(tx_extra), {}, std::move(coinbase_enotes)); } //------------------------------------------------------------------------------------------------------------------- void send_sp_coinbase_amounts_to_user(const std::vector &coinbase_amounts, @@ -285,7 +285,7 @@ void construct_tx_for_mock_ledger_v1(const legacy_mock_keys &local_user_legacy_k // 6. get ledger mappings for the input membership proofs // note: do this after making the tx proposal to demo that inputs don't have to be on-chain when proposing a tx - std::unordered_map legacy_input_ledger_mappings; + std::unordered_map legacy_input_ledger_mappings; std::unordered_map sp_input_ledger_mappings; try_get_membership_proof_real_reference_mappings(legacy_contextual_inputs, legacy_input_ledger_mappings); try_get_membership_proof_real_reference_mappings(sp_contextual_inputs, sp_input_ledger_mappings); diff --git a/src/seraphis_mocks/mock_tx_builders_legacy_inputs.cpp b/src/seraphis_mocks/mock_tx_builders_legacy_inputs.cpp index f83ab18001..f46597ff6a 100644 --- a/src/seraphis_mocks/mock_tx_builders_legacy_inputs.cpp +++ b/src/seraphis_mocks/mock_tx_builders_legacy_inputs.cpp @@ -72,10 +72,11 @@ std::vector gen_mock_legacy_input_proposals_v1(const cryp return input_proposals; } //------------------------------------------------------------------------------------------------------------------- -void gen_mock_legacy_ring_signature_members_for_enote_at_pos_v1(const std::uint64_t real_reference_index_in_ledger, +void gen_mock_legacy_ring_signature_members_for_enote_at_pos_v1( + const legacy_output_index_t real_reference_index_in_ledger, const std::uint64_t ring_size, const MockLedgerContext &ledger_context, - std::vector &reference_set_out, + LegacyReferenceSetV2 &reference_set_out, rct::ctkeyV &referenced_enotes_out, std::uint64_t &real_reference_index_out) { @@ -85,7 +86,10 @@ void gen_mock_legacy_ring_signature_members_for_enote_at_pos_v1(const std::uint6 LegacyRingSignaturePrepV1 proof_prep; // 1. flat decoy selector for mock-up - const LegacyDecoySelectorFlat decoy_selector{0, ledger_context.max_legacy_enote_index()}; + const rct::xmr_amount ledger_indexing_amount{real_reference_index_in_ledger.ledger_indexing_amount}; + const LegacyDecoySelectorFlat decoy_selector{LegacyDecoySelectorFlat::index_bounds_by_amount_t{ + {ledger_indexing_amount, {0, ledger_context.max_legacy_enote_index(ledger_indexing_amount)}} + }}; // 2. reference set CHECK_AND_ASSERT_THROW_MES(ring_size > 0, @@ -93,23 +97,23 @@ void gen_mock_legacy_ring_signature_members_for_enote_at_pos_v1(const std::uint6 decoy_selector.get_ring_members(real_reference_index_in_ledger, ring_size, - reference_set_out, + reference_set_out.indices, real_reference_index_out); - CHECK_AND_ASSERT_THROW_MES(real_reference_index_out < reference_set_out.size(), + CHECK_AND_ASSERT_THROW_MES(real_reference_index_out < reference_set_out.indices.size(), "gen mock legacy ring signature members (for enote at pos): real reference index is outside of reference set."); /// copy all referenced legacy enotes from the ledger - ledger_context.get_reference_set_proof_elements_v1(reference_set_out, referenced_enotes_out); + ledger_context.get_reference_set_proof_elements_v1(reference_set_out.indices, referenced_enotes_out); - CHECK_AND_ASSERT_THROW_MES(reference_set_out.size() == referenced_enotes_out.size(), + CHECK_AND_ASSERT_THROW_MES(reference_set_out.indices.size() == referenced_enotes_out.size(), "gen mock legacy ring signature members (for enote at pos): reference set doesn't line up with reference " "enotes."); } //------------------------------------------------------------------------------------------------------------------- LegacyRingSignaturePrepV1 gen_mock_legacy_ring_signature_prep_for_enote_at_pos_v1(const rct::key &tx_proposal_prefix, - const std::uint64_t real_reference_index_in_ledger, + const legacy_output_index_t real_reference_index_in_ledger, const LegacyEnoteImageV2 &real_reference_image, const crypto::secret_key &real_reference_view_privkey, const crypto::secret_key &commitment_mask, @@ -168,10 +172,11 @@ LegacyRingSignaturePrepV1 gen_mock_legacy_ring_signature_prep_v1(const rct::key } // 2. add mock legacy enotes as the outputs of a mock legacy coinbase tx - const std::uint64_t real_reference_index_in_ledger{ - ledger_context_inout.max_legacy_enote_index() + add_real_at_pos + 1 + const legacy_output_index_t real_reference_index_in_ledger{ + 0, // 0 amount since v5 legacy enotes all have ledger indexing amount 0 + ledger_context_inout.max_legacy_enote_index(0) + add_real_at_pos + 1 }; - ledger_context_inout.add_legacy_coinbase(rct::pkGen(), 0, TxExtra{}, {}, std::move(mock_enotes)); + ledger_context_inout.add_legacy_coinbase(rct::pkGen(), true, 0, TxExtra{}, {}, std::move(mock_enotes)); /// finish making the proof prep @@ -260,7 +265,7 @@ std::vector gen_mock_legacy_ring_signature_preps_v1(c } //------------------------------------------------------------------------------------------------------------------- void make_mock_legacy_ring_signature_preps_for_inputs_v1(const rct::key &tx_proposal_prefix, - const std::unordered_map &input_ledger_mappings, + const std::unordered_map &input_ledger_mappings, const std::vector &input_proposals, const std::uint64_t ring_size, const MockLedgerContext &ledger_context, @@ -298,7 +303,7 @@ bool try_gen_legacy_multisig_ring_signature_preps_v1(const std::vector &mapped_preps_out) { // 1. extract map [ legacy KI : enote ledger index ] from contextual records - std::unordered_map enote_ledger_mappings; + std::unordered_map enote_ledger_mappings; if (!try_get_membership_proof_real_reference_mappings(contextual_records, enote_ledger_mappings)) return false; diff --git a/src/seraphis_mocks/mock_tx_builders_legacy_inputs.h b/src/seraphis_mocks/mock_tx_builders_legacy_inputs.h index b9cd921914..846b3ac1e4 100644 --- a/src/seraphis_mocks/mock_tx_builders_legacy_inputs.h +++ b/src/seraphis_mocks/mock_tx_builders_legacy_inputs.h @@ -57,14 +57,15 @@ namespace mocks std::vector gen_mock_legacy_input_proposals_v1(const crypto::secret_key &legacy_spend_privkey, const std::vector &input_amounts); /// make mock legacy ring signature preps -void gen_mock_legacy_ring_signature_members_for_enote_at_pos_v1(const std::uint64_t real_reference_index_in_ledger, +void gen_mock_legacy_ring_signature_members_for_enote_at_pos_v1( + const legacy_output_index_t real_reference_index_in_ledger, const std::uint64_t ring_size, const MockLedgerContext &ledger_context, - std::vector &reference_set_out, + LegacyReferenceSetV2 &reference_set_out, rct::ctkeyV &referenced_enotes_out, std::uint64_t &real_reference_index_out); LegacyRingSignaturePrepV1 gen_mock_legacy_ring_signature_prep_for_enote_at_pos_v1(const rct::key &tx_proposal_prefix, - const std::uint64_t real_reference_index_in_ledger, + const legacy_output_index_t real_reference_index_in_ledger, const LegacyEnoteImageV2 &real_reference_image, const crypto::secret_key &real_reference_view_privkey, const crypto::secret_key &commitment_mask, @@ -90,7 +91,7 @@ std::vector gen_mock_legacy_ring_signature_preps_v1(c MockLedgerContext &ledger_context_inout); /// prepare membership proofs for enotes in a mock ledger void make_mock_legacy_ring_signature_preps_for_inputs_v1(const rct::key &tx_proposal_prefix, - const std::unordered_map &input_ledger_mappings, + const std::unordered_map &input_ledger_mappings, const std::vector &input_proposals, const std::uint64_t ring_size, const MockLedgerContext &ledger_context, diff --git a/src/seraphis_mocks/scan_context_async_mock.cpp b/src/seraphis_mocks/scan_context_async_mock.cpp index cb08163e25..9a6773220f 100644 --- a/src/seraphis_mocks/scan_context_async_mock.cpp +++ b/src/seraphis_mocks/scan_context_async_mock.cpp @@ -35,6 +35,7 @@ #include "misc_language.h" #include "misc_log_ex.h" #include "net/http.h" +#include "seraphis_core/legacy_enote_utils.h" #include "seraphis_impl/scan_ledger_chunk_simple.h" #include "seraphis_main/contextual_enote_record_types.h" #include "seraphis_main/enote_finding_context.h" @@ -102,25 +103,14 @@ static void validate_get_blocks_res(const ChunkRequest &req, } //------------------------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------------------------- -static std::uint64_t get_total_output_count_before_tx(const std::vector &output_indices) -{ - // total_output_count_before_tx == global output index of first output in tx. - // Some txs have no enotes, in which case we set this value to 0 as it isn't useful. - // TODO: pre-RCT outputs yield incorrect values here but this is only used for spending - // need https://github.com/UkoeHB/monero/pull/40 in order to handle pre-RCT outputs - return !output_indices.empty() ? output_indices[0] : 0; -} -//------------------------------------------------------------------------------------------------------------------- -//------------------------------------------------------------------------------------------------------------------- static void prepare_unscanned_legacy_transaction(const crypto::hash &tx_hash, const cryptonote::transaction &tx, - const std::uint64_t total_output_count_before_tx, + const std::vector legacy_output_index_number_per_enote, sp::LegacyUnscannedTransaction &unscanned_tx_out) { unscanned_tx_out = LegacyUnscannedTransaction{}; unscanned_tx_out.transaction_id = rct::hash2rct(tx_hash); - unscanned_tx_out.total_enotes_before_tx = total_output_count_before_tx; unscanned_tx_out.unlock_time = tx.unlock_time; unscanned_tx_out.tx_memo = sp::TxExtra( @@ -130,6 +120,10 @@ static void prepare_unscanned_legacy_transaction(const crypto::hash &tx_hash, sp::legacy_outputs_to_enotes(tx, unscanned_tx_out.enotes); + CHECK_AND_ASSERT_THROW_MES(legacy_output_index_number_per_enote.empty() || + legacy_output_index_number_per_enote.size() == unscanned_tx_out.enotes.size(), + "bad number of output indices compared to number of legacy tx enotes"); + unscanned_tx_out.legacy_key_images.reserve(tx.vin.size()); for (const auto &in: tx.vin) { @@ -138,6 +132,21 @@ static void prepare_unscanned_legacy_transaction(const crypto::hash &tx_hash, const auto &txin = boost::get(in); unscanned_tx_out.legacy_key_images.emplace_back(txin.k_image); } + + const bool is_rct{tx.version == 2}; + + unscanned_tx_out.legacy_output_index_per_enote.clear(); + unscanned_tx_out.legacy_output_index_per_enote.reserve(unscanned_tx_out.enotes.size()); + for (size_t i = 0; i < unscanned_tx_out.enotes.size(); ++i) + { + const rct::xmr_amount ledger_indexing_amount{ + get_legacy_ledger_indexing_amount(unscanned_tx_out.enotes[i], is_rct) + }; + const std::uint64_t global_index{ + legacy_output_index_number_per_enote.empty() ? 0 : legacy_output_index_number_per_enote[i] + }; + unscanned_tx_out.legacy_output_index_per_enote.push_back({ledger_indexing_amount, global_index}); + } } //------------------------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------------------------- @@ -242,7 +251,7 @@ static void prepare_unscanned_block(const cryptonote::block_complete_entry &res_ crypto::hash miner_tx_hash = cryptonote::get_transaction_hash(block.miner_tx); prepare_unscanned_legacy_transaction(miner_tx_hash, block.miner_tx, - get_total_output_count_before_tx(output_indices[0].indices), + output_indices[0].indices, unscanned_block_out.unscanned_txs[0]); // Prepare non-miner txs @@ -258,7 +267,7 @@ static void prepare_unscanned_block(const cryptonote::block_complete_entry &res_ prepare_unscanned_legacy_transaction(block.tx_hashes[tx_idx], std::move(tx), - get_total_output_count_before_tx(output_indices[unscanned_tx_idx].indices), + output_indices[unscanned_tx_idx].indices, unscanned_tx); } } diff --git a/src/seraphis_mocks/tx_validation_context_mock.h b/src/seraphis_mocks/tx_validation_context_mock.h index 9d7ca63610..cd00c16c3b 100644 --- a/src/seraphis_mocks/tx_validation_context_mock.h +++ b/src/seraphis_mocks/tx_validation_context_mock.h @@ -98,7 +98,7 @@ class TxValidationContextMock final : public TxValidationContext * param: indices - * outparam: proof_elements_out - {KI, C} */ - void get_reference_set_proof_elements_v1(const std::vector &indices, + void get_reference_set_proof_elements_v1(const std::set &indices, rct::ctkeyV &proof_elements_out) const override { m_mock_ledger_context.get_reference_set_proof_elements_v1(indices, proof_elements_out); diff --git a/tests/unit_tests/seraphis_enote_scanning.cpp b/tests/unit_tests/seraphis_enote_scanning.cpp index 715e04b6c0..bac13f334c 100644 --- a/tests/unit_tests/seraphis_enote_scanning.cpp +++ b/tests/unit_tests/seraphis_enote_scanning.cpp @@ -2382,6 +2382,7 @@ TEST(seraphis_enote_scanning, legacy_non_standard_tx_nth_pub_key) ASSERT_NO_THROW(ledger_context.add_legacy_coinbase( rct::pkGen(), + true, 0, tx_extra, {}, @@ -2498,6 +2499,7 @@ TEST(seraphis_enote_scanning, legacy_non_standard_more_additional_pub_keys_than_ )); ASSERT_NO_THROW(ledger_context.add_legacy_coinbase( rct::pkGen(), + true, 0, tx_extra_1, {}, @@ -2618,6 +2620,7 @@ TEST(seraphis_enote_scanning, legacy_non_standard_less_additional_pub_keys_than_ )); ASSERT_NO_THROW(ledger_context.add_legacy_coinbase( rct::pkGen(), + true, 0, tx_extra_1, {}, @@ -2815,6 +2818,7 @@ TEST(seraphis_enote_scanning, legacy_pre_transition_1) )); ASSERT_NO_THROW(ledger_context.add_legacy_coinbase( rct::pkGen(), + true, // is_rct 0, tx_extra_1, {}, @@ -2914,6 +2918,7 @@ TEST(seraphis_enote_scanning, legacy_pre_transition_2) //add legacy enote in block 0 ASSERT_NO_THROW(ledger_context.add_legacy_coinbase( rct::pkGen(), + true, // is_rct 0, tx_extra_1, {}, @@ -2947,6 +2952,7 @@ TEST(seraphis_enote_scanning, legacy_pre_transition_2) //spend enote in block 1 ASSERT_NO_THROW(ledger_context.add_legacy_coinbase( rct::pkGen(), + true, // is_rct 0, TxExtra{}, { @@ -2999,6 +3005,7 @@ TEST(seraphis_enote_scanning, legacy_pre_transition_2) //add empty block 2 (inject to test ledger index trackers) ASSERT_NO_THROW(ledger_context.add_legacy_coinbase( rct::pkGen(), + true, // is_rct 0, TxExtra{}, {}, @@ -3159,6 +3166,7 @@ TEST(seraphis_enote_scanning, legacy_pre_transition_3) //block 0: 1 -> user, 1 -> rand ASSERT_NO_THROW(ledger_context.add_legacy_coinbase( rct::pkGen(), + true, // is_rct 0, tx_extra_1, {}, @@ -3215,6 +3223,7 @@ TEST(seraphis_enote_scanning, legacy_pre_transition_3) //block 1: 2 -> user ASSERT_NO_THROW(ledger_context.add_legacy_coinbase( rct::pkGen(), + true, // is_rct 0, tx_extra_2, {}, @@ -3326,6 +3335,7 @@ TEST(seraphis_enote_scanning, legacy_pre_transition_3) //block 2: spend enote 2 ASSERT_NO_THROW(ledger_context.add_legacy_coinbase( rct::pkGen(), + true, // is_rct 0, TxExtra{}, { @@ -3429,6 +3439,7 @@ TEST(seraphis_enote_scanning, legacy_pre_transition_3) //block 2: 4 -> user, spend enote 1 ASSERT_NO_THROW(ledger_context.add_legacy_coinbase( rct::pkGen(), + true, // is_rct 0, tx_extra_3, { @@ -3492,6 +3503,7 @@ TEST(seraphis_enote_scanning, legacy_pre_transition_3) //block 3: spend enote 3 ASSERT_NO_THROW(ledger_context.add_legacy_coinbase( rct::pkGen(), + true, // is_rct 0, TxExtra{}, { @@ -3639,6 +3651,7 @@ TEST(seraphis_enote_scanning, legacy_pre_transition_4) //block 0: 1 -> user ASSERT_NO_THROW(ledger_context.add_legacy_coinbase( rct::pkGen(), + true, // is_rct 0, tx_extra_1, {}, @@ -3676,6 +3689,7 @@ TEST(seraphis_enote_scanning, legacy_pre_transition_4) //block 1: 2 -> user ASSERT_NO_THROW(ledger_context.add_legacy_coinbase( rct::pkGen(), + true, // is_rct 0, tx_extra_2, {}, @@ -3834,6 +3848,7 @@ TEST(seraphis_enote_scanning, legacy_pre_transition_5) //block 0: 1 -> user ASSERT_NO_THROW(ledger_context.add_legacy_coinbase( rct::pkGen(), + true, // is_rct 0, tx_extra_1, {}, @@ -3871,6 +3886,7 @@ TEST(seraphis_enote_scanning, legacy_pre_transition_5) //block 1: 2 -> user ASSERT_NO_THROW(ledger_context.add_legacy_coinbase( rct::pkGen(), + true, // is_rct 0, tx_extra_2, {}, @@ -3908,6 +3924,7 @@ TEST(seraphis_enote_scanning, legacy_pre_transition_5) //block 1: empty ASSERT_NO_THROW(ledger_context.add_legacy_coinbase( rct::pkGen(), + true, // is_rct 0, TxExtra{}, {}, @@ -4039,6 +4056,7 @@ TEST(seraphis_enote_scanning, legacy_pre_transition_6) //block 0: enote 1-a ASSERT_NO_THROW(ledger_context.add_legacy_coinbase( rct::pkGen(), + true, // is_rct 0, tx_extra_1, {}, @@ -4079,6 +4097,7 @@ TEST(seraphis_enote_scanning, legacy_pre_transition_6) //block 1: enote 1-b ASSERT_NO_THROW(ledger_context.add_legacy_coinbase( rct::pkGen(), + true, // is_rct 0, tx_extra_1, {}, @@ -4199,6 +4218,7 @@ TEST(seraphis_enote_scanning, legacy_pre_transition_6) //block 1: enote 1-c ASSERT_NO_THROW(ledger_context.add_legacy_coinbase( rct::pkGen(), + true, // is_rct 0, tx_extra_1, {}, @@ -4303,6 +4323,7 @@ TEST(seraphis_enote_scanning, legacy_pre_transition_6) //block 1: enote 1-d ASSERT_NO_THROW(ledger_context.add_legacy_coinbase( rct::pkGen(), + true, // is_rct 0, tx_extra_1, {}, @@ -4358,6 +4379,7 @@ TEST(seraphis_enote_scanning, legacy_pre_transition_6) //block 2: spend enote 1 ASSERT_NO_THROW(ledger_context.add_legacy_coinbase( rct::pkGen(), + true, // is_rct 0, TxExtra{}, { @@ -4531,6 +4553,7 @@ TEST(seraphis_enote_scanning, legacy_pre_transition_7) //block 0: enote 1-a (amount 3) ASSERT_NO_THROW(ledger_context.add_legacy_coinbase( rct::pkGen(), + true, // is_rct 0, tx_extra, {}, @@ -4571,6 +4594,7 @@ TEST(seraphis_enote_scanning, legacy_pre_transition_7) //block 1: enote 1-b (amount 5) ASSERT_NO_THROW(ledger_context.add_legacy_coinbase( rct::pkGen(), + true, // is_rct 0, tx_extra, {}, @@ -4700,6 +4724,7 @@ TEST(seraphis_enote_scanning, legacy_pre_transition_7) //block 1: enote 1-c (amount 1) ASSERT_NO_THROW(ledger_context.add_legacy_coinbase( rct::pkGen(), + true, // is_rct 0, tx_extra, {}, @@ -4711,6 +4736,7 @@ TEST(seraphis_enote_scanning, legacy_pre_transition_7) //block 2: enote 1-d (amount 4) ASSERT_NO_THROW(ledger_context.add_legacy_coinbase( rct::pkGen(), + true, // is_rct 0, tx_extra, {}, @@ -4766,6 +4792,7 @@ TEST(seraphis_enote_scanning, legacy_pre_transition_7) //block 3: spend enote 1 ASSERT_NO_THROW(ledger_context.add_legacy_coinbase( rct::pkGen(), + true, // is_rct 0, TxExtra{}, { @@ -4985,6 +5012,7 @@ TEST(seraphis_enote_scanning, legacy_pre_transition_8) //block 0: enote 1 (unlock at block 0) ASSERT_NO_THROW(ledger_context.add_legacy_coinbase( rct::pkGen(), + true, // is_rct 0, tx_extra_1, {}, @@ -5032,6 +5060,7 @@ TEST(seraphis_enote_scanning, legacy_pre_transition_8) //block 1: enote 2 (unlock at block 3) ASSERT_NO_THROW(ledger_context.add_legacy_coinbase( rct::pkGen(), + true, // is_rct 3, tx_extra_2, {}, @@ -5079,6 +5108,7 @@ TEST(seraphis_enote_scanning, legacy_pre_transition_8) //block 2: enote 3 (unlock at block 5) ASSERT_NO_THROW(ledger_context.add_legacy_coinbase( rct::pkGen(), + true, // is_rct 5, tx_extra_3, {}, @@ -5126,6 +5156,7 @@ TEST(seraphis_enote_scanning, legacy_pre_transition_8) //block 3: empty ASSERT_NO_THROW(ledger_context.add_legacy_coinbase( rct::pkGen(), + true, // is_rct 0, TxExtra{}, {}, @@ -5171,6 +5202,7 @@ TEST(seraphis_enote_scanning, legacy_pre_transition_8) //block 4: empty ASSERT_NO_THROW(ledger_context.add_legacy_coinbase( rct::pkGen(), + true, // is_rct 0, TxExtra{}, {}, @@ -5370,6 +5402,7 @@ TEST(seraphis_enote_scanning, legacy_pre_transition_9) //block 0: enote 1-a (amount 1; unlock 0) ASSERT_NO_THROW(ledger_context.add_legacy_coinbase( rct::pkGen(), + true, // is_rct 0, tx_extra, {}, @@ -5417,6 +5450,7 @@ TEST(seraphis_enote_scanning, legacy_pre_transition_9) //block 1: empty ASSERT_NO_THROW(ledger_context.add_legacy_coinbase( rct::pkGen(), + true, // is_rct 0, TxExtra{}, {}, @@ -5462,6 +5496,7 @@ TEST(seraphis_enote_scanning, legacy_pre_transition_9) //block 2: enote 1-b (amount 2; unlock 0) ASSERT_NO_THROW(ledger_context.add_legacy_coinbase( rct::pkGen(), + true, // is_rct 0, tx_extra, {}, @@ -5509,6 +5544,7 @@ TEST(seraphis_enote_scanning, legacy_pre_transition_9) //block 3: empty ASSERT_NO_THROW(ledger_context.add_legacy_coinbase( rct::pkGen(), + true, // is_rct 0, TxExtra{}, {}, @@ -5604,6 +5640,7 @@ TEST(seraphis_enote_scanning, legacy_pre_transition_9) //block 4: enote 1-c (amount 3; unlock 0), spend enote 1 (check balance with a locked and spent enote [enote 1-c]) ASSERT_NO_THROW(ledger_context.add_legacy_coinbase( rct::pkGen(), + true, // is_rct 0, tx_extra, { @@ -6000,6 +6037,7 @@ TEST(seraphis_enote_scanning, legacy_sp_transition_1) //block 0: legacy enote 1, legacy enote 2 ASSERT_NO_THROW(ledger_context.add_legacy_coinbase( rct::pkGen(), + true, // is_rct 0, tx_extra_1, {}, @@ -6079,6 +6117,7 @@ TEST(seraphis_enote_scanning, legacy_sp_transition_1) //block 1: legacy enote 3, spend legacy enote 2 ASSERT_NO_THROW(ledger_context.add_legacy_coinbase( rct::pkGen(), + true, // is_rct 0, tx_extra_3, { @@ -6372,6 +6411,7 @@ TEST(seraphis_enote_scanning, legacy_sp_transition_1) //block 1: legacy enote 4, legacy enote 5 ASSERT_NO_THROW(ledger_context.add_legacy_coinbase( rct::pkGen(), + true, // is_rct 0, tx_extra_4, {}, @@ -6642,6 +6682,7 @@ TEST(seraphis_enote_scanning, legacy_sp_transition_1) //block 1: legacy enote 4, legacy enote 5 (reuse these) ASSERT_NO_THROW(ledger_context.add_legacy_coinbase( rct::pkGen(), + true, // is_rct 0, tx_extra_4, {}, @@ -7088,6 +7129,7 @@ TEST(seraphis_enote_scanning, legacy_sp_transition_2) //block 0: legacy enote 1, legacy enote 2 ASSERT_NO_THROW(ledger_context.add_legacy_coinbase( rct::pkGen(), + true, // is_rct 0, tx_extra_1, {}, @@ -7167,6 +7209,7 @@ TEST(seraphis_enote_scanning, legacy_sp_transition_2) //block 1: legacy enote 3, spend legacy enote 2 ASSERT_NO_THROW(ledger_context.add_legacy_coinbase( rct::pkGen(), + true, // is_rct 0, tx_extra_2, { @@ -7441,6 +7484,7 @@ TEST(seraphis_enote_scanning, legacy_sp_transition_2) //block 2: legacy enote 3, spend legacy enote 1 ASSERT_NO_THROW(ledger_context.add_legacy_coinbase( rct::pkGen(), + true, // is_rct 0, tx_extra_2, { @@ -8085,6 +8129,7 @@ TEST(seraphis_enote_scanning, legacy_sp_transition_3) //block 0: legacy enote 1 ASSERT_NO_THROW(ledger_context.add_legacy_coinbase( rct::pkGen(), + true, // is_rct 0, tx_extra_1, {}, @@ -8211,6 +8256,7 @@ TEST(seraphis_enote_scanning, legacy_sp_transition_3) //block 0: legacy enote 1 ASSERT_NO_THROW(ledger_context.add_legacy_coinbase( rct::pkGen(), + true, // is_rct 0, tx_extra_1, {}, @@ -8648,6 +8694,7 @@ TEST(seraphis_enote_scanning, legacy_sp_transition_4) //block 0: legacy enote 1, legacy enote 2 ASSERT_NO_THROW(ledger_context.add_legacy_coinbase( rct::pkGen(), + true, // is_rct 0, tx_extra_1, {}, @@ -8727,6 +8774,7 @@ TEST(seraphis_enote_scanning, legacy_sp_transition_4) //block 1: legacy enote 3, spend legacy enote 2 ASSERT_NO_THROW(ledger_context.add_legacy_coinbase( rct::pkGen(), + true, // is_rct 0, tx_extra_2, { @@ -9045,6 +9093,7 @@ TEST(seraphis_enote_scanning, legacy_sp_transition_4) //block 1: legacy enote 1 ASSERT_NO_THROW(ledger_context.add_legacy_coinbase( rct::pkGen(), + true, // is_rct 0, TxExtra{}, { @@ -9267,6 +9316,7 @@ TEST(seraphis_enote_scanning, legacy_sp_transition_5) //block 0: legacy enote 1, legacy enote 2 ASSERT_NO_THROW(ledger_context.add_legacy_coinbase( rct::pkGen(), + true, // is_rct 0, tx_extra_1, {}, @@ -9320,6 +9370,7 @@ TEST(seraphis_enote_scanning, legacy_sp_transition_5) //block 1: legacy enote 1 ASSERT_NO_THROW(ledger_context.add_legacy_coinbase( rct::pkGen(), + true, // is_rct 0, TxExtra{}, { @@ -9552,6 +9603,7 @@ TEST(seraphis_enote_scanning, legacy_sp_transition_5) //block 2: legacy enote 2 ASSERT_NO_THROW(ledger_context.add_legacy_coinbase( rct::pkGen(), + true, // is_rct 0, TxExtra{}, { diff --git a/tests/unit_tests/seraphis_multisig.cpp b/tests/unit_tests/seraphis_multisig.cpp index a78562fad7..af6eb05dd1 100644 --- a/tests/unit_tests/seraphis_multisig.cpp +++ b/tests/unit_tests/seraphis_multisig.cpp @@ -234,7 +234,7 @@ static bool sp_multisig_input_is_ready_to_spend(const SpMultisigInputProposalV1 } //------------------------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------------------------- -static bool legacy_ring_members_are_ready_to_spend(const std::vector &reference_set, +static bool legacy_ring_members_are_ready_to_spend(const std::set &reference_set, const rct::ctkeyV &legacy_ring_members, const MockLedgerContext &ledger_context) { @@ -326,7 +326,7 @@ static void validate_multisig_tx_proposal(const SpMultisigTxProposalV1 &multisig ++legacy_input_index) { ASSERT_TRUE(legacy_ring_members_are_ready_to_spend( - multisig_tx_proposal.legacy_multisig_input_proposals[legacy_input_index].reference_set, + multisig_tx_proposal.legacy_multisig_input_proposals[legacy_input_index].reference_set.indices, multisig_tx_proposal.legacy_input_proof_proposals[legacy_input_index].ring_members, ledger_context)); }