monero_generators/
lib.rs

1//! Generators used by Monero in both its Pedersen commitments and Bulletproofs(+).
2//! An implementation of Monero's `ge_fromfe_frombytes_vartime`, simply called
3//! `hash_to_point` here, is included, as needed to generate generators.
4
5use lazy_static::lazy_static;
6
7use sha3::{Digest, Keccak256};
8
9use curve25519_dalek::{
10  constants::ED25519_BASEPOINT_POINT,
11  edwards::{EdwardsPoint as DalekPoint, CompressedEdwardsY},
12};
13
14use group::Group;
15use dalek_ff_group::EdwardsPoint;
16
17mod varint;
18use varint::write_varint;
19
20mod hash_to_point;
21pub use hash_to_point::hash_to_point;
22
23fn hash(data: &[u8]) -> [u8; 32] {
24  Keccak256::digest(data).into()
25}
26
27lazy_static! {
28  /// Monero alternate generator `H`, used for amounts in Pedersen commitments.
29  pub static ref H: DalekPoint =
30    CompressedEdwardsY(hash(&ED25519_BASEPOINT_POINT.compress().to_bytes()))
31      .decompress()
32      .unwrap()
33      .mul_by_cofactor();
34}
35
36const MAX_M: usize = 16;
37const N: usize = 64;
38const MAX_MN: usize = MAX_M * N;
39
40/// Container struct for Bulletproofs(+) generators.
41#[allow(non_snake_case)]
42pub struct Generators {
43  pub G: [EdwardsPoint; MAX_MN],
44  pub H: [EdwardsPoint; MAX_MN],
45}
46
47/// Generate generators as needed for Bulletproofs(+), as Monero does.
48pub fn bulletproofs_generators(dst: &'static [u8]) -> Generators {
49  let mut res =
50    Generators { G: [EdwardsPoint::identity(); MAX_MN], H: [EdwardsPoint::identity(); MAX_MN] };
51  for i in 0 .. MAX_MN {
52    let i = 2 * i;
53
54    let mut even = H.compress().to_bytes().to_vec();
55    even.extend(dst);
56    let mut odd = even.clone();
57
58    write_varint(&i.try_into().unwrap(), &mut even).unwrap();
59    write_varint(&(i + 1).try_into().unwrap(), &mut odd).unwrap();
60    res.H[i / 2] = EdwardsPoint(hash_to_point(hash(&even)));
61    res.G[i / 2] = EdwardsPoint(hash_to_point(hash(&odd)));
62  }
63  res
64}