mithril_era/adapters/
cardano_chain.rs

1use anyhow::{Context, anyhow};
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    EraMarkersSigner, EraMarkersVerifier, EraMarkersVerifierSignature,
11    EraMarkersVerifierVerificationKey, key_decode_hex, key_encode_hex,
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.signature.ok_or(EraMarkersPayloadError::MissingSignature)?;
77        let markers_verifier: EraMarkersVerifier =
78            EraMarkersVerifier::from_verification_key(verification_key);
79
80        markers_verifier
81            .verify(&self.message_to_bytes()?, &signature)
82            .with_context(|| "era markers payload could not verify signature")
83            .map_err(EraMarkersPayloadError::VerifySignature)
84    }
85
86    /// Sign an era markers payload
87    pub fn sign(self, signer: &EraMarkersSigner) -> Result<Self, EraMarkersPayloadError> {
88        let signature = signer.sign(
89            &self
90                .message_to_bytes()
91                .map_err(|e| EraMarkersPayloadError::CreateSignature(e.into()))?,
92        );
93
94        Ok(Self {
95            markers: self.markers,
96            signature: Some(signature),
97        })
98    }
99}
100
101/// Cardano Chain adapter retrieves era markers on chain
102pub struct CardanoChainAdapter {
103    address: ChainAddress,
104    chain_observer: Arc<dyn ChainObserver>,
105    verification_key: EraMarkersVerifierVerificationKey,
106}
107
108impl CardanoChainAdapter {
109    /// CardanoChainAdapter factory
110    pub fn new(
111        address: ChainAddress,
112        chain_observer: Arc<dyn ChainObserver>,
113        verification_key: EraMarkersVerifierVerificationKey,
114    ) -> Self {
115        Self {
116            address,
117            chain_observer,
118            verification_key,
119        }
120    }
121}
122
123#[async_trait]
124impl EraReaderAdapter for CardanoChainAdapter {
125    async fn read(&self) -> StdResult<Vec<EraMarker>> {
126        let tx_datums = self.chain_observer.get_current_datums(&self.address).await?;
127        let markers_list = tx_datums
128            .into_iter()
129            .filter_map(|datum| datum.get_fields_by_type(&TxDatumFieldTypeName::Bytes).ok())
130            .map(|fields| {
131                fields
132                    .iter()
133                    .filter_map(|field_value| field_value.as_str().map(|s| s.to_string()))
134                    .collect::<Vec<String>>()
135                    .join("")
136            })
137            .filter_map(|field_value_str| EraMarkersPayload::from_json_hex(&field_value_str).ok())
138            .filter_map(|era_markers_payload| {
139                era_markers_payload
140                    .verify_signature(self.verification_key)
141                    .ok()
142                    .map(|_| era_markers_payload.markers)
143            })
144            .collect::<Vec<Vec<EraMarker>>>();
145
146        Ok(markers_list.first().unwrap_or(&Vec::new()).to_owned())
147    }
148}
149
150#[cfg(test)]
151mod test {
152    use mithril_cardano_node_chain::entities::{TxDatum, TxDatumBuilder, TxDatumFieldValue};
153    use mithril_cardano_node_chain::test::double::FakeChainObserver;
154    use mithril_common::entities::Epoch;
155
156    use super::*;
157
158    const GOLDEN_ERA_MARKERS_PAYLOAD_WITH_SIGNATURE: &str = "7b226d61726b657273223a5b7b226e616d65223a227468616c6573222c2265706f6368223a317d2c7b226e616d\
159        65223a227079746861676f726173222c2265706f6368223a327d5d2c227369676e6174757265223a22633539373\
160        9653333663163336234376361306162353239386536353562316264653235656564303866356232653536663361\
161        6439623964373638316164663138653164656562623731616135616132636234363564643831323239633637656\
162        33030326463396632663563363664663931333164366561633039666565373065227d";
163
164    fn dummy_tx_datums_from_markers_payload(payloads: Vec<EraMarkersPayload>) -> Vec<TxDatum> {
165        payloads
166            .into_iter()
167            .map(|payload| {
168                TxDatumBuilder::new()
169                    .add_field(TxDatumFieldValue::Bytes(payload.to_json_hex().unwrap()))
170                    .build()
171                    .unwrap()
172            })
173            .collect()
174    }
175
176    #[test]
177    fn golden_markers_payload_with_signature() {
178        EraMarkersPayload::from_json_hex(GOLDEN_ERA_MARKERS_PAYLOAD_WITH_SIGNATURE)
179            .expect("Decoding golden markers payload should not fail");
180    }
181
182    #[tokio::test]
183    async fn test_cardano_chain_adapter() {
184        let era_markers_signer = EraMarkersSigner::create_deterministic_signer();
185        let fake_address = "addr_test_123456".to_string();
186        let era_marker_payload_1 = EraMarkersPayload {
187            markers: vec![
188                EraMarker::new("thales", Some(Epoch(1))),
189                EraMarker::new("pythagoras", None),
190            ],
191            signature: None,
192        };
193        let era_marker_payload_2 = EraMarkersPayload {
194            markers: vec![
195                EraMarker::new("thales", Some(Epoch(1))),
196                EraMarker::new("pythagoras", Some(Epoch(2))),
197            ],
198            signature: None,
199        };
200        let mut fake_datums = dummy_tx_datums_from_markers_payload(vec![
201            era_marker_payload_1,
202            era_marker_payload_2.clone().sign(&era_markers_signer).unwrap(),
203        ]);
204        fake_datums.push(TxDatum("not_valid_datum".to_string()));
205        let chain_observer = FakeChainObserver::default();
206        chain_observer.set_datums(fake_datums.clone()).await;
207        let cardano_chain_adapter = CardanoChainAdapter::new(
208            fake_address,
209            Arc::new(chain_observer),
210            era_markers_signer.create_verifier().to_verification_key(),
211        );
212        let markers = cardano_chain_adapter
213            .read()
214            .await
215            .expect("CardanoChainAdapter read should not fail");
216        let expected_markers = era_marker_payload_2.markers.to_owned();
217        assert_eq!(expected_markers, markers);
218    }
219}