diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 45ccaa692..47e6fd45b 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -1777,7 +1777,7 @@ bool simple_wallet::show_incoming_transfers(const std::vector& args } std::string verbose_string; if (verbose) - verbose_string = (boost::format("%68s%68s") % td.get_public_key() % td.m_key_image).str(); + verbose_string = (boost::format("%68s%68s") % td.get_public_key() % (td.m_key_image_known ? epee::string_tools::pod_to_hex(td.m_key_image) : std::string('?', 64))).str(); message_writer(td.m_spent ? epee::log_space::console_color_magenta : epee::log_space::console_color_green, false) << boost::format("%21s%8s%12s%8s%16u%68s%s") % print_money(td.amount()) % @@ -2774,7 +2774,7 @@ bool simple_wallet::sweep_all(const std::vector &args_) return true; } //---------------------------------------------------------------------------------------------------- -bool simple_wallet::accept_loaded_tx(const std::function get_num_txes, const std::function &get_tx) +bool simple_wallet::accept_loaded_tx(const std::function get_num_txes, const std::function &get_tx, const std::string &extra_message) { // gather info to ask the user uint64_t amount = 0, amount_to_dests = 0, change = 0; @@ -2847,19 +2847,25 @@ bool simple_wallet::accept_loaded_tx(const std::function get_num_txes, change_string += tr("no change"); uint64_t fee = amount - amount_to_dests; - std::string prompt_str = (boost::format(tr("Loaded %lu transactions, for %s, fee %s, %s, %s, with min mixin %lu. Is this okay? (Y/Yes/N/No)")) % (unsigned long)get_num_txes() % print_money(amount) % print_money(fee) % dest_string % change_string % (unsigned long)min_mixin).str(); + std::string prompt_str = (boost::format(tr("Loaded %lu transactions, for %s, fee %s, %s, %s, with min mixin %lu. %sIs this okay? (Y/Yes/N/No)")) % (unsigned long)get_num_txes() % print_money(amount) % print_money(fee) % dest_string % change_string % (unsigned long)min_mixin % extra_message).str(); std::string accepted = command_line::input_line(prompt_str); return is_it_true(accepted); } //---------------------------------------------------------------------------------------------------- bool simple_wallet::accept_loaded_tx(const tools::wallet2::unsigned_tx_set &txs) { - return accept_loaded_tx([&txs](){return txs.txes.size();}, [&txs](size_t n)->const tools::wallet2::tx_construction_data&{return txs.txes[n];}); + std::string extra_message; + if (!txs.transfers.empty()) + extra_message = (boost::format("%u outputs to import. ") % (unsigned)txs.transfers.size()).str(); + return accept_loaded_tx([&txs](){return txs.txes.size();}, [&txs](size_t n)->const tools::wallet2::tx_construction_data&{return txs.txes[n];}, extra_message); } //---------------------------------------------------------------------------------------------------- bool simple_wallet::accept_loaded_tx(const tools::wallet2::signed_tx_set &txs) { - return accept_loaded_tx([&txs](){return txs.ptx.size();}, [&txs](size_t n)->const tools::wallet2::tx_construction_data&{return txs.ptx[n].construction_data;}); + std::string extra_message; + if (!txs.key_images.empty()) + extra_message = (boost::format("%u key images to import. ") % (unsigned)txs.key_images.size()).str(); + return accept_loaded_tx([&txs](){return txs.ptx.size();}, [&txs](size_t n)->const tools::wallet2::tx_construction_data&{return txs.ptx[n].construction_data;}, extra_message); } //---------------------------------------------------------------------------------------------------- bool simple_wallet::sign_transfer(const std::vector &args_) @@ -2870,9 +2876,10 @@ bool simple_wallet::sign_transfer(const std::vector &args_) return true; } + std::vector ptx; try { - bool r = m_wallet->sign_tx("unsigned_monero_tx", "signed_monero_tx", [&](const tools::wallet2::unsigned_tx_set &tx){ return accept_loaded_tx(tx); }); + bool r = m_wallet->sign_tx("unsigned_monero_tx", "signed_monero_tx", ptx, [&](const tools::wallet2::unsigned_tx_set &tx){ return accept_loaded_tx(tx); }); if (!r) { fail_msg_writer() << tr("Failed to sign transaction"); @@ -2885,7 +2892,14 @@ bool simple_wallet::sign_transfer(const std::vector &args_) return true; } - success_msg_writer(true) << tr("Transaction successfully signed to file: ") << "signed_monero_tx"; + std::string txids_as_text; + for (const auto &t: ptx) + { + if (!txids_as_text.empty()) + txids_as_text += (", "); + txids_as_text += epee::string_tools::pod_to_hex(get_transaction_hash(t.tx)); + } + success_msg_writer(true) << tr("Transaction successfully signed to file ") << "signed_monero_tx" << ", txid " << txids_as_text; return true; } //---------------------------------------------------------------------------------------------------- diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h index 4fe1b0417..0d83f429b 100644 --- a/src/simplewallet/simplewallet.h +++ b/src/simplewallet/simplewallet.h @@ -155,7 +155,7 @@ namespace cryptonote uint64_t get_daemon_blockchain_height(std::string& err); bool try_connect_to_daemon(bool silent = false); bool ask_wallet_create_if_needed(); - bool accept_loaded_tx(const std::function get_num_txes, const std::function &get_tx); + bool accept_loaded_tx(const std::function get_num_txes, const std::function &get_tx, const std::string &extra_message = std::string()); bool accept_loaded_tx(const tools::wallet2::unsigned_tx_set &txs); bool accept_loaded_tx(const tools::wallet2::signed_tx_set &txs); bool get_address_from_str(const std::string &str, cryptonote::account_public_address &address, bool &has_payment_id, crypto::hash8 &payment_id); diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index bf7c1537a..093e5c2aa 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -2953,6 +2953,7 @@ bool wallet2::save_tx(const std::vector& ptx_vector, const std::stri unsigned_tx_set txs; for (auto &tx: ptx_vector) txs.txes.push_back(tx.construction_data); + txs.transfers = m_transfers; std::string s = obj_to_json_str(txs); if (s.empty()) return false; @@ -2963,7 +2964,7 @@ bool wallet2::save_tx(const std::vector& ptx_vector, const std::stri return epee::file_io_utils::save_string_to_file(filename, std::string(UNSIGNED_TX_PREFIX) + s); } //---------------------------------------------------------------------------------------------------- -bool wallet2::sign_tx(const std::string &unsigned_filename, const std::string &signed_filename, std::function accept_func) +bool wallet2::sign_tx(const std::string &unsigned_filename, const std::string &signed_filename, std::vector &txs, std::function accept_func) { std::string s; boost::system::error_code errcode; @@ -2998,6 +2999,8 @@ bool wallet2::sign_tx(const std::string &unsigned_filename, const std::string &s return false; } + import_outputs(exported_txs.transfers); + // sign the transactions signed_tx_set signed_txes; for (size_t n = 0; n < exported_txs.txes.size(); ++n) @@ -3043,6 +3046,17 @@ bool wallet2::sign_tx(const std::string &unsigned_filename, const std::string &s ptx.tx_key = rct::rct2sk(rct::identity()); // don't send it back to the untrusted view wallet ptx.dests = sd.splitted_dsts; ptx.construction_data = sd; + + txs.push_back(ptx); + } + + // add key images + signed_txes.key_images.resize(m_transfers.size()); + for (size_t i = 0; i < m_transfers.size(); ++i) + { + if (!m_transfers[i].m_key_image_known) + LOG_PRINT_L0("WARNING: key image not known in signing wallet at index " << i); + signed_txes.key_images[i] = m_transfers[i].m_key_image; } s = obj_to_json_str(signed_txes); @@ -3092,6 +3106,23 @@ bool wallet2::load_tx(const std::string &signed_filename, std::vector m_transfers.size()) + { + LOG_PRINT_L1("More key images returned that we know outputs for"); + return false; + } + for (size_t i = 0; i < signed_txs.key_images.size(); ++i) + { + transfer_details &td = m_transfers[i]; + if (td.m_key_image_known && td.m_key_image != signed_txs.key_images[i]) + LOG_PRINT_L0("WARNING: imported key image differs from previously known key image at index " << i << ": trusting imported one"); + td.m_key_image = signed_txs.key_images[i]; + m_key_images[m_transfers[i].m_key_image] = i; + td.m_key_image_known = true; + m_pub_keys[m_transfers[i].get_public_key()] = i; + } + ptx = signed_txs.ptx; return true; diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 417e754a5..d0075e2ec 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -133,6 +133,21 @@ namespace tools bool is_rct() const { return m_rct; } uint64_t amount() const { return m_amount; } const crypto::public_key &get_public_key() const { return boost::get(m_tx.vout[m_internal_output_index].target).key; } + + BEGIN_SERIALIZE_OBJECT() + FIELD(m_block_height) + FIELD(m_tx) + FIELD(m_txid) + FIELD(m_internal_output_index) + FIELD(m_global_output_index) + FIELD(m_spent) + FIELD(m_spent_height) + FIELD(m_key_image) + FIELD(m_mask) + FIELD(m_amount) + FIELD(m_rct) + FIELD(m_key_image_known) + END_SERIALIZE() }; struct payment_details @@ -226,16 +241,20 @@ namespace tools struct unsigned_tx_set { std::vector txes; + wallet2::transfer_container transfers; BEGIN_SERIALIZE_OBJECT() FIELD(txes) + FIELD(transfers) END_SERIALIZE() }; struct signed_tx_set { std::vector ptx; + std::vector key_images; BEGIN_SERIALIZE_OBJECT() FIELD(ptx) + FIELD(key_images) END_SERIALIZE() }; @@ -377,7 +396,7 @@ namespace tools void commit_tx(pending_tx& ptx_vector); void commit_tx(std::vector& ptx_vector); bool save_tx(const std::vector& ptx_vector, const std::string &filename); - bool sign_tx(const std::string &unsigned_filename, const std::string &signed_filename, std::function accept_func = NULL); + bool sign_tx(const std::string &unsigned_filename, const std::string &signed_filename, std::vector &ptx, std::function accept_func = NULL); bool load_tx(const std::string &signed_filename, std::vector &ptx, std::function accept_func = NULL); std::vector create_transactions(std::vector dsts, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector extra, bool trusted_daemon); std::vector create_transactions_2(std::vector dsts, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector extra, bool trusted_daemon);