hotshot_types/
signature_key.rs

1// Copyright (c) 2021-2024 Espresso Systems (espressosys.com)
2// This file is part of the HotShot repository.
3
4// You should have received a copy of the MIT License
5// along with the HotShot repository. If not, see <https://mit-license.org/>.
6
7//! Types and structs for the hotshot signature keys
8
9use alloy::primitives::U256;
10use ark_serialize::SerializationError;
11use bitvec::{slice::BitSlice, vec::BitVec};
12use generic_array::GenericArray;
13use jf_signature::{
14    SignatureError, SignatureScheme,
15    bls_over_bn254::{BLSOverBN254CurveSignatureScheme, KeyPair, SignKey, VerKey},
16};
17use rand::SeedableRng;
18use rand_chacha::ChaCha20Rng;
19use tracing::instrument;
20
21use crate::{
22    light_client::{CircuitField, LightClientState, StakeTableState},
23    qc::{BitVectorQc, QcParams},
24    stake_table::StakeTableEntry,
25    traits::{
26        qc::QuorumCertificateScheme,
27        signature_key::{
28            BuilderSignatureKey, LCV1StateSignatureKey, LCV2StateSignatureKey,
29            LCV3StateSignatureKey, PrivateSignatureKey, SignatureKey, StateSignatureKey,
30        },
31    },
32};
33
34/// BLS private key used to sign a consensus message
35pub type BLSPrivKey = SignKey;
36/// BLS public key used to verify a consensus signature
37pub type BLSPubKey = VerKey;
38/// BLS key pair used to sign and verify a consensus message
39pub type BLSKeyPair = KeyPair;
40/// Public parameters for BLS signature scheme
41pub type BLSPublicParam = ();
42/// BLS signature type for consensus votes
43pub type BLSSignature = jf_signature::bls_over_bn254::Signature;
44
45impl PrivateSignatureKey for BLSPrivKey {
46    fn to_bytes(&self) -> Vec<u8> {
47        self.to_bytes()
48    }
49
50    fn from_bytes(bytes: &[u8]) -> anyhow::Result<Self> {
51        Ok(Self::from_bytes(bytes))
52    }
53
54    fn to_tagged_base64(&self) -> Result<tagged_base64::TaggedBase64, tagged_base64::Tb64Error> {
55        self.to_tagged_base64()
56    }
57}
58
59impl SignatureKey for BLSPubKey {
60    type PrivateKey = BLSPrivKey;
61    type StakeTableEntry = StakeTableEntry<VerKey>;
62    type QcParams<'a> = QcParams<
63        'a,
64        BLSPubKey,
65        <BLSOverBN254CurveSignatureScheme as SignatureScheme>::PublicParameter,
66    >;
67    type PureAssembledSignatureType =
68        <BLSOverBN254CurveSignatureScheme as SignatureScheme>::Signature;
69    type VerificationKeyType = Self;
70    type QcType = (Self::PureAssembledSignatureType, BitVec);
71    type SignError = SignatureError;
72
73    #[instrument(skip(self))]
74    fn validate(&self, signature: &Self::PureAssembledSignatureType, data: &[u8]) -> bool {
75        // This is the validation for QC partial signature before append().
76        BLSOverBN254CurveSignatureScheme::verify(&(), self, data, signature).is_ok()
77    }
78
79    fn sign(
80        sk: &Self::PrivateKey,
81        data: &[u8],
82    ) -> Result<Self::PureAssembledSignatureType, Self::SignError> {
83        BitVectorQc::<BLSOverBN254CurveSignatureScheme>::sign(
84            &(),
85            sk,
86            data,
87            &mut rand::thread_rng(),
88        )
89    }
90
91    fn from_private(private_key: &Self::PrivateKey) -> Self {
92        BLSPubKey::from(private_key)
93    }
94
95    fn to_bytes(&self) -> Vec<u8> {
96        let mut buf = vec![];
97        ark_serialize::CanonicalSerialize::serialize_compressed(self, &mut buf)
98            .expect("Serialization should not fail.");
99        buf
100    }
101
102    fn from_bytes(bytes: &[u8]) -> Result<Self, SerializationError> {
103        ark_serialize::CanonicalDeserialize::deserialize_compressed(bytes)
104    }
105
106    fn generated_from_seed_indexed(seed: [u8; 32], index: u64) -> (Self, Self::PrivateKey) {
107        let mut hasher = blake3::Hasher::new();
108        hasher.update(&seed);
109        hasher.update(&index.to_le_bytes());
110        let new_seed = *hasher.finalize().as_bytes();
111        let kp = KeyPair::generate(&mut ChaCha20Rng::from_seed(new_seed));
112        (kp.ver_key(), kp.sign_key_ref().clone())
113    }
114
115    fn stake_table_entry(&self, stake: U256) -> Self::StakeTableEntry {
116        StakeTableEntry {
117            stake_key: *self,
118            stake_amount: stake,
119        }
120    }
121
122    fn public_key(entry: &Self::StakeTableEntry) -> Self {
123        entry.stake_key
124    }
125
126    fn public_parameter(
127        stake_entries: &'_ [Self::StakeTableEntry],
128        threshold: U256,
129    ) -> Self::QcParams<'_> {
130        QcParams {
131            stake_entries,
132            threshold,
133            agg_sig_pp: (),
134        }
135    }
136
137    fn check(
138        real_qc_pp: &Self::QcParams<'_>,
139        data: &[u8],
140        qc: &Self::QcType,
141    ) -> Result<(), SignatureError> {
142        let msg = GenericArray::from_slice(data);
143        BitVectorQc::<BLSOverBN254CurveSignatureScheme>::check(real_qc_pp, msg, qc).map(|_| ())
144    }
145
146    fn signers(
147        real_qc_pp: &Self::QcParams<'_>,
148        qc: &Self::QcType,
149    ) -> Result<Vec<Self>, SignatureError> {
150        BitVectorQc::<BLSOverBN254CurveSignatureScheme>::signers(real_qc_pp, qc)
151    }
152
153    fn sig_proof(signature: &Self::QcType) -> (Self::PureAssembledSignatureType, BitVec) {
154        signature.clone()
155    }
156
157    fn assemble(
158        real_qc_pp: &Self::QcParams<'_>,
159        signers: &BitSlice,
160        sigs: &[Self::PureAssembledSignatureType],
161    ) -> Self::QcType {
162        BitVectorQc::<BLSOverBN254CurveSignatureScheme>::assemble(real_qc_pp, signers, sigs)
163            .expect("this assembling shouldn't fail")
164    }
165
166    fn genesis_proposer_pk() -> Self {
167        let kp = KeyPair::generate(&mut ChaCha20Rng::from_seed([0u8; 32]));
168        kp.ver_key()
169    }
170
171    fn to_verification_key(&self) -> Self::VerificationKeyType {
172        *self
173    }
174}
175
176// Currently implement builder signature key for BLS
177// So copy pasta here, but actually Sequencer will implement the same trait for ethereum types
178/// Builder signature key
179pub type BuilderKey = BLSPubKey;
180
181impl BuilderSignatureKey for BuilderKey {
182    type BuilderPrivateKey = BLSPrivKey;
183    type BuilderSignature = <BLSOverBN254CurveSignatureScheme as SignatureScheme>::Signature;
184    type SignError = SignatureError;
185
186    fn sign_builder_message(
187        private_key: &Self::BuilderPrivateKey,
188        data: &[u8],
189    ) -> Result<Self::BuilderSignature, Self::SignError> {
190        BitVectorQc::<BLSOverBN254CurveSignatureScheme>::sign(
191            &(),
192            private_key,
193            data,
194            &mut rand::thread_rng(),
195        )
196    }
197
198    fn validate_builder_signature(&self, signature: &Self::BuilderSignature, data: &[u8]) -> bool {
199        BLSOverBN254CurveSignatureScheme::verify(&(), self, data, signature).is_ok()
200    }
201
202    fn generated_from_seed_indexed(seed: [u8; 32], index: u64) -> (Self, Self::BuilderPrivateKey) {
203        let mut hasher = blake3::Hasher::new();
204        hasher.update(&seed);
205        hasher.update(&index.to_le_bytes());
206        let new_seed = *hasher.finalize().as_bytes();
207        let kp = KeyPair::generate(&mut ChaCha20Rng::from_seed(new_seed));
208        (kp.ver_key(), kp.sign_key_ref().clone())
209    }
210}
211
212pub type SchnorrPubKey = jf_signature::schnorr::VerKey<ark_ed_on_bn254::EdwardsConfig>;
213pub type SchnorrPrivKey = jf_signature::schnorr::SignKey<ark_ed_on_bn254::Fr>;
214pub type SchnorrSignatureScheme =
215    jf_signature::schnorr::SchnorrSignatureScheme<ark_ed_on_bn254::EdwardsConfig>;
216
217impl PrivateSignatureKey for SchnorrPrivKey {
218    fn to_bytes(&self) -> Vec<u8> {
219        self.to_bytes()
220    }
221
222    fn from_bytes(bytes: &[u8]) -> anyhow::Result<Self> {
223        Ok(Self::from_bytes(bytes))
224    }
225
226    fn to_tagged_base64(&self) -> Result<tagged_base64::TaggedBase64, tagged_base64::Tb64Error> {
227        self.to_tagged_base64()
228    }
229}
230
231impl StateSignatureKey for SchnorrPubKey {
232    type StatePrivateKey = SchnorrPrivKey;
233
234    type StateSignature = jf_signature::schnorr::Signature<ark_ed_on_bn254::EdwardsConfig>;
235
236    type SignError = SignatureError;
237
238    fn generated_from_seed_indexed(seed: [u8; 32], index: u64) -> (Self, Self::StatePrivateKey) {
239        let mut hasher = blake3::Hasher::new();
240        hasher.update(&seed);
241        hasher.update(&index.to_le_bytes());
242        let new_seed = *hasher.finalize().as_bytes();
243        let kp = jf_signature::schnorr::KeyPair::generate(&mut ChaCha20Rng::from_seed(new_seed));
244        (kp.ver_key(), kp.sign_key())
245    }
246}
247
248impl LCV1StateSignatureKey for SchnorrPubKey {
249    fn sign_state(
250        sk: &Self::StatePrivateKey,
251        light_client_state: &LightClientState,
252    ) -> Result<Self::StateSignature, Self::SignError> {
253        let state_msg: [_; 3] = light_client_state.into();
254        SchnorrSignatureScheme::sign(&(), sk, state_msg, &mut rand::thread_rng())
255    }
256
257    fn verify_state_sig(
258        &self,
259        signature: &Self::StateSignature,
260        light_client_state: &LightClientState,
261    ) -> bool {
262        let state_msg: [_; 3] = light_client_state.into();
263        SchnorrSignatureScheme::verify(&(), self, state_msg, signature).is_ok()
264    }
265}
266
267impl LCV2StateSignatureKey for SchnorrPubKey {
268    fn sign_state(
269        sk: &Self::StatePrivateKey,
270        light_client_state: &LightClientState,
271        next_stake_table_state: &StakeTableState,
272    ) -> Result<Self::StateSignature, Self::SignError> {
273        let mut msg = Vec::with_capacity(7);
274        let state_msg: [_; 3] = light_client_state.into();
275        msg.extend_from_slice(&state_msg);
276        let adv_st_state_msg: [_; 4] = (*next_stake_table_state).into();
277        msg.extend_from_slice(&adv_st_state_msg);
278        SchnorrSignatureScheme::sign(&(), sk, msg, &mut rand::thread_rng())
279    }
280
281    fn verify_state_sig(
282        &self,
283        signature: &Self::StateSignature,
284        light_client_state: &LightClientState,
285        next_stake_table_state: &StakeTableState,
286    ) -> bool {
287        let mut msg = Vec::with_capacity(7);
288        let state_msg: [_; 3] = light_client_state.into();
289        msg.extend_from_slice(&state_msg);
290        let adv_st_state_msg: [_; 4] = (*next_stake_table_state).into();
291        msg.extend_from_slice(&adv_st_state_msg);
292        SchnorrSignatureScheme::verify(&(), self, msg, signature).is_ok()
293    }
294}
295
296impl LCV3StateSignatureKey for SchnorrPubKey {
297    /// Sign the light client state
298    /// The input `msg` should be the keccak256 hash of ABI encodings of the light client state,
299    /// next stake table state, and the auth root.
300    fn sign_state(
301        private_key: &Self::StatePrivateKey,
302        msg: CircuitField,
303    ) -> Result<Self::StateSignature, Self::SignError> {
304        SchnorrSignatureScheme::sign(&(), private_key, [msg], &mut rand::thread_rng())
305    }
306
307    /// Verify the light client state signature
308    /// The input `msg` should be the keccak256 hash of ABI encodings of the light client state,
309    /// next stake table state, and the auth root.
310    fn verify_state_sig(&self, signature: &Self::StateSignature, msg: CircuitField) -> bool {
311        SchnorrSignatureScheme::verify(&(), self, [msg], signature).is_ok()
312    }
313}
314
315#[cfg(test)]
316mod tests {
317    use alloy::primitives::U256;
318    use bitvec::prelude::*;
319    use jf_signature::bls_over_bn254::{BLSOverBN254CurveSignatureScheme, KeyPair};
320
321    use super::BLSPubKey;
322    use crate::{
323        qc::BitVectorQc,
324        stake_table::StakeTableEntry,
325        traits::{qc::QuorumCertificateScheme, signature_key::SignatureKey},
326    };
327
328    #[test]
329    fn test_to_verification_key_is_identity() {
330        let mut rng = jf_utils::test_rng();
331        let kp = KeyPair::generate(&mut rng);
332        let pub_key: BLSPubKey = kp.ver_key();
333        // For BLSPubKey, VerificationKeyType = Self, so this should be a copy.
334        assert_eq!(pub_key.to_verification_key(), pub_key);
335    }
336
337    #[test]
338    fn test_bls_signers_correct_keys() {
339        let mut rng = jf_utils::test_rng();
340        let kp1 = KeyPair::generate(&mut rng);
341        let kp2 = KeyPair::generate(&mut rng);
342        let kp3 = KeyPair::generate(&mut rng);
343        let entries: Vec<StakeTableEntry<BLSPubKey>> = vec![
344            StakeTableEntry {
345                stake_key: kp1.ver_key(),
346                stake_amount: U256::from(1u8),
347            },
348            StakeTableEntry {
349                stake_key: kp2.ver_key(),
350                stake_amount: U256::from(1u8),
351            },
352            StakeTableEntry {
353                stake_key: kp3.ver_key(),
354                stake_amount: U256::from(1u8),
355            },
356        ];
357        // Use BLSPubKey::public_parameter to create QcParams (threshold = 2, 1 stake each).
358        let qc_pp = BLSPubKey::public_parameter(&entries, U256::from(2u8));
359        let msg = [55u8; 32];
360        let sig1 = BitVectorQc::<BLSOverBN254CurveSignatureScheme>::sign(
361            &(),
362            kp1.sign_key_ref(),
363            msg,
364            &mut rng,
365        )
366        .unwrap();
367        let sig2 = BitVectorQc::<BLSOverBN254CurveSignatureScheme>::sign(
368            &(),
369            kp2.sign_key_ref(),
370            msg,
371            &mut rng,
372        )
373        .unwrap();
374        // nodes 0 and 1 sign (bitvec [1, 1, 0])
375        let signers_bv = bitvec![1, 1, 0];
376        let qc = BitVectorQc::<BLSOverBN254CurveSignatureScheme>::assemble(
377            &qc_pp,
378            signers_bv.as_bitslice(),
379            &[sig1, sig2],
380        )
381        .unwrap();
382        let result = BLSPubKey::signers(&qc_pp, &qc).unwrap();
383        assert_eq!(result, vec![kp1.ver_key(), kp2.ver_key()]);
384    }
385
386    #[test]
387    fn test_bls_signers_bitvec_mismatch() {
388        let mut rng = jf_utils::test_rng();
389        let kp1 = KeyPair::generate(&mut rng);
390        let kp2 = KeyPair::generate(&mut rng);
391        let kp3 = KeyPair::generate(&mut rng);
392        let entries: Vec<StakeTableEntry<BLSPubKey>> = vec![
393            StakeTableEntry {
394                stake_key: kp1.ver_key(),
395                stake_amount: U256::from(1u8),
396            },
397            StakeTableEntry {
398                stake_key: kp2.ver_key(),
399                stake_amount: U256::from(1u8),
400            },
401            StakeTableEntry {
402                stake_key: kp3.ver_key(),
403                stake_amount: U256::from(1u8),
404            },
405        ];
406        let qc_pp = BLSPubKey::public_parameter(&entries, U256::from(2u8));
407
408        // Build a QC with a bitvec of length 2 (should be 3).
409        let wrong_bv = bitvec![1, 1];
410        let sig = BitVectorQc::<BLSOverBN254CurveSignatureScheme>::sign(
411            &(),
412            kp1.sign_key_ref(),
413            [0u8; 32],
414            &mut rng,
415        )
416        .unwrap();
417        let qc_bad = (sig, wrong_bv);
418        let result = BLSPubKey::signers(&qc_pp, &qc_bad);
419        assert!(result.is_err());
420    }
421}