tx3_resolver/inputs/
mod.rs1use std::collections::HashSet;
4
5use tx3_tir::encoding::AnyTir;
6use tx3_tir::model::core::UtxoSet;
7use tx3_tir::model::v1beta0 as tir;
8use tx3_tir::model::{assets::CanonicalAssets, core::UtxoRef};
9
10use crate::{Error, UtxoStore};
11
12mod narrow;
13mod select;
14
15macro_rules! data_or_bail {
16 ($expr:expr, bytes) => {
17 $expr
18 .as_bytes()
19 .ok_or(Error::ExpectedData("bytes".to_string(), $expr.clone()))
20 };
21
22 ($expr:expr, number) => {
23 $expr
24 .as_number()
25 .ok_or(Error::ExpectedData("number".to_string(), $expr.clone()))?
26 };
27
28 ($expr:expr, assets) => {
29 $expr
30 .as_assets()
31 .ok_or(Error::ExpectedData("assets".to_string(), $expr.clone()))
32 };
33
34 ($expr:expr, utxo_refs) => {
35 $expr
36 .as_utxo_refs()
37 .ok_or(Error::ExpectedData("utxo refs".to_string(), $expr.clone()))
38 };
39}
40
41pub struct Diagnostic {
42 pub query: tir::InputQuery,
43 pub utxos: UtxoSet,
44 pub selected: UtxoSet,
45}
46
47const MAX_SEARCH_SPACE_SIZE: usize = 50;
48
49#[derive(Debug, Clone)]
50pub struct CanonicalQuery {
51 pub address: Option<Vec<u8>>,
52 pub min_amount: Option<CanonicalAssets>,
53 pub refs: HashSet<UtxoRef>,
54 pub support_many: bool,
55 pub collateral: bool,
56}
57
58impl std::fmt::Display for CanonicalQuery {
59 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
60 write!(f, "CanonicalQuery {{")?;
61
62 if let Some(address) = &self.address {
63 write!(f, "address: {}", hex::encode(address))?;
64 }
65
66 if let Some(min_amount) = &self.min_amount {
67 write!(f, "min_amount: {}", min_amount)?;
68 }
69
70 for (i, ref_) in self.refs.iter().enumerate() {
71 write!(f, "ref[{}]:{}#{}", i, hex::encode(&ref_.txid), ref_.index)?;
72 }
73
74 write!(f, "support_many: {:?}", self.support_many)?;
75 write!(f, "for_collateral: {:?}", self.collateral)?;
76 write!(f, "}}")
77 }
78}
79
80impl TryFrom<tir::InputQuery> for CanonicalQuery {
81 type Error = Error;
82
83 fn try_from(query: tir::InputQuery) -> Result<Self, Self::Error> {
84 let address = query
85 .address
86 .as_option()
87 .map(|x| data_or_bail!(x, bytes))
88 .transpose()?
89 .map(Vec::from);
90
91 let min_amount = query
92 .min_amount
93 .as_option()
94 .map(|x| data_or_bail!(x, assets))
95 .transpose()?
96 .map(|x| CanonicalAssets::from(Vec::from(x)));
97
98 let refs = query
99 .r#ref
100 .as_option()
101 .map(|x| data_or_bail!(x, utxo_refs))
102 .transpose()?
103 .map(|x| HashSet::from_iter(x.iter().cloned()))
104 .unwrap_or_default();
105
106 Ok(Self {
107 address,
108 min_amount,
109 refs,
110 support_many: query.many,
111 collateral: query.collateral,
112 })
113 }
114}
115
116pub async fn resolve<T: UtxoStore>(tx: AnyTir, utxos: &T) -> Result<AnyTir, Error> {
117 let mut selector = select::InputSelector::new(utxos);
118
119 for (name, query) in tx3_tir::reduce::find_queries(&tx) {
120 let query = CanonicalQuery::try_from(query)?;
121 selector.add(name, query).await?;
122 }
123
124 let all_inputs = selector.select_all()?;
125
126 let out = tx3_tir::reduce::apply_inputs(tx, &all_inputs)?;
127
128 Ok(out)
129}