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).
|
||||
|
||||
## [Unreleased]
|
||||
### Added
|
||||
- `halo2_gadgets::sinsemilla::MessagePiece::from_subpieces`
|
||||
- `halo2_gadgets::utilities`:
|
||||
- `FieldValue` trait.
|
||||
- `RangeConstrained` newtype wrapper.
|
||||
|
||||
## [0.1.0-beta.3] - 2022-04-06
|
||||
### Changed
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
//! Gadgets for the Sinsemilla hash function.
|
||||
use crate::{
|
||||
ecc::{self, EccInstructions, FixedPoints},
|
||||
utilities::Var,
|
||||
utilities::{FieldValue, RangeConstrained, Var},
|
||||
};
|
||||
use group::ff::{Field, PrimeField};
|
||||
use halo2_proofs::{circuit::Layouter, plonk::Error};
|
||||
|
@ -221,6 +221,40 @@ where
|
|||
let inner = chip.witness_message_piece(layouter, field_elem, num_words)?;
|
||||
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
|
||||
|
|
|
@ -1,17 +1,36 @@
|
|||
//! Utility gadgets.
|
||||
|
||||
use ff::PrimeFieldBits;
|
||||
use ff::{Field, PrimeFieldBits};
|
||||
use halo2_proofs::{
|
||||
circuit::{AssignedCell, Cell, Layouter},
|
||||
plonk::{Advice, Column, Error, Expression},
|
||||
};
|
||||
use pasta_curves::arithmetic::FieldExt;
|
||||
use std::marker::PhantomData;
|
||||
use std::ops::Range;
|
||||
|
||||
pub mod cond_swap;
|
||||
pub mod decompose_running_sum;
|
||||
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.
|
||||
pub trait Var<F: FieldExt>: Clone + std::fmt::Debug + From<AssignedCell<F, F>> {
|
||||
/// 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>(
|
||||
option_array: 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.
|
||||
#[derive(Eq, PartialEq, Debug, Clone, Copy)]
|
||||
pub struct LookupRangeCheckConfig<F: FieldExt + PrimeFieldBits, const K: usize> {
|
||||
|
|
Loading…
Reference in New Issue