openzeppelin_relayer/models/error/
stellar_validation.rs

1//! Specific error types for Stellar transaction validation
2//!
3//! This module provides granular error types for different validation failures,
4//! making it easier to handle specific error cases and provide better error messages.
5
6use serde::Serialize;
7use thiserror::Error;
8
9/// Specific errors that can occur during Stellar transaction validation
10#[derive(Error, Debug, Serialize, Clone, PartialEq)]
11#[serde(tag = "type", content = "details")]
12pub enum StellarValidationError {
13    /// Transaction has no operations
14    #[error("Transaction must have at least one operation")]
15    EmptyOperations,
16
17    /// Transaction exceeds maximum operation count
18    #[error("Transaction has {count} operations, but maximum allowed is {max}")]
19    TooManyOperations { count: usize, max: usize },
20
21    /// Multiple Soroban operations in a single transaction
22    #[error("Transaction can contain at most one Soroban operation")]
23    MultipleSorobanOperations,
24
25    /// Soroban operation mixed with non-Soroban operations
26    #[error("Soroban operations must be exclusive - no other operations allowed in the same transaction")]
27    SorobanNotExclusive,
28
29    /// Soroban operation with non-None memo
30    #[error("Soroban operations cannot have a memo (except MemoNone)")]
31    SorobanWithMemo,
32
33    /// Source account mismatch for unsigned XDR
34    #[error("XDR source account {actual} does not match relayer account {expected}")]
35    SourceAccountMismatch { expected: String, actual: String },
36
37    /// Signed XDR when unsigned was expected
38    #[error("Expected unsigned XDR but received signed XDR")]
39    UnexpectedSignedXdr,
40
41    /// Unsigned XDR when signed was expected
42    #[error("Expected signed XDR but received unsigned XDR")]
43    UnexpectedUnsignedXdr,
44
45    /// Invalid max_fee for signed XDR
46    #[error("max_fee must be greater than 0 for signed XDR")]
47    InvalidMaxFee,
48
49    /// Generic XDR parsing error
50    #[error("Invalid XDR: {0}")]
51    InvalidXdr(String),
52}
53
54/// Convert StellarValidationError to the generic TransactionError
55impl From<StellarValidationError> for crate::models::TransactionError {
56    fn from(err: StellarValidationError) -> Self {
57        crate::models::TransactionError::ValidationError(err.to_string())
58    }
59}
60
61#[cfg(test)]
62mod tests {
63    use super::*;
64
65    #[test]
66    fn test_error_messages() {
67        assert_eq!(
68            StellarValidationError::EmptyOperations.to_string(),
69            "Transaction must have at least one operation"
70        );
71
72        assert_eq!(
73            StellarValidationError::TooManyOperations {
74                count: 150,
75                max: 100
76            }
77            .to_string(),
78            "Transaction has 150 operations, but maximum allowed is 100"
79        );
80
81        assert_eq!(
82            StellarValidationError::SourceAccountMismatch {
83                expected: "GAAAA...".to_string(),
84                actual: "GBBBB...".to_string()
85            }
86            .to_string(),
87            "XDR source account GBBBB... does not match relayer account GAAAA..."
88        );
89    }
90
91    #[test]
92    fn test_serialization() {
93        let error = StellarValidationError::TooManyOperations {
94            count: 150,
95            max: 100,
96        };
97        let json = serde_json::to_string(&error).unwrap();
98        assert!(json.contains("\"type\":\"TooManyOperations\""));
99        assert!(json.contains("\"count\":150"));
100        assert!(json.contains("\"max\":100"));
101    }
102
103    #[test]
104    fn test_conversion_to_transaction_error() {
105        let stellar_err = StellarValidationError::SorobanWithMemo;
106        let tx_err: crate::models::TransactionError = stellar_err.into();
107
108        match tx_err {
109            crate::models::TransactionError::ValidationError(msg) => {
110                assert_eq!(
111                    msg,
112                    "Soroban operations cannot have a memo (except MemoNone)"
113                );
114            }
115            _ => panic!("Expected ValidationError variant"),
116        }
117    }
118}