mithril_signer/services/
single_signer.rs1use anyhow::{anyhow, Context};
2use async_trait::async_trait;
3use hex::ToHex;
4use slog::{info, trace, warn, Logger};
5use std::path::PathBuf;
6use thiserror::Error;
7
8use mithril_common::crypto_helper::{KESPeriod, ProtocolInitializer};
9use mithril_common::entities::{
10 PartyId, ProtocolMessage, ProtocolParameters, SingleSignatures, Stake,
11};
12use mithril_common::logging::LoggerExtensions;
13use mithril_common::protocol::{SignerBuilder, SingleSigner as ProtocolSingleSigner};
14use mithril_common::{StdError, StdResult};
15
16use crate::dependency_injection::EpochServiceWrapper;
17
18pub struct MithrilProtocolInitializerBuilder {}
20
21impl MithrilProtocolInitializerBuilder {
22 pub fn build(
24 stake: &Stake,
25 protocol_parameters: &ProtocolParameters,
26 kes_secret_key_path: Option<PathBuf>,
27 kes_period: Option<KESPeriod>,
28 ) -> StdResult<ProtocolInitializer> {
29 let mut rng = rand_core::OsRng;
30 let protocol_initializer = ProtocolInitializer::setup(
31 protocol_parameters.to_owned().into(),
32 kes_secret_key_path,
33 kes_period,
34 stake.to_owned(),
35 &mut rng,
36 )?;
37
38 Ok(protocol_initializer)
39 }
40}
41
42#[cfg_attr(test, mockall::automock)]
44#[async_trait]
45pub trait SingleSigner: Sync + Send {
46 async fn compute_single_signatures(
48 &self,
49 protocol_message: &ProtocolMessage,
50 ) -> StdResult<Option<SingleSignatures>>;
51
52 fn get_party_id(&self) -> PartyId;
54}
55
56#[derive(Error, Debug)]
58pub enum SingleSignerError {
59 #[error("the protocol signer creation failed")]
61 ProtocolSignerCreationFailure(#[source] StdError),
62
63 #[error("Signature Error")]
65 SignatureFailed(#[source] StdError),
66
67 #[error("Aggregate verification key computation Error")]
69 AggregateVerificationKeyComputationFailed(#[source] StdError),
70}
71
72pub struct MithrilSingleSigner {
74 party_id: PartyId,
75 epoch_service: EpochServiceWrapper,
76 logger: Logger,
77}
78
79impl MithrilSingleSigner {
80 pub fn new(party_id: PartyId, epoch_service: EpochServiceWrapper, logger: Logger) -> Self {
82 Self {
83 party_id,
84 epoch_service,
85 logger: logger.new_with_component_name::<Self>(),
86 }
87 }
88
89 async fn build_protocol_single_signer(&self) -> StdResult<ProtocolSingleSigner> {
90 let epoch_service = self.epoch_service.read().await;
91 let protocol_initializer =
92 epoch_service
93 .protocol_initializer()?
94 .as_ref()
95 .ok_or(anyhow!(
96 "Can not Sign or Compute AVK, No protocol initializer found for party_id: '{}'",
97 self.party_id.clone()
98 ))?;
99
100 let builder = SignerBuilder::new(
101 &epoch_service.current_signers_with_stake().await?,
102 &protocol_initializer.get_protocol_parameters().into(),
103 )
104 .with_context(|| "Mithril Single Signer can not build signer")
105 .map_err(SingleSignerError::ProtocolSignerCreationFailure)?;
106
107 let single_signer = builder
108 .restore_signer_from_initializer(self.party_id.clone(), protocol_initializer.clone())
109 .with_context(|| {
110 format!(
111 "Mithril Single Signer can not restore signer with party_id: '{}'",
112 self.party_id.clone()
113 )
114 })
115 .map_err(SingleSignerError::ProtocolSignerCreationFailure)?;
116
117 Ok(single_signer)
118 }
119}
120
121#[async_trait]
122impl SingleSigner for MithrilSingleSigner {
123 async fn compute_single_signatures(
124 &self,
125 protocol_message: &ProtocolMessage,
126 ) -> StdResult<Option<SingleSignatures>> {
127 let protocol_single_signer = self.build_protocol_single_signer().await?;
128
129 info!(
130 self.logger, "Signing protocol message";
131 "protocol_message" => #?protocol_message,
132 "signed message" => protocol_message.compute_hash().encode_hex::<String>()
133 );
134 let signatures = protocol_single_signer
135 .sign(protocol_message)
136 .with_context(|| {
137 format!(
138 "Mithril Single Signer can not sign protocol_message: '{:?}'",
139 protocol_message
140 )
141 })
142 .map_err(SingleSignerError::SignatureFailed)?;
143
144 match &signatures {
145 Some(signature) => {
146 trace!(
147 self.logger,
148 "Party #{}: lottery #{:?} won",
149 signature.party_id,
150 &signature.won_indexes
151 );
152 }
153 None => {
154 warn!(
155 self.logger,
156 "No signature computed, all lotteries were lost"
157 );
158 }
159 };
160
161 Ok(signatures)
162 }
163
164 fn get_party_id(&self) -> PartyId {
166 self.party_id.clone()
167 }
168}
169
170#[cfg(test)]
171mod tests {
172 use std::sync::Arc;
173 use tokio::sync::RwLock;
174
175 use crate::database::repository::{ProtocolInitializerRepository, StakePoolStore};
176 use crate::database::test_helper::main_db_connection;
177 use crate::services::MithrilEpochService;
178 use crate::test_tools::TestLogger;
179 use mithril_common::crypto_helper::ProtocolClerk;
180 use mithril_common::entities::{Epoch, ProtocolMessagePartKey};
181 use mithril_common::test_utils::MithrilFixtureBuilder;
182 use mithril_persistence::store::StakeStorer;
183
184 use super::*;
185
186 #[tokio::test]
187 async fn compute_single_signature_success() {
188 let snapshot_digest = "digest".to_string();
189 let fixture = MithrilFixtureBuilder::default().with_signers(5).build();
190 let current_signer = &fixture.signers_fixture()[0];
191 let clerk = ProtocolClerk::from_signer(¤t_signer.protocol_signer);
192 let avk = clerk.compute_avk();
193 let logger = TestLogger::stdout();
194 let connection = Arc::new(main_db_connection().unwrap());
195 let stake_store = {
196 let store = Arc::new(StakePoolStore::new(connection.clone(), None));
197 store
198 .save_stakes(
199 Epoch(10).offset_to_signer_retrieval_epoch().unwrap(),
200 fixture.stake_distribution(),
201 )
202 .await
203 .unwrap();
204 store
205 };
206 let protocol_initializer_store =
207 Arc::new(ProtocolInitializerRepository::new(connection, None));
208 let epoch_service =
209 MithrilEpochService::new(stake_store, protocol_initializer_store, logger.clone())
210 .set_data_to_default_or_fake(Epoch(10))
211 .alter_data(|data| {
212 data.protocol_initializer = Some(current_signer.protocol_initializer.clone());
213 data.current_signers = fixture.signers();
214 });
215
216 let single_signer = MithrilSingleSigner::new(
217 current_signer.party_id(),
218 Arc::new(RwLock::new(epoch_service)),
219 logger,
220 );
221
222 let mut protocol_message = ProtocolMessage::new();
223 protocol_message.set_message_part(ProtocolMessagePartKey::SnapshotDigest, snapshot_digest);
224 let sign_result = single_signer
225 .compute_single_signatures(&protocol_message)
226 .await
227 .expect("single signer should not fail")
228 .expect("single signer should produce a signature here");
229
230 let expected_message = protocol_message.compute_hash().as_bytes().to_vec();
231 let decoded_sig = sign_result.to_protocol_signature();
232 assert!(
233 decoded_sig
234 .verify(
235 &fixture.protocol_parameters().into(),
236 ¤t_signer.protocol_signer.verification_key(),
237 ¤t_signer.protocol_signer.get_stake(),
238 &avk,
239 &expected_message
240 )
241 .is_ok(),
242 "produced single signature should be valid"
243 );
244 }
245}