Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion crates/backend/symetric/src/merkle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ pub fn merkle_verify<F, Comp, const DIGEST_ELEMS: usize, const WIDTH: usize, con
opening_proof: &[[F; DIGEST_ELEMS]],
) -> bool
where
F: Default + Copy + PartialEq,
F: field::PrimeCharacteristicRing + PartialEq,
Comp: Compression<[F; WIDTH]>,
{
if opening_proof.len() != log_height {
Expand Down
73 changes: 19 additions & 54 deletions crates/backend/symetric/src/sponge.rs
Original file line number Diff line number Diff line change
@@ -1,44 +1,44 @@
// Credits: Plonky3 (https://github.com/Plonky3/Plonky3) (MIT and Apache-2.0 licenses).

use crate::Compression;
use field::PrimeCharacteristicRing;

// IV should have been added to data when necessary (typically: when the length of the data beeing hashed is not constant). Maybe we should re-add IV all the time for simplicity?
// assumes data length is a multiple of RATE (= 8 in practice).
/// Absorbs `data` RTL into an IV state `[data.len(), 0, ..., 0]` in RATE-sized chunks.
/// assumes data length is a multiple of RATE (= 8 in practice).
pub fn hash_slice<T, Comp, const WIDTH: usize, const RATE: usize, const OUT: usize>(comp: &Comp, data: &[T]) -> [T; OUT]
where
T: Default + Copy,
T: PrimeCharacteristicRing,
Comp: Compression<[T; WIDTH]>,
{
debug_assert!(RATE == OUT);
debug_assert!(WIDTH == OUT + RATE);
debug_assert!(data.len().is_multiple_of(RATE));
let n_chunks = data.len() / RATE;
debug_assert!(n_chunks >= 2);
let mut state: [T; WIDTH] = data[data.len() - WIDTH..].try_into().unwrap();
comp.compress_mut(&mut state);
for chunk_idx in (0..n_chunks - 2).rev() {
let offset = chunk_idx * RATE;
state[WIDTH - RATE..].copy_from_slice(&data[offset..offset + RATE]);
let mut state = [T::default(); WIDTH];
state[0] = T::from_usize(data.len());
for chunk in data.chunks_exact(RATE).rev() {
state[WIDTH - RATE..].copy_from_slice(chunk);
comp.compress_mut(&mut state);
}
state[..OUT].try_into().unwrap()
}

/// Precompute sponge state after absorbing `n_zero_chunks` all-zero RATE-chunks.
/// Precompute sponge state after absorbing `n_zero_chunks` all-zero RATE-chunks
/// into an IV state `[iv_first, 0, ..., 0]`. Caller provides `iv_first` (typically
/// the length, in field elements, of the full slice that will eventually be hashed).
pub fn precompute_zero_suffix_state<T, Comp, const WIDTH: usize, const RATE: usize, const OUT: usize>(
comp: &Comp,
iv_first: T,
n_zero_chunks: usize,
) -> [T; WIDTH]
where
T: Default + Copy,
T: PrimeCharacteristicRing,
Comp: Compression<[T; WIDTH]>,
{
debug_assert!(RATE == OUT);
debug_assert!(WIDTH == OUT + RATE);
debug_assert!(n_zero_chunks >= 2);
let mut state = [T::default(); WIDTH];
comp.compress_mut(&mut state);
for _ in 0..n_zero_chunks - 2 {
state[0] = iv_first;
for _ in 0..n_zero_chunks {
for s in &mut state[WIDTH - RATE..] {
*s = T::default();
}
Expand All @@ -47,29 +47,7 @@ where
state
}

/// RTL = Right-to-left
#[inline(always)]
pub fn hash_rtl_iter<T, Comp, I, const WIDTH: usize, const RATE: usize, const OUT: usize>(
comp: &Comp,
rtl_iter: I,
) -> [T; OUT]
where
T: Default + Copy,
Comp: Compression<[T; WIDTH]>,
I: IntoIterator<Item = T>,
{
debug_assert!(RATE == OUT);
debug_assert!(WIDTH == OUT + RATE);
let mut state = [T::default(); WIDTH];
let mut iter = rtl_iter.into_iter();
for pos in (0..WIDTH).rev() {
state[pos] = iter.next().unwrap();
}
comp.compress_mut(&mut state);
absorb_rtl_chunks::<T, Comp, _, WIDTH, RATE, OUT>(comp, &mut state, &mut iter)
}

/// RTL = Right-to-left
/// RTL = Right-to-left. Absorbs starting from the provided `initial_state` in RATE-sized chunks.
#[inline(always)]
pub fn hash_rtl_iter_with_initial_state<T, Comp, I, const WIDTH: usize, const RATE: usize, const OUT: usize>(
comp: &Comp,
Expand All @@ -81,28 +59,15 @@ where
Comp: Compression<[T; WIDTH]>,
I: Iterator<Item = T>,
{
debug_assert!(RATE == OUT);
debug_assert!(WIDTH == OUT + RATE);
let mut state = *initial_state;
absorb_rtl_chunks::<T, Comp, _, WIDTH, RATE, OUT>(comp, &mut state, &mut iter)
}

/// RTL = Right-to-left
#[inline(always)]
fn absorb_rtl_chunks<T, Comp, I, const WIDTH: usize, const RATE: usize, const OUT: usize>(
comp: &Comp,
state: &mut [T; WIDTH],
iter: &mut I,
) -> [T; OUT]
where
T: Default + Copy,
Comp: Compression<[T; WIDTH]>,
I: Iterator<Item = T>,
{
while let Some(elem) = iter.next() {
state[WIDTH - 1] = elem;
for pos in (WIDTH - RATE..WIDTH - 1).rev() {
state[pos] = iter.next().unwrap();
}
comp.compress_mut(state);
comp.compress_mut(&mut state);
}
state[..OUT].try_into().unwrap()
}
2 changes: 1 addition & 1 deletion crates/lean_compiler/src/c_compile_final.rs
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ pub fn compile_to_low_level_bytecode(
pc_to_location.push(current_location);
}

let hash = poseidon_compress_slice(&instructions_multilinear, true);
let hash = poseidon_compress_slice(&instructions_multilinear);

let code: Vec<_> = instructions
.into_iter()
Expand Down
2 changes: 1 addition & 1 deletion crates/rec_aggregation/src/bytecode_claims.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ pub(crate) fn hash_bytecode_claims(claims: &[Evaluation<EF>]) -> [F; DIGEST_LEN]
let mut data = flatten_scalars_to_base::<F, EF>(&ef_data);
data.resize(data.len().next_multiple_of(DIGEST_LEN), F::ZERO);

let claim_hash = poseidon_compress_slice(&data, false);
let claim_hash = poseidon_compress_slice(&data);
running_hash = poseidon16_compress_pair(&running_hash, &claim_hash);
}
running_hash
Expand Down
2 changes: 1 addition & 1 deletion crates/rec_aggregation/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ pub struct InnerVerified {
}

pub(crate) fn verify_inner(input_data: Vec<F>, proof: Proof<F>) -> Result<InnerVerified, ProofError> {
let input_data_hash = poseidon_compress_slice(&input_data, true);
let input_data_hash = poseidon_compress_slice(&input_data);
let bytecode = get_aggregation_bytecode();
let (verif, raw_proof) = verify_execution(bytecode, &input_data_hash, proof)?;
Ok(InnerVerified {
Expand Down
10 changes: 4 additions & 6 deletions crates/rec_aggregation/src/type_1_aggregation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,6 @@ pub(crate) const N_TWEAKS: usize = 1 + V * CHAIN_LENGTH + 1 + LOG_LIFETIME;
pub(crate) const TWEAK_SLOT_SIZE: usize = 4;
pub(crate) const TWEAK_TABLE_SIZE_FE_PADDED: usize = (N_TWEAKS * TWEAK_SLOT_SIZE).next_multiple_of(DIGEST_LEN);

pub(crate) const TWEAKS_HASHING_USE_IV: bool = false; // fixed size → no IV needed

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
pub(crate) struct Digest(pub [F; DIGEST_LEN]);

Expand Down Expand Up @@ -100,7 +98,7 @@ impl TypeOneInfo {

pub(crate) fn build_input_data(&self) -> Vec<F> {
let tweak_table = compute_tweak_table(self.slot);
let tweaks_hash = poseidon_compress_slice(&tweak_table, TWEAKS_HASHING_USE_IV);
let tweaks_hash = poseidon_compress_slice(&tweak_table);
build_type1_input_data(
self.pubkeys.len(),
&hash_pubkeys(&self.pubkeys),
Expand All @@ -115,7 +113,7 @@ impl TypeOneInfo {

pub(crate) fn hash_pubkeys(pub_keys: &[XmssPublicKey]) -> [F; DIGEST_LEN] {
let flat: Vec<F> = pub_keys.iter().flat_map(|pk| pk.flaten().into_iter()).collect();
poseidon_compress_slice(&flat, true)
poseidon_compress_slice(&flat)
}

/// Tweak slots are 4-FE [tw[0], tw[1], 0, 0]
Expand Down Expand Up @@ -262,7 +260,7 @@ pub(crate) fn aggregate_type_1_with_min_padding(
assert!(n_sigs <= MAX_XMSS_AGGREGATED);

let tweak_table = compute_tweak_table(slot);
let tweaks_hash = poseidon_compress_slice(&tweak_table, TWEAKS_HASHING_USE_IV);
let tweaks_hash = poseidon_compress_slice(&tweak_table);

let reduced_claims = reduce_bytecode_claims(&verified_children);

Expand All @@ -275,7 +273,7 @@ pub(crate) fn aggregate_type_1_with_min_padding(
&reduced_claims.final_claim_flat(),
bytecode,
);
let public_input = poseidon_compress_slice(&pub_input_data, true).to_vec();
let public_input = poseidon_compress_slice(&pub_input_data).to_vec();

let mut claimed: HashSet<XmssPublicKey> = HashSet::new();
let mut dup_pub_keys: Vec<XmssPublicKey> = Vec::new();
Expand Down
6 changes: 3 additions & 3 deletions crates/rec_aggregation/src/type_2_aggregation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ pub fn merge_many_type_1(

let digests: Vec<[F; DIGEST_LEN]> = verified_children.iter().map(|v| v.input_data_hash).collect();
let pub_input_data = build_type2_input_data(&digests, &reduced_claims.final_claim_flat());
let public_input_digest = poseidon_compress_slice(&pub_input_data, true).to_vec();
let public_input_digest = poseidon_compress_slice(&pub_input_data).to_vec();

let bytecode_value_hint_blobs: Vec<Vec<F>> = verified_children
.iter()
Expand Down Expand Up @@ -173,7 +173,7 @@ pub fn verify_type_2(sig: &TypeTwoMultiSignature) -> Result<InnerVerified, Proof
let digests = sig
.info
.iter()
.map(|info| poseidon_compress_slice(&info.build_input_data(), true))
.map(|info| poseidon_compress_slice(&info.build_input_data()))
.collect::<Vec<_>>();
let input_data = build_type2_input_data(&digests, &sig.bytecode_claim_flat());
verify_inner(input_data, sig.proof.proof.clone())
Expand Down Expand Up @@ -222,7 +222,7 @@ pub fn split_type_2(
let mut outer_type_1 = type_2.info[index].clone();
outer_type_1.bytecode_claim = reduced_claims.final_claim.clone();
let ourer_input_data = outer_type_1.build_input_data();
let outer_digest = poseidon_compress_slice(&ourer_input_data, true);
let outer_digest = poseidon_compress_slice(&ourer_input_data);

let inner_input_data: Vec<F> = type_2.info[index].build_input_data();

Expand Down
55 changes: 34 additions & 21 deletions crates/rec_aggregation/zkdsl_implem/hashing.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
PREAMBLE_MEMORY_LEN = PREAMBLE_MEMORY_END - PUBLIC_INPUT_LEN


def batch_hash_slice_rtl(num_queries, all_data_to_hash, all_resulting_hashes, num_chunks):
def batch_hash_slice_rtl_with_iv(num_queries, all_data_to_hash, all_resulting_hashes, num_chunks):
if num_chunks == DIM * 2:
batch_hash_slice_rtl_const(num_queries, all_data_to_hash, all_resulting_hashes, DIM * 2)
return
Expand All @@ -43,67 +43,80 @@ def batch_hash_slice_rtl(num_queries, all_data_to_hash, all_resulting_hashes, nu


def batch_hash_slice_rtl_const(num_queries, all_data_to_hash, all_resulting_hashes, num_chunks: Const):
iv = build_iv(num_chunks * DIGEST_LEN)
for i in range(0, num_queries):
data = all_data_to_hash[i]
res = slice_hash_rtl(data, num_chunks)
res = slice_hash_rtl(data, num_chunks, iv)
all_resulting_hashes[i] = res
return


# IV for the sponge: [slice length in field elements, 0, 0, ..., 0]
@inline
def slice_hash_rtl(data, num_chunks):
states = Array((num_chunks - 1) * DIGEST_LEN)
def build_iv(length):
iv = Array(DIGEST_LEN)
iv[0] = length
for k in unroll(1, DIGEST_LEN):
iv[k] = 0
return iv

poseidon16_compress(data + (num_chunks - 2) * DIGEST_LEN, data + (num_chunks - 1) * DIGEST_LEN, states)
for j in unroll(1, num_chunks - 1):

@inline
def slice_hash_rtl(data, num_chunks, iv):
debug_assert(1 <= num_chunks)
states = Array(num_chunks * DIGEST_LEN)
poseidon16_compress(iv, data + (num_chunks - 1) * DIGEST_LEN, states)
for j in unroll(1, num_chunks):
poseidon16_compress(
states + (j - 1) * DIGEST_LEN, data + (num_chunks - 2 - j) * DIGEST_LEN, states + j * DIGEST_LEN
states + (j - 1) * DIGEST_LEN, data + (num_chunks - 1 - j) * DIGEST_LEN, states + j * DIGEST_LEN
)
return states + (num_chunks - 2) * DIGEST_LEN
return states + (num_chunks - 1) * DIGEST_LEN


@inline
def slice_hash(data, num_chunks):
states = Array((num_chunks - 1) * DIGEST_LEN)
poseidon16_compress(data, data + DIGEST_LEN, states)
for j in unroll(1, num_chunks - 1):
poseidon16_compress(states + (j - 1) * DIGEST_LEN, data + (j + 1) * DIGEST_LEN, states + j * DIGEST_LEN)
return states + (num_chunks - 2) * DIGEST_LEN
def slice_hash_ret(data, num_chunks):
res = Array(DIGEST_LEN)
slice_hash(data, num_chunks, res)
return res


def slice_hash_with_iv_range(data, num_chunks, dest):
def slice_hash_range(data, num_chunks, dest):
debug_assert(0 < num_chunks)
debug_assert(2 < num_chunks)
iv = build_iv(num_chunks * DIGEST_LEN)
states = Array((num_chunks - 1) * DIGEST_LEN)
poseidon16_compress(ZERO_VEC_PTR, data, states)
poseidon16_compress(iv, data, states)
for j in range(1, num_chunks - 1):
poseidon16_compress(states + (j - 1) * DIGEST_LEN, data + j * DIGEST_LEN, states + j * DIGEST_LEN)
poseidon16_compress(states + (num_chunks - 2) * DIGEST_LEN, data + (num_chunks - 1) * DIGEST_LEN, dest)
return


@inline
def slice_hash_with_iv(data, num_chunks, dest):
def slice_hash(data, num_chunks, dest):
debug_assert(2 <= num_chunks)
iv = build_iv(num_chunks * DIGEST_LEN)
states = Array(num_chunks * DIGEST_LEN)
poseidon16_compress(ZERO_VEC_PTR, data, states)
poseidon16_compress(iv, data, states)
for j in unroll(1, num_chunks - 1):
poseidon16_compress(states + (j - 1) * DIGEST_LEN, data + j * DIGEST_LEN, states + j * DIGEST_LEN)
poseidon16_compress(states + (num_chunks - 2) * DIGEST_LEN, data + (num_chunks - 1) * DIGEST_LEN, dest)
return


def slice_hash_with_iv_dynamic_unroll(data, num_chunks, num_chunks_bits: Const):
def slice_hash_dynamic_unroll(data, num_chunks, num_chunks_bits: Const):
debug_assert(num_chunks != 0)
debug_assert(num_chunks < 2**num_chunks_bits)

iv = build_iv(num_chunks * DIGEST_LEN)

if num_chunks == 1:
result = Array(DIGEST_LEN)
poseidon16_compress(ZERO_VEC_PTR, data, result)
poseidon16_compress(iv, data, result)
return result

states = Array(num_chunks * DIGEST_LEN)
poseidon16_compress(ZERO_VEC_PTR, data, states)
poseidon16_compress(iv, data, states)
n_iters = num_chunks - 1
state_ptr: Mut = states
data_ptr: Mut = data + DIGEST_LEN
Expand Down
Loading
Loading