| 351 | |
| 352 | template<CoinSelectionAlgorithm Algorithm> |
| 353 | void FuzzCoinSelectionAlgorithm(std::span<const uint8_t> buffer) { |
| 354 | SeedRandomStateForTest(SeedRand::ZEROS); |
| 355 | FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; |
| 356 | std::vector<COutput> utxo_pool; |
| 357 | |
| 358 | const CFeeRate long_term_fee_rate{ConsumeMoney(fuzzed_data_provider, /*max=*/COIN)}; |
| 359 | const CFeeRate effective_fee_rate{ConsumeMoney(fuzzed_data_provider, /*max=*/COIN)}; |
| 360 | // Discard feerate must be at least dust relay feerate |
| 361 | const CFeeRate discard_fee_rate{fuzzed_data_provider.ConsumeIntegralInRange<CAmount>(DUST_RELAY_TX_FEE, COIN)}; |
| 362 | const CAmount target{fuzzed_data_provider.ConsumeIntegralInRange<CAmount>(1, MAX_MONEY)}; |
| 363 | const bool subtract_fee_outputs{fuzzed_data_provider.ConsumeBool()}; |
| 364 | |
| 365 | FastRandomContext fast_random_context{ConsumeUInt256(fuzzed_data_provider)}; |
| 366 | CoinSelectionParams coin_params{fast_random_context}; |
| 367 | coin_params.m_subtract_fee_outputs = subtract_fee_outputs; |
| 368 | coin_params.m_long_term_feerate = long_term_fee_rate; |
| 369 | coin_params.m_effective_feerate = effective_fee_rate; |
| 370 | coin_params.change_output_size = fuzzed_data_provider.ConsumeIntegralInRange(1, MAX_SCRIPT_SIZE); |
| 371 | coin_params.m_change_fee = effective_fee_rate.GetFee(coin_params.change_output_size); |
| 372 | coin_params.m_discard_feerate = discard_fee_rate; |
| 373 | coin_params.change_spend_size = fuzzed_data_provider.ConsumeIntegralInRange<int>(41, 1000); |
| 374 | const auto change_spend_fee{coin_params.m_discard_feerate.GetFee(coin_params.change_spend_size)}; |
| 375 | coin_params.m_cost_of_change = coin_params.m_change_fee + change_spend_fee; |
| 376 | CScript change_out_script = CScript() << std::vector<unsigned char>(coin_params.change_output_size, OP_TRUE); |
| 377 | const auto dust{GetDustThreshold(CTxOut{/*nValueIn=*/0, change_out_script}, coin_params.m_discard_feerate)}; |
| 378 | coin_params.min_viable_change = std::max(change_spend_fee + 1, dust); |
| 379 | |
| 380 | int next_locktime{0}; |
| 381 | CAmount total_balance{CreateCoins(fuzzed_data_provider, utxo_pool, coin_params, next_locktime)}; |
| 382 | |
| 383 | std::vector<OutputGroup> group_pos; |
| 384 | GroupCoins(fuzzed_data_provider, utxo_pool, coin_params, /*positive_only=*/true, group_pos); |
| 385 | |
| 386 | int max_selection_weight = fuzzed_data_provider.ConsumeIntegralInRange<int>(0, std::numeric_limits<int>::max()); |
| 387 | |
| 388 | std::optional<SelectionResult> result; |
| 389 | |
| 390 | if constexpr (Algorithm == CoinSelectionAlgorithm::BNB) { |
| 391 | if (!coin_params.m_subtract_fee_outputs) { |
| 392 | auto result_bnb = SelectCoinsBnB(group_pos, target, coin_params.m_cost_of_change, max_selection_weight); |
| 393 | if (result_bnb) { |
| 394 | result = *result_bnb; |
| 395 | assert(result_bnb->GetChange(coin_params.min_viable_change, coin_params.m_change_fee) == 0); |
| 396 | assert(result_bnb->GetSelectedValue() >= target); |
| 397 | assert(result_bnb->GetWeight() <= max_selection_weight); |
| 398 | (void)result_bnb->GetShuffledInputVector(); |
| 399 | (void)result_bnb->GetInputSet(); |
| 400 | } |
| 401 | } |
| 402 | } |
| 403 | |
| 404 | if constexpr (Algorithm == CoinSelectionAlgorithm::SRD) { |
| 405 | auto result_srd = SelectCoinsSRD(group_pos, target, coin_params.m_change_fee, fast_random_context, max_selection_weight); |
| 406 | if (result_srd) { |
| 407 | result = *result_srd; |
| 408 | assert(result_srd->GetSelectedValue() >= target); |
| 409 | assert(result_srd->GetChange(CHANGE_LOWER, coin_params.m_change_fee) > 0); |
| 410 | assert(result_srd->GetWeight() <= max_selection_weight); |
nothing calls this directly
no test coverage detected