whisky_common/algo/
utxo_selection.rs1use crate::{data::Value, errors::WError, models::*};
2use std::collections::HashSet;
3
4pub fn select_utxos(
5 inputs: &[UTxO],
6 required_assets: Value,
7 threshold: &str,
8) -> Result<Vec<UTxO>, WError> {
9 let mut total_required_assets = required_assets.clone();
10 total_required_assets.add_asset("lovelace", threshold.parse::<u64>().unwrap());
11
12 let mut only_lovelace: Vec<usize> = vec![];
14 let mut singleton: Vec<usize> = vec![];
15 let mut pair: Vec<usize> = vec![];
16 let mut rest: Vec<usize> = vec![];
17 for (index, utxo) in inputs.iter().enumerate() {
18 match utxo.output.amount.len() {
19 1 => only_lovelace.push(index),
20 2 => singleton.push(index),
21 3 => pair.push(index),
22 _ => rest.push(index),
23 }
24 }
25
26 let mut used_utxos: HashSet<usize> = HashSet::new();
27
28 let mut use_utxo = |index: usize, total_required_assets: &mut Value| {
29 let utxo = inputs[index].clone();
30 for asset in utxo.output.amount {
31 total_required_assets
32 .negate_asset(&asset.unit(), asset.quantity().parse::<u64>().unwrap());
33 used_utxos.insert(index);
34 }
35 };
36
37 let mut process_list = |list: Vec<usize>, unit: String, total_required_assets: &mut Value| {
38 for index in list {
39 let required_asset_value = total_required_assets.get(&unit);
40 if required_asset_value == 0 {
41 return;
42 }
43 let utxo = inputs[index].clone();
44 for asset in utxo.output.amount {
45 if asset.unit() == unit {
46 use_utxo(index, total_required_assets);
47 break;
48 }
49 }
50 }
51 };
52
53 let required_units: Vec<String> = total_required_assets.keys();
54
55 for unit in required_units.clone() {
56 if unit != *"lovelace" && total_required_assets.get(&unit) > 0 {
57 process_list(singleton.clone(), unit.clone(), &mut total_required_assets);
58 process_list(pair.clone(), unit.clone(), &mut total_required_assets);
59 process_list(rest.clone(), unit.clone(), &mut total_required_assets);
60 }
61 }
62
63 process_list(
64 only_lovelace.clone(),
65 "lovelace".to_string(),
66 &mut total_required_assets,
67 );
68
69 process_list(
70 singleton.clone(),
71 "lovelace".to_string(),
72 &mut total_required_assets,
73 );
74 process_list(
75 pair.clone(),
76 "lovelace".to_string(),
77 &mut total_required_assets,
78 );
79 process_list(
80 rest.clone(),
81 "lovelace".to_string(),
82 &mut total_required_assets,
83 );
84
85 for unit in required_units.clone() {
86 if total_required_assets.get(&unit) > 0 {
87 println!("Total required assets: {:?}", total_required_assets);
88 return Err(WError::new("select_utxos", &format!(
89 "{:?} value missing. Reminder, in this selection you required for {:?} extra lovelace as threshold",
90 total_required_assets,
91 threshold
92 )));
93 }
94 }
95
96 let mut selected_utxos: Vec<UTxO> = vec![];
97 for index in used_utxos.iter() {
98 selected_utxos.push(inputs[*index].clone());
99 }
100
101 Ok(selected_utxos)
102}