From 537ba29b1a3b3f4713062a3687744fa84fb613cd Mon Sep 17 00:00:00 2001 From: jeffro256 Date: Thu, 2 Sep 2021 20:01:31 -0500 Subject: [PATCH] jamtis changes * nominal address tag protection for LWs (extra address key) * flexible view tags * churning and pocketchange protection for LWs (auxiliary enote records) --- src/crypto/x25519.h | 4 + src/cryptonote_config.h | 19 +- src/seraphis_core/CMakeLists.txt | 2 +- ...e_utils.cpp => jamtis_account_secrets.cpp} | 54 +- ..._core_utils.h => jamtis_account_secrets.h} | 67 +- .../jamtis_address_tag_utils.cpp | 67 +- src/seraphis_core/jamtis_address_tag_utils.h | 11 +- src/seraphis_core/jamtis_address_utils.cpp | 6 +- src/seraphis_core/jamtis_address_utils.h | 12 +- src/seraphis_core/jamtis_destination.cpp | 57 +- src/seraphis_core/jamtis_destination.h | 34 +- src/seraphis_core/jamtis_enote_utils.cpp | 266 +++-- src/seraphis_core/jamtis_enote_utils.h | 142 ++- src/seraphis_core/jamtis_payment_proposal.cpp | 167 +-- src/seraphis_core/jamtis_payment_proposal.h | 12 +- src/seraphis_core/jamtis_support_types.cpp | 65 +- src/seraphis_core/jamtis_support_types.h | 68 +- .../enote_finding_context_legacy.cpp | 19 +- src/seraphis_impl/seraphis_serialization.h | 13 +- src/seraphis_impl/tx_builder_utils.cpp | 2 - src/seraphis_impl/tx_builder_utils.h | 1 - src/seraphis_main/enote_finding_context.h | 6 +- src/seraphis_main/enote_record_types.h | 27 +- src/seraphis_main/enote_record_utils.cpp | 1014 +++++++---------- src/seraphis_main/enote_record_utils.h | 105 +- .../scan_balance_recovery_utils.cpp | 506 ++++---- .../scan_balance_recovery_utils.h | 59 +- src/seraphis_main/scan_chunk_consumer.h | 2 +- src/seraphis_main/scan_context.h | 2 +- src/seraphis_main/sp_knowledge_proof_types.h | 24 +- .../sp_knowledge_proof_utils.cpp | 58 +- src/seraphis_main/sp_knowledge_proof_utils.h | 6 +- src/seraphis_main/tx_builder_types.cpp | 62 +- src/seraphis_main/tx_builder_types.h | 26 +- .../tx_builder_types_multisig.cpp | 1 + src/seraphis_main/tx_builder_types_multisig.h | 2 + src/seraphis_main/tx_builders_inputs.cpp | 2 + src/seraphis_main/tx_builders_inputs.h | 2 + src/seraphis_main/tx_builders_mixed.cpp | 8 +- src/seraphis_main/tx_builders_multisig.cpp | 15 +- src/seraphis_main/tx_builders_multisig.h | 3 + src/seraphis_main/tx_builders_outputs.cpp | 411 +++---- src/seraphis_main/tx_builders_outputs.h | 50 +- src/seraphis_main/tx_component_types.cpp | 10 +- src/seraphis_main/tx_component_types.h | 4 +- src/seraphis_main/tx_validators.cpp | 10 + src/seraphis_main/tx_validators.h | 4 + src/seraphis_main/txtype_coinbase_v1.cpp | 3 + src/seraphis_main/txtype_squashed_v1.cpp | 1 + .../enote_finding_context_mocks.cpp | 6 +- .../enote_finding_context_mocks.h | 26 +- src/seraphis_mocks/jamtis_mock_keys.cpp | 38 +- src/seraphis_mocks/jamtis_mock_keys.h | 18 +- src/seraphis_mocks/make_mock_tx.cpp | 13 +- src/seraphis_mocks/make_mock_tx.h | 1 + src/seraphis_mocks/mock_ledger_context.cpp | 93 +- src/seraphis_mocks/mock_ledger_context.h | 12 +- src/seraphis_mocks/mock_offchain_context.cpp | 30 +- src/seraphis_mocks/mock_offchain_context.h | 6 +- src/seraphis_mocks/mock_send_receive.cpp | 47 +- src/seraphis_mocks/mock_send_receive.h | 8 + .../mock_tx_builders_outputs.cpp | 10 +- src/seraphis_mocks/mock_tx_builders_outputs.h | 4 + .../scan_chunk_consumer_mocks.cpp | 76 +- .../scan_chunk_consumer_mocks.h | 12 +- tests/performance_tests/main.cpp | 2 - tests/performance_tests/view_scan.h | 75 +- tests/unit_tests/seraphis_basic.cpp | 671 ++++++----- tests/unit_tests/seraphis_enote_scanning.cpp | 442 +++++-- tests/unit_tests/seraphis_integration.cpp | 16 +- .../unit_tests/seraphis_knowledge_proofs.cpp | 84 +- tests/unit_tests/seraphis_multisig.cpp | 69 +- tests/unit_tests/seraphis_serialization.cpp | 4 +- 73 files changed, 2744 insertions(+), 2530 deletions(-) rename src/seraphis_core/{jamtis_core_utils.cpp => jamtis_account_secrets.cpp} (67%) rename src/seraphis_core/{jamtis_core_utils.h => jamtis_account_secrets.h} (61%) diff --git a/src/crypto/x25519.h b/src/crypto/x25519.h index ef8db93a21..8edff8bf3f 100644 --- a/src/crypto/x25519.h +++ b/src/crypto/x25519.h @@ -117,6 +117,10 @@ void x25519_invmul_key(std::vector privkeys_to_invert, } //namespace crypto +inline const unsigned char* to_bytes(const crypto::x25519_pubkey &point) { return &reinterpret_cast(point); } +inline const unsigned char* to_bytes(const crypto::x25519_scalar &scalar) { return &reinterpret_cast(scalar); } +inline const unsigned char* to_bytes(const crypto::x25519_secret_key &skey) { return &reinterpret_cast(skey); } + /// upgrade x25519 keys CRYPTO_MAKE_HASHABLE(x25519_pubkey) CRYPTO_MAKE_HASHABLE_CONSTANT_TIME(x25519_scalar) diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index 6491e7f5d4..ffbe55e303 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -306,27 +306,30 @@ namespace config const constexpr char HASH_KEY_BINNED_REF_SET_MEMBER[] = "binned_refset_member"; const constexpr char HASH_KEY_JAMTIS_VIEWBALANCE_KEY[] = "jamtis_view_balance_key"; - const constexpr char HASH_KEY_JAMTIS_UNLOCKAMOUNTS_KEY[] = "jamtis_unlock_amounts_key"; + const constexpr char HASH_KEY_JAMTIS_VIEWRECEIVED_KEY[] = "jamtis_view_received_key"; const constexpr char HASH_KEY_JAMTIS_GENERATEADDRESS_SECRET[] = "jamtis_generate_address_secret"; const constexpr char HASH_KEY_JAMTIS_CIPHERTAG_SECRET[] = "jamtis_cipher_tag_secret"; - const constexpr char HASH_KEY_JAMTIS_FINDRECEIVED_KEY[] = "jamtis_find_received_key"; + const constexpr char HASH_KEY_JAMTIS_FILTERASSIST_KEY[] = "jamtis_filter_assist_key"; const constexpr char HASH_KEY_JAMTIS_INDEX_EXTENSION_GENERATOR[] = "jamtis_index_extension_generator"; const constexpr char HASH_KEY_JAMTIS_ADDRESS_PRIVKEY[] = "jamtis_address_privkey"; const constexpr char HASH_KEY_JAMTIS_SPENDKEY_EXTENSION_G[] = "jamtis_spendkey_extension_g"; const constexpr char HASH_KEY_JAMTIS_SPENDKEY_EXTENSION_X[] = "jamtis_spendkey_extension_x"; const constexpr char HASH_KEY_JAMTIS_SPENDKEY_EXTENSION_U[] = "jamtis_spendkey_extension_u"; - const constexpr char HASH_KEY_JAMTIS_ADDRESS_TAG_HINT[] = "jamtis_address_tag_hint"; const constexpr char HASH_KEY_JAMTIS_ENCRYPTED_ADDRESS_TAG[] = "jamtis_encrypted_address_tag"; - const constexpr char HASH_KEY_JAMTIS_VIEW_TAG[] = "jamtis_view_tag"; + const constexpr char HASH_KEY_JAMTIS_VIEW_TAG_PRIMARY[] = "jamtis_view_tag_primary"; + const constexpr char HASH_KEY_JAMTIS_VIEW_TAG_COMPLEMENTARY[] = "jamtis_view_tag_complementary"; + const constexpr char HASH_KEY_JAMTIS_VIEW_TAG_AUXILIARY[] = "jamtis_view_tag_auxiliary"; const constexpr char HASH_KEY_JAMTIS_SENDER_RECEIVER_SECRET_PLAIN[] = "jamtis_sr_secret_plain"; - const constexpr char HASH_KEY_JAMTIS_SENDER_RECEIVER_SECRET_SELFSEND_DUMMY[] = "jamtis_selfsend_dummy"; - const constexpr char HASH_KEY_JAMTIS_SENDER_RECEIVER_SECRET_SELFSEND_CHANGE[] = "jamtis_selfsend_change"; - const constexpr char HASH_KEY_JAMTIS_SENDER_RECEIVER_SECRET_SELFSEND_SELF_SPEND[] = "jamtis_selfsend_self_spend"; + const constexpr char HASH_KEY_JAMTIS_SENDER_RECEIVER_SECRET_SELFSEND_EXCLUSIVE[] = "jamtis_selfsend_exclusive_secret"; + const constexpr char HASH_KEY_JAMTIS_SENDER_RECEIVER_SECRET_SELFSEND_AUXILIARY[] = "jamtis_selfsend_auxiliary_secret"; const constexpr char HASH_KEY_JAMTIS_SENDER_ONETIME_ADDRESS_EXTENSION_G[] = "jamtis_sender_extension_g"; const constexpr char HASH_KEY_JAMTIS_SENDER_ONETIME_ADDRESS_EXTENSION_X[] = "jamtis_sender_extension_x"; const constexpr char HASH_KEY_JAMTIS_SENDER_ONETIME_ADDRESS_EXTENSION_U[] = "jamtis_sender_extension_u"; const constexpr char HASH_KEY_JAMTIS_AMOUNT_BAKED_KEY_PLAIN[] = "jamtis_amount_baked_key_plain"; - const constexpr char HASH_KEY_JAMTIS_AMOUNT_BAKED_KEY_SELFSEND[] = "jamtis_amount_baked_key_selfsend"; + const constexpr char HASH_KEY_JAMTIS_AMOUNT_BAKED_KEY_EXCLUSIVE_SELFSPEND[] = "jamtis_amount_baked_key_ex_selfspend"; + const constexpr char HASH_KEY_JAMTIS_AMOUNT_BAKED_KEY_EXCLUSIVE_CHANGE[] = "jamtis_amount_baked_key_ex_change"; + const constexpr char HASH_KEY_JAMTIS_AMOUNT_BAKED_KEY_AUXILIARY_SELFSPEND[] = "jamtis_amount_baked_key_aux_selfspend"; + const constexpr char HASH_KEY_JAMTIS_AMOUNT_BAKED_KEY_AUXILIARY_CHANGE[] = "jamtis_amount_baked_key_aux_change"; const constexpr char HASH_KEY_JAMTIS_AMOUNT_BLINDING_FACTOR[] = "jamtis_amount_commitment_blinding_factor"; const constexpr char HASH_KEY_JAMTIS_ENCODED_AMOUNT_MASK[] = "jamtis_encoded_amount_mask"; const constexpr char HASH_KEY_JAMTIS_INPUT_CONTEXT_COINBASE[] = "jamtis_input_context_coinbase"; diff --git a/src/seraphis_core/CMakeLists.txt b/src/seraphis_core/CMakeLists.txt index 3cf169109e..3cb8602b18 100644 --- a/src/seraphis_core/CMakeLists.txt +++ b/src/seraphis_core/CMakeLists.txt @@ -30,9 +30,9 @@ set(seraphis_core_sources binned_reference_set.cpp binned_reference_set_utils.cpp discretized_fee.cpp + jamtis_account_secrets.cpp jamtis_address_tag_utils.cpp jamtis_address_utils.cpp - jamtis_core_utils.cpp jamtis_destination.cpp jamtis_enote_utils.cpp jamtis_payment_proposal.cpp diff --git a/src/seraphis_core/jamtis_core_utils.cpp b/src/seraphis_core/jamtis_account_secrets.cpp similarity index 67% rename from src/seraphis_core/jamtis_core_utils.cpp rename to src/seraphis_core/jamtis_account_secrets.cpp index 55f3e07dc7..15c291fac9 100644 --- a/src/seraphis_core/jamtis_core_utils.cpp +++ b/src/seraphis_core/jamtis_account_secrets.cpp @@ -27,7 +27,7 @@ // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. //paired header -#include "jamtis_core_utils.h" +#include "jamtis_account_secrets.h" //local headers #include "crypto/crypto.h" @@ -59,43 +59,51 @@ void make_jamtis_viewbalance_key(const crypto::secret_key &k_master, sp_derive_key(to_bytes(k_master), transcript.data(), transcript.size(), to_bytes(k_view_balance_out)); } //------------------------------------------------------------------------------------------------------------------- -void make_jamtis_unlockamounts_key(const crypto::secret_key &k_view_balance, - crypto::x25519_secret_key &xk_unlock_amounts_out) +void make_jamtis_viewreceived_key(const crypto::secret_key &k_view_balance, + crypto::x25519_secret_key &d_view_received_out) { - // xk_ua = H_n_x25519[k_vb]() - SpKDFTranscript transcript{config::HASH_KEY_JAMTIS_UNLOCKAMOUNTS_KEY, 0}; - sp_derive_x25519_key(to_bytes(k_view_balance), transcript.data(), transcript.size(), xk_unlock_amounts_out.data); + // d_vr = H_n_x25519[k_vb]() + SpKDFTranscript transcript{config::HASH_KEY_JAMTIS_VIEWRECEIVED_KEY, 0}; + sp_derive_x25519_key(to_bytes(k_view_balance), transcript.data(), transcript.size(), d_view_received_out.data); } //------------------------------------------------------------------------------------------------------------------- -void make_jamtis_unlockamounts_pubkey(const crypto::x25519_secret_key &xk_unlock_amounts, - crypto::x25519_pubkey &unlockamounts_pubkey_out) +void make_jamtis_exchangebase_pubkey(const crypto::x25519_secret_key &d_view_received, + crypto::x25519_pubkey &exchangebase_pubkey_out) { - // xK_ua = xk_ua * xG - x25519_scmul_base(xk_unlock_amounts, unlockamounts_pubkey_out); + // D_base = d_vr * xG + x25519_scmul_base(d_view_received, exchangebase_pubkey_out); } //------------------------------------------------------------------------------------------------------------------- -void make_jamtis_findreceived_key(const crypto::secret_key &k_view_balance, - crypto::x25519_secret_key &xk_find_received_out) +void make_jamtis_viewreceived_pubkey(const crypto::x25519_secret_key &d_view_received, + const crypto::x25519_pubkey &exchangebase_pubkey, + crypto::x25519_pubkey &viewreceived_pubkey_out) { - // xk_fr = H_n_x25519[k_vb]() - SpKDFTranscript transcript{config::HASH_KEY_JAMTIS_FINDRECEIVED_KEY, 0}; - sp_derive_x25519_key(to_bytes(k_view_balance), transcript.data(), transcript.size(), xk_find_received_out.data); + // D_vr = d_vr * D_base + x25519_scmul_key(d_view_received, exchangebase_pubkey, viewreceived_pubkey_out); } //------------------------------------------------------------------------------------------------------------------- -void make_jamtis_findreceived_pubkey(const crypto::x25519_secret_key &xk_find_received, - const crypto::x25519_pubkey &unlockamounts_pubkey, - crypto::x25519_pubkey &findreceived_pubkey_out) +void make_jamtis_filterassist_key(const crypto::x25519_secret_key &d_view_received, + crypto::x25519_secret_key &d_filter_assist_out) { - // xK_fr = xk_fr * xK_ua - x25519_scmul_key(xk_find_received, unlockamounts_pubkey, findreceived_pubkey_out); + // d_fa = H_n_x25519[d_vr]() + SpKDFTranscript transcript{config::HASH_KEY_JAMTIS_FILTERASSIST_KEY, 0}; + sp_derive_x25519_key(to_bytes(d_view_received), transcript.data(), transcript.size(), d_filter_assist_out.data); } //------------------------------------------------------------------------------------------------------------------- -void make_jamtis_generateaddress_secret(const crypto::secret_key &k_view_balance, +void make_jamtis_filterassist_pubkey(const crypto::x25519_secret_key &d_filter_assist, + const crypto::x25519_pubkey &exchangebase_pubkey, + crypto::x25519_pubkey &filterassist_pubkey_out) +{ + // D_fa = d_fa * D_base + x25519_scmul_key(d_filter_assist, exchangebase_pubkey, filterassist_pubkey_out); +} +//------------------------------------------------------------------------------------------------------------------- +void make_jamtis_generateaddress_secret(const crypto::x25519_secret_key &d_view_received, crypto::secret_key &s_generate_address_out) { - // s_ga = H_32[k_vb]() + // s_ga = H_32[d_vr]() SpKDFTranscript transcript{config::HASH_KEY_JAMTIS_GENERATEADDRESS_SECRET, 0}; - sp_derive_secret(to_bytes(k_view_balance), transcript.data(), transcript.size(), to_bytes(s_generate_address_out)); + sp_derive_secret(to_bytes(d_view_received), transcript.data(), transcript.size(), to_bytes(s_generate_address_out)); } //------------------------------------------------------------------------------------------------------------------- void make_jamtis_ciphertag_secret(const crypto::secret_key &s_generate_address, diff --git a/src/seraphis_core/jamtis_core_utils.h b/src/seraphis_core/jamtis_account_secrets.h similarity index 61% rename from src/seraphis_core/jamtis_core_utils.h rename to src/seraphis_core/jamtis_account_secrets.h index 002061863c..01f5ff0c9a 100644 --- a/src/seraphis_core/jamtis_core_utils.h +++ b/src/seraphis_core/jamtis_account_secrets.h @@ -60,47 +60,56 @@ namespace jamtis void make_jamtis_viewbalance_key(const crypto::secret_key &k_master, crypto::secret_key &k_view_balance_out); /** -* brief: make_jamtis_unlockamounts_key - unlock-amounts key, for recovering amounts and reconstructing amount commitments -* xk_ua = H_n_x25519[k_vb]() +* brief: make_jamtis_viewreceived_key - view-received key, for locating and viewing amounts of normal enotes +* d_vr = H_n_x25519[k_vb]() * param: k_view_balance - k_vb -* outparam: xk_unlock_amounts_out - xk_ua +* outparam: d_view_received_out - d_vr */ -void make_jamtis_unlockamounts_key(const crypto::secret_key &k_view_balance, - crypto::x25519_secret_key &xk_unlock_amounts_out); +void make_jamtis_viewreceived_key(const crypto::secret_key &k_view_balance, + crypto::x25519_secret_key &d_view_received_out); /** -* brief: make_jamtis_unlockamounts_pubkey - xK_ua -* - xK_ua = xk_ua * xG -* param: xk_unlock_amounts - xk_ua -* outparam: unlockamounts_pubkey_out - xK_ua + * brief make_jamtis_exchangebase_pubkey - D_base + * D_base = d_vr * xG + * param: d_view_received - d_vr + * outparam: exchangebase_pubkey_out + */ +void make_jamtis_exchangebase_pubkey(const crypto::x25519_secret_key &d_view_received, + crypto::x25519_pubkey &exchangebase_pubkey_out); +/** +* brief: make_jamtis_viewreceived_pubkey - D_vr +* - D_vr = D_vr * D_base +* param: d_view_received - d_vr +* param: exchangebase_pubkey - D_base +* outparam: viewreceived_pubkey_out - D_vr */ -void make_jamtis_unlockamounts_pubkey(const crypto::x25519_secret_key &xk_unlock_amounts, - crypto::x25519_pubkey &unlockamounts_pubkey_out); +void make_jamtis_viewreceived_pubkey(const crypto::x25519_secret_key &d_view_received, + const crypto::x25519_pubkey &exchangebase_pubkey, + crypto::x25519_pubkey &viewreceived_pubkey_out); /** -* brief: make_jamtis_findreceived_key - find-received key, for finding enotes received by the wallet -* - use to compute view tags and nominal spend keys -* xk_fr = H_n_x25519[k_vb]() -* param: k_view_balance - k_vb -* outparam: xk_find_received_out - xk_fr +* brief: make_jamtis_filterassist_key - filter-assist key, for calculating primary view tags +* d_fa = H_n_x25519[d_vr]() +* param: d_view_received - d_vr +* outparam: d_filter_assist_out - d_fa */ -void make_jamtis_findreceived_key(const crypto::secret_key &k_view_balance, - crypto::x25519_secret_key &xk_find_received_out); +void make_jamtis_filterassist_key(const crypto::x25519_secret_key &d_view_received, + crypto::x25519_secret_key &d_filter_assist_out); /** -* brief: make_jamtis_findreceived_pubkey - xK_fr -* - xK_fr = xk_fr * xK_ua -* param: xk_find_received - xk_fr -* param: unlockamounts_pubkey - xK_ua -* outparam: findreceived_pubkey_out - xK_fr +* brief: make_jamtis_filterassist_pubkey - D_fa +* - D_fa = d_fa * D_base +* param: d_filter_assist - d_fa +* param: exchangebase_pubkey - D_base +* outparam: filterassist_pubky_out - D_fa */ -void make_jamtis_findreceived_pubkey(const crypto::x25519_secret_key &xk_find_received, - const crypto::x25519_pubkey &unlockamounts_pubkey, - crypto::x25519_pubkey &findreceived_pubkey_out); +void make_jamtis_filterassist_pubkey(const crypto::x25519_secret_key &d_filter_assist, + const crypto::x25519_pubkey &exchangebase_pubkey, + crypto::x25519_pubkey &filterassist_pubkey_out); /** * brief: make_jamtis_generateaddress_secret - generate-address secret, for generating addresses -* s_ga = H_32[k_vb]() -* param: k_view_balance - k_vb +* s_ga = H_32[d_vr]() +* param: d_view_received - d_vr * outparam: s_generate_address_out - s_ga */ -void make_jamtis_generateaddress_secret(const crypto::secret_key &k_view_balance, +void make_jamtis_generateaddress_secret(const crypto::x25519_secret_key &d_view_received, crypto::secret_key &s_generate_address_out); /** * brief: make_jamtis_ciphertag_secret - cipher-tag secret, for ciphering address indices to/from address tags diff --git a/src/seraphis_core/jamtis_address_tag_utils.cpp b/src/seraphis_core/jamtis_address_tag_utils.cpp index cd8d08a594..518fc529c9 100644 --- a/src/seraphis_core/jamtis_address_tag_utils.cpp +++ b/src/seraphis_core/jamtis_address_tag_utils.cpp @@ -87,48 +87,8 @@ static encrypted_address_tag_secret_t get_encrypted_address_tag_secret(const rct } //------------------------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------------------------- -static address_tag_hint_t get_address_tag_hint(const crypto::secret_key &cipher_key, - const address_index_t &encrypted_address_index) -{ - static_assert(sizeof(address_tag_hint_t) == 2, ""); - static_assert(sizeof(config::TRANSCRIPT_PREFIX) != 0, ""); - static_assert(sizeof(config::HASH_KEY_JAMTIS_ADDRESS_TAG_HINT) != 0, ""); - - // assemble hash contents: prefix || 'domain-sep' || k || cipher[k](j) - // note: use a raw C-style struct here instead of SpKDFTranscript for maximal performance (the string produced is - // equivalent to what you'd get from SpKDFTranscript) - // note2: '-1' removes the null terminator - struct hash_context_t { - unsigned char prefix[sizeof(config::TRANSCRIPT_PREFIX) - 1]; - unsigned char domain_separator[sizeof(config::HASH_KEY_JAMTIS_ADDRESS_TAG_HINT) - 1]; - rct::key cipher_key; //not crypto::secret_key, which has significant construction cost - address_index_t enc_j; - } hash_context; - static_assert(!epee::has_padding(), ""); - - memcpy(hash_context.prefix, config::TRANSCRIPT_PREFIX, sizeof(config::TRANSCRIPT_PREFIX) - 1); - memcpy(hash_context.domain_separator, - config::HASH_KEY_JAMTIS_ADDRESS_TAG_HINT, - sizeof(config::HASH_KEY_JAMTIS_ADDRESS_TAG_HINT) - 1); - hash_context.cipher_key = rct::sk2rct(cipher_key); - hash_context.enc_j = encrypted_address_index; - - // address_tag_hint = H_2(k, cipher[k](j)) - address_tag_hint_t address_tag_hint; - sp_hash_to_2(&hash_context, sizeof(hash_context), address_tag_hint.bytes); - - // clean up cipher key bytes - memwipe(hash_context.cipher_key.bytes, 32); - - return address_tag_hint; -} -//------------------------------------------------------------------------------------------------------------------- -//------------------------------------------------------------------------------------------------------------------- jamtis_address_tag_cipher_context::jamtis_address_tag_cipher_context(const crypto::secret_key &cipher_key) { - // cache the cipher key - m_cipher_key = cipher_key; - // prepare the Twofish key Twofish_initialise(); Twofish_prepare_key(to_bytes(cipher_key), sizeof(rct::key), &(m_twofish_key)); @@ -141,7 +101,7 @@ jamtis_address_tag_cipher_context::~jamtis_address_tag_cipher_context() //------------------------------------------------------------------------------------------------------------------- address_tag_t jamtis_address_tag_cipher_context::cipher(const address_index_t &j) const { - // address tag = cipher[k](j) || H_2(k, cipher[k](j)) + // address tag = cipher[k](j) // expect address index to fit in one Twofish block (16 bytes) static_assert(sizeof(address_index_t) == TWOFISH_BLOCK_SIZE, ""); @@ -152,29 +112,20 @@ address_tag_t jamtis_address_tag_cipher_context::cipher(const address_index_t &j // encrypt the address index Twofish_encrypt_block(&m_twofish_key, encrypted_j.bytes, encrypted_j.bytes); - // make the address tag hint and complete the address tag - return make_address_tag(encrypted_j, get_address_tag_hint(m_cipher_key, encrypted_j)); + // complete the address tag + return make_address_tag(encrypted_j); } //------------------------------------------------------------------------------------------------------------------- -bool jamtis_address_tag_cipher_context::try_decipher(const address_tag_t &addr_tag, address_index_t &j_out) const +void jamtis_address_tag_cipher_context::decipher(const address_tag_t &addr_tag, address_index_t &j_out) const { static_assert(sizeof(address_index_t) == TWOFISH_BLOCK_SIZE, ""); - static_assert(sizeof(address_index_t) + sizeof(address_tag_hint_t) == sizeof(address_tag_t), ""); + static_assert(sizeof(address_index_t) == sizeof(address_tag_t), ""); // extract the encrypted index memcpy(j_out.bytes, addr_tag.bytes, sizeof(address_index_t)); - // recover the address tag hint - const address_tag_hint_t address_tag_hint{get_address_tag_hint(m_cipher_key, j_out)}; - - // check the address tag hint - if (memcmp(addr_tag.bytes + sizeof(address_index_t), address_tag_hint.bytes, sizeof(address_tag_hint_t)) != 0) - return false; - // decrypt the address index Twofish_decrypt_block(&m_twofish_key, j_out.bytes, j_out.bytes); - - return true; } //------------------------------------------------------------------------------------------------------------------- address_tag_t cipher_address_index(const jamtis_address_tag_cipher_context &cipher_context, const address_index_t &j) @@ -191,14 +142,14 @@ address_tag_t cipher_address_index(const crypto::secret_key &cipher_key, const a return cipher_address_index(cipher_context, j); } //------------------------------------------------------------------------------------------------------------------- -bool try_decipher_address_index(const jamtis_address_tag_cipher_context &cipher_context, +void decipher_address_index(const jamtis_address_tag_cipher_context &cipher_context, const address_tag_t &addr_tag, address_index_t &j_out) { - return cipher_context.try_decipher(addr_tag, j_out); + cipher_context.decipher(addr_tag, j_out); } //------------------------------------------------------------------------------------------------------------------- -bool try_decipher_address_index(const crypto::secret_key &cipher_key, +void decipher_address_index(const crypto::secret_key &cipher_key, const address_tag_t &addr_tag, address_index_t &j_out) { @@ -206,7 +157,7 @@ bool try_decipher_address_index(const crypto::secret_key &cipher_key, const jamtis_address_tag_cipher_context cipher_context{cipher_key}; // decipher it - return try_decipher_address_index(cipher_context, addr_tag, j_out); + decipher_address_index(cipher_context, addr_tag, j_out); } //------------------------------------------------------------------------------------------------------------------- encrypted_address_tag_t encrypt_address_tag(const rct::key &sender_receiver_secret, diff --git a/src/seraphis_core/jamtis_address_tag_utils.h b/src/seraphis_core/jamtis_address_tag_utils.h index 52231cece1..b38f924314 100644 --- a/src/seraphis_core/jamtis_address_tag_utils.h +++ b/src/seraphis_core/jamtis_address_tag_utils.h @@ -67,23 +67,22 @@ struct jamtis_address_tag_cipher_context final //member functions address_tag_t cipher(const address_index_t &j) const; - bool try_decipher(const address_tag_t &addr_tag, address_index_t &j_out) const; + void decipher(const address_tag_t &addr_tag, address_index_t &j_out) const; //member variables private: - crypto::secret_key m_cipher_key; Twofish_key m_twofish_key; }; -/// addr_tag = cipher[k](j) || H_2(k, cipher[k](j)) +/// addr_tag = cipher[k](j) address_tag_t cipher_address_index(const jamtis_address_tag_cipher_context &cipher_context, const address_index_t &j); address_tag_t cipher_address_index(const crypto::secret_key &cipher_key, const address_index_t &j); -/// try to get j from an address tag -bool try_decipher_address_index(const jamtis_address_tag_cipher_context &cipher_context, +/// get j from an address tag +void decipher_address_index(const jamtis_address_tag_cipher_context &cipher_context, const address_tag_t &addr_tag, address_index_t &j_out); -bool try_decipher_address_index(const crypto::secret_key &cipher_key, +void decipher_address_index(const crypto::secret_key &cipher_key, const address_tag_t &addr_tag, address_index_t &j_out); diff --git a/src/seraphis_core/jamtis_address_utils.cpp b/src/seraphis_core/jamtis_address_utils.cpp index a36d1ab28c..79ea514364 100644 --- a/src/seraphis_core/jamtis_address_utils.cpp +++ b/src/seraphis_core/jamtis_address_utils.cpp @@ -33,7 +33,7 @@ #include "crypto/crypto.h" #include "crypto/x25519.h" #include "cryptonote_config.h" -#include "jamtis_core_utils.h" +#include "jamtis_account_secrets.h" #include "jamtis_support_types.h" #include "ringct/rctOps.h" #include "seraphis_crypto/sp_crypto_utils.h" @@ -143,7 +143,7 @@ void make_jamtis_address_privkey(const rct::key &spend_pubkey, crypto::secret_key generator; make_jamtis_index_extension_generator(s_generate_address, j, generator); - // xk^j_a = H_n_x25519(K_s, j, H_32[s_ga](j)) + // d^j_a = H_n_x25519(K_s, j, H_32[s_ga](j)) SpKDFTranscript transcript{config::HASH_KEY_JAMTIS_ADDRESS_PRIVKEY, ADDRESS_INDEX_BYTES}; transcript.append("K_s", spend_pubkey); transcript.append("j", j.bytes); @@ -157,7 +157,7 @@ void make_jamtis_address_spend_key(const rct::key &spend_pubkey, const address_index_t &j, rct::key &address_spendkey_out) { - // K_1 = k^j_g G + k^j_x X + k^j_u U + K_s + // K^j_s = k^j_g G + k^j_x X + k^j_u U + K_s crypto::secret_key address_extension_key_u; crypto::secret_key address_extension_key_x; crypto::secret_key address_extension_key_g; diff --git a/src/seraphis_core/jamtis_address_utils.h b/src/seraphis_core/jamtis_address_utils.h index 2b5c1e4bf1..c20ff13354 100644 --- a/src/seraphis_core/jamtis_address_utils.h +++ b/src/seraphis_core/jamtis_address_utils.h @@ -114,24 +114,24 @@ void make_jamtis_spendkey_extension_u(const rct::key &spend_pubkey, const address_index_t &j, crypto::secret_key &extension_out); /** -* brief: make_jamtis_address_privkey - xk^j_a -* - xk^j_a = H_n_x25519(K_s, j, s^j_gen) +* brief: make_jamtis_address_privkey - d^j_a +* - d^j_a = H_n_x25519(K_s, j, s^j_gen) * param: spend_pubkey - K_s = k_vb X + k_m U * param: s_generate_address - s_ga * param: j - address index -* outparam: address_privkey_out - xk^j_a +* outparam: address_privkey_out - d^j_a */ void make_jamtis_address_privkey(const rct::key &spend_pubkey, const crypto::secret_key &s_generate_address, const address_index_t &j, crypto::x25519_secret_key &address_privkey_out); /** -* brief: make_jamtis_address_spend_key - K_1 -* - K_1 = k^j_g G + k^j_x X + k^j_u U + K_s +* brief: make_jamtis_address_spend_key - K^j_s +* - K^j_s = k^j_g G + k^j_x X + k^j_u U + K_s * param: spend_pubkey - K_s = k_vb X + k_m U * param: s_generate_address - s_ga * param: j - address index -* outparam: address_spendkey_out - K_1 +* outparam: address_spendkey_out - K^j_s */ void make_jamtis_address_spend_key(const rct::key &spend_pubkey, const crypto::secret_key &s_generate_address, diff --git a/src/seraphis_core/jamtis_destination.cpp b/src/seraphis_core/jamtis_destination.cpp index 2e839dc270..815578a7d8 100644 --- a/src/seraphis_core/jamtis_destination.cpp +++ b/src/seraphis_core/jamtis_destination.cpp @@ -32,9 +32,9 @@ //local headers #include "crypto/crypto.h" #include "crypto/x25519.h" +#include "jamtis_account_secrets.h" #include "jamtis_address_tag_utils.h" #include "jamtis_address_utils.h" -#include "jamtis_core_utils.h" #include "jamtis_support_types.h" #include "ringct/rctOps.h" #include "ringct/rctTypes.h" @@ -54,42 +54,50 @@ namespace jamtis //------------------------------------------------------------------------------------------------------------------- bool operator==(const JamtisDestinationV1 &a, const JamtisDestinationV1 &b) { - return (a.addr_K1 == b.addr_K1) && - (a.addr_K2 == b.addr_K2) && - (a.addr_K3 == b.addr_K3) && - (a.addr_tag == b.addr_tag); + return (a.addr_Ks == b.addr_Ks) && + (a.addr_Dfa == b.addr_Dfa) && + (a.addr_Dvr == b.addr_Dvr) && + (a.addr_Dbase == b.addr_Dbase) && + (a.addr_tag == b.addr_tag); } //------------------------------------------------------------------------------------------------------------------- void make_jamtis_destination_v1(const rct::key &spend_pubkey, - const crypto::x25519_pubkey &unlockamounts_pubkey, - const crypto::x25519_pubkey &findreceived_pubkey, + const crypto::x25519_pubkey &filterassist_pubkey, + const crypto::x25519_pubkey &viewreceived_pubkey, + const crypto::x25519_pubkey &exchangebase_pubkey, const crypto::secret_key &s_generate_address, const address_index_t &j, JamtisDestinationV1 &destination_out) { - // K_1 = k^j_g G + k^j_x X + k^j_u U + K_s - make_jamtis_address_spend_key(spend_pubkey, s_generate_address, j, destination_out.addr_K1); + // K^j_s = k^j_g G + k^j_x X + k^j_u U + K_s + make_jamtis_address_spend_key(spend_pubkey, s_generate_address, j, destination_out.addr_Ks); - // xK_2 = xk^j_a xK_fr + // d^j_a = H_n_x25519(K_s, j, s^j_gen) crypto::x25519_secret_key address_privkey; - make_jamtis_address_privkey(spend_pubkey, s_generate_address, j, address_privkey); //xk^j_a + make_jamtis_address_privkey(spend_pubkey, s_generate_address, j, address_privkey); - x25519_scmul_key(address_privkey, findreceived_pubkey, destination_out.addr_K2); + // D^j_fa = d^j_a * D_fa + x25519_scmul_key(address_privkey, filterassist_pubkey, destination_out.addr_Dfa); - // xK_3 = xk^j_a xK_ua - x25519_scmul_key(address_privkey, unlockamounts_pubkey, destination_out.addr_K3); + // D^j_vr = d^j_a * D_vr + x25519_scmul_key(address_privkey, viewreceived_pubkey, destination_out.addr_Dvr); - // addr_tag = cipher[k](j) || H_2(k, cipher[k](j)) + // D^j_base = d^j_a * D_base + x25519_scmul_key(address_privkey, exchangebase_pubkey, destination_out.addr_Dbase); + + // s_ct = H32[s_ga]() crypto::secret_key ciphertag_secret; make_jamtis_ciphertag_secret(s_generate_address, ciphertag_secret); + // addr_tag = cipher[s_ct](j) destination_out.addr_tag = cipher_address_index(ciphertag_secret, j); } //------------------------------------------------------------------------------------------------------------------- bool try_get_jamtis_index_from_destination_v1(const JamtisDestinationV1 &destination, const rct::key &spend_pubkey, - const crypto::x25519_pubkey &unlockamounts_pubkey, - const crypto::x25519_pubkey &findreceived_pubkey, + const crypto::x25519_pubkey &filterassist_pubkey, + const crypto::x25519_pubkey &viewreceived_pubkey, + const crypto::x25519_pubkey &exchangebase_pubkey, const crypto::secret_key &s_generate_address, address_index_t &j_out) { @@ -99,15 +107,15 @@ bool try_get_jamtis_index_from_destination_v1(const JamtisDestinationV1 &destina // get the nominal address index from the destination's address tag address_index_t nominal_address_index; - if (!try_decipher_address_index(ciphertag_secret, destination.addr_tag, nominal_address_index)) - return false; + decipher_address_index(ciphertag_secret, destination.addr_tag, nominal_address_index); // recreate the destination JamtisDestinationV1 test_destination; make_jamtis_destination_v1(spend_pubkey, - unlockamounts_pubkey, - findreceived_pubkey, + filterassist_pubkey, + viewreceived_pubkey, + exchangebase_pubkey, s_generate_address, nominal_address_index, test_destination); @@ -124,9 +132,10 @@ bool try_get_jamtis_index_from_destination_v1(const JamtisDestinationV1 &destina JamtisDestinationV1 gen_jamtis_destination_v1() { JamtisDestinationV1 temp; - temp.addr_K1 = rct::pkGen(); - temp.addr_K2 = crypto::x25519_pubkey_gen(); - temp.addr_K3 = crypto::x25519_pubkey_gen(); + temp.addr_Ks = rct::pkGen(); + temp.addr_Dfa = crypto::x25519_pubkey_gen(); + temp.addr_Dvr = crypto::x25519_pubkey_gen(); + temp.addr_Dbase = crypto::x25519_pubkey_gen(); crypto::rand(sizeof(address_tag_t), temp.addr_tag.bytes); return temp; diff --git a/src/seraphis_core/jamtis_destination.h b/src/seraphis_core/jamtis_destination.h index a1961e34af..8436127bf1 100644 --- a/src/seraphis_core/jamtis_destination.h +++ b/src/seraphis_core/jamtis_destination.h @@ -54,12 +54,14 @@ namespace jamtis /// struct JamtisDestinationV1 final { - /// K_1 = k^j_g G + k^j_x X + k^j_u U + K_s (address spend key) - rct::key addr_K1; - /// xK_2 = xk^j_a xK_fr (address view key) - crypto::x25519_pubkey addr_K2; - /// xK_3 = xk^j_a xK_ua (DH base key) - crypto::x25519_pubkey addr_K3; + /// K^j_s = k^j_g G + k^j_x X + k^j_u U + K_s (address spend key) + rct::key addr_Ks; + /// D^j_fa = d^j_a * D_fa (address filter-assist key) + crypto::x25519_pubkey addr_Dfa; + /// D^j_vr = d^j_a * D_vr (address view-received key) + crypto::x25519_pubkey addr_Dvr; + /// D^j_base = d^j_a * D_base (address DH base key) + crypto::x25519_pubkey addr_Dbase; /// addr_tag address_tag_t addr_tag; }; @@ -70,15 +72,17 @@ bool operator==(const JamtisDestinationV1 &a, const JamtisDestinationV1 &b); /** * brief: make_jamtis_destination_v1 - make a destination address * param: spend_pubkey - K_s = k_vb X + k_m U -* param: unlockamounts_pubkey - xK_ua = xk_ua xG -* param: findreceived_pubkey - xK_fr = xk_fr xk_ua xG +* param: filterassist_pubkey - D_fa = d_fa D_base +* param: viewreceived_pubkey - D_vr = d_vr D_base +* param: exchangebase_pubkey - D_base = d_vr xG * param: s_generate_address - s_ga * param: j - address_index * outparam: destination_out - the full address, with address tag */ void make_jamtis_destination_v1(const rct::key &spend_pubkey, - const crypto::x25519_pubkey &unlockamounts_pubkey, - const crypto::x25519_pubkey &findreceived_pubkey, + const crypto::x25519_pubkey &filterassist_pubkey, + const crypto::x25519_pubkey &viewreceived_pubkey, + const crypto::x25519_pubkey &exchangebase_pubkey, const crypto::secret_key &s_generate_address, const address_index_t &j, JamtisDestinationV1 &destination_out); @@ -87,16 +91,18 @@ void make_jamtis_destination_v1(const rct::key &spend_pubkey, * - note: partial-recreation of a destination will return FALSE * param: destination - destination address to recreate * param: spend_pubkey - K_s -* param: unlockamounts_pubkey - xK_ua = xk_ua xG -* param: findreceived_pubkey - xK_fr = xk_fr xk_ua xG +* param: filterassist_pubkey - D_fa = d_fa xG +* param: viewreceived_pubkey - D_vr = d_vr xG +* param: exchangebase_pubkey - D_base = d_vr xG * param: s_generate_address - s_ga * outparam: j_out - address index (if successful) * return: true if the destination can be recreated */ bool try_get_jamtis_index_from_destination_v1(const JamtisDestinationV1 &destination, const rct::key &spend_pubkey, - const crypto::x25519_pubkey &unlockamounts_pubkey, - const crypto::x25519_pubkey &findreceived_pubkey, + const crypto::x25519_pubkey &filterassist_pubkey, + const crypto::x25519_pubkey &viewreceived_pubkey, + const crypto::x25519_pubkey &exchangebase_pubkey, const crypto::secret_key &s_generate_address, address_index_t &j_out); /** diff --git a/src/seraphis_core/jamtis_enote_utils.cpp b/src/seraphis_core/jamtis_enote_utils.cpp index bdfa35f953..ecb477ea82 100644 --- a/src/seraphis_core/jamtis_enote_utils.cpp +++ b/src/seraphis_core/jamtis_enote_utils.cpp @@ -81,25 +81,25 @@ static auto make_derivation_with_wiper(const crypto::x25519_secret_key &privkey, } //------------------------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------------------------- -static const boost::string_ref selfsend_sender_receiver_secret_domain_separator(const JamtisSelfSendType self_send_type) +static const boost::string_ref selfsend_sender_receiver_secret_domain_separator(const bool auxiliary_self_spend) { - CHECK_AND_ASSERT_THROW_MES(self_send_type <= JamtisSelfSendType::MAX, - "jamtis self-send sender-receiver secret: unknown self-send type."); - - // dummy self-send - if (self_send_type == JamtisSelfSendType::DUMMY) - return config::HASH_KEY_JAMTIS_SENDER_RECEIVER_SECRET_SELFSEND_DUMMY; - - // change self-send - if (self_send_type == JamtisSelfSendType::CHANGE) - return config::HASH_KEY_JAMTIS_SENDER_RECEIVER_SECRET_SELFSEND_CHANGE; - - // self-spend self-send - if (self_send_type == JamtisSelfSendType::SELF_SPEND) - return config::HASH_KEY_JAMTIS_SENDER_RECEIVER_SECRET_SELFSEND_SELF_SPEND; - - CHECK_AND_ASSERT_THROW_MES(false, "jamtis self-send sender-receiver secret domain separator error."); - return ""; + return auxiliary_self_spend ? + config::HASH_KEY_JAMTIS_SENDER_RECEIVER_SECRET_SELFSEND_AUXILIARY : + config::HASH_KEY_JAMTIS_SENDER_RECEIVER_SECRET_SELFSEND_EXCLUSIVE; +} +//------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------------------------------------------- +static const boost::string_ref selfsend_amount_baked_key_domain_separator(const JamtisSelfSendType self_send_type) +{ + switch (self_send_type) + { + case JamtisSelfSendType::EXCLUSIVE_SELF_SPEND: return config::HASH_KEY_JAMTIS_AMOUNT_BAKED_KEY_EXCLUSIVE_SELFSPEND; + case JamtisSelfSendType::EXCLUSIVE_CHANGE: return config::HASH_KEY_JAMTIS_AMOUNT_BAKED_KEY_EXCLUSIVE_CHANGE; + case JamtisSelfSendType::AUXILIARY_SELF_SPEND: return config::HASH_KEY_JAMTIS_AMOUNT_BAKED_KEY_AUXILIARY_SELFSPEND; + case JamtisSelfSendType::AUXILIARY_CHANGE: return config::HASH_KEY_JAMTIS_AMOUNT_BAKED_KEY_AUXILIARY_CHANGE; + default: + throw std::logic_error("bug: unexpected self-send type when searching for domain seperator"); + } } //------------------------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------------------------- @@ -153,39 +153,88 @@ static void make_jamtis_amount_baked_key_plain(const crypto::x25519_pubkey &reve } //------------------------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------------------------- +static inline std::uint32_t vttou32(view_tag_t vt) +{ + // Interpret view tag as little-endian unsigned int bytes, returning uint32_t + std::uint32_t u32; + memcpy(&u32, vt.bytes, VIEW_TAG_BYTES); + return SWAP32LE(u32); +} +//------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------------------------------------------- +static void make_jamtis_naked_primary_view_tag(const crypto::x25519_pubkey &dhe_fa, + const rct::key &onetime_address, + view_tag_t &naked_primary_view_tag_out) +{ + static_assert(VIEW_TAG_BYTES == 2, "sp_hash_to_2/VIEW_TAG_BYTES output mismatch"); + + // H_2(D^d_fa, Ko) + SpKDFTranscript transcript{config::HASH_KEY_JAMTIS_VIEW_TAG_PRIMARY, 2*sizeof(rct::key)}; + transcript.append("D^d_fa", dhe_fa); + transcript.append("Ko", onetime_address); + sp_hash_to_2(transcript.data(), transcript.size(), naked_primary_view_tag_out.bytes); +} +//------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------------------------------------------- +static void make_jamtis_naked_complementary_view_tag(const rct::key &sender_receiver_secret, + view_tag_t &naked_complementary_view_tag_out) +{ + static_assert(VIEW_TAG_BYTES == 2, "sp_hash_to_2/VIEW_TAG_BYTES output mismatch"); + + // H_2(q) + SpKDFTranscript transcript{config::HASH_KEY_JAMTIS_VIEW_TAG_COMPLEMENTARY, sizeof(rct::key)}; + transcript.append("q", sender_receiver_secret); + sp_hash_to_2(transcript.data(), transcript.size(), naked_complementary_view_tag_out.bytes); +} +//------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------------------------------------------- void make_jamtis_enote_ephemeral_pubkey(const crypto::x25519_secret_key &enote_ephemeral_privkey, - const crypto::x25519_pubkey &DH_base, + const crypto::x25519_pubkey &addr_Dbase, crypto::x25519_pubkey &enote_ephemeral_pubkey_out) { - // xK_e = xr xK_3 - x25519_scmul_key(enote_ephemeral_privkey, DH_base, enote_ephemeral_pubkey_out); + // D_e = xr D^j_base + x25519_scmul_key(enote_ephemeral_privkey, addr_Dbase, enote_ephemeral_pubkey_out); } //------------------------------------------------------------------------------------------------------------------- -void make_jamtis_view_tag(const crypto::x25519_pubkey &sender_receiver_DH_derivation, +void make_jamtis_standard_view_tag(const crypto::x25519_pubkey &dhe_fa, const rct::key &onetime_address, + const rct::key &sender_receiver_secret, + const std::uint8_t num_primary_view_tag_bits, view_tag_t &view_tag_out) { - static_assert(sizeof(view_tag_t) == 1, ""); + CHECK_AND_ASSERT_THROW_MES(num_primary_view_tag_bits <= 8 * VIEW_TAG_BYTES, + "num_primary_view_tag_bits is bigger than the size of the view tag"); - // view_tag = H_1(xK_d, Ko) - SpKDFTranscript transcript{config::HASH_KEY_JAMTIS_VIEW_TAG, 2*sizeof(rct::key)}; - transcript.append("xK_d", sender_receiver_DH_derivation); - transcript.append("Ko", onetime_address); + // naked_primary_view_tag = H_2(D^d_fa, Ko) + view_tag_t naked_primary_view_tag; + make_jamtis_naked_primary_view_tag(dhe_fa, onetime_address, naked_primary_view_tag); + + // naked_complementary_view_tag = H_2(q) + view_tag_t naked_complementary_view_tag; + make_jamtis_naked_complementary_view_tag(sender_receiver_secret, naked_complementary_view_tag); + + const std::uint32_t primary_mask = (1 << num_primary_view_tag_bits) - 1; + const std::uint32_t comp_mask = ~primary_mask; + + // view_tag = naked_primary_view_tag[:npbits] || naked_complementary_view_tag[:ncbits] + std::uint32_t combined_view_tag_u32 = (vttou32(naked_primary_view_tag) & primary_mask) | + ((vttou32(naked_complementary_view_tag) << num_primary_view_tag_bits) & comp_mask); - sp_hash_to_1(transcript.data(), transcript.size(), &view_tag_out); + combined_view_tag_u32 = SWAP32LE(combined_view_tag_u32); + memcpy(view_tag_out.bytes, &combined_view_tag_u32, VIEW_TAG_BYTES); } //------------------------------------------------------------------------------------------------------------------- -void make_jamtis_view_tag(const crypto::x25519_secret_key &privkey, - const crypto::x25519_pubkey &DH_key, +void make_jamtis_auxiliary_view_tag(const crypto::secret_key &k_view_balance, const rct::key &onetime_address, view_tag_t &view_tag_out) { - // xK_d = privkey * DH_key - crypto::x25519_pubkey derivation; - auto a_wiper = make_derivation_with_wiper(privkey, DH_key, derivation); + // H_2[k_vb](Ko) + SpKDFTranscript transcript{config::HASH_KEY_JAMTIS_VIEW_TAG_AUXILIARY, sizeof(rct::key)}; + transcript.append("Ko", onetime_address); - // view_tag = H_1(xK_d, Ko) - make_jamtis_view_tag(derivation, onetime_address, view_tag_out); + std::uint8_t aux_view_tag_buffer[32]; + sp_derive_secret(to_bytes(k_view_balance), transcript.data(), transcript.size(), aux_view_tag_buffer); + memcpy(view_tag_out.bytes, aux_view_tag_buffer, VIEW_TAG_BYTES); } //------------------------------------------------------------------------------------------------------------------- void make_jamtis_input_context_coinbase(const std::uint64_t block_height, rct::key &input_context_out) @@ -219,15 +268,15 @@ void make_jamtis_input_context_standard(const std::vector &le sp_hash_to_32(transcript.data(), transcript.size(), input_context_out.bytes); } //------------------------------------------------------------------------------------------------------------------- -void make_jamtis_sender_receiver_secret_plain(const crypto::x25519_pubkey &sender_receiver_DH_derivation, +void make_jamtis_sender_receiver_secret_plain(const crypto::x25519_pubkey &dhe_vr, const crypto::x25519_pubkey &enote_ephemeral_pubkey, const rct::key &input_context, rct::key &sender_receiver_secret_out) { - // q = H_32(xK_d, xK_e, input_context) + // q = H_32(D^d_vr, D_e, input_context) SpKDFTranscript transcript{config::HASH_KEY_JAMTIS_SENDER_RECEIVER_SECRET_PLAIN, 3*sizeof(rct::key)}; - transcript.append("xK_d", sender_receiver_DH_derivation); - transcript.append("xK_e", enote_ephemeral_pubkey); + transcript.append("D^d_vr", dhe_vr); + transcript.append("D_e", enote_ephemeral_pubkey); transcript.append("input_context", input_context); sp_hash_to_32(transcript.data(), transcript.size(), sender_receiver_secret_out.bytes); @@ -239,11 +288,11 @@ void make_jamtis_sender_receiver_secret_plain(const crypto::x25519_secret_key &p const rct::key &input_context, rct::key &sender_receiver_secret_out) { - // xK_d = privkey * DH_key + // D^d_vr = privkey * DH_key crypto::x25519_pubkey derivation; auto a_wiper = make_derivation_with_wiper(privkey, DH_key, derivation); - // q = H_32(xK_d, xK_e, input_context) + // q = H_32(D^d_vr, D_e, input_context) make_jamtis_sender_receiver_secret_plain(derivation, enote_ephemeral_pubkey, input_context, @@ -253,12 +302,12 @@ void make_jamtis_sender_receiver_secret_plain(const crypto::x25519_secret_key &p void make_jamtis_sender_receiver_secret_selfsend(const crypto::secret_key &k_view_balance, const crypto::x25519_pubkey &enote_ephemeral_pubkey, const rct::key &input_context, - const JamtisSelfSendType self_send_type, + const bool is_auxiliary_self_send_type, rct::key &sender_receiver_secret_out) { - // q = H_32[k_vb](xK_e, input_context) - SpKDFTranscript transcript{selfsend_sender_receiver_secret_domain_separator(self_send_type), 2*sizeof(rct::key)}; - transcript.append("xK_e", enote_ephemeral_pubkey); + // q = H_32[k_vb](D_e, input_context) + SpKDFTranscript transcript{selfsend_sender_receiver_secret_domain_separator(is_auxiliary_self_send_type), 2*sizeof(rct::key)}; + transcript.append("D_e", enote_ephemeral_pubkey); transcript.append("input_context", input_context); sp_derive_secret(to_bytes(k_view_balance), transcript.data(), transcript.size(), sender_receiver_secret_out.bytes); @@ -269,9 +318,9 @@ void make_jamtis_onetime_address_extension_g(const rct::key &recipient_address_s const rct::key &amount_commitment, crypto::secret_key &sender_extension_out) { - // k_{g, sender} = H_n("..g..", K_1, q, C) + // k_{g, sender} = H_n("..g..", K^j_s, q, C) SpKDFTranscript transcript{config::HASH_KEY_JAMTIS_SENDER_ONETIME_ADDRESS_EXTENSION_G, 3*sizeof(rct::key)}; - transcript.append("K_1", recipient_address_spend_key); + transcript.append("K^j_s", recipient_address_spend_key); transcript.append("q", sender_receiver_secret); transcript.append("C", amount_commitment); @@ -283,9 +332,9 @@ void make_jamtis_onetime_address_extension_x(const rct::key &recipient_address_s const rct::key &amount_commitment, crypto::secret_key &sender_extension_out) { - // k_{x, sender} = H_n("..x..", K_1, q, C) + // k_{x, sender} = H_n("..x..", K^j_s, q, C) SpKDFTranscript transcript{config::HASH_KEY_JAMTIS_SENDER_ONETIME_ADDRESS_EXTENSION_X, 3*sizeof(rct::key)}; - transcript.append("K_1", recipient_address_spend_key); + transcript.append("K^j_s", recipient_address_spend_key); transcript.append("q", sender_receiver_secret); transcript.append("C", amount_commitment); @@ -297,9 +346,9 @@ void make_jamtis_onetime_address_extension_u(const rct::key &recipient_address_s const rct::key &amount_commitment, crypto::secret_key &sender_extension_out) { - // k_{u, sender} = H_n("..u..", K_1, q, C) + // k_{u, sender} = H_n("..u..", K^j_s, q, C) SpKDFTranscript transcript{config::HASH_KEY_JAMTIS_SENDER_ONETIME_ADDRESS_EXTENSION_U, 3*sizeof(rct::key)}; - transcript.append("K_1", recipient_address_spend_key); + transcript.append("K^j_s", recipient_address_spend_key); transcript.append("q", sender_receiver_secret); transcript.append("C", amount_commitment); @@ -311,7 +360,7 @@ void make_jamtis_onetime_address(const rct::key &recipient_address_spend_key, const rct::key &amount_commitment, rct::key &onetime_address_out) { - // Ko = k^o_g G + k^o_x X + k^o_u U + K_1 + // Ko = k^o_g G + k^o_x X + k^o_u U + K^j_s crypto::secret_key extension_g; crypto::secret_key extension_x; crypto::secret_key extension_u; @@ -328,46 +377,45 @@ void make_jamtis_onetime_address(const rct::key &recipient_address_spend_key, amount_commitment, extension_u); //k^o_u - onetime_address_out = recipient_address_spend_key; //K_1 - extend_seraphis_spendkey_u(extension_u, onetime_address_out); //k^o_u U + K_1 - extend_seraphis_spendkey_x(extension_x, onetime_address_out); //k^o_x X + k^o_u U + K_1 + onetime_address_out = recipient_address_spend_key; //K^j_s + extend_seraphis_spendkey_u(extension_u, onetime_address_out); //k^o_u U + K^j_s + extend_seraphis_spendkey_x(extension_x, onetime_address_out); //k^o_x X + k^o_u U + K^j_s mask_key(extension_g, onetime_address_out, - onetime_address_out); //k^o_g G + k^o_x X + k^o_u U + K_1 + onetime_address_out); //k^o_g G + k^o_x X + k^o_u U + K^j_s } //------------------------------------------------------------------------------------------------------------------- void make_jamtis_amount_baked_key_plain_sender(const crypto::x25519_secret_key &enote_ephemeral_privkey, rct::key &baked_key_out) { - // xR = xr xG - crypto::x25519_pubkey reverse_sender_receiver_secret; - crypto::x25519_scmul_base(enote_ephemeral_privkey, reverse_sender_receiver_secret); + // dhe_inv = xR = xr xG + crypto::x25519_pubkey dhe_inv; + crypto::x25519_scmul_base(enote_ephemeral_privkey, dhe_inv); - // H_32(xR) - make_jamtis_amount_baked_key_plain(reverse_sender_receiver_secret, baked_key_out); + // H_32(dhe_inv) + make_jamtis_amount_baked_key_plain(dhe_inv, baked_key_out); } //------------------------------------------------------------------------------------------------------------------- void make_jamtis_amount_baked_key_plain_recipient(const crypto::x25519_secret_key &address_privkey, - const crypto::x25519_secret_key &xk_unlock_amounts, + const crypto::x25519_secret_key &d_view_received, const crypto::x25519_pubkey &enote_ephemeral_pubkey, rct::key &baked_key_out) { - // xR = (1/(xk^j_a * xk_ua)) * xK_e = xr xG - crypto::x25519_pubkey reverse_sender_receiver_secret; - crypto::x25519_invmul_key({address_privkey, xk_unlock_amounts}, - enote_ephemeral_pubkey, - reverse_sender_receiver_secret); + // dhe_inv = 1/(d^j_a * d_vr) * D_e = xr xG + crypto::x25519_pubkey dhe_inv; + crypto::x25519_invmul_key({address_privkey, d_view_received}, enote_ephemeral_pubkey, dhe_inv); - // H_32(xR) - make_jamtis_amount_baked_key_plain(reverse_sender_receiver_secret, baked_key_out); + // H_32(dhe_inv) + make_jamtis_amount_baked_key_plain(dhe_inv, baked_key_out); } //------------------------------------------------------------------------------------------------------------------- void make_jamtis_amount_baked_key_selfsend(const crypto::secret_key &k_view_balance, const rct::key &sender_receiver_secret, + const JamtisSelfSendType self_send_type, rct::key &baked_key_out) { // [selfsend] baked_key = H_32[k_vb](q) - SpKDFTranscript transcript{config::HASH_KEY_JAMTIS_AMOUNT_BAKED_KEY_SELFSEND, sizeof(rct::key)}; + SpKDFTranscript transcript{selfsend_amount_baked_key_domain_separator(self_send_type), sizeof(rct::key)}; transcript.append("q", sender_receiver_secret); sp_derive_secret(to_bytes(k_view_balance), transcript.data(), transcript.size(), baked_key_out.bytes); @@ -417,27 +465,73 @@ bool test_jamtis_onetime_address(const rct::key &recipient_address_spend_key, return nominal_onetime_address == expected_onetime_address; } //------------------------------------------------------------------------------------------------------------------- -bool try_get_jamtis_sender_receiver_secret_plain(const crypto::x25519_pubkey &sender_receiver_DH_derivation, +bool test_jamtis_primary_view_tag(const crypto::x25519_pubkey &dhe_fa, + const rct::key &onetime_address, + const view_tag_t view_tag, + const std::uint8_t num_primary_view_tag_bits) +{ + // npbits can't be greater than total tag size (duh) + CHECK_AND_ASSERT_THROW_MES(num_primary_view_tag_bits <= 8 * VIEW_TAG_BYTES, + "num_primary_view_tag_bits is too large: " << num_primary_view_tag_bits); + + // primary_view_tag' = H_2(D^d_fa, Ko) + view_tag_t naked_primary_view_tag; + make_jamtis_naked_primary_view_tag(dhe_fa, + onetime_address, + naked_primary_view_tag); + + // primary_view_tag' ?= primary_view_tag + const std::uint32_t partial_recomputed_view_tag = vttou32(naked_primary_view_tag); + const std::uint32_t primary_mask = (1 << num_primary_view_tag_bits) - 1; + return 0 == ((partial_recomputed_view_tag ^ vttou32(view_tag)) & primary_mask); +} +//------------------------------------------------------------------------------------------------------------------- +bool test_jamtis_primary_view_tag(const crypto::x25519_secret_key &d_filter_assist, const crypto::x25519_pubkey &enote_ephemeral_pubkey, - const rct::key &input_context, const rct::key &onetime_address, const view_tag_t view_tag, - rct::key &sender_receiver_secret_out) + const std::uint8_t num_primary_view_tag_bits) { - // recompute view tag and check that it matches; short-circuit on failure - view_tag_t recomputed_view_tag; - make_jamtis_view_tag(sender_receiver_DH_derivation, onetime_address, recomputed_view_tag); - - if (recomputed_view_tag != view_tag) - return false; - - // q (normal derivation path) - make_jamtis_sender_receiver_secret_plain(sender_receiver_DH_derivation, - enote_ephemeral_pubkey, - input_context, - sender_receiver_secret_out); + // D^d_fa = d_fa D_e + crypto::x25519_pubkey dhe_fa; + crypto::x25519_scmul_key(d_filter_assist, enote_ephemeral_pubkey, dhe_fa); + + return test_jamtis_primary_view_tag(dhe_fa, + onetime_address, + view_tag, + num_primary_view_tag_bits); +} +//------------------------------------------------------------------------------------------------------------------- +bool test_jamtis_complementary_view_tag(const rct::key &sender_receiver_secret, + const view_tag_t view_tag, + const std::uint8_t num_primary_view_tag_bits) +{ + // npbits can't be greater than total tag size (duh) + CHECK_AND_ASSERT_THROW_MES(num_primary_view_tag_bits <= 8 * VIEW_TAG_BYTES, + "num_primary_view_tag_bits is too large: " << num_primary_view_tag_bits); + + // complementary_view_tag' = H_2(q) + view_tag_t naked_complementary_view_tag; + make_jamtis_naked_complementary_view_tag(sender_receiver_secret, + naked_complementary_view_tag); + + // complementary_view_tag' ?= complementary_view_tag + const std::uint32_t ncbits = 8 * VIEW_TAG_BYTES - num_primary_view_tag_bits; + const std::uint32_t complementary_mask = ((1ul << ncbits) - 1) << num_primary_view_tag_bits; + const std::uint32_t partial_recomputed_view_tag = vttou32(naked_complementary_view_tag) << num_primary_view_tag_bits; + return 0 == ((partial_recomputed_view_tag ^ vttou32(view_tag)) & complementary_mask); +} +//------------------------------------------------------------------------------------------------------------------- +bool test_jamtis_auxiliary_view_tag(const crypto::secret_key &k_view_balance, + const rct::key &onetime_address, + const view_tag_t view_tag) +{ + // view_tag' = H_2[k_vb](Ko) + view_tag_t auxiliary_view_tag; + make_jamtis_auxiliary_view_tag(k_view_balance, onetime_address, auxiliary_view_tag); - return true; + // view_tag' ?= view_tag + return auxiliary_view_tag == view_tag; } //------------------------------------------------------------------------------------------------------------------- bool try_get_jamtis_amount(const rct::key &sender_receiver_secret, diff --git a/src/seraphis_core/jamtis_enote_utils.h b/src/seraphis_core/jamtis_enote_utils.h index adcf6612f1..5e6408cfe2 100644 --- a/src/seraphis_core/jamtis_enote_utils.h +++ b/src/seraphis_core/jamtis_enote_utils.h @@ -26,7 +26,7 @@ // 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. -// Utilities for making and handling enotes with jamtis. +// @file Utilities for making and handling enotes with jamtis. #pragma once @@ -49,35 +49,37 @@ namespace jamtis { /** -* brief: make_jamtis_enote_ephemeral_pubkey - enote ephemeral pubkey xK_e -* xK_e = xr xK_3 +* brief: make_jamtis_enote_ephemeral_pubkey - enote ephemeral pubkey D_e +* D_e = xr D^j_base * param: enote_ephemeral_privkey - xr -* param: DH_base - xK_3 -* outparam: enote_ephemeral_pubkey_out - xK_e +* param: addr_Dbase - D^j_base +* outparam: enote_ephemeral_pubkey_out - D_e */ void make_jamtis_enote_ephemeral_pubkey(const crypto::x25519_secret_key &enote_ephemeral_privkey, - const crypto::x25519_pubkey &DH_base, + const crypto::x25519_pubkey &addr_Dbase, crypto::x25519_pubkey &enote_ephemeral_pubkey_out); /** -* brief: make_jamtis_view_tag - view tag for optimized identification of owned enotes -* view_tag = H_1(xK_d, Ko) -* param: sender_receiver_DH_derivation - xK_d +* brief: make_jamtis_standard_view_tag - used for optimized identification of plain enotes and exclusive self-send enotes +* view_tag = H_npbits(D^d_fa, Ko) || H_ncbits(q) +* param: dhe_fa - D^d_fa * param: onetime_address - Ko +* param: sender_receiver_secret - q +* param: num_primary_view_tag_bits - npbits * outparam: view_tag_out - view_tag */ -void make_jamtis_view_tag(const crypto::x25519_pubkey &sender_receiver_DH_derivation, +void make_jamtis_standard_view_tag(const crypto::x25519_pubkey &dhe_fa, const rct::key &onetime_address, + const rct::key &sender_receiver_secret, + const std::uint8_t num_primary_view_tag_bits, view_tag_t &view_tag_out); /** -* brief: make_jamtis_view_tag - view tag for optimized identification of owned enotes -* view_tag = H_1(privkey * DH_key, Ko) -* param: privkey - [sender: xr] [recipient: xk_fr] -* param: DH_key - [sender: xK_2] [sender-selfsend-2out: xk_fr * xK_3_other] [recipient: xK_e = xr xK_3] +* brief: make_jamtis_auxiliary_view_tag - used for optimized identification of auxiliary self-send enotes +* view_tag = H_2[k_vb](Ko) +* param: k_view_balance - k_vb * param: onetime_address - Ko * outparam: view_tag_out - view_tag */ -void make_jamtis_view_tag(const crypto::x25519_secret_key &privkey, - const crypto::x25519_pubkey &DH_key, +void make_jamtis_auxiliary_view_tag(const crypto::secret_key &k_view_balance, const rct::key &onetime_address, view_tag_t &view_tag_out); /** @@ -99,23 +101,23 @@ void make_jamtis_input_context_standard(const std::vector &le rct::key &input_context_out); /** * brief: make_jamtis_sender_receiver_secret_plain - sender-receiver secret q for a normal enote -* q = H_32(xK_d, xK_e, input_context) -* param: sender_receiver_DH_derivation - xK_d = xr xK_2 = k_fr xK_e -* param: enote_ephemeral_pubkey - xK_e +* q = H_32(D^d_vr, D_e, input_context) +* param: dhe_vr - D^d_vr = xr D^j_vr = d_vr D_e +* param: enote_ephemeral_pubkey - D_e * param: input_context - [normal: H_32({legacy KI}, {seraphis KI})] [coinbase: H_32(block height)] * outparam: sender_receiver_secret_out - q * - note: this is 'rct::key' instead of 'crypto::secret_key' for better performance in multithreaded environments */ -void make_jamtis_sender_receiver_secret_plain(const crypto::x25519_pubkey &sender_receiver_DH_derivation, +void make_jamtis_sender_receiver_secret_plain(const crypto::x25519_pubkey &dhe_vr, const crypto::x25519_pubkey &enote_ephemeral_pubkey, const rct::key &input_context, rct::key &sender_receiver_secret_out); /** * brief: make_jamtis_sender_receiver_secret_plain - sender-receiver secret q for a normal enote -* q = H_32(xr * xk_fr * xG, input_context) => H_32(privkey * DH_key, input_context) -* param: privkey - [sender: xr] [recipient: xk_fr] -* param: DH_key - [sender: xK_2] [recipient: xK_e = xr xK_3] -* param: enote_ephemeral_pubkey - xK_e +* q = H_32(xr * d_vr * xG, D_e, input_context) => H_32(privkey * DH_key, D_e, input_context) +* param: privkey - [sender: xr] [recipient: d_vr] +* param: DH_key - [sender: D^j_vr] [recipient: D_e = xr D^j_base] +* param: enote_ephemeral_pubkey - D_e * param: input_context - [normal: H_32({legacy KI}, {seraphis KI})] [coinbase: H_32(block height)] * outparam: sender_receiver_secret_out - q * - note: this is 'rct::key' instead of 'crypto::secret_key' for better performance in multithreaded environments @@ -127,24 +129,24 @@ void make_jamtis_sender_receiver_secret_plain(const crypto::x25519_secret_key &p rct::key &sender_receiver_secret_out); /** * brief: make_jamtis_sender_receiver_secret_selfsend - sender-receiver secret q for a self-send enote of a specific type -* q = H_32[k_vb](xK_e, input_context) +* q = H_32[k_vb](D_e, input_context) * param: k_view_balance - k_vb -* param: enote_ephemeral_pubkey - xK_e +* param: enote_ephemeral_pubkey - D_e * param: input_context - [normal: H_32({legacy KI}, {seraphis KI})] [coinbase: H_32(block height)] -* param: self_send_type - type of the self-send enote, used to select the domain separator +* param: is_auxiliary_self_send_type - is_aux * outparam: sender_receiver_secret_out - q * - note: this is 'rct::key' instead of 'crypto::secret_key' for better performance in multithreaded environments */ void make_jamtis_sender_receiver_secret_selfsend(const crypto::secret_key &k_view_balance, const crypto::x25519_pubkey &enote_ephemeral_pubkey, const rct::key &input_context, - const JamtisSelfSendType self_send_type, + const bool is_auxiliary_self_send_type, rct::key &sender_receiver_secret_out); /** * brief: make_jamtis_onetime_address_extension_g - extension for transforming a recipient spendkey into an * enote one-time address -* k_{g, sender} = k^o_g = H_n("..g..", K_1, q, C) -* param: recipient_address_spend_key - K_1 +* k_{g, sender} = k^o_g = H_n("..g..", K^j_s, q, C) +* param: recipient_address_spend_key - K^j_s * param: sender_receiver_secret - q * param: amount_commitment - C * outparam: sender_extension_out - k_{g, sender} @@ -156,8 +158,8 @@ void make_jamtis_onetime_address_extension_g(const rct::key &recipient_address_s /** * brief: make_jamtis_onetime_address_extension_x - extension for transforming a recipient spendkey into an * enote one-time address -* k_{x, sender} = k^o_x = H_n("..x..", K_1, q, C) -* param: recipient_address_spend_key - K_1 +* k_{x, sender} = k^o_x = H_n("..x..", K^j_s, q, C) +* param: recipient_address_spend_key - K^j_s * param: sender_receiver_secret - q * param: amount_commitment - C * outparam: sender_extension_out - k_{x, sender} @@ -169,8 +171,8 @@ void make_jamtis_onetime_address_extension_x(const rct::key &recipient_address_s /** * brief: make_jamtis_onetime_address_extension_u - extension for transforming a recipient spendkey into an * enote one-time address -* k_{u, sender} = k^o_u = H_n("..u..", K_1, q, C) -* param: recipient_address_spend_key - K_1 +* k_{u, sender} = k^o_u = H_n("..u..", K^j_s, q, C) +* param: recipient_address_spend_key - K^j_s * param: sender_receiver_secret - q * param: amount_commitment - C * outparam: sender_extension_out - k_{u, sender} @@ -181,8 +183,8 @@ void make_jamtis_onetime_address_extension_u(const rct::key &recipient_address_s crypto::secret_key &sender_extension_out); /** * brief: make_jamtis_onetime_address - create a onetime address -* Ko = k^o_g G + k^o_x X + k^o_u U + K_1 -* param: recipient_address_spend_key - K_1 +* Ko = k^o_g G + k^o_x X + k^o_u U + K^j_s +* param: recipient_address_spend_key - K^j_s * param: sender_receiver_secret - q * param: amount_commitment - C * outparam: onetime_address_out - Ko @@ -193,7 +195,7 @@ void make_jamtis_onetime_address(const rct::key &recipient_address_spend_key, rct::key &onetime_address_out); /** * brief: make_jamtis_amount_baked_key_plain_sender - key baked into amount encodings of plain enotes, to provide -* fine-tuned control over read rights to the amount +* fine-tuned control over read rights to the amount and protect against Janus attacks * [normal: sender] baked_key = H_32(xr xG) * param: enote_ephemeral_privkey - xr * outparam: baked_key_out - baked_key @@ -201,16 +203,16 @@ void make_jamtis_onetime_address(const rct::key &recipient_address_spend_key, void make_jamtis_amount_baked_key_plain_sender(const crypto::x25519_secret_key &enote_ephemeral_privkey, rct::key &baked_key_out); /** -* brief: make_jamtis_amount_baked_key_plain_recipient - key baked into amount encodings of plain enotes, to provide -* fine-tuned control over read rights to the amount -* [normal: recipient] baked_key = H_32( (1/(xk^j_a * xk_ua)) * xK_e ) -* param: address_privkey - xk^j_a -* param: xk_unlock_amounts - xk_ua -* param: enote_ephemeral_pubkey - xK_e +* brief: make_jamtis_amount_baked_key_plain_recipient - key baked into amount encodings of plain enotes, +* to provide fine-tuned control over read rights to the amount and protect against Janus attacks +* [normal: recipient] baked_key = H_32(1/(d^j_a * D_vr) * D_e) +* param: address_privkey - d^j_a +* param: d_view_received - d_vr +* param: enote_ephemeral_pubkey - D_e * outparam: baked_key_out - baked_key */ void make_jamtis_amount_baked_key_plain_recipient(const crypto::x25519_secret_key &address_privkey, - const crypto::x25519_secret_key &xk_unlock_amounts, + const crypto::x25519_secret_key &d_view_received, const crypto::x25519_pubkey &enote_ephemeral_pubkey, rct::key &baked_key_out); /** @@ -223,6 +225,7 @@ void make_jamtis_amount_baked_key_plain_recipient(const crypto::x25519_secret_ke */ void make_jamtis_amount_baked_key_selfsend(const crypto::secret_key &k_view_balance, const rct::key &sender_receiver_secret, + const JamtisSelfSendType self_send_type, rct::key &baked_key_out); /** * brief: make_jamtis_amount_blinding_factor - x for a normal enote's amount commitment C = x G + a H @@ -258,7 +261,7 @@ rct::xmr_amount decode_jamtis_amount(const encoded_amount_t &encoded_amount, const rct::key &baked_key); /** * brief: test_jamtis_onetime_address - see if a onetime address can be reconstructed -* param: recipient_address_spend_key - recipient's address spendkey K_1 +* param: recipient_address_spend_key - recipient's address spendkey K^j_s * param: sender_receiver_secret - q * param: amount_commitment - amount commtiment C * param: expected_onetime_address - onetime address to test Ko @@ -269,22 +272,51 @@ bool test_jamtis_onetime_address(const rct::key &recipient_address_spend_key, const rct::key &amount_commitment, const rct::key &expected_onetime_address); /** -* brief: try_get_jamtis_sender_receiver_secret_plain - test view tag; if it passes, get the nominal sender-receiver secret -* (for a normal enote) -* param: sender_receiver_DH_derivation - privkey * DH_key -* param: enote_ephemeral_pubkey - xK_e -* param: input_context - input_context +* brief: test_jamtis_primary_view_tag - test primary view tag +* param: dhe_fa - D^d_fa = [sender: xr * D^j_fa] = [recipient: d_fa * D_e] * param: onetime_address - Ko * param: view_tag - view_tag -* outparam: sender_receiver_secret_out - q -* return: true if successfully recomputed the view tag +* param: num_primary_view_tag_bits - npbits +* return: true if successfully recomputed the primary view tag +*/ +bool test_jamtis_primary_view_tag(const crypto::x25519_pubkey &dhe_fa, + const rct::key &onetime_address, + const view_tag_t view_tag, + const std::uint8_t num_primary_view_tag_bits); +/** +* brief: test_jamtis_primary_view_tag - test primary view tag +* param: d_filter_assist - d_fa +* param: enote_ephemeral_pubkey - D_e = xr D^j_base +* param: onetime_address - Ko +* param: view_tag - view_tag +* param: num_primary_view_tag_bits - npbits +* return: true if successfully recomputed the primary view tag */ -bool try_get_jamtis_sender_receiver_secret_plain(const crypto::x25519_pubkey &sender_receiver_DH_derivation, +bool test_jamtis_primary_view_tag(const crypto::x25519_secret_key &d_filter_assist, const crypto::x25519_pubkey &enote_ephemeral_pubkey, - const rct::key &input_context, const rct::key &onetime_address, const view_tag_t view_tag, - rct::key &sender_receiver_secret_out); + const std::uint8_t num_primary_view_tag_bits); +/** +* brief: test_jamtis_complementary_view_tag - test complementary view tag +* param: sender_receiver_secret - q +* param: view_tag - view_tag +* param: num_primary_view_tag_bits - npbits +* return: true if successfully recomputed the complementary view tag +*/ +bool test_jamtis_complementary_view_tag(const rct::key &sender_receiver_secret, + const view_tag_t view_tag, + const std::uint8_t num_primary_view_tag_bits); +/** +* brief: test_jamtis_auxiliary_view_tag - test auxiliary view tag +* param: k_view_balance - k_vb +* param: onetime_address - Ko +* param: view_tag - view_tag +* return: true if successfully recomputed the auxiliary view tag +*/ +bool test_jamtis_auxiliary_view_tag(const crypto::secret_key &k_view_balance, + const rct::key &onetime_address, + const view_tag_t view_tag); /** * brief: try_get_jamtis_amount - test recreating the amount commitment; if it is recreate-able, return the amount * param: sender_receiver_secret - q diff --git a/src/seraphis_core/jamtis_payment_proposal.cpp b/src/seraphis_core/jamtis_payment_proposal.cpp index 667396d00a..2ba58446e9 100644 --- a/src/seraphis_core/jamtis_payment_proposal.cpp +++ b/src/seraphis_core/jamtis_payment_proposal.cpp @@ -32,9 +32,9 @@ //local headers #include "crypto/crypto.h" #include "crypto/x25519.h" +#include "jamtis_account_secrets.h" #include "jamtis_address_tag_utils.h" #include "jamtis_address_utils.h" -#include "jamtis_core_utils.h" #include "jamtis_enote_utils.h" #include "jamtis_support_types.h" #include "memwipe.h" @@ -81,21 +81,33 @@ static void get_output_proposal_amount_parts_v1(const rct::key &q, //------------------------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------------------------- static void get_output_proposal_address_parts_v1(const rct::key &q, - const crypto::x25519_pubkey &xK_d, const JamtisDestinationV1 &output_destination, const rct::key &amount_commitment, rct::key &onetime_address_out, - encrypted_address_tag_t &addr_tag_enc_out, - view_tag_t &view_tag_out) + encrypted_address_tag_t &addr_tag_enc_out) { - // 1. onetime address: Ko = k^o_g G + k^o_x X + k^o_u U + K_1 - make_jamtis_onetime_address(output_destination.addr_K1, q, amount_commitment, onetime_address_out); + // 1. onetime address: Ko = k^o_g G + k^o_x X + k^o_u U + K^j_s + make_jamtis_onetime_address(output_destination.addr_Ks, q, amount_commitment, onetime_address_out); // 2. encrypt address tag: addr_tag_enc = addr_tag ^ H(q, Ko) addr_tag_enc_out = encrypt_address_tag(q, onetime_address_out, output_destination.addr_tag); +} +//------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------------------------------------------- +static void get_enote_ephemeral_pubkey(const crypto::x25519_secret_key &enote_ephemeral_privkey, + const JamtisDestinationV1 &destination, + crypto::x25519_pubkey &enote_ephemeral_pubkey_out) +{ + // sanity checks + CHECK_AND_ASSERT_THROW_MES(sc_isnonzero(enote_ephemeral_privkey.data), + "jamtis payment proposal: invalid enote ephemeral privkey (zero)."); + CHECK_AND_ASSERT_THROW_MES(x25519_scalar_is_canonical(enote_ephemeral_privkey), + "jamtis payment proposal: invalid enote ephemeral privkey (not canonical)."); - // 3. view tag: view_tag = H_1(xK_d, Ko) - make_jamtis_view_tag(xK_d, onetime_address_out, view_tag_out); + // enote ephemeral pubkey: D_e = xr D^j_base + make_jamtis_enote_ephemeral_pubkey(enote_ephemeral_privkey, + destination.addr_Dbase, + enote_ephemeral_pubkey_out); } //------------------------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------------------------- @@ -115,31 +127,13 @@ bool operator==(const JamtisPaymentProposalSelfSendV1 a, const JamtisPaymentProp void get_enote_ephemeral_pubkey(const JamtisPaymentProposalV1 &proposal, crypto::x25519_pubkey &enote_ephemeral_pubkey_out) { - // sanity checks - CHECK_AND_ASSERT_THROW_MES(sc_isnonzero(proposal.enote_ephemeral_privkey.data), - "jamtis payment proposal: invalid enote ephemeral privkey (zero)."); - CHECK_AND_ASSERT_THROW_MES(x25519_scalar_is_canonical(proposal.enote_ephemeral_privkey), - "jamtis payment proposal: invalid enote ephemeral privkey (not canonical)."); - - // enote ephemeral pubkey: xK_e = xr xK_3 - make_jamtis_enote_ephemeral_pubkey(proposal.enote_ephemeral_privkey, - proposal.destination.addr_K3, - enote_ephemeral_pubkey_out); + get_enote_ephemeral_pubkey(proposal.enote_ephemeral_privkey, proposal.destination, enote_ephemeral_pubkey_out); } //------------------------------------------------------------------------------------------------------------------- void get_enote_ephemeral_pubkey(const JamtisPaymentProposalSelfSendV1 &proposal, crypto::x25519_pubkey &enote_ephemeral_pubkey_out) { - // sanity checks - CHECK_AND_ASSERT_THROW_MES(sc_isnonzero(proposal.enote_ephemeral_privkey.data), - "jamtis payment proposal self-send: invalid enote ephemeral privkey (zero)."); - CHECK_AND_ASSERT_THROW_MES(x25519_scalar_is_canonical(proposal.enote_ephemeral_privkey), - "jamtis payment proposal self-send: invalid enote ephemeral privkey (not canonical)."); - - // enote ephemeral pubkey: xK_e = xr xK_3 - make_jamtis_enote_ephemeral_pubkey(proposal.enote_ephemeral_privkey, - proposal.destination.addr_K3, - enote_ephemeral_pubkey_out); + get_enote_ephemeral_pubkey(proposal.enote_ephemeral_privkey, proposal.destination, enote_ephemeral_pubkey_out); } //------------------------------------------------------------------------------------------------------------------- void get_coinbase_output_proposal_v1(const JamtisPaymentProposalV1 &proposal, @@ -160,27 +154,36 @@ void get_coinbase_output_proposal_v1(const JamtisPaymentProposalV1 &proposal, rct::key input_context; make_jamtis_input_context_coinbase(block_height, input_context); - // 3. enote ephemeral pubkey: xK_e = xr xK_3 + // 3. enote ephemeral pubkey: D_e = xr D^j_base get_enote_ephemeral_pubkey(proposal, enote_ephemeral_pubkey_out); - // 4. derived key: xK_d = xr * xK_2 - crypto::x25519_pubkey xK_d; auto xKd_wiper = auto_wiper(xK_d); - crypto::x25519_scmul_key(proposal.enote_ephemeral_privkey, proposal.destination.addr_K2, xK_d); + // 4. derived key: D^d_fa = xr * D^j_fa + crypto::x25519_pubkey dhe_fa; auto dhe1_wiper = auto_wiper(dhe_fa); + crypto::x25519_scmul_key(proposal.enote_ephemeral_privkey, proposal.destination.addr_Dfa, dhe_fa); - // 5. sender-receiver shared secret (plain): q = H_32(xK_d, xK_e, input_context) + // 5. derived key: D^d_vr = xr * D^j_vr + crypto::x25519_pubkey dhe_vr; auto dhe2_wiper = auto_wiper(dhe_vr); + crypto::x25519_scmul_key(proposal.enote_ephemeral_privkey, proposal.destination.addr_Dvr, dhe_vr); + + // 6. sender-receiver shared secret (plain): q = H_32(D^d_vr, D_e, input_context) rct::key q; auto q_wiper = auto_wiper(q); - make_jamtis_sender_receiver_secret_plain(xK_d, enote_ephemeral_pubkey_out, input_context, q); + make_jamtis_sender_receiver_secret_plain(dhe_vr, enote_ephemeral_pubkey_out, input_context, q); - // 6. build the output enote address pieces + // 7. build the output enote address pieces get_output_proposal_address_parts_v1(q, - xK_d, proposal.destination, rct::commit(proposal.amount, rct::I), output_enote_core_out.onetime_address, - addr_tag_enc_out, + addr_tag_enc_out); + + // 8. make standard view tag + jamtis::make_jamtis_standard_view_tag(dhe_fa, + output_enote_core_out.onetime_address, + q, + proposal.num_primary_view_tag_bits, view_tag_out); - // 7. save the amount and parial memo + // 9. save the amount and parial memo output_enote_core_out.amount = proposal.amount; partial_memo_out = proposal.partial_memo; } @@ -200,38 +203,47 @@ void get_output_proposal_v1(const JamtisPaymentProposalV1 &proposal, CHECK_AND_ASSERT_THROW_MES(x25519_scalar_is_canonical(proposal.enote_ephemeral_privkey), "jamtis payment proposal: invalid enote ephemeral privkey (not canonical)."); - // 2. enote ephemeral pubkey: xK_e = xr xK_3 + // 2. enote ephemeral pubkey: D_e = xr D^j_base get_enote_ephemeral_pubkey(proposal, enote_ephemeral_pubkey_out); - // 3. derived key: xK_d = xr * xK_2 - crypto::x25519_pubkey xK_d; auto xKd_wiper = auto_wiper(xK_d); - crypto::x25519_scmul_key(proposal.enote_ephemeral_privkey, proposal.destination.addr_K2, xK_d); + // 3. derived key: D^d_fa = xr * D^j_fa + crypto::x25519_pubkey dhe_fa; auto dhe1_wiper = auto_wiper(dhe_fa); + crypto::x25519_scmul_key(proposal.enote_ephemeral_privkey, proposal.destination.addr_Dfa, dhe_fa); + + // 4. derived key: D^d_vr = xr * D^j_vr + crypto::x25519_pubkey dhe_vr; auto dhe2_wiper = auto_wiper(dhe_vr); + crypto::x25519_scmul_key(proposal.enote_ephemeral_privkey, proposal.destination.addr_Dvr, dhe_vr); - // 4. sender-receiver shared secret (plain): q = H_32(xK_d, xK_e, input_context) + // 5. sender-receiver shared secret (plain): q = H_32(D^d_fa, D_e, input_context) rct::key q; auto q_wiper = auto_wiper(q); - make_jamtis_sender_receiver_secret_plain(xK_d, enote_ephemeral_pubkey_out, input_context, q); + make_jamtis_sender_receiver_secret_plain(dhe_vr, enote_ephemeral_pubkey_out, input_context, q); - // 5. amount baked key (plain): H_32(xr xG) + // 6. amount baked key (plain): H_32(xr xG) rct::key amount_baked_key; auto bk_wiper = auto_wiper(amount_baked_key); make_jamtis_amount_baked_key_plain_sender(proposal.enote_ephemeral_privkey, amount_baked_key); - // 6. build the output enote amount pieces + // 7. build the output enote amount pieces get_output_proposal_amount_parts_v1(q, amount_baked_key, proposal.amount, output_proposal_core_out.amount_blinding_factor, encoded_amount_out); - // 7. build the output enote address pieces + // 8. build the output enote address pieces get_output_proposal_address_parts_v1(q, - xK_d, proposal.destination, rct::commit(proposal.amount, rct::sk2rct(output_proposal_core_out.amount_blinding_factor)), output_proposal_core_out.onetime_address, - addr_tag_enc_out, + addr_tag_enc_out); + + // 9. make standard view tag + jamtis::make_jamtis_standard_view_tag(dhe_fa, + output_proposal_core_out.onetime_address, + q, + proposal.num_primary_view_tag_bits, view_tag_out); - // 8. save the amount and partial memo + // 10. save the amount and partial memo output_proposal_core_out.amount = proposal.amount; partial_memo_out = proposal.partial_memo; } @@ -258,24 +270,24 @@ void get_output_proposal_v1(const JamtisPaymentProposalSelfSendV1 &proposal, CHECK_AND_ASSERT_THROW_MES(proposal.type <= JamtisSelfSendType::MAX, "jamtis payment proposal self-send: unknown self-send type."); - // 2. enote ephemeral pubkey: xK_e = xr xK_3 + // 2. enote ephemeral pubkey: D_e = xr D^j_base get_enote_ephemeral_pubkey(proposal, enote_ephemeral_pubkey_out); - // 3. derived key: xK_d = xr * xK_2 - crypto::x25519_pubkey xK_d; auto xKd_wiper = auto_wiper(xK_d); - crypto::x25519_scmul_key(proposal.enote_ephemeral_privkey, proposal.destination.addr_K2, xK_d); + // 3. derived key: D^d_fa = xr * D^j_fa + crypto::x25519_pubkey dhe_fa; auto dhe1_wiper = auto_wiper(dhe_fa); + crypto::x25519_scmul_key(proposal.enote_ephemeral_privkey, proposal.destination.addr_Dfa, dhe_fa); - // 4. sender-receiver shared secret (selfsend): q = H_32[k_vb](xK_e, input_context) //note: xK_e not xK_d + // 4. sender-receiver shared secret (selfsend): q = H_32[k_vb](D_e, input_context) //note: D_e not D^d_vr rct::key q; auto q_wiper = auto_wiper(q); make_jamtis_sender_receiver_secret_selfsend(k_view_balance, enote_ephemeral_pubkey_out, input_context, - proposal.type, + is_jamtis_auxiliary_selfsend_type(proposal.type), q); // 5. amount baked key (selfsend): H_32[k_vb](q) rct::key amount_baked_key; auto bk_wiper = auto_wiper(amount_baked_key); - make_jamtis_amount_baked_key_selfsend(k_view_balance, q, amount_baked_key); + make_jamtis_amount_baked_key_selfsend(k_view_balance, q, proposal.type, amount_baked_key); // 6. build the output enote amount pieces get_output_proposal_amount_parts_v1(q, @@ -286,26 +298,40 @@ void get_output_proposal_v1(const JamtisPaymentProposalSelfSendV1 &proposal, // 7. build the output enote address pieces get_output_proposal_address_parts_v1(q, - xK_d, proposal.destination, rct::commit(proposal.amount, rct::sk2rct(output_proposal_core_out.amount_blinding_factor)), output_proposal_core_out.onetime_address, - addr_tag_enc_out, - view_tag_out); - - // 8. save the amount and partial memo + addr_tag_enc_out); + + // 8. make view tag (standard or auxiliary, depending on proposal type) + if (jamtis::is_jamtis_auxiliary_selfsend_type(proposal.type)) + { + jamtis::make_jamtis_auxiliary_view_tag(k_view_balance, output_proposal_core_out.onetime_address, view_tag_out); + } + else // exclusive (AKA standard) view tag + { + jamtis::make_jamtis_standard_view_tag(dhe_fa, + output_proposal_core_out.onetime_address, + q, + proposal.num_primary_view_tag_bits, + view_tag_out); + } + + // 9. save the amount and partial memo output_proposal_core_out.amount = proposal.amount; partial_memo_out = proposal.partial_memo; } //------------------------------------------------------------------------------------------------------------------- JamtisPaymentProposalV1 gen_jamtis_payment_proposal_v1(const rct::xmr_amount amount, - const std::size_t num_random_memo_elements) + const std::size_t num_random_memo_elements, + const std::uint8_t num_primary_view_tag_bits) { JamtisPaymentProposalV1 temp; - temp.destination = gen_jamtis_destination_v1(); - temp.amount = amount; - temp.enote_ephemeral_privkey = crypto::x25519_secret_key_gen(); + temp.destination = gen_jamtis_destination_v1(); + temp.amount = amount; + temp.enote_ephemeral_privkey = crypto::x25519_secret_key_gen(); + temp.num_primary_view_tag_bits = num_primary_view_tag_bits; std::vector memo_elements; memo_elements.resize(num_random_memo_elements); @@ -322,10 +348,11 @@ JamtisPaymentProposalSelfSendV1 gen_jamtis_selfsend_payment_proposal_v1(const rc { JamtisPaymentProposalSelfSendV1 temp; - temp.destination = gen_jamtis_destination_v1(); - temp.amount = amount; - temp.type = type; - temp.enote_ephemeral_privkey = crypto::x25519_secret_key_gen(); + temp.destination = gen_jamtis_destination_v1(); + temp.amount = amount; + temp.type = type; + temp.enote_ephemeral_privkey = crypto::x25519_secret_key_gen(); + temp.num_primary_view_tag_bits = crypto::rand_idx(8 * VIEW_TAG_BYTES); std::vector memo_elements; memo_elements.resize(num_random_memo_elements); diff --git a/src/seraphis_core/jamtis_payment_proposal.h b/src/seraphis_core/jamtis_payment_proposal.h index 06ea329aef..8788e4eb66 100644 --- a/src/seraphis_core/jamtis_payment_proposal.h +++ b/src/seraphis_core/jamtis_payment_proposal.h @@ -66,6 +66,8 @@ struct JamtisPaymentProposalV1 final /// enote ephemeral privkey: xr crypto::x25519_secret_key enote_ephemeral_privkey; + /// npbits + std::uint8_t num_primary_view_tag_bits; /// memo elements to add to the tx memo TxExtra partial_memo; @@ -86,6 +88,8 @@ struct JamtisPaymentProposalSelfSendV1 final JamtisSelfSendType type; /// enote ephemeral privkey: xr crypto::x25519_secret_key enote_ephemeral_privkey; + /// npbits + std::uint8_t num_primary_view_tag_bits; /// memo elements to add to the tx memo TxExtra partial_memo; @@ -96,14 +100,14 @@ bool operator==(const JamtisPaymentProposalV1 a, const JamtisPaymentProposalV1 b bool operator==(const JamtisPaymentProposalSelfSendV1 a, const JamtisPaymentProposalSelfSendV1 b); /** -* brief: get_enote_ephemeral_pubkey - get the proposal's enote ephemeral pubkey xK_e +* brief: get_enote_ephemeral_pubkey - get the proposal's enote ephemeral pubkey D_e * param: proposal - * outparam: enote_ephemeral_pubkey_out - */ void get_enote_ephemeral_pubkey(const JamtisPaymentProposalV1 &proposal, crypto::x25519_pubkey &enote_ephemeral_pubkey_out); /** -* brief: get_enote_ephemeral_pubkey - get the proposal's enote ephemeral pubkey xK_e +* brief: get_enote_ephemeral_pubkey - get the proposal's enote ephemeral pubkey D_e * outparam: enote_ephemeral_pubkey_out - */ void get_enote_ephemeral_pubkey(const JamtisPaymentProposalSelfSendV1 &proposal, @@ -169,10 +173,12 @@ void get_output_proposal_v1(const JamtisPaymentProposalSelfSendV1 &proposal, * brief: gen_jamtis_payment_proposal_v1 - generate a random proposal * param: amount - * param: num_random_memo_elements - +* param: num_primary_view_tag_bits - * return: a random proposal */ JamtisPaymentProposalV1 gen_jamtis_payment_proposal_v1(const rct::xmr_amount amount, - const std::size_t num_random_memo_elements); + const std::size_t num_random_memo_elements, + const std::uint8_t num_primary_view_tag_bits); /** * brief: gen_jamtis_selfsend_payment_proposal_v1 - generate a random selfsend proposal (with specified parameters) * param: amount - diff --git a/src/seraphis_core/jamtis_support_types.cpp b/src/seraphis_core/jamtis_support_types.cpp index d134854631..f695c2f904 100644 --- a/src/seraphis_core/jamtis_support_types.cpp +++ b/src/seraphis_core/jamtis_support_types.cpp @@ -68,21 +68,11 @@ address_index_t::address_index_t() std::memset(this->bytes, 0, ADDRESS_INDEX_BYTES); } //------------------------------------------------------------------------------------------------------------------- -address_tag_hint_t::address_tag_hint_t() -{ - std::memset(this->bytes, 0, ADDRESS_TAG_HINT_BYTES); -} -//------------------------------------------------------------------------------------------------------------------- bool operator==(const address_index_t &a, const address_index_t &b) { return memcmp(a.bytes, b.bytes, sizeof(address_index_t)) == 0; } //------------------------------------------------------------------------------------------------------------------- -bool operator==(const address_tag_hint_t &a, const address_tag_hint_t &b) -{ - return memcmp(a.bytes, b.bytes, sizeof(address_tag_hint_t)) == 0; -} -//------------------------------------------------------------------------------------------------------------------- bool operator==(const address_tag_t &a, const address_tag_t &b) { return memcmp(a.bytes, b.bytes, sizeof(address_tag_t)) == 0; @@ -103,6 +93,11 @@ encoded_amount_t operator^(const encoded_amount_t &a, const encoded_amount_t &b) return xor_bytes(a, b); } //------------------------------------------------------------------------------------------------------------------- +bool operator==(const view_tag_t &a, const view_tag_t &b) +{ + return memcmp(a.bytes, b.bytes, sizeof(view_tag_t)) == 0; +} +//------------------------------------------------------------------------------------------------------------------- address_index_t max_address_index() { address_index_t temp; @@ -126,12 +121,11 @@ address_index_t make_address_index(std::uint64_t half1, std::uint64_t half2) return temp; } //------------------------------------------------------------------------------------------------------------------- -address_tag_t make_address_tag(const address_index_t &enc_j, const address_tag_hint_t &addr_tag_hint) +address_tag_t make_address_tag(const address_index_t &enc_j) { - // addr_tag = enc(j) || hint + // addr_tag = enc(j) address_tag_t temp; memcpy(temp.bytes, &enc_j, ADDRESS_INDEX_BYTES); - memcpy(temp.bytes + ADDRESS_INDEX_BYTES, &addr_tag_hint, ADDRESS_TAG_HINT_BYTES); return temp; } //------------------------------------------------------------------------------------------------------------------- @@ -142,14 +136,22 @@ address_index_t gen_address_index() return temp; } //------------------------------------------------------------------------------------------------------------------- +view_tag_t gen_view_tag() +{ + view_tag_t temp; + crypto::rand(VIEW_TAG_BYTES, temp.bytes); + return temp; +} +//------------------------------------------------------------------------------------------------------------------- bool try_get_jamtis_enote_type(const JamtisSelfSendType self_send_type, JamtisEnoteType &enote_type_out) { switch (self_send_type) { - case (JamtisSelfSendType::DUMMY) : enote_type_out = JamtisEnoteType::DUMMY; return true; - case (JamtisSelfSendType::CHANGE) : enote_type_out = JamtisEnoteType::CHANGE; return true; - case (JamtisSelfSendType::SELF_SPEND) : enote_type_out = JamtisEnoteType::SELF_SPEND; return true; - default : return false; + case (JamtisSelfSendType::EXCLUSIVE_SELF_SPEND) : enote_type_out = JamtisEnoteType::EXCLUSIVE_SELF_SPEND; return true; + case (JamtisSelfSendType::EXCLUSIVE_CHANGE) : enote_type_out = JamtisEnoteType::EXCLUSIVE_CHANGE; return true; + case (JamtisSelfSendType::AUXILIARY_SELF_SPEND) : enote_type_out = JamtisEnoteType::AUXILIARY_SELF_SPEND; return true; + case (JamtisSelfSendType::AUXILIARY_CHANGE) : enote_type_out = JamtisEnoteType::AUXILIARY_CHANGE; return true; + default : return false; }; } //------------------------------------------------------------------------------------------------------------------- @@ -157,10 +159,11 @@ bool try_get_jamtis_self_send_type(const JamtisEnoteType enote_type, JamtisSelfS { switch (enote_type) { - case (JamtisEnoteType::DUMMY) : self_send_type_out = JamtisSelfSendType::DUMMY; return true; - case (JamtisEnoteType::CHANGE) : self_send_type_out = JamtisSelfSendType::CHANGE; return true; - case (JamtisEnoteType::SELF_SPEND) : self_send_type_out = JamtisSelfSendType::SELF_SPEND; return true; - default : return false; + case (JamtisEnoteType::EXCLUSIVE_SELF_SPEND) : self_send_type_out = JamtisSelfSendType::EXCLUSIVE_SELF_SPEND; return true; + case (JamtisEnoteType::EXCLUSIVE_CHANGE) : self_send_type_out = JamtisSelfSendType::EXCLUSIVE_CHANGE; return true; + case (JamtisEnoteType::AUXILIARY_SELF_SPEND) : self_send_type_out = JamtisSelfSendType::AUXILIARY_SELF_SPEND; return true; + case (JamtisEnoteType::AUXILIARY_CHANGE) : self_send_type_out = JamtisSelfSendType::AUXILIARY_CHANGE; return true; + default : return false; }; } //------------------------------------------------------------------------------------------------------------------- @@ -170,5 +173,25 @@ bool is_jamtis_selfsend_type(const JamtisEnoteType enote_type) return try_get_jamtis_self_send_type(enote_type, dummy); } //------------------------------------------------------------------------------------------------------------------- +bool is_jamtis_auxiliary_selfsend_type(const JamtisSelfSendType self_send_type) +{ + switch (self_send_type) + { + case JamtisSelfSendType::EXCLUSIVE_SELF_SPEND: + case JamtisSelfSendType::EXCLUSIVE_CHANGE: + return false; + case JamtisSelfSendType::AUXILIARY_SELF_SPEND: + case JamtisSelfSendType::AUXILIARY_CHANGE: + return true; + default: + ASSERT_MES_AND_THROW("is auxiliary selfsend type: unrecognized Jamtis self send type"); + }; +} +//------------------------------------------------------------------------------------------------------------------- +bool is_jamtis_exclusive_selfsend_type(const JamtisSelfSendType self_send_type) +{ + return !is_jamtis_auxiliary_selfsend_type(self_send_type); +} +//------------------------------------------------------------------------------------------------------------------- } //namespace jamtis } //namespace sp diff --git a/src/seraphis_core/jamtis_support_types.h b/src/seraphis_core/jamtis_support_types.h index 05337398e0..3e092670e2 100644 --- a/src/seraphis_core/jamtis_support_types.h +++ b/src/seraphis_core/jamtis_support_types.h @@ -26,7 +26,7 @@ // 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. -// Supporting types for Jamtis (address index, address tag hint, address tag, etc.). +//! @file Supporting types for Jamtis (address index, address tag, view tag, etc.). #pragma once @@ -57,20 +57,10 @@ struct address_index_t final address_index_t(); }; -/// hint for address tags: addr_tag_hint -constexpr std::size_t ADDRESS_TAG_HINT_BYTES{2}; -struct address_tag_hint_t final -{ - unsigned char bytes[ADDRESS_TAG_HINT_BYTES]; - - /// default constructor: default initialize to 0 - address_tag_hint_t(); -}; - -/// index ciphered with a cipher key: addr_tag = enc[cipher_key](j) || addr_tag_hint +/// index ciphered with a cipher key: addr_tag = enc[cipher_key](j) struct address_tag_t final { - unsigned char bytes[ADDRESS_INDEX_BYTES + ADDRESS_TAG_HINT_BYTES]; + unsigned char bytes[ADDRESS_INDEX_BYTES]; }; /// address tag XORd with a user-defined secret: addr_tag_enc = addr_tag XOR addr_tag_enc_secret @@ -78,9 +68,8 @@ using encrypted_address_tag_t = address_tag_t; /// sizes must be consistent static_assert( - sizeof(address_index_t) == ADDRESS_INDEX_BYTES && - sizeof(address_tag_hint_t) == ADDRESS_TAG_HINT_BYTES && - sizeof(address_tag_t) == ADDRESS_INDEX_BYTES + ADDRESS_TAG_HINT_BYTES && + sizeof(address_index_t) == ADDRESS_INDEX_BYTES && + sizeof(address_tag_t) == ADDRESS_INDEX_BYTES && sizeof(address_tag_t) == sizeof(encrypted_address_tag_t), "" ); @@ -88,19 +77,28 @@ static_assert( /// jamtis enote types enum class JamtisEnoteType : unsigned char { - PLAIN = 0, - DUMMY = 1, - CHANGE = 2, - SELF_SPEND = 3 + EXCLUSIVE_SELF_SPEND = 0, + EXCLUSIVE_CHANGE = 1, + AUXILIARY_SELF_SPEND = 2, + AUXILIARY_CHANGE = 3, + PLAIN = 4 }; /// jamtis self-send types, used to define enote-construction procedure for self-sends enum class JamtisSelfSendType : unsigned char { - DUMMY = 0, - CHANGE = 1, - SELF_SPEND = 2, - MAX = SELF_SPEND + /// In every outgoing transaction that contains owned self-send enotes, there should be *exactly* ONE enote either + /// with type EXCLUSIVE_SELF_SPEND or EXCLUSIVE_CHANGE, but not both. Any other self-send enotes in that transaction + /// should have type AUXILIARY_*. Enotes of type EXCLUSIVE_* have primary view tags which are scannable with + /// knowledge of the private filter-assist key, whereas AUXILIARY_* enotes do not. This 1) ensures that if you + /// delegate primary view tag scanning to a third party, you will always scan these enotes and 2) prevents + /// statistical fingerprints of a third-party scanner seeing multiple primary view tag matches in a single + /// transaction. The reason that there is a distinction between *_SELF_SPEND and *_CHANGE is for UX. + EXCLUSIVE_SELF_SPEND = 0, + EXCLUSIVE_CHANGE = 1, + AUXILIARY_SELF_SPEND = 2, + AUXILIARY_CHANGE = 3, + MAX = AUXILIARY_CHANGE }; /// jamtis encoded amount @@ -111,14 +109,17 @@ struct encoded_amount_t final }; /// jamtis view tags -using view_tag_t = unsigned char; +constexpr std::size_t VIEW_TAG_BYTES{2}; +struct view_tag_t final +{ + unsigned char bytes[VIEW_TAG_BYTES]; +}; + +static_assert(sizeof(view_tag_t) < 32, "uint8_t cannot index all view tag bits"); /// overloaded operators: address index bool operator==(const address_index_t &a, const address_index_t &b); inline bool operator!=(const address_index_t &a, const address_index_t &b) { return !(a == b); } -/// overloaded operators: address tag hint -bool operator==(const address_tag_hint_t &a, const address_tag_hint_t &b); -inline bool operator!=(const address_tag_hint_t &a, const address_tag_hint_t &b) { return !(a == b); } /// overloaded operators: address tag bool operator==(const address_tag_t &a, const address_tag_t &b); inline bool operator!=(const address_tag_t &a, const address_tag_t &b) { return !(a == b); } @@ -129,20 +130,29 @@ bool operator==(const encoded_amount_t &a, const encoded_amount_t &b); inline bool operator!=(const encoded_amount_t &a, const encoded_amount_t &b) { return !(a == b); } encoded_amount_t operator^(const encoded_amount_t &a, const encoded_amount_t &b); +/// overloaded operators: view tag +bool operator==(const view_tag_t &a, const view_tag_t &b); +inline bool operator!=(const view_tag_t &a, const view_tag_t &b) { return !(a == b); } + /// max address index address_index_t max_address_index(); /// make an address index address_index_t make_address_index(std::uint64_t half1, std::uint64_t half2); inline address_index_t make_address_index(std::uint64_t half1) { return make_address_index(half1, 0); } /// make an address tag -address_tag_t make_address_tag(const address_index_t &enc_j, const address_tag_hint_t &addr_tag_hint); +address_tag_t make_address_tag(const address_index_t &enc_j); /// generate a random address index address_index_t gen_address_index(); +// generate a random view tag +view_tag_t gen_view_tag(); + /// convert between jamtis enote types and self-send types bool try_get_jamtis_enote_type(const JamtisSelfSendType self_send_type, JamtisEnoteType &enote_type_out); bool try_get_jamtis_self_send_type(const JamtisEnoteType enote_type, JamtisSelfSendType &self_send_type_out); bool is_jamtis_selfsend_type(const JamtisEnoteType enote_type); +bool is_jamtis_auxiliary_selfsend_type(const JamtisSelfSendType self_send_type); +bool is_jamtis_exclusive_selfsend_type(const JamtisSelfSendType self_send_type); } //namespace jamtis } //namespace sp diff --git a/src/seraphis_impl/enote_finding_context_legacy.cpp b/src/seraphis_impl/enote_finding_context_legacy.cpp index 163c01700b..eb54207c86 100644 --- a/src/seraphis_impl/enote_finding_context_legacy.cpp +++ b/src/seraphis_impl/enote_finding_context_legacy.cpp @@ -31,6 +31,7 @@ //local headers #include "async/misc_utils.h" +#include "common/container_helpers.h" #include "device/device.hpp" #include "seraphis_main/contextual_enote_record_types.h" #include "seraphis_main/scan_balance_recovery_utils.h" @@ -80,16 +81,15 @@ void EnoteFindingContextLegacySimple::view_scan_chunk(const LegacyUnscannedChunk } // Collect key images - sp::SpContextualKeyImageSetV1 collected_key_images; - if (sp::scanning::try_collect_key_images_from_tx(blk.block_index, + if (!tx.legacy_key_images.empty()) + { + sp::scanning::collect_key_images_from_tx(blk.block_index, blk.block_timestamp, tx.transaction_id, tx.legacy_key_images, std::vector()/*sp_key_images*/, sp::SpEnoteSpentStatus::SPENT_ONCHAIN, - collected_key_images)) - { - chunk_data_out.contextual_key_images.emplace_back(std::move(collected_key_images)); + tools::add_element(chunk_data_out.contextual_key_images)); } } } @@ -160,16 +160,15 @@ void EnoteFindingContextLegacyMultithreaded::view_scan_chunk( } // Collect key images - sp::SpContextualKeyImageSetV1 collected_key_images; - if (sp::scanning::try_collect_key_images_from_tx(blk.block_index, + if (!tx.legacy_key_images.empty()) + { + sp::scanning::collect_key_images_from_tx(blk.block_index, blk.block_timestamp, tx.transaction_id, tx.legacy_key_images, std::vector()/*sp_key_images*/, sp::SpEnoteSpentStatus::SPENT_ONCHAIN, - collected_key_images)) - { - chunk_data_out.contextual_key_images.emplace_back(std::move(collected_key_images)); + tools::add_element(chunk_data_out.contextual_key_images)); } ++idx; diff --git a/src/seraphis_impl/seraphis_serialization.h b/src/seraphis_impl/seraphis_serialization.h index c1e39ddbac..9c64e8bfbd 100644 --- a/src/seraphis_impl/seraphis_serialization.h +++ b/src/seraphis_impl/seraphis_serialization.h @@ -178,14 +178,14 @@ END_SERIALIZE() BEGIN_SERIALIZE_OBJECT_FN(SpCoinbaseEnoteV1) FIELD_F(core) FIELD_F(addr_tag_enc) - VARINT_FIELD_F(view_tag) + FIELD_F(view_tag) END_SERIALIZE() //-------------------------------------------------------------------------------------------------- BEGIN_SERIALIZE_OBJECT_FN(SpEnoteV1) FIELD_F(core) FIELD_F(encoded_amount) FIELD_F(addr_tag_enc) - VARINT_FIELD_F(view_tag) + FIELD_F(view_tag) END_SERIALIZE() //-------------------------------------------------------------------------------------------------- BEGIN_SERIALIZE_OBJECT_FN(SpBalanceProofV1, const size_t implied_lr_size = SIZE_MAX) @@ -347,6 +347,7 @@ END_SERIALIZE() BEGIN_SERIALIZE_OBJECT_FN(SpTxSupplementV1, const size_t implied_num_outputs = SIZE_MAX) const size_t implied_num_ephem_pubkeys{(2 == implied_num_outputs) ? 1 : implied_num_outputs}; VEC_FIELD_OPT_EXACT_F(output_enote_ephemeral_pubkeys, implied_num_ephem_pubkeys) + VARINT_FIELD_F(num_primary_view_tag_bits) FIELD_F(tx_extra) END_SERIALIZE() //-------------------------------------------------------------------------------------------------- @@ -399,9 +400,10 @@ namespace jamtis { //-------------------------------------------------------------------------------------------------- BEGIN_SERIALIZE_OBJECT_FN(JamtisDestinationV1) - FIELD_F(addr_K1) - FIELD_F(addr_K2) - FIELD_F(addr_K3) + FIELD_F(addr_Ks) + FIELD_F(addr_Dfa) + FIELD_F(addr_Dvr) + FIELD_F(addr_Dbase) FIELD_F(addr_tag) END_SERIALIZE() //-------------------------------------------------------------------------------------------------- @@ -426,3 +428,4 @@ END_SERIALIZE() BLOB_SERIALIZER(sp::jamtis::address_index_t); BLOB_SERIALIZER(sp::jamtis::address_tag_t); BLOB_SERIALIZER(sp::jamtis::encoded_amount_t); +BLOB_SERIALIZER(sp::jamtis::view_tag_t); diff --git a/src/seraphis_impl/tx_builder_utils.cpp b/src/seraphis_impl/tx_builder_utils.cpp index 70ee1c038e..e3199bccc2 100644 --- a/src/seraphis_impl/tx_builder_utils.cpp +++ b/src/seraphis_impl/tx_builder_utils.cpp @@ -59,7 +59,6 @@ namespace sp { //------------------------------------------------------------------------------------------------------------------- bool try_prepare_inputs_and_outputs_for_transfer_v1(const jamtis::JamtisDestinationV1 &change_address, - const jamtis::JamtisDestinationV1 &dummy_address, const InputSelectorV1 &local_user_input_selector, const FeeCalculator &tx_fee_calculator, const rct::xmr_amount fee_per_tx_weight, @@ -109,7 +108,6 @@ bool try_prepare_inputs_and_outputs_for_transfer_v1(const jamtis::JamtisDestinat finalize_v1_output_proposal_set_v1(total_input_amount, reported_final_fee, change_address, - dummy_address, k_view_balance, normal_payment_proposals, selfsend_payment_proposals); diff --git a/src/seraphis_impl/tx_builder_utils.h b/src/seraphis_impl/tx_builder_utils.h index 58dfeae3d3..168c743235 100644 --- a/src/seraphis_impl/tx_builder_utils.h +++ b/src/seraphis_impl/tx_builder_utils.h @@ -72,7 +72,6 @@ namespace sp * outparam: discretized_transaction_fee_out - */ bool try_prepare_inputs_and_outputs_for_transfer_v1(const jamtis::JamtisDestinationV1 &change_address, - const jamtis::JamtisDestinationV1 &dummy_address, const InputSelectorV1 &local_user_input_selector, const FeeCalculator &tx_fee_calculator, const rct::xmr_amount fee_per_tx_weight, diff --git a/src/seraphis_main/enote_finding_context.h b/src/seraphis_main/enote_finding_context.h index ef28e08079..eac522852f 100644 --- a/src/seraphis_main/enote_finding_context.h +++ b/src/seraphis_main/enote_finding_context.h @@ -26,7 +26,7 @@ // 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. -// Dependency injectors for the find-received step of enote scanning. Intended to be stateless. +// Dependency injectors for the filter-assist step of enote scanning. Intended to be stateless. #pragma once @@ -50,7 +50,7 @@ namespace sp //// // EnoteFindingContextNonLedger -// - wraps a nonledger context of some kind, produces chunks of potentially owned enotes (from find-received scanning) +// - wraps a nonledger context of some kind, produces chunks of potentially owned enotes (from filter-assist scanning) /// class EnoteFindingContextNonLedger { @@ -69,7 +69,7 @@ class EnoteFindingContextNonLedger //// // EnoteFindingContextLedger -// - wraps a ledger context of some kind, produces chunks of potentially owned enotes (from find-received scanning) +// - wraps a ledger context of some kind, produces chunks of potentially owned enotes (from filter-assist scanning) /// class EnoteFindingContextLedger { diff --git a/src/seraphis_main/enote_record_types.h b/src/seraphis_main/enote_record_types.h index d745a8ce7e..64becc6dde 100644 --- a/src/seraphis_main/enote_record_types.h +++ b/src/seraphis_main/enote_record_types.h @@ -129,7 +129,7 @@ struct LegacyEnoteRecord final //// // SpBasicEnoteRecordV1 -// - a seraphis enote that has passed the view-tag check using a jamtis find-received key +// - a seraphis enote that resides in a transaction with an enote that passes the primary view tag check /// struct SpBasicEnoteRecordV1 final { @@ -137,15 +137,18 @@ struct SpBasicEnoteRecordV1 final SpEnoteVariant enote; /// the enote's ephemeral pubkey crypto::x25519_pubkey enote_ephemeral_pubkey; + // the transaction primary view tag size + std::uint8_t num_primary_view_tag_bits; /// context of the tx input(s) associated with this enote rct::key input_context; - /// t'_addr: nominal address tag (only useful for jamtis non-selfsend enote types) - jamtis::address_tag_t nominal_address_tag; + /// indicates if this enote passed the exclusive-enote view tag check + /// - If it did not pass, then it *might* be an auxiliary enote. + bool passed_exclusive_check; }; //// // SpIntermediateEnoteRecordV1 (jamtis non-selfsend enote type only) -// - a seraphis enote with info extracted using a jamtis find-received key, generate-address secret, and unlock-amounts key +// - a seraphis enote with info extracted using a jamtis view-received key and generate-address secret key /// struct SpIntermediateEnoteRecordV1 final { @@ -153,6 +156,8 @@ struct SpIntermediateEnoteRecordV1 final SpEnoteVariant enote; /// the enote's ephemeral pubkey crypto::x25519_pubkey enote_ephemeral_pubkey; + // the transaction primary view tag size + std::uint8_t num_primary_view_tag_bits; /// context of the tx input(s) associated with this enote rct::key input_context; /// a: amount @@ -173,22 +178,24 @@ struct SpEnoteRecordV1 final SpEnoteVariant enote; /// the enote's ephemeral pubkey crypto::x25519_pubkey enote_ephemeral_pubkey; + // the transaction primary view tag size + std::uint8_t num_primary_view_tag_bits; /// context of the tx input(s) associated with this enote rct::key input_context; + /// a: amount + rct::xmr_amount amount; + /// x: amount blinding factor + crypto::secret_key amount_blinding_factor; + /// j: jamtis address index + jamtis::address_index_t address_index; /// k_{g, sender} + k_{g, address}: enote view extension for G component crypto::secret_key enote_view_extension_g; /// k_{x, sender} + k_{x, address}: enote view extension for X component (excludes k_vb) crypto::secret_key enote_view_extension_x; /// k_{u, sender} + k_{u, address}: enote view extension for U component (excludes k_m) crypto::secret_key enote_view_extension_u; - /// a: amount - rct::xmr_amount amount; - /// x: amount blinding factor - crypto::secret_key amount_blinding_factor; /// KI: key image crypto::key_image key_image; - /// j: jamtis address index - jamtis::address_index_t address_index; /// jamtis enote type jamtis::JamtisEnoteType type; }; diff --git a/src/seraphis_main/enote_record_utils.cpp b/src/seraphis_main/enote_record_utils.cpp index 5fdd6ca72b..14247ec8c4 100644 --- a/src/seraphis_main/enote_record_utils.cpp +++ b/src/seraphis_main/enote_record_utils.cpp @@ -39,9 +39,9 @@ extern "C" #include "enote_record_types.h" #include "ringct/rctOps.h" #include "ringct/rctTypes.h" +#include "seraphis_core/jamtis_account_secrets.h" #include "seraphis_core/jamtis_address_tag_utils.h" #include "seraphis_core/jamtis_address_utils.h" -#include "seraphis_core/jamtis_core_utils.h" #include "seraphis_core/jamtis_enote_utils.h" #include "seraphis_core/jamtis_support_types.h" #include "seraphis_core/sp_core_enote_utils.h" @@ -60,108 +60,73 @@ namespace sp { //------------------------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------------------------- -static void make_enote_view_extension_g_helper(const rct::key &jamtis_spend_pubkey, - const crypto::secret_key &s_generate_address, - const jamtis::address_index_t &j, - const rct::key &recipient_address_spendkey, //K_1 - const rct::key &sender_receiver_secret, - const rct::key &amount_commitment, - crypto::secret_key &enote_view_extension_g_out) +namespace { - // enote view privkey extension on g: k_g = k^o_g + k^j_g - crypto::secret_key spendkey_extension_g; //k^j_g - crypto::secret_key sender_extension_g; //k^o_g - jamtis::make_jamtis_spendkey_extension_g(jamtis_spend_pubkey, s_generate_address, j, spendkey_extension_g); - jamtis::make_jamtis_onetime_address_extension_g(recipient_address_spendkey, - sender_receiver_secret, - amount_commitment, - sender_extension_g); - - // k_g = k^o_g + k^j_g - sc_add(to_bytes(enote_view_extension_g_out), to_bytes(sender_extension_g), to_bytes(spendkey_extension_g)); -} -//------------------------------------------------------------------------------------------------------------------- -//------------------------------------------------------------------------------------------------------------------- -static void make_enote_view_extension_x_helper(const rct::key &jamtis_spend_pubkey, - const crypto::secret_key &s_generate_address, - const jamtis::address_index_t &j, - const rct::key &recipient_address_spendkey, //K_1 - const rct::key &sender_receiver_secret, - const rct::key &amount_commitment, - crypto::secret_key &enote_view_extension_x_out) +enum class BalanceRecoveryPath { - // enote view privkey extension on x: k_x = k^o_x + k^j_x - crypto::secret_key spendkey_extension_x; //k^j_x - crypto::secret_key sender_extension_x; //k^o_x - jamtis::make_jamtis_spendkey_extension_x(jamtis_spend_pubkey, s_generate_address, j, spendkey_extension_x); - jamtis::make_jamtis_onetime_address_extension_x(recipient_address_spendkey, - sender_receiver_secret, - amount_commitment, - sender_extension_x); + PLAIN, + EXCLUSIVE_SELFSEND, + AUXILIARY_SELFSEND +}; - // k_x = k^o_x + k^j_x - sc_add(to_bytes(enote_view_extension_x_out), to_bytes(sender_extension_x), to_bytes(spendkey_extension_x)); -} +static constexpr jamtis::JamtisSelfSendType EXCLUSIVE_SELFSEND_TYPES[2] = { + jamtis::JamtisSelfSendType::EXCLUSIVE_SELF_SPEND, jamtis::JamtisSelfSendType::EXCLUSIVE_CHANGE}; +static constexpr jamtis::JamtisSelfSendType AUXILIARY_SELFSEND_TYPES[2] = { + jamtis::JamtisSelfSendType::AUXILIARY_SELF_SPEND, jamtis::JamtisSelfSendType::AUXILIARY_CHANGE}; +} // anonymous namespace //------------------------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------------------------- -static void make_enote_view_extension_u_helper(const rct::key &jamtis_spend_pubkey, - const crypto::secret_key &s_generate_address, - const jamtis::address_index_t &j, - const rct::key &recipient_address_spendkey, //K_1 - const rct::key &sender_receiver_secret, - const rct::key &amount_commitment, - crypto::secret_key &enote_view_extension_u_out) +static bool try_get_selfsend_types_for_balance_recovery_path(const BalanceRecoveryPath path, + const jamtis::JamtisSelfSendType *&self_send_types, + std::size_t &num_self_send_types) { - // enote view privkey extension on u: k_u = k^o_u + k^j_u - crypto::secret_key spendkey_extension_u; //k^j_u - crypto::secret_key sender_extension_u; //k^o_u - jamtis::make_jamtis_spendkey_extension_u(jamtis_spend_pubkey, s_generate_address, j, spendkey_extension_u); - jamtis::make_jamtis_onetime_address_extension_u(recipient_address_spendkey, - sender_receiver_secret, - amount_commitment, - sender_extension_u); - - // k_u = k^o_u + k^j_u - sc_add(to_bytes(enote_view_extension_u_out), to_bytes(sender_extension_u), to_bytes(spendkey_extension_u)); + switch (path) + { + case BalanceRecoveryPath::PLAIN: return false; + case BalanceRecoveryPath::EXCLUSIVE_SELFSEND: self_send_types = EXCLUSIVE_SELFSEND_TYPES; num_self_send_types = 2; return true; + case BalanceRecoveryPath::AUXILIARY_SELFSEND: self_send_types = AUXILIARY_SELFSEND_TYPES; num_self_send_types = 2; return true; + default: + throw std::logic_error("bug: unexpected BalanceRecoveryPath enum variant"); + } } //------------------------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------------------------- static void make_enote_view_extensions_helper(const rct::key &jamtis_spend_pubkey, const crypto::secret_key &s_generate_address, const jamtis::address_index_t &j, - const rct::key &recipient_address_spendkey, //K_1 + const rct::key &recipient_address_spendkey, //K^j_s const rct::key &sender_receiver_secret, const rct::key &amount_commitment, crypto::secret_key &enote_view_extension_g_out, crypto::secret_key &enote_view_extension_x_out, crypto::secret_key &enote_view_extension_u_out) { + crypto::secret_key spendkey_extension_g, spendkey_extension_x, spendkey_extension_u; // k^j_{g/x/u} + crypto::secret_key sender_extension_g, sender_extension_x, sender_extension_u; // // k^o_{g/x/u} + // 1. construct the enote view privkey for the G component: k_g = k^o_g + k^j_g - make_enote_view_extension_g_helper(jamtis_spend_pubkey, - s_generate_address, - j, - recipient_address_spendkey, + jamtis::make_jamtis_spendkey_extension_g(jamtis_spend_pubkey, s_generate_address, j, spendkey_extension_g); + jamtis::make_jamtis_onetime_address_extension_g(recipient_address_spendkey, sender_receiver_secret, amount_commitment, - enote_view_extension_g_out); + sender_extension_g); + sc_add(to_bytes(enote_view_extension_g_out), to_bytes(sender_extension_g), to_bytes(spendkey_extension_g)); // 2. construct the enote view privkey for the X component: k_x = k^o_x + k^j_x - make_enote_view_extension_x_helper(jamtis_spend_pubkey, - s_generate_address, - j, - recipient_address_spendkey, + jamtis::make_jamtis_spendkey_extension_x(jamtis_spend_pubkey, s_generate_address, j, spendkey_extension_x); + jamtis::make_jamtis_onetime_address_extension_x(recipient_address_spendkey, sender_receiver_secret, amount_commitment, - enote_view_extension_x_out); + sender_extension_x); + sc_add(to_bytes(enote_view_extension_x_out), to_bytes(sender_extension_x), to_bytes(spendkey_extension_x)); // 3. construct the enote view privkey for the U component: k_u = k^o_u + k^j_u - make_enote_view_extension_u_helper(jamtis_spend_pubkey, - s_generate_address, - j, - recipient_address_spendkey, + jamtis::make_jamtis_spendkey_extension_u(jamtis_spend_pubkey, s_generate_address, j, spendkey_extension_u); + jamtis::make_jamtis_onetime_address_extension_u(recipient_address_spendkey, sender_receiver_secret, amount_commitment, - enote_view_extension_u_out); + sender_extension_u); + sc_add(to_bytes(enote_view_extension_u_out), to_bytes(sender_extension_u), to_bytes(spendkey_extension_u)); } //------------------------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------------------------- @@ -181,31 +146,50 @@ static void make_seraphis_key_image_helper(const rct::key &jamtis_spend_pubkey, } //------------------------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------------------------- -static bool try_get_amount_commitment_information_plaintext(const rct::xmr_amount &enote_amount, - rct::xmr_amount &amount_out, - crypto::secret_key &amount_blinding_factor_out) -{ - amount_out = enote_amount; - amount_blinding_factor_out = rct::rct2sk(rct::I); - - return true; -} -//------------------------------------------------------------------------------------------------------------------- -//------------------------------------------------------------------------------------------------------------------- -static bool try_get_amount_commitment_information(const SpEnoteVariant &enote, +static bool try_recover_amount_commitment_info(const SpEnoteVariant &enote, + const crypto::x25519_pubkey &enote_ephemeral_pubkey, const rct::key &sender_receiver_secret, - const rct::key &amount_baked_key, + const rct::key &jamtis_spend_pubkey, + const crypto::secret_key &k_view_balance, + const crypto::x25519_secret_key &d_view_received, + const jamtis::address_index_t &address_index, + const crypto::secret_key &s_generate_address, + const BalanceRecoveryPath balance_recovery_path, rct::xmr_amount &amount_out, - crypto::secret_key &amount_blinding_factor_out) + crypto::secret_key &amount_blinding_factor_out, + jamtis::JamtisEnoteType &enote_type_out) { + // if this enote is a coinbase enote, extract the public amount info and return if (const SpCoinbaseEnoteV1 *enote_ptr = enote.try_unwrap()) { - return try_get_amount_commitment_information_plaintext(enote_ptr->core.amount, - amount_out, - amount_blinding_factor_out); + amount_out = enote_ptr->core.amount; + amount_blinding_factor_out = rct::rct2sk(rct::I); + enote_type_out = jamtis::JamtisEnoteType::PLAIN; + return true; } - else if (const SpEnoteV1 *enote_ptr = enote.try_unwrap()) + + // otherwise we should have a normal enote + const SpEnoteV1 *enote_ptr = enote.try_unwrap(); + CHECK_AND_ASSERT_THROW_MES(enote_ptr, "unknown enote type in try_get_amount_commitment_info"); + + if (balance_recovery_path == BalanceRecoveryPath::PLAIN) { + enote_type_out = jamtis::JamtisEnoteType::PLAIN; + + // d^j_a = H_n_x25519(K_s, j, s^j_gen) + crypto::x25519_secret_key address_privkey; + jamtis::make_jamtis_address_privkey(jamtis_spend_pubkey, + s_generate_address, + address_index, + address_privkey); + + // baked_key = H_32(1/(d^j_a * d_vr) * D_e) + rct::key amount_baked_key; + jamtis::make_jamtis_amount_baked_key_plain_recipient(address_privkey, + d_view_received, + enote_ephemeral_pubkey, + amount_baked_key); + return jamtis::try_get_jamtis_amount(sender_receiver_secret, amount_baked_key, enote_ptr->core.amount_commitment, @@ -213,449 +197,373 @@ static bool try_get_amount_commitment_information(const SpEnoteVariant &enote, amount_out, amount_blinding_factor_out); } - else - CHECK_AND_ASSERT_THROW_MES(false, "try get amount commitment information: unknown enote type."); + else // on self-send path + { + const jamtis::JamtisSelfSendType *self_send_types_to_check; + std::size_t num_self_send_types_to_check; + CHECK_AND_ASSERT_THROW_MES(try_get_selfsend_types_for_balance_recovery_path(balance_recovery_path, + self_send_types_to_check, + num_self_send_types_to_check), + "bug: no self send types provided for non-plain recovery path"); + + // for each applicable self send type... + for (std::size_t i = 0; i < num_self_send_types_to_check; ++i) + { + // baked_key = H_32[k_vb](q) + rct::key amount_baked_key; + jamtis::make_jamtis_amount_baked_key_selfsend(k_view_balance, + sender_receiver_secret, + self_send_types_to_check[i], + amount_baked_key); + + if (jamtis::try_get_jamtis_amount(sender_receiver_secret, + amount_baked_key, + enote_ptr->core.amount_commitment, + enote_ptr->encoded_amount, + amount_out, + amount_blinding_factor_out)) + { + // we successfully recovered enote amount, write the enote type and return, no more looping needed + CHECK_AND_ASSERT_THROW_MES(jamtis::try_get_jamtis_enote_type(self_send_types_to_check[i], + enote_type_out), "bug: could not convert self-send type into enote type"); + return true; + } + } - return false; -} -//------------------------------------------------------------------------------------------------------------------- -//------------------------------------------------------------------------------------------------------------------- -static bool try_get_basic_record_info_v1_helper(const SpEnoteVariant &enote, - const crypto::x25519_pubkey &enote_ephemeral_pubkey, - const rct::key &input_context, - const crypto::x25519_pubkey &sender_receiver_DH_derivation, - rct::key &nominal_sender_receiver_secret_out, - jamtis::address_tag_t &nominal_address_tag_out) -{ - // 1. q' (jamtis plain enote type) - if (!jamtis::try_get_jamtis_sender_receiver_secret_plain(sender_receiver_DH_derivation, - enote_ephemeral_pubkey, - input_context, - onetime_address_ref(enote), - view_tag_ref(enote), - nominal_sender_receiver_secret_out)) return false; - - // 2. t'_addr - nominal_address_tag_out = jamtis::decrypt_address_tag(nominal_sender_receiver_secret_out, - onetime_address_ref(enote), - addr_tag_enc_ref(enote)); - - return true; -} -//------------------------------------------------------------------------------------------------------------------- -//------------------------------------------------------------------------------------------------------------------- -static bool try_get_basic_record_info_v1_helper(const SpEnoteVariant &enote, - const crypto::x25519_pubkey &enote_ephemeral_pubkey, - const rct::key &input_context, - const crypto::x25519_secret_key &xk_find_received, - rct::key &nominal_sender_receiver_secret_out, - jamtis::address_tag_t &nominal_address_tag_out) -{ - // xK_d = xk_fr * xK_e - crypto::x25519_pubkey sender_receiver_DH_derivation; - crypto::x25519_scmul_key(xk_find_received, enote_ephemeral_pubkey, sender_receiver_DH_derivation); - - return try_get_basic_record_info_v1_helper(enote, - enote_ephemeral_pubkey, - input_context, - sender_receiver_DH_derivation, - nominal_sender_receiver_secret_out, - nominal_address_tag_out); + } } //------------------------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------------------------- -static bool try_handle_basic_record_info_v1_helper(const SpEnoteVariant &enote, +template +static bool try_core_balance_recovery_v1(const SpEnoteVariant &enote, const crypto::x25519_pubkey &enote_ephemeral_pubkey, + const std::uint8_t num_primary_view_tag_bits, const rct::key &input_context, - const jamtis::address_tag_t &nominal_address_tag, - const crypto::x25519_secret_key &xk_find_received, + const rct::key &jamtis_spend_pubkey, + const crypto::secret_key &k_view_balance, + const crypto::x25519_secret_key &d_view_received, + const crypto::x25519_secret_key &d_filter_assist, + const crypto::secret_key &s_generate_address, const jamtis::jamtis_address_tag_cipher_context &cipher_context, - jamtis::address_index_t &nominal_address_index_out, - rct::key &nominal_sender_receiver_secret_out) + const BalanceRecoveryPath balance_recovery_path, + rct::key &nominal_sender_receiver_secret_out, + rct::key &recipient_address_spendkey_out, + jamtis::JamtisEnoteType &enote_type_out, + SpIntermediateLikeEnoteRecord &record_out) // type must contains a superset of SpIntermediateEnoteRecordV1's fields { - // use basic record info to try and get the nominal address index and recover the nominal sender-receiver secret - - // 1. j' - if (!jamtis::try_decipher_address_index(cipher_context, nominal_address_tag, nominal_address_index_out)) - return false; + // "Core" balance recovery is the stages from nominal sender receiver secret derivation and + // before key image calculation for both plain and self-send enotes. We attempt to recover the + // following information and fill in the corresponding fields of an intermediate record: + // whether enote is owned, address index, amount, amount blinding factor, enote type - // 2. xK_d = xk_fr * xK_e - crypto::x25519_pubkey sender_receiver_DH_derivation; - crypto::x25519_scmul_key(xk_find_received, enote_ephemeral_pubkey, sender_receiver_DH_derivation); + // derive nominal sender-receiver secret q' for given path + if (balance_recovery_path == BalanceRecoveryPath::PLAIN) + { + // q' = H_32(xr * d_vr * xG, D_e, input_context) + jamtis::make_jamtis_sender_receiver_secret_plain(d_view_received, + enote_ephemeral_pubkey, + enote_ephemeral_pubkey, + input_context, + nominal_sender_receiver_secret_out); - // 3. q' (jamtis plain enote type) - if (!jamtis::try_get_jamtis_sender_receiver_secret_plain(sender_receiver_DH_derivation, + // test complementary view tag + if (!jamtis::test_jamtis_complementary_view_tag(nominal_sender_receiver_secret_out, + view_tag_ref(enote), + num_primary_view_tag_bits)) + return false; + } + else // send-send scan path + { + // q' = H_32[k_vb](D_e, input_context) + const bool is_auxiliary_self_send_type = balance_recovery_path == BalanceRecoveryPath::AUXILIARY_SELFSEND; + jamtis::make_jamtis_sender_receiver_secret_selfsend(k_view_balance, enote_ephemeral_pubkey, input_context, - onetime_address_ref(enote), - view_tag_ref(enote), - nominal_sender_receiver_secret_out)) - return false; + is_auxiliary_self_send_type, + nominal_sender_receiver_secret_out); + } - return true; -} -//------------------------------------------------------------------------------------------------------------------- -//------------------------------------------------------------------------------------------------------------------- -static bool try_get_intermediate_record_info_v1_helper(const SpEnoteVariant &enote, - const crypto::x25519_pubkey &enote_ephemeral_pubkey, - const jamtis::address_index_t &nominal_address_index, - const rct::key &nominal_sender_receiver_secret, - const rct::key &jamtis_spend_pubkey, - const crypto::x25519_secret_key &xk_unlock_amounts, - const crypto::secret_key &s_generate_address, - rct::key &recipient_address_spendkey_out, - rct::xmr_amount &amount_out, - crypto::secret_key &amount_blinding_factor_out) -{ - // get intermediate info (validate address index, amount, amount blinding factor) for a plain jamtis enote + // addr_tag' = addr_tag_enc XOR H_32(q, Ko) + const jamtis::address_tag_t addr_tag = jamtis::decrypt_address_tag( + nominal_sender_receiver_secret_out, onetime_address_ref(enote), addr_tag_enc_ref(enote)); + + // j' = decipher[s_ct](addr_tag') + jamtis::decipher_address_index(cipher_context, addr_tag, record_out.address_index); - // 1. spend key of address that might own this enote + // K^j_s' = k^j_g' G + k^j_x' X + k^j_u' U + K_s' jamtis::make_jamtis_address_spend_key(jamtis_spend_pubkey, s_generate_address, - nominal_address_index, + record_out.address_index, recipient_address_spendkey_out); - // 2. check if the spend key owns this enote + // [Ko' = k^o_g' G + k^o_x X' + k^o_u U' + K^j_s'] =?= Ko if (!jamtis::test_jamtis_onetime_address(recipient_address_spendkey_out, - nominal_sender_receiver_secret, + nominal_sender_receiver_secret_out, amount_commitment_ref(enote), onetime_address_ref(enote))) return false; - // 3. make the amount commitment baked key - crypto::x25519_secret_key address_privkey; - jamtis::make_jamtis_address_privkey(jamtis_spend_pubkey, s_generate_address, nominal_address_index, address_privkey); - - rct::key amount_baked_key; - jamtis::make_jamtis_amount_baked_key_plain_recipient(address_privkey, - xk_unlock_amounts, - enote_ephemeral_pubkey, - amount_baked_key); - - // 4. try to recover the amount and amount blinding factor - if (!try_get_amount_commitment_information(enote, - nominal_sender_receiver_secret, - amount_baked_key, - amount_out, - amount_blinding_factor_out)) - return false; - - return true; -} -//------------------------------------------------------------------------------------------------------------------- -//------------------------------------------------------------------------------------------------------------------- -static void get_final_record_info_v1_helper(const rct::key &sender_receiver_secret, - const rct::key &amount_commitment, - const jamtis::address_index_t &j, - const rct::key &jamtis_spend_pubkey, - const crypto::secret_key &k_view_balance, - const crypto::secret_key &s_generate_address, - const rct::key &recipient_address_spendkey, - crypto::secret_key &enote_view_extension_g_out, - crypto::secret_key &enote_view_extension_x_out, - crypto::secret_key &enote_view_extension_u_out, - crypto::key_image &key_image_out) -{ - // get final info (enote view privkey, key image) + // D^d_fa = d_fa D_e + crypto::x25519_pubkey dhe_fa; + crypto::x25519_scmul_key(d_filter_assist, enote_ephemeral_pubkey, dhe_fa); - // 1. construct the enote view extensions - make_enote_view_extensions_helper(jamtis_spend_pubkey, - s_generate_address, - j, - recipient_address_spendkey, - sender_receiver_secret, - amount_commitment, - enote_view_extension_g_out, - enote_view_extension_x_out, - enote_view_extension_u_out); - - // 2. make the key image: (k_u + k_m)/(k_x + k_vb) U - make_seraphis_key_image_helper(jamtis_spend_pubkey, - k_view_balance, - enote_view_extension_x_out, - enote_view_extension_u_out, - key_image_out); -} -//------------------------------------------------------------------------------------------------------------------- -//------------------------------------------------------------------------------------------------------------------- -static bool try_get_intermediate_enote_record_v1_finalize(const SpEnoteVariant &enote, - const crypto::x25519_pubkey &enote_ephemeral_pubkey, - const rct::key &input_context, - const jamtis::address_index_t &nominal_address_index, - const rct::key &nominal_sender_receiver_secret, - const rct::key &jamtis_spend_pubkey, - const crypto::x25519_secret_key &xk_unlock_amounts, - const crypto::secret_key &s_generate_address, - SpIntermediateEnoteRecordV1 &record_out) -{ - // finalize an intermediate enote record + // if we use a standard view tag, check primary tag for correctness + if (balance_recovery_path != BalanceRecoveryPath::AUXILIARY_SELFSEND) + { + if (!jamtis::test_jamtis_primary_view_tag(dhe_fa, + onetime_address_ref(enote), + view_tag_ref(enote), + num_primary_view_tag_bits)) + return false; + } - // 1. get intermediate info: address spendkey, amount and amount blinding factor - rct::key recipient_address_spendkey_temp; - if (!try_get_intermediate_record_info_v1_helper(enote, + // try to recover amount commitment information: amount & blinding factor + if (!try_recover_amount_commitment_info(enote, enote_ephemeral_pubkey, - nominal_address_index, - nominal_sender_receiver_secret, + nominal_sender_receiver_secret_out, jamtis_spend_pubkey, - xk_unlock_amounts, + k_view_balance, + d_view_received, + record_out.address_index, s_generate_address, - recipient_address_spendkey_temp, + balance_recovery_path, record_out.amount, - record_out.amount_blinding_factor)) + record_out.amount_blinding_factor, + enote_type_out)) return false; - // 2. record the enote and sender-receiver secret - record_out.enote = enote; + // finish filling out intermediate record fields + record_out.enote = enote; record_out.enote_ephemeral_pubkey = enote_ephemeral_pubkey; - record_out.input_context = input_context; - record_out.address_index = nominal_address_index; + record_out.num_primary_view_tag_bits = num_primary_view_tag_bits; + record_out.input_context = input_context; return true; } //------------------------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------------------------- -static bool try_get_enote_record_v1_plain_finalize(const SpEnoteVariant &enote, +static bool try_plain_core_balance_recovery_v1(const SpEnoteVariant &enote, const crypto::x25519_pubkey &enote_ephemeral_pubkey, + const std::uint8_t num_primary_view_tag_bits, const rct::key &input_context, - const jamtis::address_index_t &nominal_address_index, - const rct::key &nominal_sender_receiver_secret, const rct::key &jamtis_spend_pubkey, - const crypto::secret_key &k_view_balance, - const crypto::x25519_secret_key &xk_unlock_amounts, + const crypto::x25519_secret_key &d_view_received, + const crypto::x25519_secret_key &d_filter_assist, const crypto::secret_key &s_generate_address, - SpEnoteRecordV1 &record_out) + const jamtis::jamtis_address_tag_cipher_context &cipher_context, + SpIntermediateEnoteRecordV1 &record_out) { - // finalize an enote record - - // 1. get intermediate info: address spendkey, amount and amount blinding factor - rct::key recipient_address_spendkey_temp; - if (!try_get_intermediate_record_info_v1_helper(enote, - enote_ephemeral_pubkey, - nominal_address_index, - nominal_sender_receiver_secret, - jamtis_spend_pubkey, - xk_unlock_amounts, - s_generate_address, - recipient_address_spendkey_temp, - record_out.amount, - record_out.amount_blinding_factor)) - return false; - - // 2. get final info: enote view extensions, key image - get_final_record_info_v1_helper(nominal_sender_receiver_secret, - amount_commitment_ref(enote), - nominal_address_index, + rct::key dummy_sender_receiver_secret; + rct::key dummy_recipient_address_spendkey; + jamtis::JamtisEnoteType dummy_enote_type; + return try_core_balance_recovery_v1(enote, + enote_ephemeral_pubkey, + num_primary_view_tag_bits, + input_context, jamtis_spend_pubkey, - k_view_balance, + crypto::null_skey, + d_view_received, + d_filter_assist, s_generate_address, - recipient_address_spendkey_temp, - record_out.enote_view_extension_g, - record_out.enote_view_extension_x, - record_out.enote_view_extension_u, - record_out.key_image); - - // 3. record the remaining information - record_out.enote = enote; - record_out.enote_ephemeral_pubkey = enote_ephemeral_pubkey; - record_out.input_context = input_context; - record_out.address_index = nominal_address_index; - record_out.type = jamtis::JamtisEnoteType::PLAIN; - - return true; + cipher_context, + BalanceRecoveryPath::PLAIN, + dummy_sender_receiver_secret, + dummy_recipient_address_spendkey, + dummy_enote_type, + record_out); } //------------------------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------------------------- -static bool try_get_enote_record_v1_selfsend_for_type(const SpEnoteVariant &enote, +static bool try_complete_balance_recovery_v1(const SpEnoteVariant &enote, const crypto::x25519_pubkey &enote_ephemeral_pubkey, + const std::uint8_t num_primary_view_tag_bits, const rct::key &input_context, const rct::key &jamtis_spend_pubkey, const crypto::secret_key &k_view_balance, - const crypto::x25519_secret_key &xk_find_received, + const crypto::x25519_secret_key &d_view_received, + const crypto::x25519_secret_key &d_filter_assist, const crypto::secret_key &s_generate_address, const jamtis::jamtis_address_tag_cipher_context &cipher_context, - const jamtis::JamtisSelfSendType test_type, + const BalanceRecoveryPath balance_recovery_path, SpEnoteRecordV1 &record_out) { - // get an enote record for a specified jamtis selfsend enote type - - // 1. sender-receiver secret for specified self-send type - rct::key q; - jamtis::make_jamtis_sender_receiver_secret_selfsend(k_view_balance, - enote_ephemeral_pubkey, - input_context, - test_type, - q); - - // 2. decrypt encrypted address tag - const jamtis::address_tag_t decrypted_addr_tag{ - decrypt_address_tag(q, onetime_address_ref(enote), addr_tag_enc_ref(enote)) - }; - - // 3. try to get the address index - if (!jamtis::try_decipher_address_index(cipher_context, decrypted_addr_tag, record_out.address_index)) - return false; + // "Complete" balance recovery is the all stages of balance recovery after the primary view tag + // check for both plain and self-send enotes. We attempt to recover the following information + // and fill in the corresponding fields of a full enote record: whether enote is owned, address + // index, amount, amount blinding factor, enote type, enote view extensions, key image - // 4. verify the view tag - // note: we verify the view tag to ensure our enote is 100% well-formed, even though we use the address index - // decipher as the main test for identifying self-sends - jamtis::view_tag_t test_view_tag; - jamtis::make_jamtis_view_tag(xk_find_received, enote_ephemeral_pubkey, onetime_address_ref(enote), test_view_tag); - if (test_view_tag != view_tag_ref(enote)) - return false; + if (balance_recovery_path == BalanceRecoveryPath::AUXILIARY_SELFSEND) + { + // test auxiliary self-send view tag + if (!jamtis::test_jamtis_auxiliary_view_tag(k_view_balance, + onetime_address_ref(enote), + view_tag_ref(enote))) + return false; + } - // 5. spend key of address that might own this enote + // attempt core balance recovery for given path + rct::key nominal_sender_receiver_secret; rct::key recipient_address_spendkey; - jamtis::make_jamtis_address_spend_key(jamtis_spend_pubkey, - s_generate_address, - record_out.address_index, - recipient_address_spendkey); - - // 6. save a copy of the amount commitment - const rct::key amount_commitment{amount_commitment_ref(enote)}; - - // 7. check if the spend key owns this enote - if (!jamtis::test_jamtis_onetime_address(recipient_address_spendkey, - q, - amount_commitment, - onetime_address_ref(enote))) - return false; - - // 8. compute the amount baked key (selfsend version) - rct::key amount_baked_key; - jamtis::make_jamtis_amount_baked_key_selfsend(k_view_balance, q, amount_baked_key); - - // 9. try to recover the amount and blinding factor - if (!try_get_amount_commitment_information(enote, - q, - amount_baked_key, - record_out.amount, - record_out.amount_blinding_factor)) + if (!try_core_balance_recovery_v1(enote, + enote_ephemeral_pubkey, + num_primary_view_tag_bits, + input_context, + jamtis_spend_pubkey, + k_view_balance, + d_view_received, + d_filter_assist, + s_generate_address, + cipher_context, + balance_recovery_path, + nominal_sender_receiver_secret, + recipient_address_spendkey, + record_out.type, + record_out)) return false; - // 10. construct enote view extensions + // make enote view extensions make_enote_view_extensions_helper(jamtis_spend_pubkey, s_generate_address, record_out.address_index, recipient_address_spendkey, - q, - amount_commitment, + nominal_sender_receiver_secret, + amount_commitment_ref(enote), record_out.enote_view_extension_g, record_out.enote_view_extension_x, record_out.enote_view_extension_u); - // 11. make key image: (k_u + k_m)/(k_x + k_vb) U + // make key image: (k_u + k_m)/(k_x + k_vb) U make_seraphis_key_image_helper(jamtis_spend_pubkey, k_view_balance, record_out.enote_view_extension_x, record_out.enote_view_extension_u, record_out.key_image); - // 12. record the remaining information - record_out.enote = enote; - record_out.enote_ephemeral_pubkey = enote_ephemeral_pubkey; - record_out.input_context = input_context; - CHECK_AND_ASSERT_THROW_MES(jamtis::try_get_jamtis_enote_type(test_type, record_out.type), - "getting self-send enote record (v1): could not convert self-send type to enote type (bug)."); - return true; } //------------------------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------------------------- +static bool try_complete_balance_recovery_multipath_v1(const SpEnoteVariant &enote, + const crypto::x25519_pubkey &enote_ephemeral_pubkey, + const std::uint8_t num_primary_view_tag_bits, + const rct::key &input_context, + const rct::key &jamtis_spend_pubkey, + const crypto::secret_key &k_view_balance, + const crypto::x25519_secret_key &d_view_received, + const crypto::x25519_secret_key &d_filter_assist, + const crypto::secret_key &s_generate_address, + const jamtis::jamtis_address_tag_cipher_context &cipher_context, + const epee::span balance_recovery_paths, + SpEnoteRecordV1 &record_out) +{ + for (size_t i = 0; i < balance_recovery_paths.size(); ++i) + { + if (try_complete_balance_recovery_v1(enote, + enote_ephemeral_pubkey, + num_primary_view_tag_bits, + input_context, + jamtis_spend_pubkey, + k_view_balance, + d_view_received, + d_filter_assist, + s_generate_address, + cipher_context, + balance_recovery_paths[i], + record_out)) + return true; + } + + return false; +} +//------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------------------------------------------- bool try_get_basic_enote_record_v1(const SpEnoteVariant &enote, const crypto::x25519_pubkey &enote_ephemeral_pubkey, + const std::uint8_t num_primary_view_tag_bits, const rct::key &input_context, - const crypto::x25519_pubkey &sender_receiver_DH_derivation, + const crypto::x25519_pubkey &dhe_fa, SpBasicEnoteRecordV1 &basic_record_out) { // get a basic record - // 1. try to decrypt the address tag - rct::key dummy_q; - if (!try_get_basic_record_info_v1_helper(enote, - enote_ephemeral_pubkey, - input_context, - sender_receiver_DH_derivation, - dummy_q, - basic_record_out.nominal_address_tag)) + // 1. check against primary view tag + if (!jamtis::test_jamtis_primary_view_tag(dhe_fa, + onetime_address_ref(enote), + view_tag_ref(enote), + num_primary_view_tag_bits)) return false; // 2. copy remaining information basic_record_out.enote = enote; basic_record_out.enote_ephemeral_pubkey = enote_ephemeral_pubkey; basic_record_out.input_context = input_context; + basic_record_out.passed_exclusive_check = true; return true; } //------------------------------------------------------------------------------------------------------------------- bool try_get_basic_enote_record_v1(const SpEnoteVariant &enote, const crypto::x25519_pubkey &enote_ephemeral_pubkey, + const std::uint8_t num_primary_view_tag_bits, const rct::key &input_context, - const crypto::x25519_secret_key &xk_find_received, + const crypto::x25519_secret_key &d_filter_assist, SpBasicEnoteRecordV1 &basic_record_out) { // compute DH derivation then get basic record - // sender-receiver DH derivation - crypto::x25519_pubkey sender_receiver_DH_derivation; - crypto::x25519_scmul_key(xk_find_received, enote_ephemeral_pubkey, sender_receiver_DH_derivation); + // D^d_fa = xr D^j_fa = d_fa D_e + crypto::x25519_pubkey dhe_fa; + crypto::x25519_scmul_key(d_filter_assist, enote_ephemeral_pubkey, dhe_fa); return try_get_basic_enote_record_v1(enote, enote_ephemeral_pubkey, + num_primary_view_tag_bits, input_context, - sender_receiver_DH_derivation, + dhe_fa, basic_record_out); } //------------------------------------------------------------------------------------------------------------------- bool try_get_intermediate_enote_record_v1(const SpEnoteVariant &enote, const crypto::x25519_pubkey &enote_ephemeral_pubkey, + const std::uint8_t num_primary_view_tag_bits, const rct::key &input_context, const rct::key &jamtis_spend_pubkey, - const crypto::x25519_secret_key &xk_unlock_amounts, - const crypto::x25519_secret_key &xk_find_received, + const crypto::x25519_secret_key &d_view_received, + const crypto::x25519_secret_key &d_filter_assist, const crypto::secret_key &s_generate_address, const jamtis::jamtis_address_tag_cipher_context &cipher_context, SpIntermediateEnoteRecordV1 &record_out) { // try to process basic info then get an intermediate record - // 1. q' and addr_tag' - rct::key nominal_sender_receiver_secret; - jamtis::address_tag_t nominal_address_tag; - if (!try_get_basic_record_info_v1_helper(enote, - enote_ephemeral_pubkey, - input_context, - xk_find_received, - nominal_sender_receiver_secret, - nominal_address_tag)) - return false; - - // 2. j' - jamtis::address_index_t nominal_address_index; - if (!jamtis::try_decipher_address_index(cipher_context, nominal_address_tag, nominal_address_index)) - return false; - - // 3. try to finalize the intermediate enote record - if (!try_get_intermediate_enote_record_v1_finalize(enote, + // test primary view tag + if (!jamtis::test_jamtis_primary_view_tag(d_filter_assist, enote_ephemeral_pubkey, - input_context, - nominal_address_index, - nominal_sender_receiver_secret, - jamtis_spend_pubkey, - xk_unlock_amounts, - s_generate_address, - record_out)) + onetime_address_ref(enote), + view_tag_ref(enote), + num_primary_view_tag_bits)) return false; - return true; + return try_plain_core_balance_recovery_v1(enote, + enote_ephemeral_pubkey, + num_primary_view_tag_bits, + input_context, + jamtis_spend_pubkey, + d_view_received, + d_filter_assist, + s_generate_address, + cipher_context, + record_out); } //------------------------------------------------------------------------------------------------------------------- bool try_get_intermediate_enote_record_v1(const SpEnoteVariant &enote, const crypto::x25519_pubkey &enote_ephemeral_pubkey, + const std::uint8_t num_primary_view_tag_bits, const rct::key &input_context, const rct::key &jamtis_spend_pubkey, - const crypto::x25519_secret_key &xk_unlock_amounts, - const crypto::x25519_secret_key &xk_find_received, + const crypto::x25519_secret_key &d_view_received, + const crypto::x25519_secret_key &d_filter_assist, const crypto::secret_key &s_generate_address, SpIntermediateEnoteRecordV1 &record_out) { @@ -667,10 +575,11 @@ bool try_get_intermediate_enote_record_v1(const SpEnoteVariant &enote, return try_get_intermediate_enote_record_v1(enote, enote_ephemeral_pubkey, + num_primary_view_tag_bits, input_context, jamtis_spend_pubkey, - xk_unlock_amounts, - xk_find_received, + d_view_received, + d_filter_assist, s_generate_address, cipher_context, record_out); @@ -678,46 +587,33 @@ bool try_get_intermediate_enote_record_v1(const SpEnoteVariant &enote, //------------------------------------------------------------------------------------------------------------------- bool try_get_intermediate_enote_record_v1(const SpBasicEnoteRecordV1 &basic_record, const rct::key &jamtis_spend_pubkey, - const crypto::x25519_secret_key &xk_unlock_amounts, - const crypto::x25519_secret_key &xk_find_received, + const crypto::x25519_secret_key &d_view_received, + const crypto::x25519_secret_key &d_filter_assist, const crypto::secret_key &s_generate_address, const jamtis::jamtis_address_tag_cipher_context &cipher_context, SpIntermediateEnoteRecordV1 &record_out) { // process basic record then get an intermediate record - // 1. process the basic record - jamtis::address_index_t nominal_address_index; - rct::key nominal_sender_receiver_secret; - if (!try_handle_basic_record_info_v1_helper(basic_record.enote, - basic_record.enote_ephemeral_pubkey, - basic_record.input_context, - basic_record.nominal_address_tag, - xk_find_received, - cipher_context, - nominal_address_index, - nominal_sender_receiver_secret)) + if (!basic_record.passed_exclusive_check) return false; - // 2. finalize the intermediate record - if (!try_get_intermediate_enote_record_v1_finalize(basic_record.enote, - basic_record.enote_ephemeral_pubkey, - basic_record.input_context, - nominal_address_index, - nominal_sender_receiver_secret, - jamtis_spend_pubkey, - xk_unlock_amounts, - s_generate_address, - record_out)) - return false; - - return true; + return try_plain_core_balance_recovery_v1(basic_record.enote, + basic_record.enote_ephemeral_pubkey, + basic_record.num_primary_view_tag_bits, + basic_record.input_context, + jamtis_spend_pubkey, + d_view_received, + d_filter_assist, + s_generate_address, + cipher_context, + record_out); } //------------------------------------------------------------------------------------------------------------------- bool try_get_intermediate_enote_record_v1(const SpBasicEnoteRecordV1 &basic_record, const rct::key &jamtis_spend_pubkey, - const crypto::x25519_secret_key &xk_unlock_amounts, - const crypto::x25519_secret_key &xk_find_received, + const crypto::x25519_secret_key &d_view_received, + const crypto::x25519_secret_key &d_filter_assist, const crypto::secret_key &s_generate_address, SpIntermediateEnoteRecordV1 &record_out) { @@ -729,224 +625,140 @@ bool try_get_intermediate_enote_record_v1(const SpBasicEnoteRecordV1 &basic_reco return try_get_intermediate_enote_record_v1(basic_record, jamtis_spend_pubkey, - xk_unlock_amounts, - xk_find_received, + d_view_received, + d_filter_assist, s_generate_address, cipher_context, record_out); } //------------------------------------------------------------------------------------------------------------------- -bool try_get_enote_record_v1_plain(const SpEnoteVariant &enote, - const crypto::x25519_pubkey &enote_ephemeral_pubkey, - const rct::key &input_context, +bool try_get_enote_record_v1(const SpBasicEnoteRecordV1 &basic_record, const rct::key &jamtis_spend_pubkey, const crypto::secret_key &k_view_balance, - SpEnoteRecordV1 &record_out) -{ - // try to process basic info then get intermediate record - crypto::x25519_secret_key xk_unlock_amounts; - crypto::x25519_secret_key xk_find_received; - crypto::secret_key s_generate_address; - crypto::secret_key s_cipher_tag; - jamtis::make_jamtis_unlockamounts_key(k_view_balance, xk_unlock_amounts); - jamtis::make_jamtis_findreceived_key(k_view_balance, xk_find_received); - jamtis::make_jamtis_generateaddress_secret(k_view_balance, s_generate_address); - jamtis::make_jamtis_ciphertag_secret(s_generate_address, s_cipher_tag); - - const jamtis::jamtis_address_tag_cipher_context cipher_context{s_cipher_tag}; - - // 1. q' and addr_tag' - rct::key nominal_sender_receiver_secret; - jamtis::address_tag_t nominal_address_tag; - if (!try_get_basic_record_info_v1_helper(enote, - enote_ephemeral_pubkey, - input_context, - xk_find_received, - nominal_sender_receiver_secret, - nominal_address_tag)) - return false; - - // 2. j' - jamtis::address_index_t nominal_address_index; - if (!jamtis::try_decipher_address_index(cipher_context, nominal_address_tag, nominal_address_index)) - return false; - - // 3. finalize the enote record - if (!try_get_enote_record_v1_plain_finalize(enote, - enote_ephemeral_pubkey, - input_context, - nominal_address_index, - nominal_sender_receiver_secret, - jamtis_spend_pubkey, - k_view_balance, - xk_unlock_amounts, - s_generate_address, - record_out)) - return false; - - return true; -} -//------------------------------------------------------------------------------------------------------------------- -bool try_get_enote_record_v1_plain(const SpBasicEnoteRecordV1 &basic_record, - const rct::key &jamtis_spend_pubkey, - const crypto::secret_key &k_view_balance, - const crypto::x25519_secret_key &xk_unlock_amounts, - const crypto::x25519_secret_key &xk_find_received, + const crypto::x25519_secret_key &d_view_received, + const crypto::x25519_secret_key &d_filter_assist, const crypto::secret_key &s_generate_address, const jamtis::jamtis_address_tag_cipher_context &cipher_context, SpEnoteRecordV1 &record_out) { - // process basic record then get an enote record + constexpr BalanceRecoveryPath ALL_PATHS[] = { + BalanceRecoveryPath::PLAIN, + BalanceRecoveryPath::EXCLUSIVE_SELFSEND, + BalanceRecoveryPath::AUXILIARY_SELFSEND }; - // 1. process the basic record - jamtis::address_index_t nominal_address_index; - rct::key nominal_sender_receiver_secret; - if (!try_handle_basic_record_info_v1_helper(basic_record.enote, - basic_record.enote_ephemeral_pubkey, - basic_record.input_context, - basic_record.nominal_address_tag, - xk_find_received, - cipher_context, - nominal_address_index, - nominal_sender_receiver_secret)) - return false; + constexpr BalanceRecoveryPath AUX_PATH[] = { BalanceRecoveryPath::AUXILIARY_SELFSEND }; - // 2. finalize the enote record - if (!try_get_enote_record_v1_plain_finalize(basic_record.enote, - basic_record.enote_ephemeral_pubkey, - basic_record.input_context, - nominal_address_index, - nominal_sender_receiver_secret, - jamtis_spend_pubkey, - k_view_balance, - xk_unlock_amounts, - s_generate_address, - record_out)) - return false; + const auto paths_to_try = basic_record.passed_exclusive_check + ? epee::span(ALL_PATHS) + : epee::span(AUX_PATH); - return true; + return try_complete_balance_recovery_multipath_v1(basic_record.enote, + basic_record.enote_ephemeral_pubkey, + basic_record.num_primary_view_tag_bits, + basic_record.input_context, + jamtis_spend_pubkey, + k_view_balance, + d_view_received, + d_filter_assist, + s_generate_address, + cipher_context, + paths_to_try, + record_out); } //------------------------------------------------------------------------------------------------------------------- -bool try_get_enote_record_v1_plain(const SpBasicEnoteRecordV1 &basic_record, +bool try_get_enote_record_v1(const SpEnoteVariant &enote, + const crypto::x25519_pubkey &enote_ephemeral_pubkey, + const std::uint8_t num_primary_view_tag_bits, + const rct::key &input_context, const rct::key &jamtis_spend_pubkey, const crypto::secret_key &k_view_balance, SpEnoteRecordV1 &record_out) { - // make secrets then get enote record - crypto::x25519_secret_key xk_unlock_amounts; - crypto::x25519_secret_key xk_find_received; + // 1. generate account secrets tree from k_vb + crypto::x25519_secret_key d_view_received; + crypto::x25519_secret_key d_filter_assist; crypto::secret_key s_generate_address; crypto::secret_key s_cipher_tag; - jamtis::make_jamtis_unlockamounts_key(k_view_balance, xk_unlock_amounts); - jamtis::make_jamtis_findreceived_key(k_view_balance, xk_find_received); - jamtis::make_jamtis_generateaddress_secret(k_view_balance, s_generate_address); + jamtis::make_jamtis_viewreceived_key(k_view_balance, d_view_received); + jamtis::make_jamtis_filterassist_key(d_view_received, d_filter_assist); + jamtis::make_jamtis_generateaddress_secret(d_view_received, s_generate_address); jamtis::make_jamtis_ciphertag_secret(s_generate_address, s_cipher_tag); const jamtis::jamtis_address_tag_cipher_context cipher_context{s_cipher_tag}; - return try_get_enote_record_v1_plain(basic_record, + // 2. do primary DH and primary view tag check + const bool view_tag_match = jamtis::test_jamtis_primary_view_tag(d_filter_assist, + enote_ephemeral_pubkey, + onetime_address_ref(enote), + view_tag_ref(enote), + num_primary_view_tag_bits); + + // 3. if the primary view tag matched, try scanning for plain and exclusive self-send enotes + constexpr BalanceRecoveryPath ALL_PATHS[] = { + BalanceRecoveryPath::AUXILIARY_SELFSEND, + BalanceRecoveryPath::PLAIN, + BalanceRecoveryPath::EXCLUSIVE_SELFSEND}; + + const epee::span paths_to_check(ALL_PATHS, + view_tag_match ? 3 : 1); + + // 4. if the other paths fail, always try auxiliary scanning since we have no tx information + return try_complete_balance_recovery_multipath_v1(enote, + enote_ephemeral_pubkey, + num_primary_view_tag_bits, + input_context, jamtis_spend_pubkey, k_view_balance, - xk_unlock_amounts, - xk_find_received, + d_view_received, + d_filter_assist, s_generate_address, cipher_context, + paths_to_check, record_out); } //------------------------------------------------------------------------------------------------------------------- -bool try_get_enote_record_v1_plain(const SpIntermediateEnoteRecordV1 &intermediate_record, +bool try_get_enote_record_v1(const SpIntermediateEnoteRecordV1 &intermediate_record, const rct::key &jamtis_spend_pubkey, const crypto::secret_key &k_view_balance, SpEnoteRecordV1 &record_out) { - return try_get_enote_record_v1_plain(intermediate_record.enote, + return try_get_enote_record_v1(intermediate_record.enote, intermediate_record.enote_ephemeral_pubkey, + intermediate_record.num_primary_view_tag_bits, intermediate_record.input_context, jamtis_spend_pubkey, k_view_balance, record_out); } //------------------------------------------------------------------------------------------------------------------- -bool try_get_enote_record_v1_selfsend(const SpEnoteVariant &enote, - const crypto::x25519_pubkey &enote_ephemeral_pubkey, - const rct::key &input_context, +bool try_get_enote_record_plain_v1(const SpBasicEnoteRecordV1 &basic_record, const rct::key &jamtis_spend_pubkey, const crypto::secret_key &k_view_balance, - const crypto::x25519_secret_key &xk_find_received, + const crypto::x25519_secret_key &d_view_received, + const crypto::x25519_secret_key &d_filter_assist, const crypto::secret_key &s_generate_address, const jamtis::jamtis_address_tag_cipher_context &cipher_context, SpEnoteRecordV1 &record_out) { - // try to get an enote record with all the self-send types - for (unsigned char self_send_type{0}; - self_send_type <= static_cast(jamtis::JamtisSelfSendType::MAX); - ++self_send_type) - { - if (try_get_enote_record_v1_selfsend_for_type(enote, - enote_ephemeral_pubkey, - input_context, - jamtis_spend_pubkey, - k_view_balance, - xk_find_received, - s_generate_address, - cipher_context, - static_cast(self_send_type), - record_out)) - return true; - } - - return false; -} -//------------------------------------------------------------------------------------------------------------------- -bool try_get_enote_record_v1_selfsend(const SpEnoteVariant &enote, - const crypto::x25519_pubkey &enote_ephemeral_pubkey, - const rct::key &input_context, - const rct::key &jamtis_spend_pubkey, - const crypto::secret_key &k_view_balance, - SpEnoteRecordV1 &record_out) -{ - // make generate-address secret and cipher context then get enote record - crypto::x25519_secret_key xk_find_received; - crypto::secret_key s_generate_address; - crypto::secret_key s_cipher_tag; - jamtis::make_jamtis_findreceived_key(k_view_balance, xk_find_received); - jamtis::make_jamtis_generateaddress_secret(k_view_balance, s_generate_address); - jamtis::make_jamtis_ciphertag_secret(s_generate_address, s_cipher_tag); - - const jamtis::jamtis_address_tag_cipher_context cipher_context{s_cipher_tag}; + if (!basic_record.passed_exclusive_check) + return false; - return try_get_enote_record_v1_selfsend(enote, - enote_ephemeral_pubkey, - input_context, - jamtis_spend_pubkey, - k_view_balance, - xk_find_received, - s_generate_address, - cipher_context, - record_out); -} -//------------------------------------------------------------------------------------------------------------------- -bool try_get_enote_record_v1(const SpEnoteVariant &enote, - const crypto::x25519_pubkey &enote_ephemeral_pubkey, - const rct::key &input_context, - const rct::key &jamtis_spend_pubkey, - const crypto::secret_key &k_view_balance, - SpEnoteRecordV1 &record_out) -{ - // note: check for selfsend first since it is very fast for unowned enotes - // (assumes selfsends and plain enotes appear in similar quantities) - return try_get_enote_record_v1_selfsend(enote, - enote_ephemeral_pubkey, - input_context, - jamtis_spend_pubkey, - k_view_balance, - record_out) || - try_get_enote_record_v1_plain(enote, - enote_ephemeral_pubkey, - input_context, + jamtis::JamtisEnoteType enote_type{}; + if (!try_complete_balance_recovery_v1(basic_record.enote, + basic_record.enote_ephemeral_pubkey, + basic_record.num_primary_view_tag_bits, + basic_record.input_context, jamtis_spend_pubkey, k_view_balance, - record_out); + d_view_received, + d_filter_assist, + s_generate_address, + cipher_context, + BalanceRecoveryPath::PLAIN, + record_out)) + return false; + + return enote_type == jamtis::JamtisEnoteType::PLAIN; } //------------------------------------------------------------------------------------------------------------------- } //namespace sp diff --git a/src/seraphis_main/enote_record_utils.h b/src/seraphis_main/enote_record_utils.h index 4701564105..fecc0b0164 100644 --- a/src/seraphis_main/enote_record_utils.h +++ b/src/seraphis_main/enote_record_utils.h @@ -53,29 +53,34 @@ namespace sp * brief: try_get_basic_enote_record_v1 - try to extract a basic enote record from an enote * param: enote - * param: enote_ephemeral_pubkey - +* param: num_primary_view_tag_bits - npbits * param: input_context - -* param: sender_receiver_DH_derivation - +* param: dhe_fa - D^d_fa = xr D^j_fa = d_fa D_e +* param: d_filter_assist - * outparam: basic_record_out - * return: true if extraction succeeded */ bool try_get_basic_enote_record_v1(const SpEnoteVariant &enote, const crypto::x25519_pubkey &enote_ephemeral_pubkey, + const std::uint8_t num_primary_view_tag_bits, const rct::key &input_context, - const crypto::x25519_pubkey &sender_receiver_DH_derivation, + const crypto::x25519_pubkey &dhe_fa, SpBasicEnoteRecordV1 &basic_record_out); bool try_get_basic_enote_record_v1(const SpEnoteVariant &enote, const crypto::x25519_pubkey &enote_ephemeral_pubkey, + const std::uint8_t num_primary_view_tag_bits, const rct::key &input_context, - const crypto::x25519_secret_key &xk_find_received, + const crypto::x25519_secret_key &d_filter_assist, SpBasicEnoteRecordV1 &basic_record_out); /** * brief: try_get_intermediate_enote_record_v1 - try to extract an intermediate enote record from an enote * param: enote - * param: enote_ephemeral_pubkey - +* param: num_primary_view_tag_bits - npbits * param: input_context - * param: jamtis_spend_pubkey - -* param: xk_unlock_amounts - -* param: xk_find_received - +* param: d_view_received - +* param: d_filter_assist - * param: s_generate_address - * param: cipher_context - * outparam: record_out - @@ -83,111 +88,65 @@ bool try_get_basic_enote_record_v1(const SpEnoteVariant &enote, */ bool try_get_intermediate_enote_record_v1(const SpEnoteVariant &enote, const crypto::x25519_pubkey &enote_ephemeral_pubkey, + const std::uint8_t num_primary_view_tag_bits, const rct::key &input_context, const rct::key &jamtis_spend_pubkey, - const crypto::x25519_secret_key &xk_unlock_amounts, - const crypto::x25519_secret_key &xk_find_received, + const crypto::x25519_secret_key &d_view_received, + const crypto::x25519_secret_key &d_filter_assist, const crypto::secret_key &s_generate_address, const jamtis::jamtis_address_tag_cipher_context &cipher_context, SpIntermediateEnoteRecordV1 &record_out); bool try_get_intermediate_enote_record_v1(const SpEnoteVariant &enote, const crypto::x25519_pubkey &enote_ephemeral_pubkey, + const std::uint8_t num_primary_view_tag_bits, const rct::key &input_context, const rct::key &jamtis_spend_pubkey, - const crypto::x25519_secret_key &xk_unlock_amounts, - const crypto::x25519_secret_key &xk_find_received, + const crypto::x25519_secret_key &d_view_received, + const crypto::x25519_secret_key &d_filter_assist, const crypto::secret_key &s_generate_address, SpIntermediateEnoteRecordV1 &record_out); bool try_get_intermediate_enote_record_v1(const SpBasicEnoteRecordV1 &basic_record, const rct::key &jamtis_spend_pubkey, - const crypto::x25519_secret_key &xk_unlock_amounts, - const crypto::x25519_secret_key &xk_find_received, + const crypto::x25519_secret_key &d_view_received, + const crypto::x25519_secret_key &d_filter_assist, const crypto::secret_key &s_generate_address, const jamtis::jamtis_address_tag_cipher_context &cipher_context, SpIntermediateEnoteRecordV1 &record_out); bool try_get_intermediate_enote_record_v1(const SpBasicEnoteRecordV1 &basic_record, const rct::key &jamtis_spend_pubkey, - const crypto::x25519_secret_key &xk_unlock_amounts, - const crypto::x25519_secret_key &xk_find_received, + const crypto::x25519_secret_key &d_view_received, + const crypto::x25519_secret_key &d_filter_assist, const crypto::secret_key &s_generate_address, SpIntermediateEnoteRecordV1 &record_out); /** -* brief: try_get_enote_record_v1_plain - try to extract an enote record from an enote -* - plain jamtis enote type attempt -* param: enote - -* param: enote_ephemeral_pubkey - -* param: input_context - -* param: jamtis_spend_pubkey - -* param: k_view_balance - -* outparam: record_out - -* return: true if extraction succeeded +* brief: try_get_enote_record_v1 - try to extract an enote record from an enote, plain or self-send */ -bool try_get_enote_record_v1_plain(const SpEnoteVariant &enote, - const crypto::x25519_pubkey &enote_ephemeral_pubkey, - const rct::key &input_context, - const rct::key &jamtis_spend_pubkey, - const crypto::secret_key &k_view_balance, - SpEnoteRecordV1 &record_out); -bool try_get_enote_record_v1_plain(const SpBasicEnoteRecordV1 &basic_record, +bool try_get_enote_record_v1(const SpBasicEnoteRecordV1 &basic_record, const rct::key &jamtis_spend_pubkey, const crypto::secret_key &k_view_balance, - const crypto::x25519_secret_key &xk_unlock_amounts, - const crypto::x25519_secret_key &xk_find_received, + const crypto::x25519_secret_key &d_view_received, + const crypto::x25519_secret_key &d_filter_assist, const crypto::secret_key &s_generate_address, const jamtis::jamtis_address_tag_cipher_context &cipher_context, SpEnoteRecordV1 &record_out); -bool try_get_enote_record_v1_plain(const SpBasicEnoteRecordV1 &basic_record, - const rct::key &jamtis_spend_pubkey, - const crypto::secret_key &k_view_balance, - SpEnoteRecordV1 &record_out); -bool try_get_enote_record_v1_plain(const SpIntermediateEnoteRecordV1 &intermediate_record, - const rct::key &jamtis_spend_pubkey, - const crypto::secret_key &k_view_balance, - SpEnoteRecordV1 &record_out); -/** -* brief: try_get_enote_record_v1_selfsend - try to extract an enote record from an enote -* - selfsend jamtis enote type attempt -* param: enote - -* param: enote_ephemeral_pubkey - -* param: input_context - -* param: jamtis_spend_pubkey - -* param: k_view_balance - -* param: xk_find_received - -* param: s_generate_address - -* param: cipher_context - -* outparam: record_out - -* return: true if extraction succeeded -*/ -bool try_get_enote_record_v1_selfsend(const SpEnoteVariant &enote, +bool try_get_enote_record_v1(const SpEnoteVariant &enote, const crypto::x25519_pubkey &enote_ephemeral_pubkey, + const std::uint8_t num_primary_view_tag_bits, const rct::key &input_context, const rct::key &jamtis_spend_pubkey, const crypto::secret_key &k_view_balance, - const crypto::x25519_secret_key &xk_find_received, - const crypto::secret_key &s_generate_address, - const jamtis::jamtis_address_tag_cipher_context &cipher_context, SpEnoteRecordV1 &record_out); -bool try_get_enote_record_v1_selfsend(const SpEnoteVariant &enote, - const crypto::x25519_pubkey &enote_ephemeral_pubkey, - const rct::key &input_context, +bool try_get_enote_record_v1(const SpIntermediateEnoteRecordV1 &intermediate_record, const rct::key &jamtis_spend_pubkey, const crypto::secret_key &k_view_balance, SpEnoteRecordV1 &record_out); -/** -* brief: try_get_enote_record_v1 - try to extract an enote record from an enote (which can be any jamtis enote type) -* param: enote - -* param: enote_ephemeral_pubkey - -* param: input_context - -* param: jamtis_spend_pubkey - -* param: k_view_balance - -* outparam: record_out - -* return: true if extraction succeeded -*/ -bool try_get_enote_record_v1(const SpEnoteVariant &enote, - const crypto::x25519_pubkey &enote_ephemeral_pubkey, - const rct::key &input_context, +bool try_get_enote_record_plain_v1(const SpBasicEnoteRecordV1 &basic_record, const rct::key &jamtis_spend_pubkey, const crypto::secret_key &k_view_balance, + const crypto::x25519_secret_key &d_view_received, + const crypto::x25519_secret_key &d_filter_assist, + const crypto::secret_key &s_generate_address, + const jamtis::jamtis_address_tag_cipher_context &cipher_context, SpEnoteRecordV1 &record_out); } //namespace sp diff --git a/src/seraphis_main/scan_balance_recovery_utils.cpp b/src/seraphis_main/scan_balance_recovery_utils.cpp index 460335d008..fb9eb28e54 100644 --- a/src/seraphis_main/scan_balance_recovery_utils.cpp +++ b/src/seraphis_main/scan_balance_recovery_utils.cpp @@ -41,7 +41,8 @@ #include "enote_record_utils_legacy.h" #include "ringct/rctOps.h" #include "ringct/rctTypes.h" -#include "seraphis_core/jamtis_core_utils.h" +#include "seraphis_core/jamtis_account_secrets.h" +#include "seraphis_core/jamtis_enote_utils.h" #include "seraphis_core/legacy_core_utils.h" #include "seraphis_core/legacy_enote_types.h" #include "seraphis_core/legacy_enote_utils.h" @@ -166,7 +167,8 @@ static void update_with_new_record_legacy(const LegacyEnoteRecord &new_enote_rec found_spent_key_images_inout[new_record_key_image]; // b. update its spent context (update instead of assignment in case of duplicates) - try_update_enote_spent_context_v1(contextual_key_images_of_record_spent_in_this_chunk->spent_context, + try_update_enote_spent_context_v1( + contextual_key_images_of_record_spent_in_this_chunk->spent_context, found_spent_key_images_inout[new_record_key_image]); // c. save the record's current spent context @@ -198,163 +200,6 @@ static void update_with_new_intermediate_record_sp(const SpIntermediateEnoteReco } //------------------------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------------------------- -static void update_with_new_record_sp(const SpEnoteRecordV1 &new_enote_record, - const SpEnoteOriginContextV1 &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, - std::unordered_set &txs_have_spent_enotes_inout) -{ - // 1. add new record to found enotes (or refresh if already there) - const crypto::key_image &new_record_key_image{new_enote_record.key_image}; - - found_enote_records_inout[new_record_key_image].record = new_enote_record; - - // 2. if the enote is spent in this chunk, update its spent context - SpEnoteSpentContextV1 spent_context_update{}; - - auto contextual_key_images_of_record_spent_in_this_chunk = - std::find_if( - chunk_contextual_key_images.begin(), - chunk_contextual_key_images.end(), - [&new_record_key_image](const SpContextualKeyImageSetV1 &contextual_key_image_set) -> bool - { - return has_key_image(contextual_key_image_set, new_record_key_image); - } - ); - - if (contextual_key_images_of_record_spent_in_this_chunk != chunk_contextual_key_images.end()) - { - // a. record that the enote is spent in this chunk - found_spent_key_images_inout[new_record_key_image]; - - // b. update its spent context (update instead of assignment in case of duplicates) - try_update_enote_spent_context_v1(contextual_key_images_of_record_spent_in_this_chunk->spent_context, - found_spent_key_images_inout[new_record_key_image]); - - // c. save the record's current spent context - spent_context_update = found_spent_key_images_inout[new_record_key_image]; - - // d. save the tx id of the tx where this enote was spent (the tx is in this chunk) - // note: use the spent context of the contextual key images instead of the spent context update in case the - // update did not resolve to a tx in this chunk (probably a bug, but better safe than sorry here) - txs_have_spent_enotes_inout.insert( - contextual_key_images_of_record_spent_in_this_chunk->spent_context.transaction_id - ); - } - - // 3. update the record's contexts - update_contextual_enote_record_contexts_v1(new_record_origin_context, - spent_context_update, - found_enote_records_inout[new_record_key_image].origin_context, - found_enote_records_inout[new_record_key_image].spent_context); -} -//------------------------------------------------------------------------------------------------------------------- -//------------------------------------------------------------------------------------------------------------------- -static void collect_legacy_key_images_from_tx(const rct::key &requested_tx_id, - const std::list &chunk_contextual_key_images, - std::unordered_map &legacy_key_images_in_tx_inout) -{ - // 1. find key images of the requested tx - auto contextual_key_images_of_requested_tx = - std::find_if( - chunk_contextual_key_images.begin(), - chunk_contextual_key_images.end(), - [&requested_tx_id](const SpContextualKeyImageSetV1 &contextual_key_image_set) -> bool - { - return contextual_key_image_set.spent_context.transaction_id == requested_tx_id; - } - ); - - CHECK_AND_ASSERT_THROW_MES(contextual_key_images_of_requested_tx != chunk_contextual_key_images.end(), - "enote scanning (collect legacy key images from tx): could not find tx's key images."); - - // 2. record legacy key images and their spent contexts - for (const crypto::key_image &legacy_key_image : contextual_key_images_of_requested_tx->legacy_key_images) - { - try_update_enote_spent_context_v1(contextual_key_images_of_requested_tx->spent_context, - legacy_key_images_in_tx_inout[legacy_key_image]); - } -} -//------------------------------------------------------------------------------------------------------------------- -//------------------------------------------------------------------------------------------------------------------- -static std::unordered_set process_chunk_sp_selfsend_pass( - const std::unordered_set &txs_have_spent_enotes, - const rct::key &jamtis_spend_pubkey, - const crypto::secret_key &k_view_balance, - const crypto::x25519_secret_key &xk_find_received, - const crypto::secret_key &s_generate_address, - const jamtis::jamtis_address_tag_cipher_context &cipher_context, - const std::unordered_map> &chunk_basic_records_per_tx, - const std::list &chunk_contextual_key_images, - std::unordered_map &found_enote_records_inout, - std::unordered_map &found_spent_sp_key_images_inout, - std::unordered_map &legacy_key_images_in_sp_selfspends_inout) -{ - // for each tx in this chunk that spends one of our enotes, check if any of the basic records attached to that - // tx contain a self-send enote owned by us - // - if any self-send enotes identified here are also spent in txs in this chunk, return those txs' ids so this - // function can be called in a loop (those txs will contain self-send enotes that need to be scanned and that may - // in turn be spent in this chunk) - std::unordered_set txs_have_spent_enotes_fresh; - SpEnoteRecordV1 new_enote_record; - - for (const rct::key &tx_with_spent_enotes : txs_have_spent_enotes) - { - CHECK_AND_ASSERT_THROW_MES(chunk_basic_records_per_tx.find(tx_with_spent_enotes) != - chunk_basic_records_per_tx.end(), - "enote scan process chunk (self-send passthroughs): tx with spent enotes not found in records map (bug)."); - - for (const ContextualBasicRecordVariant &contextual_basic_record : - chunk_basic_records_per_tx.at(tx_with_spent_enotes)) - { - 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( - 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, - s_generate_address, - cipher_context, - new_enote_record)) - continue; - - // b. we found an owned enote, so handle it - // - 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, - sp_contextual_basic_record->origin_context, - chunk_contextual_key_images, - found_enote_records_inout, - found_spent_sp_key_images_inout, - txs_have_spent_enotes_fresh); - - // c. record all legacy key images attached to this selfsend for the caller to deal with - // - all key images of legacy owned enotes spent in seraphis txs will be attached to seraphis - // 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(sp_contextual_basic_record->origin_context.transaction_id, - chunk_contextual_key_images, - legacy_key_images_in_sp_selfspends_inout); - } catch (...) {} - } - } - - return txs_have_spent_enotes_fresh; -} -//------------------------------------------------------------------------------------------------------------------- -//------------------------------------------------------------------------------------------------------------------- static rct::key& key_by_index_ref(rct::key_keyV_variant &variant, size_t index) { struct visitor final : public tools::variant_static_visitor @@ -514,78 +359,131 @@ bool try_find_legacy_enotes_in_tx(const rct::key &legacy_base_spend_pubkey, return found_an_enote; } //------------------------------------------------------------------------------------------------------------------- -bool try_find_sp_enotes_in_tx(const crypto::x25519_secret_key &xk_find_received, - 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 rct::key &input_context, - const SpTxSupplementV1 &tx_supplement, - const std::vector &enotes_in_tx, - const SpEnoteOriginStatus origin_status, - std::list &basic_records_in_tx_out) +std::uint64_t filter_assist_scan_64(const crypto::x25519_secret_key &d_filter_assist, + const epee::span enote_ephemeral_pubkeys, + const std::uint8_t num_primary_view_tag_bits, + const epee::span enotes) { - basic_records_in_tx_out.clear(); + CHECK_AND_ASSERT_THROW_MES(enotes.size() <= 64, + "filter_assist_scan_64(): cannot scan more than 64 enotes at a time"); // 1. check if any enotes can be scanned - if (tx_supplement.output_enote_ephemeral_pubkeys.size() == 0 || - enotes_in_tx.size() == 0) + if (enote_ephemeral_pubkeys.size() == 0 || enotes.size() == 0 + || num_primary_view_tag_bits > 8 * jamtis::VIEW_TAG_BYTES) return false; - // 2. find-received scan each enote in the tx - std::size_t ephemeral_pubkey_index{0}; + // 2. filter-assist scan each enote in the tx crypto::x25519_pubkey temp_DH_derivation; - SpContextualBasicEnoteRecordV1 temp_contextual_record{}; - bool found_an_enote{false}; + std::uint64_t pvt_matched_mask{0}; - for (std::size_t enote_index{0}; enote_index < enotes_in_tx.size(); ++enote_index) + for (std::size_t enote_index{0}; enote_index < enotes.size(); ++enote_index) { // a. get the next Diffie-Hellman derivation // - there can be fewer ephemeral pubkeys than enotes; when we get to the end, keep using the last one - if (enote_index < tx_supplement.output_enote_ephemeral_pubkeys.size()) + if (enote_index < enote_ephemeral_pubkeys.size()) { - ephemeral_pubkey_index = enote_index; - crypto::x25519_scmul_key(xk_find_received, - tx_supplement.output_enote_ephemeral_pubkeys[ephemeral_pubkey_index], + crypto::x25519_scmul_key(d_filter_assist, + enote_ephemeral_pubkeys[enote_index], temp_DH_derivation); } - // b, find-receive scan the enote (in try block in case enote is malformed) + // b. filter-assist scan the enote (in try block in case enote is malformed) try { - if (!try_get_basic_enote_record_v1(enotes_in_tx[enote_index], - tx_supplement.output_enote_ephemeral_pubkeys[ephemeral_pubkey_index], - input_context, - temp_DH_derivation, - temp_contextual_record.record)) - continue; + if (jamtis::test_jamtis_primary_view_tag(temp_DH_derivation, + onetime_address_ref(enotes[enote_index]), + view_tag_ref(enotes[enote_index]), + num_primary_view_tag_bits)) + pvt_matched_mask |= (1 << enote_index); } catch (...) { continue; } + } - // c. set the origin context - temp_contextual_record.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_supplement.tx_extra - }; + return pvt_matched_mask; +} +//------------------------------------------------------------------------------------------------------------------- +bool try_find_sp_enotes_in_tx(const crypto::x25519_secret_key &d_filter_assist, + 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 rct::key &input_context, + const SpTxSupplementV1 &tx_supplement, + const std::vector &enotes_in_tx, + const SpEnoteOriginStatus origin_status, + std::list &basic_records_in_tx_out) +{ + basic_records_in_tx_out.clear(); - // d. save the contextual basic record - // note: it is possible for enotes with duplicate onetime addresses to be added here; it is assumed the - // upstream caller will be able to handle those without problems - basic_records_in_tx_out.emplace_back(temp_contextual_record); + // 1. filter-assist scan all enotes in tx + bool one_enote_matched{false}; + std::vector enote_passed_exclusive_check; + enote_passed_exclusive_check.reserve(enotes_in_tx.size()); + auto ephemeral_pubkeys_span{epee::to_span(tx_supplement.output_enote_ephemeral_pubkeys)}; - // e. record that an enote was found - found_an_enote = true; + for (size_t enote_index = 0; enote_index < enotes_in_tx.size();) + { + // a. calc chunk size + const size_t scan_chunk_size = std::min(enotes_in_tx.size() - enote_index, 64); + + // b. do filter-assist scannning of chunk + const std::uint64_t pvt_matched_mask = filter_assist_scan_64(d_filter_assist, + ephemeral_pubkeys_span, + tx_supplement.num_primary_view_tag_bits, + {enotes_in_tx.data() + enote_index, scan_chunk_size}); + if (pvt_matched_mask) + { + one_enote_matched = true; + for (size_t i = 0; i < scan_chunk_size; ++i) + enote_passed_exclusive_check.push_back((pvt_matched_mask >> i) & 1); + } + + // c. update iterators + enote_index += scan_chunk_size; + ephemeral_pubkeys_span.remove_prefix(scan_chunk_size); } - return found_an_enote; + // 2. if no primary view tag matched, return false + if (!one_enote_matched) + return false; + + // 3. create basic enote records + const crypto::x25519_pubkey *pephemeral_pubkey{}; + for (size_t enote_index = 0; enote_index < enotes_in_tx.size(); ++enote_index) + { + // a. make enote origin context + const SpEnoteOriginContextV1 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_supplement.tx_extra + }; + + // b. set associated enote ephemeral pubkey + if (enote_index < tx_supplement.output_enote_ephemeral_pubkeys.size()) + pephemeral_pubkey = &tx_supplement.output_enote_ephemeral_pubkeys[enote_index]; + + // c. make the record + const SpBasicEnoteRecordV1 basic_enote_record{ + .enote = enotes_in_tx[enote_index], + .enote_ephemeral_pubkey = *pephemeral_pubkey, + .num_primary_view_tag_bits = tx_supplement.num_primary_view_tag_bits, + .input_context = input_context, + .passed_exclusive_check = enote_passed_exclusive_check[enote_index], + }; + + basic_records_in_tx_out.push_back(SpContextualBasicEnoteRecordV1{ + .record = basic_enote_record, + .origin_context = origin_context + }); + } + + return true; } //------------------------------------------------------------------------------------------------------------------- -bool try_collect_key_images_from_tx(const std::uint64_t block_index, +void collect_key_images_from_tx(const std::uint64_t block_index, const std::uint64_t block_timestamp, const rct::key &transaction_id, std::vector legacy_key_images_in_tx, @@ -593,12 +491,6 @@ bool try_collect_key_images_from_tx(const std::uint64_t block_index, const SpEnoteSpentStatus spent_status, SpContextualKeyImageSetV1 &contextual_key_images_out) { - // 1. don't make the set if there are no key images - if (legacy_key_images_in_tx.size() == 0 && - sp_key_images_in_tx.size() == 0) - return false; - - // 2. make the set contextual_key_images_out = SpContextualKeyImageSetV1{ .legacy_key_images = std::move(legacy_key_images_in_tx), .sp_key_images = std::move(sp_key_images_in_tx), @@ -610,8 +502,6 @@ bool try_collect_key_images_from_tx(const std::uint64_t block_index, .spent_status = spent_status } }; - - return true; } //------------------------------------------------------------------------------------------------------------------- void process_chunk_intermediate_legacy(const rct::key &legacy_base_spend_pubkey, @@ -753,8 +643,8 @@ void process_chunk_full_legacy(const rct::key &legacy_base_spend_pubkey, } //------------------------------------------------------------------------------------------------------------------- void process_chunk_intermediate_sp(const rct::key &jamtis_spend_pubkey, - const crypto::x25519_secret_key &xk_unlock_amounts, - const crypto::x25519_secret_key &xk_find_received, + const crypto::x25519_secret_key &d_view_received, + const crypto::x25519_secret_key &d_filter_assist, const crypto::secret_key &s_generate_address, const jamtis::jamtis_address_tag_cipher_context &cipher_context, const std::unordered_map> &chunk_basic_records_per_tx, @@ -781,8 +671,8 @@ void process_chunk_intermediate_sp(const rct::key &jamtis_spend_pubkey, if (!try_get_intermediate_enote_record_v1( sp_contextual_basic_record->record, jamtis_spend_pubkey, - xk_unlock_amounts, - xk_find_received, + d_view_received, + d_filter_assist, s_generate_address, cipher_context, new_enote_record)) @@ -799,119 +689,117 @@ void process_chunk_intermediate_sp(const rct::key &jamtis_spend_pubkey, //------------------------------------------------------------------------------------------------------------------- void process_chunk_full_sp(const rct::key &jamtis_spend_pubkey, const crypto::secret_key &k_view_balance, - const crypto::x25519_secret_key &xk_unlock_amounts, - const crypto::x25519_secret_key &xk_find_received, + const crypto::x25519_secret_key &d_view_received, + const crypto::x25519_secret_key &d_filter_assist, const crypto::secret_key &s_generate_address, const jamtis::jamtis_address_tag_cipher_context &cipher_context, - const std::function &check_key_image_is_known_func, const std::unordered_map> &chunk_basic_records_per_tx, const std::list &chunk_contextual_key_images, std::unordered_map &found_enote_records_out, - std::unordered_map &found_spent_sp_key_images_out, - std::unordered_map &legacy_key_images_in_sp_selfspends_out) + std::unordered_map &sp_key_images_in_sp_selfsends_out, + std::unordered_map &legacy_key_images_in_sp_selfsends_out) { found_enote_records_out.clear(); - found_spent_sp_key_images_out.clear(); - legacy_key_images_in_sp_selfspends_out.clear(); - - // 1. check if any owned enotes acquired before this chunk were spent in this chunk (key image matches) - std::unordered_set txs_have_spent_enotes; + sp_key_images_in_sp_selfsends_out.clear(); + legacy_key_images_in_sp_selfsends_out.clear(); - auto key_image_handler = - [&](const SpEnoteSpentContextV1 &spent_context, const crypto::key_image &key_image) - { - // a. ask callback if key image is known (i.e. if the key image is attached to an owned enote acquired before - // this chunk) - if (check_key_image_is_known_func(key_image)) - { - // i. record the found spent key image - found_spent_sp_key_images_out[key_image]; - - // ii. update its spent context (use update instead of assignment in case of duplicates) - try_update_enote_spent_context_v1(spent_context, found_spent_sp_key_images_out[key_image]); - - // iii. record tx id of the tx that contains this key image (this tx spent an enote that we acquired - // before this chunk) - txs_have_spent_enotes.insert(spent_context.transaction_id); - } - }; - - for (const SpContextualKeyImageSetV1 &contextual_key_image_set : chunk_contextual_key_images) + // this lambda adds an enote record to found_enote_records_out, updating existing origin + // contexts if applicable + const auto add_contextual_enote_record = [&found_enote_records_out] + (const SpEnoteRecordV1 &new_enote_record, const SpEnoteOriginContextV1 &origin_context) { - // - We don't check if legacy key images are known from before this chunk because during a comprehensive view-only - // scan legacy key images are not computable by the legacy view key, so there may be owned legacy enotes with - // unknown key images. This means there may be txs in this chunk with our selfsends but only legacy key images - // that can't be identified - so we need to do a selfsend check on all of those txs. All legacy key images in - // txs that have both legacy key images and seraphis selfsends will be recorded along with their spent contexts - // for the caller to cache in preparation for when they are able to match key images with legacy enotes. - - // a. invoke the key image handler for seraphis key images in the chunk - for (const crypto::key_image &key_image : contextual_key_image_set.sp_key_images) - key_image_handler(contextual_key_image_set.spent_context, key_image); - - // b. save tx ids of txs that contain at least one legacy key image, so they can be examined by the selfsend pass - if (contextual_key_image_set.legacy_key_images.size() > 0) - txs_have_spent_enotes.insert(contextual_key_image_set.spent_context.transaction_id); - } + const auto record_it = found_enote_records_out.find(new_enote_record.key_image); + if (record_it == found_enote_records_out.end()) + { + // if no other existing enote with key image, simply insert + found_enote_records_out.emplace(new_enote_record.key_image, + SpContextualEnoteRecordV1 { + .record = new_enote_record, + .origin_context = origin_context, + .spent_context = {} // handled later + }); + } + else + { + // if another record with the same key image exists, try updating origin ctx + try_update_enote_origin_context_v1(origin_context, record_it->second.origin_context); + } + }; - // 2. check if this chunk contains owned enotes (non-self-send pass) - SpEnoteRecordV1 new_enote_record; + // 1. build a map of txid -> contextual key image set + std::unordered_map key_image_sets_by_txid; + for (const auto &key_image_set : chunk_contextual_key_images) + key_image_sets_by_txid.emplace(key_image_set.spent_context.transaction_id, &key_image_set); + // 2. go thru all basic and auxiliary records in this chunk and try converting to full records. + // if any record within a certain transaction is a self send record, then add its key images + // to the output. for (const auto &tx_basic_records : chunk_basic_records_per_tx) { - for (const ContextualBasicRecordVariant &contextual_basic_record : tx_basic_records.second) + try { - const auto * const sp_contextual_basic_record{ + bool found_self_send_in_tx = false; + + // a. for all basic enote records... + for (const auto &contextual_basic_record : tx_basic_records.second) + { + const auto * const sp_contextual_basic_record{ contextual_basic_record.try_unwrap() }; - if (sp_contextual_basic_record == nullptr) - continue; + 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( - sp_contextual_basic_record->record, - jamtis_spend_pubkey, - k_view_balance, - xk_unlock_amounts, - xk_find_received, - s_generate_address, - cipher_context, - new_enote_record)) + // i. check if we own the enote by attempting to convert it to a full enote record + SpEnoteRecordV1 new_enote_record; + if (!try_get_enote_record_v1( + sp_contextual_basic_record->record, + jamtis_spend_pubkey, + k_view_balance, + d_view_received, + d_filter_assist, + s_generate_address, + cipher_context, + new_enote_record)) continue; - // b. we found an owned enote, so handle it - // - 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, - sp_contextual_basic_record->origin_context, - chunk_contextual_key_images, - found_enote_records_out, - found_spent_sp_key_images_out, - txs_have_spent_enotes); - } catch (...) {} + // ii. we found an owned enote. update found contexualized enote record set + add_contextual_enote_record(new_enote_record, sp_contextual_basic_record->origin_context); + + // iii. if new enote record is a self-send, flag this transaction as containing a self send + jamtis::JamtisSelfSendType dummy_type; + if (jamtis::try_get_jamtis_self_send_type(new_enote_record.type, dummy_type)) + found_self_send_in_tx = true; + } + + // c. if we found at least one self send enote in this transaction, then add the + // seraphis and legacy key images to the output + if (found_self_send_in_tx) + { + const auto &key_image_set = *key_image_sets_by_txid.at(tx_basic_records.first); + + for (const auto &sp_key_image : key_image_set.sp_key_images) + sp_key_images_in_sp_selfsends_out.emplace(sp_key_image, + key_image_set.spent_context); + + for (const auto &legacy_key_image : key_image_set.legacy_key_images) + legacy_key_images_in_sp_selfsends_out.emplace(legacy_key_image, + key_image_set.spent_context); + } } + catch (...) {} } - // 3. check for owned enotes in this chunk (self-send passes) - // - a selfsend pass identifies owned selfsend enotes in txs that have been flagged, and then flags txs where - // those enotes have been spent in this chunk - // - we loop through selfsend passes until no more txs are flagged - while (txs_have_spent_enotes.size() > 0) + // 1. go thru every key image in transactions with seraphis self sends and update the spent + // context of new enotes scanned in this chunk + for (const auto &sp_key_image_in_sp_selfsend_info : sp_key_images_in_sp_selfsends_out) { - txs_have_spent_enotes = - process_chunk_sp_selfsend_pass(txs_have_spent_enotes, - jamtis_spend_pubkey, - k_view_balance, - xk_find_received, - s_generate_address, - cipher_context, - chunk_basic_records_per_tx, - chunk_contextual_key_images, - found_enote_records_out, - found_spent_sp_key_images_out, - legacy_key_images_in_sp_selfspends_out); + const auto &sp_key_image_in_sp_selfsend = sp_key_image_in_sp_selfsend_info.first; + const auto &sp_selfsend_context = sp_key_image_in_sp_selfsend_info.second; + + const auto record_with_image_it = found_enote_records_out.find(sp_key_image_in_sp_selfsend); + if (record_with_image_it != found_enote_records_out.end()) + try_update_enote_spent_context_v1(sp_selfsend_context, + record_with_image_it->second.spent_context); } } //------------------------------------------------------------------------------------------------------------------- diff --git a/src/seraphis_main/scan_balance_recovery_utils.h b/src/seraphis_main/scan_balance_recovery_utils.h index eb38ec842c..03afdb2ff2 100644 --- a/src/seraphis_main/scan_balance_recovery_utils.h +++ b/src/seraphis_main/scan_balance_recovery_utils.h @@ -88,9 +88,29 @@ bool try_find_legacy_enotes_in_tx(const rct::key &legacy_base_spend_pubkey, const SpEnoteOriginStatus origin_status, hw::device &hwdev, std::list &basic_records_in_tx_out); +/** + * brief: filter_assist_scan_64 - perform filter-assist scanning on a set of enotes in a tx (max 64) + * + * This function is written to use no allocations to be as fast as possible without the use of + * threading. This function could potentially be used directly by light wallet servers to cache the + * results of filter-assist scanning for many clients, with the contextual information being added + * later when clients actually request scanning information. In reality, the number of outputs per + * transaction will be capped to 16 in real-world rulesets, so this function's cap of 64 enotes + * should be more than enough. + * + * param: d_filter_assist - d_fa + * param: enote_ephemeral_pubkeys - D_e[] for + * param: num_primary_view_tag_bits - npbits + * param: enotes - read-only view into array of enotes, with a maximum length of 64 + * return: bit mask for whether the primary view tag matched for each enote (0th index is LSB) +*/ +std::uint64_t filter_assist_scan_64(const crypto::x25519_secret_key &d_filter_assist, + const epee::span enote_ephemeral_pubkeys, + const std::uint8_t num_primary_view_tag_bits, + const epee::span enotes); /** * brief: try_find_sp_enotes_in_tx - obtain contextual basic records from a seraphis tx's contents -* param: xk_find_received - +* param: d_filter_assist - * param: block_index - * param: block_timestamp - * param: transaction_id - @@ -101,7 +121,7 @@ bool try_find_legacy_enotes_in_tx(const rct::key &legacy_base_spend_pubkey, * param: origin_status - * outparam: basic_records_in_tx_out - */ -bool try_find_sp_enotes_in_tx(const crypto::x25519_secret_key &xk_find_received, +bool try_find_sp_enotes_in_tx(const crypto::x25519_secret_key &d_filter_assist, const std::uint64_t block_index, const std::uint64_t block_timestamp, const rct::key &transaction_id, @@ -112,7 +132,7 @@ bool try_find_sp_enotes_in_tx(const crypto::x25519_secret_key &xk_find_received, const SpEnoteOriginStatus origin_status, std::list &basic_records_in_tx_out); /** -* brief: try_collect_key_images_from_tx - if a tx has key images, collect them into a contextual key image set +* brief: collect_key_images_from_tx - collect tx key images into a contextual key image set * param: block_index - * param: block_timestamp - * param: transaction_id - @@ -120,9 +140,8 @@ bool try_find_sp_enotes_in_tx(const crypto::x25519_secret_key &xk_find_received, * param: sp_key_images_in_tx - * param: spent_status - * outparam: contextual_key_images_out - -* return: true if a set was made (there was at least one key image available) */ -bool try_collect_key_images_from_tx(const std::uint64_t block_index, +void collect_key_images_from_tx(const std::uint64_t block_index, const std::uint64_t block_timestamp, const rct::key &transaction_id, std::vector legacy_key_images_in_tx, @@ -175,47 +194,45 @@ void process_chunk_full_legacy(const rct::key &legacy_base_spend_pubkey, /** * brief: process_chunk_intermediate_sp - process a chunk of contextual basic records with seraphis {kx_ua, kx_fr, s_ga} * param: jamtis_spend_pubkey - -* param: xk_unlock_amounts - -* param: xk_find_received - +* param: d_view_received - d_vr +* param: d_filter_assist - d_fa * param: s_generate_address - * param: cipher_context - * param: chunk_basic_records_per_tx - [ tx id : contextual basic record ] * outparam: found_enote_records_out - [ Ko : legacy contextual intermediate enote record ] */ void process_chunk_intermediate_sp(const rct::key &jamtis_spend_pubkey, - const crypto::x25519_secret_key &xk_unlock_amounts, - const crypto::x25519_secret_key &xk_find_received, + const crypto::x25519_secret_key &d_view_received, + const crypto::x25519_secret_key &d_filter_assist, const crypto::secret_key &s_generate_address, const jamtis::jamtis_address_tag_cipher_context &cipher_context, const std::unordered_map> &chunk_basic_records_per_tx, std::unordered_map &found_enote_records_out); /** -* brief: process_chunk_full_sp - process a chunk of contextual basic records with seraphis view-balance privkey +* brief: process_chunk_full_sp - process a chunk of contextual basic/auxiliary records with seraphis view-balance key * param: jamtis_spend_pubkey - * param: k_view_balance - -* param: xk_unlock_amounts - -* param: xk_find_received - +* param: d_view_received - +* param: d_filter_assist - * param: s_generate_address - * param: cipher_context - -* param: check_key_image_is_known_func - callback for checking if a key image is known by the caller * param: chunk_basic_records_per_tx - [ tx id : contextual basic record ] * param: chunk_contextual_key_images - -* outparam: found_enote_records_out - [ seraphis KI : legacy contextual intermediate enote record ] -* outparam: found_spent_sp_key_images_out - [ seraphis KI : spent context ] -* outparam: legacy_key_images_in_sp_selfspends_out - [ legacy KI : spent context ] +* outparam: found_enote_records_out - [ seraphis KI : legacy contextual enote record ] +* outparam: sp_key_images_in_sp_selfsends_out - [ seraphis KI : spent context ] +* outparam: legacy_key_images_in_sp_selfsends_out - [ legacy KI : spent context ] */ void process_chunk_full_sp(const rct::key &jamtis_spend_pubkey, const crypto::secret_key &k_view_balance, - const crypto::x25519_secret_key &xk_unlock_amounts, - const crypto::x25519_secret_key &xk_find_received, + const crypto::x25519_secret_key &d_view_received, + const crypto::x25519_secret_key &d_filter_assist, const crypto::secret_key &s_generate_address, const jamtis::jamtis_address_tag_cipher_context &cipher_context, - const std::function &check_key_image_is_known_func, const std::unordered_map> &chunk_basic_records_per_tx, const std::list &chunk_contextual_key_images, std::unordered_map &found_enote_records_out, - std::unordered_map &found_spent_sp_key_images_out, - std::unordered_map &legacy_key_images_in_sp_selfspends_out); + std::unordered_map &sp_key_images_in_sp_selfsends_out, + std::unordered_map &legacy_key_images_in_sp_selfsends_out); } //namespace scanning } //namespace sp diff --git a/src/seraphis_main/scan_chunk_consumer.h b/src/seraphis_main/scan_chunk_consumer.h index f30eec8747..e1cc928ee0 100644 --- a/src/seraphis_main/scan_chunk_consumer.h +++ b/src/seraphis_main/scan_chunk_consumer.h @@ -57,7 +57,7 @@ namespace scanning //// // ChunkConsumer -// - provides an API for consuming chunks of enotes from find-received scanning +// - provides an API for consuming chunks of enotes from filter-assist scanning /// class ChunkConsumer { diff --git a/src/seraphis_main/scan_context.h b/src/seraphis_main/scan_context.h index 32dfa5dbe5..df06468962 100644 --- a/src/seraphis_main/scan_context.h +++ b/src/seraphis_main/scan_context.h @@ -26,7 +26,7 @@ // 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. -// Dependency injectors for managing the find-received step of enote scanning. Intended to be stateful, managing +// Dependency injectors for managing the filter-assist step of enote scanning. Intended to be stateful, managing // a connection to a context that contains enotes and key images, and linking together successive 'get chunk' calls. #pragma once diff --git a/src/seraphis_main/sp_knowledge_proof_types.h b/src/seraphis_main/sp_knowledge_proof_types.h index 3a24188a57..5f767d472e 100644 --- a/src/seraphis_main/sp_knowledge_proof_types.h +++ b/src/seraphis_main/sp_knowledge_proof_types.h @@ -57,7 +57,7 @@ namespace knowledge_proofs //// // AddressOwnershipProofV1 // - proof that an address K is constructed in the seraphis address style and is owned by the prover -// - {K = K_1} OR {K = K_s = k_vb X + k_m U} +// - {K = K^j_s} OR {K = K_s = k_vb X + k_m U} // // - INTERACTIVE PROOF: verifier must give a custom message to the prover, otherwise the prover can just copy-paste // a pre-computed proof that he got from who-knows-where @@ -74,30 +74,30 @@ struct AddressOwnershipProofV1 //// // AddressIndexProofV1 -// - proof that a jamtis address with spendkey K_1 was constructed from an index j from base spend key K_s +// - proof that a jamtis address with spendkey K^j_s was constructed from an index j from base spend key K_s // -// - VERIFIER: recompute K_1 ?= [G/X/U spendkey extensions from {j, generator, K_s}] + K_s +// - VERIFIER: recompute K^j_s ?= [G/X/U spendkey extensions from {j, generator, K_s}] + K_s /// struct AddressIndexProofV1 { rct::key K_s; jamtis::address_index_t j; rct::key generator; - rct::key K_1; + rct::key addr_Ks; }; //// // EnoteOwnershipProofV1 -// - proof an enote with onetime adress Ko is owned by an address K_1 -// - disclaimer: this does not prove that the owner of address K_1 can actually spend the enote; q could be computed in -// violation of the jamtis spec, in which case the owner of K_1 may never recover the enote and so the funds are +// - proof an enote with onetime adress Ko is owned by an address K^j_s +// - disclaimer: this does not prove that the owner of address K^j_s can actually spend the enote; q could be computed in +// violation of the jamtis spec, in which case the owner of K^j_s may never recover the enote and so the funds are // effectively burned // -// - VERIFIER: recompute Ko ?= [G/X/U sender extensions from {K_1, q, C}] + K_1 +// - VERIFIER: recompute Ko ?= [G/X/U sender extensions from {K^j_s, q, C}] + K^j_s /// struct EnoteOwnershipProofV1 { - rct::key K_1; + rct::key addr_Ks; rct::key q; rct::key C; rct::key Ko; @@ -183,7 +183,7 @@ struct TxFundedProofV1 //// // EnoteSentProofV1 -// - proof that an enote with amount a and onetime address Ko was sent to an address K_1 +// - proof that an enote with amount a and onetime address Ko was sent to an address K^j_s // // - VERIFIER: validate the EnoteOwnershipProofV1 and EnoteAmountProofV1 /// @@ -195,7 +195,7 @@ struct EnoteSentProofV1 //// // ReservedEnoteProofV1 -// - proof that an enote with onetime address Ko is owned by address K_1, has amount a, has key image KI, is onchain, and +// - proof that an enote with onetime address Ko is owned by address K^j_s, has amount a, has key image KI, is onchain, and // is unspent // // - VERIFIER: @@ -219,7 +219,7 @@ struct ReservedEnoteProofV1 // // - VERIFIER: // - validate the AddressOwnershipProofV1 proofs -// - check that the owning address K_1 in each of the reserved enote proofs corresponds to an address owned by the prover +// - check that the owning address K^j_s in each of the reserved enote proofs corresponds to an address owned by the prover // - check that the enotes referenced by the reserved enote proofs exist in the ledger // - check that the key images in the reserved enote proofs do not exist in the ledger // - validate the ReservedEnoteProofV1 proofs diff --git a/src/seraphis_main/sp_knowledge_proof_utils.cpp b/src/seraphis_main/sp_knowledge_proof_utils.cpp index 39aad9370c..b11a33045a 100644 --- a/src/seraphis_main/sp_knowledge_proof_utils.cpp +++ b/src/seraphis_main/sp_knowledge_proof_utils.cpp @@ -35,8 +35,8 @@ #include "crypto/generators.h" #include "ringct/rctOps.h" #include "ringct/rctTypes.h" +#include "seraphis_core/jamtis_account_secrets.h" #include "seraphis_core/jamtis_address_utils.h" -#include "seraphis_core/jamtis_core_utils.h" #include "seraphis_core/jamtis_enote_utils.h" #include "seraphis_core/jamtis_support_types.h" #include "seraphis_core/sp_core_enote_utils.h" @@ -174,11 +174,13 @@ void make_address_ownership_proof_v1(const rct::key &message, const jamtis::address_index_t &j, AddressOwnershipProofV1 &proof_out) { - // for address ownership of K_1 + // for address ownership of K^j_s // 1. prepare privkey + crypto::x25519_secret_key d_view_received; crypto::secret_key s_generate_address; - jamtis::make_jamtis_generateaddress_secret(k_view_balance, s_generate_address); + jamtis::make_jamtis_viewreceived_key(k_view_balance, d_view_received); + jamtis::make_jamtis_generateaddress_secret(d_view_received, s_generate_address); // 2. prepare K_s = k_vb X + k_m U rct::key jamtis_spend_pubkey; @@ -200,7 +202,7 @@ void make_address_ownership_proof_v1(const rct::key &message, sc_add(to_bytes(z), to_bytes(sp_spend_privkey), to_bytes(z)); //+ k_m // 4. compute address - // K_1 = x G + y X + z U + // K^j_s = x G + y X + z U rct::key jamtis_address_spend_key; make_seraphis_spendkey(y, z, jamtis_address_spend_key); //y X + z U mask_key(x, jamtis_address_spend_key, jamtis_address_spend_key); //+ x G @@ -245,23 +247,23 @@ void make_address_index_proof_v1(const rct::key &jamtis_spend_pubkey, crypto::secret_key generator; jamtis::make_jamtis_index_extension_generator(s_generate_address, j, generator); - // 2. compute K_1 - rct::key K_1; - jamtis::make_jamtis_address_spend_key(jamtis_spend_pubkey, s_generate_address, j, K_1); + // 2. compute K^j_s + rct::key addr_Ks; + jamtis::make_jamtis_address_spend_key(jamtis_spend_pubkey, s_generate_address, j, addr_Ks); // 3. assemble the full proof proof_out = AddressIndexProofV1{ .K_s = jamtis_spend_pubkey, .j = j, .generator = rct::sk2rct(generator), - .K_1 = K_1 + .addr_Ks = addr_Ks }; } //------------------------------------------------------------------------------------------------------------------- bool verify_address_index_proof_v1(const AddressIndexProofV1 &proof, const rct::key &expected_address) { // 1. check the proof matches the expected address - if (!(proof.K_1 == expected_address)) + if (!(proof.addr_Ks == expected_address)) return false; // 2. reproduce the address index extensions @@ -290,14 +292,14 @@ bool verify_address_index_proof_v1(const AddressIndexProofV1 &proof, const rct:: address_extension_key_g); // 3. compute the nominal address spendkey - // K_1 = k^j_g G + k^j_x X + k^j_u U + K_s + // K^j_s = k^j_g G + k^j_x X + k^j_u U + K_s rct::key nominal_address{proof.K_s}; //K_s extend_seraphis_spendkey_u(address_extension_key_u, nominal_address); //k^j_u U + K_s extend_seraphis_spendkey_x(address_extension_key_x, nominal_address); //k^j_x X + k^j_u U + K_s mask_key(address_extension_key_g, nominal_address, nominal_address); //k^j_g G + k^j_x X + k^j_u U + K_s // 4. check that the proof address spendkey was recreated - if (!(nominal_address == proof.K_1)) + if (!(nominal_address == proof.addr_Ks)) return false; return true; @@ -310,10 +312,10 @@ void make_enote_ownership_proof_v1(const rct::key &jamtis_address_spend_key, EnoteOwnershipProofV1 &proof_out) { proof_out = EnoteOwnershipProofV1{ - .K_1 = jamtis_address_spend_key, - .q = sender_receiver_secret, - .C = amount_commitment, - .Ko = onetime_address + .addr_Ks = jamtis_address_spend_key, + .q = sender_receiver_secret, + .C = amount_commitment, + .Ko = onetime_address }; } //------------------------------------------------------------------------------------------------------------------- @@ -327,19 +329,19 @@ void make_enote_ownership_proof_v1_sender_plain(const crypto::x25519_secret_key // 1. compute the enote ephemeral pubkey crypto::x25519_pubkey enote_ephemeral_pubkey; jamtis::make_jamtis_enote_ephemeral_pubkey(enote_ephemeral_privkey, - recipient_destination.addr_K3, + recipient_destination.addr_Dbase, enote_ephemeral_pubkey); // 2. prepare the sender-receiver secret rct::key sender_receiver_secret; jamtis::make_jamtis_sender_receiver_secret_plain(enote_ephemeral_privkey, - recipient_destination.addr_K2, + recipient_destination.addr_Dvr, enote_ephemeral_pubkey, input_context, sender_receiver_secret); // 3. complete the proof - make_enote_ownership_proof_v1(recipient_destination.addr_K1, + make_enote_ownership_proof_v1(recipient_destination.addr_Ks, sender_receiver_secret, amount_commitment, onetime_address, @@ -365,7 +367,7 @@ void make_enote_ownership_proof_v1_sender_selfsend(const crypto::x25519_pubkey & jamtis::make_jamtis_sender_receiver_secret_selfsend(k_view_balance, enote_ephemeral_pubkey, input_context, - self_send_type, + jamtis::is_jamtis_auxiliary_selfsend_type(self_send_type), sender_receiver_secret); // 2. complete the proof @@ -387,12 +389,12 @@ void make_enote_ownership_proof_v1_receiver(const SpEnoteRecordV1 &enote_record, EnoteOwnershipProofV1 &proof_out) { // 1. helper privkeys - crypto::x25519_secret_key xk_find_received; + crypto::x25519_secret_key d_view_received; crypto::secret_key s_generate_address; - jamtis::make_jamtis_findreceived_key(k_view_balance, xk_find_received); - jamtis::make_jamtis_generateaddress_secret(k_view_balance, s_generate_address); + jamtis::make_jamtis_viewreceived_key(k_view_balance, d_view_received); + jamtis::make_jamtis_generateaddress_secret(d_view_received, s_generate_address); - // 2. get the owning address's spendkey K_1 + // 2. get the owning address's spendkey K^j_s rct::key jamtis_address_spend_key; jamtis::make_jamtis_address_spend_key(jamtis_spend_pubkey, s_generate_address, @@ -408,12 +410,12 @@ void make_enote_ownership_proof_v1_receiver(const SpEnoteRecordV1 &enote_record, jamtis::make_jamtis_sender_receiver_secret_selfsend(k_view_balance, enote_record.enote_ephemeral_pubkey, enote_record.input_context, - self_send_type, + jamtis::is_jamtis_auxiliary_selfsend_type(self_send_type), sender_receiver_secret); } else { - jamtis::make_jamtis_sender_receiver_secret_plain(xk_find_received, + jamtis::make_jamtis_sender_receiver_secret_plain(d_view_received, enote_record.enote_ephemeral_pubkey, enote_record.enote_ephemeral_pubkey, enote_record.input_context, @@ -446,7 +448,7 @@ bool verify_enote_ownership_proof_v1(const EnoteOwnershipProofV1 &proof, // 2. reproduce the onetime address rct::key reproduced_Ko; - jamtis::make_jamtis_onetime_address(proof.K_1, proof.q, proof.C, reproduced_Ko); + jamtis::make_jamtis_onetime_address(proof.addr_Ks, proof.q, proof.C, reproduced_Ko); // 3. check the reproduced onetime address matches the proof if (!(proof.Ko == reproduced_Ko)) @@ -943,9 +945,9 @@ bool verify_reserve_proof_v1(const ReserveProofV1 &proof, // 2. check all the reserved enote proofs for (const ReservedEnoteProofV1 &reserved_enote_proof : proof.reserved_enote_proofs) { - // a. check that the owning address K_1 in each of the reserved enote proofs corresponds to an address owned by + // a. check that the owning address K^j_s in each of the reserved enote proofs corresponds to an address owned by // the prover - if (found_addresses.find(reserved_enote_proof.enote_ownership_proof.K_1) == found_addresses.end()) + if (found_addresses.find(reserved_enote_proof.enote_ownership_proof.addr_Ks) == found_addresses.end()) return false; // b. check that the enotes referenced by the reserved enote proofs are in the ledger and unspent diff --git a/src/seraphis_main/sp_knowledge_proof_utils.h b/src/seraphis_main/sp_knowledge_proof_utils.h index 30d4323358..b0e18c82bf 100644 --- a/src/seraphis_main/sp_knowledge_proof_utils.h +++ b/src/seraphis_main/sp_knowledge_proof_utils.h @@ -57,7 +57,7 @@ namespace knowledge_proofs /** * brief: make an address ownership proof * param: message - message provided by verifier -* param: address - address with the format xG + yX + zU (e.g. K_1 or K_s) +* param: address - address with the format xG + yX + zU (e.g. K^j_s or K_s) * param: x - secret key corresponding to base G * param: y - secret key corresponding to base X * param: z - secret key corresponding to base U @@ -73,7 +73,7 @@ void make_address_ownership_proof_v1(const rct::key &message, //for K_s const crypto::secret_key &sp_spend_privkey, const crypto::secret_key &k_view_balance, AddressOwnershipProofV1 &proof_out); -void make_address_ownership_proof_v1(const rct::key &message, //for K_1 +void make_address_ownership_proof_v1(const rct::key &message, //for K^j_s const crypto::secret_key &sp_spend_privkey, const crypto::secret_key &k_view_balance, const jamtis::address_index_t &j, @@ -108,7 +108,7 @@ void make_address_index_proof_v1(const rct::key &jamtis_spend_pubkey, bool verify_address_index_proof_v1(const AddressIndexProofV1 &proof, const rct::key &expected_address); /** * brief: make an enote ownership proof -* param: jamtis_address_spend_key - K_1 +* param: jamtis_address_spend_key - K^j_s * param: sender_receiver_secret - q * param: amount_commitment - C * param: onetime_address - Ko diff --git a/src/seraphis_main/tx_builder_types.cpp b/src/seraphis_main/tx_builder_types.cpp index 2e34e07305..e021b7ea13 100644 --- a/src/seraphis_main/tx_builder_types.cpp +++ b/src/seraphis_main/tx_builder_types.cpp @@ -164,7 +164,9 @@ void get_output_proposals_v1(const SpTxProposalV1 &tx_proposal, tx_proposal.selfsend_payment_proposals.size()); for (const jamtis::JamtisPaymentProposalV1 &normal_payment_proposal : tx_proposal.normal_payment_proposals) - make_v1_output_proposal_v1(normal_payment_proposal, input_context, tools::add_element(output_proposals_out)); + make_v1_output_proposal_v1(normal_payment_proposal, + input_context, + tools::add_element(output_proposals_out)); for (const jamtis::JamtisPaymentProposalSelfSendV1 &selfsend_payment_proposal : tx_proposal.selfsend_payment_proposals) @@ -203,6 +205,51 @@ void get_tx_proposal_prefix_v1(const SpTxProposalV1 &tx_proposal, tx_proposal_prefix_out); } //------------------------------------------------------------------------------------------------------------------- +std::uint8_t get_shared_num_primary_view_tag_bits( + const std::vector &normal_payment_proposals, + const std::vector &selfsend_payment_proposals, + const std::vector &coinbase_output_proposals, + const std::vector &output_proposals) +{ + std::unordered_set npbits_values; + + // search thru normal payment proposals + for (const jamtis::JamtisPaymentProposalV1 &normal_payment_proposal : normal_payment_proposals) + npbits_values.insert(normal_payment_proposal.num_primary_view_tag_bits); + + // search thru selfsend payment proposals + for (const jamtis::JamtisPaymentProposalSelfSendV1 &selfsend_payment_proposal : selfsend_payment_proposals) + npbits_values.insert(selfsend_payment_proposal.num_primary_view_tag_bits); + + // search thru coinbase output proposals + for (const SpCoinbaseOutputProposalV1 &coinbase_output_proposal : coinbase_output_proposals) + npbits_values.insert(coinbase_output_proposal.num_primary_view_tag_bits); + + // search thru non-coinbase output proposals + for (const SpOutputProposalV1 &output_proposal : output_proposals) + npbits_values.insert(output_proposal.num_primary_view_tag_bits); + + // assert that at least 1 value exists + CHECK_AND_ASSERT_THROW_MES(npbits_values.size(), + "get shared num primary view tag bits: there are 0 npbits values, so getting the value is undefined"); + + // assert that there is only 1 value + CHECK_AND_ASSERT_THROW_MES(npbits_values.size() == 1, + "get shared num primary view tag bits: there are multiple unique values of npbits among these proposals, " + "so getting the value is undefined"); + + // get the npbits value + const std::uint8_t num_primary_view_tag_bits{*npbits_values.begin()}; + + // assert that the value of npbits is in acceptable range + static constexpr size_t MAX_NPBITS_VALUE = 8 * jamtis::VIEW_TAG_BYTES; + CHECK_AND_ASSERT_THROW_MES(num_primary_view_tag_bits <= MAX_NPBITS_VALUE, + "get shared num primary view tag bits: the value of npbits is too large: " + << num_primary_view_tag_bits << " vs " << MAX_NPBITS_VALUE); + + return num_primary_view_tag_bits; +} +//------------------------------------------------------------------------------------------------------------------- SpInputProposalV1 gen_sp_input_proposal_v1(const crypto::secret_key &sp_spend_privkey, const crypto::secret_key &k_view_balance, const rct::xmr_amount amount) @@ -213,6 +260,7 @@ SpInputProposalV1 gen_sp_input_proposal_v1(const crypto::secret_key &sp_spend_pr } //------------------------------------------------------------------------------------------------------------------- SpCoinbaseOutputProposalV1 gen_sp_coinbase_output_proposal_v1(const rct::xmr_amount amount, + const std::uint8_t num_primary_view_tag_bits, const std::size_t num_random_memo_elements) { SpCoinbaseOutputProposalV1 temp; @@ -224,6 +272,9 @@ SpCoinbaseOutputProposalV1 gen_sp_coinbase_output_proposal_v1(const rct::xmr_amo // enote ephemeral pubkey temp.enote_ephemeral_pubkey = crypto::x25519_pubkey_gen(); + // npbits + temp.num_primary_view_tag_bits = num_primary_view_tag_bits; + // partial memo std::vector memo_elements; memo_elements.resize(num_random_memo_elements); @@ -234,7 +285,9 @@ SpCoinbaseOutputProposalV1 gen_sp_coinbase_output_proposal_v1(const rct::xmr_amo return temp; } //------------------------------------------------------------------------------------------------------------------- -SpOutputProposalV1 gen_sp_output_proposal_v1(const rct::xmr_amount amount, const std::size_t num_random_memo_elements) +SpOutputProposalV1 gen_sp_output_proposal_v1(const rct::xmr_amount amount, + const std::uint8_t num_primary_view_tag_bits, + const std::size_t num_random_memo_elements) { SpOutputProposalV1 temp; @@ -244,7 +297,10 @@ SpOutputProposalV1 gen_sp_output_proposal_v1(const rct::xmr_amount amount, const temp.enote_ephemeral_pubkey = crypto::x25519_pubkey_gen(); crypto::rand(sizeof(temp.encoded_amount), temp.encoded_amount.bytes); crypto::rand(sizeof(temp.addr_tag_enc), temp.addr_tag_enc.bytes); - temp.view_tag = crypto::rand_idx(0); + temp.view_tag = jamtis::gen_view_tag(); + + // npbits + temp.num_primary_view_tag_bits = num_primary_view_tag_bits; std::vector memo_elements; memo_elements.resize(num_random_memo_elements); diff --git a/src/seraphis_main/tx_builder_types.h b/src/seraphis_main/tx_builder_types.h index 2fbfe69b7e..1a416c4182 100644 --- a/src/seraphis_main/tx_builder_types.h +++ b/src/seraphis_main/tx_builder_types.h @@ -82,8 +82,10 @@ struct SpCoinbaseOutputProposalV1 final /// proposed enote SpCoinbaseEnoteV1 enote; - /// xK_e: enote ephemeral pubkey + /// D_e: enote ephemeral pubkey crypto::x25519_pubkey enote_ephemeral_pubkey; + /// npbits + std::uint8_t num_primary_view_tag_bits; /// memo elements to add to the tx memo TxExtra partial_memo; }; @@ -99,8 +101,10 @@ struct SpOutputProposalV1 final /// core of the proposal SpOutputProposalCore core; - /// xK_e: enote ephemeral pubkey + /// D_e: enote ephemeral pubkey crypto::x25519_pubkey enote_ephemeral_pubkey; + /// npbits + std::uint8_t num_primary_view_tag_bits; /// enc_a jamtis::encoded_amount_t encoded_amount; /// addr_tag_enc @@ -300,6 +304,17 @@ void get_tx_proposal_prefix_v1(const SpTxProposalV1 &tx_proposal, const crypto::secret_key &k_view_balance, rct::key &tx_proposal_prefix_out); /** +* brief: get_shared_num_primary_view_tag_bits - get single shared value of npbits among payment/output proposals +* param: ... +* return: shared single value of npbits amongst all proposals +* throw: std::runtime_error if the number of unique values of npbits is not equal to 1, or if npbits is too big +*/ +std::uint8_t get_shared_num_primary_view_tag_bits( + const std::vector &normal_payment_proposals, + const std::vector &selfsend_payment_proposals, + const std::vector &coinbase_output_proposals, + const std::vector &output_proposals); +/** * brief: gen_sp_input_proposal_v1 - generate an input proposal * param: sp_spend_privkey - * param: k_view_balance - @@ -312,17 +327,22 @@ SpInputProposalV1 gen_sp_input_proposal_v1(const crypto::secret_key &sp_spend_pr /** * brief: gen_sp_coinbase_output_proposal_v1 - generate a coinbase output proposal * param: amount - +* param: num_primary_view_tag_bits - * param: num_random_memo_elements - * return: random coinbase output proposal */ SpCoinbaseOutputProposalV1 gen_sp_coinbase_output_proposal_v1(const rct::xmr_amount amount, + const std::uint8_t num_primary_view_tag_bits, const std::size_t num_random_memo_elements); /** * brief: gen_sp_output_proposal_v1 - generate an output proposal * param: amount - +* param: num_primary_view_tag_bits - * param: num_random_memo_elements - * return: random output proposal */ -SpOutputProposalV1 gen_sp_output_proposal_v1(const rct::xmr_amount amount, const std::size_t num_random_memo_elements); +SpOutputProposalV1 gen_sp_output_proposal_v1(const rct::xmr_amount amount, + const std::uint8_t num_primary_view_tag_bits, + const std::size_t num_random_memo_elements); } //namespace sp diff --git a/src/seraphis_main/tx_builder_types_multisig.cpp b/src/seraphis_main/tx_builder_types_multisig.cpp index fb9ff4c969..5e588776b5 100644 --- a/src/seraphis_main/tx_builder_types_multisig.cpp +++ b/src/seraphis_main/tx_builder_types_multisig.cpp @@ -102,6 +102,7 @@ void get_sp_input_proposal_v1(const SpMultisigInputProposalV1 &multisig_input_pr { CHECK_AND_ASSERT_THROW_MES(try_make_v1_input_proposal_v1(multisig_input_proposal.enote, multisig_input_proposal.enote_ephemeral_pubkey, + multisig_input_proposal.num_primary_view_tag_bits, multisig_input_proposal.input_context, jamtis_spend_pubkey, k_view_balance, diff --git a/src/seraphis_main/tx_builder_types_multisig.h b/src/seraphis_main/tx_builder_types_multisig.h index ccb7b1b1b3..27457437ac 100644 --- a/src/seraphis_main/tx_builder_types_multisig.h +++ b/src/seraphis_main/tx_builder_types_multisig.h @@ -113,6 +113,8 @@ struct SpMultisigInputProposalV1 final SpEnoteVariant enote; /// the enote's ephemeral pubkey crypto::x25519_pubkey enote_ephemeral_pubkey; + /// npbits + std::uint8_t num_primary_view_tag_bits; /// the enote's input context rct::key input_context; diff --git a/src/seraphis_main/tx_builders_inputs.cpp b/src/seraphis_main/tx_builders_inputs.cpp index 721890e227..ea7f0bda8b 100644 --- a/src/seraphis_main/tx_builders_inputs.cpp +++ b/src/seraphis_main/tx_builders_inputs.cpp @@ -188,6 +188,7 @@ void make_v1_input_proposal_v1(const SpEnoteRecordV1 &enote_record, //------------------------------------------------------------------------------------------------------------------- bool try_make_v1_input_proposal_v1(const SpEnoteVariant &enote, const crypto::x25519_pubkey &enote_ephemeral_pubkey, + const std::uint8_t num_primary_view_tag_bits, const rct::key &input_context, const rct::key &jamtis_spend_pubkey, const crypto::secret_key &k_view_balance, @@ -199,6 +200,7 @@ bool try_make_v1_input_proposal_v1(const SpEnoteVariant &enote, SpEnoteRecordV1 enote_record; if (!try_get_enote_record_v1(enote, enote_ephemeral_pubkey, + num_primary_view_tag_bits, input_context, jamtis_spend_pubkey, k_view_balance, diff --git a/src/seraphis_main/tx_builders_inputs.h b/src/seraphis_main/tx_builders_inputs.h index a0370013d2..888a2aaa4b 100644 --- a/src/seraphis_main/tx_builders_inputs.h +++ b/src/seraphis_main/tx_builders_inputs.h @@ -113,6 +113,7 @@ void make_v1_input_proposal_v1(const SpEnoteRecordV1 &enote_record, * brief: try_make_v1_input_proposal_v1 - try to make a seraphis v1 input proposal from an enote * param: enote - * param: enote_ephemeral_pubkey - +* param: num_primary_view_tag_bits - * param: input_context - * param: jamtis_spend_pubkey - * param: k_view_balance - @@ -122,6 +123,7 @@ void make_v1_input_proposal_v1(const SpEnoteRecordV1 &enote_record, */ bool try_make_v1_input_proposal_v1(const SpEnoteVariant &enote, const crypto::x25519_pubkey &enote_ephemeral_pubkey, + const std::uint8_t num_primary_view_tag_bits, const rct::key &input_context, const rct::key &jamtis_spend_pubkey, const crypto::secret_key &k_view_balance, diff --git a/src/seraphis_main/tx_builders_mixed.cpp b/src/seraphis_main/tx_builders_mixed.cpp index 7b0139fb4c..1e20c501d0 100644 --- a/src/seraphis_main/tx_builders_mixed.cpp +++ b/src/seraphis_main/tx_builders_mixed.cpp @@ -39,7 +39,7 @@ #include "ringct/rctOps.h" #include "ringct/rctTypes.h" #include "seraphis_core/binned_reference_set_utils.h" -#include "seraphis_core/jamtis_core_utils.h" +#include "seraphis_core/jamtis_account_secrets.h" #include "seraphis_core/jamtis_support_types.h" #include "seraphis_core/sp_core_enote_utils.h" #include "seraphis_core/sp_ref_set_index_mapper_flat.h" @@ -441,6 +441,9 @@ static void check_tx_proposal_semantics_selfsend_outputs_v1(const std::size_t nu jamtis_spend_pubkey, k_view_balance); } + + // 4. assert that there is exactly 1 value & unique npbits value amongst the proposals + get_shared_num_primary_view_tag_bits({}, selfsend_payment_proposals, {}, {}); } //------------------------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------------------------- @@ -1103,6 +1106,9 @@ void make_v1_partial_tx_v1(std::vector legacy_inputs, output_amount_commitment_blinding_factors, tx_supplement.output_enote_ephemeral_pubkeys); + tx_supplement.num_primary_view_tag_bits = get_shared_num_primary_view_tag_bits({}, {}, {}, + output_proposals); + // 5. collect full memo finalize_tx_extra_v1(partial_memo, output_proposals, tx_supplement.tx_extra); diff --git a/src/seraphis_main/tx_builders_multisig.cpp b/src/seraphis_main/tx_builders_multisig.cpp index 3c464350bc..76f38369b4 100644 --- a/src/seraphis_main/tx_builders_multisig.cpp +++ b/src/seraphis_main/tx_builders_multisig.cpp @@ -52,8 +52,8 @@ #include "ringct/rctOps.h" #include "ringct/rctTypes.h" #include "seraphis_core/discretized_fee.h" +#include "seraphis_core/jamtis_account_secrets.h" #include "seraphis_core/jamtis_address_utils.h" -#include "seraphis_core/jamtis_core_utils.h" #include "seraphis_core/jamtis_enote_utils.h" #include "seraphis_core/legacy_core_utils.h" #include "seraphis_core/sp_core_enote_utils.h" @@ -825,17 +825,19 @@ void check_v1_sp_multisig_input_proposal_semantics_v1(const SpMultisigInputPropo //------------------------------------------------------------------------------------------------------------------- void make_v1_sp_multisig_input_proposal_v1(const SpEnoteVariant &enote, const crypto::x25519_pubkey &enote_ephemeral_pubkey, + const std::uint8_t num_primary_view_tag_bits, const rct::key &input_context, const crypto::secret_key &address_mask, const crypto::secret_key &commitment_mask, SpMultisigInputProposalV1 &proposal_out) { // add components - proposal_out.enote = enote; - proposal_out.enote_ephemeral_pubkey = enote_ephemeral_pubkey; - proposal_out.input_context = input_context; - proposal_out.address_mask = address_mask; - proposal_out.commitment_mask = commitment_mask; + proposal_out.enote = enote; + proposal_out.enote_ephemeral_pubkey = enote_ephemeral_pubkey; + proposal_out.num_primary_view_tag_bits = num_primary_view_tag_bits; + proposal_out.input_context = input_context; + proposal_out.address_mask = address_mask; + proposal_out.commitment_mask = commitment_mask; } //------------------------------------------------------------------------------------------------------------------- void make_v1_sp_multisig_input_proposal_v1(const SpEnoteRecordV1 &enote_record, @@ -845,6 +847,7 @@ void make_v1_sp_multisig_input_proposal_v1(const SpEnoteRecordV1 &enote_record, { make_v1_sp_multisig_input_proposal_v1(enote_record.enote, enote_record.enote_ephemeral_pubkey, + enote_record.num_primary_view_tag_bits, enote_record.input_context, address_mask, commitment_mask, diff --git a/src/seraphis_main/tx_builders_multisig.h b/src/seraphis_main/tx_builders_multisig.h index 41487daafc..96cc828b06 100644 --- a/src/seraphis_main/tx_builders_multisig.h +++ b/src/seraphis_main/tx_builders_multisig.h @@ -117,6 +117,7 @@ void check_v1_sp_multisig_input_proposal_semantics_v1(const SpMultisigInputPropo * brief: make_v1_sp_multisig_input_proposal_v1 - make a seraphis multisig input proposal (can be sent to other people) * param: enote - * param: enote_ephemeral_pubkey - +* param: num_primary_view_tag_bits - * param: input_context - * param: address_mask - * param: commitment_mask - @@ -124,6 +125,7 @@ void check_v1_sp_multisig_input_proposal_semantics_v1(const SpMultisigInputPropo */ void make_v1_sp_multisig_input_proposal_v1(const SpEnoteV1 &enote, const crypto::x25519_pubkey &enote_ephemeral_pubkey, + const std::uint8_t num_primary_view_tag_bits, const rct::key &input_context, const crypto::secret_key &address_mask, const crypto::secret_key &commitment_mask, @@ -193,6 +195,7 @@ bool try_simulate_tx_from_multisig_tx_proposal_v1(const SpMultisigTxProposalV1 & * param: selfsend_payment_proposals - * param: discretized_transaction_fee - * param: additional_memo_elements - +* param: num_primary_view_tag_bits - * param: tx_version - * param: legacy_spend_pubkey - * param: legacy_subaddress_map - diff --git a/src/seraphis_main/tx_builders_outputs.cpp b/src/seraphis_main/tx_builders_outputs.cpp index de9fdb4114..195bfb0df1 100644 --- a/src/seraphis_main/tx_builders_outputs.cpp +++ b/src/seraphis_main/tx_builders_outputs.cpp @@ -39,7 +39,7 @@ #include "misc_log_ex.h" #include "ringct/rctOps.h" #include "ringct/rctTypes.h" -#include "seraphis_core/jamtis_core_utils.h" +#include "seraphis_core/jamtis_account_secrets.h" #include "seraphis_core/jamtis_destination.h" #include "seraphis_core/jamtis_payment_proposal.h" #include "seraphis_core/jamtis_support_types.h" @@ -110,46 +110,25 @@ static bool ephemeral_pubkeys_are_unique(const std::vector &output_proposals, @@ -405,275 +417,138 @@ boost::optional try_get_additional_output_type_for const rct::xmr_amount change_amount) { // 1. txs should have at least 1 non-change output - CHECK_AND_ASSERT_THROW_MES(num_outputs > 0, "Additional output type v1: 0 outputs specified. If you want to send " - "money to yourself, use a self-spend enote type instead of forcing it via a change enote type."); + CHECK_AND_ASSERT_THROW_MES(num_outputs > 0, "Additional output type v1: 0 outputs specified. " + "If you want to send money to yourself, use a self-spend enote type instead of forcing it " + "via a change enote type."); // 2. sanity check CHECK_AND_ASSERT_THROW_MES(self_send_output_types.size() <= num_outputs, "Additional output type v1: there are more self-send outputs than outputs."); - // 3. if an extra output is needed, get it - if (num_outputs == 1) - { - if (change_amount == 0) - { - if (self_send_output_types.size() == 1) - { - // txs need at least 2 outputs; we already have a self-send, so make a random special dummy output + // 3. count the number of exclusive self sends, shouldn't be more than 1 + int num_exclusive_self_sends = 0; + for (const jamtis::JamtisSelfSendType self_send_type : self_send_output_types) + if (jamtis::is_jamtis_exclusive_selfsend_type(self_send_type)) + ++num_exclusive_self_sends; - // add a special dummy output - // - 0 amount - // - make sure the final proposal set will have 1 unique enote ephemeral pubkey - return OutputProposalSetExtraTypeV1::SPECIAL_DUMMY; - } - else //(no self-send) - { - // txs need at least 2 outputs, with at least 1 self-send enote type + CHECK_AND_ASSERT_THROW_MES(num_exclusive_self_sends <= 1, "Additional output type v1: there " + "are too many exclusive self-sends in this proposal set. If you want to send another " + "self-send to yourself, make it an auxiliary type enote"); - // add a special self-send dummy output - // - 0 amount - // - make sure the final proposal set will have 1 unique enote ephemeral pubkey - return OutputProposalSetExtraTypeV1::SPECIAL_SELF_SEND_DUMMY; - } - } - else if (/*change_amount > 0 &&*/ - self_send_output_types.size() == 1 && - self_send_output_types[0] == jamtis::JamtisSelfSendType::CHANGE) - { - // 2-out txs may not have 2 self-send type enotes of the same type from the same wallet, so since - // we already have a change output (for some dubious reason) we can't have a special change here - // reason: the outputs in a 2-out tx with 2 same-type self-sends would have the same sender-receiver shared - // secret, which could cause problems (e.g. the outputs would have the same view tags, and could even - // have the same onetime address if the destinations of the two outputs are the same) - - // two change outputs doesn't make sense, so just ban it - CHECK_AND_ASSERT_THROW_MES(false, "Additional output type v1: there is 1 change-type output already " - "specified, but the change amount is non-zero and a tx with just two change outputs is not allowed " - "for privacy reasons. If you want to make a tx with just two change outputs, avoid calling this function " - "(not recommended)."); - } - else //(change_amount > 0 && single output is not a self-send change) - { - // if there is 1 non-change output and non-zero change, then make a special change output that shares - // the other output's enote ephemeral pubkey + // 4. if we don't have any exclusive self-send enotes, we need to add one so that exactly one + // view tag in this transaction is guaranteed to match (the others may match by pure chance) + const bool cannot_add_output = 2 == num_outputs && !output_ephemeral_pubkeys_are_unique; + if (0 == num_exclusive_self_sends && !cannot_add_output) + { + // for proposal sets with 1 outputs thus far, we need a "special" enote with a shared + // enote ephemeral key. for everything else, its normal exclusive + if (1 == num_outputs) + return OutputProposalSetExtraTypeV1::SPECIAL_EXCLUSIVE_CHANGE; + else + return OutputProposalSetExtraTypeV1::NORMAL_EXCLUSIVE_CHANGE; + } - // add a special change output - // - 'change' amount - // - make sure the final proposal set will have 1 unique enote ephemeral pubkey - return OutputProposalSetExtraTypeV1::SPECIAL_CHANGE; - } + // 5. if an extra auxiliary output is needed, get it + if (1 == num_outputs) + { + // if just one exclusive self-send enote, add auxiliary change so only 1 view tag matches + return OutputProposalSetExtraTypeV1::SPECIAL_AUXILIARY_CHANGE; } - else if (num_outputs == 2 && output_ephemeral_pubkeys_are_unique) + else if (2 == num_outputs && output_ephemeral_pubkeys_are_unique) { - if (change_amount == 0) - { - // 2-out txs need 1 shared enote ephemeral pubkey; add a dummy output here since the outputs have different - // enote ephemeral pubkeys - - if (self_send_output_types.size() > 0) - { - // if we have at least 1 self-send already, we can just make a normal dummy output - - // add a normal dummy output - // - 0 amount - return OutputProposalSetExtraTypeV1::NORMAL_DUMMY; - } - else //(no self-sends) - { - // if there are no self-sends, then we need to add a dummy self-send - - // add a normal self-send dummy output - // - 0 amount - return OutputProposalSetExtraTypeV1::NORMAL_SELF_SEND_DUMMY; - } - } - else //(change_amount > 0) - { - // 2 separate outputs + 1 change output = a simple 3-out tx - - // add a normal change output - // - 'change' amount - return OutputProposalSetExtraTypeV1::NORMAL_CHANGE; - } + // 2-out txs need 1 shared enote ephemeral pubkey; add an auxiliary change output here since + // the outputs have different enote ephemeral pubkeys + return OutputProposalSetExtraTypeV1::NORMAL_AUXILIARY_CHANGE; } - else if (num_outputs == 2 && !output_ephemeral_pubkeys_are_unique) + else if (2 == num_outputs && !output_ephemeral_pubkeys_are_unique) { - if (change_amount == 0) + if (0 == change_amount) { - if (self_send_output_types.size() == 2 && - self_send_output_types[0] == self_send_output_types[1]) - { - CHECK_AND_ASSERT_THROW_MES(false, "Additional output type v1: there are 2 self-send outputs with the " - "same type that share an enote ephemeral pubkey, but this can reduce user privacy. If you want to " - "send money to yourself, then make independent self-spend types, or avoid calling this function (not " - "recommended)."); - } - else if (self_send_output_types.size() > 0) + if (num_exclusive_self_sends) { // do nothing: the proposal set is already 'final' } - else //(no self-sends) + else //(no exclusive self-sends) { - CHECK_AND_ASSERT_THROW_MES(false, "Additional output type v1: there are 2 normal outputs that share " - "an enote ephemeral pubkey, but every tx needs at least one self-send output (since the " - "2 outputs share an enote ephemeral pubkey, we can't add a dummy self-send). If you want " - "to make a 2-output tx with no self-sends, then avoid calling this function (not recommended)."); + ASSERT_MES_AND_THROW("Additional output type v1: there are 2 normal " + "and/or auxiliary selfsend outputs that share an enote ephemeral pubkey, but " + "every tx needs at exactly one exclusive self-send output (since the 2 outputs " + "share an enote ephemeral pubkey, we can't add a 0-output self-send). If you " + "want to make a 2-output tx with no self-sends, then avoid calling this " + "function (not recommended)."); } } else //(change_amount > 0) { - CHECK_AND_ASSERT_THROW_MES(false, "Additional output type v1: there are 2 outputs that share " - "an enote ephemeral pubkey, but a non-zero change amount. In >2-out txs, all enote ephemeral pubkeys " - "should be unique, so adding a change output isn't feasible here. You need to make independent output " - "proposals, or avoid calling this function (not recommended)."); + ASSERT_MES_AND_THROW("Additional output type v1: there are 2 outputs that share " + "an enote ephemeral pubkey, but a non-zero change amount. In >2-out txs, all enote " + "ephemeral pubkeys should be unique, so adding a change output isn't feasible here. " + "You need to make independent output proposals, or avoid calling this function " + "(not recommended)."); } } else //(num_outputs > 2) { CHECK_AND_ASSERT_THROW_MES(output_ephemeral_pubkeys_are_unique, - "Additional output type v1: there are >2 outputs but their enote ephemeral pubkeys aren't all unique."); + "Additional output type v1: there are >2 outputs but their enote ephemeral pubkeys " + "aren't all unique."); - if (change_amount == 0) + if (0 == change_amount) { - if (self_send_output_types.size() > 0) - { - // do nothing: the proposal set is already 'final' - } - else //(no self-sends) - { - // every tx made by this function needs a self-send output, so make a dummy self-send here - - // add a normal self-send dummy output - // - 0 amount - return OutputProposalSetExtraTypeV1::NORMAL_SELF_SEND_DUMMY; - } + // do nothing: the proposal set is already 'final' } else //(change_amount > 0) { - // >2 separate outputs + 1 change output = a simple tx with 4+ outputs - - // add a normal change output - // - 'change' amount - return OutputProposalSetExtraTypeV1::NORMAL_CHANGE; + // we need change! + return OutputProposalSetExtraTypeV1::NORMAL_AUXILIARY_CHANGE; } } return boost::none; } //------------------------------------------------------------------------------------------------------------------- -void make_additional_output_dummy_v1(const OutputProposalSetExtraTypeV1 additional_output_type, - const crypto::x25519_pubkey &first_enote_ephemeral_pubkey, - jamtis::JamtisPaymentProposalV1 &normal_proposal_out) -{ - // choose which output type to make, and make it - if (additional_output_type == OutputProposalSetExtraTypeV1::NORMAL_DUMMY) - { - // normal dummy - // - 0 amount - make_additional_output_normal_dummy_v1(normal_proposal_out); - } - else if (additional_output_type == OutputProposalSetExtraTypeV1::SPECIAL_DUMMY) - { - // special dummy - // - 0 amount - // - shared enote ephemeral pubkey - make_additional_output_special_dummy_v1(first_enote_ephemeral_pubkey, normal_proposal_out); - } - else - { - CHECK_AND_ASSERT_THROW_MES(false, "Unknown output proposal set extra type (dummy)."); - } -} -//------------------------------------------------------------------------------------------------------------------- -void make_additional_output_selfsend_v1(const OutputProposalSetExtraTypeV1 additional_output_type, +void make_additional_output_v1(const OutputProposalSetExtraTypeV1 additional_output_type, const crypto::x25519_pubkey &first_enote_ephemeral_pubkey, + const std::uint8_t num_primary_view_tag_bits, const jamtis::JamtisDestinationV1 &change_destination, - const jamtis::JamtisDestinationV1 &dummy_destination, const crypto::secret_key &k_view_balance, const rct::xmr_amount change_amount, jamtis::JamtisPaymentProposalSelfSendV1 &selfsend_proposal_out) { - // choose which output type to make, and make it - if (additional_output_type == OutputProposalSetExtraTypeV1::NORMAL_SELF_SEND_DUMMY) - { - // normal self-send dummy - // - 0 amount - make_additional_output_normal_self_send_v1(jamtis::JamtisSelfSendType::DUMMY, - dummy_destination, - 0, - selfsend_proposal_out); - } - else if (additional_output_type == OutputProposalSetExtraTypeV1::NORMAL_CHANGE) + const jamtis::JamtisSelfSendType self_send_enote_type = + (additional_output_type == OutputProposalSetExtraTypeV1::NORMAL_EXCLUSIVE_CHANGE || + additional_output_type == OutputProposalSetExtraTypeV1::SPECIAL_EXCLUSIVE_CHANGE) + ? jamtis::JamtisSelfSendType::EXCLUSIVE_CHANGE + : jamtis::JamtisSelfSendType::AUXILIARY_CHANGE; + + switch (additional_output_type) { - // normal change - // - 'change' amount - make_additional_output_normal_self_send_v1(jamtis::JamtisSelfSendType::CHANGE, + case OutputProposalSetExtraTypeV1::NORMAL_EXCLUSIVE_CHANGE: + case OutputProposalSetExtraTypeV1::NORMAL_AUXILIARY_CHANGE: + make_additional_output_normal_self_send_v1(self_send_enote_type, change_destination, change_amount, + num_primary_view_tag_bits, selfsend_proposal_out); - } - else if (additional_output_type == OutputProposalSetExtraTypeV1::SPECIAL_SELF_SEND_DUMMY) - { - // special self-send dummy - // - 0 amount - // - shared enote ephemeral pubkey - make_additional_output_special_self_send_v1(jamtis::JamtisSelfSendType::DUMMY, - first_enote_ephemeral_pubkey, - dummy_destination, - k_view_balance, - 0, - selfsend_proposal_out); - } - else if (additional_output_type == OutputProposalSetExtraTypeV1::SPECIAL_CHANGE) - { - // special change - // - 'change' amount - // - shared enote ephemeral pubkey - make_additional_output_special_self_send_v1(jamtis::JamtisSelfSendType::CHANGE, + break; + case OutputProposalSetExtraTypeV1::SPECIAL_EXCLUSIVE_CHANGE: + case OutputProposalSetExtraTypeV1::SPECIAL_AUXILIARY_CHANGE: + make_additional_output_special_self_send_v1(self_send_enote_type, first_enote_ephemeral_pubkey, + num_primary_view_tag_bits, change_destination, k_view_balance, change_amount, selfsend_proposal_out); - } - else - { - CHECK_AND_ASSERT_THROW_MES(false, "Unknown output proposal set extra type (self-send)."); - } -} -//------------------------------------------------------------------------------------------------------------------- -void make_additional_output_v1(const jamtis::JamtisDestinationV1 &change_destination, - const jamtis::JamtisDestinationV1 &dummy_destination, - const crypto::secret_key &k_view_balance, - const rct::xmr_amount change_amount, - const OutputProposalSetExtraTypeV1 additional_output_type, - const crypto::x25519_pubkey &first_enote_ephemeral_pubkey, - std::vector &normal_payment_proposals_inout, - std::vector &selfsend_payment_proposals_inout) -{ - if (additional_output_type == OutputProposalSetExtraTypeV1::NORMAL_DUMMY || - additional_output_type == OutputProposalSetExtraTypeV1::SPECIAL_DUMMY) - { - make_additional_output_dummy_v1(additional_output_type, - first_enote_ephemeral_pubkey, - tools::add_element(normal_payment_proposals_inout)); - } - else - { - make_additional_output_selfsend_v1(additional_output_type, - first_enote_ephemeral_pubkey, - change_destination, - dummy_destination, - k_view_balance, - change_amount, - tools::add_element(selfsend_payment_proposals_inout)); + break; + default: + ASSERT_MES_AND_THROW("Unknown output proposal set extra type (self-send)."); } } //------------------------------------------------------------------------------------------------------------------- void finalize_v1_output_proposal_set_v1(const boost::multiprecision::uint128_t &total_input_amount, const rct::xmr_amount transaction_fee, const jamtis::JamtisDestinationV1 &change_destination, - const jamtis::JamtisDestinationV1 &dummy_destination, const crypto::secret_key &k_view_balance, std::vector &normal_payment_proposals_inout, std::vector &selfsend_payment_proposals_inout) @@ -709,7 +584,14 @@ void finalize_v1_output_proposal_set_v1(const boost::multiprecision::uint128_t & else if (selfsend_payment_proposals_inout.size() > 0) jamtis::get_enote_ephemeral_pubkey(selfsend_payment_proposals_inout[0], first_enote_ephemeral_pubkey); - // 4. add an additional output if necessary + // 4. get shared npbits value + const std::uint8_t num_primary_view_tag_bits = get_shared_num_primary_view_tag_bits( + normal_payment_proposals_inout, + selfsend_payment_proposals_inout, + {}, + {}); + + // 5. add an additional output if necessary if (const auto additional_output_type = try_get_additional_output_type_for_output_set_v1( normal_payment_proposals_inout.size() + selfsend_payment_proposals_inout.size(), @@ -718,14 +600,13 @@ void finalize_v1_output_proposal_set_v1(const boost::multiprecision::uint128_t & change_amount) ) { - make_additional_output_v1(change_destination, - dummy_destination, + make_additional_output_v1(*additional_output_type, + first_enote_ephemeral_pubkey, + num_primary_view_tag_bits, + change_destination, k_view_balance, change_amount, - *additional_output_type, - first_enote_ephemeral_pubkey, - normal_payment_proposals_inout, - selfsend_payment_proposals_inout); + tools::add_element(selfsend_payment_proposals_inout)); } } //------------------------------------------------------------------------------------------------------------------- diff --git a/src/seraphis_main/tx_builders_outputs.h b/src/seraphis_main/tx_builders_outputs.h index ce61a44bc9..f931ddfcb5 100644 --- a/src/seraphis_main/tx_builders_outputs.h +++ b/src/seraphis_main/tx_builders_outputs.h @@ -53,18 +53,14 @@ namespace sp enum class OutputProposalSetExtraTypeV1 { - // a plain dummy output (random recipient, random enote ephemeral pubkey, zero amount) - NORMAL_DUMMY, - // a self-send dummy output (self recipient, normal enote ephemeral pubkey, zero amount) - NORMAL_SELF_SEND_DUMMY, - // a normal change output (self recipient, normal enote ephemeral pubkey, non-zero amount) - NORMAL_CHANGE, - // a special dummy output (random recipient, shared enote ephemeral pubkey, zero amount) - SPECIAL_DUMMY, - // a special self-send dummy output (self recipient, shared enote ephemeral pubkey, zero amount) - SPECIAL_SELF_SEND_DUMMY, - // a special change output (self recipient, shared enote ephemeral pubkey, non-zero amount) - SPECIAL_CHANGE + // primary view tag flagging, normal enote ephemeral pubkey + NORMAL_EXCLUSIVE_CHANGE, + // NOT primary view tag flagging, normal enote ephemeral pubkey + NORMAL_AUXILIARY_CHANGE, + // primary view tag flagging, shared enote ephemeral pubkey + SPECIAL_EXCLUSIVE_CHANGE, + // NOT primary view tag flagging, shared enote ephemeral pubkey + SPECIAL_AUXILIARY_CHANGE, }; /** @@ -117,6 +113,7 @@ void make_v1_coinbase_output_proposal_v1(const jamtis::JamtisPaymentProposalV1 & * brief: make_v1_output_proposal_v1 - convert a jamtis proposal to an output proposal * param: proposal - * param: input_context - +* param: num_primary_view_tag_bits - * outparam: output_proposal_out - */ void make_v1_output_proposal_v1(const jamtis::JamtisPaymentProposalV1 &proposal, @@ -127,6 +124,7 @@ void make_v1_output_proposal_v1(const jamtis::JamtisPaymentProposalV1 &proposal, * param: proposal - * param: k_view_balance - * param: input_context - +* param: num_primary_view_tag_bits - * outparam: output_proposal_out - */ void make_v1_output_proposal_v1(const jamtis::JamtisPaymentProposalSelfSendV1 &proposal, @@ -159,14 +157,9 @@ void make_v1_outputs_v1(const std::vector &output_proposals, * brief: finalize_v1_output_proposal_set_v1 - finalize a set of output proposals by adding 0-1 new proposals * (new proposals are appended) * - NOT FOR COINBASE OUTPUT SETS (coinbase output sets don't need to be finalized) -* - add a change output if necessary -* - add a dummy output if appropriate -* - All output sets will contain at least 1 self-send, either from the original set passed in, or by adding a change -* or selfsend dummy here. -* - Only very rare txs should have more than two outputs and include a dummy output (i.e. have numerically more outputs -* than if this invariant weren't enforced; note that all txs must have at least two outputs). Only txs with at least -* two outputs and zero change amount and zero specified self-sends will acquire an additional dummy selfsend output. -* - A self-send dummy will only be made if there are no other self-sends; otherwise dummies will be purely random. +* - add an exclusive/auxiliary change output if necessary +* - All output sets will contain exactly 1 exclusive self-send, plus any number of auxiliary self-sends, either from +* the original set passed in, or by adding an exclusive/auxiliary change enote here. * - The goal of this function is for all txs made from output sets produced by this function to be identifiable by view * tag checks. That way, a signer scanning for balance recovery only needs key images from txs that are flagged by a * view tag check in order to A) identify all spent enotes, B) identify all of their self-send enotes in txs that use @@ -177,7 +170,6 @@ void make_v1_outputs_v1(const std::vector &output_proposals, * param: total_input_amount - * param: transaction_fee - * param: change_destination - -* param: dummy_destination - * param: jamtis_spend_pubkey - * param: k_view_balance - * inoutparam: normal_payment_proposals_inout - @@ -188,28 +180,16 @@ boost::optional try_get_additional_output_type_for const std::vector &self_send_output_types, const bool output_ephemeral_pubkeys_are_unique, const rct::xmr_amount change_amount); -void make_additional_output_dummy_v1(const OutputProposalSetExtraTypeV1 additional_output_type, - const crypto::x25519_pubkey &first_enote_ephemeral_pubkey, - jamtis::JamtisPaymentProposalV1 &normal_proposal_out); //exposed for unit testing -void make_additional_output_selfsend_v1(const OutputProposalSetExtraTypeV1 additional_output_type, +void make_additional_output_v1(const OutputProposalSetExtraTypeV1 additional_output_type, const crypto::x25519_pubkey &first_enote_ephemeral_pubkey, + const std::uint8_t num_primary_view_tag_bits, const jamtis::JamtisDestinationV1 &change_destination, - const jamtis::JamtisDestinationV1 &dummy_destination, const crypto::secret_key &k_view_balance, const rct::xmr_amount change_amount, jamtis::JamtisPaymentProposalSelfSendV1 &selfsend_proposal_out); //exposed for unit testing -void make_additional_output_v1(const jamtis::JamtisDestinationV1 &change_destination, - const jamtis::JamtisDestinationV1 &dummy_destination, - const crypto::secret_key &k_view_balance, - const rct::xmr_amount change_amount, - const OutputProposalSetExtraTypeV1 additional_output_type, - const crypto::x25519_pubkey &first_enote_ephemeral_pubkey, - std::vector &normal_payment_proposals_inout, - std::vector &selfsend_payment_proposals_inout); //exposed for unit testing void finalize_v1_output_proposal_set_v1(const boost::multiprecision::uint128_t &total_input_amount, const rct::xmr_amount transaction_fee, const jamtis::JamtisDestinationV1 &change_destination, - const jamtis::JamtisDestinationV1 &dummy_destination, const crypto::secret_key &k_view_balance, std::vector &normal_payment_proposals_inout, std::vector &selfsend_payment_proposals_inout); diff --git a/src/seraphis_main/tx_component_types.cpp b/src/seraphis_main/tx_component_types.cpp index 59da085c9c..f219b75fcf 100644 --- a/src/seraphis_main/tx_component_types.cpp +++ b/src/seraphis_main/tx_component_types.cpp @@ -56,7 +56,7 @@ void append_to_transcript(const SpCoinbaseEnoteV1 &container, SpTranscriptBuilde { transcript_inout.append("core", container.core); transcript_inout.append("addr_tag_enc", container.addr_tag_enc.bytes); - transcript_inout.append("view_tag", container.view_tag); + transcript_inout.append("view_tag", container.view_tag.bytes); } //------------------------------------------------------------------------------------------------------------------- std::size_t sp_coinbase_enote_v1_size_bytes() @@ -71,7 +71,7 @@ void append_to_transcript(const SpEnoteV1 &container, SpTranscriptBuilder &trans transcript_inout.append("core", container.core); transcript_inout.append("encoded_amount", container.encoded_amount.bytes); transcript_inout.append("addr_tag_enc", container.addr_tag_enc.bytes); - transcript_inout.append("view_tag", container.view_tag); + transcript_inout.append("view_tag", container.view_tag.bytes); } //------------------------------------------------------------------------------------------------------------------- std::size_t sp_enote_v1_size_bytes() @@ -255,7 +255,7 @@ std::size_t sp_balance_proof_v1_weight(const SpBalanceProofV1 &proof) //------------------------------------------------------------------------------------------------------------------- void append_to_transcript(const SpTxSupplementV1 &container, SpTranscriptBuilder &transcript_inout) { - transcript_inout.append("output_xK_e_keys", container.output_enote_ephemeral_pubkeys); + transcript_inout.append("output_D_e_keys", container.output_enote_ephemeral_pubkeys); transcript_inout.append("tx_extra", container.tx_extra); } //------------------------------------------------------------------------------------------------------------------- @@ -342,7 +342,7 @@ SpCoinbaseEnoteV1 gen_sp_coinbase_enote_v1() // extra pieces crypto::rand(sizeof(jamtis::encrypted_address_tag_t), temp.addr_tag_enc.bytes); - temp.view_tag = crypto::rand_idx(0); + temp.view_tag = jamtis::gen_view_tag(); return temp; } @@ -357,7 +357,7 @@ SpEnoteV1 gen_sp_enote_v1() // extra pieces crypto::rand(sizeof(temp.encoded_amount), temp.encoded_amount.bytes); crypto::rand(sizeof(jamtis::encrypted_address_tag_t), temp.addr_tag_enc.bytes); - temp.view_tag = crypto::rand_idx(0); + temp.view_tag = jamtis::gen_view_tag(); return temp; } diff --git a/src/seraphis_main/tx_component_types.h b/src/seraphis_main/tx_component_types.h index 36269cd016..af0349ff09 100644 --- a/src/seraphis_main/tx_component_types.h +++ b/src/seraphis_main/tx_component_types.h @@ -200,8 +200,10 @@ std::size_t sp_balance_proof_v1_weight(const SpBalanceProofV1 &proof); /// struct SpTxSupplementV1 final { - /// xKe: enote ephemeral pubkeys for outputs + /// D_e[]: enote ephemeral pubkeys for outputs std::vector output_enote_ephemeral_pubkeys; + /// npbits + std::uint8_t num_primary_view_tag_bits; /// tx memo TxExtra tx_extra; }; diff --git a/src/seraphis_main/tx_validators.cpp b/src/seraphis_main/tx_validators.cpp index 6482e69a14..3fd8783e13 100644 --- a/src/seraphis_main/tx_validators.cpp +++ b/src/seraphis_main/tx_validators.cpp @@ -287,6 +287,7 @@ bool validate_sp_semantics_input_images_v1(const std::vector //------------------------------------------------------------------------------------------------------------------- bool validate_sp_semantics_coinbase_layout_v1(const std::vector &outputs, const std::vector &enote_ephemeral_pubkeys, + const std::uint8_t num_primary_view_tag_bits, const TxExtra &tx_extra) { // output enotes should be sorted by onetime address with byte-wise comparisons (ascending), and unique @@ -297,6 +298,10 @@ bool validate_sp_semantics_coinbase_layout_v1(const std::vector 8 * jamtis::VIEW_TAG_BYTES) + return false; + // tx extra fields should be in sorted TLV (Type-Length-Value) format std::vector extra_field_elements; if (!try_get_extra_field_elements(tx_extra, extra_field_elements)) @@ -311,6 +316,7 @@ bool validate_sp_semantics_layout_v1(const std::vector &l const std::vector &sp_input_images, const std::vector &outputs, const std::vector &enote_ephemeral_pubkeys, + const std::uint8_t num_primary_view_tag_bits, const TxExtra &tx_extra) { using legacy_ring_sig_element_t = @@ -352,6 +358,10 @@ bool validate_sp_semantics_layout_v1(const std::vector &l if (!keys_are_unique(enote_ephemeral_pubkeys)) return false; + // npbits should be less than or equal to the number of bits in a view tag + if (num_primary_view_tag_bits > 8 * jamtis::VIEW_TAG_BYTES) + return false; + // tx extra fields should be in sorted TLV (Type-Length-Value) format std::vector extra_field_elements; if (!try_get_extra_field_elements(tx_extra, extra_field_elements)) diff --git a/src/seraphis_main/tx_validators.h b/src/seraphis_main/tx_validators.h index 426d0bca6d..ce9c69dba8 100644 --- a/src/seraphis_main/tx_validators.h +++ b/src/seraphis_main/tx_validators.h @@ -177,11 +177,13 @@ bool validate_sp_semantics_input_images_v1(const std::vector * - extra field is in sorted TLV (Type-Length-Value) format * param: outputs - * param: enote_ephemeral_pubkeys - +* param: num_primary_view_tag_bits - * param: tx_extra - * return: true/false on validation result */ bool validate_sp_semantics_coinbase_layout_v1(const std::vector &outputs, const std::vector &enote_ephemeral_pubkeys, + const std::uint8_t num_primary_view_tag_bits, const TxExtra &tx_extra); /** * brief: validate_sp_semantics_layout_v1 - check tx components have the proper layout @@ -201,6 +203,7 @@ bool validate_sp_semantics_coinbase_layout_v1(const std::vector &l const std::vector &sp_input_images, const std::vector &outputs, const std::vector &enote_ephemeral_pubkeys, + const std::uint8_t num_primary_view_tag_bits, const TxExtra &tx_extra); /** * brief: validate_sp_semantics_fee_v1 - check that a discretized fee is a valid fee representation diff --git a/src/seraphis_main/txtype_coinbase_v1.cpp b/src/seraphis_main/txtype_coinbase_v1.cpp index cfb8765dea..0e916f0098 100644 --- a/src/seraphis_main/txtype_coinbase_v1.cpp +++ b/src/seraphis_main/txtype_coinbase_v1.cpp @@ -140,6 +140,8 @@ void make_seraphis_tx_coinbase_v1(const SpTxCoinbaseV1::SemanticRulesVersion sem SpTxSupplementV1 tx_supplement; make_v1_coinbase_outputs_v1(output_proposals, output_enotes, tx_supplement.output_enote_ephemeral_pubkeys); + tx_supplement.num_primary_view_tag_bits = get_shared_num_primary_view_tag_bits({}, {}, output_proposals, {}); + // 4. collect full memo finalize_tx_extra_v1(tx_proposal.partial_memo, output_proposals, tx_supplement.tx_extra); @@ -208,6 +210,7 @@ bool validate_tx_semantics(const SpTxCoinbaseV1 &tx) // validate layout (sorting, uniqueness) of outputs and tx supplement if (!validate_sp_semantics_coinbase_layout_v1(tx.outputs, tx.tx_supplement.output_enote_ephemeral_pubkeys, + tx.tx_supplement.num_primary_view_tag_bits, tx.tx_supplement.tx_extra)) return false; diff --git a/src/seraphis_main/txtype_squashed_v1.cpp b/src/seraphis_main/txtype_squashed_v1.cpp index d72e709f81..d977d260e9 100644 --- a/src/seraphis_main/txtype_squashed_v1.cpp +++ b/src/seraphis_main/txtype_squashed_v1.cpp @@ -552,6 +552,7 @@ bool validate_tx_semantics(const SpTxSquashedV1 &tx) tx.sp_input_images, tx.outputs, tx.tx_supplement.output_enote_ephemeral_pubkeys, + tx.tx_supplement.num_primary_view_tag_bits, tx.tx_supplement.tx_extra)) return false; diff --git a/src/seraphis_mocks/enote_finding_context_mocks.cpp b/src/seraphis_mocks/enote_finding_context_mocks.cpp index bcf7e68641..6397fecb49 100644 --- a/src/seraphis_mocks/enote_finding_context_mocks.cpp +++ b/src/seraphis_mocks/enote_finding_context_mocks.cpp @@ -81,7 +81,7 @@ std::unique_ptr EnoteFindingContextLedgerMockSp::get_onch m_mock_ledger_context.get_onchain_chunk_sp(chunk_start_index, chunk_max_size, - m_xk_find_received, + m_d_filter_assist, chunk_context, chunk_data); @@ -94,12 +94,12 @@ std::unique_ptr EnoteFindingContextLedgerMockSp::get_onch //------------------------------------------------------------------------------------------------------------------- void EnoteFindingContextUnconfirmedMockSp::get_nonledger_chunk(scanning::ChunkData &chunk_out) const { - m_mock_ledger_context.get_unconfirmed_chunk_sp(m_xk_find_received, chunk_out); + m_mock_ledger_context.get_unconfirmed_chunk_sp(m_d_filter_assist, chunk_out); } //------------------------------------------------------------------------------------------------------------------- void EnoteFindingContextOffchainMockSp::get_nonledger_chunk(scanning::ChunkData &chunk_out) const { - m_mock_offchain_context.get_offchain_chunk_sp(m_xk_find_received, chunk_out); + m_mock_offchain_context.get_offchain_chunk_sp(m_d_filter_assist, chunk_out); } //------------------------------------------------------------------------------------------------------------------- } //namespace mocks diff --git a/src/seraphis_mocks/enote_finding_context_mocks.h b/src/seraphis_mocks/enote_finding_context_mocks.h index dbc163591b..6e50c557d0 100644 --- a/src/seraphis_mocks/enote_finding_context_mocks.h +++ b/src/seraphis_mocks/enote_finding_context_mocks.h @@ -28,7 +28,7 @@ // NOT FOR PRODUCTION -// Dependency injectors for the find-received step of enote scanning (mock-ups). +// Dependency injectors for the filter-assist step of enote scanning (mock-ups). #pragma once @@ -104,16 +104,16 @@ class EnoteFindingContextLedgerMockLegacy final : public EnoteFindingContextLedg //// // EnoteFindingContextLedgerMockSp -// - wraps a mock ledger context, produces chunks of potentially owned enotes (from find-received scanning) +// - wraps a mock ledger context, produces chunks of potentially owned enotes (from filter-assist scanning) /// class EnoteFindingContextLedgerMockSp final : public EnoteFindingContextLedger { public: //constructors EnoteFindingContextLedgerMockSp(const MockLedgerContext &mock_ledger_context, - const crypto::x25519_secret_key &xk_find_received) : + const crypto::x25519_secret_key &d_filter_assist) : m_mock_ledger_context{mock_ledger_context}, - m_xk_find_received{xk_find_received} + m_d_filter_assist{d_filter_assist} {} //overloaded operators @@ -128,21 +128,21 @@ class EnoteFindingContextLedgerMockSp final : public EnoteFindingContextLedger //member variables private: const MockLedgerContext &m_mock_ledger_context; - const crypto::x25519_secret_key &m_xk_find_received; + const crypto::x25519_secret_key &m_d_filter_assist; }; //// // EnoteFindingContextUnconfirmedMockSp -// - wraps a mock ledger context, produces chunks of potentially owned unconfirmed enotes (from find-received scanning) +// - wraps a mock ledger context, produces chunks of potentially owned unconfirmed enotes (from filter-assist scanning) /// class EnoteFindingContextUnconfirmedMockSp final : public EnoteFindingContextNonLedger { public: //constructors EnoteFindingContextUnconfirmedMockSp(const MockLedgerContext &mock_ledger_context, - const crypto::x25519_secret_key &xk_find_received) : + const crypto::x25519_secret_key &d_filter_assist) : m_mock_ledger_context{mock_ledger_context}, - m_xk_find_received{xk_find_received} + m_d_filter_assist{d_filter_assist} {} //overloaded operators @@ -156,21 +156,21 @@ class EnoteFindingContextUnconfirmedMockSp final : public EnoteFindingContextNon //member variables private: const MockLedgerContext &m_mock_ledger_context; - const crypto::x25519_secret_key &m_xk_find_received; + const crypto::x25519_secret_key &m_d_filter_assist; }; //// // EnoteFindingContextOffchainMockSp -// - wraps a mock offchain context, produces chunks of potentially owned enotes (from find-received scanning) +// - wraps a mock offchain context, produces chunks of potentially owned enotes (from filter-assist scanning) /// class EnoteFindingContextOffchainMockSp final : public EnoteFindingContextNonLedger { public: //constructors EnoteFindingContextOffchainMockSp(const MockOffchainContext &mock_offchain_context, - const crypto::x25519_secret_key &xk_find_received) : + const crypto::x25519_secret_key &d_filter_assist) : m_mock_offchain_context{mock_offchain_context}, - m_xk_find_received{xk_find_received} + m_d_filter_assist{d_filter_assist} {} //overloaded operators @@ -184,7 +184,7 @@ class EnoteFindingContextOffchainMockSp final : public EnoteFindingContextNonLed //member variables private: const MockOffchainContext &m_mock_offchain_context; - const crypto::x25519_secret_key &m_xk_find_received; + const crypto::x25519_secret_key &m_d_filter_assist; }; } //namespace mocks diff --git a/src/seraphis_mocks/jamtis_mock_keys.cpp b/src/seraphis_mocks/jamtis_mock_keys.cpp index 12494336cb..47acc0c660 100644 --- a/src/seraphis_mocks/jamtis_mock_keys.cpp +++ b/src/seraphis_mocks/jamtis_mock_keys.cpp @@ -35,7 +35,7 @@ #include "crypto/crypto.h" #include "crypto/x25519.h" #include "ringct/rctOps.h" -#include "seraphis_core/jamtis_core_utils.h" +#include "seraphis_core/jamtis_account_secrets.h" #include "seraphis_core/sp_core_enote_utils.h" //third party headers @@ -57,28 +57,36 @@ void make_jamtis_mock_keys(jamtis_mock_keys &keys_out) { keys_out.k_m = rct::rct2sk(rct::skGen()); keys_out.k_vb = rct::rct2sk(rct::skGen()); - make_jamtis_unlockamounts_key(keys_out.k_vb, keys_out.xk_ua); - make_jamtis_findreceived_key(keys_out.k_vb, keys_out.xk_fr); - make_jamtis_generateaddress_secret(keys_out.k_vb, keys_out.s_ga); + make_jamtis_viewreceived_key(keys_out.k_vb, keys_out.d_vr); + make_jamtis_filterassist_key(keys_out.d_vr, keys_out.d_fa); + make_jamtis_generateaddress_secret(keys_out.d_vr, keys_out.s_ga); make_jamtis_ciphertag_secret(keys_out.s_ga, keys_out.s_ct); - make_seraphis_spendkey(keys_out.k_vb, keys_out.k_m, keys_out.K_1_base); - make_jamtis_unlockamounts_pubkey(keys_out.xk_ua, keys_out.xK_ua); - make_jamtis_findreceived_pubkey(keys_out.xk_fr, keys_out.xK_ua, keys_out.xK_fr); + make_seraphis_spendkey(keys_out.k_vb, keys_out.k_m, keys_out.K_s_base); + make_jamtis_exchangebase_pubkey(keys_out.d_vr, keys_out.D_base); + make_jamtis_viewreceived_pubkey(keys_out.d_vr, keys_out.D_base, keys_out.D_vr); + make_jamtis_filterassist_pubkey(keys_out.d_fa, keys_out.D_base, keys_out.D_fa); } //------------------------------------------------------------------------------------------------------------------- -void make_random_address_for_user(const jamtis_mock_keys &user_keys, JamtisDestinationV1 &user_address_out) +void make_address_for_user(const jamtis_mock_keys &user_keys, + const address_index_t &j, + JamtisDestinationV1 &user_address_out) { - address_index_t address_index; - address_index = gen_address_index(); - - make_jamtis_destination_v1(user_keys.K_1_base, - user_keys.xK_ua, - user_keys.xK_fr, + make_jamtis_destination_v1(user_keys.K_s_base, + user_keys.D_fa, + user_keys.D_vr, + user_keys.D_base, user_keys.s_ga, - address_index, + j, user_address_out); } //------------------------------------------------------------------------------------------------------------------- +void make_random_address_for_user(const jamtis_mock_keys &user_keys, JamtisDestinationV1 &user_address_out) +{ + const address_index_t random_j = gen_address_index(); + + make_address_for_user(user_keys, random_j, user_address_out); +} +//------------------------------------------------------------------------------------------------------------------- } //namespace mocks } //namespace jamtis } //namespace sp diff --git a/src/seraphis_mocks/jamtis_mock_keys.h b/src/seraphis_mocks/jamtis_mock_keys.h index 593c26ba1d..b3ca2fbade 100644 --- a/src/seraphis_mocks/jamtis_mock_keys.h +++ b/src/seraphis_mocks/jamtis_mock_keys.h @@ -64,19 +64,25 @@ struct jamtis_mock_keys { crypto::secret_key k_m; //master crypto::secret_key k_vb; //view-balance - crypto::x25519_secret_key xk_ua; //unlock-amounts - crypto::x25519_secret_key xk_fr; //find-received + crypto::x25519_secret_key d_vr; //view-received + crypto::x25519_secret_key d_fa; //filter-assist crypto::secret_key s_ga; //generate-address crypto::secret_key s_ct; //cipher-tag - rct::key K_1_base; //jamtis spend base = k_vb X + k_m U - crypto::x25519_pubkey xK_ua; //unlock-amounts pubkey = xk_ua xG - crypto::x25519_pubkey xK_fr; //find-received pubkey = xk_fr xk_ua xG + rct::key K_s_base; //jamtis spend base = k_vb X + k_m U + crypto::x25519_pubkey D_vr; //view-received pubkey = d_vr D_base + crypto::x25519_pubkey D_fa; //filter-assist pubkey = d_fa D_base + crypto::x25519_pubkey D_base; //exchange-base pubkey = d_vr xG }; /// make a set of mock jamtis keys (for mock-ups/unit testing) void make_jamtis_mock_keys(jamtis_mock_keys &keys_out); +/// make a jamtis address for the given privkeys and address index +void make_address_for_user(const jamtis_mock_keys &user_keys, + const address_index_t &j, + JamtisDestinationV1 &user_address_out); /// make a random jamtis address for the given privkeys -void make_random_address_for_user(const jamtis_mock_keys &user_keys, JamtisDestinationV1 &user_address_out); +void make_random_address_for_user(const jamtis_mock_keys &user_keys, + JamtisDestinationV1 &user_address_out); } //namespace mocks } //namespace jamtis diff --git a/src/seraphis_mocks/make_mock_tx.cpp b/src/seraphis_mocks/make_mock_tx.cpp index 21ebd0d569..938ceaba09 100644 --- a/src/seraphis_mocks/make_mock_tx.cpp +++ b/src/seraphis_mocks/make_mock_tx.cpp @@ -79,7 +79,9 @@ void make_mock_tx(const SpTxParamPackV1 ¶ms, // 2. make mock outputs std::vector output_proposals{ - gen_mock_sp_coinbase_output_proposals_v1(out_amounts, params.num_random_memo_elements) + gen_mock_sp_coinbase_output_proposals_v1(out_amounts, + params.num_primary_view_tag_bits, + params.num_random_memo_elements) }; // 3. make partial memo @@ -98,9 +100,12 @@ void make_mock_tx(const SpTxParamPackV1 ¶ms, make_v1_coinbase_outputs_v1(output_proposals, output_enotes, tx_supplement.output_enote_ephemeral_pubkeys); // 5. collect full memo + tx_supplement.num_primary_view_tag_bits = get_shared_num_primary_view_tag_bits({}, {}, output_proposals, {}); + + // 6. collect full memo finalize_tx_extra_v1(partial_memo, output_proposals, tx_supplement.tx_extra); - // 6. finish tx + // 7. finish tx make_seraphis_tx_coinbase_v1(semantic_rules_version, ledger_context_inout.chain_height() + 1, //next block std::move(output_enotes), @@ -148,7 +153,9 @@ void make_mock_tx(const SpTxParamPackV1 ¶ms, // 6. make mock outputs std::vector output_proposals{ - gen_mock_sp_output_proposals_v1(out_amounts, params.num_random_memo_elements) + gen_mock_sp_output_proposals_v1(out_amounts, + params.num_primary_view_tag_bits, + params.num_random_memo_elements) }; // 7. for 2-out txs, the enote ephemeral pubkey is shared by both outputs diff --git a/src/seraphis_mocks/make_mock_tx.h b/src/seraphis_mocks/make_mock_tx.h index a21d1cc0e6..bcc3164a12 100644 --- a/src/seraphis_mocks/make_mock_tx.h +++ b/src/seraphis_mocks/make_mock_tx.h @@ -86,6 +86,7 @@ struct SpTxParamPackV1 std::vector sp_input_amounts{}; std::vector output_amounts{}; DiscretizedFee discretized_fee{sp::discretize_fee(0)}; + std::uint8_t num_primary_view_tag_bits{0}; }; /// make an SpTxCoinbaseV1 transaction template <> diff --git a/src/seraphis_mocks/mock_ledger_context.cpp b/src/seraphis_mocks/mock_ledger_context.cpp index d5f8df8dac..a4d5819127 100644 --- a/src/seraphis_mocks/mock_ledger_context.cpp +++ b/src/seraphis_mocks/mock_ledger_context.cpp @@ -621,7 +621,7 @@ std::uint64_t MockLedgerContext::pop_blocks(const std::size_t num_blocks) return this->pop_chain_at_index(top_block_index + 1 >= num_blocks ? top_block_index + 1 - num_blocks : 0); } //------------------------------------------------------------------------------------------------------------------- -void MockLedgerContext::get_unconfirmed_chunk_sp(const crypto::x25519_secret_key &xk_find_received, +void MockLedgerContext::get_unconfirmed_chunk_sp(const crypto::x25519_secret_key &d_filter_assist, scanning::ChunkData &chunk_data_out) const { chunk_data_out.basic_records_per_tx.clear(); @@ -638,16 +638,14 @@ void MockLedgerContext::get_unconfirmed_chunk_sp(const crypto::x25519_secret_key m_unconfirmed_tx_output_contents.size() * 2 * 140 / 100 / sizeof(sp::jamtis::view_tag_t) ); - // find-received scan each tx in the unconfirmed chache - std::list collected_records; - SpContextualKeyImageSetV1 collected_key_images; - + // filter-assist scan each tx in the unconfirmed chache for (const auto &tx_with_output_contents : m_unconfirmed_tx_output_contents) { const rct::key &tx_id{sortable2rct(tx_with_output_contents.first)}; // if this tx contains at least one view-tag match, then add the tx's key images to the chunk - if (scanning::try_find_sp_enotes_in_tx(xk_find_received, + std::list collected_records; + if (scanning::try_find_sp_enotes_in_tx(d_filter_assist, -1, -1, tx_id, @@ -664,16 +662,15 @@ void MockLedgerContext::get_unconfirmed_chunk_sp(const crypto::x25519_secret_key CHECK_AND_ASSERT_THROW_MES(m_unconfirmed_tx_key_images.find(tx_with_output_contents.first) != m_unconfirmed_tx_key_images.end(), - "unconfirmed chunk find-received scanning (mock ledger context): key image map missing tx (bug)."); - - if (scanning::try_collect_key_images_from_tx(-1, - -1, - tx_id, - std::get<0>(m_unconfirmed_tx_key_images.at(tx_with_output_contents.first)), - std::get<1>(m_unconfirmed_tx_key_images.at(tx_with_output_contents.first)), - SpEnoteSpentStatus::SPENT_UNCONFIRMED, - collected_key_images)) - chunk_data_out.contextual_key_images.emplace_back(std::move(collected_key_images)); + "unconfirmed chunk filter-assist scanning (mock ledger context): key image map missing tx (bug)."); + + scanning::collect_key_images_from_tx(-1, + -1, + tx_id, + std::get<0>(m_unconfirmed_tx_key_images.at(tx_with_output_contents.first)), + std::get<1>(m_unconfirmed_tx_key_images.at(tx_with_output_contents.first)), + SpEnoteSpentStatus::SPENT_UNCONFIRMED, + tools::add_element(chunk_data_out.contextual_key_images)); } } } @@ -791,9 +788,6 @@ void MockLedgerContext::get_onchain_chunk_legacy(const std::uint64_t chunk_start } // c. legacy-view scan each block in the range - std::list collected_records; - SpContextualKeyImageSetV1 collected_key_images; - std::for_each( m_blocks_of_legacy_tx_output_contents.find(chunk_context_out.start_index), m_blocks_of_legacy_tx_output_contents.find(chunk_end_index), @@ -828,6 +822,7 @@ void MockLedgerContext::get_onchain_chunk_legacy(const std::uint64_t chunk_start // legacy view-scan the tx if in scan mode if (legacy_scan_mode == LegacyScanMode::SCAN) { + std::list collected_records; if (scanning::try_find_legacy_enotes_in_tx(legacy_base_spend_pubkey, legacy_subaddress_map, legacy_view_privkey, @@ -862,7 +857,7 @@ void MockLedgerContext::get_onchain_chunk_legacy(const std::uint64_t chunk_start .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, + scanning::collect_key_images_from_tx(block_of_tx_output_contents.first, std::get(m_block_infos.at(block_of_tx_output_contents.first)), tx_id, std::get<0>(m_blocks_of_tx_key_images @@ -872,8 +867,7 @@ void MockLedgerContext::get_onchain_chunk_legacy(const std::uint64_t chunk_start .at(block_of_tx_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)); + tools::add_element(chunk_data_out.contextual_key_images)); // add this tx's number of outputs to the chunk's total output count per amount for (const LegacyEnoteVariant &enote : enotes) @@ -894,7 +888,7 @@ void MockLedgerContext::get_onchain_chunk_legacy(const std::uint64_t chunk_start //------------------------------------------------------------------------------------------------------------------- void MockLedgerContext::get_onchain_chunk_sp(const std::uint64_t chunk_start_index, const std::uint64_t chunk_max_size, - const crypto::x25519_secret_key &xk_find_received, + const crypto::x25519_secret_key &d_filter_assist, scanning::ChunkContext &chunk_context_out, scanning::ChunkData &chunk_data_out) const { @@ -914,7 +908,7 @@ void MockLedgerContext::get_onchain_chunk_sp(const std::uint64_t chunk_start_ind { CHECK_AND_ASSERT_THROW_MES(m_block_infos.find(chunk_context_out.start_index - 1) != m_block_infos.end(), - "onchain chunk find-received scanning (mock ledger context): block ids map incorrect indexing (bug)."); + "onchain chunk filter-assist scanning (mock ledger context): block ids map incorrect indexing (bug)."); chunk_context_out.prefix_block_id = std::get(m_block_infos.at(chunk_context_out.start_index - 1)); } @@ -936,10 +930,10 @@ void MockLedgerContext::get_onchain_chunk_sp(const std::uint64_t chunk_start_ind }; CHECK_AND_ASSERT_THROW_MES(chunk_end_index > chunk_context_out.start_index, - "onchain chunk find-received scanning (mock ledger context): chunk has no blocks below failure tests (bug)."); + "onchain chunk filter-assist scanning (mock ledger context): chunk has no blocks below failure tests (bug)."); CHECK_AND_ASSERT_THROW_MES(m_block_infos.find(chunk_context_out.start_index) != m_block_infos.end() && m_block_infos.find(chunk_end_index - 1) != m_block_infos.end(), - "onchain chunk find-received scanning (mock ledger context): block range outside of block ids map (bug)."); + "onchain chunk filter-assist scanning (mock ledger context): block range outside of block ids map (bug)."); // b. prefix block id chunk_context_out.prefix_block_id = @@ -961,7 +955,7 @@ void MockLedgerContext::get_onchain_chunk_sp(const std::uint64_t chunk_start_ind CHECK_AND_ASSERT_THROW_MES(chunk_context_out.block_ids.size() == chunk_end_index - chunk_context_out.start_index, - "onchain chunk find-received scanning (mock ledger context): invalid number of block ids acquired (bug)."); + "onchain chunk filter-assist scanning (mock ledger context): invalid number of block ids acquired (bug)."); /// 3. scan blocks in the chunk range that may contain seraphis enotes or key images @@ -976,16 +970,16 @@ void MockLedgerContext::get_onchain_chunk_sp(const std::uint64_t chunk_start_ind CHECK_AND_ASSERT_THROW_MES(m_blocks_of_sp_tx_output_contents.find(chunk_start_adjusted) != m_blocks_of_sp_tx_output_contents.end(), - "onchain chunk find-received scanning (mock ledger context): start of chunk not known in tx outputs map (bug)."); + "onchain chunk filter-assist scanning (mock ledger context): start of chunk not known in tx outputs map (bug)."); CHECK_AND_ASSERT_THROW_MES(m_blocks_of_sp_tx_output_contents.find(chunk_end_index - 1) != m_blocks_of_sp_tx_output_contents.end(), - "onchain chunk find-received scanning (mock ledger context): end of chunk not known in tx outputs map (bug)."); + "onchain chunk filter-assist scanning (mock ledger context): end of chunk not known in tx outputs map (bug)."); CHECK_AND_ASSERT_THROW_MES(m_blocks_of_tx_key_images.find(chunk_start_adjusted) != m_blocks_of_tx_key_images.end(), - "onchain chunk find-received scanning (mock ledger context): start of chunk not known in key images map (bug)."); + "onchain chunk filter-assist scanning (mock ledger context): start of chunk not known in key images map (bug)."); CHECK_AND_ASSERT_THROW_MES(m_blocks_of_tx_key_images.find(chunk_end_index - 1) != m_blocks_of_tx_key_images.end(), - "onchain chunk find-received scanning (mock ledger context): end of chunk not known in key images map (bug)."); + "onchain chunk filter-assist scanning (mock ledger context): end of chunk not known in key images map (bug)."); // c. initialize output count to the total number of seraphis enotes in the ledger before the first block to scan std::uint64_t total_output_count_before_tx{0}; @@ -994,7 +988,7 @@ void MockLedgerContext::get_onchain_chunk_sp(const std::uint64_t chunk_start_ind { CHECK_AND_ASSERT_THROW_MES(m_accumulated_sp_output_counts.find(chunk_start_adjusted - 1) != m_accumulated_sp_output_counts.end(), - "onchain chunk find-received scanning (mock ledger context): output counts missing a block (bug)."); + "onchain chunk filter-assist scanning (mock ledger context): output counts missing a block (bug)."); total_output_count_before_tx = m_accumulated_sp_output_counts.at(chunk_start_adjusted - 1); } @@ -1014,24 +1008,22 @@ void MockLedgerContext::get_onchain_chunk_sp(const std::uint64_t chunk_start_ind ); } - // e. find-received scan each block in the range - std::list collected_records; - SpContextualKeyImageSetV1 collected_key_images; - + // e. filter-assist scan each block in the range std::for_each( m_blocks_of_sp_tx_output_contents.find(chunk_start_adjusted), m_blocks_of_sp_tx_output_contents.find(chunk_end_index), [&](const auto &block_of_tx_output_contents) { CHECK_AND_ASSERT_THROW_MES(m_block_infos.find(block_of_tx_output_contents.first) != m_block_infos.end(), - "onchain chunk find-received scanning (mock ledger context): block infos map missing index (bug)."); + "onchain chunk filter-assist scanning (mock ledger context): block infos map missing index (bug)."); for (const auto &tx_with_output_contents : block_of_tx_output_contents.second) { const rct::key &tx_id{sortable2rct(tx_with_output_contents.first)}; // if this tx contains at least one view-tag match, then add the tx's key images to the chunk - if (scanning::try_find_sp_enotes_in_tx(xk_find_received, + std::list collected_records; + if (scanning::try_find_sp_enotes_in_tx(d_filter_assist, block_of_tx_output_contents.first, std::get(m_block_infos.at(block_of_tx_output_contents.first)), tx_id, @@ -1051,21 +1043,20 @@ void MockLedgerContext::get_onchain_chunk_sp(const std::uint64_t chunk_start_ind .at(block_of_tx_output_contents.first).find(tx_with_output_contents.first) != m_blocks_of_tx_key_images .at(block_of_tx_output_contents.first).end(), - "onchain chunk find-received scanning (mock ledger context): key image map missing tx " + "onchain chunk filter-assist 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)), - tx_id, - std::get<0>(m_blocks_of_tx_key_images - .at(block_of_tx_output_contents.first) - .at(tx_with_output_contents.first)), - std::get<1>(m_blocks_of_tx_key_images - .at(block_of_tx_output_contents.first) - .at(tx_with_output_contents.first)), - SpEnoteSpentStatus::SPENT_ONCHAIN, - collected_key_images)) - chunk_data_out.contextual_key_images.emplace_back(std::move(collected_key_images)); + scanning::collect_key_images_from_tx(block_of_tx_output_contents.first, + std::get(m_block_infos.at(block_of_tx_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)), + std::get<1>(m_blocks_of_tx_key_images + .at(block_of_tx_output_contents.first) + .at(tx_with_output_contents.first)), + SpEnoteSpentStatus::SPENT_ONCHAIN, + tools::add_element(chunk_data_out.contextual_key_images)); } // add this tx's number of outputs to the total output count diff --git a/src/seraphis_mocks/mock_ledger_context.h b/src/seraphis_mocks/mock_ledger_context.h index dec43a2946..cfa0a35a89 100644 --- a/src/seraphis_mocks/mock_ledger_context.h +++ b/src/seraphis_mocks/mock_ledger_context.h @@ -223,11 +223,11 @@ class MockLedgerContext final */ std::uint64_t pop_blocks(const std::size_t num_blocks); /** - * brief: get_unconfirmed_chunk_sp - try to find-received scan the unconfirmed tx cache - * param: xk_find_received - + * brief: get_unconfirmed_chunk_sp - try to filter-assist scan the unconfirmed tx cache + * param: d_filter_assist - * outparam: chunk_data_out - */ - void get_unconfirmed_chunk_sp(const crypto::x25519_secret_key &xk_find_received, + void get_unconfirmed_chunk_sp(const crypto::x25519_secret_key &d_filter_assist, scanning::ChunkData &chunk_data_out) const; /** * brief: get_onchain_chunk_legacy - legacy view scan a chunk of blocks @@ -249,16 +249,16 @@ class MockLedgerContext final scanning::ChunkContext &chunk_context_out, scanning::ChunkData &chunk_data_out) const; /** - * brief: get_onchain_chunk_sp - find-received scan a chunk of blocks + * brief: get_onchain_chunk_sp - filter-assist scan a chunk of blocks * param: chunk_start_index - * param: chunk_max_size - - * param: xk_find_received - + * param: d_filter_assist - * outparam: chunk_context_out - chunk of scanned blocks (or empty chunk representing top of current chain) * outparam: chunk_data_out - */ void get_onchain_chunk_sp(const std::uint64_t chunk_start_index, const std::uint64_t chunk_max_size, - const crypto::x25519_secret_key &xk_find_received, + const crypto::x25519_secret_key &d_filter_assist, scanning::ChunkContext &chunk_context_out, scanning::ChunkData &chunk_data_out) const; diff --git a/src/seraphis_mocks/mock_offchain_context.cpp b/src/seraphis_mocks/mock_offchain_context.cpp index fb36dbe7b3..0a5a6683a5 100644 --- a/src/seraphis_mocks/mock_offchain_context.cpp +++ b/src/seraphis_mocks/mock_offchain_context.cpp @@ -32,6 +32,7 @@ #include "mock_offchain_context.h" //local headers +#include "common/container_helpers.h" #include "crypto/crypto.h" #include "crypto/x25519.h" #include "misc_log_ex.h" @@ -138,7 +139,7 @@ void MockOffchainContext::clear_cache() m_tx_key_images.clear(); } //------------------------------------------------------------------------------------------------------------------- -void MockOffchainContext::get_offchain_chunk_sp(const crypto::x25519_secret_key &xk_find_received, +void MockOffchainContext::get_offchain_chunk_sp(const crypto::x25519_secret_key &d_filter_assist, scanning::ChunkData &chunk_data_out) const { chunk_data_out.basic_records_per_tx.clear(); @@ -148,16 +149,14 @@ void MockOffchainContext::get_offchain_chunk_sp(const crypto::x25519_secret_key if (m_output_contents.size() == 0) return; - // 2. find-received scan each tx in the unconfirmed chache - std::list collected_records; - SpContextualKeyImageSetV1 collected_key_images; - + // 2. filter-assist scan each tx in the unconfirmed chache for (const auto &tx_with_output_contents : m_output_contents) { const rct::key &tx_id{tx_with_output_contents.first}; //use input context as proxy for tx id // if this tx contains at least one view-tag match, then add the tx's key images to the chunk - if (scanning::try_find_sp_enotes_in_tx(xk_find_received, + std::list collected_records; + if (scanning::try_find_sp_enotes_in_tx(d_filter_assist, -1, -1, tx_id, @@ -172,16 +171,15 @@ void MockOffchainContext::get_offchain_chunk_sp(const crypto::x25519_secret_key .splice(chunk_data_out.basic_records_per_tx[tx_id].end(), collected_records); CHECK_AND_ASSERT_THROW_MES(m_tx_key_images.find(tx_with_output_contents.first) != m_tx_key_images.end(), - "offchain find-received scanning (mock offchain context): key image map missing input context (bug)."); - - if (scanning::try_collect_key_images_from_tx(-1, - -1, - tx_id, - std::get<0>(m_tx_key_images.at(tx_with_output_contents.first)), - std::get<1>(m_tx_key_images.at(tx_with_output_contents.first)), - SpEnoteSpentStatus::SPENT_OFFCHAIN, - collected_key_images)) - chunk_data_out.contextual_key_images.emplace_back(std::move(collected_key_images)); + "offchain filter-assist scanning (mock offchain context): key image map missing input context (bug)."); + + scanning::collect_key_images_from_tx(-1, + -1, + tx_id, + std::get<0>(m_tx_key_images.at(tx_with_output_contents.first)), + std::get<1>(m_tx_key_images.at(tx_with_output_contents.first)), + SpEnoteSpentStatus::SPENT_OFFCHAIN, + tools::add_element(chunk_data_out.contextual_key_images)); } } } diff --git a/src/seraphis_mocks/mock_offchain_context.h b/src/seraphis_mocks/mock_offchain_context.h index 82c02f1d42..cc2c5b21ab 100644 --- a/src/seraphis_mocks/mock_offchain_context.h +++ b/src/seraphis_mocks/mock_offchain_context.h @@ -109,12 +109,12 @@ class MockOffchainContext final */ void clear_cache(); /** - * brief: get_offchain_chunk_sp - find-received scan the offchain tx cache - * param: xk_find_received - + * brief: get_offchain_chunk_sp - filter-assist scan the offchain tx cache + * param: d_filter_assist - * outparam: chunk_data_out - * return: true if chunk is not empty */ - void get_offchain_chunk_sp(const crypto::x25519_secret_key &xk_find_received, + void get_offchain_chunk_sp(const crypto::x25519_secret_key &d_filter_assist, scanning::ChunkData &chunk_data_out) const; private: diff --git a/src/seraphis_mocks/mock_send_receive.cpp b/src/seraphis_mocks/mock_send_receive.cpp index b2636107da..9db94e4f59 100644 --- a/src/seraphis_mocks/mock_send_receive.cpp +++ b/src/seraphis_mocks/mock_send_receive.cpp @@ -80,13 +80,15 @@ namespace mocks void convert_outlay_to_payment_proposal(const rct::xmr_amount outlay_amount, const jamtis::JamtisDestinationV1 &destination, const TxExtra &partial_memo_for_destination, + const std::uint8_t num_primary_view_tag_bits, jamtis::JamtisPaymentProposalV1 &payment_proposal_out) { payment_proposal_out = jamtis::JamtisPaymentProposalV1{ - .destination = destination, - .amount = outlay_amount, - .enote_ephemeral_privkey = crypto::x25519_secret_key_gen(), - .partial_memo = partial_memo_for_destination + .destination = destination, + .amount = outlay_amount, + .enote_ephemeral_privkey = crypto::x25519_secret_key_gen(), + .num_primary_view_tag_bits = num_primary_view_tag_bits, + .partial_memo = partial_memo_for_destination }; } //------------------------------------------------------------------------------------------------------------------- @@ -134,6 +136,7 @@ void send_legacy_coinbase_amounts_to_user(const std::vector &co //------------------------------------------------------------------------------------------------------------------- void send_sp_coinbase_amounts_to_user(const std::vector &coinbase_amounts, const jamtis::JamtisDestinationV1 &user_address, + const std::uint8_t num_primary_view_tag_bits, MockLedgerContext &ledger_context_inout) { // 1. prepare payment proposals @@ -144,9 +147,10 @@ void send_sp_coinbase_amounts_to_user(const std::vector &coinba { // a. make payment proposal convert_outlay_to_payment_proposal(coinbase_amount, - user_address, - TxExtra{}, - tools::add_element(payment_proposals)); + user_address, + TxExtra{}, + num_primary_view_tag_bits, + tools::add_element(payment_proposals)); } // 2. make a coinbase tx @@ -168,6 +172,7 @@ void send_sp_coinbase_amounts_to_user(const std::vector &coinba //------------------------------------------------------------------------------------------------------------------- void send_sp_coinbase_amounts_to_users(const std::vector> &coinbase_amounts_per_user, const std::vector &user_addresses, + const std::uint8_t num_primary_view_tag_bits, MockLedgerContext &ledger_context_inout) { CHECK_AND_ASSERT_THROW_MES(coinbase_amounts_per_user.size() == user_addresses.size(), @@ -185,6 +190,7 @@ void send_sp_coinbase_amounts_to_users(const std::vector> &outlays, + const std::uint8_t num_primary_view_tag_bits, const std::size_t legacy_ring_size, const std::size_t ref_set_decomp_n, const std::size_t ref_set_decomp_m, @@ -239,6 +246,7 @@ void construct_tx_for_mock_ledger_v1(const legacy_mock_keys &local_user_legacy_k convert_outlay_to_payment_proposal(std::get(outlay), std::get(outlay), std::get(outlay), + num_primary_view_tag_bits, tools::add_element(normal_payment_proposals)); } @@ -250,7 +258,6 @@ void construct_tx_for_mock_ledger_v1(const legacy_mock_keys &local_user_legacy_k std::vector selfsend_payment_proposals; //note: no user-defined selfsends DiscretizedFee discretized_transaction_fee; CHECK_AND_ASSERT_THROW_MES(try_prepare_inputs_and_outputs_for_transfer_v1(change_address, - dummy_address, local_user_input_selector, tx_fee_calculator, fee_per_tx_weight, @@ -328,6 +335,7 @@ void construct_tx_for_mock_ledger_v1(const legacy_mock_keys &local_user_legacy_k const rct::xmr_amount fee_per_tx_weight, const std::size_t max_inputs, const std::vector> &outlays, + const std::uint8_t num_primary_view_tag_bits, const std::size_t legacy_ring_size, const std::size_t ref_set_decomp_n, const std::size_t ref_set_decomp_m, @@ -345,6 +353,7 @@ void construct_tx_for_mock_ledger_v1(const legacy_mock_keys &local_user_legacy_k fee_per_tx_weight, max_inputs, outlays, + num_primary_view_tag_bits, legacy_ring_size, ref_set_decomp_n, ref_set_decomp_m, @@ -361,6 +370,7 @@ void transfer_funds_single_mock_v1_unconfirmed_sp_only(const jamtis::mocks::jamt const rct::xmr_amount fee_per_tx_weight, const std::size_t max_inputs, const std::vector> &outlays, + const std::uint8_t num_primary_view_tag_bits, const std::size_t ref_set_decomp_n, const std::size_t ref_set_decomp_m, const SpBinnedReferenceSetConfigV1 &bin_config, @@ -375,6 +385,7 @@ void transfer_funds_single_mock_v1_unconfirmed_sp_only(const jamtis::mocks::jamt fee_per_tx_weight, max_inputs, outlays, + num_primary_view_tag_bits, 0, ref_set_decomp_n, ref_set_decomp_m, @@ -404,6 +415,7 @@ void transfer_funds_single_mock_v1_unconfirmed(const legacy_mock_keys &local_use const rct::xmr_amount fee_per_tx_weight, const std::size_t max_inputs, const std::vector> &outlays, + const std::uint8_t num_primary_view_tag_bits, const std::size_t legacy_ring_size, const std::size_t ref_set_decomp_n, const std::size_t ref_set_decomp_m, @@ -419,6 +431,7 @@ void transfer_funds_single_mock_v1_unconfirmed(const legacy_mock_keys &local_use fee_per_tx_weight, max_inputs, outlays, + num_primary_view_tag_bits, legacy_ring_size, ref_set_decomp_n, ref_set_decomp_m, @@ -448,6 +461,7 @@ void transfer_funds_single_mock_v1(const legacy_mock_keys &local_user_legacy_key const rct::xmr_amount fee_per_tx_weight, const std::size_t max_inputs, const std::vector> &outlays, + const std::uint8_t num_primary_view_tag_bits, const std::size_t legacy_ring_size, const std::size_t ref_set_decomp_n, const std::size_t ref_set_decomp_m, @@ -463,6 +477,7 @@ void transfer_funds_single_mock_v1(const legacy_mock_keys &local_user_legacy_key fee_per_tx_weight, max_inputs, outlays, + num_primary_view_tag_bits, legacy_ring_size, ref_set_decomp_n, ref_set_decomp_m, @@ -550,14 +565,14 @@ void refresh_user_enote_store_PV(const jamtis::mocks::jamtis_mock_keys &user_key const MockLedgerContext &ledger_context, SpEnoteStorePaymentValidator &user_enote_store_inout) { - const EnoteFindingContextUnconfirmedMockSp enote_finding_context_unconfirmed{ledger_context, user_keys.xk_fr}; - const EnoteFindingContextLedgerMockSp enote_finding_context_ledger{ledger_context, user_keys.xk_fr}; + const EnoteFindingContextUnconfirmedMockSp enote_finding_context_unconfirmed{ledger_context, user_keys.d_fa}; + const EnoteFindingContextLedgerMockSp enote_finding_context_ledger{ledger_context, user_keys.d_fa}; scanning::ScanContextNonLedgerSimple scan_context_unconfirmed{enote_finding_context_unconfirmed}; scanning::ScanContextLedgerSimple scan_context_ledger{enote_finding_context_ledger}; ChunkConsumerMockSpIntermediate chunk_consumer{ - user_keys.K_1_base, - user_keys.xk_ua, - user_keys.xk_fr, + user_keys.K_s_base, + user_keys.d_vr, + user_keys.d_fa, user_keys.s_ga, user_enote_store_inout }; @@ -573,11 +588,11 @@ void refresh_user_enote_store(const jamtis::mocks::jamtis_mock_keys &user_keys, const MockLedgerContext &ledger_context, SpEnoteStore &user_enote_store_inout) { - const EnoteFindingContextUnconfirmedMockSp enote_finding_context_unconfirmed{ledger_context, user_keys.xk_fr}; - const EnoteFindingContextLedgerMockSp enote_finding_context_ledger{ledger_context, user_keys.xk_fr}; + const EnoteFindingContextUnconfirmedMockSp enote_finding_context_unconfirmed{ledger_context, user_keys.d_fa}; + const EnoteFindingContextLedgerMockSp enote_finding_context_ledger{ledger_context, user_keys.d_fa}; scanning::ScanContextNonLedgerSimple scan_context_unconfirmed{enote_finding_context_unconfirmed}; scanning::ScanContextLedgerSimple scan_context_ledger{enote_finding_context_ledger}; - ChunkConsumerMockSp chunk_consumer{user_keys.K_1_base, user_keys.k_vb, user_enote_store_inout}; + ChunkConsumerMockSp chunk_consumer{user_keys.K_s_base, user_keys.k_vb, user_enote_store_inout}; sp::refresh_enote_store(refresh_config, scan_context_unconfirmed, diff --git a/src/seraphis_mocks/mock_send_receive.h b/src/seraphis_mocks/mock_send_receive.h index 6623d6e3d7..ce13f1b352 100644 --- a/src/seraphis_mocks/mock_send_receive.h +++ b/src/seraphis_mocks/mock_send_receive.h @@ -71,6 +71,7 @@ namespace mocks void convert_outlay_to_payment_proposal(const rct::xmr_amount outlay_amount, const jamtis::JamtisDestinationV1 &destination, const TxExtra &partial_memo_for_destination, + const std::uint8_t num_primary_view_tag_bits, jamtis::JamtisPaymentProposalV1 &payment_proposal_out); /// send funds as coinbase enotes void send_legacy_coinbase_amounts_to_user(const std::vector &coinbase_amounts, @@ -79,9 +80,11 @@ void send_legacy_coinbase_amounts_to_user(const std::vector &co MockLedgerContext &ledger_context_inout); void send_sp_coinbase_amounts_to_user(const std::vector &coinbase_amounts, const jamtis::JamtisDestinationV1 &user_address, + const std::uint8_t num_primary_view_tag_bits, MockLedgerContext &ledger_context_inout); void send_sp_coinbase_amounts_to_users(const std::vector> &coinbase_amounts_per_user, const std::vector &user_addresses, + const std::uint8_t num_primary_view_tag_bits, MockLedgerContext &ledger_context_inout); /// create a seraphis transaction void construct_tx_for_mock_ledger_v1(const legacy_mock_keys &local_user_legacy_keys, @@ -91,6 +94,7 @@ void construct_tx_for_mock_ledger_v1(const legacy_mock_keys &local_user_legacy_k const rct::xmr_amount fee_per_tx_weight, const std::size_t max_inputs, const std::vector> &outlays, + const std::uint8_t num_primary_view_tag_bits, const std::size_t legacy_ring_size, const std::size_t ref_set_decomp_n, const std::size_t ref_set_decomp_m, @@ -106,6 +110,7 @@ void construct_tx_for_mock_ledger_v1(const legacy_mock_keys &local_user_legacy_k const rct::xmr_amount fee_per_tx_weight, const std::size_t max_inputs, const std::vector> &outlays, + const std::uint8_t num_primary_view_tag_bits, const std::size_t legacy_ring_size, const std::size_t ref_set_decomp_n, const std::size_t ref_set_decomp_m, @@ -119,6 +124,7 @@ void transfer_funds_single_mock_v1_unconfirmed_sp_only(const jamtis::mocks::jamt const rct::xmr_amount fee_per_tx_weight, const std::size_t max_inputs, const std::vector> &outlays, + const std::uint8_t num_primary_view_tag_bits, const std::size_t ref_set_decomp_n, const std::size_t ref_set_decomp_m, const SpBinnedReferenceSetConfigV1 &bin_config, @@ -130,6 +136,7 @@ void transfer_funds_single_mock_v1_unconfirmed(const legacy_mock_keys &local_use const rct::xmr_amount fee_per_tx_weight, const std::size_t max_inputs, const std::vector> &outlays, + const std::uint8_t num_primary_view_tag_bits, const std::size_t legacy_ring_size, const std::size_t ref_set_decomp_n, const std::size_t ref_set_decomp_m, @@ -142,6 +149,7 @@ void transfer_funds_single_mock_v1(const legacy_mock_keys &local_user_legacy_key const rct::xmr_amount fee_per_tx_weight, const std::size_t max_inputs, const std::vector> &outlays, + const std::uint8_t num_primary_view_tag_bits, const std::size_t legacy_ring_size, const std::size_t ref_set_decomp_n, const std::size_t ref_set_decomp_m, diff --git a/src/seraphis_mocks/mock_tx_builders_outputs.cpp b/src/seraphis_mocks/mock_tx_builders_outputs.cpp index 1654fadf83..e354022869 100644 --- a/src/seraphis_mocks/mock_tx_builders_outputs.cpp +++ b/src/seraphis_mocks/mock_tx_builders_outputs.cpp @@ -52,6 +52,7 @@ namespace mocks //------------------------------------------------------------------------------------------------------------------- std::vector gen_mock_sp_coinbase_output_proposals_v1( const std::vector &out_amounts, + const std::uint8_t num_primary_view_tag_bits, const std::size_t num_random_memo_elements) { // 1. generate random output proposals @@ -59,7 +60,9 @@ std::vector gen_mock_sp_coinbase_output_proposals_v1 output_proposals.reserve(out_amounts.size()); for (const rct::xmr_amount out_amount : out_amounts) - output_proposals.emplace_back(gen_sp_coinbase_output_proposal_v1(out_amount, num_random_memo_elements)); + output_proposals.emplace_back(gen_sp_coinbase_output_proposal_v1(out_amount, + num_primary_view_tag_bits, + num_random_memo_elements)); // 2. sort them std::sort(output_proposals.begin(), @@ -70,6 +73,7 @@ std::vector gen_mock_sp_coinbase_output_proposals_v1 } //------------------------------------------------------------------------------------------------------------------- std::vector gen_mock_sp_output_proposals_v1(const std::vector &out_amounts, + const std::uint8_t num_primary_view_tag_bits, const std::size_t num_random_memo_elements) { // 1. generate random output proposals @@ -77,7 +81,9 @@ std::vector gen_mock_sp_output_proposals_v1(const std::vecto output_proposals.reserve(out_amounts.size()); for (const rct::xmr_amount out_amount : out_amounts) - output_proposals.emplace_back(gen_sp_output_proposal_v1(out_amount, num_random_memo_elements)); + output_proposals.emplace_back(gen_sp_output_proposal_v1(out_amount, + num_primary_view_tag_bits, + num_random_memo_elements)); // 2. sort them std::sort(output_proposals.begin(), output_proposals.end(), tools::compare_func(compare_Ko)); diff --git a/src/seraphis_mocks/mock_tx_builders_outputs.h b/src/seraphis_mocks/mock_tx_builders_outputs.h index 6c64c0f6de..964483e134 100644 --- a/src/seraphis_mocks/mock_tx_builders_outputs.h +++ b/src/seraphis_mocks/mock_tx_builders_outputs.h @@ -52,19 +52,23 @@ namespace mocks /** * brief: gen_mock_sp_coinbase_output_proposals_v1 - create random coinbase output proposals * param: out_amounts - +* param: num_primary_view_tag_bits - * param: num_random_memo_elements - * return: set of generated output proposals */ std::vector gen_mock_sp_coinbase_output_proposals_v1( const std::vector &out_amounts, + const std::uint8_t num_primary_view_tag_bits, const std::size_t num_random_memo_elements); /** * brief: gen_mock_sp_output_proposals_v1 - create random output proposals * param: out_amounts - +* param: num_primary_view_tag_bits - * param: num_random_memo_elements - * return: set of generated output proposals */ std::vector gen_mock_sp_output_proposals_v1(const std::vector &out_amounts, + const std::uint8_t num_primary_view_tag_bits, const std::size_t num_random_memo_elements); } //namespace mocks diff --git a/src/seraphis_mocks/scan_chunk_consumer_mocks.cpp b/src/seraphis_mocks/scan_chunk_consumer_mocks.cpp index e164c95ec1..be9e7ed617 100644 --- a/src/seraphis_mocks/scan_chunk_consumer_mocks.cpp +++ b/src/seraphis_mocks/scan_chunk_consumer_mocks.cpp @@ -36,7 +36,7 @@ #include "crypto/x25519.h" #include "enote_finding_context_mocks.h" #include "ringct/rctTypes.h" -#include "seraphis_core/jamtis_core_utils.h" +#include "seraphis_core/jamtis_account_secrets.h" #include "seraphis_impl/enote_store.h" #include "seraphis_impl/enote_store_utils.h" #include "seraphis_main/enote_record_types.h" @@ -273,13 +273,13 @@ void ChunkConsumerMockLegacy::consume_onchain_chunk(const scanning::LedgerChunk // Seraphis Intermediate //------------------------------------------------------------------------------------------------------------------- ChunkConsumerMockSpIntermediate::ChunkConsumerMockSpIntermediate(const rct::key &jamtis_spend_pubkey, - const crypto::x25519_secret_key &xk_unlock_amounts, - const crypto::x25519_secret_key &xk_find_received, + const crypto::x25519_secret_key &d_view_received, + const crypto::x25519_secret_key &d_filter_assist, const crypto::secret_key &s_generate_address, SpEnoteStorePaymentValidator &enote_store) : m_jamtis_spend_pubkey{jamtis_spend_pubkey}, - m_xk_unlock_amounts{xk_unlock_amounts}, - m_xk_find_received{xk_find_received}, + m_d_view_received{d_view_received}, + m_d_filter_assist{d_filter_assist}, m_s_generate_address{s_generate_address}, m_enote_store{enote_store} { @@ -315,8 +315,8 @@ void ChunkConsumerMockSpIntermediate::consume_nonledger_chunk(const SpEnoteOrigi std::unordered_map found_enote_records; scanning::process_chunk_intermediate_sp(m_jamtis_spend_pubkey, - m_xk_unlock_amounts, - m_xk_find_received, + m_d_view_received, + m_d_filter_assist, m_s_generate_address, *m_cipher_context, chunk_data.basic_records_per_tx, @@ -340,8 +340,8 @@ void ChunkConsumerMockSpIntermediate::consume_onchain_chunk(const scanning::Ledg std::unordered_map found_enote_records; scanning::process_chunk_intermediate_sp(m_jamtis_spend_pubkey, - m_xk_unlock_amounts, - m_xk_find_received, + m_d_view_received, + m_d_filter_assist, m_s_generate_address, *m_cipher_context, chunk_data->basic_records_per_tx, @@ -365,9 +365,9 @@ ChunkConsumerMockSp::ChunkConsumerMockSp(const rct::key &jamtis_spend_pubkey, m_k_view_balance{k_view_balance}, m_enote_store{enote_store} { - jamtis::make_jamtis_unlockamounts_key(m_k_view_balance, m_xk_unlock_amounts); - jamtis::make_jamtis_findreceived_key(m_k_view_balance, m_xk_find_received); - jamtis::make_jamtis_generateaddress_secret(m_k_view_balance, m_s_generate_address); + jamtis::make_jamtis_viewreceived_key(m_k_view_balance, m_d_view_received); + jamtis::make_jamtis_filterassist_key(m_d_view_received, m_d_filter_assist); + jamtis::make_jamtis_generateaddress_secret(m_d_view_received, m_s_generate_address); jamtis::make_jamtis_ciphertag_secret(m_s_generate_address, m_s_cipher_tag); m_cipher_context = std::make_unique(m_s_cipher_tag); @@ -398,30 +398,36 @@ void ChunkConsumerMockSp::consume_nonledger_chunk(const SpEnoteOriginStatus nonl { // 1. process the chunk std::unordered_map found_enote_records; - std::unordered_map found_spent_key_images; + std::unordered_map sp_key_images_in_sp_selfsends; std::unordered_map legacy_key_images_in_sp_selfsends; scanning::process_chunk_full_sp(m_jamtis_spend_pubkey, m_k_view_balance, - m_xk_unlock_amounts, - m_xk_find_received, + m_d_view_received, + m_d_filter_assist, m_s_generate_address, *m_cipher_context, - [this](const crypto::key_image &key_image) -> bool - { - return this->m_enote_store.has_enote_with_key_image(key_image); - }, chunk_data.basic_records_per_tx, chunk_data.contextual_key_images, found_enote_records, - found_spent_key_images, + sp_key_images_in_sp_selfsends, legacy_key_images_in_sp_selfsends); - // 2. save the results + // 2. filter out key images in self send seraphis transactions which aren't "known" + auto sp_ki_it = sp_key_images_in_sp_selfsends.begin(); + while (sp_ki_it != sp_key_images_in_sp_selfsends.end()) + { + if (!m_enote_store.has_enote_with_key_image(sp_ki_it->first)) + sp_ki_it = sp_key_images_in_sp_selfsends.erase(sp_ki_it); + else + ++sp_ki_it; + } + + // 3. save the results std::list events; m_enote_store.update_with_sp_records_from_nonledger(nonledger_origin_status, found_enote_records, - found_spent_key_images, + sp_key_images_in_sp_selfsends, legacy_key_images_in_sp_selfsends, events); } @@ -437,32 +443,38 @@ void ChunkConsumerMockSp::consume_onchain_chunk(const scanning::LedgerChunk &chu // 2. process the chunk std::unordered_map found_enote_records; - std::unordered_map found_spent_key_images; + std::unordered_map sp_key_images_in_sp_selfsends; std::unordered_map legacy_key_images_in_sp_selfsends; scanning::process_chunk_full_sp(m_jamtis_spend_pubkey, m_k_view_balance, - m_xk_unlock_amounts, - m_xk_find_received, + m_d_view_received, + m_d_filter_assist, m_s_generate_address, *m_cipher_context, - [this](const crypto::key_image &key_image) -> bool - { - return this->m_enote_store.has_enote_with_key_image(key_image); - }, chunk_data->basic_records_per_tx, chunk_data->contextual_key_images, found_enote_records, - found_spent_key_images, + sp_key_images_in_sp_selfsends, legacy_key_images_in_sp_selfsends); - // 2. save the results + // 2. filter out key images in self send seraphis transactions which aren't "known" + auto sp_ki_it = sp_key_images_in_sp_selfsends.begin(); + while (sp_ki_it != sp_key_images_in_sp_selfsends.end()) + { + if (!m_enote_store.has_enote_with_key_image(sp_ki_it->first)) + sp_ki_it = sp_key_images_in_sp_selfsends.erase(sp_ki_it); + else + ++sp_ki_it; + } + + // 3. save the results std::list events; m_enote_store.update_with_sp_records_from_ledger(alignment_block_id, first_new_block, new_block_ids, found_enote_records, - found_spent_key_images, + sp_key_images_in_sp_selfsends, legacy_key_images_in_sp_selfsends, events); } diff --git a/src/seraphis_mocks/scan_chunk_consumer_mocks.h b/src/seraphis_mocks/scan_chunk_consumer_mocks.h index 0683e2da8e..8ce8ed5397 100644 --- a/src/seraphis_mocks/scan_chunk_consumer_mocks.h +++ b/src/seraphis_mocks/scan_chunk_consumer_mocks.h @@ -159,8 +159,8 @@ class ChunkConsumerMockSpIntermediate final : public scanning::ChunkConsumer //constructors /// normal constructor ChunkConsumerMockSpIntermediate(const rct::key &jamtis_spend_pubkey, - const crypto::x25519_secret_key &xk_unlock_amounts, - const crypto::x25519_secret_key &xk_find_received, + const crypto::x25519_secret_key &d_view_received, + const crypto::x25519_secret_key &d_filter_assist, const crypto::secret_key &s_generate_address, SpEnoteStorePaymentValidator &enote_store); @@ -189,8 +189,8 @@ class ChunkConsumerMockSpIntermediate final : public scanning::ChunkConsumer //member variables private: const rct::key &m_jamtis_spend_pubkey; - const crypto::x25519_secret_key &m_xk_unlock_amounts; - const crypto::x25519_secret_key &m_xk_find_received; + const crypto::x25519_secret_key &m_d_view_received; + const crypto::x25519_secret_key &m_d_filter_assist; const crypto::secret_key &m_s_generate_address; SpEnoteStorePaymentValidator &m_enote_store; @@ -235,8 +235,8 @@ class ChunkConsumerMockSp final : public scanning::ChunkConsumer const crypto::secret_key &m_k_view_balance; SpEnoteStore &m_enote_store; - crypto::x25519_secret_key m_xk_unlock_amounts; - crypto::x25519_secret_key m_xk_find_received; + crypto::x25519_secret_key m_d_view_received; + crypto::x25519_secret_key m_d_filter_assist; crypto::secret_key m_s_generate_address; crypto::secret_key m_s_cipher_tag; std::unique_ptr m_cipher_context; diff --git a/tests/performance_tests/main.cpp b/tests/performance_tests/main.cpp index 7cc9bef705..91bfbbe459 100644 --- a/tests/performance_tests/main.cpp +++ b/tests/performance_tests/main.cpp @@ -278,8 +278,6 @@ int main(int argc, char** argv) p_address_tag_decipher.core_params = p.core_params; p_address_tag_decipher.mode = AddressTagDecipherModes::ALL_SUCCESSFUL_DECIPHER; TEST_PERFORMANCE0(filter, p_address_tag_decipher, test_jamtis_address_tag_decipher_sp); - p_address_tag_decipher.mode = AddressTagDecipherModes::NO_SUCCESSFUL_DECIPHER; - TEST_PERFORMANCE0(filter, p_address_tag_decipher, test_jamtis_address_tag_decipher_sp); // test client-side scanning in a seraphis remote-scanning workflow ParamsShuttleScannerClient p_client_scan; diff --git a/tests/performance_tests/view_scan.h b/tests/performance_tests/view_scan.h index 40908aafbf..02320f82d7 100644 --- a/tests/performance_tests/view_scan.h +++ b/tests/performance_tests/view_scan.h @@ -35,7 +35,6 @@ #include "ringct/rctOps.h" #include "ringct/rctTypes.h" #include "seraphis_core/jamtis_address_tag_utils.h" -#include "seraphis_core/jamtis_core_utils.h" #include "seraphis_core/jamtis_destination.h" #include "seraphis_core/jamtis_enote_utils.h" #include "seraphis_core/jamtis_payment_proposal.h" @@ -205,6 +204,7 @@ class test_view_scan_sp { public: static const size_t loop_count = 1000; + static const uint8_t num_primary_view_tag_bits = 8; bool init(const ParamsShuttleViewScan ¶ms) { @@ -217,10 +217,7 @@ class test_view_scan_sp sp::jamtis::JamtisDestinationV1 user_address; sp::jamtis::address_index_t j{}; //address 0 - sp::jamtis::make_jamtis_destination_v1(m_keys.K_1_base, - m_keys.xK_ua, - m_keys.xK_fr, - m_keys.s_ga, + sp::jamtis::mocks::make_address_for_user(m_keys, j, user_address); @@ -234,7 +231,11 @@ class test_view_scan_sp // invalidate the view tag to test the performance of short-circuiting on failed view tags if (m_test_view_tag_check) - ++m_enote.view_tag; + { + const sp::jamtis::view_tag_t og_view_tag = m_enote.view_tag; + while (m_enote.view_tag == og_view_tag) + m_enote.view_tag = sp::jamtis::gen_view_tag(); + } return true; } @@ -246,8 +247,9 @@ class test_view_scan_sp sp::SpBasicEnoteRecordV1 basic_enote_record; if (!sp::try_get_basic_enote_record_v1(m_enote, m_enote_ephemeral_pubkey, + num_primary_view_tag_bits, rct::zero(), - m_keys.xk_fr, + m_keys.d_fa, basic_enote_record)) return m_test_view_tag_check; //note: this branch is only valid if trying to trigger the view tag check @@ -290,7 +292,8 @@ struct ParamsShuttleScannerClient final : public ParamsShuttle class test_remote_scanner_client_scan_sp { public: - static const size_t num_records = sp::math::uint_pow(2, sp::jamtis::ADDRESS_TAG_HINT_BYTES * 8); + static const uint8_t num_primary_view_tag_bits = 8; + static const size_t num_records = 1 << 16; static const size_t loop_count = 256000 / num_records + 20; bool init(const ParamsShuttleScannerClient ¶ms) @@ -304,10 +307,7 @@ class test_remote_scanner_client_scan_sp sp::jamtis::JamtisDestinationV1 user_address; m_real_address_index = sp::jamtis::address_index_t{}; //address 0 - sp::jamtis::make_jamtis_destination_v1(m_keys.K_1_base, - m_keys.xK_ua, - m_keys.xK_fr, - m_keys.s_ga, + sp::jamtis::mocks::make_address_for_user(m_keys, m_real_address_index, user_address); @@ -316,7 +316,10 @@ class test_remote_scanner_client_scan_sp // make enote paying to address const crypto::x25519_secret_key enote_privkey{crypto::x25519_secret_key_gen()}; - const sp::jamtis::JamtisPaymentProposalV1 payment_proposal{user_address, rct::xmr_amount{0}, enote_privkey}; + const sp::jamtis::JamtisPaymentProposalV1 payment_proposal{user_address, + rct::xmr_amount{0}, + enote_privkey, + num_primary_view_tag_bits}; sp::SpOutputProposalV1 output_proposal; make_v1_output_proposal_v1(payment_proposal, rct::zero(), output_proposal); sp::SpEnoteV1 real_enote; @@ -326,8 +329,9 @@ class test_remote_scanner_client_scan_sp sp::SpBasicEnoteRecordV1 basic_record; if (!sp::try_get_basic_enote_record_v1(real_enote, output_proposal.enote_ephemeral_pubkey, + num_primary_view_tag_bits, rct::zero(), - m_keys.xk_fr, + m_keys.d_fa, basic_record)) return false; @@ -354,15 +358,13 @@ class test_remote_scanner_client_scan_sp continue; } - // mangle the address tag + // mangle the view tag complementary bits so that the enote doesn't scan // - re-do the fake ones if they succeed by accident - sp::jamtis::address_index_t j_temp; - do - { - sp::jamtis::gen_address_tag(m_basic_records.back().nominal_address_tag); - } while(sp::jamtis::try_decipher_address_index(*m_cipher_context, - m_basic_records.back().nominal_address_tag, - j_temp)); + sp::jamtis::view_tag_t &last_view_tag = + m_basic_records.back().enote.unwrap().view_tag; + const sp::jamtis::view_tag_t og_last_view_tag = last_view_tag; + while (last_view_tag == og_last_view_tag) + last_view_tag = sp::jamtis::gen_view_tag(); } return true; @@ -380,11 +382,11 @@ class test_remote_scanner_client_scan_sp for (std::size_t record_index{0}; record_index < m_basic_records.size(); ++record_index) { const bool result{ - try_get_enote_record_v1_plain(m_basic_records[record_index], - m_keys.K_1_base, + try_get_enote_record_plain_v1(m_basic_records[record_index], + m_keys.K_s_base, m_keys.k_vb, - m_keys.xk_ua, - m_keys.xk_fr, + m_keys.d_vr, + m_keys.d_fa, m_keys.s_ga, *m_cipher_context, enote_record) @@ -421,8 +423,7 @@ class test_remote_scanner_client_scan_sp enum class AddressTagDecipherModes { - ALL_SUCCESSFUL_DECIPHER, - NO_SUCCESSFUL_DECIPHER + ALL_SUCCESSFUL_DECIPHER }; struct ParamsShuttleAddressTagDecipher final : public ParamsShuttle @@ -450,21 +451,9 @@ class test_jamtis_address_tag_decipher_sp for (sp::jamtis::address_tag_t &addr_tag : m_address_tags) { - if (params.mode == AddressTagDecipherModes::NO_SUCCESSFUL_DECIPHER) - { - do - { - address_index_temp = sp::jamtis::gen_address_index(); - addr_tag = sp::jamtis::make_address_tag(address_index_temp, sp::jamtis::address_tag_hint_t{}); - } - while (sp::jamtis::try_decipher_address_index(*m_cipher_context, addr_tag, address_index_temp)); - } - else - { - address_index_temp = sp::jamtis::gen_address_index(); + address_index_temp = sp::jamtis::gen_address_index(); - addr_tag = sp::jamtis::cipher_address_index(*m_cipher_context, address_index_temp); - } + addr_tag = sp::jamtis::cipher_address_index(*m_cipher_context, address_index_temp); } return true; @@ -479,7 +468,7 @@ class test_jamtis_address_tag_decipher_sp sp::jamtis::address_index_t address_index_temp; for (const sp::jamtis::address_tag_t &addr_tag : m_address_tags) - sp::jamtis::try_decipher_address_index(*m_cipher_context, addr_tag, address_index_temp); + sp::jamtis::decipher_address_index(*m_cipher_context, addr_tag, address_index_temp); return true; } diff --git a/tests/unit_tests/seraphis_basic.cpp b/tests/unit_tests/seraphis_basic.cpp index 5372a619e9..5a67e7450b 100644 --- a/tests/unit_tests/seraphis_basic.cpp +++ b/tests/unit_tests/seraphis_basic.cpp @@ -40,9 +40,9 @@ extern "C" #include "seraphis_core/binned_reference_set.h" #include "seraphis_core/binned_reference_set_utils.h" #include "seraphis_core/discretized_fee.h" +#include "seraphis_core/jamtis_account_secrets.h" #include "seraphis_core/jamtis_address_tag_utils.h" #include "seraphis_core/jamtis_address_utils.h" -#include "seraphis_core/jamtis_core_utils.h" #include "seraphis_core/jamtis_destination.h" #include "seraphis_core/jamtis_enote_utils.h" #include "seraphis_core/jamtis_payment_proposal.h" @@ -104,6 +104,7 @@ static void make_secret_key(crypto::x25519_secret_key &skey_out) //------------------------------------------------------------------------------------------------------------------- static void check_is_owned_with_intermediate_record(const SpEnoteVariant &enote, const crypto::x25519_pubkey &enote_ephemeral_pubkey, + const std::uint8_t num_primary_view_tag_bits, const rct::key &input_context, const jamtis_mock_keys &keys, const address_index_t &j_expected, @@ -112,12 +113,13 @@ static void check_is_owned_with_intermediate_record(const SpEnoteVariant &enote, // try to extract intermediate information from the enote // - only succeeds if enote is owned and is a plain jamtis enote SpIntermediateEnoteRecordV1 intermediate_enote_record; - EXPECT_TRUE(try_get_intermediate_enote_record_v1(enote, + ASSERT_TRUE(try_get_intermediate_enote_record_v1(enote, enote_ephemeral_pubkey, + num_primary_view_tag_bits, input_context, - keys.K_1_base, - keys.xk_ua, - keys.xk_fr, + keys.K_s_base, + keys.d_vr, + keys.d_fa, keys.s_ga, intermediate_enote_record)); @@ -127,7 +129,7 @@ static void check_is_owned_with_intermediate_record(const SpEnoteVariant &enote, // get full enote record from intermediate record SpEnoteRecordV1 enote_record; - EXPECT_TRUE(try_get_enote_record_v1_plain(intermediate_enote_record, keys.K_1_base, keys.k_vb, enote_record)); + EXPECT_TRUE(try_get_enote_record_v1(intermediate_enote_record, keys.K_s_base, keys.k_vb, enote_record)); // check misc fields EXPECT_TRUE(enote_record.type == JamtisEnoteType::PLAIN); @@ -135,7 +137,7 @@ static void check_is_owned_with_intermediate_record(const SpEnoteVariant &enote, EXPECT_TRUE(enote_record.address_index == j_expected); // check key image - rct::key spendkey_U_component{keys.K_1_base}; + rct::key spendkey_U_component{keys.K_s_base}; reduce_seraphis_spendkey_x(keys.k_vb, spendkey_U_component); extend_seraphis_spendkey_u(enote_record.enote_view_extension_u, spendkey_U_component); crypto::key_image reproduced_key_image; @@ -148,6 +150,7 @@ static void check_is_owned_with_intermediate_record(const SpEnoteVariant &enote, //------------------------------------------------------------------------------------------------------------------- static void check_is_owned(const SpEnoteVariant &enote, const crypto::x25519_pubkey &enote_ephemeral_pubkey, + const std::uint8_t num_primary_view_tag_bits, const rct::key &input_context, const jamtis_mock_keys &keys, const address_index_t &j_expected, @@ -156,26 +159,27 @@ static void check_is_owned(const SpEnoteVariant &enote, { // try to extract information from the enote (only succeeds if enote is owned) SpEnoteRecordV1 enote_record; - EXPECT_TRUE(try_get_enote_record_v1(enote, + ASSERT_TRUE(try_get_enote_record_v1(enote, enote_ephemeral_pubkey, + num_primary_view_tag_bits, input_context, - keys.K_1_base, + keys.K_s_base, keys.k_vb, enote_record)); // check misc fields - EXPECT_TRUE(enote_record.type == type_expected); - EXPECT_TRUE(enote_record.amount == amount_expected); - EXPECT_TRUE(enote_record.address_index == j_expected); + EXPECT_EQ(type_expected, enote_record.type); + EXPECT_EQ(amount_expected, enote_record.amount); + EXPECT_EQ(j_expected, enote_record.address_index); // check onetime address can be recomputed from the enote record rct::key recipient_address_spend_key; - make_jamtis_address_spend_key(keys.K_1_base, keys.s_ga, j_expected, recipient_address_spend_key); + make_jamtis_address_spend_key(keys.K_s_base, keys.s_ga, j_expected, recipient_address_spend_key); rct::key sender_receiver_secret; if (enote_record.type == JamtisEnoteType::PLAIN) { - make_jamtis_sender_receiver_secret_plain(keys.xk_fr, + make_jamtis_sender_receiver_secret_plain(keys.d_vr, enote_record.enote_ephemeral_pubkey, enote_record.enote_ephemeral_pubkey, enote_record.input_context, @@ -189,7 +193,7 @@ static void check_is_owned(const SpEnoteVariant &enote, make_jamtis_sender_receiver_secret_selfsend(keys.k_vb, enote_record.enote_ephemeral_pubkey, enote_record.input_context, - selfsend_type, + is_jamtis_auxiliary_selfsend_type(selfsend_type), sender_receiver_secret); } @@ -199,7 +203,7 @@ static void check_is_owned(const SpEnoteVariant &enote, onetime_address_ref(enote))); // check key image - rct::key spendkey_U_component{keys.K_1_base}; + rct::key spendkey_U_component{keys.K_s_base}; reduce_seraphis_spendkey_x(keys.k_vb, spendkey_U_component); extend_seraphis_spendkey_u(enote_record.enote_view_extension_u, spendkey_U_component); crypto::key_image reproduced_key_image; @@ -213,6 +217,7 @@ static void check_is_owned(const SpEnoteVariant &enote, { check_is_owned_with_intermediate_record(enote, enote_ephemeral_pubkey, + num_primary_view_tag_bits, input_context, keys, j_expected, @@ -235,6 +240,7 @@ static void check_is_owned(const SpCoinbaseOutputProposalV1 &test_proposal, // check info check_is_owned(test_proposal.enote, test_proposal.enote_ephemeral_pubkey, + test_proposal.num_primary_view_tag_bits, input_context, keys, j_expected, @@ -256,6 +262,7 @@ static void check_is_owned(const SpOutputProposalV1 &test_proposal, // check info check_is_owned(enote, test_proposal.enote_ephemeral_pubkey, + test_proposal.num_primary_view_tag_bits, rct::zero(), keys, j_expected, @@ -349,6 +356,7 @@ static void make_sp_txtype_squashed_v1(const std::size_t legacy_ring_size, const std::size_t ref_set_decomp_n, const std::size_t ref_set_decomp_m, const SpBinnedReferenceSetConfigV1 &bin_config, + const std::uint8_t num_primary_view_tag_bits, const std::size_t num_random_memo_elements, const std::vector &in_legacy_amounts, const std::vector &in_sp_amounts, @@ -390,7 +398,9 @@ static void make_sp_txtype_squashed_v1(const std::size_t legacy_ring_size, // make mock output proposals std::vector output_proposals{ - gen_mock_sp_output_proposals_v1(out_amounts, num_random_memo_elements) + gen_mock_sp_output_proposals_v1(out_amounts, + num_primary_view_tag_bits, + num_random_memo_elements) }; // for 2-out txs, can only have one unique enote ephemeral pubkey @@ -446,6 +456,8 @@ static void make_sp_txtype_squashed_v1(const std::size_t legacy_ring_size, output_amounts, output_amount_commitment_blinding_factors, tx_supplement.output_enote_ephemeral_pubkeys); + tx_supplement.num_primary_view_tag_bits = get_shared_num_primary_view_tag_bits({}, {}, {}, + output_proposals); for (const SpOutputProposalV1 &output_proposal : output_proposals) accumulate_extra_field_elements(output_proposal.partial_memo, additional_memo_elements); make_tx_extra(std::move(additional_memo_elements), tx_supplement.tx_extra); @@ -519,8 +531,8 @@ static bool test_info_recovery_addressindex(const address_index_t &j) make_secret_key(cipher_key); const address_tag_t address_tag{cipher_address_index(cipher_key, j)}; address_index_t decipher_j; - if (!try_decipher_address_index(cipher_key, address_tag, decipher_j)) - return false; + decipher_address_index(cipher_key, address_tag, decipher_j); + if (decipher_j != j) return false; @@ -593,32 +605,6 @@ TEST(seraphis_basic, information_recovery_amountencoding) EXPECT_TRUE(decoded_amount == amount); } //------------------------------------------------------------------------------------------------------------------- -TEST(seraphis_basic, information_recovery_jamtisaddresstaghint) -{ - // cipher an index - const address_index_t j{gen_address_index()}; - crypto::secret_key cipher_key; - make_secret_key(cipher_key); - const address_tag_t address_tag{cipher_address_index(cipher_key, j)}; - - // split the tag into encrypted index and tag hint - address_index_t enc_j; - address_tag_hint_t hint; - memcpy(enc_j.bytes, address_tag.bytes, sizeof(address_index_t)); - memcpy(hint.bytes, address_tag.bytes + sizeof(address_index_t), sizeof(address_tag_hint_t)); - - // make a tag hint using SpKDFTranscript: H_2(k, cipher[k](j)) - SpKDFTranscript transcript{config::HASH_KEY_JAMTIS_ADDRESS_TAG_HINT, sizeof(rct::key) + sizeof(address_index_t)}; - transcript.append("cipher_key", cipher_key); - transcript.append("enc_j", enc_j.bytes); - - address_tag_hint_t reconstructed_hint; - sp_hash_to_2(transcript.data(), transcript.size(), reconstructed_hint.bytes); - - // verify that the hint can be reproduced using the SpKDFTranscript utility - ASSERT_TRUE(memcmp(hint.bytes, reconstructed_hint.bytes, sizeof(address_tag_hint_t)) == 0); -} -//------------------------------------------------------------------------------------------------------------------- TEST(seraphis_basic, information_recovery_addressindex) { // test address indices @@ -642,13 +628,14 @@ TEST(seraphis_basic, information_recovery_jamtisdestination) // test making a jamtis destination then recovering the index JamtisDestinationV1 destination_known; const address_index_t j{gen_address_index()}; - make_jamtis_destination_v1(keys.K_1_base, keys.xK_ua, keys.xK_fr, keys.s_ga, j, destination_known); + make_address_for_user(keys, j, destination_known); address_index_t j_nominal; EXPECT_TRUE(try_get_jamtis_index_from_destination_v1(destination_known, - keys.K_1_base, - keys.xK_ua, - keys.xK_fr, + keys.K_s_base, + keys.D_fa, + keys.D_vr, + keys.D_base, keys.s_ga, j_nominal)); EXPECT_TRUE(j_nominal == j); @@ -657,9 +644,10 @@ TEST(seraphis_basic, information_recovery_jamtisdestination) JamtisDestinationV1 destination_unknown; destination_unknown = gen_jamtis_destination_v1(); EXPECT_FALSE(try_get_jamtis_index_from_destination_v1(destination_unknown, - keys.K_1_base, - keys.xK_ua, - keys.xK_fr, + keys.K_s_base, + keys.D_fa, + keys.D_vr, + keys.D_base, keys.s_ga, j_nominal)); } @@ -674,19 +662,17 @@ TEST(seraphis_basic, information_recovery_coinbase_enote_v1_plain) const address_index_t j{gen_address_index()}; JamtisDestinationV1 user_address; - make_jamtis_destination_v1(keys.K_1_base, - keys.xK_ua, - keys.xK_fr, - keys.s_ga, + make_address_for_user(keys, j, user_address); // make a plain enote paying to address const rct::xmr_amount amount{crypto::rand_idx(0)}; const crypto::x25519_secret_key enote_privkey{crypto::x25519_secret_key_gen()}; + const std::uint8_t num_primary_view_tag_bits{4}; const std::uint64_t block_height{0}; - JamtisPaymentProposalV1 payment_proposal{user_address, amount, enote_privkey}; + JamtisPaymentProposalV1 payment_proposal{user_address, amount, enote_privkey, num_primary_view_tag_bits}; SpCoinbaseOutputProposalV1 output_proposal; make_v1_coinbase_output_proposal_v1(payment_proposal, block_height, output_proposal); @@ -704,10 +690,7 @@ TEST(seraphis_basic, information_recovery_enote_v1_plain) const address_index_t j{gen_address_index()}; JamtisDestinationV1 user_address; - make_jamtis_destination_v1(keys.K_1_base, - keys.xK_ua, - keys.xK_fr, - keys.s_ga, + make_address_for_user(keys, j, user_address); @@ -733,26 +716,25 @@ TEST(seraphis_basic, information_recovery_enote_v1_selfsend) const address_index_t j{gen_address_index()}; JamtisDestinationV1 user_address; - make_jamtis_destination_v1(keys.K_1_base, - keys.xK_ua, - keys.xK_fr, - keys.s_ga, + make_address_for_user(keys, j, user_address); // make a self-spend enote paying to address rct::xmr_amount amount{crypto::rand_idx(0)}; crypto::x25519_secret_key enote_privkey{crypto::x25519_secret_key_gen()}; + const std::uint8_t num_primary_view_tag_bits{7}; JamtisPaymentProposalSelfSendV1 payment_proposal_selfspend{user_address, amount, - JamtisSelfSendType::SELF_SPEND, - enote_privkey}; + JamtisSelfSendType::AUXILIARY_SELF_SPEND, + enote_privkey, + num_primary_view_tag_bits}; SpOutputProposalV1 output_proposal; make_v1_output_proposal_v1(payment_proposal_selfspend, keys.k_vb, rct::zero(), output_proposal); // check the enote - check_is_owned(output_proposal, keys, j, amount, JamtisEnoteType::SELF_SPEND); + check_is_owned(output_proposal, keys, j, amount, JamtisEnoteType::AUXILIARY_SELF_SPEND); // make a change enote paying to address amount = crypto::rand_idx(0); @@ -760,12 +742,13 @@ TEST(seraphis_basic, information_recovery_enote_v1_selfsend) JamtisPaymentProposalSelfSendV1 payment_proposal_change{user_address, amount, - JamtisSelfSendType::CHANGE, - enote_privkey}; + JamtisSelfSendType::EXCLUSIVE_CHANGE, + enote_privkey, + num_primary_view_tag_bits}; make_v1_output_proposal_v1(payment_proposal_change, keys.k_vb, rct::zero(), output_proposal); // check the enote - check_is_owned(output_proposal, keys, j, amount, JamtisEnoteType::CHANGE); + check_is_owned(output_proposal, keys, j, amount, JamtisEnoteType::EXCLUSIVE_CHANGE); } //------------------------------------------------------------------------------------------------------------------- TEST(seraphis_basic, finalize_v1_output_proposal_set_v1) @@ -777,296 +760,416 @@ TEST(seraphis_basic, finalize_v1_output_proposal_set_v1) make_jamtis_mock_keys(keys); // user addresses - address_index_t j_selfspend; - address_index_t j_change; - address_index_t j_dummy; - j_selfspend = gen_address_index(); - j_change = gen_address_index(); - j_dummy = gen_address_index(); + const address_index_t j_selfspend = gen_address_index(); + const address_index_t j_change = gen_address_index(); JamtisDestinationV1 selfspend_dest; JamtisDestinationV1 change_dest; - JamtisDestinationV1 dummy_dest; - make_jamtis_destination_v1(keys.K_1_base, keys.xK_ua, keys.xK_fr, keys.s_ga, j_selfspend, selfspend_dest); - make_jamtis_destination_v1(keys.K_1_base, keys.xK_ua, keys.xK_fr, keys.s_ga, j_change, change_dest); - make_jamtis_destination_v1(keys.K_1_base, keys.xK_ua, keys.xK_fr, keys.s_ga, j_dummy, dummy_dest); - - // prepare self-spend payment proposals - JamtisPaymentProposalSelfSendV1 self_spend_payment_proposal1_amnt_1; - self_spend_payment_proposal1_amnt_1.destination = selfspend_dest; - self_spend_payment_proposal1_amnt_1.amount = 1; - self_spend_payment_proposal1_amnt_1.type = JamtisSelfSendType::SELF_SPEND; - make_secret_key(self_spend_payment_proposal1_amnt_1.enote_ephemeral_privkey); - - JamtisPaymentProposalSelfSendV1 self_spend_payment_proposal2_amnt_1{self_spend_payment_proposal1_amnt_1}; - make_secret_key(self_spend_payment_proposal2_amnt_1.enote_ephemeral_privkey); - - // prepare change output - JamtisPaymentProposalSelfSendV1 change_payment_proposal_amnt_1; - change_payment_proposal_amnt_1.destination = change_dest; - change_payment_proposal_amnt_1.amount = 1; - change_payment_proposal_amnt_1.type = JamtisSelfSendType::CHANGE; - make_secret_key(change_payment_proposal_amnt_1.enote_ephemeral_privkey); + make_address_for_user(keys, j_selfspend, selfspend_dest); + make_address_for_user(keys, j_change, change_dest); + + // payment proposal config + const std::uint8_t num_primary_view_tag_bits{8}; + + // prepare exclusive self-spend payment proposal + JamtisPaymentProposalSelfSendV1 ex_self_spend_payment_proposal_amnt_1; + ex_self_spend_payment_proposal_amnt_1.destination = selfspend_dest; + ex_self_spend_payment_proposal_amnt_1.amount = 1; + ex_self_spend_payment_proposal_amnt_1.type = JamtisSelfSendType::EXCLUSIVE_SELF_SPEND; + make_secret_key(ex_self_spend_payment_proposal_amnt_1.enote_ephemeral_privkey); + ex_self_spend_payment_proposal_amnt_1.num_primary_view_tag_bits = num_primary_view_tag_bits; + + // prepare auxiliary self-spend payment proposal + JamtisPaymentProposalSelfSendV1 aux_self_spend_payment_proposal_amnt_1; + aux_self_spend_payment_proposal_amnt_1.destination = selfspend_dest; + aux_self_spend_payment_proposal_amnt_1.amount = 1; + aux_self_spend_payment_proposal_amnt_1.type = JamtisSelfSendType::AUXILIARY_SELF_SPEND; + make_secret_key(aux_self_spend_payment_proposal_amnt_1.enote_ephemeral_privkey); + aux_self_spend_payment_proposal_amnt_1.num_primary_view_tag_bits = num_primary_view_tag_bits; + + // prepare exclusive change output + JamtisPaymentProposalSelfSendV1 ex_change_payment_proposal_amnt_1; + ex_change_payment_proposal_amnt_1.destination = change_dest; + ex_change_payment_proposal_amnt_1.amount = 1; + ex_change_payment_proposal_amnt_1.type = JamtisSelfSendType::EXCLUSIVE_CHANGE; + make_secret_key(ex_change_payment_proposal_amnt_1.enote_ephemeral_privkey); + ex_change_payment_proposal_amnt_1.num_primary_view_tag_bits = num_primary_view_tag_bits; + + // prepare auxiliary change output + JamtisPaymentProposalSelfSendV1 aux_change_payment_proposal_amnt_1; + aux_change_payment_proposal_amnt_1.destination = change_dest; + aux_change_payment_proposal_amnt_1.amount = 1; + aux_change_payment_proposal_amnt_1.type = JamtisSelfSendType::AUXILIARY_CHANGE; + make_secret_key(aux_change_payment_proposal_amnt_1.enote_ephemeral_privkey); + aux_change_payment_proposal_amnt_1.num_primary_view_tag_bits = num_primary_view_tag_bits; // sanity checks - SpOutputProposalV1 self_spend_proposal1_amnt_1; - SpOutputProposalV1 self_spend_proposal2_amnt_1; - SpOutputProposalV1 change_proposal_amnt_1; - make_v1_output_proposal_v1(self_spend_payment_proposal1_amnt_1, keys.k_vb, rct::zero(), self_spend_proposal1_amnt_1); - make_v1_output_proposal_v1(self_spend_payment_proposal2_amnt_1, keys.k_vb, rct::zero(), self_spend_proposal2_amnt_1); - make_v1_output_proposal_v1(change_payment_proposal_amnt_1, keys.k_vb, rct::zero(), change_proposal_amnt_1); - check_is_owned(self_spend_proposal2_amnt_1, keys, j_selfspend, 1, JamtisEnoteType::SELF_SPEND); - check_is_owned(self_spend_proposal1_amnt_1, keys, j_selfspend, 1, JamtisEnoteType::SELF_SPEND); - check_is_owned(change_proposal_amnt_1, keys, j_change, 1, JamtisEnoteType::CHANGE); - + SpOutputProposalV1 output_proposal_temp; + make_v1_output_proposal_v1(ex_self_spend_payment_proposal_amnt_1, keys.k_vb, rct::zero(), output_proposal_temp); + check_is_owned(output_proposal_temp, keys, j_selfspend, 1, JamtisEnoteType::EXCLUSIVE_SELF_SPEND); + make_v1_output_proposal_v1(aux_self_spend_payment_proposal_amnt_1, keys.k_vb, rct::zero(), output_proposal_temp); + check_is_owned(output_proposal_temp, keys, j_selfspend, 1, JamtisEnoteType::AUXILIARY_SELF_SPEND); + make_v1_output_proposal_v1(ex_change_payment_proposal_amnt_1, keys.k_vb, rct::zero(), output_proposal_temp); + check_is_owned(output_proposal_temp, keys, j_change, 1, JamtisEnoteType::EXCLUSIVE_CHANGE); + make_v1_output_proposal_v1(aux_change_payment_proposal_amnt_1, keys.k_vb, rct::zero(), output_proposal_temp); + check_is_owned(output_proposal_temp, keys, j_change, 1, JamtisEnoteType::AUXILIARY_CHANGE); /// test cases - boost::multiprecision::uint128_t in_amount{0}; - const rct::xmr_amount fee{1}; + bool nonzero_change{}; std::vector normal_proposals; std::vector selfsend_proposals; auto finalize_outputs_for_test = - [&](std::vector &normal_payment_proposals_inout, - std::vector &selfsend_payment_proposals_inout) + [&]() { + const rct::xmr_amount fee{1}; + + rct::xmr_amount in_amount = fee; + for (const auto &p: normal_proposals) + in_amount += p.amount; + for (const auto &p: selfsend_proposals) + in_amount += p.amount; + if (nonzero_change) + in_amount += 1; + finalize_v1_output_proposal_set_v1(in_amount, fee, change_dest, - dummy_dest, keys.k_vb, - normal_payment_proposals_inout, - selfsend_payment_proposals_inout); + normal_proposals, + selfsend_proposals); }; // 0 outputs, 0 change: error - in_amount = 0 + fee; + nonzero_change = false; normal_proposals.clear(); selfsend_proposals.clear(); - EXPECT_ANY_THROW(finalize_outputs_for_test(normal_proposals, selfsend_proposals)); + ASSERT_ANY_THROW(finalize_outputs_for_test()); // 0 outputs, >0 change: error - in_amount = 1 + fee; + nonzero_change = true; normal_proposals.clear(); selfsend_proposals.clear(); //change = 1 - EXPECT_ANY_THROW(finalize_outputs_for_test(normal_proposals, selfsend_proposals)); + ASSERT_ANY_THROW(finalize_outputs_for_test()); - // 1 normal output, 0 change: 2 outputs (1 self-send dummy) - in_amount = 1 + fee; + // 1 normal output, 0 change: 2 outputs (1 ex change) + nonzero_change = false; normal_proposals.resize(1); - normal_proposals[0] = gen_jamtis_payment_proposal_v1(1, 0); + normal_proposals[0] = gen_jamtis_payment_proposal_v1(1, 0, num_primary_view_tag_bits); selfsend_proposals.clear(); - EXPECT_NO_THROW(finalize_outputs_for_test(normal_proposals, selfsend_proposals)); - EXPECT_TRUE(normal_proposals.size() == 1); - EXPECT_TRUE(selfsend_proposals.size() == 1); - check_is_owned(selfsend_proposals[0], keys, j_dummy, 0, JamtisEnoteType::DUMMY); + ASSERT_NO_THROW(finalize_outputs_for_test()); + ASSERT_TRUE(normal_proposals.size() == 1); + ASSERT_TRUE(selfsend_proposals.size() == 1); + check_is_owned(selfsend_proposals[0], keys, j_change, nonzero_change, JamtisEnoteType::EXCLUSIVE_CHANGE); - // 1 normal output, >0 change: 2 outputs (1 change) - in_amount = 2 + fee; + // 1 normal output, >0 change: 2 outputs (1 ex change) + nonzero_change = true; normal_proposals.resize(1); - normal_proposals[0] = gen_jamtis_payment_proposal_v1(1, 0); //change = 1 + normal_proposals[0] = gen_jamtis_payment_proposal_v1(1, 0, num_primary_view_tag_bits); //change = 1 selfsend_proposals.clear(); - EXPECT_NO_THROW(finalize_outputs_for_test(normal_proposals, selfsend_proposals)); - EXPECT_TRUE(normal_proposals.size() == 1); - EXPECT_TRUE(selfsend_proposals.size() == 1); - check_is_owned(selfsend_proposals[0], keys, j_change, 1, JamtisEnoteType::CHANGE); + ASSERT_NO_THROW(finalize_outputs_for_test()); + ASSERT_TRUE(normal_proposals.size() == 1); + ASSERT_TRUE(selfsend_proposals.size() == 1); + check_is_owned(selfsend_proposals[0], keys, j_change, nonzero_change, JamtisEnoteType::EXCLUSIVE_CHANGE); - // 2 normal outputs, 0 change: 3 outputs (1 self-send dummy) - in_amount = 2 + fee; + // 2 normal outputs, 0 change: 3 outputs (1 ex change) + nonzero_change = false; normal_proposals.resize(2); - normal_proposals[0] = gen_jamtis_payment_proposal_v1(1, 0); - normal_proposals[1] = gen_jamtis_payment_proposal_v1(1, 0); + normal_proposals[0] = gen_jamtis_payment_proposal_v1(1, 0, num_primary_view_tag_bits); + normal_proposals[1] = gen_jamtis_payment_proposal_v1(1, 0, num_primary_view_tag_bits); selfsend_proposals.clear(); - EXPECT_NO_THROW(finalize_outputs_for_test(normal_proposals, selfsend_proposals)); - EXPECT_TRUE(normal_proposals.size() == 2); - EXPECT_TRUE(selfsend_proposals.size() == 1); - check_is_owned(selfsend_proposals[0], keys, j_dummy, 0, JamtisEnoteType::DUMMY); + ASSERT_NO_THROW(finalize_outputs_for_test()); + ASSERT_TRUE(normal_proposals.size() == 2); + ASSERT_TRUE(selfsend_proposals.size() == 1); + check_is_owned(selfsend_proposals[0], keys, j_change, nonzero_change, JamtisEnoteType::EXCLUSIVE_CHANGE); // 2 normal outputs (shared ephemeral pubkey), 0 change: error - in_amount = 2 + fee; + nonzero_change = false; normal_proposals.resize(2); - normal_proposals[0] = gen_jamtis_payment_proposal_v1(1, 0); - normal_proposals[1] = gen_jamtis_payment_proposal_v1(1, 0); + normal_proposals[0] = gen_jamtis_payment_proposal_v1(1, 0, num_primary_view_tag_bits); + normal_proposals[1] = gen_jamtis_payment_proposal_v1(1, 0, num_primary_view_tag_bits); normal_proposals[1].enote_ephemeral_privkey = normal_proposals[0].enote_ephemeral_privkey; - normal_proposals[1].destination.addr_K3 = normal_proposals[0].destination.addr_K3; + normal_proposals[1].destination.addr_Dbase = normal_proposals[0].destination.addr_Dbase; selfsend_proposals.clear(); - EXPECT_ANY_THROW(finalize_outputs_for_test(normal_proposals, selfsend_proposals)); + ASSERT_ANY_THROW(finalize_outputs_for_test()); // 2 normal outputs (shared ephemeral pubkey), >0 change: error - in_amount = 3 + fee; + nonzero_change = true; normal_proposals.resize(2); - normal_proposals[0] = gen_jamtis_payment_proposal_v1(1, 0); - normal_proposals[1] = gen_jamtis_payment_proposal_v1(1, 0); //change = 1 + normal_proposals[0] = gen_jamtis_payment_proposal_v1(1, 0, num_primary_view_tag_bits); + normal_proposals[1] = gen_jamtis_payment_proposal_v1(1, 0, num_primary_view_tag_bits); //change = 1 normal_proposals[1].enote_ephemeral_privkey = normal_proposals[0].enote_ephemeral_privkey; - normal_proposals[1].destination.addr_K3 = normal_proposals[0].destination.addr_K3; + normal_proposals[1].destination.addr_Dbase = normal_proposals[0].destination.addr_Dbase; selfsend_proposals.clear(); - EXPECT_ANY_THROW(finalize_outputs_for_test(normal_proposals, selfsend_proposals)); + ASSERT_ANY_THROW(finalize_outputs_for_test()); - // 3 normal outputs, 0 change: 4 outputs (1 self-send dummy) - in_amount = 3 + fee; + // 3 normal outputs, 0 change: 4 outputs (1 ex change) + nonzero_change = false; normal_proposals.resize(3); - normal_proposals[0] = gen_jamtis_payment_proposal_v1(1, 0); - normal_proposals[1] = gen_jamtis_payment_proposal_v1(1, 0); - normal_proposals[2] = gen_jamtis_payment_proposal_v1(1, 0); + normal_proposals[0] = gen_jamtis_payment_proposal_v1(1, 0, num_primary_view_tag_bits); + normal_proposals[1] = gen_jamtis_payment_proposal_v1(1, 0, num_primary_view_tag_bits); + normal_proposals[2] = gen_jamtis_payment_proposal_v1(1, 0, num_primary_view_tag_bits); selfsend_proposals.clear(); - EXPECT_NO_THROW(finalize_outputs_for_test(normal_proposals, selfsend_proposals)); - EXPECT_TRUE(normal_proposals.size() == 3); - EXPECT_TRUE(selfsend_proposals.size() == 1); - check_is_owned(selfsend_proposals[0], keys, j_dummy, 0, JamtisEnoteType::DUMMY); + ASSERT_NO_THROW(finalize_outputs_for_test()); + ASSERT_TRUE(normal_proposals.size() == 3); + ASSERT_TRUE(selfsend_proposals.size() == 1); + check_is_owned(selfsend_proposals[0], keys, j_change, nonzero_change, JamtisEnoteType::EXCLUSIVE_CHANGE); - // 3 normal outputs, >0 change: 4 outputs (1 change) - in_amount = 4 + fee; + // 3 normal outputs, >0 change: 4 outputs (1 ex change) + nonzero_change = true; normal_proposals.resize(3); - normal_proposals[0] = gen_jamtis_payment_proposal_v1(1, 0); - normal_proposals[1] = gen_jamtis_payment_proposal_v1(1, 0); - normal_proposals[2] = gen_jamtis_payment_proposal_v1(1, 0); //change = 1 + normal_proposals[0] = gen_jamtis_payment_proposal_v1(1, 0, num_primary_view_tag_bits); + normal_proposals[1] = gen_jamtis_payment_proposal_v1(1, 0, num_primary_view_tag_bits); + normal_proposals[2] = gen_jamtis_payment_proposal_v1(1, 0, num_primary_view_tag_bits); //change = 1 selfsend_proposals.clear(); - EXPECT_NO_THROW(finalize_outputs_for_test(normal_proposals, selfsend_proposals)); - EXPECT_TRUE(normal_proposals.size() == 3); - EXPECT_TRUE(selfsend_proposals.size() == 1); - check_is_owned(selfsend_proposals[0], keys, j_change, 1, JamtisEnoteType::CHANGE); + ASSERT_NO_THROW(finalize_outputs_for_test()); + ASSERT_TRUE(normal_proposals.size() == 3); + ASSERT_TRUE(selfsend_proposals.size() == 1); + check_is_owned(selfsend_proposals[0], keys, j_change, nonzero_change, JamtisEnoteType::EXCLUSIVE_CHANGE); - // 1 self-send output, 0 change: 2 outputs (1 dummy) - in_amount = 1 + fee; + // 1 exclusive self-spend output, 0 change: 2 outputs + nonzero_change = false; normal_proposals.clear(); selfsend_proposals.resize(1); - selfsend_proposals[0] = self_spend_payment_proposal1_amnt_1; - EXPECT_NO_THROW(finalize_outputs_for_test(normal_proposals, selfsend_proposals)); - EXPECT_TRUE(normal_proposals.size() == 1); - EXPECT_TRUE(selfsend_proposals.size() == 1); - EXPECT_TRUE(normal_proposals[0].amount == 0); - check_is_owned(selfsend_proposals[0], keys, j_selfspend, 1, JamtisEnoteType::SELF_SPEND); - - // 1 self-send output, >0 change: 2 outputs (1 change) - in_amount = 2 + fee; + selfsend_proposals[0] = ex_self_spend_payment_proposal_amnt_1; + ASSERT_NO_THROW(finalize_outputs_for_test()); + ASSERT_EQ(0, normal_proposals.size()); + ASSERT_EQ(2, selfsend_proposals.size()); + check_is_owned(selfsend_proposals[0], keys, j_selfspend, 1, JamtisEnoteType::EXCLUSIVE_SELF_SPEND); + check_is_owned(selfsend_proposals[1], keys, j_change, nonzero_change, JamtisEnoteType::AUXILIARY_CHANGE); + + // 1 exclusive self-spend output, >0 change: 2 outputs (1 change) + nonzero_change = true; normal_proposals.clear(); selfsend_proposals.resize(1); - selfsend_proposals[0] = self_spend_payment_proposal1_amnt_1; //change = 1 - EXPECT_NO_THROW(finalize_outputs_for_test(normal_proposals, selfsend_proposals)); - EXPECT_TRUE(normal_proposals.size() == 0); - EXPECT_TRUE(selfsend_proposals.size() == 2); - check_is_owned(selfsend_proposals[0], keys, j_selfspend, 1, JamtisEnoteType::SELF_SPEND); - check_is_owned(selfsend_proposals[1], keys, j_change, 1, JamtisEnoteType::CHANGE); - - // 1 change output, >0 change: error - in_amount = 2 + fee; + selfsend_proposals[0] = ex_self_spend_payment_proposal_amnt_1; //change = 1 + ASSERT_NO_THROW(finalize_outputs_for_test()); + ASSERT_EQ(0, normal_proposals.size()); + ASSERT_EQ(2, selfsend_proposals.size()); + check_is_owned(selfsend_proposals[0], keys, j_selfspend, 1, JamtisEnoteType::EXCLUSIVE_SELF_SPEND); + check_is_owned(selfsend_proposals[1], keys, j_change, nonzero_change, JamtisEnoteType::AUXILIARY_CHANGE); + + // 1 auxiliary self-spend output, 0 change: 2 outputs + nonzero_change = false; normal_proposals.clear(); selfsend_proposals.resize(1); - selfsend_proposals[0] = change_payment_proposal_amnt_1; //change = 1 - EXPECT_ANY_THROW(finalize_outputs_for_test(normal_proposals, selfsend_proposals)); - - // 1 self-send output & 1 normal output (shared ephemeral pubkey), 0 change: 2 outputs - in_amount = 2 + fee; + selfsend_proposals[0] = aux_self_spend_payment_proposal_amnt_1; + ASSERT_NO_THROW(finalize_outputs_for_test()); + ASSERT_EQ(0, normal_proposals.size()); + ASSERT_EQ(2, selfsend_proposals.size()); + check_is_owned(selfsend_proposals[0], keys, j_selfspend, 1, JamtisEnoteType::AUXILIARY_SELF_SPEND); + check_is_owned(selfsend_proposals[1], keys, j_change, nonzero_change, JamtisEnoteType::EXCLUSIVE_CHANGE); + + // 1 auxiliary self-spend output, >0 change: 2 outputs (1 change) + nonzero_change = true; + normal_proposals.clear(); + selfsend_proposals.resize(1); + selfsend_proposals[0] = aux_self_spend_payment_proposal_amnt_1; //change = 1 + ASSERT_NO_THROW(finalize_outputs_for_test()); + ASSERT_EQ(0, normal_proposals.size()); + ASSERT_EQ(2, selfsend_proposals.size()); + check_is_owned(selfsend_proposals[0], keys, j_selfspend, 1, JamtisEnoteType::AUXILIARY_SELF_SPEND); + check_is_owned(selfsend_proposals[1], keys, j_change, nonzero_change, JamtisEnoteType::EXCLUSIVE_CHANGE); + + // 1 exclusive change output, >0 change: add auxiliary change + nonzero_change = true; + normal_proposals.clear(); + selfsend_proposals.resize(1); + selfsend_proposals[0] = ex_change_payment_proposal_amnt_1; //change = 1 + ASSERT_NO_THROW(finalize_outputs_for_test()); + ASSERT_EQ(0, normal_proposals.size()); + ASSERT_EQ(2, selfsend_proposals.size()); + check_is_owned(selfsend_proposals[0], keys, j_change, 1, JamtisEnoteType::EXCLUSIVE_CHANGE); + check_is_owned(selfsend_proposals[1], keys, j_change, nonzero_change, JamtisEnoteType::AUXILIARY_CHANGE); + + // 1 auxiliary change output, >0 change: add exclusive change + nonzero_change = true; + normal_proposals.clear(); + selfsend_proposals.resize(1); + selfsend_proposals[0] = aux_change_payment_proposal_amnt_1; //change = 1 + ASSERT_NO_THROW(finalize_outputs_for_test()); + ASSERT_EQ(0, normal_proposals.size()); + ASSERT_EQ(2, selfsend_proposals.size()); + check_is_owned(selfsend_proposals[0], keys, j_change, 1, JamtisEnoteType::AUXILIARY_CHANGE); + check_is_owned(selfsend_proposals[1], keys, j_change, nonzero_change, JamtisEnoteType::EXCLUSIVE_CHANGE); + + // 1 exclusive self-send output & 1 normal output (shared ephemeral pubkey), 0 change: 2 outputs + nonzero_change = false; normal_proposals.resize(1); - normal_proposals[0] = gen_jamtis_payment_proposal_v1(1, 0); + normal_proposals[0] = gen_jamtis_payment_proposal_v1(1, 0, num_primary_view_tag_bits); selfsend_proposals.resize(1); - selfsend_proposals[0] = self_spend_payment_proposal1_amnt_1; + selfsend_proposals[0] = ex_self_spend_payment_proposal_amnt_1; normal_proposals[0].enote_ephemeral_privkey = selfsend_proposals[0].enote_ephemeral_privkey; - normal_proposals[0].destination.addr_K3 = selfsend_proposals[0].destination.addr_K3; - EXPECT_NO_THROW(finalize_outputs_for_test(normal_proposals, selfsend_proposals)); - EXPECT_TRUE(normal_proposals.size() == 1); - EXPECT_TRUE(selfsend_proposals.size() == 1); - check_is_owned(selfsend_proposals[0], keys, j_selfspend, 1, JamtisEnoteType::SELF_SPEND); - - // 1 self-send output & 1 normal output (shared ephemeral pubkey), >0 change: error - in_amount = 3 + fee; + normal_proposals[0].destination.addr_Dbase = selfsend_proposals[0].destination.addr_Dbase; + ASSERT_NO_THROW(finalize_outputs_for_test()); + ASSERT_TRUE(normal_proposals.size() == 1); + ASSERT_TRUE(selfsend_proposals.size() == 1); + check_is_owned(selfsend_proposals[0], keys, j_selfspend, 1, JamtisEnoteType::EXCLUSIVE_SELF_SPEND); + + // 1 exclusive self-spend output & 1 normal output (shared ephemeral pubkey), >0 change: error + nonzero_change = true; + normal_proposals.resize(1); + normal_proposals[0] = gen_jamtis_payment_proposal_v1(1, 0, num_primary_view_tag_bits); + selfsend_proposals.resize(1); + selfsend_proposals[0] = ex_self_spend_payment_proposal_amnt_1; //change = 1 + normal_proposals[0].enote_ephemeral_privkey = selfsend_proposals[0].enote_ephemeral_privkey; + normal_proposals[0].destination.addr_Dbase = selfsend_proposals[0].destination.addr_Dbase; + ASSERT_ANY_THROW(finalize_outputs_for_test()); + + // 1 auxiliary self-spend output & 1 normal output (shared ephemeral pubkey), >0 change: error + nonzero_change = true; normal_proposals.resize(1); - normal_proposals[0] = gen_jamtis_payment_proposal_v1(1, 0); + normal_proposals[0] = gen_jamtis_payment_proposal_v1(1, 0, num_primary_view_tag_bits); selfsend_proposals.resize(1); - selfsend_proposals[0] = self_spend_payment_proposal1_amnt_1; //change = 1 + selfsend_proposals[0] = aux_self_spend_payment_proposal_amnt_1; //change = 1 normal_proposals[0].enote_ephemeral_privkey = selfsend_proposals[0].enote_ephemeral_privkey; - normal_proposals[0].destination.addr_K3 = selfsend_proposals[0].destination.addr_K3; - EXPECT_ANY_THROW(finalize_outputs_for_test(normal_proposals, selfsend_proposals)); + normal_proposals[0].destination.addr_Dbase = selfsend_proposals[0].destination.addr_Dbase; + ASSERT_ANY_THROW(finalize_outputs_for_test()); - // 1 self-send output, 1 normal output, 0 change: 3 outputs (1 dummy) - in_amount = 2 + fee; + // 1 exclusive self-spend output, 1 normal output, 0 change: 3 outputs (1 dummy) + nonzero_change = false; normal_proposals.resize(1); - normal_proposals[0] = gen_jamtis_payment_proposal_v1(1, 0); + normal_proposals[0] = gen_jamtis_payment_proposal_v1(1, 0, num_primary_view_tag_bits); selfsend_proposals.resize(1); - selfsend_proposals[0] = self_spend_payment_proposal1_amnt_1; - EXPECT_NO_THROW(finalize_outputs_for_test(normal_proposals, selfsend_proposals)); - EXPECT_TRUE(normal_proposals.size() == 2); - EXPECT_TRUE(selfsend_proposals.size() == 1); - EXPECT_TRUE(normal_proposals[1].amount == 0); - check_is_owned(selfsend_proposals[0], keys, j_selfspend, 1, JamtisEnoteType::SELF_SPEND); - - // 1 self-send output, 1 normal output, >0 change: 3 outputs (1 change) - in_amount = 3 + fee; + selfsend_proposals[0] = ex_self_spend_payment_proposal_amnt_1; + ASSERT_NO_THROW(finalize_outputs_for_test()); + ASSERT_EQ(1, normal_proposals.size()); + ASSERT_EQ(2, selfsend_proposals.size()); + ASSERT_TRUE(normal_proposals[0].amount == 1); + check_is_owned(selfsend_proposals[0], keys, j_selfspend, 1, JamtisEnoteType::EXCLUSIVE_SELF_SPEND); + check_is_owned(selfsend_proposals[1], keys, j_change, nonzero_change, JamtisEnoteType::AUXILIARY_CHANGE); + + // 1 exclusive self-spend output, 1 normal output, >0 change: 3 outputs (1 change) + nonzero_change = true; normal_proposals.resize(1); - normal_proposals[0] = gen_jamtis_payment_proposal_v1(1, 0); + normal_proposals[0] = gen_jamtis_payment_proposal_v1(1, 0, num_primary_view_tag_bits); selfsend_proposals.resize(1); - selfsend_proposals[0] = self_spend_payment_proposal1_amnt_1; //change = 1 - EXPECT_NO_THROW(finalize_outputs_for_test(normal_proposals, selfsend_proposals)); - EXPECT_TRUE(normal_proposals.size() == 1); - EXPECT_TRUE(selfsend_proposals.size() == 2); - check_is_owned(selfsend_proposals[0], keys, j_selfspend, 1, JamtisEnoteType::SELF_SPEND); - check_is_owned(selfsend_proposals[1], keys, j_change, 1, JamtisEnoteType::CHANGE); - - // 1 self-send output, 2 normal outputs, 0 change: 3 outputs - in_amount = 3 + fee; + selfsend_proposals[0] = ex_self_spend_payment_proposal_amnt_1; //change = 1 + ASSERT_NO_THROW(finalize_outputs_for_test()); + ASSERT_TRUE(normal_proposals.size() == 1); + ASSERT_TRUE(selfsend_proposals.size() == 2); + check_is_owned(selfsend_proposals[0], keys, j_selfspend, 1, JamtisEnoteType::EXCLUSIVE_SELF_SPEND); + check_is_owned(selfsend_proposals[1], keys, j_change, nonzero_change, JamtisEnoteType::AUXILIARY_CHANGE); + + // 1 exclusive self-send output, 2 normal outputs, 0 change: 3 outputs + nonzero_change = false; normal_proposals.resize(2); - normal_proposals[0] = gen_jamtis_payment_proposal_v1(1, 0); - normal_proposals[1] = gen_jamtis_payment_proposal_v1(1, 0); + normal_proposals[0] = gen_jamtis_payment_proposal_v1(1, 0, num_primary_view_tag_bits); + normal_proposals[1] = gen_jamtis_payment_proposal_v1(1, 0, num_primary_view_tag_bits); selfsend_proposals.resize(1); - selfsend_proposals[0] = self_spend_payment_proposal1_amnt_1; - EXPECT_NO_THROW(finalize_outputs_for_test(normal_proposals, selfsend_proposals)); - EXPECT_TRUE(normal_proposals.size() == 2); - EXPECT_TRUE(selfsend_proposals.size() == 1); - check_is_owned(selfsend_proposals[0], keys, j_selfspend, 1, JamtisEnoteType::SELF_SPEND); - - // 1 self-send output, 2 normal outputs, >0 change: 4 outputs (1 change) - in_amount = 4 + fee; + selfsend_proposals[0] = ex_self_spend_payment_proposal_amnt_1; + ASSERT_NO_THROW(finalize_outputs_for_test()); + ASSERT_TRUE(normal_proposals.size() == 2); + ASSERT_TRUE(selfsend_proposals.size() == 1); + check_is_owned(selfsend_proposals[0], keys, j_selfspend, 1, JamtisEnoteType::EXCLUSIVE_SELF_SPEND); + + // 1 auxiliary change output, 2 normal outputs, 0 change: 4 outputs (added exclusive change) + nonzero_change = false; normal_proposals.resize(2); - normal_proposals[0] = gen_jamtis_payment_proposal_v1(1, 0); - normal_proposals[1] = gen_jamtis_payment_proposal_v1(1, 0); + normal_proposals[0] = gen_jamtis_payment_proposal_v1(1, 0, num_primary_view_tag_bits); + normal_proposals[1] = gen_jamtis_payment_proposal_v1(1, 0, num_primary_view_tag_bits); selfsend_proposals.resize(1); - selfsend_proposals[0] = self_spend_payment_proposal1_amnt_1; //change = 1 - EXPECT_NO_THROW(finalize_outputs_for_test(normal_proposals, selfsend_proposals)); - EXPECT_TRUE(normal_proposals.size() == 2); - EXPECT_TRUE(selfsend_proposals.size() == 2); - check_is_owned(selfsend_proposals[0], keys, j_selfspend, 1, JamtisEnoteType::SELF_SPEND); - check_is_owned(selfsend_proposals[1], keys, j_change, 1, JamtisEnoteType::CHANGE); - - // 2 self-send outputs (shared ephemeral pubkey), 0 change: error - in_amount = 2 + fee; + selfsend_proposals[0] = aux_change_payment_proposal_amnt_1; + ASSERT_NO_THROW(finalize_outputs_for_test()); + ASSERT_EQ(2, normal_proposals.size()); + ASSERT_EQ(2, selfsend_proposals.size()); + check_is_owned(selfsend_proposals[0], keys, j_change, 1, JamtisEnoteType::AUXILIARY_CHANGE); + check_is_owned(selfsend_proposals[1], keys, j_change, nonzero_change, JamtisEnoteType::EXCLUSIVE_CHANGE); + + // 1 exclusive self-spend output, 2 normal outputs, >0 change: 4 outputs (1 change) + nonzero_change = true; + normal_proposals.resize(2); + normal_proposals[0] = gen_jamtis_payment_proposal_v1(1, 0, num_primary_view_tag_bits); + normal_proposals[1] = gen_jamtis_payment_proposal_v1(1, 0, num_primary_view_tag_bits); + selfsend_proposals.resize(1); + selfsend_proposals[0] = ex_self_spend_payment_proposal_amnt_1; //change = 1 + ASSERT_NO_THROW(finalize_outputs_for_test()); + ASSERT_EQ(2, normal_proposals.size()); + ASSERT_EQ(2, selfsend_proposals.size()); + check_is_owned(selfsend_proposals[0], keys, j_selfspend, 1, JamtisEnoteType::EXCLUSIVE_SELF_SPEND); + check_is_owned(selfsend_proposals[1], keys, j_change, nonzero_change, JamtisEnoteType::AUXILIARY_CHANGE); + + // 1 exclusive change output, 2 normal outputs, >0 change: 4 outputs (1 change) + nonzero_change = true; + normal_proposals.resize(2); + normal_proposals[0] = gen_jamtis_payment_proposal_v1(1, 0, num_primary_view_tag_bits); + normal_proposals[1] = gen_jamtis_payment_proposal_v1(1, 0, num_primary_view_tag_bits); + selfsend_proposals.resize(1); + selfsend_proposals[0] = ex_change_payment_proposal_amnt_1; //change = 1 + ASSERT_NO_THROW(finalize_outputs_for_test()); + ASSERT_EQ(2, normal_proposals.size()); + ASSERT_EQ(2, selfsend_proposals.size()); + check_is_owned(selfsend_proposals[0], keys, j_change, 1, JamtisEnoteType::EXCLUSIVE_CHANGE); + check_is_owned(selfsend_proposals[1], keys, j_change, nonzero_change, JamtisEnoteType::AUXILIARY_CHANGE); + + // 2 exclusive self-send outputs (shared ephemeral pubkey), 0 change: error + nonzero_change = false; normal_proposals.clear(); selfsend_proposals.resize(2); - selfsend_proposals[0] = self_spend_payment_proposal1_amnt_1; - selfsend_proposals[1] = self_spend_payment_proposal1_amnt_1; - EXPECT_ANY_THROW(finalize_outputs_for_test(normal_proposals, selfsend_proposals)); - - // 2 self-send outputs (shared ephemeral pubkey), >0 change: error - in_amount = 3 + fee; + selfsend_proposals[0] = ex_self_spend_payment_proposal_amnt_1; + selfsend_proposals[1] = ex_change_payment_proposal_amnt_1; + selfsend_proposals[1].enote_ephemeral_privkey = selfsend_proposals[0].enote_ephemeral_privkey; + selfsend_proposals[1].destination.addr_Dbase = selfsend_proposals[0].destination.addr_Dbase; + ASSERT_ANY_THROW(finalize_outputs_for_test()); + + // 2 auxiliary self-send outputs (shared ephemeral pubkey), 0 change: error + nonzero_change = false; normal_proposals.clear(); selfsend_proposals.resize(2); - selfsend_proposals[0] = self_spend_payment_proposal1_amnt_1; - selfsend_proposals[1] = self_spend_payment_proposal1_amnt_1; //change = 1 - EXPECT_ANY_THROW(finalize_outputs_for_test(normal_proposals, selfsend_proposals)); - - // 2 self-send outputs, 0 change: 3 outputs (1 dummy) - in_amount = 2 + fee; + selfsend_proposals[0] = aux_self_spend_payment_proposal_amnt_1; + selfsend_proposals[1] = aux_change_payment_proposal_amnt_1; + selfsend_proposals[1].enote_ephemeral_privkey = selfsend_proposals[0].enote_ephemeral_privkey; + selfsend_proposals[1].destination.addr_Dbase = selfsend_proposals[0].destination.addr_Dbase; + ASSERT_ANY_THROW(finalize_outputs_for_test()); + + // 1 exclusive, 1 auxiliary self-send outputs (shared ephemeral pubkey), >0 change: error + nonzero_change = true; normal_proposals.clear(); selfsend_proposals.resize(2); - selfsend_proposals[0] = self_spend_payment_proposal1_amnt_1; - selfsend_proposals[1] = self_spend_payment_proposal2_amnt_1; - EXPECT_NO_THROW(finalize_outputs_for_test(normal_proposals, selfsend_proposals)); - EXPECT_TRUE(normal_proposals.size() == 1); - EXPECT_TRUE(selfsend_proposals.size() == 2); - EXPECT_TRUE(normal_proposals[0].amount == 0); - check_is_owned(selfsend_proposals[0], keys, j_selfspend, 1, JamtisEnoteType::SELF_SPEND); - check_is_owned(selfsend_proposals[1], keys, j_selfspend, 1, JamtisEnoteType::SELF_SPEND); - - // 2 self-send outputs, >0 change: 3 outputs (1 change) - in_amount = 3 + fee; + selfsend_proposals[0] = ex_self_spend_payment_proposal_amnt_1; + selfsend_proposals[1] = aux_change_payment_proposal_amnt_1; //change = 1 + selfsend_proposals[1].enote_ephemeral_privkey = selfsend_proposals[0].enote_ephemeral_privkey; + selfsend_proposals[1].destination.addr_Dbase = selfsend_proposals[0].destination.addr_Dbase; + ASSERT_ANY_THROW(finalize_outputs_for_test()); + + // 2 auxiliary self-send outputs, 0 change: 3 outputs (1 exclusive change) + nonzero_change = false; normal_proposals.clear(); selfsend_proposals.resize(2); - selfsend_proposals[0] = self_spend_payment_proposal1_amnt_1; - selfsend_proposals[1] = self_spend_payment_proposal2_amnt_1; //change = 1 - EXPECT_NO_THROW(finalize_outputs_for_test(normal_proposals, selfsend_proposals)); - EXPECT_TRUE(normal_proposals.size() == 0); - EXPECT_TRUE(selfsend_proposals.size() == 3); - check_is_owned(selfsend_proposals[0], keys, j_selfspend, 1, JamtisEnoteType::SELF_SPEND); - check_is_owned(selfsend_proposals[1], keys, j_selfspend, 1, JamtisEnoteType::SELF_SPEND); - check_is_owned(selfsend_proposals[2], keys, j_change, 1, JamtisEnoteType::CHANGE); + selfsend_proposals[0] = aux_self_spend_payment_proposal_amnt_1; + selfsend_proposals[1] = aux_change_payment_proposal_amnt_1; + ASSERT_NO_THROW(finalize_outputs_for_test()); + ASSERT_EQ(0, normal_proposals.size()); + ASSERT_EQ(3, selfsend_proposals.size()); + ASSERT_TRUE(selfsend_proposals[2].amount == 0); + check_is_owned(selfsend_proposals[0], keys, j_selfspend, 1, JamtisEnoteType::AUXILIARY_SELF_SPEND); + check_is_owned(selfsend_proposals[1], keys, j_change, 1, JamtisEnoteType::AUXILIARY_CHANGE); + check_is_owned(selfsend_proposals[2], keys, j_change, nonzero_change, JamtisEnoteType::EXCLUSIVE_CHANGE); + + // 1 auxiliary, 1 exclusive self-send outputs, 0 change: 3 outputs + nonzero_change = false; + normal_proposals.clear(); + selfsend_proposals.resize(2); + selfsend_proposals[0] = aux_self_spend_payment_proposal_amnt_1; + selfsend_proposals[1] = ex_change_payment_proposal_amnt_1; + ASSERT_NO_THROW(finalize_outputs_for_test()); + ASSERT_EQ(0, normal_proposals.size()); + ASSERT_EQ(3, selfsend_proposals.size()); + check_is_owned(selfsend_proposals[0], keys, j_selfspend, 1, JamtisEnoteType::AUXILIARY_SELF_SPEND); + check_is_owned(selfsend_proposals[1], keys, j_change, 1, JamtisEnoteType::EXCLUSIVE_CHANGE); + check_is_owned(selfsend_proposals[2], keys, j_change, nonzero_change, JamtisEnoteType::AUXILIARY_CHANGE); + + // 2 auxiliary self-send outputs, >0 change: 3 outputs (1 change) + nonzero_change = true; + normal_proposals.clear(); + selfsend_proposals.resize(2); + selfsend_proposals[0] = aux_self_spend_payment_proposal_amnt_1; + selfsend_proposals[1] = aux_change_payment_proposal_amnt_1; //change = 1 + ASSERT_NO_THROW(finalize_outputs_for_test()); + ASSERT_TRUE(normal_proposals.size() == 0); + ASSERT_TRUE(selfsend_proposals.size() == 3); + check_is_owned(selfsend_proposals[0], keys, j_selfspend, 1, JamtisEnoteType::AUXILIARY_SELF_SPEND); + check_is_owned(selfsend_proposals[1], keys, j_change, 1, JamtisEnoteType::AUXILIARY_CHANGE); + check_is_owned(selfsend_proposals[2], keys, j_change, nonzero_change, JamtisEnoteType::EXCLUSIVE_CHANGE); } //------------------------------------------------------------------------------------------------------------------- TEST(seraphis_basic, tx_extra) @@ -1255,6 +1358,9 @@ TEST(seraphis_basic, txtype_squashed_v1) out_amounts.push_back(3); } + // npbits + const std::uint8_t num_primary_view_tag_bits{7}; + // set fee const DiscretizedFee discretized_transaction_fee{num_ins_outs}; rct::xmr_amount real_transaction_fee; @@ -1288,6 +1394,7 @@ TEST(seraphis_basic, txtype_squashed_v1) .bin_radius = static_cast(sp_ref_set_config.bin_radius), .num_bin_members = static_cast(sp_ref_set_config.num_bin_members) }, + num_primary_view_tag_bits, 3, in_legacy_amounts, in_sp_amounts, diff --git a/tests/unit_tests/seraphis_enote_scanning.cpp b/tests/unit_tests/seraphis_enote_scanning.cpp index bac13f334c..d235ccaaee 100644 --- a/tests/unit_tests/seraphis_enote_scanning.cpp +++ b/tests/unit_tests/seraphis_enote_scanning.cpp @@ -35,9 +35,9 @@ #include "ringct/rctTypes.h" #include "seraphis_core/binned_reference_set.h" #include "seraphis_core/discretized_fee.h" +#include "seraphis_core/jamtis_account_secrets.h" #include "seraphis_core/jamtis_address_tag_utils.h" #include "seraphis_core/jamtis_address_utils.h" -#include "seraphis_core/jamtis_core_utils.h" #include "seraphis_core/jamtis_destination.h" #include "seraphis_core/jamtis_enote_utils.h" #include "seraphis_core/jamtis_payment_proposal.h" @@ -251,26 +251,24 @@ TEST(seraphis_enote_scanning, trivial_ledger) make_jamtis_mock_keys(user_keys); // make user address - address_index_t j; - j = sp::jamtis::gen_address_index(); + const address_index_t j = sp::jamtis::gen_address_index(); JamtisDestinationV1 user_address; - ASSERT_NO_THROW(make_jamtis_destination_v1(user_keys.K_1_base, - user_keys.xK_ua, - user_keys.xK_fr, - user_keys.s_ga, + ASSERT_NO_THROW(make_address_for_user(user_keys, j, user_address)); // make enote for user const rct::xmr_amount enote_amount{1}; const rct::key mock_input_context{rct::skGen()}; + const std::uint8_t num_primary_view_tag_bits{13}; SpTxSupplementV1 mock_tx_supplement{}; const JamtisPaymentProposalV1 payment_proposal{ .destination = user_address, .amount = enote_amount, .enote_ephemeral_privkey = crypto::x25519_secret_key_gen(), + .num_primary_view_tag_bits = num_primary_view_tag_bits, .partial_memo = mock_tx_supplement.tx_extra }; SpOutputProposalV1 output_proposal; @@ -279,6 +277,7 @@ TEST(seraphis_enote_scanning, trivial_ledger) SpEnoteV1 single_enote; get_enote_v1(output_proposal, single_enote); mock_tx_supplement.output_enote_ephemeral_pubkeys.emplace_back(output_proposal.enote_ephemeral_pubkey); + mock_tx_supplement.num_primary_view_tag_bits = num_primary_view_tag_bits; // add enote to mock ledger context as a coinbase enote MockLedgerContext ledger_context{0, 0}; @@ -294,11 +293,11 @@ TEST(seraphis_enote_scanning, trivial_ledger) .max_chunk_size_hint = 1, .max_partialscan_attempts = 0 }; - const EnoteFindingContextUnconfirmedMockSp enote_finding_context_unconfirmed{ledger_context, user_keys.xk_fr}; - const EnoteFindingContextLedgerMockSp enote_finding_context_ledger{ledger_context, user_keys.xk_fr}; + const EnoteFindingContextUnconfirmedMockSp enote_finding_context_unconfirmed{ledger_context, user_keys.d_fa}; + const EnoteFindingContextLedgerMockSp enote_finding_context_ledger{ledger_context, user_keys.d_fa}; scanning::ScanContextNonLedgerSimple scan_context_unconfirmed{enote_finding_context_unconfirmed}; scanning::ScanContextLedgerSimple scan_context_ledger{enote_finding_context_ledger}; - ChunkConsumerMockSp chunk_consumer{user_keys.K_1_base, user_keys.k_vb, user_enote_store}; + ChunkConsumerMockSp chunk_consumer{user_keys.K_s_base, user_keys.k_vb, user_enote_store}; ASSERT_NO_THROW(refresh_enote_store(refresh_config, scan_context_unconfirmed, @@ -310,8 +309,9 @@ TEST(seraphis_enote_scanning, trivial_ledger) ASSERT_TRUE(try_get_enote_record_v1(single_enote, output_proposal.enote_ephemeral_pubkey, + num_primary_view_tag_bits, mock_input_context, - user_keys.K_1_base, + user_keys.K_s_base, user_keys.k_vb, single_enote_record)); @@ -330,6 +330,8 @@ TEST(seraphis_enote_scanning, simple_ledger_1) .max_partialscan_attempts = 0 }; + const std::uint8_t num_primary_view_tag_bits{5}; + // 2. user keys jamtis_mock_keys user_keys_A; make_jamtis_mock_keys(user_keys_A); @@ -344,7 +346,7 @@ TEST(seraphis_enote_scanning, simple_ledger_1) // 1. one coinbase to user MockLedgerContext ledger_context{0, 0}; SpEnoteStore enote_store_A{0, 0, 0}; - send_sp_coinbase_amounts_to_users({{1}}, {destination_A}, ledger_context); + send_sp_coinbase_amounts_to_users({{1}}, {destination_A}, num_primary_view_tag_bits, ledger_context); refresh_user_enote_store(user_keys_A, refresh_config, ledger_context, enote_store_A); ASSERT_TRUE(get_balance(enote_store_A, {SpEnoteOriginStatus::OFFCHAIN, SpEnoteOriginStatus::UNCONFIRMED}, @@ -364,6 +366,8 @@ TEST(seraphis_enote_scanning, simple_ledger_2) .max_partialscan_attempts = 0 }; + const std::uint8_t num_primary_view_tag_bits{9}; + // 2. user keys jamtis_mock_keys user_keys_A; make_jamtis_mock_keys(user_keys_A); @@ -378,7 +382,7 @@ TEST(seraphis_enote_scanning, simple_ledger_2) // 2. two coinbase to user (one coinbase tx) MockLedgerContext ledger_context{0, 0}; SpEnoteStore enote_store_A{0, 0, 0}; - send_sp_coinbase_amounts_to_users({{1, 1}}, {destination_A}, ledger_context); + send_sp_coinbase_amounts_to_users({{1, 1}}, {destination_A}, num_primary_view_tag_bits, ledger_context); refresh_user_enote_store(user_keys_A, refresh_config, ledger_context, enote_store_A); ASSERT_TRUE(get_balance(enote_store_A, {SpEnoteOriginStatus::OFFCHAIN, SpEnoteOriginStatus::UNCONFIRMED}, @@ -398,6 +402,8 @@ TEST(seraphis_enote_scanning, simple_ledger_3) .max_partialscan_attempts = 0 }; + const std::uint8_t num_primary_view_tag_bits{8}; + // 2. user keys jamtis_mock_keys user_keys_A; jamtis_mock_keys user_keys_B; @@ -417,7 +423,10 @@ TEST(seraphis_enote_scanning, simple_ledger_3) MockLedgerContext ledger_context{0, 0}; SpEnoteStore enote_store_A{0, 0, 0}; SpEnoteStore enote_store_B{0, 0, 0}; - send_sp_coinbase_amounts_to_users({{1}, {2}}, {destination_A, destination_B}, ledger_context); + send_sp_coinbase_amounts_to_users({{1}, {2}}, + {destination_A, destination_B}, + num_primary_view_tag_bits, + ledger_context); refresh_user_enote_store(user_keys_A, refresh_config, ledger_context, enote_store_A); refresh_user_enote_store(user_keys_B, refresh_config, ledger_context, enote_store_B); @@ -442,6 +451,8 @@ TEST(seraphis_enote_scanning, simple_ledger_4) .max_partialscan_attempts = 0 }; + const std::uint8_t num_primary_view_tag_bits{15}; + // 2. user keys jamtis_mock_keys user_keys_A; make_jamtis_mock_keys(user_keys_A); @@ -456,7 +467,7 @@ TEST(seraphis_enote_scanning, simple_ledger_4) // 4. two coinbase to user, search between each send (two coinbase txs i.e. two blocks) MockLedgerContext ledger_context{0, 0}; SpEnoteStore enote_store_A{0, 0, 0}; - send_sp_coinbase_amounts_to_users({{1}}, {destination_A}, ledger_context); + send_sp_coinbase_amounts_to_users({{1}}, {destination_A}, num_primary_view_tag_bits, ledger_context); refresh_user_enote_store(user_keys_A, refresh_config, ledger_context, enote_store_A); ASSERT_TRUE(get_balance(enote_store_A, {SpEnoteOriginStatus::OFFCHAIN, SpEnoteOriginStatus::UNCONFIRMED}, @@ -464,7 +475,7 @@ TEST(seraphis_enote_scanning, simple_ledger_4) ASSERT_TRUE(get_balance(enote_store_A, {SpEnoteOriginStatus::ONCHAIN}, {SpEnoteSpentStatus::SPENT_ONCHAIN}) == 1); - send_sp_coinbase_amounts_to_users({{2}}, {destination_A}, ledger_context); + send_sp_coinbase_amounts_to_users({{2}}, {destination_A}, num_primary_view_tag_bits, ledger_context); refresh_user_enote_store(user_keys_A, refresh_config, ledger_context, enote_store_A); ASSERT_TRUE(get_balance(enote_store_A, {SpEnoteOriginStatus::OFFCHAIN, SpEnoteOriginStatus::UNCONFIRMED}, @@ -484,6 +495,8 @@ TEST(seraphis_enote_scanning, simple_ledger_5) .max_partialscan_attempts = 0 }; + const std::uint8_t num_primary_view_tag_bits{8}; + // 2. user keys jamtis_mock_keys user_keys_A; make_jamtis_mock_keys(user_keys_A); @@ -504,9 +517,9 @@ TEST(seraphis_enote_scanning, simple_ledger_5) ASSERT_TRUE(get_balance(enote_store_A, {SpEnoteOriginStatus::ONCHAIN}, {SpEnoteSpentStatus::SPENT_ONCHAIN}) == 0); - send_sp_coinbase_amounts_to_users({{1}}, {destination_A}, ledger_context); - send_sp_coinbase_amounts_to_users({{2}}, {destination_A}, ledger_context); - send_sp_coinbase_amounts_to_users({{4}}, {destination_A}, ledger_context); + send_sp_coinbase_amounts_to_users({{1}}, {destination_A}, num_primary_view_tag_bits, ledger_context); + send_sp_coinbase_amounts_to_users({{2}}, {destination_A}, num_primary_view_tag_bits, ledger_context); + send_sp_coinbase_amounts_to_users({{4}}, {destination_A}, num_primary_view_tag_bits, ledger_context); refresh_user_enote_store(user_keys_A, refresh_config, ledger_context, enote_store_A); ASSERT_TRUE(get_balance(enote_store_A, {SpEnoteOriginStatus::OFFCHAIN, SpEnoteOriginStatus::UNCONFIRMED}, @@ -522,7 +535,7 @@ TEST(seraphis_enote_scanning, simple_ledger_5) ASSERT_TRUE(get_balance(enote_store_A, {SpEnoteOriginStatus::ONCHAIN}, {SpEnoteSpentStatus::SPENT_ONCHAIN}) == 1); - send_sp_coinbase_amounts_to_users({{8}}, {destination_A}, ledger_context); + send_sp_coinbase_amounts_to_users({{8}}, {destination_A}, num_primary_view_tag_bits, ledger_context); refresh_user_enote_store(user_keys_A, refresh_config, ledger_context, enote_store_A); ASSERT_TRUE(get_balance(enote_store_A, {SpEnoteOriginStatus::OFFCHAIN, SpEnoteOriginStatus::UNCONFIRMED}, @@ -542,6 +555,8 @@ TEST(seraphis_enote_scanning, simple_ledger_6) .max_partialscan_attempts = 0 }; + const std::uint8_t num_primary_view_tag_bits{1}; + // 2. user keys jamtis_mock_keys user_keys_A; make_jamtis_mock_keys(user_keys_A); @@ -564,9 +579,9 @@ TEST(seraphis_enote_scanning, simple_ledger_6) ASSERT_TRUE(get_balance(enote_store_A, {SpEnoteOriginStatus::ONCHAIN}, {SpEnoteSpentStatus::SPENT_ONCHAIN}) == 0); - send_sp_coinbase_amounts_to_users({{1}}, {destination_A}, ledger_context); - send_sp_coinbase_amounts_to_users({{2}}, {destination_A}, ledger_context); - send_sp_coinbase_amounts_to_users({{4}}, {destination_A}, ledger_context); + send_sp_coinbase_amounts_to_users({{1}}, {destination_A}, num_primary_view_tag_bits, ledger_context); + send_sp_coinbase_amounts_to_users({{2}}, {destination_A}, num_primary_view_tag_bits, ledger_context); + send_sp_coinbase_amounts_to_users({{4}}, {destination_A}, num_primary_view_tag_bits, ledger_context); refresh_user_enote_store(user_keys_A, refresh_config, ledger_context, enote_store_A); ASSERT_TRUE(get_balance(enote_store_A, {SpEnoteOriginStatus::OFFCHAIN, SpEnoteOriginStatus::UNCONFIRMED}, @@ -582,7 +597,7 @@ TEST(seraphis_enote_scanning, simple_ledger_6) ASSERT_TRUE(get_balance(enote_store_A, {SpEnoteOriginStatus::ONCHAIN}, {SpEnoteSpentStatus::SPENT_ONCHAIN}) == 0); - send_sp_coinbase_amounts_to_users({{8}}, {destination_A}, ledger_context); + send_sp_coinbase_amounts_to_users({{8}}, {destination_A}, num_primary_view_tag_bits, ledger_context); refresh_user_enote_store(user_keys_A, refresh_config, ledger_context, enote_store_A); ASSERT_TRUE(get_balance(enote_store_A, {SpEnoteOriginStatus::OFFCHAIN, SpEnoteOriginStatus::UNCONFIRMED}, @@ -617,6 +632,8 @@ TEST(seraphis_enote_scanning, simple_ledger_7) .density_factor = 1 }; + const std::uint8_t num_primary_view_tag_bits{3}; + // 2. user keys jamtis_mock_keys user_keys_A; make_jamtis_mock_keys(user_keys_A); @@ -637,19 +654,19 @@ TEST(seraphis_enote_scanning, simple_ledger_7) {SpEnoteSpentStatus::SPENT_ONCHAIN}) == 0); // send funds: blocks 0 - 12, refresh - send_sp_coinbase_amounts_to_users({{1}}, {destination_A}, ledger_context); //block 0 - send_sp_coinbase_amounts_to_users({{1}}, {destination_A}, ledger_context); //block 1 - send_sp_coinbase_amounts_to_users({{1}}, {destination_A}, ledger_context); //block 2 - send_sp_coinbase_amounts_to_users({{1}}, {destination_A}, ledger_context); //block 3 - send_sp_coinbase_amounts_to_users({{1}}, {destination_A}, ledger_context); //block 4 - send_sp_coinbase_amounts_to_users({{1}}, {destination_A}, ledger_context); //block 5 - send_sp_coinbase_amounts_to_users({{1}}, {destination_A}, ledger_context); //block 6 - send_sp_coinbase_amounts_to_users({{1}}, {destination_A}, ledger_context); //block 7 - send_sp_coinbase_amounts_to_users({{1}}, {destination_A}, ledger_context); //block 8 - send_sp_coinbase_amounts_to_users({{1}}, {destination_A}, ledger_context); //block 9 - send_sp_coinbase_amounts_to_users({{1}}, {destination_A}, ledger_context); //block 10 - send_sp_coinbase_amounts_to_users({{1}}, {destination_A}, ledger_context); //block 11 - send_sp_coinbase_amounts_to_users({{1}}, {destination_A}, ledger_context); //block 12 + send_sp_coinbase_amounts_to_users({{1}}, {destination_A}, num_primary_view_tag_bits, ledger_context); //block 0 + send_sp_coinbase_amounts_to_users({{1}}, {destination_A}, num_primary_view_tag_bits, ledger_context); //block 1 + send_sp_coinbase_amounts_to_users({{1}}, {destination_A}, num_primary_view_tag_bits, ledger_context); //block 2 + send_sp_coinbase_amounts_to_users({{1}}, {destination_A}, num_primary_view_tag_bits, ledger_context); //block 3 + send_sp_coinbase_amounts_to_users({{1}}, {destination_A}, num_primary_view_tag_bits, ledger_context); //block 4 + send_sp_coinbase_amounts_to_users({{1}}, {destination_A}, num_primary_view_tag_bits, ledger_context); //block 5 + send_sp_coinbase_amounts_to_users({{1}}, {destination_A}, num_primary_view_tag_bits, ledger_context); //block 6 + send_sp_coinbase_amounts_to_users({{1}}, {destination_A}, num_primary_view_tag_bits, ledger_context); //block 7 + send_sp_coinbase_amounts_to_users({{1}}, {destination_A}, num_primary_view_tag_bits, ledger_context); //block 8 + send_sp_coinbase_amounts_to_users({{1}}, {destination_A}, num_primary_view_tag_bits, ledger_context); //block 9 + send_sp_coinbase_amounts_to_users({{1}}, {destination_A}, num_primary_view_tag_bits, ledger_context); //block 10 + send_sp_coinbase_amounts_to_users({{1}}, {destination_A}, num_primary_view_tag_bits, ledger_context); //block 11 + send_sp_coinbase_amounts_to_users({{1}}, {destination_A}, num_primary_view_tag_bits, ledger_context); //block 12 refresh_user_enote_store(user_keys_A, refresh_config, ledger_context, enote_store_A); ASSERT_TRUE(get_balance(enote_store_A, {SpEnoteOriginStatus::OFFCHAIN, SpEnoteOriginStatus::UNCONFIRMED}, @@ -667,11 +684,11 @@ TEST(seraphis_enote_scanning, simple_ledger_7) {SpEnoteSpentStatus::SPENT_ONCHAIN}) == 4); // send funds: blocks 8 - 12, refresh - send_sp_coinbase_amounts_to_users({{2}}, {destination_A}, ledger_context); //block 8 - send_sp_coinbase_amounts_to_users({{2}}, {destination_A}, ledger_context); //block 9 - send_sp_coinbase_amounts_to_users({{2}}, {destination_A}, ledger_context); //block 10 - send_sp_coinbase_amounts_to_users({{2}}, {destination_A}, ledger_context); //block 11 - send_sp_coinbase_amounts_to_users({{2}}, {destination_A}, ledger_context); //block 12 + send_sp_coinbase_amounts_to_users({{2}}, {destination_A}, num_primary_view_tag_bits, ledger_context); //block 8 + send_sp_coinbase_amounts_to_users({{2}}, {destination_A}, num_primary_view_tag_bits, ledger_context); //block 9 + send_sp_coinbase_amounts_to_users({{2}}, {destination_A}, num_primary_view_tag_bits, ledger_context); //block 10 + send_sp_coinbase_amounts_to_users({{2}}, {destination_A}, num_primary_view_tag_bits, ledger_context); //block 11 + send_sp_coinbase_amounts_to_users({{2}}, {destination_A}, num_primary_view_tag_bits, ledger_context); //block 12 refresh_user_enote_store(user_keys_A, refresh_config, ledger_context, enote_store_A); ASSERT_TRUE(get_balance(enote_store_A, {SpEnoteOriginStatus::OFFCHAIN, SpEnoteOriginStatus::UNCONFIRMED}, @@ -683,17 +700,17 @@ TEST(seraphis_enote_scanning, simple_ledger_7) ledger_context.pop_blocks(11); // send funds: blocks 2 - 12, refresh - send_sp_coinbase_amounts_to_users({{1}}, {destination_A}, ledger_context); //block 2 - send_sp_coinbase_amounts_to_users({{1}}, {destination_A}, ledger_context); //block 3 - send_sp_coinbase_amounts_to_users({{1}}, {destination_A}, ledger_context); //block 4 - send_sp_coinbase_amounts_to_users({{1}}, {destination_A}, ledger_context); //block 5 - send_sp_coinbase_amounts_to_users({{1}}, {destination_A}, ledger_context); //block 6 - send_sp_coinbase_amounts_to_users({{1}}, {destination_A}, ledger_context); //block 7 - send_sp_coinbase_amounts_to_users({{1}}, {destination_A}, ledger_context); //block 8 - send_sp_coinbase_amounts_to_users({{1}}, {destination_A}, ledger_context); //block 9 - send_sp_coinbase_amounts_to_users({{1}}, {destination_A}, ledger_context); //block 10 - send_sp_coinbase_amounts_to_users({{1}}, {destination_A}, ledger_context); //block 11 - send_sp_coinbase_amounts_to_users({{1}}, {destination_A}, ledger_context); //block 12 + send_sp_coinbase_amounts_to_users({{1}}, {destination_A}, num_primary_view_tag_bits, ledger_context); //block 2 + send_sp_coinbase_amounts_to_users({{1}}, {destination_A}, num_primary_view_tag_bits, ledger_context); //block 3 + send_sp_coinbase_amounts_to_users({{1}}, {destination_A}, num_primary_view_tag_bits, ledger_context); //block 4 + send_sp_coinbase_amounts_to_users({{1}}, {destination_A}, num_primary_view_tag_bits, ledger_context); //block 5 + send_sp_coinbase_amounts_to_users({{1}}, {destination_A}, num_primary_view_tag_bits, ledger_context); //block 6 + send_sp_coinbase_amounts_to_users({{1}}, {destination_A}, num_primary_view_tag_bits, ledger_context); //block 7 + send_sp_coinbase_amounts_to_users({{1}}, {destination_A}, num_primary_view_tag_bits, ledger_context); //block 8 + send_sp_coinbase_amounts_to_users({{1}}, {destination_A}, num_primary_view_tag_bits, ledger_context); //block 9 + send_sp_coinbase_amounts_to_users({{1}}, {destination_A}, num_primary_view_tag_bits, ledger_context); //block 10 + send_sp_coinbase_amounts_to_users({{1}}, {destination_A}, num_primary_view_tag_bits, ledger_context); //block 11 + send_sp_coinbase_amounts_to_users({{1}}, {destination_A}, num_primary_view_tag_bits, ledger_context); //block 12 refresh_user_enote_store(user_keys_A, refresh_config, ledger_context, enote_store_A); ASSERT_TRUE(get_balance(enote_store_A, {SpEnoteOriginStatus::OFFCHAIN, SpEnoteOriginStatus::UNCONFIRMED}, @@ -713,6 +730,8 @@ TEST(seraphis_enote_scanning, simple_ledger_locked) .max_partialscan_attempts = 0 }; + const std::uint8_t num_primary_view_tag_bits{16}; + // 2. user keys jamtis_mock_keys user_keys_A; make_jamtis_mock_keys(user_keys_A); @@ -741,7 +760,7 @@ TEST(seraphis_enote_scanning, simple_ledger_locked) ASSERT_TRUE(get_received_sum(enote_store_PV_A, {SpEnoteOriginStatus::ONCHAIN}, {BalanceExclusion::ORIGIN_LEDGER_LOCKED}) == 0); - send_sp_coinbase_amounts_to_users({{1}}, {destination_A}, ledger_context); + send_sp_coinbase_amounts_to_users({{1}}, {destination_A}, num_primary_view_tag_bits, ledger_context); refresh_user_enote_store(user_keys_A, refresh_config, ledger_context, enote_store_A); refresh_user_enote_store_PV(user_keys_A, refresh_config, ledger_context, enote_store_PV_A); @@ -754,7 +773,7 @@ TEST(seraphis_enote_scanning, simple_ledger_locked) ASSERT_TRUE(get_received_sum(enote_store_PV_A, {SpEnoteOriginStatus::ONCHAIN}, {BalanceExclusion::ORIGIN_LEDGER_LOCKED}) == 0); //amount 1 locked - send_sp_coinbase_amounts_to_users({{2}}, {destination_A}, ledger_context); + send_sp_coinbase_amounts_to_users({{2}}, {destination_A}, num_primary_view_tag_bits, ledger_context); refresh_user_enote_store(user_keys_A, refresh_config, ledger_context, enote_store_A); refresh_user_enote_store_PV(user_keys_A, refresh_config, ledger_context, enote_store_PV_A); @@ -790,6 +809,7 @@ TEST(seraphis_enote_scanning, basic_ledger_tx_passing_1) const std::size_t fee_per_tx_weight{0}; // 0 fee here const std::size_t ref_set_decomp_n{2}; const std::size_t ref_set_decomp_m{2}; + const std::uint8_t num_primary_view_tag_bits{4}; const scanning::ScanMachineConfig refresh_config{ .reorg_avoidance_increment = 1, @@ -826,7 +846,7 @@ TEST(seraphis_enote_scanning, basic_ledger_tx_passing_1) SpEnoteStore enote_store_B{0, 0, 0}; const InputSelectorMockV1 input_selector_A{enote_store_A}; const InputSelectorMockV1 input_selector_B{enote_store_B}; - send_sp_coinbase_amounts_to_users({{1, 1, 1, 1}}, {destination_A}, ledger_context); + send_sp_coinbase_amounts_to_users({{1, 1, 1, 1}}, {destination_A}, num_primary_view_tag_bits, ledger_context); refresh_user_enote_store(user_keys_A, refresh_config, ledger_context, enote_store_A); transfer_funds_single_mock_v1_unconfirmed_sp_only(user_keys_A, @@ -835,6 +855,7 @@ TEST(seraphis_enote_scanning, basic_ledger_tx_passing_1) fee_per_tx_weight, max_inputs, {{2, destination_B, TxExtra{}}}, + num_primary_view_tag_bits, ref_set_decomp_n, ref_set_decomp_m, bin_config, @@ -891,6 +912,7 @@ TEST(seraphis_enote_scanning, basic_ledger_tx_passing_2) const std::size_t fee_per_tx_weight{0}; // 0 fee here const std::size_t ref_set_decomp_n{2}; const std::size_t ref_set_decomp_m{2}; + const std::uint8_t num_primary_view_tag_bits{11}; const scanning::ScanMachineConfig refresh_config{ .reorg_avoidance_increment = 1, @@ -925,7 +947,7 @@ TEST(seraphis_enote_scanning, basic_ledger_tx_passing_2) SpEnoteStore enote_store_B{0, 0, 0}; const InputSelectorMockV1 input_selector_A{enote_store_A}; const InputSelectorMockV1 input_selector_B{enote_store_B}; - send_sp_coinbase_amounts_to_users({{0, 0, 0, 8}}, {destination_A}, ledger_context); + send_sp_coinbase_amounts_to_users({{0, 0, 0, 8}}, {destination_A}, num_primary_view_tag_bits, ledger_context); refresh_user_enote_store(user_keys_A, refresh_config, ledger_context, enote_store_A); transfer_funds_single_mock_v1_unconfirmed_sp_only(user_keys_A, @@ -934,6 +956,7 @@ TEST(seraphis_enote_scanning, basic_ledger_tx_passing_2) fee_per_tx_weight, max_inputs, {{3, destination_B, TxExtra{}}}, + num_primary_view_tag_bits, ref_set_decomp_n, ref_set_decomp_m, bin_config, @@ -982,6 +1005,7 @@ TEST(seraphis_enote_scanning, basic_ledger_tx_passing_3) const std::size_t fee_per_tx_weight{0}; // 0 fee here const std::size_t ref_set_decomp_n{2}; const std::size_t ref_set_decomp_m{2}; + const std::uint8_t num_primary_view_tag_bits{12}; const scanning::ScanMachineConfig refresh_config{ .reorg_avoidance_increment = 1, @@ -1017,7 +1041,7 @@ TEST(seraphis_enote_scanning, basic_ledger_tx_passing_3) SpEnoteStore enote_store_B{0, 0, 0}; const InputSelectorMockV1 input_selector_A{enote_store_A}; const InputSelectorMockV1 input_selector_B{enote_store_B}; - send_sp_coinbase_amounts_to_users({{0, 0, 0, 8}}, {destination_A}, ledger_context); + send_sp_coinbase_amounts_to_users({{0, 0, 0, 8}}, {destination_A}, num_primary_view_tag_bits, ledger_context); refresh_user_enote_store(user_keys_A, refresh_config, ledger_context, enote_store_A); transfer_funds_single_mock_v1_unconfirmed_sp_only(user_keys_A, @@ -1026,6 +1050,7 @@ TEST(seraphis_enote_scanning, basic_ledger_tx_passing_3) fee_per_tx_weight, max_inputs, {{3, destination_B, TxExtra{}}}, + num_primary_view_tag_bits, ref_set_decomp_n, ref_set_decomp_m, bin_config, @@ -1047,7 +1072,7 @@ TEST(seraphis_enote_scanning, basic_ledger_tx_passing_3) ASSERT_TRUE(get_balance(enote_store_B, {SpEnoteOriginStatus::ONCHAIN, SpEnoteOriginStatus::UNCONFIRMED}, {SpEnoteSpentStatus::SPENT_ONCHAIN, SpEnoteSpentStatus::SPENT_UNCONFIRMED}) == 3); - send_sp_coinbase_amounts_to_users({{8}}, {destination_B}, ledger_context); + send_sp_coinbase_amounts_to_users({{8}}, {destination_B}, num_primary_view_tag_bits, ledger_context); refresh_user_enote_store(user_keys_A, refresh_config, ledger_context, enote_store_A); refresh_user_enote_store(user_keys_B, refresh_config, ledger_context, enote_store_B); @@ -1074,6 +1099,7 @@ TEST(seraphis_enote_scanning, basic_ledger_tx_passing_4) const std::size_t fee_per_tx_weight{0}; // 0 fee here const std::size_t ref_set_decomp_n{2}; const std::size_t ref_set_decomp_m{2}; + const std::uint8_t num_primary_view_tag_bits{3}; const scanning::ScanMachineConfig refresh_config{ .reorg_avoidance_increment = 1, @@ -1109,7 +1135,10 @@ TEST(seraphis_enote_scanning, basic_ledger_tx_passing_4) SpEnoteStore enote_store_B{0, 0, 0}; const InputSelectorMockV1 input_selector_A{enote_store_A}; const InputSelectorMockV1 input_selector_B{enote_store_B}; - send_sp_coinbase_amounts_to_users({{10, 10, 10, 10}}, {destination_A}, ledger_context); + send_sp_coinbase_amounts_to_users({{10, 10, 10, 10}}, + {destination_A}, + num_primary_view_tag_bits, + ledger_context); refresh_user_enote_store(user_keys_A, refresh_config, ledger_context, enote_store_A); transfer_funds_single_mock_v1_unconfirmed_sp_only(user_keys_A, @@ -1118,6 +1147,7 @@ TEST(seraphis_enote_scanning, basic_ledger_tx_passing_4) fee_per_tx_weight, max_inputs, {{20, destination_B, TxExtra{}}}, + num_primary_view_tag_bits, ref_set_decomp_n, ref_set_decomp_m, bin_config, @@ -1162,6 +1192,7 @@ TEST(seraphis_enote_scanning, basic_ledger_tx_passing_4) fee_per_tx_weight, max_inputs, {{30, destination_B, TxExtra{}}}, + num_primary_view_tag_bits, ref_set_decomp_n, ref_set_decomp_m, bin_config, @@ -1206,6 +1237,7 @@ TEST(seraphis_enote_scanning, basic_ledger_tx_passing_4) fee_per_tx_weight, max_inputs, {{3, destination_B, TxExtra{}}}, + num_primary_view_tag_bits, ref_set_decomp_n, ref_set_decomp_m, bin_config, @@ -1254,6 +1286,7 @@ TEST(seraphis_enote_scanning, basic_ledger_tx_passing_5) const std::size_t fee_per_tx_weight{0}; // 0 fee here const std::size_t ref_set_decomp_n{2}; const std::size_t ref_set_decomp_m{2}; + const std::uint8_t num_primary_view_tag_bits{10}; const scanning::ScanMachineConfig refresh_config{ .reorg_avoidance_increment = 1, @@ -1289,7 +1322,7 @@ TEST(seraphis_enote_scanning, basic_ledger_tx_passing_5) SpEnoteStore enote_store_B{2, 0, 0}; const InputSelectorMockV1 input_selector_A{enote_store_A}; const InputSelectorMockV1 input_selector_B{enote_store_B}; - send_sp_coinbase_amounts_to_users({{10, 10, 10, 10}}, {destination_A}, ledger_context); + send_sp_coinbase_amounts_to_users({{10, 10, 10, 10}}, {destination_A}, num_primary_view_tag_bits, ledger_context); refresh_user_enote_store(user_keys_A, refresh_config, ledger_context, enote_store_A); transfer_funds_single_mock_v1_unconfirmed_sp_only(user_keys_A, @@ -1298,6 +1331,7 @@ TEST(seraphis_enote_scanning, basic_ledger_tx_passing_5) fee_per_tx_weight, max_inputs, {{11, destination_B, TxExtra{}}}, + num_primary_view_tag_bits, ref_set_decomp_n, ref_set_decomp_m, bin_config, @@ -1342,6 +1376,7 @@ TEST(seraphis_enote_scanning, basic_ledger_tx_passing_5) fee_per_tx_weight, max_inputs, {{12, destination_B, TxExtra{}}}, + num_primary_view_tag_bits, ref_set_decomp_n, ref_set_decomp_m, bin_config, @@ -1403,6 +1438,7 @@ TEST(seraphis_enote_scanning, basic_ledger_tx_passing_5) fee_per_tx_weight, max_inputs, {{13, destination_B, TxExtra{}}}, + num_primary_view_tag_bits, ref_set_decomp_n, ref_set_decomp_m, bin_config, @@ -1451,6 +1487,7 @@ TEST(seraphis_enote_scanning, basic_ledger_tx_passing_6) const std::size_t fee_per_tx_weight{0}; // 0 fee here const std::size_t ref_set_decomp_n{2}; const std::size_t ref_set_decomp_m{2}; + const std::uint8_t num_primary_view_tag_bits{0}; const scanning::ScanMachineConfig refresh_config{ .reorg_avoidance_increment = 1, @@ -1483,7 +1520,7 @@ TEST(seraphis_enote_scanning, basic_ledger_tx_passing_6) MockLedgerContext ledger_context{0, 0}; SpEnoteStore enote_store_A{0, 0, 0}; const InputSelectorMockV1 input_selector_A{enote_store_A}; - send_sp_coinbase_amounts_to_users({{16, 0, 0, 0}}, {destination_A}, ledger_context); + send_sp_coinbase_amounts_to_users({{16, 0, 0, 0}}, {destination_A}, num_primary_view_tag_bits, ledger_context); for (std::size_t iteration{0}; iteration < 12; ++iteration) { @@ -1503,6 +1540,7 @@ TEST(seraphis_enote_scanning, basic_ledger_tx_passing_6) { {amnt1, destination_A, TxExtra{}} }, + num_primary_view_tag_bits, ref_set_decomp_n, ref_set_decomp_m, bin_config, @@ -1548,9 +1586,11 @@ class InvocableTest2 final : public Invocable public: InvocableTest2(const JamtisDestinationV1 &user_address, std::vector amounts_per_new_coinbase, + const std::uint8_t num_primary_view_tag_bits, MockLedgerContext &ledger_context) : m_user_address{user_address}, m_amounts_per_new_coinbase{std::move(amounts_per_new_coinbase)}, + m_num_primary_view_tag_bits{num_primary_view_tag_bits}, m_ledger_contex{ledger_context} {} InvocableTest2& operator=(InvocableTest2&&) = delete; @@ -1564,12 +1604,16 @@ class InvocableTest2 final : public Invocable { m_ledger_contex.pop_blocks(2); for (const rct::xmr_amount new_coinbase_amount : m_amounts_per_new_coinbase) - send_sp_coinbase_amounts_to_users({{new_coinbase_amount}}, {m_user_address}, m_ledger_contex); + send_sp_coinbase_amounts_to_users({{new_coinbase_amount}}, + {m_user_address}, + m_num_primary_view_tag_bits, + m_ledger_contex); } } private: const JamtisDestinationV1 &m_user_address; const std::vector m_amounts_per_new_coinbase; + const std::uint8_t m_num_primary_view_tag_bits; MockLedgerContext &m_ledger_contex; std::size_t m_num_calls{0}; }; @@ -1579,9 +1623,11 @@ class InvocableTest3 final : public Invocable public: InvocableTest3(const JamtisDestinationV1 &user_address, std::vector amounts_per_new_coinbase, + const std::uint8_t num_primary_view_tag_bits, MockLedgerContext &ledger_context) : m_user_address{user_address}, m_amounts_per_new_coinbase{std::move(amounts_per_new_coinbase)}, + m_num_primary_view_tag_bits{num_primary_view_tag_bits}, m_ledger_contex{ledger_context} {} InvocableTest3& operator=(InvocableTest3&&) = delete; @@ -1595,7 +1641,10 @@ class InvocableTest3 final : public Invocable { m_ledger_contex.pop_blocks(2); for (const rct::xmr_amount new_coinbase_amount : m_amounts_per_new_coinbase) - send_sp_coinbase_amounts_to_users({{new_coinbase_amount}}, {m_user_address}, m_ledger_contex); + send_sp_coinbase_amounts_to_users({{new_coinbase_amount}}, + {m_user_address}, + m_num_primary_view_tag_bits, + m_ledger_contex); } } @@ -1604,6 +1653,7 @@ class InvocableTest3 final : public Invocable private: const JamtisDestinationV1 &m_user_address; const std::vector m_amounts_per_new_coinbase; + const std::uint8_t m_num_primary_view_tag_bits; MockLedgerContext &m_ledger_contex; std::size_t m_num_calls{0}; }; @@ -1613,9 +1663,11 @@ class InvocableTest4 final : public Invocable public: InvocableTest4(const JamtisDestinationV1 &user_address, const rct::xmr_amount amount_new_coinbase, + const std::uint8_t num_primary_view_tag_bits, MockLedgerContext &ledger_context) : m_user_address{user_address}, m_amount_new_coinbase{amount_new_coinbase}, + m_num_primary_view_tag_bits{num_primary_view_tag_bits}, m_ledger_contex{ledger_context} {} InvocableTest4& operator=(InvocableTest4&&) = delete; @@ -1628,12 +1680,16 @@ class InvocableTest4 final : public Invocable if (m_num_calls % 3 == 0) { m_ledger_contex.pop_blocks(1); - send_sp_coinbase_amounts_to_users({{m_amount_new_coinbase}}, {m_user_address}, m_ledger_contex); + send_sp_coinbase_amounts_to_users({{m_amount_new_coinbase}}, + {m_user_address}, + m_num_primary_view_tag_bits, + m_ledger_contex); } } private: const JamtisDestinationV1 &m_user_address; const rct::xmr_amount m_amount_new_coinbase; + const std::uint8_t m_num_primary_view_tag_bits; MockLedgerContext &m_ledger_contex; std::size_t m_num_calls{0}; }; @@ -1701,6 +1757,7 @@ TEST(seraphis_enote_scanning, reorgs_while_scanning_1) const std::size_t fee_per_tx_weight{0}; // 0 fee here const std::size_t ref_set_decomp_n{2}; const std::size_t ref_set_decomp_m{2}; + const std::uint8_t num_primary_view_tag_bits{9}; const FeeCalculatorMockTrivial fee_calculator; //just do a trivial calculator here (fee = fee/weight * 1 weight) @@ -1735,7 +1792,10 @@ TEST(seraphis_enote_scanning, reorgs_while_scanning_1) SpEnoteStore enote_store_B{0, 0, 0}; const InputSelectorMockV1 input_selector_A{enote_store_A}; const InputSelectorMockV1 input_selector_B{enote_store_B}; - send_sp_coinbase_amounts_to_users({{1, 1, 1, 1}}, {destination_A}, ledger_context); + send_sp_coinbase_amounts_to_users({{1, 1, 1, 1}}, + {destination_A}, + num_primary_view_tag_bits, + ledger_context); // a. refresh once so alignment will begin on block 0 in the test refresh_user_enote_store(user_keys_A, refresh_config, ledger_context, enote_store_A); @@ -1747,6 +1807,7 @@ TEST(seraphis_enote_scanning, reorgs_while_scanning_1) fee_per_tx_weight, max_inputs, {{2, destination_B, TxExtra{}}}, + num_primary_view_tag_bits, ref_set_decomp_n, ref_set_decomp_m, bin_config, @@ -1771,8 +1832,8 @@ TEST(seraphis_enote_scanning, reorgs_while_scanning_1) // b. unconfirmed chunk: empty // c. follow-up onchain loop: success on block 0 (range [0, 0) -> DONE) // 5. DONE: refresh enote store of A - const EnoteFindingContextUnconfirmedMockSp enote_finding_context_unconfirmed_A{ledger_context, user_keys_A.xk_fr}; - const EnoteFindingContextLedgerMockSp enote_finding_context_ledger_A{ledger_context, user_keys_A.xk_fr}; + const EnoteFindingContextUnconfirmedMockSp enote_finding_context_unconfirmed_A{ledger_context, user_keys_A.d_fa}; + const EnoteFindingContextLedgerMockSp enote_finding_context_ledger_A{ledger_context, user_keys_A.d_fa}; scanning::ScanContextNonLedgerSimple scan_context_unconfirmed_A{enote_finding_context_unconfirmed_A}; scanning::ScanContextLedgerSimple scan_context_ledger_A{enote_finding_context_ledger_A}; InvocableTest1 invocable_get_onchain{ledger_context}; @@ -1780,7 +1841,7 @@ TEST(seraphis_enote_scanning, reorgs_while_scanning_1) dummy_invocable, invocable_get_onchain, dummy_invocable); - ChunkConsumerMockSp chunk_consumer{user_keys_A.K_1_base, user_keys_A.k_vb, enote_store_A}; + ChunkConsumerMockSp chunk_consumer{user_keys_A.K_s_base, user_keys_A.k_vb, enote_store_A}; ASSERT_NO_THROW(refresh_enote_store(refresh_config, scan_context_unconfirmed_A, test_scan_context_A, @@ -1813,6 +1874,7 @@ TEST(seraphis_enote_scanning, reorgs_while_scanning_2) const std::size_t fee_per_tx_weight{0}; // 0 fee here const std::size_t ref_set_decomp_n{2}; const std::size_t ref_set_decomp_m{2}; + const std::uint8_t num_primary_view_tag_bits{14}; const FeeCalculatorMockTrivial fee_calculator; //just do a trivial calculator here (fee = fee/weight * 1 weight) @@ -1847,7 +1909,10 @@ TEST(seraphis_enote_scanning, reorgs_while_scanning_2) SpEnoteStore enote_store_B{0, 0, 0}; const InputSelectorMockV1 input_selector_A{enote_store_A}; const InputSelectorMockV1 input_selector_B{enote_store_B}; - send_sp_coinbase_amounts_to_users({{1, 1, 1, 1}}, {destination_A}, ledger_context); + send_sp_coinbase_amounts_to_users({{1, 1, 1, 1}}, + {destination_A}, + num_primary_view_tag_bits, + ledger_context); // a. refresh A so coinbase funds are available refresh_user_enote_store(user_keys_A, refresh_config, ledger_context, enote_store_A); @@ -1859,6 +1924,7 @@ TEST(seraphis_enote_scanning, reorgs_while_scanning_2) fee_per_tx_weight, max_inputs, {{1, destination_B, TxExtra{}}}, + num_primary_view_tag_bits, ref_set_decomp_n, ref_set_decomp_m, bin_config, @@ -1872,6 +1938,7 @@ TEST(seraphis_enote_scanning, reorgs_while_scanning_2) fee_per_tx_weight, max_inputs, {{2, destination_B, TxExtra{}}}, + num_primary_view_tag_bits, ref_set_decomp_n, ref_set_decomp_m, bin_config, @@ -1899,16 +1966,16 @@ TEST(seraphis_enote_scanning, reorgs_while_scanning_2) // b. unconfirmed chunk: empty // c. follow-up onchain loop: success on block 3 (range [3, 3) -> DONE) // 5. DONE: refresh enote store of A - const EnoteFindingContextUnconfirmedMockSp enote_finding_context_unconfirmed_A{ledger_context, user_keys_A.xk_fr}; - const EnoteFindingContextLedgerMockSp enote_finding_context_ledger_A{ledger_context, user_keys_A.xk_fr}; + const EnoteFindingContextUnconfirmedMockSp enote_finding_context_unconfirmed_A{ledger_context, user_keys_A.d_fa}; + const EnoteFindingContextLedgerMockSp enote_finding_context_ledger_A{ledger_context, user_keys_A.d_fa}; scanning::ScanContextNonLedgerSimple scan_context_unconfirmed_A{enote_finding_context_unconfirmed_A}; scanning::ScanContextLedgerSimple scan_context_ledger_A{enote_finding_context_ledger_A}; - InvocableTest2 invocable_get_onchain{destination_A, {3, 5}, ledger_context}; + InvocableTest2 invocable_get_onchain{destination_A, {3, 5}, num_primary_view_tag_bits, ledger_context}; ScanContextLedgerTEST test_scan_context_A(scan_context_ledger_A, dummy_invocable, invocable_get_onchain, dummy_invocable); - ChunkConsumerMockSp chunk_consumer{user_keys_A.K_1_base, user_keys_A.k_vb, enote_store_A}; + ChunkConsumerMockSp chunk_consumer{user_keys_A.K_s_base, user_keys_A.k_vb, enote_store_A}; ASSERT_NO_THROW(refresh_enote_store(refresh_config, scan_context_unconfirmed_A, test_scan_context_A, @@ -1941,6 +2008,7 @@ TEST(seraphis_enote_scanning, reorgs_while_scanning_3) const std::size_t fee_per_tx_weight{0}; // 0 fee here const std::size_t ref_set_decomp_n{2}; const std::size_t ref_set_decomp_m{2}; + const std::uint8_t num_primary_view_tag_bits{14}; const FeeCalculatorMockTrivial fee_calculator; //just do a trivial calculator here (fee = fee/weight * 1 weight) @@ -1975,7 +2043,10 @@ TEST(seraphis_enote_scanning, reorgs_while_scanning_3) SpEnoteStore enote_store_B{0, 0, 0}; const InputSelectorMockV1 input_selector_A{enote_store_A}; const InputSelectorMockV1 input_selector_B{enote_store_B}; - send_sp_coinbase_amounts_to_users({{1, 1, 1, 1}}, {destination_A}, ledger_context); + send_sp_coinbase_amounts_to_users({{1, 1, 1, 1}}, + {destination_A}, + num_primary_view_tag_bits, + ledger_context); // a. refresh once so user A can make a tx refresh_user_enote_store(user_keys_A, refresh_config, ledger_context, enote_store_A); @@ -1987,6 +2058,7 @@ TEST(seraphis_enote_scanning, reorgs_while_scanning_3) fee_per_tx_weight, max_inputs, {{1, destination_B, TxExtra{}}}, + num_primary_view_tag_bits, ref_set_decomp_n, ref_set_decomp_m, bin_config, @@ -2000,6 +2072,7 @@ TEST(seraphis_enote_scanning, reorgs_while_scanning_3) fee_per_tx_weight, max_inputs, {{2, destination_B, TxExtra{}}}, + num_primary_view_tag_bits, ref_set_decomp_n, ref_set_decomp_m, bin_config, @@ -2026,16 +2099,16 @@ TEST(seraphis_enote_scanning, reorgs_while_scanning_3) // b. unconfirmed chunk: empty // c. follow-up onchain loop: success on block 3 (range [3, 3) -> DONE) // 5. DONE: refresh enote store of B - const EnoteFindingContextUnconfirmedMockSp enote_finding_context_unconfirmed_B{ledger_context, user_keys_B.xk_fr}; - const EnoteFindingContextLedgerMockSp enote_finding_context_ledger_B{ledger_context, user_keys_B.xk_fr}; + const EnoteFindingContextUnconfirmedMockSp enote_finding_context_unconfirmed_B{ledger_context, user_keys_B.d_fa}; + const EnoteFindingContextLedgerMockSp enote_finding_context_ledger_B{ledger_context, user_keys_B.d_fa}; scanning::ScanContextNonLedgerSimple scan_context_unconfirmed_B{enote_finding_context_unconfirmed_B}; scanning::ScanContextLedgerSimple scan_context_ledger_B{enote_finding_context_ledger_B}; - InvocableTest3 invocable_get_onchain{destination_B, {3, 5}, ledger_context}; + InvocableTest3 invocable_get_onchain{destination_B, {3, 5}, num_primary_view_tag_bits, ledger_context}; ScanContextLedgerTEST test_scan_context_B(scan_context_ledger_B, dummy_invocable, invocable_get_onchain, dummy_invocable); - ChunkConsumerMockSp chunk_consumer{user_keys_B.K_1_base, user_keys_B.k_vb, enote_store_B}; + ChunkConsumerMockSp chunk_consumer{user_keys_B.K_s_base, user_keys_B.k_vb, enote_store_B}; ASSERT_NO_THROW(refresh_enote_store(refresh_config, scan_context_unconfirmed_B, test_scan_context_B, @@ -2071,6 +2144,7 @@ TEST(seraphis_enote_scanning, reorgs_while_scanning_4) const std::size_t fee_per_tx_weight{0}; // 0 fee here const std::size_t ref_set_decomp_n{2}; const std::size_t ref_set_decomp_m{2}; + const std::uint8_t num_primary_view_tag_bits{15}; const FeeCalculatorMockTrivial fee_calculator; //just do a trivial calculator here (fee = fee/weight * 1 weight) @@ -2105,7 +2179,10 @@ TEST(seraphis_enote_scanning, reorgs_while_scanning_4) SpEnoteStore enote_store_B{0, 0, 0}; const InputSelectorMockV1 input_selector_A{enote_store_A}; const InputSelectorMockV1 input_selector_B{enote_store_B}; - send_sp_coinbase_amounts_to_users({{1, 1, 1, 1}}, {destination_A}, ledger_context); + send_sp_coinbase_amounts_to_users({{1, 1, 1, 1}}, + {destination_A}, + num_primary_view_tag_bits, + ledger_context); // a. refresh once so user A can make a tx refresh_user_enote_store(user_keys_A, refresh_config, ledger_context, enote_store_A); @@ -2117,6 +2194,7 @@ TEST(seraphis_enote_scanning, reorgs_while_scanning_4) fee_per_tx_weight, max_inputs, {{1, destination_B, TxExtra{}}}, + num_primary_view_tag_bits, ref_set_decomp_n, ref_set_decomp_m, bin_config, @@ -2142,16 +2220,16 @@ TEST(seraphis_enote_scanning, reorgs_while_scanning_4) // iii. get onchain chunk: block 2 (inject: pop 1, +1 blocks) (fail: chunk range [2, 2) -> NEED_PARTIALSCAN) // b. skip unconfirmed chunk: (NEED_PARTIALSCAN) // 5. ... etc. until partialscan attempts runs out (then throw) - const EnoteFindingContextUnconfirmedMockSp enote_finding_context_unconfirmed_B{ledger_context, user_keys_B.xk_fr}; - const EnoteFindingContextLedgerMockSp enote_finding_context_ledger_B{ledger_context, user_keys_B.xk_fr}; + const EnoteFindingContextUnconfirmedMockSp enote_finding_context_unconfirmed_B{ledger_context, user_keys_B.d_fa}; + const EnoteFindingContextLedgerMockSp enote_finding_context_ledger_B{ledger_context, user_keys_B.d_fa}; scanning::ScanContextNonLedgerSimple scan_context_unconfirmed_B{enote_finding_context_unconfirmed_B}; scanning::ScanContextLedgerSimple scan_context_ledger_B{enote_finding_context_ledger_B}; - InvocableTest4 invocable_get_onchain{destination_B, 1, ledger_context}; + InvocableTest4 invocable_get_onchain{destination_B, 1, num_primary_view_tag_bits, ledger_context}; ScanContextLedgerTEST test_scan_context_B(scan_context_ledger_B, dummy_invocable, invocable_get_onchain, dummy_invocable); - ChunkConsumerMockSp chunk_consumer{user_keys_B.K_1_base, user_keys_B.k_vb, enote_store_B}; + ChunkConsumerMockSp chunk_consumer{user_keys_B.K_s_base, user_keys_B.k_vb, enote_store_B}; ASSERT_FALSE(refresh_enote_store(refresh_config, scan_context_unconfirmed_B, test_scan_context_B, @@ -2168,6 +2246,7 @@ TEST(seraphis_enote_scanning, reorgs_while_scanning_5) const std::size_t fee_per_tx_weight{0}; // 0 fee here const std::size_t ref_set_decomp_n{2}; const std::size_t ref_set_decomp_m{2}; + const std::uint8_t num_primary_view_tag_bits{8}; const FeeCalculatorMockTrivial fee_calculator; //just do a trivial calculator here (fee = fee/weight * 1 weight) @@ -2209,7 +2288,10 @@ TEST(seraphis_enote_scanning, reorgs_while_scanning_5) SpEnoteStore enote_store_B{0, 0, 0}; const InputSelectorMockV1 input_selector_A{enote_store_A}; const InputSelectorMockV1 input_selector_B{enote_store_B}; - send_sp_coinbase_amounts_to_users({{1, 1, 1, 1}}, {destination_A}, ledger_context); + send_sp_coinbase_amounts_to_users({{1, 1, 1, 1}}, + {destination_A}, + num_primary_view_tag_bits, + ledger_context); // a. refresh once so user A can make a tx refresh_user_enote_store(user_keys_A, refresh_config, ledger_context, enote_store_A); @@ -2221,6 +2303,7 @@ TEST(seraphis_enote_scanning, reorgs_while_scanning_5) fee_per_tx_weight, max_inputs, {{1, destination_B, TxExtra{}}}, + num_primary_view_tag_bits, ref_set_decomp_n, ref_set_decomp_m, bin_config, @@ -2237,6 +2320,7 @@ TEST(seraphis_enote_scanning, reorgs_while_scanning_5) fee_per_tx_weight, max_inputs, {{2, destination_B, TxExtra{}}}, + num_primary_view_tag_bits, 0, //legacy ring size ref_set_decomp_n, ref_set_decomp_m, @@ -2260,8 +2344,8 @@ TEST(seraphis_enote_scanning, reorgs_while_scanning_5) // i. get onchain chunk: block 2 (inject: commit unconfirmed) (success: chunk range [2, 3]) // ii. get onchain chunk: block 3 (success: chunk range [3, 3) -> DONE) // 4. DONE: refresh enote store of B - const EnoteFindingContextUnconfirmedMockSp enote_finding_context_unconfirmed_B{ledger_context, user_keys_B.xk_fr}; - const EnoteFindingContextLedgerMockSp enote_finding_context_ledger_B{ledger_context, user_keys_B.xk_fr}; + const EnoteFindingContextUnconfirmedMockSp enote_finding_context_unconfirmed_B{ledger_context, user_keys_B.d_fa}; + const EnoteFindingContextLedgerMockSp enote_finding_context_ledger_B{ledger_context, user_keys_B.d_fa}; scanning::ScanContextNonLedgerSimple scan_context_unconfirmed_B{enote_finding_context_unconfirmed_B}; scanning::ScanContextLedgerSimple scan_context_ledger_B{enote_finding_context_ledger_B}; InvocableTest5Commit invocable_get_unconfirmed{ledger_context}; @@ -2272,7 +2356,7 @@ TEST(seraphis_enote_scanning, reorgs_while_scanning_5) dummy_invocable, invocable_get_onchain, dummy_invocable); - ChunkConsumerMockSp chunk_consumer{user_keys_B.K_1_base, user_keys_B.k_vb, enote_store_B}; + ChunkConsumerMockSp chunk_consumer{user_keys_B.K_s_base, user_keys_B.k_vb, enote_store_B}; ASSERT_NO_THROW(refresh_enote_store(refresh_config, test_scan_context_unconfirmed_B, test_scan_context_ledger_B, @@ -5946,6 +6030,7 @@ TEST(seraphis_enote_scanning, legacy_sp_transition_1) const std::size_t legacy_ring_size{2}; const std::size_t ref_set_decomp_n{2}; const std::size_t ref_set_decomp_m{2}; + const std::uint8_t num_primary_view_tag_bits{6}; const FeeCalculatorMockTrivial fee_calculator; //just do a trivial calculator here (fee = fee/weight * 1 weight) @@ -6170,7 +6255,10 @@ TEST(seraphis_enote_scanning, legacy_sp_transition_1) enote_store_view); //block 2: seraphis amount 10 - send_sp_coinbase_amounts_to_users({{10}, {0, 0, 0}}, {sp_destination, sp_destination_random}, ledger_context); + send_sp_coinbase_amounts_to_users({{10}, {0, 0, 0}}, + {sp_destination, sp_destination_random}, + num_primary_view_tag_bits, + ledger_context); //test recovery legacy_sp_transition_test_recovery_assertions(legacy_keys, @@ -6210,7 +6298,10 @@ TEST(seraphis_enote_scanning, legacy_sp_transition_1) enote_store_view); //block 3: seraphis amount 10 - send_sp_coinbase_amounts_to_users({{10}, {0, 0, 0}}, {sp_destination, sp_destination_random}, ledger_context); + send_sp_coinbase_amounts_to_users({{10}, {0, 0, 0}}, + {sp_destination, sp_destination_random}, + num_primary_view_tag_bits, + ledger_context); //test recovery legacy_sp_transition_test_recovery_assertions(legacy_keys, @@ -6257,6 +6348,7 @@ TEST(seraphis_enote_scanning, legacy_sp_transition_1) fee_per_tx_weight, max_inputs, {{23, sp_destination_random, TxExtra{}}}, + num_primary_view_tag_bits, legacy_ring_size, ref_set_decomp_n, ref_set_decomp_m, @@ -6335,7 +6427,10 @@ TEST(seraphis_enote_scanning, legacy_sp_transition_1) enote_store_view); //block 0: seraphis amount 10 - send_sp_coinbase_amounts_to_users({{10}, {0, 0, 0}}, {sp_destination, sp_destination_random}, ledger_context); + send_sp_coinbase_amounts_to_users({{10}, {0, 0, 0}}, + {sp_destination, sp_destination_random}, + num_primary_view_tag_bits, + ledger_context); //test recovery legacy_sp_transition_test_recovery_assertions(legacy_keys, @@ -6463,7 +6558,10 @@ TEST(seraphis_enote_scanning, legacy_sp_transition_1) enote_store_view); //block 2: seraphis amount 10 - send_sp_coinbase_amounts_to_users({{10}}, {sp_destination}, ledger_context); + send_sp_coinbase_amounts_to_users({{10}}, + {sp_destination}, + num_primary_view_tag_bits, + ledger_context); //test recovery legacy_sp_transition_test_recovery_assertions(legacy_keys, @@ -6501,7 +6599,10 @@ TEST(seraphis_enote_scanning, legacy_sp_transition_1) enote_store_view); //block 3: seraphis amount 10 - send_sp_coinbase_amounts_to_users({{10}}, {sp_destination}, ledger_context); + send_sp_coinbase_amounts_to_users({{10}}, + {sp_destination}, + num_primary_view_tag_bits, + ledger_context); //test recovery legacy_sp_transition_test_recovery_assertions(legacy_keys, @@ -6546,6 +6647,7 @@ TEST(seraphis_enote_scanning, legacy_sp_transition_1) fee_per_tx_weight, max_inputs, {{32, sp_destination_random, TxExtra{}}}, + num_primary_view_tag_bits, legacy_ring_size, ref_set_decomp_n, ref_set_decomp_m, @@ -6595,7 +6697,10 @@ TEST(seraphis_enote_scanning, legacy_sp_transition_1) //no recovery: pop then add a block to see what happens //block 0: seraphis amount 10 - send_sp_coinbase_amounts_to_users({{10}}, {sp_destination}, ledger_context); + send_sp_coinbase_amounts_to_users({{10}}, + {sp_destination}, + num_primary_view_tag_bits, + ledger_context); //test recovery legacy_sp_transition_test_recovery_assertions(legacy_keys, @@ -6627,7 +6732,10 @@ TEST(seraphis_enote_scanning, legacy_sp_transition_1) enote_store_view); //block 1: seraphis amount 10 - send_sp_coinbase_amounts_to_users({{10}}, {sp_destination}, ledger_context); + send_sp_coinbase_amounts_to_users({{10}}, + {sp_destination}, + num_primary_view_tag_bits, + ledger_context); //legacy scan refresh_user_enote_store_legacy_full(legacy_keys.Ks, @@ -6734,7 +6842,10 @@ TEST(seraphis_enote_scanning, legacy_sp_transition_1) enote_store_view); //block 2: seraphis amount 10 - send_sp_coinbase_amounts_to_users({{10}}, {sp_destination}, ledger_context); + send_sp_coinbase_amounts_to_users({{10}}, + {sp_destination}, + num_primary_view_tag_bits, + ledger_context); //test recovery legacy_sp_transition_test_recovery_assertions(legacy_keys, @@ -6772,7 +6883,10 @@ TEST(seraphis_enote_scanning, legacy_sp_transition_1) enote_store_view); //block 3: seraphis amount 10 - send_sp_coinbase_amounts_to_users({{10}}, {sp_destination}, ledger_context); + send_sp_coinbase_amounts_to_users({{10}}, + {sp_destination}, + num_primary_view_tag_bits, + ledger_context); //test recovery legacy_sp_transition_test_recovery_assertions(legacy_keys, @@ -6817,6 +6931,7 @@ TEST(seraphis_enote_scanning, legacy_sp_transition_1) fee_per_tx_weight, max_inputs, {{32, sp_destination_random, TxExtra{}}}, + num_primary_view_tag_bits, legacy_ring_size, ref_set_decomp_n, ref_set_decomp_m, @@ -6866,7 +6981,10 @@ TEST(seraphis_enote_scanning, legacy_sp_transition_1) //no recovery: pop then add a block to see what happens //block 2: seraphis amount 10 - send_sp_coinbase_amounts_to_users({{10}}, {sp_destination}, ledger_context); + send_sp_coinbase_amounts_to_users({{10}}, + {sp_destination}, + num_primary_view_tag_bits, + ledger_context); //test recovery legacy_sp_transition_test_recovery_assertions(legacy_keys, @@ -6904,7 +7022,10 @@ TEST(seraphis_enote_scanning, legacy_sp_transition_1) enote_store_view); //block 3: seraphis amount 10 - send_sp_coinbase_amounts_to_users({{10}}, {sp_destination}, ledger_context); + send_sp_coinbase_amounts_to_users({{10}}, + {sp_destination}, + num_primary_view_tag_bits, + ledger_context); //test recovery legacy_sp_transition_test_recovery_assertions(legacy_keys, @@ -6949,6 +7070,7 @@ TEST(seraphis_enote_scanning, legacy_sp_transition_1) fee_per_tx_weight, max_inputs, {{32, sp_destination_random, TxExtra{}}}, + num_primary_view_tag_bits, legacy_ring_size, ref_set_decomp_n, ref_set_decomp_m, @@ -7038,6 +7160,7 @@ TEST(seraphis_enote_scanning, legacy_sp_transition_2) const std::size_t legacy_ring_size{2}; const std::size_t ref_set_decomp_n{2}; const std::size_t ref_set_decomp_m{2}; + const std::uint8_t num_primary_view_tag_bits{7}; const FeeCalculatorMockTrivial fee_calculator; //just do a trivial calculator here (fee = fee/weight * 1 weight) @@ -7262,7 +7385,10 @@ TEST(seraphis_enote_scanning, legacy_sp_transition_2) enote_store_view); //block 2: seraphis amount 10 - send_sp_coinbase_amounts_to_users({{10}, {0, 0, 0}}, {sp_destination, sp_destination_random}, ledger_context); + send_sp_coinbase_amounts_to_users({{10}, {0, 0, 0}}, + {sp_destination, sp_destination_random}, + num_primary_view_tag_bits, + ledger_context); //test recovery //seraphis scan should throw if this line in mock ledger context is changed to '> 0' @@ -7304,7 +7430,10 @@ TEST(seraphis_enote_scanning, legacy_sp_transition_2) enote_store_view); //block 3: seraphis amount 10 - send_sp_coinbase_amounts_to_users({{10}}, {sp_destination}, ledger_context); + send_sp_coinbase_amounts_to_users({{10}}, + {sp_destination}, + num_primary_view_tag_bits, + ledger_context); //test recovery legacy_sp_transition_test_recovery_assertions(legacy_keys, @@ -7344,7 +7473,10 @@ TEST(seraphis_enote_scanning, legacy_sp_transition_2) enote_store_view); //block 4: seraphis amount 10 - send_sp_coinbase_amounts_to_users({{10}}, {sp_destination}, ledger_context); + send_sp_coinbase_amounts_to_users({{10}}, + {sp_destination}, + num_primary_view_tag_bits, + ledger_context); //test recovery legacy_sp_transition_test_recovery_assertions(legacy_keys, @@ -7391,6 +7523,7 @@ TEST(seraphis_enote_scanning, legacy_sp_transition_2) fee_per_tx_weight, max_inputs, {{33, sp_destination_random, TxExtra{}}}, + num_primary_view_tag_bits, legacy_ring_size, ref_set_decomp_n, ref_set_decomp_m, @@ -7442,7 +7575,10 @@ TEST(seraphis_enote_scanning, legacy_sp_transition_2) //no recovery: pop then add a block to see what happens //block 1: seraphis amount 10 - send_sp_coinbase_amounts_to_users({{10}, {0, 0, 0}}, {sp_destination, sp_destination_random}, ledger_context); + send_sp_coinbase_amounts_to_users({{10}, {0, 0, 0}}, + {sp_destination, sp_destination_random}, + num_primary_view_tag_bits, + ledger_context); //test recovery legacy_sp_transition_test_recovery_assertions(legacy_keys, @@ -7537,7 +7673,10 @@ TEST(seraphis_enote_scanning, legacy_sp_transition_2) enote_store_view); //block 3: seraphis amount 10 - send_sp_coinbase_amounts_to_users({{10}}, {sp_destination}, ledger_context); + send_sp_coinbase_amounts_to_users({{10}}, + {sp_destination}, + num_primary_view_tag_bits, + ledger_context); //test recovery legacy_sp_transition_test_recovery_assertions(legacy_keys, @@ -7577,7 +7716,10 @@ TEST(seraphis_enote_scanning, legacy_sp_transition_2) enote_store_view); //block 4: seraphis amount 10 - send_sp_coinbase_amounts_to_users({{10}}, {sp_destination}, ledger_context); + send_sp_coinbase_amounts_to_users({{10}}, + {sp_destination}, + num_primary_view_tag_bits, + ledger_context); //test recovery legacy_sp_transition_test_recovery_assertions(legacy_keys, @@ -7624,6 +7766,7 @@ TEST(seraphis_enote_scanning, legacy_sp_transition_2) fee_per_tx_weight, max_inputs, {{33, sp_destination_random, TxExtra{}}}, + num_primary_view_tag_bits, legacy_ring_size, ref_set_decomp_n, ref_set_decomp_m, @@ -7708,7 +7851,10 @@ TEST(seraphis_enote_scanning, legacy_sp_transition_2) enote_store_view); //block 1: seraphis amount 10 - send_sp_coinbase_amounts_to_users({{10}}, {sp_destination}, ledger_context); + send_sp_coinbase_amounts_to_users({{10}}, + {sp_destination}, + num_primary_view_tag_bits, + ledger_context); //test recovery legacy_sp_transition_test_recovery_assertions(legacy_keys, @@ -7746,7 +7892,10 @@ TEST(seraphis_enote_scanning, legacy_sp_transition_2) enote_store_view); //block 2: seraphis amount 10 - send_sp_coinbase_amounts_to_users({{10}}, {sp_destination}, ledger_context); + send_sp_coinbase_amounts_to_users({{10}}, + {sp_destination}, + num_primary_view_tag_bits, + ledger_context); //test recovery legacy_sp_transition_test_recovery_assertions(legacy_keys, @@ -7784,7 +7933,10 @@ TEST(seraphis_enote_scanning, legacy_sp_transition_2) enote_store_view); //block 3: seraphis amount 10 - send_sp_coinbase_amounts_to_users({{10}}, {sp_destination}, ledger_context); + send_sp_coinbase_amounts_to_users({{10}}, + {sp_destination}, + num_primary_view_tag_bits, + ledger_context); //test recovery legacy_sp_transition_test_recovery_assertions(legacy_keys, @@ -7822,7 +7974,10 @@ TEST(seraphis_enote_scanning, legacy_sp_transition_2) enote_store_view); //block 4: seraphis amount 10 - send_sp_coinbase_amounts_to_users({{10}}, {sp_destination}, ledger_context); + send_sp_coinbase_amounts_to_users({{10}}, + {sp_destination}, + num_primary_view_tag_bits, + ledger_context); //test recovery legacy_sp_transition_test_recovery_assertions(legacy_keys, @@ -7867,6 +8022,7 @@ TEST(seraphis_enote_scanning, legacy_sp_transition_2) fee_per_tx_weight, max_inputs, {{42, sp_destination_random, TxExtra{}}}, + num_primary_view_tag_bits, legacy_ring_size, ref_set_decomp_n, ref_set_decomp_m, @@ -7949,7 +8105,10 @@ TEST(seraphis_enote_scanning, legacy_sp_transition_2) enote_store_view); //block 3: seraphis amount 10 - send_sp_coinbase_amounts_to_users({{10}}, {sp_destination}, ledger_context); + send_sp_coinbase_amounts_to_users({{10}}, + {sp_destination}, + num_primary_view_tag_bits, + ledger_context); //test recovery legacy_sp_transition_test_recovery_assertions(legacy_keys, @@ -7987,7 +8146,10 @@ TEST(seraphis_enote_scanning, legacy_sp_transition_2) enote_store_view); //block 4: seraphis amount 10 - send_sp_coinbase_amounts_to_users({{10}}, {sp_destination}, ledger_context); + send_sp_coinbase_amounts_to_users({{10}}, + {sp_destination}, + num_primary_view_tag_bits, + ledger_context); //test recovery legacy_sp_transition_test_recovery_assertions(legacy_keys, @@ -8065,6 +8227,8 @@ TEST(seraphis_enote_scanning, legacy_sp_transition_3) const std::uint64_t first_sp_allowed_block{1}; const std::uint64_t first_sp_only_block{1}; + const std::uint8_t num_primary_view_tag_bits{2}; + // 2. legacy user keys legacy_mock_keys legacy_keys; make_legacy_mock_keys(legacy_keys); @@ -8176,7 +8340,10 @@ TEST(seraphis_enote_scanning, legacy_sp_transition_3) enote_store_view); //block 1: seraphis amount 10 - send_sp_coinbase_amounts_to_users({{10}, {0, 0, 0}}, {sp_destination, sp_destination_random}, ledger_context); + send_sp_coinbase_amounts_to_users({{10}, {0, 0, 0}}, + {sp_destination, sp_destination_random}, + num_primary_view_tag_bits, + ledger_context); //test recovery legacy_sp_transition_test_recovery_assertions(legacy_keys, @@ -8212,7 +8379,10 @@ TEST(seraphis_enote_scanning, legacy_sp_transition_3) enote_store_view); //block 2: seraphis amount 10 - send_sp_coinbase_amounts_to_users({{10}, {0, 0, 0}}, {sp_destination, sp_destination_random}, ledger_context); + send_sp_coinbase_amounts_to_users({{10}, {0, 0, 0}}, + {sp_destination, sp_destination_random}, + num_primary_view_tag_bits, + ledger_context); //test recovery legacy_sp_transition_test_recovery_assertions(legacy_keys, @@ -8303,7 +8473,10 @@ TEST(seraphis_enote_scanning, legacy_sp_transition_3) enote_store_view); //block 1: seraphis amount 10 - send_sp_coinbase_amounts_to_users({{10}, {0, 0, 0}}, {sp_destination, sp_destination_random}, ledger_context); + send_sp_coinbase_amounts_to_users({{10}, {0, 0, 0}}, + {sp_destination, sp_destination_random}, + num_primary_view_tag_bits, + ledger_context); //test recovery legacy_sp_transition_test_recovery_assertions(legacy_keys, @@ -8339,7 +8512,10 @@ TEST(seraphis_enote_scanning, legacy_sp_transition_3) enote_store_view); //block 2: seraphis amount 10 - send_sp_coinbase_amounts_to_users({{10}, {0, 0, 0}}, {sp_destination, sp_destination_random}, ledger_context); + send_sp_coinbase_amounts_to_users({{10}, {0, 0, 0}}, + {sp_destination, sp_destination_random}, + num_primary_view_tag_bits, + ledger_context); //test recovery legacy_sp_transition_test_recovery_assertions(legacy_keys, @@ -8381,7 +8557,10 @@ TEST(seraphis_enote_scanning, legacy_sp_transition_3) //don't scan //block 1: seraphis amount 10 - send_sp_coinbase_amounts_to_users({{10}, {0, 0, 0}}, {sp_destination, sp_destination_random}, ledger_context); + send_sp_coinbase_amounts_to_users({{10}, {0, 0, 0}}, + {sp_destination, sp_destination_random}, + num_primary_view_tag_bits, + ledger_context); //test recovery legacy_sp_transition_test_recovery_assertions(legacy_keys, @@ -8417,7 +8596,10 @@ TEST(seraphis_enote_scanning, legacy_sp_transition_3) enote_store_view); //block 2: seraphis amount 10 - send_sp_coinbase_amounts_to_users({{10}, {0, 0, 0}}, {sp_destination, sp_destination_random}, ledger_context); + send_sp_coinbase_amounts_to_users({{10}, {0, 0, 0}}, + {sp_destination, sp_destination_random}, + num_primary_view_tag_bits, + ledger_context); //test recovery legacy_sp_transition_test_recovery_assertions(legacy_keys, @@ -8490,7 +8672,10 @@ TEST(seraphis_enote_scanning, legacy_sp_transition_3) enote_store_view); //block 1: seraphis amount 10 - send_sp_coinbase_amounts_to_users({{10}, {0, 0, 0}}, {sp_destination, sp_destination_random}, ledger_context); + send_sp_coinbase_amounts_to_users({{10}, {0, 0, 0}}, + {sp_destination, sp_destination_random}, + num_primary_view_tag_bits, + ledger_context); //test recovery legacy_sp_transition_test_recovery_assertions(legacy_keys, @@ -8526,7 +8711,10 @@ TEST(seraphis_enote_scanning, legacy_sp_transition_3) enote_store_view); //block 2: seraphis amount 10 - send_sp_coinbase_amounts_to_users({{10}, {0, 0, 0}}, {sp_destination, sp_destination_random}, ledger_context); + send_sp_coinbase_amounts_to_users({{10}, {0, 0, 0}}, + {sp_destination, sp_destination_random}, + num_primary_view_tag_bits, + ledger_context); //test recovery legacy_sp_transition_test_recovery_assertions(legacy_keys, @@ -8600,6 +8788,7 @@ TEST(seraphis_enote_scanning, legacy_sp_transition_4) const std::size_t legacy_ring_size{2}; const std::size_t ref_set_decomp_n{2}; const std::size_t ref_set_decomp_m{2}; + const std::uint8_t num_primary_view_tag_bits{3}; const FeeCalculatorMockTrivial fee_calculator; //just do a trivial calculator here (fee = fee/weight * 1 weight) @@ -8848,6 +9037,7 @@ TEST(seraphis_enote_scanning, legacy_sp_transition_4) fee_per_tx_weight, max_inputs, {{2, sp_destination_random, TxExtra{}}}, + num_primary_view_tag_bits, legacy_ring_size, ref_set_decomp_n, ref_set_decomp_m, @@ -9007,6 +9197,7 @@ TEST(seraphis_enote_scanning, legacy_sp_transition_4) fee_per_tx_weight, max_inputs, {{2, sp_destination, TxExtra{}}}, + num_primary_view_tag_bits, legacy_ring_size, ref_set_decomp_n, ref_set_decomp_m, @@ -9222,6 +9413,7 @@ TEST(seraphis_enote_scanning, legacy_sp_transition_5) const std::size_t legacy_ring_size{2}; const std::size_t ref_set_decomp_n{2}; const std::size_t ref_set_decomp_m{2}; + const std::uint8_t num_primary_view_tag_bits{16}; const FeeCalculatorMockTrivial fee_calculator; //just do a trivial calculator here (fee = fee/weight * 1 weight) @@ -9421,7 +9613,10 @@ TEST(seraphis_enote_scanning, legacy_sp_transition_5) //don't scan //block 1: seraphis block - send_sp_coinbase_amounts_to_users({{0}}, {sp_destination_random}, ledger_context); + send_sp_coinbase_amounts_to_users({{0}}, + {sp_destination_random}, + num_primary_view_tag_bits, + ledger_context); //don't scan @@ -9441,6 +9636,7 @@ TEST(seraphis_enote_scanning, legacy_sp_transition_5) fee_per_tx_weight, max_inputs, {{2, sp_destination_random, TxExtra{}}}, + num_primary_view_tag_bits, legacy_ring_size, ref_set_decomp_n, ref_set_decomp_m, @@ -9541,6 +9737,7 @@ TEST(seraphis_enote_scanning, legacy_sp_transition_5) fee_per_tx_weight, max_inputs, {{2, sp_destination_random, TxExtra{}}}, + num_primary_view_tag_bits, legacy_ring_size, ref_set_decomp_n, ref_set_decomp_m, @@ -9596,7 +9793,10 @@ TEST(seraphis_enote_scanning, legacy_sp_transition_5) //don't scan //block 1: seraphis block - send_sp_coinbase_amounts_to_users({{0}}, {sp_destination_random}, ledger_context); + send_sp_coinbase_amounts_to_users({{0}}, + {sp_destination_random}, + num_primary_view_tag_bits, + ledger_context); //don't scan diff --git a/tests/unit_tests/seraphis_integration.cpp b/tests/unit_tests/seraphis_integration.cpp index 895e540b7b..8eef3a5fe6 100644 --- a/tests/unit_tests/seraphis_integration.cpp +++ b/tests/unit_tests/seraphis_integration.cpp @@ -35,9 +35,9 @@ #include "seraphis_core/binned_reference_set.h" #include "seraphis_core/binned_reference_set_utils.h" #include "seraphis_core/discretized_fee.h" +#include "seraphis_core/jamtis_account_secrets.h" #include "seraphis_core/jamtis_address_tag_utils.h" #include "seraphis_core/jamtis_address_utils.h" -#include "seraphis_core/jamtis_core_utils.h" #include "seraphis_core/jamtis_destination.h" #include "seraphis_core/jamtis_enote_utils.h" #include "seraphis_core/jamtis_payment_proposal.h" @@ -88,6 +88,7 @@ TEST(seraphis_integration, txtype_squashed_v1) const std::size_t legacy_ring_size{2}; const std::size_t ref_set_decomp_n{2}; const std::size_t ref_set_decomp_m{2}; + const std::uint8_t num_primary_view_tag_bits{10}; const scanning::ScanMachineConfig refresh_config{ .reorg_avoidance_increment = 1, @@ -126,7 +127,10 @@ TEST(seraphis_integration, txtype_squashed_v1) JamtisDestinationV1 fake_destination; fake_destination = gen_jamtis_destination_v1(); - send_sp_coinbase_amounts_to_user(fake_sp_enote_amounts, fake_destination, ledger_context); + send_sp_coinbase_amounts_to_user(fake_sp_enote_amounts, + fake_destination, + num_primary_view_tag_bits, + ledger_context); /// make two users @@ -175,7 +179,10 @@ TEST(seraphis_integration, txtype_squashed_v1) legacy_subaddr_viewkey_A, ledger_context ); - send_sp_coinbase_amounts_to_user({1000000, 1000000, 1000000, 1000000}, destination_A, ledger_context); + send_sp_coinbase_amounts_to_user({1000000, 1000000, 1000000, 1000000}, + destination_A, + num_primary_view_tag_bits, + ledger_context); /// send funds back and forth between users @@ -198,6 +205,7 @@ TEST(seraphis_integration, txtype_squashed_v1) fee_per_tx_weight, max_inputs, {{6000000, destination_B, TxExtra{}}}, + num_primary_view_tag_bits, legacy_ring_size, ref_set_decomp_n, ref_set_decomp_m, @@ -215,6 +223,7 @@ TEST(seraphis_integration, txtype_squashed_v1) fee_per_tx_weight, max_inputs, {{3000000, destination_A, TxExtra{}}}, + num_primary_view_tag_bits, legacy_ring_size, ref_set_decomp_n, ref_set_decomp_m, @@ -232,6 +241,7 @@ TEST(seraphis_integration, txtype_squashed_v1) fee_per_tx_weight, max_inputs, {{4000000, destination_B, TxExtra{}}}, + num_primary_view_tag_bits, legacy_ring_size, ref_set_decomp_n, ref_set_decomp_m, diff --git a/tests/unit_tests/seraphis_knowledge_proofs.cpp b/tests/unit_tests/seraphis_knowledge_proofs.cpp index 9e923de95b..0807124e64 100644 --- a/tests/unit_tests/seraphis_knowledge_proofs.cpp +++ b/tests/unit_tests/seraphis_knowledge_proofs.cpp @@ -36,9 +36,9 @@ #include "seraphis_core/binned_reference_set.h" #include "seraphis_core/binned_reference_set_utils.h" #include "seraphis_core/discretized_fee.h" +#include "seraphis_core/jamtis_account_secrets.h" #include "seraphis_core/jamtis_address_tag_utils.h" #include "seraphis_core/jamtis_address_utils.h" -#include "seraphis_core/jamtis_core_utils.h" #include "seraphis_core/jamtis_destination.h" #include "seraphis_core/jamtis_enote_utils.h" #include "seraphis_core/jamtis_payment_proposal.h" @@ -95,7 +95,7 @@ static void enote_knowledge_proofs_helper(const jamtis_mock_keys &keys, // 2. RECIPIENT: enote ownership proof EnoteOwnershipProofV1 enote_ownership_proof_recipient; - make_enote_ownership_proof_v1_receiver(enote_record, keys.K_1_base, keys.k_vb, enote_ownership_proof_recipient); + make_enote_ownership_proof_v1_receiver(enote_record, keys.K_s_base, keys.k_vb, enote_ownership_proof_recipient); ASSERT_TRUE(verify_enote_ownership_proof_v1(enote_ownership_proof_recipient, enote_core.amount_commitment, @@ -170,7 +170,7 @@ static void reserve_proof_helper(const TxValidationContext &validation_context, ReserveProofV1 reserve_proof; make_reserve_proof_v1(rct::zero(), all_enote_records, - prover_keys.K_1_base, + prover_keys.K_s_base, prover_keys.k_m, prover_keys.k_vb, reserve_proof); @@ -196,7 +196,7 @@ TEST(seraphis_knowledge_proofs, address_ownership_proof_K_s) make_address_ownership_proof_v1(rct::zero(), keys.k_m, keys.k_vb, proof); //with mock message // 3. validate the address ownership proof - ASSERT_TRUE(verify_address_ownership_proof_v1(proof, rct::zero(), keys.K_1_base)); + ASSERT_TRUE(verify_address_ownership_proof_v1(proof, rct::zero(), keys.K_s_base)); } //------------------------------------------------------------------------------------------------------------------- TEST(seraphis_knowledge_proofs, address_ownership_and_index_proof_K_1) @@ -210,21 +210,21 @@ TEST(seraphis_knowledge_proofs, address_ownership_and_index_proof_K_1) // 3. make jamtis destination JamtisDestinationV1 destination; - make_jamtis_destination_v1(keys.K_1_base, keys.xK_ua, keys.xK_fr, keys.s_ga, j, destination); + make_address_for_user(keys, j, destination); // 4. address ownership proof on K_1 AddressOwnershipProofV1 address_ownership_proof; make_address_ownership_proof_v1(rct::zero(), keys.k_m, keys.k_vb, j, address_ownership_proof); //with mock message // 5. validate the address ownership proof - ASSERT_TRUE(verify_address_ownership_proof_v1(address_ownership_proof, rct::zero(), destination.addr_K1)); + ASSERT_TRUE(verify_address_ownership_proof_v1(address_ownership_proof, rct::zero(), destination.addr_Ks)); // 6. address index proof on K_1 AddressIndexProofV1 address_index_proof; - make_address_index_proof_v1(keys.K_1_base, j, keys.s_ga, address_index_proof); + make_address_index_proof_v1(keys.K_s_base, j, keys.s_ga, address_index_proof); // 7. validate the address index proof - ASSERT_TRUE(verify_address_index_proof_v1(address_index_proof, destination.addr_K1)); + ASSERT_TRUE(verify_address_index_proof_v1(address_index_proof, destination.addr_Ks)); } //------------------------------------------------------------------------------------------------------------------- TEST(seraphis_knowledge_proofs, enote_proofs_selfsend_normal) @@ -239,22 +239,19 @@ TEST(seraphis_knowledge_proofs, enote_proofs_selfsend_normal) const address_index_t j{gen_address_index()}; JamtisDestinationV1 user_address; - make_jamtis_destination_v1(keys.K_1_base, - keys.xK_ua, - keys.xK_fr, - keys.s_ga, - j, - user_address); + make_address_for_user(keys, j, user_address); // 3. make a self-spend enote paying to address const rct::xmr_amount amount{crypto::rand_idx(0)}; const crypto::x25519_secret_key enote_privkey{crypto::x25519_secret_key_gen()}; + const std::uint8_t num_primary_view_tag_bits{10}; - const jamtis::JamtisSelfSendType self_send_type{JamtisSelfSendType::SELF_SPEND}; - JamtisPaymentProposalSelfSendV1 payment_proposal_selfspend{user_address, + const jamtis::JamtisSelfSendType self_send_type{JamtisSelfSendType::EXCLUSIVE_SELF_SPEND}; + const JamtisPaymentProposalSelfSendV1 payment_proposal_selfspend{user_address, amount, self_send_type, - enote_privkey}; + enote_privkey, + num_primary_view_tag_bits}; SpOutputProposalV1 output_proposal; make_v1_output_proposal_v1(payment_proposal_selfspend, keys.k_vb, rct::zero(), output_proposal); SpEnoteV1 enote; @@ -264,15 +261,16 @@ TEST(seraphis_knowledge_proofs, enote_proofs_selfsend_normal) SpEnoteRecordV1 enote_record; ASSERT_TRUE(try_get_enote_record_v1(enote, output_proposal.enote_ephemeral_pubkey, + num_primary_view_tag_bits, rct::zero(), - keys.K_1_base, + keys.K_s_base, keys.k_vb, enote_record)); // 5. enote ownership proof: sender-selfsend EnoteOwnershipProofV1 enote_ownership_proof_sender_selfsend; make_enote_ownership_proof_v1_sender_selfsend(output_proposal.enote_ephemeral_pubkey, - user_address.addr_K1, + user_address.addr_Ks, rct::zero(), keys.k_vb, self_send_type, @@ -297,21 +295,17 @@ TEST(seraphis_knowledge_proofs, enote_proofs_selfsend_special) const address_index_t j{gen_address_index()}; JamtisDestinationV1 user_address; - make_jamtis_destination_v1(keys.K_1_base, - keys.xK_ua, - keys.xK_fr, - keys.s_ga, - j, - user_address); + make_address_for_user(keys, j, user_address); // 3. make a special change enote paying to address const rct::xmr_amount amount{crypto::rand_idx(0)}; const crypto::x25519_pubkey first_enote_ephemeral_pubkey{crypto::x25519_pubkey_gen()}; + const std::uint8_t num_primary_view_tag_bits{1}; JamtisPaymentProposalSelfSendV1 payment_proposal_special_change; - make_additional_output_selfsend_v1(OutputProposalSetExtraTypeV1::SPECIAL_CHANGE, + make_additional_output_v1(OutputProposalSetExtraTypeV1::SPECIAL_EXCLUSIVE_CHANGE, first_enote_ephemeral_pubkey, - user_address, + num_primary_view_tag_bits, user_address, keys.k_vb, amount, @@ -325,15 +319,16 @@ TEST(seraphis_knowledge_proofs, enote_proofs_selfsend_special) SpEnoteRecordV1 enote_record; ASSERT_TRUE(try_get_enote_record_v1(enote, output_proposal.enote_ephemeral_pubkey, + num_primary_view_tag_bits, rct::zero(), - keys.K_1_base, + keys.K_s_base, keys.k_vb, enote_record)); // 5. enote ownership proof: sender-selfsend EnoteOwnershipProofV1 enote_ownership_proof_sender_selfsend; make_enote_ownership_proof_v1_sender_selfsend(output_proposal.enote_ephemeral_pubkey, - user_address.addr_K1, + user_address.addr_Ks, rct::zero(), keys.k_vb, payment_proposal_special_change.type, @@ -357,18 +352,17 @@ TEST(seraphis_knowledge_proofs, enote_proofs_normal_enote) const address_index_t j{gen_address_index()}; JamtisDestinationV1 user_address; - make_jamtis_destination_v1(keys.K_1_base, - keys.xK_ua, - keys.xK_fr, - keys.s_ga, - j, - user_address); + make_address_for_user(keys, j, user_address); // 3. make a plain enote paying to address const rct::xmr_amount amount{crypto::rand_idx(0)}; const crypto::x25519_secret_key enote_privkey{crypto::x25519_secret_key_gen()}; + const std::uint8_t num_primary_view_tag_bits{5}; - JamtisPaymentProposalV1 payment_proposal{user_address, amount, enote_privkey}; + const JamtisPaymentProposalV1 payment_proposal{user_address, + amount, + enote_privkey, + num_primary_view_tag_bits}; SpOutputProposalV1 output_proposal; make_v1_output_proposal_v1(payment_proposal, rct::zero(), output_proposal); SpEnoteV1 enote; @@ -378,8 +372,9 @@ TEST(seraphis_knowledge_proofs, enote_proofs_normal_enote) SpEnoteRecordV1 enote_record; ASSERT_TRUE(try_get_enote_record_v1(enote, output_proposal.enote_ephemeral_pubkey, + num_primary_view_tag_bits, rct::zero(), - keys.K_1_base, + keys.K_s_base, keys.k_vb, enote_record)); @@ -406,6 +401,7 @@ TEST(seraphis_knowledge_proofs, reserve_proof) const std::size_t legacy_ring_size{2}; const std::size_t ref_set_decomp_n{2}; const std::size_t ref_set_decomp_m{2}; + const std::uint8_t num_primary_view_tag_bits{9}; const scanning::ScanMachineConfig refresh_config{ .reorg_avoidance_increment = 1, @@ -440,8 +436,10 @@ TEST(seraphis_knowledge_proofs, reserve_proof) JamtisDestinationV1 fake_destination; fake_destination = gen_jamtis_destination_v1(); - send_sp_coinbase_amounts_to_user(fake_sp_enote_amounts, fake_destination, ledger_context); - + send_sp_coinbase_amounts_to_user(fake_sp_enote_amounts, + fake_destination, + num_primary_view_tag_bits, + ledger_context); /// make two users @@ -467,8 +465,10 @@ TEST(seraphis_knowledge_proofs, reserve_proof) /// initial funding for user A: seraphis 40 - send_sp_coinbase_amounts_to_user({10, 10, 10, 10}, destination_A, ledger_context); - + send_sp_coinbase_amounts_to_user({10, 10, 10, 10}, + destination_A, + num_primary_view_tag_bits, + ledger_context); /// send funds back and forth between users @@ -483,6 +483,7 @@ TEST(seraphis_knowledge_proofs, reserve_proof) fee_per_tx_weight, max_inputs, {{30, destination_B, TxExtra{}}}, + num_primary_view_tag_bits, legacy_ring_size, ref_set_decomp_n, ref_set_decomp_m, @@ -500,6 +501,7 @@ TEST(seraphis_knowledge_proofs, reserve_proof) fee_per_tx_weight, max_inputs, {{20, destination_A, TxExtra{}}}, + num_primary_view_tag_bits, legacy_ring_size, ref_set_decomp_n, ref_set_decomp_m, diff --git a/tests/unit_tests/seraphis_multisig.cpp b/tests/unit_tests/seraphis_multisig.cpp index af6eb05dd1..7ddb6e1e7f 100644 --- a/tests/unit_tests/seraphis_multisig.cpp +++ b/tests/unit_tests/seraphis_multisig.cpp @@ -47,7 +47,7 @@ #include "seraphis_core/binned_reference_set.h" #include "seraphis_core/binned_reference_set_utils.h" #include "seraphis_core/discretized_fee.h" -#include "seraphis_core/jamtis_core_utils.h" +#include "seraphis_core/jamtis_account_secrets.h" #include "seraphis_core/jamtis_destination.h" #include "seraphis_core/jamtis_payment_proposal.h" #include "seraphis_core/jamtis_support_types.h" @@ -97,14 +97,15 @@ static void make_multisig_jamtis_mock_keys(const multisig::multisig_account &acc { keys_out.k_m = rct::rct2sk(rct::Z); //master key is not known in multisig keys_out.k_vb = account.get_common_privkey(); - make_jamtis_unlockamounts_key(keys_out.k_vb, keys_out.xk_ua); - make_jamtis_findreceived_key(keys_out.k_vb, keys_out.xk_fr); - make_jamtis_generateaddress_secret(keys_out.k_vb, keys_out.s_ga); + make_jamtis_viewreceived_key(keys_out.k_vb, keys_out.d_vr); + make_jamtis_filterassist_key(keys_out.d_vr, keys_out.d_fa); + make_jamtis_generateaddress_secret(keys_out.d_vr, keys_out.s_ga); make_jamtis_ciphertag_secret(keys_out.s_ga, keys_out.s_ct); - keys_out.K_1_base = rct::pk2rct(account.get_multisig_pubkey()); - extend_seraphis_spendkey_x(keys_out.k_vb, keys_out.K_1_base); - make_jamtis_unlockamounts_pubkey(keys_out.xk_ua, keys_out.xK_ua); - make_jamtis_findreceived_pubkey(keys_out.xk_fr, keys_out.xK_ua, keys_out.xK_fr); + keys_out.K_s_base = rct::pk2rct(account.get_multisig_pubkey()); + extend_seraphis_spendkey_x(keys_out.k_vb, keys_out.K_s_base); + make_jamtis_exchangebase_pubkey(keys_out.d_vr, keys_out.D_base); + make_jamtis_viewreceived_pubkey(keys_out.d_vr, keys_out.D_base, keys_out.D_vr); + make_jamtis_filterassist_pubkey(keys_out.d_fa, keys_out.D_base, keys_out.D_fa); } //------------------------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------------------------- @@ -367,6 +368,7 @@ static void seraphis_multisig_tx_v1_test(const std::uint32_t threshold, const std::size_t ref_set_decomp_n{2}; const std::size_t bin_radius{1}; const std::size_t num_bin_members{2}; + const std::uint8_t num_primary_view_tag_bits{14}; const scanning::ScanMachineConfig refresh_config{ .reorg_avoidance_increment = 1, @@ -436,10 +438,7 @@ static void seraphis_multisig_tx_v1_test(const std::uint32_t threshold, j = sp::jamtis::gen_address_index(); JamtisDestinationV1 sp_user_address; - ASSERT_NO_THROW(make_jamtis_destination_v1(shared_sp_keys.K_1_base, - shared_sp_keys.xK_ua, - shared_sp_keys.xK_fr, - shared_sp_keys.s_ga, + ASSERT_NO_THROW(make_address_for_user(shared_sp_keys, j, sp_user_address)); @@ -460,7 +459,10 @@ static void seraphis_multisig_tx_v1_test(const std::uint32_t threshold, if (sp_in_amounts_padded.size() < compute_bin_width(bin_radius)) sp_in_amounts_padded.resize(compute_bin_width(bin_radius), 0); - send_sp_coinbase_amounts_to_user(sp_in_amounts_padded, sp_user_address, ledger_context); + send_sp_coinbase_amounts_to_user(sp_in_amounts_padded, + sp_user_address, + num_primary_view_tag_bits, + ledger_context); // e) recover balance refresh_user_enote_store_legacy_multisig(legacy_accounts, @@ -492,23 +494,33 @@ static void seraphis_multisig_tx_v1_test(const std::uint32_t threshold, normal_payment_proposals.reserve(out_amounts_normal.size()); for (const rct::xmr_amount out_amount : out_amounts_normal) - tools::add_element(normal_payment_proposals) = gen_jamtis_payment_proposal_v1(out_amount, 0); + tools::add_element(normal_payment_proposals) = gen_jamtis_payment_proposal_v1(out_amount, + 0, + num_primary_view_tag_bits); // - self-send payments std::vector selfsend_payment_proposals; selfsend_payment_proposals.reserve(out_amounts_selfsend.size()); + bool first_self_send = true; for (const rct::xmr_amount out_amount : out_amounts_selfsend) { + const JamtisSelfSendType self_send_type = first_self_send + ? JamtisSelfSendType::EXCLUSIVE_SELF_SPEND + : JamtisSelfSendType::AUXILIARY_SELF_SPEND; + selfsend_payment_proposals.emplace_back( JamtisPaymentProposalSelfSendV1{ - .destination = sp_user_address, - .amount = out_amount, - .type = JamtisSelfSendType::SELF_SPEND, - .enote_ephemeral_privkey = crypto::x25519_secret_key_gen(), - .partial_memo = TxExtra{} + .destination = sp_user_address, + .amount = out_amount, + .type = self_send_type, + .enote_ephemeral_privkey = crypto::x25519_secret_key_gen(), + .num_primary_view_tag_bits = num_primary_view_tag_bits, + .partial_memo = TxExtra{} } ); + + first_self_send = false; } // b) set requested signers filter @@ -534,7 +546,6 @@ static void seraphis_multisig_tx_v1_test(const std::uint32_t threshold, std::vector sp_contextual_inputs; DiscretizedFee discretized_transaction_fee; ASSERT_NO_THROW(ASSERT_TRUE(try_prepare_inputs_and_outputs_for_transfer_v1(sp_user_address, - sp_user_address, input_selector, tx_fee_calculator, fee_per_tx_weight, @@ -572,7 +583,7 @@ static void seraphis_multisig_tx_v1_test(const std::uint32_t threshold, rct::pk2rct(legacy_accounts[0].get_multisig_pubkey()), legacy_subaddress_map, legacy_accounts[0].get_common_privkey(), - shared_sp_keys.K_1_base, + shared_sp_keys.K_s_base, shared_sp_keys.k_vb, multisig_tx_proposal)); @@ -587,7 +598,7 @@ static void seraphis_multisig_tx_v1_test(const std::uint32_t threshold, rct::pk2rct(legacy_accounts[0].get_multisig_pubkey()), legacy_subaddress_map, legacy_accounts[0].get_common_privkey(), - shared_sp_keys.K_1_base, + shared_sp_keys.K_s_base, shared_sp_keys.k_vb, enote_store, ledger_context); @@ -615,7 +626,7 @@ static void seraphis_multisig_tx_v1_test(const std::uint32_t threshold, rct::pk2rct(legacy_accounts[0].get_multisig_pubkey()), legacy_subaddress_map, legacy_accounts[0].get_common_privkey(), - shared_sp_keys.K_1_base, + shared_sp_keys.K_s_base, shared_sp_keys.k_vb, signer_nonce_records.back(), legacy_input_init_collections_per_signer[seraphis_accounts[signer_index].get_base_pubkey()], @@ -631,7 +642,7 @@ static void seraphis_multisig_tx_v1_test(const std::uint32_t threshold, rct::pk2rct(legacy_accounts[0].get_multisig_pubkey()), legacy_subaddress_map, legacy_accounts[0].get_common_privkey(), - shared_sp_keys.K_1_base, + shared_sp_keys.K_s_base, shared_sp_keys.k_vb, signer_nonce_records.back(), legacy_input_init_collections_per_signer[seraphis_accounts[signer_index].get_base_pubkey()], @@ -657,7 +668,7 @@ static void seraphis_multisig_tx_v1_test(const std::uint32_t threshold, legacy_accounts[signer_index], multisig_tx_proposal, legacy_subaddress_map, - shared_sp_keys.K_1_base, + shared_sp_keys.K_s_base, shared_sp_keys.k_vb, tx_version, legacy_input_init_collections_per_signer[legacy_accounts[signer_index].get_base_pubkey()], @@ -689,7 +700,7 @@ static void seraphis_multisig_tx_v1_test(const std::uint32_t threshold, try_make_v1_multisig_partial_sig_sets_for_legacy_inputs_v1(legacy_accounts[signer_index], multisig_tx_proposal, legacy_subaddress_map, - shared_sp_keys.K_1_base, + shared_sp_keys.K_s_base, shared_sp_keys.k_vb, tx_version, legacy_input_init_collections_per_signer[legacy_accounts[signer_index].get_base_pubkey()], @@ -732,7 +743,7 @@ static void seraphis_multisig_tx_v1_test(const std::uint32_t threshold, rct::pk2rct(legacy_accounts[0].get_multisig_pubkey()), legacy_subaddress_map, legacy_accounts[0].get_common_privkey(), - shared_sp_keys.K_1_base, + shared_sp_keys.K_s_base, shared_sp_keys.k_vb, legacy_input_partial_sigs_per_signer, sp_input_partial_sigs_per_signer, @@ -748,7 +759,7 @@ static void seraphis_multisig_tx_v1_test(const std::uint32_t threshold, rct::pk2rct(legacy_accounts[0].get_multisig_pubkey()), legacy_subaddress_map, legacy_accounts[0].get_common_privkey(), - shared_sp_keys.K_1_base, + shared_sp_keys.K_s_base, shared_sp_keys.k_vb, tx_proposal); @@ -758,7 +769,7 @@ static void seraphis_multisig_tx_v1_test(const std::uint32_t threshold, std::move(sp_partial_inputs), tx_version, rct::pk2rct(legacy_accounts[0].get_multisig_pubkey()), - shared_sp_keys.K_1_base, + shared_sp_keys.K_s_base, shared_sp_keys.k_vb, partial_tx)); diff --git a/tests/unit_tests/seraphis_serialization.cpp b/tests/unit_tests/seraphis_serialization.cpp index f94b654e4b..b527452183 100644 --- a/tests/unit_tests/seraphis_serialization.cpp +++ b/tests/unit_tests/seraphis_serialization.cpp @@ -196,7 +196,7 @@ TEST(seraphis_serialization, jamtis_destination_v1) TEST(seraphis_serialization, jamtis_payment_proposal_v1) { // generate - JamtisPaymentProposalV1 payprop{gen_jamtis_payment_proposal_v1(7, 3)}; + JamtisPaymentProposalV1 payprop{gen_jamtis_payment_proposal_v1(7, 3, 9)}; // serialize std::string serialized_payprop; @@ -214,7 +214,7 @@ TEST(seraphis_serialization, jamtis_payment_proposal_self_send_v1) { // generate JamtisPaymentProposalSelfSendV1 payprop{ - gen_jamtis_selfsend_payment_proposal_v1(7, JamtisSelfSendType::SELF_SPEND, 3) + gen_jamtis_selfsend_payment_proposal_v1(7, JamtisSelfSendType::EXCLUSIVE_SELF_SPEND, 3) }; // serialize