From 87831eaf1a9afb6611dbce94ee5425f313c484d2 Mon Sep 17 00:00:00 2001 From: sowle Date: Sat, 21 Oct 2023 23:40:39 +0200 Subject: [PATCH 01/12] coretests: ionic_swap_exact_amounts_test added which uncovers a bug in separately signed tx creation --- tests/core_tests/chaingen_main.cpp | 3 +- tests/core_tests/ionic_swap_tests.cpp | 164 ++++++++++++++++++++++++++ tests/core_tests/ionic_swap_tests.h | 7 ++ 3 files changed, 173 insertions(+), 1 deletion(-) diff --git a/tests/core_tests/chaingen_main.cpp b/tests/core_tests/chaingen_main.cpp index 9585c263..67a87489 100644 --- a/tests/core_tests/chaingen_main.cpp +++ b/tests/core_tests/chaingen_main.cpp @@ -1250,7 +1250,8 @@ int main(int argc, char* argv[]) GENERATE_AND_PLAY(zarcanum_basic_test); GENERATE_AND_PLAY_HF(multiassets_basic_test, "4-*"); - GENERATE_AND_PLAY(ionic_swap_basic_test); + GENERATE_AND_PLAY(ionic_swap_basic_test); + GENERATE_AND_PLAY_HF(ionic_swap_exact_amounts_test, "4-*"); GENERATE_AND_PLAY(zarcanum_test_n_inputs_validation); GENERATE_AND_PLAY(zarcanum_gen_time_balance); GENERATE_AND_PLAY(zarcanum_txs_with_big_shuffled_decoy_set_shuffled); diff --git a/tests/core_tests/ionic_swap_tests.cpp b/tests/core_tests/ionic_swap_tests.cpp index 0ec0602d..5281c735 100644 --- a/tests/core_tests/ionic_swap_tests.cpp +++ b/tests/core_tests/ionic_swap_tests.cpp @@ -293,3 +293,167 @@ bool ionic_swap_basic_test::c1(currency::core& c, size_t ev_index, const std::ve return true; } +//---------------------------------------------------------------------------------------------------- + +ionic_swap_exact_amounts_test::ionic_swap_exact_amounts_test() +{ + REGISTER_CALLBACK_METHOD(ionic_swap_exact_amounts_test, c1); +} + +bool ionic_swap_exact_amounts_test::generate(std::vector& events) const +{ + uint64_t ts = test_core_time::get_time(); + m_accounts.resize(TOTAL_ACCS_COUNT); + currency::account_base& miner_acc = m_accounts[MINER_ACC_IDX]; miner_acc.generate(); miner_acc.set_createtime(ts); + currency::account_base& alice_acc = m_accounts[ALICE_ACC_IDX]; alice_acc.generate(); alice_acc.set_createtime(ts); + currency::account_base& bob_acc = m_accounts[BOB_ACC_IDX]; bob_acc.generate(); bob_acc.set_createtime(ts); + currency::account_base& carol_acc = m_accounts[CAROL_ACC_IDX]; carol_acc.generate(); carol_acc.set_createtime(ts); + + MAKE_GENESIS_BLOCK(events, blk_0, miner_acc, ts); + + // rebuild genesis miner tx + std::vector destinations; + destinations.emplace_back(MK_TEST_COINS(21), alice_acc.get_public_address()); + destinations.emplace_back(MK_TEST_COINS(21), miner_acc.get_public_address()); // decoy (later Alice will spend her output using mixins) + destinations.emplace_back(MK_TEST_COINS(21), miner_acc.get_public_address()); // decoy + destinations.emplace_back(COIN, miner_acc.get_public_address()); + // leftover amount will be also send to miner + CHECK_AND_ASSERT_MES(replace_coinbase_in_genesis_block(destinations, generator, events, blk_0), false, ""); + + DO_CALLBACK(events, "configure_core"); // default configure_core callback will initialize core runtime config with m_hardforks + REWIND_BLOCKS_N(events, blk_0r, blk_0, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 1); + + DO_CALLBACK_PARAMS(events, "check_hardfork_active", static_cast(ZANO_HARDFORK_04_ZARCANUM)); + + DO_CALLBACK(events, "c1"); + + return true; +} + +bool ionic_swap_exact_amounts_test::c1(currency::core& c, size_t ev_index, const std::vector& events) +{ + bool r = false; + std::shared_ptr miner_wlt = init_playtime_test_wallet(events, c, MINER_ACC_IDX); + miner_wlt->refresh(); + + asset_descriptor_base adb{}; + adb.total_max_supply = 10000000000; + adb.full_name = "test"; + adb.ticker = "TEST"; + + std::vector destinations; + destinations.emplace_back(7070000000, m_accounts[BOB_ACC_IDX].get_public_address(), null_pkey); + destinations.emplace_back(2930000000, m_accounts[BOB_ACC_IDX].get_public_address(), null_pkey); + destinations.emplace_back(MK_TEST_COINS(21), m_accounts[CAROL_ACC_IDX].get_public_address()); // Carol will get coins with non-explicit asset id + + currency::transaction tx{}; + crypto::public_key asset_id = currency::null_pkey; + miner_wlt->deploy_new_asset(adb, destinations, tx, asset_id); + LOG_PRINT_L0("Deployed new asset: " << asset_id << ", tx_id: " << currency::get_transaction_hash(tx)); + + CHECK_AND_ASSERT_MES(tx.vout.size() >= 2, false, "Unexpected vout size: " << tx.vout.size()); + for(auto& out : tx.vout) + { + CHECK_AND_ASSERT_MES(out.type() == typeid(tx_out_zarcanum), false, "invalid out type"); + CHECK_AND_ASSERT_MES(boost::get(out).blinded_asset_id != native_coin_asset_id_1div8, false, "One of outputs has explicit native asset id, which is unexpected"); + } + + CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 1, false, "Unexpected number of txs in the pool: " << c.get_pool_transactions_count()); + r = mine_next_pow_blocks_in_playtime(m_accounts[MINER_ACC_IDX].get_public_address(), c, CURRENCY_MINED_MONEY_UNLOCK_WINDOW); + CHECK_AND_ASSERT_MES(r, false, "mine_next_pow_block_in_playtime failed"); + CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 0, false, "Unexpected number of txs in the pool: " << c.get_pool_transactions_count()); + + std::shared_ptr alice_wlt = init_playtime_test_wallet(events, c, ALICE_ACC_IDX); + alice_wlt->refresh(); + std::shared_ptr bob_wlt = init_playtime_test_wallet(events, c, BOB_ACC_IDX); + bob_wlt->refresh(); + std::shared_ptr carol_wlt = init_playtime_test_wallet(events, c, CAROL_ACC_IDX); + carol_wlt->refresh(); + + CHECK_AND_ASSERT_MES(check_balance_via_wallet(*alice_wlt, "Alice", MK_TEST_COINS(21), MK_TEST_COINS(21), MK_TEST_COINS(21), 0, 0), false, ""); + CHECK_AND_ASSERT_MES(check_balance_via_wallet(*bob_wlt, "Bob", adb.total_max_supply, 0, adb.total_max_supply, 0, 0, asset_id), false, ""); + CHECK_AND_ASSERT_MES(check_balance_via_wallet(*carol_wlt, "Carol", MK_TEST_COINS(21), 0, MK_TEST_COINS(21), 0, 0), false, ""); + + size_t current_blockchain_size = c.get_current_blockchain_size(); + + // Normal ionic swap between Alice and Bob: (Alice has only coins with explicit asset id) + // before: + // Alice (initiator): 0.21 ZANO < - > Bob (finalizer): 0.01 TEST + // after: + // Alice (initiator): 0.01 TEST Bob (finalizer): 0.20 ZANO + + view::ionic_swap_proposal_info proposal_details{}; + proposal_details.to_initiator.push_back(view::asset_funds{ asset_id, adb.total_max_supply }); + proposal_details.to_finalizer.push_back(view::asset_funds{ native_coin_asset_id, MK_TEST_COINS(20) }); + proposal_details.fee_paid_by_a = MK_TEST_COINS(1); + proposal_details.mixins = 2; + + tools::wallet_public::ionic_swap_proposal proposal{}; + alice_wlt->create_ionic_swap_proposal(proposal_details, m_accounts[BOB_ACC_IDX].get_public_address(), proposal); + + view::ionic_swap_proposal_info proposal_decoded_info{}; + bob_wlt->get_ionic_swap_proposal_info(proposal, proposal_decoded_info); + CHECK_AND_ASSERT_MES( + proposal_decoded_info.to_finalizer == proposal_details.to_finalizer && + proposal_decoded_info.to_initiator == proposal_details.to_initiator && + proposal_decoded_info.fee_paid_by_a == proposal_details.fee_paid_by_a && + proposal_decoded_info.mixins == proposal_details.mixins, + false, "actual and decoded proposal mismatch"); + + currency::transaction tx_is{}; + r = bob_wlt->accept_ionic_swap_proposal(proposal, tx_is); + CHECK_AND_ASSERT_MES(r, false, "Failed to accept ionic proposal"); + //CHECK_AND_ASSERT_MES(check_ionic_swap_tx_outs(m_accounts, tx_is, bob_wlt, proposal), false, ""); + + CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 1, false, "Unexpected number of txs in the pool: " << c.get_pool_transactions_count()); + r = mine_next_pow_blocks_in_playtime(m_accounts[MINER_ACC_IDX].get_public_address(), c, CURRENCY_MINED_MONEY_UNLOCK_WINDOW); + CHECK_AND_ASSERT_MES(r, false, "mine_next_pow_block_in_playtime failed"); + CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 0, false, "Unexpected number of txs in the pool: " << c.get_pool_transactions_count()); + + alice_wlt->refresh(); + bob_wlt->refresh(); + CHECK_AND_ASSERT_MES(check_balance_via_wallet(*alice_wlt, "Alice", adb.total_max_supply, 0, adb.total_max_supply, 0, 0, asset_id), false, ""); + CHECK_AND_ASSERT_MES(check_balance_via_wallet(*bob_wlt, "Bob", MK_TEST_COINS(20), 0, MK_TEST_COINS(20), 0, 0), false, ""); + + + // Normal ionic swap between Carol and Alice: (Carol has only coins with non-explicit asset id) + // before: + // Carol (initiator): 0.21 Zano Alice (finalizer): 0.01 TEST + // after: + // Carol (initiator): 0.01 TEST Alice (finalizer): 0.2 ZANO + + proposal_details = view::ionic_swap_proposal_info{}; + proposal_details.to_initiator.push_back(view::asset_funds{ asset_id, adb.total_max_supply }); + proposal_details.to_finalizer.push_back(view::asset_funds{ native_coin_asset_id, MK_TEST_COINS(20) }); + proposal_details.fee_paid_by_a = MK_TEST_COINS(1); + proposal_details.mixins = 2; + + proposal = tools::wallet_public::ionic_swap_proposal{}; + carol_wlt->create_ionic_swap_proposal(proposal_details, m_accounts[ALICE_ACC_IDX].get_public_address(), proposal); + + proposal_decoded_info = view::ionic_swap_proposal_info{}; + alice_wlt->get_ionic_swap_proposal_info(proposal, proposal_decoded_info); + CHECK_AND_ASSERT_MES( + proposal_decoded_info.to_finalizer == proposal_details.to_finalizer && + proposal_decoded_info.to_initiator == proposal_details.to_initiator && + proposal_decoded_info.fee_paid_by_a == proposal_details.fee_paid_by_a && + proposal_decoded_info.mixins == proposal_details.mixins, + false, "actual and decoded proposal mismatch"); + + currency::transaction tx_is2{}; + r = alice_wlt->accept_ionic_swap_proposal(proposal, tx_is2); + CHECK_AND_ASSERT_MES(r, false, "Failed to accept ionic proposal"); + //CHECK_AND_ASSERT_MES(check_ionic_swap_tx_outs(m_accounts, tx_is2, bob_wlt, proposal), false, ""); + + CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 1, false, "Unexpected number of txs in the pool: " << c.get_pool_transactions_count()); + r = mine_next_pow_blocks_in_playtime(m_accounts[MINER_ACC_IDX].get_public_address(), c, CURRENCY_MINED_MONEY_UNLOCK_WINDOW); + CHECK_AND_ASSERT_MES(r, false, "mine_next_pow_block_in_playtime failed"); + CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 0, false, "Unexpected number of txs in the pool: " << c.get_pool_transactions_count()); + + carol_wlt->refresh(); + alice_wlt->refresh(); + CHECK_AND_ASSERT_MES(check_balance_via_wallet(*carol_wlt, "Carol", adb.total_max_supply, 0, adb.total_max_supply, 0, 0, asset_id), false, ""); + CHECK_AND_ASSERT_MES(check_balance_via_wallet(*alice_wlt, "Alice", MK_TEST_COINS(20), 0, MK_TEST_COINS(20), 0, 0), false, ""); + + return true; +} diff --git a/tests/core_tests/ionic_swap_tests.h b/tests/core_tests/ionic_swap_tests.h index 49041649..b1d8ba19 100644 --- a/tests/core_tests/ionic_swap_tests.h +++ b/tests/core_tests/ionic_swap_tests.h @@ -4,6 +4,7 @@ #pragma once #include "chaingen.h" #include "wallet_tests_basic.h" +#include "random_helper.h" struct ionic_swap_basic_test : public wallet_test { @@ -12,3 +13,9 @@ struct ionic_swap_basic_test : public wallet_test bool c1(currency::core& c, size_t ev_index, const std::vector& events); }; +struct ionic_swap_exact_amounts_test : public wallet_test +{ + ionic_swap_exact_amounts_test(); + bool generate(std::vector& events) const; + bool c1(currency::core& c, size_t ev_index, const std::vector& events); +}; From 4be58aa6d89602b3b6b1bbb0dbe6dee5ec2ce12a Mon Sep 17 00:00:00 2001 From: sowle Date: Sun, 22 Oct 2023 01:22:23 +0200 Subject: [PATCH 02/12] fixed bugs: 1) flag tdef_explicit_native_asset_id has been incorrectly set for separately signed tx; 2) pseudo out amount blinding mask was incorrectly set for separately signed tx when number of ZC inputs > 1 --- src/currency_core/currency_format_utils.cpp | 54 ++++++++++++--------- 1 file changed, 32 insertions(+), 22 deletions(-) diff --git a/src/currency_core/currency_format_utils.cpp b/src/currency_core/currency_format_utils.cpp index 4a9be162..f189bb01 100644 --- a/src/currency_core/currency_format_utils.cpp +++ b/src/currency_core/currency_format_utils.cpp @@ -277,10 +277,10 @@ namespace currency bare_inputs_sum += tk.amount; VARIANT_CASE_CONST(txin_htlc, foo); CHECK_AND_ASSERT_MES(false, false, "unexpected txin_htlc input"); - VARIANT_CASE_CONST(txin_multisig, ms); + VARIANT_CASE_CONST(txin_multisig, ms); //bare_inputs_sum += ms.amount; CHECK_AND_ASSERT_MES(false, false, "unexpected txin_multisig input"); // TODO @#@# check support for multisig inputs - VARIANT_CASE_CONST(txin_zc_input, foo); + VARIANT_CASE_CONST(txin_zc_input, foo); ++zc_inputs_count; VARIANT_SWITCH_END(); } @@ -1179,11 +1179,11 @@ namespace currency out.encrypted_amount = de.amount ^ amount_mask.m_u64[0]; CHECK_AND_ASSERT_MES(~de.flags & tx_destination_entry_flags::tdef_explicit_native_asset_id || de.asset_id == currency::native_coin_asset_id, false, "explicit_native_asset_id may be used only with native asset id"); - asset_blinding_mask = de.flags & tx_destination_entry_flags::tdef_explicit_native_asset_id ? 0 : crypto::hash_helper_t::hs(CRYPTO_HDS_OUT_ASSET_BLINDING_MASK, h); // f = Hs(domain_sep, d, i) + asset_blinding_mask = de.flags & tx_destination_entry_flags::tdef_explicit_native_asset_id ? 0 : crypto::hash_helper_t::hs(CRYPTO_HDS_OUT_ASSET_BLINDING_MASK, h); // s = Hs(domain_sep, Hs(8 * r * V, i)) blinded_asset_id = crypto::point_t(de.asset_id) + asset_blinding_mask * crypto::c_point_X; out.blinded_asset_id = (crypto::c_scalar_1div8 * blinded_asset_id).to_public_key(); // T = 1/8 * (H_asset + s * X) - amount_blinding_mask = de.flags & tx_destination_entry_flags::tdef_zero_amount_blinding_mask ? 0 : crypto::hash_helper_t::hs(CRYPTO_HDS_OUT_AMOUNT_BLINDING_MASK, h); // f = Hs(domain_sep, d, i) + amount_blinding_mask = de.flags & tx_destination_entry_flags::tdef_zero_amount_blinding_mask ? 0 : crypto::hash_helper_t::hs(CRYPTO_HDS_OUT_AMOUNT_BLINDING_MASK, h); // y = Hs(domain_sep, Hs(8 * r * V, i)) amount_commitment = de.amount * blinded_asset_id + amount_blinding_mask * crypto::c_point_G; out.amount_commitment = (crypto::c_scalar_1div8 * amount_commitment).to_public_key(); // E = 1/8 * e * T + 1/8 * y * G @@ -1203,19 +1203,24 @@ namespace currency out.encrypted_amount = de.amount ^ amount_mask.m_u64[0]; CHECK_AND_ASSERT_MES(~de.flags & tx_destination_entry_flags::tdef_explicit_native_asset_id || de.asset_id == currency::native_coin_asset_id, false, "explicit_native_asset_id may be used only with native asset id"); - asset_blinding_mask = de.flags & tx_destination_entry_flags::tdef_explicit_native_asset_id ? 0 : crypto::hash_helper_t::hs(CRYPTO_HDS_OUT_ASSET_BLINDING_MASK, h); // f = Hs(domain_sep, d, i) + asset_blinding_mask = de.flags & tx_destination_entry_flags::tdef_explicit_native_asset_id ? 0 : crypto::hash_helper_t::hs(CRYPTO_HDS_OUT_ASSET_BLINDING_MASK, h); // s = Hs(domain_sep, Hs(8 * r * V, i)) blinded_asset_id = crypto::point_t(de.asset_id) + asset_blinding_mask * crypto::c_point_X; out.blinded_asset_id = (crypto::c_scalar_1div8 * blinded_asset_id).to_public_key(); // T = 1/8 * (H_asset + s * X) - amount_blinding_mask = crypto::hash_helper_t::hs(CRYPTO_HDS_OUT_AMOUNT_BLINDING_MASK, h); // f = Hs(domain_sep, d, i) + amount_blinding_mask = crypto::hash_helper_t::hs(CRYPTO_HDS_OUT_AMOUNT_BLINDING_MASK, h); // y = Hs(domain_sep, Hs(8 * r * V, i)) amount_commitment = de.amount * blinded_asset_id + amount_blinding_mask * crypto::c_point_G; out.amount_commitment = (crypto::c_scalar_1div8 * amount_commitment).to_public_key(); // E = 1/8 * e * T + 1/8 * y * G - //LOG_PRINT_CYAN("OUT " << std::setw(2) << output_index << ": " << de.asset_id << " + " << asset_blinding_mask << " x X", LOG_LEVEL_0); - //LOG_PRINT_CYAN(" == " << blinded_asset_id, LOG_LEVEL_0); - //LOG_PRINT_CYAN("OUT " << std::setw(2) << output_index << ": " << crypto::scalar_t(de.amount) << " x " << blinded_asset_id << " + " << amount_blinding_mask << " x G", LOG_LEVEL_0); - //LOG_PRINT_CYAN(" == " << amount_commitment << ", x 1/8 == " << out.amount_commitment, LOG_LEVEL_0); - + DBG_VAL_PRINT(output_index); + DBG_VAL_PRINT(de.amount); + DBG_VAL_PRINT(de.asset_id); + DBG_VAL_PRINT(amount_mask); + DBG_VAL_PRINT(asset_blinding_mask); + DBG_VAL_PRINT(blinded_asset_id); + DBG_VAL_PRINT(amount_blinding_mask); + DBG_VAL_PRINT(amount_mask); + DBG_VAL_PRINT(amount_commitment); + if (de.addr.front().is_auditable()) out.mix_attr = CURRENCY_TO_KEY_OUT_FORCED_NO_MIX; // override mix_attr to 1 for auditable target addresses else @@ -1942,7 +1947,7 @@ namespace currency }; //-------------------------------------------------------------------------------- bool generate_ZC_sig(const crypto::hash& tx_hash_for_signature, size_t input_index, const tx_source_entry& se, const input_generation_context_data& in_context, - const account_keys& sender_account_keys, const uint64_t tx_flags, tx_generation_context& ogc, transaction& tx, bool last_output) + const account_keys& sender_account_keys, const uint64_t tx_flags, tx_generation_context& ogc, transaction& tx, bool last_output, bool separately_signed_tx_complete) { bool watch_only_mode = sender_account_keys.spend_secret_key == null_skey; CHECK_AND_ASSERT_MES(se.is_zc(), false, "sources contains a non-zc input"); @@ -1970,10 +1975,10 @@ namespace currency crypto::scalar_t pseudo_out_amount_blinding_mask = 0; crypto::scalar_t pseudo_out_asset_id_blinding_mask = crypto::scalar_t::random(); - if ((last_output && (tx_flags & TX_FLAG_SIGNATURE_MODE_SEPARATE) == 0) || se.separately_signed_tx_complete) + if (last_output && ((tx_flags & TX_FLAG_SIGNATURE_MODE_SEPARATE) == 0 || separately_signed_tx_complete)) { // either normal tx or the last signature of consolidated tx -- in both cases we need to calculate non-random blinding mask for pseudo output commitment - pseudo_out_amount_blinding_mask = ogc.amount_blinding_masks_sum - ogc.pseudo_out_amount_blinding_masks_sum + (ogc.ao_commitment_in_outputs ? ogc.ao_amount_blinding_mask : -ogc.ao_amount_blinding_mask); // A_1 - A^p_0 = (f_1 - f'_1) * G => f'_{i-1} = sum{y_j} - sum{f'_i} + pseudo_out_amount_blinding_mask = ogc.amount_blinding_masks_sum - ogc.pseudo_out_amount_blinding_masks_sum + (ogc.ao_commitment_in_outputs ? ogc.ao_amount_blinding_mask : -ogc.ao_amount_blinding_mask); // A_1 - A^p_0 = (f_1 - f'_1) * G => f'_{i-1} = sum{y_j} - sum{f'_i} } else { @@ -1981,6 +1986,16 @@ namespace currency ogc.pseudo_out_amount_blinding_masks_sum += pseudo_out_amount_blinding_mask; } + DBG_VAL_PRINT("ZC sig generation"); + DBG_VAL_PRINT(input_index); + DBG_VAL_PRINT(source_blinded_asset_id); + DBG_VAL_PRINT(pseudo_out_asset_id_blinding_mask); + DBG_VAL_PRINT(se.real_out_amount_blinding_mask); + DBG_VAL_PRINT(se.real_out_asset_id_blinding_mask); + DBG_VAL_PRINT(se.asset_id); + DBG_VAL_PRINT(se.amount); + DBG_VAL_PRINT(pseudo_out_amount_blinding_mask); + crypto::point_t pseudo_out_blinded_asset_id = source_blinded_asset_id + pseudo_out_asset_id_blinding_mask * crypto::c_point_X; // T^p_i = T_i + r'_i * X sig.pseudo_out_blinded_asset_id = (crypto::c_scalar_1div8 * pseudo_out_blinded_asset_id).to_public_key(); ogc.real_in_asset_id_blinding_mask_x_amount_sum += se.real_out_asset_id_blinding_mask * se.amount; // += r_i * a_i @@ -1991,11 +2006,6 @@ namespace currency sig.pseudo_out_amount_commitment = (crypto::c_scalar_1div8 * pseudo_out_amount_commitment).to_public_key(); ogc.pseudo_out_amount_commitments_sum += pseudo_out_amount_commitment; - //LOG_PRINT_CYAN("SBAID " << ": " << asset_id_pt << " + " << se.real_out_asset_id_blinding_mask << " x X", LOG_LEVEL_0); - //LOG_PRINT_CYAN(" == " << source_blinded_asset_id, LOG_LEVEL_0); - //LOG_PRINT_CYAN("POAM " << ": " << crypto::scalar_t(se.amount) << " x " << source_blinded_asset_id << " + " << pseudo_out_amount_blinding_mask << " x G", LOG_LEVEL_0); - //LOG_PRINT_CYAN(" == " << pseudo_out_amount_commitment << ", x 1/8 == " << sig.pseudo_out_amount_commitment, LOG_LEVEL_0); - // = three-layers ring signature data outline = // (j in [0, ring_size-1]) // layer 0 ring @@ -2489,7 +2499,7 @@ namespace currency for(size_t destination_index = 0; destination_index < shuffled_dsts.size(); ++destination_index, ++output_index) { tx_destination_entry& dst_entr = shuffled_dsts[destination_index]; - if (!append_mode && all_inputs_are_obviously_native_coins && gen_context.ao_asset_id == currency::null_pkey) + if (!(flags & TX_FLAG_SIGNATURE_MODE_SEPARATE) && all_inputs_are_obviously_native_coins && gen_context.ao_asset_id == currency::null_pkey) dst_entr.flags |= tx_destination_entry_flags::tdef_explicit_native_asset_id; // all inputs are obviously native coins -- all outputs must have explicit asset ids (unless there's an asset emission) r = construct_tx_out(dst_entr, txkey.sec, output_index, tx, deriv_cache, sender_account_keys, @@ -2580,6 +2590,7 @@ namespace currency // ring signatures (per-input proofs) r = false; + bool separately_signed_tx_complete = !sources.empty() ? sources.back().separately_signed_tx_complete : false; size_t curren_zc_index = thirdparty_zc_inputs_count; for (size_t i_ = 0; i_ != sources.size(); i_++) { @@ -2592,8 +2603,7 @@ namespace currency if (source_entry.is_zc()) { // ZC - // blinding_masks_sum is supposed to be sum(mask of all tx output) - sum(masks of all pseudo out commitments) - r = generate_ZC_sig(tx_hash_for_signature, i_ + input_starter_index, source_entry, in_contexts[i_mapped], sender_account_keys, flags, gen_context, tx, i_ + 1 == sources.size()); + r = generate_ZC_sig(tx_hash_for_signature, i_ + input_starter_index, source_entry, in_contexts[i_mapped], sender_account_keys, flags, gen_context, tx, i_ + 1 == sources.size(), separately_signed_tx_complete); CHECK_AND_ASSERT_MES(r, false, "generate_ZC_sigs failed"); gen_context.zc_input_amounts[curren_zc_index] = source_entry.amount; curren_zc_index++; From 57e10326d89f277054120670755e1f7f1910343f Mon Sep 17 00:00:00 2001 From: sowle Date: Mon, 23 Oct 2023 15:42:51 +0200 Subject: [PATCH 03/12] coretests: ionic_swap_exact_amounts_test improved to reveal redundant zero-amount outputs in ionic-swap transactions --- tests/core_tests/ionic_swap_tests.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/core_tests/ionic_swap_tests.cpp b/tests/core_tests/ionic_swap_tests.cpp index 5281c735..9a181dc5 100644 --- a/tests/core_tests/ionic_swap_tests.cpp +++ b/tests/core_tests/ionic_swap_tests.cpp @@ -201,6 +201,7 @@ bool ionic_swap_basic_test::c1(currency::core& c, size_t ev_index, const std::ve currency::transaction res_tx2 = AUTO_VAL_INIT(res_tx2); r = bob_wlt->accept_ionic_swap_proposal(proposal, res_tx2); CHECK_AND_ASSERT_MES(r, false, "Failed to accept ionic proposal"); + CHECK_AND_ASSERT_MES(check_ionic_swap_tx_outs(m_accounts, res_tx2, bob_wlt, proposal), false, ""); } r = mine_next_pow_blocks_in_playtime(m_accounts[MINER_ACC_IDX].get_public_address(), c, CURRENCY_MINED_MONEY_UNLOCK_WINDOW); @@ -403,7 +404,7 @@ bool ionic_swap_exact_amounts_test::c1(currency::core& c, size_t ev_index, const currency::transaction tx_is{}; r = bob_wlt->accept_ionic_swap_proposal(proposal, tx_is); CHECK_AND_ASSERT_MES(r, false, "Failed to accept ionic proposal"); - //CHECK_AND_ASSERT_MES(check_ionic_swap_tx_outs(m_accounts, tx_is, bob_wlt, proposal), false, ""); + CHECK_AND_ASSERT_MES(check_ionic_swap_tx_outs(m_accounts, tx_is, bob_wlt, proposal), false, ""); CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 1, false, "Unexpected number of txs in the pool: " << c.get_pool_transactions_count()); r = mine_next_pow_blocks_in_playtime(m_accounts[MINER_ACC_IDX].get_public_address(), c, CURRENCY_MINED_MONEY_UNLOCK_WINDOW); @@ -443,7 +444,7 @@ bool ionic_swap_exact_amounts_test::c1(currency::core& c, size_t ev_index, const currency::transaction tx_is2{}; r = alice_wlt->accept_ionic_swap_proposal(proposal, tx_is2); CHECK_AND_ASSERT_MES(r, false, "Failed to accept ionic proposal"); - //CHECK_AND_ASSERT_MES(check_ionic_swap_tx_outs(m_accounts, tx_is2, bob_wlt, proposal), false, ""); + CHECK_AND_ASSERT_MES(check_ionic_swap_tx_outs(m_accounts, tx_is2, bob_wlt, proposal), false, ""); CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 1, false, "Unexpected number of txs in the pool: " << c.get_pool_transactions_count()); r = mine_next_pow_blocks_in_playtime(m_accounts[MINER_ACC_IDX].get_public_address(), c, CURRENCY_MINED_MONEY_UNLOCK_WINDOW); From f94aa4410445fbdedade4a811e440b000935dd84 Mon Sep 17 00:00:00 2001 From: sowle Date: Mon, 23 Oct 2023 16:28:33 +0200 Subject: [PATCH 04/12] construct_tx: redundant var removed --- src/currency_core/currency_format_utils.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/currency_core/currency_format_utils.cpp b/src/currency_core/currency_format_utils.cpp index f189bb01..baa73799 100644 --- a/src/currency_core/currency_format_utils.cpp +++ b/src/currency_core/currency_format_utils.cpp @@ -2272,7 +2272,6 @@ namespace currency bool has_non_zc_inputs = false; std::list key_images_total; - size_t thirdparty_zc_inputs_count = zc_inputs_count; // // INs // @@ -2591,7 +2590,7 @@ namespace currency // ring signatures (per-input proofs) r = false; bool separately_signed_tx_complete = !sources.empty() ? sources.back().separately_signed_tx_complete : false; - size_t curren_zc_index = thirdparty_zc_inputs_count; + size_t zc_input_index = 0; for (size_t i_ = 0; i_ != sources.size(); i_++) { size_t i_mapped = inputs_mapping[i_]; @@ -2605,8 +2604,8 @@ namespace currency // ZC r = generate_ZC_sig(tx_hash_for_signature, i_ + input_starter_index, source_entry, in_contexts[i_mapped], sender_account_keys, flags, gen_context, tx, i_ + 1 == sources.size(), separately_signed_tx_complete); CHECK_AND_ASSERT_MES(r, false, "generate_ZC_sigs failed"); - gen_context.zc_input_amounts[curren_zc_index] = source_entry.amount; - curren_zc_index++; + gen_context.zc_input_amounts[zc_input_index] = source_entry.amount; + zc_input_index++; } else { From c974f65a30213afc958f94f12895d9bf8878432d Mon Sep 17 00:00:00 2001 From: sowle Date: Mon, 23 Oct 2023 17:10:07 +0200 Subject: [PATCH 05/12] coretests: minor fix for ionic_swap_exact_amounts_test --- tests/core_tests/ionic_swap_tests.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/core_tests/ionic_swap_tests.cpp b/tests/core_tests/ionic_swap_tests.cpp index 9a181dc5..8900f76b 100644 --- a/tests/core_tests/ionic_swap_tests.cpp +++ b/tests/core_tests/ionic_swap_tests.cpp @@ -444,7 +444,7 @@ bool ionic_swap_exact_amounts_test::c1(currency::core& c, size_t ev_index, const currency::transaction tx_is2{}; r = alice_wlt->accept_ionic_swap_proposal(proposal, tx_is2); CHECK_AND_ASSERT_MES(r, false, "Failed to accept ionic proposal"); - CHECK_AND_ASSERT_MES(check_ionic_swap_tx_outs(m_accounts, tx_is2, bob_wlt, proposal), false, ""); + CHECK_AND_ASSERT_MES(check_ionic_swap_tx_outs(m_accounts, tx_is2, alice_wlt, proposal), false, ""); CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 1, false, "Unexpected number of txs in the pool: " << c.get_pool_transactions_count()); r = mine_next_pow_blocks_in_playtime(m_accounts[MINER_ACC_IDX].get_public_address(), c, CURRENCY_MINED_MONEY_UNLOCK_WINDOW); From 4a5586efad895c3a277da1145f7f7ccaad6e62a8 Mon Sep 17 00:00:00 2001 From: sowle Date: Mon, 23 Oct 2023 17:12:43 +0200 Subject: [PATCH 06/12] wallet: fixed creation of redundant zero-amount outputs in ionic-swap transactions --- src/wallet/wallet2.cpp | 45 +++++++++++++++++++++++------------------- src/wallet/wallet2.h | 4 +++- 2 files changed, 28 insertions(+), 21 deletions(-) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 20c36019..ded83250 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -6600,6 +6600,7 @@ void wallet2::prepare_tx_destinations(const assets_selection_context& needed_mon detail::split_strategy_id_t destination_split_strategy_id, const tx_dust_policy& dust_policy, const std::vector& dsts, + uint8_t tx_flags, std::vector& final_destinations) { @@ -6611,7 +6612,7 @@ void wallet2::prepare_tx_destinations(const assets_selection_context& needed_mon std::unordered_set processed_assets; for (auto& el: needed_money_map) { - prepare_tx_destinations(el.second.needed_amount, el.second.found_amount, destination_split_strategy_id, dust_policy, dsts, final_destinations, el.first); + prepare_tx_destinations(el.second.needed_amount, el.second.found_amount, destination_split_strategy_id, dust_policy, dsts, el.first, final_destinations); processed_assets.insert(el.first); } @@ -6635,23 +6636,26 @@ void wallet2::prepare_tx_destinations(const assets_selection_context& needed_mon } } - if (final_destinations.empty()) + if (!(tx_flags & TX_FLAG_SIGNATURE_MODE_SEPARATE)) { - // if there's no destinations -- make CURRENCY_TX_MIN_ALLOWED_OUTS empty destinations - for(size_t i = 0; i < CURRENCY_TX_MIN_ALLOWED_OUTS; ++i) - final_destinations.emplace_back(0, m_account.get_public_address()); - } - else if (final_destinations.size() < CURRENCY_TX_MIN_ALLOWED_OUTS) - { - // if there's not ehough destinations items (i.e. outputs), split the last one - tx_destination_entry de = final_destinations.back(); - final_destinations.pop_back(); - size_t items_to_be_added = CURRENCY_TX_MIN_ALLOWED_OUTS - final_destinations.size(); - // TODO: consider allowing to set them somewhere - size_t num_digits_to_keep = CURRENCY_TX_OUTS_RND_SPLIT_DIGITS_TO_KEEP; - decompose_amount_randomly(de.amount, [&](uint64_t amount){ de.amount = amount; final_destinations.push_back(de); }, items_to_be_added, num_digits_to_keep); - WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(final_destinations.size() == CURRENCY_TX_MIN_ALLOWED_OUTS, - "can't get necessary number of outputs using decompose_amount_randomly(), got " << final_destinations.size() << " while mininum is " << CURRENCY_TX_MIN_ALLOWED_OUTS); + if (final_destinations.empty()) + { + // if there's no destinations -- make CURRENCY_TX_MIN_ALLOWED_OUTS empty destinations + for(size_t i = 0; i < CURRENCY_TX_MIN_ALLOWED_OUTS; ++i) + final_destinations.emplace_back(0, m_account.get_public_address()); + } + else if (final_destinations.size() < CURRENCY_TX_MIN_ALLOWED_OUTS) + { + // if there's not ehough destinations items (i.e. outputs), split the last one + tx_destination_entry de = final_destinations.back(); + final_destinations.pop_back(); + size_t items_to_be_added = CURRENCY_TX_MIN_ALLOWED_OUTS - final_destinations.size(); + // TODO: consider allowing to set them somewhere + size_t num_digits_to_keep = CURRENCY_TX_OUTS_RND_SPLIT_DIGITS_TO_KEEP; + decompose_amount_randomly(de.amount, [&](uint64_t amount){ de.amount = amount; final_destinations.push_back(de); }, items_to_be_added, num_digits_to_keep); + WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(final_destinations.size() == CURRENCY_TX_MIN_ALLOWED_OUTS, + "can't get necessary number of outputs using decompose_amount_randomly(), got " << final_destinations.size() << " while mininum is " << CURRENCY_TX_MIN_ALLOWED_OUTS); + } } } } @@ -6661,7 +6665,8 @@ void wallet2::prepare_tx_destinations(uint64_t needed_money, detail::split_strategy_id_t destination_split_strategy_id, const tx_dust_policy& dust_policy, const std::vector& dsts, - std::vector& final_destinations, const crypto::public_key& asset_id) + const crypto::public_key& asset_id, + std::vector& final_destinations) { WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(found_money >= needed_money, "needed_money==" << needed_money << " < found_money==" << found_money); @@ -6767,7 +6772,7 @@ bool wallet2::prepare_transaction(construct_tx_param& ctp, currency::finalize_tx TIME_MEASURE_FINISH_MS(prepare_tx_sources_time); TIME_MEASURE_START_MS(prepare_tx_destinations_time); - prepare_tx_destinations(needed_money_map, static_cast(ctp.split_strategy_id), ctp.dust_policy, ctp.dsts, ftp.prepared_destinations); + prepare_tx_destinations(needed_money_map, static_cast(ctp.split_strategy_id), ctp.dust_policy, ctp.dsts, ctp.flags, ftp.prepared_destinations); TIME_MEASURE_FINISH_MS(prepare_tx_destinations_time); @@ -7219,7 +7224,7 @@ void wallet2::sweep_below(size_t fake_outs_count, const currency::account_public assets_selection_context needed_money_map; needed_money_map[currency::native_coin_asset_id] = {}; std::vector dsts({ tx_destination_entry(amount_swept - fee, destination_addr) }); - prepare_tx_destinations(needed_money_map, get_current_split_strategy(), tools::tx_dust_policy(), dsts, ftp.prepared_destinations); + prepare_tx_destinations(needed_money_map, get_current_split_strategy(), tools::tx_dust_policy(), ftp.prepared_destinations, ftp.flags, dsts); currency::transaction tx = AUTO_VAL_INIT(tx); crypto::secret_key tx_key = AUTO_VAL_INIT(tx_key); diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index cd32ca48..5c3a08f6 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -1110,13 +1110,15 @@ private: detail::split_strategy_id_t destination_split_strategy_id, const tx_dust_policy& dust_policy, const std::vector& dsts, + uint8_t tx_flags, std::vector& final_destinations); void prepare_tx_destinations(uint64_t needed_money, uint64_t found_money, detail::split_strategy_id_t destination_split_strategy_id, const tx_dust_policy& dust_policy, const std::vector& dsts, - std::vector& final_detinations, const crypto::public_key& asset_id); + const crypto::public_key& asset_id, + std::vector& final_detinations); bool handle_contract(wallet_public::wallet_transfer_info& wti, const bc_services::contract_private_details& cntr, const std::vector& decrypted_attach); bool handle_release_contract(wallet_public::wallet_transfer_info& wti, const std::string& release_instruction); bool handle_cancel_proposal(wallet_public::wallet_transfer_info& wti, const bc_services::escrow_cancel_templates_body& ectb, const std::vector& decrypted_attach); From edb005648822350cce9e69fdb4407a3318853a90 Mon Sep 17 00:00:00 2001 From: sowle Date: Mon, 23 Oct 2023 18:04:12 +0200 Subject: [PATCH 07/12] === build number: 235 -> 236 === --- src/version.h.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/version.h.in b/src/version.h.in index b0a7a12e..ba02cd0c 100644 --- a/src/version.h.in +++ b/src/version.h.in @@ -8,6 +8,6 @@ #define PROJECT_REVISION "0" #define PROJECT_VERSION PROJECT_MAJOR_VERSION "." PROJECT_MINOR_VERSION "." PROJECT_REVISION -#define PROJECT_VERSION_BUILD_NO 235 +#define PROJECT_VERSION_BUILD_NO 236 #define PROJECT_VERSION_BUILD_NO_STR STRINGIFY_EXPAND(PROJECT_VERSION_BUILD_NO) #define PROJECT_VERSION_LONG PROJECT_VERSION "." PROJECT_VERSION_BUILD_NO_STR "[" BUILD_COMMIT_ID "]" From f9ca8f52c5ddce6353a99ee4c3308cca1b9bc2fd Mon Sep 17 00:00:00 2001 From: sowle Date: Wed, 25 Oct 2023 21:31:11 +0200 Subject: [PATCH 08/12] get_native_coin_asset_descriptor() implemented --- src/currency_core/currency_format_utils.cpp | 19 +++++++++++++++++++ src/currency_core/currency_format_utils.h | 3 +-- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/currency_core/currency_format_utils.cpp b/src/currency_core/currency_format_utils.cpp index baa73799..c07fc4f3 100644 --- a/src/currency_core/currency_format_utils.cpp +++ b/src/currency_core/currency_format_utils.cpp @@ -2105,6 +2105,25 @@ namespace currency p_result_point->to_public_key(*p_result_pub_key); } + const asset_descriptor_base& get_native_coin_asset_descriptor() + { + static asset_descriptor_base native_coin_asset_descriptor = [](){ + asset_descriptor_base adb{}; + adb.total_max_supply = UINT64_MAX; + adb.current_supply = 0; + adb.decimal_point = CURRENCY_DISPLAY_DECIMAL_POINT; + adb.ticker = CURRENCY_NAME_ABR; + adb.full_name = CURRENCY_NAME_BASE; + adb.meta_info = ""; + adb.owner = currency::null_pkey; + adb.hidden_supply = false; + adb.version = 0; + return adb; + }(); + + return native_coin_asset_descriptor; + } + bool construct_tx_handle_ado(const account_keys& sender_account_keys, const finalize_tx_param& ftp, asset_descriptor_operation& ado, diff --git a/src/currency_core/currency_format_utils.h b/src/currency_core/currency_format_utils.h index 5a2ca910..5baad2aa 100644 --- a/src/currency_core/currency_format_utils.h +++ b/src/currency_core/currency_format_utils.h @@ -329,8 +329,7 @@ namespace currency uint64_t get_tx_version(uint64_t tx_expected_block_height, const hard_forks_descriptor& hfd); // returns tx version based on the height of the block where the transaction is expected to be bool construct_tx(const account_keys& sender_account_keys, const finalize_tx_param& param, finalized_tx& result); void calculate_asset_id(const crypto::public_key& asset_owner, crypto::point_t* p_result_point, crypto::public_key* p_result_pub_key); - - + const asset_descriptor_base& get_native_coin_asset_descriptor(); bool sign_multisig_input_in_tx(currency::transaction& tx, size_t ms_input_index, const currency::account_keys& keys, const currency::transaction& source_tx, bool *p_is_input_fully_signed = nullptr); From e2da6a1f51c320a92788fd7ac9ae9c03d75436b9 Mon Sep 17 00:00:00 2001 From: sowle Date: Wed, 25 Oct 2023 21:32:47 +0200 Subject: [PATCH 09/12] wallet: minor fixes --- src/currency_core/currency_basic.h | 15 --------------- src/wallet/wallet2.cpp | 4 +++- 2 files changed, 3 insertions(+), 16 deletions(-) diff --git a/src/currency_core/currency_basic.h b/src/currency_core/currency_basic.h index 45506cec..26b17a9f 100644 --- a/src/currency_core/currency_basic.h +++ b/src/currency_core/currency_basic.h @@ -954,21 +954,6 @@ namespace currency END_BOOST_SERIALIZATION() }; - //TODO: @val, should we call it something like schnorr_sig ? - struct simple_sig - { - crypto::signature s; - - BEGIN_SERIALIZE_OBJECT() - FIELD(s) - END_SERIALIZE() - - BEGIN_BOOST_SERIALIZATION() - BOOST_SERIALIZE(s) - END_BOOST_SERIALIZATION() - }; - - struct void_sig { //TODO: diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index ded83250..b828d36d 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -3478,7 +3478,9 @@ bool wallet2::get_asset_id_info(const crypto::public_key& asset_id, currency::as { if (asset_id == currency::native_coin_asset_id) { - return CURRENCY_NAME_ABR; + asset_info = currency::get_native_coin_asset_descriptor(); + whitelist_ = true; + return true; } //check if asset is whitelisted or customly added whitelist_ = false; From bdd9e834189cec21e3ca65002f5b14d5e7f0b29d Mon Sep 17 00:00:00 2001 From: sowle Date: Wed, 25 Oct 2023 21:34:50 +0200 Subject: [PATCH 10/12] simplewallet's "list_outputs" command and wallet2::get_transfers_str() were improved to support assets --- src/simplewallet/simplewallet.cpp | 41 +++++++++++++++++++------------ src/wallet/wallet2.cpp | 34 +++++++++++++++++++++---- src/wallet/wallet2.h | 2 +- 3 files changed, 55 insertions(+), 22 deletions(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index b80d01d5..d26d0849 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -269,7 +269,8 @@ simple_wallet::simple_wallet() m_cmd_binder.set_handler("incoming_counts", boost::bind(&simple_wallet::show_incoming_transfers_counts, this, ph::_1), "incoming_transfers counts"); m_cmd_binder.set_handler("list_recent_transfers", boost::bind(&simple_wallet::list_recent_transfers, this, ph::_1), "list_recent_transfers [offset] [count] - Show recent maximum 1000 transfers, offset default = 0, count default = 100 "); m_cmd_binder.set_handler("export_recent_transfers", boost::bind(&simple_wallet::export_recent_transfers, this, ph::_1), "list_recent_transfers_tx - Write recent transfer in json to wallet_recent_transfers.txt"); - m_cmd_binder.set_handler("list_outputs", boost::bind(&simple_wallet::list_outputs, this, ph::_1), "list_outputs [spent|unspent] - Lists all the outputs that have ever been sent to this wallet if called without arguments, otherwise it lists only the spent or unspent outputs"); + m_cmd_binder.set_handler("list_outputs", boost::bind(&simple_wallet::list_outputs, this, ph::_1), "list_outputs [spent|unspent] [ticker=ZANO] [unknown] - Lists all the outputs. The result may be filtered by spent status, asset ticker or unknown asset ids."); + m_cmd_binder.set_handler("lo", boost::bind(&simple_wallet::list_outputs, this, ph::_1), "alias for list_outputs"); m_cmd_binder.set_handler("dump_transfers", boost::bind(&simple_wallet::dump_trunsfers, this, ph::_1), "dump_transfers - Write transfers in json to dump_transfers.txt"); m_cmd_binder.set_handler("dump_keyimages", boost::bind(&simple_wallet::dump_key_images, this, ph::_1), "dump_keyimages - Write key_images in json to dump_key_images.txt"); m_cmd_binder.set_handler("payments", boost::bind(&simple_wallet::show_payments, this, ph::_1), "payments [ ... ] - Show payments , ... "); @@ -1775,28 +1776,36 @@ bool simple_wallet::save_watch_only(const std::vector &args) //---------------------------------------------------------------------------------------------------- bool simple_wallet::list_outputs(const std::vector &args) { - if (args.size() > 1) - { - fail_msg_writer() << "invalid syntax: one or none parameters are expected, " << args.size() << " was given"; - return true; - } + bool include_spent = true, include_unspent = true, show_only_unknown = false; + std::string filter_asset_ticker{}; - bool include_spent = true, include_unspent = true; - if (args.size() == 1) - { - if (args[0] == "unspent" || args[0] == "available") - include_spent = false; - else if (args[0] == "spent" || args[0] == "unavailable") - include_unspent = false; + bool arg_spent_flags = false, arg_unknown_assets = false, arg_ticker_filer = false; + + auto process_arg = [&](const std::string& arg) -> bool { + if (!arg_spent_flags && (arg == "u" || arg == "unspent" || arg == "available")) + arg_spent_flags = true, include_spent = false; + else if (!arg_spent_flags && (arg == "s" || arg == "spent" || arg == "unavailable")) + arg_spent_flags = true, include_unspent = false; + else if (!arg_unknown_assets && (arg == "unknown")) + arg_unknown_assets = true, show_only_unknown = true; + else if (!arg_ticker_filer && (arg.find("ticker=") == 0 || arg.find("t=") == 0)) + arg_ticker_filer = true, filter_asset_ticker = boost::erase_all_copy(boost::erase_all_copy(arg, "ticker="), "t="); else + return false; + return true; + }; + + for(auto& arg : args) + { + if (!process_arg(arg)) { - fail_msg_writer() << "invalid parameter: " << args[0]; + fail_msg_writer() << "invalid parameter: " << arg; return true; } } - success_msg_writer() << "list of all the outputs that have ever been sent to this wallet:" << ENDL << - m_wallet->get_transfers_str(include_spent, include_unspent); + success_msg_writer() << m_wallet->get_transfers_str(include_spent, include_unspent, show_only_unknown, filter_asset_ticker); + return true; } //---------------------------------------------------------------------------------------------------- diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index b828d36d..4f4117c1 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -3590,12 +3590,13 @@ bool wallet2::generate_utxo_defragmentation_transaction_if_needed(currency::tran return true; } //---------------------------------------------------------------------------------------------------- -std::string wallet2::get_transfers_str(bool include_spent /*= true*/, bool include_unspent /*= true*/) const +std::string wallet2::get_transfers_str(bool include_spent /*= true*/, bool include_unspent /*= true*/, bool show_only_unknown /*= false*/, const std::string& filter_asset_ticker /*= std::string{}*/) const { - static const char* header = "index amount g_index flags block tx out# key image"; + static const char* header = "index amount ticker g_index flags block tx out# asset id"; std::stringstream ss; ss << header << ENDL; size_t count = 0; + size_t unknown_assets_outs_count = 0; for (size_t i = 0; i != m_transfers.size(); ++i) { const transfer_details& td = m_transfers[i]; @@ -3603,21 +3604,44 @@ std::string wallet2::get_transfers_str(bool include_spent /*= true*/, bool inclu if ((td.is_spent() && !include_spent) || (!td.is_spent() && !include_unspent)) continue; + bool native_coin = td.is_native_coin(); + asset_descriptor_base adb{}; + bool whitelisted = false; + if (get_asset_id_info(td.get_asset_id(), adb, whitelisted) == show_only_unknown) + { + if (!show_only_unknown) + ++unknown_assets_outs_count; + continue; + } + + if (!filter_asset_ticker.empty() && adb.ticker != filter_asset_ticker) + continue; + ss << std::right << std::setw(5) << i << " " << - std::setw(21) << print_money(td.amount()) << " " << + std::setw(21) << print_asset_money(td.m_amount, adb.decimal_point) << " " << + std::setw(6) << std::left << (native_coin ? std::string(" ") : adb.ticker) << " " << std::right << std::setw(7) << td.m_global_output_index << " " << std::setw(2) << std::setfill('0') << td.m_flags << std::setfill(' ') << ":" << std::setw(5) << transfer_flags_to_str(td.m_flags) << " " << std::setw(7) << td.m_ptx_wallet_info->m_block_height << " " << get_transaction_hash(td.m_ptx_wallet_info->m_tx) << " " << - std::setw(4) << td.m_internal_output_index << " " << - td.m_key_image << ENDL; + std::setw(4) << td.m_internal_output_index << " "; + if (native_coin) + ss << " "; + else + ss << td.get_asset_id(); + ss << ENDL; + ++count; } ss << "printed " << count << " outputs of " << m_transfers.size() << " total" << ENDL; + if (unknown_assets_outs_count == 1) + ss << "(" << unknown_assets_outs_count << " output with unrecognized asset id is not shown, use 'list_outputs unknown' to see it)" << ENDL; + else if (unknown_assets_outs_count > 1) + ss << "(" << unknown_assets_outs_count << " outputs with unrecognized asset ids are not shown, use 'list_outputs unknown' to see them)" << ENDL; return ss.str(); } //---------------------------------------------------------------------------------------------------- diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 5c3a08f6..63eb6d0c 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -833,7 +833,7 @@ namespace tools bool fill_mining_context(mining_context& ctx); void get_transfers(wallet2::transfer_container& incoming_transfers) const; - std::string get_transfers_str(bool include_spent = true, bool include_unspent = true) const; + std::string get_transfers_str(bool include_spent = true, bool include_unspent = true, bool show_only_unknown = false, const std::string& filter_asset_ticker = std::string{}) const; std::string get_balance_str() const; // Returns all payments by given id in unspecified order From ba6b34c292cbc93c9d959d4c4fce9b0e9283985a Mon Sep 17 00:00:00 2001 From: sowle Date: Wed, 25 Oct 2023 21:35:38 +0200 Subject: [PATCH 11/12] === build number: 236 -> 237 === --- src/version.h.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/version.h.in b/src/version.h.in index ba02cd0c..40fffc58 100644 --- a/src/version.h.in +++ b/src/version.h.in @@ -8,6 +8,6 @@ #define PROJECT_REVISION "0" #define PROJECT_VERSION PROJECT_MAJOR_VERSION "." PROJECT_MINOR_VERSION "." PROJECT_REVISION -#define PROJECT_VERSION_BUILD_NO 236 +#define PROJECT_VERSION_BUILD_NO 237 #define PROJECT_VERSION_BUILD_NO_STR STRINGIFY_EXPAND(PROJECT_VERSION_BUILD_NO) #define PROJECT_VERSION_LONG PROJECT_VERSION "." PROJECT_VERSION_BUILD_NO_STR "[" BUILD_COMMIT_ID "]" From 7a9f54443ba9a9a1b2b06ec31f1227c9ff2317a3 Mon Sep 17 00:00:00 2001 From: cryptozoidberg Date: Sat, 28 Oct 2023 19:25:31 +0200 Subject: [PATCH 12/12] fixed bug with m_own_asset_descriptors container not being reset after resync wallet --- src/currency_core/currency_config.h | 4 ++-- src/wallet/wallet2.cpp | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/currency_core/currency_config.h b/src/currency_core/currency_config.h index 2a2a5d69..3a48cf8c 100644 --- a/src/currency_core/currency_config.h +++ b/src/currency_core/currency_config.h @@ -246,8 +246,8 @@ #define WALLET_FILE_SERIALIZATION_VERSION 160 #define WALLET_FILE_LAST_SUPPORTED_VERSION 160 #else -#define WALLET_FILE_LAST_SUPPORTED_VERSION (CURRENCY_FORMATION_VERSION+73) -#define WALLET_FILE_SERIALIZATION_VERSION (CURRENCY_FORMATION_VERSION+73) +#define WALLET_FILE_LAST_SUPPORTED_VERSION (CURRENCY_FORMATION_VERSION+74) +#define WALLET_FILE_SERIALIZATION_VERSION (CURRENCY_FORMATION_VERSION+74) #endif #define CURRENT_MEMPOOL_ARCHIVE_VER (CURRENCY_FORMATION_VERSION+31) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 4f4117c1..e805280d 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -2842,6 +2842,7 @@ bool wallet2::reset_all() m_last_pow_block_h = 0; m_current_wallet_file_size = 0; m_custom_assets.clear(); + m_own_asset_descriptors.clear(); return true; } //----------------------------------------------------------------------------------------------------