1#![cfg_attr(docsrs, feature(doc_auto_cfg))]
2#![doc = include_str!("../README.md")]
3#![deny(missing_docs)]
4#![cfg_attr(not(feature = "std"), no_std)]
56use std_shims::{sync::LazyLock, vec::Vec};
78use sha3::{Digest, Keccak256};
910use curve25519_dalek::{constants::ED25519_BASEPOINT_POINT, edwards::EdwardsPoint};
11use curve25519_dalek::edwards::CompressedEdwardsY;
12use monero_io::{write_varint, decompress_point};
1314mod hash_to_point;
15pub use hash_to_point::hash_to_point;
1617#[cfg(test)]
18mod tests;
1920fn keccak256(data: &[u8]) -> [u8; 32] {
21 Keccak256::digest(data).into()
22}
2324/// Monero's `H` generator.
25///
26/// Contrary to convention (`G` for values, `H` for randomness), `H` is used by Monero for amounts
27/// within Pedersen commitments.
28#[allow(non_snake_case)]
29pub static H: LazyLock<EdwardsPoint> = LazyLock::new(|| {
30 decompress_point(CompressedEdwardsY(keccak256(&ED25519_BASEPOINT_POINT.compress().to_bytes())))
31 .unwrap()
32 .mul_by_cofactor()
33});
3435static H_POW_2_CELL: LazyLock<[EdwardsPoint; 64]> = LazyLock::new(|| {
36let mut res = [*H; 64];
37for i in 1 .. 64 {
38 res[i] = res[i - 1] + res[i - 1];
39 }
40 res
41});
42/// Monero's `H` generator, multiplied by 2**i for i in 1 ..= 64.
43///
44/// This table is useful when working with amounts, which are u64s.
45#[allow(non_snake_case)]
46pub fn H_pow_2() -> &'static [EdwardsPoint; 64] {
47&H_POW_2_CELL
48}
4950/// The maximum amount of commitments provable for within a single range proof.
51pub const MAX_COMMITMENTS: usize = 16;
52/// The amount of bits a value within a commitment may use.
53pub const COMMITMENT_BITS: usize = 64;
54/// The logarithm (over 2) of the amount of bits a value within a commitment may use.
55pub const LOG_COMMITMENT_BITS: usize = 6; // 2 ** 6 == N
5657/// Container struct for Bulletproofs(+) generators.
58#[allow(non_snake_case)]
59pub struct Generators {
60/// The G (bold) vector of generators.
61pub G: Vec<EdwardsPoint>,
62/// The H (bold) vector of generators.
63pub H: Vec<EdwardsPoint>,
64}
6566/// Generate generators as needed for Bulletproofs(+), as Monero does.
67///
68/// Consumers should not call this function ad-hoc, yet call it within a build script or use a
69/// once-initialized static.
70pub fn bulletproofs_generators(dst: &'static [u8]) -> Generators {
71// The maximum amount of bits used within a single range proof.
72const MAX_MN: usize = MAX_COMMITMENTS * COMMITMENT_BITS;
7374let mut preimage = H.compress().to_bytes().to_vec();
75 preimage.extend(dst);
7677let mut res = Generators { G: Vec::with_capacity(MAX_MN), H: Vec::with_capacity(MAX_MN) };
78for i in 0 .. MAX_MN {
79// We generate a pair of generators per iteration
80let i = 2 * i;
8182let mut even = preimage.clone();
83 write_varint(&i, &mut even).unwrap();
84 res.H.push(hash_to_point(keccak256(&even)));
8586let mut odd = preimage.clone();
87 write_varint(&(i + 1), &mut odd).unwrap();
88 res.G.push(hash_to_point(keccak256(&odd)));
89 }
90 res
91}