From a99ab49dd58bfd8b9db7a98946a8536254eb80fa Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 13 Nov 2016 11:34:43 +0000 Subject: [PATCH 01/10] wallet: fix serialization of new m_key_image_known member --- src/wallet/wallet2.h | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index d42385ca..417e754a 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -611,7 +611,7 @@ namespace tools }; } BOOST_CLASS_VERSION(tools::wallet2, 15) -BOOST_CLASS_VERSION(tools::wallet2::transfer_details, 5) +BOOST_CLASS_VERSION(tools::wallet2::transfer_details, 6) BOOST_CLASS_VERSION(tools::wallet2::payment_details, 1) BOOST_CLASS_VERSION(tools::wallet2::unconfirmed_transfer_details, 6) BOOST_CLASS_VERSION(tools::wallet2::confirmed_transfer_details, 3) @@ -640,7 +640,10 @@ namespace boost { x.m_rct = x.m_tx.vout[x.m_internal_output_index].amount == 0; } - x.m_key_image_known = true; + if (ver < 6) + { + x.m_key_image_known = true; + } } template @@ -689,7 +692,18 @@ namespace boost } a & x.m_rct; if (ver < 5) + { + initialize_transfer_details(a, x, ver); return; + } + if (ver < 6) + { + // v5 did not properly initialize + uint8_t u; + a & u; + x.m_key_image_known = true; + return; + } a & x.m_key_image_known; } From 14cb088300423036d8aa30d583aeb7b31a7d6e24 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 13 Nov 2016 17:24:53 +0000 Subject: [PATCH 02/10] simplewallet: print public keys too on spendkey/viewkey commands --- src/simplewallet/simplewallet.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 3190c049..c07e6251 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -280,7 +280,8 @@ std::string simple_wallet::get_commands_str() bool simple_wallet::viewkey(const std::vector &args/* = std::vector()*/) { // don't log - std::cout << string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_view_secret_key) << std::endl; + std::cout << "secret: " << string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_view_secret_key) << std::endl; + std::cout << "public: " << string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_account_address.m_view_public_key) << std::endl; return true; } @@ -288,7 +289,8 @@ bool simple_wallet::viewkey(const std::vector &args/* = std::vector bool simple_wallet::spendkey(const std::vector &args/* = std::vector()*/) { // don't log - std::cout << string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_spend_secret_key) << std::endl; + std::cout << "secret: " << string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_spend_secret_key) << std::endl; + std::cout << "public: " << string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_account_address.m_spend_public_key) << std::endl; return true; } From 47413a56263a323776a79520d48e1542197bd9d8 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 13 Nov 2016 17:48:17 +0000 Subject: [PATCH 03/10] simplewallet: spell out change when signing a transfer Also catch change to multiple addresses, this is unexpected --- src/simplewallet/simplewallet.cpp | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index c07e6251..c9db4cb4 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -2803,7 +2803,12 @@ bool simple_wallet::accept_loaded_tx(const std::function get_num_txes, fail_msg_writer() << tr("Claimed change is larger than payment to the change address"); return false; } - change = cd.change_dts.amount; + if (memcmp(&cd.change_dts.addr, &get_tx(0).change_dts.addr, sizeof(cd.change_dts.addr))) + { + fail_msg_writer() << tr("Change does to more than one address"); + return false; + } + change += cd.change_dts.amount; it->second -= cd.change_dts.amount; if (it->second == 0) dests.erase(get_account_address_as_str(m_wallet->testnet(), cd.change_dts.addr)); @@ -2820,8 +2825,17 @@ bool simple_wallet::accept_loaded_tx(const std::function get_num_txes, if (dest_string.empty()) dest_string = tr("with no destinations"); + std::string change_string; + if (change > 0) + { + std::string address = get_account_address_as_str(m_wallet->testnet(), get_tx(0).change_dts.addr); + change_string += (boost::format(tr("%s change to %s")) % print_money(change) % address).str(); + } + else + 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, change %s, %s, with min mixin %lu. Is this okay? (Y/Yes/N/No)")) % (unsigned long)get_num_txes() % print_money(amount) % print_money(fee) % print_money(change) % dest_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. 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 accepted = command_line::input_line(prompt_str); return is_it_true(accepted); } From d72376d46748cb9917555ee13d6c0e643888357b Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 13 Nov 2016 18:20:46 +0000 Subject: [PATCH 04/10] simplewallet: add a verbose flag to incoming_transfers Prints pubkey and key image as well --- src/simplewallet/simplewallet.cpp | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index c9db4cb4..45ccaa69 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -1740,18 +1740,23 @@ bool simple_wallet::show_incoming_transfers(const std::vector& args bool filter = false; bool available = false; - if (!args.empty()) + bool verbose = false; + for (const auto &arg: args) { - if (args[0] == "available") + if (arg == "available") { filter = true; available = true; } - else if (args[0] == "unavailable") + else if (arg == "unavailable") { filter = true; available = false; } + else if (arg == "verbose") + { + verbose = true; + } } tools::wallet2::transfer_container transfers; @@ -1764,17 +1769,24 @@ bool simple_wallet::show_incoming_transfers(const std::vector& args { if (!transfers_found) { - message_writer() << boost::format("%21s%8s%12s%8s%16s%68s") % tr("amount") % tr("spent") % tr("unlocked") % tr("ringct") % tr("global index") % tr("tx id"); + std::string verbose_string; + if (verbose) + verbose_string = (boost::format("%68s%68s") % tr("pubkey") % tr("key image")).str(); + message_writer() << boost::format("%21s%8s%12s%8s%16s%68s%s") % tr("amount") % tr("spent") % tr("unlocked") % tr("ringct") % tr("global index") % tr("tx id") % verbose_string; transfers_found = true; } + std::string verbose_string; + if (verbose) + verbose_string = (boost::format("%68s%68s") % td.get_public_key() % td.m_key_image).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") % + boost::format("%21s%8s%12s%8s%16u%68s%s") % print_money(td.amount()) % (td.m_spent ? tr("T") : tr("F")) % (m_wallet->is_transfer_unlocked(td) ? tr("unlocked") : tr("locked")) % (td.is_rct() ? tr("RingCT") : tr("-")) % td.m_global_output_index % - td.m_txid; + td.m_txid % + verbose_string; } } From 23d80b15fdb99819ea12ae94b1223d6972de70c8 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 14 Nov 2016 19:05:52 +0000 Subject: [PATCH 05/10] core: remove any tx pubkey from extra before adding one This will happen when signing a transaction from a cold wallet, and we don't want the placeholder the hot wallet put in it. --- src/cryptonote_core/cryptonote_format_utils.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/cryptonote_core/cryptonote_format_utils.cpp b/src/cryptonote_core/cryptonote_format_utils.cpp index 234422e3..d88f66e3 100644 --- a/src/cryptonote_core/cryptonote_format_utils.cpp +++ b/src/cryptonote_core/cryptonote_format_utils.cpp @@ -480,6 +480,7 @@ namespace cryptonote tx.extra = extra; keypair txkey = keypair::generate(); + remove_field_from_tx_extra(tx.extra, typeid(tx_extra_pub_key)); add_tx_pub_key_to_extra(tx, txkey.pub); tx_key = txkey.sec; From 072d646a452648e8ff2735f379cdbba52c9d936d Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 15 Nov 2016 19:06:56 +0000 Subject: [PATCH 06/10] wallet2: fill in key image map when importing key images --- src/wallet/wallet2.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 02346e0e..1afdffbc 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -4717,6 +4717,7 @@ uint64_t wallet2::import_key_images(const std::vector Date: Tue, 15 Nov 2016 19:12:12 +0000 Subject: [PATCH 07/10] wallet2: try all tx keys when scanning a new transaction The vast majority of transactions will have just one tx pubkey, but a bug with cold wallet signing caused two such keys to be there, with the second one being the real one. --- src/cryptonote_core/cryptonote_format_utils.h | 4 ++-- src/wallet/wallet2.cpp | 9 +++++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/cryptonote_core/cryptonote_format_utils.h b/src/cryptonote_core/cryptonote_format_utils.h index d5dd6494..9f9ed862 100644 --- a/src/cryptonote_core/cryptonote_format_utils.h +++ b/src/cryptonote_core/cryptonote_format_utils.h @@ -93,9 +93,9 @@ namespace cryptonote bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::vector& sources, const std::vector& destinations, std::vector extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, bool rct = false); template - bool find_tx_extra_field_by_type(const std::vector& tx_extra_fields, T& field) + bool find_tx_extra_field_by_type(const std::vector& tx_extra_fields, T& field, size_t index = 0) { - auto it = std::find_if(tx_extra_fields.begin(), tx_extra_fields.end(), [](const tx_extra_field& f) { return typeid(T) == f.type(); }); + auto it = std::find_if(tx_extra_fields.begin(), tx_extra_fields.end(), [&index](const tx_extra_field& f) { return typeid(T) == f.type() && !index--; }); if(tx_extra_fields.end() == it) return false; diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 1afdffbc..b4a63932 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -631,11 +631,16 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, const s } // Don't try to extract tx public key if tx has no ouputs - if (!tx.vout.empty()) + size_t pk_index = 0; + while (!tx.vout.empty()) { + // if tx.vout is not empty, we loop through all tx pubkeys + tx_extra_pub_key pub_key_field; - if(!find_tx_extra_field_by_type(tx_extra_fields, pub_key_field)) + if(!find_tx_extra_field_by_type(tx_extra_fields, pub_key_field, pk_index++)) { + if (pk_index > 1) + break; LOG_PRINT_L0("Public key wasn't found in the transaction extra. Skipping transaction " << txid()); if(0 != m_callback) m_callback->on_skip_transaction(height, tx); From 5fe363cd227b4e26bc1b1028b30f6f25e3b615a4 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 15 Nov 2016 20:54:51 +0000 Subject: [PATCH 08/10] wallet: cast indices to string in logs to be nice to CLANG --- src/wallet/wallet2.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index b4a63932..b0175ef4 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -4783,16 +4783,16 @@ size_t wallet2::import_outputs(const std::vector tx_extra_fields; tx_extra_pub_key pub_key_field; - THROW_WALLET_EXCEPTION_IF(td.m_tx.vout.empty(), error::wallet_internal_error, "tx with no outputs at index " + i); + THROW_WALLET_EXCEPTION_IF(td.m_tx.vout.empty(), error::wallet_internal_error, "tx with no outputs at index " + boost::lexical_cast(i)); THROW_WALLET_EXCEPTION_IF(!parse_tx_extra(td.m_tx.extra, tx_extra_fields), error::wallet_internal_error, - "Transaction extra has unsupported format at index " + i); + "Transaction extra has unsupported format at index " + boost::lexical_cast(i)); THROW_WALLET_EXCEPTION_IF(!find_tx_extra_field_by_type(tx_extra_fields, pub_key_field), error::wallet_internal_error, - "Public key wasn't found in the transaction extra at index " + i); + "Public key wasn't found in the transaction extra at index " + boost::lexical_cast(i)); cryptonote::generate_key_image_helper(m_account.get_keys(), pub_key_field.pub_key, td.m_internal_output_index, in_ephemeral, td.m_key_image); td.m_key_image_known = true; THROW_WALLET_EXCEPTION_IF(in_ephemeral.pub != boost::get(td.m_tx.vout[td.m_internal_output_index].target).key, - error::wallet_internal_error, "key_image generated ephemeral public key not matched with output_key at index " + i); + error::wallet_internal_error, "key_image generated ephemeral public key not matched with output_key at index " + boost::lexical_cast(i)); m_transfers.push_back(td); } From f8066116dd9afad2f092593795678d569bc0cceb Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 15 Nov 2016 21:14:49 +0000 Subject: [PATCH 09/10] wallet2: fill key image and pubkey maps when importing outputs --- src/wallet/wallet2.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index b0175ef4..bf7c1537 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -4794,6 +4794,8 @@ size_t wallet2::import_outputs(const std::vector(td.m_tx.vout[td.m_internal_output_index].target).key, error::wallet_internal_error, "key_image generated ephemeral public key not matched with output_key at index " + boost::lexical_cast(i)); + m_key_images[td.m_key_image] = m_transfers.size(); + m_pub_keys[td.get_public_key()] = m_transfers.size(); m_transfers.push_back(td); } From a0131c8be3a3965e26bda82697c340962cdc0efd Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 15 Nov 2016 21:22:04 +0000 Subject: [PATCH 10/10] wallet: auto sync outputs and key images in cold signing files When passing around unsigned and signed transactions, outputs and key images are passed along (outputs are passed along unsigned transactions from the hot wallet to the cold wallet, key images are passed along with signed transations from the cold wallet to the hot wallet), to allow more user friendly syncing between hot and cold wallets. --- src/simplewallet/simplewallet.cpp | 28 +++++++++++++++++++------- src/simplewallet/simplewallet.h | 2 +- src/wallet/wallet2.cpp | 33 ++++++++++++++++++++++++++++++- src/wallet/wallet2.h | 21 +++++++++++++++++++- 4 files changed, 74 insertions(+), 10 deletions(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 45ccaa69..47e6fd45 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 4fe1b041..0d83f429 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 bf7c1537..093e5c2a 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 417e754a..d0075e2e 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);