Skip to main content

stellar_xdr/
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(_)
34            | ScVal::ExecutableTag(_) => Ok(()),
35
36            ScVal::Vec(Some(v)) => {
37                for e in v.iter() {
38                    e.validate()?;
39                }
40                Ok(())
41            }
42
43            ScVal::Symbol(s) => {
44                // 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.
45                if s.iter()
46                    .all(|c| matches!(*c as char, '_' | '0'..='9' | 'A'..='Z' | 'a'..='z'))
47                {
48                    Ok(())
49                } else {
50                    Err(Error::Invalid)
51                }
52            }
53            ScVal::Vec(None) | ScVal::Map(None) => Err(Error::Invalid),
54            ScVal::Map(Some(m)) => m.validate(),
55        }
56    }
57}
58
59impl Validate for ScMap {
60    type Error = Error;
61
62    fn validate(&self) -> Result<(), Self::Error> {
63        // Check every element for validity itself.
64        for pair in self.iter() {
65            pair.key.validate()?;
66            pair.val.validate()?;
67        }
68        // Check the map is sorted by key, and there are no keys that are
69        // duplicates.
70        if self.windows(2).all(|w| w[0].key < w[1].key) {
71            Ok(())
72        } else {
73            Err(Error::Invalid)
74        }
75    }
76}
77
78#[cfg(test)]
79mod test {
80    use crate::ScSymbol;
81
82    use super::{Error, ScVal, Validate};
83
84    #[test]
85    fn symbol() {
86        assert_eq!(
87            ScVal::Symbol(ScSymbol("".try_into().unwrap())).validate(),
88            Ok(())
89        );
90        assert_eq!(
91            ScVal::Symbol(ScSymbol("a0A_".try_into().unwrap())).validate(),
92            Ok(())
93        );
94        assert_eq!(
95            ScVal::Symbol(ScSymbol("]".try_into().unwrap())).validate(),
96            Err(Error::Invalid)
97        );
98    }
99
100    #[test]
101    #[cfg(feature = "alloc")]
102    fn map() {
103        use super::super::{ScMap, ScMapEntry};
104        extern crate alloc;
105        use alloc::vec;
106        // Maps should be sorted by key and have no duplicates. The sort order
107        // is just the "normal" sort order on ScVal emitted by derive(PartialOrd).
108        assert_eq!(
109            ScVal::Map(Some(ScMap(
110                vec![
111                    ScMapEntry {
112                        key: ScVal::I64(0),
113                        val: ScVal::U32(1),
114                    },
115                    ScMapEntry {
116                        key: ScVal::I64(1),
117                        val: ScVal::I64(1),
118                    }
119                ]
120                .try_into()
121                .unwrap()
122            )))
123            .validate(),
124            Ok(())
125        );
126        assert_eq!(
127            ScVal::Map(Some(ScMap(
128                vec![
129                    ScMapEntry {
130                        key: ScVal::I64(0),
131                        val: ScVal::I64(1),
132                    },
133                    ScMapEntry {
134                        key: ScVal::I64(1),
135                        val: ScVal::I64(1),
136                    }
137                ]
138                .try_into()
139                .unwrap()
140            )))
141            .validate(),
142            Ok(())
143        );
144        assert_eq!(
145            ScVal::Map(Some(ScMap(
146                vec![
147                    ScMapEntry {
148                        key: ScVal::I64(2),
149                        val: ScVal::I64(1),
150                    },
151                    ScMapEntry {
152                        key: ScVal::I64(1),
153                        val: ScVal::I64(1),
154                    }
155                ]
156                .try_into()
157                .unwrap()
158            )))
159            .validate(),
160            Err(Error::Invalid)
161        );
162        assert_eq!(
163            ScVal::Map(Some(ScMap(
164                vec![
165                    ScMapEntry {
166                        key: ScVal::I64(2),
167                        val: ScVal::I64(1),
168                    },
169                    ScMapEntry {
170                        key: ScVal::U32(1),
171                        val: ScVal::I64(1),
172                    },
173                ]
174                .try_into()
175                .unwrap()
176            )))
177            .validate(),
178            Err(Error::Invalid)
179        );
180    }
181}