aleph_types/message/
aggregate.rs

1use serde::{Deserialize, Serialize};
2use std::collections::HashMap;
3
4#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
5pub struct AggregateKeyDict {
6    name: String,
7}
8
9#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
10#[serde(untagged)]
11pub enum AggregateKey {
12    String(String),
13    Dict(AggregateKeyDict),
14}
15
16impl AggregateKey {
17    pub fn key(&self) -> &str {
18        match self {
19            AggregateKey::String(key) => key,
20            AggregateKey::Dict(dict) => dict.name.as_str(),
21        }
22    }
23}
24
25#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
26pub struct AggregateContent {
27    /// The aggregate key can be either a string of a dict containing the key in field 'name'.
28    key: AggregateKey,
29    /// The content of the aggregate. The only restriction is that this must be a dictionary.
30    content: HashMap<serde_json::Value, serde_json::Value>,
31}
32
33impl AggregateContent {
34    pub fn key(&self) -> &str {
35        self.key.key()
36    }
37}
38
39#[cfg(test)]
40mod tests {
41    use crate::chain::Chain;
42    use crate::message::base_message::MessageContentEnum;
43    use crate::message::{ContentSource, Message, MessageType};
44    use crate::timestamp::Timestamp;
45    use crate::{address, channel, item_hash, signature};
46    use assert_matches::assert_matches;
47
48    const AGGREGATE_FIXTURE: &str = include_str!(concat!(
49        env!("CARGO_MANIFEST_DIR"),
50        "/../../fixtures/messages/aggregate/aggregate.json"
51    ));
52
53    #[test]
54    fn test_deserialize_aggregate() {
55        let message: Message = serde_json::from_str(AGGREGATE_FIXTURE).unwrap();
56
57        assert_eq!(
58            message.sender,
59            address!("0xa1B3bb7d2332383D96b7796B908fB7f7F3c2Be10")
60        );
61        assert_eq!(message.chain, Chain::Ethereum);
62        assert_eq!(
63            message.signature,
64            signature!(
65                "0x7d14b66772d97f5a7f9915875b34eae3df117f0a2cd6ffada3bfee09313441e853c1fdf813158b3109e1f85aacb8410894e7d76b552a7821d2280aac956528591c"
66            )
67        );
68        assert_matches!(message.message_type, MessageType::Aggregate);
69        assert_matches!(
70            message.content_source,
71            ContentSource::Storage,
72            "Expected content_source to be ContentSource::Storage"
73        );
74        assert_eq!(
75            message.item_hash,
76            item_hash!("3ad7f29b5b451b3e49d6054a8966aa7e728ac0f07dd7ef25f3bd2455f1408190")
77        );
78        assert_eq!(message.time, Timestamp::from(1762518461.524221));
79        assert_eq!(message.channel, Some(channel!("FOUNDATION")));
80
81        // Check content fields
82        assert_eq!(
83            &message.content.address,
84            &address!("0xa1B3bb7d2332383D96b7796B908fB7f7F3c2Be10")
85        );
86        assert_eq!(&message.content.time, &Timestamp::from(1762518461.4893668));
87
88        // Check aggregate content fields
89        let aggregate_content = match message.content() {
90            MessageContentEnum::Aggregate(content) => content,
91            other => {
92                panic!("Expected MessageContentEnum::Aggregate, got {:?}", other);
93            }
94        };
95        assert_eq!(aggregate_content.key(), "corechannel");
96        assert!(
97            aggregate_content
98                .content
99                .contains_key(&serde_json::Value::String("nodes".to_string()))
100        );
101        assert!(
102            aggregate_content
103                .content
104                .contains_key(&serde_json::Value::String("resource_nodes".to_string()))
105        );
106
107        // No confirmation on this fixture
108        assert!(!message.confirmed());
109        assert!(message.confirmations.is_empty());
110    }
111}