Skip to content

Commit

Permalink
Use const generics for K and C.
Browse files Browse the repository at this point in the history
Also document panics and internally rename variables for clarity.
  • Loading branch information
therealyingtong committed Jun 4, 2021
1 parent f8cc0dc commit 2d39432
Show file tree
Hide file tree
Showing 3 changed files with 188 additions and 137 deletions.
157 changes: 104 additions & 53 deletions src/circuit/gadget/sinsemilla.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,53 +7,86 @@ pub mod chip;
pub use chip::{SinsemillaChip, SinsemillaConfig};

/// The set of circuit instructions required to use the [`Sinsemilla`](https://zcash.github.io/halo2/design/gadgets/sinsemilla.html) gadget.
pub trait SinsemillaInstructions<C: CurveAffine> {
/// This trait is bounded on two constant parameters: `K`, the number of bits
/// in each word accepted by the Sinsemilla hash, and `MAX_WORDS`, the maximum
/// number of words that a single hash instance can process.
pub trait SinsemillaInstructions<C: CurveAffine, const K: usize, const MAX_WORDS: usize> {
/// A piece in a message containing a number of `K`-bit words.
/// A [`MessagePiece`] fits in a single cell, which means it can only
/// contain up to `N` words, where `N*K <= C::Base::NUM_BITS`.
/// A [`MessagePiece`] fits in a single base field element,
/// which means it can only contain up to `N` words, where
/// `N*K <= C::Base::NUM_BITS`.
///
/// 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;

/// A cell in the circuit
/// A cell in the circuit with an optional assigned value.
type CellValue;

/// The x-coordinate of a point output of [`hash_to_point`].
type X;
/// A point output of [`hash_to_point`].
type Point: Clone + Debug;
/// The fixed points used in `CommitDomains`.
/// A type enumerating the fixed points used in `CommitDomains`.
type FixedPoints: Clone + Debug;

/// HashDomains used in this instruction.
type HashDomains: HashDomains<C>;
/// CommitDomains used in this instruction.
type CommitDomains: CommitDomains<C, Self::FixedPoints, Self::HashDomains>;

/// Witness a message given a bitstring.
/// Witness a message given a bitstring and the intended number of `K`-bit
/// words represented by the bitstring.
/// Returns a vector of [`MessagePiece`]s encoding the given message.
///
/// # Panics
///
/// Panics if `num_words` exceeds the `MAX_WORDS` that this Sinsemilla
/// instance can handle.
///
/// Panics if `num_words` is inconsistent with the length of the bitstring.
/// That is, we require `message.len() == num_words * K`.
fn witness_message(
&self,
layouter: impl Layouter<C::Base>,
message: Vec<Option<bool>>,
num_words: usize,
) -> Result<Vec<Self::MessagePiece>, Error>;

/// Witness a message piece given a bitstring.
/// Witness a message piece given a bitstring. Returns a [`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_bitstring(
&self,
layouter: impl Layouter<C::Base>,
message: Vec<Option<bool>>,
num_words: usize,
) -> Result<Self::MessagePiece, Error>;

/// Witness a message piece given a field element.
/// Witness a message piece given a field element. Returns a [`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.
///
/// Panics if `num_words` is inconsistent with the length of the bitstring.
/// That is, we require `message.len() == num_words * K`.
fn witness_message_piece_field(
&self,
layouter: impl Layouter<C::Base>,
value: Option<C::Base>,
num_words: usize,
) -> Result<Self::MessagePiece, Error>;

/// Prepare a message piece given a [`CellValue`] and a length.
fn prepare_message_piece(cell: &Self::CellValue, length: usize) -> Self::MessagePiece;
/// Prepare a message piece given a [`CellValue`] and the number of words
/// encoded in the contained base field element.
fn prepare_message_piece(cell: &Self::CellValue, num_words: usize) -> Self::MessagePiece;

/// Hashes a message to an ECC curve point.
#[allow(non_snake_case)]
Expand All @@ -71,23 +104,26 @@ pub trait SinsemillaInstructions<C: CurveAffine> {
/// A message to be hashed.
///
/// Composed of [`MessagePiece`]s with bitlength some multiple of [`crate::primitives::sinsemilla::K`].

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

impl<C: CurveAffine, SinsemillaChip: SinsemillaInstructions<C> + Clone + Debug + Eq>
Message<C, SinsemillaChip>
impl<C: CurveAffine, SinsemillaChip, const K: usize, const MAX_WORDS: usize>
Message<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: Vec<Option<bool>>,
num_words: usize,
) -> Result<Self, Error> {
println!("from_bitstring num_words: {}", num_words);
let inner = chip.witness_message(layouter, bitstring, num_words)?;
Ok(Self { chip, inner })
}
Expand All @@ -105,24 +141,30 @@ impl<C: CurveAffine, SinsemillaChip: SinsemillaInstructions<C> + Clone + Debug +
&self.inner
}

