diff --git a/src/currency_core/blockchain_storage.cpp b/src/currency_core/blockchain_storage.cpp index 18095d6f..3a65ae65 100644 --- a/src/currency_core/blockchain_storage.cpp +++ b/src/currency_core/blockchain_storage.cpp @@ -2475,15 +2475,21 @@ size_t blockchain_storage::get_alternative_blocks_count() const return m_alternative_chains.size(); } //------------------------------------------------------------------ -bool blockchain_storage::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) const +bool blockchain_storage::add_out_to_get_random_outs(COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& result_outs, uint64_t amount, size_t g_index, uint64_t mix_count, + bool use_only_forced_to_mix, uint64_t height_upper_limit) const { CRITICAL_REGION_LOCAL(m_read_lock); - auto out_ptr = m_db_outputs.get_subitem(amount, i); + auto out_ptr = m_db_outputs.get_subitem(amount, g_index); auto tx_ptr = m_db_transactions.find(out_ptr->tx_id); CHECK_AND_ASSERT_MES(tx_ptr, false, "internal error: transaction with id " << out_ptr->tx_id << ENDL << - ", used in mounts global index for amount=" << amount << ": i=" << i << "not found in transactions index"); + ", used in mounts global index for amount=" << amount << ": g_index=" << g_index << "not found in transactions index"); CHECK_AND_ASSERT_MES(tx_ptr->tx.vout.size() > out_ptr->out_no, false, "internal error: in global outs index, transaction out index=" << out_ptr->out_no << " more than transaction outputs = " << tx_ptr->tx.vout.size() << ", for tx id = " << out_ptr->tx_id); + + CHECK_AND_ASSERT_MES(amount != 0 || height_upper_limit != 0, false, "height_upper_limit must be nonzero for hidden amounts (amount = 0)"); + + if (height_upper_limit != 0 && tx_ptr->m_keeper_block_height > height_upper_limit) + return false; const transaction& tx = tx_ptr->tx; CHECK_AND_ASSERT_MES(tx_ptr->m_spent_flags.size() == tx.vout.size(), false, "internal error: spent_flag.size()=" << tx_ptr->m_spent_flags.size() << ", tx.vout.size()=" << tx.vout.size()); @@ -2495,12 +2501,25 @@ bool blockchain_storage::add_out_to_get_random_outs(COMMAND_RPC_GET_RANDOM_OUTPU //check if transaction is unlocked if (!is_tx_spendtime_unlocked(get_tx_unlock_time(tx, out_ptr->out_no))) return false; + + const tx_out_v& out_v = tx.vout[out_ptr->out_no]; // do not use burned coins - if (is_out_burned(tx.vout[out_ptr->out_no])) + if (is_out_burned(out_v)) return false; - VARIANT_SWITCH_BEGIN(tx.vout[out_ptr->out_no]); + // check mix_attr + uint8_t mix_attr = CURRENCY_TO_KEY_OUT_RELAXED; + if (!get_mix_attr_from_tx_out_v(out_v, mix_attr)) + return false; // output has no mix_attr, skip it + if (mix_attr == CURRENCY_TO_KEY_OUT_FORCED_NO_MIX) + return false; //COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS call means that ring signature will have more than one entry. + else if (use_only_forced_to_mix && mix_attr == CURRENCY_TO_KEY_OUT_RELAXED) + return false; //relaxed not allowed + else if (mix_attr != CURRENCY_TO_KEY_OUT_RELAXED && mix_attr > mix_count) + return false; //mix_attr set to specific minimum, and mix_count is less then desired count + + VARIANT_SWITCH_BEGIN(out_v); VARIANT_CASE_CONST(tx_out_bare, o) { if (o.target.type() == typeid(txout_htlc)) @@ -2508,41 +2527,20 @@ bool blockchain_storage::add_out_to_get_random_outs(COMMAND_RPC_GET_RANDOM_OUTPU //silently return false, it's ok return false; } - CHECK_AND_ASSERT_MES(o.target.type() == typeid(txout_to_key), false, "unknown tx out type"); + CHECK_AND_ASSERT_MES(o.target.type() == typeid(txout_to_key), false, "unexpected out target type: " << o.target.type().name()); const txout_to_key& otk = boost::get(o.target); - // TODO #@#@ remove code duplication, make extracting mix_attr in a more generalized way - - //use appropriate mix_attr out - uint8_t mix_attr = otk.mix_attr; - - if (mix_attr == CURRENCY_TO_KEY_OUT_FORCED_NO_MIX) - return false; //COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS call means that ring signature will have more than one entry. - else if (use_only_forced_to_mix && mix_attr == CURRENCY_TO_KEY_OUT_RELAXED) - return false; //relaxed not allowed - else if (mix_attr != CURRENCY_TO_KEY_OUT_RELAXED && mix_attr > mix_count) - return false;//mix_attr set to specific minimum, and mix_count is less then desired count - COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry& oen = *result_outs.outs.insert(result_outs.outs.end(), COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry()); - oen.global_amount_index = i; - oen.out_key = otk.key; + oen.global_amount_index = g_index; + oen.stealth_address = otk.key; } VARIANT_CASE_CONST(tx_out_zarcanum, toz) { - //use appropriate mix_attr out - uint8_t mix_attr = toz.mix_attr; - - if (mix_attr == CURRENCY_TO_KEY_OUT_FORCED_NO_MIX) - return false; //COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS call means that ring signature will have more than one entry. - else if (use_only_forced_to_mix && mix_attr == CURRENCY_TO_KEY_OUT_RELAXED) - return false; //relaxed not allowed - else if (mix_attr != CURRENCY_TO_KEY_OUT_RELAXED && mix_attr > mix_count) - return false;//mix_attr set to specific minimum, and mix_count is less then desired count - COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry& oen = *result_outs.outs.insert(result_outs.outs.end(), COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry()); - oen.global_amount_index = i; - oen.out_key = toz.amount_commitment; - // TODO @#@# this is certainly not enough + oen.amount_commitment = toz.amount_commitment; + oen.concealing_point = toz.concealing_point; + oen.global_amount_index = g_index; + oen.stealth_address = toz.stealth_address; } VARIANT_SWITCH_END(); @@ -2572,7 +2570,7 @@ size_t blockchain_storage::find_end_of_allowed_index(uint64_t amount) const bool blockchain_storage::get_random_outs_for_amounts(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res)const { CRITICAL_REGION_LOCAL(m_read_lock); - BOOST_FOREACH(uint64_t amount, req.amounts) + for(uint64_t amount : req.amounts) { 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; @@ -2586,31 +2584,32 @@ bool blockchain_storage::get_random_outs_for_amounts(const COMMAND_RPC_GET_RANDO //lets find upper bound of not fresh outs size_t up_index_limit = find_end_of_allowed_index(amount); CHECK_AND_ASSERT_MES(up_index_limit <= outs_container_size, false, "internal error: find_end_of_allowed_index returned wrong index=" << up_index_limit << ", with amount_outs.size = " << outs_container_size); - if (up_index_limit >= req.outs_count) + if (up_index_limit >= req.decoys_count) { std::set used; size_t try_count = 0; - for(uint64_t j = 0; j != req.outs_count && try_count < up_index_limit;) + for(uint64_t j = 0; j != req.decoys_count && try_count < up_index_limit;) { - size_t i = crypto::rand()%up_index_limit; - if(used.count(i)) + size_t g_index = crypto::rand() % up_index_limit; + if(used.count(g_index)) continue; - bool added = add_out_to_get_random_outs(result_outs, amount, i, req.outs_count, req.use_forced_mix_outs); - used.insert(i); + bool added = add_out_to_get_random_outs(result_outs, amount, g_index, req.decoys_count, req.use_forced_mix_outs, req.height_upper_limit); + used.insert(g_index); if(added) ++j; ++try_count; } - if (result_outs.outs.size() < req.outs_count) + if (result_outs.outs.size() < req.decoys_count) { - LOG_PRINT_RED_L0("Not enough inputs for amount " << print_money_brief(amount) << ", needed " << req.outs_count << ", added " << result_outs.outs.size() << " good outs from " << up_index_limit << " unlocked of " << outs_container_size << " total"); + LOG_PRINT_RED_L0("Not enough inputs for amount " << print_money_brief(amount) << ", needed " << req.decoys_count << ", added " << result_outs.outs.size() << " good outs from " << up_index_limit << " unlocked of " << outs_container_size << " total"); } - }else + } + else { size_t added = 0; for (size_t i = 0; i != up_index_limit; i++) - added += add_out_to_get_random_outs(result_outs, amount, i, req.outs_count, req.use_forced_mix_outs) ? 1 : 0; - LOG_PRINT_RED_L0("Not enough inputs for amount " << print_money_brief(amount) << ", needed " << req.outs_count << ", added " << added << " good outs from " << up_index_limit << " unlocked of " << outs_container_size << " total - respond with all good outs"); + added += add_out_to_get_random_outs(result_outs, amount, i, req.decoys_count, req.use_forced_mix_outs, req.height_upper_limit) ? 1 : 0; + LOG_PRINT_RED_L0("Not enough inputs for amount " << print_money_brief(amount) << ", needed " << req.decoys_count << ", added " << added << " good outs from " << up_index_limit << " unlocked of " << outs_container_size << " total - respond with all good outs"); } } return true; diff --git a/src/currency_core/blockchain_storage.h b/src/currency_core/blockchain_storage.h index 2671e1a6..b8eab01e 100644 --- a/src/currency_core/blockchain_storage.h +++ b/src/currency_core/blockchain_storage.h @@ -618,7 +618,7 @@ namespace currency bool add_transaction_from_block(const transaction& tx, const crypto::hash& tx_id, const crypto::hash& bl_id, uint64_t bl_height, uint64_t timestamp); bool push_transaction_to_global_outs_index(const transaction& tx, const crypto::hash& tx_id, std::vector& global_indexes); 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) const; + 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 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/currency_core/currency_format_utils.cpp b/src/currency_core/currency_format_utils.cpp index cb0abf18..db667ec0 100644 --- a/src/currency_core/currency_format_utils.cpp +++ b/src/currency_core/currency_format_utils.cpp @@ -246,10 +246,8 @@ namespace currency } CHECK_AND_ASSERT_MES(destinations.size() <= CURRENCY_TX_MAX_ALLOWED_OUTS || height == 0, false, "Too many outs (" << destinations.size() << ")! Miner tx can't be constructed."); + tx = AUTO_VAL_INIT_T(transaction); tx.version = tx_version; - tx.vin.clear(); - tx.vout.clear(); - tx.extra.clear(); keypair txkey = keypair::generate(); add_tx_pub_key_to_extra(tx, txkey.pub); diff --git a/src/currency_core/currency_format_utils_abstract.h b/src/currency_core/currency_format_utils_abstract.h index ccbbf7c8..d08f266f 100644 --- a/src/currency_core/currency_format_utils_abstract.h +++ b/src/currency_core/currency_format_utils_abstract.h @@ -229,6 +229,35 @@ namespace currency CHECK_AND_ASSERT_THROW_MES(false, "[get_key_offsets_from_txin_v] Wrong type: " << in_v.type().name()); } //--------------------------------------------------------------- + inline + bool get_mix_attr_from_tx_out_v(const tx_out_v& out_v, uint8_t& result) noexcept + { + try + { + if (out_v.type() == typeid(tx_out_bare)) + { + const tx_out_bare& ob = boost::get(out_v); + if (ob.target.type() == typeid(txout_to_key)) + { + result = boost::get(ob.target).mix_attr; + return true; + } + } + + if (out_v.type() == typeid(tx_out_zarcanum)) + { + result = boost::get(out_v).mix_attr; + return true; + } + } + catch(...) + { + // should never go here, just precaution + } + + return false; + } + //--------------------------------------------------------------- //, txin_htlc, txin_zc_input inline bool compare_variant_by_types(const txin_multisig& left, const txin_multisig& right) {