mirror of https://github.com/zcash/halo2.git
commit
505e003842
|
@ -31,6 +31,9 @@ rand = "0.8"
|
||||||
nonempty = "0.6"
|
nonempty = "0.6"
|
||||||
subtle = "2.3"
|
subtle = "2.3"
|
||||||
|
|
||||||
|
# Developer tooling dependencies
|
||||||
|
plotters = { version = "0.3.0", optional = true }
|
||||||
|
|
||||||
[dependencies.halo2]
|
[dependencies.halo2]
|
||||||
git = "https://github.com/zcash/halo2.git"
|
git = "https://github.com/zcash/halo2.git"
|
||||||
rev = "0448584333c1e262e4a7dbaefa6fdd896bdaaefb"
|
rev = "0448584333c1e262e4a7dbaefa6fdd896bdaaefb"
|
||||||
|
@ -52,6 +55,7 @@ proptest = "1.0.0"
|
||||||
bench = false
|
bench = false
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
dev-graph = ["halo2/dev-graph", "plotters"]
|
||||||
test-dependencies = ["proptest"]
|
test-dependencies = ["proptest"]
|
||||||
|
|
||||||
[[bench]]
|
[[bench]]
|
||||||
|
|
|
@ -1 +1,2 @@
|
||||||
pub(crate) mod ecc;
|
pub(crate) mod ecc;
|
||||||
|
pub(crate) mod poseidon;
|
||||||
|
|
|
@ -0,0 +1,244 @@
|
||||||
|
//! Gadget and chips for the Poseidon algebraic hash function.
|
||||||
|
|
||||||
|
use std::array;
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
use halo2::{
|
||||||
|
arithmetic::FieldExt,
|
||||||
|
circuit::{Chip, Layouter},
|
||||||
|
plonk::Error,
|
||||||
|
};
|
||||||
|
|
||||||
|
mod pow5t3;
|
||||||
|
pub use pow5t3::{Pow5T3Chip, Pow5T3Config};
|
||||||
|
|
||||||
|
use crate::primitives::poseidon::{ConstantLength, Domain, Spec, Sponge, SpongeState, State};
|
||||||
|
|
||||||
|
/// The set of circuit instructions required to use the Poseidon permutation.
|
||||||
|
pub trait PoseidonInstructions<F: FieldExt, S: Spec<F, T, RATE>, const T: usize, const RATE: usize>:
|
||||||
|
Chip<F>
|
||||||
|
{
|
||||||
|
/// Variable representing the word over which the Poseidon permutation operates.
|
||||||
|
type Word: Copy + fmt::Debug;
|
||||||
|
|
||||||
|
/// Applies the Poseidon permutation to the given state.
|
||||||
|
fn permute(
|
||||||
|
&self,
|
||||||
|
layouter: &mut impl Layouter<F>,
|
||||||
|
initial_state: &State<Self::Word, T>,
|
||||||
|
) -> Result<State<Self::Word, T>, Error>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The set of circuit instructions required to use the [`Duplex`] and [`Hash`] gadgets.
|
||||||
|
///
|
||||||
|
/// [`Hash`]: self::Hash
|
||||||
|
pub trait PoseidonDuplexInstructions<
|
||||||
|
F: FieldExt,
|
||||||
|
S: Spec<F, T, RATE>,
|
||||||
|
const T: usize,
|
||||||
|
const RATE: usize,
|
||||||
|
>: PoseidonInstructions<F, S, T, RATE>
|
||||||
|
{
|
||||||
|
/// Returns the initial empty state for the given domain.
|
||||||
|
fn initial_state(
|
||||||
|
&self,
|
||||||
|
layouter: &mut impl Layouter<F>,
|
||||||
|
domain: &impl Domain<F, S, T, RATE>,
|
||||||
|
) -> Result<State<Self::Word, T>, Error>;
|
||||||
|
|
||||||
|
/// Pads the given input (according to the specified domain) and adds it to the state.
|
||||||
|
fn pad_and_add(
|
||||||
|
&self,
|
||||||
|
layouter: &mut impl Layouter<F>,
|
||||||
|
domain: &impl Domain<F, S, T, RATE>,
|
||||||
|
initial_state: &State<Self::Word, T>,
|
||||||
|
input: &SpongeState<Self::Word, RATE>,
|
||||||
|
) -> Result<State<Self::Word, T>, Error>;
|
||||||
|
|
||||||
|
/// Extracts sponge output from the given state.
|
||||||
|
fn get_output(state: &State<Self::Word, T>) -> SpongeState<Self::Word, RATE>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A word over which the Poseidon permutation operates.
|
||||||
|
pub struct Word<
|
||||||
|
F: FieldExt,
|
||||||
|
PoseidonChip: PoseidonInstructions<F, S, T, RATE>,
|
||||||
|
S: Spec<F, T, RATE>,
|
||||||
|
const T: usize,
|
||||||
|
const RATE: usize,
|
||||||
|
> {
|
||||||
|
inner: PoseidonChip::Word,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn poseidon_duplex<
|
||||||
|
F: FieldExt,
|
||||||
|
PoseidonChip: PoseidonDuplexInstructions<F, S, T, RATE>,
|
||||||
|
S: Spec<F, T, RATE>,
|
||||||
|
D: Domain<F, S, T, RATE>,
|
||||||
|
const T: usize,
|
||||||
|
const RATE: usize,
|
||||||
|
>(
|
||||||
|
chip: &PoseidonChip,
|
||||||
|
mut layouter: impl Layouter<F>,
|
||||||
|
domain: &D,
|
||||||
|
state: &mut State<PoseidonChip::Word, T>,
|
||||||
|
input: &SpongeState<PoseidonChip::Word, RATE>,
|
||||||
|
) -> Result<SpongeState<PoseidonChip::Word, RATE>, Error> {
|
||||||
|
*state = chip.pad_and_add(&mut layouter, domain, state, input)?;
|
||||||
|
*state = chip.permute(&mut layouter, state)?;
|
||||||
|
Ok(PoseidonChip::get_output(state))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A Poseidon duplex sponge.
|
||||||
|
pub struct Duplex<
|
||||||
|
F: FieldExt,
|
||||||
|
PoseidonChip: PoseidonDuplexInstructions<F, S, T, RATE>,
|
||||||
|
S: Spec<F, T, RATE>,
|
||||||
|
D: Domain<F, S, T, RATE>,
|
||||||
|
const T: usize,
|
||||||
|
const RATE: usize,
|
||||||
|
> {
|
||||||
|
chip: PoseidonChip,
|
||||||
|
sponge: Sponge<PoseidonChip::Word, RATE>,
|
||||||
|
state: State<PoseidonChip::Word, T>,
|
||||||
|
domain: D,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<
|
||||||
|
F: FieldExt,
|
||||||
|
PoseidonChip: PoseidonDuplexInstructions<F, S, T, RATE>,
|
||||||
|
S: Spec<F, T, RATE>,
|
||||||
|
D: Domain<F, S, T, RATE>,
|
||||||
|
const T: usize,
|
||||||
|
const RATE: usize,
|
||||||
|
> Duplex<F, PoseidonChip, S, D, T, RATE>
|
||||||
|
{
|
||||||
|
/// Constructs a new duplex sponge for the given Poseidon specification.
|
||||||
|
pub fn new(
|
||||||
|
chip: PoseidonChip,
|
||||||
|
mut layouter: impl Layouter<F>,
|
||||||
|
domain: D,
|
||||||
|
) -> Result<Self, Error> {
|
||||||
|
chip.initial_state(&mut layouter, &domain)
|
||||||
|
.map(|state| Duplex {
|
||||||
|
chip,
|
||||||
|
sponge: Sponge::Absorbing([None; RATE]),
|
||||||
|
state,
|
||||||
|
domain,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Absorbs an element into the sponge.
|
||||||
|
pub fn absorb(
|
||||||
|
&mut self,
|
||||||
|
mut layouter: impl Layouter<F>,
|
||||||
|
value: Word<F, PoseidonChip, S, T, RATE>,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
match self.sponge {
|
||||||
|
Sponge::Absorbing(ref mut input) => {
|
||||||
|
for entry in input.iter_mut() {
|
||||||
|
if entry.is_none() {
|
||||||
|
*entry = Some(value.inner);
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We've already absorbed as many elements as we can
|
||||||
|
let _ = poseidon_duplex(
|
||||||
|
&self.chip,
|
||||||
|
layouter.namespace(|| "PoseidonDuplex"),
|
||||||
|
&self.domain,
|
||||||
|
&mut self.state,
|
||||||
|
&input,
|
||||||
|
)?;
|
||||||
|
self.sponge = Sponge::absorb(value.inner);
|
||||||
|
}
|
||||||
|
Sponge::Squeezing(_) => {
|
||||||
|
// Drop the remaining output elements
|
||||||
|
self.sponge = Sponge::absorb(value.inner);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Squeezes an element from the sponge.
|
||||||
|
pub fn squeeze(
|
||||||
|
&mut self,
|
||||||
|
mut layouter: impl Layouter<F>,
|
||||||
|
) -> Result<Word<F, PoseidonChip, S, T, RATE>, Error> {
|
||||||
|
loop {
|
||||||
|
match self.sponge {
|
||||||
|
Sponge::Absorbing(ref input) => {
|
||||||
|
self.sponge = Sponge::Squeezing(poseidon_duplex(
|
||||||
|
&self.chip,
|
||||||
|
layouter.namespace(|| "PoseidonDuplex"),
|
||||||
|
&self.domain,
|
||||||
|
&mut self.state,
|
||||||
|
&input,
|
||||||
|
)?);
|
||||||
|
}
|
||||||
|
Sponge::Squeezing(ref mut output) => {
|
||||||
|
for entry in output.iter_mut() {
|
||||||
|
if let Some(inner) = entry.take() {
|
||||||
|
return Ok(Word { inner });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We've already squeezed out all available elements
|
||||||
|
self.sponge = Sponge::Absorbing([None; RATE]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A Poseidon hash function, built around a duplex sponge.
|
||||||
|
pub struct Hash<
|
||||||
|
F: FieldExt,
|
||||||
|
PoseidonChip: PoseidonDuplexInstructions<F, S, T, RATE>,
|
||||||
|
S: Spec<F, T, RATE>,
|
||||||
|
D: Domain<F, S, T, RATE>,
|
||||||
|
const T: usize,
|
||||||
|
const RATE: usize,
|
||||||
|
> {
|
||||||
|
duplex: Duplex<F, PoseidonChip, S, D, T, RATE>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<
|
||||||
|
F: FieldExt,
|
||||||
|
PoseidonChip: PoseidonDuplexInstructions<F, S, T, RATE>,
|
||||||
|
S: Spec<F, T, RATE>,
|
||||||
|
D: Domain<F, S, T, RATE>,
|
||||||
|
const T: usize,
|
||||||
|
const RATE: usize,
|
||||||
|
> Hash<F, PoseidonChip, S, D, T, RATE>
|
||||||
|
{
|
||||||
|
/// Initializes a new hasher.
|
||||||
|
pub fn init(chip: PoseidonChip, layouter: impl Layouter<F>, domain: D) -> Result<Self, Error> {
|
||||||
|
Duplex::new(chip, layouter, domain).map(|duplex| Hash { duplex })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<
|
||||||
|
F: FieldExt,
|
||||||
|
PoseidonChip: PoseidonDuplexInstructions<F, S, T, RATE>,
|
||||||
|
S: Spec<F, T, RATE>,
|
||||||
|
const T: usize,
|
||||||
|
const RATE: usize,
|
||||||
|
const L: usize,
|
||||||
|
> Hash<F, PoseidonChip, S, ConstantLength<L>, T, RATE>
|
||||||
|
{
|
||||||
|
/// Hashes the given input.
|
||||||
|
pub fn hash(
|
||||||
|
mut self,
|
||||||
|
mut layouter: impl Layouter<F>,
|
||||||
|
message: [Word<F, PoseidonChip, S, T, RATE>; L],
|
||||||
|
) -> Result<Word<F, PoseidonChip, S, T, RATE>, Error> {
|
||||||
|
for (i, value) in array::IntoIter::new(message).enumerate() {
|
||||||
|
self.duplex
|
||||||
|
.absorb(layouter.namespace(|| format!("absorb_{}", i)), value)?;
|
||||||
|
}
|
||||||
|
self.duplex.squeeze(layouter.namespace(|| "squeeze"))
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,822 @@
|
||||||
|
use halo2::{
|
||||||
|
arithmetic::FieldExt,
|
||||||
|
circuit::{Cell, Chip, Layouter, Region},
|
||||||
|
plonk::{Advice, Column, ConstraintSystem, Error, Expression, Fixed, Permutation, Selector},
|
||||||
|
poly::Rotation,
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::{PoseidonDuplexInstructions, PoseidonInstructions};
|
||||||
|
use crate::primitives::poseidon::{Domain, Mds, Spec, SpongeState, State};
|
||||||
|
|
||||||
|
const WIDTH: usize = 3;
|
||||||
|
|
||||||
|
/// Configuration for an [`Pow5T3Chip`].
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct Pow5T3Config<F: FieldExt> {
|
||||||
|
state: [Column<Advice>; WIDTH],
|
||||||
|
state_permutation: Permutation,
|
||||||
|
partial_sbox: Column<Advice>,
|
||||||
|
rc_a: [Column<Fixed>; WIDTH],
|
||||||
|
rc_b: [Column<Fixed>; WIDTH],
|
||||||
|
s_full: Selector,
|
||||||
|
s_partial: Selector,
|
||||||
|
s_pad_and_add: Selector,
|
||||||
|
|
||||||
|
half_full_rounds: usize,
|
||||||
|
half_partial_rounds: usize,
|
||||||
|
alpha: [u64; 4],
|
||||||
|
round_constants: Vec<[F; WIDTH]>,
|
||||||
|
m_reg: Mds<F, WIDTH>,
|
||||||
|
m_inv: Mds<F, WIDTH>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A Poseidon chip using an $x^5$ S-Box, with a width of 3, suitable for a 2:1 reduction.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Pow5T3Chip<F: FieldExt> {
|
||||||
|
config: Pow5T3Config<F>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F: FieldExt> Pow5T3Chip<F> {
|
||||||
|
/// Configures this chip for use in a circuit.
|
||||||
|
//
|
||||||
|
// TODO: Does the rate need to be hard-coded here, or only the width? It probably
|
||||||
|
// needs to be known wherever we implement the hashing gadget, but it isn't strictly
|
||||||
|
// necessary for the permutation.
|
||||||
|
pub fn configure<S: Spec<F, WIDTH, 2>>(
|
||||||
|
meta: &mut ConstraintSystem<F>,
|
||||||
|
spec: S,
|
||||||
|
state: [Column<Advice>; WIDTH],
|
||||||
|
) -> Pow5T3Config<F> {
|
||||||
|
// Generate constants for the Poseidon permutation.
|
||||||
|
// This gadget requires R_F and R_P to be even.
|
||||||
|
assert!(S::full_rounds() & 1 == 0);
|
||||||
|
assert!(S::partial_rounds() & 1 == 0);
|
||||||
|
let half_full_rounds = S::full_rounds() / 2;
|
||||||
|
let half_partial_rounds = S::partial_rounds() / 2;
|
||||||
|
let (round_constants, m_reg, m_inv) = spec.constants();
|
||||||
|
|
||||||
|
let partial_sbox = meta.advice_column();
|
||||||
|
|
||||||
|
let rc_a = [
|
||||||
|
meta.fixed_column(),
|
||||||
|
meta.fixed_column(),
|
||||||
|
meta.fixed_column(),
|
||||||
|
];
|
||||||
|
let rc_b = [
|
||||||
|
meta.fixed_column(),
|
||||||
|
meta.fixed_column(),
|
||||||
|
meta.fixed_column(),
|
||||||
|
];
|
||||||
|
|
||||||
|
// This allows state words to be initialized (by constraining them equal to fixed
|
||||||
|
// values), and used in a permutation from an arbitrary region. rc_a is used in
|
||||||
|
// every permutation round, while rc_b is empty in the initial and final full
|
||||||
|
// rounds, so we use rc_b as "scratch space" for fixed values (enabling potential
|
||||||
|
// layouter optimisations).
|
||||||
|
let state_permutation = Permutation::new(
|
||||||
|
meta,
|
||||||
|
&[
|
||||||
|
state[0].into(),
|
||||||
|
state[1].into(),
|
||||||
|
state[2].into(),
|
||||||
|
rc_b[0].into(),
|
||||||
|
rc_b[1].into(),
|
||||||
|
rc_b[2].into(),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
let s_full = meta.selector();
|
||||||
|
let s_partial = meta.selector();
|
||||||
|
let s_pad_and_add = meta.selector();
|
||||||
|
|
||||||
|
let alpha = [5, 0, 0, 0];
|
||||||
|
let pow_5 = |v: Expression<F>| {
|
||||||
|
let v2 = v.clone() * v.clone();
|
||||||
|
v2.clone() * v2 * v
|
||||||
|
};
|
||||||
|
|
||||||
|
meta.create_gate("full round", |meta| {
|
||||||
|
let cur_0 = meta.query_advice(state[0], Rotation::cur());
|
||||||
|
let cur_1 = meta.query_advice(state[1], Rotation::cur());
|
||||||
|
let cur_2 = meta.query_advice(state[2], Rotation::cur());
|
||||||
|
let next = [
|
||||||
|
meta.query_advice(state[0], Rotation::next()),
|
||||||
|
meta.query_advice(state[1], Rotation::next()),
|
||||||
|
meta.query_advice(state[2], Rotation::next()),
|
||||||
|
];
|
||||||
|
|
||||||
|
let rc_0 = meta.query_fixed(rc_a[0], Rotation::cur());
|
||||||
|
let rc_1 = meta.query_fixed(rc_a[1], Rotation::cur());
|
||||||
|
let rc_2 = meta.query_fixed(rc_a[2], Rotation::cur());
|
||||||
|
|
||||||
|
let s_full = meta.query_selector(s_full, Rotation::cur());
|
||||||
|
|
||||||
|
let full_round = |next_idx: usize| {
|
||||||
|
s_full.clone()
|
||||||
|
* (pow_5(cur_0.clone() + rc_0.clone()) * m_reg[next_idx][0]
|
||||||
|
+ pow_5(cur_1.clone() + rc_1.clone()) * m_reg[next_idx][1]
|
||||||
|
+ pow_5(cur_2.clone() + rc_2.clone()) * m_reg[next_idx][2]
|
||||||
|
- next[next_idx].clone())
|
||||||
|
};
|
||||||
|
|
||||||
|
vec![full_round(0), full_round(1), full_round(2)]
|
||||||
|
});
|
||||||
|
|
||||||
|
meta.create_gate("partial round", |meta| {
|
||||||
|
let cur_0 = meta.query_advice(state[0], Rotation::cur());
|
||||||
|
let cur_1 = meta.query_advice(state[1], Rotation::cur());
|
||||||
|
let cur_2 = meta.query_advice(state[2], Rotation::cur());
|
||||||
|
let mid_0 = meta.query_advice(partial_sbox, Rotation::cur());
|
||||||
|
let next_0 = meta.query_advice(state[0], Rotation::next());
|
||||||
|
let next_1 = meta.query_advice(state[1], Rotation::next());
|
||||||
|
let next_2 = meta.query_advice(state[2], Rotation::next());
|
||||||
|
|
||||||
|
let rc_a0 = meta.query_fixed(rc_a[0], Rotation::cur());
|
||||||
|
let rc_a1 = meta.query_fixed(rc_a[1], Rotation::cur());
|
||||||
|
let rc_a2 = meta.query_fixed(rc_a[2], Rotation::cur());
|
||||||
|
let rc_b0 = meta.query_fixed(rc_b[0], Rotation::cur());
|
||||||
|
let rc_b1 = meta.query_fixed(rc_b[1], Rotation::cur());
|
||||||
|
let rc_b2 = meta.query_fixed(rc_b[2], Rotation::cur());
|
||||||
|
|
||||||
|
let s_partial = meta.query_selector(s_partial, Rotation::cur());
|
||||||
|
|
||||||
|
let partial_round_linear = |idx: usize, rc_b: Expression<F>| {
|
||||||
|
s_partial.clone()
|
||||||
|
* (mid_0.clone() * m_reg[idx][0]
|
||||||
|
+ (cur_1.clone() + rc_a1.clone()) * m_reg[idx][1]
|
||||||
|
+ (cur_2.clone() + rc_a2.clone()) * m_reg[idx][2]
|
||||||
|
+ rc_b
|
||||||
|
- (next_0.clone() * m_inv[idx][0]
|
||||||
|
+ next_1.clone() * m_inv[idx][1]
|
||||||
|
+ next_2.clone() * m_inv[idx][2]))
|
||||||
|
};
|
||||||
|
|
||||||
|
vec![
|
||||||
|
s_partial.clone() * (pow_5(cur_0 + rc_a0) - mid_0.clone()),
|
||||||
|
s_partial.clone()
|
||||||
|
* (pow_5(
|
||||||
|
mid_0.clone() * m_reg[0][0]
|
||||||
|
+ (cur_1.clone() + rc_a1.clone()) * m_reg[0][1]
|
||||||
|
+ (cur_2.clone() + rc_a2.clone()) * m_reg[0][2]
|
||||||
|
+ rc_b0,
|
||||||
|
) - (next_0.clone() * m_inv[0][0]
|
||||||
|
+ next_1.clone() * m_inv[0][1]
|
||||||
|
+ next_2.clone() * m_inv[0][2])),
|
||||||
|
partial_round_linear(1, rc_b1),
|
||||||
|
partial_round_linear(2, rc_b2),
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
meta.create_gate("pad-and-add", |meta| {
|
||||||
|
let initial_state_0 = meta.query_advice(state[0], Rotation::prev());
|
||||||
|
let initial_state_1 = meta.query_advice(state[1], Rotation::prev());
|
||||||
|
let initial_state_2 = meta.query_advice(state[2], Rotation::prev());
|
||||||
|
let input_0 = meta.query_advice(state[0], Rotation::cur());
|
||||||
|
let input_1 = meta.query_advice(state[1], Rotation::cur());
|
||||||
|
let output_state_0 = meta.query_advice(state[0], Rotation::next());
|
||||||
|
let output_state_1 = meta.query_advice(state[1], Rotation::next());
|
||||||
|
let output_state_2 = meta.query_advice(state[2], Rotation::next());
|
||||||
|
|
||||||
|
let s_pad_and_add = meta.query_selector(s_pad_and_add, Rotation::cur());
|
||||||
|
|
||||||
|
let pad_and_add = |initial_state, input, output_state| {
|
||||||
|
// We pad the input by storing the required padding in fixed columns and
|
||||||
|
// then constraining the corresponding input columns to be equal to it.
|
||||||
|
s_pad_and_add.clone() * (initial_state + input - output_state)
|
||||||
|
};
|
||||||
|
|
||||||
|
vec![
|
||||||
|
pad_and_add(initial_state_0, input_0, output_state_0),
|
||||||
|
pad_and_add(initial_state_1, input_1, output_state_1),
|
||||||
|
// The capacity element is never altered by the input.
|
||||||
|
s_pad_and_add * (initial_state_2 - output_state_2),
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
Pow5T3Config {
|
||||||
|
state,
|
||||||
|
state_permutation,
|
||||||
|
partial_sbox,
|
||||||
|
rc_a,
|
||||||
|
rc_b,
|
||||||
|
s_full,
|
||||||
|
s_partial,
|
||||||
|
s_pad_and_add,
|
||||||
|
half_full_rounds,
|
||||||
|
half_partial_rounds,
|
||||||
|
alpha,
|
||||||
|
round_constants,
|
||||||
|
m_reg,
|
||||||
|
m_inv,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn construct(config: Pow5T3Config<F>) -> Self {
|
||||||
|
Pow5T3Chip { config }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F: FieldExt> Chip<F> for Pow5T3Chip<F> {
|
||||||
|
type Config = Pow5T3Config<F>;
|
||||||
|
type Loaded = ();
|
||||||
|
|
||||||
|
fn config(&self) -> &Self::Config {
|
||||||
|
&self.config
|
||||||
|
}
|
||||||
|
|
||||||
|
fn loaded(&self) -> &Self::Loaded {
|
||||||
|
&()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F: FieldExt, S: Spec<F, WIDTH, 2>> PoseidonInstructions<F, S, WIDTH, 2> for Pow5T3Chip<F> {
|
||||||
|
type Word = StateWord<F>;
|
||||||
|
|
||||||
|
fn permute(
|
||||||
|
&self,
|
||||||
|
layouter: &mut impl Layouter<F>,
|
||||||
|
initial_state: &State<Self::Word, WIDTH>,
|
||||||
|
) -> Result<State<Self::Word, WIDTH>, Error> {
|
||||||
|
let config = self.config();
|
||||||
|
|
||||||
|
layouter.assign_region(
|
||||||
|
|| "permute state",
|
||||||
|
|mut region| {
|
||||||
|
// Load the initial state into this region.
|
||||||
|
let state = Pow5T3State::load(&mut region, &config, initial_state)?;
|
||||||
|
|
||||||
|
let state = (0..config.half_full_rounds).fold(Ok(state), |res, r| {
|
||||||
|
res.and_then(|state| state.full_round(&mut region, &config, r, r))
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let state = (0..config.half_partial_rounds).fold(Ok(state), |res, r| {
|
||||||
|
res.and_then(|state| {
|
||||||
|
state.partial_round(
|
||||||
|
&mut region,
|
||||||
|
&config,
|
||||||
|
config.half_full_rounds + 2 * r,
|
||||||
|
config.half_full_rounds + r,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let state = (0..config.half_full_rounds).fold(Ok(state), |res, r| {
|
||||||
|
res.and_then(|state| {
|
||||||
|
state.full_round(
|
||||||
|
&mut region,
|
||||||
|
&config,
|
||||||
|
config.half_full_rounds + 2 * config.half_partial_rounds + r,
|
||||||
|
config.half_full_rounds + config.half_partial_rounds + r,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(state.0)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F: FieldExt, S: Spec<F, WIDTH, 2>> PoseidonDuplexInstructions<F, S, WIDTH, 2>
|
||||||
|
for Pow5T3Chip<F>
|
||||||
|
{
|
||||||
|
fn initial_state(
|
||||||
|
&self,
|
||||||
|
layouter: &mut impl Layouter<F>,
|
||||||
|
domain: &impl Domain<F, S, WIDTH, 2>,
|
||||||
|
) -> Result<State<Self::Word, WIDTH>, Error> {
|
||||||
|
let config = self.config();
|
||||||
|
layouter.assign_region(
|
||||||
|
|| format!("initial state for domain {:?}", domain),
|
||||||
|
|mut region| {
|
||||||
|
let mut load_state_word = |i: usize, value: F| {
|
||||||
|
let var = region.assign_advice(
|
||||||
|
|| format!("state_{}", i),
|
||||||
|
config.state[i],
|
||||||
|
0,
|
||||||
|
|| Ok(value),
|
||||||
|
)?;
|
||||||
|
let fixed = region.assign_fixed(
|
||||||
|
|| format!("state_{}", i),
|
||||||
|
config.rc_b[i],
|
||||||
|
0,
|
||||||
|
|| Ok(value),
|
||||||
|
)?;
|
||||||
|
region.constrain_equal(&config.state_permutation, var, fixed)?;
|
||||||
|
Ok(StateWord {
|
||||||
|
var,
|
||||||
|
value: Some(value),
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok([
|
||||||
|
load_state_word(0, F::zero())?,
|
||||||
|
load_state_word(1, F::zero())?,
|
||||||
|
load_state_word(2, domain.initial_capacity_element())?,
|
||||||
|
])
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pad_and_add(
|
||||||
|
&self,
|
||||||
|
layouter: &mut impl Layouter<F>,
|
||||||
|
domain: &impl Domain<F, S, WIDTH, 2>,
|
||||||
|
initial_state: &State<Self::Word, WIDTH>,
|
||||||
|
input: &SpongeState<Self::Word, 2>,
|
||||||
|
) -> Result<State<Self::Word, WIDTH>, Error> {
|
||||||
|
let config = self.config();
|
||||||
|
layouter.assign_region(
|
||||||
|
|| format!("pad-and-add for domain {:?}", domain),
|
||||||
|
|mut region| {
|
||||||
|
config.s_pad_and_add.enable(&mut region, 1)?;
|
||||||
|
|
||||||
|
// Load the initial state into this region.
|
||||||
|
let mut load_state_word = |i: usize| {
|
||||||
|
let value = initial_state[i].value;
|
||||||
|
let var = region.assign_advice(
|
||||||
|
|| format!("load state_{}", i),
|
||||||
|
config.state[i],
|
||||||
|
0,
|
||||||
|
|| value.ok_or(Error::SynthesisError),
|
||||||
|
)?;
|
||||||
|
region.constrain_equal(&config.state_permutation, initial_state[i].var, var)?;
|
||||||
|
Ok(StateWord { var, value })
|
||||||
|
};
|
||||||
|
let initial_state = [
|
||||||
|
load_state_word(0)?,
|
||||||
|
load_state_word(1)?,
|
||||||
|
load_state_word(2)?,
|
||||||
|
];
|
||||||
|
|
||||||
|
let padding_values = domain.padding();
|
||||||
|
|
||||||
|
// Load the input and padding into this region.
|
||||||
|
let mut load_input_word = |i: usize| {
|
||||||
|
let (constraint_var, value) = match (input[i], padding_values[i]) {
|
||||||
|
(Some(word), None) => (word.var, word.value),
|
||||||
|
(None, Some(padding_value)) => {
|
||||||
|
let padding_var = region.assign_fixed(
|
||||||
|
|| format!("load pad_{}", i),
|
||||||
|
config.rc_b[i],
|
||||||
|
1,
|
||||||
|
|| Ok(padding_value),
|
||||||
|
)?;
|
||||||
|
(padding_var, Some(padding_value))
|
||||||
|
}
|
||||||
|
_ => panic!("Input and padding don't match"),
|
||||||
|
};
|
||||||
|
let var = region.assign_advice(
|
||||||
|
|| format!("load input_{}", i),
|
||||||
|
config.state[i],
|
||||||
|
1,
|
||||||
|
|| value.ok_or(Error::SynthesisError),
|
||||||
|
)?;
|
||||||
|
region.constrain_equal(&config.state_permutation, constraint_var, var)?;
|
||||||
|
|
||||||
|
Ok(StateWord { var, value })
|
||||||
|
};
|
||||||
|
let input = [load_input_word(0)?, load_input_word(1)?];
|
||||||
|
|
||||||
|
// Constrain the output.
|
||||||
|
let mut constrain_output_word = |i: usize| {
|
||||||
|
let value = initial_state[i].value.and_then(|initial_word| {
|
||||||
|
input
|
||||||
|
.get(i)
|
||||||
|
.map(|word| word.value)
|
||||||
|
// The capacity element is never altered by the input.
|
||||||
|
.unwrap_or_else(|| Some(F::zero()))
|
||||||
|
.map(|input_word| initial_word + input_word)
|
||||||
|
});
|
||||||
|
let var = region.assign_advice(
|
||||||
|
|| format!("load output_{}", i),
|
||||||
|
config.state[i],
|
||||||
|
2,
|
||||||
|
|| value.ok_or(Error::SynthesisError),
|
||||||
|
)?;
|
||||||
|
Ok(StateWord { var, value })
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok([
|
||||||
|
constrain_output_word(0)?,
|
||||||
|
constrain_output_word(1)?,
|
||||||
|
constrain_output_word(2)?,
|
||||||
|
])
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_output(state: &State<Self::Word, WIDTH>) -> SpongeState<Self::Word, 2> {
|
||||||
|
[Some(state[0]), Some(state[1])]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub struct StateWord<F: FieldExt> {
|
||||||
|
var: Cell,
|
||||||
|
value: Option<F>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct Pow5T3State<F: FieldExt>([StateWord<F>; WIDTH]);
|
||||||
|
|
||||||
|
impl<F: FieldExt> Pow5T3State<F> {
|
||||||
|
fn full_round(
|
||||||
|
self,
|
||||||
|
region: &mut Region<F>,
|
||||||
|
config: &Pow5T3Config<F>,
|
||||||
|
round: usize,
|
||||||
|
offset: usize,
|
||||||
|
) -> Result<Self, Error> {
|
||||||
|
Self::round(region, config, round, offset, config.s_full, |_| {
|
||||||
|
let q_0 = self.0[0]
|
||||||
|
.value
|
||||||
|
.map(|v| v + config.round_constants[round][0]);
|
||||||
|
let q_1 = self.0[1]
|
||||||
|
.value
|
||||||
|
.map(|v| v + config.round_constants[round][1]);
|
||||||
|
let q_2 = self.0[2]
|
||||||
|
.value
|
||||||
|
.map(|v| v + config.round_constants[round][2]);
|
||||||
|
|
||||||
|
let r_0 = q_0.map(|v| v.pow(&config.alpha));
|
||||||
|
let r_1 = q_1.map(|v| v.pow(&config.alpha));
|
||||||
|
let r_2 = q_2.map(|v| v.pow(&config.alpha));
|
||||||
|
|
||||||
|
let m = &config.m_reg;
|
||||||
|
let r = r_0.and_then(|r_0| r_1.and_then(|r_1| r_2.map(|r_2| [r_0, r_1, r_2])));
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
round + 1,
|
||||||
|
[
|
||||||
|
r.map(|r| m[0][0] * r[0] + m[0][1] * r[1] + m[0][2] * r[2]),
|
||||||
|
r.map(|r| m[1][0] * r[0] + m[1][1] * r[1] + m[1][2] * r[2]),
|
||||||
|
r.map(|r| m[2][0] * r[0] + m[2][1] * r[1] + m[2][2] * r[2]),
|
||||||
|
],
|
||||||
|
))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn partial_round(
|
||||||
|
self,
|
||||||
|
region: &mut Region<F>,
|
||||||
|
config: &Pow5T3Config<F>,
|
||||||
|
round: usize,
|
||||||
|
offset: usize,
|
||||||
|
) -> Result<Self, Error> {
|
||||||
|
Self::round(region, config, round, offset, config.s_partial, |region| {
|
||||||
|
let m = &config.m_reg;
|
||||||
|
|
||||||
|
let p = self.0[0].value.and_then(|p_0| {
|
||||||
|
self.0[1]
|
||||||
|
.value
|
||||||
|
.and_then(|p_1| self.0[2].value.map(|p_2| [p_0, p_1, p_2]))
|
||||||
|
});
|
||||||
|
|
||||||
|
let r = p.map(|p| {
|
||||||
|
[
|
||||||
|
(p[0] + config.round_constants[round][0]).pow(&config.alpha),
|
||||||
|
p[1] + config.round_constants[round][1],
|
||||||
|
p[2] + config.round_constants[round][2],
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
region.assign_advice(
|
||||||
|
|| format!("round_{} partial_sbox", round),
|
||||||
|
config.partial_sbox,
|
||||||
|
offset,
|
||||||
|
|| r.map(|r| r[0]).ok_or(Error::SynthesisError),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let p_mid = r.map(|r| {
|
||||||
|
[
|
||||||
|
m[0][0] * r[0] + m[0][1] * r[1] + m[0][2] * r[2],
|
||||||
|
m[1][0] * r[0] + m[1][1] * r[1] + m[1][2] * r[2],
|
||||||
|
m[2][0] * r[0] + m[2][1] * r[1] + m[2][2] * r[2],
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
// Load the second round constants.
|
||||||
|
let mut load_round_constant = |i: usize| {
|
||||||
|
region.assign_fixed(
|
||||||
|
|| format!("round_{} rc_{}", round + 1, i),
|
||||||
|
config.rc_b[i],
|
||||||
|
offset,
|
||||||
|
|| Ok(config.round_constants[round + 1][i]),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
for i in 0..WIDTH {
|
||||||
|
load_round_constant(i)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let r_mid = p_mid.map(|p| {
|
||||||
|
[
|
||||||
|
(p[0] + config.round_constants[round + 1][0]).pow(&config.alpha),
|
||||||
|
p[1] + config.round_constants[round + 1][1],
|
||||||
|
p[2] + config.round_constants[round + 1][2],
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
round + 2,
|
||||||
|
[
|
||||||
|
r_mid.map(|r| m[0][0] * r[0] + m[0][1] * r[1] + m[0][2] * r[2]),
|
||||||
|
r_mid.map(|r| m[1][0] * r[0] + m[1][1] * r[1] + m[1][2] * r[2]),
|
||||||
|
r_mid.map(|r| m[2][0] * r[0] + m[2][1] * r[1] + m[2][2] * r[2]),
|
||||||
|
],
|
||||||
|
))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load(
|
||||||
|
region: &mut Region<F>,
|
||||||
|
config: &Pow5T3Config<F>,
|
||||||
|
initial_state: &State<StateWord<F>, WIDTH>,
|
||||||
|
) -> Result<Self, Error> {
|
||||||
|
let mut load_state_word = |i: usize| {
|
||||||
|
let value = initial_state[i].value;
|
||||||
|
let var = region.assign_advice(
|
||||||
|
|| format!("load state_{}", i),
|
||||||
|
config.state[i],
|
||||||
|
0,
|
||||||
|
|| value.ok_or(Error::SynthesisError),
|
||||||
|
)?;
|
||||||
|
region.constrain_equal(&config.state_permutation, initial_state[i].var, var)?;
|
||||||
|
Ok(StateWord { var, value })
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Pow5T3State([
|
||||||
|
load_state_word(0)?,
|
||||||
|
load_state_word(1)?,
|
||||||
|
load_state_word(2)?,
|
||||||
|
]))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn round(
|
||||||
|
region: &mut Region<F>,
|
||||||
|
config: &Pow5T3Config<F>,
|
||||||
|
round: usize,
|
||||||
|
offset: usize,
|
||||||
|
round_gate: Selector,
|
||||||
|
round_fn: impl FnOnce(&mut Region<F>) -> Result<(usize, [Option<F>; WIDTH]), Error>,
|
||||||
|
) -> Result<Self, Error> {
|
||||||
|
// Enable the required gate.
|
||||||
|
round_gate.enable(region, offset)?;
|
||||||
|
|
||||||
|
// Load the round constants.
|
||||||
|
let mut load_round_constant = |i: usize| {
|
||||||
|
region.assign_fixed(
|
||||||
|
|| format!("round_{} rc_{}", round, i),
|
||||||
|
config.rc_a[i],
|
||||||
|
offset,
|
||||||
|
|| Ok(config.round_constants[round][i]),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
for i in 0..WIDTH {
|
||||||
|
load_round_constant(i)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute the next round's state.
|
||||||
|
let (next_round, next_state) = round_fn(region)?;
|
||||||
|
|
||||||
|
let mut next_state_word = |i: usize| {
|
||||||
|
let value = next_state[i];
|
||||||
|
let var = region.assign_advice(
|
||||||
|
|| format!("round_{} state_{}", next_round, i),
|
||||||
|
config.state[i],
|
||||||
|
offset + 1,
|
||||||
|
|| value.ok_or(Error::SynthesisError),
|
||||||
|
)?;
|
||||||
|
Ok(StateWord { var, value })
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Pow5T3State([
|
||||||
|
next_state_word(0)?,
|
||||||
|
next_state_word(1)?,
|
||||||
|
next_state_word(2)?,
|
||||||
|
]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use ff::PrimeField;
|
||||||
|
use halo2::{
|
||||||
|
arithmetic::FieldExt,
|
||||||
|
circuit::{layouter, Layouter},
|
||||||
|
dev::MockProver,
|
||||||
|
pasta::Fp,
|
||||||
|
plonk::{Assignment, Circuit, ConstraintSystem, Error},
|
||||||
|
};
|
||||||
|
use pasta_curves::pallas;
|
||||||
|
|
||||||
|
use super::{PoseidonInstructions, Pow5T3Chip, Pow5T3Config, StateWord, WIDTH};
|
||||||
|
use crate::{
|
||||||
|
circuit::gadget::poseidon::{Hash, Word},
|
||||||
|
primitives::poseidon::{self, ConstantLength, OrchardNullifier, Spec},
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PermuteCircuit {}
|
||||||
|
|
||||||
|
impl Circuit<Fp> for PermuteCircuit {
|
||||||
|
type Config = Pow5T3Config<Fp>;
|
||||||
|
|
||||||
|
fn configure(meta: &mut ConstraintSystem<Fp>) -> Pow5T3Config<Fp> {
|
||||||
|
let state = [
|
||||||
|
meta.advice_column(),
|
||||||
|
meta.advice_column(),
|
||||||
|
meta.advice_column(),
|
||||||
|
];
|
||||||
|
|
||||||
|
Pow5T3Chip::configure(meta, OrchardNullifier, state)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn synthesize(
|
||||||
|
&self,
|
||||||
|
cs: &mut impl Assignment<Fp>,
|
||||||
|
config: Pow5T3Config<Fp>,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
let mut layouter = layouter::SingleChipLayouter::new(cs)?;
|
||||||
|
|
||||||
|
let initial_state = layouter.assign_region(
|
||||||
|
|| "prepare initial state",
|
||||||
|
|mut region| {
|
||||||
|
let mut state_word = |i: usize| {
|
||||||
|
let value = Some(Fp::from(i as u64));
|
||||||
|
let var = region.assign_advice(
|
||||||
|
|| format!("load state_{}", i),
|
||||||
|
config.state[i],
|
||||||
|
0,
|
||||||
|
|| value.ok_or(Error::SynthesisError),
|
||||||
|
)?;
|
||||||
|
Ok(StateWord { var, value })
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok([state_word(0)?, state_word(1)?, state_word(2)?])
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let chip = Pow5T3Chip::construct(config.clone());
|
||||||
|
let final_state = <Pow5T3Chip<_> as PoseidonInstructions<
|
||||||
|
Fp,
|
||||||
|
OrchardNullifier,
|
||||||
|
WIDTH,
|
||||||
|
2,
|
||||||
|
>>::permute(&chip, &mut layouter, &initial_state)?;
|
||||||
|
|
||||||
|
// For the purpose of this test, compute the real final state inline.
|
||||||
|
let mut expected_final_state = [Fp::zero(), Fp::one(), Fp::from_u64(2)];
|
||||||
|
let (round_constants, mds, _) = OrchardNullifier.constants();
|
||||||
|
poseidon::permute::<_, OrchardNullifier, WIDTH, 2>(
|
||||||
|
&mut expected_final_state,
|
||||||
|
&mds,
|
||||||
|
&round_constants,
|
||||||
|
);
|
||||||
|
|
||||||
|
layouter.assign_region(
|
||||||
|
|| "constrain final state",
|
||||||
|
|mut region| {
|
||||||
|
let mut final_state_word = |i: usize| {
|
||||||
|
let var = region.assign_advice(
|
||||||
|
|| format!("load final_state_{}", i),
|
||||||
|
config.state[i],
|
||||||
|
0,
|
||||||
|
|| Ok(expected_final_state[i]),
|
||||||
|
)?;
|
||||||
|
region.constrain_equal(&config.state_permutation, final_state[i].var, var)
|
||||||
|
};
|
||||||
|
|
||||||
|
final_state_word(0)?;
|
||||||
|
final_state_word(1)?;
|
||||||
|
final_state_word(2)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn poseidon_permute() {
|
||||||
|
let k = 6;
|
||||||
|
let circuit = PermuteCircuit {};
|
||||||
|
let prover = MockProver::run(k, &circuit, vec![]).unwrap();
|
||||||
|
assert_eq!(prover.verify(), Ok(()))
|
||||||
|
}
|
||||||
|
|
||||||
|
struct HashCircuit {
|
||||||
|
message: Option<[Fp; 2]>,
|
||||||
|
// For the purpose of this test, witness the result.
|
||||||
|
// TODO: Move this into an instance column.
|
||||||
|
output: Option<Fp>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Circuit<Fp> for HashCircuit {
|
||||||
|
type Config = Pow5T3Config<Fp>;
|
||||||
|
|
||||||
|
fn configure(meta: &mut ConstraintSystem<Fp>) -> Pow5T3Config<Fp> {
|
||||||
|
let state = [
|
||||||
|
meta.advice_column(),
|
||||||
|
meta.advice_column(),
|
||||||
|
meta.advice_column(),
|
||||||
|
];
|
||||||
|
|
||||||
|
Pow5T3Chip::configure(meta, OrchardNullifier, state)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn synthesize(
|
||||||
|
&self,
|
||||||
|
cs: &mut impl Assignment<Fp>,
|
||||||
|
config: Pow5T3Config<Fp>,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
let mut layouter = layouter::SingleChipLayouter::<'_, Fp, _>::new(cs)?;
|
||||||
|
let chip = Pow5T3Chip::construct(config.clone());
|
||||||
|
|
||||||
|
let message = layouter.assign_region(
|
||||||
|
|| "load message",
|
||||||
|
|mut region| {
|
||||||
|
let mut message_word = |i: usize| {
|
||||||
|
let value = self.message.map(|message_vals| message_vals[i]);
|
||||||
|
let var = region.assign_advice(
|
||||||
|
|| format!("load message_{}", i),
|
||||||
|
config.state[i],
|
||||||
|
0,
|
||||||
|
|| value.ok_or(Error::SynthesisError),
|
||||||
|
)?;
|
||||||
|
Ok(Word::<_, _, OrchardNullifier, WIDTH, 2> {
|
||||||
|
inner: StateWord { var, value },
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok([message_word(0)?, message_word(1)?])
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let hasher = Hash::init(chip, layouter.namespace(|| "init"), ConstantLength::<2>)?;
|
||||||
|
let output = hasher.hash(layouter.namespace(|| "hash"), message)?;
|
||||||
|
|
||||||
|
layouter.assign_region(
|
||||||
|
|| "constrain output",
|
||||||
|
|mut region| {
|
||||||
|
let expected_var = region.assign_advice(
|
||||||
|
|| "load output",
|
||||||
|
config.state[0],
|
||||||
|
0,
|
||||||
|
|| self.output.ok_or(Error::SynthesisError),
|
||||||
|
)?;
|
||||||
|
let word: StateWord<_> = output.inner;
|
||||||
|
region.constrain_equal(&config.state_permutation, word.var, expected_var)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn poseidon_hash() {
|
||||||
|
let message = [Fp::rand(), Fp::rand()];
|
||||||
|
let output = poseidon::Hash::init(OrchardNullifier, ConstantLength::<2>).hash(message);
|
||||||
|
|
||||||
|
let k = 6;
|
||||||
|
let circuit = HashCircuit {
|
||||||
|
message: Some(message),
|
||||||
|
output: Some(output),
|
||||||
|
};
|
||||||
|
let prover = MockProver::run(k, &circuit, vec![]).unwrap();
|
||||||
|
assert_eq!(prover.verify(), Ok(()))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn hash_test_vectors() {
|
||||||
|
for tv in crate::primitives::poseidon::test_vectors::hash() {
|
||||||
|
let message = [
|
||||||
|
pallas::Base::from_repr(tv.input[0]).unwrap(),
|
||||||
|
pallas::Base::from_repr(tv.input[1]).unwrap(),
|
||||||
|
];
|
||||||
|
let output = poseidon::Hash::init(OrchardNullifier, ConstantLength).hash(message);
|
||||||
|
|
||||||
|
let k = 6;
|
||||||
|
let circuit = HashCircuit {
|
||||||
|
message: Some(message),
|
||||||
|
output: Some(output),
|
||||||
|
};
|
||||||
|
let prover = MockProver::run(k, &circuit, vec![]).unwrap();
|
||||||
|
assert_eq!(prover.verify(), Ok(()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "dev-graph")]
|
||||||
|
#[test]
|
||||||
|
fn print_poseidon_chip() {
|
||||||
|
use plotters::prelude::*;
|
||||||
|
|
||||||
|
let root = BitMapBackend::new("poseidon-chip-layout.png", (1024, 768)).into_drawing_area();
|
||||||
|
root.fill(&WHITE).unwrap();
|
||||||
|
let root = root
|
||||||
|
.titled("Poseidon Chip Layout", ("sans-serif", 60))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let circuit = HashCircuit {
|
||||||
|
message: None,
|
||||||
|
output: None,
|
||||||
|
};
|
||||||
|
halo2::dev::circuit_layout(&circuit, &root).unwrap();
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
use std::array;
|
use std::array;
|
||||||
|
use std::fmt;
|
||||||
use std::iter;
|
use std::iter;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
|
@ -7,6 +8,9 @@ use halo2::arithmetic::FieldExt;
|
||||||
pub(crate) mod grain;
|
pub(crate) mod grain;
|
||||||
pub(crate) mod mds;
|
pub(crate) mod mds;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
pub(crate) mod test_vectors;
|
||||||
|
|
||||||
mod nullifier;
|
mod nullifier;
|
||||||
pub use nullifier::OrchardNullifier;
|
pub use nullifier::OrchardNullifier;
|
||||||
|
|
||||||
|
@ -68,7 +72,7 @@ pub trait Spec<F: FieldExt, const T: usize, const RATE: usize> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Runs the Poseidon permutation on the given state.
|
/// Runs the Poseidon permutation on the given state.
|
||||||
fn permute<F: FieldExt, S: Spec<F, T, RATE>, const T: usize, const RATE: usize>(
|
pub(crate) fn permute<F: FieldExt, S: Spec<F, T, RATE>, const T: usize, const RATE: usize>(
|
||||||
state: &mut State<F, T>,
|
state: &mut State<F, T>,
|
||||||
mds: &Mds<F, T>,
|
mds: &Mds<F, T>,
|
||||||
round_constants: &[[F; T]],
|
round_constants: &[[F; T]],
|
||||||
|
@ -133,13 +137,13 @@ fn poseidon_duplex<F: FieldExt, S: Spec<F, T, RATE>, const T: usize, const RATE:
|
||||||
output
|
output
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Sponge<F: FieldExt, const RATE: usize> {
|
pub(crate) enum Sponge<F, const RATE: usize> {
|
||||||
Absorbing(SpongeState<F, RATE>),
|
Absorbing(SpongeState<F, RATE>),
|
||||||
Squeezing(SpongeState<F, RATE>),
|
Squeezing(SpongeState<F, RATE>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F: FieldExt, const RATE: usize> Sponge<F, RATE> {
|
impl<F: Copy, const RATE: usize> Sponge<F, RATE> {
|
||||||
fn absorb(val: F) -> Self {
|
pub(crate) fn absorb(val: F) -> Self {
|
||||||
let mut input = [None; RATE];
|
let mut input = [None; RATE];
|
||||||
input[0] = Some(val);
|
input[0] = Some(val);
|
||||||
Sponge::Absorbing(input)
|
Sponge::Absorbing(input)
|
||||||
|
@ -237,11 +241,13 @@ impl<F: FieldExt, S: Spec<F, T, RATE>, const T: usize, const RATE: usize> Duplex
|
||||||
|
|
||||||
/// A domain in which a Poseidon hash function is being used.
|
/// A domain in which a Poseidon hash function is being used.
|
||||||
pub trait Domain<F: FieldExt, S: Spec<F, T, RATE>, const T: usize, const RATE: usize>:
|
pub trait Domain<F: FieldExt, S: Spec<F, T, RATE>, const T: usize, const RATE: usize>:
|
||||||
Copy
|
Copy + fmt::Debug
|
||||||
{
|
{
|
||||||
/// The initial capacity element, encoding this domain.
|
/// The initial capacity element, encoding this domain.
|
||||||
fn initial_capacity_element(&self) -> F;
|
fn initial_capacity_element(&self) -> F;
|
||||||
|
|
||||||
|
fn padding(&self) -> SpongeState<F, RATE>;
|
||||||
|
|
||||||
/// Returns a function that will update the given state with the given input to a
|
/// Returns a function that will update the given state with the given input to a
|
||||||
/// duplex permutation round, applying padding according to this domain specification.
|
/// duplex permutation round, applying padding according to this domain specification.
|
||||||
fn pad_and_add(&self) -> Box<dyn Fn(&mut State<F, T>, &SpongeState<F, RATE>)>;
|
fn pad_and_add(&self) -> Box<dyn Fn(&mut State<F, T>, &SpongeState<F, RATE>)>;
|
||||||
|
@ -262,6 +268,16 @@ impl<F: FieldExt, S: Spec<F, T, RATE>, const T: usize, const RATE: usize, const
|
||||||
F::from_u128((L as u128) << 64)
|
F::from_u128((L as u128) << 64)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn padding(&self) -> SpongeState<F, RATE> {
|
||||||
|
// For constant-input-length hashing, padding consists of the field elements being
|
||||||
|
// zero.
|
||||||
|
let mut padding = [None; RATE];
|
||||||
|
for word in padding.iter_mut().skip(L) {
|
||||||
|
*word = Some(F::zero());
|
||||||
|
}
|
||||||
|
padding
|
||||||
|
}
|
||||||
|
|
||||||
fn pad_and_add(&self) -> Box<dyn Fn(&mut State<F, T>, &SpongeState<F, RATE>)> {
|
fn pad_and_add(&self) -> Box<dyn Fn(&mut State<F, T>, &SpongeState<F, RATE>)> {
|
||||||
Box::new(|state, input| {
|
Box::new(|state, input| {
|
||||||
// `Iterator::zip` short-circuits when one iterator completes, so this will only
|
// `Iterator::zip` short-circuits when one iterator completes, so this will only
|
||||||
|
|
|
@ -1507,12 +1507,13 @@ const MDS_INV: [[pallas::Base; 3]; 3] = [
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
|
use ff::PrimeField;
|
||||||
use halo2::arithmetic::FieldExt;
|
use halo2::arithmetic::FieldExt;
|
||||||
use pasta_curves::pallas;
|
use pasta_curves::pallas;
|
||||||
|
|
||||||
use crate::primitives::poseidon::Spec;
|
use crate::primitives::poseidon::{permute, ConstantLength, Hash, Spec};
|
||||||
|
|
||||||
use super::{MDS, MDS_INV, ROUND_CONSTANTS};
|
use super::{OrchardNullifier, MDS, MDS_INV, ROUND_CONSTANTS};
|
||||||
|
|
||||||
/// The same Poseidon specification as poseidon::OrchardNullifier, but constructed
|
/// The same Poseidon specification as poseidon::OrchardNullifier, but constructed
|
||||||
/// such that its constants will be generated at runtime.
|
/// such that its constants will be generated at runtime.
|
||||||
|
@ -1550,7 +1551,7 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_vectors() {
|
fn verify_constants() {
|
||||||
let poseidon = P128Pow5T3Plus::<pallas::Base>::new(0);
|
let poseidon = P128Pow5T3Plus::<pallas::Base>::new(0);
|
||||||
let (round_constants, mds, mds_inv) = poseidon.constants();
|
let (round_constants, mds, mds_inv) = poseidon.constants();
|
||||||
|
|
||||||
|
@ -1570,4 +1571,93 @@ mod tests {
|
||||||
assert_eq!(actual, expected);
|
assert_eq!(actual, expected);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_against_reference() {
|
||||||
|
// This is the test vector output by the reference code at
|
||||||
|
// <https://extgit.iaik.tugraz.at/krypto/hadeshash>, using parameters from
|
||||||
|
// `generate_parameters_grain.sage 1 0 255 3 8 58 0x40000000000000000000000000000000224698fc094cf91b992d30ed00000001`.
|
||||||
|
|
||||||
|
let mut input = [
|
||||||
|
pallas::Base::from_raw([
|
||||||
|
0x0000_0000_0000_0000,
|
||||||
|
0x0000_0000_0000_0000,
|
||||||
|
0x0000_0000_0000_0000,
|
||||||
|
0x0000_0000_0000_0000,
|
||||||
|
]),
|
||||||
|
pallas::Base::from_raw([
|
||||||
|
0x0000_0000_0000_0001,
|
||||||
|
0x0000_0000_0000_0000,
|
||||||
|
0x0000_0000_0000_0000,
|
||||||
|
0x0000_0000_0000_0000,
|
||||||
|
]),
|
||||||
|
pallas::Base::from_raw([
|
||||||
|
0x0000_0000_0000_0002,
|
||||||
|
0x0000_0000_0000_0000,
|
||||||
|
0x0000_0000_0000_0000,
|
||||||
|
0x0000_0000_0000_0000,
|
||||||
|
]),
|
||||||
|
];
|
||||||
|
|
||||||
|
let expected_output = [
|
||||||
|
pallas::Base::from_raw([
|
||||||
|
0x4586_0cdf_c122_4c90,
|
||||||
|
0x6ad2_1f3e_0511_2d6e,
|
||||||
|
0xe2d3_3be0_7ee5_db5c,
|
||||||
|
0x19a2_64db_f840_aaea,
|
||||||
|
]),
|
||||||
|
pallas::Base::from_raw([
|
||||||
|
0x3dc3_ed1c_3434_091e,
|
||||||
|
0x31cc_06bf_df6b_d5fd,
|
||||||
|
0x8136_86b6_df10_cf99,
|
||||||
|
0x11b8_23d6_6e94_c285,
|
||||||
|
]),
|
||||||
|
pallas::Base::from_raw([
|
||||||
|
0xc5dc_3d6d_756e_de28,
|
||||||
|
0xcbaa_5cae_abc5_96e3,
|
||||||
|
0x68a6_35c3_b4cb_b608,
|
||||||
|
0x1111_04f4_1966_d2ce,
|
||||||
|
]),
|
||||||
|
];
|
||||||
|
|
||||||
|
permute::<pallas::Base, P128Pow5T3Plus<pallas::Base>, 3, 2>(
|
||||||
|
&mut input,
|
||||||
|
&MDS,
|
||||||
|
&ROUND_CONSTANTS,
|
||||||
|
);
|
||||||
|
assert_eq!(input, expected_output);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn permute_test_vectors() {
|
||||||
|
let (round_constants, mds, _) = OrchardNullifier.constants();
|
||||||
|
|
||||||
|
for tv in crate::primitives::poseidon::test_vectors::permute() {
|
||||||
|
let mut state = [
|
||||||
|
pallas::Base::from_repr(tv.initial_state[0]).unwrap(),
|
||||||
|
pallas::Base::from_repr(tv.initial_state[1]).unwrap(),
|
||||||
|
pallas::Base::from_repr(tv.initial_state[2]).unwrap(),
|
||||||
|
];
|
||||||
|
|
||||||
|
permute::<pallas::Base, OrchardNullifier, 3, 2>(&mut state, &mds, &round_constants);
|
||||||
|
|
||||||
|
for (expected, actual) in tv.final_state.iter().zip(state.iter()) {
|
||||||
|
assert_eq!(&actual.to_repr(), expected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn hash_test_vectors() {
|
||||||
|
for tv in crate::primitives::poseidon::test_vectors::hash() {
|
||||||
|
let message = [
|
||||||
|
pallas::Base::from_repr(tv.input[0]).unwrap(),
|
||||||
|
pallas::Base::from_repr(tv.input[1]).unwrap(),
|
||||||
|
];
|
||||||
|
|
||||||
|
let result = Hash::init(OrchardNullifier, ConstantLength).hash(message);
|
||||||
|
|
||||||
|
assert_eq!(result.to_repr(), tv.output);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,632 @@
|
||||||
|
//! Test vectors for [`OrchardNullifier`].
|
||||||
|
|
||||||
|
pub(crate) struct PermuteTestVector {
|
||||||
|
pub(crate) initial_state: [[u8; 32]; 3],
|
||||||
|
pub(crate) final_state: [[u8; 32]; 3],
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) struct HashTestVector {
|
||||||
|
pub(crate) input: [[u8; 32]; 2],
|
||||||
|
pub(crate) output: [u8; 32],
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn permute() -> Vec<PermuteTestVector> {
|
||||||
|
use PermuteTestVector as TestVector;
|
||||||
|
|
||||||
|
// From https://github.com/zcash-hackworks/zcash-test-vectors/blob/master/orchard_poseidon.py
|
||||||
|
vec![
|
||||||
|
TestVector {
|
||||||
|
initial_state: [
|
||||||
|
[
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
final_state: [
|
||||||
|
[
|
||||||
|
0x90, 0x4c, 0x22, 0xc1, 0xdf, 0x0c, 0x86, 0x45, 0x6e, 0x2d, 0x11, 0x05, 0x3e,
|
||||||
|
0x1f, 0xd2, 0x6a, 0x5c, 0xdb, 0xe5, 0x7e, 0xe0, 0x3b, 0xd3, 0xe2, 0xea, 0xaa,
|
||||||
|
0x40, 0xf8, 0xdb, 0x64, 0xa2, 0x19,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0x1e, 0x09, 0x34, 0x34, 0x1c, 0xed, 0xc3, 0x3d, 0xfd, 0xd5, 0x6b, 0xdf, 0xbf,
|
||||||
|
0x06, 0xcc, 0x31, 0x99, 0xcf, 0x10, 0xdf, 0xb6, 0x86, 0x36, 0x81, 0x85, 0xc2,
|
||||||
|
0x94, 0x6e, 0xd6, 0x23, 0xb8, 0x11,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0x28, 0xde, 0x6e, 0x75, 0x6d, 0x3d, 0xdc, 0xc5, 0xe3, 0x96, 0xc5, 0xab, 0xae,
|
||||||
|
0x5c, 0xaa, 0xcb, 0x08, 0xb6, 0xcb, 0xb4, 0xc3, 0x35, 0xa6, 0x68, 0xce, 0xd2,
|
||||||
|
0x66, 0x19, 0xf4, 0x04, 0x11, 0x11,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
},
|
||||||
|
TestVector {
|
||||||
|
initial_state: [
|
||||||
|
[
|
||||||
|
0x5c, 0x7a, 0x8f, 0x73, 0xad, 0xfc, 0x70, 0xfb, 0x3f, 0x13, 0x94, 0x49, 0xac,
|
||||||
|
0x6b, 0x57, 0x07, 0x4c, 0x4d, 0x6e, 0x66, 0xb1, 0x64, 0x93, 0x9d, 0xaf, 0xfa,
|
||||||
|
0x2e, 0xf6, 0xee, 0x69, 0x21, 0x08,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0x1a, 0xdd, 0x86, 0xb3, 0xf2, 0xe1, 0xbd, 0xa6, 0x2a, 0x5d, 0x2e, 0x0e, 0x98,
|
||||||
|
0x2b, 0x77, 0xe6, 0xb0, 0xef, 0x9c, 0xa3, 0xf2, 0x49, 0x88, 0xc7, 0xb3, 0x53,
|
||||||
|
0x42, 0x01, 0xcf, 0xb1, 0xcd, 0x0d,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0xbd, 0x69, 0xb8, 0x25, 0x32, 0xb6, 0x94, 0x0f, 0xf2, 0x59, 0x0f, 0x67, 0x9b,
|
||||||
|
0xa9, 0xc7, 0x27, 0x1f, 0xe0, 0x1f, 0x7e, 0x9c, 0x8e, 0x36, 0xd6, 0xa5, 0xe2,
|
||||||
|
0x9d, 0x4e, 0x30, 0xa7, 0x35, 0x14,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
final_state: [
|
||||||
|
[
|
||||||
|
0xc5, 0x77, 0x86, 0x7d, 0xe7, 0x3a, 0xac, 0x8f, 0xb6, 0x70, 0x24, 0x17, 0x15,
|
||||||
|
0x02, 0xb9, 0x05, 0xed, 0xb3, 0x28, 0x5e, 0xd8, 0x2a, 0x83, 0xfd, 0x2d, 0x42,
|
||||||
|
0x80, 0x78, 0x58, 0x20, 0xf8, 0x2b,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0x15, 0xd5, 0xce, 0xac, 0x4b, 0x8a, 0x89, 0xf5, 0x50, 0xaf, 0x64, 0x6b, 0x9d,
|
||||||
|
0x94, 0x01, 0x5d, 0xfe, 0x2d, 0xf2, 0x5e, 0x53, 0x1f, 0xc8, 0x64, 0x5a, 0x77,
|
||||||
|
0x15, 0x25, 0xff, 0x8e, 0x79, 0x18,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0x79, 0xcc, 0x5e, 0xf9, 0xec, 0xe6, 0x3d, 0x36, 0x85, 0xe0, 0x2e, 0xa3, 0xb0,
|
||||||
|
0xb6, 0x91, 0x1b, 0xf3, 0xf1, 0x08, 0x9a, 0xf6, 0xc4, 0x5e, 0x1d, 0xbb, 0xb3,
|
||||||
|
0x69, 0x24, 0x64, 0x49, 0x74, 0x1d,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
},
|
||||||
|
TestVector {
|
||||||
|
initial_state: [
|
||||||
|
[
|
||||||
|
0xbc, 0x50, 0x98, 0x42, 0x55, 0xd6, 0xaf, 0xbe, 0x9e, 0xf9, 0x28, 0x48, 0xed,
|
||||||
|
0x5a, 0xc0, 0x08, 0x62, 0xc2, 0xfa, 0x7b, 0x2f, 0xec, 0xbc, 0xb6, 0x4b, 0x69,
|
||||||
|
0x68, 0x91, 0x2a, 0x63, 0x81, 0x0e,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0x3d, 0xc1, 0x66, 0xd5, 0x6a, 0x1d, 0x62, 0xf5, 0xa8, 0xd7, 0x55, 0x1d, 0xb5,
|
||||||
|
0xfd, 0x93, 0x13, 0xe8, 0xc7, 0x20, 0x3d, 0x99, 0x6a, 0xf7, 0xd4, 0x77, 0x08,
|
||||||
|
0x37, 0x56, 0xd5, 0x9a, 0xf8, 0x0d,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0x05, 0xa7, 0x45, 0xf4, 0x5d, 0x7f, 0xf6, 0xdb, 0x10, 0xbc, 0x67, 0xfd, 0xf0,
|
||||||
|
0xf0, 0x3e, 0xbf, 0x81, 0x30, 0xab, 0x33, 0x36, 0x26, 0x97, 0xb0, 0xe4, 0xe4,
|
||||||
|
0xc7, 0x63, 0xcc, 0xb8, 0xf6, 0x36,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
final_state: [
|
||||||
|
[
|
||||||
|
0xf3, 0x8d, 0xca, 0xec, 0x4a, 0x8f, 0x4e, 0xa2, 0x4d, 0x77, 0x8f, 0x00, 0x02,
|
||||||
|
0x36, 0xe4, 0xd3, 0x5e, 0x3c, 0xa6, 0x2e, 0x6a, 0x1b, 0xd8, 0x8b, 0x09, 0x00,
|
||||||
|
0xfb, 0xaa, 0x1f, 0xc1, 0x79, 0x3e,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0x3a, 0xe7, 0x5e, 0x3d, 0x47, 0xda, 0x8c, 0xd9, 0x9e, 0x11, 0x5e, 0x6a, 0xee,
|
||||||
|
0x89, 0x08, 0x0c, 0x19, 0x24, 0xbc, 0x47, 0xac, 0xe6, 0x1d, 0x66, 0x54, 0xf9,
|
||||||
|
0x90, 0x07, 0x4d, 0x95, 0x57, 0x0b,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0xdd, 0x21, 0xcf, 0x43, 0x97, 0xb5, 0xec, 0xe2, 0x0c, 0x7a, 0x27, 0x42, 0x1e,
|
||||||
|
0xf8, 0x18, 0x5b, 0x3d, 0xb7, 0x19, 0x1d, 0xac, 0x0e, 0xed, 0x45, 0x37, 0xe3,
|
||||||
|
0x79, 0xf1, 0x3d, 0x71, 0x78, 0x06,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
},
|
||||||
|
TestVector {
|
||||||
|
initial_state: [
|
||||||
|
[
|
||||||
|
0x49, 0x5c, 0x22, 0x2f, 0x7f, 0xba, 0x1e, 0x31, 0xde, 0xfa, 0x3d, 0x5a, 0x57,
|
||||||
|
0xef, 0xc2, 0xe1, 0xe9, 0xb0, 0x1a, 0x03, 0x55, 0x87, 0xd5, 0xfb, 0x1a, 0x38,
|
||||||
|
0xe0, 0x1d, 0x94, 0x90, 0x3d, 0x3c,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0x3d, 0x0a, 0xd3, 0x36, 0x1f, 0xec, 0x09, 0x77, 0x90, 0xd9, 0xbe, 0x0e, 0x42,
|
||||||
|
0x98, 0x8d, 0x7d, 0x25, 0xc9, 0xa1, 0x38, 0xf4, 0x9b, 0x1a, 0x53, 0x7e, 0xdc,
|
||||||
|
0xf0, 0x4b, 0xe3, 0x4a, 0x98, 0x11,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0xa4, 0xaf, 0x9d, 0xb6, 0xd2, 0x7b, 0x50, 0x72, 0x83, 0x5f, 0x0c, 0x3e, 0x88,
|
||||||
|
0x39, 0x5e, 0xd7, 0xa4, 0x1b, 0x00, 0x52, 0xad, 0x80, 0x84, 0xa8, 0xb9, 0xda,
|
||||||
|
0x94, 0x8d, 0x32, 0x0d, 0xad, 0x16,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
final_state: [
|
||||||
|
[
|
||||||
|
0xdc, 0xd8, 0xa7, 0x4b, 0x5e, 0xd4, 0xfc, 0xe5, 0x5d, 0x55, 0x3a, 0x44, 0xcb,
|
||||||
|
0x7f, 0x3e, 0x49, 0xf4, 0x58, 0xd7, 0x5c, 0xd2, 0x73, 0x54, 0xaf, 0x50, 0x87,
|
||||||
|
0xad, 0xfd, 0x62, 0xba, 0x1f, 0x39,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0x32, 0xe8, 0xb9, 0xf1, 0xe8, 0x1b, 0x94, 0xc0, 0xc5, 0x5b, 0x0a, 0x65, 0xb4,
|
||||||
|
0x3b, 0xae, 0x5e, 0xae, 0xa8, 0x5b, 0x92, 0x6c, 0xda, 0x2d, 0x7c, 0x9b, 0x12,
|
||||||
|
0xc3, 0xb3, 0x4a, 0x44, 0x02, 0x17,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0xea, 0xec, 0x69, 0x76, 0x9a, 0x01, 0xd0, 0xdb, 0x4e, 0xdc, 0x1e, 0x02, 0xb2,
|
||||||
|
0x63, 0x14, 0x06, 0xba, 0xf7, 0x27, 0x34, 0x1f, 0xf1, 0xd4, 0xae, 0xe0, 0xdc,
|
||||||
|
0xd9, 0x6e, 0x48, 0xb6, 0x0c, 0x13,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
},
|
||||||
|
TestVector {
|
||||||
|
initial_state: [
|
||||||
|
[
|
||||||
|
0x4d, 0x54, 0x31, 0xe6, 0x43, 0x7d, 0x0b, 0x5b, 0xed, 0xbb, 0xcd, 0xaf, 0x34,
|
||||||
|
0x5b, 0x86, 0xc4, 0x12, 0x1f, 0xc0, 0x0f, 0xe7, 0xf2, 0x35, 0x73, 0x42, 0x76,
|
||||||
|
0xd3, 0x8d, 0x47, 0xf1, 0xe1, 0x11,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0xdd, 0x0c, 0x7a, 0x1d, 0x81, 0x1c, 0x7d, 0x9c, 0xd4, 0x6d, 0x37, 0x7b, 0x3f,
|
||||||
|
0xde, 0xab, 0x3f, 0xb6, 0x79, 0xf3, 0xdc, 0x60, 0x1d, 0x00, 0x82, 0x85, 0xed,
|
||||||
|
0xcb, 0xda, 0xe6, 0x9c, 0xe8, 0x3c,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0x19, 0xe4, 0xaa, 0xc0, 0x35, 0x90, 0x17, 0xec, 0x85, 0xa1, 0x83, 0xd2, 0x20,
|
||||||
|
0x53, 0xdb, 0x33, 0xf7, 0x34, 0x76, 0xf2, 0x1a, 0x48, 0x2e, 0xc9, 0x37, 0x83,
|
||||||
|
0x65, 0xc8, 0xf7, 0x39, 0x3c, 0x14,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
final_state: [
|
||||||
|
[
|
||||||
|
0x55, 0x28, 0xb7, 0x18, 0xe3, 0x7d, 0x53, 0xad, 0x3c, 0x5e, 0x39, 0x8d, 0xa8,
|
||||||
|
0xe7, 0xf1, 0x76, 0x3c, 0x0e, 0x9b, 0xf5, 0xe6, 0x15, 0xb3, 0x9a, 0x42, 0x25,
|
||||||
|
0x74, 0x1d, 0x5f, 0xc2, 0x2c, 0x14,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0xa5, 0x87, 0xa7, 0xa9, 0x85, 0x48, 0xc5, 0xe6, 0xc4, 0x9c, 0xdd, 0x04, 0xdf,
|
||||||
|
0x77, 0x82, 0x6a, 0x5e, 0xf4, 0xe6, 0x24, 0xb8, 0x59, 0x5e, 0x79, 0x0e, 0x0d,
|
||||||
|
0xba, 0xb1, 0x6f, 0x59, 0xd2, 0x26,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0x53, 0xe4, 0x47, 0xbd, 0x29, 0x95, 0x72, 0x2c, 0xf5, 0x1a, 0x6c, 0x44, 0x71,
|
||||||
|
0xeb, 0xb8, 0x3f, 0x0d, 0x11, 0xd5, 0xd0, 0x1f, 0x4d, 0x84, 0x88, 0xc5, 0x78,
|
||||||
|
0x9f, 0xe9, 0x03, 0x37, 0x5e, 0x23,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
},
|
||||||
|
TestVector {
|
||||||
|
initial_state: [
|
||||||
|
[
|
||||||
|
0xe2, 0x88, 0x53, 0x15, 0xeb, 0x46, 0x71, 0x09, 0x8b, 0x79, 0x53, 0x5e, 0x79,
|
||||||
|
0x0f, 0xe5, 0x3e, 0x29, 0xfe, 0xf2, 0xb3, 0x76, 0x66, 0x97, 0xac, 0x32, 0xb4,
|
||||||
|
0xf4, 0x73, 0xf4, 0x68, 0xa0, 0x08,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0xe6, 0x23, 0x89, 0xfc, 0x16, 0x57, 0xe0, 0xde, 0xf0, 0xb6, 0x32, 0xc6, 0xae,
|
||||||
|
0x25, 0xf9, 0xf7, 0x83, 0xb2, 0x7d, 0xb5, 0x9a, 0x4a, 0x15, 0x3d, 0x88, 0x2d,
|
||||||
|
0x2b, 0x21, 0x03, 0x59, 0x65, 0x15,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0xeb, 0x94, 0x94, 0xc6, 0xd2, 0x27, 0xe2, 0x16, 0x3b, 0x46, 0x99, 0xd9, 0x91,
|
||||||
|
0xf4, 0x33, 0xbf, 0x94, 0x86, 0xa7, 0xaf, 0xcf, 0x4a, 0x0d, 0x9c, 0x73, 0x1e,
|
||||||
|
0x98, 0x5d, 0x99, 0x58, 0x9c, 0x0b,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
final_state: [
|
||||||
|
[
|
||||||
|
0x4a, 0x09, 0x0a, 0xa2, 0x98, 0xa8, 0x0c, 0xc3, 0x9c, 0x13, 0x5d, 0x51, 0xd1,
|
||||||
|
0x63, 0x7c, 0xa4, 0x4e, 0xa4, 0xb2, 0x92, 0xc7, 0xa1, 0xd4, 0xef, 0xd3, 0xc0,
|
||||||
|
0x5d, 0x0b, 0xfa, 0x3e, 0x0c, 0x09,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0xf6, 0x6b, 0x28, 0x79, 0x7c, 0xe3, 0x56, 0x83, 0x0f, 0xda, 0xbf, 0xe0, 0x79,
|
||||||
|
0x83, 0x9e, 0x9c, 0xb2, 0xe4, 0xd5, 0x66, 0x6b, 0xa6, 0x3b, 0x15, 0x94, 0x5e,
|
||||||
|
0x95, 0x85, 0x85, 0x52, 0xfd, 0x38,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0xde, 0xe0, 0x09, 0x64, 0x33, 0x8c, 0x59, 0xe6, 0x92, 0x98, 0x53, 0x66, 0xc9,
|
||||||
|
0x69, 0xa6, 0xba, 0x83, 0x1a, 0x62, 0x9f, 0xbb, 0xd6, 0xec, 0xee, 0xf1, 0x04,
|
||||||
|
0x9d, 0x78, 0xc6, 0x2f, 0x0c, 0x13,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
},
|
||||||
|
TestVector {
|
||||||
|
initial_state: [
|
||||||
|
[
|
||||||
|
0xb7, 0x38, 0xe8, 0xaa, 0x0a, 0x15, 0x26, 0xa5, 0xbd, 0xef, 0x61, 0x31, 0x20,
|
||||||
|
0x37, 0x2e, 0x83, 0x1a, 0x20, 0xda, 0x8a, 0xba, 0x18, 0xd1, 0xdb, 0xeb, 0xbc,
|
||||||
|
0x86, 0x2d, 0xed, 0x42, 0x43, 0x1e,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0x91, 0x47, 0x69, 0x30, 0xe3, 0x38, 0x5c, 0xd3, 0xe3, 0x37, 0x9e, 0x38, 0x53,
|
||||||
|
0xd9, 0x34, 0x67, 0xe0, 0x01, 0xaf, 0xa2, 0xfb, 0x8d, 0xc3, 0x43, 0x6d, 0x75,
|
||||||
|
0xa4, 0xa6, 0xf2, 0x65, 0x72, 0x10,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0x4b, 0x19, 0x22, 0x32, 0xec, 0xb9, 0xf0, 0xc0, 0x24, 0x11, 0xe5, 0x25, 0x96,
|
||||||
|
0xbc, 0x5e, 0x90, 0x45, 0x7e, 0x74, 0x59, 0x39, 0xff, 0xed, 0xbd, 0x12, 0x86,
|
||||||
|
0x3c, 0xe7, 0x1a, 0x02, 0xaf, 0x11,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
final_state: [
|
||||||
|
[
|
||||||
|
0xab, 0x9d, 0x4b, 0x1a, 0x43, 0xfc, 0xbe, 0x0f, 0x8d, 0xc4, 0xc6, 0x2d, 0x5f,
|
||||||
|
0xb1, 0x34, 0xa9, 0x3b, 0x71, 0xa4, 0x98, 0xe5, 0x76, 0xdd, 0x10, 0xe5, 0x23,
|
||||||
|
0x0a, 0x9c, 0xc5, 0xe3, 0x35, 0x07,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0x35, 0xf0, 0x6f, 0x31, 0x1e, 0x1f, 0x7b, 0x05, 0x89, 0x0b, 0xc3, 0xe4, 0xcf,
|
||||||
|
0xc9, 0xff, 0xbe, 0xd0, 0xce, 0x36, 0xf7, 0xb8, 0xa0, 0x56, 0xfd, 0x1b, 0x7a,
|
||||||
|
0xd1, 0x79, 0xff, 0x89, 0x30, 0x2a,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0xfe, 0x25, 0x79, 0xc2, 0x88, 0x75, 0xae, 0x02, 0x25, 0x0f, 0x12, 0x43, 0x13,
|
||||||
|
0xd0, 0xc9, 0xcf, 0x92, 0xbe, 0x3c, 0x5f, 0x81, 0x5c, 0xe9, 0x95, 0xf0, 0x66,
|
||||||
|
0xfe, 0x02, 0xe5, 0x1f, 0x43, 0x02,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
},
|
||||||
|
TestVector {
|
||||||
|
initial_state: [
|
||||||
|
[
|
||||||
|
0x7b, 0x41, 0x7a, 0xdb, 0x63, 0xb3, 0x71, 0x22, 0xa5, 0xbf, 0x62, 0xd2, 0x6f,
|
||||||
|
0x1e, 0x7f, 0x26, 0x8f, 0xb8, 0x6b, 0x12, 0xb5, 0x6d, 0xa9, 0xc3, 0x82, 0x85,
|
||||||
|
0x7d, 0xee, 0xcc, 0x40, 0xa9, 0x0d,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0x5e, 0x29, 0x35, 0x39, 0x71, 0xb3, 0x49, 0x94, 0xb6, 0x21, 0xb0, 0xb2, 0x61,
|
||||||
|
0xae, 0xb3, 0x78, 0x6d, 0xd9, 0x84, 0xd5, 0x67, 0xdb, 0x28, 0x57, 0xb9, 0x27,
|
||||||
|
0xb7, 0xfa, 0xe2, 0xdb, 0x58, 0x31,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0x05, 0x41, 0x5d, 0x46, 0x42, 0x78, 0x9d, 0x38, 0xf5, 0x0b, 0x8d, 0xbc, 0xc1,
|
||||||
|
0x29, 0xca, 0xb3, 0xd1, 0x7d, 0x19, 0xf3, 0x35, 0x5b, 0xcf, 0x73, 0xce, 0xcb,
|
||||||
|
0x8c, 0xb8, 0xa5, 0xda, 0x01, 0x30,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
final_state: [
|
||||||
|
[
|
||||||
|
0xbb, 0x73, 0x92, 0x3b, 0x95, 0x2e, 0x75, 0x56, 0x17, 0x3c, 0xb0, 0xdb, 0x98,
|
||||||
|
0x8f, 0x61, 0x74, 0xa7, 0x2c, 0x71, 0x89, 0xec, 0xd3, 0x61, 0x71, 0xbe, 0x6b,
|
||||||
|
0xdb, 0xaf, 0x4f, 0x82, 0xea, 0x05,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0x22, 0x49, 0xea, 0x38, 0xd9, 0x86, 0x9e, 0xd2, 0xf3, 0xc8, 0x94, 0xc7, 0x02,
|
||||||
|
0x4d, 0xef, 0x6f, 0x5b, 0x75, 0x88, 0x4e, 0x13, 0x0a, 0xbe, 0x3a, 0x5c, 0x34,
|
||||||
|
0xf0, 0x6b, 0x34, 0x4d, 0x95, 0x13,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0xbc, 0xd4, 0x0e, 0xed, 0x83, 0x3c, 0x69, 0x38, 0x69, 0xd7, 0x02, 0x58, 0x67,
|
||||||
|
0x91, 0x4a, 0x1b, 0x26, 0xf8, 0x83, 0x23, 0xac, 0xfa, 0x78, 0x91, 0xb9, 0xa7,
|
||||||
|
0xbb, 0x65, 0x4f, 0xd8, 0x21, 0x07,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
},
|
||||||
|
TestVector {
|
||||||
|
initial_state: [
|
||||||
|
[
|
||||||
|
0x71, 0x52, 0xf1, 0x39, 0x36, 0xa2, 0x70, 0x57, 0x26, 0x70, 0xdc, 0x82, 0xd3,
|
||||||
|
0x90, 0x26, 0xc6, 0xcb, 0x4c, 0xd4, 0xb0, 0xf7, 0xf5, 0xaa, 0x2a, 0x4f, 0x5a,
|
||||||
|
0x53, 0x41, 0xec, 0x5d, 0xd7, 0x15,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0x40, 0x6f, 0x2f, 0xdd, 0x2a, 0xfa, 0x73, 0x3f, 0x5f, 0x64, 0x1c, 0x8c, 0x21,
|
||||||
|
0x86, 0x2a, 0x1b, 0xaf, 0xce, 0x26, 0x09, 0xd9, 0xee, 0xcf, 0xa1, 0x58, 0xcf,
|
||||||
|
0xb5, 0xcd, 0x79, 0xf8, 0x80, 0x08,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0xe2, 0x15, 0xdc, 0x7d, 0x96, 0x57, 0xba, 0xd3, 0xfb, 0x88, 0xb0, 0x1e, 0x99,
|
||||||
|
0x38, 0x44, 0x54, 0x36, 0x24, 0xc2, 0x5f, 0xa9, 0x59, 0xcc, 0x97, 0x48, 0x9c,
|
||||||
|
0xe7, 0x57, 0x45, 0x82, 0x4b, 0x37,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
final_state: [
|
||||||
|
[
|
||||||
|
0xf9, 0xc6, 0x42, 0x74, 0xbb, 0xb8, 0xf2, 0x01, 0x2d, 0xd0, 0xa5, 0xe9, 0x94,
|
||||||
|
0xd2, 0x35, 0x9f, 0xbe, 0x09, 0x28, 0xe6, 0xc8, 0x22, 0xb3, 0xf5, 0x1a, 0x22,
|
||||||
|
0xe0, 0x0e, 0x56, 0xc7, 0xe8, 0x0c,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0xb7, 0xf7, 0x5e, 0xd0, 0x4b, 0xd8, 0xb0, 0x88, 0xc5, 0xdc, 0x10, 0xfa, 0x7f,
|
||||||
|
0x05, 0x6d, 0x41, 0x3b, 0x25, 0xd4, 0x97, 0x06, 0xfb, 0xbf, 0xd1, 0x69, 0x27,
|
||||||
|
0x35, 0xac, 0xa7, 0x14, 0x67, 0x28,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0x6d, 0x64, 0x81, 0xdf, 0x9d, 0xf5, 0x68, 0x72, 0xc2, 0xdb, 0xff, 0x63, 0x1a,
|
||||||
|
0x63, 0xa0, 0x29, 0x77, 0x6d, 0xbc, 0x52, 0x71, 0x2b, 0xad, 0xbb, 0x1f, 0x36,
|
||||||
|
0xc8, 0xf7, 0xde, 0x00, 0x8b, 0x12,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
},
|
||||||
|
TestVector {
|
||||||
|
initial_state: [
|
||||||
|
[
|
||||||
|
0x86, 0x8c, 0x53, 0x23, 0x9c, 0xfb, 0xdf, 0x73, 0xca, 0xec, 0x65, 0x60, 0x40,
|
||||||
|
0x37, 0x31, 0x4f, 0xaa, 0xce, 0xb5, 0x62, 0x18, 0xc6, 0xbd, 0x30, 0xf8, 0x37,
|
||||||
|
0x4a, 0xc1, 0x33, 0x86, 0x79, 0x3f,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0x21, 0xa9, 0xfb, 0x80, 0xad, 0x03, 0xbc, 0x0c, 0xda, 0x4a, 0x44, 0x94, 0x6c,
|
||||||
|
0x00, 0xe1, 0xb1, 0xa1, 0xdf, 0x0e, 0x5b, 0x87, 0xb5, 0xbe, 0xce, 0x47, 0x7a,
|
||||||
|
0x70, 0x96, 0x49, 0xe9, 0x50, 0x06,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0x04, 0x91, 0x39, 0x48, 0x25, 0x64, 0xf1, 0x85, 0xc7, 0x90, 0x0e, 0x83, 0xc7,
|
||||||
|
0x38, 0x07, 0x0a, 0xf6, 0x55, 0x6d, 0xf6, 0xed, 0x4b, 0x4d, 0xdd, 0x3d, 0x9a,
|
||||||
|
0x69, 0xf5, 0x33, 0x57, 0xd7, 0x36,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
final_state: [
|
||||||
|
[
|
||||||
|
0x05, 0x59, 0x21, 0x7c, 0x04, 0x8d, 0x25, 0x49, 0x7f, 0x45, 0x52, 0x61, 0x47,
|
||||||
|
0x91, 0xc3, 0x20, 0xfd, 0x9e, 0xe7, 0x4f, 0x0e, 0x72, 0x8b, 0xa3, 0x48, 0xbd,
|
||||||
|
0x0f, 0x03, 0xe7, 0x9d, 0xb3, 0x37,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0xb0, 0xaf, 0x82, 0x16, 0x25, 0x32, 0x77, 0x4a, 0x45, 0xed, 0x0e, 0xd9, 0x9b,
|
||||||
|
0xf7, 0xaa, 0x2f, 0x98, 0xec, 0xc0, 0x2f, 0x93, 0xa0, 0xbb, 0x97, 0xe5, 0x0b,
|
||||||
|
0x41, 0x80, 0x59, 0xf0, 0xc2, 0x21,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0x66, 0xf6, 0x53, 0x50, 0x6d, 0xbc, 0x5c, 0xc2, 0x99, 0x25, 0x4b, 0xf2, 0x4c,
|
||||||
|
0x1b, 0x56, 0xfc, 0x24, 0x72, 0x7c, 0xce, 0x45, 0xdf, 0xc0, 0x85, 0xe5, 0xd6,
|
||||||
|
0xe2, 0x04, 0xfd, 0x86, 0x8c, 0x08,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
},
|
||||||
|
TestVector {
|
||||||
|
initial_state: [
|
||||||
|
[
|
||||||
|
0x7d, 0x4f, 0x5c, 0xcb, 0x01, 0x64, 0x3c, 0x31, 0xdb, 0x84, 0x5e, 0xec, 0xd5,
|
||||||
|
0xd6, 0x3d, 0xc1, 0x6a, 0x95, 0xe3, 0x02, 0x5b, 0x97, 0x92, 0xff, 0xf7, 0xf2,
|
||||||
|
0x44, 0xfc, 0x71, 0x62, 0x69, 0x39,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0x26, 0xd6, 0x2e, 0x95, 0x96, 0xfa, 0x82, 0x5c, 0x6b, 0xf2, 0x1a, 0xff, 0x9e,
|
||||||
|
0x68, 0x62, 0x5a, 0x19, 0x24, 0x40, 0xea, 0x06, 0x82, 0x81, 0x23, 0xd9, 0x78,
|
||||||
|
0x84, 0x80, 0x6f, 0x15, 0xfa, 0x08,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0xd9, 0x52, 0x75, 0x4a, 0x23, 0x64, 0xb6, 0x66, 0xff, 0xc3, 0x0f, 0xdb, 0x01,
|
||||||
|
0x47, 0x86, 0xda, 0x3a, 0x61, 0x28, 0xae, 0xf7, 0x84, 0xa6, 0x46, 0x10, 0xa8,
|
||||||
|
0x9d, 0x1a, 0x70, 0x99, 0x21, 0x2d,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
final_state: [
|
||||||
|
[
|
||||||
|
0xe9, 0x14, 0x81, 0x20, 0x7d, 0x99, 0xad, 0x96, 0x5b, 0x13, 0xf6, 0xb8, 0xf0,
|
||||||
|
0xa4, 0x5a, 0xa3, 0x3c, 0x2b, 0x8e, 0x5f, 0xe4, 0x21, 0x92, 0x97, 0xf0, 0x49,
|
||||||
|
0x9c, 0x16, 0x7c, 0x55, 0xde, 0x3b,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0x6f, 0x72, 0xcd, 0xe9, 0x21, 0x4b, 0x09, 0xe3, 0x59, 0xc3, 0x71, 0x28, 0x1a,
|
||||||
|
0x45, 0xa5, 0x2d, 0xfb, 0xd4, 0x14, 0x93, 0x35, 0x99, 0x63, 0x4e, 0x86, 0x66,
|
||||||
|
0x8d, 0x11, 0xf2, 0x85, 0x96, 0x0c,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0x74, 0x22, 0x6e, 0xda, 0x06, 0xc1, 0xee, 0xef, 0x3f, 0xa3, 0x39, 0x4e, 0x03,
|
||||||
|
0x7b, 0x48, 0xc0, 0x7d, 0xc2, 0x86, 0x95, 0x88, 0x8c, 0xfb, 0x59, 0x58, 0x8c,
|
||||||
|
0x34, 0xe7, 0x12, 0xb2, 0x2b, 0x1f,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn hash() -> Vec<HashTestVector> {
|
||||||
|
use HashTestVector as TestVector;
|
||||||
|
|
||||||
|
// From https://github.com/zcash-hackworks/zcash-test-vectors/blob/master/orchard_poseidon_hash.py
|
||||||
|
vec![
|
||||||
|
TestVector {
|
||||||
|
input: [
|
||||||
|
[
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
output: [
|
||||||
|
0xc0, 0x17, 0xb5, 0x55, 0xe8, 0x05, 0x7c, 0x49, 0x1d, 0x44, 0x4f, 0xbd, 0xb0, 0x0a,
|
||||||
|
0x83, 0xf6, 0x4d, 0xa8, 0x90, 0x53, 0x87, 0xb4, 0xc8, 0x13, 0xc0, 0xea, 0xdc, 0x86,
|
||||||
|
0x33, 0x58, 0x8c, 0x14,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
TestVector {
|
||||||
|
input: [
|
||||||
|
[
|
||||||
|
0x5c, 0x7a, 0x8f, 0x73, 0xad, 0xfc, 0x70, 0xfb, 0x3f, 0x13, 0x94, 0x49, 0xac,
|
||||||
|
0x6b, 0x57, 0x07, 0x4c, 0x4d, 0x6e, 0x66, 0xb1, 0x64, 0x93, 0x9d, 0xaf, 0xfa,
|
||||||
|
0x2e, 0xf6, 0xee, 0x69, 0x21, 0x08,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0x1a, 0xdd, 0x86, 0xb3, 0xf2, 0xe1, 0xbd, 0xa6, 0x2a, 0x5d, 0x2e, 0x0e, 0x98,
|
||||||
|
0x2b, 0x77, 0xe6, 0xb0, 0xef, 0x9c, 0xa3, 0xf2, 0x49, 0x88, 0xc7, 0xb3, 0x53,
|
||||||
|
0x42, 0x01, 0xcf, 0xb1, 0xcd, 0x0d,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
output: [
|
||||||
|
0xa1, 0x86, 0x2f, 0x7c, 0xa6, 0x16, 0xa3, 0x77, 0xe6, 0x01, 0x06, 0x89, 0x19, 0x20,
|
||||||
|
0x3a, 0x0e, 0x06, 0xe9, 0xa9, 0x03, 0x93, 0xc4, 0x01, 0xa5, 0x9c, 0x55, 0x40, 0xe5,
|
||||||
|
0xad, 0x2b, 0x0a, 0x00,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
TestVector {
|
||||||
|
input: [
|
||||||
|
[
|
||||||
|
0xbd, 0x69, 0xb8, 0x25, 0x32, 0xb6, 0x94, 0x0f, 0xf2, 0x59, 0x0f, 0x67, 0x9b,
|
||||||
|
0xa9, 0xc7, 0x27, 0x1f, 0xe0, 0x1f, 0x7e, 0x9c, 0x8e, 0x36, 0xd6, 0xa5, 0xe2,
|
||||||
|
0x9d, 0x4e, 0x30, 0xa7, 0x35, 0x14,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0xbc, 0x50, 0x98, 0x42, 0x55, 0xd6, 0xaf, 0xbe, 0x9e, 0xf9, 0x28, 0x48, 0xed,
|
||||||
|
0x5a, 0xc0, 0x08, 0x62, 0xc2, 0xfa, 0x7b, 0x2f, 0xec, 0xbc, 0xb6, 0x4b, 0x69,
|
||||||
|
0x68, 0x91, 0x2a, 0x63, 0x81, 0x0e,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
output: [
|
||||||
|
0x50, 0x25, 0xee, 0xb4, 0x24, 0x55, 0x4c, 0x39, 0xe8, 0xae, 0xf7, 0x00, 0xdd, 0xb8,
|
||||||
|
0xb6, 0x1e, 0x20, 0xe6, 0x9f, 0xc2, 0x86, 0x69, 0xd6, 0x7b, 0xa6, 0x8e, 0x48, 0xdb,
|
||||||
|
0x75, 0x2f, 0x7f, 0x14,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
TestVector {
|
||||||
|
input: [
|
||||||
|
[
|
||||||
|
0x3d, 0xc1, 0x66, 0xd5, 0x6a, 0x1d, 0x62, 0xf5, 0xa8, 0xd7, 0x55, 0x1d, 0xb5,
|
||||||
|
0xfd, 0x93, 0x13, 0xe8, 0xc7, 0x20, 0x3d, 0x99, 0x6a, 0xf7, 0xd4, 0x77, 0x08,
|
||||||
|
0x37, 0x56, 0xd5, 0x9a, 0xf8, 0x0d,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0x05, 0xa7, 0x45, 0xf4, 0x5d, 0x7f, 0xf6, 0xdb, 0x10, 0xbc, 0x67, 0xfd, 0xf0,
|
||||||
|
0xf0, 0x3e, 0xbf, 0x81, 0x30, 0xab, 0x33, 0x36, 0x26, 0x97, 0xb0, 0xe4, 0xe4,
|
||||||
|
0xc7, 0x63, 0xcc, 0xb8, 0xf6, 0x36,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
output: [
|
||||||
|
0x46, 0xd5, 0x2f, 0x0f, 0xde, 0xd6, 0x08, 0xb1, 0x90, 0x27, 0x28, 0x63, 0x7f, 0xdb,
|
||||||
|
0x93, 0x51, 0xc2, 0x9c, 0xb1, 0x98, 0xc3, 0x48, 0x03, 0x8b, 0x0e, 0xf4, 0x6e, 0xbd,
|
||||||
|
0x77, 0x16, 0x03, 0x33,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
TestVector {
|
||||||
|
input: [
|
||||||
|
[
|
||||||
|
0x49, 0x5c, 0x22, 0x2f, 0x7f, 0xba, 0x1e, 0x31, 0xde, 0xfa, 0x3d, 0x5a, 0x57,
|
||||||
|
0xef, 0xc2, 0xe1, 0xe9, 0xb0, 0x1a, 0x03, 0x55, 0x87, 0xd5, 0xfb, 0x1a, 0x38,
|
||||||
|
0xe0, 0x1d, 0x94, 0x90, 0x3d, 0x3c,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0x3d, 0x0a, 0xd3, 0x36, 0x1f, 0xec, 0x09, 0x77, 0x90, 0xd9, 0xbe, 0x0e, 0x42,
|
||||||
|
0x98, 0x8d, 0x7d, 0x25, 0xc9, 0xa1, 0x38, 0xf4, 0x9b, 0x1a, 0x53, 0x7e, 0xdc,
|
||||||
|
0xf0, 0x4b, 0xe3, 0x4a, 0x98, 0x11,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
output: [
|
||||||
|
0x7a, 0xee, 0xb4, 0xbd, 0x13, 0x6c, 0x7e, 0x32, 0x13, 0xf9, 0x4e, 0x39, 0x2b, 0x87,
|
||||||
|
0xeb, 0xdc, 0x39, 0xb9, 0x1c, 0x02, 0x54, 0x96, 0x5b, 0xe6, 0x96, 0x69, 0x40, 0xe3,
|
||||||
|
0xb0, 0x94, 0xc3, 0x16,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
TestVector {
|
||||||
|
input: [
|
||||||
|
[
|
||||||
|
0xa4, 0xaf, 0x9d, 0xb6, 0xd2, 0x7b, 0x50, 0x72, 0x83, 0x5f, 0x0c, 0x3e, 0x88,
|
||||||
|
0x39, 0x5e, 0xd7, 0xa4, 0x1b, 0x00, 0x52, 0xad, 0x80, 0x84, 0xa8, 0xb9, 0xda,
|
||||||
|
0x94, 0x8d, 0x32, 0x0d, 0xad, 0x16,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0x4d, 0x54, 0x31, 0xe6, 0x43, 0x7d, 0x0b, 0x5b, 0xed, 0xbb, 0xcd, 0xaf, 0x34,
|
||||||
|
0x5b, 0x86, 0xc4, 0x12, 0x1f, 0xc0, 0x0f, 0xe7, 0xf2, 0x35, 0x73, 0x42, 0x76,
|
||||||
|
0xd3, 0x8d, 0x47, 0xf1, 0xe1, 0x11,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
output: [
|
||||||
|
0xe3, 0x47, 0xad, 0x3f, 0x1f, 0x45, 0x68, 0x11, 0x15, 0x02, 0xe4, 0x0a, 0x62, 0x5a,
|
||||||
|
0x63, 0xc4, 0xd1, 0x85, 0xd9, 0x28, 0x33, 0xff, 0xc7, 0x47, 0x13, 0x95, 0x0b, 0x2c,
|
||||||
|
0xcf, 0x23, 0xa7, 0x08,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
TestVector {
|
||||||
|
input: [
|
||||||
|
[
|
||||||
|
0xdd, 0x0c, 0x7a, 0x1d, 0x81, 0x1c, 0x7d, 0x9c, 0xd4, 0x6d, 0x37, 0x7b, 0x3f,
|
||||||
|
0xde, 0xab, 0x3f, 0xb6, 0x79, 0xf3, 0xdc, 0x60, 0x1d, 0x00, 0x82, 0x85, 0xed,
|
||||||
|
0xcb, 0xda, 0xe6, 0x9c, 0xe8, 0x3c,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0x19, 0xe4, 0xaa, 0xc0, 0x35, 0x90, 0x17, 0xec, 0x85, 0xa1, 0x83, 0xd2, 0x20,
|
||||||
|
0x53, 0xdb, 0x33, 0xf7, 0x34, 0x76, 0xf2, 0x1a, 0x48, 0x2e, 0xc9, 0x37, 0x83,
|
||||||
|
0x65, 0xc8, 0xf7, 0x39, 0x3c, 0x14,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
output: [
|
||||||
|
0x4b, 0x8a, 0x3c, 0xfd, 0x9e, 0xa3, 0x0e, 0x27, 0x13, 0xf4, 0x24, 0xcf, 0x87, 0xaa,
|
||||||
|
0x5a, 0x4f, 0xc8, 0x75, 0xdb, 0x9f, 0xed, 0x56, 0xe4, 0x48, 0x53, 0x40, 0xf6, 0x68,
|
||||||
|
0x0e, 0x50, 0xf2, 0x25,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
TestVector {
|
||||||
|
input: [
|
||||||
|
[
|
||||||
|
0xe2, 0x88, 0x53, 0x15, 0xeb, 0x46, 0x71, 0x09, 0x8b, 0x79, 0x53, 0x5e, 0x79,
|
||||||
|
0x0f, 0xe5, 0x3e, 0x29, 0xfe, 0xf2, 0xb3, 0x76, 0x66, 0x97, 0xac, 0x32, 0xb4,
|
||||||
|
0xf4, 0x73, 0xf4, 0x68, 0xa0, 0x08,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0xe6, 0x23, 0x89, 0xfc, 0x16, 0x57, 0xe0, 0xde, 0xf0, 0xb6, 0x32, 0xc6, 0xae,
|
||||||
|
0x25, 0xf9, 0xf7, 0x83, 0xb2, 0x7d, 0xb5, 0x9a, 0x4a, 0x15, 0x3d, 0x88, 0x2d,
|
||||||
|
0x2b, 0x21, 0x03, 0x59, 0x65, 0x15,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
output: [
|
||||||
|
0xcd, 0xc9, 0x0c, 0x38, 0x24, 0x2f, 0xd9, 0xf4, 0x0d, 0x1e, 0x83, 0xca, 0xdd, 0x37,
|
||||||
|
0x0d, 0x5a, 0xae, 0xa4, 0x91, 0x4b, 0x2c, 0x20, 0x9e, 0x8a, 0xc3, 0x0e, 0x97, 0x4e,
|
||||||
|
0x97, 0x5f, 0xe0, 0x36,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
TestVector {
|
||||||
|
input: [
|
||||||
|
[
|
||||||
|
0xeb, 0x94, 0x94, 0xc6, 0xd2, 0x27, 0xe2, 0x16, 0x3b, 0x46, 0x99, 0xd9, 0x91,
|
||||||
|
0xf4, 0x33, 0xbf, 0x94, 0x86, 0xa7, 0xaf, 0xcf, 0x4a, 0x0d, 0x9c, 0x73, 0x1e,
|
||||||
|
0x98, 0x5d, 0x99, 0x58, 0x9c, 0x0b,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0xb7, 0x38, 0xe8, 0xaa, 0x0a, 0x15, 0x26, 0xa5, 0xbd, 0xef, 0x61, 0x31, 0x20,
|
||||||
|
0x37, 0x2e, 0x83, 0x1a, 0x20, 0xda, 0x8a, 0xba, 0x18, 0xd1, 0xdb, 0xeb, 0xbc,
|
||||||
|
0x86, 0x2d, 0xed, 0x42, 0x43, 0x1e,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
output: [
|
||||||
|
0xac, 0x31, 0xe3, 0x4c, 0xb3, 0x68, 0x69, 0x55, 0x29, 0x9d, 0xd3, 0x30, 0x4a, 0x1f,
|
||||||
|
0x90, 0x27, 0xd9, 0x39, 0xa0, 0xe0, 0x85, 0xca, 0xac, 0xe1, 0x16, 0x19, 0x2d, 0xed,
|
||||||
|
0x3e, 0xb0, 0x07, 0x38,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
TestVector {
|
||||||
|
input: [
|
||||||
|
[
|
||||||
|
0x91, 0x47, 0x69, 0x30, 0xe3, 0x38, 0x5c, 0xd3, 0xe3, 0x37, 0x9e, 0x38, 0x53,
|
||||||
|
0xd9, 0x34, 0x67, 0xe0, 0x01, 0xaf, 0xa2, 0xfb, 0x8d, 0xc3, 0x43, 0x6d, 0x75,
|
||||||
|
0xa4, 0xa6, 0xf2, 0x65, 0x72, 0x10,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0x4b, 0x19, 0x22, 0x32, 0xec, 0xb9, 0xf0, 0xc0, 0x24, 0x11, 0xe5, 0x25, 0x96,
|
||||||
|
0xbc, 0x5e, 0x90, 0x45, 0x7e, 0x74, 0x59, 0x39, 0xff, 0xed, 0xbd, 0x12, 0x86,
|
||||||
|
0x3c, 0xe7, 0x1a, 0x02, 0xaf, 0x11,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
output: [
|
||||||
|
0x9e, 0xe9, 0xcc, 0x52, 0x0f, 0xc6, 0xea, 0xc7, 0x7e, 0xf0, 0x03, 0x90, 0x26, 0xa9,
|
||||||
|
0xe8, 0x21, 0x5e, 0x88, 0x92, 0x2c, 0x8e, 0x8e, 0x79, 0xfe, 0x00, 0xaa, 0xbe, 0x81,
|
||||||
|
0x64, 0x14, 0x1b, 0x2a,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
TestVector {
|
||||||
|
input: [
|
||||||
|
[
|
||||||
|
0x7b, 0x41, 0x7a, 0xdb, 0x63, 0xb3, 0x71, 0x22, 0xa5, 0xbf, 0x62, 0xd2, 0x6f,
|
||||||
|
0x1e, 0x7f, 0x26, 0x8f, 0xb8, 0x6b, 0x12, 0xb5, 0x6d, 0xa9, 0xc3, 0x82, 0x85,
|
||||||
|
0x7d, 0xee, 0xcc, 0x40, 0xa9, 0x0d,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0x5e, 0x29, 0x35, 0x39, 0x71, 0xb3, 0x49, 0x94, 0xb6, 0x21, 0xb0, 0xb2, 0x61,
|
||||||
|
0xae, 0xb3, 0x78, 0x6d, 0xd9, 0x84, 0xd5, 0x67, 0xdb, 0x28, 0x57, 0xb9, 0x27,
|
||||||
|
0xb7, 0xfa, 0xe2, 0xdb, 0x58, 0x31,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
output: [
|
||||||
|
0xd5, 0x03, 0xb7, 0x39, 0xaa, 0x03, 0x29, 0x51, 0xfb, 0x9a, 0x3e, 0xec, 0x0b, 0x91,
|
||||||
|
0xd3, 0x25, 0x18, 0x82, 0xa2, 0xda, 0x32, 0x8b, 0x31, 0x75, 0x20, 0xa7, 0x3c, 0x14,
|
||||||
|
0x35, 0x31, 0x5c, 0x17,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
Loading…
Reference in New Issue