stellar_xdr/next/
scval_validations.rs

1#![allow(clippy::missing_errors_doc)]
2
3use super::{Error, ScMap, ScVal};
4
5pub trait Validate {
6    type Error;
7    fn validate(&self) -> Result<(), Self::Error>;
8}
9
10impl Validate for ScVal {
11    type Error = Error;
12
13    fn validate(&self) -> Result<(), <Self as Validate>::Error> {
14        match self {
15            ScVal::U32(_)
16            | ScVal::I32(_)
17            | ScVal::Error(_)
18            | ScVal::Bool(_)
19            | ScVal::Void
20            | ScVal::U64(_)
21            | ScVal::I64(_)
22            | ScVal::Timepoint(_)
23            | ScVal::Duration(_)
24            | ScVal::U128(_)
25            | ScVal::I128(_)
26            | ScVal::U256(_)
27            | ScVal::I256(_)
28            | ScVal::Bytes(_)
29            | ScVal::String(_)
30            | ScVal::Address(_)
31            | ScVal::LedgerKeyContractInstance
32            | ScVal::LedgerKeyNonce(_)
33            | ScVal::ContractInstance(_) => Ok(()),
34
35            ScVal::Vec(Some(v)) => {
36                for e in v.iter() {
37                    e.validate()?;
38                }
39                Ok(())
40            }
41
42            ScVal::Symbol(s) => {
43                // Symbol is defined as valid per https://github.com/stellar/rs-stellar-contract-env/blob/94c1717516c8fad4ad65caa148183b9fcbc408db/stellar-contract-env-common/src/symbol.rs#L107-L111.
44                if s.iter()
45                    .all(|c| matches!(*c as char, '_' | '0'..='9' | 'A'..='Z' | 'a'..='z'))
46                {
47                    Ok(())
48                } else {
49                    Err(Error::Invalid)
50                }
51            }
52            ScVal::Vec(None) | ScVal::Map(None) => Err(Error::Invalid),
53            ScVal::Map(Some(m)) => m.validate(),
54        }
55    }
56}
57
58impl Validate for ScMap {
59    type Error = Error;
60
61    fn validate(&self) -> Result<(), Self::Error> {
62        // Check every element for validity itself.
63        for pair in self.iter() {
64            pair.key.validate()?;
65            pair.val.validate()?;
66        }
67        // Check the map is sorted by key, and there are no keys that are
68        // duplicates.
69        if self.windows(2).all(|w| w[0].key < w[1].key) {
70            Ok(())
71        } else {
72            Err(Error::Invalid)
73        }
74    }
75}
76
77#[cfg(test)]
78mod test {
79    use crate::next::ScSymbol;
80
81    use super::{Error, ScVal, Validate};
82
83    #[test]
84    fn symbol() {
85        assert_eq!(
86            ScVal::Symbol(ScSymbol("".try_into().unwrap())).validate(),
87            Ok(())
88        );
89        assert_eq!(
90            ScVal::Symbol(ScSymbol("a0A_".try_into().unwrap())).validate(),
91            Ok(())
92        );
93        assert_eq!(
94            ScVal::Symbol(ScSymbol("]".try_into().unwrap())).validate(),
95            Err(Error::Invalid)
96        );
97    }
98
99    #[test]
100    #[cfg(feature = "alloc")]
101    fn map() {
102        use super::super::{ScMap, ScMapEntry};
103        extern crate alloc;
104        use alloc::vec;
105        // Maps should be sorted by key and have no duplicates. The sort order
106        // is just the "normal" sort order on ScVal emitted by derive(PartialOrd).
107        assert_eq!(
108            ScVal::Map(Some(ScMap(
109                vec![
110                    ScMapEntry {
111                        key: ScVal::I64(0),
112                        val: ScVal::U32(1),
113                    },
114                    ScMapEntry {
115                        key: ScVal::I64(1),
116                        val: ScVal::I64(1),
117                    }
118                ]
119                .try_into()
120                .unwrap()
121            )))
122            .validate(),
123            Ok(())
124        );
125        assert_eq!(
126            ScVal::Map(Some(ScMap(
127                vec![
128                    ScMapEntry {
129                        key: ScVal::I64(0),
130                        val: ScVal::I64(1),
131                    },
132                    ScMapEntry {
133                        key: ScVal::I64(1),
134                        val: ScVal::I64(1),
135                    }
136                ]
137                .try_into()
138                .unwrap()
139            )))
140            .validate(),
141            Ok(())
142        );
143        assert_eq!(
144            ScVal::Map(Some(ScMap(
145                vec![
146                    ScMapEntry {
147                        key: ScVal::I64(2),
148                        val: ScVal::I64(1),
149                    },
150                    ScMapEntry {
151                        key: ScVal::I64(1),
152                        val: ScVal::I64(1),
153                    }
154                ]
155                .try_into()
156                .unwrap()
157            )))
158            .validate(),
159            Err(Error::Invalid)
160        );
161        assert_eq!(
162            ScVal::Map(Some(ScMap(
163                vec![
164                    ScMapEntry {
165                        key: ScVal::I64(2),
166                        val: ScVal::I64(1),
167                    },
168                    ScMapEntry {
169                        key: ScVal::U32(1),
170                        val: ScVal::I64(1),
171                    },
172                ]
173                .try_into()
174                .unwrap()
175            )))
176            .validate(),
177            Err(Error::Invalid)
178        );
179    }
180}