mithril_common/era/adapters/
cardano_chain.rs

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