From bba6af9064cb95596039f5a8f7ea460d3dedef0d Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 26 Sep 2016 23:11:10 +0100 Subject: wallet: cold wallet transaction signing This change adds the ability to create a new unsigned transaction from a watch only wallet, and save it to a file. This file can then be moved to another computer/VM where a cold wallet may load it, sign it, and save it. That cold wallet does not need to have a blockchain nor daemon. The signed transaction file can then be moved back to the watch only wallet, which can load it and send it to the daemon. Two new simplewallet commands to use it: sign_transfer (on the cold wallet) submit_transfer (on the watch only wallet) The transfer command used on a watch only wallet now writes an unsigned transaction set in a file called 'unsigned_monero_tx' instead of submitting the tx to the daemon as a normal wallet does. The signed tx file is called 'signed_monero_tx'. --- src/wallet/wallet2.cpp | 329 +++++++++++++++++++++++++++++++++++++------------ 1 file changed, 251 insertions(+), 78 deletions(-) (limited to 'src/wallet/wallet2.cpp') diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 75b1c36bd..45102d5f2 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -74,6 +74,9 @@ using namespace cryptonote; // arbitrary, used to generate different hashes from the same input #define CHACHA8_KEY_TAIL 0x8c +#define UNSIGNED_TX_PREFIX "Monero unsigned tx set\001" +#define SIGNED_TX_PREFIX "Monero signed tx set\001" + #define KILL_IOSERVICE() \ do { \ work.reset(); \ @@ -173,15 +176,17 @@ bool wallet2::is_deprecated() const return is_old_file_format; } //---------------------------------------------------------------------------------------------------- -void wallet2::set_spent(transfer_details &td, uint64_t height) +void wallet2::set_spent(size_t idx, uint64_t height) { + transfer_details &td = m_transfers[idx]; LOG_PRINT_L2("Setting SPENT at " << height << ": ki " << td.m_key_image << ", amount " << print_money(td.m_amount)); td.m_spent = true; td.m_spent_height = height; } //---------------------------------------------------------------------------------------------------- -void wallet2::set_unspent(transfer_details &td) +void wallet2::set_unspent(size_t idx) { + transfer_details &td = m_transfers[idx]; LOG_PRINT_L2("Setting UNSPENT: ki " << td.m_key_image << ", amount " << print_money(td.m_amount)); td.m_spent = false; td.m_spent_height = 0; @@ -501,7 +506,7 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, const s td.m_mask = rct::identity(); td.m_rct = false; } - set_unspent(td); + set_unspent(m_transfers.size()-1); m_key_images[td.m_key_image] = m_transfers.size()-1; LOG_PRINT_L0("Received money: " << print_money(td.amount()) << ", with tx: " << txid()); if (0 != m_callback) @@ -580,7 +585,7 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, const s amount = td.amount(); LOG_PRINT_L0("Spent money: " << print_money(amount) << ", with tx: " << txid()); tx_money_spent_in_ins += amount; - set_spent(td, height); + set_spent(it->second, height); if (0 != m_callback) m_callback->on_money_spent(height, tx, amount, tx); } @@ -997,12 +1002,13 @@ void wallet2::update_pool_state() if (pit->second.m_tx.vin[vini].type() == typeid(txin_to_key)) { txin_to_key &tx_in_to_key = boost::get(pit->second.m_tx.vin[vini]); - for (auto &td: m_transfers) + for (size_t i = 0; i < m_transfers.size(); ++i) { + const transfer_details &td = m_transfers[i]; if (td.m_key_image == tx_in_to_key.k_image) { LOG_PRINT_L1("Resetting spent status for output " << vini << ": " << td.m_key_image); - set_unspent(td); + set_unspent(i); break; } } @@ -1301,7 +1307,7 @@ void wallet2::detach_blockchain(uint64_t height) if (td.m_spent && td.m_spent_height >= height) { LOG_PRINT_L1("Resetting spent status for output " << i << ": " << td.m_key_image); - set_unspent(td); + set_unspent(i); } } @@ -1590,7 +1596,8 @@ bool wallet2::verify_password(const std::string& password) const const cryptonote::account_keys& keys = account_data_check.get_keys(); r = r && verify_keys(keys.m_view_secret_key, keys.m_account_address.m_view_public_key); - r = r && verify_keys(keys.m_spend_secret_key, keys.m_account_address.m_spend_public_key); + if(!m_watch_only) + r = r && verify_keys(keys.m_spend_secret_key, keys.m_account_address.m_spend_public_key); return r; } @@ -2128,13 +2135,13 @@ void wallet2::rescan_spent() if (td.m_spent) { LOG_PRINT_L0("Marking output " << i << "(" << td.m_key_image << ") as unspent, it was marked as spent"); - set_unspent(td); + set_unspent(i); td.m_spent_height = 0; } else { LOG_PRINT_L0("Marking output " << i << "(" << td.m_key_image << ") as spent, it was marked as unspent"); - set_spent(td, td.m_spent_height); + set_spent(i, td.m_spent_height); // unknown height, if this gets reorged, it might still be missed } } @@ -2260,7 +2267,7 @@ float wallet2::get_output_relatedness(const transfer_details &td0, const transfe return 0.0f; } //---------------------------------------------------------------------------------------------------- -size_t wallet2::pop_best_value_from(const transfer_container &transfers, std::vector &unused_indices, const std::list& selected_transfers) const +size_t wallet2::pop_best_value_from(const transfer_container &transfers, std::vector &unused_indices, const std::list& selected_transfers) const { std::vector candidates; float best_relatedness = 1.0f; @@ -2268,9 +2275,9 @@ size_t wallet2::pop_best_value_from(const transfer_container &transfers, std::ve { const transfer_details &candidate = transfers[unused_indices[n]]; float relatedness = 0.0f; - for (const auto &i: selected_transfers) + for (size_t i = 0; i < selected_transfers.size(); ++i) { - float r = get_output_relatedness(candidate, *i); + float r = get_output_relatedness(candidate, transfers[i]); if (r > relatedness) { relatedness = r; @@ -2292,7 +2299,7 @@ size_t wallet2::pop_best_value_from(const transfer_container &transfers, std::ve return pop_index (unused_indices, candidates[idx]); } //---------------------------------------------------------------------------------------------------- -size_t wallet2::pop_best_value(std::vector &unused_indices, const std::list& selected_transfers) const +size_t wallet2::pop_best_value(std::vector &unused_indices, const std::list& selected_transfers) const { return pop_best_value_from(m_transfers, unused_indices, selected_transfers); } @@ -2301,7 +2308,7 @@ size_t wallet2::pop_best_value(std::vector &unused_indices, const std::l // returns: // direct return: amount of money found // modified reference: selected_transfers, a list of iterators/indices of input sources -uint64_t wallet2::select_transfers(uint64_t needed_money, std::vector unused_transfers_indices, std::list& selected_transfers, bool trusted_daemon) +uint64_t wallet2::select_transfers(uint64_t needed_money, std::vector unused_transfers_indices, std::list& selected_transfers, bool trusted_daemon) { uint64_t found_money = 0; while (found_money < needed_money && !unused_transfers_indices.empty()) @@ -2309,7 +2316,7 @@ uint64_t wallet2::select_transfers(uint64_t needed_money, std::vector un size_t idx = pop_best_value(unused_transfers_indices, selected_transfers); transfer_container::iterator it = m_transfers.begin() + idx; - selected_transfers.push_back(it); + selected_transfers.push_back(idx); found_money += it->amount(); } @@ -2513,8 +2520,8 @@ void wallet2::commit_tx(pending_tx& ptx) { payment_id = get_payment_id(ptx); dests = ptx.dests; - BOOST_FOREACH(transfer_container::iterator it, ptx.selected_transfers) - amount_in += it->amount(); + BOOST_FOREACH(size_t idx, ptx.selected_transfers) + amount_in += m_transfers[idx].amount(); } add_unconfirmed_tx(ptx.tx, amount_in, dests, payment_id, ptx.change_dts.amount); if (store_tx_info()) @@ -2524,9 +2531,9 @@ void wallet2::commit_tx(pending_tx& ptx) LOG_PRINT_L2("transaction " << txid << " generated ok and sent to daemon, key_images: [" << ptx.key_images << "]"); - BOOST_FOREACH(transfer_container::iterator it, ptx.selected_transfers) + BOOST_FOREACH(size_t idx, ptx.selected_transfers) { - set_spent(*it, 0); + set_spent(idx, 0); } //fee includes dust if dust policy specified it. @@ -2544,7 +2551,153 @@ void wallet2::commit_tx(std::vector& ptx_vector) commit_tx(ptx); } } +//---------------------------------------------------------------------------------------------------- +bool wallet2::save_tx(const std::vector& ptx_vector, const std::string &filename) +{ + LOG_PRINT_L0("saving " << ptx_vector.size() << " transactions"); + unsigned_tx_set txs; + for (auto &tx: ptx_vector) + txs.txes.push_back(tx.construction_data); + std::string s = obj_to_json_str(txs); + if (s.empty()) + return false; + LOG_PRINT_L2("Saving unsigned tx data: " << s); + // save as binary as there's no implementation of loading a json_archive + if (!::serialization::dump_binary(txs, s)) + return false; + 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) +{ + std::string s; + boost::system::error_code errcode; + + if (!boost::filesystem::exists(unsigned_filename, errcode)) + { + LOG_PRINT_L0("File " << unsigned_filename << " does not exist: " << errcode); + return false; + } + if (!epee::file_io_utils::load_file_to_string(unsigned_filename.c_str(), s)) + { + LOG_PRINT_L0("Failed to load from " << unsigned_filename); + return false; + } + const size_t magiclen = strlen(UNSIGNED_TX_PREFIX); + if (strncmp(s.c_str(), UNSIGNED_TX_PREFIX, magiclen)) + { + LOG_PRINT_L0("Bad magic from " << unsigned_filename); + return false; + } + unsigned_tx_set exported_txs; + if (!::serialization::parse_binary(std::string(s.c_str() + magiclen, s.size() - magiclen), exported_txs)) + { + LOG_PRINT_L0("Failed to parse data from " << unsigned_filename); + return false; + } + LOG_PRINT_L1("Loaded tx unsigned data from binary: " << exported_txs.txes.size() << " transactions"); + + if (accept_func && !accept_func(exported_txs)) + { + LOG_PRINT_L1("Transactions rejected by callback"); + return false; + } + + // sign the transactions + signed_tx_set signed_txes; + for (size_t n = 0; n < exported_txs.txes.size(); ++n) + { + const tools::wallet2::tx_construction_data &sd = exported_txs.txes[n]; + LOG_PRINT_L1(" " << (n+1) << ": " << sd.sources.size() << " inputs, mixin " << (sd.sources[0].outputs.size()-1)); + signed_txes.ptx.push_back(pending_tx()); + tools::wallet2::pending_tx &ptx = signed_txes.ptx.back(); + crypto::secret_key tx_key; + std::vector dests = sd.destinations; + if (sd.change_dts.amount > 0) + dests.push_back(sd.change_dts); + bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), sd.sources, dests, sd.extra, ptx.tx, sd.unlock_time, tx_key, sd.use_rct); + THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sd.sources, sd.destinations, sd.unlock_time, m_testnet); + // we don't test tx size, because we don't know the current limit, due to not having a blockchain, + // and it's a bit pointless to fail there anyway, since it'd be a (good) guess only. We sign anyway, + // and if we really go over limit, the daemon will reject when it gets submitted. Chances are it's + // OK anyway since it was generated in the first place, and rerolling should be within a few bytes. + + // normally, the tx keys are saved in commit_tx, when the tx is actually sent to the daemon. + // we can't do that here since the tx will be sent from the compromised wallet, which we don't want + // to see that info, so we save it here + if (store_tx_info()) + { + const crypto::hash txid = get_transaction_hash(ptx.tx); + m_tx_keys.insert(std::make_pair(txid, tx_key)); + } + + std::string key_images; + bool all_are_txin_to_key = std::all_of(ptx.tx.vin.begin(), ptx.tx.vin.end(), [&](const txin_v& s_e) -> bool + { + CHECKED_GET_SPECIFIC_VARIANT(s_e, const txin_to_key, in, false); + key_images += boost::to_string(in.k_image) + " "; + return true; + }); + THROW_WALLET_EXCEPTION_IF(!all_are_txin_to_key, error::unexpected_txin_type, ptx.tx); + + ptx.key_images = key_images; + ptx.fee = 0; + for (const auto &i: sd.sources) ptx.fee += i.amount; + for (const auto &i: dests) ptx.fee -= i.amount; + ptx.dust = 0; + ptx.dust_added_to_fee = false; + ptx.change_dts = sd.change_dts; +// ptx.selected_transfers = selected_transfers; + ptx.tx_key = rct::rct2sk(rct::identity()); // don't send it back to the untrusted view wallet + ptx.dests = sd.destinations; + ptx.construction_data = sd; + } + + s = obj_to_json_str(signed_txes); + if (s.empty()) + return false; + LOG_PRINT_L2("Saving signed tx data: " << s); + // save as binary as there's no implementation of loading a json_archive + if (!::serialization::dump_binary(signed_txes, s)) + return false; + return epee::file_io_utils::save_string_to_file(signed_filename, std::string(SIGNED_TX_PREFIX) + s); +} +//---------------------------------------------------------------------------------------------------- +bool wallet2::load_tx(const std::string &signed_filename, std::vector &ptx) +{ + std::string s; + boost::system::error_code errcode; + signed_tx_set signed_txs; + + if (!boost::filesystem::exists(signed_filename, errcode)) + { + LOG_PRINT_L0("File " << signed_filename << " does not exist: " << errcode); + return false; + } + + if (!epee::file_io_utils::load_file_to_string(signed_filename.c_str(), s)) + { + LOG_PRINT_L0("Failed to load from " << signed_filename); + return false; + } + const size_t magiclen = strlen(SIGNED_TX_PREFIX); + if (strncmp(s.c_str(), SIGNED_TX_PREFIX, magiclen)) + { + LOG_PRINT_L0("Bad magic from " << signed_filename); + return false; + } + if (!::serialization::parse_binary(std::string(s.c_str() + magiclen, s.size() - magiclen), signed_txs)) + { + LOG_PRINT_L0("Failed to parse data from " << signed_filename); + return false; + } + LOG_PRINT_L1("Loaded signed tx data from binary: " << signed_txs.ptx.size() << " transactions"); + ptx = signed_txs.ptx; + + return true; +} +//---------------------------------------------------------------------------------------------------- uint64_t wallet2::get_fee_multiplier(uint32_t priority, bool use_new_fee) const { static const uint64_t old_multipliers[3] = {1, 2, 3}; @@ -2563,7 +2716,6 @@ uint64_t wallet2::get_fee_multiplier(uint32_t priority, bool use_new_fee) const THROW_WALLET_EXCEPTION_IF (false, error::invalid_priority); return 1; } - //---------------------------------------------------------------------------------------------------- // separated the call(s) to wallet2::transfer into their own function // @@ -2613,9 +2765,9 @@ std::vector wallet2::create_transactions(std::vector wallet2::create_transactions(std::vector wallet2::create_transactions(std::vector wallet2::create_transactions(std::vector wallet2::create_transactions(std::vector -void wallet2::get_outs(std::vector> &outs, const std::list &selected_transfers, size_t fake_outputs_count) +void wallet2::get_outs(std::vector> &outs, const std::list &selected_transfers, size_t fake_outputs_count) { LOG_PRINT_L2("fake_outputs_count: " << fake_outputs_count); outs.clear(); @@ -2688,8 +2840,8 @@ void wallet2::get_outs(std::vector> &outs, const std::listis_rct() ? 0 : it->amount()); + for(size_t idx: selected_transfers) + req_t.params.amounts.push_back(m_transfers[idx].is_rct() ? 0 : m_transfers[idx].amount()); std::sort(req_t.params.amounts.begin(), req_t.params.amounts.end()); auto end = std::unique(req_t.params.amounts.begin(), req_t.params.amounts.end()); req_t.params.amounts.resize(std::distance(req_t.params.amounts.begin(), end)); @@ -2708,12 +2860,13 @@ void wallet2::get_outs(std::vector> &outs, const std::listis_rct() ? 0 : it->amount(); + const transfer_details &td = m_transfers[idx]; + const uint64_t amount = td.is_rct() ? 0 : td.amount(); std::unordered_set seen_indices; // request more for rct in base recent (locked) coinbases are picked, since they're locked for longer - size_t requested_outputs_count = base_requested_outputs_count + (it->is_rct() ? CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW - CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE : 0); + size_t requested_outputs_count = base_requested_outputs_count + (td.is_rct() ? CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW - CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE : 0); size_t start = req.outputs.size(); // if there are just enough outputs to mix with, use all of them. @@ -2745,8 +2898,8 @@ void wallet2::get_outs(std::vector> &outs, const std::listm_global_output_index); - req.outputs.push_back({amount, it->m_global_output_index}); + seen_indices.emplace(td.m_global_output_index); + req.outputs.push_back({amount, td.m_global_output_index}); // while we still need more mixins while (num_found < requested_outputs_count) @@ -2798,15 +2951,16 @@ void wallet2::get_outs(std::vector> &outs, const std::list scanty_outs; size_t base = 0; outs.reserve(selected_transfers.size()); - for(transfer_container::iterator it: selected_transfers) + for(size_t idx: selected_transfers) { - size_t requested_outputs_count = base_requested_outputs_count + (it->is_rct() ? CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW - CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE : 0); + const transfer_details &td = m_transfers[idx]; + size_t requested_outputs_count = base_requested_outputs_count + (td.is_rct() ? CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW - CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE : 0); outs.push_back(std::vector()); outs.back().reserve(fake_outputs_count + 1); - const rct::key mask = it->is_rct() ? rct::commit(it->amount(), it->m_mask) : rct::zeroCommit(it->amount()); + const rct::key mask = td.is_rct() ? rct::commit(td.amount(), td.m_mask) : rct::zeroCommit(td.amount()); // pick real out first (it will be sorted when done) - outs.back().push_back(std::make_tuple(it->m_global_output_index, boost::get(it->m_tx.vout[it->m_internal_output_index].target).key, mask)); + outs.back().push_back(std::make_tuple(td.m_global_output_index, boost::get(td.m_tx.vout[td.m_internal_output_index].target).key, mask)); // then pick others in random order till we reach the required number // since we use an equiprobable pick here, we don't upset the triangular distribution @@ -2816,12 +2970,12 @@ void wallet2::get_outs(std::vector> &outs, const std::list())); - LOG_PRINT_L2("Looking for " << (fake_outputs_count+1) << " outputs of size " << print_money(it->is_rct() ? 0 : it->amount())); + LOG_PRINT_L2("Looking for " << (fake_outputs_count+1) << " outputs of size " << print_money(td.is_rct() ? 0 : td.amount())); for (size_t o = 0; o < requested_outputs_count && outs.back().size() < fake_outputs_count + 1; ++o) { size_t i = base + order[o]; - LOG_PRINT_L2("Index " << i << "/" << requested_outputs_count << ": idx " << req.outputs[i].index << " (real " << it->m_global_output_index << "), unlocked " << daemon_resp.outs[i].unlocked << ", key " << daemon_resp.outs[i].key); - if (req.outputs[i].index == it->m_global_output_index) // don't re-add real one + LOG_PRINT_L2("Index " << i << "/" << requested_outputs_count << ": idx " << req.outputs[i].index << " (real " << td.m_global_output_index << "), unlocked " << daemon_resp.outs[i].unlocked << ", key " << daemon_resp.outs[i].key); + if (req.outputs[i].index == td.m_global_output_index) // don't re-add real one continue; if (!daemon_resp.outs[i].unlocked) // don't add locked outs continue; @@ -2832,7 +2986,7 @@ void wallet2::get_outs(std::vector> &outs, const std::listis_rct() ? 0 : it->amount()] = outs.back().size(); + scanty_outs[td.is_rct() ? 0 : td.amount()] = outs.back().size(); } else { @@ -2845,18 +2999,19 @@ void wallet2::get_outs(std::vector> &outs, const std::list v; - const rct::key mask = it->is_rct() ? rct::commit(it->amount(), it->m_mask) : rct::zeroCommit(it->amount()); - v.push_back(std::make_tuple(it->m_global_output_index, boost::get(it->m_tx.vout[it->m_internal_output_index].target).key, mask)); + const rct::key mask = td.is_rct() ? rct::commit(td.amount(), td.m_mask) : rct::zeroCommit(td.amount()); + v.push_back(std::make_tuple(td.m_global_output_index, boost::get(td.m_tx.vout[td.m_internal_output_index].target).key, mask)); outs.push_back(v); } } } template -void wallet2::transfer_selected(const std::vector& dsts, const std::list selected_transfers, size_t fake_outputs_count, +void wallet2::transfer_selected(const std::vector& dsts, const std::list selected_transfers, size_t fake_outputs_count, uint64_t unlock_time, uint64_t fee, const std::vector& extra, T destination_split_strategy, const tx_dust_policy& dust_policy, cryptonote::transaction& tx, pending_tx &ptx) { using namespace cryptonote; @@ -2878,9 +3033,9 @@ void wallet2::transfer_selected(const std::vectoramount(); + found_money += m_transfers[idx].amount(); } LOG_PRINT_L2("wanted " << print_money(needed_money) << ", found " << print_money(found_money) << ", fee " << print_money(fee)); @@ -2894,11 +3049,11 @@ void wallet2::transfer_selected(const std::vector sources; - BOOST_FOREACH(transfer_container::iterator it, selected_transfers) + BOOST_FOREACH(size_t idx, selected_transfers) { sources.resize(sources.size()+1); cryptonote::tx_source_entry& src = sources.back(); - transfer_details& td = *it; + const transfer_details& td = m_transfers[idx]; src.amount = td.amount(); src.rct = td.is_rct(); //paste keys (fake and real) @@ -2983,9 +3138,15 @@ void wallet2::transfer_selected(const std::vector dsts, const std::list selected_transfers, size_t fake_outputs_count, +void wallet2::transfer_selected_rct(std::vector dsts, const std::list selected_transfers, size_t fake_outputs_count, uint64_t unlock_time, uint64_t fee, const std::vector& extra, cryptonote::transaction& tx, pending_tx &ptx) { using namespace cryptonote; @@ -3007,9 +3168,9 @@ void wallet2::transfer_selected_rct(std::vectoramount(); + found_money += m_transfers[idx].amount(); } LOG_PRINT_L2("wanted " << print_money(needed_money) << ", found " << print_money(found_money) << ", fee " << print_money(fee)); @@ -3022,11 +3183,11 @@ void wallet2::transfer_selected_rct(std::vector sources; - BOOST_FOREACH(transfer_container::iterator it, selected_transfers) + BOOST_FOREACH(size_t idx, selected_transfers) { sources.resize(sources.size()+1); cryptonote::tx_source_entry& src = sources.back(); - transfer_details& td = *it; + const transfer_details& td = m_transfers[idx]; src.amount = td.amount(); src.rct = td.is_rct(); //paste mixin transaction @@ -3096,6 +3257,12 @@ void wallet2::transfer_selected_rct(std::vector wallet2::create_transactions_2(std::vector selected_transfers; + std::list selected_transfers; std::vector dsts; cryptonote::transaction tx; pending_tx ptx; @@ -3332,7 +3499,7 @@ std::vector wallet2::create_transactions_2(std::vector wallet2::create_transactions_2(std::vector::const_iterator mi = tx.selected_transfers.begin(); mi != tx.selected_transfers.end(); ++mi) - tx_money += (*mi)->amount(); + for (size_t idx: tx.selected_transfers) + tx_money += m_transfers[idx].amount(); LOG_PRINT_L1(" Transaction " << (1+std::distance(txes.begin(), i)) << "/" << txes.size() << ": " << (tx.bytes+1023)/1024 << " kB, sending " << print_money(tx_money) << " in " << tx.selected_transfers.size() << " outputs to " << tx.dsts.size() << " destination(s), including " << @@ -3491,7 +3658,7 @@ std::vector wallet2::create_transactions_all(const cryptono std::vector unused_dust_indices; uint64_t accumulated_fee, accumulated_outputs, accumulated_change; struct TX { - std::list selected_transfers; + std::list selected_transfers; std::vector dsts; cryptonote::transaction tx; pending_tx ptx; @@ -3543,7 +3710,7 @@ std::vector wallet2::create_transactions_all(const cryptono LOG_PRINT_L2("Picking output " << idx << ", amount " << print_money(td.amount())); // add this output to the list to spend - tx.selected_transfers.push_back(m_transfers.begin() + idx); + tx.selected_transfers.push_back(idx); uint64_t available_amount = td.amount(); accumulated_outputs += available_amount; @@ -3620,8 +3787,8 @@ std::vector wallet2::create_transactions_all(const cryptono { TX &tx = *i; uint64_t tx_money = 0; - for (std::list::const_iterator mi = tx.selected_transfers.begin(); mi != tx.selected_transfers.end(); ++mi) - tx_money += (*mi)->amount(); + for (size_t idx: tx.selected_transfers) + tx_money += m_transfers[idx].amount(); LOG_PRINT_L1(" Transaction " << (1+std::distance(txes.begin(), i)) << "/" << txes.size() << ": " << (tx.bytes+1023)/1024 << " kB, sending " << print_money(tx_money) << " in " << tx.selected_transfers.size() << " outputs to " << tx.dsts.size() << " destination(s), including " << @@ -3658,14 +3825,14 @@ void wallet2::transfer_from(const std::vector &outs, size_t num_outputs, // select all dust inputs for transaction // throw if there are none uint64_t money = 0; - std::list selected_transfers; + std::list selected_transfers; #if 1 for (size_t n = 0; n < outs.size(); ++n) { const transfer_details& td = m_transfers[outs[n]]; if (!td.m_spent) { - selected_transfers.push_back (m_transfers.begin() + outs[n]); + selected_transfers.push_back (outs[n]); money += td.amount(); if (selected_transfers.size() >= num_outputs) break; @@ -3693,11 +3860,11 @@ void wallet2::transfer_from(const std::vector &outs, size_t num_outputs, //prepare inputs size_t i = 0; std::vector sources; - BOOST_FOREACH(transfer_container::iterator it, selected_transfers) + BOOST_FOREACH(size_t idx, selected_transfers) { sources.resize(sources.size()+1); cryptonote::tx_source_entry& src = sources.back(); - transfer_details& td = *it; + const transfer_details& td = m_transfers[idx]; src.amount = td.amount(); src.rct = td.is_rct(); @@ -3754,6 +3921,12 @@ void wallet2::transfer_from(const std::vector &outs, size_t num_outputs, ptx.selected_transfers = selected_transfers; ptx.tx_key = tx_key; ptx.dests = dsts; + ptx.construction_data.sources = sources; + ptx.construction_data.destinations = dsts; + ptx.construction_data.change_dts = change_dts; + ptx.construction_data.extra = tx.extra; + ptx.construction_data.unlock_time = unlock_time; + ptx.construction_data.use_rct = false; } //---------------------------------------------------------------------------------------------------- @@ -3969,9 +4142,9 @@ std::vector wallet2::create_unmixable_sweep_transactions(bo ptx_vector.push_back(ptx); // mark transfers to be used as "spent" - BOOST_FOREACH(transfer_container::iterator it, ptx.selected_transfers) + BOOST_FOREACH(size_t idx, ptx.selected_transfers) { - set_spent(*it, 0); + set_spent(idx, 0); } } @@ -3981,9 +4154,9 @@ std::vector wallet2::create_unmixable_sweep_transactions(bo for (auto & ptx : ptx_vector) { // mark transfers to be used as not spent - BOOST_FOREACH(transfer_container::iterator it2, ptx.selected_transfers) + BOOST_FOREACH(size_t idx2, ptx.selected_transfers) { - set_unspent(*it2); + set_unspent(idx2); } } @@ -3999,9 +4172,9 @@ std::vector wallet2::create_unmixable_sweep_transactions(bo for (auto & ptx : ptx_vector) { // mark transfers to be used as not spent - BOOST_FOREACH(transfer_container::iterator it2, ptx.selected_transfers) + BOOST_FOREACH(size_t idx2, ptx.selected_transfers) { - set_unspent(*it2); + set_unspent(idx2); } } @@ -4018,9 +4191,9 @@ std::vector wallet2::create_unmixable_sweep_transactions(bo for (auto & ptx : ptx_vector) { // mark transfers to be used as not spent - BOOST_FOREACH(transfer_container::iterator it2, ptx.selected_transfers) + BOOST_FOREACH(size_t idx2, ptx.selected_transfers) { - set_unspent(*it2); + set_unspent(idx2); } } -- cgit v1.2.3