1use std::collections::HashMap;
10
11use alloy::primitives::U256;
12use ark_ed_on_bn254::EdwardsConfig as Config;
13use ark_ff::PrimeField;
14use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
15use jf_crhf::CRHF;
16use jf_rescue::{crhf::VariableLengthRescueCRHF, RescueError, RescueParameter};
17use jf_signature::schnorr;
18use jf_utils::to_bytes;
19use rand::SeedableRng;
20use rand_chacha::ChaCha20Rng;
21use serde::{Deserialize, Serialize};
22use tagged_base64::tagged;
23
24use crate::signature_key::BLSPubKey;
25
26pub const STAKE_TABLE_CAPACITY: usize = 200;
29pub type CircuitField = ark_ed_on_bn254::Fq;
31pub type LightClientState = GenericLightClientState<CircuitField>;
33pub type LightClientStateMsg = GenericLightClientStateMsg<CircuitField>;
35pub type StakeTableState = GenericStakeTableState<CircuitField>;
37pub type StateSignatureScheme =
39 jf_signature::schnorr::SchnorrSignatureScheme<ark_ed_on_bn254::EdwardsConfig>;
40pub type StateSignature = schnorr::Signature<Config>;
42pub type StateVerKey = schnorr::VerKey<Config>;
44pub type StateSignKey = schnorr::SignKey<ark_ed_on_bn254::Fr>;
46pub type PublicInput = GenericPublicInput<CircuitField>;
48#[derive(Debug, Default, Clone)]
50pub struct StateKeyPair(pub schnorr::KeyPair<Config>);
51
52#[derive(Clone, Debug, CanonicalSerialize, CanonicalDeserialize, Serialize, Deserialize)]
54pub struct StateSignatureRequestBody {
55 pub key: StateVerKey,
57 pub state: LightClientState,
59 pub next_stake: StakeTableState,
61 pub signature: StateSignature,
63}
64
65#[derive(Clone, Debug, Serialize, Deserialize)]
67pub struct StateSignaturesBundle {
68 pub state: LightClientState,
70 pub next_stake: StakeTableState,
72 pub signatures: HashMap<StateVerKey, StateSignature>,
74 pub accumulated_weight: U256,
76}
77
78#[tagged("LIGHT_CLIENT_STATE")]
80#[derive(
81 Clone,
82 Debug,
83 CanonicalSerialize,
84 CanonicalDeserialize,
85 Default,
86 Eq,
87 PartialEq,
88 PartialOrd,
89 Ord,
90 Hash,
91 Copy,
92)]
93pub struct GenericLightClientState<F: PrimeField> {
94 pub view_number: u64,
96 pub block_height: u64,
98 pub block_comm_root: F,
100}
101
102pub type GenericLightClientStateMsg<F> = [F; 3];
103
104impl<F: PrimeField> From<GenericLightClientState<F>> for GenericLightClientStateMsg<F> {
105 fn from(state: GenericLightClientState<F>) -> Self {
106 [
107 F::from(state.view_number),
108 F::from(state.block_height),
109 state.block_comm_root,
110 ]
111 }
112}
113
114impl<F: PrimeField> From<&GenericLightClientState<F>> for GenericLightClientStateMsg<F> {
115 fn from(state: &GenericLightClientState<F>) -> Self {
116 [
117 F::from(state.view_number),
118 F::from(state.block_height),
119 state.block_comm_root,
120 ]
121 }
122}
123
124impl<F: PrimeField + RescueParameter> GenericLightClientState<F> {
125 pub fn new(
126 view_number: u64,
127 block_height: u64,
128 block_comm_root: &[u8],
129 ) -> anyhow::Result<Self> {
130 Ok(Self {
131 view_number,
132 block_height,
133 block_comm_root: hash_bytes_to_field(block_comm_root)?,
134 })
135 }
136}
137
138#[tagged("STAKE_TABLE_STATE")]
140#[derive(
141 Clone,
142 Debug,
143 CanonicalSerialize,
144 CanonicalDeserialize,
145 Default,
146 Eq,
147 PartialEq,
148 PartialOrd,
149 Ord,
150 Hash,
151 Copy,
152)]
153pub struct GenericStakeTableState<F: PrimeField> {
154 pub bls_key_comm: F,
156 pub schnorr_key_comm: F,
158 pub amount_comm: F,
160 pub threshold: F,
162}
163
164impl<F: PrimeField> From<GenericStakeTableState<F>> for [F; 4] {
165 fn from(state: GenericStakeTableState<F>) -> Self {
166 [
167 state.bls_key_comm,
168 state.schnorr_key_comm,
169 state.amount_comm,
170 state.threshold,
171 ]
172 }
173}
174
175impl std::ops::Deref for StateKeyPair {
176 type Target = schnorr::KeyPair<Config>;
177
178 fn deref(&self) -> &Self::Target {
179 &self.0
180 }
181}
182
183impl StateKeyPair {
184 #[must_use]
186 pub fn from_sign_key(sk: StateSignKey) -> Self {
187 Self(schnorr::KeyPair::<Config>::from(sk))
188 }
189
190 #[must_use]
192 pub fn generate() -> StateKeyPair {
193 schnorr::KeyPair::generate(&mut rand::thread_rng()).into()
194 }
195
196 #[must_use]
198 pub fn generate_from_seed(seed: [u8; 32]) -> StateKeyPair {
199 schnorr::KeyPair::generate(&mut ChaCha20Rng::from_seed(seed)).into()
200 }
201
202 #[must_use]
204 pub fn generate_from_seed_indexed(seed: [u8; 32], index: u64) -> StateKeyPair {
205 let mut hasher = blake3::Hasher::new();
206 hasher.update(&seed);
207 hasher.update(&index.to_le_bytes());
208 let new_seed = *hasher.finalize().as_bytes();
209 Self::generate_from_seed(new_seed)
210 }
211}
212
213impl From<schnorr::KeyPair<Config>> for StateKeyPair {
214 fn from(value: schnorr::KeyPair<Config>) -> Self {
215 StateKeyPair(value)
216 }
217}
218
219#[derive(Clone, Debug)]
221pub struct GenericPublicInput<F: PrimeField> {
222 pub lc_state: GenericLightClientState<F>,
224 pub voting_st_state: GenericStakeTableState<F>,
226 pub next_st_state: GenericStakeTableState<F>,
228}
229
230impl<F: PrimeField> GenericPublicInput<F> {
231 pub fn new(
233 lc_state: GenericLightClientState<F>,
234 voting_st_state: GenericStakeTableState<F>,
235 next_st_state: GenericStakeTableState<F>,
236 ) -> Self {
237 Self {
238 lc_state,
239 voting_st_state,
240 next_st_state,
241 }
242 }
243
244 pub fn to_vec(&self) -> Vec<F> {
246 vec![
247 F::from(self.lc_state.view_number),
248 F::from(self.lc_state.block_height),
249 self.lc_state.block_comm_root,
250 self.voting_st_state.bls_key_comm,
251 self.voting_st_state.schnorr_key_comm,
252 self.voting_st_state.amount_comm,
253 self.voting_st_state.threshold,
254 self.next_st_state.bls_key_comm,
255 self.next_st_state.schnorr_key_comm,
256 self.next_st_state.amount_comm,
257 self.next_st_state.threshold,
258 ]
259 }
260}
261
262impl<F: PrimeField> From<GenericPublicInput<F>> for Vec<F> {
263 fn from(v: GenericPublicInput<F>) -> Self {
264 vec![
265 F::from(v.lc_state.view_number),
266 F::from(v.lc_state.block_height),
267 v.lc_state.block_comm_root,
268 v.voting_st_state.bls_key_comm,
269 v.voting_st_state.schnorr_key_comm,
270 v.voting_st_state.amount_comm,
271 v.voting_st_state.threshold,
272 v.next_st_state.bls_key_comm,
273 v.next_st_state.schnorr_key_comm,
274 v.next_st_state.amount_comm,
275 v.next_st_state.threshold,
276 ]
277 }
278}
279
280impl<F: PrimeField> From<Vec<F>> for GenericPublicInput<F> {
281 fn from(v: Vec<F>) -> Self {
282 let lc_state = GenericLightClientState {
283 view_number: v[0].into_bigint().as_ref()[0],
284 block_height: v[1].into_bigint().as_ref()[0],
285 block_comm_root: v[2],
286 };
287 let voting_st_state = GenericStakeTableState {
288 bls_key_comm: v[3],
289 schnorr_key_comm: v[4],
290 amount_comm: v[5],
291 threshold: v[6],
292 };
293 let next_st_state = GenericStakeTableState {
294 bls_key_comm: v[7],
295 schnorr_key_comm: v[8],
296 amount_comm: v[9],
297 threshold: v[10],
298 };
299 Self {
300 lc_state,
301 voting_st_state,
302 next_st_state,
303 }
304 }
305}
306
307pub fn hash_bytes_to_field<F: RescueParameter>(bytes: &[u8]) -> Result<F, RescueError> {
308 let bytes_len = (<F as PrimeField>::MODULUS_BIT_SIZE.div_ceil(8) - 1) as usize;
310 let elem = bytes
311 .chunks(bytes_len)
312 .map(F::from_le_bytes_mod_order)
313 .collect::<Vec<_>>();
314 Ok(VariableLengthRescueCRHF::<_, 1>::evaluate(elem)?[0])
315}
316
317pub trait ToFieldsLightClientCompat {
320 const SIZE: usize;
321 fn to_fields(&self) -> Vec<CircuitField>;
322}
323
324impl ToFieldsLightClientCompat for StateVerKey {
325 const SIZE: usize = 2;
326 fn to_fields(&self) -> Vec<CircuitField> {
328 let p = self.to_affine();
329 vec![p.x, p.y]
330 }
331}
332
333impl ToFieldsLightClientCompat for BLSPubKey {
334 const SIZE: usize = 3;
335 fn to_fields(&self) -> Vec<CircuitField> {
337 match to_bytes!(&self.to_affine()) {
338 Ok(bytes) => {
339 vec![
340 CircuitField::from_le_bytes_mod_order(&bytes[..31]),
341 CircuitField::from_le_bytes_mod_order(&bytes[31..62]),
342 CircuitField::from_le_bytes_mod_order(&bytes[62..]),
343 ]
344 },
345 Err(_) => unreachable!(),
346 }
347 }
348}