use super::*;
impl<E: Environment, const NUM_BITS: u8> Commit for Pedersen<E, NUM_BITS> {
type Input = Boolean<E>;
type Output = Field<E>;
type Randomizer = Scalar<E>;
fn commit(&self, input: &[Self::Input], randomizer: &Self::Randomizer) -> Self::Output {
self.commit_uncompressed(input, randomizer).to_x_coordinate()
}
}
impl<E: Environment, const NUM_BITS: u8>
Metrics<dyn Commit<Input = Boolean<E>, Output = Field<E>, Randomizer = Scalar<E>>> for Pedersen<E, NUM_BITS>
{
type Case = (Vec<Mode>, Vec<Mode>);
fn count(case: &Self::Case) -> Count {
let (input_modes, randomizer_modes) = case;
let uncompressed_count =
count!(Pedersen<E, NUM_BITS>, HashUncompressed<Input = Boolean<E>, Output = Group<E>>, input_modes);
let uncompressed_mode =
output_mode!(Pedersen<E, NUM_BITS>, HashUncompressed<Input = Boolean<E>, Output = Group<E>>, input_modes);
let group_initialize_count = randomizer_modes
.iter()
.map(|mode| {
count!(
Group<E>,
Ternary<Boolean = Boolean<E>, Output = Group<E>>,
&(*mode, Mode::Constant, Mode::Constant)
)
})
.fold(Count::zero(), |cumulative, count| cumulative + count);
let randomizer_to_bits_count =
match Mode::combine(randomizer_modes[0], randomizer_modes.iter().copied()).is_constant() {
true => Count::is(251, 0, 0, 0),
false => Count::is(0, 0, 501, 503),
};
let modes = randomizer_modes.iter().map(|mode| {
match mode.is_constant() {
true => Mode::Constant,
false => Mode::Private,
}
});
let (_, summation_count) =
modes.fold((uncompressed_mode, Count::zero()), |(prev_mode, cumulative), curr_mode| {
let mode = output_mode!(Group<E>, Add<Group<E>, Output = Group<E>>, &(prev_mode, curr_mode));
let sum_count = count!(Group<E>, Add<Group<E>, Output = Group<E>>, &(prev_mode, curr_mode));
(mode, cumulative + sum_count)
});
uncompressed_count + group_initialize_count + randomizer_to_bits_count + summation_count
}
}
impl<E: Environment, const NUM_BITS: u8>
OutputMode<dyn Commit<Input = Boolean<E>, Output = Field<E>, Randomizer = Scalar<E>>> for Pedersen<E, NUM_BITS>
{
type Case = (Vec<Mode>, Vec<Mode>);
fn output_mode(parameters: &Self::Case) -> Mode {
let (input_modes, randomizer_modes) = parameters;
match input_modes.iter().all(|m| *m == Mode::Constant) && randomizer_modes.iter().all(|m| *m == Mode::Constant)
{
true => Mode::Constant,
false => Mode::Private,
}
}
}
#[cfg(all(test, feature = "console"))]
mod tests {
use super::*;
use snarkvm_circuit_types::environment::Circuit;
use snarkvm_utilities::{TestRng, Uniform};
const ITERATIONS: u64 = 10;
const MESSAGE: &str = "PedersenCircuit0";
const NUM_BITS_MULTIPLIER: u8 = 8;
fn check_commit<const NUM_BITS: u8>(mode: Mode, rng: &mut TestRng) {
use console::Commit as C;
let native = console::Pedersen::<<Circuit as Environment>::Network, NUM_BITS>::setup(MESSAGE);
let circuit = Pedersen::<Circuit, NUM_BITS>::constant(native.clone());
for i in 0..ITERATIONS {
let input = (0..NUM_BITS).map(|_| bool::rand(rng)).collect::<Vec<bool>>();
let randomizer = Uniform::rand(rng);
let expected = native.commit(&input, &randomizer).expect("Failed to commit native input");
let circuit_input: Vec<Boolean<_>> = Inject::new(mode, input);
let circuit_randomizer: Scalar<_> = Inject::new(mode, randomizer);
Circuit::scope(format!("Pedersen {mode} {i}"), || {
let candidate = circuit.commit(&circuit_input, &circuit_randomizer);
assert_eq!(expected, candidate.eject_value());
let input_modes = circuit_input.iter().map(|b| b.eject_mode()).collect::<Vec<_>>();
let randomizer_modes =
circuit_randomizer.to_bits_le().iter().map(|b| b.eject_mode()).collect::<Vec<_>>();
assert_count!(
Pedersen<Circuit, NUM_BITS>,
Commit<Input = Boolean<Circuit>, Output = Field<Circuit>, Randomizer = Scalar<Circuit>>,
&(input_modes.clone(), randomizer_modes.clone())
);
assert_output_mode!(
Pedersen<Circuit, NUM_BITS>,
Commit<Input = Boolean<Circuit>, Output = Field<Circuit>, Randomizer = Scalar<Circuit>>,
&(input_modes, randomizer_modes),
candidate
);
});
}
}
fn check_homomorphic_addition<
C: Display + Eject + Add<Output = C> + ToBits<Boolean = Boolean<Circuit>>,
P: Commit<Input = Boolean<Circuit>, Randomizer = Scalar<Circuit>, Output = Field<Circuit>>
+ CommitUncompressed<Input = Boolean<Circuit>, Randomizer = Scalar<Circuit>, Output = Group<Circuit>>,
>(
pedersen: &P,
first: C,
second: C,
rng: &mut TestRng,
) {
println!("Checking homomorphic addition on {first} + {second}");
let first_randomizer: Scalar<_> = Inject::new(Mode::Private, Uniform::rand(rng));
let second_randomizer: Scalar<_> = Inject::new(Mode::Private, Uniform::rand(rng));
let a = pedersen.commit_uncompressed(&first.to_bits_le(), &first_randomizer);
let b = pedersen.commit_uncompressed(&second.to_bits_le(), &second_randomizer);
let expected = (a + b).to_x_coordinate();
let combined_randomizer = first_randomizer + second_randomizer;
let candidate = pedersen.commit(&(first + second).to_bits_le(), &combined_randomizer);
assert_eq!(expected.eject(), candidate.eject());
assert!(Circuit::is_satisfied());
}
#[test]
fn test_commit_constant() {
let mut rng = TestRng::default();
check_commit::<NUM_BITS_MULTIPLIER>(Mode::Constant, &mut rng);
check_commit::<{ 2 * NUM_BITS_MULTIPLIER }>(Mode::Constant, &mut rng);
check_commit::<{ 3 * NUM_BITS_MULTIPLIER }>(Mode::Constant, &mut rng);
check_commit::<{ 4 * NUM_BITS_MULTIPLIER }>(Mode::Constant, &mut rng);
check_commit::<{ 5 * NUM_BITS_MULTIPLIER }>(Mode::Constant, &mut rng);
}
#[test]
fn test_commit_public() {
let mut rng = TestRng::default();
check_commit::<NUM_BITS_MULTIPLIER>(Mode::Public, &mut rng);
check_commit::<{ 2 * NUM_BITS_MULTIPLIER }>(Mode::Public, &mut rng);
check_commit::<{ 3 * NUM_BITS_MULTIPLIER }>(Mode::Public, &mut rng);
check_commit::<{ 4 * NUM_BITS_MULTIPLIER }>(Mode::Public, &mut rng);
check_commit::<{ 5 * NUM_BITS_MULTIPLIER }>(Mode::Public, &mut rng);
}
#[test]
fn test_commit_private() {
let mut rng = TestRng::default();
check_commit::<NUM_BITS_MULTIPLIER>(Mode::Private, &mut rng);
check_commit::<{ 2 * NUM_BITS_MULTIPLIER }>(Mode::Private, &mut rng);
check_commit::<{ 3 * NUM_BITS_MULTIPLIER }>(Mode::Private, &mut rng);
check_commit::<{ 4 * NUM_BITS_MULTIPLIER }>(Mode::Private, &mut rng);
check_commit::<{ 5 * NUM_BITS_MULTIPLIER }>(Mode::Private, &mut rng);
}
#[test]
fn test_pedersen64_homomorphism_private() {
let pedersen = Pedersen64::constant(console::Pedersen64::setup("Pedersen64HomomorphismTest"));
let mut rng = TestRng::default();
for _ in 0..ITERATIONS {
let first = U8::<Circuit>::new(Mode::Private, console::U8::new(u8::rand(&mut rng) >> 1));
let second = U8::new(Mode::Private, console::U8::new(u8::rand(&mut rng) >> 1));
check_homomorphic_addition(&pedersen, first, second, &mut rng);
let first = U16::<Circuit>::new(Mode::Private, console::U16::new(u16::rand(&mut rng) >> 1));
let second = U16::new(Mode::Private, console::U16::new(u16::rand(&mut rng) >> 1));
check_homomorphic_addition(&pedersen, first, second, &mut rng);
let first = U32::<Circuit>::new(Mode::Private, console::U32::new(u32::rand(&mut rng) >> 1));
let second = U32::new(Mode::Private, console::U32::new(u32::rand(&mut rng) >> 1));
check_homomorphic_addition(&pedersen, first, second, &mut rng);
let first = U64::<Circuit>::new(Mode::Private, console::U64::new(u64::rand(&mut rng) >> 1));
let second = U64::new(Mode::Private, console::U64::new(u64::rand(&mut rng) >> 1));
check_homomorphic_addition(&pedersen, first, second, &mut rng);
}
}
#[test]
fn test_pedersen_homomorphism_private() {
fn check_pedersen_homomorphism<
P: Commit<Input = Boolean<Circuit>, Randomizer = Scalar<Circuit>, Output = Field<Circuit>>
+ CommitUncompressed<Input = Boolean<Circuit>, Randomizer = Scalar<Circuit>, Output = Group<Circuit>>,
>(
pedersen: &P,
) {
let mut rng = TestRng::default();
for _ in 0..ITERATIONS {
let first = U8::<Circuit>::new(Mode::Private, console::U8::new(u8::rand(&mut rng) >> 1));
let second = U8::new(Mode::Private, console::U8::new(u8::rand(&mut rng) >> 1));
check_homomorphic_addition(pedersen, first, second, &mut rng);
let first = U16::<Circuit>::new(Mode::Private, console::U16::new(u16::rand(&mut rng) >> 1));
let second = U16::new(Mode::Private, console::U16::new(u16::rand(&mut rng) >> 1));
check_homomorphic_addition(pedersen, first, second, &mut rng);
let first = U32::<Circuit>::new(Mode::Private, console::U32::new(u32::rand(&mut rng) >> 1));
let second = U32::new(Mode::Private, console::U32::new(u32::rand(&mut rng) >> 1));
check_homomorphic_addition(pedersen, first, second, &mut rng);
let first = U64::<Circuit>::new(Mode::Private, console::U64::new(u64::rand(&mut rng) >> 1));
let second = U64::new(Mode::Private, console::U64::new(u64::rand(&mut rng) >> 1));
check_homomorphic_addition(pedersen, first, second, &mut rng);
let first = U128::<Circuit>::new(Mode::Private, console::U128::new(u128::rand(&mut rng) >> 1));
let second = U128::new(Mode::Private, console::U128::new(u128::rand(&mut rng) >> 1));
check_homomorphic_addition(pedersen, first, second, &mut rng);
}
}
let pedersen128 = Pedersen128::constant(console::Pedersen128::setup("Pedersen128HomomorphismTest"));
check_pedersen_homomorphism(&pedersen128);
}
}