mithril_stm/protocol/single_signature/
signature.rs

1use std::{
2    cmp::Ordering,
3    hash::{Hash, Hasher},
4};
5
6use anyhow::{Context, anyhow};
7use serde::{Deserialize, Serialize};
8
9use crate::{
10    AggregateVerificationKey, Index, MembershipDigest, Parameters, Stake, StmResult,
11    VerificationKey, is_lottery_won, signature_scheme::BlsSignature,
12};
13
14use super::SignatureError;
15
16/// Signature created by a single party who has won the lottery.
17#[derive(Debug, Clone, Serialize, Deserialize)]
18pub struct SingleSignature {
19    /// The signature from the underlying MSP scheme.
20    pub sigma: BlsSignature,
21    /// The index(es) for which the signature is valid
22    pub indexes: Vec<Index>,
23    /// Merkle tree index of the signer.
24    pub signer_index: Index,
25}
26
27impl SingleSignature {
28    /// Verify an stm signature by checking that the lottery was won, the merkle path is correct,
29    /// the indexes are in the desired range and the underlying multi signature validates.
30    pub fn verify<D: MembershipDigest>(
31        &self,
32        params: &Parameters,
33        pk: &VerificationKey,
34        stake: &Stake,
35        avk: &AggregateVerificationKey<D>,
36        msg: &[u8],
37    ) -> StmResult<()> {
38        let msgp = avk.get_merkle_tree_batch_commitment().concatenate_with_message(msg);
39        self.sigma.verify(&msgp, pk).with_context(|| {
40            format!(
41                "Single signature verification failed for signer index {}.",
42                self.signer_index
43            )
44        })?;
45        self.check_indices(params, stake, &msgp, &avk.get_total_stake())
46            .with_context(|| {
47                format!(
48                    "Single signature verification failed for signer index {}.",
49                    self.signer_index
50                )
51            })?;
52        Ok(())
53    }
54
55    /// Verify that all indices of a signature are valid.
56    pub(crate) fn check_indices(
57        &self,
58        params: &Parameters,
59        stake: &Stake,
60        msg: &[u8],
61        total_stake: &Stake,
62    ) -> StmResult<()> {
63        for &index in &self.indexes {
64            if index > params.m {
65                return Err(anyhow!(SignatureError::IndexBoundFailed(index, params.m)));
66            }
67
68            let ev = self.sigma.evaluate_dense_mapping(msg, index);
69
70            if !is_lottery_won(params.phi_f, ev, *stake, *total_stake) {
71                return Err(anyhow!(SignatureError::LotteryLost));
72            }
73        }
74
75        Ok(())
76    }
77
78    /// Convert an `SingleSignature` into bytes
79    ///
80    /// # Layout
81    /// * Stake
82    /// * Number of valid indexes (as u64)
83    /// * Indexes of the signature
84    /// * Public Key
85    /// * Signature
86    /// * Merkle index of the signer.
87    pub fn to_bytes(&self) -> Vec<u8> {
88        let mut output = Vec::new();
89        output.extend_from_slice(&(self.indexes.len() as u64).to_be_bytes());
90
91        for index in &self.indexes {
92            output.extend_from_slice(&index.to_be_bytes());
93        }
94
95        output.extend_from_slice(&self.sigma.to_bytes());
96
97        output.extend_from_slice(&self.signer_index.to_be_bytes());
98        output
99    }
100
101    /// Extract a batch compatible `SingleSignature` from a byte slice.
102    pub fn from_bytes<D: MembershipDigest>(bytes: &[u8]) -> StmResult<SingleSignature> {
103        let mut u64_bytes = [0u8; 8];
104
105        u64_bytes.copy_from_slice(bytes.get(0..8).ok_or(SignatureError::SerializationError)?);
106        let nr_indexes = u64::from_be_bytes(u64_bytes) as usize;
107
108        let mut indexes = Vec::new();
109        for i in 0..nr_indexes {
110            u64_bytes.copy_from_slice(
111                bytes
112                    .get(8 + i * 8..16 + i * 8)
113                    .ok_or(SignatureError::SerializationError)?,
114            );
115            indexes.push(u64::from_be_bytes(u64_bytes));
116        }
117
118        let offset = 8 + nr_indexes * 8;
119        let sigma = BlsSignature::from_bytes(
120            bytes
121                .get(offset..offset + 48)
122                .ok_or(SignatureError::SerializationError)?,
123        )?;
124
125        u64_bytes.copy_from_slice(
126            bytes
127                .get(offset + 48..offset + 56)
128                .ok_or(SignatureError::SerializationError)?,
129        );
130        let signer_index = u64::from_be_bytes(u64_bytes);
131
132        Ok(SingleSignature {
133            sigma,
134            indexes,
135            signer_index,
136        })
137    }
138
139    /// Compare two `SingleSignature` by their signers' merkle tree indexes.
140    fn compare_signer_index(&self, other: &Self) -> Ordering {
141        self.signer_index.cmp(&other.signer_index)
142    }
143
144    /// Compare two `SingleSignature` by their signers' merkle tree indexes.
145    #[deprecated(since = "0.5.0", note = "This function will be removed")]
146    pub fn cmp_stm_sig(&self, other: &Self) -> Ordering {
147        Self::compare_signer_index(self, other)
148    }
149}
150
151impl Hash for SingleSignature {
152    fn hash<H: Hasher>(&self, state: &mut H) {
153        Hash::hash_slice(&self.sigma.to_bytes(), state)
154    }
155}
156
157impl PartialEq for SingleSignature {
158    fn eq(&self, other: &Self) -> bool {
159        self.sigma == other.sigma
160    }
161}
162
163impl Eq for SingleSignature {}
164
165impl PartialOrd for SingleSignature {
166    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
167        Some(std::cmp::Ord::cmp(self, other))
168    }
169}
170
171impl Ord for SingleSignature {
172    fn cmp(&self, other: &Self) -> Ordering {
173        self.signer_index.cmp(&other.signer_index)
174    }
175}
176
177#[cfg(test)]
178mod tests {
179    mod golden {
180        use rand_chacha::ChaCha20Rng;
181        use rand_core::SeedableRng;
182
183        use crate::{
184            ClosedKeyRegistration, KeyRegistration, MithrilMembershipDigest, Parameters, Signer,
185            SingleSignature,
186            signature_scheme::{BlsSigningKey, BlsVerificationKeyProofOfPossession},
187        };
188
189        const GOLDEN_JSON: &str = r#"
190        {
191            "sigma": [
192                149, 157, 201, 187, 140, 54, 0, 128, 209, 88, 16, 203, 61, 78, 77, 98, 161,
193                133, 58, 152, 29, 74, 217, 113, 64, 100, 10, 161, 186, 167, 133, 114, 211,
194                153, 218, 56, 223, 84, 105, 242, 41, 54, 224, 170, 208, 185, 126, 83
195            ],
196            "indexes": [1, 4, 5, 8],
197            "signer_index": 1
198        }"#;
199
200        fn golden_value() -> SingleSignature {
201            let mut rng = ChaCha20Rng::from_seed([0u8; 32]);
202            let msg = [0u8; 16];
203            let params = Parameters {
204                m: 10,
205                k: 5,
206                phi_f: 0.8,
207            };
208            let sk_1 = BlsSigningKey::generate(&mut rng);
209            let sk_2 = BlsSigningKey::generate(&mut rng);
210            let pk_1 = BlsVerificationKeyProofOfPossession::from(&sk_1);
211            let pk_2 = BlsVerificationKeyProofOfPossession::from(&sk_2);
212            let mut key_reg = KeyRegistration::init();
213            key_reg.register(1, pk_1).unwrap();
214            key_reg.register(1, pk_2).unwrap();
215            let closed_key_reg: ClosedKeyRegistration<MithrilMembershipDigest> = key_reg.close();
216            let signer = Signer::set_signer(1, 1, params, sk_1, pk_1.vk, closed_key_reg);
217            signer.sign(&msg).unwrap()
218        }
219
220        #[test]
221        fn golden_conversions() {
222            let value = serde_json::from_str(GOLDEN_JSON)
223                .expect("This JSON deserialization should not fail");
224            assert_eq!(golden_value(), value);
225
226            let serialized =
227                serde_json::to_string(&value).expect("This JSON serialization should not fail");
228            let golden_serialized = serde_json::to_string(&golden_value())
229                .expect("This JSON serialization should not fail");
230            assert_eq!(golden_serialized, serialized);
231        }
232    }
233}