2022-01-21 17:56:27 -08:00
|
|
|
//! Pedersen commitment implementation using the Ristretto prime-order group.
|
|
|
|
|
2022-05-18 18:17:29 -07:00
|
|
|
#[cfg(not(target_os = "solana"))]
|
2022-01-21 17:56:27 -08:00
|
|
|
use rand::rngs::OsRng;
|
2021-09-29 21:45:35 -07:00
|
|
|
use {
|
2022-01-21 17:56:27 -08:00
|
|
|
core::ops::{Add, Mul, Sub},
|
2021-09-29 21:45:35 -07:00
|
|
|
curve25519_dalek::{
|
|
|
|
constants::{RISTRETTO_BASEPOINT_COMPRESSED, RISTRETTO_BASEPOINT_POINT},
|
|
|
|
ristretto::{CompressedRistretto, RistrettoPoint},
|
|
|
|
scalar::Scalar,
|
|
|
|
traits::MultiscalarMul,
|
|
|
|
},
|
|
|
|
serde::{Deserialize, Serialize},
|
|
|
|
sha3::Sha3_512,
|
|
|
|
std::convert::TryInto,
|
|
|
|
subtle::{Choice, ConstantTimeEq},
|
|
|
|
zeroize::Zeroize,
|
|
|
|
};
|
|
|
|
|
2022-01-21 17:56:27 -08:00
|
|
|
lazy_static::lazy_static! {
|
|
|
|
/// Pedersen base point for encoding messages to be committed.
|
|
|
|
pub static ref G: RistrettoPoint = RISTRETTO_BASEPOINT_POINT;
|
|
|
|
/// Pedersen base point for encoding the commitment openings.
|
|
|
|
pub static ref H: RistrettoPoint =
|
|
|
|
RistrettoPoint::hash_from_bytes::<Sha3_512>(RISTRETTO_BASEPOINT_COMPRESSED.as_bytes());
|
2021-09-29 21:45:35 -07:00
|
|
|
}
|
|
|
|
|
2022-01-21 17:56:27 -08:00
|
|
|
/// Algorithm handle for the Pedersen commitment scheme.
|
2021-09-29 21:45:35 -07:00
|
|
|
pub struct Pedersen;
|
|
|
|
impl Pedersen {
|
2022-11-08 02:03:24 -08:00
|
|
|
/// On input a message (numeric amount), the function returns a Pedersen commitment of the
|
|
|
|
/// message and the corresponding opening.
|
2022-01-21 17:56:27 -08:00
|
|
|
///
|
|
|
|
/// This function is randomized. It internally samples a Pedersen opening using `OsRng`.
|
2022-05-18 18:17:29 -07:00
|
|
|
#[cfg(not(target_os = "solana"))]
|
2021-10-05 06:02:52 -07:00
|
|
|
#[allow(clippy::new_ret_no_self)]
|
2022-11-08 02:03:24 -08:00
|
|
|
pub fn new<T: Into<Scalar>>(amount: T) -> (PedersenCommitment, PedersenOpening) {
|
2022-01-21 17:56:27 -08:00
|
|
|
let opening = PedersenOpening::new_rand();
|
2022-11-08 02:03:24 -08:00
|
|
|
let commitment = Pedersen::with(amount, &opening);
|
2021-09-29 21:45:35 -07:00
|
|
|
|
2022-01-21 17:56:27 -08:00
|
|
|
(commitment, opening)
|
2021-09-29 21:45:35 -07:00
|
|
|
}
|
|
|
|
|
2022-11-08 02:03:24 -08:00
|
|
|
/// On input a message (numeric amount) and a Pedersen opening, the function returns the
|
|
|
|
/// corresponding Pedersen commitment.
|
2022-01-21 17:56:27 -08:00
|
|
|
///
|
|
|
|
/// This function is deterministic.
|
2021-09-29 21:45:35 -07:00
|
|
|
#[allow(non_snake_case)]
|
2021-10-05 06:02:52 -07:00
|
|
|
pub fn with<T: Into<Scalar>>(amount: T, open: &PedersenOpening) -> PedersenCommitment {
|
2021-09-29 21:45:35 -07:00
|
|
|
let x: Scalar = amount.into();
|
|
|
|
let r = open.get_scalar();
|
|
|
|
|
2022-01-21 17:56:27 -08:00
|
|
|
PedersenCommitment(RistrettoPoint::multiscalar_mul(&[x, *r], &[*G, *H]))
|
2021-09-29 21:45:35 -07:00
|
|
|
}
|
2022-02-01 11:11:28 -08:00
|
|
|
|
2022-11-08 02:03:24 -08:00
|
|
|
/// On input a message (numeric amount), the function returns a Pedersen commitment with zero
|
|
|
|
/// as the opening.
|
2022-02-01 11:11:28 -08:00
|
|
|
///
|
|
|
|
/// This function is deterministic.
|
|
|
|
pub fn encode<T: Into<Scalar>>(amount: T) -> PedersenCommitment {
|
|
|
|
PedersenCommitment(amount.into() * &(*G))
|
|
|
|
}
|
2021-09-29 21:45:35 -07:00
|
|
|
}
|
|
|
|
|
2022-01-21 17:56:27 -08:00
|
|
|
/// Pedersen opening type.
|
|
|
|
///
|
|
|
|
/// Instances of Pedersen openings are zeroized on drop.
|
|
|
|
#[derive(Clone, Debug, Default, Serialize, Deserialize, Zeroize)]
|
2021-09-29 21:45:35 -07:00
|
|
|
#[zeroize(drop)]
|
2021-10-05 06:02:52 -07:00
|
|
|
pub struct PedersenOpening(pub(crate) Scalar);
|
|
|
|
impl PedersenOpening {
|
2022-01-21 17:56:27 -08:00
|
|
|
pub fn get_scalar(&self) -> &Scalar {
|
|
|
|
&self.0
|
2021-09-29 21:45:35 -07:00
|
|
|
}
|
|
|
|
|
2022-05-18 18:17:29 -07:00
|
|
|
#[cfg(not(target_os = "solana"))]
|
2022-01-21 17:56:27 -08:00
|
|
|
pub fn new_rand() -> Self {
|
|
|
|
PedersenOpening(Scalar::random(&mut OsRng))
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn as_bytes(&self) -> &[u8; 32] {
|
|
|
|
self.0.as_bytes()
|
2021-09-29 21:45:35 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn to_bytes(&self) -> [u8; 32] {
|
|
|
|
self.0.to_bytes()
|
|
|
|
}
|
|
|
|
|
2021-10-05 06:02:52 -07:00
|
|
|
pub fn from_bytes(bytes: &[u8]) -> Option<PedersenOpening> {
|
2021-09-29 21:45:35 -07:00
|
|
|
match bytes.try_into() {
|
2021-10-05 06:02:52 -07:00
|
|
|
Ok(bytes) => Scalar::from_canonical_bytes(bytes).map(PedersenOpening),
|
2021-09-29 21:45:35 -07:00
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-10-05 06:02:52 -07:00
|
|
|
impl Eq for PedersenOpening {}
|
|
|
|
impl PartialEq for PedersenOpening {
|
2021-09-29 21:45:35 -07:00
|
|
|
fn eq(&self, other: &Self) -> bool {
|
|
|
|
self.ct_eq(other).unwrap_u8() == 1u8
|
|
|
|
}
|
|
|
|
}
|
2021-10-05 06:02:52 -07:00
|
|
|
impl ConstantTimeEq for PedersenOpening {
|
2021-09-29 21:45:35 -07:00
|
|
|
fn ct_eq(&self, other: &Self) -> Choice {
|
|
|
|
self.0.ct_eq(&other.0)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-05 06:02:52 -07:00
|
|
|
impl<'a, 'b> Add<&'b PedersenOpening> for &'a PedersenOpening {
|
|
|
|
type Output = PedersenOpening;
|
2021-09-29 21:45:35 -07:00
|
|
|
|
2022-04-09 06:19:29 -07:00
|
|
|
fn add(self, opening: &'b PedersenOpening) -> PedersenOpening {
|
|
|
|
PedersenOpening(&self.0 + &opening.0)
|
2021-09-29 21:45:35 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
define_add_variants!(
|
2021-10-05 06:02:52 -07:00
|
|
|
LHS = PedersenOpening,
|
|
|
|
RHS = PedersenOpening,
|
|
|
|
Output = PedersenOpening
|
2021-09-29 21:45:35 -07:00
|
|
|
);
|
|
|
|
|
2021-10-05 06:02:52 -07:00
|
|
|
impl<'a, 'b> Sub<&'b PedersenOpening> for &'a PedersenOpening {
|
|
|
|
type Output = PedersenOpening;
|
2021-09-29 21:45:35 -07:00
|
|
|
|
2022-04-09 06:19:29 -07:00
|
|
|
fn sub(self, opening: &'b PedersenOpening) -> PedersenOpening {
|
|
|
|
PedersenOpening(&self.0 - &opening.0)
|
2021-09-29 21:45:35 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
define_sub_variants!(
|
2021-10-05 06:02:52 -07:00
|
|
|
LHS = PedersenOpening,
|
|
|
|
RHS = PedersenOpening,
|
|
|
|
Output = PedersenOpening
|
2021-09-29 21:45:35 -07:00
|
|
|
);
|
|
|
|
|
2021-10-05 06:02:52 -07:00
|
|
|
impl<'a, 'b> Mul<&'b Scalar> for &'a PedersenOpening {
|
|
|
|
type Output = PedersenOpening;
|
2021-09-29 21:45:35 -07:00
|
|
|
|
2022-04-09 06:19:29 -07:00
|
|
|
fn mul(self, scalar: &'b Scalar) -> PedersenOpening {
|
|
|
|
PedersenOpening(&self.0 * scalar)
|
2021-09-29 21:45:35 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-05 06:02:52 -07:00
|
|
|
define_mul_variants!(
|
|
|
|
LHS = PedersenOpening,
|
|
|
|
RHS = Scalar,
|
|
|
|
Output = PedersenOpening
|
|
|
|
);
|
2021-09-29 21:45:35 -07:00
|
|
|
|
2022-04-09 06:19:29 -07:00
|
|
|
impl<'a, 'b> Mul<&'b PedersenOpening> for &'a Scalar {
|
|
|
|
type Output = PedersenOpening;
|
|
|
|
|
|
|
|
fn mul(self, opening: &'b PedersenOpening) -> PedersenOpening {
|
|
|
|
PedersenOpening(self * &opening.0)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
define_mul_variants!(
|
|
|
|
LHS = Scalar,
|
|
|
|
RHS = PedersenOpening,
|
|
|
|
Output = PedersenOpening
|
|
|
|
);
|
|
|
|
|
2022-01-21 17:56:27 -08:00
|
|
|
/// Pedersen commitment type.
|
|
|
|
#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
|
2021-10-05 06:02:52 -07:00
|
|
|
pub struct PedersenCommitment(pub(crate) RistrettoPoint);
|
|
|
|
impl PedersenCommitment {
|
2022-01-21 17:56:27 -08:00
|
|
|
pub fn get_point(&self) -> &RistrettoPoint {
|
|
|
|
&self.0
|
2021-09-29 21:45:35 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn to_bytes(&self) -> [u8; 32] {
|
|
|
|
self.0.compress().to_bytes()
|
|
|
|
}
|
|
|
|
|
2021-10-05 06:02:52 -07:00
|
|
|
pub fn from_bytes(bytes: &[u8]) -> Option<PedersenCommitment> {
|
2022-08-24 22:22:52 -07:00
|
|
|
if bytes.len() != 32 {
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
|
2021-10-05 06:02:52 -07:00
|
|
|
Some(PedersenCommitment(
|
2021-09-29 21:45:35 -07:00
|
|
|
CompressedRistretto::from_slice(bytes).decompress()?,
|
|
|
|
))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-05 06:02:52 -07:00
|
|
|
impl<'a, 'b> Add<&'b PedersenCommitment> for &'a PedersenCommitment {
|
|
|
|
type Output = PedersenCommitment;
|
2021-09-29 21:45:35 -07:00
|
|
|
|
2022-04-09 06:19:29 -07:00
|
|
|
fn add(self, commitment: &'b PedersenCommitment) -> PedersenCommitment {
|
|
|
|
PedersenCommitment(&self.0 + &commitment.0)
|
2021-09-29 21:45:35 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
define_add_variants!(
|
2021-10-05 06:02:52 -07:00
|
|
|
LHS = PedersenCommitment,
|
|
|
|
RHS = PedersenCommitment,
|
|
|
|
Output = PedersenCommitment
|
2021-09-29 21:45:35 -07:00
|
|
|
);
|
|
|
|
|
2021-10-05 06:02:52 -07:00
|
|
|
impl<'a, 'b> Sub<&'b PedersenCommitment> for &'a PedersenCommitment {
|
|
|
|
type Output = PedersenCommitment;
|
2021-09-29 21:45:35 -07:00
|
|
|
|
2022-04-09 06:19:29 -07:00
|
|
|
fn sub(self, commitment: &'b PedersenCommitment) -> PedersenCommitment {
|
|
|
|
PedersenCommitment(&self.0 - &commitment.0)
|
2021-09-29 21:45:35 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
define_sub_variants!(
|
2021-10-05 06:02:52 -07:00
|
|
|
LHS = PedersenCommitment,
|
|
|
|
RHS = PedersenCommitment,
|
|
|
|
Output = PedersenCommitment
|
2021-09-29 21:45:35 -07:00
|
|
|
);
|
|
|
|
|
2021-10-05 06:02:52 -07:00
|
|
|
impl<'a, 'b> Mul<&'b Scalar> for &'a PedersenCommitment {
|
|
|
|
type Output = PedersenCommitment;
|
2021-09-29 21:45:35 -07:00
|
|
|
|
2022-04-09 06:19:29 -07:00
|
|
|
fn mul(self, scalar: &'b Scalar) -> PedersenCommitment {
|
|
|
|
PedersenCommitment(scalar * &self.0)
|
2021-09-29 21:45:35 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-05 06:02:52 -07:00
|
|
|
define_mul_variants!(
|
|
|
|
LHS = PedersenCommitment,
|
|
|
|
RHS = Scalar,
|
|
|
|
Output = PedersenCommitment
|
|
|
|
);
|
2021-09-29 21:45:35 -07:00
|
|
|
|
2022-04-09 06:19:29 -07:00
|
|
|
impl<'a, 'b> Mul<&'b PedersenCommitment> for &'a Scalar {
|
|
|
|
type Output = PedersenCommitment;
|
|
|
|
|
|
|
|
fn mul(self, commitment: &'b PedersenCommitment) -> PedersenCommitment {
|
|
|
|
PedersenCommitment(self * &commitment.0)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
define_mul_variants!(
|
|
|
|
LHS = Scalar,
|
|
|
|
RHS = PedersenCommitment,
|
|
|
|
Output = PedersenCommitment
|
|
|
|
);
|
|
|
|
|
2021-09-29 21:45:35 -07:00
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
#[test]
|
2022-01-21 17:56:27 -08:00
|
|
|
fn test_pedersen_homomorphic_addition() {
|
2021-09-29 21:45:35 -07:00
|
|
|
let amt_0: u64 = 77;
|
|
|
|
let amt_1: u64 = 57;
|
|
|
|
|
|
|
|
let rng = &mut OsRng;
|
2021-10-05 06:02:52 -07:00
|
|
|
let open_0 = PedersenOpening(Scalar::random(rng));
|
|
|
|
let open_1 = PedersenOpening(Scalar::random(rng));
|
2021-09-29 21:45:35 -07:00
|
|
|
|
2021-10-05 06:02:52 -07:00
|
|
|
let comm_0 = Pedersen::with(amt_0, &open_0);
|
|
|
|
let comm_1 = Pedersen::with(amt_1, &open_1);
|
|
|
|
let comm_addition = Pedersen::with(amt_0 + amt_1, &(open_0 + open_1));
|
2021-09-29 21:45:35 -07:00
|
|
|
|
|
|
|
assert_eq!(comm_addition, comm_0 + comm_1);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2022-01-21 17:56:27 -08:00
|
|
|
fn test_pedersen_homomorphic_subtraction() {
|
2021-09-29 21:45:35 -07:00
|
|
|
let amt_0: u64 = 77;
|
|
|
|
let amt_1: u64 = 57;
|
|
|
|
|
|
|
|
let rng = &mut OsRng;
|
2021-10-05 06:02:52 -07:00
|
|
|
let open_0 = PedersenOpening(Scalar::random(rng));
|
|
|
|
let open_1 = PedersenOpening(Scalar::random(rng));
|
2021-09-29 21:45:35 -07:00
|
|
|
|
2021-10-05 06:02:52 -07:00
|
|
|
let comm_0 = Pedersen::with(amt_0, &open_0);
|
|
|
|
let comm_1 = Pedersen::with(amt_1, &open_1);
|
|
|
|
let comm_addition = Pedersen::with(amt_0 - amt_1, &(open_0 - open_1));
|
2021-09-29 21:45:35 -07:00
|
|
|
|
|
|
|
assert_eq!(comm_addition, comm_0 - comm_1);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2022-01-21 17:56:27 -08:00
|
|
|
fn test_pedersen_homomorphic_multiplication() {
|
2021-09-29 21:45:35 -07:00
|
|
|
let amt_0: u64 = 77;
|
|
|
|
let amt_1: u64 = 57;
|
|
|
|
|
2021-10-05 06:02:52 -07:00
|
|
|
let (comm, open) = Pedersen::new(amt_0);
|
2021-09-29 21:45:35 -07:00
|
|
|
let scalar = Scalar::from(amt_1);
|
2021-10-05 06:02:52 -07:00
|
|
|
let comm_addition = Pedersen::with(amt_0 * amt_1, &(open * scalar));
|
2021-09-29 21:45:35 -07:00
|
|
|
|
|
|
|
assert_eq!(comm_addition, comm * scalar);
|
2022-04-09 06:19:29 -07:00
|
|
|
assert_eq!(comm_addition, scalar * comm);
|
2021-09-29 21:45:35 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2022-01-21 17:56:27 -08:00
|
|
|
fn test_pedersen_commitment_bytes() {
|
2021-09-29 21:45:35 -07:00
|
|
|
let amt: u64 = 77;
|
2021-10-05 06:02:52 -07:00
|
|
|
let (comm, _) = Pedersen::new(amt);
|
2021-09-29 21:45:35 -07:00
|
|
|
|
|
|
|
let encoded = comm.to_bytes();
|
2021-10-05 06:02:52 -07:00
|
|
|
let decoded = PedersenCommitment::from_bytes(&encoded).unwrap();
|
2021-09-29 21:45:35 -07:00
|
|
|
|
|
|
|
assert_eq!(comm, decoded);
|
2022-11-08 02:03:24 -08:00
|
|
|
|
|
|
|
// incorrect length encoding
|
|
|
|
assert_eq!(PedersenCommitment::from_bytes(&[0; 33]), None);
|
2021-09-29 21:45:35 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2022-01-21 17:56:27 -08:00
|
|
|
fn test_pedersen_opening_bytes() {
|
2021-10-05 06:02:52 -07:00
|
|
|
let open = PedersenOpening(Scalar::random(&mut OsRng));
|
2021-09-29 21:45:35 -07:00
|
|
|
|
|
|
|
let encoded = open.to_bytes();
|
2021-10-05 06:02:52 -07:00
|
|
|
let decoded = PedersenOpening::from_bytes(&encoded).unwrap();
|
2021-09-29 21:45:35 -07:00
|
|
|
|
|
|
|
assert_eq!(open, decoded);
|
2022-11-08 02:03:24 -08:00
|
|
|
|
|
|
|
// incorrect length encoding
|
|
|
|
assert_eq!(PedersenOpening::from_bytes(&[0; 33]), None);
|
2021-09-29 21:45:35 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2022-01-21 17:56:27 -08:00
|
|
|
fn test_serde_pedersen_commitment() {
|
2021-09-29 21:45:35 -07:00
|
|
|
let amt: u64 = 77;
|
2021-10-05 06:02:52 -07:00
|
|
|
let (comm, _) = Pedersen::new(amt);
|
2021-09-29 21:45:35 -07:00
|
|
|
|
|
|
|
let encoded = bincode::serialize(&comm).unwrap();
|
2021-10-05 06:02:52 -07:00
|
|
|
let decoded: PedersenCommitment = bincode::deserialize(&encoded).unwrap();
|
2021-09-29 21:45:35 -07:00
|
|
|
|
|
|
|
assert_eq!(comm, decoded);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2022-01-21 17:56:27 -08:00
|
|
|
fn test_serde_pedersen_opening() {
|
2021-10-05 06:02:52 -07:00
|
|
|
let open = PedersenOpening(Scalar::random(&mut OsRng));
|
2021-09-29 21:45:35 -07:00
|
|
|
|
|
|
|
let encoded = bincode::serialize(&open).unwrap();
|
2021-10-05 06:02:52 -07:00
|
|
|
let decoded: PedersenOpening = bincode::deserialize(&encoded).unwrap();
|
2021-09-29 21:45:35 -07:00
|
|
|
|
|
|
|
assert_eq!(open, decoded);
|
|
|
|
}
|
|
|
|
}
|