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)]
5
6use std_shims::{sync::LazyLock, vec::Vec};
7
8use sha3::{Digest, Keccak256};
9
10use curve25519_dalek::{constants::ED25519_BASEPOINT_POINT, edwards::EdwardsPoint};
11
12use monero_io::{write_varint, decompress_point};
13
14mod hash_to_point;
15pub use hash_to_point::hash_to_point;
16
17#[cfg(test)]
18mod tests;
19
20fn keccak256(data: &[u8]) -> [u8; 32] {
21 Keccak256::digest(data).into()
22}
23
24#[allow(non_snake_case)]
29pub static H: LazyLock<EdwardsPoint> = LazyLock::new(|| {
30 decompress_point(keccak256(&ED25519_BASEPOINT_POINT.compress().to_bytes()))
31 .expect("known on-curve point wasn't on-curve")
32 .mul_by_cofactor()
33});
34
35static H_POW_2_CELL: LazyLock<[EdwardsPoint; 64]> = LazyLock::new(|| {
36 let mut res = [*H; 64];
37 for i in 1 .. 64 {
38 res[i] = res[i - 1] + res[i - 1];
39 }
40 res
41});
42#[allow(non_snake_case)]
46pub fn H_pow_2() -> &'static [EdwardsPoint; 64] {
47 &H_POW_2_CELL
48}
49
50pub const MAX_COMMITMENTS: usize = 16;
52pub const COMMITMENT_BITS: usize = 64;
54
55#[allow(non_snake_case)]
57pub struct Generators {
58 pub G: Vec<EdwardsPoint>,
60 pub H: Vec<EdwardsPoint>,
62}
63
64pub fn bulletproofs_generators(dst: &'static [u8]) -> Generators {
69 const MAX_MN: usize = MAX_COMMITMENTS * COMMITMENT_BITS;
71
72 let mut preimage = H.compress().to_bytes().to_vec();
73 preimage.extend(dst);
74
75 let mut res = Generators { G: Vec::with_capacity(MAX_MN), H: Vec::with_capacity(MAX_MN) };
76 for i in 0 .. MAX_MN {
77 let i = 2 * i;
79
80 let mut even = preimage.clone();
81 write_varint(&i, &mut even).expect("write failed but <Vec as io::Write> doesn't fail");
82 res.H.push(hash_to_point(keccak256(&even)));
83
84 let mut odd = preimage.clone();
85 write_varint(&(i + 1), &mut odd).expect("write failed but <Vec as io::Write> doesn't fail");
86 res.G.push(hash_to_point(keccak256(&odd)));
87 }
88 res
89}