mithril_common/era/adapters/
cardano_chain.rs1use 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#[derive(Debug, Error)]
18pub enum EraMarkersPayloadError {
19 #[error("could not serialize message")]
21 SerializeMessage(#[source] StdError),
22
23 #[error("could not deserialize signature")]
25 DeserializeSignature(#[source] StdError),
26
27 #[error("could not verify signature: signature is missing")]
29 MissingSignature,
30
31 #[error("could not verify signature")]
33 VerifySignature(#[source] StdError),
34
35 #[error("could not create signature")]
37 CreateSignature(#[source] StdError),
38}
39
40#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
42pub struct EraMarkersPayload {
43 pub markers: Vec<EraMarker>,
45
46 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 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 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 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 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
102pub struct CardanoChainAdapter {
104 address: ChainAddress,
105 chain_observer: Arc<dyn ChainObserver>,
106 verification_key: EraMarkersVerifierVerificationKey,
107}
108
109impl CardanoChainAdapter {
110 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}