whisky_common/algo/
utxo_selection.rs

1use 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    // Classify the utxos
13    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}