extern crate alloc;
use alloc::string::ToString;
use alloc::vec::Vec;
use lib_q_stark_air::{
Air,
AirBuilder,
BaseAir,
};
use lib_q_stark_field::Field;
use lib_q_stark_matrix::dense::RowMajorMatrix;
use super::merkle_inclusion::{
MerkleInclusionAir,
MerkleProofInput,
};
use super::{
AirError,
TraceGenerator,
};
pub const MAX_GROUP_DEPTH: usize = 64;
#[derive(Debug, Clone)]
pub struct AnonymousAuthAir {
group_depth: usize,
use_ring_sig: bool,
merkle_air: MerkleInclusionAir,
}
impl AnonymousAuthAir {
pub fn new(group_depth: usize, use_ring_sig: bool) -> Result<Self, AirError> {
if group_depth == 0 {
return Err(AirError::InvalidDimensions {
reason: "Group depth must be greater than 0".to_string(),
});
}
if group_depth > MAX_GROUP_DEPTH {
return Err(AirError::ExceedsMaxSize {
parameter: "group_depth".to_string(),
max: MAX_GROUP_DEPTH,
actual: group_depth,
});
}
let merkle_air = MerkleInclusionAir::new(group_depth)?;
Ok(Self {
group_depth,
use_ring_sig,
merkle_air,
})
}
pub fn group_depth(&self) -> usize {
self.group_depth
}
}
impl<F: Field + lib_q_stark_field::BasedVectorSpace<lib_q_stark_mersenne31::Mersenne31>> BaseAir<F>
for AnonymousAuthAir
{
fn width(&self) -> usize {
<MerkleInclusionAir as BaseAir<F>>::width(&self.merkle_air)
}
}
impl<AB: AirBuilder> Air<AB> for AnonymousAuthAir
where
AB::F: Field + lib_q_stark_field::BasedVectorSpace<lib_q_stark_mersenne31::Mersenne31>,
{
fn eval(&self, builder: &mut AB) {
self.merkle_air.eval(builder);
let _ = self.use_ring_sig;
}
}
#[derive(Debug, Clone)]
pub struct AnonymousAuthInput {
pub member_identity: Vec<u8>,
pub membership_path: MerkleProofInput,
}
impl
TraceGenerator<
lib_q_stark_field::extension::Complex<lib_q_stark_mersenne31::Mersenne31>,
AnonymousAuthInput,
> for AnonymousAuthAir
{
fn generate_trace(
&self,
inputs: &AnonymousAuthInput,
) -> Result<
RowMajorMatrix<lib_q_stark_field::extension::Complex<lib_q_stark_mersenne31::Mersenne31>>,
AirError,
> {
let merkle_input = MerkleProofInput {
leaf: inputs.member_identity.clone(),
leaf_hash_direct: None,
path_bits: inputs.membership_path.path_bits.clone(),
siblings: inputs.membership_path.siblings.clone(),
};
self.merkle_air.generate_trace(&merkle_input)
}
fn public_values(
&self,
inputs: &AnonymousAuthInput,
) -> Vec<lib_q_stark_field::extension::Complex<lib_q_stark_mersenne31::Mersenne31>> {
let merkle_input = MerkleProofInput {
leaf: inputs.member_identity.clone(),
leaf_hash_direct: None,
path_bits: inputs.membership_path.path_bits.clone(),
siblings: inputs.membership_path.siblings.clone(),
};
self.merkle_air.public_values(&merkle_input)
}
}
#[cfg(test)]
mod tests {
use alloc::vec;
use lib_q_stark_field::extension::Complex;
use lib_q_stark_mersenne31::Mersenne31;
use super::*;
#[test]
fn test_anonymous_auth_air_creation() {
let air = AnonymousAuthAir::new(8, false).unwrap();
assert_eq!(air.group_depth(), 8);
}
#[test]
fn test_anonymous_auth_air_validation() {
let result = AnonymousAuthAir::new(0, false);
assert!(matches!(result, Err(AirError::InvalidDimensions { .. })));
let result = AnonymousAuthAir::new(MAX_GROUP_DEPTH + 1, false);
assert!(matches!(result, Err(AirError::ExceedsMaxSize { .. })));
}
#[test]
fn test_anonymous_auth_trace_generation() {
use super::super::merkle_inclusion::MerkleHash;
let air = AnonymousAuthAir::new(3, false).unwrap();
let input = AnonymousAuthInput {
member_identity: vec![1, 2, 3, 4],
membership_path: MerkleProofInput {
leaf: vec![1, 2, 3, 4],
leaf_hash_direct: None,
path_bits: vec![false, true, false],
siblings: vec![
MerkleHash::from_bytes(&[0u8; 32]).unwrap(),
MerkleHash::from_bytes(&[0u8; 32]).unwrap(),
MerkleHash::from_bytes(&[0u8; 32]).unwrap(),
],
},
};
let trace = air.generate_trace(&input);
assert!(trace.is_ok());
}
#[test]
fn test_anonymous_auth_public_values_length() {
use super::super::merkle_inclusion::MerkleHash;
let air = AnonymousAuthAir::new(3, false).unwrap();
let input = AnonymousAuthInput {
member_identity: vec![1, 2, 3, 4],
membership_path: MerkleProofInput {
leaf: vec![1, 2, 3, 4],
leaf_hash_direct: None,
path_bits: vec![false, true, false],
siblings: vec![
MerkleHash::from_bytes(&[0u8; 32]).unwrap(),
MerkleHash::from_bytes(&[0u8; 32]).unwrap(),
MerkleHash::from_bytes(&[0u8; 32]).unwrap(),
],
},
};
assert_eq!(air.public_values(&input).len(), 1);
}
#[test]
fn test_anonymous_auth_public_values_deterministic() {
use super::super::merkle_inclusion::MerkleHash;
let air = AnonymousAuthAir::new(3, false).unwrap();
let input = AnonymousAuthInput {
member_identity: vec![10, 20, 30, 40],
membership_path: MerkleProofInput {
leaf: vec![10, 20, 30, 40],
leaf_hash_direct: None,
path_bits: vec![true, false, true],
siblings: vec![
MerkleHash::from_bytes(&[1u8; 32]).unwrap(),
MerkleHash::from_bytes(&[2u8; 32]).unwrap(),
MerkleHash::from_bytes(&[3u8; 32]).unwrap(),
],
},
};
assert_eq!(air.public_values(&input), air.public_values(&input));
}
#[test]
fn test_anonymous_auth_public_values_differ_for_different_identity() {
use super::super::merkle_inclusion::MerkleHash;
let air = AnonymousAuthAir::new(3, false).unwrap();
let siblings = vec![
MerkleHash::from_bytes(&[0u8; 32]).unwrap(),
MerkleHash::from_bytes(&[0u8; 32]).unwrap(),
MerkleHash::from_bytes(&[0u8; 32]).unwrap(),
];
let make_input = |leaf: Vec<u8>| AnonymousAuthInput {
member_identity: leaf.clone(),
membership_path: MerkleProofInput {
leaf,
leaf_hash_direct: None,
path_bits: vec![false, true, false],
siblings: siblings.clone(),
},
};
assert_ne!(
air.public_values(&make_input(vec![1, 2, 3, 4])),
air.public_values(&make_input(vec![5, 6, 7, 8])),
);
}
#[test]
fn test_anonymous_auth_public_values_match_merkle_air() {
use super::super::merkle_inclusion::{
MerkleHash,
MerkleInclusionAir,
};
type Val = Complex<Mersenne31>;
let depth = 3;
let air = AnonymousAuthAir::new(depth, false).unwrap();
let merkle_air = MerkleInclusionAir::new(depth).unwrap();
let leaf = vec![1u8, 2, 3, 4];
let path_bits = vec![false, true, false];
let siblings = vec![
MerkleHash::from_bytes(&[0u8; 32]).unwrap(),
MerkleHash::from_bytes(&[1u8; 32]).unwrap(),
MerkleHash::from_bytes(&[2u8; 32]).unwrap(),
];
let auth_input = AnonymousAuthInput {
member_identity: leaf.clone(),
membership_path: MerkleProofInput {
leaf: leaf.clone(),
leaf_hash_direct: None,
path_bits: path_bits.clone(),
siblings: siblings.clone(),
},
};
let merkle_input = MerkleProofInput {
leaf: leaf.clone(),
leaf_hash_direct: None,
path_bits: path_bits.clone(),
siblings: siblings.clone(),
};
let auth_vals = air.public_values(&auth_input);
let merkle_vals: Vec<Val> = merkle_air.public_values(&merkle_input);
assert_eq!(auth_vals, merkle_vals);
}
}