mithril_aggregator/tools/
era.rs

1use std::path::{Path, PathBuf};
2
3use anyhow::anyhow;
4use mithril_common::{
5    chain_observer::{TxDatumBuilder, TxDatumFieldValue},
6    crypto_helper::EraMarkersSigner,
7    entities::Epoch,
8    era::{adapters::EraMarkersPayloadCardanoChain, EraMarker, SupportedEra},
9    StdResult,
10};
11
12type EraToolsResult<R> = StdResult<R>;
13
14pub struct EraTools {}
15
16impl EraTools {
17    pub fn new() -> Self {
18        Self {}
19    }
20
21    /// Get list of supported eras
22    pub fn get_supported_eras_list(&self) -> EraToolsResult<Vec<SupportedEra>> {
23        Ok(SupportedEra::eras())
24    }
25
26    /// Generate TxDatum for eras with sanity check of epochs
27    pub fn generate_tx_datum(
28        &self,
29        current_era_epoch: Epoch,
30        maybe_next_era_epoch: Option<Epoch>,
31        era_markers_signer: &EraMarkersSigner,
32    ) -> EraToolsResult<String> {
33        if maybe_next_era_epoch.is_some()
34            && maybe_next_era_epoch.unwrap_or_default() <= current_era_epoch
35        {
36            Err(anyhow!(
37                "next era epoch must be strictly greater than the current era epoch"
38            ))?;
39        }
40
41        let mut era_markers = Vec::new();
42        for (index, era) in SupportedEra::eras().iter().enumerate() {
43            let era_marker = match index {
44                0 => EraMarker::new(&era.to_string(), Some(current_era_epoch)),
45                1 => EraMarker::new(&era.to_string(), maybe_next_era_epoch),
46                _ => Err(anyhow!("too many eras retrieved, can't generate tx datum"))?,
47            };
48            era_markers.push(era_marker);
49        }
50        let era_markers_payload = EraMarkersPayloadCardanoChain {
51            markers: era_markers,
52            signature: None,
53        }
54        .sign(era_markers_signer)?;
55
56        let tx_datum = TxDatumBuilder::new()
57            .add_field(TxDatumFieldValue::Bytes(era_markers_payload.to_json_hex()?))
58            .build()?;
59        Ok(tx_datum.0)
60    }
61
62    /// Export the era keypair to a folder and returns the paths to the files (secret key, verification_key)
63    pub fn create_and_save_era_keypair(keypair_path: &Path) -> StdResult<(PathBuf, PathBuf)> {
64        let era_signer = EraMarkersSigner::create_non_deterministic_signer();
65        let era_secret_key_path = keypair_path.join("era.sk");
66        era_signer
67            .secret_key()
68            .write_json_hex_to_file(&era_secret_key_path)?;
69        let era_verification_key_path = keypair_path.join("era.vk");
70        era_signer
71            .verification_key()
72            .write_json_hex_to_file(&era_verification_key_path)?;
73
74        Ok((era_secret_key_path, era_verification_key_path))
75    }
76}
77
78#[cfg(test)]
79mod tests {
80    use mithril_common::{
81        crypto_helper::{EraMarkersVerifierSecretKey, EraMarkersVerifierVerificationKey},
82        test_utils::TempDir,
83    };
84    use std::fs::read_to_string;
85
86    use super::*;
87
88    fn build_tools() -> EraTools {
89        EraTools {}
90    }
91
92    fn get_temp_dir(dir_name: &str) -> PathBuf {
93        TempDir::create("era", dir_name)
94    }
95
96    #[test]
97    fn get_supported_eras_list() {
98        let era_tools = build_tools();
99        let supported_eras_list = era_tools
100            .get_supported_eras_list()
101            .expect("get_supported_eras_list should not fail");
102        assert_eq!(supported_eras_list, SupportedEra::eras());
103    }
104
105    #[test]
106    fn generate_tx_datum_ok() {
107        let era_markers_signer = EraMarkersSigner::create_deterministic_signer();
108        let era_tools = build_tools();
109        let _ = era_tools
110            .generate_tx_datum(Epoch(1), None, &era_markers_signer)
111            .expect("generate_tx_datum should not fail");
112    }
113
114    #[test]
115    fn generate_tx_datum_wrong_epochs() {
116        let era_markers_signer = EraMarkersSigner::create_deterministic_signer();
117        let era_tools = build_tools();
118        let _ = era_tools
119            .generate_tx_datum(Epoch(3), Some(Epoch(2)), &era_markers_signer)
120            .expect_err("generate_tx_datum should have failed");
121    }
122
123    #[test]
124    fn test_create_and_save_era_keypair() {
125        let temp_dir = get_temp_dir("test_create_and_save_era_keypair");
126        let (era_secret_key_path, era_verification_key_path) =
127            EraTools::create_and_save_era_keypair(&temp_dir)
128                .expect("Failed to create and save era keypair");
129        let era_secret_key = EraMarkersVerifierSecretKey::from_json_hex(
130            &read_to_string(&era_secret_key_path).expect("Failed to read era secret key file"),
131        )
132        .expect("Failed to parse era secret key");
133        let era_verification_key = EraMarkersVerifierVerificationKey::from_json_hex(
134            &read_to_string(&era_verification_key_path)
135                .expect("Failed to read era verification key file"),
136        )
137        .expect("Failed to parse era verification key");
138        let era_verifier = EraMarkersSigner::from_secret_key(era_secret_key).create_verifier();
139
140        let expected_era_verification_key = era_verifier.to_verification_key();
141        assert_eq!(expected_era_verification_key, era_verification_key);
142    }
143}