#![allow(non_snake_case)]
mod ripemd160_chip;
mod types;
mod utils;
use midnight_proofs::{circuit::Layouter, plonk::Error};
use ripemd::Digest;
pub use ripemd160_chip::{
RipeMD160Chip, RipeMD160Config, NB_RIPEMD160_ADVICE_COLS, NB_RIPEMD160_FIXED_COLS,
};
use crate::{
instructions::{hash::HashCPU, DecompositionInstructions, HashInstructions},
types::AssignedByte,
CircuitField,
};
impl<F: CircuitField> HashCPU<u8, [u8; 20]> for RipeMD160Chip<F> {
fn hash(inputs: &[u8]) -> [u8; 20] {
let output = ripemd::Ripemd160::digest(inputs);
output.into_iter().collect::<Vec<_>>().try_into().unwrap()
}
}
impl<F: CircuitField> HashInstructions<F, AssignedByte<F>, [AssignedByte<F>; 20]>
for RipeMD160Chip<F>
{
fn hash(
&self,
layouter: &mut impl Layouter<F>,
inputs: &[AssignedByte<F>],
) -> Result<[AssignedByte<F>; 20], Error> {
let mut output_bytes = Vec::with_capacity(20);
for word in self.ripemd160(layouter, inputs)? {
let bytes = self.native_gadget.assigned_to_le_bytes(layouter, &word.0, Some(4))?;
output_bytes.extend(bytes)
}
Ok(output_bytes.try_into().unwrap())
}
}
#[cfg(test)]
mod tests {
use midnight_curves::Fq as Scalar;
use crate::{
field::NativeGadget, hash::ripemd160::RipeMD160Chip, instructions::hash::tests::test_hash,
types::AssignedByte,
};
#[test]
fn test_ripemd160_hash() {
fn test_wrapper(input_size: usize, cost_model: bool) {
test_hash::<
Scalar,
AssignedByte<Scalar>,
[AssignedByte<Scalar>; 20],
RipeMD160Chip<Scalar>,
NativeGadget<Scalar, _, _>,
>(cost_model, "RIPEMD160", input_size)
}
const RIPEMD160_BLOCK_SIZE: usize = 64;
const RIPEMD160_EDGE_PADDING: usize = 55;
test_wrapper(2 * RIPEMD160_BLOCK_SIZE, true);
test_wrapper(RIPEMD160_BLOCK_SIZE, false);
test_wrapper(RIPEMD160_BLOCK_SIZE - 1, false);
test_wrapper(RIPEMD160_BLOCK_SIZE - 2, false);
test_wrapper(4 * RIPEMD160_BLOCK_SIZE, false);
test_wrapper(RIPEMD160_EDGE_PADDING, false);
test_wrapper(RIPEMD160_EDGE_PADDING - 1, false);
test_wrapper(0, false);
test_wrapper(1, false);
test_wrapper(2, false);
}
}