mithril_aggregator/database/record/
signer_registration.rs

1use chrono::{DateTime, Utc};
2
3use mithril_common::crypto_helper::KesPeriod;
4use mithril_common::entities::{
5    Epoch, HexEncodedOpCert, HexEncodedVerificationKey, HexEncodedVerificationKeySignature, Signer,
6    SignerWithStake, Stake,
7};
8use mithril_persistence::sqlite::{HydrationError, Projection, SqLiteEntity};
9
10/// SignerRegistration record is the representation of a stored signer_registration.
11#[derive(Debug, PartialEq, Clone)]
12pub struct SignerRegistrationRecord {
13    /// Signer id.
14    pub signer_id: String,
15
16    /// Epoch of creation of the signer_registration.
17    pub epoch_settings_id: Epoch,
18
19    /// Verification key of the signer
20    pub verification_key: HexEncodedVerificationKey,
21
22    /// Signature of the verification key of the signer
23    pub verification_key_signature: Option<HexEncodedVerificationKeySignature>,
24
25    /// Operational certificate of the stake pool operator associated to the signer
26    pub operational_certificate: Option<HexEncodedOpCert>,
27
28    /// The number of evolutions of the KES key since the start KES period of the operational certificate at the time of signature.
29    ///
30    /// Note: the naming 'kes_period' lacks clarity and should be renamed to 'kes_evolutions'
31    // TODO: This 'kes_period' should be renamed to 'kes_evolutions' to avoid confusion
32    pub kes_period: Option<KesPeriod>,
33
34    /// The stake associated to the signer
35    pub stake: Option<Stake>,
36
37    /// Date and time when the signer_registration was created
38    pub created_at: DateTime<Utc>,
39}
40
41impl SignerRegistrationRecord {
42    pub(crate) fn from_signer_with_stake(other: SignerWithStake, epoch: Epoch) -> Self {
43        SignerRegistrationRecord {
44            signer_id: other.party_id,
45            epoch_settings_id: epoch,
46            verification_key: other.verification_key.to_json_hex().unwrap(),
47            verification_key_signature: other
48                .verification_key_signature
49                .map(|k| k.to_json_hex().unwrap()),
50            operational_certificate: other
51                .operational_certificate
52                .map(|o| o.to_json_hex().unwrap()),
53            kes_period: other.kes_evolutions,
54            stake: Some(other.stake),
55            created_at: Utc::now(),
56        }
57    }
58}
59
60impl From<SignerRegistrationRecord> for Signer {
61    fn from(other: SignerRegistrationRecord) -> Self {
62        Self {
63            party_id: other.signer_id,
64            verification_key: other.verification_key.try_into().unwrap(),
65            verification_key_signature: other
66                .verification_key_signature
67                .map(|k| k.try_into().unwrap()),
68            operational_certificate: other.operational_certificate.map(|o| o.try_into().unwrap()),
69            kes_evolutions: other.kes_period,
70        }
71    }
72}
73
74impl From<SignerRegistrationRecord> for SignerWithStake {
75    fn from(other: SignerRegistrationRecord) -> Self {
76        Self {
77            party_id: other.signer_id,
78            verification_key: other.verification_key.try_into().unwrap(),
79            verification_key_signature: other
80                .verification_key_signature
81                .map(|k| k.try_into().unwrap()),
82            operational_certificate: other.operational_certificate.map(|o| o.try_into().unwrap()),
83            kes_evolutions: other.kes_period,
84            stake: other.stake.unwrap_or_default(),
85        }
86    }
87}
88
89impl SqLiteEntity for SignerRegistrationRecord {
90    fn hydrate(row: sqlite::Row) -> Result<Self, HydrationError>
91    where
92        Self: Sized,
93    {
94        let signer_id = row.read::<&str, _>(0).to_string();
95        let epoch_settings_id_int = row.read::<i64, _>(1);
96        let verification_key = row.read::<&str, _>(2).to_string();
97        let verification_key_signature = row.read::<Option<&str>, _>(3).map(|s| s.to_owned());
98        let operational_certificate = row.read::<Option<&str>, _>(4).map(|s| s.to_owned());
99        let kes_period_int = row.read::<Option<i64>, _>(5);
100        let stake_int = row.read::<Option<i64>, _>(6);
101        let created_at = row.read::<&str, _>(7);
102
103        let signer_registration_record = Self {
104            signer_id,
105            epoch_settings_id: Epoch(epoch_settings_id_int.try_into().map_err(|e| {
106                HydrationError::InvalidData(format!(
107                    "Could not cast i64 ({epoch_settings_id_int}) to u64. Error: '{e}'"
108                ))
109            })?),
110            verification_key,
111            verification_key_signature,
112            operational_certificate,
113            kes_period: match kes_period_int {
114                Some(kes_period_int) => Some(kes_period_int.try_into().map_err(|e| {
115                    HydrationError::InvalidData(format!(
116                        "Could not cast i64 ({kes_period_int}) to u64. Error: '{e}'"
117                    ))
118                })?),
119                None => None,
120            },
121            stake: match stake_int {
122                Some(stake_int) => Some(stake_int.try_into().map_err(|e| {
123                    HydrationError::InvalidData(format!(
124                        "Could not cast i64 ({stake_int}) to u64. Error: '{e}'"
125                    ))
126                })?),
127                None => None,
128            },
129            created_at: DateTime::parse_from_rfc3339(created_at)
130                .map_err(|e| {
131                    HydrationError::InvalidData(format!(
132                        "Could not turn string '{created_at}' to rfc3339 Datetime. Error: {e}"
133                    ))
134                })?
135                .with_timezone(&Utc),
136        };
137
138        Ok(signer_registration_record)
139    }
140
141    fn get_projection() -> Projection {
142        let mut projection = Projection::default();
143        projection.add_field("signer_id", "{:signer_registration:}.signer_id", "text");
144        projection.add_field(
145            "epoch_setting_id",
146            "{:signer_registration:}.epoch_setting_id",
147            "integer",
148        );
149        projection.add_field(
150            "verification_key",
151            "{:signer_registration:}.verification_key",
152            "text",
153        );
154        projection.add_field(
155            "verification_key_signature",
156            "{:signer_registration:}.verification_key_signature",
157            "text",
158        );
159        projection.add_field(
160            "operational_certificate",
161            "{:signer_registration:}.operational_certificate",
162            "text",
163        );
164        projection.add_field(
165            "kes_period",
166            "{:signer_registration:}.kes_period",
167            "integer",
168        );
169        projection.add_field("stake", "{:signer_registration:}.stake", "integer");
170        projection.add_field("created_at", "{:signer_registration:}.created_at", "text");
171
172        projection
173    }
174}
175
176#[cfg(test)]
177mod tests {
178    use mithril_common::test::builder::MithrilFixtureBuilder;
179
180    use super::*;
181
182    #[test]
183    fn test_convert_signer_registrations() {
184        let fixture = MithrilFixtureBuilder::default().with_signers(5).build();
185        let signer_with_stakes = fixture.signers_with_stake();
186
187        let mut signer_registration_records: Vec<SignerRegistrationRecord> = Vec::new();
188        for signer_with_stake in signer_with_stakes.clone() {
189            signer_registration_records.push(SignerRegistrationRecord::from_signer_with_stake(
190                signer_with_stake,
191                Epoch(1),
192            ));
193        }
194        let mut signer_with_stakes_new: Vec<SignerWithStake> = Vec::new();
195        for signer_registration_record in signer_registration_records {
196            signer_with_stakes_new.push(signer_registration_record.into());
197        }
198        assert_eq!(signer_with_stakes, signer_with_stakes_new);
199    }
200}