mithril_era/adapters/
cardano_chain.rs1use 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#[derive(Debug, Error)]
19pub enum EraMarkersPayloadError {
20 #[error("could not serialize message")]
22 SerializeMessage(#[source] StdError),
23
24 #[error("could not deserialize signature")]
26 DeserializeSignature(#[source] StdError),
27
28 #[error("could not verify signature: signature is missing")]
30 MissingSignature,
31
32 #[error("could not verify signature")]
34 VerifySignature(#[source] StdError),
35
36 #[error("could not create signature")]
38 CreateSignature(#[source] StdError),
39}
40
41#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
43pub struct EraMarkersPayload {
44 pub markers: Vec<EraMarker>,
46
47 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 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 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 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 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
103pub struct CardanoChainAdapter {
105 address: ChainAddress,
106 chain_observer: Arc<dyn ChainObserver>,
107 verification_key: EraMarkersVerifierVerificationKey,
108}
109
110impl CardanoChainAdapter {
111 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}