mithril_signer/services/signable_builder/
signable_seed_builder.rs1use anyhow::Context;
7use async_trait::async_trait;
8use std::sync::Arc;
9use tokio::sync::RwLock;
10
11use mithril_common::{
12 StdResult,
13 crypto_helper::{ProtocolAggregateVerificationKeyForConcatenation, 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: ProtocolAggregateVerificationKeyForConcatenation = signer_builder
51 .compute_aggregate_verification_key()
52 .to_concatenation_aggregate_verification_key()
53 .to_owned()
54 .into();
55 let encoded_avk = encoded_avk.to_json_hex().with_context(
56 || "SignerSignableSeedBuilder can not serialize aggregate verification key",
57 )?;
58
59 Ok(encoded_avk)
60 }
61}
62
63#[async_trait]
64impl SignableSeedBuilder for SignerSignableSeedBuilder {
65 async fn compute_next_aggregate_verification_key_for_concatenation(
66 &self,
67 ) -> StdResult<ProtocolMessagePartValue> {
68 let epoch_service = self.epoch_service.read().await;
69 let epoch = (*epoch_service).epoch_of_current_data()?;
70 let next_signer_retrieval_epoch = epoch.offset_to_next_signer_retrieval_epoch();
71 let next_protocol_initializer = self
72 .protocol_initializer_store
73 .get_protocol_initializer(next_signer_retrieval_epoch)
74 .await?
75 .with_context(|| {
76 format!("can not get protocol_initializer at epoch {next_signer_retrieval_epoch}")
77 })?;
78 let next_signers_with_stake = epoch_service.next_signers_with_stake().await?;
79 let next_aggregate_verification_key =
80 self.compute_encode_avk(next_protocol_initializer, &next_signers_with_stake)?;
81
82 Ok(next_aggregate_verification_key)
83 }
84
85 async fn compute_next_protocol_parameters(&self) -> StdResult<ProtocolMessagePartValue> {
86 let epoch_service = self.epoch_service.read().await;
87 let epoch = (*epoch_service).epoch_of_current_data()?;
88 let next_signer_retrieval_epoch = epoch.offset_to_next_signer_retrieval_epoch();
89 let next_protocol_initializer = self
90 .protocol_initializer_store
91 .get_protocol_initializer(next_signer_retrieval_epoch)
92 .await?
93 .with_context(|| {
94 format!("can not get protocol_initializer at epoch {next_signer_retrieval_epoch}")
95 })?;
96 let next_protocol_parameters: ProtocolParameters =
97 next_protocol_initializer.get_protocol_parameters().into();
98
99 Ok(next_protocol_parameters.compute_hash())
100 }
101
102 async fn compute_current_epoch(&self) -> StdResult<ProtocolMessagePartValue> {
103 let epoch_service = self.epoch_service.read().await;
104 let current_epoch = epoch_service.epoch_of_current_data()?.to_string();
105
106 Ok(current_epoch)
107 }
108}
109
110#[cfg(test)]
111mod tests {
112
113 use mithril_common::{
114 entities::Epoch, entities::ProtocolParameters, test::builder::MithrilFixtureBuilder,
115 };
116
117 use crate::{
118 services::mock_epoch_service::MockEpochServiceImpl, store::MockProtocolInitializerStorer,
119 };
120
121 use super::*;
122
123 struct MockDependencyInjector {
124 mock_epoch_service: MockEpochServiceImpl,
125 mock_protocol_initializer_store: MockProtocolInitializerStorer,
126 }
127
128 impl MockDependencyInjector {
129 fn new() -> MockDependencyInjector {
130 MockDependencyInjector {
131 mock_epoch_service: MockEpochServiceImpl::new(),
132 mock_protocol_initializer_store: MockProtocolInitializerStorer::new(),
133 }
134 }
135
136 fn build_signable_builder_service(self) -> SignerSignableSeedBuilder {
137 SignerSignableSeedBuilder::new(
138 Arc::new(RwLock::new(self.mock_epoch_service)),
139 Arc::new(self.mock_protocol_initializer_store),
140 )
141 }
142 }
143
144 #[tokio::test]
145 async fn test_compute_next_aggregate_verification_key_protocol_message_value() {
146 let epoch = Epoch(5);
147 let next_fixture = MithrilFixtureBuilder::default().with_signers(4).build();
148 let protocol_initializer = next_fixture.signers_fixture()[0].protocol_initializer.clone();
149 let next_signers_with_stake = next_fixture.signers_with_stake();
150 let mut mock_container = MockDependencyInjector::new();
151 mock_container.mock_epoch_service =
152 MockEpochServiceImpl::new_with_config(|mock_epoch_service| {
153 mock_epoch_service
154 .expect_epoch_of_current_data()
155 .return_once(move || Ok(epoch))
156 .once();
157 mock_epoch_service
158 .expect_next_signers_with_stake()
159 .return_once(move || Ok(next_signers_with_stake))
160 .once();
161 });
162 mock_container
163 .mock_protocol_initializer_store
164 .expect_get_protocol_initializer()
165 .return_once(move |_| Ok(Some(protocol_initializer)))
166 .once();
167 let signable_seed_builder = mock_container.build_signable_builder_service();
168
169 let next_aggregate_verification_key = signable_seed_builder
170 .compute_next_aggregate_verification_key_for_concatenation()
171 .await
172 .unwrap();
173
174 assert_eq!(
175 next_aggregate_verification_key,
176 next_fixture.compute_and_encode_concatenation_aggregate_verification_key()
177 );
178 }
179
180 #[tokio::test]
181 async fn test_compute_next_protocol_parameters_protocol_message_value() {
182 let epoch = Epoch(5);
183 let next_fixture = MithrilFixtureBuilder::default().with_signers(4).build();
184 let protocol_initializer = next_fixture.signers_fixture()[0].protocol_initializer.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}