//! Generators used by Monero in both its Pedersen commitments and Bulletproofs(+). //! An implementation of Monero's `ge_fromfe_frombytes_vartime`, simply called //! `hash_to_point` here, is included, as needed to generate generators. #![cfg_attr(not(feature = "std"), no_std)] use lazy_static::lazy_static; use sha3::{Digest, Keccak256}; use curve25519_dalek::edwards::{EdwardsPoint as DalekPoint, CompressedEdwardsY}; use curve25519_dalek::scalar::Scalar; use group::{Group, GroupEncoding}; use dalek_ff_group::EdwardsPoint; mod varint; use varint::write_varint; mod hash_to_point; pub use hash_to_point::hash_to_point; fn hash(data: &[u8]) -> [u8; 32] { Keccak256::digest(data).into() } lazy_static! { /// Monero alternate generator `H`, used for amounts in Pedersen commitments. pub static ref H: DalekPoint = CompressedEdwardsY(hash(&EdwardsPoint::generator().to_bytes())) .decompress() .unwrap() .mul_by_cofactor(); /// Monero's `H` generator multiplied 2^i for each index, i.e. H, 2H, 4H, 8H, ... /// used in old range proofs. /// https://github.com/monero-project/monero/blob/94e67bf96bbc010241f29ada6abc89f49a81759c/src/ /// ringct/rctTypes.h#L628 pub static ref H2: [DalekPoint; 64] = generate_H2(); } #[allow(non_snake_case)] fn generate_H2() -> [DalekPoint; 64] { let mut temp = Vec::with_capacity(64); for i in 0 .. 64 { temp.push(Scalar::from(2_u128.pow(i)) * *H) } temp.try_into().unwrap() } const MAX_M: usize = 16; const N: usize = 64; const MAX_MN: usize = MAX_M * N; /// Container struct for Bulletproofs(+) generators. #[allow(non_snake_case)] pub struct Generators { pub G: [EdwardsPoint; MAX_MN], pub H: [EdwardsPoint; MAX_MN], } /// Generate generators as needed for Bulletproofs(+), as Monero does. pub fn bulletproofs_generators(dst: &'static [u8]) -> Generators { let mut res = Generators { G: [EdwardsPoint::identity(); MAX_MN], H: [EdwardsPoint::identity(); MAX_MN] }; for i in 0 .. MAX_MN { let i = 2 * i; let mut even = H.compress().to_bytes().to_vec(); even.extend(dst); let mut odd = even.clone(); write_varint(&i.try_into().unwrap(), &mut even).unwrap(); write_varint(&(i + 1).try_into().unwrap(), &mut odd).unwrap(); res.H[i / 2] = EdwardsPoint(hash_to_point(hash(&even))); res.G[i / 2] = EdwardsPoint(hash_to_point(hash(&odd))); } res }