mirror of https://github.com/zcash/halo2.git
Merge pull request #133 from zcash/patch-sinsemilla
Introduce `LookupRangeCheckConfig`s for each Sinsemilla advice column
This commit is contained in:
commit
9f1bd64fe9
|
@ -3,8 +3,10 @@ use crate::circuit::gadget::{
|
||||||
ecc::{self, EccInstructions},
|
ecc::{self, EccInstructions},
|
||||||
utilities::Var,
|
utilities::Var,
|
||||||
};
|
};
|
||||||
use halo2::{arithmetic::CurveAffine, circuit::Layouter, plonk::Error};
|
use ff::PrimeField;
|
||||||
use std::fmt::Debug;
|
use halo2::{circuit::Layouter, plonk::Error};
|
||||||
|
use pasta_curves::arithmetic::{CurveAffine, FieldExt};
|
||||||
|
use std::{convert::TryInto, fmt::Debug};
|
||||||
|
|
||||||
pub mod chip;
|
pub mod chip;
|
||||||
mod message;
|
mod message;
|
||||||
|
@ -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
|
/// For example, in the case `K = 10`, `NUM_BITS = 255`, we can fit
|
||||||
/// up to `N = 25` words in a single base field element.
|
/// 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`].
|
/// The x-coordinate of a point output of [`Self::hash_to_point`].
|
||||||
type X;
|
type X;
|
||||||
|
@ -37,37 +39,6 @@ pub trait SinsemillaInstructions<C: CurveAffine, const K: usize, const MAX_WORDS
|
||||||
/// HashDomains used in this instruction.
|
/// HashDomains used in this instruction.
|
||||||
type HashDomains: HashDomains<C>;
|
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`]
|
/// Witness a message piece given a field element. Returns a [`Self::MessagePiece`]
|
||||||
/// encoding the given message.
|
/// encoding the given message.
|
||||||
///
|
///
|
||||||
|
@ -75,7 +46,7 @@ pub trait SinsemillaInstructions<C: CurveAffine, const K: usize, const MAX_WORDS
|
||||||
///
|
///
|
||||||
/// Panics if `num_words` exceed the maximum number of `K`-bit words that
|
/// Panics if `num_words` exceed the maximum number of `K`-bit words that
|
||||||
/// can fit into a single base field element.
|
/// can fit into a single base field element.
|
||||||
fn witness_message_piece_field(
|
fn witness_message_piece(
|
||||||
&self,
|
&self,
|
||||||
layouter: impl Layouter<C::Base>,
|
layouter: impl Layouter<C::Base>,
|
||||||
value: Option<C::Base>,
|
value: Option<C::Base>,
|
||||||
|
@ -130,24 +101,112 @@ where
|
||||||
{
|
{
|
||||||
fn from_bitstring(
|
fn from_bitstring(
|
||||||
chip: SinsemillaChip,
|
chip: SinsemillaChip,
|
||||||
layouter: impl Layouter<C::Base>,
|
mut layouter: impl Layouter<C::Base>,
|
||||||
bitstring: Vec<Option<bool>>,
|
bitstring: Vec<Option<bool>>,
|
||||||
) -> Result<Self, Error> {
|
) -> Result<Self, Error> {
|
||||||
let inner = chip.witness_message(layouter, bitstring)?;
|
// Message must be composed of `K`-bit words.
|
||||||
Ok(Self { chip, inner })
|
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.
|
/// Constructs a message from a vector of [`MessagePiece`]s.
|
||||||
///
|
///
|
||||||
/// [`MessagePiece`]: SinsemillaInstructions::MessagePiece
|
/// [`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 {
|
Self {
|
||||||
chip,
|
chip,
|
||||||
inner: pieces.into(),
|
inner: pieces
|
||||||
|
.into_iter()
|
||||||
|
.map(|piece| piece.inner)
|
||||||
|
.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
|
/// A domain in which $\mathsf{SinsemillaHashToPoint}$ and $\mathsf{SinsemillaHash}$ can
|
||||||
/// be used.
|
/// be used.
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
|
@ -242,7 +301,7 @@ mod tests {
|
||||||
use super::{
|
use super::{
|
||||||
chip::SinsemillaHashDomains,
|
chip::SinsemillaHashDomains,
|
||||||
chip::{SinsemillaChip, SinsemillaConfig},
|
chip::{SinsemillaChip, SinsemillaConfig},
|
||||||
HashDomain, Message, SinsemillaInstructions,
|
HashDomain, Message, MessagePiece,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -278,12 +337,30 @@ mod tests {
|
||||||
meta.advice_column(),
|
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 perm = meta.permutation(
|
let perm = meta.permutation(
|
||||||
&advices
|
&advices
|
||||||
.iter()
|
.iter()
|
||||||
.map(|advice| (*advice).into())
|
.map(|advice| (*advice).into())
|
||||||
.chain(Some(constants.into()))
|
.chain(constants_1.iter().map(|fixed| (*fixed).into()))
|
||||||
|
.chain(constants_2.iter().map(|fixed| (*fixed).into()))
|
||||||
.collect::<Vec<_>>(),
|
.collect::<Vec<_>>(),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -300,14 +377,14 @@ mod tests {
|
||||||
meta,
|
meta,
|
||||||
advices[..5].try_into().unwrap(),
|
advices[..5].try_into().unwrap(),
|
||||||
lookup,
|
lookup,
|
||||||
constants,
|
constants_1,
|
||||||
perm.clone(),
|
perm.clone(),
|
||||||
);
|
);
|
||||||
let config2 = SinsemillaChip::configure(
|
let config2 = SinsemillaChip::configure(
|
||||||
meta,
|
meta,
|
||||||
advices[5..].try_into().unwrap(),
|
advices[5..].try_into().unwrap(),
|
||||||
lookup,
|
lookup,
|
||||||
constants,
|
constants_2,
|
||||||
perm,
|
perm,
|
||||||
);
|
);
|
||||||
(ecc_config, config1, config2)
|
(ecc_config, config1, config2)
|
||||||
|
@ -337,13 +414,17 @@ mod tests {
|
||||||
|
|
||||||
// Layer 31, l = MERKLE_DEPTH_ORCHARD - 1 - layer = 0
|
// Layer 31, l = MERKLE_DEPTH_ORCHARD - 1 - layer = 0
|
||||||
let l_bitstring = vec![Some(false); K];
|
let l_bitstring = vec![Some(false); K];
|
||||||
let l = chip1
|
let l = MessagePiece::from_bitstring(
|
||||||
.witness_message_piece_bitstring(layouter.namespace(|| "l"), &l_bitstring)?;
|
chip1.clone(),
|
||||||
|
layouter.namespace(|| "l"),
|
||||||
|
&l_bitstring,
|
||||||
|
)?;
|
||||||
|
|
||||||
// Left leaf
|
// Left leaf
|
||||||
let left_bitstring: Vec<Option<bool>> =
|
let left_bitstring: Vec<Option<bool>> =
|
||||||
(0..250).map(|_| Some(rand::random::<bool>())).collect();
|
(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"),
|
layouter.namespace(|| "left"),
|
||||||
&left_bitstring,
|
&left_bitstring,
|
||||||
)?;
|
)?;
|
||||||
|
@ -351,7 +432,8 @@ mod tests {
|
||||||
// Right leaf
|
// Right leaf
|
||||||
let right_bitstring: Vec<Option<bool>> =
|
let right_bitstring: Vec<Option<bool>> =
|
||||||
(0..250).map(|_| Some(rand::random::<bool>())).collect();
|
(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"),
|
layouter.namespace(|| "right"),
|
||||||
&right_bitstring,
|
&right_bitstring,
|
||||||
)?;
|
)?;
|
||||||
|
|
|
@ -5,14 +5,13 @@ use super::{
|
||||||
use crate::{
|
use crate::{
|
||||||
circuit::gadget::{
|
circuit::gadget::{
|
||||||
ecc::chip::EccPoint,
|
ecc::chip::EccPoint,
|
||||||
utilities::{CellValue, Var},
|
utilities::{lookup_range_check::LookupRangeCheckConfig, CellValue, Var},
|
||||||
},
|
},
|
||||||
primitives::sinsemilla::{
|
primitives::sinsemilla::{
|
||||||
self, Q_COMMIT_IVK_M_GENERATOR, Q_MERKLE_CRH, Q_NOTE_COMMITMENT_M_GENERATOR,
|
self, Q_COMMIT_IVK_M_GENERATOR, Q_MERKLE_CRH, Q_NOTE_COMMITMENT_M_GENERATOR,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
use ff::PrimeField;
|
|
||||||
use halo2::{
|
use halo2::{
|
||||||
arithmetic::{CurveAffine, FieldExt},
|
arithmetic::{CurveAffine, FieldExt},
|
||||||
circuit::{Chip, Layouter},
|
circuit::{Chip, Layouter},
|
||||||
|
@ -24,8 +23,6 @@ use halo2::{
|
||||||
};
|
};
|
||||||
use pasta_curves::pallas;
|
use pasta_curves::pallas;
|
||||||
|
|
||||||
use std::convert::TryInto;
|
|
||||||
|
|
||||||
mod generator_table;
|
mod generator_table;
|
||||||
pub use generator_table::get_s_by_idx;
|
pub use generator_table::get_s_by_idx;
|
||||||
use generator_table::GeneratorTableConfig;
|
use generator_table::GeneratorTableConfig;
|
||||||
|
@ -60,13 +57,19 @@ pub struct SinsemillaConfig {
|
||||||
lambda_2: Column<Advice>,
|
lambda_2: Column<Advice>,
|
||||||
/// The lookup table where $(\mathsf{idx}, x_p, y_p)$ are loaded for the $2^K$
|
/// The lookup table where $(\mathsf{idx}, x_p, y_p)$ are loaded for the $2^K$
|
||||||
/// generators of the Sinsemilla hash.
|
/// generators of the Sinsemilla hash.
|
||||||
generator_table: GeneratorTableConfig,
|
pub(super) generator_table: GeneratorTableConfig,
|
||||||
/// Fixed column shared by the whole circuit. This is used to load the
|
/// Fixed column shared by the whole circuit. This is used to load the
|
||||||
/// x-coordinate of the domain $Q$, which is then constrained to equal the
|
/// x-coordinate of the domain $Q$, which is then constrained to equal the
|
||||||
/// initial $x_a$.
|
/// initial $x_a$.
|
||||||
constants: Column<Fixed>,
|
constants: Column<Fixed>,
|
||||||
/// Permutation over all advice columns and the `constants` fixed column.
|
/// Permutation over all advice columns and the `constants` fixed column.
|
||||||
perm: Permutation,
|
pub(super) perm: Permutation,
|
||||||
|
/// Configure each advice column to be able to perform lookup range checks.
|
||||||
|
pub(super) lookup_config_0: LookupRangeCheckConfig<pallas::Base, { sinsemilla::K }>,
|
||||||
|
pub(super) lookup_config_1: LookupRangeCheckConfig<pallas::Base, { sinsemilla::K }>,
|
||||||
|
pub(super) lookup_config_2: LookupRangeCheckConfig<pallas::Base, { sinsemilla::K }>,
|
||||||
|
pub(super) lookup_config_3: LookupRangeCheckConfig<pallas::Base, { sinsemilla::K }>,
|
||||||
|
pub(super) lookup_config_4: LookupRangeCheckConfig<pallas::Base, { sinsemilla::K }>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Eq, PartialEq, Clone, Debug)]
|
#[derive(Eq, PartialEq, Clone, Debug)]
|
||||||
|
@ -106,7 +109,7 @@ impl SinsemillaChip {
|
||||||
meta: &mut ConstraintSystem<pallas::Base>,
|
meta: &mut ConstraintSystem<pallas::Base>,
|
||||||
advices: [Column<Advice>; 5],
|
advices: [Column<Advice>; 5],
|
||||||
lookup: (Column<Fixed>, Column<Fixed>, Column<Fixed>),
|
lookup: (Column<Fixed>, Column<Fixed>, Column<Fixed>),
|
||||||
constants: Column<Fixed>,
|
constants: [Column<Fixed>; 6], // TODO: replace with public inputs API
|
||||||
perm: Permutation,
|
perm: Permutation,
|
||||||
) -> <Self as Chip<pallas::Base>>::Config {
|
) -> <Self as Chip<pallas::Base>>::Config {
|
||||||
let config = SinsemillaConfig {
|
let config = SinsemillaConfig {
|
||||||
|
@ -123,7 +126,42 @@ impl SinsemillaChip {
|
||||||
table_x: lookup.1,
|
table_x: lookup.1,
|
||||||
table_y: lookup.2,
|
table_y: lookup.2,
|
||||||
},
|
},
|
||||||
constants,
|
constants: constants[5],
|
||||||
|
lookup_config_0: LookupRangeCheckConfig::configure(
|
||||||
|
meta,
|
||||||
|
advices[0],
|
||||||
|
constants[0],
|
||||||
|
lookup.0,
|
||||||
|
perm.clone(),
|
||||||
|
),
|
||||||
|
lookup_config_1: LookupRangeCheckConfig::configure(
|
||||||
|
meta,
|
||||||
|
advices[1],
|
||||||
|
constants[1],
|
||||||
|
lookup.0,
|
||||||
|
perm.clone(),
|
||||||
|
),
|
||||||
|
lookup_config_2: LookupRangeCheckConfig::configure(
|
||||||
|
meta,
|
||||||
|
advices[2],
|
||||||
|
constants[2],
|
||||||
|
lookup.0,
|
||||||
|
perm.clone(),
|
||||||
|
),
|
||||||
|
lookup_config_3: LookupRangeCheckConfig::configure(
|
||||||
|
meta,
|
||||||
|
advices[3],
|
||||||
|
constants[3],
|
||||||
|
lookup.0,
|
||||||
|
perm.clone(),
|
||||||
|
),
|
||||||
|
lookup_config_4: LookupRangeCheckConfig::configure(
|
||||||
|
meta,
|
||||||
|
advices[4],
|
||||||
|
constants[4],
|
||||||
|
lookup.0,
|
||||||
|
perm.clone(),
|
||||||
|
),
|
||||||
perm,
|
perm,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -225,71 +263,7 @@ impl SinsemillaInstructions<pallas::Affine, { sinsemilla::K }, { sinsemilla::C }
|
||||||
|
|
||||||
type HashDomains = SinsemillaHashDomains;
|
type HashDomains = SinsemillaHashDomains;
|
||||||
|
|
||||||
#[allow(non_snake_case)]
|
fn witness_message_piece(
|
||||||
fn witness_message(
|
|
||||||
&self,
|
|
||||||
mut layouter: impl Layouter<pallas::Base>,
|
|
||||||
message: Vec<Option<bool>>,
|
|
||||||
) -> Result<Self::Message, Error> {
|
|
||||||
// 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<Vec<_>, _> = message
|
|
||||||
.chunks(piece_num_words * sinsemilla::K)
|
|
||||||
.enumerate()
|
|
||||||
.map(|(i, piece)| -> Result<Self::MessagePiece, Error> {
|
|
||||||
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<pallas::Base>,
|
|
||||||
message_piece: &[Option<bool>],
|
|
||||||
) -> Result<Self::MessagePiece, Error> {
|
|
||||||
// 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<bool>]| -> Option<pallas::Base> {
|
|
||||||
assert!(bits.len() <= pallas::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| 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(
|
|
||||||
&self,
|
&self,
|
||||||
mut layouter: impl Layouter<pallas::Base>,
|
mut layouter: impl Layouter<pallas::Base>,
|
||||||
field_elem: Option<pallas::Base>,
|
field_elem: Option<pallas::Base>,
|
||||||
|
|
|
@ -34,25 +34,17 @@ impl<F: FieldExt + PrimeFieldBits, const K: usize, const MAX_WORDS: usize> std::
|
||||||
/// cannot exceed the base field's `NUM_BITS`.
|
/// cannot exceed the base field's `NUM_BITS`.
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
pub struct MessagePiece<F: FieldExt, const K: usize> {
|
pub struct MessagePiece<F: FieldExt, const K: usize> {
|
||||||
cell: Cell,
|
cell_value: CellValue<F>,
|
||||||
field_elem: Option<F>,
|
|
||||||
/// The number of K-bit words in this message piece.
|
/// The number of K-bit words in this message piece.
|
||||||
num_words: usize,
|
num_words: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::from_over_into)]
|
|
||||||
impl<F: FieldExt + PrimeFieldBits, const K: usize> Into<CellValue<F>> for MessagePiece<F, K> {
|
|
||||||
fn into(self) -> CellValue<F> {
|
|
||||||
CellValue::new(self.cell(), self.field_elem())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<F: FieldExt + PrimeFieldBits, const K: usize> MessagePiece<F, K> {
|
impl<F: FieldExt + PrimeFieldBits, const K: usize> MessagePiece<F, K> {
|
||||||
pub fn new(cell: Cell, field_elem: Option<F>, num_words: usize) -> Self {
|
pub fn new(cell: Cell, field_elem: Option<F>, num_words: usize) -> Self {
|
||||||
assert!(num_words * K < F::NUM_BITS as usize);
|
assert!(num_words * K < F::NUM_BITS as usize);
|
||||||
|
let cell_value = CellValue::new(cell, field_elem);
|
||||||
Self {
|
Self {
|
||||||
cell,
|
cell_value,
|
||||||
field_elem,
|
|
||||||
num_words,
|
num_words,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -62,10 +54,14 @@ impl<F: FieldExt + PrimeFieldBits, const K: usize> MessagePiece<F, K> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn cell(&self) -> Cell {
|
pub fn cell(&self) -> Cell {
|
||||||
self.cell
|
self.cell_value.cell()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn field_elem(&self) -> Option<F> {
|
pub fn field_elem(&self) -> Option<F> {
|
||||||
self.field_elem
|
self.cell_value.value()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cell_value(&self) -> CellValue<F> {
|
||||||
|
self.cell_value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue