cml_chain/transaction/
utils.rs

1use std::collections::BTreeSet;
2
3use crate::{
4    address::Address,
5    plutus::Language,
6    transaction::{DatumOption, ScriptRef, TransactionOutput},
7    Value,
8};
9use cml_crypto::{DatumHash, Ed25519KeyHash, TransactionHash};
10
11use super::{
12    AlonzoFormatTxOut, ConwayFormatTxOut, NativeScript, TransactionBody, TransactionWitnessSet,
13};
14
15impl TransactionBody {
16    pub fn hash(&self) -> TransactionHash {
17        crate::crypto::hash::hash_transaction(self)
18    }
19}
20
21impl TransactionOutput {
22    pub fn new(
23        address: Address,
24        amount: Value,
25        datum_option: Option<DatumOption>,
26        script_reference: Option<ScriptRef>,
27    ) -> Self {
28        match (datum_option, script_reference) {
29            (None, None) => Self::AlonzoFormatTxOut(AlonzoFormatTxOut::new(address, amount)),
30            (Some(DatumOption::Hash { datum_hash, .. }), None) => {
31                let mut tx_out = AlonzoFormatTxOut::new(address, amount);
32                tx_out.datum_hash = Some(datum_hash);
33                Self::AlonzoFormatTxOut(tx_out)
34            }
35            (datum, script_ref) => {
36                let mut tx_out = ConwayFormatTxOut::new(address, amount);
37                tx_out.datum_option = datum;
38                tx_out.script_reference = script_ref;
39                Self::ConwayFormatTxOut(tx_out)
40            }
41        }
42    }
43
44    pub fn address(&self) -> &Address {
45        match self {
46            Self::AlonzoFormatTxOut(tx_out) => &tx_out.address,
47            Self::ConwayFormatTxOut(tx_out) => &tx_out.address,
48        }
49    }
50
51    pub fn set_address(&mut self, new_address: Address) {
52        match self {
53            Self::AlonzoFormatTxOut(tx_out) => tx_out.address = new_address,
54            Self::ConwayFormatTxOut(tx_out) => tx_out.address = new_address,
55        }
56    }
57
58    pub fn amount(&self) -> &Value {
59        match self {
60            Self::AlonzoFormatTxOut(tx_out) => &tx_out.amount,
61            Self::ConwayFormatTxOut(tx_out) => &tx_out.amount,
62        }
63    }
64
65    pub fn set_amount(&mut self, amount: Value) {
66        match self {
67            Self::AlonzoFormatTxOut(tx_out) => tx_out.amount = amount,
68            Self::ConwayFormatTxOut(tx_out) => tx_out.amount = amount,
69        }
70    }
71
72    pub fn datum(&self) -> Option<DatumOption> {
73        match self {
74            Self::AlonzoFormatTxOut(tx_out) => tx_out
75                .datum_hash
76                .as_ref()
77                .map(|hash| DatumOption::new_hash(*hash)),
78            Self::ConwayFormatTxOut(tx_out) => tx_out.datum_option.clone(),
79        }
80    }
81
82    /// Get the datum hash from a tx output if present as a hash.
83    /// Returns None if there is no datum, or the datum is inlined.
84    /// Use TransactionOutput::datum() for inlined datums.
85    pub fn datum_hash(&self) -> Option<&DatumHash> {
86        match self {
87            Self::AlonzoFormatTxOut(tx_out) => tx_out.datum_hash.as_ref(),
88            Self::ConwayFormatTxOut(tx_out) => match &tx_out.datum_option {
89                Some(DatumOption::Hash { datum_hash, .. }) => Some(datum_hash),
90                _ => None,
91            },
92        }
93    }
94
95    pub fn script_ref(&self) -> Option<&ScriptRef> {
96        match self {
97            Self::AlonzoFormatTxOut(_) => None,
98            Self::ConwayFormatTxOut(tx_out) => tx_out.script_reference.as_ref(),
99        }
100    }
101}
102
103impl From<AlonzoFormatTxOut> for TransactionOutput {
104    fn from(tx_out: AlonzoFormatTxOut) -> Self {
105        Self::AlonzoFormatTxOut(tx_out)
106    }
107}
108
109impl From<ConwayFormatTxOut> for TransactionOutput {
110    fn from(tx_out: ConwayFormatTxOut) -> Self {
111        Self::ConwayFormatTxOut(tx_out)
112    }
113}
114
115pub type RequiredSignersSet = BTreeSet<Ed25519KeyHash>;
116
117impl From<&NativeScript> for RequiredSignersSet {
118    fn from(script: &NativeScript) -> Self {
119        fn from_scripts(scripts: &[NativeScript]) -> RequiredSignersSet {
120            scripts.iter().fold(BTreeSet::new(), |mut set, s| {
121                RequiredSignersSet::from(s).iter().for_each(|pk| {
122                    set.insert(*pk);
123                });
124                set
125            })
126        }
127        match script {
128            NativeScript::ScriptPubkey(spk) => {
129                let mut set = BTreeSet::new();
130                set.insert(spk.ed25519_key_hash);
131                set
132            }
133            NativeScript::ScriptAll(all) => from_scripts(&all.native_scripts),
134            NativeScript::ScriptAny(any) => from_scripts(&any.native_scripts),
135            NativeScript::ScriptNOfK(ofk) => from_scripts(&ofk.native_scripts),
136            _ => BTreeSet::new(),
137        }
138    }
139}
140
141impl NativeScript {
142    /// Returns an array of unique Ed25519KeyHashes
143    /// contained within this script recursively on any depth level.
144    /// The order of the keys in the result is not determined in any way.
145    pub fn get_required_signers(&self) -> Vec<Ed25519KeyHash> {
146        RequiredSignersSet::from(self).iter().cloned().collect()
147    }
148}
149
150impl TransactionWitnessSet {
151    pub fn add_all_witnesses(&mut self, other: Self) {
152        // TODO: should we do duplicate checking here?
153        if let Some(other_vkeys) = other.vkeywitnesses {
154            if let Some(vkeys) = &mut self.vkeywitnesses {
155                vkeys.extend(Vec::from(other_vkeys));
156            } else {
157                self.vkeywitnesses = Some(other_vkeys);
158            }
159        }
160        if let Some(other_native_scripts) = other.native_scripts {
161            if let Some(scripts) = &mut self.native_scripts {
162                scripts.extend(Vec::from(other_native_scripts));
163            } else {
164                self.native_scripts = Some(other_native_scripts);
165            }
166        }
167        if let Some(other_bootstraps) = other.bootstrap_witnesses {
168            if let Some(bootstraps) = &mut self.bootstrap_witnesses {
169                bootstraps.extend(Vec::from(other_bootstraps));
170            } else {
171                self.bootstrap_witnesses = Some(other_bootstraps);
172            }
173        }
174        if let Some(other_plutus_v1_scripts) = other.plutus_v1_scripts {
175            if let Some(scripts) = &mut self.plutus_v1_scripts {
176                scripts.extend(Vec::from(other_plutus_v1_scripts));
177            } else {
178                self.plutus_v1_scripts = Some(other_plutus_v1_scripts);
179            }
180        }
181        if let Some(other_plutus_v2_scripts) = other.plutus_v2_scripts {
182            if let Some(scripts) = &mut self.plutus_v2_scripts {
183                scripts.extend(Vec::from(other_plutus_v2_scripts));
184            } else {
185                self.plutus_v2_scripts = Some(other_plutus_v2_scripts);
186            }
187        }
188        if let Some(other_plutus_v3_scripts) = other.plutus_v3_scripts {
189            if let Some(scripts) = &mut self.plutus_v3_scripts {
190                scripts.extend(Vec::from(other_plutus_v3_scripts));
191            } else {
192                self.plutus_v3_scripts = Some(other_plutus_v3_scripts);
193            }
194        }
195        if let Some(other_plutus_datums) = other.plutus_datums {
196            if let Some(datums) = &mut self.plutus_datums {
197                datums.extend(Vec::from(other_plutus_datums));
198            } else {
199                self.plutus_datums = Some(other_plutus_datums);
200            }
201        }
202        if let Some(other_redeemers) = other.redeemers {
203            if let Some(redeemers) = &mut self.redeemers {
204                redeemers.extend(other_redeemers);
205            } else {
206                self.redeemers = Some(other_redeemers);
207            }
208        }
209    }
210
211    pub fn languages(&self) -> Vec<Language> {
212        let mut used_langs = vec![];
213        if self.plutus_v1_scripts.is_some() {
214            used_langs.push(Language::PlutusV1);
215        }
216        if self.plutus_v2_scripts.is_some() {
217            used_langs.push(Language::PlutusV2);
218        }
219        if self.plutus_v3_scripts.is_some() {
220            used_langs.push(Language::PlutusV3);
221        }
222        used_langs
223    }
224}