diff --git a/src/circuit/gadget/sinsemilla/chip.rs b/src/circuit/gadget/sinsemilla/chip.rs index 183e84803..b8dddbebf 100644 --- a/src/circuit/gadget/sinsemilla/chip.rs +++ b/src/circuit/gadget/sinsemilla/chip.rs @@ -210,7 +210,35 @@ impl SinsemillaInstructions for SinsemillaChip { message: Vec>, num_words: usize, ) -> Result, Error> { - todo!() + // Message must have at most SinsemillaC words. + assert!(num_words <= SinsemillaC); + + let message = if message.len() < num_words * K { + // If message is shorter than the number of words specified, pad it to the + // correct length. + let pad_len = num_words * K - message.len(); + let mut message = message.clone(); + message.extend_from_slice(&vec![None; pad_len]); + message + } else { + // If message is longer than the number of words specified, truncate it + // to the correct length. + message[..(num_words * K)].to_vec() + }; + + // Number of words that can be contained in a piece + let piece_num_words = C::Base::NUM_BITS as usize / K; + message + .chunks(piece_num_words * K) + .map(|piece| { + let num_words = (piece.len() + K - 1) / K; + self.witness_message_piece_bitstring( + layouter.namespace(|| "message piece"), + piece.to_vec(), + num_words, + ) + }) + .collect() } #[allow(non_snake_case)] @@ -220,7 +248,66 @@ impl SinsemillaInstructions for SinsemillaChip { message: Vec>, num_words: usize, ) -> Result { - todo!() + println!("num_words: {}", num_words); + assert_eq!(message.len(), num_words * 10); + // Message piece must be at most `ceil(C::Base::NUM_BITS / 10)` bits + let max_len = C::Base::NUM_BITS / 10; + assert!(num_words <= max_len as usize); + + // A `K`-bit bitstring. + struct Word([Option; K]); + + // Chunk message into `K`-bit words. + let message: Vec = message + .chunks_exact(K) + .map(|word| Word(word.try_into().unwrap())) + .collect(); + + // Closure to parse a vector of K-bit words into a base field element. + let to_base_field = |words: &[Word]| -> Option { + let bits = words + .into_iter() + .map(|word| word.0.to_vec()) + .flatten() + .collect::>>(); + let bytearray: Vec> = bits + .chunks(8) + .map(|byte| { + byte.iter().rev().fold(Some(0u8), |acc, bit| { + acc.zip(*bit).map(|(acc, bit)| acc * 2 + bit as u8) + }) + }) + .collect(); + + // Pad to 32 bytes + let pad_len = 32 - bytearray.len(); + let bytearray: Option> = bytearray.into_iter().collect(); + let bytearray: Option> = bytearray.map(|mut bytearray| { + bytearray.extend_from_slice(&vec![0; pad_len]); + bytearray + }); + + bytearray.map(|bytearray| C::Base::from_bytes(&bytearray.try_into().unwrap()).unwrap()) + }; + + let config = self.config().clone(); + + layouter.assign_region( + || "message", + |mut region: Region<'_, C::Base>| { + let piece_value = to_base_field(&message); + let cell = region.assign_advice( + || "message piece", + config.bits, + 0, + || piece_value.ok_or(Error::SynthesisError), + )?; + Ok(MessagePiece::new( + CellValue::new(cell, piece_value), + message.len(), + )) + }, + ) } fn witness_message_piece_field( @@ -229,7 +316,23 @@ impl SinsemillaInstructions for SinsemillaChip { value: Option, num_words: usize, ) -> Result { - todo!() + let config = self.config().clone(); + + let cell = layouter.assign_region( + || "witness message piece", + |mut region| { + region.assign_advice( + || "witness message piece", + config.bits, + 0, + || value.ok_or(Error::SynthesisError), + ) + }, + )?; + Ok(Self::prepare_message_piece( + &CellValue::new(cell, value), + num_words, + )) } fn prepare_message_piece(cell: &Self::CellValue, length: usize) -> Self::MessagePiece {