/// Construct a `MessagePiece` from a vector of `CellValue` and a length.
/// Construct a `MessagePiece` given a vector of `CellValue`s and the
/// number of words encoded in the contained base field elements.
fn new_piece(
_chip: SinsemillaChip,
cell: &SinsemillaChip::CellValue,
length: usize,
num_words: usize,
) -> SinsemillaChip::MessagePiece {
SinsemillaChip::prepare_message_piece(cell, length)
SinsemillaChip::prepare_message_piece(cell, num_words)
}
}

#[allow(non_snake_case)]
pub struct HashDomain<C: CurveAffine, SinsemillaChip, EccChip>
where
SinsemillaChip: SinsemillaInstructions<C> + Clone + Debug + Eq,
pub struct HashDomain<
C: CurveAffine,
SinsemillaChip,
EccChip,
const K: usize,
const MAX_WORDS: usize,
> where
SinsemillaChip: SinsemillaInstructions<C, K, MAX_WORDS> + Clone + Debug + Eq,
EccChip: EccInstructions<
C,
Point = <SinsemillaChip as SinsemillaInstructions<C>>::Point,
FixedPoints = <SinsemillaChip as SinsemillaInstructions<C>>::FixedPoints,
Point = <SinsemillaChip as SinsemillaInstructions<C, K, MAX_WORDS>>::Point,
FixedPoints = <SinsemillaChip as SinsemillaInstructions<C, K, MAX_WORDS>>::FixedPoints,
> + Clone
+ Debug
+ Eq,
Expand All @@ -132,13 +174,14 @@ where
pub Q: C,
}

