From b951bc8780f9a11ccf8dbc1ee89c2aa550bad101 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 21 Aug 2016 09:56:32 +0100 Subject: [PATCH] wallet: transfer_selected_rct now also selects fake outs --- src/cryptonote_core/blockchain.cpp | 4 +- src/rpc/core_rpc_server_commands_defs.h | 4 +- src/wallet/wallet2.cpp | 495 ++++++++++-------------- src/wallet/wallet2.h | 2 + 4 files changed, 213 insertions(+), 292 deletions(-) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 8ff286145..097998ec8 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -1753,11 +1753,11 @@ bool Blockchain::get_outs(const COMMAND_RPC_GET_OUTPUTS::request& req, COMMAND_R for (const auto &i: req.outputs) { // get tx_hash, tx_out_index from DB - crypto::public_key key = m_db->get_output_key(i.amount, i.index).pubkey; + const output_data_t &od = m_db->get_output_key(i.amount, i.index); tx_out_index toi = m_db->get_output_tx_and_index(i.amount, i.index); bool unlocked = is_tx_spendtime_unlocked(m_db->get_tx_unlock_time(toi.first)); - res.outs.push_back({key, unlocked}); + res.outs.push_back({od.pubkey, od.commitment, unlocked}); } return true; } diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index 5f0608532..8cba53943 100644 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -41,7 +41,7 @@ namespace cryptonote #define CORE_RPC_STATUS_BUSY "BUSY" #define CORE_RPC_STATUS_NOT_MINING "NOT MINING" -#define CORE_RPC_VERSION 2 +#define CORE_RPC_VERSION 3 struct COMMAND_RPC_GET_HEIGHT { @@ -316,10 +316,12 @@ namespace cryptonote struct outkey { crypto::public_key key; + rct::key mask; bool unlocked; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE_VAL_POD_AS_BLOB(key) + KV_SERIALIZE_VAL_POD_AS_BLOB(mask) KV_SERIALIZE(unlocked) END_KV_SERIALIZE_MAP() }; diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 985ebe778..df8e505f7 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -29,6 +29,7 @@ // Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers #include +#include #include #include @@ -2655,6 +2656,185 @@ std::vector wallet2::create_transactions(std::vector +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(); + if (fake_outputs_count > 0) + { + // get histogram for the amounts we need + epee::json_rpc::request req_t = AUTO_VAL_INIT(req_t); + epee::json_rpc::response resp_t = AUTO_VAL_INIT(resp_t); + m_daemon_rpc_mutex.lock(); + req_t.jsonrpc = "2.0"; + req_t.id = epee::serialization::storage_entry(0); + req_t.method = "get_output_histogram"; + for(auto it: selected_transfers) + req_t.params.amounts.push_back(it->is_rct() ? 0 : it->amount()); + req_t.params.unlocked = true; + bool r = net_utils::invoke_http_json_remote_command2(m_daemon_address + "/json_rpc", req_t, resp_t, m_http_client); + m_daemon_rpc_mutex.unlock(); + THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "transfer_selected"); + THROW_WALLET_EXCEPTION_IF(resp_t.result.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_output_histogram"); + THROW_WALLET_EXCEPTION_IF(resp_t.result.status != CORE_RPC_STATUS_OK, error::get_histogram_error, resp_t.result.status); + + // we ask for more, to have spares if some outputs are still locked + size_t requested_outputs_count = (size_t)((fake_outputs_count + 1) * 1.5 + 1); + LOG_PRINT_L2("requested_outputs_count: " << requested_outputs_count); + + // generate output indices to request + COMMAND_RPC_GET_OUTPUTS::request req = AUTO_VAL_INIT(req); + COMMAND_RPC_GET_OUTPUTS::response daemon_resp = AUTO_VAL_INIT(daemon_resp); + + for(transfer_container::iterator it: selected_transfers) + { + const uint64_t amount = it->is_rct() ? 0 : it->amount(); + std::unordered_set seen_indices; + size_t start = req.outputs.size(); + + // if there are just enough outputs to mix with, use all of them. + // Eventually this should become impossible. + uint64_t num_outs = 0; + for (auto he: resp_t.result.histogram) + { + if (he.amount == amount) + { + num_outs = he.instances; + break; + } + } + LOG_PRINT_L1("" << num_outs << " outputs of size " << print_money(amount)); + + if (num_outs <= requested_outputs_count) + { + for (uint64_t i = 0; i < num_outs; i++) + req.outputs.push_back({amount, i}); + // duplicate to make up shortfall: this will be caught after the RPC call, + // so we can also output the amounts for which we can't reach the required + // mixin after checking the actual unlockedness + for (uint64_t i = num_outs; i < requested_outputs_count; ++i) + req.outputs.push_back({amount, num_outs - 1}); + } + else + { + // start with real one + uint64_t num_found = 1; + seen_indices.emplace(it->m_global_output_index); + req.outputs.push_back({amount, it->m_global_output_index}); + + // while we still need more mixins + while (num_found < requested_outputs_count) + { + // if we've gone through every possible output, we've gotten all we can + if (seen_indices.size() == num_outs) + break; + + // get a random output index from the DB. If we've already seen it, + // return to the top of the loop and try again, otherwise add it to the + // list of output indices we've seen. + + // triangular distribution over [a,b) with a=0, mode c=b=up_index_limit + uint64_t r = crypto::rand() % ((uint64_t)1 << 53); + double frac = std::sqrt((double)r / ((uint64_t)1 << 53)); + uint64_t i = (uint64_t)(frac*num_outs); + // just in case rounding up to 1 occurs after sqrt + if (i == num_outs) + --i; + + if (seen_indices.count(i)) + continue; + seen_indices.emplace(i); + + req.outputs.push_back({amount, i}); + ++num_found; + } + } + + // sort the subsection, to ensure the daemon doesn't know wich output is ours + std::sort(req.outputs.begin() + start, req.outputs.end(), + [](const COMMAND_RPC_GET_OUTPUTS::out &a, const COMMAND_RPC_GET_OUTPUTS::out &b) { return a.index < b.index; }); + } + + for (auto i: req.outputs) + LOG_PRINT_L1("asking for output " << i.index << " for " << print_money(i.amount)); + + // get the keys for those + m_daemon_rpc_mutex.lock(); + r = epee::net_utils::invoke_http_bin_remote_command2(m_daemon_address + "/get_outs.bin", req, daemon_resp, m_http_client, 200000); + m_daemon_rpc_mutex.unlock(); + THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_outs.bin"); + THROW_WALLET_EXCEPTION_IF(daemon_resp.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_outs.bin"); + THROW_WALLET_EXCEPTION_IF(daemon_resp.status != CORE_RPC_STATUS_OK, error::get_random_outs_error, daemon_resp.status); + THROW_WALLET_EXCEPTION_IF(daemon_resp.outs.size() != req.outputs.size(), error::wallet_internal_error, + "daemon returned wrong response for get_outs.bin, wrong amounts count = " + + std::to_string(daemon_resp.outs.size()) + ", expected " + std::to_string(req.outputs.size())); + + std::unordered_map scanty_outs; + size_t base = 0; + outs.reserve(selected_transfers.size()); + for(transfer_container::iterator it: selected_transfers) + { + 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()); + + // 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)); + + // 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 + std::vector order; + order.resize(requested_outputs_count); + for (size_t n = 0; n < order.size(); ++n) + order[n] = n; + std::shuffle(order.begin(), order.end(), std::default_random_engine(crypto::rand())); + + for (size_t o = 0; o < requested_outputs_count && outs.back().size() < fake_outputs_count + 1; ++o) + { + size_t i = base + order[o]; + if (req.outputs[i].index == it->m_global_output_index) // don't re-add real one + continue; + if (!daemon_resp.outs[i].unlocked) // don't add locked outs + continue; + if (o > 0 && daemon_resp.outs[i].key == daemon_resp.outs[i - 1].key) // don't add duplicates + continue; + outs.back().push_back(std::make_tuple(req.outputs[i].index, daemon_resp.outs[i].key, daemon_resp.outs[i].mask)); + } + if (outs.back().size() < fake_outputs_count + 1) + { + scanty_outs[it->amount()] = outs.back().size(); + } + else + { + // sort the subsection, so any spares are reset in order + std::sort(outs.back().begin(), outs.back().end(), [](const entry &a, const entry &b) { return std::get<0>(a) < std::get<0>(b); }); + + // sanity check + for (size_t n = 1; n < outs.back().size(); ++n) + { + THROW_WALLET_EXCEPTION_IF(std::get<0>(outs.back()[n]) == std::get<0>(outs.back()[n-1]), error::wallet_internal_error, + "Duplicate indices though we did not ask for any"); + THROW_WALLET_EXCEPTION_IF(std::get<1>(outs.back()[n]) == std::get<1>(outs.back()[n-1]), error::wallet_internal_error, + "Duplicate keys after we have weeded them out"); + } + } + base += requested_outputs_count; + } + THROW_WALLET_EXCEPTION_IF(!scanty_outs.empty(), error::not_enough_outs_to_mix, scanty_outs, fake_outputs_count); + } + else + { + for (transfer_container::iterator it: selected_transfers) + { + std::vector 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)); + outs.push_back(v); + } + } +} + template 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) @@ -2686,170 +2866,9 @@ void wallet2::transfer_selected(const std::vector entry; + typedef std::tuple entry; std::vector> outs; - if (fake_outputs_count) - { - // get histogram for the amounts we need - epee::json_rpc::request req_t = AUTO_VAL_INIT(req_t); - epee::json_rpc::response resp_t = AUTO_VAL_INIT(resp_t); - m_daemon_rpc_mutex.lock(); - req_t.jsonrpc = "2.0"; - req_t.id = epee::serialization::storage_entry(0); - req_t.method = "get_output_histogram"; - for(auto it: selected_transfers) - req_t.params.amounts.push_back(it->amount()); - req_t.params.unlocked = true; - bool r = net_utils::invoke_http_json_remote_command2(m_daemon_address + "/json_rpc", req_t, resp_t, m_http_client); - m_daemon_rpc_mutex.unlock(); - THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "transfer_selected"); - THROW_WALLET_EXCEPTION_IF(resp_t.result.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_output_histogram"); - THROW_WALLET_EXCEPTION_IF(resp_t.result.status != CORE_RPC_STATUS_OK, error::get_histogram_error, resp_t.result.status); - - // we ask for more, to have spares if some outputs are still locked - size_t requested_outputs_count = (size_t)((fake_outputs_count + 1) * 1.5 + 1); - - // generate output indices to request - COMMAND_RPC_GET_OUTPUTS::request req = AUTO_VAL_INIT(req); - COMMAND_RPC_GET_OUTPUTS::response daemon_resp = AUTO_VAL_INIT(daemon_resp); - for(transfer_container::iterator it: selected_transfers) - { - std::unordered_set seen_indices; - size_t start = req.outputs.size(); - - // if there are just enough outputs to mix with, use all of them. - // Eventually this should become impossible. - uint64_t num_outs = 0; - for (auto he: resp_t.result.histogram) - { - if (he.amount == it->amount()) - { - num_outs = he.instances; - break; - } - } - if (num_outs <= requested_outputs_count) - { - for (uint64_t i = 0; i < num_outs; i++) - req.outputs.push_back({it->amount(), i}); - // duplicate to make up shortfall: this will be caught after the RPC call, - // so we can also output the amounts for which we can't reach the required - // mixin after checking the actual unlockedness - for (uint64_t i = num_outs; i < requested_outputs_count; ++i) - req.outputs.push_back({it->amount(), num_outs - 1}); - } - else - { - // start with real one - uint64_t num_found = 1; - seen_indices.emplace(it->m_global_output_index); - req.outputs.push_back({it->amount(), it->m_global_output_index}); - - // while we still need more mixins - while (num_found < requested_outputs_count) - { - // if we've gone through every possible output, we've gotten all we can - if (seen_indices.size() == num_outs) - break; - - // get a random output index from the DB. If we've already seen it, - // return to the top of the loop and try again, otherwise add it to the - // list of output indices we've seen. - - // triangular distribution over [a,b) with a=0, mode c=b=up_index_limit - uint64_t r = crypto::rand() % ((uint64_t)1 << 53); - double frac = std::sqrt((double)r / ((uint64_t)1 << 53)); - uint64_t i = (uint64_t)(frac*num_outs); - // just in case rounding up to 1 occurs after sqrt - if (i == num_outs) - --i; - - if (seen_indices.count(i)) - continue; - seen_indices.emplace(i); - - req.outputs.push_back({it->amount(), i}); - ++num_found; - } - } - - // sort the subsection, to ensure the daemon doesn't know wich output is ours - std::sort(req.outputs.begin() + start, req.outputs.end(), - [](const COMMAND_RPC_GET_OUTPUTS::out &a, const COMMAND_RPC_GET_OUTPUTS::out &b) { return a.index < b.index; }); - } - - // get the keys for those - m_daemon_rpc_mutex.lock(); - r = epee::net_utils::invoke_http_bin_remote_command2(m_daemon_address + "/get_outs.bin", req, daemon_resp, m_http_client, 200000); - m_daemon_rpc_mutex.unlock(); - THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_outs.bin"); - THROW_WALLET_EXCEPTION_IF(daemon_resp.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_outs.bin"); - THROW_WALLET_EXCEPTION_IF(daemon_resp.status != CORE_RPC_STATUS_OK, error::get_random_outs_error, daemon_resp.status); - THROW_WALLET_EXCEPTION_IF(daemon_resp.outs.size() != req.outputs.size(), error::wallet_internal_error, - "daemon returned wrong response for get_outs.bin, wrong amounts count = " + - std::to_string(daemon_resp.outs.size()) + ", expected " + std::to_string(req.outputs.size())); - - std::unordered_map scanty_outs; - size_t base = 0; - outs.reserve(selected_transfers.size()); - for(transfer_container::iterator it: selected_transfers) - { - outs.push_back(std::vector()); - outs.back().reserve(fake_outputs_count + 1); - - // pick real out first (it will be sorted when done) - outs.back().push_back({it->m_global_output_index, boost::get(it->m_tx.vout[it->m_internal_output_index].target).key}); - - // 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 - std::vector order; - order.resize(requested_outputs_count); - for (size_t n = 0; n < order.size(); ++n) - order[n] = n; - std::shuffle(order.begin(), order.end(), std::default_random_engine(crypto::rand())); - - for (size_t o = 0; o < requested_outputs_count && outs.back().size() < fake_outputs_count + 1; ++o) - { - size_t i = base + order[o]; - if (req.outputs[i].index == it->m_global_output_index) // don't re-add real one - continue; - if (!daemon_resp.outs[i].unlocked) // don't add locked outs - continue; - if (o > 0 && daemon_resp.outs[i].key == daemon_resp.outs[i - 1].key) // don't add duplicates - continue; - outs.back().push_back({req.outputs[i].index, daemon_resp.outs[i].key}); - } - if (outs.back().size() < fake_outputs_count + 1) - { - scanty_outs[it->amount()] = outs.back().size(); - } - else - { - // sort the subsection, so any spares are reset in order - std::sort(outs.back().begin(), outs.back().end(), [](const entry &a, const entry &b) { return a.first < b.first; }); - - // sanity check - for (size_t n = 1; n < outs.back().size(); ++n) - { - THROW_WALLET_EXCEPTION_IF(outs.back()[n].first == outs.back()[n-1].first, error::wallet_internal_error, - "Duplicate indices though we did not ask for any"); - THROW_WALLET_EXCEPTION_IF(outs.back()[n].second == outs.back()[n-1].second, error::wallet_internal_error, - "Duplicate keys after we have weeded them out"); - } - } - base += requested_outputs_count; - } - THROW_WALLET_EXCEPTION_IF(!scanty_outs.empty(), error::not_enough_outs_to_mix, scanty_outs, fake_outputs_count); - } - else - { - for (transfer_container::iterator it: selected_transfers) - { - std::vector v; - v.push_back({it->m_global_output_index, boost::get(it->m_tx.vout[it->m_internal_output_index].target).key}); - outs.push_back(v); - } - } + get_outs(outs, selected_transfers, fake_outputs_count); // may throw //prepare inputs typedef cryptonote::tx_source_entry::output_entry tx_output_entry; @@ -2867,9 +2886,9 @@ void wallet2::transfer_selected(const std::vector(outs[out_index][n]); + oe.second.dest = rct::pk2rct(std::get<1>(outs[out_index][n])); + oe.second.mask = std::get<2>(outs[out_index][n]); src.outputs.push_back(oe); ++i; @@ -2976,76 +2995,13 @@ void wallet2::transfer_selected_rct(std::vector selected_transfer_to_daemon_resp; - COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::request rct_req = AUTO_VAL_INIT(rct_req); - COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::response rct_daemon_resp = AUTO_VAL_INIT(rct_daemon_resp); - COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request daemon_req = AUTO_VAL_INIT(daemon_req); - COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response daemon_resp = AUTO_VAL_INIT(daemon_resp); - if(fake_outputs_count) - { - rct_req.outs_count = 0; - size_t n_rct_inputs = 0; - BOOST_FOREACH(transfer_container::iterator it, selected_transfers) - { - THROW_WALLET_EXCEPTION_IF(it->m_tx.vout.size() <= it->m_internal_output_index, error::wallet_internal_error, - "m_internal_output_index = " + std::to_string(it->m_internal_output_index) + - " is greater or equal to outputs count = " + std::to_string(it->m_tx.vout.size())); - if (it->is_rct()) - ++n_rct_inputs; - else - daemon_req.amounts.push_back(it->amount()); - selected_transfer_to_daemon_resp.push_back(daemon_req.amounts.size() - 1); - } - size_t n_amounts_requested = daemon_req.amounts.size(); - - rct_req.outs_count = n_rct_inputs * (fake_outputs_count + 1); - if (rct_req.outs_count > 0) - { - LOG_PRINT_L2("We need RCT fake outs for " << n_rct_inputs << " inputs"); - rct_req.outs_count = n_rct_inputs * (fake_outputs_count + 1); // add one to make possible (if need) to skip real output key - m_daemon_rpc_mutex.lock(); - bool r = epee::net_utils::invoke_http_bin_remote_command2(m_daemon_address + "/getrandom_rctouts.bin", rct_req, rct_daemon_resp, m_http_client, 200000); - m_daemon_rpc_mutex.unlock(); - THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "getrandom_rctouts.bin"); - THROW_WALLET_EXCEPTION_IF(rct_daemon_resp.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "getrandom_rctouts.bin"); - THROW_WALLET_EXCEPTION_IF(rct_daemon_resp.status != CORE_RPC_STATUS_OK, error::get_random_outs_error, rct_daemon_resp.status); - THROW_WALLET_EXCEPTION_IF(rct_daemon_resp.outs.size() != n_rct_inputs * (fake_outputs_count + 1), error::wallet_internal_error, - "daemon returned wrong response for getrandom_rctouts.bin, wrong amounts count = " + - std::to_string(daemon_resp.outs.size()) + ", expected " + std::to_string(n_rct_inputs * (fake_outputs_count + 1))); - } - if (!daemon_req.amounts.empty()) - { - LOG_PRINT_L2("We need non-RCT fake outs for " << daemon_req.amounts.size() << " inputs"); - daemon_req.outs_count = fake_outputs_count + 1; // add one to make possible (if need) to skip real output key - m_daemon_rpc_mutex.lock(); - bool r = epee::net_utils::invoke_http_bin_remote_command2(m_daemon_address + "/getrandom_outs.bin", daemon_req, daemon_resp, m_http_client, 200000); - m_daemon_rpc_mutex.unlock(); - THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "getrandom_outs.bin"); - THROW_WALLET_EXCEPTION_IF(daemon_resp.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "getrandom_outs.bin"); - THROW_WALLET_EXCEPTION_IF(daemon_resp.status != CORE_RPC_STATUS_OK, error::get_random_outs_error, daemon_resp.status); - THROW_WALLET_EXCEPTION_IF(daemon_resp.outs.size() != n_amounts_requested, error::wallet_internal_error, - "daemon returned wrong response for getrandom_outs.bin, wrong amounts count = " + - std::to_string(daemon_resp.outs.size()) + ", expected " + std::to_string(n_amounts_requested)); - - std::unordered_map scanty_outs; - BOOST_FOREACH(COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& amount_outs, daemon_resp.outs) - { - if (amount_outs.outs.size() < fake_outputs_count) - { - scanty_outs[amount_outs.amount] = amount_outs.outs.size(); - } - } - THROW_WALLET_EXCEPTION_IF(!scanty_outs.empty(), error::not_enough_outs_to_mix, scanty_outs, fake_outputs_count); - } - } + typedef std::tuple entry; + std::vector> outs; + get_outs(outs, selected_transfers, fake_outputs_count); // may throw //prepare inputs - size_t i = 0; + size_t i = 0, out_index = 0; std::vector sources; - rct_daemon_resp.outs.sort([](const COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::out_entry& a, const COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::out_entry& b){return a.global_amount_index < b.global_amount_index;}); - std::list::const_iterator rctit = rct_daemon_resp.outs.begin(); BOOST_FOREACH(transfer_container::iterator it, selected_transfers) { sources.resize(sources.size()+1); @@ -3054,76 +3010,37 @@ void wallet2::transfer_selected_rct(std::vectoris_rct()) - { - while (src.outputs.size() < fake_outputs_count) - { - THROW_WALLET_EXCEPTION_IF(rctit == rct_daemon_resp.outs.end(), error::wallet_internal_error, "Out of rct inputs"); - // check if we have the daemon supplied output in our real ones - bool found = false; - BOOST_FOREACH(transfer_container::iterator checkit, selected_transfers) - { - if (checkit->m_global_output_index == rctit->global_amount_index && checkit->m_tx.vout[checkit->m_internal_output_index].amount == rctit->amount) - { - found = true; - break; - } - } - if(found) - { - ++rctit; - continue; - } - tx_output_entry oe; - oe.first = rctit->global_amount_index; - oe.second.dest = rct::pk2rct(rctit->out_key); - oe.second.mask = rctit->commitment; - ++rctit; - src.outputs.push_back(oe); - } - } - else - { - typedef COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry out_entry; - typedef cryptonote::tx_source_entry::output_entry tx_output_entry; - size_t idx = selected_transfer_to_daemon_resp[i]; - THROW_WALLET_EXCEPTION_IF(daemon_req.amounts.size() != daemon_resp.outs.size(), error::wallet_internal_error, "Bad amounts/out size"); - THROW_WALLET_EXCEPTION_IF(idx >= daemon_resp.outs.size(), error::wallet_internal_error, "Bad mapping to daemon_resp.outs"); - THROW_WALLET_EXCEPTION_IF(td.amount() != daemon_req.amounts[idx], error::wallet_internal_error, "Bad mapping to daemon_resp.outs/amounts"); - daemon_resp.outs[idx].outs.sort([](const out_entry& a, const out_entry& b){return a.global_amount_index < b.global_amount_index;}); - BOOST_FOREACH(out_entry& daemon_oe, daemon_resp.outs[idx].outs) - { - if(td.m_global_output_index == daemon_oe.global_amount_index) - continue; - tx_output_entry oe; - oe.first = daemon_oe.global_amount_index; - oe.second.dest = rct::pk2rct(daemon_oe.out_key); - oe.second.mask = rct::zeroCommit(td.amount()); - src.outputs.push_back(oe); - if(src.outputs.size() >= fake_outputs_count) - break; - } + typedef cryptonote::tx_source_entry::output_entry tx_output_entry; + for (size_t n = 0; n < fake_outputs_count + 1; ++n) + { + tx_output_entry oe; + oe.first = std::get<0>(outs[out_index][n]); + oe.second.dest = rct::pk2rct(std::get<1>(outs[out_index][n])); + oe.second.mask = std::get<2>(outs[out_index][n]); + src.outputs.push_back(oe); } + ++i; //paste real transaction to the random index - auto it_to_insert = std::find_if(src.outputs.begin(), src.outputs.end(), [&](const tx_output_entry& a) + auto it_to_replace = std::find_if(src.outputs.begin(), src.outputs.end(), [&](const tx_output_entry& a) { - return a.first >= td.m_global_output_index; + return a.first == td.m_global_output_index; }); - //size_t real_index = src.outputs.size() ? (rand() % src.outputs.size() ):0; -// TODO: same index (for now) + THROW_WALLET_EXCEPTION_IF(it_to_replace == src.outputs.end(), error::wallet_internal_error, + "real output not found"); + tx_output_entry real_oe; real_oe.first = td.m_global_output_index; real_oe.second.dest = rct::pk2rct(boost::get(td.m_tx.vout[td.m_internal_output_index].target).key); real_oe.second.mask = rct::commit(td.amount(), td.m_mask); - auto interted_it = src.outputs.insert(it_to_insert, real_oe); + *it_to_replace = real_oe; src.real_out_tx_key = get_tx_pub_key_from_extra(td.m_tx); - src.real_output = interted_it - src.outputs.begin(); + src.real_output = it_to_replace - src.outputs.begin(); src.real_output_in_tx_index = td.m_internal_output_index; src.mask = td.m_mask; detail::print_source_entry(src); - ++i; + ++out_index; } cryptonote::tx_destination_entry change_dts = AUTO_VAL_INIT(change_dts); diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 259f7aca7..0e87614b4 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -463,6 +463,8 @@ namespace tools std::vector pick_prefered_rct_inputs(uint64_t needed_money) const; void set_spent(transfer_details &td, uint64_t height); void set_unspent(transfer_details &td); + template + void get_outs(std::vector> &outs, const std::list &selected_transfers, size_t fake_outputs_count); cryptonote::account_base m_account; std::string m_daemon_address;