}
}
+ #[test]
+ #[should_panic(expected = "BnBNoExactMatch")]
+ fn test_bnb_function_no_exact_match() {
+ let fee_rate = FeeRate::from_sat_per_vb(10.0);
+ let utxos: Vec<OutputGroup> = get_test_utxos()
+ .into_iter()
+ .map(|u| OutputGroup::new(u.0, u.1, fee_rate))
+ .collect();
+
+ let curr_available_value = utxos
+ .iter()
+ .fold(0, |acc, x| acc + x.effective_value as u64);
+
+ let size_of_change = 31;
+ let cost_of_change = size_of_change as f32 * fee_rate.as_sat_vb();
+ BranchAndBoundCoinSelection::new(size_of_change)
+ .bnb(
+ vec![],
+ utxos,
+ 0,
+ curr_available_value,
+ 20_000,
+ 50.0,
+ cost_of_change,
+ )
+ .unwrap();
+ }
+
+ #[test]
+ #[should_panic(expected = "BnBTotalTriesExceeded")]
+ fn test_bnb_function_tries_exceeded() {
+ let fee_rate = FeeRate::from_sat_per_vb(10.0);
+ let utxos: Vec<OutputGroup> = generate_same_value_utxos(100_000, 100_000)
+ .into_iter()
+ .map(|u| OutputGroup::new(u.0, u.1, fee_rate))
+ .collect();
+
+ let curr_available_value = utxos
+ .iter()
+ .fold(0, |acc, x| acc + x.effective_value as u64);
+
+ let size_of_change = 31;
+ let cost_of_change = size_of_change as f32 * fee_rate.as_sat_vb();
+
+ BranchAndBoundCoinSelection::new(size_of_change)
+ .bnb(
+ vec![],
+ utxos,
+ 0,
+ curr_available_value,
+ 20_000,
+ 50.0,
+ cost_of_change,
+ )
+ .unwrap();
+ }
+
+ // The match won't be exact but still in the range
+ #[test]
+ fn test_bnb_function_almost_exact_match_with_fees() {
+ let fee_rate = FeeRate::from_sat_per_vb(1.0);
+ let size_of_change = 31;
+ let cost_of_change = size_of_change as f32 * fee_rate.as_sat_vb();
+ let fee_amount = 50.0;
+
+ let utxos: Vec<_> = generate_same_value_utxos(50_000, 10)
+ .into_iter()
+ .map(|u| OutputGroup::new(u.0, u.1, fee_rate))
+ .collect();
+
+ let curr_value = 0;
+
+ let curr_available_value = utxos
+ .iter()
+ .fold(0, |acc, x| acc + x.effective_value as u64);
+
+ // 2*(value of 1 utxo) - 2*(1 utxo fees with 1.0sat/vbyte fee rate) -
+ // cost_of_change + 5.
+ let target_amount = 2 * 50_000 - 2 * 67 - cost_of_change.ceil() as u64 + 5;
+
+ let result = BranchAndBoundCoinSelection::new(size_of_change)
+ .bnb(
+ vec![],
+ utxos,
+ curr_value,
+ curr_available_value,
+ target_amount,
+ fee_amount,
+ cost_of_change,
+ )
+ .unwrap();
+ assert_eq!(result.fee_amount, 186.0);
+ assert_eq!(result.selected_amount, 100_000);
+ }
+
+ // TODO: bnb() function should be optimized, and this test should be done with more utxos
+ #[test]
+ fn test_bnb_function_exact_match_more_utxos() {
+ let seed = [0; 32];
+ let mut rng: StdRng = SeedableRng::from_seed(seed);
+ let fee_rate = FeeRate::from_sat_per_vb(0.0);
+
+ for _ in 0..200 {
+ let optional_utxos: Vec<_> = generate_random_utxos(&mut rng, 40)
+ .into_iter()
+ .map(|u| OutputGroup::new(u.0, u.1, fee_rate))
+ .collect();
+
+ let curr_value = 0;
+
+ let curr_available_value = optional_utxos
+ .iter()
+ .fold(0, |acc, x| acc + x.effective_value as u64);
+
+ let target_amount = optional_utxos[3].effective_value as u64
+ + optional_utxos[23].effective_value as u64;
+
+ let result = BranchAndBoundCoinSelection::new(0)
+ .bnb(
+ vec![],
+ optional_utxos,
+ curr_value,
+ curr_available_value,
+ target_amount,
+ 0.0,
+ 0.0,
+ )
+ .unwrap();
+ assert_eq!(result.selected_amount, target_amount);
+ }
+ }
}