Skip to main content

aleph_types/message/
authorization.rs

1use serde::{Deserialize, Serialize};
2
3use crate::chain::{Address, Chain};
4use crate::message::MessageType;
5
6/// A single authorization entry granting a delegate address
7/// permission to act on behalf of the owner.
8#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
9pub struct Authorization {
10    pub address: Address,
11    #[serde(skip_serializing_if = "Option::is_none")]
12    pub chain: Option<Chain>,
13    #[serde(default, skip_serializing_if = "Vec::is_empty")]
14    pub channels: Vec<String>,
15    #[serde(default, skip_serializing_if = "Vec::is_empty")]
16    pub types: Vec<MessageType>,
17    #[serde(default, skip_serializing_if = "Vec::is_empty")]
18    pub post_types: Vec<String>,
19    #[serde(default, skip_serializing_if = "Vec::is_empty")]
20    pub aggregate_keys: Vec<String>,
21}
22
23/// Content of the "security" aggregate.
24#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
25pub struct SecurityAggregateContent {
26    #[serde(default)]
27    pub authorizations: Vec<Authorization>,
28}
29
30#[cfg(test)]
31mod tests {
32    use super::*;
33
34    #[test]
35    fn test_minimal_authorization_round_trip() {
36        let auth = Authorization {
37            address: Address::from("0xabc123".to_string()),
38            chain: None,
39            channels: vec![],
40            types: vec![],
41            post_types: vec![],
42            aggregate_keys: vec![],
43        };
44        let json = serde_json::to_string(&auth).unwrap();
45        // Empty/None fields must be omitted
46        assert_eq!(json, r#"{"address":"0xabc123"}"#);
47        let deserialized: Authorization = serde_json::from_str(&json).unwrap();
48        assert_eq!(auth, deserialized);
49    }
50
51    #[test]
52    fn test_full_authorization_round_trip() {
53        let auth = Authorization {
54            address: Address::from("0xdelegate".to_string()),
55            chain: Some(Chain::Ethereum),
56            channels: vec!["my-channel".to_string()],
57            types: vec![MessageType::Post, MessageType::Aggregate],
58            post_types: vec!["blog".to_string()],
59            aggregate_keys: vec!["profile".to_string()],
60        };
61        let json = serde_json::to_string(&auth).unwrap();
62        let deserialized: Authorization = serde_json::from_str(&json).unwrap();
63        assert_eq!(auth, deserialized);
64    }
65
66    #[test]
67    fn test_python_sdk_wire_format_compatibility() {
68        // JSON as produced by the Python SDK
69        let python_json = r#"{
70            "address": "0xdelegate",
71            "chain": "ETH",
72            "channels": ["aleph-test"],
73            "types": ["POST", "AGGREGATE"],
74            "post_types": ["blog"],
75            "aggregate_keys": ["profile"]
76        }"#;
77        let auth: Authorization = serde_json::from_str(python_json).unwrap();
78        assert_eq!(auth.address, Address::from("0xdelegate".to_string()));
79        assert_eq!(auth.chain, Some(Chain::Ethereum));
80        assert_eq!(auth.types, vec![MessageType::Post, MessageType::Aggregate]);
81    }
82
83    #[test]
84    fn test_security_aggregate_content_round_trip() {
85        let content = SecurityAggregateContent {
86            authorizations: vec![Authorization {
87                address: Address::from("0xabc".to_string()),
88                chain: None,
89                channels: vec![],
90                types: vec![MessageType::Post],
91                post_types: vec![],
92                aggregate_keys: vec![],
93            }],
94        };
95        let json = serde_json::to_string(&content).unwrap();
96        let deserialized: SecurityAggregateContent = serde_json::from_str(&json).unwrap();
97        assert_eq!(content, deserialized);
98    }
99
100    #[test]
101    fn test_empty_security_aggregate_deserialization() {
102        let json = r#"{"authorizations":[]}"#;
103        let content: SecurityAggregateContent = serde_json::from_str(json).unwrap();
104        assert!(content.authorizations.is_empty());
105    }
106}