diff --git a/src/currency_core/blockchain_storage.cpp b/src/currency_core/blockchain_storage.cpp index 901bdfa0..7c39c5f9 100644 --- a/src/currency_core/blockchain_storage.cpp +++ b/src/currency_core/blockchain_storage.cpp @@ -3121,7 +3121,7 @@ bool blockchain_storage::get_est_height_from_date(uint64_t date, uint64_t& res_h uint64_t iteration_coun = 0; uint64_t current_low_boundary = 0; - uint64_t current_hight_boundary = m_db_blocks.size() - 1; + uint64_t current_height_boundary = m_db_blocks.size() - 1; while (true) { iteration_coun++; @@ -3135,10 +3135,10 @@ bool blockchain_storage::get_est_height_from_date(uint64_t date, uint64_t& res_h { //we moved too much forward - current_hight_boundary = calculated_estimated_height; - CHECK_AND_ASSERT_MES(current_hight_boundary > current_low_boundary, true, - "Internal error: current_hight_boundary(" << current_hight_boundary << ") > current_low_boundary("<< current_low_boundary << ")"); - uint64_t offset = (current_hight_boundary - current_low_boundary)/2; + current_height_boundary = calculated_estimated_height; + CHECK_AND_ASSERT_MES(current_height_boundary > current_low_boundary, true, + "Internal error: current_hight_boundary(" << current_height_boundary << ") > current_low_boundary("<< current_low_boundary << ")"); + uint64_t offset = (current_height_boundary - current_low_boundary)/2; if (offset <= 2) { //something really wrong with distribution of blocks, just use current_low_boundary to be sure that we didn't mess any transactions @@ -3154,9 +3154,9 @@ bool blockchain_storage::get_est_height_from_date(uint64_t date, uint64_t& res_h { //we too much in past current_low_boundary = calculated_estimated_height; - CHECK_AND_ASSERT_MES(current_hight_boundary > current_low_boundary, true, - "Internal error: current_hight_boundary(" << current_hight_boundary << ") > current_low_boundary(" << current_low_boundary << ")"); - uint64_t offset = (current_hight_boundary - current_low_boundary) / 2; + CHECK_AND_ASSERT_MES(current_height_boundary > current_low_boundary, true, + "Internal error: current_hight_boundary(" << current_height_boundary << ") > current_low_boundary(" << current_low_boundary << ")"); + uint64_t offset = (current_height_boundary - current_low_boundary) / 2; if (offset <= 2) { //something really wrong with distribution of blocks, just use current_low_boundary to be sure that we didn't mess any transactions diff --git a/src/currency_core/currency_format_utils.cpp b/src/currency_core/currency_format_utils.cpp index 501c595e..771d6c5e 100644 --- a/src/currency_core/currency_format_utils.cpp +++ b/src/currency_core/currency_format_utils.cpp @@ -604,6 +604,12 @@ namespace currency } //--------------------------------------------------------------- bool construct_tx_out(const tx_destination_entry& de, const crypto::secret_key& tx_sec_key, size_t output_index, transaction& tx, std::set& deriv_cache, const account_keys& self, uint8_t tx_outs_attr) + { + finalized_tx result = AUTO_VAL_INIT(result); + return construct_tx_out(de, tx_sec_key, output_index, tx, deriv_cache, self, result, tx_outs_attr); + } + //--------------------------------------------------------------- + bool construct_tx_out(const tx_destination_entry& de, const crypto::secret_key& tx_sec_key, size_t output_index, transaction& tx, std::set& deriv_cache, const account_keys& self, finalized_tx& result, uint8_t tx_outs_attr) { CHECK_AND_ASSERT_MES(de.addr.size() == 1 || (de.addr.size() > 1 && de.minimum_sigs <= de.addr.size()), false, "Invalid destination entry: amount: " << de.amount << " minimum_sigs: " << de.minimum_sigs << " addr.size(): " << de.addr.size()); @@ -655,16 +661,17 @@ namespace currency if (htlc_dest.htlc_hash == null_hash) { //we use deterministic origin, to make possible access origin on different wallets copies - std::string hltc_origin = generate_origin_for_htlc(htlc, self); + + result.htlc_origin = generate_origin_for_htlc(htlc, self); //calculate hash if (htlc.flags&CURRENCY_TXOUT_HTLC_FLAGS_HASH_TYPE_MASK) { - htlc.htlc_hash = crypto::sha256_hash(hltc_origin.data(), hltc_origin.size()); + htlc.htlc_hash = crypto::sha256_hash(result.htlc_origin.data(), result.htlc_origin.size()); } else { - crypto::hash160 h160 = crypto::RIPEMD160_hash(hltc_origin.data(), hltc_origin.size()); + crypto::hash160 h160 = crypto::RIPEMD160_hash(result.htlc_origin.data(), result.htlc_origin.size()); std::memcpy(&htlc.htlc_hash, &h160, sizeof(h160)); } } @@ -1076,7 +1083,7 @@ namespace currency shuffle, flags); } - + //--------------------------------------------------------------- bool construct_tx(const account_keys& sender_account_keys, const std::vector& sources, const std::vector& destinations, const std::vector& extra, @@ -1090,6 +1097,43 @@ namespace currency bool shuffle, uint64_t flags) { + //extra copy operation, but creating transaction is not sensitive to this + finalize_tx_param ftp = AUTO_VAL_INIT(ftp); + ftp.sources = sources; + ftp.prepared_destinations = destinations; + ftp.extra = extra; + ftp.attachments = attachments; + ftp.unlock_time = unlock_time; + ftp.crypt_address = crypt_destination_addr; + ftp.expiration_time = 0; + ftp.tx_outs_attr = tx_outs_attr; + ftp.shuffle = shuffle; + ftp.flags = flags; + + finalized_tx ft = AUTO_VAL_INIT(ft); + bool r = construct_tx(sender_account_keys, ftp, ft); + tx = ft.tx; + one_time_secret_key = ft.one_time_key; + return r; + } + //--------------------------------------------------------------- + bool construct_tx(const account_keys& sender_account_keys, const finalize_tx_param& ftp, finalized_tx& result) + { + const std::vector& sources = ftp.sources; + const std::vector& destinations = ftp.prepared_destinations; + const std::vector& extra = ftp.extra; + const std::vector& attachments = ftp.attachments; + const uint64_t& unlock_time = ftp.unlock_time; + const account_public_address& crypt_destination_addr = ftp.crypt_address; + const uint64_t& expiration_time = ftp.expiration_time; + const uint8_t& tx_outs_attr = ftp.tx_outs_attr; + const bool& shuffle = ftp.shuffle; + const uint64_t& flags = ftp.flags; + + transaction& tx = result.tx; + crypto::secret_key& one_time_secret_key = result.one_time_key; + + result.ftp = ftp; CHECK_AND_ASSERT_MES(destinations.size() <= CURRENCY_TX_MAX_ALLOWED_OUTS, false, "Too many outs (" << destinations.size() << ")! Tx can't be constructed."); bool watch_only_mode = sender_account_keys.spend_secret_key == null_skey; @@ -1290,7 +1334,7 @@ namespace currency for(const tx_destination_entry& dst_entr : shuffled_dsts) { CHECK_AND_ASSERT_MES(dst_entr.amount > 0, false, "Destination with wrong amount: " << dst_entr.amount); - bool r = construct_tx_out(dst_entr, txkey.sec, output_index, tx, deriv_cache, sender_account_keys, tx_outs_attr); + bool r = construct_tx_out(dst_entr, txkey.sec, output_index, tx, deriv_cache, sender_account_keys, result, tx_outs_attr); CHECK_AND_ASSERT_MES(r, false, "Failed to construc tx out"); output_index++; summary_outs_money += dst_entr.amount; diff --git a/src/currency_core/currency_format_utils.h b/src/currency_core/currency_format_utils.h index d7d1664e..0c8ba27a 100644 --- a/src/currency_core/currency_format_utils.h +++ b/src/currency_core/currency_format_utils.h @@ -132,9 +132,60 @@ namespace currency END_KV_SERIALIZE_MAP() }; - struct htlc_info + struct htlc_info + { + bool hltc_our_out_is_before_expiration; + }; + + + struct finalize_tx_param { - bool hltc_our_out_is_before_expiration; + uint64_t unlock_time; + std::vector extra; + std::vector attachments; + currency::account_public_address crypt_address; + uint8_t tx_outs_attr; + bool shuffle; + uint8_t flags; + crypto::hash multisig_id; + std::vector sources; + std::vector selected_transfers; + std::vector prepared_destinations; + uint64_t expiration_time; + crypto::public_key spend_pub_key; // only for validations + + BEGIN_SERIALIZE_OBJECT() + FIELD(unlock_time) + FIELD(extra) + FIELD(attachments) + FIELD(crypt_address) + FIELD(tx_outs_attr) + FIELD(shuffle) + FIELD(flags) + FIELD(multisig_id) + FIELD(sources) + FIELD(selected_transfers) + FIELD(prepared_destinations) + FIELD(expiration_time) + FIELD(spend_pub_key) + END_SERIALIZE() + }; + + struct finalized_tx + { + currency::transaction tx; + crypto::secret_key one_time_key; + finalize_tx_param ftp; + std::string htlc_origin; + std::vector> outs_key_images; // pairs (out_index, key_image) for each change output + + BEGIN_SERIALIZE_OBJECT() + FIELD(tx) + FIELD(one_time_key) + FIELD(ftp) + FIELD(htlc_origin) + FIELD(outs_key_images) + END_SERIALIZE() }; @@ -163,6 +214,7 @@ namespace currency //--------------------------------------------------------------- uint64_t get_string_uint64_hash(const std::string& str); + bool construct_tx_out(const tx_destination_entry& de, const crypto::secret_key& tx_sec_key, size_t output_index, transaction& tx, std::set& deriv_cache, const account_keys& self, finalized_tx& result, uint8_t tx_outs_attr = CURRENCY_TO_KEY_OUT_RELAXED); bool construct_tx_out(const tx_destination_entry& de, const crypto::secret_key& tx_sec_key, size_t output_index, transaction& tx, std::set& deriv_cache, const account_keys& self, uint8_t tx_outs_attr = CURRENCY_TO_KEY_OUT_RELAXED); bool validate_alias_name(const std::string& al); bool validate_password(const std::string& password); @@ -186,6 +238,7 @@ namespace currency uint8_t tx_outs_attr = CURRENCY_TO_KEY_OUT_RELAXED, bool shuffle = true, uint64_t flags = 0); + bool construct_tx(const account_keys& sender_account_keys, const std::vector& sources, const std::vector& destinations, @@ -200,6 +253,9 @@ namespace currency bool shuffle = true, uint64_t flags = 0); + bool construct_tx(const account_keys& sender_account_keys, const finalize_tx_param& param, finalized_tx& result); + + 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); bool sign_extra_alias_entry(extra_alias_entry& ai, const crypto::public_key& pkey, const crypto::secret_key& skey); diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index e05d063d..91115f5f 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -800,7 +800,7 @@ void wallet2::accept_proposal(const crypto::hash& contract_id, uint64_t b_accept construct_param.extra.push_back(tsa); //build transaction - finalize_tx_param ftp = AUTO_VAL_INIT(ftp); + currency::finalize_tx_param ftp = AUTO_VAL_INIT(ftp); prepare_transaction(construct_param, ftp, tx); mark_transfers_as_spent(ftp.selected_transfers, std::string("contract <") + epee::string_tools::pod_to_hex(contract_id) + "> has been accepted with tx <" + epee::string_tools::pod_to_hex(get_transaction_hash(tx)) + ">"); @@ -930,7 +930,7 @@ void wallet2::request_cancel_contract(const crypto::hash& contract_id, uint64_t construct_param.crypt_address = contr_it->second.private_detailes.b_addr; construct_param.split_strategy_id = detail::ssi_digit; - finalize_tx_param ftp = AUTO_VAL_INIT(ftp); + currency::finalize_tx_param ftp = AUTO_VAL_INIT(ftp); prepare_transaction(construct_param, ftp); currency::transaction tx = AUTO_VAL_INIT(tx); crypto::secret_key sk = AUTO_VAL_INIT(sk); @@ -2891,7 +2891,7 @@ void wallet2::sign_transfer(const std::string& tx_sources_blob, std::string& sig std::string decrypted_src_blob = crypto::chacha_crypt(tx_sources_blob, m_account.get_keys().view_secret_key); // deserialize args - finalized_tx ft = AUTO_VAL_INIT(ft); + currency::finalized_tx ft = AUTO_VAL_INIT(ft); bool r = t_unserializable_object_from_blob(ft.ftp, decrypted_src_blob); THROW_IF_FALSE_WALLET_EX(r, error::wallet_common_error, "Failed to decrypt tx sources blob"); @@ -2973,7 +2973,7 @@ void wallet2::submit_transfer(const std::string& signed_tx_blob, currency::trans std::string decrypted_src_blob = crypto::chacha_crypt(signed_tx_blob, m_account.get_keys().view_secret_key); // deserialize tx data - finalized_tx ft = AUTO_VAL_INIT(ft); + currency::finalized_tx ft = AUTO_VAL_INIT(ft); bool r = t_unserializable_object_from_blob(ft, decrypted_src_blob); THROW_IF_FALSE_WALLET_EX(r, error::wallet_common_error, "Failed to decrypt signed tx data"); tx = ft.tx; @@ -3741,7 +3741,7 @@ void wallet2::build_escrow_release_templates(crypto::hash multisig_id, tsa.instruction = BC_ESCROW_SERVICE_INSTRUCTION_RELEASE_NORMAL; construct_params.extra.push_back(tsa); { - finalize_tx_param ftp = AUTO_VAL_INIT(ftp); + currency::finalize_tx_param ftp = AUTO_VAL_INIT(ftp); prepare_transaction(construct_params, ftp); crypto::secret_key sk = AUTO_VAL_INIT(sk); finalize_transaction(ftp, tx_release_template, sk, false); @@ -3757,7 +3757,7 @@ void wallet2::build_escrow_release_templates(crypto::hash multisig_id, tsa.instruction = BC_ESCROW_SERVICE_INSTRUCTION_RELEASE_BURN; construct_params.extra.push_back(tsa); { - finalize_tx_param ftp = AUTO_VAL_INIT(ftp); + currency::finalize_tx_param ftp = AUTO_VAL_INIT(ftp); prepare_transaction(construct_params, ftp); crypto::secret_key sk = AUTO_VAL_INIT(sk); finalize_transaction(ftp, tx_burn_template, sk, false); @@ -3777,7 +3777,7 @@ void wallet2::build_escrow_cancel_template(crypto::hash multisig_id, "multisig id out amount no more than escrow total amount"); construct_tx_param construct_params = AUTO_VAL_INIT(construct_params); - finalize_tx_param ftp = AUTO_VAL_INIT(ftp); + currency::finalize_tx_param ftp = AUTO_VAL_INIT(ftp); construct_params.fee = it->second.amount() - (ecrow_details.amount_a_pledge + ecrow_details.amount_to_pay + ecrow_details.amount_b_pledge); construct_params.multisig_id = multisig_id; construct_params.split_strategy_id = detail::ssi_digit; @@ -3859,7 +3859,7 @@ void wallet2::build_escrow_template(const bc_services::contract_private_details& ctp.attachments.push_back(att); } - finalize_tx_param ftp = AUTO_VAL_INIT(ftp); + currency::finalize_tx_param ftp = AUTO_VAL_INIT(ftp); prepare_transaction(ctp, ftp, tx); selected_transfers = ftp.selected_transfers; @@ -3991,7 +3991,7 @@ void wallet2::send_escrow_proposal(const bc_services::contract_private_details& ctp.tx_outs_attr = CURRENCY_TO_KEY_OUT_RELAXED; ctp.unlock_time = unlock_time; - finalize_tx_param ftp = AUTO_VAL_INIT(ftp); + currency::finalize_tx_param ftp = AUTO_VAL_INIT(ftp); try { prepare_transaction(ctp, ftp); @@ -4013,22 +4013,20 @@ void wallet2::send_escrow_proposal(const bc_services::contract_private_details& print_tx_sent_message(tx, "(from multisig)", fee); } //---------------------------------------------------------------------------------------------------- -void wallet2::create_htlc_proposal(uint64_t amount, const currency::account_public_address& addr, uint64_t lock_blocks_count, currency::transaction &tx, const crypto::hash& htlc_hash) +void wallet2::create_htlc_proposal(uint64_t amount, const currency::account_public_address& addr, uint64_t lock_blocks_count, currency::transaction &tx, std::string &origin) { - std::vector extra; - std::vector attachments; - - std::vector dst; - dst.resize(1); - dst.back().addr.push_back(addr); - dst.back().amount = amount; - destination_option_htlc_out& htlc_option = dst.back().htlc_options; + construct_tx_param ctp = get_default_construct_tx_param(); + ctp.fee = TX_DEFAULT_FEE; + ctp.dsts.resize(1); + ctp.dsts.back().addr.push_back(addr); + ctp.dsts.back().amount = amount; + destination_option_htlc_out& htlc_option = ctp.dsts.back().htlc_options; htlc_option.expiration = 740; //about 12 hours - htlc_option.htlc_hash = htlc_hash; - + htlc_option.htlc_hash = null_hash; - transaction result_tx = AUTO_VAL_INIT(result_tx); - this->transfer(dst, 0, 0, TX_DEFAULT_FEE, extra, attachments, tools::detail::ssi_digit, tools::tx_dust_policy(DEFAULT_DUST_THRESHOLD), result_tx); + finalized_tx ft = AUTO_VAL_INIT(ft); + this->transfer(ctp, ft, true, nullptr); + origin = ft.htlc_origin; } //---------------------------------------------------------------------------------------------------- void wallet2::get_list_of_active_htlc(bool only_redeem_txs, std::list& htlcs) @@ -4840,7 +4838,7 @@ void wallet2::prepare_tx_destinations(uint64_t needed_money, } } //---------------------------------------------------------------------------------------------------- -void wallet2::prepare_transaction(construct_tx_param& ctp, finalize_tx_param& ftp, const currency::transaction& tx_for_mode_separate /* = currency::transaction() */) +void wallet2::prepare_transaction(construct_tx_param& ctp, currency::finalize_tx_param& ftp, const currency::transaction& tx_for_mode_separate /* = currency::transaction() */) { TIME_MEASURE_START_MS(get_needed_money_time); uint64_t needed_money = get_needed_money(ctp.fee, ctp.dsts); @@ -4911,7 +4909,15 @@ void wallet2::prepare_transaction(construct_tx_param& ctp, finalize_tx_param& ft LOG_LEVEL_0);*/ } //---------------------------------------------------------------------------------------------------- -void wallet2::finalize_transaction(const finalize_tx_param& ftp, currency::transaction& tx, crypto::secret_key& tx_key, bool broadcast_tx, bool store_tx_secret_key /* = true */) +void wallet2::finalize_transaction(const currency::finalize_tx_param& ftp, currency::transaction& tx, crypto::secret_key& tx_key, bool broadcast_tx, bool store_tx_secret_key /* = true */) +{ + currency::finalized_tx result = AUTO_VAL_INIT(result); + finalize_transaction(ftp, result, broadcast_tx, store_tx_secret_key); + tx = result.tx; + tx_key = result.one_time_key; +} +//---------------------------------------------------------------------------------------------------- +void wallet2::finalize_transaction(const currency::finalize_tx_param& ftp, currency::finalized_tx& result, bool broadcast_tx, bool store_tx_secret_key /* = true */) { // NOTE: if broadcast_tx == true callback rise_on_transfer2() may be called at the end of this function. // That callback may call balance(), so it's important to have all used/spending transfers @@ -4922,18 +4928,7 @@ void wallet2::finalize_transaction(const finalize_tx_param& ftp, currency::trans //TIME_MEASURE_START_MS(construct_tx_time); bool r = currency::construct_tx(m_account.get_keys(), - ftp.sources, - ftp.prepared_destinations, - ftp.extra, - ftp.attachments, - tx, - tx_key, - ftp.unlock_time, - ftp.crypt_address, - 0, // expiration time - ftp.tx_outs_attr, - ftp.shuffle, - ftp.flags); + ftp, result); //TIME_MEASURE_FINISH_MS(construct_tx_time); THROW_IF_FALSE_WALLET_EX(r, error::tx_not_constructed, ftp.sources, ftp.prepared_destinations, ftp.unlock_time); @@ -4946,24 +4941,24 @@ void wallet2::finalize_transaction(const finalize_tx_param& ftp, currency::trans WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(it != m_multisig_transfers.end(), "can't find multisig_id: " << ftp.multisig_id); const currency::transaction& ms_source_tx = it->second.m_ptx_wallet_info->m_tx; bool is_tx_input_fully_signed = false; - r = sign_multisig_input_in_tx(tx, 0, m_account.get_keys(), ms_source_tx, &is_tx_input_fully_signed); // it's assumed that ms input is the first one (index 0) + r = sign_multisig_input_in_tx(result.tx, 0, m_account.get_keys(), ms_source_tx, &is_tx_input_fully_signed); // it's assumed that ms input is the first one (index 0) WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(r && !is_tx_input_fully_signed, "sign_multisig_input_in_tx failed: r = " << r << ", is_tx_input_fully_signed = " << is_tx_input_fully_signed); } //TIME_MEASURE_FINISH_MS(sign_ms_input_time); - THROW_IF_FALSE_WALLET_EX(get_object_blobsize(tx) < CURRENCY_MAX_TRANSACTION_BLOB_SIZE, error::tx_too_big, tx, m_upper_transaction_size_limit); + THROW_IF_FALSE_WALLET_EX(get_object_blobsize(result.tx) < CURRENCY_MAX_TRANSACTION_BLOB_SIZE, error::tx_too_big, result.tx, m_upper_transaction_size_limit); if (store_tx_secret_key) - m_tx_keys.insert(std::make_pair(get_transaction_hash(tx), tx_key)); + m_tx_keys.insert(std::make_pair(get_transaction_hash(result.tx), result.one_time_key)); //TIME_MEASURE_START(send_transaction_to_network_time); if (broadcast_tx) - send_transaction_to_network(tx); + send_transaction_to_network(result.tx); //TIME_MEASURE_FINISH(send_transaction_to_network_time); //TIME_MEASURE_START(add_sent_tx_detailed_info_time); if (broadcast_tx) - add_sent_tx_detailed_info(tx, ftp.prepared_destinations, ftp.selected_transfers); + add_sent_tx_detailed_info(result.tx, ftp.prepared_destinations, ftp.selected_transfers); //TIME_MEASURE_FINISH(add_sent_tx_detailed_info_time); /* TODO @@ -5035,7 +5030,7 @@ const construct_tx_param& wallet2::get_default_construct_tx_param() return ctp; } //---------------------------------------------------------------------------------------------------- -bool wallet2::store_unsigned_tx_to_file_and_reserve_transfers(const finalize_tx_param& ftp, const std::string& filename, std::string* p_unsigned_tx_blob_str /* = nullptr */) +bool wallet2::store_unsigned_tx_to_file_and_reserve_transfers(const currency::finalize_tx_param& ftp, const std::string& filename, std::string* p_unsigned_tx_blob_str /* = nullptr */) { TIME_MEASURE_START(store_unsigned_tx_time); blobdata bl = t_serializable_object_to_blob(ftp); @@ -5096,13 +5091,23 @@ void wallet2::transfer(construct_tx_param& ctp, currency::transaction &tx, bool send_to_network, std::string* p_unsigned_filename_or_tx_blob_str) +{ + currency::finalized_tx result = AUTO_VAL_INIT(result); + transfer(ctp, result, send_to_network, p_unsigned_filename_or_tx_blob_str); + tx = result.tx; +} +//---------------------------------------------------------------------------------------------------- +void wallet2::transfer(construct_tx_param& ctp, + currency::finalized_tx& result, + bool send_to_network, + std::string* p_unsigned_filename_or_tx_blob_str) { WLT_THROW_IF_FALSE_WALLET_CMN_ERR_EX(!is_auditable() || !is_watch_only(), "You can't initiate coins transfer using an auditable watch-only wallet."); // btw, watch-only wallets can call transfer() within cold-signing process check_and_throw_if_self_directed_tx_with_payment_id_requested(ctp); TIME_MEASURE_START(prepare_transaction_time); - finalize_tx_param ftp = AUTO_VAL_INIT(ftp); + currency::finalize_tx_param ftp = AUTO_VAL_INIT(ftp); prepare_transaction(ctp, ftp); TIME_MEASURE_FINISH(prepare_transaction_time); @@ -5115,18 +5120,17 @@ void wallet2::transfer(construct_tx_param& ctp, } TIME_MEASURE_START(mark_transfers_as_spent_time); - mark_transfers_as_spent(ftp.selected_transfers, std::string("money transfer, tx: ") + epee::string_tools::pod_to_hex(get_transaction_hash(tx))); + mark_transfers_as_spent(ftp.selected_transfers, std::string("money transfer, tx: ") + epee::string_tools::pod_to_hex(get_transaction_hash(result.tx))); TIME_MEASURE_FINISH(mark_transfers_as_spent_time); TIME_MEASURE_START(finalize_transaction_time); try { - crypto::secret_key sk = AUTO_VAL_INIT(sk); - finalize_transaction(ftp, tx, sk, send_to_network); + finalize_transaction(ftp, result, send_to_network); } catch (...) { - clear_transfers_from_flag(ftp.selected_transfers, WALLET_TRANSFER_DETAIL_FLAG_SPENT, std::string("exception on money transfer, tx: ") + epee::string_tools::pod_to_hex(get_transaction_hash(tx))); + clear_transfers_from_flag(ftp.selected_transfers, WALLET_TRANSFER_DETAIL_FLAG_SPENT, std::string("exception on money transfer, tx: ") + epee::string_tools::pod_to_hex(get_transaction_hash(result.tx))); throw; } TIME_MEASURE_FINISH(finalize_transaction_time); @@ -5139,7 +5143,7 @@ void wallet2::transfer(construct_tx_param& ctp, << ", mark_transfers_as_spent_time: " << print_fixed_decimal_point(mark_transfers_as_spent_time, 3) , LOG_LEVEL_0); - print_tx_sent_message(tx, std::string() + "(transfer)", ctp.fee); + print_tx_sent_message(result.tx, std::string() + "(transfer)", ctp.fee); } //---------------------------------------------------------------------------------------------------- void wallet2::sweep_below(size_t fake_outs_count, const currency::account_public_address& destination_addr, uint64_t threshold_amount, const currency::payment_id_t& payment_id, @@ -5209,7 +5213,7 @@ void wallet2::sweep_below(size_t fake_outs_count, const currency::account_public THROW_IF_FALSE_WALLET_EX(scanty_outs.empty(), error::not_enough_outs_to_mix, scanty_outs, fake_outs_count); } - finalize_tx_param ftp = AUTO_VAL_INIT(ftp); + currency::finalize_tx_param ftp = AUTO_VAL_INIT(ftp); if (!payment_id.empty()) set_payment_id_to_tx(ftp.attachments, payment_id); // put encrypted payer info into the extra @@ -5232,7 +5236,7 @@ void wallet2::sweep_below(size_t fake_outs_count, const currency::account_public { return t == rc_ok ? "rc_ok" : t == rc_too_few_outputs ? "rc_too_few_outputs" : t == rc_too_many_outputs ? "rc_too_many_outputs" : t == rc_create_tx_failed ? "rc_create_tx_failed" : "unknown"; }; auto try_construct_tx = [this, &selected_transfers, &rpc_get_random_outs_resp, &fake_outs_count, &fee, &destination_addr] - (size_t st_index_upper_boundary, finalize_tx_param& ftp, uint64_t& amount_swept) -> try_construct_result_t + (size_t st_index_upper_boundary, currency::finalize_tx_param& ftp, uint64_t& amount_swept) -> try_construct_result_t { // prepare inputs amount_swept = 0; @@ -5319,7 +5323,7 @@ void wallet2::sweep_below(size_t fake_outs_count, const currency::account_public WLT_LOG_L1("sweep_below: first try of try_construct_tx(" << st_index_upper_boundary << ") returned " << get_result_t_str(res)); size_t low_bound = 0; size_t high_bound = st_index_upper_boundary; - finalize_tx_param ftp_ok = ftp; + currency::finalize_tx_param ftp_ok = ftp; for (;;) { if (low_bound + 1 >= high_bound) diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 7a15354d..ce272372 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -286,52 +286,52 @@ namespace tools bool perform_packing; }; - struct finalize_tx_param - { - uint64_t unlock_time; - std::vector extra; - std::vector attachments; - currency::account_public_address crypt_address; - uint8_t tx_outs_attr; - bool shuffle; - uint8_t flags; - crypto::hash multisig_id; - std::vector sources; - std::vector selected_transfers; - std::vector prepared_destinations; - - crypto::public_key spend_pub_key; // only for validations - - BEGIN_SERIALIZE_OBJECT() - FIELD(unlock_time) - FIELD(extra) - FIELD(attachments) - FIELD(crypt_address) - FIELD(tx_outs_attr) - FIELD(shuffle) - FIELD(flags) - FIELD(multisig_id) - FIELD(sources) - FIELD(selected_transfers) - FIELD(prepared_destinations) - FIELD(spend_pub_key) - END_SERIALIZE() - }; - - struct finalized_tx - { - currency::transaction tx; - crypto::secret_key one_time_key; - finalize_tx_param ftp; - std::vector> outs_key_images; // pairs (out_index, key_image) for each change output - - BEGIN_SERIALIZE_OBJECT() - FIELD(tx) - FIELD(one_time_key) - FIELD(ftp) - FIELD(outs_key_images) - END_SERIALIZE() - }; +// struct currency::finalize_tx_param +// { +// uint64_t unlock_time; +// std::vector extra; +// std::vector attachments; +// currency::account_public_address crypt_address; +// uint8_t tx_outs_attr; +// bool shuffle; +// uint8_t flags; +// crypto::hash multisig_id; +// std::vector sources; +// std::vector selected_transfers; +// std::vector prepared_destinations; +// +// crypto::public_key spend_pub_key; // only for validations +// +// BEGIN_SERIALIZE_OBJECT() +// FIELD(unlock_time) +// FIELD(extra) +// FIELD(attachments) +// FIELD(crypt_address) +// FIELD(tx_outs_attr) +// FIELD(shuffle) +// FIELD(flags) +// FIELD(multisig_id) +// FIELD(sources) +// FIELD(selected_transfers) +// FIELD(prepared_destinations) +// FIELD(spend_pub_key) +// END_SERIALIZE() +// }; +// +// struct currency::finalized_tx +// { +// currency::transaction tx; +// crypto::secret_key one_time_key; +// currency::finalize_tx_param ftp; +// std::vector> outs_key_images; // pairs (out_index, key_image) for each change output +// +// BEGIN_SERIALIZE_OBJECT() +// FIELD(tx) +// FIELD(one_time_key) +// FIELD(ftp) +// FIELD(outs_key_images) +// END_SERIALIZE() +// }; class wallet2 { @@ -580,6 +580,12 @@ namespace tools currency::transaction &tx, bool send_to_network, std::string* p_unsigned_filename_or_tx_blob_str); + + void transfer(construct_tx_param& ctp, + currency::finalized_tx& result, + bool send_to_network, + std::string* p_unsigned_filename_or_tx_blob_str); + template void transfer_from_contract( @@ -820,8 +826,10 @@ namespace tools const std::list& get_expiration_entries() const { return m_money_expirations; }; bool get_tx_key(const crypto::hash &txid, crypto::secret_key &tx_key) const; - void prepare_transaction(construct_tx_param& ctp, finalize_tx_param& ftp, const currency::transaction& tx_for_mode_separate = currency::transaction()); - void finalize_transaction(const finalize_tx_param& ftp, currency::transaction& tx, crypto::secret_key& tx_key, bool broadcast_tx, bool store_tx_secret_key = true); + void prepare_transaction(construct_tx_param& ctp, currency::finalize_tx_param& ftp, const currency::transaction& tx_for_mode_separate = currency::transaction()); + + void finalize_transaction(const currency::finalize_tx_param& ftp, currency::transaction& tx, crypto::secret_key& tx_key, bool broadcast_tx, bool store_tx_secret_key = true); + void finalize_transaction(const currency::finalize_tx_param& ftp, currency::finalized_tx& result, bool broadcast_tx, bool store_tx_secret_key = true ); std::string get_log_prefix() const { return m_log_prefix; } static uint64_t get_max_unlock_time_from_receive_indices(const currency::transaction& tx, const money_transfer2_details& td); @@ -836,7 +844,7 @@ namespace tools opener-hash will be given by other side */ void create_htlc_proposal(uint64_t amount, const currency::account_public_address& addr, uint64_t lock_blocks_count, - currency::transaction &tx, const crypto::hash& htlc_hash = currency::null_hash); + currency::transaction &tx, std::string &origin); void get_list_of_active_htlc(bool only_redeem_txs, std::list& htlcs); void redeem_htlc(const crypto::hash& htlc_tx_id, std::string origin); @@ -960,7 +968,7 @@ private: void exception_handler() const; uint64_t get_minimum_allowed_fee_for_contract(const crypto::hash& ms_id); bool generate_packing_transaction_if_needed(currency::transaction& tx, uint64_t fake_outputs_number); - bool store_unsigned_tx_to_file_and_reserve_transfers(const finalize_tx_param& ftp, const std::string& filename, std::string* p_unsigned_tx_blob_str = nullptr); + bool store_unsigned_tx_to_file_and_reserve_transfers(const currency::finalize_tx_param& ftp, const std::string& filename, std::string* p_unsigned_tx_blob_str = nullptr); void check_and_throw_if_self_directed_tx_with_payment_id_requested(const construct_tx_param& ctp); void push_new_block_id(const crypto::hash& id, uint64_t height); bool lookup_item_around(uint64_t i, std::pair& result); diff --git a/tests/core_tests/atomic_tests.cpp b/tests/core_tests/atomic_tests.cpp new file mode 100644 index 00000000..d45d5581 --- /dev/null +++ b/tests/core_tests/atomic_tests.cpp @@ -0,0 +1,136 @@ +// Copyright (c) 2014-2021 Zano Project +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "chaingen.h" +#include "escrow_wallet_tests.h" +#include "random_helper.h" +#include "chaingen_helpers.h" +#include "atomic_tests.h" + +using namespace epee; +using namespace crypto; +using namespace currency; + +//============================================================================================================================== + +struct wallet_tests_callback_handler : public tools::i_wallet2_callback +{ + virtual void on_transfer2(const tools::wallet_public::wallet_transfer_info& wti, uint64_t balance, uint64_t unlocked_balance, uint64_t total_mined) + { + all_wtis.push_back(wti); + } + + std::vector all_wtis; +}; + +atomic_simple_test::atomic_simple_test() +{ + REGISTER_CALLBACK_METHOD(atomic_simple_test, c1); +} + +bool atomic_simple_test::generate(std::vector& events) const +{ + epee::debug::get_set_enable_assert(true, true); + + currency::account_base genesis_acc; + genesis_acc.generate(); + m_mining_accunt.generate(); + + + block blk_0 = AUTO_VAL_INIT(blk_0); + generator.construct_genesis_block(blk_0, genesis_acc, test_core_time::get_time()); + events.push_back(blk_0); + + REWIND_BLOCKS_N(events, blk_0r, blk_0, m_mining_accunt, CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 5); + + DO_CALLBACK(events, "c1"); + epee::debug::get_set_enable_assert(true, false); + return true; +} + + + +bool atomic_simple_test::c1(currency::core& c, size_t ev_index, const std::vector& events) +{ + epee::debug::get_set_enable_assert(true, true); + misc_utils::auto_scope_leave_caller scope_exit_handler = misc_utils::create_scope_leave_handler([&](){epee::debug::get_set_enable_assert(true, false); }); + + + /* + let's pretend that we have two different blockchains: A and B + Alice and Bob want to have atomic swap via htlc, both has wallets in blockchain A and B + Steps are following: + 1. Alice initiate swap by sending to Bob HTLC in blockchain A + 2. Bob see HTLC to his address in blockchain A and create HTLC with + the same hash addressed to Alice in blockchain B + 3. Alice see HTLC addressed to her in blockchain B and creates there redeem transaction, which reveals HTLC origin + 4. Bob observe origin revealed by Alice in blockchain B and create redeem transaction in blockchain A + 5. Everybody check balances and celebrate successful atomic swap + + */ + + currency::account_base accunt_alice_blockchain_a; + currency::account_base accunt_alice_blockchain_b; + currency::account_base accunt_bob_blockchain_a; + currency::account_base accunt_bob_blockchain_b; + accunt_alice_blockchain_a.generate(); + accunt_alice_blockchain_b.generate(); + accunt_bob_blockchain_a.generate(); + accunt_bob_blockchain_b.generate(); + + LOG_PRINT_MAGENTA("Mining Address: " << currency::get_account_address_as_str(m_mining_accunt.get_public_address()), LOG_LEVEL_0); + LOG_PRINT_MAGENTA("Alice [A] Address: " << currency::get_account_address_as_str(accunt_alice_blockchain_a.get_public_address()), LOG_LEVEL_0); + LOG_PRINT_MAGENTA("Alice [B] Address: " << currency::get_account_address_as_str(accunt_alice_blockchain_b.get_public_address()), LOG_LEVEL_0); + LOG_PRINT_MAGENTA("Bob [A] Address: " << currency::get_account_address_as_str(accunt_bob_blockchain_a.get_public_address()), LOG_LEVEL_0); + LOG_PRINT_MAGENTA("Bob [B] Address: " << currency::get_account_address_as_str(accunt_bob_blockchain_b.get_public_address()), LOG_LEVEL_0); + +#define AMOUNT_TO_TRANSFER_HTLC (TESTS_DEFAULT_FEE*10) + + std::shared_ptr miner_wlt = init_playtime_test_wallet(events, c, m_mining_accunt); + + size_t blocks_fetched = 0; + bool received_money = false; + std::atomic atomic_false = ATOMIC_VAR_INIT(false); + miner_wlt->refresh(blocks_fetched, received_money, atomic_false); + CHECK_AND_FORCE_ASSERT_MES(c.get_pool_transactions_count() == 0, false, "Incorrect txs count in the pool"); + + uint64_t transfer_amount = AMOUNT_TO_TRANSFER_HTLC + TESTS_DEFAULT_FEE; + miner_wlt->transfer(transfer_amount, accunt_alice_blockchain_a.get_public_address()); + LOG_PRINT_MAGENTA("Transaction sent to Alice A: " << transfer_amount, LOG_LEVEL_0); + + miner_wlt->transfer(transfer_amount, accunt_bob_blockchain_a.get_public_address()); + LOG_PRINT_MAGENTA("Transaction sent to Bob A: " << transfer_amount, LOG_LEVEL_0); + + bool r = mine_next_pow_blocks_in_playtime(m_mining_accunt.get_public_address(), c, CURRENCY_MINED_MONEY_UNLOCK_WINDOW); + CHECK_AND_ASSERT_MES(r, false, "mine_next_pow_blocks_in_playtime failed"); + + //create wallet instances and calculate balances + std::shared_ptr alice_a_wlt_instance = init_playtime_test_wallet(events, c, accunt_alice_blockchain_a); + std::shared_ptr alice_b_wlt_instance = init_playtime_test_wallet(events, c, accunt_alice_blockchain_b); + std::shared_ptr bob_a_wlt_instance = init_playtime_test_wallet(events, c, accunt_bob_blockchain_a); + std::shared_ptr bob_b_wlt_instance = init_playtime_test_wallet(events, c, accunt_bob_blockchain_b); +// std::shared_ptr backend_mock(new wallet_tests_callback_handler()); +// alice_a_wlt_instance->callback(backend_mock); + + alice_a_wlt_instance->refresh(); + alice_b_wlt_instance->refresh(); + bob_a_wlt_instance->refresh(); + bob_a_wlt_instance->refresh(); + + CHECK_AND_FORCE_ASSERT_MES(alice_a_wlt_instance->balance() == transfer_amount, false, "Incorrect balance"); + CHECK_AND_FORCE_ASSERT_MES(bob_a_wlt_instance->balance() == transfer_amount, false, "Incorrect balance"); + + + std::string alice_origin; //will be deterministically generated by Alice's A wallet + currency::transaction res_tx = AUTO_VAL_INIT(res_tx); + alice_a_wlt_instance->create_htlc_proposal(transfer_amount, bob_a_wlt_instance->get_account().get_public_address(), 20, res_tx, alice_origin); + + + + return r; +} + + +//------------------------------------------------------------------------------ + diff --git a/tests/core_tests/atomic_tests.h b/tests/core_tests/atomic_tests.h new file mode 100644 index 00000000..4c8c93fe --- /dev/null +++ b/tests/core_tests/atomic_tests.h @@ -0,0 +1,19 @@ +// Copyright (c) 2014-2021 Zano Project +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once +#include "chaingen.h" +#include "wallet_tests_basic.h" + + +struct atomic_simple_test : public wallet_test +{ + atomic_simple_test(); + bool generate(std::vector& events) const; + bool c1(currency::core& c, size_t ev_index, const std::vector& events); +private: + mutable currency::account_base m_mining_accunt; + +}; + diff --git a/tests/core_tests/chaingen_main.cpp b/tests/core_tests/chaingen_main.cpp index c61c8986..96f485e0 100644 --- a/tests/core_tests/chaingen_main.cpp +++ b/tests/core_tests/chaingen_main.cpp @@ -1020,6 +1020,8 @@ int main(int argc, char* argv[]) GENERATE_AND_PLAY(hard_fork_2_incorrect_alias_update); GENERATE_AND_PLAY(hard_fork_2_incorrect_alias_update); + // atomics + GENERATE_AND_PLAY(atomic_simple_test); // GENERATE_AND_PLAY(gen_block_reward); // END OF TESTS */ diff --git a/tests/core_tests/chaingen_tests_list.h b/tests/core_tests/chaingen_tests_list.h index bccf06ee..2a419569 100644 --- a/tests/core_tests/chaingen_tests_list.h +++ b/tests/core_tests/chaingen_tests_list.h @@ -38,3 +38,4 @@ #include "hard_fork_1_bad_pos_source.h" #include "hard_fork_1.h" #include "hard_fork_2.h" +#include "atomic_tests.h"