mithril_era/adapters/
cardano_chain.rs

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