diff options
-rw-r--r-- | src/wallet/wallet2.cpp | 137 | ||||
-rw-r--r-- | src/wallet/wallet2.h | 2 |
2 files changed, 129 insertions, 10 deletions
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 83e7e3ed5..3b17c9bc9 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -3981,10 +3981,134 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions(std::vector<crypto } } + +bool wallet2::tx_add_fake_output(std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, uint64_t global_index, const crypto::public_key& tx_public_key, const rct::key& mask, uint64_t real_index, bool unlocked) const +{ + if (!unlocked) // don't add locked outs + return false; + if (global_index == real_index) // don't re-add real one + return false; + auto item = std::make_tuple(global_index, tx_public_key, mask); + if (std::find(outs.back().begin(), outs.back().end(), item) != outs.back().end()) // don't add duplicates + return false; + outs.back().push_back(item); + return true; +} + +void wallet2::light_wallet_get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, const std::list<size_t> &selected_transfers, size_t fake_outputs_count) { + + MDEBUG("LIGHTWALLET - Getting random outs"); + + cryptonote::COMMAND_RPC_GET_RANDOM_OUTS::request oreq; + cryptonote::COMMAND_RPC_GET_RANDOM_OUTS::response ores; + + size_t light_wallet_requested_outputs_count = (size_t)((fake_outputs_count + 1) * 1.5 + 1); + + // Amounts to ask for + // MyMonero api handle amounts and fees as strings + for(size_t idx: selected_transfers) { + const uint64_t ask_amount = m_transfers[idx].is_rct() ? 0 : m_transfers[idx].amount(); + std::ostringstream amount_ss; + amount_ss << ask_amount; + oreq.amounts.push_back(amount_ss.str()); + } + + oreq.count = light_wallet_requested_outputs_count; + m_daemon_rpc_mutex.lock(); + bool r = epee::net_utils::invoke_http_json("/get_random_outs", oreq, ores, m_http_client, rpc_timeout, "POST"); + m_daemon_rpc_mutex.unlock(); + THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_random_outs"); + THROW_WALLET_EXCEPTION_IF(ores.amount_outs.empty() , error::wallet_internal_error, "No outputs recieved from light wallet node. Error: " + ores.Error); + + // Check if we got enough outputs for each amount + for(auto& out: ores.amount_outs) { + const uint64_t out_amount = boost::lexical_cast<uint64_t>(out.amount); + THROW_WALLET_EXCEPTION_IF(out.outputs.size() < light_wallet_requested_outputs_count , error::wallet_internal_error, "Not enough outputs for amount: " + boost::lexical_cast<std::string>(out.amount)); + MDEBUG(out.outputs.size() << " outputs for amount "+ boost::lexical_cast<std::string>(out.amount) + " received from light wallet node"); + } + + MDEBUG("selected transfers size: " << selected_transfers.size()); + + for(size_t idx: selected_transfers) + { + // Create new index + outs.push_back(std::vector<get_outs_entry>()); + outs.back().reserve(fake_outputs_count + 1); + + // add real output first + const transfer_details &td = m_transfers[idx]; + const uint64_t amount = td.is_rct() ? 0 : td.amount(); + outs.back().push_back(std::make_tuple(td.m_global_output_index, td.get_public_key(), rct::commit(td.amount(), td.m_mask))); + MDEBUG("added real output " << string_tools::pod_to_hex(td.get_public_key())); + + // Even if the lightwallet server returns random outputs, we pick them randomly. + std::vector<size_t> order; + order.resize(light_wallet_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<unsigned>())); + + + LOG_PRINT_L2("Looking for " << (fake_outputs_count+1) << " outputs with amounts " << print_money(td.is_rct() ? 0 : td.amount())); + MDEBUG("OUTS SIZE: " << outs.back().size()); + for (size_t o = 0; o < light_wallet_requested_outputs_count && outs.back().size() < fake_outputs_count + 1; ++o) + { + // Random pick + size_t i = order[o]; + + // Find which random output key to use + bool found_amount = false; + size_t amount_key; + for(amount_key = 0; amount_key < ores.amount_outs.size(); ++amount_key) + { + if(boost::lexical_cast<uint64_t>(ores.amount_outs[amount_key].amount) == amount) { + found_amount = true; + break; + } + } + THROW_WALLET_EXCEPTION_IF(!found_amount , error::wallet_internal_error, "Outputs for amount " + boost::lexical_cast<std::string>(ores.amount_outs[amount_key].amount) + " not found" ); + + LOG_PRINT_L2("Index " << i << "/" << light_wallet_requested_outputs_count << ": idx " << ores.amount_outs[amount_key].outputs[i].global_index << " (real " << td.m_global_output_index << "), unlocked " << "(always in light)" << ", key " << ores.amount_outs[0].outputs[i].public_key); + + // Convert light wallet string data to proper data structures + crypto::public_key tx_public_key; + rct::key mask = AUTO_VAL_INIT(mask); // decrypted mask - not used here + rct::key rct_commit = AUTO_VAL_INIT(rct_commit); + THROW_WALLET_EXCEPTION_IF(string_tools::validate_hex(64, ores.amount_outs[amount_key].outputs[i].public_key), error::wallet_internal_error, "Invalid public_key"); + string_tools::hex_to_pod(ores.amount_outs[amount_key].outputs[i].public_key, tx_public_key); + const uint64_t global_index = ores.amount_outs[amount_key].outputs[i].global_index; + if(!light_wallet_parse_rct_str(ores.amount_outs[amount_key].outputs[i].rct, tx_public_key, 0, mask, rct_commit, false)) + rct_commit = rct::zeroCommit(td.amount()); + + if (tx_add_fake_output(outs, global_index, tx_public_key, rct_commit, td.m_global_output_index, true)) { + MDEBUG("added fake output " << ores.amount_outs[amount_key].outputs[i].public_key); + MDEBUG("index " << global_index); + } + } + + THROW_WALLET_EXCEPTION_IF(outs.back().size() < fake_outputs_count + 1 , error::wallet_internal_error, "Not enough fake outputs found" ); + + // Real output is the first. Shuffle outputs + MTRACE(outs.back().size() << " outputs added. Sorting outputs by index:"); + std::sort(outs.back().begin(), outs.back().end(), [](const get_outs_entry &a, const get_outs_entry &b) { return std::get<0>(a) < std::get<0>(b); }); + + // Print output order + for(auto added_out: outs.back()) + MTRACE(std::get<0>(added_out)); + + } +} + void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, const std::list<size_t> &selected_transfers, size_t fake_outputs_count) { LOG_PRINT_L2("fake_outputs_count: " << fake_outputs_count); outs.clear(); + + if(m_light_wallet && fake_outputs_count > 0) { + light_wallet_get_outs(outs, selected_transfers, fake_outputs_count); + return; + } + if (fake_outputs_count > 0) { // get histogram for the amounts we need @@ -4181,14 +4305,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> { size_t i = base + order[o]; 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; - auto item = std::make_tuple(req.outputs[i].index, daemon_resp.outs[i].key, daemon_resp.outs[i].mask); - if (std::find(outs.back().begin(), outs.back().end(), item) != outs.back().end()) // don't add duplicates - continue; - outs.back().push_back(item); + tx_add_fake_output(outs, req.outputs[i].index, daemon_resp.outs[i].key, daemon_resp.outs[i].mask, td.m_global_output_index, daemon_resp.outs[i].unlocked); } if (outs.back().size() < fake_outputs_count + 1) { @@ -4210,7 +4327,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> const transfer_details &td = m_transfers[idx]; std::vector<get_outs_entry> v; 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<txout_to_key>(td.m_tx.vout[td.m_internal_output_index].target).key, mask)); + v.push_back(std::make_tuple(td.m_global_output_index, td.get_public_key(), mask)); outs.push_back(v); } } @@ -4446,7 +4563,7 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry tx_output_entry real_oe; real_oe.first = td.m_global_output_index; - real_oe.second.dest = rct::pk2rct(boost::get<txout_to_key>(td.m_tx.vout[td.m_internal_output_index].target).key); + real_oe.second.dest = rct::pk2rct(td.get_public_key()); real_oe.second.mask = rct::commit(td.amount(), td.m_mask); *it_to_replace = real_oe; src.real_out_tx_key = get_tx_pub_key_from_extra(td.m_tx, td.m_pk_index); diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 983b448d6..e8a30dba9 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -796,6 +796,8 @@ namespace tools void set_spent(size_t idx, uint64_t height); void set_unspent(size_t idx); void get_outs(std::vector<std::vector<get_outs_entry>> &outs, const std::list<size_t> &selected_transfers, size_t fake_outputs_count); + bool tx_add_fake_output(std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, uint64_t global_index, const crypto::public_key& tx_public_key, const rct::key& mask, uint64_t real_index, bool unlocked) const; + bool wallet_generate_key_image_helper(const cryptonote::account_keys& ack, const crypto::public_key& tx_public_key, size_t real_output_index, cryptonote::keypair& in_ephemeral, crypto::key_image& ki); crypto::public_key get_tx_pub_key_from_received_outs(const tools::wallet2::transfer_details &td) const; bool should_pick_a_second_output(bool use_rct, size_t n_transfers, const std::vector<size_t> &unused_transfers_indices, const std::vector<size_t> &unused_dust_indices) const; std::vector<size_t> get_only_rct(const std::vector<size_t> &unused_dust_indices, const std::vector<size_t> &unused_transfers_indices) const; |