mithril_signer/services/signable_builder/
signable_seed_builder.rs1use anyhow::{anyhow, Context};
7use async_trait::async_trait;
8use std::sync::Arc;
9use tokio::sync::RwLock;
10
11use mithril_common::{
12 crypto_helper::ProtocolInitializer,
13 entities::{ProtocolMessagePartValue, ProtocolParameters, SignerWithStake},
14 protocol::SignerBuilder,
15 signable_builder::SignableSeedBuilder,
16 StdResult,
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]
145 .protocol_initializer
146 .clone();
147 let next_signers_with_stake = next_fixture.signers_with_stake();
148 let mut mock_container = MockDependencyInjector::new();
149 mock_container.mock_epoch_service =
150 MockEpochServiceImpl::new_with_config(|mock_epoch_service| {
151 mock_epoch_service
152 .expect_epoch_of_current_data()
153 .return_once(move || Ok(epoch))
154 .once();
155 mock_epoch_service
156 .expect_next_signers_with_stake()
157 .return_once(move || Ok(next_signers_with_stake))
158 .once();
159 });
160 mock_container
161 .mock_protocol_initializer_store
162 .expect_get_protocol_initializer()
163 .return_once(move |_| Ok(Some(protocol_initializer)))
164 .once();
165 let signable_seed_builder = mock_container.build_signable_builder_service();
166
167 let next_aggregate_verification_key = signable_seed_builder
168 .compute_next_aggregate_verification_key()
169 .await
170 .unwrap();
171
172 assert_eq!(
173 next_aggregate_verification_key,
174 next_fixture.compute_and_encode_avk()
175 );
176 }
177
178 #[tokio::test]
179 async fn test_compute_next_protocol_parameters_protocol_message_value() {
180 let epoch = Epoch(5);
181 let next_fixture = MithrilFixtureBuilder::default().with_signers(4).build();
182 let protocol_initializer = next_fixture.signers_fixture()[0]
183 .protocol_initializer
184 .clone();
185 let protocol_parameters: ProtocolParameters =
186 protocol_initializer.get_protocol_parameters().into();
187 let expected_next_protocol_parameters = protocol_parameters.compute_hash();
188 let mut mock_container = MockDependencyInjector::new();
189 mock_container.mock_epoch_service =
190 MockEpochServiceImpl::new_with_config(|mock_epoch_service| {
191 mock_epoch_service
192 .expect_epoch_of_current_data()
193 .return_once(move || Ok(epoch))
194 .once();
195 });
196 mock_container
197 .mock_protocol_initializer_store
198 .expect_get_protocol_initializer()
199 .return_once(move |_| Ok(Some(protocol_initializer)))
200 .once();
201 let signable_seed_builder = mock_container.build_signable_builder_service();
202
203 let next_protocol_parameters = signable_seed_builder
204 .compute_next_protocol_parameters()
205 .await
206 .unwrap();
207
208 assert_eq!(next_protocol_parameters, expected_next_protocol_parameters);
209 }
210
211 #[tokio::test]
212 async fn test_compute_current_epoch_protocol_message_value() {
213 let epoch = Epoch(5);
214 let expected_current_epoch = epoch.to_string();
215 let mut mock_container = MockDependencyInjector::new();
216 mock_container.mock_epoch_service =
217 MockEpochServiceImpl::new_with_config(|mock_epoch_service| {
218 mock_epoch_service
219 .expect_epoch_of_current_data()
220 .return_once(move || Ok(epoch))
221 .once();
222 });
223 let signable_seed_builder = mock_container.build_signable_builder_service();
224
225 let current_epoch = signable_seed_builder.compute_current_epoch().await.unwrap();
226
227 assert_eq!(current_epoch, expected_current_epoch);
228 }
229}