mithril_aggregator/database/record/
open_message.rs

1use chrono::{DateTime, Utc};
2use sqlite::Row;
3use uuid::Uuid;
4
5use mithril_common::entities::{Epoch, ProtocolMessage, SignedEntityType};
6use mithril_persistence::database::Hydrator;
7use mithril_persistence::sqlite::{HydrationError, Projection, SqLiteEntity};
8
9/// ## OpenMessage
10///
11/// An open message is a message open for signatures. Every signer may send a
12/// single signature for this message from which a multi signature will be
13/// generated if possible.
14#[derive(Debug, Clone, PartialEq, Eq)]
15pub struct OpenMessageRecord {
16    /// OpenMessage unique identifier
17    pub open_message_id: Uuid,
18
19    /// Epoch
20    pub epoch: Epoch,
21
22    /// Type of message
23    pub signed_entity_type: SignedEntityType,
24
25    /// Message used by the Mithril Protocol
26    pub protocol_message: ProtocolMessage,
27
28    /// Has this open message been converted into a certificate?
29    pub is_certified: bool,
30
31    /// Has this open message expired
32    pub is_expired: bool,
33
34    /// Message creation datetime, it is set by the database.
35    pub created_at: DateTime<Utc>,
36
37    /// Message expiration datetime, if it exists.
38    pub expires_at: Option<DateTime<Utc>>,
39}
40
41impl OpenMessageRecord {
42    /// Creates a new random id that can be used for a new record
43    pub fn new_id() -> Uuid {
44        Uuid::new_v4()
45    }
46}
47
48impl SqLiteEntity for OpenMessageRecord {
49    fn hydrate(row: Row) -> Result<Self, HydrationError>
50    where
51        Self: Sized,
52    {
53        let open_message_id = row.read::<&str, _>(0);
54        let open_message_id = Uuid::parse_str(open_message_id).map_err(|e| {
55            HydrationError::InvalidData(format!(
56                "Invalid UUID in open_message.open_message_id: '{open_message_id}'. Error: {e}"
57            ))
58        })?;
59        let protocol_message = row.read::<&str, _>(4);
60        let protocol_message = serde_json::from_str(protocol_message).map_err(|e| {
61            HydrationError::InvalidData(format!(
62                "Invalid protocol message JSON representation '{protocol_message}'. Error: {e}"
63            ))
64        })?;
65        let epoch_settings_id = row.read::<i64, _>(1);
66        let epoch_val = u64::try_from(epoch_settings_id)
67            .map_err(|e| panic!("Integer field open_message.epoch_setting_id (value={epoch_settings_id}) is incompatible with u64 Epoch representation. Error = {e}"))?;
68        let beacon_str = Hydrator::read_signed_entity_beacon_column(&row, 2);
69        let signed_entity_type_id = usize::try_from(row.read::<i64, _>(3)).map_err(|e| {
70            panic!(
71                "Integer field open_message.signed_entity_type_id cannot be turned into usize: {e}"
72            )
73        })?;
74        let signed_entity_type =
75            Hydrator::hydrate_signed_entity_type(signed_entity_type_id, &beacon_str)?;
76        let is_certified = row.read::<i64, _>(5) != 0;
77        let datetime = &row.read::<&str, _>(7);
78        let created_at =
79            DateTime::parse_from_rfc3339(datetime).map_err(|e| {
80                HydrationError::InvalidData(format!(
81                    "Could not turn open_message.created_at field value '{datetime}' to rfc3339 Datetime. Error: {e}"
82                ))
83            })?.with_timezone(&Utc);
84        let is_expired = row.read::<i64, _>(6) != 0;
85        let datetime = &row.read::<Option<&str>, _>(8);
86        let expires_at = datetime.map(|datetime| DateTime::parse_from_rfc3339(datetime).map_err(|e| {
87            HydrationError::InvalidData(format!(
88                "Could not turn open_message.expires_at field value '{datetime}' to rfc3339 Datetime. Error: {e}"
89            ))
90        })).transpose()?.map(|datetime| datetime.with_timezone(&Utc));
91        let open_message = Self {
92            open_message_id,
93            epoch: Epoch(epoch_val),
94            signed_entity_type,
95            protocol_message,
96            is_certified,
97            is_expired,
98            created_at,
99            expires_at,
100        };
101
102        Ok(open_message)
103    }
104
105    fn get_projection() -> Projection {
106        Projection::from(&[
107            (
108                "open_message_id",
109                "{:open_message:}.open_message_id",
110                "text",
111            ),
112            (
113                "epoch_setting_id",
114                "{:open_message:}.epoch_setting_id",
115                "int",
116            ),
117            ("beacon", "{:open_message:}.beacon", "text"),
118            (
119                "signed_entity_type_id",
120                "{:open_message:}.signed_entity_type_id",
121                "int",
122            ),
123            (
124                "protocol_message",
125                "{:open_message:}.protocol_message",
126                "text",
127            ),
128            ("is_certified", "{:open_message:}.is_certified", "bool"),
129            ("is_expired", "{:open_message:}.is_expired", "bool"),
130            ("created_at", "{:open_message:}.created_at", "text"),
131            ("expires_at", "{:open_message:}.expires_at", "text"),
132        ])
133    }
134}