cml_chain/assets/
utils.rs

1use crate::PolicyId;
2use cbor_event::{de::Deserializer, se::Serializer};
3use cml_core::{
4    error::{DeserializeError, DeserializeFailure, Key},
5    ordered_hash_map::OrderedHashMap,
6    serialization::{fit_sz, CBORReadLen, Deserialize, LenEncoding, Serialize, StringEncoding},
7    ArithmeticError,
8};
9use cml_crypto::{RawBytesEncoding, ScriptHash};
10use std::io::{BufRead, Seek, Write};
11use std::{
12    cmp::PartialOrd,
13    convert::{TryFrom, TryInto},
14};
15
16use std::collections::BTreeMap;
17
18use super::AssetName;
19
20pub type Coin = u64;
21
22/// This should technically now allow 0 but this would make the API harder to use.
23/// At least for now we'll keep it as Coin (aka u64) as an alias with this comment here.
24/// Later on it could potentially be redone to its own struct.
25pub type NonZeroInt64 = i64;
26
27/// This should technically now allow 0 but this would make the API harder to use.
28/// At least for now we'll keep it as Coin (aka u64) as an alias with this comment here.
29/// Later on it could potentially be redone to its own struct.
30pub type PositiveCoin = Coin;
31
32#[derive(Debug, thiserror::Error)]
33pub enum AssetArithmeticError {
34    #[error("Arithmetic failed: {0}")]
35    Arithmetic(#[from] ArithmeticError),
36    #[error("Asset {0:?} doesn't exist")]
37    AssetDoesntExist(AssetName),
38    #[error("PolicyId {0:?} doesn't exist")]
39    PolicyIdDoesntExist(PolicyId),
40}
41
42impl TryFrom<&str> for AssetName {
43    type Error = DeserializeError;
44
45    fn try_from(utf8_str: &str) -> Result<Self, Self::Error> {
46        Self::new(utf8_str.as_bytes().to_vec())
47    }
48}
49
50impl<'a> TryInto<&'a str> for &'a AssetName {
51    type Error = std::str::Utf8Error;
52
53    fn try_into(self) -> Result<&'a str, Self::Error> {
54        std::str::from_utf8(self.to_raw_bytes())
55    }
56}
57
58impl RawBytesEncoding for AssetName {
59    fn to_raw_bytes(&self) -> &[u8] {
60        self.inner.as_ref()
61    }
62
63    fn from_raw_bytes(bytes: &[u8]) -> Result<Self, DeserializeError> {
64        Self::new(bytes.to_vec())
65    }
66}
67
68/// Bundle of assets within range of T, grouped by PolicyID then AssetName
69#[derive(
70    Clone, Default, PartialEq, Hash, serde::Deserialize, serde::Serialize, schemars::JsonSchema,
71)]
72pub struct AssetBundle<T>(OrderedHashMap<PolicyId, OrderedHashMap<AssetName, T>>);
73
74impl<T: std::fmt::Debug> std::fmt::Debug for AssetBundle<T> {
75    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
76        let mut ds = f.debug_struct(if self.0.iter().any(|(_, a)| !a.is_empty()) {
77            ""
78        } else {
79            "{}"
80        });
81        for (pid, assets) in self.0.iter() {
82            let pid_hex = hex::encode(pid.to_raw_bytes());
83            for (an, val) in assets.iter() {
84                let an_hex = hex::encode(an.to_raw_bytes());
85                let an_name = if an_hex.len() > 8 {
86                    format!(
87                        "{}..{}",
88                        an_hex.get(0..3).unwrap(),
89                        an_hex.get(an_hex.len() - 2..an_hex.len() - 1).unwrap()
90                    )
91                } else {
92                    an_hex
93                };
94                ds.field(
95                    &format!(
96                        "{}..{}:{}",
97                        pid_hex.get(0..3).unwrap(),
98                        pid_hex.get(pid_hex.len() - 4..pid_hex.len() - 1).unwrap(),
99                        an_name
100                    ),
101                    val,
102                );
103            }
104        }
105        ds.finish()
106    }
107}
108
109impl<T: Default> AssetBundle<T> {
110    pub fn new() -> Self {
111        Self::default()
112    }
113}
114
115impl<T> From<OrderedHashMap<PolicyId, OrderedHashMap<AssetName, T>>> for AssetBundle<T> {
116    fn from(bundle: OrderedHashMap<PolicyId, OrderedHashMap<AssetName, T>>) -> Self {
117        Self(bundle)
118    }
119}
120
121impl<T> std::ops::Deref for AssetBundle<T> {
122    type Target = OrderedHashMap<PolicyId, OrderedHashMap<AssetName, T>>;
123
124    fn deref(&self) -> &Self::Target {
125        &self.0
126    }
127}
128
129impl<T> std::ops::DerefMut for AssetBundle<T> {
130    fn deref_mut(&mut self) -> &mut Self::Target {
131        &mut self.0
132    }
133}
134
135/// Subtraction over a numerical type where the type's minimum is returned if underflow would occur.
136pub trait ClampedSub {
137    fn clamped_sub(&self, rhs: &Self) -> Self;
138}
139
140impl<T: num::CheckedSub + num::Bounded + num::Zero + Ord> ClampedSub for T {
141    fn clamped_sub(&self, rhs: &Self) -> Self {
142        self.checked_sub(rhs).unwrap_or_else(|| {
143            if *rhs < T::zero() {
144                T::max_value()
145            } else {
146                T::min_value()
147            }
148        })
149    }
150}
151
152impl<T> ClampedSub for AssetBundle<T>
153where
154    T: num::CheckedAdd + num::CheckedSub + num::Zero + num::Bounded + Copy + Clone + ClampedSub,
155{
156    fn clamped_sub(&self, rhs: &Self) -> Self {
157        let mut bundle = self.clone();
158        for (policy, rhs_assets) in rhs.iter() {
159            for (asset_name, rhs_amount) in rhs_assets.iter() {
160                match bundle.get_mut(policy) {
161                    Some(lhs_assets) => match lhs_assets.get_mut(asset_name) {
162                        Some(lhs_amount) => match lhs_amount.checked_sub(rhs_amount) {
163                            Some(new_lhs_amount) => {
164                                if new_lhs_amount.is_zero() {
165                                    lhs_assets.remove(asset_name);
166                                    if lhs_assets.is_empty() {
167                                        bundle.remove(policy);
168                                    }
169                                } else {
170                                    *lhs_amount = new_lhs_amount;
171                                }
172                            }
173                            None => {
174                                if T::min_value().is_zero() {
175                                    // if underflow and unsigned, remove
176                                    lhs_assets.remove(asset_name);
177                                    if lhs_assets.is_empty() {
178                                        bundle.remove(policy);
179                                    }
180                                } else {
181                                    // otherwise keep it as minimum
182                                    *lhs_amount = T::min_value();
183                                }
184                            }
185                        },
186                        None => {
187                            // asset name is missing from left hand side
188                            if !T::min_value().is_zero() {
189                                bundle.set(
190                                    *policy,
191                                    asset_name.clone(),
192                                    T::zero().clamped_sub(rhs_amount),
193                                );
194                            }
195                        }
196                    },
197                    None => {
198                        // policy id missing from left hand side
199                        if !T::min_value().is_zero() {
200                            bundle.set(
201                                *policy,
202                                asset_name.clone(),
203                                T::zero().clamped_sub(rhs_amount),
204                            );
205                        }
206                    }
207                }
208            }
209        }
210        bundle
211    }
212}
213
214impl<T> AssetBundle<T>
215where
216    T: num::CheckedAdd + num::CheckedSub + num::Zero + num::Bounded + Copy + Clone,
217{
218    /// Set the value of policy_id:asset_name to value.
219    /// Returns the previous value, or None if it didn't exist
220    pub fn set(&mut self, policy_id: PolicyId, asset_name: AssetName, value: T) -> Option<T> {
221        self.0
222            .entry(policy_id)
223            .or_default()
224            .insert(asset_name, value)
225    }
226
227    /// Get the value of policy_id:asset_name if it exists.
228    pub fn get(&self, policy_id: &PolicyId, asset_name: &AssetName) -> Option<T> {
229        self.0
230            .get(policy_id)
231            .and_then(|assets| assets.get(asset_name))
232            .copied()
233    }
234
235    /// Adds to bundles together, checking value bounds.
236    /// Does not modify self, and instead returns the result.
237    pub fn checked_add(&self, rhs: &Self) -> Result<Self, AssetArithmeticError> {
238        use linked_hash_map::Entry;
239        let mut bundle = self.0.clone();
240        for (policy, assets) in rhs.0.iter() {
241            for (asset_name, amount) in assets.iter() {
242                match bundle.entry(*policy) {
243                    Entry::Occupied(mut assets) => {
244                        match assets.get_mut().entry(asset_name.clone()) {
245                            Entry::Occupied(mut assets2) => {
246                                let current = assets2.get_mut();
247                                *current = current
248                                    .checked_add(amount)
249                                    .ok_or(ArithmeticError::IntegerOverflow)?;
250                            }
251                            Entry::Vacant(vacant_entry) => {
252                                vacant_entry.insert(*amount);
253                            }
254                        }
255                    }
256                    Entry::Vacant(entry) => {
257                        let mut assets = OrderedHashMap::new();
258                        assets.insert(asset_name.clone(), *amount);
259                        entry.insert(assets);
260                    }
261                }
262            }
263        }
264        Ok(Self(bundle))
265    }
266
267    /// Subtracts rhs from this bundle.
268    /// This does not modify self, and instead returns the result.
269    /// Use clamped_sub (ClampedSub trait) if you need to only try to remove assets when they exist
270    /// and ignore them when they don't.
271    pub fn checked_sub(&self, rhs: &Self) -> Result<Self, AssetArithmeticError> {
272        let mut bundle = self.0.clone();
273        for (policy, rhs_assets) in rhs.iter() {
274            for (asset_name, rhs_amount) in rhs_assets.iter() {
275                match bundle.get_mut(policy) {
276                    Some(lhs_assets) => match lhs_assets.get_mut(asset_name) {
277                        Some(lhs_amount) => match lhs_amount.checked_sub(rhs_amount) {
278                            Some(new_lhs_amount) => {
279                                if new_lhs_amount.is_zero() {
280                                    lhs_assets.remove(asset_name);
281                                    if lhs_assets.is_empty() {
282                                        bundle.remove(policy);
283                                    }
284                                } else {
285                                    *lhs_amount = new_lhs_amount;
286                                }
287                            }
288                            None => {
289                                // underflow
290                                return Err(ArithmeticError::IntegerUnderflow.into());
291                            }
292                        },
293                        None => {
294                            // asset name is missing from left hand side
295                            return Err(AssetArithmeticError::AssetDoesntExist(asset_name.clone()));
296                        }
297                    },
298                    None => {
299                        // policy id missing from left hand side
300                        return Err(AssetArithmeticError::PolicyIdDoesntExist(*policy));
301                    }
302                }
303            }
304        }
305        Ok(Self(bundle))
306    }
307}
308
309pub type Mint = AssetBundle<NonZeroInt64>;
310
311pub type MultiAsset = AssetBundle<PositiveCoin>;
312
313impl Mint {
314    fn as_multiasset(&self, is_positive: bool) -> MultiAsset {
315        self.0
316            .iter()
317            .fold(MultiAsset::default(), |mut acc, (policy, assets)| {
318                let new_assets =
319                    assets
320                        .iter()
321                        .fold(OrderedHashMap::new(), |mut acc, (asset, value)| {
322                            if (*value >= 0) == is_positive {
323                                acc.insert(asset.clone(), value.unsigned_abs());
324                            }
325                            acc
326                        });
327                if !assets.is_empty() {
328                    acc.insert(*policy, new_assets);
329                }
330                acc
331            })
332    }
333
334    /// Returns the multiasset where only positive (minting) entries are present
335    pub fn as_positive_multiasset(&self) -> MultiAsset {
336        self.as_multiasset(true)
337    }
338
339    /// Returns the multiasset where only negative (burning) entries are present
340    pub fn as_negative_multiasset(&self) -> MultiAsset {
341        self.as_multiasset(false)
342    }
343}
344
345// note: we purposefully don't derive or implement Ord for Value to avoid potentially confusing
346// orderings that don't obey Cardano semantics i.e. if x >= y then input x can cover cost y
347// If you need to use Value or something in a tree map please consider using a hash map instead.
348#[derive(
349    Clone, Debug, derivative::Derivative, serde::Deserialize, serde::Serialize, schemars::JsonSchema,
350)]
351#[derivative(Hash, Eq, PartialEq, PartialOrd)]
352pub struct Value {
353    pub coin: PositiveCoin,
354    pub multiasset: MultiAsset,
355    #[serde(skip)]
356    #[derivative(Hash = "ignore", PartialEq = "ignore", PartialOrd = "ignore")]
357    pub encodings: Option<ValueEncoding>,
358}
359
360impl Value {
361    pub fn new(coin: PositiveCoin, multiasset: MultiAsset) -> Self {
362        Self {
363            coin,
364            multiasset,
365            encodings: None,
366        }
367    }
368
369    pub fn zero() -> Value {
370        0u64.into()
371    }
372
373    pub fn is_zero(&self) -> bool {
374        self.coin == 0 && !self.has_multiassets()
375    }
376
377    pub fn has_multiassets(&self) -> bool {
378        self.multiasset
379            .values()
380            .any(|assets| assets.values().any(|amount| *amount != 0))
381    }
382
383    pub fn checked_add(&self, rhs: &Value) -> Result<Self, AssetArithmeticError> {
384        let coin = self
385            .coin
386            .checked_add(rhs.coin)
387            .ok_or(ArithmeticError::IntegerOverflow)?;
388        let multiasset = self.multiasset.checked_add(&rhs.multiasset)?;
389        Ok(Value {
390            coin,
391            multiasset,
392            encodings: None,
393        })
394    }
395
396    /// Subtract ADA and/or assets
397    /// Removes an asset from the list if the result is 0 or less
398    /// Does not modify this object, instead the result is returned
399    /// None is returned if there would be integer underflow
400    pub fn checked_sub(&self, rhs: &Value) -> Result<Self, AssetArithmeticError> {
401        let coin = self
402            .coin
403            .checked_sub(rhs.coin)
404            .ok_or(ArithmeticError::IntegerUnderflow)?;
405        let multiasset = self.multiasset.checked_sub(&rhs.multiasset)?;
406        Ok(Value {
407            coin,
408            multiasset,
409            encodings: None,
410        })
411    }
412
413    pub fn clamped_sub(&self, rhs: &Value) -> Value {
414        let coin = self.coin.clamped_sub(&rhs.coin);
415        let multiasset = self.multiasset.clamped_sub(&rhs.multiasset);
416        Value {
417            coin,
418            multiasset,
419            encodings: None,
420        }
421    }
422}
423
424// deriving PartialOrd doesn't work in a way that's useful , as the
425// implementation of PartialOrd for BTreeMap compares keys by their order,
426// i.e, is equivalent to comparing the iterators of (pid, Assets).
427// that would mean that: v1 < v2 if the min_pid(v1) < min_pid(v2)
428// this function instead compares amounts, assuming that if a pair (pid, aname)
429// is not in the MultiAsset then it has an amount of 0
430impl<T> PartialOrd for AssetBundle<T>
431where
432    T: num::CheckedAdd + num::CheckedSub + num::Zero + num::Bounded + Copy + Clone + PartialOrd,
433{
434    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
435        // idea: if (a-b) > 0 for some asset, then a > b for at least some asset
436        fn is_all_zeros<T>(lhs: &AssetBundle<T>, rhs: &AssetBundle<T>) -> bool
437        where
438            T: num::CheckedAdd
439                + num::CheckedSub
440                + num::Zero
441                + num::Bounded
442                + Copy
443                + Clone
444                + PartialOrd,
445        {
446            for (pid, assets) in lhs.0.iter() {
447                for (aname, amount) in assets.iter() {
448                    match amount
449                        .checked_sub(&rhs.get(pid, aname).unwrap_or(T::zero()))
450                        .and_then(|o| o.partial_cmp(&T::zero()))
451                    {
452                        Some(std::cmp::Ordering::Equal) => (),
453                        _ => return false,
454                    }
455                }
456            }
457            true
458        }
459
460        match (is_all_zeros(self, other), is_all_zeros(other, self)) {
461            (true, true) => Some(std::cmp::Ordering::Equal),
462            (true, false) => Some(std::cmp::Ordering::Less),
463            (false, true) => Some(std::cmp::Ordering::Greater),
464            (false, false) => None,
465        }
466    }
467}
468
469impl From<PositiveCoin> for Value {
470    fn from(coin: PositiveCoin) -> Self {
471        Self {
472            coin,
473            multiasset: AssetBundle::default(),
474            encodings: None,
475        }
476    }
477}
478
479impl From<MultiAsset> for Value {
480    fn from(multiasset: MultiAsset) -> Self {
481        Self {
482            coin: 0,
483            multiasset,
484            encodings: None,
485        }
486    }
487}
488
489impl Serialize for Value {
490    fn serialize<'se, W: Write>(
491        &self,
492        serializer: &'se mut Serializer<W>,
493        force_canonical: bool,
494    ) -> cbor_event::Result<&'se mut Serializer<W>> {
495        if self.multiasset.is_empty()
496            && self
497                .encodings
498                .as_ref()
499                .map(|encs| !encs.use_multiasset_format)
500                .unwrap_or(true)
501        {
502            // coin-only format
503            serializer.write_unsigned_integer_sz(
504                self.coin,
505                fit_sz(
506                    self.coin,
507                    self.encodings
508                        .as_ref()
509                        .map(|encs| encs.coin_encoding)
510                        .unwrap_or_default(),
511                    force_canonical,
512                ),
513            )
514        } else {
515            // general format
516            serializer.write_array_sz(
517                self.encodings
518                    .as_ref()
519                    .map(|encs| encs.len_encoding)
520                    .unwrap_or_default()
521                    .to_len_sz(2, force_canonical),
522            )?;
523            serializer.write_unsigned_integer_sz(
524                self.coin,
525                fit_sz(
526                    self.coin,
527                    self.encodings
528                        .as_ref()
529                        .map(|encs| encs.coin_encoding)
530                        .unwrap_or_default(),
531                    force_canonical,
532                ),
533            )?;
534            serializer.write_map_sz(
535                self.encodings
536                    .as_ref()
537                    .map(|encs| encs.multiasset_encoding)
538                    .unwrap_or_default()
539                    .to_len_sz(self.multiasset.len() as u64, force_canonical),
540            )?;
541            let mut key_order = self
542                .multiasset
543                .iter()
544                .map(|(k, v)| {
545                    let mut buf = cbor_event::se::Serializer::new_vec();
546                    let multiasset_key_encoding = self
547                        .encodings
548                        .as_ref()
549                        .and_then(|encs| encs.multiasset_key_encodings.get(k))
550                        .cloned()
551                        .unwrap_or_default();
552                    buf.write_bytes_sz(
553                        k.to_raw_bytes(),
554                        multiasset_key_encoding
555                            .to_str_len_sz(k.to_raw_bytes().len() as u64, force_canonical),
556                    )?;
557                    Ok((buf.finalize(), k, v))
558                })
559                .collect::<Result<Vec<(Vec<u8>, &_, &_)>, cbor_event::Error>>()?;
560            if force_canonical {
561                key_order.sort_by(|(lhs_bytes, _, _), (rhs_bytes, _, _)| {
562                    match lhs_bytes.len().cmp(&rhs_bytes.len()) {
563                        std::cmp::Ordering::Equal => lhs_bytes.cmp(rhs_bytes),
564                        diff_ord => diff_ord,
565                    }
566                });
567            }
568            for (key_bytes, key, value) in key_order {
569                serializer.write_raw_bytes(&key_bytes)?;
570                let (multiasset_value_encoding, multiasset_value_value_encodings) = self
571                    .encodings
572                    .as_ref()
573                    .and_then(|encs| encs.multiasset_value_encodings.get(key))
574                    .cloned()
575                    .unwrap_or_else(|| (LenEncoding::default(), BTreeMap::new()));
576                serializer.write_map_sz(
577                    multiasset_value_encoding.to_len_sz(value.len() as u64, force_canonical),
578                )?;
579                let mut key_order = value
580                    .iter()
581                    .map(|(k, v)| {
582                        let mut buf = cbor_event::se::Serializer::new_vec();
583                        k.serialize(&mut buf, force_canonical)?;
584                        Ok((buf.finalize(), k, v))
585                    })
586                    .collect::<Result<Vec<(Vec<u8>, &_, &_)>, cbor_event::Error>>()?;
587                if force_canonical {
588                    key_order.sort_by(|(lhs_bytes, _, _), (rhs_bytes, _, _)| {
589                        match lhs_bytes.len().cmp(&rhs_bytes.len()) {
590                            std::cmp::Ordering::Equal => lhs_bytes.cmp(rhs_bytes),
591                            diff_ord => diff_ord,
592                        }
593                    });
594                }
595                for (key_bytes, key, value) in key_order {
596                    serializer.write_raw_bytes(&key_bytes)?;
597                    let multiasset_value_value_encoding = multiasset_value_value_encodings
598                        .get(key)
599                        .cloned()
600                        .unwrap_or_default();
601                    serializer.write_unsigned_integer_sz(
602                        *value,
603                        fit_sz(*value, multiasset_value_value_encoding, force_canonical),
604                    )?;
605                }
606                multiasset_value_encoding.end(serializer, force_canonical)?;
607            }
608            self.encodings
609                .as_ref()
610                .map(|encs| encs.multiasset_encoding)
611                .unwrap_or_default()
612                .end(serializer, force_canonical)?;
613            self.encodings
614                .as_ref()
615                .map(|encs| encs.len_encoding)
616                .unwrap_or_default()
617                .end(serializer, force_canonical)
618        }
619    }
620}
621
622impl Deserialize for Value {
623    fn deserialize<R: BufRead + Seek>(raw: &mut Deserializer<R>) -> Result<Self, DeserializeError> {
624        (|| -> Result<_, DeserializeError> {
625            match raw.cbor_type()? {
626                // coin-only format
627                cbor_event::Type::UnsignedInteger => {
628                    let (coin, coin_encoding) = raw
629                        .unsigned_integer_sz()
630                        .map(|(x, enc)| (x, Some(enc)))
631                        .map_err(Into::<DeserializeError>::into)
632                        .map_err(|e: DeserializeError| e.annotate("coin"))?;
633                    Ok(Value {
634                        coin,
635                        multiasset: AssetBundle::default(),
636                        encodings: Some(ValueEncoding {
637                            len_encoding: LenEncoding::default(),
638                            coin_encoding,
639                            multiasset_encoding: LenEncoding::default(),
640                            multiasset_key_encodings: BTreeMap::default(),
641                            multiasset_value_encodings: BTreeMap::default(),
642                            use_multiasset_format: false,
643                        }),
644                    })
645                }
646                // general format
647                cbor_event::Type::Array => {
648                    let len = raw.array_sz()?;
649                    let len_encoding: LenEncoding = len.into();
650                    let mut read_len = CBORReadLen::new(len);
651                    read_len.read_elems(2)?;
652                    let (coin, coin_encoding) = raw
653                        .unsigned_integer_sz()
654                        .map(|(x, enc)| (x, Some(enc)))
655                        .map_err(Into::<DeserializeError>::into)
656                        .map_err(|e: DeserializeError| e.annotate("coin"))?;
657                    let (
658                        multiasset,
659                        multiasset_encoding,
660                        multiasset_key_encodings,
661                        multiasset_value_encodings,
662                    ) = (|| -> Result<_, DeserializeError> {
663                        let mut multiasset_table = OrderedHashMap::new();
664                        let multiasset_len = raw.map_sz()?;
665                        let multiasset_encoding = multiasset_len.into();
666                        let mut multiasset_key_encodings = BTreeMap::new();
667                        let mut multiasset_value_encodings = BTreeMap::new();
668                        while match multiasset_len {
669                            cbor_event::LenSz::Len(n, _) => (multiasset_table.len() as u64) < n,
670                            cbor_event::LenSz::Indefinite => true,
671                        } {
672                            if raw.cbor_type()? == cbor_event::Type::Special {
673                                assert_eq!(raw.special()?, cbor_event::Special::Break);
674                                break;
675                            }
676                            let (multiasset_key, multiasset_key_encoding) = raw
677                                .bytes_sz()
678                                .map_err(Into::<DeserializeError>::into)
679                                .and_then(|(bytes, enc)| {
680                                    ScriptHash::from_raw_bytes(&bytes)
681                                        .map(|bytes| (bytes, StringEncoding::from(enc)))
682                                        .map_err(|e| {
683                                            DeserializeFailure::InvalidStructure(Box::new(e)).into()
684                                        })
685                                })?;
686                            let mut multiasset_value_table = OrderedHashMap::new();
687                            let multiasset_value_len = raw.map_sz()?;
688                            let multiasset_value_encoding = multiasset_value_len.into();
689                            let mut multiasset_value_value_encodings = BTreeMap::new();
690                            while match multiasset_value_len {
691                                cbor_event::LenSz::Len(n, _) => {
692                                    (multiasset_value_table.len() as u64) < n
693                                }
694                                cbor_event::LenSz::Indefinite => true,
695                            } {
696                                if raw.cbor_type()? == cbor_event::Type::Special {
697                                    assert_eq!(raw.special()?, cbor_event::Special::Break);
698                                    break;
699                                }
700                                let multiasset_value_key = AssetName::deserialize(raw)?;
701                                let (multiasset_value_value, multiasset_value_value_encoding) =
702                                    raw.unsigned_integer_sz().map(|(x, enc)| (x, Some(enc)))?;
703                                if multiasset_value_table
704                                    .insert(multiasset_value_key.clone(), multiasset_value_value)
705                                    .is_some()
706                                {
707                                    return Err(DeserializeFailure::DuplicateKey(Key::Str(
708                                        String::from("some complicated/unsupported type"),
709                                    ))
710                                    .into());
711                                }
712                                multiasset_value_value_encodings
713                                    .insert(multiasset_value_key, multiasset_value_value_encoding);
714                            }
715                            let (
716                                multiasset_value,
717                                multiasset_value_encoding,
718                                multiasset_value_value_encodings,
719                            ) = (
720                                multiasset_value_table,
721                                multiasset_value_encoding,
722                                multiasset_value_value_encodings,
723                            );
724                            if multiasset_table
725                                .insert(multiasset_key, multiasset_value)
726                                .is_some()
727                            {
728                                return Err(DeserializeFailure::DuplicateKey(Key::Str(
729                                    String::from("some complicated/unsupported type"),
730                                ))
731                                .into());
732                            }
733                            multiasset_key_encodings
734                                .insert(multiasset_key, multiasset_key_encoding);
735                            multiasset_value_encodings.insert(
736                                multiasset_key,
737                                (multiasset_value_encoding, multiasset_value_value_encodings),
738                            );
739                        }
740                        Ok((
741                            multiasset_table,
742                            multiasset_encoding,
743                            multiasset_key_encodings,
744                            multiasset_value_encodings,
745                        ))
746                    })()
747                    .map_err(|e| e.annotate("multiasset"))?;
748                    match len {
749                        cbor_event::LenSz::Len(_, _) => (),
750                        cbor_event::LenSz::Indefinite => match raw.special()? {
751                            cbor_event::Special::Break => (),
752                            _ => return Err(DeserializeFailure::EndingBreakMissing.into()),
753                        },
754                    }
755                    Ok(Value {
756                        coin,
757                        multiasset: multiasset.into(),
758                        encodings: Some(ValueEncoding {
759                            len_encoding,
760                            coin_encoding,
761                            multiasset_encoding,
762                            multiasset_key_encodings,
763                            multiasset_value_encodings,
764                            use_multiasset_format: true,
765                        }),
766                    })
767                }
768                _ => Err(DeserializeFailure::NoVariantMatched.into()),
769            }
770        })()
771        .map_err(|e| e.annotate("Value"))
772    }
773}
774
775#[derive(Clone, Debug, Default)]
776pub struct ValueEncoding {
777    pub len_encoding: LenEncoding,
778    pub coin_encoding: Option<cbor_event::Sz>,
779    pub multiasset_encoding: LenEncoding,
780    pub multiasset_key_encodings: BTreeMap<ScriptHash, StringEncoding>,
781    pub multiasset_value_encodings:
782        BTreeMap<ScriptHash, (LenEncoding, BTreeMap<AssetName, Option<cbor_event::Sz>>)>,
783    // the fields above are directly code-generated but we need to keep track of which variant
784    // we created this from since you can have an empty multiasset map but still use the MA format
785    pub use_multiasset_format: bool,
786}