threshold_bls/hash/
try_and_increment.rs

1use super::{hasher::Hasher, HashToCurve};
2use crate::curve::BLSError;
3use ark_ec::models::{
4    short_weierstrass_jacobian::{GroupAffine, GroupProjective},
5    SWModelParameters,
6};
7use ark_ff::{Field, PrimeField, Zero};
8use ethers_core::utils::hex;
9use log::debug;
10use std::marker::PhantomData;
11
12const NUM_TRIES: u8 = 255;
13
14/// A try-and-increment method for hashing to G1 and G2.
15#[derive(Clone)]
16pub struct TryAndIncrement<'a, H, P> {
17    hasher: &'a H,
18    curve_params: PhantomData<P>,
19}
20
21impl<'a, H, P> TryAndIncrement<'a, H, P>
22where
23    H: Hasher<Error = BLSError>,
24    P: SWModelParameters,
25{
26    /// Instantiates a new Try-and-increment hasher with the provided hashing method
27    /// and curve parameters based on the type
28    pub fn new(h: &'a H) -> Self {
29        TryAndIncrement {
30            hasher: h,
31            curve_params: PhantomData,
32        }
33    }
34}
35
36impl<'a, H, P> HashToCurve for TryAndIncrement<'a, H, P>
37where
38    H: Hasher<Error = BLSError>,
39    P: SWModelParameters,
40{
41    type Output = GroupProjective<P>;
42
43    fn hash(&self, domain: &[u8], message: &[u8]) -> Result<Self::Output, BLSError> {
44        self.hash_with_attempt(domain, message).map(|res| res.0)
45    }
46}
47
48impl<'a, H, P> TryAndIncrement<'a, H, P>
49where
50    H: Hasher<Error = BLSError>,
51    P: SWModelParameters,
52{
53    pub fn hash_with_attempt(
54        &self,
55        domain: &[u8],
56        message: &[u8],
57    ) -> Result<(GroupProjective<P>, usize), BLSError> {
58        let mut candidate_hash = self.hasher.hash(domain, message)?;
59
60        for c in 0..NUM_TRIES {
61            let xfield = if P::BaseField::extension_degree() == 1 {
62                // TODO BN254 G1 curve, currently we have the same simple implementation in contract
63                let f = <P::BaseField as Field>::BasePrimeField::from_be_bytes_mod_order(
64                    &candidate_hash,
65                );
66                P::BaseField::from_base_prime_field_elems(&[f])
67            } else {
68                P::BaseField::from_random_bytes(&candidate_hash)
69            };
70
71            if let Some(x) = xfield {
72                if let Some(p) = GroupAffine::get_point_from_x(x, false) {
73                    debug!(
74                        "succeeded hashing \"{}\" to curve in {} tries",
75                        hex::encode(message),
76                        c + 1
77                    );
78
79                    let scaled = p.scale_by_cofactor();
80                    if scaled.is_zero() {
81                        continue;
82                    }
83                    return Ok((scaled, (c + 1) as usize));
84                }
85            }
86
87            candidate_hash = self.hasher.hash(domain, &candidate_hash)?;
88        }
89
90        Err(BLSError::HashToCurveError)
91    }
92}