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.iter().map(|se| Self::fake(*epoch, se.clone()))
57 })
58 .collect()
59 }
60}
61
62#[cfg(test)]
63impl PartialEq<BeaconToSign> for SignedBeaconRecord {
64 fn eq(&self, other: &BeaconToSign) -> bool {
65 self.epoch.eq(&other.epoch)
66 && self.signed_entity_type.eq(&other.signed_entity_type)
67 && self.initiated_at.eq(&other.initiated_at)
68 }
69}
70
71#[cfg(test)]
72impl PartialEq<SignedBeaconRecord> for BeaconToSign {
73 fn eq(&self, other: &SignedBeaconRecord) -> bool {
74 other.eq(self)
75 }
76}
77
78impl SqLiteEntity for SignedBeaconRecord {
79 fn hydrate(row: Row) -> Result<Self, HydrationError>
80 where
81 Self: Sized,
82 {
83 let epoch = row.read::<i64, _>(0);
84 let beacon_str = Hydrator::read_signed_entity_beacon_column(&row, 1);
85 let signed_entity_type_id = usize::try_from(row.read::<i64, _>(2)).map_err(|e| {
86 panic!(
87 "Integer field signed_beacon.signed_entity_type_id cannot be turned into usize: {e}"
88 )
89 })?;
90 let initiated_at = &row.read::<&str, _>(3);
91 let signed_at = &row.read::<&str, _>(4);
92
93 let signed_beacon = Self {
94 epoch: Epoch(epoch.try_into().map_err(|e| {
95 HydrationError::InvalidData(format!(
96 "Could not cast i64 ({epoch}) to u64. Error: '{e}'"
97 ))
98 })?),
99 signed_entity_type: Hydrator::hydrate_signed_entity_type(signed_entity_type_id, &beacon_str)?,
100 initiated_at: DateTime::parse_from_rfc3339(initiated_at).map_err(|e| {
101 HydrationError::InvalidData(format!(
102 "Could not turn signed_beacon.initiated_at field value '{initiated_at}' to rfc3339 Datetime. Error: {e}"
103 ))
104 })?.with_timezone(&Utc),
105 signed_at:DateTime::parse_from_rfc3339(signed_at).map_err(|e| {
106 HydrationError::InvalidData(format!(
107 "Could not turn signed_beacon.initiated_at field value '{initiated_at}' to rfc3339 Datetime. Error: {e}"
108 ))
109 })?.with_timezone(&Utc),
110 };
111
112 Ok(signed_beacon)
113 }
114
115 fn get_projection() -> Projection {
116 Projection::from(&[
117 ("epoch", "{:signed_beacon:}.epoch", "int"),
118 ("beacon", "{:signed_beacon:}.beacon", "text"),
119 (
120 "signed_entity_type_id",
121 "{:signed_beacon:}.signed_entity_type_id",
122 "int",
123 ),
124 ("created_at", "{:signed_beacon:}.initiated_at", "text"),
125 ("expires_at", "{:signed_beacon:}.signed_at", "text"),
126 ])
127 }
128}
129
130#[cfg(test)]
131mod tests {
132 use super::*;
133
134 #[test]
135 fn eq_beacon_to_sign() {
136 let initiated_at = DateTime::<Utc>::default();
137 let beacon_to_sign = BeaconToSign {
138 epoch: Epoch(3),
139 signed_entity_type: SignedEntityType::MithrilStakeDistribution(Epoch(4)),
140 initiated_at,
141 };
142 let signed_beacon = SignedBeaconRecord {
143 epoch: Epoch(3),
144 signed_entity_type: SignedEntityType::MithrilStakeDistribution(Epoch(4)),
145 initiated_at,
146 signed_at: initiated_at + chrono::TimeDelta::minutes(2),
147 };
148
149 assert_eq!(signed_beacon, beacon_to_sign);
151 assert_ne!(
152 signed_beacon,
153 BeaconToSign {
154 epoch: beacon_to_sign.epoch + 13,
155 ..beacon_to_sign.clone()
156 }
157 );
158
159 assert_eq!(beacon_to_sign, signed_beacon);
161 assert_ne!(
162 beacon_to_sign,
163 SignedBeaconRecord {
164 epoch: signed_beacon.epoch + 11,
165 ..signed_beacon.clone()
166 }
167 );
168 }
169}