diff --git a/src/circuit/gadget/sinsemilla/chip.rs b/src/circuit/gadget/sinsemilla/chip.rs index da6a079b..eb07b13a 100644 --- a/src/circuit/gadget/sinsemilla/chip.rs +++ b/src/circuit/gadget/sinsemilla/chip.rs @@ -128,7 +128,26 @@ impl SinsemillaInstructions, message: Vec>, ) -> Result { - todo!() + // Message must be composed of `K`-bit words. + assert_eq!(message.len() % sinsemilla::K, 0); + + // Message must have at most `sinsemilla::C` words. + assert!(message.len() / sinsemilla::K <= sinsemilla::C); + + // Message piece must be at most `ceil(pallas::Base::NUM_BITS / sinsemilla::K)` bits + let piece_num_words = pallas::Base::NUM_BITS as usize / sinsemilla::K; + let pieces: Result, _> = message + .chunks(piece_num_words * sinsemilla::K) + .enumerate() + .map(|(i, piece)| -> Result { + self.witness_message_piece_bitstring( + layouter.namespace(|| format!("message piece {}", i)), + piece, + ) + }) + .collect(); + + pieces.map(|pieces| pieces.into()) } #[allow(non_snake_case)] @@ -137,7 +156,34 @@ impl SinsemillaInstructions, message_piece: &[Option], ) -> Result { - todo!() + // Message must be composed of `K`-bit words. + assert_eq!(message_piece.len() % sinsemilla::K, 0); + let num_words = message_piece.len() / sinsemilla::K; + + // Message piece must be at most `ceil(C::Base::NUM_BITS / sinsemilla::K)` bits + let piece_max_num_words = pallas::Base::NUM_BITS as usize / sinsemilla::K; + assert!(num_words <= piece_max_num_words as usize); + + // Closure to parse a bitstring (little-endian) into a base field element. + let to_base_field = |bits: &[Option]| -> Option { + assert!(bits.len() <= pallas::Base::NUM_BITS as usize); + + let bits: Option> = bits.iter().cloned().collect(); + let bytes: Option> = bits.map(|bits| { + // Pad bits to 256 bits + let pad_len = 256 - bits.len(); + let mut bits = bits; + bits.extend_from_slice(&vec![false; pad_len]); + + bits.chunks_exact(8) + .map(|byte| byte.iter().rev().fold(0u8, |acc, bit| acc * 2 + *bit as u8)) + .collect() + }); + bytes.map(|bytes| pallas::Base::from_bytes(&bytes.try_into().unwrap()).unwrap()) + }; + + let piece_value = to_base_field(message_piece); + self.witness_message_piece_field(layouter, piece_value, num_words) } fn witness_message_piece_field( @@ -146,7 +192,20 @@ impl SinsemillaInstructions, 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, + || field_elem.ok_or(Error::SynthesisError), + ) + }, + )?; + Ok(MessagePiece::new(cell, field_elem, num_words)) } #[allow(non_snake_case)]