diff --git a/src/carrot_impl/carrot_tx_builder_utils.cpp b/src/carrot_impl/carrot_tx_builder_utils.cpp index 58c583a..93975b3 100644 --- a/src/carrot_impl/carrot_tx_builder_utils.cpp +++ b/src/carrot_impl/carrot_tx_builder_utils.cpp @@ -37,6 +37,7 @@ extern "C" #include "crypto/crypto-ops.h" } #include "cryptonote_basic/cryptonote_format_utils.h" +#include "ringct/rctSigs.h" #include "ringct/rctOps.h" //third party headers @@ -205,7 +206,7 @@ void make_carrot_transaction_proposal_v1(const std::vector 0; -} - void make_carrot_transaction_proposal_v1(const std::vector &normal_payment_proposals, const std::vector &selfsend_payment_proposals, const rct::xmr_amount fee_per_weight, @@ -112,10 +107,14 @@ void make_carrot_transaction_proposal_v1_sweep( const crypto::public_key &account_spend_pubkey, CarrotTransactionProposalV1 &tx_proposal_out); +void make_signable_tx_hash_from_carrot_transaction_proposal_v1(const CarrotTransactionProposalV1 &tx_proposal, + const view_balance_secret_device *s_view_balance_dev, + const view_incoming_key_device *k_view_dev, + crypto::hash &signable_tx_hash_out); + void make_pruned_transaction_from_carrot_proposal_v1(const CarrotTransactionProposalV1 &tx_proposal, const view_balance_secret_device *s_view_balance_dev, const view_incoming_key_device *k_view_dev, - const crypto::public_key &account_spend_pubkey, cryptonote::transaction &pruned_tx_out); } //namespace carrot diff --git a/src/carrot_impl/carrot_tx_format_utils.cpp b/src/carrot_impl/carrot_tx_format_utils.cpp index 475f01d..fde0600 100644 --- a/src/carrot_impl/carrot_tx_format_utils.cpp +++ b/src/carrot_impl/carrot_tx_format_utils.cpp @@ -281,12 +281,14 @@ bool try_load_carrot_from_transaction_v1(const cryptonote::transaction &tx, std::vector enote_ephemeral_pubkeys; if (!try_load_carrot_extra_v1(tx.extra, enote_ephemeral_pubkeys, encrypted_payment_id_out)) return false; - else if (enote_ephemeral_pubkeys.size() > nouts) + + const size_t n_ephemeral = enote_ephemeral_pubkeys.size(); + if (n_ephemeral < 0 || n_ephemeral > nouts) return false; // collect D_e for (size_t i = 0; i < enotes_out.size(); ++i) - enotes_out[i].enote_ephemeral_pubkey = enote_ephemeral_pubkeys.at(std::min(i, nouts - 1)); + enotes_out[i].enote_ephemeral_pubkey = enote_ephemeral_pubkeys.at(std::min(i, n_ephemeral - 1)); return true; } diff --git a/tests/unit_tests/carrot_impl.cpp b/tests/unit_tests/carrot_impl.cpp index 21209c9..d4ae54b 100644 --- a/tests/unit_tests/carrot_impl.cpp +++ b/tests/unit_tests/carrot_impl.cpp @@ -168,7 +168,6 @@ static void subtest_multi_account_transfer_over_transaction(const unittest_trans make_pruned_transaction_from_carrot_proposal_v1(tx_proposal, &ss_keys.s_view_balance_dev, &ss_keys.k_view_incoming_dev, - ss_keys.carrot_account_spend_pubkey, tx); // calculate acceptable fee margin between proposed amount and actual amount for subtractable outputs diff --git a/tests/unit_tests/wallet_scanning.cpp b/tests/unit_tests/wallet_scanning.cpp index ebcb771..9f24e13 100644 --- a/tests/unit_tests/wallet_scanning.cpp +++ b/tests/unit_tests/wallet_scanning.cpp @@ -368,7 +368,6 @@ static cryptonote::transaction construct_carrot_pruned_transaction_fake_inputs( carrot::make_pruned_transaction_from_carrot_proposal_v1(tx_proposal, /*s_view_balance_dev=*/nullptr, &k_view_dev, - acc_keys.m_account_address.m_spend_public_key, tx); return tx; diff --git a/tests/unit_tests/wallet_tx_builder.cpp b/tests/unit_tests/wallet_tx_builder.cpp index bf0af4a..e6e50f1 100644 --- a/tests/unit_tests/wallet_tx_builder.cpp +++ b/tests/unit_tests/wallet_tx_builder.cpp @@ -135,3 +135,90 @@ TEST(wallet_tx_builder, input_selection_basic) } ASSERT_EQ(selected_transfer_indices.size(), matched_transfer_indices.size()); } +//---------------------------------------------------------------------------------------------------------------------- +TEST(wallet_tx_builder, make_carrot_transaction_proposal_wallet2_transfer_1) +{ + cryptonote::account_base alice; + alice.generate(); + cryptonote::account_base bob; + bob.generate(); + + const tools::wallet2::transfer_container transfers{ + gen_transfer_details(), + gen_transfer_details()}; + + const rct::xmr_amount out_amount = rct::randXmrAmount(transfers.front().amount() / 2); + + const std::vector dsts{ + cryptonote::tx_destination_entry(out_amount, bob.get_keys().m_account_address, false) + }; + + const uint64_t top_block_index = std::max(transfers.front().m_block_height, transfers.back().m_block_height) + + CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE; + + const carrot::CarrotTransactionProposalV1 tx_proposal = tools::wallet::make_carrot_transaction_proposal_wallet2_transfer( + transfers, + /*subaddress_map=*/{}, + dsts, + /*fee_per_weight=*/1, + /*extra=*/{}, + /*subaddr_account=*/0, + /*subaddr_indices=*/{}, + /*ignore_above=*/MONEY_SUPPLY, + /*ignore_below=*/0, + top_block_index, + alice); + + std::vector expected_key_images{ + transfers.front().m_key_image, + transfers.back().m_key_image}; + std::sort(expected_key_images.begin(), + expected_key_images.end(), + std::greater{}); + + // Assert basic length facts about tx proposal + ASSERT_EQ(2, tx_proposal.key_images_sorted.size()); // we always try 2 when available + EXPECT_EQ(expected_key_images, tx_proposal.key_images_sorted); + ASSERT_EQ(1, tx_proposal.normal_payment_proposals.size()); + ASSERT_EQ(1, tx_proposal.selfsend_payment_proposals.size()); + EXPECT_EQ(0, tx_proposal.extra.size()); + + // Assert amounts + EXPECT_EQ(out_amount, tx_proposal.normal_payment_proposals.front().amount); + EXPECT_EQ(out_amount + tx_proposal.selfsend_payment_proposals.front().proposal.amount + tx_proposal.fee, + transfers.front().amount() + transfers.back().amount()); +} +//---------------------------------------------------------------------------------------------------------------------- +TEST(wallet_tx_builder, make_carrot_transaction_proposal_wallet2_sweep_1) +{ + cryptonote::account_base alice; + alice.generate(); + cryptonote::account_base bob; + bob.generate(); + + const tools::wallet2::transfer_container transfers{gen_transfer_details()}; + + const carrot::CarrotTransactionProposalV1 tx_proposal = tools::wallet::make_carrot_transaction_proposal_wallet2_sweep( + transfers, + /*subaddress_map=*/{}, + {transfers.front().m_key_image}, + bob.get_keys().m_account_address, + /*is_subaddress=*/false, + /*n_dests=*/1, + /*fee_per_weight=*/1, + /*extra=*/{}, + transfers.front().m_block_height + CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE, + alice); + + // Assert basic length facts about tx proposal + ASSERT_EQ(1, tx_proposal.key_images_sorted.size()); + EXPECT_EQ(transfers.front().m_key_image, tx_proposal.key_images_sorted.front()); + ASSERT_EQ(1, tx_proposal.normal_payment_proposals.size()); + ASSERT_EQ(1, tx_proposal.selfsend_payment_proposals.size()); + EXPECT_EQ(0, tx_proposal.extra.size()); + + // Assert amounts + EXPECT_EQ(0, tx_proposal.selfsend_payment_proposals.front().proposal.amount); + EXPECT_EQ(transfers.front().amount(), tx_proposal.fee + tx_proposal.normal_payment_proposals.front().amount); +} +//----------------------------------------------------------------------------------------------------------------------