mithril_signer/services/
single_signer.rs

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