Skip to main content

pallas_primitives/conway/
script_data.rs

1use std::collections::BTreeMap;
2
3use super::{CostModel, PlutusData, Redeemers, WitnessSet};
4use pallas_codec::minicbor::{self, Encode};
5use pallas_codec::utils::{KeepRaw, NonEmptySet};
6use serde::{Deserialize, Serialize};
7
8pub type PlutusVersion = u8;
9
10#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
11pub struct LanguageViews(pub BTreeMap<PlutusVersion, CostModel>);
12
13impl FromIterator<(PlutusVersion, CostModel)> for LanguageViews {
14    fn from_iter<I: IntoIterator<Item = (PlutusVersion, CostModel)>>(iter: I) -> Self {
15        Self(iter.into_iter().collect())
16    }
17}
18
19impl<C> Encode<C> for LanguageViews {
20    fn encode<W: minicbor::encode::Write>(
21        &self,
22        e: &mut minicbor::Encoder<W>,
23        ctx: &mut C,
24    ) -> Result<(), minicbor::encode::Error<W::Error>> {
25        let order: Vec<u8> = self.0.keys().copied().collect();
26        let mut canonical_order: Vec<u8> = order.into_iter().filter(|&k| k != 0).collect();
27        canonical_order.sort();
28        // PlutusV1 is CBOR encoded as 0x4100 so it goes last
29        if self.0.contains_key(&0) {
30            canonical_order.push(0);
31        }
32
33        e.map(self.0.len() as u64)?;
34        for lang in canonical_order {
35            let cost_model = self.0.get(&lang).unwrap();
36            match lang {
37                0 => {
38                    let mut inner = vec![];
39                    let mut sub = minicbor::Encoder::new(&mut inner);
40                    sub.begin_array().unwrap();
41                    for v in cost_model.iter() {
42                        sub.encode_with(v, ctx).unwrap();
43                    }
44                    sub.end().unwrap();
45                    e.bytes(&minicbor::to_vec(0).unwrap())?;
46                    e.bytes(&inner)?;
47                }
48                _ => {
49                    e.encode(lang)?;
50                    e.encode(cost_model)?;
51                }
52            }
53        }
54        Ok(())
55    }
56}
57
58#[derive(Debug, Clone)]
59pub struct ScriptData<'b> {
60    pub redeemers: Option<Redeemers>,
61    pub datums: Option<KeepRaw<'b, NonEmptySet<KeepRaw<'b, PlutusData>>>>,
62    pub language_views: Option<LanguageViews>,
63}
64
65impl ScriptData<'_> {
66    pub fn hash(&self) -> pallas_crypto::hash::Hash<32> {
67        let mut buf = vec![];
68
69        if let Some(redeemers) = &self.redeemers {
70            minicbor::encode(redeemers, &mut buf).unwrap(); // infallible
71        } else {
72            buf.push(0xa0);
73        }
74
75        if let Some(datums) = &self.datums {
76            minicbor::encode(datums, &mut buf).unwrap(); // infallible
77        }
78
79        if let Some(language_views) = &self.language_views {
80            minicbor::encode(language_views, &mut buf).unwrap(); // infallible
81        } else {
82            buf.push(0xa0);
83        }
84
85        pallas_crypto::hash::Hasher::<256>::hash(&buf)
86    }
87}
88
89impl<'b> ScriptData<'b> {
90    pub fn build_for(
91        witness: &WitnessSet<'b>,
92        language_views_opt: &Option<LanguageViews>,
93    ) -> Option<Self> {
94        let redeemers = witness.redeemer.as_ref().map(|x| x.to_owned().unwrap());
95        let datums = witness.plutus_data.clone();
96
97        if redeemers.is_none() && datums.is_none() {
98            return None;
99        }
100
101        let language_views = if redeemers.is_some() && language_views_opt.is_some() {
102            language_views_opt.clone()
103        } else {
104            None
105        };
106
107        Some(ScriptData {
108            redeemers,
109            datums,
110            language_views,
111        })
112    }
113}
114
115#[cfg(test)]
116mod tests {
117    use std::sync::LazyLock;
118
119    use crate::conway::Tx;
120
121    use super::*;
122
123    static COST_MODEL_PLUTUS_V1: LazyLock<Vec<i64>> = LazyLock::new(|| {
124        vec![
125            100788, 420, 1, 1, 1000, 173, 0, 1, 1000, 59957, 4, 1, 11183, 32, 201305, 8356, 4,
126            16000, 100, 16000, 100, 16000, 100, 16000, 100, 16000, 100, 16000, 100, 100, 100,
127            16000, 100, 94375, 32, 132994, 32, 61462, 4, 72010, 178, 0, 1, 22151, 32, 91189, 769,
128            4, 2, 85848, 228465, 122, 0, 1, 1, 1000, 42921, 4, 2, 24548, 29498, 38, 1, 898148,
129            27279, 1, 51775, 558, 1, 39184, 1000, 60594, 1, 141895, 32, 83150, 32, 15299, 32,
130            76049, 1, 13169, 4, 22100, 10, 28999, 74, 1, 28999, 74, 1, 43285, 552, 1, 44749, 541,
131            1, 33852, 32, 68246, 32, 72362, 32, 7243, 32, 7391, 32, 11546, 32, 85848, 228465, 122,
132            0, 1, 1, 90434, 519, 0, 1, 74433, 32, 85848, 228465, 122, 0, 1, 1, 85848, 228465, 122,
133            0, 1, 1, 270652, 22588, 4, 1457325, 64566, 4, 20467, 1, 4, 0, 141992, 32, 100788, 420,
134            1, 1, 81663, 32, 59498, 32, 20142, 32, 24588, 32, 20744, 32, 25933, 32, 24623, 32,
135            53384111, 14333, 10,
136        ]
137    });
138
139    static COST_MODEL_PLUTUS_V2: LazyLock<Vec<i64>> = LazyLock::new(|| {
140        vec![
141            100788, 420, 1, 1, 1000, 173, 0, 1, 1000, 59957, 4, 1, 11183, 32, 201305, 8356, 4,
142            16000, 100, 16000, 100, 16000, 100, 16000, 100, 16000, 100, 16000, 100, 100, 100,
143            16000, 100, 94375, 32, 132994, 32, 61462, 4, 72010, 178, 0, 1, 22151, 32, 91189, 769,
144            4, 2, 85848, 228465, 122, 0, 1, 1, 1000, 42921, 4, 2, 24548, 29498, 38, 1, 898148,
145            27279, 1, 51775, 558, 1, 39184, 1000, 60594, 1, 141895, 32, 83150, 32, 15299, 32,
146            76049, 1, 13169, 4, 22100, 10, 28999, 74, 1, 28999, 74, 1, 43285, 552, 1, 44749, 541,
147            1, 33852, 32, 68246, 32, 72362, 32, 7243, 32, 7391, 32, 11546, 32, 85848, 228465, 122,
148            0, 1, 1, 90434, 519, 0, 1, 74433, 32, 85848, 228465, 122, 0, 1, 1, 85848, 228465, 122,
149            0, 1, 1, 955506, 213312, 0, 2, 270652, 22588, 4, 1457325, 64566, 4, 20467, 1, 4, 0,
150            141992, 32, 100788, 420, 1, 1, 81663, 32, 59498, 32, 20142, 32, 24588, 32, 20744, 32,
151            25933, 32, 24623, 32, 43053543, 10, 53384111, 14333, 10, 43574283, 26308, 10,
152        ]
153    });
154
155    static COST_MODEL_PLUTUS_V3: LazyLock<Vec<i64>> = LazyLock::new(|| {
156        vec![
157            100788, 420, 1, 1, 1000, 173, 0, 1, 1000, 59957, 4, 1, 11183, 32, 201305, 8356, 4,
158            16000, 100, 16000, 100, 16000, 100, 16000, 100, 16000, 100, 16000, 100, 100, 100,
159            16000, 100, 94375, 32, 132994, 32, 61462, 4, 72010, 178, 0, 1, 22151, 32, 91189, 769,
160            4, 2, 85848, 123203, 7305, -900, 1716, 549, 57, 85848, 0, 1, 1, 1000, 42921, 4, 2,
161            24548, 29498, 38, 1, 898148, 27279, 1, 51775, 558, 1, 39184, 1000, 60594, 1, 141895,
162            32, 83150, 32, 15299, 32, 76049, 1, 13169, 4, 22100, 10, 28999, 74, 1, 28999, 74, 1,
163            43285, 552, 1, 44749, 541, 1, 33852, 32, 68246, 32, 72362, 32, 7243, 32, 7391, 32,
164            11546, 32, 85848, 123203, 7305, -900, 1716, 549, 57, 85848, 0, 1, 90434, 519, 0, 1,
165            74433, 32, 85848, 123203, 7305, -900, 1716, 549, 57, 85848, 0, 1, 1, 85848, 123203,
166            7305, -900, 1716, 549, 57, 85848, 0, 1, 955506, 213312, 0, 2, 270652, 22588, 4,
167            1457325, 64566, 4, 20467, 1, 4, 0, 141992, 32, 100788, 420, 1, 1, 81663, 32, 59498, 32,
168            20142, 32, 24588, 32, 20744, 32, 25933, 32, 24623, 32, 43053543, 10, 53384111, 14333,
169            10, 43574283, 26308, 10, 16000, 100, 16000, 100, 962335, 18, 2780678, 6, 442008, 1,
170            52538055, 3756, 18, 267929, 18, 76433006, 8868, 18, 52948122, 18, 1995836, 36, 3227919,
171            12, 901022, 1, 166917843, 4307, 36, 284546, 36, 158221314, 26549, 36, 74698472, 36,
172            333849714, 1, 254006273, 72, 2174038, 72, 2261318, 64571, 4, 207616, 8310, 4, 1293828,
173            28716, 63, 0, 1, 1006041, 43623, 251, 0, 1, 100181, 726, 719, 0, 1, 100181, 726, 719,
174            0, 1, 100181, 726, 719, 0, 1, 107878, 680, 0, 1, 95336, 1, 281145, 18848, 0, 1, 180194,
175            159, 1, 1, 158519, 8942, 0, 1, 159378, 8813, 0, 1, 107490, 3298, 1, 106057, 655, 1,
176            1964219, 24520, 3,
177        ]
178    });
179
180    static TEST_VECTORS: LazyLock<Vec<(Vec<u8>, Option<LanguageViews>)>> = LazyLock::new(|| {
181        vec![
182            (
183                hex::decode(include_str!("../../../test_data/conway1.tx")).unwrap(),
184                Some(LanguageViews::from_iter([(
185                    1,
186                    COST_MODEL_PLUTUS_V2.clone(),
187                )])),
188            ),
189            (
190                hex::decode(include_str!("../../../test_data/conway2.tx")).unwrap(),
191                Some(LanguageViews::from_iter([(
192                    0,
193                    COST_MODEL_PLUTUS_V1.clone(),
194                )])),
195            ),
196            (
197                hex::decode(include_str!("../../../test_data/hydra-init.tx")).unwrap(),
198                Some(LanguageViews::from_iter([(
199                    1,
200                    COST_MODEL_PLUTUS_V2.clone(),
201                )])),
202            ),
203            (
204                hex::decode(include_str!("../../../test_data/datum-only.tx")).unwrap(),
205                None,
206            ),
207            (
208                hex::decode(include_str!("../../../test_data/conway9.tx")).unwrap(),
209                Some(LanguageViews::from_iter([
210                    (0, COST_MODEL_PLUTUS_V1.clone()),
211                    (1, COST_MODEL_PLUTUS_V2.clone()),
212                    (2, COST_MODEL_PLUTUS_V3.clone()),
213                ])),
214            ),
215        ]
216    });
217
218    fn assert_script_data_hash_matches(bytes: &[u8], language_views_opt: &Option<LanguageViews>) {
219        let tx: Tx = pallas_codec::minicbor::decode(bytes).unwrap();
220
221        let witness = tx.transaction_witness_set.clone().unwrap();
222
223        let script_data = ScriptData::build_for(&witness, language_views_opt).unwrap();
224
225        let obtained = script_data.hash();
226
227        let expected = tx.transaction_body.script_data_hash.unwrap();
228
229        assert_eq!(obtained, expected);
230    }
231
232    #[test]
233    fn test_script_data_hash() {
234        for (bytes, language_views_opt) in TEST_VECTORS.iter() {
235            assert_script_data_hash_matches(bytes, language_views_opt);
236        }
237    }
238}