Skip to content

Commit 85368aa

Browse files
committed
test: Run simple tests at various feerates
1 parent d610951 commit 85368aa

File tree

1 file changed

+74
-66
lines changed

1 file changed

+74
-66
lines changed

src/wallet/test/coinselection_tests.cpp

Lines changed: 74 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,9 @@ static void AddCoins(std::vector<OutputGroup>& utxo_pool, std::vector<CAmount> c
6363
}
6464

6565
/** Make multiple coins that share the same effective value */
66-
static void AddDuplicateCoins(std::vector<OutputGroup>& utxo_pool, int count, int amount) {
66+
static void AddDuplicateCoins(std::vector<OutputGroup>& utxo_pool, int count, int amount, CoinSelectionParams cs_params = default_cs_params) {
6767
for (int i = 0 ; i < count; ++i) {
68-
utxo_pool.push_back(MakeCoin(amount));
68+
utxo_pool.push_back(MakeCoin(amount, true, cs_params));
6969
}
7070
}
7171

@@ -116,72 +116,80 @@ static void TestBnBFail(std::string test_title, std::vector<OutputGroup>& utxo_p
116116

117117
BOOST_AUTO_TEST_CASE(bnb_test)
118118
{
119-
std::vector<OutputGroup> utxo_pool;
120-
121-
// Fail for empty UTXO pool
122-
TestBnBFail("Empty UTXO pool", utxo_pool, /*selection_target=*/1 * CENT);
123-
124-
AddCoins(utxo_pool, {1 * CENT, 3 * CENT, 5 * CENT});
125-
126-
// Simple success cases
127-
TestBnBSuccess("Select smallest UTXO", utxo_pool, /*selection_target=*/1 * CENT, /*expected_input_amounts=*/{1 * CENT});
128-
TestBnBSuccess("Select middle UTXO", utxo_pool, /*selection_target=*/3 * CENT, /*expected_input_amounts=*/{3 * CENT});
129-
TestBnBSuccess("Select biggest UTXO", utxo_pool, /*selection_target=*/5 * CENT, /*expected_input_amounts=*/{5 * CENT});
130-
TestBnBSuccess("Select two UTXOs", utxo_pool, /*selection_target=*/4 * CENT, /*expected_input_amounts=*/{1 * CENT, 3 * CENT});
131-
TestBnBSuccess("Select all UTXOs", utxo_pool, /*selection_target=*/9 * CENT, /*expected_input_amounts=*/{1 * CENT, 3 * CENT, 5 * CENT});
132-
133-
// BnB finds changeless solution while overshooting by up to cost_of_change
134-
TestBnBSuccess("Select upper bound", utxo_pool, /*selection_target=*/4 * CENT - default_cs_params.m_cost_of_change, /*expected_input_amounts=*/{1 * CENT, 3 * CENT});
135-
136-
// BnB fails to find changeless solution when overshooting by cost_of_change + 1 sat
137-
TestBnBFail("Overshoot upper bound", utxo_pool, /*selection_target=*/4 * CENT - default_cs_params.m_cost_of_change - 1);
138-
139-
// Simple cases without BnB solution
140-
TestBnBFail("Smallest combination too big", utxo_pool, /*selection_target=*/0.5 * CENT);
141-
TestBnBFail("No UTXO combination in target window", utxo_pool, /*selection_target=*/7 * CENT);
142-
TestBnBFail("Select more than available", utxo_pool, /*selection_target=*/10 * CENT);
143-
144-
// Test skipping of equivalent input sets
145-
std::vector<OutputGroup> clone_pool;
146-
AddCoins(clone_pool, {2 * CENT, 7 * CENT, 7 * CENT});
147-
AddDuplicateCoins(clone_pool, 50'000, 5 * CENT);
148-
TestBnBSuccess("Skip equivalent input sets", clone_pool, /*selection_target=*/16 * CENT, /*expected_input_amounts=*/{2 * CENT, 7 * CENT, 7 * CENT});
149-
150-
/* Test BnB attempt limit (`TOTAL_TRIES`)
151-
*
152-
* Generally, on a diverse UTXO pool BnB will quickly pass over UTXOs bigger than the target and then start
153-
* combining small counts of UTXOs that in sum remain under the selection_target+cost_of_change. When there are
154-
* multiple UTXOs that have matching amount and cost, combinations with equivalent input sets are skipped. The UTXO
155-
* pool for this test is specifically crafted to create as much branching as possible. The selection target is
156-
* 8 CENT while all UTXOs are slightly bigger than 1 CENT. The smallest eight are 100,000…100,007 sats, while the larger
157-
* nine are 100,368…100,375 (i.e., 100,008…100,016 sats plus cost_of_change (359 sats)).
158-
*
159-
* Because BnB will only select input sets that fall between selection_target and selection_target + cost_of_change,
160-
* and the search traverses the UTXO pool from large to small amounts, the search will visit every single
161-
* combination of eight inputs. All except the last combination will overshoot by more than cost_of_change on the
162-
* eighth input, because the larger nine inputs each exceed 1 CENT by more than cost_of_change. Only the last
163-
* combination consisting of the eight smallest UTXOs falls into the target window.
164-
*/
165-
std::vector<OutputGroup> doppelganger_pool;
166-
std::vector<CAmount> doppelgangers;
167-
std::vector<CAmount> expected_inputs;
168-
for (int i = 0; i < 17; ++i) {
169-
if (i < 8) {
170-
// The eight smallest UTXOs can be combined to create expected_result
171-
doppelgangers.push_back(1 * CENT + i);
172-
expected_inputs.push_back(doppelgangers[i]);
173-
} else {
174-
// Any eight UTXOs including at least one UTXO with the added cost_of_change will exceed target window
175-
doppelgangers.push_back(1 * CENT + default_cs_params.m_cost_of_change + i);
119+
std::vector<int> feerates = {0, 1, 5'000, 10'000, 25'000, 59'764, 500'000, 999'000, 1'500'000};
120+
121+
for (int feerate : feerates) {
122+
std::vector<OutputGroup> utxo_pool;
123+
124+
CoinSelectionParams cs_params = init_default_params();
125+
cs_params.m_effective_feerate = CFeeRate{feerate};
126+
127+
// Fail for empty UTXO pool
128+
TestBnBFail("Empty UTXO pool", utxo_pool, /*selection_target=*/1 * CENT);
129+
130+
AddCoins(utxo_pool, {1 * CENT, 3 * CENT, 5 * CENT}, cs_params);
131+
132+
// Simple success cases
133+
TestBnBSuccess("Select smallest UTXO", utxo_pool, /*selection_target=*/1 * CENT, /*expected_input_amounts=*/{1 * CENT}, cs_params);
134+
TestBnBSuccess("Select middle UTXO", utxo_pool, /*selection_target=*/3 * CENT, /*expected_input_amounts=*/{3 * CENT}, cs_params);
135+
TestBnBSuccess("Select biggest UTXO", utxo_pool, /*selection_target=*/5 * CENT, /*expected_input_amounts=*/{5 * CENT}, cs_params);
136+
TestBnBSuccess("Select two UTXOs", utxo_pool, /*selection_target=*/4 * CENT, /*expected_input_amounts=*/{1 * CENT, 3 * CENT}, cs_params);
137+
TestBnBSuccess("Select all UTXOs", utxo_pool, /*selection_target=*/9 * CENT, /*expected_input_amounts=*/{1 * CENT, 3 * CENT, 5 * CENT}, cs_params);
138+
139+
// BnB finds changeless solution while overshooting by up to cost_of_change
140+
TestBnBSuccess("Select upper bound", utxo_pool, /*selection_target=*/4 * CENT - default_cs_params.m_cost_of_change, /*expected_input_amounts=*/{1 * CENT, 3 * CENT}, cs_params);
141+
142+
// BnB fails to find changeless solution when overshooting by cost_of_change + 1 sat
143+
TestBnBFail("Overshoot upper bound", utxo_pool, /*selection_target=*/4 * CENT - default_cs_params.m_cost_of_change - 1);
144+
145+
// Simple cases without BnB solution
146+
TestBnBFail("Smallest combination too big", utxo_pool, /*selection_target=*/0.5 * CENT);
147+
TestBnBFail("No UTXO combination in target window", utxo_pool, /*selection_target=*/7 * CENT);
148+
TestBnBFail("Select more than available", utxo_pool, /*selection_target=*/10 * CENT);
149+
150+
// Test skipping of equivalent input sets
151+
std::vector<OutputGroup> clone_pool;
152+
AddCoins(clone_pool, {2 * CENT, 7 * CENT, 7 * CENT}, cs_params);
153+
AddDuplicateCoins(clone_pool, 50'000, 5 * CENT, cs_params);
154+
TestBnBSuccess("Skip equivalent input sets", clone_pool, /*selection_target=*/16 * CENT, /*expected_input_amounts=*/{2 * CENT, 7 * CENT, 7 * CENT}, cs_params);
155+
156+
/* Test BnB attempt limit (`TOTAL_TRIES`)
157+
*
158+
* Generally, on a diverse UTXO pool BnB will quickly pass over UTXOs bigger than the target and then start
159+
* combining small counts of UTXOs that in sum remain under the selection_target+cost_of_change. When there are
160+
* multiple UTXOs that have matching amount and cost, combinations with equivalent input sets are skipped. The
161+
* UTXO pool for this test is specifically crafted to create as much branching as possible. The selection target
162+
* is 8 CENT while all UTXOs are slightly bigger than 1 CENT. The smallest eight are 100,000…100,007 sats, while
163+
* the larger nine are 100,368…100,375 (i.e., 100,008…100,016 sats plus cost_of_change (359 sats)).
164+
*
165+
* Because BnB will only select input sets that fall between selection_target and selection_target +
166+
* cost_of_change, and the search traverses the UTXO pool from large to small amounts, the search will visit
167+
* every single combination of eight inputs. All except the last combination will overshoot by more than
168+
* cost_of_change on the eighth input, because the larger nine inputs each exceed 1 CENT by more than
169+
* cost_of_change. Only the last combination consisting of the eight smallest UTXOs falls into the target
170+
* window.
171+
*/
172+
std::vector<OutputGroup> doppelganger_pool;
173+
std::vector<CAmount> doppelgangers;
174+
std::vector<CAmount> expected_inputs;
175+
for (int i = 0; i < 17; ++i) {
176+
if (i < 8) {
177+
// The eight smallest UTXOs can be combined to create expected_result
178+
doppelgangers.push_back(1 * CENT + i);
179+
expected_inputs.push_back(doppelgangers[i]);
180+
} else {
181+
// Any eight UTXOs including at least one UTXO with the added cost_of_change will exceed target window
182+
doppelgangers.push_back(1 * CENT + default_cs_params.m_cost_of_change + i);
183+
}
176184
}
177-
}
178-
AddCoins(doppelganger_pool, doppelgangers);
179-
// Among up to 17 unique UTXOs of similar effective value we will find a solution composed of the eight smallest UTXOs
180-
TestBnBSuccess("Combine smallest 8 of 17 unique UTXOs", doppelganger_pool, /*selection_target=*/8 * CENT, /*expected_input_amounts=*/expected_inputs);
185+
AddCoins(doppelganger_pool, doppelgangers, cs_params);
186+
// Among up to 17 unique UTXOs of similar effective value we will find a solution composed of the eight smallest UTXOs
187+
TestBnBSuccess("Combine smallest 8 of 17 unique UTXOs", doppelganger_pool, /*selection_target=*/8 * CENT, /*expected_input_amounts=*/expected_inputs, cs_params);
181188

182-
// Starting with 18 unique UTXOs of similar effective value we will not find the solution due to exceeding the attempt limit
183-
AddCoins(doppelganger_pool, {1 * CENT + default_cs_params.m_cost_of_change + 17});
184-
TestBnBFail("Exhaust looking for smallest 8 of 18 unique UTXOs", doppelganger_pool, /*selection_target=*/8 * CENT);
189+
// Starting with 18 unique UTXOs of similar effective value we will not find the solution due to exceeding the attempt limit
190+
AddCoins(doppelganger_pool, {1 * CENT + default_cs_params.m_cost_of_change + 17}, cs_params);
191+
TestBnBFail("Exhaust looking for smallest 8 of 18 unique UTXOs", doppelganger_pool, /*selection_target=*/8 * CENT);
192+
}
185193
}
186194

187195
BOOST_AUTO_TEST_CASE(bnb_feerate_sensitivity_test)

0 commit comments

Comments
 (0)