mirror of https://github.com/zcash/halo2.git
commit
4e7d4a0f64
|
@ -1,3 +1,7 @@
|
|||
# Gadgets
|
||||
|
||||
In this section we document the gadgets and chip designs provided by halo2.
|
||||
In this section we document some example gadgets and chip designs that are suitable for
|
||||
Halo 2.
|
||||
|
||||
> Neither these gadgets, nor their implementations, have been reviewed, and they should
|
||||
> not be used in production.
|
||||
|
|
|
@ -0,0 +1,163 @@
|
|||
use halo2::{
|
||||
arithmetic::FieldExt,
|
||||
circuit::Chip,
|
||||
circuit::{layouter, Layouter},
|
||||
pasta::EqAffine,
|
||||
plonk::{
|
||||
create_proof, keygen_pk, keygen_vk, verify_proof, Assignment, Circuit, ConstraintSystem,
|
||||
Error, VerifyingKey,
|
||||
},
|
||||
poly::commitment::Params,
|
||||
transcript::{Blake2bRead, Blake2bWrite},
|
||||
};
|
||||
|
||||
use std::{
|
||||
fs::File,
|
||||
io::{prelude::*, BufReader},
|
||||
path::Path,
|
||||
};
|
||||
|
||||
use criterion::{criterion_group, criterion_main, Criterion};
|
||||
|
||||
use crate::{BlockWord, Sha256, Table16Chip, Table16Config, BLOCK_SIZE};
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn bench(name: &str, k: u32, c: &mut Criterion) {
|
||||
struct MyCircuit {}
|
||||
|
||||
impl<F: FieldExt> Circuit<F> for MyCircuit {
|
||||
type Config = Table16Config;
|
||||
|
||||
fn configure(meta: &mut ConstraintSystem<F>) -> Table16Config {
|
||||
Table16Chip::configure(meta)
|
||||
}
|
||||
|
||||
fn synthesize(
|
||||
&self,
|
||||
cs: &mut impl Assignment<F>,
|
||||
config: Table16Config,
|
||||
) -> Result<(), Error> {
|
||||
let mut layouter = layouter::SingleChip::<Table16Chip<F>, _>::new(cs, config)?;
|
||||
Table16Chip::load(&mut layouter)?;
|
||||
|
||||
// Test vector: "abc"
|
||||
let test_input = [
|
||||
BlockWord::new(0b01100001011000100110001110000000),
|
||||
BlockWord::new(0),
|
||||
BlockWord::new(0),
|
||||
BlockWord::new(0),
|
||||
BlockWord::new(0),
|
||||
BlockWord::new(0),
|
||||
BlockWord::new(0),
|
||||
BlockWord::new(0),
|
||||
BlockWord::new(0),
|
||||
BlockWord::new(0),
|
||||
BlockWord::new(0),
|
||||
BlockWord::new(0),
|
||||
BlockWord::new(0),
|
||||
BlockWord::new(0),
|
||||
BlockWord::new(0),
|
||||
];
|
||||
|
||||
// Create a message of length 31 blocks
|
||||
let mut input = Vec::with_capacity(31 * BLOCK_SIZE);
|
||||
for _ in 0..31 {
|
||||
input.extend_from_slice(&test_input);
|
||||
}
|
||||
|
||||
Sha256::digest(layouter.namespace(|| "test vector"), &input)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize the polynomial commitment parameters
|
||||
let params_path = Path::new("./benches/sha256_assets/sha256_params");
|
||||
if File::open(¶ms_path).is_err() {
|
||||
let params: Params<EqAffine> = Params::new(k);
|
||||
let mut buf = Vec::new();
|
||||
|
||||
params.write(&mut buf).expect("Failed to write params");
|
||||
let mut file = File::create(¶ms_path).expect("Failed to create sha256_params");
|
||||
|
||||
file.write_all(&buf[..])
|
||||
.expect("Failed to write params to file");
|
||||
}
|
||||
|
||||
let params_fs = File::open(¶ms_path).expect("couldn't load sha256_params");
|
||||
let params: Params<EqAffine> =
|
||||
Params::read::<_>(&mut BufReader::new(params_fs)).expect("Failed to read params");
|
||||
|
||||
let empty_circuit: MyCircuit = MyCircuit {};
|
||||
|
||||
// Initialize the proving key
|
||||
let vk_path = Path::new("./benches/sha256_assets/sha256_vk");
|
||||
if File::open(&vk_path).is_err() {
|
||||
let vk = keygen_vk(¶ms, &empty_circuit).expect("keygen_vk should not fail");
|
||||
let mut buf = Vec::new();
|
||||
|
||||
vk.write(&mut buf).expect("Failed to write vk");
|
||||
let mut file = File::create(&vk_path).expect("Failed to create sha256_vk");
|
||||
|
||||
file.write_all(&buf[..])
|
||||
.expect("Failed to write vk to file");
|
||||
}
|
||||
|
||||
let vk_fs = File::open(&vk_path).expect("couldn't load sha256_params");
|
||||
let vk: VerifyingKey<EqAffine> =
|
||||
VerifyingKey::<EqAffine>::read::<_, MyCircuit>(&mut BufReader::new(vk_fs), ¶ms)
|
||||
.expect("Failed to read vk");
|
||||
|
||||
let pk = keygen_pk(¶ms, vk, &empty_circuit).expect("keygen_pk should not fail");
|
||||
|
||||
let circuit: MyCircuit = MyCircuit {};
|
||||
|
||||
// let prover_name = name.to_string() + "-prover";
|
||||
let verifier_name = name.to_string() + "-verifier";
|
||||
|
||||
// /// Benchmark proof creation
|
||||
// c.bench_function(&prover_name, |b| {
|
||||
// b.iter(|| {
|
||||
// let mut transcript = Blake2bWrite::init(Fq::one());
|
||||
// create_proof(¶ms, &pk, &circuit, &[], &mut transcript)
|
||||
// .expect("proof generation should not fail");
|
||||
// let proof: Vec<u8> = transcript.finalize();
|
||||
// });
|
||||
// });
|
||||
|
||||
// Create a proof
|
||||
let proof_path = Path::new("./benches/sha256_assets/sha256_proof");
|
||||
if File::open(&proof_path).is_err() {
|
||||
let mut transcript = Blake2bWrite::init(vec![]);
|
||||
create_proof(¶ms, &pk, &[circuit], &[], &mut transcript)
|
||||
.expect("proof generation should not fail");
|
||||
let proof: Vec<u8> = transcript.finalize();
|
||||
let mut file = File::create(&proof_path).expect("Failed to create sha256_proof");
|
||||
file.write_all(&proof[..]).expect("Failed to write proof");
|
||||
}
|
||||
|
||||
let mut proof_fs = File::open(&proof_path).expect("couldn't load sha256_proof");
|
||||
let mut proof = Vec::<u8>::new();
|
||||
proof_fs
|
||||
.read_to_end(&mut proof)
|
||||
.expect("Couldn't read proof");
|
||||
|
||||
c.bench_function(&verifier_name, |b| {
|
||||
b.iter(|| {
|
||||
let msm = params.empty_msm();
|
||||
let mut transcript = Blake2bRead::init(&proof[..]);
|
||||
let guard = verify_proof(¶ms, pk.get_vk(), msm, &[], &mut transcript).unwrap();
|
||||
let msm = guard.clone().use_challenges();
|
||||
assert!(msm.eval());
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn criterion_benchmark(c: &mut Criterion) {
|
||||
bench("sha256", 16, c);
|
||||
// bench("sha256", 20, c);
|
||||
}
|
||||
|
||||
criterion_group!(benches, criterion_benchmark);
|
||||
criterion_main!(benches);
|
|
@ -6,13 +6,18 @@ use std::cmp::min;
|
|||
use std::convert::TryInto;
|
||||
use std::fmt;
|
||||
|
||||
use crate::{
|
||||
use halo2::{
|
||||
circuit::{Chip, Layouter},
|
||||
plonk::Error,
|
||||
};
|
||||
|
||||
mod benches;
|
||||
mod table16;
|
||||
|
||||
pub use table16::{BlockWord, Table16Chip, Table16Config};
|
||||
|
||||
/// The size of a SHA-256 block, in 32-bit words.
|
||||
const BLOCK_SIZE: usize = 16;
|
||||
pub const BLOCK_SIZE: usize = 16;
|
||||
/// The size of a SHA-256 digest, in 32-bit words.
|
||||
const DIGEST_SIZE: usize = 8;
|
||||
|
||||
|
@ -153,3 +158,5 @@ impl<Sha256Chip: Sha256Instructions> Sha256<Sha256Chip> {
|
|||
hasher.finalize(layouter.namespace(|| "finalize"))
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
|
@ -0,0 +1,456 @@
|
|||
use std::marker::PhantomData;
|
||||
|
||||
use super::Sha256Instructions;
|
||||
use halo2::{
|
||||
arithmetic::FieldExt,
|
||||
circuit::{Cell, Chip, Layouter, Region},
|
||||
plonk::{Advice, Column, ConstraintSystem, Error, Permutation},
|
||||
};
|
||||
|
||||
mod compression;
|
||||
mod gates;
|
||||
mod message_schedule;
|
||||
mod spread_table;
|
||||
mod util;
|
||||
|
||||
use compression::*;
|
||||
use gates::*;
|
||||
use message_schedule::*;
|
||||
use spread_table::*;
|
||||
|
||||
const ROUNDS: usize = 64;
|
||||
const STATE: usize = 8;
|
||||
|
||||
#[allow(clippy::unreadable_literal)]
|
||||
pub(crate) const ROUND_CONSTANTS: [u32; ROUNDS] = [
|
||||
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
|
||||
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
|
||||
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
|
||||
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
|
||||
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
|
||||
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
|
||||
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
|
||||
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
|
||||
];
|
||||
|
||||
const IV: [u32; STATE] = [
|
||||
0x6a09_e667,
|
||||
0xbb67_ae85,
|
||||
0x3c6e_f372,
|
||||
0xa54f_f53a,
|
||||
0x510e_527f,
|
||||
0x9b05_688c,
|
||||
0x1f83_d9ab,
|
||||
0x5be0_cd19,
|
||||
];
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
/// A word in a `Table16` message block.
|
||||
pub struct BlockWord {
|
||||
var: (),
|
||||
value: Option<u32>,
|
||||
}
|
||||
|
||||
impl BlockWord {
|
||||
/// Create a new `BlockWord`.
|
||||
pub fn new(value: u32) -> Self {
|
||||
BlockWord {
|
||||
var: (),
|
||||
value: Some(value),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct CellValue16 {
|
||||
var: Cell,
|
||||
value: Option<u16>,
|
||||
}
|
||||
|
||||
impl CellValue16 {
|
||||
pub fn new(var: Cell, value: u16) -> Self {
|
||||
CellValue16 {
|
||||
var,
|
||||
value: Some(value),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct CellValue32 {
|
||||
var: Cell,
|
||||
value: Option<u32>,
|
||||
}
|
||||
|
||||
impl CellValue32 {
|
||||
pub fn new(var: Cell, value: u32) -> Self {
|
||||
CellValue32 {
|
||||
var,
|
||||
value: Some(value),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<CellValue32> for CellValue16 {
|
||||
fn into(self) -> CellValue32 {
|
||||
CellValue32::new(self.var, self.value.unwrap() as u32)
|
||||
}
|
||||
}
|
||||
|
||||
/// Configuration for a [`Table16Chip`].
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Table16Config {
|
||||
lookup_table: SpreadTable,
|
||||
message_schedule: MessageSchedule,
|
||||
compression: Compression,
|
||||
}
|
||||
|
||||
/// A chip that implements SHA-256 with a maximum lookup table size of $2^16$.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Table16Chip<F: FieldExt> {
|
||||
_marker: PhantomData<F>,
|
||||
}
|
||||
|
||||
impl<F: FieldExt> Table16Chip<F> {
|
||||
/// Configures this chip for use in a circuit.
|
||||
pub fn configure(meta: &mut ConstraintSystem<F>) -> Table16Config {
|
||||
// Columns required by this chip:
|
||||
// - Three advice columns to interact with the lookup table.
|
||||
let tag = meta.advice_column();
|
||||
let dense = meta.advice_column();
|
||||
let spread = meta.advice_column();
|
||||
|
||||
let message_schedule = meta.advice_column();
|
||||
let extras = [
|
||||
meta.advice_column(),
|
||||
meta.advice_column(),
|
||||
meta.advice_column(),
|
||||
meta.advice_column(),
|
||||
meta.advice_column(),
|
||||
meta.advice_column(),
|
||||
];
|
||||
|
||||
let (lookup_inputs, lookup_table) = SpreadTable::configure(meta, tag, dense, spread);
|
||||
|
||||
// Rename these here for ease of matching the gates to the specification.
|
||||
let _a_0 = lookup_inputs.tag;
|
||||
let a_1 = lookup_inputs.dense;
|
||||
let a_2 = lookup_inputs.spread;
|
||||
let a_3 = extras[0];
|
||||
let a_4 = extras[1];
|
||||
let a_5 = message_schedule;
|
||||
let a_6 = extras[2];
|
||||
let a_7 = extras[3];
|
||||
let a_8 = extras[4];
|
||||
let _a_9 = extras[5];
|
||||
|
||||
let perm = Permutation::new(
|
||||
meta,
|
||||
&[
|
||||
a_1.into(),
|
||||
a_2.into(),
|
||||
a_3.into(),
|
||||
a_4.into(),
|
||||
a_5.into(),
|
||||
a_6.into(),
|
||||
a_7.into(),
|
||||
a_8.into(),
|
||||
],
|
||||
);
|
||||
|
||||
let compression = Compression::configure(
|
||||
meta,
|
||||
lookup_inputs.clone(),
|
||||
message_schedule,
|
||||
extras,
|
||||
perm.clone(),
|
||||
);
|
||||
|
||||
let message_schedule =
|
||||
MessageSchedule::configure(meta, lookup_inputs, message_schedule, extras, perm);
|
||||
|
||||
Table16Config {
|
||||
lookup_table,
|
||||
message_schedule,
|
||||
compression,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: FieldExt> Chip for Table16Chip<F> {
|
||||
type Field = F;
|
||||
type Config = Table16Config;
|
||||
type Loaded = ();
|
||||
|
||||
fn load(layouter: &mut impl Layouter<Self>) -> Result<(), Error> {
|
||||
let table = layouter.config().lookup_table.clone();
|
||||
table.load(layouter)
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: FieldExt> Sha256Instructions for Table16Chip<F> {
|
||||
type State = State;
|
||||
type BlockWord = BlockWord;
|
||||
|
||||
fn zero() -> Self::BlockWord {
|
||||
BlockWord::new(0)
|
||||
}
|
||||
|
||||
fn initialization_vector(layouter: &mut impl Layouter<Self>) -> Result<State, Error> {
|
||||
let config = layouter.config().clone();
|
||||
config.compression.initialize_with_iv(layouter, IV)
|
||||
}
|
||||
|
||||
fn initialization(
|
||||
layouter: &mut impl Layouter<Table16Chip<F>>,
|
||||
init_state: &Self::State,
|
||||
) -> Result<Self::State, Error> {
|
||||
let config = layouter.config().clone();
|
||||
config
|
||||
.compression
|
||||
.initialize_with_state(layouter, init_state.clone())
|
||||
}
|
||||
|
||||
// Given an initialized state and an input message block, compress the
|
||||
// message block and return the final state.
|
||||
fn compress(
|
||||
layouter: &mut impl Layouter<Self>,
|
||||
initialized_state: &Self::State,
|
||||
input: [Self::BlockWord; super::BLOCK_SIZE],
|
||||
) -> Result<Self::State, Error> {
|
||||
let config = layouter.config().clone();
|
||||
let (_, w_halves) = config.message_schedule.process(layouter, input)?;
|
||||
|
||||
config
|
||||
.compression
|
||||
.compress(layouter, initialized_state.clone(), w_halves)
|
||||
}
|
||||
|
||||
fn digest(
|
||||
layouter: &mut impl Layouter<Self>,
|
||||
state: &Self::State,
|
||||
) -> Result<[Self::BlockWord; super::DIGEST_SIZE], Error> {
|
||||
// Copy the dense forms of the state variable chunks down to this gate.
|
||||
// Reconstruct the 32-bit dense words.
|
||||
let config = layouter.config().clone();
|
||||
config.compression.digest(layouter, state.clone())
|
||||
}
|
||||
}
|
||||
|
||||
/// Common assignment patterns used by Table16 regions.
|
||||
trait Table16Assignment<F: FieldExt> {
|
||||
// Assign cells for general spread computation used in sigma, ch, ch_neg, maj gates
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
#[allow(clippy::type_complexity)]
|
||||
fn assign_spread_outputs(
|
||||
&self,
|
||||
region: &mut Region<'_, Table16Chip<F>>,
|
||||
lookup: &SpreadInputs,
|
||||
a_3: Column<Advice>,
|
||||
perm: &Permutation,
|
||||
row: usize,
|
||||
r_0_even: u16,
|
||||
r_0_odd: u16,
|
||||
r_1_even: u16,
|
||||
r_1_odd: u16,
|
||||
) -> Result<((CellValue16, CellValue16), (CellValue16, CellValue16)), Error> {
|
||||
// Lookup R_0^{even}, R_0^{odd}, R_1^{even}, R_1^{odd}
|
||||
let r_0_even = SpreadVar::with_lookup(region, lookup, row - 1, SpreadWord::new(r_0_even))?;
|
||||
let r_0_odd = SpreadVar::with_lookup(region, lookup, row, SpreadWord::new(r_0_odd))?;
|
||||
let r_1_even = SpreadVar::with_lookup(region, lookup, row + 1, SpreadWord::new(r_1_even))?;
|
||||
let r_1_odd = SpreadVar::with_lookup(region, lookup, row + 2, SpreadWord::new(r_1_odd))?;
|
||||
|
||||
// Assign and copy R_1^{odd}
|
||||
let r_1_odd_spread = region.assign_advice(
|
||||
|| "Assign and copy R_1^{odd}",
|
||||
a_3,
|
||||
row,
|
||||
|| Ok(F::from_u64(r_1_odd.spread.value.unwrap().into())),
|
||||
)?;
|
||||
region.constrain_equal(perm, r_1_odd.spread.var, r_1_odd_spread)?;
|
||||
|
||||
Ok((
|
||||
(
|
||||
CellValue16::new(r_0_even.dense.var, r_0_even.dense.value.unwrap()),
|
||||
CellValue16::new(r_1_even.dense.var, r_1_even.dense.value.unwrap()),
|
||||
),
|
||||
(
|
||||
CellValue16::new(r_0_odd.dense.var, r_0_odd.dense.value.unwrap()),
|
||||
CellValue16::new(r_1_odd.dense.var, r_1_odd.dense.value.unwrap()),
|
||||
),
|
||||
))
|
||||
}
|
||||
|
||||
// Assign outputs of sigma gates
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn assign_sigma_outputs(
|
||||
&self,
|
||||
region: &mut Region<'_, Table16Chip<F>>,
|
||||
lookup: &SpreadInputs,
|
||||
a_3: Column<Advice>,
|
||||
perm: &Permutation,
|
||||
row: usize,
|
||||
r_0_even: u16,
|
||||
r_0_odd: u16,
|
||||
r_1_even: u16,
|
||||
r_1_odd: u16,
|
||||
) -> Result<(CellValue16, CellValue16), Error> {
|
||||
let (even, _odd) = self.assign_spread_outputs(
|
||||
region, lookup, a_3, perm, row, r_0_even, r_0_odd, r_1_even, r_1_odd,
|
||||
)?;
|
||||
|
||||
Ok(even)
|
||||
}
|
||||
|
||||
// Assign a cell the same value as another cell and set up a copy constraint between them
|
||||
fn assign_and_constrain<A, AR>(
|
||||
&self,
|
||||
region: &mut Region<'_, Table16Chip<F>>,
|
||||
annotation: A,
|
||||
column: Column<Advice>,
|
||||
row: usize,
|
||||
copy: &CellValue32,
|
||||
perm: &Permutation,
|
||||
) -> Result<Cell, Error>
|
||||
where
|
||||
A: Fn() -> AR,
|
||||
AR: Into<String>,
|
||||
{
|
||||
let cell = region.assign_advice(annotation, column, row, || {
|
||||
Ok(F::from_u64(copy.value.unwrap() as u64))
|
||||
})?;
|
||||
region.constrain_equal(perm, cell, copy.var)?;
|
||||
Ok(cell)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[cfg(feature = "dev-graph")]
|
||||
use halo2::{
|
||||
arithmetic::FieldExt,
|
||||
circuit::Chip,
|
||||
circuit::{layouter, Layouter},
|
||||
gadget::sha256::{BlockWord, Sha256, Table16Chip, Table16Config, BLOCK_SIZE},
|
||||
pasta::Fq,
|
||||
plonk::{Assignment, Circuit, ConstraintSystem, Error},
|
||||
};
|
||||
|
||||
#[cfg(feature = "dev-graph")]
|
||||
#[test]
|
||||
fn print_sha256_circuit() {
|
||||
struct MyCircuit {}
|
||||
|
||||
impl<F: FieldExt> Circuit<F> for MyCircuit {
|
||||
type Config = Table16Config;
|
||||
|
||||
fn configure(meta: &mut ConstraintSystem<F>) -> Table16Config {
|
||||
Table16Chip::configure(meta)
|
||||
}
|
||||
|
||||
fn synthesize(
|
||||
&self,
|
||||
cs: &mut impl Assignment<F>,
|
||||
config: Table16Config,
|
||||
) -> Result<(), Error> {
|
||||
let mut layouter = layouter::SingleChip::<Table16Chip<F>, _>::new(cs, config)?;
|
||||
Table16Chip::load(&mut layouter)?;
|
||||
|
||||
// Test vector: "abc"
|
||||
let test_input = [
|
||||
BlockWord::new(0b01100001011000100110001110000000),
|
||||
BlockWord::new(0),
|
||||
BlockWord::new(0),
|
||||
BlockWord::new(0),
|
||||
BlockWord::new(0),
|
||||
BlockWord::new(0),
|
||||
BlockWord::new(0),
|
||||
BlockWord::new(0),
|
||||
BlockWord::new(0),
|
||||
BlockWord::new(0),
|
||||
BlockWord::new(0),
|
||||
BlockWord::new(0),
|
||||
BlockWord::new(0),
|
||||
BlockWord::new(0),
|
||||
BlockWord::new(0),
|
||||
];
|
||||
|
||||
// Create a message of length 31 blocks
|
||||
let mut input = Vec::with_capacity(31 * BLOCK_SIZE);
|
||||
for _ in 0..31 {
|
||||
input.extend_from_slice(&test_input);
|
||||
}
|
||||
|
||||
Sha256::digest(layouter.namespace(|| "'abc' * 31"), &input)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
let circuit: MyCircuit = MyCircuit {};
|
||||
eprintln!("{}", crate::dev::circuit_dot_graph::<Fq, _>(&circuit));
|
||||
}
|
||||
|
||||
#[cfg(feature = "dev-graph")]
|
||||
#[test]
|
||||
fn print_table16_chip() {
|
||||
use plotters::prelude::*;
|
||||
struct MyCircuit {}
|
||||
|
||||
impl<F: FieldExt> Circuit<F> for MyCircuit {
|
||||
type Config = Table16Config;
|
||||
|
||||
fn configure(meta: &mut ConstraintSystem<F>) -> Table16Config {
|
||||
Table16Chip::configure(meta)
|
||||
}
|
||||
|
||||
fn synthesize(
|
||||
&self,
|
||||
cs: &mut impl Assignment<F>,
|
||||
config: Table16Config,
|
||||
) -> Result<(), Error> {
|
||||
let mut layouter = layouter::SingleChip::<Table16Chip<F>, _>::new(cs, config)?;
|
||||
Table16Chip::load(&mut layouter)?;
|
||||
|
||||
// Test vector: "abc"
|
||||
let test_input = [
|
||||
BlockWord::new(0b01100001011000100110001110000000),
|
||||
BlockWord::new(0),
|
||||
BlockWord::new(0),
|
||||
BlockWord::new(0),
|
||||
BlockWord::new(0),
|
||||
BlockWord::new(0),
|
||||
BlockWord::new(0),
|
||||
BlockWord::new(0),
|
||||
BlockWord::new(0),
|
||||
BlockWord::new(0),
|
||||
BlockWord::new(0),
|
||||
BlockWord::new(0),
|
||||
BlockWord::new(0),
|
||||
BlockWord::new(0),
|
||||
BlockWord::new(0),
|
||||
];
|
||||
|
||||
// Create a message of length 2 blocks
|
||||
let mut input = Vec::with_capacity(2 * BLOCK_SIZE);
|
||||
for _ in 0..2 {
|
||||
input.extend_from_slice(&test_input);
|
||||
}
|
||||
|
||||
Sha256::digest(layouter.namespace(|| "'abc' * 2"), &input)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
let root =
|
||||
SVGBackend::new("sha-256-table16-chip-layout.svg", (1024, 20480)).into_drawing_area();
|
||||
root.fill(&WHITE).unwrap();
|
||||
let root = root
|
||||
.titled("16-bit Table SHA-256 Chip", ("sans-serif", 60))
|
||||
.unwrap();
|
||||
|
||||
let circuit = MyCircuit {};
|
||||
crate::dev::circuit_layout::<Fq, _, _>(&circuit, &root).unwrap();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,932 @@
|
|||
use super::{
|
||||
super::DIGEST_SIZE, BlockWord, CellValue16, CellValue32, SpreadInputs, SpreadVar,
|
||||
Table16Assignment, Table16Chip, ROUNDS, STATE,
|
||||
};
|
||||
use halo2::{
|
||||
arithmetic::FieldExt,
|
||||
circuit::Layouter,
|
||||
plonk::{Advice, Column, ConstraintSystem, Error, Fixed, Permutation},
|
||||
poly::Rotation,
|
||||
};
|
||||
|
||||
mod compression_gates;
|
||||
mod compression_util;
|
||||
mod subregion_digest;
|
||||
mod subregion_initial;
|
||||
mod subregion_main;
|
||||
|
||||
use compression_gates::CompressionGate;
|
||||
|
||||
/// A variable that represents the `[A,B,C,D]` words of the SHA-256 internal state.
|
||||
///
|
||||
/// The structure of this variable is influenced by the following factors:
|
||||
/// - In `Σ_0(A)` we need `A` to be split into pieces `(a,b,c,d)` of lengths `(2,11,9,10)`
|
||||
/// bits respectively (counting from the little end), as well as their spread forms.
|
||||
/// - `Maj(A,B,C)` requires having the bits of each input in spread form. For `A` we can
|
||||
/// reuse the pieces from `Σ_0(A)`. Since `B` and `C` are assigned from `A` and `B`
|
||||
/// respectively in each round, we therefore also have the same pieces in earlier rows.
|
||||
/// We align the columns to make it efficient to copy-constrain these forms where they
|
||||
/// are needed.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct AbcdVar {
|
||||
idx: i32,
|
||||
val: u32,
|
||||
a: SpreadVar,
|
||||
b: SpreadVar,
|
||||
c_lo: SpreadVar,
|
||||
c_mid: SpreadVar,
|
||||
c_hi: SpreadVar,
|
||||
d: SpreadVar,
|
||||
}
|
||||
|
||||
/// A variable that represents the `[E,F,G,H]` words of the SHA-256 internal state.
|
||||
///
|
||||
/// The structure of this variable is influenced by the following factors:
|
||||
/// - In `Σ_1(E)` we need `E` to be split into pieces `(a,b,c,d)` of lengths `(6,5,14,7)`
|
||||
/// bits respectively (counting from the little end), as well as their spread forms.
|
||||
/// - `Ch(E,F,G)` requires having the bits of each input in spread form. For `E` we can
|
||||
/// reuse the pieces from `Σ_1(E)`. Since `F` and `G` are assigned from `E` and `F`
|
||||
/// respectively in each round, we therefore also have the same pieces in earlier rows.
|
||||
/// We align the columns to make it efficient to copy-constrain these forms where they
|
||||
/// are needed.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct EfghVar {
|
||||
idx: i32,
|
||||
val: u32,
|
||||
a_lo: SpreadVar,
|
||||
a_hi: SpreadVar,
|
||||
b_lo: SpreadVar,
|
||||
b_hi: SpreadVar,
|
||||
c: SpreadVar,
|
||||
d: SpreadVar,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct RoundWordDense {
|
||||
dense_halves: (CellValue16, CellValue16),
|
||||
}
|
||||
|
||||
impl RoundWordDense {
|
||||
pub fn new(dense_halves: (CellValue16, CellValue16)) -> Self {
|
||||
RoundWordDense { dense_halves }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct RoundWordSpread {
|
||||
dense_halves: (CellValue16, CellValue16),
|
||||
spread_halves: (CellValue32, CellValue32),
|
||||
}
|
||||
|
||||
impl RoundWordSpread {
|
||||
pub fn new(
|
||||
dense_halves: (CellValue16, CellValue16),
|
||||
spread_halves: (CellValue32, CellValue32),
|
||||
) -> Self {
|
||||
RoundWordSpread {
|
||||
dense_halves,
|
||||
spread_halves,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<RoundWordDense> for RoundWordSpread {
|
||||
fn into(self) -> RoundWordDense {
|
||||
RoundWordDense::new(self.dense_halves)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct RoundWordA {
|
||||
pieces: Option<AbcdVar>,
|
||||
dense_halves: (CellValue16, CellValue16),
|
||||
spread_halves: Option<(CellValue32, CellValue32)>,
|
||||
}
|
||||
|
||||
impl RoundWordA {
|
||||
pub fn new(
|
||||
pieces: AbcdVar,
|
||||
dense_halves: (CellValue16, CellValue16),
|
||||
spread_halves: (CellValue32, CellValue32),
|
||||
) -> Self {
|
||||
RoundWordA {
|
||||
pieces: Some(pieces),
|
||||
dense_halves,
|
||||
spread_halves: Some(spread_halves),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_dense(dense_halves: (CellValue16, CellValue16)) -> Self {
|
||||
RoundWordA {
|
||||
pieces: None,
|
||||
dense_halves,
|
||||
spread_halves: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<RoundWordSpread> for RoundWordA {
|
||||
fn into(self) -> RoundWordSpread {
|
||||
RoundWordSpread::new(self.dense_halves, self.spread_halves.unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct RoundWordE {
|
||||
pieces: Option<EfghVar>,
|
||||
dense_halves: (CellValue16, CellValue16),
|
||||
spread_halves: Option<(CellValue32, CellValue32)>,
|
||||
}
|
||||
|
||||
impl RoundWordE {
|
||||
pub fn new(
|
||||
pieces: EfghVar,
|
||||
dense_halves: (CellValue16, CellValue16),
|
||||
spread_halves: (CellValue32, CellValue32),
|
||||
) -> Self {
|
||||
RoundWordE {
|
||||
pieces: Some(pieces),
|
||||
dense_halves,
|
||||
spread_halves: Some(spread_halves),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_dense(dense_halves: (CellValue16, CellValue16)) -> Self {
|
||||
RoundWordE {
|
||||
pieces: None,
|
||||
dense_halves,
|
||||
spread_halves: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<RoundWordSpread> for RoundWordE {
|
||||
fn into(self) -> RoundWordSpread {
|
||||
RoundWordSpread::new(self.dense_halves, self.spread_halves.unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
/// The internal state for SHA-256.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct State {
|
||||
a: Option<StateWord>,
|
||||
b: Option<StateWord>,
|
||||
c: Option<StateWord>,
|
||||
d: Option<StateWord>,
|
||||
e: Option<StateWord>,
|
||||
f: Option<StateWord>,
|
||||
g: Option<StateWord>,
|
||||
h: Option<StateWord>,
|
||||
}
|
||||
|
||||
impl State {
|
||||
#[allow(clippy::many_single_char_names)]
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn new(
|
||||
a: StateWord,
|
||||
b: StateWord,
|
||||
c: StateWord,
|
||||
d: StateWord,
|
||||
e: StateWord,
|
||||
f: StateWord,
|
||||
g: StateWord,
|
||||
h: StateWord,
|
||||
) -> Self {
|
||||
State {
|
||||
a: Some(a),
|
||||
b: Some(b),
|
||||
c: Some(c),
|
||||
d: Some(d),
|
||||
e: Some(e),
|
||||
f: Some(f),
|
||||
g: Some(g),
|
||||
h: Some(h),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn empty_state() -> Self {
|
||||
State {
|
||||
a: None,
|
||||
b: None,
|
||||
c: None,
|
||||
d: None,
|
||||
e: None,
|
||||
f: None,
|
||||
g: None,
|
||||
h: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum StateWord {
|
||||
A(RoundWordA),
|
||||
B(RoundWordSpread),
|
||||
C(RoundWordSpread),
|
||||
D(RoundWordDense),
|
||||
E(RoundWordE),
|
||||
F(RoundWordSpread),
|
||||
G(RoundWordSpread),
|
||||
H(RoundWordDense),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(super) struct Compression {
|
||||
lookup: SpreadInputs,
|
||||
message_schedule: Column<Advice>,
|
||||
extras: [Column<Advice>; 6],
|
||||
|
||||
s_ch: Column<Fixed>,
|
||||
s_ch_neg: Column<Fixed>,
|
||||
s_maj: Column<Fixed>,
|
||||
s_h_prime: Column<Fixed>,
|
||||
s_a_new: Column<Fixed>,
|
||||
s_e_new: Column<Fixed>,
|
||||
|
||||
s_upper_sigma_0: Column<Fixed>,
|
||||
s_upper_sigma_1: Column<Fixed>,
|
||||
|
||||
// Decomposition gate for AbcdVar
|
||||
s_decompose_abcd: Column<Fixed>,
|
||||
// Decomposition gate for EfghVar
|
||||
s_decompose_efgh: Column<Fixed>,
|
||||
|
||||
s_digest: Column<Fixed>,
|
||||
|
||||
perm: Permutation,
|
||||
}
|
||||
|
||||
impl<F: FieldExt> Table16Assignment<F> for Compression {}
|
||||
|
||||
impl Compression {
|
||||
pub(super) fn configure<F: FieldExt>(
|
||||
meta: &mut ConstraintSystem<F>,
|
||||
lookup: SpreadInputs,
|
||||
message_schedule: Column<Advice>,
|
||||
extras: [Column<Advice>; 6],
|
||||
perm: Permutation,
|
||||
) -> Self {
|
||||
let s_ch = meta.fixed_column();
|
||||
let s_ch_neg = meta.fixed_column();
|
||||
let s_maj = meta.fixed_column();
|
||||
let s_h_prime = meta.fixed_column();
|
||||
let s_a_new = meta.fixed_column();
|
||||
let s_e_new = meta.fixed_column();
|
||||
|
||||
let s_upper_sigma_0 = meta.fixed_column();
|
||||
let s_upper_sigma_1 = meta.fixed_column();
|
||||
|
||||
// Decomposition gate for AbcdVar
|
||||
let s_decompose_abcd = meta.fixed_column();
|
||||
// Decomposition gate for EfghVar
|
||||
let s_decompose_efgh = meta.fixed_column();
|
||||
|
||||
let s_digest = meta.fixed_column();
|
||||
|
||||
// Rename these here for ease of matching the gates to the specification.
|
||||
let a_0 = lookup.tag;
|
||||
let a_1 = lookup.dense;
|
||||
let a_2 = lookup.spread;
|
||||
let a_3 = extras[0];
|
||||
let a_4 = extras[1];
|
||||
let a_5 = message_schedule;
|
||||
let a_6 = extras[2];
|
||||
let a_7 = extras[3];
|
||||
let a_8 = extras[4];
|
||||
let a_9 = extras[5];
|
||||
|
||||
// Decompose `A,B,C,D` words into (2, 11, 9, 10)-bit chunks.
|
||||
// `c` is split into (3, 3, 3)-bit c_lo, c_mid, c_hi.
|
||||
meta.create_gate("decompose ABCD", |meta| {
|
||||
let s_decompose_abcd = meta.query_fixed(s_decompose_abcd, Rotation::cur());
|
||||
let a = meta.query_advice(a_3, Rotation::next()); // 2-bit chunk
|
||||
let spread_a = meta.query_advice(a_4, Rotation::next());
|
||||
let b = meta.query_advice(a_1, Rotation::cur()); // 11-bit chunk
|
||||
let spread_b = meta.query_advice(a_2, Rotation::cur());
|
||||
let tag_b = meta.query_advice(a_0, Rotation::cur());
|
||||
let c_lo = meta.query_advice(a_3, Rotation::cur()); // 3-bit chunk
|
||||
let spread_c_lo = meta.query_advice(a_4, Rotation::cur());
|
||||
let c_mid = meta.query_advice(a_5, Rotation::cur()); // 3-bit chunk
|
||||
let spread_c_mid = meta.query_advice(a_6, Rotation::cur());
|
||||
let c_hi = meta.query_advice(a_5, Rotation::next()); // 3-bit chunk
|
||||
let spread_c_hi = meta.query_advice(a_6, Rotation::next());
|
||||
let d = meta.query_advice(a_1, Rotation::next()); // 7-bit chunk
|
||||
let spread_d = meta.query_advice(a_2, Rotation::next());
|
||||
let tag_d = meta.query_advice(a_0, Rotation::next());
|
||||
let word_lo = meta.query_advice(a_7, Rotation::cur());
|
||||
let spread_word_lo = meta.query_advice(a_8, Rotation::cur());
|
||||
let word_hi = meta.query_advice(a_7, Rotation::next());
|
||||
let spread_word_hi = meta.query_advice(a_8, Rotation::next());
|
||||
|
||||
CompressionGate::s_decompose_abcd(
|
||||
s_decompose_abcd,
|
||||
a,
|
||||
spread_a,
|
||||
b,
|
||||
spread_b,
|
||||
tag_b,
|
||||
c_lo,
|
||||
spread_c_lo,
|
||||
c_mid,
|
||||
spread_c_mid,
|
||||
c_hi,
|
||||
spread_c_hi,
|
||||
d,
|
||||
spread_d,
|
||||
tag_d,
|
||||
word_lo,
|
||||
spread_word_lo,
|
||||
word_hi,
|
||||
spread_word_hi,
|
||||
)
|
||||
.0
|
||||
});
|
||||
|
||||
// Decompose `E,F,G,H` words into (6, 5, 14, 7)-bit chunks.
|
||||
// `a` is split into (3, 3)-bit a_lo, a_hi
|
||||
// `b` is split into (2, 3)-bit b_lo, b_hi
|
||||
meta.create_gate("Decompose EFGH", |meta| {
|
||||
let s_decompose_efgh = meta.query_fixed(s_decompose_efgh, Rotation::cur());
|
||||
let a_lo = meta.query_advice(a_3, Rotation::next()); // 3-bit chunk
|
||||
let spread_a_lo = meta.query_advice(a_4, Rotation::next());
|
||||
let a_hi = meta.query_advice(a_5, Rotation::next()); // 3-bit chunk
|
||||
let spread_a_hi = meta.query_advice(a_6, Rotation::next());
|
||||
let b_lo = meta.query_advice(a_3, Rotation::cur()); // 2-bit chunk
|
||||
let spread_b_lo = meta.query_advice(a_4, Rotation::cur());
|
||||
let b_hi = meta.query_advice(a_5, Rotation::cur()); // 3-bit chunk
|
||||
let spread_b_hi = meta.query_advice(a_6, Rotation::cur());
|
||||
let c = meta.query_advice(a_1, Rotation::next()); // 14-bit chunk
|
||||
let spread_c = meta.query_advice(a_2, Rotation::next());
|
||||
let tag_c = meta.query_advice(a_0, Rotation::next());
|
||||
let d = meta.query_advice(a_1, Rotation::cur()); // 7-bit chunk
|
||||
let spread_d = meta.query_advice(a_2, Rotation::cur());
|
||||
let tag_d = meta.query_advice(a_0, Rotation::cur());
|
||||
let word_lo = meta.query_advice(a_7, Rotation::cur());
|
||||
let spread_word_lo = meta.query_advice(a_8, Rotation::cur());
|
||||
let word_hi = meta.query_advice(a_7, Rotation::next());
|
||||
let spread_word_hi = meta.query_advice(a_8, Rotation::next());
|
||||
|
||||
CompressionGate::s_decompose_efgh(
|
||||
s_decompose_efgh,
|
||||
a_lo,
|
||||
spread_a_lo,
|
||||
a_hi,
|
||||
spread_a_hi,
|
||||
b_lo,
|
||||
spread_b_lo,
|
||||
b_hi,
|
||||
spread_b_hi,
|
||||
c,
|
||||
spread_c,
|
||||
tag_c,
|
||||
d,
|
||||
spread_d,
|
||||
tag_d,
|
||||
word_lo,
|
||||
spread_word_lo,
|
||||
word_hi,
|
||||
spread_word_hi,
|
||||
)
|
||||
.0
|
||||
});
|
||||
|
||||
// s_upper_sigma_0 on abcd words
|
||||
// (2, 11, 9, 10)-bit chunks
|
||||
meta.create_gate("s_upper_sigma_0", |meta| {
|
||||
let s_upper_sigma_0 = meta.query_fixed(s_upper_sigma_0, Rotation::cur());
|
||||
let spread_r0_even = meta.query_advice(a_2, Rotation::prev());
|
||||
let spread_r0_odd = meta.query_advice(a_2, Rotation::cur());
|
||||
let spread_r1_even = meta.query_advice(a_2, Rotation::next());
|
||||
let spread_r1_odd = meta.query_advice(a_3, Rotation::cur());
|
||||
|
||||
let spread_a = meta.query_advice(a_3, Rotation::next());
|
||||
let spread_b = meta.query_advice(a_5, Rotation::cur());
|
||||
let spread_c_lo = meta.query_advice(a_3, Rotation::prev());
|
||||
let spread_c_mid = meta.query_advice(a_4, Rotation::prev());
|
||||
let spread_c_hi = meta.query_advice(a_4, Rotation::next());
|
||||
let spread_d = meta.query_advice(a_4, Rotation::cur());
|
||||
|
||||
CompressionGate::s_upper_sigma_0(
|
||||
s_upper_sigma_0,
|
||||
spread_r0_even,
|
||||
spread_r0_odd,
|
||||
spread_r1_even,
|
||||
spread_r1_odd,
|
||||
spread_a,
|
||||
spread_b,
|
||||
spread_c_lo,
|
||||
spread_c_mid,
|
||||
spread_c_hi,
|
||||
spread_d,
|
||||
)
|
||||
.0
|
||||
});
|
||||
|
||||
// s_upper_sigma_1 on efgh words
|
||||
// (6, 5, 14, 7)-bit chunks
|
||||
meta.create_gate("s_upper_sigma_1", |meta| {
|
||||
let s_upper_sigma_1 = meta.query_fixed(s_upper_sigma_1, Rotation::cur());
|
||||
let spread_r0_even = meta.query_advice(a_2, Rotation::prev());
|
||||
let spread_r0_odd = meta.query_advice(a_2, Rotation::cur());
|
||||
let spread_r1_even = meta.query_advice(a_2, Rotation::next());
|
||||
let spread_r1_odd = meta.query_advice(a_3, Rotation::cur());
|
||||
let spread_a_lo = meta.query_advice(a_3, Rotation::next());
|
||||
let spread_a_hi = meta.query_advice(a_4, Rotation::next());
|
||||
let spread_b_lo = meta.query_advice(a_3, Rotation::prev());
|
||||
let spread_b_hi = meta.query_advice(a_4, Rotation::prev());
|
||||
let spread_c = meta.query_advice(a_5, Rotation::cur());
|
||||
let spread_d = meta.query_advice(a_4, Rotation::cur());
|
||||
|
||||
CompressionGate::s_upper_sigma_1(
|
||||
s_upper_sigma_1,
|
||||
spread_r0_even,
|
||||
spread_r0_odd,
|
||||
spread_r1_even,
|
||||
spread_r1_odd,
|
||||
spread_a_lo,
|
||||
spread_a_hi,
|
||||
spread_b_lo,
|
||||
spread_b_hi,
|
||||
spread_c,
|
||||
spread_d,
|
||||
)
|
||||
.0
|
||||
});
|
||||
|
||||
// s_ch on efgh words
|
||||
// First part of choice gate on (E, F, G), E ∧ F
|
||||
meta.create_gate("s_ch", |meta| {
|
||||
let s_ch = meta.query_fixed(s_ch, Rotation::cur());
|
||||
let spread_p0_even = meta.query_advice(a_2, Rotation::prev());
|
||||
let spread_p0_odd = meta.query_advice(a_2, Rotation::cur());
|
||||
let spread_p1_even = meta.query_advice(a_2, Rotation::next());
|
||||
let spread_p1_odd = meta.query_advice(a_3, Rotation::cur());
|
||||
let spread_e_lo = meta.query_advice(a_3, Rotation::prev());
|
||||
let spread_e_hi = meta.query_advice(a_4, Rotation::prev());
|
||||
let spread_f_lo = meta.query_advice(a_3, Rotation::next());
|
||||
let spread_f_hi = meta.query_advice(a_4, Rotation::next());
|
||||
|
||||
CompressionGate::s_ch(
|
||||
s_ch,
|
||||
spread_p0_even,
|
||||
spread_p0_odd,
|
||||
spread_p1_even,
|
||||
spread_p1_odd,
|
||||
spread_e_lo,
|
||||
spread_e_hi,
|
||||
spread_f_lo,
|
||||
spread_f_hi,
|
||||
)
|
||||
.0
|
||||
});
|
||||
|
||||
// s_ch_neg on efgh words
|
||||
// Second part of Choice gate on (E, F, G), ¬E ∧ G
|
||||
meta.create_gate("s_ch_neg", |meta| {
|
||||
let s_ch_neg = meta.query_fixed(s_ch_neg, Rotation::cur());
|
||||
let spread_q0_even = meta.query_advice(a_2, Rotation::prev());
|
||||
let spread_q0_odd = meta.query_advice(a_2, Rotation::cur());
|
||||
let spread_q1_even = meta.query_advice(a_2, Rotation::next());
|
||||
let spread_q1_odd = meta.query_advice(a_3, Rotation::cur());
|
||||
let spread_e_lo = meta.query_advice(a_5, Rotation::prev());
|
||||
let spread_e_hi = meta.query_advice(a_5, Rotation::cur());
|
||||
let spread_e_neg_lo = meta.query_advice(a_3, Rotation::prev());
|
||||
let spread_e_neg_hi = meta.query_advice(a_4, Rotation::prev());
|
||||
let spread_g_lo = meta.query_advice(a_3, Rotation::next());
|
||||
let spread_g_hi = meta.query_advice(a_4, Rotation::next());
|
||||
|
||||
CompressionGate::s_ch_neg(
|
||||
s_ch_neg,
|
||||
spread_q0_even,
|
||||
spread_q0_odd,
|
||||
spread_q1_even,
|
||||
spread_q1_odd,
|
||||
spread_e_lo,
|
||||
spread_e_hi,
|
||||
spread_e_neg_lo,
|
||||
spread_e_neg_hi,
|
||||
spread_g_lo,
|
||||
spread_g_hi,
|
||||
)
|
||||
.0
|
||||
});
|
||||
|
||||
// s_maj on abcd words
|
||||
meta.create_gate("s_maj", |meta| {
|
||||
let s_maj = meta.query_fixed(s_maj, Rotation::cur());
|
||||
let spread_m0_even = meta.query_advice(a_2, Rotation::prev());
|
||||
let spread_m0_odd = meta.query_advice(a_2, Rotation::cur());
|
||||
let spread_m1_even = meta.query_advice(a_2, Rotation::next());
|
||||
let spread_m1_odd = meta.query_advice(a_3, Rotation::cur());
|
||||
let spread_a_lo = meta.query_advice(a_4, Rotation::prev());
|
||||
let spread_a_hi = meta.query_advice(a_5, Rotation::prev());
|
||||
let spread_b_lo = meta.query_advice(a_4, Rotation::cur());
|
||||
let spread_b_hi = meta.query_advice(a_5, Rotation::cur());
|
||||
let spread_c_lo = meta.query_advice(a_4, Rotation::next());
|
||||
let spread_c_hi = meta.query_advice(a_5, Rotation::next());
|
||||
|
||||
CompressionGate::s_maj(
|
||||
s_maj,
|
||||
spread_m0_even,
|
||||
spread_m0_odd,
|
||||
spread_m1_even,
|
||||
spread_m1_odd,
|
||||
spread_a_lo,
|
||||
spread_a_hi,
|
||||
spread_b_lo,
|
||||
spread_b_hi,
|
||||
spread_c_lo,
|
||||
spread_c_hi,
|
||||
)
|
||||
.0
|
||||
});
|
||||
|
||||
// s_h_prime to compute H' = H + Ch(E, F, G) + s_upper_sigma_1(E) + K + W
|
||||
meta.create_gate("s_h_prime", |meta| {
|
||||
let s_h_prime = meta.query_fixed(s_h_prime, Rotation::cur());
|
||||
let h_prime_lo = meta.query_advice(a_7, Rotation::next());
|
||||
let h_prime_hi = meta.query_advice(a_8, Rotation::next());
|
||||
let h_prime_carry = meta.query_advice(a_9, Rotation::next());
|
||||
let sigma_e_lo = meta.query_advice(a_4, Rotation::cur());
|
||||
let sigma_e_hi = meta.query_advice(a_5, Rotation::cur());
|
||||
let ch_lo = meta.query_advice(a_1, Rotation::cur());
|
||||
let ch_hi = meta.query_advice(a_6, Rotation::next());
|
||||
let ch_neg_lo = meta.query_advice(a_5, Rotation::prev());
|
||||
let ch_neg_hi = meta.query_advice(a_5, Rotation::next());
|
||||
let h_lo = meta.query_advice(a_7, Rotation::prev());
|
||||
let h_hi = meta.query_advice(a_7, Rotation::cur());
|
||||
let k_lo = meta.query_advice(a_6, Rotation::prev());
|
||||
let k_hi = meta.query_advice(a_6, Rotation::cur());
|
||||
let w_lo = meta.query_advice(a_8, Rotation::prev());
|
||||
let w_hi = meta.query_advice(a_8, Rotation::cur());
|
||||
|
||||
CompressionGate::s_h_prime(
|
||||
s_h_prime,
|
||||
h_prime_lo,
|
||||
h_prime_hi,
|
||||
h_prime_carry,
|
||||
sigma_e_lo,
|
||||
sigma_e_hi,
|
||||
ch_lo,
|
||||
ch_hi,
|
||||
ch_neg_lo,
|
||||
ch_neg_hi,
|
||||
h_lo,
|
||||
h_hi,
|
||||
k_lo,
|
||||
k_hi,
|
||||
w_lo,
|
||||
w_hi,
|
||||
)
|
||||
.0
|
||||
});
|
||||
|
||||
// s_a_new
|
||||
meta.create_gate("s_a_new", |meta| {
|
||||
let s_a_new = meta.query_fixed(s_a_new, Rotation::cur());
|
||||
let a_new_lo = meta.query_advice(a_8, Rotation::cur());
|
||||
let a_new_hi = meta.query_advice(a_8, Rotation::next());
|
||||
let a_new_carry = meta.query_advice(a_9, Rotation::cur());
|
||||
let sigma_a_lo = meta.query_advice(a_6, Rotation::cur());
|
||||
let sigma_a_hi = meta.query_advice(a_6, Rotation::next());
|
||||
let maj_abc_lo = meta.query_advice(a_1, Rotation::cur());
|
||||
let maj_abc_hi = meta.query_advice(a_3, Rotation::prev());
|
||||
let h_prime_lo = meta.query_advice(a_7, Rotation::prev());
|
||||
let h_prime_hi = meta.query_advice(a_8, Rotation::prev());
|
||||
|
||||
CompressionGate::s_a_new(
|
||||
s_a_new,
|
||||
a_new_lo,
|
||||
a_new_hi,
|
||||
a_new_carry,
|
||||
sigma_a_lo,
|
||||
sigma_a_hi,
|
||||
maj_abc_lo,
|
||||
maj_abc_hi,
|
||||
h_prime_lo,
|
||||
h_prime_hi,
|
||||
)
|
||||
.0
|
||||
});
|
||||
|
||||
// s_e_new
|
||||
meta.create_gate("s_e_new", |meta| {
|
||||
let s_e_new = meta.query_fixed(s_e_new, Rotation::cur());
|
||||
let e_new_lo = meta.query_advice(a_8, Rotation::cur());
|
||||
let e_new_hi = meta.query_advice(a_8, Rotation::next());
|
||||
let e_new_carry = meta.query_advice(a_9, Rotation::next());
|
||||
let d_lo = meta.query_advice(a_7, Rotation::cur());
|
||||
let d_hi = meta.query_advice(a_7, Rotation::next());
|
||||
let h_prime_lo = meta.query_advice(a_7, Rotation::prev());
|
||||
let h_prime_hi = meta.query_advice(a_8, Rotation::prev());
|
||||
|
||||
CompressionGate::s_e_new(
|
||||
s_e_new,
|
||||
e_new_lo,
|
||||
e_new_hi,
|
||||
e_new_carry,
|
||||
d_lo,
|
||||
d_hi,
|
||||
h_prime_lo,
|
||||
h_prime_hi,
|
||||
)
|
||||
.0
|
||||
});
|
||||
|
||||
// s_digest for final round
|
||||
meta.create_gate("s_digest", |meta| {
|
||||
let s_digest = meta.query_fixed(s_digest, Rotation::cur());
|
||||
let lo_0 = meta.query_advice(a_3, Rotation::cur());
|
||||
let hi_0 = meta.query_advice(a_4, Rotation::cur());
|
||||
let word_0 = meta.query_advice(a_5, Rotation::cur());
|
||||
let lo_1 = meta.query_advice(a_6, Rotation::cur());
|
||||
let hi_1 = meta.query_advice(a_7, Rotation::cur());
|
||||
let word_1 = meta.query_advice(a_8, Rotation::cur());
|
||||
let lo_2 = meta.query_advice(a_3, Rotation::next());
|
||||
let hi_2 = meta.query_advice(a_4, Rotation::next());
|
||||
let word_2 = meta.query_advice(a_5, Rotation::next());
|
||||
let lo_3 = meta.query_advice(a_6, Rotation::next());
|
||||
let hi_3 = meta.query_advice(a_7, Rotation::next());
|
||||
let word_3 = meta.query_advice(a_8, Rotation::next());
|
||||
|
||||
CompressionGate::s_digest(
|
||||
s_digest, lo_0, hi_0, word_0, lo_1, hi_1, word_1, lo_2, hi_2, word_2, lo_3, hi_3,
|
||||
word_3,
|
||||
)
|
||||
.0
|
||||
});
|
||||
|
||||
Compression {
|
||||
lookup,
|
||||
message_schedule,
|
||||
extras,
|
||||
s_ch,
|
||||
s_ch_neg,
|
||||
s_maj,
|
||||
s_h_prime,
|
||||
s_a_new,
|
||||
s_e_new,
|
||||
s_upper_sigma_0,
|
||||
s_upper_sigma_1,
|
||||
s_decompose_abcd,
|
||||
s_decompose_efgh,
|
||||
s_digest,
|
||||
perm,
|
||||
}
|
||||
}
|
||||
|
||||
/// Initialize compression with a constant Initialization Vector of 32-byte words.
|
||||
/// Returns an initialized state.
|
||||
pub(super) fn initialize_with_iv<F: FieldExt>(
|
||||
&self,
|
||||
layouter: &mut impl Layouter<Table16Chip<F>>,
|
||||
init_state: [u32; STATE],
|
||||
) -> Result<State, Error> {
|
||||
let mut new_state = State::empty_state();
|
||||
layouter.assign_region(
|
||||
|| "initialize_with_iv",
|
||||
|mut region| {
|
||||
new_state = self.initialize_iv(&mut region, init_state)?;
|
||||
Ok(())
|
||||
},
|
||||
)?;
|
||||
Ok(new_state)
|
||||
}
|
||||
|
||||
/// Initialize compression with some initialized state. This could be a state
|
||||
/// output from a previous compression round.
|
||||
pub(super) fn initialize_with_state<F: FieldExt>(
|
||||
&self,
|
||||
layouter: &mut impl Layouter<Table16Chip<F>>,
|
||||
init_state: State,
|
||||
) -> Result<State, Error> {
|
||||
let mut new_state = State::empty_state();
|
||||
layouter.assign_region(
|
||||
|| "initialize_with_state",
|
||||
|mut region| {
|
||||
new_state = self.initialize_state(&mut region, init_state.clone())?;
|
||||
Ok(())
|
||||
},
|
||||
)?;
|
||||
Ok(new_state)
|
||||
}
|
||||
|
||||
/// Given an initialized state and a message schedule, perform 64 compression rounds.
|
||||
pub(super) fn compress<F: FieldExt>(
|
||||
&self,
|
||||
layouter: &mut impl Layouter<Table16Chip<F>>,
|
||||
initialized_state: State,
|
||||
w_halves: [(CellValue16, CellValue16); ROUNDS],
|
||||
) -> Result<State, Error> {
|
||||
let mut state = State::empty_state();
|
||||
layouter.assign_region(
|
||||
|| "compress",
|
||||
|mut region| {
|
||||
state = initialized_state.clone();
|
||||
for idx in 0..64 {
|
||||
state =
|
||||
self.assign_round(&mut region, idx, state.clone(), w_halves[idx as usize])?;
|
||||
}
|
||||
Ok(())
|
||||
},
|
||||
)?;
|
||||
Ok(state)
|
||||
}
|
||||
|
||||
/// After the final round, convert the state into the final digest.
|
||||
pub(super) fn digest<F: FieldExt>(
|
||||
&self,
|
||||
layouter: &mut impl Layouter<Table16Chip<F>>,
|
||||
state: State,
|
||||
) -> Result<[BlockWord; DIGEST_SIZE], Error> {
|
||||
let mut digest = [BlockWord::new(0); DIGEST_SIZE];
|
||||
layouter.assign_region(
|
||||
|| "digest",
|
||||
|mut region| {
|
||||
digest = self.assign_digest(&mut region, state.clone())?;
|
||||
|
||||
Ok(())
|
||||
},
|
||||
)?;
|
||||
Ok(digest)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub(super) fn empty_configure<F: FieldExt>(
|
||||
meta: &mut ConstraintSystem<F>,
|
||||
lookup: SpreadInputs,
|
||||
message_schedule: Column<Advice>,
|
||||
extras: [Column<Advice>; 6],
|
||||
perm: Permutation,
|
||||
) -> Self {
|
||||
let s_ch = meta.fixed_column();
|
||||
let s_ch_neg = meta.fixed_column();
|
||||
let s_maj = meta.fixed_column();
|
||||
let s_h_prime = meta.fixed_column();
|
||||
let s_a_new = meta.fixed_column();
|
||||
let s_e_new = meta.fixed_column();
|
||||
|
||||
let s_upper_sigma_0 = meta.fixed_column();
|
||||
let s_upper_sigma_1 = meta.fixed_column();
|
||||
|
||||
// Decomposition gate for AbcdVar
|
||||
let s_decompose_abcd = meta.fixed_column();
|
||||
// Decomposition gate for EfghVar
|
||||
let s_decompose_efgh = meta.fixed_column();
|
||||
|
||||
let s_digest = meta.fixed_column();
|
||||
|
||||
Compression {
|
||||
lookup,
|
||||
message_schedule,
|
||||
extras,
|
||||
s_ch,
|
||||
s_ch_neg,
|
||||
s_maj,
|
||||
s_h_prime,
|
||||
s_a_new,
|
||||
s_e_new,
|
||||
s_upper_sigma_0,
|
||||
s_upper_sigma_1,
|
||||
s_decompose_abcd,
|
||||
s_decompose_efgh,
|
||||
s_digest,
|
||||
perm,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::super::{
|
||||
super::BLOCK_SIZE, get_msg_schedule_test_input, BlockWord, MessageSchedule, SpreadTable,
|
||||
Table16Chip, Table16Config, IV,
|
||||
};
|
||||
use super::Compression;
|
||||
use halo2::{
|
||||
arithmetic::FieldExt,
|
||||
circuit::{layouter, Layouter},
|
||||
dev::MockProver,
|
||||
pasta::Fp,
|
||||
plonk::{Assignment, Circuit, ConstraintSystem, Error, Permutation},
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn compress() {
|
||||
struct MyCircuit {}
|
||||
|
||||
impl<F: FieldExt> Circuit<F> for MyCircuit {
|
||||
type Config = Table16Config;
|
||||
|
||||
fn configure(meta: &mut ConstraintSystem<F>) -> Self::Config {
|
||||
let a = meta.advice_column();
|
||||
let b = meta.advice_column();
|
||||
let c = meta.advice_column();
|
||||
|
||||
let (lookup_inputs, lookup_table) = SpreadTable::configure(meta, a, b, c);
|
||||
|
||||
let message_schedule = meta.advice_column();
|
||||
let extras = [
|
||||
meta.advice_column(),
|
||||
meta.advice_column(),
|
||||
meta.advice_column(),
|
||||
meta.advice_column(),
|
||||
meta.advice_column(),
|
||||
meta.advice_column(),
|
||||
];
|
||||
|
||||
// Rename these here for ease of matching the gates to the specification.
|
||||
let _a_0 = lookup_inputs.tag;
|
||||
let a_1 = lookup_inputs.dense;
|
||||
let a_2 = lookup_inputs.spread;
|
||||
let a_3 = extras[0];
|
||||
let a_4 = extras[1];
|
||||
let a_5 = message_schedule;
|
||||
let a_6 = extras[2];
|
||||
let a_7 = extras[3];
|
||||
let a_8 = extras[4];
|
||||
let _a_9 = extras[5];
|
||||
|
||||
let perm = Permutation::new(
|
||||
meta,
|
||||
&[
|
||||
a_1.into(),
|
||||
a_2.into(),
|
||||
a_3.into(),
|
||||
a_4.into(),
|
||||
a_5.into(),
|
||||
a_6.into(),
|
||||
a_7.into(),
|
||||
a_8.into(),
|
||||
],
|
||||
);
|
||||
|
||||
let compression = Compression::configure(
|
||||
meta,
|
||||
lookup_inputs.clone(),
|
||||
message_schedule,
|
||||
extras,
|
||||
perm.clone(),
|
||||
);
|
||||
|
||||
let message_schedule = MessageSchedule::configure(
|
||||
meta,
|
||||
lookup_inputs.clone(),
|
||||
message_schedule,
|
||||
extras,
|
||||
perm.clone(),
|
||||
);
|
||||
|
||||
Table16Config {
|
||||
lookup_table,
|
||||
message_schedule,
|
||||
compression,
|
||||
}
|
||||
}
|
||||
|
||||
fn synthesize(
|
||||
&self,
|
||||
cs: &mut impl Assignment<F>,
|
||||
config: Self::Config,
|
||||
) -> Result<(), Error> {
|
||||
let mut layouter = layouter::SingleChip::<Table16Chip<F>, _>::new(cs, config)?;
|
||||
|
||||
// Load table
|
||||
let table = layouter.config().lookup_table.clone();
|
||||
table.load(&mut layouter)?;
|
||||
|
||||
// Test vector: "abc"
|
||||
let input: [BlockWord; BLOCK_SIZE] = get_msg_schedule_test_input();
|
||||
|
||||
let config = layouter.config().clone();
|
||||
let (_, w_halves) = config.message_schedule.process(&mut layouter, input)?;
|
||||
|
||||
let compression = config.compression.clone();
|
||||
let initial_state = compression.initialize_with_iv(&mut layouter, IV)?;
|
||||
|
||||
let state =
|
||||
config
|
||||
.compression
|
||||
.compress(&mut layouter, initial_state.clone(), w_halves)?;
|
||||
|
||||
let digest = config.compression.digest(&mut layouter, state)?;
|
||||
for (idx, digest_word) in digest.iter().enumerate() {
|
||||
assert_eq!(
|
||||
(digest_word.value.unwrap() as u64 + IV[idx] as u64) as u32,
|
||||
super::compression_util::COMPRESSION_OUTPUT[idx]
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
let circuit: MyCircuit = MyCircuit {};
|
||||
|
||||
let prover = match MockProver::<Fp>::run(16, &circuit, vec![]) {
|
||||
Ok(prover) => prover,
|
||||
Err(e) => panic!("{:?}", e),
|
||||
};
|
||||
assert_eq!(prover.verify(), Ok(()));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,422 @@
|
|||
use super::super::{util::*, Gate};
|
||||
use halo2::{arithmetic::FieldExt, plonk::Expression};
|
||||
|
||||
pub struct CompressionGate<F: FieldExt>(pub Expression<F>);
|
||||
|
||||
impl<F: FieldExt> CompressionGate<F> {
|
||||
fn ones() -> Expression<F> {
|
||||
Expression::Constant(F::one())
|
||||
}
|
||||
|
||||
// Decompose `A,B,C,D` words
|
||||
// (2, 11, 9, 10)-bit chunks
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn s_decompose_abcd(
|
||||
s_decompose_abcd: Expression<F>,
|
||||
a: Expression<F>,
|
||||
spread_a: Expression<F>,
|
||||
b: Expression<F>,
|
||||
spread_b: Expression<F>,
|
||||
tag_b: Expression<F>,
|
||||
c_lo: Expression<F>,
|
||||
spread_c_lo: Expression<F>,
|
||||
c_mid: Expression<F>,
|
||||
spread_c_mid: Expression<F>,
|
||||
c_hi: Expression<F>,
|
||||
spread_c_hi: Expression<F>,
|
||||
d: Expression<F>,
|
||||
spread_d: Expression<F>,
|
||||
tag_d: Expression<F>,
|
||||
word_lo: Expression<F>,
|
||||
spread_word_lo: Expression<F>,
|
||||
word_hi: Expression<F>,
|
||||
spread_word_hi: Expression<F>,
|
||||
) -> Self {
|
||||
let check_spread_and_range =
|
||||
Gate::three_bit_spread_and_range(c_lo.clone(), spread_c_lo.clone())
|
||||
+ Gate::three_bit_spread_and_range(c_mid.clone(), spread_c_mid.clone())
|
||||
+ Gate::three_bit_spread_and_range(c_hi.clone(), spread_c_hi.clone())
|
||||
+ Gate::two_bit_spread_and_range(a.clone(), spread_a.clone());
|
||||
let range_check_tag_b = Gate::range_check(tag_b, 0, 2);
|
||||
let range_check_tag_d = Gate::range_check(tag_d, 0, 1);
|
||||
let dense_check = a
|
||||
+ b * F::from_u64(1 << 2)
|
||||
+ c_lo * F::from_u64(1 << 13)
|
||||
+ c_mid * F::from_u64(1 << 16)
|
||||
+ c_hi * F::from_u64(1 << 19)
|
||||
+ d * F::from_u64(1 << 22)
|
||||
+ word_lo * (-F::one())
|
||||
+ word_hi * F::from_u64(1 << 16) * (-F::one());
|
||||
let spread_check = spread_a
|
||||
+ spread_b * F::from_u64(1 << 4)
|
||||
+ spread_c_lo * F::from_u64(1 << 26)
|
||||
+ spread_c_mid * F::from_u64(1 << 32)
|
||||
+ spread_c_hi * F::from_u64(1 << 38)
|
||||
+ spread_d * F::from_u64(1 << 44)
|
||||
+ spread_word_lo * (-F::one())
|
||||
+ spread_word_hi * F::from_u64(1 << 32) * (-F::one());
|
||||
|
||||
CompressionGate(
|
||||
s_decompose_abcd
|
||||
* (range_check_tag_b
|
||||
+ range_check_tag_d
|
||||
+ dense_check
|
||||
+ spread_check
|
||||
+ check_spread_and_range),
|
||||
)
|
||||
}
|
||||
|
||||
// Decompose `E,F,G,H` words
|
||||
// (6, 5, 14, 7)-bit chunks
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn s_decompose_efgh(
|
||||
s_decompose_efgh: Expression<F>,
|
||||
a_lo: Expression<F>,
|
||||
spread_a_lo: Expression<F>,
|
||||
a_hi: Expression<F>,
|
||||
spread_a_hi: Expression<F>,
|
||||
b_lo: Expression<F>,
|
||||
spread_b_lo: Expression<F>,
|
||||
b_hi: Expression<F>,
|
||||
spread_b_hi: Expression<F>,
|
||||
c: Expression<F>,
|
||||
spread_c: Expression<F>,
|
||||
tag_c: Expression<F>,
|
||||
d: Expression<F>,
|
||||
spread_d: Expression<F>,
|
||||
tag_d: Expression<F>,
|
||||
word_lo: Expression<F>,
|
||||
spread_word_lo: Expression<F>,
|
||||
word_hi: Expression<F>,
|
||||
spread_word_hi: Expression<F>,
|
||||
) -> Self {
|
||||
let check_spread_and_range =
|
||||
Gate::three_bit_spread_and_range(a_lo.clone(), spread_a_lo.clone())
|
||||
+ Gate::three_bit_spread_and_range(a_hi.clone(), spread_a_hi.clone())
|
||||
+ Gate::three_bit_spread_and_range(b_hi.clone(), spread_b_hi.clone())
|
||||
+ Gate::two_bit_spread_and_range(b_lo.clone(), spread_b_lo.clone());
|
||||
let range_check_tag_c = Gate::range_check(tag_c, 0, 4);
|
||||
let range_check_tag_d = Gate::range_check(tag_d, 0, 0);
|
||||
let dense_check = a_lo
|
||||
+ a_hi * F::from_u64(1 << 3)
|
||||
+ b_lo * F::from_u64(1 << 6)
|
||||
+ b_hi * F::from_u64(1 << 8)
|
||||
+ c * F::from_u64(1 << 11)
|
||||
+ d * F::from_u64(1 << 25)
|
||||
+ word_lo * (-F::one())
|
||||
+ word_hi * F::from_u64(1 << 16) * (-F::one());
|
||||
let spread_check = spread_a_lo
|
||||
+ spread_a_hi * F::from_u64(1 << 6)
|
||||
+ spread_b_lo * F::from_u64(1 << 12)
|
||||
+ spread_b_hi * F::from_u64(1 << 16)
|
||||
+ spread_c * F::from_u64(1 << 22)
|
||||
+ spread_d * F::from_u64(1 << 50)
|
||||
+ spread_word_lo * (-F::one())
|
||||
+ spread_word_hi * F::from_u64(1 << 32) * (-F::one());
|
||||
|
||||
CompressionGate(
|
||||
s_decompose_efgh
|
||||
* (range_check_tag_c
|
||||
+ range_check_tag_d
|
||||
+ dense_check
|
||||
+ spread_check
|
||||
+ check_spread_and_range),
|
||||
)
|
||||
}
|
||||
|
||||
// s_upper_sigma_0 on abcd words
|
||||
// (2, 11, 9, 10)-bit chunks
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn s_upper_sigma_0(
|
||||
s_upper_sigma_0: Expression<F>,
|
||||
spread_r0_even: Expression<F>,
|
||||
spread_r0_odd: Expression<F>,
|
||||
spread_r1_even: Expression<F>,
|
||||
spread_r1_odd: Expression<F>,
|
||||
spread_a: Expression<F>,
|
||||
spread_b: Expression<F>,
|
||||
spread_c_lo: Expression<F>,
|
||||
spread_c_mid: Expression<F>,
|
||||
spread_c_hi: Expression<F>,
|
||||
spread_d: Expression<F>,
|
||||
) -> Self {
|
||||
let spread_witness = spread_r0_even
|
||||
+ spread_r0_odd * F::from_u64(2)
|
||||
+ (spread_r1_even + spread_r1_odd * F::from_u64(2)) * F::from_u64(1 << 32);
|
||||
let xor_0 = spread_b.clone()
|
||||
+ spread_c_lo.clone() * F::from_u64(1 << 22)
|
||||
+ spread_c_mid.clone() * F::from_u64(1 << 28)
|
||||
+ spread_c_hi.clone() * F::from_u64(1 << 34)
|
||||
+ spread_d.clone() * F::from_u64(1 << 40)
|
||||
+ spread_a.clone() * F::from_u64(1 << 60);
|
||||
let xor_1 = spread_c_lo.clone()
|
||||
+ spread_c_mid.clone() * F::from_u64(1 << 6)
|
||||
+ spread_c_hi.clone() * F::from_u64(1 << 12)
|
||||
+ spread_d.clone() * F::from_u64(1 << 18)
|
||||
+ spread_a.clone() * F::from_u64(1 << 38)
|
||||
+ spread_b.clone() * F::from_u64(1 << 42);
|
||||
let xor_2 = spread_d
|
||||
+ spread_a * F::from_u64(1 << 20)
|
||||
+ spread_b * F::from_u64(1 << 24)
|
||||
+ spread_c_lo * F::from_u64(1 << 46)
|
||||
+ spread_c_mid * F::from_u64(1 << 52)
|
||||
+ spread_c_hi * F::from_u64(1 << 58);
|
||||
let xor = xor_0 + xor_1 + xor_2;
|
||||
|
||||
CompressionGate(s_upper_sigma_0 * (spread_witness + (xor * -F::one())))
|
||||
}
|
||||
|
||||
// s_upper_sigma_1 on efgh words
|
||||
// (6, 5, 14, 7)-bit chunks
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn s_upper_sigma_1(
|
||||
s_upper_sigma_1: Expression<F>,
|
||||
spread_r0_even: Expression<F>,
|
||||
spread_r0_odd: Expression<F>,
|
||||
spread_r1_even: Expression<F>,
|
||||
spread_r1_odd: Expression<F>,
|
||||
spread_a_lo: Expression<F>,
|
||||
spread_a_hi: Expression<F>,
|
||||
spread_b_lo: Expression<F>,
|
||||
spread_b_hi: Expression<F>,
|
||||
spread_c: Expression<F>,
|
||||
spread_d: Expression<F>,
|
||||
) -> Self {
|
||||
let spread_witness = spread_r0_even
|
||||
+ spread_r0_odd * F::from_u64(2)
|
||||
+ (spread_r1_even + spread_r1_odd * F::from_u64(2)) * F::from_u64(1 << 32);
|
||||
|
||||
let xor_0 = spread_b_lo.clone()
|
||||
+ spread_b_hi.clone() * F::from_u64(1 << 4)
|
||||
+ spread_c.clone() * F::from_u64(1 << 10)
|
||||
+ spread_d.clone() * F::from_u64(1 << 38)
|
||||
+ spread_a_lo.clone() * F::from_u64(1 << 52)
|
||||
+ spread_a_hi.clone() * F::from_u64(1 << 58);
|
||||
let xor_1 = spread_c.clone()
|
||||
+ spread_d.clone() * F::from_u64(1 << 28)
|
||||
+ spread_a_lo.clone() * F::from_u64(1 << 42)
|
||||
+ spread_a_hi.clone() * F::from_u64(1 << 48)
|
||||
+ spread_b_lo.clone() * F::from_u64(1 << 54)
|
||||
+ spread_b_hi.clone() * F::from_u64(1 << 58);
|
||||
let xor_2 = spread_d
|
||||
+ spread_a_lo * F::from_u64(1 << 14)
|
||||
+ spread_a_hi * F::from_u64(1 << 20)
|
||||
+ spread_b_lo * F::from_u64(1 << 26)
|
||||
+ spread_b_hi * F::from_u64(1 << 30)
|
||||
+ spread_c * F::from_u64(1 << 36);
|
||||
let xor = xor_0 + xor_1 + xor_2;
|
||||
|
||||
CompressionGate(s_upper_sigma_1 * (spread_witness + (xor * -F::one())))
|
||||
}
|
||||
|
||||
// First part of choice gate on (E, F, G), E ∧ F
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn s_ch(
|
||||
s_ch: Expression<F>,
|
||||
spread_p0_even: Expression<F>,
|
||||
spread_p0_odd: Expression<F>,
|
||||
spread_p1_even: Expression<F>,
|
||||
spread_p1_odd: Expression<F>,
|
||||
spread_e_lo: Expression<F>,
|
||||
spread_e_hi: Expression<F>,
|
||||
spread_f_lo: Expression<F>,
|
||||
spread_f_hi: Expression<F>,
|
||||
) -> Self {
|
||||
let lhs_lo = spread_e_lo + spread_f_lo;
|
||||
let lhs_hi = spread_e_hi + spread_f_hi;
|
||||
let lhs = lhs_lo + lhs_hi * F::from_u64(1 << 32);
|
||||
|
||||
let rhs_even = spread_p0_even + spread_p1_even * F::from_u64(1 << 32);
|
||||
let rhs_odd = spread_p0_odd + spread_p1_odd * F::from_u64(1 << 32);
|
||||
let rhs = rhs_even + rhs_odd * F::from_u64(2);
|
||||
|
||||
CompressionGate(s_ch * (lhs + rhs * -F::one()))
|
||||
}
|
||||
|
||||
// Second part of Choice gate on (E, F, G), ¬E ∧ G
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn s_ch_neg(
|
||||
s_ch_neg: Expression<F>,
|
||||
spread_q0_even: Expression<F>,
|
||||
spread_q0_odd: Expression<F>,
|
||||
spread_q1_even: Expression<F>,
|
||||
spread_q1_odd: Expression<F>,
|
||||
spread_e_lo: Expression<F>,
|
||||
spread_e_hi: Expression<F>,
|
||||
spread_e_neg_lo: Expression<F>,
|
||||
spread_e_neg_hi: Expression<F>,
|
||||
spread_g_lo: Expression<F>,
|
||||
spread_g_hi: Expression<F>,
|
||||
) -> Self {
|
||||
let neg_check = Self::neg_check(
|
||||
spread_e_lo,
|
||||
spread_e_hi,
|
||||
spread_e_neg_lo.clone(),
|
||||
spread_e_neg_hi.clone(),
|
||||
);
|
||||
let lhs_lo = spread_e_neg_lo + spread_g_lo;
|
||||
let lhs_hi = spread_e_neg_hi + spread_g_hi;
|
||||
let lhs = lhs_lo + lhs_hi * F::from_u64(1 << 32);
|
||||
|
||||
let rhs_even = spread_q0_even + spread_q1_even * F::from_u64(1 << 32);
|
||||
let rhs_odd = spread_q0_odd + spread_q1_odd * F::from_u64(1 << 32);
|
||||
let rhs = rhs_even + rhs_odd * F::from_u64(2);
|
||||
|
||||
CompressionGate(s_ch_neg * (neg_check + lhs + rhs * -F::one()))
|
||||
}
|
||||
|
||||
// Majority gate on (A, B, C)
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn s_maj(
|
||||
s_maj: Expression<F>,
|
||||
spread_m_0_even: Expression<F>,
|
||||
spread_m_0_odd: Expression<F>,
|
||||
spread_m_1_even: Expression<F>,
|
||||
spread_m_1_odd: Expression<F>,
|
||||
spread_a_lo: Expression<F>,
|
||||
spread_a_hi: Expression<F>,
|
||||
spread_b_lo: Expression<F>,
|
||||
spread_b_hi: Expression<F>,
|
||||
spread_c_lo: Expression<F>,
|
||||
spread_c_hi: Expression<F>,
|
||||
) -> Self {
|
||||
let maj_even = spread_m_0_even + spread_m_1_even * F::from_u64(1 << 32);
|
||||
let maj_odd = spread_m_0_odd + spread_m_1_odd * F::from_u64(1 << 32);
|
||||
let maj = maj_even + maj_odd * F::from_u64(2);
|
||||
|
||||
let a = spread_a_lo + spread_a_hi * F::from_u64(1 << 32);
|
||||
let b = spread_b_lo + spread_b_hi * F::from_u64(1 << 32);
|
||||
let c = spread_c_lo + spread_c_hi * F::from_u64(1 << 32);
|
||||
let sum = a + b + c;
|
||||
|
||||
CompressionGate(s_maj * (sum + maj * -F::one()))
|
||||
}
|
||||
|
||||
// Negation gate, used in second part of Choice gate
|
||||
fn neg_check(
|
||||
word_lo: Expression<F>,
|
||||
word_hi: Expression<F>,
|
||||
neg_word_lo: Expression<F>,
|
||||
neg_word_hi: Expression<F>,
|
||||
) -> Expression<F> {
|
||||
let evens = Self::ones() * F::from_u64(MASK_EVEN_32 as u64);
|
||||
// evens - word_lo = neg_word_lo
|
||||
let lo_check = neg_word_lo + word_lo + (evens.clone() * (-F::one()));
|
||||
// evens - word_hi = neg_word_hi
|
||||
let hi_check = neg_word_hi + word_hi + (evens * (-F::one()));
|
||||
|
||||
lo_check + hi_check
|
||||
}
|
||||
|
||||
// s_h_prime to get H' = H + Ch(E, F, G) + s_upper_sigma_1(E) + K + W
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn s_h_prime(
|
||||
s_h_prime: Expression<F>,
|
||||
h_prime_lo: Expression<F>,
|
||||
h_prime_hi: Expression<F>,
|
||||
h_prime_carry: Expression<F>,
|
||||
sigma_e_lo: Expression<F>,
|
||||
sigma_e_hi: Expression<F>,
|
||||
ch_lo: Expression<F>,
|
||||
ch_hi: Expression<F>,
|
||||
ch_neg_lo: Expression<F>,
|
||||
ch_neg_hi: Expression<F>,
|
||||
h_lo: Expression<F>,
|
||||
h_hi: Expression<F>,
|
||||
k_lo: Expression<F>,
|
||||
k_hi: Expression<F>,
|
||||
w_lo: Expression<F>,
|
||||
w_hi: Expression<F>,
|
||||
) -> Self {
|
||||
let lo = h_lo + ch_lo + ch_neg_lo + sigma_e_lo + k_lo + w_lo;
|
||||
let hi = h_hi + ch_hi + ch_neg_hi + sigma_e_hi + k_hi + w_hi;
|
||||
|
||||
let sum = lo + hi * F::from_u64(1 << 16);
|
||||
let h_prime = h_prime_lo + h_prime_hi * F::from_u64(1 << 16);
|
||||
|
||||
CompressionGate(
|
||||
s_h_prime
|
||||
* (sum
|
||||
+ h_prime_carry * F::from_u64(1 << 32) * (-F::one())
|
||||
+ h_prime * (-F::one())),
|
||||
)
|
||||
}
|
||||
|
||||
// s_a_new to get A_new = H' + Maj(A, B, C) + s_upper_sigma_0(A)
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn s_a_new(
|
||||
s_a_new: Expression<F>,
|
||||
a_new_lo: Expression<F>,
|
||||
a_new_hi: Expression<F>,
|
||||
a_new_carry: Expression<F>,
|
||||
sigma_a_lo: Expression<F>,
|
||||
sigma_a_hi: Expression<F>,
|
||||
maj_abc_lo: Expression<F>,
|
||||
maj_abc_hi: Expression<F>,
|
||||
h_prime_lo: Expression<F>,
|
||||
h_prime_hi: Expression<F>,
|
||||
) -> Self {
|
||||
let lo = sigma_a_lo + maj_abc_lo + h_prime_lo;
|
||||
let hi = sigma_a_hi + maj_abc_hi + h_prime_hi;
|
||||
let sum = lo + hi * F::from_u64(1 << 16);
|
||||
let a_new = a_new_lo + a_new_hi * F::from_u64(1 << 16);
|
||||
|
||||
CompressionGate(
|
||||
s_a_new
|
||||
* (sum + a_new_carry * F::from_u64(1 << 32) * (-F::one()) + a_new * (-F::one())),
|
||||
)
|
||||
}
|
||||
|
||||
// s_e_new to get E_new = H' + D
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn s_e_new(
|
||||
s_e_new: Expression<F>,
|
||||
e_new_lo: Expression<F>,
|
||||
e_new_hi: Expression<F>,
|
||||
e_new_carry: Expression<F>,
|
||||
d_lo: Expression<F>,
|
||||
d_hi: Expression<F>,
|
||||
h_prime_lo: Expression<F>,
|
||||
h_prime_hi: Expression<F>,
|
||||
) -> Self {
|
||||
let lo = h_prime_lo + d_lo;
|
||||
let hi = h_prime_hi + d_hi;
|
||||
let sum = lo + hi * F::from_u64(1 << 16);
|
||||
let e_new = e_new_lo + e_new_hi * F::from_u64(1 << 16);
|
||||
|
||||
CompressionGate(
|
||||
s_e_new
|
||||
* (sum + e_new_carry * F::from_u64(1 << 32) * (-F::one()) + e_new * (-F::one())),
|
||||
)
|
||||
}
|
||||
|
||||
fn check_lo_hi(lo: Expression<F>, hi: Expression<F>, word: Expression<F>) -> Expression<F> {
|
||||
lo + hi * F::from_u64(1 << 16) + (word * (-F::one()))
|
||||
}
|
||||
|
||||
// s_digest on final round
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn s_digest(
|
||||
s_digest: Expression<F>,
|
||||
lo_0: Expression<F>,
|
||||
hi_0: Expression<F>,
|
||||
word_0: Expression<F>,
|
||||
lo_1: Expression<F>,
|
||||
hi_1: Expression<F>,
|
||||
word_1: Expression<F>,
|
||||
lo_2: Expression<F>,
|
||||
hi_2: Expression<F>,
|
||||
word_2: Expression<F>,
|
||||
lo_3: Expression<F>,
|
||||
hi_3: Expression<F>,
|
||||
word_3: Expression<F>,
|
||||
) -> Self {
|
||||
CompressionGate(
|
||||
s_digest
|
||||
* (Self::check_lo_hi(lo_0, hi_0, word_0)
|
||||
+ Self::check_lo_hi(lo_1, hi_1, word_1)
|
||||
+ Self::check_lo_hi(lo_2, hi_2, word_2)
|
||||
+ Self::check_lo_hi(lo_3, hi_3, word_3)),
|
||||
)
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,123 @@
|
|||
use super::super::{super::DIGEST_SIZE, BlockWord, CellValue16, Table16Assignment, Table16Chip};
|
||||
use super::{compression_util::*, Compression, State};
|
||||
use halo2::{
|
||||
arithmetic::FieldExt,
|
||||
circuit::Region,
|
||||
plonk::{Advice, Column, Error},
|
||||
};
|
||||
|
||||
impl Compression {
|
||||
#[allow(clippy::many_single_char_names)]
|
||||
pub fn assign_digest<F: FieldExt>(
|
||||
&self,
|
||||
region: &mut Region<'_, Table16Chip<F>>,
|
||||
state: State,
|
||||
) -> Result<[BlockWord; DIGEST_SIZE], Error> {
|
||||
let a_3 = self.extras[0];
|
||||
let a_4 = self.extras[1];
|
||||
let a_5 = self.message_schedule;
|
||||
let a_6 = self.extras[2];
|
||||
let a_7 = self.extras[3];
|
||||
let a_8 = self.extras[4];
|
||||
|
||||
let (a, b, c, d, e, f, g, h) = match_state(state);
|
||||
|
||||
let abcd_row = 0;
|
||||
region.assign_fixed(|| "s_digest", self.s_digest, abcd_row, || Ok(F::one()))?;
|
||||
let efgh_row = abcd_row + 2;
|
||||
region.assign_fixed(|| "s_digest", self.s_digest, efgh_row, || Ok(F::one()))?;
|
||||
|
||||
// Assign digest for A, B, C, D
|
||||
self.assign_and_constrain(
|
||||
region,
|
||||
|| "a_lo",
|
||||
a_3,
|
||||
abcd_row,
|
||||
&a.dense_halves.0.into(),
|
||||
&self.perm,
|
||||
)?;
|
||||
self.assign_and_constrain(
|
||||
region,
|
||||
|| "a_hi",
|
||||
a_4,
|
||||
abcd_row,
|
||||
&a.dense_halves.1.into(),
|
||||
&self.perm,
|
||||
)?;
|
||||
let a = a.dense_halves.0.value.unwrap() as u32
|
||||
+ (1 << 16) * (a.dense_halves.1.value.unwrap() as u32);
|
||||
region.assign_advice(|| "a", a_5, abcd_row, || Ok(F::from_u64(a as u64)))?;
|
||||
|
||||
let b = self.assign_digest_word(region, abcd_row, a_6, a_7, a_8, b.dense_halves)?;
|
||||
let c = self.assign_digest_word(region, abcd_row + 1, a_3, a_4, a_5, c.dense_halves)?;
|
||||
let d = self.assign_digest_word(region, abcd_row + 1, a_6, a_7, a_8, d.dense_halves)?;
|
||||
|
||||
// Assign digest for E, F, G, H
|
||||
self.assign_and_constrain(
|
||||
region,
|
||||
|| "e_lo",
|
||||
a_3,
|
||||
efgh_row,
|
||||
&e.dense_halves.0.into(),
|
||||
&self.perm,
|
||||
)?;
|
||||
self.assign_and_constrain(
|
||||
region,
|
||||
|| "e_hi",
|
||||
a_4,
|
||||
efgh_row,
|
||||
&e.dense_halves.1.into(),
|
||||
&self.perm,
|
||||
)?;
|
||||
let e = e.dense_halves.0.value.unwrap() as u32
|
||||
+ (1 << 16) * (e.dense_halves.1.value.unwrap() as u32);
|
||||
region.assign_advice(|| "e", a_5, efgh_row, || Ok(F::from_u64(e as u64)))?;
|
||||
|
||||
let f = self.assign_digest_word(region, efgh_row, a_6, a_7, a_8, f.dense_halves)?;
|
||||
let g = self.assign_digest_word(region, efgh_row + 1, a_3, a_4, a_5, g.dense_halves)?;
|
||||
let h = self.assign_digest_word(region, efgh_row + 1, a_6, a_7, a_8, h.dense_halves)?;
|
||||
|
||||
Ok([
|
||||
BlockWord::new(a),
|
||||
BlockWord::new(b),
|
||||
BlockWord::new(c),
|
||||
BlockWord::new(d),
|
||||
BlockWord::new(e),
|
||||
BlockWord::new(f),
|
||||
BlockWord::new(g),
|
||||
BlockWord::new(h),
|
||||
])
|
||||
}
|
||||
|
||||
fn assign_digest_word<F: FieldExt>(
|
||||
&self,
|
||||
region: &mut Region<'_, Table16Chip<F>>,
|
||||
row: usize,
|
||||
lo_col: Column<Advice>,
|
||||
hi_col: Column<Advice>,
|
||||
word_col: Column<Advice>,
|
||||
dense_halves: (CellValue16, CellValue16),
|
||||
) -> Result<u32, Error> {
|
||||
self.assign_and_constrain(
|
||||
region,
|
||||
|| "lo",
|
||||
lo_col,
|
||||
row,
|
||||
&dense_halves.0.into(),
|
||||
&self.perm,
|
||||
)?;
|
||||
self.assign_and_constrain(
|
||||
region,
|
||||
|| "hi",
|
||||
hi_col,
|
||||
row,
|
||||
&dense_halves.1.into(),
|
||||
&self.perm,
|
||||
)?;
|
||||
let val = dense_halves.0.value.unwrap() as u32
|
||||
+ (1 << 16) * (dense_halves.1.value.unwrap() as u32);
|
||||
region.assign_advice(|| "word", word_col, row, || Ok(F::from_u64(val as u64)))?;
|
||||
|
||||
Ok(val)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,158 @@
|
|||
use super::super::{RoundWordDense, RoundWordSpread, StateWord, Table16Chip, STATE};
|
||||
use super::{compression_util::*, Compression, State};
|
||||
use halo2::{arithmetic::FieldExt, circuit::Region, plonk::Error};
|
||||
|
||||
impl Compression {
|
||||
#[allow(clippy::many_single_char_names)]
|
||||
pub fn initialize_iv<F: FieldExt>(
|
||||
&self,
|
||||
region: &mut Region<'_, Table16Chip<F>>,
|
||||
iv: [u32; STATE],
|
||||
) -> Result<State, Error> {
|
||||
let a_7 = self.extras[3];
|
||||
|
||||
let idx = -1;
|
||||
|
||||
// Decompose E into (6, 5, 14, 7)-bit chunks
|
||||
let e = self.decompose_e(region, idx, iv[4])?;
|
||||
|
||||
// Decompose F, G
|
||||
let f = self.decompose_f(region, idx, iv[5])?;
|
||||
let g = self.decompose_g(region, idx, iv[6])?;
|
||||
|
||||
// Assign H
|
||||
let h_row = get_h_row(idx);
|
||||
let h_dense = self.assign_word_halves_dense(region, h_row, a_7, h_row + 1, a_7, iv[7])?;
|
||||
let h = RoundWordDense::new(h_dense);
|
||||
|
||||
// Decompose A into (2, 11, 9, 10)-bit chunks
|
||||
let a = self.decompose_a(region, idx, iv[0])?;
|
||||
|
||||
// Decompose B, C
|
||||
let b = self.decompose_b(region, idx, iv[1])?;
|
||||
let c = self.decompose_c(region, idx, iv[2])?;
|
||||
|
||||
// Assign D
|
||||
let d_row = get_d_row(idx);
|
||||
let d_dense = self.assign_word_halves_dense(region, d_row, a_7, d_row + 1, a_7, iv[3])?;
|
||||
let d = RoundWordDense::new(d_dense);
|
||||
|
||||
Ok(State::new(
|
||||
StateWord::A(a),
|
||||
StateWord::B(b),
|
||||
StateWord::C(c),
|
||||
StateWord::D(d),
|
||||
StateWord::E(e),
|
||||
StateWord::F(f),
|
||||
StateWord::G(g),
|
||||
StateWord::H(h),
|
||||
))
|
||||
}
|
||||
|
||||
#[allow(clippy::many_single_char_names)]
|
||||
pub fn initialize_state<F: FieldExt>(
|
||||
&self,
|
||||
region: &mut Region<'_, Table16Chip<F>>,
|
||||
state: State,
|
||||
) -> Result<State, Error> {
|
||||
let a_7 = self.extras[3];
|
||||
let (a, b, c, d, e, f, g, h) = match_state(state);
|
||||
|
||||
let idx = -1;
|
||||
|
||||
// Decompose E into (6, 5, 14, 7)-bit chunks
|
||||
let e = val_from_dense_halves(e.dense_halves);
|
||||
let e = self.decompose_e(region, idx, e)?;
|
||||
|
||||
// Decompose F, G
|
||||
let f = val_from_dense_halves(f.dense_halves);
|
||||
let f = self.decompose_f(region, idx, f)?;
|
||||
let g = val_from_dense_halves(g.dense_halves);
|
||||
let g = self.decompose_g(region, idx, g)?;
|
||||
|
||||
// Assign H
|
||||
let h = val_from_dense_halves(h.dense_halves);
|
||||
let h_row = get_h_row(idx);
|
||||
let h_dense = self.assign_word_halves_dense(region, h_row, a_7, h_row + 1, a_7, h)?;
|
||||
let h = RoundWordDense::new(h_dense);
|
||||
|
||||
// Decompose A into (2, 11, 9, 10)-bit chunks
|
||||
let a = val_from_dense_halves(a.dense_halves);
|
||||
let a = self.decompose_a(region, idx, a)?;
|
||||
|
||||
// Decompose B, C
|
||||
let b = val_from_dense_halves(b.dense_halves);
|
||||
let b = self.decompose_b(region, idx, b)?;
|
||||
let c = val_from_dense_halves(c.dense_halves);
|
||||
let c = self.decompose_c(region, idx, c)?;
|
||||
|
||||
// Assign D
|
||||
let d = val_from_dense_halves(d.dense_halves);
|
||||
let d_row = get_d_row(idx);
|
||||
let d_dense = self.assign_word_halves_dense(region, d_row, a_7, d_row + 1, a_7, d)?;
|
||||
let d = RoundWordDense::new(d_dense);
|
||||
|
||||
Ok(State::new(
|
||||
StateWord::A(a),
|
||||
StateWord::B(b),
|
||||
StateWord::C(c),
|
||||
StateWord::D(d),
|
||||
StateWord::E(e),
|
||||
StateWord::F(f),
|
||||
StateWord::G(g),
|
||||
StateWord::H(h),
|
||||
))
|
||||
}
|
||||
|
||||
fn decompose_b<F: FieldExt>(
|
||||
&self,
|
||||
region: &mut Region<'_, Table16Chip<F>>,
|
||||
idx: i32,
|
||||
b_val: u32,
|
||||
) -> Result<RoundWordSpread, Error> {
|
||||
let row = get_decompose_b_row(idx);
|
||||
|
||||
let (dense_halves, spread_halves) = self.assign_word_halves(region, row, b_val)?;
|
||||
self.decompose_abcd(region, row, b_val)?;
|
||||
Ok(RoundWordSpread::new(dense_halves, spread_halves))
|
||||
}
|
||||
|
||||
fn decompose_c<F: FieldExt>(
|
||||
&self,
|
||||
region: &mut Region<'_, Table16Chip<F>>,
|
||||
idx: i32,
|
||||
c_val: u32,
|
||||
) -> Result<RoundWordSpread, Error> {
|
||||
let row = get_decompose_c_row(idx);
|
||||
|
||||
let (dense_halves, spread_halves) = self.assign_word_halves(region, row, c_val)?;
|
||||
self.decompose_abcd(region, row, c_val)?;
|
||||
Ok(RoundWordSpread::new(dense_halves, spread_halves))
|
||||
}
|
||||
|
||||
fn decompose_f<F: FieldExt>(
|
||||
&self,
|
||||
region: &mut Region<'_, Table16Chip<F>>,
|
||||
idx: i32,
|
||||
f_val: u32,
|
||||
) -> Result<RoundWordSpread, Error> {
|
||||
let row = get_decompose_f_row(idx);
|
||||
|
||||
let (dense_halves, spread_halves) = self.assign_word_halves(region, row, f_val)?;
|
||||
self.decompose_efgh(region, row, f_val)?;
|
||||
Ok(RoundWordSpread::new(dense_halves, spread_halves))
|
||||
}
|
||||
|
||||
fn decompose_g<F: FieldExt>(
|
||||
&self,
|
||||
region: &mut Region<'_, Table16Chip<F>>,
|
||||
idx: i32,
|
||||
g_val: u32,
|
||||
) -> Result<RoundWordSpread, Error> {
|
||||
let row = get_decompose_g_row(idx);
|
||||
|
||||
let (dense_halves, spread_halves) = self.assign_word_halves(region, row, g_val)?;
|
||||
self.decompose_efgh(region, row, g_val)?;
|
||||
Ok(RoundWordSpread::new(dense_halves, spread_halves))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,138 @@
|
|||
use super::super::{
|
||||
CellValue16, RoundWordA, RoundWordE, StateWord, Table16Assignment, Table16Chip, ROUND_CONSTANTS,
|
||||
};
|
||||
use super::{compression_util::*, Compression, State};
|
||||
use halo2::{arithmetic::FieldExt, circuit::Region, plonk::Error};
|
||||
|
||||
impl Compression {
|
||||
#[allow(clippy::many_single_char_names)]
|
||||
pub fn assign_round<F: FieldExt>(
|
||||
&self,
|
||||
region: &mut Region<'_, Table16Chip<F>>,
|
||||
idx: i32,
|
||||
state: State,
|
||||
schedule_word: (CellValue16, CellValue16),
|
||||
) -> Result<State, Error> {
|
||||
let a_3 = self.extras[0];
|
||||
let a_4 = self.extras[1];
|
||||
let a_7 = self.extras[3];
|
||||
|
||||
let (a, b, c, d, e, f, g, h) = match_state(state);
|
||||
|
||||
// s_upper_sigma_1(E)
|
||||
let sigma_1 = self.assign_upper_sigma_1(region, idx, e.pieces.unwrap())?;
|
||||
|
||||
// Ch(E, F, G)
|
||||
let ch = self.assign_ch(region, idx, e.spread_halves.unwrap(), f.spread_halves)?;
|
||||
let ch_neg = self.assign_ch_neg(region, idx, e.spread_halves.unwrap(), g.spread_halves)?;
|
||||
|
||||
// s_upper_sigma_0(A)
|
||||
let sigma_0 = self.assign_upper_sigma_0(region, idx, a.pieces.unwrap())?;
|
||||
|
||||
// Maj(A, B, C)
|
||||
let maj = self.assign_maj(
|
||||
region,
|
||||
idx,
|
||||
a.spread_halves.unwrap(),
|
||||
b.spread_halves,
|
||||
c.spread_halves,
|
||||
)?;
|
||||
|
||||
// H' = H + Ch(E, F, G) + s_upper_sigma_1(E) + K + W
|
||||
let h_prime = self.assign_h_prime(
|
||||
region,
|
||||
idx,
|
||||
h.dense_halves,
|
||||
ch,
|
||||
ch_neg,
|
||||
sigma_1,
|
||||
ROUND_CONSTANTS[idx as usize],
|
||||
schedule_word,
|
||||
)?;
|
||||
|
||||
// E_new = H' + D
|
||||
let e_new_dense = self.assign_e_new(region, idx, d.dense_halves, h_prime)?;
|
||||
let e_new_val = val_from_dense_halves(e_new_dense);
|
||||
|
||||
// A_new = H' + Maj(A, B, C) + sigma_0(A)
|
||||
let a_new_dense = self.assign_a_new(region, idx, maj, sigma_0, h_prime)?;
|
||||
let a_new_val = val_from_dense_halves(a_new_dense);
|
||||
|
||||
if idx < 63 {
|
||||
// Assign and copy A_new
|
||||
let a_new_row = get_decompose_a_row(idx + 1);
|
||||
self.assign_and_constrain(
|
||||
region,
|
||||
|| "a_new_lo",
|
||||
a_7,
|
||||
a_new_row,
|
||||
&a_new_dense.0.into(),
|
||||
&self.perm,
|
||||
)?;
|
||||
self.assign_and_constrain(
|
||||
region,
|
||||
|| "a_new_hi",
|
||||
a_7,
|
||||
a_new_row + 1,
|
||||
&a_new_dense.1.into(),
|
||||
&self.perm,
|
||||
)?;
|
||||
|
||||
// Assign and copy E_new
|
||||
let e_new_row = get_decompose_e_row(idx + 1);
|
||||
self.assign_and_constrain(
|
||||
region,
|
||||
|| "e_new_lo",
|
||||
a_7,
|
||||
e_new_row,
|
||||
&e_new_dense.0.into(),
|
||||
&self.perm,
|
||||
)?;
|
||||
self.assign_and_constrain(
|
||||
region,
|
||||
|| "e_new_hi",
|
||||
a_7,
|
||||
e_new_row + 1,
|
||||
&e_new_dense.1.into(),
|
||||
&self.perm,
|
||||
)?;
|
||||
|
||||
// Decompose A into (2, 11, 9, 10)-bit chunks
|
||||
let a_new = self.decompose_a(region, idx + 1, a_new_val)?;
|
||||
|
||||
// Decompose E into (6, 5, 14, 7)-bit chunks
|
||||
let e_new = self.decompose_e(region, idx + 1, e_new_val)?;
|
||||
|
||||
Ok(State::new(
|
||||
StateWord::A(a_new),
|
||||
StateWord::B(a.into()),
|
||||
StateWord::C(b),
|
||||
StateWord::D(c.into()),
|
||||
StateWord::E(e_new),
|
||||
StateWord::F(e.into()),
|
||||
StateWord::G(f),
|
||||
StateWord::H(g.into()),
|
||||
))
|
||||
} else {
|
||||
let abcd_row = get_digest_abcd_row();
|
||||
let efgh_row = get_digest_efgh_row();
|
||||
|
||||
let a_final =
|
||||
self.assign_word_halves_dense(region, abcd_row, a_3, abcd_row, a_4, a_new_val)?;
|
||||
|
||||
let e_final =
|
||||
self.assign_word_halves_dense(region, efgh_row, a_3, efgh_row, a_4, e_new_val)?;
|
||||
|
||||
Ok(State::new(
|
||||
StateWord::A(RoundWordA::new_dense(a_final)),
|
||||
StateWord::B(a.into()),
|
||||
StateWord::C(b),
|
||||
StateWord::D(c.into()),
|
||||
StateWord::E(RoundWordE::new_dense(e_final)),
|
||||
StateWord::F(e.into()),
|
||||
StateWord::G(f),
|
||||
StateWord::H(g.into()),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,117 @@
|
|||
use halo2::{arithmetic::FieldExt, plonk::Expression};
|
||||
|
||||
pub struct Gate<F: FieldExt>(pub Expression<F>);
|
||||
|
||||
impl<F: FieldExt> Gate<F> {
|
||||
fn ones() -> Expression<F> {
|
||||
Expression::Constant(F::one())
|
||||
}
|
||||
|
||||
// Helper gates
|
||||
fn lagrange_interpolate(
|
||||
var: Expression<F>,
|
||||
points: Vec<u16>,
|
||||
evals: Vec<u32>,
|
||||
) -> (F, Expression<F>) {
|
||||
assert_eq!(points.len(), evals.len());
|
||||
let deg = points.len();
|
||||
|
||||
fn factorial(n: u64) -> u64 {
|
||||
if n < 2 {
|
||||
1
|
||||
} else {
|
||||
n * factorial(n - 1)
|
||||
}
|
||||
}
|
||||
|
||||
// Scale the whole expression by factor to avoid divisions
|
||||
let factor = factorial((deg - 1) as u64);
|
||||
|
||||
let numerator = |var: Expression<F>, eval: u32, idx: u64| {
|
||||
let mut expr = Self::ones();
|
||||
for i in 0..deg {
|
||||
let i = i as u64;
|
||||
if i != idx {
|
||||
expr = expr * (Self::ones() * (-F::one()) * F::from_u64(i) + var.clone());
|
||||
}
|
||||
}
|
||||
expr * F::from_u64(eval.into())
|
||||
};
|
||||
let denominator = |idx: i32| {
|
||||
let mut denom: i32 = 1;
|
||||
for i in 0..deg {
|
||||
let i = i as i32;
|
||||
if i != idx {
|
||||
denom *= idx - i
|
||||
}
|
||||
}
|
||||
if denom < 0 {
|
||||
-F::one() * F::from_u64(factor / (-denom as u64))
|
||||
} else {
|
||||
F::from_u64(factor / (denom as u64))
|
||||
}
|
||||
};
|
||||
|
||||
let mut expr = Self::ones() * F::zero();
|
||||
for ((idx, _), eval) in points.iter().enumerate().zip(evals.iter()) {
|
||||
expr = expr + numerator(var.clone(), *eval, idx as u64) * denominator(idx as i32)
|
||||
}
|
||||
|
||||
(F::from_u64(factor), expr)
|
||||
}
|
||||
|
||||
pub fn range_check(value: Expression<F>, lower_range: u64, upper_range: u64) -> Expression<F> {
|
||||
let mut expr = Self::ones();
|
||||
for i in lower_range..(upper_range + 1) {
|
||||
expr = expr * (Self::ones() * (-F::one()) * F::from_u64(i) + value.clone())
|
||||
}
|
||||
expr
|
||||
}
|
||||
|
||||
// 2-bit range check
|
||||
fn two_bit_range_check(value: Expression<F>) -> Expression<F> {
|
||||
Self::range_check(value, 0, (1 << 2) - 1)
|
||||
}
|
||||
|
||||
// 2-bit spread interpolation
|
||||
fn two_bit_spread(dense: Expression<F>, spread: Expression<F>) -> Expression<F> {
|
||||
let (factor, lagrange_poly) = Self::lagrange_interpolate(
|
||||
dense,
|
||||
vec![0b00, 0b01, 0b10, 0b11],
|
||||
vec![0b0000, 0b0001, 0b0100, 0b0101],
|
||||
);
|
||||
|
||||
lagrange_poly + (spread * factor * (-F::one()))
|
||||
}
|
||||
|
||||
// 3-bit range check
|
||||
fn three_bit_range_check(value: Expression<F>) -> Expression<F> {
|
||||
Self::range_check(value, 0, (1 << 3) - 1)
|
||||
}
|
||||
|
||||
// 3-bit spread
|
||||
fn three_bit_spread(dense: Expression<F>, spread: Expression<F>) -> Expression<F> {
|
||||
let (factor, lagrange_poly) = Self::lagrange_interpolate(
|
||||
dense,
|
||||
vec![0b000, 0b001, 0b010, 0b011, 0b100, 0b101, 0b110, 0b111],
|
||||
vec![
|
||||
0b000000, 0b000001, 0b000100, 0b000101, 0b010000, 0b010001, 0b010100, 0b010101,
|
||||
],
|
||||
);
|
||||
|
||||
lagrange_poly + (spread * factor * (-F::one()))
|
||||
}
|
||||
|
||||
/// Spread and range check on 2-bit word
|
||||
pub fn two_bit_spread_and_range(dense: Expression<F>, spread: Expression<F>) -> Expression<F> {
|
||||
Self::two_bit_range_check(dense.clone()) + Self::two_bit_spread(dense, spread)
|
||||
}
|
||||
|
||||
/// Spread and range check on 3-bit word
|
||||
pub fn three_bit_spread_and_range(
|
||||
dense: Expression<F>,
|
||||
spread: Expression<F>,
|
||||
) -> Expression<F> {
|
||||
Self::three_bit_range_check(dense.clone()) + Self::three_bit_spread(dense, spread)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,597 @@
|
|||
use std::convert::TryInto;
|
||||
|
||||
use super::{
|
||||
super::BLOCK_SIZE, BlockWord, CellValue16, SpreadInputs, Table16Assignment, Table16Chip, ROUNDS,
|
||||
};
|
||||
use halo2::{
|
||||
arithmetic::FieldExt,
|
||||
circuit::{Cell, Layouter},
|
||||
plonk::{Advice, Column, ConstraintSystem, Error, Fixed, Permutation},
|
||||
poly::Rotation,
|
||||
};
|
||||
|
||||
mod schedule_gates;
|
||||
mod schedule_util;
|
||||
mod subregion1;
|
||||
mod subregion2;
|
||||
mod subregion3;
|
||||
|
||||
use schedule_gates::ScheduleGate;
|
||||
use schedule_util::*;
|
||||
|
||||
#[cfg(test)]
|
||||
pub use schedule_util::get_msg_schedule_test_input;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(super) struct MessageWord {
|
||||
var: Cell,
|
||||
value: Option<u32>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(super) struct MessageSchedule {
|
||||
lookup: SpreadInputs,
|
||||
message_schedule: Column<Advice>,
|
||||
extras: [Column<Advice>; 6],
|
||||
|
||||
/// Construct a word using reduce_4.
|
||||
s_word: Column<Fixed>,
|
||||
/// Decomposition gate for W_0, W_62, W_63.
|
||||
s_decompose_0: Column<Fixed>,
|
||||
/// Decomposition gate for W_[1..14]
|
||||
s_decompose_1: Column<Fixed>,
|
||||
/// Decomposition gate for W_[14..49]
|
||||
s_decompose_2: Column<Fixed>,
|
||||
/// Decomposition gate for W_[49..62]
|
||||
s_decompose_3: Column<Fixed>,
|
||||
/// sigma_0 gate for W_[1..14]
|
||||
s_lower_sigma_0: Column<Fixed>,
|
||||
/// sigma_1 gate for W_[49..62]
|
||||
s_lower_sigma_1: Column<Fixed>,
|
||||
/// sigma_0_v2 gate for W_[14..49]
|
||||
s_lower_sigma_0_v2: Column<Fixed>,
|
||||
/// sigma_1_v2 gate for W_[14..49]
|
||||
s_lower_sigma_1_v2: Column<Fixed>,
|
||||
perm: Permutation,
|
||||
}
|
||||
|
||||
impl<F: FieldExt> Table16Assignment<F> for MessageSchedule {}
|
||||
|
||||
impl MessageSchedule {
|
||||
/// Configures the message schedule.
|
||||
///
|
||||
/// `message_schedule` is the column into which the message schedule will be placed.
|
||||
/// The caller must create appropriate permutations in order to load schedule words
|
||||
/// into the compression rounds.
|
||||
///
|
||||
/// `extras` contains columns that the message schedule will only use for internal
|
||||
/// gates, and will not place any constraints on (such as lookup constraints) outside
|
||||
/// itself.
|
||||
#[allow(clippy::many_single_char_names)]
|
||||
pub(super) fn configure<F: FieldExt>(
|
||||
meta: &mut ConstraintSystem<F>,
|
||||
lookup: SpreadInputs,
|
||||
message_schedule: Column<Advice>,
|
||||
extras: [Column<Advice>; 6],
|
||||
perm: Permutation,
|
||||
) -> Self {
|
||||
// Create fixed columns for the selectors we will require.
|
||||
let s_word = meta.fixed_column();
|
||||
let s_decompose_0 = meta.fixed_column();
|
||||
let s_decompose_1 = meta.fixed_column();
|
||||
let s_decompose_2 = meta.fixed_column();
|
||||
let s_decompose_3 = meta.fixed_column();
|
||||
let s_lower_sigma_0 = meta.fixed_column();
|
||||
let s_lower_sigma_1 = meta.fixed_column();
|
||||
let s_lower_sigma_0_v2 = meta.fixed_column();
|
||||
let s_lower_sigma_1_v2 = meta.fixed_column();
|
||||
|
||||
// Rename these here for ease of matching the gates to the specification.
|
||||
let a_0 = lookup.tag;
|
||||
let a_1 = lookup.dense;
|
||||
let a_2 = lookup.spread;
|
||||
let a_3 = extras[0];
|
||||
let a_4 = extras[1];
|
||||
let a_5 = message_schedule;
|
||||
let a_6 = extras[2];
|
||||
let a_7 = extras[3];
|
||||
let a_8 = extras[4];
|
||||
let a_9 = extras[5];
|
||||
|
||||
// s_word for W_[16..64]
|
||||
meta.create_gate("s_word for W_[16..64]", |meta| {
|
||||
let s_word = meta.query_fixed(s_word, Rotation::cur());
|
||||
|
||||
let sigma_0_lo = meta.query_advice(a_6, Rotation::prev());
|
||||
let sigma_0_hi = meta.query_advice(a_6, Rotation::cur());
|
||||
|
||||
let sigma_1_lo = meta.query_advice(a_7, Rotation::prev());
|
||||
let sigma_1_hi = meta.query_advice(a_7, Rotation::cur());
|
||||
|
||||
let w_minus_9_lo = meta.query_advice(a_8, Rotation::prev());
|
||||
let w_minus_9_hi = meta.query_advice(a_8, Rotation::cur());
|
||||
|
||||
let w_minus_16_lo = meta.query_advice(a_3, Rotation::prev());
|
||||
let w_minus_16_hi = meta.query_advice(a_4, Rotation::prev());
|
||||
|
||||
let word = meta.query_advice(a_5, Rotation::cur());
|
||||
let carry = meta.query_advice(a_9, Rotation::cur());
|
||||
|
||||
ScheduleGate::s_word(
|
||||
s_word,
|
||||
sigma_0_lo,
|
||||
sigma_0_hi,
|
||||
sigma_1_lo,
|
||||
sigma_1_hi,
|
||||
w_minus_9_lo,
|
||||
w_minus_9_hi,
|
||||
w_minus_16_lo,
|
||||
w_minus_16_hi,
|
||||
word,
|
||||
carry,
|
||||
)
|
||||
.0
|
||||
});
|
||||
|
||||
// s_decompose_0 for all words
|
||||
meta.create_gate("s_decompose_0", |meta| {
|
||||
let s_decompose_0 = meta.query_fixed(s_decompose_0, Rotation::cur());
|
||||
let lo = meta.query_advice(a_3, Rotation::cur());
|
||||
let hi = meta.query_advice(a_4, Rotation::cur());
|
||||
let word = meta.query_advice(a_5, Rotation::cur());
|
||||
|
||||
ScheduleGate::s_decompose_0(s_decompose_0, lo, hi, word).0
|
||||
});
|
||||
|
||||
// s_decompose_1 for W_[1..14]
|
||||
// (3, 4, 11, 14)-bit chunks
|
||||
meta.create_gate("s_decompose_1", |meta| {
|
||||
let s_decompose_1 = meta.query_fixed(s_decompose_1, Rotation::cur());
|
||||
let a = meta.query_advice(a_3, Rotation::next()); // 3-bit chunk
|
||||
let b = meta.query_advice(a_4, Rotation::next()); // 4-bit chunk
|
||||
let c = meta.query_advice(a_1, Rotation::next()); // 11-bit chunk
|
||||
let tag_c = meta.query_advice(a_0, Rotation::next());
|
||||
let d = meta.query_advice(a_1, Rotation::cur()); // 14-bit chunk
|
||||
let tag_d = meta.query_advice(a_0, Rotation::cur());
|
||||
let word = meta.query_advice(a_5, Rotation::cur());
|
||||
|
||||
ScheduleGate::s_decompose_1(s_decompose_1, a, b, c, tag_c, d, tag_d, word).0
|
||||
});
|
||||
|
||||
// s_decompose_2 for W_[14..49]
|
||||
// (3, 4, 3, 7, 1, 1, 13)-bit chunks
|
||||
meta.create_gate("s_decompose_2", |meta| {
|
||||
let s_decompose_2 = meta.query_fixed(s_decompose_2, Rotation::cur());
|
||||
let a = meta.query_advice(a_3, Rotation::prev()); // 3-bit chunk
|
||||
let b = meta.query_advice(a_1, Rotation::next()); // 4-bit chunk
|
||||
let c = meta.query_advice(a_4, Rotation::prev()); // 3-bit chunk
|
||||
let d = meta.query_advice(a_1, Rotation::cur()); // 7-bit chunk
|
||||
let tag_d = meta.query_advice(a_0, Rotation::cur());
|
||||
let e = meta.query_advice(a_3, Rotation::next()); // 1-bit chunk
|
||||
let f = meta.query_advice(a_4, Rotation::next()); // 1-bit chunk
|
||||
let g = meta.query_advice(a_1, Rotation::prev()); // 13-bit chunk
|
||||
let tag_g = meta.query_advice(a_0, Rotation::prev());
|
||||
let word = meta.query_advice(a_5, Rotation::cur());
|
||||
|
||||
ScheduleGate::s_decompose_2(s_decompose_2, a, b, c, d, tag_d, e, f, g, tag_g, word).0
|
||||
});
|
||||
|
||||
// s_decompose_3 for W_49 to W_61
|
||||
// (10, 7, 2, 13)-bit chunks
|
||||
meta.create_gate("s_decompose_3", |meta| {
|
||||
let s_decompose_3 = meta.query_fixed(s_decompose_3, Rotation::cur());
|
||||
let a = meta.query_advice(a_1, Rotation::next()); // 10-bit chunk
|
||||
let tag_a = meta.query_advice(a_0, Rotation::next());
|
||||
let b = meta.query_advice(a_4, Rotation::next()); // 7-bit chunk
|
||||
let c = meta.query_advice(a_3, Rotation::next()); // 2-bit chunk
|
||||
let d = meta.query_advice(a_1, Rotation::cur()); // 13-bit chunk
|
||||
let tag_d = meta.query_advice(a_0, Rotation::cur());
|
||||
let word = meta.query_advice(a_5, Rotation::cur());
|
||||
|
||||
ScheduleGate::s_decompose_3(s_decompose_3, a, tag_a, b, c, d, tag_d, word).0
|
||||
});
|
||||
|
||||
// sigma_0 v1 on W_[1..14]
|
||||
// (3, 4, 11, 14)-bit chunks
|
||||
meta.create_gate("sigma_0 v1", |meta| {
|
||||
ScheduleGate::s_lower_sigma_0(
|
||||
meta.query_fixed(s_lower_sigma_0, Rotation::cur()), // s_lower_sigma_0
|
||||
meta.query_advice(a_2, Rotation::prev()), // spread_r0_even
|
||||
meta.query_advice(a_2, Rotation::cur()), // spread_r0_odd
|
||||
meta.query_advice(a_2, Rotation::next()), // spread_r1_even
|
||||
meta.query_advice(a_3, Rotation::cur()), // spread_r1_odd
|
||||
meta.query_advice(a_5, Rotation::next()), // a
|
||||
meta.query_advice(a_6, Rotation::next()), // spread_a
|
||||
meta.query_advice(a_6, Rotation::cur()), // b
|
||||
meta.query_advice(a_3, Rotation::prev()), // b_lo
|
||||
meta.query_advice(a_4, Rotation::prev()), // spread_b_lo
|
||||
meta.query_advice(a_5, Rotation::prev()), // b_hi
|
||||
meta.query_advice(a_6, Rotation::prev()), // spread_b_hi
|
||||
meta.query_advice(a_4, Rotation::cur()), // spread_c
|
||||
meta.query_advice(a_5, Rotation::cur()), // spread_d
|
||||
)
|
||||
.0
|
||||
});
|
||||
|
||||
// sigma_0 v2 on W_[14..49]
|
||||
// (3, 4, 3, 7, 1, 1, 13)-bit chunks
|
||||
meta.create_gate("sigma_0 v2", |meta| {
|
||||
ScheduleGate::s_lower_sigma_0_v2(
|
||||
meta.query_fixed(s_lower_sigma_0_v2, Rotation::cur()), // s_lower_sigma_0_v2
|
||||
meta.query_advice(a_2, Rotation::prev()), // spread_r0_even
|
||||
meta.query_advice(a_2, Rotation::cur()), // spread_r0_odd
|
||||
meta.query_advice(a_2, Rotation::next()), // spread_r1_even
|
||||
meta.query_advice(a_3, Rotation::cur()), // spread_r1_odd
|
||||
meta.query_advice(a_3, Rotation::next()), // a
|
||||
meta.query_advice(a_4, Rotation::next()), // spread_a
|
||||
meta.query_advice(a_6, Rotation::cur()), // b
|
||||
meta.query_advice(a_3, Rotation::prev()), // b_lo
|
||||
meta.query_advice(a_4, Rotation::prev()), // spread_b_lo
|
||||
meta.query_advice(a_5, Rotation::prev()), // b_hi
|
||||
meta.query_advice(a_6, Rotation::prev()), // spread_b_hi
|
||||
meta.query_advice(a_5, Rotation::next()), // c
|
||||
meta.query_advice(a_6, Rotation::next()), // spread_c
|
||||
meta.query_advice(a_4, Rotation::cur()), // spread_d
|
||||
meta.query_advice(a_7, Rotation::cur()), // spread_e
|
||||
meta.query_advice(a_7, Rotation::next()), // spread_f
|
||||
meta.query_advice(a_5, Rotation::cur()), // spread_g
|
||||
)
|
||||
.0
|
||||
});
|
||||
|
||||
// sigma_1 v2 on W_14 to W_48
|
||||
// (3, 4, 3, 7, 1, 1, 13)-bit chunks
|
||||
meta.create_gate("sigma_1 v2", |meta| {
|
||||
ScheduleGate::s_lower_sigma_1_v2(
|
||||
meta.query_fixed(s_lower_sigma_1_v2, Rotation::cur()), // s_lower_sigma_1_v2
|
||||
meta.query_advice(a_2, Rotation::prev()), // spread_r0_even
|
||||
meta.query_advice(a_2, Rotation::cur()), // spread_r0_odd
|
||||
meta.query_advice(a_2, Rotation::next()), // spread_r1_even
|
||||
meta.query_advice(a_3, Rotation::cur()), // spread_r1_odd
|
||||
meta.query_advice(a_3, Rotation::next()), // a
|
||||
meta.query_advice(a_4, Rotation::next()), // spread_a
|
||||
meta.query_advice(a_6, Rotation::cur()), // b
|
||||
meta.query_advice(a_3, Rotation::prev()), // b_lo
|
||||
meta.query_advice(a_4, Rotation::prev()), // spread_b_lo
|
||||
meta.query_advice(a_5, Rotation::prev()), // b_hi
|
||||
meta.query_advice(a_6, Rotation::prev()), // spread_b_hi
|
||||
meta.query_advice(a_5, Rotation::next()), // c
|
||||
meta.query_advice(a_6, Rotation::next()), // spread_c
|
||||
meta.query_advice(a_4, Rotation::cur()), // spread_d
|
||||
meta.query_advice(a_7, Rotation::cur()), // spread_e
|
||||
meta.query_advice(a_7, Rotation::next()), // spread_f
|
||||
meta.query_advice(a_5, Rotation::cur()), // spread_g
|
||||
)
|
||||
.0
|
||||
});
|
||||
|
||||
// sigma_1 v1 on W_49 to W_61
|
||||
// (10, 7, 2, 13)-bit chunks
|
||||
meta.create_gate("sigma_1 v1", |meta| {
|
||||
ScheduleGate::s_lower_sigma_1(
|
||||
meta.query_fixed(s_lower_sigma_1, Rotation::cur()), // s_lower_sigma_1
|
||||
meta.query_advice(a_2, Rotation::prev()), // spread_r0_even
|
||||
meta.query_advice(a_2, Rotation::cur()), // spread_r0_odd
|
||||
meta.query_advice(a_2, Rotation::next()), // spread_r1_even
|
||||
meta.query_advice(a_3, Rotation::cur()), // spread_r1_odd
|
||||
meta.query_advice(a_4, Rotation::cur()), // spread_a
|
||||
meta.query_advice(a_6, Rotation::cur()), // b
|
||||
meta.query_advice(a_3, Rotation::prev()), // b_lo
|
||||
meta.query_advice(a_4, Rotation::prev()), // spread_b_lo
|
||||
meta.query_advice(a_5, Rotation::prev()), // b_mid
|
||||
meta.query_advice(a_6, Rotation::prev()), // spread_b_mid
|
||||
meta.query_advice(a_5, Rotation::next()), // b_hi
|
||||
meta.query_advice(a_6, Rotation::next()), // spread_b_hi
|
||||
meta.query_advice(a_3, Rotation::next()), // c
|
||||
meta.query_advice(a_4, Rotation::next()), // spread_c
|
||||
meta.query_advice(a_5, Rotation::cur()), // spread_d
|
||||
)
|
||||
.0
|
||||
});
|
||||
|
||||
MessageSchedule {
|
||||
lookup,
|
||||
message_schedule,
|
||||
extras,
|
||||
s_word,
|
||||
s_decompose_0,
|
||||
s_decompose_1,
|
||||
s_decompose_2,
|
||||
s_decompose_3,
|
||||
s_lower_sigma_0,
|
||||
s_lower_sigma_1,
|
||||
s_lower_sigma_0_v2,
|
||||
s_lower_sigma_1_v2,
|
||||
perm,
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub(super) fn process<F: FieldExt>(
|
||||
&self,
|
||||
layouter: &mut impl Layouter<Table16Chip<F>>,
|
||||
input: [BlockWord; BLOCK_SIZE],
|
||||
) -> Result<([MessageWord; ROUNDS], [(CellValue16, CellValue16); ROUNDS]), Error> {
|
||||
let mut w = Vec::<MessageWord>::with_capacity(ROUNDS);
|
||||
let mut w_halves = Vec::<(CellValue16, CellValue16)>::with_capacity(ROUNDS);
|
||||
|
||||
layouter.assign_region(
|
||||
|| "process message block",
|
||||
|mut region| {
|
||||
w = Vec::<MessageWord>::with_capacity(ROUNDS);
|
||||
w_halves = Vec::<(CellValue16, CellValue16)>::with_capacity(ROUNDS);
|
||||
|
||||
// Assign all fixed columns
|
||||
for index in 1..14 {
|
||||
let row = get_word_row(index);
|
||||
region.assign_fixed(
|
||||
|| "s_decompose_1",
|
||||
self.s_decompose_1,
|
||||
row,
|
||||
|| Ok(F::one()),
|
||||
)?;
|
||||
region.assign_fixed(
|
||||
|| "s_lower_sigma_0",
|
||||
self.s_lower_sigma_0,
|
||||
row + 3,
|
||||
|| Ok(F::one()),
|
||||
)?;
|
||||
}
|
||||
|
||||
for index in 14..49 {
|
||||
let row = get_word_row(index);
|
||||
region.assign_fixed(
|
||||
|| "s_decompose_2",
|
||||
self.s_decompose_2,
|
||||
row,
|
||||
|| Ok(F::one()),
|
||||
)?;
|
||||
region.assign_fixed(
|
||||
|| "s_lower_sigma_0_v2",
|
||||
self.s_lower_sigma_0_v2,
|
||||
row + 3,
|
||||
|| Ok(F::one()),
|
||||
)?;
|
||||
region.assign_fixed(
|
||||
|| "s_lower_sigma_1_v2",
|
||||
self.s_lower_sigma_1_v2,
|
||||
row + SIGMA_0_V2_ROWS + 3,
|
||||
|| Ok(F::one()),
|
||||
)?;
|
||||
|
||||
let new_word_idx = index + 2;
|
||||
region.assign_fixed(
|
||||
|| "s_word",
|
||||
self.s_word,
|
||||
get_word_row(new_word_idx - 16) + 1,
|
||||
|| Ok(F::one()),
|
||||
)?;
|
||||
}
|
||||
|
||||
for index in 49..62 {
|
||||
let row = get_word_row(index);
|
||||
region.assign_fixed(
|
||||
|| "s_decompose_3",
|
||||
self.s_decompose_3,
|
||||
row,
|
||||
|| Ok(F::one()),
|
||||
)?;
|
||||
region.assign_fixed(
|
||||
|| "s_lower_sigma_1",
|
||||
self.s_lower_sigma_1,
|
||||
row + 3,
|
||||
|| Ok(F::one()),
|
||||
)?;
|
||||
|
||||
let new_word_idx = index + 2;
|
||||
region.assign_fixed(
|
||||
|| "s_word",
|
||||
self.s_word,
|
||||
get_word_row(new_word_idx - 16) + 1,
|
||||
|| Ok(F::one()),
|
||||
)?;
|
||||
}
|
||||
|
||||
for index in 0..64 {
|
||||
let row = get_word_row(index);
|
||||
region.assign_fixed(
|
||||
|| "s_decompose_0",
|
||||
self.s_decompose_0,
|
||||
row,
|
||||
|| Ok(F::one()),
|
||||
)?;
|
||||
}
|
||||
|
||||
// Assign W[0..16]
|
||||
for (i, word) in input.iter().enumerate() {
|
||||
let (var, halves) =
|
||||
self.assign_word_and_halves(&mut region, word.value.unwrap(), i)?;
|
||||
w.push(MessageWord {
|
||||
var,
|
||||
value: word.value,
|
||||
});
|
||||
w_halves.push(halves);
|
||||
}
|
||||
|
||||
// Returns the output of sigma_0 on W_[1..14]
|
||||
let lower_sigma_0_output = self.assign_subregion1(&mut region, &input[1..14])?;
|
||||
|
||||
// sigma_0_v2 and sigma_1_v2 on W_[14..49]
|
||||
// Returns the output of sigma_0_v2 on W_[36..49], to be used in subregion3
|
||||
let lower_sigma_0_v2_output = self.assign_subregion2(
|
||||
&mut region,
|
||||
lower_sigma_0_output,
|
||||
&mut w,
|
||||
&mut w_halves,
|
||||
)?;
|
||||
|
||||
// sigma_1 v1 on W[49..62]
|
||||
self.assign_subregion3(
|
||||
&mut region,
|
||||
lower_sigma_0_v2_output,
|
||||
&mut w,
|
||||
&mut w_halves,
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
},
|
||||
)?;
|
||||
|
||||
Ok((w.try_into().unwrap(), w_halves.try_into().unwrap()))
|
||||
}
|
||||
|
||||
/// Empty configuration without gates. Useful for fast testing
|
||||
#[cfg(test)]
|
||||
pub(super) fn empty_configure<F: FieldExt>(
|
||||
meta: &mut ConstraintSystem<F>,
|
||||
lookup: SpreadInputs,
|
||||
message_schedule: Column<Advice>,
|
||||
extras: [Column<Advice>; 6],
|
||||
perm: Permutation,
|
||||
) -> Self {
|
||||
// Create fixed columns for the selectors we will require.
|
||||
let s_word = meta.fixed_column();
|
||||
let s_decompose_0 = meta.fixed_column();
|
||||
let s_decompose_1 = meta.fixed_column();
|
||||
let s_decompose_2 = meta.fixed_column();
|
||||
let s_decompose_3 = meta.fixed_column();
|
||||
let s_lower_sigma_0 = meta.fixed_column();
|
||||
let s_lower_sigma_1 = meta.fixed_column();
|
||||
let s_lower_sigma_0_v2 = meta.fixed_column();
|
||||
let s_lower_sigma_1_v2 = meta.fixed_column();
|
||||
|
||||
MessageSchedule {
|
||||
lookup,
|
||||
message_schedule,
|
||||
extras,
|
||||
s_word,
|
||||
s_decompose_0,
|
||||
s_decompose_1,
|
||||
s_decompose_2,
|
||||
s_decompose_3,
|
||||
s_lower_sigma_0,
|
||||
s_lower_sigma_1,
|
||||
s_lower_sigma_0_v2,
|
||||
s_lower_sigma_1_v2,
|
||||
perm,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::super::{
|
||||
super::BLOCK_SIZE, BlockWord, Compression, SpreadTable, Table16Chip, Table16Config,
|
||||
};
|
||||
use super::{schedule_util::*, MessageSchedule};
|
||||
use halo2::{
|
||||
arithmetic::FieldExt,
|
||||
circuit::{layouter, Layouter},
|
||||
dev::MockProver,
|
||||
pasta::Fp,
|
||||
plonk::{Assignment, Circuit, ConstraintSystem, Error, Permutation},
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn message_schedule() {
|
||||
struct MyCircuit {}
|
||||
|
||||
impl<F: FieldExt> Circuit<F> for MyCircuit {
|
||||
type Config = Table16Config;
|
||||
|
||||
fn configure(meta: &mut ConstraintSystem<F>) -> Self::Config {
|
||||
let a = meta.advice_column();
|
||||
let b = meta.advice_column();
|
||||
let c = meta.advice_column();
|
||||
|
||||
let (lookup_inputs, lookup_table) = SpreadTable::configure(meta, a, b, c);
|
||||
|
||||
let message_schedule = meta.advice_column();
|
||||
let extras = [
|
||||
meta.advice_column(),
|
||||
meta.advice_column(),
|
||||
meta.advice_column(),
|
||||
meta.advice_column(),
|
||||
meta.advice_column(),
|
||||
meta.advice_column(),
|
||||
];
|
||||
|
||||
// Rename these here for ease of matching the gates to the specification.
|
||||
let _a_0 = lookup_inputs.tag;
|
||||
let a_1 = lookup_inputs.dense;
|
||||
let a_2 = lookup_inputs.spread;
|
||||
let a_3 = extras[0];
|
||||
let a_4 = extras[1];
|
||||
let a_5 = message_schedule;
|
||||
let a_6 = extras[2];
|
||||
let a_7 = extras[3];
|
||||
let a_8 = extras[4];
|
||||
let _a_9 = extras[5];
|
||||
|
||||
let perm = Permutation::new(
|
||||
meta,
|
||||
&[
|
||||
a_1.into(),
|
||||
a_2.into(),
|
||||
a_3.into(),
|
||||
a_4.into(),
|
||||
a_5.into(),
|
||||
a_6.into(),
|
||||
a_7.into(),
|
||||
a_8.into(),
|
||||
],
|
||||
);
|
||||
|
||||
let compression = Compression::empty_configure(
|
||||
meta,
|
||||
lookup_inputs.clone(),
|
||||
message_schedule,
|
||||
extras,
|
||||
perm.clone(),
|
||||
);
|
||||
|
||||
let message_schedule =
|
||||
MessageSchedule::configure(meta, lookup_inputs, message_schedule, extras, perm);
|
||||
|
||||
Table16Config {
|
||||
lookup_table,
|
||||
message_schedule,
|
||||
compression,
|
||||
}
|
||||
}
|
||||
|
||||
fn synthesize(
|
||||
&self,
|
||||
cs: &mut impl Assignment<F>,
|
||||
config: Self::Config,
|
||||
) -> Result<(), Error> {
|
||||
let mut layouter = layouter::SingleChip::<Table16Chip<F>, _>::new(cs, config)?;
|
||||
|
||||
// Load table
|
||||
let table = layouter.config().lookup_table.clone();
|
||||
table.load(&mut layouter)?;
|
||||
|
||||
// Provide input
|
||||
// Test vector: "abc"
|
||||
let inputs: [BlockWord; BLOCK_SIZE] = get_msg_schedule_test_input();
|
||||
|
||||
// Run message_scheduler to get W_[0..64]
|
||||
let message_schedule = layouter.config().message_schedule.clone();
|
||||
let (w, _) = message_schedule.process(&mut layouter, inputs)?;
|
||||
for (word, test_word) in w.iter().zip(MSG_SCHEDULE_TEST_OUTPUT.iter()) {
|
||||
let word = word.value.unwrap();
|
||||
assert_eq!(word, *test_word);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
let circuit: MyCircuit = MyCircuit {};
|
||||
|
||||
let prover = match MockProver::<Fp>::run(16, &circuit, vec![]) {
|
||||
Ok(prover) => prover,
|
||||
Err(e) => panic!("{:?}", e),
|
||||
};
|
||||
assert_eq!(prover.verify(), Ok(()));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,364 @@
|
|||
use super::super::Gate;
|
||||
use halo2::{arithmetic::FieldExt, plonk::Expression};
|
||||
|
||||
pub struct ScheduleGate<F: FieldExt>(pub Expression<F>);
|
||||
|
||||
impl<F: FieldExt> ScheduleGate<F> {
|
||||
/// s_word for W_16 to W_63
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn s_word(
|
||||
s_word: Expression<F>,
|
||||
sigma_0_lo: Expression<F>,
|
||||
sigma_0_hi: Expression<F>,
|
||||
sigma_1_lo: Expression<F>,
|
||||
sigma_1_hi: Expression<F>,
|
||||
w_minus_9_lo: Expression<F>,
|
||||
w_minus_9_hi: Expression<F>,
|
||||
w_minus_16_lo: Expression<F>,
|
||||
w_minus_16_hi: Expression<F>,
|
||||
word: Expression<F>,
|
||||
carry: Expression<F>,
|
||||
) -> Self {
|
||||
let lo = sigma_0_lo + sigma_1_lo + w_minus_9_lo + w_minus_16_lo;
|
||||
let hi = sigma_0_hi + sigma_1_hi + w_minus_9_hi + w_minus_16_hi;
|
||||
|
||||
let word_check = lo
|
||||
+ hi * F::from_u64(1 << 16)
|
||||
+ (carry.clone() * F::from_u64(1 << 32) * (-F::one()))
|
||||
+ (word * (-F::one()));
|
||||
let carry_check = Gate::range_check(carry, 0, 3);
|
||||
|
||||
ScheduleGate(s_word * (word_check + carry_check))
|
||||
}
|
||||
|
||||
/// s_decompose_0 for all words
|
||||
pub fn s_decompose_0(
|
||||
s_decompose_0: Expression<F>,
|
||||
lo: Expression<F>,
|
||||
hi: Expression<F>,
|
||||
word: Expression<F>,
|
||||
) -> Self {
|
||||
ScheduleGate(s_decompose_0 * (lo + hi * F::from_u64(1 << 16) + word * (-F::one())))
|
||||
}
|
||||
|
||||
/// s_decompose_1 for W_1 to W_13
|
||||
/// (3, 4, 11, 14)-bit chunks
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn s_decompose_1(
|
||||
s_decompose_1: Expression<F>,
|
||||
a: Expression<F>,
|
||||
b: Expression<F>,
|
||||
c: Expression<F>,
|
||||
tag_c: Expression<F>,
|
||||
d: Expression<F>,
|
||||
tag_d: Expression<F>,
|
||||
word: Expression<F>,
|
||||
) -> Self {
|
||||
let decompose_check = a
|
||||
+ b * F::from_u64(1 << 3)
|
||||
+ c * F::from_u64(1 << 7)
|
||||
+ d * F::from_u64(1 << 18)
|
||||
+ word * (-F::one());
|
||||
let range_check_tag_c = Gate::range_check(tag_c, 0, 2);
|
||||
let range_check_tag_d = Gate::range_check(tag_d, 0, 4);
|
||||
ScheduleGate(s_decompose_1 * (decompose_check + range_check_tag_c + range_check_tag_d))
|
||||
}
|
||||
|
||||
/// s_decompose_2 for W_14 to W_48
|
||||
/// (3, 4, 3, 7, 1, 1, 13)-bit chunks
|
||||
#[allow(clippy::many_single_char_names)]
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn s_decompose_2(
|
||||
s_decompose_2: Expression<F>,
|
||||
a: Expression<F>,
|
||||
b: Expression<F>,
|
||||
c: Expression<F>,
|
||||
d: Expression<F>,
|
||||
tag_d: Expression<F>,
|
||||
e: Expression<F>,
|
||||
f: Expression<F>,
|
||||
g: Expression<F>,
|
||||
tag_g: Expression<F>,
|
||||
word: Expression<F>,
|
||||
) -> Self {
|
||||
let decompose_check = a
|
||||
+ b * F::from_u64(1 << 3)
|
||||
+ c * F::from_u64(1 << 7)
|
||||
+ d * F::from_u64(1 << 10)
|
||||
+ e * F::from_u64(1 << 17)
|
||||
+ f * F::from_u64(1 << 18)
|
||||
+ g * F::from_u64(1 << 19)
|
||||
+ word * (-F::one());
|
||||
let range_check_tag_d = Gate::range_check(tag_d, 0, 0);
|
||||
let range_check_tag_g = Gate::range_check(tag_g, 0, 3);
|
||||
ScheduleGate(s_decompose_2 * (decompose_check + range_check_tag_g + range_check_tag_d))
|
||||
}
|
||||
|
||||
/// s_decompose_3 for W_49 to W_61
|
||||
/// (10, 7, 2, 13)-bit chunks
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn s_decompose_3(
|
||||
s_decompose_3: Expression<F>,
|
||||
a: Expression<F>,
|
||||
tag_a: Expression<F>,
|
||||
b: Expression<F>,
|
||||
c: Expression<F>,
|
||||
d: Expression<F>,
|
||||
tag_d: Expression<F>,
|
||||
word: Expression<F>,
|
||||
) -> Self {
|
||||
let decompose_check = a
|
||||
+ b * F::from_u64(1 << 10)
|
||||
+ c * F::from_u64(1 << 17)
|
||||
+ d * F::from_u64(1 << 19)
|
||||
+ word * (-F::one());
|
||||
let range_check_tag_a = Gate::range_check(tag_a, 0, 1);
|
||||
let range_check_tag_d = Gate::range_check(tag_d, 0, 3);
|
||||
|
||||
ScheduleGate(s_decompose_3 * (decompose_check + range_check_tag_a + range_check_tag_d))
|
||||
}
|
||||
|
||||
/// b_lo + 2^2 * b_mid = b, on W_[1..49]
|
||||
fn check_b(b: Expression<F>, b_lo: Expression<F>, b_hi: Expression<F>) -> Expression<F> {
|
||||
let expected_b = b_lo + b_hi * F::from_u64(1 << 2);
|
||||
expected_b + (b * -F::one())
|
||||
}
|
||||
|
||||
/// b_lo + 2^2 * b_mid + 2^4 * b_hi = b, on W_[49..62]
|
||||
fn check_b1(
|
||||
b: Expression<F>,
|
||||
b_lo: Expression<F>,
|
||||
b_mid: Expression<F>,
|
||||
b_hi: Expression<F>,
|
||||
) -> Expression<F> {
|
||||
let expected_b = b_lo + b_mid * F::from_u64(1 << 2) + b_hi * F::from_u64(1 << 4);
|
||||
expected_b + (b * -F::one())
|
||||
}
|
||||
|
||||
/// sigma_0 v1 on W_1 to W_13
|
||||
/// (3, 4, 11, 14)-bit chunks
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn s_lower_sigma_0(
|
||||
s_lower_sigma_0: Expression<F>,
|
||||
spread_r0_even: Expression<F>,
|
||||
spread_r0_odd: Expression<F>,
|
||||
spread_r1_even: Expression<F>,
|
||||
spread_r1_odd: Expression<F>,
|
||||
a: Expression<F>,
|
||||
spread_a: Expression<F>,
|
||||
b: Expression<F>,
|
||||
b_lo: Expression<F>,
|
||||
spread_b_lo: Expression<F>,
|
||||
b_hi: Expression<F>,
|
||||
spread_b_hi: Expression<F>,
|
||||
spread_c: Expression<F>,
|
||||
spread_d: Expression<F>,
|
||||
) -> Self {
|
||||
let check_spread_and_range =
|
||||
Gate::two_bit_spread_and_range(b_lo.clone(), spread_b_lo.clone())
|
||||
+ Gate::two_bit_spread_and_range(b_hi.clone(), spread_b_hi.clone())
|
||||
+ Gate::three_bit_spread_and_range(a, spread_a.clone());
|
||||
let check_b = Self::check_b(b, b_lo, b_hi);
|
||||
let spread_witness = spread_r0_even
|
||||
+ spread_r0_odd * F::from_u64(2)
|
||||
+ (spread_r1_even + spread_r1_odd * F::from_u64(2)) * F::from_u64(1 << 32);
|
||||
let xor_0 = spread_b_lo.clone()
|
||||
+ spread_b_hi.clone() * F::from_u64(1 << 4)
|
||||
+ spread_c.clone() * F::from_u64(1 << 8)
|
||||
+ spread_d.clone() * F::from_u64(1 << 30);
|
||||
let xor_1 = spread_c.clone()
|
||||
+ spread_d.clone() * F::from_u64(1 << 22)
|
||||
+ spread_a.clone() * F::from_u64(1 << 50)
|
||||
+ spread_b_lo.clone() * F::from_u64(1 << 56)
|
||||
+ spread_b_hi.clone() * F::from_u64(1 << 60);
|
||||
let xor_2 = spread_d
|
||||
+ spread_a * F::from_u64(1 << 28)
|
||||
+ spread_b_lo * F::from_u64(1 << 34)
|
||||
+ spread_b_hi * F::from_u64(1 << 38)
|
||||
+ spread_c * F::from_u64(1 << 42);
|
||||
let xor = xor_0 + xor_1 + xor_2;
|
||||
|
||||
ScheduleGate(
|
||||
s_lower_sigma_0
|
||||
* (check_spread_and_range + check_b + spread_witness + (xor * -F::one())),
|
||||
)
|
||||
}
|
||||
|
||||
/// sigma_1 v1 on W_49 to W_61
|
||||
/// (10, 7, 2, 13)-bit chunks
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn s_lower_sigma_1(
|
||||
s_lower_sigma_1: Expression<F>,
|
||||
spread_r0_even: Expression<F>,
|
||||
spread_r0_odd: Expression<F>,
|
||||
spread_r1_even: Expression<F>,
|
||||
spread_r1_odd: Expression<F>,
|
||||
spread_a: Expression<F>,
|
||||
b: Expression<F>,
|
||||
b_lo: Expression<F>,
|
||||
spread_b_lo: Expression<F>,
|
||||
b_mid: Expression<F>,
|
||||
spread_b_mid: Expression<F>,
|
||||
b_hi: Expression<F>,
|
||||
spread_b_hi: Expression<F>,
|
||||
c: Expression<F>,
|
||||
spread_c: Expression<F>,
|
||||
spread_d: Expression<F>,
|
||||
) -> Self {
|
||||
let check_spread_and_range =
|
||||
Gate::two_bit_spread_and_range(b_lo.clone(), spread_b_lo.clone())
|
||||
+ Gate::two_bit_spread_and_range(b_mid.clone(), spread_b_mid.clone())
|
||||
+ Gate::two_bit_spread_and_range(c, spread_c.clone())
|
||||
+ Gate::three_bit_spread_and_range(b_hi.clone(), spread_b_hi.clone());
|
||||
let check_b1 = Self::check_b1(b, b_lo, b_mid, b_hi);
|
||||
let spread_witness = spread_r0_even
|
||||
+ spread_r0_odd * F::from_u64(2)
|
||||
+ (spread_r1_even + spread_r1_odd * F::from_u64(2)) * F::from_u64(1 << 32);
|
||||
let xor_0 = spread_b_lo.clone()
|
||||
+ spread_b_mid.clone() * F::from_u64(1 << 4)
|
||||
+ spread_b_hi.clone() * F::from_u64(1 << 8)
|
||||
+ spread_c.clone() * F::from_u64(1 << 14)
|
||||
+ spread_d.clone() * F::from_u64(1 << 18);
|
||||
let xor_1 = spread_c.clone()
|
||||
+ spread_d.clone() * F::from_u64(1 << 4)
|
||||
+ spread_a.clone() * F::from_u64(1 << 30)
|
||||
+ spread_b_lo.clone() * F::from_u64(1 << 50)
|
||||
+ spread_b_mid.clone() * F::from_u64(1 << 54)
|
||||
+ spread_b_hi.clone() * F::from_u64(1 << 58);
|
||||
let xor_2 = spread_d
|
||||
+ spread_a * F::from_u64(1 << 26)
|
||||
+ spread_b_lo * F::from_u64(1 << 46)
|
||||
+ spread_b_mid * F::from_u64(1 << 50)
|
||||
+ spread_b_hi * F::from_u64(1 << 54)
|
||||
+ spread_c * F::from_u64(1 << 60);
|
||||
let xor = xor_0 + xor_1 + xor_2;
|
||||
|
||||
ScheduleGate(
|
||||
s_lower_sigma_1
|
||||
* (check_spread_and_range + check_b1 + spread_witness + (xor * -F::one())),
|
||||
)
|
||||
}
|
||||
|
||||
/// sigma_0 v2 on W_14 to W_48
|
||||
/// (3, 4, 3, 7, 1, 1, 13)-bit chunks
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn s_lower_sigma_0_v2(
|
||||
s_lower_sigma_0_v2: Expression<F>,
|
||||
spread_r0_even: Expression<F>,
|
||||
spread_r0_odd: Expression<F>,
|
||||
spread_r1_even: Expression<F>,
|
||||
spread_r1_odd: Expression<F>,
|
||||
a: Expression<F>,
|
||||
spread_a: Expression<F>,
|
||||
b: Expression<F>,
|
||||
b_lo: Expression<F>,
|
||||
spread_b_lo: Expression<F>,
|
||||
b_hi: Expression<F>,
|
||||
spread_b_hi: Expression<F>,
|
||||
c: Expression<F>,
|
||||
spread_c: Expression<F>,
|
||||
spread_d: Expression<F>,
|
||||
spread_e: Expression<F>,
|
||||
spread_f: Expression<F>,
|
||||
spread_g: Expression<F>,
|
||||
) -> Self {
|
||||
let check_spread_and_range =
|
||||
Gate::two_bit_spread_and_range(b_lo.clone(), spread_b_lo.clone())
|
||||
+ Gate::two_bit_spread_and_range(b_hi.clone(), spread_b_hi.clone())
|
||||
+ Gate::three_bit_spread_and_range(a, spread_a.clone())
|
||||
+ Gate::three_bit_spread_and_range(c, spread_c.clone());
|
||||
let check_b = Self::check_b(b, b_lo, b_hi);
|
||||
let spread_witness = spread_r0_even
|
||||
+ spread_r0_odd * F::from_u64(2)
|
||||
+ (spread_r1_even + spread_r1_odd * F::from_u64(2)) * F::from_u64(1 << 32);
|
||||
let xor_0 = spread_b_lo.clone()
|
||||
+ spread_b_hi.clone() * F::from_u64(1 << 4)
|
||||
+ spread_c.clone() * F::from_u64(1 << 8)
|
||||
+ spread_d.clone() * F::from_u64(1 << 14)
|
||||
+ spread_e.clone() * F::from_u64(1 << 28)
|
||||
+ spread_f.clone() * F::from_u64(1 << 30)
|
||||
+ spread_g.clone() * F::from_u64(1 << 32);
|
||||
let xor_1 = spread_c.clone()
|
||||
+ spread_d.clone() * F::from_u64(1 << 6)
|
||||
+ spread_e.clone() * F::from_u64(1 << 20)
|
||||
+ spread_f.clone() * F::from_u64(1 << 22)
|
||||
+ spread_g.clone() * F::from_u64(1 << 24)
|
||||
+ spread_a.clone() * F::from_u64(1 << 50)
|
||||
+ spread_b_lo.clone() * F::from_u64(1 << 56)
|
||||
+ spread_b_hi.clone() * F::from_u64(1 << 60);
|
||||
let xor_2 = spread_f
|
||||
+ spread_g * F::from_u64(1 << 2)
|
||||
+ spread_a * F::from_u64(1 << 28)
|
||||
+ spread_b_lo * F::from_u64(1 << 34)
|
||||
+ spread_b_hi * F::from_u64(1 << 38)
|
||||
+ spread_c * F::from_u64(1 << 42)
|
||||
+ spread_d * F::from_u64(1 << 48)
|
||||
+ spread_e * F::from_u64(1 << 62);
|
||||
let xor = xor_0 + xor_1 + xor_2;
|
||||
|
||||
ScheduleGate(
|
||||
s_lower_sigma_0_v2
|
||||
* (check_spread_and_range + check_b + spread_witness + (xor * -F::one())),
|
||||
)
|
||||
}
|
||||
|
||||
/// sigma_1 v2 on W_14 to W_48
|
||||
/// (3, 4, 3, 7, 1, 1, 13)-bit chunks
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn s_lower_sigma_1_v2(
|
||||
s_lower_sigma_1_v2: Expression<F>,
|
||||
spread_r0_even: Expression<F>,
|
||||
spread_r0_odd: Expression<F>,
|
||||
spread_r1_even: Expression<F>,
|
||||
spread_r1_odd: Expression<F>,
|
||||
a: Expression<F>,
|
||||
spread_a: Expression<F>,
|
||||
b: Expression<F>,
|
||||
b_lo: Expression<F>,
|
||||
spread_b_lo: Expression<F>,
|
||||
b_hi: Expression<F>,
|
||||
spread_b_hi: Expression<F>,
|
||||
c: Expression<F>,
|
||||
spread_c: Expression<F>,
|
||||
spread_d: Expression<F>,
|
||||
spread_e: Expression<F>,
|
||||
spread_f: Expression<F>,
|
||||
spread_g: Expression<F>,
|
||||
) -> Self {
|
||||
let check_spread_and_range =
|
||||
Gate::two_bit_spread_and_range(b_lo.clone(), spread_b_lo.clone())
|
||||
+ Gate::two_bit_spread_and_range(b_hi.clone(), spread_b_hi.clone())
|
||||
+ Gate::three_bit_spread_and_range(a, spread_a.clone())
|
||||
+ Gate::three_bit_spread_and_range(c, spread_c.clone());
|
||||
let check_b = Self::check_b(b, b_lo, b_hi);
|
||||
let spread_witness = spread_r0_even
|
||||
+ spread_r0_odd * F::from_u64(2)
|
||||
+ (spread_r1_even + spread_r1_odd * F::from_u64(2)) * F::from_u64(1 << 32);
|
||||
let xor_0 = spread_d.clone()
|
||||
+ spread_e.clone() * F::from_u64(1 << 14)
|
||||
+ spread_f.clone() * F::from_u64(1 << 16)
|
||||
+ spread_g.clone() * F::from_u64(1 << 18);
|
||||
let xor_1 = spread_e.clone()
|
||||
+ spread_f.clone() * F::from_u64(1 << 2)
|
||||
+ spread_g.clone() * F::from_u64(1 << 4)
|
||||
+ spread_a.clone() * F::from_u64(1 << 30)
|
||||
+ spread_b_lo.clone() * F::from_u64(1 << 36)
|
||||
+ spread_b_hi.clone() * F::from_u64(1 << 40)
|
||||
+ spread_c.clone() * F::from_u64(1 << 44)
|
||||
+ spread_d.clone() * F::from_u64(1 << 50);
|
||||
let xor_2 = spread_g
|
||||
+ spread_a * F::from_u64(1 << 26)
|
||||
+ spread_b_lo * F::from_u64(1 << 32)
|
||||
+ spread_b_hi * F::from_u64(1 << 36)
|
||||
+ spread_c * F::from_u64(1 << 40)
|
||||
+ spread_d * F::from_u64(1 << 46)
|
||||
+ spread_e * F::from_u64(1 << 60)
|
||||
+ spread_f * F::from_u64(1 << 62);
|
||||
let xor = xor_0 + xor_1 + xor_2;
|
||||
|
||||
ScheduleGate(
|
||||
s_lower_sigma_1_v2
|
||||
* (check_spread_and_range + check_b + spread_witness + (xor * -F::one())),
|
||||
)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,196 @@
|
|||
use super::super::{CellValue16, Table16Chip};
|
||||
use super::MessageSchedule;
|
||||
use halo2::{
|
||||
arithmetic::FieldExt,
|
||||
circuit::{Cell, Region},
|
||||
plonk::Error,
|
||||
};
|
||||
|
||||
#[cfg(test)]
|
||||
use super::super::{super::BLOCK_SIZE, BlockWord, ROUNDS};
|
||||
|
||||
// Rows needed for each gate
|
||||
pub const DECOMPOSE_0_ROWS: usize = 2;
|
||||
pub const DECOMPOSE_1_ROWS: usize = 2;
|
||||
pub const DECOMPOSE_2_ROWS: usize = 3;
|
||||
pub const DECOMPOSE_3_ROWS: usize = 2;
|
||||
pub const SIGMA_0_V1_ROWS: usize = 4;
|
||||
pub const SIGMA_0_V2_ROWS: usize = 4;
|
||||
pub const SIGMA_1_V1_ROWS: usize = 4;
|
||||
pub const SIGMA_1_V2_ROWS: usize = 4;
|
||||
|
||||
// Rows needed for each subregion
|
||||
pub const SUBREGION_0_LEN: usize = 1; // W_0
|
||||
pub const SUBREGION_0_ROWS: usize = SUBREGION_0_LEN * DECOMPOSE_0_ROWS;
|
||||
pub const SUBREGION_1_WORD: usize = DECOMPOSE_1_ROWS + SIGMA_0_V1_ROWS;
|
||||
pub const SUBREGION_1_LEN: usize = 13; // W_[1..14]
|
||||
pub const SUBREGION_1_ROWS: usize = SUBREGION_1_LEN * SUBREGION_1_WORD;
|
||||
pub const SUBREGION_2_WORD: usize = DECOMPOSE_2_ROWS + SIGMA_0_V2_ROWS + SIGMA_1_V2_ROWS;
|
||||
pub const SUBREGION_2_LEN: usize = 35; // W_[14..49]
|
||||
pub const SUBREGION_2_ROWS: usize = SUBREGION_2_LEN * SUBREGION_2_WORD;
|
||||
pub const SUBREGION_3_WORD: usize = DECOMPOSE_3_ROWS + SIGMA_1_V1_ROWS;
|
||||
pub const SUBREGION_3_LEN: usize = 13; // W[49..62]
|
||||
pub const SUBREGION_3_ROWS: usize = SUBREGION_3_LEN * SUBREGION_3_WORD;
|
||||
// pub const SUBREGION_4_LEN: usize = 2; // W_[62..64]
|
||||
// pub const SUBREGION_4_ROWS: usize = SUBREGION_4_LEN * DECOMPOSE_0_ROWS;
|
||||
|
||||
/// Returns row number of a word
|
||||
pub fn get_word_row(word_idx: usize) -> usize {
|
||||
assert!(word_idx <= 63);
|
||||
if word_idx == 0 {
|
||||
0
|
||||
} else if (1..=13).contains(&word_idx) {
|
||||
SUBREGION_0_ROWS + SUBREGION_1_WORD * (word_idx - 1) as usize
|
||||
} else if (14..=48).contains(&word_idx) {
|
||||
SUBREGION_0_ROWS + SUBREGION_1_ROWS + SUBREGION_2_WORD * (word_idx - 14) + 1
|
||||
} else if (49..=61).contains(&word_idx) {
|
||||
SUBREGION_0_ROWS
|
||||
+ SUBREGION_1_ROWS
|
||||
+ SUBREGION_2_ROWS
|
||||
+ SUBREGION_3_WORD * (word_idx - 49) as usize
|
||||
} else {
|
||||
SUBREGION_0_ROWS
|
||||
+ SUBREGION_1_ROWS
|
||||
+ SUBREGION_2_ROWS
|
||||
+ SUBREGION_3_ROWS
|
||||
+ DECOMPOSE_0_ROWS * (word_idx - 62) as usize
|
||||
}
|
||||
}
|
||||
|
||||
/// Test vector: "abc"
|
||||
#[cfg(test)]
|
||||
pub fn get_msg_schedule_test_input() -> [BlockWord; BLOCK_SIZE] {
|
||||
[
|
||||
BlockWord::new(0b01100001011000100110001110000000),
|
||||
BlockWord::new(0b00000000000000000000000000000000),
|
||||
BlockWord::new(0b00000000000000000000000000000000),
|
||||
BlockWord::new(0b00000000000000000000000000000000),
|
||||
BlockWord::new(0b00000000000000000000000000000000),
|
||||
BlockWord::new(0b00000000000000000000000000000000),
|
||||
BlockWord::new(0b00000000000000000000000000000000),
|
||||
BlockWord::new(0b00000000000000000000000000000000),
|
||||
BlockWord::new(0b00000000000000000000000000000000),
|
||||
BlockWord::new(0b00000000000000000000000000000000),
|
||||
BlockWord::new(0b00000000000000000000000000000000),
|
||||
BlockWord::new(0b00000000000000000000000000000000),
|
||||
BlockWord::new(0b00000000000000000000000000000000),
|
||||
BlockWord::new(0b00000000000000000000000000000000),
|
||||
BlockWord::new(0b00000000000000000000000000000000),
|
||||
BlockWord::new(0b00000000000000000000000000011000),
|
||||
]
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub const MSG_SCHEDULE_TEST_OUTPUT: [u32; ROUNDS] = [
|
||||
0b01100001011000100110001110000000,
|
||||
0b00000000000000000000000000000000,
|
||||
0b00000000000000000000000000000000,
|
||||
0b00000000000000000000000000000000,
|
||||
0b00000000000000000000000000000000,
|
||||
0b00000000000000000000000000000000,
|
||||
0b00000000000000000000000000000000,
|
||||
0b00000000000000000000000000000000,
|
||||
0b00000000000000000000000000000000,
|
||||
0b00000000000000000000000000000000,
|
||||
0b00000000000000000000000000000000,
|
||||
0b00000000000000000000000000000000,
|
||||
0b00000000000000000000000000000000,
|
||||
0b00000000000000000000000000000000,
|
||||
0b00000000000000000000000000000000,
|
||||
0b00000000000000000000000000011000,
|
||||
0b01100001011000100110001110000000,
|
||||
0b00000000000011110000000000000000,
|
||||
0b01111101101010000110010000000101,
|
||||
0b01100000000000000000001111000110,
|
||||
0b00111110100111010111101101111000,
|
||||
0b00000001100000111111110000000000,
|
||||
0b00010010110111001011111111011011,
|
||||
0b11100010111000101100001110001110,
|
||||
0b11001000001000010101110000011010,
|
||||
0b10110111001101100111100110100010,
|
||||
0b11100101101111000011100100001001,
|
||||
0b00110010011001100011110001011011,
|
||||
0b10011101001000001001110101100111,
|
||||
0b11101100100001110010011011001011,
|
||||
0b01110000001000010011100010100100,
|
||||
0b11010011101101111001011100111011,
|
||||
0b10010011111101011001100101111111,
|
||||
0b00111011011010001011101001110011,
|
||||
0b10101111111101001111111111000001,
|
||||
0b11110001000010100101110001100010,
|
||||
0b00001010100010110011100110010110,
|
||||
0b01110010101011111000001100001010,
|
||||
0b10010100000010011110001100111110,
|
||||
0b00100100011001000001010100100010,
|
||||
0b10011111010001111011111110010100,
|
||||
0b11110000101001100100111101011010,
|
||||
0b00111110001001000110101001111001,
|
||||
0b00100111001100110011101110100011,
|
||||
0b00001100010001110110001111110010,
|
||||
0b10000100000010101011111100100111,
|
||||
0b01111010001010010000110101011101,
|
||||
0b00000110010111000100001111011010,
|
||||
0b11111011001111101000100111001011,
|
||||
0b11001100011101100001011111011011,
|
||||
0b10111001111001100110110000110100,
|
||||
0b10101001100110010011011001100111,
|
||||
0b10000100101110101101111011011101,
|
||||
0b11000010000101000110001010111100,
|
||||
0b00010100100001110100011100101100,
|
||||
0b10110010000011110111101010011001,
|
||||
0b11101111010101111011100111001101,
|
||||
0b11101011111001101011001000111000,
|
||||
0b10011111111000110000100101011110,
|
||||
0b01111000101111001000110101001011,
|
||||
0b10100100001111111100111100010101,
|
||||
0b01100110100010110010111111111000,
|
||||
0b11101110101010111010001011001100,
|
||||
0b00010010101100011110110111101011,
|
||||
];
|
||||
|
||||
impl MessageSchedule {
|
||||
// Assign a word and its hi and lo halves
|
||||
pub fn assign_word_and_halves<F: FieldExt>(
|
||||
&self,
|
||||
region: &mut Region<'_, Table16Chip<F>>,
|
||||
word: u32,
|
||||
word_idx: usize,
|
||||
) -> Result<(Cell, (CellValue16, CellValue16)), Error> {
|
||||
// Rename these here for ease of matching the gates to the specification.
|
||||
let a_3 = self.extras[0];
|
||||
let a_4 = self.extras[1];
|
||||
|
||||
let row = get_word_row(word_idx);
|
||||
|
||||
let var = region.assign_advice(
|
||||
|| format!("W_{}", word_idx),
|
||||
self.message_schedule,
|
||||
row,
|
||||
|| Ok(F::from_u64(word as u64)),
|
||||
)?;
|
||||
|
||||
let w_lo = word as u16;
|
||||
let w_hi = (word >> 16) as u16;
|
||||
|
||||
let w_lo_cell = region.assign_advice(
|
||||
|| format!("W_{}_lo", word_idx),
|
||||
a_3,
|
||||
row,
|
||||
|| Ok(F::from_u64(w_lo as u64)),
|
||||
)?;
|
||||
let w_hi_cell = region.assign_advice(
|
||||
|| format!("W_{}_hi", word_idx),
|
||||
a_4,
|
||||
row,
|
||||
|| Ok(F::from_u64(w_hi as u64)),
|
||||
)?;
|
||||
|
||||
Ok((
|
||||
var,
|
||||
(
|
||||
CellValue16::new(w_lo_cell, w_lo),
|
||||
CellValue16::new(w_hi_cell, w_hi),
|
||||
),
|
||||
))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,183 @@
|
|||
use super::super::{
|
||||
util::*, BlockWord, CellValue16, CellValue32, SpreadVar, SpreadWord, Table16Assignment,
|
||||
Table16Chip,
|
||||
};
|
||||
use super::{schedule_util::*, MessageSchedule};
|
||||
use halo2::{arithmetic::FieldExt, circuit::Region, plonk::Error};
|
||||
|
||||
// A word in subregion 1
|
||||
// (3, 4, 11, 14)-bit chunks
|
||||
#[derive(Debug)]
|
||||
pub struct Subregion1Word {
|
||||
index: usize,
|
||||
a: CellValue32,
|
||||
b: CellValue32,
|
||||
c: CellValue32,
|
||||
d: CellValue32,
|
||||
spread_c: CellValue32,
|
||||
spread_d: CellValue32,
|
||||
}
|
||||
|
||||
impl MessageSchedule {
|
||||
pub fn assign_subregion1<F: FieldExt>(
|
||||
&self,
|
||||
region: &mut Region<'_, Table16Chip<F>>,
|
||||
input: &[BlockWord],
|
||||
) -> Result<Vec<(CellValue16, CellValue16)>, Error> {
|
||||
assert_eq!(input.len(), SUBREGION_1_LEN);
|
||||
Ok(input
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(idx, word)| {
|
||||
// s_decompose_1 on W_[1..14]
|
||||
let subregion1_word = self
|
||||
.decompose_subregion1_word(region, word.value.unwrap(), idx + 1)
|
||||
.unwrap();
|
||||
|
||||
// lower_sigma_0 on W_[1..14]
|
||||
self.lower_sigma_0(region, subregion1_word).unwrap()
|
||||
})
|
||||
.collect::<Vec<_>>())
|
||||
}
|
||||
|
||||
fn decompose_subregion1_word<F: FieldExt>(
|
||||
&self,
|
||||
region: &mut Region<'_, Table16Chip<F>>,
|
||||
word: u32,
|
||||
index: usize,
|
||||
) -> Result<Subregion1Word, Error> {
|
||||
let row = get_word_row(index);
|
||||
|
||||
// Rename these here for ease of matching the gates to the specification.
|
||||
let a_3 = self.extras[0];
|
||||
let a_4 = self.extras[1];
|
||||
|
||||
let pieces = chop_u32(word, &[3, 4, 11, 14]);
|
||||
|
||||
// Assign `a` (3-bit piece)
|
||||
let a = region.assign_advice(
|
||||
|| "word_a",
|
||||
a_3,
|
||||
row + 1,
|
||||
|| Ok(F::from_u64(pieces[0] as u64)),
|
||||
)?;
|
||||
// Assign `b` (4-bit piece)
|
||||
let b = region.assign_advice(
|
||||
|| "word_b",
|
||||
a_4,
|
||||
row + 1,
|
||||
|| Ok(F::from_u64(pieces[1] as u64)),
|
||||
)?;
|
||||
|
||||
// Assign `c` (11-bit piece) lookup
|
||||
let spread_c = SpreadWord::new(pieces[2] as u16);
|
||||
let spread_c = SpreadVar::with_lookup(region, &self.lookup, row + 1, spread_c)?;
|
||||
|
||||
// Assign `d` (14-bit piece) lookup
|
||||
let spread_d = SpreadWord::new(pieces[3] as u16);
|
||||
let spread_d = SpreadVar::with_lookup(region, &self.lookup, row, spread_d)?;
|
||||
|
||||
Ok(Subregion1Word {
|
||||
index,
|
||||
a: CellValue32::new(a, pieces[0]),
|
||||
b: CellValue32::new(b, pieces[1]),
|
||||
c: CellValue32::new(spread_c.dense.var, spread_c.dense.value.unwrap().into()),
|
||||
d: CellValue32::new(spread_d.dense.var, spread_d.dense.value.unwrap().into()),
|
||||
spread_c: CellValue32::new(spread_c.spread.var, spread_c.spread.value.unwrap()),
|
||||
spread_d: CellValue32::new(spread_d.spread.var, spread_d.spread.value.unwrap()),
|
||||
})
|
||||
}
|
||||
|
||||
// sigma_0 v1 on a word in W_1 to W_13
|
||||
// (3, 4, 11, 14)-bit chunks
|
||||
fn lower_sigma_0<F: FieldExt>(
|
||||
&self,
|
||||
region: &mut Region<'_, Table16Chip<F>>,
|
||||
word: Subregion1Word,
|
||||
) -> Result<(CellValue16, CellValue16), Error> {
|
||||
let a_3 = self.extras[0];
|
||||
let a_4 = self.extras[1];
|
||||
let a_5 = self.message_schedule;
|
||||
let a_6 = self.extras[2];
|
||||
|
||||
let row = get_word_row(word.index) + 3;
|
||||
|
||||
// Assign `a` and copy constraint
|
||||
self.assign_and_constrain(region, || "a", a_5, row + 1, &word.a, &self.perm)?;
|
||||
|
||||
// Witness `spread_a`
|
||||
let spread_a = interleave_u16_with_zeros(word.a.value.unwrap() as u16);
|
||||
region.assign_advice(
|
||||
|| "spread_a",
|
||||
a_6,
|
||||
row + 1,
|
||||
|| Ok(F::from_u64(spread_a as u64)),
|
||||
)?;
|
||||
|
||||
// Split `b` (2-bit chunk) into `b_hi` and `b_lo`
|
||||
let b = word.b.value.unwrap();
|
||||
let (b_lo, b_hi) = bisect_four_bit(b);
|
||||
let spread_b_lo = interleave_u16_with_zeros(b_lo as u16);
|
||||
let spread_b_hi = interleave_u16_with_zeros(b_hi as u16);
|
||||
|
||||
// Assign `b_hi`, `spread_b_hi`, `b_lo`, `spread_b_lo`
|
||||
region.assign_advice(|| "b_lo", a_3, row - 1, || Ok(F::from_u64(b_lo as u64)))?;
|
||||
region.assign_advice(
|
||||
|| "spread_b_lo",
|
||||
a_4,
|
||||
row - 1,
|
||||
|| Ok(F::from_u64(spread_b_lo as u64)),
|
||||
)?;
|
||||
region.assign_advice(|| "b_hi", a_5, row - 1, || Ok(F::from_u64(b_hi as u64)))?;
|
||||
region.assign_advice(
|
||||
|| "spread_b_hi",
|
||||
a_6,
|
||||
row - 1,
|
||||
|| Ok(F::from_u64(spread_b_hi as u64)),
|
||||
)?;
|
||||
|
||||
// Assign `b` and copy constraint
|
||||
self.assign_and_constrain(region, || "b", a_6, row, &word.b, &self.perm)?;
|
||||
|
||||
// Assign `spread_c` and copy constraint
|
||||
self.assign_and_constrain(region, || "spread_c", a_4, row, &word.spread_c, &self.perm)?;
|
||||
|
||||
// Assign `spread_d` and copy constraint
|
||||
self.assign_and_constrain(region, || "spread_d", a_5, row, &word.spread_d, &self.perm)?;
|
||||
|
||||
// Calculate R_0^{even}, R_0^{odd}, R_1^{even}, R_1^{odd}
|
||||
let spread_a = spread_a as u64;
|
||||
let spread_b_lo = spread_b_lo as u64;
|
||||
let spread_b_hi = spread_b_hi as u64;
|
||||
let spread_c = word.spread_c.value.unwrap() as u64;
|
||||
let spread_d = word.spread_d.value.unwrap() as u64;
|
||||
let xor_0: u64 =
|
||||
spread_b_lo + (1 << 4) * spread_b_hi + (1 << 8) * spread_c + (1 << 30) * spread_d;
|
||||
let xor_1: u64 = spread_c
|
||||
+ (1 << 22) * spread_d
|
||||
+ (1 << 50) * spread_a
|
||||
+ (1 << 56) * spread_b_lo
|
||||
+ (1 << 60) * spread_b_hi;
|
||||
let xor_2: u64 = spread_d
|
||||
+ (1 << 28) * spread_a
|
||||
+ (1 << 34) * spread_b_lo
|
||||
+ (1 << 38) * spread_b_hi
|
||||
+ (1 << 42) * spread_c;
|
||||
let r = xor_0 + xor_1 + xor_2;
|
||||
let r_pieces = chop_u64(r, &[32, 32]); // r_0, r_1
|
||||
let (r_0_even, r_0_odd) = get_even_and_odd_bits_u32(r_pieces[0] as u32);
|
||||
let (r_1_even, r_1_odd) = get_even_and_odd_bits_u32(r_pieces[1] as u32);
|
||||
|
||||
self.assign_sigma_outputs(
|
||||
region,
|
||||
&self.lookup,
|
||||
a_3,
|
||||
&self.perm,
|
||||
row,
|
||||
r_0_even,
|
||||
r_0_odd,
|
||||
r_1_even,
|
||||
r_1_odd,
|
||||
)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,428 @@
|
|||
use super::super::{
|
||||
util::*, CellValue16, CellValue32, SpreadVar, SpreadWord, Table16Assignment, Table16Chip,
|
||||
};
|
||||
use super::{schedule_util::*, MessageSchedule, MessageWord};
|
||||
use halo2::{arithmetic::FieldExt, circuit::Region, plonk::Error};
|
||||
|
||||
// A word in subregion 2
|
||||
// (3, 4, 3, 7, 1, 1, 13)-bit chunks
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Subregion2Word {
|
||||
index: usize,
|
||||
a: CellValue32,
|
||||
b: CellValue32,
|
||||
c: CellValue32,
|
||||
d: CellValue32,
|
||||
e: CellValue32,
|
||||
f: CellValue32,
|
||||
g: CellValue32,
|
||||
spread_d: CellValue32,
|
||||
spread_g: CellValue32,
|
||||
}
|
||||
|
||||
impl MessageSchedule {
|
||||
// W_[14..49]
|
||||
pub fn assign_subregion2<F: FieldExt>(
|
||||
&self,
|
||||
region: &mut Region<'_, Table16Chip<F>>,
|
||||
lower_sigma_0_output: Vec<(CellValue16, CellValue16)>,
|
||||
w: &mut Vec<MessageWord>,
|
||||
w_halves: &mut Vec<(CellValue16, CellValue16)>,
|
||||
) -> Result<Vec<(CellValue16, CellValue16)>, Error> {
|
||||
let a_5 = self.message_schedule;
|
||||
let a_6 = self.extras[2];
|
||||
let a_7 = self.extras[3];
|
||||
let a_8 = self.extras[4];
|
||||
let a_9 = self.extras[5];
|
||||
|
||||
let mut lower_sigma_0_v2_results =
|
||||
Vec::<(CellValue16, CellValue16)>::with_capacity(SUBREGION_2_LEN);
|
||||
let mut lower_sigma_1_v2_results =
|
||||
Vec::<(CellValue16, CellValue16)>::with_capacity(SUBREGION_2_LEN);
|
||||
|
||||
// Closure to compose new word
|
||||
// W_i = sigma_1(W_{i - 2}) + W_{i - 7} + sigma_0(W_{i - 15}) + W_{i - 16}
|
||||
// e.g. W_16 = sigma_1(W_14) + W_9 + sigma_0(W_1) + W_0
|
||||
|
||||
// sigma_0(W_[1..14]) will be used to get the new W_[16..29]
|
||||
// sigma_0_v2(W_[14..36]) will be used to get the new W_[29..51]
|
||||
// sigma_1_v2(W_[14..49]) will be used to get the W_[16..51]
|
||||
// The lowest-index words involved will be W_[0..13]
|
||||
let mut new_word = |idx: usize,
|
||||
sigma_0_output: (CellValue16, CellValue16)|
|
||||
-> Result<Vec<(CellValue16, CellValue16)>, Error> {
|
||||
// Decompose word into (3, 4, 3, 7, 1, 1, 13)-bit chunks
|
||||
let subregion2_word =
|
||||
self.decompose_subregion2_word(region, w[idx].value.unwrap(), idx)?;
|
||||
|
||||
// sigma_0 v2 and sigma_1 v2 on subregion2_word
|
||||
lower_sigma_0_v2_results.push(self.lower_sigma_0_v2(region, subregion2_word.clone())?);
|
||||
lower_sigma_1_v2_results.push(self.lower_sigma_1_v2(region, subregion2_word)?);
|
||||
|
||||
let new_word_idx = idx + 2;
|
||||
|
||||
// Copy sigma_0(W_{i - 15}) output from Subregion 1
|
||||
self.assign_and_constrain(
|
||||
region,
|
||||
|| format!("sigma_0(W_{})_lo", new_word_idx - 15),
|
||||
a_6,
|
||||
get_word_row(new_word_idx - 16),
|
||||
&sigma_0_output.0.into(),
|
||||
&self.perm,
|
||||
)?;
|
||||
self.assign_and_constrain(
|
||||
region,
|
||||
|| format!("sigma_0(W_{})_hi", new_word_idx - 15),
|
||||
a_6,
|
||||
get_word_row(new_word_idx - 16) + 1,
|
||||
&sigma_0_output.1.into(),
|
||||
&self.perm,
|
||||
)?;
|
||||
|
||||
// Copy sigma_1(W_{i - 2})
|
||||
self.assign_and_constrain(
|
||||
region,
|
||||
|| format!("sigma_1(W_{})_lo", new_word_idx - 2),
|
||||
a_7,
|
||||
get_word_row(new_word_idx - 16),
|
||||
&lower_sigma_1_v2_results[new_word_idx - 16].0.into(),
|
||||
&self.perm,
|
||||
)?;
|
||||
self.assign_and_constrain(
|
||||
region,
|
||||
|| format!("sigma_1(W_{})_hi", new_word_idx - 2),
|
||||
a_7,
|
||||
get_word_row(new_word_idx - 16) + 1,
|
||||
&lower_sigma_1_v2_results[new_word_idx - 16].1.into(),
|
||||
&self.perm,
|
||||
)?;
|
||||
|
||||
// Copy W_{i - 7}
|
||||
self.assign_and_constrain(
|
||||
region,
|
||||
|| format!("W_{}_lo", new_word_idx - 7),
|
||||
a_8,
|
||||
get_word_row(new_word_idx - 16),
|
||||
&w_halves[new_word_idx - 7].0.into(),
|
||||
&self.perm,
|
||||
)?;
|
||||
self.assign_and_constrain(
|
||||
region,
|
||||
|| format!("W_{}_hi", new_word_idx - 7),
|
||||
a_8,
|
||||
get_word_row(new_word_idx - 16) + 1,
|
||||
&w_halves[new_word_idx - 7].1.into(),
|
||||
&self.perm,
|
||||
)?;
|
||||
|
||||
// Calculate W_i, carry_i
|
||||
let word_lo: u32 = lower_sigma_1_v2_results[new_word_idx - 16].0.value.unwrap() as u32
|
||||
+ w_halves[new_word_idx - 7].0.value.unwrap() as u32
|
||||
+ sigma_0_output.0.value.unwrap() as u32
|
||||
+ w_halves[new_word_idx - 16].0.value.unwrap() as u32;
|
||||
let word_hi: u32 = lower_sigma_1_v2_results[new_word_idx - 16].1.value.unwrap() as u32
|
||||
+ w_halves[new_word_idx - 7].1.value.unwrap() as u32
|
||||
+ sigma_0_output.1.value.unwrap() as u32
|
||||
+ w_halves[new_word_idx - 16].1.value.unwrap() as u32;
|
||||
|
||||
let word: u64 = word_lo as u64 + (1 << 16) * (word_hi as u64);
|
||||
let carry = word >> 32;
|
||||
let word = word as u32;
|
||||
|
||||
// Assign W_i, carry_i
|
||||
region.assign_advice(
|
||||
|| format!("W_{}", new_word_idx),
|
||||
a_5,
|
||||
get_word_row(new_word_idx - 16) + 1,
|
||||
|| Ok(F::from_u64(word as u64)),
|
||||
)?;
|
||||
region.assign_advice(
|
||||
|| format!("carry_{}", new_word_idx),
|
||||
a_9,
|
||||
get_word_row(new_word_idx - 16) + 1,
|
||||
|| Ok(F::from_u64(carry as u64)),
|
||||
)?;
|
||||
let (var, halves) = self.assign_word_and_halves(region, word, new_word_idx)?;
|
||||
w.push(MessageWord {
|
||||
var,
|
||||
value: Some(word),
|
||||
});
|
||||
w_halves.push(halves);
|
||||
|
||||
Ok(lower_sigma_0_v2_results.clone())
|
||||
};
|
||||
|
||||
let mut tmp_lower_sigma_0_v2_results: Vec<(CellValue16, CellValue16)> =
|
||||
Vec::with_capacity(SUBREGION_2_LEN);
|
||||
|
||||
// Use up all the output from Subregion 1 lower_sigma_0
|
||||
for i in 14..27 {
|
||||
tmp_lower_sigma_0_v2_results = new_word(i, lower_sigma_0_output[i - 14])?;
|
||||
}
|
||||
|
||||
for i in 27..49 {
|
||||
tmp_lower_sigma_0_v2_results =
|
||||
new_word(i, tmp_lower_sigma_0_v2_results[i + 2 - 15 - 14])?;
|
||||
}
|
||||
|
||||
// Return lower_sigma_0_v2 output for W_[36..49]
|
||||
Ok(lower_sigma_0_v2_results.split_off(36 - 14))
|
||||
}
|
||||
|
||||
fn decompose_subregion2_word<F: FieldExt>(
|
||||
&self,
|
||||
region: &mut Region<'_, Table16Chip<F>>,
|
||||
word: u32,
|
||||
index: usize,
|
||||
) -> Result<Subregion2Word, Error> {
|
||||
let row = get_word_row(index);
|
||||
|
||||
// Rename these here for ease of matching the gates to the specification.
|
||||
let a_3 = self.extras[0];
|
||||
let a_4 = self.extras[1];
|
||||
|
||||
let pieces = chop_u32(word, &[3, 4, 3, 7, 1, 1, 13]);
|
||||
|
||||
// Assign `a` (3-bit piece)
|
||||
let a = region.assign_advice(|| "a", a_3, row - 1, || Ok(F::from_u64(pieces[0] as u64)))?;
|
||||
|
||||
// Assign `b` (4-bit piece) lookup
|
||||
let spread_b = SpreadWord::new(pieces[1] as u16);
|
||||
let spread_b = SpreadVar::with_lookup(region, &self.lookup, row + 1, spread_b)?;
|
||||
|
||||
// Assign `c` (3-bit piece)
|
||||
let c = region.assign_advice(|| "c", a_4, row - 1, || Ok(F::from_u64(pieces[2] as u64)))?;
|
||||
|
||||
// Assign `d` (7-bit piece) lookup
|
||||
let spread_d = SpreadWord::new(pieces[3] as u16);
|
||||
let spread_d = SpreadVar::with_lookup(region, &self.lookup, row, spread_d)?;
|
||||
|
||||
// Assign `e` (1-bit piece)
|
||||
let e = region.assign_advice(|| "e", a_3, row + 1, || Ok(F::from_u64(pieces[4] as u64)))?;
|
||||
|
||||
// Assign `f` (1-bit piece)
|
||||
let f = region.assign_advice(|| "f", a_4, row + 1, || Ok(F::from_u64(pieces[5] as u64)))?;
|
||||
|
||||
// Assign `g` (13-bit piece) lookup
|
||||
let spread_g = SpreadWord::new(pieces[6] as u16);
|
||||
let spread_g = SpreadVar::with_lookup(region, &self.lookup, row - 1, spread_g)?;
|
||||
|
||||
Ok(Subregion2Word {
|
||||
index,
|
||||
a: CellValue32::new(a, pieces[0]),
|
||||
b: CellValue32::new(spread_b.dense.var, spread_b.dense.value.unwrap().into()),
|
||||
c: CellValue32::new(c, pieces[2]),
|
||||
d: CellValue32::new(spread_d.dense.var, spread_d.dense.value.unwrap().into()),
|
||||
e: CellValue32::new(e, pieces[4]),
|
||||
f: CellValue32::new(f, pieces[5]),
|
||||
g: CellValue32::new(spread_g.dense.var, spread_g.dense.value.unwrap().into()),
|
||||
spread_d: CellValue32::new(spread_d.spread.var, spread_d.spread.value.unwrap()),
|
||||
spread_g: CellValue32::new(spread_g.spread.var, spread_g.spread.value.unwrap()),
|
||||
})
|
||||
}
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
fn assign_lower_sigma_v2_pieces<F: FieldExt>(
|
||||
&self,
|
||||
region: &mut Region<'_, Table16Chip<F>>,
|
||||
row: usize,
|
||||
subregion2_word: Subregion2Word,
|
||||
) -> Result<(u64, u64, u64, u64, u64, u64, u64, u64), Error> {
|
||||
let a_3 = self.extras[0];
|
||||
let a_4 = self.extras[1];
|
||||
let a_5 = self.message_schedule;
|
||||
let a_6 = self.extras[2];
|
||||
let a_7 = self.extras[3];
|
||||
|
||||
// Assign `a` and copy constraint
|
||||
self.assign_and_constrain(region, || "a", a_3, row + 1, &subregion2_word.a, &self.perm)?;
|
||||
|
||||
// Witness `spread_a`
|
||||
let spread_a = interleave_u16_with_zeros(subregion2_word.a.value.unwrap() as u16);
|
||||
region.assign_advice(
|
||||
|| "spread_a",
|
||||
a_4,
|
||||
row + 1,
|
||||
|| Ok(F::from_u64(spread_a as u64)),
|
||||
)?;
|
||||
|
||||
// Split `b` (2-bit chunk) into `b_hi` and `b_lo`
|
||||
let b = subregion2_word.b.value.unwrap();
|
||||
let (b_lo, b_hi) = bisect_four_bit(b);
|
||||
let spread_b_lo = interleave_u16_with_zeros(b_lo as u16);
|
||||
let spread_b_hi = interleave_u16_with_zeros(b_hi as u16);
|
||||
|
||||
// Assign `b_hi`, `spread_b_hi`, `b_lo`, `spread_b_lo`
|
||||
region.assign_advice(|| "b_lo", a_3, row - 1, || Ok(F::from_u64(b_lo as u64)))?;
|
||||
region.assign_advice(
|
||||
|| "spread_b_lo",
|
||||
a_4,
|
||||
row - 1,
|
||||
|| Ok(F::from_u64(spread_b_lo as u64)),
|
||||
)?;
|
||||
region.assign_advice(|| "b_hi", a_5, row - 1, || Ok(F::from_u64(b_hi as u64)))?;
|
||||
region.assign_advice(
|
||||
|| "spread_b_hi",
|
||||
a_6,
|
||||
row - 1,
|
||||
|| Ok(F::from_u64(spread_b_hi as u64)),
|
||||
)?;
|
||||
|
||||
// Assign `b` and copy constraint
|
||||
self.assign_and_constrain(region, || "b", a_6, row, &subregion2_word.b, &self.perm)?;
|
||||
|
||||
// Assign `c` and copy constraint
|
||||
self.assign_and_constrain(region, || "c", a_5, row + 1, &subregion2_word.c, &self.perm)?;
|
||||
|
||||
// Witness `spread_c`
|
||||
let spread_c = interleave_u16_with_zeros(subregion2_word.c.value.unwrap() as u16);
|
||||
region.assign_advice(
|
||||
|| "spread_c",
|
||||
a_6,
|
||||
row + 1,
|
||||
|| Ok(F::from_u64(spread_c as u64)),
|
||||
)?;
|
||||
|
||||
// Assign `spread_d` and copy constraint
|
||||
self.assign_and_constrain(
|
||||
region,
|
||||
|| "spread_d",
|
||||
a_4,
|
||||
row,
|
||||
&subregion2_word.spread_d,
|
||||
&self.perm,
|
||||
)?;
|
||||
|
||||
// Assign `e` and copy constraint
|
||||
self.assign_and_constrain(region, || "e", a_7, row, &subregion2_word.e, &self.perm)?;
|
||||
|
||||
// Assign `f` and copy constraint
|
||||
self.assign_and_constrain(region, || "f", a_7, row + 1, &subregion2_word.f, &self.perm)?;
|
||||
|
||||
// Assign `spread_g` and copy constraint
|
||||
self.assign_and_constrain(
|
||||
region,
|
||||
|| "spread_g",
|
||||
a_5,
|
||||
row,
|
||||
&subregion2_word.spread_g,
|
||||
&self.perm,
|
||||
)?;
|
||||
|
||||
Ok((
|
||||
spread_a as u64,
|
||||
spread_b_lo as u64,
|
||||
spread_b_hi as u64,
|
||||
spread_c as u64,
|
||||
subregion2_word.spread_d.value.unwrap() as u64,
|
||||
subregion2_word.e.value.unwrap() as u64,
|
||||
subregion2_word.f.value.unwrap() as u64,
|
||||
subregion2_word.spread_g.value.unwrap() as u64,
|
||||
))
|
||||
}
|
||||
|
||||
fn lower_sigma_0_v2<F: FieldExt>(
|
||||
&self,
|
||||
region: &mut Region<'_, Table16Chip<F>>,
|
||||
subregion2_word: Subregion2Word,
|
||||
) -> Result<(CellValue16, CellValue16), Error> {
|
||||
let a_3 = self.extras[0];
|
||||
let row = get_word_row(subregion2_word.index) + 3;
|
||||
|
||||
// Get spread pieces
|
||||
let (spread_a, spread_b_lo, spread_b_hi, spread_c, spread_d, e, f, spread_g) =
|
||||
self.assign_lower_sigma_v2_pieces(region, row, subregion2_word)?;
|
||||
|
||||
// Calculate R_0^{even}, R_0^{odd}, R_1^{even}, R_1^{odd}
|
||||
let xor_0 = spread_b_lo
|
||||
+ (1 << 4) * spread_b_hi
|
||||
+ (1 << 8) * spread_c
|
||||
+ (1 << 14) * spread_d
|
||||
+ (1 << 28) * e
|
||||
+ (1 << 30) * f
|
||||
+ (1 << 32) * spread_g;
|
||||
let xor_1 = spread_c
|
||||
+ (1 << 6) * spread_d
|
||||
+ (1 << 20) * e
|
||||
+ (1 << 22) * f
|
||||
+ (1 << 24) * spread_g
|
||||
+ (1 << 50) * spread_a
|
||||
+ (1 << 56) * spread_b_lo
|
||||
+ (1 << 60) * spread_b_hi;
|
||||
let xor_2 = f
|
||||
+ (1 << 2) * spread_g
|
||||
+ (1 << 28) * spread_a
|
||||
+ (1 << 34) * spread_b_lo
|
||||
+ (1 << 38) * spread_b_hi
|
||||
+ (1 << 42) * spread_c
|
||||
+ (1 << 48) * spread_d
|
||||
+ (1 << 62) * e;
|
||||
|
||||
let r = xor_0 + xor_1 + xor_2;
|
||||
let r_pieces = chop_u64(r, &[32, 32]); // r_0, r_1
|
||||
let (r_0_even, r_0_odd) = get_even_and_odd_bits_u32(r_pieces[0] as u32);
|
||||
let (r_1_even, r_1_odd) = get_even_and_odd_bits_u32(r_pieces[1] as u32);
|
||||
|
||||
self.assign_sigma_outputs(
|
||||
region,
|
||||
&self.lookup,
|
||||
a_3,
|
||||
&self.perm,
|
||||
row,
|
||||
r_0_even,
|
||||
r_0_odd,
|
||||
r_1_even,
|
||||
r_1_odd,
|
||||
)
|
||||
}
|
||||
|
||||
fn lower_sigma_1_v2<F: FieldExt>(
|
||||
&self,
|
||||
region: &mut Region<'_, Table16Chip<F>>,
|
||||
subregion2_word: Subregion2Word,
|
||||
) -> Result<(CellValue16, CellValue16), Error> {
|
||||
let a_3 = self.extras[0];
|
||||
let row = get_word_row(subregion2_word.index) + SIGMA_0_V2_ROWS + 3;
|
||||
|
||||
let (spread_a, spread_b_lo, spread_b_hi, spread_c, spread_d, e, f, spread_g) =
|
||||
self.assign_lower_sigma_v2_pieces(region, row, subregion2_word)?;
|
||||
|
||||
// (3, 4, 3, 7, 1, 1, 13)
|
||||
|
||||
// Calculate R_0^{even}, R_0^{odd}, R_1^{even}, R_1^{odd}
|
||||
let xor_0 = spread_d + (1 << 14) * e + (1 << 16) * f + (1 << 18) * spread_g;
|
||||
let xor_1 = e
|
||||
+ (1 << 2) * f
|
||||
+ (1 << 4) * spread_g
|
||||
+ (1 << 30) * spread_a
|
||||
+ (1 << 36) * spread_b_lo
|
||||
+ (1 << 40) * spread_b_hi
|
||||
+ (1 << 44) * spread_c
|
||||
+ (1 << 50) * spread_d;
|
||||
let xor_2 = spread_g
|
||||
+ (1 << 26) * spread_a
|
||||
+ (1 << 32) * spread_b_lo
|
||||
+ (1 << 36) * spread_b_hi
|
||||
+ (1 << 40) * spread_c
|
||||
+ (1 << 46) * spread_d
|
||||
+ (1 << 60) * e
|
||||
+ (1 << 62) * f;
|
||||
|
||||
let r = xor_0 + xor_1 + xor_2;
|
||||
let r_pieces = chop_u64(r, &[32, 32]); // r_0, r_1
|
||||
let (r_0_even, r_0_odd) = get_even_and_odd_bits_u32(r_pieces[0] as u32);
|
||||
let (r_1_even, r_1_odd) = get_even_and_odd_bits_u32(r_pieces[1] as u32);
|
||||
|
||||
self.assign_sigma_outputs(
|
||||
region,
|
||||
&self.lookup,
|
||||
a_3,
|
||||
&self.perm,
|
||||
row,
|
||||
r_0_even,
|
||||
r_0_odd,
|
||||
r_1_even,
|
||||
r_1_odd,
|
||||
)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,296 @@
|
|||
use super::super::{
|
||||
util::*, CellValue16, CellValue32, SpreadVar, SpreadWord, Table16Assignment, Table16Chip,
|
||||
};
|
||||
use super::{schedule_util::*, MessageSchedule, MessageWord};
|
||||
use halo2::{arithmetic::FieldExt, circuit::Region, plonk::Error};
|
||||
|
||||
// A word in subregion 3
|
||||
// (10, 7, 2, 13)-bit chunks
|
||||
pub struct Subregion3Word {
|
||||
index: usize,
|
||||
#[allow(dead_code)]
|
||||
a: CellValue32,
|
||||
b: CellValue32,
|
||||
c: CellValue32,
|
||||
#[allow(dead_code)]
|
||||
d: CellValue32,
|
||||
spread_a: CellValue32,
|
||||
spread_d: CellValue32,
|
||||
}
|
||||
|
||||
impl MessageSchedule {
|
||||
// W_[49..62]
|
||||
pub fn assign_subregion3<F: FieldExt>(
|
||||
&self,
|
||||
region: &mut Region<'_, Table16Chip<F>>,
|
||||
lower_sigma_0_v2_output: Vec<(CellValue16, CellValue16)>,
|
||||
w: &mut Vec<MessageWord>,
|
||||
w_halves: &mut Vec<(CellValue16, CellValue16)>,
|
||||
) -> Result<(), Error> {
|
||||
let a_5 = self.message_schedule;
|
||||
let a_6 = self.extras[2];
|
||||
let a_7 = self.extras[3];
|
||||
let a_8 = self.extras[4];
|
||||
let a_9 = self.extras[5];
|
||||
|
||||
// Closure to compose new word
|
||||
// W_i = sigma_1(W_{i - 2}) + W_{i - 7} + sigma_0(W_{i - 15}) + W_{i - 16}
|
||||
// e.g. W_51 = sigma_1(W_49) + W_44 + sigma_0(W_36) + W_35
|
||||
|
||||
// sigma_0_v2(W_[36..49]) will be used to get the new W_[51..64]
|
||||
// sigma_1(W_[49..62]) will also be used to get the W_[51..64]
|
||||
// The lowest-index words involved will be W_[35..58]
|
||||
let mut new_word = |idx: usize| -> Result<(), Error> {
|
||||
// Decompose word into (10, 7, 2, 13)-bit chunks
|
||||
let subregion3_word =
|
||||
self.decompose_subregion3_word(region, w[idx].value.unwrap(), idx)?;
|
||||
|
||||
// sigma_1 on subregion3_word
|
||||
let (r_0_even, r_1_even) = self.lower_sigma_1(region, subregion3_word)?;
|
||||
|
||||
let new_word_idx = idx + 2;
|
||||
|
||||
// Copy sigma_0_v2(W_{i - 15}) output from Subregion 2
|
||||
self.assign_and_constrain(
|
||||
region,
|
||||
|| format!("sigma_0(W_{})_lo", new_word_idx - 15),
|
||||
a_6,
|
||||
get_word_row(new_word_idx - 16),
|
||||
&lower_sigma_0_v2_output[idx - 49].0.into(),
|
||||
&self.perm,
|
||||
)?;
|
||||
self.assign_and_constrain(
|
||||
region,
|
||||
|| format!("sigma_0(W_{})_hi", new_word_idx - 15),
|
||||
a_6,
|
||||
get_word_row(new_word_idx - 16) + 1,
|
||||
&lower_sigma_0_v2_output[idx - 49].1.into(),
|
||||
&self.perm,
|
||||
)?;
|
||||
|
||||
// Copy sigma_1(W_{i - 2})
|
||||
self.assign_and_constrain(
|
||||
region,
|
||||
|| format!("sigma_1(W_{})_lo", new_word_idx - 2),
|
||||
a_7,
|
||||
get_word_row(new_word_idx - 16),
|
||||
&r_0_even.into(),
|
||||
&self.perm,
|
||||
)?;
|
||||
self.assign_and_constrain(
|
||||
region,
|
||||
|| format!("sigma_1(W_{})_hi", new_word_idx - 2),
|
||||
a_7,
|
||||
get_word_row(new_word_idx - 16) + 1,
|
||||
&r_1_even.into(),
|
||||
&self.perm,
|
||||
)?;
|
||||
|
||||
// Copy W_{i - 7}
|
||||
self.assign_and_constrain(
|
||||
region,
|
||||
|| format!("W_{}_lo", new_word_idx - 7),
|
||||
a_8,
|
||||
get_word_row(new_word_idx - 16),
|
||||
&w_halves[new_word_idx - 7].0.into(),
|
||||
&self.perm,
|
||||
)?;
|
||||
self.assign_and_constrain(
|
||||
region,
|
||||
|| format!("W_{}_hi", new_word_idx - 7),
|
||||
a_8,
|
||||
get_word_row(new_word_idx - 16) + 1,
|
||||
&w_halves[new_word_idx - 7].1.into(),
|
||||
&self.perm,
|
||||
)?;
|
||||
|
||||
// Calculate W_i, carry_i
|
||||
let word_lo: u32 = r_0_even.value.unwrap() as u32
|
||||
+ w_halves[new_word_idx - 7].0.value.unwrap() as u32
|
||||
+ lower_sigma_0_v2_output[idx - 49].0.value.unwrap() as u32
|
||||
+ w_halves[new_word_idx - 16].0.value.unwrap() as u32;
|
||||
let word_hi: u32 = r_1_even.value.unwrap() as u32
|
||||
+ w_halves[new_word_idx - 7].1.value.unwrap() as u32
|
||||
+ lower_sigma_0_v2_output[idx - 49].1.value.unwrap() as u32
|
||||
+ w_halves[new_word_idx - 16].1.value.unwrap() as u32;
|
||||
|
||||
let word: u64 = word_lo as u64 + (1 << 16) * (word_hi as u64);
|
||||
let carry = word >> 32;
|
||||
let word = word as u32;
|
||||
|
||||
// Assign W_i, carry_i
|
||||
region.assign_advice(
|
||||
|| format!("W_{}", new_word_idx),
|
||||
a_5,
|
||||
get_word_row(new_word_idx - 16) + 1,
|
||||
|| Ok(F::from_u64(word as u64)),
|
||||
)?;
|
||||
region.assign_advice(
|
||||
|| format!("carry_{}", new_word_idx),
|
||||
a_9,
|
||||
get_word_row(new_word_idx - 16) + 1,
|
||||
|| Ok(F::from_u64(carry as u64)),
|
||||
)?;
|
||||
let (var, halves) = self.assign_word_and_halves(region, word, new_word_idx)?;
|
||||
w.push(MessageWord {
|
||||
var,
|
||||
value: Some(word),
|
||||
});
|
||||
w_halves.push(halves);
|
||||
|
||||
Ok(())
|
||||
};
|
||||
|
||||
for i in 49..62 {
|
||||
new_word(i)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn decompose_subregion3_word<F: FieldExt>(
|
||||
&self,
|
||||
region: &mut Region<'_, Table16Chip<F>>,
|
||||
word: u32,
|
||||
index: usize,
|
||||
) -> Result<Subregion3Word, Error> {
|
||||
let row = get_word_row(index);
|
||||
|
||||
// Rename these here for ease of matching the gates to the specification.
|
||||
let a_3 = self.extras[0];
|
||||
let a_4 = self.extras[1];
|
||||
|
||||
let pieces = chop_u32(word, &[10, 7, 2, 13]);
|
||||
|
||||
// Assign `a` (10-bit piece)
|
||||
let spread_a = SpreadWord::new(pieces[0] as u16);
|
||||
let spread_a = SpreadVar::with_lookup(region, &self.lookup, row + 1, spread_a)?;
|
||||
|
||||
// Assign `b` (7-bit piece)
|
||||
let b = region.assign_advice(|| "b", a_4, row + 1, || Ok(F::from_u64(pieces[1] as u64)))?;
|
||||
|
||||
// Assign `c` (2-bit piece)
|
||||
let c = region.assign_advice(|| "c", a_3, row + 1, || Ok(F::from_u64(pieces[2] as u64)))?;
|
||||
|
||||
// Assign `d` (13-bit piece) lookup
|
||||
let spread_d = SpreadWord::new(pieces[3] as u16);
|
||||
let spread_d = SpreadVar::with_lookup(region, &self.lookup, row, spread_d)?;
|
||||
|
||||
Ok(Subregion3Word {
|
||||
index,
|
||||
a: CellValue32::new(spread_a.dense.var, spread_a.dense.value.unwrap().into()),
|
||||
b: CellValue32::new(b, pieces[1]),
|
||||
c: CellValue32::new(c, pieces[2]),
|
||||
d: CellValue32::new(spread_d.dense.var, spread_d.dense.value.unwrap().into()),
|
||||
spread_a: CellValue32::new(spread_a.spread.var, spread_a.spread.value.unwrap()),
|
||||
spread_d: CellValue32::new(spread_d.spread.var, spread_d.spread.value.unwrap()),
|
||||
})
|
||||
}
|
||||
|
||||
fn lower_sigma_1<F: FieldExt>(
|
||||
&self,
|
||||
region: &mut Region<'_, Table16Chip<F>>,
|
||||
word: Subregion3Word,
|
||||
) -> Result<(CellValue16, CellValue16), Error> {
|
||||
let a_3 = self.extras[0];
|
||||
let a_4 = self.extras[1];
|
||||
let a_5 = self.message_schedule;
|
||||
let a_6 = self.extras[2];
|
||||
|
||||
let row = get_word_row(word.index) + 3;
|
||||
|
||||
// Assign `spread_a` and copy constraint
|
||||
self.assign_and_constrain(region, || "spread_a", a_4, row, &word.spread_a, &self.perm)?;
|
||||
|
||||
// Split `b` (7-bit chunk) into (2,2,3)-bit `b_lo`, `b_mid` and `b_hi`
|
||||
let b = word.b.value.unwrap();
|
||||
let b_hi = (b & 0b1110000) >> 4;
|
||||
let (b_lo, b_mid) = bisect_four_bit(b & 0b1111);
|
||||
let spread_b_lo = interleave_u16_with_zeros(b_lo as u16);
|
||||
let spread_b_mid = interleave_u16_with_zeros(b_mid as u16);
|
||||
let spread_b_hi = interleave_u16_with_zeros(b_hi as u16);
|
||||
|
||||
// Assign `b_lo`, `spread_b_lo`, `b_mid`, `spread_b_mid`, `b_hi`, `spread_b_hi`
|
||||
region.assign_advice(|| "b_lo", a_3, row - 1, || Ok(F::from_u64(b_lo as u64)))?;
|
||||
region.assign_advice(
|
||||
|| "spread_b_lo",
|
||||
a_4,
|
||||
row - 1,
|
||||
|| Ok(F::from_u64(spread_b_lo as u64)),
|
||||
)?;
|
||||
region.assign_advice(|| "b_mid", a_5, row - 1, || Ok(F::from_u64(b_mid as u64)))?;
|
||||
region.assign_advice(
|
||||
|| "spread_b_mid",
|
||||
a_6,
|
||||
row - 1,
|
||||
|| Ok(F::from_u64(spread_b_mid as u64)),
|
||||
)?;
|
||||
region.assign_advice(|| "b_hi", a_5, row + 1, || Ok(F::from_u64(b_hi as u64)))?;
|
||||
region.assign_advice(
|
||||
|| "spread_b_hi",
|
||||
a_6,
|
||||
row + 1,
|
||||
|| Ok(F::from_u64(spread_b_hi as u64)),
|
||||
)?;
|
||||
|
||||
// Assign `b` and copy constraint
|
||||
self.assign_and_constrain(region, || "b", a_6, row, &word.b, &self.perm)?;
|
||||
|
||||
// Assign `c` and copy constraint
|
||||
self.assign_and_constrain(region, || "c", a_3, row + 1, &word.c, &self.perm)?;
|
||||
|
||||
// Witness `spread_c`
|
||||
let spread_c = interleave_u16_with_zeros(word.c.value.unwrap() as u16);
|
||||
region.assign_advice(
|
||||
|| "spread_c",
|
||||
a_4,
|
||||
row + 1,
|
||||
|| Ok(F::from_u64(spread_c as u64)),
|
||||
)?;
|
||||
|
||||
// Assign `spread_d` and copy constraint
|
||||
self.assign_and_constrain(region, || "spread_d", a_5, row, &word.spread_d, &self.perm)?;
|
||||
|
||||
// (10, 7, 2, 13)
|
||||
// Calculate R_0^{even}, R_0^{odd}, R_1^{even}, R_1^{odd}
|
||||
let spread_a = word.spread_a.value.unwrap() as u64;
|
||||
let spread_b_lo = spread_b_lo as u64;
|
||||
let spread_b_mid = spread_b_mid as u64;
|
||||
let spread_b_hi = spread_b_hi as u64;
|
||||
let spread_c = spread_c as u64;
|
||||
let spread_d = word.spread_d.value.unwrap() as u64;
|
||||
let xor_0 = spread_b_lo
|
||||
+ (1 << 4) * spread_b_mid
|
||||
+ (1 << 8) * spread_b_hi
|
||||
+ (1 << 14) * spread_c
|
||||
+ (1 << 18) * spread_d;
|
||||
let xor_1 = spread_c
|
||||
+ (1 << 4) * spread_d
|
||||
+ (1 << 30) * spread_a
|
||||
+ (1 << 50) * spread_b_lo
|
||||
+ (1 << 54) * spread_b_mid
|
||||
+ (1 << 58) * spread_b_hi;
|
||||
let xor_2 = spread_d
|
||||
+ (1 << 26) * spread_a
|
||||
+ (1 << 46) * spread_b_lo
|
||||
+ (1 << 50) * spread_b_mid
|
||||
+ (1 << 54) * spread_b_hi
|
||||
+ (1 << 60) * spread_c;
|
||||
let r = xor_0 + xor_1 + xor_2;
|
||||
let r_pieces = chop_u64(r, &[32, 32]); // r_0, r_1
|
||||
let (r_0_even, r_0_odd) = get_even_and_odd_bits_u32(r_pieces[0] as u32);
|
||||
let (r_1_even, r_1_odd) = get_even_and_odd_bits_u32(r_pieces[1] as u32);
|
||||
|
||||
self.assign_sigma_outputs(
|
||||
region,
|
||||
&self.lookup,
|
||||
a_3,
|
||||
&self.perm,
|
||||
row,
|
||||
r_0_even,
|
||||
r_0_odd,
|
||||
r_1_even,
|
||||
r_1_odd,
|
||||
)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,654 @@
|
|||
use super::{util::*, CellValue16, CellValue32, Table16Chip};
|
||||
use halo2::{
|
||||
arithmetic::FieldExt,
|
||||
circuit::{Chip, Layouter, Region},
|
||||
plonk::{Advice, Column, ConstraintSystem, Error, Fixed},
|
||||
poly::Rotation,
|
||||
};
|
||||
|
||||
/// An input word into a lookup, containing (tag, dense, spread)
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub(super) struct SpreadWord {
|
||||
pub tag: u8,
|
||||
pub dense: u16,
|
||||
pub spread: u32,
|
||||
}
|
||||
|
||||
impl SpreadWord {
|
||||
pub(super) fn new(word: u16) -> Self {
|
||||
SpreadWord {
|
||||
tag: get_tag(word),
|
||||
dense: word,
|
||||
spread: interleave_u16_with_zeros(word),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A variable stored in advice columns corresponding to a row of [`SpreadTable`].
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub(super) struct SpreadVar {
|
||||
pub tag: u8,
|
||||
pub dense: CellValue16,
|
||||
pub spread: CellValue32,
|
||||
}
|
||||
|
||||
impl SpreadVar {
|
||||
pub(super) fn with_lookup<'r, C: Chip>(
|
||||
region: &mut Region<'r, C>,
|
||||
cols: &SpreadInputs,
|
||||
row: usize,
|
||||
word: SpreadWord,
|
||||
) -> Result<Self, Error> {
|
||||
let tag = word.tag;
|
||||
let dense_val = Some(word.dense);
|
||||
let spread_val = Some(word.spread);
|
||||
region.assign_advice(
|
||||
|| "tag",
|
||||
cols.tag,
|
||||
row,
|
||||
|| Ok(C::Field::from_u64(tag as u64)),
|
||||
)?;
|
||||
let dense_var = region.assign_advice(
|
||||
|| "dense",
|
||||
cols.dense,
|
||||
row,
|
||||
|| {
|
||||
dense_val
|
||||
.map(|v| C::Field::from_u64(v as u64))
|
||||
.ok_or(Error::SynthesisError)
|
||||
},
|
||||
)?;
|
||||
let spread_var = region.assign_advice(
|
||||
|| "spread",
|
||||
cols.spread,
|
||||
row,
|
||||
|| {
|
||||
spread_val
|
||||
.map(|v| C::Field::from_u64(v as u64))
|
||||
.ok_or(Error::SynthesisError)
|
||||
},
|
||||
)?;
|
||||
|
||||
Ok(SpreadVar {
|
||||
tag,
|
||||
dense: CellValue16::new(dense_var, dense_val.unwrap()),
|
||||
spread: CellValue32::new(spread_var, spread_val.unwrap()),
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) fn without_lookup<C: Chip>(
|
||||
region: &mut Region<'_, C>,
|
||||
dense_col: Column<Advice>,
|
||||
dense_row: usize,
|
||||
spread_col: Column<Advice>,
|
||||
spread_row: usize,
|
||||
word: SpreadWord,
|
||||
) -> Result<Self, Error> {
|
||||
let tag = word.tag;
|
||||
let dense_val = Some(word.dense);
|
||||
let spread_val = Some(word.spread);
|
||||
let dense_var = region.assign_advice(
|
||||
|| "dense",
|
||||
dense_col,
|
||||
dense_row,
|
||||
|| {
|
||||
dense_val
|
||||
.map(|v| C::Field::from_u64(v as u64))
|
||||
.ok_or(Error::SynthesisError)
|
||||
},
|
||||
)?;
|
||||
let spread_var = region.assign_advice(
|
||||
|| "spread",
|
||||
spread_col,
|
||||
spread_row,
|
||||
|| {
|
||||
spread_val
|
||||
.map(|v| C::Field::from_u64(v as u64))
|
||||
.ok_or(Error::SynthesisError)
|
||||
},
|
||||
)?;
|
||||
|
||||
Ok(SpreadVar {
|
||||
tag,
|
||||
dense: CellValue16::new(dense_var, dense_val.unwrap()),
|
||||
spread: CellValue32::new(spread_var, spread_val.unwrap()),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(super) struct SpreadInputs {
|
||||
pub(super) tag: Column<Advice>,
|
||||
pub(super) dense: Column<Advice>,
|
||||
pub(super) spread: Column<Advice>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(super) struct SpreadTable {
|
||||
table_tag: Column<Fixed>,
|
||||
table_dense: Column<Fixed>,
|
||||
table_spread: Column<Fixed>,
|
||||
}
|
||||
|
||||
impl SpreadTable {
|
||||
pub(super) fn configure<F: FieldExt>(
|
||||
meta: &mut ConstraintSystem<F>,
|
||||
tag: Column<Advice>,
|
||||
dense: Column<Advice>,
|
||||
spread: Column<Advice>,
|
||||
) -> (SpreadInputs, Self) {
|
||||
let table_tag = meta.fixed_column();
|
||||
let table_dense = meta.fixed_column();
|
||||
let table_spread = meta.fixed_column();
|
||||
|
||||
let tag_ = meta.query_any(tag.into(), Rotation::cur());
|
||||
let dense_ = meta.query_any(dense.into(), Rotation::cur());
|
||||
let spread_ = meta.query_any(spread.into(), Rotation::cur());
|
||||
let table_tag_ = meta.query_any(table_tag.into(), Rotation::cur());
|
||||
let table_dense_ = meta.query_any(table_dense.into(), Rotation::cur());
|
||||
let table_spread_ = meta.query_any(table_spread.into(), Rotation::cur());
|
||||
meta.lookup(
|
||||
&[tag_, dense_, spread_],
|
||||
&[table_tag_, table_dense_, table_spread_],
|
||||
);
|
||||
|
||||
(
|
||||
SpreadInputs { tag, dense, spread },
|
||||
SpreadTable {
|
||||
table_tag,
|
||||
table_dense,
|
||||
table_spread,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fn generate<F: FieldExt>() -> impl Iterator<Item = (F, F, F)> {
|
||||
(1..=(1 << 16)).scan(
|
||||
(F::zero(), F::zero(), F::zero()),
|
||||
|(tag, dense, spread), i| {
|
||||
// We computed this table row in the previous iteration.
|
||||
let res = (*tag, *dense, *spread);
|
||||
|
||||
// i holds the zero-indexed row number for the next table row.
|
||||
match i {
|
||||
BITS_7 | BITS_10 | BITS_11 | BITS_13 | BITS_14 => *tag += F::one(),
|
||||
_ => (),
|
||||
}
|
||||
*dense += F::one();
|
||||
if i & 1 == 0 {
|
||||
// On even-numbered rows we recompute the spread.
|
||||
*spread = F::zero();
|
||||
for b in 0..16 {
|
||||
if (i >> b) & 1 != 0 {
|
||||
*spread += F::from_u64(1 << (2 * b));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// On odd-numbered rows we add one.
|
||||
*spread += F::one();
|
||||
}
|
||||
|
||||
Some(res)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
pub(super) fn load<F: FieldExt>(
|
||||
&self,
|
||||
layouter: &mut impl Layouter<Table16Chip<F>>,
|
||||
) -> Result<(), Error> {
|
||||
layouter.assign_region(
|
||||
|| "spread table",
|
||||
|mut gate| {
|
||||
// We generate the row values lazily (we only need them during keygen).
|
||||
let mut rows = Self::generate::<F>();
|
||||
|
||||
for index in 0..(1 << 16) {
|
||||
let mut row = None;
|
||||
gate.assign_fixed(
|
||||
|| "tag",
|
||||
self.table_tag,
|
||||
index,
|
||||
|| {
|
||||
row = rows.next();
|
||||
row.map(|(tag, _, _)| tag).ok_or(Error::SynthesisError)
|
||||
},
|
||||
)?;
|
||||
gate.assign_fixed(
|
||||
|| "dense",
|
||||
self.table_dense,
|
||||
index,
|
||||
|| row.map(|(_, dense, _)| dense).ok_or(Error::SynthesisError),
|
||||
)?;
|
||||
gate.assign_fixed(
|
||||
|| "spread",
|
||||
self.table_spread,
|
||||
index,
|
||||
|| {
|
||||
row.map(|(_, _, spread)| spread)
|
||||
.ok_or(Error::SynthesisError)
|
||||
},
|
||||
)?;
|
||||
}
|
||||
Ok(())
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use rand::Rng;
|
||||
use std::cmp;
|
||||
use std::collections::HashMap;
|
||||
use std::fmt;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use super::{
|
||||
super::{util::*, Compression, MessageSchedule, Table16Chip, Table16Config},
|
||||
SpreadInputs, SpreadTable,
|
||||
};
|
||||
use halo2::{
|
||||
arithmetic::FieldExt,
|
||||
circuit::{layouter, Cell, Layouter, Region, RegionIndex},
|
||||
dev::MockProver,
|
||||
pasta::Fp,
|
||||
plonk::{
|
||||
Advice, Any, Assignment, Circuit, Column, ConstraintSystem, Error, Fixed, Permutation,
|
||||
},
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn lookup_table() {
|
||||
/// This represents an advice column at a certain row in the ConstraintSystem
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct Variable(Column<Advice>, usize);
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct MyConfig {
|
||||
lookup_inputs: SpreadInputs,
|
||||
sha256: Table16Config,
|
||||
}
|
||||
|
||||
struct MyCircuit {}
|
||||
|
||||
struct MyLayouter<'a, F: FieldExt, CS: Assignment<F> + 'a> {
|
||||
cs: &'a mut CS,
|
||||
config: MyConfig,
|
||||
regions: Vec<usize>,
|
||||
/// Stores the first empty row for each column.
|
||||
columns: HashMap<Column<Any>, usize>,
|
||||
_marker: PhantomData<F>,
|
||||
}
|
||||
|
||||
impl<'a, F: FieldExt, CS: Assignment<F> + 'a> fmt::Debug for MyLayouter<'a, F, CS> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("MyLayouter")
|
||||
.field("config", &self.config)
|
||||
.field("regions", &self.regions)
|
||||
.field("columns", &self.columns)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, FF: FieldExt, CS: Assignment<FF>> MyLayouter<'a, FF, CS> {
|
||||
fn new(cs: &'a mut CS, config: MyConfig) -> Result<Self, Error> {
|
||||
let mut res = MyLayouter {
|
||||
cs,
|
||||
config,
|
||||
regions: vec![],
|
||||
columns: HashMap::default(),
|
||||
_marker: PhantomData,
|
||||
};
|
||||
|
||||
let table = res.config.sha256.lookup_table.clone();
|
||||
table.load(&mut res)?;
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, F: FieldExt, CS: Assignment<F> + 'a> Layouter<Table16Chip<F>> for MyLayouter<'a, F, CS> {
|
||||
type Root = Self;
|
||||
|
||||
fn config(&self) -> &Table16Config {
|
||||
&self.config.sha256
|
||||
}
|
||||
|
||||
fn loaded(&self) -> &() {
|
||||
&()
|
||||
}
|
||||
|
||||
fn assign_region<A, AR, N, NR>(
|
||||
&mut self,
|
||||
name: N,
|
||||
mut assignment: A,
|
||||
) -> Result<AR, Error>
|
||||
where
|
||||
A: FnMut(Region<'_, Table16Chip<F>>) -> Result<AR, Error>,
|
||||
N: Fn() -> NR,
|
||||
NR: Into<String>,
|
||||
{
|
||||
let region_index = self.regions.len();
|
||||
|
||||
// Get shape of the region.
|
||||
let mut shape = layouter::RegionShape::new(region_index.into());
|
||||
{
|
||||
let region: &mut dyn layouter::RegionLayouter<Table16Chip<F>> = &mut shape;
|
||||
assignment(region.into())?;
|
||||
}
|
||||
|
||||
// Lay out this region. We implement the simplest approach here: position the
|
||||
// region starting at the earliest row for which none of the columns are in use.
|
||||
let mut region_start = 0;
|
||||
for column in shape.columns() {
|
||||
region_start =
|
||||
cmp::max(region_start, self.columns.get(column).cloned().unwrap_or(0));
|
||||
}
|
||||
self.regions.push(region_start);
|
||||
|
||||
// Update column usage information.
|
||||
for column in shape.columns() {
|
||||
self.columns
|
||||
.insert(*column, region_start + shape.row_count());
|
||||
}
|
||||
|
||||
self.cs.enter_region(name);
|
||||
let mut region = MyRegion::new(self, region_index.into());
|
||||
let result = {
|
||||
let region: &mut dyn layouter::RegionLayouter<Table16Chip<F>> = &mut region;
|
||||
assignment(region.into())
|
||||
}?;
|
||||
self.cs.exit_region();
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn get_root(&mut self) -> &mut Self::Root {
|
||||
self
|
||||
}
|
||||
|
||||
fn push_namespace<NR, N>(&mut self, name_fn: N)
|
||||
where
|
||||
NR: Into<String>,
|
||||
N: FnOnce() -> NR,
|
||||
{
|
||||
self.cs.push_namespace(name_fn)
|
||||
}
|
||||
|
||||
fn pop_namespace(&mut self, gadget_name: Option<String>) {
|
||||
self.cs.pop_namespace(gadget_name)
|
||||
}
|
||||
}
|
||||
|
||||
struct MyRegion<'r, 'a, F: FieldExt, CS: Assignment<F> + 'a> {
|
||||
layouter: &'r mut MyLayouter<'a, F, CS>,
|
||||
region_index: RegionIndex,
|
||||
_marker: PhantomData<F>,
|
||||
}
|
||||
|
||||
impl<'r, 'a, F: FieldExt, CS: Assignment<F> + 'a> fmt::Debug for MyRegion<'r, 'a, F, CS> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("MyRegion")
|
||||
.field("layouter", &self.layouter)
|
||||
.field("region_index", &self.region_index)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'r, 'a, F: FieldExt, CS: Assignment<F> + 'a> MyRegion<'r, 'a, F, CS> {
|
||||
fn new(layouter: &'r mut MyLayouter<'a, F, CS>, region_index: RegionIndex) -> Self {
|
||||
MyRegion {
|
||||
layouter,
|
||||
region_index,
|
||||
_marker: PhantomData::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'r, 'a, F: FieldExt, CS: Assignment<F> + 'a> layouter::RegionLayouter<Table16Chip<F>>
|
||||
for MyRegion<'r, 'a, F, CS>
|
||||
{
|
||||
fn assign_advice<'v>(
|
||||
&'v mut self,
|
||||
annotation: &'v (dyn Fn() -> String + 'v),
|
||||
column: Column<Advice>,
|
||||
offset: usize,
|
||||
to: &'v mut (dyn FnMut() -> Result<F, Error> + 'v),
|
||||
) -> Result<Cell, Error> {
|
||||
self.layouter.cs.assign_advice(
|
||||
annotation,
|
||||
column,
|
||||
self.layouter.regions[*self.region_index] + offset,
|
||||
to,
|
||||
)?;
|
||||
|
||||
Ok(Cell {
|
||||
region_index: self.region_index,
|
||||
row_offset: offset,
|
||||
column: column.into(),
|
||||
})
|
||||
}
|
||||
|
||||
fn assign_fixed<'v>(
|
||||
&'v mut self,
|
||||
annotation: &'v (dyn Fn() -> String + 'v),
|
||||
column: Column<Fixed>,
|
||||
offset: usize,
|
||||
to: &'v mut (dyn FnMut() -> Result<F, Error> + 'v),
|
||||
) -> Result<Cell, Error> {
|
||||
self.layouter.cs.assign_fixed(
|
||||
annotation,
|
||||
column,
|
||||
self.layouter.regions[*self.region_index] + offset,
|
||||
to,
|
||||
)?;
|
||||
Ok(Cell {
|
||||
region_index: self.region_index,
|
||||
row_offset: offset,
|
||||
column: column.into(),
|
||||
})
|
||||
}
|
||||
|
||||
fn constrain_equal(
|
||||
&mut self,
|
||||
permutation: &Permutation,
|
||||
left: Cell,
|
||||
right: Cell,
|
||||
) -> Result<(), Error> {
|
||||
self.layouter.cs.copy(
|
||||
permutation,
|
||||
left.column,
|
||||
self.layouter.regions[*left.region_index] + left.row_offset,
|
||||
right.column,
|
||||
self.layouter.regions[*right.region_index] + right.row_offset,
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: FieldExt> Circuit<F> for MyCircuit {
|
||||
type Config = MyConfig;
|
||||
|
||||
fn configure(meta: &mut ConstraintSystem<F>) -> MyConfig {
|
||||
let a = meta.advice_column();
|
||||
let b = meta.advice_column();
|
||||
let c = meta.advice_column();
|
||||
|
||||
let (lookup_inputs, lookup_table) = SpreadTable::configure(meta, a, b, c);
|
||||
|
||||
let message_schedule = meta.advice_column();
|
||||
let extras = [
|
||||
meta.advice_column(),
|
||||
meta.advice_column(),
|
||||
meta.advice_column(),
|
||||
meta.advice_column(),
|
||||
meta.advice_column(),
|
||||
meta.advice_column(),
|
||||
];
|
||||
|
||||
// Rename these here for ease of matching the gates to the specification.
|
||||
let _a_0 = lookup_inputs.tag;
|
||||
let a_1 = lookup_inputs.dense;
|
||||
let a_2 = lookup_inputs.spread;
|
||||
let a_3 = extras[0];
|
||||
let a_4 = extras[1];
|
||||
let a_5 = message_schedule;
|
||||
let a_6 = extras[2];
|
||||
let a_7 = extras[3];
|
||||
let a_8 = extras[4];
|
||||
let _a_9 = extras[5];
|
||||
|
||||
let perm = Permutation::new(
|
||||
meta,
|
||||
&[
|
||||
a_1.into(),
|
||||
a_2.into(),
|
||||
a_3.into(),
|
||||
a_4.into(),
|
||||
a_5.into(),
|
||||
a_6.into(),
|
||||
a_7.into(),
|
||||
a_8.into(),
|
||||
],
|
||||
);
|
||||
|
||||
let compression = Compression::empty_configure(
|
||||
meta,
|
||||
lookup_inputs.clone(),
|
||||
message_schedule,
|
||||
extras,
|
||||
perm.clone(),
|
||||
);
|
||||
|
||||
let message_schedule = MessageSchedule::empty_configure(
|
||||
meta,
|
||||
lookup_inputs.clone(),
|
||||
message_schedule,
|
||||
extras,
|
||||
perm.clone(),
|
||||
);
|
||||
|
||||
MyConfig {
|
||||
lookup_inputs,
|
||||
sha256: Table16Config {
|
||||
lookup_table,
|
||||
message_schedule,
|
||||
compression,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn synthesize(
|
||||
&self,
|
||||
cs: &mut impl Assignment<F>,
|
||||
config: MyConfig,
|
||||
) -> Result<(), Error> {
|
||||
let lookup = config.lookup_inputs.clone();
|
||||
let mut layouter = MyLayouter::new(cs, config)?;
|
||||
|
||||
layouter.assign_region(
|
||||
|| "spread_test",
|
||||
|mut gate| {
|
||||
let mut row = 0;
|
||||
let mut add_row = |tag, dense, spread| {
|
||||
gate.assign_advice(|| "tag", lookup.tag, row, || Ok(tag))?;
|
||||
gate.assign_advice(|| "dense", lookup.dense, row, || Ok(dense))?;
|
||||
gate.assign_advice(|| "spread", lookup.spread, row, || Ok(spread))?;
|
||||
row += 1;
|
||||
Ok(())
|
||||
};
|
||||
|
||||
// Test the first few small values.
|
||||
add_row(F::zero(), F::from_u64(0b000), F::from_u64(0b000000))?;
|
||||
add_row(F::zero(), F::from_u64(0b001), F::from_u64(0b000001))?;
|
||||
add_row(F::zero(), F::from_u64(0b010), F::from_u64(0b000100))?;
|
||||
add_row(F::zero(), F::from_u64(0b011), F::from_u64(0b000101))?;
|
||||
add_row(F::zero(), F::from_u64(0b100), F::from_u64(0b010000))?;
|
||||
add_row(F::zero(), F::from_u64(0b101), F::from_u64(0b010001))?;
|
||||
|
||||
// Test the tag boundaries:
|
||||
// 7-bit
|
||||
add_row(
|
||||
F::zero(),
|
||||
F::from_u64(0b1111111),
|
||||
F::from_u64(0b01010101010101),
|
||||
)?;
|
||||
add_row(
|
||||
F::one(),
|
||||
F::from_u64(0b10000000),
|
||||
F::from_u64(0b0100000000000000),
|
||||
)?;
|
||||
// - 10-bit
|
||||
add_row(
|
||||
F::one(),
|
||||
F::from_u64(0b1111111111),
|
||||
F::from_u64(0b01010101010101010101),
|
||||
)?;
|
||||
add_row(
|
||||
F::from_u64(2),
|
||||
F::from_u64(0b10000000000),
|
||||
F::from_u64(0b0100000000000000000000),
|
||||
)?;
|
||||
// - 11-bit
|
||||
add_row(
|
||||
F::from_u64(2),
|
||||
F::from_u64(0b11111111111),
|
||||
F::from_u64(0b0101010101010101010101),
|
||||
)?;
|
||||
add_row(
|
||||
F::from_u64(3),
|
||||
F::from_u64(0b100000000000),
|
||||
F::from_u64(0b010000000000000000000000),
|
||||
)?;
|
||||
// - 13-bit
|
||||
add_row(
|
||||
F::from_u64(3),
|
||||
F::from_u64(0b1111111111111),
|
||||
F::from_u64(0b01010101010101010101010101),
|
||||
)?;
|
||||
add_row(
|
||||
F::from_u64(4),
|
||||
F::from_u64(0b10000000000000),
|
||||
F::from_u64(0b0100000000000000000000000000),
|
||||
)?;
|
||||
// - 14-bit
|
||||
add_row(
|
||||
F::from_u64(4),
|
||||
F::from_u64(0b11111111111111),
|
||||
F::from_u64(0b0101010101010101010101010101),
|
||||
)?;
|
||||
add_row(
|
||||
F::from_u64(5),
|
||||
F::from_u64(0b100000000000000),
|
||||
F::from_u64(0b010000000000000000000000000000),
|
||||
)?;
|
||||
|
||||
// Test random lookup values
|
||||
let mut rng = rand::thread_rng();
|
||||
|
||||
for _ in 0..10 {
|
||||
let word: u16 = rng.gen();
|
||||
add_row(
|
||||
F::from_u64(get_tag(word).into()),
|
||||
F::from_u64(word.into()),
|
||||
F::from_u64(interleave_u16_with_zeros(word).into()),
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
let circuit: MyCircuit = MyCircuit {};
|
||||
|
||||
let prover = match MockProver::<Fp>::run(16, &circuit, vec![]) {
|
||||
Ok(prover) => prover,
|
||||
Err(e) => panic!("{:?}", e),
|
||||
};
|
||||
assert_eq!(prover.verify(), Ok(()));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
pub const BITS_7: usize = 1 << 7;
|
||||
pub const BITS_10: usize = 1 << 10;
|
||||
pub const BITS_11: usize = 1 << 11;
|
||||
pub const BITS_13: usize = 1 << 13;
|
||||
pub const BITS_14: usize = 1 << 14;
|
||||
pub const MASK_EVEN_32: u32 = 0x55555555;
|
||||
pub const MASK_ODD_32: u32 = 0xAAAAAAAA;
|
||||
|
||||
// Helper function that returns tag of 16-bit input
|
||||
pub fn get_tag(input: u16) -> u8 {
|
||||
let input = input as usize;
|
||||
if input < BITS_7 {
|
||||
0
|
||||
} else if input < BITS_10 {
|
||||
1
|
||||
} else if input < BITS_11 {
|
||||
2
|
||||
} else if input < BITS_13 {
|
||||
3
|
||||
} else if input < BITS_14 {
|
||||
4
|
||||
} else {
|
||||
5
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper function that returns 32-bit spread version of 16-bit input.
|
||||
pub fn interleave_u16_with_zeros(word: u16) -> u32 {
|
||||
let mut word: u32 = word.into();
|
||||
word = (word ^ (word << 8)) & 0x00ff00ff;
|
||||
word = (word ^ (word << 4)) & 0x0f0f0f0f;
|
||||
word = (word ^ (word << 2)) & 0x33333333;
|
||||
word = (word ^ (word << 1)) & 0x55555555;
|
||||
word
|
||||
}
|
||||
|
||||
// Reverses interleaving function by removing interleaved zeros.
|
||||
pub fn compress_u32(word: u32) -> u16 {
|
||||
let mut word = word;
|
||||
assert_eq!(word & MASK_EVEN_32, word);
|
||||
word = (word | (word >> 1)) & 0x33333333;
|
||||
word = (word | (word >> 2)) & 0x0f0f0f0f;
|
||||
word = (word | (word >> 4)) & 0x00ff00ff;
|
||||
word = (word | (word >> 8)) & 0x0000ffff;
|
||||
word as u16
|
||||
}
|
||||
|
||||
// Chops a 32-bit word into pieces of given length. The lengths are specified
|
||||
// starting from the little end.
|
||||
pub fn chop_u32(word: u32, lengths: &[u8]) -> Vec<u32> {
|
||||
assert_eq!(lengths.iter().sum::<u8>(), 32u8);
|
||||
let mut pieces: Vec<u32> = Vec::with_capacity(lengths.len());
|
||||
for i in 0..lengths.len() {
|
||||
assert!(lengths[i] > 0);
|
||||
// lengths[i] bitstring of all 1's
|
||||
let mask: u32 = (1 << lengths[i]) as u32 - 1;
|
||||
// Shift mask by bits already shifted
|
||||
let offset: u8 = lengths[0..i].iter().sum();
|
||||
let mask: u32 = mask << offset;
|
||||
pieces.push((word & mask) >> offset as u32);
|
||||
}
|
||||
pieces
|
||||
}
|
||||
|
||||
// Chops a 64-bit word into pieces of given length. The lengths are specified
|
||||
// starting from the little end.
|
||||
pub fn chop_u64(word: u64, lengths: &[u8]) -> Vec<u64> {
|
||||
assert_eq!(lengths.iter().sum::<u8>(), 64u8);
|
||||
let mut pieces: Vec<u64> = Vec::with_capacity(lengths.len());
|
||||
for i in 0..lengths.len() {
|
||||
assert!(lengths[i] > 0);
|
||||
// lengths[i] bitstring of all 1's
|
||||
let mask: u64 = (1u64 << lengths[i]) - 1;
|
||||
// Shift mask by bits already shifted
|
||||
let offset: u8 = lengths[0..i].iter().sum();
|
||||
let mask: u64 = mask << offset;
|
||||
pieces.push((word & mask) >> offset as u64);
|
||||
}
|
||||
pieces
|
||||
}
|
||||
|
||||
// Returns compressed even and odd bits of 32-bit word
|
||||
pub fn get_even_and_odd_bits_u32(word: u32) -> (u16, u16) {
|
||||
let even = word & MASK_EVEN_32;
|
||||
let odd = (word & MASK_ODD_32) >> 1;
|
||||
(compress_u32(even), compress_u32(odd))
|
||||
}
|
||||
|
||||
// Split 4-bit value into 2-bit lo and hi halves
|
||||
pub fn bisect_four_bit(word: u32) -> (u32, u32) {
|
||||
assert!(word < 16); // 4-bit range-check
|
||||
let word_hi = (word & 0b1100) >> 2;
|
||||
let word_lo = word & 0b0011;
|
||||
(word_lo, word_hi)
|
||||
}
|
|
@ -79,7 +79,9 @@ impl std::ops::Deref for RegionStart {
|
|||
pub struct Cell {
|
||||
/// Identifies the region in which this cell resides.
|
||||
region_index: RegionIndex,
|
||||
/// The relative offset of this cell within its region.
|
||||
row_offset: usize,
|
||||
/// The column of this cell.
|
||||
column: Column<Any>,
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
//! Self-contained circuit implementations of various primitives.
|
||||
|
||||
pub mod sha256;
|
|
@ -19,7 +19,6 @@
|
|||
|
||||
pub mod arithmetic;
|
||||
pub mod circuit;
|
||||
pub mod gadget;
|
||||
pub mod pasta;
|
||||
pub mod plonk;
|
||||
pub mod poly;
|
||||
|
|
|
@ -198,7 +198,9 @@ impl Selector {
|
|||
/// A permutation.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Permutation {
|
||||
/// The index of this permutation.
|
||||
index: usize,
|
||||
/// The mapping between columns involved in this permutation.
|
||||
mapping: Vec<Column<Any>>,
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue