mirror of
https://github.com/wownero/wownero.git
synced 2026-10-18 08:01:34 +02:00
+3









j-berman
jeffro256
Luke Parker
Lee *!* Clagett
SyntheticBird45
jeetrex17
UkoeHB
MrCyjaneK
tobtoht
selsta
Thomas
navidR
Boog900
a9145f9d38
Co-authored-by: jeffro256 <jeffro256@tutanota.com> Co-authored-by: Luke Parker <lukeparker5132@gmail.com> Co-authored-by: Lee *!* Clagett <code@leeclagett.com> Co-authored-by: SyntheticBird45 <someoneelse.is_on.github.rio7x@simplelogin.com> Co-authored-by: jeetrex17 <jeetrex@tutamail.com> Co-authored-by: UkoeHB <ukoehb@users.noreply.github.com> Co-authored-by: MrCyjaneK Co-authored-by: tobtoht Co-authored-by: selsta Co-authored-by: Thomas <thomas.giudici@proton.me> Co-authored-by: navidR Co-authored-by: Boog900 <boog900@tutanota.com>
1327 lines
48 KiB
C++
1327 lines
48 KiB
C++
// Copyright (c) 2025-2026, The Monero Project
|
|
//
|
|
// All rights reserved.
|
|
//
|
|
// Redistribution and use in source and binary forms, with or without modification, are
|
|
// permitted provided that the following conditions are met:
|
|
//
|
|
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
|
// conditions and the following disclaimer.
|
|
//
|
|
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
|
// of conditions and the following disclaimer in the documentation and/or other
|
|
// materials provided with the distribution.
|
|
//
|
|
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
|
// used to endorse or promote products derived from this software without specific
|
|
// prior written permission.
|
|
//
|
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
|
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
|
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
|
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
|
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
|
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
#include "gtest/gtest.h"
|
|
|
|
#include "carrot_core/enote_utils.h"
|
|
#include "carrot_impl/address_device_ram_borrowed.h"
|
|
#include "carrot_impl/tx_builder_inputs.h"
|
|
#include "carrot_mock_helpers.h"
|
|
#include "fcmp_pp/prove.h"
|
|
|
|
using namespace carrot;
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
TEST(carrot_tx_builder, make_sal_proof_legacy_to_legacy_v1_mainaddr)
|
|
{
|
|
mock::mock_carrot_and_legacy_keys keys;
|
|
keys.generate(AddressDeriveType::PreCarrot);
|
|
|
|
// (K^0_s, K^0_v)
|
|
const CarrotDestinationV1 addr = keys.cryptonote_address();
|
|
|
|
const cryptonote_hierarchy_address_device cn_addr_dev(
|
|
std::make_shared<cryptonote_view_incoming_key_ram_borrowed_device>(keys.k_view_incoming_dev),
|
|
addr.address_spend_pubkey);
|
|
|
|
const crypto::hash signable_tx_hash = crypto::rand<crypto::hash>();
|
|
|
|
// k_e, K_e
|
|
crypto::secret_key enote_ephemeral_privkey;
|
|
crypto::public_key enote_ephemeral_pubkey;
|
|
crypto::generate_keys(enote_ephemeral_pubkey, enote_ephemeral_privkey);
|
|
|
|
// 8 k_e K^0_v
|
|
crypto::key_derivation kd;
|
|
ASSERT_TRUE(crypto::generate_key_derivation(addr.address_view_pubkey, enote_ephemeral_privkey, kd));
|
|
|
|
// K_o = K^0_s + Hs1(8 k_e K^0_v, i) G
|
|
const std::size_t local_output_index = 2;
|
|
crypto::public_key onetime_address;
|
|
crypto::derive_public_key(kd, local_output_index, addr.address_spend_pubkey, onetime_address);
|
|
|
|
// a
|
|
const rct::xmr_amount amount = crypto::rand<rct::xmr_amount>();
|
|
// z
|
|
const crypto::secret_key amount_blinding_factor = mock::gen_secret_key();
|
|
|
|
// C_a = z G + a H
|
|
const amount_commitment_t amount_commitment = commit_carrot_amount(amount, amount_blinding_factor);
|
|
|
|
const LegacyOutputOpeningHintV1 opening_hint{
|
|
.onetime_address = onetime_address,
|
|
.ephemeral_tx_pubkey = enote_ephemeral_pubkey,
|
|
.subaddr_index = {0, 0},
|
|
.amount = amount,
|
|
.amount_blinding_factor = amount_blinding_factor,
|
|
.local_output_index = local_output_index
|
|
};
|
|
|
|
const crypto::key_image expected_key_image = keys.key_image_dev->derive_key_image(opening_hint);
|
|
|
|
// fake output amount blinding factor in a hypothetical tx where we spent the aforementioned output
|
|
const crypto::secret_key output_amount_blinding_factor = mock::gen_secret_key();
|
|
|
|
// make rerandomized outputs
|
|
std::vector<FcmpRerandomizedOutputCompressed> rerandomized_outputs;
|
|
fcmp_pp::make_balanced_rerandomized_output_set(
|
|
{onetime_address},
|
|
{amount_commitment},
|
|
{true},
|
|
{amount_blinding_factor},
|
|
{mock::gen_secret_key()},
|
|
output_amount_blinding_factor,
|
|
rerandomized_outputs);
|
|
ASSERT_EQ(1, rerandomized_outputs.size());
|
|
|
|
// make SA/L proof for spending aforementioned enote
|
|
fcmp_pp::FcmpPpSalProof sal_proof;
|
|
crypto::key_image actual_key_image;
|
|
make_sal_proof_any_to_legacy_v1(signable_tx_hash,
|
|
rerandomized_outputs.front(),
|
|
opening_hint,
|
|
keys.legacy_acb.get_keys().m_spend_secret_key,
|
|
cn_addr_dev,
|
|
sal_proof,
|
|
actual_key_image);
|
|
|
|
ASSERT_EQ(expected_key_image, actual_key_image);
|
|
|
|
// verify SA/L
|
|
EXPECT_TRUE(fcmp_pp::verify_sal(signable_tx_hash,
|
|
rerandomized_outputs.front().input,
|
|
actual_key_image,
|
|
sal_proof));
|
|
}
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
TEST(carrot_tx_builder, make_sal_proof_legacy_to_legacy_v1_subaddr)
|
|
{
|
|
mock::mock_carrot_and_legacy_keys keys;
|
|
keys.generate(AddressDeriveType::PreCarrot);
|
|
|
|
const cryptonote_hierarchy_address_device cn_addr_dev(
|
|
std::make_shared<cryptonote_view_incoming_key_ram_borrowed_device>(keys.k_view_incoming_dev),
|
|
keys.legacy_acb.get_keys().m_account_address.m_spend_public_key);
|
|
|
|
// j
|
|
const subaddress_index_extended subaddress_index = mock::gen_subaddress_index_extended(keys.default_derive_type);
|
|
|
|
// (K^j_s, K^j_v)
|
|
const CarrotDestinationV1 addr = keys.subaddress(subaddress_index);
|
|
|
|
const crypto::hash signable_tx_hash = crypto::rand<crypto::hash>();
|
|
|
|
// k_e, K_e = k_e K^j_s
|
|
const crypto::secret_key enote_ephemeral_privkey = mock::gen_secret_key();
|
|
crypto::public_key enote_ephemeral_pubkey;
|
|
{
|
|
ge_p3 Kjs;
|
|
[[maybe_unused]] const int r = ge_frombytes_vartime(&Kjs, to_bytes(addr.address_spend_pubkey));
|
|
ge_p2 Ke;
|
|
ge_scalarmult(&Ke, to_bytes(enote_ephemeral_privkey), &Kjs);
|
|
ge_tobytes(to_bytes(enote_ephemeral_pubkey), &Ke);
|
|
}
|
|
|
|
// 8 k_e K^j_v
|
|
crypto::key_derivation kd;
|
|
ASSERT_TRUE(crypto::generate_key_derivation(addr.address_view_pubkey, enote_ephemeral_privkey, kd));
|
|
|
|
// K_o = K^j_s + Hs1(8 k_e K^0_v, i) G
|
|
const std::size_t local_output_index = 0;
|
|
crypto::public_key onetime_address;
|
|
crypto::derive_public_key(kd, local_output_index, addr.address_spend_pubkey, onetime_address);
|
|
|
|
// a
|
|
const rct::xmr_amount amount = crypto::rand<rct::xmr_amount>();
|
|
// z
|
|
const crypto::secret_key amount_blinding_factor = mock::gen_secret_key();
|
|
// k^g_o
|
|
const crypto::secret_key sender_extension_g = mock::gen_secret_key();
|
|
|
|
// C_a = z G + a H
|
|
const amount_commitment_t amount_commitment = commit_carrot_amount(amount, amount_blinding_factor);
|
|
|
|
const LegacyOutputOpeningHintV1 opening_hint{
|
|
.onetime_address = onetime_address,
|
|
.ephemeral_tx_pubkey = enote_ephemeral_pubkey,
|
|
.subaddr_index = subaddress_index.index,
|
|
.amount = amount,
|
|
.amount_blinding_factor = amount_blinding_factor,
|
|
.local_output_index = local_output_index
|
|
};
|
|
|
|
const bool use_biased_htp = use_biased_hash_to_point(opening_hint);
|
|
|
|
const crypto::key_image expected_key_image = keys.key_image_dev->derive_key_image(opening_hint);
|
|
|
|
// fake output amount blinding factor in a hypothetical tx where we spent the aforementioned output
|
|
const crypto::secret_key output_amount_blinding_factor = mock::gen_secret_key();
|
|
|
|
// make rerandomized outputs
|
|
std::vector<FcmpRerandomizedOutputCompressed> rerandomized_outputs;
|
|
fcmp_pp::make_balanced_rerandomized_output_set({opening_hint.onetime_address},
|
|
{amount_commitment},
|
|
{use_biased_htp},
|
|
{amount_blinding_factor},
|
|
{mock::gen_secret_key()},
|
|
{output_amount_blinding_factor},
|
|
rerandomized_outputs);
|
|
|
|
ASSERT_EQ(1, rerandomized_outputs.size());
|
|
|
|
// make SA/L proof for spending aforementioned enote
|
|
fcmp_pp::FcmpPpSalProof sal_proof;
|
|
crypto::key_image actual_key_image;
|
|
make_sal_proof_any_to_legacy_v1(signable_tx_hash,
|
|
rerandomized_outputs.front(),
|
|
opening_hint,
|
|
keys.legacy_acb.get_keys().m_spend_secret_key,
|
|
cn_addr_dev,
|
|
sal_proof,
|
|
actual_key_image);
|
|
|
|
ASSERT_EQ(expected_key_image, actual_key_image);
|
|
|
|
// verify SA/L
|
|
EXPECT_TRUE(fcmp_pp::verify_sal(signable_tx_hash,
|
|
rerandomized_outputs.front().input,
|
|
actual_key_image,
|
|
sal_proof));
|
|
}
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
TEST(carrot_tx_builder, make_sal_proof_carrot_to_legacy_v1_mainaddr_normal)
|
|
{
|
|
mock::mock_carrot_and_legacy_keys keys;
|
|
keys.generate(AddressDeriveType::PreCarrot);
|
|
|
|
const cryptonote_hierarchy_address_device cn_addr_dev(
|
|
std::make_shared<cryptonote_view_incoming_key_ram_borrowed_device>(keys.k_view_incoming_dev),
|
|
keys.legacy_acb.get_keys().m_account_address.m_spend_public_key);
|
|
|
|
// (K^0_s, K^0_v)
|
|
const CarrotDestinationV1 addr = keys.cryptonote_address();
|
|
|
|
const crypto::key_image tx_first_key_image = mock::gen_key_image();
|
|
|
|
const CarrotPaymentProposalV1 normal_payment_proposal{
|
|
.destination = addr,
|
|
.amount = crypto::rand<rct::xmr_amount>(),
|
|
.randomness = gen_janus_anchor()
|
|
};
|
|
|
|
RCTOutputEnoteProposal output_enote_proposal;
|
|
encrypted_payment_id_t encrypted_payment_id;
|
|
get_output_proposal_normal_v1(normal_payment_proposal,
|
|
tx_first_key_image,
|
|
output_enote_proposal,
|
|
encrypted_payment_id);
|
|
|
|
const CarrotEnoteV1 &enote = output_enote_proposal.enote;
|
|
|
|
// scan enote to get sender extensions and calculate expected key image
|
|
std::vector<mock::mock_scan_result_t> scan_results;
|
|
mock::mock_scan_enote_set({enote},
|
|
encrypted_payment_id,
|
|
keys,
|
|
scan_results);
|
|
|
|
ASSERT_EQ(1, scan_results.size());
|
|
|
|
const mock::mock_scan_result_t &scan_result = scan_results.front();
|
|
|
|
ASSERT_EQ(addr.address_spend_pubkey, scan_result.address_spend_pubkey);
|
|
|
|
const CarrotOutputOpeningHintV1 opening_hint{
|
|
.source_enote = enote,
|
|
.encrypted_payment_id = encrypted_payment_id,
|
|
.subaddr_index = {{0, 0}, keys.default_derive_type}
|
|
};
|
|
|
|
const bool use_biased_htp = use_biased_hash_to_point(opening_hint);
|
|
|
|
const crypto::key_image expected_key_image = keys.derive_key_image(addr.address_spend_pubkey,
|
|
scan_result.sender_extension_g,
|
|
scan_result.sender_extension_t,
|
|
enote.onetime_address,
|
|
use_biased_htp);
|
|
|
|
// fake output amount blinding factor in a hypothetical tx where we spent the aforementioned output
|
|
const crypto::secret_key output_amount_blinding_factor = mock::gen_secret_key();
|
|
const crypto::hash signable_tx_hash = crypto::rand<crypto::hash>();
|
|
|
|
// make rerandomized outputs
|
|
std::vector<FcmpRerandomizedOutputCompressed> rerandomized_outputs;
|
|
fcmp_pp::make_balanced_rerandomized_output_set({enote.onetime_address},
|
|
{enote.amount_commitment},
|
|
{use_biased_htp},
|
|
{scan_result.amount_blinding_factor},
|
|
{mock::gen_secret_key()},
|
|
{output_amount_blinding_factor},
|
|
rerandomized_outputs);
|
|
|
|
ASSERT_EQ(1, rerandomized_outputs.size());
|
|
|
|
// make SA/L proof for spending aforementioned enote
|
|
fcmp_pp::FcmpPpSalProof sal_proof;
|
|
crypto::key_image actual_key_image;
|
|
make_sal_proof_any_to_legacy_v1(signable_tx_hash,
|
|
rerandomized_outputs.front(),
|
|
opening_hint,
|
|
keys.legacy_acb.get_keys().m_spend_secret_key,
|
|
cn_addr_dev,
|
|
sal_proof,
|
|
actual_key_image);
|
|
|
|
ASSERT_EQ(expected_key_image, actual_key_image);
|
|
|
|
// verify SA/L
|
|
EXPECT_TRUE(fcmp_pp::verify_sal(signable_tx_hash,
|
|
rerandomized_outputs.front().input,
|
|
actual_key_image,
|
|
sal_proof));
|
|
}
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
TEST(carrot_tx_builder, make_sal_proof_carrot_to_legacy_v1_subaddr_normal)
|
|
{
|
|
mock::mock_carrot_and_legacy_keys keys;
|
|
keys.generate(AddressDeriveType::PreCarrot);
|
|
|
|
const cryptonote_hierarchy_address_device cn_addr_dev(
|
|
std::make_shared<cryptonote_view_incoming_key_ram_borrowed_device>(keys.k_view_incoming_dev),
|
|
keys.legacy_acb.get_keys().m_account_address.m_spend_public_key);
|
|
|
|
// j
|
|
const subaddress_index subaddr_index = mock::gen_subaddress_index();
|
|
|
|
// (K^j_s, K^j_v)
|
|
const CarrotDestinationV1 addr = keys.subaddress({subaddr_index});
|
|
|
|
const crypto::key_image tx_first_key_image = mock::gen_key_image();
|
|
|
|
const CarrotPaymentProposalV1 normal_payment_proposal{
|
|
.destination = addr,
|
|
.amount = crypto::rand<rct::xmr_amount>(),
|
|
.randomness = gen_janus_anchor()
|
|
};
|
|
|
|
RCTOutputEnoteProposal output_enote_proposal;
|
|
encrypted_payment_id_t encrypted_payment_id;
|
|
get_output_proposal_normal_v1(normal_payment_proposal,
|
|
tx_first_key_image,
|
|
output_enote_proposal,
|
|
encrypted_payment_id);
|
|
|
|
const CarrotEnoteV1 &enote = output_enote_proposal.enote;
|
|
|
|
// scan enote to get sender extensions and calculate expected key image
|
|
std::vector<mock::mock_scan_result_t> scan_results;
|
|
mock::mock_scan_enote_set({enote},
|
|
encrypted_payment_id,
|
|
keys,
|
|
scan_results);
|
|
|
|
ASSERT_EQ(1, scan_results.size());
|
|
|
|
const mock::mock_scan_result_t &scan_result = scan_results.front();
|
|
|
|
ASSERT_EQ(addr.address_spend_pubkey, scan_result.address_spend_pubkey);
|
|
|
|
const CarrotOutputOpeningHintV1 opening_hint{
|
|
.source_enote = enote,
|
|
.encrypted_payment_id = encrypted_payment_id,
|
|
.subaddr_index = {subaddr_index, keys.default_derive_type}
|
|
};
|
|
|
|
const bool use_biased_htp = use_biased_hash_to_point(opening_hint);
|
|
|
|
const crypto::key_image expected_key_image = keys.derive_key_image(addr.address_spend_pubkey,
|
|
scan_result.sender_extension_g,
|
|
scan_result.sender_extension_t,
|
|
enote.onetime_address,
|
|
use_biased_htp);
|
|
|
|
// fake output amount blinding factor in a hypothetical tx where we spent the aforementioned output
|
|
const crypto::secret_key output_amount_blinding_factor = mock::gen_secret_key();
|
|
const crypto::hash signable_tx_hash = crypto::rand<crypto::hash>();
|
|
|
|
// make rerandomized outputs
|
|
std::vector<FcmpRerandomizedOutputCompressed> rerandomized_outputs;
|
|
fcmp_pp::make_balanced_rerandomized_output_set({enote.onetime_address},
|
|
{enote.amount_commitment},
|
|
{use_biased_htp},
|
|
{scan_result.amount_blinding_factor},
|
|
{mock::gen_secret_key()},
|
|
{output_amount_blinding_factor},
|
|
rerandomized_outputs);
|
|
|
|
ASSERT_EQ(1, rerandomized_outputs.size());
|
|
|
|
// make SA/L proof for spending aforementioned enote
|
|
fcmp_pp::FcmpPpSalProof sal_proof;
|
|
crypto::key_image actual_key_image;
|
|
make_sal_proof_any_to_legacy_v1(signable_tx_hash,
|
|
rerandomized_outputs.front(),
|
|
opening_hint,
|
|
keys.legacy_acb.get_keys().m_spend_secret_key,
|
|
cn_addr_dev,
|
|
sal_proof,
|
|
actual_key_image);
|
|
|
|
ASSERT_EQ(expected_key_image, actual_key_image);
|
|
|
|
// verify SA/L
|
|
EXPECT_TRUE(fcmp_pp::verify_sal(signable_tx_hash,
|
|
rerandomized_outputs.front().input,
|
|
actual_key_image,
|
|
sal_proof));
|
|
}
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
TEST(carrot_tx_builder, make_sal_proof_carrot_to_legacy_v1_subaddr_special)
|
|
{
|
|
mock::mock_carrot_and_legacy_keys keys;
|
|
keys.generate(AddressDeriveType::PreCarrot);
|
|
|
|
const cryptonote_hierarchy_address_device cn_addr_dev(
|
|
std::make_shared<cryptonote_view_incoming_key_ram_borrowed_device>(keys.k_view_incoming_dev),
|
|
keys.legacy_acb.get_keys().m_account_address.m_spend_public_key);
|
|
|
|
// j
|
|
const subaddress_index subaddr_index = mock::gen_subaddress_index();
|
|
|
|
// (K^j_s, K^j_v)
|
|
const CarrotDestinationV1 addr = keys.subaddress({subaddr_index});
|
|
|
|
const crypto::key_image tx_first_key_image = mock::gen_key_image();
|
|
|
|
const CarrotPaymentProposalSelfSendV1 selfsend_payment_proposal{
|
|
.destination_address_spend_pubkey = addr.address_spend_pubkey,
|
|
.amount = crypto::rand<rct::xmr_amount>(),
|
|
.enote_type = CarrotEnoteType::CHANGE,
|
|
.enote_ephemeral_privkey = mock::gen_secret_key()
|
|
};
|
|
|
|
RCTOutputEnoteProposal output_enote_proposal;
|
|
get_output_proposal_special_v1(selfsend_payment_proposal,
|
|
keys.k_view_incoming_dev,
|
|
tx_first_key_image,
|
|
nullptr,
|
|
nullptr,
|
|
output_enote_proposal);
|
|
|
|
const CarrotEnoteV1 &enote = output_enote_proposal.enote;
|
|
|
|
// scan enote to get sender extensions and calculate expected key image
|
|
std::vector<mock::mock_scan_result_t> scan_results;
|
|
mock::mock_scan_enote_set({enote},
|
|
gen_encrypted_payment_id(),
|
|
keys,
|
|
scan_results);
|
|
|
|
ASSERT_EQ(1, scan_results.size());
|
|
|
|
const mock::mock_scan_result_t &scan_result = scan_results.front();
|
|
|
|
ASSERT_EQ(addr.address_spend_pubkey, scan_result.address_spend_pubkey);
|
|
|
|
const CarrotOutputOpeningHintV1 opening_hint{
|
|
.source_enote = enote,
|
|
.encrypted_payment_id = std::nullopt,
|
|
.subaddr_index = {subaddr_index, keys.default_derive_type}
|
|
};
|
|
|
|
const bool use_biased_htp = use_biased_hash_to_point(opening_hint);
|
|
|
|
const crypto::key_image expected_key_image = keys.derive_key_image(addr.address_spend_pubkey,
|
|
scan_result.sender_extension_g,
|
|
scan_result.sender_extension_t,
|
|
enote.onetime_address,
|
|
use_biased_htp);
|
|
|
|
// fake output amount blinding factor in a hypothetical tx where we spent the aforementioned output
|
|
const crypto::secret_key output_amount_blinding_factor = mock::gen_secret_key();
|
|
const crypto::hash signable_tx_hash = crypto::rand<crypto::hash>();
|
|
|
|
// make rerandomized outputs
|
|
std::vector<FcmpRerandomizedOutputCompressed> rerandomized_outputs;
|
|
fcmp_pp::make_balanced_rerandomized_output_set({enote.onetime_address},
|
|
{enote.amount_commitment},
|
|
{use_biased_htp},
|
|
{scan_result.amount_blinding_factor},
|
|
{mock::gen_secret_key()},
|
|
{output_amount_blinding_factor},
|
|
rerandomized_outputs);
|
|
|
|
ASSERT_EQ(1, rerandomized_outputs.size());
|
|
|
|
// make SA/L proof for spending aforementioned enote
|
|
fcmp_pp::FcmpPpSalProof sal_proof;
|
|
crypto::key_image actual_key_image;
|
|
make_sal_proof_any_to_legacy_v1(signable_tx_hash,
|
|
rerandomized_outputs.front(),
|
|
opening_hint,
|
|
keys.legacy_acb.get_keys().m_spend_secret_key,
|
|
cn_addr_dev,
|
|
sal_proof,
|
|
actual_key_image);
|
|
|
|
ASSERT_EQ(expected_key_image, actual_key_image);
|
|
|
|
// verify SA/L
|
|
EXPECT_TRUE(fcmp_pp::verify_sal(signable_tx_hash,
|
|
rerandomized_outputs.front().input,
|
|
actual_key_image,
|
|
sal_proof));
|
|
}
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
TEST(carrot_tx_builder, make_sal_proof_carrot_to_legacy_v1_mainaddr_special)
|
|
{
|
|
mock::mock_carrot_and_legacy_keys keys;
|
|
keys.generate(AddressDeriveType::PreCarrot);
|
|
|
|
const cryptonote_hierarchy_address_device cn_addr_dev(
|
|
std::make_shared<cryptonote_view_incoming_key_ram_borrowed_device>(keys.k_view_incoming_dev),
|
|
keys.legacy_acb.get_keys().m_account_address.m_spend_public_key);
|
|
|
|
// (K^0_s, K^0_v)
|
|
const CarrotDestinationV1 addr = keys.cryptonote_address();
|
|
|
|
const crypto::key_image tx_first_key_image = mock::gen_key_image();
|
|
|
|
const CarrotPaymentProposalSelfSendV1 selfsend_payment_proposal{
|
|
.destination_address_spend_pubkey = addr.address_spend_pubkey,
|
|
.amount = crypto::rand<rct::xmr_amount>(),
|
|
.enote_type = CarrotEnoteType::CHANGE,
|
|
.enote_ephemeral_privkey = mock::gen_secret_key()
|
|
};
|
|
|
|
RCTOutputEnoteProposal output_enote_proposal;
|
|
get_output_proposal_special_v1(selfsend_payment_proposal,
|
|
keys.k_view_incoming_dev,
|
|
tx_first_key_image,
|
|
nullptr,
|
|
nullptr,
|
|
output_enote_proposal);
|
|
|
|
const CarrotEnoteV1 &enote = output_enote_proposal.enote;
|
|
|
|
// scan enote to get sender extensions and calculate expected key image
|
|
std::vector<mock::mock_scan_result_t> scan_results;
|
|
mock::mock_scan_enote_set({enote},
|
|
gen_encrypted_payment_id(),
|
|
keys,
|
|
scan_results);
|
|
|
|
ASSERT_EQ(1, scan_results.size());
|
|
|
|
const mock::mock_scan_result_t &scan_result = scan_results.front();
|
|
|
|
ASSERT_EQ(addr.address_spend_pubkey, scan_result.address_spend_pubkey);
|
|
|
|
const CarrotOutputOpeningHintV1 opening_hint{
|
|
.source_enote = enote,
|
|
.encrypted_payment_id = std::nullopt,
|
|
.subaddr_index = {{0, 0}, keys.default_derive_type}
|
|
};
|
|
|
|
const bool use_biased_htp = use_biased_hash_to_point(opening_hint);
|
|
|
|
const crypto::key_image expected_key_image = keys.derive_key_image(addr.address_spend_pubkey,
|
|
scan_result.sender_extension_g,
|
|
scan_result.sender_extension_t,
|
|
enote.onetime_address,
|
|
use_biased_htp);
|
|
|
|
// fake output amount blinding factor in a hypothetical tx where we spent the aforementioned output
|
|
const crypto::secret_key output_amount_blinding_factor = mock::gen_secret_key();
|
|
const crypto::hash signable_tx_hash = crypto::rand<crypto::hash>();
|
|
|
|
// make rerandomized outputs
|
|
std::vector<FcmpRerandomizedOutputCompressed> rerandomized_outputs;
|
|
fcmp_pp::make_balanced_rerandomized_output_set({enote.onetime_address},
|
|
{enote.amount_commitment},
|
|
{use_biased_htp},
|
|
{scan_result.amount_blinding_factor},
|
|
{mock::gen_secret_key()},
|
|
{output_amount_blinding_factor},
|
|
rerandomized_outputs);
|
|
|
|
ASSERT_EQ(1, rerandomized_outputs.size());
|
|
|
|
// make SA/L proof for spending aforementioned enote
|
|
fcmp_pp::FcmpPpSalProof sal_proof;
|
|
crypto::key_image actual_key_image;
|
|
make_sal_proof_any_to_legacy_v1(signable_tx_hash,
|
|
rerandomized_outputs.front(),
|
|
opening_hint,
|
|
keys.legacy_acb.get_keys().m_spend_secret_key,
|
|
cn_addr_dev,
|
|
sal_proof,
|
|
actual_key_image);
|
|
|
|
ASSERT_EQ(expected_key_image, actual_key_image);
|
|
|
|
// verify SA/L
|
|
EXPECT_TRUE(fcmp_pp::verify_sal(signable_tx_hash,
|
|
rerandomized_outputs.front().input,
|
|
actual_key_image,
|
|
sal_proof));
|
|
}
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
TEST(carrot_tx_builder, make_sal_proof_carrot_to_carrot_v1_mainaddr_normal)
|
|
{
|
|
mock::mock_carrot_and_legacy_keys keys;
|
|
keys.generate();
|
|
|
|
// (K^0_s, K^0_v)
|
|
const CarrotDestinationV1 addr = keys.cryptonote_address();
|
|
|
|
const crypto::key_image tx_first_key_image = mock::gen_key_image();
|
|
|
|
const CarrotPaymentProposalV1 normal_payment_proposal{
|
|
.destination = addr,
|
|
.amount = crypto::rand<rct::xmr_amount>(),
|
|
.randomness = gen_janus_anchor()
|
|
};
|
|
|
|
RCTOutputEnoteProposal output_enote_proposal;
|
|
encrypted_payment_id_t encrypted_payment_id;
|
|
get_output_proposal_normal_v1(normal_payment_proposal,
|
|
tx_first_key_image,
|
|
output_enote_proposal,
|
|
encrypted_payment_id);
|
|
|
|
const CarrotEnoteV1 &enote = output_enote_proposal.enote;
|
|
|
|
// scan enote to get sender extensions and calculate expected key image
|
|
std::vector<mock::mock_scan_result_t> scan_results;
|
|
mock::mock_scan_enote_set({enote},
|
|
encrypted_payment_id,
|
|
keys,
|
|
scan_results);
|
|
|
|
ASSERT_EQ(1, scan_results.size());
|
|
|
|
const mock::mock_scan_result_t &scan_result = scan_results.front();
|
|
|
|
ASSERT_EQ(addr.address_spend_pubkey, scan_result.address_spend_pubkey);
|
|
|
|
const CarrotOutputOpeningHintV1 opening_hint{
|
|
.source_enote = enote,
|
|
.encrypted_payment_id = encrypted_payment_id,
|
|
.subaddr_index = {{0, 0}, keys.default_derive_type}
|
|
};
|
|
|
|
const bool use_biased_htp = use_biased_hash_to_point(opening_hint);
|
|
|
|
const crypto::key_image expected_key_image = keys.derive_key_image(addr.address_spend_pubkey,
|
|
scan_result.sender_extension_g,
|
|
scan_result.sender_extension_t,
|
|
enote.onetime_address,
|
|
use_biased_htp);
|
|
|
|
// fake output amount blinding factor in a hypothetical tx where we spent the aforementioned output
|
|
const crypto::secret_key output_amount_blinding_factor = mock::gen_secret_key();
|
|
const crypto::hash signable_tx_hash = crypto::rand<crypto::hash>();
|
|
|
|
// make rerandomized outputs
|
|
std::vector<FcmpRerandomizedOutputCompressed> rerandomized_outputs;
|
|
fcmp_pp::make_balanced_rerandomized_output_set({enote.onetime_address},
|
|
{enote.amount_commitment},
|
|
{use_biased_htp},
|
|
{scan_result.amount_blinding_factor},
|
|
{mock::gen_secret_key()},
|
|
{output_amount_blinding_factor},
|
|
rerandomized_outputs);
|
|
|
|
ASSERT_EQ(1, rerandomized_outputs.size());
|
|
|
|
// make SA/L proof for spending aforementioned enote
|
|
fcmp_pp::FcmpPpSalProof sal_proof;
|
|
crypto::key_image actual_key_image;
|
|
make_sal_proof_any_to_carrot_v1(signable_tx_hash,
|
|
rerandomized_outputs.front(),
|
|
opening_hint,
|
|
keys.k_prove_spend,
|
|
keys.k_generate_image,
|
|
*keys.addr_dev,
|
|
keys.s_view_balance_dev,
|
|
keys.k_view_incoming_dev,
|
|
sal_proof,
|
|
actual_key_image);
|
|
|
|
ASSERT_EQ(expected_key_image, actual_key_image);
|
|
|
|
// verify SA/L
|
|
EXPECT_TRUE(fcmp_pp::verify_sal(signable_tx_hash,
|
|
rerandomized_outputs.front().input,
|
|
actual_key_image,
|
|
sal_proof));
|
|
}
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
TEST(carrot_tx_builder, make_sal_proof_carrot_to_carrot_v1_subaddr_normal)
|
|
{
|
|
mock::mock_carrot_and_legacy_keys keys;
|
|
keys.generate();
|
|
|
|
// j
|
|
const subaddress_index subaddr_index = mock::gen_subaddress_index();
|
|
|
|
// (K^j_s, K^j_v)
|
|
const CarrotDestinationV1 addr = keys.subaddress({subaddr_index});
|
|
|
|
const crypto::key_image tx_first_key_image = mock::gen_key_image();
|
|
|
|
const CarrotPaymentProposalV1 normal_payment_proposal{
|
|
.destination = addr,
|
|
.amount = crypto::rand<rct::xmr_amount>(),
|
|
.randomness = gen_janus_anchor()
|
|
};
|
|
|
|
RCTOutputEnoteProposal output_enote_proposal;
|
|
encrypted_payment_id_t encrypted_payment_id;
|
|
get_output_proposal_normal_v1(normal_payment_proposal,
|
|
tx_first_key_image,
|
|
output_enote_proposal,
|
|
encrypted_payment_id);
|
|
|
|
const CarrotEnoteV1 &enote = output_enote_proposal.enote;
|
|
|
|
// scan enote to get sender extensions and calculate expected key image
|
|
std::vector<mock::mock_scan_result_t> scan_results;
|
|
mock::mock_scan_enote_set({enote},
|
|
encrypted_payment_id,
|
|
keys,
|
|
scan_results);
|
|
|
|
ASSERT_EQ(1, scan_results.size());
|
|
|
|
const mock::mock_scan_result_t &scan_result = scan_results.front();
|
|
|
|
ASSERT_EQ(addr.address_spend_pubkey, scan_result.address_spend_pubkey);
|
|
|
|
const CarrotOutputOpeningHintV1 opening_hint{
|
|
.source_enote = enote,
|
|
.encrypted_payment_id = encrypted_payment_id,
|
|
.subaddr_index = {subaddr_index, keys.default_derive_type}
|
|
};
|
|
|
|
const bool use_biased_htp = use_biased_hash_to_point(opening_hint);
|
|
|
|
const crypto::key_image expected_key_image = keys.derive_key_image(addr.address_spend_pubkey,
|
|
scan_result.sender_extension_g,
|
|
scan_result.sender_extension_t,
|
|
enote.onetime_address,
|
|
use_biased_htp);
|
|
|
|
// fake output amount blinding factor in a hypothetical tx where we spent the aforementioned output
|
|
const crypto::secret_key output_amount_blinding_factor = mock::gen_secret_key();
|
|
const crypto::hash signable_tx_hash = crypto::rand<crypto::hash>();
|
|
|
|
// make rerandomized outputs
|
|
std::vector<FcmpRerandomizedOutputCompressed> rerandomized_outputs;
|
|
fcmp_pp::make_balanced_rerandomized_output_set({enote.onetime_address},
|
|
{enote.amount_commitment},
|
|
{use_biased_htp},
|
|
{scan_result.amount_blinding_factor},
|
|
{mock::gen_secret_key()},
|
|
{output_amount_blinding_factor},
|
|
rerandomized_outputs);
|
|
|
|
ASSERT_EQ(1, rerandomized_outputs.size());
|
|
|
|
// make SA/L proof for spending aforementioned enote
|
|
fcmp_pp::FcmpPpSalProof sal_proof;
|
|
crypto::key_image actual_key_image;
|
|
make_sal_proof_any_to_carrot_v1(signable_tx_hash,
|
|
rerandomized_outputs.front(),
|
|
opening_hint,
|
|
keys.k_prove_spend,
|
|
keys.k_generate_image,
|
|
*keys.addr_dev,
|
|
keys.s_view_balance_dev,
|
|
keys.k_view_incoming_dev,
|
|
sal_proof,
|
|
actual_key_image);
|
|
|
|
ASSERT_EQ(expected_key_image, actual_key_image);
|
|
|
|
// verify SA/L
|
|
EXPECT_TRUE(fcmp_pp::verify_sal(signable_tx_hash,
|
|
rerandomized_outputs.front().input,
|
|
actual_key_image,
|
|
sal_proof));
|
|
}
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
TEST(carrot_tx_builder, make_sal_proof_carrot_to_carrot_v1_mainaddr_special)
|
|
{
|
|
mock::mock_carrot_and_legacy_keys keys;
|
|
keys.generate();
|
|
|
|
// (K^0_s, K^0_v)
|
|
const CarrotDestinationV1 addr = keys.cryptonote_address();
|
|
|
|
const crypto::key_image tx_first_key_image = mock::gen_key_image();
|
|
|
|
const CarrotPaymentProposalSelfSendV1 selfsend_payment_proposal{
|
|
.destination_address_spend_pubkey = addr.address_spend_pubkey,
|
|
.amount = crypto::rand<rct::xmr_amount>(),
|
|
.enote_type = CarrotEnoteType::PAYMENT,
|
|
.enote_ephemeral_privkey = mock::gen_secret_key()
|
|
};
|
|
|
|
RCTOutputEnoteProposal output_enote_proposal;
|
|
get_output_proposal_special_v1(selfsend_payment_proposal,
|
|
keys.k_view_incoming_dev,
|
|
tx_first_key_image,
|
|
nullptr,
|
|
nullptr,
|
|
output_enote_proposal);
|
|
|
|
const CarrotEnoteV1 &enote = output_enote_proposal.enote;
|
|
|
|
// scan enote to get sender extensions and calculate expected key image
|
|
std::vector<mock::mock_scan_result_t> scan_results;
|
|
mock::mock_scan_enote_set({enote},
|
|
gen_encrypted_payment_id(),
|
|
keys,
|
|
scan_results);
|
|
|
|
ASSERT_EQ(1, scan_results.size());
|
|
|
|
const mock::mock_scan_result_t &scan_result = scan_results.front();
|
|
|
|
ASSERT_EQ(addr.address_spend_pubkey, scan_result.address_spend_pubkey);
|
|
|
|
const CarrotOutputOpeningHintV1 opening_hint{
|
|
.source_enote = enote,
|
|
.encrypted_payment_id = std::nullopt,
|
|
.subaddr_index = {{0, 0}, keys.default_derive_type}
|
|
};
|
|
|
|
const bool use_biased_htp = use_biased_hash_to_point(opening_hint);
|
|
|
|
const crypto::key_image expected_key_image = keys.derive_key_image(addr.address_spend_pubkey,
|
|
scan_result.sender_extension_g,
|
|
scan_result.sender_extension_t,
|
|
enote.onetime_address,
|
|
use_biased_htp);
|
|
|
|
// fake output amount blinding factor in a hypothetical tx where we spent the aforementioned output
|
|
const crypto::secret_key output_amount_blinding_factor = mock::gen_secret_key();
|
|
const crypto::hash signable_tx_hash = crypto::rand<crypto::hash>();
|
|
|
|
// make rerandomized outputs
|
|
std::vector<FcmpRerandomizedOutputCompressed> rerandomized_outputs;
|
|
fcmp_pp::make_balanced_rerandomized_output_set({enote.onetime_address},
|
|
{enote.amount_commitment},
|
|
{use_biased_htp},
|
|
{scan_result.amount_blinding_factor},
|
|
{mock::gen_secret_key()},
|
|
{output_amount_blinding_factor},
|
|
rerandomized_outputs);
|
|
|
|
ASSERT_EQ(1, rerandomized_outputs.size());
|
|
|
|
// make SA/L proof for spending aforementioned enote
|
|
fcmp_pp::FcmpPpSalProof sal_proof;
|
|
crypto::key_image actual_key_image;
|
|
make_sal_proof_any_to_carrot_v1(signable_tx_hash,
|
|
rerandomized_outputs.front(),
|
|
opening_hint,
|
|
keys.k_prove_spend,
|
|
keys.k_generate_image,
|
|
*keys.addr_dev,
|
|
keys.s_view_balance_dev,
|
|
keys.k_view_incoming_dev,
|
|
sal_proof,
|
|
actual_key_image);
|
|
|
|
ASSERT_EQ(expected_key_image, actual_key_image);
|
|
|
|
// verify SA/L
|
|
EXPECT_TRUE(fcmp_pp::verify_sal(signable_tx_hash,
|
|
rerandomized_outputs.front().input,
|
|
actual_key_image,
|
|
sal_proof));
|
|
}
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
TEST(carrot_tx_builder, make_sal_proof_carrot_to_carrot_v1_subaddr_special)
|
|
{
|
|
mock::mock_carrot_and_legacy_keys keys;
|
|
keys.generate();
|
|
|
|
// j
|
|
const subaddress_index subaddr_index = mock::gen_subaddress_index();
|
|
|
|
// (K^j_s, K^j_v)
|
|
const CarrotDestinationV1 addr = keys.subaddress({subaddr_index});
|
|
|
|
const crypto::key_image tx_first_key_image = mock::gen_key_image();
|
|
|
|
const CarrotPaymentProposalSelfSendV1 selfsend_payment_proposal{
|
|
.destination_address_spend_pubkey = addr.address_spend_pubkey,
|
|
.amount = crypto::rand<rct::xmr_amount>(),
|
|
.enote_type = CarrotEnoteType::CHANGE,
|
|
.enote_ephemeral_privkey = mock::gen_secret_key()
|
|
};
|
|
|
|
RCTOutputEnoteProposal output_enote_proposal;
|
|
get_output_proposal_special_v1(selfsend_payment_proposal,
|
|
keys.k_view_incoming_dev,
|
|
tx_first_key_image,
|
|
nullptr,
|
|
nullptr,
|
|
output_enote_proposal);
|
|
|
|
const CarrotEnoteV1 &enote = output_enote_proposal.enote;
|
|
|
|
// scan enote to get sender extensions and calculate expected key image
|
|
std::vector<mock::mock_scan_result_t> scan_results;
|
|
mock::mock_scan_enote_set({enote},
|
|
gen_encrypted_payment_id(),
|
|
keys,
|
|
scan_results);
|
|
|
|
ASSERT_EQ(1, scan_results.size());
|
|
|
|
const mock::mock_scan_result_t &scan_result = scan_results.front();
|
|
|
|
ASSERT_EQ(addr.address_spend_pubkey, scan_result.address_spend_pubkey);
|
|
|
|
const CarrotOutputOpeningHintV1 opening_hint{
|
|
.source_enote = enote,
|
|
.encrypted_payment_id = std::nullopt,
|
|
.subaddr_index = {subaddr_index, keys.default_derive_type}
|
|
};
|
|
|
|
const bool use_biased_htp = use_biased_hash_to_point(opening_hint);
|
|
|
|
const crypto::key_image expected_key_image = keys.derive_key_image(addr.address_spend_pubkey,
|
|
scan_result.sender_extension_g,
|
|
scan_result.sender_extension_t,
|
|
enote.onetime_address,
|
|
use_biased_htp);
|
|
|
|
// fake output amount blinding factor in a hypothetical tx where we spent the aforementioned output
|
|
const crypto::secret_key output_amount_blinding_factor = mock::gen_secret_key();
|
|
const crypto::hash signable_tx_hash = crypto::rand<crypto::hash>();
|
|
|
|
// make rerandomized outputs
|
|
std::vector<FcmpRerandomizedOutputCompressed> rerandomized_outputs;
|
|
fcmp_pp::make_balanced_rerandomized_output_set({enote.onetime_address},
|
|
{enote.amount_commitment},
|
|
{use_biased_htp},
|
|
{scan_result.amount_blinding_factor},
|
|
{mock::gen_secret_key()},
|
|
{output_amount_blinding_factor},
|
|
rerandomized_outputs);
|
|
|
|
ASSERT_EQ(1, rerandomized_outputs.size());
|
|
|
|
// make SA/L proof for spending aforementioned enote
|
|
fcmp_pp::FcmpPpSalProof sal_proof;
|
|
crypto::key_image actual_key_image;
|
|
make_sal_proof_any_to_carrot_v1(signable_tx_hash,
|
|
rerandomized_outputs.front(),
|
|
opening_hint,
|
|
keys.k_prove_spend,
|
|
keys.k_generate_image,
|
|
*keys.addr_dev,
|
|
keys.s_view_balance_dev,
|
|
keys.k_view_incoming_dev,
|
|
sal_proof,
|
|
actual_key_image);
|
|
|
|
ASSERT_EQ(expected_key_image, actual_key_image);
|
|
|
|
// verify SA/L
|
|
EXPECT_TRUE(fcmp_pp::verify_sal(signable_tx_hash,
|
|
rerandomized_outputs.front().input,
|
|
actual_key_image,
|
|
sal_proof));
|
|
}
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
TEST(carrot_tx_builder, make_sal_proof_carrot_to_carrot_v1_mainaddr_internal)
|
|
{
|
|
mock::mock_carrot_and_legacy_keys keys;
|
|
keys.generate();
|
|
|
|
// (K^0_s, K^0_v)
|
|
const CarrotDestinationV1 addr = keys.cryptonote_address();
|
|
|
|
const crypto::key_image tx_first_key_image = mock::gen_key_image();
|
|
|
|
const CarrotPaymentProposalSelfSendV1 selfsend_payment_proposal{
|
|
.destination_address_spend_pubkey = addr.address_spend_pubkey,
|
|
.amount = crypto::rand<rct::xmr_amount>(),
|
|
.enote_type = CarrotEnoteType::PAYMENT,
|
|
.enote_ephemeral_privkey = mock::gen_secret_key()
|
|
};
|
|
|
|
RCTOutputEnoteProposal output_enote_proposal;
|
|
get_output_proposal_internal_v1(selfsend_payment_proposal,
|
|
keys.s_view_balance_dev,
|
|
tx_first_key_image,
|
|
nullptr,
|
|
nullptr,
|
|
output_enote_proposal);
|
|
|
|
const CarrotEnoteV1 &enote = output_enote_proposal.enote;
|
|
|
|
// scan enote to get sender extensions and calculate expected key image
|
|
std::vector<mock::mock_scan_result_t> scan_results;
|
|
mock::mock_scan_enote_set({enote},
|
|
gen_encrypted_payment_id(),
|
|
keys,
|
|
scan_results);
|
|
|
|
ASSERT_EQ(1, scan_results.size());
|
|
|
|
const mock::mock_scan_result_t &scan_result = scan_results.front();
|
|
|
|
ASSERT_EQ(addr.address_spend_pubkey, scan_result.address_spend_pubkey);
|
|
|
|
const CarrotOutputOpeningHintV1 opening_hint{
|
|
.source_enote = enote,
|
|
.encrypted_payment_id = std::nullopt,
|
|
.subaddr_index = {{0, 0}, keys.default_derive_type}
|
|
};
|
|
|
|
const bool use_biased_htp = use_biased_hash_to_point(opening_hint);
|
|
|
|
const crypto::key_image expected_key_image = keys.derive_key_image(addr.address_spend_pubkey,
|
|
scan_result.sender_extension_g,
|
|
scan_result.sender_extension_t,
|
|
enote.onetime_address,
|
|
use_biased_htp);
|
|
|
|
// fake output amount blinding factor in a hypothetical tx where we spent the aforementioned output
|
|
const crypto::secret_key output_amount_blinding_factor = mock::gen_secret_key();
|
|
const crypto::hash signable_tx_hash = crypto::rand<crypto::hash>();
|
|
|
|
// make rerandomized outputs
|
|
std::vector<FcmpRerandomizedOutputCompressed> rerandomized_outputs;
|
|
fcmp_pp::make_balanced_rerandomized_output_set({enote.onetime_address},
|
|
{enote.amount_commitment},
|
|
{use_biased_htp},
|
|
{scan_result.amount_blinding_factor},
|
|
{mock::gen_secret_key()},
|
|
{output_amount_blinding_factor},
|
|
rerandomized_outputs);
|
|
|
|
ASSERT_EQ(1, rerandomized_outputs.size());
|
|
|
|
// make SA/L proof for spending aforementioned enote
|
|
fcmp_pp::FcmpPpSalProof sal_proof;
|
|
crypto::key_image actual_key_image;
|
|
make_sal_proof_any_to_carrot_v1(signable_tx_hash,
|
|
rerandomized_outputs.front(),
|
|
opening_hint,
|
|
keys.k_prove_spend,
|
|
keys.k_generate_image,
|
|
*keys.addr_dev,
|
|
keys.s_view_balance_dev,
|
|
keys.k_view_incoming_dev,
|
|
sal_proof,
|
|
actual_key_image);
|
|
|
|
ASSERT_EQ(expected_key_image, actual_key_image);
|
|
|
|
// verify SA/L
|
|
EXPECT_TRUE(fcmp_pp::verify_sal(signable_tx_hash,
|
|
rerandomized_outputs.front().input,
|
|
actual_key_image,
|
|
sal_proof));
|
|
}
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
TEST(carrot_tx_builder, make_sal_proof_carrot_to_carrot_v1_subaddr_internal)
|
|
{
|
|
mock::mock_carrot_and_legacy_keys keys;
|
|
keys.generate();
|
|
|
|
// j
|
|
const subaddress_index subaddr_index = mock::gen_subaddress_index();
|
|
|
|
// (K^j_s, K^j_v)
|
|
const CarrotDestinationV1 addr = keys.subaddress({subaddr_index});
|
|
|
|
const crypto::key_image tx_first_key_image = mock::gen_key_image();
|
|
|
|
const CarrotPaymentProposalSelfSendV1 selfsend_payment_proposal{
|
|
.destination_address_spend_pubkey = addr.address_spend_pubkey,
|
|
.amount = crypto::rand<rct::xmr_amount>(),
|
|
.enote_type = CarrotEnoteType::CHANGE,
|
|
.enote_ephemeral_privkey = mock::gen_secret_key()
|
|
};
|
|
|
|
RCTOutputEnoteProposal output_enote_proposal;
|
|
get_output_proposal_internal_v1(selfsend_payment_proposal,
|
|
keys.s_view_balance_dev,
|
|
tx_first_key_image,
|
|
nullptr,
|
|
nullptr,
|
|
output_enote_proposal);
|
|
|
|
const CarrotEnoteV1 &enote = output_enote_proposal.enote;
|
|
|
|
// scan enote to get sender extensions and calculate expected key image
|
|
std::vector<mock::mock_scan_result_t> scan_results;
|
|
mock::mock_scan_enote_set({enote},
|
|
gen_encrypted_payment_id(),
|
|
keys,
|
|
scan_results);
|
|
|
|
ASSERT_EQ(1, scan_results.size());
|
|
|
|
const mock::mock_scan_result_t &scan_result = scan_results.front();
|
|
|
|
ASSERT_EQ(addr.address_spend_pubkey, scan_result.address_spend_pubkey);
|
|
|
|
const CarrotOutputOpeningHintV1 opening_hint{
|
|
.source_enote = enote,
|
|
.encrypted_payment_id = std::nullopt,
|
|
.subaddr_index = {subaddr_index, keys.default_derive_type}
|
|
};
|
|
|
|
const bool use_biased_htp = use_biased_hash_to_point(opening_hint);
|
|
|
|
const crypto::key_image expected_key_image = keys.derive_key_image(addr.address_spend_pubkey,
|
|
scan_result.sender_extension_g,
|
|
scan_result.sender_extension_t,
|
|
enote.onetime_address,
|
|
use_biased_htp);
|
|
|
|
// fake output amount blinding factor in a hypothetical tx where we spent the aforementioned output
|
|
const crypto::secret_key output_amount_blinding_factor = mock::gen_secret_key();
|
|
const crypto::hash signable_tx_hash = crypto::rand<crypto::hash>();
|
|
|
|
// make rerandomized outputs
|
|
std::vector<FcmpRerandomizedOutputCompressed> rerandomized_outputs;
|
|
fcmp_pp::make_balanced_rerandomized_output_set({enote.onetime_address},
|
|
{enote.amount_commitment},
|
|
{use_biased_htp},
|
|
{scan_result.amount_blinding_factor},
|
|
{mock::gen_secret_key()},
|
|
{output_amount_blinding_factor},
|
|
rerandomized_outputs);
|
|
|
|
ASSERT_EQ(1, rerandomized_outputs.size());
|
|
|
|
// make SA/L proof for spending aforementioned enote
|
|
fcmp_pp::FcmpPpSalProof sal_proof;
|
|
crypto::key_image actual_key_image;
|
|
make_sal_proof_any_to_carrot_v1(signable_tx_hash,
|
|
rerandomized_outputs.front(),
|
|
opening_hint,
|
|
keys.k_prove_spend,
|
|
keys.k_generate_image,
|
|
*keys.addr_dev,
|
|
keys.s_view_balance_dev,
|
|
keys.k_view_incoming_dev,
|
|
sal_proof,
|
|
actual_key_image);
|
|
|
|
ASSERT_EQ(expected_key_image, actual_key_image);
|
|
|
|
// verify SA/L
|
|
EXPECT_TRUE(fcmp_pp::verify_sal(signable_tx_hash,
|
|
rerandomized_outputs.front().input,
|
|
actual_key_image,
|
|
sal_proof));
|
|
}
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
TEST(carrot_tx_builder, make_sal_proof_carrot_coinbase_to_legacy_v1)
|
|
{
|
|
mock::mock_carrot_and_legacy_keys keys;
|
|
keys.generate(AddressDeriveType::PreCarrot);
|
|
|
|
const cryptonote_hierarchy_address_device cn_addr_dev(
|
|
std::make_shared<cryptonote_view_incoming_key_ram_borrowed_device>(keys.k_view_incoming_dev),
|
|
keys.legacy_acb.get_keys().m_account_address.m_spend_public_key);
|
|
|
|
// (K^0_s, K^0_v)
|
|
const CarrotDestinationV1 addr = keys.cryptonote_address();
|
|
|
|
const CarrotPaymentProposalV1 normal_payment_proposal{
|
|
.destination = addr,
|
|
.amount = crypto::rand<rct::xmr_amount>(),
|
|
.randomness = gen_janus_anchor()
|
|
};
|
|
|
|
const uint64_t block_index = mock::gen_block_index();
|
|
|
|
CarrotCoinbaseEnoteV1 coinbase_enote;
|
|
get_coinbase_enote_v1(normal_payment_proposal,
|
|
block_index,
|
|
coinbase_enote);
|
|
const amount_commitment_t coinbase_enote_amount_commitment = commit_carrot_amount(coinbase_enote.amount, {{1}});
|
|
|
|
// scan enote to get sender extensions and calculate expected key image
|
|
std::vector<mock::mock_scan_result_t> scan_results;
|
|
mock::mock_scan_coinbase_enote_set({coinbase_enote}, keys, scan_results);
|
|
|
|
ASSERT_EQ(1, scan_results.size());
|
|
|
|
const mock::mock_scan_result_t &scan_result = scan_results.front();
|
|
|
|
ASSERT_EQ(addr.address_spend_pubkey, scan_result.address_spend_pubkey);
|
|
|
|
const CarrotCoinbaseOutputOpeningHintV1 opening_hint{
|
|
.source_enote = coinbase_enote,
|
|
.derive_type = keys.default_derive_type
|
|
};
|
|
|
|
const bool use_biased_htp = use_biased_hash_to_point(opening_hint);
|
|
|
|
const crypto::key_image expected_key_image = keys.derive_key_image(addr.address_spend_pubkey,
|
|
scan_result.sender_extension_g,
|
|
scan_result.sender_extension_t,
|
|
coinbase_enote.onetime_address,
|
|
use_biased_htp);
|
|
|
|
// fake output amount blinding factor in a hypothetical tx where we spent the aforementioned output
|
|
const crypto::secret_key output_amount_blinding_factor = mock::gen_secret_key();
|
|
const crypto::hash signable_tx_hash = crypto::rand<crypto::hash>();
|
|
|
|
// make rerandomized outputs
|
|
std::vector<FcmpRerandomizedOutputCompressed> rerandomized_outputs;
|
|
fcmp_pp::make_balanced_rerandomized_output_set({coinbase_enote.onetime_address},
|
|
{coinbase_enote_amount_commitment},
|
|
{use_biased_htp},
|
|
{scan_result.amount_blinding_factor},
|
|
{mock::gen_secret_key()},
|
|
{output_amount_blinding_factor},
|
|
rerandomized_outputs);
|
|
|
|
ASSERT_EQ(1, rerandomized_outputs.size());
|
|
|
|
// make SA/L proof for spending aforementioned enote
|
|
fcmp_pp::FcmpPpSalProof sal_proof;
|
|
crypto::key_image actual_key_image;
|
|
make_sal_proof_any_to_legacy_v1(signable_tx_hash,
|
|
rerandomized_outputs.front(),
|
|
opening_hint,
|
|
keys.legacy_acb.get_keys().m_spend_secret_key,
|
|
cn_addr_dev,
|
|
sal_proof,
|
|
actual_key_image);
|
|
|
|
ASSERT_EQ(expected_key_image, actual_key_image);
|
|
|
|
// verify SA/L
|
|
EXPECT_TRUE(fcmp_pp::verify_sal(signable_tx_hash,
|
|
rerandomized_outputs.front().input,
|
|
actual_key_image,
|
|
sal_proof));
|
|
}
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
TEST(carrot_tx_builder, make_sal_proof_carrot_coinbase_to_carrot_v1)
|
|
{
|
|
mock::mock_carrot_and_legacy_keys keys;
|
|
keys.generate();
|
|
|
|
// (K^0_s, K^0_v)
|
|
const CarrotDestinationV1 addr = keys.cryptonote_address();
|
|
|
|
const CarrotPaymentProposalV1 normal_payment_proposal{
|
|
.destination = addr,
|
|
.amount = crypto::rand<rct::xmr_amount>(),
|
|
.randomness = gen_janus_anchor()
|
|
};
|
|
|
|
const uint64_t block_index = mock::gen_block_index();
|
|
|
|
CarrotCoinbaseEnoteV1 coinbase_enote;
|
|
get_coinbase_enote_v1(normal_payment_proposal,
|
|
block_index,
|
|
coinbase_enote);
|
|
const amount_commitment_t coinbase_enote_amount_commitment = commit_carrot_amount(coinbase_enote.amount, {{1}});
|
|
|
|
// scan enote to get sender extensions and calculate expected key image
|
|
std::vector<mock::mock_scan_result_t> scan_results;
|
|
mock::mock_scan_coinbase_enote_set({coinbase_enote}, keys, scan_results);
|
|
|
|
ASSERT_EQ(1, scan_results.size());
|
|
|
|
const mock::mock_scan_result_t &scan_result = scan_results.front();
|
|
|
|
ASSERT_EQ(addr.address_spend_pubkey, scan_result.address_spend_pubkey);
|
|
|
|
const CarrotCoinbaseOutputOpeningHintV1 opening_hint{
|
|
.source_enote = coinbase_enote,
|
|
.derive_type = keys.default_derive_type
|
|
};
|
|
|
|
const bool use_biased_htp = use_biased_hash_to_point(opening_hint);
|
|
|
|
const crypto::key_image expected_key_image = keys.derive_key_image(addr.address_spend_pubkey,
|
|
scan_result.sender_extension_g,
|
|
scan_result.sender_extension_t,
|
|
coinbase_enote.onetime_address,
|
|
use_biased_htp);
|
|
|
|
// fake output amount blinding factor in a hypothetical tx where we spent the aforementioned output
|
|
const crypto::secret_key output_amount_blinding_factor = mock::gen_secret_key();
|
|
const crypto::hash signable_tx_hash = crypto::rand<crypto::hash>();
|
|
|
|
// make rerandomized outputs
|
|
std::vector<FcmpRerandomizedOutputCompressed> rerandomized_outputs;
|
|
fcmp_pp::make_balanced_rerandomized_output_set({coinbase_enote.onetime_address},
|
|
{coinbase_enote_amount_commitment},
|
|
{use_biased_htp},
|
|
{scan_result.amount_blinding_factor},
|
|
{mock::gen_secret_key()},
|
|
{output_amount_blinding_factor},
|
|
rerandomized_outputs);
|
|
|
|
ASSERT_EQ(1, rerandomized_outputs.size());
|
|
|
|
// make SA/L proof for spending aforementioned enote
|
|
fcmp_pp::FcmpPpSalProof sal_proof;
|
|
crypto::key_image actual_key_image;
|
|
make_sal_proof_any_to_carrot_v1(signable_tx_hash,
|
|
rerandomized_outputs.front(),
|
|
opening_hint,
|
|
keys.k_prove_spend,
|
|
keys.k_generate_image,
|
|
*keys.addr_dev,
|
|
keys.s_view_balance_dev,
|
|
keys.k_view_incoming_dev,
|
|
sal_proof,
|
|
actual_key_image);
|
|
|
|
ASSERT_EQ(expected_key_image, actual_key_image);
|
|
|
|
// verify SA/L
|
|
EXPECT_TRUE(fcmp_pp::verify_sal(signable_tx_hash,
|
|
rerandomized_outputs.front().input,
|
|
actual_key_image,
|
|
sal_proof));
|
|
}
|
|
//----------------------------------------------------------------------------------------------------------------------
|