use super::*;
#[test]
fn same_messages_same_challenge() {
let mut t1 = Transcript::new();
t1.absorb(b"hello");
let c1 = t1.squeeze_challenge();
let mut t2 = Transcript::new();
t2.absorb(b"hello");
let c2 = t2.squeeze_challenge();
assert_eq!(c1.as_u64(), c2.as_u64());
}
#[test]
fn different_messages_different_challenges() {
let mut t1 = Transcript::new();
t1.absorb(b"hello");
let c1 = t1.squeeze_challenge();
let mut t2 = Transcript::new();
t2.absorb(b"world");
let c2 = t2.squeeze_challenge();
assert_ne!(c1.as_u64(), c2.as_u64());
}
#[cfg(feature = "legacy")]
#[test]
fn different_domains_different_challenges() {
let mut t1 = Transcript::new();
t1.absorb(b"same");
let c1 = t1.squeeze_challenge();
let mut t2 = Transcript::new_recursive();
t2.absorb(b"same");
let c2 = t2.squeeze_challenge();
assert_ne!(c1.as_u64(), c2.as_u64());
}
#[test]
fn multiple_squeezes_are_independent() {
let mut t = Transcript::new();
t.absorb(b"test");
let c1 = t.squeeze_challenge();
let c2 = t.squeeze_challenge();
assert_ne!(c1.as_u64(), c2.as_u64());
}
#[test]
fn encode_decode_field_roundtrip() {
let f = Goldilocks::new(12345678);
let bytes = encode_field(f);
let decoded = decode_field(&bytes).unwrap();
assert_eq!(f.as_u64(), decoded.as_u64());
}
#[test]
fn decode_field_rejects_noncanonical() {
let bytes = P.to_le_bytes();
assert!(decode_field(&bytes).is_none());
let bytes2 = (P + 1).to_le_bytes();
assert!(decode_field(&bytes2).is_none());
}
#[test]
fn decode_field_accepts_p_minus_1() {
let bytes = (P - 1).to_le_bytes();
let f = decode_field(&bytes).unwrap();
assert_eq!(f.as_u64(), P - 1);
}
#[test]
fn encode_decode_fields_roundtrip() {
let elems: Vec<Goldilocks> = (0u64..8).map(Goldilocks::new).collect();
let bytes = encode_fields(&elems);
let decoded = decode_fields(&bytes).unwrap();
for (a, b) in elems.iter().zip(decoded.iter()) {
assert_eq!(a.as_u64(), b.as_u64());
}
}
#[test]
fn decode_fields_rejects_odd_length() {
assert!(decode_fields(&[0u8; 7]).is_none());
assert!(decode_fields(&[0u8; 9]).is_none());
}
#[test]
fn sumcheck_poly_encode_decode_roundtrip() {
let poly = SumcheckPoly {
degree: 3,
coeffs: vec![
Goldilocks::new(1),
Goldilocks::new(2),
Goldilocks::new(3),
Goldilocks::new(4),
],
};
let bytes = encode_sumcheck_poly(&poly);
let (decoded, consumed) = decode_sumcheck_poly(&bytes).unwrap();
assert_eq!(consumed, bytes.len());
assert_eq!(decoded.degree, poly.degree);
for (a, b) in poly.coeffs.iter().zip(decoded.coeffs.iter()) {
assert_eq!(a.as_u64(), b.as_u64());
}
}
#[test]
fn commitment_domain_separation() {
use lens::{Lens, MultilinearPoly, brakedown::Brakedown};
let poly = MultilinearPoly::new(vec![
Goldilocks::new(1),
Goldilocks::new(2),
Goldilocks::new(3),
Goldilocks::new(4),
]);
let c = Brakedown::commit(&poly);
let mut t1 = Transcript::new();
t1.absorb_commitment(&c);
let ch1 = t1.squeeze_challenge();
let mut t2 = Transcript::new();
t2.absorb(c.as_bytes());
let ch2 = t2.squeeze_challenge();
assert_ne!(ch1.as_u64(), ch2.as_u64());
}
#[test]
fn v1_rule_reproduces_the_first_limb_of_one_squeeze() {
let mut a = Transcript::new_v1();
a.absorb(b"compat");
let mut b = a.clone();
let hash = b.squeeze_hash();
let mut limb = [0u8; 8];
limb.copy_from_slice(&hash[..8]);
assert_eq!(a.squeeze_challenge().as_u64(), u64::from_le_bytes(limb));
let mut wide = Transcript::new();
wide.absorb(b"compat");
let mut v1 = Transcript::new_v1();
v1.absorb(b"compat");
assert_ne!(wide.squeeze_challenge(), v1.squeeze_challenge());
}
#[test]
fn reduce_192_is_the_integer_modulo_p() {
let shift = Goldilocks::new(1 << 32) * Goldilocks::new(1 << 32); let mut state = 0x9E37_79B9_7F4A_7C15u64;
let mut next = || {
state ^= state << 13;
state ^= state >> 7;
state ^= state << 17;
state
};
let edge = [0, 1, P - 1, P, P + 1, u64::MAX];
for i in 0..2000 {
let limbs = if i < 216 {
[edge[i % 6], edge[(i / 6) % 6], edge[(i / 36) % 6]]
} else {
[next(), next(), next()]
};
let mut bytes = [0u8; 24];
for (k, l) in limbs.iter().enumerate() {
bytes[8 * k..8 * k + 8].copy_from_slice(&l.to_le_bytes());
}
let expect = Goldilocks::new(limbs[0]).canonicalize()
+ Goldilocks::new(limbs[1]).canonicalize() * shift
+ Goldilocks::new(limbs[2]).canonicalize() * shift * shift;
let got = reduce_192(&bytes);
assert!(got.as_u64() < P);
assert_eq!(got, expect, "{limbs:?}");
}
assert!((P as u128) < 1u128 << 64);
}
#[test]
fn hemera_output_limbs_are_canonical() {
let mut h = Hasher::new();
h.update(b"limb-encoding");
let mut xof = h.finalize_xof();
for _ in 0..4096 {
let mut limb = [0u8; 8];
xof.fill(&mut limb);
assert!(u64::from_le_bytes(limb) < P);
}
}
#[test]
fn fp3_challenges_are_deterministic_chained_and_extension_valued() {
let mut a = Transcript::new();
a.absorb(b"fp3");
let mut b = a.clone();
let x = a.squeeze_fp3();
assert_eq!(x, b.squeeze_fp3());
let y = a.squeeze_fp3();
assert_ne!(x, y);
assert!(x.c1 != Goldilocks::ZERO || x.c2 != Goldilocks::ZERO);
let mut c = Transcript::new_v1();
c.absorb(b"fp3");
assert_eq!(c.squeeze_fp3(), x);
}