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 digest::generic_array::GenericArray;
13use jf_signature::{
14    bls_over_bn254::{BLSOverBN254CurveSignatureScheme, KeyPair, SignKey, VerKey},
15    SignatureError, SignatureScheme,
16};
17use rand::SeedableRng;
18use rand_chacha::ChaCha20Rng;
19use tracing::instrument;
20
21use crate::{
22    light_client::{LightClientState, StakeTableState},
23    qc::{BitVectorQc, QcParams},
24    stake_table::StakeTableEntry,
25    traits::{
26        qc::QuorumCertificateScheme,
27        signature_key::{
28            BuilderSignatureKey, PrivateSignatureKey, SignatureKey, StateSignatureKey,
29        },
30    },
31};
32
33/// BLS private key used to sign a consensus message
34pub type BLSPrivKey = SignKey;
35/// BLS public key used to verify a consensus signature
36pub type BLSPubKey = VerKey;
37/// BLS key pair used to sign and verify a consensus message
38pub type BLSKeyPair = KeyPair;
39/// Public parameters for BLS signature scheme
40pub type BLSPublicParam = ();
41
42impl PrivateSignatureKey for BLSPrivKey {
43    fn to_bytes(&self) -> Vec<u8> {
44        self.to_bytes()
45    }
46
47    fn from_bytes(bytes: &[u8]) -> anyhow::Result<Self> {
48        Ok(Self::from_bytes(bytes))
49    }
50
51    fn to_tagged_base64(&self) -> Result<tagged_base64::TaggedBase64, tagged_base64::Tb64Error> {
52        self.to_tagged_base64()
53    }
54}
55
56impl SignatureKey for BLSPubKey {
57    type PrivateKey = BLSPrivKey;
58    type StakeTableEntry = StakeTableEntry<VerKey>;
59    type QcParams<'a> = QcParams<
60        'a,
61        BLSPubKey,
62        <BLSOverBN254CurveSignatureScheme as SignatureScheme>::PublicParameter,
63    >;
64    type PureAssembledSignatureType =
65        <BLSOverBN254CurveSignatureScheme as SignatureScheme>::Signature;
66    type QcType = (Self::PureAssembledSignatureType, BitVec);
67    type SignError = SignatureError;
68
69    #[instrument(skip(self))]
70    fn validate(&self, signature: &Self::PureAssembledSignatureType, data: &[u8]) -> bool {
71        // This is the validation for QC partial signature before append().
72        BLSOverBN254CurveSignatureScheme::verify(&(), self, data, signature).is_ok()
73    }
74
75    fn sign(
76        sk: &Self::PrivateKey,
77        data: &[u8],
78    ) -> Result<Self::PureAssembledSignatureType, Self::SignError> {
79        BitVectorQc::<BLSOverBN254CurveSignatureScheme>::sign(
80            &(),
81            sk,
82            data,
83            &mut rand::thread_rng(),
84        )
85    }
86
87    fn from_private(private_key: &Self::PrivateKey) -> Self {
88        BLSPubKey::from(private_key)
89    }
90
91    fn to_bytes(&self) -> Vec<u8> {
92        let mut buf = vec![];
93        ark_serialize::CanonicalSerialize::serialize_compressed(self, &mut buf)
94            .expect("Serialization should not fail.");
95        buf
96    }
97
98    fn from_bytes(bytes: &[u8]) -> Result<Self, SerializationError> {
99        ark_serialize::CanonicalDeserialize::deserialize_compressed(bytes)
100    }
101
102    fn generated_from_seed_indexed(seed: [u8; 32], index: u64) -> (Self, Self::PrivateKey) {
103        let mut hasher = blake3::Hasher::new();
104        hasher.update(&seed);
105        hasher.update(&index.to_le_bytes());
106        let new_seed = *hasher.finalize().as_bytes();
107        let kp = KeyPair::generate(&mut ChaCha20Rng::from_seed(new_seed));
108        (kp.ver_key(), kp.sign_key_ref().clone())
109    }
110
111    fn stake_table_entry(&self, stake: U256) -> Self::StakeTableEntry {
112        StakeTableEntry {
113            stake_key: *self,
114            stake_amount: stake,
115        }
116    }
117
118    fn public_key(entry: &Self::StakeTableEntry) -> Self {
119        entry.stake_key
120    }
121
122    fn public_parameter(
123        stake_entries: &'_ [Self::StakeTableEntry],
124        threshold: U256,
125    ) -> Self::QcParams<'_> {
126        QcParams {
127            stake_entries,
128            threshold,
129            agg_sig_pp: (),
130        }
131    }
132
133    fn check(
134        real_qc_pp: &Self::QcParams<'_>,
135        data: &[u8],
136        qc: &Self::QcType,
137    ) -> Result<(), SignatureError> {
138        let msg = GenericArray::from_slice(data);
139        BitVectorQc::<BLSOverBN254CurveSignatureScheme>::check(real_qc_pp, msg, qc).map(|_| ())
140    }
141
142    fn sig_proof(signature: &Self::QcType) -> (Self::PureAssembledSignatureType, BitVec) {
143        signature.clone()
144    }
145
146    fn assemble(
147        real_qc_pp: &Self::QcParams<'_>,
148        signers: &BitSlice,
149        sigs: &[Self::PureAssembledSignatureType],
150    ) -> Self::QcType {
151        BitVectorQc::<BLSOverBN254CurveSignatureScheme>::assemble(real_qc_pp, signers, sigs)
152            .expect("this assembling shouldn't fail")
153    }
154
155    fn genesis_proposer_pk() -> Self {
156        let kp = KeyPair::generate(&mut ChaCha20Rng::from_seed([0u8; 32]));
157        kp.ver_key()
158    }
159}
160
161// Currently implement builder signature key for BLS
162// So copy pasta here, but actually Sequencer will implement the same trait for ethereum types
163/// Builder signature key
164pub type BuilderKey = BLSPubKey;
165
166impl BuilderSignatureKey for BuilderKey {
167    type BuilderPrivateKey = BLSPrivKey;
168    type BuilderSignature = <BLSOverBN254CurveSignatureScheme as SignatureScheme>::Signature;
169    type SignError = SignatureError;
170
171    fn sign_builder_message(
172        private_key: &Self::BuilderPrivateKey,
173        data: &[u8],
174    ) -> Result<Self::BuilderSignature, Self::SignError> {
175        BitVectorQc::<BLSOverBN254CurveSignatureScheme>::sign(
176            &(),
177            private_key,
178            data,
179            &mut rand::thread_rng(),
180        )
181    }
182
183    fn validate_builder_signature(&self, signature: &Self::BuilderSignature, data: &[u8]) -> bool {
184        BLSOverBN254CurveSignatureScheme::verify(&(), self, data, signature).is_ok()
185    }
186
187    fn generated_from_seed_indexed(seed: [u8; 32], index: u64) -> (Self, Self::BuilderPrivateKey) {
188        let mut hasher = blake3::Hasher::new();
189        hasher.update(&seed);
190        hasher.update(&index.to_le_bytes());
191        let new_seed = *hasher.finalize().as_bytes();
192        let kp = KeyPair::generate(&mut ChaCha20Rng::from_seed(new_seed));
193        (kp.ver_key(), kp.sign_key_ref().clone())
194    }
195}
196
197pub type SchnorrPubKey = jf_signature::schnorr::VerKey<ark_ed_on_bn254::EdwardsConfig>;
198pub type SchnorrPrivKey = jf_signature::schnorr::SignKey<ark_ed_on_bn254::Fr>;
199pub type SchnorrSignatureScheme =
200    jf_signature::schnorr::SchnorrSignatureScheme<ark_ed_on_bn254::EdwardsConfig>;
201
202impl PrivateSignatureKey for SchnorrPrivKey {
203    fn to_bytes(&self) -> Vec<u8> {
204        self.to_bytes()
205    }
206
207    fn from_bytes(bytes: &[u8]) -> anyhow::Result<Self> {
208        Ok(Self::from_bytes(bytes))
209    }
210
211    fn to_tagged_base64(&self) -> Result<tagged_base64::TaggedBase64, tagged_base64::Tb64Error> {
212        self.to_tagged_base64()
213    }
214}
215
216impl StateSignatureKey for SchnorrPubKey {
217    type StatePrivateKey = SchnorrPrivKey;
218
219    type StateSignature = jf_signature::schnorr::Signature<ark_ed_on_bn254::EdwardsConfig>;
220
221    type SignError = SignatureError;
222
223    fn sign_state(
224        sk: &Self::StatePrivateKey,
225        light_client_state: &LightClientState,
226        next_stake_table_state: &StakeTableState,
227    ) -> Result<Self::StateSignature, Self::SignError> {
228        let mut msg = Vec::with_capacity(7);
229        let state_msg: [_; 3] = light_client_state.into();
230        msg.extend_from_slice(&state_msg);
231        let adv_st_state_msg: [_; 4] = (*next_stake_table_state).into();
232        msg.extend_from_slice(&adv_st_state_msg);
233        SchnorrSignatureScheme::sign(&(), sk, msg, &mut rand::thread_rng())
234    }
235
236    fn verify_state_sig(
237        &self,
238        signature: &Self::StateSignature,
239        light_client_state: &LightClientState,
240        next_stake_table_state: &StakeTableState,
241    ) -> bool {
242        let mut msg = Vec::with_capacity(7);
243        let state_msg: [_; 3] = light_client_state.into();
244        msg.extend_from_slice(&state_msg);
245        let adv_st_state_msg: [_; 4] = (*next_stake_table_state).into();
246        msg.extend_from_slice(&adv_st_state_msg);
247        SchnorrSignatureScheme::verify(&(), self, msg, signature).is_ok()
248    }
249
250    fn generated_from_seed_indexed(seed: [u8; 32], index: u64) -> (Self, Self::StatePrivateKey) {
251        let mut hasher = blake3::Hasher::new();
252        hasher.update(&seed);
253        hasher.update(&index.to_le_bytes());
254        let new_seed = *hasher.finalize().as_bytes();
255        let kp = jf_signature::schnorr::KeyPair::generate(&mut ChaCha20Rng::from_seed(new_seed));
256        (kp.ver_key(), kp.sign_key())
257    }
258}