mirror of
https://github.com/monero-project/monero.git
synced 2024-11-17 08:17:37 +00:00
Merge pull request #4253
be001326
remove obsolete daemon selection of fake outs and old tx construction (moneromooo-monero)
This commit is contained in:
commit
239a7e10ff
16 changed files with 18 additions and 955 deletions
|
@ -1717,17 +1717,6 @@ size_t Blockchain::get_alternative_blocks_count() const
|
|||
//------------------------------------------------------------------
|
||||
// This function adds the output specified by <amount, i> to the result_outs container
|
||||
// unlocked and other such checks should be done by here.
|
||||
void Blockchain::add_out_to_get_random_outs(COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& result_outs, uint64_t amount, size_t i) const
|
||||
{
|
||||
LOG_PRINT_L3("Blockchain::" << __func__);
|
||||
CRITICAL_REGION_LOCAL(m_blockchain_lock);
|
||||
|
||||
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;
|
||||
output_data_t data = m_db->get_output_key(amount, i);
|
||||
oen.out_key = data.pubkey;
|
||||
}
|
||||
|
||||
uint64_t Blockchain::get_num_mature_outputs(uint64_t amount) const
|
||||
{
|
||||
uint64_t num_outs = m_db->get_num_outputs(amount);
|
||||
|
@ -1745,243 +1734,12 @@ uint64_t Blockchain::get_num_mature_outputs(uint64_t amount) const
|
|||
return num_outs;
|
||||
}
|
||||
|
||||
std::vector<uint64_t> Blockchain::get_random_outputs(uint64_t amount, uint64_t count) const
|
||||
{
|
||||
uint64_t num_outs = get_num_mature_outputs(amount);
|
||||
|
||||
std::vector<uint64_t> indices;
|
||||
|
||||
std::unordered_set<uint64_t> seen_indices;
|
||||
|
||||
// if there aren't enough outputs to mix with (or just enough),
|
||||
// use all of them. Eventually this should become impossible.
|
||||
if (num_outs <= count)
|
||||
{
|
||||
for (uint64_t i = 0; i < num_outs; i++)
|
||||
{
|
||||
// get tx_hash, tx_out_index from DB
|
||||
tx_out_index toi = m_db->get_output_tx_and_index(amount, i);
|
||||
|
||||
// if tx is unlocked, add output to indices
|
||||
if (is_tx_spendtime_unlocked(m_db->get_tx_unlock_time(toi.first)))
|
||||
{
|
||||
indices.push_back(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// while we still need more mixins
|
||||
while (indices.size() < 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>() % ((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);
|
||||
|
||||
// get tx_hash, tx_out_index from DB
|
||||
tx_out_index toi = m_db->get_output_tx_and_index(amount, i);
|
||||
|
||||
// if the output's transaction is unlocked, add the output's index to
|
||||
// our list.
|
||||
if (is_tx_spendtime_unlocked(m_db->get_tx_unlock_time(toi.first)))
|
||||
{
|
||||
indices.push_back(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return indices;
|
||||
}
|
||||
|
||||
crypto::public_key Blockchain::get_output_key(uint64_t amount, uint64_t global_index) const
|
||||
{
|
||||
output_data_t data = m_db->get_output_key(amount, global_index);
|
||||
return data.pubkey;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------
|
||||
// This function takes an RPC request for mixins and creates an RPC response
|
||||
// with the requested mixins.
|
||||
// TODO: figure out why this returns boolean / if we should be returning false
|
||||
// in some cases
|
||||
bool Blockchain::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
|
||||
{
|
||||
LOG_PRINT_L3("Blockchain::" << __func__);
|
||||
CRITICAL_REGION_LOCAL(m_blockchain_lock);
|
||||
|
||||
// for each amount that we need to get mixins for, get <n> random outputs
|
||||
// from BlockchainDB where <n> is req.outs_count (number of mixins).
|
||||
for (uint64_t amount : req.amounts)
|
||||
{
|
||||
// create outs_for_amount struct and populate amount field
|
||||
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;
|
||||
|
||||
std::vector<uint64_t> indices = get_random_outputs(amount, req.outs_count);
|
||||
|
||||
for (auto i : indices)
|
||||
{
|
||||
COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry& oe = *result_outs.outs.insert(result_outs.outs.end(), COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry());
|
||||
|
||||
oe.global_amount_index = i;
|
||||
oe.out_key = get_output_key(amount, i);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------
|
||||
// This function adds the ringct output at index i to the list
|
||||
// unlocked and other such checks should be done by here.
|
||||
void Blockchain::add_out_to_get_rct_random_outs(std::list<COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::out_entry>& outs, uint64_t amount, size_t i) const
|
||||
{
|
||||
LOG_PRINT_L3("Blockchain::" << __func__);
|
||||
CRITICAL_REGION_LOCAL(m_blockchain_lock);
|
||||
|
||||
COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::out_entry& oen = *outs.insert(outs.end(), COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::out_entry());
|
||||
oen.amount = amount;
|
||||
oen.global_amount_index = i;
|
||||
output_data_t data = m_db->get_output_key(amount, i);
|
||||
oen.out_key = data.pubkey;
|
||||
oen.commitment = data.commitment;
|
||||
}
|
||||
//------------------------------------------------------------------
|
||||
// This function takes an RPC request for mixins and creates an RPC response
|
||||
// with the requested mixins.
|
||||
// TODO: figure out why this returns boolean / if we should be returning false
|
||||
// in some cases
|
||||
bool Blockchain::get_random_rct_outs(const COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::request& req, COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::response& res) const
|
||||
{
|
||||
LOG_PRINT_L3("Blockchain::" << __func__);
|
||||
CRITICAL_REGION_LOCAL(m_blockchain_lock);
|
||||
|
||||
// for each amount that we need to get mixins for, get <n> random outputs
|
||||
// from BlockchainDB where <n> is req.outs_count (number of mixins).
|
||||
auto num_outs = m_db->get_num_outputs(0);
|
||||
// ensure we don't include outputs that aren't yet eligible to be used
|
||||
// outpouts are sorted by height
|
||||
while (num_outs > 0)
|
||||
{
|
||||
const tx_out_index toi = m_db->get_output_tx_and_index(0, num_outs - 1);
|
||||
const uint64_t height = m_db->get_tx_block_height(toi.first);
|
||||
if (height + CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE <= m_db->height())
|
||||
break;
|
||||
--num_outs;
|
||||
}
|
||||
|
||||
std::unordered_set<uint64_t> seen_indices;
|
||||
|
||||
// if there aren't enough outputs to mix with (or just enough),
|
||||
// use all of them. Eventually this should become impossible.
|
||||
if (num_outs <= req.outs_count)
|
||||
{
|
||||
for (uint64_t i = 0; i < num_outs; i++)
|
||||
{
|
||||
// get tx_hash, tx_out_index from DB
|
||||
tx_out_index toi = m_db->get_output_tx_and_index(0, i);
|
||||
|
||||
// if tx is unlocked, add output to result_outs
|
||||
if (is_tx_spendtime_unlocked(m_db->get_tx_unlock_time(toi.first)))
|
||||
{
|
||||
add_out_to_get_rct_random_outs(res.outs, 0, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// while we still need more mixins
|
||||
while (res.outs.size() < req.outs_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>() % ((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);
|
||||
|
||||
// get tx_hash, tx_out_index from DB
|
||||
tx_out_index toi = m_db->get_output_tx_and_index(0, i);
|
||||
|
||||
// if the output's transaction is unlocked, add the output's index to
|
||||
// our list.
|
||||
if (is_tx_spendtime_unlocked(m_db->get_tx_unlock_time(toi.first)))
|
||||
{
|
||||
add_out_to_get_rct_random_outs(res.outs, 0, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (res.outs.size() < req.outs_count)
|
||||
return false;
|
||||
#if 0
|
||||
// if we do not have enough RCT inputs, we can pick from the non RCT ones
|
||||
// which will have a zero mask
|
||||
if (res.outs.size() < req.outs_count)
|
||||
{
|
||||
LOG_PRINT_L0("Out of RCT inputs (" << res.outs.size() << "/" << req.outs_count << "), using regular ones");
|
||||
|
||||
// TODO: arbitrary selection, needs better
|
||||
COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request req2 = AUTO_VAL_INIT(req2);
|
||||
COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response res2 = AUTO_VAL_INIT(res2);
|
||||
req2.outs_count = req.outs_count - res.outs.size();
|
||||
static const uint64_t amounts[] = {1, 10, 20, 50, 100, 200, 500, 1000, 10000};
|
||||
for (uint64_t a: amounts)
|
||||
req2.amounts.push_back(a);
|
||||
if (!get_random_outs_for_amounts(req2, res2))
|
||||
return false;
|
||||
|
||||
// pick random ones from there
|
||||
while (res.outs.size() < req.outs_count)
|
||||
{
|
||||
int list_idx = rand() % (sizeof(amounts)/sizeof(amounts[0]));
|
||||
if (!res2.outs[list_idx].outs.empty())
|
||||
{
|
||||
const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry oe = res2.outs[list_idx].outs.back();
|
||||
res2.outs[list_idx].outs.pop_back();
|
||||
add_out_to_get_rct_random_outs(res.outs, res2.outs[list_idx].amount, oe.global_amount_index);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------
|
||||
bool Blockchain::get_outs(const COMMAND_RPC_GET_OUTPUTS_BIN::request& req, COMMAND_RPC_GET_OUTPUTS_BIN::response& res) const
|
||||
{
|
||||
|
|
|
@ -446,16 +446,6 @@ namespace cryptonote
|
|||
*/
|
||||
uint64_t get_num_mature_outputs(uint64_t amount) const;
|
||||
|
||||
/**
|
||||
* @brief get random outputs (indices) for an amount
|
||||
*
|
||||
* @param amount the amount
|
||||
* @param count the number of random outputs to choose
|
||||
*
|
||||
* @return the outputs' amount-global indices
|
||||
*/
|
||||
std::vector<uint64_t> get_random_outputs(uint64_t amount, uint64_t count) const;
|
||||
|
||||
/**
|
||||
* @brief get the public key for an output
|
||||
*
|
||||
|
@ -466,22 +456,6 @@ namespace cryptonote
|
|||
*/
|
||||
crypto::public_key get_output_key(uint64_t amount, uint64_t global_index) const;
|
||||
|
||||
/**
|
||||
* @brief gets random outputs to mix with
|
||||
*
|
||||
* This function takes an RPC request for outputs to mix with
|
||||
* and creates an RPC response with the resultant output indices.
|
||||
*
|
||||
* Outputs to mix with are randomly selected from the utxo set
|
||||
* for each output amount in the request.
|
||||
*
|
||||
* @param req the output amounts and number of mixins to select
|
||||
* @param res return-by-reference the resultant output indices
|
||||
*
|
||||
* @return true
|
||||
*/
|
||||
bool 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;
|
||||
|
||||
/**
|
||||
* @brief gets specific outputs to mix with
|
||||
*
|
||||
|
@ -508,23 +482,6 @@ namespace cryptonote
|
|||
*/
|
||||
void get_output_key_mask_unlocked(const uint64_t& amount, const uint64_t& index, crypto::public_key& key, rct::key& mask, bool& unlocked) const;
|
||||
|
||||
/**
|
||||
* @brief gets random ringct outputs to mix with
|
||||
*
|
||||
* This function takes an RPC request for outputs to mix with
|
||||
* and creates an RPC response with the resultant output indices
|
||||
* and the matching keys.
|
||||
*
|
||||
* Outputs to mix with are randomly selected from the utxo set
|
||||
* for each output amount in the request.
|
||||
*
|
||||
* @param req the output amounts and number of mixins to select
|
||||
* @param res return-by-reference the resultant output indices
|
||||
*
|
||||
* @return true
|
||||
*/
|
||||
bool get_random_rct_outs(const COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::request& req, COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::response& res) const;
|
||||
|
||||
/**
|
||||
* @brief gets per block distribution of outputs of a given amount
|
||||
*
|
||||
|
@ -1271,24 +1228,6 @@ namespace cryptonote
|
|||
*/
|
||||
void get_last_n_blocks_weights(std::vector<size_t>& weights, size_t count) const;
|
||||
|
||||
/**
|
||||
* @brief adds the given output to the requested set of random outputs
|
||||
*
|
||||
* @param result_outs return-by-reference the set the output is to be added to
|
||||
* @param amount the output amount
|
||||
* @param i the output index (indexed to amount)
|
||||
*/
|
||||
void add_out_to_get_random_outs(COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& result_outs, uint64_t amount, size_t i) const;
|
||||
|
||||
/**
|
||||
* @brief adds the given output to the requested set of random ringct outputs
|
||||
*
|
||||
* @param outs return-by-reference the set the output is to be added to
|
||||
* @param amount the output amount (0 for rct inputs)
|
||||
* @param i the rct output index
|
||||
*/
|
||||
void add_out_to_get_rct_random_outs(std::list<COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::out_entry>& outs, uint64_t amount, size_t i) const;
|
||||
|
||||
/**
|
||||
* @brief checks if a transaction is unlocked (its outputs spendable)
|
||||
*
|
||||
|
|
|
@ -1181,21 +1181,11 @@ namespace cryptonote
|
|||
return m_blockchain_storage.find_blockchain_supplement(req_start_block, qblock_ids, blocks, total_height, start_height, pruned, get_miner_tx_hash, max_count);
|
||||
}
|
||||
//-----------------------------------------------------------------------------------------------
|
||||
bool core::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
|
||||
{
|
||||
return m_blockchain_storage.get_random_outs_for_amounts(req, res);
|
||||
}
|
||||
//-----------------------------------------------------------------------------------------------
|
||||
bool core::get_outs(const COMMAND_RPC_GET_OUTPUTS_BIN::request& req, COMMAND_RPC_GET_OUTPUTS_BIN::response& res) const
|
||||
{
|
||||
return m_blockchain_storage.get_outs(req, res);
|
||||
}
|
||||
//-----------------------------------------------------------------------------------------------
|
||||
bool core::get_random_rct_outs(const COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::request& req, COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::response& res) const
|
||||
{
|
||||
return m_blockchain_storage.get_random_rct_outs(req, res);
|
||||
}
|
||||
//-----------------------------------------------------------------------------------------------
|
||||
bool core::get_output_distribution(uint64_t amount, uint64_t from_height, uint64_t to_height, uint64_t &start_height, std::vector<uint64_t> &distribution, uint64_t &base) const
|
||||
{
|
||||
return m_blockchain_storage.get_output_distribution(amount, from_height, to_height, start_height, distribution, base);
|
||||
|
|
|
@ -550,13 +550,6 @@ namespace cryptonote
|
|||
*/
|
||||
difficulty_type get_block_cumulative_difficulty(uint64_t height) const;
|
||||
|
||||
/**
|
||||
* @copydoc Blockchain::get_random_outs_for_amounts
|
||||
*
|
||||
* @note see Blockchain::get_random_outs_for_amounts
|
||||
*/
|
||||
bool 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;
|
||||
|
||||
/**
|
||||
* @copydoc Blockchain::get_outs
|
||||
*
|
||||
|
@ -564,14 +557,6 @@ namespace cryptonote
|
|||
*/
|
||||
bool get_outs(const COMMAND_RPC_GET_OUTPUTS_BIN::request& req, COMMAND_RPC_GET_OUTPUTS_BIN::response& res) const;
|
||||
|
||||
/**
|
||||
*
|
||||
* @copydoc Blockchain::get_random_rct_outs
|
||||
*
|
||||
* @note see Blockchain::get_random_rct_outs
|
||||
*/
|
||||
bool get_random_rct_outs(const COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::request& req, COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::response& res) const;
|
||||
|
||||
/**
|
||||
* @copydoc Blockchain::get_output_distribution
|
||||
*
|
||||
|
|
|
@ -364,49 +364,6 @@ namespace cryptonote
|
|||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------------------------------------------------------
|
||||
bool core_rpc_server::on_get_random_outs(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res)
|
||||
{
|
||||
PERF_TIMER(on_get_random_outs);
|
||||
bool r;
|
||||
if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS>(invoke_http_mode::BIN, "/getrandom_outs.bin", req, res, r))
|
||||
return r;
|
||||
|
||||
res.status = "Failed";
|
||||
|
||||
if (m_restricted)
|
||||
{
|
||||
if (req.amounts.size() > 100 || req.outs_count > MAX_RESTRICTED_FAKE_OUTS_COUNT)
|
||||
{
|
||||
res.status = "Too many outs requested";
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if(!m_core.get_random_outs_for_amounts(req, res))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
res.status = CORE_RPC_STATUS_OK;
|
||||
std::stringstream ss;
|
||||
typedef COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount outs_for_amount;
|
||||
typedef COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry out_entry;
|
||||
std::for_each(res.outs.begin(), res.outs.end(), [&](outs_for_amount& ofa)
|
||||
{
|
||||
ss << "[" << ofa.amount << "]:";
|
||||
CHECK_AND_ASSERT_MES(ofa.outs.size(), ;, "internal error: ofa.outs.size() is empty for amount " << ofa.amount);
|
||||
std::for_each(ofa.outs.begin(), ofa.outs.end(), [&](out_entry& oe)
|
||||
{
|
||||
ss << oe.global_amount_index << " ";
|
||||
});
|
||||
ss << ENDL;
|
||||
});
|
||||
std::string s = ss.str();
|
||||
LOG_PRINT_L2("COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS: " << ENDL << s);
|
||||
res.status = CORE_RPC_STATUS_OK;
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------------------------------------------------------
|
||||
bool core_rpc_server::on_get_outs_bin(const COMMAND_RPC_GET_OUTPUTS_BIN::request& req, COMMAND_RPC_GET_OUTPUTS_BIN::response& res)
|
||||
{
|
||||
PERF_TIMER(on_get_outs_bin);
|
||||
|
@ -476,34 +433,6 @@ namespace cryptonote
|
|||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------------------------------------------------------
|
||||
bool core_rpc_server::on_get_random_rct_outs(const COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::request& req, COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::response& res)
|
||||
{
|
||||
PERF_TIMER(on_get_random_rct_outs);
|
||||
bool r;
|
||||
if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS>(invoke_http_mode::BIN, "/getrandom_rctouts.bin", req, res, r))
|
||||
return r;
|
||||
|
||||
res.status = "Failed";
|
||||
if(!m_core.get_random_rct_outs(req, res))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
res.status = CORE_RPC_STATUS_OK;
|
||||
std::stringstream ss;
|
||||
typedef COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::out_entry out_entry;
|
||||
CHECK_AND_ASSERT_MES(res.outs.size(), true, "internal error: res.outs.size() is empty");
|
||||
std::for_each(res.outs.begin(), res.outs.end(), [&](out_entry& oe)
|
||||
{
|
||||
ss << oe.global_amount_index << " ";
|
||||
});
|
||||
ss << ENDL;
|
||||
std::string s = ss.str();
|
||||
LOG_PRINT_L2("COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS: " << ENDL << s);
|
||||
res.status = CORE_RPC_STATUS_OK;
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------------------------------------------------------
|
||||
bool core_rpc_server::on_get_indexes(const COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::request& req, COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::response& res)
|
||||
{
|
||||
PERF_TIMER(on_get_indexes);
|
||||
|
|
|
@ -87,11 +87,7 @@ namespace cryptonote
|
|||
MAP_URI_AUTO_BIN2("/get_hashes.bin", on_get_hashes, COMMAND_RPC_GET_HASHES_FAST)
|
||||
MAP_URI_AUTO_BIN2("/gethashes.bin", on_get_hashes, COMMAND_RPC_GET_HASHES_FAST)
|
||||
MAP_URI_AUTO_BIN2("/get_o_indexes.bin", on_get_indexes, COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES)
|
||||
MAP_URI_AUTO_BIN2("/get_random_outs.bin", on_get_random_outs, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS)
|
||||
MAP_URI_AUTO_BIN2("/getrandom_outs.bin", on_get_random_outs, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS)
|
||||
MAP_URI_AUTO_BIN2("/get_outs.bin", on_get_outs_bin, COMMAND_RPC_GET_OUTPUTS_BIN)
|
||||
MAP_URI_AUTO_BIN2("/get_random_rctouts.bin", on_get_random_rct_outs, COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS)
|
||||
MAP_URI_AUTO_BIN2("/getrandom_rctouts.bin", on_get_random_rct_outs, COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS)
|
||||
MAP_URI_AUTO_JON2("/get_transactions", on_get_transactions, COMMAND_RPC_GET_TRANSACTIONS)
|
||||
MAP_URI_AUTO_JON2("/gettransactions", on_get_transactions, COMMAND_RPC_GET_TRANSACTIONS)
|
||||
MAP_URI_AUTO_JON2("/get_alt_blocks_hashes", on_get_alt_blocks_hashes, COMMAND_RPC_GET_ALT_BLOCKS_HASHES)
|
||||
|
@ -171,10 +167,8 @@ namespace cryptonote
|
|||
bool on_start_mining(const COMMAND_RPC_START_MINING::request& req, COMMAND_RPC_START_MINING::response& res);
|
||||
bool on_stop_mining(const COMMAND_RPC_STOP_MINING::request& req, COMMAND_RPC_STOP_MINING::response& res);
|
||||
bool on_mining_status(const COMMAND_RPC_MINING_STATUS::request& req, COMMAND_RPC_MINING_STATUS::response& res);
|
||||
bool on_get_random_outs(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res);
|
||||
bool on_get_outs_bin(const COMMAND_RPC_GET_OUTPUTS_BIN::request& req, COMMAND_RPC_GET_OUTPUTS_BIN::response& res);
|
||||
bool on_get_outs(const COMMAND_RPC_GET_OUTPUTS::request& req, COMMAND_RPC_GET_OUTPUTS::response& res);
|
||||
bool on_get_random_rct_outs(const COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::request& req, COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::response& res);
|
||||
bool on_get_info(const COMMAND_RPC_GET_INFO::request& req, COMMAND_RPC_GET_INFO::response& res);
|
||||
bool on_save_bc(const COMMAND_RPC_SAVE_BC::request& req, COMMAND_RPC_SAVE_BC::response& res);
|
||||
bool on_get_peer_list(const COMMAND_RPC_GET_PEER_LIST::request& req, COMMAND_RPC_GET_PEER_LIST::response& res);
|
||||
|
|
|
@ -680,50 +680,6 @@ namespace cryptonote
|
|||
};
|
||||
};
|
||||
//-----------------------------------------------
|
||||
struct COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS
|
||||
{
|
||||
struct request
|
||||
{
|
||||
std::vector<uint64_t> amounts;
|
||||
uint64_t outs_count;
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(amounts)
|
||||
KV_SERIALIZE(outs_count)
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
|
||||
#pragma pack (push, 1)
|
||||
struct out_entry
|
||||
{
|
||||
uint64_t global_amount_index;
|
||||
crypto::public_key out_key;
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
struct outs_for_amount
|
||||
{
|
||||
uint64_t amount;
|
||||
std::list<out_entry> outs;
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(amount)
|
||||
KV_SERIALIZE_CONTAINER_POD_AS_BLOB(outs)
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
|
||||
struct response
|
||||
{
|
||||
std::vector<outs_for_amount> outs;
|
||||
std::string status;
|
||||
bool untrusted;
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(outs)
|
||||
KV_SERIALIZE(status)
|
||||
KV_SERIALIZE(untrusted)
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
};
|
||||
//-----------------------------------------------
|
||||
struct get_outputs_out
|
||||
{
|
||||
uint64_t amount;
|
||||
|
@ -818,39 +774,6 @@ namespace cryptonote
|
|||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
};
|
||||
|
||||
struct COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS
|
||||
{
|
||||
struct request
|
||||
{
|
||||
uint64_t outs_count;
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(outs_count)
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
|
||||
#pragma pack (push, 1)
|
||||
struct out_entry
|
||||
{
|
||||
uint64_t amount;
|
||||
uint64_t global_amount_index;
|
||||
crypto::public_key out_key;
|
||||
rct::key commitment;
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
struct response
|
||||
{
|
||||
std::list<out_entry> outs;
|
||||
std::string status;
|
||||
bool untrusted;
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE_CONTAINER_POD_AS_BLOB(outs)
|
||||
KV_SERIALIZE(status)
|
||||
KV_SERIALIZE(untrusted)
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
};
|
||||
//-----------------------------------------------
|
||||
struct COMMAND_RPC_SEND_RAW_TX
|
||||
{
|
||||
|
|
|
@ -260,44 +260,6 @@ namespace rpc
|
|||
|
||||
}
|
||||
|
||||
//TODO: handle "restricted" RPC
|
||||
void DaemonHandler::handle(const GetRandomOutputsForAmounts::Request& req, GetRandomOutputsForAmounts::Response& res)
|
||||
{
|
||||
auto& chain = m_core.get_blockchain_storage();
|
||||
|
||||
try
|
||||
{
|
||||
for (const uint64_t& amount : req.amounts)
|
||||
{
|
||||
std::vector<uint64_t> indices = chain.get_random_outputs(amount, req.count);
|
||||
|
||||
outputs_for_amount ofa;
|
||||
|
||||
ofa.resize(indices.size());
|
||||
|
||||
for (size_t i = 0; i < indices.size(); i++)
|
||||
{
|
||||
crypto::public_key key = chain.get_output_key(amount, indices[i]);
|
||||
ofa[i].amount_index = indices[i];
|
||||
ofa[i].key = key;
|
||||
}
|
||||
|
||||
amount_with_random_outputs amt;
|
||||
amt.amount = amount;
|
||||
amt.outputs = ofa;
|
||||
|
||||
res.amounts_with_outputs.push_back(amt);
|
||||
}
|
||||
|
||||
res.status = Message::STATUS_OK;
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
res.status = Message::STATUS_FAILED;
|
||||
res.error_details = e.what();
|
||||
}
|
||||
}
|
||||
|
||||
void DaemonHandler::handle(const SendRawTx::Request& req, SendRawTx::Response& res)
|
||||
{
|
||||
auto tx_blob = cryptonote::tx_to_blob(req.tx);
|
||||
|
@ -824,7 +786,6 @@ namespace rpc
|
|||
REQ_RESP_TYPES_MACRO(request_type, GetTransactions, req_json, resp_message, handle);
|
||||
REQ_RESP_TYPES_MACRO(request_type, KeyImagesSpent, req_json, resp_message, handle);
|
||||
REQ_RESP_TYPES_MACRO(request_type, GetTxGlobalOutputIndices, req_json, resp_message, handle);
|
||||
REQ_RESP_TYPES_MACRO(request_type, GetRandomOutputsForAmounts, req_json, resp_message, handle);
|
||||
REQ_RESP_TYPES_MACRO(request_type, SendRawTx, req_json, resp_message, handle);
|
||||
REQ_RESP_TYPES_MACRO(request_type, GetInfo, req_json, resp_message, handle);
|
||||
REQ_RESP_TYPES_MACRO(request_type, StartMining, req_json, resp_message, handle);
|
||||
|
|
|
@ -66,8 +66,6 @@ class DaemonHandler : public RpcHandler
|
|||
|
||||
void handle(const GetTxGlobalOutputIndices::Request& req, GetTxGlobalOutputIndices::Response& res);
|
||||
|
||||
void handle(const GetRandomOutputsForAmounts::Request& req, GetRandomOutputsForAmounts::Response& res);
|
||||
|
||||
void handle(const SendRawTx::Request& req, SendRawTx::Response& res);
|
||||
|
||||
void handle(const StartMining::Request& req, StartMining::Response& res);
|
||||
|
|
|
@ -41,7 +41,6 @@ const char* const GetHashesFast::name = "get_hashes_fast";
|
|||
const char* const GetTransactions::name = "get_transactions";
|
||||
const char* const KeyImagesSpent::name = "key_images_spent";
|
||||
const char* const GetTxGlobalOutputIndices::name = "get_tx_global_output_indices";
|
||||
const char* const GetRandomOutputsForAmounts::name = "get_random_outputs_for_amounts";
|
||||
const char* const SendRawTx::name = "send_raw_tx";
|
||||
const char* const StartMining::name = "start_mining";
|
||||
const char* const StopMining::name = "stop_mining";
|
||||
|
@ -273,42 +272,6 @@ void GetTxGlobalOutputIndices::Response::fromJson(rapidjson::Value& val)
|
|||
GET_FROM_JSON_OBJECT(val, output_indices, output_indices);
|
||||
}
|
||||
|
||||
|
||||
rapidjson::Value GetRandomOutputsForAmounts::Request::toJson(rapidjson::Document& doc) const
|
||||
{
|
||||
auto val = Message::toJson(doc);
|
||||
|
||||
auto& al = doc.GetAllocator();
|
||||
|
||||
INSERT_INTO_JSON_OBJECT(val, doc, amounts, amounts);
|
||||
INSERT_INTO_JSON_OBJECT(val, doc, count, count);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
void GetRandomOutputsForAmounts::Request::fromJson(rapidjson::Value& val)
|
||||
{
|
||||
GET_FROM_JSON_OBJECT(val, amounts, amounts);
|
||||
GET_FROM_JSON_OBJECT(val, count, count);
|
||||
}
|
||||
|
||||
rapidjson::Value GetRandomOutputsForAmounts::Response::toJson(rapidjson::Document& doc) const
|
||||
{
|
||||
auto val = Message::toJson(doc);
|
||||
|
||||
auto& al = doc.GetAllocator();
|
||||
|
||||
INSERT_INTO_JSON_OBJECT(val, doc, amounts_with_outputs, amounts_with_outputs);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
void GetRandomOutputsForAmounts::Response::fromJson(rapidjson::Value& val)
|
||||
{
|
||||
GET_FROM_JSON_OBJECT(val, amounts_with_outputs, amounts_with_outputs);
|
||||
}
|
||||
|
||||
|
||||
rapidjson::Value SendRawTx::Request::toJson(rapidjson::Document& doc) const
|
||||
{
|
||||
auto val = Message::toJson(doc);
|
||||
|
|
|
@ -445,7 +445,7 @@ namespace
|
|||
LOG_ERROR("RPC error: " << e.to_string());
|
||||
fail_msg_writer() << tr("RPC error: ") << e.what();
|
||||
}
|
||||
catch (const tools::error::get_random_outs_error &e)
|
||||
catch (const tools::error::get_outs_error &e)
|
||||
{
|
||||
fail_msg_writer() << tr("failed to get random outputs to mix: ") << e.what();
|
||||
}
|
||||
|
@ -4921,15 +4921,12 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri
|
|||
unlock_block = bc_height + locked_blocks;
|
||||
ptx_vector = m_wallet->create_transactions_2(dsts, fake_outs_count, unlock_block /* unlock_time */, priority, extra, m_current_subaddress_account, subaddr_indices);
|
||||
break;
|
||||
default:
|
||||
LOG_ERROR("Unknown transfer method, using default");
|
||||
/* FALLTHRU */
|
||||
case TransferNew:
|
||||
ptx_vector = m_wallet->create_transactions_2(dsts, fake_outs_count, 0 /* unlock_time */, priority, extra, m_current_subaddress_account, subaddr_indices);
|
||||
break;
|
||||
default:
|
||||
LOG_ERROR("Unknown transfer method, using original");
|
||||
/* FALLTHRU */
|
||||
case TransferOriginal:
|
||||
ptx_vector = m_wallet->create_transactions(dsts, fake_outs_count, 0 /* unlock_time */, priority, extra);
|
||||
break;
|
||||
}
|
||||
|
||||
if (ptx_vector.empty())
|
||||
|
|
|
@ -1381,8 +1381,8 @@ PendingTransaction *WalletImpl::createTransaction(const string &dst_addr, const
|
|||
setStatusError(tr("no connection to daemon. Please make sure daemon is running."));
|
||||
} catch (const tools::error::wallet_rpc_error& e) {
|
||||
setStatusError(tr("RPC error: ") + e.to_string());
|
||||
} catch (const tools::error::get_random_outs_error &e) {
|
||||
setStatusError((boost::format(tr("failed to get random outputs to mix: %s")) % e.what()).str());
|
||||
} catch (const tools::error::get_outs_error &e) {
|
||||
setStatusError((boost::format(tr("failed to get outputs to mix: %s")) % e.what()).str());
|
||||
} catch (const tools::error::not_enough_unlocked_money& e) {
|
||||
std::ostringstream writer;
|
||||
|
||||
|
@ -1463,8 +1463,8 @@ PendingTransaction *WalletImpl::createSweepUnmixableTransaction()
|
|||
setStatusError(tr("no connection to daemon. Please make sure daemon is running."));
|
||||
} catch (const tools::error::wallet_rpc_error& e) {
|
||||
setStatusError(tr("RPC error: ") + e.to_string());
|
||||
} catch (const tools::error::get_random_outs_error&) {
|
||||
setStatusError(tr("failed to get random outputs to mix"));
|
||||
} catch (const tools::error::get_outs_error&) {
|
||||
setStatusError(tr("failed to get outputs to mix"));
|
||||
} catch (const tools::error::not_enough_unlocked_money& e) {
|
||||
setStatusError("");
|
||||
std::ostringstream writer;
|
||||
|
|
|
@ -4996,69 +4996,6 @@ void wallet2::add_unconfirmed_tx(const cryptonote::transaction& tx, uint64_t amo
|
|||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
void wallet2::transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, const size_t fake_outs_count, const std::vector<size_t> &unused_transfers_indices,
|
||||
uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, cryptonote::transaction& tx, pending_tx& ptx)
|
||||
{
|
||||
transfer(dsts, fake_outs_count, unused_transfers_indices, unlock_time, fee, extra, detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), tx, ptx);
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
void wallet2::transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, const size_t fake_outs_count, const std::vector<size_t> &unused_transfers_indices,
|
||||
uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra)
|
||||
{
|
||||
cryptonote::transaction tx;
|
||||
pending_tx ptx;
|
||||
transfer(dsts, fake_outs_count, unused_transfers_indices, unlock_time, fee, extra, tx, ptx);
|
||||
}
|
||||
|
||||
namespace {
|
||||
// split_amounts(vector<cryptonote::tx_destination_entry> dsts, size_t num_splits)
|
||||
//
|
||||
// split amount for each dst in dsts into num_splits parts
|
||||
// and make num_splits new vector<crypt...> instances to hold these new amounts
|
||||
std::vector<std::vector<cryptonote::tx_destination_entry>> split_amounts(
|
||||
std::vector<cryptonote::tx_destination_entry> dsts, size_t num_splits)
|
||||
{
|
||||
std::vector<std::vector<cryptonote::tx_destination_entry>> retVal;
|
||||
|
||||
if (num_splits <= 1)
|
||||
{
|
||||
retVal.push_back(dsts);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
// for each split required
|
||||
for (size_t i=0; i < num_splits; i++)
|
||||
{
|
||||
std::vector<cryptonote::tx_destination_entry> new_dsts;
|
||||
|
||||
// for each destination
|
||||
for (size_t j=0; j < dsts.size(); j++)
|
||||
{
|
||||
cryptonote::tx_destination_entry de;
|
||||
uint64_t amount;
|
||||
|
||||
amount = dsts[j].amount;
|
||||
amount = amount / num_splits;
|
||||
|
||||
// if last split, add remainder
|
||||
if (i + 1 == num_splits)
|
||||
{
|
||||
amount += dsts[j].amount % num_splits;
|
||||
}
|
||||
|
||||
de.addr = dsts[j].addr;
|
||||
de.amount = amount;
|
||||
|
||||
new_dsts.push_back(de);
|
||||
}
|
||||
|
||||
retVal.push_back(new_dsts);
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
} // anonymous namespace
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
crypto::hash wallet2::get_payment_id(const pending_tx &ptx) const
|
||||
{
|
||||
|
@ -6059,116 +5996,6 @@ uint32_t wallet2::adjust_priority(uint32_t priority)
|
|||
return priority;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// separated the call(s) to wallet2::transfer into their own function
|
||||
//
|
||||
// this function will make multiple calls to wallet2::transfer if multiple
|
||||
// transactions will be required
|
||||
std::vector<wallet2::pending_tx> wallet2::create_transactions(std::vector<cryptonote::tx_destination_entry> dsts, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra)
|
||||
{
|
||||
const std::vector<size_t> unused_transfers_indices = select_available_outputs_from_histogram(fake_outs_count + 1, true, true, true);
|
||||
|
||||
const uint64_t base_fee = get_base_fee();
|
||||
const uint64_t fee_multiplier = get_fee_multiplier(priority, get_fee_algorithm());
|
||||
const bool use_per_byte_fee = use_fork_rules(HF_VERSION_PER_BYTE_FEE);
|
||||
const uint64_t fee_quantization_mask = get_fee_quantization_mask();
|
||||
|
||||
// failsafe split attempt counter
|
||||
size_t attempt_count = 0;
|
||||
|
||||
for(attempt_count = 1; ;attempt_count++)
|
||||
{
|
||||
size_t num_tx = 0.5 + pow(1.7,attempt_count-1);
|
||||
|
||||
auto split_values = split_amounts(dsts, num_tx);
|
||||
|
||||
// Throw if split_amounts comes back with a vector of size different than it should
|
||||
if (split_values.size() != num_tx)
|
||||
{
|
||||
throw std::runtime_error("Splitting transactions returned a number of potential tx not equal to what was requested");
|
||||
}
|
||||
|
||||
std::vector<pending_tx> ptx_vector;
|
||||
try
|
||||
{
|
||||
// for each new destination vector (i.e. for each new tx)
|
||||
for (auto & dst_vector : split_values)
|
||||
{
|
||||
cryptonote::transaction tx;
|
||||
pending_tx ptx;
|
||||
|
||||
// loop until fee is met without increasing tx size to next KB boundary.
|
||||
uint64_t needed_fee = estimate_fee(use_per_byte_fee, false, unused_transfers_indices.size(), fake_outs_count, dst_vector.size()+1, extra.size(), false, base_fee, fee_multiplier, fee_quantization_mask);
|
||||
do
|
||||
{
|
||||
transfer(dst_vector, fake_outs_count, unused_transfers_indices, unlock_time, needed_fee, extra, tx, ptx);
|
||||
auto txBlob = t_serializable_object_to_blob(ptx.tx);
|
||||
needed_fee = calculate_fee(use_per_byte_fee, ptx.tx, txBlob.size(), base_fee, fee_multiplier, fee_quantization_mask);
|
||||
} while (ptx.fee < needed_fee);
|
||||
|
||||
ptx_vector.push_back(ptx);
|
||||
|
||||
// mark transfers to be used as "spent"
|
||||
for(size_t idx: ptx.selected_transfers)
|
||||
{
|
||||
set_spent(idx, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// if we made it this far, we've selected our transactions. committing them will mark them spent,
|
||||
// so this is a failsafe in case they don't go through
|
||||
// unmark pending tx transfers as spent
|
||||
for (auto & ptx : ptx_vector)
|
||||
{
|
||||
// mark transfers to be used as not spent
|
||||
for(size_t idx2: ptx.selected_transfers)
|
||||
{
|
||||
set_unspent(idx2);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// if we made it this far, we're OK to actually send the transactions
|
||||
return ptx_vector;
|
||||
|
||||
}
|
||||
// only catch this here, other exceptions need to pass through to the calling function
|
||||
catch (const tools::error::tx_too_big& e)
|
||||
{
|
||||
|
||||
// unmark pending tx transfers as spent
|
||||
for (auto & ptx : ptx_vector)
|
||||
{
|
||||
// mark transfers to be used as not spent
|
||||
for(size_t idx2: ptx.selected_transfers)
|
||||
{
|
||||
set_unspent(idx2);
|
||||
}
|
||||
}
|
||||
|
||||
if (attempt_count >= MAX_SPLIT_ATTEMPTS)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// in case of some other exception, make sure any tx in queue are marked unspent again
|
||||
|
||||
// unmark pending tx transfers as spent
|
||||
for (auto & ptx : ptx_vector)
|
||||
{
|
||||
// mark transfers to be used as not spent
|
||||
for(size_t idx2: ptx.selected_transfers)
|
||||
{
|
||||
set_unspent(idx2);
|
||||
}
|
||||
}
|
||||
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool wallet2::set_ring_database(const std::string &filename)
|
||||
{
|
||||
m_ring_database = filename;
|
||||
|
@ -6939,7 +6766,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
|
|||
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.status != CORE_RPC_STATUS_OK, error::get_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()));
|
||||
|
|
|
@ -713,12 +713,6 @@ namespace tools
|
|||
uint64_t balance_all() const;
|
||||
uint64_t unlocked_balance_all() const;
|
||||
template<typename T>
|
||||
void transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, const size_t fake_outputs_count, const std::vector<size_t> &unused_transfers_indices, uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, T destination_split_strategy, const tx_dust_policy& dust_policy);
|
||||
template<typename T>
|
||||
void transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, const size_t fake_outputs_count, const std::vector<size_t> &unused_transfers_indices, uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, T destination_split_strategy, const tx_dust_policy& dust_policy, cryptonote::transaction& tx, pending_tx& ptx);
|
||||
void transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, const size_t fake_outputs_count, const std::vector<size_t> &unused_transfers_indices, uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra);
|
||||
void transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, const size_t fake_outputs_count, const std::vector<size_t> &unused_transfers_indices, uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, cryptonote::transaction& tx, pending_tx& ptx);
|
||||
template<typename T>
|
||||
void transfer_selected(const std::vector<cryptonote::tx_destination_entry>& dsts, const std::vector<size_t>& selected_transfers, size_t fake_outputs_count,
|
||||
std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs,
|
||||
uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, T destination_split_strategy, const tx_dust_policy& dust_policy, cryptonote::transaction& tx, pending_tx &ptx);
|
||||
|
@ -746,7 +740,6 @@ namespace tools
|
|||
bool parse_unsigned_tx_from_str(const std::string &unsigned_tx_st, unsigned_tx_set &exported_txs) const;
|
||||
bool load_tx(const std::string &signed_filename, std::vector<tools::wallet2::pending_tx> &ptx, std::function<bool(const signed_tx_set&)> accept_func = NULL);
|
||||
bool parse_tx_from_str(const std::string &signed_tx_st, std::vector<tools::wallet2::pending_tx> &ptx, std::function<bool(const signed_tx_set &)> accept_func);
|
||||
std::vector<pending_tx> create_transactions(std::vector<cryptonote::tx_destination_entry> dsts, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra);
|
||||
std::vector<wallet2::pending_tx> create_transactions_2(std::vector<cryptonote::tx_destination_entry> dsts, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices); // pass subaddr_indices by value on purpose
|
||||
std::vector<wallet2::pending_tx> create_transactions_all(uint64_t below, const cryptonote::account_public_address &address, bool is_subaddress, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices);
|
||||
std::vector<wallet2::pending_tx> create_transactions_single(const crypto::key_image &ki, const cryptonote::account_public_address &address, bool is_subaddress, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra);
|
||||
|
@ -1817,198 +1810,4 @@ namespace tools
|
|||
//----------------------------------------------------------------------------------------------------
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
template<typename T>
|
||||
void wallet2::transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, const size_t fake_outs_count, const std::vector<size_t> &unused_transfers_indices,
|
||||
uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, T destination_split_strategy, const tx_dust_policy& dust_policy)
|
||||
{
|
||||
pending_tx ptx;
|
||||
cryptonote::transaction tx;
|
||||
transfer(dsts, fake_outs_count, unused_transfers_indices, unlock_time, fee, extra, destination_split_strategy, dust_policy, tx, ptx);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void wallet2::transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, const size_t fake_outputs_count, const std::vector<size_t> &unused_transfers_indices,
|
||||
uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, T destination_split_strategy, const tx_dust_policy& dust_policy, cryptonote::transaction& tx, pending_tx &ptx)
|
||||
{
|
||||
using namespace cryptonote;
|
||||
// throw if attempting a transaction with no destinations
|
||||
THROW_WALLET_EXCEPTION_IF(dsts.empty(), error::zero_destination);
|
||||
|
||||
THROW_WALLET_EXCEPTION_IF(m_multisig, error::wallet_internal_error, "Multisig wallets cannot spend non rct outputs");
|
||||
|
||||
uint64_t upper_transaction_weight_limit = get_upper_transaction_weight_limit();
|
||||
uint64_t needed_money = fee;
|
||||
|
||||
// calculate total amount being sent to all destinations
|
||||
// throw if total amount overflows uint64_t
|
||||
for(auto& dt: dsts)
|
||||
{
|
||||
THROW_WALLET_EXCEPTION_IF(0 == dt.amount, error::zero_destination);
|
||||
needed_money += dt.amount;
|
||||
THROW_WALLET_EXCEPTION_IF(needed_money < dt.amount, error::tx_sum_overflow, dsts, fee, m_nettype);
|
||||
}
|
||||
|
||||
// randomly select inputs for transaction
|
||||
// throw if requested send amount is greater than (unlocked) amount available to send
|
||||
std::vector<size_t> selected_transfers;
|
||||
uint64_t found_money = select_transfers(needed_money, unused_transfers_indices, selected_transfers);
|
||||
THROW_WALLET_EXCEPTION_IF(found_money < needed_money, error::not_enough_unlocked_money, found_money, needed_money - fee, fee);
|
||||
|
||||
uint32_t subaddr_account = m_transfers[*selected_transfers.begin()].m_subaddr_index.major;
|
||||
for (auto i = ++selected_transfers.begin(); i != selected_transfers.end(); ++i)
|
||||
THROW_WALLET_EXCEPTION_IF(subaddr_account != *i, error::wallet_internal_error, "the tx uses funds from multiple accounts");
|
||||
|
||||
typedef COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry out_entry;
|
||||
typedef cryptonote::tx_source_entry::output_entry tx_output_entry;
|
||||
|
||||
COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response daemon_resp = AUTO_VAL_INIT(daemon_resp);
|
||||
if(fake_outputs_count)
|
||||
{
|
||||
COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request req = AUTO_VAL_INIT(req);
|
||||
req.outs_count = fake_outputs_count + 1;// add one to make possible (if need) to skip real output key
|
||||
for(size_t idx: selected_transfers)
|
||||
{
|
||||
const transfer_container::const_iterator it = m_transfers.begin() + idx;
|
||||
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()));
|
||||
req.amounts.push_back(it->amount());
|
||||
}
|
||||
|
||||
m_daemon_rpc_mutex.lock();
|
||||
bool r = epee::net_utils::invoke_http_bin("/getrandom_outs.bin", req, daemon_resp, m_http_client, rpc_timeout);
|
||||
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() != selected_transfers.size(), 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(selected_transfers.size()));
|
||||
|
||||
std::unordered_map<uint64_t, uint64_t> scanty_outs;
|
||||
for(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);
|
||||
}
|
||||
|
||||
//prepare inputs
|
||||
size_t i = 0;
|
||||
std::vector<cryptonote::tx_source_entry> sources;
|
||||
for(size_t idx: selected_transfers)
|
||||
{
|
||||
sources.resize(sources.size()+1);
|
||||
cryptonote::tx_source_entry& src = sources.back();
|
||||
const transfer_details& td = m_transfers[idx];
|
||||
src.amount = td.amount();
|
||||
src.rct = false;
|
||||
//paste mixin transaction
|
||||
if(daemon_resp.outs.size())
|
||||
{
|
||||
daemon_resp.outs[i].outs.sort([](const out_entry& a, const out_entry& b){return a.global_amount_index < b.global_amount_index;});
|
||||
for(out_entry& daemon_oe: daemon_resp.outs[i].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::identity();
|
||||
src.outputs.push_back(oe);
|
||||
if(src.outputs.size() >= fake_outputs_count)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//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)
|
||||
{
|
||||
return a.first >= td.m_global_output_index;
|
||||
});
|
||||
//size_t real_index = src.outputs.size() ? (rand() % src.outputs.size() ):0;
|
||||
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.mask = rct::identity();
|
||||
auto interted_it = src.outputs.insert(it_to_insert, 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_in_tx_index = td.m_internal_output_index;
|
||||
src.multisig_kLRki = rct::multisig_kLRki({rct::zero(), rct::zero(), rct::zero(), rct::zero()});
|
||||
detail::print_source_entry(src);
|
||||
++i;
|
||||
}
|
||||
|
||||
cryptonote::tx_destination_entry change_dts = AUTO_VAL_INIT(change_dts);
|
||||
if (needed_money < found_money)
|
||||
{
|
||||
change_dts.addr = get_subaddress({subaddr_account, 0});
|
||||
change_dts.amount = found_money - needed_money;
|
||||
}
|
||||
|
||||
std::vector<cryptonote::tx_destination_entry> splitted_dsts, dust_dsts;
|
||||
uint64_t dust = 0;
|
||||
destination_split_strategy(dsts, change_dts, dust_policy.dust_threshold, splitted_dsts, dust_dsts);
|
||||
for(auto& d: dust_dsts) {
|
||||
THROW_WALLET_EXCEPTION_IF(dust_policy.dust_threshold < d.amount, error::wallet_internal_error, "invalid dust value: dust = " +
|
||||
std::to_string(d.amount) + ", dust_threshold = " + std::to_string(dust_policy.dust_threshold));
|
||||
}
|
||||
for(auto& d: dust_dsts) {
|
||||
if (!dust_policy.add_to_fee)
|
||||
splitted_dsts.push_back(cryptonote::tx_destination_entry(d.amount, dust_policy.addr_for_dust, d.is_subaddress));
|
||||
dust += d.amount;
|
||||
}
|
||||
|
||||
crypto::secret_key tx_key;
|
||||
std::vector<crypto::secret_key> additional_tx_keys;
|
||||
rct::multisig_out msout;
|
||||
bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), m_subaddresses, sources, splitted_dsts, change_dts.addr, extra, tx, unlock_time, tx_key, additional_tx_keys, false, rct::RangeProofBorromean, m_multisig ? &msout : NULL);
|
||||
THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sources, splitted_dsts, unlock_time, m_nettype);
|
||||
THROW_WALLET_EXCEPTION_IF(upper_transaction_weight_limit <= get_transaction_weight(tx), error::tx_too_big, tx, upper_transaction_weight_limit);
|
||||
|
||||
std::string key_images;
|
||||
bool all_are_txin_to_key = std::all_of(tx.vin.begin(), 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, tx);
|
||||
|
||||
bool dust_sent_elsewhere = (dust_policy.addr_for_dust.m_view_public_key != change_dts.addr.m_view_public_key
|
||||
|| dust_policy.addr_for_dust.m_spend_public_key != change_dts.addr.m_spend_public_key);
|
||||
|
||||
if (dust_policy.add_to_fee || dust_sent_elsewhere) change_dts.amount -= dust;
|
||||
|
||||
ptx.key_images = key_images;
|
||||
ptx.fee = (dust_policy.add_to_fee ? fee+dust : fee);
|
||||
ptx.dust = ((dust_policy.add_to_fee || dust_sent_elsewhere) ? dust : 0);
|
||||
ptx.dust_added_to_fee = dust_policy.add_to_fee;
|
||||
ptx.tx = tx;
|
||||
ptx.change_dts = change_dts;
|
||||
ptx.selected_transfers = selected_transfers;
|
||||
ptx.tx_key = tx_key;
|
||||
ptx.additional_tx_keys = additional_tx_keys;
|
||||
ptx.dests = dsts;
|
||||
ptx.construction_data.sources = sources;
|
||||
ptx.construction_data.change_dts = change_dts;
|
||||
ptx.construction_data.splitted_dsts = splitted_dsts;
|
||||
ptx.construction_data.selected_transfers = selected_transfers;
|
||||
ptx.construction_data.extra = tx.extra;
|
||||
ptx.construction_data.unlock_time = unlock_time;
|
||||
ptx.construction_data.use_rct = false;
|
||||
ptx.construction_data.use_bulletproofs = false;
|
||||
ptx.construction_data.dests = dsts;
|
||||
// record which subaddress indices are being used as inputs
|
||||
ptx.construction_data.subaddr_account = subaddr_account;
|
||||
ptx.construction_data.subaddr_indices.clear();
|
||||
for (size_t idx: selected_transfers)
|
||||
ptx.construction_data.subaddr_indices.insert(m_transfers[idx].m_subaddr_index.minor);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -73,7 +73,7 @@ namespace tools
|
|||
// get_tx_pool_error
|
||||
// out_of_hashchain_bounds_error
|
||||
// transfer_error *
|
||||
// get_random_outs_general_error
|
||||
// get_outs_general_error
|
||||
// not_enough_unlocked_money
|
||||
// not_enough_money
|
||||
// tx_not_possible
|
||||
|
@ -128,7 +128,7 @@ namespace tools
|
|||
get_blocks_error_message_index,
|
||||
get_hashes_error_message_index,
|
||||
get_out_indices_error_message_index,
|
||||
get_random_outs_error_message_index
|
||||
get_outs_error_message_index
|
||||
};
|
||||
|
||||
template<typename Base, int msg_index>
|
||||
|
@ -427,7 +427,7 @@ namespace tools
|
|||
}
|
||||
};
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
typedef failed_rpc_request<transfer_error, get_random_outs_error_message_index> get_random_outs_error;
|
||||
typedef failed_rpc_request<transfer_error, get_outs_error_message_index> get_outs_error;
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
struct not_enough_unlocked_money : public transfer_error
|
||||
{
|
||||
|
|
|
@ -84,10 +84,10 @@ bool do_send_money(tools::wallet2& w1, tools::wallet2& w2, size_t mix_in_factor,
|
|||
|
||||
try
|
||||
{
|
||||
tools::wallet2::pending_tx ptx;
|
||||
std::vector<size_t> indices = w1.select_available_outputs([](const tools::wallet2::transfer_details&) { return true; });
|
||||
w1.transfer(dsts, mix_in_factor, indices, 0, TEST_FEE, std::vector<uint8_t>(), tools::detail::null_split_strategy, tools::tx_dust_policy(TEST_DUST_THRESHOLD), tx, ptx);
|
||||
w1.commit_tx(ptx);
|
||||
std::vector<tools::wallet2::pending_tx> ptx;
|
||||
ptx = w1.create_transactions_2(dsts, mix_in_factor, 0, 0, std::vector<uint8_t>(), 0, {});
|
||||
for (auto &p: ptx)
|
||||
w1.commit_tx(p);
|
||||
return true;
|
||||
}
|
||||
catch (const std::exception&)
|
||||
|
@ -167,8 +167,8 @@ bool transactions_flow_test(std::string& working_folder,
|
|||
daemon_req.miner_address = w1.get_account().get_public_address_str(MAINNET);
|
||||
daemon_req.threads_count = 9;
|
||||
r = net_utils::invoke_http_json("/start_mining", daemon_req, daemon_rsp, http_client, std::chrono::seconds(10));
|
||||
CHECK_AND_ASSERT_MES(r, false, "failed to get getrandom_outs");
|
||||
CHECK_AND_ASSERT_MES(daemon_rsp.status == CORE_RPC_STATUS_OK, false, "failed to getrandom_outs.bin");
|
||||
CHECK_AND_ASSERT_MES(r, false, "failed to start mining getrandom_outs");
|
||||
CHECK_AND_ASSERT_MES(daemon_rsp.status == CORE_RPC_STATUS_OK, false, "failed to start mining");
|
||||
|
||||
//wait for money, until balance will have enough money
|
||||
w1.refresh(true, blocks_fetched, received_money, ok);
|
||||
|
|
Loading…
Reference in a new issue