mithril_era/adapters/
cardano_chain.rs1use 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#[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).with_context(|| "era markers payload could not be json hex encoded")
60 }
61
62 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 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 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
98pub struct CardanoChainAdapter {
100 address: ChainAddress,
101 chain_observer: Arc<dyn ChainObserver>,
102 verification_key: EraMarkersVerifierVerificationKey,
103}
104
105impl CardanoChainAdapter {
106 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}