tx3_resolver/
lib.rs

1use tx3_lang::{
2    applying::{self, Apply as _},
3    backend::{self, Compiler, TxEval, UtxoStore},
4    ir::{self, Node},
5};
6
7pub mod inputs;
8
9#[cfg(test)]
10pub mod mock;
11
12#[derive(Debug, thiserror::Error)]
13pub enum Error {
14    #[error("input query too broad")]
15    InputQueryTooBroad,
16
17    #[error("input not resolved: {0} {1:?}")]
18    InputNotResolved(String, ir::InputQuery),
19
20    #[error("expected {0}, got {1:?}")]
21    ExpectedData(String, ir::Expression),
22
23    #[error("apply error: {0}")]
24    ApplyError(#[from] applying::Error),
25
26    #[error("can't compile non-constant tir")]
27    CantCompileNonConstantTir,
28
29    #[error("backend error: {0}")]
30    BackendError(#[from] backend::Error),
31}
32
33async fn eval_pass<C: Compiler, S: UtxoStore>(
34    tx: &ir::Tx,
35    compiler: &C,
36    utxos: &S,
37    last_eval: Option<&TxEval>,
38) -> Result<Option<TxEval>, Error> {
39    let attempt = tx.clone();
40
41    let fees = last_eval.as_ref().map(|e| e.fee).unwrap_or(0);
42
43    let attempt = applying::apply_fees(attempt, fees)?;
44
45    let attempt = applying::reduce(attempt)?;
46
47    let attempt = crate::inputs::resolve(attempt, utxos).await?;
48
49    let attempt = tx3_lang::ProtoTx::from(attempt);
50
51    let attempt = attempt.apply()?;
52
53    if !attempt.as_ref().is_constant() {
54        return Err(Error::CantCompileNonConstantTir);
55    }
56
57    let eval = compiler.compile(attempt.as_ref())?;
58
59    let Some(last_eval) = last_eval else {
60        return Ok(Some(eval));
61    };
62
63    if eval != *last_eval {
64        return Ok(Some(eval));
65    }
66
67    Ok(None)
68}
69
70pub async fn resolve_tx<C: Compiler, S: UtxoStore>(
71    tx: tx3_lang::ProtoTx,
72    compiler: &mut C,
73    utxos: &S,
74    max_optimize_rounds: usize,
75) -> Result<TxEval, Error> {
76    let max_optimize_rounds = max_optimize_rounds.max(3);
77
78    let mut last_eval = None;
79    let mut rounds = 0;
80
81    // one initial pass to reduce any available params;
82    let tx = tx.apply()?;
83
84    // reduce compiler ops
85    let tx = ir::Tx::from(tx);
86    let tx = tx.apply(compiler)?;
87
88    while let Some(better) = eval_pass(&tx, compiler, utxos, last_eval.as_ref()).await? {
89        last_eval = Some(better);
90
91        if rounds > max_optimize_rounds {
92            break;
93        }
94
95        rounds += 1;
96    }
97
98    Ok(last_eval.unwrap())
99}