mirror of
https://github.com/monero-project/monero.git
synced 2024-12-04 23:51:08 +02:00
Compare commits
6 Commits
d8d07334ce
...
d02c1040ed
Author | SHA1 | Date | |
---|---|---|---|
|
d02c1040ed | ||
|
36cb2e5846 | ||
|
995484b1e0 | ||
|
2a638842a4 | ||
|
965a5d88c9 | ||
|
1604edf301 |
@ -35,6 +35,7 @@ set(carrot_core_sources
|
||||
device_ram_borrowed.cpp
|
||||
enote_utils.cpp
|
||||
hash_functions.cpp
|
||||
output_set_finalization.cpp
|
||||
payment_proposal.cpp)
|
||||
|
||||
monero_find_all_headers(carrot_core_headers, "${CMAKE_CURRENT_SOURCE_DIR}")
|
||||
|
@ -66,4 +66,7 @@ static constexpr const unsigned char CARROT_DOMAIN_SEP_GENERATE_ADDRESS_SECRET[]
|
||||
static constexpr const unsigned char CARROT_DOMAIN_SEP_ADDRESS_INDEX_GEN[] = "Carrot address index generator";
|
||||
static constexpr const unsigned char CARROT_DOMAIN_SEP_SUBADDRESS_SCALAR[] = "Carrot subaddress scalar";
|
||||
|
||||
// Carrot misc constants
|
||||
static constexpr const unsigned int CARROT_MIN_TX_OUTPUTS = 2;
|
||||
static constexpr const unsigned int CARROT_MAX_TX_OUTPUTS = 16;
|
||||
} //namespace carrot
|
||||
|
@ -125,16 +125,29 @@ bool make_carrot_uncontextualized_shared_key_receiver(const crypto::secret_key &
|
||||
// @TODO: this is slower than a turtle on morphine, and will cripple scan speed, but should be correct
|
||||
|
||||
// K_e = ConvertPointM(D_e)
|
||||
ge_p3 D_e_in_ed25519_p3;
|
||||
if (ge_fromx25519_vartime(&D_e_in_ed25519_p3, enote_ephemeral_pubkey.data) != 0)
|
||||
ge_p3 p3_tmp;
|
||||
if (ge_fromx25519_vartime(&p3_tmp, enote_ephemeral_pubkey.data) != 0)
|
||||
return false;
|
||||
|
||||
// serialize K_e
|
||||
crypto::public_key K_e;
|
||||
ge_p3_tobytes(to_bytes(K_e), &p3_tmp);
|
||||
|
||||
// [ed25519] s_sr = 8 d_e K^j_v
|
||||
crypto::key_derivation s_sr_in_ed25519;
|
||||
if (!crypto::wallet::generate_key_derivation(K_e, k_view, s_sr_in_ed25519))
|
||||
return false;
|
||||
else if (memcmp(&s_sr_in_ed25519, &rct::I, sizeof(rct::key)) == 0)
|
||||
return false;
|
||||
|
||||
// serialize K_e
|
||||
crypto::public_key D_e_in_ed25519;
|
||||
ge_p3_tobytes(to_bytes(D_e_in_ed25519), &D_e_in_ed25519_p3);
|
||||
// deserialize s_sr
|
||||
ge_p3 s_sr_in_ed25519_p3;
|
||||
ge_frombytes_vartime(&s_sr_in_ed25519_p3, to_bytes(s_sr_in_ed25519));
|
||||
|
||||
// do ECDH exchange 8 k_v D_e
|
||||
return make_carrot_uncontextualized_shared_key_sender(k_view, D_e_in_ed25519, s_sender_receiver_unctx_out);
|
||||
// ConvertPointE(s_sr)
|
||||
ge_p3_to_x25519(s_sender_receiver_unctx_out.data, &s_sr_in_ed25519_p3);
|
||||
|
||||
return true;
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
bool make_carrot_uncontextualized_shared_key_sender(const crypto::secret_key &enote_ephemeral_privkey,
|
||||
|
244
src/carrot_core/output_set_finalization.cpp
Normal file
244
src/carrot_core/output_set_finalization.cpp
Normal file
@ -0,0 +1,244 @@
|
||||
// Copyright (c) 2024, 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.
|
||||
|
||||
//paired header
|
||||
#include "output_set_finalization.h"
|
||||
|
||||
//local headers
|
||||
#include "common/container_helpers.h"
|
||||
#include "enote_utils.h"
|
||||
#include "misc_log_ex.h"
|
||||
|
||||
//third party headers
|
||||
|
||||
//standard headers
|
||||
|
||||
#undef MONERO_DEFAULT_LOG_CATEGORY
|
||||
#define MONERO_DEFAULT_LOG_CATEGORY "carrot"
|
||||
|
||||
namespace carrot
|
||||
{
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
std::optional<AdditionalOutputType> get_additional_output_type(const size_t num_outgoing,
|
||||
const size_t num_selfsend,
|
||||
const bool remaining_change,
|
||||
const bool have_payment_type_selfsend)
|
||||
{
|
||||
const size_t num_outputs = num_outgoing + num_selfsend;
|
||||
const bool already_completed = num_outputs >= 2 && num_selfsend >= 1 && !remaining_change;
|
||||
if (num_outputs == 0)
|
||||
{
|
||||
ASSERT_MES_AND_THROW("get additional output type: set contains 0 outputs");
|
||||
}
|
||||
else if (already_completed)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
else if (num_outputs == 1)
|
||||
{
|
||||
if (num_selfsend == 0)
|
||||
{
|
||||
return AdditionalOutputType::CHANGE_SHARED;
|
||||
}
|
||||
else if (!remaining_change)
|
||||
{
|
||||
return AdditionalOutputType::DUMMY;
|
||||
}
|
||||
else // num_selfsend == 1 && remaining_change
|
||||
{
|
||||
if (have_payment_type_selfsend)
|
||||
{
|
||||
return AdditionalOutputType::CHANGE_SHARED;
|
||||
}
|
||||
else
|
||||
{
|
||||
return AdditionalOutputType::PAYMENT_SHARED;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (num_outputs < CARROT_MAX_TX_OUTPUTS)
|
||||
{
|
||||
return AdditionalOutputType::CHANGE_UNIQUE;
|
||||
}
|
||||
else // num_outputs >= CARROT_MAX_TX_OUTPUTS
|
||||
{
|
||||
ASSERT_MES_AND_THROW("get additional output type: "
|
||||
"set needs finalization but already contains too many outputs");
|
||||
}
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
tools::optional_variant<CarrotPaymentProposalV1, CarrotPaymentProposalSelfSendV1> get_additional_output_proposal(
|
||||
const size_t num_outgoing,
|
||||
const size_t num_selfsend,
|
||||
const rct::xmr_amount remaining_change,
|
||||
const bool have_payment_type_selfsend,
|
||||
const crypto::public_key &change_address_spend_pubkey,
|
||||
const crypto::x25519_pubkey &other_enote_ephemeral_pubkey)
|
||||
{
|
||||
const std::optional<AdditionalOutputType> additional_output_type = get_additional_output_type(
|
||||
num_outgoing,
|
||||
num_selfsend,
|
||||
remaining_change,
|
||||
have_payment_type_selfsend
|
||||
);
|
||||
|
||||
if (!additional_output_type)
|
||||
return {};
|
||||
|
||||
switch (*additional_output_type)
|
||||
{
|
||||
case AdditionalOutputType::PAYMENT_SHARED:
|
||||
return CarrotPaymentProposalSelfSendV1{
|
||||
.destination_address_spend_pubkey = change_address_spend_pubkey,
|
||||
.amount = remaining_change,
|
||||
.enote_type = CarrotEnoteType::PAYMENT,
|
||||
.enote_ephemeral_pubkey = other_enote_ephemeral_pubkey
|
||||
};
|
||||
case AdditionalOutputType::CHANGE_SHARED:
|
||||
return CarrotPaymentProposalSelfSendV1{
|
||||
.destination_address_spend_pubkey = change_address_spend_pubkey,
|
||||
.amount = remaining_change,
|
||||
.enote_type = CarrotEnoteType::CHANGE,
|
||||
.enote_ephemeral_pubkey = other_enote_ephemeral_pubkey
|
||||
};
|
||||
case AdditionalOutputType::CHANGE_UNIQUE:
|
||||
return CarrotPaymentProposalSelfSendV1{
|
||||
.destination_address_spend_pubkey = change_address_spend_pubkey,
|
||||
.amount = remaining_change,
|
||||
.enote_type = CarrotEnoteType::CHANGE,
|
||||
.enote_ephemeral_pubkey = crypto::x25519_pubkey_gen()
|
||||
};
|
||||
case AdditionalOutputType::DUMMY:
|
||||
return CarrotPaymentProposalV1{
|
||||
.destination = gen_carrot_main_address_v1(),
|
||||
.amount = 0,
|
||||
.randomness = gen_janus_anchor()
|
||||
};
|
||||
}
|
||||
|
||||
ASSERT_MES_AND_THROW("get additional output proposal: unrecognized additional output type");
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
void get_output_enote_proposals(std::vector<CarrotPaymentProposalV1> &&normal_payment_proposals,
|
||||
std::vector<CarrotPaymentProposalSelfSendV1> &&selfsend_payment_proposals,
|
||||
const view_balance_secret_device &s_view_balance_dev,
|
||||
const crypto::key_image &tx_first_key_image,
|
||||
std::vector<RCTOutputEnoteProposal> &output_enote_proposals_out,
|
||||
encrypted_payment_id_t &encrypted_payment_id_out)
|
||||
{
|
||||
output_enote_proposals_out.clear();
|
||||
encrypted_payment_id_out = null_payment_id;
|
||||
|
||||
// assert payment proposals numbers
|
||||
const size_t num_proposals = normal_payment_proposals.size() + selfsend_payment_proposals.size();
|
||||
CHECK_AND_ASSERT_THROW_MES(num_proposals >= CARROT_MIN_TX_OUTPUTS,
|
||||
"get output enote proposals: too few payment proposals");
|
||||
CHECK_AND_ASSERT_THROW_MES(num_proposals <= CARROT_MAX_TX_OUTPUTS,
|
||||
"get output enote proposals: too many payment proposals");
|
||||
CHECK_AND_ASSERT_THROW_MES(selfsend_payment_proposals.size(),
|
||||
"get output enote proposals: no selfsend payment proposal");
|
||||
|
||||
// assert there is a max of 1 integrated address payment proposals
|
||||
size_t num_integrated = 0;
|
||||
for (const CarrotPaymentProposalV1 &normal_payment_proposal : normal_payment_proposals)
|
||||
if (normal_payment_proposal.destination.payment_id != null_payment_id)
|
||||
++num_integrated;
|
||||
CHECK_AND_ASSERT_THROW_MES(num_integrated <= 1,
|
||||
"get output enote proposals: only one integrated address is allowed per tx output set");
|
||||
|
||||
// assert anchor_norm != 0 for payments
|
||||
for (const CarrotPaymentProposalV1 &normal_payment_proposal : normal_payment_proposals)
|
||||
CHECK_AND_ASSERT_THROW_MES(normal_payment_proposal.randomness != janus_anchor_t{},
|
||||
"get output enote proposals: normal payment proposal has unset anchor_norm AKA randomness");
|
||||
|
||||
// sort normal payment proposals by anchor_norm and assert uniqueness of randomness for each payment
|
||||
const auto sort_by_randomness = [](const CarrotPaymentProposalV1 &a, const CarrotPaymentProposalV1 &b) -> bool
|
||||
{
|
||||
return memcmp(&a.randomness, &b.randomness, JANUS_ANCHOR_BYTES) < 0;
|
||||
};
|
||||
std::sort(normal_payment_proposals.begin(), normal_payment_proposals.end(), sort_by_randomness);
|
||||
const bool has_unique_randomness = tools::is_sorted_and_unique(normal_payment_proposals,
|
||||
sort_by_randomness);
|
||||
CHECK_AND_ASSERT_THROW_MES(has_unique_randomness,
|
||||
"get output enote proposals: normal payment proposals contain duplicate anchor_norm AKA randomness");
|
||||
|
||||
// input_context = "R" || KI_1
|
||||
input_context_t input_context;
|
||||
make_carrot_input_context(tx_first_key_image, input_context);
|
||||
|
||||
// construct normal enotes
|
||||
output_enote_proposals_out.reserve(num_proposals);
|
||||
for (size_t i = 0; i < normal_payment_proposals.size(); ++i)
|
||||
{
|
||||
encrypted_payment_id_t encrypted_payment_id;
|
||||
get_output_proposal_normal_v1(normal_payment_proposals[i],
|
||||
tx_first_key_image,
|
||||
tools::add_element(output_enote_proposals_out),
|
||||
encrypted_payment_id);
|
||||
|
||||
// set pid to the first payment proposal or only integrated proposal
|
||||
const bool is_first = i == 0;
|
||||
const bool is_integrated = normal_payment_proposals[i].destination.payment_id != null_payment_id;
|
||||
if (is_first || is_integrated)
|
||||
encrypted_payment_id_out = encrypted_payment_id;
|
||||
}
|
||||
|
||||
// in the case that the pid is ambiguous, set it to random bytes
|
||||
const bool ambiguous_pid_destination = num_integrated == 0 && normal_payment_proposals.size() > 1;
|
||||
if (ambiguous_pid_destination)
|
||||
encrypted_payment_id_out = gen_payment_id();
|
||||
|
||||
// construct selfsend enotes
|
||||
for (const CarrotPaymentProposalSelfSendV1 &selfsend_payment_proposal : selfsend_payment_proposals)
|
||||
{
|
||||
get_output_proposal_internal_v1(selfsend_payment_proposal,
|
||||
s_view_balance_dev,
|
||||
tx_first_key_image,
|
||||
tools::add_element(output_enote_proposals_out));
|
||||
}
|
||||
|
||||
// sort enotes by D_e and assert uniqueness properties of D_e
|
||||
const auto sort_by_ephemeral_pubkey = [](const RCTOutputEnoteProposal &a, const RCTOutputEnoteProposal &b) -> bool
|
||||
{
|
||||
return memcmp(&a.enote.enote_ephemeral_pubkey,
|
||||
&b.enote.enote_ephemeral_pubkey,
|
||||
sizeof(crypto::x25519_pubkey)) < 0;
|
||||
};
|
||||
std::sort(output_enote_proposals_out.begin(), output_enote_proposals_out.end(), sort_by_ephemeral_pubkey);
|
||||
const bool has_unique_ephemeral_pubkeys = tools::is_sorted_and_unique(output_enote_proposals_out,
|
||||
sort_by_ephemeral_pubkey);
|
||||
CHECK_AND_ASSERT_THROW_MES(!(num_proposals == 2 && has_unique_ephemeral_pubkeys),
|
||||
"get output enote proposals: a 2-out set needs to share an ephemeral pubkey, but this 2-out set doesn't");
|
||||
CHECK_AND_ASSERT_THROW_MES(!(num_proposals != 2 && !has_unique_ephemeral_pubkeys),
|
||||
"get output enote proposals: this >2-out set contains duplicate enote ephemeral pubkeys");
|
||||
|
||||
// sort enotes by Ko
|
||||
std::sort(output_enote_proposals_out.begin(), output_enote_proposals_out.end());
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
} //namespace carrot
|
99
src/carrot_core/output_set_finalization.h
Normal file
99
src/carrot_core/output_set_finalization.h
Normal file
@ -0,0 +1,99 @@
|
||||
// Copyright (c) 2024, 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.
|
||||
|
||||
//! @file Utilities for constructing output proposal sets that adhere to Carrot rules
|
||||
|
||||
#pragma once
|
||||
|
||||
//local headers
|
||||
#include "carrot_enote_types.h"
|
||||
#include "common/variant.h"
|
||||
#include "config.h"
|
||||
#include "payment_proposal.h"
|
||||
#include "ringct/rctTypes.h"
|
||||
|
||||
//third party headers
|
||||
|
||||
//standard headers
|
||||
#include <optional>
|
||||
|
||||
//forward declarations
|
||||
|
||||
|
||||
namespace carrot
|
||||
{
|
||||
enum class AdditionalOutputType
|
||||
{
|
||||
PAYMENT_SHARED, // selfsend proposal with enote_type="payment" with a shared D_e
|
||||
CHANGE_SHARED, // selfsend proposal with enote_type="change" with a shared D_e
|
||||
CHANGE_UNIQUE, // selfsend proposal with enote_type="change" with a unique D_e
|
||||
DUMMY // outgoing proposal to a random address
|
||||
};
|
||||
|
||||
/**
|
||||
* brief: get_additional_output_type - get the type of the additional enote needed to finalize an output set
|
||||
* param: num_outgoing - number of outgoing transfers
|
||||
* param: num_selfsend - number of selfsend transfers
|
||||
* param: remaining_change - whether there is any leftover change needed to be included
|
||||
* param: have_payment_type_selfsend - true if the enote set has a selfsend enote with enote_type="payment"
|
||||
* return: AdditionalOutputType if need an additional enote, else std::nullopt
|
||||
* throw: std::runtime_error if the output set is in a state where it cannot be finalized
|
||||
*/
|
||||
std::optional<AdditionalOutputType> get_additional_output_type(const size_t num_outgoing,
|
||||
const size_t num_selfsend,
|
||||
const bool remaining_change,
|
||||
const bool have_payment_type_selfsend);
|
||||
/**
|
||||
* brief: get_additional_output_proposal - get an additional output proposal to complete an output set
|
||||
* param: num_outgoing - number of outgoing transfers
|
||||
* param: num_selfsend - number of selfsend transfers
|
||||
* param: remaining_change - the amount of leftover change needed to be included
|
||||
* param: have_payment_type_selfsend - true if the enote set has a selfsend enote with enote_type="payment"
|
||||
* param: change_address_spend_pubkey - K^j_s of our change address
|
||||
* param: other_enote_ephemeral_pubkey - D^other_e
|
||||
* return: an output proposal if need an additional enote, else none
|
||||
* throw: std::runtime_error if the output set is in a state where it cannot be finalized
|
||||
*/
|
||||
tools::optional_variant<CarrotPaymentProposalV1, CarrotPaymentProposalSelfSendV1> get_additional_output_proposal(
|
||||
const size_t num_outgoing,
|
||||
const size_t num_selfsend,
|
||||
const rct::xmr_amount remaining_change,
|
||||
const bool have_payment_type_selfsend,
|
||||
const crypto::public_key &change_address_spend_pubkey,
|
||||
const crypto::x25519_pubkey &other_enote_ephemeral_pubkey);
|
||||
/**
|
||||
* brief: get_output_enote_proposals - convert payment proposals into output enote proposals
|
||||
*/
|
||||
void get_output_enote_proposals(std::vector<CarrotPaymentProposalV1> &&normal_payment_proposals,
|
||||
std::vector<CarrotPaymentProposalSelfSendV1> &&selfsend_payment_proposals,
|
||||
const view_balance_secret_device &s_view_balance_dev,
|
||||
const crypto::key_image &tx_first_key_image,
|
||||
std::vector<RCTOutputEnoteProposal> &output_enote_proposals_out,
|
||||
encrypted_payment_id_t &encrypted_payment_id_out);
|
||||
|
||||
} //namespace carrot
|
@ -84,7 +84,7 @@ static void get_normal_proposal_ecdh_parts(const CarrotPaymentProposalV1 &propos
|
||||
const crypto::secret_key enote_ephemeral_privkey = get_enote_ephemeral_privkey(proposal, input_context);
|
||||
|
||||
// 2. make D_e
|
||||
get_enote_ephemeral_pubkey(proposal, input_context, enote_ephemeral_pubkey_out);
|
||||
enote_ephemeral_pubkey_out = get_enote_ephemeral_pubkey(proposal, input_context);
|
||||
|
||||
// 3. s_sr = 8 d_e ConvertPointE(K^j_v)
|
||||
make_carrot_uncontextualized_shared_key_sender(enote_ephemeral_privkey,
|
||||
@ -93,7 +93,48 @@ static void get_normal_proposal_ecdh_parts(const CarrotPaymentProposalV1 &propos
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
static void get_output_proposal_parts(const unsigned char s_sender_receiver_unctx[32],
|
||||
static void get_output_proposal_parts(const crypto::hash &s_sender_receiver,
|
||||
const crypto::public_key &destination_spend_pubkey,
|
||||
const payment_id_t payment_id,
|
||||
const rct::xmr_amount amount,
|
||||
const CarrotEnoteType enote_type,
|
||||
const crypto::x25519_pubkey &enote_ephemeral_pubkey,
|
||||
const input_context_t &input_context,
|
||||
const bool coinbase_amount_commitment,
|
||||
crypto::secret_key &amount_blinding_factor_out,
|
||||
rct::key &amount_commitment_out,
|
||||
crypto::public_key &onetime_address_out,
|
||||
encrypted_amount_t &encrypted_amount_out,
|
||||
encrypted_payment_id_t &encrypted_payment_id_out)
|
||||
{
|
||||
// 1. k_a = H_n(s^ctx_sr, enote_type) if !coinbase, else 1
|
||||
if (coinbase_amount_commitment)
|
||||
amount_blinding_factor_out = rct::rct2sk(rct::I);
|
||||
else
|
||||
make_carrot_amount_blinding_factor(s_sender_receiver,
|
||||
enote_type,
|
||||
amount_blinding_factor_out);
|
||||
|
||||
// 2. C_a = k_a G + a H
|
||||
amount_commitment_out = rct::commit(amount, rct::sk2rct(amount_blinding_factor_out));
|
||||
|
||||
// 3. Ko = K^j_s + K^o_ext = K^j_s + (k^o_g G + k^o_t T)
|
||||
make_carrot_onetime_address(destination_spend_pubkey,
|
||||
s_sender_receiver,
|
||||
amount_commitment_out,
|
||||
onetime_address_out);
|
||||
|
||||
// 4. a_enc = a XOR m_a
|
||||
encrypted_amount_out = encrypt_carrot_amount(amount,
|
||||
s_sender_receiver,
|
||||
onetime_address_out);
|
||||
|
||||
// 5. pid_enc = pid XOR m_pid
|
||||
encrypted_payment_id_out = encrypt_legacy_payment_id(payment_id, s_sender_receiver, onetime_address_out);
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
static void get_external_output_proposal_parts(const crypto::x25519_pubkey &s_sender_receiver_unctx,
|
||||
const crypto::public_key &destination_spend_pubkey,
|
||||
const payment_id_t payment_id,
|
||||
const rct::xmr_amount amount,
|
||||
@ -110,38 +151,28 @@ static void get_output_proposal_parts(const unsigned char s_sender_receiver_unct
|
||||
view_tag_t &view_tag_out)
|
||||
{
|
||||
// 1. s^ctx_sr = H_32(s_sr, D_e, input_context)
|
||||
make_carrot_sender_receiver_secret(s_sender_receiver_unctx,
|
||||
make_carrot_sender_receiver_secret(s_sender_receiver_unctx.data,
|
||||
enote_ephemeral_pubkey,
|
||||
input_context,
|
||||
s_sender_receiver_out);
|
||||
|
||||
// 2. k_a = H_n(s^ctx_sr, enote_type) if !coinbase, else 1
|
||||
if (coinbase_amount_commitment)
|
||||
amount_blinding_factor_out = rct::rct2sk(rct::I);
|
||||
else
|
||||
make_carrot_amount_blinding_factor(s_sender_receiver_out,
|
||||
enote_type,
|
||||
amount_blinding_factor_out);
|
||||
|
||||
// 3. C_a = k_a G + a H
|
||||
amount_commitment_out = rct::commit(amount, rct::sk2rct(amount_blinding_factor_out));
|
||||
|
||||
// 4. Ko = K^j_s + K^o_ext = K^j_s + (k^o_g G + k^o_t T)
|
||||
make_carrot_onetime_address(destination_spend_pubkey,
|
||||
s_sender_receiver_out,
|
||||
// 2. get other parts: k_a, C_a, Ko, a_enc, pid_enc
|
||||
get_output_proposal_parts(s_sender_receiver_out,
|
||||
destination_spend_pubkey,
|
||||
payment_id,
|
||||
amount,
|
||||
enote_type,
|
||||
enote_ephemeral_pubkey,
|
||||
input_context,
|
||||
coinbase_amount_commitment,
|
||||
amount_blinding_factor_out,
|
||||
amount_commitment_out,
|
||||
onetime_address_out);
|
||||
|
||||
// 5. a_enc = a XOR m_a
|
||||
encrypted_amount_out = encrypt_carrot_amount(amount,
|
||||
s_sender_receiver_out,
|
||||
onetime_address_out);
|
||||
|
||||
// 6. pid_enc = pid XOR m_pid
|
||||
encrypted_payment_id_out = encrypt_legacy_payment_id(payment_id, s_sender_receiver_out, onetime_address_out);
|
||||
onetime_address_out,
|
||||
encrypted_amount_out,
|
||||
encrypted_payment_id_out);
|
||||
|
||||
// 7. view tag: vt = H_3(s_sr || input_context || Ko)
|
||||
make_carrot_view_tag(s_sender_receiver_unctx, input_context, onetime_address_out, view_tag_out);
|
||||
// 3. vt = H_3(s_sr || input_context || Ko)
|
||||
make_carrot_view_tag(s_sender_receiver_unctx.data, input_context, onetime_address_out, view_tag_out);
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
@ -160,22 +191,29 @@ bool operator==(const CarrotPaymentProposalSelfSendV1 &a, const CarrotPaymentPro
|
||||
a.enote_ephemeral_pubkey == b.enote_ephemeral_pubkey;
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
void get_enote_ephemeral_pubkey(const CarrotPaymentProposalV1 &proposal,
|
||||
const input_context_t &input_context,
|
||||
crypto::x25519_pubkey &enote_ephemeral_pubkey_out)
|
||||
bool operator<(const RCTOutputEnoteProposal &a, const RCTOutputEnoteProposal &b)
|
||||
{
|
||||
return memcmp(&a.enote.onetime_address, &b.enote.onetime_address, sizeof(crypto::public_key)) < 0;
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
crypto::x25519_pubkey get_enote_ephemeral_pubkey(const CarrotPaymentProposalV1 &proposal,
|
||||
const input_context_t &input_context)
|
||||
{
|
||||
// d_e = H_n(anchor_norm, input_context, K^j_s, K^j_v, pid))
|
||||
const crypto::secret_key enote_ephemeral_privkey{get_enote_ephemeral_privkey(proposal, input_context)};
|
||||
|
||||
crypto::x25519_pubkey enote_ephemeral_pubkey;
|
||||
if (proposal.destination.is_subaddress)
|
||||
// D_e = d_e ConvertPointE(K^j_s)
|
||||
make_carrot_enote_ephemeral_pubkey_subaddress(enote_ephemeral_privkey,
|
||||
proposal.destination.address_spend_pubkey,
|
||||
enote_ephemeral_pubkey_out);
|
||||
enote_ephemeral_pubkey);
|
||||
else
|
||||
// D_e = d_e B
|
||||
make_carrot_enote_ephemeral_pubkey_cryptonote(enote_ephemeral_privkey,
|
||||
enote_ephemeral_pubkey_out);
|
||||
enote_ephemeral_pubkey);
|
||||
|
||||
return enote_ephemeral_pubkey;
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
void get_coinbase_output_proposal_v1(const CarrotPaymentProposalV1 &proposal,
|
||||
@ -207,14 +245,14 @@ void get_coinbase_output_proposal_v1(const CarrotPaymentProposalV1 &proposal,
|
||||
rct::key dummy_amount_commitment;
|
||||
encrypted_amount_t dummy_encrypted_amount;
|
||||
encrypted_payment_id_t dummy_encrypted_payment_id;
|
||||
get_output_proposal_parts(s_sender_receiver_unctx.data,
|
||||
get_external_output_proposal_parts(s_sender_receiver_unctx,
|
||||
proposal.destination.address_spend_pubkey,
|
||||
null_payment_id,
|
||||
proposal.amount,
|
||||
CarrotEnoteType::PAYMENT,
|
||||
output_enote_out.enote_ephemeral_pubkey,
|
||||
input_context,
|
||||
true,
|
||||
true, // coinbase_amount_commitment
|
||||
s_sender_receiver,
|
||||
dummy_amount_blinding_factor,
|
||||
dummy_amount_commitment,
|
||||
@ -235,10 +273,8 @@ void get_coinbase_output_proposal_v1(const CarrotPaymentProposalV1 &proposal,
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
void get_output_proposal_normal_v1(const CarrotPaymentProposalV1 &proposal,
|
||||
const crypto::key_image &tx_first_key_image,
|
||||
CarrotEnoteV1 &output_enote_out,
|
||||
encrypted_payment_id_t &encrypted_payment_id_out,
|
||||
rct::xmr_amount &amount_out,
|
||||
crypto::secret_key &amount_blinding_factor_out)
|
||||
RCTOutputEnoteProposal &output_enote_out,
|
||||
encrypted_payment_id_t &encrypted_payment_id_out)
|
||||
{
|
||||
// 1. sanity checks
|
||||
CHECK_AND_ASSERT_THROW_MES(proposal.randomness != null_anchor,
|
||||
@ -252,44 +288,42 @@ void get_output_proposal_normal_v1(const CarrotPaymentProposalV1 &proposal,
|
||||
crypto::x25519_pubkey s_sender_receiver_unctx; auto dhe_wiper = auto_wiper(s_sender_receiver_unctx);
|
||||
get_normal_proposal_ecdh_parts(proposal,
|
||||
input_context,
|
||||
output_enote_out.enote_ephemeral_pubkey,
|
||||
output_enote_out.enote.enote_ephemeral_pubkey,
|
||||
s_sender_receiver_unctx);
|
||||
|
||||
// 4. build the output enote address pieces
|
||||
crypto::hash s_sender_receiver; auto q_wiper = auto_wiper(s_sender_receiver);
|
||||
get_output_proposal_parts(s_sender_receiver_unctx.data,
|
||||
get_external_output_proposal_parts(s_sender_receiver_unctx,
|
||||
proposal.destination.address_spend_pubkey,
|
||||
proposal.destination.payment_id,
|
||||
proposal.amount,
|
||||
CarrotEnoteType::PAYMENT,
|
||||
output_enote_out.enote_ephemeral_pubkey,
|
||||
output_enote_out.enote.enote_ephemeral_pubkey,
|
||||
input_context,
|
||||
false,
|
||||
false, // coinbase_amount_commitment
|
||||
s_sender_receiver,
|
||||
amount_blinding_factor_out,
|
||||
output_enote_out.amount_commitment,
|
||||
output_enote_out.onetime_address,
|
||||
output_enote_out.amount_enc,
|
||||
output_enote_out.amount_blinding_factor,
|
||||
output_enote_out.enote.amount_commitment,
|
||||
output_enote_out.enote.onetime_address,
|
||||
output_enote_out.enote.amount_enc,
|
||||
encrypted_payment_id_out,
|
||||
output_enote_out.view_tag);
|
||||
output_enote_out.enote.view_tag);
|
||||
|
||||
// 5. anchor_enc = anchor XOR m_anchor
|
||||
output_enote_out.anchor_enc = encrypt_carrot_anchor(proposal.randomness,
|
||||
output_enote_out.enote.anchor_enc = encrypt_carrot_anchor(proposal.randomness,
|
||||
s_sender_receiver,
|
||||
output_enote_out.onetime_address);
|
||||
output_enote_out.enote.onetime_address);
|
||||
|
||||
// 6. save the amount and first key image
|
||||
amount_out = proposal.amount;
|
||||
output_enote_out.tx_first_key_image = tx_first_key_image;
|
||||
output_enote_out.amount = proposal.amount;
|
||||
output_enote_out.enote.tx_first_key_image = tx_first_key_image;
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
void get_output_proposal_special_v1(const CarrotPaymentProposalSelfSendV1 &proposal,
|
||||
const crypto::secret_key &k_view,
|
||||
const view_incoming_key_device &k_view_dev,
|
||||
const crypto::public_key &primary_address_spend_pubkey,
|
||||
const crypto::key_image &tx_first_key_image,
|
||||
CarrotEnoteV1 &output_enote_out,
|
||||
rct::xmr_amount &amount_out,
|
||||
crypto::secret_key &amount_blinding_factor_out)
|
||||
RCTOutputEnoteProposal &output_enote_out)
|
||||
{
|
||||
// 1. sanity checks
|
||||
// @TODO
|
||||
@ -300,89 +334,94 @@ void get_output_proposal_special_v1(const CarrotPaymentProposalSelfSendV1 &propo
|
||||
|
||||
// 3. s_sr = 8 * k_v * D_e
|
||||
crypto::x25519_pubkey s_sender_receiver_unctx;
|
||||
make_carrot_uncontextualized_shared_key_receiver(k_view,
|
||||
proposal.enote_ephemeral_pubkey,
|
||||
s_sender_receiver_unctx);
|
||||
CHECK_AND_ASSERT_THROW_MES(k_view_dev.view_key_8_scalar_mult_x25519(proposal.enote_ephemeral_pubkey,
|
||||
s_sender_receiver_unctx),
|
||||
"get output proposal special v1: HW device failed to perform ECDH with ephemeral pubkey");
|
||||
|
||||
// 4. build the output enote address pieces
|
||||
crypto::hash s_sender_receiver; auto q_wiper = auto_wiper(s_sender_receiver);
|
||||
encrypted_payment_id_t dummy_encrypted_payment_id;
|
||||
get_output_proposal_parts(s_sender_receiver_unctx.data,
|
||||
get_external_output_proposal_parts(s_sender_receiver_unctx,
|
||||
proposal.destination_address_spend_pubkey,
|
||||
null_payment_id,
|
||||
proposal.amount,
|
||||
proposal.enote_type,
|
||||
proposal.enote_ephemeral_pubkey,
|
||||
input_context,
|
||||
false,
|
||||
false, // coinbase_amount_commitment
|
||||
s_sender_receiver,
|
||||
amount_blinding_factor_out,
|
||||
output_enote_out.amount_commitment,
|
||||
output_enote_out.onetime_address,
|
||||
output_enote_out.amount_enc,
|
||||
output_enote_out.amount_blinding_factor,
|
||||
output_enote_out.enote.amount_commitment,
|
||||
output_enote_out.enote.onetime_address,
|
||||
output_enote_out.enote.amount_enc,
|
||||
dummy_encrypted_payment_id,
|
||||
output_enote_out.view_tag);
|
||||
output_enote_out.enote.view_tag);
|
||||
|
||||
// 5. make special janus anchor: anchor_sp = H_16(D_e, input_context, Ko, k_v, K_s)
|
||||
janus_anchor_t janus_anchor_special;
|
||||
make_carrot_janus_anchor_special(proposal.enote_ephemeral_pubkey,
|
||||
k_view_dev.make_janus_anchor_special(proposal.enote_ephemeral_pubkey,
|
||||
input_context,
|
||||
output_enote_out.onetime_address,
|
||||
k_view,
|
||||
output_enote_out.enote.onetime_address,
|
||||
primary_address_spend_pubkey,
|
||||
janus_anchor_special);
|
||||
|
||||
// 6. encrypt special anchor: anchor_enc = anchor XOR m_anchor
|
||||
output_enote_out.anchor_enc = encrypt_carrot_anchor(janus_anchor_special,
|
||||
output_enote_out.enote.anchor_enc = encrypt_carrot_anchor(janus_anchor_special,
|
||||
s_sender_receiver,
|
||||
output_enote_out.onetime_address);
|
||||
output_enote_out.enote.onetime_address);
|
||||
|
||||
// 7. save the enote ephemeral pubkey, first tx key image, and amount
|
||||
output_enote_out.enote_ephemeral_pubkey = proposal.enote_ephemeral_pubkey;
|
||||
output_enote_out.tx_first_key_image = tx_first_key_image;
|
||||
amount_out = proposal.amount;
|
||||
output_enote_out.enote.enote_ephemeral_pubkey = proposal.enote_ephemeral_pubkey;
|
||||
output_enote_out.enote.tx_first_key_image = tx_first_key_image;
|
||||
output_enote_out.amount = proposal.amount;
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
void get_output_proposal_internal_v1(const CarrotPaymentProposalSelfSendV1 &proposal,
|
||||
const crypto::secret_key &s_view_balance,
|
||||
const view_balance_secret_device &s_view_balance_dev,
|
||||
const crypto::key_image &tx_first_key_image,
|
||||
CarrotEnoteV1 &output_enote_out,
|
||||
rct::xmr_amount &amount_out,
|
||||
crypto::secret_key &amount_blinding_factor_out)
|
||||
RCTOutputEnoteProposal &output_enote_out)
|
||||
{
|
||||
// 1. sanity checks
|
||||
// @TODO
|
||||
|
||||
// 2. input context: input_context = "R" || KI_1
|
||||
// 2. input_context = "R" || KI_1
|
||||
input_context_t input_context;
|
||||
make_carrot_input_context(tx_first_key_image, input_context);
|
||||
|
||||
// 3. build the output enote address pieces
|
||||
// 3. s^ctx_sr = H_32(s_vb, D_e, input_context)
|
||||
crypto::hash s_sender_receiver; auto q_wiper = auto_wiper(s_sender_receiver);
|
||||
s_view_balance_dev.make_internal_sender_receiver_secret(proposal.enote_ephemeral_pubkey,
|
||||
input_context,
|
||||
s_sender_receiver);
|
||||
|
||||
// 4. build the output enote address pieces
|
||||
encrypted_payment_id_t dummy_encrypted_payment_id;
|
||||
get_output_proposal_parts(to_bytes(s_view_balance),
|
||||
get_output_proposal_parts(s_sender_receiver,
|
||||
proposal.destination_address_spend_pubkey,
|
||||
null_payment_id,
|
||||
proposal.amount,
|
||||
proposal.enote_type,
|
||||
proposal.enote_ephemeral_pubkey,
|
||||
input_context,
|
||||
false,
|
||||
s_sender_receiver,
|
||||
amount_blinding_factor_out,
|
||||
output_enote_out.amount_commitment,
|
||||
output_enote_out.onetime_address,
|
||||
output_enote_out.amount_enc,
|
||||
dummy_encrypted_payment_id,
|
||||
output_enote_out.view_tag);
|
||||
false, // coinbase_amount_commitment
|
||||
output_enote_out.amount_blinding_factor,
|
||||
output_enote_out.enote.amount_commitment,
|
||||
output_enote_out.enote.onetime_address,
|
||||
output_enote_out.enote.amount_enc,
|
||||
dummy_encrypted_payment_id);
|
||||
|
||||
// 5. vt = H_3(s_vb || input_context || Ko)
|
||||
s_view_balance_dev.make_internal_view_tag(input_context,
|
||||
output_enote_out.enote.onetime_address,
|
||||
output_enote_out.enote.view_tag);
|
||||
|
||||
// 4. generate random encrypted anchor
|
||||
output_enote_out.anchor_enc = gen_janus_anchor();
|
||||
output_enote_out.enote.anchor_enc = gen_janus_anchor();
|
||||
|
||||
// 5. save the enote ephemeral pubkey, first tx key image, and amount
|
||||
output_enote_out.enote_ephemeral_pubkey = proposal.enote_ephemeral_pubkey;
|
||||
output_enote_out.tx_first_key_image = tx_first_key_image;
|
||||
amount_out = proposal.amount;
|
||||
output_enote_out.enote.enote_ephemeral_pubkey = proposal.enote_ephemeral_pubkey;
|
||||
output_enote_out.enote.tx_first_key_image = tx_first_key_image;
|
||||
output_enote_out.amount = proposal.amount;
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
CarrotPaymentProposalV1 gen_carrot_payment_proposal_v1(const bool is_subaddress,
|
||||
|
@ -35,6 +35,7 @@
|
||||
#include "carrot_enote_types.h"
|
||||
#include "crypto/x25519.h"
|
||||
#include "destination.h"
|
||||
#include "device.h"
|
||||
#include "ringct/rctTypes.h"
|
||||
|
||||
//third party headers
|
||||
@ -78,20 +79,31 @@ struct CarrotPaymentProposalSelfSendV1 final
|
||||
crypto::x25519_pubkey enote_ephemeral_pubkey;
|
||||
};
|
||||
|
||||
struct RCTOutputEnoteProposal
|
||||
{
|
||||
CarrotEnoteV1 enote;
|
||||
|
||||
// we need this opening information to make amount range proofs
|
||||
rct::xmr_amount amount;
|
||||
crypto::secret_key amount_blinding_factor;
|
||||
};
|
||||
|
||||
/// equality operators
|
||||
bool operator==(const CarrotPaymentProposalV1 &a, const CarrotPaymentProposalV1 &b);
|
||||
/// equality operators
|
||||
bool operator==(const CarrotPaymentProposalSelfSendV1 &a, const CarrotPaymentProposalSelfSendV1 &b);
|
||||
|
||||
/// comparison operators
|
||||
bool operator<(const RCTOutputEnoteProposal &a, const RCTOutputEnoteProposal &b);
|
||||
|
||||
/**
|
||||
* brief: get_enote_ephemeral_pubkey - get the proposal's enote ephemeral pubkey D_e
|
||||
* param: proposal -
|
||||
* param: input_context -
|
||||
* outparam: enote_ephemeral_pubkey_out -
|
||||
* return: D_e
|
||||
*/
|
||||
void get_enote_ephemeral_pubkey(const CarrotPaymentProposalV1 &proposal,
|
||||
const input_context_t &input_context,
|
||||
crypto::x25519_pubkey &enote_ephemeral_pubkey_out);
|
||||
crypto::x25519_pubkey get_enote_ephemeral_pubkey(const CarrotPaymentProposalV1 &proposal,
|
||||
const input_context_t &input_context);
|
||||
/**
|
||||
* brief: get_coinbase_output_proposal_v1 - convert the carrot proposal to a coinbase output proposal
|
||||
* param: proposal -
|
||||
@ -108,49 +120,37 @@ void get_coinbase_output_proposal_v1(const CarrotPaymentProposalV1 &proposal,
|
||||
* param: tx_first_key_image -
|
||||
* outparam: output_enote_out -
|
||||
* outparam: encrypted_payment_id_out - pid_enc
|
||||
* outparam: amount_out - used to open commitment C_a
|
||||
* outparam: amount_blinding_factor_out - used to open commitment C_a
|
||||
*/
|
||||
void get_output_proposal_normal_v1(const CarrotPaymentProposalV1 &proposal,
|
||||
const crypto::key_image &tx_first_key_image,
|
||||
CarrotEnoteV1 &output_enote_out,
|
||||
encrypted_payment_id_t &encrypted_payment_id_out,
|
||||
rct::xmr_amount &amount_out,
|
||||
crypto::secret_key &amount_blinding_factor_out);
|
||||
RCTOutputEnoteProposal &output_enote_out,
|
||||
encrypted_payment_id_t &encrypted_payment_id_out);
|
||||
/**
|
||||
* brief: get_output_proposal_v1 - convert the carrot proposal to an output proposal (external selfsend)
|
||||
* param: proposal -
|
||||
* param: k_view -
|
||||
* param: k_view_dev -
|
||||
* param: primary_address_spend_pubkey -
|
||||
* param: tx_first_key_image -
|
||||
* outparam: output_enote_out -
|
||||
* outparam: amount_out - used to open commitment C_a
|
||||
* outparam: amount_blinding_factor_out - used to open commitment C_a
|
||||
*/
|
||||
void get_output_proposal_special_v1(const CarrotPaymentProposalSelfSendV1 &proposal,
|
||||
const crypto::secret_key &k_view,
|
||||
const view_incoming_key_device &k_view_dev,
|
||||
const crypto::public_key &primary_address_spend_pubkey,
|
||||
const crypto::key_image &tx_first_key_image,
|
||||
CarrotEnoteV1 &output_enote_out,
|
||||
rct::xmr_amount &amount_out,
|
||||
crypto::secret_key &amount_blinding_factor_out);
|
||||
RCTOutputEnoteProposal &output_enote_out);
|
||||
/**
|
||||
* brief: get_output_proposal_internal_v1 - convert the carrot proposal to an output proposal (internal)
|
||||
* param: proposal -
|
||||
* param: s_view_balance -
|
||||
* param: s_view_balance_dev -
|
||||
* param: primary_address_spend_pubkey -
|
||||
* param: tx_first_key_image -
|
||||
* outparam: output_enote_out -
|
||||
* outparam: partial_memo_out -
|
||||
* outparam: amount_out - used to open commitment C_a
|
||||
* outparam: amount_blinding_factor_out - used to open commitment C_a
|
||||
*/
|
||||
void get_output_proposal_internal_v1(const CarrotPaymentProposalSelfSendV1 &proposal,
|
||||
const crypto::secret_key &s_view_balance,
|
||||
const view_balance_secret_device &s_view_balance_dev,
|
||||
const crypto::key_image &tx_first_key_image,
|
||||
CarrotEnoteV1 &output_enote_out,
|
||||
rct::xmr_amount &amount_out,
|
||||
crypto::secret_key &amount_blinding_factor_out);
|
||||
RCTOutputEnoteProposal &output_enote_out);
|
||||
/**
|
||||
* brief: gen_jamtis_payment_proposal_v1 - generate a random proposal
|
||||
* param: is_subaddress - whether to generate a proposal to subaddress
|
||||
|
@ -33,6 +33,7 @@
|
||||
#include "carrot_core/carrot_enote_scan.h"
|
||||
#include "carrot_core/device_ram_borrowed.h"
|
||||
#include "carrot_core/enote_utils.h"
|
||||
#include "carrot_core/output_set_finalization.h"
|
||||
#include "carrot_core/payment_proposal.h"
|
||||
#include "crypto/crypto.h"
|
||||
#include "crypto/generators.h"
|
||||
@ -40,6 +41,41 @@
|
||||
|
||||
using namespace carrot;
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
// https://github.com/jedisct1/libsodium/blob/master/src/libsodium/crypto_scalarmult/curve25519/ref10/x25519_ref10.c#L17
|
||||
static const crypto::x25519_pubkey x25519_small_order_points[7] = {
|
||||
/* 0 (order 4) */
|
||||
{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }},
|
||||
/* 1 (order 1) */
|
||||
{{ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }},
|
||||
/* 325606250916557431795983626356110631294008115727848805560023387167927233504
|
||||
(order 8) */
|
||||
{{ 0xe0, 0xeb, 0x7a, 0x7c, 0x3b, 0x41, 0xb8, 0xae, 0x16, 0x56, 0xe3,
|
||||
0xfa, 0xf1, 0x9f, 0xc4, 0x6a, 0xda, 0x09, 0x8d, 0xeb, 0x9c, 0x32,
|
||||
0xb1, 0xfd, 0x86, 0x62, 0x05, 0x16, 0x5f, 0x49, 0xb8, 0x00 }},
|
||||
/* 39382357235489614581723060781553021112529911719440698176882885853963445705823
|
||||
(order 8) */
|
||||
{{ 0x5f, 0x9c, 0x95, 0xbc, 0xa3, 0x50, 0x8c, 0x24, 0xb1, 0xd0, 0xb1,
|
||||
0x55, 0x9c, 0x83, 0xef, 0x5b, 0x04, 0x44, 0x5c, 0xc4, 0x58, 0x1c,
|
||||
0x8e, 0x86, 0xd8, 0x22, 0x4e, 0xdd, 0xd0, 0x9f, 0x11, 0x57 }},
|
||||
/* p-1 (order 2) */
|
||||
{{ 0xec, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f }},
|
||||
/* p (=0, order 4) */
|
||||
{{ 0xed, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f }},
|
||||
/* p+1 (=1, order 1) */
|
||||
{{ 0xee, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f }}
|
||||
};
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
struct mock_carrot_keys
|
||||
@ -107,6 +143,80 @@ static bool can_open_fcmp_onetime_address(const crypto::secret_key &k_prove_spen
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
struct unittest_carrot_scan_result_t
|
||||
{
|
||||
crypto::public_key address_spend_pubkey = rct::rct2pk(rct::I);
|
||||
crypto::secret_key sender_extension_g = rct::rct2sk(rct::I);
|
||||
crypto::secret_key sender_extension_t = rct::rct2sk(rct::I);
|
||||
|
||||
rct::xmr_amount amount = 0;
|
||||
crypto::secret_key amount_blinding_factor = rct::rct2sk(rct::I);
|
||||
|
||||
CarrotEnoteType enote_type = CarrotEnoteType::PAYMENT;
|
||||
|
||||
payment_id_t payment_id = null_payment_id;
|
||||
|
||||
size_t output_index = 0;
|
||||
};
|
||||
static void unittest_scan_enote_set(const std::vector<CarrotEnoteV1> &enotes,
|
||||
const encrypted_payment_id_t encrypted_payment_id,
|
||||
const mock_carrot_keys keys,
|
||||
std::vector<unittest_carrot_scan_result_t> &res)
|
||||
{
|
||||
res.clear();
|
||||
|
||||
// external scans
|
||||
for (size_t output_index = 0; output_index < enotes.size(); ++output_index)
|
||||
{
|
||||
const CarrotEnoteV1 &enote = enotes.at(output_index);
|
||||
|
||||
// s_sr = 8 k_v D_e
|
||||
crypto::x25519_pubkey s_sr;
|
||||
make_carrot_uncontextualized_shared_key_receiver(keys.k_view, enote.enote_ephemeral_pubkey, s_sr);
|
||||
|
||||
unittest_carrot_scan_result_t scan_result{};
|
||||
const bool r = try_scan_carrot_enote_external(enote,
|
||||
encrypted_payment_id,
|
||||
s_sr,
|
||||
keys.k_view_dev,
|
||||
keys.account_spend_pubkey,
|
||||
scan_result.sender_extension_g,
|
||||
scan_result.sender_extension_t,
|
||||
scan_result.address_spend_pubkey,
|
||||
scan_result.amount,
|
||||
scan_result.amount_blinding_factor,
|
||||
scan_result.payment_id,
|
||||
scan_result.enote_type);
|
||||
|
||||
scan_result.output_index = output_index;
|
||||
|
||||
if (r)
|
||||
res.push_back(scan_result);
|
||||
}
|
||||
|
||||
// internal scans
|
||||
for (size_t output_index = 0; output_index < enotes.size(); ++output_index)
|
||||
{
|
||||
const CarrotEnoteV1 &enote = enotes.at(output_index);
|
||||
|
||||
unittest_carrot_scan_result_t scan_result{};
|
||||
const bool r = try_scan_carrot_enote_internal(enote,
|
||||
keys.s_view_balance_dev,
|
||||
scan_result.sender_extension_g,
|
||||
scan_result.sender_extension_t,
|
||||
scan_result.address_spend_pubkey,
|
||||
scan_result.amount,
|
||||
scan_result.amount_blinding_factor,
|
||||
scan_result.enote_type);
|
||||
|
||||
scan_result.output_index = output_index;
|
||||
|
||||
if (r)
|
||||
res.push_back(scan_result);
|
||||
}
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
TEST(carrot_core, ECDH_cryptonote_completeness)
|
||||
{
|
||||
crypto::secret_key k_view = rct::rct2sk(rct::skGen());
|
||||
@ -146,6 +256,38 @@ TEST(carrot_core, ECDH_subaddress_completeness)
|
||||
EXPECT_EQ(s_sr_sender, s_sr_receiver);
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
TEST(carrot_core, ECDH_mx25519_convergence)
|
||||
{
|
||||
const crypto::x25519_pubkey P = crypto::x25519_pubkey_gen();
|
||||
const crypto::x25519_secret_key a = crypto::x25519_secret_key_gen();
|
||||
const crypto::x25519_secret_key eight = crypto::x25519_scalar{{8}};
|
||||
|
||||
// do Q = 8 * a * P using mx25519
|
||||
crypto::x25519_pubkey Q_mx25519;
|
||||
crypto::x25519_scmul_key(eight, P, Q_mx25519);
|
||||
crypto::x25519_scmul_key(a, Q_mx25519, Q_mx25519);
|
||||
|
||||
// do Q = 8 * a * P using make_carrot_uncontextualized_shared_key_receiver()
|
||||
crypto::secret_key a_sk;
|
||||
memcpy(&a_sk, &a, sizeof(a_sk));
|
||||
crypto::x25519_pubkey Q_carrot;
|
||||
ASSERT_TRUE(make_carrot_uncontextualized_shared_key_receiver(a_sk, P, Q_carrot));
|
||||
|
||||
// check equal
|
||||
EXPECT_EQ(Q_mx25519, Q_carrot);
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
TEST(carrot_core, ECDH_catch_small_order_points)
|
||||
{
|
||||
const crypto::secret_key sk = rct::rct2sk(rct::skGen());
|
||||
|
||||
for (const crypto::x25519_pubkey &P : x25519_small_order_points)
|
||||
{
|
||||
crypto::x25519_pubkey Q;
|
||||
EXPECT_FALSE(make_carrot_uncontextualized_shared_key_receiver(sk, P, Q));
|
||||
}
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
TEST(carrot_core, main_address_normal_scan_completeness)
|
||||
{
|
||||
const mock_carrot_keys keys = mock_carrot_keys::generate();
|
||||
@ -161,23 +303,20 @@ TEST(carrot_core, main_address_normal_scan_completeness)
|
||||
|
||||
const crypto::key_image tx_first_key_image = rct::rct2ki(rct::pkGen());
|
||||
|
||||
CarrotEnoteV1 enote;
|
||||
RCTOutputEnoteProposal enote_proposal;
|
||||
encrypted_payment_id_t encrypted_payment_id;
|
||||
rct::xmr_amount amount;
|
||||
crypto::secret_key amount_blinding_factor;
|
||||
get_output_proposal_normal_v1(proposal,
|
||||
tx_first_key_image,
|
||||
enote,
|
||||
encrypted_payment_id,
|
||||
amount,
|
||||
amount_blinding_factor);
|
||||
enote_proposal,
|
||||
encrypted_payment_id);
|
||||
|
||||
ASSERT_EQ(proposal.amount, amount);
|
||||
ASSERT_EQ(enote.amount_commitment, rct::commit(amount, rct::sk2rct(amount_blinding_factor)));
|
||||
ASSERT_EQ(proposal.amount, enote_proposal.amount);
|
||||
const rct::key recomputed_amount_commitment = rct::commit(enote_proposal.amount, rct::sk2rct(enote_proposal.amount_blinding_factor));
|
||||
ASSERT_EQ(enote_proposal.enote.amount_commitment, recomputed_amount_commitment);
|
||||
|
||||
crypto::x25519_pubkey s_sender_receiver_unctx;
|
||||
make_carrot_uncontextualized_shared_key_receiver(keys.k_view,
|
||||
enote.enote_ephemeral_pubkey,
|
||||
enote_proposal.enote.enote_ephemeral_pubkey,
|
||||
s_sender_receiver_unctx);
|
||||
|
||||
crypto::secret_key recovered_sender_extension_g;
|
||||
@ -187,7 +326,7 @@ TEST(carrot_core, main_address_normal_scan_completeness)
|
||||
crypto::secret_key recovered_amount_blinding_factor;
|
||||
encrypted_payment_id_t recovered_payment_id;
|
||||
CarrotEnoteType recovered_enote_type;
|
||||
const bool scan_success = try_scan_carrot_enote_external(enote,
|
||||
const bool scan_success = try_scan_carrot_enote_external(enote_proposal.enote,
|
||||
encrypted_payment_id,
|
||||
s_sender_receiver_unctx,
|
||||
keys.k_view_dev,
|
||||
@ -204,8 +343,8 @@ TEST(carrot_core, main_address_normal_scan_completeness)
|
||||
|
||||
// check recovered data
|
||||
EXPECT_EQ(proposal.destination.address_spend_pubkey, recovered_address_spend_pubkey);
|
||||
EXPECT_EQ(amount, recovered_amount);
|
||||
EXPECT_EQ(amount_blinding_factor, recovered_amount_blinding_factor);
|
||||
EXPECT_EQ(proposal.amount, recovered_amount);
|
||||
EXPECT_EQ(enote_proposal.amount_blinding_factor, recovered_amount_blinding_factor);
|
||||
EXPECT_EQ(null_payment_id, recovered_payment_id);
|
||||
EXPECT_EQ(CarrotEnoteType::PAYMENT, recovered_enote_type);
|
||||
|
||||
@ -215,7 +354,7 @@ TEST(carrot_core, main_address_normal_scan_completeness)
|
||||
rct::rct2sk(rct::I),
|
||||
recovered_sender_extension_g,
|
||||
recovered_sender_extension_t,
|
||||
enote.onetime_address));
|
||||
enote_proposal.enote.onetime_address));
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
TEST(carrot_core, subaddress_normal_scan_completeness)
|
||||
@ -241,23 +380,20 @@ TEST(carrot_core, subaddress_normal_scan_completeness)
|
||||
|
||||
const crypto::key_image tx_first_key_image = rct::rct2ki(rct::pkGen());
|
||||
|
||||
CarrotEnoteV1 enote;
|
||||
RCTOutputEnoteProposal enote_proposal;
|
||||
encrypted_payment_id_t encrypted_payment_id;
|
||||
rct::xmr_amount amount;
|
||||
crypto::secret_key amount_blinding_factor;
|
||||
get_output_proposal_normal_v1(proposal,
|
||||
tx_first_key_image,
|
||||
enote,
|
||||
encrypted_payment_id,
|
||||
amount,
|
||||
amount_blinding_factor);
|
||||
enote_proposal,
|
||||
encrypted_payment_id);
|
||||
|
||||
ASSERT_EQ(proposal.amount, amount);
|
||||
ASSERT_EQ(enote.amount_commitment, rct::commit(amount, rct::sk2rct(amount_blinding_factor)));
|
||||
ASSERT_EQ(proposal.amount, enote_proposal.amount);
|
||||
const rct::key recomputed_amount_commitment = rct::commit(enote_proposal.amount, rct::sk2rct(enote_proposal.amount_blinding_factor));
|
||||
ASSERT_EQ(enote_proposal.enote.amount_commitment, recomputed_amount_commitment);
|
||||
|
||||
crypto::x25519_pubkey s_sender_receiver_unctx;
|
||||
make_carrot_uncontextualized_shared_key_receiver(keys.k_view,
|
||||
enote.enote_ephemeral_pubkey,
|
||||
enote_proposal.enote.enote_ephemeral_pubkey,
|
||||
s_sender_receiver_unctx);
|
||||
|
||||
crypto::secret_key recovered_sender_extension_g;
|
||||
@ -267,7 +403,7 @@ TEST(carrot_core, subaddress_normal_scan_completeness)
|
||||
crypto::secret_key recovered_amount_blinding_factor;
|
||||
encrypted_payment_id_t recovered_payment_id;
|
||||
CarrotEnoteType recovered_enote_type;
|
||||
const bool scan_success = try_scan_carrot_enote_external(enote,
|
||||
const bool scan_success = try_scan_carrot_enote_external(enote_proposal.enote,
|
||||
encrypted_payment_id,
|
||||
s_sender_receiver_unctx,
|
||||
keys.k_view_dev,
|
||||
@ -284,8 +420,8 @@ TEST(carrot_core, subaddress_normal_scan_completeness)
|
||||
|
||||
// check recovered data
|
||||
EXPECT_EQ(proposal.destination.address_spend_pubkey, recovered_address_spend_pubkey);
|
||||
EXPECT_EQ(amount, recovered_amount);
|
||||
EXPECT_EQ(amount_blinding_factor, recovered_amount_blinding_factor);
|
||||
EXPECT_EQ(proposal.amount, recovered_amount);
|
||||
EXPECT_EQ(enote_proposal.amount_blinding_factor, recovered_amount_blinding_factor);
|
||||
EXPECT_EQ(null_payment_id, recovered_payment_id);
|
||||
EXPECT_EQ(CarrotEnoteType::PAYMENT, recovered_enote_type);
|
||||
|
||||
@ -308,7 +444,7 @@ TEST(carrot_core, subaddress_normal_scan_completeness)
|
||||
subaddr_scalar,
|
||||
recovered_sender_extension_g,
|
||||
recovered_sender_extension_t,
|
||||
enote.onetime_address));
|
||||
enote_proposal.enote.onetime_address));
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
TEST(carrot_core, integrated_address_normal_scan_completeness)
|
||||
@ -329,23 +465,20 @@ TEST(carrot_core, integrated_address_normal_scan_completeness)
|
||||
|
||||
const crypto::key_image tx_first_key_image = rct::rct2ki(rct::pkGen());
|
||||
|
||||
CarrotEnoteV1 enote;
|
||||
RCTOutputEnoteProposal enote_proposal;
|
||||
encrypted_payment_id_t encrypted_payment_id;
|
||||
rct::xmr_amount amount;
|
||||
crypto::secret_key amount_blinding_factor;
|
||||
get_output_proposal_normal_v1(proposal,
|
||||
tx_first_key_image,
|
||||
enote,
|
||||
encrypted_payment_id,
|
||||
amount,
|
||||
amount_blinding_factor);
|
||||
enote_proposal,
|
||||
encrypted_payment_id);
|
||||
|
||||
ASSERT_EQ(proposal.amount, amount);
|
||||
ASSERT_EQ(enote.amount_commitment, rct::commit(amount, rct::sk2rct(amount_blinding_factor)));
|
||||
ASSERT_EQ(proposal.amount, enote_proposal.amount);
|
||||
const rct::key recomputed_amount_commitment = rct::commit(enote_proposal.amount, rct::sk2rct(enote_proposal.amount_blinding_factor));
|
||||
ASSERT_EQ(enote_proposal.enote.amount_commitment, recomputed_amount_commitment);
|
||||
|
||||
crypto::x25519_pubkey s_sender_receiver_unctx;
|
||||
make_carrot_uncontextualized_shared_key_receiver(keys.k_view,
|
||||
enote.enote_ephemeral_pubkey,
|
||||
enote_proposal.enote.enote_ephemeral_pubkey,
|
||||
s_sender_receiver_unctx);
|
||||
|
||||
crypto::secret_key recovered_sender_extension_g;
|
||||
@ -355,7 +488,7 @@ TEST(carrot_core, integrated_address_normal_scan_completeness)
|
||||
crypto::secret_key recovered_amount_blinding_factor;
|
||||
encrypted_payment_id_t recovered_payment_id;
|
||||
CarrotEnoteType recovered_enote_type;
|
||||
const bool scan_success = try_scan_carrot_enote_external(enote,
|
||||
const bool scan_success = try_scan_carrot_enote_external(enote_proposal.enote,
|
||||
encrypted_payment_id,
|
||||
s_sender_receiver_unctx,
|
||||
keys.k_view_dev,
|
||||
@ -372,8 +505,8 @@ TEST(carrot_core, integrated_address_normal_scan_completeness)
|
||||
|
||||
// check recovered data
|
||||
EXPECT_EQ(proposal.destination.address_spend_pubkey, recovered_address_spend_pubkey);
|
||||
EXPECT_EQ(amount, recovered_amount);
|
||||
EXPECT_EQ(amount_blinding_factor, recovered_amount_blinding_factor);
|
||||
EXPECT_EQ(proposal.amount, recovered_amount);
|
||||
EXPECT_EQ(enote_proposal.amount_blinding_factor, recovered_amount_blinding_factor);
|
||||
EXPECT_EQ(integrated_address.payment_id, recovered_payment_id);
|
||||
EXPECT_EQ(CarrotEnoteType::PAYMENT, recovered_enote_type);
|
||||
|
||||
@ -383,7 +516,7 @@ TEST(carrot_core, integrated_address_normal_scan_completeness)
|
||||
rct::rct2sk(rct::I),
|
||||
recovered_sender_extension_g,
|
||||
recovered_sender_extension_t,
|
||||
enote.onetime_address));
|
||||
enote_proposal.enote.onetime_address));
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
TEST(carrot_core, main_address_special_scan_completeness)
|
||||
@ -407,23 +540,20 @@ TEST(carrot_core, main_address_special_scan_completeness)
|
||||
|
||||
const crypto::key_image tx_first_key_image = rct::rct2ki(rct::pkGen());
|
||||
|
||||
CarrotEnoteV1 enote;
|
||||
rct::xmr_amount amount;
|
||||
crypto::secret_key amount_blinding_factor;
|
||||
RCTOutputEnoteProposal enote_proposal;
|
||||
get_output_proposal_special_v1(proposal,
|
||||
keys.k_view,
|
||||
keys.k_view_dev,
|
||||
keys.account_spend_pubkey,
|
||||
tx_first_key_image,
|
||||
enote,
|
||||
amount,
|
||||
amount_blinding_factor);
|
||||
enote_proposal);
|
||||
|
||||
ASSERT_EQ(proposal.amount, amount);
|
||||
ASSERT_EQ(enote.amount_commitment, rct::commit(amount, rct::sk2rct(amount_blinding_factor)));
|
||||
ASSERT_EQ(proposal.amount, enote_proposal.amount);
|
||||
const rct::key recomputed_amount_commitment = rct::commit(enote_proposal.amount, rct::sk2rct(enote_proposal.amount_blinding_factor));
|
||||
ASSERT_EQ(enote_proposal.enote.amount_commitment, recomputed_amount_commitment);
|
||||
|
||||
crypto::x25519_pubkey s_sender_receiver_unctx;
|
||||
make_carrot_uncontextualized_shared_key_receiver(keys.k_view,
|
||||
enote.enote_ephemeral_pubkey,
|
||||
enote_proposal.enote.enote_ephemeral_pubkey,
|
||||
s_sender_receiver_unctx);
|
||||
|
||||
crypto::secret_key recovered_sender_extension_g;
|
||||
@ -433,7 +563,7 @@ TEST(carrot_core, main_address_special_scan_completeness)
|
||||
crypto::secret_key recovered_amount_blinding_factor;
|
||||
encrypted_payment_id_t recovered_payment_id;
|
||||
CarrotEnoteType recovered_enote_type;
|
||||
const bool scan_success = try_scan_carrot_enote_external(enote,
|
||||
const bool scan_success = try_scan_carrot_enote_external(enote_proposal.enote,
|
||||
std::nullopt,
|
||||
s_sender_receiver_unctx,
|
||||
keys.k_view_dev,
|
||||
@ -450,8 +580,8 @@ TEST(carrot_core, main_address_special_scan_completeness)
|
||||
|
||||
// check recovered data
|
||||
EXPECT_EQ(proposal.destination_address_spend_pubkey, recovered_address_spend_pubkey);
|
||||
EXPECT_EQ(amount, recovered_amount);
|
||||
EXPECT_EQ(amount_blinding_factor, recovered_amount_blinding_factor);
|
||||
EXPECT_EQ(proposal.amount, recovered_amount);
|
||||
EXPECT_EQ(enote_proposal.amount_blinding_factor, recovered_amount_blinding_factor);
|
||||
EXPECT_EQ(null_payment_id, recovered_payment_id);
|
||||
EXPECT_EQ(enote_type, recovered_enote_type);
|
||||
|
||||
@ -461,7 +591,7 @@ TEST(carrot_core, main_address_special_scan_completeness)
|
||||
rct::rct2sk(rct::I),
|
||||
recovered_sender_extension_g,
|
||||
recovered_sender_extension_t,
|
||||
enote.onetime_address));
|
||||
enote_proposal.enote.onetime_address));
|
||||
}
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
@ -494,23 +624,20 @@ TEST(carrot_core, subaddress_special_scan_completeness)
|
||||
|
||||
const crypto::key_image tx_first_key_image = rct::rct2ki(rct::pkGen());
|
||||
|
||||
CarrotEnoteV1 enote;
|
||||
rct::xmr_amount amount;
|
||||
crypto::secret_key amount_blinding_factor;
|
||||
RCTOutputEnoteProposal enote_proposal;
|
||||
get_output_proposal_special_v1(proposal,
|
||||
keys.k_view,
|
||||
keys.k_view_dev,
|
||||
keys.account_spend_pubkey,
|
||||
tx_first_key_image,
|
||||
enote,
|
||||
amount,
|
||||
amount_blinding_factor);
|
||||
enote_proposal);
|
||||
|
||||
ASSERT_EQ(proposal.amount, amount);
|
||||
ASSERT_EQ(enote.amount_commitment, rct::commit(amount, rct::sk2rct(amount_blinding_factor)));
|
||||
ASSERT_EQ(proposal.amount, enote_proposal.amount);
|
||||
const rct::key recomputed_amount_commitment = rct::commit(enote_proposal.amount, rct::sk2rct(enote_proposal.amount_blinding_factor));
|
||||
ASSERT_EQ(enote_proposal.enote.amount_commitment, recomputed_amount_commitment);
|
||||
|
||||
crypto::x25519_pubkey s_sender_receiver_unctx;
|
||||
make_carrot_uncontextualized_shared_key_receiver(keys.k_view,
|
||||
enote.enote_ephemeral_pubkey,
|
||||
enote_proposal.enote.enote_ephemeral_pubkey,
|
||||
s_sender_receiver_unctx);
|
||||
|
||||
crypto::secret_key recovered_sender_extension_g;
|
||||
@ -520,7 +647,7 @@ TEST(carrot_core, subaddress_special_scan_completeness)
|
||||
crypto::secret_key recovered_amount_blinding_factor;
|
||||
encrypted_payment_id_t recovered_payment_id;
|
||||
CarrotEnoteType recovered_enote_type;
|
||||
const bool scan_success = try_scan_carrot_enote_external(enote,
|
||||
const bool scan_success = try_scan_carrot_enote_external(enote_proposal.enote,
|
||||
std::nullopt,
|
||||
s_sender_receiver_unctx,
|
||||
keys.k_view_dev,
|
||||
@ -537,8 +664,8 @@ TEST(carrot_core, subaddress_special_scan_completeness)
|
||||
|
||||
// check recovered data
|
||||
EXPECT_EQ(proposal.destination_address_spend_pubkey, recovered_address_spend_pubkey);
|
||||
EXPECT_EQ(amount, recovered_amount);
|
||||
EXPECT_EQ(amount_blinding_factor, recovered_amount_blinding_factor);
|
||||
EXPECT_EQ(proposal.amount, recovered_amount);
|
||||
EXPECT_EQ(enote_proposal.amount_blinding_factor, recovered_amount_blinding_factor);
|
||||
EXPECT_EQ(null_payment_id, recovered_payment_id);
|
||||
EXPECT_EQ(enote_type, recovered_enote_type);
|
||||
|
||||
@ -555,13 +682,13 @@ TEST(carrot_core, subaddress_special_scan_completeness)
|
||||
j_major,
|
||||
j_minor,
|
||||
subaddr_scalar);
|
||||
|
||||
|
||||
EXPECT_TRUE(can_open_fcmp_onetime_address(keys.k_prove_spend,
|
||||
keys.k_generate_image,
|
||||
subaddr_scalar,
|
||||
recovered_sender_extension_g,
|
||||
recovered_sender_extension_t,
|
||||
enote.onetime_address));
|
||||
enote_proposal.enote.onetime_address));
|
||||
}
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
@ -586,18 +713,15 @@ TEST(carrot_core, main_address_internal_scan_completeness)
|
||||
|
||||
const crypto::key_image tx_first_key_image = rct::rct2ki(rct::pkGen());
|
||||
|
||||
CarrotEnoteV1 enote;
|
||||
rct::xmr_amount amount;
|
||||
crypto::secret_key amount_blinding_factor;
|
||||
RCTOutputEnoteProposal enote_proposal;
|
||||
get_output_proposal_internal_v1(proposal,
|
||||
keys.s_view_balance,
|
||||
keys.s_view_balance_dev,
|
||||
tx_first_key_image,
|
||||
enote,
|
||||
amount,
|
||||
amount_blinding_factor);
|
||||
enote_proposal);
|
||||
|
||||
ASSERT_EQ(proposal.amount, amount);
|
||||
ASSERT_EQ(enote.amount_commitment, rct::commit(amount, rct::sk2rct(amount_blinding_factor)));
|
||||
ASSERT_EQ(proposal.amount, enote_proposal.amount);
|
||||
const rct::key recomputed_amount_commitment = rct::commit(enote_proposal.amount, rct::sk2rct(enote_proposal.amount_blinding_factor));
|
||||
ASSERT_EQ(enote_proposal.enote.amount_commitment, recomputed_amount_commitment);
|
||||
|
||||
crypto::secret_key recovered_sender_extension_g;
|
||||
crypto::secret_key recovered_sender_extension_t;
|
||||
@ -605,7 +729,7 @@ TEST(carrot_core, main_address_internal_scan_completeness)
|
||||
rct::xmr_amount recovered_amount;
|
||||
crypto::secret_key recovered_amount_blinding_factor;
|
||||
CarrotEnoteType recovered_enote_type;
|
||||
const bool scan_success = try_scan_carrot_enote_internal(enote,
|
||||
const bool scan_success = try_scan_carrot_enote_internal(enote_proposal.enote,
|
||||
keys.s_view_balance_dev,
|
||||
recovered_sender_extension_g,
|
||||
recovered_sender_extension_t,
|
||||
@ -618,8 +742,8 @@ TEST(carrot_core, main_address_internal_scan_completeness)
|
||||
|
||||
// check recovered data
|
||||
EXPECT_EQ(proposal.destination_address_spend_pubkey, recovered_address_spend_pubkey);
|
||||
EXPECT_EQ(amount, recovered_amount);
|
||||
EXPECT_EQ(amount_blinding_factor, recovered_amount_blinding_factor);
|
||||
EXPECT_EQ(proposal.amount, recovered_amount);
|
||||
EXPECT_EQ(enote_proposal.amount_blinding_factor, recovered_amount_blinding_factor);
|
||||
EXPECT_EQ(enote_type, recovered_enote_type);
|
||||
|
||||
// check spendability
|
||||
@ -628,7 +752,7 @@ TEST(carrot_core, main_address_internal_scan_completeness)
|
||||
rct::rct2sk(rct::I),
|
||||
recovered_sender_extension_g,
|
||||
recovered_sender_extension_t,
|
||||
enote.onetime_address));
|
||||
enote_proposal.enote.onetime_address));
|
||||
}
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
@ -661,18 +785,15 @@ TEST(carrot_core, subaddress_internal_scan_completeness)
|
||||
|
||||
const crypto::key_image tx_first_key_image = rct::rct2ki(rct::pkGen());
|
||||
|
||||
CarrotEnoteV1 enote;
|
||||
rct::xmr_amount amount;
|
||||
crypto::secret_key amount_blinding_factor;
|
||||
RCTOutputEnoteProposal enote_proposal;
|
||||
get_output_proposal_internal_v1(proposal,
|
||||
keys.s_view_balance,
|
||||
keys.s_view_balance_dev,
|
||||
tx_first_key_image,
|
||||
enote,
|
||||
amount,
|
||||
amount_blinding_factor);
|
||||
enote_proposal);
|
||||
|
||||
ASSERT_EQ(proposal.amount, amount);
|
||||
ASSERT_EQ(enote.amount_commitment, rct::commit(amount, rct::sk2rct(amount_blinding_factor)));
|
||||
ASSERT_EQ(proposal.amount, enote_proposal.amount);
|
||||
const rct::key recomputed_amount_commitment = rct::commit(enote_proposal.amount, rct::sk2rct(enote_proposal.amount_blinding_factor));
|
||||
ASSERT_EQ(enote_proposal.enote.amount_commitment, recomputed_amount_commitment);
|
||||
|
||||
crypto::secret_key recovered_sender_extension_g;
|
||||
crypto::secret_key recovered_sender_extension_t;
|
||||
@ -680,7 +801,7 @@ TEST(carrot_core, subaddress_internal_scan_completeness)
|
||||
rct::xmr_amount recovered_amount;
|
||||
crypto::secret_key recovered_amount_blinding_factor;
|
||||
CarrotEnoteType recovered_enote_type;
|
||||
const bool scan_success = try_scan_carrot_enote_internal(enote,
|
||||
const bool scan_success = try_scan_carrot_enote_internal(enote_proposal.enote,
|
||||
keys.s_view_balance_dev,
|
||||
recovered_sender_extension_g,
|
||||
recovered_sender_extension_t,
|
||||
@ -693,8 +814,8 @@ TEST(carrot_core, subaddress_internal_scan_completeness)
|
||||
|
||||
// check recovered data
|
||||
EXPECT_EQ(proposal.destination_address_spend_pubkey, recovered_address_spend_pubkey);
|
||||
EXPECT_EQ(amount, recovered_amount);
|
||||
EXPECT_EQ(amount_blinding_factor, recovered_amount_blinding_factor);
|
||||
EXPECT_EQ(proposal.amount, recovered_amount);
|
||||
EXPECT_EQ(enote_proposal.amount_blinding_factor, recovered_amount_blinding_factor);
|
||||
EXPECT_EQ(enote_type, recovered_enote_type);
|
||||
|
||||
// check spendability
|
||||
@ -716,7 +837,7 @@ TEST(carrot_core, subaddress_internal_scan_completeness)
|
||||
subaddr_scalar,
|
||||
recovered_sender_extension_g,
|
||||
recovered_sender_extension_t,
|
||||
enote.onetime_address));
|
||||
enote_proposal.enote.onetime_address));
|
||||
}
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
@ -772,3 +893,331 @@ TEST(carrot_core, main_address_coinbase_scan_completeness)
|
||||
enote.onetime_address));
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
TEST(carrot_core, main2main_transfer_2out_completeness)
|
||||
{
|
||||
const mock_carrot_keys alice = mock_carrot_keys::generate();
|
||||
const mock_carrot_keys bob = mock_carrot_keys::generate();
|
||||
|
||||
CarrotDestinationV1 alice_address;
|
||||
make_carrot_main_address_v1(alice.account_spend_pubkey,
|
||||
alice.main_address_view_pubkey,
|
||||
alice_address);
|
||||
|
||||
CarrotDestinationV1 bob_address;
|
||||
make_carrot_main_address_v1(bob.account_spend_pubkey,
|
||||
bob.main_address_view_pubkey,
|
||||
bob_address);
|
||||
|
||||
const crypto::key_image tx_first_key_image = rct::rct2ki(rct::pkGen());
|
||||
input_context_t input_context;
|
||||
make_carrot_input_context(tx_first_key_image, input_context);
|
||||
|
||||
const CarrotPaymentProposalV1 bob_payment_proposal = CarrotPaymentProposalV1{
|
||||
.destination = bob_address,
|
||||
.amount = crypto::rand_idx<rct::xmr_amount>(1000000),
|
||||
.randomness = gen_janus_anchor()
|
||||
};
|
||||
|
||||
const CarrotPaymentProposalSelfSendV1 alice_payment_proposal = CarrotPaymentProposalSelfSendV1{
|
||||
.destination_address_spend_pubkey = alice_address.address_spend_pubkey,
|
||||
.amount = crypto::rand_idx<rct::xmr_amount>(1000000),
|
||||
.enote_type = CarrotEnoteType::CHANGE,
|
||||
.enote_ephemeral_pubkey = get_enote_ephemeral_pubkey(bob_payment_proposal, input_context)
|
||||
};
|
||||
|
||||
std::vector<RCTOutputEnoteProposal> enote_proposals;
|
||||
encrypted_payment_id_t encrypted_payment_id;
|
||||
get_output_enote_proposals({bob_payment_proposal},
|
||||
{alice_payment_proposal},
|
||||
alice.s_view_balance_dev,
|
||||
tx_first_key_image,
|
||||
enote_proposals,
|
||||
encrypted_payment_id);
|
||||
|
||||
ASSERT_EQ(2, enote_proposals.size()); // 2-out tx
|
||||
|
||||
// collect enotes
|
||||
std::vector<CarrotEnoteV1> enotes;
|
||||
for (const RCTOutputEnoteProposal &enote_proposal : enote_proposals)
|
||||
enotes.push_back(enote_proposal.enote);
|
||||
|
||||
// check that alice scanned 1 enote
|
||||
std::vector<unittest_carrot_scan_result_t> alice_scan_vec;
|
||||
unittest_scan_enote_set(enotes, encrypted_payment_id, alice, alice_scan_vec);
|
||||
ASSERT_EQ(1, alice_scan_vec.size());
|
||||
unittest_carrot_scan_result_t alice_scan = alice_scan_vec.front();
|
||||
|
||||
// check that bob scanned 1 enote
|
||||
std::vector<unittest_carrot_scan_result_t> bob_scan_vec;
|
||||
unittest_scan_enote_set(enotes, encrypted_payment_id, bob, bob_scan_vec);
|
||||
ASSERT_EQ(1, bob_scan_vec.size());
|
||||
unittest_carrot_scan_result_t bob_scan = bob_scan_vec.front();
|
||||
|
||||
// set named references to enotes
|
||||
ASSERT_TRUE((alice_scan.output_index == 0 && bob_scan.output_index == 1) ||
|
||||
(alice_scan.output_index == 1 && bob_scan.output_index == 0));
|
||||
const CarrotEnoteV1 &alice_enote = enotes.at(alice_scan.output_index);
|
||||
const CarrotEnoteV1 &bob_enote = enotes.at(bob_scan.output_index);
|
||||
|
||||
// check Alice's recovered data
|
||||
EXPECT_EQ(alice_payment_proposal.destination_address_spend_pubkey, alice_scan.address_spend_pubkey);
|
||||
EXPECT_EQ(alice_payment_proposal.amount, alice_scan.amount);
|
||||
EXPECT_EQ(alice_enote.amount_commitment, rct::commit(alice_scan.amount, rct::sk2rct(alice_scan.amount_blinding_factor)));
|
||||
EXPECT_EQ(null_payment_id, alice_scan.payment_id);
|
||||
EXPECT_EQ(alice_payment_proposal.enote_type, alice_scan.enote_type);
|
||||
|
||||
// check Bob's recovered data
|
||||
EXPECT_EQ(bob_payment_proposal.destination.address_spend_pubkey, bob_scan.address_spend_pubkey);
|
||||
EXPECT_EQ(bob_payment_proposal.amount, bob_scan.amount);
|
||||
EXPECT_EQ(bob_enote.amount_commitment, rct::commit(bob_scan.amount, rct::sk2rct(bob_scan.amount_blinding_factor)));
|
||||
EXPECT_EQ(null_payment_id, bob_scan.payment_id);
|
||||
EXPECT_EQ(CarrotEnoteType::PAYMENT, bob_scan.enote_type);
|
||||
|
||||
// check Alice spendability
|
||||
EXPECT_TRUE(can_open_fcmp_onetime_address(alice.k_prove_spend,
|
||||
alice.k_generate_image,
|
||||
crypto::secret_key{{1}},
|
||||
alice_scan.sender_extension_g,
|
||||
alice_scan.sender_extension_t,
|
||||
alice_enote.onetime_address));
|
||||
|
||||
// check Bob spendability
|
||||
EXPECT_TRUE(can_open_fcmp_onetime_address(bob.k_prove_spend,
|
||||
bob.k_generate_image,
|
||||
crypto::secret_key{{1}},
|
||||
bob_scan.sender_extension_g,
|
||||
bob_scan.sender_extension_t,
|
||||
bob_enote.onetime_address));
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
TEST(carrot_core, sub2sub_transfer_2out_completeness)
|
||||
{
|
||||
const mock_carrot_keys alice = mock_carrot_keys::generate();
|
||||
const mock_carrot_keys bob = mock_carrot_keys::generate();
|
||||
|
||||
const uint32_t alice_j_major = crypto::rand<uint32_t>();
|
||||
const uint32_t alice_j_minor = crypto::rand<uint32_t>();
|
||||
CarrotDestinationV1 alice_address;
|
||||
make_carrot_subaddress_v1(alice.account_spend_pubkey,
|
||||
alice.account_view_pubkey,
|
||||
alice.s_generate_address,
|
||||
alice_j_major,
|
||||
alice_j_minor,
|
||||
alice_address);
|
||||
|
||||
const uint32_t bob_j_major = crypto::rand<uint32_t>();
|
||||
const uint32_t bob_j_minor = crypto::rand<uint32_t>();
|
||||
CarrotDestinationV1 bob_address;
|
||||
make_carrot_subaddress_v1(bob.account_spend_pubkey,
|
||||
bob.account_view_pubkey,
|
||||
bob.s_generate_address,
|
||||
bob_j_major,
|
||||
bob_j_minor,
|
||||
bob_address);
|
||||
|
||||
const crypto::key_image tx_first_key_image = rct::rct2ki(rct::pkGen());
|
||||
input_context_t input_context;
|
||||
make_carrot_input_context(tx_first_key_image, input_context);
|
||||
|
||||
const CarrotPaymentProposalV1 bob_payment_proposal = CarrotPaymentProposalV1{
|
||||
.destination = bob_address,
|
||||
.amount = crypto::rand_idx<rct::xmr_amount>(1000000),
|
||||
.randomness = gen_janus_anchor()
|
||||
};
|
||||
|
||||
const CarrotPaymentProposalSelfSendV1 alice_payment_proposal = CarrotPaymentProposalSelfSendV1{
|
||||
.destination_address_spend_pubkey = alice_address.address_spend_pubkey,
|
||||
.amount = crypto::rand_idx<rct::xmr_amount>(1000000),
|
||||
.enote_type = CarrotEnoteType::CHANGE,
|
||||
.enote_ephemeral_pubkey = get_enote_ephemeral_pubkey(bob_payment_proposal, input_context)
|
||||
};
|
||||
|
||||
std::vector<RCTOutputEnoteProposal> enote_proposals;
|
||||
encrypted_payment_id_t encrypted_payment_id;
|
||||
get_output_enote_proposals({bob_payment_proposal},
|
||||
{alice_payment_proposal},
|
||||
alice.s_view_balance_dev,
|
||||
tx_first_key_image,
|
||||
enote_proposals,
|
||||
encrypted_payment_id);
|
||||
|
||||
ASSERT_EQ(2, enote_proposals.size()); // 2-out tx
|
||||
|
||||
// collect enotes
|
||||
std::vector<CarrotEnoteV1> enotes;
|
||||
for (const RCTOutputEnoteProposal &enote_proposal : enote_proposals)
|
||||
enotes.push_back(enote_proposal.enote);
|
||||
|
||||
// check that alice scanned 1 enote
|
||||
std::vector<unittest_carrot_scan_result_t> alice_scan_vec;
|
||||
unittest_scan_enote_set(enotes, encrypted_payment_id, alice, alice_scan_vec);
|
||||
ASSERT_EQ(1, alice_scan_vec.size());
|
||||
unittest_carrot_scan_result_t alice_scan = alice_scan_vec.front();
|
||||
|
||||
// check that bob scanned 1 enote
|
||||
std::vector<unittest_carrot_scan_result_t> bob_scan_vec;
|
||||
unittest_scan_enote_set(enotes, encrypted_payment_id, bob, bob_scan_vec);
|
||||
ASSERT_EQ(1, bob_scan_vec.size());
|
||||
unittest_carrot_scan_result_t bob_scan = bob_scan_vec.front();
|
||||
|
||||
// set named references to enotes
|
||||
ASSERT_TRUE((alice_scan.output_index == 0 && bob_scan.output_index == 1) ||
|
||||
(alice_scan.output_index == 1 && bob_scan.output_index == 0));
|
||||
const CarrotEnoteV1 &alice_enote = enotes.at(alice_scan.output_index);
|
||||
const CarrotEnoteV1 &bob_enote = enotes.at(bob_scan.output_index);
|
||||
|
||||
// check Alice's recovered data
|
||||
EXPECT_EQ(alice_payment_proposal.destination_address_spend_pubkey, alice_scan.address_spend_pubkey);
|
||||
EXPECT_EQ(alice_payment_proposal.amount, alice_scan.amount);
|
||||
EXPECT_EQ(alice_enote.amount_commitment, rct::commit(alice_scan.amount, rct::sk2rct(alice_scan.amount_blinding_factor)));
|
||||
EXPECT_EQ(null_payment_id, alice_scan.payment_id);
|
||||
EXPECT_EQ(alice_payment_proposal.enote_type, alice_scan.enote_type);
|
||||
|
||||
// check Bob's recovered data
|
||||
EXPECT_EQ(bob_payment_proposal.destination.address_spend_pubkey, bob_scan.address_spend_pubkey);
|
||||
EXPECT_EQ(bob_payment_proposal.amount, bob_scan.amount);
|
||||
EXPECT_EQ(bob_enote.amount_commitment, rct::commit(bob_scan.amount, rct::sk2rct(bob_scan.amount_blinding_factor)));
|
||||
EXPECT_EQ(null_payment_id, bob_scan.payment_id);
|
||||
EXPECT_EQ(CarrotEnoteType::PAYMENT, bob_scan.enote_type);
|
||||
|
||||
// check Alice spendability
|
||||
crypto::secret_key alice_address_generator;
|
||||
make_carrot_index_extension_generator(alice.s_generate_address,
|
||||
alice_j_major,
|
||||
alice_j_minor,
|
||||
alice_address_generator);
|
||||
|
||||
crypto::secret_key alice_subaddr_scalar;
|
||||
make_carrot_subaddress_scalar(alice.account_spend_pubkey,
|
||||
alice_address_generator,
|
||||
alice_j_major,
|
||||
alice_j_minor,
|
||||
alice_subaddr_scalar);
|
||||
|
||||
EXPECT_TRUE(can_open_fcmp_onetime_address(alice.k_prove_spend,
|
||||
alice.k_generate_image,
|
||||
alice_subaddr_scalar,
|
||||
alice_scan.sender_extension_g,
|
||||
alice_scan.sender_extension_t,
|
||||
alice_enote.onetime_address));
|
||||
|
||||
// check Bob spendability
|
||||
crypto::secret_key bob_address_generator;
|
||||
make_carrot_index_extension_generator(bob.s_generate_address,
|
||||
bob_j_major,
|
||||
bob_j_minor,
|
||||
bob_address_generator);
|
||||
|
||||
crypto::secret_key bob_subaddr_scalar;
|
||||
make_carrot_subaddress_scalar(bob.account_spend_pubkey,
|
||||
bob_address_generator,
|
||||
bob_j_major,
|
||||
bob_j_minor,
|
||||
bob_subaddr_scalar);
|
||||
|
||||
EXPECT_TRUE(can_open_fcmp_onetime_address(bob.k_prove_spend,
|
||||
bob.k_generate_image,
|
||||
bob_subaddr_scalar,
|
||||
bob_scan.sender_extension_g,
|
||||
bob_scan.sender_extension_t,
|
||||
bob_enote.onetime_address));
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
TEST(carrot_core, main2integ_transfer_2out_completeness)
|
||||
{
|
||||
const mock_carrot_keys alice = mock_carrot_keys::generate();
|
||||
const mock_carrot_keys bob = mock_carrot_keys::generate();
|
||||
|
||||
CarrotDestinationV1 alice_address;
|
||||
make_carrot_main_address_v1(alice.account_spend_pubkey,
|
||||
alice.main_address_view_pubkey,
|
||||
alice_address);
|
||||
|
||||
CarrotDestinationV1 bob_address;
|
||||
make_carrot_integrated_address_v1(bob.account_spend_pubkey,
|
||||
bob.main_address_view_pubkey,
|
||||
gen_payment_id(),
|
||||
bob_address);
|
||||
|
||||
const crypto::key_image tx_first_key_image = rct::rct2ki(rct::pkGen());
|
||||
input_context_t input_context;
|
||||
make_carrot_input_context(tx_first_key_image, input_context);
|
||||
|
||||
const CarrotPaymentProposalV1 bob_payment_proposal = CarrotPaymentProposalV1{
|
||||
.destination = bob_address,
|
||||
.amount = crypto::rand_idx<rct::xmr_amount>(1000000),
|
||||
.randomness = gen_janus_anchor()
|
||||
};
|
||||
|
||||
const CarrotPaymentProposalSelfSendV1 alice_payment_proposal = CarrotPaymentProposalSelfSendV1{
|
||||
.destination_address_spend_pubkey = alice_address.address_spend_pubkey,
|
||||
.amount = crypto::rand_idx<rct::xmr_amount>(1000000),
|
||||
.enote_type = CarrotEnoteType::CHANGE,
|
||||
.enote_ephemeral_pubkey = get_enote_ephemeral_pubkey(bob_payment_proposal, input_context)
|
||||
};
|
||||
|
||||
std::vector<RCTOutputEnoteProposal> enote_proposals;
|
||||
encrypted_payment_id_t encrypted_payment_id;
|
||||
get_output_enote_proposals({bob_payment_proposal},
|
||||
{alice_payment_proposal},
|
||||
alice.s_view_balance_dev,
|
||||
tx_first_key_image,
|
||||
enote_proposals,
|
||||
encrypted_payment_id);
|
||||
|
||||
ASSERT_EQ(2, enote_proposals.size()); // 2-out tx
|
||||
|
||||
// collect enotes
|
||||
std::vector<CarrotEnoteV1> enotes;
|
||||
for (const RCTOutputEnoteProposal &enote_proposal : enote_proposals)
|
||||
enotes.push_back(enote_proposal.enote);
|
||||
|
||||
// check that alice scanned 1 enote
|
||||
std::vector<unittest_carrot_scan_result_t> alice_scan_vec;
|
||||
unittest_scan_enote_set(enotes, encrypted_payment_id, alice, alice_scan_vec);
|
||||
ASSERT_EQ(1, alice_scan_vec.size());
|
||||
unittest_carrot_scan_result_t alice_scan = alice_scan_vec.front();
|
||||
|
||||
// check that bob scanned 1 enote
|
||||
std::vector<unittest_carrot_scan_result_t> bob_scan_vec;
|
||||
unittest_scan_enote_set(enotes, encrypted_payment_id, bob, bob_scan_vec);
|
||||
ASSERT_EQ(1, bob_scan_vec.size());
|
||||
unittest_carrot_scan_result_t bob_scan = bob_scan_vec.front();
|
||||
|
||||
// set named references to enotes
|
||||
ASSERT_TRUE((alice_scan.output_index == 0 && bob_scan.output_index == 1) ||
|
||||
(alice_scan.output_index == 1 && bob_scan.output_index == 0));
|
||||
const CarrotEnoteV1 &alice_enote = enotes.at(alice_scan.output_index);
|
||||
const CarrotEnoteV1 &bob_enote = enotes.at(bob_scan.output_index);
|
||||
|
||||
// check Alice's recovered data
|
||||
EXPECT_EQ(alice_payment_proposal.destination_address_spend_pubkey, alice_scan.address_spend_pubkey);
|
||||
EXPECT_EQ(alice_payment_proposal.amount, alice_scan.amount);
|
||||
EXPECT_EQ(alice_enote.amount_commitment, rct::commit(alice_scan.amount, rct::sk2rct(alice_scan.amount_blinding_factor)));
|
||||
EXPECT_EQ(null_payment_id, alice_scan.payment_id);
|
||||
EXPECT_EQ(alice_payment_proposal.enote_type, alice_scan.enote_type);
|
||||
|
||||
// check Bob's recovered data
|
||||
EXPECT_EQ(bob_payment_proposal.destination.address_spend_pubkey, bob_scan.address_spend_pubkey);
|
||||
EXPECT_EQ(bob_payment_proposal.amount, bob_scan.amount);
|
||||
EXPECT_EQ(bob_enote.amount_commitment, rct::commit(bob_scan.amount, rct::sk2rct(bob_scan.amount_blinding_factor)));
|
||||
EXPECT_EQ(bob_address.payment_id, bob_scan.payment_id); // DIFFERENT FROM MAIN
|
||||
EXPECT_EQ(CarrotEnoteType::PAYMENT, bob_scan.enote_type);
|
||||
|
||||
// check Alice spendability
|
||||
EXPECT_TRUE(can_open_fcmp_onetime_address(alice.k_prove_spend,
|
||||
alice.k_generate_image,
|
||||
crypto::secret_key{{1}},
|
||||
alice_scan.sender_extension_g,
|
||||
alice_scan.sender_extension_t,
|
||||
alice_enote.onetime_address));
|
||||
|
||||
// check Bob spendability
|
||||
EXPECT_TRUE(can_open_fcmp_onetime_address(bob.k_prove_spend,
|
||||
bob.k_generate_image,
|
||||
crypto::secret_key{{1}},
|
||||
bob_scan.sender_extension_g,
|
||||
bob_scan.sender_extension_t,
|
||||
bob_enote.onetime_address));
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
|
Loading…
Reference in New Issue
Block a user