wallet: implement coin splitting for sweep_* 'outputs' option

Implemented strategy splits total amount into N equal parts,
where N is a specified number of outputs. If N > 1, dummy
change output is NOT created.

rebased by moneromooo
This commit is contained in:
whythat 2018-05-06 11:45:04 +03:00 committed by moneromooo-monero
parent 24f5239693
commit 4ed30bab50
No known key found for this signature in database
GPG Key ID: 686F07454D6CEFC3
1 changed files with 25 additions and 3 deletions

View File

@ -8532,7 +8532,9 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
needed_fee = estimate_fee(use_per_byte_fee, use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size()+1, extra.size(), bulletproof, base_fee, fee_multiplier, fee_quantization_mask); needed_fee = estimate_fee(use_per_byte_fee, use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size()+1, extra.size(), bulletproof, base_fee, fee_multiplier, fee_quantization_mask);
tx.dsts.push_back(tx_destination_entry(1, address, is_subaddress)); // add N - 1 outputs for correct initial fee estimation
for (size_t i = 0; i < ((outputs > 1) ? outputs - 1 : outputs); ++i)
tx.dsts.push_back(tx_destination_entry(1, address, is_subaddress));
LOG_PRINT_L2("Trying to create a tx now, with " << tx.dsts.size() << " destinations and " << LOG_PRINT_L2("Trying to create a tx now, with " << tx.dsts.size() << " destinations and " <<
tx.selected_transfers.size() << " outputs"); tx.selected_transfers.size() << " outputs");
@ -8544,15 +8546,35 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx); detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx);
auto txBlob = t_serializable_object_to_blob(test_ptx.tx); auto txBlob = t_serializable_object_to_blob(test_ptx.tx);
needed_fee = calculate_fee(use_per_byte_fee, test_ptx.tx, txBlob.size(), base_fee, fee_multiplier, fee_quantization_mask); needed_fee = calculate_fee(use_per_byte_fee, test_ptx.tx, txBlob.size(), base_fee, fee_multiplier, fee_quantization_mask);
available_for_fee = test_ptx.fee + test_ptx.dests[0].amount + test_ptx.change_dts.amount; available_for_fee = test_ptx.fee + test_ptx.change_dts.amount;
for (auto &dt: test_ptx.dests)
available_for_fee += dt.amount;
LOG_PRINT_L2("Made a " << get_weight_string(test_ptx.tx, txBlob.size()) << " tx, with " << print_money(available_for_fee) << " available for fee (" << LOG_PRINT_L2("Made a " << get_weight_string(test_ptx.tx, txBlob.size()) << " tx, with " << print_money(available_for_fee) << " available for fee (" <<
print_money(needed_fee) << " needed)"); print_money(needed_fee) << " needed)");
// add last output, missed for fee estimation
if (outputs > 1)
tx.dsts.push_back(tx_destination_entry(1, address, is_subaddress));
THROW_WALLET_EXCEPTION_IF(needed_fee > available_for_fee, error::wallet_internal_error, "Transaction cannot pay for itself"); THROW_WALLET_EXCEPTION_IF(needed_fee > available_for_fee, error::wallet_internal_error, "Transaction cannot pay for itself");
do { do {
LOG_PRINT_L2("We made a tx, adjusting fee and saving it"); LOG_PRINT_L2("We made a tx, adjusting fee and saving it");
tx.dsts[0].amount = available_for_fee - needed_fee; // distribute total transferred amount between outputs
uint64_t amount_transferred = available_for_fee - needed_fee;
uint64_t dt_amount = amount_transferred / outputs;
// residue is distributed as one atomic unit per output until it reaches zero
uint64_t residue = amount_transferred % outputs;
for (auto &dt: tx.dsts)
{
uint64_t dt_residue = 0;
if (residue > 0)
{
dt_residue = 1;
residue -= 1;
}
dt.amount = dt_amount + dt_residue;
}
if (use_rct) if (use_rct)
transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra, transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra,
test_tx, test_ptx, range_proof_type); test_tx, test_ptx, range_proof_type);