mithril_signer/services/
single_signer.rs

1use 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
18/// This is responsible for creating new instances of ProtocolInitializer.
19pub struct MithrilProtocolInitializerBuilder {}
20
21impl MithrilProtocolInitializerBuilder {
22    /// Create a ProtocolInitializer instance.
23    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/// The SingleSigner is the structure responsible for issuing [SingleSignature].
43#[cfg_attr(test, mockall::automock)]
44#[async_trait]
45pub trait SingleSigner: Sync + Send {
46    /// Computes single signature
47    async fn compute_single_signature(
48        &self,
49        protocol_message: &ProtocolMessage,
50    ) -> StdResult<Option<SingleSignature>>;
51
52    /// Get party id
53    fn get_party_id(&self) -> PartyId;
54}
55
56/// SingleSigner error structure.
57#[derive(Error, Debug)]
58pub enum SingleSignerError {
59    /// Cryptographic Signer creation error.
60    #[error("the protocol signer creation failed")]
61    ProtocolSignerCreationFailure(#[source] StdError),
62
63    /// Signature Error
64    #[error("Signature Error")]
65    SignatureFailed(#[source] StdError),
66
67    /// Avk computation Error
68    #[error("Aggregate verification key computation Error")]
69    AggregateVerificationKeyComputationFailed(#[source] StdError),
70}
71
72/// Implementation of the SingleSigner.
73pub struct MithrilSingleSigner {
74    party_id: PartyId,
75    epoch_service: EpochServiceWrapper,
76    logger: Logger,
77}
78
79impl MithrilSingleSigner {
80    /// Create a new instance of the MithrilSingleSigner.
81    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    /// Get party id
164    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(&current_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                    &current_signer.protocol_signer.verification_key(),
236                    &current_signer.protocol_signer.get_stake(),
237                    &avk,
238                    &expected_message
239                )
240                .is_ok(),
241            "produced single signature should be valid"
242        );
243    }
244}