mithril_era/adapters/
cardano_chain.rs1use 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#[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.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 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
101pub struct CardanoChainAdapter {
103 address: ChainAddress,
104 chain_observer: Arc<dyn ChainObserver>,
105 verification_key: EraMarkersVerifierVerificationKey,
106}
107
108impl CardanoChainAdapter {
109 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}