From dd39f94ca6f7f6f3ff345c59ac8b6b2e6491311e Mon Sep 17 00:00:00 2001 From: akildemir Date: Fri, 11 Jul 2025 12:08:44 +0300 Subject: [PATCH] all pre-carrot txs work; Add support for return txs post-carrot. --- src/carrot_core/output_set_finalization.cpp | 34 +- src/carrot_core/output_set_finalization.h | 3 +- src/carrot_core/payment_proposal.cpp | 56 +-- src/carrot_core/payment_proposal.h | 7 +- src/carrot_impl/account.cpp | 8 +- src/carrot_impl/account.h | 3 +- src/carrot_impl/format_utils.cpp | 15 +- src/carrot_impl/format_utils.h | 1 - src/carrot_impl/tx_builder_outputs.cpp | 2 +- src/carrot_impl/tx_proposal.h | 2 + src/carrot_impl/tx_proposal_utils.cpp | 8 + src/carrot_impl/tx_proposal_utils.h | 3 + src/simplewallet/simplewallet.cpp | 6 +- src/wallet/scanning_tools.cpp | 13 +- src/wallet/scanning_tools.h | 15 + src/wallet/tx_builder.cpp | 125 +++++-- src/wallet/tx_builder.h | 14 +- src/wallet/wallet2.cpp | 368 ++++++++----------- src/wallet/wallet2.h | 12 +- src/wallet/wallet_rpc_server.cpp | 2 +- tests/unit_tests/carrot_core.cpp | 1 + tests/unit_tests/carrot_fcmp.cpp | 3 +- tests/unit_tests/carrot_impl.cpp | 2 + tests/unit_tests/carrot_legacy.cpp | 1 + tests/unit_tests/carrot_sparc.cpp | 1 + tests/unit_tests/tx_construction_helpers.cpp | 1 + tests/unit_tests/wallet_tx_builder.cpp | 46 ++- 27 files changed, 422 insertions(+), 330 deletions(-) diff --git a/src/carrot_core/output_set_finalization.cpp b/src/carrot_core/output_set_finalization.cpp index d5ab10b4d..0a6574c23 100644 --- a/src/carrot_core/output_set_finalization.cpp +++ b/src/carrot_core/output_set_finalization.cpp @@ -160,8 +160,9 @@ void get_output_enote_proposals(const std::vector &norm const crypto::key_image &tx_first_key_image, std::vector &output_enote_proposals_out, encrypted_payment_id_t &encrypted_payment_id_out, + cryptonote::transaction_type tx_type, size_t &change_index_out, - std::unordered_map &normal_payments_indices_out, + std::unordered_map &payments_indices_out, std::vector> *payment_proposal_order_out) { output_enote_proposals_out.clear(); @@ -204,8 +205,7 @@ void get_output_enote_proposals(const std::vector &norm // D^other_e std::optional other_enote_ephemeral_pubkey; - - // map destinations to output keys to be able to find the indices of these outputs in tx + // map destinations to output keys in order to find the indices of these outputs in tx std::unordered_map output_destinations_to_keys; // construct normal enotes @@ -215,11 +215,19 @@ void get_output_enote_proposals(const std::vector &norm output_entry.second = {false, i}; encrypted_payment_id_t encrypted_payment_id; - get_output_proposal_normal_v1(normal_payment_proposals[i], - tx_first_key_image, - s_view_balance_dev, - output_entry.first, - encrypted_payment_id); + if (tx_type == cryptonote::transaction_type::RETURN) { + get_output_proposal_return_v1(normal_payment_proposals[i], + tx_first_key_image, + s_view_balance_dev, + output_entry.first, + encrypted_payment_id); + } else { + get_output_proposal_normal_v1(normal_payment_proposals[i], + tx_first_key_image, + s_view_balance_dev, + output_entry.first, + encrypted_payment_id); + } // if 1 normal, and 2 self-send, set D^other_e equal to this D_e if (num_proposals == 2) @@ -281,10 +289,16 @@ void get_output_enote_proposals(const std::vector &norm CARROT_THROW(std::invalid_argument, "neither a view-balance nor view-incoming device was provided"); } + // save the change one time key if (selfsend_payment_proposal.enote_type == CarrotEnoteType::CHANGE) { change_address = output_entry.first.enote.onetime_address; } + + // save the one time key for this destination + output_destinations_to_keys.insert( + {output_entry.first.enote.onetime_address, selfsend_payment_proposal.destination_address_spend_pubkey} + ); } // sort enotes by K_o @@ -298,11 +312,11 @@ void get_output_enote_proposals(const std::vector &norm if (sortable_data[i].first.enote.onetime_address == change_address) { change_index_out = i; - continue; } + // map destinations to output indices const auto spend_key = output_destinations_to_keys[sortable_data[i].first.enote.onetime_address]; - normal_payments_indices_out.insert( + payments_indices_out.insert( {spend_key, i} ); } diff --git a/src/carrot_core/output_set_finalization.h b/src/carrot_core/output_set_finalization.h index 42b5f84b0..125e9f570 100644 --- a/src/carrot_core/output_set_finalization.h +++ b/src/carrot_core/output_set_finalization.h @@ -110,8 +110,9 @@ void get_output_enote_proposals(const std::vector &norm const crypto::key_image &tx_first_key_image, std::vector &output_enote_proposals_out, encrypted_payment_id_t &encrypted_payment_id_out, + cryptonote::transaction_type tx_type, size_t &change_index_out, - std::unordered_map &normal_payments_indices_out, + std::unordered_map &payments_indices_out, std::vector> *payment_proposal_order_out = nullptr); /** * brief: get_coinbase_output_enotes - convert a *finalized* set of payment proposals into coinbase output enotes diff --git a/src/carrot_core/payment_proposal.cpp b/src/carrot_core/payment_proposal.cpp index 3cba6986b..0432ee884 100644 --- a/src/carrot_core/payment_proposal.cpp +++ b/src/carrot_core/payment_proposal.cpp @@ -422,13 +422,15 @@ void get_output_proposal_special_v1(const CarrotPaymentProposalSelfSendV1 &propo output_enote_out.enote.enote_ephemeral_pubkey = enote_ephemeral_pubkey; output_enote_out.enote.tx_first_key_image = tx_first_key_image; output_enote_out.enote.asset_type = "SAL1"; + output_enote_out.enote.return_enc = crypto::rand(); output_enote_out.amount = proposal.amount; } //------------------------------------------------------------------------------------------------------------------- void get_output_proposal_return_v1(const CarrotPaymentProposalV1 &proposal, - const crypto::key_image &tx_first_key_image, - RCTOutputEnoteProposal &output_enote_out, - encrypted_payment_id_t &encrypted_payment_id_out) + const crypto::key_image &tx_first_key_image, + const view_balance_secret_device *s_view_balance_dev, + RCTOutputEnoteProposal &output_enote_out, + encrypted_payment_id_t &encrypted_payment_id_out) { // 1. sanity checks CARROT_CHECK_AND_THROW(proposal.randomness != null_anchor, @@ -446,30 +448,30 @@ void get_output_proposal_return_v1(const CarrotPaymentProposalV1 &proposal, // 4. build the output enote address pieces crypto::hash s_sender_receiver; auto q_wiper = auto_wiper(s_sender_receiver); - encrypted_return_pubkey_t return_pubkey_out; - // HERE BE DRAGONS!!! - // SRCG: the following call needs the "destination" parameter adjusted for return_payment - 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.enote_ephemeral_pubkey, - input_context, - nullptr, - false, - s_sender_receiver, - 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.enote.view_tag, - return_pubkey_out); + encrypted_return_pubkey_t return_pubkey; + 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.enote_ephemeral_pubkey, + input_context, + s_view_balance_dev, + false, // coinbase_amount_commitment + s_sender_receiver, + 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.enote.view_tag, + return_pubkey + ); // 5. Override the values that change because of the enote onetime address (K_o) changing // i.e. {K_o, vt, m_a, a_enc, m_anchor, anchor_enc, m_pid, pid_enc} - + // Override the onetime address output_enote_out.enote.onetime_address = rct::rct2pk(rct::addKeys(rct::pk2rct(proposal.destination.address_spend_pubkey), rct::pk2rct(proposal.destination.address_view_pubkey))); @@ -485,9 +487,12 @@ void get_output_proposal_return_v1(const CarrotPaymentProposalV1 &proposal, // Recalculate the pid_enc = pid XOR m_pid encrypted_payment_id_out = encrypt_legacy_payment_id(proposal.destination.payment_id, s_sender_receiver, output_enote_out.enote.onetime_address); - // 6. save the amount and first key image + // 6. save the amount & first key image & asset type output_enote_out.amount = proposal.amount; + output_enote_out.enote.asset_type = proposal.asset_type; output_enote_out.enote.tx_first_key_image = tx_first_key_image; + // disable returning an already returned tx. + output_enote_out.enote.return_enc = crypto::rand(); } //------------------------------------------------------------------------------------------------------------------- void get_output_proposal_internal_v1(const CarrotPaymentProposalSelfSendV1 &proposal, @@ -553,6 +558,7 @@ void get_output_proposal_internal_v1(const CarrotPaymentProposalSelfSendV1 &prop output_enote_out.enote.enote_ephemeral_pubkey = enote_ephemeral_pubkey; output_enote_out.enote.tx_first_key_image = tx_first_key_image; output_enote_out.enote.asset_type = "SAL1"; + output_enote_out.enote.return_enc = crypto::rand(); output_enote_out.amount = proposal.amount; } //------------------------------------------------------------------------------------------------------------------- diff --git a/src/carrot_core/payment_proposal.h b/src/carrot_core/payment_proposal.h index db5927cdf..4623bad7d 100644 --- a/src/carrot_core/payment_proposal.h +++ b/src/carrot_core/payment_proposal.h @@ -145,9 +145,10 @@ void get_output_proposal_normal_v1(const CarrotPaymentProposalV1 &proposal, * outparam: encrypted_payment_id_out - pid_enc */ void get_output_proposal_return_v1(const CarrotPaymentProposalV1 &proposal, - const crypto::key_image &tx_first_key_image, - RCTOutputEnoteProposal &output_enote_out, - encrypted_payment_id_t &encrypted_payment_id_out); + const crypto::key_image &tx_first_key_image, + const view_balance_secret_device *s_view_balance_dev, + RCTOutputEnoteProposal &output_enote_out, + encrypted_payment_id_t &encrypted_payment_id_out); /** * brief: get_output_proposal_special_v1 - convert the carrot proposal to an output proposal (external selfsend) * param: proposal - diff --git a/src/carrot_impl/account.cpp b/src/carrot_impl/account.cpp index 80ef28d8c..f19d88d71 100644 --- a/src/carrot_impl/account.cpp +++ b/src/carrot_impl/account.cpp @@ -336,10 +336,12 @@ void carrot_and_legacy_account::set_carrot_keys(const AddressDeriveType default_ generate_subaddress_map(); } //---------------------------------------------------------------------------------------------------------------------- -void carrot_and_legacy_account::set_cn_subaddress_map(const std::unordered_map& subaddress_map_cn) +void carrot_and_legacy_account::insert_subaddresses(const std::unordered_map& subaddress_map_cn) { - for (const auto &p : subaddress_map_cn) - subaddress_map.insert({p.first, {{p.second.major, p.second.minor}, AddressDeriveType::PreCarrot}}); + // if AddressDeriveType::Auto causing a problem, this function can take it as a parameter, you then have to update + // all inserts to `subaddress_map`, and only use this function to insert into it with appropriate derive type. + for (const auto &p : subaddress_map_cn) + subaddress_map.insert({p.first, {{p.second.major, p.second.minor}, AddressDeriveType::Auto}}); } //---------------------------------------------------------------------------------------------------------------------- diff --git a/src/carrot_impl/account.h b/src/carrot_impl/account.h index eb780dc57..74a317079 100644 --- a/src/carrot_impl/account.h +++ b/src/carrot_impl/account.h @@ -107,8 +107,7 @@ namespace carrot ); void set_carrot_keys(const AddressDeriveType default_derive_type = AddressDeriveType::Carrot); - - void set_cn_subaddress_map(const std::unordered_map& subaddress_map); + void insert_subaddresses(const std::unordered_map& subaddress_map); AddressDeriveType resolve_derive_type(const AddressDeriveType derive_type) const; }; diff --git a/src/carrot_impl/format_utils.cpp b/src/carrot_impl/format_utils.cpp index cfe53bf56..b5fe119cd 100644 --- a/src/carrot_impl/format_utils.cpp +++ b/src/carrot_impl/format_utils.cpp @@ -198,14 +198,13 @@ cryptonote::transaction store_carrot_to_transaction_v1(const std::vector &sources, const rct::xmr_amount fee, const cryptonote::transaction_type tx_type, - const size_t change_index, const std::vector change_masks, const encrypted_payment_id_t encrypted_payment_id) { const size_t nins = key_images.size(); const size_t nouts = enotes.size(); CHECK_AND_ASSERT_THROW_MES(nins == sources.size(), "invalid inputs/sources size"); - CHECK_AND_ASSERT_THROW_MES(change_masks.size() == nouts - 1, "invalid change masks size. Expected: " << nouts - 1 << " got: " << change_masks.size()); + CHECK_AND_ASSERT_THROW_MES(change_masks.size() == nouts, "invalid change masks size. Expected: " << nouts - 1 << " got: " << change_masks.size()); cryptonote::transaction tx; tx.pruned = true; @@ -213,7 +212,7 @@ cryptonote::transaction store_carrot_to_transaction_v1(const std::vector &sources, const rct::xmr_amount fee, const cryptonote::transaction_type tx_type, - const size_t change_index, const std::vector change_masks, const encrypted_payment_id_t encrypted_payment_id); /** diff --git a/src/carrot_impl/tx_builder_outputs.cpp b/src/carrot_impl/tx_builder_outputs.cpp index b22460d9e..24f13f1cc 100644 --- a/src/carrot_impl/tx_builder_outputs.cpp +++ b/src/carrot_impl/tx_builder_outputs.cpp @@ -69,6 +69,7 @@ void get_output_enote_proposals_from_proposal_v1(const CarrotTransactionProposal tx_proposal.key_images_sorted.at(0), output_enote_proposals_out, encrypted_payment_id_out, + tx_proposal.tx_type, change_index, normal_payments_indices, payment_proposal_order_out); @@ -126,7 +127,6 @@ void make_pruned_transaction_from_proposal_v1(const CarrotTransactionProposalV1 tx_proposal.sources, tx_proposal.fee, cryptonote::transaction_type::TRANSFER, - 0, // change_index {}, // change_masks encrypted_payment_id); diff --git a/src/carrot_impl/tx_proposal.h b/src/carrot_impl/tx_proposal.h index 1418dce15..72ec2ee70 100644 --- a/src/carrot_impl/tx_proposal.h +++ b/src/carrot_impl/tx_proposal.h @@ -84,6 +84,8 @@ struct CarrotTransactionProposalV1 encrypted_payment_id_t dummy_encrypted_payment_id; /// Fee to miner rct::xmr_amount fee; + /// transaction type, e.g. + cryptonote::transaction_type tx_type; /// This field is truly "extra". It should contain only tx.extra fields that aren't present in a /// normal Carrot transaction, i.e. NOT ephemeral pubkeys nor encrypted PIDs diff --git a/src/carrot_impl/tx_proposal_utils.cpp b/src/carrot_impl/tx_proposal_utils.cpp index 7428be1cc..884b5825e 100644 --- a/src/carrot_impl/tx_proposal_utils.cpp +++ b/src/carrot_impl/tx_proposal_utils.cpp @@ -117,6 +117,7 @@ void make_carrot_transaction_proposal_v1(const std::vector &extra, + const cryptonote::transaction_type tx_type, select_inputs_func_t &&select_inputs, carve_fees_and_balance_func_t &&carve_fees_and_balance, const crypto::public_key &change_address_spend_pubkey, @@ -226,6 +227,9 @@ void make_carrot_transaction_proposal_v1(const std::vector &extra, + const cryptonote::transaction_type tx_type, select_inputs_func_t &&select_inputs, const crypto::public_key &change_address_spend_pubkey, const subaddress_index_extended &change_address_index, @@ -385,6 +390,7 @@ void make_carrot_transaction_proposal_v1_transfer( fee_per_weight, fee_quantization_mask, extra, + tx_type, std::forward(select_inputs), std::move(carve_fees_and_balance), change_address_spend_pubkey, @@ -398,6 +404,7 @@ void make_carrot_transaction_proposal_v1_sweep( const rct::xmr_amount fee_per_weight, const rct::xmr_amount fee_quantization_mask, const std::vector &extra, + const cryptonote::transaction_type tx_type, std::vector &&selected_inputs, const crypto::public_key &change_address_spend_pubkey, const subaddress_index_extended &change_address_index, @@ -477,6 +484,7 @@ void make_carrot_transaction_proposal_v1_sweep( fee_per_weight, fee_quantization_mask, extra, + tx_type, std::move(select_inputs), std::move(carve_fees_and_balance), change_address_spend_pubkey, diff --git a/src/carrot_impl/tx_proposal_utils.h b/src/carrot_impl/tx_proposal_utils.h index 72dbd155a..cd6aa04de 100644 --- a/src/carrot_impl/tx_proposal_utils.h +++ b/src/carrot_impl/tx_proposal_utils.h @@ -187,6 +187,7 @@ void make_carrot_transaction_proposal_v1(const std::vector &extra, + const cryptonote::transaction_type tx_type, select_inputs_func_t &&select_inputs, carve_fees_and_balance_func_t &&carve_fees_and_balance, const crypto::public_key &change_address_spend_pubkey, @@ -199,6 +200,7 @@ void make_carrot_transaction_proposal_v1_transfer( const rct::xmr_amount fee_per_weight, const rct::xmr_amount fee_quantization_mask, const std::vector &extra, + const cryptonote::transaction_type tx_type, select_inputs_func_t &&select_inputs, const crypto::public_key &change_address_spend_pubkey, const subaddress_index_extended &change_address_index, @@ -212,6 +214,7 @@ void make_carrot_transaction_proposal_v1_sweep( const rct::xmr_amount fee_per_weight, const rct::xmr_amount fee_quantization_mask, const std::vector &extra, + const cryptonote::transaction_type tx_type, std::vector &&selected_inputs, const crypto::public_key &change_address_spend_pubkey, const subaddress_index_extended &change_address_index, diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 5d4497165..e95db1d9d 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -8138,7 +8138,7 @@ bool simple_wallet::sweep_single(const std::vector &args_) try { // figure out what tx will be necessary - auto ptx_vector = m_wallet->create_transactions_single(ki, info.address, info.is_subaddress, outputs, fake_outs_count, 0 /* unlock_time */, priority, extra); + auto ptx_vector = m_wallet->create_transactions_single(ki, info.address, cryptonote::transaction_type::TRANSFER, info.is_subaddress, outputs, fake_outs_count, 0 /* unlock_time */, priority, extra); if (ptx_vector.empty()) { @@ -8411,8 +8411,8 @@ bool simple_wallet::return_payment(const std::vector &args_) asset_type = td.asset_type; } std::ostringstream prompt; - if (!process_ring_members(ptx_vector, prompt, m_wallet->print_ring_members())) - return true; + // if (!process_ring_members(ptx_vector, prompt, m_wallet->print_ring_members())) + // return true; prompt << boost::format(tr("Returning %s %s for a total fee of %s %s. Is this okay?")) % print_money(total_sent) % asset_type % print_money(total_fee) % asset_type; diff --git a/src/wallet/scanning_tools.cpp b/src/wallet/scanning_tools.cpp index f537df35d..3f128f913 100644 --- a/src/wallet/scanning_tools.cpp +++ b/src/wallet/scanning_tools.cpp @@ -55,7 +55,7 @@ namespace wallet { //------------------------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------------------------- -static bool parse_tx_extra_for_scanning(const std::vector &tx_extra, +bool parse_tx_extra_for_scanning(const std::vector &tx_extra, const std::size_t n_outputs, std::vector &main_tx_ephemeral_pubkeys_out, std::vector &additional_tx_ephemeral_pubkeys_out, @@ -373,7 +373,7 @@ static std::optional view_incoming_scan_carrot_ } //------------------------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------------------------- -static void perform_ecdh_derivations(const epee::span main_tx_ephemeral_pubkeys, +void perform_ecdh_derivations(const epee::span main_tx_ephemeral_pubkeys, const epee::span additional_tx_ephemeral_pubkeys, const crypto::secret_key &k_view_incoming, hw::device &hwdev, @@ -859,7 +859,14 @@ std::optional try_derive_enote_key_image( { const cryptonote::subaddress_index subaddr_index_cn{enote_scan_info.subaddr_index->index.major, enote_scan_info.subaddr_index->index.minor}; - subaddress_extension = rct::sk2rct(acc.get_keys().get_device().get_subaddress_secret_key(enote_scan_info.is_carrot ? acc.get_keys().k_view_incoming : acc.get_keys().m_view_secret_key, subaddr_index_cn)); + subaddress_extension = rct::sk2rct( + acc.get_keys() + .get_device() + .get_subaddress_secret_key( + enote_scan_info.is_carrot ? acc.get_keys().k_view_incoming : acc.get_keys().m_view_secret_key, + subaddr_index_cn + ) + ); } else // !subaddr_index_cn.is_zero() { diff --git a/src/wallet/scanning_tools.h b/src/wallet/scanning_tools.h index 0480baa62..f971434f7 100644 --- a/src/wallet/scanning_tools.h +++ b/src/wallet/scanning_tools.h @@ -100,6 +100,21 @@ using MoneroEnoteVariant = std::variant; + +bool parse_tx_extra_for_scanning(const std::vector &tx_extra, + const std::size_t n_outputs, + std::vector &main_tx_ephemeral_pubkeys_out, + std::vector &additional_tx_ephemeral_pubkeys_out, + cryptonote::blobdata &tx_extra_nonce_out); + +void perform_ecdh_derivations(const epee::span main_tx_ephemeral_pubkeys, + const epee::span additional_tx_ephemeral_pubkeys, + const crypto::secret_key &k_view_incoming, + hw::device &hwdev, + const bool is_carrot, + std::vector &main_derivations_out, + std::vector &additional_derivations_out); + std::optional view_incoming_scan_enote_from_prefix( const cryptonote::transaction_prefix &tx_prefix, const rct::xmr_amount amount, diff --git a/src/wallet/tx_builder.cpp b/src/wallet/tx_builder.cpp index 164a714dd..24f3df2d7 100644 --- a/src/wallet/tx_builder.cpp +++ b/src/wallet/tx_builder.cpp @@ -437,6 +437,7 @@ std::vector make_carrot_transaction_proposa const rct::xmr_amount fee_per_weight, const rct::xmr_amount fee_quantization_mask, const std::vector &extra, + const cryptonote::transaction_type tx_type, const uint32_t subaddr_account, const std::set &subaddr_indices, wallet2::unique_index_container subtract_fee_from_outputs, @@ -499,6 +500,7 @@ std::vector make_carrot_transaction_proposa fee_per_weight, fee_quantization_mask, extra, + tx_type, std::move(select_inputs), change_address_spend_pubkey, {{subaddr_account, 0}, carrot::AddressDeriveType::PreCarrot}, //! @TODO: handle Carrot keys @@ -533,6 +535,7 @@ std::vector make_carrot_transaction_proposa const std::vector &dsts, const std::uint32_t priority, const std::vector &extra, + const cryptonote::transaction_type tx_type, const std::uint32_t subaddr_account, const std::set &subaddr_indices, const wallet2::unique_index_container &subtract_fee_from_outputs) @@ -561,6 +564,7 @@ std::vector make_carrot_transaction_proposa fee_per_weight, fee_quantization_mask, extra, + tx_type, subaddr_account, subaddr_indices, subtract_fee_from_outputs, @@ -568,8 +572,7 @@ std::vector make_carrot_transaction_proposa } //------------------------------------------------------------------------------------------------------------------- std::vector make_carrot_transaction_proposals_wallet2_sweep( - const wallet2::transfer_container &transfers, - const std::unordered_map &subaddress_map, + wallet2 &w, const std::vector &input_key_images, const cryptonote::account_public_address &address, const bool is_subaddress, @@ -577,8 +580,12 @@ std::vector make_carrot_transaction_proposa const rct::xmr_amount fee_per_weight, const rct::xmr_amount fee_quantization_mask, const std::vector &extra, + const cryptonote::transaction_type tx_type, const std::uint64_t top_block_index) { + wallet2::transfer_container transfers; + w.get_transfers(transfers); + const size_t n_inputs = input_key_images.size(); CARROT_CHECK_AND_THROW(n_inputs, carrot::too_few_inputs, "no key images provided"); CARROT_CHECK_AND_THROW(n_dests_per_tx, carrot::too_few_outputs, "sweep must have at least one destination"); @@ -608,17 +615,22 @@ std::vector make_carrot_transaction_proposa } const crypto::public_key change_address_spend_pubkey - = find_change_address_spend_pubkey(subaddress_map, subaddr_account); + = find_change_address_spend_pubkey(w.get_account().subaddress_map, subaddr_account); // get 1 payment proposal corresponding to (address, is_subaddres) std::vector normal_payment_proposals; std::vector selfsend_payment_proposals; for (size_t i = 0; i < n_dests_per_tx; ++i) { + cryptonote::tx_destination_entry de; + de.amount = 0; + de.addr = address; + de.is_subaddress = is_subaddress; + de.asset_type = "SAL1"; const bool is_selfsend_dest = build_payment_proposals(normal_payment_proposals, selfsend_payment_proposals, - cryptonote::tx_destination_entry(/*amount=*/0, address, is_subaddress), - subaddress_map); + de, + w.get_account().get_subaddress_map()); CHECK_AND_ASSERT_THROW_MES((is_selfsend_dest && selfsend_payment_proposals.size() == i+1) || (!is_selfsend_dest && normal_payment_proposals.size() == i+1), __func__ << ": BUG in build_payment_proposals: incorrect count for payment proposal lists"); @@ -648,10 +660,18 @@ std::vector make_carrot_transaction_proposa fee_per_weight, fee_quantization_mask, extra, + tx_type, std::move(selected_inputs), change_address_spend_pubkey, {{subaddr_account, 0}, carrot::AddressDeriveType::PreCarrot}, //! @TODO: handle Carrot keys tx_proposal); + + // populate the sources + std::vector selected_transfer_indices_sorted; + for (const auto &ki: tx_proposal.key_images_sorted) { + selected_transfer_indices_sorted.push_back(w.get_transfer_details(ki)); + } + tx_proposal.sources = get_sources(transfers, selected_transfer_indices_sorted, "SAL1", w); } CARROT_CHECK_AND_THROW(ki_idx == input_key_images.size(), @@ -667,11 +687,9 @@ std::vector make_carrot_transaction_proposa const bool is_subaddress, const size_t n_dests_per_tx, const std::uint32_t priority, - const std::vector &extra) + const std::vector &extra, + const cryptonote::transaction_type tx_type) { - wallet2::transfer_container transfers; - w.get_transfers(transfers); - const rct::xmr_amount fee_per_weight = w.get_base_fee(priority); const rct::xmr_amount fee_quantization_mask = w.get_fee_quantization_mask(); @@ -681,8 +699,7 @@ std::vector make_carrot_transaction_proposa const std::uint64_t top_block_index = current_chain_height - 1; return make_carrot_transaction_proposals_wallet2_sweep( - transfers, - w.get_subaddress_map_ref(), + w, input_key_images, address, is_subaddress, @@ -690,12 +707,12 @@ std::vector make_carrot_transaction_proposa fee_per_weight, fee_quantization_mask, extra, + tx_type, top_block_index); } //------------------------------------------------------------------------------------------------------------------- std::vector make_carrot_transaction_proposals_wallet2_sweep_all( - const wallet2::transfer_container &transfers, - const std::unordered_map &subaddress_map, + wallet2 &w, const rct::xmr_amount only_below, const cryptonote::account_public_address &address, const bool is_subaddress, @@ -703,10 +720,14 @@ std::vector make_carrot_transaction_proposa const rct::xmr_amount fee_per_weight, const rct::xmr_amount fee_quantization_mask, const std::vector &extra, + const cryptonote::transaction_type tx_type, const std::uint32_t subaddr_account, const std::set &subaddr_indices, const std::uint64_t top_block_index) { + wallet2::transfer_container transfers; + w.get_transfers(transfers); + const std::unordered_map unburned_transfers_by_key_image = collect_non_burned_transfers_by_key_image(transfers); @@ -736,8 +757,7 @@ std::vector make_carrot_transaction_proposa CHECK_AND_ASSERT_THROW_MES(!input_key_images.empty(), __func__ << ": no usable transfers to sweep"); return make_carrot_transaction_proposals_wallet2_sweep( - transfers, - subaddress_map, + w, input_key_images, address, is_subaddress, @@ -745,6 +765,7 @@ std::vector make_carrot_transaction_proposa fee_per_weight, fee_quantization_mask, extra, + tx_type, top_block_index); } //------------------------------------------------------------------------------------------------------------------- @@ -756,12 +777,10 @@ std::vector make_carrot_transaction_proposa const size_t n_dests_per_tx, const std::uint32_t priority, const std::vector &extra, + const cryptonote::transaction_type tx_type, const std::uint32_t subaddr_account, const std::set &subaddr_indices) { - wallet2::transfer_container transfers; - w.get_transfers(transfers); - const rct::xmr_amount fee_per_weight = w.get_base_fee(priority); const rct::xmr_amount fee_quantization_mask = w.get_fee_quantization_mask(); @@ -771,8 +790,7 @@ std::vector make_carrot_transaction_proposa const std::uint64_t top_block_index = current_chain_height - 1; return make_carrot_transaction_proposals_wallet2_sweep_all( - transfers, - w.get_subaddress_map_ref(), + w, only_below, address, is_subaddress, @@ -780,6 +798,7 @@ std::vector make_carrot_transaction_proposa fee_per_weight, fee_quantization_mask, extra, + tx_type, subaddr_account, subaddr_indices, top_block_index); @@ -876,25 +895,52 @@ bool get_address_openings_x_y( //------------------------------------------------------------------------------------------------------------------- void encrypt_change_index( const std::vector &proposals, + const std::vector &selfsend_proposal_cores, const crypto::key_image &tx_first_key_image, const size_t change_index, - const std::unordered_map &normal_payments_indices, + const std::unordered_map &payments_indices, std::vector &change_masks_out ) { - // 2. input context: input_context = "R" || KI_1 + // 1. input context: input_context = "R" || KI_1 const carrot::input_context_t input_context = carrot::make_carrot_input_context(tx_first_key_image); - // 3. make D_e and do external ECDH - for (const auto &p: proposals) { + // 2. collect proposals and selfsend proposals destinations + std::vector> destinations; + for (const auto &p : proposals) { + destinations.emplace_back(p.destination.address_spend_pubkey, payments_indices.at(p.destination.address_spend_pubkey), true); + } + for (const auto &p : selfsend_proposal_cores) { + destinations.emplace_back(p.destination_address_spend_pubkey, payments_indices.at(p.destination_address_spend_pubkey), false); + } + + // 3. sort by indices + std::sort(destinations.begin(), destinations.end(), + [](const auto &a, const auto &b) { + return std::get<1>(a) < std::get<1>(b); + } + ); + + // 4. calculate change masks + for (const auto &d: destinations) { // get shared secret - mx25519_pubkey s_sender_receiver_unctx; mx25519_pubkey eph_pubkey; - carrot::get_normal_proposal_ecdh_parts( - p, - input_context, - eph_pubkey, - s_sender_receiver_unctx - ); + mx25519_pubkey s_sender_receiver_unctx; + if (std::get<2>(d)) { + // normal payment proposal + const auto it = std::find_if(proposals.begin(), proposals.end(), + [&d](const carrot::CarrotPaymentProposalV1 &p) { + return p.destination.address_spend_pubkey == std::get<0>(d); + }); + CHECK_AND_ASSERT_THROW_MES(it != proposals.end(), "Failed to find normal payment proposal"); + carrot::get_normal_proposal_ecdh_parts( + *it, + input_context, + eph_pubkey, + s_sender_receiver_unctx + ); + } else { + s_sender_receiver_unctx = crypto::rand(); + } // derive a scalar from the shared secret crypto::secret_key output_index_key; @@ -902,7 +948,7 @@ void encrypt_change_index( memcpy(output_index_derivation.data, s_sender_receiver_unctx.data, sizeof(output_index_derivation.data)); crypto::derivation_to_scalar( output_index_derivation, - normal_payments_indices.at(p.destination.address_spend_pubkey), + std::get<1>(d), output_index_key ); @@ -923,7 +969,6 @@ void encrypt_change_index( //------------------------------------------------------------------------------------------------------------------- cryptonote::transaction finalize_all_proofs_from_transfer_details( const carrot::CarrotTransactionProposalV1 &tx_proposal, - const cryptonote::transaction_type tx_type, const wallet2 &w) { const size_t n_inputs = tx_proposal.key_images_sorted.size(); @@ -957,7 +1002,7 @@ cryptonote::transaction finalize_all_proofs_from_transfer_details( std::vector output_enote_proposals; carrot::encrypted_payment_id_t encrypted_payment_id; size_t change_index; - std::unordered_map normal_payments_indices; + std::unordered_map payments_indices; carrot::get_output_enote_proposals(tx_proposal.normal_payment_proposals, selfsend_payment_proposal_cores, tx_proposal.dummy_encrypted_payment_id, @@ -966,8 +1011,9 @@ cryptonote::transaction finalize_all_proofs_from_transfer_details( tx_proposal.key_images_sorted.at(0), output_enote_proposals, encrypted_payment_id, + tx_proposal.tx_type, change_index, - normal_payments_indices, + payments_indices, nullptr); CHECK_AND_ASSERT_THROW_MES(output_enote_proposals.size() == n_outputs, "finalize_all_proofs_from_transfer_details: unexpected number of output enote proposals"); @@ -995,9 +1041,10 @@ cryptonote::transaction finalize_all_proofs_from_transfer_details( std::vector change_masks; encrypt_change_index( tx_proposal.normal_payment_proposals, + selfsend_payment_proposal_cores, tx_proposal.key_images_sorted.at(0), change_index, - normal_payments_indices, + payments_indices, change_masks); // serialize transaction @@ -1005,8 +1052,7 @@ cryptonote::transaction finalize_all_proofs_from_transfer_details( tx_proposal.key_images_sorted, tx_proposal.sources, tx_proposal.fee, - tx_type, - change_index, + tx_proposal.tx_type, change_masks, encrypted_payment_id); @@ -1142,7 +1188,7 @@ cryptonote::transaction finalize_all_proofs_from_transfer_details( rct::hash2rct(tx_prefix_hash), inSk, destinations, - tx_type, + tx_proposal.tx_type, "SAL1", destination_asset_types, inamounts, @@ -1309,7 +1355,6 @@ wallet2::pending_tx finalize_all_proofs_from_transfer_details_as_pending_tx( ptx.tx = finalize_all_proofs_from_transfer_details( tx_proposal, - cryptonote::transaction_type::TRANSFER, // TODO:take this as a parameter w ); diff --git a/src/wallet/tx_builder.h b/src/wallet/tx_builder.h index 711dee821..c2d87972a 100644 --- a/src/wallet/tx_builder.h +++ b/src/wallet/tx_builder.h @@ -63,6 +63,7 @@ std::vector make_carrot_transaction_proposa const rct::xmr_amount fee_per_weight, const rct::xmr_amount fee_quantization_mask, const std::vector &extra, + const cryptonote::transaction_type tx_type, const uint32_t subaddr_account, const std::set &subaddr_indices, wallet2::unique_index_container subtract_fee_from_outputs, @@ -72,13 +73,13 @@ std::vector make_carrot_transaction_proposa const std::vector &dsts, const std::uint32_t priority, const std::vector &extra, + const cryptonote::transaction_type tx_type, const std::uint32_t subaddr_account, const std::set &subaddr_indices, const wallet2::unique_index_container &subtract_fee_from_outputs); std::vector make_carrot_transaction_proposals_wallet2_sweep( - const wallet2::transfer_container &transfers, - const std::unordered_map &subaddress_map, + wallet2 &w, const std::vector &input_key_images, const cryptonote::account_public_address &address, const bool is_subaddress, @@ -86,6 +87,7 @@ std::vector make_carrot_transaction_proposa const rct::xmr_amount fee_per_weight, const rct::xmr_amount fee_quantization_mask, const std::vector &extra, + const cryptonote::transaction_type tx_type, const std::uint64_t top_block_index); std::vector make_carrot_transaction_proposals_wallet2_sweep( wallet2 &w, @@ -94,11 +96,11 @@ std::vector make_carrot_transaction_proposa const bool is_subaddress, const size_t n_dests_per_tx, const std::uint32_t priority, - const std::vector &extra); + const std::vector &extra, + const cryptonote::transaction_type tx_type); std::vector make_carrot_transaction_proposals_wallet2_sweep_all( - const wallet2::transfer_container &transfers, - const std::unordered_map &subaddress_map, + wallet2 &w, const rct::xmr_amount only_below, const cryptonote::account_public_address &address, const bool is_subaddress, @@ -106,6 +108,7 @@ std::vector make_carrot_transaction_proposa const rct::xmr_amount fee_per_weight, const rct::xmr_amount fee_quantization_mask, const std::vector &extra, + const cryptonote::transaction_type tx_type, const std::uint32_t subaddr_account, const std::set &subaddr_indices, const std::uint64_t top_block_index); @@ -117,6 +120,7 @@ std::vector make_carrot_transaction_proposa const size_t n_dests_per_tx, const std::uint32_t priority, const std::vector &extra, + const cryptonote::transaction_type tx_type, const std::uint32_t subaddr_account, const std::set &subaddr_indices); wallet2::pending_tx make_pending_carrot_tx(const carrot::CarrotTransactionProposalV1 &tx_proposal, diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 63d1475dc..fd3e4c6ac 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -97,6 +97,7 @@ using namespace epee; #include "net/socks_connect.h" #include "carrot_impl/format_utils.h" #include "tx_builder.h" +#include "scanning_tools.h" extern "C" { @@ -2532,23 +2533,20 @@ void wallet2::process_new_scanned_transaction( } bool notify = false; - // per receiving subaddress index + // per receiving subaddress index -> (asset type, amount, one time address) std::unordered_map> tx_money_got_in_outs; - std::unordered_map tx_amounts_individual_outs; + std::unordered_map>> tx_amounts_individual_outs; // Note that this includes any 0-amount outputs, which we keep track of for background scanning bool received_an_output = false; - crypto::hash payment_id = null_hash; // for each for scanned output... for (size_t local_output_index = 0; local_output_index < n_scanned_enotes; ++local_output_index) { - const auto &enote_scan_info_local = enote_scan_infos[local_output_index]; - bool is_protocol = (tx.type == cryptonote::transaction_type::PROTOCOL); - if (!is_protocol) - if (!enote_scan_info_local || !enote_scan_info_local->subaddr_index) - continue; + const auto &enote_scan_info = enote_scan_infos[local_output_index]; + if (!enote_scan_info || !enote_scan_info->subaddr_index) + continue; received_an_output = true; @@ -2559,113 +2557,18 @@ void wallet2::process_new_scanned_transaction( continue; } - uint64_t td_origin_idx = (uint64_t)-1; - wallet::enote_view_incoming_scan_info_t enote_scan_info_protocol; - if (is_protocol) { - - // Check that it is intended for us - if (!m_salvium_txs.count(onetime_address)) - continue; - - // Get the origin TX details - td_origin_idx = m_salvium_txs.at(onetime_address); - THROW_WALLET_EXCEPTION_IF(td_origin_idx >= get_num_transfer_details(), error::wallet_internal_error, "cannot locate protocol TX origin in m_transfers"); - const transfer_details &td_origin = m_transfers[td_origin_idx]; - THROW_WALLET_EXCEPTION_IF(td_origin.m_tx.type != cryptonote::transaction_type::AUDIT && - td_origin.m_tx.type != cryptonote::transaction_type::STAKE, - error::wallet_internal_error, - "incorrect TX type for protocol_tx origin in m_transfers"); - - // Perform the correct sanity checks needed for each type of origin TX - if (td_origin.m_tx.type == cryptonote::transaction_type::STAKE) { - - // Check the age of the TX - uint64_t stake_lock_period = get_config(m_nettype).STAKE_LOCK_PERIOD; - THROW_WALLET_EXCEPTION_IF(td_origin.m_block_height != height - stake_lock_period - 1, - error::wallet_internal_error, - "STAKE TX payout at incorrect height: found at " + std::to_string(height) + - ", but expected at " + std::to_string(td_origin.m_block_height + stake_lock_period + 1)); - - // Check the amount - THROW_WALLET_EXCEPTION_IF(td_origin.m_tx.amount_burnt > tx.vout[local_output_index].amount, - error::wallet_internal_error, - "STAKE TX payout (" + print_money(tx.vout[local_output_index].amount) + - ") is less than staked amount " + print_money(td_origin.m_tx.amount_burnt)); - - // Populate the dummy enote_scan_info struct - THROW_WALLET_EXCEPTION_IF(!cryptonote::get_output_public_key(td_origin.m_tx.vout[0], enote_scan_info_protocol.address_spend_pubkey), - error::wallet_internal_error, - "failed to retrieve origin TX address spend pubkey"); - enote_scan_info_protocol.amount = tx.vout[local_output_index].amount; - enote_scan_info_protocol.amount_blinding_factor = rct::I; - THROW_WALLET_EXCEPTION_IF(!cryptonote::get_output_asset_type(tx.vout[local_output_index], enote_scan_info_protocol.asset_type), - error::wallet_internal_error, - "failed to retrieve output asset type"); - enote_scan_info_protocol.main_tx_pubkey_index = 0; - enote_scan_info_protocol.payment_id = crypto::null_hash; - enote_scan_info_protocol.subaddr_index = carrot::subaddress_index_extended{{td_origin.m_subaddr_index.major,td_origin.m_subaddr_index.minor}}; - - } else if (td_origin.m_tx.type == cryptonote::transaction_type::AUDIT) { - - // Check the amount - THROW_WALLET_EXCEPTION_IF(td_origin.m_tx.amount_burnt != tx.vout[local_output_index].amount, - error::wallet_internal_error, - "AUDIT TX error - payout " + print_money(tx.vout[local_output_index].amount) + - " does not match audited amount " + print_money(td_origin.m_tx.amount_burnt)); - - // Populate the dummy enote_scan_info struct - THROW_WALLET_EXCEPTION_IF(!cryptonote::get_output_public_key(td_origin.m_tx.vout[0], enote_scan_info_protocol.address_spend_pubkey), - error::wallet_internal_error, - "failed to retrieve origin TX address spend pubkey"); - enote_scan_info_protocol.amount = tx.vout[local_output_index].amount; - enote_scan_info_protocol.amount_blinding_factor = rct::I; - THROW_WALLET_EXCEPTION_IF(!cryptonote::get_output_asset_type(tx.vout[local_output_index], enote_scan_info_protocol.asset_type), - error::wallet_internal_error, - "failed to retrieve output asset type"); - enote_scan_info_protocol.main_tx_pubkey_index = 0; - enote_scan_info_protocol.payment_id = crypto::null_hash; - enote_scan_info_protocol.subaddr_index = carrot::subaddress_index_extended{{td_origin.m_subaddr_index.major,td_origin.m_subaddr_index.minor}}; - - } else { - - // Should never happen - only STAKE and AUDIT should pay out in a PROTOCOL transaction - assert(false); - } - - // Get the output key for the change entry - crypto::public_key pk_locked_coins = crypto::null_pkey; - THROW_WALLET_EXCEPTION_IF(!get_output_public_key(td_origin.m_tx.vout[td_origin.m_internal_output_index], pk_locked_coins), - error::wallet_internal_error, - "Failed to get output public key for locked coins"); - - // At this point, we need to clear the "locked coins" count, because otherwise we will be counting yield stakes twice in our balance - //THROW_WALLET_EXCEPTION_IF(!m_locked_coins.erase(pk_locked_coins), error::wallet_internal_error, "Failed to remove protocol_tx entry from m_locked_coins"); - if (!m_locked_coins.erase(pk_locked_coins)) { - LOG_ERROR("Failed to remove protocol_tx entry from m_locked_coins - possible duplicate output key detected"); - } - } else { - // Check for return_payment - if (m_salvium_txs.count(enote_scan_info_local->address_spend_pubkey)) { - // Must be a return payment - td_origin_idx = m_salvium_txs[enote_scan_info_local->address_spend_pubkey]; - } - } - - const auto &enote_scan_info = (is_protocol) ? enote_scan_info_protocol : enote_scan_info_local; - // check for burning bug const auto ot_it = m_pub_keys.find(onetime_address); const transfer_details *burning_td = ot_it != m_pub_keys.cend() ? &m_transfers.at(ot_it->second) : nullptr; const bool should_treat_as_burned = burning_td && - (burning_td->amount() >= enote_scan_info->amount || burning_td->m_spent) - ; + (burning_td->amount() >= enote_scan_info->amount || burning_td->m_spent); if (should_treat_as_burned) { // We already have an older received transfer with a greater-than-or-equal amount LOG_ERROR("Public key " << epee::string_tools::pod_to_hex(ot_it->first) - << " from received " << print_money(enote_scan_info->amount) << " output already exists with " - << (burning_td->m_spent ? "spent" : "unspent") << " " - << print_money(burning_td->amount()) << " in tx " << burning_td->m_txid << ", received output ignored"); + << " from received " << print_money(enote_scan_info->amount) << " output already exists with " + << (burning_td->m_spent ? "spent" : "unspent") << " " + << print_money(burning_td->amount()) << " in tx " << burning_td->m_txid << ", received output ignored"); continue; } else if (burning_td) @@ -2678,8 +2581,13 @@ void wallet2::process_new_scanned_transaction( const rct::xmr_amount extra_received_money = enote_scan_info->amount - (burning_td ? burning_td->amount() : 0); const cryptonote::subaddress_index subaddr_index_cn{ enote_scan_info->subaddr_index->index.major, enote_scan_info->subaddr_index->index.minor}; - tx_money_got_in_outs[subaddr_index_cn][enote_scan_info->asset_type] += extra_received_money; - tx_amounts_individual_outs[subaddr_index_cn].push_back(extra_received_money); + + // Only count >0 amount outs + if (enote_scan_info->amount > 0) + { + tx_money_got_in_outs[subaddr_index_cn][enote_scan_info->asset_type] += extra_received_money; + tx_amounts_individual_outs[subaddr_index_cn].push_back(std::make_tuple(extra_received_money, onetime_address)); + } // set payment id const bool ignore_long_pid = block_version >= IGNORE_LONG_PAYMENT_ID_FROM_BLOCK_VERSION; @@ -2705,10 +2613,6 @@ void wallet2::process_new_scanned_transaction( if (pool) continue; - // expand subaddress table if applicable - if (should_expand(subaddr_index_cn)) - expand_subaddresses(subaddr_index_cn); - // update m_transfers view-incoming scan info, and default values transfer_details& td = tools::add_element(m_transfers); td.m_block_height = height; @@ -2725,47 +2629,12 @@ void wallet2::process_new_scanned_transaction( td.m_recovered_spend_pubkey = enote_scan_info->address_spend_pubkey; td.m_rct = tx.version >= 2; td.m_frozen = false; - set_unspent(m_transfers.size() - 1); - td.m_td_origin_idx = td_origin_idx; + td.m_td_origin_idx = m_salvium_txs.find(enote_scan_info->address_spend_pubkey) != m_salvium_txs.end() ? + m_salvium_txs.at(enote_scan_info->address_spend_pubkey) : std::numeric_limits::max(); // update m_transfers key image values - if (td_origin_idx != (uint64_t)-1) { - - // Return payment received - rebuild the correct key image - THROW_WALLET_EXCEPTION_IF(td_origin_idx >= get_num_transfer_details(), error::wallet_internal_error, "cannot locate return_payment TX origin in m_transfers"); - const transfer_details &td_origin = m_transfers[td_origin_idx]; - hw::device &hwdev = m_account.get_device(); - hw::reset_mode rst(hwdev); - hwdev.set_mode(hw::device::TRANSACTION_PARSE); - const crypto::public_key tx_public_key = get_tx_pub_key_from_extra(tx); - const std::vector additional_tx_public_keys = get_additional_tx_pub_keys_from_extra(tx); - keypair in_ephemeral; - crypto::key_image ki; - origin_data origin_tx_data; - origin_tx_data.tx_pub_key = get_tx_pub_key_from_extra(td_origin.m_tx); - origin_tx_data.output_index = td_origin.m_internal_output_index; - origin_tx_data.tx_type = td_origin.m_tx.type; - rct::salvium_input_data_t sid; - THROW_WALLET_EXCEPTION_IF(!cryptonote::generate_key_image_helper(m_account.get_keys(), - m_subaddresses, - onetime_address, - tx_public_key, - additional_tx_public_keys, - local_output_index, - in_ephemeral, - ki, - hwdev, - true, - origin_tx_data, - sid), - error::wallet_internal_error, - "failed to obtain key image for protocol_tx output"); - td.m_key_image_known = true; - td.m_key_image = ki; - } else { - td.m_key_image_known = bool(output_key_images[local_output_index]); - td.m_key_image = output_key_images[local_output_index].value_or(crypto::key_image{}); - } + td.m_key_image_known = bool(output_key_images[local_output_index]); + td.m_key_image = output_key_images[local_output_index].value_or(crypto::key_image{}); if (!td.m_key_image_known) { // we might have cold signed, and have a mapping to key images @@ -2776,11 +2645,12 @@ void wallet2::process_new_scanned_transaction( td.m_key_image_known = true; } } - if (!td.m_key_image_known && is_protocol) + + // override the key image for PROTOCOL/RETURN tx outputs. + if (td.m_td_origin_idx != std::numeric_limits::max()) { - // We need to manually create the key image data - THROW_WALLET_EXCEPTION_IF(td_origin_idx >= get_num_transfer_details(), error::wallet_internal_error, "cannot locate protocol TX origin in m_transfers"); - const transfer_details &td_origin = m_transfers[td_origin_idx]; + THROW_WALLET_EXCEPTION_IF(td.m_td_origin_idx >= get_num_transfer_details(), error::wallet_internal_error, "cannot locate return_payment TX origin in m_transfers"); + const transfer_details &td_origin = m_transfers[td.m_td_origin_idx]; hw::device &hwdev = m_account.get_device(); hw::reset_mode rst(hwdev); hwdev.set_mode(hw::device::TRANSACTION_PARSE); @@ -2794,7 +2664,7 @@ void wallet2::process_new_scanned_transaction( origin_tx_data.tx_type = td_origin.m_tx.type; rct::salvium_input_data_t sid; THROW_WALLET_EXCEPTION_IF(!cryptonote::generate_key_image_helper(m_account.get_keys(), - m_subaddresses, + m_account.get_subaddress_map(), onetime_address, tx_public_key, additional_tx_public_keys, @@ -2813,6 +2683,8 @@ void wallet2::process_new_scanned_transaction( td.m_key_image_request = m_watch_only; // for view wallets, that flag means "we want to request it" td.m_key_image_partial = m_multisig; + set_unspent(m_transfers.size() - 1); + // update m_key_images, m_pub_keys, and output_tracker_cache if (td.m_key_image_known) m_key_images[td.m_key_image] = m_transfers.size() - 1; @@ -2832,44 +2704,9 @@ void wallet2::process_new_scanned_transaction( update_multisig_rescan_info(*m_multisig_rescan_k, *m_multisig_rescan_info, m_transfers.size() - 1); } - // Check for Salvium-specific logic - if (tx.type == cryptonote::transaction_type::TRANSFER) { - - // Store the potential return details for a pre-Carrot return_payment - // We might store garbage entries here occasionally, but they shouldn't impact performance significantly - crypto::public_key P_change = crypto::null_pkey; - size_t change_idx = local_output_index; - THROW_WALLET_EXCEPTION_IF(!cryptonote::get_output_public_key(tx.vout[change_idx], P_change), error::wallet_internal_error, "Failed to get output public key"); - m_subaddresses[P_change] = subaddr_index_cn; - m_salvium_txs.insert({P_change, m_transfers.size()-1}); - - } else if (tx.type == cryptonote::transaction_type::CONVERT || tx.type == cryptonote::transaction_type::STAKE || tx.type == cryptonote::transaction_type::AUDIT) { - - // The CONVERT/STAKE/AUDIT TX was created by us - therefore we need to expect an output in the PROTOCOL_TX - // It could be a refund, an audit, or a conversion - THROW_WALLET_EXCEPTION_IF(tx.vout.size() != 1, error::wallet_internal_error, "Incorrect number of outputs from CONVERT/STAKE/AUDIT TX"); - - // Add the change output_public_key to the list of subaddresses to check - crypto::public_key P_change = crypto::null_pkey; - THROW_WALLET_EXCEPTION_IF(!cryptonote::get_output_public_key(tx.vout[0], P_change), error::wallet_internal_error, "Failed to get change output public key"); - m_subaddresses[P_change] = subaddr_index_cn;//tx_scan_info[o].received->index;//{0,0}; - if (tx.version >= TRANSACTION_VERSION_N_OUTS) - m_salvium_txs.insert({tx.return_address_list[local_output_index], m_transfers.size()-1}); - else - m_salvium_txs.insert({tx.return_address, m_transfers.size()-1}); - - if (tx.type == cryptonote::transaction_type::STAKE || tx.type == cryptonote::transaction_type::AUDIT) { - // Additionally, with STAKE and AUDIT TXs, we need to update our "balance staked" subtotal, because otherwise our balance is out by the staked coins until they mature! - // SRCG: must remember to deduct the number of staked coins when they mature!! - LOG_PRINT_L1("***** STAKED/AUDITED COINS : " << tx.amount_burnt << " *****"); - m_locked_coins.insert({P_change, {0, tx.amount_burnt, tx.source_asset_type}}); - } - - } - // money received callbacks LOG_PRINT_L0("Received money: " << print_money(td.amount()) << ", with tx: " << txid); - if (!ignore_callbacks && 0 != m_callback) + if (!ignore_callbacks && 0 != m_callback && td.m_amount > 0) m_callback->on_money_received(height, txid, tx, td.m_amount, td.asset_type, 0, td.m_subaddr_index, spends_one_of_ours(tx), td.m_tx.unlock_time, td.m_td_origin_idx); } @@ -3000,8 +2837,22 @@ void wallet2::process_new_scanned_transaction( { if (subaddr_account && i->first.major == *subaddr_account) { - // tx_amounts_individual_outs.erase(i->first); - // i = tx_money_got_in_outs.erase(i); + // save the change output key in our subaddress_map, so we can receive money to it later + // from protocol or return txs + for (const auto entry: tx_amounts_individual_outs[i->first]) { + const crypto::public_key &onetime_address = std::get<1>(entry); + m_account.insert_subaddresses({{onetime_address, {i->first.major, i->first.minor}}}); + // save to m_subaddresses as well, so that we can populate account subaddress map + // when we open the wallet first time. + m_subaddresses[onetime_address] = i->first; + + // update m_salvium_txs + m_salvium_txs.insert({onetime_address, m_transfers.size()-1}); + } + + // delete change output from amounts + tx_amounts_individual_outs.erase(i->first); + // delete individual change asset output for (auto assets_it = i->second.begin(); assets_it != i->second.end();) { if (assets_it->first == source_asset) @@ -3027,12 +2878,16 @@ void wallet2::process_new_scanned_transaction( { for (const auto& j : i.second) { + amounts_container amounts; + for (const auto& entry: tx_amounts_individual_outs[i.first]) { + amounts.push_back(std::get<0>(entry)); + } payment_details payment; payment.m_tx_hash = txid; payment.m_fee = fee; payment.m_asset_type = j.first; payment.m_amount = j.second; - payment.m_amounts = tx_amounts_individual_outs[i.first]; + payment.m_amounts = amounts; payment.m_block_height = height; payment.m_unlock_time = tx.unlock_time; payment.m_timestamp = ts; @@ -5536,7 +5391,6 @@ bool wallet2::load_keys_buf(const std::string& keys_buf, const epee::wipeable_st if (r) { m_account.set_carrot_keys(); - m_account.set_cn_subaddress_map(m_subaddresses); if (!m_is_background_wallet) setup_keys(password); @@ -6720,6 +6574,7 @@ void wallet2::load(const std::string& wallet_, const epee::wipeable_string& pass if (get_num_subaddress_accounts() == 0) add_subaddress_account(tr("Primary account")); + m_account.insert_subaddresses(m_subaddresses); try { @@ -10543,7 +10398,7 @@ void wallet2::transfer_selected_rct(std::vector> circ_amounts; THROW_WALLET_EXCEPTION_IF(!get_circulating_supply(circ_amounts), error::wallet_internal_error, "Failed to get circulating supply"); // make a normal tx - bool r = cryptonote::construct_tx_and_get_tx_key(a_keys/*m_account.get_keys()*/, m_subaddresses, sources, splitted_dsts, hf_version, source_asset, dest_asset, tx_type, change_dts.addr, extra, tx, unlock_time, tx_key, additional_tx_keys, true, rct_config, use_view_tags); + bool r = cryptonote::construct_tx_and_get_tx_key(a_keys/*m_account.get_keys()*/, m_account.get_subaddress_map(), sources, splitted_dsts, hf_version, source_asset, dest_asset, tx_type, change_dts.addr, extra, tx, unlock_time, tx_key, additional_tx_keys, true, rct_config, use_view_tags); LOG_PRINT_L2("constructed tx, r="< wallet2::create_transactions_2(std::vector ptx_vector; ptx_vector.reserve(tx_proposals.size()); for (const auto &tx_proposal : tx_proposals) @@ -11686,7 +11541,7 @@ std::vector wallet2::create_transactions_all(uint64_t below const bool do_carrot_tx_construction = use_fork_rules(HF_VERSION_CARROT); if (do_carrot_tx_construction) { - const auto tx_proposals = tools::wallet::make_carrot_transaction_proposals_wallet2_sweep_all(*this, below, address, is_subaddress, outputs, priority, extra, subaddr_account, subaddr_indices); + const auto tx_proposals = tools::wallet::make_carrot_transaction_proposals_wallet2_sweep_all(*this, below, address, is_subaddress, outputs, priority, extra, tx_type, subaddr_account, subaddr_indices); std::vector ptx_vector; ptx_vector.reserve(tx_proposals.size()); // TODO: use CLSAGS here.. @@ -11772,12 +11627,12 @@ std::vector wallet2::create_transactions_all(uint64_t below return create_transactions_from(address, tx_type, asset_type, is_subaddress, outputs, unused_transfers_indices, unused_dust_indices, fake_outs_count, unlock_time, priority, extra); } -std::vector wallet2::create_transactions_single(const crypto::key_image &ki, const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector& extra) +std::vector wallet2::create_transactions_single(const crypto::key_image &ki, const cryptonote::account_public_address &address, const cryptonote::transaction_type tx_type, bool is_subaddress, const size_t outputs, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector& extra) { const bool do_carrot_tx_construction = use_fork_rules(HF_VERSION_CARROT); if (do_carrot_tx_construction) { - const auto tx_proposals = tools::wallet::make_carrot_transaction_proposals_wallet2_sweep(*this, {ki}, address, is_subaddress, outputs, priority, extra); + const auto tx_proposals = tools::wallet::make_carrot_transaction_proposals_wallet2_sweep(*this, {ki}, address, is_subaddress, outputs, priority, extra, tx_type); std::vector ptx_vector; ptx_vector.reserve(tx_proposals.size()); // TODO: use CLSAGS here.. @@ -11829,6 +11684,108 @@ std::vector wallet2::create_transactions_return(std::vector THROW_WALLET_EXCEPTION_IF(td.m_txid != td_origin.m_txid, error::wallet_internal_error, tr("TX hashes do not match for inputs to return_payment")); THROW_WALLET_EXCEPTION_IF(td.asset_type != td_origin.asset_type, error::wallet_internal_error, tr("TX asset_type values do not match for inputs to return_payment")); } + + const bool do_carrot_tx_construction = use_fork_rules(HF_VERSION_CARROT); + if (do_carrot_tx_construction) + { + // calculate the return address + // 1. parse tx extra + std::vector main_tx_ephemeral_pubkeys; + std::vector additional_tx_ephemeral_pubkeys; + cryptonote::blobdata tx_extra_nonce; + if (!wallet::parse_tx_extra_for_scanning(td_origin.m_tx.extra, + td_origin.m_tx.vout.size(), + main_tx_ephemeral_pubkeys, + additional_tx_ephemeral_pubkeys, + tx_extra_nonce)) + MWARNING("Return transaction extra has unsupported format: "); + + THROW_WALLET_EXCEPTION_IF(main_tx_ephemeral_pubkeys.empty() && additional_tx_ephemeral_pubkeys.empty(), error::wallet_internal_error, + "Transaction missing ephemeral pubkeys"); + const bool is_carrot = carrot::is_carrot_transaction_v1(td_origin.m_tx); + THROW_WALLET_EXCEPTION_IF(!is_carrot, error::wallet_internal_error, + "Return payment is only supported for Carrot transactions"); + + // 2. perform ECDH derivations + std::vector main_derivations; + std::vector additional_derivations; + wallet::perform_ecdh_derivations( + epee::to_span(main_tx_ephemeral_pubkeys), + epee::to_span(additional_tx_ephemeral_pubkeys), + m_account.get_keys().k_view_incoming, + m_account.get_keys().get_device(), + is_carrot, + main_derivations, + additional_derivations + ); + + // 3. input context + carrot::input_context_t input_context = carrot::gen_input_context(); + if (td_origin.m_tx.vin[0].type() == typeid(cryptonote::txin_to_key)) { + input_context = carrot::make_carrot_input_context(boost::get(td_origin.m_tx.vin[0]).k_image); + } else if (td_origin.m_tx.vin[0].type() == typeid(cryptonote::txin_gen)) { + input_context = carrot::make_carrot_input_context_coinbase(boost::get(td_origin.m_tx.vin[0]).height); + } else { + THROW_WALLET_EXCEPTION_IF(true, error::wallet_internal_error, "Unsupported input type for return_payment"); + } + const mx25519_pubkey origin_tx_shared_secret_unctx = carrot::raw_byte_convert(main_derivations[0]); + + // 4. compute m_return + crypto::public_key output_key; + carrot::encrypted_return_pubkey_t m_return; + THROW_WALLET_EXCEPTION_IF( + !cryptonote::get_output_public_key(td_origin.m_tx.vout[td_origin.m_internal_output_index], output_key), + error::wallet_internal_error, + "Failed to identify output key" + ); + carrot::make_sparc_return_pubkey_encryption_mask( + origin_tx_shared_secret_unctx.data, + input_context, + output_key, + m_return + ); + + // 5. compute K_return from m_return + carrot::encrypted_return_pubkey_t K_return; + crypto::public_key return_pub; + std::memcpy( + K_return.bytes, + td_origin.m_tx.return_address_list[td_origin.m_internal_output_index].data, + sizeof(carrot::encrypted_return_pubkey_t) + ); + K_return = K_return ^ m_return; + static_assert(sizeof(K_return.bytes) == sizeof(return_pub.data), "Size mismatch"); + std::memcpy(return_pub.data, K_return.bytes, sizeof(carrot::encrypted_return_pubkey_t)); + + // 6. compute the change index + crypto::secret_key z_i; + derivation_to_scalar(main_derivations[0], td_origin.m_internal_output_index, z_i); + struct { + char domain_separator[8]; + crypto::secret_key output_index_key; + } buf; + std::memset(buf.domain_separator, 0x0, sizeof(buf.domain_separator)); + std::strncpy(buf.domain_separator, "CHG_IDX", 8); + std::memcpy(buf.output_index_key.data, z_i.data, sizeof(crypto::secret_key)); + uint8_t eci_data = td_origin.m_tx.return_address_change_mask[td_origin.m_internal_output_index]; + crypto::secret_key eci_out; + keccak((uint8_t *)&buf, sizeof(buf), (uint8_t*)&eci_out, sizeof(eci_out)); + uint8_t change_index = eci_data ^ eci_out.data[0]; + + crypto::public_key change_key; + THROW_WALLET_EXCEPTION_IF( + !cryptonote::get_output_public_key(td_origin.m_tx.vout[change_index], change_key), + error::wallet_internal_error, + "Failed to identify change output" + ); + + // 7. Make a destination address for the return + cryptonote::account_public_address address; + address.m_spend_public_key = change_key; + address.m_view_public_key = return_pub; + + return create_transactions_from(address, cryptonote::transaction_type::RETURN, asset_type, is_subaddress, outputs, transfers_indices, unused_dust_indices, fake_outs_count, unlock_time, priority, extra); + } // To return a payment, we need to know the y value to process the F value // ...but the y value is calculated differently depending on the original TX @@ -11952,12 +11909,11 @@ std::vector wallet2::create_transactions_from(const crypton for (const size_t transfer_idx : unused_dust_indices) input_key_images.push_back(m_transfers.at(transfer_idx).m_key_image); - const auto tx_proposals = tools::wallet::make_carrot_transaction_proposals_wallet2_sweep(*this, input_key_images, address, is_subaddress, outputs, priority, extra); + const auto tx_proposals = tools::wallet::make_carrot_transaction_proposals_wallet2_sweep(*this, input_key_images, address, is_subaddress, outputs, priority, extra, tx_type); std::vector ptx_vector; ptx_vector.reserve(tx_proposals.size()); - // TODO: use CLSAGS here.. - // for (const auto &tx_proposal : tx_proposals) - // ptx_vector.push_back(tools::wallet::finalize_all_proofs_from_transfer_details_as_pending_tx(tx_proposal, *this)); + for (const auto &tx_proposal : tx_proposals) + ptx_vector.push_back(tools::wallet::finalize_all_proofs_from_transfer_details_as_pending_tx(tx_proposal, *this)); return ptx_vector; } diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 9efd9c51b..55ce376c7 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -1272,7 +1272,7 @@ private: bool parse_tx_from_str(const std::string &signed_tx_st, std::vector &ptx, std::function accept_func); std::vector create_transactions_2(std::vector dsts, const std::string& source_asset, const std::string& dest_asset, const cryptonote::transaction_type tx_type, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector& extra, uint32_t subaddr_account, std::set subaddr_indices, const unique_index_container& subtract_fee_from_outputs = {}); // pass subaddr_indices by value on purpose std::vector create_transactions_all(uint64_t below, cryptonote::transaction_type tx_type, const std::string& asset_type, const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector& extra, uint32_t subaddr_account, std::set subaddr_indices); - std::vector create_transactions_single(const crypto::key_image &ki, const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector& extra); + std::vector create_transactions_single(const crypto::key_image &ki, const cryptonote::account_public_address &address, const cryptonote::transaction_type tx_type, bool is_subaddress, const size_t outputs, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector& extra); std::vector create_transactions_return(std::vector transfers_indices); std::vector create_transactions_from(const cryptonote::account_public_address &address, const cryptonote::transaction_type tx_type, const std::string& asset_type, bool is_subaddress, const size_t outputs, std::vector unused_transfers_indices, std::vector unused_dust_indices, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector& extra); bool sanity_check(const std::vector &ptx_vector, const std::vector& dsts, const unique_index_container& subtract_fee_from_outputs = {}) const; @@ -1947,6 +1947,16 @@ private: std::optional &ki_out, bool &password_failure_inout); + void scan_protocol_tx_output( + const cryptonote::transaction& tx, + const size_t local_output_index, + const crypto::public_key &onetime_address, + const uint64_t height, + uint64_t &td_origin_idx_out, + crypto::public_key locked_coins_pk_out, + wallet::enote_view_incoming_scan_info_t &enote_scan_info_out + ); + void process_new_transaction( const crypto::hash &txid, const cryptonote::transaction& tx, diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index 92fac9911..8fce8198a 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -1873,7 +1873,7 @@ namespace tools { uint64_t mixin = m_wallet->adjust_mixin(req.ring_size ? req.ring_size - 1 : 0); uint32_t priority = m_wallet->adjust_priority(req.priority); - std::vector ptx_vector = m_wallet->create_transactions_single(ki, dsts[0].addr, dsts[0].is_subaddress, req.outputs, mixin, req.unlock_time, priority, extra); + std::vector ptx_vector = m_wallet->create_transactions_single(ki, dsts[0].addr, cryptonote::transaction_type::TRANSFER, dsts[0].is_subaddress, req.outputs, mixin, req.unlock_time, priority, extra); if (ptx_vector.empty()) { diff --git a/tests/unit_tests/carrot_core.cpp b/tests/unit_tests/carrot_core.cpp index 68054e3ea..3756e0250 100644 --- a/tests/unit_tests/carrot_core.cpp +++ b/tests/unit_tests/carrot_core.cpp @@ -723,6 +723,7 @@ static void subtest_2out_transfer_get_output_enote_proposals_completeness(const tx_first_key_image, enote_proposals, encrypted_payment_id, + cryptonote::transaction_type::TRANSFER, change_index, normal_payments_indices); diff --git a/tests/unit_tests/carrot_fcmp.cpp b/tests/unit_tests/carrot_fcmp.cpp index 341aeb21a..dd92e0f72 100644 --- a/tests/unit_tests/carrot_fcmp.cpp +++ b/tests/unit_tests/carrot_fcmp.cpp @@ -339,6 +339,7 @@ TEST(carrot_fcmp, receive_scan_spend_and_verify_serialized_carrot_tx) /*selfsend_payment_proposals=*/{}, fee_per_weight, /*extra=*/{}, + cryptonote::transaction_type::TRANSFER, [&input_info_by_ki] ( const boost::multiprecision::int128_t&, @@ -385,6 +386,7 @@ TEST(carrot_fcmp, receive_scan_spend_and_verify_serialized_carrot_tx) tx_proposal.key_images_sorted.at(0), output_enote_proposals, encrypted_payment_id, + cryptonote::transaction_type::TRANSFER, change_index, normal_payments_indices); @@ -415,7 +417,6 @@ TEST(carrot_fcmp, receive_scan_spend_and_verify_serialized_carrot_tx) tx_proposal.key_images_sorted, tx_proposal.sources, tx_proposal.fee, - 0, // change_index {}, // change_masks encrypted_payment_id); diff --git a/tests/unit_tests/carrot_impl.cpp b/tests/unit_tests/carrot_impl.cpp index ac969e6af..4ddd9e03d 100644 --- a/tests/unit_tests/carrot_impl.cpp +++ b/tests/unit_tests/carrot_impl.cpp @@ -160,6 +160,7 @@ static void subtest_multi_account_transfer_over_transaction(const unittest_trans tx_preproposal.fee_per_weight, 5, tx_preproposal.extra_extra, + cryptonote::transaction_type::TRANSFER, make_fake_input_selection_callback(), ss_keys.carrot_account_spend_pubkey, {{0, 0}, AddressDeriveType::Carrot}, @@ -214,6 +215,7 @@ static void subtest_multi_account_transfer_over_transaction(const unittest_trans parsed_key_images.at(0), rederived_output_enote_proposals, rederived_encrypted_payment_id, + cryptonote::transaction_type::TRANSFER, change_index, normal_payments_indices); EXPECT_EQ(*parsed_encrypted_payment_id, rederived_encrypted_payment_id); diff --git a/tests/unit_tests/carrot_legacy.cpp b/tests/unit_tests/carrot_legacy.cpp index 5923ec713..c686e8321 100644 --- a/tests/unit_tests/carrot_legacy.cpp +++ b/tests/unit_tests/carrot_legacy.cpp @@ -197,6 +197,7 @@ static void subtest_legacy_2out_transfer_get_output_enote_proposals_completeness tx_first_key_image, enote_proposals, encrypted_payment_id, + cryptonote::transaction_type::TRANSFER, change_index, normal_payments_indices); diff --git a/tests/unit_tests/carrot_sparc.cpp b/tests/unit_tests/carrot_sparc.cpp index 9b46a0fb6..b16c6c2d9 100644 --- a/tests/unit_tests/carrot_sparc.cpp +++ b/tests/unit_tests/carrot_sparc.cpp @@ -226,6 +226,7 @@ std::tuple, crypto::public_key> make_return_ get_output_proposal_return_v1( proposal_return, tx_return_first_key_image, + &bob.s_view_balance_dev, enote_proposal_return, encrypted_payment_id_return ); diff --git a/tests/unit_tests/tx_construction_helpers.cpp b/tests/unit_tests/tx_construction_helpers.cpp index af3596b4a..edfb4e75a 100644 --- a/tests/unit_tests/tx_construction_helpers.cpp +++ b/tests/unit_tests/tx_construction_helpers.cpp @@ -409,6 +409,7 @@ cryptonote::transaction construct_carrot_pruned_transaction_fake_inputs( fake_fee_per_weight, 5, /*extra=*/{}, + cryptonote::transaction_type::TRANSFER, std::move(select_inputs), acc_keys.m_account_address.m_spend_public_key, {{0, 0}, carrot::AddressDeriveType::PreCarrot}, diff --git a/tests/unit_tests/wallet_tx_builder.cpp b/tests/unit_tests/wallet_tx_builder.cpp index 5f12ab662..24d514a8f 100644 --- a/tests/unit_tests/wallet_tx_builder.cpp +++ b/tests/unit_tests/wallet_tx_builder.cpp @@ -167,6 +167,7 @@ TEST(wallet_tx_builder, make_carrot_transaction_proposals_wallet2_transfer_1) /*fee_per_weight=*/1, 5, /*extra=*/{}, + /*tx_type=*/cryptonote::transaction_type::TRANSFER, /*subaddr_account=*/0, /*subaddr_indices=*/{}, {}, @@ -229,6 +230,7 @@ TEST(wallet_tx_builder, make_carrot_transaction_proposals_wallet2_transfer_2) /*fee_per_weight=*/1, 5, /*extra=*/{}, + /*tx_type=*/cryptonote::transaction_type::TRANSFER, /*subaddr_account=*/spending_subaddr_account, /*subaddr_indices=*/{}, {}, @@ -288,9 +290,11 @@ TEST(wallet_tx_builder, make_carrot_transaction_proposals_wallet2_sweep_1) const tools::wallet2::transfer_container transfers{gen_transfer_details()}; + tools::wallet2 w; const std::vector tx_proposals = tools::wallet::make_carrot_transaction_proposals_wallet2_sweep( - transfers, - {{alice.get_keys().m_account_address.m_spend_public_key, {}}}, + // transfers, + // {{alice.get_keys().m_account_address.m_spend_public_key, {}}}, + w, {transfers.front().m_key_image}, bob.get_keys().m_account_address, /*is_subaddress=*/false, @@ -298,6 +302,7 @@ TEST(wallet_tx_builder, make_carrot_transaction_proposals_wallet2_sweep_1) /*fee_per_weight=*/1, 5, /*extra=*/{}, + /*tx_type=*/cryptonote::transaction_type::TRANSFER, transfers.front().m_block_height + CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE); ASSERT_EQ(1, tx_proposals.size()); const carrot::CarrotTransactionProposalV1 &tx_proposal = tx_proposals.at(0); @@ -322,10 +327,11 @@ TEST(wallet_tx_builder, make_carrot_transaction_proposals_wallet2_sweep_2) bob.generate(); const tools::wallet2::transfer_container transfers{gen_transfer_details()}; - + tools::wallet2 w; const std::vector tx_proposals = tools::wallet::make_carrot_transaction_proposals_wallet2_sweep( - transfers, - {{alice.get_keys().m_account_address.m_spend_public_key, {}}}, + // transfers, + // {{alice.get_keys().m_account_address.m_spend_public_key, {}}}, + w, {transfers.front().m_key_image}, bob.get_keys().m_account_address, /*is_subaddress=*/false, @@ -333,6 +339,7 @@ TEST(wallet_tx_builder, make_carrot_transaction_proposals_wallet2_sweep_2) /*fee_per_weight=*/1, 5, /*extra=*/{}, + /*tx_type=*/cryptonote::transaction_type::TRANSFER, transfers.front().m_block_height + CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE); ASSERT_EQ(1, tx_proposals.size()); const carrot::CarrotTransactionProposalV1 &tx_proposal = tx_proposals.at(0); @@ -365,10 +372,11 @@ TEST(wallet_tx_builder, make_carrot_transaction_proposals_wallet2_sweep_3) alice.generate(); const tools::wallet2::transfer_container transfers{gen_transfer_details()}; - + tools::wallet2 w; const std::vector tx_proposals = tools::wallet::make_carrot_transaction_proposals_wallet2_sweep( - transfers, - {{alice.get_keys().m_account_address.m_spend_public_key, {}}}, + // transfers, + // {{alice.get_keys().m_account_address.m_spend_public_key, {}}}, + w, {transfers.front().m_key_image}, alice.get_keys().m_account_address, /*is_subaddress=*/false, @@ -376,6 +384,7 @@ TEST(wallet_tx_builder, make_carrot_transaction_proposals_wallet2_sweep_3) /*fee_per_weight=*/1, 5, /*extra=*/{}, + /*tx_type=*/cryptonote::transaction_type::TRANSFER, transfers.front().m_block_height + CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE); ASSERT_EQ(1, tx_proposals.size()); const carrot::CarrotTransactionProposalV1 &tx_proposal = tx_proposals.at(0); @@ -442,9 +451,11 @@ TEST(wallet_tx_builder, make_carrot_transaction_proposals_wallet2_sweep_4) const size_t n_dests_per_tx = 4; // make tx proposals + tools::wallet2 w; const std::vector tx_proposals = tools::wallet::make_carrot_transaction_proposals_wallet2_sweep( - transfers, - {{alice.get_keys().m_account_address.m_spend_public_key, {}}}, + // transfers, + // {{alice.get_keys().m_account_address.m_spend_public_key, {}}}, + w, selected_key_images, bob.get_keys().m_account_address, /*is_subaddress=*/false, @@ -452,6 +463,7 @@ TEST(wallet_tx_builder, make_carrot_transaction_proposals_wallet2_sweep_4) /*fee_per_weight=*/1, 5, /*extra=*/{}, + /*tx_type=*/cryptonote::transaction_type::TRANSFER, top_block_index); ASSERT_EQ(4, tx_proposals.size()); @@ -524,9 +536,11 @@ TEST(wallet_tx_builder, make_carrot_transaction_proposals_wallet2_sweep_5) const size_t n_dests_per_tx = 8; // make tx proposals + tools::wallet2 w; const std::vector tx_proposals = tools::wallet::make_carrot_transaction_proposals_wallet2_sweep( - transfers, - {{alice.get_keys().m_account_address.m_spend_public_key, {}}}, + // transfers, + // {{alice.get_keys().m_account_address.m_spend_public_key, {}}}, + w, selected_key_images, alice.get_keys().m_account_address, /*is_subaddress=*/false, @@ -534,6 +548,7 @@ TEST(wallet_tx_builder, make_carrot_transaction_proposals_wallet2_sweep_5) /*fee_per_weight=*/1, 5, /*extra=*/{}, + /*tx_type=*/cryptonote::transaction_type::TRANSFER, top_block_index); ASSERT_EQ(8, tx_proposals.size()); @@ -609,9 +624,11 @@ TEST(wallet_tx_builder, make_carrot_transaction_proposals_wallet2_sweep_6) const size_t n_dests_per_tx = 2; // make tx proposals + tools::wallet2 w; const std::vector tx_proposals = tools::wallet::make_carrot_transaction_proposals_wallet2_sweep( - transfers, - {{alice.get_keys().m_account_address.m_spend_public_key, {}}}, + // transfers, + // {{alice.get_keys().m_account_address.m_spend_public_key, {}}}, + w, selected_key_images, alice.get_keys().m_account_address, /*is_subaddress=*/false, @@ -619,6 +636,7 @@ TEST(wallet_tx_builder, make_carrot_transaction_proposals_wallet2_sweep_6) /*fee_per_weight=*/1, 5, /*extra=*/{}, + /*tx_type=*/cryptonote::transaction_type::TRANSFER, top_block_index); ASSERT_EQ(1, tx_proposals.size()); const carrot::CarrotTransactionProposalV1 &tx_proposal = tx_proposals.at(0);