carrot_impl: input selection overhaul
* Split subsetting into `form_preferred_input_candidate_subsets` fuction * Use `get_input_counts_in_preferred_order` directly from `make_single_transfer_input_selector` * Input selection policies are passed number of inputs to select for, instead of dynamic * Filter dust before calling policies * Throw more descriptive errors when not enough money (could still be improved for not enough input case) * Replace multiprecision::int128_t with multiprecision::uint128_t for simplicity and cryptonote::print_money * More documentation * Remove "select two, prefer oldest" policy for now
This commit is contained in:
@@ -30,6 +30,7 @@
|
||||
|
||||
#include <boost/multiprecision/cpp_int.hpp>
|
||||
|
||||
#include "carrot_core/exceptions.h"
|
||||
#include "carrot_core/output_set_finalization.h"
|
||||
#include "carrot_core/payment_proposal.h"
|
||||
#include "carrot_impl/carrot_tx_builder_utils.h"
|
||||
@@ -109,7 +110,7 @@ struct unittest_transaction_preproposal
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
select_inputs_func_t make_fake_input_selection_callback(size_t num_ins = 0)
|
||||
{
|
||||
return [num_ins](const boost::multiprecision::int128_t &nominal_output_sum,
|
||||
return [num_ins](const boost::multiprecision::uint128_t &nominal_output_sum,
|
||||
const std::map<std::size_t, rct::xmr_amount> &fee_per_input_count,
|
||||
size_t,
|
||||
size_t,
|
||||
@@ -1164,28 +1165,14 @@ TEST(carrot_impl, multi_account_transfer_over_transaction_16)
|
||||
subtest_multi_account_transfer_over_transaction(tx_proposal);
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
TEST(carrot_impl, make_single_transfer_input_selector_TwoInputsPreferOldest_1)
|
||||
TEST(carrot_impl, make_single_transfer_input_selector_not_enough_money_1)
|
||||
{
|
||||
// no input candidates, should throw `not_enough_money`
|
||||
|
||||
const std::vector<CarrotPreSelectedInput> input_candidates = {
|
||||
CarrotPreSelectedInput {
|
||||
.core = CarrotSelectedInput {
|
||||
.amount = 500,
|
||||
.key_image = mock::gen_key_image(),
|
||||
},
|
||||
.is_external = false,
|
||||
.block_index = 72
|
||||
},
|
||||
CarrotPreSelectedInput {
|
||||
.core = CarrotSelectedInput {
|
||||
.amount = 200,
|
||||
.key_image = mock::gen_key_image(),
|
||||
},
|
||||
.is_external = false,
|
||||
.block_index = 34
|
||||
}
|
||||
};
|
||||
|
||||
const std::vector<input_selection_policy_t> policies = { &carrot::ispolicy::select_two_inputs_prefer_oldest };
|
||||
const std::vector<input_selection_policy_t> policies = { input_selection_policy_t{} };
|
||||
|
||||
const uint32_t flags = 0;
|
||||
|
||||
@@ -1195,7 +1182,7 @@ TEST(carrot_impl, make_single_transfer_input_selector_TwoInputsPreferOldest_1)
|
||||
flags,
|
||||
&selected_input_indices);
|
||||
|
||||
boost::multiprecision::int128_t nominal_output_sum = 369;
|
||||
const boost::multiprecision::uint128_t nominal_output_sum = 369;
|
||||
|
||||
const std::map<size_t, rct::xmr_amount> fee_by_input_count = {
|
||||
{1, 50},
|
||||
@@ -1205,21 +1192,119 @@ TEST(carrot_impl, make_single_transfer_input_selector_TwoInputsPreferOldest_1)
|
||||
const size_t num_normal_payment_proposals = 1;
|
||||
const size_t num_selfsend_payment_proposals = 1;
|
||||
|
||||
ASSERT_GT(input_candidates[0].core.amount, nominal_output_sum + fee_by_input_count.crbegin()->second);
|
||||
std::vector<CarrotSelectedInput> selected_inputs;
|
||||
EXPECT_THROW(input_selector(nominal_output_sum,
|
||||
fee_by_input_count,
|
||||
num_normal_payment_proposals,
|
||||
num_selfsend_payment_proposals,
|
||||
selected_inputs),
|
||||
not_enough_money);
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
TEST(carrot_impl, make_single_transfer_input_selector_not_enough_money_2)
|
||||
{
|
||||
// 1 input candidates w/ strictly less than nominal output sum, should throw `not_enough_money`
|
||||
|
||||
const std::vector<CarrotPreSelectedInput> input_candidates = {
|
||||
CarrotPreSelectedInput {
|
||||
.core = CarrotSelectedInput {
|
||||
.amount = 222,
|
||||
.key_image = mock::gen_key_image(),
|
||||
},
|
||||
.is_external = false,
|
||||
.block_index = 23
|
||||
},
|
||||
};
|
||||
|
||||
const std::vector<input_selection_policy_t> policies = { input_selection_policy_t{} };
|
||||
|
||||
const uint32_t flags = 0;
|
||||
|
||||
std::set<size_t> selected_input_indices;
|
||||
select_inputs_func_t input_selector = make_single_transfer_input_selector(epee::to_span(input_candidates),
|
||||
epee::to_span(policies),
|
||||
flags,
|
||||
&selected_input_indices);
|
||||
|
||||
const boost::multiprecision::uint128_t nominal_output_sum = 369;
|
||||
|
||||
const std::map<size_t, rct::xmr_amount> fee_by_input_count = {
|
||||
{1, 50},
|
||||
{2, 75}
|
||||
};
|
||||
|
||||
const size_t num_normal_payment_proposals = 1;
|
||||
const size_t num_selfsend_payment_proposals = 1;
|
||||
|
||||
std::vector<CarrotSelectedInput> selected_inputs;
|
||||
input_selector(nominal_output_sum,
|
||||
fee_by_input_count,
|
||||
num_normal_payment_proposals,
|
||||
num_selfsend_payment_proposals,
|
||||
selected_inputs);
|
||||
EXPECT_THROW(input_selector(nominal_output_sum,
|
||||
fee_by_input_count,
|
||||
num_normal_payment_proposals,
|
||||
num_selfsend_payment_proposals,
|
||||
selected_inputs),
|
||||
not_enough_money);
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
TEST(carrot_impl, make_single_transfer_input_selector_not_enough_money_3)
|
||||
{
|
||||
// 2 input candidates who sum is strictly greater than the required money for a 1-in tx, but
|
||||
// strictly less than the required money for a 2-out tx, should throw `not_enough_usable_money`
|
||||
|
||||
ASSERT_EQ(2, input_candidates.size());
|
||||
ASSERT_EQ(2, selected_inputs.size());
|
||||
EXPECT_NE(input_candidates.at(0).core, input_candidates.at(1).core);
|
||||
EXPECT_NE(selected_inputs.at(0), selected_inputs.at(1));
|
||||
EXPECT_TRUE((selected_inputs.at(0) == input_candidates.at(0).core) ^ (selected_inputs.at(0) == input_candidates[1].core));
|
||||
EXPECT_TRUE((selected_inputs.at(1) == input_candidates.at(0).core) ^ (selected_inputs.at(1) == input_candidates.at(1).core));
|
||||
const rct::xmr_amount nominal_output_sum = 369;
|
||||
|
||||
const std::map<size_t, rct::xmr_amount> fee_by_input_count = {
|
||||
{1, 50},
|
||||
{2, 75}
|
||||
};
|
||||
|
||||
const rct::xmr_amount required_1in = fee_by_input_count.at(1) + nominal_output_sum;
|
||||
const rct::xmr_amount required_2in = fee_by_input_count.at(2) + nominal_output_sum;
|
||||
ASSERT_GT(required_1in, 0);
|
||||
ASSERT_GT(required_2in, required_1in + 1);
|
||||
|
||||
const rct::xmr_amount input_sum_target = (required_2in + required_1in) / 2;
|
||||
const rct::xmr_amount inamount_0 = rct::randXmrAmount(required_1in);
|
||||
const rct::xmr_amount inamount_1 = input_sum_target - inamount_0;
|
||||
|
||||
const std::vector<CarrotPreSelectedInput> input_candidates = {
|
||||
CarrotPreSelectedInput {
|
||||
.core = CarrotSelectedInput {
|
||||
.amount = inamount_0,
|
||||
.key_image = mock::gen_key_image(),
|
||||
},
|
||||
.is_external = false,
|
||||
.block_index = 3407684
|
||||
},
|
||||
CarrotPreSelectedInput {
|
||||
.core = CarrotSelectedInput {
|
||||
.amount = inamount_1,
|
||||
.key_image = mock::gen_key_image(),
|
||||
},
|
||||
.is_external = false,
|
||||
.block_index = 4867043
|
||||
},
|
||||
};
|
||||
|
||||
const std::vector<input_selection_policy_t> policies = { input_selection_policy_t{} };
|
||||
|
||||
const uint32_t flags = 0;
|
||||
|
||||
std::set<size_t> selected_input_indices;
|
||||
select_inputs_func_t input_selector = make_single_transfer_input_selector(epee::to_span(input_candidates),
|
||||
epee::to_span(policies),
|
||||
flags,
|
||||
&selected_input_indices);
|
||||
|
||||
const size_t num_normal_payment_proposals = 1;
|
||||
const size_t num_selfsend_payment_proposals = 1;
|
||||
|
||||
std::vector<CarrotSelectedInput> selected_inputs;
|
||||
EXPECT_THROW(input_selector(nominal_output_sum,
|
||||
fee_by_input_count,
|
||||
num_normal_payment_proposals,
|
||||
num_selfsend_payment_proposals,
|
||||
selected_inputs),
|
||||
not_enough_usable_money);
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
TEST(carrot_impl, make_single_transfer_input_selector_greedy_aging_1)
|
||||
@@ -1253,7 +1338,7 @@ TEST(carrot_impl, make_single_transfer_input_selector_greedy_aging_1)
|
||||
flags,
|
||||
&selected_input_indices);
|
||||
|
||||
boost::multiprecision::int128_t nominal_output_sum = 369;
|
||||
const boost::multiprecision::uint128_t nominal_output_sum = 369;
|
||||
|
||||
const std::map<size_t, rct::xmr_amount> fee_by_input_count = {
|
||||
{1, 50},
|
||||
@@ -1312,7 +1397,7 @@ TEST(carrot_impl, make_single_transfer_input_selector_greedy_aging_2)
|
||||
flags,
|
||||
&selected_input_indices);
|
||||
|
||||
boost::multiprecision::int128_t nominal_output_sum = 369;
|
||||
const boost::multiprecision::uint128_t nominal_output_sum = 369;
|
||||
|
||||
const std::map<size_t, rct::xmr_amount> fee_by_input_count = {
|
||||
{1, 50},
|
||||
@@ -1347,6 +1432,7 @@ TEST(carrot_impl, make_single_transfer_input_selector_greedy_aging_3)
|
||||
.amount = 100,
|
||||
.key_image = mock::gen_key_image(),
|
||||
},
|
||||
.is_pre_carrot = false,
|
||||
.is_external = false,
|
||||
.block_index = 55
|
||||
},
|
||||
@@ -1355,6 +1441,7 @@ TEST(carrot_impl, make_single_transfer_input_selector_greedy_aging_3)
|
||||
.amount = 100,
|
||||
.key_image = mock::gen_key_image(),
|
||||
},
|
||||
.is_pre_carrot = false,
|
||||
.is_external = false,
|
||||
.block_index = 22
|
||||
},
|
||||
@@ -1363,6 +1450,7 @@ TEST(carrot_impl, make_single_transfer_input_selector_greedy_aging_3)
|
||||
.amount = 100,
|
||||
.key_image = mock::gen_key_image(),
|
||||
},
|
||||
.is_pre_carrot = false,
|
||||
.is_external = false,
|
||||
.block_index = 11
|
||||
},
|
||||
@@ -1371,6 +1459,7 @@ TEST(carrot_impl, make_single_transfer_input_selector_greedy_aging_3)
|
||||
.amount = 100,
|
||||
.key_image = mock::gen_key_image(),
|
||||
},
|
||||
.is_pre_carrot = false,
|
||||
.is_external = false,
|
||||
.block_index = 88
|
||||
},
|
||||
@@ -1379,6 +1468,7 @@ TEST(carrot_impl, make_single_transfer_input_selector_greedy_aging_3)
|
||||
.amount = 100,
|
||||
.key_image = mock::gen_key_image(),
|
||||
},
|
||||
.is_pre_carrot = false,
|
||||
.is_external = false,
|
||||
.block_index = 72
|
||||
}
|
||||
@@ -1394,7 +1484,7 @@ TEST(carrot_impl, make_single_transfer_input_selector_greedy_aging_3)
|
||||
flags,
|
||||
&selected_input_indices);
|
||||
|
||||
boost::multiprecision::int128_t nominal_output_sum = 223;
|
||||
const boost::multiprecision::uint128_t nominal_output_sum = 223;
|
||||
|
||||
const std::map<size_t, rct::xmr_amount> fee_by_input_count = {
|
||||
{1, 10},
|
||||
|
||||
@@ -38,9 +38,12 @@
|
||||
|
||||
static tools::wallet2::transfer_details gen_transfer_details()
|
||||
{
|
||||
cryptonote::transaction carrot_tx;
|
||||
carrot_tx.vout.push_back(cryptonote::tx_out{.target = cryptonote::txout_to_carrot_v1{}});
|
||||
|
||||
return tools::wallet2::transfer_details{
|
||||
.m_block_height = crypto::rand_idx<uint64_t>(CRYPTONOTE_MAX_BLOCK_NUMBER),
|
||||
.m_tx = {},
|
||||
.m_tx = carrot_tx,
|
||||
.m_txid = crypto::rand<crypto::hash>(),
|
||||
.m_internal_output_index = crypto::rand_idx<uint64_t>(carrot::CARROT_MAX_TX_OUTPUTS),
|
||||
.m_global_output_index = crypto::rand_idx<uint64_t>(CRYPTONOTE_MAX_BLOCK_NUMBER * 1000ull),
|
||||
@@ -74,7 +77,7 @@ TEST(wallet_tx_builder, input_selection_basic)
|
||||
for (size_t i = carrot::CARROT_MIN_TX_INPUTS; i <= carrot::CARROT_MAX_TX_INPUTS; ++i)
|
||||
fee_by_input_count[i] = 30680000 * i - i*i;
|
||||
|
||||
const boost::multiprecision::int128_t nominal_output_sum = 4444444444444; // 4.444... XMR
|
||||
const boost::multiprecision::uint128_t nominal_output_sum = 4444444444444; // 4.444... XMR
|
||||
|
||||
// add 10 random transfers
|
||||
tools::wallet2::transfer_container transfers;
|
||||
|
||||
Reference in New Issue
Block a user