From c145285dcf9207b45350293a8eb323a936352757 Mon Sep 17 00:00:00 2001 From: Some Random Crypto Guy Date: Fri, 16 May 2025 21:06:20 +0100 Subject: [PATCH] finished SPARC library prototype - anonymised return payments now pass unit tests; removed extraneous code --- src/carrot_core/payment_proposal.cpp | 2 +- tests/unit_tests/carrot_sparc.cpp | 134 ++++++++------------------- 2 files changed, 40 insertions(+), 96 deletions(-) diff --git a/src/carrot_core/payment_proposal.cpp b/src/carrot_core/payment_proposal.cpp index d17e17c..5c70071 100644 --- a/src/carrot_core/payment_proposal.cpp +++ b/src/carrot_core/payment_proposal.cpp @@ -444,7 +444,7 @@ void get_output_proposal_return_v1(const CarrotPaymentProposalV1 &proposal, // i.e. {K_o, vt, m_a, a_enc, m_anchor, anchor_enc, m_pid, pid_enc} // Override the onetime address - sc_add(to_bytes(output_enote_out.enote.onetime_address), to_bytes(proposal.destination.address_spend_pubkey), to_bytes(proposal.destination.address_view_pubkey)); + output_enote_out.enote.onetime_address = rct::rct2pk(rct::addKeys(rct::pk2rct(proposal.destination.address_spend_pubkey), rct::pk2rct(proposal.destination.address_view_pubkey))); // Recalculate the view tag : vt = H_3(s_sr || input_context || Ksra) make_carrot_view_tag(s_sender_receiver_unctx.data, input_context, output_enote_out.enote.onetime_address, output_enote_out.enote.view_tag); diff --git a/tests/unit_tests/carrot_sparc.cpp b/tests/unit_tests/carrot_sparc.cpp index 75fa2e9..ece82cc 100644 --- a/tests/unit_tests/carrot_sparc.cpp +++ b/tests/unit_tests/carrot_sparc.cpp @@ -45,71 +45,6 @@ using namespace carrot; -//---------------------------------------------------------------------------------------------------------------------- -//---------------------------------------------------------------------------------------------------------------------- -struct mock_carrot_keys -{ - crypto::secret_key s_master; - crypto::secret_key k_prove_spend; - crypto::secret_key s_view_balance; - crypto::secret_key k_generate_image; - crypto::secret_key k_view; - crypto::secret_key s_generate_address; - crypto::public_key account_spend_pubkey; - crypto::public_key account_view_pubkey; - crypto::public_key main_address_view_pubkey; - - view_incoming_key_ram_borrowed_device k_view_dev; - view_balance_secret_ram_borrowed_device s_view_balance_dev; - - mock_carrot_keys(): k_view_dev(k_view), s_view_balance_dev(s_view_balance) - {} - - static mock_carrot_keys generate() - { - mock_carrot_keys k; - crypto::generate_random_bytes_thread_safe(sizeof(crypto::secret_key), to_bytes(k.s_master)); - make_carrot_provespend_key(k.s_master, k.k_prove_spend); - make_carrot_viewbalance_secret(k.s_master, k.s_view_balance); - make_carrot_generateimage_key(k.s_view_balance, k.k_generate_image); - make_carrot_viewincoming_key(k.s_view_balance, k.k_view); - make_carrot_generateaddress_secret(k.s_view_balance, k.s_generate_address); - make_carrot_spend_pubkey(k.k_generate_image, k.k_prove_spend, k.account_spend_pubkey); - k.account_view_pubkey = rct::rct2pk(rct::scalarmultKey(rct::pk2rct(k.account_spend_pubkey), - rct::sk2rct(k.k_view))); - k.main_address_view_pubkey = rct::rct2pk(rct::scalarmultBase(rct::sk2rct(k.k_view))); - return k; - } -}; -//---------------------------------------------------------------------------------------------------------------------- -//---------------------------------------------------------------------------------------------------------------------- -static bool can_open_fcmp_onetime_address(const crypto::secret_key &k_prove_spend, - const crypto::secret_key &k_generate_image, - const crypto::secret_key &subaddr_scalar, - const crypto::secret_key &sender_extension_g, - const crypto::secret_key &sender_extension_t, - const crypto::public_key &onetime_address) -{ - // K_s = k_gi G + k_ps T - // K^j_s = k^j_subscal * K_s - // Ko = K^j_s + k^o_g G + k^o_t T - // = (k^o_g + k^j_subscal * k_gi) G + (k^o_t + k^j_subscal * k_ps) T - - // combined_g = k^o_g + k^j_subscal * k_gi - rct::key combined_g; - sc_muladd(combined_g.bytes, to_bytes(subaddr_scalar), to_bytes(k_generate_image), to_bytes(sender_extension_g)); - - // combined_t = k^o_t + k^j_subscal * k_ps - rct::key combined_t; - sc_muladd(combined_t.bytes, to_bytes(subaddr_scalar), to_bytes(k_prove_spend), to_bytes(sender_extension_t)); - - // Ko' = combined_g G + combined_t T - rct::key recomputed_onetime_address; - rct::addKeys2(recomputed_onetime_address, combined_g, combined_t, rct::pk2rct(crypto::get_T())); - - // Ko' ?= Ko - return recomputed_onetime_address == onetime_address; -} //---------------------------------------------------------------------------------------------------------------------- template static auto auto_wiper(T &obj) @@ -178,8 +113,7 @@ std::tuple, crypto::public_key> make_origin_ memcpy(K_return.bytes, return_pub.data, sizeof(encrypted_return_pubkey_t)); // compute K_o = K_change + K_return - crypto::public_key K_o; - sc_add(to_bytes(K_o), to_bytes(enote_proposal_change.enote.onetime_address), K_return.bytes); + crypto::public_key K_o = rct::rct2pk(rct::addKeys(rct::pk2rct(return_pub), rct::pk2rct(enote_proposal_change.enote.onetime_address))); // calculate the shared secret mx25519_pubkey shared_secret_unctx; @@ -247,14 +181,10 @@ std::tuple, crypto::public_key> make_return_ EXPECT_TRUE(scan_success); // check we can spend it - EXPECT_TRUE(can_open_fcmp_onetime_address( - bob.k_prove_spend, - bob.k_generate_image, - rct::rct2sk(rct::I), - recovered_sender_extension_g, - recovered_sender_extension_t, - received_output.onetime_address - )); + EXPECT_TRUE(bob.can_open_fcmp_onetime_address(bob.carrot_account_spend_pubkey, + recovered_sender_extension_g, + recovered_sender_extension_t, + received_output.onetime_address)); // spend the received output for a return tx const crypto::key_image tx_return_first_key_image = bob.derive_key_image( @@ -334,10 +264,9 @@ TEST(carrot_sparc, main_address_return_payment_normal_scan_completeness) crypto::secret_key_to_public_key(k_return, K_return); // 4. compute K_o' = K_return' + K_change - crypto::public_key K_o_verify; - sc_add(to_bytes(K_o_verify), to_bytes(K_return), to_bytes(change_output.onetime_address)); + crypto::public_key K_o_verify = rct::rct2pk(rct::addKeys(rct::pk2rct(K_return), rct::pk2rct(change_output.onetime_address))); ASSERT_EQ(K_o_verify, return_pubkey); - + // 5. recover the shared secret(s_sr) of return tx // s_sr = k_return * D_e const input_context_t input_context_return = make_carrot_input_context(return_output.tx_first_key_image); @@ -350,7 +279,7 @@ TEST(carrot_sparc, main_address_return_payment_normal_scan_completeness) input_context_return, shared_secret_return); - // 6. verify the view_tag (covers step [7] as well) + // 6. verify the view_tag EXPECT_TRUE(test_carrot_view_tag(shared_secret_return_unctx.data, input_context_return, return_output.onetime_address, return_output.view_tag)); // 8. compute the amount encryption mask and recover the amount @@ -392,25 +321,40 @@ TEST(carrot_sparc, main_address_return_payment_normal_scan_completeness) // 19. verify the enote ephemeral pubkey ASSERT_EQ(recovered_ephemeral_pubkey_return, return_output.enote_ephemeral_pubkey); + + // Scan the change output + crypto::secret_key recovered_sender_extension_g_change; + crypto::secret_key recovered_sender_extension_t_change; + crypto::public_key recovered_address_spend_pubkey_change; + rct::xmr_amount recovered_amount_change; + crypto::secret_key recovered_amount_blinding_factor_change; + CarrotEnoteType recovered_enote_type_change; + janus_anchor_t recovered_internal_message_out_change; + const bool scan_success_change = try_scan_carrot_enote_internal_receiver(change_output, + alice.s_view_balance_dev, + recovered_sender_extension_g_change, + recovered_sender_extension_t_change, + recovered_address_spend_pubkey_change, + recovered_amount_change, + recovered_amount_blinding_factor_change, + recovered_enote_type_change, + recovered_internal_message_out_change); - // check recovered data - // EXPECT_EQ(enote_proposal_change.enote.onetime_address, recovered_address_spend_pubkey_return); - // EXPECT_EQ(proposal_out.amount, recovered_amount_return + txnFee); // returned minus the deducted TX fee - // EXPECT_EQ(enote_proposal_return.amount_blinding_factor, recovered_amount_blinding_factor_return); - // EXPECT_EQ(null_payment_id, recovered_payment_id_return); - // EXPECT_EQ(CarrotEnoteType::PAYMENT, recovered_enote_type_return); + ASSERT_TRUE(scan_success_change); + + // check spendability of the change output + EXPECT_TRUE(alice.can_open_fcmp_onetime_address(alice.carrot_account_spend_pubkey, + recovered_sender_extension_g_change, + recovered_sender_extension_t_change, + change_output.onetime_address)); // check spendability of the return_payment - // rct::key combined_extension_g; - // sc_add(combined_extension_g.bytes, to_bytes(recovered_sender_extension_g_change), to_bytes(recovered_sender_extension_g_return)); - // rct::key combined_extension_t; - // sc_add(combined_extension_t.bytes, to_bytes(recovered_sender_extension_t_change), to_bytes(recovered_sender_extension_t_return)); - // EXPECT_TRUE(can_open_fcmp_onetime_address(alice.k_prove_spend, - // alice.k_generate_image, - // rct::rct2sk(rct::I), - // rct::rct2sk(combined_extension_g), - // rct::rct2sk(combined_extension_t), - // enote_proposal_return.enote.onetime_address)); + crypto::secret_key sum_g; + sc_add(to_bytes(sum_g), to_bytes(recovered_sender_extension_g_change), to_bytes(k_return)); + ASSERT_TRUE(alice.can_open_fcmp_onetime_address(alice.carrot_account_spend_pubkey, + sum_g, + recovered_sender_extension_t_change, + return_output.onetime_address)); } //---------------------------------------------------------------------------------------------------------------------- TEST(carrot_sparc, get_spend_authority_proof_completeness)