diff --git a/src/currency_core/blockchain_storage.cpp b/src/currency_core/blockchain_storage.cpp index 49f49910..ac550fb6 100644 --- a/src/currency_core/blockchain_storage.cpp +++ b/src/currency_core/blockchain_storage.cpp @@ -2726,11 +2726,12 @@ bool blockchain_storage::get_target_outs_for_amount_prezarcanum(const COMMAND_RP } } //------------------------------------------------------------------ - -bool blockchain_storage::get_target_outs_for_amount_postzarcanum(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS2::request& req, const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS2::offsets_distribution& details, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& result_outs, std::map& amounts_to_up_index_limit_cache) const +bool blockchain_storage::get_target_outs_for_postzarcanum(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS2::request& req, const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS2::offsets_distribution& details, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& result_outs, std::map& amounts_to_up_index_limit_cache) const { + std::set used; for (auto offset : details.offsets) { + //perfectly we would need to find transaction's output on the given height, with the given probability //of being coinbase(coinbase outputs should be included less in decoy selection algorithm) bool is_coinbase = (crypto::rand() % 101) > req.coinbase_percents ? false : true; @@ -2749,8 +2750,6 @@ bool blockchain_storage::get_target_outs_for_amount_postzarcanum(const COMMAND_R #define TARGET_RANDOM_OUTS_SELECTIOM_POOL_MIN 10 //try to find output around given H std::vector selected_global_indexes; - - auto process_tx = [&](const crypto::hash& tx_id) { auto tx_ptr = m_db_transactions.find(tx_id); @@ -2778,6 +2777,11 @@ bool blockchain_storage::get_target_outs_for_amount_postzarcanum(const COMMAND_R continue; } + if (used.find(tx_ptr->m_global_output_indexes[i]) != used.end()) + { + continue; + } + // add output // note: code that will process selected_global_indes will be revisiting transactions entries to obtain all // needed data, that should work relatively effective because of on-top-of-db cache keep daya unserialized @@ -2788,7 +2792,7 @@ bool blockchain_storage::get_target_outs_for_amount_postzarcanum(const COMMAND_R while (selected_global_indexes.size() < TARGET_RANDOM_OUTS_SELECTIOM_POOL_MIN) { - auto block_ptr = m_db_blocks.get(estimated_h--); + auto block_ptr = m_db_blocks.get(estimated_h); if (is_coinbase && is_pos_block(block_ptr->bl) ) { process_tx(get_transaction_hash(block_ptr->bl.miner_tx)); @@ -2801,12 +2805,25 @@ bool blockchain_storage::get_target_outs_for_amount_postzarcanum(const COMMAND_R process_tx(tx_id); } } + if(estimated_h) + estimated_h--; + else + { + //likely unusual situation when blocks enumerated all way back to genesis + //let's check if we have at least something + if (!selected_global_indexes.size()) + { + //need to regenerate offsets + return false; + } + } } //pick up a random output from selected_global_indes uint64_t global_index = selected_global_indexes[crypto::rand() % selected_global_indexes.size()]; bool res = add_out_to_get_random_outs(result_outs, details.amount, global_index, details.offsets.size(), req.use_forced_mix_outs, req.height_upper_limit); CHECK_AND_ASSERT_THROW_MES(res, "Failed to add_out_to_get_random_outs([" << global_index << "]) at postzarcanum era"); + used.insert(global_index); } return true; } @@ -2824,16 +2841,19 @@ bool blockchain_storage::get_random_outs_for_amounts2(const COMMAND_RPC_GET_RAND COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& result_outs = *res.outs.insert(res.outs.end(), COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount()); result_outs.amount = amount; + bool r = false; if (amount == 0) { //zarcanum era inputs - get_target_outs_for_amount_postzarcanum(req, req.amounts[i], result_outs, amounts_to_up_index_limit_cache); + r = get_target_outs_for_postzarcanum(req, req.amounts[i], result_outs, amounts_to_up_index_limit_cache); } else { //zarcanum era inputs - get_target_outs_for_amount_prezarcanum(req, req.amounts[i], result_outs, amounts_to_up_index_limit_cache); + r = get_target_outs_for_amount_prezarcanum(req, req.amounts[i], result_outs, amounts_to_up_index_limit_cache); } + if (!r) + return false; } return true; } diff --git a/src/currency_core/blockchain_storage.h b/src/currency_core/blockchain_storage.h index 2ebae077..3f040f19 100644 --- a/src/currency_core/blockchain_storage.h +++ b/src/currency_core/blockchain_storage.h @@ -639,7 +639,7 @@ namespace currency bool pop_transaction_from_global_index(const transaction& tx, const crypto::hash& tx_id); bool add_out_to_get_random_outs(COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& result_outs, uint64_t amount, size_t i, uint64_t mix_count, bool use_only_forced_to_mix = false, uint64_t height_upper_limit = 0) const; bool get_target_outs_for_amount_prezarcanum(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS2::request& req, const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS2::offsets_distribution& details, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& result_outs, std::map& amounts_to_up_index_limit_cache) const; - bool get_target_outs_for_amount_postzarcanum(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS2::request& req, const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS2::offsets_distribution& details, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& result_outs, std::map& amounts_to_up_index_limit_cache) const; + bool get_target_outs_for_postzarcanum(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS2::request& req, const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS2::offsets_distribution& details, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& result_outs, std::map& amounts_to_up_index_limit_cache) const; bool add_block_as_invalid(const block& bl, const crypto::hash& h); bool add_block_as_invalid(const block_extended_info& bei, const crypto::hash& h); size_t find_end_of_allowed_index(uint64_t amount)const; diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index b65daa61..6029d5f5 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -5922,12 +5922,29 @@ bool wallet2::prepare_tx_sources(size_t fake_outputs_count, std::vectorcall_COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS2(req, daemon_resp); - THROW_IF_FALSE_WALLET_EX(r, error::no_connection_to_daemon, "getrandom_outs2.bin"); - THROW_IF_FALSE_WALLET_EX(daemon_resp.status != API_RETURN_CODE_BUSY, error::daemon_busy, "getrandom_outs.bin"); - THROW_IF_FALSE_WALLET_EX(daemon_resp.status == API_RETURN_CODE_OK, error::get_random_outs_error, daemon_resp.status); - WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(daemon_resp.outs.size() == selected_indicies.size(), - "daemon returned wrong response for getrandom_outs.bin, wrong amounts count = " << daemon_resp.outs.size() << ", expected: " << selected_indicies.size()); + size_t attempt_count = 0; + while (true) + { + bool r = m_core_proxy->call_COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS2(req, daemon_resp); + THROW_IF_FALSE_WALLET_EX(r, error::no_connection_to_daemon, "getrandom_outs2.bin"); + if(daemon_resp.status == API_RETURN_CODE_FAIL) + { + if (attempt_count < 10) + { + attempt_count++; + continue; + } + else + { + WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(daemon_resp.outs.size() == selected_indicies.size(), + "unable to exacute getrandom_outs.bin after 10 attempts with code API_RETURN_CODE_FAIL, there must be problems with mixins"); + } + } + THROW_IF_FALSE_WALLET_EX(daemon_resp.status != API_RETURN_CODE_BUSY, error::daemon_busy, "getrandom_outs.bin"); + THROW_IF_FALSE_WALLET_EX(daemon_resp.status == API_RETURN_CODE_OK, error::get_random_outs_error, daemon_resp.status); + WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(daemon_resp.outs.size() == selected_indicies.size(), + "daemon returned wrong response for getrandom_outs.bin, wrong amounts count = " << daemon_resp.outs.size() << ", expected: " << selected_indicies.size()); + } std::vector scanty_outs; for(COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& amount_outs : daemon_resp.outs)