fix wallet2::check_tx_hey_helper
And fix compilation bug in wallet_tx_builder and reorganize unit tests.
This commit is contained in:
@@ -55,8 +55,8 @@ namespace wallet
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
static bool parse_tx_extra_for_scanning(const std::vector<std::uint8_t> &tx_extra,
|
||||
const std::size_t n_outputs,
|
||||
std::vector<crypto::public_key> &main_ephemeral_pubkeys_out,
|
||||
std::vector<crypto::public_key> &additional_ephemeral_pubkeys_out,
|
||||
std::vector<crypto::public_key> &main_tx_ephemeral_pubkeys_out,
|
||||
std::vector<crypto::public_key> &additional_tx_ephemeral_pubkeys_out,
|
||||
cryptonote::blobdata &tx_extra_nonce_out)
|
||||
{
|
||||
// 1. parse extra fields
|
||||
@@ -67,14 +67,14 @@ static bool parse_tx_extra_for_scanning(const std::vector<std::uint8_t> &tx_extr
|
||||
cryptonote::tx_extra_pub_key field_main_pubkey;
|
||||
size_t field_main_pubkey_index = 0;
|
||||
while (cryptonote::find_tx_extra_field_by_type(tx_extra_fields, field_main_pubkey, field_main_pubkey_index++))
|
||||
main_ephemeral_pubkeys_out.push_back(field_main_pubkey.pub_key);
|
||||
main_tx_ephemeral_pubkeys_out.push_back(field_main_pubkey.pub_key);
|
||||
|
||||
// 3. extract additional tx pubkeys
|
||||
cryptonote::tx_extra_additional_pub_keys field_additional_pubkeys;
|
||||
if (cryptonote::find_tx_extra_field_by_type(tx_extra_fields, field_additional_pubkeys))
|
||||
{
|
||||
if (field_additional_pubkeys.data.size() == n_outputs)
|
||||
additional_ephemeral_pubkeys_out = std::move(field_additional_pubkeys.data);
|
||||
additional_tx_ephemeral_pubkeys_out = std::move(field_additional_pubkeys.data);
|
||||
else
|
||||
fully_parsed = false;
|
||||
}
|
||||
@@ -89,20 +89,20 @@ static bool parse_tx_extra_for_scanning(const std::vector<std::uint8_t> &tx_extr
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
static bool parse_tx_extra_for_scanning(const cryptonote::transaction_prefix &tx_prefix,
|
||||
std::vector<crypto::public_key> &main_ephemeral_pubkeys_out,
|
||||
std::vector<crypto::public_key> &additional_ephemeral_pubkeys_out,
|
||||
std::vector<crypto::public_key> &main_tx_ephemeral_pubkeys_out,
|
||||
std::vector<crypto::public_key> &additional_tx_ephemeral_pubkeys_out,
|
||||
cryptonote::blobdata &tx_extra_nonce_out)
|
||||
{
|
||||
return parse_tx_extra_for_scanning(tx_prefix.extra,
|
||||
tx_prefix.vout.size(),
|
||||
main_ephemeral_pubkeys_out,
|
||||
additional_ephemeral_pubkeys_out,
|
||||
main_tx_ephemeral_pubkeys_out,
|
||||
additional_tx_ephemeral_pubkeys_out,
|
||||
tx_extra_nonce_out);
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
static void perform_ecdh_derivations(const epee::span<const crypto::public_key> main_ephemeral_pubkeys,
|
||||
const epee::span<const crypto::public_key> additional_ephemeral_pubkeys,
|
||||
static void perform_ecdh_derivations(const epee::span<const crypto::public_key> main_tx_ephemeral_pubkeys,
|
||||
const epee::span<const crypto::public_key> additional_tx_ephemeral_pubkeys,
|
||||
const cryptonote::account_keys &acc,
|
||||
const bool is_carrot,
|
||||
std::vector<crypto::key_derivation> &main_derivations_out,
|
||||
@@ -110,7 +110,7 @@ static void perform_ecdh_derivations(const epee::span<const crypto::public_key>
|
||||
{
|
||||
main_derivations_out.clear();
|
||||
additional_derivations_out.clear();
|
||||
main_derivations_out.reserve(main_ephemeral_pubkeys.size());
|
||||
main_derivations_out.reserve(main_tx_ephemeral_pubkeys.size());
|
||||
additional_derivations_out.reserve(additional_derivations_out.size());
|
||||
|
||||
if (is_carrot)
|
||||
@@ -118,36 +118,36 @@ static void perform_ecdh_derivations(const epee::span<const crypto::public_key>
|
||||
//! @TODO: HW device
|
||||
carrot::view_incoming_key_ram_borrowed_device k_view_dev(acc.m_view_secret_key);
|
||||
|
||||
for (const crypto::public_key &main_ephemeral_pubkey : main_ephemeral_pubkeys)
|
||||
for (const crypto::public_key &main_tx_ephemeral_pubkey : main_tx_ephemeral_pubkeys)
|
||||
{
|
||||
mx25519_pubkey s_sender_receiver_unctx;
|
||||
k_view_dev.view_key_scalar_mult_x25519(
|
||||
carrot::raw_byte_convert<mx25519_pubkey>(main_ephemeral_pubkey),
|
||||
carrot::raw_byte_convert<mx25519_pubkey>(main_tx_ephemeral_pubkey),
|
||||
s_sender_receiver_unctx);
|
||||
main_derivations_out.push_back(carrot::raw_byte_convert<crypto::key_derivation>(s_sender_receiver_unctx));
|
||||
}
|
||||
|
||||
for (const crypto::public_key &additional_ephemeral_pubkey : additional_ephemeral_pubkeys)
|
||||
for (const crypto::public_key &additional_tx_ephemeral_pubkey : additional_tx_ephemeral_pubkeys)
|
||||
{
|
||||
mx25519_pubkey s_sender_receiver_unctx;
|
||||
k_view_dev.view_key_scalar_mult_x25519(
|
||||
carrot::raw_byte_convert<mx25519_pubkey>(additional_ephemeral_pubkey),
|
||||
carrot::raw_byte_convert<mx25519_pubkey>(additional_tx_ephemeral_pubkey),
|
||||
s_sender_receiver_unctx);
|
||||
additional_derivations_out.push_back(carrot::raw_byte_convert<crypto::key_derivation>(s_sender_receiver_unctx));
|
||||
}
|
||||
}
|
||||
else // !is_carrot
|
||||
{
|
||||
for (const crypto::public_key &main_ephemeral_pubkey : main_ephemeral_pubkeys)
|
||||
for (const crypto::public_key &main_tx_ephemeral_pubkey : main_tx_ephemeral_pubkeys)
|
||||
{
|
||||
acc.get_device().generate_key_derivation(main_ephemeral_pubkey,
|
||||
acc.get_device().generate_key_derivation(main_tx_ephemeral_pubkey,
|
||||
acc.m_view_secret_key,
|
||||
tools::add_element(main_derivations_out));
|
||||
}
|
||||
|
||||
for (const crypto::public_key &additional_ephemeral_pubkey : additional_ephemeral_pubkeys)
|
||||
for (const crypto::public_key &additional_tx_ephemeral_pubkey : additional_tx_ephemeral_pubkeys)
|
||||
{
|
||||
acc.get_device().generate_key_derivation(additional_ephemeral_pubkey,
|
||||
acc.get_device().generate_key_derivation(additional_tx_ephemeral_pubkey,
|
||||
acc.m_view_secret_key,
|
||||
tools::add_element(additional_derivations_out));
|
||||
}
|
||||
@@ -384,16 +384,16 @@ std::optional<enote_view_incoming_scan_info_t> try_view_incoming_scan_enote_dest
|
||||
const std::unordered_map<crypto::public_key, cryptonote::subaddress_index> &subaddress_map)
|
||||
{
|
||||
// 1. parse tx extra
|
||||
std::vector<crypto::public_key> main_ephemeral_pubkeys;
|
||||
std::vector<crypto::public_key> additional_ephemeral_pubkeys;
|
||||
std::vector<crypto::public_key> main_tx_ephemeral_pubkeys;
|
||||
std::vector<crypto::public_key> additional_tx_ephemeral_pubkeys;
|
||||
cryptonote::blobdata tx_extra_nonce;
|
||||
parse_tx_extra_for_scanning(tx_prefix, main_ephemeral_pubkeys, additional_ephemeral_pubkeys, tx_extra_nonce);
|
||||
parse_tx_extra_for_scanning(tx_prefix, main_tx_ephemeral_pubkeys, additional_tx_ephemeral_pubkeys, tx_extra_nonce);
|
||||
|
||||
// 2. perform ECDH derivations
|
||||
std::vector<crypto::key_derivation> main_derivations;
|
||||
std::vector<crypto::key_derivation> additional_derivations;
|
||||
perform_ecdh_derivations(epee::to_span(main_ephemeral_pubkeys),
|
||||
epee::to_span(additional_ephemeral_pubkeys),
|
||||
perform_ecdh_derivations(epee::to_span(main_tx_ephemeral_pubkeys),
|
||||
epee::to_span(additional_tx_ephemeral_pubkeys),
|
||||
acc,
|
||||
carrot::is_carrot_transaction_v1(tx_prefix),
|
||||
main_derivations,
|
||||
@@ -402,8 +402,8 @@ std::optional<enote_view_incoming_scan_info_t> try_view_incoming_scan_enote_dest
|
||||
// 3. view-scan enote destination
|
||||
return try_view_incoming_scan_enote_destination(tx_prefix.vout.at(local_output_index),
|
||||
amount_commitment,
|
||||
epee::to_span(main_ephemeral_pubkeys),
|
||||
epee::to_span(additional_ephemeral_pubkeys),
|
||||
epee::to_span(main_tx_ephemeral_pubkeys),
|
||||
epee::to_span(additional_tx_ephemeral_pubkeys),
|
||||
tx_extra_nonce,
|
||||
tx_prefix.vin.at(0),
|
||||
local_output_index,
|
||||
@@ -543,33 +543,21 @@ std::optional<enote_view_incoming_scan_info_t> try_view_incoming_scan_enote(
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
void view_incoming_scan_transaction(
|
||||
const cryptonote::transaction &tx,
|
||||
const epee::span<const crypto::public_key> main_tx_ephemeral_pubkeys,
|
||||
const epee::span<const crypto::public_key> additional_tx_ephemeral_pubkeys,
|
||||
const cryptonote::blobdata &tx_extra_nonce,
|
||||
const epee::span<const crypto::key_derivation> main_derivations,
|
||||
const std::vector<crypto::key_derivation> &additional_derivations,
|
||||
const cryptonote::account_keys &acc,
|
||||
const std::unordered_map<crypto::public_key, cryptonote::subaddress_index> &subaddress_map,
|
||||
const epee::span<std::optional<enote_view_incoming_scan_info_t>> &enote_scan_infos_out)
|
||||
const epee::span<std::optional<enote_view_incoming_scan_info_t>> enote_scan_infos_out)
|
||||
{
|
||||
const size_t n_outputs = tx.vout.size();
|
||||
|
||||
CHECK_AND_ASSERT_THROW_MES(enote_scan_infos_out.size() == n_outputs,
|
||||
"view_incoming_scan_transaction: enote scan span wrong length");
|
||||
|
||||
// 1. parse tx extra
|
||||
std::vector<crypto::public_key> main_ephemeral_pubkeys;
|
||||
std::vector<crypto::public_key> additional_ephemeral_pubkeys;
|
||||
cryptonote::blobdata tx_extra_nonce;
|
||||
if (!parse_tx_extra_for_scanning(tx, main_ephemeral_pubkeys, additional_ephemeral_pubkeys, tx_extra_nonce))
|
||||
MWARNING("Transaction extra has unsupported format: " << cryptonote::get_transaction_hash(tx));
|
||||
|
||||
// 2. perform ECDH derivations
|
||||
std::vector<crypto::key_derivation> main_derivations;
|
||||
std::vector<crypto::key_derivation> additional_derivations;
|
||||
perform_ecdh_derivations(epee::to_span(main_ephemeral_pubkeys),
|
||||
epee::to_span(additional_ephemeral_pubkeys),
|
||||
acc,
|
||||
carrot::is_carrot_transaction_v1(tx),
|
||||
main_derivations,
|
||||
additional_derivations);
|
||||
|
||||
// 3. view-incoming scan output enotes
|
||||
// do view-incoming scan for each output enotes
|
||||
for (size_t local_output_index = 0; local_output_index < n_outputs; ++local_output_index)
|
||||
{
|
||||
auto &enote_scan_info = const_cast<std::optional<enote_view_incoming_scan_info_t>&>(enote_scan_infos_out[local_output_index]);
|
||||
@@ -578,18 +566,80 @@ void view_incoming_scan_transaction(
|
||||
|
||||
enote_scan_info = try_view_incoming_scan_enote(enote_destination,
|
||||
tx.rct_signatures,
|
||||
epee::to_span(main_ephemeral_pubkeys),
|
||||
epee::to_span(additional_ephemeral_pubkeys),
|
||||
main_tx_ephemeral_pubkeys,
|
||||
additional_tx_ephemeral_pubkeys,
|
||||
tx_extra_nonce,
|
||||
tx.vin.at(0),
|
||||
local_output_index,
|
||||
epee::to_span(main_derivations),
|
||||
main_derivations,
|
||||
additional_derivations,
|
||||
acc,
|
||||
subaddress_map);
|
||||
}
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
void view_incoming_scan_transaction(
|
||||
const cryptonote::transaction &tx,
|
||||
const epee::span<const crypto::key_derivation> custom_main_derivations,
|
||||
const std::vector<crypto::key_derivation> &custom_additional_derivations,
|
||||
const cryptonote::account_keys &acc,
|
||||
const std::unordered_map<crypto::public_key, cryptonote::subaddress_index> &subaddress_map,
|
||||
const epee::span<std::optional<enote_view_incoming_scan_info_t>> enote_scan_infos_out)
|
||||
{
|
||||
// 1. parse tx extra
|
||||
std::vector<crypto::public_key> main_tx_ephemeral_pubkeys;
|
||||
std::vector<crypto::public_key> additional_tx_ephemeral_pubkeys;
|
||||
cryptonote::blobdata tx_extra_nonce;
|
||||
if (!parse_tx_extra_for_scanning(tx, main_tx_ephemeral_pubkeys, additional_tx_ephemeral_pubkeys, tx_extra_nonce))
|
||||
MWARNING("Transaction extra has unsupported format: " << cryptonote::get_transaction_hash(tx));
|
||||
|
||||
// 2. view-incoming scan output enotes
|
||||
view_incoming_scan_transaction(tx,
|
||||
epee::to_span(main_tx_ephemeral_pubkeys),
|
||||
epee::to_span(additional_tx_ephemeral_pubkeys),
|
||||
tx_extra_nonce,
|
||||
custom_main_derivations,
|
||||
custom_additional_derivations,
|
||||
acc,
|
||||
subaddress_map,
|
||||
enote_scan_infos_out);
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
void view_incoming_scan_transaction(
|
||||
const cryptonote::transaction &tx,
|
||||
const cryptonote::account_keys &acc,
|
||||
const std::unordered_map<crypto::public_key, cryptonote::subaddress_index> &subaddress_map,
|
||||
const epee::span<std::optional<enote_view_incoming_scan_info_t>> enote_scan_infos_out)
|
||||
{
|
||||
// 1. parse tx extra
|
||||
std::vector<crypto::public_key> main_tx_ephemeral_pubkeys;
|
||||
std::vector<crypto::public_key> additional_tx_ephemeral_pubkeys;
|
||||
cryptonote::blobdata tx_extra_nonce;
|
||||
if (!parse_tx_extra_for_scanning(tx, main_tx_ephemeral_pubkeys, additional_tx_ephemeral_pubkeys, tx_extra_nonce))
|
||||
MWARNING("Transaction extra has unsupported format: " << cryptonote::get_transaction_hash(tx));
|
||||
|
||||
// 2. perform ECDH derivations
|
||||
std::vector<crypto::key_derivation> main_derivations;
|
||||
std::vector<crypto::key_derivation> additional_derivations;
|
||||
perform_ecdh_derivations(epee::to_span(main_tx_ephemeral_pubkeys),
|
||||
epee::to_span(additional_tx_ephemeral_pubkeys),
|
||||
acc,
|
||||
carrot::is_carrot_transaction_v1(tx),
|
||||
main_derivations,
|
||||
additional_derivations);
|
||||
|
||||
// 3. view-incoming scan output enotes
|
||||
view_incoming_scan_transaction(tx,
|
||||
epee::to_span(main_tx_ephemeral_pubkeys),
|
||||
epee::to_span(additional_tx_ephemeral_pubkeys),
|
||||
tx_extra_nonce,
|
||||
epee::to_span(main_derivations),
|
||||
additional_derivations,
|
||||
acc,
|
||||
subaddress_map,
|
||||
enote_scan_infos_out);
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
std::vector<std::optional<enote_view_incoming_scan_info_t>> view_incoming_scan_transaction(
|
||||
const cryptonote::transaction &tx,
|
||||
const cryptonote::account_keys &acc,
|
||||
|
||||
@@ -123,9 +123,26 @@ std::optional<enote_view_incoming_scan_info_t> try_view_incoming_scan_enote(
|
||||
|
||||
void view_incoming_scan_transaction(
|
||||
const cryptonote::transaction &tx,
|
||||
const epee::span<const crypto::public_key> main_tx_ephemeral_pubkeys,
|
||||
const epee::span<const crypto::public_key> additional_tx_ephemeral_pubkeys,
|
||||
const cryptonote::blobdata &tx_extra_nonce,
|
||||
const epee::span<const crypto::key_derivation> main_derivations,
|
||||
const std::vector<crypto::key_derivation> &additional_derivations,
|
||||
const cryptonote::account_keys &acc,
|
||||
const std::unordered_map<crypto::public_key, cryptonote::subaddress_index> &subaddress_map,
|
||||
const epee::span<std::optional<enote_view_incoming_scan_info_t>> &enote_scan_infos_out);
|
||||
const epee::span<std::optional<enote_view_incoming_scan_info_t>> enote_scan_infos_out);
|
||||
void view_incoming_scan_transaction(
|
||||
const cryptonote::transaction &tx,
|
||||
const epee::span<const crypto::key_derivation> custom_main_derivations,
|
||||
const std::vector<crypto::key_derivation> &custom_additional_derivations,
|
||||
const cryptonote::account_keys &acc,
|
||||
const std::unordered_map<crypto::public_key, cryptonote::subaddress_index> &subaddress_map,
|
||||
const epee::span<std::optional<enote_view_incoming_scan_info_t>> enote_scan_infos_out);
|
||||
void view_incoming_scan_transaction(
|
||||
const cryptonote::transaction &tx,
|
||||
const cryptonote::account_keys &acc,
|
||||
const std::unordered_map<crypto::public_key, cryptonote::subaddress_index> &subaddress_map,
|
||||
const epee::span<std::optional<enote_view_incoming_scan_info_t>> enote_scan_infos_out);
|
||||
std::vector<std::optional<enote_view_incoming_scan_info_t>> view_incoming_scan_transaction(
|
||||
const cryptonote::transaction &tx,
|
||||
const cryptonote::account_keys &acc,
|
||||
|
||||
@@ -12401,7 +12401,14 @@ void wallet2::check_tx_key_helper(const cryptonote::transaction &tx, const crypt
|
||||
{
|
||||
received = 0;
|
||||
|
||||
const auto enote_scan_infos = wallet::view_incoming_scan_transaction(tx, m_account.get_keys(), m_subaddresses);
|
||||
std::vector<std::optional<wallet::enote_view_incoming_scan_info_t>> enote_scan_infos(tx.vout.size());
|
||||
wallet::view_incoming_scan_transaction(tx,
|
||||
{&derivation, 1},
|
||||
additional_derivations,
|
||||
m_account.get_keys(),
|
||||
{{address.m_spend_public_key, {}}}, // use a fake subaddress map with just the provided address in it
|
||||
epee::to_mut_span(enote_scan_infos));
|
||||
|
||||
for (const auto &enote_scan_info : enote_scan_infos)
|
||||
if (enote_scan_info && enote_scan_info->address_spend_pubkey == address.m_spend_public_key)
|
||||
received += enote_scan_info->amount; //! @TODO: check overflow
|
||||
|
||||
@@ -95,6 +95,7 @@ set(unit_tests_sources
|
||||
test_peerlist.cpp
|
||||
test_protocol_pack.cpp
|
||||
threadpool.cpp
|
||||
tx_construction_helpers.cpp
|
||||
tx_proof.cpp
|
||||
hardfork.cpp
|
||||
unbound.cpp
|
||||
|
||||
@@ -32,12 +32,9 @@
|
||||
#include "fake_pruned_blockchain.h"
|
||||
|
||||
//local headers
|
||||
#include "carrot_core/device_ram_borrowed.h"
|
||||
#include "carrot_core/output_set_finalization.h"
|
||||
#include "carrot_impl/carrot_tx_builder_utils.h"
|
||||
#include "carrot_impl/carrot_tx_format_utils.h"
|
||||
#include "common/container_helpers.h"
|
||||
#include "crypto/generators.h"
|
||||
#include "ringct/rctOps.h"
|
||||
#include "tx_construction_helpers.h"
|
||||
|
||||
#undef MONERO_DEFAULT_LOG_CATEGORY
|
||||
#define MONERO_DEFAULT_LOG_CATEGORY "unit_tests.fake_pruned_bc"
|
||||
@@ -45,318 +42,122 @@
|
||||
namespace mock
|
||||
{
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
bool construct_miner_tx_fake_reward_1out(const size_t height,
|
||||
const rct::xmr_amount reward,
|
||||
const cryptonote::account_public_address &miner_address,
|
||||
cryptonote::transaction& tx,
|
||||
const uint8_t hf_version)
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
static constexpr std::size_t selene_chunk_width = fcmp_pp::curve_trees::SELENE_CHUNK_WIDTH;
|
||||
static constexpr std::size_t helios_chunk_width = fcmp_pp::curve_trees::HELIOS_CHUNK_WIDTH;
|
||||
const auto curve_trees = fcmp_pp::curve_trees::curve_trees_v1(selene_chunk_width, helios_chunk_width);
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
template <class C>
|
||||
static bool compare_curve_point(const typename C::Point &p1, const typename C::Point &p2)
|
||||
{
|
||||
const bool is_carrot = hf_version >= HF_VERSION_CARROT;
|
||||
if (is_carrot)
|
||||
const crypto::ec_point p1_compressed = C().to_bytes(p1);
|
||||
const crypto::ec_point p2_compressed = C().to_bytes(p2);
|
||||
return p1_compressed == p2_compressed;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
template <class C>
|
||||
static bool compare_curve_layer(const std::vector<typename C::Point> &p1s,
|
||||
const std::vector<typename C::Point> &p2s)
|
||||
{
|
||||
if (p1s.size() != p2s.size())
|
||||
return false;
|
||||
for (size_t i = 0; i < p1s.size(); ++i)
|
||||
{
|
||||
carrot::CarrotDestinationV1 miner_destination;
|
||||
make_carrot_main_address_v1(miner_address.m_spend_public_key,
|
||||
miner_address.m_view_public_key,
|
||||
miner_destination);
|
||||
|
||||
const carrot::CarrotPaymentProposalV1 normal_payment_proposal{
|
||||
.destination = miner_destination,
|
||||
.amount = reward,
|
||||
.randomness = carrot::gen_janus_anchor()
|
||||
};
|
||||
|
||||
std::vector<carrot::CarrotCoinbaseEnoteV1> coinbase_enotes;
|
||||
carrot::get_coinbase_output_enotes({normal_payment_proposal},
|
||||
height,
|
||||
coinbase_enotes);
|
||||
|
||||
tx = carrot::store_carrot_to_coinbase_transaction_v1(coinbase_enotes);
|
||||
}
|
||||
else // !is_carrot
|
||||
{
|
||||
tx.vin.clear();
|
||||
tx.vout.clear();
|
||||
tx.extra.clear();
|
||||
|
||||
cryptonote::txin_gen in;
|
||||
in.height = height;
|
||||
|
||||
cryptonote::keypair txkey = cryptonote::keypair::generate(hw::get_device("default"));
|
||||
cryptonote::add_tx_pub_key_to_extra(tx, txkey.pub);
|
||||
if (!cryptonote::sort_tx_extra(tx.extra, tx.extra))
|
||||
if (!compare_curve_point<C>(p1s.at(i), p2s.at(i)))
|
||||
return false;
|
||||
|
||||
crypto::key_derivation derivation;
|
||||
crypto::public_key out_eph_public_key;
|
||||
bool r = crypto::generate_key_derivation(miner_address.m_view_public_key, txkey.sec, derivation);
|
||||
CHECK_AND_ASSERT_MES(r, false,
|
||||
"while creating outs: failed to generate_key_derivation(" << miner_address.m_view_public_key << ", "
|
||||
<< crypto::secret_key_explicit_print_ref{txkey.sec} << ")");
|
||||
|
||||
const size_t local_output_index = 0;
|
||||
r = crypto::derive_public_key(derivation, local_output_index, miner_address.m_spend_public_key, out_eph_public_key);
|
||||
CHECK_AND_ASSERT_MES(r, false,
|
||||
"while creating outs: failed to derive_public_key(" << derivation << ", "
|
||||
<< local_output_index << ", "<< miner_address.m_spend_public_key << ")");
|
||||
|
||||
const bool use_view_tags = hf_version >= HF_VERSION_VIEW_TAGS;
|
||||
crypto::view_tag view_tag;
|
||||
if (use_view_tags)
|
||||
crypto::derive_view_tag(derivation, local_output_index, view_tag);
|
||||
|
||||
cryptonote::tx_out out;
|
||||
cryptonote::set_tx_out(reward, out_eph_public_key, use_view_tags, view_tag, out);
|
||||
|
||||
tx.vout.push_back(out);
|
||||
|
||||
if (hf_version >= 4)
|
||||
tx.version = 2;
|
||||
else
|
||||
tx.version = 1;
|
||||
|
||||
tx.unlock_time = height + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW;
|
||||
tx.vin.push_back(in);
|
||||
|
||||
tx.invalidate_hashes();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
cryptonote::transaction construct_miner_tx_fake_reward_1out(const size_t height,
|
||||
const rct::xmr_amount reward,
|
||||
const cryptonote::account_public_address &miner_address,
|
||||
const uint8_t hf_version)
|
||||
{
|
||||
cryptonote::transaction tx;
|
||||
const bool r = construct_miner_tx_fake_reward_1out(height, reward, miner_address, tx, hf_version);
|
||||
CHECK_AND_ASSERT_THROW_MES(r, "failed to construct miner tx");
|
||||
return tx;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
cryptonote::tx_source_entry gen_tx_source_entry_fake_members(
|
||||
const stripped_down_tx_source_entry_t &in,
|
||||
const size_t mixin,
|
||||
const uint64_t max_global_output_index)
|
||||
template <class C>
|
||||
static bool compare_curve_chunks(const std::vector<std::vector<typename C::Point>> &chunks1,
|
||||
const std::vector<std::vector<typename C::Point>> &chunks2)
|
||||
{
|
||||
const size_t ring_size = mixin + 1;
|
||||
const bool is_rct = in.mask == rct::I;
|
||||
|
||||
CHECK_AND_ASSERT_THROW_MES(in.global_output_index <= max_global_output_index,
|
||||
"real global output index too low");
|
||||
CHECK_AND_ASSERT_THROW_MES(max_global_output_index >= ring_size,
|
||||
"not enough global output indices for mixin");
|
||||
|
||||
cryptonote::tx_source_entry res;
|
||||
|
||||
// populate ring with fake data
|
||||
std::unordered_set<uint64_t> used_indices;
|
||||
res.outputs.reserve(mixin + 1);
|
||||
res.outputs.push_back(
|
||||
{in.global_output_index,
|
||||
{ rct::pk2rct(in.onetime_address), rct::commit(in.amount, in.mask) }});
|
||||
used_indices.insert(in.global_output_index);
|
||||
while (res.outputs.size() < ring_size)
|
||||
if (chunks1.size() != chunks2.size())
|
||||
return false;
|
||||
for (size_t i = 0; i < chunks1.size(); ++i)
|
||||
{
|
||||
const uint64_t global_output_index = crypto::rand_range<uint64_t>(0, max_global_output_index);
|
||||
if (used_indices.count(global_output_index))
|
||||
continue;
|
||||
used_indices.insert(global_output_index);
|
||||
const rct::ctkey output_pair{rct::pkGen(),
|
||||
is_rct ? rct::pkGen() : rct::zeroCommitVartime(in.amount)};
|
||||
res.outputs.push_back({global_output_index, output_pair});
|
||||
if (!compare_curve_layer<C>(chunks1.at(i), chunks2.at(i)))
|
||||
return false;
|
||||
}
|
||||
// sort by index
|
||||
std::sort(res.outputs.begin(), res.outputs.end(), [](const auto &a, const auto &b) -> bool {
|
||||
return a.first < b.first;
|
||||
});
|
||||
|
||||
// real_output
|
||||
res.real_output = 0;
|
||||
while (res.outputs.at(res.real_output).second.dest != in.onetime_address)
|
||||
++res.real_output;
|
||||
|
||||
// copy from in
|
||||
res.real_out_tx_key = in.real_out_tx_key;
|
||||
res.real_out_additional_tx_keys = in.real_out_additional_tx_keys;
|
||||
res.real_output_in_tx_index = in.local_output_index;
|
||||
res.amount = in.amount;
|
||||
res.rct = is_rct;
|
||||
res.mask = in.mask;
|
||||
|
||||
return res;
|
||||
return true;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
cryptonote::transaction construct_pre_carrot_tx_with_fake_inputs(
|
||||
const cryptonote::account_keys &sender_account_keys,
|
||||
const std::unordered_map<crypto::public_key, cryptonote::subaddress_index> &subaddresses,
|
||||
std::vector<stripped_down_tx_source_entry_t> &&stripped_sources,
|
||||
std::vector<cryptonote::tx_destination_entry> &destinations,
|
||||
const boost::optional<cryptonote::account_public_address> &change_addr,
|
||||
const rct::xmr_amount fee,
|
||||
const uint8_t hf_version,
|
||||
const bool sweep_unmixable_override)
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
static bool compare_output_tuple(const fcmp_pp::curve_trees::OutputTuple &tup1,
|
||||
const fcmp_pp::curve_trees::OutputTuple &tup2)
|
||||
{
|
||||
// derive config from hf version
|
||||
const bool rct = hf_version >= HF_VERSION_DYNAMIC_FEE && !sweep_unmixable_override;
|
||||
rct::RCTConfig rct_config;
|
||||
switch (hf_version)
|
||||
{
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
case HF_VERSION_DYNAMIC_FEE:
|
||||
case 5:
|
||||
case HF_VERSION_MIN_MIXIN_4:
|
||||
case 7:
|
||||
rct_config = { rct::RangeProofBorromean, 0 };
|
||||
break;
|
||||
case HF_VERSION_PER_BYTE_FEE:
|
||||
case 9:
|
||||
rct_config = { rct::RangeProofPaddedBulletproof, 1 };
|
||||
break;
|
||||
case HF_VERSION_SMALLER_BP:
|
||||
case 11:
|
||||
case HF_VERSION_MIN_2_OUTPUTS:
|
||||
rct_config = { rct::RangeProofPaddedBulletproof, 2 };
|
||||
break;
|
||||
case HF_VERSION_CLSAG:
|
||||
case 14:
|
||||
rct_config = { rct::RangeProofPaddedBulletproof, 3 };
|
||||
break;
|
||||
case HF_VERSION_BULLETPROOF_PLUS:
|
||||
case 16:
|
||||
rct_config = { rct::RangeProofPaddedBulletproof, 4 };
|
||||
break;
|
||||
default:
|
||||
ASSERT_MES_AND_THROW("unrecognized hf version");
|
||||
}
|
||||
const bool use_view_tags = hf_version >= HF_VERSION_VIEW_TAGS;
|
||||
const size_t mixin = 15;
|
||||
const uint64_t max_global_output_index = 1000000;
|
||||
|
||||
// count missing money and balance if necessary
|
||||
boost::multiprecision::int128_t missing_money = fee;
|
||||
for (const cryptonote::tx_destination_entry &destination : destinations)
|
||||
missing_money += destination.amount;
|
||||
for (const stripped_down_tx_source_entry_t &stripped_source : stripped_sources)
|
||||
missing_money -= stripped_source.amount;
|
||||
if (missing_money > 0)
|
||||
{
|
||||
const rct::xmr_amount missing_money64 = boost::numeric_cast<rct::xmr_amount>(missing_money);
|
||||
|
||||
hw::device &hwdev = hw::get_device("default");
|
||||
cryptonote::keypair main_tx_keypair = cryptonote::keypair::generate(hwdev);
|
||||
|
||||
std::vector<crypto::public_key> dummy_additional_tx_public_keys;
|
||||
std::vector<rct::key> amount_keys;
|
||||
crypto::public_key input_onetime_address;
|
||||
crypto::view_tag vt;
|
||||
const bool r = hwdev.generate_output_ephemeral_keys(rct ? 2 : 1,
|
||||
cryptonote::account_keys(),
|
||||
main_tx_keypair.pub,
|
||||
main_tx_keypair.sec,
|
||||
cryptonote::tx_destination_entry(missing_money64, sender_account_keys.m_account_address, false),
|
||||
boost::none,
|
||||
/*output_index=*/0,
|
||||
/*need_additional_txkeys=*/false,
|
||||
/*additional_tx_keys=*/{},
|
||||
dummy_additional_tx_public_keys,
|
||||
amount_keys,
|
||||
input_onetime_address,
|
||||
use_view_tags,
|
||||
vt);
|
||||
CHECK_AND_ASSERT_THROW_MES(r, "failed to generate balancing input");
|
||||
|
||||
const stripped_down_tx_source_entry_t balancing_in{
|
||||
.global_output_index = crypto::rand_range<uint64_t>(0, max_global_output_index),
|
||||
.onetime_address = input_onetime_address,
|
||||
.real_out_tx_key = main_tx_keypair.pub,
|
||||
.real_out_additional_tx_keys = {},
|
||||
.local_output_index = 0,
|
||||
.amount = missing_money64,
|
||||
.mask = rct ? rct::genCommitmentMask(amount_keys.at(0)) : rct::I
|
||||
};
|
||||
|
||||
stripped_sources.push_back(balancing_in);
|
||||
}
|
||||
|
||||
// populate random sources
|
||||
std::vector<cryptonote::tx_source_entry> sources;
|
||||
sources.reserve(stripped_sources.size());
|
||||
for (const auto &stripped_source : stripped_sources)
|
||||
sources.push_back(gen_tx_source_entry_fake_members(stripped_source,
|
||||
mixin,
|
||||
max_global_output_index));
|
||||
|
||||
// construct tx
|
||||
cryptonote::transaction tx;
|
||||
crypto::secret_key tx_key;
|
||||
std::vector<crypto::secret_key> additional_tx_keys;
|
||||
fcmp_pp::ProofParams dummy_fcmp_params;
|
||||
const bool r = cryptonote::construct_tx_and_get_tx_key(
|
||||
sender_account_keys,
|
||||
subaddresses,
|
||||
sources,
|
||||
destinations,
|
||||
change_addr,
|
||||
/*extra=*/{},
|
||||
tx,
|
||||
tx_key,
|
||||
additional_tx_keys,
|
||||
dummy_fcmp_params,
|
||||
rct,
|
||||
rct_config,
|
||||
use_view_tags);
|
||||
CHECK_AND_ASSERT_THROW_MES(r, "failed to construct tx");
|
||||
return tx;
|
||||
return tup1.O == tup2.O && tup1.I == tup2.I && tup1.C == tup2.C;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
cryptonote::transaction construct_carrot_pruned_transaction_fake_inputs(
|
||||
const std::vector<carrot::CarrotPaymentProposalV1> &normal_payment_proposals,
|
||||
const std::vector<carrot::CarrotPaymentProposalVerifiableSelfSendV1> &selfsend_payment_proposals,
|
||||
const cryptonote::account_keys &acc_keys)
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
static bool compare_leaf_layer(const std::vector<fcmp_pp::curve_trees::OutputTuple> &leaves1,
|
||||
const std::vector<fcmp_pp::curve_trees::OutputTuple> &leaves2)
|
||||
{
|
||||
carrot::select_inputs_func_t select_inputs = [](
|
||||
const boost::multiprecision::int128_t &nominal_output_sum,
|
||||
const std::map<std::size_t, rct::xmr_amount> &fee_by_input_count,
|
||||
const std::size_t,
|
||||
const std::size_t,
|
||||
std::vector<carrot::CarrotSelectedInput> &select_inputs_out
|
||||
)
|
||||
MDEBUG("compare_leaf_layer: " << leaves1.size() << " vs " << leaves2.size());
|
||||
if (leaves1.size() != leaves2.size())
|
||||
return false;
|
||||
bool r = true;
|
||||
for (size_t i = 0; i < leaves1.size(); ++i)
|
||||
{
|
||||
const auto in_amount = boost::numeric_cast<rct::xmr_amount>(nominal_output_sum + fee_by_input_count.at(1));
|
||||
const crypto::key_image ki = rct::rct2ki(rct::pkGen());
|
||||
select_inputs_out = {carrot::CarrotSelectedInput{.amount = in_amount, .key_image = ki}};
|
||||
};
|
||||
|
||||
const carrot::view_incoming_key_ram_borrowed_device k_view_dev(acc_keys.m_view_secret_key);
|
||||
|
||||
carrot::CarrotTransactionProposalV1 tx_proposal;
|
||||
carrot::make_carrot_transaction_proposal_v1_transfer(
|
||||
normal_payment_proposals,
|
||||
selfsend_payment_proposals,
|
||||
fake_fee_per_weight,
|
||||
/*extra=*/{},
|
||||
std::move(select_inputs),
|
||||
/*s_view_balance_dev=*/nullptr,
|
||||
&k_view_dev,
|
||||
acc_keys.m_account_address.m_spend_public_key,
|
||||
tx_proposal);
|
||||
|
||||
cryptonote::transaction tx;
|
||||
carrot::make_pruned_transaction_from_carrot_proposal_v1(tx_proposal,
|
||||
/*s_view_balance_dev=*/nullptr,
|
||||
&k_view_dev,
|
||||
tx);
|
||||
|
||||
return tx;
|
||||
MDEBUG(" Leaf O: " << leaves1.at(i).O << " vs " << leaves2.at(i).O);
|
||||
if (!compare_output_tuple(leaves1.at(i), leaves2.at(i)))
|
||||
r = false;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
const cryptonote::account_public_address null_addr{
|
||||
.m_spend_public_key = crypto::get_G(),
|
||||
.m_view_public_key = crypto::get_G()
|
||||
};
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
static bool compare_paths_between_tree_cache_and_global_tree(
|
||||
const fcmp_pp::curve_trees::TreeCacheV1 &tree_cache,
|
||||
const CurveTreesGlobalTree &global_tree,
|
||||
const std::vector<fcmp_pp::curve_trees::OutputContext> &leaves)
|
||||
{
|
||||
// this check compares the paths returned by tree_cache and global_tree against each other for a
|
||||
// given set of leaves
|
||||
|
||||
using namespace fcmp_pp::curve_trees;
|
||||
|
||||
CHECK_AND_ASSERT_MES(tree_cache.get_n_leaf_tuples() == global_tree.get_n_leaf_tuples(), false,
|
||||
"mismatch in number of leaf tuples");
|
||||
for (const OutputContext &leaf : leaves)
|
||||
{
|
||||
CurveTreesV1::Path path_in_cache;
|
||||
CHECK_AND_ASSERT_THROW_MES(tree_cache.get_output_path(leaf.output_pair, path_in_cache),
|
||||
"could not get path from tree cache");
|
||||
const CurveTreesV1::Path path_in_global =
|
||||
global_tree.get_path_at_leaf_idx(leaf.output_id);
|
||||
CHECK_AND_ASSERT_MES(compare_leaf_layer(path_in_cache.leaves, path_in_global.leaves), false,
|
||||
"paths' leaves are not equal");
|
||||
CHECK_AND_ASSERT_MES(compare_curve_chunks<Selene>(path_in_cache.c1_layers, path_in_global.c1_layers),
|
||||
false,
|
||||
"paths' c1 layers are not equal");
|
||||
CHECK_AND_ASSERT_MES(compare_curve_chunks<Helios>(path_in_cache.c2_layers, path_in_global.c2_layers),
|
||||
false,
|
||||
"paths' c2 layers are not equal");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
static bool is_valid_output_pair_for_tree(const fcmp_pp::curve_trees::OutputPair &p)
|
||||
{
|
||||
return rct::isInMainSubgroup(rct::pk2rct(p.output_pubkey)) && rct::isInMainSubgroup(p.commitment);
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
fake_pruned_blockchain::fake_pruned_blockchain(const uint64_t start_block_index,
|
||||
const cryptonote::network_type nettype):
|
||||
m_start_block_index(start_block_index),
|
||||
m_nettype(nettype),
|
||||
m_num_outputs(0),
|
||||
m_global_curve_tree(*curve_trees)
|
||||
{
|
||||
add_starting_block();
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
void fake_pruned_blockchain::add_block(const uint8_t hf_version,
|
||||
std::vector<cryptonote::transaction> &&txs,
|
||||
|
||||
@@ -30,65 +30,14 @@
|
||||
|
||||
#define IN_UNIT_TESTS
|
||||
|
||||
#include "carrot_impl/carrot_tx_builder_types.h"
|
||||
#include "wallet/wallet2.h"
|
||||
|
||||
namespace mock
|
||||
{
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
bool construct_miner_tx_fake_reward_1out(const size_t height,
|
||||
const rct::xmr_amount reward,
|
||||
const cryptonote::account_public_address &miner_address,
|
||||
cryptonote::transaction& tx,
|
||||
const uint8_t hf_version);
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
cryptonote::transaction construct_miner_tx_fake_reward_1out(const size_t height,
|
||||
const rct::xmr_amount reward,
|
||||
const cryptonote::account_public_address &miner_address,
|
||||
const uint8_t hf_version);
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
struct stripped_down_tx_source_entry_t
|
||||
{
|
||||
uint64_t global_output_index;
|
||||
crypto::public_key onetime_address;
|
||||
crypto::public_key real_out_tx_key;
|
||||
std::vector<crypto::public_key> real_out_additional_tx_keys;
|
||||
size_t local_output_index;
|
||||
rct::xmr_amount amount;
|
||||
rct::key mask;
|
||||
};
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
cryptonote::tx_source_entry gen_tx_source_entry_fake_members(
|
||||
const stripped_down_tx_source_entry_t &in,
|
||||
const size_t mixin,
|
||||
const uint64_t max_global_output_index);
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
cryptonote::transaction construct_pre_carrot_tx_with_fake_inputs(
|
||||
const cryptonote::account_keys &sender_account_keys,
|
||||
const std::unordered_map<crypto::public_key, cryptonote::subaddress_index> &subaddresses,
|
||||
std::vector<stripped_down_tx_source_entry_t> &&stripped_sources,
|
||||
std::vector<cryptonote::tx_destination_entry> &destinations,
|
||||
const boost::optional<cryptonote::account_public_address> &change_addr,
|
||||
const rct::xmr_amount fee,
|
||||
const uint8_t hf_version,
|
||||
const bool sweep_unmixable_override = false);
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
static constexpr rct::xmr_amount fake_fee_per_weight = 2023;
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
cryptonote::transaction construct_carrot_pruned_transaction_fake_inputs(
|
||||
const std::vector<carrot::CarrotPaymentProposalV1> &normal_payment_proposals,
|
||||
const std::vector<carrot::CarrotPaymentProposalVerifiableSelfSendV1> &selfsend_payment_proposals,
|
||||
const cryptonote::account_keys &acc_keys);
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
extern const cryptonote::account_public_address null_addr;
|
||||
using fcmp_generic_object_t = std::unique_ptr<void, decltype(&free)>;
|
||||
static inline fcmp_generic_object_t make_fcmp_generic_object(void *p) { return fcmp_generic_object_t(p, &free); }
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
class fake_pruned_blockchain
|
||||
|
||||
@@ -0,0 +1,415 @@
|
||||
// Copyright (c) 2025, 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 "tx_construction_helpers.h"
|
||||
|
||||
//local headers
|
||||
#include "carrot_core/device_ram_borrowed.h"
|
||||
#include "carrot_core/output_set_finalization.h"
|
||||
#include "carrot_impl/carrot_tx_builder_utils.h"
|
||||
#include "carrot_impl/carrot_tx_format_utils.h"
|
||||
#include "crypto/generators.h"
|
||||
|
||||
#undef MONERO_DEFAULT_LOG_CATEGORY
|
||||
#define MONERO_DEFAULT_LOG_CATEGORY "unit_tests.tx_construction_helpers"
|
||||
|
||||
namespace mock
|
||||
{
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
bool construct_miner_tx_fake_reward_1out(const size_t height,
|
||||
const rct::xmr_amount reward,
|
||||
const cryptonote::account_public_address &miner_address,
|
||||
cryptonote::transaction& tx,
|
||||
const uint8_t hf_version,
|
||||
const size_t num_tx_outputs)
|
||||
{
|
||||
const bool is_carrot = hf_version >= HF_VERSION_CARROT;
|
||||
if (is_carrot)
|
||||
{
|
||||
carrot::CarrotDestinationV1 miner_destination;
|
||||
make_carrot_main_address_v1(miner_address.m_spend_public_key,
|
||||
miner_address.m_view_public_key,
|
||||
miner_destination);
|
||||
|
||||
std::vector<carrot::CarrotPaymentProposalV1> normal_payment_proposals;
|
||||
normal_payment_proposals.reserve(num_tx_outputs);
|
||||
for (size_t i = 0; i < num_tx_outputs; ++i)
|
||||
{
|
||||
normal_payment_proposals.push_back(carrot::CarrotPaymentProposalV1{
|
||||
.destination = miner_destination,
|
||||
.amount = reward,
|
||||
.randomness = carrot::gen_janus_anchor()
|
||||
});
|
||||
}
|
||||
|
||||
std::vector<carrot::CarrotCoinbaseEnoteV1> coinbase_enotes;
|
||||
carrot::get_coinbase_output_enotes(normal_payment_proposals,
|
||||
height,
|
||||
coinbase_enotes);
|
||||
|
||||
tx = carrot::store_carrot_to_coinbase_transaction_v1(coinbase_enotes);
|
||||
}
|
||||
else // !is_carrot
|
||||
{
|
||||
tx.vin.clear();
|
||||
tx.vout.clear();
|
||||
tx.extra.clear();
|
||||
|
||||
cryptonote::txin_gen in;
|
||||
in.height = height;
|
||||
|
||||
if (hf_version >= 4)
|
||||
tx.version = 2;
|
||||
else
|
||||
tx.version = 1;
|
||||
|
||||
tx.unlock_time = height + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW;
|
||||
tx.vin.push_back(in);
|
||||
|
||||
for (size_t i = 0; i < num_tx_outputs; ++i)
|
||||
{
|
||||
cryptonote::keypair txkey = cryptonote::keypair::generate(hw::get_device("default"));
|
||||
cryptonote::add_tx_pub_key_to_extra(tx, txkey.pub);
|
||||
if (!cryptonote::sort_tx_extra(tx.extra, tx.extra))
|
||||
return false;
|
||||
|
||||
crypto::key_derivation derivation;
|
||||
crypto::public_key out_eph_public_key;
|
||||
bool r = crypto::generate_key_derivation(miner_address.m_view_public_key, txkey.sec, derivation);
|
||||
CHECK_AND_ASSERT_MES(r, false,
|
||||
"while creating outs: failed to generate_key_derivation(" << miner_address.m_view_public_key << ", "
|
||||
<< crypto::secret_key_explicit_print_ref{txkey.sec} << ")");
|
||||
|
||||
const size_t local_output_index = 0;
|
||||
r = crypto::derive_public_key(derivation, local_output_index, miner_address.m_spend_public_key, out_eph_public_key);
|
||||
CHECK_AND_ASSERT_MES(r, false,
|
||||
"while creating outs: failed to derive_public_key(" << derivation << ", "
|
||||
<< local_output_index << ", "<< miner_address.m_spend_public_key << ")");
|
||||
|
||||
const bool use_view_tags = hf_version >= HF_VERSION_VIEW_TAGS;
|
||||
crypto::view_tag view_tag;
|
||||
if (use_view_tags)
|
||||
crypto::derive_view_tag(derivation, local_output_index, view_tag);
|
||||
|
||||
cryptonote::tx_out out;
|
||||
cryptonote::set_tx_out(reward, out_eph_public_key, use_view_tags, view_tag, out);
|
||||
|
||||
tx.vout.push_back(out);
|
||||
}
|
||||
|
||||
tx.invalidate_hashes();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
cryptonote::transaction construct_miner_tx_fake_reward_1out(const size_t height,
|
||||
const rct::xmr_amount reward,
|
||||
const cryptonote::account_public_address &miner_address,
|
||||
const uint8_t hf_version,
|
||||
const size_t num_tx_outputs)
|
||||
{
|
||||
cryptonote::transaction tx;
|
||||
const bool r = construct_miner_tx_fake_reward_1out(height, reward, miner_address, tx, hf_version, num_tx_outputs);
|
||||
CHECK_AND_ASSERT_THROW_MES(r, "failed to construct miner tx");
|
||||
return tx;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
cryptonote::tx_source_entry gen_tx_source_entry_fake_members(
|
||||
const stripped_down_tx_source_entry_t &in,
|
||||
const size_t mixin,
|
||||
const uint64_t max_global_output_index)
|
||||
{
|
||||
const size_t ring_size = mixin + 1;
|
||||
const bool is_rct = in.mask == rct::I;
|
||||
|
||||
CHECK_AND_ASSERT_THROW_MES(in.global_output_index <= max_global_output_index,
|
||||
"real global output index too low");
|
||||
CHECK_AND_ASSERT_THROW_MES(max_global_output_index >= ring_size,
|
||||
"not enough global output indices for mixin");
|
||||
|
||||
cryptonote::tx_source_entry res;
|
||||
|
||||
// populate ring with fake data
|
||||
std::unordered_set<uint64_t> used_indices;
|
||||
res.outputs.reserve(mixin + 1);
|
||||
res.outputs.push_back(
|
||||
{in.global_output_index,
|
||||
{ rct::pk2rct(in.onetime_address), rct::commit(in.amount, in.mask) }});
|
||||
used_indices.insert(in.global_output_index);
|
||||
while (res.outputs.size() < ring_size)
|
||||
{
|
||||
const uint64_t global_output_index = crypto::rand_range<uint64_t>(0, max_global_output_index);
|
||||
if (used_indices.count(global_output_index))
|
||||
continue;
|
||||
used_indices.insert(global_output_index);
|
||||
const rct::ctkey output_pair{rct::pkGen(),
|
||||
is_rct ? rct::pkGen() : rct::zeroCommitVartime(in.amount)};
|
||||
res.outputs.push_back({global_output_index, output_pair});
|
||||
}
|
||||
// sort by index
|
||||
std::sort(res.outputs.begin(), res.outputs.end(), [](const auto &a, const auto &b) -> bool {
|
||||
return a.first < b.first;
|
||||
});
|
||||
|
||||
// real_output
|
||||
res.real_output = 0;
|
||||
while (res.outputs.at(res.real_output).second.dest != in.onetime_address)
|
||||
++res.real_output;
|
||||
|
||||
// copy from in
|
||||
res.real_out_tx_key = in.real_out_tx_key;
|
||||
res.real_out_additional_tx_keys = in.real_out_additional_tx_keys;
|
||||
res.real_output_in_tx_index = in.local_output_index;
|
||||
res.amount = in.amount;
|
||||
res.rct = is_rct;
|
||||
res.mask = in.mask;
|
||||
|
||||
return res;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
cryptonote::transaction construct_pre_carrot_tx_with_fake_inputs(
|
||||
const cryptonote::account_keys &sender_account_keys,
|
||||
const std::unordered_map<crypto::public_key, cryptonote::subaddress_index> &sender_subaddress_map,
|
||||
std::vector<stripped_down_tx_source_entry_t> &&stripped_sources,
|
||||
std::vector<cryptonote::tx_destination_entry> &destinations,
|
||||
const boost::optional<cryptonote::account_public_address> &change_addr,
|
||||
const rct::xmr_amount fee,
|
||||
const uint8_t hf_version,
|
||||
crypto::secret_key &main_tx_privkey_out,
|
||||
std::vector<crypto::secret_key> &additional_tx_privkeys_out,
|
||||
const bool sweep_unmixable_override)
|
||||
{
|
||||
// derive config from hf version
|
||||
const bool rct = hf_version >= HF_VERSION_DYNAMIC_FEE && !sweep_unmixable_override;
|
||||
rct::RCTConfig rct_config;
|
||||
switch (hf_version)
|
||||
{
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
case HF_VERSION_DYNAMIC_FEE:
|
||||
case 5:
|
||||
case HF_VERSION_MIN_MIXIN_4:
|
||||
case 7:
|
||||
rct_config = { rct::RangeProofBorromean, 0 };
|
||||
break;
|
||||
case HF_VERSION_PER_BYTE_FEE:
|
||||
case 9:
|
||||
rct_config = { rct::RangeProofPaddedBulletproof, 1 };
|
||||
break;
|
||||
case HF_VERSION_SMALLER_BP:
|
||||
case 11:
|
||||
case HF_VERSION_MIN_2_OUTPUTS:
|
||||
rct_config = { rct::RangeProofPaddedBulletproof, 2 };
|
||||
break;
|
||||
case HF_VERSION_CLSAG:
|
||||
case 14:
|
||||
rct_config = { rct::RangeProofPaddedBulletproof, 3 };
|
||||
break;
|
||||
case HF_VERSION_BULLETPROOF_PLUS:
|
||||
case 16:
|
||||
rct_config = { rct::RangeProofPaddedBulletproof, 4 };
|
||||
break;
|
||||
default:
|
||||
ASSERT_MES_AND_THROW("unrecognized hf version");
|
||||
}
|
||||
const bool use_view_tags = hf_version >= HF_VERSION_VIEW_TAGS;
|
||||
const size_t mixin = 15;
|
||||
const uint64_t max_global_output_index = 1000000;
|
||||
|
||||
// count missing money and balance if necessary
|
||||
boost::multiprecision::int128_t missing_money = fee;
|
||||
for (const cryptonote::tx_destination_entry &destination : destinations)
|
||||
missing_money += destination.amount;
|
||||
for (const stripped_down_tx_source_entry_t &stripped_source : stripped_sources)
|
||||
missing_money -= stripped_source.amount;
|
||||
if (missing_money > 0)
|
||||
{
|
||||
const rct::xmr_amount missing_money64 = boost::numeric_cast<rct::xmr_amount>(missing_money);
|
||||
|
||||
hw::device &hwdev = hw::get_device("default");
|
||||
cryptonote::keypair main_tx_keypair = cryptonote::keypair::generate(hwdev);
|
||||
|
||||
std::vector<crypto::public_key> dummy_additional_tx_public_keys;
|
||||
std::vector<rct::key> amount_keys;
|
||||
crypto::public_key input_onetime_address;
|
||||
crypto::view_tag vt;
|
||||
const bool r = hwdev.generate_output_ephemeral_keys(rct ? 2 : 1,
|
||||
cryptonote::account_keys(),
|
||||
main_tx_keypair.pub,
|
||||
main_tx_keypair.sec,
|
||||
cryptonote::tx_destination_entry(missing_money64, sender_account_keys.m_account_address, false),
|
||||
boost::none,
|
||||
/*output_index=*/0,
|
||||
/*need_additional_txkeys=*/false,
|
||||
/*additional_tx_keys=*/{},
|
||||
dummy_additional_tx_public_keys,
|
||||
amount_keys,
|
||||
input_onetime_address,
|
||||
use_view_tags,
|
||||
vt);
|
||||
CHECK_AND_ASSERT_THROW_MES(r, "failed to generate balancing input");
|
||||
|
||||
const stripped_down_tx_source_entry_t balancing_in{
|
||||
.global_output_index = crypto::rand_range<uint64_t>(0, max_global_output_index),
|
||||
.onetime_address = input_onetime_address,
|
||||
.real_out_tx_key = main_tx_keypair.pub,
|
||||
.real_out_additional_tx_keys = {},
|
||||
.local_output_index = 0,
|
||||
.amount = missing_money64,
|
||||
.mask = rct ? rct::genCommitmentMask(amount_keys.at(0)) : rct::I
|
||||
};
|
||||
|
||||
stripped_sources.push_back(balancing_in);
|
||||
}
|
||||
|
||||
// populate random sources
|
||||
std::vector<cryptonote::tx_source_entry> sources;
|
||||
sources.reserve(stripped_sources.size());
|
||||
for (const auto &stripped_source : stripped_sources)
|
||||
sources.push_back(gen_tx_source_entry_fake_members(stripped_source,
|
||||
mixin,
|
||||
max_global_output_index));
|
||||
|
||||
// construct tx
|
||||
cryptonote::transaction tx;
|
||||
|
||||
fcmp_pp::ProofParams dummy_fcmp_params;
|
||||
const bool r = cryptonote::construct_tx_and_get_tx_key(
|
||||
sender_account_keys,
|
||||
sender_subaddress_map,
|
||||
sources,
|
||||
destinations,
|
||||
change_addr,
|
||||
/*extra=*/{},
|
||||
tx,
|
||||
main_tx_privkey_out,
|
||||
additional_tx_privkeys_out,
|
||||
dummy_fcmp_params,
|
||||
rct,
|
||||
rct_config,
|
||||
use_view_tags);
|
||||
CHECK_AND_ASSERT_THROW_MES(r, "failed to construct tx");
|
||||
return tx;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
cryptonote::transaction construct_pre_carrot_tx_with_fake_inputs(
|
||||
const cryptonote::account_keys &sender_account_keys,
|
||||
const std::unordered_map<crypto::public_key, cryptonote::subaddress_index> &sender_subaddress_map,
|
||||
std::vector<stripped_down_tx_source_entry_t> &&stripped_sources,
|
||||
std::vector<cryptonote::tx_destination_entry> &destinations,
|
||||
const boost::optional<cryptonote::account_public_address> &change_addr,
|
||||
const rct::xmr_amount fee,
|
||||
const uint8_t hf_version,
|
||||
const bool sweep_unmixable_override)
|
||||
{
|
||||
crypto::secret_key dummy_main_tx_privkey;
|
||||
std::vector<crypto::secret_key> dummy_additional_tx_privkeys;
|
||||
return construct_pre_carrot_tx_with_fake_inputs(sender_account_keys,
|
||||
sender_subaddress_map,
|
||||
std::forward<std::vector<stripped_down_tx_source_entry_t>>(stripped_sources),
|
||||
destinations,
|
||||
change_addr,
|
||||
fee,
|
||||
hf_version,
|
||||
dummy_main_tx_privkey,
|
||||
dummy_additional_tx_privkeys,
|
||||
sweep_unmixable_override);
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
cryptonote::transaction construct_pre_carrot_tx_with_fake_inputs(
|
||||
std::vector<cryptonote::tx_destination_entry> &destinations,
|
||||
const rct::xmr_amount fee,
|
||||
const uint8_t hf_version,
|
||||
const bool sweep_unmixable_override)
|
||||
{
|
||||
cryptonote::account_base aether_acb;
|
||||
aether_acb.generate();
|
||||
const std::unordered_map<crypto::public_key, cryptonote::subaddress_index> sender_subaddress_map = {
|
||||
{ aether_acb.get_keys().m_account_address.m_spend_public_key, {0, 0 } }
|
||||
};
|
||||
|
||||
return construct_pre_carrot_tx_with_fake_inputs(aether_acb.get_keys(),
|
||||
sender_subaddress_map,
|
||||
/*stripped_sources=*/{},
|
||||
destinations,
|
||||
/*change_addr*/boost::none,
|
||||
fee,
|
||||
hf_version,
|
||||
sweep_unmixable_override);
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
cryptonote::transaction construct_carrot_pruned_transaction_fake_inputs(
|
||||
const std::vector<carrot::CarrotPaymentProposalV1> &normal_payment_proposals,
|
||||
const std::vector<carrot::CarrotPaymentProposalVerifiableSelfSendV1> &selfsend_payment_proposals,
|
||||
const cryptonote::account_keys &acc_keys)
|
||||
{
|
||||
carrot::select_inputs_func_t select_inputs = [](
|
||||
const boost::multiprecision::int128_t &nominal_output_sum,
|
||||
const std::map<std::size_t, rct::xmr_amount> &fee_by_input_count,
|
||||
const std::size_t,
|
||||
const std::size_t,
|
||||
std::vector<carrot::CarrotSelectedInput> &select_inputs_out
|
||||
)
|
||||
{
|
||||
const auto in_amount = boost::numeric_cast<rct::xmr_amount>(nominal_output_sum + fee_by_input_count.at(1));
|
||||
const crypto::key_image ki = rct::rct2ki(rct::pkGen());
|
||||
select_inputs_out = {carrot::CarrotSelectedInput{.amount = in_amount, .key_image = ki}};
|
||||
};
|
||||
|
||||
const carrot::view_incoming_key_ram_borrowed_device k_view_dev(acc_keys.m_view_secret_key);
|
||||
|
||||
carrot::CarrotTransactionProposalV1 tx_proposal;
|
||||
carrot::make_carrot_transaction_proposal_v1_transfer(
|
||||
normal_payment_proposals,
|
||||
selfsend_payment_proposals,
|
||||
fake_fee_per_weight,
|
||||
/*extra=*/{},
|
||||
std::move(select_inputs),
|
||||
/*s_view_balance_dev=*/nullptr,
|
||||
&k_view_dev,
|
||||
acc_keys.m_account_address.m_spend_public_key,
|
||||
tx_proposal);
|
||||
|
||||
cryptonote::transaction tx;
|
||||
carrot::make_pruned_transaction_from_carrot_proposal_v1(tx_proposal,
|
||||
/*s_view_balance_dev=*/nullptr,
|
||||
&k_view_dev,
|
||||
tx);
|
||||
|
||||
return tx;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
const cryptonote::account_public_address null_addr{
|
||||
.m_spend_public_key = crypto::get_G(),
|
||||
.m_view_public_key = crypto::get_G()
|
||||
};
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
} //namespace mock
|
||||
@@ -0,0 +1,121 @@
|
||||
// Copyright (c) 2025, 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
//local headers
|
||||
#include "carrot_impl/carrot_tx_builder_types.h"
|
||||
#include "cryptonote_core/cryptonote_tx_utils.h"
|
||||
|
||||
//third party headers
|
||||
|
||||
//standard headers
|
||||
|
||||
//forward declarations
|
||||
|
||||
namespace mock
|
||||
{
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
bool construct_miner_tx_fake_reward_1out(const size_t height,
|
||||
const rct::xmr_amount reward,
|
||||
const cryptonote::account_public_address &miner_address,
|
||||
cryptonote::transaction& tx,
|
||||
const uint8_t hf_version,
|
||||
const size_t num_tx_outputs = 1);
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
cryptonote::transaction construct_miner_tx_fake_reward_1out(const size_t height,
|
||||
const rct::xmr_amount reward,
|
||||
const cryptonote::account_public_address &miner_address,
|
||||
const uint8_t hf_version,
|
||||
const size_t num_tx_outputs = 1);
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
struct stripped_down_tx_source_entry_t
|
||||
{
|
||||
uint64_t global_output_index;
|
||||
crypto::public_key onetime_address;
|
||||
crypto::public_key real_out_tx_key;
|
||||
std::vector<crypto::public_key> real_out_additional_tx_keys;
|
||||
size_t local_output_index;
|
||||
rct::xmr_amount amount;
|
||||
rct::key mask;
|
||||
};
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
cryptonote::tx_source_entry gen_tx_source_entry_fake_members(
|
||||
const stripped_down_tx_source_entry_t &in,
|
||||
const size_t mixin,
|
||||
const uint64_t max_global_output_index);
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
cryptonote::transaction construct_pre_carrot_tx_with_fake_inputs(
|
||||
const cryptonote::account_keys &sender_account_keys,
|
||||
const std::unordered_map<crypto::public_key, cryptonote::subaddress_index> &sender_subaddress_map,
|
||||
std::vector<stripped_down_tx_source_entry_t> &&stripped_sources,
|
||||
std::vector<cryptonote::tx_destination_entry> &destinations,
|
||||
const boost::optional<cryptonote::account_public_address> &change_addr,
|
||||
const rct::xmr_amount fee,
|
||||
const uint8_t hf_version,
|
||||
crypto::secret_key &main_tx_privkey_out,
|
||||
std::vector<crypto::secret_key> &additional_tx_privkeys_out,
|
||||
const bool sweep_unmixable_override = false);
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
cryptonote::transaction construct_pre_carrot_tx_with_fake_inputs(
|
||||
const cryptonote::account_keys &sender_account_keys,
|
||||
const std::unordered_map<crypto::public_key, cryptonote::subaddress_index> &sender_subaddress_map,
|
||||
std::vector<stripped_down_tx_source_entry_t> &&stripped_sources,
|
||||
std::vector<cryptonote::tx_destination_entry> &destinations,
|
||||
const boost::optional<cryptonote::account_public_address> &change_addr,
|
||||
const rct::xmr_amount fee,
|
||||
const uint8_t hf_version,
|
||||
const bool sweep_unmixable_override = false);
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
cryptonote::transaction construct_pre_carrot_tx_with_fake_inputs(
|
||||
std::vector<cryptonote::tx_destination_entry> &destinations,
|
||||
const rct::xmr_amount fee,
|
||||
const uint8_t hf_version,
|
||||
const bool sweep_unmixable_override = false);
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
static constexpr rct::xmr_amount fake_fee_per_weight = 2023;
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
cryptonote::transaction construct_carrot_pruned_transaction_fake_inputs(
|
||||
const std::vector<carrot::CarrotPaymentProposalV1> &normal_payment_proposals,
|
||||
const std::vector<carrot::CarrotPaymentProposalVerifiableSelfSendV1> &selfsend_payment_proposals,
|
||||
const cryptonote::account_keys &acc_keys);
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
extern const cryptonote::account_public_address null_addr;
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
} //namespace mock
|
||||
@@ -31,13 +31,84 @@
|
||||
#include "carrot_impl/address_device_ram_borrowed.h"
|
||||
#include "carrot_impl/carrot_tx_builder_inputs.h"
|
||||
#include "carrot_mock_helpers.h"
|
||||
#include "cryptonote_basic/cryptonote_basic_impl.h"
|
||||
#include "fake_pruned_blockchain.h"
|
||||
#include "fcmp_pp/prove.h"
|
||||
#include "tx_construction_helpers.h"
|
||||
#include "wallet/tx_builder.h"
|
||||
|
||||
#undef MONERO_DEFAULT_LOG_CATEGORY
|
||||
#define MONERO_DEFAULT_LOG_CATEGORY "unit_tests.wallet_scanning"
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
TEST(wallet_scanning, view_scan_as_sender_mainaddr)
|
||||
{
|
||||
cryptonote::account_base aether;
|
||||
aether.generate();
|
||||
|
||||
cryptonote::account_base bob;
|
||||
bob.generate();
|
||||
const cryptonote::account_public_address bob_main_addr = bob.get_keys().m_account_address;
|
||||
const crypto::public_key bob_main_spend_pubkey = bob_main_addr.m_spend_public_key;
|
||||
|
||||
const rct::xmr_amount amount = rct::randXmrAmount(10 * COIN);
|
||||
|
||||
const rct::xmr_amount fee = 565678;
|
||||
|
||||
for (uint8_t hf_version = 1; hf_version < HF_VERSION_FCMP_PLUS_PLUS; ++hf_version)
|
||||
{
|
||||
MDEBUG("view_scan_as_sender_mainaddr: hf_version=" << static_cast<int>(hf_version));
|
||||
|
||||
std::vector<cryptonote::tx_destination_entry> destinations{
|
||||
cryptonote::tx_destination_entry(amount, bob_main_addr, false)};
|
||||
|
||||
crypto::secret_key main_tx_privkey;
|
||||
std::vector<crypto::secret_key> additional_tx_privkeys;
|
||||
const cryptonote::transaction tx = mock::construct_pre_carrot_tx_with_fake_inputs(aether.get_keys(),
|
||||
{{aether.get_keys().m_account_address.m_spend_public_key, {0, 0}}},
|
||||
{},
|
||||
destinations,
|
||||
{},
|
||||
fee,
|
||||
hf_version,
|
||||
main_tx_privkey,
|
||||
additional_tx_privkeys);
|
||||
|
||||
ASSERT_EQ(0, additional_tx_privkeys.size());
|
||||
ASSERT_GT(tx.vout.size(), 0);
|
||||
|
||||
// do K_d = 8 * r * K^j_v
|
||||
crypto::key_derivation main_derivation;
|
||||
ASSERT_TRUE(crypto::generate_key_derivation(bob_main_addr.m_view_public_key,
|
||||
main_tx_privkey,
|
||||
main_derivation));
|
||||
|
||||
aether.generate();
|
||||
|
||||
// call view_incoming_scan_transaction with no meaningful key nor subaddresses maps,
|
||||
// just with the proper ECDH
|
||||
std::vector<std::optional<tools::wallet::enote_view_incoming_scan_info_t>> enote_scan_infos(tx.vout.size());
|
||||
tools::wallet::view_incoming_scan_transaction(tx,
|
||||
{&main_derivation, 1},
|
||||
{},
|
||||
aether.get_keys(),
|
||||
{{bob_main_spend_pubkey, {}}}, // use a fake subaddress map with just the provided address in it
|
||||
epee::to_mut_span(enote_scan_infos));
|
||||
|
||||
bool matched = false;
|
||||
for (const auto &enote_scan_info : enote_scan_infos)
|
||||
{
|
||||
if (enote_scan_info)
|
||||
{
|
||||
ASSERT_FALSE(matched);
|
||||
ASSERT_EQ(amount, enote_scan_info->amount);
|
||||
ASSERT_EQ(bob_main_spend_pubkey, enote_scan_info->address_spend_pubkey);
|
||||
matched = true;
|
||||
}
|
||||
}
|
||||
ASSERT_TRUE(matched);
|
||||
}
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
TEST(wallet_scanning, positive_smallout_main_addr_all_types_outputs)
|
||||
{
|
||||
|
||||
@@ -32,6 +32,8 @@
|
||||
#include "carrot_core/config.h"
|
||||
#include "common/container_helpers.h"
|
||||
#include "ringct/rctOps.h"
|
||||
#include "ringct/rctSigs.h"
|
||||
#include "tx_construction_helpers.h"
|
||||
#include "wallet/tx_builder.h"
|
||||
|
||||
static tools::wallet2::transfer_details gen_transfer_details()
|
||||
|
||||
Reference in New Issue
Block a user