Skip to main content

tx3_resolver/inputs/
mod.rs

1//! Tx input resolution pipeline.
2//!
3//! Orchestrates three stages:
4//! 1. **Narrow**: query the UTxO store to build a pool of candidate UTxOs
5//! 2. **Approximate**: filter and rank candidates for each query independently
6//! 3. **Assign**: allocate UTxOs across all queries simultaneously
7
8use std::collections::BTreeMap;
9
10use tx3_tir::encoding::AnyTir;
11use tx3_tir::model::core::{UtxoRef, UtxoSet};
12
13use crate::{Error, UtxoStore};
14
15mod approximate;
16pub(crate) mod assign;
17mod canonical;
18mod narrow;
19
20#[cfg(test)]
21pub(crate) mod test_utils;
22#[cfg(test)]
23mod tests;
24
25pub use canonical::CanonicalQuery;
26
27/// Resolve input queries against a UTxO store, returning a map of
28/// query name → selected UTxOs.
29pub async fn resolve_queries<T: UtxoStore>(
30    utxos: &T,
31    queries: Vec<(String, CanonicalQuery)>,
32) -> Result<BTreeMap<String, UtxoSet>, Error> {
33    // 1. Narrow: build pool of candidate UTxOs from all queries
34    let pool = narrow::build_utxo_pool(utxos, &queries).await?;
35
36    // 2. Approximate: rank candidates for each query independently
37    let prepared = approximate::approximate_queries(&pool, queries);
38
39    // 3. Assign: allocate UTxOs across all queries
40    let assignments = assign::assign_all(prepared);
41
42    // 4. Validate: ensure all queries were resolved
43    let pool_refs: Vec<UtxoRef> = pool.keys().cloned().collect();
44    let mut all_inputs = BTreeMap::new();
45
46    for entry in assignments {
47        if entry.selection.is_empty() {
48            return Err(Error::InputNotResolved(entry.name, entry.query, pool_refs));
49        }
50        all_inputs.insert(entry.name, entry.selection);
51    }
52
53    Ok(all_inputs)
54}
55
56/// Resolve all input queries in a TIR transaction.
57pub async fn resolve<T: UtxoStore>(tx: AnyTir, utxos: &T) -> Result<AnyTir, Error> {
58    let mut queries: Vec<(String, CanonicalQuery)> = Vec::new();
59
60    for (name, query) in tx3_tir::reduce::find_queries(&tx) {
61        queries.push((name, CanonicalQuery::try_from(query)?));
62    }
63
64    let all_inputs = resolve_queries(utxos, queries).await?;
65
66    let out = tx3_tir::reduce::apply_inputs(tx, &all_inputs)?;
67
68    Ok(out)
69}