Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce LookupRangeCheckConfigs for each Sinsemilla advice column #133

Merged
merged 5 commits into from
Jun 29, 2021
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
182 changes: 133 additions & 49 deletions src/circuit/gadget/sinsemilla.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -27,7 +29,7 @@ pub trait SinsemillaInstructions<C: CurveAffine, const K: usize, const MAX_WORDS
///
/// For example, in the case `K = 10`, `NUM_BITS = 255`, we can fit
/// up to `N = 25` words in a single base field element.
type MessagePiece;
type MessagePiece: Clone + Debug;

/// The x-coordinate of a point output of [`Self::hash_to_point`].
type X;
Expand All @@ -37,45 +39,14 @@ pub trait SinsemillaInstructions<C: CurveAffine, const K: usize, const MAX_WORDS
/// HashDomains used in this instruction.
type HashDomains: HashDomains<C>;

/// 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<C::Base>,
message: Vec<Option<bool>>,
) -> Result<Self::Message, Error>;

/// 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<C::Base>,
message: &[Option<bool>],
) -> Result<Self::MessagePiece, Error>;

/// Witness a message piece given a field element. Returns a [`Self::MessagePiece`]
/// encoding the given message.
///
/// # Panics
///
/// Panics if `num_words` exceed the maximum number of `K`-bit words that
/// can fit into a single base field element.
fn witness_message_piece_field(
fn witness_message_piece(
&self,
layouter: impl Layouter<C::Base>,
value: Option<C::Base>,
Expand Down Expand Up @@ -130,24 +101,112 @@ where
{
fn from_bitstring(
chip: SinsemillaChip,
layouter: impl Layouter<C::Base>,
mut layouter: impl Layouter<C::Base>,
bitstring: Vec<Option<bool>>,
) -> Result<Self, Error> {
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<Vec<_>, _> = bitstring
.chunks(piece_num_words * K)
.enumerate()
.map(
|(i, piece)| -> Result<MessagePiece<C, SinsemillaChip, K, MAX_WORDS>, 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<SinsemillaChip::MessagePiece>) -> Self {
fn from_pieces(
chip: SinsemillaChip,
pieces: Vec<MessagePiece<C, SinsemillaChip, K, MAX_WORDS>>,
) -> Self {
Self {
chip,
inner: pieces.into(),
inner: pieces
.iter()
.map(|piece| piece.inner.clone())
therealyingtong marked this conversation as resolved.
Show resolved Hide resolved
.collect::<Vec<_>>()
.into(),
}
}
}

#[derive(Clone, Debug)]
pub struct MessagePiece<C: CurveAffine, SinsemillaChip, const K: usize, const MAX_WORDS: usize>
where
SinsemillaChip: SinsemillaInstructions<C, K, MAX_WORDS> + Clone + Debug + Eq,
{
chip: SinsemillaChip,
inner: SinsemillaChip::MessagePiece,
}

impl<C: CurveAffine, SinsemillaChip, const K: usize, const MAX_WORDS: usize>
MessagePiece<C, SinsemillaChip, K, MAX_WORDS>
where
SinsemillaChip: SinsemillaInstructions<C, K, MAX_WORDS> + Clone + Debug + Eq,
{
fn from_bitstring(
chip: SinsemillaChip,
layouter: impl Layouter<C::Base>,
bitstring: &[Option<bool>],
) -> Result<Self, Error> {
// 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<bool>]| -> Option<C::Base> {
assert!(bits.len() <= C::Base::NUM_BITS as usize);

let bits: Option<Vec<bool>> = bits.iter().cloned().collect();
let bytes: Option<Vec<u8>> = 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<C::Base>,
field_elem: Option<C::Base>,
num_words: usize,
) -> Result<Self, Error> {
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)]
Expand Down Expand Up @@ -242,7 +301,7 @@ mod tests {
use super::{
chip::SinsemillaHashDomains,
chip::{SinsemillaChip, SinsemillaConfig},
HashDomain, Message, SinsemillaInstructions,
HashDomain, Message, MessagePiece,
};

use crate::{
Expand Down Expand Up @@ -278,12 +337,32 @@ mod tests {
meta.advice_column(),
];

let constants = meta.fixed_column();
// TODO: Replace with public inputs API
let constants_1 = [
meta.fixed_column(),
meta.fixed_column(),
meta.fixed_column(),
meta.fixed_column(),
meta.fixed_column(),
meta.fixed_column(),
];
let constants_2 = [
meta.fixed_column(),
meta.fixed_column(),
meta.fixed_column(),
meta.fixed_column(),
meta.fixed_column(),
meta.fixed_column(),
];
let constants_3 = meta.fixed_column();
therealyingtong marked this conversation as resolved.
Show resolved Hide resolved

let perm = meta.permutation(
&advices
.iter()
.map(|advice| (*advice).into())
.chain(Some(constants.into()))
.chain(Some(constants_3.into()))
.chain(constants_1.iter().map(|fixed| (*fixed).into()))
.chain(constants_2.iter().map(|fixed| (*fixed).into()))
.collect::<Vec<_>>(),
);

Expand All @@ -300,14 +379,14 @@ mod tests {
meta,
advices[..5].try_into().unwrap(),
lookup,
constants,
constants_1,
perm.clone(),
);
let config2 = SinsemillaChip::configure(
meta,
advices[5..].try_into().unwrap(),
lookup,
constants,
constants_2,
perm,
);
(ecc_config, config1, config2)
Expand Down Expand Up @@ -337,21 +416,26 @@ 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<Option<bool>> =
(0..250).map(|_| Some(rand::random::<bool>())).collect();
let left = chip1.witness_message_piece_bitstring(
let left = MessagePiece::from_bitstring(
chip1.clone(),
layouter.namespace(|| "left"),
&left_bitstring,
)?;

// Right leaf
let right_bitstring: Vec<Option<bool>> =
(0..250).map(|_| Some(rand::random::<bool>())).collect();
let right = chip1.witness_message_piece_bitstring(
let right = MessagePiece::from_bitstring(
chip1.clone(),
layouter.namespace(|| "right"),
&right_bitstring,
)?;
Expand Down
Loading