mithril_aggregator/http_server/validators/
prover_transactions_hash_validator.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
use mithril_common::entities::ClientError;

pub struct ProverTransactionsHashValidator {
    max_hashes: usize,
}

impl ProverTransactionsHashValidator {
    const LABEL: &'static str = "invalid_transaction_hashes";

    pub fn new(max_hashes: usize) -> Self {
        Self { max_hashes }
    }

    pub fn validate(&self, hashes: &[String]) -> Result<(), ClientError> {
        if hashes.len() > self.max_hashes {
            return Err(ClientError::new(
                Self::LABEL,
                format!(
                    "Transaction hashes list contains more than maximum allowed number of hashes: '{}'",
                    self.max_hashes
                ),
            ));
        }

        for hash in hashes {
            if hash.is_empty() {
                return Err(ClientError::new(
                    Self::LABEL,
                    "Transaction hash cannot be empty",
                ));
            }

            if hash.chars().count() != 64 {
                return Err(ClientError::new(
                    Self::LABEL,
                    "Transaction hash must have 64 characters",
                ));
            }

            if !hash.chars().all(|c| c.is_ascii_hexdigit()) {
                return Err(ClientError::new(
                    Self::LABEL,
                    "Transaction hash must contain only hexadecimal characters",
                ));
            }
        }

        Ok(())
    }
}

#[cfg(test)]
impl Default for ProverTransactionsHashValidator {
    fn default() -> Self {
        Self::new(usize::MAX)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn prover_transactions_hash_validator_return_error_when_empty_hash() {
        let error = ProverTransactionsHashValidator::default()
            .validate(&["".to_string()])
            .expect_err("Should return an error");

        assert_eq!(
            error,
            ClientError::new(
                "invalid_transaction_hashes",
                "Transaction hash cannot be empty"
            )
        );
    }

    #[test]
    fn prover_transactions_hash_validator_return_error_when_hash_size_different_than_64() {
        let error = ProverTransactionsHashValidator::default()
            .validate(&["abc".to_string()])
            .expect_err("Should return an error");

        assert_eq!(
            error,
            ClientError::new(
                "invalid_transaction_hashes",
                "Transaction hash must have 64 characters"
            )
        );
    }

    #[test]
    fn prover_transactions_hash_validator_return_error_when_hash_contains_non_hexadecimal_characters(
    ) {
        for invalid_char in ["g", "x", ";", " ", "à"].iter() {
            let hash = format!("{}{}", "a".repeat(63), invalid_char);
            let error = ProverTransactionsHashValidator::default()
                .validate(&[hash.clone()])
                .expect_err("Should return an error");
            assert_eq!(
                error,
                ClientError::new(
                    "invalid_transaction_hashes",
                    "Transaction hash must contain only hexadecimal characters"
                ),
                "Invalid hash: {}",
                hash
            );
        }
    }

    #[test]
    fn prover_transactions_hash_validator_when_hash_contains_only_hexadecimal_characters() {
        ProverTransactionsHashValidator::default()
            .validate(&[format!("bcd9{}", "a".repeat(60))])
            .expect("Should succeed");
    }

    #[test]
    fn prover_transactions_hash_validator_return_error_when_more_hashes_than_max_allowed() {
        let transactions_hashes = vec!["a".repeat(64), "b".repeat(64), "c".repeat(64)];
        let validator = ProverTransactionsHashValidator::new(2);

        let error = validator
            .validate(&transactions_hashes)
            .expect_err("Should return an error");

        assert_eq!(
            error,
            ClientError::new(
                "invalid_transaction_hashes",
                format!(
                    "Transaction hashes list contains more than maximum allowed number of hashes: '{}'",
                    validator.max_hashes
                )
            )
        );
    }
}