diff --git a/src/carrot_core/payment_proposal.cpp b/src/carrot_core/payment_proposal.cpp index 404681a..d17e17c 100644 --- a/src/carrot_core/payment_proposal.cpp +++ b/src/carrot_core/payment_proposal.cpp @@ -440,18 +440,23 @@ void get_output_proposal_return_v1(const CarrotPaymentProposalV1 &proposal, encrypted_payment_id_out, output_enote_out.enote.view_tag); + // 5. Override the values that change because of the enote onetime address (K_o) changing + // 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)); // 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); - // LAND AHOY!!! + // Recalculate a_enc = BytesToInt64(a) XOR m_a + output_enote_out.enote.amount_enc = encrypt_carrot_amount(proposal.amount, s_sender_receiver, output_enote_out.enote.onetime_address); - // 5. anchor_enc = anchor XOR m_anchor - output_enote_out.enote.anchor_enc = encrypt_carrot_anchor(proposal.randomness, - s_sender_receiver, - output_enote_out.enote.onetime_address); + // Recalculate anchor_enc = anchor XOR m_anchor + output_enote_out.enote.anchor_enc = encrypt_carrot_anchor(proposal.randomness, s_sender_receiver, output_enote_out.enote.onetime_address); + + // Recalculate the pid_enc = pid XOR m_pid + encrypted_payment_id_out =encrypt_legacy_payment_id(proposal.destination.payment_id, s_sender_receiver, output_enote_out.enote.onetime_address); // 6. save the amount and first key image output_enote_out.amount = proposal.amount; diff --git a/tests/unit_tests/carrot_sparc.cpp b/tests/unit_tests/carrot_sparc.cpp index b6683a9..75fa2e9 100644 --- a/tests/unit_tests/carrot_sparc.cpp +++ b/tests/unit_tests/carrot_sparc.cpp @@ -317,45 +317,82 @@ TEST(carrot_sparc, main_address_return_payment_normal_scan_completeness) auto [return_tx_outputs, return_pubkey] = make_return_tx(bob, origin_tx_outputs); const auto return_output = return_tx_outputs[0].enote; - // Alice checks the "hashmap" for a known return_address + // 1. Alice checks the "hashmap" for a known return_address ASSERT_EQ(origin_return_pubkey, return_pubkey); - // 1. Alice Recover the shared secret(s_sr^ctx) of return tx + // Alice should now have access to the origin TX, including the "change" and "output" enotes + const auto change_output = origin_tx_outputs[0].enote; + const auto sent_output = origin_tx_outputs[1].enote; + + // 2. compute k_return' + const input_context_t input_context = make_carrot_input_context(sent_output.tx_first_key_image); + crypto::secret_key k_return; + alice.s_view_balance_dev.make_internal_return_privkey(input_context, sent_output.onetime_address, k_return); + + // 3. compute K_return' = k_return * G + crypto::public_key K_return; + 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)); + 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); mx25519_pubkey shared_secret_return_unctx; crypto::hash shared_secret_return; - EXPECT_TRUE(bob.k_view_incoming_dev.view_key_scalar_mult_x25519(return_output.enote_ephemeral_pubkey, shared_secret_return_unctx)); - make_carrot_sender_receiver_secret( - shared_secret_return_unctx.data, - return_output.enote_ephemeral_pubkey, - input_context_return, - shared_secret_return - ); - /* - // 2. scan the enote to see if it belongs to Alice - crypto::secret_key recovered_sender_extension_g_return; - crypto::secret_key recovered_sender_extension_t_return; - crypto::public_key recovered_address_spend_pubkey_return; - rct::xmr_amount recovered_amount_return; + make_carrot_uncontextualized_shared_key_receiver(k_return, return_output.enote_ephemeral_pubkey, shared_secret_return_unctx); + // s^ctx_sr = H_32(s_sr, D_e, input_context) + make_carrot_sender_receiver_secret(shared_secret_return_unctx.data, + return_output.enote_ephemeral_pubkey, + input_context_return, + shared_secret_return); + + // 6. verify the view_tag (covers step [7] as well) + 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 + rct::xmr_amount recovered_amount_return = decrypt_carrot_amount(return_output.amount_enc, shared_secret_return, return_output.onetime_address); + + // 10. compute k_a' = H_n(s^ctx_sr, a', K^j_s', enote_type') crypto::secret_key recovered_amount_blinding_factor_return; - CarrotEnoteType recovered_enote_type_return; - payment_id_t recovered_payment_id_return; - const bool scan_success_return = try_scan_carrot_enote_external( - return_output, - std::nullopt, - shared_secret_return_unctx, - alice.k_view_incoming_dev, - alice.carrot_account_spend_pubkey, - recovered_sender_extension_g_return, - recovered_sender_extension_t_return, - recovered_address_spend_pubkey_return, - recovered_amount_return, - recovered_amount_blinding_factor_return, - recovered_payment_id_return, - recovered_enote_type_return - ); - ASSERT_TRUE(scan_success_return); + make_carrot_amount_blinding_factor(shared_secret_return, + recovered_amount_return, + change_output.onetime_address, + CarrotEnoteType::PAYMENT, + recovered_amount_blinding_factor_return); + + // 11. compute C_a' = k_a' G + a' H + rct::key recovered_amount_commitment_return = rct::commit(recovered_amount_return, rct::sk2rct(recovered_amount_blinding_factor_return)); + + // 12. verify the commitment + ASSERT_EQ(return_output.amount_commitment, recovered_amount_commitment_return); + + // 13. compute m_pid and pid_enc - not implemented/tested since not supported in SPARC + /* + if (return_output.encrypted_payment_id) + nominal_payment_id_out = decrypt_legacy_payment_id(*encrypted_payment_id, s_sender_receiver, onetime_address); + else + nominal_payment_id_out = null_payment_id; + encrypted_payment_id_out =encrypt_legacy_payment_id(proposal.destination.payment_id, s_sender_receiver, output_enote_out.enote.onetime_address); */ + + // 15. compute m_anchor and anchor' + janus_anchor_t recovered_anchor_return = decrypt_carrot_anchor(return_output.anchor_enc, shared_secret_return, return_output.onetime_address); + + // 17. compute d_e' + crypto::secret_key recovered_ephemeral_privkey_return; + make_carrot_enote_ephemeral_privkey(recovered_anchor_return, input_context_return, change_output.onetime_address, null_payment_id, recovered_ephemeral_privkey_return); + + // 18. compute D_e' + mx25519_pubkey recovered_ephemeral_pubkey_return; + make_carrot_enote_ephemeral_pubkey(recovered_ephemeral_privkey_return, change_output.onetime_address, false, recovered_ephemeral_pubkey_return); + + // 19. verify the enote ephemeral pubkey + ASSERT_EQ(recovered_ephemeral_pubkey_return, return_output.enote_ephemeral_pubkey); + // 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