21#include <boost/test/unit_test.hpp>
32#define RANDOM_REPEATS 5
42 tx.
vout.resize(nInput + 1);
43 tx.
vout[nInput].nValue = nValue;
47 group.Insert(std::make_shared<COutput>(output), 0, 0);
54 tx.
vout.resize(nInput + 1);
55 tx.
vout[nInput].nValue = nValue;
57 std::shared_ptr<COutput> coin = std::make_shared<COutput>(
COutPoint(tx.
GetHash(), nInput), tx.
vout.at(nInput), 1, 148,
true,
true, 0,
false,
fee);
59 group.Insert(coin, 0, 0);
60 coin->long_term_fee = long_term_fee;
68 tx.
vout.resize(nInput + 1);
69 tx.
vout[nInput].nValue = nValue;
76 auto ret =
wallet.mapWallet.emplace(std::piecewise_construct, std::forward_as_tuple(txid), std::forward_as_tuple(MakeTransactionRef(std::move(tx)),
TxStateInactive{}));
79 const auto& txout = wtx.
GetTx()->vout.at(nInput);
80 available_coins.
Add(
OutputType::BECH32, {
COutPoint(wtx.
GetHash(), nInput), txout, nAge, custom_size == 0 ?
CalculateMaximumSignedInputSize(txout, &
wallet,
nullptr) : custom_size,
true,
true, wtx.
GetTxTime(), fIsFromMe, feerate});
88 return res ? std::optional<SelectionResult>(*res) : std::nullopt;
94 return res ? std::optional<SelectionResult>(*res) : std::nullopt;
101 std::vector<CAmount> a_amts;
102 std::vector<CAmount> b_amts;
104 a_amts.push_back(coin->txout.nValue);
107 b_amts.push_back(coin->txout.nValue);
109 std::sort(a_amts.begin(), a_amts.end());
110 std::sort(b_amts.begin(), b_amts.end());
112 std::pair<std::vector<CAmount>::iterator, std::vector<CAmount>::iterator>
ret = std::mismatch(a_amts.begin(), a_amts.end(), b_amts.begin());
113 return ret.first == a_amts.end() &&
ret.second == b_amts.end();
120 [](
const std::shared_ptr<COutput>& a,
const std::shared_ptr<COutput>& b) {
121 return a->outpoint == b->outpoint;
126inline std::vector<OutputGroup>&
GroupCoins(
const std::vector<COutput>& available_coins,
bool subtract_fee_outputs =
false)
128 static std::vector<OutputGroup> static_groups;
129 static_groups.clear();
130 for (
auto& coin : available_coins) {
131 static_groups.emplace_back();
133 group.Insert(std::make_shared<COutput>(coin), 0, 0);
134 group.m_subtract_fee_outputs = subtract_fee_outputs;
136 return static_groups;
154 static_groups =
GroupOutputs(
wallet, available_coins, coin_selection_params, {{filter}})[filter];
163 wallet->SetupDescriptorScriptPubKeyMans();
173 size_t expected_attempts;
201 available_coins.
All().at(0).input_bytes = 40;
205 available_coins = {};
207 available_coins.
All().at(0).input_bytes = 40;
211 expected_attempts = 1;
212 BOOST_CHECK_MESSAGE(result9->GetSelectionsEvaluated() == expected_attempts,
strprintf(
"Expected %i attempts, but got %i", expected_attempts, result9->GetSelectionsEvaluated()));
226 COutput select_coin = available_coins.
All().at(0);
233 const auto result10 =
SelectCoins(*
wallet, available_coins, selected_input, 10 *
CENT, coin_control, coin_selection_params_bnb);
235 expected_attempts = 3;
236 BOOST_CHECK_MESSAGE(result10->GetSelectionsEvaluated() == expected_attempts,
strprintf(
"Expected %i attempts, but got %i", expected_attempts, result10->GetSelectionsEvaluated()));
254 expected_result.Clear();
259 COutput select_coin = available_coins.
All().at(1);
264 const auto result13 =
SelectCoins(*
wallet, available_coins, selected_input, 10 *
CENT, coin_control, coin_selection_params_bnb);
266 expected_attempts = 2;
267 BOOST_CHECK_MESSAGE(result13->GetSelectionsEvaluated() == expected_attempts,
strprintf(
"Expected %i attempts, but got %i", expected_attempts, result13->GetSelectionsEvaluated()));
287 BOOST_REQUIRE(!no_res);
294 expected_result.Clear();
299 expected_attempts = 22;
300 BOOST_CHECK_MESSAGE(res->GetSelectionsEvaluated() == expected_attempts,
strprintf(
"Expected %i attempts, but got %i", expected_attempts, res->GetSelectionsEvaluated()));
354 available_coins = {};
423 available_coins = {};
481 available_coins = {};
513 available_coins = {};
514 for (
int j = 0; j < 20; j++)
526 available_coins = {};
537 available_coins = {};
548 available_coins = {};
568 available_coins = {};
570 for (uint16_t j = 0; j < 676; j++)
578 if (amt - 2000 <
CENT) {
580 uint16_t returnSize = std::ceil((2000.0 +
CENT)/amt);
581 CAmount returnValue = amt * returnSize;
594 available_coins = {};
595 for (
int i2 = 0; i2 < 100; i2++)
658 for (
int i = 0; i < 1000; i++)
675 std::default_random_engine generator;
676 std::exponential_distribution<double> distribution (100);
680 for (
int i = 0; i < 100; ++i)
686 for (
int j = 0; j < 1000; ++j)
688 CAmount val = distribution(generator)*10000000;
714 const auto result =
SelectCoins(*
wallet, available_coins, {}, target, cc, cs_params);
716 BOOST_CHECK_GE(result->GetSelectedValue(), target);
723 const CAmount min_viable_change{300};
724 const CAmount change_cost{125};
766 selection_nochange1.
RecalculateWaste(min_viable_change, change_cost, change_fee);
774 selection_nochange2.
RecalculateWaste(min_viable_change, change_cost, change_fee);
817 const CAmount new_target{exact_target - fee_diff * 2};
828 const CAmount target_waste1{-2 * fee_diff};
838 const CAmount large_fee_diff{90};
839 const CAmount target_waste2{-2 * large_fee_diff + change_cost};
843 assert(target_waste2 == -55);
855 const CAmount min_viable_change{200};
856 const CAmount change_cost{125};
867 for (
size_t i = 0; i < inputs.size(); ++i) {
868 inputs[i]->ApplyBumpFee(20*(i+1));
872 CAmount expected_waste = fee_diff * -2 + change_cost + 60;
877 expected_waste = fee_diff * -2 + change_cost + 60 - 30;
893 for (
size_t i = 0; i < inputs.size(); ++i) {
894 inputs[i]->ApplyBumpFee(20*(i+1));
898 CAmount expected_waste = fee_diff * -2 + 60 + 40;
903 expected_waste = fee_diff * -2 + 60 - 30 + 70;
910 const int input_bytes = 148;
913 const int nInput = 0;
917 tx.
vout[nInput].nValue = nValue;
921 const CAmount expected_ev1 = 9852;
930 const CAmount expected_ev3 = -4800;
946 int max_selection_weight,
984 int max_selection_weight = 10'000;
987 for (
int j = 0; j < 10; ++j) {
991 return available_coins;
1002 int max_selection_weight = 3000;
1005 for (
int j = 0; j < 10; ++j) {
1009 return available_coins;
1020 int max_selection_weight = 10'000;
1023 for (
int j = 0; j < 60; ++j) {
1026 for (
int i = 0; i < 10; i++) {
1029 return available_coins;
1032 for (
int i = 0; i < 10; ++i) {
1035 for (
int j = 0; j < 17; ++j) {
1040 size_t expected_attempts = 37;
1041 BOOST_CHECK_MESSAGE(res->GetSelectionsEvaluated() == expected_attempts,
strprintf(
"Expected %i attempts, but got %i", expected_attempts, res->GetSelectionsEvaluated()));
1049 int max_selection_weight = 400'000;
1055 return available_coins;
1062 size_t expected_attempts = 3;
1063 BOOST_CHECK_MESSAGE(res->GetSelectionsEvaluated() == expected_attempts,
strprintf(
"Expected %i attempts, but got %i", expected_attempts, res->GetSelectionsEvaluated()));
1071 int max_selection_weight = 400'000;
1074 for (
int j = 0; j < 5; ++j) {
1082 return available_coins;
1091 size_t expected_attempts = 92;
1092 BOOST_CHECK_MESSAGE(res->GetSelectionsEvaluated() == expected_attempts,
strprintf(
"Expected %i attempts, but got %i", expected_attempts, res->GetSelectionsEvaluated()));
1100 int max_selection_weight = 400'000;
1109 for (
int j = 0; j < 100; ++j) {
1112 for (
int j = 0; j < 100; ++j) {
1115 for (
int j = 0; j < 100; ++j) {
1118 for (
int j = 0; j < 100; ++j) {
1121 return available_coins;
1130 size_t expected_attempts = 38;
1131 BOOST_CHECK_MESSAGE(res->GetSelectionsEvaluated() == expected_attempts,
strprintf(
"Expected %i attempts, but got %i", expected_attempts, res->GetSelectionsEvaluated()));
1139 int max_selection_weight = 40000;
1145 for (
int j = 0; j < 100; ++j) {
1149 return available_coins;
1156 size_t expected_attempts = 7;
1157 BOOST_CHECK_MESSAGE(res->GetSelectionsEvaluated() == expected_attempts,
strprintf(
"Expected %i attempts, but got %i", expected_attempts, res->GetSelectionsEvaluated()));
1165 int max_selection_weight = 3200;
1169 for (
int i = 0; i < 18; ++i) {
1172 return doppelgangers;
1176 for (
int i = 0; i < 8; ++i) {
1181 size_t expected_attempts = 87'525;
1182 BOOST_CHECK_MESSAGE(result_a->GetSelectionsEvaluated() == expected_attempts,
strprintf(
"Expected %i attempts, but got %i", expected_attempts, result_a->GetSelectionsEvaluated()));
1187 for (
int i = 0; i < 19; ++i) {
1190 return doppelgangers;
1199 auto available_coins = coin_setup(*
wallet);
1204 const auto signedTxSize = 10 + 34 + 68 * result->GetInputSet().size();
1207 BOOST_CHECK_GE(result->GetSelectedValue(), target);
1214 return std::any_of(set.begin(), set.end(), [&](
const auto& coin) { return coin->GetEffectiveValue() == amount; });
1247 for (
int j = 0; j < 1515; ++j) {
1252 return available_coins;
1259 BOOST_CHECK_LE(result->GetWeight(), max_weight);
1273 for (
int j = 0; j < 400; ++j) {
1276 for (
int j = 0; j < 2000; ++j) {
1279 return available_coins;
1285 BOOST_CHECK_LE(result->GetWeight(), max_weight);
1298 for (
int j = 0; j < 1515; ++j) {
1301 return available_coins;
1322 std::unique_ptr<CWallet> dummyWallet =
NewWallet(m_node,
"dummy");
1323 add_coin(available_coins, *dummyWallet, 100000);
1342 COutput output = available_coins.
All().at(0);
1350 const auto result =
SelectCoins(*
wallet, available_coins, preset_inputs, target, cc, cs_params);
1359 std::unique_ptr<CWallet> dummyWallet =
NewWallet(m_node,
"dummy");
1362 for (
int i=0; i<10; i++) {
1370 std::unordered_set<COutPoint, SaltedOutpointHasher> outs_to_remove;
1371 const auto& coins = available_coins.
All();
1372 for (
int i = 0; i < 2; i++) {
1373 outs_to_remove.emplace(coins[i].outpoint);
1375 available_coins.
Erase(outs_to_remove);
1378 const auto& updated_coins = available_coins.
All();
1379 for (
const auto& out: outs_to_remove) {
1380 auto it = std::find_if(updated_coins.begin(), updated_coins.end(), [&out](
const COutput &coin) {
1381 return coin.outpoint == out;
CScript GetScriptForDestination(const CTxDestination &dest)
Generate a Bitcoin scriptPubKey for the given CTxDestination.
int64_t CAmount
Amount in satoshis (Can be negative).
constexpr CAmount COIN
The amount of satoshis in one BTC.
BOOST_CHECK_LT(ZeroL, OneL)
#define Assert(val)
Identity function.
Fee rate in satoshis per virtualbyte: CAmount / vB the feerate is represented internally as FeeFrac.
CFeeRate()=default
Fee rate of 0 satoshis per 0 vB.
CAmount GetFee(int32_t virtual_bytes) const
Return the fee in satoshis for the given vsize in vbytes.
I randrange(I range) noexcept
Generate a random integer in the range [0..range), with range > 0.
PreselectedInput & Select(const COutPoint &outpoint)
Lock-in the given output for spending.
bool m_allow_other_inputs
void SetInputWeight(const COutPoint &outpoint, int64_t weight)
Set an input's weight.
A CWallet maintains a set of transactions and balances, and provides the ability to create new transa...
A transaction with a bunch of additional info that only the owner cares about.
const Txid & GetHash() const LIFETIMEBOUND
int64_t GetTxTime() const
CTransactionRef GetTx() const
constexpr int WITNESS_SCALE_FACTOR
BOOST_FIXTURE_TEST_SUITE(cuckoocache_tests, BasicTestingSetup)
Test Suite for CuckooCache.
BOOST_AUTO_TEST_SUITE_END()
bilingual_str ErrorString(const Result< T > &result)
static const CoinEligibilityFilter filter_standard(1, 6, 0)
std::unique_ptr< WalletDatabase > CreateMockableWalletDatabase()
FilteredOutputGroups GroupOutputs(const CWallet &wallet, const CoinsResult &coins, const CoinSelectionParams &coin_sel_params, const std::vector< SelectionFilter > &filters, std::vector< OutputGroup > &ret_discarded_groups)
util::Result< CoinsResult > FetchSelectedInputs(const CWallet &wallet, const CCoinControl &coin_control, const CoinSelectionParams &coin_selection_params)
Fetch and validate coin control selected inputs.
BOOST_FIXTURE_TEST_CASE(wallet_coinsresult_test, BasicTestingSetup)
util::Result< SelectionResult > SelectCoinsBnB(std::vector< OutputGroup > &utxo_pool, const CAmount &selection_target, const CAmount &cost_of_change, int max_selection_weight)
static std::unique_ptr< CWallet > NewWallet(const node::NodeContext &m_node, const std::string &wallet_name="")
util::Result< SelectionResult > CoinGrinder(std::vector< OutputGroup > &utxo_pool, const CAmount &selection_target, CAmount change_target, int max_selection_weight)
std::vector< OutputGroup > & GroupCoins(const std::vector< COutput > &available_coins, bool subtract_fee_outputs=false)
std::vector< OutputGroup > & KnapsackGroupOutputs(const CoinsResult &available_coins, CWallet &wallet, const CoinEligibilityFilter &filter)
util::Result< SelectionResult > KnapsackSolver(std::vector< OutputGroup > &groups, const CAmount &nTargetValue, CAmount change_target, FastRandomContext &rng, int max_selection_weight)
util::Result< SelectionResult > SelectCoins(const CWallet &wallet, CoinsResult &available_coins, const CoinsResult &pre_set_inputs, const CAmount &nTargetValue, const CCoinControl &coin_control, const CoinSelectionParams &coin_selection_params)
Select all coins from coin_control, and if coin_control 'm_allow_other_inputs=true',...
static bool has_coin(const OutputSet &set, CAmount amount)
static const CoinEligibilityFilter filter_standard_extra(6, 6, 0)
static bool EqualResult(const SelectionResult &a, const SelectionResult &b)
Check if this selection is equal to another one.
BOOST_AUTO_TEST_CASE(bnb_test)
std::set< std::shared_ptr< COutput >, OutputPtrComparator > OutputSet
@ WALLET_FLAG_DESCRIPTORS
Indicate that this wallet supports DescriptorScriptPubKeyMan.
static const CoinEligibilityFilter filter_confirmed(1, 1, 0)
int CalculateMaximumSignedInputSize(const CTxOut &txout, const COutPoint outpoint, const SigningProvider *provider, bool can_grind_r, const CCoinControl *coin_control)
static void add_coin(const CAmount &nValue, int nInput, SelectionResult &result)
static bool EquivalentResult(const SelectionResult &a, const SelectionResult &b)
Check if SelectionResult a is equivalent to SelectionResult b.
static util::Result< SelectionResult > select_coins(const CAmount &target, const CoinSelectionParams &cs_params, const CCoinControl &cc, std::function< CoinsResult(CWallet &)> coin_setup, const node::NodeContext &m_node)
#define BOOST_CHECK_EQUAL(v1, v2)
#define BOOST_CHECK(expr)
constexpr int32_t MAX_STANDARD_TX_WEIGHT
The maximum weight for transactions we're willing to relay/mine.
A mutable version of CTransaction.
std::vector< CTxOut > vout
Txid GetHash() const
Compute the hash of this CMutableTransaction.
std::unique_ptr< interfaces::Chain > chain
A UTXO under consideration for use in funding a new transaction.
COutPoint outpoint
The outpoint identifying this UTXO.
CTxOut txout
The output itself.
CAmount GetEffectiveValue() const
Parameters for filtering which OutputGroups we may use in coin selection.
Parameters for one iteration of Coin Selection.
CAmount m_min_change_target
Mininmum change to target in Knapsack solver and CoinGrinder: select coins to cover the payment and a...
bool m_subtract_fee_outputs
Indicate that we are subtracting the fee from outputs.
CFeeRate m_effective_feerate
The targeted feerate of the transaction being built.
CAmount min_viable_change
Minimum amount for creating a change output.
int change_spend_size
Size of the input to spend a change output in virtual bytes.
CAmount m_cost_of_change
Cost of creating the change output + cost of spending the change output in the future.
CAmount m_change_fee
Cost of creating the change output.
int change_output_size
Size of a change output in bytes, determined by the output type.
CFeeRate m_long_term_feerate
The feerate estimate used to estimate an upper bound on what should be sufficient to spend the change...
CFeeRate m_discard_feerate
If the cost to spend a change output at the discard feerate exceeds its value, drop it to fees.
int tx_noinputs_size
Size of the transaction before coin selection, consisting of the header and recipient output(s),...
COutputs available for spending, stored by OutputType.
void Add(OutputType type, const COutput &out)
std::vector< COutput > All() const
Concatenate and return all COutputs as one vector.
size_t Size() const
The following methods are provided so that CoinsResult can mimic a vector, i.e., methods can work wit...
std::map< OutputType, std::vector< COutput > > coins
void Erase(const std::unordered_set< COutPoint, SaltedOutpointHasher > &coins_to_remove)
std::vector< OutputGroup > mixed_group
A group of UTXOs paid to the same output script.
Stores several 'Groups' whose were mapped by output type.
void SetBumpFeeDiscount(CAmount discount)
How much individual inputs overestimated the bump fees for shared ancestries.
void RecalculateWaste(CAmount min_viable_change, CAmount change_cost, CAmount change_fee)
Calculates and stores the waste for this result given the cost of change and the opportunity cost of ...
void AddInput(const OutputGroup &group)
const OutputSet & GetInputSet() const
Get m_selected_inputs.
std::vector< std::shared_ptr< COutput > > GetShuffledInputVector() const
Get the vector of COutputs that will be used to fill in a CTransaction's vin.
Testing setup and teardown for wallet.
#define WITH_LOCK(cs, code)
transaction_identifier< false > Txid
Txid commits to all transaction fields except the witness.
BOOST_CHECK_NE(OneL.ToString(), ArrayToString(ZeroArray, 32))