mithril_signer/database/record/
signed_beacon_record.rs1use chrono::{DateTime, Utc};
2use sqlite::Row;
3
4use mithril_common::entities::{Epoch, SignedEntityType};
5use mithril_persistence::database::Hydrator;
6use mithril_persistence::sqlite::{HydrationError, Projection, SqLiteEntity};
7
8use crate::entities::BeaconToSign;
9
10#[derive(Debug, Clone, PartialEq)]
12pub struct SignedBeaconRecord {
13 pub epoch: Epoch,
15
16 pub signed_entity_type: SignedEntityType,
18
19 pub initiated_at: DateTime<Utc>,
21
22 pub signed_at: DateTime<Utc>,
24}
25
26impl From<BeaconToSign> for SignedBeaconRecord {
27 fn from(beacon: BeaconToSign) -> Self {
28 Self {
29 epoch: beacon.epoch,
30 signed_entity_type: beacon.signed_entity_type,
31 initiated_at: beacon.initiated_at,
32 signed_at: Utc::now(),
33 }
34 }
35}
36
37#[cfg(test)]
38impl SignedBeaconRecord {
39 pub(crate) fn fake(epoch: Epoch, signed_entity_type: SignedEntityType) -> Self {
43 let initiated_at = DateTime::<Utc>::default();
44 Self {
45 epoch,
46 signed_entity_type,
47 initiated_at,
48 signed_at: initiated_at + chrono::TimeDelta::minutes(3),
49 }
50 }
51
52 pub(crate) fn fakes(records: &[(Epoch, Vec<SignedEntityType>)]) -> Vec<Self> {
53 records
54 .iter()
55 .flat_map(|(epoch, signed_entity_types)| {
56 signed_entity_types
57 .iter()
58 .map(|se| Self::fake(*epoch, se.clone()))
59 })
60 .collect()
61 }
62}
63
64#[cfg(test)]
65impl PartialEq<BeaconToSign> for SignedBeaconRecord {
66 fn eq(&self, other: &BeaconToSign) -> bool {
67 self.epoch.eq(&other.epoch)
68 && self.signed_entity_type.eq(&other.signed_entity_type)
69 && self.initiated_at.eq(&other.initiated_at)
70 }
71}
72
73#[cfg(test)]
74impl PartialEq<SignedBeaconRecord> for BeaconToSign {
75 fn eq(&self, other: &SignedBeaconRecord) -> bool {
76 other.eq(self)
77 }
78}
79
80impl SqLiteEntity for SignedBeaconRecord {
81 fn hydrate(row: Row) -> Result<Self, HydrationError>
82 where
83 Self: Sized,
84 {
85 let epoch = row.read::<i64, _>(0);
86 let beacon_str = Hydrator::read_signed_entity_beacon_column(&row, 1);
87 let signed_entity_type_id = usize::try_from(row.read::<i64, _>(2)).map_err(|e| {
88 panic!(
89 "Integer field signed_beacon.signed_entity_type_id cannot be turned into usize: {e}"
90 )
91 })?;
92 let initiated_at = &row.read::<&str, _>(3);
93 let signed_at = &row.read::<&str, _>(4);
94
95 let signed_beacon = Self {
96 epoch: Epoch(epoch.try_into().map_err(|e| {
97 HydrationError::InvalidData(format!(
98 "Could not cast i64 ({epoch}) to u64. Error: '{e}'"
99 ))
100 })?),
101 signed_entity_type: Hydrator::hydrate_signed_entity_type(signed_entity_type_id, &beacon_str)?,
102 initiated_at: DateTime::parse_from_rfc3339(initiated_at).map_err(|e| {
103 HydrationError::InvalidData(format!(
104 "Could not turn signed_beacon.initiated_at field value '{initiated_at}' to rfc3339 Datetime. Error: {e}"
105 ))
106 })?.with_timezone(&Utc),
107 signed_at:DateTime::parse_from_rfc3339(signed_at).map_err(|e| {
108 HydrationError::InvalidData(format!(
109 "Could not turn signed_beacon.initiated_at field value '{initiated_at}' to rfc3339 Datetime. Error: {e}"
110 ))
111 })?.with_timezone(&Utc),
112 };
113
114 Ok(signed_beacon)
115 }
116
117 fn get_projection() -> Projection {
118 Projection::from(&[
119 ("epoch", "{:signed_beacon:}.epoch", "int"),
120 ("beacon", "{:signed_beacon:}.beacon", "text"),
121 (
122 "signed_entity_type_id",
123 "{:signed_beacon:}.signed_entity_type_id",
124 "int",
125 ),
126 ("created_at", "{:signed_beacon:}.initiated_at", "text"),
127 ("expires_at", "{:signed_beacon:}.signed_at", "text"),
128 ])
129 }
130}
131
132#[cfg(test)]
133mod tests {
134 use super::*;
135
136 #[test]
137 fn eq_beacon_to_sign() {
138 let initiated_at = DateTime::<Utc>::default();
139 let beacon_to_sign = BeaconToSign {
140 epoch: Epoch(3),
141 signed_entity_type: SignedEntityType::MithrilStakeDistribution(Epoch(4)),
142 initiated_at,
143 };
144 let signed_beacon = SignedBeaconRecord {
145 epoch: Epoch(3),
146 signed_entity_type: SignedEntityType::MithrilStakeDistribution(Epoch(4)),
147 initiated_at,
148 signed_at: initiated_at + chrono::TimeDelta::minutes(2),
149 };
150
151 assert_eq!(signed_beacon, beacon_to_sign);
153 assert_ne!(
154 signed_beacon,
155 BeaconToSign {
156 epoch: beacon_to_sign.epoch + 13,
157 ..beacon_to_sign.clone()
158 }
159 );
160
161 assert_eq!(beacon_to_sign, signed_beacon);
163 assert_ne!(
164 beacon_to_sign,
165 SignedBeaconRecord {
166 epoch: signed_beacon.epoch + 11,
167 ..signed_beacon.clone()
168 }
169 );
170 }
171}