From bdcdb8ac134a4f8582e5a4402ebd2153c9da21ac Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Thu, 24 Jun 2021 16:29:55 +0800 Subject: [PATCH] Move witness_message() and witness_message_piece_bitstring() to gadget level These instructions were not making any assignments; instead, they were calling through to witness_message_piece_field(). This PR also renames the witness_message_piece_field() instruction to witness_message_piece(). --- src/circuit/gadget/sinsemilla.rs | 154 ++++++++++++++++++-------- src/circuit/gadget/sinsemilla/chip.rs | 69 +----------- 2 files changed, 110 insertions(+), 113 deletions(-) diff --git a/src/circuit/gadget/sinsemilla.rs b/src/circuit/gadget/sinsemilla.rs index bc06c8ea..2b4c6735 100644 --- a/src/circuit/gadget/sinsemilla.rs +++ b/src/circuit/gadget/sinsemilla.rs @@ -3,8 +3,10 @@ use crate::circuit::gadget::{ ecc::{self, EccInstructions}, utilities::Var, }; -use halo2::{arithmetic::CurveAffine, circuit::Layouter, plonk::Error}; -use std::fmt::Debug; +use ff::PrimeField; +use halo2::{circuit::Layouter, plonk::Error}; +use pasta_curves::arithmetic::{CurveAffine, FieldExt}; +use std::{convert::TryInto, fmt::Debug}; pub mod chip; mod message; @@ -27,7 +29,7 @@ pub trait SinsemillaInstructions; - /// Witness a message in the given bitstring. - /// Returns a vector of [`Self::MessagePiece`]s encoding the given message. - /// - /// # Panics - /// - /// Panics if the message length is not a multiple of `K`. - /// - /// Panics if the message length exceeds `K * MAX_WORDS`. - fn witness_message( - &self, - layouter: impl Layouter, - message: Vec>, - ) -> Result; - - /// Witnesses a message piece given a field element and the intended number of `K`-bit - /// words it contains. - /// - /// Returns a [`Self::MessagePiece`] encoding the given message. - /// - /// # Panics - /// - /// Panics if the message length is not a multiple of `K`. - /// - /// Panics if the message length exceeds the maximum number of words - /// that can fit in a field element. - fn witness_message_piece_bitstring( - &self, - layouter: impl Layouter, - message: &[Option], - ) -> Result; - /// Witness a message piece given a field element. Returns a [`Self::MessagePiece`] /// encoding the given message. /// @@ -75,7 +46,7 @@ pub trait SinsemillaInstructions, value: Option, @@ -130,24 +101,112 @@ where { fn from_bitstring( chip: SinsemillaChip, - layouter: impl Layouter, + mut layouter: impl Layouter, bitstring: Vec>, ) -> Result { - let inner = chip.witness_message(layouter, bitstring)?; - Ok(Self { chip, inner }) + // Message must be composed of `K`-bit words. + assert_eq!(bitstring.len() % K, 0); + + // Message must have at most `MAX_WORDS` words. + assert!(bitstring.len() / K <= MAX_WORDS); + + // Message piece must be at most `ceil(C::NUM_BITS / K)` bits + let piece_num_words = C::Base::NUM_BITS as usize / K; + let pieces: Result, _> = bitstring + .chunks(piece_num_words * K) + .enumerate() + .map( + |(i, piece)| -> Result, Error> { + MessagePiece::from_bitstring( + chip.clone(), + layouter.namespace(|| format!("message piece {}", i)), + piece, + ) + }, + ) + .collect(); + + pieces.map(|pieces| Self::from_pieces(chip, pieces)) } /// Constructs a message from a vector of [`MessagePiece`]s. /// /// [`MessagePiece`]: SinsemillaInstructions::MessagePiece - fn from_pieces(chip: SinsemillaChip, pieces: Vec) -> Self { + fn from_pieces( + chip: SinsemillaChip, + pieces: Vec>, + ) -> Self { Self { chip, - inner: pieces.into(), + inner: pieces + .iter() + .map(|piece| piece.inner.clone()) + .collect::>() + .into(), } } } +#[derive(Clone, Debug)] +pub struct MessagePiece +where + SinsemillaChip: SinsemillaInstructions + Clone + Debug + Eq, +{ + chip: SinsemillaChip, + inner: SinsemillaChip::MessagePiece, +} + +impl + MessagePiece +where + SinsemillaChip: SinsemillaInstructions + Clone + Debug + Eq, +{ + fn from_bitstring( + chip: SinsemillaChip, + layouter: impl Layouter, + bitstring: &[Option], + ) -> Result { + // Message must be composed of `K`-bit words. + assert_eq!(bitstring.len() % K, 0); + let num_words = bitstring.len() / K; + + // Message piece must be at most `ceil(C::Base::NUM_BITS / K)` bits + let piece_max_num_words = C::Base::NUM_BITS as usize / 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() <= C::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| C::Base::from_bytes(&bytes.try_into().unwrap()).unwrap()) + }; + + let piece_value = to_base_field(bitstring); + Self::from_field_elem(chip, layouter, piece_value, num_words) + } + + fn from_field_elem( + chip: SinsemillaChip, + layouter: impl Layouter, + field_elem: Option, + num_words: usize, + ) -> Result { + let inner = chip.witness_message_piece(layouter, field_elem, num_words)?; + Ok(Self { chip, inner }) + } +} + /// A domain in which $\mathsf{SinsemillaHashToPoint}$ and $\mathsf{SinsemillaHash}$ can /// be used. #[allow(non_snake_case)] @@ -242,7 +301,7 @@ mod tests { use super::{ chip::SinsemillaHashDomains, chip::{SinsemillaChip, SinsemillaConfig}, - HashDomain, Message, SinsemillaInstructions, + HashDomain, Message, MessagePiece, }; use crate::{ @@ -337,13 +396,17 @@ mod tests { // Layer 31, l = MERKLE_DEPTH_ORCHARD - 1 - layer = 0 let l_bitstring = vec![Some(false); K]; - let l = chip1 - .witness_message_piece_bitstring(layouter.namespace(|| "l"), &l_bitstring)?; + let l = MessagePiece::from_bitstring( + chip1.clone(), + layouter.namespace(|| "l"), + &l_bitstring, + )?; // Left leaf let left_bitstring: Vec> = (0..250).map(|_| Some(rand::random::())).collect(); - let left = chip1.witness_message_piece_bitstring( + let left = MessagePiece::from_bitstring( + chip1.clone(), layouter.namespace(|| "left"), &left_bitstring, )?; @@ -351,7 +414,8 @@ mod tests { // Right leaf let right_bitstring: Vec> = (0..250).map(|_| Some(rand::random::())).collect(); - let right = chip1.witness_message_piece_bitstring( + let right = MessagePiece::from_bitstring( + chip1.clone(), layouter.namespace(|| "right"), &right_bitstring, )?; diff --git a/src/circuit/gadget/sinsemilla/chip.rs b/src/circuit/gadget/sinsemilla/chip.rs index 1f3be1fc..d4b80b66 100644 --- a/src/circuit/gadget/sinsemilla/chip.rs +++ b/src/circuit/gadget/sinsemilla/chip.rs @@ -12,7 +12,6 @@ use crate::{ }, }; -use ff::PrimeField; use halo2::{ arithmetic::{CurveAffine, FieldExt}, circuit::{Chip, Layouter}, @@ -24,8 +23,6 @@ use halo2::{ }; use pasta_curves::pallas; -use std::convert::TryInto; - mod generator_table; pub use generator_table::get_s_by_idx; use generator_table::GeneratorTableConfig; @@ -231,71 +228,7 @@ impl SinsemillaInstructions, - message: Vec>, - ) -> Result { - // 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)] - fn witness_message_piece_bitstring( - &self, - layouter: impl Layouter, - message_piece: &[Option], - ) -> Result { - // 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( + fn witness_message_piece( &self, mut layouter: impl Layouter, field_elem: Option,