mithril_signer/services/signable_builder/
signable_seed_builder.rs1use anyhow::{Context, anyhow};
7use async_trait::async_trait;
8use std::sync::Arc;
9use tokio::sync::RwLock;
10
11use mithril_common::{
12 StdResult,
13 crypto_helper::ProtocolInitializer,
14 entities::{ProtocolMessagePartValue, ProtocolParameters, SignerWithStake},
15 protocol::SignerBuilder,
16 signable_builder::SignableSeedBuilder,
17};
18
19use crate::{services::EpochService, store::ProtocolInitializerStorer};
20
21pub struct SignerSignableSeedBuilder {
23 epoch_service: Arc<RwLock<dyn EpochService>>,
24 protocol_initializer_store: Arc<dyn ProtocolInitializerStorer>,
25}
26
27impl SignerSignableSeedBuilder {
28 pub fn new(
30 epoch_service: Arc<RwLock<dyn EpochService>>,
31 protocol_initializer_store: Arc<dyn ProtocolInitializerStorer>,
32 ) -> Self {
33 Self {
34 epoch_service,
35 protocol_initializer_store,
36 }
37 }
38
39 fn compute_encode_avk(
40 &self,
41 protocol_initializer: ProtocolInitializer,
42 signers_with_stake: &[SignerWithStake],
43 ) -> StdResult<String> {
44 let signer_builder = SignerBuilder::new(
45 signers_with_stake,
46 &protocol_initializer.get_protocol_parameters().into(),
47 )
48 .with_context(|| "SignerSignableSeedBuilder can not compute aggregate verification key")?;
49
50 let encoded_avk = signer_builder
51 .compute_aggregate_verification_key()
52 .to_json_hex()
53 .with_context(
54 || "SignerSignableSeedBuilder can not serialize aggregate verification key",
55 )?;
56
57 Ok(encoded_avk)
58 }
59}
60
61#[async_trait]
62impl SignableSeedBuilder for SignerSignableSeedBuilder {
63 async fn compute_next_aggregate_verification_key(&self) -> StdResult<ProtocolMessagePartValue> {
64 let epoch_service = self.epoch_service.read().await;
65 let epoch = (*epoch_service).epoch_of_current_data()?;
66 let next_signer_retrieval_epoch = epoch.offset_to_next_signer_retrieval_epoch();
67 let next_protocol_initializer = self
68 .protocol_initializer_store
69 .get_protocol_initializer(next_signer_retrieval_epoch)
70 .await?
71 .ok_or_else(|| {
72 anyhow!("can not get protocol_initializer at epoch {next_signer_retrieval_epoch}")
73 })?;
74 let next_signers_with_stake = epoch_service.next_signers_with_stake().await?;
75 let next_aggregate_verification_key =
76 self.compute_encode_avk(next_protocol_initializer, &next_signers_with_stake)?;
77
78 Ok(next_aggregate_verification_key)
79 }
80
81 async fn compute_next_protocol_parameters(&self) -> StdResult<ProtocolMessagePartValue> {
82 let epoch_service = self.epoch_service.read().await;
83 let epoch = (*epoch_service).epoch_of_current_data()?;
84 let next_signer_retrieval_epoch = epoch.offset_to_next_signer_retrieval_epoch();
85 let next_protocol_initializer = self
86 .protocol_initializer_store
87 .get_protocol_initializer(next_signer_retrieval_epoch)
88 .await?
89 .ok_or_else(|| {
90 anyhow!("can not get protocol_initializer at epoch {next_signer_retrieval_epoch}")
91 })?;
92 let next_protocol_parameters: ProtocolParameters =
93 next_protocol_initializer.get_protocol_parameters().into();
94
95 Ok(next_protocol_parameters.compute_hash())
96 }
97
98 async fn compute_current_epoch(&self) -> StdResult<ProtocolMessagePartValue> {
99 let epoch_service = self.epoch_service.read().await;
100 let current_epoch = epoch_service.epoch_of_current_data()?.to_string();
101
102 Ok(current_epoch)
103 }
104}
105
106#[cfg(test)]
107mod tests {
108
109 use mithril_common::{
110 entities::Epoch, entities::ProtocolParameters, test_utils::MithrilFixtureBuilder,
111 };
112
113 use crate::{
114 services::mock_epoch_service::MockEpochServiceImpl, store::MockProtocolInitializerStorer,
115 };
116
117 use super::*;
118
119 struct MockDependencyInjector {
120 mock_epoch_service: MockEpochServiceImpl,
121 mock_protocol_initializer_store: MockProtocolInitializerStorer,
122 }
123
124 impl MockDependencyInjector {
125 fn new() -> MockDependencyInjector {
126 MockDependencyInjector {
127 mock_epoch_service: MockEpochServiceImpl::new(),
128 mock_protocol_initializer_store: MockProtocolInitializerStorer::new(),
129 }
130 }
131
132 fn build_signable_builder_service(self) -> SignerSignableSeedBuilder {
133 SignerSignableSeedBuilder::new(
134 Arc::new(RwLock::new(self.mock_epoch_service)),
135 Arc::new(self.mock_protocol_initializer_store),
136 )
137 }
138 }
139
140 #[tokio::test]
141 async fn test_compute_next_aggregate_verification_key_protocol_message_value() {
142 let epoch = Epoch(5);
143 let next_fixture = MithrilFixtureBuilder::default().with_signers(4).build();
144 let protocol_initializer = next_fixture.signers_fixture()[0].protocol_initializer.clone();
145 let next_signers_with_stake = next_fixture.signers_with_stake();
146 let mut mock_container = MockDependencyInjector::new();
147 mock_container.mock_epoch_service =
148 MockEpochServiceImpl::new_with_config(|mock_epoch_service| {
149 mock_epoch_service
150 .expect_epoch_of_current_data()
151 .return_once(move || Ok(epoch))
152 .once();
153 mock_epoch_service
154 .expect_next_signers_with_stake()
155 .return_once(move || Ok(next_signers_with_stake))
156 .once();
157 });
158 mock_container
159 .mock_protocol_initializer_store
160 .expect_get_protocol_initializer()
161 .return_once(move |_| Ok(Some(protocol_initializer)))
162 .once();
163 let signable_seed_builder = mock_container.build_signable_builder_service();
164
165 let next_aggregate_verification_key = signable_seed_builder
166 .compute_next_aggregate_verification_key()
167 .await
168 .unwrap();
169
170 assert_eq!(
171 next_aggregate_verification_key,
172 next_fixture.compute_and_encode_avk()
173 );
174 }
175
176 #[tokio::test]
177 async fn test_compute_next_protocol_parameters_protocol_message_value() {
178 let epoch = Epoch(5);
179 let next_fixture = MithrilFixtureBuilder::default().with_signers(4).build();
180 let protocol_initializer = next_fixture.signers_fixture()[0].protocol_initializer.clone();
181 let protocol_parameters: ProtocolParameters =
182 protocol_initializer.get_protocol_parameters().into();
183 let expected_next_protocol_parameters = protocol_parameters.compute_hash();
184 let mut mock_container = MockDependencyInjector::new();
185 mock_container.mock_epoch_service =
186 MockEpochServiceImpl::new_with_config(|mock_epoch_service| {
187 mock_epoch_service
188 .expect_epoch_of_current_data()
189 .return_once(move || Ok(epoch))
190 .once();
191 });
192 mock_container
193 .mock_protocol_initializer_store
194 .expect_get_protocol_initializer()
195 .return_once(move |_| Ok(Some(protocol_initializer)))
196 .once();
197 let signable_seed_builder = mock_container.build_signable_builder_service();
198
199 let next_protocol_parameters = signable_seed_builder
200 .compute_next_protocol_parameters()
201 .await
202 .unwrap();
203
204 assert_eq!(next_protocol_parameters, expected_next_protocol_parameters);
205 }
206
207 #[tokio::test]
208 async fn test_compute_current_epoch_protocol_message_value() {
209 let epoch = Epoch(5);
210 let expected_current_epoch = epoch.to_string();
211 let mut mock_container = MockDependencyInjector::new();
212 mock_container.mock_epoch_service =
213 MockEpochServiceImpl::new_with_config(|mock_epoch_service| {
214 mock_epoch_service
215 .expect_epoch_of_current_data()
216 .return_once(move || Ok(epoch))
217 .once();
218 });
219 let signable_seed_builder = mock_container.build_signable_builder_service();
220
221 let current_epoch = signable_seed_builder.compute_current_epoch().await.unwrap();
222
223 assert_eq!(current_epoch, expected_current_epoch);
224 }
225}