mirror of https://github.com/zcash/halo2.git
Merge pull request #568 from zcash/circuit-review
Changes from Orchard circuit review
This commit is contained in:
commit
b2e2b9b081
|
@ -6,6 +6,11 @@ and this project adheres to Rust's notion of
|
||||||
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
### Added
|
||||||
|
- `halo2_gadgets::sinsemilla::MessagePiece::from_subpieces`
|
||||||
|
- `halo2_gadgets::utilities`:
|
||||||
|
- `FieldValue` trait.
|
||||||
|
- `RangeConstrained` newtype wrapper.
|
||||||
|
|
||||||
## [0.1.0-beta.3] - 2022-04-06
|
## [0.1.0-beta.3] - 2022-04-06
|
||||||
### Changed
|
### Changed
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
//! Gadgets for the Sinsemilla hash function.
|
//! Gadgets for the Sinsemilla hash function.
|
||||||
use crate::{
|
use crate::{
|
||||||
ecc::{self, EccInstructions, FixedPoints},
|
ecc::{self, EccInstructions, FixedPoints},
|
||||||
utilities::Var,
|
utilities::{FieldValue, RangeConstrained, Var},
|
||||||
};
|
};
|
||||||
use group::ff::{Field, PrimeField};
|
use group::ff::{Field, PrimeField};
|
||||||
use halo2_proofs::{circuit::Layouter, plonk::Error};
|
use halo2_proofs::{circuit::Layouter, plonk::Error};
|
||||||
|
@ -221,6 +221,40 @@ where
|
||||||
let inner = chip.witness_message_piece(layouter, field_elem, num_words)?;
|
let inner = chip.witness_message_piece(layouter, field_elem, num_words)?;
|
||||||
Ok(Self { chip, inner })
|
Ok(Self { chip, inner })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Constructs a `MessagePiece` by concatenating a sequence of [`RangeConstrained`]
|
||||||
|
/// subpieces.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if the total number of bits across the subpieces is not a multiple of the
|
||||||
|
/// word size, or if the required bitshift for any subpiece is greater than 63 bits.
|
||||||
|
pub fn from_subpieces(
|
||||||
|
chip: SinsemillaChip,
|
||||||
|
layouter: impl Layouter<C::Base>,
|
||||||
|
subpieces: impl IntoIterator<Item = RangeConstrained<C::Base, Option<C::Base>>>,
|
||||||
|
) -> Result<Self, Error> {
|
||||||
|
let (field_elem, total_bits) =
|
||||||
|
subpieces
|
||||||
|
.into_iter()
|
||||||
|
.fold((Some(C::Base::zero()), 0), |(acc, bits), subpiece| {
|
||||||
|
assert!(bits < 64);
|
||||||
|
let subpiece_shifted = subpiece
|
||||||
|
.inner()
|
||||||
|
.value()
|
||||||
|
.map(|v| C::Base::from(1 << bits) * v);
|
||||||
|
(
|
||||||
|
acc.zip(subpiece_shifted).map(|(a, b)| a + b),
|
||||||
|
bits + subpiece.num_bits(),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
// Message must be composed of `K`-bit words.
|
||||||
|
assert_eq!(total_bits % K, 0);
|
||||||
|
let num_words = total_bits / K;
|
||||||
|
|
||||||
|
Self::from_field_elem(chip, layouter, field_elem, num_words)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A domain in which $\mathsf{SinsemillaHashToPoint}$ and $\mathsf{SinsemillaHash}$ can
|
/// A domain in which $\mathsf{SinsemillaHashToPoint}$ and $\mathsf{SinsemillaHash}$ can
|
||||||
|
|
|
@ -1,17 +1,36 @@
|
||||||
//! Utility gadgets.
|
//! Utility gadgets.
|
||||||
|
|
||||||
use ff::PrimeFieldBits;
|
use ff::{Field, PrimeFieldBits};
|
||||||
use halo2_proofs::{
|
use halo2_proofs::{
|
||||||
circuit::{AssignedCell, Cell, Layouter},
|
circuit::{AssignedCell, Cell, Layouter},
|
||||||
plonk::{Advice, Column, Error, Expression},
|
plonk::{Advice, Column, Error, Expression},
|
||||||
};
|
};
|
||||||
use pasta_curves::arithmetic::FieldExt;
|
use pasta_curves::arithmetic::FieldExt;
|
||||||
|
use std::marker::PhantomData;
|
||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
|
|
||||||
pub mod cond_swap;
|
pub mod cond_swap;
|
||||||
pub mod decompose_running_sum;
|
pub mod decompose_running_sum;
|
||||||
pub mod lookup_range_check;
|
pub mod lookup_range_check;
|
||||||
|
|
||||||
|
/// A type that has a value at either keygen or proving time.
|
||||||
|
pub trait FieldValue<F: Field> {
|
||||||
|
/// Returns the value of this type.
|
||||||
|
fn value(&self) -> Option<&F>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F: Field> FieldValue<F> for Option<F> {
|
||||||
|
fn value(&self) -> Option<&F> {
|
||||||
|
self.as_ref()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F: Field> FieldValue<F> for AssignedCell<F, F> {
|
||||||
|
fn value(&self) -> Option<&F> {
|
||||||
|
self.value()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Trait for a variable in the circuit.
|
/// Trait for a variable in the circuit.
|
||||||
pub trait Var<F: FieldExt>: Clone + std::fmt::Debug + From<AssignedCell<F, F>> {
|
pub trait Var<F: FieldExt>: Clone + std::fmt::Debug + From<AssignedCell<F, F>> {
|
||||||
/// The cell at which this variable was allocated.
|
/// The cell at which this variable was allocated.
|
||||||
|
@ -59,6 +78,62 @@ pub trait UtilitiesInstructions<F: FieldExt> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A type representing a range-constrained field element.
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub struct RangeConstrained<F: Field, T: FieldValue<F>> {
|
||||||
|
inner: T,
|
||||||
|
num_bits: usize,
|
||||||
|
_phantom: PhantomData<F>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F: Field, T: FieldValue<F>> RangeConstrained<F, T> {
|
||||||
|
/// Returns the range-constrained inner type.
|
||||||
|
pub fn inner(&self) -> &T {
|
||||||
|
&self.inner
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the number of bits to which this cell is constrained.
|
||||||
|
pub fn num_bits(&self) -> usize {
|
||||||
|
self.num_bits
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F: PrimeFieldBits> RangeConstrained<F, Option<F>> {
|
||||||
|
/// Constructs a `RangeConstrained<Option<F>>` as a bitrange of the given value.
|
||||||
|
pub fn bitrange_of(value: Option<&F>, bitrange: Range<usize>) -> Self {
|
||||||
|
let num_bits = bitrange.len();
|
||||||
|
Self {
|
||||||
|
inner: value.map(|value| bitrange_subset(value, bitrange)),
|
||||||
|
num_bits,
|
||||||
|
_phantom: PhantomData::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F: Field> RangeConstrained<F, AssignedCell<F, F>> {
|
||||||
|
/// Constructs a `RangeConstrained<AssignedCell<F, F>>` without verifying that the
|
||||||
|
/// cell is correctly range constrained.
|
||||||
|
///
|
||||||
|
/// This API only exists to ease with integrating this type into existing circuits,
|
||||||
|
/// and will likely be removed in future.
|
||||||
|
pub fn unsound_unchecked(cell: AssignedCell<F, F>, num_bits: usize) -> Self {
|
||||||
|
Self {
|
||||||
|
inner: cell,
|
||||||
|
num_bits,
|
||||||
|
_phantom: PhantomData::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Extracts the range-constrained value from this range-constrained cell.
|
||||||
|
pub fn value(&self) -> RangeConstrained<F, Option<F>> {
|
||||||
|
RangeConstrained {
|
||||||
|
inner: self.inner.value().copied(),
|
||||||
|
num_bits: self.num_bits,
|
||||||
|
_phantom: PhantomData::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn transpose_option_array<T: Copy + std::fmt::Debug, const LEN: usize>(
|
pub(crate) fn transpose_option_array<T: Copy + std::fmt::Debug, const LEN: usize>(
|
||||||
option_array: Option<[T; LEN]>,
|
option_array: Option<[T; LEN]>,
|
||||||
) -> [Option<T>; LEN] {
|
) -> [Option<T>; LEN] {
|
||||||
|
|
|
@ -23,6 +23,37 @@ impl<F: FieldExt + PrimeFieldBits> std::ops::Deref for RunningSum<F> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<F: FieldExt + PrimeFieldBits> RangeConstrained<F, AssignedCell<F, F>> {
|
||||||
|
/// Witnesses a subset of the bits in `value` and constrains them to be the correct
|
||||||
|
/// number of bits.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if `bitrange.len() >= K`.
|
||||||
|
pub fn witness_short<const K: usize>(
|
||||||
|
lookup_config: &LookupRangeCheckConfig<F, K>,
|
||||||
|
layouter: impl Layouter<F>,
|
||||||
|
value: Option<&F>,
|
||||||
|
bitrange: Range<usize>,
|
||||||
|
) -> Result<Self, Error> {
|
||||||
|
let num_bits = bitrange.len();
|
||||||
|
assert!(num_bits < K);
|
||||||
|
|
||||||
|
// Witness the subset and constrain it to be the correct number of bits.
|
||||||
|
lookup_config
|
||||||
|
.witness_short_check(
|
||||||
|
layouter,
|
||||||
|
value.map(|value| bitrange_subset(value, bitrange)),
|
||||||
|
num_bits,
|
||||||
|
)
|
||||||
|
.map(|inner| Self {
|
||||||
|
inner,
|
||||||
|
num_bits,
|
||||||
|
_phantom: PhantomData::default(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Configuration that provides methods for a lookup range check.
|
/// Configuration that provides methods for a lookup range check.
|
||||||
#[derive(Eq, PartialEq, Debug, Clone, Copy)]
|
#[derive(Eq, PartialEq, Debug, Clone, Copy)]
|
||||||
pub struct LookupRangeCheckConfig<F: FieldExt + PrimeFieldBits, const K: usize> {
|
pub struct LookupRangeCheckConfig<F: FieldExt + PrimeFieldBits, const K: usize> {
|
||||||
|
|
Loading…
Reference in New Issue