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, SingleSignature, 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_signature(
48 &self,
49 protocol_message: &ProtocolMessage,
50 ) -> StdResult<Option<SingleSignature>>;
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_signature(
124 &self,
125 protocol_message: &ProtocolMessage,
126 ) -> StdResult<Option<SingleSignature>> {
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 signature = protocol_single_signer
135 .sign(protocol_message)
136 .with_context(|| {
137 format!(
138 "Mithril Single Signer can not sign protocol_message: '{protocol_message:?}'"
139 )
140 })
141 .map_err(SingleSignerError::SignatureFailed)?;
142
143 match &signature {
144 Some(signature) => {
145 trace!(
146 self.logger,
147 "Party #{}: lottery #{:?} won",
148 signature.party_id,
149 &signature.won_indexes
150 );
151 }
152 None => {
153 warn!(
154 self.logger,
155 "No signature computed, all lotteries were lost"
156 );
157 }
158 };
159
160 Ok(signature)
161 }
162
163 fn get_party_id(&self) -> PartyId {
165 self.party_id.clone()
166 }
167}
168
169#[cfg(test)]
170mod tests {
171 use std::sync::Arc;
172 use tokio::sync::RwLock;
173
174 use crate::database::repository::{ProtocolInitializerRepository, StakePoolStore};
175 use crate::database::test_helper::main_db_connection;
176 use crate::services::MithrilEpochService;
177 use crate::test_tools::TestLogger;
178 use mithril_common::crypto_helper::ProtocolClerk;
179 use mithril_common::entities::{Epoch, ProtocolMessagePartKey};
180 use mithril_common::test_utils::MithrilFixtureBuilder;
181 use mithril_persistence::store::StakeStorer;
182
183 use super::*;
184
185 #[tokio::test]
186 async fn compute_single_signature_success() {
187 let snapshot_digest = "digest".to_string();
188 let fixture = MithrilFixtureBuilder::default().with_signers(5).build();
189 let current_signer = &fixture.signers_fixture()[0];
190 let clerk = ProtocolClerk::from_signer(¤t_signer.protocol_signer);
191 let avk = clerk.compute_avk();
192 let logger = TestLogger::stdout();
193 let connection = Arc::new(main_db_connection().unwrap());
194 let stake_store = {
195 let store = Arc::new(StakePoolStore::new(connection.clone(), None));
196 store
197 .save_stakes(
198 Epoch(10).offset_to_signer_retrieval_epoch().unwrap(),
199 fixture.stake_distribution(),
200 )
201 .await
202 .unwrap();
203 store
204 };
205 let protocol_initializer_store =
206 Arc::new(ProtocolInitializerRepository::new(connection, None));
207 let epoch_service =
208 MithrilEpochService::new(stake_store, protocol_initializer_store, logger.clone())
209 .set_data_to_default_or_fake(Epoch(10))
210 .alter_data(|data| {
211 data.protocol_initializer = Some(current_signer.protocol_initializer.clone());
212 data.current_signers = fixture.signers();
213 });
214
215 let single_signer = MithrilSingleSigner::new(
216 current_signer.party_id(),
217 Arc::new(RwLock::new(epoch_service)),
218 logger,
219 );
220
221 let mut protocol_message = ProtocolMessage::new();
222 protocol_message.set_message_part(ProtocolMessagePartKey::SnapshotDigest, snapshot_digest);
223 let sign_result = single_signer
224 .compute_single_signature(&protocol_message)
225 .await
226 .expect("single signer should not fail")
227 .expect("single signer should produce a signature here");
228
229 let expected_message = protocol_message.compute_hash().as_bytes().to_vec();
230 let decoded_sig = sign_result.to_protocol_signature();
231 assert!(
232 decoded_sig
233 .verify(
234 &fixture.protocol_parameters().into(),
235 ¤t_signer.protocol_signer.verification_key(),
236 ¤t_signer.protocol_signer.get_stake(),
237 &avk,
238 &expected_message
239 )
240 .is_ok(),
241 "produced single signature should be valid"
242 );
243 }
244}