cml_chain/auxdata/
metadata.rs

1use crate::json::metadatums::{
2    decode_metadatum_to_json_value, encode_json_value_to_metadatum, MetadataJsonSchema,
3};
4use cbor_event::{de::Deserializer, se::Serializer};
5use cml_core::{
6    error::{DeserializeError, DeserializeFailure},
7    serialization::{fit_sz, Deserialize, LenEncoding, Serialize, StringEncoding},
8    Int,
9};
10use derivative::Derivative;
11
12use std::io::{BufRead, Seek, Write};
13
14pub type TransactionMetadatumLabel = u64;
15
16pub const METADATA_MAX_LEN: usize = 64;
17
18/// Collection of TransactionMetadatums indexed by TransactionMetadatumLabels
19/// Handles the extremely rare edge-case of in previous generations allowing
20/// duplicate metadatum labels.
21#[derive(Clone, Debug, Default, serde::Deserialize, serde::Serialize, schemars::JsonSchema)]
22pub struct Metadata {
23    pub entries: Vec<(TransactionMetadatumLabel, TransactionMetadatum)>,
24    #[serde(skip)]
25    pub encodings: Option<MetadataEncoding>,
26}
27
28impl Metadata {
29    pub fn new() -> Self {
30        Self::default()
31    }
32
33    pub fn len(&self) -> usize {
34        self.entries.len()
35    }
36
37    pub fn is_empty(&self) -> bool {
38        self.entries.is_empty()
39    }
40
41    /// Replaces all metadatums of a given label, if any exist.
42    pub fn set(&mut self, label: TransactionMetadatumLabel, datum: TransactionMetadatum) {
43        self.entries.retain(|(l, _)| *l != label);
44        self.entries.push((label, datum));
45    }
46
47    /// Gets the Metadatum corresponding to a given label, if it exists.
48    /// Note: In the case of duplicate labels this only returns the first metadatum.
49    /// This is an extremely rare occurence on-chain but can happen.
50    pub fn get(&self, label: TransactionMetadatumLabel) -> Option<&TransactionMetadatum> {
51        self.entries
52            .iter()
53            .find(|(l, _)| *l == label)
54            .map(|(_, md)| md)
55    }
56
57    /// In the extremely unlikely situation there are duplicate labels, this gets all of a single label
58    pub fn get_all(&self, label: TransactionMetadatumLabel) -> Option<Vec<&TransactionMetadatum>> {
59        let matches = self
60            .entries
61            .iter()
62            .filter_map(|(l, md)| if *l == label { Some(md) } else { None })
63            .collect::<Vec<_>>();
64        if matches.is_empty() {
65            None
66        } else {
67            Some(matches)
68        }
69    }
70}
71
72#[derive(Clone, Debug, Default)]
73pub struct MetadataEncoding {
74    pub len_encoding: LenEncoding,
75    pub label_encodings: Vec<cbor_event::Sz>,
76}
77
78impl Serialize for Metadata {
79    fn serialize<'se, W: Write>(
80        &self,
81        serializer: &'se mut Serializer<W>,
82        force_canonical: bool,
83    ) -> cbor_event::Result<&'se mut Serializer<W>> {
84        serializer.write_map_sz(
85            self.encodings
86                .as_ref()
87                .map(|encs| encs.len_encoding)
88                .unwrap_or_default()
89                .to_len_sz(self.entries.len() as u64, force_canonical),
90        )?;
91        let mut key_order = Vec::new();
92        for (i, (label, datum)) in self.entries.iter().enumerate() {
93            let mut buf = cbor_event::se::Serializer::new_vec();
94            let metadata_key_encoding = self
95                .encodings
96                .as_ref()
97                .and_then(|encs| encs.label_encodings.get(i))
98                .cloned();
99            buf.write_unsigned_integer_sz(
100                *label,
101                fit_sz(*label, metadata_key_encoding, force_canonical),
102            )?;
103            key_order.push((buf.finalize(), label, datum));
104        }
105        if force_canonical {
106            key_order.sort_by(|(lhs_bytes, _, _), (rhs_bytes, _, _)| {
107                match lhs_bytes.len().cmp(&rhs_bytes.len()) {
108                    std::cmp::Ordering::Equal => lhs_bytes.cmp(rhs_bytes),
109                    diff_ord => diff_ord,
110                }
111            });
112        }
113        for (key_bytes, _key, value) in key_order {
114            serializer.write_raw_bytes(&key_bytes)?;
115            value.serialize(serializer, force_canonical)?;
116        }
117        self.encodings
118            .as_ref()
119            .map(|encs| encs.len_encoding)
120            .unwrap_or_default()
121            .end(serializer, force_canonical)
122    }
123}
124
125impl Deserialize for Metadata {
126    fn deserialize<R: BufRead + Seek>(raw: &mut Deserializer<R>) -> Result<Self, DeserializeError> {
127        let mut entries = Vec::new();
128        let len = raw.map_sz()?;
129        let len_encoding = len.into();
130        let mut label_encodings = Vec::new();
131        while match len {
132            cbor_event::LenSz::Len(n, _) => (entries.len() as u64) < n,
133            cbor_event::LenSz::Indefinite => true,
134        } {
135            if raw.cbor_type()? == cbor_event::Type::Special {
136                assert_eq!(raw.special()?, cbor_event::Special::Break);
137                break;
138            }
139            let (metadatum_label, label_encoding) = raw.unsigned_integer_sz()?;
140            let metadatum = TransactionMetadatum::deserialize(raw)?;
141            entries.push((metadatum_label, metadatum));
142            label_encodings.push(label_encoding);
143        }
144        Ok(Self {
145            entries,
146            encodings: Some(MetadataEncoding {
147                len_encoding,
148                label_encodings,
149            }),
150        })
151    }
152}
153
154/// Handles the extremely rare (2 total instances on mainnet) edge-case of in
155/// previous generations allowing duplicate metadatum keys.
156#[derive(Clone, Debug, Default, Derivative)]
157#[derivative(Eq, PartialEq, Ord, PartialOrd, Hash)]
158pub struct MetadatumMap {
159    pub entries: Vec<(TransactionMetadatum, TransactionMetadatum)>,
160    #[derivative(
161        PartialEq = "ignore",
162        Ord = "ignore",
163        PartialOrd = "ignore",
164        Hash = "ignore"
165    )]
166    pub entries_encoding: LenEncoding,
167}
168
169impl MetadatumMap {
170    pub fn new() -> Self {
171        Self::default()
172    }
173
174    pub fn len(&self) -> usize {
175        self.entries.len()
176    }
177
178    pub fn is_empty(&self) -> bool {
179        self.entries.is_empty()
180    }
181
182    /// Replaces all metadatums of a given key, if any exist.
183    pub fn set(&mut self, key: TransactionMetadatum, datum: TransactionMetadatum) {
184        self.entries.retain(|(k, _)| *k != key);
185        self.entries.push((key, datum));
186    }
187
188    /// Gets the Metadatum corresponding to a given key, if it exists.
189    /// Note: In the case of duplicate keys this only returns the first metadatum.
190    /// This is an extremely rare occurence (2 total on mainnet) on-chain but can happen.
191    pub fn get(&self, key: &TransactionMetadatum) -> Option<&TransactionMetadatum> {
192        self.entries
193            .iter()
194            .find(|(k, _)| *k == *key)
195            .map(|(_, md)| md)
196    }
197
198    /// In the extremely unlikely situation there are duplicate keys, this gets all of a single key
199    pub fn get_all(&self, key: &TransactionMetadatum) -> Option<Vec<&TransactionMetadatum>> {
200        let matches = self
201            .entries
202            .iter()
203            .filter_map(|(k, md)| if *k == *key { Some(md) } else { None })
204            .collect::<Vec<_>>();
205        if matches.is_empty() {
206            None
207        } else {
208            Some(matches)
209        }
210    }
211
212    /// Gets the Metadatum by string only. Convenience functionality for get()
213    pub fn get_str(&self, key: &str) -> Option<&TransactionMetadatum> {
214        self.get(&TransactionMetadatum::new_text(key.to_owned()).ok()?)
215    }
216}
217
218impl Serialize for MetadatumMap {
219    fn serialize<'se, W: Write>(
220        &self,
221        serializer: &'se mut Serializer<W>,
222        force_canonical: bool,
223    ) -> cbor_event::Result<&'se mut Serializer<W>> {
224        serializer.write_map_sz(
225            self.entries_encoding
226                .to_len_sz(self.entries.len() as u64, force_canonical),
227        )?;
228        let mut key_order = self
229            .entries
230            .iter()
231            .map(|(k, v)| {
232                let mut buf = cbor_event::se::Serializer::new_vec();
233                k.serialize(&mut buf, force_canonical)?;
234                Ok((buf.finalize(), k, v))
235            })
236            .collect::<Result<Vec<(Vec<u8>, &_, &_)>, cbor_event::Error>>()?;
237        if force_canonical {
238            key_order.sort_by(|(lhs_bytes, _, _), (rhs_bytes, _, _)| {
239                match lhs_bytes.len().cmp(&rhs_bytes.len()) {
240                    std::cmp::Ordering::Equal => lhs_bytes.cmp(rhs_bytes),
241                    diff_ord => diff_ord,
242                }
243            });
244        }
245        for (key_bytes, _key, value) in key_order {
246            serializer.write_raw_bytes(&key_bytes)?;
247            value.serialize(serializer, force_canonical)?;
248        }
249        self.entries_encoding.end(serializer, force_canonical)
250    }
251}
252
253impl Deserialize for MetadatumMap {
254    fn deserialize<R: BufRead + Seek>(raw: &mut Deserializer<R>) -> Result<Self, DeserializeError> {
255        let mut entries = Vec::new();
256        let entries_len = raw.map_sz()?;
257        let entries_encoding = entries_len.into();
258        while match entries_len {
259            cbor_event::LenSz::Len(n, _) => (entries.len() as u64) < n,
260            cbor_event::LenSz::Indefinite => true,
261        } {
262            if raw.cbor_type()? == cbor_event::Type::Special {
263                assert_eq!(raw.special()?, cbor_event::Special::Break);
264                break;
265            }
266            let key = TransactionMetadatum::deserialize(raw)?;
267            let value = TransactionMetadatum::deserialize(raw)?;
268            entries.push((key, value));
269        }
270        Ok(Self {
271            entries,
272            entries_encoding,
273        })
274    }
275}
276
277#[derive(Clone, Debug, Derivative)]
278#[derivative(
279    Eq,
280    PartialEq,
281    Ord = "feature_allow_slow_enum",
282    PartialOrd = "feature_allow_slow_enum",
283    Hash
284)]
285pub enum TransactionMetadatum {
286    Map(MetadatumMap),
287    List {
288        elements: Vec<TransactionMetadatum>,
289        #[derivative(
290            PartialEq = "ignore",
291            Ord = "ignore",
292            PartialOrd = "ignore",
293            Hash = "ignore"
294        )]
295        elements_encoding: LenEncoding,
296    },
297    Int(Int),
298    Bytes {
299        bytes: Vec<u8>,
300        #[derivative(
301            PartialEq = "ignore",
302            Ord = "ignore",
303            PartialOrd = "ignore",
304            Hash = "ignore"
305        )]
306        bytes_encoding: StringEncoding,
307    },
308    Text {
309        text: String,
310        #[derivative(
311            PartialEq = "ignore",
312            Ord = "ignore",
313            PartialOrd = "ignore",
314            Hash = "ignore"
315        )]
316        text_encoding: StringEncoding,
317    },
318}
319
320impl TransactionMetadatum {
321    pub fn new_map(map: MetadatumMap) -> Self {
322        Self::Map(map)
323    }
324
325    pub fn new_list(elements: Vec<TransactionMetadatum>) -> Self {
326        Self::List {
327            elements,
328            elements_encoding: LenEncoding::default(),
329        }
330    }
331
332    pub fn new_int(int: Int) -> Self {
333        Self::Int(int)
334    }
335
336    pub fn new_bytes(bytes: Vec<u8>) -> Result<Self, DeserializeError> {
337        if bytes.len() > METADATA_MAX_LEN {
338            return Err(DeserializeFailure::RangeCheck {
339                found: bytes.len() as isize,
340                min: None,
341                max: Some(METADATA_MAX_LEN as isize),
342            }
343            .into());
344        }
345        Ok(Self::Bytes {
346            bytes,
347            bytes_encoding: StringEncoding::default(),
348        })
349    }
350
351    pub fn new_text(text: String) -> Result<Self, DeserializeError> {
352        if text.len() > METADATA_MAX_LEN {
353            return Err(DeserializeFailure::RangeCheck {
354                found: text.len() as isize,
355                min: None,
356                max: Some(METADATA_MAX_LEN as isize),
357            }
358            .into());
359        }
360        Ok(Self::Text {
361            text,
362            text_encoding: StringEncoding::default(),
363        })
364    }
365
366    pub fn as_map(&self) -> Option<&MetadatumMap> {
367        match self {
368            Self::Map(map) => Some(map),
369            _ => None,
370        }
371    }
372
373    pub fn as_list(&self) -> Option<&Vec<TransactionMetadatum>> {
374        match self {
375            Self::List { elements, .. } => Some(elements),
376            _ => None,
377        }
378    }
379
380    pub fn as_int(&self) -> Option<&Int> {
381        match self {
382            Self::Int(x) => Some(x),
383            _ => None,
384        }
385    }
386
387    pub fn as_bytes(&self) -> Option<&Vec<u8>> {
388        match self {
389            Self::Bytes { bytes, .. } => Some(bytes),
390            _ => None,
391        }
392    }
393
394    pub fn as_text(&self) -> Option<&String> {
395        match self {
396            Self::Text { text, .. } => Some(text),
397            _ => None,
398        }
399    }
400}
401
402impl serde::Serialize for TransactionMetadatum {
403    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
404    where
405        S: serde::Serializer,
406    {
407        let json_value = decode_metadatum_to_json_value(self, MetadataJsonSchema::DetailedSchema)
408            .expect("DetailedSchema can represent everything");
409        serde_json::Value::from(json_value).serialize(serializer)
410    }
411}
412
413impl<'de> serde::de::Deserialize<'de> for TransactionMetadatum {
414    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
415    where
416        D: serde::de::Deserializer<'de>,
417    {
418        let serde_json_value =
419            <serde_json::Value as serde::de::Deserialize>::deserialize(deserializer)?;
420        let json_value = crate::json::json_serialize::Value::from(serde_json_value);
421        encode_json_value_to_metadatum(json_value.clone(), MetadataJsonSchema::DetailedSchema)
422            .map_err(|_e| {
423                serde::de::Error::invalid_value(
424                    (&json_value).into(),
425                    &"invalid tx metadatum (cardano-node JSON format)",
426                )
427            })
428    }
429}
430
431impl schemars::JsonSchema for TransactionMetadatum {
432    fn schema_name() -> String {
433        String::from("TransactionMetadatum")
434    }
435
436    fn json_schema(_gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
437        schemars::schema::Schema::from(schemars::schema::SchemaObject::new_ref(
438            "TransactionMetadatum".to_owned(),
439        ))
440    }
441
442    fn is_referenceable() -> bool {
443        true
444    }
445}
446
447impl Serialize for TransactionMetadatum {
448    fn serialize<'se, W: Write>(
449        &self,
450        serializer: &'se mut Serializer<W>,
451        force_canonical: bool,
452    ) -> cbor_event::Result<&'se mut Serializer<W>> {
453        match self {
454            TransactionMetadatum::Map(map) => map.serialize(serializer, force_canonical),
455            TransactionMetadatum::List {
456                elements,
457                elements_encoding,
458            } => {
459                serializer.write_array_sz(
460                    elements_encoding.to_len_sz(elements.len() as u64, force_canonical),
461                )?;
462                for element in elements.iter() {
463                    element.serialize(serializer, force_canonical)?;
464                }
465                elements_encoding.end(serializer, force_canonical)
466            }
467            TransactionMetadatum::Int(int) => int.serialize(serializer, force_canonical),
468            TransactionMetadatum::Bytes {
469                bytes,
470                bytes_encoding,
471            } => serializer.write_bytes_sz(
472                bytes,
473                bytes_encoding.to_str_len_sz(bytes.len() as u64, force_canonical),
474            ),
475            TransactionMetadatum::Text {
476                text,
477                text_encoding,
478            } => serializer.write_text_sz(
479                text,
480                text_encoding.to_str_len_sz(text.len() as u64, force_canonical),
481            ),
482        }
483    }
484}
485
486impl Deserialize for TransactionMetadatum {
487    fn deserialize<R: BufRead + Seek>(raw: &mut Deserializer<R>) -> Result<Self, DeserializeError> {
488        (|| -> Result<_, DeserializeError> {
489            match raw.cbor_type()? {
490                cbor_event::Type::Map => MetadatumMap::deserialize(raw).map(Self::Map),
491                cbor_event::Type::Array => {
492                    let mut elements_arr = Vec::new();
493                    let len = raw.array_sz()?;
494                    let elements_encoding = len.into();
495                    while match len {
496                        cbor_event::LenSz::Len(n, _) => (elements_arr.len() as u64) < n,
497                        cbor_event::LenSz::Indefinite => true,
498                    } {
499                        if raw.cbor_type()? == cbor_event::Type::Special {
500                            assert_eq!(raw.special()?, cbor_event::Special::Break);
501                            break;
502                        }
503                        elements_arr.push(TransactionMetadatum::deserialize(raw)?);
504                    }
505                    Ok(Self::List {
506                        elements: elements_arr,
507                        elements_encoding,
508                    })
509                }
510                cbor_event::Type::UnsignedInteger | cbor_event::Type::NegativeInteger => {
511                    Int::deserialize(raw).map(Self::Int)
512                }
513                cbor_event::Type::Bytes => raw
514                    .bytes_sz()
515                    .map_err(Into::<DeserializeError>::into)
516                    .and_then(|(bytes, enc)| {
517                        if bytes.len() > METADATA_MAX_LEN {
518                            Err(DeserializeFailure::RangeCheck {
519                                found: bytes.len() as isize,
520                                min: None,
521                                max: Some(METADATA_MAX_LEN as isize),
522                            }
523                            .into())
524                        } else {
525                            Ok(Self::Bytes {
526                                bytes,
527                                bytes_encoding: StringEncoding::from(enc),
528                            })
529                        }
530                    }),
531                cbor_event::Type::Text => raw
532                    .text_sz()
533                    .map_err(Into::<DeserializeError>::into)
534                    .and_then(|(text, enc)| {
535                        if text.len() > METADATA_MAX_LEN {
536                            Err(DeserializeFailure::RangeCheck {
537                                found: text.len() as isize,
538                                min: None,
539                                max: Some(METADATA_MAX_LEN as isize),
540                            }
541                            .into())
542                        } else {
543                            Ok(Self::Text {
544                                text,
545                                text_encoding: StringEncoding::from(enc),
546                            })
547                        }
548                    }),
549                _ => Err(DeserializeFailure::NoVariantMatched.into()),
550            }
551        })()
552        .map_err(|e| e.annotate("TransactionMetadatum"))
553    }
554}
555
556/// encodes arbitrary bytes into chunks of 64 bytes (the limit for bytes) as a list to be valid Metadata
557pub fn encode_arbitrary_bytes_as_metadatum(bytes: &[u8]) -> TransactionMetadatum {
558    let mut list = Vec::new();
559    for chunk in bytes.chunks(METADATA_MAX_LEN) {
560        list.push(
561            TransactionMetadatum::new_bytes(chunk.to_vec())
562                .expect("this should never fail as we are already chunking it"),
563        );
564    }
565    TransactionMetadatum::new_list(list)
566}
567
568/// decodes from chunks of bytes in a list to a byte vector if that is the metadata format, otherwise returns None
569pub fn decode_arbitrary_bytes_from_metadatum(metadata: &TransactionMetadatum) -> Option<Vec<u8>> {
570    let mut bytes = Vec::new();
571    for elem in metadata.as_list()? {
572        bytes.extend(elem.as_bytes()?.iter());
573    }
574    Some(bytes)
575}
576
577#[cfg(test)]
578mod tests {
579    use super::*;
580
581    #[test]
582    fn metadata_duplicate_labels() {
583        let bytes_hex = "a219270fa16474657374747365636f6e64206d657461646174612066696c6519270fa16474657374736669727374206d657461646174612066696c65";
584        let md = Metadata::from_cbor_bytes(&hex::decode(bytes_hex).unwrap()).unwrap();
585        assert_eq!(bytes_hex, hex::encode(md.to_cbor_bytes()));
586    }
587
588    #[test]
589    fn metdatum_duplicate_keys() {
590        let bytes_hex = "a100a567536572766963656c4c4946542042616c6c6f7473685175657374696f6e6d536f6d65207175657374696f6e66417574686f7273736f6d652d677569642d686572652d736f6f6e64547970656653696e676c656743686f69636573a26643686f6963656b536f6d652043686f6963656643686f69636573536f6d6520416e6f746865722043686f696365";
591        let md = Metadata::from_cbor_bytes(&hex::decode(bytes_hex).unwrap()).unwrap();
592        assert_eq!(bytes_hex, hex::encode(md.to_cbor_bytes()));
593    }
594
595    #[test]
596    fn binary_encoding() {
597        let input_bytes = (0..1000).map(|x| x as u8).collect::<Vec<u8>>();
598        let metadata = encode_arbitrary_bytes_as_metadatum(input_bytes.as_ref());
599        let output_bytes = decode_arbitrary_bytes_from_metadatum(&metadata).expect("decode failed");
600        assert_eq!(input_bytes, output_bytes);
601    }
602
603    #[test]
604    fn metadatum_default_json() {
605        let json_str = "{\"map\":[{\"k\":{\"list\":[{\"map\":[{\"k\":{\"int\":5},\"v\":{\"int\":-7}},{\"k\":{\"string\":\"hello\"},\"v\":{\"string\":\"world\"}}]},{\"bytes\":\"ff00ff00\"}]},\"v\":{\"int\":5}}]}";
606        let metadatum: TransactionMetadatum = serde_json::from_str(json_str).unwrap();
607        let roundtrip_str = serde_json::to_string(&metadatum).unwrap();
608        assert_eq!(json_str, roundtrip_str);
609    }
610}