penumbra_sdk_proof_setup/single/
group.rs

1//! This module serves as a small abstraction layer over the group operations we need.
2//!
3//! This simplifies logic in other parts of this crate, since we don't need to rely on
4//! arkworks directly.
5use ark_ec::pairing::{Pairing, PairingOutput};
6use ark_ec::scalar_mul::{variable_base::VariableBaseMSM, ScalarMul};
7use ark_serialize::CanonicalSerialize;
8use decaf377::Bls12_377;
9use rand_core::CryptoRngCore;
10
11/// The group used for the left side of pairings.
12pub type G1 = <Bls12_377 as Pairing>::G1;
13
14/// A prepared version of G1 for more efficient pairings.
15pub type G1Prepared = <Bls12_377 as Pairing>::G1Prepared;
16
17/// The group used for the right side of pairings.
18pub type G2 = <Bls12_377 as Pairing>::G2;
19
20/// A prepared version of G2 for more efficient pairings.
21pub type G2Prepared = <Bls12_377 as Pairing>::G2Prepared;
22
23/// The group used for the output of pairings.
24pub type GT = PairingOutput<Bls12_377>;
25
26/// The field of scalars over which these groups form modules.
27pub type F = <Bls12_377 as Pairing>::ScalarField;
28
29/// The pairing operation between the two groups.
30pub fn pairing(a: impl Into<G1Prepared>, b: impl Into<G2Prepared>) -> GT {
31    <Bls12_377 as Pairing>::pairing(a, b)
32}
33
34// # Batched Pairing Checks
35
36/// Sample a random field that's "small" but still big enough for pairing checks.
37fn rand_small_f<R: CryptoRngCore>(rng: &mut R) -> F {
38    // 128 bits of security
39    let mut bytes = [0u8; 16];
40    rng.fill_bytes(&mut bytes);
41    F::from_le_bytes_mod_order(&bytes)
42}
43
44// This is just a little trick to avoid code duplication in the variants of this pairing checker
45// for different base groups.
46macro_rules! make_batched_pairing_checker {
47    ($name:ident, $gl:ident, $gr:ident, $glp:ident, $grp:ident, $pairing:ident) => {
48        /// A tool for efficiently making many pairing checks.
49        ///
50        /// This version is for pairing checks where the varying parts
51        /// of each side of the pairing equality are in $gl and $gr, respectively.
52        pub struct $name {
53            // Invariant: both vecs have the same length.
54            vary_l: Vec<$gl>,
55            base_l: $glp,
56            vary_r: Vec<$gr>,
57            base_r: $grp,
58        }
59
60        impl $name {
61            pub fn new(base_l: impl Into<$glp>, base_r: impl Into<$grp>) -> Self {
62                Self {
63                    vary_l: Vec::new(),
64                    base_l: base_l.into(),
65                    vary_r: Vec::new(),
66                    base_r: base_r.into(),
67                }
68            }
69
70            pub fn add(&mut self, l: $gl, r: $gr) {
71                self.vary_l.push(l);
72                self.vary_r.push(r);
73            }
74
75            #[must_use]
76            pub fn check<R: CryptoRngCore>(self, rng: &mut R) -> bool {
77                let n = self.vary_l.len();
78                let scalars = (0..n).map(|_| rand_small_f(rng)).collect::<Vec<_>>();
79
80                let ready_to_msm_l = <$gl as ScalarMul>::batch_convert_to_mul_base(&self.vary_l);
81                let l = <$gl as VariableBaseMSM>::msm_unchecked(&ready_to_msm_l, &scalars);
82                let ready_to_msm_r = <$gr as ScalarMul>::batch_convert_to_mul_base(&self.vary_r);
83                let r = <$gr as VariableBaseMSM>::msm_unchecked(&ready_to_msm_r, &scalars);
84
85                pairing(l, self.base_l) == $pairing(self.base_r, r)
86            }
87        }
88    };
89}
90
91// This is just a gimmick to support our macro shenanigans
92fn swapped_pairing(a: impl Into<G2Prepared>, b: impl Into<G1Prepared>) -> GT {
93    pairing(b, a)
94}
95
96make_batched_pairing_checker!(
97    BatchedPairingChecker11,
98    G1,
99    G1,
100    G2Prepared,
101    G2Prepared,
102    swapped_pairing
103);
104make_batched_pairing_checker!(
105    BatchedPairingChecker12,
106    G1,
107    G2,
108    G2Prepared,
109    G1Prepared,
110    pairing
111);
112
113/// The size of the hash we use.
114pub(crate) const HASH_SIZE: usize = 32;
115
116/// The hash output we use when we need bytes.
117pub(crate) type Hash = [u8; 32];
118
119/// A utility struct for hashing group elements and producing fields.
120///
121/// This avoids having to deal with some serialization and reduction code from arkworks.
122///
123/// All methods of this struct will handle separation between elements correctly.
124/// This means that feeding in two elements is distinct from feeding in the "concatenation"
125/// of this elements. One place where you still need manual effort on the user's end
126/// is when you're hashing a variable number of elements.
127#[derive(Clone)]
128pub(crate) struct GroupHasher {
129    state: blake2b_simd::State,
130}
131
132impl GroupHasher {
133    /// Create a new hasher with a personalization string.
134    ///
135    /// Because of BLAKE2's limitations, this has to be 16 bytes at most.
136    /// This function will panic if that isn't the case.
137    pub fn new(personalization: &'static [u8]) -> Self {
138        let state = blake2b_simd::Params::new()
139            .personal(personalization)
140            .to_state();
141        Self { state }
142    }
143
144    // Separate methods because the semantics of what this is trying to do are different,
145    // even if eating a usize happens to do the right thing.
146    fn write_len(&mut self, len: usize) {
147        self.eat_usize(len);
148    }
149
150    /// Consume some bytes, adding it to the state of the hash.
151    ///
152    /// These bytes will be length prefixed, and so calling this function
153    /// multiple times is not the same as calling it with the concatenation
154    /// of those bytes.
155    pub fn eat_bytes(&mut self, x: &[u8]) {
156        self.write_len(x.len());
157        self.state.update(x);
158    }
159
160    /// Eat anything that's canonically serializable (yummy!).
161    ///
162    /// This will handle padding between elements, using the declared length.
163    ///
164    /// We keep this internal, to make a simpler public API, since we only have
165    /// a handful of types we actually need to use this for.
166    fn eat_canonical<T: CanonicalSerialize>(&mut self, x: &T) {
167        self.write_len(x.compressed_size());
168        x.serialize_compressed(&mut self.state)
169            .expect("failed to serialize element");
170    }
171
172    /// Consume a usize value, adding it into the state of this hash.
173    ///
174    /// This is useful for (i.e. intended for) encoding metadata.
175    pub fn eat_usize(&mut self, x: usize) {
176        // On basically any platform this should fit in a u64
177        self.state.update(&(x as u64).to_le_bytes());
178    }
179
180    /// Consume a G1 group element, adding it into the state of this hash.
181    pub fn eat_g1(&mut self, x: &G1) {
182        self.eat_canonical(x);
183    }
184
185    /// Consume a G2 group element, adding it into the state of this hash.
186    pub fn eat_g2(&mut self, x: &G2) {
187        self.eat_canonical(x);
188    }
189
190    /// Consume a scalar, adding it into the state of this hash.
191    pub fn eat_f(&mut self, x: &F) {
192        self.eat_canonical(x);
193    }
194
195    /// Finalize this hash function, producing a scalar.
196    pub fn finalize(self) -> F {
197        F::from_le_bytes_mod_order(self.state.finalize().as_bytes())
198    }
199
200    /// Finalize this hash function, producing bytes.
201    pub fn finalize_bytes(self) -> Hash {
202        let mut out = [0u8; HASH_SIZE];
203        out.copy_from_slice(&self.state.finalize().as_bytes()[..HASH_SIZE]);
204        out
205    }
206}