mithril_aggregator/tools/
era.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
use std::path::{Path, PathBuf};

use anyhow::anyhow;
use mithril_common::{
    chain_observer::{TxDatumBuilder, TxDatumFieldValue},
    crypto_helper::EraMarkersSigner,
    entities::Epoch,
    era::{adapters::EraMarkersPayloadCardanoChain, EraMarker, SupportedEra},
    StdResult,
};

type EraToolsResult<R> = StdResult<R>;

pub struct EraTools {}

impl EraTools {
    pub fn new() -> Self {
        Self {}
    }

    /// Get list of supported eras
    pub fn get_supported_eras_list(&self) -> EraToolsResult<Vec<SupportedEra>> {
        Ok(SupportedEra::eras())
    }

    /// Generate TxDatum for eras with sanity check of epochs
    pub fn generate_tx_datum(
        &self,
        current_era_epoch: Epoch,
        maybe_next_era_epoch: Option<Epoch>,
        era_markers_signer: &EraMarkersSigner,
    ) -> EraToolsResult<String> {
        if maybe_next_era_epoch.is_some()
            && maybe_next_era_epoch.unwrap_or_default() <= current_era_epoch
        {
            Err(anyhow!(
                "next era epoch must be strictly greater than the current era epoch"
            ))?;
        }

        let mut era_markers = Vec::new();
        for (index, era) in SupportedEra::eras().iter().enumerate() {
            let era_marker = match index {
                0 => EraMarker::new(&era.to_string(), Some(current_era_epoch)),
                1 => EraMarker::new(&era.to_string(), maybe_next_era_epoch),
                _ => Err(anyhow!("too many eras retrieved, can't generate tx datum"))?,
            };
            era_markers.push(era_marker);
        }
        let era_markers_payload = EraMarkersPayloadCardanoChain {
            markers: era_markers,
            signature: None,
        }
        .sign(era_markers_signer)?;

        let tx_datum = TxDatumBuilder::new()
            .add_field(TxDatumFieldValue::Bytes(era_markers_payload.to_json_hex()?))
            .build()?;
        Ok(tx_datum.0)
    }

    /// Export the era keypair to a folder and returns the paths to the files (secret key, verification_key)
    pub fn create_and_save_era_keypair(keypair_path: &Path) -> StdResult<(PathBuf, PathBuf)> {
        let era_signer = EraMarkersSigner::create_non_deterministic_signer();
        let era_secret_key_path = keypair_path.join("era.sk");
        era_signer
            .secret_key()
            .write_json_hex_to_file(&era_secret_key_path)?;
        let era_verification_key_path = keypair_path.join("era.vk");
        era_signer
            .verification_key()
            .write_json_hex_to_file(&era_verification_key_path)?;

        Ok((era_secret_key_path, era_verification_key_path))
    }
}

#[cfg(test)]
mod tests {
    use mithril_common::{
        crypto_helper::{EraMarkersVerifierSecretKey, EraMarkersVerifierVerificationKey},
        test_utils::TempDir,
    };
    use std::fs::read_to_string;

    use super::*;

    fn build_tools() -> EraTools {
        EraTools {}
    }

    fn get_temp_dir(dir_name: &str) -> PathBuf {
        TempDir::create("era", dir_name)
    }

    #[test]
    fn get_supported_eras_list() {
        let era_tools = build_tools();
        let supported_eras_list = era_tools
            .get_supported_eras_list()
            .expect("get_supported_eras_list should not fail");
        assert_eq!(supported_eras_list, SupportedEra::eras());
    }

    #[test]
    fn generate_tx_datum_ok() {
        let era_markers_signer = EraMarkersSigner::create_deterministic_signer();
        let era_tools = build_tools();
        let _ = era_tools
            .generate_tx_datum(Epoch(1), None, &era_markers_signer)
            .expect("generate_tx_datum should not fail");
    }

    #[test]
    fn generate_tx_datum_wrong_epochs() {
        let era_markers_signer = EraMarkersSigner::create_deterministic_signer();
        let era_tools = build_tools();
        let _ = era_tools
            .generate_tx_datum(Epoch(3), Some(Epoch(2)), &era_markers_signer)
            .expect_err("generate_tx_datum should have failed");
    }

    #[test]
    fn test_create_and_save_era_keypair() {
        let temp_dir = get_temp_dir("test_create_and_save_era_keypair");
        let (era_secret_key_path, era_verification_key_path) =
            EraTools::create_and_save_era_keypair(&temp_dir)
                .expect("Failed to create and save era keypair");
        let era_secret_key = EraMarkersVerifierSecretKey::from_json_hex(
            &read_to_string(&era_secret_key_path).expect("Failed to read era secret key file"),
        )
        .expect("Failed to parse era secret key");
        let era_verification_key = EraMarkersVerifierVerificationKey::from_json_hex(
            &read_to_string(&era_verification_key_path)
                .expect("Failed to read era verification key file"),
        )
        .expect("Failed to parse era verification key");
        let era_verifier = EraMarkersSigner::from_secret_key(era_secret_key).create_verifier();

        let expected_era_verification_key = era_verifier.to_verification_key();
        assert_eq!(expected_era_verification_key, era_verification_key);
    }
}