bsv_wasm/transaction/
match_criteria.rs

1use crate::{ScriptTemplate, Transaction, TxIn, TxOut};
2
3#[cfg(target_arch = "wasm32")]
4use wasm_bindgen::prelude::*;
5
6#[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-transaction"), wasm_bindgen)]
7#[derive(Debug, Clone, Default)]
8pub struct MatchCriteria {
9    pub(crate) script_template: Option<ScriptTemplate>,
10    pub(crate) exact_value: Option<u64>,
11    pub(crate) min_value: Option<u64>,
12    pub(crate) max_value: Option<u64>,
13}
14
15#[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-transaction"), wasm_bindgen)]
16impl MatchCriteria {
17    #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-transaction"), wasm_bindgen(constructor))]
18    pub fn new() -> MatchCriteria {
19        MatchCriteria::default()
20    }
21
22    #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-transaction"), wasm_bindgen(js_name = setScriptTemplate))]
23    pub fn set_script_template(&mut self, script_template: &ScriptTemplate) -> MatchCriteria {
24        self.script_template = Some(script_template.clone());
25
26        self.clone()
27    }
28
29    #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-transaction"), wasm_bindgen(js_name = setValue))]
30    pub fn set_value(&mut self, value: u64) -> MatchCriteria {
31        self.exact_value = Some(value);
32
33        self.clone()
34    }
35
36    #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-transaction"), wasm_bindgen(js_name = setMin))]
37    pub fn set_min(&mut self, min: u64) -> MatchCriteria {
38        self.min_value = Some(min);
39
40        self.clone()
41    }
42
43    #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-transaction"), wasm_bindgen(js_name = setMax))]
44    pub fn set_max(&mut self, max: u64) -> MatchCriteria {
45        self.max_value = Some(max);
46
47        self.clone()
48    }
49}
50
51#[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-transaction"), wasm_bindgen)]
52impl Transaction {
53    fn is_matching_output(txout: &TxOut, criteria: &MatchCriteria) -> bool {
54        // If script is specified and doesnt match
55        if matches!(&criteria.script_template, Some(crit_script) if !txout.script_pub_key.is_match(crit_script)) {
56            return false;
57        }
58
59        // If exact_value is specified and doesnt match
60        if criteria.exact_value.is_some() && criteria.exact_value != Some(txout.value) {
61            return false;
62        }
63
64        // If min_value is specified and value is less than min value
65        if criteria.min_value.is_some() && criteria.min_value > Some(txout.value) {
66            return false;
67        }
68
69        // If min_value is specified and value is greater than max value
70        if criteria.max_value.is_some() && criteria.max_value < Some(txout.value) {
71            return false;
72        }
73
74        true
75    }
76
77    /**
78     * Returns the first output index that matches the given parameters, returns None or null if not found.
79     */
80    #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-transaction"), wasm_bindgen(js_name = matchOutput))]
81    pub fn match_output(&self, criteria: &MatchCriteria) -> Option<usize> {
82        self.outputs.iter().enumerate().find_map(|(i, txout)| match Transaction::is_matching_output(txout, criteria) {
83            true => Some(i),
84            false => None,
85        })
86    }
87
88    /**
89     * Returns a list of outputs indexes that match the given parameters
90     */
91    #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-transaction"), wasm_bindgen(js_name = matchOutputs))]
92    pub fn match_outputs(&self, criteria: &MatchCriteria) -> Vec<usize> {
93        let matches = self
94            .outputs
95            .iter()
96            .enumerate()
97            .filter_map(|(i, txout)| match Transaction::is_matching_output(txout, criteria) {
98                true => Some(i),
99                false => None,
100            })
101            .collect();
102
103        matches
104    }
105
106    fn is_matching_input(txin: &TxIn, criteria: &MatchCriteria) -> bool {
107        // If script is specified and doesnt match
108        if matches!(&criteria.script_template, Some(crit_script) if !txin.get_finalised_script().unwrap().is_match(crit_script)) {
109            return false;
110        }
111
112        // If exact_value is specified and doesnt match
113        if criteria.exact_value.is_some() && criteria.exact_value != txin.satoshis {
114            return false;
115        }
116
117        // If min_value is specified and value is less than min value
118        if criteria.min_value.is_some() && criteria.min_value > txin.satoshis {
119            return false;
120        }
121
122        // If min_value is specified and value is greater than max value
123        if criteria.max_value.is_some() && criteria.max_value < txin.satoshis {
124            return false;
125        }
126
127        true
128    }
129
130    /**
131     * Returns the first input index that matches the given parameters, returns None or null if not found.
132     */
133    #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-transaction"), wasm_bindgen(js_name = matchInput))]
134    pub fn match_input(&self, criteria: &MatchCriteria) -> Option<usize> {
135        self.inputs.iter().enumerate().find_map(|(i, txin)| match Transaction::is_matching_input(txin, criteria) {
136            true => Some(i),
137            false => None,
138        })
139    }
140
141    /**
142     * Returns a list of input indexes that match the given parameters
143     */
144    #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-transaction"), wasm_bindgen(js_name = matchInputs))]
145    pub fn match_inputs(&self, criteria: &MatchCriteria) -> Vec<usize> {
146        let matches = self
147            .inputs
148            .iter()
149            .enumerate()
150            .filter_map(|(i, txin)| match Transaction::is_matching_input(txin, criteria) {
151                true => Some(i),
152                false => None,
153            })
154            .collect();
155
156        matches
157    }
158}