iota_validation/
input_validator.rs

1use regex::Regex;
2
3use iota_constants;
4use iota_model::*;
5
6lazy_static! {
7    static ref TRYTE_REGEX: Regex = Regex::new("^[A-Z9]*$").expect("Failed to parse regex");
8    static ref NINE_TRYTE_REGEX: Regex = Regex::new("^[9]*$").expect("Failed to parse regex");
9}
10
11/// Validates that the provided string is an address
12pub fn is_address(address: &str) -> bool {
13    address.len() == iota_constants::ADDRESS_LENGTH_WITHOUT_CHECKSUM
14        || address.len() == iota_constants::ADDRESS_LENGTH_WITH_CHECKSUM && is_trytes(address)
15}
16
17/// Validates that a slice of strings are all addresses
18pub fn is_addresses_collection_valid(addresses: &[String]) -> bool {
19    for address in addresses {
20        if !is_address(&address) {
21            return false;
22        }
23    }
24    true
25}
26
27/// Validates that a string contains only tryte characters
28pub fn is_trytes(trytes: &str) -> bool {
29    TRYTE_REGEX.is_match(trytes)
30}
31
32/// Validates that a string contains only the number 9
33pub fn is_nine_trytes(trytes: &str) -> bool {
34    NINE_TRYTE_REGEX.is_match(trytes)
35}
36
37/// Validates that a string contains only tryte characters
38pub fn is_trytes_with_length(trytes: &str, len: usize) -> bool {
39    trytes.len() == len && TRYTE_REGEX.is_match(trytes)
40}
41
42/// Validates that a string is an integer
43pub fn is_value(value: &str) -> bool {
44    match value.parse::<i64>() {
45        Ok(_val) => true,
46        Err(_e) => match value.parse::<u64>() {
47            Ok(_val) => true,
48            Err(_e) => false,
49        },
50    }
51}
52
53/// Validates that a slice of strings are all valid trytes
54pub fn is_array_of_trytes<T: AsRef<str>>(trytes: &[T]) -> bool {
55    if trytes.is_empty() {
56        return false;
57    }
58    for tryte in trytes {
59        if !is_trytes(&tryte.as_ref()) {
60            return false;
61        }
62    }
63    true
64}
65
66/// Validates that a slice of strings are all valid hashes
67pub fn is_array_of_hashes<T: AsRef<str>>(hashes: &[T]) -> bool {
68    if hashes.is_empty() {
69        return false;
70    }
71    for hash in hashes {
72        let hash = hash.as_ref();
73        match hash.len() {
74            90 => {
75                if !is_trytes(&hash[0..90]) {
76                    return false;
77                }
78            }
79            81 => {
80                if !is_trytes(&hash[0..81]) {
81                    return false;
82                }
83            }
84            _ => return false,
85        }
86    }
87    true
88}
89
90/// Validates a transfer
91pub fn is_valid_transfer(transfer: &Transfer) -> bool {
92    is_address(&transfer.address) && is_trytes(&transfer.message) && is_trytes(&transfer.tag)
93}
94
95/// Validates a slice of transfers
96pub fn is_transfers_collection_valid(transfers: &[Transfer]) -> bool {
97    if transfers.is_empty() {
98        return false;
99    }
100    for transfer in transfers {
101        if !is_valid_transfer(&transfer) {
102            return false;
103        }
104    }
105    true
106}
107
108/// Validates a slice of transactions
109pub fn is_slice_of_transactions(bundle: &[Transaction]) -> bool {
110    if bundle.is_empty() {
111        return false;
112    }
113
114    let mut valid = true;
115    for tx in bundle {
116        if tx.hash == "" {
117            return false;
118        }
119        valid &= is_hash(&tx.hash);
120        if tx.signature_fragments == "" {
121            return false;
122        }
123        valid &= is_trytes(&tx.signature_fragments);
124        if tx.address == "" {
125            return false;
126        }
127        valid &= is_hash(&tx.address);
128        if tx.tag == "" {
129            return false;
130        }
131        valid &= is_trytes(&tx.tag);
132        if tx.obsolete_tag == "" {
133            return false;
134        }
135        valid &= is_trytes(&tx.obsolete_tag);
136        if tx.bundle == "" {
137            return false;
138        }
139        valid &= is_hash(&tx.bundle);
140        if tx.trunk_transaction == "" {
141            return false;
142        }
143        valid &= is_hash(&tx.trunk_transaction);
144        if tx.branch_transaction == "" {
145            return false;
146        }
147        valid &= is_hash(&tx.branch_transaction);
148        if tx.nonce == "" {
149            return false;
150        }
151        valid &= is_trytes(&tx.nonce);
152        if !valid {
153            return false;
154        }
155    }
156    valid
157}
158
159/// Validates that a string is a seed
160pub fn is_valid_seed(seed: &str) -> bool {
161    is_trytes(seed)
162}
163
164/// Validates that a string is a hash
165pub fn is_hash(hash: &str) -> bool {
166    if hash.len() == 81 {
167        return is_trytes(&hash[0..81]);
168    }
169    false
170}
171
172/// Validates that a slice of strings are all hash
173pub fn is_hashes(hashes: &[String]) -> bool {
174    for hash in hashes {
175        if !is_trytes(&hash[0..81]) {
176            return false;
177        }
178    }
179    true
180}
181
182/// Validates that a slice of strings contains only attached trytes
183pub fn is_array_of_attached_trytes(trytes: &[String]) -> bool {
184    if trytes.is_empty() {
185        return false;
186    }
187    for tryte_value in trytes {
188        if tryte_value.len() != 2673 {
189            return false;
190        }
191        if !is_trytes(&tryte_value[0..2673]) {
192            return false;
193        }
194        if is_nine_trytes(&tryte_value[2673 - (3 * 81)..]) {
195            return false;
196        }
197    }
198    true
199}
200
201#[cfg(test)]
202mod tests {
203    use super::*;
204
205    const TEST_ADDRESS_WITHOUT_CHECKSUM: &str = "PNGMCSNRCTRHCHPXYTPKEJYPCOWKOMRXZFHH9N9VDIKMNVAZCMIYRHVJIAZARZTUETJVFDMBEBIQE9QTHBFWDAOEFA";
206    const TEST_ADDRESS_WITH_CHECKSUM: &str = "PNGMCSNRCTRHCHPXYTPKEJYPCOWKOMRXZFHH9N9VDIKMNVAZCMIYRHVJIAZARZTUETJVFDMBEBIQE9QTHBFWDAOEFA";
207    const TEST_TRYTES: &str = "BYSWEAUTWXHXZ9YBZISEK9LUHWGMHXCGEVNZHRLUWQFCUSDXZHOFHWHL9MQPVJXXZLIXPXPXF9KYEREFSKCPKYIIKPZVLHUTDFQKKVVBBN9ATTLPCNPJDWDEVIYYLGPZGCWXOBDXMLJC9VO9QXTTBLAXTTBFUAROYEGQIVB9MJWJKXJMCUPTWAUGFZBTZCSJVRBGMYXTVBDDS9MYUJCPZ9YDWWQNIPUAIJXXSNLKUBSCOIJPCLEFPOXFJREXQCUVUMKSDOVQGGHRNILCO9GNCLWFM9APMNMWYASHXQAYBEXF9QRIHIBHYEJOYHRQJAOKAQ9AJJFQ9WEIWIJOTZATIBOXQLBMIJU9PCGBLVDDVFP9CFFSXTDUXMEGOOFXWRTLFGV9XXMYWEMGQEEEDBTIJ9OJOXFAPFQXCDAXOUDMLVYRMRLUDBETOLRJQAEDDLNVIRQJUBZBO9CCFDHIX9MSQCWYAXJVWHCUPTRSXJDESISQPRKZAFKFRULCGVRSBLVFOPEYLEE99JD9SEBALQINPDAZHFAB9RNBH9AZWIJOTLBZVIEJIAYGMC9AZGNFWGRSWAXTYSXVROVNKCOQQIWGPNQZKHUNODGYADPYLZZZUQRTJRTODOUKAOITNOMWNGHJBBA99QUMBHRENGBHTH9KHUAOXBVIVDVYYZMSEYSJWIOGGXZVRGN999EEGQMCOYVJQRIRROMPCQBLDYIGQO9AMORPYFSSUGACOJXGAQSPDY9YWRRPESNXXBDQ9OZOXVIOMLGTSWAMKMTDRSPGJKGBXQIVNRJRFRYEZ9VJDLHIKPSKMYC9YEGHFDS9SGVDHRIXBEMLFIINOHVPXIFAZCJKBHVMQZEVWCOSNWQRDYWVAIBLSCBGESJUIBWZECPUCAYAWMTQKRMCHONIPKJYYTEGZCJYCT9ABRWTJLRQXKMWY9GWZMHYZNWPXULNZAPVQLPMYQZCYNEPOCGOHBJUZLZDPIXVHLDMQYJUUBEDXXPXFLNRGIPWBRNQQZJSGSJTTYHIGGFAWJVXWL9THTPWOOHTNQWCNYOYZXALHAZXVMIZE9WMQUDCHDJMIBWKTYH9AC9AFOT9DPCADCV9ZWUTE9QNOMSZPTZDJLJZCJGHXUNBJFUBJWQUEZDMHXGBPTNSPZBR9TGSKVOHMOQSWPGFLSWNESFKSAZY9HHERAXALZCABFYPOVLAHMIHVDBGKUMDXC9WHHTIRYHZVWNXSVQUWCR9M9RAGMFEZZKZ9XEOQGOSLFQCHHOKLDSA9QCMDGCGMRYJZLBVIFOLBIJPROKMHOYTBTJIWUZWJMCTKCJKKTR9LCVYPVJI9AHGI9JOWMIWZAGMLDFJA9WU9QAMEFGABIBEZNNAL9OXSBFLOEHKDGHWFQSHMPLYFCNXAAZYJLMQDEYRGL9QKCEUEJ9LLVUOINVSZZQHCIKPAGMT9CAYIIMTTBCPKWTYHOJIIY9GYNPAJNUJ9BKYYXSV9JSPEXYMCFAIKTGNRSQGUNIYZCRT9FOWENSZQPD9ALUPYYAVICHVYELYFPUYDTWUSWNIYFXPX9MICCCOOZIWRNJIDALWGWRATGLJXNAYTNIZWQ9YTVDBOFZRKO9CFWRPAQQRXTPACOWCPRLYRYSJARRKSQPR9TCFXDVIXLP9XVL99ERRDSOHBFJDJQQGGGCZNDQ9NYCTQJWVZIAELCRBJJFDMCNZU9FIZRPGNURTXOCDSQGXTQHKHUECGWFUUYS9J9NYQ9U9P9UUP9YMZHWWWCIASCFLCMSKTELZWUGCDE9YOKVOVKTAYPHDF9ZCCQAYPJIJNGSHUIHHCOSSOOBUDOKE9CJZGYSSGNCQJVBEFTZFJ9SQUHOASKRRGBSHWKBCBWBTJHOGQ9WOMQFHWJVEG9NYX9KWBTCAIXNXHEBDIOFO9ALYMFGRICLCKKLG9FOBOX9PDWNQRGHBKHGKKRLWTBEQMCWQRLHAVYYZDIIPKVQTHYTWQMTOACXZOQCDTJTBAAUWXSGJF9PNQIJ9AJRUMUVCPWYVYVARKR9RKGOUHHNKNVGGPDDLGKPQNOYHNKAVVKCXWXOQPZNSLATUJT9AUWRMPPSWHSTTYDFAQDXOCYTZHOYYGAIM9CELMZ9AZPWB9MJXGHOKDNNSZVUDAGXTJJSSZCPZVPZBYNNTUQABSXQWZCHDQSLGK9UOHCFKBIBNETK999999999999999999999999999999999999999999999999999999999999999999999999999999999NOXDXXKUDWLOFJLIPQIBRBMGDYCPGDNLQOLQS99EQYKBIU9VHCJVIPFUYCQDNY9APGEVYLCENJIOBLWNB999999999XKBRHUD99C99999999NKZKEKWLDKMJCI9N9XQOLWEPAYWSH9999999999999999999999999KDDTGZLIPBNZKMLTOLOXQVNGLASESDQVPTXALEKRMIOHQLUHD9ELQDBQETS9QFGTYOYWLNTSKKMVJAUXSIROUICDOXKSYZTDPEDKOQENTJOWJONDEWROCEJIEWFWLUAACVSJFTMCHHXJBJRKAAPUDXXVXFWP9X9999IROUICDOXKSYZTDPEDKOQENTJOWJONDEWROCEJIEWFWLUAACVSJFTMCHHXJBJRKAAPUDXXVXFWP9X9999";
208    const TEST_HASH: &str =
209        "OAATQS9VQLSXCLDJVJJVYUGONXAXOFMJOZNSYWRZSWECMXAQQURHQBJNLD9IOFEPGZEPEMPXCIVRX9999";
210    const TEST_MESSAGE: &str = "JOTA";
211    const TEST_TAG: &str = "JOTASPAM9999999999999999999";
212
213    #[test]
214    fn test_is_address() {
215        assert!(is_address(TEST_ADDRESS_WITHOUT_CHECKSUM))
216    }
217
218    #[test]
219    fn test_is_trytes() {
220        assert!(is_trytes(TEST_TRYTES))
221    }
222
223    #[test]
224    fn test_is_value() {
225        assert!(is_value("1234"));
226    }
227
228    #[test]
229    fn test_is_array_of_hashes() {
230        assert!(is_array_of_hashes(&vec![
231            TEST_HASH.to_string(),
232            TEST_HASH.to_string(),
233        ]));
234    }
235
236    #[test]
237    fn test_is_array_of_trytes() {
238        assert!(is_array_of_trytes(&vec![
239            TEST_TRYTES.to_string(),
240            TEST_TRYTES.to_string(),
241        ]));
242    }
243
244    #[test]
245    fn test_is_nine_trytes() {
246        assert!(is_nine_trytes("999999999"));
247    }
248
249    #[test]
250    fn test_is_valid_transfer() {
251        let mut t = Transfer::default();
252        t.address = TEST_ADDRESS_WITH_CHECKSUM.to_string();
253        t.value = 0;
254        t.message = TEST_MESSAGE.to_string();
255        t.tag = TEST_TAG.to_string();
256        assert!(is_valid_transfer(&t));
257    }
258
259    #[test]
260    fn test_is_transfers_collection_valid() {
261        let mut t = Transfer::default();
262        t.address = TEST_ADDRESS_WITH_CHECKSUM.to_string();
263        t.value = 0;
264        t.message = TEST_MESSAGE.to_string();
265        t.tag = TEST_TAG.to_string();
266
267        let mut t2 = Transfer::default();
268        t2.address = TEST_ADDRESS_WITH_CHECKSUM.to_string();
269        t2.value = 0;
270        t2.message = "".to_string();
271
272        let transfers = vec![t, t2];
273        assert!(is_transfers_collection_valid(&transfers));
274    }
275}