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#[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 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 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 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#[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 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 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 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 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
556pub 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
568pub 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}