use bulletproofs::{BulletproofGens, PedersenGens, RangeProof};
use curve25519_dalek_ng::{
ristretto::{CompressedRistretto, RistrettoPoint},
scalar::Scalar,
};
use derive_more::{Add, From, Sub};
use merlin::Transcript;
use once_cell::sync::OnceCell;
use crate::error::FastCryptoError::{GeneralOpaqueError, InvalidInput};
use crate::serde_helpers::ToFromByteArray;
use crate::{
error::FastCryptoError, serialize_deserialize_with_to_from_byte_array, traits::ToFromBytes,
};
use serde::de;
use serde::Deserialize;
const PEDERSEN_COMMITMENT_LENGTH: usize = 32;
#[derive(Debug, Clone, From, Add, Sub, PartialEq, Eq)]
pub struct PedersenCommitment {
point: RistrettoPoint,
}
impl PedersenCommitment {
pub fn new(
value: [u8; PEDERSEN_COMMITMENT_LENGTH],
blinding_factor: [u8; PEDERSEN_COMMITMENT_LENGTH],
) -> Self {
let generators = PedersenGens::default();
let value = Scalar::from_bits(value);
let blinding = Scalar::from_bits(blinding_factor);
generators.commit(value, blinding).into()
}
}
impl ToFromByteArray<PEDERSEN_COMMITMENT_LENGTH> for PedersenCommitment {
fn from_byte_array(bytes: &[u8; PEDERSEN_COMMITMENT_LENGTH]) -> Result<Self, FastCryptoError> {
let point = CompressedRistretto::from_slice(bytes);
point.decompress().ok_or(InvalidInput).map(Self::from)
}
fn to_byte_array(&self) -> [u8; PEDERSEN_COMMITMENT_LENGTH] {
self.point.compress().to_bytes()
}
}
serialize_deserialize_with_to_from_byte_array!(PedersenCommitment);
#[derive(Debug)]
pub struct BulletproofsRangeProof {
proof: RangeProof,
bytes: OnceCell<Vec<u8>>,
}
impl From<RangeProof> for BulletproofsRangeProof {
fn from(proof: RangeProof) -> Self {
Self {
proof,
bytes: OnceCell::new(),
}
}
}
impl BulletproofsRangeProof {
pub fn prove_bit_length(
value: u64,
blinding: [u8; 32],
bits: usize,
domain: &'static [u8],
) -> Result<(PedersenCommitment, Self), FastCryptoError> {
if !(bits == 8 || bits == 16 || bits == 32 || bits == 64) {
return Err(FastCryptoError::InvalidInput);
}
let pc_gens = PedersenGens::default();
let bp_gens = BulletproofGens::new(bits, 1);
let mut prover_transcript = Transcript::new(domain);
let blinding = Scalar::from_bits(blinding);
let (proof, commitment) = RangeProof::prove_single(
&bp_gens,
&pc_gens,
&mut prover_transcript,
value,
&blinding,
bits,
)
.map_err(|_| GeneralOpaqueError)?;
Ok((
commitment.decompress().ok_or(GeneralOpaqueError)?.into(),
proof.into(),
))
}
pub fn verify_bit_length(
&self,
commitment: &PedersenCommitment,
bits: usize,
domain: &'static [u8],
) -> Result<(), FastCryptoError> {
if !(bits == 8 || bits == 16 || bits == 32 || bits == 64) {
return Err(FastCryptoError::InvalidInput);
}
let pc_gens = PedersenGens::default();
let bp_gens = BulletproofGens::new(bits, 1);
let mut verifier_transcript = Transcript::new(domain);
self.proof
.verify_single(
&bp_gens,
&pc_gens,
&mut verifier_transcript,
&CompressedRistretto::from_slice(&commitment.to_byte_array()),
bits,
)
.map_err(|_| FastCryptoError::GeneralError("Failed to verify proof".to_string()))
}
}
impl AsRef<[u8]> for BulletproofsRangeProof {
fn as_ref(&self) -> &[u8] {
self.bytes.get_or_init(|| self.proof.to_bytes())
}
}
impl ToFromBytes for BulletproofsRangeProof {
fn from_bytes(bytes: &[u8]) -> Result<Self, FastCryptoError> {
RangeProof::from_bytes(bytes)
.map_err(|_| InvalidInput)
.map(Self::from)
}
}