mithril_era/adapters/
cardano_chain.rs

1use anyhow::{anyhow, Context};
2use async_trait::async_trait;
3use serde::{Deserialize, Serialize};
4use std::sync::Arc;
5use thiserror::Error;
6
7use mithril_cardano_node_chain::chain_observer::ChainObserver;
8use mithril_cardano_node_chain::entities::{ChainAddress, TxDatumFieldTypeName};
9use mithril_common::crypto_helper::{
10    key_decode_hex, key_encode_hex, EraMarkersSigner, EraMarkersVerifier,
11    EraMarkersVerifierSignature, EraMarkersVerifierVerificationKey,
12};
13use mithril_common::{StdError, StdResult};
14
15use crate::{EraMarker, EraReaderAdapter};
16
17/// [EraMarkersPayload] related errors.
18#[derive(Debug, Error)]
19pub enum EraMarkersPayloadError {
20    /// Error raised when the message serialization fails
21    #[error("could not serialize message")]
22    SerializeMessage(#[source] StdError),
23
24    /// Error raised when the signature deserialization fails
25    #[error("could not deserialize signature")]
26    DeserializeSignature(#[source] StdError),
27
28    /// Error raised when the signature is missing
29    #[error("could not verify signature: signature is missing")]
30    MissingSignature,
31
32    /// Error raised when the signature is invalid
33    #[error("could not verify signature")]
34    VerifySignature(#[source] StdError),
35
36    /// Error raised when the signing the markers
37    #[error("could not create signature")]
38    CreateSignature(#[source] StdError),
39}
40
41/// Era markers payload
42#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
43pub struct EraMarkersPayload {
44    /// List of Era markers
45    pub markers: Vec<EraMarker>,
46
47    /// Era markers signature
48    pub signature: Option<EraMarkersVerifierSignature>,
49}
50
51impl EraMarkersPayload {
52    fn message_to_bytes(&self) -> Result<Vec<u8>, EraMarkersPayloadError> {
53        serde_json::to_vec(&self.markers)
54            .map_err(|e| EraMarkersPayloadError::SerializeMessage(e.into()))
55    }
56
57    /// Encode this payload to a json hex string
58    pub fn to_json_hex(&self) -> StdResult<String> {
59        key_encode_hex(self)
60            .map_err(|e| anyhow!(e).context("era markers payload could not be json hex encoded"))
61    }
62
63    /// Decode a [EraMarkersPayload] from a json hex string
64    pub fn from_json_hex(payload: &str) -> StdResult<Self> {
65        let payload = key_decode_hex(payload).map_err(|e| {
66            anyhow!(e).context("era markers payload could not be decoded from json hex")
67        })?;
68        Ok(payload)
69    }
70
71    /// Verify the signature an era markers payload
72    pub fn verify_signature(
73        &self,
74        verification_key: EraMarkersVerifierVerificationKey,
75    ) -> Result<(), EraMarkersPayloadError> {
76        let signature = self
77            .signature
78            .ok_or(EraMarkersPayloadError::MissingSignature)?;
79        let markers_verifier: EraMarkersVerifier =
80            EraMarkersVerifier::from_verification_key(verification_key);
81
82        markers_verifier
83            .verify(&self.message_to_bytes()?, &signature)
84            .with_context(|| "era markers payload could not verify signature")
85            .map_err(EraMarkersPayloadError::VerifySignature)
86    }
87
88    /// Sign an era markers payload
89    pub fn sign(self, signer: &EraMarkersSigner) -> Result<Self, EraMarkersPayloadError> {
90        let signature = signer.sign(
91            &self
92                .message_to_bytes()
93                .map_err(|e| EraMarkersPayloadError::CreateSignature(e.into()))?,
94        );
95
96        Ok(Self {
97            markers: self.markers,
98            signature: Some(signature),
99        })
100    }
101}
102
103/// Cardano Chain adapter retrieves era markers on chain
104pub struct CardanoChainAdapter {
105    address: ChainAddress,
106    chain_observer: Arc<dyn ChainObserver>,
107    verification_key: EraMarkersVerifierVerificationKey,
108}
109
110impl CardanoChainAdapter {
111    /// CardanoChainAdapter factory
112    pub fn new(
113        address: ChainAddress,
114        chain_observer: Arc<dyn ChainObserver>,
115        verification_key: EraMarkersVerifierVerificationKey,
116    ) -> Self {
117        Self {
118            address,
119            chain_observer,
120            verification_key,
121        }
122    }
123}
124
125#[async_trait]
126impl EraReaderAdapter for CardanoChainAdapter {
127    async fn read(&self) -> StdResult<Vec<EraMarker>> {
128        let tx_datums = self
129            .chain_observer
130            .get_current_datums(&self.address)
131            .await?;
132        let markers_list = tx_datums
133            .into_iter()
134            .filter_map(|datum| datum.get_fields_by_type(&TxDatumFieldTypeName::Bytes).ok())
135            .map(|fields| {
136                fields
137                    .iter()
138                    .filter_map(|field_value| field_value.as_str().map(|s| s.to_string()))
139                    .collect::<Vec<String>>()
140                    .join("")
141            })
142            .filter_map(|field_value_str| EraMarkersPayload::from_json_hex(&field_value_str).ok())
143            .filter_map(|era_markers_payload| {
144                era_markers_payload
145                    .verify_signature(self.verification_key)
146                    .ok()
147                    .map(|_| era_markers_payload.markers)
148            })
149            .collect::<Vec<Vec<EraMarker>>>();
150
151        Ok(markers_list.first().unwrap_or(&Vec::new()).to_owned())
152    }
153}
154
155#[cfg(test)]
156mod test {
157    use mithril_cardano_node_chain::entities::{TxDatum, TxDatumBuilder, TxDatumFieldValue};
158    use mithril_cardano_node_chain::test::double::FakeChainObserver;
159    use mithril_common::entities::Epoch;
160
161    use super::*;
162
163    const GOLDEN_ERA_MARKERS_PAYLOAD_WITH_SIGNATURE: &str =
164        "7b226d61726b657273223a5b7b226e616d65223a227468616c6573222c2265706f6368223a317d2c7b226e616d\
165        65223a227079746861676f726173222c2265706f6368223a327d5d2c227369676e6174757265223a22633539373\
166        9653333663163336234376361306162353239386536353562316264653235656564303866356232653536663361\
167        6439623964373638316164663138653164656562623731616135616132636234363564643831323239633637656\
168        33030326463396632663563363664663931333164366561633039666565373065227d";
169
170    fn dummy_tx_datums_from_markers_payload(payloads: Vec<EraMarkersPayload>) -> Vec<TxDatum> {
171        payloads
172            .into_iter()
173            .map(|payload| {
174                TxDatumBuilder::new()
175                    .add_field(TxDatumFieldValue::Bytes(payload.to_json_hex().unwrap()))
176                    .build()
177                    .unwrap()
178            })
179            .collect()
180    }
181
182    #[test]
183    fn golden_markers_payload_with_signature() {
184        EraMarkersPayload::from_json_hex(GOLDEN_ERA_MARKERS_PAYLOAD_WITH_SIGNATURE)
185            .expect("Decoding golden markers payload should not fail");
186    }
187
188    #[tokio::test]
189    async fn test_cardano_chain_adapter() {
190        let era_markers_signer = EraMarkersSigner::create_deterministic_signer();
191        let fake_address = "addr_test_123456".to_string();
192        let era_marker_payload_1 = EraMarkersPayload {
193            markers: vec![
194                EraMarker::new("thales", Some(Epoch(1))),
195                EraMarker::new("pythagoras", None),
196            ],
197            signature: None,
198        };
199        let era_marker_payload_2 = EraMarkersPayload {
200            markers: vec![
201                EraMarker::new("thales", Some(Epoch(1))),
202                EraMarker::new("pythagoras", Some(Epoch(2))),
203            ],
204            signature: None,
205        };
206        let mut fake_datums = dummy_tx_datums_from_markers_payload(vec![
207            era_marker_payload_1,
208            era_marker_payload_2
209                .clone()
210                .sign(&era_markers_signer)
211                .unwrap(),
212        ]);
213        fake_datums.push(TxDatum("not_valid_datum".to_string()));
214        let chain_observer = FakeChainObserver::default();
215        chain_observer.set_datums(fake_datums.clone()).await;
216        let cardano_chain_adapter = CardanoChainAdapter::new(
217            fake_address,
218            Arc::new(chain_observer),
219            era_markers_signer.create_verifier().to_verification_key(),
220        );
221        let markers = cardano_chain_adapter
222            .read()
223            .await
224            .expect("CardanoChainAdapter read should not fail");
225        let expected_markers = era_marker_payload_2.markers.to_owned();
226        assert_eq!(expected_markers, markers);
227    }
228}