impl<C: CurveAffine, SinsemillaChip, EccChip> HashDomain<C, SinsemillaChip, EccChip>
impl<C: CurveAffine, SinsemillaChip, EccChip, const K: usize, const MAX_WORDS: usize>
HashDomain<C, SinsemillaChip, EccChip, K, MAX_WORDS>
where
SinsemillaChip: SinsemillaInstructions<C> + Clone + Debug + Eq,
SinsemillaChip: SinsemillaInstructions<C, K, MAX_WORDS> + Clone + Debug + Eq,
EccChip: EccInstructions<
C,
Point = <SinsemillaChip as SinsemillaInstructions<C>>::Point,
FixedPoints = <SinsemillaChip as SinsemillaInstructions<C>>::FixedPoints,
Point = <SinsemillaChip as SinsemillaInstructions<C, K, MAX_WORDS>>::Point,
FixedPoints = <SinsemillaChip as SinsemillaInstructions<C, K, MAX_WORDS>>::FixedPoints,
> + Clone
+ Debug
+ Eq,
Expand All @@ -163,7 +206,7 @@ where
pub fn hash_to_point(
&self,
layouter: impl Layouter<C::Base>,
message: Message<C, SinsemillaChip>,
message: Message<C, SinsemillaChip, K, MAX_WORDS>,
) -> Result<ecc::Point<C, EccChip>, Error> {
assert_eq!(self.sinsemilla_chip, message.chip);
self.sinsemilla_chip
Expand All @@ -177,7 +220,7 @@ where
pub fn hash(
&self,
layouter: impl Layouter<C::Base>,
message: Message<C, SinsemillaChip>,
message: Message<C, SinsemillaChip, K, MAX_WORDS>,
) -> Result<ecc::X<C, EccChip>, Error> {
assert_eq!(self.sinsemilla_chip, message.chip);
let p = self.hash_to_point(layouter, message);
Expand All @@ -203,28 +246,34 @@ pub trait HashDomains<C: CurveAffine>: Clone + Debug {
}

#[allow(non_snake_case)]
pub struct CommitDomain<C: CurveAffine, SinsemillaChip, EccChip>
where
SinsemillaChip: SinsemillaInstructions<C> + Clone + Debug + Eq,
pub struct CommitDomain<
C: CurveAffine,
SinsemillaChip,
EccChip,
const K: usize,
const MAX_WORDS: usize,
> where
SinsemillaChip: SinsemillaInstructions<C, K, MAX_WORDS> + Clone + Debug + Eq,
EccChip: EccInstructions<
C,
Point = <SinsemillaChip as SinsemillaInstructions<C>>::Point,
FixedPoints = <SinsemillaChip as SinsemillaInstructions<C>>::FixedPoints,
Point = <SinsemillaChip as SinsemillaInstructions<C, K, MAX_WORDS>>::Point,
FixedPoints = <SinsemillaChip as SinsemillaInstructions<C, K, MAX_WORDS>>::FixedPoints,
> + Clone
+ Debug
+ Eq,
{
M: HashDomain<C, SinsemillaChip, EccChip>,
M: HashDomain<C, SinsemillaChip, EccChip, K, MAX_WORDS>,
R: ecc::FixedPoint<C, EccChip>,
}

impl<C: CurveAffine, SinsemillaChip, EccChip> CommitDomain<C, SinsemillaChip, EccChip>
impl<C: CurveAffine, SinsemillaChip, EccChip, const K: usize, const MAX_WORDS: usize>
CommitDomain<C, SinsemillaChip, EccChip, K, MAX_WORDS>
where
SinsemillaChip: SinsemillaInstructions<C> + Clone + Debug + Eq,
SinsemillaChip: SinsemillaInstructions<C, K, MAX_WORDS> + Clone + Debug + Eq,
EccChip: EccInstructions<
C,
Point = <SinsemillaChip as SinsemillaInstructions<C>>::Point,
FixedPoints = <SinsemillaChip as SinsemillaInstructions<C>>::FixedPoints,
Point = <SinsemillaChip as SinsemillaInstructions<C, K, MAX_WORDS>>::Point,
FixedPoints = <SinsemillaChip as SinsemillaInstructions<C, K, MAX_WORDS>>::FixedPoints,
> + Clone
+ Debug
+ Eq,
Expand All @@ -247,7 +296,7 @@ where
pub fn commit(
&self,
mut layouter: impl Layouter<C::Base>,
message: Message<C, SinsemillaChip>,
message: Message<C, SinsemillaChip, K, MAX_WORDS>,
r: ecc::ScalarFixed<C, EccChip>,
) -> Result<ecc::Point<C, EccChip>, Error> {
assert_eq!(self.M.sinsemilla_chip, message.chip);
Expand All @@ -263,7 +312,7 @@ where
pub fn short_commit(
&self,
mut layouter: impl Layouter<C::Base>,
message: Message<C, SinsemillaChip>,
message: Message<C, SinsemillaChip, K, MAX_WORDS>,
r: ecc::ScalarFixed<C, EccChip>,
) -> Result<ecc::X<C, EccChip>, Error> {
assert_eq!(self.M.sinsemilla_chip, message.chip);
Expand Down Expand Up @@ -295,11 +344,13 @@ mod tests {

use std::convert::TryInto;

struct MyCircuit<C: CurveAffine> {
struct MyCircuit<C: CurveAffine, const K: usize, const MAX_WORDS: usize> {
_marker: std::marker::PhantomData<C>,
}

impl<C: CurveAffine> Circuit<C::Base> for MyCircuit<C> {
impl<C: CurveAffine, const K: usize, const MAX_WORDS: usize> Circuit<C::Base>
for MyCircuit<C, K, MAX_WORDS>
{
type Config = (EccConfig, SinsemillaConfig, SinsemillaConfig);

#[allow(non_snake_case)]
Expand Down Expand Up @@ -332,13 +383,13 @@ mod tests {
meta.fixed_column(),
);

let config1 = SinsemillaChip::<C>::configure(
let config1 = SinsemillaChip::<C, K, MAX_WORDS>::configure(
meta,
advices[..5].try_into().unwrap(),
lookup,
perm.clone(),
);
let config2 = SinsemillaChip::<C>::configure(
let config2 = SinsemillaChip::<C, K, MAX_WORDS>::configure(
meta,
advices[5..].try_into().unwrap(),
lookup,
Expand All @@ -356,13 +407,12 @@ mod tests {
let ecc_chip = EccChip::<C>::construct(config.0);

// The two `SinsemillaChip`s share the same lookup table.
SinsemillaChip::<C>::load(config.1.clone(), &mut layouter)?;
SinsemillaChip::<C, K, MAX_WORDS>::load(config.1.clone(), &mut layouter)?;

// This MerkleCRH example does not use the optimal bit-packing that will
// be used in the actual Orchard circuit.
// That requires bit decomposition which will be done in the Orchard circuit.
// This MerkleCRH example is purely for illustrative purposes.
// It is not an implementation of the Orchard protocol spec.
{
let chip1 = SinsemillaChip::<C>::construct(config.1);
let chip1 = SinsemillaChip::<C, K, MAX_WORDS>::construct(config.1);

let merkle_crh = HashDomain::new(
chip1.clone(),
Expand Down Expand Up @@ -414,7 +464,7 @@ mod tests {
}

{
let chip2 = SinsemillaChip::<C>::construct(config.2);
let chip2 = SinsemillaChip::<C, K, MAX_WORDS>::construct(config.2);

let commit_ivk = CommitDomain::new(
chip2.clone(),
Expand All @@ -427,7 +477,7 @@ mod tests {
Some(C::Scalar::rand()),
)?;
let message: Vec<Option<bool>> =
(0..501).map(|_| Some(rand::random::<bool>())).collect();
(0..500).map(|_| Some(rand::random::<bool>())).collect();
let message = Message::from_bitstring(
chip2,
layouter.namespace(|| "witness message"),
Expand All @@ -443,8 +493,9 @@ mod tests {

#[test]
fn sinsemilla() {
use crate::primitives::sinsemilla::{C, K};
let k = 11;
let circuit = MyCircuit::<pallas::Affine> {
let circuit = MyCircuit::<pallas::Affine, K, C> {
_marker: std::marker::PhantomData,
};
let prover = MockProver::run(k, &circuit, vec![]).unwrap();
Expand Down
Loading

0 comments on commit 2d39432

Please sign in to comment.